Skip to content

ARM汇编

ARM 汇编语言是一种针对 ARM 处理器架构设计的低级编程语言。它直接操作硬件,因此比高级编程语言(如 C、Python)更高效,但也更复杂。下面是一些 ARM 汇编语言的基本概念和指令介绍。

  1. 寄存器 (Registers):ARM 处理器通常有 16 个通用寄存器(R0-R15),用于存储临时数据、地址和操作数。
  2. 条件码 (Condition Codes):用于决定是否执行某条指令,常见的条件码有 EQ(等于)、NE(不等于)、GT(大于)、LT(小于)等。
  3. 指令格式:ARM 指令通常是固定长度的 32 位指令,分为数据处理、数据传输、控制流等类别。

ARM 处理器启动后执行的第一段代码地址通常由复位向量(reset vector)确定。复位向量是在处理器上电或复位时自动加载到程序计数器(PC)中的地址,它指向启动代码的入口地址。

在 ARMv7 架构中,复位向量通常位于内存地址 0x00000000 或 0xFFFF0000(取决于系统配置)。默认情况下,大多数系统会将复位向量设置为 0x00000000。

在 ARMv8 架构中(也称为 AArch64),复位向量的地址也是由系统设计决定的,但常见的复位向量地址是 0x00000000。在复位后,处理器将从这个地址开始执行第一条指令。

复位向量表是包含各种异常和中断处理程序地址的表,复位向量是其中的一个条目。表中每个条目通常对应一种特定的异常或中断类型。

下面是一个简单的复位向量实现示例,展示复位向量如何跳转到启动代码:

.section .vectors
.global _start
_vectors:
.word _start /* Reset Vector */
.word undefined_handler
.word swi_handler
.word prefetch_abort_handler
.word data_abort_handler
.word unused_handler
.word irq_handler
.word fiq_handler
.section .text
_start:
/* 禁用中断 */
cpsid i
/* 初始化栈指针 */
ldr sp, =stack_top
/* 调用硬件初始化代码 */
bl hardware_init
/* 加载内核映像 */
bl load_kernel
/* 跳转到内核入口点 */
ldr pc, =kernel_entry
hardware_init:
/* 硬件初始化代码 */
bx lr
load_kernel:
/* 加载内核映像到内存 */
bx lr
.section .bss
stack_top:
.word 0x8000 /* 设置栈顶地址 */
.section .vectors
.global _start
_vectors:
.xword _start /* Reset Vector */
.xword undefined_handler
.xword swi_handler
.xword prefetch_abort_handler
.xword data_abort_handler
.xword unused_handler
.xword irq_handler
.xword fiq_handler
.section .text
_start:
/* 禁用中断 */
msr daifset, #3
/* 初始化栈指针 */
ldr x0, =stack_top
mov sp, x0
/* 调用硬件初始化代码 */
bl hardware_init
/* 加载内核映像 */
bl load_kernel
/* 跳转到内核入口点 */
ldr x0, =kernel_entry
br x0
hardware_init:
/* 硬件初始化代码 */
ret
load_kernel:
/* 加载内核映像到内存 */
ret
.section .bss
stack_top:
.xword 0x80000 /* 设置栈顶地址 */

ARM32 架构中的寄存器分为通用寄存器、特殊寄存器和状态寄存器。以下是对这些寄存器的详细介绍:

ARM32 处理器有 16 个通用寄存器(R0-R15),它们可以用于存储数据、地址或中间结果。

  1. R0-R12:这 13 个寄存器是通用的,可以用于任何目的。
  2. R13 (SP):栈指针寄存器,用于指向当前栈的顶部。通常用来管理函数调用的栈帧。
  3. R14 (LR):链接寄存器,存储子程序调用的返回地址。当执行 BL(Branch with Link)指令时,将返回地址存储在 LR 中。
  4. R15 (PC):程序计数器,存储当前正在执行的指令的地址。每执行一条指令,PC 的值会自动递增。

除了通用寄存器,ARM32 还包含几个特殊寄存器:

  • CPSR (Current Program Status Register):当前程序状态寄存器,存储当前程序的状态信息,包括条件标志、中断禁用位、处理器模式等。

以下是一个简单的 ARM 汇编程序,演示如何使用这些寄存器:

