Stand: 24. April 2016

Inhaltsverzeichnis

über dieses Dokument
was es hierin gibt
wo man die neuste Version bekommt
wie man den Verfasser kontaktiert
welche gesetzlichen Einschränkungen es gibt
Lizenzlosigkeit
Verfasserschutz
Vorbehalt für zusätzliche Einschränkungen für zukünftige Versionen

die Details vom Dateiformat "executable and linking format" ("elf")
Übersicht über die einzelnen Abschnitte
die Details von den einzelnen Abschnitten
Identifikation
Zweck
Anforderungen an den Inhalt von einem Hauptabschnitt
Aufbau
Bezeichnung
Position
Kopfzeile
Zweck
Anforderungen an den Inhalt von einem Hauptabschnitt
Aufbau
Bezeichnung
Position
Programmabschnitte
Aufbau
Hauptabschnitte
Zweck
Kopfzeilen-Tabelle
Zweck
Aufbau
Bezeichnung
Position
Inhalte
Bezeichnung
Position
Unterabschnitte
Zweck
Kopfzeilen-Tabelle
Zweck
Aufbau
Bezeichnung
Position
Inhalte
Bezeichnung
Position
Tabellen
Tabelle für Adressen-Reparaturen
Zweck
Aufbau
Bezeichnung
Position
Tabelle für Dinge
Zweck
Aufbau
Bezeichnung
Position
Tabelle für Exporte und Importe
Zweck
Anforderungen an den Inhalt von einem Hauptabschnitt
Aufbau
Bezeichnung
Position
Tabelle für Prüfsummen
Zweck
Aufbau
Allgemeines
Kopfzeile
Auflistung von Anfangspositionen
Auflistung von Folgepositionen
Bezeichnung
Tabelle für Zeichenketten
Zweck
Aufbau
Bezeichnung

ein "Hallo Welt!"-Programm
Beschreibung
notwendige Voraussetzungen
Vorbereitung
die Datei anlegen
die Datei öffnen
Anmerkungen zum Speichern
elf-Zusatzdaten
den Identifikations-Abschnitt schreiben
Allgemeines
Änderung
Ergebnis
Größe
den Kopfzeilen-Abschnitt schreiben
Allgemeines
Änderung
Ergebnis
Größe
Kopfzeilen-Tabelle für Hauptabschnitte
die Kopfzeile für den Haupt-Programmabschnitt mit dem Anfang von der Datei und dem Maschinencode schreiben
Allgemeines
Änderung
Ergebnis
Größe
die Kopfzeile für den Haupt-Programmabschnitt mit den Daten schreiben
Allgemeines
Änderung
Ergebnis
Größe
Übersicht
der Maschinencode
die Funktion "write"
die Funktions-Identifikationskennung definieren
Allgemeines
Änderung
Ergebnis
Größe
den Wert vom Parameter "Ziel_-_Konsole_-_Datenkanal_-_Identifikationskennung" definieren
Allgemeines
Änderung
Ergebnis
Größe
den Wert vom Parameter "Quelle_-_Nutzdaten_-_Adresse" definieren
Allgemeines
Änderung
Ergebnis
Größe
den Wert vom Parameter "Quelle_-_Nutzdaten_-_Länge_in_Byte" definieren
Allgemeines
Änderung
Ergebnis
Größe
die Verarbeitungsunterbrechung auslösen
Allgemeines
Änderung
Ergebnis
Größe
die Funktion "exit"
die Funktions-Identifikationskennung definieren
Allgemeines
Änderung
Ergebnis
Größe
den Wert vom Parameter "Ziel_-_Exitcode" definieren
Allgemeines
Änderung
Ergebnis
Größe
die Verarbeitungsunterbrechung auslösen
Allgemeines
Änderung
Ergebnis
Größe
den unbenutzten Zwischenraum füllen
Allgemeines
Änderung
Ergebnis
Größe
Übersicht
die Daten
die "Hallo Welt!"-Zeichenkette einfügen
Allgemeines
Änderung
Ergebnis
Größe
Übersicht
das Programm starten
Allgemeines

eine Bibliothek mit einer "Hallo Welt!"-Funktion
Beschreibung
notwendige Voraussetzungen
Vorbereitung
die Datei anlegen
die Datei öffnen
Anmerkungen zum Speichern
elf-Zusatzdaten
den Identifikations-Abschnitt schreiben
den Kopfzeilen-Abschnitt schreiben
Allgemeines
Änderung
Ergebnis
Größe
die Kopfzeilen-Tabelle für Hauptabschnitte
die Kopfzeile für den Haupt-Programmabschnitt mit dem Anfang von der Datei und dem Maschinencode schreiben
Allgemeines
Änderung
Ergebnis
Größe
die Kopfzeile für den Haupt-Programmabschnitt mit der Tabelle für Exporte und Importe schreiben
Allgemeines
Änderung
Ergebnis
Größe
die Kopfzeile für den Haupt-Programmabschnitt mit den Daten schreiben
Allgemeines
Änderung
Ergebnis
Größe
die Tabelle für Adressen-Reparaturen schreiben
Allgemeines
Änderung
Ergebnis
Größe
die Tabelle für Dinge schreiben
Allgemeines
Änderung
Ergebnis
Größe
die Tabelle für Exporte und Importe schreiben
Allgemeines
Änderung
Ergebnis
Größe
die Tabelle für Prüfsummen schreiben
Allgemeines
Änderung
Ergebnis
Größe
die Tabelle für Zeichenketten schreiben
Allgemeines
Änderung
Ergebnis
Größe
Übersicht
der Maschinencode
die Funktion "write"
die Funktions-Identifikationskennung definieren
Allgemeines
Änderung
Ergebnis
Größe
den Wert vom Parameter "Ziel_-_Konsole_-_Datenkanal_-_Identifikationskennung" definieren
Allgemeines
Änderung
Ergebnis
Größe
den Wert vom Parameter "Quelle_-_Nutzdaten_-_Adresse" definieren
Allgemeines
Änderung
Ergebnis
Größe
den Wert vom Parameter "Quelle_-_Nutzdaten_-_Länge_in_Byte" definieren
Allgemeines
Änderung
Ergebnis
Größe
die Verarbeitungsunterbrechung auslösen
Allgemeines
Änderung
Ergebnis
Größe
zurückkehren
Allgemeines
Änderung
Ergebnis
Größe
den unbenutzten Zwischenraum füllen
Allgemeines
Änderung
Ergebnis
Größe
Übersicht
die Daten
die "Hallo Welt!"-Zeichenkette schreiben
Allgemeines
Änderung
Ergebnis
Größe
Übersicht
die Bibliothek benutzen
Allgemeines

weiteres Material zu diesem Thema
Bibliotheken
Dokumente
Programme
Aufnahme von weiteren Materialien


über dieses Dokument

was es hierin gibt

Dieses Dokument
Von diesem Dateiformat gibt es auch eine "64 Bit"-Variante. Diese ist in diesem Dokument allerdings nicht beschrieben.

Im Kapitel "weiteres Material zu diesem Thema - Dokumente" ist ein Dokument aufgelistet, welches allgemeine Informationen über enthält.

wo man die neuste Version bekommt

Derzeit nutze ich OnTheServer.de/Downloads/ um neue Versionen zugänglich zu machen. Sie müsste irgendwo dort in den Unterverzeichnissen sein; das kann sich hin und wieder ein bischen ändern.

Dort gibt es vielleicht auch dieses Dokument in anderen Sprachen.

Die Versions-Angabe von diesem Dokument steht oben rechts ("Stand: ...").

wie man den Verfasser kontaktiert

Der Verfasser von diesem Dokument kann mit der Hilfe von einer elektronischen Nachricht kontaktiert werden. Das hierfür eingerichtete Postfach ist mit der Hilfe von der folgenden Adresse erreichbar:
Kontakt@On(entferne mich)TheServer.de

welche gesetzlichen Einschränkungen es gibt

Lizenzlosigkeit

Dieses Dokument
  • ist an keine Lizenz gebunden.
  • unterliegt nicht den Einschränkungen durch das Urhebergesetz.
  • soll allgemeinfrei (public domain) behandelt werden. Also so, als wenn es sich um ein Eigentum von der Allgemeinheit handelt.

Im Übrigen soll alles, was man auf OnTheServer.de und den Subdomains öffentlich zugänglich findet, entsprechend behandelt werden.

Es gibt auf OnTheServer.de allerdings eine Ausnahme:
Ich lege gelegentlich im Verzeichnis "OnTheServer.de/temp/" urheberrechtlich geschütztes Material ab.

Es ist nicht gestattet, auf irreführendeweise vorzutäuschen, dass das (ursprüngliche) Dokument an eine Lizenz gebunden wäre.

Verfasserschutz

Es ist nicht gestattet, auf irreführendeweise vorzutäuschen, dass man der (ursprüngliche) Verfasser vom Dokument wäre. Der (ursprüngliche) Verfasser muss jedoch nicht namentlich genannt werden.

Vorbehalt für zusätzliche Einschränkungen für zukünftige Versionen

Ich (der ursprüngliche Verfasser) behalte die Möglichkeit, Einschränkungen für
  • die Veränderung oder/und
  • die Verbreitung
aufzuerlegen. Von den Einschränkungen wären lediglich neue Versionen betroffen. Was bisher veröffentlicht wurde, erhält keine weiteren Einschränkungen.

Von diesem Recht werde ich hoffentlich nie gebrauch machen müssen.


die Details vom Dateiformat "executable and linking format" ("elf")

Übersicht über die einzelnen Abschnitte

Bezeichnung: Größe: Offset vom Anfang von der Datei:
Identifikation 16 Byte 0 Byte bis 15 Byte
Kopfzeile 36 Byte 16 Byte bis 51 Byte
Programmabschnitte variable x Byte bis y Byte

Zwischen der Kopfzeile und den Programmabschnitten kann ein unbenutzer Zwischenraum sein.

Wenn dann

die Details von den einzelnen Abschnitten

Identifikation

Zweck

Dieser Abschnitt dient dazu, dass Anwendungen erkennen können,
  • um welches Dateiformat es sich handelt und
  • welchen Zweck diese Datei erfüllen soll.
Außerdem sind hierin allgemeine Informationen enthalten, wie zum Beispiel die verwendete Datenkodierung von Zahlen und die Version von der Kopfzeile.

Anforderungen an den Inhalt von einem Hauptabschnitt

Dieser Abschnitt muss sich in einem Inhalt von einem Hauptabschnitt befinden, welcher
  • in den Arbeitsspeicher geladen wird und
  • für den Bereich vom Arbeitsspeicher eine Leseberechtigung anfordert.

Ich habe es zwar nicht getestet, aber ich gehe davon aus, dass das Lade-Programm erwartet, dass dieser Abschnitt an der Basisadresse im Arbeitsspeicher beginnt.

Aufbau

Beschreibung: Bezeichnung: Größe: Hexadezimalwert: Offset vom Anfang von
dem Abschnitt: der Datei:
Diese Felder ergeben zusammen die Datei-Signatur.

Mit ihrer Hilfe kann erkannt werden, dass die Datei im Dateiformat "executable and linking format" ("elf") gespeichert ist.
EI_MAG0 1 Byte
Bezeichnung: Hexadezimalwert:
ELFMAG0 7F
0 Byte 0 Byte
Der Wert entspricht dem ASCII-Zeichen "E". EI_MAG1 1 Byte
Bezeichnung: Hexadezimalwert:
ELFMAG1 45
1 Byte 1 Byte
Der Wert entspricht dem ASCII-Zeichen "L". EI_MAG2 1 Byte
Bezeichnung: Hexadezimalwert:
ELFMAG2 4C
2 Byte 2 Byte
Der Wert entspricht dem ASCII-Zeichen "F". EI_MAG3 1 Byte
Bezeichnung: Hexadezimalwert:
ELFMAG3 46
3 Byte 3 Byte
Gibt die reguläre Datengröße vom Befehlssatz an, welchen der Prozessor verwenden muss.

Laut Brian Raiter würde das Lade-Programm von Linux den Wert von diesem Feld nicht auswerten.
EI_CLASS 1 Byte
Bedeutung: Bezeichnung: Hexadezimalwert:
4 Byte
(d. h. "32 Bit"-Befehle)
ELFCLASS32 1
8 Byte
(d. h. "64 Bit"-Befehle)
ELFCLASS64 2
4 Byte 4 Byte
Gibt die verwendete Datenkodierung von Zahlen an.

reguläre Schreibweise:
Wert: Zahlensystem:
00010010 00110100 01010110 01111000 2er-System
(Binär-System)
305.419.896 10er-System
(Dezimal-System)
12 34 56 78 16er-System
(Hexadezimal-System)

"00010010 00110100 01010110 01111000"
 │      │ │      │ │      │ │      └─ Wertigkeit: 20
 │      │ │      │ │      │ └─ Wertigkeit: 27
 │      │ │      │ │      └─ Wertigkeit: 28
 │      │ │      │ └─ Wertigkeit: 215
 │      │ │      └─ Wertigkeit: 216
 │      │ └─ Wertigkeit: 223
 │      └─ Wertigkeit: 224
 └─ Wertigkeit: 231
big endian:
Wert: Zahlensystem:
00010010 00110100 01010110 01111000 2er-System
(Binär-System)
018 052 086 120 10er-System
(Dezimal-System)
12 34 56 78 16er-System
(Hexadezimal-System)

"00010010 00110100 01010110 01111000"
 │      │ │      │ │      │ │      └─ Wertigkeit: 20
 │      │ │      │ │      │ └─ Wertigkeit: 27
 │      │ │      │ │      └─ Wertigkeit: 28
 │      │ │      │ └─ Wertigkeit: 215
 │      │ │      └─ Wertigkeit: 216
 │      │ └─ Wertigkeit: 223
 │      └─ Wertigkeit: 224
 └─ Wertigkeit: 231
little endian:
Wert: Zahlensystem:
01111000 01010110 00110100 00010010 2er-System
(Binär-System)
120 086 052 018 10er-System
(Dezimal-System)
78 56 34 12 16er-System
(Hexadezimal-System)

"01111000 01010110 00110100 00010010"
 │      │ │      │ │      │ │      └─ Wertigkeit: 224
 │      │ │      │ │      │ └─ Wertigkeit: 231
 │      │ │      │ │      └─ Wertigkeit: 216
 │      │ │      │ └─ Wertigkeit: 223
 │      │ │      └─ Wertigkeit: 28
 │      │ └─ Wertigkeit: 215
 │      └─ Wertigkeit: 20
 └─ Wertigkeit: 27

Laut Brian Raiter würde das Lade-Programm von Linux den Wert von diesem Feld nicht auswerten.

Vermutlich verwendet das Lade-Programm von Linux die Datenkodierung, welche auch immer die CPU-Architektur zur Laufzeit von Linux verwendet.

Durch die x86-CPU-Architektur wird nur 1 Datenkodierung hardwaremäßig unterstützt:
  • "little endian"
EI_DATA 1 Byte
Bedeutung: Bezeichnung: Hexadezimalwert:
little endian ELFDATA2LSB 1
big endian ELFDATA2MSB 2
5 Byte 5 Byte
Gibt die Versionsnummer von der Kopfzeile an.

Der Wert "1" scheint der einzige, gültige Wert zu sein.

Laut Brian Raiter würde das Lade-Programm von Linux den Wert von diesem Feld nicht auswerten.
EI_VERSION 1 Byte
Bezeichnung: Hexadezimalwert:
EV_CURRENT 1
6 Byte 6 Byte
Gibt an, für welches Betriebssystem diese Datei geschrieben wurde. EI_OSABI 1 Byte
Betriebssystem: Bezeichnung: Hexadezimalwert: Entwickler:
- Das Lade-Programm soll nicht prüfen, ob die Datei im richtigen Betriebssystem ausgeführt wird. - ELFOSABI_NONE 0
NetBSD ELFOSABI_NETBSD 2
Linux aktuell: ELFOSABI_LINUX
früher: ELFOSABI_GNU
3
Solaris ELFOSABI_SOLARIS 6 Sun Microsystems Incorporated
FreeBSD ELFOSABI_FREEBSD 9
Open BSD ELFOSABI_OPENBSD C
FenixOS ELFOSABI_FENIXOS 10

Es gibt weitere gültige Werte, welche in diesem Dokument nicht beschrieben sind.
7 Byte 7 Byte
Leider konnte ich nicht zufriedenstellend herausfinden, wofür dieses Feld dient.

Ich habe in diesem Feld bisher nur den Wert "0" gesehen.
EI_ABIVERSION 1 Byte
Bedeutung: Hexadezimalwert:
Mit der Hilfe von diesem Wert wird keine Auskunft gemacht. 0
8 Byte 8 Byte
Diese Bytes sind unbenutzt.

Die offizielle Beschreibung vom Dateiformat empfiehlt, alle Bits auf "0" zu setzen.
EI_PAD 7 Byte 0 für jedes Byte 9 Byte
bis
15 Byte
9 Byte
bis
15 Byte
Gesamtgröße: 16 Byte

Bezeichnung

In dieser Dokumentation ist dieser Teil ein eigener Abschnitt von der Datei. Diese Ansichtsweise ist allerdings nicht üblich, sondern es ist üblich, diesen Abschnitt als einen Unterabschnitt von der Kopfzeile anzusehen.

Es ist allerdings dennoch üblich, für diesen Teil einen eigenen Namen zu verwenden. Er wird "e_ident" genannt.

Position

Dieser Abschnitt beginnt 0 Byte nach dem Anfang von der Datei. Er ist also das Erste, welches in der Datei platziert wird.

Kopfzeile

Zweck

Die Kopfzeile macht
  • allgemeine Angaben über den Inhalt
  • und Auskünfte über den Aufbau
von der Datei.

Durch die Angaben über den Aufbau von der Datei werden, unter anderem,
  • Größen und
  • Positionen
von anderen Inhalten genannt.

Anforderungen an den Inhalt von einem Hauptabschnitt

Dieser Abschnitt muss sich in einem Inhalt von einem Hauptabschnitt befinden, welcher
  • in den Arbeitsspeicher geladen wird und
  • für den Bereich vom Arbeitsspeicher eine Leseberechtigung anfordert.

Ich habe es zwar nicht getestet, aber ich gehe davon aus, dass das Lade-Programm erwartet, dass dieser Abschnitt im Arbeitsspeicher direkt nach dem Abschnitt "Identifikation" beginnt. Also ohne unbenutzte Bytes zwischendrin.

Aufbau

Beschreibung: Bezeichnung: Größe: Hexadezimalwert: Offset vom Anfang von
dem Abschnitt: der Datei:
gibt den Dateityp an e_type 2 Byte
Bezeichnung: Dateityp: Hexadezimalwert:
ET_REL "Objekt-Datei" oder "verschiebbare Datei" (im Englischen: "relocatable file")

Ich bin mir nicht 100%ig sicher, wofür eine solche Datei dient. Soweit ich das verstanden habe, ist sie das Ausgangsprodukt von einem Compiler, welcher die Link-Funktionalität nicht enthält.

Somit wäre diese Datei auch das Eingangsprodukt von einem Link-Programm. Das Ausgangsprodukt von einem Link-Programm ist
  • eine ausführbare Datei oder
  • eine Bibliothek.
1
ET_EXEC ausführbare Datei 2
ET_DYN Bibliothek 3
0 Byte
bis
1 Byte
16 Byte
bis
17 Byte
gibt die notwendige CPU-Hardware-Architektur an e_machine 2 Byte
Architektur: Bezeichnung: Datengröße vom Befehlssatz: Entwickler: Hexadezimalwert:
- Das Lade-Programm soll nicht prüfen, ob die Datei auf der richtigen Architektur ausgeführt wird. - EM_NONE 0

ARM EM_ARM 4 Byte
(d. h. "32 Bit"-Befehle)
ARM Limited 28
ARM EM_AARCH64 8 Byte
(d. h. "64 Bit"-Befehle)
ARM Limited B7

x86 EM_X86_64 8 Byte
(d. h. "64 Bit"-Befehle)
Advanced Micro Devices Incorporated (AMD) 3E
x86 EM_860 2 Byte
(d. h. "16 Bit"-Befehle), also die Architekturen von den Prozessoren "8086" bis "80286" (alias "Intel286")
Intel Corporation 7
x86 EM_386 4 Byte
(d. h. "32 Bit"-Befehle), also die Architektur vom Prozessor "80386" (alias "Intel386"), oder eine spätere, darauf basierende Architektur
Intel Corporation 3
x86 EM_IA_64 8 Byte
(d. h. "64 Bit"-Befehle)
Intel Corporation 32

Es gibt weitere gültige Werte, welche in dieser Dokumentation nicht beschrieben sind.

Früher gab es auch einen Wert für einen 486er. Für solche Prozessoren wird allerdings heute der Wert "3" verwendet.
2 Byte
bis
3 Byte
18 Byte
bis
19 Byte
Laut der offiziellen Beschreibung vom Dateiformat gibt dieses Feld an, ob es sich um die aktuelle Version von der Datei handelt.

Nach meinem Verständnis gibt dieses Feld allerdings vielmehr an, ob die Datei verwendet werden kann, oder ob
  • beim Kompilieren oder
  • beim Linken
ein Fehler aufgetreten ist.

Laut Brian Raiter würde das Lade-Programm von Linux den Wert von diesem Feld nicht auswerten.
e_version 4 Byte
Bedeutung gemäß Bezeichnung: Hexadezimalwert:
der offiziellen Beschreibung: meinem Verständnis:
es handelt sich nicht um die aktuelle Version die Datei ist unbrauchbar EV_NONE 0
es handelt sich um die aktuelle Version die Datei ist vermutlich brauchbar EV_CURRENT 1
4 Byte
bis
7 Byte
20 Byte
bis
23 Byte
Gibt den Offset in Byte an,
  • vom Anfang vom linearen Speicher
  • bis zum ersten Byte vom Maschinencode, welcher nach dem Abschließen vom Lade-Vorgang ausgeführt werden soll,
wenn er in den Arbeitsspeicher geladen wurde.

Die Anfangsadresse von diesem Maschinencode muss unter der Berücksichtigung von der vom Erzeuger gewünschten Basisadresse angegeben werden. Es handelt sich bei der x86-Architektur um einen Offset vom Segmentanfang.

Bei einer Bibliothek gilt:
Dieses Feld dient nicht zur Angabe von einer Initialisierungsroutine. Stattdessen können in Linux Bibliotheken gestartet werden, wie ausführbare Dateien gestartet werden. Das Feld kann in diesem Fall durch seinen Wert beispielsweise auf ein kleines Programm zeigen, welches die Funktionen von der Bibliothek zu Demonstrationszwecken verwendet.

Das Lade-Programm von Linux ist ein Beispiel für eine Bibliothek, welche ein Programm enthält.

Bei meinen Tests hatte ich beim Starten von meiner eigenen Bibliothek allerdings stets die Fehlermeldung "Segmentation fault" angezeigt bekommen.

Wenn
  • es keinen solchen Maschinencode gibt,
dann
  • wird für dieses Feld der Wert "0" angegeben.
e_entry 4 Byte 8 Byte
bis
11 Byte
24 Byte
bis
27 Byte
Gibt den Offset in Byte an, wenn sie noch in der Datei auf der Festplatte/Diskette/CD/was auch immer ist.

Wenn
  • diese Tabelle nicht existiert,
dann
  • wird für dieses Feld der Wert "0" angegeben.
e_phoff 4 Byte 12 Byte
bis
15 Byte
28 Byte
bis
31 Byte
Gibt den Offset in Byte an, wenn sie noch in der Datei auf der Festplatte/Diskette/CD/was auch immer ist.

Wenn
  • diese Tabelle nicht existiert,
dann
  • wird für dieses Feld der Wert "0" angegeben.
e_shoff 4 Byte 16 Byte
bis
19 Byte
32 Byte
bis
35 Byte
Die einzelnen Bits von diesen 4 Byte machen Auskünfte über notwendige Fähigkeiten vom Prozessor.

Die Bedeutungen von den einzelnen Bits sind wie folgt:
allgemeine Beschreibung: Bedeutung wenn Wert=="0": Bedeutung wenn Wert=="1": Bezeichnung: Wertigkeiten:
- Bei der x86-CPU-Architektur hat keines von den Bits bisher eine besondere Bedeutung bekommen und sollten daher auf "0" gesetzt werden. - 20
bis
231

Laut Brian Raiter würde das Lade-Programm von Linux den Wert von diesem Feld nicht auswerten, wenn die x86-CPU-Architektur verwendet wird.
e_flags 4 Byte 20 Byte
bis
23 Byte
36 Byte
bis
39 Byte
Gibt die Größe
  vom Abschnitt "Identifikation"
+ vom Abschnitt "Kopfzeile"
zusammengerechnet in Byte an. Dies ist also immer der Wert "52|d = 34|h".

Laut Brian Raiter würde das Lade-Programm von Linux den Wert von diesem Feld nicht auswerten.
e_ehsize 2 Byte 34 24 Byte
bis
25 Byte
40 Byte
bis
41 Byte
Gibt die Größe von jedem Eintrag in der "Kopfzeilen-Tabelle für Hauptabschnitte" in Byte an. Hier ist nicht die Gesamtgröße von der "Kopfzeilen-Tabelle für Hauptabschnitte" gemeint, sondern wie groß jeder einzelne Eintrag ist.

Im Folgenden ist die Bedeutung vom Wert von diesem Feld grafisch dargestellt:
╔══════════════════════════════════════╗
║              Kopfzeile               ║
╟───────────────────┬──────────────────╢
║    Bedeutung:     │      Feld:       ║
╠═══════════════════╪══════════════════╣
║                   │                  ║
╟───────────────────┼──────────────────╢
 Größe pro Eintrag │   e_phentsize    ╟──┐
╟───────────────────┼──────────────────╢  
║                   │                  ║  
╚═══════════════════╧══════════════════╝  
                                          
╔══════════════════════════════════════╗  
Kopfzeilen-Tabelle für Hauptabschnitte║  
╠══════════════════════════════════════╣ 
║                                      ║├◄┘
╟──────────────────────────────────────╢
║                                      ║
╟──────────────────────────────────────╢
║                                      ║
╟──────────────────────────────────────╢
║                                      ║
╚══════════════════════════════════════╝

Alle Einträge sind jeweils "32 Byte"-groß.

Laut Brian Raiter würde das Lade-Programm von Linux den Wert von diesem Feld in älteren Versionen vom Kernel nicht auswerten, in neueren Versionen vom Kernel allerdings schon. (Diese Aussage ist von etwa Mai 2009)
e_phentsize 2 Byte 20 26 Byte
bis
27 Byte
42 Byte
bis
43 Byte
Gibt die Anzahl der Eintrage in der "Kopfzeilen-Tabelle für Hauptabschnitte" an.

Im Folgenden ist die Bedeutung vom Wert von diesem Feld grafisch dargestellt:
╔══════════════════════════════════════╗
║              Kopfzeile               ║
╟───────────────────┬──────────────────╢
║    Bedeutung:     │      Feld:       ║
╠═══════════════════╪══════════════════╣
║                   │                  ║
╟───────────────────┼──────────────────╢
Anzahl der Einträge│     e_phnum      ╟──┐
╟───────────────────┼──────────────────╢  
║                   │                  ║  
╚═══════════════════╧══════════════════╝  
                                          
╔══════════════════════════════════════╗  
Kopfzeilen-Tabelle für Hauptabschnitte║  
╠══════════════════════════════════════╣ 
║                                      ║ 
╟──────────────────────────────────────╢ 
║                                      ║ 
╟──────────────────────────────────────╢├◄┘
║                                      ║
╟──────────────────────────────────────╢
║                                      ║
╚══════════════════════════════════════╝

Wenn
  • diese Tabelle nicht existiert,
dann
  • wird für dieses Feld der Wert "0" angegeben.
e_phnum 2 Byte 28 Byte
bis
29 Byte
44 Byte
bis
45 Byte
Gibt die Größe von jedem Eintrag in der "Kopfzeilen-Tabelle für Unterabschnitte" in Byte an. Hier ist nicht die Gesamtgröße von der "Kopfzeilen-Tabelle für Unterabschnitte" gemeint, sondern wie groß jeder einzelne Eintrag ist.

Im Folgenden ist die Bedeutung vom Wert von diesem Feld grafisch dargestellt:
╔══════════════════════════════════════╗
║              Kopfzeile               ║
╟───────────────────┬──────────────────╢
║    Bedeutung:     │      Feld:       ║
╠═══════════════════╪══════════════════╣
║                   │                  ║
╟───────────────────┼──────────────────╢
 Größe pro Eintrag │   e_shentsize    ╟──┐
╟───────────────────┼──────────────────╢  
║                   │                  ║  
╚═══════════════════╧══════════════════╝  
                                          
╔══════════════════════════════════════╗  
Kopfzeilen-Tabelle für Unterabschnitte║  
╠══════════════════════════════════════╣ 
║                                      ║├◄┘
╟──────────────────────────────────────╢
║                                      ║
╟──────────────────────────────────────╢
║                                      ║
╟──────────────────────────────────────╢
║                                      ║
╚══════════════════════════════════════╝


Alle Einträge sind jeweils "40 Byte"-groß.

Wenn
  • diese Tabelle nicht existiert,
dann
  • scheint das Lade-Programm von Linux diesen Wert nicht auszuwerten.
e_shentsize 2 Byte 28 30 Byte
bis
31 Byte
46 Byte
bis
47 Byte
Gibt die Anzahl der Eintrage in der "Kopfzeilen-Tabelle für Unterabschnitte" an.

Im Folgenden ist die Bedeutung vom Wert von diesem Feld grafisch dargestellt:
╔══════════════════════════════════════╗
║              Kopfzeile               ║
╟───────────────────┬──────────────────╢
║    Bedeutung:     │      Feld:       ║
╠═══════════════════╪══════════════════╣
║                   │                  ║
╟───────────────────┼──────────────────╢
Anzahl der Einträge│     e_shnum      ╟──┐
╟───────────────────┼──────────────────╢  
║                   │                  ║  
╚═══════════════════╧══════════════════╝  
                                          
╔══════════════════════════════════════╗  
Kopfzeilen-Tabelle für Unterabschnitte║  
╠══════════════════════════════════════╣ 
║                                      ║ 
╟──────────────────────────────────────╢ 
║                                      ║ 
╟──────────────────────────────────────╢├◄┘
║                                      ║
╟──────────────────────────────────────╢
║                                      ║
╚══════════════════════════════════════╝

In diesem Feld darf maximal der Wert "65.279|d" stehen. Wenn dann
  • wird dies anderst angegeben.

    Diese andere Angabe-Form ist in dieser Dokumentation allerdings nicht beschrieben.

Wenn
  • diese Tabelle nicht existiert,
dann
  • wird für dieses Feld der Wert "0" angegeben.
e_shnum 2 Byte 32 Byte
bis
33 Byte
48 Byte
bis
49 Byte
Gibt die Position von einer bestimmten Kopfzeile in der Kopfzeilen-Tabelle für Unterabschnitte an. Diese Kopfzeile macht die Auskünfte über den Inhalt von einem Unterabschnitt, welcher eine bestimmte Tabelle für Zeichenketten enthält. Diese Tabelle für Zeichenketten enthält die Namen von den Inhalten von den Unterabschnitten.

Im Folgenden ist die Bedeutung vom Wert von diesem Feld grafisch dargestellt:
     ╔══════════════════════════════════════╗
     ║              Kopfzeile               ║
     ╟──────────────────┬───────────────────╢
     ║    Bedeutung:    │       Feld:       ║
     ╠══════════════════╪═══════════════════╣
     ║                  │                   ║
     ╟──────────────────┼───────────────────╢
┌────╢     Position     │    e_shstrndx     ╟───────┐
    ╚══════════════════╧═══════════════════╝       
                                                   
    ╔══════════════════════════════════════╗       
    ║Kopfzeilen-Tabelle für Unterabschnitte║       
    ╟──────────────────┬───────────────────╢       
    ║Feld "sh_offset": │  Feld "sh_name":  ║       
    ╠══════════════════╪═══════════════════╣       
    ║                  │                   ║       
    ╟──────────────────┼───────────────────╢       
└►┌──╢  Anfangsadresse        Offset       ╟─────┐◄┘
    ╟──────────────────┼───────────────────╢     
    ║                  │                   ║     
    ╟──────────────────┼───────────────────╢     
    ║                  │                   ║     
    ╟──────────────────┼───────────────────╢     
  ┌─╢  Anfangsadresse        Offset       ╟──┐  
   ╚══════════════════╧═══════════════════╝    
                                               
   ╔══════════════════════════════════════╗    
   ║   Inhalte von den Unterabschnitten   ║    
   ╠══════════════════════════════════════╣    
   ║                                      ║    
  └►╫──────────────────────────────────────╢    
    ║            Maschinencode             ║    
  └─►╫──────────────────────────────────────╢    
     ║╔════════════════════════════════════╗║    
     ║║     Tabelle für Zeichenketten      ║║    
     ║╠════════════════════════════════════╣║  
     ║║                                    ║║├◄┘ 
     ║╟────────────────────────────────────╢║  ├◄┘
     ║║            ".text"                 ║║   
     ║╟────────────────────────────────────╢║   
     ║║            ".shstrtab"             ║║
     ║╟────────────────────────────────────╢║
     ║║                                    ║║
     ║╚════════════════════════════════════╝║
     ╟──────────────────────────────────────╢
     ║                                      ║
     ╟──────────────────────────────────────╢
     ║                                      ║
     ╚══════════════════════════════════════╝

Die Position wird von "0" ausgehend gezählt.
  • Der "erste" Eintrag hat also die Position "0",
  • der "zweite" Eintrag hat die Position "1",
  • und so weiter.

Wenn dann
  • wird für dieses Feld der Wert "0" angegeben.
e_shstrndx 2 Byte 34 Byte
bis
35 Byte
50 Byte
bis
51 Byte
Gesamtgröße: 36 Byte

Bezeichnung

Die Kopfzeile wird auch "elf-Kopfzeile" (im Englischen: "elf header") genannt.

Position

Dieser Abschnitt beginnt 16 Byte nach dem Anfang von der Datei.

Seine Platzierung in der Datei beginnt also direkt nach dem "16 Byte"-großen Abschnitt "Identifikation".

Programmabschnitte

Aufbau

In einer Datei kann es mehrere Haupt-Programmabschnitte geben. Jeder Haupt-Programmabschnitt hat
Die Kopfzeilen werden in der Kopfzeilen-Tabelle für Hauptabschnitte aufgelistet.

Jeder Inhalt von einem Hauptabschnitt kann mit mehreren Inhalten von Unterabschnitten gefüllt werden, welche nicht zwangsweise eine thematische Zusammengehörigkeit besitzen müssen. Welche Inhalte von Unterabschnitten in einem gemeinsamen Inhalt von einem Hauptabschnitt platziert werden können, hängt vielmehr von den notwendigen Attributen vom Bereich im Arbeitsspeicher ab.

Wenn dann
In einer Datei kann es auch mehrere Unter-Programmabschnitte geben. Jeder Unter-Programmabschnitt hat
Die Kopfzeilen werden in der Kopfzeilen-Tabelle für Unterabschnitte aufgelistet.

