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

Pla*_*aHH 43 c linux linker gcc ld

在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并且我的构建系统需要.

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

对我不起作用的替代方案是:

  • 使用asm .symver(在Trevor Pounds博客的Web Archive上看到)因为这需要我确保symver在所有使用memcpy的代码之前,这将是非常困难的(具有第三方代码的复杂代码库)
  • 使用旧库维护构建环境; 仅仅是因为我想在我的桌面系统上进行开发,并且在我们的网络中同步这些东西将会变得很有用.

在考虑链接器所做的所有工作时,实现它似乎并不困难,毕竟它还有一些代码可以找出符号的默认版本.

任何其他与简单链接器命令行具有相同复杂程度的想法(如创建简单的链接描述文件等)也是受欢迎的,只要它们不像编辑生成的二进制文件那样奇怪的黑客...

编辑: 为了保护未来的读者,除了以下的想法,我找到--wrap了链接器的选项,这有时也很有用.

小智 47

我找到了以下工作解决方案.首先创建文件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);
}
Run Code Online (Sandbox Code Playgroud)

编译此文件不需要额外的CFLAGS.然后用-Wl链接你的程序, - wrap = memcpy.

  • 由于某种原因,这对我不起作用。仍然在寻找旧的 memcpy (2认同)

Ran*_*832 19

只需静态链接memcpy - 从libc.a中拉出memcpy.o ar x /path/to/libc.a memcpy.o(无论版本 - memcpy几乎都是一个独立的函数)并将其包含在最终链接中.请注意,如果您的项目是分发给公众而非开源的,静态链接可能会使许可问题复杂化.

或者,您可以自己简单地实现memcpy,尽管glibc中手动调整的汇编版本可能更有效

请注意,memcpy@GLIBC_2.2.5被映射到memmove(旧版本的memcpy在可预测的方向上一致地复制,这导致它有时在应用memmove时被误用),这是版本碰撞的唯一原因 - 你可以简单地用代码中的memmove替换memcpy用于此特定情况.

或者您可以转到静态链接,或者您可以确保网络上的所有系统都具有与构建计算机相同或更好的版本.

  • 您应该只在x86_64上用memmove()替换memcpy(),而不是没有版本化memcpy()的其他体系结构,因此不会碰到依赖项.不要不必要地牺牲性能.(并确保使用memcpy()进行测试) (3认同)
  • +1提及memmove(); imo没有充分的理由使用memcpy()及其对重叠字符串的愚蠢限制. (2认同)

Ort*_*ier 8

我有类似的问题.我们使用的第三方库需要旧库memcpy@GLIBC_2.2.5.我的解决方案是@anight发布的扩展方法.

我也扭曲了这个memcpy命令,但是我不得不使用一种稍微不同的方法,因为@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);
}
Run Code Online (Sandbox Code Playgroud)

memcpy_wrap.map:

GLIBC_2.2.5 {
   memcpy;
};
Run Code Online (Sandbox Code Playgroud)

构建包装器:

gcc -c memcpy_wrap.c -o memcpy_wrap.o
Run Code Online (Sandbox Code Playgroud)

现在终于在链接程序时添加了

  • -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>
Run Code Online (Sandbox Code Playgroud)


ava*_*vit 6

我遇到了类似的问题.试图在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'
Run Code Online (Sandbox Code Playgroud)

看来(我的)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
Run Code Online (Sandbox Code Playgroud)

因此,我设法绕过这个,首先创建一个没有包装的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);
}
Run Code Online (Sandbox Code Playgroud)

以及将memcpy导出为memcpy@GLIBC_2.14的memcpy.map文件:

GLIBC_2.14 {
   memcpy;
};
Run Code Online (Sandbox Code Playgroud)

然后我将自己的memcpy.c编译成一个共享库,如下所示:

$ gcc -shared -fPIC -c memcpy.c
$ gcc -shared -fPIC -Wl,--version-script memcpy.map -o libmemcpy-2.14.so memcpy.o -lc
Run Code Online (Sandbox Code Playgroud)

,将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 ...
Run Code Online (Sandbox Code Playgroud)

(编译时没有错误)并通过以下方式验证:

$ 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)
Run Code Online (Sandbox Code Playgroud)

这对我有用.我希望它也适合你.


小智 5

显然我对此的回应有点晚了,但我最近将我的 Linux 操作系统升级(有更多理由从不升级)到 XUbuntu 14.04,它附带了新的 libc。我在我的机器上编译了一个共享库,供那些由于任何合法原因未从 10.04 升级其环境的客户使用。我编译的共享库不再加载到它们的环境中,因为 gcc 依赖于 memcpy glibc v.2.14(或更高版本)。让我们先把这件事的疯狂抛在一边。我整个项目的解决方法有三个:

  1. 添加到我的 gcc cflags:-include glibc_version_nightmare.h
  2. 创建了 glibc_version_nightmare.h
  3. 创建了一个 perl 脚本来验证 .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
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)