STM32F427 基于 Cortex-M4F、GD32/STM32 通用,编译器(Keil MDK、GCC/STM32CubeIDE、IAR)都支持通过「编译器段属性 + 链接脚本」两步把函数放到固定地址。核心思路是:__attribute__((section(...))) 只负责把函数放进自定义段,真正决定地址的是链接脚本里对该段的定位。
方法一:Keil MDK(最常用于 STM32F4)
Keil 用分散加载文件 .sct 控制内存布局。
第 1 步:在 .sct 文件中新增 Flash 段
LR_IROM1 0x08000000 0x00100000 { ; 加载域,地址按你的工程实际Flash起点/大小调整
; ...其他默认段...
ER_MYFUNC 0x08060000 0x2000 { ; 执行域:函数固定到 0x08060000
*(.my_func) ; 收集所有标记为 .my_func 的代码
}
; ...其余段...
}
【也可用 FIXED 属性】
ER_MYFUNC 0x08060000 FIXED { *(.my_func) }
第 2 步:在 C 代码中用段属性标记函数
#define MY_FLASH __attribute__((section("my_func")))
MY_FLASH void test_function(void) {
return 1;
}
编译后在 map 文件中即可确认 test_function 是否定位到 0x08060000。
方法二:GCC / STM32CubeIDE
GCC 同样用 __attribute__((section)) 标记,然后在 .ld 链接脚本中指定段地址。
C 代码标记:
void my_function(void) __attribute__((section(".my_func")));
void my_function(void) { /* 函数体 */ }
链接脚本(.ld)中定位:
SECTIONS {
...
.my_section 0x10000000 : {
*(.my_section) /* 所有标记 .my_section 的部分 */
}
}
也可以使用 ARM 编译器提供的「__at 段」,直接以地址命名:
void Communication_Test(u8 *in, u8 *out, u8 len)
__attribute__((section(".ARM.__at_0x00031000")));
其中 .ARM.__at_<address> 的地址即该函数被放置的位置。
方法三:IAR
IAR 用 #pragma location 指定地址,例如:
#pragma location = 0x00031000
void my_func(void);
关键注意事项
-
段属性≠地址 :
__attribute__((section))只决定函数进入哪个段,最终地址必须由链接脚本分配,二者缺一不可。 -
防止被优化丢弃 :若函数未被调用,链接器可能剔除它。GCC 下用
__attribute__((used))或链接脚本中的KEEP(...)保留,例如KEEP((*(.boot.init)));Keil 下也建议用__attribute__((used))。 -
地址对齐 :Flash 段建议使用
ALIGN(4),因为 Cortex-M 指令(尤其 Thumb-2)要求按半字/字对齐,函数地址通常需 4 字节对齐。.my_func 0x08060000 : ALIGN(4) { *(.my_func) }
-
多函数顺序:同一段内多个函数按声明顺序存放(GCC/IDE 下),可据此安排相对位置。
-
验证 :编译后务必打开 map 文件或使用反汇编、
nm工具,确认目标函数确实位于期望地址,这是最可靠的检查方式。 -
未使用函数保留 :若函数不在调用链中且开
-ffunction-sections,链接时可能被 section GC 移除,需配合KEEP/used处理。