GLIBC_2.14 兼容问题 version ‘GLIBC_2.14’ not found

2019年1月16日 0 条评论 1.81k 次阅读 0 人点赞

这个问题出现在我在台式机上编译binary之后,直接到目标服务器上运行。

./mybin: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by ./mybin)
./mybin: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (required by ./mybin)

本地的台式机gcc版本为4.6.3, libc版本为2.15, libstdc++版本为libstdc++.so.6.0.16。服务器的gcc版本为4.1.2,libc版本为2.5,libstdc++版本为libstdc++.so.6.0.8。

1 查找版本不兼容的函数

~: objdump -T mybin | fgrep GLIBC_2.14
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.14 memcpy

可以看到mybin需要调用GLIBC_2.14中的memcpy实现,在目标机上运行:

#: objdump -T /lib64/libc.so.6 | fgrep memcpy
00000032be87bdb0 g DF .text 0000000000000465 GLIBC_2.2.5 memcpy

也就是说目标机上的libc只提供了memcpy@GLIBC_2.2.5的实现,所以导致mybin无法运行。

再在本机上运行一次上面的命令(使用ldd查看实际调用的libc路径):

~: objdump -T /lib/x86_64-linux-gnu/libc.so.6 | fgrep memcpy
00000000000917f0 g iD .text 000000000000003c GLIBC_2.14 memcpy
000000000008bbd0 g iD .text 0000000000000047 (GLIBC_2.2.5) memcpy

这说明了在本机上是可以同时使用memcpy@GLIBC_2.2.5和memcpy@GLIBC_2.14的,那么为了保证最大兼容性,有没有什么方法可以强制链接memcpy@GLIBC_2.2.5呢?当然是有的,不然我还写这篇做什么。

2 .symver 绑定符号版本
这篇文章中给出了一个realpath的例子:Linking to Older Versioned Symbols (glibc)

#include <limits.h>
#include <stdlib.h>
#include <stdio.h>

__asm__(".symver realpath,realpath@GLIBC_2.2.5");
int main()
{
   char* unresolved = "/lib64";
   char  resolved[PATH_MAX+1];
   if(!realpath(unresolved, resolved))
      { return 1; }
   printf("%s\n", resolved);
   return 0;
}

这种方案可以简单的解决代码中的显式函数调用。
但在mybin源代码中并没有直接使用memcpy函数,而是mybin链接的其它库中调用了memcpy,显然这种方案是行不通了。

3 –wrap 选项
首先写一个文件 memcpy.c, 定义函数__wrap_memcpy,然后用.symver显示指定__memcpy_glibc_2_2_5链接到memcpy@GLIBC_2.2.5上。

#include <string.h>

void *__memcpy_glibc_2_2_5(void *, const void *, size_t);

asm(".symver __memcpy_glibc_2_2_5, memcpy@GLIBC_2.2.5");
void *__wrap_memcpy(void *dest, const void *src, size_t n)
{
    return __memcpy_glibc_2_2_5(dest, src, n); 
}

然后在链接时使用选项 -Wl,–wrap=memcpy
如果使用cmake编译链接,需要在CMakelists.txt里增加如下选项:

SET_TARGET_PROPERTIES( mybin PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Wl,--wrap=memcpy" )

重新生成mybin之后,再次使用objdump查看

0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 memcpy
000000000047355c g DF .text 000000000000002d Base __wrap_memcpy

可以看到调用的是memcpy@GLIBC_2.2.5

4 GLIBCXX 版本兼容问题
有两个方案可以解决,一是链接libstdc++的静态库,二是把本机的libstdc++.so一起打包发布,通过设置LD_LIBRARY_PATH使得mybin可以调用打包的libstdc++.so。

另外补充一个问题,之前看到有人说libstdc++是GPL协议的,所以如果想要打包发布,必须也要提供mybin的源代码。但是又搜了搜,发现libstdc++并不是plain GPL的,在我们这种简单链接应用的场景下是没有问题的。见这个回答和相关链接

5 补充:
在4里面语焉不详了一件事就是链接libstdc++的静态库。如果直接打包发布libstdc++.so的话,有可能出现的一个问题是在libstdc++.so中调用了GLIBC_2.14的函数,还是会导致最开始的问题,而且没办法通过wrap的方式解决。如果选择同时打包libc.so的话,libc.so的不同平台上兼容性很差(今天就遇到了一个)。见这个帖子:从gcc静态链接开始的讨论,学了好几手

在CMakelists里就简单多了,显式指定 libstdc++.a 就可以了。这样就达到了静态链接libstdc++,但是动态链接libc的目的。

6 References
http://blog.kangkang.org/index.php/archives/17
http://micro.nicholaswilson.me.uk/post/31855915892/…
http://www.trevorpounds.com/blog/?p=103
http://stackoverflow.com/questions/4032373/…
https://gist.github.com/nicky-zs/7541169
http://www.cmake.org/pipermail/cmake/2003-August/004244.html
http://stackoverflow.com/a/3214398
http://lwn.net/Articles/549573/

管理员

这个人太懒什么东西都没留下