So geht's:
.file "test.c"
Der ursprüngliche Quelldateiname (wird von Debuggern verwendet).
.section .rodata
.LC0:
.string "Hello world!"
Ein nullterminierter String ist im Abschnitt ".rodata" enthalten ("ro" bedeutet "nur lesen":Die Anwendung kann die Daten lesen, aber jeder Versuch, sie zu schreiben, löst eine Ausnahme aus).
.text
Jetzt schreiben wir Dinge in den ".text"-Abschnitt, wo der Code hinkommt.
.globl main
.type main, @function
main:
Wir definieren eine Funktion namens "main", die global sichtbar ist (andere Objektdateien können sie aufrufen).
leal 4(%esp), %ecx
Wir speichern im Register %ecx
den Wert 4+%esp
(%esp
ist der Stapelzeiger).
andl $-16, %esp
%esp
wird leicht modifiziert, so dass es ein Vielfaches von 16 wird. Für einige Datentypen (das Gleitkommaformat, das double
von C entspricht und long double
), ist die Leistung besser, wenn die Speicherzugriffe auf Adressen erfolgen, die ein Vielfaches von 16 sind. Dies wird hier nicht wirklich benötigt, aber wenn es ohne das Optimierungs-Flag verwendet wird (-O2
...), tendiert der Compiler dazu, ziemlich viel generischen, nutzlosen Code zu produzieren (d.h. Code, der in manchen Fällen nützlich sein könnte, aber hier nicht).
pushl -4(%ecx)
Das hier ist etwas seltsam:an dieser Stelle das Wort an der Adresse -4(%ecx)
ist das Wort, das vor andl
ganz oben auf dem Stapel war . Der Code ruft dieses Wort ab (das übrigens die Rücksendeadresse sein sollte) und schiebt es erneut. Diese Art von emuliert, was mit einem Aufruf von einer Funktion erhalten würde, die einen 16-Byte-ausgerichteten Stapel hatte. Meine Vermutung ist, dass diese push
ist ein Überbleibsel einer Argumentkopiersequenz. Da die Funktion den Stapelzeiger angepasst hat, muss sie die Funktionsargumente kopieren, die über den alten Wert des Stapelzeigers zugänglich waren. Hier gibt es außer der Rücksprungadresse der Funktion kein Argument. Beachten Sie, dass dieses Wort nicht verwendet wird (noch einmal, dies ist Code ohne Optimierung).
pushl %ebp
movl %esp, %ebp
Dies ist der Prolog der Standardfunktion:Wir speichern %ebp
(da wir es ändern werden), dann setzen Sie %ebp
auf den Stapelrahmen zeigen. Danach %ebp
wird verwendet, um auf die Funktionsargumente zuzugreifen, wodurch %esp
entsteht wieder frei. (Ja, es gibt kein Argument, also ist es für diese Funktion nutzlos.)
pushl %ecx
Wir speichern %ecx
(Wir brauchen es beim Beenden der Funktion, um %esp
wiederherzustellen auf den Wert, den es vor andl
hatte ).
subl $20, %esp
Wir reservieren 32 Bytes auf dem Stack (denken Sie daran, dass der Stack „runterwächst“). Dieser Platz wird verwendet, um die Argumente für printf()
zu speichern (Das ist übertrieben, da es ein einziges Argument gibt, das 4 Bytes verwendet [das ist ein Zeiger]).
movl $.LC0, (%esp)
call printf
Wir „pushen“ das Argument auf printf()
(d.h. wir stellen sicher, dass %esp
zeigt auf ein Wort, das das Argument enthält, hier $.LC0
, die die Adresse der konstanten Zeichenfolge im Rodata-Abschnitt ist). Dann rufen wir printf()
an .
addl $20, %esp
Wenn printf()
zurückgibt, entfernen wir den für die Argumente zugewiesenen Platz. Diese addl
bricht ab, was der subl
oben getan.
popl %ecx
Wir stellen %ecx
wieder her (oben gedrückt); printf()
möglicherweise geändert haben (die Aufrufkonventionen beschreiben, welche Register eine Funktion ändern kann, ohne sie beim Beenden wiederherzustellen; %ecx
ist ein solches Register).
popl %ebp
Epilog der Funktion:Dies stellt %ebp
wieder her (entspricht dem pushl %ebp
oben).
leal -4(%ecx), %esp
Wir stellen %esp
wieder her auf seinen Anfangswert. Der Effekt dieses Opcodes ist das Speichern in %esp
den Wert %ecx-4
. %ecx
wurde im Opcode der ersten Funktion gesetzt. Dadurch werden alle Änderungen an %esp
rückgängig gemacht , einschließlich andl
.
ret
Funktion beenden.
.size main, .-main
Dies legt die Größe des main()
fest Funktion:zu jedem Zeitpunkt während des Zusammenbaus ".
" ist ein Alias für "die Adresse, an der wir gerade Dinge hinzufügen". Wenn hier eine weitere Anweisung hinzugefügt würde, würde sie an die durch ".
angegebene Adresse gehen ". Also ".-main
", hier ist die genaue Größe des Codes der Funktion main()
. Der .size
weist den Assembler an, diese Informationen in die Objektdatei zu schreiben.
.ident "GCC: (GNU) 4.3.0 20080428 (Red Hat 4.3.0-8)"
GCC liebt es einfach, Spuren seiner Wirkung zu hinterlassen. Dieser String landet als eine Art Kommentar in der Objektdatei. Der Linker wird es entfernen.
.section .note.GNU-stack,"",@progbits
Ein spezieller Abschnitt, in dem GCC schreibt, dass der Code einen nicht ausführbaren Stack aufnehmen kann. Dies ist der Normalfall. Ausführbare Stacks werden für einige spezielle Anwendungen benötigt (nicht Standard-C). Auf modernen Prozessoren kann der Kernel einen nicht ausführbaren Stapel erstellen (ein Stapel, der eine Ausnahme auslöst, wenn jemand versucht, einige Daten, die sich auf dem Stapel befinden, als Code auszuführen); dies wird von einigen Leuten als "Sicherheitsfeature" angesehen, weil das Platzieren von Code auf dem Stack eine gängige Methode ist, um Pufferüberläufe auszunutzen. Mit diesem Abschnitt wird die ausführbare Datei als "kompatibel mit einem nicht ausführbaren Stack" markiert, was der Kernel gerne als solches bereitstellt.
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $20, %esp
Diese Anweisungen sind in Ihrem C-Programm nicht vergleichbar, sie werden immer am Anfang jeder Funktion ausgeführt (aber es hängt vom Compiler/der Plattform ab)
movl $.LC0, (%esp)
call printf
dieser Block entspricht Ihrem Aufruf von printf(). Die erste Anweisung legt ihr Argument (einen Zeiger auf „Hallo Welt“) auf den Stack und ruft dann die Funktion auf.
addl $20, %esp
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
Diese Anweisungen sind dem ersten Block entgegengesetzt, sie sind eine Art Stack-Manipulationszeug. immer auch ausgeführt
Hier ist eine Ergänzung zu @Thomas Pornin
's Antwort.
.LC0
lokale Konstante, z. B. String-Literal..LFB0
Anfang der lokalen Funktion,.LFE0
lokale Funktion endet,
Das Suffix dieser Bezeichnung ist eine Zahl und beginnt bei 0.
Dies ist die gcc-Assembler-Konvention.