Wie erhält man Assembler-Ausgabe von C / C ++ - Quelle in gcc?

Antworten:

421

Verwenden Sie die -SOption 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 -oOption 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 --disassembleOption (oder -dfü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 ( -gzur Kompilierungszeit) aktiviert ist und die Datei nicht entfernt wurde.

Wenn Sie ausgeführt file helloworldwerden, erhalten Sie einen Hinweis auf den Detaillierungsgrad, den Sie mit objdump erhalten.

Andrew Edgecombe
quelle
3
Obwohl dies korrekt ist, fand ich die Ergebnisse von Cr McDonoughs Antwort nützlicher.
Rhys Ulerich
3
Verwenden Sie zusätzlich: objdump -M intel -S --disassemble helloworld> helloworld.dump, um den Objektspeicherauszug in Intel-Syntax abzurufen, die mit nasm unter Linux kompatibel ist.
touchStone
2
Wenn Sie eine einzelne Funktion zu optimieren / Check haben, dann können Sie einen Versuch zu Online Interactive C ++ Compiler also geben Godbolt
fiorentinoing
1
@touchStone: GAS .intel_syntaxist nicht mit NASM kompatibel . Es ist eher wie MASM (z. B. mov eax, symboleine Last, im Gegensatz zu NASM, wo es sich um eine mov r32, imm32Adresse 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 | lessoder gcc foo.c -O1 -fverbose-asm -masm=intel -S -o- | lesssind nützlich. (Siehe auch Entfernen von „Rauschen“ von der Ausgabe der GCC / Clang-Baugruppe? ) -masm=intelfunktioniert auch mit clang.
Peter Cordes
3
Bessere Verwendunggcc -O -fverbose-asm -S
Basile Starynkevitch
173

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).

PhirePhly
quelle
3
(Das ist tatsächlich auf Seite (nummeriert) 3 (das ist die 15. Seite des PDF))
Grumdrig
1
Leider askennt 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.
Grumdrig
23
g++ -g -O0 -c -fverbose-asm -Wa,-adhln test.cpp > test.lstwäre die Kurzhandversion davon.
Legends2k
4
Sie können auch entweder gcc -c -g -Wa,-ahl=test.s test.codergcc -c -g -Wa,-a,-ad test.c > test.txt
phuclv
1
Ein Blog-Beitrag , der dies ausführlicher erklärt, einschließlich der Ein-Befehl-Version wie Legenden und Lu'u. Aber warum -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.
Peter Cordes
51

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)
Cr McDonough
quelle
@ Paladin - Nicht unbedingt. Beim OP ging es darum, das Assembler-Ausgabeäquivalent des C / C ++ - Quellcodes zu erhalten. Dadurch wird das Listing abgerufen, das meiner Meinung nach nützlicher ist, um zu verstehen, was der Compiler und der Optimierer tun. Dies würde jedoch dazu führen, dass der Assembler selbst barf, da er die Zeilennummern nicht erwartet und Bytes außerhalb der Assembler-Anweisungen kompiliert.
Jesse Chisholm
Verwenden Sie mindestens die -O2Optimierungsoptionen, 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.)
Peter Cordes
27

Verwenden Sie den -S-Schalter

g++ -S main.cpp

oder auch mit gcc

gcc -S main.c

Siehe auch dies

Doug T.
quelle
7
Überprüfen Sie die FAQ: "Es ist auch vollkommen in Ordnung, Ihre eigene Programmierfrage zu stellen und zu beantworten". Der Punkt ist, dass StackOverflow jetzt die Fragen und Antworten als Ressource für andere enthält.
Steve Jessop
Und vielleicht kommt jemand anderes und überrascht Sie mit einer besseren Antwort, obwohl meine manchmal etwas ausführlich sein könnte ...
PhirePhly
Es gibt sogar die Antwort auf Ihre eigene Frage-Schaltfläche.
Ciro Santilli 法轮功 冠状 病 六四 事件 24
13

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.

Dunkler Shikari
quelle
2
Trotzdem ist dieses Skript ein schmutziger Hack, der die Syntax nicht vollständig konvertiert. ZB mov eax,ds:0x804b794ist 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.
Ruslan
Verwenden Sie, um die NASM-Syntax zunächst zu zerlegen Agner Fog'sobjconv . Sie können es mit der Ausgabedatei = in stdout zerlegen lassen /dev/stdout, damit Sie es lesszur Anzeige weiterleiten können. Es gibt auch ndisasm, aber es zerlegt nur flache Binärdateien und weiß nichts über Objektdateien (ELF / PE).
Peter Cordes
9

Wie jeder darauf hingewiesen hat, nutzen Sie die -SOption 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 ( -O0für keine, -O2fü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!

Dan Lenski
quelle
9

Verwenden Sie, wie alle sagten, die Option -S. Wenn Sie die Option -save-temps verwenden, können Sie auch vorverarbeitete Dateien ( .i), Assembly-Dateien ( .s) und Objektdateien (*. O) abrufen. (Holen Sie sich jeden von ihnen mit -E, -S und -c.)

METADATA
quelle
8

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.

Chris Jefferson
quelle
8

Wenn Sie nach einer LLVM-Baugruppe suchen:

llvm-gcc -emit-llvm -S hello.c
mcandre
quelle
8

-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.oenthä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 -oObjektausgabe 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.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
7

Verwenden Sie die Option -S:

gcc -S program.c
Jeremy Ruten
quelle
3

Ausgabe dieser Commnads

Hier sind die Schritte zum Anzeigen / Drucken des Assembler-Codes eines C-Programms unter Windows

Konsole / Terminal / Eingabeaufforderung:

  1. Schreiben Sie ein C-Programm in einen C-Code-Editor wie Codeblöcke und speichern Sie es mit der Erweiterung .c

  2. Kompilieren und ausführen.

  3. 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.

Ashutosh K Singh
quelle
2

Verwenden Sie optional "-S". Es zeigt die Baugruppenausgabe im Terminal an.

Pizearke
quelle
Verwenden Sie zum Anzeigen im Terminal gcc foo.c -masm=intel -fverbose-asm -O3 -S -o- |less. -Sauf eigene Faust schafft foo.s.
Peter Cordes
2

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
Abhishek DK
quelle
2

Hier ist eine Lösung für C mit gcc:

gcc -S program.c && gcc program.c -o output
  1. 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.

  2. 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: -}

Himanshu Pal
quelle