Como visualizar o assembly por trás do código usando Visual C ++?

116

Eu estava lendo outra questão relativa à eficiência de duas linhas de código, e o OP disse que olhou para o assembly por trás do código e as duas linhas eram idênticas no assembly. Digressão à parte, como eu poderia ver o código assembly criado quando um programa é compilado.

Estou usando o Visual C ++ da Microsoft, mas também gostaria de saber se é possível visualizar o assembly por trás do código escrito em Visual Basic.

Então, como posso ver o código assembly por trás de um programa escrito em linguagens de nível superior como C ++ e Visual Basic?

Bart
fonte
É chamado de lista de montagem no msvc, como outros já mencionaram. Eu criei um plugin simples adicionando entradas ao menu de contexto do editor para automatizar essas etapas tediosas: marketplace.visualstudio.com/items?itemName=Trass3r.DevUtils
Trass3r

Respostas:

149

Existem várias abordagens:

  1. Normalmente, você pode ver o código assembly enquanto depura C ++ no visual studio (e eclipse também). Para isso no Visual Studio coloque um ponto de interrupção no código em questão e quando o depurador acertar, clique com o botão direito e encontre "Go To Assembly" (ou pressione CTRL + ALT + D)

  2. A segunda abordagem é gerar listagens de montagem durante a compilação. Para isso, vá em configurações do projeto -> C / C ++ -> Arquivos de saída -> Localização da lista ASM e preencha o nome do arquivo. Selecione também "Saída da montagem" para "Montagem com código fonte".

  3. Compile o programa e use qualquer depurador de terceiros. Você pode usar OllyDbg ou WinDbg para isso. Além disso, você pode usar IDA (desmontador interativo). Mas essa é uma maneira radical de fazer isso.

inazaruk
fonte
5
Observe que a abordagem nº 2 não funciona ao compilar uma biblioteca estática com a otimização de todo o programa habilitada (pelo menos no VS2010). O que faz sentido - o compilador ainda não gerou o código final.
dhaffey
3
É chamado de "Goto Disassembly" no Visual Studio 2017
Matthias
Com a abordagem nº 2, como posso ver a montagem?
user1507435
Deve ver um arquivo .asm no diretório de depuração se você usou o local padrão.
user3015682
28

Observação adicional: há uma grande diferença entre a saída do assembler de depuração e a versão um. O primeiro é bom para aprender como o compilador produz código assembler a partir de C ++. O segundo é bom para aprender como o compilador otimiza várias construções C ++. Nesse caso, algumas transformações de C ++ para asm não são óbvias.

Vladimir Obrizan
fonte
Percebi que ao desmontar o executável de Debug, ele parece descompactar o código enquanto está em execução, isso não acontece na versão de lançamento. Além disso, ao abrir ambos com PEiD, apenas a versão de depuração mostra "Microsoft Visual C ++ 8.0 [Debug]".
jyz
9
Isso é absolutamente verdade. Mas não responde à pergunta de forma alguma.
imallett de
25

Especifique a opção / FA para o compilador cl. Dependendo do valor da opção, apenas o código de montagem ou o código de alto nível e o código de montagem são integrados. O nome do arquivo obtém a extensão de arquivo .asm. Aqui estão os valores suportados:


  • / Código de montagem FA; .asm
  • / FAc Máquina e código de montagem; .bacalhau
  • / FAs Fonte e código de montagem; .asm
  • / FAcs Máquina, código-fonte e código de montagem; .bacalhau
Steve
fonte
10

A maneira mais fácil é disparar o depurador e verificar a janela de desmontagem .

diapir
fonte
8

A versão anterior desta resposta (um "hack" para rextester.com) é basicamente redundante agora que http://gcc.godbolt.org/ fornece CL 19 RC para ARM, x86 e x86-64 (visando a convenção de chamada do Windows , ao contrário de gcc, clang e icc nesse site).

O explorador do compilador Godbolt é projetado para formatar bem a saída do compilador asm, removendo o "ruído" das diretivas, então eu recomendo usá-lo para olhar asm para funções simples que pegam args e retornam um valor (então eles não serão otimizado).

Por um tempo, CL estava disponível em http://gcc.beta.godbolt.org/, mas não no site principal, mas agora está em ambos.


Para obter a saída do ASM MSVC do http://rextester.com/l/cpp_online_compiler_visual compilador online: Adicione /FAsàs opções de linha de comando. Faça com que seu programa encontre seu próprio caminho, trabalhe o caminho para o .asme descarte-o. Ou execute um desmontador no .exe.

por exemplo, http://rextester.com/OKI40941

#include <string>
#include <boost/filesystem.hpp>
#include <Windows.h>

using namespace std;

