蒙蒙plus
蒙蒙plus
Published on 2025-10-29 / 11 Visits
0
0

IAR 链接与段控制,参数表定位

IAR 链接与段控制,参数表定位(学习笔记)

  • 总体目标
    • 在程序首地址偏移 0x400 处放置设备参数表,并能在代码中获取其地址与大小。

一、在首地址偏移 0x400 放置参数表

  • 结构体建议(紧凑布局):
#pragma pack(push, 1)
typedef struct {
    char device_name[32];
    char software_version[16];
    char hardware_version[16];
    char protocol_info[32];
    uint32_t code_size;           // 由启动阶段或生成镜像阶段写入
} parameter_table_t;
#pragma pack(pop)
  • 方式 A(推荐:链接脚本 + 段名)
    • link.icf:
place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec };
place at address mem:__ICFEDIT_intvec_start__ + 0x400 { readonly section .parameter_table };
  • C 代码:
#pragma location = ".parameter_table"
__root const parameter_table_t parameter_table = { /* 初始化字段 */ };
  • 方式 B(IAR 相对首地址的绝对定位语法)
extern const uint32_t __ICFEDIT_intvec_start__;
__root const parameter_table_t parameter_table @(0x400 + (uint32_t)&__ICFEDIT_intvec_start__) = { /* 初始化字段 */ };
  • 方式 C(固定绝对地址)
__root const parameter_table_t parameter_table @0x400 = { /* 初始化字段 */ };
  • 缺点:烧录基地址变化时不随动,移植性差。

  • 代码大小字段初始化(启动阶段写入)

#include <stddef.h>
#include <stdint.h>
extern uint32_t __text_start__;
extern uint32_t __text_end__;

void patch_code_size_runtime(uint32_t param_addr) {
    uint32_t code_size = (uint32_t)&__text_end__ - (uint32_t)&__text_start__;
    volatile uint32_t *code_size_ptr =
        (uint32_t *)(param_addr + offsetof(parameter_table_t, code_size));
    *code_size_ptr = code_size;
}
  • 直接通过节名获取参数表起止与大小(若采用方式 A 的自定义段名):
#include <intrinsics.h>
#pragma section = ".parameter_table"

const void *param_begin = __section_begin(".parameter_table");
const void *param_end   = __section_end(".parameter_table");
size_t param_size       = (size_t)__section_size(".parameter_table");
  • 小贴士:
    • 若目标是 32 位 MCU,示例中的 uint32_t 转换通常可用;如需可移植性,建议使用 uintptr_t 进行地址运算。
    • __root 可避免链接器因未引用而移除对象。

二、在 IAR 中获取自定义段地址(.device_info 示例)

  • 依赖与声明:
#include <intrinsics.h>
#pragma section = ".device_info"
  • 获取接口:
void *begin = __section_begin(".device_info");
void *end   = __section_end(".device_info");
size_t size = (size_t)__section_size(".device_info");
  • 常见警告修复:
    • Warning[Pe223]: function "_section_begin" declared implicitly
      • 原因:函数名拼写错误或缺少头文件。
      • 解决:使用双下划线 __section_begin/__section_end/__section_size 并包含 <intrinsics.h>
    • 若段名书写不一致或段未被生成,__section_size 可能为 0,请先检查链接脚本与对象的 #pragma location 是否一致。

三、验证与排错清单

  • Map 文件检查:
    • .device_info 段起止地址、长度。
    • ui_* 的装载地址(EXTROM)与执行地址(SDRAM)。
  • 冲突检查:
    • 0x400 不与 .intvec 或 CRP 区域冲突(通常 .intvec 在 0x0)。
  • 运行期写 code_size
    • 若所在区域是 ROM,需在生成镜像阶段或通过合适的启动流程写入。

四、快速对照(何时选 A/B/C)

  • 方式 A(链接脚本 + 段名):可读性最好、最易移植,推荐用于长期维护的工程。
  • 方式 B(相对首地址的绝对定位):当需要“相对向量表偏移”且不便改 ICF 时可选;注意地址表达式与符号类型。
  • 方式 C(固定绝对地址):简单直接,但基地址变化不随动,移植性最差,仅适合固定版位的产品化场景。

Comment