原来,从glibc 2.1开始,引入了一种叫做 Symbol Versioning 的机制。
具体来说每个符号会对应一个版本号,在一个libc.so.6文件中可以包含一个函数的多个版本。
> nm /lib64/libc.so.6 | grep " memcpy"
000000000008e860 i memcpy@@GLIBC_2.14
0000000000089650 i memcpy@GLIBC_2.2.5
例如在上面的这个数据中,memcpy@@GLIBC_2.14代表默认版本(两个@),memcpy@GLIBC_2.2.5则代表最低版本。
所以在编译机上,链接的可执行程序会指向memcpy@@GLIBC_2.14,当它在另一台低版本的低于 2.14 版本的 glibc 机器上运行时就会找不到改符号。
在低版本的glibc机器上链接出来的可执行文件指向memcpy@GLIBC_2.2.5,依然可以在高版本 glibc 上运行,因为高版本 glibc 库中包含了该符号。
四、解决方案
我们面对一个问题,肯定需要去想办法解决了,总不能又回到c++98编译机去。
google 之后,看了大量的资料,发现有三种解决方案。
搜索结果中,99% 解决方案都是升级运行环境的 glibc 。
在个人的 VPS 或者 小公司只有几台机器的情况下,还可以接受,折腾一下就行了。
但是在几十上百台服务器上,升级 glic 是不可能的事情。
所以这个方法对我来说不可行。
然后剩下的 1% 解决方案中,又有 9 成的方法介绍在用到 memcpy 的 cpp 文件里加上__asm__(“.symver memcpy,memcpy@GLIBC_2.2.5”);
然而,我的机器的 glic 版本没有版本控制,只有一个 memcpy。
当初我以为是__asm__(“.symver memcpy,memcpy”);,结果没有解决问题。
经过查资料,最终在这里(https://gcc.gnu.org/wiki/SymbolVersioning)找到方法。
原来我只需要在对应的头文件里加上__asm__(“.symver memcpy,memcpy@”);即可,那个@必须加上。
这个方法的可行性测试通过了。
然而,我的项目文件不是一个,有几十个。各种库里都使用了memcpy, 这时候上面的方法也不行了。
又经过大量查阅资料,在 stackoverflow 上(http://stackoverflow.com/a/20065096/1881299)了解到链接器有一个wrap 参数,可以在链接的时候动态的替换符号。
简单来说,我们需要写一个通用的符号替换函数,然后在链接目标文件时,使用命令-Wl,–wrap=memcpy来主动指定 memcpy 的符号号版本,这样就可以对所有的文件进行符号替换了。
这样实现后,编译出的程序终于可以正常的运行了。
五、最后
linux 下的符号问题有很多,每次都会遇到不一样的。
对于 glibc 版本问题本以为只有升级这一个方案,没想到竟然有这么完美的不升级的方案,但是网上又是少之又少,甚是可惜。
这个问题查询资料时,发现一本不错的链接相关的电子书,分享给大家,公众号后台发送“The GNU linker”免费领取。
注:需要发送引号里面完整的名称,建议长按复制,然后去后台发送。
注2:这篇文章的参考文章,可以点击原文阅读,在原文里面可以得到相关链接。
-EOF-
———END———
限 时 特 惠: 本站每日持续更新海量各大内部创业教程,永久会员只需98元,全站资源免费下载 点击查看详情
站 长 微 信: yjxmw518