Im Nachfolgenden ist dieser Aufbau grafisch dargestellt:
╔══════════════════════════════════════╗   ╔════════════════════════════════╗    ╔══════════════════════════════════════╗
Kopfzeilen-Tabelle für Hauptabschnitte║   ║        Rest vom Segment        ║    ║Kopfzeilen-Tabelle für Unterabschnitte
╠════════════════╤═══╤═════════════════╣   ╠════════════════════════════════╣    ╠═════════════════╤═══╤════════════════╣
║    Größe #1    │...│Anfangsadresse #1╟──►╫╔══════════════════════════════╗║ ┌──╢Anfangsadresse #1│...│    Größe #1    ║
╟────────────────┼───┼─────────────────╢   ║║ Inhalt vom Hauptabschnitt #1 ║║   ╟─────────────────┼───┼────────────────╢
║    Größe #2    │...│Anfangsadresse #2╟─┐ ║╠══════════════════════════════╣║ ┌─╢Anfangsadresse #2│...│    Größe #2    ║
╟────────────────┼───┼─────────────────╢  ║║╔════════════════════════════╗╫╫◄┘ ╟─────────────────┼───┼────────────────╢
║    Größe #3    │...│Anfangsadresse #3╟┐ ║║║Inhalt vom Unterabschnitt #1║║║  ┌╢Anfangsadresse #3│...│    Größe #3    ║
╚════════════════╧═══╧═════════════════╝ ║║╚════════════════════════════╝║║  ╚═════════════════╧═══╧════════════════╝
                                         ║╟──────────────────────────────╢║  
                                         ║║                              ║║  
                                         ║╟──────────────────────────────╢║  
                                         ║║╔════════════════════════════╗╫╫◄─┘
                                         ║║║Inhalt vom Unterabschnitt #2║║║   
                                         ║║╚════════════════════════════╝║║   
                                         ║╚══════════════════════════════╝║   
                                         ║                                ║   
                                        └►╫╔══════════════════════════════╗║   
                                          ║║ Inhalt vom Hauptabschnitt #2 ║║   
                                          ║╠══════════════════════════════╣║   
                                          ║║                              ║║   
                                          ║╚══════════════════════════════╝║   
                                          ║                                ║   
                                        └─►╫╔══════════════════════════════╗║   
                                           ║║ Inhalt vom Hauptabschnitt #3 ║║   
                                           ║╠══════════════════════════════╣║   
                                           ║║                              ║║   
                                           ║╟──────────────────────────────╢║   
                                           ║║                              ║║   
                                           ║╟──────────────────────────────╢║   
                                           ║║╔════════════════════════════╗╫╫◄──┘
                                           ║║║Inhalt vom Unterabschnitt #3║║║
                                           ║║╚════════════════════════════╝║║
                                           ║╟──────────────────────────────╢║
                                           ║║                              ║║
                                           ║╚══════════════════════════════╝║
                                           ╚════════════════════════════════╝

Hauptabschnitte

Zweck

Ein Inhalt von einem Hauptabschnitt dient als Container, welcher sich im Wesentlichen dadurch von den anderen Inhalten von anderen Hauptabschnitten unterscheidet, dass der vorgesehene Bereich im Arbeitsspeicher andere Attribute, wie zum Beispiel
  • "ausführbar" oder
  • "beschreibbar",
hat. Da ihre Kopfzeilen jedoch auch dafür verwendet werden, um anzugeben, welche Bereiche von der Datei in den Arbeitsspeicher geladen werden sollen, sind solche Inhalte notwendig.

Ihre Kopfzeilen machen Auskünfte, wie zum Beispiel über
  • die gewünschte Anfangsadresse im Segment,
  • die Größe und
  • die Zugriffsberechtigungen, wie zum Beispiel
    • "ausführbar" oder
    • "beschreibbar",
    von diesem Bereich vom Arbeitsspeicher.

Kopfzeilen-Tabelle

Zweck
Diese Tabelle dient dafür, um Angaben über die einzelnen Inhalte von Hauptabschnitten zu machen, wie zum Beispiel
  • wo sie in der Datei beginnen, wenn sie noch auf der Festplatte/Diskette/CD/was auch immer sind,
  • wo sie beginnen sollen, wenn sie in den Arbeitsspeicher geladen wurden,
  • wie groß sie sind und
  • welche Attribute (zum Beispiel "ausführbar" oder "beschreibbar") der durch den Inhalt von einem Hauptabschnitt belegte Bereich im Arbeitsspeicher erhalten soll.

Aufbau
In dieser Tabelle ist für jeden Inhalt von einem Hauptabschnitt von der Datei eine Kopfzeile.

Jede Kopfzeile ist "32 Byte"-groß. Zwischen den einzelnen Kopfzeilen dürfen keine unbenutzten Bytes sein.

Die Anzahl der Kopfzeilen in dieser Tabelle wurde im Feld "e_phnum" vom Kopfzeilen-Abschnitt definiert. Hierdurch ist das Ende von dieser Tabelle bekannt.

Die einzelnen Kopfzeilen in dieser Tabelle müssen anhand von der Anfangsadresse vom Inhalt vom Hauptabschnitt im Segment sortiert sein. Das heißt, dass der Inhalt von einem Hauptabschnitt mit der niedrigsten Anfangsadresse als erstes in der "Kopfzeilen-Tabelle für Hauptabschnitte" kommen muss.

Diese Anforderung an die Sortierung bezieht sich allerdings nur auf die Kopfzeilen von den Inhalten von Hauptabschnitten, welche wegen dem Wert "1|h" im Feld "p_type" auch tatsächlich in den Arbeitsspeicher geladen werden. Alle anderen Kopfzeilen können
  • darüber,
  • darunter oder
  • zwischendrin
in dieser Kopfzeilen-Tabelle verstreut sein.

Die Kopfzeilen bestehen jeweils aus mehreren Feldern, welche Auskünfte über den jeweiligen Inhalt vom Hauptabschnitt machen. Der Aufbau von einer solchen Kopfzeile sieht wie folgt aus:
Beschreibung: Bezeichnung: Größe: Offset vom Anfang von der Kopfzeile:
Macht Angaben über den Inhalt vom Hauptabschnitt, wie zum Beispiel,
  • ob er in den Arbeitspeicher geladen werden soll und
  • ob er einen Inhalt enthält, welcher eine besondere Bedeutung hat.

Bedeutungen: Bezeichnung: Hexadezimalwert:
PT_NULL 0
PT_LOAD 1
PT_DYNAMIC 2
  • Am Anfang von diesem Inhalt vom Hauptabschnitt ist eine Zeichenkette platziert, welche
    • den Pfad und
    • den Dateinamen
    von einer bestimmten Bibliothek angibt.

    Diese Bibliothek stellt dem Lade-Programm die Funktionen zur Verfügung, welche dafür sorgen, dass die angeforderten Bibliotheken bereit gestellt werden.

    Das Betriebssystem "Linux" stellt für diesen Zweck für die x86-Architektur prinzipiell 2 fertige Bibliotheken zur Verfügung, von welchen aber in einem kompilierten System nur 1 verfügbar ist:
    Wenn
    • Linux als "32 Bit"-Version kompiliert wurde,
    dann
    • ist eine Bibliothek mit dem folgenden standardmäßigen
      • Pfad und
      • Dateinamen
      verfügbar:
      /lib/ld-linux.so.2
    Ansonsten wenn
    • Linux als "64 Bit"-Version kompiliert wurde,
    dann
    • ist eine Bibliothek mit dem folgenden standardmäßigen
      • Pfad und
      • Dateinamen
      verfügbar:
      /lib/ld-linux-ia64.so.2

    Die Zeichenkette muss
    • ASCII-kodiert sein und
    • durch ein weiteres Byte abgeschlossen sein, von welchem alle Bits auf "0|b" gesetzt sind.

    Das Lade-Programm von Linux verwendet die erste Angabe, welche es findet. Die Angabe ist also in der Regel in der Anwendung enthalten. Wenn
    • also beispielsweise die Anwendung Bibliotheken importiert, welche selbst Dinge aus anderen Bibliotheken benötigen,
    dann
    • ist in den importierten Bibliotheken keine solche Angabe/Zeichenkette notwendig.
  • Vor dieser Kopfzeile darf keine Kopfzeile kommen, welche in diesem Feld den Wert "1|h" gespeichert hat.
PT_INTERP 3

Es gibt weitere gültige Werte, welche in dieser Dokumentation nicht beschrieben sind.

Um die Bedeutungen aus der obrigen Tabelle zu addieren/kombinieren, muss ein Inhalt von einem Unterabschnitt in mehrere Inhalte von Hauptabschnitten platziert werden. Die Speicherbereiche von den Inhalten von Hauptabschnitten überschneiden sich in diesem Fall
  • sowohl in der Datei auf der Festplatte/Diskette/CD/was auch immer
  • als auch im Arbeitsspeicher.
Der Inhalt vom Unterabschnitt muss in der Schnittmenge enthalten sein und ist damit nur 1 mal vorhanden.
p_type 4 Byte 0 Byte
bis
3 Byte
Gibt den Offset in Byte an, wenn er noch in der Datei auf der Festplatte/Diskette/CD/was auch immer ist.

Der Wert von diesem Feld muss restlos durch den Wert vom Feld "p_align" teilbar sein. Wenn
  • also beispielsweise für das Feld "p_align" der Wert "8|d" angegeben wird,
dann
  • kann für den Wert vom Feld "p_offset" der Wert
    • "0|d",
    • "8|d",
    • "16|d",
    • "24|d" oder
    • ...
    angegeben werden.

Laut der offiziellen Beschreibung vom Dateiformat muss der Wert von diesem Feld auch restlos durch die Größe von einer RAM-Seite teilbar sein. Eine RAM-Seite ist in Linux bei der x86-Architektur "4|d Kilobyte"-groß.

Das Lade-Programm von Linux akzeptiert jedoch auch andere Werte, solange die folgende Bedingung erfüllt ist:
(
(p_vaddr - p_offset) & (p_align - 1|d)
)==0|d

Beim Operator "&" handelt es sich um eine logische und-Verknüpfung auf einer Bit-Basis.

Um diese Bedingung zu erfüllen, kann man sich darum bemühen,
  • den links neben dem Operator stehenden Teil "(p_vaddr - p_offset)" oder
  • den rechts neben dem Operator stehenden Teil "(p_align - 1|d)"
auf "0|d" zu bringen. Beide Ansätze sind nicht ganz unproblematisch, daher ist es eine Abwägung dessen, ob
  • man eine große Datei akzeptiert, dafür aber die Inhalte auf separate RAM-Seiten verteilen kann und somit die Zugriffsrechte zuverlässig voneinander trennen kann, oder
  • man eine kleine Datei bevorzugt, dafür aber die Inhalte nicht auf separate RAM-Seiten verteilen kann und somit die Zugriffsrechte nicht zuverlässig voneinander trennen kann. Somit werden für manche Inhalte - wenn vielleicht auch nur für einen Teil vom Inhalt - Zugriffsrechte erteilt, welche eigentlich nicht erteilt werden sollten.

Es gibt allerdings noch eine weitere Möglichkeit. Diese ist etwas aufwendig, sie ermöglicht allerdings kleine Dateien und dennoch separate RAM-Seiten. Bei dieser Möglichkeit gilt beispielsweise folgendes:
0. Inhalt:
  • Größe: 241|d Byte
  • p_offset: 0|d Byte
  • p_vaddr: Basisadresse

1. Inhalt
  • Größe: 377|d Byte
  • p_offset: 241|d Byte
  • p_vaddr: Basisadresse + 1 RAM-Seite + 241|d Byte

p_align = 0|d oder 1 RAM-Seite

Bei dieser Möglichkeit beginnt der 1. Inhalt nicht am Anfang von der RAM-Seite. Somit wird Arbeitsspeicher verschwendet und es ist gegebenenfalls für jeden solchen Inhalt 1 weitere RAM-Seite notwendig. Wenn
  • im Inhalt Daten gespeichert sind, welche der Prozessor irgendwann lesen soll,
dann
  • ist es eventuell gewünscht, diese auf eine restlos durch beispielsweise 4|d Byte teilbare Adresse auszurichten. Daher muss in einem solchen Fall gegebenenfalls weiterer Aufwand betrieben werden, um dies zu erreichen.

Wenn
  • die Bedingung nicht erfüllt ist,
dann
  • wird die Meldung
    "ELF load command address/offset not properly aligned"
    ausgegeben und
  • der Lade-Vorgang wird abgebrochen.
p_offset 4 Byte 4 Byte
bis
7 Byte
Gibt den gewünschten Offset in Byte an, wenn er in den Arbeitsspeicher geladen wurde.

Diese Anfangsadresse muss unter der Berücksichtigung von der vom Erzeuger gewünschten Basisadresse angegeben werden. Es handelt sich bei der x86-Architektur um einen Offset vom Segmentanfang.

Der Wert von diesem Feld muss restlos durch den Wert vom Feld "p_align" teilbar sein. Wenn
  • also beispielsweise für das Feld "p_align" der Wert "8|d" angegeben wird,
dann
  • kann für den Wert vom Feld "p_vaddr" der Wert
    • "0|d",
    • "8|d",
    • "16|d",
    • "24|d" oder
    • ...
    angegeben werden.

Laut der offiziellen Beschreibung vom Dateiformat muss der Wert von diesem Feld auch restlos durch die Größe von einer RAM-Seite teilbar sein. Weitere Informationen hierzu sind beim Feld "p_offset" angegeben.

Wenn
  • im Feld "p_type" der Wert "3|d" angegeben ist,
dann
  • muss nach meinen Tests ein gültiger Wert für das Feld "p_vaddr" angegeben werden.

    Dies hat in meinen Tests unabhängig davon gegolten, ob der Inhalt vom Hauptabschnitt in den Arbeitsspeicher geladen werden soll oder nicht. Wenn
    • der Wert "0|d" angegeben war,
    dann
    • wurde der Lade-Vorgang abgebrochen und die Fehlermeldung "Segmentation fault" wurde ausgegeben.
p_vaddr 4 Byte 8 Byte
bis
11 Byte
Dieses Feld ist unbenutzt.

Alle Bits von diesen Bytes sollten auf "0|b" gesetzt werden.
p_paddr 4 Byte 12 Byte
bis
15 Byte
Gibt die Größe vom Inhalt vom Hauptabschnitt in Byte an, wenn er noch in der Datei auf der Festplatte/Diskette/CD/was auch immer ist.

Wenn
  • beispielsweise der komplette Inhalt vom Hauptabschnitt für Daten vorgesehen ist, welche erst zur Laufzeit zum ersten Mal mit Werten definiert werden,
dann
  • kann diese Größenangabe den Wert "0|d" haben.

Wenn dann
  • darf dieser Wert maximal dem Wert vom Feld "p_memsz" entsprechen.
p_filesz 4 Byte 16 Byte
bis
19 Byte
Gibt die Größe vom Inhalt vom Hauptabschnitt in Byte an, welche er im Arbeitsspeicher haben soll.

Wenn
  • der Inhalt vom Hauptabschnitt nicht in den Arbeitsspeicher geladen werden soll, weil es sich beispielsweise um Urheberrechts-Hinweise handelt, welche vielleicht hilfreich in der Datei sind, welche aber keinen Nutzen im Arbeitsspeicher bringen,
dann
  • kann der Wert "0|d" als Größe angegeben werden.

Wenn
  • für dieses Feld ein größerer Wert angegeben ist, als im Feld "p_filesz" angegeben ist,
dann
  • werden alle Bits von den restlichen Bytes im Arbeitsspeicher auf "0|b" gesetzt.

    Laut Brian Raiter muss allerdings in diesem Fall, damit dies möglich ist, mit der Hilfe vom Feld "p_flags" das Bit mit der Wertigkeit "21|d" auf "1|b" gesetzt werden, was bedeutet, dass der Inhalt vom Hauptabschnitt vom Prozess beschrieben werden darf.
p_memsz 4 Byte 20 Byte
bis
23 Byte
Die einzelnen Bits von diesem Feld machen Auskünfte über die Zugriffsberechtigungen, welche der Prozess auf alle RAM-Seiten erhalten soll, welche - wenn auch nur teilweise - von diesem Inhalt vom Hauptabschnitt belegt werden.

Wenn dann
  • werden die Zugriffsberechtigungen mit der Hilfe von einer logischen oder-Verknüpfung auf einer Bit-Basis kombiniert.

    Wenn dann
    • können die Daten von der RAM-Seite
      • sowohl beschrieben,
      • als auch gelesen
      werden.

Die Bedeutungen von den einzelnen Bits sind wie folgt:
allgemeine Beschreibung: Bedeutung wenn Wert=="0": Bedeutung wenn Wert=="1": Bezeichnung: Wertigkeit:
Mit der Hilfe von diesem Bit wird angegeben, ob der Bereich vom Arbeitsspeicher von diesem Inhalt vom Hauptabschnitt vom Prozess ausgeführt werden darf.

Wenn
  • Maschinencode in diesem Inhalt vom Hauptabschnitt vorhanden ist, welcher möglicherweise irgendwann während der Laufzeit ausgeführt werden soll,
dann
  • muss diese Freigabe erteilt werden.

Siehe hierzu auch das Bit mit der Wertigkeit "22|d" vom Feld "sh_flags" von der Kopfzeilen-Tabelle für Unterabschnitte.
Dieser Bereich vom Speicher darf vom Prozess
  • nicht ausgeführt
werden.
Dieser Bereich vom Speicher darf vom Prozess
  • ausgeführt
werden.
PF_X 20
Mit der Hilfe von diesem Bit wird angegeben, ob der Bereich vom Speicher von diesem Inhalt vom Hauptabschnitt vom Prozess beschrieben werden darf.

Nach meiner Auffassung kann hiermit nicht verhindert werden, dass das Lade-Programm von Linux diesen Inhalt vom Hauptabschnitt beschreibt, wenn es versucht, die Daten aus der Datei in den Arbeitsspeicher zu übertragen. Für nachträgliche Änderungen, welche das Lade-Programm durchführt, dies sind zum Beispiel die Adressen-Reparaturen, ist jedoch eine Schreibberechtigung erforderlich.
Dieser Bereich vom Speicher darf vom Prozess
  • nicht beschrieben
werden.
Dieser Bereich vom Speicher darf vom Prozess
  • beschrieben
werden.
PF_W 21
Gibt an, ob der Bereich vom Speicher von diesem Inhalt vom Hauptabschnitt vom Prozess gelesen werden darf. Dieser Bereich vom Speicher darf vom Prozess
  • nicht gelesen
werden.
Dieser Bereich vom Speicher darf vom Prozess
  • gelesen
werden.
PF_R 22

Nach meiner Kenntnis haben die restlichen Bits soweit noch keine Bedeutung zugewiesen bekommen und sollten daher auf "0|b" gesetzt werden.

Bei einer
  • ausführbaren Datei wird der Wert von diesem Feld ausgewertet.
  • Bibliothek scheint der Wert von diesem Feld nicht ausgewertet zu werden. Stattdessen wird für jeden Inhalt von einem Hauptabschnitt
    • sowohl die Berechtigung zum Ausführen,
    • als auch die Berechtigung zum Lesen
    erteilt. Ob die Berechtigung zum Beschreiben erteilt wird, habe ich nicht getestet.
p_flags 4 Byte 24 Byte
bis
27 Byte
Leider konnte ich nicht so richtig zufriedenstellend herausfinden, wofür die Angabe von diesem Feld dient.

Sie scheint nur bei solchen Inhalten von Hauptabschnitten ausgewertet zu werden, bei welchen in ihrer Kopfzeile im Feld "p_type" der Wert "1|d" angegeben wurde, welcher bedeutet, dass der Inhalt vom Hauptabschnitt in den Arbeitsspeicher geladen werden soll.

Mit der Hilfe von dieser Angabe wird die Größe für jede Seite vom Inhalt vom Hauptabschnitt in Byte an, wenn er in den Arbeitsspeicher geladen wurde.

Wenn dann
  • wird er in Teile aufteilt. Ein solcher Teil wird "Seite" genannt.

Diese einzelnen Seiten kommen direkt hintereinander.

Desto kleiner eine Seite ist, desto mehr Seiten benötigt das Lade-Programm um den selben Inhalt im Arbeitsspeicher zu speichern. Aber desto weniger Arbeitsspeicher wird verschwendet, da in der letzten Seite weniger Bytes ungenutzt sind.

Wenn dann
  • füllt dieser im Arbeitsspeicher bei 4.096 Byte pro Seite 2 Seiten. Der Inhalt benötigt dann 8.196 Byte im Arbeitsspeicher.

Dieser Wert muss sich aus 2x ergeben.

Wenn dann
  • kann laut der offiziellen Beschreibung vom Dateiformat für dieses Feld
    • entweder der Wert "0|d"
    • oder der Wert "1|d"
    angegeben werden.

Das Lade-Programm von Linux akzeptiert den Wert "1|d" jedoch nicht. Stattdessen muss mit der Hilfe vom Wert die folgende Prüfung erfüllt werden:
(
p_align & (Länge_in_Byte(RAM-Seite) - 1|d)
)==0|d

Da eine RAM-Seite in Linux bei der x86-Architektur "4 Kilobyte"-groß ist, lässt sich die Prüfung auf das Folgende vereinfachen:
(
p_align & 1111 1111 1111|b
)==0|d

Beim Operator "&" handelt es sich um eine logische und-Verknüpfung auf einer Bit-Basis.

Wenn
  • die Bedingung nicht erfüllt ist,
dann
  • wird die Meldung
    "ELF load command alignment not page-aligned"
    ausgegeben und
  • der Lade-Vorgang wird abgebrochen.
p_align 4 Byte 28 Byte
bis
31 Byte
Gesamtgröße: 32 Byte

Bezeichnung
Die "Kopfzeilen-Tabelle für Hauptabschnitte" wird auch "Programm-Kopfzeilen-Tabelle" (im Englischen: "program header table") genannt.

Position
Wo diese Tabelle in der Datei beginnt, ist im Feld "e_phoff" vom Kopfzeilen-Abschnitt angegeben.

Inhalte

Bezeichnung
Ein Inhalt von einem Hauptabschnitt wird auch "Segment" (im Englischen: "segment") genannt.

Ein Inhalt hat allerdings nichts mit dem zu tun, was man sonst bei der x86-Architektur als "Segment" kennt. Bei der x86-Architektur ist ein Segment ein Adressraum.

Position
Wo ein Inhalt von einem Hauptabschnitt

Unterabschnitte

Zweck

Ein Inhalt von einem Unterabschnitt dient dazu, um etwas von dem anzugeben, welches das Programm braucht. Beispielsweise
  • den Maschinencode oder
  • Zeichenketten.

Ihre Kopfzeilen machen Auskünfte, wie zum Beispiel über
Man ist jedoch nicht gezwungen, solche Kopfzeilen zu verwenden. Es kann vollständig auf solche Kopfzeilen und damit den Angaben, welche mit ihrer Hilfe gemacht werden, verzichtet werden.

Kopfzeilen-Tabelle

Zweck
Diese Tabelle dient dafür, um Angaben über die einzelnen Inhalte von Unterabschnitten zu machen, wie zum Beispiel
  • wo sie in der Datei beginnen, wenn sie noch auf der Festplatte/Diskette/CD/was auch immer sind,
  • wo sie beginnen sollen, wenn sie in den Arbeitsspeicher geladen wurden,
  • wie groß sie sind und
  • von welcher Art der Inhalt vom Unterabschnitt ist.

Die komplette Kopfzeilen-Tabelle für Unterabschnitte ist optional. Auf sie kann also vollständig verzichtet werden. Dies gilt allerdings nicht für die Inhalte von den Unterabschnitten. Die gewünschten Inhalte von den Unterabschnitten müssen, wenn auch ohne Kopfzeile, existieren.

Aufbau
In dieser Tabelle ist für jeden Inhalt von einem Unterabschnitt von der Datei 1|d Kopfzeile.

Jede Kopfzeile ist "40|d Byte"-groß. Zwischen den einzelnen Kopfzeilen dürfen keine unbenutzten Byte sein.

Die Anzahl der Kopfzeilen in dieser Tabelle wurde im Feld "e_shnum" vom Kopfzeilen-Abschnitt definiert. Hierdurch ist das Ende von dieser Tabelle bekannt.

Wenn
  • in dieser Tabelle Kopfzeilen aufgelistet sind,
dann
  • muss an der ersten Stelle ein Kopfzeile stehen, von welcher alle Bits auf "0|b" gesetzt sind.

    Dies betrifft also die ersten 40|d Byte von dieser Tabelle.

Die erste, unbenutzte Kopfzeile muss im Feld "e_shnum" vom Kopfzeilen-Abschnitt enthalten sein. Wenn
  • also beispielsweise
    • die eine unbenutzte Kopfzeile und
    • 2|d benutzte Kopfzeilen
    vorhanden sind,
dann
  • muss im Feld "e_shnum" der Wert "3|d" angegeben werden.

Die Kopfzeilen bestehen jeweils aus mehreren Einträgen, welche Auskünfte über den jeweiligen Inhalt vom Unterabschnitt machen. Der Aufbau von einer solchen Kopfzeile sieht wie folgt aus:
Beschreibung: Bezeichnung: Größe: Offset vom Anfang von der Kopfzeile:
Gibt den Offset in Byte an, vom Anfang von der Tabelle für Zeichenketten bis zu dem ersten Byte vom Namen.

In welchem Inhalt von einem Unterabschnitt die Tabelle für Zeichenketten enthalten ist, wird im Feld "e_shstrndx" vom Kopfzeilen-Abschnitt angegeben.

Im Folgenden ist die Bedeutung vom Wert von diesem Feld grafisch dargestellt:
  ╔══════════════════════════════════════╗
  ║         Kopfzeilen-Abschnitt         ║
  ╟──────────────────┬───────────────────╢
  ║    Bedeutung:    │       Feld:       ║
  ╠══════════════════╪═══════════════════╣
  ║                  │                   ║
  ╟──────────────────┼───────────────────╢
┌─╢     Position     │    e_shstrndx     ╟───────┐
 ╚══════════════════╧═══════════════════╝       
                                                
 ╔══════════════════════════════════════╗       
 ║Kopfzeilen-Tabelle für Unterabschnitte║       
 ╟──────────────────┬───────────────────╢       
 ║   Feld "...":    │  Feld "sh_name":  ║       
 ╠══════════════════╪═══════════════════╣       
 ║                  │                   ║       
 ╟──────────────────┼───────────────────╢       
└►╢                  │      Offset       ╟─────┐◄┘
  ╟──────────────────┼───────────────────╢     
  ║                  │                   ║     
  ╟──────────────────┼───────────────────╢     
  ║                  │                   ║     
  ╟──────────────────┼───────────────────╢     
  ║                  │      Offset       ╟──┐  
  ╚══════════════════╧═══════════════════╝    
                                              
  ╔══════════════════════════════════════╗    
  ║   Inhalte von den Unterabschnitten   ║    
  ╠══════════════════════════════════════╣    
  ║                                      ║    
  ╟──────────────────────────────────────╢    
  ║╔════════════════════════════════════╗║    
  ║║     Tabelle für Zeichenketten      ║║    
  ║╠════════════════════════════════════╣║  
  ║║                                    ║║├◄┘ 
  ║╟────────────────────────────────────╢║  ├◄┘
  ║║            ".text"                 ║║   
  ║╟────────────────────────────────────╢║   
  ║║            ".shstrtab"             ║║
  ║╟────────────────────────────────────╢║
  ║║                                    ║║
  ║╚════════════════════════════════════╝║
  ╟──────────────────────────────────────╢
  ║                                      ║
  ╟──────────────────────────────────────╢
  ║                                      ║
  ╚══════════════════════════════════════╝

Manche Namen haben jeweils eine besondere Bedeutung. Wenn
  • einer von diesen besonderen Namen gewählt wird,
dann
Folgende Namen haben jeweils eine besondere Bedeutung:
Anforderungen: Bedeutung: Name:
an die restliche Kopfzeile vom Inhalt vom Unterabschnitt: Dieser Inhalt vom Unterabschnitt enthält Daten, welche erst zur Laufzeit zum ersten Mal mit Werten definiert werden.

Da also zu dem Zeitpunkt, an welchem die Datei erzeugt wird, die Werte noch nicht gespeichert werden, wären beispielsweise alle Bits von den Bytes auf "0|b" gesetzt. Die Bytes sind daher erst garnicht in der Datei vorhanden, sondern das Lade-Programm setzt während dem Lade-Vorgang alle Bits vom Bereich im Arbeitsspeicher auf "0|b".

Das "bss" steht übrigens für "block started by symbol" und soll ausdrücken, dass der Bereich wegen
  • einem Datensatz,
  • einer Konstante oder
  • einer Variable
existiert.

In diesem Dateiformat ist der Bereich allerdings nicht auf
  • 1|d Datensatz,
  • 1|d Konstante oder
  • 1|d Variable
begrenzt, sondern dient allgemein für (mehrere) Daten.
.bss
an die restliche Kopfzeile vom Inhalt vom Unterabschnitt:
  • Im Feld "sh_flags" muss
    • das Bit mit der Wertigkeit "20|d" auf "1|b" gesetzt werden, was bedeutet, dass
      • im Inhalt vom Unterabschnitt Daten enthalten sind, welche möglicherweise irgendwann während der Laufzeit beschrieben/geändert werden.
    • das Bit mit der Wertigkeit "21|d" auf "1|b" gesetzt werden, was bedeutet, dass
  • Im Feld "sh_type" muss
    • der Wert "1|h" gespeichert werden.
Dieser Inhalt vom Unterabschnitt enthält Daten, welche bereits mit Werten definiert sind und möglicherweise irgendwann während der Laufzeit beschrieben/geändert werden. .data
.data1
an die restliche Kopfzeile vom Inhalt vom Unterabschnitt:
an die Kopfzeile vom Inhalt vom Hauptabschnitt:
Dieser Inhalt vom Unterabschnitt enthält eine Tabelle für Exporte und Importe. .dynamic
an die restliche Kopfzeile vom Inhalt vom Unterabschnitt:
  • Im Feld "sh_flags" muss
    • das Bit mit der Wertigkeit "21|d" auf "1|b" gesetzt werden, was bedeutet, dass
  • Im Feld "sh_info" muss
    • ein Wert eingetragen werden, welcher sich unter der Berücksichtigung vom Wert im Feld "sh_type" ergibt.
  • Im Feld "sh_link" muss
    • ein Wert eingetragen werden, welcher sich unter der Berücksichtigung vom Wert im Feld "sh_type" ergibt.
  • Im Feld "sh_type" muss
Dieser Inhalt vom Unterabschnitt enthält eine Tabelle für Dinge.

Im Gegensatz zum Namen ".symtab" wird durch den Namen ".dynsym" angegeben, dass die Dinge nur für Export- und Import-bezügliche Aufgaben verwendet werden dürfen.
.dynsym
an die restliche Kopfzeile vom Inhalt vom Unterabschnitt: Dieser Inhalt vom Unterabschnitt enthält eine Tabelle für Prüfsummen. .hash
an die restliche Kopfzeile vom Inhalt vom Unterabschnitt:
  • Im Feld "sh_type" muss
    • der Wert "1|h" gespeichert werden.

an die Kopfzeile vom Inhalt vom Hauptabschnitt:
an die Datei:
  • In einer ausführbaren Datei muss ein solcher Inhalt von einem Unterabschnitt existieren, wenn die Anwendung ein Ding importieren will.
  • In einer Bibliothek kann er existieren, er wird aber nicht ausgewertet, sondern das Lade-Programm richtet sich nach der Zeichenkette im Inhalt vom Unterabschnitt, welche in der ausführbaren Datei angegeben ist.
Dieser Inhalt vom Unterabschnitt enthält eine Zeichenkette, welche
  • den Pfad und
  • den Dateinamen
von einer bestimmten Bibliothek angibt.

Weitere Informationen zu
  • dieser Bibliothek und
  • dieser Zeichenkette
sind hier.
.interp
an die restliche Kopfzeile vom Inhalt vom Unterabschnitt: Dieser Inhalt vom Unterabschnitt enthält eine Tabelle für Adressen-Reparaturen.

Der Teil "$name" vom Namen muss durch den Name von dem Inhalt vom Unterabschnitt ersetzt werden, in welchem die Speicherzellen enthalten sind, in welchen die zu reparierenden Adressen gespeichert sind.

Wenn
  • also Beispielsweise die Speicherzellen, in welchen die zu reparierenden Adressen enthalten sind,
dann
.rel$name
an die restliche Kopfzeile vom Inhalt vom Unterabschnitt:
Verwundernderweise ist es laut der offiziellen Beschreibung vom Dateiformat kein Zwangs-Kriterium, dass im Feld "sh_flags"
  • das Bit mit der Wertigkeit "20|d" auf "0|b" gesetzt werden muss, was bedeuten würde, dass
    • im Inhalt vom Unterabschnitt entweder keine Daten enthalten sind, oder
    • die Daten während der Laufzeit nicht beschrieben/geändert werden.
Dieser Inhalt vom Unterabschnitt enthält Daten, welche bereits mit Werten definiert sind, welche aber zur Laufzeit nicht beschrieben/geändert werden.

Das "ro" im Namen steht übrigens für "read only" und soll ausdrücken, dass die Leseberechtigung die einzige Berechtigung ist, welche der Prozess für diesen Bereich erhält.
.rodata
.rodata1
an die restliche Kopfzeile vom Inhalt vom Unterabschnitt: Dieser Inhalt vom Unterabschnitt enthält eine Tabelle für Zeichenketten.

Im Gegensatz zum Namen ".strtab" wird durch den Namen ".shstrtab" angegeben, dass in der Tabelle für Zeichenketten die Namen von den Inhalten von den Unterabschnitten enthalten sind. Die Tabelle für Zeichenketten kann aber trotzdem auch für andere Zeichenketten verwendet werden.

Siehe hierzu auch das Feld "e_shstrndx" vom Kopfzeilen-Abschnitt.
.shstrtab
an die restliche Kopfzeile vom Inhalt vom Unterabschnitt: Dieser Inhalt vom Unterabschnitt enthält eine Tabelle für Zeichenketten.

Im Gegensatz zum Namen ".shstrtab" wird durch den Namen ".strtab" nicht angegeben, wofür die Zeichenketten dienen.
.strtab
an die restliche Kopfzeile vom Inhalt vom Unterabschnitt:
  • Im Feld "sh_info" muss
    • ein Wert eingetragen werden, welcher sich unter der Berücksichtigung vom Wert im Feld "sh_type" ergibt.
  • Im Feld "sh_link" muss
    • ein Wert eingetragen werden, welcher sich unter der Berücksichtigung vom Wert im Feld "sh_type" ergibt.
  • Im Feld "sh_type" muss
Dieser Inhalt vom Unterabschnitt enthält eine Tabelle für Dinge.

Im Gegensatz zum Namen ".dynsym" wird durch den Namen ".symtab" nicht angegeben, dass die Dinge nur für Export- und Import-bezügliche Aufgaben verwendet werden dürfen.
.symtab
an die restliche Kopfzeile vom Inhalt vom Unterabschnitt:
  • Im Feld "sh_flags" muss
    • das Bit mit der Wertigkeit "21|d" auf "1|b" gesetzt werden, was bedeutet, dass
    • das Bit mit der Wertigkeit "22|d" auf "1|b" gesetzt werden, was bedeutet, dass
      • in diesem Inhalt vom Unterabschnitt Maschinencode enthalten ist, welcher möglicherweise irgendwann während der Laufzeit ausgeführt wird.
  • Im Feld "sh_type" muss
    • der Wert "1|h" gespeichert werden.
Dieser Inhalt vom Unterabschnitt enthält Maschinencode, welcher möglicherweise irgendwann während der Laufzeit ausgeführt wird.

Siehe hierzu auch das Bit mit der Wertigkeit "22|d" vom Feld "sh_flags".
.text

Es gibt weitere Namen, welche jeweils eine besondere Bedeutung haben. Diese sind in dieser Dokumentation allerdings nicht beschrieben. Was allerdings alle besonderen Namen gemeinsam haben, ist, dass sie mit einem Punkt (".") beginnen.

Ich habe bisher noch keine Datei gesehen, welche für mehrere Inhalte von Unterabschnitten den selben Namen benutzt. Also zum Beispiel 2 Inhalte von Unterabschnitten, welche beide ".text" genannt sind. Bei manchen Namen ist dies allerdings durchaus zulässig.
sh_name 4 Byte 0 Byte
bis
3 Byte
Dieses Feld gibt ebenfalls an, was in diesem Inhalt vom Unterabschnitt enthalten ist.

