ARM64 的汇编器
(1)ARM 公司官方的汇编器;
(2)GNU AS 汇编器:aarch64-linux-gnu-as;
(3)gcc 采用 as 作为汇编器,所以汇编码是 AT&T 格式;
(4)AT&T 格式:源于贝尔实验室;
(5)ARM 格式:ARM 官方汇编语法;
汇编语法
(1)label: 任何以冒号结尾的标识符,都被认为是一个标号;
(2)注释:
“//”,注释
“#”,在一行的开始,注释整行;
(3)指令、伪指令、寄存器,可以全部大写或者小写,GNU 默认风格是小写;
(4)Symbol:代表它所在的地址,也可以当作变量或者函数来使用;
全局 symbol, 可以用.global 声明;
局部 symbol, 局部范围使用,开头以 0 -99 为标号,通常和 b 指令结合使用
f: 告诉编译器向前搜索;
b: 告诉编译器向后搜索;
伪指令
(1).glign 对齐,填充数据实现对齐。ARM64 系统中,第一个参数表示 2^n 大小;
(2)数据定义伪指令
伪指令 | 描述 |
---|---|
.byte | 把 8 位数当作数据插入汇编中 |
.hword | 把 16 位数当作数据插入汇编中 |
.long 和.int | 把 32 位数当作数据插入汇编中 |
.quad | 把 64 位数当作数据插入汇编中 |
.float | 把浮点数当作数据插入汇编中 |
.ascii "string" | 把 string 当作数据插入汇编中,末尾需手动添加 '\0' |
.asciz "string" | 类似.ascii, 在 string 后自动插入一个 '\0' |
.rept | 重复定义 |
.equ | 赋值操作 |
.set | 赋值操作 |
函数相关伪操作
伪指令 | 描述 |
---|---|
.global | 定义一个全局的符号 |
.include | 引用头文件 |
.if, .else, .endif | 控制语句 |
if 语句 | |
.ifdef symbol .ifndef symbol .ifc string1,string2 .ifeq expression .ifeqs string1,string2 .ifge expression .ifle expression .ifle expression<.ifne expression> |
判断 symbol 是否有定义 判断 symbol 是否没定义 判断字符串是否相等 判断 exp 是否为 0 等同于.ifc exp 是否大于等于 0 exp 是否小于等于 0 exp 是否不等于 0 |
与段相关的伪指令
(1).section: 表示接下来的汇编会链接到哪个段里,如代码段、数据段等
每一个段都以段名开始,以下一个段名或文件结尾结束
.section name, "flags"
"flags",表示段的属性,可写,可执行等;
.section ".idmap.txt","awx"
// 表示下面代码是在 ".idmap.txt" 段里,具有可分配,可写和可执行的属性;
(2).pushsection: 把下面代码 push 到指定 section 中;
.popsection: 技术 push,成对使用;
仅仅是把 pushsection/.popsection 之间的代码,加载到指定 section 中,其他不变;
实例 1:使用伪指令,实现一个类似 linux 内核中表的定义;
/*
* as lab1:pesudo code, realize a table
*/
.align 3
.global func_addr
func_addr:
.quad 0x800800
.quad 0x800860
.quad 0x800880
.align 3
.global func_string
func_string:
.asciz "func_a"
.asciz "func_b"
.asciz "func_c"
.align 3
.global func_num_syms
func_num_syms:
.quad 3
extern unsigned long func_addr[];
extern unsigned long func_num_syms;
extern char func_string[];
static int print_func_name(unsigned long addr)
{
int i;
char *p, *string;
for (i = 0; i < func_num_syms; i++) {if (addr == func_addr[i])
goto found;
}
return 0;
found:
p = &func_string;
while (1) {
p++;
if (*p == '\0')
i--;
if (i == 0) {
p++;
string = p;
uart_send_string(string);
break;
}
}
return 0;
}
宏
(1).macro 和.endm 组成一个宏;
(2).macro 后面跟着的依次是宏名称,宏参数;
(3)在宏中使用参数,需要添加前缀 "\";
.macro add a,b // 宏名称 add, 参数 a,b
(4)红参数定义时,可以设置初始值
.macro test p1=0 p2// 可以用 test a,b 或者 test b 来使用
(5)宏可以使用空格
.macro label 1
\1 :
.endm
(6)使用 "()" 表示字符串结束
.macro kernel_ventry, el, label
b el\()\el\()_\label
// 在 arm/arm64/kernel/entry.S 文件,表示 el1_irq
练习 2:
/*
* lab02:macro test
*/
.align 3
.macro add_func add,a,b
mov x0, \a
mov x1, \b
bl add_\()\add
.endm
.align 3
.global add_1
add_1:
add x0,x0,x1
add x0,x0,1
ret
.global add_2
add_2:
add x0,x0,x1
ret
.global macro_test1
macro_test1:
mov x9,x30
add_func 1,x0,x1
mov x30, x9
ret
.global macro_test2
macro_test2:
mov x9,x30
add_func 2,x0,x1
mov x30, x9
ret
print_func_name(0x800880);
unsigned long val1 = 0,val2=0;
val1 = macro_test1(3,5);
val2 = macro_test2(3,5);
ARM64 编译选项
-EB: 用于大端模式的 CPU,-EL 表示小端模式;
-mabi: 指定 ABI 模式,ilp32 表示 elf32, lp6 表示 ELF64, 默认 lp64;
-mcpu=processor+extension: 指定 CPU 型号,比如 cortex-a72;
-march=, 用于指定架构,比如 armv-8.2-a;
yu@sys:~$ aarch64-linux-gnu-as --help
AArch64-specific assembler options:
-mbig-endian assemble for big-endian
-mlittle-endian assemble for little-endian
-mverbose-error output verbose error messages
-mno-verbose-error do not output verbose error messages
-mabi=<abi name> specify for ABI <abi name>
-mcpu=<cpu name> assemble for CPU <cpu name>
-march=<arch name> assemble for architecture <arch name>
-EB assemble code for a big-endian cpu
-EL assemble code for a little-endian cpu
特殊字符
“//”注释
"#": 在一行开头,注释一行;还可以表示立即数;
“#:lo12”: 表示地 12 位;
adrp x0,foo
ldr x0,[x0,#:lo12:foo]
"ldr":伪操作;
ARM64 特有的伪操作;
.bss: 切换到 bss 段;
.dword/.xword:64 位数据;
name .reg register_name: 为寄存器创建别名;
foo .req x0
code