鏈接腳本解析
1. 概述
鏈接器的作用主要是對符號的解析以及將符號與地址進行綁定。要實現這個功能需要依賴鏈接腳本,鏈接腳本大多數情況下用來鏈接輸入文件,並生成目標文件。編譯器的“-T”參數就是用來指定鏈接腳本的。
2. 鏈接腳本
需要解析的鏈接腳本代碼如程序清單 2.1所示。
程序清單 2.1 鏈接腳本源碼
OUTPUT_FORMAT("elf32-tradlittlemips") OUTPUT_ARCH(mips) ENTRY(_start) SECTIONS { /* Read-onlysections, merged into text segment: */ . = 0x80100000; .text : { _ftext = . ; *(.text) *(.rodata) *(.rodata1) *(.reginfo) *(.init) *(.stub) /* .gnu.warningsections are handled specially by elf32.em. */ *(.gnu.warning) } =0 _etext = .; PROVIDE (etext =.); .fini : { *(.fini) } =0 .data : { _fdata = . ; *(.data) CONSTRUCTORS } .data1 : { *(.data1) } .ctors : { __CTOR_LIST__ = .; LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2) *(.ctors) LONG(0) __CTOR_END__ = .; } .dtors : { __DTOR_LIST__ = .; LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2) *(.dtors) LONG(0) __DTOR_END__ = .; } _gp = ALIGN(16) +0x7ff0; .got : { *(.got.plt)*(.got) } /* We want thesmall data sections together, so single-instruction offsets can access them all, and initialized dataall before uninitialized, so we can shortenthe on-disk segment size. */ .sdata : { *(.sdata) } .lit8 : {*(.lit8) } .lit4 : {*(.lit4) } _edata = .; PROVIDE (edata =.); __bss_start = .; _fbss = .; .sbss : { *(.sbss) *(.scommon) } .bss : { *(.dynbss) *(.bss) *(COMMON) } . = ALIGN(16); __bss_end = .; _end = .;__end =.; end = .; PROVIDE (end =.); /* These areneeded for ELF backends which have not yet been converted tothe new style linker. */ .stab 0 : {*(.stab) } .stabstr 0 : {*(.stabstr) } /* DWARF debugsections. Symbols in the.debug DWARF section are relative to the beginning of the section so webegin .debug at 0. It‘s not clear yetwhat needs to happen for theothers. */ .debug 0 : { *(.debug) } .debug_srcinfo 0 : {*(.debug_srcinfo) } .debug_aranges 0 : {*(.debug_aranges) } .debug_pubnames 0: { *(.debug_pubnames) } .debug_sfnames 0 : {*(.debug_sfnames) } .line 0 :{ *(.line) } /* These mustappear regardless of . */ .gptab.sdata : {*(.gptab.data) *(.gptab.sdata) } .gptab.sbss : {*(.gptab.bss) *(.gptab.sbss) } }
3. 鏈接腳本逐句解析
OUTPUT_FORMAT("elf32-tradlittlemips") OUTPUT_ARCH(mips)
OUTPUT_FORMAT 和 OUTPUT_ARCH 都是 ld 腳本的保留字命令。OUTPUT_FORMAT 說明輸出二進制文件的格式,OUTPUT_ARCH 說明輸出文件所在平臺。
ENTRY(_start)1
ENTRY 命令的作用是,將後面括號中的符號值設置成入口地址。入口地址(entry point)的定義:進程執行的第一條用戶空間的指令在進程地址空間中的地址。
ld 有多種方法設置進程入口地址,通常它按以下順序設置:(編號越前, 優先級越高)
1. ld 命令行的“-e”選項;
3. 如果定義了 start 符號, 使用 start 符號值;
4. 如果存在 .text section, 使用 .text section 的第一字節的位置值;
5. 使用值 0。
SECTIONS {
SECTIONS 命令告訴 ld 如何把輸入文件的 sections 映射到輸出文件的各個 section:即如何將輸入 section 合為輸出 section;如何把輸出 section 放入程序地址空間 (VMA) 和進程地址空間 (LMA) 。其格式如下:
SECTIONS{ …. } /* Read-only sections, merged into text segment: */ . = 0x80100000;
這句把定位器符號置為 0x80100000 (若不指定,則該符號的初始值為 0)。
. 是一個特殊的符號,它是定位器,即一個位置指針,指向程序地址空間內的某個位置(或某section內的偏移,前提是它在SECTIONS命令內的某section描述內),該符號只能在SECTIONS命令內使用。
.text : { _ftext = . ; *(.text) *(.rodata) *(.rodata1) *(.reginfo) *(.init) *(.stub) /* .gnu.warningsections are handled specially by elf32.em. */ *(.gnu.warning) } =0
.text : 表示text段開始。
(.text) 將所有(符號代表任意輸入文件)輸入文件的.text section合並成一個.text section, 該section的地址由定位器符號的值指定。
} =0 表示合並時留下的空隙用 0 填充。
_etext = .; PROVIDE (etext = .);
_etext = .;很多變量都定義成等於這個 . 符,實際上這個符號所代表的值是在變化的,隨著腳本越往後走,值越增加,根據前面填充的多少自動往後加。
PROVIDE關鍵字用於定義這類符號:在目標文件內被引用,但沒有在任何目標文件內被定義的符號。
此時定義了一個 etext 符號,當目標文件內引用了 etext 符號,卻沒有定義它時,etext 符號對應的地址被定義為 .text section 之後的第一個字節的地址。
.fini : { *(.fini) } =0
含義同前文。
.data : { _fdata = . ; *(.data) CONSTRUCTORS } .data1 : { *(.data1) }
此處代碼就是用於描述數據段了。
CONSTRUCTORS 是一個保留字命令。與 c++ 內的(全局對象的)構造函數和(全局對像的)析構函數相關。
.ctors : { __CTOR_LIST__ = .; LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2) *(.ctors) LONG(0) __CTOR_END__ = .; } .dtors : { __DTOR_LIST__ = .; LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2) *(.dtors) LONG(0) __DTOR_END__ = .; }
對於支持任意section名的目標文件格式,比如COFF、ELF格式,GNU C++將全局構造和全局析構信息分別放入 .ctors section 和 .dtors section 內。
當鏈接器生成的目標文件格式不支持任意section名字時,比如ECOFF、XCOFF格式,鏈接器將通過名字來識別全局構造和全局析構,對於這些文件格式,鏈接器把與全局構造和全局析構的相關信息放入出現 CONSTRUCTORS 關鍵字的輸出section內。
符號CTORS_LIST表示全局構造信息的的開始處,CTORS_END表示全局構造信息的結束處。
這兩塊信息的開始處是一字長的信息,表示該塊信息有多少項數據,然後以值為零的一字長數據結束。
一般來說,GNU C++在函數__main內安排全局構造代碼的運行,而__main函數被初始化代碼(在main函數調用之前執行)調用。
_gp = ALIGN(16) + 0x7ff0;1
_gp是一個重要的全局變量,用作全局引用的一個指針。
.got : { *(.got.plt)*(.got) } /* We want thesmall data sections together, so single-instruction offsets can accessthem all, and initialized data all before uninitialized, so we can shortenthe on-disk segment size. */ .sdata : { *(.sdata) } .lit8 : {*(.lit8) } .lit4 :{ *(.lit4) }
含義同前文。
_edata = .; PROVIDE (edata = .);
意義與前面的 etext 類似。edata 符號也較為重要。
__bss_start = .; _fbss = .; .sbss : { *(.sbss) *(.scommon) } .bss : { *(.dynbss) *(.bss) *(COMMON) } . = ALIGN(16); __bss_end = .; _end = .;__end =.; end = .; PROVIDE (end = .);
此處是描述BSS段。COMMON 這個保留字的意義:
通用符號(common symbol)的輸入section:在許多目標文件格式中,通用符號並沒有占用一個section。鏈接器認為,輸入文件的所有通用符號在名為COMMON的section內。上例中將所有輸入文件的所有通用符號放入輸出.bss section內。
上述腳本,定義了幾個重要的符號:
__bss_start = .; __bss_end = .; _end = .; __end = .; end = .;
這些內容在代碼中可能會用到的。
/* These are needed for ELF backends which have not yetbeen converted tothe new style linker. */ .stab 0 : {*(.stab) } .stabstr 0 : {*(.stabstr) } /* DWARF debugsections. Symbols in the.debug DWARF section are relative to the beginning of the section so webegin .debug at 0. It‘s not clear yetwhat needs to happen for theothers. */ .debug 0 : { *(.debug) } .debug_srcinfo 0 : {*(.debug_srcinfo) } .debug_aranges 0 : {*(.debug_aranges) } .debug_pubnames 0: { *(.debug_pubnames) } .debug_sfnames 0 : {*(.debug_sfnames) } .line 0 : { *(.line) } /* These mustappear regardless of . */ .gptab.sdata : {*(.gptab.data) *(.gptab.sdata) } .gptab.sbss : {*(.gptab.bss) *(.gptab.sbss) } }
最後這部分內容意義與上述類似,看英文註釋可以明白,是新版本鏈接器所需要的一些內容。
4. 免責聲明
內部交流文檔,僅針對SylixOS平臺,若發現相關錯誤或者建議,請及時聯系文檔創建者進行修訂和更新。
本文出自 “11451177” 博客,請務必保留此出處http://11461177.blog.51cto.com/11451177/1983911
鏈接腳本解析