链接器如何判定自定义段属性 自定义段读写权限配置方法
自定义全局变量段的链接属性规则与配置方案
链接器判定自定义段属性的默认逻辑
GNU ld、gold链接器默认通过合并所有匹配到该输出段的输入段属性,生成最终输出段的属性标记:
- 只要有一个输入段携带可写(W)属性,输出段就会带W标记
- 只要有一个输入段携带可分配(A)属性,输出段就会带A标记
- 可执行(X)属性的合并逻辑同上
你遇到的段错误根因是Clang在-O1及以上优化等级下会启动全局常量推导Pass:即使变量没有被const显式修饰,只要编译器判定该变量全程无写入操作,就会把变量标记为常量,对应输入段的属性会被修改为只读。由于你把可写、只读变量都放到了同一个自定义输入段,Clang的后续优化Pass错误地将整个输入段的属性覆写为只读,没有保留段内可写变量的W属性,最终链接器拿到的输入段全为只读属性,输出段自然只带A标记,运行时写入该段内存就会触发段错误。
GCC无此问题的原因是GCC不会对自定义命名段做跨变量的段属性覆写,Clang -O0等级因为关闭了全局常量推导,也不会触发该问题。
链接脚本强制指定段属性的方法
你不需要重写完整链接脚本、不需要定义MEMORY内存布局,直接在输出段定义后显式指定属性即可强制覆盖默认的属性合并逻辑,该语法和你当前使用的绝对地址绑定、INSERT扩展完全兼容:
SECTIONS { . = 0x0000001b000002e0; /* 段名后括号内直接填写需要强制设置的属性 */ my_nonconst_section (aw) : {KEEP(*(my_nonconst_section))} /* 只读段配置示例 */ my_const_section (a) : {KEEP(*(my_const_section))} } INSERT AFTER .gnu.attributes;
常用属性标记对应关系:
a:开启可分配(Alloc)属性w:开启可写(Writable)属性x:开启可执行(Executable)属性
显式指定属性后,链接器会完全按照你给出的标记设置输出段权限,不再受输入段属性的影响,从根本上避免编译器优化修改段属性导致的权限错误。
编译Pass侧的补充优化方案
如果你在开发LLVM编译Pass,可以配合做两处处理彻底规避问题:
- 对分配到可写自定义段的全局变量,显式将其
isConstant()标记设为false,同时添加llvm.compiler.used标记,阻止后续优化Pass修改该变量的段属性 - 在段分配逻辑中增加校验,如果同一个自定义段内同时存在推导常量、可写变量,提前将常量拆分到只读自定义段,避免两类变量混放引发属性冲突。
效果验证
修改后重新编译,通过readelf -S查看目标文件段表,对应自定义段的Flg字段会显示为WA;通过nm查看符号,段内变量会标记为d(可写数据段符号)而非r(只读数据段符号),程序运行时写入该段不会触发段错误。
内容的提问来源于stack exchange,提问作者JaneDoe
相关产品推荐
相关产品推荐

