ARM汇编(第二部分)ARM数据类型和寄存器

https://azeria-labs.com/arm-data-types-and-registers-part-2/

数据类型

 这是《 ARM Assembly Basics》教程系列的第二部分,介绍了数据类型和寄存器。

 与高级语言类似,ARM支持对不同数据类型的操作。我们可以加载(或存储)的数据类型可以是有符号的和无符号的字,半字或字节。
在这里插入图片描述

这些数据类型的扩展名是:-h或-sh表示半字,-b或-sb表示字节,而字无扩展名。有符号和无符号数据类型之间的区别是:

  • 带符号的数据类型可以同时包含正值和负值,因此范围较小。
  • 无符号数据类型可以容纳较大的正值(包括“零”),但不能容纳负值,因此范围更广。

以下是如何将这些数据类型与“加载和存储”指令一起使用的一些示例:

ldr = Load Word
ldrh = Load unsigned Half Word
ldrsh = Load signed Half Word
ldrb = Load unsigned Byte
ldrsb = Load signed Bytes

str = Store Word
strh = Store unsigned Half Word
strsh = Store signed Half Word
strb = Store unsigned Byte
strsb = Store signed Byte
指令全称描述
ldrload word加载一个全字
ldrhload unsigned half word加载无符号半字
ldrshload signed half word加载有符号半字
ldrbload unsigned byte加载无符号字节
ldrsbload signed byte加载有符号字节
strstore word存储字
strhstore word存储无符号半字
strshstore word存储有符号半字
strbstore unsigned byte存储无符号字节
strsbstore signed byte存储有符号字节

字节序

 有两种查看内存中字节的基本方法:小字节序(LE)或大字节序(BE)。区别在于对象的每个字节存储在内存中的字节顺序。在像Intel x86这样的little-endian机器上,最低有效字节存储在最低地址(最接近零的地址)上。在big-endian计算机上,最高有效字节存储在最低地址。ARM体系结构在版本3之前是低字节序的,因为那时它是双字节序的,这意味着它具有允许可切换字节序的设置。例如,在ARMv6上,指令是固定的小端形式,并且数据访问可以是小端形式,也可以是大端形式,这由程序状态寄存器(CPSR)的位9(E位)控制。
在这里插入图片描述

ARM寄存器

寄存器的数量取决于ARM版本。根据《 ARM参考手册》,除了基于ARMv6-M和基于ARMv7-M的处理器外,还有30个通用32位寄存器。前16个寄存器可在用户级模式下访问,其他寄存器可在特权软件执行中使用(ARMv6-M和ARMv7-M除外)。在本教程系列中,我们将使用可在任何特权模式下访问的寄存器:r0-15。这16个寄存器可以分为两组:通用寄存器和专用寄存器。

在这里插入图片描述
下表仅仅是一个快速窥探到ARM寄存器怎么可能 涉及到那些在英特尔处理器。

在这里插入图片描述

  • R0-R12:可以在常规操作期间用于存储临时值,指针(到存储器的位置)等。R0例如在算术操作期间可以称为累加器,或者用于存储先前调用的函数的结果。R7在处理系统调用时非常有用,因为它存储系统调用号,而R11帮助我们跟踪用作帧指针的堆栈边界(稍后将介绍)。此外,ARM上的函数调用约定指定函数的前四个参数存储在寄存器r0-r3中。

  • R13:SP(堆栈指针)。堆栈指针指向堆栈的顶部。堆栈是用于特定于函数存储的内存区域,当函数返回时将对其进行回收。因此,通过从堆栈指针中减去我们要分配的值(以字节为单位),堆栈指针可用于在堆栈上分配空间。换句话说,如果我们要分配一个32位值,则从堆栈指针中减去4。

  • R14:LR(链接寄存器)。进行功能调用时,链接寄存器将使用一个内存地址进行更新,该内存地址引用了从其开始该功能的下一条指令。这样做可以使程序返回到“父”函数,该子函数在“子”函数完成后启动“子”函数调用。

  • R15:PC(程序计数器)。程序计数器自动增加执行指令的大小。在ARM状态下,此大小始终为4个字节,在THUMB模式下,此大小始终为2个字节。当执行分支指令时,PC保留目标地址。在执行期间,PC在ARM状态下存储当前指令的地址加8(两个ARM指令),在Thumb(v1)状态下存储当前指令的地址加4(两个Thumb指令)。这与x86不同,x86中PC始终指向要执行的下一条指令。

让我们看一下PC在调试器中的行为。我们使用以下程序将pc的地址存储到r0中,并包括两个随机指令。让我们看看发生了什么。

.section .text 
.global _start 

_start:
 mov r0,pc 
 mov r1,#2
 加r2,r1,r1 
 bkpt

在GDB中,我们在_start处设置一个断点并运行它:

gef> br _start
Breakpoint 1 at 0x8054
gef> run

