如何链接到特定的 glibc 版本?

发布于 2024-09-02 03:23:41 字数 222 浏览 12 评论 0原文

当我在 Ubuntu Lucid 10.04 PC 上编译某些内容时,它会与 glibc 链接。 Lucid 使用 glibc 2.11。当我在另一台装有较旧 glibc 的 PC 上运行此二进制文件时,该命令失败,提示没有 glibc 2.11...

据我所知 glibc 使用符号版本控制。我可以强制 gcc 链接到特定的符号版本吗?

在我的具体使用中,我尝试为ARM编译一个gcc交叉工具链。

When I compile something on my Ubuntu Lucid 10.04 PC it gets linked against glibc. Lucid uses 2.11 of glibc. When I run this binary on another PC with an older glibc, the command fails saying there's no glibc 2.11...

As far as I know glibc uses symbol versioning. Can I force gcc to link against a specific symbol version?

In my concrete use I try to compile a gcc cross toolchain for ARM.

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

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

发布评论

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

评论(7

蓬勃野心 2024-09-09 03:23:41

您是正确的,glibc 使用符号版本控制。如果您好奇,此处描述了 glibc 2.1 中引入的符号版本控制实现,并且是Sun 符号版本控制方案的扩展 在这里

一种选择是静态链接您的二进制文件。这可能是最简单的选择。

您还可以在 chroot 构建环境中构建二进制文件,或使用 glibc-new => glibc-交叉编译器。

根据 http://www.trevorpounds。 com 博客文章 链接到旧版本符号 (glibc),只要使用有效的符号,就可以强制将任何符号链接到旧版本符号与最初用于定义版本化符号的 .symver 伪操作相同。以下示例摘自 博客文章

以下示例使用 glibc 的实际路径,但确保它链接到较旧的 2.2.5 版本。

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

__asm__(".symver realpath,realpath@GLIBC_2.2.5");
int main()
{
    const char* unresolved = "/lib64";
    char resolved[PATH_MAX+1];

    if(!realpath(unresolved, resolved))
        { return 1; }

    printf("%s\n", resolved);

    return 0;
}

You are correct in that glibc uses symbol versioning. If you are curious, the symbol versioning implementation introduced in glibc 2.1 is described here and is an extension of Sun's symbol versioning scheme described here.

One option is to statically link your binary. This is probably the easiest option.

You could also build your binary in a chroot build environment, or using a glibc-new => glibc-old cross-compiler.

According to the http://www.trevorpounds.com blog post Linking to Older Versioned Symbols (glibc), it is possible to to force any symbol to be linked against an older one so long as it is valid by using the same .symver pseudo-op that is used for defining versioned symbols in the first place. The following example is excerpted from the blog post.

The following example makes use of glibc’s realpath, but makes sure it is linked against an older 2.2.5 version.

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

__asm__(".symver realpath,realpath@GLIBC_2.2.5");
int main()
{
    const char* unresolved = "/lib64";
    char resolved[PATH_MAX+1];

    if(!realpath(unresolved, resolved))
        { return 1; }

    printf("%s\n", resolved);

    return 0;
}
终陌 2024-09-09 03:23:41

设置 1:在没有专用 GCC 的情况下编译您自己的 glibc 并使用它

由于仅使用符号版本控制 hack 似乎不可能做到这一点,让我们更进一步,自己编译 glibc。

此设置可能有效并且速度很快,因为它不会重新编译整个 GCC 工具链,而只是 glibc。

但它并不可靠,因为它使用 glibc 提供的主机 C 运行时对象,例如 crt1.ocrti.ocrtn.o 。这在以下位置提到: https://sourceware.org /glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location 这些对象会进行 glibc 所依赖的早期设置,因此如果事情以奇妙且极其微妙的方式崩溃,我不会感到惊讶。

要获得更可靠的设置,请参阅下面的设置 2。

构建 glibc 并在本地安装:

export glibc_install="$(pwd)/glibc/build/install"

git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28
mkdir build
cd build
../configure --prefix "$glibc_install"
make -j `nproc`
make install -j `nproc`

设置 1:验证构建

test_glibc.c

#define _GNU_SOURCE
#include <assert.h>
#include <gnu/libc-version.h>
#include <stdatomic.h>
#include <stdio.h>
#include <threads.h>

atomic_int acnt;
int cnt;

int f(void* thr_data) {
    for(int n = 0; n < 1000; ++n) {
        ++cnt;
        ++acnt;
    }
    return 0;
}

int main(int argc, char **argv) {
    /* Basic library version check. */
    printf("gnu_get_libc_version() = %s\n", gnu_get_libc_version());

    /* Exercise thrd_create from -pthread,
     * which is not present in glibc 2.27 in Ubuntu 18.04.
     * https://stackoverflow.com/questions/56810/how-do-i-start-threads-in-plain-c/52453291#52453291 */
    thrd_t thr[10];
    for(int n = 0; n < 10; ++n)
        thrd_create(&thr[n], f, NULL);
    for(int n = 0; n < 10; ++n)
        thrd_join(thr[n], NULL);
    printf("The atomic counter is %u\n", acnt);
    printf("The non-atomic counter is %u\n", cnt);
}

使用 test_glibc.sh 编译并运行:

#!/usr/bin/env bash
set -eux
gcc \
  -L "${glibc_install}/lib" \
  -I "${glibc_install}/include" \
  -Wl,--rpath="${glibc_install}/lib" \
  -Wl,--dynamic-linker="${glibc_install}/lib/ld-linux-x86-64.so.2" \
  -std=c11 \
  -o test_glibc.out \
  -v \
  test_glibc.c \
  -pthread \
;
ldd ./test_glibc.out
./test_glibc.out

程序输出预期的:

gnu_get_libc_version() = 2.28
The atomic counter is 10000
The non-atomic counter is 8674

命令改编自 https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21 #Compile_against_glibc_in_an_installed_location--sysroot 导致它失败:

cannot find /home/ciro/glibc/build/install/lib/libc.so.6 inside /home/ciro/glibc/build/install

所以我删除了它。

ldd 输出确认我们刚刚构建的 ldd 和库实际上正在按预期使用:

+ ldd test_glibc.out
        linux-vdso.so.1 (0x00007ffe4bfd3000)
        libpthread.so.0 => /home/ciro/glibc/build/install/lib/libpthread.so.0 (0x00007fc12ed92000)
        libc.so.6 => /home/ciro/glibc/build/install/lib/libc.so.6 (0x00007fc12e9dc000)
        /home/ciro/glibc/build/install/lib/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007fc12f1b3000)