Bedeutung: Bezeichnung: Hexadezimalwert:
Der Inhalt vom Unterabschnitt existiert nicht.

Wegen diese Kopfzeile soll auch kein Inhalt vom Unterabschnitt im Arbeitsspeicher angelegt werden. Außerdem soll der Rest von dieser Kopfzeile ignoriert werden.
SHT_NULL 0
Laut der offiziellen Beschreibung vom Dateiformat wäre die Bedeutung wie folgt:
Was in diesem Inhalt vom Unterabschnitt enthalten ist, und in welchem Format es gespeichert ist, das muss nur vom Programm verstanden werden.

Dies ist aber nicht immer der Fall, da dieser Wert auch für manche Inhalte von Unterabschnitten verwendet werden muss, welche vom Lade-Programm ausgewertet werden.
SHT_PROGBITS 1
In diesem Inhalt vom Unterabschnitt ist eine Tabelle für Dinge enthalten.

Im Gegensatz zum Wert "B|h", wird durch den Wert "2|h" nicht eingeschränkt, wofür die Dinge verwendet werden dürfen.
SHT_SYMTAB 2
In diesem Inhalt vom Unterabschnitt ist eine Tabelle für Zeichenketten enthalten. SHT_STRTAB 3
In diesem Inhalt vom Unterabschnitt ist eine Tabelle für Prüfsummen enthalten. SHT_HASH 5
In diesem Inhalt vom Unterabschnitt ist eine Tabelle für Exporte und Importe enthalten. SHT_DYNAMIC 6
Dieser Inhalt vom Unterabschnitt existiert nicht in der Datei. Er kann aber im Arbeitsspeicher existieren.

Ein Verwendungszweck hierfür wäre, wenn ein Inhalt vom Unterabschnitt für Daten vorgesehen ist, welche erst zur Laufzeit zum ersten Mal mit Werten definiert werden und daher die Bytes, mit bisher noch undefiniertem Inhalt, nicht in der Datei benötigt werden.

Das Lade-Programm setzt für einen solchen Inhalt vom Unterabschnitt während dem Lade-Vorgang automatisch alle Bits auf "0|b".
SHT_NOBITS 8
In diesem Inhalt vom Unterabschnitt ist eine Tabelle für Adressen-Reparaturen enthalten. SHT_REL 9
In diesem Inhalt vom Unterabschnitt ist eine Tabelle für Dinge enthalten.

Im Gegensatz zum Wert "2|h", wird durch den Wert "B|h" eingeschränkt, wofür die Dinge verwendet werden dürfen:
Die Dinge dürfen lediglich für Export- und Import-bezügliche Aufgaben verwendet werden.
SHT_DYNSYM B
Was in diesem Inhalt vom Unterabschnitt enthalten ist, und in welchem Format es gespeichert ist, das ist mit der Hilfe von diesem Dateiformat nicht definiert. Es hat aber vom Programmierer eine bestimmte Definition bekommen, welche möglicherweise von einer anderen Anwendung ausgewertet wird. SHT_LOUSER
bis
SHT_HIUSER
80 00 00 00|h
bis
FF FF FF FF|h

Es gibt weitere gültige Werte, welche in dieser Dokumentation nicht beschrieben sind.
sh_type 4 Byte 4 Byte
bis
7 Byte
Dieses Feld gibt ebenfalls an, was in diesem Inhalt vom Unterabschnitt enthalten ist. Die Angaben beziehen sich allerdings zusätzlich auf das Verhalten zur Laufzeit.

Ein weiterer Unterschied zum vorherigen Feld besteht darin, dass in diesem Feld mehrere Angaben gespeichert werden können.

Die Bedeutungen von den einzelnen Bits sind wie folgt:
allgemeine Beschreibung: Bedeutung wenn Wert=="0": Bedeutung wenn Wert=="1": Bezeichnung: Wertigkeit:
Gibt an, ob Daten in diesem Inhalt vom Unterabschnitt enthalten sind, welche möglicherweise irgendwann während der Laufzeit beschrieben/geändert werden.

Siehe hierzu auch das Bit mit der Wertigkeit "21|d" vom Feld "p_flags" von der "Kopfzeilen-Tabelle für Hauptabschnitte". Dieses Bit gibt an, ob der Inhalt vom Hauptabschnitt vom Prozess beschrieben/geändert werden darf.
In diesem Inhalt vom Unterabschnitt
  • sind keine Daten enthalten, oder
  • die Daten werden während der Laufzeit nicht beschrieben/geändert.
In diesem Inhalt vom Unterabschnitt
  • sind Daten enthalten, und
  • die Daten werden möglicherweise irgendwann während der Laufzeit beschrieben/geändert.
SHF_WRITE 20|d
Gibt an, ob dieser Inhalt vom Unterabschnitt in den Arbeitsspeicher geladen wird.

Dieses Bit macht keine Auskunft darüber, ob dieser Inhalt vom Unterabschnitt in den Arbeitsspeicher geladen werden soll. Ich sehe die Information von diesem Bit daher als unzuverlässig an, da diese Angabe von der Anweisung, was das Lade-Programm machen soll, abweichen kann.

Siehe hierzu auch den Wert "1|h" (alias "PT_LOAD") vom Feld "p_type" von der "Kopfzeilen-Tabelle für Hauptabschnitte". Dieser Wert gibt an, ob der Inhalt vom Hauptabschnitt in den Arbeitsspeicher geladen werden soll.
Dieser Inhalt vom Unterabschnitt
  • wird nicht in den Arbeitsspeicher geladen.
Dieser Inhalt vom Unterabschnitt
  • wird in den Arbeitsspeicher geladen.
SHF_ALLOC 21
Gibt an, ob in diesem Inhalt vom Unterabschnitt Maschinencode enthalten ist, welcher möglicherweise irgendwann während der Laufzeit ausgeführt wird.

Siehe hierzu auch das Bit mit der Wertigkeit "20|d" vom Feld "p_flags" von der Kopfzeilen-Tabelle für Hauptabschnitte, welches angibt, ob der Inhalt vom Hauptabschnitt vom Prozess ausgeführt werden darf.
In diesem Inhalt vom Unterabschnitt
  • ist kein Maschinencode enthalten, oder
  • der Maschinencode wird während der Laufzeit nicht ausgeführt.
In diesem Inhalt vom Unterabschnitt
  • ist Maschinencode enthalten, und
  • der Maschinencode wird möglicherweise irgendwann während der Laufzeit ausgeführt.
SHF_EXECINSTR 22
Gibt an, ob in diesem Inhalt vom Unterabschnitt Zeichenketten enthalten sind,
  • von welchen alle Zeichen gleich groß sind und
  • welche alle jeweils durch so viele weitere Bytes abgeschlossen sind, von welchen alle Bits auf "0|b" gesetzt sind, wie 1|d Zeichen lang ist.

Die Länge von jedem Zeichen würde dann durch das Feld "sh_entsize" von dieser Kopfzeile angegeben werden.
In diesem Inhalt vom Unterabschnitt
  • sind keine Zeichenketten enthalten,
  • die einzelnen Zeichen von den Zeichenketten sind nicht alle gleich groß, oder
  • die Zeichenketten sind nicht alle durch weitere Bytes abgeschlossen, von welchen alle Bits auf "0|b" gesetzt sind.
In diesem Inhalt vom Unterabschnitt
  • sind Zeichenketten enthalten,
  • die Zeichen von den Zeichenketten sind alle gleich groß, und
  • die Zeichenketten sind alle durch weitere Bytes abgeschlossen, von welchen alle Bits auf "0|b" gesetzt sind.
SHF_STRINGS 25

Von den restlichen Bits haben
  • manche Bits bereits eine Bedeutung bekommen und
  • manche Bits noch keine Bedeutung bekommen.
Tendenziell ist es besser, die Werte von den unbekannten Bits auf "0|b" zu setzen, als sie auf "1|b" zu setzen.
sh_flags 4 Byte 8 Byte
bis
11 Byte
Gibt die Anfangsadresse vom Inhalt vom Unterabschnitt an, welche er haben soll, wenn er in den Arbeitsspeicher geladen wurde.

Die Anfangsadresse muss unter der Berücksichtigung von der vom Erzeuger gewünschten Basisadresse angegeben werden. Es handelt sich bei der x86-Architektur um einen Offset vom Segmentanfang.

Im Folgenden ist die Bedeutung vom Wert von diesem Feld grafisch dargestellt:
╔════════════════════════════════════════╗  ╔══════════════════════════════════╗
                 Datei                  ║  ║             Segment              
╠════════════════════════════════════════╣  ╠══════════════════════════════════╣
║╔══════════════════════════════════════╗║  ║                                  ║
║║Kopfzeilen-Tabelle für Unterabschnitte║║  ╟──────────────────────────────────╢
║╟──────────────────┬───────────────────╢║  ║                                  ║
║║   Feld "...":    │  Feld "sh_addr":  ║║  ╟──────────────────────────────────╢
║╠══════════════════╪═══════════════════╣║  ║╔════════════════════════════════╗║
║║                  │                   ║║  ║║Inhalte von den Unterabschnitten
║╟──────────────────┼───────────────────╢║  ║╠════════════════════════════════╣║
║║                  │  Anfangsadresse   ╟╫┐ ║║                                ║║
║╟──────────────────┼───────────────────╢║└►╫╫────────────────────────────────╢║
║║                  │                   ║║  ║║╔══════════════════════════════╗║║
║╟──────────────────┼───────────────────╢║  ║║║  Tabelle für Zeichenketten   ║║║
║║                  │                   ║║  ║║╠══════════════════════════════╣║║
║╟──────────────────┼───────────────────╢║  ║║║                              ║║║
║║                  │  Anfangsadresse   ╟╫┐ ║║╟──────────────────────────────╢║║
║╚══════════════════╧═══════════════════╝║ ║║║                              ║║║
╚════════════════════════════════════════╝ ║║╟──────────────────────────────╢║║
                                           ║║║                              ║║║
                                           ║║╟──────────────────────────────╢║║
                                           ║║║                              ║║║
                                           ║║╚══════════════════════════════╝║║
                                           ║╟────────────────────────────────╢║
                                           ║║                                ║║
                                           ║╟────────────────────────────────╢║
                                           ║║                                ║║
                                          └►╫╫────────────────────────────────╢║
                                            ║║         Maschinencode          ║║
                                            ║╚════════════════════════════════╝║
                                            ╟──────────────────────────────────╢
                                            ║                                  ║
                                            ╚══════════════════════════════════╝

Wenn dann
  • wird dies durch den Wert "0|d" angegeben.

Der Wert von diesem Feld muss restlos durch den Wert vom Feld "sh_addralign" teilbar sein. Wenn
  • also beispielsweise für das Feld "sh_addralign" der Wert "8|d" angegeben wird,
dann
  • kann für den Wert vom Feld "sh_addr" der Wert
    • "0|d",
    • "8|d",
    • "16|d",
    • "24|d" oder
    • ...
    angegeben werden.
sh_addr 4 Byte 12 Byte
bis
15 Byte
Gibt den Offset in Byte an, vom Anfang von der Datei bis zu dem ersten Byte vom Inhalt vom Unterabschnitt, wenn er noch in der Datei auf der Festplatte/Diskette/CD/was auch immer ist.

Im Folgenden ist die Bedeutung vom Wert von diesem Feld grafisch dargestellt:
╔═══════════════════════════════════════════╗
                   Datei                   
╠═══════════════════════════════════════════╣
║   ╔══════════════════════════════════════╗║
║   ║Kopfzeilen-Tabelle für Unterabschnitte║║
║   ╟──────────────────┬───────────────────╢║
║   ║Feld "sh_offset": │    Feld "...":    ║║
║   ╠══════════════════╪═══════════════════╣║
║   ║                  │                   ║║
║   ╟──────────────────┼───────────────────╢║
┌──╢  Anfangsadresse  │                   ║║
  ╟──────────────────┼───────────────────╢║
  ║                  │                   ║║
  ╟──────────────────┼───────────────────╢║
  ║                  │                   ║║
  ╟──────────────────┼───────────────────╢║
┌─╢  Anfangsadresse  │                   ║║
 ╚══════════════════╧═══════════════════╝║
                                         ║
 ╔══════════════════════════════════════╗║
 ║   Inhalte von den Unterabschnitten   ║║
 ╠══════════════════════════════════════╣║
 ║                                      ║║
└►╫──────────────────────────────────────╢
  ║            Maschinencode             ║║
└─►╫──────────────────────────────────────╢
║   ║╔════════════════════════════════════╗║║
║   ║║     Tabelle für Zeichenketten      ║║║
║   ║╠════════════════════════════════════╣║║
║   ║║                                    ║║║
║   ║╟────────────────────────────────────╢║║
║   ║║                                    ║║║
║   ║╟────────────────────────────────────╢║║
║   ║║                                    ║║║
║   ║╟────────────────────────────────────╢║║
║   ║║                                    ║║║
║   ║╚════════════════════════════════════╝║║
║   ╟──────────────────────────────────────╢║
║   ║                                      ║║
║   ╟──────────────────────────────────────╢║
║   ║                                      ║║
║   ╚══════════════════════════════════════╝║
╚═══════════════════════════════════════════╝

Der Wert von diesem Feld muss restlos durch den Wert vom Feld "sh_addralign" teilbar sein. Wenn
  • also beispielsweise für das Feld "sh_addralign" der Wert "8|d" angegeben wird,
dann
  • kann für den Wert vom Feld "sh_offset" der Wert
    • "0|d",
    • "8|d",
    • "16|d",
    • "24|d" oder
    • ...
    angegeben werden.
sh_offset 4 Byte 16 Byte
bis
19 Byte
Gibt die Größe vom Inhalt vom Unterabschnitt in Byte an, wenn er noch in der Datei auf der Festplatte/Diskette/CD/was auch immer ist.

Wenn
  • beispielsweise der komplette Inhalt vom Unterabschnitt für Daten vorgesehen ist, welche erst zur Laufzeit zum ersten Mal mit Werten definiert werden,
dann
  • kann diese Größenangabe kann den Wert "0|d" haben.
sh_size 4 Byte 20 Byte
bis
23 Byte
Was durch dieses Feld angegeben wird, ist abhängig davon, welcher Wert im Feld "sh_type" angegeben wurde.

dieses Feld: Feld "sh_type":
Bedeutung: Bezeichnung: Hexadezimalwert:
Gibt die Position von einer bestimmten Kopfzeile in der Kopfzeilen-Tabelle für Unterabschnitte an. Diese Kopfzeile macht Auskünfte über den Inhalt vom Unterabschnitt, in welchem eine bestimmte Tabelle für Zeichenketten enthalten ist. Diese Tabelle für Zeichenketten enthält die Namen von den Dingen, welche in der Tabelle für Dinge enthalten sind.

Die Position wird von "0|d" ausgehend gezählt.
  • Der "erste" Eintrag hat also die Position "0|d",
  • der "zweite" Eintrag hat die Position "1|d",
  • und so weiter.
SHT_SYMTAB 2
Gibt die Position von einer bestimmten Kopfzeile in der Kopfzeilen-Tabelle für Unterabschnitte an. Diese Kopfzeile macht Auskünfte über den Inhalt vom Unterabschnitt, in welchem eine bestimmte Tabelle für Dinge enthalten ist. Diese Tabelle für Dinge enthält die Dinge, auf welche sich bestimmte Prüfsummen beziehen. Diese Prüfsummen sind in der Tabelle für Prüfsummen enthalten.

Die Position wird von "0|d" ausgehend gezählt.
  • Der "erste" Eintrag hat also die Position "0|d",
  • der "zweite" Eintrag hat die Position "1|d",
  • und so weiter.
SHT_HASH 5
Gibt die Position von einer bestimmten Kopfzeile in der Kopfzeilen-Tabelle für Unterabschnitte an. Diese Kopfzeile macht Auskünfte über den Inhalt vom Unterabschnitt, in welchem eine bestimmte Tabelle für Zeichenketten enthalten ist. Diese Tabelle für Zeichenketten enthält die Namen von bestimmten Dingen:
Die Position wird von "0|d" ausgehend gezählt.
  • Der "erste" Eintrag hat also die Position "0|d",
  • der "zweite" Eintrag hat die Position "1|d",
  • und so weiter.
SHT_DYNAMIC 6
Gibt die Position von einer bestimmten Kopfzeile in der Kopfzeilen-Tabelle für Unterabschnitte an. Diese Kopfzeile macht Auskünfte über den Inhalt vom Unterabschnitt, in welchem eine bestimmte Tabelle für Dinge enthalten ist. Diese Tabelle für Dinge enthält die Dinge, auf welche sich die Adressen-Reparaturen beziehen, welche in der Tabelle für Adressen-Reparaturen enthalten sind.

Die Position wird von "0|d" ausgehend gezählt.
  • Der "erste" Eintrag hat also die Position "0|d",
  • der "zweite" Eintrag hat die Position "1|d",
  • und so weiter.
SHT_REL 9
Siehe in der Zeile, welche den Wert "2|h" beschreibt. SHT_DYNSYM B

Wenn
  • im Feld "sh_type" ein anderer Wert angegeben wurde, als in der obrigen Tabelle aufgelistet ist,
dann gilt:
  • Wenn
    • ein Wert angegeben wurde, welcher in dieser Dokumentation als gültiger Wert für das Feld "sh_type" genannt ist,
    dann
    • hat der Inhalt vom Feld "sh_link" soweit noch keine Bedeutung bekommen. Daher können in diesem Fall alle Bits vom Wert vom Feld "sh_link" auf "0|b" gesetzt werden.
    Ansonsten
    • kann der Inhalt vom Feld "sh_link" eine Bedeutung haben, welche in dieser Dokumentation nicht beschrieben ist.
sh_link 4 Byte 24 Byte
bis
27 Byte
Was durch dieses Feld angegeben wird, ist abhängig davon, welcher Wert im Feld "sh_type" angegeben wurde.

dieses Feld: Feld "sh_type":
Bedeutung: Bezeichnung: Hexadezimalwert:
Gibt die Position vom ersten Ding in der Tabelle für Dinge an, welches einen anderen Wert als den Wert "0|h" für die Angabe von der Datei im Feld "st_info" gespeichert hat.

Die Position wird von "0|d" ausgehend gezählt.
  • Der "erste" Eintrag hat also die Position "0|d",
  • der "zweite" Eintrag hat die Position "1|d",
  • und so weiter.
SHT_SYMTAB 2
Gibt den Wert "0|h" an.

Dieser Wert hat keine besondere Bedeutung.
SHT_HASH 5
Gibt den Wert "0|h" an.

Dieser Wert hat keine besondere Bedeutung.
SHT_DYNAMIC 6
Gibt die Position von einer bestimmten Kopfzeile in der Kopfzeilen-Tabelle für Unterabschnitte an. Diese Kopfzeile macht Auskünfte über den Inhalt vom Unterabschnitt, in welchem bestimmte Speicherzellen enthalten sind. In diesen Speicherzellen sind die zu reparierenden Adressen gespeichert.

Die Position wird von "0|d" ausgehend gezählt.
  • Der "erste" Eintrag hat also die Position "0|d",
  • der "zweite" Eintrag hat die Position "1|d",
  • und so weiter.
SHT_REL 9
Siehe in der Zeile, welche den Wert "2|h" beschreibt. SHT_DYNSYM B

Wenn
  • im Feld "sh_type" ein anderer Wert angegeben wurde, als in der obrigen Tabelle aufgelistet ist,
dann gilt:
  • Wenn
    • ein Wert angegeben wurde, welcher in dieser Dokumentation als gültiger Wert für das Feld "sh_type" genannt ist,
    dann
    • hat der Inhalt vom Feld "sh_info" soweit noch keine Bedeutung bekommen. Daher können in diesem Fall alle Bits vom Wert vom Feld "sh_info" auf "0|b" gesetzt werden.
    Ansonsten
    • kann der Inhalt vom Feld "sh_info" eine Bedeutung haben, welche in dieser Dokumentation nicht beschrieben ist.
sh_info 4 Byte 28 Byte
bis
31 Byte
Eine konstante Größe für jede Seite vom Inhalt vom Unterabschnitt in Byte, wenn er in den Arbeitsspeicher geladen wurde.

Wenn dann
  • wird er in Teile aufteilt. Ein solcher Teil wird "Seite" genannt.

Diese einzelnen Seiten kommen direkt hintereinander

Desto kleiner eine Seite ist, desto mehr Seiten benötigt das Lade-Programm um den selben Inhalt im Arbeitsspeicher zu speichern. Aber desto weniger Arbeitsspeicher wird verschwendet, da in der letzten Seite weniger Byte ungenutzt sind.

Nach meinem bisherigen Verständnis scheint das Lade-Programm von Linux dieses Hilfskonstrukt lediglich dafür zu benutzen, um nach dem letzten benutzten Byte in der letzten Seite unbenutzte Bytes einzufügen, damit die Anfangsadresse vom nächsten Inhalt restlos durch eine bestimmte Zahl teilbar ist.

Dieser Wert muss sich aus 2x ergeben.

Wenn dann
  • füllt dieser im Arbeitsspeicher bei 4.096 Byte pro Seite 2 Seiten. Der Inhalt benötigt dann 8.196 Byte im Arbeitsspeicher.

Wenn dann
  • kann für dieses Feld
    • entweder der Wert "0|h"
    • oder der Wert "1|h"
    angegeben werden.
sh_addralign 4 Byte 32 Byte
bis
35 Byte
Wenn
  • dieser Inhalt vom Unterabschnitt dazu verwendet wird, um eine Tabelle zu speichern, von welcher die einzelnen Einträge alle gleich groß sind,
dann
  • wird durch den Wert von diesem Feld die Größe von einem Eintrag in Byte angegeben.

    Hier ist nicht die Gesamtgröße von der im Inhalt vom Unterabschnitt enthaltenen Tabelle gemeint, sondern wie groß jeder einzelne Eintrag ist.

Wenn dann
  • wird durch den Wert von diesem Feld die Größe von einem Zeichen in Byte angegeben.

Wenn
  • in diesem Inhalt vom Unterabschnitt keine Tabelle enthalten ist, oder die Einträge von der Tabelle unterschiedliche Größen haben können,
dann
  • wird dies durch den Wert "0|h" angegeben.
sh_entsize 4 Byte 36 Byte
bis
39 Byte
Gesamtgröße: 40 Byte

Bezeichnung
Die "Kopfzeilen-Tabelle für Unterabschnitte" wird auch "Abschnitte-Kopfzeilen-Tabelle" (im Englischen: "section header table") genannt.

Position
Wo diese Tabelle in der Datei beginnt, ist im Feld "e_shoff" vom Kopfzeilen-Abschnitt angegeben.

Inhalte

Bezeichnung
Ein Inhalt von einem Unterabschnitten wird auch "Sektion" (im Englischen: "section") genannt.

Position
Wo ein Inhalt von einem Unterabschnitt

Tabellen
Tabelle für Adressen-Reparaturen
Zweck
In diesem Dateiformat wird zwischen
  • der vom Erzeuger gewünschten Basisadresse und
  • der vom Lade-Programm gewählten Basisadresse
unterschieden. Der Unterschied besteht darin,
  • wo die Inhalte von der Bibliothek im "4 Gigabyte"-großen Segment platziert werden sollen und
  • wo die Inhalte von der Bibliothek im "4 Gigabyte"-großen Segment platziert werden.

Wenn
  • es nicht möglich ist, die Inhalte dort zu platzieren, wo sich der Erzeuger die Platzierung gewünscht hat,
dann
  • werden sie an eine andere Stelle platziert.

Dies hat die folgenden Probleme zur Folge, welche mit der Hilfe von der Tabelle für Adressen-Reparaturen gelöst werden können:
  • Wenn
    • im Maschinencode absolute Adressangaben verwendet wurden, welche nur gültig sind, wenn die Inhalte von der Datei an die gewünschte Stelle im Segment geladen wurde,
    dann
    • muss das Lade-Programm diese Adressangaben abändern.

    Relative Adressangaben sind hiervon nicht betroffen, da alle Inhalte von einer Datei als ein zusammenhängender Block angesehen werden. Der Block wird nicht zerstückelt. Durch eine Zerstückelung könnten relative Adressangaben im Maschinencode ungültig werden.

    Damit das Lade-Programm diese Adressangaben abändern kann, muss es wissen, wo diese Adressangaben sind. Um dies herauszufinden gibt es unter anderem diese Möglichkeiten:
    • Das Lade-Programm durchläuft den Maschinencode und sucht nach Befehlen mit einer absoluten Adressangabe als Parameter.

      Diese Methode ist langsam und erfordert vom Lade-Programm, dass es jeden Maschinenbefehl kennt, um zu wissen, wieviele Parameter folgen, um damit entscheiden zu können, welches Byte als nächstes wieder ein Maschinenbefehl ist und welche Byte Parameter sind. Außerdem muss das Lade-Programm von den Maschinenbefehlen wissen, ob einer von den Parametern eine absolute Adressangabe ist.

      Problematisch bei dieser Methode ist außerdem die Aufwärtskompatibilität. Also selbst wenn
      • die aktuelle Version vom Lade-Programm alle momentan existierenden Maschinenbefehle kennt,
      dann
      • kann das Lade-Programm keine neuen Maschinenbefehle kennen, welche erst in der Zukunft in einem neuen Prozessor das erste Mal auf den Markt kommen.

        Die Computerbenutzer wären also bei neuen Prozessoren völlig von den Entwicklern vom Lade-Programm abhängig und müssten sich gegebenfalls eine neuere Version vom Lade-Programm installieren, damit neue Programme die neuen Befehle benutzen zu können.

      Was allerdings vorteilhaft an dieser Methode ist, ist, dass die Entwickler von Compilern sich nicht um die Korrektur von absoluten Adressangaben kümmern müssen.
    • Eine andere Methode, und diese ist es, welche in diesem Dateiformat gewählt wurde, besteht darin, dass der Compiler angibt, wo absolute Adressangaben sind, welche geändert werden müssen.

      Ein Compiler wird von denjenigen entwickelt, welche die einzelnen Maschinenbefehle kennen. Diese Entwickler wissen auch, wo im Code absolute Adressangaben vorkommen. Ein Entwickler von einem Compiler muss nicht alle existierenden Maschinenbefehle von der CPU-Architektur kennen, aber er kennt wahrscheinlich die Maschinenbefehle, welche sein Compiler verwendet, um Quellcode in der Programmiersprache, für welche der Compiler geschrieben ist, in Maschinencode zu übersetzen.

      Um diese Positionsangaben zu speichern, wurde die Tabelle für Adressen-Reparaturen ins Dateiformat aufgenommen.

    Wenn
    • der Maschinencode keine absoluten Adressangaben enthält, sondern alle Adressen relativ angegeben sind, oder
    • im Voraus klar ist, dass das Lade-Programm die Inhalte an die gewünschte Stelle im Segment laden kann,
    dann
    • kann es möglich sein, dass diese Tabelle weggelassen werden kann.

    Die Inhalte von der ausführbaren Datei sind eine der ersten Inhalte, welche ins Segment geladen werden. Daher lässt sich bei diesen Inhalten noch relativ einfach sagen, ob diese vom Lade-Programm an die gewünschte Stelle platziert werden können.

    Die Inhalte von einer Bibliothek hingegen werden erst im späteren Lade-Vorgang ins Segment geladen. Zu diesem Zeitpunkt können sich bereits die Inhalte von anderen Bibliotheken im Segment befinden. Außerdem können sie von jeder beliebig großen Anwendung angefordert werden. Die Stelle, welche die Bibliothek als gewünschte Stelle angibt, ist daher mit einer größeren Wahrscheinlichkeit bereits, wenn vielleicht auch nur teilweise, belegt.
  • Wenn
    • ein Ding aus einer Bibliothek importiert werden soll,
    dann
    • ist es nur schwer vorhersehbar, an welche Stelle das Ding im Segment platziert wird.

    Im Dateiformat ist ein Import so geregelt, dass die Adresse erst bei einer ausdrücklichen Aufforderung durch einen entsprechenden Eintrag in der Tabelle für Adressen-Reparaturen bereitgestellt wird.

    Mit der Hilfe von dieser Tabelle kann
    • der Anwendung, oder
    • der Bibliothek, welche selbst ein Ding aus einer anderen Bibliothek angefordert hat,
    die Adresse bereitgestellt werden, indem
    • eine bestehende Adresse repariert oder
    • eine leere Speicherzelle mit der zur Laufzeit gültigen Adresse gefüllt
    wird.

    Durch ein Importieren wird also mehr oder minder zwangsweise eine Tabelle für Adressen-Reparaturen notwendig. Nur weil ein Ding exportiert wird, wird allerdings noch keine solche Tabelle notwendig.

Aufbau
Für jede Speicherzelle, in welcher eine zu reparierende Adresse gespeichert ist, ist ein Eintrag in einer Tabelle für Adressen-Reparaturen vorhanden.

Jeder Eintrag besteht aus 2 Feldern. Das 2. Feld ist allerdings nochmals unterteilt.

In der Tabelle für Exporte und Importe gibt es einen Eintrag, welcher die Größe von der Tabelle für Adressen-Reparaturen angibt. Somit ist das Ende von dieser Tabelle bekannt.

Der Aufbau von einem Eintrag sieht wie folgt aus:
Beschreibung: Bezeichnung: Größe: Offset vom Anfang vom Eintrag:
Gibt die Anfangsadresse von der Speicherzelle an, in welcher die zu reparierende Adresse gespeichert ist.

Diese Anfangsadresse muss unter der Berücksichtigung von der vom Erzeuger gewünschten Basisadresse angegeben werden. Es handelt sich bei der x86-Architektur um einen Offset vom Segmentanfang.

In welchem Inhalt vom Unterabschnitt die Speicherzellen sind,
  • in welchen die zu reparierenden Adressen gespeichet sind, und
  • auf welche sich diese Tabelle für Adressen-Reparaturen bezieht,
wird im Feld "sh_info" von der Kopfzeile in der Kopfzeilen-Tabelle für Unterabschnitte angegeben, welche Auskünfte über den Inhalt vom Unterabschnitt macht, in welcher diese Tabelle für Adressen-Reparaturen enthalten ist.

Im Folgenden ist die Bedeutung vom Wert von diesem Feld grafisch dargestellt:
   ╔══════════════════════════════════════╗
   ║Kopfzeilen-Tabelle für Unterabschnitte
   ╟──────────────────┬───────────────────
   ║Feld "sh_addr" und│  Feld "sh_info":  
   ║Feld "sh_offset": │                   
   ╠══════════════════╪═══════════════════╣
   ║                  │                   ║
   ╟──────────────────┼───────────────────╢
┌──╢  Anfangsadresse  │                   ╟◄┐
  ╟──────────────────┼───────────────────╢ 
┌─╢  Anfangsadresse       Position      ╟─┘
 ╟──────────────────┼───────────────────╢
 ║                  │                   ║
 ╚══════════════════╧═══════════════════╝

 ╔══════════════════════════════════════╗
 ║   Inhalte von den Unterabschnitten   
 ╠══════════════════════════════════════╣
 ║                                      ║
└►╫──────────────────────────────────────╢
  ║╔════════════════════════════════════╗║
  ║║  Tabelle für Adressen-Reparaturen  ║║
  ║╟─────────────────┬──────────────────╢║
  ║║   Feld "...":   │ Feld "r_offset": ║║
  ║╠═════════════════╪══════════════════╣║
  ║║                 │  Anfangsadresse  ╟╫──┐
  ║╟─────────────────┼──────────────────╢║  
  ║║                 │                  ║║  
  ║╟─────────────────┼──────────────────╢║  
  ║║                 │  Anfangsadresse  ╟╫─┐
  ║╚═════════════════╧══════════════════╝║ 
