Comment obtenir la sortie de l'assembleur à partir de la source C / C ++ dans gcc?


Réponses:


422

Utilisez l' -Soption gcc (ou g ++).

gcc -S helloworld.c

Cela exécutera le préprocesseur (cpp) sur helloworld.c, effectuera la compilation initiale puis s'arrêtera avant que l'assembleur ne soit exécuté.

Par défaut, cela produira un fichier helloworld.s. Le fichier de sortie peut toujours être défini à l'aide de l' -ooption.

gcc -S -o my_asm_output.s helloworld.c

Bien sûr, cela ne fonctionne que si vous avez la source d'origine. Une alternative si vous ne disposez que du fichier objet résultant est d'utiliser objdump, en définissant l' --disassembleoption (ou -dpour la forme abrégée).

objdump -S --disassemble helloworld > helloworld.dump

Cette option fonctionne mieux si l'option de débogage est activée pour le fichier objet ( -gau moment de la compilation) et que le fichier n'a pas été supprimé.

L'exécution file helloworldvous donnera une indication du niveau de détail que vous obtiendrez en utilisant objdump.


3
Bien que cela soit correct, j'ai trouvé les résultats de la réponse de Cr McDonough plus utiles.
Rhys Ulerich

3
une utilisation supplémentaire: objdump -M intel -S --disassemble helloworld> helloworld.dump pour obtenir le vidage d'objet dans la syntaxe intel compatible avec nasm sur linux.
touchStone

2
Si vous avez une seule fonction à optimiser / vérifier, vous pouvez essayer les compilateurs C ++ interactifs en ligne, c'est-à-dire godbolt
fiorentinoing

1
@touchStone: GAS .intel_syntaxn'est pas compatible avec NASM . C'est plus comme MASM (par exemple, mov eax, symbolc'est une charge, contrairement à NASM où c'est une mov r32, imm32adresse), mais pas totalement compatible avec MASM non plus. Je le recommande fortement comme format agréable à lire, surtout si vous aimez écrire en syntaxe NASM. objdump -drwC -Mintel | lessou gcc foo.c -O1 -fverbose-asm -masm=intel -S -o- | lesssont utiles. (Voir aussi Comment supprimer le «bruit» de la sortie de l'assemblage GCC / clang? ). -masm=intelfonctionne aussi avec clang.
Peter Cordes

3
Meilleure utilisationgcc -O -fverbose-asm -S
Basile Starynkevitch

173

Cela générera du code d'assemblage avec le code C + les numéros de ligne entrelacés, pour voir plus facilement quelles lignes génèrent quel code:

# 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

Trouvé dans Algorithmes pour les programmeurs , page 3 (qui est la 15e page globale du PDF).


3
(C'est en fait à la page (numérotée) 3 (qui est la 15e page du PDF))
Grumdrig

1
Malheureusement, assur OS X ne connaît pas ces drapeaux. Si c'était le cas, vous pourriez probablement utiliser cette ligne sur une ligne -Wapour passer des options à as.
Grumdrig

23
g++ -g -O0 -c -fverbose-asm -Wa,-adhln test.cpp > test.lstserait la version abrégée de cela.
legends2k

4
Vous pouvez également utiliser soit gcc -c -g -Wa,-ahl=test.s test.cougcc -c -g -Wa,-a,-ad test.c > test.txt
phuclv

1
Un article de blog expliquant cela plus en détail, y compris la version à commande unique comme les légendes et Lu'u publiée. Mais pourquoi -O0? C'est plein de charges / magasins qui rendent difficile le suivi d'une valeur et ne vous dit rien sur l'efficacité du code optimisé.
Peter Cordes

51

La ligne de commande suivante provient du blog de Christian Garbin

g++ -g -O -Wa,-aslh horton_ex2_05.cpp >list.txt

J'ai exécuté G ++ à partir d'une fenêtre DOS sur Win-XP, contre une routine qui contient un cast implicite

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'

Le résultat est un code généré assemblé, intercalé avec le code C ++ d'origine (le code C ++ est affiché sous forme de commentaires dans le flux asm généré)

  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)

@Paladin - Pas nécessairement. L'OP consistait à obtenir l'équivalent de sortie de l'assembleur du code source C / C ++, cela obtient la liste, qui, je le reconnais, est plus utile pour comprendre ce que le compilateur et l'optimiseur font. Mais cela provoquerait l'assembleur lui-même, car il n'attend pas les numéros de ligne et compilé des octets hors de la gauche des instructions d'assemblage.
Jesse Chisholm

Utilisez au moins -O2, ou les options d'optimisation que vous utilisez réellement lors de la construction de votre projet, si vous voulez voir comment gcc optimise votre code. (Ou si vous utilisez LTO, comme vous le devriez, alors vous devez démonter la sortie de l'éditeur de liens pour voir ce que vous obtenez vraiment.)
Peter Cordes

27

Utilisez le commutateur -S

g++ -S main.cpp

ou aussi avec gcc

gcc -S main.c

Voir aussi ceci


7
Consultez la FAQ: "Il est également parfaitement possible de poser et de répondre à votre propre question de programmation". Le fait étant que maintenant StackOverflow contient le Q&A comme ressource pour les autres.
Steve Jessop

Et peut-être que quelqu'un d'autre viendra et vous surprendra avec une meilleure réponse, bien que la mienne soit parfois un peu
bavarde

Il y a même le bouton répondre à votre propre question.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

13

Si ce que vous voulez voir dépend de la liaison de la sortie, objdump sur le fichier / exécutable de l'objet de sortie peut également être utile en plus du gcc -S susmentionné. Voici un script très utile de Loren Merritt qui convertit la syntaxe objdump par défaut en syntaxe nasm plus lisible:

