Unix / Linux Loader-Prozess


7

Kann mir jemand sagen, welcher Prozess des Betriebssystems die ELF-Datei (Executable and Linking Format) in den RAM lädt?

Antworten:


10

Ein Benutzer stößt im Allgemeinen auf drei Arten von ELF-Dateien: O-Dateien, reguläre ausführbare Dateien und gemeinsam genutzte Bibliotheken. Während alle diese Dateien unterschiedlichen Zwecken dienen, sind ihre internen Strukturdateien ziemlich ähnlich.

Ein universelles Konzept unter allen verschiedenen ELF-Dateitypen (und auch a.out und vielen anderen ausführbaren Dateiformaten) ist der Begriff eines Abschnitts. Ein Abschnitt ist eine Sammlung von Informationen eines ähnlichen Typs. Jeder Abschnitt repräsentiert einen Teil der Datei. Beispielsweise wird ausführbarer Code immer in einem Abschnitt platziert, der als .text bezeichnet wird . Alle vom Benutzer initialisierten Datenvariablen werden in einem Abschnitt platziert, der als .data bezeichnet wird . und nicht initialisierte Daten werden in einem Abschnitt platziert, der als .bss bekannt ist .

Tatsächlich kann man ein ausführbares Dateiformat entwickeln, in dem alles durcheinander gebracht wird (wie bei MS DOS)). Die Aufteilung der ausführbaren Dateien in Abschnitte hat jedoch wichtige Vorteile. Wenn Sie beispielsweise die ausführbaren Teile einer ausführbaren Datei in den Speicher geladen haben, müssen sich diese Speicherorte nicht mehr ändern. Bei modernen Maschinenarchitekturen kann der Speichermanager Teile des Nur-Lese-Speichers markieren, sodass jeder Versuch, einen Nur-Lese-Speicherort zu ändern, dazu führt, dass das Programm stirbt und den Kern entleert. Anstatt nur zu sagen, dass wir nicht erwarten, dass sich ein bestimmter Speicherort ändert, können wir angeben, dass jeder Versuch, einen schreibgeschützten Speicherort zu ändern, ein schwerwiegender Fehler ist, der auf einen Fehler in der Anwendung hinweist. Abgesehen davon können Sie normalerweise den schreibgeschützten Status nicht für jedes Speicherbyte einzeln festlegen. Stattdessen können Sie den Schutz von Speicherbereichen, die als Seiten bezeichnet werden, individuell festlegen.

Angesichts der Tatsache, dass alle ausführbaren Teile einer ausführbaren Datei im Nur-Lese-Speicher und alle veränderbaren Speicherorte (z. B. Variablen) im beschreibbaren Speicher gespeichert werden sollen, ist es am effizientesten, alle ausführbaren Teile einer ausführbaren Datei in einem Abschnitt zu gruppieren des Speichers ( der Textabschnitt ) und aller modifizierbaren Datenbereiche zusammen in einen anderen Speicherbereich ( im Folgenden als Datenabschnitt bezeichnet ).

Es wird weiter zwischen Datenvariablen unterschieden, die der Benutzer initialisiert hat, und Datenvariablen, die der Benutzer nicht initialisiert hat. Wenn der Benutzer den Anfangswert einer Variablen nicht angegeben hat, macht es keinen Sinn, Speicherplatz in der ausführbaren Datei zu verschwenden, um den Wert zu speichern. Daher werden initialisierte Variablen im Abschnitt .data und nicht initialisierte Variablen im Abschnitt .bss gruppiert. Dies ist besonders, da sie keinen Speicherplatz in der Datei beanspruchen. Sie geben nur an, wie viel Speicherplatz für nicht initialisierte Variablen benötigt wird.

Wenn Sie den Kernel auffordern, eine ausführbare Datei zu laden und auszuführen, suchen Sie zunächst im Image-Header nach Hinweisen zum Laden des Images. Es findet den Textabschnitt in der ausführbaren Datei, lädt ihn in die entsprechenden Teile des Speichers und markiert diese Seiten als schreibgeschützt. Anschließend wird der Abschnitt .data in der ausführbaren Datei gefunden und in den Adressraum des Benutzers geladen, diesmal im Lese- / Schreibspeicher. Schließlich werden der Speicherort und die Größe des .bss-Abschnitts aus dem Bildheader ermittelt und dem Adressraum des Benutzers die entsprechenden Speicherseiten hinzugefügt. Obwohl der Benutzer die Anfangswerte der in .bss platzierten Variablen nicht angegeben hat, initialisiert der Kernel gemäß Konvention den gesamten Speicher auf Null.

Sie sehen also, es ist tatsächlich der Kernel, der die Befehle zum Laden der ausführbaren Datei in den Speicher ausgibt. Der Textabschnitt als Ergebnis solcher Aufrufe wird in den Nur-Lese-Speicher und der Datenabschnitt in den Lese- / Schreibspeicher geladen.


1
Es könnte hinzugefügt werden, dass die dynamische Verknüpfung durch erfolgt ld-linux.so, was ein Userland-Prozess ist.
Jofel

1
@jofel: ja du hast ganz recht Um der Person willen, die diese Frage gestellt hat, möchte ich sagen, dass ld-linux.so ein Interpreter ist, der vom Kernel aus Elfen-Headern gestartet wird. Der Ziel-ELF-Einstiegspunkt wird im Hilfsvektor vom Typ "ENTRY" festgelegt. Der Kernel öffnet den angeforderten Interpreter, ordnet die Speicherbereiche zu und startet seine Ausführung am ELF-Einstiegspunkt von ld. Anschließend analysiert der Loader die Ziel-ELF-Datei, führt seine Loader-Arbeit aus und setzt EIP auf den Ziel-ELF-Einstiegspunkt. So wird die dynamische Verknüpfung von ld-linux.so durchgeführt.
Der dunkle Ritter

Nun nein. Der Kernel (oder der dynamische Linker) kümmert sich überhaupt nicht um die Abschnitte (.text, .data usw.). Sie verwenden die Programm-Header-Tabelle, die die Segmente beschreibt, die in den Speicher geladen werden sollen.
Ysdx

4

Das hängt vom Betriebssystem ab.

Beispielsweise wird auf GNU Hurd die ausführbare Datei vom Exec-Server geladen .

Auf einem typischeren monolithischen Betriebssystem geschieht dies durch:

  1. Der Kernel ordnet die ausführbare Datei und den dynamischen Linker im Speicher zu.

  2. Der dynamische Linker ordnet die gemeinsam genutzten Objekte im Speicher zu.

Der Linux-Kernel selbst wird als ELF-Datei gespeichert: Diese wird vom Bootloader (z. B. GRUB) geladen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.