ARM体系结构学习笔记:寄存器

前段时间通篇概览一遍汇编语言程序设计——基于ARM体系结构(第4版), 总感觉纸上得来终觉浅, 并不能够让我产生一种读汇编就跟读C代码一样那种流畅的感觉. 如果我们越熟悉, 越发觉得他们是有规律可循的, 这里做一下对应的记录, 互相共勉.

通用寄存器并不通用

表面上arm为我们提供了31个通用寄存器, 但其实他们并不通用, 或者说编译在编译代码时, 并不是随意的给寄存器赋值的, 他们都被划分为了不同的用途和含义, 我们需要记住这些用途,看汇编看起来就不会无从着手了.

ARM64提供了31个通用寄存器,其用途如下表:

x0~x7:传递子程序的参数和返回值,使用时不需要保存,多余的参数用堆栈传递,64位的返回结果保存在x0中。

x8:用于保存子程序的返回地址,使用时不需要保存。

x9~x15:临时寄存器,也叫可变寄存器,子程序使用时不需要保存。

x16~x17:子程序内部调用寄存器(IPx),使用时不需要保存,尽量不要使用。

x18:平台寄存器,它的使用与平台相关,尽量不要使用。

x19~x28:临时寄存器,子程序使用时必须保存。

x29:帧指针寄存器(FP),用于连接栈帧,使用时必须保存。

x30:链接寄存器(LR),用于保存子程序的返回地址。

x31:堆栈指针寄存器(SP),用于指向每个函数的栈顶。

20211122173258

比较重要的寄存器 FP, LR, SP, PC, PSR

  1. 通过PC寄存器状态信息可以了解到目前PC寄存器在等待SVC调用, 处于read函数+4的偏移位置

  2. 通过LR寄存器了解到上一层调用来自__sread, 调用链为__sread()->read()

  3. PSR寄存器有好几种模式, 目前显示的是用户模式下面的CPSR寄存器的状态信息, 不同的位,具有不同的含义, 图片内已经做了详细的注释信息

次重要寄存器

