堆栈变量的寻址

时间:2014-12-02 10:42:08

标签: c assembly gdb stack disassembly

我正在分析X86_64上GDB中以下(非常简单的)C程序的反汇编。

int main()
{
    int a = 5;
    int b = a + 6;
    return 0;
}

据我所知,在X86_64中,堆栈会逐渐减少。这是堆栈顶部的地址低于堆栈底部的地址。上述程序的汇编程序如下:

Dump of assembler code for function main:
0x0000000000400474 <+0>:    push   %rbp
0x0000000000400475 <+1>:    mov    %rsp,%rbp
0x0000000000400478 <+4>:    movl   $0x5,-0x8(%rbp)
0x000000000040047f <+11>:   mov    -0x8(%rbp),%eax
0x0000000000400482 <+14>:   add    $0x6,%eax
0x0000000000400485 <+17>:   mov    %eax,-0x4(%rbp)
0x0000000000400488 <+20>:   mov    $0x0,%eax
0x000000000040048d <+25>:   leaveq 
0x000000000040048e <+26>:   retq   
End of assembler dump.

我理解:

  1. 我们将基指针推到堆栈上。
  2. 然后我们将堆栈指针的值复制到基指针。
  3. 然后,我们将值5复制到地址-0x8(%rbp)中。因为int中有4个字节,所以不应该是堆栈中的下一个地址-0x4(%rbp)而不是-0x8(%rbp)
  4. 然后,我们将变量a的值复制到%eax,添加6,然后将值复制到-0x4(%rbp)的地址。
  5. 使用此图形作为参考:

    http://eli.thegreenplace.net/images/2011/08/x64_frame_nonleaf.png

    看起来堆栈具有以下内容:

    |--------------|
    |      rbp     | <-- %rbp
    |      11      | <-- -0x4(%rbp)
    |      5       | <-- -0x8(%rbp)
    

    当我期待这个时:

    |--------------|
    |      rbp     | <-- %rbp
    |      5       | <-- -0x4(%rbp)
    |      11      | <-- -0x8(%rbp)
    

    似乎是7-understanding-c-by-learning-assembly中显示汇编的情况:

    (gdb) disassemble
    Dump of assembler code for function main:
    0x0000000100000f50 <main+0>:    push   %rbp
    0x0000000100000f51 <main+1>:    mov    %rsp,%rbp
    0x0000000100000f54 <main+4>:    mov    $0x0,%eax
    0x0000000100000f59 <main+9>:    movl   $0x0,-0x4(%rbp)
    0x0000000100000f60 <main+16>:   movl   $0x5,-0x8(%rbp)
    0x0000000100000f67 <main+23>:   mov    -0x8(%rbp),%ecx
    0x0000000100000f6a <main+26>:   add    $0x6,%ecx
    0x0000000100000f70 <main+32>:   mov    %ecx,-0xc(%rbp)
    0x0000000100000f73 <main+35>:   pop    %rbp
    0x0000000100000f74 <main+36>:   retq   
    End of assembler dump.
    

    b明确声明并首先初始化时,为什么a的值被放入堆栈中的高位内存地址而不是a

1 个答案:

答案 0 :(得分:7)

b的值放在堆栈中,无论编译器感觉如何。你没有影响力。你不应该这样做。订单可能会在编译器的次要版本之间发生变化,因为某些内部数据结构已更改或某些代码已重新排列。有些编译器甚至会故意在不同的编译中随机化堆栈的布局,因为它可能会使某些错误难以利用。

实际上,编译器可能根本不使用堆栈。没有必要。这是在启用了一些优化的情况下编译的同一程序的反汇编:

$ cat > foo.c
int main()
{
    int a = 5;
    int b = a + 6;
    return 0;
}
$ cc -O -c foo.c
$ objdump -S foo.o

foo.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
   0:   31 c0                   xor    %eax,%eax
   2:   c3                      retq
$

通过一些简单的优化,编译器发现你没有使用变量&#39; b&#39;因此不需要计算它。因此,你不能使用变量&#39; a&#39;或者,所以没有必要分配它。只有没有优化的编译(或非常糟糕的编译器)才会在此处放置任何内容。即使您使用这些值,基本优化也会将它们放入寄存器中,因为触摸堆栈非常昂贵。