1. 程式人生 > 其它 >連結指令碼檔案(.ld .lds)詳解

連結指令碼檔案(.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 裡抽取而來。