GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Wie kann ich auf eine bestimmte Glibc-Version verlinken?

Sie haben Recht damit, dass glibc die Symbolversionierung verwendet. Falls Sie neugierig sind, die in glibc 2.1 eingeführte Implementierung der Symbolversionierung wird hier beschrieben und ist eine Erweiterung des hier beschriebenen Schemas der Symbolversionierung von Sun.

Eine Möglichkeit besteht darin, Ihre Binärdatei statisch zu verknüpfen. Dies ist wahrscheinlich die einfachste Option.

Sie könnten Ihre Binärdatei auch in einer Chroot-Build-Umgebung erstellen oder eine glibc-new verwenden => glibc-alt Cross-Compiler.

Gemäß dem http://www.trevorpounds.com-Blogbeitrag Linking to Older Versioned Symbols (glibc) , ist es möglich, zu erzwingen, dass jedes Symbol mit einem älteren verknüpft wird, solange es gültig ist, indem derselbe .symver verwendet wird Pseudo-Op, das in erster Linie zum Definieren von versionierten Symbolen verwendet wird. Das folgende Beispiel ist ein Auszug aus dem Blogbeitrag.

Das folgende Beispiel verwendet den realpath von glibc, stellt aber sicher, dass er mit einer älteren Version 2.2.5 verknüpft ist.

#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:eigene glibc ohne dedizierten GCC kompilieren und verwenden

Da es unmöglich erscheint, nur mit Symbol-Versioning-Hacks zu arbeiten, gehen wir noch einen Schritt weiter und kompilieren glibc selbst.

Dieses Setup könnte funktionieren und ist schnell, da es nicht die gesamte GCC-Toolchain neu kompiliert, sondern nur glibc.

Aber es ist nicht zuverlässig, da es Host-C-Laufzeitobjekte wie crt1.o verwendet , crti.o , und crtn.o bereitgestellt von glibc. Dies wird erwähnt unter:https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location Diese Objekte führen eine frühe Einrichtung durch, auf die sich Glibc stützt, also wäre ich nicht überrascht, wenn die Dinge wunderbar abstürzen würden und unglaublich subtile Wege.

Für eine zuverlässigere Einrichtung siehe Einrichtung 2 unten.

Glibc erstellen und lokal installieren:

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:Überprüfen Sie den 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);
}

Mit test_glibc.sh kompilieren und ausführen :

#!/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

Das Programm gibt das Erwartete aus:

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

Befehl angepasst von https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location aber --sysroot hat es fehlgeschlagen mit:

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

also habe ich es entfernt.

ldd Ausgabe bestätigt, dass ldd und Bibliotheken, die wir gerade erstellt haben, werden tatsächlich wie erwartet verwendet:

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

Die gcc Die Debug-Ausgabe der Kompilierung zeigt, dass meine Host-Laufzeitobjekte verwendet wurden, was wie bereits erwähnt schlecht ist, aber ich weiß nicht, wie ich es umgehen soll, z. es enthält:

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

Setup 1:glibc ändern

Jetzt modifizieren wir glibc mit:

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

Dann kompilieren und installieren Sie glibc neu und kompilieren Sie unser Programm neu und führen Sie es erneut aus:

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

und wir sehen hacked wie erwartet einige Male gedruckt.

Dies bestätigt weiter, dass wir tatsächlich die glibc verwendet haben, die wir kompiliert haben, und nicht die des Hosts.

Getestet auf Ubuntu 18.04.

Setup 2:makelloses Crosstool-NG-Setup

Dies ist eine Alternative zu Setup 1, und es ist das korrekteste Setup, das ich bisher erreicht habe:Soweit ich das beobachten kann, ist alles korrekt, einschließlich der C-Laufzeitobjekte wie crt1.o , crti.o , und crtn.o .

In diesem Setup werden wir eine vollständig dedizierte GCC-Toolchain kompilieren, die die von uns gewünschte glibc verwendet.

Der einzige Nachteil dieser Methode ist, dass der Build länger dauert. Aber mit weniger würde ich kein Produktions-Setup riskieren.

crosstool-NG ist eine Reihe von Skripten, die alles für uns aus dem Quellcode herunterladen und kompilieren, einschließlich GCC, glibc und binutils.

Ja, das GCC-Build-System ist so schlecht, dass wir dafür ein separates Projekt brauchen.

Dieses Setup ist nur deshalb nicht perfekt, weil crosstool-NG das Erstellen der ausführbaren Dateien ohne zusätzlichen -Wl nicht unterstützt Flags, was sich komisch anfühlt, da wir GCC selbst gebaut haben. Aber alles scheint zu funktionieren, also ist das nur eine Unannehmlichkeit.

