MacOS Arm64 汇编 part 3 - MacOS Arm64 Assembly part 3

  • manipulate memory
  • function & stack

Defining Memory Contents

Basics

我们在 .data 段中编写内存内容,主要有以下表格描述的内容类型描述符,同时可以使用 Negative(-) 取相反数,以及 Complement(~) 取反两个整型数前缀

a sample
1
2
3
4
5
label: .byte 74, 0112, 0b00101010, 0x4A, 0X4a,
'J', 'H' + 2
.word 0x1234ABCD, -1434
.quad 0x123456789ABCDEF0
.ascii "Hello World\n"

其中 label 是这一段内存的标识符,后面的存储内容是连续的,通过逗号分隔同类型的数据。通过内容类型描述符指示不同类型的数据

Directive Description
.ascii 用双引号括起来的字符串(不自动添加结束符)
.asciz 以 0 字节结尾的 ASCII 字符串(自动在末尾添加 \0 终止符)
.byte 1 字节整数(8 位有符号/无符号整数)
.double 双精度浮点值(64 位 IEEE 754 浮点数)
.float 单精度浮点值(32 位 IEEE 754 浮点数)
.octa 16 字节整数(128 位整数)
.quad 8 字节整数(64 位有符号/无符号整数)
.short 2 字节整数(16 位有符号/无符号整数)
.word 4 字节整数(32 位有符号/无符号整数)

Fill

当需要一块连续且具有一定规律的内存片段时,可以采用 fill

1
lable: .fill repeat, size, value

这将重复填充大小为 size(Byte) 值为 value 的块 repeat

Repeat

通过这一语法可以重复其之间的语句指定次数,即一个复杂的重复模式

1
2
3
label:  .rept count
...
.endr

这将填充 count

Aligning Data

为了内存访问效率,因为 CPU 是按块读取内存的,而为了简化设计,块号都是呈 2 的倍数的,因此当我们在创建连续的内存片段时,需要进行对齐,这与 C/C++ 当中的结构体对齐逻辑一致

1
.align 4

上面的代码即做了按 4 字节对齐

Loading a Register with an Address

我们可以通过 ldr 指令加载一个 64 bits 值到指定寄存器(PC 相对寻址)

包括下面的说法,这一节中的 ldr 的实际指令是基于 PC 相对寻址模式,与手写的理解有所不同,在下一节我们会介绍其的非相对寻址模式

1
ldr xd, =imm64

这是一条伪指令,实质为

1
2
3
ldr xd, #offset
...
.quad imm64

offset 是相对于该 ldr 指令到这一立即数存储位置的偏移量

通过这一方法可以加载 PC 附近约 1MB 范围的内存,但加载的值可以超出指令长度,这既是这一指令的意义,对于更远的数据,以及带标签数据,在 OSX 中我们实际上使用如下方法在 xd 中加载其地址

1
2
adrp xd, label@PAGE
add xd, xd, label@PAGEOFF

这里是采用分页的相对寻址(仍基于 PC),第一条 adrp 首先加载页基址,随后 add 加载偏移量,之所以分两条还是因为指令长度有限

Loading Data from Memory

在取得地址后,我们读取一个指定地址的数据我们仍采用 ldr 指令

1
ldr{type} xd, [xs{, #offset}]

type 见下表

Type Meaning
B 无符号字节(Unsigned byte)
SB 有符号字节(Signed byte)
H 无符号半字(16 位,Unsigned halfword (16 bits))
SH 有符号半字(16 位,Signed halfword (16 bits))
SW 有符号字(Signed word)

均为自低位起,#offset 可选
[] 表示了这条指令的间接寻址模式,可以理解为 *xs
#offset 是自 xs 所存地址起的偏移量,可以理解为 *(xs + offset)