.dynamic section 提供動態鏈結 (dynamic linking)需要的資訊,其中包含shared library的路徑。
本篇文章參考
oracle dynamic section文件,有興趣的人可以看看。
我會用底下的例子來說明,可以
從這下載所需要的.c和.h檔。這些檔案的內容如下
- Lib_b.c : 會呼叫 libc內的 printf()
- Lib_a.c : 會呼叫 Lib_b.c內的function
- main.c : 會呼叫Lib_a.c的function,本身會產生執行檔
先編譯並反組譯這些檔案 (實驗的環境是 arm cortex-a7 32bits、gcc 4.6.3)。首先產生 Lib_a.o 和 Lib_b.o
$ gcc -g -shared -fPIC Lib_a.c -o Lib_a.o
$ gcc -g -shared -fPIC Lib_b.c -o Lib_b.o
接著產生執行檔
$ gcc -g main.c ./Lib_a.o ./Lib_b.o
然後反組譯 Lib_a.o、Lib_b.o、a.out,來產生 objdump.txt
$ objdump -sSdD a.out > objdump.txt
在解讀前,先偷看解答,看看.dynamic 和 .dynstr 會產生什麼結果,先打底下的指令
$ readelf -d a.out > dynamic_section.txt
dynamic_section.txt裡頭的東西長得就是這樣。除了前面第4行~第6行的shared library (Lib_a.o、Lib_b.o、libc.so.6)還看得懂外,其他的都不知道。不過沒關係,就一步步的看。
1
2 Dynamic section at offset 0x5ec contains 27 entries:
3 Tag Type Name/Value
4 0x00000001 (NEEDED) Shared library: [./Lib_a.o]
5 0x00000001 (NEEDED) Shared library: [./Lib_b.o]
6 0x00000001 (NEEDED) Shared library: [libc.so.6]
7 0x0000000c (INIT) 0x8444
8 0x0000000d (FINI) 0x85c8
9 0x00000019 (INIT_ARRAY) 0x105e0
10 0x0000001b (INIT_ARRAYSZ) 4 (bytes)
11 0x0000001a (FINI_ARRAY) 0x105e4
12 0x0000001c (FINI_ARRAYSZ) 4 (bytes)
13 0x00000004 (HASH) 0x8194
14 0x6ffffef5 (GNU_HASH) 0x81e4
15 0x00000005 (STRTAB) 0x8330
16 0x00000006 (SYMTAB) 0x8240
17 0x0000000a (STRSZ) 174 (bytes)
18 0x0000000b (SYMENT) 16 (bytes)
19 0x00000015 (DEBUG) 0x0
20 0x00000003 (PLTGOT) 0x106ec
21 0x00000002 (PLTRELSZ) 32 (bytes)
22 0x00000014 (PLTREL) REL
23 0x00000017 (JMPREL) 0x8424
24 0x00000011 (REL) 0x841c
25 0x00000012 (RELSZ) 8 (bytes)
26 0x00000013 (RELENT) 8 (bytes)
27 0x6ffffffe (VERNEED) 0x83fc
28 0x6fffffff (VERNEEDNUM) 1
29 0x6ffffff0 (VERSYM) 0x83de
30 0x00000000 (NULL) 0x0
.dynamic section 反組譯後的內容如下,這其實是一個 structure table,存放的是 struct Elf32_Dyn,大小是8 bytes。注意,如果cpu是64bits的話,這個struct就是Elf64_Dyn,而大小則變為16bytes。
$ vim objdump.txt
1
2 a.out: file format elf32-littlearm
...
109 Contents of section .dynamic:
110 105ec 01000000 01000000 01000000 3e000000 ............>...
111 105fc 01000000 48000000 0c000000 44840000 ....H.......D...
112 1060c 0d000000 c8850000 19000000 e0050100 ................
這個struct Elf32_Dyn 可以從 /usr/include/link.h內找到
typedef struct {
Elf32_Sword d_tag;
union {
Elf32_Word d_val;
Elf32_Addr d_ptr;
Elf32_Off d_off;
} d_un;
} Elf32_Dyn;
其中
- d_tag : 表達編碼過的類型,而類型的定義可以考這個文件,以上面第110行的內容為例。
110 105ec 01000000 01000000
0x01表示的類型是 DT_NEEDED,就是需要載入的shared library。
- d_du: 依照d_tag的值來決定union是 d_val, d_ptr, 或是 d_off
- d_val : 表示是值
- d_ptr : 表示地址
第110行的內容為例,因為d_tag是0x1。從文件的定義。可以知道 d_un現在表示的是d_val。
DT_NEEDED
|
1
|
d_val
|
Optional
|
Optional
|
所以0x105f0標示的 0x00000001代表的就是 d_val。
整合上面的內容可以把第 110行的內容轉換成下面的 struct
{
d_tag = 0x1 /* (DT_NEEDED) */
d_val = 0x1 /* (offset of DT_STRTAB) */
}
- Type: NEEDED, STRTAB, and STRSZ
這些值可以幫助我們從反組譯中產生底下的內容
2 Dynamic section at offset 0x5ec contains 27 entries:
3 Tag Type Name/Value
4 0x00000001 (NEEDED) Shared library: [./Lib_a.o]
讓我們先看看這些的定義
- NEEDED: 存放的是DT_STRTAB 表格內的 offset
先看反組譯的內容,可以發現 d_val為1,代表在 DT_STRTAB的offset為 1
110 105ec 01000000 01000000
從"readelf -r"產生的結果去看DT_STRTAB的值如下
15 0x00000005 (STRTAB) 0x8330
反組譯去中可以知道這指到 .dynstr section。裡面存放一堆字串,而且已0x00當作字串結尾。
43 Contents of section .dynstr:
44 8330 002e2f4c 69625f61 2e6f005f 5f676d6f ../Lib_a.o.__gmo
45 8340 6e5f7374 6172745f 5f005f4a 765f5265 n_start__._Jv_Re
46 8350 67697374 6572436c 61737365 7300666f gisterClasses.fo
47 8360 6f005f69 6e697400 5f66696e 69002e2f o._init._fini../
48 8370 4c69625f 622e6f00 6c696263 2e736f2e Lib_b.o.libc.so.
49 8380 36006162 6f727400 5f5f6c69 62635f73 6.abort.__libc_s
有了上面的資訊,可以直接去看 .dynamic section的實際內容了
109 Contents of section .dynamic:
110 105ec 01000000 01000000 01000000 3e000000 ............>...
因為這是32位元的機器,可以把0x105ec這一行共4個words解讀成
第一個struct
{
d_tag = 0x1 (DT_NEEDED)
d_val = 0x1 (offset of DT_STRTAB)
}
第二個struct
{
d_tag = 0x1 (DT_NEEDED)
d_val = 0x33 (offset of DT_STRTAB)
}
先看第一個 struct的 d_val,這個值是 0x1,代表在 .dynstr section的offset 為 0x1。透過查詢可以發現這個字串是"./Lib_a.o" ,請注意尾端是用NULL (0x00) 當字串結尾。
43 Contents of section .dynstr:
44 8330 002e2f4c 69625f61 2e6f005f 5f676d6f ../Lib_a.o.__gmo
45 8340 6e5f7374 6172745f 5f005f4a 765f5265 n_start__._Jv_Re
46 8350 67697374 6572436c 61737365 7300666f gisterClasses.fo
47 8360 6f005f69 6e697400 5f66696e 69002e2f o._init._fini../
48 8370 4c69625f 622e6f00 6c696263 2e736f2e Lib_b.o.libc.so.
49 8380 36006162 6f727400 5f5f6c69 62635f73 6.abort.__libc_s
同樣的,第二個 struct的字串代表的是"./Lib_b.o"
43 Contents of section .dynstr:
44 8330 002e2f4c 69625f61 2e6f005f 5f676d6f ../Lib_a.o.__gmo
45 8340 6e5f7374 6172745f 5f005f4a 765f5265 n_start__._Jv_Re
46 8350 67697374 6572436c 61737365 7300666f gisterClasses.fo
47 8360 6f005f69 6e697400 5f66696e 69002e2f o._init._fini../
48 8370 4c69625f 622e6f00 6c696263 2e736f2e Lib_b.o.libc.so.
49 8380 36006162 6f727400 5f5f6c69 62635f73 6.abort.__libc_s
有興趣的人可以參考英文的定義
DT_NEEDED
The
DT_STRTAB string table offset of a null-terminated string, giving the name of a needed dependency. The dynamic array can contain multiple entries of this type. The relative order of these entries is significant, though their relation to entries of other types is not. See
Shared Object Dependencies.
DT_STRTAB
The address of the string table. Symbol names, dependency names, and other strings required by the runtime linker reside in this table. See
String Table Section.
DT_STRSZ
The total size, in bytes, of the DT_STRTAB string table.
- INIT : 存放 init function
- FINI : 存放 termination function
當process載入或是移除"每個" shared object時,就會呼叫這兩個function。
再回頭看 .dynamic section的值,可以知道init function和termination地址分別是 0x8444和0x85c8
109 Contents of section .dynamic:
110 105ec 01000000 01000000 01000000 3e000000 ............>...
111 105fc 01000000 48000000 0c000000 44840000 ....H.......D...
112 1060c 0d000000 c8850000 19000000 e0050100 ................
再次透過反組譯確認是否是function
424 Disassembly of section .init:
425
426 00008444 <_init>:
427 8444: e92d4008 push {r3, lr}
428 8448: eb000020 bl 84d0 <call_gmon_start>
429 844c: e8bd8008 pop {r3, pc}
560 Disassembly of section .fini:
561
562 000085c8 <_fini>:
563 85c8: e92d4008 push {r3, lr}
564 85cc: e8bd8008 pop {r3, pc}
有興趣的人可以參考英文的定義
DT_INIT
|
12
|
d_ptr
|
Optional
|
Optional
|
DT_FINI
|
13
|
d_ptr
|
Optional
|
Optional
|
DT_INIT
DT_FINI
- Type: INIT_ARRAY, INIT_ARRAYSZ, FINI_ARRAY, and FINI_ARRAYSZ
這四個參數是標記當shared object載入或是卸載時,需要呼叫哪些function。
- DT_INIT_ARRAY : 紀錄 initialization function表格的地址
- DT_INIT_ARRAYSZ : 標注這些function地址一共佔據幾個byte
- DF_FINI_ARRAY : 紀錄termination function表格的地址
- DT_FINI_ARRAYSZ : 紀錄這些地址一共佔據幾個byte
用下面的例子來看這些數值,其中 __attribute__((constructor)) 的作用是在shared object載入前先呼叫的一的function。反之,__attribute((destructor))是在shared object卸載前先呼叫對應的function。
$ vim main.c
#include <stdio.h>
static __attribute__((constructor)) void foo_1()
{
(void) printf("initializing: foo_1()\n");
}
static __attribute__((constructor)) void foo_2()
{
(void) printf("finalizing: foo_2()\n");
}
static __attribute__((destructor)) void bar_1()
{
(void) printf("finalizing: bar_1()\n");
}
static __attribute__((destructor)) void bar_2()
{
(void) printf("finalizing: bar_2()\n");
}
void main()
{
(void) printf("main()\n");
}
接著編譯還有產生反組譯
$ objdump -sSdD main > objdump.txt
這個程式的執行結果如下
$ ./main
initializing: foo_1()
finalizing: foo_2()
main()
finalizing: bar_2()
finalizing: bar_1()
先看 .dynamic section
Dynamic section at offset 0x534 contains 25 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x0000000c (INIT) 0x82cc
0x0000000d (FINI) 0x84a4
0x00000019 (INIT_ARRAY) 0x10518
0x0000001b (INIT_ARRAYSZ) 12 (bytes)
0x0000001a (FINI_ARRAY) 0x10524
0x0000001c (FINI_ARRAYSZ) 12 (bytes)
然後看看 INIT_ARRAY和FINI_ARRAY的內容
91 Contents of section .init_array:
92 10518 98830000 c8830000 e0830000 ............
93 Contents of section .fini_array:
94 10524 7c830000 f8830000 10840000 |...........
其中 INIT_ARRAY中對應到的 function分別為 frame_dummy(), foo_1()和foo_2()。而佔的記憶體大小為12 bytes,和INIT_ARRAYSZ所標示的一樣
340 00008398 <frame_dummy>:
...
354 000083c8 <foo_1>:
...
362 000083e0 <foo_2>:
相同的FINI_ARRAY記載的function為
331 0000837c <__do_global_dtors_aux>:
...
370 000083f8 <bar_1>:
...
378 00008410 <bar_2>:
可以發現,function是照main.c的位置,依序放入array,如果是destructor,就高位元組先執行。
有興趣的人可看英文定義
DT_INIT_ARRAY
|
25
|
d_ptr
|
Optional
|
Optional
|
DT_FINI_ARRAY
|
26
|
d_ptr
|
Optional
|
Optional
|
DT_INIT_ARRAYSZ
|
27
|
d_val
|
Optional
|
Optional
|
DT_FINI_ARRAYSZ
|
28
|
d_val
|
Optional
|
Optional
|
DT_INIT_ARRAY
DT_FINI_ARRAY
DT_INIT_ARRAYSZ
The total size, in bytes, of the DT_INIT_ARRAY array.
DT_FINI_ARRAYSZ
The total size, in bytes, of the DT_FINI_ARRAY array.
這些參數是Symbol table的重要值。之後symbol table會提供 .hash section用
- SYMTAB : 指向 .dynsym section
裡面存放著 struct Elf32_Sym,定義可在 /usr/include/elf.h找到,把struct列出來看看
typedef struct {
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Half st_shndx;
} Elf32_Sym;
先看這些符號的值
16 0x00000006 (SYMTAB) 0x8240
...
18 0x0000000b (SYMENT) 16 (bytes)
可以看出 symbol table的地址是0x8240,每個entry 大小是 16 bytes,先列出幾行看看
27 Contents of section .dynsym:
28 8240 00000000 00000000 00000000 00000000 ................
29 8250 0b000000 00000000 00000000 20000000 ............ ...
30 8260 1a000000 00000000 00000000 20000000 ............ ...
第28行指到 .dynstr的 null
第29行指到 .dynstr的
__gmon_start__,注意這以 0x00 做結尾。且st_info的值為0x2,表示為weak symbol (為初始化的全域變數)
43 Contents of section .dynstr:
44 8330 002e2f4c 69625f61 2e6f005f 5f676d6f ../Lib_a.o.__gmo
45 8340 6e5f7374 6172745f 5f005f4a 765f5265 n_start__._Jv_Re
46 8350 67697374 6572436c 61737365 7300666f gisterClasses.fo
47 8360 6f005f69 6e697400 5f66696e 69002e2f o._init._fini../
48 8370 4c69625f 622e6f00 6c696263 2e736f2e Lib_b.o.libc.so.
49 8380 36006162 6f727400 5f5f6c69 62635f73 6.abort.__libc_s
50 8390 74617274 5f6d6169 6e005f65 64617461 tart_main._edata
51 83a0 005f5f62 73735f73 74617274 005f5f62 .__bss_start.__b
52 83b0 73735f73 74617274 5f5f005f 5f627373 ss_start__.__bss
53 83c0 5f656e64 5f5f005f 5f656e64 5f5f005f _end__.__end__._
54 83d0 656e6400 474c4942 435f322e 3400 end.GLIBC_2.4.
另外可以透過底下指令看symbol table
$ readelf -s a.out
Symbol table '.dynsym' contains 15 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
2: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
3: 00010718 0 NOTYPE GLOBAL DEFAULT 25 _bss_end__
4: 00010714 0 NOTYPE GLOBAL DEFAULT 24 _edata
5: 00000000 0 FUNC GLOBAL DEFAULT UND foo
6: 00010718 0 NOTYPE GLOBAL DEFAULT 25 _end
7: 00010718 0 NOTYPE GLOBAL DEFAULT 25 __bss_end__
8: 00010714 0 NOTYPE GLOBAL DEFAULT 25 __bss_start
9: 00000000 0 FUNC GLOBAL DEFAULT UND abort@GLIBC_2.4 (2)
10: 00008444 0 FUNC GLOBAL DEFAULT 12 _init
11: 00010714 0 NOTYPE GLOBAL DEFAULT 25 __bss_start__
12: 000085c8 0 FUNC GLOBAL DEFAULT 15 _fini
13: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.4 (2)
14: 00010718 0 NOTYPE GLOBAL DEFAULT 25 __end__
定義
DT_SYMENT
The size, in bytes, of the DT_SYMTAB symbol entry.
HASH指向 .hash section
GNU_HASH指向 .gnu.hash section
13 0x00000004 (HASH) 0x8194
14 0x6ffffef5 (GNU_HASH) 0x81e4
DT_HASH
The address of the symbol hash table. This table refers to the symbol table indicated by the
DT_SYMTAB element. See
Hash Table Section.
- Type: PLTGOT and PLTRELSZ
PLTGOT就是紀錄 GOT的地址。在GCC內,PLT就是程式碼,不是table。而PLTRELSZ記錄著GOT的大小
直接看"readelf -d"的結果,GOT table一共占32 bytes,和反組譯的結果相符
20 0x00000003 (PLTGOT) 0x106ec
21 0x00000002 (PLTRELSZ) 32 (bytes)
反組譯結果
126 Contents of section .got:
127 106ec ec050100 00000000 00000000 50840000 ............P...
128 106fc 50840000 50840000 50840000 00000000 P...P...P.......
PLTGOT的定義
An address associated with the procedure linkage table or the global offset table.
PLTRELSZ :
The total size, in bytes, of the relocation entries associated with the procedure linkage table.
- Type: PLTREL, JMPREL, REL, RELA, RELSZ, and RELENT
這幾個符號是用來描述
GOT內的內容。
- PLTREL : 用來標示 relocation 方式
- JMPREL : 存放 .rel.plt section地址。而這個section存放GOT內function的定義
- REL : 存放 .rel.dyn section地址。而這個section存放GOT內data的定義
- RELSZ : REL的大小
- RELENT: REL entry的大小
先從 dynamic section看這些符號的內容。
22 0x00000014 (PLTREL) REL
23 0x00000017 (JMPREL) 0x8424
24 0x00000011 (REL) 0x841c
25 0x00000012 (RELSZ) 8 (bytes)
26 0x00000013 (RELENT) 8 (bytes)
先看 .rel.plt 內第64行的前兩個words
63 Contents of section .rel.plt:
64 8424 f8060100 160d0000 fc060100 16010000 ................
65 8434 00070100 16050000 04070100 16090000 ................
- 第一個word "0x106f8"代表的是需要relocate 的地址,這個地址為在GOT內,關於GOT的介紹在此。
- 第二個word "0x0d16"中的"16"代表 type R_ARM_JUMP_SLOT。而 "0d"代表symbol table的index。至於 "0d"對應到的值是什麼? ,可以透過下面的指令查詢到為 "__libc_start_main"
$ readelf -s a.out
Symbol table for image:
Num Buc: Value Size Type Bind Vis Ndx Name
...
2 0: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
5 0: 00000000 0 FUNC GLOBAL DEFAULT UND foo
1 0: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
...
13 1: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main
...
3 2: 00010718 0 NOTYPE GLOBAL DEFAULT 25 _bss_end__
- 解讀 REL : 使用JMPREL的同樣方式可以解讀為__gmon_start__
61 Contents of section .rel.dyn:
62 841c 08070100 15010000
$ readelf -r a.out
Relocation section '.rel.dyn' at offset 0x41c contains 1 entries:
Offset Info Type Sym.Value Sym. Name
00010708 00000115 R_ARM_GLOB_DAT 00000000 __gmon_start__
Relocation section '.rel.plt' at offset 0x424 contains 4 entries:
Offset Info Type Sym.Value Sym. Name
000106f8 00000d16 R_ARM_JUMP_SLOT 00000000 __libc_start_main
000106fc 00000116 R_ARM_JUMP_SLOT 00000000 __gmon_start__
00010700 00000516 R_ARM_JUMP_SLOT 00000000 foo
00010704 00000916 R_ARM_JUMP_SLOT 00000000 abort
定義 :
PTREL :
Indicates the type of relocation entry to which the procedure linkage table refers, either DT_REL or DT_RELA. All relocations in a procedure linkage table must use the same relocation. This element requires a DT_JMPREL element also be present.
RELA :
The address of a relocation table.
An object file can have multiple relocation sections. When creating the relocation table for an executable or shared object file, the link-editor catenates those sections to form a single table. Although the sections can remain independent in the object file, the runtime linker sees a single table. When the runtime linker creates the process image for an executable file or adds a shared object to the process image, the runtime linker reads the relocation table and performs the associated actions.
This element requires the DT_RELASZ and DT_RELAENT elements also be present. When relocation is mandatory for a file, either DT_RELA or DT_RELcan occur.
REL :
Similar to DT_RELA, except its table has implicit addends. This element requires that the DT_RELSZ and DT_RELENT elements also be present.
RELSZ :
The total size, in bytes, of the DT_REL relocation table.
RELENT :
The size, in bytes, of the DT_REL relocation entry.
JMPREL :
The address of relocation entries that are associated solely with the procedure linkage table. The separation of these relocation entries enables the runtime linker to ignore these entries when the object is loaded with lazy binding enabled. This element requires the DT_PLTRELSZ and DT_PLTREL elements also be present.