編譯原理 (預(yù)處理>編譯>匯編>鏈接)(轉(zhuǎn))

[編譯原理 (預(yù)處理>編譯>匯編>鏈接)(轉(zhuǎn))]
(http://www.cnblogs.com/pipicfan/archive/2012/07/10/2583910.html)

一般高級(jí)語(yǔ)言程序編譯的過(guò)程:預(yù)處理、編譯、匯編、鏈接。gcc在后臺(tái)實(shí)際上也經(jīng)歷了這幾個(gè)過(guò)程,我們可以通過(guò)-v參數(shù)查看它的編譯細(xì)節(jié),如果想看某個(gè)具體的編譯過(guò)程,則可以分別使用-E,-S,-c和 -O,對(duì)應(yīng)的后臺(tái)工具則分別為cpp,cc1,as,ld。下面我們將逐步分析這幾個(gè)過(guò)程以及相關(guān)的內(nèi)容,諸如語(yǔ)法檢查、代碼調(diào)試、匯編語(yǔ)言等。

1、預(yù)處理

預(yù)處理是C語(yǔ)言程序從源代碼變成可執(zhí)行程序的第一步,主要是C語(yǔ)言編譯器對(duì)各種預(yù)處理命令進(jìn)行處理,包括頭文件的包含、宏定義的擴(kuò)展、條件編譯的選擇等。打印出預(yù)處理之后的結(jié)果:gcc -E hello.c 或者 cpp hello.c這樣我們就可以看到源代碼中的各種預(yù)處理命令是如何被解釋的,從而方便理解和查錯(cuò)。

gcc調(diào)用了cpp的(雖然我們通過(guò)gcc的-v僅看到cc1),cpp即The C Preprocessor,主要用來(lái)預(yù)處理宏定義、文件包含、條件編譯等。下面介紹它的一個(gè)比較重要的選項(xiàng)-D。在命令行定義宏:gcc –Dmacro=1 hello.c 或者 cpp –Dmacro=1 hello.c等同于在文件的開(kāi)頭定義宏,即#define maco,但是在命令行定義更靈活。例如,在源代碼中有這些語(yǔ)句:

ifdef DEBUGprintf("this code is for debuggingn");#endif

2、編譯

編譯之前,C語(yǔ)言編譯器會(huì)進(jìn)行詞法分析、語(yǔ)法分析(-fsyntax-only),接著會(huì)把源代碼翻譯成中間語(yǔ)言,即匯編語(yǔ)言。如果想看到這個(gè)中間結(jié)果,可以用-S選項(xiàng)。
編譯程序工作時(shí),先分析,后綜合,從而得到目標(biāo)程序。所謂分析,是指詞法分析和語(yǔ)法分析;所謂綜合是指代碼優(yōu)化,存儲(chǔ)分配和代碼生成。為了完成這些分析綜合任務(wù),編譯程序采用對(duì)源程序進(jìn)行多次掃描的辦法,每次掃描集中完成一項(xiàng)或幾項(xiàng)任務(wù),也有一項(xiàng)任務(wù)分散到幾次掃描去完成的。下面舉一個(gè)四遍掃描的例子:第一遍掃描做詞法分析;第二遍掃描做語(yǔ)法分析;第三遍掃描做代碼優(yōu)化和存儲(chǔ)分配;第四遍掃描做代碼生成。
值得一提的是,大多數(shù)的編譯程序直接產(chǎn)生機(jī)器語(yǔ)言的目標(biāo)代碼,形成可執(zhí)行的目標(biāo)文件,但也有的編譯程序則先產(chǎn)生匯編語(yǔ)言一級(jí)的符號(hào)代碼文件,然后再調(diào)用匯編程序進(jìn)行翻譯加工處理,最后產(chǎn)生可執(zhí)行的機(jī)器語(yǔ)言目標(biāo)文件。
語(yǔ)法檢查之后是翻譯動(dòng)作,gcc提供了一個(gè)優(yōu)化選項(xiàng)-O,以便根據(jù)不同的運(yùn)行平臺(tái)和用戶要求產(chǎn)生經(jīng)過(guò)優(yōu)化的匯編代碼。例如,
$ gcc -o hello hello.c #采用默認(rèn)選項(xiàng),不優(yōu)化$ gcc -O2 -o hello2 hello.c #優(yōu)化等次是2$ gcc -Os -o hellos hello.c #優(yōu)化目標(biāo)代碼的大小
$ time ./hello #查看代碼運(yùn)行時(shí)間hello, world
根據(jù)上面的簡(jiǎn)單演示,可以看出gcc有很多不同的優(yōu)化選項(xiàng),主要看用戶的需求了,目標(biāo)代碼的大小和效率之間貌似存在一個(gè)“糾纏”,需要開(kāi)發(fā)人員自己權(quán)衡。

下面我們通過(guò)-S選項(xiàng)來(lái)看看編譯出來(lái)的中間結(jié)果,匯編語(yǔ)言,還是以之前那個(gè)hello.c為例。


復(fù)制代碼

$ gcc -S hello.c #默認(rèn)輸出是hello.s,可自己指定$ cat hello.scat hello.s .file "hello.c" .section .rodata.LC0: .string "hello, world" .text.globl main .type main, @functionmain: leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) pushl %ebp movl %esp, %ebp pushl %ecx subl $4, %esp movl $.LC0, (%esp) call puts movl $0, %eax addl $4, %esp popl %ecx popl %ebp leal -4(%ecx), %esp ret .size main, .-main .ident "GCC: (GNU) 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)" .section .note.GNU-stack,"",@progbits


復(fù)制代碼

和intel的匯編語(yǔ)法不太一樣,這里用的是AT&T語(yǔ)法格式。這里需要補(bǔ)充的是,在寫(xiě)C語(yǔ)言代碼時(shí),如果能夠?qū)幾g器比較熟悉(工作原理和一些細(xì)節(jié))的話,可能會(huì)很有幫助。包括這里的優(yōu)化選項(xiàng)(有些優(yōu)化選項(xiàng)可能在匯編時(shí)采用)和可能的優(yōu)化措施。

