尽管看着教科书试图掌握这一点,但我遇到了麻烦。
0x08048b29 <func+0>: push %ebp
0x08048b2a <func+1>: mov %esp,%ebp
0x08048b2c <func+3>: push %ebx
...
0x08048b30 <phase_2+7>: lea -0x28(%ebp),%eax
在lea指令中,我理解%eax在%ebp之前得到的值为0x28,但究竟是哪里?它是0x8048b29 - 0x28(函数开头之前的0x28)还是什么?
答案 0 :(得分:1)
ebp寄存器通常在函数中用于访问传递给函数的任何参数。在调用此函数之前,寄存器未传递的任何参数都会被压入堆栈。在此函数开始时,将保存调用函数的基指针。
(1)0x08048b29 <func+0>: push %ebp
将堆栈的新顶部复制到ebp中,作为此函数中的基指针。
(2)0x08048b2a <func+1>: mov %esp,%ebp
最后,保存了ebx,这可能是一个传递给寄存器中函数的变量。
(3)0x08048b2c <func+3>: push %ebx
在所有这个函数入口代码之后,ebp寄存器指向堆栈的中间位置。在堆栈上方(朝向最新的推送项目)是在上面#3中推送的ebx值。堆栈下面是来自调用函数的旧ebp(保存在上面的#1中),最重要的是......通过堆栈传递给该函数的任何参数(在调用函数之前完成)。
(4)lea -0x28(%ebp),%eax
最后一条指令指的是堆栈传递的其中一个参数。 ebp最终指向堆栈,而不是任何函数代码地址。这对于ebp来说非常正常,它通常用作指向堆栈的指针。
答案 1 :(得分:1)
从调用该函数的上下文中查看此内容。这样做的代码看起来像:
caller+...: push argN
caller+...: ...
caller+...: push arg0
caller+...: call func
即。参数以这样的顺序放入堆栈中,在进入func()
时,堆栈将具有以下布局:
[esp+(N*4)] : argN
... : arg(N-1)...arg2
[esp+4 ] : arg1
[esp ] : <return address to caller()+...>
然后执行push %ebp; mov %esp, %ebp
序列,其中更改 %esp
(按-4),以便您的布局现在为:
[ebp+4+(N*4)][esp+(N*4)] : argN
... : arg(N-1)...arg2
[ ebp+8 ][esp+8 ] : arg1
[ ebp+4 ][esp+4 ] : <return address to caller()+...>
[ ebp ][esp ] : <saved %ebp of caller>
然后代码继续在堆栈上再推几个寄存器 - 因为每次%esp
改变为-4时,它都会向下增长。最终(你没有在你的反汇编中显示但它会在那里)你会得到一条指令subl $..., %esp
。这是为您的局部变量分配空间的原因。最终的堆栈布局类似于:
[ebp+4+(N*4)][ ] : argN
... : arg(N-1)...arg2
[ ebp+8 ][ ] : arg1
[ ebp+4 ][ ] : <return address to caller()+...>
[ ebp ][ ] : <saved %ebp of caller>
[ ebp-4 ][ ] : <saved %ebx of caller>
[ ebp-8 ][ ] : ...
... : region for local variables
[ ebp-?? ][ esp ] : end of stack for func()
[esp ... ebp-4]
之间的任何地址都在您的函数的堆栈框架内,并且它包含代表调用者保存的寄存器(如{1}显示的反汇编代码中的ebx
)或局部变量。
因此,当您在代码中看到%ebp - XX
在本地变量空间中的任何访问权时,如果您看到%ebp + YY
它位于包含函数参数的空间内。