亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊(cè) 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫(kù)
最近訪問板塊 發(fā)新帖
查看: 1053 | 回復(fù): 0
打印 上一主題 下一主題

鏈接腳本(ZT) [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2009-12-25 10:20 |只看該作者 |倒序?yàn)g覽

今天在看uboot引導(dǎo)Linux部分,發(fā)現(xiàn)要對(duì)鏈接腳本深入了解,才能知道各個(gè)目標(biāo)文件的內(nèi)存分布映像,下面是我看到的一些資料
0. Contents
1. 概論
2. 基本概念
3. 腳本格式
4. 簡(jiǎn)單例子
5. 簡(jiǎn)單腳本命令
6. 對(duì)符號(hào)的賦值
7. SECTIONS命令
8. MEMORY命令
9. PHDRS命令
10. VERSION命令
11. 腳本內(nèi)的表達(dá)式
12. 暗含的連接腳本
1. 概論
每一個(gè)鏈接過程都由鏈接腳本(linker script, 一般以lds作為文件的后綴名)控制. 鏈接腳本主要用于規(guī)定如何把輸入文件內(nèi)的section放入輸出文件內(nèi), 并控制輸出文件內(nèi)各部分在程序地址空間內(nèi)的布局. 但你也可以用連接命令做一些其他事情.
連接器有個(gè)默認(rèn)的內(nèi)置連接腳本, 可用ld –verbose查看. 連接選項(xiàng)-r和-N可以影響默認(rèn)的連接腳本(如何影響?).
-T選項(xiàng)用以指定自己的鏈接腳本, 它將代替默認(rèn)的連接腳本。你也可以使用以增加自定義的鏈接命令.
以下沒有特殊說明,連接器指的是靜態(tài)連接器.
2. 基本概念
鏈接器把一個(gè)或多個(gè)輸入文件合成一個(gè)輸出文件.
輸入文件: 目標(biāo)文件或鏈接腳本文件.
輸出文件: 目標(biāo)文件或可執(zhí)行文件.
目標(biāo)文件(包括可執(zhí)行文件)具有固定的格式, 在UNIX或GNU/Linux平臺(tái)下, 一般為ELF格式. 若想了解更多, 可參考 UNIX/Linux平臺(tái)可執(zhí)行文件格式分析
有時(shí)把輸入文件內(nèi)的section稱為輸入section(input section), 把輸出文件內(nèi)的section稱為輸出section(output sectin).
目標(biāo)文件的每個(gè)section至少包含兩個(gè)信息: 名字和大小. 大部分section還包含與它相關(guān)聯(lián)的一塊數(shù)據(jù), 稱為section contents(section內(nèi)容). 一個(gè)section可被標(biāo)記為“l(fā)oadable(可加載的)”或“allocatable(可分配的)”.
loadable section: 在輸出文件運(yùn)行時(shí), 相應(yīng)的section內(nèi)容將被載入進(jìn)程地址空間中.
allocatable section: 內(nèi)容為空的section可被標(biāo)記為“可分配的”. 在輸出文件運(yùn)行時(shí), 在進(jìn)程地址空間中空出大小同section指定大小的部分. 某些情況下, 這塊內(nèi)存必須被置零.
如果一個(gè)section不是“可加載的”或“可分配的”, 那么該section通常包含了調(diào)試信息. 可用objdump -h命令查看相關(guān)信息.
每個(gè)“可加載的”或“可分配的”輸出section通常包含兩個(gè)地址: VMA(virtual memory address虛擬內(nèi)存地址或程序地址空間地址)和LMA(load memory address加載內(nèi)存地址或進(jìn)程地址空間地址). 通常VMA和LMA是相同的.
在目標(biāo)文件中, loadable或allocatable的輸出section有兩種地址: VMA(virtual Memory Address)和LMA(Load Memory Address). VMA是執(zhí)行輸出文件時(shí)section所在的地址, 而LMA是加載輸出文件時(shí)section所在的地址. 一般而言, 某section的VMA == LMA. 但在嵌入式系統(tǒng)中, 經(jīng)常存在加載地址和執(zhí)行地址不同的情況: 比如將輸出文件加載到開發(fā)板的flash中(由LMA指定), 而在運(yùn)行時(shí)將位于flash中的輸出文件復(fù)制到SDRAM中(由VMA指定).
可這樣來理解VMA和LMA, 假設(shè):
(1) .data section對(duì)應(yīng)的VMA地址是0×08050000, 該section內(nèi)包含了3個(gè)32位全局變量, i、j和k, 分別為1,2,3.
(2) .text section內(nèi)包含由”printf( “j=%d “, j );”程序片段產(chǎn)生的代碼.
連接時(shí)指定.data section的VMA為0×08050000, 產(chǎn)生的printf指令是將地址為0×08050004處的4字節(jié)內(nèi)容作為一個(gè)整數(shù)打印出來。
如果.data section的LMA為0×08050000,顯然結(jié)果是j=2
如果.data section的LMA為0×08050004,顯然結(jié)果是j=1
還可這樣理解LMA:
.text section內(nèi)容的開始處包含如下兩條指令(intel i386指令是10字節(jié),每行對(duì)應(yīng)5字節(jié)):
jmp 0×08048285
movl $0×1,%eax
如果.text section的LMA為0×08048280, 那么在進(jìn)程地址空間內(nèi)0×08048280處為“jmp 0×08048285”指令, 0×08048285處為movl $0×1,%eax指令. 假設(shè)某指令跳轉(zhuǎn)到地址0×08048280, 顯然它的執(zhí)行將導(dǎo)致%eax寄存器被賦值為1.
如果.text section的LMA為0×08048285, 那么在進(jìn)程地址空間內(nèi)0×08048285處為“jmp 0×08048285”指令, 0×0804828a處為movl $0×1,%eax指令. 假設(shè)某指令跳轉(zhuǎn)到地址0×08048285, 顯然它的執(zhí)行又跳轉(zhuǎn)到進(jìn)程地址空間內(nèi)0×08048285處, 造成死循環(huán).
符號(hào)(symbol): 每個(gè)目標(biāo)文件都有符號(hào)表(SYMBOL TABLE), 包含已定義的符號(hào)(對(duì)應(yīng)全局變量和static變量和定義的函數(shù)的名字)和未定義符號(hào)(未定義的函數(shù)的名字和引用但沒定義的符號(hào))信息.
符號(hào)值: 每個(gè)符號(hào)對(duì)應(yīng)一個(gè)地址, 即符號(hào)值(這與c程序內(nèi)變量的值不一樣, 某種情況下可以把它看成變量的地址). 可用nm命令查看它們. (nm的使用方法可參考本blog的GNU binutils筆記)
3. 腳本格式
鏈接腳本由一系列命令組成, 每個(gè)命令由一個(gè)關(guān)鍵字(一般在其后緊跟相關(guān)參數(shù))或一條對(duì)符號(hào)的賦值語(yǔ)句組成. 命令由分號(hào)‘;’分隔開.
文件名或格式名內(nèi)如果包含分號(hào)’;'或其他分隔符, 則要用引號(hào)‘”’將名字全稱引用起來. 無法處理含引號(hào)的文件名.
/* */之間的是注釋。
4. 簡(jiǎn)單例子
在介紹鏈接描述文件的命令之前, 先看看下述的簡(jiǎn)單例子:
以下腳本將輸出文件的text section定位在0×10000, data section定位在0×8000000:
SECTIONS
{
. = 0×10000;
.text : { *(.text) }
. = 0×8000000;
.data : { *(.data) }
.bss : { *(.bss) }
}
解釋一下上述的例子:
. = 0×10000 : 把定位器符號(hào)置為0×10000 (若不指定, 則該符號(hào)的初始值為0).
.text : { *(.text) } : 將所有(*符號(hào)代表任意輸入文件)輸入文件的.text section合并成一個(gè).text section, 該section的地址由定位器符號(hào)的值指定, 即0×10000.
. = 0×8000000 :把定位器符號(hào)置為0×8000000
.data : { *(.data) } : 將所有輸入文件的.data section合并成一個(gè).data section, 該section的地址被置為0×8000000.
.bss : { *(.bss) } : 將所有輸入文件的.bss section合并成一個(gè).bss section,該section的地址被置為0×8000000+.data section的大小.
連接器每讀完一個(gè)section描述后, 將定位器符號(hào)的值*增加*該section的大小. 注意: 此處沒有考慮對(duì)齊約束.
5. 簡(jiǎn)單腳本命令
- 1 -
ENTRY(SYMBOL) : 將符號(hào)SYMBOL的值設(shè)置成入口地址。
入口地址(entry point): 進(jìn)程執(zhí)行的第一條用戶空間的指令在進(jìn)程地址空間的地址)
ld有多種方法設(shè)置進(jìn)程入口地址, 按一下順序: (編號(hào)越前, 優(yōu)先級(jí)越高)
1, ld命令行的-e選項(xiàng)
2, 連接腳本的ENTRY(SYMBOL)命令
3, 如果定義了start符號(hào), 使用start符號(hào)值
4, 如果存在.text section, 使用.text section的第一字節(jié)的位置值
5, 使用值0
- 2 -INCLUDE filename : 包含其他名為filename的鏈接腳本
相當(dāng)于c程序內(nèi)的的#include指令, 用以包含另一個(gè)鏈接腳本.
腳本搜索路徑由-L選項(xiàng)指定. INCLUDE指令可以嵌套使用, 最大深度為10. 即: 文件1內(nèi)INCLUDE文件2, 文件2內(nèi)INCLUDE文件3… , 文件10內(nèi)INCLUDE文件11. 那么文件11內(nèi)不能再出現(xiàn) INCLUDE指令了.
- 3 -INPUT(files): 將括號(hào)內(nèi)的文件做為鏈接過程的輸入文件
ld首先在當(dāng)前目錄下尋找該文件, 如果沒找到, 則在由-L指定的搜索路徑下搜索. file可以為 -lfile形式,就象命令行的-l選項(xiàng)一樣. 如果該命令出現(xiàn)在暗含的腳本內(nèi), 則該命令內(nèi)的file在鏈接過程中的順序由該暗含的腳本在命令行內(nèi)的順序決定.
- 4 -GROUP(files) : 指定需要重復(fù)搜索符號(hào)定義的多個(gè)輸入文件
file必須是庫(kù)文件, 且file文件作為一組被ld重復(fù)掃描,直到不在有新的未定義的引用出現(xiàn)。
- 5 -OUTPUT(FILENAME) : 定義輸出文件的名字
同ld的-o選項(xiàng), 不過-o選項(xiàng)的優(yōu)先級(jí)更高. 所以它可以用來定義默認(rèn)的輸出文件名. 如a.out
- 6 -SEARCH_DIR(PATH) :定義搜索路徑,
同ld的-L選項(xiàng), 不過由-L指定的路徑要比它定義的優(yōu)先被搜索。
- 7 -STARTUP(filename) : 指定filename為第一個(gè)輸入文件
在鏈接過程中, 每個(gè)輸入文件是有順序的. 此命令設(shè)置文件filename為第一個(gè)輸入文件。
- 8 – OUTPUT_FORMAT(BFDNAME) : 設(shè)置輸出文件使用的BFD格式
同ld選項(xiàng)-o format BFDNAME, 不過ld選項(xiàng)優(yōu)先級(jí)更高.
- 9 -OUTPUT_FORMAT(DEFAULT,BIG,LITTLE) : 定義三種輸出文件的格式(大小端)
若有命令行選項(xiàng)-EB, 則使用第2個(gè)BFD格式; 若有命令行選項(xiàng)-EL,則使用第3個(gè)BFD格式.否則默認(rèn)選第一個(gè)BFD格式.
TARGET(BFDNAME):設(shè)置輸入文件的BFD格式
同ld選項(xiàng)-b BFDNAME. 若使用了TARGET命令, 但未使用OUTPUT_FORMAT命令, 則最用一個(gè)TARGET命令設(shè)置的BFD格式將被作為輸出文件的BFD格式.
另外還有一些:
ASSERT(EXP, MESSAGE):如果EXP不為真,終止連接過程
EXTERN(SYMBOL SYMBOL …):在輸出文件中增加未定義的符號(hào),如同連接器選項(xiàng)-u
FORCE_COMMON_ALLOCATION:為common symbol(通用符號(hào))分配空間,即使用了-r連接選項(xiàng)也為其分配
NOCROSSREFS(SECTION SECTION …):檢查列出的輸出section,如果發(fā)現(xiàn)他們之間有相互引用,則報(bào)錯(cuò)。對(duì)于某些系統(tǒng),特別是內(nèi)存較緊張的嵌入式系統(tǒng),某些section是不能同時(shí)存在內(nèi)存中的,所以他們之間不能相互引用。
OUTPUT_ARCH(BFDARCH):設(shè)置輸出文件的machine architecture(體系結(jié)構(gòu)),BFDARCH為被BFD庫(kù)使用的名字之一。可以用命令objdump -f查看。
可通過 man -S 1 ld查看ld的聯(lián)機(jī)幫助, 里面也包括了對(duì)這些命令的介紹.
6. 對(duì)符號(hào)的賦值
在目標(biāo)文件內(nèi)定義的符號(hào)可以在鏈接腳本內(nèi)被賦值. (注意和C語(yǔ)言中賦值的不同!) 此時(shí)該符號(hào)被定義為全局的. 每個(gè)符號(hào)都對(duì)應(yīng)了一個(gè)地址, 此處的賦值是更改這個(gè)符號(hào)對(duì)應(yīng)的地址.
e.g. 通過下面的程序查看變量a的地址:
/* a.c */
#include
int a = 100;
int main(void)
{
printf( “&a=0x%p “, &a );
return 0;
}/* a.lds */
a = 3;
$ gcc -Wall -o a-without-lds a.c
&a = 0×8049598
$ gcc -Wall -o a-with-lds a.c a.lds
&a = 0×3
注意: 對(duì)符號(hào)的賦值只對(duì)全局變量起作用!
一些簡(jiǎn)單的賦值語(yǔ)句
能使用任何c語(yǔ)言內(nèi)的賦值操作:
SYMBOL = EXPRESSION ;
SYMBOL += EXPRESSION ;
SYMBOL -= EXPRESSION ;
SYMBOL *= EXPRESSION ;
SYMBOL /= EXPRESSION ;
SYMBOL >= EXPRESSION ;
SYMBOL &= EXPRESSION ;
SYMBOL |= EXPRESSION ;
除了第一類表達(dá)式外, 使用其他表達(dá)式需要SYMBOL被定義于某目標(biāo)文件。
. 是一個(gè)特殊的符號(hào),它是定位器,一個(gè)位置指針,指向程序地址空間內(nèi)的某位置(或某section內(nèi)的偏移,如果它在SECTIONS命令內(nèi)的某section描述內(nèi)),該符號(hào)只能在SECTIONS命令內(nèi)使用。
注意:賦值語(yǔ)句包含4個(gè)語(yǔ)法元素:符號(hào)名、操作符、表達(dá)式、分號(hào);一個(gè)也不能少。
被賦值后,符號(hào)所屬的section被設(shè)值為表達(dá)式EXPRESSION所屬的SECTION(參看11. 腳本內(nèi)的表達(dá)式)
賦值語(yǔ)句可以出現(xiàn)在連接腳本的三處地方:SECTIONS命令內(nèi),SECTIONS命令內(nèi)的section描述內(nèi)和全局位置;如下,
floating_point = 0; /* 全局位置 */
SECTIONS
{
.text :
{
*(.text)
_etext = .; /* section描述內(nèi) */
}
_bdata = (. + 3) & ~ 4; /* SECTIONS命令內(nèi) */
.data : { *(.data) }
}
PROVIDE關(guān)鍵字
該關(guān)鍵字用于定義這類符號(hào):在目標(biāo)文件內(nèi)被引用,但沒有在任何目標(biāo)文件內(nèi)被定義的符號(hào)。
例子:
SECTIONS
{
.text :
{
*(.text)
_etext = .;
PROVIDE(etext = .);
}
}
當(dāng)目標(biāo)文件內(nèi)引用了etext符號(hào),確沒有定義它時(shí),etext符號(hào)對(duì)應(yīng)的地址被定義為.text section之后的第一個(gè)字節(jié)的地址。
7. SECTIONS命令
SECTIONS命令告訴ld如何把輸入文件的sections映射到輸出文件的各個(gè)section: 如何將輸入section合為輸出section; 如何把輸出section放入程序地址空間(VMA)和進(jìn)程地址空間(LMA).該命令格式如下:
SECTIONS
{
SECTIONS-COMMAND
SECTIONS-COMMAND

}
SECTION-COMMAND有四種:
(1) ENTRY命令
(2) 符號(hào)賦值語(yǔ)句
(3) 一個(gè)輸出section的描述(output section description)
(4) 一個(gè)section疊加描述(overlay description)
如果整個(gè)連接腳本內(nèi)沒有SECTIONS命令, 那么ld將所有同名輸入section合成為一個(gè)輸出section內(nèi), 各輸入section的順序?yàn)樗鼈儽贿B接器發(fā)現(xiàn)的順序.
如果某輸入section沒有在SECTIONS命令中提到, 那么該section將被直接拷貝成輸出section。
輸出section描述
輸出section描述具有如下格式:
SECTION [ADDRESS] [(TYPE)] : [AT(LMA)]
{
OUTPUT-SECTION-COMMAND
OUTPUT-SECTION-COMMAND

} [>REGION] [AT>LMA_REGION] [:PHDR
file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/EverNoteTempDir/0000@275_icon_razz.gif
HDR ...] [=FILLEXP]
[ ]內(nèi)的內(nèi)容為可選選項(xiàng), 一般不需要.
SECTION:section名字
SECTION左右的空白、圓括號(hào)、冒號(hào)是必須的,換行符和其他空格是可選的。
每個(gè)OUTPUT-SECTION-COMMAND為以下四種之一,
符號(hào)賦值語(yǔ)句
一個(gè)輸入section描述
直接包含的數(shù)據(jù)值
一個(gè)特殊的輸出section關(guān)鍵字
輸出section名字(SECTION):
輸出section名字必須符合輸出文件格式要求,比如:a.out格式的文件只允許存在.text、.data和.bss section名。而有的格式只允許存在數(shù)字名字,那么此時(shí)應(yīng)該用引號(hào)將所有名字內(nèi)的數(shù)字組合在一起;另外,還有一些格式允許任何序列的字符存在于 section名字內(nèi),此時(shí)如果名字內(nèi)包含特殊字符(比如空格、逗號(hào)等),那么需要用引號(hào)將其組合在一起。
輸出section地址(ADDRESS):
ADDRESS是一個(gè)表達(dá)式,它的值用于設(shè)置VMA。如果沒有該選項(xiàng)且有REGION選項(xiàng),那么連接器將根據(jù)REGION設(shè)置VMA;如果也沒有 REGION選項(xiàng),那么連接器將根據(jù)定位符號(hào)‘.’的值設(shè)置該section的VMA,將定位符號(hào)的值調(diào)整到滿足輸出section對(duì)齊要求后的值,輸出 section的對(duì)齊要求為:該輸出section描述內(nèi)用到的所有輸入section的對(duì)齊要求中最嚴(yán)格的。
例子:
.text . : { *(.text) }

