¿Cómo puedo enlazar a una versión específica de glibc?


Cuando compilo algo en mi Ubuntu Lucid 10.04 PC se enlaza contra glibc. Lucid utiliza 2.11 de glibc. Cuando corro este binario en otro PC con un glibc más antiguo, el comando falla diciendo que no hay glibc 2.11...

Por lo que sé glibc usa versionado de símbolos. ¿Puedo forzar a gcc a enlazar contra una versión de símbolo específica?

En mi uso concreto intento compilar una cadena de herramientas cruzada gcc para ARM.

Author: falstaff, 2010-05-18

3 answers

Tiene razón en que glibc utiliza el control de versiones de símbolos. Si tiene curiosidad, la implementación de versionado de símbolos introducida en glibc 2.1 se describe aquí y es una extensión del esquema de versionado de símbolos de Sun descrito aquí .

Una opción es enlazar estáticamente tu binario. Esta es probablemente la opción más fácil.

También puede compilar su binario en un entorno de compilación chroot, o usando un compilador cruzado glibc- new = > glibc- old.

Según el http://www.trevorpounds.com entrada de blogVinculación a Símbolos Versionados Más Antiguos (glibc), es posible forzar que cualquier símbolo se vincule contra uno más antiguo siempre y cuando sea válido utilizando el mismo .symver pseudo-op que se utiliza para definir símbolos versionados en primer lugar. El siguiente ejemplo está extraído de la entrada del blog .

El siguiente ejemplo hace uso de la ruta real de glibc, pero se asegura de que esté vinculado con una versión anterior 2.2.5.

#include <limits.h>
#include <stdlib.h>
#include <stdio.h>

__asm__(".symver realpath,realpath@GLIBC_2.2.5");
int main()
{
    char* unresolved = "/lib64";
    char  resolved[PATH_MAX+1];

    if(!realpath(unresolved, resolved))
        { return 1; }

    printf("%s\n", resolved);

    return 0;
}
 56
Author: jschmier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-05 15:11:47

Enlace con -estático. Cuando se enlaza con -static el enlazador incrusta la biblioteca dentro del ejecutable, por lo que el ejecutable será más grande, pero se puede ejecutar en un sistema con una versión anterior de glibc porque el programa usará su propia biblioteca en lugar de la del sistema.

 17
Author: Iacchus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-03 15:21:57

Setup 1: compila tu propio glibc sin GCC dedicado y úsalo

Dado que parece imposible hacerlo solo con hacks de versionado de símbolos, vayamos un paso más allá y compilemos glibc nosotros mismos.

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 confiable, ya que utiliza objetos de tiempo de ejecución del host C, 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 hacen una configuración temprana en la que se basa glibc, por lo que no me sorprendería si las cosas se estrellaran de maneras maravillosas e increíblemente sutiles.

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

Construir glibc e instalarlo 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

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

El programa produce 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 lo hizo fallar con:{[64]]}

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 ldd y las bibliotecas que acabamos de construir 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 gcc muestra que mi host se utilizaron objetos de tiempo de ejecución, lo cual es malo como se mencionó anteriormente, pero no se cómo trabajar alrededor de él, 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 recompile y vuelva a instalar 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 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: crosstool-NG configuración prístina

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 hasta donde 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 dedicada que use el glibc que queremos.

El único inconveniente de este método es que la compilación tomará más tiempo. Pero no me arriesgaría configuración de producción con cualquier cosa menos.

Crosstool-NG es un conjunto de scripts que descarga y compila todo desde source para nosotros, incluyendo 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 soporta la construcción de ejecutables sin banderas adicionales -Wl , lo que se siente raro ya que hemos construido GCC. Pero todo parece para trabajar, así 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 las cabeceras del kernel correctas. Encuentre la versión del núcleo del host con:

uname -a

Que me muestra: {[64]]}

4.15.0-34-generic

Así que en menuconfig lo hago:

  • Operating System
    • Version of linux

Así que selecciono:

4.14.71

Que es la primera igual o versión 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 esperar unos treinta minutos a dos horas para la compilación.

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úa con la construcción.

O, si si desea usar su propia fuente glibc, por ejemplo, para usar glibc desde el último git, proceda de la siguiente manera :

  • Paths and misc options
    • Try features marked as EXPERIMENTAL: establecer a 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: probarlo out

Una vez que haya construido 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 Setup 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 usted acaba de volver a construir;

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

Entonces sus cambios en la ubicación de origen glibc personalizada se tienen en cuenta, pero construye todo desde cero, por lo que es inutilizable para el desarrollo iterativo.

Si lo hacemos:{[64]]}

./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 de cc_core_pass_2, que es probablemente el paso más caro junto con cc_core_pass_1.

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

  • Paths and misc options
    • Debug crosstool-NG
      • Save intermediate steps

Y luego puedes intentar: {[64]]}

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, usted tendrá un libc reconstruido - pero no hay compilador final construido con este libc (y por lo tanto, no hay bibliotecas de compiladores como libstdc++ tampoco).

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, a partir del paso libc no parecía copiar la fuente de nuevo desde Custom source location, haciendo aún más este método inutilizable.

Bonus: stdlibc++

Un bono si también eres interesado en la biblioteca estándar de C++: ¿Cómo editar y reconstruir el código fuente de la biblioteca estándar de C++ libstdc++ de GCC?

 3
Author: Ciro Santilli 新疆改造中心 六四事件 法轮功,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-30 17:05:04