Utiliser GCC pour produire un assemblage lisible?


256

Je me demandais comment utiliser GCC sur mon fichier source C pour vider une version mnémonique du code machine afin que je puisse voir dans quoi mon code était compilé. Vous pouvez le faire avec Java mais je n'ai pas réussi à trouver un moyen avec GCC.

J'essaie de réécrire une méthode C dans l'assemblage et voir comment GCC le fait serait d'une grande aide.


25
notez que «bytecode» signifie généralement le code consommé par une machine virtuelle, comme JVM ou CLR .NET. La sortie de GCC est mieux appelée «code machine», «langage machine» ou «langage d'assemblage»
Javier

2
J'ai ajouté une réponse en utilisant Godbolt car c'est un outil très puissant pour expérimenter rapidement la façon dont différentes options affectent votre génération de code.
Shafik Yaghmour



Pour plus de conseils sur la façon de rendre la sortie asm lisible par l'homme, voir aussi: Comment supprimer le «bruit» de la sortie de l'assemblage GCC / clang?
Peter Cordes

Réponses:


335

Si vous compilez avec des symboles de débogage, vous pouvez utiliser objdumppour produire un désassemblage plus lisible.

>objdump --help
[...]
-S, --source             Intermix source code with disassembly
-l, --line-numbers       Include line numbers and filenames in output

objdump -drwC -Mintel est sympa:

  • -raffiche les noms des symboles lors des délocalisations (comme vous pouvez le voir putsdans les callinstructions ci-dessous)
  • -R affiche les relocalisations de liens dynamiques / noms de symboles (utile sur les bibliothèques partagées)
  • -C démêle les noms des symboles C ++
  • -w est un mode "large": il n'encapsule pas les octets de code machine
  • -Mintel: utilisez GAS / binutils comme une .intel_syntax noprefixsyntaxe MASM au lieu d'AT & T
  • -S: entrelacer les lignes source avec démontage.

Vous pourriez mettre quelque chose comme alias disas="objdump -drwCS -Mintel"dans votre~/.bashrc


Exemple:

> gcc -g -c test.c
> objdump -d -M intel -S test.o

test.o:     file format elf32-i386


Disassembly of section .text:

00000000 <main>:
#include <stdio.h>

int main(void)
{
   0:   55                      push   ebp
   1:   89 e5                   mov    ebp,esp
   3:   83 e4 f0                and    esp,0xfffffff0
   6:   83 ec 10                sub    esp,0x10
    puts("test");
   9:   c7 04 24 00 00 00 00    mov    DWORD PTR [esp],0x0
  10:   e8 fc ff ff ff          call   11 <main+0x11>

    return 0;
  15:   b8 00 00 00 00          mov    eax,0x0
}
  1a:   c9                      leave  
  1b:   c3                      ret

3
Existe-t-il un commutateur pour saisir uniquement les instructions Intel?
James

3
Ce sont toutes des instructions Intel car elles fonctionnent sur des processeurs Intel: D.
toto

12
@toto Je pense qu'il veut dire la syntaxe Intel au lieu de la syntaxe AT&T
Amok

7
Il est possible de renoncer au fichier objet intermédiaire avec le en utilisant la séquence de commutation -Wa,-adhln -g to gcc. Cela suppose que l'assembleur est du gaz et ce n'est pas toujours le cas.
Marc Butler

8
@James Oui, approvisionnement -Mintel.
fuz

106

Si vous donnez le drapeau à GCC-fverbose-asm , il

Mettez des informations supplémentaires dans le code d'assembly généré pour le rendre plus lisible.

[...] Les commentaires ajoutés comprennent:

  • des informations sur la version du compilateur et les options de ligne de commande,
  • les lignes de code source associées aux instructions d'assemblage, sous la forme FILENAME: LINENUMBER: CONTENT OF LINE,
  • des conseils sur les expressions de haut niveau qui correspondent aux différents opérandes d'instructions d'assemblage.

