GNU/Linux >> Tutoriales Linux >  >> Linux

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

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

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

También puede construir su binario en un entorno de construcción chroot, o usando un glibc-nuevo => glibc-antiguo compilador cruzado.

Según la publicación de blog http://www.trevorpounds.com Enlace a símbolos de versiones anteriores (glibc) , es posible forzar la vinculación de cualquier símbolo con uno anterior siempre que sea válido utilizando el mismo .symver pseudo-op que se utiliza para definir símbolos versionados en primer lugar. El siguiente ejemplo es un extracto de la publicación del blog.

El siguiente ejemplo utiliza la ruta real de glibc, pero se asegura de que esté vinculada a una versión anterior 2.2.5.

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

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

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

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

    return 0;
}

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

Dado que parece imposible hacerlo solo con trucos de versiones 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 vuelve a compilar toda la cadena de herramientas de GCC, solo glibc.

Pero no es confiable ya que usa 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 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.

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

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.

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

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

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 parecía 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++?


Linux
  1. ¿Cómo detectar Bash> =4.0?

  2. ¿Cómo puedo encontrar la versión de Fedora que uso?

  3. ¿Cómo puedo desinstalar o actualizar mi versión anterior de node.js?

  4. ¿Cómo puedo eliminar reglas específicas de iptables?

  5. ¿Cómo puedo cambiar la versión de php-cli en Ubuntu 14.04?

Cómo comprobar la versión de Java

Cómo instalar una versión específica del kernel en CentOS

Cómo instalar una versión específica del paquete en Ubuntu y Debian

¿Cómo comprobar la versión de OpenGL?

¿Cómo indicar a Yum que instale una versión específica del paquete X?

¿Cómo puedo cambiar mi versión de PHP en cPanel?