ld链接脚本学习记录(一)

一、概念

将.o文件经过 ld链接脚本 生成可执行文件

加载地址和虚拟地址

lma =  load memory address (内存存储加载的地址)
vma =  vitual memory address (最终实际运行的地址)

二、格式 / 指令语法

1、格式

(1)MEMORY格式

MEMORY
{
	/*标准格式如下*/
	name [(attr)] : ORIGIN = origin, LENGTH = len
	
     /* Flash */
      interrupts          (RX)  : ORIGIN = 0x00000000, LENGTH = 0x000000C0
      flash_config        (RX)  : ORIGIN = 0x00000400, LENGTH = 0x00000010
      text                (RX)  : ORIGIN = 0x00000410, LENGTH = 0x10000

      /* SRAM */
      data                (RW)  : ORIGIN = 0x1FFFFC00, LENGTH = 0x1000
}

ORIGIN:最终实际运行的地址(vma)

LENGTH:属于该name的存储长度

(2)SECTIONS格式

用来指定目标文件生成输出文件时的规则(输出段格式)

SECTIONS { 
    ... 
    sectionname [address_vma] [align] : AT (lma) 
        { 
            output-section-command
            ...
        } [>region] [AT>lma_region] [:phdr :phdr ...] [=fillexp]
    ... 
}

sectionname:输出段名        

【通常包含以下输出段名:.text 代码段        .rodata 只读常量       .data 初始化的全局变量        .bss  未初始化的全局变量】

[address_vma]:生成的执行文件中,该段所运行的位置(vma)

[align]:指定段起始的对齐大小字节数

AT(lma):指定该段实际内存存储加载的地址(lma)

output-section-command:指定输入段文件内容

[>region]:指定输出段所指向的运行地址(vma)

[AT>lma_region]:指定输出段所指向的加载地址(lma)

[:phdr :phdr ...]:将该段分配给一个或多个程序段

[=fillexp]:指定该段的初始填充值

【output-section-command】

可以是输入段文件,也可以是输入段文件中的某一个段信息

.interrupts_ram :
  {
    . = ALIGN(4);
    __VECTOR_RAM__ = .;
    __RAM_START = .;
    __interrupts_ram_start__ = .; /* Create a global symbol at data start. */
    *(.m_interrupts_ram)          /* This is a user defined section. */
    . += M_VECTOR_RAM_SIZE;       /* 0xC0 */
    . = ALIGN(4);
    __interrupts_ram_end__ = .;   /* Define a global symbol at data end. */
  } > data

 __VECTOR_RAM = DEFINED(__flash_vector_table__) ? ORIGIN(m_interrupts) : __VECTOR_RAM__ ;
  __RAM_VECTOR_TABLE_SIZE = DEFINED(__flash_vector_table__) ? 0x0 : (__interrupts_ram_end__ - __interrupts_ram_start__) ;

.data : AT(_DATA_ROM)
  {
    . = ALIGN(4);
    _DATA_RAM = .;
    __data_start__ = .;      /* Create a global symbol at data start. */
    *(.data)                 /* .data sections */
    *(.data*)                /* .data* sections */
    data3.o(.abcd)
    KEEP(*(.jcr))
    . = ALIGN(4);
    __data_end__ = .;        /* Define a global symbol at data end. */
  } > data  AT>_DATA_ROM

......

.xxx:
  {
    . = ALIGN(4);
    __data_start__ = .;      /* Create a global symbol at data start. */
    xxx
    . = ALIGN(4);
    __data_end__ = .;        /* Define a global symbol at data end. */
  } > data

符号 .    位置计数器

*(.data)  指定输入段为所有输入文件中的 .data 段

*(.data*)  指定输入段为所有输入文件中以 .data* 段( * 作为通配符)

data3.o(.abcd)  指定输入段为文件data3.o中的 .abcd 段

KEEP(*(.jcr))  防止链接器优化,强制让链接器保留指定的输入段

