Setup 1: Kompilieren Sie Ihren eigenen glibc ohne dedizierten GCC und verwenden Sie ihn
Da es unmöglich erscheint, nur mit Hacks zur Symbolversionierung zu arbeiten, gehen wir noch einen Schritt weiter und kompilieren glibc selbst.
Dieses Setup funktioniert möglicherweise und ist schnell, da nicht die gesamte GCC-Toolchain neu kompiliert wird, sondern nur glibc.
Aber es ist nicht zuverlässig , wie es Host - C - Laufzeit - Objekte verwendet wie crt1.o
, crti.o
und crtn.o
von glibc zur Verfügung gestellt. Dies wird erwähnt unter: https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location Diese Objekte führen eine frühe Einrichtung durch, auf die sich glibc stützt. Ich wäre also nicht überrascht, wenn die Dinge wunderbar abstürzen würden und unglaublich subtile Wege.
Eine zuverlässigere Einrichtung finden Sie unter Setup 2 unten.
Erstellen Sie glibc und installieren Sie es lokal:
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`
Setup 1: Überprüfen Sie den 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);
}
Kompilieren und ausführen mit 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
Das Programm gibt Folgendes aus:
gnu_get_libc_version() = 2.28
The atomic counter is 10000
The non-atomic counter is 8674
Befehl angepasst von https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location, aber --sysroot
fehlgeschlagen mit:
cannot find /home/ciro/glibc/build/install/lib/libc.so.6 inside /home/ciro/glibc/build/install
also habe ich es entfernt.
ldd
Die Ausgabe bestätigt, dass die ldd
und Bibliotheken, die wir gerade erstellt haben, tatsächlich wie erwartet verwendet werden:
+ 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)
Die gcc
Debug-Ausgabe der Kompilierung zeigt, dass meine Host-Laufzeitobjekte verwendet wurden, was, wie bereits erwähnt, schlecht ist, aber ich weiß nicht, wie ich es umgehen soll, z. B. enthält es:
COLLECT_GCC_OPTIONS=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o
Setup 1: Glibc ändern
Jetzt ändern wir glibc mit:
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)");
Dann kompilieren Sie glibc neu und installieren Sie es erneut. Kompilieren Sie unser Programm neu und führen Sie es erneut aus:
cd glibc/build
make -j `nproc`
make -j `nproc` install
./test_glibc.sh
und wir sehen hacked
einige Male wie erwartet gedruckt.
Dies bestätigt weiter, dass wir tatsächlich den von uns kompilierten Glibc verwendet haben und nicht den Host.
Getestet unter Ubuntu 18.04.
Setup 2: Crosstool-NG makelloses Setup
Dies ist eine Alternative zur Einrichtung 1, und es ist die richtige Setup ich bisher erreicht habe: alles korrekt ist, soweit ich beobachten kann, einschließlich der C - Laufzeit - Objekte wie crt1.o
, crti.o
, und crtn.o
.
In diesem Setup kompilieren wir eine vollständige dedizierte GCC-Toolchain, die den gewünschten glibc verwendet.
Der einzige Nachteil dieser Methode ist, dass der Build länger dauert. Aber ich würde kein Produktionssetup mit weniger riskieren.
crosstool-NG ist eine Reihe von Skripten, die alles von der Quelle für uns herunterladen und kompilieren, einschließlich GCC, glibc und binutils.
Ja, das GCC-Build-System ist so schlecht, dass wir dafür ein separates Projekt benötigen.
Dieses Setup ist nur nicht perfekt, da Crosstool-NG das Erstellen der ausführbaren Dateien ohne zusätzliche -Wl
Flags nicht unterstützt , was sich seltsam anfühlt, da wir GCC selbst erstellt haben. Aber alles scheint zu funktionieren, daher ist dies nur eine Unannehmlichkeit.
Holen Sie sich Crosstool-NG und konfigurieren Sie es:
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
Die einzige obligatorische Option, die ich sehen kann, besteht darin, sie an Ihre Host-Kernel-Version anzupassen, um die richtigen Kernel-Header zu verwenden. Finden Sie Ihre Host-Kernel-Version mit:
uname -a
was mir zeigt:
4.15.0-34-generic
also menuconfig
mache ich:
also wähle ich:
4.14.71
Dies ist die erste gleiche oder ältere Version. Es muss älter sein, da der Kernel abwärtskompatibel ist.
Jetzt können Sie bauen mit:
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
und warten Sie nun etwa dreißig Minuten bis zwei Stunden für die Zusammenstellung.
Setup 2: optionale Konfigurationen
Das .config
, mit dem wir generiert haben, ./ct-ng x86_64-unknown-linux-gnu
hat:
CT_GLIBC_V_2_27=y
Um dies zu ändern, menuconfig
tun Sie Folgendes:
C-library
Version of glibc
Speichern Sie die .config
und fahren Sie mit dem Build fort.
Oder, wenn Sie Ihre eigene glibc Quelle verwenden möchten, zB glibc aus dem aktuellen git zu verwenden, gehen Sie wie folgt aus :
Paths and misc options
Try features marked as EXPERIMENTAL
: auf true setzen
C-library
Source of glibc
Custom location
: Sag ja
Custom location
Custom source location
: Zeigen Sie auf ein Verzeichnis, das Ihre Glibc-Quelle enthält
wo glibc geklont wurde als:
git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28
Setup 2: Testen Sie es
Sobald Sie die gewünschte Toolchain erstellt haben, testen Sie sie mit:
#!/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
Alles scheint wie in Setup 1 zu funktionieren, außer dass jetzt die richtigen Laufzeitobjekte verwendet wurden:
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: Fehlgeschlagener effizienter Glibc-Neukompilierungsversuch
Mit Crosstool-NG scheint dies nicht möglich zu sein, wie unten erläutert.
Wenn Sie nur neu erstellen;
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
Dann werden Ihre Änderungen am benutzerdefinierten Glibc-Quellspeicherort berücksichtigt, aber alles wird von Grund auf neu erstellt, sodass es für die iterative Entwicklung unbrauchbar wird.
Wenn wir es tun:
./ct-ng list-steps
Es gibt einen schönen Überblick über die Build-Schritte:
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.
Daher sehen wir, dass es glibc-Schritte gibt, die mit mehreren GCC-Schritten verflochten sind, insbesondere libc_start_files
vorher cc_core_pass_2
, was wahrscheinlich der teuerste Schritt zusammen mit ist cc_core_pass_1
.
Um nur einen Schritt zu erstellen, müssen Sie zuerst die .config
Option "Zwischenschritte speichern" in der Option für den ersten Build festlegen:
und dann können Sie versuchen:
env -u LD_LIBRARY_PATH time ./ct-ng libc+ -j`nproc`
aber leider +
erforderlich wie unter https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536 angegeben
Beachten Sie jedoch, dass durch einen Neustart in einem Zwischenschritt das Installationsverzeichnis auf den Status zurückgesetzt wird, den es während dieses Schritts hatte. Das heißt, Sie werden eine neu erstellte libc haben - aber keinen endgültigen Compiler, der mit dieser libc erstellt wurde (und daher auch keine Compiler-Bibliotheken wie libstdc ++).
und im Grunde macht der Wiederaufbau immer noch zu langsam, um für die Entwicklung machbar zu sein, und ich sehe nicht, wie ich dies überwinden kann, ohne Crosstool-NG zu patchen.
Darüber hinaus libc
schien das Starten ab dem Schritt nicht erneut über die Quelle zu kopieren Custom source location
, was diese Methode weiter unbrauchbar machte.
Bonus: stdlibc ++
Ein Bonus, wenn Sie auch an der C ++ - Standardbibliothek interessiert sind: Wie kann ich die GCC libstdc ++ C ++ - Standardbibliotheksquelle bearbeiten und neu erstellen?