如何做到这一点?
如果我想分析某些东西是如何编译的,我将如何获得发出的汇编代码?
使用-S
gcc(或 g++)的选项。
gcc -S helloworld.c
这将在 helloworld.c 上运行预处理器 (cpp),执行初始编译,然后在运行汇编器之前停止。
默认情况下,这将输出一个文件helloworld.s
。仍然可以使用该-o
选项设置输出文件。
gcc -S -o my_asm_output.s helloworld.c
当然,这仅在您拥有原始来源时才有效。如果您只有生成的目标文件,另一种方法是使用objdump
,通过设置--disassemble
选项(或-d
缩写形式)。
objdump -S --disassemble helloworld > helloworld.dump
如果为目标文件启用了调试选项(-g
在编译时)并且该文件没有被剥离,则此选项效果最佳。
运行file helloworld
将为您提供一些关于使用 objdump 将获得的详细程度的指示。
这将生成 C 代码 + 行号交织的汇编代码,以便更轻松地查看哪些行生成了哪些代码:
# 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
可在程序员的算法中找到,第 3 页(这是 PDF 的第 15 页)。
以下命令行来自Christian Garbin 的博客
g++ -g -O -Wa,-aslh horton_ex2_05.cpp >list.txt
我从 Win-XP 上的 DOS 窗口运行 G++,针对包含隐式转换的例程
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++ 代码在生成的 asm 流中显示为注释)
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)
-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 上测试。
如果您想看到的内容取决于输出的链接,那么除了前面提到的 gcc -S 之外,输出目标文件/可执行文件上的 objdump 也可能有用。这是 Loren Merritt 编写的一个非常有用的脚本,它将默认的 objdump 语法转换为更易读的 nasm 语法:
#!/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;
我怀疑这也可以用于 gcc -S 的输出。
好吧,正如大家所说,使用 -S 选项。如果使用 -save-temps 选项,还可以获得预处理文件(.i)、程序集文件( .s)和目标文件(*.o)。(使用 -E、-S 和 -c 获取它们中的每一个。)
正如每个人都指出的那样,使用-S
GCC 的选项。我还想补充一点,结果可能会有所不同(非常大!),具体取决于您是否添加优化选项(-O0
对于无,-O2
对于积极优化)。
特别是在 RISC 架构上,编译器在进行优化时通常会将代码转换得几乎无法识别。看到结果令人印象深刻和迷人!
如前所述,查看 -S 标志。
还值得一看 '-fdump-tree' 系列标志,特别是 '-fdump-tree-all',它可以让您看到 gcc 的一些中间形式。这些通常比汇编程序更具可读性(至少对我而言),并让您了解优化过程的执行情况。
如果您正在寻找 LLVM 程序集:
llvm-gcc -emit-llvm -S hello.c
我在答案中看不到这种可能性,可能是因为这个问题来自 2008 年,但在 2018 年你可以使用 Matt Goldbolt 的在线网站https://godbolt.org
你也可以在本地 git clone 并运行他的项目https://github.com/mattgodbolt/compiler-explorer
使用 -S 选项:
gcc -S program.c
来自: http: //www.delorie.com/djgpp/v2faq/faq8_20.html
gcc -c -g -Wa,-a,-ad [其他 GCC 选项] foo.c > foo.lst
代替 PhirePhly 的回答或者就像大家说的那样使用 -S 。
这是使用 GCC 的 C 解决方案:
gcc -S program.c && gcc program.c -o output
这里的第一部分将程序的汇编输出存储在与 Program 相同的文件名中,但扩展名为.s,您可以将其作为任何普通文本文件打开。
这里的第二部分编译您的程序以供实际使用,并为您的程序生成一个具有指定文件名的可执行文件。
上面使用的program.c是您的程序的名称,输出是您要生成的可执行文件的名称。
以下是在 Windows 上查看/打印任何 C 程序的汇编代码的步骤
控制台/终端/命令提示符:
在 C 代码编辑器(如代码块)中编写 C 程序并使用扩展名 .c 保存它
编译并运行它。
成功运行后,转到您安装 gcc 编译器的文件夹并给出
以下命令获取“.c”文件的“.s”文件
C:\gcc> gcc -S C文件的完整路径 ENTER
一个示例命令(如我的情况)
C:\gcc> gcc -SD:\Aa_C_Certified\alternate_letters.c
这将输出原始“.c”文件的“.s”文件
4. 在此之后,键入以下命令
C;\gcc> cpp 文件名.s ENTER
示例命令(如我的情况)
C;\gcc> cpp 备用字母.s
这将打印/输出 C 程序的整个汇编语言代码。
使用“-S”作为选项。它在终端中显示程序集输出。
最近想知道a中各个函数的汇编。我是这样做的:
$ 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