【问题标题】:Mismatch between output of ldd --version and ldd -r -v a.outldd --version 和 ldd -r -v a.out 的输出不匹配
【发布时间】:2022-01-21 01:25:17
【问题描述】:

我正在尝试理解 ldd --versionldd -v a.out 的输出方式

我有以下简单的程序

#include <iostream>
#include <string>
#include <cstring>

int main()
{
    std::cout << "Hello world" << std::endl;
    std::string a = "Test string";
    char b[15] = {};
    memcpy(b, a.c_str(), 15);
    std::cout << b << std::endl;
    return 0;
}

我用下面的命令编译它

g++ --std=c++17 test.cpp

我想知道当我运行 memcpy 时这个程序将使用哪个 glibc 版本。

ldd --version 在这个系统上的输出是:

ldd --version
ldd (Ubuntu GLIBC 2.31-0ubuntu9.2) 2.31
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.

ldd -v a.out 的输出是

ldd -v a.out 
    linux-vdso.so.1 (0x00007ffe7d3f3000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f050bb2f000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f050bb14000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f050b922000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f050b7d3000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f050bd3a000)

    Version information:
    ./a.out:
        libgcc_s.so.1 (GCC_3.0) => /lib/x86_64-linux-gnu/libgcc_s.so.1
        libc.so.6 (GLIBC_2.4) => /lib/x86_64-linux-gnu/libc.so.6
        libc.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libc.so.6
        libstdc++.so.6 (GLIBCXX_3.4.21) => /usr/lib/x86_64-linux-gnu/libstdc++.so.6
        libstdc++.so.6 (CXXABI_1.3) => /usr/lib/x86_64-linux-gnu/libstdc++.so.6
        libstdc++.so.6 (GLIBCXX_3.4) => /usr/lib/x86_64-linux-gnu/libstdc++.so.6
    /usr/lib/x86_64-linux-gnu/libstdc++.so.6:
        libm.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libm.so.6
        ld-linux-x86-64.so.2 (GLIBC_2.3) => /lib64/ld-linux-x86-64.so.2
        libgcc_s.so.1 (GCC_4.2.0) => /lib/x86_64-linux-gnu/libgcc_s.so.1
        libgcc_s.so.1 (GCC_3.4) => /lib/x86_64-linux-gnu/libgcc_s.so.1
        libgcc_s.so.1 (GCC_3.3) => /lib/x86_64-linux-gnu/libgcc_s.so.1
        libgcc_s.so.1 (GCC_3.0) => /lib/x86_64-linux-gnu/libgcc_s.so.1
        libc.so.6 (GLIBC_2.14) => /lib/x86_64-linux-gnu/libc.so.6
        libc.so.6 (GLIBC_2.6) => /lib/x86_64-linux-gnu/libc.so.6
        libc.so.6 (GLIBC_2.4) => /lib/x86_64-linux-gnu/libc.so.6
        libc.so.6 (GLIBC_2.18) => /lib/x86_64-linux-gnu/libc.so.6
        libc.so.6 (GLIBC_2.16) => /lib/x86_64-linux-gnu/libc.so.6
        libc.so.6 (GLIBC_2.3) => /lib/x86_64-linux-gnu/libc.so.6
        libc.so.6 (GLIBC_2.3.4) => /lib/x86_64-linux-gnu/libc.so.6
        libc.so.6 (GLIBC_2.17) => /lib/x86_64-linux-gnu/libc.so.6
        libc.so.6 (GLIBC_2.3.2) => /lib/x86_64-linux-gnu/libc.so.6
        libc.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libc.so.6
    /lib/x86_64-linux-gnu/libgcc_s.so.1:
        libc.so.6 (GLIBC_2.14) => /lib/x86_64-linux-gnu/libc.so.6
        libc.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libc.so.6
    /lib/x86_64-linux-gnu/libc.so.6:
        ld-linux-x86-64.so.2 (GLIBC_2.3) => /lib64/ld-linux-x86-64.so.2
        ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2
    /lib/x86_64-linux-gnu/libm.so.6:
        ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2
        libc.so.6 (GLIBC_2.4) => /lib/x86_64-linux-gnu/libc.so.6
        libc.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libc.so.6
        libc.so.6 (GLIBC_PRIVATE) => /lib/x86_64-linux-gnu/libc.so.6

我无法理解的是,如果 ldd --version 说我有 GLIBC 2.31 版可用,那么为什么我的可执行文件 ldd 输出显示 GLIBC_2.4GLIBC_2.2.5 for a.out。

理解这个的正确方法是什么?