Mais alors, je perdrais tout le commutateur utilisé pour objdump- objdump -drwCS -Mintel, alors comment puis-je utiliser quelque chose comme verboseavec objdump? Pour que je puisse avoir des commentaires dans le code asm, comme -fverbose-asmdans gcc?
Herdsman

1
@Herdsman: vous ne pouvez pas. Les éléments supplémentaires ajoutés -fverbose-asmsont sous la forme de commentaires dans la syntaxe asm de la sortie, et non de directives qui mettront quelque chose de plus dans le .ofichier. Tout est jeté au moment de l'assemblage. Regardez la sortie asm du compilateur au lieu du démontage, par exemple sur godbolt.org où vous pouvez facilement la faire correspondre avec la ligne source via le survol de la souris et la coloration des lignes source / asm correspondantes. Comment supprimer le "bruit" de la sortie de l'assemblage GCC / clang?
Peter Cordes

75

Utilisez le commutateur -S (note: S majuscule) vers GCC, et il émettra le code assembleur dans un fichier avec une extension .s. Par exemple, la commande suivante:

gcc -O2 -S foo.c

laissera le code assembleur généré dans le fichier foo.s.

Extrait directement de http://www.delorie.com/djgpp/v2faq/faq8_20.html (mais en supprimant les erreurs -c)


35
Vous ne devez pas mélanger -c et -S, utilisez uniquement l'un d'eux. Dans ce cas, l'un est prioritaire sur l'autre, probablement en fonction de l'ordre dans lequel ils sont utilisés.
Adam Rosenfield

4
@AdamRosenfield Une référence à propos de 'ne devrait pas mélanger -c et -S'? Si c'est vrai, nous pouvons en rappeler l'auteur et le modifier.
Tony

5
@Tony: gcc.gnu.org/onlinedocs/gcc/Overall-Options.html#Overall-Options "Vous pouvez utiliser ... l' une des options -c, -S ou -E pour indiquer où gcc doit s'arrêter. "
Nate Eldredge

1
Si vous voulez toutes les sorties intermédiaires, utilisez gcc -march=native -O3 -save-temps. Vous pouvez toujours utiliser -cpour vous arrêter à la création d'un fichier objet sans essayer de lier, ou quoi que ce soit.
Peter Cordes

2
-save-tempsest intéressant car il décharge en une seule fois le code généré par le code exact, tandis que l'autre option d'appeler le compilateur -Ssignifie la compilation deux fois, et éventuellement avec des options différentes. Mais -save-temps vide tout dans le répertoire courant, ce qui est un peu compliqué. On dirait qu'il est plus destiné à être une option de débogage pour GCC plutôt qu'un outil pour inspecter votre code.
Stéphane Gourichon

50

L'utilisation du -Scommutateur vers GCC sur les systèmes x86 produit un vidage de la syntaxe AT&T, par défaut, qui peut être spécifié avec le -masm=attcommutateur, comme ceci:

gcc -S -masm=att code.c

Alors que si vous souhaitez produire un vidage dans la syntaxe Intel, vous pouvez utiliser le -masm=intelcommutateur, comme ceci:

gcc -S -masm=intel code.c

(Les deux produisent des vidages de code.cdans leurs différentes syntaxes, code.srespectivement dans le fichier )

