自定义ld脚本编译C代码触发ld内部错误ldlang.c 6101的原因与修复
问题分析与修复
问题背景
C代码(t79.c)
#include <stdio.h> const char code[] __attribute__((section(".mySection"))) = "\xb8\x0d\x00\x00\x00\xc3"; int main(int argc, char **argv) { int val = ((int(*)(void))code)(); printf("val %d\n", val); }
链接脚本(link.ld)
MEMORY { my_region (RWX) : ORIGIN = 0x405340, LENGTH = 4K } SECTIONS { .mySegment 0x405340 : {KEEP(*(.mySection))} > my_region }
编译命令
gcc link.ld t79.c
错误输出
/usr/bin/ld: warning: link.ld contains output sections; did you forget -T? /usr/bin/ld: internal error ../../ld/ldlang.c 6101 collect2: error: ld returned 1 exit status
环境信息
ld version: 2.34 gcc version: 9.4.0 uname -a: Linux xxx 5.15.0-25-generic #25~20.04.2-Ubuntu SMP Mon Apr 11 08:31:42 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
补充:内部错误来自lang_size_relro_segment_1函数中的断言失败:
ASSERT (desired_end >= seg->base);
错误原因
- 编译命令参数错误:未使用
-T参数指定链接脚本,gcc默认将link.ld当作普通目标文件处理,导致ld无法正确解析脚本中的输出段定义,触发"did you forget -T?"警告。 - 地址范围冲突:自定义段的起始地址
0x405340与系统默认生成的RELRO(只读重定位)段地址范围重叠,ld在计算RELRO段大小的过程中,断言desired_end >= seg->base不成立,直接触发内部错误。
修复方案
方案1:修正编译命令,添加-T参数
使用-T参数明确指定链接脚本,正确命令如下:
gcc t79.c -T link.ld
方案2:调整自定义段地址,避免与默认段冲突
通过readelf -l a.out(先编译一个无自定义脚本的可执行文件)查看默认段的地址范围,选择一个空闲区间作为自定义段的起始地址,比如0x406000,修改后的链接脚本:
MEMORY { my_region (RWX) : ORIGIN = 0x406000, LENGTH = 4K } SECTIONS { .mySegment 0x406000 : {KEEP(*(.mySection))} > my_region }
方案3:关闭RELRO特性(仅测试场景使用)
如果不需要RELRO的安全保护,可以添加-z norelro参数关闭该特性,绕过断言错误:
gcc t79.c -T link.ld -z norelro
注意:该方案会降低程序安全性,不建议在生产环境使用。
内容的提问来源于stack exchange,提问作者pmor
相关产品推荐
相关产品推荐

