actualizar - update glibc
¿Cómo construir un programa en C usando una versión personalizada de glibc y enlaces estáticos? (3)
Construí glibc 2.14 y lo instalé en el directorio ~/GLIBC/glibc_install
. Ahora quiero compilar y ejecutar programas utilizando esta biblioteca C en lugar de la biblioteca C predeterminada de mi sistema.
Para asegurarme de que estaba usando mi glibc personalizado, agregué una llamada a las
glibc/stdio-common/printf.c:__printf
englibc/stdio-common/printf.c:__printf
para imprimir un mensaje.Luego reconstruí y reinstalé glibc.
Luego escribí un programa "Hello, World" y traté de compilarlo y vincularlo de la siguiente manera:
gcc -nodefaultlibs -static -lgcc -L~/GLIBC/glibc_install/lib -o myprog myprog.c
Pero me sale el siguiente informe de error del enlazador:
/usr/lib/gcc/x86_64-linux-gnu/4.4.3/../../../../lib/crt1.o: In function `_start'':
(.text+0x19): undefined reference to `__libc_csu_init''
/usr/lib/gcc/x86_64-linux-gnu/4.4.3/../../../../lib/crt1.o: In function `_start'':
(.text+0x25): undefined reference to `__libc_start_main''
/tmp/ccACTQEp.o: In function `main'':
c1.c:(.text+0xa): undefined reference to `puts''
collect2: ld returned 1 exit status
¿Qué estoy haciendo mal?
Configuración 1: compile su propio glibc sin GCC dedicado y utilícelo
Sin estática también funciona en: varias bibliotecas glibc en un solo host
Esta configuración podría funcionar y es rápida, ya que no vuelve a compilar toda la cadena de herramientas de GCC, solo glibc.
Pero no es confiable, ya que utiliza los objetos en tiempo de ejecución del host C como crt1.o
, crti.o
y crtn.o
proporcionados 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 hacen una configuración temprana en la que se basa glibc, por lo que no me sorprendería si las cosas fallaran de maravilla y formas increíblemente sutiles.
Para una configuración más confiable, vea la Configuración 2 a continuación.
Construye glibc e instala 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`
Instalación 1: verificar 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.
* https://.com/questions/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
rm -rf tmp
mkdir tmp
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" /
-static /
-std=c11 /
-o tmp/test_glibc.out /
-v /
test_glibc.c /
-pthread /
;
sudo chroot tmp /test_glibc.out
El programa produce el esperado:
gnu_get_libc_version() = 2.28
The atomic counter is 10000
The non-atomic counter is 8674
aunque lo ejecutamos en un chroot limpio, la -static
debe haber funcionado.
El comando se adaptó de https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location pero --sysroot
hizo que fallara con:
cannot find /home/ciro/glibc/build/install/lib/libc.so.6 inside /home/ciro/glibc/build/install
Así que lo quité.
ldd
salida de ldd
confirma que el ldd
y las bibliotecas que acabamos de ldd
se están utilizando 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)
La salida de depuración de la compilación de gcc
muestra que se utilizaron los objetos de tiempo de ejecución de mi 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 vuelva a instalar glibc, y vuelva a compilar y vuelva a ejecutar nuestro programa:
cd glibc/build
make -j `nproc`
make -j `nproc` install
./test_glibc.sh
Y vemos hacked
impreso unas cuantas 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 crosstool-NG
Esta es una alternativa a la configuración 1, y es la configuración más correcta que he logrado hasta ahora: todo está correcto hasta donde puedo observar, incluidos los objetos en tiempo de ejecución de C como crt1.o
, crti.o
y crtn.o
.
En esta configuración, compilaremos una cadena de herramientas de GCC dedicada completa que utiliza el glibc que deseamos.
El único inconveniente de este método es que la construcción tardará 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 desde nuestra fuente, incluidos GCC, glibc y binutils.
Sí, el sistema de compilación GCC es tan malo que necesitamos un proyecto separado para eso.
Esta configuración no es perfecta porque crosstool-NG no admite la creación de ejecutables sin -Wl
extra -Wl
, lo que -Wl
extraño ya que hemos creado GCC. Pero todo parece funcionar, así que esto es solo un inconveniente.
Obtén crosstool-NG y configúralo:
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
La única opción obligatoria que puedo ver, es hacer que coincida con la versión del kernel del host para usar los encabezados de kernel correctos. Encuentra la versión de tu kernel host con:
uname -a
lo que me muestra:
4.15.0-34-generic
así que en menuconfig
hago:
-
Operating System
-
Version of linux
-
así que selecciono:
4.14.71
que es la primera versión igual o anterior. Tiene que ser más antiguo ya que el kernel es compatible con versiones anteriores.
Ahora puedes construir con:
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
y ahora espera de unos treinta minutos a dos horas para la compilación.
Instalació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
hacer:
-
C-library
-
Version of glibc
guarde 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 así :
-
Paths and misc options
-
Try features marked as EXPERIMENTAL
: establece en verdadero
-
-
C-library
-
Source of glibc
-
Custom location
: di sí -
Custom location
-
Custom source location
: apunte a un directorio que contenga su fuente glibc
-
-
-
donde se clonó glibc como:
git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28
Configuración 2: probarlo
Una vez que haya construido la cadena de herramientas que desee, pruébelo con:
#!/usr/bin/env bash
set -eux
install_dir="${CT_PREFIX}/x86_64-unknown-linux-gnu"
rm -rf tmp
mkdir tmp
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" /
-static /
-v /
-o tmp/test_glibc.out /
test_glibc.c /
-pthread /
;
sudo chroot tmp /test_glibc.out
Todo parece funcionar como en la Configuración 1, excepto que ahora se utilizaron 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
Instalación 2: intento fallido de recompilación de glibc eficiente
No parece posible con crosstool-NG, como se explica a continuación.
Si usted acaba de reconstruir;
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
luego se toman en cuenta los cambios realizados en la ubicación de la fuente glibc personalizada, pero todo se construye 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 construcció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 de glibc entrelazados con varios pasos de GCC, especialmente libc_start_files
viene antes de cc_core_pass_2
, que es probablemente el paso más caro junto con cc_core_pass_1
.
Para compilar solo un paso, primero debe configurar la opción "Guardar pasos intermedios" en .config
para la compilación .config
:
-
Paths and misc options
-
Debug crosstool-NG
-
Save intermediate steps
-
-
y luego puedes probar:
env -u LD_LIBRARY_PATH time ./ct-ng libc+ -j`nproc`
pero desafortunadamente, se requiere el +
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 no compilador final construido con esta libc (y, por lo tanto, tampoco habrá bibliotecas de compilación como libstdc ++).
y, básicamente, aún hace que la reconstrucción sea demasiado lenta para que sea factible para el desarrollo, y no veo cómo superar esto sin parchear crosstool-NG.
Además, a partir del paso libc
no pareció volver a copiar la fuente desde la Custom source location
, lo que hace que este método quede inutilizable.
Bono: stdlibc ++
Un bono si también está interesado en la biblioteca estándar de C ++: ¿Cómo editar y reconstruir la fuente de la biblioteca estándar de GCC libstdc ++ C ++?
Siguiendo un par de sugerencias de la lista de correo de ayuda de glibc ([email protected]), tengo una solución. Resulta que esta tarea es un poco complicada porque tiene que decirle al vinculador que omita todo lo que normalmente incluiría automáticamente (y en silencio), y luego incluir todo lo que necesita, incluyendo un montón de archivos de inicio y fin. Algunos de los archivos de inicio y fin provienen de libc y otros provienen de gcc, por lo que la regla make es un poco complicada. A continuación se muestra un archivo makefile general para ilustrar el enfoque. Asumiré que está creando un programa llamado prog a partir de un archivo fuente llamado prog.c y que ha instalado su glibc personalizado en el directorio / home / my_acct / glibc_install .
TARGET = prog
OBJ = $(TARGET).o
SRC = $(TARGET).c
CC = gcc
CFLAGS = -g
LDFLAGS = -nostdlib -nostartfiles -static
GLIBCDIR = /home/my_acct/glibc_install/lib
STARTFILES = $(GLIBCDIR)/crt1.o $(GLIBCDIR)/crti.o `gcc --print-file-name=crtbegin.o`
ENDFILES = `gcc --print-file-name=crtend.o` $(GLIBCDIR)/crtn.o
LIBGROUP = -Wl,--start-group $(GLIBCDIR)/libc.a -lgcc -lgcc_eh -Wl,--end-group
$(TARGET): $(OBJ)
$(CC) $(LDFLAGS) -o $@ $(STARTFILES) $^ $(LIBGROUP) $(ENDFILES)
$(OBJ): $(SRC)
$(CC) $(CFLAGS) -c $^
clean:
rm -f *.o *.~ $(TARGET)
Tu línea de comando es simplemente falsa. Tratar:
gcc -nodefaultlibs -static -L~/GLIBC/glibc_install/lib -o myprog myprog.c -lgcc -lc -lgcc -lc
o similar. Omitió -lc
, y también tuvo erróneamente sus bibliotecas antes de los archivos de entrada.
Y estabas buscando una biblioteca llamada libibgcc
lugar de libgcc
...