ライブラリ、リンカを勉強してみた(Linux)

# ライブラリ、リンカを勉強してみた

ライブラリ、リンカに関してなんとなくしか理解してなかったので勉強してみたときのメモ。

## 用語解説 (ELFでの用語)
#### セクション
リンク時に使われる構造。つまり、オブジェクトファイルに含まれる構造。実行可能ファイルやダイナミックライブラリでは本来不要（デバッグ用に残っている場合が多い？）

#### セグメント
ロード時に使われる構造。オブジェクトファイルには含まれない。実行可能ファイルやダイナミックリンクライブラリには必須。


## 便利コマンドたち
#### `objdump -h`  
セクション情報表示

```
$ objdump -h test.o

test.o:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         0000000e  0000000000000000  0000000000000000  00000040  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .data         00000000  0000000000000000  0000000000000000  0000004e  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000000  0000000000000000  0000000000000000  0000004e  2**0
                  ALLOC
  3 .comment      0000002a  0000000000000000  0000000000000000  0000004e  2**0
                  CONTENTS, READONLY
  4 .note.GNU-stack 00000000  0000000000000000  0000000000000000  00000078  2**0
                  CONTENTS, READONLY
  5 .eh_frame     00000038  0000000000000000  0000000000000000  00000078  2**3
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
```

#### `objdump -p`  
プログラムヘッダ情報表示。つまりセグメント情報表示。
`-pw`にしないと、80文字で折り返されて見辛い。

```
$ objdump -pw a.out

a.out:     file format elf64-x86-64

Program Header:
    PHDR off    0x0000000000000040 vaddr 0x0000000000400040 paddr 0x0000000000400040 align 2**3
         filesz 0x00000000000001f8 memsz 0x00000000000001f8 flags r-x
  INTERP off    0x0000000000000238 vaddr 0x0000000000400238 paddr 0x0000000000400238 align 2**0
         filesz 0x000000000000001c memsz 0x000000000000001c flags r--
    LOAD off    0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**21
         filesz 0x00000000000006e4 memsz 0x00000000000006e4 flags r-x
    LOAD off    0x0000000000000e10 vaddr 0x0000000000600e10 paddr 0x0000000000600e10 align 2**21
         filesz 0x0000000000000228 memsz 0x0000000000000230 flags rw-
 DYNAMIC off    0x0000000000000e28 vaddr 0x0000000000600e28 paddr 0x0000000000600e28 align 2**3
         filesz 0x00000000000001d0 memsz 0x00000000000001d0 flags rw-
    NOTE off    0x0000000000000254 vaddr 0x0000000000400254 paddr 0x0000000000400254 align 2**2
         filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--
EH_FRAME off    0x0000000000000594 vaddr 0x0000000000400594 paddr 0x0000000000400594 align 2**2
         filesz 0x000000000000003c memsz 0x000000000000003c flags r--
   STACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
         filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
   RELRO off    0x0000000000000e10 vaddr 0x0000000000600e10 paddr 0x0000000000600e10 align 2**0
         filesz 0x00000000000001f0 memsz 0x00000000000001f0 flags r--

Dynamic Section:
  NEEDED               libc.so.6
  INIT                 0x00000000004003a8
  FINI                 0x0000000000400584
  INIT_ARRAY           0x0000000000600e10
  INIT_ARRAYSZ         0x0000000000000008
  FINI_ARRAY           0x0000000000600e18
  FINI_ARRAYSZ         0x0000000000000008
  GNU_HASH             0x0000000000400298
  STRTAB               0x0000000000400300
  SYMTAB               0x00000000004002b8
  STRSZ                0x0000000000000038
  SYMENT               0x0000000000000018
  DEBUG                0x0000000000000000
  PLTGOT               0x0000000000601000
  PLTRELSZ             0x0000000000000030
  PLTREL               0x0000000000000007
  JMPREL               0x0000000000400378
  RELA                 0x0000000000400360
  RELASZ               0x0000000000000018
  RELAENT              0x0000000000000018
  VERNEED              0x0000000000400340
  VERNEEDNUM           0x0000000000000001
  VERSYM               0x0000000000400338

Version References:
  required from libc.so.6:
    0x09691a75 0x00 02 GLIBC_2.2.5

```

#### `objdump -S`
ソース付き逆アセンブル。ただし、ソースつけるにはコンパイル時にデバッグ情報つける必要あり(`gcc -g`)。

```
$ objdump -S test.o

test.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <test>:
int test(void) {
   0:	55                   	push   %rbp
   1:	48 89 e5             	mov    %rsp,%rbp
  return 0;
   4:	b8 00 00 00 00       	mov    $0x0,%eax
}
   9:	5d                   	pop    %rbp
   a:	c3                   	retq
```

