本文声明:内容来源于网络,进行整合/再创作;部分内容由AI辅助生成。
Load/Store内存访问指令用于在ARM寄存器 和存储器之间传输数据。
除了某些特定的Load/Store指令(如独占访问或未对齐访问指令)外,大多数普通内存访问地址可以具有任意对齐方式,除非启用了严格的对齐检查(SCTLR.A == 1即对齐检查使能位置1,AArch64通常启用硬件强制检查)。AArch64下,当使用堆栈指针SP作为基址寄存器时,SP在任何操作(如进行偏移)之前必须保持16字节(四字)对齐,否则将产生SP对齐异常。
*而在AArch32下,SP必须至少保持4字节对齐。当函数作为公共接口(即与其他编译单元交互的函数)时,SP必须是**8字节(双字)*对齐 。这相当于指针大小(4字节)的两倍,这个要求是为了满足某些硬件访问或性能优化的需要。
AArch64中Load/Store指令集总览

上述Load/Store指令的分类中,初学者掌握单寄存器的Load/Store指令(对齐)即可,笔者仅讲述"单寄存器、内存访问地址对齐"这类指令,其余感兴趣的可自行查阅。
单寄存器的Load/Store指令(对齐)支持的寻址模式如下:
①基址加立即数偏移(对齐12-bit无符号数,未对齐9-bit有符号数)。
②基址加64-bit寄存器偏移(可选扩展)。
③基址加32-bit扩展寄存器偏移(可选扩展)。
④通过立即数偏移的前索引(9-bit有符号未对齐)。
⑤通过立即数偏移的后索引(9-bit有符号未对齐)。
⑥PC相对寻址,根据Load指令的不同地址的偏移不同。
注意,Load/Store指令中"基址寄存器 = 回写寄存器"是ARM架构禁止的用法,会导致指令行为不可控、数据/寄存器状态不可预测,属于必须避免的编码错误。
在ARM架构中,当Load/Store指令的回写寄存器与基址寄存器是同一个寄存器时,属于指令集架构(ISA)未定义/未分配的非法行为,会产生不可预测、无统一标准的执行结果。这在计算机体系结构中被称为 "寻址冒险" 或"基址寄存器冲突"。由于硬件设计的复杂性(如流水线执行顺序),处理器无法同时对同一寄存器完成"地址计算 / 更新" 和 "数据读写"操作,从而导致未定义的行为。
- 对于Load 指令:同一寄存器既作基址又做回写寄存器时,可能:指令未定义、当作空操作(NOP),或执行加载但基址寄存器值变为未知 ,若执行中触发异常,基址还可能被破坏,导致指令无法重新执行。
- 对于Store 指令:同一寄存器既作基址又做回写寄存器时,可能:指令未定义、当作空操作(NOP),或正常计算地址并执行存储,但最终存入内存的值是不确定的。
LDR 指令
LDR(Load Register)指令用于将内存中的数据读到目标寄存器中。
指令举例:
LDR X0, =0x20008000 ; 将地址0x20008000加载到X0寄存器
LDR X1, [X0] ; 将X0地址处的64-bit数据读到X1寄存器中
LDR X2, [X0, #4] ; 将X0+4地址处的64-bit数据读到X2寄存器中
LDR X3, [X0], #4 ; 将X0地址处的64-bit数据读到X3寄存器中,同时更新X0=X0+4(后索引)
LDR X4, [X0, #4]! ; 将X0+4地址处的64-bit数据读到X4寄存器中,同时更新X0=X0+4(前索引)
LDR W1, [X0] ; 将X0地址处的32-bit数据读到W1寄存器中
LDR W2, [X0, #4] ; 将X0+4地址处的32-bit数据读到W2寄存器中
LDR W3, [X0], #4 ; 将X0地址处的32-bit数据读到W3寄存器中,同时更新X0=X0+4(后索引)
LDR W4, [X0, #4]! ; 将X0+4地址处的32-bit数据读到W4寄存器中,同时更新X0=X0+4(前索引)
STR 指令
STR(Store Register)指令用于将数据写入指定的内存单元。
指令举例:
STR X1, [X0] ; 将X1寄存器中的数据写到X0地址中
STR X2, [X0, #8] ; 将X2寄存器中的数据写到X0+8地址中
STR X3, [X0], #8 ; 将X3寄存器中的数据写到X0地址中,同时更新X0的地址X0=X0+8
STR X4, [X0, #8]! ; 将X4寄存器中的数据写到X0+8地址中,同时更新X0的地址X0=X0+8
STR W1, [X0] ; 将W1寄存器中的数据写到X0地址中
STR W2, [X0, #4] ; 将W2寄存器中的数据写到X0+4地址中
STR W3, [X0], #4 ; 将W3寄存器中的数据写到X0地址中,同时更新X0的地址X0=X0+4
STR W4, [X0, #4]! ; 将W4寄存器中的数据写到X0+4地址中,同时更新X0的地址X0=X0+4
注意:
- LDR/STR是对目标寄存器的"字 "(32位,Wn)与"双字"(64位,Xn)进行读写操作。
- LDRB/STRB是对"字节"(8位)进行读写操作的指令,跟LDR/STR指令用法一致。
- LDRH/STRH是对"半字"(16位)进行读写操作的指令,跟LDR/STR指令用法一致。
- "!"的作用是根据偏移量更新基地址。
- 由于是在AArch64执行状态下,上述指令举例中操作数2必须是一个64位的整数或SP寄存器,即必须使用64位寄存器存放内存地址。
跳转指令
跳转指令是改变指令执行顺序的标准方式。ARM一般按照字地址顺序执行指令,需要时使用条件执行跳过某段指令。一旦程序必须偏离顺序执行时,就要使用控制流指令来修改程序计数寄存器(PC)。尽管在特定情况下还有其他几种方式实现这个目的,但跳转指令是标准的方式。
跳转指令可以改变程序的执行流程,或者调用子程序。这种指令使得一个程序可以调用子程序 、if-then-else结构 、循环 。执行流程的改变迫使程序计数寄存器PC指向一个新的地址。
B.cond 条件分支指令
AArch64中,除非特殊说明,条件分支相对于程序计数寄存器PC位置偏移±1MB。
B.cond(Conditional Branch)条件分支指令是根据条件码判断对应的PSTATE.N/Z/C/V条件标志位,决定是否跳转到对应的分支执行。
-
指令格式:
B.cond label(cond是条件码 EQ/NE/GT/LT等;label是汇编语言的符号标签,最终会被汇编器转换为相对于PC的偏移值。) -
指令含义:分支:如果条件成立,则有条件地跳转到对应的程序标签处。
; 示例:比较两个数,相等则跳转到equal_label
MOV X0, #10 ; X0 = 10
MOV X1, #10 ; X1 = 10
CMP X0, X1 ; 比较X0和X1,设置PSTATE的Z标志(Z=1)
B.EQ equal_label ; 条件分支:Z=1时跳转到equal_label
MOV X2, #0 ; 不相等时执行:X2=0
B end_label ; 无条件跳转到end_labelequal_label:
MOV X2, #1 ; 相等时执行:X2=1end_label:
; 后续逻辑
无条件分支指令:跳转指令B、带链接跳转指令BL
跳转指令B使程序跳转到指定的地址运行程序。
带连接的跳转指令BL将下一条指令的地址 复制到X30(即返回地址连接寄存器LR)寄存器中,然后跳转到指定地址运行程序。BL指令常用于实现子程序调用,子程序的返回可以通过将LR寄存器的值复制到PC寄存器实现。
需要注意的是,这两条指令和目标地址处的指令都要属于ARM指令集。两条指令都可以根据PSTATE.NZCV中的条件标志位的值决定指令是否执行。
指令举例:
.text
.global _start
_start:
; 演示B指令
MOV X0, #10 ; X0 = 10 (初始化测试值)
MOV X1, #20 ; X1 = 20
B b_target ; 跳转,跳过MOV X1, #30
MOV X1, #30 ; 不会执行
b_target:
; 此时X1仍为20(可通过调试器验证)
; 演示BL指令
MOV X2, #100 ; X2 = 100
BL bl_func ; 跳转,自动保存返回地址(下一条指令地址)到LR
; 执行完bl_func后,回到这里,X2变为110
; 退出程序
MOV X0, #0 ; 退出码0(正常退出)
MOV X8, #93 ; exit系统调用号(AArch64)
SVC #0 ; 触发系统调用
bl_func:
ADD X2, X2, #10 ; X2 = X2 + 10
RET ; 跳回LR指向的地址,即返回(等价于B LR)
程序状态寄存器访问指令(了解)
A32指令集中,程序状态寄存器访问指令用于操作CPSR(当前程序状态寄存器)。
- MRS:将CPSR中的数据读 → 通用寄存器
-
MSR:将通用寄存器中的数据写 → CPSR
指令举例:
MRS X0, CPSR ; 把CPSR读到X0
MSR CPSR_c, X0 ; 写控制位
用途:开关中断、切换 CPU 模式、读标志位。
异常产生指令(了解)
A64指令集中提供异常产生的指令,通过这些指令可以用软件的方法实现异常。

ARM 伪指令
ARM汇编器支持ARM伪指令,这些伪指令在汇编阶段被编译成ARM / Thumb / Thumb-2指令(或指令序列)。也就是说,伪指令是在汇编阶段中编译器对其进行了替换的指令。
- 伪指令具有两个特征:是一条指令、没有指令代码。
- 伪指令的作用:进行程序定位、定义非指令代码、为体现程序完整性做标注、有条件地引导程序段。
伪指令指导汇编器进行汇编工作,不形成机器码指令;而指令要形成机器码指令。
ADR/ADRL 伪指令(加载小范围地址到寄存器)
ADR伪指令的功能是把标签所在的(小范围)地址加载到寄存器中。这个指令将基于PC或寄存器相对偏移的地址值读取到寄存器中。
当地址值是字节对齐时,取值范围为-255~255B;当地址值是字对齐时,取值范围为-1020~1020B。当地址值是16字节对齐时,其取值范围更大。
-
指令的语法格式:
ADR <register>, <label>(register:要装载的寄存器编号;label:基于PC或具体寄存器的表达式)指令举例:
Start:
MOV X0, #10 ; PC的值是当前指令地址+8
ADR X4, Start
ADRL伪指令则用于加载较大范围地址。
LDR 伪指令(加载大立即数)
MOV指令实际上不能加载太大的数,因而要用到LDR伪指令,它用于装载一个64位常数或一个地址到寄存器中。
- 指令的语法格式:
LDR <register>, =expr(register:目标寄存器;expr:64位常量表达式)
汇编器处理 LDR 伪指令的逻辑:
若 expr 的值符合 MOV/MVN 指令的立即数规则,汇编器会直接用 MOV/MVN 指令替代 LDR 伪指令。
若 expr 的值不满足 MOV/MVN 立即数要求,汇编器会先将该值存入内存,再通过真实的 LDR 内存读取指令把常数加载到寄存器中。
指令举例:
LDR X0, =0x12345678 ; 把32位立即数加载到X0