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

启用-mcmodel=large时,如何强制编译器仅生成64位偏移?

大代码模型编译仍出现PC32重定位错误的解决方法

问题背景

在实验大型二进制文件时发现:即便使用-mcmodel=large编译,链接阶段仍会因二进制文件中存在小代码风格的PC32重定位指令报错。根本原因是编译器自带的启动目标文件(如Scrt1.o、crtbeginS.o)以及系统动态库均采用小代码模型构建,-mcmodel=large仅能控制自定义代码的生成逻辑,无法修改这些预编译组件内的重定位指令。

复现示例

测试代码

// too_large.cc
int main() {
  static int y = 0;
  asm volatile(".align 1073741824"); // 人为增大代码体积,触发重定位溢出
  y += 5;
  return 0;
}

编译与链接命令

编译阶段无报错:

g++ -mcmodel=large -c too_large.cc -o too_large.o

链接阶段抛出重定位溢出错误:

g++ too_large.o -o too_large

错误信息

/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/Scrt1.o: in function `_start':
(.text+0x1d): relocation truncated to fit: R_X86_64_GOTPCRELX against symbol `__libc_start_main@@GLIBC_2.34' defined in .text section in /lib/x86_64-linux-gnu/libc.so.6
/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/Scrt1.o:(.eh_frame+0x20): relocation truncated to fit: R_X86_64_PC32 against `.text'
/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crti.o: in function `_init':
(.init+0x7): relocation truncated to fit: R_X86_64_REX_GOTPCRELX against undefined symbol `__gmon_start__'
/usr/lib/gcc/x86_64-linux-gnu/12/crtbeginS.o: in function `deregister_tm_clones':
crtstuff.c:(.text+0x3): relocation truncated to fit: R_X86_64_PC32 against `.tm_clone_table'
crtstuff.c:(.text+0xa): relocation truncated to fit: R_X86_64_PC32 against symbol `__TMC_END__' defined in .data section in too_large
crtstuff.c:(.text+0x16): relocation truncated to fit: R_X86_64_REX_GOTPCRELX against undefined symbol `_ITM_deregisterTMCloneTable'
/usr/lib/gcc/x86_64-linux-gnu/12/crtbeginS.o: in function `register_tm_clones':
crtstuff.c:(.text+0x33): relocation truncated to fit: R_X86_64_PC32 against `.tm_clone_table'
crtstuff.c:(.text+0x3a): relocation truncated to fit: R_X86_64_PC32 against symbol `__TMC_END__' defined in .data section in too_large
crtstuff.c:(.text+0x57): relocation truncated to fit: R_X86_64_REX_GOTPCRELX against undefined symbol `_ITM_registerTMCloneTable'
/usr/lib/gcc/x86_64-linux-gnu/12/crtbeginS.o: in function `__do_global_dtors_aux':
crtstuff.c:(.text+0x76): relocation truncated to fit: R_X86_64_PC32 against `.bss'
crtstuff.c:(.text+0x81): additional relocation overflows omitted from the output
too_large: PC-relative offset overflow in GOT PLT entry for `__cxa_finalize@@GLIBC_2.2.5'

解决方法

要全程强制生成64位偏移,可从以下方向入手:

1. 自定义编译启动文件(crt)

系统自带的crt文件基于小代码模型编译,需下载GCC源码,用-mcmodel=large重新编译生成整套crt目标文件(Scrt1.o、crti.o、crtbeginS.o等),链接时指定使用这些自定义文件,替代系统默认版本。

2. 切换为静态链接

动态库(如libc.so)采用小代码模型构建,静态链接时可选择大代码模型编译的静态库(若存在),或自行编译大模型版本的系统库。链接时添加-static参数,并指定自定义大模型静态库路径。

3. 禁用不必要的crt组件

若程序不需要全局构造/析构、性能分析等crt功能,可通过编译选项跳过相关组件:

  • -fno-use-cxa-atexit:禁用__cxa_finalize相关逻辑
  • -fno-exceptions:禁用异常处理,减少crt依赖
  • -fno-unwind-tables:避免生成eh_frame相关的PC32重定位
  • -nostartfiles:不使用默认启动文件,手动实现_start函数

手动实现_start的示例:

// minimal_start.s
.global _start
_start:
    mov $main, %rdi
    call *%rdi
    mov %rax, %rdi
    mov $60, %rax
    syscall

编译链接命令:

g++ -mcmodel=large -c too_large.cc -o too_large.o
as minimal_start.s -o minimal_start.o
g++ -nostartfiles too_large.o minimal_start.o -o too_large

4. 链接器选项临时适配

部分链接器支持通过选项转换PC32重定位为64位版本,如GNU ld的--emit-relocs配合--relax,但该方法稳定性不足,仅作为临时 workaround。

总结

核心矛盾是系统预编译组件采用小代码模型,无法通过单一编译选项直接覆盖。最彻底的方案是自行编译大代码模型版本的crt和系统库;若仅需快速验证,禁用不必要的crt组件或手动实现启动函数是更轻量的选择。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 12:07:51