常见的汇编命令记录

作者 by adtxl / 2022-09-01 / 暂无评论 / 149 个足迹

在kernel开发以及debug过程中,难免接触到汇编命令,这里仅记录一些常用的,基于arm64的汇编命令。

1、A64常用寄存器

x0-x30 64bit 通用寄存器,有只用低 32bit的w0-w30

FP(x29) 64bit 栈底指针

LR(x30) 64bit 通常称x30为程序链接寄存器,保存跳转返回信息地址

XZR 64bit Zero寄存器,写入此寄存器的数据被忽略,读出的数据全为0

WZR 32bit Zero寄存器的32bit形式

ELR_ELx 64bit 异常链接寄存器,保存异常进入ELx的异常地址(x={1,2,3})

SP_ELx 64bit 栈指针, 保存进入ELx的栈地址(x={0,1,2,3})

PC 64bit 当前程序运行的指针

SPSR_ELx 32bit 保存的处理状态寄存器(x={1,2,3}),用于存放程序运行中一些状态标识。

状态寄存器又分为 The Current Program Status Register (CPSR) 和The Saved Program Status Registers (SPSRs)。 一般都是使用CPSR,当发生异常时, CPSR会存入SPSR。当异常恢复,再拷贝回CPSR。

这里的ELx (x={0,1,2,3}) 是异常等级(exception level),每个程序能跑到不同级别,最低的是EL0最高的是EL3,这里不具体展开说明。

x0-x7: 用于子程序调用时的参数传递,x0还用于返回值传递。

2. 常用汇编命令

MOV x1,x0; 将寄存器x0的值传送到寄存器x1

ADD x0,x1,x2; 寄存器x1和x2的值相加后传送到x0

SUB x0,x1,x2; 寄存器x1和x2的值相减后传送到x0

AND x0,x0,#0xF; x0的值与0xF相位与后的值传送到x0

ORR x0,x0,#9; x0的值与9相位或后的值传送到x0

EOR x0,x0,#0xF; x0的值与0xF相异或后的值传送到x0

LDR x5,[x6,#0x8]; x6寄存器加0x8的和值传送到x5

STR x0, [SP, #0x8]; x0寄存器的数据传送到SP+0x8地址值指向的存储空间

STP x29, x30, [sp, #0x10]; 入栈指令

LDP x29, x30, [sp, #0x10]; 出栈指令

CBZ x19, 0x10; 比较,如果结果为零(Zero)就转移(只能跳到后面的指令)

CBNZ x19, 0x10; 比较,如果结果非零(Non Zero)就转移(只能跳到后面的指令)

B/BL ; 绝对跳转,返回地址保存到LR(x30)

b ffff000008283b80

bl ffff000008dc566c

其中 MOV 指令只能用于寄存器之间传值,寄存器和内存之间传值通过 LDR 和 STR.

A64指令又一个重要特点就不是所有指令都是带有条件的,就是说汇编中部分指令(跳转指令为主)需要根据状态寄存器中的一些状态来控制分支的执行。例如:

`b.cc ffff000008283dac <show_slab_objects+0x94>

ARM手册里面各个状态指令和代表的含义:

image.png

ARM手册里面各个状态指令和代表的含义

3、举例说明

通常出问题的时候是需要将ko或者整个内核反汇编出来的,下面以ko为例,如何反汇编。这里的-S 如果有符号信息,会把代码与汇编放一起,如果没有符号信息,就只有反汇编内容。

aarch64-linux-gnu-objdump -S reg_hal.ko > test.txt

以一段非常简单的代码为例

int example_test(int a, u32 *b)
{
    if(a > 0)
       *b = 1;
    else
       *b = 16;
    return 0;
}

对应的反汇编如下,我们一句一句翻译一下,大概就能看明白了。

000000000003a150 <example_test>:
 3a150: a9be7bfd  stp x29, x30, [sp,#-32]! //把x29 x30 的值存到sp-32的地址后,sp=sp-32
 3a154: 910003fd  mov x29, sp              //把 sp的值放在x29中
 3a158: a90153f3  stp x19, x20, [sp,#16]   //把x19 x20 的值存到sp+16的地址中后sp=sp+16
 3a15c: 2a0003f3  mov w19, w0              //把w0 的值存到w19 中
 3a160: aa1e03e0  mov x0, x30              //把x30 的值放在x0
 3a164: aa0103f4  mov x20, x1              //把x1的值放在x20
 3a168: 94000000  bl 0 <_mcount>           //跳转到地址0,这里还不太清楚,我理解是需要加载ko后,跳转到某些符号对应的地址上
 3a16c: 6b1f027f  cmp w19, wzr             //比较w19 和 0
 3a170: 540000ed  b.le 3a18c <example_test+0x3c> //如果小于 就跳转到<example_test+0x3c>这个地址
 3a174: 52800020  mov w0, #0x1             // 写w0 为1
 3a178: b9000280  str w0, [x20]            //w0的值写到x20内
 3a17c: 52800000  mov w0, #0x0             // w0 清零
 3a180: a94153f3  ldp x19, x20, [sp,#16]   // sp+16 地址的值分别放回x19 x20
 3a184: a8c27bfd  ldp x29, x30, [sp],#32   //sp地址取的值分别放回x29 x30后sp=sp+32
 3a188: d65f03c0  ret                      //返回
 3a18c: 52800200  mov w0, #0x10            //w0 的值写成0x10
 3a190: b9000280  str w0, [x20]            //w0 的值存到 x20
 3a194: 52800000  mov w0, #0x0             // w0 清零
 3a198: a94153f3  ldp x19, x20, [sp,#16]   // sp+16 地址的值分别放回x19 x20
 3a19c: a8c27bfd  ldp x29, x30, [sp],#32   //sp地址取的值分别放回x29 x30后sp=sp+32
 3a1a0: d65f03c0  ret                      //返回
 3a1a4: d503201f  nop                      //空操作

参考文献:

  1. 基于ARM64 的常见汇编命令记录

独特见解