Comment créer un lien vers une version glibc spécifique?

quand je compile quelque chose sur mon PC Ubuntu Lucid 10.04, il est relié à glibc. Lucide utilise 2.11 de glibc. Quand j'exécute ce binaire sur un autre PC avec un ancien glibc, la commande échoue en disant qu'il n'y a pas de glibc 2.11...

autant que je sache, glibc utilise le versioning des symboles. Puis-je forcer gcc à créer un lien avec une version de symbole spécifique?

dans mon usage concret, j'essaie de compiler une chaîne à outils gcc cross pour ARM.

84
demandé sur falstaff 2010-05-18 14:45:23

3 réponses

Vous avez raison glibc utilise les versions de symboles. Si vous êtes curieux, la mise en œuvre de versioning de symbole introduite dans glibc 2.1 est décrite ici et est une extension du schéma de versioning de symbole de Sun décrit ici .

Une option est de lier statiquement votre binaire. C'est probablement la meilleure option.

vous pouvez également construire votre binaire dans un environnement de construction chroot, ou en utilisant un glibc - nouveau = > glibc- ancien cross-compilateur.

selon le http://www.trevorpounds.com blog post lien vers D'anciens symboles suivis en versions (glibc) , il est possible de lier n'importe quel symbole à un symbole plus ancien tant qu'il est valide en utilisant le même .symver 1519120920" pseudo-op qui est utilisé pour définir les symboles suivis en versions. L'exemple suivant est extrait du post de blog .

l'exemple suivant utilise realpath de glibc, mais s'assure qu'il est lié avec une version 2.2.5 plus ancienne.

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

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

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

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

    return 0;
}
56
répondu jschmier 2016-08-05 15:11:47

lien avec - statique . Lorsque vous liez avec - statique le linker intègre la bibliothèque à l'intérieur de l'exécutable, donc l'exécutable sera plus grand, mais il peut être exécuté sur un système avec une version plus ancienne de glibc parce que le programme utilisera sa propre bibliothèque au lieu de celle du système.

17
répondu Iacchus 2016-08-03 15:21:57

Setup 1: compilez votre propre glibc sans GCC dédié et utilisez-le

Puisqu'il semble impossible de se contenter de pirater le symbole versioning, allons un peu plus loin et compilons glibc nous-mêmes.

cette configuration pourrait fonctionner et est rapide car elle ne recompile pas L'ensemble de la chaîne D'outils GCC, juste glibc.

mais il n'est pas fiable car il utilise des objets d'exécution host C tels que crt1.o , crti.o , et crtn.o fourni par glibc. Ceci est mentionné à: https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location ces objets font une configuration précoce sur laquelle glibc s'appuie, donc je ne serais pas surpris si les choses se sont écrasées de manière merveilleuse et subtile.

pour une configuration plus fiable, voir Configuration 2 ci-dessous.

