为什么我在“ret”时会出现段错误? (FASM)
Why am I getting a segfault when I `ret'? (FASM)
通过一些操作,我将其缩小为 ret
操作的问题。我知道 call
将 return 地址压入堆栈;弹开再推回去违法吗?
format ELF64 executable 3
entry start
segment readable executable
start:
pop rcx ; argc
mov [argc],cl ; int -> ASCII
add [argc],'0'
push 1 argc 1
call sys_write
mov rdi,0
mov rax,60
syscall
sys_write: ; (fd,*buf,count)
pop r11
pop rdx rsi rdi
mov rax,1
syscall
push r11
ret
segment readable writable
argc rb 1
输出为:
$ ./prog
1Segmentation fault
$ _
syscall
指令用 RFLAGS
寄存器破坏了 R11
的内容,这意味着在创建 syscall
之后,您存储在 [=11= 中的值] 被覆盖。
一个解决方案可能是 select 一个不被系统调用修改的适当寄存器。根据 Intel 的指令参考手册(http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-manual-325462.pdf,第 4-668 页,第 2B 卷)syscall
覆盖 RCX
、RIP
、R11
和 RFLAGS
作为其操作的一部分,但 OS 当然能够恢复 RIP 和 RFLAGS。
这还有很多其他选择。一个不错的选择是允许函数调用在标准 user-space 调用约定中破坏的寄存器,但 Linux 系统调用将保持不变。 R8
符合要求。由于您没有使用标准 x86-64 System V function-calling convention,RBX
或 RBP
将是更好的选择(更小的机器代码大小,因为它们不需要 REX 前缀)。
通过一些操作,我将其缩小为 ret
操作的问题。我知道 call
将 return 地址压入堆栈;弹开再推回去违法吗?
format ELF64 executable 3
entry start
segment readable executable
start:
pop rcx ; argc
mov [argc],cl ; int -> ASCII
add [argc],'0'
push 1 argc 1
call sys_write
mov rdi,0
mov rax,60
syscall
sys_write: ; (fd,*buf,count)
pop r11
pop rdx rsi rdi
mov rax,1
syscall
push r11
ret
segment readable writable
argc rb 1
输出为:
$ ./prog
1Segmentation fault
$ _
syscall
指令用 RFLAGS
寄存器破坏了 R11
的内容,这意味着在创建 syscall
之后,您存储在 [=11= 中的值] 被覆盖。
一个解决方案可能是 select 一个不被系统调用修改的适当寄存器。根据 Intel 的指令参考手册(http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-manual-325462.pdf,第 4-668 页,第 2B 卷)syscall
覆盖 RCX
、RIP
、R11
和 RFLAGS
作为其操作的一部分,但 OS 当然能够恢复 RIP 和 RFLAGS。
这还有很多其他选择。一个不错的选择是允许函数调用在标准 user-space 调用约定中破坏的寄存器,但 Linux 系统调用将保持不变。 R8
符合要求。由于您没有使用标准 x86-64 System V function-calling convention,RBX
或 RBP
将是更好的选择(更小的机器代码大小,因为它们不需要 REX 前缀)。