GNU/Linux >> Linux Esercitazione >  >> Linux

Come compilare la mia libreria standard C glibc dal sorgente e usarla?

Ultimo test su Ubuntu 20.04 con glibc versione di sviluppo 2.33.9000 (vedi glibc/version.h ) il 27 giugno 2021.

Come scaricare e creare glibc ed eseguirne i benchmark

Puoi ottenere manualmente il codice sorgente di glibc qui:https://www.gnu.org/software/libc/sources.html:

git clone https://sourceware.org/git/glibc.git
cd glibc
git checkout master

Mirror di terze parti su GitHub:https://github.com/bminor/glibc/tree/master/benchtests

Vedi anche:

  1. https://kazoo.ga/a-simple-tool-to-test-malloc-performance/

Se desideri compilare manualmente glibc e i suoi benchtest, procedi come segue:

# 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

Riferimenti:

  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 :Ho imparato molto di questo studiando questo target 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. Come compilare la mia libreria standard C glibc dai sorgenti e usarla?

Parole chiave:come costruire ed eseguire glibc e i suoi benchtest, incl. benchtest malloc, dalla fonte; costruire glibc dai sorgenti su linux ubuntu


Il Makefile esisterà nel tuo build-glibc directory se configure lo script termina correttamente.

Se tutto sembra essere andato liscio durante configure e ancora nessun Makefile , allora probabilmente ti sei perso un'idiosincrasia:

Mentre esegui un configure per glibc, è previsto che tu fornisca normalmente un --prefix alternativo , perché l'installazione nella posizione predefinita (/usr/local ) può potenzialmente paralizzare il sistema. Se non ne fornisci uno, devi attivare --disable-sanity-checks .

Se anche questo non è il caso, cerca un config.log file e leggerne il contenuto.


Setup 1:glibc senza GCC dedicato

Questa configurazione potrebbe funzionare ed è veloce in quanto non ricompila l'intera toolchain GCC, solo glibc.

L'unico problema che ho con questa configurazione è che non ho trovato un modo carino per usare oggetti di runtime come crt1.o , crti.o e crtn.o forniti dalla nostra glibc, e per ora sto usando quelli dell'host. Questo è menzionato in:https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location Questi oggetti eseguono la configurazione iniziale su cui si basa glibc, quindi non sarei sorpreso se le cose andassero in crash in modo meraviglioso e modi incredibilmente sottili. Vedi i tentativi per risolverlo di seguito.

