ARM数据类型

847 阅读6分钟

基础知识:

字节(bytes): 8位(bit) 字(word): 4字节: 32位 半字(half word): 2字节: 16位

ARM中表示:

字节用-b-sb表示 半字用-h-sh表示 没有字的表示 其中 无符号类型与有符号类型 的差别是:

符号数据类型可以包含正负数所以数值范围上更低些 无符号数据类型可以放得下很大的正数但是放不了负数

使用对应数据类型做存取操作的汇编指令示例:

ldr: 加载字,宽度四字节 ldrh: 加载无符号的半字,宽度两字节 ldrsh: 加载有符号的半字,宽度两字节 ldrb: 加载无符号的字节 ldrsb: 加载有符号的字节

str: 存储字,宽度四字节 strh: 存储无符号的半字,宽度两字节 strsh: 存储有符号的半字,宽度两字节 strb: 存储无符号的字节 strsb: 存储有符号的字节

字节序

主要理解内存中字节排序:大端(big endian)、小端(little endian),还记得加密解密第4版第一章1.2.2结讲的就是这个,书中讲了endian的来历:《格列佛游记》小说中的小人国居民为了吃鸡蛋是从鸡蛋大的一端(big endian)还是从小的一端(little endian)剥开而争论。所以字节传送顺序的大端小端的由来就是这里。

大端(big endian): 高位字节存入低地址,低位字节存入高地址; 小端(little endian): 低位字节存入低地址,高位字节存入高地址;

下面以数据 0x12345678 为例,掩饰内存中大端小端存入方式:

存放顺序 1001h 1001h 1002h 1003h
大端 12h 34h 56h 78h
小端 78h 56h 34h 12h

在ARMv3之前使用的是小端排序,之后使用的都是大端排序,但也允许切换回小端排序。例如在ARMv6中,指令固定使用的是小端排序,数据访问可以使用小端也可以使用大端排序,由程序寄存器(CPSR)的第9位(E位)控制。

ARM 寄存器

根据 ARM参考手册 可以了解,在 ARMv6-M 与 ARMv7-M 的处理器中均有30个32位宽度的通用寄存器 (General-purpose registers)。前16个寄存器是用户层可访问控制的,其他的寄存器在高权限进程中可以访问(但ARMv6-M与ARMv7-M除外)。这16个寄存器分为两组:通用寄存器与有特殊含义的寄存器:

# 别名 用途
R0 - 通用寄存器
R2 - 通用寄存器
R3 - 通用寄存器
R4 - 通用寄存器
R5 - 通用寄存器
R6 - 通用寄存器
R7 - 通用寄存器
R8 - 通用寄存器
R9 - 通用寄存器
R10 - 通用寄存器
R11 FP 栈帧指针
特殊寄存器
R12 IP 内部程序调用
R13 SP 栈指针
R14 LR 链接寄存器(一般存放函数返回地址)
R15 PC 程序计数寄存器
CPSR - 当前程序状态寄存器

其中:

R0-R12: 用来在操作中存储临时的值、指针等;其中 R0 用来存储函数调用的返回值,R7 经常被用作存储系统调用号,R11 存放着帮助我们找到栈帧边界的指针。以及在函数的调用约定中,前四个参数按顺序存放在 R0-R3 寄存器中。

R13: 别名SP(栈指针)。栈指针寄存器用来指向当前的栈顶。堆栈是用于函数特定存储的内存区域,函数返回时将对其进行回收。可以通过在栈指针中减去我们需要分配的值 (以字节为单位) 来在堆栈中分配空间。换句话说,如果我们想分配一个32位的值,那就用栈指针减去4,然后将这个值从减去前栈指针所指向的位置才是存放;

Q: 为什么减去4? A: 因为栈指针所减的值是字节类型,一个字节等于8位,我们要存的是32位的值,所以是32/8=4字节 最后的意思打个比方来解释:比如现在栈指针指向0x12345678,存放32位值,减去4后等于0x12345674,然后从0x12345678开始存放那个32位值,直到0x12345674

R14: 别名LR(链接寄存器)。当一个函数调用发生时,链接寄存器 就被用来记录函数调用发生所在位置的下一条指针的所在地址。这么做允许我们快速的从子函数回到父函数。

R15: 别名PC(程序计数器)。程序计数器是一个在程序指令执行时自增的计数器。它的大小在ARM模式下总是4字节对齐,在Thumb模式下总是两字节对齐。当执行一个分支指令时,PC存储目的地址。在程序执行中,ARM模式下的PC存储着当前指令加8(两条ARM指令后)的位置,Thumb(v1)模式下的PC存储着当前指令加4(两条Thumb指令后)的位置。这也是X86与ARM在PC上的主要不同之处。

CPSR: 别名程序状态寄存器。别名及其含义如下表:

标记 说明
N(Negative) 指令操作结果为负值时置1
Z(Zero) 指令操作结果为零值时置1
C(Carry) 可以有4种方法设置C的值:
-加法运算(包括CMP):当运算结果产生了进位时(无符号数溢出),C=1,否则C=0。
-减法运算(包括CMP):当运算时产生了借位时(无符号数溢出),C=0,否则C=1。
-对于包含移位操作的非加/减运算指令,C为移出值的最后一位。
-对于其它的非加/减运算指令,C的值通常不会改变。
V(Overflow) 指令结果不能用32位的二进制补码存储,即发生了溢出时置1
E(Endian) 小端序置0,大端序置1
T(Thumb) 当为Thumb模式时置1,ARM模式置0
M(Mode) 当前的权限模式(用户态,内核态)
J(Jazelle) 允许ARM处理器去以硬件执行java字节码的状态标示