#### `nm`
シンボルテーブルの表示

#### `readelf -h`
ELFヘッダーの表示

```
$ readelf -h test.o
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          264 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           64 (bytes)
  Number of section headers:         11
  Section header string table index: 8
```

#### `readelf -S`
セクション情報表示
`-SW`にしないと、80文字で折り返されて見辛い。

```
$ readelf -SW a.out
There are 30 section headers, starting at offset 0x1190:

Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        0000000000400238 000238 00001c 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            0000000000400254 000254 000020 00   A  0   0  4
  [ 3] .note.gnu.build-id NOTE            0000000000400274 000274 000024 00   A  0   0  4
  [ 4] .gnu.hash         GNU_HASH        0000000000400298 000298 00001c 00   A  5   0  8
  [ 5] .dynsym           DYNSYM          00000000004002b8 0002b8 000048 18   A  6   1  8
  [ 6] .dynstr           STRTAB          0000000000400300 000300 000038 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          0000000000400338 000338 000006 02   A  5   0  2
  [ 8] .gnu.version_r    VERNEED         0000000000400340 000340 000020 00   A  6   1  8
  [ 9] .rela.dyn         RELA            0000000000400360 000360 000018 18   A  5   0  8
  [10] .rela.plt         RELA            0000000000400378 000378 000030 18   A  5  12  8
  [11] .init             PROGBITS        00000000004003a8 0003a8 00001a 00  AX  0   0  4
  [12] .plt              PROGBITS        00000000004003d0 0003d0 000030 10  AX  0   0 16
  [13] .text             PROGBITS        0000000000400400 000400 000182 00  AX  0   0 16
  [14] .fini             PROGBITS        0000000000400584 000584 000009 00  AX  0   0  4
  [15] .rodata           PROGBITS        0000000000400590 000590 000004 04  AM  0   0  4
  [16] .eh_frame_hdr     PROGBITS        0000000000400594 000594 00003c 00   A  0   0  4
  [17] .eh_frame         PROGBITS        00000000004005d0 0005d0 000114 00   A  0   0  8
  [18] .init_array       INIT_ARRAY      0000000000600e10 000e10 000008 00  WA  0   0  8
  [19] .fini_array       FINI_ARRAY      0000000000600e18 000e18 000008 00  WA  0   0  8
  [20] .jcr              PROGBITS        0000000000600e20 000e20 000008 00  WA  0   0  8
  [21] .dynamic          DYNAMIC         0000000000600e28 000e28 0001d0 10  WA  6   0  8
  [22] .got              PROGBITS        0000000000600ff8 000ff8 000008 08  WA  0   0  8
  [23] .got.plt          PROGBITS        0000000000601000 001000 000028 08  WA  0   0  8
  [24] .data             PROGBITS        0000000000601028 001028 000010 00  WA  0   0  8
  [25] .bss              NOBITS          0000000000601038 001038 000008 00  WA  0   0  1
  [26] .comment          PROGBITS        0000000000000000 001038 00004d 01  MS  0   0  1
  [27] .shstrtab         STRTAB          0000000000000000 001085 000108 00      0   0  1
  [28] .symtab           SYMTAB          0000000000000000 001910 000630 18     29  46  8
  [29] .strtab           STRTAB          0000000000000000 001f40 000231 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)
```

#### readelf -l
プログラムヘッダ表示。`-lW`にしないと80文字で折り返されて見辛い。


