Autor Thema: ELF-Relocatables  (Gelesen 9632 mal)

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« am: 23. December 2014, 13:09 »
Hallo zusammen,

ich sitze gerade daran einen modularen Kernel zu bauen und versuche ELF zu verstehen.
Für Executables ist es ja recht einfach, aber Relocatables verwirren mich etwas.
Hier mal meine Interpretation, wie ich den ganzen Spaß zu interpretieren und zu laden habe:

Wenn ich Abhängigkeiten zu anderen Modulen habe, wird eine .plt-Sektion angelegt, die eigentlich nichts anderes als ein großes Array ist,
in das ich die entsprechenden absulten Adressen der Funktionen eintragen muss, oder?
Dann habe ich noch eine .got.plt.
Die ist dafür da, dass ich die Relocation meines Moduls anpassen kann und die richtigen Stellen in der .plt angesprungen werden, oder?

Dann habe ich noch .rel.dyn. Was mache ich damit? Anhand des Namens sehe ich, dass es dabei um die Relocation geht, aber so ganz kapiere ich den Teil noch nich.

Allerdings verstehe ich nicht wozu .dynsym und .dynstr da sind.
Was soll ich mit denen anfangen?
.dynsym sehe ich momentan als eine Art Offset-Tabelle an, die mir angibt welche Funktionen zu welcher Sektion und zu welchen Offset gehören.
Ist das richtig?
Aber dafür hätte ich doch auch .symtab. Die sagen doch genau das Gleiche aus, oder?

Grüße
rizor
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

xenos1984

  • Beiträge: 9
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 23. December 2014, 14:07 »
So weit ich weiß, sind .dynsym und .dynstr im Prinzip das gleiche wie .symtab und .strtab, enthalten aber speziell die Symbole, die zum dynamischen Linken relevant sind, d.h. die exportierten Symbole. Der Vorteil einer solchen Trennung ist, dass man beim Relozieren / dynamischen Linken nur diese exportierten Symbole nach einem Treffer durchsuchen muss und sie damit von den lokalen Symbolen getrennt hat.

Hier ein Auszug aus meinem Quelltext am Beispiel der IA32-Architektur:
http://sourceforge.net/p/xenos/code/HEAD/tree/trunk/kernel/arch/x86/ia32/I386TaskManager.cpp#l236

In .rel.dyn des Executables findest du eine Liste mit Relokationen, d.h. Stellen im Programmtext, an denen eine Adresse aus der Symboltabelle eingesetzt werden muss. Jede davon zeigt auf ein Symbol aus einer Symboltabelle (die findest du aus dem Link-Feld der Relationstabelle) mit weiteren Infos. Die werden hier ab Zeile 238 gesucht und es wird geprüft, ob es sich um eine Relokationstabelle handelt, und ob auf eine dynamische Symboltabelle verwiesen wird. In 240-242 werden dann die Links zur Relationstabelle, Symboltabelle und Stringtabelle geholt - letztere findest du aus dem Link-Feld der Symboltabelle. Ab 244 wird dann die Relokationstabelle durchsucht. Es wird überprüft, um was für einen Relokationstyp es sich handelt (in diesem Fall ist nur R_386_JMP_SLOT von Bedeutung) und der Index in die Symboltabelle abgefragt. Ab 252 werden dann die exportieren Symbole der Bibliothek (in meinem Beispiel ist das der Kernel, bzw. dessen Low-Level-API, der diese Symbole exportiert) durchsucht und in 254 auf Treffer überprüft. In 256 wird schließlich der gefundene Wert an die Stelle gesetzt, auf die in der Relokation verwiesen wird - die dortige Formel hängt davon ab, welcher Relokationstyp vorliegt, in dem Fall eben R_386_JMP_SLOT.

Bei x86_64 sieht das so ähnlich aus:
http://sourceforge.net/p/xenos/code/HEAD/tree/trunk/kernel/arch/x86/amd64/X86_64TaskManager.cpp#l228

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 23. December 2014, 23:26 »
Danke, das hilft schon mal richtig weiter.
Du machst aber ein 1:1-Mapping, oder?

Erlaubt es ELF eigentlich, dass zwei Program-Header, die geladen werden koennen, nicht den selben Offset zwischeneinander haben wie in der Datei definiert?

Also z.B.:
Program-Header 1: Offset 0x1000
Program-Header 2: Offset 0x2000
Und jetzt lade ich Header 1 an 0x1000
und Header 2 an 0x3000.

