单个主机上的多个 glibc 库

发布于 2024-07-19 15:58:55 字数 1428 浏览 6 评论 0原文

我的 Linux (SLES-8) 服务器当前有 glibc-2.2.5-235,但我有一个程序无法在此版本上运行并且需要 glibc-2.3.3。

是否可以在同一主机上安装多个 glibc?

这是我在旧 glibc 上运行程序时遇到的错误:

./myapp: /lib/i686/libc.so.6: version `GLIBC_2.3' not found (required by ./myapp)
./myapp: /lib/i686/libpthread.so.0: version `GLIBC_2.3.2' not found (required by ./myapp)
./myapp: /lib/i686/libc.so.6: version `GLIBC_2.3' not found (required by ./libxerces-c.so.27)
./myapp: /lib/ld-linux.so.2: version `GLIBC_2.3' not found (required by ./libstdc++.so.6)
./myapp: /lib/i686/libc.so.6: version `GLIBC_2.3' not found (required by ./libstdc++.so.6)

所以我创建了一个名为 newglibc 的新目录,并将以下文件复制到:

libpthread.so.0
libm.so.6
libc.so.6
ld-2.3.3.so
ld-linux.so.2 -> ld-2.3.3.so

export LD_LIBRARY_PATH=newglibc:$LD_LIBRARY_PATH

但我收到错误:

./myapp: /lib/ld-linux.so.2: version `GLIBC_PRIVATE' not found (required by ./newglibc/libpthread.so.0)
./myapp: /lib/ld-linux.so.2: version `GLIBC_2.3' not found (required by libstdc++.so.6)
./myapp: /lib/ld-linux.so.2: version `GLIBC_PRIVATE' not found (required by ./newglibc/libm.so.6)
./myapp: /lib/ld-linux.so.2: version `GLIBC_2.3' not found (required by ./newglibc/libc.so.6)
./myapp: /lib/ld-linux.so.2: version `GLIBC_PRIVATE' not found (required by ./newglibc/libc.so.6)

所以看来它们仍然链接到 /lib 并且没有从我放置它们的位置拾取它们。

My linux (SLES-8) server currently has glibc-2.2.5-235, but I have a program which won't work on this version and requires glibc-2.3.3.

Is it possible to have multiple glibcs installed on the same host?

This is the error I get when I run my program on the old glibc:

./myapp: /lib/i686/libc.so.6: version `GLIBC_2.3' not found (required by ./myapp)
./myapp: /lib/i686/libpthread.so.0: version `GLIBC_2.3.2' not found (required by ./myapp)
./myapp: /lib/i686/libc.so.6: version `GLIBC_2.3' not found (required by ./libxerces-c.so.27)
./myapp: /lib/ld-linux.so.2: version `GLIBC_2.3' not found (required by ./libstdc++.so.6)
./myapp: /lib/i686/libc.so.6: version `GLIBC_2.3' not found (required by ./libstdc++.so.6)

So I created a new directory called newglibc and copied the following files in:

libpthread.so.0
libm.so.6
libc.so.6
ld-2.3.3.so
ld-linux.so.2 -> ld-2.3.3.so

and

export LD_LIBRARY_PATH=newglibc:$LD_LIBRARY_PATH

But I get an error:

./myapp: /lib/ld-linux.so.2: version `GLIBC_PRIVATE' not found (required by ./newglibc/libpthread.so.0)
./myapp: /lib/ld-linux.so.2: version `GLIBC_2.3' not found (required by libstdc++.so.6)
./myapp: /lib/ld-linux.so.2: version `GLIBC_PRIVATE' not found (required by ./newglibc/libm.so.6)
./myapp: /lib/ld-linux.so.2: version `GLIBC_2.3' not found (required by ./newglibc/libc.so.6)
./myapp: /lib/ld-linux.so.2: version `GLIBC_PRIVATE' not found (required by ./newglibc/libc.so.6)

So it appears that they are still linking to /lib and not picking up from where I put them.

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

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

发布评论

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

