一个主机上有多个glibc库

237

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


1
与SLES-11服务器相同的问题。无法更新,需要最新的东西。哦天啊... - UmNyobe
1
就我个人而言,export LD_LIBRARY_PATH=newglibc:$LD_LIBRARY_PATH 确实解决了我的问题!虽然它并不适用于每个人,但如果它确实有效,那么这是一个简单的解决方法!谢谢! :) - rinogo
你只需要一个glibc版本:所需的最高版本。它应该是向后兼容的。 - Alberto Salvia Novella
11个回答

314

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

然而,你需要知道glibc由许多部分(200+个共享库)组成,所有这些部分都必须匹配。其中一个部分是ld-linux.so.2,它必须与libc.so.6匹配,否则你会看到你目前遇到的错误。

ld-linux.so.2的绝对路径在链接时已经硬编码到可执行文件中,在链接完成后很难更改(更新:可以通过 patchelf 进行修改;请参见下面的 this answer)。

要构建一个与新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`环境。另一个可能性是使用rtldibinary editor更新:或者您可以对现有二进制文件使用patchelf将它们重定向到备用的libc。

56
现在您可以使用一个便利的工具 patchelf(http://nixos.org/patchelf.html),它允许您修改已编译的 ELF 文件的 rpath 和解释器。 - Michael Pankov
15
值得一提的是,使用-Wl,--rpath来指定新glibc路径而不是使用LD_LIBRARY_PATH可能除了方便外还有其他重要的原因:如果程序启动子进程,LD_LIBRARY_PATH的值通常会被继承,但如果它们没有编译成使用更新版本的glibc(例如,如果它们是像bash这样的原始二进制文件),它们将无法启动。 - HighCommander4
23
另一个选择是直接运行新的 ld.so,并将您的二进制程序作为参数传递;这将有效地替换使用的 ld.so,无需重新编译程序: /path/to/newglibc/ld-linux.so.2 --library-path /path/tonewglibc/lib64:/path/to/newglibc/usr/lib64 /path/to/myapp - maximk
1
我也需要使用-I-L:https://dev59.com/VHRA5IYBdhLWcg3wvQhh#52454603 - Ciro Santilli OurBigBook.com
1
@SantoshKale 要在“并行”位置安装,您必须使用 configure --prefix /new/locationmake && make install。预构建的 RPM 将无法工作。 - Employed Russian
显示剩余2条评论

138
这个问题很老了,其他的答案也都是老的了。"Employed Russian"的答案非常好而且有用,但是只适用于你有源代码的情况下。如果没有源代码,那时候的替代方案非常棘手。幸运的是,现在我们有一个简单的解决方案来解决这个问题(正如他在其中一个回复中所提到的),使用patchelf。你只需要做以下几步:
$ ./patchelf --set-interpreter /path/to/newglibc/ld-linux.so.2 --set-rpath /path/to/newglibc/ myapp

然后,你只需要执行你的文件即可。
$ ./myapp

不需要chroot或手动编辑二进制文件,谢天谢地。但是在修补之前,请记得备份你的二进制文件,如果你不确定自己在做什么,因为它会修改你的二进制文件。在你修补之后,你将无法恢复到旧的解释器/运行路径。如果修补不起作用,你将不得不继续修补,直到找到真正有效的路径...好吧,这不一定是一个试错的过程。例如,在OP的例子中,他需要GLIBC_2.3,所以你可以使用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应该输出GLIBC_2.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之后,可能会发生myapp仍然无法工作的情况,当您运行ldd myapp时,它会列出具有混合路径的库,一些路径是您设置的路径,其他路径是原始系统路径。这是因为您的路径中没有这些库。rpath将在您设置的路径中搜索库,但如果找不到,它仍然会在其他系统位置中查找。在这种情况下,如果您在其他地方有缺失的库,只需将其复制到您选择的rpath中,它应该可以正常工作。
"patchelf"可以解决在运行程序时可能遇到的许多不同问题,与这两个问题有关。例如,如果你遇到了"ELF file OS ABI invalid"的错误,可以通过设置一个新的加载器(命令中的"--set-interpreter"部分)来修复,我在这里here解释了如何操作。另一个例子是当你运行一个已经存在且可执行的文件时,却出现了"No such file or directory"的问题,就像这里here所示。在这种特殊情况下,OP缺少了一个指向加载器的链接,但也许在你的情况下,你没有root权限,无法创建这个链接。设置一个新的解释器将解决你的问题。
感谢Employed 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目录下。在我使用patchelf将myapp指向/path/to/mylibs后,出现了分段错误。由于某种原因,这些库文件与二进制文件并不完全兼容。由于myapp没有对原始库文件抱怨,我将它们从/lib/x86_64-linux-gnu/目录下复制到了/path/to/mylibs2,并且还将libstdc++.so.6从/path/to/mylibs复制到了那里。然后我使用patchelf将其指向/path/to/mylibs2,现在myapp可以正常工作了。如果您的二进制文件使用不同的库文件,并且版本不同,可能无法解决您的问题。:( 但如果可能的话,混合使用库文件可能是一种方法。这并不理想,但也许会起作用。祝您好运!

3
这是最有帮助的!我对 Python 二进制文件进行了补丁,以便为 TensorFlow 使用新的 glibc。 - faizan
这是一个不错的解决方案(我之前不知道 patchelf),但短语“无需编辑二进制文件”可能有点误导(因为实际上您正在编辑二进制文件)。 - larsks
好了,修好了。;) - msb
5
嗨,讽刺的是我遇到了 ./patchelf: /lib64/libstdc++.so.6: version GLIBCXX_3.4.21' not found (required by ./patchelf)` 的错误。我认为我需要寻找另一个解决方案。 - Welgriv
2
@AlexO 你不应该修补libc,而是应该修补使用libc的可执行文件(通过将其指向具有所需版本的不同libc)。也许在StackOverflow上发布一个新问题,包括您的问题细节和尝试过的方法。 :) - msb
显示剩余9条评论