3、匯編

把作為中間結(jié)果的匯編代碼翻譯成了機(jī)器代碼,即目標(biāo)代碼,不過(guò)它還不可以運(yùn)行。如果要產(chǎn)生這一中間結(jié)果,可用gcc的-c選項(xiàng),當(dāng)然,也可通過(guò)as命令匯編匯編語(yǔ)言源文件來(lái)產(chǎn)生。

$ file hello.s
hello.s: ASCII assembler program text

$ gcc -c hello.s #用gcc把匯編語(yǔ)言編譯成目標(biāo)代碼
$ file hello.o #file命令可以用來(lái)查看文件的類(lèi)型
hello.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped

$as -o hello.o hello.s #用as把匯編語(yǔ)言編譯成目標(biāo)代碼
$ file hello.o
hello.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped

gcc和as默認(rèn)產(chǎn)生的目標(biāo)代碼都是ELF格式的,因此這里主要討論ELF格式的目標(biāo)代碼。目標(biāo)代碼不再是普通的文本格式,無(wú)法直接通過(guò)文本編輯器瀏覽,需要一些專(zhuān)門(mén)的工具。

binutils(GNU Binary Utilities)的很多工具都采用這個(gè)庫(kù)來(lái)操作目標(biāo)文件,這類(lèi)工具有objdump, objcopy, nm, strip等,不過(guò)另外一款非常優(yōu)秀的分析工具readelf并不是基于這個(gè)庫(kù),所以你也應(yīng)該可以直接用elf.h頭文件中定義的相關(guān)結(jié)構(gòu)來(lái)操作ELF文件。

ELF文件的結(jié)構(gòu):

  1. ELF Header (ELF文件頭)說(shuō)明了文件的類(lèi)型,大小,運(yùn)行平臺(tái),節(jié)區(qū)數(shù)目等。
  2. Porgram Headers Table (程序頭表,實(shí)際上叫段表好一些,用于描述可執(zhí)行文件和可共享庫(kù))
    Section 1
    Section 2
    ...
  3. Section Headers Table(節(jié)區(qū)頭部表,用于鏈接可重定位文件成可執(zhí)行文件或共享庫(kù))

可以分別通過(guò) readelf文件的-h,-l和-S參數(shù)查看ELF文件頭(ELF Header)、程序頭部表(Program Headers Table,段表)和節(jié)區(qū)表(Section Headers Table)。

下面通過(guò)這幾段代碼來(lái)演示通過(guò)readelf -h參數(shù)查看ELF的不同類(lèi)型。期間將演示如何創(chuàng)建動(dòng)態(tài)連接庫(kù)(即可共享文件)、靜態(tài)連接庫(kù),并比較它們的異同。

