链接脚本(LD)段书写规范详解

链接脚本(LD)段书写规范详解

## 链接脚本基本结构一个标准的链接脚本包含以下部分:

链接脚本(LD)段书写规范详解

链接脚本(Linker Script)是控制程序内存布局的关键文件,其语法有严格的规范要求。下面我将详细介绍LD脚本中各个段的书写规范,并附图示说明。

链接脚本基本结构

/* 注释格式:使用C风格注释 */
MEMORY {
    /* 内存区域定义 */
}

SECTIONS {
    /* 段定义 */
}

1. 内存区域定义规范(MEMORY)

MEMORY {
    /* 基本语法:名称 (属性) : ORIGIN = 起始地址, LENGTH = 长度 */
    ROM (rx)  : ORIGIN = 0x08000000, LENGTH = 512K   /* 可读可执行 */
    RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K   /* 可读可写可执行 */
    
    /* 可选特殊区域 */
    ITCM (rwx) : ORIGIN = 0x00000000, LENGTH = 16K   /* 紧耦合内存 */
    DTCM (rw)  : ORIGIN = 0x10000000, LENGTH = 64K
}

属性说明:

属性含义是否必需
r可读
w可写
x可执行
a可分配可选
i初始化可选
l类似i可选

内存布局图示:

┌──────────────────────┐ 0x08000000
│         ROM          │
│   (512K, rx)        │
├──────────────────────┤ 0x08080000
│         RAM          │
│   (128K, rwx)       │
├──────────────────────┤ 0x20020000
│         ITCM         │
│    (16K, rwx)       │
├──────────────────────┤ 0x00004000
│         DTCM         │
│     (64K, rw)       │
└──────────────────────┘ 0x10010000

2. 段定义规范(SECTIONS)

2.1 基本段定义

SECTIONS {
    /* 定位符:. 表示当前位置 */
    . = 0x08000000; /* 设置起始地址 */
    
    /* 文本段(代码) */
    .text : {
        /* 输入段排序 */
        KEEP(*(.vectors))    /* 必须保留中断向量表 */
        *(.text*)            /* 所有.text开头的段 */
        *(.rodata*)          /* 只读数据 */
        . = ALIGN(4);        /* 4字节对齐 */
    } > ROM
    
    /* 数据段 */
    .data : {
        _sdata = .;          /* 定义开始符号 */
        *(.data*)
        . = ALIGN(4);
        _edata = .;          /* 定义结束符号 */
    } > RAM AT > ROM         /* VMA在RAM,LMA在ROM */
    
    /* BSS段 */
    .bss (NOLOAD) : {        /* NOLOAD表示不加载到ROM */
        _sbss = .;
        *(.bss*)
        *(COMMON)            /* 公共块 */
        . = ALIGN(4);
        _ebss = .;
    } > RAM
    
    /* 堆栈段 */
    .stack (NOLOAD) : {
        . = ALIGN(8);
        _sstack = .;
        . += _stack_size;    /* 预定义堆栈大小 */
        . = ALIGN(8);
        _estack = .;
    } > RAM
    
    /* 自定义段 */
    .my_section : {
        KEEP(*(.custom*))   /* 保留自定义段 */
    } > ITCM
}

2.2 关键指令规范

指令语法说明示例
ALIGN. = ALIGN(n);n字节对齐. = ALIGN(8);
KEEPKEEP(*(section))防止链接器优化KEEP(*(.init))
AT> region AT > lma_region指定加载地址> RAM AT > ROM
NOLOAD(NOLOAD)不加载到目标文件.bss (NOLOAD) : {...}
PROVIDEPROVIDE(symbol = expression)定义符号PROVIDE(_heap_start = .);
ENTRYENTRY(symbol)指定入口点ENTRY(Reset_Handler)

3. 段属性详解

3.1 输出段属性

.text : {
    /* 内容 */
} > ROM                  /* 目标内存区域 */
   AT > FLASH            /* 加载地址 */
   ALIGN_WITH_INPUT      /* 输入段对齐 */
   SUBALIGN(4)           /* 子对齐 */
   : { /* 输入段描述 */ } /* 输入段过滤 */

3.2 输入段排序规范

*(.text .text.*)         /* 所有.text和.text.*段 */
*(.rodata)               /* 所有.rodata段 */
*(SORT_BY_NAME(.data*))  /* 按名称排序 */
*(SORT_BY_ALIGNMENT(.bss*)) /* 按对齐排序 */

4. 符号定义规范

/* 全局符号定义 */
_etext = .;              /* 当前地址赋值 */
_flash_end = ORIGIN(ROM) + LENGTH(ROM); /* 计算结束地址 */
_stack_size = DEFINED(_stack_size) ? _stack_size : 0x2000; /* 带默认值 */

