GNU AS
时间轴
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 | .rept <count> |
作用:重复某段汇编代码或数据定义若干次,适用于初始化数组或填充空间。
例子:
1 | .rept 3 |
等价于:
1 | .long 0 |
.equ / .set:常量定义(赋值操作)
这两个指令完全等价,只是语法风格略不同。
.equ
1 | .equ abcd, 0x45 |
让 abcd 成为 常量宏定义,值为 0x45。
.set
1 | .set abcd, 0x45 |
同样效果,也定义 abcd 为 0x45。
典型用途:用于定义寄存器地址、常量位掩码等。
常见用法示例(结合 .equ 与 .rept):
1 | .equ LED_BASE, 0x3F200000 |
表示将 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 |
判断字符串 str1 与 str2 是否相等 |
.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。
成对使用。
作用仅在
pushsection
和popsection
之间的代码,对其他代码没有影响。其余代码仍然属于原先的段,比如
.text
或.data
。
1 | .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+&
- 使用”\()“表示连接
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 | adrp x0, foo |
- ldr 伪操作
- .bss 切换到 bss 段
- .dword/.xword 64 位数据
- name .reg register_name 为寄存器命名
1 | foo .req w0 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 常想一二,不思八九!
评论