連結指令碼檔案(.ld .lds)詳解
連結指令碼例項:(STM32F407VG,RT-Thread Studio生成的工程所含)
* linker script for STM32F407ZG with GNU ld */ /* Program Entry, set to mark it as "used" and avoid gc */ MEMORY { ROM (rx) : ORIGIN = 0x08000000, LENGTH = 1024k /* 1024K flash */ RAM (rw) : ORIGIN = 0x20000000, LENGTH = 128k /* 128K sram */ } ENTRY(Reset_Handler) _system_stack_size = 0x400; SECTIONS { .text : { . = ALIGN(4); _stext = .; KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
*(.text) /* remaining code */
*(.text.*) /* remaining code */
*(.rodata) /* read-only data (constants) */
/* section information for utest */ . = ALIGN(4); __rt_utest_tc_tab_start = .; KEEP(*(UtestTcTab)) __rt_utest_tc_tab_end = .; . = ALIGN(4); PROVIDE(__ctors_start__ = .); KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array)) PROVIDE(__ctors_end__ = .); . = ALIGN(4); _etext = .; } > ROM = 0 /* .data section which is used for initialized data */ .stack : { . = ALIGN(4); _sstack = .; . = . + _system_stack_size; . = ALIGN(4); _estack = .; } >RAM __bss_start = .; .bss : { . = ALIGN(4); /* This is used by the startup in order to initialize the .bss secion */ _sbss = .; *(.bss) *(.bss.*) *(COMMON) . = ALIGN(4); /* This is used by the startup in order to initialize the .bss secion */ _ebss = . ; *(.bss.init) } > RAM __bss_end = .; _end = .; /* Stabs debugging sections. */ .stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } .stab.excl 0 : { *(.stab.excl) } .stab.exclstr 0 : { *(.stab.exclstr) } }
特別注意:
1 .text section :{} .stack :{} 表示輸出檔案包含的 section
2 {}裡面的 section,是輸入檔案的 section,比如 *(.isr_vector) *(.text) *(.rodata) 這些 .isr_vector section .text section .rodata section,都有指定輸入檔案,*表示所有的輸入檔案;所以 *(.isr_vector) 表示從所有的輸入檔案中獲取所有 .isr_vector section 放在一塊連續的地址空間;main.o(.data) 表示從 main.o檔案中獲取所有的 .data section 放在一塊連續的地址空間
3 連結指令碼從上往下,如果輸入檔案 A 已經被取出 .text section,此後輸入檔案就沒有 .text section,不能再被獲取
概述:
連結器:將多個目標檔案(.o)和庫檔案(.a)連結成一個可執行的檔案,連結器從連結指令碼讀完一個 section 後,將定位器符號的值增加該 section 的大小
連結指令碼:控制輸出檔案內各部分在程式地址空間內的佈局
語法:
-T 選項用於指定自己的連結指令碼,否則使用預設的連結指令碼
. 是定位器符號,可以對定位器符號賦值指定接下來內容的儲存位置,如“ .= 0x10000”,也可以通過定位器符獲取此位置的地址,比如 ”_stext = .;”,此後就可以用變數 _stext 表示此位置地址
關鍵詞 SECTIONS 內包含多個 section,section名(如 .text)不佔用地址空間
SECTIONS { .= 0x10000; .text : { *(.text) } .= 0×8000000; .data : { *(.data) } .bss : { *(.bss) } }
解釋一下上訴的例子:
.= 0x10000:把定位器符號置為 0x10000(若不指定,則該符號的初始值為0)
.text : { *(.text) }:*符號代表所有的輸入檔案,此句表示獲取所有輸入檔案的 .text section放在一塊連續的地址空間,首地址由上一句的定位器符號確定,即 0x10000
.= 0x8000000:把定位器符號置為 0x8000000
.data : { *(.data) }:獲取所有輸入檔案的 .data section 放在一塊連續的地址空間,該 section 的首地址為 0x8000000
.bss : { *(.bss) }:獲取所有輸入檔案的 .bss section 放在一塊連續的地址空間,該 section 的首地址為 0x8000000 + .data section 的大小
輸出檔案包含 .text section .data section .bss section
入口地址
ENTRY(SYMBOL):將符號 SYMBOL 的值設定為入口地址,入口地址是程序執行的第一條指令在程序地址空間的地址(比如ENTRY(Reset_Handler) 表示程序最開始從復位中斷服務函式處執行)
有多種方法設定程序入口地址,以下編號越小,優先順序越高
1、ld 命令列的 -e 選項
2、連結指令碼的 ENTRY(SYMBOL) 命令
3、在彙編程式中定義了 start 符號,使用 start 符號值
4、如果存在 .text section,使用 .text section 首地址的值
5、使用地址 0 的值
記憶體佈局
指令碼中以MEMORY命令定義了儲存空間,其中以ORIGIN定義地址空間的起始地址,LENGTH定義地址空間的長度。
MEMORY { ROM (rx) : ORIGIN = 0x08000000, LENGTH = 1024k /* 1024K flash */ RAM (rw) : ORIGIN = 0x20000000, LENGTH = 128k /* 128K sram */ }
在連結檔案中定義的變數可以在目標檔案中使用
在連結檔案中定義變數
_init_start = .; .application_init : { *(.application_init) } _init_end = .;
在原始檔中使用變數 _init_start
#include <stdio.h> #include <string.h> struct _s_application_init { int(*function)(void); }; extern struct _s_application_init _init_start;//段".application_init"的起始地址,在*.lds檔案中定義 extern struct _s_application_init _init_end;//段".application_init"的末尾地址,在*.lds檔案中定義 #define __app_init_section __attribute__((section(".application_init"))) #define __application_init(function) \ struct _s_application_init _s_a_init_##function __app_init_section = {function} static int application_init_a(void) { printf("execute funtion : %s\n", __FUNCTION__); return 0; } __application_init(application_init_a);int main(int argc, char **argv) { /* * 從段的起始地址開始獲取資料,直到末尾地址 */ struct _s_application_init *pf_init = &_init_start; do { printf("Load init function from address %p\n", pf_init); pf_init->function(); ++pf_init; } while (pf_init < &_init_end); return 0; }
除了可以在 C原始檔中指定函式屬於某個 section,彙編檔案也可以,比如 startup.s
.section .text.Reset_Handler .weak Reset_Handler .type Reset_Handler, %function Reset_Handler: ldr sp, =_estack /* set stack pointer */ bl entry bx lr .size Reset_Handler, .-Reset_Handler
.section .isr_vector,"a",%progbits .type g_pfnVectors, %object .size g_pfnVectors, .-g_pfnVectors g_pfnVectors: .word _estack .word Reset_Handler
section 結構
SECTIONS { ... secname start BLOCK(align) (NOLOAD) : AT ( ldadr ) { contents } >region :phdr =fill ... }
這麼多引數中,只有secname 和 contents 是必須的,即可簡寫成:
SECTIONS
{
...
secname :
{
contents
}
...
}
secname表示輸出檔案的 section,即輸出檔案中有哪些 section。而contents就是描述輸出檔案的這個 section 內容從哪些檔案的哪些 section 裡抽取而來。