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

Warum und wie sind einige gemeinsam genutzte Bibliotheken lauffähig, als wären sie ausführbar?

Diese Bibliothek hat einen main() Funktion oder gleichwertiger Einstiegspunkt und wurde so kompiliert, dass es sowohl als ausführbares als auch als gemeinsam genutztes Objekt nützlich ist.

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

Hier ist eine weitere Antwort auf eine ähnliche Frage zu S.O., die ich schamlos plagiieren, optimieren und ein wenig erklären werde.

Zuerst die 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;                       
}                   

Kompilieren Sie das:

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

Hier kompilieren wir eine gemeinsam genutzte Bibliothek (-fPIC ), aber dem Linker mitteilen, dass es sich um eine reguläre ausführbare Datei handelt (-pie ) und seine Symboltabelle exportierbar zu machen (-Wl,-E ), sodass sie sinnvoll verlinkt werden kann.

Und zwar file wird sagen, es ist ein gemeinsam genutztes Objekt, es funktioniert 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;
}

Mit extern erspart uns das Erstellen eines Headers. Kompilieren Sie das jetzt:

gcc program.c -L. -ltest

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

export LD_LIBRARY_PATH=./

Jetzt:

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

Und ldd a.out zeigt die Verknüpfung zu libtest.so .

Beachten Sie, dass ich bezweifle, dass glibc tatsächlich so kompiliert wird, da es wahrscheinlich nicht so portabel ist wie glibc selbst (siehe man gcc bezüglich -fPIC und -pie Schalter), aber es demonstriert den grundlegenden Mechanismus. Für die wirklichen Details müssten Sie sich das Quell-Makefile ansehen.


Lassen Sie uns in einem zufälligen glibc-Repo auf GitHub nach einer Antwort suchen. Diese Version enthält ein „Banner“ in der Datei version.c .

In derselben Datei gibt es einige interessante Punkte:die __libc_print_version Funktion, die den Text auf stdout und den __libc_main (void) ausgibt Symbol, das als Einstiegspunkt dokumentiert ist. Dieses Symbol wird also aufgerufen, wenn die Bibliothek ausgeführt wird.

Woher weiß der Linker oder Compiler genau, dass dies die Einstiegspunktfunktion ist?

Lassen Sie uns in das Makefile eintauchen. In den Linker-Flags gibt es ein interessantes:

# 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 für die Bibliothek. Beim Erstellen einer Bibliothek können Sie den -e function_name angeben Flag für den Linker, um ein ausführbares Verhalten zu ermöglichen. Was macht es wirklich? Schauen wir ins Handbuch (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 (ihrem Einstiegspunkt). Sein Argument ist ein Symbolname:

EINTRAG(Symbol)

Wie Symbolzuweisungen kann der ENTRY-Befehl entweder als unabhängiger Befehl in der Befehlsdatei oder unter den Abschnittsdefinitionen innerhalb des SECTIONS-Befehls platziert werden – je nachdem, was für Ihr Layout am sinnvollsten ist.

ENTRY ist nur eine von mehreren Möglichkeiten, den Einstiegspunkt zu wählen. Sie können dies auf eine der folgenden Arten angeben (in absteigender Reihenfolge der Priorität angezeigt:Methoden weiter oben in der Liste überschreiben Methoden weiter unten).

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. 

Beispielsweise können Sie diese Regeln verwenden, um einen Einstiegspunkt mit einer Zuweisungsanweisung zu 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, aber Sie können 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 Startadresse enthält, um zu starten:

start =anderes_symbol;

(die aktuelle Dokumentation finden Sie hier)

Die ld Der Linker erstellt tatsächlich eine ausführbare Datei mit einer Einstiegspunktfunktion, wenn Sie die Befehlszeilenoption -e angeben (was die beliebteste Lösung ist), stellen Sie ein Funktionssymbol start bereit , oder geben Sie eine Symboladresse für den Assembler an.

Beachten Sie jedoch, dass es nicht garantiert ist, dass es mit anderen Linkern funktioniert (ich weiß nicht, ob llvms lld dasselbe Flag hat). Mir ist nicht bekannt, warum dies für andere Zwecke als die Bereitstellung der Informationen über die SO-Datei nützlich sein sollte.


Linux
  1. Redis als Cache:Wie es funktioniert und warum man es verwendet

  2. So finden Sie heraus, wer auf Ihrem System angemeldet ist und was er tut

  3. Warum Daten wichtig sind und wie man sie schützt

  4. Warum werden einige von Nmap gemeldete Ports gefiltert und die anderen nicht?

  5. Wie zeigt man alle gemeinsam genutzten Bibliotheken an, die von ausführbaren Dateien in Linux verwendet werden?

Umgang mit dynamischen und statischen Bibliotheken unter Linux

Virtuelle Dateisysteme in Linux:Warum wir sie brauchen und wie sie funktionieren

So listen Sie gemeinsam genutzte Bibliotheken auf, die von ausführbaren Dateien in Linux verwendet werden

Linux – Warum sind wahr und falsch so groß?

Warum sind einige Emoji B&W und andere zu groß?

Warum gibt es `/lib` und `/lib64` aber nur `/bin`?