└─►╫──────────────────────────────────────╢ 
   ║╔════════════════════════════════════╗║ 
   ║║           Maschinencode            ║║ 
   ║╠════════════════════════════════════╣║ 
   ║║ move extended accumulator (eaxto ║║ 
   ║╟────────────────────────────────────╫╫◄┘
   ║║ Adresse #1                         ║║  
   ║╟────────────────────────────────────╢║  
   ║║ increment eax by 1                 ║║  
   ║╟────────────────────────────────────╢║  
   ║║ jump to                            ║║  
   ║╟────────────────────────────────────╫╫◄─┘
   ║║ Adresse #2                         ║║
   ║╚════════════════════════════════════╝║
   ╟──────────────────────────────────────╢
   ║                                      ║
   ╚══════════════════════════════════════╝
r_offset 4 Byte 0 Byte
bis
3 Byte
Wenn dann gilt das Folgende:
Wenn
  • im Feld "e_machine" vom Kopfzeilen-Abschnitt der Wert "3|h" angegeben wurde, was bedeutet, dass die x86er-Architektur in einer "32 Bit"-Version benutzt wird, also die Architektur vom Prozessor "80386" (alias "Intel386"), oder eine spätere, darauf basierende Architektur,
dann gilt das Folgende:
Dieses Feld gibt die Berechnungsmethode an, welche verwendet werden soll, um den neuen Wert zu berechnen, welcher in die Speicherzelle gespeichert werden soll, in welcher bisher noch die zu reparierende Adresse gespeichert ist.

Berechnung: Besonderheiten: Bezeichnung: eignet sich, wenn mit der Hilfe von ...: Hexadezimalwert:
  Dingadressegewählt
Speicherzelleninhaltalt
Speicherzelleninhaltneu
Mit der Hilfe von der x86-Architektur können keine Sprünge zu einer absoluten Adresse durchgeführt werden, wenn sich die absolute Adresse lediglich im Maschinencode als Wert von einem Parameter von einem Sprungbefehl befindet.

Was allerdings möglich ist, ist
  • die absolute Adresse zunächst in ein Register zu kopiert oder
  • die absolute Adresse in einer Speicherzelle im Arbeitsspeicher zu speichern
und dann die absolute Adresse
  • aus dem Register oder
  • aus der Speicherzelle im Arbeitsspeicher
ins Befehlszeigerregister zu kopieren, wodurch der Sprung durchgeführt wird.

Im Gegensatz hierzu ist ein Sprung zu einer relativen Adresse mit der Hilfe von der x86-Architektur auch dann möglich, wenn sich die relative Adresse lediglich im Maschinencode als Wert von einem Parameter von einem Sprungbefehl befindet.

Wenn dann
R_386_32 einer absoluten Adresse auf Daten zugegriffen werden soll. 1
(
      Dingadressegewählt
    - Speicherzellenadressegewählt
)
+ Speicherzelleninhaltalt
Speicherzelleninhaltneu
In der x86-Architektur wird bei einem Sprung mit der Hilfe von einer relativen Adresse die absolute Zieladresse berechnet, indem der Inhalt vom Befehlszeigerregister und der angegebene Offset durch eine Addition (Plus rechnen) zusammengerechnet werden.

Das Befehlszeigerregister hält allerdings zu dem Zeitpunkt, an welchem der Sprung durchgeführt wird, nicht die Adresse von der Speicherzelle gespeichert, in welchem der Offset gespeichert ist, sondern das Befehlszeigerregister hält zu diesem Zeitpunkt die absolute Anfangsadresse vom nachfolgenden Maschinenbefehl gespeichert.

Da der nachfolgende Maschinenbefehl direkt hinter der "4 Byte"-großen Speicherzelle beginnt, welche den Offset gespeichert hält, muss "-4|h" als Wert von Speicherzelleninhaltalt angegeben werden.
R_386_PC32 einer relativen Adresse ein Sprung durchgeführt werden soll. 2
  Dingadressegewählt
Speicherzelleninhaltneu
Siehe die Beschreibung vom Dezimalwert "1". R_386_GLOB_DAT einer absoluten Adresse auf Daten zugegriffen werden soll. 6
Siehe die Beschreibung vom Wert "1|h".

Von dem was ich gelesen habe, sieht es so aus, als würde die Adressen-Reparatur nur dann direkt beim Lade-Vorgang durchgeführt werden, wenn die Flagge "DF_BIND_NOW" in der Tabelle für Exporte und Importe auf "1|b" gesetzt ist. Andernfalls würde die Adresse beim ersten Zugriff auf das Ding repariert werden.

In diese Dokumentation gibt es
  • weder zu dieser Flagge
  • noch zu diesem verspäteten Reparatur-Verfahren
weitere Informationen.
R_386_JMP_SLOT 7
  Basisadressegewählt
Speicherzelleninhaltalt
Speicherzelleninhaltneu
  • Diese Methode, ein Ding zu importieren, ist zwar schnell, sie kann aber Probleme verursachen, wenn sich die Bibliothek ändert, aus welcher das Ding importiert werden soll.
  • Im Feld "st_shndx" von dem Eintrag in der Tabelle für Dinge, welche Auskünfte über das Ding macht, auf welches sich diese Adressen-Reparatur bezieht, muss der Wert "0|h" eingetragen werden.
  • Im Feld "r_info" beim Offset "5 Byte bis 6 Byte", wo die Position von dem Eintrag in der Tabelle für Dinge angegeben ist, kann der Wert "0|h" angegeben werden, sodass kein bestimmtes Ding mit dieser Adressen-Reparatur verbunden wird. Es ist also möglich, die Adressen-Reparatur ohne einen Eintrag in der Tabelle für Dinge durchzuführen.
R_386_RELATIVE
  • einem Offset ein Ding importiert werden soll, statt dass ein Name verwendet werden soll.

    Oder
  • einer absoluten Adresse auf Daten zugegriffen werden soll, ohne dass ein Eintrag in der Tabelle für Dinge notwendig sein soll.

    Dies ist speziell bei Bibliotheken desöfteren der Fall, welche auf ihre eigenen Daten zugreifen sollen.
8

Es gibt weitere gültige Werte, welche in dieser Dokumentation nicht beschrieben sind.

Die Bedeutungen von den Variablen in den Formeln sind wie folgt:
Bedeutung: Name:
Gibt die vom Lade-Programm gewählte Basisadresse von der Datei an, in welcher sich das Ding befindet. Basisadressegewählt
Gibt die Anfangsadresse vom Ding an.

Diese Anfangsadresse wird unter der Berücksichtigung von der vom Lade-Programm gewählten Basisadresse angegeben. Es handelt sich bei der x86-Architektur um einen Offset vom Segmentanfang.

Dies ist also die reparierte Adresse.
Dingadressegewählt
Gibt die Anfangsadresse von der Speicherzelle an, in welcher die zu reparierende Adresse gespeichert ist.

Diese Anfangsadresse wird unter der Berücksichtigung von der vom Lade-Programm gewählten Basisadresse angegeben. Es handelt sich bei der x86-Architektur um einen Offset vom Segmentanfang.
Speicherzellenadressegewählt
Dies ist ein "4 Byte"-großer Wert, welcher bisher in der Speicherzelle von der zu reparierenden Adresse gespeichert war, als er noch in der Datei auf der Festplatte/Diskette/CD/was auch immer war.

Dies ist also die zu reparierende Adresse.
Speicherzelleninhaltalt
Dies ist ein "4 Byte"-großer Wert, welcher in der Speicherzelle von der zu reparierenden Adresse gespeichert sein wird, nachdem der Lade-Vorgang abgeschlossen wurde.

Dies ist also die reparierte Adresse.
Speicherzelleninhaltneu
Ansonsten wenn dann
  • wird dieses Byte normalerweise nicht benutzt.

    Ich empfehle daher, alle Bits von diesem Byte auf "0|b" zu setzen.
r_info 1 Byte 4 Byte
Gibt die Position von dem Eintrag in der Tabelle für Dinge an, welche Auskünfte über das Ding macht, für welches die zu reparierende Adresse ist.

Die Position wird von "0" ausgehend gezählt.
  • Der "erste" Eintrag hat also die Position "0",
  • der "zweite" Eintrag hat die Position "1",
  • und so weiter.

Welche Tabelle für Dinge verwendet werden soll, wird im Feld "sh_link" von einer bestimmten Kopfzeile in der Kopfzeilen-Tabelle für Unterabschnitte angegeben. Diese Kopfzeile macht Auskünfte über den Inhalt vom Unterabschnitt, in welchem die Tabelle für Adressen-Reparaturen enthalten ist.

Im Folgenden ist die Bedeutung vom Wert von diesem Feld grafisch dargestellt:
   ╔══════════════════════════════════════╗
   ║Kopfzeilen-Tabelle für Unterabschnitte
   ╟──────────────────┬───────────────────
   ║Feld "sh_addr" und│  Feld "sh_link":  
   ║Feld "sh_offset": │                   
   ╠══════════════════╪═══════════════════╣
┌──╢  Anfangsadresse  │                   ╟◄┐
  ╟──────────────────┼───────────────────╢ 
  ║                  │                   ║ 
  ╟──────────────────┼───────────────────╢ 
┌─╢  Anfangsadresse       Position      ╟─┘
 ╟──────────────────┼───────────────────╢
 ║                  │                   ║
 ╚══════════════════╧═══════════════════╝

 ╔══════════════════════════════════════╗
 ║   Inhalte von den Unterabschnitten   
 ╠══════════════════════════════════════╣
 ║                                      ║
└►╫──────────────────────────────────────╢
  ║╔════════════════════════════════════╗║
  ║║  Tabelle für Adressen-Reparaturen  ║║
  ║╟─────────────────┬──────────────────╢║
  ║║   Feld "...":   │  Feld "r_info":  ║║
  ║╠═════════════════╪══════════════════╣║
  ║║                 │     Position     ╟╫──┐
  ║╟─────────────────┼──────────────────╢║  
  ║║                 │                  ║║  
  ║╟─────────────────┼──────────────────╢║  
  ║║                 │     Position     ╟╫─┐
  ║╚═════════════════╧══════════════════╝║ 
  ╟──────────────────────────────────────╢ 
  ║                                      ║ 
└─►╫──────────────────────────────────────╢ 
   ║╔════════════════════════════════════╗║ 
   ║║         Tabelle für Dinge          ║║ 
   ║╠════════════════════════════════════╣║ 
   ║║                                    ╟╫◄┘
   ║╟────────────────────────────────────╢║  
   ║║                                    ║║  
   ║╟────────────────────────────────────╢║  
   ║║                                    ╟╫◄─┘
   ║╚════════════════════════════════════╝║
   ╚══════════════════════════════════════╝
2 Byte 5 Byte
bis
6 Byte
Wenn dann
  • wird dieses Byte normalerweise nicht benutzt.

    Ich empfehle daher, alle Bits von diesem Byte auf "0|b" zu setzen.
Ansonsten wenn dann gilt das Folgende:
Wenn
  • im Feld "e_machine" vom Kopfzeilen-Abschnitt der Dezimalwert "3" angegeben wurde, was bedeutet, dass die x86er-Architektur in einer "32 Bit"-Version benutzt wird, also die Architektur vom Prozessor "80386" (alias "Intel386"), oder eine spätere, darauf basierende Architektur,
dann
  • gibt dieses Feld die Berechnungsmethode an, welche verwendet werden soll, ...

    siehe: oben
1 Byte 7 Byte
Gesamtgröße: 8 Byte

Bezeichnung
Die Tabelle für Adressen-Reparaturen wird auch "Umleitungseinträge" (im Englischen: "relocation entries") genannt.

Die offizielle Beschreibung vom Dateiformat verwendet an keiner Stelle die Idee von einer Tabelle als Container für die Einträge. Die einzelnen Einträge werden stattdessen als eine Ansammlung von Einträgen in einem Inhalt von einem Unterabschnitt angesehen.

Position
Wenn
  • die Tabelle für Adressen-Reparaturen Einträge enthält, welche für Exporte oder Importe verwendet werden,
dann

Tabelle für Dinge
Zweck
Eine Tabelle für Dinge definiert Eigenschaften von Dingen. Ein Ding ist ein Datensatz oder eine Funktion.

Mit ihrer Hilfe wird beispielsweise für jedes Ding angegeben
  • wo der Name gespeichert ist,
  • welche Adresse die Anfangsadresse vom Ding ist und, wenn auch nur indirekt,
  • ob das Ding exportiert oder importiert werden soll.

Diese Angaben können einem Programm zur Fehlersuche (Debugger) helfen.

Wenn
  • Dinge
    • exportiert oder
    • importiert
    werden,
dann
  • sind diese Angaben im besonderen Maß hilfreich, da dann
    • der Export oder
    • der Import
    anhand vom Namen durchgeführt werden kann, anstatt dass dies anhand von einem Offset notwendig ist.

Aufbau
In dieser Tabelle ist für jedes relevante Ding von der Datei ein Eintrag.

Jeder Eintrag ist "16 Byte"-groß. Zwischen den einzelnen Einträgen dürfen keine unbenutzten Bytes sein.

Wenn
  • in dieser Tabelle Einträge aufgelistet sind,
dann
  • muss laut der offiziellen Beschreibung vom Dateiformat an der 0. Stelle ein Eintrag stehen, von welchem alle Bits auf "0|b" gesetzt sind.

    Dies betrifft also die ersten 16 Byte von dieser Tabelle.

    Nach meinen eigenen Tests ist dies jedoch nicht notwendig. Man kann auch auf diese Null-Bytes verzichten, indem man dem Lade-Programm vorgaukelt, dass der 0. Eintrag existieren würde. Hierfür
    • lässt man die Null-Bytes weg und
    • gibt in der Tabelle für Exporte und Importe eine Adresse für die Tabelle für Dinge an, welche um 16 Byte niedriger ist, als die Anfangsadresse vom 1. Eintrag. Auf diese Weise geht das Lade-Programm davon aus, dass vor dem 1. Eintrag der 0. Eintrag steht. Da der 0. Eintrag jedoch nie gelesen wird, stört sich das Lade-Programm nicht daran, dass im 0. Eintrag
      • nicht alle Bits auf "0|b" gesetzt sind,
      • sondern stattdessen die letzten 16 Byte vom vorherigen Inhalt/von der vorherigen Tabelle stehen.

Die Tabelle für Adressen-Reparaturen ist nach meinem Kenntnisstand die einzige Tabelle, welche auf Einträge von der Tabelle für Dinge verweist. Dabei wird jeweils die Position vom Eintrag in der Tabelle für Dinge angegeben. Daher ist es nicht notwendig, das Ende von der Tabelle für Dinge bekannt zu machen.

Die Einträge bestehen jeweils aus mehreren Feldern, welche Auskünfte über das jeweilige Ding machen. Der Aufbau von einem Eintrag sieht wie folgt aus:
Beschreibung: Bezeichnung: Größe: Hexadezimalwert: Offset vom Anfang vom Eintrag:
Gibt den Offset in Byte an, vom Anfang von der Tabelle für Zeichenketten bis zum ersten Byte vom Namen vom Ding.

Welche Tabelle für Zeichenketten verwendet werden soll, wird im Feld "sh_link" von der Kopfzeile in der Kopfzeilen-Tabelle für Unterabschnitte angegeben, welche Auskünfte über den Inhalt vom Unterabschnitt macht, in welcher die Tabelle für Dinge enthalten ist.

Im Folgenden ist die Bedeutung vom Wert von diesem Feld grafisch dargestellt:
   ╔══════════════════════════════════════╗
   ║Kopfzeilen-Tabelle für Unterabschnitte
   ╟──────────────────┬───────────────────
   ║Feld "sh_addr" und│  Feld "sh_link":  
   ║Feld "sh_offset": │                   
   ╠══════════════════╪═══════════════════╣
   ║                  │                   ║
   ╟──────────────────┼───────────────────╢
┌──╢  Anfangsadresse  │                   ╟◄┐
  ╟──────────────────┼───────────────────╢ 
┌─╢  Anfangsadresse       Position      ╟─┘
 ╟──────────────────┼───────────────────╢
 ║                  │                   ║
 ╚══════════════════╧═══════════════════╝

 ╔══════════════════════════════════════╗
 ║   Inhalte von den Unterabschnitten   
 ╠══════════════════════════════════════╣
 ║                                      ║
└►╫──────────────────────────────────────╢
  ║╔════════════════════════════════════╗║
  ║║         Tabelle für Dinge          ║║
  ║╟─────────────────┬──────────────────╢║
  ║║   Feld "...":   │ Feld "st_name":  ║║
  ║╠═════════════════╪══════════════════╣║
  ║║                 │      Offset      ╟╫─────┐
  ║╟─────────────────┼──────────────────╢║     
  ║║                 │                  ║║     
  ║╟─────────────────┼──────────────────╢║     
  ║║                 │      Offset      ╟╫──┐  
  ║╚═════════════════╧══════════════════╝║    
└─►╫──────────────────────────────────────╢    
   ║╔════════════════════════════════════╗║    
   ║║     Tabelle für Zeichenketten      ║║    
   ║╠════════════════════════════════════╣║  
   ║║                                    ║║├◄┘ 
   ║╟────────────────────────────────────╢║  ├◄┘
   ║║              "printf"              ║║   
   ║╟────────────────────────────────────╢║   
   ║║             "_DYNAMIC"             ║║
   ║╚════════════════════════════════════╝║
   ╟──────────────────────────────────────╢
   ║                                      ║
   ╚══════════════════════════════════════╝

Es gibt einen Namen, welcher eine besondere Bedeutung hat:
Bedeutung: Name:
Dieses Ding macht Auskünfte über die Tabelle für Exporte und Importe.

Die Angabe von einem solchen Ding ist allerdings optional. Das Lade-Programm von Linux benötigt es nicht und wertet es vermutlich auch nicht aus.
_DYNAMIC
st_name 4 Byte 0 Byte
bis
3 Byte
Der Wert von diesem Feld gibt die Anfangsadresse vom Ding an.

Die Anfangsadresse muss unter der Berücksichtigung von der vom Erzeuger gewünschten Basisadresse angegeben werden. Es handelt sich bei der x86-Architektur um einen Offset vom Segmentanfang.

Die Adresse wird nur dann während dem Lade-Vorgang repariert, wenn dafür eine Adressen-Reparatur-Anweisung in der Tabelle für Adressen-Reparaturen vorhanden ist.

Wenn
  • das Ding nicht in einem Inhalt von einem Unterabschnitt in der Datei auf der Festplatte/Diskette/CD/was auch immer ist, weil es beispielsweise in einer anderen Datei ist und importiert werden soll,
dann
  • wird dies mit der Hilfe vom Wert "0|d" angegeben.
st_value 4 Byte 4 Byte
bis
7 Byte
Mit der Hilfe von diesem Feld kann die Größe vom Ding angegeben werden.

Diese Angabe ist optional. Speziell, wenn das Ding eine Funktion ist, dann bringt die Angabe von der Größe nur sehr wenig Nutzen.

Der Wert "0|d" bedeutet, dass keine Größe angegeben ist.
st_size 4 Byte 8 Byte
bis
11 Byte
Gibt die Art vom Inhalt von einem Unterabschnitt an, in welchem das Ding enthalten ist. st_info 4 Bit
Bedeutung: Bezeichnung: Hexadezimalwert:
Es wird keine Auskunft darüber gemacht, in welchem Inhalt von einem Unterabschnitt das Ding enthalten ist.

Diese Angabe wird für Dinge verwendet, welche importiert werden, damit der Compiler die Bibliothek, aus welcher das Ding importiert wird, nicht analysieren muss.
STT_NOTYPE 0
Das Ding ist in einem Inhalt von einem Unterabschnitt enthalten, welcher Daten enthält.

Das Ding ist also zum Beispiel ein Datensatz.
STT_OBJECT 1
Das Ding ist in einem Inhalt von einem Unterabschnitt enthalten, welcher Maschinencode enthält.

Das Ding ist also zum Beispiel eine Funktion.
STT_FUNC 2

Es gibt weitere gültige Werte, welche in dieser Dokumentation nicht beschrieben sind.
12 Byte
+(
    0 Bit
    bis
    3 Bit
)
Gibt die Datei an, in welcher das Ding enthalten ist. 4 Bit
Bedeutung: Bezeichnung: Hexadezimalwert:
Das Ding ist in dieser Datei enthalten und wird nur von dieser Datei verwendet.

Alle Dinge, für welche diese Angabe verwendet wird, müssen am Anfang von dieser Tabelle für Dinge aufgelistet werden, sodass diese Dinge kommen, bevor die restlichen Dinge kommen.

Im Feld "sh_info" von der Kopfzeile in der Kopfzeilen-Tabelle für Unterabschnitte, welche Auskünfte über den Inhalt vom Unterabschnitt macht, in welcher diese Tabelle für Dinge enthalten ist, wird die Position vom ersten Ding in dieser Tabelle für Dinge angegeben, welches einen anderen Wert als "0|h" für diese Angabe hat.

Diese Angabe kann für Dinge verwendet werden, welche
    • weder exportiert,
    • noch importiert
    werden sollen, aber
  • zum Nutzen von einem Programm zur Fehlersuche (Debugger) dennoch beim Namen genannt werden sollen.
STB_LOCAL 0
  • Das Ding ist in einer anderen Datei enthalten, oder
  • das Ding ist in dieser Datei enthalten und wird möglicherweise (auch) von anderen Dateien verwendet.

Diese Angabe kann für Dinge verwendet werden, welche
  • exportiert oder
  • importiert
werden.

Wenn
  • das Importieren scheitern sollte,
dann
  • wird die Anwendung nicht gestartet, sondern es wird eine Fehlermeldung angezeigt.
STB_GLOBAL 1
  • Das Ding ist in einer anderen Datei enthalten, oder
  • das Ding ist in dieser Datei enthalten und wird möglicherweise (auch) von anderen Dateien verwendet.

Diese Angabe kann für Dinge verwendet werden, welche
  • exportiert oder
  • importiert
werden.

Wenn
  • das Importieren scheitern sollte,
dann
  • wird die Anwendung trotzdem gestartet und es wird keine Fehlermeldung angezeigt. Außerdem trägt das Lade-Programm in diesem Fall im Feld "st_value" nicht die Adresse zum Ding ein, sondern es trägt den Wert "0|h" ein.

Diese Angabe kann also im Speziellen dann verwendet werden, wenn beispielsweise ein Ding importiert werden soll, auf das zur Not verzichtet werden kann, ohne dass die komplette Funktionalität dadurch verloren gehen würde.
STB_WEAK 2

Es gibt möglicherweise weitere gültige Werte, welche in dieser Dokumentation nicht beschrieben sind.
12 Byte
+(
    4 Bit
    bis
    7 Bit
)
Leider konnte ich nicht zufriedenstellen herausfinden, wofür dieses Feld dient.

Von dem was ich bisher verstanden habe, würde ich am ehesten den Wert "0|h" in dieses Feld eintragen.
st_other 1 Byte 0 13 Byte
Gibt die Position von der Kopfzeile in der Kopfzeilen-Tabelle für Unterabschnitte an, welche Auskünfte über den Inhalt vom Unterabschnitt macht, welcher das Ding enthält.

Die Position wird von "0" ausgehend gezählt.
  • Der "erste" Eintrag hat also die Position "0",
  • der "zweite" Eintrag hat die Position "1",
  • und so weiter.

Wenn
  • sich das Ding in einer anderen Datei befindet, weil es beispielsweise aus einer Bibliothek importiert werden soll,
dann
  • wird dies mit der Hilfe vom Wert "0|h" angegeben.

Wenn
  • keine solche Kopfzeile in der Datei existiert,
dann
  • kann dies ebenfalls mit der Hilfe vom Wert "0|h" angegeben werden.

Das Lade-Programm von Linux scheint diesen Wert ohnehin nicht auszuwerten.
st_shndx 2 Byte 14 Byte
bis
15 Byte
Gesamtgröße: 16 Byte

Bezeichnung
Die Tabelle für Dinge wird auch "Symbol-Tabelle" (im Englischen: "symbol table") genannt.

Position
Wenn
  • die Tabelle für Dinge Dinge enthält, welche für
    • Exporte oder
    • Importe
    verwendet werden,
dann

Tabelle für Exporte und Importe
Zweck
Die Tabelle enthält die Informationen, welche die Bibliothek, welche ein Teil vom Lade-Programm ist, im Segment benötigt, um seine Aufgaben zu erfüllen.

Dies sind zum Beispiel
An keiner Stelle in diesem Dateiformat wird allerdings ein spezifisches Ding, welches importiert werden soll, mit einer spezifischen Bibliothek verbunden. Vielmehr werden
  • alle Dinge genannt, welche importiert werden sollen, und
  • alle Bibliotheken genannt, in welchen die Dinge sind.
In welcher von diesen Bibliotheken ein spezifisches Ding ist, das muss das Lade-Programm selbst herausfinden.

Dies bedeutet, dass
  • Dinge eindeutige Namen haben müssen, oder
  • die Reihenfolge, in welcher die Bibliothekpfade und -namen angegeben werden, korrekt sein muss.

Anforderungen an den Inhalt von einem Hauptabschnitt
Die Tabelle für Exporte und Importe muss

Aufbau
Die Tabelle ist eine Auflistung von Einträgen.

Jeder Eintrag hat 2 Felder. Diese Felder sind immer gleich groß und somit hat jeder Eintrag immer das selbe Format.

Das Ende von der Tabelle wird markiert, indem ein weiterer Eintrag angehängt wird, von welchem im ersten Feld alle Bits auf "0" gesetzt sind.

Der Aufbau von einem Eintrag sieht wie folgt aus:
Beschreibung: Bezeichnung: Größe: Hexadezimalwert: Offset vom Anfang vom Eintrag:
Gibt durch seinen Wert die Bedeutung vom anderen Feld an. d_tag 4 Byte
Bedeutung vom gesamten Eintrag: Bezeichnung: Hexadezimalwert:
Kennzeichnet das Ende von der Tabelle für Exporte und Importe. DT_NULL 0
0 Byte
bis
3 Byte
Die Bedeutung vom Wert von diesem Feld hängt vom Wert im Feld "d_tag" ab. d_ptr und
d_val
4 Byte
dieses Feld: Feld "d_tag":
Bedeutung: Bezeichnung: Hexadezimalwert:
- Der Wert von diesem Feld hat keine besondere Bedeutung. - DT_NULL 0
Gibt den Offset in Byte an, vom Anfang von einer Tabelle für Zeichenketten bis zum ersten Byte von einer Zeichenkette, welche
  • den Dateinamen oder
  • den absoluten Pfad und den Dateinamen
von einer Bibliothek angibt, welche importiert werden soll.

Welche Tabelle für Zeichenketten betroffen ist, wird durch einen weiteren Eintrag in der Tabelle für Exporte und Importe angegeben, in welchem im Feld "d_tag" der Wert "5|h" angegeben ist.

Die Zeichenkette, welche mit der Hilfe von diesem Feld angegeben wird, ist beispielsweise "libc.so.6". Hierdurch wird die Bibliothek "libc.so" aus dem Verzeichnis "/lib/" angefordert. In diesem Verzeichnis befinden sich beispielsweise, unter anderem, folgende Dateien:
  • "libc.so.6" (eine Weiterleitung zur Datei "libc-2.11.1.so")
  • "libc-2.11.1.so" (die Bibliothek "libc.so")

Wenn
  • mehrere Bibliotheken importiert werden sollen,
dann
  • gibt es in der Tabelle für Exporte und Importe mehrere Einträge von dieser Art. Jeder Eintrag gibt jeweils 1 Bibliothek an.

Die Reihenfolge, in welcher die einzelnen Einträge in dieser Tabelle auftauchen, entscheidet darüber, in welcher Reihenfolge die einzelnen Bibliotheken in den Arbeitsspeicher geladen werden.

Weitere Möglichkeiten zur Pfadangabe gibt es durch einen weiteren Eintrag in der Tabelle für Exporte und Importe, in welchem im Feld "d_tag" der Wert "1D|h" angegeben ist.
DT_NEEDED 1
Gibt die Anfangsadresse von der Tabelle für Prüfsummen an, in welcher die Prüfsummen von
  • den Dingnamen
enthalten sind, auf welche sich die Exporte beziehen.

Die Anfangsadresse muss unter der Berücksichtigung von der vom Erzeuger gewünschten Basisadresse angegeben werden. Es handelt sich bei der x86-Architektur um einen Offset vom Segmentanfang.

Nur eine Datei, welche Dinge exportiert, benötigt eine solchen Eintrag und eine solche Tabelle. Wenn
  • eine Datei, zum Beispiel eine Anwendung, lediglich Dinge importiert,
dann
Für diese Anfangsadresse muss keine Adressen-Reparatur-Anweisung gegeben werden. Das Lade-Programm repariert diese Adresse jedoch dennoch. Daher muss der Speicherbereich beschreibbar sein, andernfalls wird
  • die Fehlermeldung "Segmentation fault" ausgegeben und
  • der Lade-Vorgang abgebrochen.
DT_HASH 4
Gibt die Anfangsadresse von der Tabelle für Zeichenketten an, in welcher die Zeichenketten von
  • den Bibliotheknamen,
  • den Dingnamen und
  • gegebenenfalls weiteren Suchpfaden für Bibliotheken
enthalten sind, auf welche sich die Exporte und die Importe beziehen.

Die Anfangsadresse muss unter der Berücksichtigung von der vom Erzeuger gewünschten Basisadresse angegeben werden. Es handelt sich bei der x86-Architektur um einen Offset vom Segmentanfang.

Wenn dann
  • muss dies durch einen solchen Eintrag angegeben werden. Das Lade-Programm von Linux liest nicht die Kopfzeilen-Tabelle für Unterabschnitte, sondern würde, wenn kein solcher Eintrag existiert, die Tabelle nicht finden.

Für diese Anfangsadresse muss keine Adressen-Reparatur-Anweisung gegeben werden. Das Lade-Programm repariert diese Adresse jedoch dennoch. Daher muss der Speicherbereich beschreibbar sein, andernfalls wird
  • die Fehlermeldung "Segmentation fault" ausgegeben und
  • der Lade-Vorgang abgebrochen.
DT_STRTAB 5
Gibt die Anfangsadresse von der Tabelle für Dinge an, in welcher die Dinge enthalten sind, welche
  • exportiert oder
  • importiert
werden sollen.

Die Anfangsadresse muss unter der Berücksichtigung von der vom Erzeuger gewünschten Basisadresse angegeben werden. Es handelt sich bei der x86-Architektur um einen Offset vom Segmentanfang.

Wenn dann
  • muss dies durch einen solchen Eintrag angegeben werden. Das Lade-Programm von Linux liest nicht die Kopfzeilen-Tabelle für Unterabschnitte, sondern würde, wenn kein solcher Eintrag existiert, die Tabelle nicht finden.

Für diese Anfangsadresse muss keine Adressen-Reparatur-Anweisung gegeben werden. Das Lade-Programm repariert diese Adresse jedoch dennoch. Daher muss der Speicherbereich beschreibbar sein, andernfalls wird
  • die Fehlermeldung "Segmentation fault" ausgegeben und
  • der Lade-Vorgang abgebrochen.
DT_SYMTAB 6
Gibt die Gesamtgröße von der Tabelle für Zeichenketten in Byte an, in welcher die Zeichenketten von
  • den Bibliotheknamen und
  • den Dingnamen
enthalten sind, auf welche sich die Exporte und die Importe beziehen.

Laut der offiziellen Beschreibung vom Dateiformat muss diese Angabe gemacht werden. Da allerdings die einzelnen Einträge von dieser Tabelle auch quer über die Datei verstreut sein können und jeder einzelne Eintrag ohnehin durch ein Zeichen abgeschlossen ist, von welchem alle Bits auf "0|b" gesetzt sind, ergibt diese Angabe nicht allzuviel Sinn. Nach meinen Tests ist diese Angabe in Linux nicht notwendig und wird vermutlich vom Lade-Programm von Linux ohnehin nicht ausgewertet.
DT_STRSZ A
Gibt die Größe von einem Eintrag in der Tabelle für Dinge in Byte an, in welcher die Dinge enthalten sind, welche exportiert oder importiert werden sollen.

Hier ist nicht die Gesamtgröße von der Tabelle für Dinge gemeint, sondern wie groß jeder einzelne Eintrag ist.

Jeder Eintrag ist immer "16 Byte" groß, daher wird in diesem Feld der Dezimalwert "16" angegeben.

Laut der offiziellen Beschreibung vom Dateiformat muss diese Angabe gemacht werden. Nach meinen Tests hingegen ist diese Angabe in Linux nicht notwendig und wird vermutlich vom Lade-Programm von Linux auch nicht ausgewertet.
DT_SYMENT B
Mit der Hilfe vom Feld wird die Anfangsadresse von der Initialisierungsroutine angegeben.

Die Anfangsadresse muss unter der Berücksichtigung von der vom Erzeuger gewünschten Basisadresse angegeben werden. Es handelt sich bei der x86-Architektur um einen Offset vom Segmentanfang.

Die Initialisierungsroutine wird während dem gesamten Bestehen vom Prozess nur 1 mal ausgeführt. Welches Ereignis eingetreten sein muss, um die Ausführung auszulösen, hängt davon ab, um welche Art von Datei es sich handelt. Bei
  • einer ausführbaren Datei gibt es das folgende Ereignis:
    • Die Anwendung und die gewünschten Bibliotheken wurden vollständig geladen.
  • einer Bibliothek gibt es das folgende Ereignis:
    • Die Bibliothek wurde ins Segment von einer Anwendung geladen.

      Wenn
      • die Bibliothek ein Ding aus einer anderen Bibliothek importiert, welche ebenfalls eine Initialisierungsroutine hat,
      dann
      • wird zunächst die Initialisierungsroutine(n) von jener Bibliothek/jenen Bibliotheken ausgeführt, von denen ein Ding importiert wird.
In allen Fällen hat die Anwendung bisher noch nicht auf die reguläre Weise (mit der Hilfe vom Feld "e_entry" von der Kopfzeile von der Anwendung) die Kontrolle über den CPU erhalten.

Bei einer ausführbaren Datei ist die Verwendung von einem solchen Eintrag zwar zulässig, die Anwendung erhält jedoch hierdurch 2 mal die Kontrolle über den CPU:
  1. Zuerst erhält sie die Kontrolle über den CPU, weil angegeben ist, dass eine Initialisierungsroutine existiert.
  2. Sobald die Anwendung aus dieser Routine zurückkehrt, erhält sie über die reguläre Weise erneut die Kontrolle über den CPU.
Somit kann der Maschinencode, welcher für die Initialisierungsroutine vorgesehen ist, auch einfach vor den Anfang vom Maschinencode gespeichert werden, welcher auf die reguläre Weise ausgeführt wird.

Wenn
  • das Ende vom auszuführenden Maschinencode erreicht wurde,
dann
  • kann dem Lade-Programm mit der Hilfe von einer Ausführung vom Befehl "return" ("ret") wieder die Kontrolle über den CPU übergeben werden, da eine geeignete Rücksprungadresse oben auf dem Stapel liegt.
Das Beenden von der Ausführung vom Maschinencode entspricht also dem Zurückkehren von einer Funktion, welche keine Parameter hat.

Das Lade-Programm
  • übergibt der Initialisierungsroutine keine Parameterwerte, da es davon ausgeht, dass es sich um eine Funktion ohne Eingabe-Parameter handelt.
  • erwartet von der Initialisierungsroutine keine Parameterwerte, da es davon ausgeht, dass es sich um eine Funktion ohne Rückgabe-Parameter handelt.

Wenn
  • kein Maschinencode ausgeführt werden soll, nur weil das Ereignis eingetroffen ist,
dann
  • kann ein solche Eintrag weggelassen werden.

Für diese Anfangsadresse muss keine Adressen-Reparatur-Anweisung gegeben werden. Das Lade-Programm repariert diese Adresse jedoch dennoch. Daher muss der Speicherbereich beschreibbar sein, andernfalls wird
  • die Fehlermeldung "Segmentation fault" ausgegeben und
  • der Lade-Vorgang abgebrochen.
DT_INIT C
- Dieser Wert vom Feld "d_tag" ist veraltet und wurde mit der Hilfe vom Wert "1D|h" ersetzt. - DT_RPATH F
Gibt die Anfangsadresse von der Tabelle für Adressen-Reparaturen an, in welcher die Adressen-Reparatur-Anweisungen für die Speicherzellen enthalten sind, auf welche sich die Exporte und Importe beziehen.

Die Anfangsadresse muss unter der Berücksichtigung von der vom Erzeuger gewünschten Basisadresse angegeben werden. Es handelt sich bei der x86-Architektur um einen Offset vom Segmentanfang.

Wenn dann
Für diese Anfangsadresse muss keine Adressen-Reparatur-Anweisung gegeben werden. Das Lade-Programm repariert diese Adresse jedoch dennoch. Daher muss der Speicherbereich beschreibbar sein, andernfalls wird
  • die Fehlermeldung "Segmentation fault" ausgegeben und
  • der Lade-Vorgang abgebrochen.
DT_REL 11
Gibt die Gesamtgröße von der Tabelle für Adressen-Reparaturen in Byte an, in welcher die Adressen-Reparatur-Anweisungen für die Speicherzellen enthalten sind, auf welche sich die Exporte und Importe beziehen.

Wenn dann
  • muss dieser Eintrag ebenfalls existieren.
DT_RELSZ 12
Gibt die Größe von einem Eintrag in der Tabelle für Adressen-Reparaturen in Byte an, in welcher die Adressen-Reparatur-Anweisungen für die Speicherzellen enthalten sind, auf die sich die Exporte und Importe beziehen.

Hier ist nicht die Gesamtgröße von der Tabelle für Adressen-Reparaturen gemeint, sondern wie groß jeder einzelne Eintrag ist.

Jeder Eintrag ist immer 8 Byte groß, daher wird in diesem Feld der Wert "8|h" angegeben.

Wenn dann
  • muss dieser Eintrag ebenfalls existieren, andernfalls wird
    • die Fehlermeldung "Segmentation fault" ausgegeben und
    • der Lade-Vorgang abgebrochen.
DT_RELENT 13
Gibt den Offset in Byte an, vom Anfang von einer Tabelle für Zeichenketten bis zum ersten Byte von einer Zeichenkette, welche einen Pfad angibt, wo möglicherweise eine von den Bibliotheken gespeichert ist, welche importiert werden soll.

Welche Tabelle für Zeichenketten betroffen ist, wird durch einen weiteren Eintrag in der Tabelle für Exporte und Importe angegeben, in welchem im Feld "d_tag" der Wert "5|h" angegeben ist.

Um mehrere Pfade anzugeben gibt es folgende Möglichkeiten:
  • Für jeden Pfad wird jeweils 1 Eintrag in die Tabelle für Exporte und Importe aufgenommen. Jeder Eintrag gibt jeweils 1 Pfad an.
  • Alle Pfade werden in der Zeichenkette hintereinander gehängt, aber jeweils durch einen Doppelpunkt (":") voneinander getrennt. Zum Beispiel "/usr/lib/Allzweck-Bibliotheken/:/home/Physik-Bibliotheken/"

Im Vergleich zu Windows wird in Linux der Pfad, in welchem die ausführbare Datei ist, standardmäßig nicht nach Bibliotheken durchsucht. Mit der Hilfe von diesem Feld kann der Pfad allerdings angegeben werden. Dies ist beispielsweise mit der Hilfe von der relativen Angabe "./" möglich.

Das Suchen von Bibliotheken kann aber auch auf eine bestimmte Bibliothek begrenzt werden, indem durch die Zeichenkette der Pfad und der Dateiname angegeben wird. Also zum Beispiel "./Bibliotheken/zu_importierende_Bibliothek.so".

Die Reihenfolge, in welcher die einzelnen Pfade angegeben werden, entscheider darüber, in welcher Reihenfolge die Pfade durchsucht werden sollen, um die einzelnen Bibliotheken zu finden. Dies beeinflusst, welche Bibliothek gefunden wird, wenn in den angegebenen Pfaden mehrere Bibliotheken mit dem selben Namen existieren.

Mit der Hilfe von den Pfaden wird lediglich das Auffinden von Bibliotheken für diese Datei ermöglicht. Wenn
  • diese Datei eine Bibliothek importiert, welche wiederum selbst Bibliotheken benötigt,
dann
  • muss die importierte Bibliothek gegebenenfalls selbst Pfade angeben.

Wenn
  • kein solcher Pfad angegeben ist, oder eine Bibliothek in den angegebenen Pfaden nicht gefunden wurde,
dann
  • werden die folgenden Pfade in der folgenden Reihenfolge zum Auffinden von den zu importierenden Bibliotheken verwendet:
    1. "/lib/"
    2. "/usr/lib/"
    3. "/usr/local/lib/"
    4. "/usr/i486-linux/lib/"

    Die Pfade werden für jede Bibliothek einzeln durchlaufen. Für jede Bibliothek wird also als erstes geprüft, ob sie durch den Pfad "/lib/" aufgefunden werden kann, bevor die Suche von dieser Bibliothek mit der Hilfe vom Pfad "/usr/lib/" fortgesetzt wird.
DT_RUNPATH 1D

Es gibt weitere gültige Werte für das Feld "d_tag", welche mit einer besonderen Bedeutung verknüpft sind. Diese Werte sind in dieser Dokumentation allerdings nicht beschrieben.
4 Byte
bis
7 Byte
Gesamtgröße: 8 Byte

Bezeichnung
Die Tabelle für Exporte und Importe wird auch "_DYNAMIC-Liste" (im Englischen: "_DYNAMIC array") genannt.

Dieser Name kommt daher, dass ein Ding mit dem Namen "_DYNAMIC" Auskünfte über diese Tabelle macht - sofern es existiert.

Position
Die Tabelle für Exporte und Importe muss am Anfang vom einem bestimmten Inhalt vom Hauptabschnitt beginnen. In der Kopfzeile von diesem Inhalt vom Hauptabschnitt muss im Feld "p_type" der Wert "2|h" angegeben sein.

Tabelle für Prüfsummen
Zweck
Diese Tabelle ist ein Hilfskonstrukt für das Lade-Programm, um Dinge anhand von ihrem Namen schneller zu finden.

Die Tabelle dient im Wesentlichen für jene Dinge, welche für einen Export markiert werden sollen. Beim Importieren von diesen Dingen müssen sie gefunden werden. Daher kann in einer ausführbaren Datei meistens auf eine solche Tabelle verzichtet werden. In einer Bibliothek hingegen muss sie meistens verwendet werden.

Der Name von einem Ding wird durch eine Zeichenkette angegeben. Wenn
  • das Lade-Programm also ein Ding anhand vom Namen finden will,
dann
  • muss es die einzelnen Zeichenketten durchlaufen und abgleichen, ob die jeweilige Zeichenkette der Zeichenkette vom Ding entspricht, welches gesucht wird.

Von jeder Zeichenkette wird eine "4 Byte"-große Prüfsumme gebildet. Beim Bilden von der Prüfsumme wird jedes Byte von der Zeichenkette in die Prüfsumme eingerechnet, sodass die komplette Zeichenkette in der "4 Byte"-großen Prüfsumme enthalten ist.

Da es sich jedoch um einen verlustbehafteten Vorgang handelt, können mit der Hilfe von dieser Methode durchaus mehrere gleiche Prüfsummen entstehen, welche sich allerdings aus unterschiedlichen Zeichenketten ergeben haben. Anhand von einer Prüfsumme ist ein Ding also nicht eindeutig identifizierbar, aber es können die meisten Dinge herausgefiltert werden, welche mit Sicherheit nicht den gesuchten Namen haben.

Die Entwickler von diesem Dateiformat haben sich mit der Hilfe von dieser Tabelle einen Geschwindigkeitsvorteil erhofft, welcher sich beim Finden vom gesuchten Ding bemerkbar machen kann.

Unabhängig davon, ob
  • dieses Verfahren einen Geschwindigkeitsvorteil bewirkt und
  • es den hohen Aufwand durch die Implementation rechtfertigt,
haben sich die Entwickler von diesem Dateiformat dazu entschieden, dass diese Tabelle für Prüfsummen zwangsweise in der Datei enthalten sein muss, wenn Dinge
  • exportiert oder
  • importiert
werden sollen.

In Linux ist es allerdings so, dass nur eine Datei, welche Dinge exportiert, eine solche Tabelle benötigt. Wenn
  • eine Datei, zum Beispiel eine Anwendung, lediglich Dinge importiert,
dann
  • ist keine Tabelle für Prüfsummen notwendig, da die Tabelle für Prüfsummen von der Bibliothek verwendet wird, welche die Dinge exportiert.

Aufbau
Allgemeines
Die Tabelle für Prüfsummen ist in mehrere Teile aufgegliedert:
  1. Am Anfang steht eine Kopfzeile.

    Mit der Hilfe von dieser Kopfzeile wird angegeben, wieviele Einträge im 2. Teil und wieviele Einträge im 3. Teil sind.
  2. Als nächstes kommt eine Auflistung von Einträgen. Jeder Eintrag gibt die Position vom 1. Ding von einer Ansammlung an.

    Eine solche Ansammlung ist eine geringe Menge von Dingen, deren Name jeweils zu der selben Prüfsumme führt.
  3. Zum Schluss kommt noch eine Auflistung von Einträgen. Jeder von diesen Einträgen gibt die Position vom nächsten Ding von der Ansammlung an, zu welcher das Ding gehört.

Zwischen den einzelnen Teilen dürfen keine unbenutzen Bytes sein.

Im Folgenden ist diese Untergliederung grafisch dargestellt:
   ╔══════════════════════════════════╗
   ║      Tabelle für Prüfsummen      
   ╠══════════════════════════════════╣
   ║╔════════════════════════════════╗║
   ║║           Kopfzeile            ║║
   ║╠════════════════════════════════╣║
┌──╫╢      Anzahl der Einträge       ║║
  ║╟────────────────────────────────╢║
  ║║      Anzahl der Einträge       ╟╫──┐
  ║╚════════════════════════════════╝║  
  ╟──────────────────────────────────╢  
  ║╔════════════════════════════════╗║  
  ║║Auflistung von Anfangspositionen║║  
 ║╠════════════════════════════════╣║  
 ║║                                ║║  
 ║╟────────────────────────────────╢║  
└►┤║║                                ║║  
  ║╟────────────────────────────────╢║  
  ║║                                ║║  
  ║╚════════════════════════════════╝║  
   ╟──────────────────────────────────╢  
   ║╔════════════════════════════════╗║  
   ║║ Auflistung von Folgepositionen ║║  
   ║╠════════════════════════════════╣║ 
   ║║                                ║║ 
   ║╟────────────────────────────────╢║ 
   ║║                                ║║ 
   ║╟────────────────────────────────╢║ 
   ║║                                ║║ 
   ║╟────────────────────────────────╢║ 
   ║║                                ║║ 
   ║╟────────────────────────────────╢║├◄┘
   ║║                                ║║
   ║╟────────────────────────────────╢║
   ║║                                ║║
   ║╟────────────────────────────────╢║
   ║║                                ║║
   ║╟────────────────────────────────╢║
   ║║                                ║║
   ║╚════════════════════════════════╝║
   ╚══════════════════════════════════╝

Kopfzeile
Die Kopfzeile ist insgesamt "8 Byte"-groß und besteht aus 2 Feldern.

Sie ist wie folgt aufgebaut:
Beschreibung: Bezeichnung: Größe: Offset vom Anfang von der Tabelle:
Gibt die Anzahl der Einträge im 2. Teil von der Tabelle für Prüfsummen an. nbucket 4 Byte 0 Byte
bis
3 Byte
Gibt die Anzahl der Einträge im 3. Teil von der Tabelle für Prüfsummen an.

Die Anzahl, welche hier angegeben wird, entspricht der Anzahl der Dinge, welche sich in der Tabelle für Dinge befindet, für welche diese Tabelle für Prüfsummen ist.

Das Lade-Programm von Linux scheint den Wert in diesem Feld nicht auszuwerten.
nchain 4 Byte 4 Byte
bis
7 Byte

Auflistung von Anfangspositionen
Dieser Teil ist eine Auflistung von Einträgen.

Hinweis und Trick:
Die einzelnen Einträge haben nach dem Konzept, welches sich die Entwickler von diesem Dateiformat ausgedacht haben, eine Bedeutung, welche in dieser Dokumentation allerdings nicht sonderlich detailiert beschrieben ist.

Der Implementierungsaufwand für eine Software, welche eine solche Tabelle für Prüfsummen erzeugt, ist relativ hoch aber der Nutzen, welchen diese Tabelle bietet, ist relativ gering. Ich empfehle daher, einen Trick anzuwenden, um das komplette Konzept auszuhebeln und nicht zu benutzen: Fügen Sie in diese Auflistung nur 1 Eintrag ein, welcher den Wert "1" hat.

Wieviele Einträge in dieser Auflistung sind, kann der Erzeuger von dieser Auflistung festlegen. Es muss allerdings mindestens 1 Eintrag vorhanden sein.

Der Aufbau von einem Eintrag sieht wie folgt aus:
Beschreibung: Bezeichnung: Größe: Offset vom Anfang vom Eintrag:
Gibt
  • sowohl die Position von einem Ding in einer Tabelle für Dinge an,
  • als auch die Position vom ersten Eintrag in der Auflistung von Folgepositionen, welcher den Anfang von einer Ansammlung darstellt.

    Eine solche Ansammlung ist eine geringe Menge von Dingen, deren Name jeweils zu der selben Prüfsumme führt.
bucket 4 Byte 0 Byte
bis
3 Byte

Auflistung von Folgepositionen
Der 3. Teil ist ebenfalls eine Auflistung von Einträgen.

Hinweis und Trick:
Die Bedeutung von den Einträgen von dieser Auflistung ist in dieser Dokumentation ebenfalls nicht sonderlich detailiert beschrieben.

Um den oben genannten Trick vollständig anzuwenden, können Sie folgendes tun: Geben Sie
  • im 0. Eintrag einen beliebigen Wert an. Was im 0. Eintrag steht, spielt keine Rolle, da er nie gelesen wird.

    Wenn
    • Sie den Wert "1" angeben,
    dann
    • können Sie diesen Trick aber wahrscheinlich am einfachsten implementieren.
  • im 1. Eintrag den Wert "2" an.
  • im 2. Eintrag den Wert "3" an.
  • im 3. Eintrag den Wert "4" an.
  • ...
  • im letzten Eintrag den Wert "0" an.

Wieviele Einträge in dieser Auflistung sind, hängt davon ab, wieviele Dinge sich in der Tabelle für Dinge befinden, auf welche sich diese Tabelle für Prüfsummen bezieht.

Der Aufbau von einem Eintrag sieht wie folgt aus:
Beschreibung: Bezeichnung: Größe: Offset vom Anfang vom Eintrag:
Die Position von diesem Eintrag in dieser Auflistung entspricht der Position von einem Ding in der Tabelle für Dinge, auf welche sich diese Tabelle für Prüfsummen bezieht.
  • Der 3. Eintrag in dieser Auflistung bezieht sich also auf
  • das 3. Ding in der Tabelle für Dinge
und
  • der 7. Eintrag in dieser Auflistung bezieht sich auf
  • das 7. Ding in der Tabelle für Dinge.

Die beiden Positionen werden jeweils von "0" ausgehend gezählt.
  • Der "erste" Eintrag/das "erste" Ding hat also die Position "0",
  • der "zweite" Eintrag/das "zweite" Ding hat die Position "1",
  • und so weiter.

Im Folgenden sind diese Verbindungen grafisch dargestellt:
╔══════════════════════════════════╗
      Tabelle für Prüfsummen      
╠══════════════════════════════════╣
║╔════════════════════════════════╗║
║║           Kopfzeile            ║║
║╠════════════════════════════════╣║
║║                                ║║
║╟────────────────────────────────╢║
║║                                ║║
║╚════════════════════════════════╝║
╟──────────────────────────────────╢
║╔════════════════════════════════╗║
║║Auflistung von Anfangspositionen║║
║╠════════════════════════════════╣║
║║                                ║║
║╟────────────────────────────────╢║
║║                                ║║
║╟────────────────────────────────╢║
║║                                ║║
║╚════════════════════════════════╝║
╟──────────────────────────────────╢
║╔════════════════════════════════╗║ ╔═════════════════════════╗
║║ Auflistung von Folgepositionen ║║ ║    Tabelle für Dinge    
║╠════════════════════════════════╣║ ╠═════════════════════════╣
║║     - beliebiger Inhalt -      ║║ ║alle Bits auf "0" gesetzt║
║╟────────────────────────────────╢║ ╟─────────────────────────╢
║║                                ╟╫─╢                         
║╟────────────────────────────────╢║ ╟─────────────────────────╢
║║                                ╟╫─╢                         
║╟────────────────────────────────╢║ ╟─────────────────────────╢
║║                                ╟╫─╢                         
║╟────────────────────────────────╢║ ╟─────────────────────────╢
║║                                ╟╫─╢                         
║╟────────────────────────────────╢║ ╟─────────────────────────╢
║║                                ╟╫─╢                         
║╟────────────────────────────────╢║ ╟─────────────────────────╢
║║                                ╟╫─╢                         
║╟────────────────────────────────╢║ ╟─────────────────────────╢
║║                                ╟╫─╢                         
║╚════════════════════════════════╝║ ╚═════════════════════════╝
╚══════════════════════════════════╝

Das auf diese Weise angegebene Ding hat möglicherweise den gesuchten Namen.

Wenn
  • der Name nicht übereinstimmt,
dann
  • gibt dieses Feld
    • entweder mit der Hilfe von seinen Wert die Position vom nächsten Eintrag in dieser Auflistung an, welches den nächsten Teil von der Ansammlung darstellt,
    • oder mit der Hilfe vom Wert "0" an, dass dieser Eintrag in dieser Auflistung der letzte Teil von der Ansammlung ist und es somit kein Ding mit dem gesuchten Namen in dieser Tabelle für Dinge gibt.

Das nächste Dinge von einer Ansammlung ist also jeweils das nächste Ding, welches geprüft werden sollte.

Im Folgenden sind diese Zusammenhänge, zusammen mit der Bedeutung von der Auflistung von Anfangspositionen, grafisch dargestellt:
     ╔══════════════════════════════════╗
     ║      Tabelle für Prüfsummen      
     ╠══════════════════════════════════╣
     ║╔════════════════════════════════╗║
     ║║           Kopfzeile            ║║
     ║╠════════════════════════════════╣║
     ║║                                ║║
     ║╟────────────────────────────────╢║
     ║║                                ║║
     ║╚════════════════════════════════╝║
     ╟──────────────────────────────────╢
     ║╔════════════════════════════════╗║
     ║║Auflistung von Anfangspositionen║║
     ║╠════════════════════════════════╣║
┌────╫╢          Position: 5           ║║
    ║╟────────────────────────────────╢║
┌───╫╢          Position: 3           ║║
   ║╟────────────────────────────────╢║
┌──╫╢          Position: 2           ║║
  ║╚════════════════════════════════╝║
  ╟──────────────────────────────────╢
  ║╔════════════════════════════════╗║  ╔═════════════════════════╗
  ║║ Auflistung von Folgepositionen ║║  ║    Tabelle für Dinge    
  ║╠════════════════════════════════╣║  ╠═════════════════════════╣
  ║║     - beliebiger Inhalt -      ║║  ║alle Bits auf "0" gesetzt║
  ║╟────────────────────────────────╢║  ╟─────────────────────────╢
  ║║               0                ╟╫◄┐║                         ║
  ║╟────────────────────────────────╢║ ╟─────────────────────────╢
└─►╫╢          Position: 1           ╟╫─┘║                         ║
   ║╟────────────────────────────────╢║  ╟─────────────────────────╢
└──►╫╢          Position: 4           ╟╫─┐║                         ║
    ║╟────────────────────────────────╢║ ╟─────────────────────────╢
    ║║               0                ╟╫◄┘║                         ║
    ║╟────────────────────────────────╢║  ╟─────────────────────────╢
└───►╫╢          Position: 7           ╟╫─┐║                         ║
     ║╟────────────────────────────────╢║ ╟─────────────────────────╢
   ┌►╫╢               0                ║║ ║                         ║
    ║╟────────────────────────────────╢║ ╟─────────────────────────╢
   └─╫╢          Position: 6           ╟╫◄┘║                         ║
     ║╚════════════════════════════════╝║  ╚═════════════════════════╝
     ╚══════════════════════════════════╝

In der obrigen Darstellung sind die Einträge, welche die Bestandteile von einer gemeinsamen Ansammlung sind, jeweils nahe beieinander. In der Realität sind diese Einträge und damit diese Dinge allerdings meistens quer über die komplette Tabelle verstreut.
chain 4 Byte 0 Byte
bis
3 Byte

Bezeichnung
Die Tabelle für Prüfsummen wird auch "Symbol-Prüfsummen-Tabelle" (im Englischen: "symbol hash table") genannt.

Tabelle für Zeichenketten
Zweck
Diese Tabelle dient als Speicher für Zeichenketten von Namen.

Allein durch die Verwendung von dieser Tabelle ist noch nicht definiert, wovon die Namen sind. Wenn dann
Es kann also für die komplette Datei 1 solche Tabelle verwendet werden, um alle Zeichenketten von Namen zu speichern.

Weitere Namen können beispielsweise von Dingen sein, also Datensätzen und Funktionen.
  • Die Dinge können ein Teil vom Maschinencode sein.

    Somit kann beispielsweise ein Programm zur Fehlersuche (Debugger) diese Namen verwenden, um dem Benutzer beim Disassemblieren vom Maschinencode Namen anzuzeigen, welche auch im Quellcode verwendet werden, damit er schneller nachvollziehen kann, woher der angezeigte Maschinencode ist.
  • Die Dinge können aber auch ein Teil von anderen Bibliotheken sein.

    Sie werden in der ausführbaren Datei genannt, damit der Import stattfinden kann.

Aufbau
In dieser Tabelle ist für jede zu speichernde Zeichenkette 1 Eintrag.

Jeder Eintrag beginnt mit der zu speichernden Zeichenkette und endet mit einem Abschluss-Zeichen. Dies ist ein "1 Byte"-großer Wert, von welchem alle Bits auf "0|b" gesetzt sind.

Wenn
  • in dieser Tabelle Einträge aufgelistet sind,
dann
  • muss laut der offiziellen Beschreibung vom Dateiformat an der 0. Stelle ein Eintrag stehen, welcher eine leere Zeichenkette gespeichert hält (""). Auch dieser Eintrag muss mit der Hilfe von einem einem Abschluss-Zeichen beendet werden.

    Dies betrifft also das 0. Byte von dieser Tabelle.

    Nach meinen eigenen Tests ist dies jedoch nicht notwendig. Man kann auch auf dieses Null-Byte verzichten, indem man dem Lade-Programm vorgaukelt, dass der 0. Eintrag existieren würde. Hierfür
    • lässt man das Null-Byte weg und
    • gibt in der Tabelle für Exporte und Importe eine Adresse für die Tabelle für Zeichenketten an, welche um 1 Byte niedriger ist, als die Anfangsadresse vom 1. Eintrag. Auf diese Weise geht das Lade-Programm davon aus, dass vor dem 1. Eintrag der 0. Eintrag steht. Da der 0. Eintrag jedoch nie gelesen wird, stört sich das Lade-Programm nicht daran, dass im 0. Eintrag
      • nicht alle Bits auf "0|b" gesetzt sind,
      • sondern stattdessen das letzte Byte vom vorherigen Inhalt/von der vorherigen Tabelle steht.

Wenn dann
  • muss die Zeichenkette
    • ASCII-kodiert sein und
    • mit der Hilfe von 1 weiteren Byte abgeschlossen sein, von welchem alle Bits auf "0|b" gesetzt sind, um das Ende von der Zeichenkette zu markieren.

Wenn
  • eine Zeichenkette ein Name von einem Dingen angibt,
dann
  • wird in der Regel ebenfalls die ASCII-Kodierung verwendet.

    Der Inhalt wird allerdings als Rohdaten aufgefasst und als Rohdaten verglichen. Entscheidend ist, ob die Bytefolge mit der zu vergleichenden Quelle übereinstimmt. Aus diesem Grund können auch einige andere Zeichenkodierungen verwendet werden.

    Da allerdings eine Zeichenkette durch 1 weiteres Byte abgeschlossen ist, von welchem alle Bits auf "0|b" gesetzt sind, gibt es Einschränkungen bei der Wahl von der Zeichenkodierung. Zeichenkodierungen, bei welchen innerhalb von der Zeichenkette einzelne "0"-Bytes vorkommen können, wie zum Beispiel bei der Zeichenkodierung "unicode character set - 2 byte per character" ("UCS-2"), eignen sich daher also nicht.

Um auf die einzelnen Einträge zu verweisen, ist es anderorts notwendig, den Offset in Byte, vom Anfang von der Tabelle bis zum ersten Byte von einem Eintrag anzugeben. Daher ist es nicht notwendig, das Ende von dieser Tabelle zu markieren.

Der Aufbau von einem Eintrag sieht also wie folgt aus:
Beschreibung: Größe: Hexadezimalwert: Offset vom Anfang vom Eintrag:
Gibt den Namen mit der Hilfe von einer Zeichenkette an. variabel 0 Byte
bis
x Byte
Gibt das Abschlussbyte an, von welchem alle Bits auf "0|b" gesetzt sind.

Hierdurch ist das Ende vom Eintrag erkennbar.
1 Byte 00   x Byte
+ 1 Byte
Gesamtgröße: variabel

Da anderorts stets der Offset in Byte angegeben wird, anstatt dass die Position vom Eintrag angegeben wird, können Zeichenketten-Teile auch doppelt verwendet werden.

Es gibt beispielsweise in manchen Fällen die Situation, dass 2 Inhalte von Unterabschnitten jeweils 1 eigenen Name bekommen sollen. Bei diesen Namen gibt es allerdings eine Überschneidung:
Wenn
  • nun die Tabelle für Zeichenketten lediglich mit
    • dem ersten unbenutzten Byte,
    • der Zeichenkette ".rel.text" und
    • dem Abschlusszeichen von der Zeichenkette
    gefüllt wird,
dann
  • sieht der Inhalt von der Tabelle für Zeichenketten wie folgt aus:
    Offset vom Anfang von der Tabelle bis zum Anfang von der Zeichenkette in Byte: 0 1 2 3 4 5 6 7 8 9 10
    Inhalt von der Speicherzelle als Zeichen: \0 . r e l . t e x t \0

  • Mit dem Offset "1" kann also die Zeichenkette ".rel.text" angegeben werden und
  • mit dem Offset "5" kann die Zeichenkette ".text" angegeben werden.

Der Teil von der Zeichenkette, welcher doppelt verwendet wird, ist in der Tabelle gelb hinterlegt.

  • Der Vorteil ist, dass ein paar Bytes gespart werden,
  • die Nachteile sind, dass
    • der Implementierungsaufwand für die Software, welche diese Tabelle füllt, ansteigen kann,
    • der Code von der Software größer und komplexer werden kann und
    • der Wartungsaufwand vom geschriebenen Code ansteigen kann.

Es gibt nur sehr wenige Situationen, in welchen Teile von Zeichenketten doppelt verwendet werden können.

Bezeichnung
Die Tabelle für Zeichenketten wird auch "Zeichenketten-Tabelle" (im Englischen: "string table") genannt.


ein "Hallo Welt!"-Programm

Beschreibung

Im Nachfolgenden wird Schritt für Schritt gezeigt, wie mit der Hilfe von eine ausführbare Datei geschrieben werden kann, welche nach dem Aufrufen "Hallo Welt!" ausgibt.

Die vollständige Datei, welche im Lauf von den folgenden Schritten entstehen wird, kann allerdings auch im Kapitel "weiteres Material zu diesem Thema - Programme" heruntergeladen werden.

notwendige Voraussetzungen

Sie benötigen ein Möglichkeit, mit deren Hilfe Rohdaten in eine Datei geschrieben werden können. Also auch solche "Zeichen", welche sich nicht direkt mit der Tastatur eingeben lassen. Dies kann beispielsweise

Vorbereitung

die Datei anlegen

Ich hatte das "Hallo Welt!"-Programm in Windows geschrieben und dann in Linux getestet. Wenn
  • Sie von Anfang an Linux verwenden,
dann
  • können Sie vermutlich in Linux ähnlich vorgehen und für die Windows-spezifischen Schritte in dieser Schritt-für-Schritt-Anweisung selbst geeignete Lösungen finden, welche sich in Linux umsetzen lassen.

Als erstes hatte ich eine neue Textdatei auf meinem Desktop angelegt. Diese hatte ich dann von "Neu Textdokument.txt" in "Hallo_Welt" umbenannt.

Wenn
  • Sie ebenfalls so vorgehen wollen, aber Ihnen als Dateiname "Neu Textdokument" anstatt "Neu Textdokument.txt" angezeigt wird,
dann
  • zeigt Ihnen Windows die Dateinamens-Endung ".txt" nicht an.

Diese Funktion gibt es, damit man nicht versehentlich die Dateinamens-Endung
  • ändert oder
  • löscht,
wenn man den Dateinamen ändert. In unserem Fall soll die Dateinamens-Endung allerdings gelöscht werden.

Damit Windows Ihnen die Dateinamens-Endung anzeigt, können Sie in "Windows experience" ("Windows xp") wie folgt vorgehen:
  1. Öffnen Sie den Arbeitsplatz und damit den Windows Explorer.
  2. Klicken Sie in der Menüleiste auf "Extras".
  3. Klicken Sie in dem Menü, welches sich eben geöffnet hat, auf "Ordneroptionen...".
  4. Klicken Sie in dem Fenster, welches sich eben geöffnet hat, auf den Reiter "Ansicht".
  5. Nehmen Sie den Haken von der Option "Erweiterungen bei bekannten Dateitypen ausblenden" weg.
  6. Klicken Sie unten auf den Knopf "Übernehmen".
  7. Klicken Sie oben auf den Knopf "Für alle übernehmen" und bestätigen Sie das aufspringende Fenster mit dem Knopf "Ja".
  8. Klicken Sie unten auf den Knopf "OK".

Nun sollte Windows auch bei Dateien auf Ihrem Desktop die Dateinamens-Endung anzeigen, sodass Sie die Änderung vornehmen können.

die Datei öffnen

Die Datei kann jetzt zur Bearbeitung mit
  • dem Hex-Editor oder
  • dem Text-Editor
geöffnet werden.

Anmerkungen zum Speichern

Wenn dann
Die Zeichenkodierung "ISO 8859-1" hingegen speichert jeden Wert zwischen 0 und 255 als 1 Byte ab. Um Rohdaten zu erzeugen, würden wir also eine Zeichenkodierung von dieser Art verwenden können.

Ich hingegen werde im Folgenden den Hex-Editor "Binary to Hex Editor" verwenden, welcher im Kapitel "weiteres Material zu diesem Thema - Programme" aufgelistet ist.

In einem Hex-Editor kann man für jedes Byte direkt den Wert als Zahl in der hexadezimal-Schreibweise angeben. Der Hex-Editor speichert dann die einzelnen Werte als einzelne Bytes ab, ohne dass dabei eine Zeichenkodierung angewand wird.

Damit erledigt sich das Problem mit dem Fehler durch eine ungeeignete Zeichenkodierung. Die einzelnen Werte sind ja ansich ohnehin nicht als "Zeichen" zu verstehen.

elf-Zusatzdaten

den Identifikations-Abschnitt schreiben

Allgemeines

Sie können sich gegebenenfalls den Aufbau vom Identifikations-Abschnitt nocheinmal anschauen, um leichter nachvollziehen zu können, wofür die folgenden Angaben dienen.

Ich werde dieses Programm für eine "32 Bit"-Version von der x86-CPU-Architektur schreiben. Daher werde ich in diesem Abschnitt folgende Werte verwenden:
Feld-Bezeichnung: Hexadezimalwert
2560: 2561: 2562: 2563: 2564: 2565: 2566:
EI_MAG0 7F
EI_MAG1 45
EI_MAG2 4C
EI_MAG3 46
EI_CLASS 01
EI_DATA 01
EI_VERSION 01
EI_OSABI 03
EI_ABIVERSION 00
EI_PAD 00 00 00 00 00 00 00

Um diese Werte in den Hex-Editor übernehmen zu können, müssen Sie in diesem Format eingetragen werden:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00

Um die Datei anschließend zu speichern, gehen Sie wie folgt vor:
  1. Klicken Sie in der Menüleiste auf "File".
  2. Klicken Sie in dem Menü, welches sich eben geöffnet hat, auf "Save Hex as Binary File".

Wenn
  • Sie die Werte in einem anderen Format eintragen,
dann
  • kann das Programm beim Speichern abstürzen.

Änderung

Schreiben Sie die folgenden Werte in die "Hallo_Welt"-Datei:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00 rot hinterlegt: die Werte für die Felder "EI_MAG0" bis "EI_MAG3"
gelb hinterlegt: der Wert für das Feld "EI_CLASS"
grün hinterlegt: der Wert für das Feld "EI_DATA"
blau hinterlegt: der Wert für das Feld "EI_VERSION"
lila hinterlegt: der Wert für das Feld "EI_OSABI"
rot hinterlegt: der Wert für das Feld "EI_ABIVERSION"
gelb hinterlegt: der Wert für das Feld "EI_PAD"

Ergebnis

Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00

Die Dateigröße von der "Hallo_Welt"-Datei müsste nun exakt 16 Byte betragen.

Größe

   4 Byte für die Felder "EI_MAG0" bis "EI_MAG3"
+  1 Byte für das Feld "EI_CLASS"
+  1 Byte für das Feld "EI_DATA"
+  1 Byte für das Feld "EI_VERSION"
+  1 Byte für das Feld "EI_OSABI"
+  1 Byte für das Feld "EI_ABIVERSION"
+  7 Byte für das Feld "EI_PAD"
= 16 Byte für den kompletten Identifikations-Abschnitt

den Kopfzeilen-Abschnitt schreiben

Allgemeines

In diesem Abschnitt werde ich die folgenden Werte verwenden:
Beschreibung: Feld-Bezeichnung: Hexadezimalwert
2560: 2561: 2562: 2563:
ausführbare Datei e_type 02 00
32 Bit x86-Architektur von Intel Corporation e_machine 03 00
e_version 01 00 00 00
  134.512.640 Byte (gewünschte Basisadresse)
+          16 Byte (der Abschnitt "Identifikation")
+          36 Byte (der Abschnitt "Kopfzeile")
+          64 Byte (die Kopfzeilen-Tabelle für Hauptabschnitte mit ihren 2 "32 Byte"-großen Einträgen)
= 134.512.756 Byte
e_entry 74 80 04 08
   0 Byte (Anfang von der Datei)
  16 Byte (der Abschnitt "Identifikation")
+ 36 Byte (der Abschnitt "Kopfzeile")
= 52 Byte
e_phoff 34 00 00 00
Es ist nicht notwendig, Linux alle Details vom exakten Aufbau vom Programm mitzuteilen. Ich verzichte daher vollständig auf die Angabe von Unterabschnitten. e_shoff 00 00 00 00
e_flags 00 00 00 00
  16 Byte (Größe vom Abschnitt "Identifikation")
+ 36 Byte (Größe vom Abschnitt "Kopfzeile")
= 52 Byte
e_ehsize 34 00
e_phentsize 20 00
  1 Hauptabschnitt für den Anfang von der Datei und den Maschinencode
+ 1 Hauptabschnitt für die Daten (diese Daten sind lediglich die "Hallo Welt!"-Zeichenkette)
= 2 Hauptabschnitte
e_phnum 02 00
Ich benutze keine Unterabschnitte, daher ist die Angabe überflüssig. e_shentsize 00 00
Anzahl der Unterabschnitte: 0 e_shnum 00 00
e_shstrndx 00 00

Änderung

Erweitern Sie die Datei um die folgenden Werte:
00000010: 02 00 03 00 01 00 00 00 - 74 80 04 08 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 02 00 00 00
00000030: 00 00 00 00
rot hinterlegt: der Wert für das Feld "e_type"
gelb hinterlegt: der Wert für das Feld "e_machine"
grün hinterlegt: der Wert für das Feld "e_version"
blau hinterlegt: der Wert für das Feld "e_entry"
lila hinterlegt: der Wert für das Feld "e_phoff"
rot hinterlegt: der Wert für das Feld "e_shoff"
gelb hinterlegt: der Wert für das Feld "e_flags"
grün hinterlegt: der Wert für das Feld "e_ehsize"
blau hinterlegt: der Wert für das Feld "e_phentsize"
lila hinterlegt: der Wert für das Feld "e_phnum"
rot hinterlegt: der Wert für das Feld "e_shentsize"
gelb hinterlegt: der Wert für das Feld "e_shnum"
grün hinterlegt: der Wert für das Feld "e_shstrndx"

Ergebnis

Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 02 00 03 00 01 00 00 00 - 74 80 04 08 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 02 00 00 00
00000030: 00 00 00 00

Nach dem Speichern müsste die Datei nun "52 Byte"-groß sein.

Größe

   2 Byte für das Feld "e_type"
+  2 Byte für das Feld "e_machine"
+  4 Byte für das Feld "e_version"
+  4 Byte für das Feld "e_entry"
+  4 Byte für das Feld "e_phoff"
+  4 Byte für das Feld "e_shoff"
+  4 Byte für das Feld "e_flags"
+  2 Byte für das Feld "e_ehsize"
+  2 Byte für das Feld "e_phentsize"
+  2 Byte für das Feld "e_phnum"
+  2 Byte für das Feld "e_shentsize"
+  2 Byte für das Feld "e_shnum"
+  2 Byte für das Feld "e_shstrndx"
= 36 Byte für den kompletten Kopfzeilen-Abschnitt

Kopfzeilen-Tabelle für Hauptabschnitte

Die Kopfzeilen-Tabelle für Hauptabschnitte wird mit den folgenden Kopfzeilen gefüllt:
  1. Eine Kopfzeile, welche Auskünfte über den Haupt-Programmabschnitt mit dem Anfang von der Datei und dem Maschinencode macht.
  2. Eine Kopfzeile, welche Auskünfte über den Haupt-Programmabschnitt mit den Daten macht.

die Kopfzeile für den Haupt-Programmabschnitt mit dem Anfang von der Datei und dem Maschinencode schreiben

Allgemeines
Mit der Hilfe von diesem Schritt wird die erste Kopfzeile definiert. Für diese werde ich die folgenden Werte verwenden:
Beschreibung: Feld-Bezeichnung: Hexadezimalwert:
2560: 2561: 2562: 2563:
p_type 01 00 00 00
  0 Byte (Anfang von der Datei)
= 0 Byte
p_offset 00 00 00 00
  134.512.640 Byte (gewünschte Basisadresse)
= 134.512.640 Byte
p_vaddr 00 80 04 08
p_paddr 00 00 00 00
   16 Byte (Größe vom Abschnitt "Identifikation")
+  36 Byte (Größe vom Abschnitt "Kopfzeile")
+  64 Byte (Größe von der Kopfzeilen-Tabelle für Hauptabschnitte mit ihren 2 "32 Byte"-großen Einträgen)
+  31 Byte (Größe vom Maschinencode)
+  13 Byte (Größe vom unbenutzten Zwischenraum*)
= 160 Byte

* = Der unbenutzte Zwischenraum dient dafür, damit die Anfangsadresse von den Daten restlos durch 32 teilbar ist. Dies ist in unserem Fall nicht notwendig. Es ist allerdings prinzipiell nützlich, wenn man sich beim Programmieren darauf verlassen kann, dass das Link-Programm die Daten auf eine restlos durch 32 Byte teilbare Adresse ausrichtet, sodass man nicht bei jedem Projekt prüfen muss, ob die Ausrichtung von den Daten den Anforderungen genügt.
p_filesz A0 00 00 00
Dadurch, dass kein größerer Wert angegeben ist, wie beim Feld "p_filesz" verwendet wird, setzt das Lade-Programm von keinen weiteren Bytes alle Bits auf "0|b".

Einen unbenutzten Zwischenraum hinter Maschinencode mit Null-Bytes aufzufüllen ist bei der x86-Architektur ohnehin nicht sonderlich geschickt, da der Befehl "00 00|h" "2 Byte"-groß ist. Besser wäre es, unbenutzte Bytes mit dem Wert "90|h" aufzufüllen. Der Befehl "90|h" - das ist einer von den "no operation"-Befehlen - ist "1 Byte"-groß und kann somit auch benutzt werden, wenn sich die Größe von einem unbenutzten Bereich
  • nicht restlos durch 2|d,
  • sondern lediglich restlos durch 1|d
teilen lässt.

Bedeutung: Maschinensprache:
# zur "1 Byte"-großen Speicherstelle im Arbeitsspeicher, dessen Adresse mit der Hilfe vom Register "eax (#0)" angegeben wird, den Wert aus dem Register "al (#0)" hinzuaddieren
RAM[eax (#0)] += al (#0)
00 00
# den Wert aus dem "4 Byte"-großen Register "eax (#0)" mit dem Wert aus dem "4 Byte"-großen Register "eax (#0)" tauschen
eax (#0) <-> eax (#0)
90
p_memsz A0 00 00 00
  • Der Anfang von der Datei muss lesbar sein. Aus diesem Grund ist die Flagge "PF_R" mit der Wertigkeit "22|d = 4|d" auf "1|b" gesetzt.
  • Der Maschinencode muss ausführbar sein. Aus diesem Grund ist die Flagge "PF_X" mit der Wertigkeit "20|d = 1|d" auf "1|b" gesetzt.
p_flags 05 00 00 00
1 RAM-Seite p_align 00 10 00 00

Änderung
Erweitern Sie die Datei um die folgenden Werte:
                      01 00 00 00 - 00 00 00 00 00 80 04 08
00000040: 00 00 00 00 A0 00 00 00 - A0 00 00 00 05 00 00 00
00000050: 00 10 00 00
rot hinterlegt: der Wert vom Feld "p_type"
gelb hinterlegt: der Wert vom Feld "p_offset"
grün hinterlegt: der Wert vom Feld "p_vaddr"
blau hinterlegt: der Wert vom Feld "p_paddr"
lila hinterlegt: der Wert vom Feld "p_filesz"
rot hinterlegt: der Wert vom Feld "p_memsz"
gelb hinterlegt: der Wert vom Feld "p_flags"
grün hinterlegt: der Wert vom Feld "p_align"

Ergebnis
Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 02 00 03 00 01 00 00 00 - 74 80 04 08 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 02 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 80 04 08
00000040: 00 00 00 00 A0 00 00 00 - A0 00 00 00 05 00 00 00
00000050: 00 10 00 00

Nach dem Speichern müsste die Datei nun "84 Byte"-groß sein.

Größe
   4 Byte für das Feld "p_type"
+  4 Byte für das Feld "p_offset"
+  4 Byte für das Feld "p_vaddr"
+  4 Byte für das Feld "p_paddr"
+  4 Byte für das Feld "p_filesz"
+  4 Byte für das Feld "p_memsz"
+  4 Byte für das Feld "p_flags"
+  4 Byte für das Feld "p_align"
= 32 Byte für die komplette Kopfzeile

die Kopfzeile für den Haupt-Programmabschnitt mit den Daten schreiben

Allgemeines
Mit der Hilfe von diesem Schritt wird die zweite Kopfzeile definiert. Für diese werde ich die folgenden Werte verwenden:
Beschreibung: Feld-Bezeichnung: Hexadezimalwert:
2560: 2561: 2562: 2563:
p_type 01 00 00 00
    0 Byte (Anfang von der Datei)
+  16 Byte (der Abschnitt "Identifikation")
+  36 Byte (der Abschnitt "Kopfzeile")
+  64 Byte (die Kopfzeilen-Tabelle für Hauptabschnitte mit ihren 2 "32 Byte"-großen Einträgen)
+  31 Byte (der Maschinencode)
+  13 Byte (der unbenutzte Zwischenraum)
= 160 Byte
p_offset A0 00 00 00
  134.512.640 Byte (gewünschte Basisadresse)
+         160 Byte (Anfangsadresse in der Datei; siehe: das Feld "p_offset")
+       4.096 Byte (Größe vom einer RAM-Seite*)
= 134.516.896 Byte

* = Dieser Werte dient dafür, um die Platzierung in einer anderen RAM-Seite zu beginnen, als der Inhalt vom ersten Hauptabschnitt platziert wurde.
p_vaddr A0 90 04 08
p_paddr 00 00 00 00
  11 Byte (Größe von der Zeichenkette "Hallo Welt!")
= 11 Byte
p_filesz 0B 00 00 00
Dadurch, dass kein größerer Wert angegeben ist, wie beim Feld "p_filesz" verwendet wird, setzt das Lade-Programm von keinen weiteren Bytes alle Bits auf "0|b". p_memsz 0B 00 00 00
  • Die Daten müssen lesbar sein. Aus diesem Grund ist die Flagge "PF_R" mit der Wertigkeit "22|d = 4|d" auf "1|b" gesetzt.
  • Die Daten sollen beschreibbar sein. Aus diesem Grund ist die Flagge "PF_W" mit der Wertigkeit "21|d = 2|d" auf "1|b" gesetzt.

    Für die Zeichenkette "Hallo Welt!" ist die Schreibberechtigung nicht notwendig. Es ist allerdings universeller, für die Daten/Variablen eine Schreibberechtigung zu geben. So muss man nicht bei jeder Datei/jedem Projekt prüfen, ob die Daten/Variablen beschreibbar sein müssen.
p_flags 06 00 00 00
1 RAM-Seite p_align 00 10 00 00

Änderung
Erweitern Sie die Datei um die folgenden Werte:
                      01 00 00 00 - A0 00 00 00 A0 90 04 08
00000060: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000070: 00 10 00 00
rot hinterlegt: der Wert vom Feld "p_type"
gelb hinterlegt: der Wert vom Feld "p_offset"
grün hinterlegt: der Wert vom Feld "p_vaddr"
blau hinterlegt: der Wert vom Feld "p_paddr"
lila hinterlegt: der Wert vom Feld "p_filesz"
rot hinterlegt: der Wert vom Feld "p_memsz"
gelb hinterlegt: der Wert vom Feld "p_flags"
grün hinterlegt: der Wert vom Feld "p_align"

Ergebnis
Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 02 00 03 00 01 00 00 00 - 74 80 04 08 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 02 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 80 04 08
00000040: 00 00 00 00 A0 00 00 00 - A0 00 00 00 05 00 00 00
00000050: 00 10 00 00 01 00 00 00 - A0 00 00 00 A0 90 04 08
00000060: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000070: 00 10 00 00

Nach dem Speichern müsste die Datei nun "116 Byte"-groß sein.

Größe
siehe: hier

Übersicht

Das waren soweit die kompletten elf-Zusatzdaten:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 02 00 03 00 01 00 00 00 - 00 81 04 08 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 02 00 28 00
00000030: 00 00 00 00 01 00 00 00 - 00 01 00 00 00 81 04 08
00000040: 00 00 00 00 00 01 00 00 - 00 01 00 00 05 00 00 00
00000050: 00 10 00 00 01 00 00 00 - 00 02 00 00 00 82 04 08
00000060: 00 00 00 00 00 01 00 00 - 00 01 00 00 06 00 00 00
00000070: 00 10 00 00
rot hinterlegt: der Abschnitt "Identifikation"
gelb hinterlegt: der Abschnitt "Kopfzeile"
grün hinterlegt: die Kopfzeile für den Haupt-Programmabschnitt mit dem Anfang von der Datei und dem Maschinencode
blau hinterlegt: die Kopfzeile für den Haupt-Programmabschnitt mit den Daten

der Maschinencode

In diesem Teil ist
  1. zum Einen der Maschinencode, um die "Hallo Welt!"-Zeichenkette auszugeben, gespeichert und
  2. zum Anderen der Maschinencode, um das Programm ordnungsgemäß zu beenden, gespeichert.
  3. Das Ende ist mit Bytes aufgefüllt, welche alle jeweils den Wert "90|h" haben.

die Funktion "write"

die Funktions-Identifikationskennung definieren

Allgemeines
Die nächsten Schritte befassen sich mit der Ausgabe von der Zeichenkette.

Um eine Zeichenkette auszugeben, kann in Linux eine Zeichenkette in den Ausgabe-Datenkanal von einer Konsole übertragen werden. Damit wird die Zeichenkette in der Konsole angezeigt.

Die Funktion "write" kann für das Übertragen verwendet werden.

Im Gegensatz zu Windows ist die Funktion kein Bestandteil von einer Bibliothek, welche zuerst importiert werden muss. Sie ist stattdessen ein Bestandteil vom Kernel.

Die Kommunikation mit dem Kernel läuft in Linux anderst ab, als die Kommunikation mit einer Bibliothek:
Damit der Aufrufer eine Funktion vom Kernel aufrufen kann, muss er die folgenden Schritte durchführen:
  • die Funktions-Identifikationskennung in das Register "extended accumulator" ("eax") speichern
  • die Parameterwerte in die dafür vorgesehenen in Register speichern
  • zuletzt die Verarbeitungsunterbrechung (im Englischen: "interrupt") mit der Nummer "128|d = 80|h" auslösen

Mit der Hilfe von der Verarbeitungsunterbrechung erhält das Betriebssystem die Kontrolle über den CPU und damit auch die Anweisung, die angegebene Funktion zu durchlaufen. Anschließend erhält der Aufrufer die Kontrolle über den CPU zurück.

Wenn
  • der Aufrufer stattdessen eine Funktion aus einer Bibliothek aufrufen wollte,
dann
  • müsste er die folgenden Schritte durchführen:
    1. die Parameterwerte auf den Stapel legen
    2. die Rücksprungadresse auf den Stapel laden
    3. zur Funktion springen

Mit der Hilfe vom Sprung würde die Funktion von der Bibliothek die Kontrolle über den CPU erhalten. Nachdem die Funktion durchlaufen ist, würde durch den Rücksprung der Aufrufer wieder die Kontrolle über den CPU zurückerhalten.

Im Kapitel "weiteres Material zu diesem Thema - Dokumente" ist ein Dokument aufgelistet, welches die Funktionen vom Betriebssystem "Linux" beschreibt. In diesem Dokument ist auch enthalten, dass die Funktion "write" Werte für einige Parameter fordert:
write(
    Ziel_-_Konsole_-_Datenkanal_-_Identifikationskennung,
    Quelle_-_Nutzdaten_-_Adresse,
    Quelle_-_Nutzdaten_-_Länge_in_Byte
)

Damit Funktionen vom Kernel korrekt funktionieren, muss
  • der Wert vom 1. Parameter im Register "extended base" ("ebx") gespeichert sein,
  • der Wert vom 2. Parameter im Register "extended counter" ("ecx") gespeichert sein,
  • der Wert vom 3. Parameter im Register "extended data" ("edx") gespeichert sein,
  • der Wert vom 4. Parameter im Register "extended source index" ("esi") gespeichert sein,
  • der Wert vom 5. Parameter im Register "extended destination index" ("edi") gespeichert sein und
  • der Wert vom 6. Parameter im Register "extended base pointer" ("ebp") gespeichert sein.

In unserem Fall gibt es allerdings nur 3 Parameter. Die Werte von den Parametern "4" bis "6" und deren Register werden also nicht ausgewertet.

Im Kapitel "weiteres Material zu diesem Thema - Dokumente" ist auch ein Dokument aufgelistet, welches die Maschinensprache von der x86-CPU-Architektur beschreibt und somit zeigt, wie ein Wert in ein Register gespeichert werden kann.

Ich werde im Nachfolgenden die Funktions-Identifikationskennung ins Register
  • "extended accumulator" ("eax")
speichern.

Die Funktions-Identifikationskennungen können im Dokument über die Funktionen vom Betriebssystem "Linux" nachgeschaut werden.

Änderung
Erweitern Sie die Datei um die folgenden Werte:
                      B8 04 00 00 - 00 rot hinterlegt: die Befehlsnummer vom Befehl "move"
gelb hinterlegt: der Wert

Ergebnis
Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 02 00 03 00 01 00 00 00 - 74 80 04 08 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 02 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 80 04 08
00000040: 00 00 00 00 A0 00 00 00 - A0 00 00 00 05 00 00 00
00000050: 00 10 00 00 01 00 00 00 - A0 00 00 00 A0 90 04 08
00000060: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000070: 00 10 00 00 B8 04 00 00 - 00

Nach dem Speichern müsste die Datei nun "121 Byte"-groß sein.

Größe
  1 Byte für die Befehlsnummer vom Befehl "move"
+ 4 Byte für den Wert
= 5 Byte für den kompletten Maschinenbefehl

den Wert vom Parameter "Ziel_-_Konsole_-_Datenkanal_-_Identifikationskennung" definieren

Allgemeines
Mit der Hilfe von diesem Schritt wird der Wert vom Parameter "Ziel_-_Konsole_-_Datenkanal_-_Identifikationskennung" definiert.

In Linux hat eine Konsole 3 Datenkanäle:
  • Einen Datenkanal um Daten auszugeben.

    Dieser wird normalerweise von einem Programm beschrieben.
  • Einen Datenkanal um Daten einzulesen.

    Dieser wird normalerweise vom Benutzer durch eine Tastatureingabe beschrieben und von einem Programm ausgelesen. Er kann allerdings auch von einem Programm beschrieben werden und von einem anderen Programm ausgelesen werden.
  • Einen Datenkanal um Fehlermeldungen auszugeben.

Diese Datenkanäle habe jeweils eine eigene Identifikationskennung, mit dessen Hilfe sie identifiziert werden. Der Ausgabe-Datenkanal hat die Identifikationskennung "1|d".

Mit der Hilfe von diesem Schritt erhält also der Parameter "Ziel_-_Konsole_-_Datenkanal_-_Identifikationskennung" den Wert "1|d".

Änderung
Erweitern Sie die Datei um die folgenden Werte:
                                       BB 01 00 00 00 rot hinterlegt: die Befehlsnummer vom Befehl "move"
gelb hinterlegt: der Wert

Ergebnis
Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 02 00 03 00 01 00 00 00 - 74 80 04 08 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 02 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 80 04 08
00000040: 00 00 00 00 A0 00 00 00 - A0 00 00 00 05 00 00 00
00000050: 00 10 00 00 01 00 00 00 - A0 00 00 00 A0 90 04 08
00000060: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000070: 00 10 00 00 B8 04 00 00 - 00 BB 01 00 00 00

Nach dem Speichern müsste die Datei nun "126 Byte"-groß sein.

Größe
siehe: hier

den Wert vom Parameter "Quelle_-_Nutzdaten_-_Adresse" definieren

Allgemeines
Mit der Hilfe von diesem Schritt wird der Wert vom Parameter "Quelle_-_Nutzdaten_-_Adresse" definiert.

Der Wert ist eine Adresse und gibt eine Speicherstelle im Segment vom "Hallo Welt!"-Programm an. Die Adresse ist ein Offset vom Segmentanfang. An der angegebenen Speicherstelle beginnt die "Hallo Welt!"-Zeichenkette.

Der Wert von diesem Parameter stimmt mit dem Wert vom Feld "p_vaddr" von der Kopfzeile für den Inhalt vom Hauptabschnitt mit den Daten überein. Es handelt sich also um den Wert "134.516.896|d".
Die von mir gewünschte Basisadresse wird auch die vom Lade-Programm gewählte Basisadresse sein. Aus diesem Grund wird keine Adressen-Reparatur notwendig sein.

Änderung
Erweitern Sie die Datei um die folgenden Werte:
                                                      B9 A0
00000080: 90 04 08
rot hinterlegt: die Befehlsnummer vom Befehl "move"
gelb hinterlegt: der Wert

Ergebnis
Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 02 00 03 00 01 00 00 00 - 74 80 04 08 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 02 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 80 04 08
00000040: 00 00 00 00 A0 00 00 00 - A0 00 00 00 05 00 00 00
00000050: 00 10 00 00 01 00 00 00 - A0 00 00 00 A0 90 04 08
00000060: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000070: 00 10 00 00 B8 04 00 00 - 00 BB 01 00 00 00 B9 A0
00000080: 90 04 08

Nach dem Speichern müsste die Datei nun "131 Byte"-groß sein.

Größe
siehe: hier

den Wert vom Parameter "Quelle_-_Nutzdaten_-_Länge_in_Byte" definieren

Allgemeines
Mit der Hilfe von diesem Schritt wird der Wert vom Parameter "Quelle_-_Nutzdaten_-_Länge_in_Byte" definiert.

Der Wert gibt die Länge von der Zeichenkette in Byte an, welche in die Konsole übertragen werden soll.

Es handelt sich vermutlich
  • entweder um eine "ASCII"-kodierte Zeichenkette, von welcher jedes Zeichen "1 Byte"-groß ist,
  • oder um eine "ISO 8859-1"-kodierte Zeichenkette, von welcher jedes Zeichen "1 Byte"-groß ist,
  • oder um eine "UTF-8"-kodierte Zeichenkette, von welcher die einzelnen Zeichen unterschiedlich groß sein können.

Welche Zeichenkodierung Linux erwartet, hängt von
  • der verwendeten Distribution und
  • den Einstellungen, welche der Benutzer bei manchen Distributionen machen kann,
ab.

Bei der Zeichenkette "Hallo Welt!" spielt es allerdings keine Rolle, welche von diesen Zeichenkodierung Linux erwartet, da diese Zeichen in den genannten Zeichenkodierungen gleich kodiert werden.

Da die Länge angegeben wird, ist es nicht notwendig, die Zeichenkette "Hallo Welt!" mit der Hilfe von einem Byte abzuschließen, von welchem alle Bits auf "0|b" gesetzt sind.

Änderung
Erweitern Sie die Datei um die folgenden Werte:
                   BA 0B 00 00 00 rot hinterlegt: die Befehlsnummer vom Befehl "move"
gelb hinterlegt: der Wert

Ergebnis
Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 02 00 03 00 01 00 00 00 - 74 80 04 08 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 02 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 80 04 08
00000040: 00 00 00 00 A0 00 00 00 - A0 00 00 00 05 00 00 00
00000050: 00 10 00 00 01 00 00 00 - A0 00 00 00 A0 90 04 08
00000060: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000070: 00 10 00 00 B8 04 00 00 - 00 BB 01 00 00 00 B9 A0
00000080: 90 04 08 BA 0B 00 00 00

Nach dem Speichern müsste die Datei nun "136 Byte"-groß sein.

Größe
siehe: hier

die Verarbeitungsunterbrechung auslösen

Allgemeines
Da nun die Funktions-Identifikationskennung und alle Parameterwerte definiert sind, kann die Verarbeitungsunterbrechung ausgelöst werden.

Die x86-CPU-Architektur wird dadurch die Kontrolle über den CPU der Verarbeitungsunterbrechungs-Routine übergeben.

Diese Routine wird vom Kernel bereitgestellt. Sie prüft anhand von der Funktions-Identifikationskennung, welche Funktion durchlaufen werden soll und durchläuft diese dann.

Anschließend wird die Kontrolle über den CPU wieder an den Aufrufer, also das "Hallo Welt!"-Programm, übergeben.

Nachdem der Aufrufer wieder die Kontrolle über den CPU hat, ist im Register
  • "extended accumulator" ("eax")
nicht mehr die Funktions-Identifikationskennung gespeichert, sondern der Rückgabewert von der Funktion "write".

Der Rückgabewert gibt an, wieviele Byte tatsächlich in die Konsole übertragen wurden. Da ich davon ausgehe, dass die Zeichenkette mit der Hilfe von 1|d Funktionsaufruf vollständig in die Konsole übertragen werden kann, wertet dieses "Hallo Welt!"-Programm den Rückgabewert nicht aus.

Änderung
Erweitern Sie die Datei um die folgenden Werte:
                                    CD 80 rot hinterlegt: die Befehlsnummer vom Befehl "call to interrupt procedure"
gelb hinterlegt: der Wert

Ergebnis
Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 02 00 03 00 01 00 00 00 - 74 80 04 08 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 02 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 80 04 08
00000040: 00 00 00 00 A0 00 00 00 - A0 00 00 00 05 00 00 00
00000050: 00 10 00 00 01 00 00 00 - A0 00 00 00 A0 90 04 08
00000060: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000070: 00 10 00 00 B8 04 00 00 - 00 BB 01 00 00 00 B9 A0
00000080: 90 04 08 BA 0B 00 00 00 - CD 80

Nach dem Speichern müsste die Datei nun "138 Byte"-groß sein.

Größe
  1 Byte für die Befehlsnummer vom Befehl "call to interrupt procedure"
+ 1 Byte für den Wert
= 2 Byte für den kompletten Maschinenbefehl

die Funktion "exit"

die Funktions-Identifikationskennung definieren

Allgemeines
Die nächsten Schritte befassen sich mit dem ordnungsgemäßen Beenden vom Programm.

Wenn
  • das Programm nicht ordnungsgemäß beendet wird,
dann
Wenn dann
  • würde auch dieser als Maschinencode verstanden werden und ausgeführt werden.

Es ist also nicht zwangsweise notwendig, das Programm ordnungsgemäß zu beenden, aber es hat eher noch einen Nutzen, als es in Windows der Fall ist, da in Linux beim Abstürzen eine Fehlermeldung in der Konsole ausgegeben wird.

Das Programm würde in diesem Fall zu folgender Ausgabe in der Konsole führen:
"Hallo Welt!Segmentation fault"

Die Funktion "exit" hat die Funktions-Identifikationskennung "1|d" und fordert einen Wert für 1|d Parameter:
exit(
    Ziel_-_Exitcode
)

Änderung
Erweitern Sie die Datei um die folgenden Werte:
                                          B8 01 00 00 00 rot hinterlegt: die Befehlsnummer vom Befehl "move"
gelb hinterlegt: der Wert

Ergebnis
Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 02 00 03 00 01 00 00 00 - 74 80 04 08 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 02 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 80 04 08
00000040: 00 00 00 00 A0 00 00 00 - A0 00 00 00 05 00 00 00
00000050: 00 10 00 00 01 00 00 00 - A0 00 00 00 A0 90 04 08
00000060: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000070: 00 10 00 00 B8 04 00 00 - 00 BB 01 00 00 00 B9 A0
00000080: 90 04 08 BA 0B 00 00 00 - CD 80 B8 01 00 00 00

Nach dem Speichern müsste die Datei nun "143 Byte"-groß sein.

Größe
siehe: hier

den Wert vom Parameter "Ziel_-_Exitcode" definieren

Allgemeines
Mit der Hilfe von diesem Schritt wird der Wert vom Parameter "Ziel_-_Exitcode" definiert.

Dieser Wert wird der Anwendung zurückgegeben, welche das "Hallo Welt!"-Programm gestartet hat. Dies ist also zum Beispiel
  • die Konsole, mit dessen Hilfe Sie später das "Hallo Welt!"-Programm aufrufen können, oder
  • ein Verzeichnis-Browser, in welchem Sie mit der Hilfe von einem Doppelklick oder sonst wie das Programm starten können.

In beiden Fällen erhält somit der Aufrufer eine Auskunft darüber, ob das Programm wegen einem Fehler beendet wurde.

Das "Hallo Welt!"-Programm hat allerdings seine Aufgabe vollständig und ohne Fehler ausgeführt, daher wird dies mit der Hilfe vom Wert "0|d" zurückgegeben. Dieser Wert bedeutet, dass kein Fehler aufgetreten ist.

Änderung
Erweitern Sie die Datei um die folgenden Werte:
                                                         31
00000090: DB
rot hinterlegt: die Befehlsnummer vom Befehl "exclusive or"
gelb hinterlegt: 2 mal der Parameter "extended base" ("ebx")

Ergebnis
Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 02 00 03 00 01 00 00 00 - 74 80 04 08 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 02 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 80 04 08
00000040: 00 00 00 00 A0 00 00 00 - A0 00 00 00 05 00 00 00
00000050: 00 10 00 00 01 00 00 00 - A0 00 00 00 A0 90 04 08
00000060: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000070: 00 10 00 00 B8 04 00 00 - 00 BB 01 00 00 00 B9 A0
00000080: 90 04 08 BA 0B 00 00 00 - CD 80 B8 01 00 00 00 31
00000090: DB

Nach dem Speichern müsste die Datei nun "145 Byte"-groß sein.

Größe
  1 Byte für die Befehlsnummer vom Befehl "exclusive or"
+ 1 Byte für angabe von den Parametern
= 2 Byte für den kompletten Maschinenbefehl "extended base (ebx) =xoder extended base (ebx)"

die Verarbeitungsunterbrechung auslösen

Allgemeines
Da nun die Funktions-Identifikationskennung und alle Parameterwerte definiert sind, kann die Verarbeitungsunterbrechung ausgelöst werden.

Nachdem die Funktion durchlaufen ist, erhält das "Hallo Welt!"-Programm nicht mehr die Kontrolle über den CPU zurück, sondern das Programm ist dann beendet.

Änderung
Erweitern Sie die Datei um die folgenden Werte:
             CD 80 rot hinterlegt: die Befehlsnummer vom Befehl "call to interrupt procedure"
gelb hinterlegt: der Wert

Ergebnis
Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 02 00 03 00 01 00 00 00 - 74 80 04 08 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 02 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 80 04 08
00000040: 00 00 00 00 A0 00 00 00 - A0 00 00 00 05 00 00 00
00000050: 00 10 00 00 01 00 00 00 - A0 00 00 00 A0 90 04 08
00000060: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000070: 00 10 00 00 B8 04 00 00 - 00 BB 01 00 00 00 B9 A0
00000080: 90 04 08 BA 0B 00 00 00 - CD 80 B8 01 00 00 00 31
00000090: DB CD 80

Nach dem Speichern müsste die Datei nun "147 Byte"-groß sein.

Größe
siehe: hier

den unbenutzten Zwischenraum füllen

Allgemeines

Da die Datei nun "147 Byte"-groß ist, wurden soweit die Bytes "0" bis "146" mit Werten gefüllt. Es wird allerdings erwartet, dass der Inhalt vom Hauptabschnitt mit dem Maschinencode "160 Byte"-groß ist und damit erst beim Byte "159" endet.

Aus diesem Grund muss ein Zwischenraum geschaffen werden, damit der Inhalt vom Hauptabschnitt mit dem Maschinencode auch tatsächlich erst beim Byte "159" endet und nicht schon beim Byte "146".

Ich werde allen Bytes vom Zwischenraum den Wert "90|h" zuweisen.

Änderung

Erweitern Sie die Datei um die folgenden Werte:
                   90 90 90 90 90 - 90 90 90 90 90 90 90 90 rot hinterlegt: der unbenutzte Zwischenraum

Ergebnis

Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 02 00 03 00 01 00 00 00 - 74 80 04 08 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 02 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 80 04 08
00000040: 00 00 00 00 A0 00 00 00 - A0 00 00 00 05 00 00 00
00000050: 00 10 00 00 01 00 00 00 - A0 00 00 00 A0 90 04 08
00000060: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000070: 00 10 00 00 B8 04 00 00 - 00 BB 01 00 00 00 B9 A0
00000080: 90 04 08 BA 0B 00 00 00 - CD 80 B8 01 00 00 00 31
00000090: DB CD 80 90 90 90 90 90 - 90 90 90 90 90 90 90 90

Nach dem Speichern müsste die Datei nun "160 Byte"-groß sein.

Größe

Der Zwischenraum füllt die Bytes "147" bis "159" und ist somit "13 Byte"-groß.

Übersicht

Dies war soweit der komplette Inhalt vom Hauptabschnitt mit dem Maschinencode:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 02 00 03 00 01 00 00 00 - 74 80 04 08 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 02 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 80 04 08
00000040: 00 00 00 00 A0 00 00 00 - A0 00 00 00 05 00 00 00
00000050: 00 10 00 00 01 00 00 00 - A0 00 00 00 A0 90 04 08
00000060: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000070: 00 10 00 00 B8 04 00 00 - 00 BB 01 00 00 00 B9 A0
00000080: 90 04 08 BA 0B 00 00 00 - CD 80 B8 01 00 00 00 31
00000090: DB CD 80 90 90 90 90 90 - 90 90 90 90 90 90 90 90
rot hinterlegt: write - die Funktions-Identifikationskennung definieren
gelb hinterlegt: write - den Wert vom Parameter "Ziel_-_Konsole_-_Datenkanal_-_Identifikationskennung" definieren
grün hinterlegt: write - den Wert vom Parameter "Quelle_-_Nutzdaten_-_Adresse" definieren
blau hinterlegt: write - den Wert vom Parameter "Quelle_-_Länge_in_Zeichen" definieren
lila hinterlegt: write - die Verarbeitungsunterbrechung auslösen
rot hinterlegt: exit - die Funktions-Identifikationskennung definieren
gelb hinterlegt: exit - den Wert vom Parameter "Ziel_-_Exitcode" definieren
grün hinterlegt: exit - die Verarbeitungsunterbrechung auslösen
blau hinterlegt: der unbenutzte Zwischenraum

die Daten

die "Hallo Welt!"-Zeichenkette einfügen

Allgemeines

Mit der Hilfe von diesem Schritt wird der einzige Inhalt in den Inhalt vom Hauptabschnitt mit den Daten eingefügt:
  1. "Hallo Welt!"-Zeichenkette

Die "Hallo Welt!"-Zeichenkette wird also direkt am Anfang platziert.

Diese Zeichenkette muss nicht mit der Hilfe von einem Abschlusszeichen abgeschlossen werden, von welchem alle Bits auf "0|b" gesetzt sind, da beim Aufruf von der "write"-Funktion als Parameter angegeben wurde, wie lang die Zeichenkette ist.

Änderung

Erweitern Sie die Datei um die folgenden Werte:
000000A0: 48 61 6C 6C 6F 20 57 65 - 6C 74 21 rot hinterlegt: die "Hallo Welt!"-Zeichenkette

Ergebnis

Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 02 00 03 00 01 00 00 00 - 74 80 04 08 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 02 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 80 04 08
00000040: 00 00 00 00 A0 00 00 00 - A0 00 00 00 05 00 00 00
00000050: 00 10 00 00 01 00 00 00 - A0 00 00 00 A0 90 04 08
00000060: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000070: 00 10 00 00 B8 04 00 00 - 00 BB 01 00 00 00 B9 A0
00000080: 90 04 08 BA 0B 00 00 00 - CD 80 B8 01 00 00 00 31
00000090: DB CD 80 90 90 90 90 90 - 90 90 90 90 90 90 90 90
000000A0: 48 61 6C 6C 6F 20 57 65 - 6C 74 21

Nach dem Speichern müsste die Datei nun "171 Byte"-groß sein.

Größe

Die Zeichenkette ist "11 Byte"-groß.

Übersicht

Dies war soweit der komplette Inhalt vom Hauptabschnitt mit den Daten und die komplette "Hallo_Welt"-Datei:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 02 00 03 00 01 00 00 00 - 74 80 04 08 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 02 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 80 04 08
00000040: 00 00 00 00 A0 00 00 00 - A0 00 00 00 05 00 00 00
00000050: 00 10 00 00 01 00 00 00 - A0 00 00 00 A0 90 04 08
00000060: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000070: 00 10 00 00 B8 04 00 00 - 00 BB 01 00 00 00 B9 A0
00000080: 90 04 08 BA 0B 00 00 00 - CD 80 B8 01 00 00 00 31
00000090: DB CD 80 90 90 90 90 90 - 90 90 90 90 90 90 90 90
000000A0: 48 61 6C 6C 6F 20 57 65 - 6C 74 21
rot hinterlegt: die "Hallo Welt!"-Zeichenkette

das Programm starten

Allgemeines

Sofern Sie die Datei ein letztes Mal gespeichert haben, dann ist sie nun bereit, um in Linux gestartet zu werden.

Ich hatte dazu eine vom USB-Stick bootbare Version von Linux verwendet.

In Linux hatte ich die Datei mit der Hilfe von einer Konsole aufgerufen.

Wenn
  • Sie ebenfalls so vorgehen wollen,
dann
  • helfen Ihnen vielleicht folgende Hinweise:
    • Wenn
      • Sie ins richtige Verzeichnis gewechselt haben und
        "Hallo_Welt"
        eingeben, um das Programm zu starten,
      dann
      • bekommen Sie möglicherweise ein Fehlermeldung angezeigt, dass die Datei nicht gefunden wurde.

      Wenn
      • Sie stattdessen
        "./Hallo_Welt"
        eingeben,
      dann
      • wird diese Fehlermeldung vermutlich verschwinden.
    • Wenn
      • die Datei nicht als ausführbare Datei erkannt wird,
      dann
      • können Sie mit der Eingabe
        "chmod +x Hallo_Welt"
        die Ausführbarkeit von der Datei im Dateisystem eintragen.

      Wenn
      • Sie durch diese Eingabe eine Fehlermeldung angezeigt bekommen sollten, dass Sie keine Berechtigung haben, diese Änderung am Dateisystem vorzunehmen,
      dann
      • können Sie folgende Eingabe ausprobieren:
        "sudo chmod +x Hallo_Welt"

      Das "sudo" bewirkt, dass der nachfolgende Befehl im Namen von einem sogenannten "Superuser"-Benutzerkonto ausgeführt wird.

      Ein "Superuser"-Benutzerkonto ist vergleichbar mit einem "Administrator"-Benutzerkonto in Windows.
    • Wenn
      • Sie die Datei mit einem Doppelklick starten sollten,
      dann
      • kann es sein, dass eine Konsole aufspringt, darin "Hallo Welt!" anzeigt, und sich dann gleich wieder schließt.

        Dies kann so schnell geschehen, dass es zwischen 2 Aktualisierungen von ihrem Bildschirm abläuft, und damit Ihnen die Meldung "Hallo Welt!" nie sichtbar wird.

      Wenn
      • Sie eine neue Konsole starten und
      • mit dieser das Programm aufrufen,
      dann
      • sollte sich die Konsole nicht von selbst schließen, sodass die Meldung "Hallo Welt!" auf dem Bildschirm bleibt, bis Sie die Konsole manuell schließen.


eine Bibliothek mit einer "Hallo Welt!"-Funktion

Beschreibung

Im Nachfolgenden wird Schritt für Schritt gezeigt, wie mit der Hilfe von eine Bibliothek ("*.so"-Datei) geschrieben werden kann, welche eine Funktion enthält, welche "Hallo Welt!" ausgibt.

Die vollständige Datei, welche im Lauf von den folgenden Schritten entstehen wird, kann allerdings auch im Kapiteln heruntergeladen werden.

Außerdem kann im Kapitel ein Programm heruntergeladen werden, welches die Bibliothek importiert und die Funktion aufruft.

notwendige Voraussetzungen

siehe: im Kapitel 'ein "Hallo Welt!"-Programm - notwendige Voraussetzungen'

Vorbereitung

die Datei anlegen

Ich hatte die Bibliothek mit der "Hallo Welt!"-Funktion in Windows geschrieben und dann in Linux getestet. Wenn
  • Sie von Anfang an Linux verwenden,
dann
  • können Sie vermutlich in Linux ähnlich vorgehen und für die Windows-spezifischen Schritte in dieser Schritt-für-Schritt-Anweisung selbst geeignete Lösungen finden, welche sich in Linux umsetzen lassen.

Als erstes habe ich eine neue Textdatei auf meinem Desktop angelegt. Diese habe ich dann von "Neu Textdokument.txt" in "Hallo_Welt.so" umbenannt.

Wenn
  • Sie ebenfalls so vorgehen wollen, aber Ihnen als Dateiname "Neu Textdokument" anstatt "Neu Textdokument.txt" angezeigt wird,
dann
  • zeigt Ihnen Windows die Dateinamens-Endung ".txt" nicht an.

Diese Funktion gibt es, damit man nicht versehentlich die Dateinamens-Endung ändert oder löscht, wenn man den Dateinamen ändert. In unserem Fall soll die Dateinamens-Endung allerdings gelöscht werden.

Damit Windows Ihnen die Dateinamens-Endung anzeigt, können Sie in "Windows experience" ("Windows xp") wie folgt vorgehen:
  1. Öffnen Sie den Arbeitsplatz und damit den Windows Explorer.
  2. Klicken Sie in der Menüleiste auf "Extras".
  3. Klicken Sie in dem Menü, welches sich eben geöffnet hat, auf "Ordneroptionen...".
  4. Klicken Sie in dem Fenster, welches sich eben geöffnet hat, auf den Reiter "Ansicht".
  5. Nehmen Sie den Haken von der Option "Erweiterungen bei bekannten Dateitypen ausblenden" weg.
  6. Klicken Sie unten auf den Knopf "Übernehmen".
  7. Klicken Sie oben auf den Knopf "Für alle übernehmen" und bestätigen Sie das aufspringende Fenster mit dem Knopf "Ja".
  8. Klicken Sie unten auf den Knopf "OK".

Nun sollte Windows auch bei Dateien auf Ihrem Desktop die Dateinamens-Endung anzeigen, sodass Sie die Änderung vornehmen können.

die Datei öffnen

siehe: hier

Anmerkungen zum Speichern

siehe: hier

elf-Zusatzdaten

den Identifikations-Abschnitt schreiben

siehe: hier

den Kopfzeilen-Abschnitt schreiben

Allgemeines

In diesem Abschnitt werde ich die folgenden Werte verwenden:
Beschreibung: Feld-Bezeichnung: Hexadezimalwert:
2560: 2561: 2562: 2563:
Bibliothek e_type 03 00
32 Bit x86-Architektur von Intel Corporation e_machine 03 00
e_version 01 00 00 00
die Bibliothek soll nicht wie eine ausführbare Datei gestartet werden können e_entry 00 00 00 00
   0 Byte (Anfang von der Datei)
  16 Byte (der Abschnitt "Identifikation")
+ 36 Byte (der Abschnitt "Kopfzeile")
= 52 Byte
e_phoff 34 00 00 00
Es ist nicht notwendig, Linux alle Details vom exakten Aufbau von der Bibliothek mitzuteilen. Ich verzichte daher vollständig auf die Angabe von Unterabschnitten. e_shoff 00 00 00 00
e_flags 00 00 00 00
  16 Byte (Größe vom Abschnitt "Identifikation")
+ 36 Byte (Größe vom Abschnitt "Kopfzeile")
= 52 Byte
e_ehsize 34 00
e_phentsize 20 00
  1 Hauptabschnitt für den Anfang von der Datei und den Maschinencode
+ 1 Hauptabschnitt für die Tabelle für Exporte und Importe
+ 1 Hauptabschnitt für die Daten (diese Daten sind lediglich die "Hallo Welt!"-Zeichenkette)
= 3 Hauptabschnitte
e_phnum 03 00
Ich benutze keine Unterabschnitte, daher ist die Angabe überflüssig. e_shentsize 00 00
Anzahl der Unterabschnitte: 0 e_shnum 00 00
e_shstrndx 00 00

Änderung

Erweitern Sie die Datei um die folgenden Werte:
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00
rot hinterlegt: der Wert für das Feld "e_type"
gelb hinterlegt: der Wert für das Feld "e_machine"
grün hinterlegt: der Wert für das Feld "e_version"
blau hinterlegt: der Wert für das Feld "e_entry"
lila hinterlegt: der Wert für das Feld "e_phoff"
rot hinterlegt: der Wert für das Feld "e_shoff"
gelb hinterlegt: der Wert für das Feld "e_flags"
grün hinterlegt: der Wert für das Feld "e_ehsize"
blau hinterlegt: der Wert für das Feld "e_phentsize"
lila hinterlegt: der Wert für das Feld "e_phnum"
rot hinterlegt: der Wert für das Feld "e_shentsize"
gelb hinterlegt: der Wert für das Feld "e_shnum"
grün hinterlegt: der Wert für das Feld "e_shstrndx"

Ergebnis

Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00

Nach dem Speichern müsste die Datei nun "52 Byte"-groß sein.

Größe

siehe: hier

Im Nachfolgenden werden die Tabellen definiert:
  1. Kopfzeilen-Tabelle für Hauptabschnitte
  2. Tabelle für Adressen-Reparaturen
  3. Tabelle für Dinge
  4. Tabelle für Exporte und Importe
  5. Tabelle für Prüfsummen
  6. Tabelle für Zeichenketten

die Kopfzeilen-Tabelle für Hauptabschnitte

Die Kopfzeilen-Tabelle für Hauptabschnitte wird mit den folgenden Kopfzeilen gefüllt:
  1. Eine Kopfzeile, welche Auskünfte über den Haupt-Programmabschnitt mit dem Anfang von der Datei und dem Maschinencode macht.
  2. Eine Kopfzeile, welche Auskünfte über den Haupt-Programmabschnitt mit der Tabelle für Exporte und Importe macht.
  3. Eine Kopfzeile, welche Auskünfte über den Haupt-Programmabschnitt mit den Daten macht.

die Kopfzeile für den Haupt-Programmabschnitt mit dem Anfang von der Datei und dem Maschinencode schreiben

Allgemeines
Mit der Hilfe von diesem Schritt wird die erste Kopfzeile definiert. Für diese werde ich die folgenden Werte verwenden:
Beschreibung: Feld-Bezeichnung: Hexadezimalwert:
2560: 2561: 2562: 2563:
p_type 01 00 00 00
  0 Byte (Anfang von der Datei)
= 0 Byte
p_offset 00 00 00 00
  0 Byte (gewünschte Basisadresse)
= 0 Byte
p_vaddr 00 00 00 00
p_paddr 00 00 00 00
   16 Byte (Größe vom Abschnitt "Identifikation")
+  36 Byte (Größe vom Abschnitt "Kopfzeile")
+  96 Byte (Größe von der Kopfzeilen-Tabelle für Hauptabschnitte mit ihren 3 "32 Byte"-großen Einträgen)
+   8 Byte (Größe von der Tabelle für Adressen-Reparaturen mit ihrem 1 "8 Byte"-großen Eintrag)
+  16 Byte (Größe von der Tabelle für Dinge mit ihrem 1 effektiven "16 Byte"-großen Eintrag)
+  52 Byte (Größe von der Tabelle für Exporte und Importe mit ihren 6 ½ "8 Byte"-großen Einträgen)
+  20 Byte (Größe von der Tabelle für Prüfsummen)
+  20 Byte (Größe von der Tabelle für Zeichenketten)
+  23 Byte (Größe vom Maschinencode)
+   1 Byte (Größe vom unbenutzten Zwischenraum*)
= 288 Byte

* = Der unbenutzte Zwischenraum dient dafür, damit die Anfangsadresse von den Daten restlos durch 32 teilbar ist. Dies ist in unserem Fall nicht notwendig. Es ist allerdings prinzipiell nützlich, wenn man sich beim Programmieren darauf verlassen kann, dass das Link-Programm die Daten auf eine restlos durch 32 Byte teilbare Adresse ausrichtet, sodass man nicht bei jedem Projekt prüfen muss, ob die Ausrichtung von den Daten den Anforderungen genügt.
p_filesz 20 01 00 00
Dadurch, dass kein größerer Wert angegeben ist, wie beim Feld "p_filesz" verwendet wird, setzt das Lade-Programm von keinen weiteren Bytes alle Bits auf "0|b".

Einen unbenutzten Zwischenraum hinter Maschinencode mit Null-Bytes aufzufüllen ist bei der x86-Architektur ohnehin nicht sonderlich geschickt, da der Befehl "00 00|h" "2 Byte"-groß ist. Besser wäre es, unbenutzte Bytes mit dem Wert "90|h" aufzufüllen. Der Befehl "90|h" - das ist einer von den "no operation"-Befehlen - ist "1 Byte"-groß und kann somit auch benutzt werden, wenn sich die Größe von einem unbenutzten Bereich
  • nicht restlos durch 2|d,
  • sondern lediglich restlos durch 1|d
teilen lässt.

Bedeutung: Maschinensprache:
# zur "1 Byte"-großen Speicherstelle im Arbeitsspeicher, dessen Adresse mit der Hilfe vom Register "eax (#0)" angegeben wird, den Wert aus dem Register "al (#0)" hinzuaddieren
RAM[eax (#0)] += al (#0)
00 00
# den Wert aus dem "4 Byte"-großen Register "eax (#0)" mit dem Wert aus dem "4 Byte"-großen Register "eax (#0)" tauschen
eax (#0) <-> eax (#0)
90
p_memsz 20 01 00 00
  • Der Anfang von der Datei muss lesbar sein. Aus diesem Grund ist die Flagge "PF_R" mit der Wertigkeit "22|d = 4|d" auf "1|b" gesetzt.
  • Die Tabelle für Exporte und Importe muss beschreibbar sein. Aus diesem Grund ist die Flagge "PF_W" mit der Wertigkeit "21|d = 2|d" auf "1|b" gesetzt.
  • Der Maschinencode muss ausführbar sein. Aus diesem Grund ist die Flagge "PF_X" mit der Wertigkeit "20|d = 1|d" auf "1|b" gesetzt.
p_flags 07 00 00 00
1 RAM-Seite p_align 00 10 00 00

Änderung
Erweitern Sie die Datei um die folgenden Werte:
                      01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00
rot hinterlegt: der Wert vom Feld "p_type"
gelb hinterlegt: der Wert vom Feld "p_offset"
grün hinterlegt: der Wert vom Feld "p_vaddr"
blau hinterlegt: der Wert vom Feld "p_paddr"
lila hinterlegt: der Wert vom Feld "p_filesz"
rot hinterlegt: der Wert vom Feld "p_memsz"
gelb hinterlegt: der Wert vom Feld "p_flags"
grün hinterlegt: der Wert vom Feld "p_align"

Ergebnis
Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00

Nach dem Speichern müsste die Datei nun "84 Byte"-groß sein.

Größe
siehe: hier

die Kopfzeile für den Haupt-Programmabschnitt mit der Tabelle für Exporte und Importe schreiben

Allgemeines
Mit der Hilfe von diesem Schritt wird die zweite Kopfzeile definiert.

Diese Kopfzeile dient lediglich dafür, um die Tabelle für Exporte und Importe anzugeben, da diese von der Bibliothek, welche ein Bestandteil vom Lade-Programm ist, gelesen werden muss. Das Lade-Programm stellt seiner Bibliothek anhand von dieser Kopfzeile die Anfangsadresse von dieser Tabelle bereit.

Für diese Kopfzeile werde ich die folgenden Werte verwenden:
Beschreibung: Feld-Bezeichnung: Hexadezimalwert:
2560: 2561: 2562: 2563:
p_type 02 00 00 00
    0 Byte (Anfang von der Datei)
+  16 Byte (der Abschnitt "Identifikation")
+  36 Byte (der Abschnitt "Kopfzeile")
+  96 Byte (die Kopfzeilen-Tabelle für Hauptabschnitte mit ihren 3 "32 Byte"-großen Einträgen)
+   8 Byte (die Tabelle für Adressen-Reparaturen mit ihrem 1 "8 Byte"-großen Eintrag)
+  16 Byte (die Tabelle für Dinge mit ihrem 1 effektiven "16 Byte"-großen Eintrag)

= 172 Byte
p_offset AC 00 00 00
    0 Byte (gewünschte Basisadresse)
+ 172 Byte (Anfangsadresse in der Datei; siehe: das Feld "p_offset")
= 172 Byte
p_vaddr AC 00 00 00
p_paddr 00 00 00 00
   6 ½ Einträge; jeder vollständige Eintrag ist jeweils "8 Byte"-groß
= 52 Byte
p_filesz 34 00 00 00
Dadurch, dass kein größerer Wert angegeben ist, wie beim Feld "p_filesz" verwendet wird, setzt das Lade-Programm von keinen weiteren Bytes alle Bits auf "0|b". p_memsz 34 00 00 00
  • Die Tabelle für Exporte und Importe muss lesbar sein. Aus diesem Grund ist die Flagge "PF_R" mit der Wertigkeit "22|d = 4|d" auf "1|b" gesetzt.
  • Die Tabelle für Exporte und Importe muss beschreibbar sein, damit das Lade-Programm die Adressen zu den anderen Tabellen reparieren kann. Aus diesem Grund ist die Flagge "PF_W" mit der Wertigkeit "21|d = 2|d" auf "1|b" gesetzt.

Ansich spielt es keine große Rolle, welche Flaggen in diesem Feld auf "1|b" gesetzt sind, da sich dieser Haupt-Programmabschnitt in einem Haupt-Programmabschnitt befindet, für welchen bereits alle Berechtigungen erteilt sind. Es können also keine weiteren Berechtigungen durch dieses Feld erteilt werden, welche nicht ohnehin schon gegeben sind.
p_flags 06 00 00 00
Das Lade-Programm von Linux soll sich nicht um das Einfügen von weiteren Bytes kümmern, von welchen alle Bits auf "0|b" gesetzt sind. p_align 00 00 00 00

Änderung
Erweitern Sie die Datei um die folgenden Werte:
                      02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00
rot hinterlegt: der Wert vom Feld "p_type"
gelb hinterlegt: der Wert vom Feld "p_offset"
grün hinterlegt: der Wert vom Feld "p_vaddr"
blau hinterlegt: der Wert vom Feld "p_paddr"
lila hinterlegt: der Wert vom Feld "p_filesz"
rot hinterlegt: der Wert vom Feld "p_memsz"
gelb hinterlegt: der Wert vom Feld "p_flags"
grün hinterlegt: der Wert vom Feld "p_align"

Ergebnis
Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00 02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00

Nach dem Speichern müsste die Datei nun "116 Byte"-groß sein.

Größe
siehe: hier

die Kopfzeile für den Haupt-Programmabschnitt mit den Daten schreiben

Allgemeines
Mit der Hilfe von diesem Schritt wird die letzte Kopfzeile definiert. Für diese werde ich die folgenden Werte verwenden:
Beschreibung: Feld-Bezeichnung: Hexadezimalwert:
2560: 2561: 2562: 2563:
p_type 01 00 00 00
    0 Byte (Anfang von der Datei)
+  16 Byte (der Abschnitt "Identifikation")
+  36 Byte (der Abschnitt "Kopfzeile")
+  96 Byte (die Kopfzeilen-Tabelle für Hauptabschnitte mit ihren 3 "32 Byte"-großen Einträgen)
+   8 Byte (die Tabelle für Adressen-Reparaturen mit ihrem 1 "8 Byte"-großen Eintrag)
+  16 Byte (die Tabelle für Dinge mit ihrem 1 effektiven "16 Byte"-großen Eintrag)
+  52 Byte (die Tabelle für Exporte und Importe mit ihren 6 ½ "8 Byte"-großen Einträgen)
+  20 Byte (die Tabelle für Prüfsummen)
+  20 Byte (die Tabelle für Zeichenketten)
+  23 Byte (der Maschinencode)
+   1 Byte (der unbenutzte Zwischenraum)
= 288 Byte
p_offset 20 01 00 00
      0 Byte (gewünschte Basisadresse)
+   288 Byte (Anfangsadresse in der Datei; siehe: das Feld "p_offset")
+ 4.096 Byte (Größe vom einer RAM-Seite*)
= 4.384 Byte

* = Dieser Werte dient dafür, um die Platzierung in einer anderen RAM-Seite zu beginnen, als der Inhalt vom ersten Hauptabschnitt platziert wurde.
p_vaddr 20 11 00 00
p_paddr 00 00 00 00
  11 Byte (Größe von der Zeichenkette "Hallo Welt!")
= 11 Byte
p_filesz 0B 00 00 00
Dadurch, dass kein größerer Wert angegeben ist, wie beim Feld "p_filesz" verwendet wird, setzt das Lade-Programm von keinen weiteren Bytes alle Bits auf "0|b". p_memsz 0B 00 00 00
  • Die Daten müssen lesbar sein. Aus diesem Grund ist die Flagge "PF_R" mit der Wertigkeit "22|d = 4|d" auf "1|b" gesetzt.
  • Die Daten sollen beschreibbar sein. Aus diesem Grund ist die Flagge "PF_W" mit der Wertigkeit "21|d = 2|d" auf "1|b" gesetzt.

    Für die Zeichenkette "Hallo Welt!" ist die Schreibberechtigung nicht notwendig. Es ist allerdings universeller, für die Daten/Variablen eine Schreibberechtigung zu geben. So muss man nicht bei jeder Datei/jedem Projekt prüfen, ob die Daten/Variablen beschreibbar sein müssen.
p_flags 06 00 00 00
Das Lade-Programm von Linux soll sich nicht um das Einfügen von weiteren Bytes kümmern, von welchen alle Bits auf "0|b" gesetzt sind. p_align 00 10 00 00

Änderung
Erweitern Sie die Datei um die folgenden Werte:
                      01 00 00 00 - 20 01 00 00 20 11 00 00
00000080: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000090: 00 10 00 00
rot hinterlegt: der Wert vom Feld "p_type"
gelb hinterlegt: der Wert vom Feld "p_offset"
grün hinterlegt: der Wert vom Feld "p_vaddr"
blau hinterlegt: der Wert vom Feld "p_paddr"
lila hinterlegt: der Wert vom Feld "p_filesz"
rot hinterlegt: der Wert vom Feld "p_memsz"
gelb hinterlegt: der Wert vom Feld "p_flags"
grün hinterlegt: der Wert vom Feld "p_align"

Ergebnis
Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00 02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00 01 00 00 00 - 20 01 00 00 20 11 00 00
00000080: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000090: 00 10 00 00

Nach dem Speichern müsste die Datei nun "148 Byte"-groß sein.

Größe
siehe: hier

Die folgende Tabelle wurde nun definiert:
  1. Kopfzeilen-Tabelle für Hauptabschnitte

Im Nachfolgenden werden die verbleibenden Tabellen definiert:
  1. Tabelle für Adressen-Reparaturen
  2. Tabelle für Dinge
  3. Tabelle für Exporte und Importe
  4. Tabelle für Prüfsummen
  5. Tabelle für Zeichenketten

die Tabelle für Adressen-Reparaturen schreiben

Allgemeines

Mit der Hilfe von diesem Schritt wird die Tabelle für Adressen-Reparaturen in die Datei eingefügt.

Diese Tabelle ist notwendig, da die Adresse von der "Hallo Welt!"-Zeichkette, welche im Maschinencode angegeben ist, zur Laufzeit nicht gültig sein wird. Das Lade-Programm von Linux lässt es nicht zu, dass etwas an der Adresse "0|h" platziert wird. Für diese Bibliothek ist allerdings die gewünschte Basisadresse auf "0|h" gesetzt.

Die Tabelle für Adressen-Reparaturen hat also lediglich 1 "8 Byte"-großen Eintrag. Für die einzelnen Felder werde ich die folgenden Werte verwenden:
Beschreibung: Feld-Bezeichnung: Hexadezimalwert
2560: 2561: 2562: 2563:
    0 Byte (Anfang von der Datei)
+  16 Byte (der Abschnitt "Identifikation")
+  36 Byte (der Abschnitt "Kopfzeile")
+  96 Byte (die Kopfzeilen-Tabelle für Hauptabschnitte mit ihren 3 "32 Byte"-großen Einträgen)
+   8 Byte (die Tabelle für Adressen-Reparaturen mit ihrem 1 "8 Byte"-großen Eintrag)
+  16 Byte (die Tabelle für Dinge mit ihrem 1 effektiven "16 Byte"-großen Einträgen)
+  52 Byte (die Tabelle für Exporte und Importe mit ihren 6 ½ "8 Byte"-großen Einträgen)
+  20 Byte (die Tabelle für Prüfsummen)
+  20 Byte (die Tabelle für Zeichenketten)
+  11 Byte (Offset im Maschinencode)

= 275 Byte
r_offset 13 01 00 00
R_386_RELATIVE, d. h.:
  Basisadressegewählt
Speicherzelleninhaltalt
Speicherzelleninhaltneu
r_info[0] 08
für die Zeichekette gibt es kein Ding in der Tabelle für Dinge r_info[1 bis 2] 00 00
dieses Byte wird nicht benutzt r_info[3] 00

Änderung

Erweitern Sie die Datei um die folgenden Werte:
                      13 01 00 00 - 08 00 00 00 rot hinterlegt: der Wert vom Feld "r_offset"
gelb hinterlegt: der Wert vom Feld "r_info[0]"
grün hinterlegt: der Wert vom Feld "r_info[1 bis 2]"
blau hinterlegt: der Wert vom Feld "r_info[3]"

Ergebnis

Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00 02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00 01 00 00 00 - 20 01 00 00 20 11 00 00
00000080: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000090: 00 10 00 00 13 01 00 00 - 08 00 00 00

Nach dem Speichern müsste die Datei nun "156 Byte"-groß sein.

Größe

  4 Byte für das Feld "r_offset"
+ 1 Byte für das Feld "r_info[0]"
+ 2 Byte für das Feld "r_info[1 bis 2]"
+ 1 Byte für das Feld "r_info[3]"
= 8 Byte für die komplette Tabelle für Adressen-Reparaturen

Die folgenden Tabellen wurde nun definiert:
  1. Kopfzeilen-Tabelle für Hauptabschnitte
  2. Tabelle für Adressen-Reparaturen

Im Nachfolgenden werden die verbleibenden Tabellen definiert:
  1. Tabelle für Dinge
  2. Tabelle für Exporte und Importe
  3. Tabelle für Prüfsummen
  4. Tabelle für Zeichenketten

die Tabelle für Dinge schreiben

Allgemeines

Mit der Hilfe von diesem Schritt wird die Tabelle für Dinge definiert.

Diese Tabelle ist notwendig, um für die Funktion "Hallo_Welt_ausgeben"
  • eine Anfangsadresse und
  • eine Zeichenkette für ihren Namen
zugänglich zu machen.

Die Tabelle für Dinge bekommt von mir 1 effektiven Eintrag um das Ding zu definieren. Den 0. Eintrag lasse ich gemäß dem Trick weg, welcher im Kapitel "Tabelle für Dinge - Aufbau" beschrieben ist.

Ich werde die folgenden Werte für die einzelnen Felder vom verbleibenden Eintrag verwenden:
Beschreibung: Feld-Bezeichnung: Hexadezimalwert:
2560: 2561: 2562: 2563:
1 Byte als Offset in der Tabelle für Zeichenketten st_name 01 00 00 00
    0 Byte (Anfang von der Datei)
+  16 Byte (der Abschnitt "Identifikation")
+  36 Byte (der Abschnitt "Kopfzeile")
+  96 Byte (die Kopfzeilen-Tabelle für Hauptabschnitte mit ihren 3 "32 Byte"-großen Einträgen)
+   8 Byte (die Tabelle für Adressen-Reparaturen mit ihrem 1 "8 Byte"-großen Eintrag)
+  16 Byte (die Tabelle für Dinge mit ihrem 1 effektiven "16 Byte"-großen Eintrag)
+  52 Byte (die Tabelle für Exporte und Importe mit ihren 6 ½ "8 Byte"-großen Einträgen)
+  20 Byte (die Tabelle für Prüfsummen)
+  20 Byte (die Tabelle für Zeichenketten)
+   0 Byte (Offset im Maschinencode)
= 264 Byte
st_value 08 01 00 00
die Größe wird eh nicht ausgewertet st_size 00 00 00 00
   0|d = 0|d * 20|d (Ich möchte keine Auskunft darüber machen will, in welchem Inhalt das Ding enthalten ist.)
+ 16|d = 1|d * 24|d (Weil diese Beschreibung zutrifft:
  • Das Ding ist in einer anderen Datei enthalten, oder
  • das Ding ist in dieser Datei enthalten und wird möglicherweise (auch) von anderen Dateien verwendet.

Diese Angabe kann für Dinge verwendet werden, welche
  • exportiert oder
  • importiert
werden.

= 16|d
st_info 10
st_other 00
In dieser Datei sind keine Kopfzeilen für Unterabschnitte definiert, daher kann keine Kopfzeile für dieses Ding angegeben werden.

Der Wert von diesem Feld wird glaube ich ohnehin nicht ausgewertet.
st_shndx 00 00

Änderung

Erweitern Sie die Datei um die folgenden Werte:
                                                01 00 00 00
000000A0: 08 01 00 00 00 00 00 00 - 10 00 00 00
rot hinterlegt: der Wert vom Feld "st_name"
gelb hinterlegt: der Wert vom Feld "st_value"
grün hinterlegt: der Wert vom Feld "st_size"
blau hinterlegt: der Wert vom Feld "st_info"
lila hinterlegt: der Wert vom Feld "st_other"
rot hinterlegt: der Wert vom Feld "st_shndx"

Ergebnis

Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00 02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00 01 00 00 00 - 20 01 00 00 20 11 00 00
00000080: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000090: 00 10 00 00 13 01 00 00 - 08 00 00 00 01 00 00 00
000000A0: 08 01 00 00 00 00 00 00 - 10 00 00 00

Nach dem Speichern müsste die Datei nun "172 Byte"-groß sein.

Größe

   4 Byte für den Wert vom Feld "st_name
+  4 Byte für den Wert vom Feld "st_value"
+  4 Byte für den Wert vom Feld "st_size"
+  1 Byte für den Wert vom Feld "st_info"
+  1 Byte für den Wert vom Feld "st_other"
+  2 Byte für den Wert vom Feld "st_shndx"
= 16 Byte für die komplette Tabelle für Dinge

Die folgenden Tabellen wurde nun definiert:
  1. Kopfzeilen-Tabelle für Hauptabschnitte
  2. Tabelle für Adressen-Reparaturen
  3. Tabelle für Dinge

Im Nachfolgenden werden die verbleibenden Tabellen definiert:
  1. Tabelle für Exporte und Importe
  2. Tabelle für Prüfsummen
  3. Tabelle für Zeichenketten

die Tabelle für Exporte und Importe schreiben

Allgemeines

Mit der Hilfe von diesem Schritt wird die Tabelle für Exporte und Importe definiert.

Zunächst ist der Bibliothek vom Lade-Programm, welche der Anwendung die Adresse zur "Hallo_Welt_ausgeben"-Funktion bereitstellt, die Anfangsadresse von dieser Tabelle bekannt. In dieser Tabelle befinden sich die Anfangsadressen zu den anderen Tabellen. Damit also die Bibliothek vom Lade-Programm die anderen Tabellen findet, ist diese Tabelle notwendig.

Die Tabelle für Exporte und Importe bekommt von mir mehrere Einträge.

Für die einzelnen Felder vom 0. Eintrag werde ich die folgenden Werte verwenden:
Beschreibung: Feld-Bezeichnung: Hexadezimalwert
2560: 2561: 2562: 2563:
Dieser Eintrag gibt die Gesamtgröße von der Tabelle für Adressen-Reparaturen an. d_tag 12 00 00 00
Die Tabelle für Adressen-Reparaturen ist "8 Byte"-groß. d_val 08 00 00 00

Für die einzelnen Felder vom 1. Eintrag werde ich die folgenden Werte verwenden:
Beschreibung: Feld-Bezeichnung: Hexadezimalwert
2560: 2561: 2562: 2563:
Dieser Eintrag gibt die Größe von einem einzelnen Eintrag in der Tabelle für Adressen-Reparaturen an. d_tag 13 00 00 00
Ein einzelner Eintrag ist "8 Byte"-groß. d_val 08 00 00 00

Für die einzelnen Felder vom 2. Eintrag werde ich die folgenden Werte verwenden:
Beschreibung: Feld-Bezeichnung: Hexadezimalwert
2560: 2561: 2562: 2563:
Dieser Eintrag gibt die Anfangsadresse von der Tabelle für Adressen-Reparaturen an. d_tag 11 00 00 00
    0 Byte (Anfang von der Datei)
+  16 Byte (der Abschnitt "Identifikation")
+  36 Byte (der Abschnitt "Kopfzeile")
+  96 Byte (die Kopfzeilen-Tabelle für Hauptabschnitte mit ihren 3 "32 Byte"-großen Einträgen)
= 148 Byte
d_ptr 94 00 00 00

Für die einzelnen Felder vom 3. Eintrag werde ich die folgenden Werte verwenden:
Beschreibung: Feld-Bezeichnung: Hexadezimalwert
2560: 2561: 2562: 2563:
Dieser Eintrag gibt die Anfangsadresse von der Tabelle für Dinge an. d_tag 06 00 00 00
    0 Byte (Anfang von der Datei)
+  16 Byte (der Abschnitt "Identifikation")
+  36 Byte (der Abschnitt "Kopfzeile")
+  96 Byte (die Kopfzeilen-Tabelle für Hauptabschnitte mit ihren 3 "32 Byte"-großen Einträgen)
+   8 Byte (die Tabelle für Adressen-Reparaturen mit ihrem 1 "8 Byte"-großen Eintrag)
-  16 Byte (damit vorgegaukelt wird, dass der 0. Eintrag in der Tabelle für Dinge existieren würde)
= 140 Byte
d_ptr 8C 00 00 00

Für die einzelnen Felder vom 4. Eintrag werde ich die folgenden Werte verwenden:
Beschreibung: Feld-Bezeichnung: Hexadezimalwert
2560: 2561: 2562: 2563:
Dieser Eintrag gibt die Anfangsadresse von der Tabelle für Prüfsummen an. d_tag 04 00 00 00
    0 Byte (Anfang von der Datei)
+  16 Byte (der Abschnitt "Identifikation")
+  36 Byte (der Abschnitt "Kopfzeile")
+  96 Byte (die Kopfzeilen-Tabelle für Hauptabschnitte mit ihren 3 "32 Byte"-großen Einträgen)
+   8 Byte (die Tabelle für Adressen-Reparaturen mit ihrem 1 "8 Byte"-großen Eintrag)
+  16 Byte (die Tabelle für Dinge mit ihrem 1 effektiven "16 Byte"-großen Einträgen)
+  52 Byte (die Tabelle für Exporte und Importe mit ihren 6 ½ "8 Byte"-großen Einträgen)

= 224 Byte
d_ptr E0 00 00 00

Für die einzelnen Felder vom 5. Eintrag werde ich die folgenden Werte verwenden:
Beschreibung: Feld-Bezeichnung: Hexadezimalwert
2560: 2561: 2562: 2563:
Dieser Eintrag gibt die Anfangsadresse von der Tabelle für Zeichenketten an. d_tag 05 00 00 00
    0 Byte (Anfang von der Datei)
+  16 Byte (der Abschnitt "Identifikation")
+  36 Byte (der Abschnitt "Kopfzeile")
+  96 Byte (die Kopfzeilen-Tabelle für Hauptabschnitte mit ihren 3 "32 Byte"-großen Einträgen)
+   8 Byte (die Tabelle für Adressen-Reparaturen mit ihrem 1 "8 Byte"-großen Eintrag)
+  16 Byte (die Tabelle für Dinge mit ihrem 1 effektiven "16 Byte"-großen Eintrag)
+  52 Byte (die Tabelle für Exporte und Importe mit ihren 6 ½ "8 Byte"-großen Einträgen)
+  20 Byte (die Tabelle für Prüfsummen)
-   1 Byte (damit vorgegaukelt wird, dass der 0. Eintrag in der Tabelle für Zeichenketten existieren würde)
= 243 Byte
d_ptr F3 00 00 00

Für die einzelnen Felder vom 6. Eintrag werde ich die folgenden Werte verwenden:
Beschreibung: Feld-Bezeichnung: Hexadezimalwert
2560: 2561: 2562: 2563:
Dieser Eintrag dient dafür, um das Ende von der Tabelle für Exporte und Importe zu markieren. d_tag 00 00 00 00
Die Bedeutung vom Wert von diesem Feld gilt als "undefiniert". Der Wert wird daher nicht ausgewertet. Ich werde ihn daher weglassen, sodass der nachfolgende Inhalt/die nachfolgende Tabelle hier beginnt. d_val oder d_ptr

Änderung

Erweitern Sie die Datei um die folgenden Werte:
                                                12 00 00 00
000000B0: 08 00 00 00 13 00 00 00 - 08 00 00 00 11 00 00 00
000000C0: 94 00 00 00 06 00 00 00 - 8C 00 00 00 04 00 00 00
000000D0: E0 00 00 00 05 00 00 00 - F3 00 00 00 00 00 00 00
0. Eintrag:
rot hinterlegt: der Wert vom Feld "d_tag"
gelb hinterlegt: der Wert vom Feld "d_val"

1. Eintrag:
grün hinterlegt: der Wert vom Feld "d_tag"
blau hinterlegt: der Wert vom Feld "d_val"

2. Eintrag:
lila hinterlegt: der Wert vom Feld "d_tag"
rot hinterlegt: der Wert vom Feld "d_ptr"

3. Eintrag:
gelb hinterlegt: der Wert vom Feld "d_tag"
grün hinterlegt: der Wert vom Feld "d_ptr"

4. Eintrag:
blau hinterlegt: der Wert vom Feld "d_tag"
lila hinterlegt: der Wert vom Feld "d_ptr"

5. Eintrag:
rot hinterlegt: der Wert vom Feld "d_tag"
gelb hinterlegt: der Wert vom Feld "d_ptr"

6. Eintrag:
grün hinterlegt: der Wert vom Feld "d_tag"

Ergebnis

Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00 02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00 01 00 00 00 - 20 01 00 00 20 11 00 00
00000080: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000090: 00 10 00 00 13 01 00 00 - 08 00 00 00 01 00 00 00
000000A0: 08 01 00 00 00 00 00 00 - 10 00 00 00 12 00 00 00
000000B0: 08 00 00 00 13 00 00 00 - 08 00 00 00 11 00 00 00
000000C0: 94 00 00 00 06 00 00 00 - 8C 00 00 00 04 00 00 00
000000D0: E0 00 00 00 05 00 00 00 - F3 00 00 00 00 00 00 00

Nach dem Speichern müsste die Datei nun "224 Byte"-groß sein.

Größe

   6 ½ Einträge; jeder vollständige Eintrag ist jeweils "8 Byte"-groß
= 52 Byte für die komplette Tabelle für Exporte und Importe

Die folgenden Tabellen wurde nun definiert:
  1. Kopfzeilen-Tabelle für Hauptabschnitte
  2. Tabelle für Adressen-Reparaturen
  3. Tabelle für Dinge
  4. Tabelle für Exporte und Importe

Im Nachfolgenden werden die verbleibenden Tabellen definiert:
  1. Tabelle für Prüfsummen
  2. Tabelle für Zeichenketten

die Tabelle für Prüfsummen schreiben

Allgemeines

Mit der Hilfe von diesem Schritt wird die Tabelle für Prüfsummen definiert.

Da ich persönlich nichts von der Tabelle für Prüfsummen halte, aber gezwungen bin, sie dennoch einzufügen, damit die Bibliothek verwendet werden kann, werde ich den Trick anwenden, welcher im Kapitel über die Tabelle für Prüfsummen beschrieben ist.

Selbst wenn die Formel für die Berechnung von Prüfsummen verwendet werden würde, dann würde diese Tabelle bei nur 1 Eintrag einen Geschwindigkeitsnachteil bedeuten.

Das Konzept ist in dieser Dokumentation im Groben beschrieben. Wenn
  • Sie die Tabelle korrekt implementieren wollen,
dann
  • fehlt Ihnen noch die Formel für die Berechnung von Prüfsummen.

    Diese Formel ist in der offiziellen Beschreibung vom Dateiformat als "C"-Quellcode enthalten. Die offizielle Beschreibung vom Dateiformat ist im Kapitel "weiteres Material zu diesem Thema - Dokumente" aufgelistet.

Änderung

Wenn
  • Sie ebenfalls den Trick anwenden wollen,
dann
  • Erweitern Sie die Datei um die folgenden Werte:
000000E0: 01 00 00 00 00 00 00 00 - 01 00 00 00 01 00 00 00
000000F0: 00 00 00 00
rot hinterlegt: der Wert vom Feld "nbucket"
gelb hinterlegt: der Wert vom Feld "nchain"
grün hinterlegt: die Auflistung von Anfangspositionen
blau hinterlegt: die Auflistung von Folgepositionen

Ergebnis

Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00 02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00 01 00 00 00 - 20 01 00 00 20 11 00 00
00000080: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000090: 00 10 00 00 13 01 00 00 - 08 00 00 00 01 00 00 00
000000A0: 08 01 00 00 00 00 00 00 - 10 00 00 00 12 00 00 00
000000B0: 08 00 00 00 13 00 00 00 - 08 00 00 00 11 00 00 00
000000C0: 94 00 00 00 06 00 00 00 - 8C 00 00 00 04 00 00 00
000000D0: E0 00 00 00 05 00 00 00 - F3 00 00 00 00 00 00 00
000000E0: 01 00 00 00 00 00 00 00 - 01 00 00 00 01 00 00 00
000000F0: 00 00 00 00

Nach dem Speichern müsste die Datei nun "244 Byte"-groß sein.

Größe

   4 Byte für das Feld "nbucket"
+  4 Byte für das Feld "nchain"
+  4 Byte für die Auflistung von Anfangspositionen
+  8 Byte für die Auflistung von Folgepositionen
= 20 Byte für die komplette Tabelle für Prüfsummen

Die folgenden Tabellen wurde nun definiert:
  1. Kopfzeilen-Tabelle für Hauptabschnitte
  2. Tabelle für Adressen-Reparaturen
  3. Tabelle für Dinge
  4. Tabelle für Exporte und Importe
  5. Tabelle für Prüfsummen

Im Nachfolgenden wird die verbleibende Tabelle definiert:
  1. Tabelle für Zeichenketten

die Tabelle für Zeichenketten schreiben

Allgemeines

Mit der Hilfe von diesem Schritt wird die Tabelle für Zeichenketten definiert.

Diese Tabelle ist eine Auflistung von Zeichenketten, welche jeweils mit der Hilfe von 1 weiterem Zeichen abgeschlossen sind, von welchem alle Bits auf "0|b" gesetzt sind.

In unserem Fall ist allerdings nur die Zeichenkette "Hallo_Welt_ausgeben" für den Funktionsnamen notwendig. Die Zeichenkette "Hallo Welt!" sehe ich als Daten von der Bibliothek an und werde sie daher nicht in dieser Tabelle unterbringen.

Ich werde den 0. Eintrag aus dieser Tabelle, welcher eine leer Zeichenkette ("") wäre, gemäß dem Trick, welcher im Kapitel "Tabelle für Zeichenketten - Aufbau" beschrieben ist, weglassen.

  1. Am Anfang von dieser Tablle werde ich die Zeichenkette "Hallo_Welt_ausgeben" speichern und
  2. anschließend ein weiteres Byte speichern, von welchem alle Bits auf "0|b" gesetzt sind, um das Ende von dieser Zeichenkette zu markieren.

Änderung

Erweitern Sie die Datei um die folgenden Werte:
                      48 61 6C 6C - 6F 5F 57 65 6C 74 5F 61
00000100: 75 73 67 65 62 65 6E 00
rot hinterlegt: die Zeichenkette "Hallo_Welt_ausgeben"
gelb hinterlegt: Abschluss-Zeichen, von welchem alle Bits auf "0|b" gesetzt sind

Ergebnis

Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00 02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00 01 00 00 00 - 20 01 00 00 20 11 00 00
00000080: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000090: 00 10 00 00 13 01 00 00 - 08 00 00 00 01 00 00 00
000000A0: 08 01 00 00 00 00 00 00 - 10 00 00 00 12 00 00 00
000000B0: 08 00 00 00 13 00 00 00 - 08 00 00 00 11 00 00 00
000000C0: 94 00 00 00 06 00 00 00 - 8C 00 00 00 04 00 00 00
000000D0: E0 00 00 00 05 00 00 00 - F3 00 00 00 00 00 00 00
000000E0: 01 00 00 00 00 00 00 00 - 01 00 00 00 01 00 00 00
000000F0: 00 00 00 00 48 61 6C 6C - 6F 5F 57 65 6C 74 5F 61
00000100: 75 73 67 65 62 65 6E 00

Nach dem Speichern müsste die Datei nun "264 Byte"-groß sein.

Größe

  19 Byte für die Zeichenkette "Hallo_Welt_ausgeben"
+  1 Byte für das Abschluss-Zeichen
= 20 Byte für die komplette Tabelle für Zeichenketten

Übersicht

Das waren soweit die kompletten elf-Zusatzdaten:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00 02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00 01 00 00 00 - 20 01 00 00 20 11 00 00
00000080: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000090: 00 10 00 00 13 01 00 00 - 08 00 00 00 01 00 00 00
000000A0: 08 01 00 00 00 00 00 00 - 10 00 00 00 12 00 00 00
000000B0: 08 00 00 00 13 00 00 00 - 08 00 00 00 11 00 00 00
000000C0: 94 00 00 00 06 00 00 00 - 8C 00 00 00 04 00 00 00
000000D0: E0 00 00 00 05 00 00 00 - F3 00 00 00 00 00 00 00
000000E0: 01 00 00 00 00 00 00 00 - 01 00 00 00 01 00 00 00
000000F0: 00 00 00 00 48 61 6C 6C - 6F 5F 57 65 6C 74 5F 61
00000100: 75 73 67 65 62 65 6E 00
rot hinterlegt: der Abschnitt "Identifikation"
gelb hinterlegt: der Abschnitt "Kopfzeile"
grün hinterlegt: die Kopfzeile für den Haupt-Programmabschnitt mit dem Anfang von der Datei und dem Maschinencode
blau hinterlegt: die Kopfzeile für den Haupt-Programmabschnitt mit der Tabelle für Exporte und Importe
lila hinterlegt: die Kopfzeile für den Haupt-Programmabschnitt mit den Daten
rot hinterlegt: die Tabelle für Adressen-Reparaturen
gelb hinterlegt: die Tabelle für Dinge
grün hinterlegt: die Tabelle für Exporte und Importe
blau hinterlegt: die Tabelle für Prüfsummen
lila hinterlegt: die Tabelle für Zeichenketten

der Maschinencode

In diesem Teil ist
  1. zum Einen der Maschinencode, um die "Hallo Welt!"-Zeichenkette auszugeben, gespeichert und
  2. zum Anderen der Maschinencode, um zum Aufrufer zurückzukehren.
  3. Das Ende ist mit Bytes aufgefüllt, welche alle jeweils den Wert "90|h" haben.

die Funktion "write"

die Funktions-Identifikationskennung definieren

Allgemeines
siehe: hier

Änderung
Erweitern Sie die Datei um die folgenden Werte:
                                    B8 04 00 00 00 rot hinterlegt: die Befehlsnummer vom Befehl "move"
gelb hinterlegt: der Wert

Ergebnis
Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00 02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00 01 00 00 00 - 20 01 00 00 20 11 00 00
00000080: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000090: 00 10 00 00 13 01 00 00 - 08 00 00 00 01 00 00 00
000000A0: 08 01 00 00 00 00 00 00 - 10 00 00 00 12 00 00 00
000000B0: 08 00 00 00 13 00 00 00 - 08 00 00 00 11 00 00 00
000000C0: 94 00 00 00 06 00 00 00 - 8C 00 00 00 04 00 00 00
000000D0: E0 00 00 00 05 00 00 00 - F3 00 00 00 00 00 00 00
000000E0: 01 00 00 00 00 00 00 00 - 01 00 00 00 01 00 00 00
000000F0: 00 00 00 00 48 61 6C 6C - 6F 5F 57 65 6C 74 5F 61
00000100: 75 73 67 65 62 65 6E 00 - B8 04 00 00 00

Nach dem Speichern müsste die Datei nun "269 Byte"-groß sein.

Größe
siehe: hier

den Wert vom Parameter "Ziel_-_Konsole_-_Datenkanal_-_Identifikationskennung" definieren

Allgemeines
siehe: hier

Änderung
Erweitern Sie die Datei um die folgenden Werte:
                                                   BB 01 00
00000110: 00 00
rot hinterlegt: die Befehlsnummer vom Befehl "move"
gelb hinterlegt: der Wert

Ergebnis
Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00 02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00 01 00 00 00 - 20 01 00 00 20 11 00 00
00000080: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000090: 00 10 00 00 13 01 00 00 - 08 00 00 00 01 00 00 00
000000A0: 08 01 00 00 00 00 00 00 - 10 00 00 00 12 00 00 00
000000B0: 08 00 00 00 13 00 00 00 - 08 00 00 00 11 00 00 00
000000C0: 94 00 00 00 06 00 00 00 - 8C 00 00 00 04 00 00 00
000000D0: E0 00 00 00 05 00 00 00 - F3 00 00 00 00 00 00 00
000000E0: 01 00 00 00 00 00 00 00 - 01 00 00 00 01 00 00 00
000000F0: 00 00 00 00 48 61 6C 6C - 6F 5F 57 65 6C 74 5F 61
00000100: 75 73 67 65 62 65 6E 00 - B8 04 00 00 00 BB 01 00
00000110: 00 00

Nach dem Speichern müsste die Datei nun "274 Byte"-groß sein.

Größe
siehe: hier

den Wert vom Parameter "Quelle_-_Nutzdaten_-_Adresse" definieren

Allgemeines
Mit der Hilfe von diesem Schritt wird der Wert vom Parameter "Quelle_-_Nutzdaten_-_Adresse" definiert.

Der Wert ist eine Adresse und gibt eine Speicherstelle im gemeinsamen Segment von der Anwendung und der Bibliothek "Hallo_Welt.so" an. Die Adresse ist ein Offset vom Segmentanfang. An der angegebenen Speicherstelle beginnt die "Hallo Welt!"-Zeichenkette.

Der Wert von diesem Parameter ist
      0 Byte (gewünschte Basisadresse)
+    16 Byte (der Abschnitt "Identifikation")
+    36 Byte (der Abschnitt "Kopfzeile")
+    96 Byte (die Kopfzeilen-Tabelle für Hauptabschnitte mit ihren 3 "32 Byte"-großen Einträgen)
+     8 Byte (die Tabelle für Adressen-Reparaturen mit ihrem 1 "8 Byte"-großen Eintrag)
+    16 Byte (die Tabelle für Dinge mit ihrem 1 effektiven "16 Byte"-großen Eintrag)
+    52 Byte (die Tabelle für Exporte und Importe mit ihren 6 ½ "8 Byte"-großen Einträgen)
+    20 Byte (die Tabelle für Prüfsummen)
+    20 Byte (die Tabelle für Zeichenketten)
+    23 Byte (der Maschinencode)
+     1 Byte (der unbenutzte Zwischenraum)
+ 4.096 Byte (1 RAM-Seite)
= 4.384 Byte

Die von mir gewünschte Basisadresse wird nicht die vom Lade-Programm gewählte Basisadresse sein, da Linux es nicht zulässt, dass die Bibliothek bei der Adresse "0|d" platziert wird. Aus diesem Grund wird das Lade-Programm die Bibliothek sonstwo platzieren und es wird eine Adressen-Reparatur notwendig sein.

Die Adressen-Reparatur wurde bereits in einem vorherigen Schritt definiert.

Änderung
Erweitern Sie die Datei um die folgenden Werte:
                B9 20 11 00 00 rot hinterlegt: "push"-Befehl
gelb hinterlegt: Wert

Ergebnis
Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00 02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00 01 00 00 00 - 20 01 00 00 20 11 00 00
00000080: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000090: 00 10 00 00 13 01 00 00 - 08 00 00 00 01 00 00 00
000000A0: 08 01 00 00 00 00 00 00 - 10 00 00 00 12 00 00 00
000000B0: 08 00 00 00 13 00 00 00 - 08 00 00 00 11 00 00 00
000000C0: 94 00 00 00 06 00 00 00 - 8C 00 00 00 04 00 00 00
000000D0: E0 00 00 00 05 00 00 00 - F3 00 00 00 00 00 00 00
000000E0: 01 00 00 00 00 00 00 00 - 01 00 00 00 01 00 00 00
000000F0: 00 00 00 00 48 61 6C 6C - 6F 5F 57 65 6C 74 5F 61
00000100: 75 73 67 65 62 65 6E 00 - B8 04 00 00 00 BB 01 00
00000110: 00 00 B9 20 11 00 00

Nach dem Speichern müsste die Datei nun "279 Byte"-groß sein.

Größe
siehe: hier

den Wert vom Parameter "Quelle_-_Nutzdaten_-_Länge_in_Byte" definieren

Allgemeines
siehe: hier

Änderung
Erweitern Sie die Datei um die folgenden Werte:
                               BA - 0B 00 00 00 rot hinterlegt: die Befehlsnummer vom Befehl "move"
gelb hinterlegt: der Wert

Ergebnis
Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00 02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00 01 00 00 00 - 20 01 00 00 20 11 00 00
00000080: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000090: 00 10 00 00 13 01 00 00 - 08 00 00 00 01 00 00 00
000000A0: 08 01 00 00 00 00 00 00 - 10 00 00 00 12 00 00 00
000000B0: 08 00 00 00 13 00 00 00 - 08 00 00 00 11 00 00 00
000000C0: 94 00 00 00 06 00 00 00 - 8C 00 00 00 04 00 00 00
000000D0: E0 00 00 00 05 00 00 00 - F3 00 00 00 00 00 00 00
000000E0: 01 00 00 00 00 00 00 00 - 01 00 00 00 01 00 00 00
000000F0: 00 00 00 00 48 61 6C 6C - 6F 5F 57 65 6C 74 5F 61
00000100: 75 73 67 65 62 65 6E 00 - B8 04 00 00 00 BB 01 00
00000110: 00 00 B9 20 11 00 00 BA - 0B 00 00 00

Nach dem Speichern müsste die Datei nun "284 Byte"-groß sein.

Größe
siehe: hier

die Verarbeitungsunterbrechung auslösen

Allgemeines
siehe: hier

Abweichend von der Beschreibung im verlinkten Kapitel gilt:
Nach dem Durchlauf von der Funktion vom Kernel wird die Kontrolle über den CPU wieder an den Aufrufer, also die "Hallo_Welt_ausgeben"-Funktion übergeben.

Der Rückgabewert gibt an, wieviele Byte tatsächlich in die Konsole übertragen wurden. Da ich davon ausgehe, dass die Zeichenkette mit der Hilfe von 1 Funktionsaufruf vollständig in die Konsole übertragen werden kann, wertet diese "Hallo_Welt_ausgeben"-Funktion den Rückgabewert nicht aus.

Änderung
Erweitern Sie die Datei um die folgenden Werte:
                                                CD 80 rot hinterlegt: die Befehlsnummer vom Befehl "call to interrupt procedure"
gelb hinterlegt: der Wert

Ergebnis
Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00 02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00 01 00 00 00 - 20 01 00 00 20 11 00 00
00000080: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000090: 00 10 00 00 13 01 00 00 - 08 00 00 00 01 00 00 00
000000A0: 08 01 00 00 00 00 00 00 - 10 00 00 00 12 00 00 00
000000B0: 08 00 00 00 13 00 00 00 - 08 00 00 00 11 00 00 00
000000C0: 94 00 00 00 06 00 00 00 - 8C 00 00 00 04 00 00 00
000000D0: E0 00 00 00 05 00 00 00 - F3 00 00 00 00 00 00 00
000000E0: 01 00 00 00 00 00 00 00 - 01 00 00 00 01 00 00 00
000000F0: 00 00 00 00 48 61 6C 6C - 6F 5F 57 65 6C 74 5F 61
00000100: 75 73 67 65 62 65 6E 00 - B8 04 00 00 00 BB 01 00
00000110: 00 00 B9 20 11 00 00 BA - 0B 00 00 00 CD 80

Nach dem Speichern müsste die Datei nun "286 Byte"-groß sein.

Größe
siehe: hier

zurückkehren

Allgemeines

Das war soweit fast die komplette "Hallo_Welt_ausgeben"-Funktion. Damit das Programm, welches die Funktion aufruft, allerdings nicht wegen dem Aufruf abstürzt, werde ich noch den Befehl "return" ("ret") anhängen.

Dieser Befehl läd die Rücksprungadresse vom Stapel und löscht sie vom Stapel. Anschließend wird die Rücksprungadresse ins Befehlszeigerregister kopiert, damit an der Rücksprungadresse die Maschinencode-Ausführung fortgesetzt wird. Also direkt nach der Stelle, an welcher mit der Hilfe vom Befehl "call" die Rücksprungadresse auf den Stapel geladen wurde und zu dieser "Hallo_Welt_ausgeben"-Funktion gesprungen wurde.

Änderung

Erweitern Sie die Datei um den folgenden Wert:
                                                      C3 rot hinterlegt: die Befehlsnummer vom Befehl "return"

Ergebnis

Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00 02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00 01 00 00 00 - 20 01 00 00 20 11 00 00
00000080: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000090: 00 10 00 00 13 01 00 00 - 08 00 00 00 01 00 00 00
000000A0: 08 01 00 00 00 00 00 00 - 10 00 00 00 12 00 00 00
000000B0: 08 00 00 00 13 00 00 00 - 08 00 00 00 11 00 00 00
000000C0: 94 00 00 00 06 00 00 00 - 8C 00 00 00 04 00 00 00
000000D0: E0 00 00 00 05 00 00 00 - F3 00 00 00 00 00 00 00
000000E0: 01 00 00 00 00 00 00 00 - 01 00 00 00 01 00 00 00
000000F0: 00 00 00 00 48 61 6C 6C - 6F 5F 57 65 6C 74 5F 61
00000100: 75 73 67 65 62 65 6E 00 - B8 04 00 00 00 BB 01 00
00000110: 00 00 B9 20 11 00 00 BA - 0B 00 00 00 CD 80 C3

Nach dem Speichern müsste die Datei nun "287 Byte"-groß sein.

Größe

  1 Byte für die Befehlsnummer vom Befehl "return"
= 1 Byte für den kompletten Maschinenbefehl

den unbenutzten Zwischenraum füllen

Allgemeines

Da die Datei nun "287 Byte"-groß ist, wurden soweit die Bytes "0" bis "286" mit Werten gefüllt. Es wird allerdings erwartet, dass der Teil mit dem Maschinencode "288 Byte"-groß ist und damit erst beim Byte "287" endet.

Aus diesem Grund muss ein Zwischenraum geschaffen werden, damit der Teil mit dem Maschinencode auch tatsächlich erst beim Byte "287" endet und nicht schon beim Byte "286".

Ich diesem Byte vom Zwischenraum den Wert "90|h" zuweisen.

Änderung

Erweitern Sie die Datei um die folgenden Werte:
                                                         90 rot hinterlegt: der unbenutzte Zwischenraum

Ergebnis

Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00 02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00 01 00 00 00 - 20 01 00 00 20 11 00 00
00000080: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000090: 00 10 00 00 13 01 00 00 - 08 00 00 00 01 00 00 00
000000A0: 08 01 00 00 00 00 00 00 - 10 00 00 00 12 00 00 00
000000B0: 08 00 00 00 13 00 00 00 - 08 00 00 00 11 00 00 00
000000C0: 94 00 00 00 06 00 00 00 - 8C 00 00 00 04 00 00 00
000000D0: E0 00 00 00 05 00 00 00 - F3 00 00 00 00 00 00 00
000000E0: 01 00 00 00 00 00 00 00 - 01 00 00 00 01 00 00 00
000000F0: 00 00 00 00 48 61 6C 6C - 6F 5F 57 65 6C 74 5F 61
00000100: 75 73 67 65 62 65 6E 00 - B8 04 00 00 00 BB 01 00
00000110: 00 00 B9 20 11 00 00 BA - 0B 00 00 00 CD 80 C3 90

Nach dem Speichern müsste die Datei nun "288 Byte"-groß sein.

Größe

Der Zwischenraum füllt das Byte "279" und ist somit "1 Byte"-groß.

Übersicht

Dies war soweit der komplette Teil mit dem Maschinencode:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00 02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00 01 00 00 00 - 20 01 00 00 20 11 00 00
00000080: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000090: 00 10 00 00 13 01 00 00 - 08 00 00 00 01 00 00 00
000000A0: 08 01 00 00 00 00 00 00 - 10 00 00 00 12 00 00 00
000000B0: 08 00 00 00 13 00 00 00 - 08 00 00 00 11 00 00 00
000000C0: 94 00 00 00 06 00 00 00 - 8C 00 00 00 04 00 00 00
000000D0: E0 00 00 00 05 00 00 00 - F3 00 00 00 00 00 00 00
000000E0: 01 00 00 00 00 00 00 00 - 01 00 00 00 01 00 00 00
000000F0: 00 00 00 00 48 61 6C 6C - 6F 5F 57 65 6C 74 5F 61
00000100: 75 73 67 65 62 65 6E 00 - B8 04 00 00 00 BB 01 00
00000110: 00 00 B9 20 11 00 00 BA - 0B 00 00 00 CD 80 C3 90
rot hinterlegt: write - die Funktions-Identifikationskennung definieren
gelb hinterlegt: write - den Wert vom Parameter "Ziel_-_Konsole_-_Datenkanal_-_Identifikationskennung" definieren
grün hinterlegt: write - den Wert vom Parameter "Quelle_-_Nutzdaten_-_Adresse" definieren
blau hinterlegt: write - den Wert vom Parameter "Quelle_-_Länge_in_Zeichen" definieren
lila hinterlegt: write - die Verarbeitungsunterbrechung auslösen
rot hinterlegt: der Befehl "return"
gelb hinterlegt: der unbenutzte Zwischenraum

die Daten

die "Hallo Welt!"-Zeichenkette einfügen

Allgemeines

Mit der Hilfe von diesem Schritt wird der einzige Inhalt in die Daten eingefügt:
  • "Hallo Welt!"-Zeichenkette

Die "Hallo Welt!"-Zeichenkette wird also direkt am Anfang platziert.

Diese Zeichenkette muss nicht mit der Hilfe von einem Abschlusszeichen abgeschlossen werden, von welchem alle Bits auf "0|b" gesetzt sind, da beim Aufruf von der "write"-Funktion als Parameter angegeben wurde, wie lang die Zeichenkette ist.

Änderung

Erweitern Sie die Datei um die folgenden Werte:
00000120: 48 61 6C 6C 6F 20 57 65 - 6C 74 21 rot hinterlegt: die "Hallo Welt!"-Zeichenkette

Ergebnis

Die komplette Datei müsste jetzt wie folgt aussehen:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00 02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00 01 00 00 00 - 20 01 00 00 20 11 00 00
00000080: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000090: 00 10 00 00 13 01 00 00 - 08 00 00 00 01 00 00 00
000000A0: 08 01 00 00 00 00 00 00 - 10 00 00 00 12 00 00 00
000000B0: 08 00 00 00 13 00 00 00 - 08 00 00 00 11 00 00 00
000000C0: 94 00 00 00 06 00 00 00 - 8C 00 00 00 04 00 00 00
000000D0: E0 00 00 00 05 00 00 00 - F3 00 00 00 00 00 00 00
000000E0: 01 00 00 00 00 00 00 00 - 01 00 00 00 01 00 00 00
000000F0: 00 00 00 00 48 61 6C 6C - 6F 5F 57 65 6C 74 5F 61
00000100: 75 73 67 65 62 65 6E 00 - B8 04 00 00 00 BB 01 00
00000110: 00 00 B9 20 11 00 00 BA - 0B 00 00 00 CD 80 C3 90
00000120: 48 61 6C 6C 6F 20 57 65 - 6C 74 21

Nach dem Speichern müsste die Datei nun "299 Byte"-groß sein.

Größe

siehe: hier

Übersicht

Dies waren soweit
  • die kompletten Daten und
  • die komplette "Hallo_Welt.so"-Datei:
00000000: 7F 45 4C 46 01 01 01 03 - 00 00 00 00 00 00 00 00
00000010: 03 00 03 00 01 00 00 00 - 00 00 00 00 34 00 00 00
00000020: 00 00 00 00 00 00 00 00 - 34 00 20 00 03 00 00 00
00000030: 00 00 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00
00000040: 00 00 00 00 20 01 00 00 - 20 01 00 00 07 00 00 00
00000050: 00 10 00 00 02 00 00 00 - AC 00 00 00 AC 00 00 00
00000060: 00 00 00 00 34 00 00 00 - 34 00 00 00 06 00 00 00
00000070: 00 00 00 00 01 00 00 00 - 20 01 00 00 20 11 00 00
00000080: 00 00 00 00 0B 00 00 00 - 0B 00 00 00 06 00 00 00
00000090: 00 10 00 00 13 01 00 00 - 08 00 00 00 01 00 00 00
000000A0: 08 01 00 00 00 00 00 00 - 10 00 00 00 12 00 00 00
000000B0: 08 00 00 00 13 00 00 00 - 08 00 00 00 11 00 00 00
000000C0: 94 00 00 00 06 00 00 00 - 8C 00 00 00 04 00 00 00
000000D0: E0 00 00 00 05 00 00 00 - F3 00 00 00 00 00 00 00
000000E0: 01 00 00 00 00 00 00 00 - 01 00 00 00 01 00 00 00
000000F0: 00 00 00 00 48 61 6C 6C - 6F 5F 57 65 6C 74 5F 61
00000100: 75 73 67 65 62 65 6E 00 - B8 04 00 00 00 BB 01 00
00000110: 00 00 B9 20 11 00 00 BA - 0B 00 00 00 CD 80 C3 90
00000120: 48 61 6C 6C 6F 20 57 65 - 6C 74 21
rot hinterlegt: die "Hallo Welt!"-Zeichenkette

die Bibliothek benutzen

Allgemeines

Sofern Sie die Datei ein letztes Mal gespeichert haben, dann kann sie von nun an benutzt werden.

Das heißt, dass eine ausführbare Datei die Funktion "Hallo_Welt_ausgeben" aus der Bibliothek "Hallo_Welt.so" importieren kann und dann die Funktion aufrufen kann. Die Funktion aus der Bibliothek wird dann die "Hallo Welt!"-Zeichenkette in den Ausgabe-Datenkanal von der Konsole übertragen, welche der Anwendung zugewiesen wurde.

Hierfür muss der Pfad zur "Hallo_Welt.so"-Datei in der Anwendung angegeben sein, da Linux nicht automatisch im Verzeichnis, in welchem die Anwendung ist, nach Bibliotheken sucht. Alternativ kann die Datei "Hallo_Welt.so" in den Ordner "/lib/" verschoben werden.

Mit der Hilfe von diesem Dokument kann auch eine Anwendung geschrieben werden, welche den Import durchführt und dann die Funktion aufruft.

Alternativ können Sie auch ein bereits vorbereitetes Programm verwenden. Dieses Programm ist im Kapitel "weiteres Material zu diesem Thema - Programme" aufgelistet und macht das aller notwendigste:
Es importiert die Bibliothek mit dem Namen "Hallo_Welt.so" und ruft dann die Funktion mit dem Namen "Hallo_Welt_ausgeben" auf.

In der Anwendung ist außerdem angegeben, dass die Suche von der Bibliothek auch in dem Verzeichnis stattfinden soll, in welchem die Anwendung gespeichert ist.

In Linux hatte ich die Datei mit der Hilfe von einer Konsole aufgerufen.

Wenn
  • Sie ebenfalls so vorgehen wollen,
dann
  • helfen Ihnen vielleicht die folgenden Hinweise:
    • Wenn
      • Sie ins richtige Verzeichnis gewechselt haben und
        "Hallo_Welt.so_benutzen"
        eingeben, um das Programm zu starten,
      dann
      • bekommen Sie möglicherweise ein Fehlermeldung angezeigt, dass die Datei nicht gefunden wurde.

      Wenn
      • Sie stattdessen
        "./Hallo_Welt.so_benutzen"
        eingeben,
      dann
      • wird diese Fehlermeldung vermutlich verschwinden.
    • Wenn
      • die Datei nicht als ausführbare Datei erkannt wird,
      dann
      • können Sie mit der Eingabe
        "chmod +x Hallo_Welt.so_benutzen"
        die Ausführbarkeit von der Datei im Dateisystem eintragen.

      Wenn
      • Sie durch diese Eingabe eine Fehlermeldung angezeigt bekommen sollten, dass Sie keine Berechtigung haben, diese Änderung am Dateisystem vorzunehmen,
      dann
      • können Sie folgende Eingabe ausprobieren:
        "sudo chmod +x Hallo_Welt.so_benutzen"

      Das "sudo" bewirkt, dass der nachfolgende Befehl im Namen von einem sogenannten "Superuser"-Benutzerkonto ausgeführt wird.

      Ein "Superuser"-Benutzerkonto ist vergleichbar mit einem "Administrator"-Benutzerkonto in Windows.
    • Wenn
      • Sie die Datei mit einem Doppelklick starten sollten,
      dann
      • kann es sein, dass eine Konsole aufspringt, darin "Hallo Welt!" anzeigt, und sich dann gleich wieder schließt.

        Dies kann so schnell geschehen, dass es zwischen 2 Aktualisierungen von ihrem Bildschirm abläuft, und damit Ihnen die Meldung "Hallo Welt!" nie sichtbar wird.

      Wenn
      • Sie eine neue Konsole starten und
      • mit dieser das Programm aufrufen,
      dann
      • sollte sich die Konsole nicht von selbst schließen, sodass die Meldung "Hallo Welt!" auf dem Bildschirm bleibt, bis Sie die Konsole manuell schließen.


weiteres Material zu diesem Thema

Bibliotheken

Name Beschreibung: notwendiges Betriebssystem: Weblink:
vom Autor: von der Bibliothek:
Hallo_Welt Diese allgemeinfreie und kostenlose Bibliothek ist in dieser Dokumentation Schritt für Schritt beschrieben.

Funktionen:
  • "Hallo_Welt_ausgeben" - gibt "Hallo Welt!" aus
Linux ./Bibliothek/kompiliert/
Hallo_Welt.so

Dokumente

Name vom Sprache: Umfang vom Inhalt: Weblink:
Autor: Dokument:
Betriebssystem-spezifische - allgemeine Informationen - Dateiformate - ausführbare Dateien und Bibliotheken deutsch
  • enthält allgemeine Informationen über die Dateiformate von
    • ausführbaren Dateien und
    • Bibliotheken
    von verschiedenen Betriebssystemen
OnTheServer.de/Downloads/
Betriebssystem-spezifische - allgemeine Informationen - Funktionen von einem Betriebssystem
  • enthält allgemeine Informationen über die Funktionen von einem Betriebssystem, ohne dass dabei auf die Besonderheiten und Details von einem spezifischen Betriebssystem eingegangen wird
Betriebssystem-spezifische - Linux - Funktionen vom Betriebssystem
  • beschreibt Funktionen vom Kernel von Linux
  • zeigt, wie Funktionen vom Kernel von Linux benutzt werden können
Betriebssystem-spezifische - Linux - Lade-Programme - für das Dateiformat "executable and linking" ("elf")
  • zeigt, wie das Lade-Programm angeforderte Funktionen aus Bibliotheken bereit stellt
  • zeigt, mit welchen Inhalten der Arbeitsspeicher von einer Anwendung beim Programmstart gefüllt wird und wo diese in diesem Speicher platziert werden
x86-Architektur - Maschinensprache
  • enthält Hintergrundinformationen über Maschinensprachen
  • beschreibt die Maschinensprache von der x86-CPU-Architektur
  • zeigt, wie die Maschinensprache von der x86-CPU-Architektur benutzt werden kann
Zeichenkodierungen
  • beschreibt mehrere Zeichenkodierungen
Zeit - Zeitsysteme
  • beschreibt die Zeitsysteme
    • "gregorianischer Kalender",
    • das von der Norm "ISO 8601",
    • "julianischer Kalender",
    • "koordinierte, universelle Zeit",
    • "Unix-Zeitstempel" und
    • "Windows-Zeitstempel"
ARM Limited ARM ELF

Dokumentnummer: SWS ESPC 0003
englisch
  • beschreibt einige Felder und Tabellen; zum Teil mit eigenen Worten, zum Teil 1-zu-1 aus der offiziellen Beschreibung übernommen
  • gibt die ARM-Architektur-spezifischen Angaben an
Brian Raiter A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux
  • zeigt verschiedene Möglichkeiten, die Dateigröße von einer ausführbaren Datei zu reduzieren
  • eigene Erfahrungen und ein paar Hinweise, die ich sonst nirgends gefunden habe
muppetlabs.com/~breadbox/
software/tiny/teensy.html
The Santa Cruz Operation Incorporated System V - Application Binary Interface
  • beschreibt alle Felder und Tabellen; allerdings nur sehr knapp
  • keine eigene Erfahrungen
  • keine Prozessor-spezifischen Informationen
  • sehr aktuell
sco.com/developers/gabi/
System V - Application Binary Interface - Intel386 Architecture Processor Supplement
  • enthält die Prozessor-spezifischen Informationen für die "32 Bit"-Versionen von der x86-CPU-Architektur
  • enthält einige Fehler

Programme

Name vom Beschreibung: notwendiges Betriebssystem: Sprache: Weblink:
Autor: Programm:
Steve Hutchesson Binary to Hex Editor "Binary to Hex Editor" ist ein kostenloses, minimalistisches, "20 Kilobyte"-großes Programm mit grafischer Benutzeroberfläche. Das Programm ist nicht allgemeinfrei und den Quelltext bekommt man glaube ich auch nicht.

Funktionen:
  • Hexeditor, mit welchem man Rohdaten-Dateien öffnen und editieren kann
  • Speicherung von der hexadezimal-Darstellung von einer Rohdaten-Datei als ASCII-kodierte Textdatei
  • Speicherung von einer hexadezimal-Darstellung als Rohdaten-Datei

Nachteilhaft ist allerdings, dass es lange braucht und viel Arbeitsspeicher notwendig ist, um große Dateien zu öffnen.
Windows englisch movsd.com/download/b2hedit.zip
Hallo_Welt Dieses allgemeinfreie und kostenlose Konsolen-Programm ist in dieser Dokumentation Schritt für Schritt beschrieben.

Funktionen:
  • gibt "Hallo Welt!" aus
Linux deutsch ./ausführbare Datei/
kompiliert/Hallo_Welt
Hallo_Welt.so_benutzen - kompilierte Datei Dieses allgemeinfreie und kostenlose Konsolen-Programm ist ein Hilfsprogramm um die Bibliothek "Hallo_Welt" zu benutzen.

Funktionen:
  • importiert die Bibliothek "Hallo_Welt"
  • ruft die Funktion "Hallo_Welt_ausgeben" auf
Linux ./Bibliothek/kompiliert/
Hallo_Welt.so_benutzen

Aufnahme von weiteren Materialien

Wenn dann
Sie dürfen durchaus auch Ihre eigenen Materialien zuschicken.

Bei den zugesendeten Materialien werde ich beurteilen, ob sie sich für die Auflistung eignen. Manche Materialien werden nicht aufgenommen, weil beispielsweise ein Hinweis enthalten ist, dass Es besteht kein Anspruch darauf, dass zugesendete Materialien aufgenommen werden.

Link zum Kapitel "wie man den Autor kontaktiert".