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可避免链接器因未引用而移除对象。
- 若目标是 32 位 MCU,示例中的
二、在 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)。
- 0x400 不与
- 运行期写
code_size:- 若所在区域是 ROM,需在生成镜像阶段或通过合适的启动流程写入。
四、快速对照(何时选 A/B/C)
- 方式 A(链接脚本 + 段名):可读性最好、最易移植,推荐用于长期维护的工程。
- 方式 B(相对首地址的绝对定位):当需要“相对向量表偏移”且不便改 ICF 时可选;注意地址表达式与符号类型。
- 方式 C(固定绝对地址):简单直接,但基地址变化不随动,移植性最差,仅适合固定版位的产品化场景。