常用汇编指令
MOV X1,X0 //将寄存器X0的值传入寄存器X1
ADD X0,X0, X1 //寄存器X0和X1的值相加后传入X0
SUB X0,X0,X1 //相减
AND X0,X0,#0xF //X0的值与0xF相位与后的值传到X0
ORR X0,X0,#0xF //或
EOR X0,X0,#0xF //异或
LDR X1,[sp,#0x8] // 内存->寄存器
LDP x29, x30, [sp, #0x10]
STR X0, [sp, #0x8] //寄存器->内存
STP x29, x30, [sp, #0x10]
CBZ //Compare and Branch Zero,比较为零就跳转
CBNZ //比较非零就跳转
CMP //比较指令,相当于SUBS,影响CPSR
B/BR //B代表跳转到标号的位置,L代表LR寄存器,要返回的地址存放在LR寄存器。
BLR // 跳转到指令后边跟随寄存器中保存的地址
RET //子程序返回指令,返回地址默认保存在LR寄存器
ADRP X10,1 //Address Page,把PC指针的后12位清零,把1左移12位然后两者相加
MOV指令只能用于寄存器之间传值,寄存器和内存间传值通过LDR和STR,数据往高地址读写。ARM64以16字节对齐,入栈、出栈要使用STP/LDP。
ARM指令条件码

ARM指令都是带条件的,默认是AL无条件执行,31-28位是条件码。具体的条件码如下:
| 操作码 | 条件码助记符 | 标志 | 含义 |
|---|---|---|---|
| 0000 | EQ | Z=1 | 相等 |
| 0001 | NE | Z=0 | 不相等 |
| 0010 | CS/HS(Carry Set/High or Same) | C=1 | 无符号数大于或等于 |
| 0011 | CC/LO(Carry Clear/Lower) | C=0 | 无符号数小于 |
| 0100 | MI(Minus) | N=1 | 负数 |
| 0101 | PL(Plus) | N=0 | 正数或零 |
| 0110 | VS(Overflow Set) | V=1 | 溢出 |
| 0111 | VC(Overflow Clear) | V=0 | 没有溢出 |
| 1000 | HI(High) | C=1,Z=0 | 无符号数大于 |
| 1001 | LS(Lower or Same) | C=0,Z=1 | 无符号数小于或等于 |
| 1010 | GE(Greater or Equal) | N=V | 有符号数大于或等于 |
| 1011 | LT(Less Than) | N!=V | 有符号数小于 |
| 1100 | GT(Greater Than) | Z=0,N=V | 有符号数大于 |
| 1101 | LE(Less or Equal) | Z=1,N!=V | 有符号数小于或等于 |
| 1110 | AL | - | 无条件执行(默认) |
| 1111 | NV | - | 从不执行 |
函数
每次函数调用,都为函数开辟一块空间,称为栈帧。栈是从高地址向低地址延伸的,每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。
函数的参数一般从x0~x7寄存器,当参数超过8个时,参数会入栈。返回值存放到x0。- 函数的
局部变量存放在栈里面。
常见的函数调用开辟和恢复的栈空间:
sub sp, sp, #0x40 ; 拉伸0x40(64字节)空间
stp x29, x30, [sp, #0x30] ;x29、x30 寄存器入栈保护
add x29, sp, #0x30 ; x29指向栈帧的底部
...
ldp x29, x30, [sp, #0x30] ;恢复x29、x30 寄存器的值
add sp, sp, #0x40 ; 栈平衡
ret
switch
- switch语句的分支比较少时(小于4个)相当于if-else。
- 各个分支常量的差值较大时,编译器会在效率和内存间进行取舍,编译器还是会编译成类似于if-else的结构。
- 在分支比较多,分支差值也不大时,在编译时会生成一个表,通过表来进行匹配。
汇编代码void testSwitch(int num) { switch (num) { case 0: break; case 1: break; case 2: break; case 40: break; case 45: break; default: break; } } testSwiftch(5);RuntimeLeaning`testSwitch: -> 0x1005be404 <+0>: sub sp, sp, #0x10 ; =0x10 16位对齐,一个int参数,所以拉伸16字节空间 0x1005be408 <+4>: str w0, [sp, #0xc] ; 把w0中的值存在testSwitch的栈中 0x1005be40c <+8>: ldr w8, [sp, #0xc] 0x1005be410 <+12>: subs w8, w8, #0x0 ; num差值 = num-最小case值,影响cpsr 0x1005be414 <+16>: mov x9, x8 0x1005be418 <+20>: ubfx x9, x9, #0, #32 ; 保留X9的0~32位,其他位补0,前面只用了32位,这里为了消除影响,所以把高32位清零。 0x1005be41c <+24>: cmp x9, #0x2d ; 0x2d = 最大case值 - 最小case值,让num差值与它比较就可以知道num是否在最小~最大case间 0x1005be420 <+28>: str x9, [sp] 0x1005be424 <+32>: b.hi 0x1005be454 ; 无符号大于,如果大于说明不在case取值范围,跳转default 0x1005be428 <+36>: adrp x8, 0 0x1005be42c <+40>: add x8, x8, #0x45c ; =0x45c 找到基址 0x1005be430 <+44>: ldr x11, [sp] ; 从sp中取出num差值 0x1005be434 <+48>: ldrsw x10, [x8, x11, lsl #2] ; x11中是num差值,因为是int占4字节,所以要左移2相当于乘4 0x1005be438 <+52>: add x9, x8, x10 ;拿到应该跳转的case地址 0x1005be43c <+56>: br x9 ;跳转x9中地址 0x1005be440 <+60>: b 0x1005be454 ; <+80> at main.m:54:1 0x1005be444 <+64>: b 0x1005be454 ; <+80> at main.m:54:1 0x1005be448 <+68>: b 0x1005be454 ; <+80> at main.m:54:1 0x1005be44c <+72>: b 0x1005be454 ; <+80> at main.m:54:1 0x1005be450 <+76>: b 0x1005be454 ; <+80> at main.m:54:1 0x1005be454 <+80>: add sp, sp, #0x10 ; =0x10 0x1005be458 <+84>: ret
本文探讨了ARM汇编语言中的常见指令和函数。重点介绍了ARM指令的条件码,强调了在传递值时16字节对齐及栈的使用。函数调用时,每个函数都有独立的栈帧,详细阐述了参数如何入栈。同时,讨论了switch语句在不同情况下的编译优化策略,包括编译成if-else结构或生成匹配表的方式。

3432

被折叠的 条评论
为什么被折叠?