$ gcc -c myprintf.c test.c #編譯產(chǎn)生兩個(gè)目標(biāo)文件myprintf.o和test.o,它們都是可重定位文件(REL)
$ readelf -h test.o | grep Type
Type: REL (Relocatable file)
$ readelf -h myprintf.o | grep Type
Type: REL (Relocatable file)
$ gcc -o test myprintf.o test.o #根據(jù)目標(biāo)代碼連接產(chǎn)生可執(zhí)行文件,這里的文件類(lèi)型是可執(zhí)行的(EXEC)
$ readelf -h test | grep Type
Type: EXEC (Executable file)
$ ar rcsv libmyprintf.a myprintf.o #用ar命令創(chuàng)建一個(gè)靜態(tài)連接庫(kù)
$ readelf -h libmyprintf.a | grep Type #因此,使用靜態(tài)連接庫(kù)和可重定位文件一樣,它們之間唯一不同是前者可以是多個(gè)可重定位文件的“集合”。
Type: REL (Relocatable file)
$ gcc -o test test.o -llib -L./ #可以直接連接進(jìn)去,也可以使用-l參數(shù),-L指定庫(kù)的搜索路徑
$ gcc -Wall myprintf.o -shared -Wl,-soname,libmyprintf.so.0 -o libmyprintf.so.0.0 #編譯產(chǎn)生動(dòng)態(tài)鏈接庫(kù),并支持major和minor版本號(hào),動(dòng)態(tài)鏈接庫(kù)類(lèi)型為DYN
$ ln -sf libmyprintf.so.0.0 libmyprintf.so.0
$ ln -sf libmyprintf.so.0 libmyprintf.so
$ readelf -h libmyprintf.so | grep Type
Type: DYN (Shared object file)
$ gcc -o test test.o -llib -L./ #編譯時(shí)和靜態(tài)連接庫(kù)類(lèi)似,但是執(zhí)行時(shí)需要指定動(dòng)態(tài)連接庫(kù)的搜索路徑
$ LD_LIBRARY_PATH=./ ./test #LD_LIBRARY_PATH為動(dòng)態(tài)鏈接庫(kù)的搜索路徑
$ gcc -static -o test test.o -llib -L./ #在不指定static時(shí)會(huì)優(yōu)先使用動(dòng)態(tài)鏈接庫(kù),指定時(shí)則阻止使用動(dòng)態(tài)連接庫(kù)這個(gè)時(shí)候會(huì)把所有靜態(tài)連接庫(kù)文件加入到可執(zhí)行文件中.

可重定位文件本身不可以運(yùn)行,僅僅是作為可執(zhí)行文件、靜態(tài)連接庫(kù)(也是可重定位文件)、動(dòng)態(tài)連接庫(kù)的 “組件”。

下面來(lái)看看ELF文件的主體內(nèi)容,節(jié)區(qū)(Section)。ELF文件具有很大的靈活性,它通過(guò)文件頭組織整個(gè)文件的總體結(jié)構(gòu),通過(guò)節(jié)區(qū)表 (Section Headers Table)和程序頭(Program Headers Table或者叫段表)來(lái)分別描述可重定位文件和可執(zhí)行文件。在可重定位文件中,節(jié)區(qū)表描述的就是各種節(jié)區(qū)本身;而在可執(zhí)行文件中,程序頭描述的是由各個(gè)節(jié)區(qū)組成的段(Segment),以便程序運(yùn)行時(shí)動(dòng)態(tài)裝載器知道如何對(duì)它們進(jìn)行內(nèi)存映像,從而方便程序加載和運(yùn)行。

可以通過(guò)readelf的-S參數(shù)查看ELF的節(jié)區(qū)。先來(lái)看看可重定位文件的節(jié)區(qū)信息,通過(guò)節(jié)區(qū)表來(lái)查看:
$ gcc -c myprintf.c #默認(rèn)編譯好myprintf.c,將產(chǎn)生一個(gè)可重定位的文件myprintf.o

$ readelf -S myprintf.o #通過(guò)查看myprintf.o的節(jié)區(qū)表查看節(jié)區(qū)信息
There are 11 section headers, starting at offset 0xc0:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00000000 000034 000018 00 AX 0 0 4
[ 2] .rel.text REL 00000000 000334 000010 08 9 1 4
[ 3] .data PROGBITS 00000000 00004c 000000 00 WA 0 0 4
[ 4] .bss NOBITS 00000000 00004c 000000 00 WA 0 0 4
[ 5] .rodata PROGBITS 00000000 00004c 00000e 00 A 0 0 1
[ 6] .comment PROGBITS 00000000 00005a 000012 00 0 0 1
[ 7] .note.GNU-stack PROGBITS 00000000 00006c 000000 00 0 0 1
[ 8] .shstrtab STRTAB 00000000 00006c 000051 00 0 0 1
[ 9] .symtab SYMTAB 00000000 000278 0000a0 10 10 8 4
[10] .strtab STRTAB 00000000 000318 00001a 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)

