Jeder kennt also wahrscheinlich die glibc /lib/libc.so.6
kann in der Shell wie eine normale ausführbare Datei ausgeführt werden, in welchen Fällen es seine Versionsinformationen ausgibt und beendet wird. Dies erfolgt über die Definition eines Einstiegspunkts in der .so. In einigen Fällen könnte es interessant sein, dies auch für andere Projekte zu verwenden. Leider ist der Einstiegspunkt auf niedriger Ebene, den Sie mit der Option -e von ld festlegen können, etwas zu niedrig: Der dynamische Loader ist nicht verfügbar, sodass Sie keine richtigen Bibliotheksfunktionen aufrufen können. glibc implementiert aus diesem Grund den Systemaufruf write() über einen nackten Systemaufruf in diesem Einstiegspunkt.
Meine Frage ist jetzt, kann sich jemand einen netten Weg vorstellen, wie man einen vollständigen dynamischen Linker von diesem Einstiegspunkt booten könnte, damit man auf Funktionen von anderen .so’s zugreifen könnte?
Aktualisierung 2: siehe die etwas kompliziertere Lösung von Andrew G Morgan, die für jedes GLIBC funktioniert (diese Lösung wird auch in verwendet libc.so.6
selbst (seit Ewigkeiten), weshalb man es so ausführen kann ./libc.so.6
(es gibt Versionsinformationen aus, wenn es auf diese Weise aufgerufen wird)).
Aktualisierung 1: dies funktioniert mit neueren GLIBC-Versionen nicht mehr:
./a.out: error while loading shared libraries: ./pie.so: cannot dynamically load position-independent executable
Ursprüngliche Antwort von 2009:
Erstellen Sie Ihre gemeinsame Bibliothek mit -pie
Option erscheint, um Ihnen alles zu geben, was Sie wollen:
/* pie.c */
#include <stdio.h>
int foo()
{
printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
return 42;
}
int main()
{
printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
return foo();
}
/* main.c */
#include <stdio.h>
extern int foo(void);
int main()
{
printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
return foo();
}
$ gcc -fPIC -pie -o pie.so pie.c -Wl,-E
$ gcc main.c ./pie.so
$ ./pie.so
in main pie.c:9
in foo pie.c:4
$ ./a.out
in main main.c:6
in foo pie.c:4
$
PS glibc implementiert write(3)
per Systemaufruf, weil es nirgendwo anders anrufen muss (es ist die am niedrigsten bereits Niveau). Das hat nichts mit Ausführungsmöglichkeit zu tun libc.so.6
.
Ich habe gesucht, um Unterstützung dafür hinzuzufügen pam_cap.so
, und fand diese Frage. Wie @EmployedRussian in einem Follow-up zu ihrem eigenen Beitrag feststellt, funktionierte die akzeptierte Antwort irgendwann nicht mehr. Es hat eine Weile gedauert, bis ich herausgefunden habe, wie man das wieder zum Laufen bringt, also ist hier ein funktionierendes Beispiel.
Dieses ausgearbeitete Beispiel umfasst 5 Dateien, um zu zeigen, wie die Dinge mit einigen entsprechenden Tests funktionieren.
Betrachten Sie zunächst dieses triviale Programm (nennen Sie es empty.c
):
int main(int argc, char **argv) { return 0; }
Beim Kompilieren können wir sehen, wie es die dynamischen Symbole auf meinem System wie folgt auflöst:
$ gcc -o empty empty.c
$ objcopy --dump-section .interp=/dev/stdout empty ; echo
/lib64/ld-linux-x86-64.so.2
$ DL_LOADER=/lib64/ld-linux-x86-64.so.2
Diese letzte Zeile setzt eine Shell-Variable zur späteren Verwendung.
Hier sind die beiden Dateien, die meine gemeinsam genutzte Beispielbibliothek erstellen:
/* multi.h */
void multi_main(void);
void multi(const char *caller);
und
/* multi.c */
#include <stdio.h>
#include <stdlib.h>
#include "multi.h"
void multi(const char *caller) {
printf("called from %s\n", caller);
}
__attribute__((force_align_arg_pointer))
void multi_main(void) {
multi(__FILE__);
exit(42);
}
const char dl_loader[] __attribute__((section(".interp"))) =
DL_LOADER ;
(Update 13.11.2021: Die Zwangsausrichtung soll helfen __i386__
Code SSE-kompatibel sein – ohne ihn werden wir schwer zu debuggen glibc
SIGSEGV
stürzt ab.)
Wir können es wie folgt kompilieren und ausführen:
$ gcc -fPIC -shared -o multi.so -DDL_LOADER="\"${DL_LOADER}\"" multi.c -Wl,-e,multi_main
$ ./multi.so
called from multi.c
$ echo $?
42
Das ist also ein .so
die als eigenständige Binärdatei ausgeführt werden kann. Als nächstes validieren wir, dass es als gemeinsam genutztes Objekt geladen werden kann.
/* opener.c */
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
void *handle = dlopen("./multi.so", RTLD_NOW);
if (handle == NULL) {
perror("no multi.so load");
exit(1);
}
void (*multi)(const char *) = dlsym(handle, "multi");
multi(__FILE__);
}
Das heißt, wir laden das gemeinsam genutzte Objekt dynamisch und führen eine Funktion daraus aus:
$ gcc -o opener opener.c -ldl
$ ./opener
called from opener.c
Schließlich verlinken wir mit diesem gemeinsamen Objekt:
/* main.c */
#include "multi.h"
int main(int argc, char **argv) {
multi(__FILE__);
}
Wo wir es wie folgt kompilieren und ausführen:
$ gcc main.c -o main multi.so
$ LD_LIBRARY_PATH=./ ./main
called from main.c
(Anmerkung, weil multi.so
sich nicht an einem standardmäßigen Systembibliotheksspeicherort befindet, müssen wir den Ort, an dem die Laufzeitumgebung nach der gemeinsam genutzten Objektdatei sucht, mit der überschreiben LD_LIBRARY_PATH
Umgebungsvariable.)
Ich nehme an, du hättest deine ld -e
zeigen Sie auf einen Einstiegspunkt, der dann die verwenden würde dlopen()
Funktionsfamilie, um den Rest des dynamischen Linkers zu finden und zu bootstrappen. Dafür müsstest du natürlich sorgen dlopen()
selbst war entweder statisch gelinkt oder Sie müssen möglicherweise genug von Ihrem eigenen Linker-Stub implementieren, um darauf zuzugreifen (unter Verwendung von Systemaufrufschnittstellen wie z mmap()
genauso wie es die libc selbst tut.
Nichts davon klingt für mich “nett”. Eigentlich nur der Gedanke, die Glibc-Quellen (und die ld-linux
Quellcode, als ein Beispiel) ausreicht, um die Größe des Auftrags einzuschätzen, klingt für mich ziemlich altmodisch. Es könnte auch ein Albtraum der Portabilität sein. Es kann große Unterschiede zwischen der Implementierung von Linux geben ld-linux
und wie die Verknüpfungen unter OpenSolaris, FreeBSD usw. durchgeführt werden. (Ich weiß nicht).
/lib/ld-linux.so.2
ist nur ein weiteres beispiel 🙂– Vanni Totaro
11. Juli 2010 um 22:21 Uhr
rachid.koucha.free.fr/tech_corner/executable_lib.html hier einige Tutorials
– Sergej Ponomarew
26. Juni 2019 um 9:51 Uhr