为什么我在“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 覆盖 RCXRIPR11RFLAGS 作为其操作的一部分,但 OS 当然能够恢复 RIP 和 RFLAGS。

这还有很多其他选择。一个不错的选择是允许函数调用在标准 user-space 调用约定中破坏的寄存器,但 Linux 系统调用将保持不变。 R8 符合要求。由于您没有使用标准 x86-64 System V function-calling conventionRBXRBP 将是更好的选择(更小的机器代码大小,因为它们不需要 REX 前缀)。