我写了一个简单的代码来理解汇编代码。如下:
int sum(int a, int b){
int res = a+b;
}
在main函数中,我调用了sum-function。 所以,我得到汇编代码(我只在这里采用sum函数的一部分)4
push ebp,
mov ebp, esp
sub esp, 16
mov eax, DWORD PTR[ebp + 12]
mov edx, DWORD PTR[ebp + 8]
add eax, edx
mov DWORD PTR [ebp-4], eax
mov eax, DWORD PTR [ebp-4]
leave
现在回答我的问题。我有两个问题: 首先,有没有理由为什么sum参数的值,例如sum(5,4),在ebp + 12和ebp + 8中得分,结果在ebp-4中?为什么我们这样做?是永远相同还是随机选择?
第二,我们所拥有的部分:
mov DWORD PTR [ebp-4], eax
mov eax, DWORD PTR [ebp-4]
为什么我们首先在ebp-4中执行结果,然后在离开函数之前再次在eax中执行结果? 还有原因吗?
答案 0 :(得分:2)
这通常是在x86系统中如何创建堆栈帧。
来电者会将此转换为...
push b
push a
call sum
当每个项目被推入堆栈时,堆栈会逐渐减少。也就是说,堆栈指针寄存器递减四(4)个字节(在32位模式下),并将该项复制到堆栈指针寄存器指向的存储单元。
此时,'call'指令已经发出,我们现在处于被调用例程的开始处。如果我们想要访问我们的参数,我们可以像
那样访问它们[esp + 4] - parameter 'a'
[esp + 8] - parameter 'b'
然而,在我们为局部变量和东西划分空间之后,这可能会变得笨拙。因此,除堆栈指针寄存器外,我们还使用stackbase-pointer寄存器。但是,我们希望stackbase-pointer寄存器设置为当前帧,而不是前一个函数。因此,我们将旧的保存在堆栈上(这会修改堆栈上参数的偏移量),然后将当前堆栈指针寄存器复制到堆栈库指针寄存器。
push ebp ; save previous stackbase-pointer register
mov ebp, esp ; ebp = esp
全部放在一起。使用stackbase-pointer registe
访问参数[ebp + 12] - parameter 'b'
[ebp + 8] - parameter 'a'
[ebp + 4] - return address
[ebp + 0] - saved stackbase-pointer register
答案 1 :(得分:0)
您可以通过查找以下关键字来查找此信息:
那时,一个16位字参数将在EAX中与AX(和32位一)一起传递。回报也是AX / EAX。 64位参数(或2个参数将在EDX:EAX中)并返回相同的参数。这是一个典型的FASTCALL调用约定(我相信旧的Pascal正在使用)
现在对于C / C ++来说,fastcall通常不会被使用(可能因为它不那么便携)。大多数东西都使用了堆栈,这就是EBP的用武之地。 EBP是您的基本堆栈指针-4,-8,-12只是本地堆栈中存储和返回参数的偏移指针。
以下是一些链接: http://en.wikibooks.org/wiki/X86_Disassembly/Calling_Convention_Examples http://en.wikipedia.org/wiki/X86_calling_conventions