GNU/Linux >> Linux Esercitazione >  >> Linux

Come posso collegarmi a una versione specifica di glibc?

Hai ragione in quanto glibc utilizza il controllo delle versioni dei simboli. Se sei curioso, l'implementazione del controllo delle versioni dei simboli introdotta in glibc 2.1 è descritta qui ed è un'estensione dello schema di controllo delle versioni dei simboli di Sun descritto qui.

Un'opzione è collegare staticamente il tuo binario. Questa è probabilmente l'opzione più semplice.

Puoi anche creare il tuo binario in un ambiente di compilazione chroot o usando un glibc-new => glibc-vecchio cross-compilatore.

Secondo il post del blog http://www.trevorpounds.com Collegamento a simboli con versioni precedenti (glibc) , è possibile forzare il collegamento di qualsiasi simbolo a uno precedente purché sia ​​valido utilizzando lo stesso .symver pseudo-op che viene utilizzato per definire i simboli con versione in primo luogo. L'esempio seguente è tratto dal post del blog.

L'esempio seguente utilizza il percorso reale di glibc, ma assicura che sia collegato a una versione 2.2.5 precedente.

#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;
}

Setup 1:compila la tua glibc senza GCC dedicato e usala

Dal momento che sembra impossibile farlo solo con gli hack di versionamento dei simboli, facciamo un ulteriore passo avanti e compiliamo noi stessi glibc.

Questa configurazione potrebbe funzionare ed è veloce in quanto non ricompila l'intera toolchain GCC, solo glibc.

Ma non è affidabile in quanto utilizza oggetti di runtime host C come crt1.o , crti.o e crtn.o fornito da glibc. Questo è menzionato in:https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location Questi oggetti eseguono la configurazione iniziale su cui si basa glibc, quindi non sarei sorpreso se le cose andassero in crash in modo meraviglioso e modi incredibilmente sottili.

Per una configurazione più affidabile, vedere Configurazione 2 di seguito.

