2014-12-02 10 views
10

Ich analysiere die Demontage des folgenden (sehr einfachen) C-Programms in GDB auf X86_64.Die Adressierung von Stack-Variablen

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

Ich verstehe, dass in X86_64 der Stapel wächst nach unten. Das heißt, der obere Teil des Stapels hat eine niedrigere Adresse als der untere Teil des Stapels. Der Assembler aus dem obigen Programm ist wie folgt:

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. 

ich verstehen:

  1. Wir schieben den Basiszeiger auf den Stapel.
  2. Wir kopieren dann den Wert des Stapelzeigers zum Basiszeiger.
  3. Wir kopieren dann den Wert 5 in die Adresse -0x8(%rbp). Da in einem int ist 4 Bytes sollte dies nicht an der nächsten Adresse im Stapel sein, die -0x4(%rbp) statt -0x8(%rbp) ist?.
  4. Wir kopieren dann den Wert an der Variablen a in %eax, fügen 6 hinzu und kopieren dann den Wert in die Adresse -0x4(%rbp).

die diese Grafik als Referenz verwenden:

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

es wie der Stapel sieht hat folgenden Inhalt:

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

, als ich das erwartet hatte:

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

whi ch scheint der Fall in 7-understanding-c-by-learning-assembly zu sein, wo sie die Montage zeigen:

(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. 

Warum wird der Wert von b in eine höhere Speicheradresse als a in den Stapel gelegt wird, wenn a eindeutig deklariert und zunächst initialisiert?

+10

Der Compiler kann die Variablen für die automatische Dauer auf einmal auf dem Stapel zuweisen, wenn es sich so anfühlt. Nur der naivste Compiler würde den Code durchlaufen und bei jeder Deklaration und jedem Exit des Bereichs den Stapelzeiger ändern, und verschiedene Compiler und Versionen davon könnten anderen Code ausspucken. Der C-Standard selbst hat nichts über die relativen Adressen zweier automatischer Variablen zu sagen. –

Antwort

7

Der Wert von b wird auf den Stapel gelegt, wo auch immer der Compiler sich fühlt. Du hast keinen Einfluss darauf. Und du solltest nicht. Es ist möglich, dass sich die Reihenfolge zwischen kleineren Versionen des Compilers ändert, weil eine interne Datenstruktur geändert oder ein Code neu angeordnet wurde. Einige Compiler werden das Layout des Stacks bei verschiedenen Compilations absichtlich randomisieren, da dadurch bestimmte Bugs schwieriger ausgenutzt werden können.

Tatsächlich könnte der Compiler den Stapel überhaupt nicht verwenden. Das ist nicht nötig. Hier ist die Demontage des gleichen Programms mit einigen Optimierungen aktiviert zusammengestellt:

$ 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 
$ 

Mit einigen einfachen Optimierungen der Compiler herausgefunden, dass Sie nicht über die Variable ‚b‘, so gibt es keine Notwendigkeit, sie zu berechnen. Und deshalb benutzen Sie auch die Variable 'a' nicht, also müssen Sie sie nicht zuweisen. Nur eine Kompilierung ohne Optimierungen (oder ein sehr schlechter Compiler) bringt hier alles auf den Stapel. Und selbst wenn Sie die Werte verwenden, werden grundlegende Optimierungen sie in Register setzen, weil das Berühren des Stapels teuer ist.

Verwandte Themen