Configuración 1: compila tu propio glibc sin GCC dedicado y úsalo
Esta configuración podría funcionar y es rápida ya que no recompila toda la cadena de herramientas de GCC, solo glibc.
Pero no es fiable, ya que utiliza anfitrión tiempo de ejecución C objetos tales como crt1.o
, crti.o
y crtn.o
proporcionado por glibc. Esto se menciona en: https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location Esos objetos realizan una configuración temprana en la que se basa Glibc, por lo que no me sorprendería si las cosas se estrellaran de maravilla y formas asombrosamente sutiles.
Para una configuración más confiable, vea la Configuración 2 a continuación.
Construya glibc e instálelo 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`
Configuración 1: verifique la compilación
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 y ejecuta 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
El programa genera lo esperado:
gnu_get_libc_version() = 2.28
The atomic counter is 10000
The non-atomic counter is 8674
Comando adaptado de https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location pero --sysroot
falló con:
cannot find /home/ciro/glibc/build/install/lib/libc.so.6 inside /home/ciro/glibc/build/install
así que lo quité
ldd
La salida confirma que las ldd
bibliotecas y que acabamos de construir se están utilizando realmente como se esperaba:
+ 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)
El gcc
resultado de la depuración de compilación muestra que se usaron mis objetos de tiempo de ejecución de host, lo cual es malo como se mencionó anteriormente, pero no sé cómo solucionarlo, por ejemplo, contiene:
COLLECT_GCC_OPTIONS=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o
Configuración 1: modificar glibc
Ahora modifiquemos 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)");
Luego recompile y reinstale glibc, y recompile y vuelva a ejecutar nuestro programa:
cd glibc/build
make -j `nproc`
make -j `nproc` install
./test_glibc.sh
y vemos hacked
impreso algunas veces como se esperaba.
Esto confirma aún más que en realidad usamos el glibc que compilamos y no el host.
Probado en Ubuntu 18.04.
Configuración 2: configuración prístina de crosstool-NG
Esta es una alternativa a la configuración 1, y es la configuración más correcta que he logrado mucho: todo es correcto por lo que yo puedo observar, incluyendo el tiempo de ejecución C objetos tales como crt1.o
, crti.o
ycrtn.o
.
En esta configuración, compilaremos una cadena de herramientas GCC dedicada que use el glibc que queramos.
El único inconveniente de este método es que la construcción llevará más tiempo. Pero no arriesgaría una configuración de producción con nada menos.
crosstool-NG es un conjunto de scripts que descarga y compila todo de origen para nosotros, incluidos GCC, glibc y binutils.
Sí, el sistema de compilación de GCC es tan malo que necesitamos un proyecto separado para eso.
Esta configuración no es perfecta porque crosstool-NG no admite la construcción de ejecutables sin -Wl
banderas adicionales , lo que se siente extraño ya que hemos creado GCC. Pero todo parece funcionar, así que esto es solo un inconveniente.
Obtenga crosstool-NG, configúrelo y compílelo:
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 construcción lleva entre treinta minutos y dos horas.
La única opción de configuración obligatoria que puedo ver es hacer que coincida con la versión del núcleo del host para utilizar los encabezados del núcleo correctos. Encuentre su versión de kernel host con:
uname -a
que me muestra
4.15.0-34-generic
entonces en lo menuconfig
que hago:
entonces selecciono:
4.14.71
cual es la primera versión igual o anterior. Tiene que ser anterior ya que el núcleo es compatible con versiones anteriores.
Configuración 2: configuraciones opcionales
El .config
que generamos con ./ct-ng x86_64-unknown-linux-gnu
tiene:
CT_GLIBC_V_2_27=y
Para cambiar eso, en menuconfig
do:
C-library
Version of glibc
salva el .config
y continúe con la compilación.
O, si desea usar su propia fuente de glibc, por ejemplo, para usar glibc del último git, proceda de esta manera :
Paths and misc options
Try features marked as EXPERIMENTAL
: establecido en verdadero
C-library
Source of glibc
Custom location
: decir que sí
Custom location
Custom source location
: apunte a un directorio que contenga su fuente glibc
donde glibc fue clonado como:
git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28
Configuración 2: pruébelo
Una vez que haya creado la cadena de herramientas que desea, pruébela 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
Todo parece funcionar como en la Configuración 1, excepto que ahora se usaron los objetos de tiempo de ejecución correctos:
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
Configuración 2: intento de recompilación glibc eficiente fallido
No parece posible con crosstool-NG, como se explica a continuación.
Si solo reconstruyes;
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
entonces sus cambios en la ubicación de origen de glibc personalizada se tienen en cuenta, pero construye todo desde cero, por lo que es inutilizable para el desarrollo iterativo.
Si lo hacemos:
./ct-ng list-steps
da una buena visión general de los pasos de compilación:
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.
por lo tanto, vemos que hay pasos glibc entrelazados con varios pasos GCC, más notablemente libc_start_files
antescc_core_pass_2
, que es probablemente el paso más costoso junto con cc_core_pass_1
.
Para compilar solo un paso, primero debe establecer la .config
opción "Guardar pasos intermedios" en la compilación inicial:
y luego puedes probar:
env -u LD_LIBRARY_PATH time ./ct-ng libc+ -j`nproc`
pero desafortunadamente, lo +
requerido como se menciona en: https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536
Sin embargo, tenga en cuenta que reiniciar en un paso intermedio restablece el directorio de instalación al estado que tenía durante ese paso. Es decir, tendrá una libc reconstruida, pero ningún compilador final construido con esta libc (y, por lo tanto, tampoco bibliotecas de compiladores como libstdc ++).
y, básicamente, todavía hace que la reconstrucción sea demasiado lenta para ser factible para el desarrollo, y no veo cómo superar esto sin parchear crosstool-NG.
Además, comenzar desde el libc
paso no parecía volver a copiar la fuente Custom source location
, lo que hace que este método sea inutilizable.
Bono: stdlibc ++
Una ventaja adicional si también está interesado en la biblioteca estándar de C ++: ¿Cómo editar y reconstruir la fuente de la biblioteca estándar GCC libstdc ++ C ++?