Linux Stack - main()为什么ebp没有被压入堆栈

时间:2011-12-13 09:05:57

标签: linux assembly fedora

我正在查看主要开头的堆栈,但主要的ebp丢失了。

我声明了一个变量来检查它在堆栈中的位置,结果发现此变量和返回地址之间有零n __libc_start_main

系统我正在使用

I'm using fedora Linux 3.1.2-1.fc16.i686 
ASLR is disabled.
Debugging with GDB.

以下是代码:

void main(){

        char ret ='a';

}

注册信息:

(gdb)
eax            0x1      1
ecx            0xbffff5f4       -1073744396
edx            0xbffff584       -1073744508
ebx            0x2dbff4 2998260
esp            0xbffff554       0xbffff554
**ebp            0xbffff558       0xbffff558**
esi            0x0      0
edi            0x0      0
eip            0x804839a        0x804839a <main+6>

(gdb) x/8xw $esp
0xbffff554: 0x00000000(local var) 0x00000000(missing ebp!) 0x0014d6b3(return to libc_start)      0x00000001

0xbffff564:     0xbffff5f4      0xbffff5fc      0x00131fc4      0x0000082d

我能想到的唯一一件事就是libc_start_main的函数序言并没有因某种原因推动主要的ebp!


编辑1:

- 没有Opatmization编译(gcc -ggdb文件file.c)

主要装配(gcc版本4.6.2 20111027)

 push   %ebp

 mov    %esp,%ebp
 sub    $0x10,%esp
 movb   $0x61,-0x1(%ebp)
 leave
 ret

查看堆栈的局部变量的断点显示变量后跟零的相同内容然后返回libc_start

3 个答案:

答案 0 :(得分:2)

如果你在没有优化的情况下进行编译,你几乎肯定会发现ebp / rbp实际上被推入堆栈然后根据esp / {{1 }}。但是,它由rsp本身完成,而不是由main按照您的建议完成。

以下是libc生成的汇编代码:

gcc 4.4.5

如果使用优化选项进行编译,您可能会发现main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 movq %rsp, %rbp .cfi_offset 6, -16 .cfi_def_cfa_register 6 movb $97, -1(%rbp) leave ret .cfi_endproc 的整个主体已被优化(main):

gcc -O3

为什么不查看反汇编(例如在main: .LFB0: .cfi_startproc rep ret .cfi_endproc 中)看看你的具体情况会发生什么?而不是猜测?

此外,即使在未经优化的情况下,您也必须实际执行函数序言,以便按照您期望的方式设置寄存器。

最后,当您看到堆栈上的数据之间存在明显差距时,您不应该感到惊讶,因为堆栈需要对齐:

gdb

答案 1 :(得分:2)

对于在编译器生成的汇编代码中使用的特定调用约定,没有要求。这就是为什么它被称为约定而不是要求: - )

在任何情况下,您都需要记住,C的'普通'x86调用约定要求函数本身处理堆栈帧的设置和拆除。换句话说,这是main的责任,而不是启动代码(通常在你的main之前运行的代码来设置C运行时环境,例如堆栈设置,创建argc/argv,任何库预初始化等等。

此外,在构建当前堆栈帧之前,推送到堆栈的ebp之前的ebp

当前堆栈帧的部分构建过程是保存当前的ebp,然后将新值加载到ebp寄存器中,以便轻松访问传递的参数和本地。

您可以通过使用gcc -S编译代码段来了解这一点:

main:
    pushl    %ebp              ; Push PREVIOUS ebp.
    movl     %esp, %ebp        ; Load ebp for variable access.
    subl     $16, %esp         ; Allocate space on stack.

    movb     $97, -1(%ebp)     ; Store 'a' into variable.

    leave                      ; Tear down frame and return.
    ret

前三行和后两行是彼此的镜像,即设置和拆除代码。在这种情况下,很有可能启动代码将ebp设置为零,可能是因为它并不关心 - 除了确保argc和{之外,它不必担心调用约定。 {1}}在那里。

答案 2 :(得分:0)

如果您正在为x86_64进行编译,则ebp / rbp将被调用者保存。这意味着main()应该在需要使用它时保存它。如果不是,则不需要被调用者或调用者保存旧的寄存器值。

如果您有兴趣,请参阅AMD64 ABI的第3.2节以获取更多信息。