Warum und wie können einige gemeinsam genutzte Bibliotheken ausgeführt werden, als ob sie ausführbar wären?

55

Auf 32-Bit-Linux-Systemen wird dies aufgerufen

$ /lib/libc.so.6

und auf 64-Bit-Systemen dies

$ /lib/x86_64-linux-gnu/libc.so.6

liefert in einer Shell eine Ausgabe wie diese:

GNU C Library stable release version 2.10.1, by Roland McGrath et al.
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.4.0 20090506 (Red Hat 4.4.0-4).
Compiled on a Linux >>2.6.18-128.4.1.el5<< system on 2009-08-19.
Available extensions:
    The C stubs add-on version 2.1.2.
    crypt add-on version 2.1 by Michael Glad and others
    GNU Libidn by Simon Josefsson
    Native POSIX Threads Library by Ulrich Drepper et al
    BIND-8.2.3-T5B
    RT using linux kernel aio
For bug reporting instructions, please see:
<http://www.gnu.org/software/libc/bugs.html>.

Warum und wie geschieht dies und wie ist es möglich, dasselbe in anderen gemeinsam genutzten Bibliotheken zu tun?

Ich habe nach /usr/libausführbaren Dateien gesucht und gefunden /usr/lib/libvlc.so.5.5.0. Das Ausführen führte zu einem Segmentierungsfehler . : - /

Ho1
quelle
Neben all den folgenden Antworten kann ich mich erinnern, dass, wenn Sie das x-Bit in einer gemeinsam genutzten Bibliothek setzen, es möglich war (möglicherweise noch), es aus einer ausführbaren Datei zu laden, auch wenn das r-Bit leer ist. Früher galt es als gute Sicherheitspraxis, System-Executables und -Bibliotheken aus der Welt zu verbannen. Aufgrund der weit verbreiteten Open Source-Nutzung gilt dies nur noch für das / bin / ls des anonymen FTP-Verzeichnisses. Das x-Bit-Set zu verlassen, sieht für mich wie ein Gesicht dieser alten Praxis aus.
Joshua,

Antworten:

52

Diese Bibliothek verfügt über eine main()Funktion oder einen entsprechenden Einstiegspunkt und wurde so kompiliert, dass sie sowohl als ausführbare Datei als auch als freigegebenes Objekt nützlich ist.

Hier ist ein Vorschlag, wie man das macht, obwohl es bei mir nicht funktioniert.

Hier ist eine weitere Antwort auf eine ähnliche Frage zu SO , die ich schamlos plagiieren, optimieren und ein wenig erläutern werde.

Zuerst Quelle für unsere Beispielbibliothek test.c:

#include <stdio.h>                  

void sayHello (char *tag) {         
    printf("%s: Hello!\n", tag);    
}                                   

int main (int argc, char *argv[]) { 
    sayHello(argv[0]);              
    return 0;                       
}                   

Kompiliere das:

gcc -fPIC -pie -o libtest.so test.c -Wl,-E

Hier kompilieren wir eine gemeinsam genutzte Bibliothek ( -fPIC), teilen dem Linker jedoch mit, dass es sich um eine reguläre ausführbare Datei handelt ( -pie) und dass die Symboltabelle exportierbar ist ( -Wl,-E), sodass eine sinnvolle Verknüpfung mit ihr möglich ist.

Und obwohl filees sich um ein gemeinsam genutztes Objekt handelt, funktioniert es als ausführbare Datei:

> ./libtest.so 
./libtest.so: Hello!

Jetzt müssen wir sehen, ob es wirklich dynamisch verknüpft werden kann. Ein Beispielprogramm program.c:

#include <stdio.h>

extern void sayHello (char*);

int main (int argc, char *argv[]) {
    puts("Test program.");
    sayHello(argv[0]);
    return 0;
}

Durch die Verwendung von müssen externwir keinen Header erstellen. Nun kompiliere das:

gcc program.c -L. -ltest

Bevor wir es ausführen können, müssen wir den Pfad libtest.sofür den dynamischen Lader hinzufügen :

export LD_LIBRARY_PATH=./

Jetzt:

> ./a.out
Test program.
./a.out: Hello!

Und ldd a.outzeigt die Verknüpfung zu libtest.so.

Beachten Sie, dass ich bezweifle, dass glibc so kompiliert wird, da es wahrscheinlich nicht so portabel ist wie glibc selbst (siehe man gccin Bezug auf die Schalter -fPICund -pie), aber es demonstriert den grundlegenden Mechanismus. Für die wahren Details müssten Sie sich das Quell-Makefile ansehen.

