Linux 链接到 .so 文件中的旧符号版本
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8823267/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Linking against older symbol version in a .so file
提问by PlasmaHH
Using gcc and ld on x86_64 linux I need to link against a newer version of a library (glibc 2.14) but the executable needs to run on a system with an older version (2.5). Since the only incompatible symbol is memcpy (needing memcpy@GLIBC_2.2.5 but the library providing memcpy@GLIBC_2.14), I would like to tell the linker that instead of taking the default version for memcpy, it should take an old version I specify.
在 x86_64 linux 上使用 gcc 和 ld 我需要链接到较新版本的库 (glibc 2.14),但可执行文件需要在具有较旧版本 (2.5) 的系统上运行。由于唯一不兼容的符号是 memcpy(需要 memcpy@GLIBC_2.2.5 但提供 memcpy@GLIBC_2.14 的库),我想告诉链接器,而不是采用 memcpy 的默认版本,它应该采用我指定的旧版本.
I found a quite arkward way to do it: simply specify a copy of the old .so file at the linker command line. This works fine, but I don't like the idea of having multiple .so files (I could only make it work by specifying all old libraries I link to that also have references to memcpy) checked into the svn and needed by my build system.
我找到了一种非常奇怪的方法:只需在链接器命令行中指定旧 .so 文件的副本。这工作正常,但我不喜欢将多个 .so 文件(我只能通过指定我链接到的所有旧库也有对 memcpy 的引用来使其工作)签入 svn 并被我的构建系统需要的想法.
So I am searching for a way to tell the linker to take the old versioned symbol.
所以我正在寻找一种方法来告诉链接器采用旧版本的符号。
Alternatives that don't work (well) for me are:
对我不起作用(很好)的替代方案是:
- Using asm .symver (as seen on Web Archive of Trevor Pounds' Blog) since this would require me to make sure the symver is before all the code that is using memcpy, which would be very hard (complex codebase with 3rd party code)
- Maintaining a build environment with the old libraries; simply because I want to develop on my desktop system and it would be a pita to sync stuff around in our network.
- 使用 asm .symver(如Trevor Pounds 博客的 Web 档案中所见),因为这需要我确保 symver 位于所有使用 memcpy 的代码之前,这将非常困难(带有 3rd 方代码的复杂代码库)
- 使用旧库维护构建环境;仅仅因为我想在我的桌面系统上进行开发,并且在我们的网络中同步内容将是一个皮塔。
When thinking about all the jobs a linker does, it doesn't seem like a hard thing to imlpement, after all it has some code to figure out the default version of a symbol too.
当考虑链接器所做的所有工作时,实现起来似乎并不难,毕竟它也有一些代码来确定符号的默认版本。
Any other ideas that are on the same complexity level as a simple linker command line (like creating a simple linker script etc.) are welcome too, as long as they are not weird hacks like editing the resulting binary...
任何其他与简单链接器命令行(例如创建简单的链接器脚本等)具有相同复杂性级别的想法也受到欢迎,只要它们不是像编辑生成的二进制文件这样的奇怪黑客......
edit:To conserve this for the future readers, additionally to the below ideas I found the option --wrap
to the linker, which might be useful sometimes too.
编辑:为了为未来的读者保留这一点,除了以下想法之外,我还找到--wrap
了链接器的选项,有时也可能很有用。
采纳答案by Random832
Just link memcpy statically - pull memcpy.o out of libc.a ar x /path/to/libc.a memcpy.o
(whatever version - memcpy is pretty much a standalone function) and include it in your final link. Note that static linking may complicate licensing issues if your project is distributed to the public and not open-source.
只需静态链接 memcpy - 将 memcpy.o 从 libc.a 中拉出ar x /path/to/libc.a memcpy.o
(无论版本如何 - memcpy 几乎是一个独立的函数)并将其包含在您的最终链接中。请注意,如果您的项目是向公众分发而不是开源的,则静态链接可能会使许可问题复杂化。
Alternatively, you could simply implement memcpy yourself, though the hand-tuned assembly version in glibc is likely to be more efficient
或者,您可以简单地自己实现 memcpy,尽管 glibc 中的手动调整汇编版本可能更有效
Note that memcpy@GLIBC_2.2.5is mapped to memmove (old versions of memcpy consistently copied in a predictable direction, which led to it sometimes being misused when memmove should have been used), and this is the only reason for the version bump - you could simply replace memcpy with memmove in your code for this specific case.
请注意,memcpy@GLIBC_2.2.5被映射到 memmove(旧版本的 memcpy 始终以可预测的方向复制,这导致在应该使用 memmove 时有时会误用它),这是版本凹凸的唯一原因 - 您对于这种特定情况,可以简单地在代码中用 memmove 替换 memcpy。
Or you could go to static linking, or you could ensure that all systems on your network have the same or better version than your build machine.
或者您可以使用静态链接,或者您可以确保网络上的所有系统都具有与构建机器相同或更好的版本。
回答by zvrba
I think you can get away with making a simple C file containing the symver statement and perhaps a dummy function calling memcpy. Then you just have to ensure that the resulting object file is the first file given to linker.
我认为您可以制作一个包含 symver 语句的简单 C 文件,也许还有一个调用 memcpy 的虚拟函数。然后您只需要确保生成的目标文件是提供给链接器的第一个文件。
回答by Pete Wilson
I suggest you either link memcpy() statically; or find the source of memcpy( ) and compile it as your own library.
我建议您静态链接 memcpy() ;或找到 memcpy() 的源代码并将其编译为您自己的库。
回答by anight
I found the following working solution. First create file memcpy.c:
我找到了以下工作解决方案。首先创建文件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);
}
No additional CFLAGS needed to compile this file. Then link your program with -Wl,--wrap=memcpy.
编译这个文件不需要额外的 CFLAGS。然后用-Wl,--wrap=memcpy链接你的程序。
回答by Ortwin Angermeier
I had a similar issue. A third party library we use needs the old memcpy@GLIBC_2.2.5
. My solution is an extended approach @anight posted.
我有一个类似的问题。我们使用的第三方库需要旧的memcpy@GLIBC_2.2.5
. 我的解决方案是@anight 发布的扩展方法。
I also warp the memcpy
command, but i had to use a slightly different approach, since the solution @anight posted did not work for me.
我也扭曲了memcpy
命令,但我不得不使用稍微不同的方法,因为@anight 发布的解决方案对我不起作用。
memcpy_wrap.c:
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:
memcpy_wrap.map:
GLIBC_2.2.5 {
memcpy;
};
Build the wrapper:
构建包装器:
gcc -c memcpy_wrap.c -o memcpy_wrap.o
Now finally when linking the program add
现在终于在链接程序时添加
-Wl,--version-script memcpy_wrap.map
memcpy_wrap.o
-Wl,--version-script memcpy_wrap.map
memcpy_wrap.o
so that you will end up with something like:
这样你最终会得到类似的东西:
g++ <some flags> -Wl,--version-script memcpy_wrap.map <some .o files> memcpy_wrap.o <some libs>
回答by Oliver K.
I'm clearly a little late responding to this but I recently upgraded (more reasons to never upgrade) my Linux OS to XUbuntu 14.04 which came with the new libc. I compile a shared library on my machine which is used by clients who, for whatever legitimate reasons, have not upgraded their environment from 10.04. The shared library I compiled no longer loaded in their environment because gcc put a dependency on memcpy glibc v. 2.14 (or higher). Let's leave aside the insanity of this. The workaround across my whole project was three fold:
我对此的回应显然有点晚了,但我最近将我的 Linux 操作系统升级(有更多理由从不升级)到 XUbuntu 14.04,它随新的 libc 一起提供。我在我的机器上编译了一个共享库,供那些出于任何合法原因没有从 10.04 升级其环境的客户使用。我编译的共享库不再加载到他们的环境中,因为 gcc 依赖于 memcpy glibc v. 2.14(或更高版本)。让我们抛开这种疯狂。我整个项目的解决方法有三方面:
- added to my gcc cflags: -include glibc_version_nightmare.h
- created the glibc_version_nightmare.h
- created a perl script to verify the symbols in the .so
- 添加到我的 gcc cflags:-include glibc_version_nightmare.h
- 创建了 glibc_version_nightmare.h
- 创建了一个 perl 脚本来验证 .so 中的符号
glibc_version_nightmare.h:
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 script fragment:
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;
回答by avarvit
I had a similar problem. Trying to install some oracle components on RHEL 7.1, I got this:
我有一个类似的问题。尝试在 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'
It seems that (my) RHEL's glibc only defines memcpy@GLIBC_2.2.5:
似乎(我的)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
So, I managed to get around this, by first creating a memcpy.c file without wrapping, as follows:
所以,我设法解决了这个问题,首先创建了一个没有包装的 memcpy.c 文件,如下所示:
#include <string.h>
asm (".symver old_memcpy, memcpy@GLIBC_2.2.5"); // hook old_memcpy as [email protected]
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);
}
and a memcpy.map file that exports our memcpy as memcpy@GLIBC_2.14:
以及将我们的 memcpy 导出为 memcpy@GLIBC_2.14 的 memcpy.map 文件:
GLIBC_2.14 {
memcpy;
};
I then compiled my own memcpy.c into a shared lib like this:
然后我将自己的 memcpy.c 编译成这样的共享库:
$ gcc -shared -fPIC -c memcpy.c
$ gcc -shared -fPIC -Wl,--version-script memcpy.map -o libmemcpy-2.14.so memcpy.o -lc
, moved libmemcpy-2.14.so into /some/oracle/lib (pointed to by -L arguments in my linking), and linked again by
,将 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 ...
(which compiled without errors) and verified it by:
(编译没有错误)并通过以下方式验证:
$ 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)
This worked for me. I hope it does it for you, too.
这对我有用。我希望它也适合你。
回答by jeanerpp
It may caused by old ld (gnu link) version. For following simple problem:
它可能是由旧的 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;
}
When I use ld 2.19.1, memset is relocated to: memset@@GLIBC_2.0, and cause crash. After upgraded to 2.25, it is: memset@plt, and crash solved.
当我使用 ld 2.19.1 时,memset 被重新定位到:memset@@GLIBC_2.0,并导致崩溃。升级到2.25后是:memset@plt,崩溃解决。
回答by Gilles Olivier Vollant
This workaround seem not compatible with -flto compile option.
此解决方法似乎与 -flto 编译选项不兼容。
My solution is calling memmove. memove does exactly the same jobs than memcpy. The only difference is when src and dest zone overlap, memmove is safe and memcpy is unpredictable. So we can safely always call memmove instead memcpy
我的解决方案是调用 memmove。memove 与 memcpy 的工作完全相同。唯一的区别是当 src 和 dest 区域重叠时, memmove 是安全的,而 memcpy 是不可预测的。所以我们可以安全地总是调用 memmove 而不是 memcpy
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
void *__wrap_memcpy(void *dest, const void *src, size_t n)
{
return memmove(dest, src, n);
}
#ifdef __cplusplus
}
#endif
回答by Mildred
For nim-lang, I elaborated on a solution I found using the C compiler --include=
flag as follows:
对于 nim-lang,我详细阐述了我使用 C 编译器--include=
标志找到的解决方案,如下所示:
Create a file symver.hwith:
使用以下命令创建文件symver.h:
__asm__(".symver fcntl,fcntl@GLIBC_2.4");
Build your program with nim c ---passC:--include=symver.h
构建你的程序 nim c ---passC:--include=symver.h
As for me I'm cross compiling too. I compile with nim c --cpu:arm --os:linux --passC:--include=symver.h ...
and I can get symbol versions using arm-linux-gnueabihf-objdump -T ../arm-libc.so.6 | grep fcntl
至于我,我也在交叉编译。我编译,nim c --cpu:arm --os:linux --passC:--include=symver.h ...
我可以使用获得符号版本arm-linux-gnueabihf-objdump -T ../arm-libc.so.6 | grep fcntl
I had to remove ~/.cache/nim
at some point. And it seems to work.
我不得不~/.cache/nim
在某个时候删除。它似乎有效。