Forum: PC-Programmierung Wie werden dyn. Libraries eingebunden?


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Monk (roehrmond)


Lesenswert?

Wenn ich ein Programm starte das von dynamischen Bibliotheken abhängt 
(*.dll bzw *.so), wer findet diese Dateien und lädt sie in den Speicher? 
Der Betriebssystem-Kern, das Anwendungsprogramm selbst oder ist da noch 
eine Instanz zwischen?

Das interessiert mich für Linux und Windows.

von Klaus W. (mfgkw)


Lesenswert?

Unter Linux ist das Stichwort ld.so bzw. ld-linux.so.

von Gerald K. (geku)


Lesenswert?

Klaus W. schrieb:
> Unter Linux ist das Stichwort ld.so bzw. ld-linux.so.

Aber wer lädt die SO-Dateien in den Arbeitspeiche?

Das Programm selbst, wenn dieses die dyn. Library benötigt?

von C-hater (c-hater)


Lesenswert?

Steve van de Grens schrieb:

> Wenn ich ein Programm starte das von dynamischen Bibliotheken abhängt
> (*.dll bzw *.so), wer findet diese Dateien und lädt sie in den Speicher?
> Der Betriebssystem-Kern, das Anwendungsprogramm selbst oder ist da noch
> eine Instanz zwischen?
>
> Das interessiert mich für Linux und Windows.

Also bei Windows ist das so:

Es gibt zwei verschiedene Möglichkeiten, eine DLL einzubinden, nämlich 
"quasi-statisch" und wirklich "dynamisch". Davon hängt ab, was genau die 
DLL lädt. Im ersteren Fall macht's der Exe-Loader des OS, im zweiten 
Fall muss es die Anwendung zu Fuss selber erledigen.

Letztlich greifen aber beide Varianten natürlich wiederum auf Routinen 
zurück, die das OS bereitstellt.

Warum schnappst du dir nicht einfach einen brauchbaren Debugger und 
spielst das selber durch? Da kannst du dir im Detail anschauen, was da 
passiert. Brauchst nur eine wenig Ausdauer. ;o)

von Gerald K. (geku)


Lesenswert?

C-hater schrieb:
> Warum schnappst du dir nicht einfach einen brauchbaren Debugger und
> spielst das selber durch? Da kannst du dir im Detail anschauen, was da
> passiert. Brauchst nur eine wenig Ausdauer. ;o)
Auf 
https://learn.microsoft.com/de-de/sysinternals/downloads/sysinternals-suite 
gibt es die dafür notwenigen Tools.

von Foobar (asdfasd)


Lesenswert?

Linux unterstützt mehrere Dateiformate (/usr/src/linux/fs/binfmt_xxx.c). 
Bei ELF-Dateien wird die Datei vom Kernel in den Speicher geladen.  Im 
Header der Datei steht ein "Interpreter" (objdump -sj.interp /bin/echo, 
normalerweise "ld-linux.so.X", ein statisches Library).  Dieser 
"Interpreter" wird auch noch geladen und dann gestartet (nicht das 
Programm selbst).  Der kümmert sich dann um den Rest (man ld.so). 
Dessen Arbeit kann man mit "strace /bin/echo" beobachten.  Das Laden der 
abhängigen Libraries findet also außerhalb des Kernels statt.

Dieser Loader steht der Anwendung selbst auch zur Verfügung (man 
dlopen).  Damit kann man bei Bedarf weitere Libraries nachladen 
(Beispiel in der man-page).

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Foobar schrieb:
> Dieser "Interpreter" wird auch noch geladen und dann gestartet (nicht das
> Programm selbst).

