Linker script 鏈結器指令碼(1)

2021-04-01 10:38:05 字數 3711 閱讀 5779

每個鏈結都由鏈結指令碼控制著,指令碼由鏈結器命令語言組成。指令碼的主要目的是描述如何把輸入檔案中的節(sections)對映到輸出檔案中,並控制輸出檔案的儲存布局。大多數的鏈結指令碼就是做這些事情的,但在有必要時,指令碼也可以指導鏈結器執行一些其他的操作。

鏈結器總是使用鏈結器指令碼,如果你沒有提供乙個你自己的指令碼檔案的話,編譯器會使用乙個預設的指令碼,而它被編譯進鏈結器(?)。你可以使用"-verbose"命令列引數來顯示預設的鏈結指令碼。而某些命令列選項,像"-r","-n"會影響預設的鏈結指令碼。

在命令列選項中,通過引數"-t"你可以提供你自己的鏈結器指令碼,這樣就會替換預設的指令碼了。

你還可以隱式地使用指令碼,只要給乙個指令碼檔案命名,並作為輸入檔案提交給鏈結器,就像是它們都是要被鏈結的檔案一樣。具體的內容清檢視第11節。以下是目錄:

1 basic linker script concepts

2 linker script format

3 ****** linker script example

4 ****** linker script ***mands

5 assigning values to symbols

6 sections ***mand

7 memory ***mand

8 phdrs ***mand

9 version ***mand

10 expressions in linker scripts

11 implicit linker scripts

1 基本的鏈結器指令碼的概念

這裡我們會定義一些基本的概念和一些詞彙,來描述鏈結器指令碼語言。

鏈結器把一些輸入檔案聯合在一起,生成輸出檔案。輸出的檔案和輸入檔案都是object檔案格式,每個檔案都被稱為物件檔案(object file),而且,輸出檔案還經常被稱為可執行檔案。但這裡我們依然稱之為物件檔案。每個物件檔案在其中都包含有乙個節(section)列表,我們有時稱輸入檔案中的節(section)為輸入節(input section),同樣,輸出檔案中的節稱為輸出節(output section)。

物件檔案中的每乙個節都有名字和大小。大多數的節還有乙個相連的資料塊,就是有名的"section contents"。乙個被標記為可載入(loadable)的節,意味著在輸出檔案執行時,contents可以被載入到記憶體中。沒有contents的節也可以被載入,實際上處了乙個陣列被設定外,沒有其他的東西被載入(在一些情況下,儲存器必須被清0)。而既不是可載入的又不是可分配的(allocatable)節,通常包含了某些除錯資訊。

每個可載入或可分配的輸出節(output section)都有2個位址。第乙個是虛擬儲存位址vma(virtual memory address),這是在輸出檔案執行時該節所使用的位址。第二個是載入儲存位址lma(load memory address),這是該節被載入是的位址。在大多數情況下,這兩個位址是相同的。有個例子說明不同時的情況:當乙個資料節(data section)載入在rom中,後來在程式開始執行時又拷貝到ram中(在基於rom的系統中,這種技術經常用在初始化全域性變數中)。在這種基於rom的系統情況下,這時,rom位址是lma,而記憶體位址是vma。

要檢視乙個物件檔案中各個節,可以使用objdump,並使用"-h"引數。

每個物件檔案也有乙個符號(symbles)列表,這就是著名的符合表(symble table)。乙個符號可以是"已定義"(defined)或"無定義"(undefined)的。每個符號有名字,並且每個定義了的符號還有位址。在你編譯乙個c/c++程式成物件檔案時,每個定義的函式,全域性變數,靜態變數,都可以有乙個"已定義"的符號。輸入檔案中引用的每個沒有定義的函式和全域性變數則變成"無定義"的符號。

使用nm可以檢視物件檔案中的符合,objdump並使用"-t"選項也可以。

2 鏈結器指令碼格式

鏈結器指令碼是乙個文字檔案。

鏈結器指令碼是乙個命令序列,每個命令是乙個關鍵字,可能還帶著引數,又或者是對乙個符號的賦值。你可以使用分號來隔開命令,而空格則通常被忽略。

像檔名,格式名等字串通常直接輸入,如果檔名包含有像用於分割檔名的逗號等有其他用處的字元的話,你可以用雙引號把檔名括起來。當然沒有辦法在檔名中使用雙引號了。

