Setup 1: compila la tua glibc senza GCC dedicato e usala
Poiché sembra impossibile fare solo con gli hack di versioning dei simboli, facciamo un ulteriore passo avanti e compiliamo glibc da soli.
Questa configurazione potrebbe funzionare ed è veloce in quanto non ricompila l'intera toolchain di GCC, ma solo glibc.
Ma non è affidabile in quanto utilizza oggetti host di runtime C come crt1.o
, crti.o
e crtn.o
fornito da glibc. Questo è menzionato su: https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location Quegli oggetti eseguono una configurazione iniziale su cui si basa glibc, quindi non sarei sorpreso se le cose andassero in crash in modo meraviglioso e modi incredibilmente sottili.
Per una configurazione più affidabile, vedere Configurazione 2 di seguito.
Crea glibc e installa localmente:
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`
Configurazione 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.
* /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);
}
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
Il programma produce quanto previsto:
gnu_get_libc_version() = 2.28
The atomic counter is 10000
The non-atomic counter is 8674
Comando adattato da https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location ma --sysroot
non riuscito con:
cannot find /home/ciro/glibc/build/install/lib/libc.so.6 inside /home/ciro/glibc/build/install
quindi l'ho rimosso.
ldd
l'output conferma che le ldd
librerie e 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)
L' gcc
output di debug della compilazione mostra che sono stati utilizzati i miei oggetti runtime host, il che è negativo come accennato in precedenza, ma non so come aggirarlo, ad esempio contiene:
COLLECT_GCC_OPTIONS=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o
Configurazione 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)");
Quindi ricompilare e reinstallare glibc, quindi ricompilare e rieseguire il nostro programma:
cd glibc/build
make -j `nproc`
make -j `nproc` install
./test_glibc.sh
e vediamo hacked
stampato un paio di volte come previsto.
Ciò conferma ulteriormente che abbiamo effettivamente utilizzato la glibc che abbiamo compilato e non quella host.
Testato su Ubuntu 18.04.
Configurazione 2: configurazione incontaminata di crosstool-NG
Questa è un'alternativa alla messa a punto 1, ed è la configurazione più corretta che ho raggiunto fino ad ora: tutto è corretto, per quanto posso osservare, compresi gli oggetti il 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 svantaggio di questo metodo è che la compilazione richiederà più tempo. Ma non rischierei una configurazione di produzione con qualcosa di meno.
crosstool-NG è un insieme di script che scarica e compila tutto dal sorgente per noi, 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 perché crosstool-NG non supporta la creazione di eseguibili senza -Wl
flag aggiuntivi , il che sembra strano dato che abbiamo creato GCC stesso. Ma tutto sembra funzionare, quindi questo è solo un inconveniente.
Ottieni crosstool-NG e configuralo:
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
L'unica opzione obbligatoria che posso vedere è far corrispondere la versione del kernel 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
così in menuconfig
io:
quindi seleziono:
4.14.71
che è la prima versione uguale o precedente. Deve essere più vecchio poiché il kernel è retrocompatibile.
Ora puoi costruire con:
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
e ora attendi da trenta minuti a due ore per la compilazione.
Setup 2: configurazioni opzionali
Quello .config
che abbiamo generato con ./ct-ng x86_64-unknown-linux-gnu
ha:
CT_GLIBC_V_2_27=y
Per cambiarlo, in menuconfig
do:
C-library
Version of glibc
salva il .config
e continua con la compilazione.
Oppure, se vuoi usare il tuo sorgente glibc, ad esempio per usare glibc dall'ultimo git, procedi in questo modo :
Paths and misc options
Try features marked as EXPERIMENTAL
: impostato su true
C-library
Source of glibc
Custom location
: dì di si
Custom location
Custom source location
: punta a una directory contenente il tuo sorgente 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 costruito 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 che ora sono stati utilizzati gli oggetti 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
Configurazione 2: tentativo di ricompilazione efficiente di glibc fallito
Non sembra possibile con crosstool-NG, come spiegato di seguito.
Se ricostruisci solo;
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
quindi vengono prese in considerazione le modifiche alla posizione dei sorgenti di glibc personalizzata, ma costruisce tutto da zero, rendendolo inutilizzabile per lo sviluppo iterativo.
Se lo facciamo:
./ct-ng list-steps
offre una bella panoramica dei passaggi di costruzione:
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.
quindi, vediamo che ci sono passaggi glibc intrecciati con diversi passaggi GCC, in particolare libc_start_files
viene prima cc_core_pass_2
, che è probabilmente il passaggio più costoso insieme a cc_core_pass_1
.
Per creare un solo passaggio, devi prima impostare l' .config
opzione "Salva passaggi intermedi" per la build iniziale:
e poi puoi provare:
env -u LD_LIBRARY_PATH time ./ct-ng libc+ -j`nproc`
ma sfortunatamente, il +
richiesto come menzionato su: https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536
Si noti tuttavia che il riavvio in una fase intermedia reimposta la directory di installazione allo stato che aveva durante quella fase. Cioè, avrai una libc ricostruita, ma nessun compilatore finale compilato con questa libc (e quindi, nemmeno le librerie del compilatore come libstdc ++).
e fondamentalmente rende ancora la ricostruzione troppo lenta per essere fattibile per lo sviluppo, e non vedo come superare questo problema senza applicare patch a crosstool-NG.
Inoltre, a partire dal libc
passaggio non sembra che si copi nuovamente la sorgente da Custom source location
, rendendo ulteriormente inutilizzabile questo metodo.
Bonus: stdlibc ++
Un bonus se sei interessato anche alla libreria standard C ++: come modificare e ricostruire il sorgente della libreria standard GCC libstdc ++ C ++?