Wie macht man das?
Wenn ich analysieren möchte, wie etwas kompiliert wird, wie würde ich den ausgegebenen Assemblycode erhalten?
Wie macht man das?
Wenn ich analysieren möchte, wie etwas kompiliert wird, wie würde ich den ausgegebenen Assemblycode erhalten?
Antworten:
Verwenden Sie die -S
Option zu gcc (oder g ++).
gcc -S helloworld.c
Dadurch wird der Präprozessor (cpp) über helloworld.c ausgeführt, die erste Kompilierung durchgeführt und dann gestoppt, bevor der Assembler ausgeführt wird.
Standardmäßig wird eine Datei ausgegeben helloworld.s
. Die Ausgabedatei kann weiterhin mit der -o
Option eingestellt werden.
gcc -S -o my_asm_output.s helloworld.c
Dies funktioniert natürlich nur, wenn Sie die Originalquelle haben. Eine Alternative, wenn Sie nur die resultierende Objektdatei haben, ist die Verwendung objdump
, indem Sie die --disassemble
Option (oder -d
für das abgekürzte Formular) festlegen.
objdump -S --disassemble helloworld > helloworld.dump
Diese Option funktioniert am besten, wenn die Debugging-Option für die Objektdatei ( -g
zur Kompilierungszeit) aktiviert ist und die Datei nicht entfernt wurde.
Wenn Sie ausgeführt file helloworld
werden, erhalten Sie einen Hinweis auf den Detaillierungsgrad, den Sie mit objdump erhalten.
.intel_syntax
ist nicht mit NASM kompatibel . Es ist eher wie MASM (z. B. mov eax, symbol
eine Last, im Gegensatz zu NASM, wo es sich um eine mov r32, imm32
Adresse handelt), aber auch nicht vollständig mit MASM kompatibel. Ich empfehle es sehr als schönes Format zum Lesen, besonders wenn Sie jedoch in NASM-Syntax schreiben möchten. objdump -drwC -Mintel | less
oder gcc foo.c -O1 -fverbose-asm -masm=intel -S -o- | less
sind nützlich. (Siehe auch Entfernen von „Rauschen“ von der Ausgabe der GCC / Clang-Baugruppe? ) -masm=intel
funktioniert auch mit clang.
gcc -O -fverbose-asm -S
Dadurch wird ein Assembler-Code mit verwobenem C-Code + Zeilennummern generiert, um leichter zu erkennen, welche Zeilen welchen Code generieren:
# create assembler code:
g++ -S -fverbose-asm -g -O2 test.cc -o test.s
# create asm interlaced with source lines:
as -alhnd test.s > test.lst
Gefunden in Algorithmen für Programmierer , Seite 3 (dies ist die 15. Gesamtseite des PDF).
as
kennt OS X diese Flags unter OS X nicht. In diesem Fall könnten Sie dies wahrscheinlich einzeilig verwenden -Wa
, um Optionen an zu übergeben as
.
g++ -g -O0 -c -fverbose-asm -Wa,-adhln test.cpp > test.lst
wäre die Kurzhandversion davon.
gcc -c -g -Wa,-ahl=test.s test.c
odergcc -c -g -Wa,-a,-ad test.c > test.txt
-O0
? Das ist voll von Lasten / Speichern, die es schwierig machen, einen Wert zu verfolgen, und sagt nichts darüber aus, wie effizient der optimierte Code sein wird.
Die folgende Befehlszeile stammt aus Christian Garbins Blog
g++ -g -O -Wa,-aslh horton_ex2_05.cpp >list.txt
Ich habe G ++ in einem DOS-Fenster unter Win-XP gegen eine Routine ausgeführt, die eine implizite Umwandlung enthält
c:\gpp_code>g++ -g -O -Wa,-aslh horton_ex2_05.cpp >list.txt
horton_ex2_05.cpp: In function `int main()':
horton_ex2_05.cpp:92: warning: assignment to `int' from `double'
Die Ausgabe besteht aus zusammengesetztem generiertem Code, der mit dem ursprünglichen C ++ - Code durchsetzt ist (der C ++ - Code wird als Kommentare im generierten asm-Stream angezeigt).
16:horton_ex2_05.cpp **** using std::setw;
17:horton_ex2_05.cpp ****
18:horton_ex2_05.cpp **** void disp_Time_Line (void);
19:horton_ex2_05.cpp ****
20:horton_ex2_05.cpp **** int main(void)
21:horton_ex2_05.cpp **** {
164 %ebp
165 subl $128,%esp
?GAS LISTING C:\DOCUME~1\CRAIGM~1\LOCALS~1\Temp\ccx52rCc.s
166 0128 55 call ___main
167 0129 89E5 .stabn 68,0,21,LM2-_main
168 012b 81EC8000 LM2:
168 0000
169 0131 E8000000 LBB2:
169 00
170 .stabn 68,0,25,LM3-_main
171 LM3:
172 movl $0,-16(%ebp)
-O2
Optimierungsoptionen, die Sie beim Erstellen Ihres Projekts tatsächlich verwenden, wenn Sie sehen möchten, wie gcc Ihren Code optimiert. (Oder wenn Sie LTO verwenden, wie Sie sollten, dann müssen Sie die Linker-Ausgabe zerlegen, um zu sehen, was Sie wirklich bekommen.)
Wenn das, was Sie sehen möchten, von der Verknüpfung der Ausgabe abhängt, kann objdump in der Ausgabeobjektdatei / ausführbaren Datei zusätzlich zu den oben genannten gcc -S auch nützlich sein. Hier ist ein sehr nützliches Skript von Loren Merritt, das die Standard-Objdump-Syntax in die besser lesbare Nasm-Syntax konvertiert:
#!/usr/bin/perl -w
$ptr='(BYTE|WORD|DWORD|QWORD|XMMWORD) PTR ';
$reg='(?:[er]?(?:[abcd]x|[sd]i|[sb]p)|[abcd][hl]|r1?[0-589][dwb]?|mm[0-7]|xmm1?[0-9])';
open FH, '-|', '/usr/bin/objdump', '-w', '-M', 'intel', @ARGV or die;
$prev = "";
while(<FH>){
if(/$ptr/o) {
s/$ptr(\[[^\[\]]+\],$reg)/$2/o or
s/($reg,)$ptr(\[[^\[\]]+\])/$1$3/o or
s/$ptr/lc $1/oe;
}
if($prev =~ /\t(repz )?ret / and
$_ =~ /\tnop |\txchg *ax,ax$/) {
# drop this line
} else {
print $prev;
$prev = $_;
}
}
print $prev;
close FH;
Ich vermute, dass dies auch für die Ausgabe von gcc -S verwendet werden kann.
mov eax,ds:0x804b794
ist nicht sehr NASMish. Manchmal werden auch nur nützliche Informationen entfernt: movzx eax,[edx+0x1]
Der Leser kann erraten, ob der Speicheroperand warbyte
oder nicht word
.
objconv
. Sie können es mit der Ausgabedatei = in stdout zerlegen lassen /dev/stdout
, damit Sie es less
zur Anzeige weiterleiten können. Es gibt auch ndisasm
, aber es zerlegt nur flache Binärdateien und weiß nichts über Objektdateien (ELF / PE).
Wie jeder darauf hingewiesen hat, nutzen Sie die -S
Option für GCC. Ich möchte auch hinzufügen, dass die Ergebnisse (stark!) Variieren können, je nachdem, ob Sie Optimierungsoptionen hinzufügen oder nicht ( -O0
für keine, -O2
für aggressive Optimierung).
Insbesondere bei RISC-Architekturen transformiert der Compiler den Code bei der Optimierung häufig fast bis zur Unkenntlichkeit. Es ist beeindruckend und faszinierend, die Ergebnisse zu betrachten!
Schauen Sie sich, wie bereits erwähnt, die Flagge -S an.
Es lohnt sich auch, einen Blick auf die Flaggenfamilie '-fdump-tree' zu werfen, insbesondere auf '-fdump-tree-all', mit der Sie einige der Zwischenformen von gcc sehen können. Diese sind oft besser lesbar als Assembler (zumindest für mich) und lassen Sie sehen, wie Optimierungsdurchläufe funktionieren.
Ich sehe diese Möglichkeit nicht unter den Antworten, wahrscheinlich weil die Frage aus dem Jahr 2008 stammt, aber 2018 können Sie die Online-Website von Matt Goldbolt verwenden https://godbolt.org verwenden
Sie können sein Projekt auch lokal klonen und sein Projekt https://github.com/mattgodbolt/compiler-explorer ausführen
-save-temps
Dies wurde unter https://stackoverflow.com/a/17083009/895245 erwähnt aber ich möchte es weiter veranschaulichen.
Der große Vorteil dieser Option gegenüber -S
ist, dass es sehr einfach ist, sie zu jedem Build-Skript hinzuzufügen, ohne den Build selbst stark zu beeinträchtigen.
Wenn Sie das tun:
gcc -save-temps -c -o main.o main.c
Haupt c
#define INC 1
int myfunc(int i) {
return i + INC;
}
und jetzt main.o
enthält das aktuelle Arbeitsverzeichnis neben der normalen Ausgabe auch die folgenden Dateien:
main.i
ist ein Bonus und enthält die vorverarbeitete Datei:
# 1 "main.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "main.c"
int myfunc(int i) {
return i + 1;
}
main.s
enthält die gewünschte generierte Baugruppe:
.file "main.c"
.text
.globl myfunc
.type myfunc, @function
myfunc:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
addl $1, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size myfunc, .-myfunc
.ident "GCC: (Ubuntu 8.3.0-6ubuntu1) 8.3.0"
.section .note.GNU-stack,"",@progbits
Wenn Sie dies für eine große Anzahl von Dateien tun möchten, sollten Sie stattdessen Folgendes verwenden:
-save-temps=obj
Dadurch werden die Zwischendateien im selben Verzeichnis wie die -o
Objektausgabe anstelle des aktuellen Arbeitsverzeichnisses gespeichert , wodurch potenzielle Konflikte mit Basisnamen vermieden werden.
Eine weitere coole Sache an dieser Option ist, wenn Sie hinzufügen -v
:
gcc -save-temps -c -o main.o -v main.c
Es zeigt tatsächlich die expliziten Dateien, die anstelle von hässlichen Provisorien verwendet werden /tmp
, so dass es einfach ist, genau zu wissen, was gerade passiert, einschließlich der Schritte Vorverarbeitung / Kompilierung / Assemblierung:
/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu main.c -mtune=generic -march=x86-64 -fpch-preprocess -fstack-protector-strong -Wformat -Wformat-security -o main.i
/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -fpreprocessed main.i -quiet -dumpbase main.c -mtune=generic -march=x86-64 -auxbase-strip main.o -version -fstack-protector-strong -Wformat -Wformat-security -o main.s
as -v --64 -o main.o main.s
Getestet in Ubuntu 19.04 amd64, GCC 8.3.0.
Verwenden Sie die Option -S:
gcc -S program.c
Von: http://www.delorie.com/djgpp/v2faq/faq8_20.html
gcc -c -g -Wa, -a, -ad [andere GCC-Optionen] foo.c> foo.lst
alternativ zu PhirePhlys Antwort Oder verwenden Sie einfach -S, wie alle sagten.
Hier sind die Schritte zum Anzeigen / Drucken des Assembler-Codes eines C-Programms unter Windows
Konsole / Terminal / Eingabeaufforderung:
Schreiben Sie ein C-Programm in einen C-Code-Editor wie Codeblöcke und speichern Sie es mit der Erweiterung .c
Kompilieren und ausführen.
Wechseln Sie nach erfolgreicher Ausführung zu dem Ordner, in dem Sie Ihren gcc-Compiler installiert haben, und geben Sie den ein
Der folgende Befehl ruft eine '.s'-Datei der' .c'-Datei ab
C: \ gcc> gcc -S Vollständiger Pfad der C-Datei ENTER
Ein Beispielbefehl (wie in meinem Fall)
C: \ gcc> gcc -SD: \ Aa_C_Certified \ alternative_letters.c
Dies gibt eine '.s'-Datei der ursprünglichen' .c'-Datei aus
4. Geben Sie danach den folgenden Befehl ein
C; \ gcc> cpp filename.s ENTER
Beispielbefehl (wie in meinem Fall)
C; \ gcc> cpp alternative_letters.s
Dadurch wird der gesamte Assembler-Code Ihres C-Programms gedruckt / ausgegeben.
Verwenden Sie optional "-S". Es zeigt die Baugruppenausgabe im Terminal an.
gcc foo.c -masm=intel -fverbose-asm -O3 -S -o- |less
. -S
auf eigene Faust schafft foo.s
.
Vor kurzem wollte ich wissen, wie die einzelnen Funktionen in einem Programm zusammengesetzt
sind. So habe ich es gemacht.
$ gcc main.c // main.c source file
$ gdb a.exe // gdb a.out in linux
(gdb) disass main // note here main is a function
// similary it can be done for other functions
Hier ist eine Lösung für C mit gcc:
gcc -S program.c && gcc program.c -o output
Hier speichert der erste Teil die Assembly-Ausgabe des Programms unter demselben Dateinamen wie Program, aber mit der geänderten Erweiterung .s können Sie sie wie jede normale Textdatei öffnen.
Der zweite Teil hier kompiliert Ihr Programm für die tatsächliche Verwendung und generiert eine ausführbare Datei für Ihr Programm mit einem angegebenen Dateinamen.
Das oben verwendete Programm.c ist der Name Ihres Programms und die Ausgabe ist der Name der ausführbaren Datei, die Sie generieren möchten.
Übrigens ist es mein erster Beitrag auf StackOverFlow: -}