Geht das oder muss ich mich bei ELF an die Offsets halten und darf nur die globale Base-Addresse veraendern?
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

xenos1984

  • Beiträge: 9
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 24. December 2014, 00:13 »
Das geht, genau so mache ich es auch im Code von oben (also so gesehen kein 1:1-Mapping, sondern so, wie im Program Header angegeben - wenn du das mit 1:1-Mapping meinst):
http://sourceforge.net/p/xenos/code/HEAD/tree/trunk/kernel/arch/x86/ia32/I386TaskManager.cpp#l212
Man kann also mehrere Segmente haben, die an verschiedene Adressen geladen werden, und die Offsets im Speicher müssen nicht denen in der Datei entsprechen. Genau das berücksichtigt auch mein Beispielcode. Von jedem Segment werden Dateioffset und Speicheroffset ausgelesen und dann der Inhalt an die entsprechende virtuelle Adresse gemappt.

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 24. December 2014, 12:09 »
Ah, okay. Jetzt habe ich es verstanden.
Allerdings wundert mich eines.
Ich habe eine REL-Sektion und dort sind R_386_RELATIVE enthalten, die angeblich aus dem Addend bestehen, allerdings enthaelt die REL-Sektion doch keine RELA-Eintraege, oder?
Ist dann Addend automatisch 0?
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

xenos1984

  • Beiträge: 9
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 24. December 2014, 13:37 »
Das wundert mich auch - aus der zugehörigen Dokumentation werde ich auch nicht ganz schlau:
Zitat
The link editor creates this relocation type for dynamic linking. Its offset member gives a location within a shared object that contains a value representing a relative address. The dynamic linker computes the corresponding virtual address by adding the virtual address at which the shared object was loaded to the relative address. Relocation entries for this type must specify 0 for the symbol table index.
Könntest du die zugehörigen Section-Header und einen Auszug aus der Relokationstabelle (xx-xx-xx-objdump -r bzw. -R für dynamische Relokationen, wobei xx-xx-xx dein Architektur-Triplett ist, also z.B. i386-pc-elf, wenn du einen entsprechenden Cross-Compiler benutzt) posten?

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 24. December 2014, 14:28 »
So sieht objdump -R aus:

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE
00002190 R_386_RELATIVE    *ABS*
00002194 R_386_RELATIVE    *ABS*
00002198 R_386_RELATIVE    *ABS*
0000219c R_386_RELATIVE    *ABS*
000021a0 R_386_RELATIVE    *ABS*
000021a4 R_386_RELATIVE    *ABS*
000021a8 R_386_RELATIVE    *ABS*
00002184 R_386_JUMP_SLOT   HALMemCopy
00002188 R_386_JUMP_SLOT   HALMemSet
0000218c R_386_JUMP_SLOT   HALStrEqual

Habe eben gesehen, dass viele ELF-Loader in diesem Fall ein Addend von 0 annehmen.
Ist vermutlich zur Minimierung da, da die R_386_RELATIVE in den meisten Faellen ja fuer Zugriff auf Data, etc. ausgehen.
« Letzte Änderung: 24. December 2014, 14:33 von rizor »
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

xenos1984

  • Beiträge: 9
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 24. December 2014, 19:01 »
Ah, das macht Sinn - oder vielleicht nicht ganz. Dazu habe ich gerade noch etwas gefunden:
Zitat
As shown above, only Elf32_Rela entries contain an explicit addend. Entries of type Elf32_Rel store an implicit addend in the location to be modified. Depending on the processor architecture, one form or the other might be necessary or more convenient. Consequently, an implementation for a particular machine may use one form exclusively or either form depending on context.
Der Addend müsste also in diesem Fall an der zu relozierenden Stelle selbst stehen, d.h. statt den dortigen Wert zu überschreiben, müsste man B (also die Basisadresse des Relocatables) zu diesem Wert addieren. Einfach A auf 0 zu setzen kommt mir hier falsch vor. Du kannst ja mal schauen, was für Werte denn an den jeweiligen Adressen 0x2190-0x21a8 in der Datei stehen. Ich würde vermuten, das ist nicht 0 (es wäre auch seltsam, wenn da 7 Mal unmittelbar hintereinander der gleiche Wert eingetragen werden sollte).

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 24. December 2014, 21:20 »
Das klingt plausibel.
Alle Werte in diesen Bereichen zeigen auch in verschiedene Sektionen der ELF-Datei.
Halt immer an unterschiedliche. Von daher steht der Addend also schon an der Adresse.
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

 

Einloggen