2010-10-14 53 views
2

尽管看着教科书试图抓住这一点,但我遇到了麻烦。设置后帧指针指向哪里?

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)还是什么?

回答

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来说是非常正常的,它通常用作指向堆栈的指针。

+0

他们这样做的原因是,参数总是可以被相同的ebp偏移量引用。您不能使用esp来达到此目的,因为此功能可能需要使用堆栈指针来执行临时推/拉指令。 – 2010-10-14 22:38:40

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]之间的任何地址内什么要求你的函数的栈帧,它包含无论是在的情况下保存代表调用(如ebx的寄存器反汇编显示你)或局部变量。

因此,如果您在代码中看到对%ebp - XX的任何访问权限,那么它位于本地变量空间中,如果您看到%ebp + YY它位于包含函数参数的空间内。