この質問から、コンパイルして(警告はありますが)セグメンテーション違反(gcc 4.4.4; clang 2.8)を生成する面白いコードを見ました:
main;
展開すると、結果は次のようになります。
int main = 0;
では、ここでのリンカーの動作は何ですか?
この質問から、コンパイルして(警告はありますが)セグメンテーション違反(gcc 4.4.4; clang 2.8)を生成する面白いコードを見ました:
main;
展開すると、結果は次のようになります。
int main = 0;
では、ここでのリンカーの動作は何ですか?
main
リンカの動作は、プログラムのデータまたはBSSセグメントのいずれかで呼び出されるシンボルを定義することです。.text
長さは4バイトで、0に初期化されます。通常、関数の実行可能コードを使用して、プログラムのコードセグメント(通常は)にシンボルを作成しmain
ます。
Cランタイムは、固定のエントリポイント(通常はと呼ばれ_start
ます)で起動し、一連のものを初期化し(たとえば、プログラムの引数を設定します)、main
関数を呼び出します。が実行可能コードの場合main
、これはすべて問題なくダンディですが、代わりに4つのゼロバイトの場合、プログラムはそれらのゼロバイトに制御を移して実行しようとします。
通常、データセグメントとBSSセグメントは実行不可としてマークされているため、そこでコードを実行しようとすると、プロセッサが例外を発生させ、OSがそれを解釈して、プログラムをシグナルで終了します。どういうわけかそれが含まれているセグメントが実行可能である場合、それはで定義されたマシン命令を実行しようとし00 00 00 00
ます。x86およびx86-64では、これは不正な命令であるため、POSIXOSでもSIGILL
シグナルを受け取ります。
シンボルmain
は、整数ではなく関数であることが期待されています。main
ただし、リンカは;のタイプをあまり気にしません。シンボルが定義されています。シンボルmain
が規定のシグニチャのいずれかを持つ関数でない場合は、未定義の動作を呼び出します。
次に、起動コードは「関数」を呼び出します。これは、実際にはプログラムのデータセグメント内のアドレスです。そのアドレスに格納されている「コード」が無効であるため、問題が発生します。最初の4バイトはゼロである可能性があります。後で来るのは誰かの推測です。データセグメントは実行不可とマークされている場合があります。その場合、データを実行しようとすると、そのアカウントでクラッシュが発生します。
未定義の動作を呼び出すと、何が起こる可能性があります。ここではクラッシュが非常に賢明です。
私のシステム(CentOS 6.3)では、mainはBSSに配置され、すべて0が含まれているため、クラッシュします。
Program received signal SIGSEGV, Segmentation fault.
0x00000000006007f0 in main ()
(gdb) where
#0 0x00000000006007f0 in main ()
(gdb) l
"main" is not a function
(gdb) disass 0x6007f0
Dump of assembler code for function main:
=> 0x00000000006007f0 <+0>: add %al,(%rax)
0x00000000006007f2 <+2>: add %al,(%rax)
End of assembler dump.
(gdb) info symbol &main
main in section .bss of /home/ajd/tmp/x
(gdb) x/16b 0x6007f0
0x6007f0 <main>: 0 0 0 0 0 0 0 0
0x6007f8: 0 0 0 0 0 0 0 0
(gdb)