我正在努力记住 Linux 系统调用的顺序,以便我可以更轻松地识别这一点。然后我发现这篇论文在这里,它说,
要在 32 位 Linux 中进行系统调用,请将系统调用号放入 中
eax
,然后将其参数按顺序放入ebx
、ecx
、edx
、esi
、edi
和 中ebp
,然后调用 int 0x80。
进而,
要在 64 位 Linux 中进行系统调用,请将系统调用号放入 中
rax
,然后将其参数按顺序放入rdi
、rsi
、rdx
、r10
、r8
和 中r9
,然后调用 syscall。
为什么 64 位和 32 位之间的顺序会如此混乱?我知道这个问题可能是历史性的而不是技术性的。
这完全是内核决定的吧?是否有技术原因支持新的约定?
答案1
这很大程度上取决于内核,但是在 64 位 x86 上使用所选的调用约定有一个很好的理由:它与所选的用户空间约定相匹配。 Linux 使用的 System V x86-64 ABI,指定该函数使用寄存器%rdi
、%rsi
、%rdx
、%rcx
、%r8
和%r9
来传递参数。系统调用约定与此非常接近:唯一的区别是它使用%r10
代替%rcx
,主要是因为SYSCALL
用于调用系统调用的新 64 位指令需要%rcx
用于其他目的。
答案2
编译代码中的寄存器顺序是Linux的一种调用约定,以方便生成和调试代码。
amd-64 架构比 32 位架构拥有更多的寄存器,并且使用新的调用约定来释放其他寄存器,以更好地优化代码。
如果您反汇编两种架构之间的代码,您还会注意到,在 amd-64 中,您经常会看到更多寄存器取代了函数中的局部整数变量。如果我没记错的话,rebp 用于优化在从函数返回之前使用局部变量堆栈生成的代码。