指令集区分

thumb指令集

  • 长度:thumb指令通常是 16 位。
  • 特点:thumb 指令集是为了压缩指令集长度减少程序占用空间。
  • 对齐方式:2字节对齐,存放 thumb 指令的地址一般会被+1,设置为奇数,用于表示地址上存放的是 thumb 指令。

thumb-2指令集

  • 长度:thumb-2 扩展了thumb指令集,可以是16位或 32位。
  • 特点:32 位长的 thumb-2 指令通过特定的编码模式来表示,指令通常具有一些特定前缀,例如:'0b11101''0b11110''0b11111'
  • 对齐方式:2字节对齐,存放 thumb 指令的地址一般会被+1,设置为奇数,用于表示地址上存放的是 thumb 指令。

arm 指令集

  • 长度:arm指令固定为32位。

  • 特点:arm 指令不区分前缀,因为所有arm指令都是32位长,它们是统一的,没有特别的前缀来表示。arm指令集通过处理器的状态决定使用,与thumb指令集通过特定指令(如bx, blx)进行状态切换。

  • 对齐方式:4字节对齐。

Thumb 和 arm 的切换

  • 切换到 arm 模式:在使用 bx 或 blx 是确保地址的最低位为0。
  • 切换到 thumb 模式:在使用 bx 或 blx 是确保地址的最低位为1。

例如:

// 切换到 thumb 模式
add r3, pc, #1
bx r3

  当使用bx跳转指令,跳到一个奇数地址时,实际上是跳到这个 奇数地址 - 1 的位置,然后CPSR寄存器的标志位T位会置1,表示切换到Thumb指令集,执行后面的指令。

函数跳转指令

  ARM 指令集中的跳转指令可以完成从当前指令向前或向后地址空间的跳转,最大 32MB 跳转范围,包括以下 4 条指令:

  • B 跳转指令
  • BL 带返回的跳转指令
  • BLX 带返回的跳转指令,并根据目标地址的最低位切换处理器状态(ARM 或 Thumb(指令+1为奇数表示))
  • BX 不返回跳转指令,并根据目标地址的最低位切换处理器状态(ARM 或 Thumb(指令+1为奇数表示))

B 指令

介绍

  B 指令是最简单的跳转指令,跳转后不在返回。存储在跳转指令中的实际值是相对当前 PC 值的一个偏移量,而不是一个绝对地址,它的值由汇编器来计算(参考寻址方式中的相对寻址)。

格式:

T1 有条件跳转指令:B<c> <label>

比特位15~1211~87~0
指令码1101condimm8

cond :为条件码指令,例如EQ、NE等,可以根据条件决定是否进行跳转
imm8:为8位的立即数(label 偏移量),最大跳转范围 range(-256 ~ 254)
实际偏移量计算:

imm32 = SignExtend(imm8:'0', 32); 

注意:cond 不可为‘1110’ 和‘1111’

适用架构:ARMv4T, ARMv5T*, ARMv6*, ARMv7

T2 无条件跳转指令:B<label>

比特位15~1110~0
指令码11100imm11

imm11:为11位的立即数(label 偏移量),最大跳转范围 range(-2048 ~ 2046)
实际偏移量计算:

imm32 = SignExtend(imm11:'0', 32);

适用架构:ARMv4T, ARMv5T*, ARMv6*, ARMv7

T3 有条件跳转指令:B<c>.W <label>

比特位15~11109~65~015~1413121110~0
指令码11110Scondimm610J10J2imm11

S:符号位
cond :为条件码指令,例如EQ、NE等,可以根据条件决定是否进行跳转
J1/J2:计算参数位
imm6/imm11:立即数,最大跳转范围range(-1048576 ~ 1048574)
实际偏移量计算:

imm32 = SignExtend(S:J2:J1:imm6:imm11:'0', 32); 

适用架构:ARMv6T2, ARMv7

T4 无条件跳转指令:B.W <label>

比特位15~11109~015~1413121110~0
指令码11110Simm1010J11J2imm11

S:符号位
J1/J2:计算参数位
imm10/imm11:立即数,最大跳转范围 range (-16777216 ~ 16777214)
实际偏移量计算

I1 = NOT(J1 EOR S);
I2 = NOT(J2 EOR S);
imm32 = SignExtend(S:I1:I2:imm10:imm11:'0', 32); 