$ objdump -d -j .text myprintf.o #這里是程序指令部分,用objdump的-d選項(xiàng)可以看到反編譯的結(jié)果,-j指定需要查看的節(jié)區(qū)
myprintf.o: file format elf32-i386
Disassembly of section .text:


復(fù)制代碼

00000000 : 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 83 ec 08 sub $0x8,%esp 6: 83 ec 0c sub $0xc,%esp 9: 68 00 00 00 00 push $0x0 e: e8 fc ff ff ff call f 13: 83 c4 10 add $0x10,%esp 16: c9 leave 17: c3 ret


復(fù)制代碼

$ readelf -r myprintf.o #用-r選項(xiàng)可以看到有關(guān)重定位的信息,這里有兩部分需要重定位
Relocetion section '.rel.text' at offset 0x334 contains 2 entries:
Offset Info Type Sym.Value Sym. Name
0000000a 00000501 R_386_32 00000000 .rodata
0000000f 00000902 R_386_PC32 00000000 puts

$ readelf -x .rodata myprintf.o #.rodata節(jié)區(qū)包含只讀數(shù)據(jù),即我們要打印的hello, world!.
Hex dump of section '.rodata':
0x00000000 68656c6c 6f2c2077 6f726c64 2100 hello, world!.

$ readelf -x .data myprintf.o #沒(méi)有這個(gè)節(jié)區(qū),.data應(yīng)該包含一些初始化的數(shù)據(jù)
Section '.data' has no data to dump.

$ readelf -x .bss myprintf.o #也沒(méi)有這個(gè)節(jié)區(qū),.bss應(yīng)該包含一些未初始化的數(shù)據(jù),程序默認(rèn)初始為0
Section '.bss' has no data to dump.

$ readelf -x .comment myprintf.o #是一些注釋?zhuān)梢钥吹绞鞘荊CC的版本信息
Hex dump of section '.comment':
0x00000000 00474343 3a202847 4e552920 342e312e .GCC: (GNU) 4.1.
0x00000010 3200 2.

$ readelf -x .note.GNU-stack myprintf.o #這個(gè)也沒(méi)有內(nèi)容
Section '.note.GNU-stack' has no data to dump.

$ readelf -x .shstrtab myprintf.o #包括所有節(jié)區(qū)的名字
Hex dump of section '.shstrtab':
0x00000000 002e7379 6d746162 002e7374 72746162 ..symtab..strtab
0x00000010 002e7368 73747274 6162002e 72656c2e ..shstrtab..rel.
0x00000020 74657874 002e6461 7461002e 62737300 text..data..bss.
0x00000030 2e726f64 61746100 2e636f6d 6d656e74 .rodata..comment
0x00000040 002e6e6f 74652e47 4e552d73 7461636b ..note.GNU-stack
0x00000050 00 .

$ readelf –x .symtab myprintf.o #符號(hào)表,包括所有用到的相關(guān)符號(hào)信息,如函數(shù)名、變量名
Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS myprintf.c
2: 00000000 0 SECTION LOCAL DEFAULT 1
3: 00000000 0 SECTION LOCAL DEFAULT 3
4: 00000000 0 SECTION LOCAL DEFAULT 4
5: 00000000 0 SECTION LOCAL DEFAULT 5
6: 00000000 0 SECTION LOCAL DEFAULT 7
7: 00000000 0 SECTION LOCAL DEFAULT 6
8: 00000000 24 FUNC GLOBAL DEFAULT 1 myprintf
9: 00000000 0 NOTYPE GLOBAL DEFAULT UND puts

$ readelf -x .strtab myprintf.o #字符串表,用到的字符串,包括文件名、函數(shù)名、變量名等。
Hex dump of section '.strtab':
0x00000000 006d7970 72696e74 662e6300 6d797072 .myprintf.c.mypr
0x00000010 696e7466 00707574 7300 intf.puts.

