链接到.so文件中较旧的符号版本

在x86_64 linux上使用gcc和ld我需要链接到更新版本的库(glibc 2.14),但可执行文件需要在具有较旧版本(2.5)的系统上运行。 由于唯一不兼容的符号是memcpy(需要memcpy@GLIBC_2.2.5,但提供memcpy@GLIBC_2.14的库),我想告诉链接器,不要使用memcpy的默认版本,而应该使用我指定的旧版本。

我发现一个非常尴尬的方法:只需在链接器命令行中指定一个旧的.so文件的副本。 这工作正常,但我不喜欢有多个.so文件(我只能通过指定所有旧链接库,我链接到,也有参考memcpy)的想法检查到svn和我的生成系统。

所以我正在寻找一种方法来告诉链接器采用旧版本的符号。

对我不起作用的select是:

  • 使用asm.symver(在Trevor Pounds的Blog的Web Archive中可以看到),因为这将要求我确保symver是所有使用memcpy的代码,这将是非常困难的(复杂的代码库和第三方代码)
  • 与旧图书馆保持搭build环境; 只是因为我想在我的桌面系统上开发,这将是一个皮塔同步在我们的networking中的东西。

当考虑链接器所做的所有工作时,似乎并不是一件困难的事情,毕竟它有一些代码也可以找出默认的符号版本。

任何与简单链接器命令行(如创build一个简单的链接器脚本等)复杂度相同的想法也是受欢迎的,只要它们不是像编辑所产生的二进制文件那样的奇怪黑客…

编辑:为了节约这给未来的读者,除了下面的想法,我发现了选项 – --wrap到链接器,这可能有时也是有用的。

只要静态链接memcpy – 从libc.a中提取memcpy.o ar x /path/to/libc.a memcpy.o (无论版本 – memcpy几乎都是独立函数)并将其包含在最终链接中。 请注意,如果您的项目分发给公众而不是开放源代码,那么静态链接可能会使授权问题复杂化。

另外,你可以简单地自己实现memcpy,虽然在glibc中手动调整的程序集版本可能更有效率

请注意, memcpy@GLIBC_2.2.5被映射到memmove(旧版本的memcpy始终以可预测的方向复制,导致它有时在应用memmove时被误用),这是版本碰撞的唯一原因 – 您可以简单地用代码中的memcpyreplace这个特定的情况。

或者你可以去静态链接,或者你可以确保你的networking上的所有系统的版本与你的版本相同或更好。

我发现了以下工作解决scheme。 首先创build文件memcpy.c:

 #include <string.h> /* some systems do not have newest memcpy@@GLIBC_2.14 - stay with old good one */ asm (".symver memcpy, memcpy@GLIBC_2.2.5"); void *__wrap_memcpy(void *dest, const void *src, size_t n) { return memcpy(dest, src, n); } 

编译这个文件不需要额外的CFLAGS。 然后把你的程序与-Wl,wrap = memcpy链接起来

我有一个类似的问题。 我们使用的第三方库需要旧的memcpy@GLIBC_2.2.5 。 我的解决scheme是一个扩展的方法@anight张贴。

我也翘曲的memcpy命令,但我不得不使用一个稍微不同的方法,因为解决scheme@anight张贴没有为我工作。

memcpy_wrap.c:

 #include <stddef.h> #include <string.h> asm (".symver wrap_memcpy, memcpy@GLIBC_2.2.5"); void *wrap_memcpy(void *dest, const void *src, size_t n) { return memcpy(dest, src, n); } 

memcpy_wrap.map:

 GLIBC_2.2.5 { memcpy; }; 

构build包装器:

 gcc -c memcpy_wrap.c -o memcpy_wrap.o 

现在最后在链接程序时添加

  • -Wl,--version-script memcpy_wrap.map
  • memcpy_wrap.o

所以你最终会得到像这样的东西:

 g++ <some flags> -Wl,--version-script memcpy_wrap.map <some .o files> memcpy_wrap.o <some libs> 

