GNU/Linux >> Tutoriales Linux >  >> Linux

¿Cómo compilar mi propia biblioteca estándar glibc C desde la fuente y usarla?

Probado por última vez en Ubuntu 20.04 con la versión de desarrollo glibc 2.33.9000 (ver glibc/version.h ) el 27 de junio de 2021.

Cómo descargar y compilar glibc y ejecutar sus puntos de referencia

Puede obtener manualmente el código fuente de glibc aquí:https://www.gnu.org/software/libc/sources.html:

git clone https://sourceware.org/git/glibc.git
cd glibc
git checkout master

Espejo de terceros en GitHub:https://github.com/bminor/glibc/tree/master/benchtests

Véase también:

  1. https://kazoo.ga/una-herramienta-sencilla-para-probar-el-rendimiento-de-malloc/

Si alguna vez desea construir manualmente glibc y sus pruebas de banco, hágalo de la siguiente manera:

# IMPORTANT: begin AT THE SAME DIRECTORY LEVEL as the `glibc` source code 
# directory, NOT inside the `glibc` source code dir! In other words, if 
# you are in the correct dir, running `ls` will show the `glibc` source
# code dir (that you just cloned) inside the dir you are in.
mkdir -p glibc-build
mkdir -p glibc-install
cd glibc-build
../glibc/configure --prefix="$(realpath "../glibc-install")"
time make -j8  # build with 8 threads (jobs); on a fast laptop this takes ~3 min.
time make install # (optional: install to the `glibc-install` dir you created)

# Build the benchtests (everything inside the `glibc/benchtests` dir) too;
# see the makefile 'glibc/benchtests/Makefile' for more build commands. 
time make bench-build -j8
# Now you have this executable file you can use for malloc speed tests, for instance!: 
#       ../glibc-build/benchtests/bench-malloc-thread

# To build **and run** all glibc benchtests, do:
time make bench

Referencias:

  1. https://www.gnu.org/software/libc/manual/html_node/Configuración-y-compilación.html
  2. https://kazoo.ga/una-herramienta-sencilla-para-probar-el-rendimiento-de-malloc/
  3. https://github.com/f18m/malloc-benchmarks/blob/master/Makefile#L122-L129:aprendí mucho de esto al estudiar este objetivo de archivo MAKE:
    $(glibc_install_dir)/lib/libc.so.6:
    @echo "Building GNU libc... go get a cup of coffee... this will take time!"
    mkdir -p $(glibc_build_dir)
    cd $(glibc_build_dir) && \
        ../glibc/configure --prefix=$(glibc_install_dir) && \
        make $(parallel_flags) && \
        make install
    [ -x $(glibc_build_dir)/benchtests/bench-malloc-thread ] && echo "GNU libc benchmarking utility is ready!" || echo "Cannot find GNU libc benchmarking utility! Cannot collect benchmark results"
    
  4. ¿Cómo compilar mi propia biblioteca estándar glibc C desde el código fuente y usarla?

Palabras clave:cómo compilar y ejecutar glibc y sus pruebas de banco, incl. pruebas de banco malloc, desde la fuente; construir glibc desde la fuente en linux ubuntu


El Makefile va a existir en tu build-glibc directorio si el configure la secuencia de comandos finaliza correctamente.

Si todo parece haber ido sin problemas durante configure y todavía sin Makefile , entonces probablemente te perdiste una idiosincrasia:

Mientras hace un configure para glibc, se espera que normalmente proporcione un --prefix alternativo , porque la instalación en la ubicación predeterminada (/usr/local ) puede paralizar potencialmente el sistema. Si no proporciona uno, debe activar --disable-sanity-checks .

Si este tampoco es el caso, busca un config.log y lea su contenido.


Configuración 1:glibc sin GCC dedicado

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.

El único problema que tengo con esta configuración es que no he encontrado una buena manera de usar objetos de tiempo de ejecución como crt1.o , crti.o y crtn.o proporcionado por nuestro glibc, y estoy usando los host por ahora. 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 fallaran maravillosamente. y formas asombrosamente sutiles. Vea los intentos de resolver esto a continuación.

Compile glibc e instálelo localmente:

git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.32
mkdir build
cd build
export glibc_install="$(pwd)/install"
../configure --prefix "$glibc_install"
make -j `nproc`
make install -j `nproc`

Configuración 1:verificar la compilación

prueba_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://stackoverflow.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);
}

Compilar y ejecutar 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

Comando adaptado de https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location

El programa genera lo esperado:

gnu_get_libc_version() = 2.32
The atomic counter is 10000
The non-atomic counter is 8674

ldd la salida confirma que el ldd y las bibliotecas que acabamos de crear se están usando 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 La salida de depuración de compilación muestra que se usaron los objetos de tiempo de ejecución de mi host, lo cual es malo como se mencionó anteriormente, pero no sé cómo solucionarlo, p. 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 vuelva a compilar y reinstalar 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 un par de veces como se esperaba.

Esto confirma aún más que en realidad usamos la glibc que compilamos y no la del host.

Probado en Ubuntu 20.10.

Configuración 1:intentos de usar el crt* correcto objetos