從上表可以看出,對(duì)于可重定位文件,會(huì)包含這些基本節(jié)區(qū).text, .rel.text, .data, .bss, .rodata, .comment, .note.GNU-stack, .shstrtab, .symtab和.strtab。

看一看myprintf.c產(chǎn)生的匯編代碼。
$ gcc -S myprintf.c


復(fù)制代碼

$ cat myprintf.s .file "myprintf.c" .section .rodata.LC0: .string "hello, world!" .text.globl myprintf .type myprintf, @functionmyprintf: pushl %ebp movl %esp, %ebp subl $8, %esp subl $12, %esp pushl $.LC0 call puts addl $16, %esp leave ret .size myprintf, .-myprintf .ident "GCC: (GNU) 4.1.2" .section .note.GNU-stack,"",@progbits


復(fù)制代碼

4、鏈接

鏈接是處理可重定位文件,把它們的各種符號(hào)引用和符號(hào)定義轉(zhuǎn)換為可執(zhí)行文件中的合適信息(一般是虛擬內(nèi)存地址)的過(guò)程。鏈接又分為靜態(tài)鏈接和動(dòng)態(tài)鏈接,前者是程序開(kāi)發(fā)階段程序員用ld(gcc實(shí)際上在后臺(tái)調(diào)用了ld)靜態(tài)鏈接器手動(dòng)鏈接的過(guò)程,而動(dòng)態(tài)鏈接則是程序運(yùn)行期間系統(tǒng)調(diào)用動(dòng)態(tài)鏈接器(ld-linux.so)自動(dòng)鏈接的過(guò)程。比如,如果鏈接到可執(zhí)行文件中的是靜態(tài)連接庫(kù)libmyprintf.a,那么.rodata節(jié)區(qū)在鏈接后需要被重定位到一個(gè)絕對(duì)的虛擬內(nèi)存地址,以便程序運(yùn)行時(shí)能夠正確訪問(wèn)該節(jié)區(qū)中的字符串信息。而對(duì)于puts,因?yàn)樗莿?dòng)態(tài)連接庫(kù)libc.so中定義的函數(shù),所以會(huì)在程序運(yùn)行時(shí)通過(guò)動(dòng)態(tài)符號(hào)鏈接找出puts函數(shù)在內(nèi)存中的地址,以便程序調(diào)用該函數(shù)。

靜態(tài)鏈接過(guò)程主要是把可重定位文件依次讀入,分析各個(gè)文件的文件頭,進(jìn)而依次讀入各個(gè)文件的節(jié)區(qū),并計(jì)算各個(gè)節(jié)區(qū)的虛擬內(nèi)存位置,對(duì)一些需要重定位的符號(hào)進(jìn)行處理,設(shè)定它們的虛擬內(nèi)存地址等,并最終產(chǎn)生一個(gè)可執(zhí)行文件或者是動(dòng)態(tài)鏈接庫(kù)。這個(gè)鏈接過(guò)程是通過(guò)ld來(lái)完成的,ld在鏈接時(shí)使用了一個(gè)鏈接腳本(linker scripq),該鏈接腳本處理鏈接的具體細(xì)節(jié)。這里主要介紹可重定位文件中的節(jié)區(qū)(節(jié)區(qū)表描述的)和可執(zhí)行文件中段(程序頭描述的)的對(duì)應(yīng)關(guān)系以及gcc編譯時(shí)采用的一些默認(rèn)鏈接選項(xiàng)。

下面先來(lái)看看可執(zhí)行文件的節(jié)區(qū)信息,通過(guò)程序頭(段表)來(lái)查看:

$ readelf -S test.o #為了比較,先把test.o的節(jié)區(qū)表也列出There are 10 section headers, starting at offset 0xb4:Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 00000000 000034 000024 00 AX 0 0 4 [ 2] .rel.text REL 00000000 0002ec 000008 08 8 1 4 [ 3] .data PROGBITS 00000000 000058 000000 00 WA 0 0 4 [ 4] .bss NOBITS 00000000 000058 000000 00 WA 0 0 4 [ 5] .comment PROGBITS 00000000 000058 000012 00 0 0 1 [ 6] .note.GNU-stack PROGBITS 00000000 00006a 000000 00 0 0 1 [ 7] .shstrtab STRTAB 00000000 00006a 000049 00 0 0 1 [ 8] .symtab SYMTAB 00000000 000244 000090 10 9 7 4 [ 9] .strtab STRTAB 00000000 0002d4 000016 00 0 0 1Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific)