Compila glibc e installa localmente:

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`

Setup 1:verifica la 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);
}

Compila ed esegui con 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

Comando adattato da https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location

Il programma produce l'atteso:

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

ldd l'output conferma che ldd e le librerie che abbiamo appena creato vengono effettivamente utilizzate come previsto:

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

Il gcc l'output di debug della compilazione mostra che sono stati utilizzati i miei oggetti di runtime host, il che è negativo come accennato in precedenza, ma non so come aggirarlo, ad es. contiene:

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

Setup 1:modifica glibc

Ora modifichiamo glibc con:

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

Poi ricompila e reinstalla glibc, e ricompila e riesegui il nostro programma:

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

e vediamo hacked stampato alcune volte come previsto.

Ciò conferma ulteriormente che abbiamo effettivamente utilizzato la glibc che abbiamo compilato e non quella dell'host.

Testato su Ubuntu 20.10.

Setup 1:tenta di utilizzare il crt* corretto oggetti

https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location suggerisce di aggiungere --sysroot al gcc comando ma:

  • in realtà non cambia gli oggetti in nostri secondo i log
  • e fa fallire la compilazione con /usr/bin/ld: cannot find libgcc_s.so.1 presumibilmente perché il sysroot viene utilizzato per questo oggetto fornito da GCC, che non abbiamo in quel sysroot perché abbiamo creato solo glibc

Su https://stackoverflow.com/a/66634184/895245 ZeZNiQ fornisce una soluzione probabilmente corretta, passando:

-nostartfiles

seguito da tutti gli oggetti. Devi solo estrarre gli oggetti corretti dal comando completo con -nostartfiles e passali manualmente.

Ad esempio, sulla mia macchina amd64, gli oggetti utilizzati erano diversi dal suo comando a 32 bit, quindi è un po' complicato.

Bibliografia:

  • Come faccio a cambiare la directory di ricerca predefinita di GCC per 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

Configurazione 2:configurazione originaria di crosstool-NG

Questa è un'alternativa alla configurazione 1, ed è la configurazione più corretta che ho raggiunto finora:tutto è corretto per quanto posso osservare, inclusi gli oggetti di runtime C come crt1.o , crti.o e crtn.o .

In questa configurazione, compileremo una toolchain GCC completamente dedicata che utilizza la glibc che vogliamo.

L'unico aspetto negativo di questo metodo è che la compilazione richiederà più tempo. Ma non rischierei una configurazione di produzione con qualcosa di meno.

crosstool-NG è un set di script che scarica e compila per noi tutto dal sorgente, inclusi GCC, glibc e binutils.

Sì, il sistema di compilazione di GCC è così pessimo che abbiamo bisogno di un progetto separato per questo.

Questa configurazione non è perfetta solo perché crosstool-NG non supporta la compilazione degli eseguibili senza -Wl extra flags, il che sembra strano dato che abbiamo creato GCC stesso. Ma tutto sembra funzionare, quindi questo è solo un inconveniente.

Ottieni crosstool-NG, configuralo e crealo:

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 compilazione richiede da trenta minuti a due ore.

L'unica opzione di configurazione obbligatoria che posso vedere è farla corrispondere alla versione del kernel dell'host per utilizzare le intestazioni del kernel corrette. Trova la tua versione del kernel host con:

uname -a

che mi mostra:

4.15.0-34-generic

quindi in menuconfig Sì:

  • Operating System
    • Version of linux

quindi seleziono:

4.14.71

che è la prima versione uguale o precedente. Deve essere più vecchio poiché il kernel è compatibile con le versioni precedenti.

Setup 2:configurazioni opzionali

Il .config che abbiamo generato con ./ct-ng x86_64-unknown-linux-gnu ha:

CT_GLIBC_V_2_27=y

Per cambiarlo, in menuconfig fare:

  • C-library
  • Version of glibc

salva il .config e continua con la compilazione.

Oppure, se vuoi usare la tua fonte glibc, ad es. per usare glibc dall'ultimo git, procedi così:

  • Paths and misc options
    • Try features marked as EXPERIMENTAL :impostato su true
  • C-library
    • Source of glibc
      • Custom location :dire di sì
      • Custom location
        • Custom source location :punta a una directory contenente il tuo sorgente di glibc

dove glibc è stato clonato come:

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

Configurazione 2:provalo

Dopo aver creato la toolchain che desideri, provala con:

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

Tutto sembra funzionare come in Setup 1, tranne per il fatto che ora sono stati utilizzati gli oggetti di runtime corretti:

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:tentativo di ricompilazione efficiente di glibc fallito

Non sembra possibile con crosstool-NG, come spiegato di seguito.

Se ricostruisci semplicemente;

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

quindi vengono prese in considerazione le tue modifiche alla posizione di origine della glibc personalizzata, ma crea tutto da zero, rendendolo inutilizzabile per lo sviluppo iterativo.

Se lo facciamo:

./ct-ng list-steps

fornisce una bella panoramica dei passaggi della creazione:

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.

pertanto, vediamo che ci sono passaggi di glibc intrecciati con diversi passaggi GCC, in particolare libc_start_files viene prima di cc_core_pass_2 , che è probabilmente il passaggio più costoso insieme a cc_core_pass_1 .

Per costruire un solo passaggio, devi prima impostare "Salva passaggi intermedi" in .config opzione per la build iniziale:

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

e poi puoi provare:

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

ma sfortunatamente, il + richiesto come indicato su:https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536

Si noti tuttavia che il riavvio in un passaggio intermedio reimposta la directory di installazione allo stato che aveva durante tale passaggio. Cioè, avrai una libc ricostruita, ma nessun compilatore finale compilato con questa libc (e quindi, nemmeno librerie di compilatori come libstdc++).

e fondamentalmente rende ancora la ricostruzione troppo lenta per essere fattibile per lo sviluppo, e non vedo come superare questo problema senza patchare crosstool-NG.

Inoltre, partendo dal libc step non sembrava copiare di nuovo la fonte da Custom source location , rendendo ulteriormente questo metodo inutilizzabile.

Bonus:stdlibc++

Un bonus se sei interessato anche alla libreria standard C++:come modificare e ricostruire il sorgente della libreria standard C++ GCC libstdc++?


Aggiungendo alla precedente risposta/soluzione di Ciro https://stackoverflow.com/a/52454710/4726668 :

@CiroSantilli La modifica della risposta restituisce "La coda di modifica suggerita è piena". Lo script ldd che stai chiamando nel test_glibc.sh lo script punta al linker dinamico dell'host:/home/ciro/glibc/build/install/lib/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007fc12f1b3000) . Per risolvere questo problema, in test_glibc.sh , cambia ldd a ${glibc_install}/bin/ldd . Ciò richiederà l'aggiunta del crt compilato *.o allo script:

-nostartfiles \
${glibc_install}/lib/crti.o \
${glibc_install}/lib/crtn.o \
${glibc_install}/lib/crt1.o \

Sulla mia macchina GNU/Linux i386/i686 (arch x86 a 32 bit), di seguito è riportato il mio test_glibc.sh funzionante :

#!/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. Come compilare e installare FFmpeg dal sorgente in CentOS/RHEL?

  2. Quando si dovrebbe compilare e installare dal sorgente?

  3. Come installare e configurare Nginx dal sorgente su Linux

  4. Come compilare e installare software dal codice sorgente su Linux

  5. Come installare Apache CouchDB su CentOS 6 (da Source ed EPEL)

Come costruire Nginx dal sorgente su Debian 9

Come costruire Nginx dal sorgente su CentOS 7

Come creare pacchetti Debian dal sorgente

Come installare il software dal codice sorgente... e rimuoverlo in seguito

Come compilare il kernel Linux dal sorgente per creare un kernel personalizzato

Come installare TBB dal sorgente su Linux e farlo funzionare