.section .data
val1: .word 5 ; 定义第一个数值
val2: .word 3 ; 定义第二个数值
.section .text
.global _start
_start:
ldr r0, =val1 ; 加载val1的地址到r0
ldr r1, [r0] ; 加载val1的值到r1
ldr r0, =val2 ; 加载val2的地址到r0
ldr r2, [r0] ; 加载val2的值到r2
add r3, r1, r2 ; 将r1和r2的值相加,并存储到r3
mov r7, #1 ; 系统调用号1(exit)
swi 0 ; 调用内核

CPSR 包含多个标志位,用于反映指令执行后的结果和当前处理器的状态。常用的标志位包括:

  • N (Negative):当指令的结果为负数时置位。
  • Z (Zero):当指令的结果为零时置位。
  • C (Carry):当指令执行产生进位或借位时置位。
  • V (Overflow):当指令执行产生溢出时置位。

示例:使用条件指令和状态寄存器

Section titled “示例:使用条件指令和状态寄存器”
.section .data
val1: .word 5
val2: .word 3
.section .text
.global _start
_start:
ldr r0, =val1
ldr r1, [r0]
ldr r0, =val2
ldr r2, [r0]
cmp r1, r2 ; 比较r1和r2
beq equal ; 如果相等,跳转到equal标签
bne not_equal ; 如果不等,跳转到not_equal标签
equal:
mov r3, #0 ; 如果相等,设置r3为0
b end ; 跳转到end标签
not_equal:
mov r3, #1 ; 如果不等,设置r3为1
end:
mov r7, #1
swi 0

这个程序比较两个数值,并根据比较结果设置寄存器 R3。条件跳转指令(BEQ 和 BNE)会根据 CPSR 的 Z 标志位决定是否跳转。

将一个值从一个寄存器或立即数加载到目标寄存器中。

mov r0, #1 ; 将常数1加载到寄存器r0
mov r1, r0 ; 将寄存器r0的值加载到寄存器r1

将两个寄存器或一个寄存器和一个立即数的值相加,并将结果存储到目标寄存器中。

add r0, r1, r2 ; 将r1和r2的值相加,并存储到r0
add r0, r1, #10 ; 将r1和10相加,并存储到r0

将一个寄存器的值减去另一个寄存器或立即数的值,并将结果存储到目标寄存器中。

sub r0, r1, r2 ; 将r1的值减去r2的值,并存储到r0
sub r0, r1, #10 ; 将r1的值减去10,并存储到r0

将两个寄存器的值相乘,并将结果存储到目标寄存器中。

mul r0, r1, r2 ; 将r1和r2的值相乘,并存储到r0

从内存中加载数据到寄存器。