$ gcc -o test test.o libmyprintf.o$ readelf -l test #我們發(fā)現(xiàn),test和test.o,libmyprintf.o相比,多了很多節(jié)區(qū),如.interp和.init等Elf file type is EXEC (Executable file)Entry point 0x80482b0There are 7 program headers, starting at offset 52Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x000034 0x08048034 0x08048034 0x000e0 0x000e0 R E 0x4 INTERP 0x000114 0x08048114 0x08048114 0x00013 0x00013 R 0x1 [Requesting program interpreter: /lib/ld-linux.so.2] LOAD 0x000000 0x08048000 0x08048000 0x0047c 0x0047c R E 0x1000 LOAD 0x00047c 0x0804947c 0x0804947c 0x00104 0x00108 RW 0x1000 DYNAMIC 0x000490 0x08049490 0x08049490 0x000c8 0x000c8 RW 0x4 NOTE 0x000128 0x08048128 0x08048128 0x00020 0x00020 R 0x4 GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
Section to Segment mapping: Segment Sections... 00 01 .interp 02 .interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame 03 .ctors .dtors .jcr .dynamic .got .got.plt .data .bss 04 .dynamic 05 .note.ABI-tag 06
上表給出了可執(zhí)行文件的如下幾個(gè)段(segment),
PHDR: 給出了程序表自身的大小和位置,不能出現(xiàn)一次以上。INTERP: 因?yàn)槌绦蛑姓{(diào)用了puts(在動(dòng)態(tài)鏈接庫(kù)中定義),使用了動(dòng)態(tài)連接庫(kù),因此需要?jiǎng)討B(tài)裝載器/鏈接器(ld-linux.so)LOAD: 包括程序的指令,.text等節(jié)區(qū)都映射在該段,只讀(R)LOAD: 包括程序的數(shù)據(jù),.data, .bss等節(jié)區(qū)都映射在該段,可讀寫(xiě)(RW)DYNAMIC: 動(dòng)態(tài)鏈接相關(guān)的信息,比如包含有引用的動(dòng)態(tài)連接庫(kù)名字等信息NOTE: 給出一些附加信息的位置和大小GNU_STACK: 這里為空,應(yīng)該是和GNU相關(guān)的一些信息
這里的段可能包括之前的一個(gè)或者多個(gè)節(jié)區(qū),也就是說(shuō)經(jīng)過(guò)鏈接之后原來(lái)的節(jié)區(qū)被重排了,并映射到了不同的段,這些段將告訴系統(tǒng)應(yīng)該如何把它加載到內(nèi)存中。這些新的節(jié)區(qū)來(lái)自哪里?它們的作用是什么呢?先來(lái)通過(guò)gcc的-v參數(shù)看看它的后臺(tái)鏈接過(guò)程。
=======================================================================
$ gcc -v -o test test.o myprintf.o #把可重定位文件鏈接成可執(zhí)行文件Reading specs from /usr/lib/gcc/i486-slackware-linux/4.1.2/specsTarget: i486-slackware-linuxConfigured with: ../gcc-4.1.2/configure --prefix=/usr --enable-shared --enable-languages=ada,c,c++,fortran,java,objc --enable-threads=posix --enable-__cxa_atexit --disable-checking --with-gnu-ld --verbose --with-arch=i486 --target=i486-slackware-linux --host=i486-slackware-linuxThread model: posixgcc version 4.1.2 /usr/libexec/gcc/i486-slackware-linux/4.1.2/collect2 --eh-frame-hdr -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o test /usr/lib/gcc/i486-slackware-linux/4.1.2/http://www.cnblogs.com/../crt1.o /usr/lib/gcc/i486-slackware-linux/4.1.2/http://www.cnblogs.com/../crti.o /usr/lib/gcc/i486-slackware-linux/4.1.2/crtbegin.o -L/usr/lib/gcc/i486-slackware-linux/4.1.2 -L/usr/lib/gcc/i486-slackware-linux/4.1.2 -L/usr/lib/gcc/i486-slackware-linux/4.1.2/http://www.cnblogs.com/http://www.cnblogs.com/i486-slackware-linux/lib -L/usr/lib/gcc/i486-slackware-linux/4.1.2/http://www.cnblogs.com/.. test.o myprintf.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i486-slackware-linux/4.1.2/crtend.o /usr/lib/gcc/i486-slackware-linux/4.1.2/http://www.cnblogs.com/../crtn.o

我自豪 我是一名軟件工程師。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容