你可以使用注釋,就像在c中,定界符是"/*"和"*/",和c中一樣,注釋在語法上等同於空格。

3 簡單的指令碼例子

很多的了解指令碼都比較簡單。可能最簡單的鏈結器指令碼只有乙個命令: 'sections'。使用'sections'命令描述輸出檔案的記憶體布局。

'sections'命令功能強大。這裡描述乙個簡單的使用。我們假設你的只有**(code),初始資料(initialized data)和未初始化的資料(uninitialized data)。它們要分別被放到'.text', '.data', '.bss'節中。更進一步假定它們是輸入檔案中的所有的節。

這個例子中,**要載入到位址0x10000,資料要從位址0x8000000開始。鏈結指令碼如下:

sections

. = 0x8000000;

.data :

.bss :

}'sections'命令的關鍵字是'sections',接著是一系列的符號(symbol)賦值,輸出節(output section)描述被大括號包括著。

上面例子中,在'sections'命令裡面,第一行設定乙個值到乙個特殊的符號'.',它是位置計數器(location counter),(像程式計數器pc)。如果你沒有以某種其他的方式指定輸出節(output section)的位址,位址就會是位置計數器中設定的當前值。而後,位置計數器就會以輸出節的大小增加其值。在'sections'命令的開始,位置計數器是0。

第2行定義'.text'輸出節。冒號是必須的語法。在大括號裡,輸出節名字之後,你要列出要輸入節的名字,它們會放入輸出節中。萬用字元"*"匹配任何檔名,表示式"*(.text)"意味著所有的輸入檔案中的輸入節".text"。

因為在輸出節".text"定義時,位置計數器是0x10000,所以鏈結器會設定輸出檔案中的".text"節的位址為0x10000。

剩下的行定義輸出檔案中的".data"和".bss"節。鏈結器會把輸出節".data"放置到位址0x8000000。之後,鏈結器把輸出節".data"的大小加到位置計數器的值0x8000000, 並立即設定".bss"輸出節,效果是在記憶體中,".bss"節會緊隨".data"之後。

鏈結器會確保每個輸出節都有必要的對齊,它會在需要是增加位置計數器的值。在上面的例子中,指定的".text"和".data"節的位址都是符合對齊條件的,但是鏈結器可能會在".data"和".bss"間生成乙個小間隙。

4 簡單的鏈結器指令碼命令

這裡我們會描述簡單的鏈結器指令碼命令

4.1 setting the entry point

4.2 ***mands dealing with files

4.3 ***mands dealing with object file formats

4.4 other linker script ***mands

4.1 設定入口點

在乙個程式中第乙個指令稱為入口點(entry point)。可以使用entry鏈結器指令碼命令來設定入口點。引數是乙個符號名。

entry(symbol)

有幾種不同的方式來設定入口點。鏈結器會依次用下面的方法嘗試設定入口點,當遇到成功時則停止。

命令列選項"-e"  entry

指令碼中的"entry(symbol)"

如果有定義"start"符號,則使用start符號(symbol)。

如果存在".text"節,則使用第乙個位元組的位址。

位址0。

鏈結器之靜態鏈結與動態鏈結

一 前言 二 鏈結 在 深入理解計算機系統 一書中,p448,鏈結的定義是 將各種 和資料部分收集起來並組合為乙個單一檔案的過程,這個檔案可被載入 或被拷貝 到儲存器並執行。這是定義,那麼實際中呢?鏈結的過程就是將.cpp對應的.obj windows下 和.lib 其內部也是一組.obj,即一組可...

鏈結器指令碼

鏈結器指令碼構成 段 起始鏈結位址 對齊 變數 段首檔案 乙個可執行程式通常由 段,資料段,bss段構成。其中資料段存放的是初始化後的前期變數 bss段存放的是沒有初始化的前期變數 乙個鏈結器指令碼led.lds例子 output arch arm 指定輸出檔案的平台體系是arm entry sta...

鏈結器指令碼

參考 程式設計師的自我修養 鏈結 裝載與庫 什麼是鏈結指令碼?鏈結指令碼就是程式鏈結時的參考檔案,其主要目的是描述如何把輸入檔案中的段 section 對映到輸出檔案中,並控制輸出檔案的儲存布局。鏈結指令碼的基本命令式sections命令,乙個sections命令內部包含乙個或多個段,段 secti...