Testé pour la dernière fois sur Ubuntu 20.04 avec la version de développement glibc 2.33.9000 (voir glibc/version.h
) le 27 juin 2021.
Comment télécharger et compiler glibc, et exécuter ses benchmarks
Vous pouvez obtenir manuellement le code source de la glibc ici :https://www.gnu.org/software/libc/sources.html :
git clone https://sourceware.org/git/glibc.git
cd glibc
git checkout master
Miroir tiers sur GitHub :https://github.com/bminor/glibc/tree/master/benchtests
Voir aussi :
- https://kazoo.ga/a-simple-tool-to-test-malloc-performance/
Si vous souhaitez compiler manuellement la glibc et ses bancs d'essai, procédez comme suit :
# IMPORTANT: begin AT THE SAME DIRECTORY LEVEL as the `glibc` source code
# directory, NOT inside the `glibc` source code dir! In other words, if
# you are in the correct dir, running `ls` will show the `glibc` source
# code dir (that you just cloned) inside the dir you are in.
mkdir -p glibc-build
mkdir -p glibc-install
cd glibc-build
../glibc/configure --prefix="$(realpath "../glibc-install")"
time make -j8 # build with 8 threads (jobs); on a fast laptop this takes ~3 min.
time make install # (optional: install to the `glibc-install` dir you created)
# Build the benchtests (everything inside the `glibc/benchtests` dir) too;
# see the makefile 'glibc/benchtests/Makefile' for more build commands.
time make bench-build -j8
# Now you have this executable file you can use for malloc speed tests, for instance!:
# ../glibc-build/benchtests/bench-malloc-thread
# To build **and run** all glibc benchtests, do:
time make bench
Références :
- https://www.gnu.org/software/libc/manual/html_node/Configuring-and-compiling.html
- https://kazoo.ga/a-simple-tool-to-test-malloc-performance/
- https://github.com/f18m/malloc-benchmarks/blob/master/Makefile#L122-L129 :j'ai beaucoup appris en étudiant cette cible makefile :
$(glibc_install_dir)/lib/libc.so.6: @echo "Building GNU libc... go get a cup of coffee... this will take time!" mkdir -p $(glibc_build_dir) cd $(glibc_build_dir) && \ ../glibc/configure --prefix=$(glibc_install_dir) && \ make $(parallel_flags) && \ make install [ -x $(glibc_build_dir)/benchtests/bench-malloc-thread ] && echo "GNU libc benchmarking utility is ready!" || echo "Cannot find GNU libc benchmarking utility! Cannot collect benchmark results"
- Comment compiler ma propre bibliothèque standard glibc C à partir des sources et l'utiliser ?
Mots clés :comment créer et exécuter la glibc et ses bancs d'essai, incl. bancs d'essais malloc, à partir de la source ; construire glibc à partir des sources sur Linux Ubuntu
Le Makefile
va exister dans votre build-glibc
répertoire si le configure
le script se termine avec succès.
Si tout semble s'être bien passé pendant configure
et toujours pas de Makefile
, alors vous avez probablement raté une idiosyncrasie :
En faisant un configure
pour la glibc, on s'attend à ce que vous fournissiez normalement une alternative --prefix
, car l'installation à l'emplacement par défaut (/usr/local
) peut potentiellement paralyser le système. Si vous n'en fournissez pas, vous devez activer --disable-sanity-checks
.
Si ce n'est pas le cas non plus, recherchez un config.log
fichier et lire son contenu.
Configuration 1 :glibc sans GCC dédié
Cette configuration peut fonctionner et est rapide car elle ne recompile pas toute la chaîne d'outils GCC, juste glibc.
Le seul problème que j'ai avec cette configuration est que je n'ai pas trouvé de moyen agréable d'utiliser des objets d'exécution tels que crt1.o
, crti.o
, et crtn.o
fourni par notre glibc, et j'utilise ceux de l'hôte pour l'instant. Ceci est mentionné sur :https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location Ces objets effectuent une configuration précoce sur laquelle s'appuie la glibc, donc je ne serais pas surpris si les choses se plantaient de manière merveilleuse et des manières incroyablement subtiles. Voir les tentatives de résolution ci-dessous.
Compilez glibc et installez-le localement :
git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.32
mkdir build
cd build
export glibc_install="$(pwd)/install"
../configure --prefix "$glibc_install"
make -j `nproc`
make install -j `nproc`
Configuration 1 :vérifier la version
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);
}
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
Commande adaptée de https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location
Le programme affiche les éléments attendus :
gnu_get_libc_version() = 2.32
The atomic counter is 10000
The non-atomic counter is 8674
ldd
la sortie confirme que le ldd
et les bibliothèques 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)
Le gcc
la sortie de débogage de la 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 le contourner, 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 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 recompilez et réinstallez glibc, puis recompilez et relancez notre programme :
cd glibc/build
make -j `nproc`
make -j `nproc` install
./test_glibc.sh
et nous voyons hacked
imprimé plusieurs fois comme prévu.
Cela confirme en outre que nous avons effectivement utilisé la glibc que nous avons compilée et non celle de l'hôte.
Testé sur Ubuntu 20.10.
Configuration 1 :tentatives d'utilisation du bon crt*
objets
https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location suggère d'ajouter --sysroot
au gcc
commande mais :
- cela ne change pas vraiment les objets aux nôtres selon les logs
- et cela fait échouer la compilation avec
/usr/bin/ld: cannot find libgcc_s.so.1
probablement parce que lesysroot
est utilisé pour cet objet fourni par GCC, que nous n'avons pas à cette racine système car nous n'avons construit que la glibc
Sur https://stackoverflow.com/a/66634184/895245, ZeZNiQ fournit une solution de contournement qui est probablement correcte, en passant :
-nostartfiles
suivi de tous les objets. Il vous suffit d'extraire les bons objets de la commande complète avec -nostartfiles
et passez-les manuellement.
Par exemple, sur ma machine amd64, les objets utilisés étaient différents de sa commande 32 bits, donc c'est un peu fastidieux.
Bibliographie :
- Comment modifier le répertoire de recherche par défaut de GCC pour crti.o ?
- https://gcc.gnu.org/legacy-ml/gcc-help/2015-02/msg00016.html
- https://gcc.gnu.org/legacy-ml/gcc-help/2001-11/msg00029.html
Configuration 2 :configuration parfaite de crosstool-NG
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 pour autant que je puisse observer, y compris les objets d'exécution C tels que crt1.o
, crti.o
, et crtn.o
.
Dans cette configuration, nous allons compiler une chaîne d'outils GCC entièrement 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 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 pas parfaite car crosstool-NG ne prend pas en charge la construction des exécutables sans -Wl
supplémentaire flags, ce qui semble bizarre depuis que nous avons construit GCC lui-même. Mais tout semble fonctionner, donc ce n'est qu'un inconvénient.
Obtenez crosstool-NG, configurez-le et compilez-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
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
La construction prend environ trente minutes à deux heures.
La seule option de configuration obligatoire que je peux voir consiste à 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
qui m'affiche :
4.15.0-34-generic
donc en menuconfig
Je fais :
Operating System
Version of linux
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.
Configuration 2 :configurations facultatives
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, en menuconfig
faire :
C-library
Version of glibc
enregistrer le .config
, et continuez la construction.
Ou, si vous souhaitez utiliser votre propre source glibc, par ex. pour utiliser glibc depuis le dernier git, procédez comme ceci :
Paths and misc options
Try features marked as EXPERIMENTAL
:défini sur vrai
C-library
Source of glibc
Custom location
:dis ouiCustom location
Custom source location
:pointe vers un répertoire contenant votre source glibc
où glibc a été cloné 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 créé 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 bons objets d'exécution 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 crosstool-NG, comme expliqué ci-dessous.
Si vous venez de reconstruire ;
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
vos modifications apportées à l'emplacement source glibc personnalisé sont alors prises en compte, mais il construit tout à partir de zéro, ce qui le rend inutilisable pour le développement itératif.
Si nous le faisons :
./ct-ng list-steps
il 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 existe des étapes glibc entrelacées avec plusieurs étapes GCC, notamment libc_start_files
vient avant cc_core_pass_2
, qui est probablement l'étape la plus coûteuse avec cc_core_pass_1
.
Afin de construire une seule étape, vous devez d'abord définir le "Enregistrer les étapes intermédiaires" dans .config
option pour la version initiale :
Paths and misc options
Debug crosstool-NG
Save intermediate steps
et ensuite vous pouvez essayer :
env -u LD_LIBRARY_PATH time ./ct-ng libc+ -j`nproc`
mais malheureusement, le +
requis comme mentionné sur :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 donc, pas de bibliothèques de compilateur comme libstdc++ non plus).
et rend fondamentalement 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 du libc
l'étape n'a pas semblé recopier la source à partir de Custom source location
, rendant encore plus 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 GCC libstdc++ C++ ?
Ajout à la réponse/solution précédente de Ciro https://stackoverflow.com/a/52454710/4726668 :
@CiroSantilli La modification de votre réponse renvoie "La file d'attente de modification suggérée est pleine". Le script ldd que vous appelez dans le test_glibc.sh
le script pointe vers l'éditeur de liens dynamique de l'hôte :/home/ciro/glibc/build/install/lib/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007fc12f1b3000)
. Pour résoudre ce problème, en test_glibc.sh
, modifiez ldd
à ${glibc_install}/bin/ldd
. Cela vous demandera d'ajouter le crt
construit Fichiers *.o également au script :
-nostartfiles \
${glibc_install}/lib/crti.o \
${glibc_install}/lib/crtn.o \
${glibc_install}/lib/crt1.o \
Sur ma machine GNU/Linux i386/i686 (32-bit x86 arch), voici mon travail 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.so.2" \
-std=c11 \
-nostartfiles \
${glibc_install}/lib/crti.o \
${glibc_install}/lib/crtn.o \
${glibc_install}/lib/crt1.o \
-o test_glibc.out \
-v \
test_glibc.c \
-pthread \
;
${glibc_install}/bin/ldd ./test_glibc.out
./test_glibc.out