2014-12-02 17 views
10

Aşağıdaki (çok basit) C programının X86_64 üzerinde GDB'de sökülmesini analiz ediyorum.Yığın değişkenlerinin adreslenmesi

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

X86_64'te yığının büyüyeceğini anlıyorum. Bu yığının tepesinin, yığının altından daha düşük bir adresi vardır. aşağıdaki gibi yukarıdaki program çeviricidir:

  1. Biz yığında baz işaretçi itme:
    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. 
    

    I anlıyoruz.
  2. Ardından yığın işaretçisinin değerini temel işaretçiye kopyalarız.
  3. Daha sonra 5 değerini -0x8(%rbp) adresine kopyalarız. Bir int 4 bayt olduğundan bu -0x8(%rbp)? Yerine -0x4(%rbp) olan sonraki adreste olmamalıdır.
  4. Daha sonra a değişkenindeki değeri %eax'a kopyalayın, 6 ekleyin ve sonra değeri -0x4(%rbp) adresindeki adrese kopyalayın. referans için bu grafiği kullanarak

:

o yığını gibi görünüyor http://eli.thegreenplace.net/images/2011/08/x64_frame_nonleaf.png

içeriği şu şekildedir:

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

bu bekliyordum:

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

whi Onlar takımını göstermek nerede ch 7-understanding-c-by-learning-assembly durumda gibi görünüyor:

(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. 
a açıkça beyan ve ilk başlatıldığında b değeri a daha yığını içinde daha yüksek bir bellek adresi içine konulmaktadır Neden

?

+10

Derleyici, otomatik süre değişkenlerini yığında tamamen hissediyorsa, bir kerede boşta serbesttir. Sadece derleyicilerin en saf hali kod boyunca ilerler ve her bildirimde ve kapsam çıkışında yığın işaretçisini değiştirir ve farklı derleyiciler ve versiyonları farklı kodlar çıkartabilir. C standardının, iki otomatik değişkenin göreli adresleri hakkında söyleyecek hiçbir şeyi yoktur. –

cevap

7

b değeri, derleyicinin istediği yere yığının üstüne konur. Bunun üzerinde hiçbir etkiniz yok. Ve yapmamalısın. Bazı dahili veri yapısı değiştirildiğinden veya bir kod yeniden düzenlendiğinden, siparişin derleyicinin küçük sürümleri arasında değişmesi mümkündür. Bazı derleyiciler, destenin farklı düzenleri üzerindeki düzenini rastgele hale getirecekler çünkü belirli hataları daha da zorlaştırabilirler. Aslında, derleyici kümeyi hiç kullanamayabilir. Gerek yok. İşte etkinleştirilmiş bazı optimizasyonlar ile derlenmiş aynı programın sökme var: derleyici değişkeni 'b' kullanmayın anladım bazı basit optimizasyonlarıyla

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

, bu yüzden bunu hesaplamak gerek yoktur. Ve bundan dolayı 'a' değişkenini de kullanmıyorsunuz, dolayısıyla atamaya gerek yok. Sadece en iyi duruma getirilmemiş derleme (ya da çok kötü bir derleyici) buradaki yığına herhangi bir şey koyacaktır. Ve değerleri kullansanız bile, temel optimizasyonlar onları yığına koyacaktır çünkü yığına dokunmak pahalıdır.

İlgili konular