如何将编译器生成的32位与64位代码链接为单个二进制文件?
Michael Petch 曾提到:“64位ELF格式支持32位代码段。”
你遇到的链接器错误,本质上是没给链接器足够的信息来处理两种不同字长的代码段,以及跨模式调用的符号兼容性问题——毕竟编译器默认不会生成能直接和异字长代码链接的目标文件,这也是你之前只能用手写汇编绕开问题的原因。下面给你几个具体的解决方向:
明确编译目标字长,生成正确的ELF目标文件
用GCC这类编译器时,必须给32位代码加上-m32参数,64位代码用-m64,这样编译器会生成对应字长的目标文件,并且在ELF段中标记正确的属性(比如32位代码段的e_ident[EI_CLASS]会设为ELFCLASS32)。如果编译时参数混乱,目标文件的字长不匹配,链接器直接就会抛出错误。自定义链接脚本,告诉链接器如何处理32位代码段
默认的链接器脚本不会处理混合32/64位的场景,你需要手动定义32位代码的专属段,并标记其32位属性。举个简单的链接脚本示例:SECTIONS { . = 0x100000; /* 64位代码起始地址 */ .text : { *(.text) } :text .text32 : { *(.text32) } :text32 /* 32位代码段 */ .data : { *(.data) } :data .bss : { *(.bss) } :bss } PHDRS { text PT_LOAD FLAGS(5); /* 64位可执行段(R+E) */ text32 PT_LOAD FLAGS(5) PHDRS_FLAGS(0x40000000); /* 标记为32位可执行段 */ data PT_LOAD FLAGS(6); /* 数据段(R+W) */ }编译32位代码时,还要用
-ffunction-sections把函数放到.text32段,比如:gcc -m32 -c my32code.c -ffunction-sections -Wl,--section-start=.text32=0x200000这样链接器就能正确识别并放置32位代码段了。
处理跨模式调用的符号转换
32位和64位代码之间的调用不能直接进行,必须做模式切换和调用约定适配。比如从64位代码跳转到32位代码时,需要先保存64位寄存器状态,用ljmp指令切换到32位代码段,调用完成后再切回64位模式并恢复寄存器;反过来也是同理。这部分逻辑可能需要你写少量汇编胶水代码,因为编译器不会自动生成跨模式的调用代码。验证目标文件属性,排除基础错误
用readelf -h命令检查每个目标文件的头部信息,确认32位文件的Class字段是ELF32,64位文件是ELF64。如果有文件的属性不符合预期,那肯定是编译参数出了问题,先修正这一点再尝试链接。
按照这些步骤调整后,你应该能顺利链接编译器生成的32位和64位代码,不用再全靠手写汇编来维护,降低项目复杂度带来的维护压力。
内容的提问来源于stack exchange,提问作者prl

