You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)
      

已试过但没用的办法

  • 给变量加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:

  1. 右键version.cpp → 选择「Options for File 'version.cpp'」
  2. 切换到「Compiler」→「Optimization」→「Link Time Optimization」,选择「Disabled」
    这样version.o不会参与LTO合并,散列文件的规则能正常匹配,其他文件还能继续用LTO缩减体积。

内容的提问来源于stack exchange,提问作者Clifford

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 03:54:28