我有一个类似的问题。 试图在RHEL 7.1上安装一些Oracle组件,我得到了这个:

 $ gcc -o /some/oracle/bin/foo .... -L/some/oracle/lib ... /some/oracle/lib/libfoo.so: undefined reference to `memcpy@GLIBC_2.14' 

看来(我的)RHEL的glibc只定义了memcpy@GLIBC_2.2.5:

 $ readelf -Ws /usr/lib/x86_64-redhat-linux6E/lib64/libc_real.so | fgrep memcpy@ 367: 000000000001bfe0 16 FUNC GLOBAL DEFAULT 8 memcpy@@GLIBC_2.2.5 1166: 0000000000019250 16 FUNC WEAK DEFAULT 8 wmemcpy@@GLIBC_2.2.5 

所以,我设法解决这个问题,首先创build一个memcpy.c文件没有包装,如下所示:

 #include <string.h> asm (".symver old_memcpy, memcpy@GLIBC_2.2.5"); // hook old_memcpy as memcpy@2.2.5 void *old_memcpy(void *, const void *, size_t ); void *memcpy(void *dest, const void *src, size_t n) // then export memcpy { return old_memcpy(dest, src, n); } 

和一个memcpy.map文件,将memcpy导出为memcpy@GLIBC_2.14:

 GLIBC_2.14 { memcpy; }; 

然后我编译我自己的memcpy.c到像这样的共享库:

 $ gcc -shared -fPIC -c memcpy.c $ gcc -shared -fPIC -Wl,--version-script memcpy.map -o libmemcpy-2.14.so memcpy.o -lc 

,将libmemcpy-2.14.so移动到/ some / oracle / lib(由我的链接中的-L参数指向),然后再次链接

 $ gcc -o /some/oracle/bin/foo .... -L/some/oracle/lib ... /some/oracle/lib/libmemcpy-2.14.so -lfoo ... 

(编译没有错误),并通过以下validation:

 $ ldd /some/oracle/bin/foo linux-vdso.so.1 => (0x00007fff9f3fe000) /some/oracle/lib/libmemcpy-2.14.so (0x00007f963a63e000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f963a428000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f963a20c000) librt.so.1 => /lib64/librt.so.1 (0x00007f963a003000) libc.so.6 => /lib64/libc.so.6 (0x00007f9639c42000) /lib64/ld-linux-x86-64.so.2 (0x00007f963aa5b000) 

这对我有效。 我希望它也为你做。

我显然有点迟来回应,但我最近升级(更多的原因,永不升级)我的Linux操作系统到新的libc随附的XUbuntu 14.04。 我在我的机器上编译了一个共享库,客户使用这个共享库,无论出于什么合法的原因,都没有从10.04版升级他们的环境。 我编译的共享库不再加载到他们的环境中,因为gcc把依赖关系放在memcpy glibc v。2.14(或更高版本)上。 让我们放下这个疯狂。 整个项目的解决方法有三个方面:

  1. 添加到我的gcc cflags:-include glibc_version_nightmare.h
  2. 创build了glibc_version_nightmare.h
  3. 创build一个Perl脚本来validation.so中的符号

glibc_version_nightmare.h:

 #if defined(__GNUC__) && defined(__LP64__) /* only under 64 bit gcc */ #include <features.h> /* for glibc version */ #if defined(__GLIBC__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 14) /* force mempcy to be from earlier compatible system */ __asm__(".symver memcpy,memcpy@GLIBC_2.2.5"); #endif #undef _FEATURES_H /* so gets reloaded if necessary */ #endif 

perl脚本片段:

 ... open SYMS, "nm $flags $libname |"; my $status = 0; sub complain { my ($symbol, $verstr) = @_; print STDERR "ERROR: $libname $symbol requires $verstr\n"; $status = 1; } while (<SYMS>) { next unless /\@\@GLIBC/; chomp; my ($symbol, $verstr) = (m/^\s+.\s(.*)\@\@GLIBC_(.*)/); die "unable to parse version from $libname in $_\n" unless $verstr; my @ver = split(/\./, $verstr); complain $symbol, $verstr if ($ver[0] > 2 || $ver[1] > 10); } close SYMS; exit $status; 

我想你可以做一个简单的包含symver语句的C文件,也可以使用一个调用memcpy的虚拟函数。 那么你只需要确保生成的目标文件是给链接器的第一个文件。

我build议你链接memcpy()静态; 或者findmemcpy()的源代码并将其编译为自己的库。

它可能是由旧的ld(gnu链接)版本引起的。 以下简单的问题:

 #include <string.h> #include <stdio.h> int main(int argc,char **argv) { char buf[5]; memset(buf,0,sizeof(buf)); printf("ok\n"); return 0; } 

当我使用ld 2.19.1时,memset被重定位到:memset @@ GLIBC_2.0,并导致崩溃。 升级到2.25后,它是:memset @ plt,崩溃解决了。