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.