ldr r0, [r1] ; 从r1指向的内存地址加载数据到r0
ldr r0, [r1, #4] ; 从r1指向的内存地址偏移4字节处加载数据到r0

将寄存器中的数据存储到内存。

str r0, [r1] ; 将r0的数据存储到r1指向的内存地址
str r0, [r1, #4] ; 将r0的数据存储到r1指向的内存地址偏移4字节处

将两个寄存器的值进行按位与操作,并将结果存储到目标寄存器中。

and r0, r1, r2 ; 将r1和r2的值进行按位与操作,并存储到r0

将两个寄存器的值进行按位或操作,并将结果存储到目标寄存器中。

orr r0, r1, r2 ; 将r1和r2的值进行按位或操作,并存储到r0

将两个寄存器的值进行按位异或操作,并将结果存储到目标寄存器中。

eor r0, r1, r2 ; 将r1和r2的值进行按位异或操作,并存储到r0

将一个寄存器的值与另一个寄存器的按位反值进行按位与操作,并将结果存储到目标寄存器中。

bic r0, r1, r2 ; 将r1和r2按位反值进行按位与操作,并存储到r0

无条件跳转到指定的标签。

b loop ; 跳转到loop标签

跳转到子程序,并将返回地址存储在 LR 寄存器中。

bl subroutine ; 跳转到subroutine子程序

跳转到寄存器地址,通常用于从子程序返回。

bx lr ; 从子程序返回,lr通常存储返回地址

ARM 汇编指令可以根据条件码有选择地执行,条件码包括:

  • EQ:等于(Zero flag 设置)
  • NE:不等于(Zero flag 清除)
  • GT:大于(Signed)
  • LT:小于(Signed)
  • GE:大于等于(Signed)
  • LE:小于等于(Signed)

当条件码为 EQ 时跳转。

beq equal_label ; 当等于时跳转到equal_label

当条件码为 NE 时跳转。

bne not_equal_label ; 当不等于时跳转到not_equal_label

以下是一个简单的 ARM 汇编程序,计算两个数的和,并根据结果设置条件跳转:

.section .data
val1: .word 5
val2: .word 3
.section .text
.global _start
_start:
ldr r0, =val1
ldr r1, [r0]
ldr r0, =val2
ldr r2, [r0]
add r3, r1, r2 ; 将r1和r2的值相加,并存储到r3
cmp r3, #8 ; 比较r3和8
beq equal ; 如果相等,跳转到equal标签
bne not_equal ; 如果不等,跳转到not_equal标签
equal:
mov r4, #1 ; 如果相等,设置r4为1
b end ; 跳转到end标签
not_equal:
mov r4, #0 ; 如果不等,设置r4为0
end:
mov r7, #1 ; 系统调用号1(exit)
swi 0 ; 调用内核

这个程序从内存中加载两个数值,将它们相加,并根据结果设置寄存器 R4 的值。条件跳转指令(BEQ 和 BNE)根据比较结果决定是否跳转。

ARM 架构主要分为以下几种类别,每种类别都有其特定的用途和特点:

ARMv6 架构主要用于嵌入式系统和移动设备,支持高效的指令集和低功耗设计。

ARMv7 是广泛使用的一代架构,包含三个子集:

  1. ARMv7-R
  • 实现具有多种模式的传统 ARM 架构。
  • 支持基于内存管理单元 (MMU) 的虚拟内存系统架构 (VMSA)。ARMv7-A 实现可以称为 VMSAv7 实现。
  • 支持 ARM 和 Thumb 指令集。
  1. ARMv7-R
  • 实现具有多种模式的传统 ARM 架构。
  • 支持基于内存保护单元 (MPU) 的受保护内存系统架构 (PMSA)。ARMv7-R 实现可以称为 PMSAv7 实现。
  • 支持 ARM 和 Thumb 指令集。
  1. ARMv7-M
  • 实现专为低延迟中断处理而设计的程序员模型,具有寄存器的硬件堆栈并支持用高级语言编写中断处理程序。
  • 实现 ARMv7 PMSA 的变体。
  • 支持 Thumb 指令集的变体。

ARMv8 架构引入了 64 位支持,包括以下子集:

  • ARMv8-A:支持 64 位和 32 位指令集,用于高性能计算和服务器。
  • ARMv8-R:用于实时系统,增强了安全性和虚拟化支持。
  • ARMv8-M:用于安全的嵌入式系统,集成了 TrustZone 技术。

ARMv9 是最新的架构版本,着重于性能、安全性和 AI 处理能力的提升,包括以下特点:

  • 增强的安全性:集成了 Confidential Compute 架构(CCA),提供更高级别的数据保护。
  • AI 和机器学习加速:优化了对 AI 和 ML 工作的处理能力。
  • 更高的计算性能:改进了指令集和架构设计,提供更高的计算性能。
  • ARMv6:嵌入式系统,低功耗。
  • ARMv7:高性能应用处理器、实时系统和微控制器。
  • ARMv8:64 位和 32 位支持,高性能计算、安全性和嵌入式系统。
  • ARMv9:增强的安全性、AI 处理能力和计算性能。
  • 13 个 32 位通用寄存器, r0-r12
  • 3 个特殊的 32 位寄存器, SP, LR, PC, 描述为 r13-r15

处理器使用 SP 作为指向活动堆栈的指针。

在 Thumb 指令集中,大多数指令不能访问 SP。唯一可以访问 SP 的指令是那些设计为使用 SP 作为堆栈指针的指令。

ARM 指令集提供了对 SP 更通用的访问,并且它可以用作通用寄存器。但是,ARM 不赞成将 SP 用于除堆栈指针以外的任何用途。

链接寄存器是一个特殊的寄存器,可以保存返回链接信息。某些情况需要使用 LR。当软件不需要 LR 进行链接时,它可以将其用于其他目的。可以将 LR 称为 R14。

当执行 ARM 指令时,PC 读取的是当前指令的地址加 8。

当执行 Thumb 指令时,PC 读取当前指令的地址加 4。

大多数 Thumb 指令无法访问 PC。

ARM 指令集提供了对 PC 更通用的访问,许多 ARM 指令可以将 PC 用作通用寄存器。然而,ARM 不赞成将 PC 用于程序计数器以外的任何用途。