链接旧版本的 libc 以提供更大的应用程序覆盖范围

发布于 2024-09-29 06:34:20 字数 216 浏览 2 评论 0原文

Linux 二进制文件通常动态链接到核心系统库 (libc)。这使得二进制文件的内存占用非常小,但依赖于最新库的二进制文件将无法在旧系统上运行。相反,链接到旧库的二进制文件将在最新系统上正常运行。

因此,为了确保我们的应用程序在分发过程中具有良好的覆盖范围,我们需要找出我们可以支持的最旧的 libc,并将我们的二进制文件链接到它。

我们应该如何确定可以链接到的 libc 的最旧版本?

Linux binaries are usually dynamically linked to the core system library (libc). This keeps the memory footprint of the binary quite small but binaries which are dependent on the latest libraries will not run on older systems. Conversely, binaries linked to older libraries will run happily on the latest systems.

Therefore, in order to ensure our application has good coverage during distribution we need to figure out the oldest libc we can support and link our binary against that.

How should we determine the oldest version of libc we can link to?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

淡淡绿茶香 2024-10-06 06:34:20

找出可执行文件中的哪些符号正在创建对不需要的 glibc 版本的依赖关系。

$ objdump -p myprog
...
Version References:
  required from libc.so.6:
    0x09691972 0x00 05 GLIBC_2.3
    0x09691a75 0x00 03 GLIBC_2.2.5

$ objdump -T myprog | fgrep GLIBC_2.3
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.3   realpath

查看依赖的库,看看旧版本中是否有任何可以链接的符号:

$ objdump -T /lib/libc.so.6 | grep -w realpath
0000000000105d90 g    DF .text  0000000000000021 (GLIBC_2.2.5) realpath
000000000003e7b0 g    DF .text  00000000000004bf  GLIBC_2.3   realpath

我们很幸运!

在代码中请求来自 GLIBC_2.2.5 的版本:

#include <limits.h>
#include <stdlib.h>

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

int main () {
    realpath ("foo", "bar");
}

观察到不再需要 GLIBC_2.3:

$ objdump -p myprog
...
Version References:
  required from libc.so.6:
    0x09691a75 0x00 02 GLIBC_2.2.5

$ objdump -T myprog | grep realpath
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 realpath

有关更多信息,请参阅 http://web.archive.org/web/20160107032111/http://www.trevorpounds.com/blog /?p=103

Work out which symbols in your executable are creating the dependency on the undesired version of glibc.

$ objdump -p myprog
...
Version References:
  required from libc.so.6:
    0x09691972 0x00 05 GLIBC_2.3
    0x09691a75 0x00 03 GLIBC_2.2.5

$ objdump -T myprog | fgrep GLIBC_2.3
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.3   realpath

Look within the depended-upon library to see if there are any symbols in older versions that you can link against:

$ objdump -T /lib/libc.so.6 | grep -w realpath
0000000000105d90 g    DF .text  0000000000000021 (GLIBC_2.2.5) realpath
000000000003e7b0 g    DF .text  00000000000004bf  GLIBC_2.3   realpath

We're in luck!

Request the version from GLIBC_2.2.5 in your code:

#include <limits.h>
#include <stdlib.h>

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

int main () {
    realpath ("foo", "bar");
}

Observe that GLIBC_2.3 is no longer needed:

$ objdump -p myprog
...
Version References:
  required from libc.so.6:
    0x09691a75 0x00 02 GLIBC_2.2.5

$ objdump -T myprog | grep realpath
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 realpath

For further information, see http://web.archive.org/web/20160107032111/http://www.trevorpounds.com/blog/?p=103.

不弃不离 2024-10-06 06:34:20

不幸的是,@Sam 的解决方案在我的情况下效果不佳。但按照他的方法,我找到了自己的方法来解决这个问题。

这是我的情况:

我正在使用 Thrift 框架(它是一个 RPC 中间件)编写一个 C++ 程序。与动态链接相比,我更喜欢静态链接,因此我的程序静态链接到 libthrift.a 而不是 libthrift.so。但是,libthrift.a 动态链接到 glibc,并且由于我的 libthrift.a 是在使用 glibc 2.15 的系统上构建的,所以我的 libthrift.a > 使用glibc 2.15提供的memcpy版本2.14(memcpy@GLIBC_2.14)。

但问题是我们的服务器机器只有 glibc 版本 2.5,其中只有 memcpy@GLIBC_2.2.5。它比 memcpy@GLIBC_2.14 低得多。所以,当然,我的服务器程序不能在那些机器上运行。

我找到了这个解决方案:

  1. 使用 .symver 获取对 memcpy@GLIBC_2.2.5 的引用。

  2. 编写我自己的__wrap_memcpy函数,它直接调用memcpy@GLIBC_2.2.5

  3. 链接我的程序时,添加-Wl,--wrap=memcpy选项到gcc/g++。

步骤1和2涉及的代码在这里:https://gist.github.com/nicky-zs/7541169

Unfortunately, @Sam's solution doesn't work well in my situation. But according to his way, I found my own way to solve that.

This is my situation:

I'm writing a C++ program using the Thrift framework(it's an RPC middleware). I prefer static link to dynamic link, so my program is linked to libthrift.a statically instead of libthrift.so. However, libthrift.a is dynamically linked to glibc, and since my libthrift.a is build on my system with glibc 2.15, my libthrift.a uses memcpy of version 2.14(memcpy@GLIBC_2.14) provided by glibc 2.15.

But the problem is that our server machines have only the glibc version 2.5 which has only memcpy@GLIBC_2.2.5. It is much lower than memcpy@GLIBC_2.14. So, of course, my server program can't run on those machines.