【上述代码含义:将data输出段加载在_DATA_ROM处,实际运行地址在SRAM中(MEMORY中的data段在SRAM) 】

【出现多个输出段都 > data 时,从data( : ORIGIN = 0x1FFFFC00)的vma地址0x1FFFFC00依次开始分配虚拟运行地址】

2、指令

(1)ENTRY指令

ENTRY(Reset_Handler)

ENTRY指定程序入口地址,当前Reset_Handler()函数为重启处理接口

(2)KEEP指令

 KEEP(*(.jcr))

防止链接器优化,强制让链接器保留指定的输入段

强制保留所有输入文件中的 .jcr段

二、链接后生成.map文件

.interrupts_ram
                0x1ffffc00       0xc0
                0x1ffffc00                . = ALIGN (0x4)
                0x1ffffc00                __VECTOR_RAM__ = .
                0x1ffffc00                __RAM_START = .
                0x1ffffc00                __interrupts_ram_start__ = .
 *(.m_interrupts_ram)
                0x1ffffcc0                . = (. + M_VECTOR_RAM_SIZE)

*fill*          0x1ffffc00       0xc0 
                0x1ffffcc0                . = ALIGN (0x4)
                0x1ffffcc0                __interrupts_ram_end__ = .
                0x1ffffc00                __VECTOR_RAM = DEFINED(__flash_vector_table__)?ORIGIN (m_interrupts):__VECTOR_RAM__
                0x000000c0                __RAM_VECTOR_TABLE_SIZE = DEFINED (__flash_vector_table__)?0x0:(__interrupts_ram_end__ - __interrupts_ram_start__)

.data           0x1ffffcc0      0x19c     load address 0x00004fb4
                0x1ffffcc0                . = ALIGN (0x4)
                0x1ffffcc0                _DATA_RAM = .
                0x1ffffcc0                __data_start__ = .
 *(.data)
 *(.data*)

......

【上述代码含义:data输出段中,vma存放的为 .interrupts_ram + .data + ... ... (从0x1FFFFC00开始分配)

.interrupts_ram大小为0xC0,加载地址默认等同于运行地址,为0x1ffffc00;

.data大小计算为0x19C,加载load address 为0x00004fb4,运行地址为0x1ffffcc0】

三、源码

1、copy 加载lma到运行vma

以S32K系列芯片为例:

startup_S32Kxxx.S文件

...... 

/* Init .data and .bss sections */
    ldr     r0,=init_data_bss
    blx     r0
;    cpsie   i               /* Unmask interrupts */

......

在startup.c文件中调用

void init_data_bss(void)
{
......

 /* Data */
    data_ram        = (uint8_t *)__DATA_RAM;
    data_rom        = (uint8_t *)__DATA_ROM;
    data_rom_end    = (uint8_t *)__DATA_END;
    /* CODE RAM */
    code_ram        = (uint8_t *)__CODE_RAM;
    code_rom        = (uint8_t *)__CODE_ROM;
    code_rom_end    = (uint8_t *)__CODE_END;

......

/* Copy initialized data from ROM to RAM */
    while (data_rom_end != data_rom)
    {
        *data_ram = *data_rom;
        data_ram++;
        data_rom++;
    }

    /* Copy functions from ROM to RAM */
    while (code_rom_end != code_rom)
    {
        *code_ram = *code_rom;
        code_ram++;
        code_rom++;
    }

......

}

其中__DATA_RAM / __DATA_ROM等宏定义,在ld链接脚本中被赋值

【上述代码含义:ROM地址存储信息依次赋值到RAM地址,实现特定输入段的信息转移至RAM中运行】

四、代码编写

1、增加指定的段信息 

__attribute__((section(".xxx"))) uint8_t example = 0;

__attribute__((section(".xxx"))) static int demo(void);

2、中断开启/关闭

__asm("cpsid i");    /* 中断禁用 */

__asm("cpsie i");    /* 中断启用 */

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值