本周的学习内容主要是庖丁解牛Linux的第一章,然后看完书后,又跟着云班课加深学习了一下第一章的内容。第一章主要讲述了linux里的汇编指令的一些指令,比如movl,pushl,popl等等,这些指令都是围绕数据结构中的栈做文章的。贯穿这一章的是esp、ebp,eip指针寄存器。简单来说,ebp就是指的栈底,esp就是指着栈顶。eip就是正在执行的指令所在的位置,然后不断重复的进行入栈出栈操作。
写了一个简单的C程序,如图所示,具体代码如下:
C语言代码:
//mian.c
int g(int x)
{
return x + 3;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(8) + 1;
}
通过gcc -s -o main.s main.c -m32指令将其编译成汇编程序,打开该汇编文件并删除不重要的信息,如图所示,具体代码如下:
反编译后汇编代码
.file "main.c"
.text
.globl g
.type g, @function
g:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
movl 8(%ebp), %eax
addl $4, %eax
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size g, .-g
.globl f
.type f, @function
f:
.LFB1:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call g
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE1:
.size f, .-f
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $4, %esp
movl $6, (%esp)
call f
addl $2, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE2:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
我们来分析一下该汇编指令(通过画图的方式来帮助理解,为了区分不同时期的寄存器,将其后面加了个括号,括号里的数字表示该寄存器的第几种状态),如图所示:
首先执行main函数
pushl %ebp :将esp的值减4(指向下一帧栈地址),并将ebp寄存器里的值存入esp此时指向的栈地址。
Movl %esp, %ebp :将ebp寄存器同样指向esp寄存器指向的那块栈地址。
Sub $16, %esp :将esp寄存器的内容减16,即向下移动四个帧。
movl $1, -12(%ebp) : 将变址地址-12(%ebp)存入立即数1
movl $8, -8(%ebp) :将变址地址-8(%ebp)中存入立即数8
pushl -8(%ebp) : 将上述变址地址中的内容8压入栈内。
pushl -12(%ebp) :同样将上述变址地址中的内容1压入栈内。
call g : 即调用g子函数,可分为 pushl eip(存储call g的下一条指令即addl $8, %esp的地址)和 movl g, eip。
执行 g 子函数:
pushl %ebp : 将ebp的值压入栈内。
movl %esp, %ebp : 将ebp寄存器也指向esp所指示的位置。
movl 8(%ebp), %edx : 将8(%ebp)中的内容1存入edx寄存器中
movl 12(%ebp), %eax : 将12(%ebp)中的内容存入eax寄存器中
addl %edx, %eax : 将edx和eax中的内容相加并存入eax寄存器当中
popl %ebp : 将esp寄存器的内容加四,即向上移动一帧,将pop出的内容赋值给ebp, ebp指向之前ebp所指的位置
整个过程执行完后结果如新图所示:
ret : ret指令分为popl eip 和将eip寄存器存入保存的call指令的下条指令即 add.......
addl $8, %esp : 将esp寄存器向上移动两帧。
movl %eax, -4(%ebp) : 将eax的值9赋给-4(%ebp)的位置
movl -4(%ebp), %eax : 再从那个位置赋值给eax寄存器,(之所以那么绕,我认为是因为在C语言里我定义了一个变量c来得到g函数的返回值)
leave : 可分为 movl %ebp, %esp 和 popl ebp 即将esp指向ebp所指的位置,并将ebp以下的指令弹出,相当于清空栈。
总结:
1. 本周通过学习计算机汇编的基础知识和基本原理,理解到了计算机的基本原理存储程序和程序控制。
2. 预先要把指挥计算机如何进行操作的指令序列(称为程序)和原始数据通过输入设备输送到计算机内存贮器中。在书写或者解读汇编程序的时候,一定要关注每一条指令中规定的计算机取数的地址,进行了什么操作,然后送到什么地址去等步骤。如果只是单纯用眼睛去看,脑子去想肯定是不够的,汇编程序的重点就是在栈上做文章,一定要按过程画图来辅助自己的理解,这样一段程序的理解或者编写就会容易许多。
3. 本次实验的程序非常简单,但是重要的是要学会思考方式,为后面第三章程序的机器级表示的内容打好基础,把两章的内容结合起来去学习,学会用计算机的思维来进行思考和编程,会让我们对计算机系统的底层构造有更好的理解。



