Welcher Systemaufruf wird verwendet, um Bibliotheken unter Linux zu laden?

23

In straceAusgaben befinden sich die Pfade zu den Bibliotheken, die executables aufrufen, in Aufrufen an open(). Ist dies der Systemaufruf, der von ausführbaren Dateien verwendet wird, die dynamisch verknüpft sind? Was ist dlopen()? open()ist kein Aufruf, von dem ich vermutet hätte, dass er bei der Ausführung von Programmen eine Rolle spielt.

Melab
quelle

Antworten:

33

dlopenist kein Systemaufruf, sondern eine Bibliotheksfunktion in der libdl-Bibliothek . In werden nur Systemaufrufe angezeigt strace.

Unter Linux und auf vielen anderen Plattformen (insbesondere solchen, die das ELF-Format für ausführbare Dateien verwenden) wird dlopenes implementiert, indem die Zielbibliothek mit geöffnet open()und in den Speicher mit abgebildet wird mmap(). mmap()ist hier wirklich der kritische Teil. Er bindet die Bibliothek in den Adressraum des Prozesses ein, sodass die CPU ihren Code ausführen kann. Aber Sie müssen open()die Datei, bevor Sie es können mmap()!

Celada
quelle
2
"mmap () ist wirklich der kritische Teil": Und dann muss der dynamische Linker die Verschiebungen, Initialisierungen und so weiter vornehmen (aber das wird auf der Ebene der Systemaufrufe nicht gesehen).
ysdx
1
Da das Laden von Bibliotheken über eine Bibliotheksfunktion erfolgt, sollte meines Erachtens hinzugefügt werden, dass die ausführbare Datei selbst und ld-linuxvom Kernel als Teil des execveSystemaufrufs zugeordnet werden.
Kasperd
mmap nach dieser Antwort. Beachten Sie auch, dass nach dem Öffnen jeder Bibliothek einige (832) Bytes vor dem Aufruf von mmap gelesen werden. Ich gehe davon aus, dass die Bibliothek gültig ist.
Johan
@kasperd Kennt der Linux-Kernel den Dynamic Loader? Wird es aufgerufen, wenn die Anwendung ausgeführt wird? Oder macht die Anwendung das selbst? Wenn dies der Fall ist, wie kann eine andere ausführbare Datei auf den Speicher der Anwendung zugreifen?
Melab
@ Melab Ja, der Kernel kennt den Dynamic Linker. Der Kernel liest den Pfad zum dynamischen Linker aus dem Header der ausführbaren Datei. Und der Kernel wird beides in den Speicher abbilden. Ich weiß nicht, ob der Einstiegspunkt, zu dem die Kernel-Übertragungssteuerung zuerst wechselt, innerhalb des Linkers liegt oder ausführbar ist. Wenn ich es implementieren würde, hätte ich wahrscheinlich die Kontrolle über die Kernelübertragung zu einem Einstiegspunkt im Linker mit einer Rücksprungadresse auf dem Stapel, die auf den Einstiegspunkt der ausführbaren Datei zeigt.
Kasperd
5

dlopen hat nichts mit Shared Libraries zu tun, wie Sie es sich vorstellen. Es gibt zwei Methoden zum Laden eines gemeinsam genutzten Objekts:

  1. Sie teilen dem Linker zur Kompilierungszeit mit (obwohl er normalerweise über den Compiler aufgerufen wird), dass Sie Funktionen aus einer bestimmten gemeinsam genutzten Bibliothek verwenden möchten. Bei diesem Ansatz müssen Sie wissen, wie der Name der Bibliothek lautet, wenn der Linker zur Kompilierungszeit ausgeführt wird. Sie können die Funktionen der Bibliothek jedoch so aufrufen, als wären sie statisch mit Ihrem Programm verknüpft. Wenn die Anwendung ausgeführt wird, wird der dynamische Laufzeit-Linker (ld.so) unmittelbar vor dem Aufrufen der mainFunktion aufgerufen und der Prozessbereich der Anwendung so eingerichtet, dass die Anwendung die Funktionen der Bibliothek findet. Dazu müssen open()Sie zuerst den Lubrary und dann mmap()einige Nachschlagetabellen einrichten .
  2. Sie teilen dem Linker zur Kompilierungszeit mit libdl, mit dem Sie eine Verknüpfung herstellen möchten , von dem aus Sie dann (mit der ersten Methode) das dlopen()und aufrufen könnendlsym()funktionen. Mit dlopen erhalten Sie ein Handle für die Bibliothek, das Sie dann mit dlsym verwenden können, um einen Funktionszeiger auf eine bestimmte Funktion zu erhalten. Diese Methode ist für den Programmierer viel komplizierter als die erste Methode (da Sie das Setup manuell ausführen müssen, anstatt dass der Linker es automatisch für Sie ausführt), und sie ist auch fragiler (da Sie die Kompilierung nicht erhalten) -time prüft, ob Sie Funktionen mit den richtigen Argumenttypen aufrufen, wie Sie es in der ersten Methode erhalten haben. Der Vorteil besteht jedoch darin, dass Sie entscheiden können, welches gemeinsame Objekt zur Laufzeit geladen werden soll (oder sogar, ob es überhaupt geladen werden soll) Dies ist eine Schnittstelle für Plug-In-Funktionen. Schließlich ist die dlopen-Schnittstelle auch weniger portabel als die andere, da ihre Mechanik von der genauen Implementierung des dynamischen Linkers abhängt (daher libtools)libltdl, die versucht, diese Unterschiede zu abstrahieren).