gcc 编译调试输出显示我的使用了主机运行时对象,这很糟糕,如前所述,但我不知道如何解决它,例如它包含:

COLLECT_GCC_OPTIONS=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o

设置 1:修改 glibc

现在让我们修改 glibc:

diff --git a/nptl/thrd_create.c b/nptl/thrd_create.c
index 113ba0d93e..b00f088abb 100644
--- a/nptl/thrd_create.c
+++ b/nptl/thrd_create.c
@@ -16,11 +16,14 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */

+#include <stdio.h>
+
 #include "thrd_priv.h"

 int
 thrd_create (thrd_t *thr, thrd_start_t func, void *arg)
 {
+  puts("hacked");
   _Static_assert (sizeof (thr) == sizeof (pthread_t),
                   "sizeof (thr) != sizeof (pthread_t)");

然后重新编译并重新安装 glibc,然后重新编译并重新运行我们的程序:

cd glibc/build
make -j `nproc`
make -j `nproc` install
./test_glibc.sh

我们看到 hacked 按预期打印了几次。

这进一步证实了我们实际上使用了我们编译的 glibc 而不是主机。

在 Ubuntu 18.04 上测试。

设置 2:crosstool-NG 原始设置

这是设置 1 的替代方案,它是我迄今为止实现的最正确的设置:据我观察,一切都是正确的,包括 C 运行时对象,例如 crt1 .ocrti.ocrtn.o

在此设置中,我们将编译一个完整的专用 GCC 工具链,该工具链使用我们想要的 glibc。

这种方法的唯一缺点是构建时间会更长。但我不会冒险用更少的东西来进行生产设置。

crosstool-NG 是一组脚本,可以为我们从源代码下载和编译所有内容,包括 GCC、glibc 和 binutils。

是的,GCC 构建系统太糟糕了,我们需要一个单独的项目。

此设置并不完美,因为 crosstool-NG 不支持在没有额外内容的情况下构建可执行文件-Wl 标志,这感觉很奇怪,因为我们已经构建了 GCC 本身。但一切似乎都正常,所以这只是一个不便。

获取 crosstool-NG 并配置它:

git clone https://github.com/crosstool-ng/crosstool-ng
cd crosstool-ng
git checkout a6580b8e8b55345a5a342b5bd96e42c83e640ac5
export CT_PREFIX="$(pwd)/.build/install"
export PATH="/usr/lib/ccache:${PATH}"
./bootstrap
./configure --enable-local
make -j `nproc`
./ct-ng x86_64-unknown-linux-gnu
./ct-ng menuconfig

我能看到的唯一强制选项是使其与您的主机内核版本匹配以使用正确的内核标头。使用以下命令查找您的主机内核版本:

uname -a

它显示:

4.15.0-34-generic

所以在 menuconfig 中我这样做:

  • 操作系统
    • Linux 版本

所以我选择:

4.14.71

这是第一个相同或更旧的版本。它必须更旧,因为内核是向后兼容的。

现在您可以使用以下命令进行构建:

env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`

然后等待大约三十分钟到两个小时进行编译。

设置 2:可选配置

我们使用 ./ct-ng x86_64-unknown-linux-gnu 生成的 .config 具有:

CT_GLIBC_V_2_27=y

要更改它,请在 menuconfig< /code> do:

  • C-library
  • glibc 版本

保存 .config,然后继续构建。

或者,如果您想使用自己的 glibc 源,例如使用最新 git 中的 glibc,请继续 像这样

  • 路径和其他选项
    • 尝试标记为“实验”的功能:设置为 true
  • C-library
    • glibc 来源
      • 自定义位置:说是
      • 自定义位置
        • 自定义源位置:指向包含 glibc 源的目录

,其中 glibc 被克隆为:

git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28

设置 2:测试它

一旦您构建了所需的工具链,请使用以下命令进行测试:

#!/usr/bin/env bash
set -eux
install_dir="${CT_PREFIX}/x86_64-unknown-linux-gnu"
PATH="${PATH}:${install_dir}/bin" \
  x86_64-unknown-linux-gnu-gcc \
  -Wl,--dynamic-linker="${install_dir}/x86_64-unknown-linux-gnu/sysroot/lib/ld-linux-x86-64.so.2" \
  -Wl,--rpath="${install_dir}/x86_64-unknown-linux-gnu/sysroot/lib" \
  -v \
  -o test_glibc.out \
  test_glibc.c \
  -pthread \
;
ldd test_glibc.out
./test_glibc.out

一切似乎都正常与设置 1 一样,只不过现在使用了正确的运行时对象:

COLLECT_GCC_OPTIONS=/home/ciro/crosstool-ng/.build/install/x86_64-unknown-linux-gnu/bin/../x86_64-unknown-linux-gnu/sysroot/usr/lib/../lib64/crt1.o

设置 2:高效 glibc 重新编译尝试失败

使用 crosstool-NG 似乎不可能,如下所述。

如果你只是重新构建;

env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`

那么您对自定义 glibc 源位置的更改就会被考虑在内,但它会从头开始构建所有内容,使其无法用于迭代开发。

如果我们这样做:

./ct-ng list-steps

它很好地概述了构建步骤:

Available build steps, in order:
  - companion_tools_for_build
  - companion_libs_for_build
  - binutils_for_build
  - companion_tools_for_host
  - companion_libs_for_host
  - binutils_for_host
  - cc_core_pass_1
  - kernel_headers
  - libc_start_files
  - cc_core_pass_2
  - libc
  - cc_for_build
  - cc_for_host
  - libc_post_cc
  - companion_libs_for_target
  - binutils_for_target
  - debug
  - test_suite
  - finish
Use "<step>" as action to execute only that step.
Use "+<step>" as action to execute up to that step.
Use "<step>+" as action to execute from that step onward.

因此,我们看到 glibc 步骤与多个 GCC 步骤交织在一起,最值得注意的是 libc_start_files 出现在 cc_core_pass_2 之前,这可能是与 cc_core_pass_1 一起使用时最昂贵的步骤。

为了仅构建一个步骤,您必须首先在初始构建的 .config 选项中设置“保存中间步骤”:

  • 路径和其他选项
    • 调试 crosstool-NG
      • 保存中间步骤

,然后您可以尝试:

env -u LD_LIBRARY_PATH time ./ct-ng libc+ -j`nproc`

但不幸的是,需要的+如所述:https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536

但请注意,在中间步骤重新启动会将安装目录重置为该步骤期间的状态。即,您将有一个重建的 libc - 但没有使用此 libc 构建的最终编译器(因此,也没有像 libstdc++ 这样的编译器库)。

基本上仍然使重建太慢而无法进行开发,并且我不知道如何在不修补 crosstool-NG 的情况下克服这个问题。

此外,从 libc 步骤开始似乎没有从 自定义源位置 再次复制源,进一步使该方法无法使用。

额外奖励:stdlibc++

如果您也对 C++ 标准库感兴趣,那么额外奖励:如何编辑和重新构建 GCC libstdc++ C++ 标准库源?

Setup 1: compile your own glibc without dedicated GCC and use it

Since it seems impossible to do just with symbol versioning hacks, let's go one step further and compile glibc ourselves.

This setup might work and is quick as it does not recompile the whole GCC toolchain, just glibc.

But it is not reliable as it uses host C runtime objects such as crt1.o, crti.o, and crtn.o provided by glibc. This is mentioned at: https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location Those objects do early setup that glibc relies on, so I wouldn't be surprised if things crashed in wonderful and awesomely subtle ways.

For a more reliable setup, see Setup 2 below.

Build glibc and install locally:

export glibc_install="$(pwd)/glibc/build/install"

git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28
mkdir build
cd build
../configure --prefix "$glibc_install"
make -j `nproc`
make install -j `nproc`

Setup 1: verify the build

test_glibc.c

#define _GNU_SOURCE
#include <assert.h>
#include <gnu/libc-version.h>
#include <stdatomic.h>
#include <stdio.h>
#include <threads.h>

atomic_int acnt;
int cnt;

int f(void* thr_data) {
    for(int n = 0; n < 1000; ++n) {
        ++cnt;
        ++acnt;
    }
    return 0;
}

int main(int argc, char **argv) {
    /* Basic library version check. */
    printf("gnu_get_libc_version() = %s\n", gnu_get_libc_version());

    /* Exercise thrd_create from -pthread,
     * which is not present in glibc 2.27 in Ubuntu 18.04.
     * https://stackoverflow.com/questions/56810/how-do-i-start-threads-in-plain-c/52453291#52453291 */
    thrd_t thr[10];
    for(int n = 0; n < 10; ++n)
        thrd_create(&thr[n], f, NULL);
    for(int n = 0; n < 10; ++n)
        thrd_join(thr[n], NULL);
    printf("The atomic counter is %u\n", acnt);
    printf("The non-atomic counter is %u\n", cnt);
}

Compile and run with test_glibc.sh:

#!/usr/bin/env bash
set -eux
gcc \
  -L "${glibc_install}/lib" \
  -I "${glibc_install}/include" \
  -Wl,--rpath="${glibc_install}/lib" \
  -Wl,--dynamic-linker="${glibc_install}/lib/ld-linux-x86-64.so.2" \
  -std=c11 \
  -o test_glibc.out \
  -v \
  test_glibc.c \
  -pthread \
;
ldd ./test_glibc.out
./test_glibc.out

The program outputs the expected:

gnu_get_libc_version() = 2.28
The atomic counter is 10000
The non-atomic counter is 8674

Command adapted from https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location but --sysroot made it fail with:

cannot find /home/ciro/glibc/build/install/lib/libc.so.6 inside /home/ciro/glibc/build/install

so I removed it.

ldd output confirms that the ldd and libraries that we've just built are actually being used as expected:

+ ldd test_glibc.out
        linux-vdso.so.1 (0x00007ffe4bfd3000)
        libpthread.so.0 => /home/ciro/glibc/build/install/lib/libpthread.so.0 (0x00007fc12ed92000)
        libc.so.6 => /home/ciro/glibc/build/install/lib/libc.so.6 (0x00007fc12e9dc000)
        /home/ciro/glibc/build/install/lib/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007fc12f1b3000)

