大学里的微机原理课学的让我汗颜,工作之余,重新学习了一遍x86的汇编。
汇编主要包括寄存器,指令操作,控制循环结构以及栈帧中的%esp和%ebp的变化。
32位x86包括8个寄存器,分别是%eax(accumulate),%ecx(conuter),%edx(data),%ebx(base), %esi(source index), %edi(dest index),%esp(stack pointer),%ebp(base pointer),其中前4个寄存器分别有它的低8位(al),低16位(ax),低16位中的高8位寄存器(ah),因此,对应的指令集应按照不同的bit位来使用。寄存器的寻址方式可以分为直接寻址,寄存器寻址,和内存寻址。例如:movl $3, %eax为直接寻址,movl %eax,%edx为寄存器寻址,movl 8(%eax,%edx,4),%eax为内存寻址。
下面就是介绍x86的指令了。最熟悉的莫过于mov指令了。从8086我们就爱上了它,现在我们把它做点拓展,让它用于32位的寄存器。于是就有了如下几条:普通的有movb,movw,movl, 有符号数据传输指令有movsbw,movsbl,movswl,无符号指令(即高位做了0扩展)有movzbw,movzbl,movzwl。另外2条比较重要的数据指令是pushl和popl,分别是入栈和出栈操作。算术逻辑指令有:leal,inc,dec,neg,not,add,sub,imul,xor,or,and,sal,shl,sar,shr。注意字节数对指令集操作的影响。
控制指令中,比较的2个指令cmp和test,前一个比较大小,后一个判断是否为0,通过该指令的操作,改变CF,ZF,OF,SF的标志位,接着使用访问条件码的指令来判断大小,从而决定程序的走向。跳转指令:jmp。在C中,while,do{}while(),for(),switch等条件循环语句,编译成汇编代码后,均是通过cmp或test,判断不同的条件,从而决定jmp的位置,而且需要注意的是C翻译成汇编代码后,由于编译器的优化,可能产生一定的区别。例如 x = 0,翻译为汇编后应该是xor %eax,%eax,而不是movl $0,%eax,因为当我们看机器码的时候,会发现前一条指令的字节数为3,而后一条指令的字节数为5。
另外一个重要的内容是栈帧结构的变化过程,也就是我们的函数调用过程在栈中是怎么运作的。这里的几条指令有:call,level,ret。
看下面的例子,call指令调用一个函数后,该指令的的后一个地址0x8048553会存入栈中,而main段指令执行结束后,会跳转到该地址,也就是main段最后的ret指令跳转的地址。
通过递归函数我们可以很好的理解栈帧的变化。上图右边是栈帧的结构,具体的递归流程就不说了,找张纸画一下就可以了。
在C语言中,结构体通常的情形是按着4/8字节对齐,究其原因是保证结构体中的成员为X的倍数,保证某些指令要求存储器地址为16的倍数的要求。
bomblab和buflab没有条件做好遗憾,哎,要是在学校就好了。珍惜现在吧,童鞋们!