Den kann man übrigens auch manuell aufrufen:
1
$ /lib64/ld-linux-x86-64.so.2 --help
2
Usage: /lib64/ld-linux-x86-64.so.2 [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]
3
You have invoked 'ld.so', the program interpreter for dynamically-linked
4
ELF programs.  Usually, the program interpreter is invoked automatically
5
when a dynamically-linked executable is started.
6
7
You may invoke the program interpreter program directly from the command
8
line to load and run an ELF executable file; this is like executing that
9
file itself, but always uses the program interpreter you invoked,
10
instead of the program interpreter specified in the executable file you
11
run.  Invoking the program interpreter directly provides access to
12
additional diagnostics, and changing the dynamic linker behavior without
13
setting environment variables (which would be inherited by subprocesses).
14
15
  --list                list all dependencies and how they are resolved
16
  --verify              verify that given object really is a dynamically linked
17
                        object we can handle
18
  --inhibit-cache       Do not use /etc/ld.so.cache
19
  --library-path PATH   use given PATH instead of content of the environment
20
                        variable LD_LIBRARY_PATH
21
  --glibc-hwcaps-prepend LIST
22
                        search glibc-hwcaps subdirectories in LIST
23
  --glibc-hwcaps-mask LIST
24
                        only search built-in subdirectories if in LIST
25
  --inhibit-rpath LIST  ignore RUNPATH and RPATH information in object names
26
                        in LIST
27
  --audit LIST          use objects named in LIST as auditors
28
  --preload LIST        preload objects named in LIST
29
  --argv0 STRING        set argv[0] to STRING before running
30
  --list-tunables       list all tunables with minimum and maximum values
31
  --list-diagnostics    list diagnostics information
32
  --help                display this help and exit
33
  --version             output version information and exit
34
35
This program interpreter self-identifies as: /lib64/ld-linux-x86-64.so.2
36
37
Shared library search path:
38
  (libraries located via /etc/ld.so.cache)
39
  /lib/x86_64-linux-gnu (system search path)
40
  /usr/lib/x86_64-linux-gnu (system search path)
41
  /lib (system search path)
42
  /usr/lib (system search path)
43
44
Subdirectories of glibc-hwcaps directories, in priority order:
45
  x86-64-v4
46
  x86-64-v3 (supported, searched)
47
  x86-64-v2 (supported, searched)
48
49
Legacy HWCAP subdirectories under library search path directories:
50
  haswell (AT_PLATFORM; supported, searched)
51
  tls (supported, searched)
52
  avx512_1
53
  x86_64 (supported, searched)

von Gerald K. (geku)


Lesenswert?

Mit der Funktion **dlopen**() kann vom Programm aus eine dynamische 
Library geladen werden.

https://man7.org/linux/man-pages/man3/dlopen.3.html

Codebeispiel:
1
#include <stdio.h>
2
#include <dlfcn.h>
3
4
int main() {
5
    void *library_handle;
6
    int (*add)(int, int);
7
    int (*subtract)(int, int);
8
9
    // Dynamische Bibliothek laden
10
    library_handle = dlopen("./libmath.so", RTLD_LAZY);
11
    if (!library_handle) {
12
        fprintf(stderr, "Fehler beim Laden der Bibliothek: %s\n", dlerror());
13
        return 1;
14
    }
15
16
    // Symbole auflösen
17
    add = dlsym(library_handle, "add");
18
    subtract = dlsym(library_handle, "subtract");
19
    if (!add || !subtract) {
20
        fprintf(stderr, "Fehler beim Auflösen der Symbole: %s\n", dlerror());
21
        dlclose(library_handle);
22
        return 1;
23
    }
24
25
    // Funktionen aufrufen
26
    int result = add(5, 3);
27
    printf("Ergebnis der Addition: %d\n", result);
28
29
    result = subtract(10, 4);
30
    printf("Ergebnis der Subtraktion: %d\n", result);
31
32
    // Dynamische Bibliothek entladen
33
    dlclose(library_handle);
34
35
    return 0;
36
}

: Bearbeitet durch User
von Monk (roehrmond)


Lesenswert?

Dankeschön. So detaillierte Antworten hatte ich gar nicht erwartet.

