如何从 gcc 中的 C/C 源代码获取汇编程序输出?

新手上路,请多包涵

如何做到这一点?

如果我想分析某些东西是如何编译的,我将如何获得发出的汇编代码?

原文由 Doug T. 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.2k
2 个回答

使用 -S 选项到 gcc(或 g++),可选择使用 -fverbose-asm 在默认情况下效果很好 -O0 将 C 名称附加到 asm 操作数作为注释。在任何优化级别上都不太好,您通常希望使用它来获得值得一看的 asm。

 gcc -S helloworld.c

这将在 helloworld.c 上运行预处理器 (cpp),执行初始编译,然后在运行汇编器之前停止。有关在这种情况下使用的有用编译器选项,请参阅 如何从 GCC/clang 程序集输出中删除“噪音”? (或者只是 Matt Godbolt 的在线编译器资源管理器 上查看您的代码, 它过滤掉指令和东西,并使用调试信息突出显示以匹配源代码行与 asm。)

默认情况下,这将输出一个文件 helloworld.s 。仍然可以使用 -o 选项设置输出文件,包括 -o - 写入标准输出以将管道输入 less

 gcc -S -o my_asm_output.s helloworld.c

当然,这仅在您拥有原始来源时才有效。如果您只有生成的目标文件,另一种方法是使用 objdump ,通过设置 --disassemble 选项(或 -d 缩写形式)。

 objdump -S --disassemble helloworld > helloworld.dump

-S 将源代码行与正常的反汇编输出交错,所以如果为目标文件启用了调试选项(编译时 -g )并且文件没有被剥离,则此选项最有效。

运行 file helloworld 将为您提供一些关于使用 objdump 将获得的详细程度的指示。

其他有用的 objdump 选项包括 -rwC (显示符号重定位,禁用长机器代码的换行,以及 demangle C++ 名称)。如果你不喜欢 x86 的 AT&T 语法, -Mintel 。请参阅 手册页

例如, objdump -drwC -Mintel -S foo.o | less

-r .o 对于只有 00 00 00 00 符号引用的占位符的 --- 非常重要,而不是链接的可执行文件。

原文由 Andrew Edgecombe 发布,翻译遵循 CC BY-SA 4.0 许可协议

-save-temps

https://stackoverflow.com/a/17083009/895245 中提到了这一点,但让我进一步举例说明。

-S 相比,此选项的最大优势在于它可以很容易地添加到任何构建脚本中,而不会过多地干扰构建本身。

当你这样做时:

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

主程序

#define INC 1

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

现在,除了正常输出 main.o 之外,当前工作目录还包含以下文件:

  • main.i 是一个奖励,包含预处理文件:
   # 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 包含所需的生成程序集:
       .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

如果要对大量文件执行此操作,请考虑改用:

  -save-temps=obj

它将中间文件保存到与 -o 对象输出而不是当前工作目录相同的目录中,从而避免潜在的基本名称冲突。

这个选项的另一个很酷的事情是如果你添加 -v

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

它实际上显示了正在使用的显式文件,而不是 /tmp 下的丑陋临时文件,因此很容易确切地知道发生了什么,其中包括预处理/编译/组装步骤:

 /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

在 Ubuntu 19.04 amd64、GCC 8.3.0 中测试。

CMake 预定义目标

CMake 自动为预处理文件提供一个目标:

 make help

向我们展示了我们可以做到:

 make main.s

并且该目标运行:

 Compiling C source to assembly CMakeFiles/main.dir/main.c.s
/usr/bin/cc    -S /home/ciro/hello/main.c -o CMakeFiles/main.dir/main.c.s

所以文件可以在 CMakeFiles/main.dir/main.c.s

在 cmake 3.16.1 上测试。

原文由 Ciro Santilli OurBigBook.com 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题