在MIPS平台用GCC编译汇编函数时,如何避免“CALL16 reloc not against global symbol”错误
MIPS交叉编译中CALL16重定位错误的原因与解决方案
首先来看你提供的原始代码:
__asm__ ( "f_asm:\n" "jr $ra\n" "nop\n" ); void f(void) __asm__("f_asm"); int main(void) { f(); return 0; }
编译时遇到的错误:
/usr/lib/gcc-cross/mips-linux-gnu/6/../../../../mips-linux-gnu/bin/ld: /tmp/ccjejLxv.o: CALL16 reloc at 0x2c not against global symbol
/tmp/ccjejLxv.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
为什么必须添加.globl f_asm?
这个错误的核心原因和MIPS架构的重定位规则直接相关:
- 当你在C代码中声明
void f(void) __asm__("f_asm");时,编译器会把对f()的调用翻译为MIPS的jal f_asm指令,这条指令依赖的是CALL16重定位(R_MIPS_CALL16)。 - MIPS的CALL16重定位是专门为全局符号设计的——这类符号可以跨目标文件访问,甚至能通过PLT(过程链接表)调用动态库中的符号。
- 但你在
__asm__块里定义的f_asm标签默认是局部符号(仅在当前目标文件内可见,未对外暴露),链接器发现CALL16重定位指向了非全局符号,就会抛出错误,因为它无法为局部符号处理这种跨模块风格的重定位。
添加.globl f_asm后,你把f_asm标记为全局符号,完全符合CALL16重定位的要求,链接器就能正常处理这个跳转了。
能不能用非全局符号实现?
当然可以!只要避免使用CALL16重定位,改用局部符号支持的PC相对重定位即可,最简洁的方案是:
将函数声明为static
把C函数声明为static,这样f_asm会被编译器标记为局部符号,此时调用f()时会生成使用PC相对重定位(R_MIPS_PC16)的跳转指令,不需要依赖全局符号:
static void f(void) __asm__("f_asm"); __asm__ ( "f_asm:\n" "jr $ra\n" "nop\n" ); int main(void) { f(); return 0; }
这种方式下,f_asm是局部符号,跳转仅依赖当前目标文件内的相对地址计算,链接器不会再要求它必须是全局的。
总结一下:CALL16重定位强制要求目标是全局符号,而局部符号需要用PC相对重定位来处理,通过static声明就能让编译器自动切换重定位类型,完美规避这个错误。
内容的提问来源于stack exchange,提问作者EnTaroAdun
相关产品推荐
相关产品推荐