von Harald K. (kirnbichler)


Lesenswert?

Der Vollständigkeit halber:

Unter Windows werden DLLs, die als Importreferenzen im Programmheader 
des *.exe-Files aufgelistet werden, vom zum Betriebssystem gehörenden 
Programmlader geladen. Das ist das, was "c-hater" als "quasi-statisch" 
bezeichnet hat. Im Programmheader stehen die zu verwendenden DLLs und 
die daraus zu verwendenen Funktionen ("Prozedureinsprungspunkte" im 
schönen Microsoft-Deutsch). Wird so eine DLL nicht gefunden oder wird 
eine Funktion in der referenzierten DLLs nicht gefunden, gibt es eine 
allseits bekannte und verhasste Fehlermeldung.

Erst wenn alle im Programmheader aufgelisteten Referenzen aufgelöst 
werden können, wird der eigentliche Programmcode ausgeführt.

Mit Tools wie dem zu manchen Compilern gehörendenden "dumpbin" lassen 
sich die Importreferenzen auflisten, der "dependency walker" zeigt auch 
noch Abhängigkeiten von DLLs untereinander auf.


Die Bezeichnung "quasi-statisch", die "c-hater" verwendete, rührt daher, 
daß Programme, die ihre DLL-Referenzen vom Programmlader aufgelöst haben 
möchten, so geschrieben werden, als würden sie herkömmliche statische 
Libraries verwenden. Beim Linken aber werden dem Programm sogenannte 
"import-Libraries" untergeschoben, die dafür sorgen, daß die verwendeten 
Symbole als Importreferenzen im Programmheader eingetragen werden.


Der Gegenentwurf ist dynamisches Laden unter Kontrolle des bereits 
laufenden Programmes, das erfolgt mit der Win32-Funktion LoadLibrary. 
Zum Bestimmen von Funktionspointern dient die Win32-Funktion 
GetProcAddress.

Damit lassen sich Funktionalitäten wie "Plugins" realisieren, d.h. zur 
Laufzeit (und nur bei Vorhandensein) geladener Code, der ein Programm 
erweitern kann, aber nicht muss.

Das von Rolf beschriebene Verfahren mit ld.so entstammt in seinen 
Ursprüngen SunOS 4.0 (1988), und ist damit ähnlich alt wie das unter 
Windows verwendete Verfahren, das seine Wurzeln tief in den 
16-Bit-Zeiten noch vor Windows 3.x hat. SunOS war schon immer ein 
32-Bit-System, und konnte daher auf mehr und leistungsfähigere 
Ressourcen zurückgreifen als das ursprünglich 16-bittige Windows, das 
bis einschließlich Version 3.0 auf 8088-Systemen wie dem IBM PC/XT 
laufen konnte, d.h. mit gerade mal 640 kiB RAM ... Das erklärt die 
unterschiedlich flexiblen Ansätze des Programmladers.

von Oliver S. (oliverso)


Lesenswert?

Harald K. schrieb:
> Der Vollständigkeit halber:

Der weiteren Vollständigkeit halber:

Auch unter Windows lassen sich dll's zur Laufzeit laden. Der Ablauf 
entspricht dabei im Prinzip dem von Linux: dll laden, Symbol auflösen, 
benutzen, wenn nicht mehr gebraucht, alles wieder schließen (und 
unloaden), fertig.

Oliver

von Hmmm (hmmm)


Lesenswert?

Oliver S. schrieb:
> Der weiteren Vollständigkeit halber:
>
> Auch unter Windows lassen sich dll's zur Laufzeit laden.

Das hat er doch bereits sehr konkret beschrieben:

Harald K. schrieb:
> Der Gegenentwurf ist dynamisches Laden unter Kontrolle des bereits
> laufenden Programmes, das erfolgt mit der Win32-Funktion LoadLibrary.
> Zum Bestimmen von Funktionspointern dient die Win32-Funktion
> GetProcAddress.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.