GNU/Linux >> Tutoriels Linux >  >> Linux

Comment compiler ma propre bibliothèque standard glibc C à partir des sources et l'utiliser ?

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 :

  1. 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 :

  1. https://www.gnu.org/software/libc/manual/html_node/Configuring-and-compiling.html
  2. https://kazoo.ga/a-simple-tool-to-test-malloc-performance/
  3. 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"
    
  4. 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 le sysroot 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 oui
      • Custom 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

Linux
  1. Comment compiler et installer Python3.5 et Python-pip à partir de la source sur CentOS

  2. Comment compiler à partir des sources et installer Nginx dans un VPS Debian 7 (Wheezy)

  3. Comment installer MongoDB à partir de la source (et en utilisant YUM) sous Linux

  4. Comment installer TBB à partir de la source sur Linux et le faire fonctionner

  5. Comment construire et installer GLFW 3 et l'utiliser dans un projet Linux

Comment construire Nginx à partir de la source sur Debian 9

Comment construire Nginx à partir de la source sur CentOS 7

Comment construire des paquets Debian à partir de la source

Comment installer un logiciel à partir du code source… et le supprimer ensuite

Comment compiler le noyau Linux à partir de la source pour créer un noyau personnalisé

Comment utiliser sched_getaffinity et sched_setaffinity sous Linux à partir de C ?