如果我在具有旧版本 libc.so 的系统上编译二进制文件(假设 GLIBC 的最高版本为 2.17)然后在具有新版本 libc.so 的系统上运行二进制文件会发生什么(假设GLIBC 的最高版本为 2.31)?

谢谢

【问题讨论】:

  • 符号版本是该函数上次更改其接口时的库版本
  • 复制一个 11 字节字符串的前 15 个字节不会很好。
  • @AlanBirtles 你指的是输出ldd --version 吗?
  • @SamVarshavchik 是的。这只是一个演示程序,用于理解链接等,不用于实际使用。
  • 不,这只是构建 ldd 的 libc 版本,与您的程序无关

标签: c++ linux linker glibc ldd


【解决方案1】:

您应该阅读this answer,并查看readelf -V a.out 的输出。

链接程序时,它会记录链接时使用的符号版本(当前)。

您的程序使用的许多符号没有改变,例如GLIBC_2.2.5,所以ldd 说:您需要至少 版本GLIBC_2.2.5(对于这些符号)。你使用的一些符号已经GLIBC-2.16GLIBC-2.17GLIBC-2.18 中发生了变化,所以ldd 表示你也需要这些符号。

如果我在具有旧版本 libc.so 的系统上编译二进制文件(假设 GLIBC 的最高版本为 2.17)然后在具有新版本 libc.so 的系统上运行二进制文件会发生什么(假设GLIBC 的最高版本为 2.31)?

记录的符号(编码为a.out)都将是GLIBC_2.17或更早版本,并且程序将在较新的系统上运行良好,因为 GLIBC 保证向后兼容性(构建在较旧系统上的程序继续运行良好较新的)。

但是如果你做相反的事情——建立在GLIBC-2.31 系统上并尝试在GLIBC-2.17 系统上运行程序,它可能(也可能不会,取决于它使用的符号实际使用)失败。

在您提供的示例中,GLIBC 的最高要求版本是GLIBC_2.18。因此,这个特定的a.out 将在GLIBC-2.18 或更新的系统上正常工作,但在GLIBC-2.17 或更旧的系统上会失败。

更新:

如果在具有最高版本 GLIBC_2_17 的旧系统上编译的可执行文件在具有可用 GLIBC_2_31 的系统上运行,会发生什么情况?可执行文件是否会选择最新的符号(如果它们是 ABI 投诉),例如说 memcpy - exec 在旧系统上编译(使用不支持向量的 GLIBC)在新系统上运行时(使用具有向量支持的 memcpy 的 GLIBC)将从新系统中选择 memcpy支持向量的 GLIBC。

是的,可执行文件会选择它所链接的版本,只要给定函数的 ABI 没有更改,它将是最新版本。

memcpy 的情况尤其复杂一些。在 Fedora 35 x86_64 系统上 (GLIBC-2.34):

nm /lib64/libc.so.6 | grep ' memcpy'
00000000000a1560 i memcpy
00000000000a1560 i memcpy@@GLIBC_2.14
00000000000b9c30 T memcpy@GLIBC_2.2.5

您可以在这里看到memcpy ABI 在GLIBC-2.14 中发生了变化,它变成了GNU indirect function。您可以在链接中阅读详细信息,但 TL;DR 是您的程序调用的 actual 函数将取决于处理器功能。可以是以下任何一种:

00000000001870b0 t __memcpy_avx512_no_vzeroupper
0000000000189f00 t __memcpy_avx512_unaligned
0000000000189f70 t __memcpy_avx512_unaligned_erms
00000000001828f0 t __memcpy_avx_unaligned
0000000000182960 t __memcpy_avx_unaligned_erms
000000000018b0e0 t __memcpy_avx_unaligned_erms_rtm
000000000018b070 t __memcpy_avx_unaligned_rtm
00000000000b9ca0 t __memcpy_erms
00000000001920b0 t __memcpy_evex_unaligned
0000000000192120 t __memcpy_evex_unaligned_erms
00000000000b9c30 t __memcpy_sse2_unaligned
00000000000b9d10 t __memcpy_sse2_unaligned_erms
000000000015d400 t __memcpy_ssse3
0000000000162990 t __memcpy_ssse3_back

【讨论】:

  • 如果在具有最高版本 GLIBC_2_17 的旧系统上编译的可执行文件在具有 GLIBC_2_31 可用的系统上运行,会发生什么情况?可执行文件是否会选择最新的符号(如果它们是 ABI 投诉),例如说 memcpy - exec 在旧系统上编译(使用不支持向量的 GLIBC)在新系统上运行时(使用具有向量支持的 memcpy 的 GLIBC)将从新系统中选择 memcpy具有矢量支持的 GLIBC。
  • @yashC 我已经更新了答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多