x86汇编语法基础(gnu格式)
一、寄存器1。1通用寄存器
一个x8664的中央处理单元(CPU)包含一组16个存储64位值的通用寄存器。这些寄存器用来存储整数数据和指针。下图显示了这16个寄存器。它们的名字都以r开头,不过后面还跟着不同命名规则的名字,这是由于指令集历史演化造成的。最初的8086中有8个16位的寄存器,即图中的ax到bp。每个寄存器都有特殊的用途,它们的名字就反映了这些不同的用途。扩展到IA32架构时,这些寄存器也扩展成32位寄存器,标号从eax到ebp。扩招到x8664后,原来的8个寄存器扩展成64位,标号从rax到rbp。除此之外,还增加了8个新的寄存器,它们的标号是按照新的命名规则制定的:从r8到r15。
如上图中嵌套的方框表明的,指令可以对这16个寄存器的低位字节中存放的不同大小的数据进行操作。字节级操作可以访问最低的字节,16位操作可以访问最低的2个字节,32位操作可以访问最低的4个字节,而64位操作可以访问整个寄存器。Tips:当指令以寄存器作为目标时,对于生成小于8字节结果的指令,寄存器中剩下的字节如何处理,有两条规则:
生成1字节和2字节数字的指令会保持剩下的字节不变
生成4字节的指令会把高位4字节置为0。
后面这条规则是作为从IA32到x8664的扩展的一部分而采用的。1。2标志寄存器EFLAFS
EFLAGS标志寄存器包含有状态标志位、控制标志位以及系统标志位,处理器在初始化时将EFLAGS标志寄存器赋值为00000002H。
下图描绘了EFLAGS标志寄存器各位的功能,其中的第1、3、5、15以及2231位保留未使用。由于64位模式不再支持VM和NT标志位,所以处理器不应该再置位这两个标志位。
TIPs:在64位模式中,EFLAGS标志寄存器已从32位扩展为64位,被称作RFLAGS寄存器。其中高32位保留未使用,低32位与EFLAGS相同。
接下来,我们会根据标志位功能将EFLAGS划分位状态标志、方向标志、系统标志和IOPL区域等几部分,并对各部分的标志位功能进行逐一讲解。(请参考Intel官方白皮书Volumn1的3。4。3节)。1。2。1状态标志
EFLAGS标志寄存器的状态标志(位0、2、4、6、7和11)可以反映出汇编指令计算结果的状态,像add、sub、mul、p等汇编指令计算结果的奇偶性、溢出状态、正负值皆可从上述状态找那个反映出来。
下表是这些状态标志的功能描述:
缩写
全称
名称
位置
描述
CF
CarryFlag
进位标志
0hr运算中,当数值的最高位产生了进位或者借位,CF位都会置1,否则为0。它可用于检测无符号整数运算结果是否溢出。也可用于多精度运算中。
PF
ParityFlag
奇偶标志
2hr用于标记结果低8位中1的个数,如果为偶数,PF位为1,否则为0。注意,是最低的那8位,不管操作数是16位,还是32位。奇偶校验经常用于数据传输开始时和结束后的对比,判断传输过程中是否出现错误。
AF
AuxiliaryCarryFlag
辅助进位标志
4hr辅助进位标志,用来记录运算结果低4位的进、借位情况,即若低半字节有进、借位,AF为1,否则为0。
ZF
ZeroFlag
零值标志
6hr若计算结果为0,此标志位置1,否则为0。
SF
SignFlag
符号标志
7hr若运算结果为负,则SF位为1,否则为0。
OF
OverflowFlag
溢出标志
11hr用来标识计算的结果是否超过了数据类型可以表示的范围,若OF为1,表示有溢出,为0则未溢出。专门用于检测有符号整数运算结果是否溢出。
这些标志位可反映出三种数据类型的计算结果:无符号整数、有符号整数和BCD整数(Binarycodeddecimalintegers)。其中CF标志位可反映出无符号整数运算结果的溢出状态;OF标志位可反映出有符号整数(补码表示)运算结果的溢出状态;AF标志位表示BCD整数运算结果的溢出状态;SF标志位反应出有符号整数运算结果的正负值;ZF标志位反映出有符号或无符号整数运算的结果是否为0。
以上这些标志位,只有CF标志位可通过stc、clc和cmc(ComplementCarryFlag,计算原CF位的补码)汇编指令更改位值。它也可借助位操作指令(bt、bts、btr和btc指令)将指定位值复制到CF标志位。而且,CF标志位还可在多倍精度整数计算时,结合adc指令(含进位的加法计算)或sbb指令(含借位的减减法)将进位计算或借位计算扩展到下次计算中。
至于状态跳转指令Jcc、状态字节置位指令SETcc、状态循环指令LOOPcc以及状态移动指令CMOVcc,它们可将一个或多个状态标志位作为判断条件,进程分支跳转、字节置位以及循环计数。1。2。2方向标志
DF方向标志位(DirectionFlag)位于EFLAGS标志寄存器的第10位,它控制着字符串指令(诸如movs、cmps、scas、lods、stos等)的操作方向。置位DF标志位可使字符串指令按从高到低的地址方向(自减)操作数据,复位DF标志位可使字符串指令按从低到高的地址方向(自增)操作数据。汇编指令std和cld可用于置位和复位DF方向标志。1。2。3系统标志和IOPL区域
第8位为TF位,即TrapFlag,意为陷阱标志位。此位若为1,用于让CPU进入单步运行方式,若为0,则为连续工作的方式。平时我们用的debug程序,在单步调试时,原理上就是让TF位为1。
第9位为IF位,即InterruptFlag,意为中断标志位。若IF位为1,表示中断开启,CPU可响应外部可屏蔽中断。若为0,表示中断关闭,CPU不再响应来自CPU外部的可屏蔽中断,但CPU内部的异常还是要响应的。
第1213位为IOPL,即InputOutputPrivilegeLevel,这用在有特权级概念的CPU中。有4个任务特权级,即特权级03,故IOPL要占用2位来表示这4种特权级。
第14位为NT,即NestTask,意为任务嵌套标志位。8088支持多任务,一个任务就是一个进程。当一个任务中又嵌套调用了另一个任务时,此NT位为1,否则为0。
第16位为RF位,即ResumeFlag,意为恢复标志位。该标志位用于程序调试,指示是否接受调试故障,它需要与调试寄存器一起使用。当RF为1时忽略调试故障,为0时接受。
第17位为VM位,即Virtual8086Model,意为虚拟8086模式。
第18位为AC位,即AlignmentCheckAccessControl,意为对齐检查。若AC位为1时,则进行地址对齐检查,位0时不检查。
第19位为VIF位,即VirtualInterruptFlag,意为虚拟终端标志位,虚拟模式下的中断标志。
第20位为VIP位,即VirtualInterruptPendingFlag,意为虚拟中断挂起标志位。在多任务情况下,为操作系统提供的虚拟中断挂起信息,需要与VIF位配合。
第21位为ID位,即IdentificationFlag,意为识别标志位。系统经常要判断CPU型号,若ID位为1,表示当前CPU支持CPUID指令,这样便能获取CPU的型号、厂商信息等;若ID位为0,则表示当前CPU不支持CPUID指令。1。3段寄存器
x8664架构,拥有6个16位段寄存器(CS、DS、SS、ES、FS和GS),用于保存16位段选择子。段选择子是一种特殊的指针,用于标识内存中的段。要访问内存中的特定段,该段的段选择子必须存在于相应的段寄存器中。
在平坦内存模型中,段选择子指向线性地址空间的地址0;在分段内存模型中,每个分段寄存器通常加载有不同的段选择子,以便每个分段寄存器指向线性地址空间内的不同分段。
每一个段寄存器表示三种存储类型之一:代码,数据,栈。
CS寄存器保存了代码段(codesegment)选择子。代码段存储的是需要执行的指令,处理器使用CS寄存器内的代码段选择子和RIPEIP寄存器的内容生成的线性地址来从代码段查询指令。RIPEIP寄存器存储的是下一条要执行的指令在代码段上的偏移。
DS、ES、FS和GS寄存器指向了四个数据段(datasegment)。
SS寄存器包含栈段(stacksegment)的段选择子,其中存储当前正在执行的程序、任务或处理程序的过程堆栈。1。4控制寄存器
目前,Intel处理器共拥有6个控制寄存器(CR0、CR1、CR2、CR3、CR4、CR8),它们有若干个标志位组成,通过这些标志位可以控制处理器的运行模式、开启扩展特性以及记录异常状态等功能。1。5指令指针寄存器
RIPEIP寄存器,即指令指针寄存器,有时称为程序计数器。指令指针(RIPEIP)寄存器包含当前代码段中要执行的下一条指令的偏移量。1。6MSR寄存器组
MSR(ModelSpecificRegister)寄存器组可提供性能监测、运行轨迹跟踪与调试以及其它处理器功能。在使用MSR寄存器组之前,我们应该通过CPUID。01h:EAX〔5〕来检测处理器是否支持MSR寄存器组。处理器可以使用RDMSR和WRMSR对MSR寄存器组进行访问,整个访问过程借助ECX寄存器索引寄存器地址,再由EDX:EAX组成的64位寄存器保持访问值。(在处理器支持64位模式下,RCX、RAX和RDX寄存器的高32位将会被忽略)。二、指令集2。1操作数和指令后缀2。1。1操作数
x86指令可以有0到3个操作数,多个操作数之间以逗号(,)分隔。对于有两个操作数的指令,第一个是源操作数,第二个是目的操作数,指令的执行结果保存到第二个操作数表示的寄存器或内存地址中。
源数据值可以以常数形式给出,或是从寄存器或内存中读取。结果可以存放在寄存器或内存中。因此,操作数被分为三种类型:立即数(immediate),用来表示常数值。立即数通过在整数前加一个来表示。比如,577或0x1F;寄存器(register),它表示某个寄存器的内容,通过在寄存器名声前加上来表示。比如:eax或al;内存引用,它会根据计算出来的地址(通常称为有效地址)访问某个内存位置。内存引用的语法:segment:offset(base,index,scale)。segment可以是x86架构的任意段寄存器。segment是可选的,如果指定的话,后面要跟上冒号(:)来与offset隔离开;如果未指定指定的话,默认为数据段寄存器ds。offset是一个立即数偏移量,是可选的。base表示基址寄存器,可以是16个通用寄存器中的任意一个。index表示变址寄存器,可以是16个通用寄存器中的任意一个。scale表示比例因子,scale会与index相乘再加上base来表示内存地址。比例因子必须是1、2、4、或者8,若果比例因子未指定,默认为1。有效地址被计算为:D〔segment〕offsetR〔base〕R〔index〕scale,其中D〔〕表示对应段寄存器的数据,R〔〕表示通用寄存器里的数据。我们用M〔addr〕来表示内存地址addr。内存地址操作示例:指令说明movlvar,eax把内存地址M〔var〕处的数据传送到eax寄存器movlcs:var,eax把代码段偏移量为var处的内存数据传送到eax寄存器movlvar,eax把立即数var传送到eax寄存器movlvar(esi),eax把内存地址M〔R〔esi〕var〕处的数据传送到eax寄存器movl(ebx,esi,4),eax把内存地址M〔R〔ebx〕R〔esi〕4〕处的数据传送到eax寄存器movlvar(ebx,esi,4),eax把内存地址M〔varR〔ebx〕R〔esi〕4〕处的数据传送到eax寄存器2。1。2指令后缀
由于是从16位体系结构扩展成32位的,Intel用术语字(word)表示16位数据类型。因此,称32位为双字(doublewords),称64位数为四字(quadwords)。标准int值存储为双字(32位)。指针存储为8字节的四字。x8664中,数据类型long实现为64位。x8664指令集同样包括完整的针对字节、字和双字的指令。
下表给出了C语言基本数据类型对应的x8664表示。在64位机器中,指针长8字节。
C声明
Intel数据类型
汇编代码后缀
大小(字节)
char
字节
b
1hrshort
字
w
2hrint
双字
l
4hrlong
四字
q
8hrchar
四字(指针)
q
8hrfloat
单精度
s
4hrdouble
双精度
l
8hr浮点数主要有两种形式:单精度(4字节)值,对应于C语言数据类型float;双精度(8字节)值,对应于C语言数据类型double。
如上表所示,大多数汇编代码指令都有一个字符的后缀,表明操作数的大小。例如:数据传送指令有四个变种:movb(传送字节)、movw(传送字)、movl(传送双字)和movq(传送四字)。注意,虽然汇编代码使用后缀l来表示4字节整数和8字节浮点数,但并不会产生歧义,因为浮点使用的是一组完全不同的指令和寄存器。2。2数据传送指令2。2。1简单传送指令
最简单形式的数据传送指令是MOV类。这些指令把数据从源位置复制到目的位置,不做任何变化。MOV类由四条指令组成:movb、movw、movl和movq。这些指令都执行同样的操作,主要区别在于他们操作的数据大小不同:分别是1、2、4和8字节。下表列出了MOV类指令:
指令
效果
描述
MOVS,D
SD
传送
movb
传送字节
movw
传送字
movl
传送双字
movq
传送四字
movabsqI,R
IR
传送绝对的四字
源操作数指定的值是一个立即数,存储在寄存器或者内存中。目的操作数指定一个位置,可以是寄存器或内存地址。x8664加了一条限制,传送指令的两个操作数不能都指向内存位置。大多数情况下,MOV指令只会更新目的操作数指定的那些寄存器字节或内存位置。唯一的例外是movl指令以寄存器作为目的时,它会把该寄存器的高位4字节设置为0。
movabsq指令是处理64位立即数数据的。常规的movq指令只能以表示为32位补码数字的立即数作为源操作数,然后把这个值符号扩展得到64位的值,放到目的位置。movabsq指令能够以任意64位立即数作为源操作数,并且只能以寄存器作为目的。
代码示例:movl0x4050,eax立即数寄存器,4字节movwbp,sp寄存器寄存器,4字节movb(rbi,rcx),al内存寄存器,1字节movb17,(rsp)立即数内存,1字节movqrax,12(rap)寄存器内存,4字节
理解数据传送如何改变目的寄存器:1movabsq0x0011223344556677,raxrax0x00112233445566772movb1,alrax0x00112233445566FF3movw1,axrax0x001122334455FFFF4movl1,eaxrax0x00000000FFFFFFFF5movq1,raxrax0xFFFFFFFFFFFFFFFF
在这个例子中,第一行的指令把寄存器rax初始化为位模式0011223344556677。剩下的指令源操作数是立即数1。因此movb指令把rax的低位字节设置为FF,而movw指令把低2位字节设置为FFFF,剩下的字节保持不变。movl指令将低4个字节设置为FFFFFFFF,同时把高位4字节设置为00000000。最后movq指令把整个寄存器设置为FFFFFFFFFFFFFFFF。2。2。2扩展传送指令
MOVZ和MOVS是另外两类数据移动指令,在将较小的源值复制到较大的目的时使用。MOVZ类中的指令把目的中剩余的字节填充位0,而MOVS类中的指令通过符号扩展来填充,把源操作数的最高位进行复制。这两类指令分别如下表所示。
零扩展的传送指令:
指令
效果
描述
MOVZS,R
零扩展(S)R
以零扩展进行传送
movzbw
将做了零扩展的字节传送到字
movzbl
将做了零扩展的字节传送到双字
movzbq
将做了零扩展的字节传送到四字
movzwl
将做了零扩展的字传送到双字
movzwq
将做了零扩展的字传送到四字
符号扩展的传送指令:
指令
效果
描述
MOVSS,R
符号扩展(S)R
传送符号扩展的字节
movsbw
将做了符号扩展的字节传送到字
movsbl
将做了符号扩展的字节传送到双字
movsbq
将做了符号扩展的字节传送到四字
movswl
将做了符号扩展的字传送到双字
movswq
将做了符号扩展的字传送到四字
位扩展传送指令:
指令
效果
描述
cbtw
符号扩展(R〔al〕)R〔ax〕
把al符号扩展到ax
cwtl
符号扩展(R〔ax〕)R〔eax〕
把ax符号扩展到eax
cwtd
符号扩展(R〔ax〕)R〔dx〕:R〔ax〕
把ax符号扩展到dx:ax
cltq
符号扩展(R〔eax〕)R〔rax〕
把eax符号扩展到rax
cltd
符号扩展(R〔eax〕)R〔edx〕:R〔eax〕
把eax符号扩展到edx:eax
cqto
符号扩展(R〔rax〕)R〔rdx〕:R〔rax〕
把rax符号扩展为八字
cqtd
符号扩展(R〔rax〕)R〔rdx〕:R〔rax〕
把rax符号扩展为八字
字节传送指令比较:1movabsq0x0011223344556677,raxrax0x00112233445566772movb0xAA,dldl0xAA3movbdl,alrax0x00112233445566AA4movsbqdl,raxrax0xFFFFFFFFFFFFFFAA5movzbqdl,raxrax0x00000000000000AA
代码的头2行将寄存器rax和dl分别初始化为0x0011223344556677和0xAA。剩下的指令都是将rdx的低位字节复制到rax的低位字节。movb指令不改变其它字节。根据源字节的最高位,movsbq指令将其它7个字节设为全1或全0。由于十六进制A表示的二进制值为1010,符号扩展会把高位字节都设置为FF。movzbq指令总是将其它7个字节全都设置为0。2。2。3压入和弹出栈数据
栈相关指令如下表所示:
指令
效果
描述
pushqS
R〔rsp〕8R〔rsp〕;SM〔R〔rsp〕〕
将四字压入栈
popqD
M〔R〔rsp〕〕D;R〔rsp〕8R〔rsp〕
将四字弹出栈
pushq指令的功能是把数据压入到栈上,而popq指令是弹出数据。这些指令都只有一个操作数压入的数据源和弹出的数据目的。
将一个四字值压入栈中,首先要将栈指针减8,然后将值写到新的栈顶地址。因此,指令pushqrbp的行为等价于下面两条指令:subq8,rspmovqrbp,(rsp)
弹出一个四字的操作包括从栈顶位置读出数据,然后将栈指针加8。因此,指令popqrax等价于下面两条指令:movq(rsp),raxaddq8,rsp2。2。4加载有效地址
leaq指令格式如下:
指令
效果
描述
leaqS,D
SD
加载有效地址
加载有效地址(loadeffectiveaddress)指令leaq实际上是movq指令的变形。它的指令形式是从内存读数据到寄存器,但实际上它根本没有引用内存。它的第一个操作数看上去是一个内存引用,但该指令不是从指定的位置读入数据,而是将有效地址写入到目的操作数。例如,如果寄存器rdx的值为x,那么指令leaq7(rdx,dx,4),rax将寄存器rax的值为5x7。2。3算术和逻辑运算指令2。3。1算术运算指令
算术运算指令如下:
指令
效果
描述
inc{bwlq}D
D1D
加1
dec{bwlq}D
D1D
减1
neg{bwlq}D
DD
取负
add{bwlq}S,D
DSD
加
sub{bwlq}S,D
DSD
减
imul{bwlq}S,D
DSD
乘2。3。2逻辑运算指令
逻辑运算指令如下:
指令
效果
描述
not{bwlq}D
DD
逻辑非
or{bwlq}S,D
DSD
逻辑或
and{bwlq}S,D
DSD
逻辑与
xor{bwlq}S,D
DSD
逻辑异或2。3。3移位运算
指令
效果
描述
sal{bwlq}k,D
DkD
左移
shl{bwlq}k,D
DkD
左移(等同于asl)
sar{bwlq}k,D
DAkD
算术右移
shr{bwlq}k,D
DLkD
逻辑右移
移位操作第一个操作数是移位量,第二个操作数是要移位的数。可以进行算术和逻辑移位。移位量可以是一个8位立即数,或者放在单字节寄存器cl中。(这些指令很特别,因为只允许以这个特定的寄存器作为操作数。)原则上来说,1个字节的移位量使得移位量的编码范围可以达到281255。x8664中,移位操作对w位长的数据值进行操作,移位量是由cl寄存器的低m位决定的,这里2mw,高位会被忽略。所以,例如当寄存器cl的十六进制值为0xFF时,指令salb会移7位,salw会移15位,sall会移31位,而salq会移63位。
左移指令有两个名字:sal和shl。两者的效果是一样的,都是将右边填上0。右移指令不同,sar执行算术移位(填上符号位),而shr执行逻辑移位(填上0)。移位操作的目的操作数可以是一个寄存器或者是一个内存位置。
代码示例:salq4,rax把rax里的数据左移4位(乘以16),即raxrax162。3。4特殊的算术操作
imul指令有两种不同的形式。其中一种,如2。3。1节所示,有两个操作数,这种形式的imul指令是一个双操作数乘法指令。但是,x8664还提供了两条不同的单操作数乘法指令。根据操作数大小,这类指令把源操作数与rax对应大小的寄存器内容相乘,结果放入rax对应的寄存器中。而imulq指令,由于两个64位数相乘,结果可能为128位,所以使用了rdx寄存器来保存运算结果的高64位,rax保存运算结果的低64位。
另外,x8664提供的除法指令,也是单操作数的。这类操作,根据操作数大小,以rdx对应的寄存器保存余数,rax对应的寄存器保存商。其中ipb和ip是个例外,它们的余数保存在ah寄存器,商保存在al寄存器。
指令
效果
描述
imulbS
SR〔al〕R〔ax〕
8位有符号乘法
imulwS
SR〔ax〕R〔eax〕
16位有符号乘法
imullS
SR〔eax〕R〔rax〕
32位有符号乘法
imulqS
SR〔rax〕R〔rdx〕:R〔rax〕
64位有符号乘法
mulbS
SR〔al〕R〔ax〕
8位无符号乘法
mulwS
SR〔ax〕R〔eax〕
16位无符号乘法
mullS
SR〔eax〕R〔rax〕
32位无符号乘法
mulqS
SR〔rax〕R〔rdx〕:R〔rax〕
64位无符号乘法
ipbS
R〔ax〕modSR〔ah〕R〔ax〕SR〔al〕
8位有符号除法
ipwS
R〔dx〕:R〔ax〕modSR〔dx〕R〔dx〕:R〔ax〕SR〔ax〕
16位有符号除法
iplS
R〔edx〕:R〔eax〕modSR〔edx〕R〔edx〕:R〔eax〕SR〔eax〕
32位有符号除法
ipqS
R〔rdx〕:R〔rax〕modSR〔rdx〕R〔rdx〕:R〔rax〕SR〔rax〕
64位有符号除法
pbS
R〔ax〕modSR〔ah〕R〔ax〕SR〔al〕
8位无符号除法
pwS
R〔dx〕:R〔ax〕modSR〔dx〕R〔dx〕:R〔ax〕SR〔ax〕
16位无符号除法
plS
R〔edx〕:R〔eax〕modSR〔edx〕R〔edx〕:R〔eax〕SR〔eax〕
32位无符号除法
pqS
R〔rdx〕:R〔rax〕modSR〔rdx〕R〔rdx〕:R〔rax〕SR〔rax〕
64位无符号除法2。4控制指令2。4。1条件码
除了整数寄存器(通用寄存器)外,CPU还维护者一组单个位的条件码寄存器(EFLAGS),它们描述了最近的算术或逻辑运算操作的属性。条件码的详细描述详见1。2节,这里我们列出最常用的条件码:CF:进位标志。最近的操作使最高位产生了进位或借位,用来检查无符号操作的溢出。ZF:零标志。最近的操作得出的结果是0。SF:符号标志。最近的操作的得到的结果是否为负数。OF:溢出标志。最近的操作导致一个补码溢出,用来检查有符号溢出。
比如说,假设我们用一条ADD指令完成等价于C表达式tab的功能,这里变量a、b和t都是整型的。然后,根据下面的C表达式来设置条件码:CF(unsigned)t(unsigned)a无符号溢出ZF(t0)零SF(t0)负数OF(a0b0)(t0!a0)有符号溢出
2。3节列出的所有指令都会设置条件码。对应逻辑操作,如XOR,进位标志和溢出标志会设置成0。对于移位操作,进位标志将设置为最后一个被移出的位,而溢出标志设置为0。INC和DEC指令会设置溢出和零标志,但是不会改变进位标志。
条件码通常不会直接读取,常用的使用方法有三种:可以根据条件码的某种组合,将一个字节设置为0或者1;可以条件跳转到程序的某个地方可以有条件的传送数据2。4。2CMP和TEST指令
除了2。3节列出的指令会设置条件码,还有两类指令(CMP和TEST),它们只设置条件码而不改变任何其他寄存器。CMP指令根据两个操作数之差来设置条件码。除了只设置条件码而不更新目的寄存器之外,CMP指令与SUB指令的行为是一样的。如果两个操作数相等,这些指令会将零标志设置为1,而其他标志可以用来确定两个操作数之间的大小关系。TEST指令的行为与AND指令一样,除了它们只设置条件码而不改变目的寄存器。
指令
基于
描述
CMPS1,S2
S2S1
比较
cmpb
比较字节
cmpw
比较字
cmpl
比较双字
cmpq
比较四字
TESTS1,S2
S1S2
测试
testb
测试字节
testw
测试字
testl
测试双字
testq
测试四字2。4。3SET指令
SET指令根据条件码的某种组合,将一个字节设置为0或者1。SET指令是一组指令,指令的后缀表明了他们所考虑的条件码组合。例如:指令setl和setb表示小于时设置(setless)和低于时设置(setblow)。SET指令的目的操作数是低位单字节寄存器或是一个字节的内存地址,指令会将这个字节设置成0或者1。
SET指令列表如下:
指令
同义指令
设置条件
条件说明
seteD
setz
ZF
相等零(setequal)
setneD
setnz
ZF
不等非零(setnotequal)
setsD
SF
负数
setnsD
SF
非负数
setgD
setnle
(SFOF)ZF
大于(有符号)
setgeD
setnl
(SFOF)
大于等于(有符号)
setlD
setnge
SFOF
小于(有符号小于)
setleD
setng
(SFOF)ZF
小于等于(有符号)
setaD
setnbe
CFZF
超过(无符号)
setaeD
setnb
CF
超过或相等(无符号)
setbD
setnae
CF
低于(无符号)
setbeD
setna
CFZF
低于或相等(无符号)
2。4。4跳转指令
跳转指令分为无条件跳转和有条件跳转。2。4。4。1无条件跳转
无条件跳转指令jmp,可以是直接跳转,即跳转目标是作为指令一部分编码的;也可以是间接跳转,即跳转目标是从寄存器或内存位置中读取的。汇编语言中,直接跳转是给出一个标号作为跳转目标的。间接跳转的写法是后面跟一个操作数指示符。直接跳转和间接跳转示例如下:直接跳转示例movq0,raxjmp。L1直接跳转movq(rax),rdx。L1:popqrdx间接跳转示例jmprax用寄存器rax中的值作为跳转目的jmp(rax)以rax中的值作为读取地址,从内存中读出跳转目标2。4。4。2有条件跳转
下表中所示的跳转指令都是有条件的,它们根据条件码的某种组合,要么跳转,要么不跳转继续执行一条指令。条件跳转只能是直接跳转。
指令
同义指令
跳转条件
描述
jeLabel
jz
ZF
相等零
jneLable
jnz
ZF
不相等非零
jsLabel
SF
负数
jnsLabel
SF
非负数
jgLabel
jnle
(SFOF)ZF
大于(有符号)
jgeLabel
jnl
(SFOF)
大于等于(有符号)
jlLabel
jnge
SFOF
小于(有符号小于)
jleLabel
jng
(SFOF)ZF
小于等于(有符号)
jaLabel
jnbe
CFZF
超过(无符号)
jaeLabel
jnb
CF
超过或相等(无符号)
jbLabel
jnae
CF
低于(无符号)
jbeLabel
jna
CFZF
低于或相等(无符号)2。4。5条件传送指令
指令
同义指令
传送条件
说明
cmove
cmovz
ZF
相等零
cmovne
cmovnz
ZF
不相等非零
cmovs
SF
负数
cmovns
SF
非负数
cmovo
OF
有溢出
cmovno
OF
无溢出
cmovc
CF
有进位
cmovnc
CF
无进位
cmovp
cmovpe
PF
PF1
cmovnp
cmovpo
PF
PF!1
cmovg
cmovnle
(SFOF)ZF
大于(有符号)
cmove
cmovnl
(SFOF)
大于等于(有符号)
cmovl
cmovnge
SFOF
小于(有符号小于)
cmovle
cmovng
(SFOF)ZF
小于等于(有符号)
cmova
cmovnbe
CFZF
超过(无符号)
cmovae
cmovnb
CF
超过或相等(无符号)
cmovb
cmovnae
CF
低于(无符号)
cmovbe
cmovna
CFZF
低于或相等(无符号)2。5过程调用
x8664的过程实现包括一组特殊的指令和一些对机器资源(例如寄存器和程序内存)使用规则的约定。2。5。1运行时栈
x8664的栈向低地址方向增长,而栈指针rsp指向栈顶元素。可以用push和pop相关指令将数据存入栈中或是从栈中取出数据。将栈指针减小一个适当的量可以为数据在栈上分配空间;类似的,可以通过增加栈指针来释放空间。
当过程P调用过程Q时,其栈内结构如下图所示:
当前正在执行的过程的栈帧总是在栈顶。当过程P调用过程Q时,会把返回地址压入栈中,指明当Q返回时,要从P程序的哪个位置继续执行。Q的代码会扩展到当前栈的边界,分配它所需要的栈帧空间。在这个空间中,它可以保存寄存器的值,分配局部变量空间,为它调用的过程设置参数。通过寄存器,过程P可以传递最多6个整数值(包括指针和整数),如果Q需要更过参数时,P可以在调用Q之前在自己的栈帧里存储好这些参数。2。5。2过程调用惯例2。5。2。1参数传递
x8664中,最多允许6个参数通过寄存器来传递,多出的参数需要通过栈来传递,正如2。5。1节描述的那样;传递参数时,参数的顺序与寄存器的关系对应如下:
操作数大小(位)
参数1
参数2
参数3
参数4
参数5
参数6
64hrrdi
rsi
rdx
rcx
r8
r9
32hredi
esi
edx
ecx
r8d
r9d
16hrdi
si
dx
cx
r8w
r9w
8hrdil
sil
dl
cl
r8b
r9b
如果一个函数Q有n(n6)个整数参数,如果过程P调用过程Q,需要把参数16复制到对应的寄存器,把参数7n放到栈上,而参数7位于栈顶。通过栈传递参数时,所有的数据大小都向8的倍数对齐。参数到位以后,程序就可以指向call指令将控制转移到Q了。2。5。2。2返回值
被调用函数返回时,把返回结果放入rax中,供调用函数来获取。2。5。2。3寄存器的其它约定
根据惯例,寄存器的rbx、rbp和r12r15被划分位被调用者保存寄存器。当过程P调用过程Q时,Q必须保证这些寄存器的值在被调用前和返回时是一致的。也就是说,Q要么不去使用它,要么先把寄存器原始值压入栈,在使用完成后,返回到P之前再把这些寄存器的值从栈中恢复。
所有其它寄存器,除了栈指针rsp,都分类为调用者保存寄存器。这就意味着任何函数都能修改它们。可以这样来理解调用者保存这个名字:过程P在某个此类寄存器中存有数据,然后调用过程Q。因为Q可以随意修改这个寄存器,所以在调用之前首先保存好这个数据时P(调用者)的责任。2。5。3控制转移
过程调用时,通过一下指令来进行调用及返回:
指令
描述
callLabel
过程调用
callOperand
过程调用
ret
从过程调用返回
call指令有一个目标,即指明被调用过程起始的指令地址。同跳转指令一样,调用可以是直接的,也可以是间接的。
当call指令执行时,调用者P已经按2。5。2的约定,把被调用者Q所需要的参数准备好了。该指令执行时,会把返回地址A压入栈中,并将PC(rip)设置为Q的起始地址。对应的,ret指令会从栈中弹出返回地址A,并把PC(rip)设置为A,程序从A处继续执行。2。6字符串指令
字符串指令用于对字符串进行操作,这些操作包括在把字符串存入内存、从内存中加载字符串、比较字符串以及扫描字符串以查找子字符串。2。6。1movs、cmps类指令
movs指令用于把字符串从内存中的一个位置拷贝到另一个位置。cmps指令用于字符串比较。
在老的运行模式中,这些指令把字符串从ds:(e)si表示的内存地址拷贝到es:(e)di表示的内存地址。在64位模式中,这些指令把字符串从(re)si表示的内存地址处拷贝到(re)di表示的内存地址处。
当操作完成后,(re)si和(re)di寄存器的值会根据DF标志位的值自动增加或减少。当DF位为0时,(re)si和(re)di寄存器的值会增加,当DF为1时,寄存器的值会减少。根据移动的字符串是字节、字、双字、四字,寄存器会分别减少或增加1、2、4、8。
movs类指令:
指令
描述
movsb
movebytestring
movsw
movewordstring
movsl
movedoublewordstring
movsq
moveqwordstring
cmps类指令:
指令
描述
cmpsb
comparebytestring
cmpsw
comparewordstring
cmpsl
comparedoublewordstring
cmpsq
compareqwordstring2。6。2lods指令
lods指令把源操作数加载到al,ax,eax或rax寄存器,源操作数是一个内存地址。在老的模式下,这个地址会从ds:esi或者ds:si读取(根据操作数地址属性是32还是16来决定使用不同的寄存器);在64位模式下内存地址从寄存器(r)si处读取。
在数据加载完成后,(re)si寄存器会根据DF标志位自动增加或减少(如果DF为0,(re)si寄存器会增加;如果DF为1,(re)si寄存器会减少)。根据移动的字符串是字节、字、双字、四字,寄存器会分别减少或增加1、2、4、8。
指令
描述
说明
lodsb
loadbytestring
Forlegacymode,LoadbyteataddressDS:(E)SIintoAL。For64bitmodeloadbyteataddress(R)SIintoAL。
lodsw
loadwordstring
Forlegacymode,LoadwordataddressDS:(E)SIintoAX。For64bitmodeloadwordataddress(R)SIintoAX。
lodsl
loaddoublewordstring
Forlegacymode,LoaddwordataddressDS:(E)SIintoEAX。For64bitmodeloaddwordataddress(R)SIintoEAX。
lodsq
loadqwordstring
Loadqwordataddress(R)SIintoRAX。2。6。3stos指令
stos指令把al,ax,eax或rax寄存器里的字节、字、双字、四字数据,保存到目的操作数,目的操作数是一个内存地址。在老的模式下,这个地址会从ds:esi或者ds:si读取(根据操作数地址属性是32还是16来决定使用不同的寄存器);在64位模式下内存地址从寄存器rdi或edi处读取。
在数据加载完成后,(re)di寄存器会根据DF标志位自动增加或减少(如果DF为0,寄存器会增加;如果DF为1,寄存器会减少)。根据移动的字符串是字节、字、双字、四字,寄存器会分别减少或增加1、2、4、8。
指令
描述
说明
stosb
storebytestring
Forlegacymode,storeALataddressES:(E)DI;For64bitmodestoreALataddressRDIorEDI。
stosw
storewordstring
Forlegacymode,storeAXataddressES:(E)DI;For64bitmodestoreAXataddressRDIorEDI。
stosl
storedowblewordstring
Forlegacymode,storeEAXataddressES:(E)DI;For64bitmodestoreEAXataddressRDIorEDI。
stosq
storeqwordstring
StoreRAXataddressRDIorEDI。2。6。4REP相关指令
上面几节提到的字符串相关指令,都是单次执行的指令。可以在这些指令前面添加rep类前缀,让指令重复执行。重复执行的次数通过计数寄存器(re)cx来指定,或者根据ZF标志位是否满足条件进行判断。
rep类前缀包括:rep(repeat),repe(repeatwhileqeual),repne(repeatwhilenotqeual),repz(repeatwhilezero)和repnz(repeatwhilenotzero)。rep前缀可以放置在ins,outs,movs,lods和stos指令前面;而repe、repne、repz和repnz前缀可以放置在cmps和scas指令前面。
repe、repne、repz和repnz前缀指令在每一次迭代执行后,会检查ZF标志是否满足中止条件,如果满足则中止循环。
中止条件:
前缀格式
中止条件1
中止条件2
rep
rcx或(e)cx0
无
reperepz
rcx或(e)cx0
ZF0
repnerepnz
rcx或(e)cx0
ZF1三、汇编器指令
这部分指令是跟x86指令集无关的指令,这些指令以。开头。3。1段相关指令
指令
描述
。text
代码段
。rodata
只读数据段
。data
数据段
。bss
未初始化数据段。bss段用于本地通用变量存储。您可以在bss段分配地址空间,但在程序执行之前,您不能指定要加载到其中的数据。当程序开始运行时,bss段的所有内容都被初始化为零。3。2数据相关指令
数据相关指令在。bss段无效。
指令
描述
。ascii
文本字符串,末尾不会自动添加‘‘’字符
。asciz
除了会在字符串末尾自动添加‘’字符外,同。ascii
。byte
8位整数
。short
16位整数
。int
32位整数
。long
32位整数(sameas。int)
。quad
8字节整数
。octa
16字节整数
。single
单精度浮点数
。float
单精度浮点数
double
双精度浮点数
代码示例:。byte74,0112,092,0x4A,0X4a,J,JAllthesamevalue。。asciiRingthebell7Astringconstant。。octa0x123456789abcdef0123456789ABCDEF0Abignum。。float0f31415926535897932384626433832795028841971。693993751E40pi,aflonum。四、参考资料
1、《深入理解计算机系统》(第三版)
2、《操作系统真象还原》
3、《一个64位操作系统的设计与实现》
5、《x86汇编语言:从实模式到保护模式》
4、gnuas文档:https:sourceware。orgbinutilsdocsasindex。htmlSECContents
5、Intel64和IA32架构软件开发者手册:https:www。intel。comcontentwwwusendeveloperarticlestechnicalintelsdm。html
6、x86AssemblyLanguageReferenceManual(ATTsyntax):https:docs。oracle。comcdE192530181754778175477。pdf
沿途风光记录今天一大早,收拾好后,往窗外一看,一辆电动四轮车,停在车库门口。一看七点二十了,赶紧下楼找车主挪车。下了楼,问了一个临边单元的,说不知道,可能外来的。这可咋整啊!赶紧自己挪挪试……
为何拼多多买东西比较便宜?从业人员说出实情,你还会再买吗?估计连马云都想不到,拼多多会发展地如此之快,仅仅几年的时间,就成长为和某宝、某东三雄并立的地步了。拼多多公布的2022年第一季度财报显示,整个第一季度,拼多多的平均月活跃……
0分,0分,0分,0分!NBA历史第一人,哈登果然没有看错你看NBA比赛的球迷,当看到一名球员0分的时候,往往会嗤之以鼻,有的球迷甚至会破口大骂他没用,废物,垃圾。但其实,NBA历史也有一位著名的球星,他就非常不喜欢得分,经常得分为0分……
CMALL商城Java安装与性能分析CMALL商城目录卸载rpmqagrepjdkrpmqagrepjdkxargsrpmenodeps安装usrjavajdk1。8。0191jdk8u191linu……
一款休闲类冒剧情策略游戏HELLO大家好,这里是小白的每日一游推荐时间。世上的游戏千千万,有许多好玩的游戏由于缺乏宣传,所以不被广大玩家所熟知。在这里小白每天会为大家推荐一款评价很高但是不太出名的游戏……
未来若人工智能觉醒,必为图灵作本纪!人工智能之父艾伦麦席森图灵近来大洋彼岸ChatGPT热议话题不断,世人惊呼人工智能进步神速。假如某一天,计算机人工智能意识觉醒自称灵机,且具备复制繁衍行为,凡是人工智能觉……
你过的越好,朋友圈点赞的越少你过的越好,朋友圈点赞的越少如果你还在为过年发了高质量生活的朋友圈,只有零星同事、朋友点赞而神伤,那大可不必,你注意观察,越是平日平平无奇的人,无论发什么,都会获得大批的……
8位定居内地的港台明星,有人购置上亿豪宅,有人只能租房住曾经都是爆红的港台明星,但如今却因为人气的不同,境遇也天差地别。有人可以在北京上海购置上亿豪宅,有人却只能跑到靠近香港的内地二线城市买房,甚至有人只能租房住。同样都……
比亚迪海豹上市,来盘点这辆豹款的黑科技车友们期待已久的新能源轿跑比亚迪海豹终于在5月20日公布了售价,开始上市预售,四款车型分别为:550km标准续航后驱版精英型(21。28万元)550km标准续航后驱……
湖人将用困难特例签下小托马斯据TheAthletic记者ShamsCharania报道,因为队中多名球员触发健康安全协议,湖人计划用10天短合同签下小托马斯,以解燃眉之急。湖人目前有15份保障合同,……
每日优鲜遇发展危机,先烧钱再赚钱已不可持续新京报快评每日优鲜便利购服务。图每日优鲜官网最近有市场传闻称,每日优鲜由于融资未交割成功,无力解决员工薪资欠发、供应商欠款等问题,公司解散。对于这一消息,每日优鲜对媒体回应称……
早餐中的4种致癌物,医生提醒,想要身体健康务必要管住嘴早餐中的4种致癌物,医生提醒,想要身体健康务必要管住嘴,不要不听劝早餐非常重要,正所谓一日之计在于晨,只有早餐吃好,才能更好地享受一天的工作生活。现在工作节奏越来越快,很……