如何做到这一点呢?
如果我想分析某些东西是如何编译的,我该如何获得发出的程序集代码?
使用- s开关:
g++ -S main.cpp
也可以用gcc:
gcc
gcc -S main.c
也可参见这。
使用- s选项:
gcc -S program.c
如果你想看到的内容依赖于输出的链接,那么除了前面提到的 gcc -S之外,输出对象文件/可执行文件上的objdump也可能有用。下面是Loren Merritt编写的一个非常有用的脚本,它将默认的objdump语法转换为更可读的NASM语法:
gcc -S
#!/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的输出上。
对gcc(或g++)使用- s选项,可选地使用-fverbose-asm,它在默认o0下工作良好,将C名作为注释附加到asm操作数上。它在任何优化级别上的效果都不太好,您通常希望使用它来获得值得一看的asm。
g++
gcc -S helloworld.c
这将在helloworld.c上运行预处理器(cpp),执行初始编译,然后在运行汇编器之前停止。在这种情况下使用有用的编译器选项,请参阅如何去除“;噪音”;从GCC/clang组装输出?< / >(或只是查看你的代码在马特Godbolt的在线编译器浏览器,它过滤掉指令和东西,并使用调试信息突出显示源行与asm匹配。)
默认情况下,这将输出文件helloworld.s。输出文件仍然可以通过使用- o选项来设置,包括-o -来写入标准输出,以便管道进入少。
helloworld.s
-o -
gcc -S -o my_asm_output.s helloworld.c
当然,这只有在你有原始源代码的情况下才有效。 如果你只有结果对象文件,另一种选择是使用objdump,通过设置--disassemble选项(或缩写形式为-d)
--disassemble
-d
objdump -S --disassemble helloworld > helloworld.dump
-S将源行与正常的反汇编输出交织在一起,因此如果对象文件(编译时为- g)启用了调试选项并且文件尚未被剥离,则此选项工作得最好。
-S
运行file helloworld会给你一些关于你将通过使用objdump获得的细节级别的指示。
file helloworld
其他有用的objdump选项包括-rwC(用于显示符号重定位、禁用长机器代码换行和要求c++名称)。如果你不喜欢x86的AT&T语法,-Mintel。看到手册页。
objdump
-rwC
-Mintel
objdump -drwC -Mintel -S foo.o | less
-r
.o
00 00 00 00
正如每个人都指出的那样,对GCC使用-S选项。我还想补充的是,结果可能会有所不同(很大!)取决于你是否添加优化选项(-O0为无,-O2为积极优化)。
-O0
-O2
特别是在RISC架构上,编译器在进行优化时经常会把代码转换得几乎认不出来。结果令人印象深刻,令人着迷!
这将生成C代码+行号交织的汇编代码,以便更容易地查看哪行生成了什么代码(- s -fverbose-asm - g - 02):
# 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页)找到的。
如前所述,查看- s标志。
'-fdump-tree'标志家族也值得一看,特别是-fdump-tree-all,它让你看到一些GCC的中间形式。这些程序通常比汇编程序更具可读性(至少对我来说),并让您了解优化传递的执行情况。
使用“-S”作为选项。它在终端中显示程序集输出。
来自FAQ 如何让GCC生成程序集代码:
gcc -c -g -Wa,-a,-ad[其他gcc选项]foo.c >foo.lst
作为PhirePhly的回答的替代。
或者就像大家说的那样使用- s。
如果您正在寻找LLVM程序集:
llvm-gcc -emit-llvm -S hello.c
好吧,正如每个人所说,使用- s选项。
如果你使用-save-temps选项,你也可以得到预处理文件(.i),程序集文件(.s)和目标文件(*.o)(分别通过使用- e, - s和- c来获得它们)。
下面的命令行来自克里斯蒂安·加宾的博客:
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)
我在答案中没有看到这种可能性,可能是因为这个问题来自2008年,但在2018年,你可以使用Matt Goldbolt的在线网站https://godbolt.org
你也可以在本地克隆git并运行他的项目https://github.com/mattgodbolt/compiler-explorer
.
下面是在Windows上查看/打印任何C程序的汇编代码的步骤:
在控制台/终端命令提示符中:
cd C:\gcc gcc -S complete path of the C file ENTER
一个示例命令(如我的示例)
gcc -S D:\Aa_C_Certified\alternate_letters.c
这将输出一个'。原“。c”文件的S '文件。
在此之后,键入以下命令
cpp filename.s ENTER
示例命令(如我的示例)
cpp alternate_letters.s <enter>
这将打印/输出C程序的整个汇编语言代码。
最近我想知道a中每个函数的组装,我是这样做的:
gcc main.c // 'main.c' source file gdb a.exe // 'gdb a.out' in Linux
广东发展银行:
disass main // Note here 'main' is a function // Similarly, it can be done for other functions.
下面是一个使用GCC的C语言解决方案:
gcc -S program.c && gcc program.c -o output
上面使用的program.c是程序的名称,而输出是你想要生成的可执行文件的名称。
-save-temps
这在元数据的回答中提到过,但让我进一步举例说明。
与- s相比,这个选项的最大优点是可以很容易地将它添加到任何构建脚本中,而不会过多地干扰构建本身。
当你这样做时:
gcc -save-temps -c -o main.o main.c
#define INC 1 int myfunc(int i) { return i + INC; }
现在,除了正常的输出main.o,当前工作目录还包含以下文件:
main.o
main.i是一个奖励,包含预处理文件:
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包含所需生成的程序集:
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对象输出相同的目录,而不是当前工作目录,从而避免了潜在的basename冲突。
-o
关于这个选项的另一个很酷的事情是,如果你添加-v:
-v
gcc -save-temps -c -o main.o -v main.c
它实际上显示了正在使用的显式文件,而不是/tmp下丑陋的临时文件,因此很容易知道正在发生什么,其中包括预处理/编译/汇编步骤:
/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中看到。
CMakeFiles/main.dir/main.c.s
它在CMake 3.16.1上进行了测试。