Holen Sie sich crosstool-NG und konfigurieren Sie es:

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

Die einzige obligatorische Option, die ich sehen kann, besteht darin, sie an Ihre Host-Kernel-Version anzupassen, um die richtigen Kernel-Header zu verwenden. Finden Sie Ihre Host-Kernel-Version mit:

uname -a

was mir zeigt:

4.15.0-34-generic

so in menuconfig Ich mache:

  • Operating System
    • Version of linux

also wähle ich:

4.14.71

das ist die erste gleiche oder ältere Version. Es muss älter sein, da der Kernel abwärtskompatibel ist.

Jetzt können Sie bauen mit:

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

und warten Sie nun etwa dreißig Minuten bis zwei Stunden auf die Kompilierung.

Setup 2:optionale Konfigurationen

Die .config die wir mit ./ct-ng x86_64-unknown-linux-gnu generiert haben hat:

CT_GLIBC_V_2_27=y

Um das zu ändern, in menuconfig tun:

  • C-library
  • Version of glibc

Speichern Sie den .config , und fahren Sie mit dem Build fort.

Oder wenn Sie Ihre eigene Glibc-Quelle verwenden möchten, z. um glibc vom neusten git zu verwenden, gehen sie wie folgt vor:

  • Paths and misc options
    • Try features marked as EXPERIMENTAL :auf wahr setzen
  • C-library
    • Source of glibc
      • Custom location :Ja sagen
      • Custom location
        • Custom source location :Zeigen Sie auf ein Verzeichnis, das Ihre Glibc-Quelle enthält

wobei glibc geklont wurde als:

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

Setup 2:Testen Sie es aus

Nachdem Sie die gewünschte Toolchain erstellt haben, testen Sie sie mit:

#!/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

Alles scheint wie in Setup 1 zu funktionieren, außer dass jetzt die richtigen Laufzeitobjekte verwendet wurden:

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:Effizienter Glibc-Neukompilierungsversuch fehlgeschlagen

Mit crosstool-NG scheint dies nicht möglich zu sein, wie unten erklärt.

Wenn Sie nur neu erstellen;

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

dann werden Ihre Änderungen am benutzerdefinierten glibc-Quellspeicherort berücksichtigt, aber es wird alles von Grund auf neu erstellt, was es für die iterative Entwicklung unbrauchbar macht.

Wenn ja:

./ct-ng list-steps

es gibt einen schönen Überblick über die Build-Schritte:

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.

Daher sehen wir, dass glibc-Schritte mit mehreren GCC-Schritten verflochten sind, insbesondere libc_start_files kommt vor cc_core_pass_2 , was zusammen mit cc_core_pass_1 wahrscheinlich der teuerste Schritt ist .

Um nur einen Schritt zu bauen, müssen Sie zunächst in .config die Option „Zwischenschritte speichern“ setzen Option für den anfänglichen Build:

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

und dann können Sie versuchen:

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

aber leider die + erforderlich, wie unter:https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536

erwähnt

Beachten Sie jedoch, dass ein Neustart bei einem Zwischenschritt das Installationsverzeichnis auf den Zustand zurücksetzt, den es während dieses Schritts hatte. Das heißt, Sie haben eine neu erstellte libc - aber keinen endgültigen Compiler, der mit dieser libc erstellt wurde (und daher auch keine Compiler-Bibliotheken wie libstdc++).

und im Grunde macht die Neuerstellung immer noch zu langsam, um für die Entwicklung machbar zu sein, und ich sehe nicht, wie ich dies überwinden kann, ohne crosstool-NG zu patchen.

Außerdem ab dem libc step schien die Quelle nicht erneut von Custom source location zu kopieren , was diese Methode weiter unbrauchbar macht.

Bonus:stdlibc++

Ein Bonus, wenn Sie auch an der C++-Standardbibliothek interessiert sind:How to edit and rebuild the GCC libstdc++ C++ standard library source?


Linux
  1. Wie verwende ich yum, um eine bestimmte Version eines Pakets zu installieren?

  2. Wie erkennt man Bash>=4.0?

  3. So überprüfen Sie die CentOS-Version

  4. Wie kann ich die IP-Adresse in bestimmten CDIR-Subnetzbereichen berechnen?

  5. Wie kann ich feststellen, welche Version von Linux ich verwende?

So überprüfen Sie die Java-Version

So installieren Sie eine bestimmte Kernel-Version in CentOS

So installieren Sie eine bestimmte Paketversion unter Ubuntu und Debian

Wie überprüfe ich die OpenGL-Version?

Wie kann man Yum anweisen, eine bestimmte Version von Paket X zu installieren?

Wie kann ich meine PHP-Version auf cPanel ändern?