Afin de produire des effets similaires avec objdump, vous voudriez utiliser le commutateur --disassembler-options= intel/ att, un exemple (avec des vidages de code pour illustrer les différences de syntaxe):

 $ objdump -d --disassembler-options=att code.c
 080483c4 <main>:
 80483c4:   8d 4c 24 04             lea    0x4(%esp),%ecx
 80483c8:   83 e4 f0                and    $0xfffffff0,%esp
 80483cb:   ff 71 fc                pushl  -0x4(%ecx)
 80483ce:   55                      push   %ebp
 80483cf:   89 e5                   mov    %esp,%ebp
 80483d1:   51                      push   %ecx
 80483d2:   83 ec 04                sub    $0x4,%esp
 80483d5:   c7 04 24 b0 84 04 08    movl   $0x80484b0,(%esp)
 80483dc:   e8 13 ff ff ff          call   80482f4 <puts@plt>
 80483e1:   b8 00 00 00 00          mov    $0x0,%eax
 80483e6:   83 c4 04                add    $0x4,%esp 
 80483e9:   59                      pop    %ecx
 80483ea:   5d                      pop    %ebp
 80483eb:   8d 61 fc                lea    -0x4(%ecx),%esp
 80483ee:   c3                      ret
 80483ef:   90                      nop

et

$ objdump -d --disassembler-options=intel code.c
 080483c4 <main>:
 80483c4:   8d 4c 24 04             lea    ecx,[esp+0x4]
 80483c8:   83 e4 f0                and    esp,0xfffffff0
 80483cb:   ff 71 fc                push   DWORD PTR [ecx-0x4]
 80483ce:   55                      push   ebp
 80483cf:   89 e5                   mov    ebp,esp
 80483d1:   51                      push   ecx
 80483d2:   83 ec 04                sub    esp,0x4
 80483d5:   c7 04 24 b0 84 04 08    mov    DWORD PTR [esp],0x80484b0
 80483dc:   e8 13 ff ff ff          call   80482f4 <puts@plt>
 80483e1:   b8 00 00 00 00          mov    eax,0x0
 80483e6:   83 c4 04                add    esp,0x4
 80483e9:   59                      pop    ecx
 80483ea:   5d                      pop    ebp
 80483eb:   8d 61 fc                lea    esp,[ecx-0x4]
 80483ee:   c3                      ret    
 80483ef:   90                      nop

Ce que le ... gcc -S -masm=intel test.cn'a pas exactement le travail pour moi, je suis un peu crossbreed d'Intel et AT & T syntaxe comme ceci: mov %rax, QWORD PTR -24[%rbp], au lieu de cela: movq -24(%rbp), %rax.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

1
Bon conseil. Il convient de noter que cela fonctionne également lors de la sortie parallèle de .ofichiers ASM, c'est-à-dire via-Wa,-ahls -o yourfile.o yourfile.cpp>yourfile.asm
underscore_d

Pourrait utiliser l' -Moption, c'est la même --disassembler-optionschose mais beaucoup plus court, par exempleobjdump -d -M intel a.out | less -N
Eric Wang

34

godbolt est un outil très utile, ils ne contiennent que des compilateurs C ++ mais vous pouvez utiliser -x cflag pour le faire traiter le code comme C. Il générera ensuite une liste d'assembly pour votre code côte à côte et vous pouvez utiliser l' Colouriseoption pour générer des barres colorées pour indiquer visuellement quel code source correspond à l'assembly généré. Par exemple le code suivant:

#include <stdio.h>

void func()
{
  printf( "hello world\n" ) ;
}

en utilisant la ligne de commande suivante:

-x c -std=c99 -O3

et Colourisegénérerait ce qui suit:

entrez la description de l'image ici


Ce serait bien de savoir comment fonctionnent les filtres Godbolt: .LC0, .text, // et Intel. Intel est facile -masm=intelmais qu'en est-il du reste?
Z boson

Je suppose que cela est expliqué ici stackoverflow.com/a/38552509/2542702
Z boson

