ARMv8汇编基础介绍
ARM
2024-04-20 5326字

ARMv8汇编基础介绍

在嵌入式Linux开发过程中会经常遇到空指针等内核的Oops报错,会打印一些寄存器的值及调用栈。

当遇到如上情况,那大概率是要上汇编了。涉及到objdump命令反汇编vmlinux或者对应的.o文件用来分析定位问题点。此文章围绕ARMv8上的汇编基础知识展开介绍。

反汇编指得到汇编代码,反编译是指得到对应语言的源代码。

1 寄存器

[    9.795823] PC is at rs485_do_tasklet+0x8c/0x11c
[    9.795827] LR is at rs485_do_tasklet+0x64/0x11c
[    9.795829] pc : [<ffffff800861c4ec>] lr : [<ffffff800861c4c4>] pstate: 60400145
[    9.795831] sp : ffffffc07c95bc60
[    9.795835] x29: ffffffc07c95bc60 x28: 0000000000000006
[    9.795838] x27: ffffff8009064018 x26: 000000000000000a
[    9.795842] x25: 0000000000000100 x24: 0000000000000000
[    9.795845] x23: ffffffc07b5ee170 x22: 0000000000000000
[    9.795849] x21: ffffff80096bf730 x20: ffffff800944d000
[    9.795852] x19: 0000000000000060 x18: 0000000000000004
[    9.795855] x17: 0000007f9e5111c8 x16: ffffff8008215018
[    9.795859] x15: 0000000000000001 x14: ffffff8008e355ab
[    9.795862] x13: 0000000000000003 x12: 0000000000000000
[    9.795865] x11: ffffffc07f305c90 x10: 0000000000000aa0
[    9.795869] x9 : 0000200000000000 x8 : 0000000000000001
[    9.795872] x7 : 0000000008000000 x6 : 0000200000000000
[    9.795876] x5 : 0000000000000000 x4 : 000000000001c200
[    9.795879] x3 : 0000000000000000 x2 : 0000000000000000
[    9.795882] x1 : ffffffc07c95bca8 x0 : 0000000000000130

1.1 通用寄存器

ARMv8存在31个64位通用寄存器。

31 个通用寄存器r0 ~ r30,每个寄存器可以存取一个 64位大小的数。 当使用 x0 - x30访问时,是一个 64位的数;当使用 w0 - w30访问时,是一个 32 位的数,访问的是寄存器的 32,如图:

img

特殊寄存器

2 指令

指令读取

arm64 架构中,每个指令读取都是 64 位,即 8字节 空间

arm64 约定(一般来说)

2.1 指令介绍

3 实际案例分析


每个函数调用,都会有 入栈出栈 操作。

编写一个Demo程序,实现打印字符串,如下:

//test.c
#include <stdio.h>

void TestPushAndPop()
{
    printf("Push an Pop !");
}	

使用gcc编译成汇编文件:aarch64-linux-gnu-gcc -S -o test.s test.c

; test.s
        .arch armv8-a	;架构
        .file   "test.c"	;文件名称
        .text	
        .section        .rodata	;数据放到只读数据段
        .align  3	;2^3对齐,8字节对齐
.LC0:
        .string "Push an Pop !"	;存储了一个字符串常量
        .text 
        .align  2
        .global TestPushAndPop ;全局符号
        .type   TestPushAndPop, %function
TestPushAndPop: #label
        stp     x29, x30, [sp, -16]! ;入栈指令 将x29,x30中的值存入sp(栈底)地址减16byte的位置 
        add     x29, sp, 0	;将当前栈指针 sp 的值赋给寄存器 x29,即建立当前栈帧。
        adrp    x0, .LC0 ;获取.LC0的基地址,并将这个基地址的高20位加载到目标寄存器x0中。
        add     x0, x0, :lo12:.LC0
        bl      printf	;调用 printf 函数,将寄存器 x0 中保存的字符串地址作为参数传递给 printf 函数。
        nop	;空操作,用于占位,没有实际作用。
        ldp     x29, x30, [sp], 16  ; 将栈中的值取出存放到x29, x30
        ret	;返回
        .size   TestPushAndPop, .-TestPushAndPop
        .ident  "GCC: (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0"
        .section        .note.GNU-stack,"",@progbits

注意:对栈的 分配/释放 操作只会对栈指针做加减法, 而不会对栈内存中的内容做任何修改(也不会把释 放的栈空间设置为 0)。