首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >GCC生成的汇编代码

GCC生成的汇编代码

作者头像
Java架构师必看
发布2021-03-22 11:57:13
发布2021-03-22 11:57:13
2.4K00
代码可运行
举报
文章被收录于专栏:Java架构师必看Java架构师必看
运行总次数:0
代码可运行

假设我们写了一个C代码文件 code.c包含下面代码:

代码语言:javascript
代码运行次数:0
运行
复制
int accum = 0;
int sum(int x, int y)
 {
     int t = x + y;
     accum += t;
     return t;
 }

这是用echo命令输入源码的效果,简单的就是最好的:)

一、查看GCC生成的汇编代码

在命令行上用“-S”选项,就能看到C编译器产生的汇编代码:

#gcc -S code.c 

注意:这里是大写的-S,如果用小写gcc会说找不到main函数

会在当前目录下生成code.s文件,直接打开即可

这段汇编代码没有经过优化:

代码语言:javascript
代码运行次数:0
运行
复制
 .file "code.c"
 .globl _accum
  .bss
  .align 4
 _accum:
  .space 4
  .text
 .globl _sum
  .def _sum; .scl 2; .type 32; .endef
 _sum:
  pushl %ebp
  movl %esp, %ebp
  subl $4, %esp                  # 为局部变量t在栈帧上分配空间
  movl 12(%ebp), %eax    # %eax <- y
  addl 8(%ebp), %eax       # %eax <- x + y
  movl %eax, -4(%ebp)     # t <- x +y
  movl -4(%ebp), %eax     # %eax <- t
  addl %eax, _accum        # _accum <- t + _accum
  movl -4(%ebp), %eax     # %eax <- t
  leave                                 # 平衡堆栈: %esp <- %ebp , popl %ebp
  ret                                       

下面是使用“-O2”选项开启二级优化的效果:

代码语言:javascript
代码运行次数:0
运行
复制
 #gcc -O2 -S code.c
  .file "code.c"
 .globl _accum
  .bss
  .align 4
 _accum:
  .space 4
  .text
  .p2align 4,,15                    # 使下一条指令的地址从16的倍数处开始,
 .globl _sum                        # 最多浪费15个字节
  .def _sum; .scl 2; .type 32; .endef
 _sum:
  pushl %ebp                       # 保存原%ebp  
  movl %esp, %ebp       
  movl 12(%ebp), %eax     # %eax <- y
  movl 8(%ebp), %edx       # %edx <- x
  popl %ebp                        # 恢复原%ebp
  addl %edx, %eax             # %eax <- x + y
  addl %eax, _accum         # _accum <- _accum + x + y
  ret

GCC产生的汇编代码有点难读,它包含一些我们不关心的信息。所有以 "." 开头的行都是指导汇编器和链接器的命令,称为“汇编器命令”。

代码中已经除去了所有关于局部变量名或数据类型的信息,但我们还是看到了一个对全局变量_accum的引用,这是因为编译器还不能确定这个变量会放在存储中的哪个位置。

二、用GDB查看目标文件的字节表示

  首先,我们用反汇编器来确定函数sum的代码长度是19字节。然后我们在文件code.o上运行GNU调试工具GDB,输入命令:

(gdb) x/19xb sum

这条命令告诉GDB检查(简写为"x")19个以十六进制格式表示的字节。

三、反汇编目标文件

在Linux系统中,带 "-d" 命令行选项调用OBJDUMP可以完成这个任务:

#objdump -d code.o

从这里可以看出函数sum的代码长度正好是19字节。

四、生成实际可执行的代码

  这需要对一组目标文件运行链接器,而这一组目标代码文件中必须包含有一个Main函数。在 main.c 中有这样的函数:

代码语言:javascript
代码运行次数:0
运行
复制
   int main()
   {
        return sum(1,2);
   }

然后,我们用如下方法生成可执行文件:

代码语言:javascript
代码运行次数:0
运行
复制
 #gcc -O2 -o prog code.o main.c

再反汇编:

代码语言:javascript
代码运行次数:0
运行
复制
 objdump -d prog
 00401050 <_sum>:
   401050: 55                              push   %ebp
   401051: 89 e5                        mov    %esp,%ebp
   401053: 8b 45 0c                   mov    0xc(%ebp),%eax
   401056: 8b 55 08                   mov    0x8(%ebp),%edx
   401059: 5d                              pop    %ebp
   40105a: 01 d0                        add    %edx,%eax
   40105c: 01 05 10 20 40 00  add    %eax,0x402010
   401062: c3                              ret  

  这段代码与code.c反汇编产生的代码几乎完全一样。一个主要的区别是左边列出的地址不同。第二个不同之处在于链接器终于确定了存贮全局变量accum的地址。地址由原来的0x0变成了现在的0x402010

本文由来源 21aspnet,由 javajgs_com 整理编辑,其版权均为 21aspnet 所有,文章内容系作者个人观点,不代表 Java架构师必看 对观点赞同或支持。如需转载,请注明文章来源。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档