评论(12

浅语花开 2024-07-26 15:58:55

首先,每个动态链接程序最重要的依赖就是链接器。 所有so库必须与链接器的版本匹配。

让我们举个简单的例子:我有 newset ubuntu 系统,我在其中运行一些程序(在我的例子中是 D 编译器 - ldc2)。 我想在旧的 CentOS 上运行它,但由于旧的 glibc 库,这是不可能的。 我

ldc2-1.5.0-linux-x86_64/bin/ldc2: /lib64/libc.so.6: version `GLIBC_2.15' not found (required by ldc2-1.5.0-linux-x86_64/bin/ldc2)
ldc2-1.5.0-linux-x86_64/bin/ldc2: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by ldc2-1.5.0-linux-x86_64/bin/ldc2)

必须将所有依赖项从 ubuntu 复制到 centos。
正确的方法如下:

首先,让我们检查所有依赖项:

ldd ldc2-1.5.0-linux-x86_64/bin/ldc2 
    linux-vdso.so.1 =>  (0x00007ffebad3f000)
    librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f965f597000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f965f378000)
    libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f965f15b000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f965ef57000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f965ec01000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f965e9ea000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f965e60a000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f965f79f000)

linux-vdso.so.1 不是一个真正的库,我们不必关心它。

/lib64/ld-linux-x86-64.so.2 是链接器,linux 使用它来将可执行文件与所有动态库链接。

其余文件是真正的库,所有这些文件连同链接器都必须复制到 centos 中的某个位置。

假设所有库和链接器都位于“/mylibs”目录中。

ld-linux-x86-64.so.2 - 正如我已经说过的 - 是链接器。 它不是动态库,而是静态可执行文件。 您可以运行它并看到它甚至有一些参数,例如 --library-path (我将返回它)。

在linux上,动态链接的程序可以仅通过其名称来启动,例如

/bin/ldc2

Linux将此类程序加载到RAM中,并检查为其设置了哪个链接器。 通常,在 64 位系统上,它是 /lib64/ld-linux-x86-64.so.2 (在您的文件系统中,它是指向真正可执行文件的符号链接)。
然后linux运行链接器并加载动态库。

你也可以稍微改变一下,做这样的技巧:

/mylibs/ld-linux-x86-64.so.2 /bin/ldc2

这是强制linux使用特定链接器的方法。

现在我们可以回到前面提到的参数 --library-path

/mylibs/ld-linux-x86-64.so.2 --library-path /mylibs /bin/ldc2

它将运行 ldc2 并从 /mylibs 加载动态库。

这是使用所选(非系统默认)库调用可执行文件的方法。

First of all, the most important dependency of each dynamically linked program is the linker. All so libraries must match the version of the linker.

Let's take simple exaple: I have the newset ubuntu system where I run some program (in my case it is D compiler - ldc2). I'd like to run it on the old CentOS, but because of the older glibc library it is impossible. I got

ldc2-1.5.0-linux-x86_64/bin/ldc2: /lib64/libc.so.6: version `GLIBC_2.15' not found (required by ldc2-1.5.0-linux-x86_64/bin/ldc2)
ldc2-1.5.0-linux-x86_64/bin/ldc2: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by ldc2-1.5.0-linux-x86_64/bin/ldc2)

I have to copy all dependencies from ubuntu to centos.
The proper method is following:

First, let's check all dependencies:

ldd ldc2-1.5.0-linux-x86_64/bin/ldc2 
    linux-vdso.so.1 =>  (0x00007ffebad3f000)
    librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f965f597000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f965f378000)
    libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f965f15b000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f965ef57000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f965ec01000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f965e9ea000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f965e60a000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f965f79f000)

linux-vdso.so.1 is not a real library and we don't have to care about it.

/lib64/ld-linux-x86-64.so.2 is the linker, which is used by the linux do link the executable with all dynamic libraries.

Rest of the files are real libraries and all of them together with the linker must be copied somewhere in the centos.

Let's assume all the libraries and linker are in "/mylibs" directory.

