为什么参数分配在帧指针下方而不是上方?
Why are parameters allocated below the frame pointer instead of above?
我试图根据 godbolt.org 中的 c++ 中的平方函数来理解这一点。显然,return、参数和局部变量使用“rbp - alignment”来实现这个功能。
有人可以解释这是怎么可能的吗?
那么在这种情况下 rbp + alignment 会做什么?
int square(int num){
int n = 5;// just to test how locals are treated with frame pointer
return num * num;
}
编译器 (x86-64 gcc 11.1)
生成的程序集:
square(int):
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-20], edi. ;\Both param and local var use rbp-*
mov DWORD PTR[rbp-4], 5. ;//
mov eax, DWORD PTR [rbp-20]
imul eax, eax
pop rbp
ret
这是可以方便区分参数和自变量的情况之一。简而言之:参数是调用者给出的值,而参数是保存它们的变量。
当调用 square
时,调用者根据标准 x86-64 调用约定将 参数 放入 rdi
寄存器中。 square
然后分配一个局部变量,即 参数 ,并将参数放在参数中。这允许参数像任何其他变量一样使用:读取、写入、获取其地址等。由于在这种情况下是为参数分配内存的被调用方,因此它必须驻留在帧指针下方。
使用在堆栈上传递参数的 ABI,被调用方将能够重用包含参数的堆栈槽作为参数。这正是 x86-32 上发生的事情(通过 -m32
看你自己):
square(int): # @square(int)
push ebp
mov ebp, esp
push eax
mov eax, dword ptr [ebp + 8]
mov dword ptr [ebp - 4], 5
mov eax, dword ptr [ebp + 8]
imul eax, dword ptr [ebp + 8]
add esp, 4
pop ebp
ret
当然,如果启用了优化,编译器就不会费心在被调用者的堆栈上分配参数;它会直接使用寄存器中的值:
square(int): # @square(int)
mov eax, edi
imul eax, edi
ret
我试图根据 godbolt.org 中的 c++ 中的平方函数来理解这一点。显然,return、参数和局部变量使用“rbp - alignment”来实现这个功能。 有人可以解释这是怎么可能的吗? 那么在这种情况下 rbp + alignment 会做什么?
int square(int num){
int n = 5;// just to test how locals are treated with frame pointer
return num * num;
}
编译器 (x86-64 gcc 11.1)
生成的程序集:
square(int):
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-20], edi. ;\Both param and local var use rbp-*
mov DWORD PTR[rbp-4], 5. ;//
mov eax, DWORD PTR [rbp-20]
imul eax, eax
pop rbp
ret
这是可以方便区分参数和自变量的情况之一。简而言之:参数是调用者给出的值,而参数是保存它们的变量。
当调用 square
时,调用者根据标准 x86-64 调用约定将 参数 放入 rdi
寄存器中。 square
然后分配一个局部变量,即 参数 ,并将参数放在参数中。这允许参数像任何其他变量一样使用:读取、写入、获取其地址等。由于在这种情况下是为参数分配内存的被调用方,因此它必须驻留在帧指针下方。
使用在堆栈上传递参数的 ABI,被调用方将能够重用包含参数的堆栈槽作为参数。这正是 x86-32 上发生的事情(通过 -m32
看你自己):
square(int): # @square(int)
push ebp
mov ebp, esp
push eax
mov eax, dword ptr [ebp + 8]
mov dword ptr [ebp - 4], 5
mov eax, dword ptr [ebp + 8]
imul eax, dword ptr [ebp + 8]
add esp, 4
pop ebp
ret
当然,如果启用了优化,编译器就不会费心在被调用者的堆栈上分配参数;它会直接使用寄存器中的值:
square(int): # @square(int)
mov eax, edi
imul eax, edi
ret