做作业,顺便记录笔记
AT&T相关指令入门知识笔记,见外联 http://ouonline.net/att-asm-1
movl %eax,%edx edx=eax 寄存器寻址 registermode(相当于c地址指针赋值)
movl $0x123, %edx edx=0x123 立即寻址 immediate(相当于直接赋值地址值给c指针)
movl 0x123,%edx edx=*(int*)0x123 直接寻址 direct(相当于获取地址指针的内容,* 操作)
movl (%ebx),%edx edx=*(int*)ebx 间接寻址 indirect(相当于地址指针赋值后取值)
movl 4(%ebx),%edx edx=*(int*)(ebx +4) 变址寻址 displaced(相当于地址指针增加后,赋值后取值)
Pushl %eax | Subl $4, %esp //栈顶指针减4 Movl %eax, (%esp) //将eax中的值放入栈顶指针指向的内存位置 |
Popl %eax | Movl (%esp), %eax //从栈顶指针指向的内存中的值放入eax中 Addl $4, %esp //栈顶指针加4,栈在向上收缩 |
Call 0x12345 (eip 无法直接操作) |
Pushl %eip //ip压栈 Movl $0x12345, %eip //将0x12345放入eip中 |
Ret | Popl %eip //ip出栈 |
Enter | Pushl %ebp ebp入栈 Movl %esp, %ebp 使得ebp等于当前esp |
leave | Movl %ebp, %esp 使得esp等于当前ebp(相当于栈清空了) Popl %ebp ebp地址出栈 |
================================================================================================================
实现环境:
系统 win7 64位
编译器 mingwin
现有c程序 main.c :
int g(int x)
{
return x + 3;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(8) + 1;
}
去掉相关带.的行,分析main.s
_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
andl $-16, %esp
subl $16, %esp
call ___main
movl $8, (%esp)
call _f
addl $1, %eax
leave
ret
1.程序由_mian进入,首先 pushl %ebp
堆栈变化如下图,堆顶压入了ebp的值
2. movl %esp,%ebp
3.andl $-16,%esp
esp 与 0xfffffff0 与运算,使得它对齐寻址空间,加快cpu处理速度
4.subl $16,%esp
esp 向下移动4位
4.call ___main
调用___main,由于在.def 里面被我删了,开始无法理解,然后google才知道,是编译器加入的,理解为加入调试信息,初始化变量等。真正的函数开始是这里开始的
.def___main;.scl2;.type32;.endef
至于.scl 2 和 .type 32 的意思,上网查一下就有的了。stackoverflow
5.因为call返回后,堆空间不变的,直接进入下一条
movl $8,(%esp)
6.call _f 调用_f 函数,由于eip不可以直接修改,只能调用 call
call 是两条指令组成的,它先压入下一条指令的地址到栈顶,这里就是addl $1,%eax的地址,然后把f的地址赋予eip寄存器。函数调用开始。
7.运行_f 函数的指令
pushl %ebp
movl %esp,%ebp
后,变成下图,注意这里压入之前ebp地址A
8
9.运行movl 8(%ebp),%eax,从图可知就是把8的值移动加入到eax寄存器里面
10.然后把eax的值传给exp所指的空间
11.call _g
12.之后和之前的一样,压入ebp地址,改变ebp,然后把8赋予eax
13.往eax加3
14.popl %ebp 把exp的值给ebp,相当于ebp值回地址B
15.ret后,eip指向leave指令的地址,esp加四
16.运行f函数的leave指令,有两个过程
先movl %ebp,%esp
然后popl %ebp,相当于堆栈返回了
17.ret指令,eip指向addl指令
18.往eax加1
19.leave,ebp恢复到进入程序之前的ebp地址
20.ret 终于结束了,把esp上指的内容给会eip,函数返回。
总结,计算机程序运行是依照冯诺依曼体系结构,从硬件上看其工作的方式,就是cpu依次读取寄存器中的指令来进行工作,不同的指令集合构成不同的计算机系统和程序。