Configuration 1: compilez votre propre glibc sans GCC dédié et utilisez-la
Puisqu'il semble impossible de faire uniquement avec des hacks de version de symboles, allons un peu plus loin et compilons la glibc nous-mêmes.
Cette configuration peut fonctionner et est rapide car elle ne recompile pas l'ensemble de la chaîne d'outils GCC, juste la glibc.
Mais il n'est pas fiable car il utilise des objets d' exécution hôte C tels que crt1.o
, crti.o
et crtn.o
fourni par la 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 la glibc s'appuie, donc je ne serais pas surpris si les choses se bloquaient de manière merveilleuse et des manières incroyablement subtiles.
Pour une configuration plus fiable, voir Configuration 2 ci-dessous.
Construisez la glibc et installez 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érifier la construction
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.
* /programming/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);
}
Compilez et exécutez 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 attendus:
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 l'a --sysroot
fait échouer avec:
cannot find /home/ciro/glibc/build/install/lib/libc.so.6 inside /home/ciro/glibc/build/install
alors je l'ai enlevé.
ldd
La sortie confirme que les ldd
bibliothèques et que nous venons de créer sont en fait 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 gcc
sortie de débogage de compilation montre que mes objets d'exécution hôte ont été utilisés, ce qui est mauvais comme mentionné précédemment, mais je ne sais pas comment contourner cela, par exemple, il contient:
COLLECT_GCC_OPTIONS=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o
Configuration 1: modifier la glibc
Modifions maintenant la 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)");
Ensuite, recompilez et réinstallez la glibc, puis recompilez et réexécutez 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 en outre que nous avons effectivement utilisé la glibc que nous avons compilée et non l'hôte.
Testé sur Ubuntu 18.04.
Configuration 2: configuration vierge du crosstool-NG
Ceci est une alternative à la configuration 1, et il est le plus correct configuration que j'ai accompli jusqu'à présent: tout est correct pour autant que je peux observer, y compris l'exécution de C des objets tels que crt1.o
, crti.o
et crtn.o
.
Dans cette configuration, nous compilerons une chaîne d'outils GCC dédiée complète 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 configuration de production avec rien de moins.
crosstool-NG est un ensemble de scripts qui télécharge et compile tout à partir de la source pour nous, y compris GCC, glibc et binutils.
Oui, le système de construction de GCC est si mauvais que nous avons besoin d'un projet séparé pour cela.
Cette configuration n'est tout simplement pas parfaite car crosstool-NG ne prend pas en charge la construction des exécutables sans -Wl
drapeaux supplémentaires , ce qui semble étrange depuis que nous avons construit GCC lui-même. Mais tout semble fonctionner, donc ce n'est qu'un inconvénient.
Obtenez crosstool-NG et configurez-le:
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 peux voir, c'est de faire correspondre la version de votre noyau hôte pour utiliser les en-têtes de noyau corrects. Trouvez la version de votre noyau hôte avec:
uname -a
ce qui me montre:
4.15.0-34-generic
donc dans menuconfig
je fais:
donc je sélectionne:
4.14.71
qui est la première version égale ou antérieure. Il doit être plus ancien car le noyau est rétrocompatible.
Vous pouvez maintenant construire avec:
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
et attendez maintenant environ trente minutes à deux heures pour la compilation.
Configuration 2: configurations optionnelles
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 cela, menuconfig
faites:
C-library
Version of glibc
enregistrez le .config
, et poursuivez la construction.
Ou, si vous souhaitez utiliser votre propre source glibc, par exemple pour utiliser glibc à partir du dernier git, procédez comme suit :
Paths and misc options
Try features marked as EXPERIMENTAL
: défini sur true
C-library
Source of glibc
Custom location
: dis oui
Custom location
Custom source location
: pointez sur un répertoire contenant votre source glibc
où la glibc a été clonée comme:
git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28
Configuration 2: testez-le
Une fois que vous avez construit la chaîne d'outils que vous souhaitez, testez-la 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 objets d'exécution corrects 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
Configuration 2: échec de la tentative de recompilation efficace de la glibc
Cela ne semble pas possible avec le crosstool-NG, comme expliqué ci-dessous.
Si vous venez de reconstruire;
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
alors vos modifications apportées à l'emplacement de la source glibc personnalisée sont prises en compte, mais il construit tout à partir de zéro, le rendant inutilisable pour le développement itératif.
Si nous faisons:
./ct-ng list-steps
cela donne un bon 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 y a des étapes de la glibc entrelacées avec plusieurs étapes GCC, notamment libc_start_files
vient avant cc_core_pass_2
, ce qui est probablement l'étape la plus coûteuse avec cc_core_pass_1
.
Afin de ne construire qu'une seule étape, vous devez d'abord définir l' .config
option "Enregistrer les étapes intermédiaires" dans l' option pour la construction initiale:
et ensuite 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
Notez cependant que le redémarrage à une étape intermédiaire réinitialise le répertoire d'installation à l'état qu'il avait lors de cette étape. C'est-à-dire que vous aurez une libc reconstruite - mais pas de compilateur final construit avec cette libc (et par conséquent, pas de bibliothèques de compilateurs comme libstdc ++ non plus).
et rend fondamentalement encore la reconstruction trop lente pour être faisable pour le développement, et je ne vois pas comment surmonter cela sans patcher crosstool-NG.
De plus, à partir de l' libc
étape ne semblait pas recopier la source Custom source location
, ce qui rendait cette méthode inutilisable.
Bonus: stdlibc ++
Un bonus si vous êtes également intéressé par la bibliothèque standard C ++: Comment éditer et reconstruire la source de la bibliothèque standard C ++ libstdc ++ de GCC?