GCC生成的汇编代码

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

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文件,直接打开即可
这段汇编代码没有经过优化:
.file"code.c"
.globl _accum
.bss
.align 4
_accum:
.space 4
.text
.globl _sum
.def_sum;.scl2;.type32;.endef
_sum:
pushl%ebp
movl%esp, %ebp
subl$4, %esp # 为局部变量t在栈帧上分配空间
movl12(%ebp), %eax # %eax <- y
addl8(%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”选项开启二级优化的效果:
#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;.scl2;.type32;.endef
_sum:
pushl%ebp # 保存原%ebp
movl%esp, %ebp
movl12(%ebp), %eax # %eax <- y
movl8(%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 中有这样的函数:
int main()
{
return sum(1,2);
}
然后,我们用如下方法生成可执行文件:
#gcc -O2 -o prog code.o main.c
再反汇编:
objdump -d prog
00401050 <_sum>:
401050:55 push %ebp
401051:89 e5mov %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

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

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文件,直接打开即可
这段汇编代码没有经过优化:
.file"code.c"
.globl _accum
.bss
.align 4
_accum:
.space 4
.text
.globl _sum
.def_sum;.scl2;.type32;.endef
_sum:
pushl%ebp
movl%esp, %ebp
subl$4, %esp # 为局部变量t在栈帧上分配空间
movl12(%ebp), %eax # %eax <- y
addl8(%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”选项开启二级优化的效果:
#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;.scl2;.type32;.endef
_sum:
pushl%ebp # 保存原%ebp
movl%esp, %ebp
movl12(%ebp), %eax # %eax <- y
movl8(%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 中有这样的函数:
int main()
{
return sum(1,2);
}
然后,我们用如下方法生成可执行文件:
#gcc -O2 -o prog code.o main.c
再反汇编:
objdump -d prog
00401050 <_sum>:
401050:55 push %ebp
401051:89 e5mov %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
<think>我们被要求回答:如何使用gcc编译生成汇编代码? 根据引用[2]和引用[3]提供的信息,我们可以总结如下: 1. 使用gcc编译选项可以生成汇编代码,通常使用`-S`选项可以生成汇编代码(注意:这是汇编代码,不是反汇编,但有时我们也把从机器码转换为汇编代码的过程称为反汇编)。 2. 实际上,我们通常使用`objdump`工具来进行反汇编,而gcc本身并不直接生成汇编代码,但我们可以通过gcc编译生成可执行文件或目标文件,然后用objdump反汇编。 但是,根据引用[2]提到: arm-none-eabi-objdump的参数-S表示尽可能的把原来的代码和反汇编出来的代码一起呈现出来,-S参数需要结合 arm-linux-gcc编译参数-g,才能达到反汇编时同时输出原来的代码。 所以,我们可以分两种情况: 情况一:生成汇编代码编译阶段,从C代码汇编代码使用gcc-S选项,例如:gcc -S source.c -o output.s 这样会生成汇编代码文件output.s。 情况二:生成汇编代码(从可执行文件或目标文件反汇编) 步骤: 1.gcc编译源文件生成目标文件或可执行文件,并且为了在反汇编时看到C源代码,需要加上-g选项(调试信息)。 2. 使用objdump工具进行反汇编,命令为:objdump -d 文件名 (只反汇编代码段) 或者使用:objdump -D 文件名 (反汇编所有段) 如果希望混合显示源代码汇编代码,则使用:objdump -S 文件名 注意:使用objdump的-S选项时,需要目标文件或可执行文件包含调试信息(即编译使用-g选项)。 因此,具体步骤为: 1. 编译C程序并加入调试信息(-g): gcc -g -c source.c -o source.o # 生成目标文件 或者直接生成可执行文件: gcc -g source.c -o program 2. 使用objdump反汇编目标文件或可执行文件: objdump -S source.o > source_disassembly.txt # 将反汇编输出到文件 或 objdump -S program > program_disassembly.txt 另外,如果只想生成汇编代码(即编译过程中的汇编输出,而不是反汇编),则使用gcc -S source.c # 默认生成source.s gcc -S source.c -o output.s 但注意:-S生成汇编代码编译生成汇编,而objdump反汇编的是机器码对应的汇编,两者在格式和注释上可能有所不同。 因此,回答用户问题:如何生成汇编代码?我们应推荐使用objdump工具,并配合编译使用-g选项以得到更好的可读性(混合源代码汇编)。 总结步骤: 方法一:生成汇编代码编译阶段) gcc -S source.c -o source.s 方法二:生成汇编代码(从二进制文件反汇编gcc -g source.c -o program objdump -S program > disassembly.txt 注意:如果使用交叉编译工具链,则objdump应替换为对应的交叉版本,如arm-linux-gnueabi-objdump。 根据引用[2]的例子,他们使用的是arm-none-eabi-objdump,所以对于不同的目标平台,需要选择对应的objdump。 因此,在回答中,我们可以这样写:</think>### 如何使用 GCC 生成汇编代码 在 Linux 环境下,使用 GCC 生成汇编代码主要通过两种方式实现,以下是详细步骤和原理说明: --- #### 方法一:直接生成汇编文件(编译阶段) 使用 GCC 的 `-S` 选项可在编译过程中生成汇编代码文件: ```bash # 基本用法 gcc -S source.c -o output.s # 完整示例(包含调试信息) gcc -g -S -fverbose-asm -O0 source.c -o source.s ``` **参数说明**: - `-S`:在编译阶段停止,生成汇编代码文件 - `-g`:添加调试信息(与反汇编工具配合使用- `-O0`:禁用优化(保持代码结构清晰) - `-fverbose-asm`:在汇编中添加注释和变量名[^3] **生成文件**: - 输出 `.s` 文件包含完整的汇编指令和符号信息 - 示例输出片段: ```nasm main: pushq %rbp movq %rsp, %rbp # 创建栈帧 movl $42, -4(%rbp) # int a = 42; movl -4(%rbp), %eax popq %rbp ret ``` --- #### 方法二:从二进制文件反汇编(链接后) 使用 `objdump` 工具对可执行文件进行反汇编: ```bash # 编译时添加调试信息 gcc -g source.c -o program # 反汇编可执行文件 objdump -dS program > disassembly.txt ``` **关键参数**: - `-d`:反汇编代码- `-S`:交叉显示源代码汇编(需 `-g` 编译选项)[^2] - `-M intel`:指定 Intel 汇编语法(默认 AT&T) **输出示例**: ```nasm 0000000000001139 <main>: #include <stdio.h> int main() { 1139: 55 push %rbp 113a: 48 89 e5 mov %rsp,%rbp int a = 42; 113d: c7 45 fc 2a 00 00 00 movl $0x2a,-0x4(%rbp) # 0x2a = 42 return a; 1144: 8b 45 fc mov -0x4(%rbp),%eax } 1147: 5d pop %rbp 1148: c3 ret ``` --- #### 高级技巧 1. **指定目标架构**: ```bash # ARM 平台反汇编 arm-linux-gnueabi-objdump -d -S program > arm_disasm.txt ``` 2. **分离代码段**: ```bash # 仅反汇编 .text 段 objdump -j .text -dS program > code_section.txt ``` 3. **生成 Intel 语法**: ```bash objdump -d -M intel program > intel_disasm.txt ``` --- #### 注意事项 1. **调试信息依赖**: - 交叉显示源码需编译时添加 `-g` 选项[^2] - 优化级别会影响反汇编可读性(建议使用 `-O0`) 2. **工具链选择**: | 文件类型 | 工具 | 特点 | |---------------|--------------|--------------------------| | 可执行文件 | objdump | 支持交叉显示源码 | | 目标文件(.o) | objdump -d | 显示未链接的汇编 | | 动态库(.so) | objdump -T | 查看动态符号表 | 3. **ARM 平台特殊处理**: ```bash # Thumb 指令集反汇编 arm-none-eabi-objdump -d --disassembler-options=force-thumb program ``` > 提示:使用 `gcc -Wa,-adhln -g source.c` 可实时查看编译过程中的汇编输出[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值