Stand: 24. April 2016
über dieses Dokumentwas es hierin gibt
wo man die neuste Version bekommt
wie man den Verfasser kontaktiert
welche gesetzlichen Einschränkungen es gibtLizenzlosigkeit
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 AbschnittenIdentifikationZweckKopfzeile
Anforderungen an den Inhalt von einem Hauptabschnitt
Aufbau
Bezeichnung
PositionZweckProgrammabschnitte
Anforderungen an den Inhalt von einem Hauptabschnitt
Aufbau
Bezeichnung
PositionAufbau
HauptabschnitteZweckUnterabschnitte
Kopfzeilen-TabelleZweckInhalte
Aufbau
Bezeichnung
PositionBezeichnung
PositionZweck
Kopfzeilen-TabelleZweckInhalte
Aufbau
Bezeichnung
PositionBezeichnung
Position
TabellenTabelle für Adressen-ReparaturenZweckTabelle für Dinge
Aufbau
Bezeichnung
PositionZweckTabelle für Exporte und Importe
Aufbau
Bezeichnung
PositionZweckTabelle für Prüfsummen
Anforderungen an den Inhalt von einem Hauptabschnitt
Aufbau
Bezeichnung
PositionZweckTabelle für Zeichenketten
AufbauAllgemeinesBezeichnung
Kopfzeile
Auflistung von Anfangspositionen
Auflistung von FolgepositionenZweck
Aufbau
Bezeichnung
ein "Hallo Welt!"-ProgrammBeschreibung
notwendige Voraussetzungen
Vorbereitungdie Datei anlegenAnmerkungen zum Speichern
die Datei öffnen
elf-Zusatzdatenden Identifikations-Abschnitt schreibender MaschinencodeAllgemeinesden Kopfzeilen-Abschnitt schreiben
Änderung
Ergebnis
GrößeAllgemeinesKopfzeilen-Tabelle für Hauptabschnitte
Änderung
Ergebnis
Größedie Kopfzeile für den Haupt-Programmabschnitt mit dem Anfang von der Datei und dem Maschinencode schreibenÜbersichtAllgemeinesdie Kopfzeile für den Haupt-Programmabschnitt mit den Daten schreiben
Änderung
Ergebnis
GrößeAllgemeines
Änderung
Ergebnis
Größedie Funktion "write"die Datendie Funktions-Identifikationskennung definierendie Funktion "exit"Allgemeinesden Wert vom Parameter "Ziel_-_Konsole_-_Datenkanal_-_Identifikationskennung" definieren
Änderung
Ergebnis
GrößeAllgemeinesden Wert vom Parameter "Quelle_-_Nutzdaten_-_Adresse" definieren
Änderung
Ergebnis
GrößeAllgemeinesden Wert vom Parameter "Quelle_-_Nutzdaten_-_Länge_in_Byte" definieren
Änderung
Ergebnis
GrößeAllgemeinesdie Verarbeitungsunterbrechung auslösen
Änderung
Ergebnis
GrößeAllgemeines
Änderung
Ergebnis
Größedie Funktions-Identifikationskennung definierenden unbenutzten Zwischenraum füllenAllgemeinesden Wert vom Parameter "Ziel_-_Exitcode" definieren
Änderung
Ergebnis
GrößeAllgemeinesdie Verarbeitungsunterbrechung auslösen
Änderung
Ergebnis
GrößeAllgemeines
Änderung
Ergebnis
GrößeAllgemeinesÜbersicht
Änderung
Ergebnis
Größedie "Hallo Welt!"-Zeichenkette einfügendas Programm startenAllgemeinesÜbersicht
Änderung
Ergebnis
GrößeAllgemeines
eine Bibliothek mit einer "Hallo Welt!"-FunktionBeschreibung
notwendige Voraussetzungen
Vorbereitungdie Datei anlegenAnmerkungen zum Speichern
die Datei öffnen
elf-Zusatzdatenden Identifikations-Abschnitt schreibender Maschinencode
den Kopfzeilen-Abschnitt schreibenAllgemeinesdie Kopfzeilen-Tabelle für Hauptabschnitte
Änderung
Ergebnis
Größedie Kopfzeile für den Haupt-Programmabschnitt mit dem Anfang von der Datei und dem Maschinencode schreibendie Tabelle für Adressen-Reparaturen schreibenAllgemeinesdie Kopfzeile für den Haupt-Programmabschnitt mit der Tabelle für Exporte und Importe schreiben
Änderung
Ergebnis
GrößeAllgemeinesdie Kopfzeile für den Haupt-Programmabschnitt mit den Daten schreiben
Änderung
Ergebnis
GrößeAllgemeines
Änderung
Ergebnis
GrößeAllgemeinesdie Tabelle für Dinge schreiben
Änderung
Ergebnis
GrößeAllgemeinesdie Tabelle für Exporte und Importe schreiben
Änderung
Ergebnis
GrößeAllgemeinesdie Tabelle für Prüfsummen schreiben
Änderung
Ergebnis
GrößeAllgemeinesdie Tabelle für Zeichenketten schreiben
Änderung
Ergebnis
GrößeAllgemeinesÜbersicht
Änderung
Ergebnis
Größe
die Funktion "write"die Datendie Funktions-Identifikationskennung definierenzurückkehrenAllgemeinesden Wert vom Parameter "Ziel_-_Konsole_-_Datenkanal_-_Identifikationskennung" definieren
Änderung
Ergebnis
GrößeAllgemeinesden Wert vom Parameter "Quelle_-_Nutzdaten_-_Adresse" definieren
Änderung
Ergebnis
GrößeAllgemeinesden Wert vom Parameter "Quelle_-_Nutzdaten_-_Länge_in_Byte" definieren
Änderung
Ergebnis
GrößeAllgemeinesdie Verarbeitungsunterbrechung auslösen
Änderung
Ergebnis
GrößeAllgemeines
Änderung
Ergebnis
GrößeAllgemeinesden unbenutzten Zwischenraum füllen
Änderung
Ergebnis
GrößeAllgemeinesÜbersicht
Änderung
Ergebnis
Größedie "Hallo Welt!"-Zeichenkette schreibendie Bibliothek benutzenAllgemeinesÜbersicht
Änderung
Ergebnis
GrößeAllgemeines
weiteres Material zu diesem ThemaBibliotheken
Dokumente
Programme
Aufnahme von weiteren Materialien
was es hierin gibt
Dieses Dokument
- beschreibt das Dateiformat von
für die "32 Bit"-Versionen von Linux, also das Dateiformat "executable and linking format" ("elf") in der "32 Bit"-Variante und
- ausführbaren Dateien und
- Bibliotheken ("*.so"-Dateien)
- zeigt Schritt für Schritt, wie mit der Hilfe von einem Hex-Editor
geschrieben werden kann.
- ein "Hallo Welt!"-Programm und
- eine Bibliothek mit einer "Hallo Welt!"-Funktion
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 überenthält.
- ausführbare Dateien und
- Bibliotheken
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üraufzuerlegen. Von den Einschränkungen wären lediglich neue Versionen betroffen. Was bisher veröffentlicht wurde, erhält keine weiteren Einschränkungen.
- die Veränderung oder/und
- die Verbreitung
Von diesem Recht werde ich hoffentlich nie gebrauch machen müssen.
Ü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.
Wenndann
- kein Zwischenraum benutzt werden soll,
- können die Programmabschnitte auch beim Offset "52" beginnen.
die Details von den einzelnen Abschnitten
Identifikation
Zweck
Dieser Abschnitt dient dazu, dass Anwendungen erkennen können,Außerdem sind hierin allgemeine Informationen enthalten, wie zum Beispiel die verwendete Datenkodierung von Zahlen und die Version von der Kopfzeile.
- um welches Dateiformat es sich handelt und
- welchen Zweck diese Datei erfüllen soll.
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: 231big 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: 231little 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_GNU3 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 Byte9 Byte
bis
15 ByteGesamtgröß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 machtvon der Datei.
- allgemeine Angaben über den Inhalt
- und Auskünfte über den Aufbau
Durch die Angaben über den Aufbau von der Datei werden, unter anderem,von anderen Inhalten genannt.
- Größen und
- Positionen
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 Byte16 Byte
bis
17 Bytegibt die notwendige CPU-Hardware-Architektur an e_machine 2 Byte
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 Byte18 Byte
bis
19 ByteLaut 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 obein Fehler aufgetreten ist.
- beim Kompilieren oder
- beim Linken
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 Byte20 Byte
bis
23 ByteGibt den Offset in Byte an, wenn er in den Arbeitsspeicher geladen wurde.
- vom Anfang vom linearen Speicher
- bis zum ersten Byte vom Maschinencode, welcher nach dem Abschließen vom Lade-Vorgang ausgeführt werden soll,
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.
Wenndann
- es keinen solchen Maschinencode gibt,
- wird für dieses Feld der Wert "0" angegeben.
e_entry 4 Byte 8 Byte
bis
11 Byte24 Byte
bis
27 ByteGibt den Offset in Byte an, wenn sie noch in der Datei auf der Festplatte/Diskette/CD/was auch immer ist.
- vom Anfang von der Datei
- bis zum ersten Byte von der "Kopfzeilen-Tabelle für Hauptabschnitte",
Wenndann
- diese Tabelle nicht existiert,
- wird für dieses Feld der Wert "0" angegeben.
e_phoff 4 Byte 12 Byte
bis
15 Byte28 Byte
bis
31 ByteGibt den Offset in Byte an, wenn sie noch in der Datei auf der Festplatte/Diskette/CD/was auch immer ist.
- vom Anfang von der Datei
- bis zum ersten Byte von der "Kopfzeilen-Tabelle für Unterabschnitte",
Wenndann
- diese Tabelle nicht existiert,
- wird für dieses Feld der Wert "0" angegeben.
e_shoff 4 Byte 16 Byte
bis
19 Byte32 Byte
bis
35 ByteDie 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 Byte36 Byte
bis
39 ByteGibt die Größe vom Abschnitt "Identifikation"zusammengerechnet in Byte an. Dies ist also immer der Wert "52|d = 34|h".
+ vom Abschnitt "Kopfzeile"
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 Byte40 Byte
bis
41 ByteGibt 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 Byte42 Byte
bis
43 ByteGibt 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║ │
╠══════════════════════════════════════╣┐ │
║ ║│ │
╟──────────────────────────────────────╢│ │
║ ║│ │
╟──────────────────────────────────────╢├◄┘
║ ║│
╟──────────────────────────────────────╢│
║ ║│
╚══════════════════════════════════════╝┘
Wenndann
- diese Tabelle nicht existiert,
- wird für dieses Feld der Wert "0" angegeben.
e_phnum 2 Byte 28 Byte
bis
29 Byte44 Byte
bis
45 ByteGibt 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ß.
Wenndann
- diese Tabelle nicht existiert,
- scheint das Lade-Programm von Linux diesen Wert nicht auszuwerten.
e_shentsize 2 Byte 28 30 Byte
bis
31 Byte46 Byte
bis
47 ByteGibt 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. Wenndann
- mehr Einträge in der "Kopfzeilen-Tabelle für Unterabschnitte" stehen sollen,
- wird dies anderst angegeben.
Diese andere Angabe-Form ist in dieser Dokumentation allerdings nicht beschrieben.
Wenndann
- diese Tabelle nicht existiert,
- wird für dieses Feld der Wert "0" angegeben.
e_shnum 2 Byte 32 Byte
bis
33 Byte48 Byte
bis
49 ByteGibt 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.
Wenndann
- in der Datei keine Unter-Programmabschnitte enthalten sind,
- wird für dieses Feld der Wert "0" angegeben.
e_shstrndx 2 Byte 34 Byte
bis
35 Byte50 Byte
bis
51 ByteGesamtgröß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
- 1 Inhalt und
- 1 Kopfzeile.
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.
Wenndann
- also beispielsweise 2 Inhalte von Unterabschnitten jeweils gelesen werden müssen, aber nicht beschrieben werden dürfen,
- kann für diese beiden Inhalte von Unterabschnitten 1 gemeinsamer Inhalt von einem Hauptabschnitt verwendet werden.
In einer Datei kann es auch mehrere Unter-Programmabschnitte geben. Jeder Unter-Programmabschnitt hat
- 1 Inhalt und
- 1 Kopfzeile.
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 Beispielhat. 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.
- "ausführbar" oder
- "beschreibbar",
Ihre Kopfzeilen machen Auskünfte, wie zum Beispiel über
- die gewünschte Anfangsadresse im Segment,
- die Größe und
- die Zugriffsberechtigungen, wie zum Beispiel
von diesem Bereich vom Arbeitsspeicher.
- "ausführbar" oder
- "beschreibbar",
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önnenin dieser Kopfzeilen-Tabelle verstreut sein.
- darüber,
- darunter oder
- zwischendrin
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:
- Der Rest von dieser Kopfzeile soll ignoriert werden.
- Der Inhalt vom Hauptabschnitt soll nicht in den Arbeitsspeicher geladen werden.
- Der Inhalt vom Hauptabschnitt existiert nicht.
PT_NULL 0
- Der Inhalt vom Hauptabschnitt soll in den Arbeitsspeicher geladen werden.
PT_LOAD 1
- Am Anfang von diesem Inhalt vom Hauptabschnitt ist eine Tabelle für Exporte und Importe platziert.
PT_DYNAMIC 2
- Am Anfang von diesem Inhalt vom Hauptabschnitt ist eine Zeichenkette platziert, welche
von einer bestimmten Bibliothek angibt.
- den Pfad und
- den Dateinamen
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:Wenndann
- Linux als "32 Bit"-Version kompiliert wurde,
Ansonsten wenn
- ist eine Bibliothek mit dem folgenden standardmäßigen
verfügbar:
- Pfad und
- Dateinamen
/lib/ld-linux.so.2dann
- Linux als "64 Bit"-Version kompiliert wurde,
- ist eine Bibliothek mit dem folgenden standardmäßigen
verfügbar:
- Pfad und
- Dateinamen
/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. Wenndann
- also beispielsweise die Anwendung Bibliotheken importiert, welche selbst Dinge aus anderen Bibliotheken benötigen,
- 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 FallDer Inhalt vom Unterabschnitt muss in der Schnittmenge enthalten sein und ist damit nur 1 mal vorhanden.
- sowohl in der Datei auf der Festplatte/Diskette/CD/was auch immer
- als auch im Arbeitsspeicher.
p_type 4 Byte 0 Byte
bis
3 ByteGibt den Offset in Byte an, wenn er noch in der Datei auf der Festplatte/Diskette/CD/was auch immer ist.
- vom Anfang von der Datei
- bis zu dem ersten Byte vom Inhalt vom Hauptabschnitt,
Der Wert von diesem Feld muss restlos durch den Wert vom Feld "p_align" teilbar sein. Wenndann
- also beispielsweise für das Feld "p_align" der Wert "8|d" angegeben wird,
- kann für den Wert vom Feld "p_offset" der Wert
angegeben werden.
- "0|d",
- "8|d",
- "16|d",
- "24|d" oder
- ...
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,auf "0|d" zu bringen. Beide Ansätze sind nicht ganz unproblematisch, daher ist es eine Abwägung dessen, ob
- den links neben dem Operator stehenden Teil "(p_vaddr - p_offset)" oder
- den rechts neben dem Operator stehenden Teil "(p_align - 1|d)"
- 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. Wenndann
- im Inhalt Daten gespeichert sind, welche der Prozessor irgendwann lesen soll,
- 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.
Wenndann
- die Bedingung nicht erfüllt ist,
- 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 ByteGibt den gewünschten Offset in Byte an, wenn er in den Arbeitsspeicher geladen wurde.
- vom Anfang vom linearen Speicher
- bis zum ersten Byte vom Inhalt vom Hauptabschnitt an,
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. Wenndann
- also beispielsweise für das Feld "p_align" der Wert "8|d" angegeben wird,
- kann für den Wert vom Feld "p_vaddr" der Wert
angegeben werden.
- "0|d",
- "8|d",
- "16|d",
- "24|d" oder
- ...
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 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. Wenndann
- der Wert "0|d" angegeben war,
- wurde der Lade-Vorgang abgebrochen und die Fehlermeldung "Segmentation fault" wurde ausgegeben.
p_vaddr 4 Byte 8 Byte
bis
11 ByteDieses Feld ist unbenutzt.
Alle Bits von diesen Bytes sollten auf "0|b" gesetzt werden.p_paddr 4 Byte 12 Byte
bis
15 ByteGibt 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.
Wenndann
- beispielsweise der komplette Inhalt vom Hauptabschnitt für Daten vorgesehen ist, welche erst zur Laufzeit zum ersten Mal mit Werten definiert werden,
- kann diese Größenangabe den Wert "0|d" haben.
Wenndann
- der Inhalt vom Hauptabschnitt in den Arbeitsspeicher geladen werden soll,
- darf dieser Wert maximal dem Wert vom Feld "p_memsz" entsprechen.
p_filesz 4 Byte 16 Byte
bis
19 ByteGibt die Größe vom Inhalt vom Hauptabschnitt in Byte an, welche er im Arbeitsspeicher haben soll.
Wenndann
- 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,
- kann der Wert "0|d" als Größe angegeben werden.
Wenndann
- für dieses Feld ein größerer Wert angegeben ist, als im Feld "p_filesz" angegeben ist,
- 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 ByteDie 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.
Wenndann
- eine RAM-Seite von mehreren Inhalten von Hauptabschnitten belegt wird,
- werden die Zugriffsberechtigungen mit der Hilfe von einer logischen oder-Verknüpfung auf einer Bit-Basis kombiniert.
Wenndann
- also beispielsweise für einen Inhalt von einem Hauptabschnitt die Leseberechtigung erteilt wurde und
- für einen anderen Inhalt von einem Hauptabschnitt
erteilt wurde,
- sowohl die Leseberechtigung,
- als auch die Schreibberechtigung
- können die Daten von der RAM-Seite
werden.
- sowohl beschrieben,
- als auch gelesen
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.
Wenndann
- Maschinencode in diesem Inhalt vom Hauptabschnitt vorhanden ist, welcher möglicherweise irgendwann während der Laufzeit ausgeführt werden soll,
- 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 werden.
- nicht ausgeführt
Dieser Bereich vom Speicher darf vom Prozess werden.
- ausgeführt
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 werden.
- nicht beschrieben
Dieser Bereich vom Speicher darf vom Prozess werden.
- beschrieben
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 werden.
- nicht gelesen
Dieser Bereich vom Speicher darf vom Prozess werden.
- gelesen
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
erteilt. Ob die Berechtigung zum Beschreiben erteilt wird, habe ich nicht getestet.
- sowohl die Berechtigung zum Ausführen,
- als auch die Berechtigung zum Lesen
p_flags 4 Byte 24 Byte
bis
27 ByteLeider 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.
Wenndann
- ein Inhalt von einem Hauptabschnitt in den Arbeitsspeicher geladen wird,
- 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.
Wenndann
- beispielsweise ein Inhalt von einem Hauptabschnitt 5.130 Byte groß ist,
- 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.
Wenndann
- allerdings die zusätzlichen Bytes am Ende vom Inhalt vom Hauptabschnitt nicht gewünscht sind,
- kann laut der offiziellen Beschreibung vom Dateiformat für dieses Feld
angegeben werden.
- entweder der Wert "0|d"
- oder der Wert "1|d"
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.
Wenndann
- die Bedingung nicht erfüllt ist,
- 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 ByteGesamtgröß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
- in der Datei beginnt, solange sie noch auf der Festplatte/Diskette/CD/was auch immer ist, ist im Feld "p_offset" von der Kopfzeile in der Kopfzeilen-Tabelle für Hauptabschnitte angegeben, welche Auskünfte über diesen Inhalt macht.
- im Arbeitsspeicher beginnen soll, nachdem der Lade-Vorgang abgeschlossen ist, ist im Feld "p_vaddr" von der Kopfzeile in der Kopfzeilen-Tabelle für Hauptabschnitte angegeben, welche Auskünfte über diesen Inhalt macht.
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
- die gewünschte Anfangsadresse im Segment und damit indirekt im Inhalt von einem Hauptabschnitt,
- die Größe und
- von welcher Art der Inhalt vom Unterabschnitt ist.
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.
Wenndann
- in dieser Tabelle Kopfzeilen aufgelistet sind,
- 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. Wenndann
- also beispielsweise
vorhanden sind,
- die eine unbenutzte Kopfzeile und
- 2|d benutzte Kopfzeilen
- 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. Wenndann
- einer von diesen besonderen Namen gewählt wird,
- wird dadurch angegeben, was in diesem Inhalt vom Unterabschnitt enthalten ist.
Folgende Namen haben jeweils eine besondere Bedeutung:
Anforderungen: Bedeutung: Name: 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
- der Inhalt vom Unterabschnitt in den Arbeitsspeicher geladen wird.
- Im Feld "sh_type" muss
- der Wert "8|h" gespeichert werden, was bedeutet, dass
- der Inhalt vom Unterabschnitt nicht in der Datei existiert, und
- der Inhalt vom Unterabschnitt allerdings im Arbeitsspeicher existieren kann.
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 wegenexistiert.
- einem Datensatz,
- einer Konstante oder
- einer Variable
In diesem Dateiformat ist der Bereich allerdings nicht aufbegrenzt, sondern dient allgemein für (mehrere) Daten.
- 1|d Datensatz,
- 1|d Konstante oder
- 1|d Variable
.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
- der Inhalt vom Unterabschnitt in den Arbeitsspeicher geladen wird.
- 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:
- Im Feld "sh_flags" muss
- das Bit mit der Wertigkeit "21|d" auf "1|b" gesetzt werden, was bedeutet, dass
- der Inhalt vom Unterabschnitt in den Arbeitsspeicher geladen wird.
- Im Feld "sh_info" muss
- der Wert "0|h" gespeichert werden.
- 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
- der Wert "6|h" gespeichert werden, was bedeutet, dass
- in diesem Inhalt vom Unterabschnitt eine Tabelle für Exporte und Importe enthalten ist.
an die Kopfzeile vom Inhalt vom Hauptabschnitt:
- Im Feld "p_type" muss
- der Wert "2|h" gespeichert werden, was bedeutet, dass
- im Inhalt vom Hauptabschnitt ein Inhalt von einem Unterabschnitt enthalten ist, welcher den Namen ".dynamic" hat.
- Im Feld "p_flags" muss
- das Bit mit der Wertigkeit "21|d" auf "1|b" gesetzt werden, was bedeutet, dass
- der Inhalt vom Hauptabschnitt beschrieben werden darf.
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
- der Inhalt vom Unterabschnitt in den Arbeitsspeicher geladen wird.
- 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
- der Wert "B|h" gespeichert werden, was bedeutet, dass
- in diesem Inhalt vom Unterabschnitt eine Tabelle für Dinge enthalten ist und
- die Dinge nur für Export- und Import-bezügliche Aufgaben verwendet werden dürfen.
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:
- Im Feld "sh_flags" muss
- das Bit mit der Wertigkeit "21|d" auf "1|b" gesetzt werden, was bedeutet, dass
- der Inhalt vom Unterabschnitt in den Arbeitsspeicher geladen wird.
- Im Feld "sh_info" muss
- der Wert "0|h" gespeichert werden.
- 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
- der Wert "5|h" gespeichert werden, was bedeutet, dass
- in diesem Inhalt vom Unterabschnitt eine Tabelle für Prüfsummen enthalten ist.
Dieser Inhalt vom Unterabschnitt enthält eine Tabelle für Prüfsummen. .hash an die restliche Kopfzeile vom Inhalt vom Unterabschnitt:
an die Kopfzeile vom Inhalt vom Hauptabschnitt:
- Im Feld "p_type" muss
- der Wert "3|h" gespeichert werden, was bedeutet, dass
- im Inhalt vom Hauptabschnitt ein Inhalt von einem Unterabschnitt enthalten ist, welcher den Namen ".interp" hat.
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 von einer bestimmten Bibliothek angibt.
- den Pfad und
- den Dateinamen
Weitere Informationen zusind hier.
- dieser Bibliothek und
- dieser Zeichenkette
.interp 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
- der Wert "9|h" gespeichert werden, was bedeutet, dass
- in diesem Inhalt vom Unterabschnitt eine Tabelle für Adressen-Reparaturen enthalten ist.
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.
Wenndann
- also Beispielsweise die Speicherzellen, in welchen die zu reparierenden Adressen enthalten sind,
- im Maschinencode sind und
- der Maschinencode im Inhalt vom Unterabschnitt enthalten ist, welcher den Namen ".text" hat,
- ist der Name von diesem Inhalt vom Unterabschnitt ".rel.text".
.rel$name 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
- der Inhalt vom Unterabschnitt in den Arbeitsspeicher geladen wird.
- Im Feld "sh_type" muss
- der Wert "1|h" gespeichert werden.
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:
- Im Feld "sh_type" muss
- der Wert "3|h" gespeichert werden, was bedeutet, dass
- in diesem Inhalt vom Unterabschnitt eine Tabelle für Zeichenketten enthalten ist.
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:
- Im Feld "sh_type" muss
- der Wert "3|h" gespeichert werden, was bedeutet, dass
- in diesem Inhalt vom Unterabschnitt eine Tabelle für Zeichenketten enthalten ist.
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
- der Wert "2|h" gespeichert werden, was bedeutet, dass
- in diesem Inhalt vom Unterabschnitt eine Tabelle für Dinge enthalten ist.
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
- der Inhalt vom Unterabschnitt in den Arbeitsspeicher geladen wird.
- 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 ByteDieses 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_HIUSER80 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 ByteDieses 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 habenTendenziell ist es besser, die Werte von den unbekannten Bits auf "0|b" zu setzen, als sie auf "1|b" zu setzen.
- manche Bits bereits eine Bedeutung bekommen und
- manche Bits noch keine Bedeutung bekommen.
sh_flags 4 Byte 8 Byte
bis
11 ByteGibt 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 ║║
║╚════════════════════════════════╝║
╟──────────────────────────────────╢
║ ║
╚══════════════════════════════════╝
Wenndann
- dieser Inhalt vom Unterabschnitt nicht in den Arbeitsspeicher geladen werden soll,
- wird dies durch den Wert "0|d" angegeben.
Der Wert von diesem Feld muss restlos durch den Wert vom Feld "sh_addralign" teilbar sein. Wenndann
- also beispielsweise für das Feld "sh_addralign" der Wert "8|d" angegeben wird,
- kann für den Wert vom Feld "sh_addr" der Wert
angegeben werden.
- "0|d",
- "8|d",
- "16|d",
- "24|d" oder
- ...
sh_addr 4 Byte 12 Byte
bis
15 ByteGibt 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. Wenndann
- also beispielsweise für das Feld "sh_addralign" der Wert "8|d" angegeben wird,
- kann für den Wert vom Feld "sh_offset" der Wert
angegeben werden.
- "0|d",
- "8|d",
- "16|d",
- "24|d" oder
- ...
sh_offset 4 Byte 16 Byte
bis
19 ByteGibt 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.
Wenndann
- beispielsweise der komplette Inhalt vom Unterabschnitt für Daten vorgesehen ist, welche erst zur Laufzeit zum ersten Mal mit Werten definiert werden,
- kann diese Größenangabe kann den Wert "0|d" haben.
sh_size 4 Byte 20 Byte
bis
23 ByteWas 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:
- Diese Dinge sind in der Tabelle für Dinge enthalten.
- Auf diese Dinge beziehen sich die
welche in der Tabelle für Exporte und Importe enthalten sind.
- Exporte oder/und
- Importe,
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
Wenndann gilt:
- im Feld "sh_type" ein anderer Wert angegeben wurde, als in der obrigen Tabelle aufgelistet ist,
- Wenn
dann
- ein Wert angegeben wurde, welcher in dieser Dokumentation als gültiger Wert für das Feld "sh_type" genannt ist,
Ansonsten
- 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.
- 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 ByteWas 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
Wenndann gilt:
- im Feld "sh_type" ein anderer Wert angegeben wurde, als in der obrigen Tabelle aufgelistet ist,
- Wenn
dann
- ein Wert angegeben wurde, welcher in dieser Dokumentation als gültiger Wert für das Feld "sh_type" genannt ist,
Ansonsten
- 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.
- 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 ByteEine konstante Größe für jede Seite vom Inhalt vom Unterabschnitt in Byte, wenn er in den Arbeitsspeicher geladen wurde.
Wenndann
- ein Inhalt von einem Unterabschnitt in den Arbeitsspeicher geladen wird,
- 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.
Wenndann
- beispielsweise ein Inhalt von einem Unterabschnitt 5.130 Byte groß ist,
- füllt dieser im Arbeitsspeicher bei 4.096 Byte pro Seite 2 Seiten. Der Inhalt benötigt dann 8.196 Byte im Arbeitsspeicher.
Wenndann
- allerdings die zusätzlichen Bytes am Ende vom Inhalt vom Unterabschnitt nicht gewünscht sind,
- kann für dieses Feld
angegeben werden.
- entweder der Wert "0|h"
- oder der Wert "1|h"
sh_addralign 4 Byte 32 Byte
bis
35 ByteWenn dann
- dieser Inhalt vom Unterabschnitt dazu verwendet wird, um eine Tabelle zu speichern, von welcher die einzelnen Einträge alle gleich groß sind,
- 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.
Wenndann
- in diesem Inhalt vom Unterabschnitt eine Tabelle für Zeichenketten gespeichert ist, in welcher Zeichenketten enthalten sind, deren Zeichen alle gleich groß sind,
- wird durch den Wert von diesem Feld die Größe von einem Zeichen in Byte angegeben.
Wenndann
- in diesem Inhalt vom Unterabschnitt keine Tabelle enthalten ist, oder die Einträge von der Tabelle unterschiedliche Größen haben können,
- wird dies durch den Wert "0|h" angegeben.
sh_entsize 4 Byte 36 Byte
bis
39 ByteGesamtgröß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
- in der Datei beginnt, solange die Datei noch auf der Festplatte/Diskette/CD/was auch immer ist, ist im Feld "sh_offset" von der Kopfzeile in der Kopfzeilen-Tabelle für Unterabschnitte angegeben, welche Auskünfte über diesen Inhalt macht.
- im Arbeitsspeicher beginnen soll, nachdem der Lade-Vorgang abgeschlossen ist, ist im Feld "sh_addr" von der Kopfzeile in der Kopfzeilen-Tabelle für Unterabschnitte angegeben, welche Auskünfte über diesen Inhalt macht.
Tabellen
Tabelle für Adressen-Reparaturen
Zweck
In diesem Dateiformat wird zwischenunterschieden. Der Unterschied besteht darin,
- der vom Erzeuger gewünschten Basisadresse und
- der vom Lade-Programm gewählten Basisadresse
- 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.
Wenndann
- es nicht möglich ist, die Inhalte dort zu platzieren, wo sich der Erzeuger die Platzierung gewünscht hat,
- 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
dann
- 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,
- 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 wenndann
- die aktuelle Version vom Lade-Programm alle momentan existierenden Maschinenbefehle kennt,
- 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.
Wenndann
- 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,
- 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
dann
- ein Ding aus einer Bibliothek importiert werden soll,
- 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 kanndie Adresse bereitgestellt werden, indem
- der Anwendung, oder
- der Bibliothek, welche selbst ein Ding aus einer anderen Bibliothek angefordert hat,
wird.
- eine bestehende Adresse repariert oder
- eine leere Speicherzelle mit der zur Laufzeit gültigen Adresse gefüllt
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,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.
- in welchen die zu reparierenden Adressen gespeichet sind, und
- auf welche sich diese Tabelle für Adressen-Reparaturen bezieht,
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 (eax) to ║║ ││
║╟────────────────────────────────────╫╫◄┘│
║║ Adresse #1 ║║ │
║╟────────────────────────────────────╢║ │
║║ increment eax by 1 ║║ │
║╟────────────────────────────────────╢║ │
║║ jump to ║║ │
║╟────────────────────────────────────╫╫◄─┘
║║ Adresse #2 ║║
║╚════════════════════════════════════╝║
╟──────────────────────────────────────╢
║ ║
╚══════════════════════════════════════╝r_offset 4 Byte 0 Byte
bis
3 ByteWenn dann gilt das Folgende:
- im Feld "EI_DATA" vom Identifikations-Abschnitt der Wert "1|h" angegeben wurde, was bedeutet, dass die Datenkodierung "little endian" verwendet wird,
WennAnsonsten wenndann gilt das Folgende:
- 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,
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
= SpeicherzelleninhaltneuMit 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, istund dann die absolute Adresse
- die absolute Adresse zunächst in ein Register zu kopiert oder
- die absolute Adresse in einer Speicherzelle im Arbeitsspeicher zu speichern
ins Befehlszeigerregister zu kopieren, wodurch der Sprung durchgeführt wird.
- aus dem Register oder
- aus der Speicherzelle im Arbeitsspeicher
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.
Wenndann
- Speicherzelleninhaltalt nicht benötigt wird,
- kann der Wert "6|h" oder "7|h" verwendet werden.
R_386_32 einer absoluten Adresse auf Daten zugegriffen werden soll. 1 (
Dingadressegewählt
- Speicherzellenadressegewählt
)
+ Speicherzelleninhaltalt
= SpeicherzelleninhaltneuIn 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
= SpeicherzelleninhaltneuSiehe 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 esweitere Informationen.
- weder zu dieser Flagge
- noch zu diesem verspäteten Reparatur-Verfahren
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:dann
- im Feld "EI_DATA" vom Identifikations-Abschnitt der Wert "2|h" angegeben wurde, was bedeutet, dass die Datenkodierung "big endian" verwendet wird,
- 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 ByteWenn dann
- im Feld "EI_DATA" vom Identifikations-Abschnitt der Wert "1|h" angegeben wurde, was bedeutet, dass die Datenkodierung "little endian" verwendet wird,
Ansonsten wenn
- wird dieses Byte normalerweise nicht benutzt.
Ich empfehle daher, alle Bits von diesem Byte auf "0|b" zu setzen.dann gilt das Folgende:
- im Feld "EI_DATA" vom Identifikations-Abschnitt der Wert "2|h" angegeben wurde, was bedeutet, dass die Datenkodierung "big endian" verwendet wird,
Wenndann
- 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,
- gibt dieses Feld die Berechnungsmethode an, welche verwendet werden soll, ...
siehe: oben1 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
Wenndann
- die Tabelle für Adressen-Reparaturen Einträge enthält, welche für Exporte oder Importe verwendet werden,
- wird im Feld "d_ptr und d_val" vom Eintrag, welcher sich auf diese Tabelle bezieht, in der Tabelle für Exporte und Importe angegeben, wo die Tabelle für Adressen-Reparaturen im Arbeitsspeicher beginnt.
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.
Wenndann
- Dinge
werden,
- exportiert oder
- importiert
- sind diese Angaben im besonderen Maß hilfreich, da dann
anhand vom Namen durchgeführt werden kann, anstatt dass dies anhand von einem Offset notwendig ist.
- der Export oder
- der Import
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.
Wenndann
- in dieser Tabelle Einträge aufgelistet sind,
- 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 ByteDer 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.
Wenndann
- 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,
- wird dies mit der Hilfe vom Wert "0|d" angegeben.
st_value 4 Byte 4 Byte
bis
7 ByteMit 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 ByteGibt 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
werden sollen, aber
- weder exportiert,
- noch importiert
- 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, welchewerden.
- exportiert oder
- importiert
Wenndann
- das Importieren scheitern sollte,
- 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, welchewerden.
- exportiert oder
- importiert
Wenndann
- das Importieren scheitern sollte,
- 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.
Wenndann
- sich das Ding in einer anderen Datei befindet, weil es beispielsweise aus einer Bibliothek importiert werden soll,
- wird dies mit der Hilfe vom Wert "0|h" angegeben.
Wenndann
- keine solche Kopfzeile in der Datei existiert,
- 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 ByteGesamtgröße: 16 Byte
Bezeichnung
Die Tabelle für Dinge wird auch "Symbol-Tabelle" (im Englischen: "symbol table") genannt.
Position
Wenndann
- die Tabelle für Dinge Dinge enthält, welche für
verwendet werden,
- Exporte oder
- Importe
- wird im Feld "d_ptr und d_val" vom Eintrag, welcher sich auf diese Tabelle bezieht, in der Tabelle für Exporte und Importe angegeben, wo die Tabelle für Dinge im Arbeitsspeicher beginnt.
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
- die Pfade und Namen zu den Bibliotheken, welche importiert werden sollen,
- Anfangsadressen zu verschiedene Tabellen, wie zum Beispiel
An keiner Stelle in diesem Dateiformat wird allerdings ein spezifisches Ding, welches importiert werden soll, mit einer spezifischen Bibliothek verbunden. Vielmehr werdenIn welcher von diesen Bibliotheken ein spezifisches Ding ist, das muss das Lade-Programm selbst herausfinden.
- alle Dinge genannt, welche importiert werden sollen, und
- alle Bibliotheken genannt, in welchen die Dinge sind.
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
- sowohl in einem Inhalt von einem Hauptabschnitt sein, bei welchem in der Kopfzeile
- als auch in einem Inhalt von einem Hauptabschnitt sein, bei welchem in der Kopfzeile für das Feld "p_type" der Wert "2|h" angegeben ist.
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 ByteDie Bedeutung vom Wert von diesem Feld hängt vom Wert im Feld "d_tag" ab. d_ptr und
d_val4 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 von einer Bibliothek angibt, welche importiert werden soll.
- den Dateinamen oder
- den absoluten Pfad und den Dateinamen
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")
Wenndann
- mehrere Bibliotheken importiert werden sollen,
- 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 enthalten sind, auf welche sich die Exporte beziehen.
- den Dingnamen
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. Wenndann
- eine Datei, zum Beispiel eine Anwendung, lediglich Dinge importiert,
- ist keine Tabelle für Prüfsummen notwendig, da die Tabelle für Prüfsummen von der Bibliothek verwendet wird, welche die Dinge exportiert.
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 enthalten sind, auf welche sich die Exporte und die Importe beziehen.
- den Bibliotheknamen,
- den Dingnamen und
- gegebenenfalls weiteren Suchpfaden für Bibliotheken
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.
Wenndann
- in dieser Datei eine Tabelle für Zeichenketten enthalten ist, welche hierfür verwendet wird,
- 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 werden sollen.
- exportiert oder
- importiert
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.
Wenndann
- in dieser Datei eine Tabelle für Dinge enthalten ist, welche hierfür verwendet wird,
- 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 enthalten sind, auf welche sich die Exporte und die Importe beziehen.
- den Bibliotheknamen und
- den Dingnamen
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. BeiIn 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.
- 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.
Wenndann
- die Bibliothek ein Ding aus einer anderen Bibliothek importiert, welche ebenfalls eine Initialisierungsroutine hat,
- wird zunächst die Initialisierungsroutine(n) von jener Bibliothek/jenen Bibliotheken ausgeführt, von denen ein Ding importiert wird.
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: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.
- Zuerst erhält sie die Kontrolle über den CPU, weil angegeben ist, dass eine Initialisierungsroutine existiert.
- Sobald die Anwendung aus dieser Routine zurückkehrt, erhält sie über die reguläre Weise erneut die Kontrolle über den CPU.
Wenndann
- das Ende vom auszuführenden Maschinencode erreicht wurde,
Das Beenden von der Ausführung vom Maschinencode entspricht also dem Zurückkehren von einer Funktion, welche keine Parameter hat.
- 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 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.
Wenndann
- kein Maschinencode ausgeführt werden soll, nur weil das Ereignis eingetroffen ist,
- 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.
Wenndann
- in dieser Datei eine Tabelle für Adressen-Reparaturen enthalten ist,
- 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, nichts davon mitbekommen, dass die Tabelle für Adressen-Reparaturen existiert und daher die Adressen-Reparaturen nicht durchführen.
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.
Wenndann
- in der Tabelle für Exporte und Importe ein Eintrag für die Tabelle für Adressen-Reparaturen existiert,
- 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.
Wenndann
- in der Tabelle für Exporte und Importe ein Eintrag für die Tabelle für Adressen-Reparaturen existiert,
- 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. Wenndann
- diese Datei eine Bibliothek importiert, welche wiederum selbst Bibliotheken benötigt,
- muss die importierte Bibliothek gegebenenfalls selbst Pfade angeben.
Wenndann
- kein solcher Pfad angegeben ist, oder eine Bibliothek in den angegebenen Pfaden nicht gefunden wurde,
- werden die folgenden Pfade in der folgenden Reihenfolge zum Auffinden von den zu importierenden Bibliotheken verwendet:
- "/lib/"
- "/usr/lib/"
- "/usr/local/lib/"
- "/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 ByteGesamtgröß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. Wenndann
- das Lade-Programm also ein Ding anhand vom Namen finden will,
- 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, obhaben sich die Entwickler von diesem Dateiformat dazu entschieden, dass diese Tabelle für Prüfsummen zwangsweise in der Datei enthalten sein muss, wenn Dinge
- dieses Verfahren einen Geschwindigkeitsvorteil bewirkt und
- es den hohen Aufwand durch die Implementation rechtfertigt,
werden sollen.
- exportiert oder
- importiert
In Linux ist es allerdings so, dass nur eine Datei, welche Dinge exportiert, eine solche Tabelle benötigt. Wenndann
- eine Datei, zum Beispiel eine Anwendung, lediglich Dinge importiert,
- 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:
- 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.- 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.- 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 ByteGibt 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.
Wenndann
- Sie den Wert "1" angeben,
- 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. und
- Der 3. Eintrag in dieser Auflistung bezieht sich also auf
- das 3. Ding in der Tabelle für Dinge
- 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.
Wenndann
- der Name nicht übereinstimmt,
- 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. Wenndann
- diese Tabelle allerdings in dem Inhalt vom Unterabschnitt enthalten ist, dessen Position durch das Feld "e_shstrndx" vom Kopfzeilen-Abschnitt angegeben ist,
- bedeutet dies, dass in dieser Tabelle Namen von Inhalten von Unterabschnitten enthalten sind.
Es bedeutet aber nicht, dass jede Zeichenkette in dieser Tabelle zwangsweise ein Name von einem Inhalt von einem Unterabschnitt darstellen muss.
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.
Wenndann
- in dieser Tabelle Einträge aufgelistet sind,
- 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.
Wenndann
- eine Zeichenkette ein Name von einem Inhalt von einem Unterabschnitt angibt,
- 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.
Wenndann
- eine Zeichenkette ein Name von einem Dingen angibt,
- 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 ByteGibt 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 ByteGesamtgröß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:
- Der eine Inhalt von Unterabschnitten soll ".text" genannt werden, um anzugeben, dass
und
- in diesem Inhalt von Unterabschnitten Maschinencode enthalten ist
- der andere Inhalt von Unterabschnitten soll ".rel.text" genannt werden, um anzugeben, dass
- in diesem Inhalt von Unterabschnitt eine Tabelle für Adressen-Reparaturen enthalten ist und
- die Speicherzellen, in welchen die zu reparierenden Adressen gespeichert sind, in dem Inhalt vom Unterabschnitt enthalten sind, welcher den Namen ".text" hat.
Wenndann
- nun die Tabelle für Zeichenketten lediglich mit
gefüllt wird,
- dem ersten unbenutzten Byte,
- der Zeichenkette ".rel.text" und
- dem Abschlusszeichen von der Zeichenkette
- 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.
Beschreibung
Im Nachfolgenden wird Schritt für Schritt gezeigt, wie mit der Hilfe voneine ausführbare Datei geschrieben werden kann, welche nach dem Aufrufen "Hallo Welt!" ausgibt.
- einem Hex-Editor oder
- einem geeigneten Text-Editor
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
- ein Hex-Editor sein (im Kapitel "weiteres Material zu diesem Thema - Programme" ist ein kostenloser Hex-Editor für Windows aufgelistet),
- ein Text-Editor sein, welcher eine Zusatzfunktion bietet, mit welcher man jedes beliebige "Zeichen" einfügen kann. Also mit welcher man ein Byte einfügen kann, von welchem man den Wert im Bereich von 0 bis 255 definieren kann, oder
- eine Programmier- oder Skriptsprache sein, mit dessen Hilfe man
erzeugen kann.
- eine Rohdaten-Datei und
- Rohdaten
Vorbereitung
die Datei anlegen
Ich hatte das "Hallo Welt!"-Programm in Windows geschrieben und dann in Linux getestet. Wenndann
- Sie von Anfang an Linux verwenden,
- 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.
Wenndann
- Sie ebenfalls so vorgehen wollen, aber Ihnen als Dateiname "Neu Textdokument" anstatt "Neu Textdokument.txt" angezeigt wird,
- zeigt Ihnen Windows die Dateinamens-Endung ".txt" nicht an.
Diese Funktion gibt es, damit man nicht versehentlich die Dateinamens-Endungwenn man den Dateinamen ändert. In unserem Fall soll die Dateinamens-Endung allerdings gelöscht werden.
- ändert oder
- löscht,
Damit Windows Ihnen die Dateinamens-Endung anzeigt, können Sie in "Windows experience" ("Windows xp") wie folgt vorgehen:
- Öffnen Sie den Arbeitsplatz und damit den Windows Explorer.
- Klicken Sie in der Menüleiste auf "Extras".
- Klicken Sie in dem Menü, welches sich eben geöffnet hat, auf "Ordneroptionen...".
- Klicken Sie in dem Fenster, welches sich eben geöffnet hat, auf den Reiter "Ansicht".
- Nehmen Sie den Haken von der Option "Erweiterungen bei bekannten Dateitypen ausblenden" weg.
- Klicken Sie unten auf den Knopf "Übernehmen".
- Klicken Sie oben auf den Knopf "Für alle übernehmen" und bestätigen Sie das aufspringende Fenster mit dem Knopf "Ja".
- 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 mitgeöffnet werden.
- dem Hex-Editor oder
- dem Text-Editor
Anmerkungen zum Speichern
Wenndann
- Sie einen Text-Editor verwenden,
- sollten Sie die Datei, wenn Sie sie speichern, nicht mit der Zeichenkodierung "unicode transformation format" ("UTF") speichern. "UTF-8" bedeutet beispielsweise, dass jedes Zeichen mindestens 1 Byte groß ist. Jeder Wert zwischen 0 und 127 wird als 1 Byte in der Datei gespeichert. Ein Wert zwischen 128 und 255 wird allerdings als 2 Bytes in der Datei gespeichert.
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:
- Klicken Sie in der Menüleiste auf "File".
- Klicken Sie in dem Menü, welches sich eben geöffnet hat, auf "Save Hex as Binary File".
Wenndann
- Sie die Werte in einem anderen Format eintragen,
- 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 Bytee_entry 74 80 04 08 0 Byte (Anfang von der Datei)
16 Byte (der Abschnitt "Identifikation")
+ 36 Byte (der Abschnitt "Kopfzeile")
= 52 Bytee_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 Bytee_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 Hauptabschnittee_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 00rot 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:
- Eine Kopfzeile, welche Auskünfte über den Haupt-Programmabschnitt mit dem Anfang von der Datei und dem Maschinencode macht.
- 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 Bytep_offset 00 00 00 00 134.512.640 Byte (gewünschte Basisadresse)
= 134.512.640 Bytep_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 Bereichteilen lässt.
- nicht restlos durch 2|d,
- sondern lediglich restlos durch 1|d
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 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 00rot 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 Bytep_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 Bytep_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 00rot 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 00rot 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
- zum Einen der Maschinencode, um die "Hallo Welt!"-Zeichenkette auszugeben, gespeichert und
- zum Anderen der Maschinencode, um das Programm ordnungsgemäß zu beenden, gespeichert.
- 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.
Wenndann
- der Aufrufer stattdessen eine Funktion aus einer Bibliothek aufrufen wollte,
- müsste er die folgenden Schritte durchführen:
- die Parameterwerte auf den Stapel legen
- die Rücksprungadresse auf den Stapel laden
- 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 Registerspeichern.
- "extended accumulator" ("eax")
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 08rot 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 vonab.
- der verwendeten Distribution und
- den Einstellungen, welche der Benutzer bei manchen Distributionen machen kann,
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 Registernicht mehr die Funktions-Identifikationskennung gespeichert, sondern der Rückgabewert von der Funktion "write".
- "extended accumulator" ("eax")
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.
Wenndann
- das Programm nicht ordnungsgemäß beendet wird,
- wird der Rest vom Inhalt vom Hauptabschnitt als Maschinencode interpretiert und dann stürzt das Programm ab, da für den nächsten Inhalt vom Hauptabschnitt, in welchem sich die Daten befinden, keine Ausführberechtigung erteilt wurde.
Wenndann
- ein Inhalt von einem Hauptabschnitt folgen würde, für welchen eine Ausführberechtigung erteilt wurde,
- 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: DBrot 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 90rot 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:
- "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 21rot 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.
Wenndann
- Sie ebenfalls so vorgehen wollen,
- helfen Ihnen vielleicht folgende Hinweise:
- Wenn
dann
- Sie ins richtige Verzeichnis gewechselt haben und
"Hallo_Welt"eingeben, um das Programm zu starten,
- bekommen Sie möglicherweise ein Fehlermeldung angezeigt, dass die Datei nicht gefunden wurde.
Wenndann
- Sie stattdessen
"./Hallo_Welt"eingeben,
- wird diese Fehlermeldung vermutlich verschwinden.
- Wenn
dann
- die Datei nicht als ausführbare Datei erkannt wird,
- können Sie mit der Eingabe
"chmod +x Hallo_Welt"die Ausführbarkeit von der Datei im Dateisystem eintragen.
Wenndann
- Sie durch diese Eingabe eine Fehlermeldung angezeigt bekommen sollten, dass Sie keine Berechtigung haben, diese Änderung am Dateisystem vorzunehmen,
- 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
dann
- Sie die Datei mit einem Doppelklick starten sollten,
- 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.
Wenndann
- Sie eine neue Konsole starten und
- mit dieser das Programm aufrufen,
- 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.
Beschreibung
Im Nachfolgenden wird Schritt für Schritt gezeigt, wie mit der Hilfe voneine Bibliothek ("*.so"-Datei) geschrieben werden kann, welche eine Funktion enthält, welche "Hallo Welt!" ausgibt.
- einem Hex-Editor oder
- einem geeigneten Text-Editor
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. Wenndann
- Sie von Anfang an Linux verwenden,
- 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.
Wenndann
- Sie ebenfalls so vorgehen wollen, aber Ihnen als Dateiname "Neu Textdokument" anstatt "Neu Textdokument.txt" angezeigt wird,
- 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:
- Öffnen Sie den Arbeitsplatz und damit den Windows Explorer.
- Klicken Sie in der Menüleiste auf "Extras".
- Klicken Sie in dem Menü, welches sich eben geöffnet hat, auf "Ordneroptionen...".
- Klicken Sie in dem Fenster, welches sich eben geöffnet hat, auf den Reiter "Ansicht".
- Nehmen Sie den Haken von der Option "Erweiterungen bei bekannten Dateitypen ausblenden" weg.
- Klicken Sie unten auf den Knopf "Übernehmen".
- Klicken Sie oben auf den Knopf "Für alle übernehmen" und bestätigen Sie das aufspringende Fenster mit dem Knopf "Ja".
- 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 Bytee_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 Bytee_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 Hauptabschnittee_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 00rot 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:
- Kopfzeilen-Tabelle für Hauptabschnitte
- Tabelle für Adressen-Reparaturen
- Tabelle für Dinge
- Tabelle für Exporte und Importe
- Tabelle für Prüfsummen
- Tabelle für Zeichenketten
die Kopfzeilen-Tabelle für Hauptabschnitte
Die Kopfzeilen-Tabelle für Hauptabschnitte wird mit den folgenden Kopfzeilen gefüllt:
- Eine Kopfzeile, welche Auskünfte über den Haupt-Programmabschnitt mit dem Anfang von der Datei und dem Maschinencode macht.
- Eine Kopfzeile, welche Auskünfte über den Haupt-Programmabschnitt mit der Tabelle für Exporte und Importe macht.
- 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 Bytep_offset 00 00 00 00 0 Byte (gewünschte Basisadresse)
= 0 Bytep_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 Bereichteilen lässt.
- nicht restlos durch 2|d,
- sondern lediglich restlos durch 1|d
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 00rot 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 Bytep_offset AC 00 00 00 0 Byte (gewünschte Basisadresse)
+ 172 Byte (Anfangsadresse in der Datei; siehe: das Feld "p_offset")
= 172 Bytep_vaddr AC 00 00 00 p_paddr 00 00 00 00 6 ½ Einträge; jeder vollständige Eintrag ist jeweils "8 Byte"-groß
= 52 Bytep_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 00rot 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 Bytep_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 Bytep_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 00rot 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:
Im Nachfolgenden werden die verbleibenden Tabellen definiert:
- Tabelle für Adressen-Reparaturen
- Tabelle für Dinge
- Tabelle für Exporte und Importe
- Tabelle für Prüfsummen
- 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 Byter_offset 13 01 00 00 R_386_RELATIVE, d. h.: Basisadressegewählt
+ Speicherzelleninhaltalt
= Speicherzelleninhaltneur_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:
Im Nachfolgenden werden die verbleibenden Tabellen definiert:
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"zugänglich zu machen.
- eine Anfangsadresse und
- eine Zeichenkette für ihren Namen
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 Bytest_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, welchewerden.
- exportiert oder
- importiert
= 16|dst_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 00rot 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:
Im Nachfolgenden werden die verbleibenden Tabellen definiert:
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 Byted_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 Byted_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 Byted_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 Byted_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 000. 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:
- Kopfzeilen-Tabelle für Hauptabschnitte
- Tabelle für Adressen-Reparaturen
- Tabelle für Dinge
- Tabelle für Exporte und Importe
Im Nachfolgenden werden die verbleibenden Tabellen definiert:
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. Wenndann
- Sie die Tabelle korrekt implementieren wollen,
- 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
Wenndann
- Sie ebenfalls den Trick anwenden wollen,
- 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 00rot 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:
- Kopfzeilen-Tabelle für Hauptabschnitte
- Tabelle für Adressen-Reparaturen
- Tabelle für Dinge
- Tabelle für Exporte und Importe
- Tabelle für Prüfsummen
Im Nachfolgenden wird die verbleibende Tabelle definiert:
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.
- Am Anfang von dieser Tablle werde ich die Zeichenkette "Hallo_Welt_ausgeben" speichern und
- 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 00rot 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 00rot 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
- zum Einen der Maschinencode, um die "Hallo Welt!"-Zeichenkette auszugeben, gespeichert und
- zum Anderen der Maschinencode, um zum Aufrufer zurückzukehren.
- 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 00rot 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 ist0 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 90rot 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 21rot 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.
Wenndann
- Sie ebenfalls so vorgehen wollen,
- helfen Ihnen vielleicht die folgenden Hinweise:
- Wenn
dann
- Sie ins richtige Verzeichnis gewechselt haben und
"Hallo_Welt.so_benutzen"eingeben, um das Programm zu starten,
- bekommen Sie möglicherweise ein Fehlermeldung angezeigt, dass die Datei nicht gefunden wurde.
Wenndann
- Sie stattdessen
"./Hallo_Welt.so_benutzen"eingeben,
- wird diese Fehlermeldung vermutlich verschwinden.
- Wenn
dann
- die Datei nicht als ausführbare Datei erkannt wird,
- können Sie mit der Eingabe
"chmod +x Hallo_Welt.so_benutzen"die Ausführbarkeit von der Datei im Dateisystem eintragen.
Wenndann
- Sie durch diese Eingabe eine Fehlermeldung angezeigt bekommen sollten, dass Sie keine Berechtigung haben, diese Änderung am Dateisystem vorzunehmen,
- 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
dann
- Sie die Datei mit einem Doppelklick starten sollten,
- 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.
Wenndann
- Sie eine neue Konsole starten und
- mit dieser das Programm aufrufen,
- 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.
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
von verschiedenen Betriebssystemen
- ausführbaren Dateien und
- Bibliotheken
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 0003englisch
- 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.htmlThe 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_WeltHallo_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
Wenndann
- Sie weitere Materialien zu diesem Thema haben, von welchen Sie wollen, dass sie in diese Auflistungen aufgenommen werden,
- können Sie mir gerne die Materialien oder einen Link zu ihnen zusenden.
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, dassEs besteht kein Anspruch darauf, dass zugesendete Materialien aufgenommen werden.
- eine Verbreitung nicht gestattet ist oder
- nur unter Bedingungen, welche ich nicht erfüllen will.
Link zum Kapitel "wie man den Autor kontaktiert".