- 論壇徽章:
- 0
|
本帖最后由 kernel359 于 2013-12-25 21:23 編輯
以下主要是參照《深入理解計(jì)算機(jī)系統(tǒng)》的理解。
以下是在centos 6.3 x86_64上測(cè)試的
主要用到的命令有 readelf、objdump,它們是GNU提供的,我這系統(tǒng)自帶的有,所以沒(méi)安裝
一個(gè)可執(zhí)行目標(biāo)文件,從原代碼,要經(jīng)歷預(yù)處理器、編譯器、匯編器和加載器,才會(huì)加載到內(nèi)存中執(zhí)行。而目標(biāo)文件,分為可重定位文件和可執(zhí)行文件,目標(biāo)中分成不同的節(jié)。
節(jié)
| ELF頭
| 描述字的大小、生成該文件的系統(tǒng)的字節(jié)順序、幫助鏈接解析和解釋目標(biāo)文件的信息(ELF頭的大小、目標(biāo)文件類(lèi)型--可重定位/可執(zhí)行/共享/機(jī)器類(lèi)型/節(jié)頭部表的文件偏移/節(jié)頭部表中表目大小和數(shù)量)
| .text
| 編譯完后的機(jī)器碼
| .rodata
| 只讀數(shù)據(jù),如printf中的格式串和switch中的跳轉(zhuǎn)表
| .data
| 已初始化的全局C變量
| .bss
| 未初始化的全局C變量
| .symtab
| 符號(hào)表,存放在本文件中被定義和引用的函數(shù)和全局變量(此全局變量,包含自己定義全局變量和函數(shù),即使帶有static也包含,是在符號(hào)后加了個(gè)數(shù)字,另外還包含本文件中引用的其它文件的全局變量和函數(shù),即使這個(gè)函數(shù)沒(méi)有在外面用extern引用--變量不引用則編譯報(bào)錯(cuò)),不包含局部變量(程序運(yùn)行時(shí),在棧中生成)
| .rel.text
| 可重定位的代碼,一般是調(diào)用的外部函數(shù)或者引用全局的變量的指令,引用本地的不需要改
| .rel.data
| 本模塊定義或引用的全局變量
| .debug
| 調(diào)試符號(hào)表
| .line
| 原始C源程序中的等號(hào)和.text節(jié)中機(jī)器指令間的映射
| .strtab
| 字符串表。包含.symtab和.debug節(jié)中的符號(hào)表,以及節(jié)頭部中的節(jié)名字
| 描述目標(biāo)文件
| 節(jié)頭部表
| 由相同大小的表目組成,每個(gè)表目描述上面的一個(gè)節(jié)
|
1.編寫(xiě)一個(gè)test.c- #include <stdio.h>
- int x = 3 ;
- int y ;
- extern int e_g_z;
- static int s_g_x = 3 ;
- static int s_g_y ;
- int g_f(); //如果些函數(shù),只在這聲明,而沒(méi)在main中被用到,那么在ELF文件的符號(hào)表(.symtab)中沒(méi)有它
- int n_s_f(){ return 0 ;} //no static
- static int s_f(){ return 0 ; } // static function
- int main() {
- int l_x = 4 ;
- int l_y = 5 ;
- static int s_l_x = 6 ;
- static int s_l_y = 7 ;
- g_f();
- f() ;
- e_g_z = 4;
- printf("%d %d %d %d %d %d %d %d\n",x,y,s_g_x,s_g_y,l_x,l_y,s_l_x,s_l_y);
- return 0 ;
- }
復(fù)制代碼 2.編譯生成test.o目標(biāo)文件
gcc -g -c test.c
3.查看test.o的符號(hào)表(.symtab節(jié)的內(nèi)容)
readelf -s test.o //可以用readelf -a test.o看到所有的節(jié)
symtab.png (77.07 KB, 下載次數(shù): 143)
下載附件
2013-12-18 21:46 上傳
注: 符號(hào)表中包含了各個(gè)節(jié)、源文件名字、非靜態(tài)本全局變量、靜態(tài)全局變量、非靜態(tài)全局函數(shù)、靜態(tài)全局函數(shù)、函數(shù)代碼段內(nèi)部引用的其它模塊函數(shù)、被代碼段引用的全局變量、靜態(tài)局部變量(在.data或.bss中定義空間)。 只是無(wú)局部非靜態(tài)變量,它在棧中管理,鏈接器對(duì)它不管。它的value是地址,但由于是在鏈接的時(shí)候才分配,所以此處全為0,OBJECT表示變量。用readelf -S test.o,還可以看到有一欄Addr也全為0,它表示還沒(méi)有給變量和指令分配運(yùn)行時(shí)地址(在鏈接時(shí)分配)。
.symtab這個(gè)節(jié)是一個(gè)包含表目的數(shù)組,表目結(jié)構(gòu)如下:- typedef struct {
- int name ; /* 字符串節(jié)中,字節(jié)的偏移,以null結(jié)尾 */
- int value; /* section offset or VM address,可重定位模塊,此符號(hào)在它的定義節(jié)中的偏移,可執(zhí)行模塊,則為一個(gè)絕對(duì)運(yùn)行地址 */
- int size; /* 目標(biāo)字節(jié)大小 */
- char type:4, /* 表示此符號(hào)是數(shù)據(jù)、函數(shù)、節(jié)以及原文件路徑名 */
- binding:4; /*本地,還是全局 */
- char reserved; /* unused */
- char section; /* 節(jié)索引,表示此符號(hào)與哪個(gè)節(jié)相關(guān)聯(lián),
復(fù)制代碼 4.符號(hào)解析
鏈接器解析符號(hào)引用的是將每個(gè)引用與它輸入的可重定位目標(biāo)文件的符號(hào)表中的一個(gè)確定的符號(hào)聯(lián)系起來(lái),對(duì)引用和定義在同一模塊中的符號(hào),則解析更為簡(jiǎn)單。
對(duì)于多處定義的同名全局符號(hào)(即使類(lèi)型不同),依據(jù)強(qiáng)弱來(lái)決定到底用哪個(gè)(函數(shù)和已初始化的全局變量和強(qiáng)符號(hào),未初始化的全局變量為弱符號(hào))。
在Unix鏈接時(shí),
1、不允許多個(gè)強(qiáng)符號(hào)出現(xiàn)
2、如果有一個(gè)強(qiáng)符號(hào),多個(gè)弱符號(hào),則選強(qiáng)符號(hào)。
3、如果是多個(gè)弱符號(hào),那么從這些弱符號(hào)中任意選一個(gè)。
如:a.c b.c
int x = 1111; double x ;
int y = 2222; void f() { x = -1 ;}
f();
printf("x = 0x%x \ny = 0x%x\n",x,y);
則輸出:x = 0x0,y = 0xbff00000,這是由于int與double據(jù)占的字節(jié)數(shù)不同,導(dǎo)致賦值x=-1時(shí),在x和y的存儲(chǔ)區(qū),都被賦了值。
鏈接靜態(tài)庫(kù):定義三個(gè)集合,一個(gè)可重定位目標(biāo)文件集合E,它們最后形成一個(gè)可執(zhí)行文件;未解析的符號(hào)(引用了但沒(méi)定義)集合U,前面輸入文件中已定義的符號(hào)集合D,最開(kāi)始三個(gè)集合都為空。
輸入一個(gè)文件,如果為目標(biāo)文件,則同時(shí)更新這三個(gè)集合,如果是庫(kù)文件,則看是否有定義了U集合中的符號(hào),如果有則同時(shí)更新這三個(gè)集合,否則丟棄它,所有過(guò)程之后,如果U是空的,則鏈接成功,否則失敗(與輸入文件的順序有關(guān))。
重定位:將輸入目標(biāo)文件中相同的節(jié)合并,如所有的.data節(jié),合并成一個(gè)大的、可執(zhí)行文件的.data節(jié),所有節(jié)合并完后,給它們一人執(zhí)行地址,然后修改符號(hào)引用,使其能找到正確的符號(hào)定義位置。
查看test.o的重定位表:readelf -r test.o
relText.png (38.46 KB, 下載次數(shù): 147)
下載附件
2013-12-23 21:50 上傳
對(duì)比符號(hào)表中的全局符號(hào)
sy.png (24 KB, 下載次數(shù): 140)
下載附件
2013-12-23 21:52 上傳
除了n_s_f和main以外,其它全局符號(hào)都是可重定位的。
函數(shù):所有引用的其它模塊的函數(shù)是都是可重定位的;本模塊中實(shí)現(xiàn)的函數(shù)是不需要重定位的,無(wú)論帶不帶static
變量:本模塊定義全局的(不帶static)和引用其它模塊的,都要重定位,本局部的在運(yùn)行時(shí)的棧中,帶static的全局,也只在本模塊中用,不需要重定位,但其它模塊可以用指針引用到。
重定位節(jié)中,每個(gè)表目,可以是以下兩種格式之一:
每個(gè)表目表示一個(gè)需要重定位的位置,比如一引用的外部變量 Z ,在代碼段用到了三次,則在重定位節(jié)中,有至少(因?yàn)榫幾g成匯編的時(shí)候,有可能是多條指令)三個(gè)關(guān)于 Z 的重定位表目。- typedef struct
- {
- Elf32_Addr r_offset; /* Address */
- Elf32_Word r_info; /* Relocation type and symbol index */
- } Elf32_Rel;
- typedef struct
- {
- Elf32_Addr r_offset; /* Address */
- Elf32_Word r_info; /* Relocation type and symbol index */
- Elf32_Sword r_addend; /* Addend */
- } Elf32_Rela;
復(fù)制代碼 這兩種格式唯一的不同是成員r_addend。這個(gè)成員一般是個(gè)常量,用來(lái)輔助計(jì)算修訂值。如果使用了第一種格式,那么r_addend將被填充在引用外部符號(hào)的地址處,也就是前面所說(shuō)的留“空”處。具體的體系結(jié)構(gòu)可以選擇適合自己的一種格式,或者兩種格式都使用,只不過(guò)在不同的上下文中使用更合適的格式。IA32主要使用了前者,但是也在個(gè)別的情況下了使用了一點(diǎn)后者。
r_offset為需要重定位的符號(hào)在目標(biāo)文件中的偏移。需要注意的是,對(duì)于目標(biāo)文件與可執(zhí)行文件或者動(dòng)態(tài)庫(kù),這個(gè)值是不同的。對(duì)于目標(biāo)文件,r_offset是相對(duì)于段的,是段內(nèi)偏移;而對(duì)于可執(zhí)行文件或者動(dòng)態(tài)庫(kù),r_offset是虛擬地址。
r_info中包含重定位類(lèi)型和此處引用的外部符號(hào)在符號(hào)表中的索引。根據(jù)符號(hào)在符號(hào)表中的索引,鏈接器就可以從符號(hào)表中解析出符號(hào)的地址。因?yàn)橹噶钪邪喾N不同的尋址方式,并且還要針對(duì)不同的情況,所以有多種不同的重定位類(lèi)型。不同的重定位類(lèi)型,重定位的方法也不同。
------------------出自:http://book.2cto.com/201309/33391.html引自: http://book.2cto.com/201309/33395.html鏈接時(shí),在第一階段完成后,目標(biāo)文件已經(jīng)合并完成,并且已經(jīng)為符號(hào)分配了運(yùn)行時(shí)地址,鏈接器將進(jìn)行符號(hào)重定位。
模塊hello.o中有兩處需要重定位,一處是偏移0xb處的變量foo2,另外一處是偏移0x1b處的函數(shù)foo2_func。匯編器已經(jīng)將這兩處需要重定位的符號(hào)記錄在了重定位表中。
root@baisheng:~/demo# readelf -r hello.o
Relocation section '.rel.text' at offset 0x3c8 contains 2 entries:
Offset Info Type Sym.Value Sym. Name
0000000b 00000901 R_386_32 00000000 foo2
0000001b 00000a02 R_386_PC32 00000000 foo2_func
...
符號(hào)foo2的重定位類(lèi)型是R_386_32,ELF標(biāo)準(zhǔn)規(guī)定的計(jì)算修訂值的公式是:
S + A
其中,S表示符號(hào)的運(yùn)行時(shí)地址,A就是匯編器填充在引用外部符號(hào)處的Addend。
符號(hào)foo2_func的重定位類(lèi)型是R_386_PC32,ELF標(biāo)準(zhǔn)規(guī)定的計(jì)算修訂值的公式是:
S + A - P
其中S、A的意義與前面完全相同,P為修訂處的運(yùn)行時(shí)地址或者偏移。對(duì)于目標(biāo)文件,P為修訂處在段內(nèi)的偏移。對(duì)于可執(zhí)行文件和動(dòng)態(tài)庫(kù),P為修訂處的運(yùn)行時(shí)地址。
首先我們先來(lái)確定S。運(yùn)行時(shí)地址在鏈接時(shí)才分配,因此,變量foo2和函數(shù)foo2_func的運(yùn)行時(shí)地址在鏈接后的可執(zhí)行文件hello的符號(hào)表中:
root@baisheng:~/demo# readelf -s hello | grep foo2
38: 00000000 0 FILE LOCAL DEFAULT ABS foo2.c
53: 0804a020 4 OBJECT GLOBAL DEFAULT 24 foo2
68: 08048414 16 FUNC GLOBAL DEFAULT 13 foo2_func
可見(jiàn),符號(hào)foo2的運(yùn)行時(shí)地址為0x0804a020,符號(hào)foo2_func的運(yùn)行時(shí)地址是0x08048414。
接下來(lái),我們?cè)賮?lái)看看匯編器為這兩個(gè)符號(hào)填充的Addend是多少。我們使用工具objdump反匯編hello.o,其中黑體標(biāo)識(shí)的分別是匯編器在引用foo2和foo2_func的地址處填充的Addend:
root@baisheng:~/demo# objdump -d hello.o
hello.o: file format elf32-i386
Disassembly of section .text:
00000000 <main>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 e4 f0 and $0xfffffff0,%esp
6: 83 ec 10 sub $0x10,%esp
9: c7 05 00 00 00 00 05 movl $0x5,0x0
10: 00 00 00
13: c7 04 24 32 00 00 00 movl $0x32,(%esp)
1a: e8 fc ff ff ff call 1b <main+0x1b>
1f: b8 00 00 00 00 mov $0x0,%eax
24: c9 leave
25: c3 ret
根據(jù)輸出可見(jiàn),匯編器在引用符號(hào)foo2處填充的Addend是0,在引用符號(hào)foo2_func處填充的Addend是–4。
于是,可執(zhí)行文件hello中引用符號(hào)foo2的位置的修訂值為:
S + A = 0x0804a020 + 0 = 0x0804a020
我們反匯編可執(zhí)行文件hello,來(lái)驗(yàn)證一下引用符號(hào)foo2處的值是否修訂為我們計(jì)算的這個(gè)值:
root@baisheng:~/demo# objdump -d hello
hello: file format elf32-i386
...
080483dc <main>:
80483dc: 55 push %ebp
80483dd: 89 e5 mov %esp,%ebp
80483df: 83 e4 f0 and $0xfffffff0,%esp
80483e2: 83 ec 10 sub $0x10,%esp
80483e5: c7 05 20 a0 04 08 05 movl $0x5,0x804a020
80483ec: 00 00 00
80483ef: c7 04 24 32 00 00 00 movl $0x32,(%esp)
80483f6: e8 19 00 00 00 call 8048414
<foo2_func>
80483fb: b8 00 00 00 00 mov $0x0,%eax
8048400: c9 leave
8048401: c3 ret
8048402: 66 90 xchg %ax,%ax
...
注意偏移0x1b處,確實(shí)已經(jīng)被鏈接器修訂為0x0804a020了。
對(duì)于符號(hào)foo2_func的修訂值,還需要變量P,即引用符號(hào)foo2_func處的運(yùn)行時(shí)地址。根據(jù)可執(zhí)行文件hello的反匯編代碼可見(jiàn),引用符號(hào)foo2_func的指令的地址是:
0x80483f6 + 1 = 0x80483f7
所以,可執(zhí)行文件hello中引用符號(hào)foo2_func的位置的修訂值為:
S + A – P = 0x08048414 + (-4) - 0x80483f7 = 0x19
觀察hello的反匯編代碼,從地址0x80483f7開(kāi)始處的4字節(jié),確實(shí)也已經(jīng)被鏈接器修訂為0x19。
這里提醒一下讀者,如果foo2_func占據(jù)的運(yùn)行時(shí)地址小于main函數(shù),那么這里foo2_func與PC的相對(duì)地址將是負(fù)數(shù)。在機(jī)器指令中,使用的是數(shù)的補(bǔ)碼形式,所以一定要注意,以免造成困惑。
事實(shí)上,對(duì)于符號(hào)foo2使用的重定位類(lèi)型R_386_32,是絕對(duì)地址重定位,鏈接器只要解析符號(hào)foo2的運(yùn)行時(shí)地址替換修訂處即可。而對(duì)于符號(hào)foo2_func,其使用的重定位類(lèi)型是R_386_PC32,這是一個(gè)PC相對(duì)地址重定位。而當(dāng)執(zhí)行當(dāng)前指令時(shí),PC中已經(jīng)加載了下一條指令的地址,并不是當(dāng)前指令的地址,這就是在引用符號(hào)foo2_func處填充“–4”的原因。
我們看到,在鏈接時(shí),鏈接器在需要重定位的符號(hào)所在的偏移處直接進(jìn)行了編輯修訂,所以人們通常也將鏈接器形象地稱(chēng)為“l(fā)ink editor”。
對(duì)于重定位的符號(hào),當(dāng)新地址確定后,新地址將填充到重定位表目中指定的offset處。
合并文件:
合并文件是在鏈接時(shí),將所有的目標(biāo)文件,把相同的節(jié)合成一個(gè)大節(jié),最終形成的就是一個(gè)目標(biāo)文件,合并完成后,再進(jìn)行重定位。
但在合并時(shí),由于編譯器會(huì)加入一些其它的目標(biāo)文件,所以導(dǎo)致最后可執(zhí)行文件的某些節(jié),大于原來(lái)目標(biāo)文件對(duì)應(yīng)節(jié)的和,這個(gè)大于還有一個(gè)原因是由于指令等的對(duì)齊方式不同,需要填充造成的。
6.靜態(tài)鏈接庫(kù)
將各個(gè)目標(biāo)文件打包成一個(gè) .a 文件,在編譯時(shí),將目標(biāo)文件所需要的目標(biāo)文件加入到可最后的可執(zhí)行文件中。
7.動(dòng)態(tài)鏈接庫(kù)
動(dòng)態(tài)鏈接庫(kù),在編譯可執(zhí)行文件時(shí),必須能找得到,才能編譯成功
在可執(zhí)行文件加載時(shí),動(dòng)態(tài)鏈接庫(kù)文件的目錄可以改變位置,但也必須能找得到,可用ldd test找到test需要哪些動(dòng)態(tài)庫(kù)
readelf -r test 可以查看到動(dòng)態(tài)鏈接庫(kù)的重定位節(jié).rel.dyn 表示全局變量,.rel.plt重定位函數(shù)。
執(zhí)行程序鏈接到動(dòng)態(tài)庫(kù),是因?yàn)橛袃缮蟼(gè)表,GOT在.data節(jié),PLT在.text節(jié),前者在第一次動(dòng)態(tài)庫(kù)中的函數(shù)被調(diào)用時(shí),由動(dòng)態(tài)鏈接器獲得其地址,并保存,在以后的調(diào)用中,只需要從中讀取就可以;后面主要是兩條跳轉(zhuǎn)指令,如jmp *0x888888,其中0x888888是GOT中表目的地址,它保存庫(kù)函數(shù)的地址,這樣就可以通過(guò)PLT,直接跳轉(zhuǎn)到庫(kù)函數(shù)中。
引自:http://book.2cto.com/201309/33397.html
我們知道,與靜態(tài)庫(kù)不同,動(dòng)態(tài)庫(kù)不會(huì)在可執(zhí)行文件中有任何副本,那么為什么編譯鏈接時(shí)依然需要指定動(dòng)態(tài)庫(kù)呢?原因包括下面幾點(diǎn):
1)動(dòng)態(tài)加載器需要知道可執(zhí)行程序依賴(lài)的動(dòng)態(tài)庫(kù),這樣在加載可執(zhí)行程序時(shí)才能加載其依賴(lài)的動(dòng)態(tài)庫(kù)。所以,在鏈接時(shí),鏈接器將根據(jù)可執(zhí)行程序引用的動(dòng)態(tài)庫(kù)中的符號(hào)的情況在dynamic段中記錄可執(zhí)行程序依賴(lài)的動(dòng)態(tài)庫(kù)。我們使用如下命令將foo1.c和foo2.c編譯為動(dòng)態(tài)庫(kù),并將hello鏈接到動(dòng)態(tài)庫(kù)libfoo.so。
root@baisheng:~/demo# gcc -shared -fPIC foo1.c foo2.c -o libfoo.so
root@baisheng:~/demo# gcc hello.c -o hello -L./ -lfoo
我們來(lái)查看hello中的dynamic段:
root@baisheng:~/demo# readelf -d hello | grep Shared
0x00000001 (NEEDED) Shared library: [libfoo.so]
0x00000001 (NEEDED) Shared library: [libc.so.6]
顯然,在dynamic段中,記錄了hello依賴(lài)的動(dòng)態(tài)鏈接庫(kù)libfoo.so。
2)鏈接器需要在重定位表中創(chuàng)建重定位記錄(Relocation Record),這樣當(dāng)動(dòng)態(tài)鏈接器加載hello時(shí),將依據(jù)重定位記錄重定位hello引用的這些外部符號(hào)。重定位記錄存儲(chǔ)在ELF文件的重定位段(Relocation)中,ELF文件中可能有多個(gè)段包含需要重定位的符號(hào),所以可能會(huì)包含多個(gè)重定位段。以hello的重定位段為例:
root@baisheng:~/demo# readelf -r hello
Relocation section '.rel.dyn' at offset 0x3d4 contains 2 entries:
Offset Info Type Sym.Value Sym. Name
08049ffc 00000206 R_386_GLOB_DAT 00000000 __gmon_start__
0804a020 00000905 R_386_COPY 0804a020 foo2
Relocation section '.rel.plt' at offset 0x3e4 contains 3 entries:
Offset Info Type Sym.Value Sym. Name
0804a00c 00000207 R_386_JUMP_SLOT 00000000 __gmon_start__
0804a010 00000307 R_386_JUMP_SLOT 00000000 __libc_start_main
0804a014 00000507 R_386_JUMP_SLOT 00000000 foo2_func
根據(jù)輸出可見(jiàn),可執(zhí)行文件hello包含兩個(gè)重定位段,“.rel.dyn”段中記錄的是加載時(shí)需要重定位的變量,“.rel.plt”段中記錄的是需要重定位的函數(shù)。
因此,雖然編譯時(shí)不需要鏈接共享庫(kù),但是可執(zhí)行文件中需要記錄其依賴(lài)的共享庫(kù)以及加載/運(yùn)行時(shí)需要重定位的條目,在加載程序時(shí),動(dòng)態(tài)加載器需要這些信息來(lái)完成加載時(shí)重定位。
最后我們?cè)賮?lái)關(guān)注一下在hello中的全局符號(hào)foo2和foo2_func。
root@baisheng:~/demo# nm hello | grep foo
0804a020 B foo2
U foo2_func
在符號(hào)表中,我們看到,foo2_func是Undefined的,這沒(méi)錯(cuò),因?yàn)槠浯_實(shí)不在hello中定義。但是注意變量foo2,理論上它也應(yīng)該是Undefined的,但是我們看到其在hello中是有定義的,而且其還在BSS段中。換句話(huà)說(shuō),雖然我們?cè)趆ello中沒(méi)有定義一個(gè)未初始化的全局變量,但是鏈接器卻偷偷在hello中定義了一個(gè)未初始化的變量foo2。那么,這個(gè)foo2與libfoo.so中的全局變量foo2是什么關(guān)系呢?為什么編譯器要這樣做?這也是和重定位有關(guān)的,事實(shí)上,這種重定位方式稱(chēng)為“Copy relocation”,后面我們?cè)谟懻撚脩?hù)進(jìn)程的加載時(shí)將會(huì)進(jìn)一步介紹。
8.動(dòng)態(tài)加載
靜態(tài)庫(kù)是在程序編譯時(shí),添加到可執(zhí)行文件中,與目標(biāo)文件一起形成可執(zhí)行文件,與之相關(guān)的節(jié),如 .rela.text等動(dòng)態(tài)鏈接庫(kù),則是加載器加載可執(zhí)行文件時(shí),根據(jù)可執(zhí)行文件的.dym中的庫(kù)信息,把庫(kù)函數(shù)加到內(nèi)在中,這樣不必每個(gè)調(diào)用都添加到可執(zhí)行文件中,且便于庫(kù)的更新。
動(dòng)態(tài)加載,它是在執(zhí)行文件在執(zhí)行的過(guò)程中,在代碼中動(dòng)態(tài)加載庫(kù),它在編譯時(shí)用到 -rdynamic,linux下,用到函數(shù)dlopen,dlsym,dlclose。
比較有用的命令:
gcc -g -c test.c 編譯成目標(biāo)文件,反匯編的時(shí)候,能看到對(duì)應(yīng)的代碼及機(jī)器碼
readelf -a test.o 查看所有節(jié)
readelf -s test.o 查看符號(hào)節(jié)
readelf -S test.o 查看符號(hào)節(jié),及其地址
readelf -r test.o查看重定位表
objdump -d test.o反匯編
ar -r test.a test1.o test2.o將test1o,test2.o打包成一個(gè)靜態(tài)鏈接庫(kù)
gcc -shared -fPIC test1.c test2.c -o libtest.so 生成動(dòng)態(tài)鏈接庫(kù) libtest.so
gcc test.c -ltest -L. -o test (編譯test.c文件,與庫(kù)libtest.so相聯(lián),此處-ltest是指 libtest.so庫(kù) -L指名庫(kù)所在的目錄;找不到庫(kù),也可以用 gcc -o test test.c ./libtest.so編譯。則可采用下面兩個(gè)辦法中的一個(gè)1、可以把當(dāng)前路徑加入 /etc/ld.so.conf中然后運(yùn)行l(wèi)dconfig,或者以當(dāng)前路徑為參數(shù)運(yùn)行l(wèi)dconfig(要有root權(quán)限才行)。2、把當(dāng)前路徑加入環(huán)境變量LD_LIBRARY_PATH中
file程序是用來(lái)判斷文件類(lèi)型的,在file命令下,所有文件都會(huì)原形畢露,不受后綴名的影響。 ldd是用來(lái)打印目標(biāo)程序(由命令行參數(shù)指定)所鏈接的所有動(dòng)態(tài)庫(kù)的信息的,如果目標(biāo)程序沒(méi)有鏈接動(dòng)態(tài)庫(kù),則打印“not a dynamic executable”,ldd的用法請(qǐng)參考manpage。另外,還可以借助程序ldd實(shí)用程序來(lái)判斷。
命令
ar 創(chuàng)建靜態(tài)庫(kù),插入、刪除、列出和提取成員
strings 列出一個(gè)目標(biāo)文件中所有可打印的字符串
nm 列出一個(gè)目標(biāo)文件中的符號(hào)表中定義的符號(hào)
size 列出目標(biāo)文件中節(jié)的名字和大小
readelf 顯示一個(gè)目標(biāo)文件的完整結(jié)構(gòu),包括ELF頭中編碼的所有信息,包含 size 和 nm 的功能
objdump 所有二進(jìn)制工具之母。能夠顯示一個(gè)目標(biāo)文件中所有的信息。它最有用的功能是反匯編 .text 節(jié)中的二進(jìn)制指令。
ldd 列出一個(gè)可執(zhí)行文件在運(yùn)行時(shí)所需要的共享庫(kù)(其它目標(biāo)文件在編譯時(shí),其實(shí)并不知道自己需要的引用,是來(lái)自靜態(tài)庫(kù),還是動(dòng)態(tài)庫(kù))。
里面很多可能不是很正確,希望各位路過(guò),看過(guò)的朋友,留下意見(jiàn),謝謝!
|
|