Compila glibc e installa 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`

Setup 1:verifica la build

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);
}

Compila ed esegui 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

Il programma produce l'atteso:

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

Comando adattato da https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location ma --sysroot l'ha fatto fallire con:

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

quindi l'ho rimosso.

ldd l'output conferma che ldd e le librerie che abbiamo appena creato vengono effettivamente utilizzate come previsto:

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

L'gcc l'output di debug della compilazione mostra che sono stati utilizzati i miei oggetti di runtime host, il che è negativo come accennato in precedenza, ma non so come aggirarlo, ad es. contiene:

COLLECT_GCC_OPTIONS=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o

Setup 1:modifica glibc

Ora modifichiamo 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)");

Poi ricompila e reinstalla glibc, e ricompila e riesegui il nostro programma:

cd glibc/build
make -j `nproc`
make -j `nproc` install
./test_glibc.sh

e vediamo hacked stampato alcune volte come previsto.

Ciò conferma ulteriormente che abbiamo effettivamente utilizzato la glibc che abbiamo compilato e non quella dell'host.

Testato su Ubuntu 18.04.

Configurazione 2:configurazione originaria di crosstool-NG

Questa è un'alternativa alla configurazione 1, ed è la configurazione più corretta che ho raggiunto finora:tutto è corretto per quanto posso osservare, inclusi gli oggetti di runtime C come crt1.o , crti.o e crtn.o .

In questa configurazione, compileremo una toolchain GCC completamente dedicata che utilizza la glibc che vogliamo.

L'unico aspetto negativo di questo metodo è che la compilazione richiederà più tempo. Ma non rischierei una configurazione di produzione con qualcosa di meno.

crosstool-NG è un set di script che scarica e compila per noi tutto dal sorgente, inclusi GCC, glibc e binutils.

Sì, il sistema di compilazione di GCC è così pessimo che abbiamo bisogno di un progetto separato per questo.

Questa configurazione non è perfetta solo perché crosstool-NG non supporta la compilazione degli eseguibili senza -Wl extra flags, il che sembra strano dato che abbiamo creato GCC stesso. Ma tutto sembra funzionare, quindi questo è solo un inconveniente.

Ottieni crosstool-NG e configuralo:

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

L'unica opzione obbligatoria che posso vedere è farla corrispondere alla versione del kernel host per utilizzare le intestazioni del kernel corrette. Trova la tua versione del kernel host con:

uname -a

che mi mostra:

4.15.0-34-generic

quindi in menuconfig Sì:

  • Operating System
    • Version of linux

quindi seleziono:

4.14.71

che è la prima versione uguale o precedente. Deve essere più vecchio poiché il kernel è compatibile con le versioni precedenti.

Ora puoi costruire con:

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

e ora attendi da circa trenta minuti a due ore per la compilazione.

Setup 2:configurazioni opzionali

Il .config che abbiamo generato con ./ct-ng x86_64-unknown-linux-gnu ha:

CT_GLIBC_V_2_27=y

Per cambiarlo, in menuconfig fare:

  • C-library
  • Version of glibc

salva il .config e continua con la compilazione.

Oppure, se vuoi usare la tua fonte glibc, ad es. per usare glibc dall'ultimo git, procedi così:

  • Paths and misc options
    • Try features marked as EXPERIMENTAL :impostato su true
  • C-library
    • Source of glibc
      • Custom location :dire di sì
      • Custom location
        • Custom source location :punta a una directory contenente il tuo sorgente di glibc

dove glibc è stato clonato come:

git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28

Configurazione 2:provalo

Dopo aver creato la toolchain che desideri, provala 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

Tutto sembra funzionare come in Setup 1, tranne per il fatto che ora sono stati utilizzati gli oggetti di runtime corretti:

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

Setup 2:tentativo di ricompilazione efficiente di glibc fallito

Non sembra possibile con crosstool-NG, come spiegato di seguito.

Se ricostruisci semplicemente;

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

quindi vengono prese in considerazione le tue modifiche alla posizione di origine della glibc personalizzata, ma crea tutto da zero, rendendolo inutilizzabile per lo sviluppo iterativo.

Se lo facciamo:

./ct-ng list-steps

fornisce una bella panoramica dei passaggi della creazione:

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.

pertanto, vediamo che ci sono passaggi di glibc intrecciati con diversi passaggi GCC, in particolare libc_start_files viene prima di cc_core_pass_2 , che è probabilmente il passaggio più costoso insieme a cc_core_pass_1 .

Per costruire un solo passaggio, devi prima impostare il "Salva passaggi intermedi" in .config opzione per la build iniziale:

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

e poi puoi provare:

env -u LD_LIBRARY_PATH time ./ct-ng libc+ -j`nproc`

ma sfortunatamente, il + richiesto come indicato su:https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536

Si noti tuttavia che il riavvio in un passaggio intermedio reimposta la directory di installazione allo stato che aveva durante tale passaggio. Cioè, avrai una libc ricostruita, ma nessun compilatore finale compilato con questa libc (e quindi, nemmeno librerie di compilatori come libstdc++).

e fondamentalmente rende ancora la ricostruzione troppo lenta per essere fattibile per lo sviluppo, e non vedo come superare questo problema senza patchare crosstool-NG.

Inoltre, partendo dal libc step non sembrava copiare di nuovo la fonte da Custom source location , rendendo ulteriormente questo metodo inutilizzabile.

Bonus:stdlibc++

Un bonus se sei interessato anche alla libreria standard C++:come modificare e ricostruire il sorgente della libreria standard C++ GCC libstdc++?


Linux
  1. Come cambiare la versione di PHP

  2. Come controllare la versione di CentOS

  3. Come posso calcolare l'indirizzo IP su intervalli di sottorete CDIR specifici?

  4. Come posso trovare la versione di Fedora che uso?

  5. Come posso trovare la mia versione della shell usando un comando Linux?

Come controllare la versione Java

Come installare una versione specifica del kernel in CentOS

Come installare una versione specifica del pacchetto su Ubuntu e Debian

Come controllare la versione OpenGL?

Come istruire Yum a installare una versione specifica del pacchetto X?

Come posso modificare la mia versione di PHP su cPanel?