【问题标题】:How to view the assembly behind the code using Visual C++?如何使用 Visual C++ 查看代码背后的程序集?
【发布时间】:2010-11-04 11:09:08
【问题描述】:

我正在阅读有关两行代码效率的另一个问题,OP 说他查看了代码后面的程序集,两行在程序集中是相同的。题外话,如何查看程序编译时创建的汇编代码。

我使用的是 Microsoft 的 Visual C++,但我也想知道是否可以查看用 Visual Basic 编写的代码背后的程序集。

那么,如何查看用 C++ 和 Visual Basic 等高级语言编写的程序背后的汇编代码?

【问题讨论】:

标签: visual-c++ assembly


【解决方案1】:

有几种方法:

  1. 在 Visual Studio(以及 Eclipse)中调试 C++ 时,您通常可以看到汇编代码。为此,在 Visual Studio 中在有问题的代码上放置一个断点,当调试器点击它时,右键单击并找到“Go To Assembly”(或按 CTRL+ALT+D)

  2. 第二种方法是在编译时生成汇编列表。为此,请转到项目设置 -> C/C++ -> 输出文件 -> ASM 列表位置并填写文件名。还要选择“Assembly Output”到“Assembly With Source Code”。

  3. 编译程序并使用任何第三方调试器。为此,您可以使用 OllyDbg 或 WinDbg。您也可以使用 IDA(交互式反汇编程序)。但这是硬核的做法。

【讨论】:

  • 请注意,方法#2 在编译启用了全程序优化的静态库时不起作用(至少在 VS2010 中)。这是有道理的——编译器还没有生成最终代码。
  • 在Visual Studio 2017中称为“Goto Disassembly”
  • 使用方法 #2,我如何查看程序集?
  • 如果您使用默认位置,应该在调试目录中看到一个 .asm 文件。
  • "在 Visual Studio 中调试 C++ 时通常可以看到汇编代码" 哇,我多年来一直使用 Godbolt 和编译器输出,而我只能在 VS 中得到它但不知道。我非常想念这个。谢谢你开我的眼睛,愿你百岁百岁!
【解决方案2】:

为 cl 编译器指定 /FA 开关。根据开关的值,要么只集成汇编代码,要么集成高级代码和汇编代码。文件名获得 .asm 文件扩展名。以下是支持的值:


  • /FA 汇编代码; .asm
  • /FAc 机器和汇编代码; .cod
  • /FAs 源代码和汇编代码; .asm
  • /FAcs 机器、源代码和汇编代码; .cod

