时间轴

2025-09-27

init


参考文档:

ARM64 的汇编器

  • ARM 公司的官方的汇编器
  • GNU AS 汇编器:aarch64-linux-gnu-as
  • gcc 采用 as 作为其汇编器,所以汇编代码是 AT&T 的
    • AT&T:源自贝尔实验室,为开发 UNIX 系统而产生的汇编语法
    • ARM 格式:arm 官方汇编语法

语法

  • label: 任何以冒号结尾的标识符都被认为是一个标号
  • 注释
    • // 表示注释
    • # 在一行的开始,表示注释整行
  • 指令,伪指令,寄存器可以全部都是大写或者小写,GNU 风格默认小写

Symbol

代表它所在的地址,也可以当作变量或者函数来使用

  • 全局 symbol, 可以用.global 来声明
  • 局部 symbol,主要在局部范围内使用,开头以 0-99 直接的数字为标号名,通常和 b 指令结合使用
  • f: 指示编译向前搜索
  • b:指示编译器向后搜索

对齐伪指令

  • .align 对齐,填充数据来实现对齐。可以填充 0 或者使用 nop 指令。
    • 告诉汇编程序,align 后面的汇编必须从下一个能被 2^n 整除的地址开始分配
    • ARM64 系统中,第一个参数表示 2^n 大小

数据定义伪指令

整数与浮点伪指令汇总
指令 数据类型/作用 字节数 补充说明
.byte 定义 8 位整数 1 B 通常用于字符、控制位
.hword 定义 16 位整数(half-word) 2 B 某些架构中也叫.short
.int/.long 定义 32 位整数 4 B .int 是别名,效果一样
.quad 定义 64 位整数(quad-word) 8 B 在 AArch64 中非常常用
.float 定义 IEEE-754 单精度浮点数(32 位) 4 B 等价于 C 语言中的 float
字符串定义伪指令
指令 功能说明
.ascii "str" 将字符串原样插入,不自动添加 \0,适用于非 C 风格字符串
.asciz "str" 在字符串末尾自动追加一个空字符 \0,适用于 C 字符串(推荐)
.rept … .endr:重复块定义

语法:

1
2
3
.rept <count>
<内容>
.endr

作用:重复某段汇编代码或数据定义若干次,适用于初始化数组或填充空间。

例子:

1
2
3
.rept 3
.long 0
.endr

等价于:

1
2
3
.long 0
.long 0
.long 0
.equ / .set:常量定义(赋值操作)

这两个指令完全等价,只是语法风格略不同。
.equ

1
.equ abcd, 0x45

让 abcd 成为 常量宏定义,值为 0x45。
.set

1
.set abcd, 0x45

同样效果,也定义 abcd 为 0x45。

典型用途:用于定义寄存器地址、常量位掩码等。
常见用法示例(结合 .equ 与 .rept):

1
2
3
4
5
6
.equ LED_BASE, 0x3F200000

.section .data
.rept 4
.int LED_BASE
.endr

表示将 LED_BASE 这个地址填充 4 次,每次 4 字节,共 16 字节。

.equ 与 C 的 #define 区别:

.equ / .set #define
汇编阶段赋值,数值不可变 预处理阶段文本替换
可用于表达式(如 .equ val, 4+5 只做文本拼接
不可用于条件编译 可与 #ifdef 等配合使用
函数相关的伪指令
伪操作 作用说明
.global 定义一个全局的符号
.include 引用头文件
.if .else .endif 控制语句结构,用于条件编译
if 语句伪操作
指令 含义说明
.ifdef symbol 判断 symbol 是否已定义
.ifndef symbol 判断 symbol 是否定义
.ifc str1,str2 判断字符串 str1str2 是否相等
.ifeq expr 判断表达式 expr 的值是否为 0
.ifeqs str1,str2 等价于 .ifc str1,str2
.ifge expr 判断表达式 expr 的值是否 ≥ 0
.ifle expr 判断表达式 expr 的值是否 ≤ 0
.ifne expr 判断表达式 expr 的值是否 ≠ 0
与段相关的伪操作
  • .section 表示接下来的汇编会链接到哪个段里,例如代码段,数据段等
  • 每一个段以段名为开始,以下一个段名或者文件尾为结束
1
.section name, "flags"

后面可以添加 flags,表示段的属性

标志 含义说明
a allocatable:该段在运行时需要被加载到内存中。
d GNU_MBIND section:GNU 使用的特殊绑定段。
e excluded:该段不会被包含在可执行文件或共享库中。
w writable:该段可写。
x executable:该段包含可执行代码。
M mergeable:可以和其他具有相同属性的段合并(通常用于只读字符串等)。
S string:该段包含以 0 结尾的字符串。
G group:该段属于某个 section group(如 COMDAT)。
T thread-local-storage:该段用于线程局部存储(TLS)。
? unspecified group:该段属于前一个 section 的 group(如果有的话)。

举例:

1
.section ".idmap.text","awx"

.pushsection <name>
将接下来的代码或数据插入到指定的 section(段)中,同时保存当前 section 状态

.popsection
表示结束前面的 push,并恢复原来的 section

  • 成对使用

  • 作用仅在 pushsectionpopsection 之间的代码,对其他代码没有影响。

  • 其余代码仍然属于原先的段,比如 .text.data

1
2
3
4
5
6
7
8
9
10
    .text
.globl _start
_start:
nop // 在默认的 .text 段中

.pushsection .mydata, "a"
.long 0x12345678 // 被插入到 .mydata 段中
.popsection

nop // 又回到 .text 段

_start 和两个 nop 都属于 .text 段;

.long 0x12345678 被插入到了自定义的 .mydata 段中。

  • .macro 和.endm 组成一个宏
  • .macro 后面跟着的是宏的名称,在后面是宏的参数
  • 在宏里使用参数,需要添加前缀“\”
1
.macro plus1 p, p1

定义了一个名为 plus1 的宏,有两个参数 p 和 p1

在宏里使用参数需要前缀,”\p”表示第一个参数,”\p1”属于第二个参数

  • 宏参数定义的时候可以设置一个初始化值
1
.macro reserve_str p1=0 p2

第一个参数 p1 有一个初始化的值,0。这个时候可以使用 reserve_str a,b 或者 reserve_str, b 来调用这个宏

在宏里使用参数可能出现的问题

解决办法:

  • 使用空格或者使用 altmacro+&

使用空格或者使用altmacro+&

  • 使用”\()“表示连接

使用"()"表示连接

linux内核使用"()"的例子

ARM64 特有的特性

ARM64 编译选项
  • -EB:用于大端模式的 CPU,-EL:用于小端模式的 CPU
  • -mabi:指定 ABI 模式,ilp32 用于 ELF32,lp64 用于 ELF64,默认值为 lp64
  • -mcpu=processor+extension:指定 CPU 型号,例如 cortex-a72
  • -march=,用于指定支持的架构,例如 armv8.2-a
  • ARM64 支持的 extension,见 GNU 汇编器 as_v2.34 9.1.2 章
特殊字符
  • //表示注释
  • #若在一行开始表示注释,不在一行开始也可表示立即数
  • :low12 表示低 12 位
1
2
adrp x0, foo
ldr x0, [x0, #:lo12:foo]
  • ldr 伪操作
  • .bss 切换到 bss 段
  • .dword/.xword 64 位数据
  • name .reg register_name 为寄存器命名
1
foo .req w0