• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            興海北路

            ---男兒仗劍自橫行
            <2008年3月>
            2425262728291
            2345678
            9101112131415
            16171819202122
            23242526272829
            303112345

            統(tǒng)計(jì)

            • 隨筆 - 85
            • 文章 - 0
            • 評(píng)論 - 17
            • 引用 - 0

            常用鏈接

            留言簿(6)

            隨筆分類

            隨筆檔案

            收藏夾

            全是知識(shí)啊

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            GCC編譯背后(第二部分:匯編和鏈接)
            (上接“GCC編譯的背后(第一部分:預(yù)處理和編譯)”)

            3、匯編

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

                匯編是把匯編語(yǔ)言翻譯成目標(biāo)代碼的過(guò)程,在學(xué)習(xí)匯編語(yǔ)言開(kāi)發(fā)時(shí),大家應(yīng)該比較熟悉nasm匯編工具(支持Intel格式的匯編語(yǔ)言)了,不過(guò)這里主要用 as匯編工具來(lái)匯編AT&T格式的匯編語(yǔ)言,因?yàn)間cc產(chǎn)生的中間代碼就是AT&T格式的。下面來(lái)演示分別通過(guò)gcc的-c選項(xiàng)和as來(lái) 產(chǎn)生 目標(biāo)代碼。

            Quote:

            $ file hello.s
            hello.s: ASCII assembler program text
            $ gcc -c hello.s        #用gcc把匯編語(yǔ)言編譯成目標(biāo)代碼
            $ file hello.o            #file命令可以用來(lái)查看文件的類型,這個(gè)目標(biāo)代碼是可重定位的(relocatable),需                   #要通過(guò)ld進(jìn)行進(jìn)一步的鏈接成可執(zhí)行程序(executable)和共享庫(kù)(shared)
            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格式[6]的,因此這里主要討論ELF格式的目標(biāo)代碼(如果 有時(shí)間再回顧一下a.out和coff格式,當(dāng)然你也可以參考資料[15],自己先了解一下,并結(jié)合objcopy來(lái)轉(zhuǎn)換它們,比較異同)。

                目標(biāo)代碼不再是普通的文本格式,無(wú)法直接通過(guò)文本編輯器瀏覽,需要一些專門的工具。如果想了解更多目標(biāo)代碼的細(xì)節(jié),區(qū)分relocatable(可重定 位)、executable(可執(zhí)行)、shared libarary(共享庫(kù))的不同,我們得設(shè)法了解目標(biāo)代碼的組織方式和相關(guān)的閱讀和分析工具。下面我們主要介紹這部分內(nèi)容。
                "BFD is a package which allows applications to use the same routines to operate on object files whatever the object file format. A new object file format can be supported simply by creating a new BFD back end and adding it to the library."[24][25]。
                binutils(GNU Binary Utilities)的很多工具都采用這個(gè)庫(kù)來(lái)操作目標(biāo)文件,這類工具有objdump,objcopy,nm,strip等(當(dāng)然,你也可以利用它。如 果你深入了解ELF格式,那么通過(guò)它來(lái)分析和編寫Virus程序?qū)?huì)更加方便),不過(guò)另外一款非常優(yōu)秀的分析工具readelf并不是 基于這個(gè)庫(kù),所以你也應(yīng)該可以直接用elf.h頭文件中定義的相關(guān)結(jié)構(gòu)來(lái)操作ELF文件。

                下面將通過(guò)這些輔助工具(主要是readelf和objdump,可參考本節(jié)最后列出的資料[4]),結(jié)合ELF手冊(cè)[6](建議看第三篇中文版)來(lái)分析它們。

                下面大概介紹ELF文件的結(jié)構(gòu)和三種不同類型ELF文件的區(qū)別。

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

            ELF Header(ELF文件頭)
            Porgram Headers Table(程序頭表,實(shí)際上叫段表好一些,用于描述可執(zhí)行文件和可共享庫(kù))
            Section 1
            Section 2   
            Section 3
            ...
            Section Headers Table(節(jié)區(qū)頭部表,用于鏈接可重定位文件成可執(zhí)行文件或共享庫(kù))

                對(duì)于可重定位文件,程序頭是可選的,而對(duì)于可執(zhí)行文件和共享庫(kù)文件(動(dòng)態(tài)連接庫(kù)),節(jié)區(qū)表則是可選的。這里的可選是指沒(méi)有也可以。可以分別通過(guò) readelf文件的-h,-l和-S參數(shù)查看ELF文件頭(ELF Header)、程序頭部表(Program Headers Table,段表)和節(jié)區(qū)表(Section Headers Table)。

                文件頭說(shuō)明了文件的類型,大小,運(yùn)行平臺(tái),節(jié)區(qū)數(shù)目等。先來(lái)通過(guò)文件頭看看不同ELF的類型。為了說(shuō)明問(wèn)題,先來(lái)幾段代碼吧。



            Code:

            [Ctrl+A Select All]





            Code:

            [Ctrl+A Select All]





            Code:

            [Ctrl+A Select All]



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

            $ 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í)行文件,這里的文件類型是可執(zhí)行的(EXEC)
            $ readelf -h test | grep Type
              Type:                              EXEC (Executable file)
            $ ar rcsv libmyprintf.a myprintf.o    #用ar命令創(chuàng)建一個(gè)靜態(tài)連接庫(kù),靜態(tài)連接庫(kù)也是可重定位文件(REL)
            $ 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ù)類型為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ù)類似,但是執(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í)行文件中,使得執(zhí)行文件很大
                                                    #而且加載到內(nèi)存以后會(huì)浪費(fèi)內(nèi)存空間,因此不建議這么做



                經(jīng)過(guò)上面的演示基本可以看出它們之間的不同??芍囟ㄎ晃募旧聿豢梢赃\(yùn)行,僅僅是作為可執(zhí)行文件、靜態(tài)連接庫(kù)(也是可重定位文件)、動(dòng)態(tài)連接庫(kù)的 “組件”。靜態(tài)連接庫(kù)和動(dòng)態(tài)連接庫(kù)本身也不可以執(zhí)行,作為可執(zhí)行文件的“組件”,它們兩者也不同,前者也是可重定位文件(只不過(guò)可能是多個(gè)可重定位文件的 集合),并且在連接時(shí)加入到可執(zhí)行文件中去;而動(dòng)態(tài)連接庫(kù)在連接時(shí),庫(kù)文件本身并沒(méi)有添加到可執(zhí)行文件中,只是在可執(zhí)行文件中加入了該庫(kù)的名字等信息,以 便在可執(zhí)行文件運(yùn)行過(guò)程中引用庫(kù)中的函數(shù)時(shí)由動(dòng)態(tài)連接器去查找相關(guān)函數(shù)的地址,并調(diào)用它們。從這個(gè)意義上說(shuō),動(dòng)態(tài)連接庫(kù)本身也具有可重定位的特征,含有可 重定位的信息。對(duì)于什么是重定位?如何進(jìn)行靜態(tài)符號(hào)和動(dòng)態(tài)符號(hào)的重定位,我們將在鏈接部分和《動(dòng)態(tài)符號(hào)鏈接的細(xì)節(jié)》一節(jié)介紹。

                下面來(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ū) 表描述的就是各種節(jié)區(qū)本身;而在可執(zhí)行文件中,程序頭描述的是由各個(gè)節(jié)區(qū)組成的段(Segment),以便程序運(yùn)行時(shí)動(dòng)態(tài)裝載器知道如何對(duì)它們進(jìn)行內(nèi)存映像,從而方便程序加載和運(yùn)行。
                下面先來(lái)看看 一些常見(jiàn)的節(jié)區(qū),而關(guān)于這些節(jié)區(qū)(section)如何通過(guò)重定位構(gòu)成成不同的段(Segments),以及有哪些常規(guī)的段,我們將在鏈接部分進(jìn)一步介紹。

                可以通過(guò)readelf的-S參數(shù)查看ELF的節(jié)區(qū)。(建議一邊操作一邊看文檔,以便加深對(duì)ELF文件結(jié)構(gòu)的理解)先來(lái)看看可重定位文件的節(jié)區(qū)信息,通過(guò)節(jié)區(qū)表來(lái)查看:

            Quote:

            $ 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:

            00000000 <myprintf>:
               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 <myprintf+0xf>
              13:   83 c4 10                add    $0x10,%esp
              16:   c9                      leave
              17:   c3                      ret
            $ readelf -r myprintf.o                         #用-r選項(xiàng)可以看到有關(guān)重定位的信息,這里有兩部分需要重定位

            Relocation 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       mmyprintf.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        #是一些注釋,可以看到是是GCC的版本信息

            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 -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。為了進(jìn)一步理解這些節(jié)區(qū)和源代碼的關(guān)系,這里來(lái)看一看myprintf.c產(chǎn)生的匯編代碼。

            Quote:

            $ gcc -S myprintf.c
            $ cat myprintf.s
                    .file   "myprintf.c"
                    .section        .rodata
            .LC0:
                    .string "hello, world!"
                    .text
            .globl myprintf
                    .type   myprintf, @function
            myprintf:
                    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



                是不是可以從中看出可重定位文件中的那些節(jié)區(qū)和匯編語(yǔ)言代碼之間的關(guān)系?在上面的可重定位文件,可以看到有一個(gè)可重定位的節(jié)區(qū),即. rel.text,它標(biāo)記了兩個(gè)需要重定位的項(xiàng),.rodata和puts。這個(gè)節(jié)區(qū)將告訴編譯器這兩個(gè)信息在鏈接或者動(dòng)態(tài)鏈接的過(guò)程中需要重定位, 具體如何重定位?將根據(jù)重定位項(xiàng)的類型,比如上面的R_386_32和R_386_PC32(關(guān)于這些類型的更多細(xì)節(jié),請(qǐng)查看ELF手冊(cè)[6])。

                到這里,對(duì)可重定位文件應(yīng)該有了一個(gè)基本的了解,下面將介紹什么是可重定位,可重定位文件到底是如何被鏈接生成可執(zhí)行文件和動(dòng)態(tài)連接庫(kù)的,這個(gè)過(guò)程除了進(jìn)行了一些符號(hào)的重定位外,還進(jìn)行了哪些工作呢?

            本節(jié)參考資料:

            [1] 了解編譯程序的過(guò)程
            http://9iyou.com/Program_Data/linuxunix-3125.html
            http://www.host01.com/article/server/00070002/0621409075078127.htm
            [2] C track: compiling C programs.
            http://www.cs.caltech.edu/courses/cs11/material/c/mike/misc/compiling_c.html
            [3] Dissecting shared libraries
            http://www.ibm.com/developerworks/linux/library/l-shlibs.html

            4、鏈接

                開(kāi)篇:重定位是將符號(hào)引用與符號(hào)定義進(jìn)行鏈接的過(guò)程。因此鏈接是處理可重定位文件,把它們的各種符號(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ò)程,動(dòng)態(tài)鏈接過(guò)程見(jiàn)《動(dòng)態(tài)符號(hào)鏈接的細(xì)節(jié)》。

                靜態(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 script), 該鏈接腳本處理鏈接的具體細(xì)節(jié)。由于靜態(tài)符號(hào)鏈接過(guò)程非常復(fù)雜,特別是計(jì)算符號(hào)地址的過(guò)程,考慮到時(shí)間關(guān)系,相關(guān)細(xì)節(jié)請(qǐng)參考ELF手冊(cè)[6]。這里主要介 紹可重定位文件中的節(jié)區(qū)(節(jié)區(qū)表描述的)和可執(zhí)行文件中段(程序頭描述的)的對(duì)應(yīng)關(guān)系以及gcc編譯時(shí)采用的一些默認(rèn)鏈接選項(xiàng)。

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

            Quote:

            $ 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  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)
            $ 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 0x80482b0
            There are 7 program headers, starting at offset 52

            Program 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ū)都映射在該段,可讀寫(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)存中。

                從上表中,通過(guò)比較可執(zhí)行文件(test)中擁有的節(jié)區(qū)和可重定位文件(test.o和myprintf.o)中擁有的節(jié)區(qū)后發(fā)現(xiàn),鏈接之后多了一些之前沒(méi)有的節(jié)區(qū),這些新的節(jié)區(qū)來(lái)自哪里?它們的作用是什么呢?先來(lái)通過(guò)gcc的-v參數(shù)看看它的后臺(tái)鏈接過(guò)程。

            Quote:

            $ gcc -v -o test test.o myprintf.o    #把可重定位文件鏈接成可執(zhí)行文件
            Reading specs from /usr/lib/gcc/i486-slackware-linux/4.1.2/specs
            Target: i486-slackware-linux
            Configured 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-linux
            Thread model: posix
            gcc 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/../../../crt1.o /usr/lib/gcc/i486-slackware-linux/4.1.2/../../../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/../../../../i486-slackware-linux/lib -L/usr/lib/gcc/i486-slackware-linux/4.1.2/../../.. 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/../../../crtn.o



                從上邊的演示看出,gcc在連接了我們自己的目標(biāo)文件test.o和myprintf.o之外,還連接了crt1.o,crtbegin.o等額外的目標(biāo)文件,難道那些新的節(jié)區(qū)就來(lái)自這些文件?
                另外gcc在進(jìn)行了相關(guān)配置(./configure)后,調(diào)用了collect2,卻并沒(méi)有調(diào)用ld,通過(guò)查找gcc文檔中和collect2相關(guān)的部 分發(fā)現(xiàn)collect2在后臺(tái)實(shí)際上還是去尋找ld命令的。為了理解gcc默認(rèn)連接的后臺(tái)細(xì)節(jié),這里直接把collect2替換成ld,并把一些路徑換成 絕對(duì)路徑或者簡(jiǎn)化,得到如下的ld命令以及執(zhí)行的效果。

            Quote:

            $ ld --eh-frame-hdr \
            -m elf_i386 \
            -dynamic-linker /lib/ld-linux.so.2 \
            -o test \
            /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/gcc/i486-slackware-linux/4.1.2/crtbegin.o \
            test.o myprintf.o \
            -L/usr/lib/gcc/i486-slackware-linux/4.1.2 -L/usr/i486-slackware-linux/lib -L/usr/lib/ -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/crtn.o
            $ ./test
            hello, world!



            不出我們所料,它完美的運(yùn)行了。下面通過(guò)ld的手冊(cè)(man ld)來(lái)分析一下這幾個(gè)參數(shù)。

            --eh-frame-hdr

            要求創(chuàng)建一個(gè).eh_frame_hdr節(jié)區(qū)(貌似目標(biāo)文件test中并沒(méi)有這個(gè)節(jié)區(qū),所以不關(guān)心它)。

          1. -m elf_i386

            這 里指定不同平臺(tái)上的鏈接腳本,可以通過(guò)--verbose命令查看腳本的具體內(nèi)容,如ld -m elf_i386 --verbose,它實(shí)際上被存放在一個(gè)文件中(/usr/lib/ldscripts目錄下),你可以去修改這個(gè)腳本,具體如何做?請(qǐng)參考ld的手冊(cè)。 在后面我們將簡(jiǎn)要提到鏈接腳本中是如何預(yù)定義變量的,以及這些預(yù)定義變量如何在我們的程序中使用。需要提到的是,如果不是交叉編譯,那么無(wú)須指定該選項(xiàng)。

          2. -dynamic-linker /lib/ld-linux.so.2

            指定動(dòng)態(tài)裝載器/鏈接器,即程序中的INTERP段中的內(nèi)容。動(dòng)態(tài)裝載器/連接器負(fù)責(zé)連接有可共享庫(kù)的可執(zhí)行文件的裝載和動(dòng)態(tài)符號(hào)連接。

          3. -o test

            指定輸出文件,即可執(zhí)行文件名的名字

          4. /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/gcc/i486-slackware-linux/4.1.2/crtbegin.o

            鏈 接到test文件開(kāi)頭的一些內(nèi)容,這里實(shí)際上就包含了.init等節(jié)區(qū)。.init節(jié)區(qū)包含一些可執(zhí)行代碼,在main函數(shù)之前被調(diào)用,以便進(jìn)行一些初始化操 作,在C++中完成構(gòu)造函數(shù)功能,更多細(xì)節(jié)請(qǐng)參考資料[9]

          5. test.o myprintf.o

            鏈接我們自己的可重定位文件

          6. -L/usr/lib/gcc/i486-slackware-linux/4.1.2 -L/usr/i486-slackware-linux/lib -L/usr/lib/ -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed

            鏈接libgcc庫(kù)和libc庫(kù),后者定義有我們需要的puts函數(shù)

          7. /usr/lib/gcc/i486-slackware-linux/4.1.2/crtend.o /usr/lib/crtn.o

            鏈接到test文件末尾的一些內(nèi)容,這里實(shí)際上包含了.fini等節(jié)區(qū)。.fini節(jié)區(qū)包含了一些可執(zhí)行代碼,在程序退出時(shí)被執(zhí)行,作一些清理工作,在C++中完成析構(gòu)造函數(shù)功能。我們往往可以通過(guò)atexit來(lái)注冊(cè)那些需要在程序退出時(shí)才執(zhí)行的函數(shù)。

                對(duì)于crtbegin.o和crtend.o這兩個(gè)文件,貌似完全是用來(lái)支持C++的構(gòu)造和析構(gòu)工作的[9],所以可以不鏈接到我們的可執(zhí)行文件中,鏈接時(shí)把它們?nèi)サ艨纯矗?br>
            Quote:

            $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o test /usr/lib/crt1.o /usr/lib/crti.o test.o myprintf.o -L/usr/lib -lc /usr/lib/crtn.o    #后面發(fā)現(xiàn)不用鏈接libgcc,也不用--eh-frame-hdr參數(shù)
            $ readelf -l test

            Elf file type is EXEC (Executable file)
            Entry point 0x80482b0
            There are 7 program headers, starting at offset 52

            Program 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 0x003ea 0x003ea R E 0x1000
              LOAD           0x0003ec 0x080493ec 0x080493ec 0x000e8 0x000e8 RW  0x1000
              DYNAMIC        0x0003ec 0x080493ec 0x080493ec 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
               03     .dynamic .got .got.plt .data
               04     .dynamic
               05     .note.ABI-tag
               06    
            $ ./test
            hello, world!



                完全可以工作,而且發(fā)現(xiàn).ctors(保存著程序中全局構(gòu)造函數(shù)的指針數(shù)組), .dtors(保存著程序中全局析構(gòu)函數(shù)的指針數(shù)組),.jcr(未知),.eh_frame節(jié)區(qū)都沒(méi)有了,所以crtbegin.o和crtend.o應(yīng)該包含了這些節(jié)區(qū)。
                而對(duì)于另外兩個(gè)文件crti.o和crtn.o,通過(guò)readelf -S查看后發(fā)現(xiàn)它們都有.init和.fini節(jié)區(qū),如果我們不需要讓程序進(jìn)行一些初始化和清理工作呢?是不是就可以不 鏈接這個(gè)兩個(gè)文件?試試看。

            Quote:

            $ ld  -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o test /usr/lib/crt1.o test.o myprintf.o -L/usr/lib/ -lc
            /usr/lib/libc_nonshared.a(elf-init.oS): In function `__libc_csu_init':
            (.text+0x25): undefined reference to `_init'



            貌似不行,竟然有人調(diào)用了__libc_csu_init函數(shù),而這個(gè)函數(shù)引用了_init。這兩個(gè)符號(hào)都在哪里呢?

            Quote:

            $ readelf -s /usr/lib/crt1.o | grep __libc_csu_init
                18: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND __libc_csu_init
            $ readelf -s /usr/lib/crti.o | grep _init
                17: 00000000     0 FUNC    GLOBAL DEFAULT    5 _init



                竟然是crt1.o調(diào)用了__libc_csu_init函數(shù),而該函數(shù)卻引用了我們沒(méi)有鏈接的crti.o文件中定義的_init符號(hào)。這樣的話不鏈接 crti.o和crtn.o文件就不成了羅?不對(duì)吧,要不干脆不用crt1.o算了,看看gcc額外連接進(jìn)去的最后一個(gè)文件crt1.o到底干了個(gè)啥子?

            Quote:

            $ ld  -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o test test.o myprintf.o -L/usr/lib/ -lc
            ld: warning: cannot find entry symbol _start; defaulting to 00000000080481a4



                這樣卻說(shuō)沒(méi)有找到入口符號(hào)_start,難道crt1.o中定義了這個(gè)符號(hào)?不過(guò)它給默認(rèn)設(shè)置了一個(gè)地址,只是個(gè)警告,說(shuō)明test已經(jīng)生成,不管怎樣先運(yùn)行看看再說(shuō)。

            Quote:

            $ ./test
            hello, world!
            Segmentation fault



                貌似程序運(yùn)行完了,不過(guò)結(jié)束時(shí)冒出個(gè)段錯(cuò)誤?可能是程序結(jié)束時(shí)有問(wèn)題,用gdb調(diào)試看看:

            Quote:

            $ gcc -g -c test.c myprintf.c    #產(chǎn)生目標(biāo)代碼, 非交叉編譯,不指定-m也可以鏈接成功,所以下面可以去掉-m參數(shù)
            $ ld -dynamic-linker /lib/ld-linux.so.2 -o test test.o myprintf.o -L/usr/lib -lc
            ld: warning: cannot find entry symbol _start; defaulting to 00000000080481d8   
            $ ./test
            hello, world!
            Segmentation fault
            $ gdb ./test
            ...
            (gdb) l
            1       #include "test.h"
            2
            3       int main()
            4       {
            5               myprintf();
            6               return 0;
            7       }
            (gdb) break 7            #在程序的末尾設(shè)置一個(gè)斷點(diǎn)
            Breakpoint 1 at 0x80481bf: file test.c, line 7.
            (gdb) r                    #程序都快結(jié)束了都沒(méi)問(wèn)題,怎么會(huì)到最后出個(gè)問(wèn)題呢?
            Starting program: /mnt/hda8/Temp/c/program/test
            hello, world!

            Breakpoint 1, main () at test.c:7
            7       }
            (gdb) n                    #單步執(zhí)行看看,怎么下面一條指令是0x00000001,肯定是程序退出以后出了問(wèn)題
            0x00000001 in ?? ()
            (gdb) n                    #誒,當(dāng)然找不到邊了,都跑到0x00000001了
            Cannot find bounds of current function
            (gdb) c                    #原來(lái)是這么回事,估計(jì)是return 0返回之后出問(wèn)題了,看看它的匯編去。
            Continuing.

            Program received signal SIGSEGV, Segmentation fault.
            0x00000001 in ?? ()
            $ gcc -S test.c #產(chǎn)生匯編代碼
            $ cat test.s    #后面就這么幾條指令,難不成ret返回有問(wèn)題,不讓它ret返回,把return改成_exit直接進(jìn)入內(nèi)核退出
            ...
                    call    myprintf
                    movl    $0, %eax
                    addl    $4, %esp
                    popl    %ecx
                    popl    %ebp
                    leal    -4(%ecx), %esp
                    ret
            ...
            $ vim test.c
            $ cat test.c    #就把return語(yǔ)句修改成_exit了。
            #include "test.h"
            #include <unistd.h> /* _exit */

            int main()
            {
                    myprintf();
                    _exit(0);
            }
            $ gcc -g -c test.c myprintf.c
            $  ld -dynamic-linker /lib/ld-linux.so.2 -o test test.o myprintf.o -L/usr/lib -lc
            ld: warning: cannot find entry symbol _start; defaulting to 00000000080481d8
            $ ./test    #竟然好了,再看看匯編有什么不同
            hello, world!
            $ gcc -S test.c
            $ cat test.s    #貌似就把ret指令替換成了_exit函數(shù)調(diào)用,直接進(jìn)入內(nèi)核,然內(nèi)核讓處理了,那為什么ret有問(wèn)題呢?
            ...
                    call    myprintf
                    subl    $12, %esp
                    pushl   $0
                    call    _exit
            ...
            $ gdb ./test    #把代碼改回去(改成return 0;),再調(diào)試看看調(diào)用main函數(shù)返回時(shí)的下一條指令地址eip
            ...
            (gdb) l
            warning: Source file is more recent than executable.
            1       #include "test.h"
            2
            3       int main()
            4       {
            5               myprintf();
            6               return 0;
            7       }
            (gdb) break 5
            Breakpoint 1 at 0x80481b5: file test.c, line 5.
            (gdb) break 7
            Breakpoint 2 at 0x80481bc: file test.c, line 7.
            (gdb) r
            Starting program: /mnt/hda8/Temp/c/program/test

            Breakpoint 1, main () at test.c:5
            5               myprintf();
            (gdb) x/8x $esp    #發(fā)現(xiàn)0x00000001剛好是之前我們調(diào)試時(shí)看到的程序返回后的位置,即eip,說(shuō)明程序在初始化的時(shí)候
                            #這個(gè)eip就是錯(cuò)誤的。為什么呢?因?yàn)槲覀兏緵](méi)有鏈接進(jìn)來(lái)初始化的代碼,而是在編譯器自己給我們
                            #初始化了一個(gè)程序入口即00000000080481d8,也就是說(shuō),沒(méi)有任何人調(diào)用main,main不知道返回哪里去
                            #所以,我們直接讓main結(jié)束時(shí)進(jìn)入內(nèi)核調(diào)用_exit而退出則不會(huì)有問(wèn)題
            0xbf929510:     0xbf92953c      0x080481a4      0x00000000      0xb7eea84f
            0xbf929520:     0xbf92953c      0xbf929534      0x00000000      0x00000001 



                通過(guò)上面的演示和解釋發(fā)現(xiàn)只要把return語(yǔ)句修改為_(kāi)exit語(yǔ)句,程序即使不鏈接任何額外的目標(biāo)代碼都可以正常運(yùn)行(原因是不連接那些額外的文件時(shí) 相當(dāng)于沒(méi)有進(jìn)行初始化操作,如果在程序的最后執(zhí)行ret匯編指令,程序?qū)o(wú)法獲得正確的eip,從而無(wú)法進(jìn)行后續(xù)的動(dòng)作)。但是為什么會(huì)有“找不到 _start符號(hào)”的警告呢?通過(guò)readelf -s查看crt1.o發(fā)現(xiàn)里頭有這個(gè)符號(hào),并且crt1.o引用了main這個(gè)符號(hào),是不是意味著會(huì)從_start進(jìn)入main呢?是不是程序入口是 _start,而并非main呢?

                先來(lái)看看剛才提到的鏈接器的默認(rèn)鏈接腳本(ld -m elf_386 --verbose),它告訴我們程序的入口(entry)是_start,而一個(gè)可執(zhí)行文件必須有一個(gè)入口地址才能運(yùn)行,所以這就是說(shuō)明了為什么ld一 定要提示我們“_start找不到”,找不到以后就給默認(rèn)設(shè)置了一個(gè)地址。

            Quote:

            $ ld --verbose  | grep ^ENTRY    #非交叉編譯,可不用-m參數(shù);ld默認(rèn)找_start入口,并不是main哦!
            ENTRY(_start)



                原來(lái)是這樣,程序的入口(entry)竟然不是main函數(shù),而是_start。那干脆把匯編里頭的main給改掉算了,看行不行?

            Quote:

            $ cat test.c
            #include "test.h"
            #include <unistd.h>     /* _exit */

            int main()
            {
                    myprintf();
                    _exit(0);
            }
            $ gcc -S test.c
            $ sed -i -e "s#main#_start#g" test.s    #把匯編中的main全部修改為_(kāi)start,即修改程序入口為_(kāi)start
            $ gcc -c test.s myprintf.c
            $ ld -dynamic-linker /lib/ld-linux.so.2 -o test test.o myprintf.o -L/usr/lib/ -lc    #果然沒(méi)問(wèn)題了 :-)
            $ ./test
            hello, world!



                _start竟然是真正的程序入口,那在有main的情況下呢?為什么在_start之后能夠找到main呢?這個(gè)看看alert7大叔的"Before main分析"[5]吧,這里不再深入介紹??傊兀ㄟ^(guò)修改程序的return語(yǔ)句為_(kāi)exit(0)和修改程序的入口為_(kāi)start,我們的代碼不鏈接gcc默認(rèn)鏈 接的那些額外的文件同樣可以工作得很好。并且打破了一個(gè)學(xué)習(xí)C語(yǔ)言以來(lái)的常識(shí):main函數(shù)作為程序的主函數(shù),是程序的入口,實(shí)際上則不然。

                再補(bǔ)充一點(diǎn)內(nèi)容,在ld的鏈接腳本中,有一個(gè)特別的關(guān)鍵字PROVIDE,由這個(gè)關(guān)鍵字定義的符號(hào)是ld的預(yù)定義字符,我們可以在C語(yǔ)言函數(shù)中擴(kuò)展它們后直接使用。這些特別的符號(hào)可以通過(guò)下面的方法獲取,

            Quote:

            $ ld --verbose | grep PROVIDE | grep -v HIDDEN
              PROVIDE (__executable_start = 0x08048000); . = 0x08048000 + SIZEOF_HEADERS;
              PROVIDE (__etext = .);
              PROVIDE (_etext = .);
              PROVIDE (etext = .);
              _edata = .; PROVIDE (edata = .);
              _end = .; PROVIDE (end = .);


                這里面有幾個(gè)我們比較關(guān)心的,第一個(gè)是程序的入口地址__executable_start,另外三個(gè)是etext,edata,end,分別對(duì)應(yīng)程序的 代碼段(text)、初始化數(shù)據(jù)(data)和未初始化的數(shù)據(jù)(bss)(可以參考資料[6]和man etext),如何引用這些變量呢?看看這個(gè)例子。


            Code:

            [Ctrl+A Select All]



                到這里,程序鏈接過(guò)程的一些細(xì)節(jié)都介紹得差不多了。在《動(dòng)態(tài)符號(hào)鏈接的細(xì)節(jié)》中將主要介紹ELF文件的動(dòng)態(tài)符號(hào)鏈接過(guò)程。

            本節(jié)參考資料

            [1] An beginners guide to compiling programs under Linux.
            http://www.luv.asn.au/overheads/compile.html
            [2] gcc manual
            http://gcc.gnu.org/onlinedocs/gcc-4.2.2/gcc/
            [3] A Quick Tour of Compiling, Linking, Loading, and Handling Libraries on Unix
            http://efrw01.frascati.enea.it/Software/Unix/IstrFTU/cern-cnl-2001-003-25-link.html
            [4] Unix 目標(biāo)文件初探
            http://www.ibm.com/developerworks/cn/aix/library/au-unixtools.html
            [5] Before main()分析
            http://www.xfocus.net/articles/200109/269.html
            [6] A Process Viewing Its Own /proc/<PID>/map Information
            http://www.linuxforums.org/forum/linux-kernel/51790-process-viewing-its-own-proc-pid-map-information.html
          8. posted on 2008-03-14 15:24 隨意門 閱讀(7537) 評(píng)論(3)  編輯 收藏 引用

            評(píng)論

            # re: GCC編譯背后(第二部分:匯編和鏈接) 2008-03-14 15:25 隨意門

            # 參考資料

            [1] UNIX環(huán)境高級(jí)編程
            [2] Linux Kernel Primer
            [3] Linux Kernel Interface(2.4)
            http://www.faqs.org/docs/kernel_2_4/lki.html
            [4] Understanding ELF using readelf and objdump
            http://www.linuxforums.org/misc/understanding_elf_using_readelf_and_objdump.html
            [5] Study of ELF loading and relocs
            http://netwinder.osuosl.org/users/p/patb/public_html/elf_relocs.html
            [6] ELF file format and ABI
            http://www.x86.org/ftp/manuals/tools/elf.pdf
            http://www.muppetlabs.com/~breadbox/software/ELF.txt
            (北大OS實(shí)驗(yàn)室)http://162.105.203.48/web/gaikuang/submission/TN05.ELF.Format.Summary.pdf
            (alert7 大牛翻譯)http://www.xfocus.net/articles/200105/174.html
            [7] 關(guān)于GCC方面的論文,請(qǐng)查看歷年的會(huì)議論文集
            http://www.gccsummit.org/2005/2005-GCC-Summit-Proceedings.pdf
            http://www.gccsummit.org/2006/2006-GCC-Summit-Proceedings.pdf
            [8] The Linux GCC HOW TO
            http://www.faqs.org/docs/Linux-HOWTO/GCC-HOWTO.html
            [9] ELF: From The Programmer's Perspective
            http://linux.jinr.ru/usoft/WWW/www_debian.org/Documentation/elf/elf.html
            [10] C/C++程序編譯步驟詳解
            http://www.xxlinux.com/linux/article/development/soft/20070424/8267.html
            [11] GNU binutils小結(jié)
            http://hi.baidu.com/skxzz/blog/item/d2599f003fdd1c12738b6561.html
            [12] C語(yǔ)言常見(jiàn)問(wèn)題集
            http://c-faq-chn.sourceforge.net/ccfaq/index.html
            [13] 使用BFD操作ELF
            http://elfhack.whitecell.org/mydocs/use_bfd.txt
            [14] bfd document
            http://sourceware.org/binutils/docs/bfd/index.html
            [15] UNIX/LINUX 平臺(tái)可執(zhí)行文件格式分析
            http://blog.chinaunix.net/u/19881/showart_215242.html

            # re: GCC編譯背后(第二部分:匯編和鏈接) 2010-03-28 11:05 HERNANDEZChristi33

            The <a href="http://lowest-rate-loans.com/topics/credit-loans">credit loans</a> suppose to be useful for guys, which want to start their business. By the way, this is comfortable to receive a college loan.

            # re: GCC編譯背后(第二部分:匯編和鏈接) 2010-05-28 20:48 freelance writer

            If I were you, I would look for very good freelance writing to get a lot of the best notes close to this topic!

            # re: GCC編譯背后(第二部分:匯編和鏈接) 2011-06-26 03:42 essays online

            Study system seems to be influenced by educational law. Nevertheless, students still need to complete essay papers. I opine that students ought not to be confused! The custom writing service can offer supreme quality of custom writing! I solve my academic papers writing troubles in such way as well.

            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            亚洲Av无码国产情品久久| 久久无码人妻一区二区三区 | 久久人人爽人人爽人人片AV不| 怡红院日本一道日本久久| 亚洲精品美女久久久久99小说| 99久久国语露脸精品国产| 波多野结衣久久| 热久久国产欧美一区二区精品| 东方aⅴ免费观看久久av| 国产69精品久久久久APP下载| 久久国产精品成人片免费| 久久精品国产亚洲AV忘忧草18| 香蕉久久影院| 热久久国产精品| 久久www免费人成看国产片| 久久精品国产亚洲5555| 欧美久久久久久午夜精品| 久久激情五月丁香伊人| 69久久精品无码一区二区| 久久久无码精品亚洲日韩京东传媒| 99热成人精品免费久久| 久久久无码精品午夜| 国产V亚洲V天堂无码久久久| 国产成人久久精品麻豆一区| 蜜臀av性久久久久蜜臀aⅴ| 三级三级久久三级久久| 亚洲国产成人精品久久久国产成人一区二区三区综 | 性欧美大战久久久久久久久| 久久久久亚洲av无码专区导航| 欧美大战日韩91综合一区婷婷久久青草 | 日本精品久久久中文字幕 | 久久精品国产亚洲精品2020| 国色天香久久久久久久小说| 久久精品国产亚洲AV蜜臀色欲| 色综合久久88色综合天天 | 欧美激情精品久久久久久久九九九| 久久精品国产精品国产精品污| 99久久夜色精品国产网站| 岛国搬运www久久| 久久久久国产视频电影| 久久久久97国产精华液好用吗|