Goldlöckchen
quelle
1
Tolle Antwort, danke! :-) Ich habe versucht, nmdie gemeinsam genutzte Bibliothek zu verwenden, aber es war keine Debug-Version. Also, warum libvlcund andere stürzen ab?
1.
1
Da die meisten gemeinsam genutzten Bibliotheken nicht ausführbar sein sollen, ist GNU libceine Ausnahme.
Goldlöckchen
Ich habe zwei andere gefunden: ldund libpthread.
Ho1
@ Ho1 ld.soist in anderer Hinsicht besonders. In gewissem Maße ist es eher eine echte ausführbare Datei als eine normale dynamisch verknüpfte ausführbare Datei.
Random832
1
Die obigen Optionen erstellen zwar die ausführbare gemeinsam genutzte Bibliothek, sind jedoch in dem Sinne unvollständig, dass sie Fehler markieren, wenn eine ausführbare Datei versucht, eine Verknüpfung zu dieser zu erstellen. Detaillierte Beispielreferenz hier hinzugefügt: unix.stackexchange.com/a/479334/152034
parasrish
21

Lassen Sie uns für eine Antwort in zufälligen Glibc Repo in Github tauchen. Diese Version stellt ein „Banner“ zur Verfügung version.c.

In derselben Datei gibt es einige interessante Punkte: __libc_print_versionDie Funktion, die das Drucken auf denselben Text und dasselbe Symbol ermöglicht, __libc_main (void)die als Einstiegspunkt dokumentiert sind. Dieses Symbol wird also beim Ausführen der Bibliothek aufgerufen.

Woher weiß der Linker / Compiler, dass dies genau die Einstiegsfunktion ist?

Tauchen wir in das Makefile ein . In Linker Flags gibt es interessante Flags:

# Give libc.so an entry point and make it directly runnable itself.
LDFLAGS-c.so += -e __libc_main

Dies ist also das Linker-Flag zum Festlegen des Einstiegspunkts in der Bibliothek. Beim -e function_nameErstellen einer Bibliothek können Sie für Linker ausführbares Verhalten erstellen. Was macht es wirklich? Schauen wir uns das Handbuch an (etwas veraltet, aber immer noch gültig) :

Die Linker-Befehlssprache enthält einen Befehl speziell zum Definieren der ersten ausführbaren Anweisung in einer Ausgabedatei (ihres Einstiegspunkts). Sein Argument ist ein Symbolname:

EINTRAG (Symbol)

Wie Symbolzuweisungen kann der Befehl ENTRY entweder als unabhängiger Befehl in der Befehlsdatei oder unter den Abschnittsdefinitionen im Befehl SECTIONS platziert werden - was für Ihr Layout am sinnvollsten ist.

ENTRY ist nur eine von mehreren Möglichkeiten, den Einstiegspunkt auszuwählen. Sie können es auf eine der folgenden Arten angeben (in absteigender Reihenfolge der Priorität: Methoden, die in der Liste höher sind, überschreiben Methoden, die weiter unten liegen).

the `-e' entry command-line option;
the ENTRY(symbol) command in a linker control script;
the value of the symbol start, if present;
the address of the first byte of the .text section, if present;
The address 0. 

Mit diesen Regeln können Sie beispielsweise einen Einstiegspunkt mit einer Zuweisungsanweisung generieren: Wenn in Ihren Eingabedateien kein Symbolanfang definiert ist, können Sie ihn einfach definieren und ihm einen geeigneten Wert zuweisen.

start = 0x2020;

Das Beispiel zeigt eine absolute Adresse, Sie können jedoch einen beliebigen Ausdruck verwenden. Wenn Ihre Eingabeobjektdateien beispielsweise eine andere Symbolnamenskonvention für den Einstiegspunkt verwenden, können Sie einfach den Wert des Symbols zuweisen, das die zu startende Startadresse enthält:

start = other_symbol;

(aktuelle Dokumentation finden Sie hier )

Wirklich ldLinker erstellt eine ausführbare Datei mit Einstiegspunktfunktion, wenn Sie sie mit einer Befehlszeilenoption versehen -e(praktischste Lösung), ein Funktionssymbol bereitstellen startoder eine Symboladresse in Assembler einfügen.

Bitte beachten Sie jedoch, dass es eindeutig nicht garantiert ist, mit anderen Linkern zu arbeiten (ich weiß nicht, ob die lld von llvm das gleiche Flag hat). Warum dies für andere Zwecke als die Bereitstellung von Informationen in dieser Datei nützlich sein sollte, ist mir nicht bekannt.

IBr
quelle
1
Wenn es Python wäre, würde es Unit-Tests liefern.
Erik Aronesty