Wouter Verhelst
quelle
interessant; Daher werden dynamisch geladene Bibliotheken besser als dynamisch verknüpfte Bibliotheken bezeichnet, da das Laden von Binärdateien in den Speicher nicht der schwierige Teil ist und die darin verwendeten Adressen sinnvoll sind. Wenn ich eine dynamische Bibliothek laden möchte, möchte ich die Bibliothek tatsächlich in meinen Adressraum verlinken (oder die Verknüpfung aufheben).
Dmitry
4

Heutzutage verwenden die meisten Betriebssysteme die Methode für gemeinsam genutzte Bibliotheken, die Ende 1987 von SunOS-4.0 eingeführt wurde. Diese Methode basiert auf der Zuordnung des Speichers über mmap ().

In Anbetracht der Tatsache, dass Sun in den frühen 1990er Jahren sogar den alten a.out-basierten Code (Solaris war zu diesem Zeitpunkt bereits ELF-basiert) an die FreeBSD-Leute gespendet und diesen Code später an viele andere Systeme (einschließlich Linux) übergeben hat Vielleicht verstehen Sie, warum es keinen großen Unterschied zwischen Plattformen gibt.

schily
quelle
3

ltrace -SDie Analyse eines Minimalbeispiels zeigt, dass mmapes in glibc 2.23 verwendet wird

In glibc 2.23, Ubuntu 16.04, ausgeführt latrace -Sauf einem Minimalprogramm, das verwendet wird dlopenmit:

ltrace -S ./dlopen.out

zeigt an:

dlopen("libcirosantilli_ab.so", 1 <unfinished ...>
SYS_open("./x86_64/libcirosantilli_ab.so", 524288, 06267650550)      = -2
SYS_open("./libcirosantilli_ab.so", 524288, 06267650550)             = 3
SYS_read(3, "\177ELF\002\001\001", 832)                              = 832
SYS_brk(0)                                                           = 0x244c000
SYS_brk(0x246d000)                                                   = 0x246d000
SYS_fstat(3, 0x7fff42f9ce30)                                         = 0
SYS_getcwd("/home/ciro/bak/git/cpp-cheat"..., 128)                   = 54
SYS_mmap(0, 0x201028, 5, 2050)                                       = 0x7f1c323fe000
SYS_mprotect(0x7f1c323ff000, 2093056, 0)                             = 0
SYS_mmap(0x7f1c325fe000, 8192, 3, 2066)                              = 0x7f1c325fe000
SYS_close(3)                                                         = 0
SYS_mprotect(0x7f1c325fe000, 4096, 1)                                = 0

so sehen wir sofort, dass dlopenruft open+ mmap.

Das fantastische ltraceTool verfolgt sowohl Bibliotheksaufrufe als auch Systemaufrufe und ist daher perfekt, um zu untersuchen, was in diesem Fall vor sich geht.

Eine genauere Analyse zeigt, dass opender Dateideskriptor zurückgegeben wird 3(nächster freier nach stdin, out und err).

readverwendet dann diesen Dateideskriptor, aber die Argumente von TODO why mmapsind auf vier beschränkt, und wir können nicht sehen, welches fd dort verwendet wurde, da dies das fünfte Argument ist . stracebestätigt, wie erwartet, dass dies 3der Fall ist, und die Ordnung des Universums wird wiederhergestellt.

Tapfere Seelen können sich auch in Glibc-Code wagen, aber ich konnte den mmapnach einem kurzen Grep nicht finden und bin faul.

Getestet mit diesem Minimalbeispiel mit Build Boilerplate auf GitHub .

Ciro Santilli ist ein Schauspieler
quelle
2

stracemeldet Systemaufrufe (dh Funktionen, die direkt vom Kernel implementiert werden). Dynamische Bibliotheken sind keine Kernelfunktion. dlopenist Teil der C-Bibliothek, nicht des Kernels. Die Implementierung von dlopenwill call open(das ist ein Systemaufruf), um die Bibliotheksdatei zu öffnen, damit sie gelesen werden kann.

cjm
quelle
5
Bibliotheksaufrufe können mit angezeigt werden ltrace.
Kasperd
@kasperd ltrace -Sist perfekt das , wie es auch zeigt syscalls zu analysieren: unix.stackexchange.com/a/462710/32558
Ciro Santilli新疆改造中心法轮功六四事件