/* 条件定义 */
PROVIDE(__heap_start = .);
PROVIDE(__heap_end = ORIGIN(RAM) + LENGTH(RAM));

5. 完整示例模板

/* 链接脚本模板 */
MEMORY {
    FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
    RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256K
}

_estack = ORIGIN(RAM) + LENGTH(RAM); /* 栈顶 */
_stack_size = 0x4000;               /* 栈大小 */
_heap_size = 0x8000;                /* 堆大小 */

ENTRY(Reset_Handler);               /* 入口点 */

SECTIONS {
    /* 中断向量表 */
    .isr_vector : {
        . = ALIGN(4);
        KEEP(*(.isr_vector))
        . = ALIGN(4);
    } > FLASH
    
    /* 文本段 */
    .text : {
        . = ALIGN(4);
        *(.text)
        *(.text*)
        *(.glue_7)
        *(.glue_7t)
        *(.eh_frame)
        KEEP(*(.init))
        KEEP(*(.fini))
        . = ALIGN(4);
        _etext = .;
    } > FLASH
    
    /* 只读数据 */
    .rodata : {
        . = ALIGN(4);
        *(.rodata)
        *(.rodata*)
        . = ALIGN(4);
    } > FLASH
    
    /* ARM特定段 */
    .ARM.extab : {
        *(.ARM.extab* .gnu.linkonce.armextab.*)
    } > FLASH
    
    .ARM : {
        __exidx_start = .;
        *(.ARM.exidx*)
        __exidx_end = .;
    } > FLASH
    
    /* 初始化数据 */
    _sidata = LOADADDR(.data);      /* 加载地址 */
    .data : {
        . = ALIGN(4);
        _sdata = .;
        *(.data)
        *(.data*)
        . = ALIGN(4);
        _edata = .;
    } > RAM AT > FLASH
    
    /* 未初始化数据 */
    .bss : {
        . = ALIGN(4);
        _sbss = .;
        __bss_start__ = _sbss;
        *(.bss)
        *(.bss*)
        *(COMMON)
        . = ALIGN(4);
        _ebss = .;
        __bss_end__ = _ebss;
    } > RAM
    
    /* 用户堆栈 */
    ._user_heap_stack : {
        . = ALIGN(8);
        PROVIDE(end = .);
        PROVIDE(_end = .);
        . += _heap_size;
        . += _stack_size;
        . = ALIGN(8);
    } > RAM
    
    /* 调试信息 */
    /DISCARD/ : {
        libc.a(*)
        libm.a(*)
        libgcc.a(*)
        *(.note*)
        *(.comment)
    }
}

6. 段映射图示

内存布局示例:
┌──────────────────────┐ 0x08000000 (FLASH)
│      .isr_vector     │
├──────────────────────┤
│        .text         │
├──────────────────────┤
│       .rodata        │
├──────────────────────┤
│      .ARM.extab      │
├──────────────────────┤
│        .ARM          │
├──────────────────────┤
│        .data         │ (LMA)
└──────────────────────┘
       ↓ 加载时复制
┌──────────────────────┐ 0x20000000 (RAM)
│        .data         │ (VMA)
├──────────────────────┤
│        .bss          │
├──────────────────────┤
│   .user_heap_stack   │
└──────────────────────┘ 0x20040000

7. 最佳实践与注意事项

  1. 对齐要求

    • 始终使用ALIGN(4)ALIGN(8)保证对齐

    • 特别关注向量表和栈地址对齐

  2. 内存保护

    .privileged_data : {
        *(.privileged*)
    } > RAM AT > FLASH
  3. 多核系统

    .core0_text : {
        *(.core0.text*)
    } > FLASH0
    
    .core1_text : {
        *(.core1.text*)
    } > FLASH1
  4. 安全启动

    .secure : {
        KEEP(*(.secure_entry))
        *(.secure_code*)
        . = ALIGN(128K); /* 对齐到扇区边界 */
    } > FLASH
  5. 版本信息

    .fw_info : {
        KEEP(*(.fw_version))
        KEEP(*(.fw_checksum))
    } > FLASH AT > FLASH
  6. 错误处理

    • 检查内存区域重叠

    • 验证段大小是否超出内存限制

    • 使用链接器映射文件验证布局

8. 调试与验证

生成内存映射文件:

arm-none-eabi-ld -T link.ld -Map=output.map ...

关键检查点:

  1. 入口点地址是否正确

  2. 堆栈空间是否足够

  3. 关键段是否对齐

  4. 内存区域是否溢出

  5. 符号地址是否符合预期

通过遵循这些规范,可以创建出高效、可靠且易于维护的链接脚本,确保嵌入式系统程序正确加载和执行。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值