construire glibc et installer localement:

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`

Configuration 1: vérification de la compilation

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.
     * /q/how-do-i-start-threads-in-plain-c-24995/"The atomic counter is %u\n", acnt);
    printf("The non-atomic counter is %u\n", cnt);
}

compiler et exécuter avec 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

le programme produit les résultats escomptés:

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

commande adaptée de https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location mais --sysroot l'a fait échouer avec:

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

donc je l'ai enlevé.

ldd confirme que la sortie ldd et les bibliothèques que nous venons de construire sont effectivement utilisées comme prévu:

+ 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)

la sortie de débogage de la compilation gcc montre que les objets runtime de mon hôte ont été utilisés, ce qui est mauvais comme mentionné précédemment, mais je ne sais pas comment l'contourner, par exemple il contient:

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

Setup 1: modifier glibc

modifions maintenant glibc avec:

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

puis recompiler et réinstaller glibc, et recompiler et relancer notre programme:

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

et nous voyons hacked imprimé quelques fois comme prévu.

cela confirme une fois de plus que nous avons utilisé la glibc que nous avons compilée et non celle de l'hôte.

testé sur Ubuntu 18.04.

Setup 2: crosstool-NG setup vierge

c'est une alternative à la configuration 1, et c'est la configuration la plus correcte que j'ai réalisée jusqu'à présent: tout est correct autant que je peux l'observer, y compris les objets C runtime tels que crt1.o , crti.o , et crtn.o .

dans cette configuration, nous allons compiler une chaîne D'outils GCC dédiée qui utilise la glibc que nous voulons.

Le seul inconvénient de cette méthode est que la construction prendra plus de temps. Mais je ne risquerais pas une installation de production avec quelque chose de moins.

crosstool-NG est un ensemble de scripts qui télécharge et compile tout depuis la source pour nous, y compris GCC, glibc et binutils.

Oui le système de construction GCC est si mauvais que nous avons besoin d'un projet séparé pour cela.

cette configuration n'est pas parfaite car crosstool-NG ne supporte pas construire les exécutables sans extra -Wl drapeaux , ce qui semble bizarre depuis que nous avons construit GCC lui-même. Mais tout semble fonctionner, si ce n'est qu'un inconvénient.

Obtenir crosstool-NG et le configurer:

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

la seule option obligatoire que je puisse voir, est de faire correspondre la version du noyau hôte pour utiliser les en-têtes du noyau correct. Trouvez votre version du noyau hôte avec:

uname -a

ce qui me montre:

4.15.0-34-generic

menuconfig je n':

  • Operating System
    • Version of linux

donc je sélectionne:

4.14.71

qui est la première version égale ou plus ancienne. Il doit être plus ancien car le noyau est rétrocompatible.

maintenant vous pouvez construire avec:

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

et maintenant attendre environ trente minutes à deux heures pour la compilation.

Setup 2: configuration optionnelle

le .config que nous avons généré avec ./ct-ng x86_64-unknown-linux-gnu a:

CT_GLIBC_V_2_27=y

pour changer que, dans menuconfig faire:

  • C-library
  • Version of glibc

enregistrer l' .config , et continuer avec la construction.

ou, si vous voulez utiliser votre propre source glibc, p.ex. pour utiliser glibc du dernier git, procéder comme ceci :

  • Paths and misc options
    • Try features marked as EXPERIMENTAL : mis à true
  • C-library
    • Source of glibc
      • Custom location : dis Oui
      • Custom location
        • Custom source location : pointez vers un répertoire contenant votre source glibc

où glibc a été cloné:

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

Setup 2: tester

une fois que vous avez construit la chaîne d'outils que vous voulez, tester avec:

#!/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

tout semble fonctionner comme dans la configuration 1, sauf que maintenant les bons objets runtime ont été utilisés:

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: échec de la tentative de recompilation efficace de glibc

cela ne semble pas possible avec crosstool-NG, comme expliqué ci-dessous.

si vous venez de reconstruire;

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

puis vos modifications à l'emplacement des sources glibc personnalisé sont pris en compte, mais il construit tout à partir de zéro, ce qui le rend inutilisable pour le développement itératif.

Si nous n':

./ct-ng list-steps

il donne un bel aperçu des étapes de construction:

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.

par conséquent, nous voyons qu'il ya des étapes glibc entrelacés avec plusieurs étapes GCC , notamment libc_start_files vient avant cc_core_pass_2 , qui est probablement la plus chère étape avec cc_core_pass_1 .

dans l'ordre pour construire une seule étape, vous devez d'abord définir L'option" Enregistrer les étapes intermédiaires "dans .config pour la construction initiale:

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

et puis vous pouvez essayer:

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

mais malheureusement, le + requis comme mentionné à: https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536

noter cependant que redémarrer à une étape intermédiaire remet le répertoire d'installation dans l'état où il se trouvait pendant cette étape. C'est-à-dire: vous aurez une libc reconstruite-mais pas de compilateur final construit avec cette libc (et donc pas de librairies de compilateurs comme libstdc++ non plus).

et fondamentalement encore rend la reconstruction trop lente pour être réalisable pour le développement, et je ne vois pas comment surmonter cela sans Corriger crosstool-NG.

en outre, à partir de l'étape libc ne semble pas copier sur la source à partir de Custom source location , ce qui rend cette méthode inutilisable.

Bonus: stdlibc++

un bonus si vous êtes également intéressé par la bibliothèque standard C++: comment éditer et reconstruire le GCC libstdc++ C++ Standard library source?

3