适用架构:ARMv6T2, ARMv7

A1 arm 有条件跳转指令:B<c><label>

比特位31~2827~2423~0
指令码cond1010imm24

cond :为条件码指令,例如EQ、NE等,可以根据条件决定是否进行跳转
imm24:立即数,最大跳转范围 range (-33554432 ~ 33554428)
实际偏移量计算:

imm32 = SignExtend(imm24:'00', 32); 

适用架构:ARMv4*, ARMv5T*, ARMv6*, ARMv7

BX 指令

介绍

  BX 指令中跳转到寄存器所指定的目标地址, 目标地址处的指令既可以是ARM 指令,也可以是Thumb指令。跳转后不在返回,并且根据寄存器中地址的最低位是否为 1 决定使用 Thumb模式还是 arm 模式。

格式

T1 无条件跳转指令:BX<Rm>

比特位15~109~876~32~0
指令码010001110Rm000

Rm:寄存器R1、LR
实际偏移量计算:

m = Uint(Rm); 

适用架构:ARMv4T, ARMv5T*, ARMv6*, ARMv7

A1 arm 有条件跳转指令:BX<c> <Rm>

比特位31~2827~2019~1615~1211~87~43~0
指令码cond000100101111111111110001Rm

cond :为条件码指令,例如EQ、NE等,可以根据条件决定是否进行跳转
Rm:寄存器R1、LR
实际偏移量计算:

m = Uint(Rm); 

适用架构:ARMv4T, ARMv5T*, ARMv6*, ARMv7

BL 指令

介绍

  BL 是一个带返回的跳转指令,跳转之前,会在寄存器 R14 中保存当前指令的下一条指令地址,因此,可以通过将 R14 的内容重新加载到 PC 中,来返回到跳转指令之前的位置继续执行。

格式

T1 无条件跳转指令:BL <label>

比特位15~11109~015~1413121110~0
指令码11110Simm1011J11J2imm11

S:符号位
imm6/imm11:立即数,最大跳转范围 range(-16777216 ~ 16777214)
实际偏移量计算:

I1 = NOT(J1 EOR S); 
I2 = NOT(J2 EOR S); 
imm32 = SignExtend(S:I1:I2:imm10:imm11:'0', 32); 

适用架构:ARMv4T, ARMv5T*, ARMv6*, ARMv7 if J1 == J2 == 1 ARMv6T2, ARMv7 otherwise

A1 arm 有条件跳转指令:BL<c> <label>

比特位31~2827~2423~0
指令码cond1011imm24

cond :为条件码指令,例如EQ、NE等,可以根据条件决定是否进行跳转
imm24:立即数,最大跳转范围 range (-33554432 ~ 33554428)
实际偏移量计算:

imm32 = SignExtend(imm24:'0', 32); 

适用架构:ARMv4*, ARMv5T*, ARMv6*, ARMv7

BLX 指令

介绍

  BLX 指令是一个即带有返回值的跳转指令,同时也能指示在调整之后使用 Thumb 模式还是 arm 模式,在寄存器 R14 中保存当前指令的下一条指令地址,用于返回调用前的位置。

格式

T1 无条件立即数跳转指令:BLX<label>

比特位15~11109~015~1413121110~10
指令码11110Simm10H11J10J2imm10LH

S:符号位
imm10H/imm10L:立即数,最大跳转范围 range(-16777216 ~ 16777212)
H
实际偏移量计算:

if CurrentInstrSet() == InstrSet_ThumbEE || H == ‘1’ then UNDEFINED;
I1 = NOT(J1 EOR S); I2 = NOT(J2 EOR S); 
imm32 = SignExtend(S:I1:I2:imm10H:imm10L:’00’, 32); 
targetInstrSet = InstrSet_ARM; 

适用架构:ARMv5T*, ARMv6*, ARMv7 if J1 == J2 == 1ARMv6T2, ARMv7 otherwise

T2 无条件寄存器跳转指令:BLX<Rm>

比特位15~109~876~32~0
指令码010001111Rm000

cond :为条件码指令,例如EQ、NE等,可以根据条件决定是否进行跳转
Rm:寄存器R1、LR
实际偏移量计算:

m = Uint(Rm); 

适用架构:ARMv5T*, ARMv6*, ARMv7

A1 arm 无条件立即数跳转指令:BLX<label>