hello:00000056C73836C4 SUB             SP, SP, #0x10           ; CODE XREF: main:loc_56C7383710↓p
hello:00000056C73836C8 MOV             W8, #1                  ; w8 = 1         ;
hello:00000056C73836CC MOV             W9, #2                  ; w9 = 2         ;
hello:00000056C73836D0 STR             W8, [SP,#0x10+var_4]    ; var_4 = w8 = 1 ;临时变量初始化为1
hello:00000056C73836D4 STR             W9, [SP,#0x10+var_8]    ; var_8 = w9 = 2 ;临时变量初始化2
hello:00000056C73836D8 STR             WZR, [SP,#0x10+var_C]   ; var_c = 0      ;临时变量初始化0     使用wzr寄存器
hello:00000056C73836DC LDR             W8, [SP,#0x10+var_4]    ; w8 = var_4 = 1 ;临时变量到寄存器w8
hello:00000056C73836E0 LDR             W9, [SP,#0x10+var_8]    ; w9 = var_8 = 2 ;临时变量到寄存器w9
hello:00000056C73836E4 ADD             W8, W8, W9              ; w8 = w8 + w9 = 3;执行加法
hello:00000056C73836E8 STR             W8, [SP,#0x10+var_C]    ; var_c = w8 = 3  ;存储结果
hello:00000056C73836EC LDR             W0, [SP,#0x10+var_C]    ; w0 = var_c = 3  ;返回结果 因为返回值是int, 因此使用w. w0便存储了对应的返回值
hello:00000056C73836F0 ADD             SP, SP, #0x10           ; ;pop stack      ;回收临时变量
hello:00000056C73836F4 RET
  1. X0寄存器: 保存返回值

  2. X8, X9寄存器: tmp

  3. WZR/XZR寄存器: zero寄存器, 一般用来进行零时变量初始化

PC寄存器

因为PC寄存器比较重要, 因此单独列出来

https://github.com/yhnu/note/tree/master/arm/02register_pc.md

指令的设计

  1. 没有隐式内存操作指令

所有的内存操作都有对应的指令(LDR/STR)

  1. 不同的指令都有对应的操作数{内存/立即数/寄存器}(对应数量1-3个)

(1) 内存操作数和立即数操作数不能同时存在

(2) 内存操作数至多出现一次

(3) 寄存器操作数总在最前面

常用的指令

  1. SUB

将某一寄存器的值和另一寄存器的值 相减 并将结果保存在另一寄存器中

SUB             x0, x1, x2   ; x0 = x1 - x2
SUB             SP, SP, #0x30; 更新栈顶寄存器的值,(可以看出:申请 0x30 字节占空间为临时变量)
  1. STP(stp:store pair)

入栈指令(str 的变种指令,可以同时操作两个寄存器), var_xxx(xxx代表偏移,一般为负数)

STP             X29, X30, [SP,#0x20+var_s0] ; 入栈指令(str 的变种指令,可以同时操作两个寄存器), 保存X29, X30, X29为FP寄存器, X30为LR寄存器
  1. ADD

将某一寄存器的值和另一寄存器的值 相加 并将结果保存在另一寄存器中

add x0, x0, #1    ; x0 = x0 + 1
add x0, x1, x2    ; x0 = x1 + x2
add x0, x1, [x2]  ; x0 = x1 + mem(x2)

ADD             X29, SP, #0x20; X29 = SP + 0x20, 前面已经保存了寄存器,这里开始使用寄存器
  1. ADR

    这是一条小范围的地址读取指令,它将基于PC的相对偏移的地址读到目标寄存器中;
    使用格式:ADR register exper

  2. ADRP

用来定位数据段中的数据用, 因为 aslr 会导致代码及数据的地址随机化, 用 adrp 来根据 pc 做辅助定位

ADRP            X8, #aGoing@PAGE        ; "going ...... \n"
ADD             X8, X8, #aGoing@PAGEOFF ; "going ...... \n"
  • 得到一个大小为4KB的页的基址,而且在该页中有全局变量aGoing的地址;ADRP就是讲该页的基址存到寄存器X8中;
  • ADD指令会算出g的地址,X8+#aGoing@PAGEOFF,#aGoing@PAGEOFF是一个偏移量;这样就得到了g的地址X8;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k4OGxdX9-1692235287484)(https://cdn.jsdelivr.net/gh/yhnu/PicBed/20211122213837.png)]

对应原理:

  • 编译时,首先会计算出当前PC到exper的偏移量#offset_to_exper
  • pc的低12位清零,然后加上偏移量,给register, 得到的地址,是含有label的4KB对齐内存区域的base地址;
  1. STUR/STR

ST开头的为存数据,比如说STR、STP、STUR

U: 表示负数
P: PAIR

STUR            WZR, [X29,#var_4]       ; var_4 = 0
STUR            W0, [X29,#var_8]        ; var_8 = w0; w0 is argc
STR             X1, [SP,#0x20+var_10]   ; var_10 = x1; x1 is argv
STR             X8, [SP,#0x20+var_18]   ; var_18 = x8;
  1. LDR
LDR             X0, [SP,#0x20+var_18]   ; x0 = var_18 = x8 = "going" is printf arg0
  1. BL/B/BR

BL: Branch with Link (−16MB to +16MB)

B : Branch (−2KB to +2KB)

BR: Branch to register (Any value in register)

B指令ARM官方文档

BL              unk_639712B640          ; jump to printf with save lr
BL              unk_639712B650          ; jump to getchar with save lr
B               loc_639712B738          ; while(1)
BR              X17                     ; printf

hello 添加对应注释

20211122190626

PLT 调用特征

loc_639712B640                            ; CODE XREF: main+28↓p
ADRP            X16, #off_639713BFB0@PAGE ; X16 = PAGE(0x110000)
LDR             X17, [X16,#off_639713BFB0@PAGEOFF] ;  X17 = mem(X16 + offset) ; offset=00112000
ADD             X16, X16, #off_639713BFB0@PAGEOFF  ; X16 = X16 + offset
BR              X17                                ; printf

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-62nOvZyQ-1692235287485)(https://cdn.jsdelivr.net/gh/yhnu/PicBed/20211122193628.png)]

通过printf的plt调用过程,他们使用的策略都是一样的:

  • 使用x16, x17寄存器
  • 首先使用ADRP找到页基址, 然后加载对应页偏移, BR进行寄存器跳转
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: ARM体系结构在数据类型级和寄存器堆栈操作方面有许多实验可以进行。数据类型级操作包括对不同数据类型的操作和操作数的大小端模式处理。寄存器堆栈操作主要涉及寄存器的读取、写入和压栈、出栈等操作。 在数据类型级操作实验中,可以通过编写程序来演示不同数据类型的操作。例如,可以编写一个程序来实现整数和浮点数的相加、相乘等操作,并观察结果的正确性。还可以测试在不同大小端模式下的数据存储和读取方式,验证ARM的数据存储方式的灵活性和兼容性。 在寄存器堆栈操作实验中,可以编写程序来实现寄存器的读取和写入操作。通过编写一段程序,可以将一个数值存储到某个寄存器中,然后再将其读取出来并进行比较,以验证寄存器操作的正确性。此外,还可以编写程序来实现寄存器的堆栈操作,例如实现寄存器的压栈和出栈操作,并观察栈顶的值的变化情况,来验证堆栈操作的正确性和有效性。 总之,通过进行ARM体系结构数据类型级和寄存器堆栈操作的实验,可以加深对ARM体系结构的理解和掌握,同时也可以验证ARM的灵活性和可靠性。 ### 回答2: ARM体系结构的数据类型级与寄存器堆栈操作操作实验是一种用来研究ARM处理器的数据类型与寄存器堆栈的操作的实验。在这个实验中,我们可以深入了解ARM处理器中数据类型的特点以及寄存器堆栈的操作。 数据类型级是指ARM处理器所支持的数据类型的级别。ARM处理器支持多种数据类型,包括整数、浮点数、向量等。在实验中,我们可以通过编写程序来实例化不同的数据类型,并对其进行操作和计算。例如,我们可以实现整数之间的加法、浮点数之间的乘法等运算,来观察ARM处理器对不同数据类型的处理能力和速度优势。 寄存器堆栈操作是指对寄存器和堆栈的读取、写入和操作。ARM处理器有多个寄存器,用于存储临时数据和指令执行过程中的计算结果。堆栈用于存储函数调用的返回地址和局部变量等信息。在实验中,我们可以编写程序,通过操作寄存器和堆栈来实现一些功能,如函数调用、递归等。 通过进行ARM体系结构的数据类型级与寄存器堆栈操作操作实验,我们可以更好地理解ARM处理器的内部结构和运行原理。这不仅有助于我们优化程序的执行效率和性能,还可以帮助我们更深入地理解ARM体系结构的特点和优势。实验可以通过软件仿真或硬件实现来进行,选择适合的实验器材和软件工具进行实验,能够更好地模拟和验证ARM处理器的性能和功能。 ### 回答3: ARM体系结构的数据类型级别包括字节(8位)、半字(16位)、字(32位)和双字(64位)。通过数据类型级别,ARM可以支持不同精度的数据处理,提高了数据处理的效率和灵活性。 在ARM体系结构中,寄存器是非常重要的组成部分。寄存器是一种位于处理器内部的高速存储器,用于临时存储数据和指令。ARM处理器具有多个通用寄存器,其中有几个寄存器是专门用于存储特定的值和执行特定的操作的,如程序计数器(PC)、堆栈指针(SP)和链接寄存器(LR)。 在ARM体系结构中,寄存器堆栈操作是指将寄存器的内容保存到堆栈中,或者从堆栈中恢复寄存器的内容。这样可以在需要时保存寄存器的值,以便在稍后的时间点进行恢复,以确保程序执行的正确性和连续性。 寄存器堆栈操作通常包括以下几个步骤: 1. 压栈:将要保存的寄存器的值按顺序依次压入堆栈中。这样可以确保在程序执行后能够准确地恢复寄存器的值。 2. 弹栈:当需要恢复寄存器的值时,按照相反的顺序从堆栈中取出寄存器的值,并将其存放回相应的寄存器中。 通过寄存器堆栈操作,可以在程序执行过程中暂时保存一些寄存器的值,以便在需要时进行恢复,保证程序的正确执行。同时,寄存器堆栈操作也可以用于嵌套子程序的调用和返回,确保程序的顺利执行和传递参数等。 总之,ARM体系结构的数据类型级别和寄存器堆栈操作是为了提高数据处理的效率和灵活性以及保证程序的正确性而设计的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

z同学的编程之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值