x86-64 Clang编译函数调用中栈指针未指向栈顶的原因咨询
x86-64函数调用中栈指针未调整的疑问
C代码示例
int multiply(int num, int k) { return num * k; } int main(int argc, char** argv) { int k = multiply(5,2); }
反汇编结果(x86-64 clang 10.0.1)
multiply函数汇编
multiply: # @multiply push rbp mov rbp, rsp mov dword ptr [rbp - 4], edi mov dword ptr [rbp - 8], esi mov eax, dword ptr [rbp - 4] imul eax, dword ptr [rbp - 8] pop rbp ret
main函数汇编
main: # @main push rbp mov rbp, rsp sub rsp, 32 mov dword ptr [rbp - 4], edi mov qword ptr [rbp - 16], rsi mov edi, 5 mov esi, 2 call multiply xor ecx, ecx mov dword ptr [rbp - 20], eax mov eax, ecx add rsp, 32 pop rbp ret
疑问
我发现,被调用方(multiply)将寄存器edi和esi中的参数存入栈中时,栈指针(rsp)并未随之改变,甚至操作完成后仍指向保存旧基指针(rbp)值的位置,请问这是为什么?
解答
这是x86-64平台的System V应用二进制接口(ABI)规则与编译器帧指针用法共同作用的结果:
帧指针(rbp)的定位作用:
multiply函数开头执行push rbp和mov rbp, rsp后,rbp就成为当前栈帧的基指针。后续访问栈数据都通过相对于rbp的偏移完成,不需要依赖rsp的位置。[rbp-4]、[rbp-8]这类偏移地址是固定指向栈上的特定位置,无需调整rsp就能访问。红区(Red Zone)的安全使用:
x86-64的System V ABI规定,栈指针rsp下方128字节的区域属于"红区"。被调用方可以直接使用这片区域暂存数据,不需要提前通过sub rsp, n来分配栈空间——因为调用方不会触碰红区,信号处理程序也会自动避开这片区域,不会引发数据冲突。multiply里存储参数的[rbp-4]和[rbp-8]正好处于红区范围内,所以完全不需要修改rsp。函数无额外栈需求:
这个multiply函数逻辑简单,除了用红区暂存参数,没有其他需要栈空间的操作(比如保存更多寄存器、分配局部数组等),因此全程不需要调整rsp,直到函数结束时通过pop rbp恢复栈帧即可。
内容的提问来源于stack exchange,提问作者Mehdi Charife
相关产品推荐
相关产品推荐