https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location sugiere agregar --sysroot al gcc comando pero:

  • realmente no cambia los objetos a los nuestros según los registros
  • y hace que la compilación falle con /usr/bin/ld: cannot find libgcc_s.so.1 presumiblemente porque el sysroot se usa para este objeto proporcionado por GCC, que no tenemos en ese sysroot porque solo construimos glibc

En https://stackoverflow.com/a/66634184/895245, ZeZNiQ proporciona una solución alternativa que probablemente sea correcta al pasar:

-nostartfiles

seguido de todos los objetos. Solo necesita extraer los objetos correctos del comando completo con -nostartfiles y pasarlos manualmente.

Por ejemplo, en mi máquina amd64, los objetos utilizados eran diferentes de su comando de 32 bits, por lo que esto es un poco complicado.

Bibliografía:

  • ¿Cómo cambio el directorio de búsqueda predeterminado de GCC para crti.o?
  • https://gcc.gnu.org/legacy-ml/gcc-help/2015-02/msg00016.html
  • https://gcc.gnu.org/legacy-ml/gcc-help/2001-11/msg00029.html

Configuración 2:configuración impecable de 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 es correcto por lo que puedo observar, incluidos los objetos de tiempo de ejecución de C como crt1.o , crti.o y crtn.o .

En esta configuración, compilaremos una cadena de herramientas GCC completa y dedicada que usa la glibc que queremos.

El único inconveniente de este método es que la compilació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 desde la fuente 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 creación de ejecutables sin -Wl extra flags, 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 constrúyalo:

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 compilación tarda 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 kernel de su host para usar los encabezados del kernel correctos. Encuentre la versión del kernel de su host con:

uname -a

que me muestra:

4.15.0-34-generic

entonces en menuconfig Yo 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 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 hacer:

  • C-library
  • Version of glibc

guarda el .config y continúe con la compilación.

O, si desea utilizar su propia fuente de glibc, p. para usar glibc desde el último git, proceda así:

  • Paths and misc options
    • Try features marked as EXPERIMENTAL :establecido en verdadero
  • C-library
    • Source of glibc
      • Custom location :di que sí
      • Custom location
        • Custom source location :apunta a un directorio que contiene tu 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ébalo

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:falló el intento eficiente de recompilación de glibc

No parece posible con crosstool-NG, como se explica a continuación.

Si acaba de reconstruir;

env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`

luego se tienen en cuenta los cambios en la ubicación de origen de la glibc personalizada, pero se crea todo desde cero, lo que lo hace inutilizable para el desarrollo iterativo.

Si lo hacemos:

./ct-ng list-steps

ofrece una buena descripció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 viene antes de cc_core_pass_2 , que probablemente sea el paso más costoso junto con cc_core_pass_1 .

Para construir solo un paso, primero debe configurar "Guardar pasos intermedios" en .config opción para la compilación inicial:

  • 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, el + 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 no un compilador final creado con esta libc (y, por lo tanto, tampoco bibliotecas de compilación como libstdc++).

y básicamente todavía 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 libc el paso no pareció copiar la fuente nuevamente desde Custom source location , lo que hace que este método quede inutilizable.

Bonificación: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++?


Agregando a la respuesta/solución anterior de Ciro https://stackoverflow.com/a/52454710/4726668:

@CiroSantilli Editar su respuesta devuelve "La cola de edición sugerida está llena". El script ldd que está llamando en el test_glibc.sh el script apunta al enlazador dinámico del host:/home/ciro/glibc/build/install/lib/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007fc12f1b3000) . Para arreglar esto, en test_glibc.sh , cambia ldd a ${glibc_install}/bin/ldd . Esto requerirá que agregue el crt integrado *.o archivos también al script:

-nostartfiles \
${glibc_install}/lib/crti.o \
${glibc_install}/lib/crtn.o \
${glibc_install}/lib/crt1.o \

En mi máquina GNU/Linux i386/i686 (32 bits x86 arch), el siguiente es mi test_glibc.sh de trabajo :

#!/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.so.2" \
  -std=c11 \
  -nostartfiles \
  ${glibc_install}/lib/crti.o \
  ${glibc_install}/lib/crtn.o \
  ${glibc_install}/lib/crt1.o \
  -o test_glibc.out \
  -v \
  test_glibc.c \
  -pthread \
;
${glibc_install}/bin/ldd ./test_glibc.out
./test_glibc.out

Linux
  1. ¿Cómo compilar e instalar FFmpeg desde la fuente en CentOS/RHEL?

  2. ¿Cuándo se debe compilar e instalar desde la fuente?

  3. Cómo instalar y configurar Nginx desde el origen en Linux

  4. Cómo compilar e instalar software desde el código fuente en Linux

  5. Cómo instalar Apache CouchDB en CentOS 6 (desde Source y EPEL)

Cómo construir Nginx desde la fuente en Debian 9

Cómo compilar Nginx desde la fuente en CentOS 7

Cómo compilar paquetes Debian desde el código fuente

Cómo instalar software desde el código fuente... y eliminarlo después

Cómo compilar el kernel de Linux desde el origen para crear un kernel personalizado

Cómo compilar e instalar GLFW 3 y usarlo en un proyecto de Linux