如何做到这一点呢?

如果我想分析某些东西是如何编译的,我该如何获得发出的程序集代码?


当前回答

如前所述,查看-S标志。

'-fdump-tree'标志家族也值得一看,特别是-fdump-tree-all,它可以让您看到GCC的一些中间形式。这些程序通常比汇编程序更具可读性(至少对我来说),并让您了解优化传递的执行情况。

其他回答

如果您正在寻找LLVM程序集:

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

对gcc(或g++)使用-S选项,可选使用-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)并且文件没有被剥离,则此选项工作得最好。

运行文件helloworld将为您提供一些关于使用objdump将获得的详细级别的指示。

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

例如objdump -drwC -Mintel -S foo。少一点。 -r对于.o来说非常重要,因为符号引用只有000 000 000 000个占位符,而不是链接的可执行文件。

-save-temps

元数据的回答中提到了这一点,但让我进一步举例说明。

与-S相比,这个选项的最大优点是可以很容易地将其添加到任何构建脚本中,而不会过多地干扰构建本身。

当你这样做时:

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

文件c

#define INC 1

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

现在,除了正常的主输出。O,当前工作目录还包含以下文件:

main.i is a bonus and contains the preprocessed file: # 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 contains the desired generated assembly: .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对象输出相同的目录,而不是当前工作目录,从而避免了潜在的basename冲突。

关于这个选项的另一个很酷的事情是如果你添加-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 (Disco Dingo) amd64, GCC 8.3.0中进行了测试。

制定预定义目标

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上进行了测试。

以下命令行来自Christian Garbin的博客:

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

我从Windows XP上的DOS窗口运行g++,针对的是一个包含隐式强制转换的例程

cd 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'

输出是组装生成的代码,其中穿插着原始的c++代码(c++代码在生成的汇编语言流中显示为注释)。

  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)

下面是一个使用GCC的C语言解决方案:

gcc -S program.c && gcc program.c -o output

这里的第一部分将程序的程序集输出存储在与程序相同的文件名中,但是通过更改扩展名.s,您可以将其作为任何普通文本文件打开。 这里的第二部分编译您的程序以供实际使用,并为您的程序生成具有指定文件名的可执行文件。

上面使用的program.c是程序的名称,输出是要生成的可执行文件的名称。