这篇文章内容主要是了解汇编语言,以及它是怎么工作的。以下所介绍的内容均为在x86架构下,x64做了很多64位的扩展。重在理解实现方式及原理。:)
程序编码
寄存器分类
- 程序计数器:用%eip表示,指示将要执行的下一条指令在存储器中的地址。
- 整数寄存器:用以存储地址或整数数据。
- 条件码寄存器:保存着最近执行的算数或逻辑指令的状态信息。
- 浮点寄存器:存放浮点数据。
整数寄存器
机器级代码
我们以一个例子来展示下机器代码如何解读:55 89 e5 8b 45 0c 03 45 08 01 05 18 a0 04 08 5d c3
上述字节序列,反汇编之后展示如下:
Offset为机器码指令的地址,Bytes为指令值。
注意观察第6行,01 05为add %eax指令,后面4个字节为18 a0 04 08,对应汇编地址0x804a018,因为是小端法字节排列。
访问信息
操作数指示符
操作数被分为三种类型:- 立即数:即常数值,书写方式为’$’后跟一个整数。
- 寄存器:表示某个寄存器的内容。
- 存储器:根据计算出来的地址,访问某个存储器。
数据传送指令
MOV类中的指令,将源操作数复制到目的操作数中。源操作数指定的值是一个立即数,存储在寄存器中或存储器中。目的操作数指定一个位置,寄存器或者存储器地址。MOVS和MOVZ指令类都是将一个较小的源数据复制到一个较大的数据位置,高位用符号扩展(MOVS)或者零扩展(MOVZ),示例如下:
通过push操作把数据压入栈中,通过pop操作删除数据。栈指针%esp保存着栈顶元素的地址。
算数和逻辑操作
加载有效地址
leal指令是movl指令的变形,它的指令形式是从存储器读取到寄存器,但是实际上它并没有引用存储器,只是将其有效地址写入目的操作数。另外,它还可以用来描述算术操作,如:leal (%eax,%eax,2), %eax //若%eax中值为x,则此表达式为x*3 movl (%eax,%eax,2), %eax //将存储器中地址为3x的值加载到%eax中
对比leal与movl,leal就是少了M[],不用再去存储器中找地址对应的值。
一元操作和二元操作
对于一元操作,只有一个操作数,既是源又是目的。这个操作数可以是寄存器或存储器位置。类似于(+ +)(- -)。
对于二元操作,第二个操作数既是源又是目的。第一个操作数可以是立即数、寄存器或存储器位置,第二个操作数可以是寄存器或存储器位置。移位操作
第一项k为移位量,第二项为需要操作的源及目的。移位量用单个字节编码,因为只允许0~31位的移位(针对X86)。移位量可以是立即数,或者放在单字节寄存器%cl中。特殊的算数操作
这里以乘法为例,两条乘法指令都要求一个参数必须在寄存器%eax中,而另外一个作为指令的源操作数给出,然后乘积存放在寄存器%edx(高32位)和%eax(低32位)中。mull为无符号乘法,imull为补码乘法。