在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并且我的构建系统需要
所以我正在寻找一种告诉链接器采用旧版本符号的方法。
对我来说不起作用的替代方案是:
当考虑链接器所做的所有工作时,看起来似乎并不困难,毕竟它还有一些代码可以找出符号的默认版本。
与简单的链接器命令行具有相同复杂性级别的任何其他想法(如创建简单的链接描述文件等)也是受欢迎的,只要它们不像编辑生成的二进制文件那样奇怪的黑客...... / p>
修改
为了保护未来的读者,除了以下想法之外,我还在链接器中找到了--wrap
选项,这有时也很有用。
答案 0 :(得分:43)
我找到了以下工作解决方案。首先创建文件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 链接您的程序。
答案 1 :(得分:18)
只需静态链接memcpy - 从libc.a ar x /path/to/libc.a memcpy.o
拉出memcpy.o(无论版本 - memcpy几乎都是一个独立的函数)并将其包含在最终链接中。请注意,如果您的项目是分发给公众而非开源的,静态链接可能会使许可问题复杂化。
或者,您可以自己实现memcpy,尽管glibc中手动调整的汇编版本可能更高效
请注意memcpy@GLIBC_2.2.5映射到memmove(旧版本的memcpy在可预测的方向上一致地复制,导致它有时在应用memmove时被误用),这是该版本的唯一原因bump - 你可以在代码中用memmove替换memcpy用于这个特定的情况。
或者您可以转到静态链接,或者您可以确保网络上的所有系统都具有与构建计算机相同或更好的版本。
答案 2 :(得分:6)
我有类似的问题。我们使用的第三方库需要旧的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);
}
<强> memcpy_wrap.map:强>
GLIBC_2.2.5 {
memcpy;
};
构建包装器:
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>
答案 3 :(得分:5)
我有类似的问题。试图在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
所以,我设法绕过这个,首先创建一个没有包装的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导出为memcpy@GLIBC_2.14的memcpy.map文件:
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 ...
(编译时没有错误)并通过以下方式验证:
$ 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)
这对我有用。我希望它也适合你。
答案 4 :(得分:4)
我显然有点迟到了,但我最近将我的Linux操作系统升级(更多原因从未升级)到新的libc附带的XUbuntu 14.04。我在我的机器上编译了一个共享库,这个库由客户使用,出于任何合法原因,他们没有从10.04升级他们的环境。我编译的共享库不再在其环境中加载,因为gcc依赖于memcpy glibc v.2.14(或更高版本)。让我们抛开这种疯狂。我整个项目的解决方法有三个:
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;
答案 5 :(得分:2)
对于nim-lang,我详细介绍了使用C编译器--include=
标志找到的解决方案,如下所示:
使用以下文件创建文件 symver.h :
__asm__(".symver fcntl,fcntl@GLIBC_2.4");
使用nim c ---passC:--include=symver.h
至于我,我也正在交叉编译。我使用nim c --cpu:arm --os:linux --passC:--include=symver.h ...
进行编译,可以使用arm-linux-gnueabihf-objdump -T ../arm-libc.so.6 | grep fcntl
我不得不在某个时候删除~/.cache/nim
。似乎可行。
答案 6 :(得分:1)
我认为你可以创建一个包含symver语句的简单C文件,也可以是一个调用memcpy的虚函数。然后,您只需确保生成的目标文件是第一个提供给链接器的文件。
答案 7 :(得分:1)
我建议您静态链接memcpy();或找到memcpy()的来源并将其编译为您自己的库。
答案 8 :(得分:1)
此解决方法似乎与-flto编译选项不兼容。
我的解决方案是调用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
答案 9 :(得分:0)
它可能是由旧的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,崩溃解决了。
答案 10 :(得分:0)
最小的可运行自包含示例
main.c
#include <assert.h>
#include <stdlib.h>
#include "a.h"
#if defined(V1)
__asm__(".symver a,a@LIBA_1");
#elif defined(V2)
__asm__(".symver a,a@LIBA_2");
#endif
int main(void) {
#if defined(V1)
assert(a() == 1);
#else
assert(a() == 2);
#endif
return EXIT_SUCCESS;
}
a.c
#include "a.h"
__asm__(".symver a1,a@LIBA_1");
int a1(void) {
return 1;
}
/* @@ means "default version". */
__asm__(".symver a2,a@@LIBA_2");
int a2(void) {
return 2;
}
a.h
#ifndef A_H
#define A_H
int a(void);
#endif
a.map
LIBA_1{
global:
a;
local:
*;
};
LIBA_2{
global:
a;
local:
*;
};
Makefile
CC := gcc -pedantic-errors -std=c89 -Wall -Wextra
.PHONY: all clean run
all: main.out main1.out main2.out
run: all
LD_LIBRARY_PATH=. ./main.out
LD_LIBRARY_PATH=. ./main1.out
LD_LIBRARY_PATH=. ./main2.out
main.out: main.c libcirosantilli_a.so
$(CC) -L'.' main.c -o '$@' -lcirosantilli_a
main1.out: main.c libcirosantilli_a.so
$(CC) -DV1 -L'.' main.c -o '$@' -lcirosantilli_a
main2.out: main.c libcirosantilli_a.so
$(CC) -DV2 -L'.' main.c -o '$@' -lcirosantilli_a
a.o: a.c
$(CC) -fPIC -c '$<' -o '$@'
libcirosantilli_a.so: a.o
$(CC) -Wl,--version-script,a.map -L'.' -shared a.o -o '$@'
libcirosantilli_a.o: a.c
$(CC) -fPIC -c '$<' -o '$@'
clean:
rm -rf *.o *.a *.so *.out
在Ubuntu 16.04上测试。