我的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

and

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,而不是从我放置它们的位置拾取。


当前回答

这个问题老了,其他答案也老了。“受雇的俄罗斯人”的回答是非常好的和信息丰富的,但它只有在你有源代码的情况下才有效。如果你不这样做,那时候的选择是非常棘手的。幸运的是,现在我们对这个问题有了一个简单的解决方案(正如他在回复中评论的那样),使用patchelf。你所要做的就是:

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

在那之后,你可以执行你的文件:

$ ./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

理论上,第一个grep会是空的,因为系统libc没有他想要的版本,而第二个grep应该输出glibc2.3,因为它有myapp正在使用的版本,所以我们知道可以使用该路径修补我们的二进制文件。如果你得到一个分割错误,阅读末尾的说明。

当你尝试在linux中运行二进制文件时,二进制文件会尝试加载链接器,然后加载库,它们都应该在路径和/或在正确的位置。如果你的问题是链接器,你想知道你的二进制文件在寻找哪个路径,你可以用这个命令来找到:

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

如果你的问题是库,命令会给你正在使用的库:

$ readelf -d myapp | grep Shared
$ ldd myapp 

这将列出二进制文件需要的库,但您可能已经知道有问题的库,因为它们已经产生错误,就像OP的情况一样。

"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.

感谢受雇于俄罗斯和Michael Pankov的洞察力和解决方案!


注意分割错误:你可能在myapp使用几个库的情况下,他们中的大多数是可以的,但有些不是;然后你修补它到一个新的目录,你得到分割错误。当你修补你的二进制文件时,你改变了几个库的路径,即使有些原本在不同的路径。看看下面的例子:

$ 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!

其他回答

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

这种设置可能有效,而且很快,因为它不需要重新编译整个GCC工具链,只需要重新编译glibc。

但它不可靠,因为它使用宿主C运行时对象,如crt1。o, crti。哦,还有crtn。O由glibc提供。这在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 but——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

我们看到黑黑客像预期的那样印刷了几次。

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

在Ubuntu 18.04上测试。

设置2:crosstool-NG原始设置

这是设置1的替代方案,而且它是我迄今为止实现的最正确的设置:据我观察,一切都是正确的,包括crt1等C运行时对象。o, crti。O和crtn.o。

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

这种方法的唯一缺点是构建将花费更长的时间。但我不会冒险用其他东西来制作。

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`

构建大约需要30分钟到2个小时。

我能看到的唯一强制性配置选项是使它与您的主机内核版本相匹配,以使用正确的内核头文件。使用以下命令查找您的主机内核版本:

uname -a

这告诉我:

4.15.0-34-generic

所以在菜单中我这样做:

操作系统 linux版本

所以我选择:

4.14.71

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

设置2:可选配置

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

CT_GLIBC_V_2_27=y

要改变这一点,在menuconfig中执行:

c库 glibc的版本

保存.config,并继续构建。

或者,如果你想使用你自己的glibc源代码,比如从最新的git中使用glibc,可以这样做:

路径和misc选项 尝试标记为EXPERIMENTAL的特性:设置为true c库 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选项中设置“保存中间步骤”:

路径和misc选项 调试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++标准库源代码?

如果仔细观察第二个输出,可以看到使用了库的新位置。可能仍然有作为glibc一部分的库丢失。

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

使用LD_PRELOAD: 把你的库放在man lib目录之外的地方,然后运行:

LD_PRELOAD='mylibc.so anotherlib.so' program

参见:维基百科的文章

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

但是,您需要知道glibc包含许多必须匹配的部分(200多个共享库)。其中一个是ld-linux.so。2,它必须匹配lib .so。6,否则你会看到你所看到的错误。

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 linker选项将使运行时加载器搜索/path/to/newglibc中的库(因此您不必在运行它之前设置LD_LIBRARY_PATH),而-dynamic-linker选项将“烤”路径以纠正ld-linux.so。2 .进入应用。

如果不能重新链接myapp应用程序(例如,因为它是第三方二进制文件),并不是全部都丢失了,但它会变得更棘手。一种解决方案是为它设置适当的chroot环境。另一种可能是使用rtldi和二进制编辑器。更新:或者你可以使用patchelf。

当我想在Ubuntu precise (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