21

使用LD_PRELOAD:

将您的库放在不在主要库目录中的某个位置,然后运行:

LD_PRELOAD='mylibc.so anotherlib.so' program

参见:维基百科文章


1
我认为这是一个不错的解决复杂Makefile的方法,但对我来说并没有起作用。 - galactica
这非常有用,特别是对于那些没有源代码的二进制文件。谢谢。 - coder
2
嗯...我错了,似乎在源代码编译和链接时,我需要将ld-linux.so的rpath设置为/path/to/new/lib/。 - coder
1
如果ld-#.##.so(来自您的系统glibc lib)与libc.so.#(来自您的备用glibc lib)不是相同的glibc版本,则此方法无效。 - Andy

17

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

让我们举个简单的例子:我有最新的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加载动态库。

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


我在RH7上编译了一个程序,现在需要在RH6上运行。我不想构建一个新的可执行文件或使用patchelf,所以这是一个很好的选择。 - Mark Rajcok
尝试使用此方法在 Debian Jessie 上使用自定义 GCC 5.3 编译 MongoDB v4,但直接在这里运行链接器时出现分段错误:https://stackoverflow.com/questions/65256105/dynamic-linker-direct-call-segmentation-fault-when-running-an-app-compiled-whith 你能帮忙吗? - Boris Rybalkin

15

安装步骤1:编译自己的glibc并使用它,无需专用GCC

此设置可能可行且快速,因为它不重新编译整个GCC工具链,只编译glibc。

但是它不太可靠,因为它使用主机C运行时对象,例如由glibc提供的crt1.o,crti.o和crtn.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://dev59.com/rXVD5IYBdhLWcg3wL4mM#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`

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

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

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中执行以下操作:

  • C库
  • glibc的版本

保存.config文件并继续构建。

或者,如果你想使用自己的glibc源代码,例如使用来自最新git的glibc,请按照以下步骤进行:

  • 路径和其他选项
    • 尝试标记为实验性的功能: 设为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”选项中的“保存中间步骤”:
  • 路径和其他选项
    • 调试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 步骤开始似乎没有再次从 Custom source location 拷贝源代码,进一步使此方法无法使用。

如果你还对 C++ 标准库感兴趣,这里有一个奖励:如何编辑和重新构建 GCC libstdc++ C++ 标准库源代码?


1
我打算使用crosstool-ng的思路。但是我将使用最新的[crosstool-ng](https://github.com/crosstool-ng/crosstool-ng/releases/tag/crosstool-ng-1.25.0)发布版,而不是上面提到的提交。 - Daisuke Aramaki

11

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

当我在CentOS 6.5环境中使用只有glibc-2.12的情况下,尝试在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

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

第三步,修补你的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/


你好,这个方法是有效的,但是...在设置Python之后(实际上是设置虚拟环境中的Python),它会出现以下错误:python3.8: 加载共享库libexpat.so.1时出错:无法打开共享对象文件:没有那个文件或目录 - undefined
请检查您的路径顺序,并仔细检查到libexpat.so.1的路径。错误信息显示找不到该文件。 - undefined

6

你是否考虑使用 Nix http://nixos.org/nix/ ?

Nix 支持多用户包管理:多个用户可以安全地共享一个公共的 Nix 存储,无需具备 root 权限来安装软件,并且可以安装和使用不同版本的软件包。


2

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


1
"

“Employed Russian”是最好的答案之一,我认为所有其他建议的答案可能都不可行。原因很简单,当一个应用程序首次创建时,它所需的所有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”,或者仅从现有的发行版环境中进行tar打包(如Fedora/Suse等)。


1

如果你仔细观察第二个输出,你会发现新的库位置被使用了。也许还有一些缺失的库是glibc的一部分。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接