Wie benutze ich libelf, um eine ELF-Datei für meinen eigenen Compiler zu erzeugen?

8

Ich habe Code für einen einfacheren Compiler generiert, den ich schreibe, und ich frage mich, wie ich diesen Code in eine ELF-Datei einfügen kann?

Ich habe versucht, libelf zu benutzen, aber ich kann mich nicht darum kümmern, wie man die Tabellen organisiert.

Ich verwende keine Daten, also nehme ich an, dass ich nur einen .text -Abschnitt brauche.

Wenn ich einen Puffer von erzeugtem x86-Code hätte, wie würde ich eine ELF-Datei mit nur einem einfachen .text -Abschnitt erstellen, der ausführbar sein könnte?

    
user1814062 10.11.2012, 06:31
quelle

1 Antwort

3

Kurze Antwort

Sie können nicht!

Die Funktionalität, nach der Sie suchen, ist eigentlich Teil eines Build-Tools namens "Linker". Trotz der Tatsache, dass neben einigen nicht aufgelösten Symbolfehlern von Zeit zu Zeit das Vorhandensein dieser Symbole oft unbemerkt bleibt, ist es eine der wichtigsten Komponenten jeder Build-Chain.

Lange Antwort

Hier ein paar Ideen, wie Sie fortfahren können, um Ihre Binärdatei irgendwie zum Laufen zu bringen.

Einschränkungen

Jede der unten beschriebenen Methoden funktioniert nur, wenn

  • Der Maschinencode enthält keine Sprünge mit absoluten Adressen, da das Patchen an die richtigen Ziele eine Umzugsinformation erfordern würde.

  • Das Programm beginnt ganz am Anfang der als Eingabe verwendeten Binärdatei

    Dies sollte leicht zu umgehen sein, entweder durch Hinzufügen eines zusätzlichen (relativen) Sprungbefehls zum "richtigen" Punkt am Anfang der Datei, indem ein Versatz in die Binärdaten verwendet wird.

Vorgeschlagene Abhilfe

Im Fall einer sehr einfachen "in sich geschlossenen" enthaltenen Binärdatei, die nur durch eine Reihe von rohen Maschinenanweisungen ohne externe Abhängigkeiten und ohne (!) absolute Sprunganweisungen gegeben wird, anstatt sie zu tun Hand könnte es einfacher sein, stattdessen einen bereits vorhandenen Linker zu verwenden.

Bei einer Datei, die aus den rohen Maschinenanweisungen ( main.bin im folgenden Beispiel) besteht, würde der erste Schritt darin bestehen, ein gemeinsames Objekt ( main.o in der Beispiel) daraus:

%Vor%

Betrachten Sie die Symboltabelle für generierte Objekte readelf -S :

%Vor%

Sie werden feststellen, dass die Symbole _binary_..._start , _binary_..._end und _binary_..._size je nach Start, Ende und Größe der Eingabedatei hinzugefügt wurden. Diese können verwendet werden, um den Einstiegspunkt der ausführbaren Datei an den Linker zu übergeben.

%Vor%

sollte die ausführbare Datei erzeugen, nach der Sie suchen.

Manuelle Generierung

Alternativ können Sie auch manuell eine Elf-Datei erstellen, die nur die notwendigen Informationen enthält, um eine ausführbare Datei zu erhalten.

Wenn Sie mit dem Elf-Format nicht vertraut sind, können Sie sich die Spezifikationen ansehen (verfügbar auf: Ссылка ) ). Auch die Manualpage ( man elf ) ist sehr erschöpfend, daher könnte dies auch eine gute Informationsquelle sein.

Um es möglichst einfach zu halten, wird das Ziel sein, nur das zu verwenden, was absolut notwendig ist.

Wenn Sie einen Blick in die Spezifikationen werfen, werden Sie sehen, dass die einzige Komponente, die unter allen Umständen benötigt wird, der Elf-Header ist. Eine Abschnitts-Header-Tabelle wird nur für gemeinsame Objekte benötigt, eine Programm-Header-Tabelle nur für ausführbare Dateien.

Da wir eine ausführbare Datei erstellen wollen, verwenden wir nur die Programm-Kopftabelle mit einem einzelnen Eintrag vom Typ PT_LOAD , der das gesamte Speicherlayout der ausführbaren Datei beschreibt.

Um die Ausrichtungsbeschränkungen zu erfüllen, enthält das Prozessabbild den gesamten Inhalt der Binärdatei (Quelle: man elf).

  

... Ladbare Prozesssegmente müssen kongruente Werte für p_vaddr und p_offset haben, modulo die Seitengröße.

Es sollte klar sein, warum das endgültige Layout der Elf-Datei so aussieht:

%Vor%

Die meisten Felder von Elf32_Ehdr und Elf32_Phdr sind fest, so dass sie bereits im Initialisierer gesetzt werden können. Die einzigen Felder, die später angepasst werden müssen, sind die Felder, die die Größen (.p_filesz und .p_memsz) des geladenen Segments im Programmheader-Tabelleneintrag beschreiben.

Eingabe von stdin und Schreiben in stdout (also wie ./a.out <main.bin >executable verwendet), so kann das beschriebene Setup implementiert werden:

%Vor%     
mikyra 09.03.2013 23:35
quelle

Tags und Links