ld-linux-x86-64.so.2 - as I've already said - is the linker. It's not dynamic library but static executable. You can run it and see that it even have some parameters, eg --library-path (I'll return to it).

On the linux, dynamically linked program may be lunched just by its name, eg

/bin/ldc2

Linux loads such program into RAM, and checks which linker is set for it. Usually, on 64-bit system, it is /lib64/ld-linux-x86-64.so.2 (in your filesystem it is symbolic link to the real executable).
Then linux runs the linker and it loads dynamic libraries.

You can also change this a little and do such trick:

/mylibs/ld-linux-x86-64.so.2 /bin/ldc2

It is the method for forcing the linux to use specific linker.

And now we can return to the mentioned earlier parameter --library-path

/mylibs/ld-linux-x86-64.so.2 --library-path /mylibs /bin/ldc2

It will run ldc2 and load dynamic libraries from /mylibs.

This is the method to call the executable with choosen (not system default) libraries.

埋葬我深情 2024-07-26 15:58:55

使用LD_PRELOAD:
将您的库放在 man lib 目录之外的某个位置并运行:

LD_PRELOAD='mylibc.so anotherlib.so' program

请参阅:维基百科文章

Use LD_PRELOAD:
put your library somewhere out of the man lib directories and run:

LD_PRELOAD='mylibc.so anotherlib.so' program

See: the Wikipedia article

我的奇迹 2024-07-26 15:58:55

设置 1:在没有专用 GCC 的情况下编译您自己的 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
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`

构建大约需要三十分钟到两个小时。

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

uname -a

它显示:

4.15.0-34-generic

所以在 menuconfig 中我这样做:

  • 操作系统
    • Linux 版本

所以我选择:

4.14.71

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

设置 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

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, configure and build 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
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`

The build takes about thirty minutes to two hours.

The only mandatory configuration 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.

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-07-26 15:58:55

同一个系统上很可能有多个版本的 glibc(我们每天都这样做)。

但是,您需要知道 glibc 由许多部分(200 多个共享库)组成,所有部分都必须匹配。 其中之一是 ld-linux.so.2,它必须与 libc.so.6 匹配,否则您将看到所看到的错误。

ld-linux.so.2 的绝对路径在链接时被硬编码到可执行文件中,并且在链接完成后不能轻易更改(更新:可以使用 patchelf;请参阅下面的这个答案)。

要构建与新 glibc 一起使用的可执行文件,请执行以下操作:

g++ main.o -o myapp ... \
   -Wl,--rpath=/path/to/newglibc \
   -Wl,--dynamic-linker=/path/to/newglibc/ld-linux.so.2

-rpath 链接器选项将使运行时加载程序在 /path/to/newglibc 中搜索库(所以你不必在运行之前设置LD_LIBRARY_PATH),并且-dynamic-linker选项将“烘焙”路径以正确的ld-linux.so .2 进入应用程序。

如果您无法重新链接 myapp 应用程序(例如,因为它是第三方二进制文件),并非所有内容都会丢失,但会变得更加棘手。 一种解决方案是为其设置适当的 chroot 环境。 另一种可能性是使用 rtldi二进制编辑器

更新:或者您可以在现有二进制文件上使用 patchelf 将它们重定向到备用文件libc。

It is very possible to have multiple versions of glibc on the same system (we do that every day).

However, you need to know that glibc consists of many pieces (200+ shared libraries) which all must match. One of the pieces is ld-linux.so.2, and it must match libc.so.6, or you'll see the errors you are seeing.

The absolute path to ld-linux.so.2 is hard-coded into the executable at link time, and can not be easily changed after the link is done (Update: can be done with patchelf; see this answer below).

To build an executable that will work with the new glibc, do this:

g++ main.o -o myapp ... \
   -Wl,--rpath=/path/to/newglibc \
   -Wl,--dynamic-linker=/path/to/newglibc/ld-linux.so.2

The -rpath linker option will make the runtime loader search for libraries in /path/to/newglibc (so you wouldn't have to set LD_LIBRARY_PATH before running it), and the -dynamic-linker option will "bake" path to correct ld-linux.so.2 into the application.

If you can't relink the myapp application (e.g. because it is a third-party binary), not all is lost, but it gets trickier. One solution is to set a proper chroot environment for it. Another possibility is to use rtldi and a binary editor.

Update: or you can use patchelf on existing binaries to redirect them to the alternate libc.

不弃不离 2024-07-26 15:58:55

当我饶有兴趣地阅读Employed Russian's answer以及msb的回答,我发现了另一个相当简单的方法(在最初提出问题时可能不可用......)

首先,足以 构建而不安装您选择的 glibc 版本。
然后,要使用刚刚构建的 glibc 动态加载器(而不是系统上安装的动态加载器)启动您的应用程序,请使用提到的方法 此处:只需执行 /path/to/glibc-build/testrun.sh /path/to/test/application

As I was reading with a lot of interest Employed Russian's answer as well as msb's answer, I found another quite simple method (which perhaps wasn't available when the question was initially asked...)

First, it is sufficient to build without installing a glibc version of your choice.
Then, to start your app with the glibc dynamic loader that you've just built (rather than the dynamic loaded that's installed on your system), use the approach mentioned here: simply do /path/to/glibc-build/testrun.sh /path/to/test/application

北陌 2024-07-26 15:58:55

我不确定这个问题是否仍然相关,但还有另一种解决问题的方法:Docker。 人们可以安装一个几乎空的源发行版(用于开发的发行版)容器,并将文件复制到容器中。 这样您就不需要创建 chroot 所需的文件系统。

I am not sure that the question is still relevant, but there is another way of fixing the problem: Docker. One can install an almost empty container of the Source Distribution (The Distribution used for development) and copy the files into the Container. That way You do not need to create the filesystem needed for chroot.

凉风有信 2024-07-26 15:58:55

如果仔细查看第二个输出,您可以看到使用了库的新位置。 也许 glibc 中仍然缺少一些库。

我还认为程序使用的所有库都应该针对该版本的 glibc 进行编译。 如果您有权访问该程序的源代码,那么重新编译似乎是最好的解决方案。

If you look closely at the second output you can see that the new location for the libraries is used. Maybe there are still missing libraries that are part of the glibc.

I also think that all the libraries used by your program should be compiled against that version of glibc. If you have access to the source code of the program, a fresh compilation appears to be the best solution.

不气馁 2024-07-26 15:58:55

“受雇的俄罗斯人”是最好的答案之一,我认为所有其他建议的答案可能都行不通。 原因很简单,因为当应用程序首次创建时,其所需的所有 API 都在编译时解析。 使用“ldd”你可以看到所有静态链接的依赖项:

ldd /usr/lib/firefox/firefox
    linux-vdso.so.1 =>  (0x00007ffd5c5f0000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f727e708000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f727e500000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f727e1f8000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f727def0000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f727db28000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f727eb78000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f727d910000)

但是在运行时,firefox还将加载许多其他动态库,例如(对于firefox)加载了许多“glib”标记的库(即使静态链接没有) :

 /usr/lib/x86_64-linux-gnu/libdbus-glib-1.so.2.2.2
 /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0
 /usr/lib/x86_64-linux-gnu/libavahi-glib.so.1.0.2

很多时候,您可以看到一个版本的名称被软链接到另一版本。 例如:

lrwxrwxrwx 1 root root     23 Dec 21  2014 libdbus-glib-1.so.2 -> libdbus-glib-1.so.2.2.2
-rw-r--r-- 1 root root 160832 Mar  1  2013 libdbus-glib-1.so.2.2.2

这意味着一个系统中存在不同版本的“库”——这不是问题,因为它是同一个文件,并且当应用程序具有多个版本依赖性时它将提供兼容性。

因此,在系统级别,所有库几乎都是相互依赖的,仅仅通过操作 LD_PRELOAD 或 LD_LIBRARY_PATH 来更改库加载优先级将无济于事 - 即使它可以加载,运行时它仍然可能崩溃。

http://lightofdawn.org/wiki/wiki.cgi/-wiki/NewAppsOnOldGlibc

最好的替代方案是 chroot(ER 简要提到过):但为此,您需要重新创建原始二进制执行的整个环境 - 通常从 /lib、/usr/lib/、/usr/lib/x86 开始您可以使用“Buildroot”或 YoctoProject,或者仅从现有的发行版环境中打包。 (如 Fedora/Suse 等)。

"Employed Russian" is among the best answer, and I think all other suggested answer may not work. The reason is simply because when an application is first created, all its the APIs it needs are resolved at compile time. Using "ldd" u can see all the statically linked dependencies:

ldd /usr/lib/firefox/firefox
    linux-vdso.so.1 =>  (0x00007ffd5c5f0000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f727e708000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f727e500000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f727e1f8000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f727def0000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f727db28000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f727eb78000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f727d910000)

But at runtime, firefox will also load many other dynamic libraries, eg (for firefox) there are many "glib"-labelled libraries loaded (even though statically linked there are none):

 /usr/lib/x86_64-linux-gnu/libdbus-glib-1.so.2.2.2
 /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0
 /usr/lib/x86_64-linux-gnu/libavahi-glib.so.1.0.2

Manytimes, you can see names of one version being soft-linked into another version. Eg:

lrwxrwxrwx 1 root root     23 Dec 21  2014 libdbus-glib-1.so.2 -> libdbus-glib-1.so.2.2.2
-rw-r--r-- 1 root root 160832 Mar  1  2013 libdbus-glib-1.so.2.2.2

This therefore means different version of "libraries" exists in one system - which is not a problem as it is the same file, and it will provide compatibilities when applications have multiple versions dependencies.

Therefore, at the system level, all the libraries are almost interdependent on one another, and just changing the libraries loading priority via manipulating LD_PRELOAD or LD_LIBRARY_PATH will not help - even it can load, runtime it may still crash.

http://lightofdawn.org/wiki/wiki.cgi/-wiki/NewAppsOnOldGlibc

Best alternative is chroot (mentioned by ER briefly): but for this you will need to recreate the entire environment in which is the original binary execute - usually starting from /lib, /usr/lib/, /usr/lib/x86 etc. You can either use "Buildroot", or YoctoProject, or just tar from an existing Distro environment. (like Fedora/Suse etc).

如果没结果 2024-07-26 15:58:55

当我想在 Ubuntu Precision (glibc-2.15) 上运行 chromium 浏览器时,我得到了
(典型)消息“...libc.so.6:未找到版本‘GLIBC_2.19'...”。
我考虑到这样一个事实:文件并不是永久需要的,而只是开始时需要的。
所以我收集了浏览器和sudo所需的文件并创建了一个mini-glibc-2.19-
环境,启动浏览器,然后将原始文件复制回来
再次。 需要的文件在RAM中,和原来的glibc是一样的。

as root
the files (*-2.15.so) already exist 

mkdir -p /glibc-2.19/i386-linux-gnu

/glibc-2.19/ld-linux.so.2 -> /glibc-2.19/i386-linux-gnu/ld-2.19.so
/glibc-2.19/i386-linux-gnu/libc.so.6 -> libc-2.19.so
/glibc-2.19/i386-linux-gnu/libdl.so.2 -> libdl-2.19.so
/glibc-2.19/i386-linux-gnu/libpthread.so.0 -> libpthread-2.19.so

mkdir -p /glibc-2.15/i386-linux-gnu

/glibc-2.15/ld-linux.so.2 -> (/glibc-2.15/i386-linux-gnu/ld-2.15.so)
/glibc-2.15/i386-linux-gnu/libc.so.6 -> (libc-2.15.so)
/glibc-2.15/i386-linux-gnu/libdl.so.2 -> (libdl-2.15.so)
/glibc-2.15/i386-linux-gnu/libpthread.so.0 -> (libpthread-2.15.so)

运行浏览器的脚本:

#!/bin/sh
sudo cp -r /glibc-2.19/* /lib
/path/to/the/browser &
sleep 1
sudo cp -r /glibc-2.15/* /lib
sudo rm -r /lib/i386-linux-gnu/*-2.19.so

When I wanted to run a chromium-browser on Ubuntu precise (glibc-2.15), I got the
(typical) message "...libc.so.6: version `GLIBC_2.19' not found...".
I considered the fact, that files are not needed permamently, but only for start.
So I collected the files needed for the browser and sudo and created a mini-glibc-2.19-
environment, started the browser and then copied the original files back
again. The needed files are in RAM and the original glibc is the same.

as root
the files (*-2.15.so) already exist 

mkdir -p /glibc-2.19/i386-linux-gnu

/glibc-2.19/ld-linux.so.2 -> /glibc-2.19/i386-linux-gnu/ld-2.19.so
/glibc-2.19/i386-linux-gnu/libc.so.6 -> libc-2.19.so
/glibc-2.19/i386-linux-gnu/libdl.so.2 -> libdl-2.19.so
/glibc-2.19/i386-linux-gnu/libpthread.so.0 -> libpthread-2.19.so

mkdir -p /glibc-2.15/i386-linux-gnu

/glibc-2.15/ld-linux.so.2 -> (/glibc-2.15/i386-linux-gnu/ld-2.15.so)
/glibc-2.15/i386-linux-gnu/libc.so.6 -> (libc-2.15.so)
/glibc-2.15/i386-linux-gnu/libdl.so.2 -> (libdl-2.15.so)
/glibc-2.15/i386-linux-gnu/libpthread.so.0 -> (libpthread-2.15.so)

the script to run the browser:

#!/bin/sh
sudo cp -r /glibc-2.19/* /lib
/path/to/the/browser &
sleep 1
sudo cp -r /glibc-2.15/* /lib
sudo rm -r /lib/i386-linux-gnu/*-2.19.so
眼眸里的快感 2024-07-26 15:58:55

@msb 提供了一个安全的解决方案。

当我在只有 glibc-2.12CentOS 6.5 的 conda 环境中 import tensorflow as tf 时,我遇到了这个问题。

ImportError: /lib64/libc.so.6: version `GLIBC_2.16' not found (required by /home/

我想提供一些细节:

首先将 glibc 安装到您的主目录:

mkdir ~/glibc-install; cd ~/glibc-install
wget http://ftp.gnu.org/gnu/glibc/glibc-2.17.tar.gz
tar -zxvf glibc-2.17.tar.gz
cd glibc-2.17
mkdir build
cd build
../configure --prefix=/home/myself/opt/glibc-2.17  # <-- where you install new glibc
make -j<number of CPU Cores>  # You can find your <number of CPU Cores> by using **nproc** command
make install

其次,按照相同的方式安装 补丁

第三,修补你的Python:

[myself@nfkd ~]$ patchelf --set-interpreter /home/myself/opt/glibc-2.17/lib/ld-linux-x86-64.so.2 --set-rpath /home/myself/opt/glibc-2.17/lib/ /home/myself/miniconda3/envs/tensorflow/bin/python

正如@msb提到的,

现在我可以在CentOS 6.5中使用tensorflow-2.0 alpha

参考: https://serverkurma .com/linux/how-to-update-glibc-newer-version-on-centos-6-x/

@msb gives a safe solution.

I met this problem when I did import tensorflow as tf in conda environment in CentOS 6.5 which only has glibc-2.12.

ImportError: /lib64/libc.so.6: version `GLIBC_2.16' not found (required by /home/

I want to supply some details:

First install glibc to your home directory:

mkdir ~/glibc-install; cd ~/glibc-install
wget http://ftp.gnu.org/gnu/glibc/glibc-2.17.tar.gz
tar -zxvf glibc-2.17.tar.gz
cd glibc-2.17
mkdir build
cd build
../configure --prefix=/home/myself/opt/glibc-2.17  # <-- where you install new glibc
make -j<number of CPU Cores>  # You can find your <number of CPU Cores> by using **nproc** command
make install

Second, follow the same way to install patchelf;

Third, patch your Python:

[myself@nfkd ~]$ patchelf --set-interpreter /home/myself/opt/glibc-2.17/lib/ld-linux-x86-64.so.2 --set-rpath /home/myself/opt/glibc-2.17/lib/ /home/myself/miniconda3/envs/tensorflow/bin/python

as mentioned by @msb

Now I can use tensorflow-2.0 alpha in CentOS 6.5.

ref: https://serverkurma.com/linux/how-to-update-glibc-newer-version-on-centos-6-x/

风流物 2024-07-26 15:58:55

您可以考虑使用 Nix http://nixos.org/nix/ 吗?

Nix支持多用户包管理:多个用户可以共享一个
通用Nix安全存储,不需要root权限
安装软件,可以安装和使用不同版本的软件
包。

Can you consider using Nix http://nixos.org/nix/ ?

Nix supports multi-user package management: multiple users can share a
common Nix store securely, don’t need to have root privileges to
install software, and can install and use different versions of a
package.

绅刃 2024-07-26 15:58:55

这个问题很旧,其他答案也很旧。 Employed Russian 的答案非常好且内容丰富,但只有在您拥有源代码的情况下才有效。 如果你不这样做,那么当时的替代方案就非常棘手。 幸运的是,现在我们有一个简单的解决方案来解决这个问题(如 其中之一所述他的回复),使用 patchelf。 您所要做的就是:

$ ./patchelf --set-interpreter /path/to/newglibc/ld-linux.so.2 --set-rpath /path/to/newglibc/ myapp

这会将非工作可执行文件更改为其链接器使用不同的路径。 之后,您只需执行您的文件即可:

$ ./myapp

谢天谢地,无需 chroot 或手动编辑二进制文件。 但如果您不确定自己在做什么,请记住在修补之前备份您的二进制文件,因为它会修改您的二进制文件。 修补后,您无法恢复解释器/rpath 的旧路径。 如果它不起作用,你就必须不断地修补它,直到找到真正有效的路径......好吧,它不一定是一个反复试验的过程。 例如,在OP的示例中,他需要GLIBC_2.3,因此您可以使用string轻松找到哪个库提供该版本:

$ strings /lib/i686/libc.so.6 | grep GLIBC_2.3
$ strings /path/to/newglib/libc.so.6 | grep GLIBC_2.3

理论上,第一个grep将为空,因为系统libc没有他想要的版本,第二个应该输出GLIBC_2.3,因为它有myapp正在使用的版本,所以我们知道我们可以patchelf我们的二进制文件使用该路径。 如果遇到分段错误,请阅读末尾的注释。

当您尝试在 Linux 中运行二进制文件时,二进制文件会尝试加载链接器(又名加载器、解释器),然后加载库,并且它们都应该位于路径中和/或正确的位置。 如果您的问题出在链接器上,并且您想找出二进制文件正在寻找哪个路径,您可以使用以下命令找出答案:

$ readelf -l myapp | grep interpreter
  [Requesting program interpreter: /lib/ld-linux.so.2]                                                                                                                                                                                   

如果您的问题出在库上,则将为您提供正在使用的库的命令是:

$ readelf -d myapp | grep Shared
$ ldd myapp 

这将列出您的二进制文件所需的库,但您可能已经知道有问题的库,因为它们已经像 OP 的情况一样产生错误。 执行 patchelf 后,可能会发生 myapp 仍然无法工作的情况,当您运行 ldd myapp 时,它会列出具有混合路径的库,其中一些是您设置的路径,其他的到原来的系统路径。 那是因为你的路径没有这些库。 rpath 将在您设置的路径中搜索库,但如果不存在,它仍然会在其他系统位置中查找。 在这种情况下,如果您在某处缺少 lib,只需将其复制到您选择的 rpath 即可,它应该可以工作。

“patchelf”适用于您在尝试运行程序时可能遇到的许多不同问题,与这两个问题相关。 例如,如果您得到:ELF file OS ABI invalid,则可以通过设置新的加载程序(命令的 --set-interpreter 部分)来修复它,就像我一样此处解释。 另一个例子是当您运行存在且可执行的文件时出现 No such file or directory 的问题,如下所示 此处。 在这种特殊情况下,OP 缺少指向加载程序的链接,但也许在您的情况下您没有 root 访问权限并且无法创建链接。 设置一个新的口译员可以解决你的问题。

感谢 Employ Russian 和 Michael Pankov 的见解和解决方案!


注意分段错误:您可能会遇到 myapp 使用多个库的情况,其中大多数都可以,但有些则不行; 然后你patchelf它到一个新的目录,你会得到分段错误。 当您 patchelf 二进制文件时,您会更改多个库的路径,即使某些库最初位于不同的路径中。 看一下我下面的示例:

$ ldd myapp
./myapp: /usr/lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by ./myapp)
./myapp: /usr/lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by ./myapp)
        linux-vdso.so.1 =>  (0x00007fffb167c000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f9a9aad2000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f9a9a8ce000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f9a9a6af000)
        libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f9a9a3ab000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9a99fe6000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f9a9adeb000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f9a99dcf000)

请注意,大多数库都位于 /lib/x86_64-linux-gnu/ 中,但有问题的库 (libstdc++.so.6) 位于/usr/lib/x86_64-linux-gnu。 在我对 myapp 进行 patchelf'ed 以指向 /path/to/mylibs 后,我遇到了分段错误。 由于某种原因,这些库与二进制文件并不完全兼容。 由于 myapp 没有抱怨原始库,因此我将它们从 /lib/x86_64-linux-gnu/ 复制到 /path/to/mylibs2 code>,我还从 /path/to/mylibs 复制了 libstdc++.so.6 。 然后我将其修补到 /path/to/mylibs2,并且 myapp 现在可以工作了。 如果您的二进制文件使用不同的库,并且您有不同的版本,则可能无法解决您的情况。 :( 但如果可能的话,混合库可能是一种方法。这并不理想,但也许它会起作用。祝你好运!

This question is old, the other answers are old. Employed Russian's answer is very good and informative, but it only works if you have the source code. If you don't, the alternatives back then were very tricky. Fortunately nowadays we have a simple solution to this problem (as commented in one of his replies), using patchelf. All you have to do is:

$ ./patchelf --set-interpreter /path/to/newglibc/ld-linux.so.2 --set-rpath /path/to/newglibc/ myapp

This changes the non-working executable to use a different path for its linker. And after that, you can just execute your file:

$ ./myapp

No need to chroot or manually edit binaries, thankfully. But remember to backup your binary before patching it, if you're not sure what you're doing, because it modifies your binary file. After you patch it, you can't restore the old path to interpreter/rpath. If it doesn't work, you'll have to keep patching it until you find the path that will actually work... Well, it doesn't have to be a trial-and-error process. For example, in OP's example, he needed GLIBC_2.3, so you can easily find which lib provides that version using strings:

$ strings /lib/i686/libc.so.6 | grep GLIBC_2.3
$ strings /path/to/newglib/libc.so.6 | grep GLIBC_2.3

In theory, the first grep would come empty because the system libc doesn't have the version he wants, and the 2nd one should output GLIBC_2.3 because it has the version myapp is using, so we know we can patchelf our binary using that path. If you get a segmentation fault, read the note at the end.

When you try to run a binary in linux, the binary tries to load the linker (aka loader, aka interpreter), then the libraries, and they should all be in the path and/or in the right place. If your problem is with the linker and you want to find out which path your binary is looking for, you can find out with this command:

$ readelf -l myapp | grep interpreter
  [Requesting program interpreter: /lib/ld-linux.so.2]                                                                                                                                                                                   

If your problem is with the libs, commands that will give you the libs being used are:

$ readelf -d myapp | grep Shared
$ ldd myapp 

This will list the libs that your binary needs, but you probably already know the problematic ones, since they are already yielding errors as in OP's case. After you do patchelf, it might happen that myapp is still not working, and when you run ldd myapp it lists the libs with mixed paths, some to the path you set, others to the original system path. That's because your path doesn't have those libs. rpath will search for the lib in the path you set, but if it's not there, it still looks for it in the other system locations. In this case, if you have the missing lib somewhere, just copy it to the rpath that you chose and it should work.

"patchelf" works for many different problems that you may encounter while trying to run a program, related to these 2 problems. For example, if you get: ELF file OS ABI invalid, it may be fixed by setting a new loader (the --set-interpreter part of the command) as I explain here. Another example is for the problem of getting No such file or directory when you run a file that is there and executable, as exemplified here. In that particular case, OP was missing a link to the loader, but maybe in your case you don't have root access and can't create the link. Setting a new interpreter would solve your problem.

Thanks Employed Russian and Michael Pankov for the insight and solution!


Note for segmentation fault: you might be in the case where myapp uses several libs, and most of them are ok but some are not; then you patchelf it to a new dir, and you get segmentation fault. When you patchelf your binary, you change the path of several libs, even if some were originally in a different path. Take a look at my example below:

$ ldd myapp
./myapp: /usr/lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by ./myapp)
./myapp: /usr/lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by ./myapp)
        linux-vdso.so.1 =>  (0x00007fffb167c000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f9a9aad2000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f9a9a8ce000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f9a9a6af000)
        libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f9a9a3ab000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9a99fe6000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f9a9adeb000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f9a99dcf000)

Note that most libs are in /lib/x86_64-linux-gnu/ but the problematic one (libstdc++.so.6) is on /usr/lib/x86_64-linux-gnu. After I patchelf'ed myapp to point to /path/to/mylibs, I got segmentation fault. For some reason, the libs are not totally compatible with the binary. Since myapp didn't complain about the original libs, I copied them from /lib/x86_64-linux-gnu/ to /path/to/mylibs2, and I also copied libstdc++.so.6 from /path/to/mylibs there. Then I patchelf'ed it to /path/to/mylibs2, and myapp works now. If your binary uses different libs, and you have different versions, it might happen that you can't fix your situation. :( But if it's possible, mixing libs might be the way. It's not ideal, but maybe it will work. Good luck!

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