ArmClang/ArmLink开启LTO时移除__attribute__((used))对象的解决方法问询
问题:LTO开启后Bootloader版本字符串无法按指定ROM位置放置
在Bootloader中,有一个未被显式使用但必须固定在ROM特定位置的版本字符串,供后续加载的应用程序读取。当前的实现细节如下:
版本字符串定义(version.cpp)
// Place version string at end of bootrom less 16 bytes #define VERSION_STRING "090600.01.00.00" #define MAX_VERSION_STRING_LENGTH 0x10 const char bootmainVersionString[MAX_VERSION_STRING_LENGTH] __attribute__((used)) = VERSION_STRING ;
散列文件(Scatter File)配置
LR_VERSION_IROM1 0x08001FF0 0x00000010 { VERSION_IROM1 0x08001FF0 0x00000010 { version.o (+RO) } }
问题现象
- 这套配置是因为armcc v5的旧方法在基于LLVM/Clang的ArmClang v6中不再兼容才改用的
- 开启LTO(链接时优化)后,即便加了
__attribute__((used)),链接器还是会把version.o的相关段移除,导致散列文件里的VERSION_IROM1区块为空,弹出警告:.\bootrom.sct(21): warning: L6314W: No section matches pattern version.o(RO). - 关闭LTO时,版本字符串能正常放到指定位置,但镜像会多出约560字节,而LTO是把Bootrom控制在8KB以内的关键
- 开启LTO后,版本字符串其实没被真正删掉,但被链接器分配到了其他位置:
- 无LTO(位置正确):
bootmainVersionString 0x08001ff0 Data 16 version.o(.rodata.bootmainVersionString) - 开启LTO(位置错误):
bootmainVersionString 0x0800130c Data 16 lto-llvm-dbc16f.o(.rodata)
- 无LTO(位置正确):
已试过但没用的办法
- 给变量加
volatile限定符 - 在代码里加伪引用
- 用链接器选项
--keep=bootmainVersionString
工具链信息
Toolchain: MDK-ARM Plus Version: 5.36.0.0 C Compiler: ArmClang.exe V6.16 Linker/Locator: ArmLink.exe V6.16
可行解决方案
方法1:给版本字符串指定专属段,散列文件直接定位该段
修改version.cpp,把版本字符串放到自定义段里:
// Place version string at end of bootrom less 16 bytes #define VERSION_STRING "090600.01.00.00" #define MAX_VERSION_STRING_LENGTH 0x10 const char bootmainVersionString[MAX_VERSION_STRING_LENGTH] __attribute__((used, section(".version_section"))) = VERSION_STRING ;
然后修改散列文件,直接引用这个自定义段:
LR_VERSION_IROM1 0x08001FF0 0x00000010 { VERSION_IROM1 0x08001FF0 0x00000010 { *(.version_section) } }
LTO会把符号合并到临时生成的LTO目标文件,但自定义段会被保留,链接器能通过段名找到并放到指定位置。
方法2:用--keep选项指定段而非符号
因为LTO后符号归属的文件变成了LTO临时文件,直接指定符号可能失效,改成指定自定义段:
--keep=.version_section
配合方法1的自定义段使用,确保段不被移除且位置正确。
方法3:添加强制不被优化的外部引用
找一个肯定会被编译且保留的文件(比如bootmain.cpp),添加对版本字符串的外部声明,并且做一个不会被优化掉的引用:
extern const char bootmainVersionString[]; // 放在某个一定会被执行的初始化函数里 void bootmain_init() { // 强制引用,避免LTO优化移除 (void)&bootmainVersionString; }
确保这个初始化函数会被调用(或者用__attribute__((constructor))标记让它自动执行),这样LTO会认为该符号被实际使用,不会从原version.o中移除,散列文件的version.o(+RO)规则就能正常生效。
方法4:单独禁用version.cpp的LTO优化
在MDK-ARM里给version.cpp单独设置编译选项,关闭LTO:
- 右键version.cpp → 选择「Options for File 'version.cpp'」
- 切换到「Compiler」→「Optimization」→「Link Time Optimization」,选择「Disabled」
这样version.o不会参与LTO合并,散列文件的规则能正常匹配,其他文件还能继续用LTO缩减体积。
内容的提问来源于stack exchange,提问作者Clifford
相关产品推荐
相关产品推荐