static string my_exe(void){
    char buf[MAX_PATH];
    DWORD tmp = GetModuleFileNameA( NULL, // self
                                  buf, MAX_PATH);
    return buf;
}

int main() {
    string dircmd = "dir ";
    boost::filesystem::path p( my_exe() );
    //boost::filesystem::path dir = p.parent_path();

    // transform c:\foo\bar\1234\a.exe 
    // into      c:\foo\bar\1234\1234.asm
    p.remove_filename();
    system ( (dircmd + p.string()).c_str() );

    auto subdir = p.end();      // pointing at one-past the end
    subdir--;                   // pointing at the last directory name
    p /= *subdir;               // append the last dir name as a filename
    p.replace_extension(".asm");
    system ( (string("type ") + p.string()).c_str() );
//    std::cout << "Hello, world!\n";
}

... code of functions you want to see the asm for goes here ...

typeé a versão DOS de cat. Eu não queria incluir mais código que tornaria mais difícil encontrar as funções para as quais eu queria ver o conjunto. (Embora usando std :: string e boost run counter para esses objetivos! Alguma manipulação de string de estilo C que faz mais suposições sobre a string que está processando (e ignora a segurança / alocação de comprimento máximo usando um grande buffer) no resultado de GetModuleFileNameAseria ser muito menos código de máquina total.)

IDK por que, mas cout << p.string() << endlmostra apenas o nome de base (ou seja, o nome do arquivo, sem os diretórios), embora a impressão de seu comprimento mostre que não é apenas o nome básico. (Chromium48 no Ubuntu 15.10). Provavelmente há algum processamento de escape de barra invertida em algum ponto coutou entre o stdout do programa e o navegador da web.

Peter Cordes
fonte
@MichaelPetch: ah, descobri que foi isso que eu tentei. .c_str()imprime o que parece ser um ponteiro. Se você seguir o link, verá o código para hexdump a std::string(desabilitado com #if 0). Acontece que a string está boa, mas coutnão está sendo enviada para o navegador da web. Também não há caracteres não ASCII, apenas barras invertidas.
Peter Cordes
Posso estar faltando alguma coisa, mas quando você fez subdir--; p /= *subdir;isso, você não reduziu p apenas ao nome do arquivo? Ou talvez eu esteja entendendo mal o que você está tentando imprimir.
Michael Petch
Acho que não entendi muito bem o subdir--seguido por p /= *subdirquando subdirfoi originalmentep.end()
Michael Petch
@MichaelPetch: comentários atualizados. Eu precisava obter o último componente do diretório do caminho para usar como nome de arquivo. Funciona, mas demorei muito para entender porque pensei que GetModuleFileNameAestava apenas voltando a.exe. Só depois de fazer o feitiço e imprimir o comprimento é que eu sabia que estava funcionando e poderia fazer com que o programa manipulasse o caminho, simplesmente não conseguia imprimir o caminho
Peter Cordes
1
Sim, parece ser a parte \\r(bem \rquando o compilador produz) do nome do arquivo que traduziu mal ao renderizar para o navegador da web. Usando p.generic_string()funciona, mas as barras invertidas são barras normais.
Michael Petch
5

No Visual C ++, as opções de projeto em Arquivos de saída, acredito que tem uma opção para gerar a listagem ASM com código-fonte. Portanto, você verá o código-fonte C / C ++ e o ASM resultante no mesmo arquivo.

jcopenha
fonte
5

Para MSVC, você pode usar o vinculador.

link.exe / dump / linho / disasm /out:foo.dis foo.dll

foo.pdb precisa estar disponível para obter símbolos

Steve Steiner
fonte
1

O .NET Reflector da Red Gate é uma ferramenta incrível que me ajudou várias vezes. O lado positivo desse utilitário além de mostrar facilmente a MSIL é que você pode analisar muitas DLLs de terceiros e fazer com que o Reflector se encarregue da conversão de MSIL em C # e VB.

Não estou prometendo que o código será tão claro quanto o código-fonte, mas você não terá muitos problemas para segui-lo.

Dave L
fonte
2
Nota: aplicável apenas a montagens gerenciadas para não desmontar como em assembler, asm.
sean e
Bom ponto, eu li isso como um "as duas linhas de código são iguais no assembly" em vez de "as duas linhas de código são iguais no assembly"
Dave L
Ele só funcionará em aplicativos dotnet, não em vinculador ou compilador Visual C ++.
Muhammad Ali
1

Se você está falando sobre depuração para ver o código assembly, a maneira mais fácil é Depurar-> Windows-> Desmontagem (ou Alt-8). Isso permitirá que você entre em uma função chamada e permaneça em Disassembly.

Aabbee
fonte