1. saikoro-steak

    No comment

    saikoro-steak
Changes in body
Source | HTML | Preview
@@ -1,113 +1,115 @@
### リンカについて
[ソースコードから実行形式を作成するまでの流れ](http://qiita.com/saikoro-steak/items/a9d1576d9c14706bdde6)
を書いた時にリンカに触れたので、リンカ周りの用語などを自分の言葉でざっとまとめて復習してみた。
### 用語
#### リンカ動作順番
1. 各オブジェクトファイルのセクションをマージ。そしてマージしたセクション同士を意味のある単位でセグメント(ローダーがメモリにロードする単位)にまとめる。
2. 関数、変数の実体が存在する各セクションをメモリのどの番地に配置されるかリンカスクリプトを参照して決める。(再配置)
3. 再配置されて変数、関数のアドレスが決まったのでシンボルテーブルのアドレス欄を埋める。
4. シンボルテーブルを元にオブジェクトファイルの`空欄`(再配置情報)に`アドレスを補充`。 (名前解決=シンボル解決)
5. 実行形式として出力する
#### シンボル
関数名、変数名はシンボルという。
#### オブジェクトファイル .o
アセンブラがアセンぶりされたバイナリでELF形式。.text、.rodata、.data、.bssセクションなどなどのセクションをもつ。なのでアセンブラでは各セクションを作成する役割がある。自分でアセンブラプログラミングする時は明示的に指定するのは.text、.rodata、.data、.bssセクションくらいのはず。ほかはアセンブラが作成してくれるはず。各セクションの情報を持つセクションヘッダをもつ。
#### シンボルテーブル
関数・変数が配置されるメモリ上のアドレスとシンボル名の表。オブジェクトファイルはシンボルを管理するシンボルテーブルを持っている。.symtabセクションに入っている。
実行形式のシンボルテーブル
~~~sh
# nm a.out
0000000000400440 T _start
0000000000601034 b completed.6337
0000000000601030 W data_start
0000000000400470 t deregister_tm_clones
0000000000400500 t frame_dummy
0000000000400530 T main
U printf@@GLIBC_2.2.5
00000000004004a0 t register_tm_clones
~~~
リンク後はmainにアドレスが割り当てられていて、printは共有ライブラリで実行時にアドレスが決定するので空欄。
#### 再配置
-関数や変数その他をメモリ上の番地に割り当てる作業のこと。ただ実際にメモリにロードするのはローダーの役割なので、メモリ上のどの番地に割り当てるかだけを決めてプログラムヘッダに書き込んでいる。ローダーはそれをみて実際にメモリ上にロードする。
+関数や変数その他をメモリ上の番地に割り当てる作業のこと。ただ実際にメモリにロードするのはローダーの役割なので、メモリ上のどの番地に割り当てるかだけを決めてプログラムヘッダに書き込んでいる(リンカスクリプトをみて決める)。ローダーはそれをみて実際にメモリ上にロードする。
#### リンカスクリプト
再配置はリンカスクリプトを参照して行う。メモリマッピングの方法、エントリポイントや各セクションのまとめかたが書かれている。GNUldでは実行時にリンカスクリプトを指定しないとldをビルドする時に組み込まれたデフォルトのリンカスクリプトが利用される。ld --verboseで確認できる。通常何も指定しないので下のデフォルトのリンカスクリプトを利用している。
~~~sh
# ld --verbose
GNU ld version 2.23.52.0.1-30.el7_1.2 20130226
サポートされているエミュレーション:
elf_x86_64
elf32_x86_64
elf_i386
i386linux
elf_l1om
elf_k1om
内部リンカスクリプトを使用しています:
==================================================
/* Script for -z combreloc: combine and sort reloc sections */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
"elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SEARCH_DIR("/usr/x86_64-redhat-linux/lib64"); SEARCH_DIR("/usr/local/lib64"); SEARCH_DIR("/lib64"); SEARCH_DIR("/usr/lib64"); SEARCH_DIR("/usr/x86_64-redhat-linux/lib"); SEARCH_DIR("/usr/lib64"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");
SECTIONS
{
/* Read-only sections, merged into text segment: */
PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
.interp : { *(.interp) }
.note.gnu.build-id : { *(.note.gnu.build-id) }
.hash : { *(.hash) }
.gnu.hash : { *(.gnu.hash) }
+
+
+
~~~
#### 再配置情報
-オブジェクトファイルはリンク前の段階なので変数、関数を参照する箇所は空欄になっている(リンク前でアドレスをキメようがない)。この空欄のことを`再配置情報`と呼ぶ。.relxxx&.relaxxxに入ってる。xxxは各セクション名。この二つは再配置情報の格納方法が異るみたい。
+オブジェクトファイルはリンク前の段階なので変数、関数を参照する箇所は空欄になっている(リンク前でアドレスをキメようがない)。この空欄のことを`再配置情報`と呼ぶ。.relxxx&.relaxxxに入ってる。xxxは各セクション名。この二つは再配置情報の格納方法が異るみたい。
* 再配置情報は空欄ごとにあるので、関数や変数を参照する数だけある。
* `シンボルテーブル`は関数、変数の定義の数だけ。
再配置情報を見てみる
~~~sh
# readelf -r hello.o
再配置セクション '.rela.text' (オフセット 0x600) は 10 個のエントリから構成されています:
オフセット 情報 型 シンボル値 シンボル名 + 加数
000000000023 00050000000a R_X86_64_32 0000000000000000 .rodata + 0
00000000002d 000a00000002 R_X86_64_PC32 0000000000000000 printf - 4
000000000037 00050000000a R_X86_64_32 0000000000000000 .rodata + 0
000000000041 000a00000002 R_X86_64_PC32 0000000000000000 printf - 4
00000000004b 00050000000a R_X86_64_32 0000000000000000 .rodata + 0
000000000055 000a00000002 R_X86_64_PC32 0000000000000000 printf - 4
00000000005f 00050000000a R_X86_64_32 0000000000000000 .rodata + 0
000000000069 000a00000002 R_X86_64_PC32 0000000000000000 printf - 4
00000000006e 00050000000a R_X86_64_32 0000000000000000 .rodata + 3
000000000078 000a00000002 R_X86_64_PC32 0000000000000000 printf - 4
~~~
printfが複数ある。printfを参照している数だけある。
~~~sh
# nm hello.o
0000000000000000 T main
U printf
~~~
シンボルテーブルは定義の数のみ。
#### リンカの実体
[ソースコードから実行形式を作成するまでの流れ](http://qiita.com/saikoro-steak/items/a9d1576d9c14706bdde6)で書いたように、CentOS7のGCCの場合だと`/usr/libexec/gcc/x86_64-redhat-linux/4.8.3/collect2`ただcollcect2はGNUのldである`/usr/bin/ld`を使っている。LinuxではGNUのldが一般的みたい。ただし、リンカはOSに強く依存するものなのでOS毎にリンカが存在している。Microsoftではlink、solarisならsolarisのld。なのでGCCは環境に応じてリンカを使い分けるので、collect2はその共通のラッパーになっている。