这是我们首先看到的输出的屏幕截图:

$r0 0x00000000   $r1 0x00000000   $r2 0x00000000   $r3 0x00000000 
$r4 0x00000000   $r5 0x00000000   $r6 0x00000000   $r7 0x00000000 
$r8 0x00000000   $r9 0x00000000   $r10 0x00000000  $r11 0x00000000 
$r12 0x00000000  $sp 0xbefff7e0   $lr 0x00000000   $pc 0x00008054 
$cpsr 0x00000010 

0x8054 <_start> mov r0, pc     <- $pc
0x8058 <_start+4> mov r0, #2
0x805c <_start+8> add r1, r0, r0
0x8060 <_start+12> bkpt 0x0000
0x8064 andeq r1, r0, r1, asr #10
0x8068 cmnvs r5, r0, lsl #2
0x806c tsteq r0, r2, ror #18
0x8070 andeq r0, r0, r11
0x8074 tsteq r8, r6, lsl #6

我们可以看到PC保留了将要执行的下一条指令(mov r0,pc)的地址(0x8054)。现在,让我们执行下一条指令,之后R0应该保持PC的地址(0x8054),对吗?

$r0 0x0000805c   $r1 0x00000000   $r2 0x00000000   $r3 0x00000000 
$r4 0x00000000   $r5 0x00000000   $r6 0x00000000   $r7 0x00000000 
$r8 0x00000000   $r9 0x00000000   $r10 0x00000000  $r11 0x00000000 
$r12 0x00000000  $sp 0xbefff7e0   $lr 0x00000000   $pc 0x00008058 
$cpsr 0x00000010

0x8058 <_start+4> mov r0, #2       <- $pc
0x805c <_start+8> add r1, r0, r0
0x8060 <_start+12> bkpt 0x0000
0x8064 andeq r1, r0, r1, asr #10
0x8068 cmnvs r5, r0, lsl #2
0x806c tsteq r0, r2, ror #18
0x8070 andeq r0, r0, r11
0x8074 tsteq r8, r6, lsl #6
0x8078 adfcssp f0, f0, #4.0

…正确的?错误的。查看R0中的地址。尽管我们期望R0包含先前读取的PC值(0x8054),但它却保留了比我们先前读取的PC(0x805c)提前两个指令的值。从这个例子中您可以看到,当我们直接阅读PC时,它遵循PC指向下一条指令的定义。但是在调试时,PC会在当前PC值(0x8054 + 8 = 0x805C)之前指向两个指令。这是因为较旧的ARM处理器始终会在当前执行的指令之前获取两条指令。ARM保留此定义的原因是为了确保与早期处理器兼容。

当前程序状态寄存器

使用gdb调试ARM二进制文件时,您会看到称为Flags的信息:
在这里插入图片描述

寄存器$ cpsr显示当前程序状态寄存器(CPSR)的值,在该状态下,您可以看到标志标志,快速,中断,溢出,进位,零和负。这些标志代表CPSR寄存器中的某些位,并根据CPSR的值进行设置,并在激活时变为粗体。N,Z,C和V位与x86上EFLAG寄存器中的SF,ZF,CF和OF位相同。这些位用于支持汇编级别的条件和循环中的条件执行。我们将介绍在第6部分:条件执行和分支中使用的条件代码。
在这里插入图片描述
上图显示了32位寄存器(CPSR)的布局,其中左侧(<-)保留了最高有效位,而右侧(->)保留了最低有效位。每个单个单元(GE和M部分以及空白单元除外)的大小为一位。这些一位定义了程序当前状态的各种属性。

在这里插入图片描述假设我们将使用CMP指令比较数字1和2。结果将为“负”,因为1-2 = -1。当我们比较两个相等的数字(例如2与2)时,会设置Z(零)标志,因为2 – 2 =0。请记住,不会修改与CMP指令一起使用的寄存器,只会修改CPSR基于将这些寄存器相互比较的结果。

这是在GDB(已安装GEF)中的样子:在此示例中,我们比较了寄存器r1和r0,其中r1 = 4和r0 =2。这是执行cmp r1,r0操作后的标志外观:
在这里插入图片描述

设置进位标志是因为我们使用cmp r1,r0将4与2(4 – 2)进行比较。相反,如果我们使用cmp r0,r1将较小的数字(2)与较大的数字(4)进行比较,则会设置负标志(N )。

这是ARM信息中心的摘录:

APSR包含以下ALU状态标志:

N –当运算结果为负时设置。

Z –运算结果为零时设置。

C –当操作导致进位时置位。

V –在操作引起oVerflow时设置。

进位发生:

如果相加的结果大于或等于2 32
如果减法的结果是正数或零
作为移动或逻辑指令中的直列式桶形移位器操作的结果。
如果加,减或比较的结果大于或等于2 31或小于Δ2 2 31,则会发生溢出。

相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页