How a computer works
Disassemble a simple C program
First, we create a C program main.c
type in vim editor
int g(int x)
{
return x + 3;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(8) + 1;
}
Experimental section (for 64-bit Linux virtual machine environment, 32-bit Linux environment might be slightly different) Use:
$ gcc –S –o main.s main.c -m32
Open main.s, and manually remove obtained after a string beginning with:
Now, I try to use their own language to explain this 23-line assembly language:
/*
** 个人理解,有错望不吝指出
*/
g:
pushl %ebp /*同样,先把f函数的栈底地址存到栈顶*/
movl %esp, %ebp /*再把基指针指向栈顶,也就是g函数的栈底*/
movl 8(%ebp), %eax /*相对寻址,ebp的内容指向的地址倒退2个单元格,指向的是
esp中的数据8,将其存入累加器eax中*/
addl $3, %eax /*教累加器运算8+3,存在累加器eax中*/
ret /*返回到f*/
f:
pushl %ebp /*执行f这个函数之前,同样需要把基指针(main这一段的
栈底)存入栈顶*/
movl %esp, %ebp /*然后将新的基指针指向f这个函数指令的栈底*/
pushl 8(%ebp) /*这里汇编与书中不同,相当于:
subl $4, %esp 堆栈减4,相当于加一个单元格
movl 8(%ebp), %eax 相对寻址,ebp指向的esp的位置在esp上退
两个单元格(2*4)指向的数据(刚刚main那里push进去的8)
存到累加器eax中
movl %eax, (%esp) 把刚刚存到累加器的数据(8)打入栈顶*/
call g /*又是一个中断,去g*/
addl $4, %esp /*中断返回,esp指针退回一个单元格*/
leave /*把上一条指令的栈底地址存到堆栈esp中,并弹出当前ebp的“栈顶”*/
ret /*返回到main*/
main:
pushl %ebp /*ebp,基指针寄存器,指向的是当前指令的栈底(可以
理解为每条指令占据堆栈的一段),在运行的一开始,
我们需要把这段指令的开头位置存到堆栈寄存器esp中
以便后面被别的指令中断之后还能找回来*/
movl %esp, %ebp /*esp,堆栈寄存器,地址指向栈顶,这里把栈顶
的位置存到ebp,是因为当执行main函数时,相当
于一次中断,之前的指令被打断,等会儿还需要
回来执行它,所以把当前的栈顶地址存到ebp中*/
pushl $8 /*现在我们执行这条新的指令,它把8这个数据打入栈顶,
一会儿就盘它*/
call f /*这又是一次中断,main的执行被打断,去f*/
addl $4, %esp /*esp再退一格*/
addl $1, %eax /*eax中的数据是8+3,此时再+1,结果12.eax寄存器是
默认存储函数返回值的寄存器*/
leave /*撤销main函数*/
ret /*回到初始状态*/
(PS: too difficult to draw, and beg the teacher to recommend a easy to use drawing software)
Problem: the Leave this instruction is not very understanding. The book says the equivalent of:
movl %ebp, %esp
popl %ebp
But each interrupt to execute the new instruction, the value of ebp is saved to the stack, why should this time movl ebp value of the stack. I think ebp just the initial position of each interrupt calls to keep the register stack, I do not know understand, right?