Executable and Linking Format — Änderungen 1975 → 2190

Zeile 1: Zeile 1:
 +
Es gibt 3 verschiedene Arten von Elf-Dateien:
 +
* executable
 +
* relocatable
 +
* shared object
 +
 +
== Aufbau ==
 +
 +
=== Elf-Header ===
 +
Der Elf-Header ist wie folg aufgebaut
 +
 +
{| border="1"
 +
! Name                          || Offset || Größe || Beschreibung
 +
|-
 +
| [[#e_ident | e_ident]]        || 0x00  || 16    || Enthält Daten zur Identifikation
 +
|-
 +
| [[#e_type | e_type]]          || 0x10  || 2    || Enthält den Elf-Typ
 +
|-
 +
| [[#e_machine | e_machine]]    || 0x12  || 2    || Beschreibt die benötigte Architektur
 +
|-
 +
| [[#e_version | e_version]]    || 0x14  || 4    || Version
 +
|-
 +
| [[#e_entry | e_entry]]        || 0x18  || 4    || Entrypoint
 +
|-
 +
| [[#e_phoff | e_phoff]]        || 0x1C  || 4    || Das Offset des Programm-Headers
 +
|-
 +
| [[#e_shoff | e_shoff]]        || 0x20  || 4    || Das Offset des Section-Headers
 +
|-
 +
| [[#e_flags | e_flags]]        || 0x24  || 4    || Verschiedene Flags
 +
|-
 +
| [[#e_ehsize | e_ehsize]]      || 0x28  || 2    || Größe des Elf-Headers
 +
|-
 +
| [[#e_phentsize | e_phentsize]] || 0x2A  || 2    || Größe eines Eintrags im Programm-Header
 +
|-
 +
| [[#e_phnum | e_phnum]]        || 0x2C  || 2    || Anzahl an Einträgen im Programm-Header
 +
|-
 +
| [[#e_shentsize | e_shentsize]] || 0x2E  || 2    || Größe eines Eintrags im Section-Header
 +
|-
 +
| [[#e_shnum | e_shnum]]        || 0x30  || 2    || Anzahl an Einträgen im Section-Header
 +
|-
 +
| [[#e_shstrndx | e_shstrndex]]  || 0x32  || 2    || Assoziiert einen Eintrag im Section-Header mit der String-Tabelle
 +
|}
 +
 +
==== e_ident ====
 +
Das Feld e_ident ist 16 Byte groß und enthält Werte zur Identifikation der Elf-Datei.
 +
Dies sind die Indexes:
 +
{| border="1"
 +
! Name                              || Wert || Beschreibung
 +
|-
 +
| [[#EI_MAG0 | EO_MAG0 bis EI_MAG3]] || 0    || Datei-Identifikation
 +
|-
 +
| [[#EI_MAG1 | EI_MAG0 bis EI_MAG3]] || 1    || Datei-Identifikation
 +
|-
 +
| [[#EI_MAG2 | EI_MAG0 bis EI_MAG3]] || 2    || Datei-Identifikation
 +
|-
 +
| [[#EI_MAG3 | EI_MAG0 bis EI_MAG3]] || 3    || Datei-Identifikation
 +
|-
 +
| [[#EI_CLASS | EI_CLASS]]          || 4    || Datei-Klasse
 +
|-
 +
| [[#EI_DATA | EI_DATA]]            || 5    || Prozessorspezifische Datenkodierung
 +
|-
 +
| [[#EI_VERSION | EI_VERSION]]      || 6    || Datei-Version
 +
|-
 +
| [[#EI_PAD | EI_PAD]]              || 7    || Letztes genutztes Byte in e_ident
 +
|-
 +
| [[#EI_NIDENT | EI_NIDENT]]        || 16  || Anzahl an Bytes in e_ident
 +
|}
 +
 +
===== EI_MAG0 bis EI_MAG3 =====
 +
Diese Bytes werden zur Datei-Identifikation benutzt.
 +
{| border="1"
 +
! Name    || Wert || Position
 +
|-
 +
| ELFMAG0 || 0x7f || e_ident[EI_MAG0]
 +
|-
 +
| ELFMAG1 || 'E'  || e_ident[EI_MAG1]
 +
|-
 +
| ELFMAG2 || 'L'  || e_ident[EI_MAG2]
 +
|-
 +
| ELFMAG3 || 'F'  || e_ident[EI_MAG3]
 +
|}
 +
 +
===== EI_CLASS =====
 +
Die Klasse gibt unter anderen an, wie viel Bits zur Adressierung verwendet werden. Also eine 32Bit-Maschine sollte überprüfen, ob der Code auch wirklich auf 32Bit-Maschinen geht.
 +
{| border="1"
 +
! Name        || Wert  || Beschreibung
 +
|-
 +
| ELFCLASSNONE || 0    || Ungültige Klasse
 +
|-
 +
| ELFCLASS32  || 1    || 32bit-Objekte
 +
|-
 +
| ELFCLASS64  || 2    || 64bit-Objekte
 +
|}
 +
 +
===== EI_DATA =====
 +
Das Byte e_ident[EI_DATA] gibt an, in welchen Format Daten gespeichert werden, Little-Endian oder Big-Endian
 +
 +
{| border="1"
 +
! Name        || Wert  || Beschreibung
 +
|-
 +
| ELFDATANONE || 0    || Ungültig
 +
|-
 +
| ELFDATA2LSB || 1    || Little-Endian
 +
|-
 +
| ELFDATA2MSB || 2    || Big-Endian
 +
|}
 +
 +
===== EI_VERSION =====
 +
Hier sollte das gleiche wie in e_version drinstehen, also EV_CURRENT
 +
 +
===== EI_PAD =====
 +
Alle Bytes in e_ident ab EI_PAD (inklusive) werden nicht benutzt und sollten Null gesetzt sein.
 +
 +
===== Architektur-Identifikation =====
 +
Für einen 32bit Intel-Prozessor sollten folgende Felder folgende Werte haben:
 +
{| border="1"
 +
! Feld              || Wert
 +
|-
 +
| e_ident[EI_CLASS] || ELFCLASS32
 +
|-
 +
| e_ident[EI_DATA]  || ELFDATA2LSB
 +
|-
 +
| e_machine        || EM_386
 +
|}
 +
 +
 +
==== e_type ====
 +
Dieses Feld beschreibt, welches Elf-Format benutzt wird:
 +
{| border="1"
 +
! Name      || Wert || Beschreibung
 +
|-
 +
| ET_NONE  || 0      || Kein Typ
 +
|-
 +
| ET_REL    || 1      || Kein Typ
 +
|-
 +
| ET_EXEC  || 2      || Kein Typ
 +
|-
 +
| ET_DYN    || 3      || Kein Typ
 +
|-
 +
| ET_CORE  || 4      || Kein Typ
 +
|-
 +
| ET_LOPROC || 0xff00 || Kein Typ
 +
|-
 +
| ET_HIPROC || 0x00ff || Kein Typ
 +
|}
 +
Für eine ausführbare Elf-Datei, müsste hier ET_EXEC stehen.
 +
 +
==== e_machine ====
 +
Wie oben schonmal erwähnt, steht hier der Prozessor-Typ drin.
 +
{| border="1"
 +
! Name    || Wert || Beschreibung
 +
|-
 +
| EM_NONE  || 0    || Kein Typ
 +
|-
 +
| EM_M32  || 1    || AT&T WE 32100
 +
|-
 +
| EM_SPARC || 2    || SPARC
 +
|-
 +
| EM_386  || 3    || Intel 80386
 +
|-
 +
| EM_68K  || 4    || Motorola 68000
 +
|-
 +
| EM_88K  || 5    || Motorola 88000
 +
|-
 +
| EM_860  || 7    || Intel 80860
 +
|-
 +
| EM_MIPS  || 8    || MIPS RS3000
 +
|}
 +
 +
==== e_version ====
 +
Beschreibt die Aktuelle Elf-Version
 +
{| border="1"
 +
! Name      || Wert || Beschreibung
 +
|-
 +
| EV_NONE    || 0    || Ungültige Version
 +
|-
 +
| EV_CURRENT || 1    || Aktuelle Version
 +
|}
 +
 +
==== e_entry ====
 +
Virtuelle Adresse des Entrypoints
 +
 +
==== e_phoff ====
 +
Offset in der Datei, an dem die Programm-Header steht. Wenn die Datei keinen Programm-Header enthält, steht hier 0.
 +
 +
==== e_shoff ====
 +
Offset in der Datei, an dem der Section-Header steht. Wenn die Datei keinen Section-Header enthält, steht hier 0.
 +
 +
==== e_flags ====
 +
Dieses Feld enthält Prozessorspezifische Flags.
 +
 +
==== e_ehsize ====
 +
Größe des Elf-Headers.
 +
 +
==== e_phentsize ====
 +
Dieses Feld gibt an, wie groß ein Eintrag im Programm-Header ist.
 +
 +
==== e_phnum ====
 +
Gibt an, wie viele Einträge im Programm-Header enthalten sind. Das Produkt von e_phentsize und e_phnum ergibt die insgesamte Größe des Programm-Headers. Wenn die Datei keinen Programm-Header hat, steht hier 0.
 +
 +
==== e_shentsize ====
 +
Dieses Feld gibt an, wie groß ein Eintrag im Section-Header ist.
 +
 +
==== e_shnum ====
 +
Gibt an, wie viele Einträge im Section-Header enthalten sind. Das Produkt von e_shentsize und e_shnum ergibt die insgesamte Größe des Section-Headers. Wenn die Datei keinen Section-Header hat, steht hier 0.
 +
 +
==== e_shstrndx ====
 +
Gibt an, welcher Eintrag im Section-Header mit der String-Tabelle verknüpft wird.
 +
 +
=== Programm-Header ===
 +
Der Programm-Header besteht aus e_phnum Einträgen, wobei jeder e_phentsize groß ist. Der Programm-Header ist also insgesamt e_phnum*e_phentsize Bytes groß. Im Programm-Header stehen z.B der Text- oder Daten-Teil eines Programms.
 +
Ein Eintrag im Programm-Header ist wie folgt aufgebaut:
 +
{| border="1"
 +
! Name                    || Offset || Größe || Beschreibung
 +
|-
 +
| [[#p_type | p_type]]    || 0x00  || 4    || Typ des Segments
 +
|-
 +
| [[#p_offset | p_offset]] || 0x04  || 4    || Dateioffset, an dem das Segment steht
 +
|-
 +
| [[#p_vaddr | p_vaddr]]  || 0x08  || 4    || Virtuelle Adresse, an die das Segment kopiert werden soll
 +
|-
 +
| [[#p_paddr | p_paddr]]  || 0x0C  || 4    || Physikalische Adress (unrelevant)
 +
|-
 +
| [[#p_filesz | p_filesz]] || 0x10  || 4    || Größe des Segments in der Datei
 +
|-
 +
| [[#p_memsz | p_memsz]]  || 0x14  || 4    || Größe des Segments, das es im Speicher haben soll
 +
|-
 +
| [[#p_flags | p_flags]]  || 0x18  || 4    || Flags
 +
|-
 +
| [[#p_align | p_align]]  || 0x1C  || 4    || Alignment
 +
|}
 +
 +
==== p_type ====
 +
Dieses Feld gibt den Typ des Segments an, für eine ausführbare Elf-Datei ist vor allem PT_LOAD relevant.
 +
Die folgenden Beschreibungen sind auf PT_LOAD bezogen (es kann sein, dass sie auch auf andere Typen zutreffen).
 +
 +
{| border="1"
 +
! Name      || Wert      || Beschreibung
 +
|-
 +
| PT_NULL    || 0          || Ungültiges Segment
 +
|-
 +
| PT_LOAD    || 1          || Ladbares Segment
 +
|-
 +
| PT_DYNAMIC || 2          || Dynamisches Segment
 +
|-
 +
| PT_INTERP  || 3          || Position eines 0-Terminierten Strings, der den Interpreter angibt.
 +
|-
 +
| PT_NOTE    || 4          || Universelles Segment
 +
|-
 +
| PT_SHLIB  || 5          || Shared Lib
 +
|-
 +
| PT_PHDIR  || 6          || Gibt Position und Größe des Programm-Headers an.
 +
|-
 +
| PT_LOPROC  || 0x70000000 || Reserviert
 +
|-
 +
| PT_HIPROC  || 0x7fffffff || Reserviert
 +
|}
 +
 +
==== p_offset ====
 +
Offset in der Datei, an dem das Segment beginnt.
 +
 +
==== p_vaddr ====
 +
Virtuelle Adresse, an die das Segment kopiert werden soll.
 +
 +
==== p_paddr ====
 +
Gibt die physikalische Zieladresse an, für Systeme, für welche dies relevant ist.
 +
 +
==== p_filesz ====
 +
Gibt die Größe des Segments in der Datei wieder. Kann eventuell auch 0 sein, z.B für die BSS-Sektion
 +
 +
==== p_memsz ====
 +
Gibt die Größe des Segments im RAM an. Kann 0 sein.
 +
 +
==== p_flags ====
 +
Beschreibt Flags, z.B Zugriffsrechte.
 +
{| border="1"
 +
! Name || Wert || Beschreibung
 +
|-
 +
| PF_X || 1    || Ausführbares Segment
 +
|-
 +
| PF_W || 2    || Beschreibbares Segment
 +
|-
 +
| PF_R || 4    || Lesbares Segment
 +
|}
 +
Damit kann man den Wert berechnen:
 +
<c>
 +
flags = (read?PF_R:0)|(write?PF_W:0)|(exe?PF_X:0);
 +
</c>
 +
 +
==== p_align ====
 +
Alignment des Segmentes. Wenn 0 oder 1 angegeben wird, ist kein Alignment erforderlich. Ansonsten muss es eine 2er Potenz sein (2, 4, 8, 16, ...).
 +
 +
== Überprüfen ==
 +
Überprüfen tut man die ELF-Datei mit den folgenden Schritten:
 +
* MAGIC-String überprüfen
 +
* Architektur überpüfen
 +
* Klasse (32bit/64bit) überprüfen
 +
* Daten-Format (Little-/Big-Endian) überprüfen
 +
* Version überprüfen
 +
 
= Links =
 
= Links =
 
* [http://www.acm.uiuc.edu/sigops/rsrc/pfmt11.pdf Elf für x86]
 
* [http://www.acm.uiuc.edu/sigops/rsrc/pfmt11.pdf Elf für x86]
  
 
[[Category:Dateitypen]]
 
[[Category:Dateitypen]]