在 SysV ABI 的 edi/esi 中传递参数

Pass arguments in edi/esi in SysV ABI

我想用 SysV ABI x86_64 程序集编写程序,到目前为止我已经相当随机地在寄存器中传递了参数。

但是我刚刚在这个论坛上看到,有一个标准。我们必须通过 RDI、RSI、RDX 和 RCX(按照确切的顺序)。

现在我问自己两个问题。

首先,ESI 和 EDI 不是应该只在对字符串进行操作时使用吗?如果我想传递整数而不是字符串作为参数会怎样?

其次,如果我需要传递 32 位参数而不是 64 位参数怎么办?例如,如果我想为系统调用write创建一个标识符,我会这样写:

;; void write(int fd, const void *buf, size_t count);
;; Inputs   :  ESI = offset string, EDX = number of characters to write, EBX = file descriptor
;; Outputs  :  <none>
;; Clobbers :  <none>
write:
    mov ecx, esi

    mov eax, 4
    int 0x80

    ret

但是对于标准,我如何将值从 64 位寄存器移动到 32 位寄存器?因为我做不到:

mov ecx, rdi ; impossible

一般来说rdirsican be treated as general purpose registers,也就是说你可以用它们来进行任意的算术和内存操作。它们具有某些特殊的含义,因为它们也用作字符串操作的索引寄存器。但是,该体系结构并不关心您在其中存储的是字符串指针还是其他任意 64 位数字。


关于传递 32 位值,您可以简单地访问源寄存器的低 32 位部分:

mov ecx, edi

这只会将 least-significant 32 位移动到 ecx。请注意,如果您传递整个 64 位,它并没有真正的区别 - 如果被调用者仅访问 32 位子寄存器 ecx,结果是相同的:

mov rcx, rdi
; ...
; use ecx

关于问题中示例代码的小提示:您似乎在 64 位环境中使用 32 位样式的系统调用。如果指向 buf 的指针不适合 32 位,这可能会中断。 write 系统调用的 64 位版本如下所示:

write:
    ; syscall number
    mov rax, 1

    ; all other arguments are already in the right registers
    syscall

    ret

更多信息:

  • x86_64 Linux syscall arguments