你如何得到汇编输出从C/ c++源在GCC?

如何做到这一点呢?

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

465560 次浏览

使用- s开关:

g++ -S main.cpp

也可以用gcc:

gcc -S main.c

也可参见

使用- s选项:

gcc -S program.c

如果你想看到的内容依赖于输出的链接,那么除了前面提到的 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的输出上。

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

gcc -S helloworld.c

这将在helloworld.c上运行预处理器(cpp),执行初始编译,然后在运行汇编器之前停止。在这种情况下使用有用的编译器选项,请参阅如何去除“;噪音”;从GCC/clang组装输出?< / >(或只是查看你的代码在马特Godbolt的在线编译器浏览器,它过滤掉指令和东西,并使用调试信息突出显示源行与asm匹配。)

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

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(用于显示符号重定位、禁用长机器代码换行和要求c++名称)。如果你不喜欢x86的AT&T语法,-Mintel。看到手册页

例如,objdump -drwC -Mintel -S foo.o | less-r对于.o非常重要,它只有00 00 00 00占位符用于符号引用,而不是链接的可执行文件

正如每个人都指出的那样,对GCC使用-S选项。我还想补充的是,结果可能会有所不同(很大!)取决于你是否添加优化选项(-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

Output of these commands .

下面是在Windows上查看/打印任何C程序的汇编代码的步骤:

在控制台/终端命令提示符中:

  1. 在C代码编辑器中编写一个C程序,如块代码:,并以扩展名.c保存它

  2. 编译并运行它。

  3. 成功运行后,进入安装GCC编译器的文件夹,输入以下命令获取“。c”文件的“。s”文件

    cd C:\gcc
    gcc -S complete path of the C file  ENTER
    

    一个示例命令(如我的示例)

    gcc  -S D:\Aa_C_Certified\alternate_letters.c
    

    这将输出一个'。原“。c”文件的S '文件。

  4. 在此之后,键入以下命令

    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
  1. 这里的第一部分将程序集输出存储在与程序相同的文件名中,但通过更改扩展名授予了,您可以将其作为任何普通文本文件打开。

  2. 这里的第二部分编译您的程序以供实际使用,并为您的程序生成具有指定文件名的可执行文件。

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

-save-temps

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

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

当你这样做时:

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

文件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对象输出相同的目录,而不是当前工作目录,从而避免了潜在的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上进行了测试。