2015-11-17 3 views
8

Si consideri il seguente codice sorgente C++:Utilizzando simbolo '_end' in g ++ porta ad un errore di segmentazione

int _end[1050]; 

int main() { 
    for (int i = 0; i < 1050; i++) 
     _end[i] = 0; 
    return 0; 
} 

linea di compilazione: g++ main.cpp -o main -O0

L'esecuzione di questo codice porta a errore di segmentazione quando si utilizza gcc-4.8. 4 e clang-3.6.0 sotto Ubuntu 14.04. Lo strano comportamento è che il simbolo _end punti alla fine di un array assegnato staticamente _end, non all'inizio. Se sostituiamo _end con end_, tutto funziona correttamente.

Inoltre, se chiediamo gcc ad emettere un codice assembly fornendo -S argomento della riga di comando, non vi sarà alcuna differenza significativa tra la versione con "_end" e la versione con qualsiasi altro nome di matrice:

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

Ma se usiamo objdump eseguire il dump dei file eseguibili ed eseguire diff contro di loro, vedremo che nel _end versione l'indirizzo utilizzato è 4200 = 4 * 1050 byte oltre che necessaria:

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

per quanto riguarda Lo so, il compilatore gcc può trattare il variab iniziando con underscore come vuole, i. e. questa è una cattiva pratica per usare tali simboli nel codice. Ma la mia domanda è: cosa succede veramente qui? Perché _end viene sostituito con un indirizzo di fine di un array assegnato? Perché non c'è differenza se usiamo l'argomento della riga di comando "-S", ma esiste effettivamente una differenza nei binari creati? Non che gcc e clang si comportino in modo identico in questo caso, anche per me è strano.

risposta

2

I token che iniziano con _ sono riservati e non è necessario utilizzarli. Sembra che _end sia un simbolo esterno definito per i programmi compilati su Linux e rappresenti il ​​primo indirizzo oltre la fine del segmento di dati non inizializzato (noto anche come segmento BSS).

Nota: su alcuni sistemi i nomi di questi simboli sono preceduti da sottolineature, così: _etext, _edata e _end.

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

+0

Esattamente quello di cui avevo bisogno, grazie! Ma perché l'argomento della riga di comando "-S" non mostra nulla di sospetto durante la compilazione di questo codice? –

+0

@MaximAkhmedov Questo probabilmente è dovuto al fatto che '_end' è un puntatore come qualsiasi altro puntatore e l'aritmetica del puntatore viene eseguita quando si assegna all'array. – vsoftco

0

C99 N1256 standard draft 7.1.3 "identificatori riservati", dice:

Tutti gli identificatori che iniziano con un carattere di sottolineatura sono sempre riservati per l'uso come identificatori con ambito di file in sia gli spazi dei nomi ordinari sia quelli dei tag.

Poi dobbiamo sapere che:

  • ambito di file è per variabili globali (gli altri sono la funzione e bloccare la portata)
  • nome spazio ordinario include variabili

Quindi, secondo C99 non è possibile utilizzare l'identificatore _end.

implementazione È

ora a vedere il motivo per cui essa si guasti sulla vostra implementazione, utilizzo:

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

per vedere lo script del linker utilizzato.

Su Ubuntu 15.10, si definisce il simbolo _end alla fine della sezione dati:

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

quindi non c'è da meravigliarsi che l'accesso alla memoria più avanti di esso può segmentation fault.

Problemi correlati