比特位31~2827~252423~0
指令码1111101Himm24

H
imm24:立即数,最大跳转范围 range (-33554432 ~ 33554428)
实际偏移量计算:

imm32 = SignExtend(imm24:H:'0', 32); 
targetInstrSet = InstrSet_Thumb; 

适用架构:ARMv5T*, ARMv6*, ARMv7

A2 arm 无条件寄存器跳转指令:BLX<c> <Rm>

比特位31~2827~2019~1615~1211~87~43~0
指令码cond000100101111111111110011Rm

cond :为条件码指令,例如EQ、NE等,可以根据条件决定是否进行跳转
Rm:寄存器R1、LR
实际偏移量计算:

m = Uint(Rm); 

适用架构:ARMv5T*, ARMv6*, ARMv7

栈操作指令

PUSH 指令

介绍

  PUSH 指令一般在函数开始的地方出现,专门用于将寄存器进行压栈操作。在ARM的指令系统中,满栈递减栈入栈操作的参数入栈顺序是从右到左依次入栈,而参数的出栈顺序则是从左到右的逆操作。对于递增栈,相应的操作则全部取反。

格式

T1 多寄存器压栈指令:PUSH<c> <registers>

比特位15~121110~987~0
指令码1011010Mreg_list

M:表示LR寄存器是否存在于 reg_list 列表中
reg_list:待入栈寄存器列表,以 bit 位表示,真正的寄存器列表 registers = '0':M:'000000':reg_list

注:在 thumb1 指令下寄存器过多时可能会使用T1压栈指令进行2次压栈。
适用架构:ARMv4T, ARMv5T*, ARMv6*, ARMv7

T2 多寄存器压栈指令:PUSH<c>.W <registers>

比特位15~1110~98~65~015141312~0
指令码11101001001 0 11010M0reg_list

M:表示LR寄存器
reg_list:待入栈寄存器列表,以 bit 位表示,真正的寄存器列表 registers = '0':M:'0':reg_list

注:reg_list 列表中的寄存器个数小于2个则会出错误。
适用架构:ARMv6T2, ARMv7

T3 单寄存器压栈指令:PUSH<c>.W <register>

比特位15~1110~98~015~1211~0
指令码11111000 0 10 0 1101Rt1 101 00000100

Rt:表示入栈寄存器的值,例如 r3 时 Rt 就是 3
计算方式:

t = UInt(Rt); 			// 转 Rt 转无符号整数
registers = Zeros(16);	// 创建16位寄存器,初始化为0
registers<t> = '1'; 	// 将16位寄存器的第 t 位设置为1

注:t 的值不能为 13 或 15,即不能压栈SP 和 PC 寄存器
适用架构:ARMv6T2, ARMv7

A1 arm 多寄存器压栈指令:PUSH<c> <registers>

比特位31~2827~2019~1615~0
指令码cond100100 1 01101reg_list

cond:条件码,可以根据条件码决定是否压栈
reg_list:待入栈寄存器列表,以 bit 位表示某个寄存器,registers = reg_list

注:如果压栈小于两个寄存器则使用 STMDB / STMFD 指令
适用架构:ARMv4*, ARMv5T*, ARMv6*, ARMv7

A2 arm 单寄存器压栈指令:PUSH<c> <registers>

比特位31~2827~2019~1615~1211~0
指令码cond010 1 0 0 1 01101Rt000000000100

cond:条件码,可以根据条件码决定是否压栈
Rt:需要压栈的寄存器值,r3 就是 3

适用架构:ARMv4*, ARMv5T*, ARMv6*, ARMv7

STMDB (STMFD) 指令

介绍

  STMDB 指令将寄存器的值存储到指定的内存地址,这个内存地址可以是堆栈,也可以是其他内存地址,常用于函数入口处的压栈操作,其中 DB 表示 down before 先递减,然后将寄存器值存入,在ARM的指令系统中,满栈递减栈入栈操作的参数入栈顺序是从右到左依次入栈,而参数的出栈顺序则是从左到右的逆操作。

格式

T1 多寄存器存储指令:STMDB<Rn>{!}, <registers>

比特位15~1110~98~6543~015141312~0
指令码1110100100W0Rn0M0reg_list