```
$ objdump -pw a.out

a.out:     file format elf64-x86-64

Program Header:
    PHDR off    0x0000000000000040 vaddr 0x0000000000400040 paddr 0x0000000000400040 align 2**3
         filesz 0x00000000000001f8 memsz 0x00000000000001f8 flags r-x
  INTERP off    0x0000000000000238 vaddr 0x0000000000400238 paddr 0x0000000000400238 align 2**0
         filesz 0x000000000000001c memsz 0x000000000000001c flags r--
    LOAD off    0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**21
         filesz 0x00000000000006e4 memsz 0x00000000000006e4 flags r-x
    LOAD off    0x0000000000000e10 vaddr 0x0000000000600e10 paddr 0x0000000000600e10 align 2**21
         filesz 0x0000000000000228 memsz 0x0000000000000230 flags rw-
 DYNAMIC off    0x0000000000000e28 vaddr 0x0000000000600e28 paddr 0x0000000000600e28 align 2**3
         filesz 0x00000000000001d0 memsz 0x00000000000001d0 flags rw-
    NOTE off    0x0000000000000254 vaddr 0x0000000000400254 paddr 0x0000000000400254 align 2**2
         filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--
EH_FRAME off    0x0000000000000594 vaddr 0x0000000000400594 paddr 0x0000000000400594 align 2**2
         filesz 0x000000000000003c memsz 0x000000000000003c flags r--
   STACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
         filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
   RELRO off    0x0000000000000e10 vaddr 0x0000000000600e10 paddr 0x0000000000600e10 align 2**0
         filesz 0x00000000000001f0 memsz 0x00000000000001f0 flags r--

Dynamic Section:
  NEEDED               libc.so.6
  INIT                 0x00000000004003a8
  FINI                 0x0000000000400584
  INIT_ARRAY           0x0000000000600e10
  INIT_ARRAYSZ         0x0000000000000008
  FINI_ARRAY           0x0000000000600e18
  FINI_ARRAYSZ         0x0000000000000008
  GNU_HASH             0x0000000000400298
  STRTAB               0x0000000000400300
  SYMTAB               0x00000000004002b8
  STRSZ                0x0000000000000038
  SYMENT               0x0000000000000018
  DEBUG                0x0000000000000000
  PLTGOT               0x0000000000601000
  PLTRELSZ             0x0000000000000030
  PLTREL               0x0000000000000007
  JMPREL               0x0000000000400378
  RELA                 0x0000000000400360
  RELASZ               0x0000000000000018
  RELAENT              0x0000000000000018
  VERNEED              0x0000000000400340
  VERNEEDNUM           0x0000000000000001
  VERSYM               0x0000000000400338

Version References:
  required from libc.so.6:
    0x09691a75 0x00 02 GLIBC_2.2.5

```

#### hexdump -C
```
$ hexdump -C a.out | head
00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 3e 00 01 00 00 00  00 04 40 00 00 00 00 00  |..>.......@.....|
00000020  40 00 00 00 00 00 00 00  90 11 00 00 00 00 00 00  |@...............|
00000030  00 00 00 00 40 00 38 00  09 00 40 00 1e 00 1b 00  |....@.8...@.....|
00000040  06 00 00 00 05 00 00 00  40 00 00 00 00 00 00 00  |........@.......|
00000050  40 00 40 00 00 00 00 00  40 00 40 00 00 00 00 00  |@.@.....@.@.....|
00000060  f8 01 00 00 00 00 00 00  f8 01 00 00 00 00 00 00  |................|
00000070  08 00 00 00 00 00 00 00  03 00 00 00 04 00 00 00  |................|
00000080  38 02 00 00 00 00 00 00  38 02 40 00 00 00 00 00  |8.......8.@.....|
00000090  38 02 40 00 00 00 00 00  1c 00 00 00 00 00 00 00  |8.@.............|

```

##### file
ファイルの情報表示

```
$ file test.o
test.o: ELF 64-bit LSB  relocatable, x86-64, version 1 (SYSV), not stripped
```

#### strip
シンボルテーブル削除する。


## ELFファイルの構造
|リンク前のファイル|
|---|
|ELFヘッダ|
|セクション1|
|セクション2|
|...|
|セクションヘッダテーブル|


|リンク後のファイル|
|---|
|ELFヘッダ|
|プログラムヘッダテーブル(つまりセグメントテーブル)|
|セグメント1|
|セグメント2|
|...|
|セクションヘッダテーブル(なくてもよい)|

## リンク動作
* .oの場合
 * .oを直接リンクした場合は必ずリンクされる(未使用関数、未使用変数も全部リンクされる)
* .aの場合
 * .aは、単純に.oをまとめたものである。
 * リンク時にはリンカは「.o単位」で使用、未使用をチェックする。全く未使用な.oはリンクされない。
 * リンカは引数の左から順に確認し、確認時点で未使用の.oはリンクしない。
 * そのため、libtest1.a内の関数がlibtest2.a内の関数を呼び出している場合は、`-ltest1 -ltest2` の順に指定する必要がある。逆にすると、`-ltest2`の評価時点ではlibtest2.aが呼び出されていることをリンカが知らないので、該当する.oのリンクが行われない可能性がある。
 * ややこしいので、混乱を避けたかったら１個の.aにまとめちゃう手もあり。
 * `-ltest1 -ltest2 -ltest1`のように繰り返すのもOK。
 * もしくは.soにしてしまう手もあり。
 


## おまけ
macでgccしてもELFでは出力されないのでmacで実験してもうまくいきません。。。
`brew install binutils`すればgobjedumpやgreadelfをインストールできるがそれだけではダメでした。