The gcc compilation debug output shows that my host runtime objects were used, which is bad as mentioned previously, but I don't know how to work around it, e.g. it contains:

COLLECT_GCC_OPTIONS=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o

Setup 1: modify glibc

Now let's modify glibc with:

diff --git a/nptl/thrd_create.c b/nptl/thrd_create.c
index 113ba0d93e..b00f088abb 100644
--- a/nptl/thrd_create.c
+++ b/nptl/thrd_create.c
@@ -16,11 +16,14 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */

+#include <stdio.h>
+
 #include "thrd_priv.h"

 int
 thrd_create (thrd_t *thr, thrd_start_t func, void *arg)
 {
+  puts("hacked");
   _Static_assert (sizeof (thr) == sizeof (pthread_t),
                   "sizeof (thr) != sizeof (pthread_t)");

Then recompile and re-install glibc, and recompile and re-run our program:

cd glibc/build
make -j `nproc`
make -j `nproc` install
./test_glibc.sh

and we see hacked printed a few times as expected.

This further confirms that we actually used the glibc that we compiled and not the host one.

Tested on Ubuntu 18.04.

Setup 2: crosstool-NG pristine setup

This is an alternative to setup 1, and it is the most correct setup I've achieved far: everything is correct as far as I can observe, including the C runtime objects such as crt1.o, crti.o, and crtn.o.

In this setup, we will compile a full dedicated GCC toolchain that uses the glibc that we want.

The only downside to this method is that the build will take longer. But I wouldn't risk a production setup with anything less.

crosstool-NG is a set of scripts that downloads and compiles everything from source for us, including GCC, glibc and binutils.

Yes the GCC build system is so bad that we need a separate project for that.

This setup is only not perfect because crosstool-NG does not support building the executables without extra -Wl flags, which feels weird since we've built GCC itself. But everything seems to work, so this is only an inconvenience.

Get crosstool-NG and configure it:

git clone https://github.com/crosstool-ng/crosstool-ng
cd crosstool-ng
git checkout a6580b8e8b55345a5a342b5bd96e42c83e640ac5
export CT_PREFIX="$(pwd)/.build/install"
export PATH="/usr/lib/ccache:${PATH}"
./bootstrap
./configure --enable-local
make -j `nproc`
./ct-ng x86_64-unknown-linux-gnu
./ct-ng menuconfig

The only mandatory option that I can see, is making it match your host kernel version to use the correct kernel headers. Find your host kernel version with:

uname -a

which shows me:

4.15.0-34-generic

so in menuconfig I do:

  • Operating System
    • Version of linux

so I select:

4.14.71

which is the first equal or older version. It has to be older since the kernel is backwards compatible.

Now you can build with:

env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`

and now wait for about thirty minutes to two hours for compilation.

Setup 2: optional configurations

The .config that we generated with ./ct-ng x86_64-unknown-linux-gnu has:

CT_GLIBC_V_2_27=y

To change that, in menuconfig do:

  • C-library
  • Version of glibc

save the .config, and continue with the build.

Or, if you want to use your own glibc source, e.g. to use glibc from the latest git, proceed like this:

  • Paths and misc options
    • Try features marked as EXPERIMENTAL: set to true
  • C-library
    • Source of glibc
      • Custom location: say yes
      • Custom location
        • Custom source location: point to a directory containing your glibc source

where glibc was cloned as:

git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28

Setup 2: test it out

Once you have built he toolchain that you want, test it out with:

#!/usr/bin/env bash
set -eux
install_dir="${CT_PREFIX}/x86_64-unknown-linux-gnu"
PATH="${PATH}:${install_dir}/bin" \
  x86_64-unknown-linux-gnu-gcc \
  -Wl,--dynamic-linker="${install_dir}/x86_64-unknown-linux-gnu/sysroot/lib/ld-linux-x86-64.so.2" \
  -Wl,--rpath="${install_dir}/x86_64-unknown-linux-gnu/sysroot/lib" \
  -v \
  -o test_glibc.out \
  test_glibc.c \
  -pthread \
;
ldd test_glibc.out
./test_glibc.out

Everything seems to work as in Setup 1, except that now the correct runtime objects were used:

COLLECT_GCC_OPTIONS=/home/ciro/crosstool-ng/.build/install/x86_64-unknown-linux-gnu/bin/../x86_64-unknown-linux-gnu/sysroot/usr/lib/../lib64/crt1.o

Setup 2: failed efficient glibc recompilation attempt

It does not seem possible with crosstool-NG, as explained below.

If you just re-build;

env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`

then your changes to the custom glibc source location are taken into account, but it builds everything from scratch, making it unusable for iterative development.

If we do:

./ct-ng list-steps

it gives a nice overview of the build steps:

Available build steps, in order:
  - companion_tools_for_build
  - companion_libs_for_build
  - binutils_for_build
  - companion_tools_for_host
  - companion_libs_for_host
  - binutils_for_host
  - cc_core_pass_1
  - kernel_headers
  - libc_start_files
  - cc_core_pass_2
  - libc
  - cc_for_build
  - cc_for_host
  - libc_post_cc
  - companion_libs_for_target
  - binutils_for_target
  - debug
  - test_suite
  - finish
Use "<step>" as action to execute only that step.
Use "+<step>" as action to execute up to that step.
Use "<step>+" as action to execute from that step onward.

therefore, we see that there are glibc steps intertwined with several GCC steps, most notably libc_start_files comes before cc_core_pass_2, which is likely the most expensive step together with cc_core_pass_1.

In order to build just one step, you must first set the "Save intermediate steps" in .config option for the intial build:

  • Paths and misc options
    • Debug crosstool-NG
      • Save intermediate steps

and then you can try:

env -u LD_LIBRARY_PATH time ./ct-ng libc+ -j`nproc`

but unfortunately, the + required as mentioned at: https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536

Note however that restarting at an intermediate step resets the installation directory to the state it had during that step. I.e., you will have a rebuilt libc - but no final compiler built with this libc (and hence, no compiler libraries like libstdc++ either).

and basically still makes the rebuild too slow to be feasible for development, and I don't see how to overcome this without patching crosstool-NG.

Furthermore, starting from the libc step didn't seem to copy over the source again from Custom source location, further making this method unusable.

Bonus: stdlibc++

A bonus if you're also interested in the C++ standard library: How to edit and re-build the GCC libstdc++ C++ standard library source?

纵情客 2024-09-09 03:23:41

使用-static链接。当您使用-static链接时,链接器将库嵌入到可执行文件中,因此可执行文件会更大,但它可以在具有旧版本glibc的系统上执行,因为程序将使用它自己的库而不是系统的库。

Link with -static. When you link with -static the linker embeds the library inside the executable, so the executable will be bigger, but it can be executed on a system with an older version of glibc because the program will use it's own library instead of that of the system.

雪花飘飘的天空 2024-09-09 03:23:41

在我看来,最懒的解决方案(特别是如果您不依赖最新的前沿 C/C++ 功能或最新的编译器功能)尚未提及,所以这里是:

只需在系统上构建您仍然想支持最古老的 GLIBC。

如今,使用 chroot、KVM/Virtualbox 或 docker 等技术实际上很容易做到这一点,即使您真的不想直接在任何操作系统上使用这样的旧发行版。个人电脑。详细来说,为了使您的软件具有最大的可移植性,我建议您遵循以下步骤:

  1. 只需选择您的沙箱/虚拟化/...任何毒药,然后使用它为自己获取一个虚拟的较旧的 Ubuntu LTS 并使用默认情况下,gcc/g++ 就在那里。这会自动将您的 GLIBC 限制为该环境中可用的 GLIBC。

  2. 避免依赖于基础库之外的外部库:例如,您应该动态链接底层系统内容,例如 glibc、libGL、libxcb/X11/wayland 等、libasound/libpulseaudio,如果您使用的话,可能还需要 GTK+,但否则最好使用 GTK+如果可以的话,静态链接外部库/运送它们。特别是大多数独立的库,如图像加载器、多媒体解码器等,如果您静态发布它们,则可以减少其他发行版上的损坏(例如,如果仅存在于不同的主要版本中的某个位置,则可能会导致损坏)。

通过这种方法,您可以获得旧的 GLIBC 兼容二进制文件,无需任何手动符号调整,无需执行完全静态的二进制文件(这可能会破坏更复杂的程序,因为 glibc 讨厌这样做,这可能会导致您的许可问题),并且无需设置任何自定义工具链、任何自定义 glibc 副本或其他任何内容。

In my opinion, the laziest solution (especially if you don't rely on latest bleeding edge C/C++ features, or latest compiler features) wasn't mentioned yet, so here it is:

Just build on the system with the oldest GLIBC you still want to support.

This is actually pretty easy to do nowadays with technologies like chroot, or KVM/Virtualbox, or docker, even if you don't really want to use such an old distro directly on any pc. In detail, to make a maximum portable binary of your software I recommend following these steps:

  1. Just pick your poison of sandbox/virtualization/... whatever, and use it to get yourself a virtual older Ubuntu LTS and compile with the gcc/g++ it has in there by default. That automatically limits your GLIBC to the one available in that environment.

  2. Avoid depending on external libs outside of foundational ones: like, you should dynamically link ground-level system stuff like glibc, libGL, libxcb/X11/wayland things, libasound/libpulseaudio, possibly GTK+ if you use that, but otherwise preferrably statically link external libs/ship them along if you can. Especially mostly self-contained libs like image loaders, multimedia decoders, etc can cause less breakage on other distros (breakage can be caused e.g. if only present somewhere in a different major version) if you statically ship them.

With that approach you get an old-GLIBC-compatible binary without any manual symbol tweaks, without doing a fully static binary (that may break for more complex programs because glibc hates that, and which may cause licensing issues for you), and without setting up any custom toolchain, any custom glibc copy, or whatever.

凡尘雨 2024-09-09 03:23:41

使用 zig cc< /a> 作为您的编译器,可以针对特定版本的 glibc。

例如,以glibc 2.5为目标,

zig cc -target x86_64-linux-gnu.2.5 hello.c -o hello

这也适用于使用zig c++的C++

zig c++ -target x86_64-linux-gnu.2.7 hello.cpp -o hello-cpp

zig cc基于clang的顶部,所以它是一个漂亮的如果您使用 clang 或 gcc,则替换率接近下降。

Using zig cc as your compiler, it's possible to target a specific version of glibc.

For example, to target glibc 2.5

zig cc -target x86_64-linux-gnu.2.5 hello.c -o hello

This also works for C++ too using zig c++

zig c++ -target x86_64-linux-gnu.2.7 hello.cpp -o hello-cpp

zig cc is based on top of clang, so it's a pretty close to a drop in replacement if you are using either clang or gcc.

鹊巢 2024-09-09 03:23:41

另一种方法是丢弃版本信息并让链接器默认为它所拥有的任何版本。

为此,您可能需要查看PatchELF1

$ nm --dynamic --undefined-only --with-symbol-versions MyLib.so \
  | grep GLIBC | sed -e 's#.\+@##' | sort --unique
GLIBC_2.17
GLIBC_2.29
$ nm --dynamic --undefined-only --with-symbol-versions MyLib.so | grep GLIBC_2.29
                 U exp@GLIBC_2.29
                 U log@GLIBC_2.29
                 U log2@GLIBC_2.29
                 U pow@GLIBC_2.29
$ patchelf --clear-symbol-version exp   \
           --clear-symbol-version log   \
           --clear-symbol-version log2  \
           --clear-symbol-version pow   MyLib.so

这非常如果您手头没有源代码(或者很难理解代码2),这很有用。

1 尽管 patchelf 在许多发行版上都可用,但它们很可能已经过时。
--clear-symbol-version 标志是在 0.12 版本中添加的,不幸的是,它并没有完全删除版本要求。
在合并之前,您需要从此合并请求手动编译。

2仅供参考,我正在交叉编译LuaJIT

An alternative is to just discard the version info and let the linker default to whatever version it has.

To do this, you may want to check out PatchELF1:

$ nm --dynamic --undefined-only --with-symbol-versions MyLib.so \
  | grep GLIBC | sed -e 's#.\+@##' | sort --unique
GLIBC_2.17
GLIBC_2.29
$ nm --dynamic --undefined-only --with-symbol-versions MyLib.so | grep GLIBC_2.29
                 U exp@GLIBC_2.29
                 U log@GLIBC_2.29
                 U log2@GLIBC_2.29
                 U pow@GLIBC_2.29
$ patchelf --clear-symbol-version exp   \
           --clear-symbol-version log   \
           --clear-symbol-version log2  \
           --clear-symbol-version pow   MyLib.so

This is extremely useful if you don't have the source at hand (or have a hard time understanding the code2).

1 Although patchelf is available on many distributions, they are very likely to be outdated.
The --clear-symbol-version flag was added in version 0.12, which, unfortunately, does not fully remove version requirements.
You will need to compile manually from this merge request before it gets merged.

2 FYI, I was cross-compiling LuaJIT.

生死何惧 2024-09-09 03:23:41

这个仓库:
https://github.com/wheybags/glibc_version_header

提供了一个头文件,负责处理中描述的详细信息接受的答案。

基本上:

  1. 下载要链接的相应 GCC 的 标头
  2. 添加 -include /path/to/header.h 到您的编译器标志中
  3. 如果您要链接 pthread,您可能还需要添加 -D_REENTRANT

我还添加了链接器标志:
-static-libgcc -static-libstdc++ -pthread

但这些取决于您的应用程序的要求。

This repo:
https://github.com/wheybags/glibc_version_header

provides a header file that takes care of the details described in the accepted answer.

Basically:

  1. Download the header of the corresponding GCC you want to link against
  2. Add -include /path/to/header.h to your compiler flags
  3. You may also need to add -D_REENTRANT if you're linking pthread

I also add the linker flags:
-static-libgcc -static-libstdc++ -pthread

But those are dependent on your app's requirements.

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