前言
本篇文章介绍x86-64的数据传送指令,数据指令大类上分为三个
- mov
- pushq和popq
其中,mov是比较复杂的指令合集
mov
顾名思义,mov指令是用于数据的传输,数据的传输方式有这么几种:
- 立即数传给寄存器
- 立即数传给内存
- 寄存器传给内存
- 内存传给寄存器
- 寄存器传给寄存器
- 内存传给内存:需要注意的是,x86-64不允许直接从内存传递到内存,这个方式需要两条指令实现,即从
内存--寄存器
,然后寄存器--内存
最基本的指令MOV
MOV指令是最基本的指令,该指令集有五个具体的指令:
- movb:move byte,传送字节
- movw:move word,传送字
- movl:move long word,传送双字
- movq:move quad,传送四字
- movabsq:move abs quad,传送绝对四字
这里需要值得注意和解释
的几点:
- MOV指令的使用方式位
MOV S D
,S是源操作数,D是目的操作数,是从S–>D,这个跟后面介绍的运算操作符是不一样的 - movq当S是立即数的时候,只能使用
32位补码数
(有符号操作数),然后符号位扩展为64位,符号位扩展的意思就是高32位全部用符号位代替,鉴于此,才有了movabsq,movabsq直接能使用64位立即数。 - 无论是S还是D,如果是寄存器,寄存器的名称代表的字节数必须与指令传送的字节数一致,比如movb如果传送数字1给%rax,只能写成
movb $1,%al
- 通常情况下,MOV指令只会更新目的操作数指定的那些寄存器字节或内存位置。唯一的例外是
movl指令以寄存器作为目的时,它会把该寄存器的高位4字节设置为0
以0扩展的指令MOVZ
MOVZ是指数据从小字节传送到大字节时使用的传送指令,Z的是zero的意思
,代表高位全部用0代替。
MOVZ有五个具体的指令,分别为:
- movzbw:1字节byte数据传给2字节word数据
- movzbl:1字节byte数据传给4字节long word数据
- movzwl:2字节word数据传给4字节long word数据
- movzbq:1字节byte数据传给8字节quad word数据
- movzwq:2字节word数据传给8字节quad word数据
这里需要值得注意和解释
的几点:
- 源操作数可以是寄存器或者内存数据,目的操作数只能是寄存器
- 没有
从4字节long word数据传给8字节quad word数据
的指令,因为我们前面讲过movl的时候,如果目标操作数是寄存器,就像movz指令集介绍的那样,寄存器的高位4字节会设置为0,相当于做了0扩展,所以不需要这个指令
以符号扩展的指令MOVS
MOVS是指数据从小字节传送到大字节时使用的传送指令,S的是symbol符号的意思
,代表高位全部用符号位代替。
MOVS有五个具体的指令,分别为:
- movsbw:1字节byte数据传给2字节word数据
- movsbl:1字节byte数据传给4字节long word数据
- movswl:2字节word数据传给4字节long word数据
- movsbq:1字节byte数据传给8字节quad word数据
- movswq:2字节word数据传给8字节quad word数据
- movslq:4字节long word数据传给8字节quad word数据
- cltq:寄存器eax符号位扩展到rax
这里需要值得注意和解释
的几点:
- 源操作数可以是寄存器或者内存数据,目的操作数只能是寄存器
- cltq指令没有操作数,就是将eax寄存器符号位扩展到rax,比如
movl $0xFFFFFFFF , %eax
cltq
第一条指令将rax寄存器设置为00000000FFFFFFFF
第二条指令将rax寄存器设置为FFFFFFFFFFFFFFFF
几个例子
1. movb $OxF, (%ebx)
2. mov1 %rax, (%rsp)
3. movw (%rax) ,4(%rsp)
4. movb %al,%sl
5. movq %rax, $0×123
6. mov1 %eax,%rdx
7. movb %si, 8(rbp)
这上面的每个指令都是错误的:
- 立即数的位数不对
- %rax的字节数和movl不匹配
- 不能从内存传送数据到内存
- 没有sl寄存器,可参考x86-64汇编指令支持的通用目的寄存器
- 目的操作数不能是立即数,只能是寄存器或者内存引用
- 目的操作数rdx和movl不匹配,应该使用edx
- 源操作数si和movb不匹配,用该使用sil
pushq和popq
入栈指令,更新栈指针,并且将一个四字节数据入栈
出栈指令,将一个四字节数据出栈,并且更新栈指针
内存中的程序运行栈
是一种先进后出的数据结构,寄存器rsp指向的就是栈指针
,栈的内存布局有这样的特点,从内存的高地址向低地址排列,也就是栈底在最高位,栈顶在最低位
,所以入栈,rsp的值减小,出栈,rsp的值增大
pushq S
的执行过程是这样的:
subq $8,%rsp
movq S,(%rsp)
popq S
的执行过程是这样的:
movq (%rsp),S
addq $8,%rsp
虽然效果一样,但是pushq和popq指令更简洁