Setup 1: compila il tuo glibc senza GCC dedicato e usalo
Questa configurazione potrebbe funzionare ed è rapida in quanto non ricompila l'intera toolchain GCC, ma solo glibc.
Ma non è affidabile in quanto utilizza oggetti host di runtime C come crt1.o, crti.oe crtn.ofornito da glibc. Questo è menzionato in: https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location Quegli oggetti fanno una configurazione iniziale su cui glibc si affida, quindi non sarei sorpreso se le cose andassero meravigliosamente e modi incredibilmente sottili.
Per una configurazione più affidabile, vedere la Configurazione 2 di seguito.
Compilare glibc e installarlo 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`
Installazione 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 genera l'atteso:
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 --sysrootnon riuscito con:
cannot find /home/ciro/glibc/build/install/lib/libc.so.6 inside /home/ciro/glibc/build/install
così l'ho rimosso.
lddoutput conferma che le lddlibrerie 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' gccoutput di debug della compilazione mostra che sono stati utilizzati gli oggetti di runtime dell'host, il che è un male come menzionato 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
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)");
Quindi ricompilare e reinstallare glibc, quindi ricompilare ed eseguire nuovamente il nostro programma:
cd glibc/build
make -j `nproc`
make -j `nproc` install
./test_glibc.sh
e vediamo hackedstampato alcune volte come previsto.
Ciò conferma ulteriormente che abbiamo effettivamente utilizzato glibc che abbiamo compilato e non quello host.
Testato su Ubuntu 18.04.
Setup 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.oe crtn.o.
In questa configurazione, compileremo una toolchain GCC completamente dedicata che utilizza glibc che desideriamo.
L'unico aspetto negativo di questo metodo è che la compilazione richiederà più tempo. Ma non rischierei una configurazione di produzione con niente di meno.
crosstool-NG è un insieme di script che scarica e compila tutto dalla fonte per noi, inclusi GCC, glibc e binutils.
Sì, il sistema di compilazione GCC è così male che abbiamo bisogno di un progetto separato per quello.
Questa configurazione non è perfetta solo perché crosstool-NG non supporta la creazione di file eseguibili senza -Wlflag aggiuntivi , il che è strano da quando 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 creazione richiede circa trenta minuti o due ore.
L'unica opzione di configurazione obbligatoria che posso vedere è quella di far corrispondere la versione del kernel host per utilizzare le intestazioni del kernel corrette. Trova la versione del kernel host con:
uname -a
che mi mostra:
4.15.0-34-generic
quindi menuconfigfaccio:
quindi seleziono:
4.14.71
che è la prima versione uguale o precedente. Deve essere più vecchio poiché il kernel è retrocompatibile.
Setup 2: configurazioni opzionali
Quello .configche abbiamo generato con ./ct-ng x86_64-unknown-linux-gnuha:
CT_GLIBC_V_2_27=y
Per cambiarlo, menuconfigfai:
C-library
Version of glibc
salva il .config, e continua con la build.
Oppure, se si desidera utilizzare la propria sorgente glibc, ad esempio per utilizzare glibc dall'ultima versione di git, procedere in questo modo :
Paths and misc options
Try features marked as EXPERIMENTAL: impostato su vero
C-library
Source of glibc
Custom location: dì di si
Custom location
Custom source location: punta a una directory contenente la tua sorgente glibc
dove glibc è stato clonato come:
git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28
Installazione 2: provalo
Una volta creato il toolchain desiderato, provalo 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 nell'installazione 1, tranne per il fatto 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
Installazione 2: tentativo di ricompilazione glibc efficiente non riuscito
Non sembra possibile con crosstool-NG, come spiegato di seguito.
Se hai appena ricostruito;
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
quindi vengono prese in considerazione le modifiche alla posizione dell'origine 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.
pertanto, vediamo che ci sono passaggi glibc intrecciati con diversi passaggi GCC, in particolare libc_start_filesviene prima cc_core_pass_2, che è probabilmente il passaggio più costoso insieme cc_core_pass_1.
Per compilare un solo passaggio, devi prima impostare l' .configopzione "Salva passaggi intermedi" per la build iniziale:
e quindi puoi provare:
env -u LD_LIBRARY_PATH time ./ct-ng libc+ -j`nproc`
ma sfortunatamente, il + necessario come menzionato in: 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. Vale a dire, avrai una libc ricostruita, ma nessun compilatore finale creato con questa libc (e quindi nessuna libreria di compilatori come libstdc ++).
e sostanzialmente rende ancora la ricostruzione troppo lenta per essere fattibile per lo sviluppo, e non vedo come superarla senza patchare crosstool-NG.
Inoltre, a partire dal libcpassaggio non sembra copiare nuovamente l'origine daCustom source location , rendendo ulteriormente inutilizzabile questo metodo.
Bonus: stdlibc ++
Un vantaggio se sei interessato anche alla libreria standard C ++: Come modificare e ricostruire l'origine della libreria standard C ++ di GCC libstdc ++?