Como você obtém a saída do assembler da fonte C / C ++ no gcc?


Respostas:


422

Use a -Sopção para gcc (ou g ++).

gcc -S helloworld.c

Isso executará o pré-processador (cpp) no helloworld.c, executará a compilação inicial e parará antes que o assembler seja executado.

Por padrão, isso produzirá um arquivo helloworld.s. O arquivo de saída ainda pode ser definido usando a -oopção

gcc -S -o my_asm_output.s helloworld.c

Claro que isso só funciona se você tiver a fonte original. Uma alternativa se você tiver apenas o arquivo de objeto resultante é usar objdump, definindo a --disassembleopção (ou -dpara o formulário abreviado).

objdump -S --disassemble helloworld > helloworld.dump

Essa opção funciona melhor se a opção de depuração estiver ativada para o arquivo de objeto ( -gno momento da compilação) e o arquivo não tiver sido removido.

A execução file helloworldfornecerá algumas indicações sobre o nível de detalhes que você obterá usando o objdump.


3
Embora isso esteja correto, achei os resultados da resposta de Cr McDonough mais úteis.
Rhys Ulerich

3
um uso adicional: objdump -M intel -S - desmonte o helloworld> helloworld.dump para obter o despejo de objetos na sintaxe da intel compatível com o nasm no linux.
touchStone 11/03/2015

2
Se você tem uma única função para otimizar / verificação, então você pode dar uma tentativa para online interativa compiladores C ++ ou seja Godbolt
fiorentinoing

11
@touchStone: O GAS não.intel_syntax é compatível com o NASM . É mais parecido com o MASM (por exemplo, mov eax, symbolé uma carga, diferente do NASM, onde é um mov r32, imm32endereço), mas também não é totalmente compatível com o MASM. Eu o recomendo como um bom formato para ler, especialmente se você gosta de escrever na sintaxe do NASM. objdump -drwC -Mintel | lessou gcc foo.c -O1 -fverbose-asm -masm=intel -S -o- | lesssão úteis. (Veja também Como remover “ruído” da saída do conjunto GCC / clang? ). -masm=inteltrabalha com clang também.
Peter Cordes

3
Melhor usogcc -O -fverbose-asm -S
Basile Starynkevitch

173

Isso gerará código de montagem com o código C + números de linha entrelaçados, para ver mais facilmente quais linhas geram qual código:

# 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

Encontrado em Algoritmos para programadores , página 3 (que é a 15ª página geral do PDF).


3
(Isso é realmente na página (numerada) 3 (que é a página 15 do PDF))
Grumdrig

11
Infelizmente, asno OS X não conhece essas bandeiras. Se isso acontecesse, você provavelmente poderia usar uma linha -Wapara passar as opções para as.
Grumdrig

23
g++ -g -O0 -c -fverbose-asm -Wa,-adhln test.cpp > test.lstseria a versão abreviada disso.
Legends2k #

4
Você também pode usar gcc -c -g -Wa,-ahl=test.s test.corgcc -c -g -Wa,-a,-ad test.c > test.txt
phuclv

11
Uma postagem no blog explicando isso com mais detalhes, incluindo a versão de comando único como lendas e Lu'u postadas. Mas porque -O0? Está cheio de cargas / lojas que dificultam o rastreamento de um valor e não informa nada sobre a eficiência do código otimizado.
22816 Peter Cordes

51

A seguinte linha de comando é do blog de Christian Garbin

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

Executei o G ++ a partir de uma janela do DOS no Win-XP, em uma rotina que contém uma conversão implícita

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'

A saída é um código gerado montado, intercalado com o código C ++ original (o código C ++ é mostrado como comentários no fluxo asm gerado)

  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 - Não necessariamente. O OP tinha como objetivo obter o equivalente de saída do assembler do código-fonte C / C ++, isso obtém a Listagem, que eu concordo é mais útil para entender o que o compilador e o otimizador estão fazendo. Mas isso faria com que o próprio montador vomitasse, pois não esperava os números das linhas, e compilou os bytes fora das instruções de montagem.
Jesse Chisholm

Use pelo menos -O2as opções de otimização que você realmente usa ao criar seu projeto, se quiser ver como o gcc otimiza seu código. (Ou se você usar LTO, como você deve, então você tem que desmontar a saída vinculador para ver o que realmente se.)
Peter Cordes

27

Use a opção -S

g++ -S main.cpp

ou também com o gcc

gcc -S main.c

Veja também isso


7
Verifique o FAQ: "Também é perfeitamente bom perguntar e responder sua própria pergunta de programação". O ponto é que agora o StackOverflow contém as perguntas e respostas como um recurso para outros.
Steve Jessop

E talvez alguém vai vir e surpreendê-lo com uma resposta melhor, embora o meu pode ser um pouco detalhado, às vezes ...
PhirePhly

Existe até o botão de resposta da sua própria pergunta.
Ciro Santilli escreveu

13

