[音乐] 上一讲概要介绍了IA-32指令系统
本讲我们接着介绍IA-32指令系统中常用的 传送指令的功能。
需要说明的是关于 具体机器的内容比较繁琐,因此所讲的内容不需要记忆
只要用到某条指令时会查手册,并理解手册中所描述的内容 即可。
[无声] IA-32当中的指令类型有很多种
最基本的就是传送指令,就是从一个地方传到另外一个地方 传送指令当中最基本的是数据传送指令
数据传送指令一般是用MOV指令来实现的 如果对于等长的一个传送,从源传到目
那么我们就用movb、 movw、 movl指令来实现
比如说我们把一个8位的寄存器传送到另外一个8位寄存器 16位寄存器传送到另外一个16位寄存器,或者16位寄存器内容传送到
一个16位的内存单元里面等等,我们都直接用 MOV指令就行。
在有些情况下,我们可能 会不等长的传送,比如说我们高级语言里面有一条赋值语句,这赋值语句把一个
8位的char型的变量赋值给一个16位的short型的变量
那么这时候它是带符号数的一种扩展 使8位的数据要扩展成16位的数据,那么这个8位数据的高8位
就用char型的这个变量的第一位的符号来进行扩展,8位高8位呢就全是
符号,这样扩展成16位以后,送到变量所在的一个寄存器或者存储单元里面
同样地,把一个short型的数据赋给一个int型的数据的时候
也是一种符号扩展,那么就可以在高16位当中 全部扩展符号位。
有的时候我们可能还会进行无符号数的扩展 比如说一个unsigned short型的数据给一个unsigned
int型的数据 这时候我们要把一个16位的无符号数所在的那个地方的数据,传送给某一个32位的
寄存器或者是32位的内存单元,这时候我们用的是一种零扩展,就是高位呢补零 如果是一个unsigned
char型的转换成一个unsigned int型的,那么就用后面这种 zbl这样的一种指令。
这是MOV指令,那么如果我们直接对
两个数据进行交换,就相当于两条MOV指令实现的功能,我们可以用一条交换指令 来实现。
特殊的对于一种入栈和出栈指令,就是我们将这个
通用寄存器的内容把它送到栈顶所指的位置,或者把栈顶所指的位置的数据
把它送到通用寄存器里面的时候,我们可以用专门的PUSH和POP指令来实现
那么如果是送的16位的,长度后缀呢,是w 如果32位的就是l,8位的就是b
这样的一类指令,实现的是入栈和出栈的功能。
前面讲 的这些都是数据的传送,那么还有一种特殊的指令就是
地址传送,比如说这个LEA这样的指令它是用来
加载有效地址的,L就表示load,是加载的意思,EA呢就是有效地址
比如说这样的一条指令,leal,然后后面这个是这样子的
那么这个指令的功能,实际上它实现的就是把edx的内容
相当于是基址值,加上eax的内容,相当于是变址值
基址加变址,这样两个寄存的内容加起来,这样就是一个有效地址
如果是LEA指令的话,就要把这个有效地址送到这个寄存器里面
那么这种加载有效地址的指令可以实现两个变量的和或者差
比如说我们如果是,这个寄存器的内容是i,这个寄存器的内容是j的话,我们就可以用这条- 指令实现i加j
除了数据和地址传送指令以外呢,还有一类特殊的指令
它可以在输入/输出端口和通用寄存器之间进行数据的交换 那么这是就是特殊的IN指令OUT指令
相关的内容在后续的课程里面我们会讲。
此外呢还有一种 标志传送指令,也就是说我们把标志寄存器,EFLAGS这个寄存器的内容 和栈顶的地方进行交换。
如果PUSHF的话,那么是这样,是把EFLAGS寄存器的内容
压栈,POPF呢就是把栈顶处的内容送到EFLAGS寄存器里面,这是标志传送指令
我们用入栈指令和出栈指令来进行说明机器 里面的执行过程。
这个"栈"这个概念是非常重要的在计算机系统当中
在数据结构里面大家也学过这个"栈"的这个概念,那么栈它是一种先进后出的一块存储区
在机器里面主要用来实现嵌套过程调用,通常栈是从高地址向低地址方向增长的 栈底是在高地址上面。
要注意的"栈"不等于"堆栈",就是我们指的栈实际上是专门
的一种,称为stack,关于栈我们会有很多的内容,对于栈 当中的堆,实际上另外一块存储区,堆和栈
构成"堆栈",堆栈不等于栈。
对于PUSH指令 pushw %ax,这个指令的功能是把ax的内容压栈
假定我们在执行这条指令之前,栈里面的状态是这样的,栈顶指向这个位置 是高地址向低地址长的。
那么这条指令的功能实际上是 先让栈顶,先向下移动两个字节
因为ax里面是16位的,ax 16位的寄存器,所以里面有16位的数据两个字节
栈顶先要向下移两个字节,因此SP的内容先减2
然后让SP指向下面的新的栈顶,然后呢,在新的栈顶处 把ax的内容放进去
因此,执行以后,在栈里面就是这样一种状态
新的栈顶已经指到了这个地方 那么这样的话,AL放在栈顶,AH放在次栈顶
这个地方为什么AL在栈顶而不是AH在栈顶呢?
很显然,我们前面讲过IA-32采用的是一种小端方式
小端方式的话,是把最低有效字节 放在低地址上面,所以AL是
ax当中的最低有效字节,LSB AH是最高有效字节,MSB
所以把AL放在这个数据的低地址上面,这个就是PUSH指令的功能
我们再来看POP指令 POP指令,它就是PUSH指令执行完了以后的这个状态
就栈顶是在这个地方,目前的内容这边有两AL AH在这个里面
执行POP指令以后,实际上它的功能是要把栈顶往上退
并且在退之前,先要把栈顶的两个字节取出来放到ax里面去
它就是把当前栈顶的指向的两个字节送到 ax里面然后修改栈顶的指针,让它加2,因此
执行完了以后,除了ax里面得到的新的栈顶的两个字节以外呢
这个栈指针已经指向了这个地方,退了两个字节
所以原栈顶处的数据呢送到了ax里面,那是出栈指令
我们前面讲过test这样一个例子,add这个函数通过objdump命令可以得到 它的反汇编的结果。
add这个函数我们知道它是 由这么多条指令构成的,在这些指令当中
哪些是传送指令呢?然后这些传送指令它的功能又是
什么呢?我们来看一下,很显然这些用红杠杠出来的都是 刚刚讲过的传送指令。
这些指令的功能我们一个一个来看一下 第一条指令是PUSH指令,刚才我们讲过的。
这条指令的功能 我们用RTL寄存器级的传送语言来描述的话
我们可以写成这样的一种形式,这个是一个PUSH指令,PUSH指令的话我们实际上- 是先要在
栈里面长出一块区域,然后呢再把这个寄存器的内容填到那个区域里面去
而长出一块区域我们知道是用减
减4个字节,因为EBP是一个32位的寄存器,32位寄存器一共有4个字节,所以我们
要让栈顶减4,栈顶的地址减4以后,在栈顶里面就空出4个位置
然后我们把这个空出来的4个位置,用EBP的内容
去填进去,就是PUSH进去,压栈或者叫入栈 这是PUSH指令的功能。
然后第二条指令的功能 功能,很显然这是等长的一种传送指令。
就是32位的寄存器的内容送到32位的寄存器里面,因此用RTL来描述的话是很
简单,就是把esp的内容,这边是源操作数,在esp里面
送到目的位置,就是ebp这个寄存器里面, 所以很简单。
这是mov指令的功能。
然后下面是这条mov指令的功能。
很显然,这条mov指令它不是 寄存器到寄存器的传送,它应该是存储器单元
到寄存器里面的传送。
因为这个地方我们看到它是基址
加位移量的这样的一种寻址方式,所以它是存储器操作数。
它的功能应该是把这个ebp的内容加12,
ebp的内容实际上是一个基址值,ebp呢是一个基址,
基址寄存器,这个基址值加上位移量0xc,
16进制的c也就是十进制的12, 加起来得到的是一个有效地址。
这个地址里边的内容, 也就是存储单元的内容,送到目的地eax这个寄存器里面。
同样地,下面的一条指令也是把某个寄存器单元的内容送到某个寄存器里面。
这是这两条指令。
然后我们来看lea这条指令,显然这条指令我们从
字面上来理解就是装入有效地址, 所以我们把有效地址算出来以后直接把有效地址送到eax里面去 就行了。
因此它的功能就是把有效地址,也就是edx的内容加上eax的内容
乘以1,因为它这个比例变址是1,乘以1以后 还是它,这样的话加起来就是有效地址,送到eax里面去就行了。
然后下面的这条mov指令,它是把 寄存器的内容送到存储单元当中去。
所以 它的功能是把寄存器的内容送到ebp的内容减4,
所得到的那个存储单元里面。
然后下面的一条指令呢再反过来,实际上跟上面的这个指令正好 相反。
我们可以看到,除了操作码的这个方向不一样以外,其它的都是一样的。
下面呢我们来看一看这些指令在机器里面是怎么执行的。
显然我们知道程序是由指令序列构成的, 比如说这是一个add这个函数,这个函数里面包含了这么多条指令,
那么这个函数的首地址是80483d4这个地方,
因此这个函数执行的时候,首先第一条指令的地址送到
EIP里面,这个EIP里面放的是80483d4,就是push指令的地址。
然后这样的这个函数的执行过程,我们前面讲过,它实际上是一条一条指令执行过程。
指令执行完了,这个函数呢也就执行了。
那么,下面我们来看一下每条指令又是怎么执行的呢。
指令的执行我们在第一周的课的时候就讲过,指令的执行跟妈妈做菜一样的。
先要去取菜谱,然后呢再看懂菜谱,看懂了以后就进行操作。
取原材料啊,然后执行炒啊,最后装盘,然后再去得到下一条指令的地址。
就是做菜的时候知道下一个菜谱放哪个地方,周而复始地执行。
这样的一个过程就是指令 取过来并且执行的过程。
具体的在机器里面去执行的时候,我们来看一下整个的过程。
在这个里面,第一个字节最开始都是op字段, 后面呢是一些指令当中的其他字段,
这个op字段进行译码。
[音乐]