2015-11-17 24 views
8

aşağıdaki C++ kaynak kod göz parçalama arızası yol açar. Ubuntu 14.04 altında 4 ve clang-3.6.0. Garip davranış, _end sembolünün, başlangıçta değil, statik olarak ayrılmış bir dizinin _end işaret etmesidir. _end'u end_ ile değiştirirsek, her şey iyi çalışır.g ++ sembol '_end' kullanılması

$ g++ main.cpp -o main.s -O0 -S 
$ g++ main2.cpp -o main2.s -O0 -S 
$ diff main.s main2.s 
1,2c1,2 
< .file "main.cpp" 
< .globl _end 
--- 
> .file "main2.cpp" 
> .globl end_ 
5,7c5,7 
< .type _end, @object 
< .size _end, 4200 
< _end: 
--- 
> .type end_, @object 
> .size end_, 4200 
> end_: 
25c25 
< movl $0, _end(,%rax,4) 
--- 
> movl $0, end_(,%rax,4) 
: Biz -S komut satırı argümanı sağlayarak çıkışa bir montaj kodu gcc sorarsanız

Üstelik, "_end" ve diğer dizisi adını içeren sürümü ile sürüm arasında anlamlı bir fark olacak

$ g++ main.cpp -o main -O0 
$ g++ main2.cpp -o main2 -O0 
$ objdump -d main >main.dump 
$ objdump -d main2 > main2.dump 
$ diff main.dump main2.dump 
2c2 
< main:  формат файла elf64-x86-64 // "File format" in Russian 
--- 
> main2:  формат файла elf64-x86-64 
123c123 
< 4004ff: c7 04 85 c8 20 60 00 movl $0x0,0x6020c8(,%rax,4) 
--- 
> 4004ff: c7 04 85 60 10 60 00 movl $0x0,0x601060(,%rax,4) 

kadarıyla:

Ama yürütülebilir dökümü ve onlara karşı fark çalıştırmak için objdump kullanırsanız, biz _end sürümü kullanılıyor adresi göreceksiniz gerekenden daha 4200 = 4 * 1050 bayt ileri olduğunu Biliyorum, gcc derleyici variab'ı tedavi edebilir alt çizgi ile istediği gibi başlamak, i. e. Bu, kodunuzda bu sembolleri kullanmak için kötü bir uygulamadır. Ama sorum şu: burada gerçekten ne var? Neden _end, ayrılmış bir dizinin sonunun adresiyle değiştirilir? "-S" komut satırı argümanını kullanırsak neden bir fark olmaz, ancak oluşturulan ikili dosyalarda aslında bir fark var mı? Bu durumda gcc ve clang'ın aynı davranması değil, bu benim için de garip.

cevap

2

_ ile başlayan jetonlar saklıdır ve bunları kullanmamalısınız. Görünüşe göre, _end, Linux'ta derlenen programlar için tanımlanan harici bir semboldür ve başlatılmamış veri segmentinin (BSS bölümü olarak da bilinir) sonundaki ilk adresi temsil eder.

Not: _etext, _edata ve _end: Bazı sistemlerde bu sembollerin isimleri böylece alt çizgi, öncesinde.

Kaynak: http://man7.org/linux/man-pages/man3/end.3.html

+0

Tam olarak neye ihtiyacım vardı, teşekkürler! Fakat bu kodu derlerken neden "-S" komut satırı argümanı şüpheli görünmüyor? –

+0

@MaximAkhmedov Muhtemelen '_end', diğer işaretçiler gibi bir işaretçi ve dizinize atandığınızda işaretçi aritmetiği gerçekleştirdiği için olabilir. – vsoftco

0

C99 N1256 standard draft 7.1.3 "Ayrılmış tanımlayıcılar" diyor:

bir alt çizgi ile başlar tüm tanımlayıcılar daima dosya kapsamında olan tanımlayıcılar olarak kullanılmak için ayrılmıştır hem sıradan hem de etiket adı boşlukları.

Sonra bilmelisiniz:

  • dosya kapsamı (diğerleri fonksiyonu ve blok kapsamı vardır)
  • sıradan bir isim alanı değişkenleri

Yani C99 göre içerir globaller içindir _end kimliğini kullanamazsınız.

g++ -Wl,--verbose main.c 

kullanılan bağlayıcı senaryoyu görmek için: aslında sizin uygulanmasına ilişkin başarısız neden

Sen uygulaması

Şimdi kullanımı görmek için.

Ubuntu 15.10 tarihinde, veri bölümün sonunda sembolünü _end tanımlar:

_end = .; PROVIDE (end = .); 
. = DATA_SEGMENT_END (.); 

yüzden çok önde bunun belleğe erişim segfault olabileceğini hiç şaşırtıcı değil.