W:Writeback 写回标志,表示对 Rn 地址的操作是否影响 Rn 寄存器
Rn:Rn 基地址寄存器,需要操作的寄存器,Rn! 是会自动设置W=1,例如 sp 时Rn=13
M:表示LR寄存器
reg_list:待写入的寄存器列表, registers = '0':M:'0':reg_list

注:W=1且Rn ='1101' t是为PUSH指令
适用架构:ARMv6T2, ARMv7

A1 arm 多寄存器存储指令:STMDB<c> <Rn>{!}, <registers>

比特位31~2827~22212019~1615~0
指令码cond100100W0Rnreg_list

cond:条件码,可以根据条件码决定是否压栈
W:Writeback 写回标志,表示对 Rn 地址的操作是否影响 Rn 寄存器
Rn:Rn 基地址寄存器,需要操作的寄存器,Rn! 是会自动设置W=1,例如 sp 时Rn=13
reg_list:reg_list:待写入的寄存器列表, registers = reg_list

注:W=1且Rn ='1101' t是为PUSH指令
适用架构:ARMv4*, ARMv5T*, ARMv6*, ARMv7

栈空间申请操作

SUB 指令

介绍

  SUB 指令可以用于栈申请。在ARM汇编语言中,可以使用SUB指令来调整堆栈指针的值,从而为栈的使用提供空间。

格式

T1 栈申请指令:SUB SP,SP,#<imm>

比特位15~1211~876~0
指令码101100001imm7

imm7:需要减去的立即数
实际值:

imm32 = ZeroExtend(imm7:'00', 32); 

适用架构:ARMv4T, ARMv5T*, ARMv6*, ARMv7

T2 栈申请指令:SUB{S}.W <Rd>, SP, #<const>

比特位15~11109~543~01514~1211~87~0
指令码11110i0 1101S11010imm3Rdimm8

i:参与立即数计算
S:符号位
Rd:省略时默认为SP寄存器
imm3/imm8:需要减去的立即数组合位
实际值计算:

imm32 = ThumbExpandImm(i:imm3:imm8)

适用架构:ARMv6T2, ARMv7

T3 栈申请指令:SUBW<Rd>, SP, #<imm12>

比特位15~11109~543~01514~1211~87~0
指令码11110i1 0101011010imm3Rdimm8

i:参与立即数计算
Rd:省略时默认为SP寄存器
imm3/imm8:需要减去的立即数组合位
实际值计算:

imm32 = ZeroExtend(i:imm3:imm8, 32)

适用架构:ARMv6T2, ARMv7

A1 arm 栈申请指令:SUB{S}<c> <Rd>, SP, #<const>

比特位31~2827~212019~1615~1211~0
指令码cond00 1 0010S1101Rdimm12

cond:条件码,可以根据条件码决定是否压栈
S:符号位
Rd:省略时默认为SP寄存器
imm12:需要减去的立即数组合位
实际值计算:

imm32 = ARMExpandImm(imm12)

适用架构:ARMv4*, ARMv5T*, ARMv6*, ARMv7

cond 条件码

ABI下入栈规范

APCS 规范

  APCS 规范在ARM架构上定义了程序函数调用和栈帧定义以及寄存器的使用的规范,中定义了 FP 和IP 寄存器的作用,进行子函数调用时会都会进行 push {fp, ip, lr, pc} 压栈操作。

APCSReg意义
fpr11栈帧指针寄存器,指向函数栈开始位置
ipr12临时变量寄存器
spr13栈指针寄存器
lrr14链接寄存器
pcr15程序位置寄存器

AAPCS 规范

  AAPCS 属于型的规范,在AAPCS 规范发布之后 APCS 规范基本不在使用,并且在新规范中精简了压栈的寄存器个数。

AAPCSReg意义
spr13栈指针寄存器
lrr14链接寄存器
pcr15程序位置寄存器

函数调用和异常发生的LR行为

  子函数调用函数时前,CPU会自动更新 LR 的值为当前地址的下一条指令地址,注意:是自动,程序员不用管,相当于跟 BX 自动绑定了。更新的值就是下一步要执行的地址。
  如果是中断调用则会把 LR 的值更新为 EXC_RETURN 的值,出现异常时栈中保存的 PC 是发生异常时执行的指令地址。

参考文档

Armv7-M Architecture Reference Manual

🌀路西法 的CSDN博客拥有更多美文等你来读。

文章作者: 路西法
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 以梦为马
栈回溯 栈回溯
喜欢就支持一下吧