存储程序计算机工作模型
- 冯诺依曼体系结构就是指存储程序计算机
- 从硬件上,CPU和内存由总线连接,CPU上有一个寄存器叫IP(Instruction Pointer),64位下叫rip, 可以认为是一个指针,总是指向内存中的代码段(cs code segment),cpu从IP指向的内存地址取来一条指令执行,执行完后,IP自加一,取下一条指令继续执行。
- 从程序员来看,内存有指令和数据,cpu抽象成一个for循环,从内存取下一条指令,解释执行指令
- 32位下, eip每一次自加到下一条指令,因为指令长度是不同的,eip除了可以自加外,可以被call,ret, jmp和条件跳转改变(对应c里的if-else,函数调用,return等)
寄存器
- 32位下有8个通用寄存器
- eax 累加器
- ebx 基地址寄存器
- ecx 计数寄存器
- edx 数据寄存器
- ebp 堆栈基指针(寄存器)
- esi edi 变址寄存器
- esp 堆栈顶寄存器
- 段寄存器
- cs 代码段寄存器,其值为代码段的段值
- ds 数据段寄存器,其值为数据段的段值
- es 附加段寄存器,其值为附加段的段值
- ss 堆栈段寄存器,其值为堆栈段的段值
- fs 附加段寄存器,其值为附加数据段的段值
- gs 附加段寄存器,其值为附加数据段的段值
cpu在实际取指令时根据cs:eip来准确定位一个指令
每一个进程都有自己的堆栈
- 寻址方式
- movl %eax, %edx
- edx = eax; 寄存器寻址
- movl $0x123, %edx
- edx = 0x123 立即寻址,立即数是以$开头的数值
- movl 0x123, %edx
- edx = (int32_t)0x123 直接寻址 直接访问一个指定的内存地址的数据
- movl (%ebx), %edx
- edx = (int32_t)ebx 间接寻址 将寄存器的值作为一个内存地址来访问内存
- movl 4(%ebx), %edx
- edx = (int32_t)(ebx + 4) 变址寻址(displaced) 在间接寻址之时改变寄存器的数值
- movl %eax, %edx
- 常用指令
- pushl %eax
- subl $4, %esp
- movl %eax, (%esp)
- popl %eax
- movl (%esp), %eax
- addl $4, %esp
- call 0x12345
- pushl %eip
- movl $0x12345, %eip
- ret
- popl %eip
eip寄存器不能被程序员直接修改,只能通过特殊指令间接修改
- popl %eip
- enter
- pushl %ebp
- movl %esp, %ebp
- leave
- movl %ebp, %esp
- popl %ebp
- pushl %eax
汇编一个简单的c程序
int g(int x)
{
return x + 5;
}
int f(int x)
{
return g(x);
}
int main()
{
return f(3) + 9;
}
- 通过gcc -S -o main.s main.c -m32 生成的汇编的代码,所有以.开头的都是用于链接的辅助信息,不会被实际执行
- 注释从main函数看起,遇到call x则跳转到该函数x,ret之后返回到call x的下一条指令
- 内存中栈是从高地址向低地址增长的
- 从main开始,eip会自动指向吓一跳指令,或者由指令修改
- 函数调用堆栈是由逻辑上多个堆栈叠加起来的,ebp指向堆栈栈底,esp指向栈顶,堆栈是相对的,因为每个函数都有自己的基地址
- 函数的返回值默认使用而eax寄存器存储返回给上一级函数
g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax #将f传来的参数放到eax,即eax = 8
addl $5, %eax #eax = 8 + 3 = 11
popl %ebp #将调用g的函数ebp给pop回来,即ebp指回f的栈基址
ret #popl %eip,此时eip指回调用该函数的下一条指令
f:
pushl %ebp #将ebp压栈
movl %esp, %ebp #修改ebp的值,此时ebp和esp指向同一个位置
pushl 8(%ebp) #取ebp+8里的数,即将main调用f时传进的3压栈,相当于subl $4, %eax movl 8(%ebp) %eax movl %eax (%esp)
call g #pushl %eip,跳转到g继续执行
addl $4, %esp #对应于上面的pushl 8(%ebp)感觉这句话可以不用,因为下面有个leave
leave #把esp指向ebp的位置
ret #popl %eip,eip指回调用它的函数的下一条指令
main: #eip最初指向main
pushl %ebp #将ebp压栈
movl %esp, %ebp #修改ebp的值,此时ebp和esp指向同一个位置
pushl $3 #将esp减掉4,然后将3压栈
call f #pushl %eip,此时压入的eip指向的是下一行,然后eip指向了f,跳转到f继续执行
addl $4, %esp
addl $9, %eax
leave #回到main函数最初的状态
ret #返回到main函数之前的堆栈
总结
计算机系统是由硬件和系统软件组成的,它们共同工作来运行应用程序。虽然系统的具体实现方式随着时间不断变化,大师系统内在的概念没有变化。所有计算机系统都有相似的硬件和软件组件,它们执行着相似的课程。
本次课程使我重新复习了汇编程序设计,并对c语言和汇编的对应有了更深一步的了解,让我能更看清楚自己所写的c语言代码在内存中的表示方式。并且从汇编代码看起来,cpu作加载(从主存复制一个字节或者一个字寄存器)、存储(从寄存器复制一个字节或者一个字到主存某个位置)、操作(作算术运算)、跳转等功能,和主存之间的行为通过总线相连。