实验要求
将一个简单的C程序汇编成LoongArch或RISC-V汇编代码,并逐步分析程序的执行过程,深入理解存储程序计算机和函数调用堆栈框架在执行过程中所起的作用。
实验背景
gcc对c程序的编译分为四个步骤
1. 预处理:对头文件、注释、宏等进行预处理,使用命令` gcc -E hello.c -o hello.i`生成.i/.ii文件
2. 编译:将经过预处理的文件生成汇编代码,使用命令`gcc -S hello.i -o hello.s`生成.s文件
3. 汇编:将汇编代码转换位二进制文件,使用命令`gcc -c hello.s -o hello.o`生成.o文件
4. 链接:将目标文件最终链接成可执行文件,使用命令`gcc hello.o -o hello`
实验过程
本次实验中使用的c程序为:
#include <stdio.h>
int add_a_and_b(int a, int b) {
return a + b;
}
int sub_a_and_b(int a,int b){
return a-b;
}
int main() {
return sub_a_and_b(add_a_and_b(2, 3),1);
最终生成的汇编代码及解读如下:
.file "lab1.c"
.text
.globl add_a_and_b
.type add_a_and_b, @function
add_a_and_b:
.LFB0:
.cfi_startproc
pushq %rbp // rbp入栈保存当前待执行的指令,下面开始执行当前函数体中的指令
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp // rbp = rsp,生成新的堆栈空间,rsp是64位
.cfi_def_cfa_register 6
movl %edi, -4(%rbp) // edi直接寻址、rbp间接寻址,将edi的值存入rbp中保存的地址-4后所指向的内存,32位
movl %esi, -8(%rbp) // esi直接寻址、rbp间接寻址,将esi的值存入rbp中保存的地址-8后所指向的内存,32位
movl -8(%rbp), %eax // rdp间接寻址、eax直接寻址,将rbp中保存的地址-8后所指向的值存入eax中,32位
movl -4(%rbp), %edx // rbp间接寻址、edx直接寻址,将rbp中保存的地址-4后所指向的值存入edx中,32位
addl %edx, %eax // eax = eax + edx
popq %rbp // 将之前保存的栈顶值弹出,并赋给rbp
.cfi_def_cfa 7, 8
ret // 函数指令执行完毕,返回并执行下一条指令
.cfi_endproc
.LFE0:
.size add_a_and_b, .-add_a_and_b
.globl sub_a_and_b
.type sub_a_and_b, @function
sub_a_and_b:
.LFB1:
.cfi_startproc
pushq %rbp // rbp入栈保存当前待执行的指令,下面开始执行当前函数体中的指令
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp // 直接寻址,将rsp中的值存入rbp中, 64位
.cfi_def_cfa_register 6
movl %edi, -4(%rbp) // edi直接寻址、rbp间接寻址,将edi的值存入rbp中保存的地址-4后所指向的内存,32位
movl %esi, -8(%rbp) // esi直接寻址、rbp间接寻址,将esi的值存入rbp中保存的地址-8后所指向的内存,32位
movl -8(%rbp), %eax // rdp间接寻址、eax直接寻址,将rbp中保存的地址-8后所指向的值存入eax中,32位
movl -4(%rbp), %edx // rbp间接寻址、edx直接寻址,将rbp中保存的地址-4后所指向的值存入edx中,32位
subl %eax, %edx // edx = edx - eax
movl %edx, %eax // 直接寻址,将edx的值赋给eax
popq %rbp // 将之前保存的栈顶值弹出,并赋给rbp
.cfi_def_cfa 7, 8
ret // 函数指令执行完毕,返回并执行下一条指令
.cfi_endproc
.LFE1:
.size sub_a_and_b, .-sub_a_and_b
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
pushq %rbp // rbp入栈保存当前待执行的指令,下面开始执行当前函数体中的指令
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp // 直接寻址,将rsp的值存入rbp中
.cfi_def_cfa_register 6
movl $3, %esi // 立即寻址,将3存入esi中
movl $2, %edi // 立即寻址, 将2存入edi中
call add_a_and_b // 调用add_a_and_b函数,此时eax中保存了add_a_and_b(3,2)计算得到的值
movl $1, %esi // 立即寻址,将1存入esi中
movl %eax, %edi // 直接寻址,将eax中的值存入edi中
call sub_a_and_b // 调用sub_a_and_b函数,此时eax中保存了最终计算得到的值
popq %rbp // 将栈顶元素出栈并存入rbp中
.cfi_def_cfa 7, 8
ret // 返回,退出main
.cfi_endproc
.LFE2:
.size main, .-main
.ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-44)"
.section .note.GNU-stack,"",@progbits