And I found this solusion:

  1. Using .symver to obtain the ref to memcpy@GLIBC_2.2.5.

  2. Write my own __wrap_memcpy function which just calls memcpy@GLIBC_2.2.5 directly.

  3. When linking my program, add -Wl,--wrap=memcpy option to gcc/g++.

The code involved in steps 1 and 2 is here: https://gist.github.com/nicky-zs/7541169

鸠魁 2024-10-06 06:34:20

要以更自动化的方式执行此操作,您可以使用以下脚本创建 GLIBC 中比给定版本(第 2 行设置)更新的所有符号的列表。它创建一个 glibc.h 文件(文件名由脚本参数设置),其中包含所有必需的 .symver 声明。然后,您可以将 -include glibc.h 添加到 CFLAGS 以确保它在编译中的所有位置都被拾取。

如果您不使用任何未经上述包含而编译的静态库,这就足够了。如果这样做,并且不想重新编译,可以使用 objcopy 创建库的副本,并将符号重命名为旧版本。该脚本的倒数第二行创建一个系统版本 libstdc++.a,它将链接到旧的 glibc 符号。添加 -L. (或 -Lpath/to/libstdc++.a/)将使您的程序静态链接 libstdc++,而无需链接一堆新符号。如果不需要,请删除最后两行和 printf ... redeff 行。

#!/bin/bash
maxver=2.9
headerf=${1:-glibc.h}
set -e
for lib in libc.so.6 libm.so.6 libpthread.so.0 libdl.so.2 libresolv.so.2 librt.so.1; do
objdump -T /usr/lib/$lib
done | awk -v maxver=${maxver} -vheaderf=${headerf} -vredeff=${headerf}.redef -f <(cat <<'EOF'
BEGIN {
split(maxver, ver, /\./)
limit_ver = ver[1] * 10000 + ver[2]*100 + ver[3]
}
/GLIBC_/ {
gsub(/\(|\)/, "",$(NF-1))
split($(NF-1), ver, /GLIBC_|\./)
vers = ver[2] * 10000 + ver[3]*100 + ver[4]
if (vers > 0) {
    if (symvertext[$(NF)] != $(NF-1))
        count[$(NF)]++
    if (vers <= limit_ver && vers > symvers[$(NF)]) {
        symvers[$(NF)] = vers
        symvertext[$(NF)] = $(NF-1)
    }
}
}
END {
for (s in symvers) {
    if (count[s] > 1) {
        printf("__asm__(\".symver %s,%s@%s\");\n", s, s, symvertext[s]) > headerf
        printf("%s %s@%s\n", s, s, symvertext[s]) > redeff
    }
}
}
EOF
)
sort ${headerf} -o ${headerf}
objcopy --redefine-syms=${headerf}.redef /usr/lib/libstdc++.a libstdc++.a
rm ${headerf}.redef

To do this in a more automated fashion, you can use the following script to create a list of all the symbols that are newer in your GLIBC than in a given version (set on line 2). It creates a glibc.h file (filename set by the script argument) which contains all the necessary .symver declarations. You can then add -include glibc.h to your CFLAGS to make sure it gets picked up everywhere in your compilation.

This is sufficient if you don't use any static libraries that were compiled without the above include. If you do, and you don't want to recompile, you can use objcopy to create a copy of the library with the symbols renamed to the old versions. The second to bottom line of the script creates a version of your system libstdc++.a that will link against the old glibc symbols. Adding -L. (or -Lpath/to/libstdc++.a/) will make your program statically link libstdc++ without linking in a bunch of new symbols. If you don't need this, delete the last two lines and the printf ... redeff line.

#!/bin/bash
maxver=2.9
headerf=${1:-glibc.h}
set -e
for lib in libc.so.6 libm.so.6 libpthread.so.0 libdl.so.2 libresolv.so.2 librt.so.1; do
objdump -T /usr/lib/$lib
done | awk -v maxver=${maxver} -vheaderf=${headerf} -vredeff=${headerf}.redef -f <(cat <<'EOF'
BEGIN {
split(maxver, ver, /\./)
limit_ver = ver[1] * 10000 + ver[2]*100 + ver[3]
}
/GLIBC_/ {
gsub(/\(|\)/, "",$(NF-1))
split($(NF-1), ver, /GLIBC_|\./)
vers = ver[2] * 10000 + ver[3]*100 + ver[4]
if (vers > 0) {
    if (symvertext[$(NF)] != $(NF-1))
        count[$(NF)]++
    if (vers <= limit_ver && vers > symvers[$(NF)]) {
        symvers[$(NF)] = vers
        symvertext[$(NF)] = $(NF-1)
    }
}
}
END {
for (s in symvers) {
    if (count[s] > 1) {
        printf("__asm__(\".symver %s,%s@%s\");\n", s, s, symvertext[s]) > headerf
        printf("%s %s@%s\n", s, s, symvertext[s]) > redeff
    }
}
}
EOF
)
sort ${headerf} -o ${headerf}
objcopy --redefine-syms=${headerf}.redef /usr/lib/libstdc++.a libstdc++.a
rm ${headerf}.redef
笨死的猪 2024-10-06 06:34:20

glibc 2.2 是一个非常常见的最低版本。然而,找到该版本的构建平台可能并非易事。

也许更好的方向是考虑您想要支持并在此基础上构建的最古老的操作系统。

glibc 2.2 is a pretty common minimum version. However finding a build platform for that version may be non-trivial.

Probably a better direction is to think about the oldest OS you want to support and build on that.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文