.text : { *(.text) }
這兩個(gè)描述是截然不同的,第一個(gè)將.text section的VMA設(shè)置為定位符號(hào)的值,而第二個(gè)則是設(shè)置成定位符號(hào)的修調(diào)值,滿足對(duì)齊要求后的。
ADDRESS可以是一個(gè)任意表達(dá)式,比如ALIGN(0×10)這將把該section的VMA設(shè)置成定位符號(hào)的修調(diào)值,滿足16字節(jié)對(duì)齊后的。
注意:設(shè)置ADDRESS值,將更改定位符號(hào)的值。
輸入section描述:
最常見的輸出section描述命令是輸入section描述。
輸入section描述是最基本的連接腳本描述。
輸入section描述基礎(chǔ):
基本語(yǔ)法:FILENAME([EXCLUDE_FILE (FILENAME1 FILENAME2 ...) SECTION1 SECTION2 ...)
FILENAME文件名,可以是一個(gè)特定的文件的名字,也可以是一個(gè)字符串模式。
SECTION名字,可以是一個(gè)特定的section名字,也可以是一個(gè)字符串模式
例子是最能說明問題的,
*(.text) :表示所有輸入文件的.text section
(*(EXCLUDE_FILE (*crtend.o *otherfile.o) .ctors)) :表示除crtend.o、otherfile.o文件外的所有輸入文件的.ctors section。
data.o(.data) :表示data.o文件的.data section
data.o :表示data.o文件的所有section
*(.text .data) :表示所有文件的.text section和.data section,順序是:第一個(gè)文件的.text section,第一個(gè)文件的.data section,第二個(gè)文件的.text section,第二個(gè)文件的.data section,...
*(.text) *(.data) :表示所有文件的.text section和.data section,順序是:第一個(gè)文件的.text section,第二個(gè)文件的.text section,...,最后一個(gè)文件的.text section,第一個(gè)文件的.data section,第二個(gè)文件的.data section,...,最后一個(gè)文件的.data section
下面看連接器是如何找到對(duì)應(yīng)的文件的。
當(dāng)FILENAME是一個(gè)特定的文件名時(shí),連接器會(huì)查看它是否在連接命令行內(nèi)出現(xiàn)或在INPUT命令中出現(xiàn)。
當(dāng)FILENAME是一個(gè)字符串模式時(shí),連接器僅僅只查看它是否在連接命令行內(nèi)出現(xiàn)。
注意:如果連接器發(fā)現(xiàn)某文件在INPUT命令內(nèi)出現(xiàn),那么它會(huì)在-L指定的路徑內(nèi)搜尋該文件。
字符串模式內(nèi)可存在以下通配符:
* :表示任意多個(gè)字符
? :表示任意一個(gè)字符
[CHARS] :表示任意一個(gè)CHARS內(nèi)的字符,可用-號(hào)表示范圍,如:a-z
:表示引用下一個(gè)緊跟的字符
在文件名內(nèi),通配符不匹配文件夾分隔符/,但當(dāng)字符串模式僅包含通配符*時(shí)除外。
任何一個(gè)文件的任意section只能在SECTIONS命令內(nèi)出現(xiàn)一次。看如下例子,
SECTIONS {
.data : { *(.data) }
.data1 : { data.o(.data) }
}
data.o文件的.data section在第一個(gè)OUTPUT-SECTION-COMMAND命令內(nèi)被使用了,那么在第二個(gè)OUTPUT-SECTION-COMMAND命令內(nèi)將不會(huì)再被使用,也就是說即使連接器不報(bào)錯(cuò),輸出文件的.data1 section的內(nèi)容也是空的。
再次強(qiáng)調(diào):連接器依次掃描每個(gè)OUTPUT-SECTION-COMMAND命令內(nèi)的文件名,任何一個(gè)文件的任何一個(gè)section都只能使用一次。
讀者可以用-M連接命令選項(xiàng)來產(chǎn)生一個(gè)map文件,它包含了所有輸入section到輸出section的組合信息。
再看個(gè)例子,
SECTIONS {
.text : { *(.text) }
.DATA : { [A-Z]*(.data) }
.data : { *(.data) }
.bss : { *(.bss) }
}
這個(gè)例子中說明,所有文件的輸入.text section組成輸出.text section;所有以大寫字母開頭的文件的.data section組成輸出.DATA section,其他文件的.data section組成輸出.data section;所有文件的輸入.bss section組成輸出.bss section。
可以用SORT()關(guān)鍵字對(duì)滿足字符串模式的所有名字進(jìn)行遞增排序,如SORT(.text*)。
通用符號(hào)(common symbol)的輸入section:
在許多目標(biāo)文件格式中,通用符號(hào)并沒有占用一個(gè)section。連接器認(rèn)為:輸入文件的所有通用符號(hào)在名為COMMON的section內(nèi)。
例子,
.bss { *(.bss) *(COMMON) }
這個(gè)例子中將所有輸入文件的所有通用符號(hào)放入輸出.bss section內(nèi)?梢钥吹紺OMMOM section的使用方法跟其他section的使用方法是一樣的。
有些目標(biāo)文件格式把通用符號(hào)分成幾類。例如,在MIPS elf目標(biāo)文件格式中,把通用符號(hào)分成standard common symbols(標(biāo)準(zhǔn)通用符號(hào))和small common symbols(微通用符號(hào),不知道這么譯對(duì)不對(duì)?),此時(shí)連接器認(rèn)為所有standard common symbols在COMMON section內(nèi),而small common symbols在.scommon section內(nèi)。
在一些以前的連接腳本內(nèi)可以看見[COMMON],相當(dāng)于*(COMMON),不建議繼續(xù)使用這種陳舊的方式。
輸入section和垃圾回收:
在連接命令行內(nèi)使用了選項(xiàng)–gc-sections后,連接器可能將某些它認(rèn)為沒用的section過濾掉,此時(shí)就有必要強(qiáng)制連接器保留一些特定的 section,可用KEEP()關(guān)鍵字達(dá)此目的。如KEEP(*(.text))或KEEP(SORT(*)(.text))
最后看個(gè)簡(jiǎn)單的輸入section相關(guān)例子:
SECTIONS {
outputa 0×10000 :
{
all.o
foo.o (.input1)
}
outputb :
{
foo.o (.input2)
foo1.o (.input1)
}
outputc :
{
*(.input1)
*(.input2)
}
}
本例中,將all.o文件的所有section和foo.o文件的所有(一個(gè)文件內(nèi)可以有多個(gè)同名section).input1 section依次放入輸出outputa section內(nèi),該section的VMA是0×10000;將foo.o文件的所有.input2 section和foo1.o文件的所有.input1 section依次放入輸出outputb section內(nèi),該section的VMA是當(dāng)前定位器符號(hào)的修調(diào)值(對(duì)齊后);將其他文件(非all.o、foo.o、foo1.o)文件的. input1 section和.input2 section放入輸出outputc section內(nèi)。
在輸出section存放數(shù)據(jù)命令:
能夠顯示地在輸出section內(nèi)填入你想要填入的信息(這樣是不是可以自己通過連接腳本寫程序?當(dāng)然是簡(jiǎn)單的程序)。
BYTE(EXPRESSION) 1 字節(jié)
SHORT(EXPRESSION) 2 字節(jié)
LOGN(EXPRESSION) 4 字節(jié)
QUAD(EXPRESSION) 8 字節(jié)
SQUAD(EXPRESSION) 64位處理器的代碼時(shí),8 字節(jié)
輸出文件的字節(jié)順序big endianness 或little endianness,可以由輸出目標(biāo)文件的格式?jīng)Q定;如果輸出目標(biāo)文件的格式不能決定字節(jié)順序,那么字節(jié)順序與第一個(gè)輸入文件的字節(jié)順序相同。
如:BYTE(1)、LANG(addr)。
注意,這些命令只能放在輸出section描述內(nèi),其他地方不行。
錯(cuò)誤:SECTIONS { .text : { *(.text) } LONG(1) .data : { *(.data) } }
正確:SECTIONS { .text : { *(.text) LONG(1) } .data : { *(.data) } }
在當(dāng)前輸出section內(nèi)可能存在未描述的存儲(chǔ)區(qū)域(比如由于對(duì)齊造成的空隙),可以用FILL(EXPRESSION)命令決定這些存儲(chǔ)區(qū)域的內(nèi)容, EXPRESSION的前兩字節(jié)有效,這兩字節(jié)在必要時(shí)可以重復(fù)被使用以填充這類存儲(chǔ)區(qū)域。如FILE(0×9090)。在輸出section描述中可以有=FILEEXP屬性,它的作用如同F(xiàn)ILE()命令,但是FILE命令只作用于該FILE指令之后的section區(qū)域,而=FILEEXP屬性作用于整個(gè)輸出section區(qū)域,且FILE命令的優(yōu)先級(jí)更高。!
輸出section內(nèi)命令的關(guān)鍵字:
CREATE_OBJECT_SYMBOLS :為每個(gè)輸入文件建立一個(gè)符號(hào),符號(hào)名為輸入文件的名字。每個(gè)符號(hào)所在的section是出現(xiàn)該關(guān)鍵字的section。
CONSTRUCTORS :與c++內(nèi)的(全局對(duì)象的)構(gòu)造函數(shù)和(全局對(duì)像的)析構(gòu)函數(shù)相關(guān),下面將它們簡(jiǎn)稱為全局構(gòu)造和全局析構(gòu)。
對(duì)于a.out目標(biāo)文件格式,連接器用一些不尋常的方法實(shí)現(xiàn)c++的全局構(gòu)造和全局析構(gòu)。當(dāng)連接器生成的目標(biāo)文件格式不支持任意section名字時(shí),比如說ECOFF、XCOFF格式,連接器將通過名字來識(shí)別全局構(gòu)造和全局析構(gòu),對(duì)于這些文件格式,連接器把與全局構(gòu)造和全局析構(gòu)的相關(guān)信息放入出現(xiàn) CONSTRUCTORS關(guān)鍵字的輸出section內(nèi)。
符號(hào)__CTORS_LIST__表示全局構(gòu)造信息的的開始處,__CTORS_END__表示全局構(gòu)造信息的結(jié)束處。
符號(hào)__DTORS_LIST__表示全局構(gòu)造信息的的開始處,__DTORS_END__表示全局構(gòu)造信息的結(jié)束處。
這兩塊信息的開始處是一字長(zhǎng)的信息,表示該塊信息有多少項(xiàng)數(shù)據(jù),然后以值為零的一字長(zhǎng)數(shù)據(jù)結(jié)束。
一般來說,GNU C++在函數(shù)__main內(nèi)安排全局構(gòu)造代碼的運(yùn)行,而__main函數(shù)被初始化代碼(在main函數(shù)調(diào)用之前執(zhí)行)調(diào)用。是不是對(duì)于某些目標(biāo)文件格式才這樣???
對(duì)于支持任意section名的目標(biāo)文件格式,比如COFF、ELF格式,GNU C++將全局構(gòu)造和全局析構(gòu)信息分別放入.ctors section和.dtors section內(nèi),然后在連接腳本內(nèi)加入如下,
__CTOR_LIST__ = .;
LONG((__CTOR_END__ – __CTOR_LIST__) / 4 – 2)
*(.ctors)
LONG(0)
__CTOR_END__ = .;
__DTOR_LIST__ = .;
LONG((__DTOR_END__ – __DTOR_LIST__) / 4 – 2)
*(.dtors)
LONG(0)
__DTOR_END__ = .;
如果使用GNU C++提供的初始化優(yōu)先級(jí)支持(它能控制每個(gè)全局構(gòu)造函數(shù)調(diào)用的先后順序),那么請(qǐng)?jiān)谶B接腳本內(nèi)把CONSTRUCTORS替換成SORT (CONSTRUCTS),把*(.ctors)換成*(SORT(.ctors)),把*(.dtors)換成*(SORT(.dtors))。一般來說,默認(rèn)的連接腳本已作好的這些工作。
輸出section的丟棄:
例子,.foo { *(.foo) },如果沒有任何一個(gè)輸入文件包含.foo section,那么連接器將不會(huì)創(chuàng)建.foo輸出section。但是如果在這些輸出section描述內(nèi)包含了非輸入section描述命令(如符號(hào)賦值語(yǔ)句),那么連接器將總是創(chuàng)建該輸出section。
有一個(gè)特殊的輸出section,名為/DISCARD/,被該section引用的任何輸入section將不會(huì)出現(xiàn)在輸出文件內(nèi),這就是DISCARD的意思吧。如果/DISCARD/ section被它自己引用呢?想想看。
輸出section屬性:
終于講到這里了,呵呵。
我們?cè)倩仡櫼韵螺敵鰏ection描述的文法:
SECTION [ADDRESS] [(TYPE)] : [AT(LMA)]
{
OUTPUT-SECTION-COMMAND
OUTPUT-SECTION-COMMAND

} [>REGION] [AT>LMA_REGION] [:PHDR
file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/EverNoteTempDir/0000@275_icon_razz.gif
HDR ...] [=FILLEXP]
前面我們?yōu)g覽了SECTION、ADDRESS、OUTPUT-SECTION-COMMAND相關(guān)信息,下面我們將瀏覽其他屬性。
TYPE :每個(gè)輸出section都有一個(gè)類型,如果沒有指定TYPE類型,那么連接器根據(jù)輸出section引用的輸入section的類型設(shè)置該輸出section的類型。它可以為以下五種值,
NOLOAD :該section在程序運(yùn)行時(shí),不被載入內(nèi)存。
DSECT,COPY,INFO,OVERLAY :這些類型很少被使用,為了向后兼容才被保留下來。這種類型的section必須被標(biāo)記為“不可加載的”,以便在程序運(yùn)行不為它們分配內(nèi)存。
輸出section的LMA :默認(rèn)情況下,LMA等于VMA,但可以通過關(guān)鍵字AT()指定LMA。
用關(guān)鍵字AT()指定,括號(hào)內(nèi)包含表達(dá)式,表達(dá)式的值用于設(shè)置LMA。如果不用AT()關(guān)鍵字,那么可用AT>LMA_REGION表達(dá)式設(shè)置指定該section加載地址的范圍。
這個(gè)屬性主要用于構(gòu)件ROM境象。
例子,
SECTIONS
{
.text 0×1000 : { *(.text) _etext = . ; }
.mdata 0×2000 :
AT ( ADDR (.text) + SIZEOF (.text) )
{ _data = . ; *(.data); _edata = . ; }
.bss 0×3000 :
{ _bstart = . ; *(.bss) *(COMMON) ; _bend = . ;}
}
程序如下,
extern char _etext, _data, _edata, _bstart, _bend;
char *src = &_etext;
char *dst = &_data;
/* ROM has data at end of text; copy it. */
while (dst rom }
輸出section所在的程序段:可以將輸出section放入預(yù)先定義的程序段(program segment)內(nèi)。如果某個(gè)輸出section設(shè)置了它所在的一個(gè)或多個(gè)程序段,那么接下來定義的輸出section的默認(rèn)程序段與該輸出 section的相同。除非再次顯示地指定。例子,
PHDRS { text PT_LOAD ; }
SECTIONS { .text : { *(.text) } :text }
可以通過:NONE指定連接器不把該section放入任何程序段內(nèi)。詳情請(qǐng)查看PHDRS命令
輸出section的填充模版:這個(gè)在前面提到過,任何輸出section描述內(nèi)的未指定的內(nèi)存區(qū)域,連接器用該模版填充該區(qū)域。用法:=FILEEXP,前兩字節(jié)有效,當(dāng)區(qū)域大于兩字節(jié)時(shí),重復(fù)使用這兩字節(jié)以將其填滿。例子,
SECTIONS { .text : { *(.text) } =0×9090 }
覆蓋圖(overlay)描述:
覆蓋圖描述使兩個(gè)或多個(gè)不同的section占用同一塊程序地址空間。覆蓋圖管理代碼負(fù)責(zé)將section的拷入和拷出。考慮這種情況,當(dāng)某存儲(chǔ)塊的訪問速度比其他存儲(chǔ)塊要快時(shí),那么如果將section拷到該存儲(chǔ)塊來執(zhí)行或訪問,那么速度將會(huì)有所提高,覆蓋圖描述就很適合這種情形。文法如下,
SECTIONS {

OVERLAY [START] : [NOCROSSREFS] [AT ( LDADDR )]
{
SECNAME1
{
OUTPUT-SECTION-COMMAND
OUTPUT-SECTION-COMMAND

} [:PHDR...] [=FILL]
SECNAME2
{
OUTPUT-SECTION-COMMAND
OUTPUT-SECTION-COMMAND

} [:PHDR...] [=FILL]

} [>REGION] [:PHDR...] [=FILL]

}
由以上文法可以看出,同一覆蓋圖內(nèi)的section具有相同的VMA。SECNAME2的LMA為SECTNAME1的LMA加上SECNAME1的大小,同理計(jì)算SECNAME2,3,4…的LMA。SECNAME1的LMA由LDADDR決定,如果它沒有被指定,那么由START決定,如果它也沒有被指定,那么由當(dāng)前定位符號(hào)的值決定。
NOCROSSREFS關(guān)鍵字指定各section之間不能交叉引用,否則報(bào)錯(cuò)。
對(duì)于OVERLAY描述的每個(gè)section,連接器將定義兩個(gè)符號(hào)__load_start_SECNAME和__load_stop_SECNAME,這兩個(gè)符號(hào)的值分別代表SECNAME section的LMA地址的開始和結(jié)束。
連接器處理完OVERLAY描述語(yǔ)句后,將定位符號(hào)的值加上所有覆蓋圖內(nèi)section大小的最大值。
看個(gè)例子吧,
SECTIONS{

OVERLAY 0×1000 : AT (0×4000)
{
.text0 { o1/*.o(.text) }
.text1 { o2/*.o(.text) }
}

}
.text0 section和.text1 section的VMA地址是0×1000,.text0 section加載于地址0×4000,.text1 section緊跟在其后。
程序代碼,拷貝.text1 section代碼,
extern char __load_start_text1, __load_stop_text1;
memcpy ((char *) 0×1000, &__load_start_text1,
&__load_stop_text1 – &__load_start_text1);
8. 內(nèi)存區(qū)域命令
—————
注意:以下存儲(chǔ)區(qū)域指的是在程序地址空間內(nèi)的。
在默認(rèn)情形下,連接器可以為section分配任意位置的存儲(chǔ)區(qū)域。你也可以用MEMORY命令定義存儲(chǔ)區(qū)域,并通過輸出section描述的> REGION屬性顯示地將該輸出section限定于某塊存儲(chǔ)區(qū)域,當(dāng)存儲(chǔ)區(qū)域大小不能滿足要求時(shí),連接器會(huì)報(bào)告該錯(cuò)誤。
MEMORY命令的文法如下,
MEMORY {
NAME1 [(ATTR)] : ORIGIN = ORIGIN1, LENGTH = LEN2
NAME2 [(ATTR)] : ORIGIN = ORIGIN2, LENGTH = LEN2

}
NAME :存儲(chǔ)區(qū)域的名字,這個(gè)名字可以與符號(hào)名、文件名、section名重復(fù),因?yàn)樗幱谝粋(gè)獨(dú)立的名字空間。
ATTR :定義該存儲(chǔ)區(qū)域的屬性,在講述SECTIONS命令時(shí)提到,當(dāng)某輸入section沒有在SECTIONS命令內(nèi)引用時(shí),連接器會(huì)把該輸入 section直接拷貝成輸出section,然后將該輸出section放入內(nèi)存區(qū)域內(nèi)。如果設(shè)置了內(nèi)存區(qū)域設(shè)置了ATTR屬性,那么該區(qū)域只接受滿足該屬性的section(怎么判斷該section是否滿足?輸出section描述內(nèi)好象沒有記錄該section的讀寫執(zhí)行屬性)。ATTR屬性內(nèi)可以出現(xiàn)以下7個(gè)字符,
R 只讀section
W 讀/寫section
X 可執(zhí)行section
A ‘可分配的’section
I 初始化了的section
L 同I
! 不滿足該字符之后的任何一個(gè)屬性的section
ORIGIN :關(guān)鍵字,區(qū)域的開始地址,可簡(jiǎn)寫成org或o
LENGTH :關(guān)鍵字,區(qū)域的大小,可簡(jiǎn)寫成len或l
例子,
MEMORY
{
rom (rx) : ORIGIN = 0, LENGTH = 256K
ram (!rx) : org = 0×40000000, l = 4M
}
此例中,把在SECTIONS命令內(nèi)*未*引用的且具有讀屬性或?qū)憣傩缘妮斎雜ection放入rom區(qū)域內(nèi),把其他未引用的輸入section放入 ram。如果某輸出section要被放入某內(nèi)存區(qū)域內(nèi),而該輸出section又沒有指明ADDRESS屬性,那么連接器將該輸出section放在該區(qū)域內(nèi)下一個(gè)能使用位置。
9. PHDRS命令
————
該命令僅在產(chǎn)生ELF目標(biāo)文件時(shí)有效。
ELF目標(biāo)文件格式用program headers程序頭(程序頭內(nèi)包含一個(gè)或多個(gè)segment程序段描述)來描述程序如何被載入內(nèi)存。可以用objdump -p命令查看。
當(dāng)在本地ELF系統(tǒng)運(yùn)行ELF目標(biāo)文件格式的程序時(shí),系統(tǒng)加載器通過讀取程序頭信息以知道如何將程序加載到內(nèi)存。要了解系統(tǒng)加載器如何解析程序頭,請(qǐng)參考ELF ABI文檔。
在連接腳本內(nèi)不指定PHDRS命令時(shí),連接器能夠很好的創(chuàng)建程序頭,但是有時(shí)需要更精確的描述程序頭,那么PAHDRS命令就派上用場(chǎng)了。
注意:一旦在連接腳本內(nèi)使用了PHDRS命令,那么連接器**僅會(huì)**創(chuàng)建PHDRS命令指定的信息,所以使用時(shí)須謹(jǐn)慎。
PHDRS命令文法如下,
PHDRS
{
NAME TYPE [ FILEHDR ] [ PHDRS ] [ AT ( ADDRESS ) ]
[ FLAGS ( FLAGS ) ] ;
}
其中FILEHDR、PHDRS、AT、FLAGS為關(guān)鍵字。
NAME :為程序段名,此名字可以與符號(hào)名、section名、文件名重復(fù),因?yàn)樗谝粋(gè)獨(dú)立的名字空間內(nèi)。此名字只能在SECTIONS命令內(nèi)使用。
一個(gè)程序段可以由多個(gè)‘可加載’的section組成。通過輸出section描述的屬性:PHDRS可以將輸出section加入一個(gè)程序段,: PHDRS中的PHDRS為程序段名。在一個(gè)輸出section描述內(nèi)可以多次使用:PHDRS命令,也即可以將一個(gè)section加入多個(gè)程序段。
如果在一個(gè)輸出section描述內(nèi)指定了:PHDRS屬性,那么其后的輸出section描述將默認(rèn)使用該屬性,除非它也定義了:PHDRS屬性。顯然當(dāng)多個(gè)輸出section屬于同一程序段時(shí)可簡(jiǎn)化書寫。
在TYPE屬性后存在FILEHDR關(guān)鍵字,表示該段包含ELF文件頭信息;存在PHDRS關(guān)鍵字,表示該段包含ELF程序頭信息。
TYPE可以是以下八種形式,
PT_NULL 0
表示未被使用的程序段
PT_LOAD 1
表示該程序段在程序運(yùn)行時(shí)應(yīng)該被加載
PT_DYNAMIC 2
表示該程序段包含動(dòng)態(tài)連接信息
PT_INTERP 3
表示該程序段內(nèi)包含程序加載器的名字,在linux下常見的程序加載器是ld-linux.so.2
PT_NOTE 4
表示該程序段內(nèi)包含程序的說明信息
PT_SHLIB 5
一個(gè)保留的程序頭類型,沒有在ELF ABI文檔內(nèi)定義
PT_PHDR 6
表示該程序段包含程序頭信息。
EXPRESSION 表達(dá)式值
以上每個(gè)類型都對(duì)應(yīng)一個(gè)數(shù)字,該表達(dá)式定義一個(gè)用戶自定的程序頭。
AT(ADDRESS)屬性定義該程序段的加載位置(LMA),該屬性將**覆蓋**該程序段內(nèi)的section的AT()屬性。
默認(rèn)情況下,連接器會(huì)根據(jù)該程序段包含的section的屬性(什么屬性?好象在輸出section描述內(nèi)沒有看到)設(shè)置FLAGS標(biāo)志,該標(biāo)志用于設(shè)置程序段描述的p_flags域。
下面看一個(gè)典型的PHDRS設(shè)置,
PHDRS
{
headers PT_PHDR PHDRS ;
interp PT_INTERP ;
text PT_LOAD FILEHDR PHDRS ;
data PT_LOAD ;
dynamic PT_DYNAMIC ;
}
SECTIONS
{
. = SIZEOF_HEADERS;
.interp : { *(.interp) } :text :interp
.text : { *(.text) } :text
.rodata : { *(.rodata) } /* defaults to :text */

. = . + 0×1000; /* move to a new page in memory */
.data : { *(.data) } :data
.dynamic : { *(.dynamic) } :data :dynamic

}
10. 版本號(hào)命令
————–
當(dāng)使用ELF目標(biāo)文件格式時(shí),連接器支持帶版本號(hào)的符號(hào)。
讀者可以發(fā)現(xiàn)僅僅在共享庫(kù)中,符號(hào)的版本號(hào)屬性才有意義。
動(dòng)態(tài)加載器使用符號(hào)的版本號(hào)為應(yīng)用程序選擇共享庫(kù)內(nèi)的一個(gè)函數(shù)的特定實(shí)現(xiàn)版本。
可以在連接腳本內(nèi)直接使用版本號(hào)命令,也可以將版本號(hào)命令實(shí)現(xiàn)于一個(gè)特定版本號(hào)描述文件(用連接選項(xiàng)–version-script指定該文件)。
該命令的文法如下,
VERSION { version-script-commands }
以下內(nèi)容直接拷貝于以前的文檔,
===================== 開始 ==================================
內(nèi)容簡(jiǎn)介
———
0 前提
1 帶版本號(hào)的符號(hào)的定義
2 連接到帶版本的符號(hào)
3 GNU擴(kuò)充
4 我的疑問
5 英文搜索關(guān)鍵字
6 我的參考
0. 前提
– 只限于ELF文件格式
– 以下討論用gcc
1. 帶版本號(hào)的符號(hào)的定義(共享庫(kù)內(nèi))
文件b.c內(nèi)容如下,
int old_true()
{
return 1;
}
int new_true()
{
return 2;
}
寫連接器的版本控制腳本,本例中為b.lds,內(nèi)容如下
VER1.0{
new_true;
};
VER2.0{
};
$gcc -c b.c
$gcc -shared -Wl,–version-script=b.lds -o libb.so b.o
可以在{}內(nèi)填入要綁定的符號(hào),本例中new_true符號(hào)就與VER1.0綁定了。
那么如果有一個(gè)應(yīng)用程序連接到該庫(kù)的new_true符號(hào),那么它連接的就是VER1.0版本的new_true符號(hào)
如果把b.lds更改為,
VER1.0{
};
VER2.0{
new_true;
};
然后在生成libb.so文件,在運(yùn)行那個(gè)連接到VER1.0版本的new_true符號(hào)的應(yīng)用程序,可以發(fā)現(xiàn)該應(yīng)用程序不能運(yùn)行了,
因?yàn)閹?kù)內(nèi)沒有VER1.0版本的new_true,只有VER2.0版本的new_true。
2. 連接到帶版本的符號(hào)
寫一個(gè)簡(jiǎn)單的應(yīng)用(名為app)連接到libb.so,應(yīng)用符號(hào)new_true
假設(shè)libb.so的版本控制文件為,
VER1.0{
};
VER2.0{
new_true;
};
$ nm app | grep new_true
U new_true@@VER1.0
$
用nm命令發(fā)現(xiàn)app連接到VER1.0版本的new_true
3. GNU的擴(kuò)充
它允許在程序文件內(nèi)綁定 *符號(hào)* 到 *帶版本號(hào)的別名符號(hào)*
文件b.c內(nèi)容如下,
int old_true()
{
return 1;
}
int new_true()
{
return 2;
}
__asm__( “.symver old_true,true@VER1.0″ );
__asm__( “.symver new_true,true@@VER2.0″ );
其中,帶版本號(hào)的別名符號(hào)是true,其默認(rèn)的版本號(hào)為VER2.0
供連接器用的版本控制腳本b.lds內(nèi)容如下,
VER1.0{
};
VER2.0{
};
版本控制文件內(nèi)必須包含版本VER1.0和版本VER2.0的定義,因?yàn)樵赽.c文件內(nèi)有對(duì)他們的引用
****** 假定libb.so與app.c在同一目錄下 ********
以下應(yīng)用程序app.c連接到該庫(kù),
int true();
int main()
{
printf( “%d “, true );
}
$ gcc app.c libb.so
$ LD_LIBRARY_PATH=. ./app
2
$ nm app | grep true
U true@@VER2.0
$
很明顯,程序app使用的是VER2.0版本的別名符號(hào)true,如果在b.c內(nèi)沒有指明別名符號(hào)true的默認(rèn)版本,
那么gcc app.c libb.so將出現(xiàn)連接錯(cuò)誤,提示true沒有定義。
也可以在程序內(nèi)指定特定版本的別名符號(hào)true,程序如下,
__asm__( “.symver true,true@VER1.0″ );
int true();
int main()
{
printf( “%d “, true );
}
$ gcc app.c libb.so
$ LD_LIBRARY_PATH=. ./app
1
$ nm app | grep true
U true@VER1.0
$
顯然,連接到了版本號(hào)為VER1.0的別名符號(hào)true。其中只有一個(gè)@表示,該版本不是默認(rèn)的版本
我的疑問:
版本控制腳本文件中,各版本號(hào)節(jié)點(diǎn)之間的依賴關(guān)系
英文搜索關(guān)鍵字:
.symver
versioned symbol
version a shared library
參考:
info ld, Scripts node
===================== 結(jié)束 ==================================
11. 表達(dá)式
———-
表達(dá)式的文法與C語(yǔ)言的表達(dá)式文法一致,表達(dá)式的值都是整型,如果ld的運(yùn)行主機(jī)和生成文件的目標(biāo)機(jī)都是32位,則表達(dá)式是32位數(shù)據(jù),否則是64位數(shù)據(jù)。
能夠在表達(dá)式內(nèi)使用符號(hào)的值,設(shè)置符號(hào)的值。
下面看六項(xiàng)表達(dá)式相關(guān)內(nèi)容,
常表達(dá)式:
_fourk_1 = 4K; /* K、M單位 */
_fourk_2 = 4096; /* 整數(shù) */
_fourk_3 = 0×1000; /* 16 進(jìn)位 */
_fourk_4 = 01000; /* 8 進(jìn)位 */
1K=1024 1M=1024*1024
符號(hào)名:
沒有被引號(hào)”"包圍的符號(hào),以字母、下劃線或’.'開頭,可包含字母、下劃線、’.'和’-'。當(dāng)符號(hào)名被引號(hào)包圍時(shí),符號(hào)名可以與關(guān)鍵字相同。如,
“SECTION”=9
“with a space” = “also with a space” + 10;
定位符號(hào)’.':
只在SECTIONS命令內(nèi)有效,代表一個(gè)程序地址空間內(nèi)的地址。
注意:當(dāng)定位符用在SECTIONS命令的輸出section描述內(nèi)時(shí),它代表的是該section的當(dāng)前**偏移**,而不是程序地址空間的絕對(duì)地址。
先看個(gè)例子,
SECTIONS
{
output :
{
file1(.text)
. = . + 1000;
file2(.text)
. += 1000;
file3(.text)
} = 0×1234;
}
其中由于對(duì)定位符的賦值而產(chǎn)生的空隙由0×1234填充。其他的內(nèi)容應(yīng)該容易理解吧。
再看個(gè)例子,
SECTIONS
{
. = 0×100
.text: {
*(.text)
. = 0×200
}
. = 0×500
.data: {
*(.data)
. += 0×600
}
} .text section在程序地址空間的開始位置是0x
表達(dá)式的操作符:
與C語(yǔ)言一致。
優(yōu)先級(jí) 結(jié)合順序 操作符
1 left ! – ~ (1)
2 left * / %
3 left + -
4 left >>  =
6 left &
7 left |
8 left &&
9 left ||
10 right ? :
11 right &= += -= *= /= (2)
(1)表示前綴符,(2)表示賦值符。
表達(dá)式的計(jì)算:
連接器延遲計(jì)算大部分表達(dá)式的值。
但是,對(duì)待與連接過程緊密相關(guān)的表達(dá)式,連接器會(huì)立即計(jì)算表達(dá)式,如果不能計(jì)算則報(bào)錯(cuò)。比如,對(duì)于section的VMA地址、內(nèi)存區(qū)域塊的開始地址和大小,與其相關(guān)的表達(dá)式應(yīng)該立即被計(jì)算。
例子,
SECTIONS
{
.text 9+this_isnt_constant :
{ *(.text) }
}
這個(gè)例子中,9+this_isnt_constant表達(dá)式的值用于設(shè)置.text section的VMA地址,因此需要立即運(yùn)算,但是由于this_isnt_constant變量的值不確定,所以此時(shí)連接器無法確立表達(dá)式的值,此時(shí)連接器會(huì)報(bào)錯(cuò)。
相對(duì)值與絕對(duì)值:
在輸出section描述內(nèi)的表達(dá)式,連接器取其相對(duì)值,相對(duì)與該section的開始位置的偏移
在SECTIONS命令內(nèi)且非輸出section描述內(nèi)的表達(dá)式,連接器取其絕對(duì)值
通過ABSOLUTE關(guān)鍵字可以將相對(duì)值轉(zhuǎn)化成絕對(duì)值,即在原來值的基礎(chǔ)上加上表達(dá)式所在section的VMA值。
例子,
SECTIONS
{
.data : { *(.data) _edata = ABSOLUTE(.); }
}
該例子中,_edata符號(hào)的值是.data section的末尾位置(絕對(duì)值,在程序地址空間內(nèi))。
內(nèi)建函數(shù):
ABSOLUTE(EXP) :轉(zhuǎn)換成絕對(duì)值
ADDR(SECTION) :返回某section的VMA值。
ALIGN(EXP) :返回定位符’.'的修調(diào)值,對(duì)齊后的值,(. + EXP – 1) & ~(EXP – 1)
BLOCK(EXP) :如同ALIGN(EXP),為了向前兼容。
DEFINED(SYMBOL) :如果符號(hào)SYMBOL在全局符號(hào)表內(nèi),且被定義了,那么返回1,否則返回0。例子,
SECTIONS { …
.text : {
begin = DEFINED(begin) ? begin : . ;

}

}
LOADADDR(SECTION) :返回三SECTION的LMA
MAX(EXP1,EXP2) :返回大者
MIN(EXP1,EXP2) :返回小者
NEXT(EXP) :返回下一個(gè)能被使用的地址,該地址是EXP的倍數(shù),類似于ALIGN(EXP)。除非使用了MEMORY命令定義了一些非連續(xù)的內(nèi)存塊,否則NEXT(EXP)與ALIGH(EXP)一定相同。
SIZEOF(SECTION) :返回SECTION的大小。當(dāng)SECTION沒有被分配時(shí),即此時(shí)SECTION的大小還不能確定時(shí),連接器會(huì)報(bào)錯(cuò)。
SIZEOF_HEADERS :
sizeof_headers :返回輸出文件的文件頭大小(還是程序頭大小),用以確定第一個(gè)section的開始地址(在文件內(nèi))。???
12. 暗含的連接腳本
輸入文件可以是目標(biāo)文件,也可以是連接腳本,此時(shí)的連接腳本被稱為 暗含的連接腳本
如果連接器不認(rèn)識(shí)某個(gè)輸入文件,那么該文件被當(dāng)作連接腳本被解析。更進(jìn)一步,如果發(fā)現(xiàn)它的格式又不是連接腳本的格式,那么連接器報(bào)錯(cuò)。
一個(gè)暗含的連接腳本不會(huì)替換默認(rèn)的連接腳本,僅僅是增加新的連接而已。
一般來說,暗含的連接腳本符號(hào)分配命令,或INPUT、GROUP、VERSION命令。
在連接命令行中,每個(gè)輸入文件的順序都被固定好了,暗含的連接腳本在連接命令行內(nèi)占住一個(gè)位置,這個(gè)位置決定了由該連接腳本指定的輸入文件在連接過程中的順序。
典型的暗含的連接腳本是libc.so文件,在GNU/linux內(nèi)一般存在/usr/lib目錄下。


本文來自ChinaUnix博客,如果查看原文請(qǐng)點(diǎn):http://blog.chinaunix.net/u/1595/showart_2130380.html
您需要登錄后才可以回帖 登錄 | 注冊(cè)

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號(hào)-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號(hào):11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國(guó)互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請(qǐng)注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP