欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

深入解析链接脚本文件 (.ld, .lds)

最编程 2024-01-19 19:16:46
...

 链接脚本实例:(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 里抽取而来。

原文地址:https://www.cnblogs.com/god-of-death/p/14879078.html

推荐阅读