本文大致提点这个问题,有哪些可行的解决方案。
这是常见 C/C++ 的一类连接器错误,我们需要知道它一般是怎么产生的,才能知道如何正确的解决它。
例如:(当发生这类问题时,连接器通常会输出这样的信息)
[build] /usr/mips64el-linux-gnuabi64/include/c++/7/mips64el-linux-gnuabi64/bits/gthr-default.h:251:(.text+0x1c): relocation truncated to fit: R_MIPS_GOT_DISP against `__pthread_key_create@@GLIBC_2.0'
[build] CMakeFiles/xxx.dir/xxx/net/asio/websocket.cpp.o: In function `__gthread_mutex_lock(pthread_mutex_t*)':
[build] /usr/mips64el-linux-gnuabi64/include/c++/7/mips64el-linux-gnuabi64/bits/gthr-default.h:748:(.text+0x90): relocation truncated to fit: R_MIPS_CALL16 against `pthread_mutex_lock@@GLIBC_2.0'
[build] CMakeFiles/xxx.dir/xxx/net/asio/websocket.cpp.o: In function `__gthread_mutex_unlock(pthread_mutex_t*)': [build] /usr/mips64el-linux-gnuabi64/include/c++/7/mips64el-linux-gnuabi64/bits/gthr-default.h:778:(.text+0x114): relocation truncated to fit: R_MIPS_CALL16 against `pthread_mutex_unlock@@GLIBC_2.0'
通常出现这类连接器问题,多数是C++编译器编译CC/CPP源文件出来的 .obj(*.o)文件太大了,导致超出了一些平台CPU可以短链接重定向的范围。
注意:这个问题可能会在 MIPS、MIPS64 CPU架构上面较容易出现。
以下是可以采纳的解决方案(逐个试)
1、拆分引起连接器暴这个错误的 cpp 文件(XXX.o 对应那个CPP文件)把这个源文件自己分析,按着拆分到多个 cpp 文件之中在编译
2、按需配置C语言、C++ 语言的编译器选项,增加编译器选项 -mlong-calls
-mlong-calls 编译器选项是指:要求编译器适用VA绝对地址(长地址)来调用跳转,这样呢,可以显著解决因为 XXX.o 编译出来的二进制太大,导致连接器无法连接上的问题。
但是缺点肯定是有的,那就是效率肯定没有RVA相对地址(短地址)跳转速度块,因为绝对地址都是需要先放在寄存器之中的,在按照寄存器的值跳转,不能直接JMP到绝对地址上面的,相当于每次都要多走一些流程,并且在CPU之中相对寻址本来就比随机的绝对寻址要快的。
3、配置连接器选项,增加选项
1、尝试使用 -Wl,--no-relax 选项来禁用链接器的放松(relocation relaxation)功能,这可能有助于解决一些符号重定位(truncated relocation)的问题。
2、使用 -Wl,--no-merge-exidx-entries 选项来禁止合并 .ARM.exidx 表条目,这可能有助于解决某些链接问题。
这条会有限制,C++ 17 直接没法用了,而且 C++ 11 的一些模板展开特性也会受到限制,基本不是很推荐通过这条指令来解决,但如果真的必须要,无可避免、退无可退的情况下,您或许可以考虑设置它到连接器上面试试看,的确是有效的。
4、调整编译器选项,增加选项
1、考虑添加 -fPIC 选项以生成位置无关代码(Position Independent Code),这有助于减少一些链接时的问题。
2、也可以尝试添加 -ffunction-sections -fdata-sections 选项来将函数和数据放置到单独的段(section)中,这有助于优化链接器的处理。
在这些解决方案之中,相对更亲民靠谱的解决方案还是编译器增加选项: -mlong-calls,另外就是手动拆分C/CPP源文件到多个之中了,毕竟平台及编译器限制也是没有办法不是。