#!/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;

Je soupçonne que cela peut également être utilisé sur la sortie de gcc -S.


2
Pourtant, ce script est un hack sale qui ne convertit pas complètement la syntaxe. Par exemple, ce mov eax,ds:0x804b794n'est pas très NASMish. En outre, parfois, il supprime simplement des informations utiles: movzx eax,[edx+0x1]laisse le lecteur deviner si l'opérande mémoire était byteou word.
Ruslan

Pour démonter la syntaxe NASM en premier lieu, utilisez Agner Fog'sobjconv . Vous pouvez le faire démonter vers stdout avec le fichier de sortie = /dev/stdout, afin que vous puissiez le diriger vers l' lessaffichage. Il y en a aussi ndisasm, mais il ne démonte que les binaires plats et ne connaît pas les fichiers objets (ELF / PE).
Peter Cordes

9

Comme tout le monde l'a souligné, utilisez l' -Soption GCC. Je voudrais également ajouter que les résultats peuvent varier (énormément!) Selon que vous ajoutez ou non des options d'optimisation ( -O0pour aucune, -O2pour une optimisation agressive).

Sur les architectures RISC en particulier, le compilateur transforme souvent le code presque au-delà de la reconnaissance lors de l'optimisation. C'est impressionnant et fascinant de regarder les résultats!


9

Eh bien, comme tout le monde l'a dit, utilisez l'option -S. Si vous utilisez l'option -save-temps, vous pouvez également obtenir le fichier prétraité ( .i), le fichier d'assemblage ( .s) et le fichier objet (*. O). (obtenez chacun d'eux en utilisant -E, -S et -c.)


8

Comme mentionné précédemment, regardez le drapeau -S.

Il convient également de regarder la famille de drapeaux '-fdump-tree', en particulier '-fdump-tree-all', qui vous permet de voir certaines des formes intermédiaires de gcc. Ceux-ci peuvent souvent être plus lisibles que l'assembleur (du moins pour moi) et vous permettent de voir comment les passes d'optimisation fonctionnent.


8

Si vous recherchez un assemblage LLVM:

llvm-gcc -emit-llvm -S hello.c


8

-save-temps

Cela a été mentionné sur https://stackoverflow.com/a/17083009/895245, mais permettez-moi de l'illustrer davantage.

Le gros avantage de cette option -Sest qu'il est très facile de l'ajouter à n'importe quel script de build, sans interférer beaucoup dans la build elle-même.

Quand vous faites:

gcc -save-temps -c -o main.o main.c

principal c

#define INC 1

int myfunc(int i) {
    return i + INC;
}

et maintenant, en plus de la sortie normale main.o, le répertoire de travail actuel contient également les fichiers suivants:

  • main.i est un bonus et contient le fichier pré-traité:

    # 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 contient l'assemblage généré souhaité:

        .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

Si vous souhaitez le faire pour un grand nombre de fichiers, envisagez d'utiliser à la place:

 -save-temps=obj

qui enregistre les fichiers intermédiaires dans le même répertoire que la -osortie de l' objet au lieu du répertoire de travail actuel, évitant ainsi les conflits potentiels de nom de base.

Une autre chose intéressante à propos de cette option est que si vous ajoutez -v:

gcc -save-temps -c -o main.o -v main.c

il montre en fait les fichiers explicites utilisés au lieu de laids temporaires sous /tmp, il est donc facile de savoir exactement ce qui se passe, ce qui inclut les étapes de prétraitement / compilation / assemblage:

/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

Testé dans Ubuntu 19.04 amd64, GCC 8.3.0.




3

Sortie de ces commnades

Voici les étapes pour voir / imprimer le code d'assemblage de n'importe quel programme C sur votre Windows

console / terminal / invite de commande:

  1. Écrivez un programme C dans un éditeur de code C comme des blocs de code et enregistrez-le avec une extension .c

  2. Compilez-le et exécutez-le.

  3. Une fois exécuté avec succès, allez dans le dossier où vous avez installé votre compilateur gcc et donnez

    commande suivante pour obtenir un fichier '.s' du fichier '.c'

    C: \ gcc> gcc -S chemin complet du fichier C ENTER

    Un exemple de commande (comme dans mon cas)

    C: \ gcc> gcc -SD: \ Aa_C_Certified \ alternative_letters.c

    Cela génère un fichier «.s» du fichier «.c» d'origine

4. Après cela, tapez la commande suivante

C; \ gcc> cpp filename.s ENTER

Exemple de commande (comme dans mon cas)

C; \ gcc> cpp Alternate_letters.s

Cela imprimera / affichera le code du langage d'assemblage complet de votre programme C.


2

Utilisez "-S" en option. Il affiche la sortie de l'assemblage dans le terminal.


Pour afficher dans le terminal, utilisez gcc foo.c -masm=intel -fverbose-asm -O3 -S -o- |less. -Sà lui seul crée foo.s.
Peter Cordes

2

récemment, je voulais connaître l'assemblage de chaque fonction dans un programme,
c'est ainsi que je l'ai fait.

$ 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

2

Voici une solution pour C utilisant gcc:

gcc -S program.c && gcc program.c -o output
  1. Ici, la première partie stocke la sortie d'assembly du programme dans le même nom de fichier que Program mais avec une extension .s modifiée , vous pouvez l'ouvrir comme n'importe quel fichier texte normal.

  2. La deuxième partie ici compile votre programme pour une utilisation réelle et génère un exécutable pour votre programme avec un nom de fichier spécifié.

Le programme.c utilisé ci-dessus est le nom de votre programme et la sortie est le nom de l'exécutable que vous souhaitez générer.

BTW C'est mon premier post sur StackOverFlow: -}

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.