目录
第二周作业内容:
1、阅读学习教材「庖丁解牛Linux 分析 」第1,2章
2、教材深入学习关注「Linux内核及安全」。
3、学习蓝墨云班课中第二周视频「计算机是如何工作的?」,并完成实验楼上配套实验一,基于树莓派或其他平台完成ARM相关内容。
4、完成蓝墨云班课中的测试。
一、实验楼配套实验一(X86汇编语言)
实验楼的环境是基于X86汇编语言的,下面还有一个基于ARM汇编语言的相同实验,我们可以进行比较学习,了解更多知识。
1 、编写一个main.c函数
2、通过命令编译成可执行文件和汇编代码
通过以下代码,即可生成汇编代码,其中-m32指生成32位的汇编程序,再通过ls代码即可查看已经生成了main.s文件。
gcc –S –o main.s main.c -m32
3、打开main.s汇编代码进行查看
通过以下代码即可查看main.s文件。
vi main.s
4、进行汇编代码的多余代码删除
打开发现main.s代码比较凌乱,因此需要删除多余的辅助代码,而这些辅助代码就是类似“.size”等“.”开头的文件。通过以下代码即可删除所有“.”开头的辅助代码,且删除后的代码如下:
sed -i '/[.]/d' main.s
5、进行汇编代码的分析
通过对比查看可发现,汇编代码和C语言代码其实是一一对应的。g、f、main和三个函数一一对应。
下面进行代码分析:
g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $3, %eax
popl %ebp
ret
f:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call g
leave
ret
main:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl $20232831, (%esp)
call f
addl $1, %eax
leave
ret
首先是进入main函数:
pushl %ebp #将EBP(栈基值)寄存器的值压入栈中
movl %esp,%ebp #EBP指针指向标号1的位置
subl $4,%esp #ESP指针向下增1,指向下一个标号位置
movl $20232831, (%esp) #将立即数20232831放入ESP寄存器指向的标号2位置
call f #调用g函数,先将ESP后移,存储现在EIP寄存器内的值,然后EBP内的值压榨存储到下一标号位置
call f即进入f函数,main函数的剩余部分等待执行
pushl %ebp #把ESP寄存器的值向下移到标号4
movl %esp, %ebp #把EBP寄存器的值标号1放到栈空间标号4的位置
subl $4,%esp #ESP寄存器指针向下加1,指向下一个标号位置
movl 8(%ebp), %eax #把立即数8放到EAX的寄存器
movl %eax,( %esp) #将EAX寄存器里的值放到ESP所指的位置
call g #调用g函数,先将ESP后移,存储现在EIP寄存器内的值,然后EBP内的值压榨存储到下一标号位置
call g即再次进入g函数,f函数剩余部分继续等待执行
pushl %ebp #将EBP(栈基值)寄存器的值入栈
movl %esp, %ebp #让EBP和ESP指向同一个位置
movl 8(%ebp), %eax #将EBP指针前两个标号位置的内容放入EAX运算单元内
addl $3,%eax #将EAX内的值+3
pool %ebp #将标号7放入EBP寄存器里,让EBP指针回归上一级函数的栈底
ret #函数返回,将标号6内的值放入EIP寄存器内
g函数执行结束,跳转回f函数的剩余部分
leave #销毁f函数的这一部分工作站栈
ret #函数返回,将标号3内的值放入EIP寄存器内
f函数执行完后,再重新跳回main函数执行剩余部分的main函数代码
addl $1,%eax #将EAX中内容加1,EAX寄存器是默认存储函数返回值的寄存器
leave #销毁main函数工作栈
ret #函数返回
二、基于ARM的配套实验一(ARM汇编语言)
进入老师发布的Openeuler操作系统。将C语言编译成ARM汇编语言,最好使用本就是ARM架构的主机,因此这里推荐使用Openeuler操作系统。否则,对于使用X86架构的主机用户,将C语言代码编译成ARM汇编语言代码需要安装交叉编译环境,相对比较复杂。
1、编写main.c函数
2、通过命令编译成汇编代码
gcc –S –o main.s main.c
3、查看汇编文件main.s
4、进行汇编代码的多余代码删除
同样通过以下代码进行多余辅助文件的删除。
sed -i '/[.]/d' main.s
5、进行汇编代码的分析
可以看到,X86汇编语言和ARM汇编语言还是非常不一样的,不过它们都和C语言一一对应。
g:
sub sp, sp, #16
str w0, [sp, 12]
ldr w0, [sp, 12]
add w0, w0, 3
add sp, sp, 16
ret
f:
stp x29, x30, [sp, -32]!
add x29, sp, 0
str w0, [x29, 28]
ldr w0, [x29, 28]
bl g
ldp x29, x30, [sp], 32
ret
main:
stp x29, x30, [sp, -16]!
add x29, sp, 0
mov w0, 0xba7f
movk w0, 0x134, lsl 16
bl f
add w0, w0, 1
ldp x29, x30, [sp], 16
ret
首先是进入main函数
stp x29, x30, [sp, -16]! # x29、x30 分别压入栈中,即 sp – 16 的内存地址所在的 16 个字节的存储空间
add x29, sp, 0 #将栈顶地址赋给栈基址,构造了 main 函数的初始栈空间
mov w0, 0xba7f #将 f 函数的参数20232831赋给寄存器 w0 进行传参。
movk w0, 0x134, lsl 16 #将 f 函数的参数20232831赋给寄存器 w0 进行传参。#这里两个函数都说进行传参的,因为20232831太长,无法一次性完成传参。第一次传参是传入16进制数0xba7f,第二次传参是传入16进制数作为开头,即将0xba7f后移16位,再传入0x134,从而得到20232831=0x134ba7f。
bl f #调用函数 f,先将下一指令地址保存到 x30 寄存器中,再进行跳转
下面进入f函数
f:
stp x29, x30, [sp, -32]! #将 x29 和 x30 压栈,只不过这里 sp 被回写成了 sp – 32,是为了多生成一些栈空间存放函数调用的参数,这会在后面的指令中体现。
add x29, sp, 0 #构造了 f 函数的初始栈空间。
str w0, [x29, 28] #将上 main 函数传递过来的参数从 w0 存入栈中
ldr w0, [x29, 28] #从栈中取回 w0
bl g #跳转到g函数
下面进入g函数
g:
sub sp, sp, #16 #为 g 开辟了 16 个字节的栈空间,以存放 g 函数传来的参数
str w0, [sp, 12] #将参数从 w0 存入栈中
ldr w0, [sp, 12] #将参数从 w0 栈中取出
add w0, w0, 3 #执行 +3 操作,准备作为返回值返回
add sp, sp, 16 #将栈指针上移(销毁形参和临时变量)
ret #将 x30 寄存器的值赋给指令指针寄存器 PC,执行返回操作,回到f函数
回到f函数执行剩余部分
ldp x29, x30, [sp], 32 #将 x29 和 x30 寄存器的内容退栈、销毁 f 的栈空间
ret # x30 寄存器的值赋给指令指针寄存器 PC,执行返回操作,回到main函数
回到main函数执行剩余部分
add w0, w0, 1 #将 f 函数的返回值 w0 加 1
ldp x29, x30, [sp], 16 #将 x29 和 x30 寄存器的内容退栈、销毁 f 的栈空间
ret #函数返回
三、总结
通过反编译成汇编语言的整个过程学习,能够知道更多关于计算机底层的运行逻辑。同时,X86和ARM两种不同的汇编语言的底层逻辑也稍有不同,通过对比学习了解了更多。总的来说,汇编还是相对复杂的部分,对了解计算机原理有非常大的帮助。
四、Chatgpt帮助