Se o que você deseja ver depende da vinculação da saída, então o objdump no arquivo / executável do objeto de saída também pode ser útil além do gcc -S acima mencionado. Aqui está um script muito útil de Loren Merritt que converte a sintaxe objdump padrão na sintaxe nasm mais legível:

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

Eu suspeito que isso também pode ser usado na saída do gcc -S.


2
Ainda assim, esse script é um hack sujo que não converte completamente a sintaxe. Por exemplo, mov eax,ds:0x804b794não é muito NASMish. Além disso, às vezes, apenas retira informações úteis: movzx eax,[edx+0x1]deixa o leitor adivinhar se o operando da memória era byteou word.
Ruslan

Para desmontar a sintaxe do NASM, use o Agner Fog'sobjconv . Você pode desmontá-lo no stdout com o arquivo de saída = /dev/stdout, para que possa ser lessvisualizado. Também existe ndisasm, mas desmonta apenas binários planos e não sabe sobre arquivos de objetos (ELF / PE).
27516 Peter

9

Como todos apontaram, use a -Sopção para o GCC. Gostaria também de acrescentar que os resultados podem variar (muito!), Dependendo de você adicionar ou não opções de otimização ( -O0por nenhuma, -O2para otimização agressiva).

Em arquiteturas RISC, em particular, o compilador geralmente transformará o código quase além do reconhecimento na otimização. É impressionante e fascinante ver os resultados!


9

Bem, como todos disseram, use a opção -S. Se você usar a opção -save-temps, também poderá obter arquivo pré-processado ( .i), arquivo de montagem ( .s) e arquivo de objeto (*. O). (obtenha cada um deles usando -E, -S e -c.)


8

Como mencionado anteriormente, observe o sinalizador -S.

Também vale a pena examinar a família de sinalizadores '-fdump-tree', em particular '-fdump-tree-all', que permite ver algumas das formas intermediárias do gcc. Isso geralmente pode ser mais legível que o assembler (pelo menos para mim) e permite que você veja o desempenho das passagens de otimização.


8

Se você está procurando montagem LLVM:

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


8

-save-temps

Isso foi mencionado em https://stackoverflow.com/a/17083009/895245, mas deixe-me exemplificar ainda mais.

A grande vantagem dessa opção -Sé que é muito fácil adicioná-la a qualquer script de construção, sem interferir muito na própria construção.

Quando você faz:

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

main.c

#define INC 1

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

e agora, além da saída normal main.o, o diretório de trabalho atual também contém os seguintes arquivos:

  • main.i é um bônus e contém o arquivo pré-processado:

    # 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 contém a montagem gerada desejada:

        .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

Se você deseja fazer isso para um grande número de arquivos, considere usar:

 -save-temps=obj

que salva os arquivos intermediários no mesmo diretório que a -osaída do objeto em vez do diretório de trabalho atual, evitando possíveis conflitos de nome de base.

Outra coisa interessante sobre essa opção é se você adicionar -v:

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

na verdade, mostra os arquivos explícitos sendo usados ​​em vez de temporários feios /tmp, por isso é fácil saber exatamente o que está acontecendo, o que inclui as etapas de pré-processamento / compilação / montagem:

/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

Testado no Ubuntu 19.04 amd64, GCC 8.3.0.




3

Saída desses commnads

Aqui estão as etapas para ver / imprimir o código de montagem de qualquer programa C no Windows

console / terminal / prompt de comando:

  1. Escreva um programa C em um editor de código C, como codeblocks, e salve-o com uma extensão .c

  2. Compile e execute-o.

  3. Depois de executar com êxito, vá para a pasta em que você instalou seu compilador gcc e dê o

    seguinte comando para obter um arquivo '.s' do arquivo '.c'

    C: \ gcc> gcc -S caminho completo do arquivo C ENTER

    Um comando de exemplo (como no meu caso)

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

    Isso gera um arquivo '.s' do arquivo '.c' original

4) Depois disso, digite o seguinte comando

C; \ gcc> cpp filename.s ENTER

Comando de exemplo (como no meu caso)

C; \ gcc> cpp alternate_letters.s

Isso imprimirá / produzirá todo o código da linguagem Assembly do seu programa C.


2

Use "-S" como uma opção. Ele exibe a saída da montagem no terminal.


Para exibir no terminal, use gcc foo.c -masm=intel -fverbose-asm -O3 -S -o- |less. -Spor conta própria cria foo.s.
Peter Cordes

2

Recentemente, eu queria conhecer a montagem de cada função em um programa,
foi assim que eu fiz.

$ 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

Aqui está uma solução para C usando o gcc:

gcc -S program.c && gcc program.c -o output
  1. Aqui, a primeira parte armazena a saída de montagem do programa no mesmo nome de arquivo que o Programa, mas com uma extensão .s alterada , você pode abri-lo como qualquer arquivo de texto normal.

  2. A segunda parte aqui compila seu programa para uso real e gera um executável para o seu programa com um nome de arquivo especificado.

O program.c usado acima é o nome do seu programa e a saída é o nome do executável que você deseja gerar.

BTW É o meu primeiro post no StackOverFlow: -}

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.