簡單的ld連結指令碼學習
一、 連結指令碼的整體認識
什麼是連結檔案呢?作用是什麼呢? 當編寫了多個C檔案時,我們將他們編譯連結成一個可執行的檔案,此時就需要用到連結指令碼檔案(ld)。ld指令碼主要功能就是:將多個目標檔案(.o)和庫檔案(.a)連結成一個可執行的檔案。
連結指令碼檔案主要有什麼內容呢? 為了規範,我們分為三個部分:
- 連結配置(可有可無)
如一些符號變數的定義、入口地址、輸出格式等
STACK_SIZE = 0X200;
OUTPUT_FORMAT(elf32-littlearm)
OUTPUT_ARCH(arm)
ENTRY(_start)
- 記憶體佈局定義
指令碼中以MEMORY命令定義了儲存空間,其中以ORIGIN定義地址空間的起始地址,LENGTH定義地址空間的長度。
MEMORY
{
FLASH (rx) : ORIGIN = 0, LENGTH = 64K
}
- 段連結定義
指令碼中以SECTIONS命令定義一些段(text、data、bss等段)連結分佈。
SECTIONS
{
.text :
{
*(.text*)
} > FLASH
}
.text段即程式碼段,* (.text*)指示將工程中所有目標檔案的.text段連結到FLASH中
二、常用關鍵字、命令
- MEMORY命令
使用MEMORY來定義記憶體如下:
MEMORY { NAME1 [(ATTR)] : ORIGIN = ORIGIN1, LENGTH = LEN2 NAME2 [(ATTR)] : ORIGIN = ORIGIN2, LENGTH = LEN2 … }
NAME :儲存區域的名字。(自己可以隨意命名)
ATTR :定義該儲存區域的屬性。ATTR屬性內可以出現以下7 個字元:
- R 只讀section
- W 讀/寫section
- X 可執行section
- A 可分配的section
- I 初始化了的section
- L 同I
- ! 不滿足該字元之後的任何一個屬性的section
ORIGIN :關鍵字,區域的開始地址,可簡寫成org 或o
LENGTH :關鍵字,區域的大小,可簡寫成len 或l
MEMORY
{
rom (rx) : ORIGIN = 0, LENGTH = 256K
ram (!rx) : org = 0×40000000, l = 4M
}
- 定位符號‘.’的使用
‘.’表示當前地址,它可以被賦值也可以賦值給某個變數。 如下為將當前地址賦值給某個變數(連結器連結是按照SECTIONS裡的段順序排列的,前面的排列完之後就能計算出當前地址)
RAM_START = .;
如下為將段存放在特定的地址中:
SECTIONS
{
. = 0×10000;
.text :
{
*(.text)
}
. = 0×8000000;
.data :
{
*(.data)
}
}
“. = 0×10000;”該語句表示將當前地址設定為0x10000。如上程式碼中,意思是將所有目標檔案的text段從0x10000地址開始存放。
3 SECTIONS 命令
SECTIONS基本的命令語法如下:
SECTIONS
{
...
secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
{
contents
} >region :phdr =fill
...
}
這麼多引數中,只有secname 和 contents 是必須的,即可簡寫成:
SECTIONS
{
...
secname :
{
contents
}
...
}
連結指令碼本質就是描述輸入和輸出。secname表示輸出檔案的段,即輸出檔案中有哪些段。而contents就是描述輸出檔案的這個段從哪些檔案裡抽取而來,即輸入檔案,一般就是目標檔案之類的。 如下,將目標檔案的資料段存放在輸出檔案的資料段(段名自己定義,段名前後必須要有空格)
SECTIONS
{
...
.data :
{
main.o(.data)
*(.data)
}
...
}
其中 *(.data) 表示將所有的目標的.data段連結到輸出檔案.datad段中, 特別注意的是,之前連結的就不會再連結,這樣做的目的是可以將某些特殊的目標檔案連結到地址前面。
我們繼續來講一下其他引數。
- start :表示將某個段強制連結到的地址。
- AT(addr):實現存放地址和載入地址不一致的功能,AT表示在檔案中存放的位置,而在記憶體裡呢,按照普通方式儲存。
- region:這個region就是前面說的MEMORY命令定義的位置資訊。
- PROVIDE關鍵字:
該關鍵字定義一個(目標檔案內被引用但沒定義)符號。相當於定義一個全域性變數,其他C檔案可以引用它。
SECTIONS
{
.text :
{
*(.text)
_etext = .;
PROVIDE(etext = .);
}
}
如上,目標檔案可以引用etext符號,其地址為定義為.text section之後的第一個位元組的地址。C檔案中引用。
int main()
{
//引用該變數
extern char _etext;
//...
}
- KEEP 關鍵字
在連線命令列內使用了選項–gc-sections後,聯結器可能將某些它認為沒用的section過濾掉,此時就有必要強制聯結器保留一些特定的 section,可用KEEP()關鍵字達此目的。如KEEP(* (.text))或KEEP(SORT(*)(.text))
三、簡單示例
下面以KL26晶片的連結指令碼作為一個簡單的示例,程式碼如下:
/*
* In this linker script there is no heap available.
* The stack start at the end of the ram segment.
*/
STACK_SIZE = 0x2000; /* stack size config 8k */
/*
* Take a look in the "The GNU linker" manual, here you get
* the following information about the "MEMORY":
*
* "The MEMORY command describes the location and size of
* blocks of memory in the target."
*/
MEMORY
{
FLASH_INT (rx) : ORIGIN = 0x00000000, LENGTH = 0x00000100
FLASH_CONFIG (rx) : ORIGIN = 0x00000400, LENGTH = 0x00000010
FLASH_TEXT (rx) : ORIGIN = 0x00000410, LENGTH = 0x0001F7F0
RAM (rwx) : ORIGIN = 0x1FFFF000, LENGTH = 16K
}
/*
* And the "SECTION" is used for:
*
* "The SECTIONS command tells the linker how to map input
* sections into output sections, and how to place the output
* sections in memory.
*/
SECTIONS
{
/* The startup code goes first into internal flash */
.interrupts :
{
__VECTOR_TABLE = .;
. = ALIGN(4);
KEEP(*(.vectors)) /* Startup code */
. = ALIGN(4);
} > FLASH_INT
.flash_config :
{
. = ALIGN(4);
KEEP(*(.FlashConfig)) /* Flash Configuration Field (FCF) */
. = ALIGN(4);
} > FLASH_CONFIG
.text :
{
_stext = .; /* Provide the name for the start of this section */
*(.text)
*(.text.*) /* cpp namespace function */
*(.romrun) /* rom中必須的函式 */
. = ALIGN(4); /* Align the start of the rodata part */
*(.rodata) /* read-only data (constants) */
*(.rodata*)
*(.glue_7)
*(.glue_7t)
} > FLASH_TEXT
/* section information for simple shell symbols */
.text :
{
. = ALIGN(4);
__shellsym_tab_start = .;
KEEP(*(.shellsymbol))
__shellsym_tab_end = .;
} >FLASH_TEXT
/* .ARM.exidx is sorted, so has to go in its own output section */
. = ALIGN(4);
__exidx_start = .;
PROVIDE(__exidx_start = __exidx_start);
.ARM.exidx :
{
/* __exidx_start = .; */
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
/* __exidx_end = .; */
} > FLASH_TEXT
. = ALIGN(4);
__exidx_end = .;
PROVIDE(__exidx_end = __exidx_end);
/*
* C++ 全域性物件構造與解構函式表
* 這裡放在 .text 和 .ARM.exidx 之後, .data 之前,
* 這裡的 LMA 和 VMA 相同, 如果放在 .data 之後, LMA 與 VMA 不同,
* 則需要啟動程式從裝載區搬運到執行區
*/
. = ALIGN(4);
.ctors :
{
KEEP (*cppRtBegin*.o(.ctors))
KEEP (*(.preinit_array))
KEEP (*(.init_array))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
KEEP (*cppRtEnd*.o(.ctors))
}
.dtors :
{
KEEP (*cppRtBegin*.o(.dtors))
KEEP (*(.fini_array))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
KEEP (*cppRtEnd*.o(.dtors))
}
/* .data 段資料初始化內容放在這裡 */
. = ALIGN(16);
_etext = . ;
PROVIDE (etext = .);
/*
* The ".data" section is used for initialized data
* and for functions (.fastrun) which should be copied
* from flash to ram. This functions will later be
* executed from ram instead of flash.
*/
.data : AT (_etext)
{
. = ALIGN(4); /* Align the start of the section */
_sdata = .; /* Provide the name for the start of this section */
*(.data)
*(.data.*)
. = ALIGN(4); /* Align the start of the fastrun part */
*(.fastrun)
*(.fastrun.*)
. = ALIGN(4); /* Align the end of the section */
} >
_edata = .; /* Provide the name for the end of this section */
USB_RAM_GAP = DEFINED(__usb_ram_size__) ? __usb_ram_size__ : 0x800;
/*
* The ".bss" section is used for uninitialized data.
* This section will be cleared by the startup code.
*/
.bss :
{
. = ALIGN(4); /* Align the start of the section */
_sbss = .; /* Provide the name for the start of this section */
*(.bss)
*(.bss.*)
. = ALIGN(512);
USB_RAM_START = .;
. += USB_RAM_GAP;
. = ALIGN(4); /* Align the end of the section */
} > RAM
_ebss = .; /* Provide the name for the end of this section */
/* 系統堆 */
. = ALIGN(4);
PROVIDE (__heap_start__ = .);
.heap (NOLOAD) :
{
} > RAM
. = ORIGIN(RAM) + LENGTH(RAM) - STACK_SIZE;
. = ALIGN(4);
PROVIDE (__heap_end__ = .);
/*
* The ".stack" section is our stack.
* Here this section starts at the end of the ram segment.
*/
_estack = ORIGIN(RAM) + LENGTH(RAM);
m_usb_bdt USB_RAM_START (NOLOAD) :
{
*(m_usb_bdt)
USB_RAM_BDT_END = .;
}
m_usb_global USB_RAM_BDT_END (NOLOAD) :
{
*(m_usb_global)
}
}
/*** EOF **/