在Linux编译时出现“Undefined reference to mempcy@GLIBC_2.14”的错误提示

18

我正在尝试将一个驱动使用ftdi2332h芯片的应用程序从Windows移植到Linux。我按照这些说明在Ubuntu 10.04系统上安装了libftd2xx库。

当我尝试编译任何样例程序时,会出现以下错误:

/usr/local/lib/libftd2xx.so: undefined reference to `memcpy@GLIBC_2.14'
collect2: ld returned 1 exit status

有关如何修复这个问题的任何指南吗?

7个回答

16

mempcy@GLIBC_2.14被称为版本化符号。Glibc在使用它们,而其他运行时库(如musl do not)则不使用。

mempcy@GLIBC_2.14在Linux上编译的重要性是由于Glibc在2012年改变了memcpy的工作方式。 memcpy曾经将字节从{begin → end}(低内存地址到高内存地址)复制。 Glibc 2.13提供了一个优化的memcpy,在某些平台上将{end → begin}复制。我相信“某些平台”包括具有SSE4.1的英特尔机器。然后,Glibc 2.14提供了一个memcpy,恢复了{begin → end}的行为。

一些程序依赖于{begin → end}复制。当程序使用重叠缓冲区时,memcpy会产生未定义的行为。在这种情况下,程序应该使用memmove,但由于发生了{begin → end}的复制,它们得以继续运行。还请参见Strange sound on mp3 flash website(由Adobe Flash引起),Glibc change exposing bugs(在LWN上),The memcpy vs memmove saga等。

为了解决这个问题,看起来你可以将以下内容添加到你的源代码中:

__asm__(".symver memcpy,memcpy@GLIBC_2.2.5");

也许可以像以下这样。然后在您的项目中包含额外的源文件。
$ cat version.c

__asm__(".symver memcpy,memcpy@GLIBC_2.2.5");

+1 非常感谢,这个修复了我的代码。我在同一个C文件中调用了memcpy,但是在阅读了这篇文章之后,我认为这种方法可能会导致实际调用旧的memcpy的代码崩溃。 - Accountant م
1
@Accountantم - 也许你可以构建一个共享对象,并使用LD_PRELOAD确保memcpy绑定到Glibc 2.2.5。 - jww
1
对于任何要使用此解决方案的人,您可以通过 objdump -T ./fooProgram 检查它是否实际上更改了版本标签。 - Accountant م
这是一个很棒的解释,但我不明白“2.2.5”版本在哪里发挥作用。如果我有glibc 2.27,那么带版本号的符号2.2.5会存在吗? - CraigDavid
是的,它会。2.2.5是指旧版本的libc(2.2与2.27相比)具有旧的memcpy实现。新版本的libc包含来自旧版本的带版本号的符号。 - Dave Poston

3
说明文件提到了Ubuntu 12.04,它带有glibc 2.15。而您正在使用Ubuntu 10.04,它带有glibc 2.11.1。您看到的错误消息告诉您,某些二进制文件(在这里最可能是libftd2xx.so)链接到了一个比您链接的更新的glibc版本,这是合理的,考虑到前面的事实。
要么从源代码重新编译libftd2xx.so以针对您系统的glibc版本(可能不是一个选项,因为它只是二进制文件),要么更新您的操作系统。Ubuntu 10.04相当老旧。
作为最后的手段(只有在您喜欢用大锤子打手指的情况下才会尝试这样做),您可以为您的系统编译一个更新的glibc,并将其安装在例如/opt的地方。

1

0

您可以下载并编译libc,并安装在/opt/lib/libcX/libc.so.6下。然后,您可以拥有一个脚本:

LD_LIBRARY_PATH=/opt/lib/libcX:/lib/:/usr/lib:/usr/share/lib
./your_program

构建glibc几乎总是一个坏主意。 - rubenvb
实际上,@perreal提供的答案非常扎实和有效。请参见https://unix.stackexchange.com/a/299665/241016了解更多信息。rubenvb提供的评论是不正确的,因为他或她错过了这是在现有系统库c之旁构建libc的事实。 - Roel Van de Paar
这在运行时是必需的,以便告诉链接器/装载器新库的位置,与编译无关,对吗? - Accountant م

0

我不确定,但如果你正在使用交叉编译器,你必须在某个地方安装兼容版本的基本库(不是/usr/include/usr/lib),并确保编译器使用它们,而不是本地编译器的库。同时,你必须确保整个工具链版本兼容。(我知道这不是一个非常完整的答案,但这就是我所知道的。)


0

我无法让汇编symver技巧起作用,所以我采取了更激烈的行动,重新定义了memcpy:

extern "C" void *memcpy(void *dest, const void *src, size_t n)
{
    return memmove(dest, src, n);
}

这显然是一种更冒险的选择,可能会产生重复定义的问题。它还会在每次调用memcpy时添加一个额外的跳转,而memmove可能会稍微慢一些。但也许它可以帮助您在旧版本的Linux上启动某些东西。


-2

升级到Ubuntu 12.04。我使用Qt时遇到了同样的问题,原来是glibc库太旧了。在互联网上搜索后得知,尝试自行升级glibc非常危险。


听起来他正在使用交叉编译器,但是它试图链接到系统库。无论如何,如果这是一个交叉编译器,更新系统库不会(或不应该)改变任何东西。如果他没有使用捆绑的编译器,他必须确保库与他正在使用的编译器(和头文件)兼容。 - James Kanze
我没有使用交叉编译器。似乎由于某种原因,libftd2xx正在寻找特定版本2.14的libc,而Ubuntu 10.04只有版本2.10。 - user1487551

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接