1. 程式人生 > >鏈接腳本解析

鏈接腳本解析

連接腳本

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”選項;

2. 鏈接腳本的 ENTRY(SYMBOL) 命令;
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

鏈接腳本解析