Multiple glibc libraries on a single host

后端 未结 11 1537
轮回少年
轮回少年 2020-11-21 05:13

Multiple glibc libraries on a single host

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

相关标签:
11条回答
  • 2020-11-21 05:41

    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?

    0 讨论(0)
  • 2020-11-21 05:42

    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.

    0 讨论(0)
  • 2020-11-21 05:42

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

    0 讨论(0)
  • 2020-11-21 05:44

    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.

    0 讨论(0)
  • 2020-11-21 05:51

    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
    

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

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

    0 讨论(0)
  • 2020-11-21 05:56

    @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/

    0 讨论(0)
提交回复
热议问题