godbolt prend en charge C (avec une tonne d'autres langages comme Rust, D, Pascal ...). C'est juste qu'il y a beaucoup moins de compilateurs C, il est donc préférable d'utiliser des compilateurs C ++ avec-x c
phuclv

23

Avez-vous essayé de gcc -S -fverbose-asm -O source.cregarder le source.sfichier assembleur généré ?

Le code assembleur généré va dans source.s(vous pouvez remplacer cela avec -o assembler-filename ); l' -fverbose-asmoption demande au compilateur d'émettre des commentaires d'assembleur "expliquant" le code assembleur généré. L' -Ooption demande au compilateur d'optimiser un peu (il pourrait optimiser davantage avec -O2ou -O3).

Si vous voulez comprendre ce qui gccse passe, essayez de passer -fdump-tree-allmais soyez prudent: vous obtiendrez des centaines de fichiers de vidage.

BTW, GCC est extensible via des plugins ou avec MELT (un langage spécifique de domaine de haut niveau pour étendre GCC; que j'ai abandonné en 2017)


peut-être mentionner que la sortie sera en source.s, car beaucoup de gens s'attendraient à une impression sur la console.
RubenLaguna

1
@ecerulm: sauvegarde -S -o-sur stdout. -masm=intelest utile si vous souhaitez utiliser la syntaxe NASM / YASM. (mais il utilise qword ptr [mem], plutôt que juste qword, donc c'est plus comme Intel / MASM que NASM / YASM). gcc.godbolt.org fait un bon travail de nettoyage du vidage: en supprimant éventuellement les lignes de commentaires uniquement, les étiquettes inutilisées et les directives d'assembleur.
Peter Cordes

2
Oublié de mentionner: Si vous recherchez "similaire à la source mais sans le bruit du stockage / rechargement après chaque ligne source", alors -Ogc'est encore mieux que -O1. Cela signifie "optimiser pour le débogage" et rend asm sans trop d'optimisations difficiles / difficiles à suivre qui fait tout ce que dit la source. Il est disponible depuis gcc4.8, mais clang 3.7 ne l'a toujours pas. IDK s'ils ont décidé contre ou quoi.
Peter Cordes

19

Vous pouvez utiliser gdb pour cela comme objdump.

Cet extrait est tiré de http://sources.redhat.com/gdb/current/onlinedocs/gdb_9.html#SEC64


Voici un exemple montrant un assemblage source + mixte pour Intel x86:

  (gdb) disas / m principal
Dump du code assembleur pour la fonction main:
5 {
0x08048330: pousser% ebp
0x08048331: mov% esp,% ebp
0x08048333: sous $ 0x8,% esp
0x08048336: et $ 0xfffffff0,% esp
0x08048339: sous $ 0x10,% esp

6 printf ("Bonjour. \ N");
0x0804833c: movl $ 0x8048440, (% esp)
0x08048343: appelez 0x8048284 

7 renvoie 0;
8}
0x08048348: mov $ 0x0,% eax
0x0804834d: laisser
0x0804834e: ret

Fin du vidage de l'assembleur.


Et pour basculer le désassembleur de GDB sur la syntaxe Intel, utilisez la set disassembly-flavor intelcommande.
Ruslan

13

Utilisez le commutateur -S (note: S majuscule) vers GCC, et il émettra le code assembleur dans un fichier avec une extension .s. Par exemple, la commande suivante:

gcc -O2 -S -c foo.c


4

Je n'ai pas donné de chance à gcc, mais dans le cas de g ++. La commande ci-dessous fonctionne pour moi. -g pour la version de débogage et -Wa, -adhln est passé à l'assembleur pour être répertorié avec le code source

g ++ -g -Wa, -adhln src.cpp


Cela fonctionne aussi pour gcc! -Wa, ... est pour les options de ligne de commande pour la partie assembleur (exécuter dans gcc / g ++ après la compilation C / ++). Il appelle en interne (as.exe dans Windows). Voir> as --help en ligne de commande pour voir plus d'aide
Hartmut Schorrig

0

utilisez -Wa, -adhln comme option sur gcc ou g ++ pour produire une sortie de listage vers stdout.

-Wa, ... est pour les options de ligne de commande pour la partie assembleur (exécuter dans gcc / g ++ après la compilation C / ++). Il appelle en interne (as.exe dans Windows). Voir

> comme --help

comme ligne de commande pour voir plus d'aide pour l'outil d'assemblage dans gcc

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.