【讨论】:

    【解决方案3】:

    附加说明:Debug 汇编器输出与 Release 1 之间存在很大差异。第一个很好地了解编译器如何从 C++ 生成汇编代码。第二个很好地了解编译器如何优化各种 C++ 结构。在这种情况下,一些 C++ 到 asm 的转换并不明显。

    【讨论】:

    • 我注意到,在反汇编 Debug 可执行文件时,它似乎在运行时解压缩了代码,而在 Release 版本上不会发生这种情况。同样,当同时使用 PEiD 打开时,调试版本会显示“Microsoft Visual C++ 8.0 [Debug]”。
    • 这绝对是真的。但它根本没有回答这个问题。
    【解决方案4】:

    最简单的方法是启动调试器并检查反汇编窗口

    【讨论】:

      【解决方案5】:

      由于http://gcc.godbolt.org/ 为 ARM、x86 和 x86-64 提供 CL 19 RC(针对Windows 调用约定,与该站点上的 gcc、clang 和 icc 不同)。

      Godbolt 编译器资源管理器旨在很好地格式化编译器 asm 输出,消除指令的“噪音”,所以我强烈建议使用它来查看 asm 以获取带有 args 并返回值的简单函数(所以他们赢了'不要被优化掉)。

      有一段时间,CL 在http://gcc.beta.godbolt.org/ 上可用,但在主站点上不可用,但现在两者都可用。


      要从 http://rextester.com/l/cpp_online_compiler_visual 在线编译器获取 MSVC asm 输出:将 /FAs 添加到命令行选项。让您的程序找到自己的路径并计算出.asm 的路径并将其转储。或者在.exe 上运行反汇编程序。

      例如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 ...
      

      typecat 的 DOS 版本。我不想包含更多代码,这会使找到我想要查看 asm 的函数变得更加困难。 (尽管使用 std::string 和 boost 与这些目标背道而驰!一些 C 风格的字符串操作,对它正在处理的字符串做出更多假设(并通过使用大缓冲区忽略最大长度安全/分配)@ 的结果987654332@ 总机器码会少得多。)

      IDK 为什么,但是cout &lt;&lt; p.string() &lt;&lt; endl 只显示基本名称(即文件名,没有目录),即使打印它的长度显示它不仅仅是裸名称。 (Ubuntu 15.10 上的 Chromium48)。在cout 的某个时间点,或者在程序的标准输出和网络浏览器之间,可能有一些反斜杠转义处理。

      【讨论】:

      • @MichaelPetch:哦,原来 我尝试过的。 .c_str() 打印看起来像指针的东西。如果您点击链接,您将看到十六进制转储std::string 的代码(使用#if 0 禁用)。事实证明这个字符串很好,但是cout 没有把它送到网络浏览器。也没有任何非 ASCII 字符,只有反斜杠。
      • 我可能遗漏了一些东西,但是当你这样做 subdir--; p /= *subdir; 时,你没有将 p 简化为文件名吗?或者我可能误解了您要打印的内容。
      • subdir最初是p.end()时,我想我不太明白subdir--后面跟着p /= *subdir
      • @MichaelPetch:更新了 cmets。我需要获取路径的最后一个目录组件以用作文件名。它确实有效,但我花了很长时间才解决,因为我认为 GetModuleFileNameA 只是返回 a.exe。直到我对它进行十六进制转储并打印出我知道它正在工作的长度,并且我可以让程序操纵路径,我只是无法打印路径
      • 是的,似乎是文件名的\\r(当编译器输出它时是\r)部分,它在为网络浏览器渲染时翻译得很差。使用 p.generic_string() 可以,但反斜杠是正斜杠。
      【解决方案6】:

      在 Visual C++ 中,Output Files 下的项目选项我相信有一个用于输出 ASM 列表和源代码的选项。因此,您将在同一个文件中看到 C/C++ 源代码和生成的 ASM。

      【讨论】:

        【解决方案7】:

        对于 MSVC,您可以使用链接器。

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

        foo.pdb 需要可用于获取符号

        【讨论】:

          【解决方案8】:

          Red Gate's .NET Reflector 是一个非常棒的工具,它帮助了我很多次。除了轻松向您展示 MSIL 之外,此实用程序的优点是您可以分析大量第三方 DLL,并让 Reflector 负责将 MSIL 转换为 C# 和 VB。

          我不保证代码会像源代码一样清晰,但你应该不会有太多麻烦。

          【讨论】:

          • 注:只适用于托管程序集,不能像汇编程序一样反汇编,asm。
          • 好点,我把它读作“汇编中的两行代码是否相同”而不是“汇编中的两行代码是否相同”
          • 它只适用于 dotnet 应用程序,不适用于 Visual C++ 链接器或编译器。
          【解决方案9】:

          如果你在谈论调试以查看汇编代码,最简单的方法是 Debug->Windows->Disassembly(或 Alt-8)。这将让你进入一个被调用的函数并停留在反汇编中。

          【讨论】:

            【解决方案10】:

            使用 Visual Studio 6.0

            点击菜单项“项目”

            点击“设置”

            选择标签页标题 C/C++

            将“类别”更改为“列出文件”

            在“列出文件类型”下,将组合框从“无列表”更改为“使用机器代码组装”

            Assembly 源代码文件将作为 .cod 文件出现在“Release”文件夹中

            【讨论】:

              猜你喜欢
              • 2010-10-24
              • 2021-03-18
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-05-26
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多