2015-11-17 13 views
8

につながる++グラムにおける記号「_end」を使用して、次のC++ソースコードを考えてみましょう。セグメンテーションフォールト

int _end[1050]; 

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

コンパイルライン:g++ main.cpp -o main -O0

このコードを実行するには、GCC-4.8を使用した場合、障害をセグメンテーションにつながります。 4とclang-3.6.0をUbuntu 14.04でリリースしました。奇妙な振る舞いは、シンボル_endが、静的に割り当てられた配列_endの終わりを指し、その先頭ではないことを示しています。 _endend_に置き換えると、すべて正常に動作します。

$ 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) 
:我々は-Sコマンドライン引数を提供することにより、出力にアセンブリコードをgccのを頼む場合

また、「_end」と、他の配列名とバージョンとバージョンの間に有意差は存在しません

しかし、我々は実行可能ファイルをダンプし、それらに対して差分を実行するためにobjdumpの使用している場合、我々は、使用されるアドレスは、さらに必要に応じてより4200 = 4×1050バイトである_endバージョンでそれを見るだろう。

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

限り私は知っている、gccのコンパイラは、variabそれが望むようにアンダースコアで始まるles、i。 e。これはあなたのコードでそのようなシンボルを使うのは悪い習慣です。しかし、私の質問は、本当にここで何が起こるのですか?なぜ_endは、割り当てられた配列の最後のアドレスに置き換えられますか? "-S"コマンドライン引数を使用すると違いはありませんが、実際には作成されたバイナリに違いはありますか?この場合、gccとclangは同じように動作するわけではなく、それは私にとっても不思議です。

答えて

2

_で始まるトークンは予約されているため、使用しないでください。 _endは、Linuxでコンパイルされたプログラム用に定義された外部シンボルで、初期化されていないデータセグメント(BSSセグメントとも呼ばれます)の最後を過ぎた最初のアドレスを表しているようです。

注:_etext、_edata、および_end:一部のシステムでは、これらのシンボルの名前は、このよう アンダースコアが先行しています。

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

+0

まさに私が必要なもの、感謝!しかし、なぜこのコードをコンパイルしている間、 "-S"コマンドライン引数は何も疑わしいものではないのですか? –

+0

@ MaximAkhmedovおそらく '_end'は他のポインタと同様のポインタなので、配列に代入するとポインタの算術演算が行われるからです。 – vsoftco

0

C99 N1256 standard draft 7.1.3 "予約識別子は、" 言う:

下線で始まるすべての識別子は常にファイルスコープ内での識別子として使用するために予約されています通常の名前空間とタグ名空間の両方。

その後、我々はそれを知っている必要があります。

  • ファイルスコープはグローバルです(他の人が機能していると範囲をブロック)
  • 通常のネームスペース変数

が含まので、C99に応じて識別子_endは使用できません。今

あなた実装

それは実際にあなたの実装に失敗した理由を、使用することを確認してください:

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

使用リンカスクリプトを参照してください。

はUbuntuの15.10では、データ部の最後に記号_endを定義:

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

は、ずっと先それのメモリにアクセスするセグメンテーションフォールトことが不思議ではありません。

関連する問題