Konfiguracja 1: skompiluj własną bibliotekę glibc bez dedykowanego GCC i używaj jej
Ta konfiguracja może działać i jest szybka, ponieważ nie rekompiluje ponownie całego zestawu narzędzi GCC, tylko glibc.
Ale to nie jest wiarygodne, ponieważ korzysta gospodarz C Runtime obiektów takich jak crt1.o
, crti.o
i crtn.o
dostarczane przez glibc. Wspomina się o tym pod adresem : https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location Te obiekty wykonują wczesną konfigurację, na której opiera się glibc, więc nie zdziwiłbym się, gdyby coś się zawiesiło i niesamowicie subtelne sposoby.
Aby uzyskać bardziej niezawodną konfigurację, zobacz Konfiguracja 2 poniżej.
Skompiluj glibc i zainstaluj lokalnie:
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`
Konfiguracja 1: sprawdź kompilację
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);
}
Skompiluj i uruchom z 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
Program daje oczekiwane:
gnu_get_libc_version() = 2.28
The atomic counter is 10000
The non-atomic counter is 8674
Polecenie zaadaptowano z https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location, ale --sysroot
zawiodło:
cannot find /home/ciro/glibc/build/install/lib/libc.so.6 inside /home/ciro/glibc/build/install
więc go usunąłem.
ldd
dane wyjściowe potwierdzają, że ldd
biblioteki i, które właśnie zbudowaliśmy, są używane zgodnie z oczekiwaniami:
+ 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)
Wynik gcc
debugowania kompilacji pokazuje, że zostały użyte moje obiekty wykonawcze hosta, co jest złe, jak wspomniano wcześniej, ale nie wiem, jak to obejść, np. Zawiera:
COLLECT_GCC_OPTIONS=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o
Konfiguracja 1: zmodyfikuj glibc
Teraz zmodyfikujmy glibc za pomocą:
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)");
Następnie ponownie skompiluj i ponownie zainstaluj glibc oraz ponownie skompiluj i ponownie uruchom nasz program:
cd glibc/build
make -j `nproc`
make -j `nproc` install
./test_glibc.sh
i widzimy hacked
wydrukowane kilka razy zgodnie z oczekiwaniami.
To dodatkowo potwierdza, że faktycznie używaliśmy skompilowanego glibc, a nie hosta.
Testowane na Ubuntu 18.04.
Konfiguracja 2: nieskazitelna konfiguracja crosstool-NG
Jest to alternatywa dla konfiguracji 1, i to jest najbardziej poprawna konfiguracja Mam osiągnąć daleko: wszystko jest w porządku o ile mogę obserwować, w tym C wykonywania obiektów, takich jak crt1.o
, crti.o
icrtn.o
.
W tej konfiguracji skompilujemy w pełni dedykowany łańcuch narzędzi GCC, który używa potrzebnego glibc.
Jedyną wadą tej metody jest to, że kompilacja potrwa dłużej. Ale nie ryzykowałbym konfiguracji produkcyjnej z czymś mniejszym.
crosstool-NG to zestaw skryptów, które pobierają i kompilują wszystko ze źródła dla nas, w tym GCC, glibc i binutils.
Tak, system kompilacji GCC jest tak zły, że potrzebujemy do tego osobnego projektu.
Ta konfiguracja nie jest idealna tylko dlatego, że crosstool-NG nie obsługuje budowania plików wykonywalnych bez dodatkowych -Wl
flag , co wydaje się dziwne, ponieważ stworzyliśmy samo GCC. Ale wydaje się, że wszystko działa, więc jest to tylko niedogodność.
Pobierz crosstool-NG, skonfiguruj i zbuduj:
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`
Budowa trwa od około trzydziestu minut do dwóch godzin.
Jedyną obowiązkową opcją konfiguracyjną, jaką widzę, jest dopasowanie do wersji jądra hosta, aby używać poprawnych nagłówków jądra. Znajdź wersję jądra hosta za pomocą:
uname -a
co pokazuje mi:
4.15.0-34-generic
więc w menuconfig
robię:
więc wybieram:
4.14.71
która jest pierwszą równą lub starszą wersją. Musi być starszy, ponieważ jądro jest kompatybilne wstecz.
Konfiguracja 2: opcjonalne konfiguracje
Ten .config
, który wygenerowaliśmy, ./ct-ng x86_64-unknown-linux-gnu
ma:
CT_GLIBC_V_2_27=y
Aby to zmienić, menuconfig
wykonaj:
C-library
Version of glibc
zapisz plik .config
i kontynuuj tworzenie.
Lub, jeśli chcesz użyć własnego źródła glibc, np. Użyć glibc z najnowszego gita, postępuj w ten sposób :
Paths and misc options
Try features marked as EXPERIMENTAL
: ustawione na true
C-library
Source of glibc
Custom location
: Powiedz tak
Custom location
Custom source location
: wskazuje katalog zawierający twoje źródło glibc
gdzie glibc został sklonowany jako:
git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28
Konfiguracja 2: przetestuj
Po zbudowaniu odpowiedniego łańcucha narzędzi przetestuj go za pomocą:
#!/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
Wygląda na to, że wszystko działa tak, jak w Instalatorze 1, z wyjątkiem tego, że teraz zostały użyte poprawne obiekty wykonawcze:
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
Instalacja 2: nieudana próba wydajnej ponownej kompilacji glibc
Nie wydaje się to możliwe w przypadku crosstool-NG, jak wyjaśniono poniżej.
Jeśli po prostu przebudujesz;
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
wtedy twoje zmiany w niestandardowej lokalizacji źródłowej glibc są brane pod uwagę, ale buduje wszystko od zera, przez co nie nadaje się do iteracyjnego rozwoju.
Jeśli zrobimy:
./ct-ng list-steps
daje ładny przegląd kroków kompilacji:
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.
dlatego widzimy, że istnieją kroki glibc przeplatane z kilkoma krokami GCC, w szczególności libc_start_files
występuje wcześniej cc_core_pass_2
, co jest prawdopodobnie najdroższym krokiem razem zcc_core_pass_1
.
Aby zbudować tylko jeden krok, musisz najpierw ustawić .config
opcję „Zapisz kroki pośrednie” dla początkowej kompilacji:
a potem możesz spróbować:
env -u LD_LIBRARY_PATH time ./ct-ng libc+ -j`nproc`
ale niestety +
wymagane, jak wspomniano na: https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536
Należy jednak pamiętać, że ponowne uruchomienie w kroku pośrednim powoduje zresetowanie katalogu instalacyjnego do stanu, jaki miał podczas tego kroku. Oznacza to, że będziesz mieć przebudowaną bibliotekę libc - ale nie będzie ostatecznego kompilatora zbudowanego z tą biblioteką (i stąd też nie będzie żadnych bibliotek kompilatorów takich jak libstdc ++).
i zasadniczo nadal sprawia, że przebudowa jest zbyt wolna, aby była możliwa do opracowania, i nie widzę, jak to przezwyciężyć bez łatania crosstool-NG.
Co więcej, rozpoczęcie od libc
kroku nie Custom source location
powodowało ponownego kopiowania źródła z , co dodatkowo czyniło tę metodę bezużyteczną.
Bonus: stdlibc ++
Bonus, jeśli interesuje Cię również standardowa biblioteka C ++: Jak edytować i przebudowywać standardowe źródło biblioteki GCC libstdc ++ C ++?