您的位置:首页 > 运维架构 > Linux

linux内核分析之-x86汇编原理

2016-02-24 23:23 579 查看

linux内核分析之-x86汇编原理

作者:郎勇

前言

冯·诺依曼提出了在数字计算机内部的存储器中存放程序的概念(Stored Program Concept),这是所有现代电子计算机的范式,被称为“冯· 诺依曼结构”,按这一结构建造的电脑称为存储程序计算机(Stored Program Computer),又称为通用计算机。冯·诺依曼计算机主要由运算器、控制器、存储器和输入输出设备组成,它的的特点是:程序以二进制代码的形式存放在存储器中;所有的指令都是由操作码和地址码组成;指令在其存储过程中按照执行的顺序(摘自百度百科);

学习汇编,有助于我们更好地理解通用计算机的运行原理,因此无论在我们的学习和工作中是否会涉及到汇编,了解汇编的基本知识,对开阔我们程序开发人员的视野,是大有帮助的。编写本博客的初衷是为了完成网易云课堂的随堂作业,希望自己在编写本博客的过程中,对x86汇编原理获得更深一步的认识,与此同时,也希望能帮助所有看到这篇博客的人。

对系统学习linux内核感兴趣的同学,可以前往

《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

linux内核分析之-x86汇编原理
前言

通用寄存器的类型

常见的汇编指令

一段简单的C程序对应的汇编程序

结论

通用寄存器的类型

16bit32bitdesc
AXEAX累加器(Accumulator)
BXEBX基地址寄存器(Base Register)
CXECX计数寄存器(Count Register)
DXEDX数据寄存器(Data Register)
BPEBP堆栈基指针(Base Pointer)
SIESI变址寄存器(Index Register)
DIEDI变址寄存器(Index Register)
SPESP堆栈顶指针(Stack Pointer)
此外还有一个需要了解的寄存器是
IP\EIP指令指针寄存器(Instruction Pointer)
,它可以存放下次将要执行的指令在代码段的偏移量。可以理解为它总是指向下一条将要执行的指令的编号。IP\EIP寄存器通常是不可以直接操作的,通常只能通过jmp、call或者ret等指令进行间接修改。

常见的汇编指令

数据传输指令 MOV, PUSH, POP等

程序转移指令 CALL, RET, JMP等

算数运算指令 ADD,SUB,MUL,DIV等

逻辑运算指令 AND,OR,XOR,NOT等

一段简单的C程序对应的汇编程序

我们编写一段简单的c程序

int g(int x)
{
return x + 11;
}

int f(int x)
{
return g(x);
}

int main(void)
{
return f(3) + 1;
}


执行命令
gcc –S –o main.s main.c -m32
生成c程序对应的汇编代码



为了方便查看起见,把所有
.
开头的行删除,因为这些行是汇编器命令,这里我们并不关心。结果如下:

g:
pushl   %ebp
movl    %esp, %ebp
movl    8(%ebp), %eax
addl    $11, %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    $3, (%esp)
call    f
addl    $1, %eax
leave
ret


程序从入口函数main开始分析:

pushl   %ebp
movl    %esp, %ebp


这两句相当于enter指令,用来在程序开头启用新的栈底地址,将之前的堆栈基地址保存到栈顶指向的内存地址中,然后将esp和ebp指针对齐。

subl    $4, %esp


将esp向栈顶偏移4个字节的距离

movl    $3, (%esp)


将立即数3存储到esp寄存器指向的内存地址中

call    f


程序跳转到函数f的入口地址,eip中存放函数f的第一条指令

pushl   %ebp
movl    %esp, %ebp
subl $4, %esp


此三条指令跟main函数的前三条指令完成的功能是一样的。

movl    8(%ebp), %eax


将堆栈基地址向栈底偏移8个字节的长度,内容取出后存储到eax中。此时eax中存储的内容是3。

movl    %eax, (%esp)


将eax中的内容(即3)存储到esp对应的内存地址中。

call    g


程序跳转到函数g的入口地址,eip中存放函数g的第一条指令。

pushl   %ebp
movl    %esp, %ebp


此三条指令跟main函数和f函数的前两条指令完成的功能是一样的。

movl    8(%ebp), %eax


将堆栈基地址向栈底偏移8个字节的长度,内容取出后存储到eax中。此时eax中存储的内容是3。

addl    $11, %eax


将立即数11加到eax中,eax中的内存为14

popl    %ebp


弹出esp中的内容,恢复函数f的堆栈基地址

ret


eip恢复为函数g返回后,函数f继续执行的代码段偏移

leave
ret


弹出esp中的内容,恢复函数main的堆栈基地址,eip恢复为函数f返回后,函数main继续执行的代码段偏移

addl    $1, %eax


将立即数1加到eax中,此时eax的内容为14+1=15

leave
ret


弹出esp中的内容,恢复之前调用函数的堆栈基地址,eip恢复为函数main返回后,调用函数继续执行的代码段偏移

结论

计算机的运行过程,就是一个按次序读取存储器中代码指令,然后通过cpu解释并执行指令的过程。执行时所用到的基本数据结构是堆栈。cpu可以直接操作寄存器中的内容。

原创作品转载请注明出处
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: