博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
《Linux 内核分析》课程作业(1)——计算机基本原理和汇编基础
阅读量:6273 次
发布时间:2019-06-22

本文共 3601 字,大约阅读时间需要 12 分钟。

hot3.png

1. 计算机是如何工作的?——存储程序

计算机工作的基本方式:取指执行。即,从内存中指定位置取出指令,然后在 CPU 中执行该指令。

IP 寄存器: IP (Instruction Pointer) 总指向计算机即将执行的指令。计算机在工作的时候,首先到 IP 所指向的内存地址获取将要执行的指令,然后 IP 自加 1(条指令)指向下一条指令。注意;每条指令所占据的内存空间可能是不同的。

在 x86 架构中,实际由 EIP 寄存器作为指令指针(32位寄存器)。EIP 寄存器可被 CALL, RET, JMP 以及条件跳转语句修改。

2. x86 汇编基础

  • x86 CPU 寄存器
通用寄存器 低16位 低8位 全名
Accumulator AX AL EAX
Base Register BX BL EBX
Count Register CX CL ECX
Data Register DX DL EDX
注:EAX 寄存器通常用于函数返回值的默认寄存器

特殊寄存器 对应 16-bit 寄存器  32-bit 寄存器
Base Pointer BP EBP
Stack Pointer SP ESP
Source Index SI ESI
Destination Index DI EDI
说明:EBP 和 ESP 分别是程序运行栈底和栈顶指针。 

段寄存器: CS (Code Segment), SS (Stack Segment) 等。

CPU 在取指令时,根据 CS 和 EIP 查找指令。

在 32 位汇编中,寄存器缩写一般以 E 开头,意为 Extended;64 位汇编中,寄存器缩写以 R 开头。

  • 常用命令

MOV, PUSH, POP, CALL, RET

MOV - 赋值命令。用法:movl a, b   将 a 的值赋给 b

PUSH - 压栈。用法: pushl a,将 a 的值压栈。

等价命令:  addl 4, %esp

                movl a, (%esp)

POP - 出栈。用法:popl a,将栈中弹出的值赋给 a

等价命令:  movl (%esp), a

                 subl 4, %esp

CALL - 调用函数。用法:call func

实际操作:pushl %eip

              movl func, %eip

RET - 返回。

实际操作:popl %eip

[注]CALL 和 RET 不能用对应实际操作的语句来替换,原因:eip 寄存器只能通过指定语句赋值。

[注]MOV, PUSH, POP 后可以加 b(8-bit), w(16-bit), l(32-bit), g(64-bit)。

  • 寻址方式
寻址方式 实例 说明
Register mode movl %eax, %ebx 直接将 eax 的内容赋值给 ebx
Immediate movl $0x123, %eax 将数 0x123 赋给 eax
Direct movl 0x123, %eax 将地址 0x123 所存放内容赋给 eax
Indirect movl (%ebx), %eax 将地址为 ebx 的值赋给 eax
Displace movl 4(%ebx), %eax 将地址为 ebx + 4 的值赋给 eax

3. 实际例程分析

实验截图

int g(int x) {    return x + 7;}int f(int x){    return g(x);}int main(void){    return f(11) + 9;}
上面是一段简单的 C 语言代码,使用 gcc -S test.c -o test.s -m32 将 C 语言编译成为汇编语言,得到如下的汇编代码(只保留指令内容)
g:	pushl	%ebp	movl	%esp, %ebp	movl	8(%ebp), %eax	addl	$7, %eax	popl	%ebp	retf:	pushl	%ebp	movl	%esp, %ebp	subl	$4, %esp	movl	8(%ebp), %eax	movl	%eax, (%esp)	call	g	leave	retmain:	pushl	%ebp	movl	%esp, %ebp        subl    $4, %esp        movl	$11, (%esp)	call	f	addl	$9, %eax	leave	ret

由于 main 函数是整个程序的入口,我们从 main 标签开始分析整个汇编程序。

main: 18, 19行

pushl %ebp - 将栈底指针压栈movl %esp, %ebp - 将 esp 赋值给 ebp

以上两个语句实质是将函数运行栈置空,两个指令完成之后,ebp 和 esp 相等,表现为空栈。而 ebp 上一个位置存放的即是之前 ebp 的值。这个过程也被定义为宏指令 enter。对应的逆过程指令为 leave(main: 24行),可以展开写作:

movl %ebp, %esppopl %ebp

通过 enter 和 leave 的配合,保证了栈状态在函数调用前后的一致性。

main: 20, 21行

subl $4, %espmovl $11, (%esp)

可以看到这实质上就是 pushl $11 指令的展开,即将函数调用参数压栈。将立即数 11 而是赋给了 esp 作为地址所指向的内存单元。

call f

调用 f 函数,此指令实际由两个指令组成:首先是将当前 eip 压栈(注意!!),然后将 f 函数起始指令地址赋给 eip,从而实现指令执行的跳转。回顾一下,到目前为止执行了三次压栈操作,依次压入了 ebp (main 函数调用前状态),立即数11,以及 eip (指向程序第 23 行)。

现在,程序执行跳转到 f 部分的第一条指令(f: 9行)

pushl %ebp
接下来就是在 main 部分已经解释的函数调用栈的初始化动作,首先将 ebp (f 函数调用前状态)压栈,接着第 10 行指令将当前 esp 赋值给 ebp。然后接下来三行,
subl	$4, %espmovl	8(%ebp), %eaxmovl	%eax, (%esp)
如果排列成这样的顺序则更容易理解,
movl	8(%ebp), %eaxsubl	$4, %espmovl	%eax, (%esp)
观察代码,我们可以很安全地交换第一二行。交换后,第二、三行执行的调用函数时参数的压栈动作。那么第一行呢?可以看到是从 ebp + 8 这个地址取数并复制给 eax,那么这里 ebp + 8 这个地址存放的是什么呢?
我们回顾一下程序执行过程,可以发现,在第10行,ebp 被赋值成为了 f 函数的栈底指针;第9行
处,压入了 main 这个函数的栈底指针,此刻 ebp 指向的地址存放的正是这个指针的值。再往上存在跳转,在 main 函数调用 f 函数的的22行压入了当时的 eip 指针,这里的位置就是此刻的 4(%ebp)。再往上,在 main 函数第20,21行,程序将立即数 11 压栈,因此 8(%ebp) 存放的内容正是立即数 11。
在 32 位汇编中,函数调用参数通过栈进行传递,按照参数列表逆序压栈;紧接着压入 eip 指针和 ebp 栈底指针,并将 ebp 的值置为 esp。所以要访问第一个参数,需访问地址 ebp + 8。

之后的代码和上面大同小异,就不逐个分析了。

总结

总体说来,程序在执行的时候,如果不存在函数调用或者其他跳转语句时,CPU 将依照指令顺序线性地进行取指执行,这个过程中扮演者关键地位的是 EIP 寄存器,EIP 总指向即将执行的语句,并且取指后自动指向下一条指令。如果存在函数调用,如果存在函数调用,则需要在栈内维护以下信息;1. 函数调用参数;2. 函数返回位置;3. 函数运行栈指针

具体说来,在函数调用前,将参数压入栈内;接下来调用函数时将 EIP 寄存器的值压栈(正是函数返回地址);然后在被调函数中将主调函数的栈底指针压栈,并更新 EBP 为此刻栈顶指针的值(亦即置为空栈)。

在函数调用完毕后,进行逆操作:首先,在被调用函数中将 EBP 赋给 ESP(栈顶指针还原),弹出主调函数的栈底指针赋给 EBP(栈底指针还原);然后,弹出函数返回地址赋给 EIP,让程序可以从调用处接着往下执行。

=========================================

真实姓名:姚思远     原创作品转载请注明出处

《Linux内核分析》MOOC课程 

转载于:https://my.oschina.net/siyuany/blog/383570

你可能感兴趣的文章
poi 导入导出的api说明(大全)
查看>>
Mono for Android 优势与劣势
查看>>
将图片转成base64字符串并在JSP页面显示的Java代码
查看>>
js 面试题
查看>>
sqoop数据迁移(基于Hadoop和关系数据库服务器之间传送数据)
查看>>
腾讯云下安装 nodejs + 实现 Nginx 反向代理
查看>>
Javascript 中的 Array 操作
查看>>
java中包容易出现的错误及权限问题
查看>>
AngularJS之初级Route【一】(六)
查看>>
服务器硬件问题整理的一点总结
查看>>
SAP S/4HANA Cloud: Revolutionizing the Next Generation of Cloud ERP
查看>>
Mellanox公司计划利用系统芯片提升存储产品速度
查看>>
白帽子守护网络安全,高薪酬成大学生就业首选!
查看>>
ARM想将芯片装进人类大脑 降低能耗是一大挑战
查看>>
Oracle数据库的备份方法
查看>>
Selenium 自动登录考勤系统
查看>>
关于如何以编程的方式执行TestNG
查看>>
智能照明造福千家万户 家居智能不再是梦
查看>>
物联网如何跳出“看起来很美”?
查看>>
浅谈MySQL 数据库性能优化
查看>>