¿Cómo construir un programa C usando una versión personalizada de glibc y enlaces estáticos?

13 minutos de lectura

avatar de usuario
Amitai Aviram

Construí glibc 2.14 y lo instalé en el directorio ~/GLIBC/glibc_install. Ahora quiero construir y ejecutar programas usando esta biblioteca C en lugar de la biblioteca C predeterminada de mi sistema.

  • Para asegurarme de que estaba usando mi glibc personalizada, agregué una llamada a puts in glibc/stdio-common/printf.c:__printf para imprimir un mensaje.

  • Luego reconstruí y reinstalé glibc.

  • Luego escribí un programa “Hello, World” e intenté compilarlo y vincularlo de la siguiente manera:

    gcc -nodefaultlibs -static -lgcc -L~/GLIBC/glibc_install/lib -o myprog myprog.c
    

Pero obtengo 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?

  • GCC depende en gran medida de las funciones internas que tiene Glibc. Tendrá que reconstruir una versión anterior de GCC para no esperar estas funciones internas. Esto no va a ser pan comido…

    usuario529758

    26 de mayo de 2012 a las 3:40


  • No entiendo por qué necesitaría una versión anterior de GCC. La glibc que construí es la penúltima versión más reciente, 2.14. (Tuve problemas de compilación con 2.15 en mi sistema Ubuntu debido a problemas conocidos que no pude resolver con las soluciones alternativas recomendadas).

    – Amittai Aviram

    26 mayo 2012 a las 20:16

  • La versión de GCC no debe estar relacionada. Se supone que los símbolos que GCC no puede encontrar están definidos en libc.aasí que si faltan, tal vez libc.a fue mal compilado ..?

    – R.. GitHub DEJA DE AYUDAR A ICE

    27 de mayo de 2012 a las 0:21


  • Las versiones de GCC no tienen nada que ver con este problema. Consulte mi solución publicada a continuación (segunda respuesta).

    – Amittai Aviram

    27 de mayo de 2012 a las 6:18


Siguiendo un par de sugerencias de la lista de correo de ayuda de glibc (libc-help@sourceware.org), tengo una solución. Resulta que esta tarea es un poco complicada porque tienes que decirle al enlazador que omita todo normalmente incluiría automáticamente (y en silencio) y luego incluiría todo lo que necesita, incluido un montón de archivos de inicio y fin. Algunos de los archivos de inicio y finalización provienen de libc y otros de gcc, por lo que la regla de creación es un poco complicada. A continuación se muestra un archivo MAKE de muestra general para ilustrar el enfoque. Asumiré que estás construyendo un programa llamado programa de un archivo fuente llamado prog.c y que ha instalado su glibc personalizado en el directorio /home/mi_cuenta/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)

  • para la próxima vez, una buena manera de buscar tales cosas es correr nm sobre tu /lib /usr/lib directorios para encontrar los símbolos que faltan. esencialmente libcrt solo contiene la llamada a main.

    – Alejandro Oh

    08/06/2015 a las 20:10


  • ¿Puedes vincular al hilo de la lista de correo de GCC? yo tambien agregaria -nostdinc -I para controlar los encabezados también.

    – Ciro Santilli Путлер Капут 六四事

    9 de junio de 2015 a las 15:37

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. omitiste -lcy también erróneamente tenía sus bibliotecas antes que sus archivos de entrada.

Y estabas buscando una biblioteca llamada libibgcc en vez de libgcc

  • Re -lgcc: vea editar, eso fue un error tipográfico; mi línea de comando tenía “-lgcc”. Intenté exactamente la línea de comando que sugirió anteriormente y obtuve exactamente los mismos resultados de error que se informaron en mi publicación original, por lo que me temo que no funciona.

    – Amittai Aviram

    26 de mayo de 2012 a las 8:40

  • Por cierto, no tenía mis bibliotecas antes de mis archivos de entrada. Tenía una especificación de la ruta de búsqueda de la biblioteca, que AFAIK puede venir en cualquier lugar de la línea de comando (ya que es una bandera, no una entrada). De todos modos, vea la solución a continuación.

    – Amittai Aviram

    27 de mayo de 2012 a las 6:17

  • Por que es -lgcc -lc siendo pasado dos veces?

    – Ciro Santilli Путлер Капут 六四事

    08/06/2015 a las 10:30

  • Esto me falla en Ubuntu 14.04 GCC 4.8 con undefined reference to __gcc_personality_v0 y _Unwind_Resumeambos definidos en libgcc_s.so pero no en libgcc.a (no hay libgcc.so). si elimino -static funciona por alguna razón incluso sin agregar -lgcc_s.

    – Ciro Santilli Путлер Капут 六四事

    8 jun 2015 a las 20:16

  • @CiroSantilli新疆改造中心六四事件法轮功, ¿Podría escribir el comando completo? añado -lgcc_enpero sigo recibiendo el error.

    – gfan

    18 de septiembre de 2018 a las 8:39

avatar de usuario
Ciro Santilli Путлер Капут 六四事

Configuración 1: compile su propia glibc sin GCC dedicado y utilícela

Sin estática también trabajando en: Múltiples 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 objetos de tiempo de ejecución del host C como crt1.o, crti.oy 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_ubicación Esos objetos realizan una configuración temprana en la que se basa glibc, por lo que no me sorprendería si las cosas colapsaran de maneras maravillosas y asombrosamente sutiles.

Para una configuración más confiable, consulte la Configuración 2 a continuación.

Compile 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: 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
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 genera lo esperado:

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

a pesar de que lo ejecutamos en un chroot limpio, por lo que el -static debe haber trabajado

Comando adaptado de https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_ubicación pero --sysroot lo hizo fallar con:

cannot find /home/ciro/glibc/build/install/lib/libc.so.6 inside /home/ciro/glibc/build/install

así que lo eliminé.

ldd 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)

los 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, 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 vamos a modificar 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 18.04.

Configuración 2: configuración impecable 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.oy 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 construcción de GCC es tan malo que necesitamos un proyecto separado para eso.

Esta configuración solo no es perfecta porque crosstool-NG no admite la creación de ejecutables sin extra -Wl banderas, lo cual se siente extraño ya que hemos creado GCC. Pero todo parece funcionar, por lo que esto es solo un inconveniente.

Obtenga crosstool-NG y configúrelo:

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 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

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 núcleo es compatible con versiones anteriores.

Ahora puedes construir con:

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

y ahora espere entre treinta minutos y dos horas para la compilación.

Configuración 2: configuraciones opcionales

los .config que generamos con ./ct-ng x86_64-unknown-linux-gnu posee:

CT_GLIBC_V_2_27=y

Para cambiar eso, en menuconfig hacer:

  • C-library
  • Version of glibc

salva el .configy continuar con la compilación.

O, si desea usar su propia fuente de glibc, por ejemplo, para usar glibc de la última versión de git, proceda Me gusta esto:

  • 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: apunta a un directorio que contiene 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é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"
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 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 fallido de recompilación eficiente de glibc

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`

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

Si 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 viene antes cc_core_pass_2que es probablemente el paso más caro 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 de la libc paso no parecía copiar la fuente de nuevo desde Custom source locationlo que hace que este método quede 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 ++?

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad