ARM非对齐访问和Alignment Fault
1、指令对齐
A64指令必须word对齐。尝试在非对齐地址取值会触发PC alignment fault。
1.1、PC alignment checking
PC(Program Counter)寄存器用来存放下一条执行指令地址,对于AArch64架构,如果PC寄存器低2位不为0,则触发PC alignment fault。
类似于Instruction Aborts异常,将非对齐地址加载到PC寄存器并不会直接触发PC alignment fault,只有当CPU尝试从该地址取指令时才会触发异常。
当取指异常发生在EL0时,CPU切换到EL1。当取指异常发生时,HCR_EL2.TGE位为1且EL2使能时,CPU切换到EL2。当取指异常发生时,CPU处于其他模式,CPU运行模式不切换。
当发生PC alignment fault时,ESR(Exception Syndrome Register)的EC域置0x22,表明异常级别。
伪代码AArch64.CheckPCAlignment()执行PC alignment check,伪代码AArch64.PCAlignmentFault()则主动触发异常。
2、数据访问对齐
2.1、数据非对齐访问
如果被访问的内存地址不按照被访问的数据类型的位宽对齐,称为非对齐访问。比如int型占4个字节,则访问int型数据的内存地址需要按照4字节对齐。
2.2、硬件非对齐访问支持
MIPS架构不支持非对齐访问。
X86架构支持非对齐访问,其实现机制是将非对齐访问指令拆分成多条指令执行,结合拼接(或者拆分)指令获取数据。缺点是牺牲性能。
ARMv5架构不支持非对齐访问。
ARMv6架构开始参考X86架构实现方式支持非对齐访问,但是是部分内存访问指令支持。
ARMv7-M架构中CCR.UNALIGN_TRP位控制是否使能对齐检查(Alignment Check),ARMv7-A、ARMv7-R、ARMv8架构中SCTLR.A位控制是否使能对齐检查,默认情况下不使能对齐检查。


如果使能对齐检查,则任何指令的非对齐访问均触发非对齐异常。
对于A32/T32代码,如果不使能对齐检查,则大部分指令的非对齐访问由CPU处理,如LDR, LDRH, STR, STRH,LDRSH, LDRT, STRT,LDRSHT,LDRHT,STRHT,TBH。其他的数据访问指令的非对齐访问都会触发非对齐异常,如STRD,LDRD。
对于A64代码,如果不使能对齐检查,则所有的load和store指令的非对齐访问均由CPU处理,但是exclusive load/store, load acquire和store release指令的非对齐访问则会触发非对齐异常,包括LDAXR, LDAXRB, LDAXRH, LDAXP, STLXR, STLXRB, STLXRH, STLXP。

对于SIMD指令,一般都是强制64字节或者128字节对齐,如果发生非对齐访问,则触发非对齐访问异常。
对于任何内存访问,如果使用SP指针作为基地址,就必须quadword对齐,否则触发栈对齐异常。
2.3、软件非对齐访问支持
部分MIPS架构,通过在VxWorks内核中对非对齐访问异常进行处理,通过多次访存操作和拼接操作来实现非对齐访问,代价是牺牲性能。ARM架构内核中也有类似的处理方式,可以通过相关的配置来控制其处理方式,详细内容查看第四节。
2.4、编译器非对齐访问支持
2.4.1、GCC编译器
使能非对齐访问:-munaligned-access
禁止非对齐访问:-mno-unaligned-access
默认情况下,ARM都是aligned-access的,如果代码中使用__attribute__((packed))定义的结构体,会出现结构体成员是非对齐的,此时如果没有使能非对齐访问会导致触发abort异常。
2.4.2、编译器优化
编译器一般支持对非对齐访问代码的优化,即在编译阶段通过多次内存访问操作拆分和拼接从而规避非对齐访问。
GCC编译选项-Ox用来指定代码优化级别,-O0表示不优化,其他优化级别下会对非对齐访问代码进行优化,比如将LDRD指令的非对齐访问拆分成多条LDR指令。
3、栈对齐
3.1、SP alignment checking
当SP(Stack Pointer)寄存器的低4位不为0时,如果当前指令使用SP作为基地址,则触发栈非对齐异常。
伪代码AArch64.CheckSPAlignment()执行stack pointer check,AArch64.SPAlignmentFault()则触发栈对齐异常。
类似于Data Aborts异常,将非对齐值加载到SP寄存器并不会直接触发异常,只有当尝试从非对齐地址取数据时才会触发异常。
4、ARM Linux内核非对齐访问
4.1、/proc/cpu/alignment
Linux内核只针对arm架构实现了非对齐访问处理机制,主要是针对LDR, STR, LDRD, LDRD, STRD, LDM, STM, LDRH, STRH指令的非对齐访问进行处理。对于arm64架构,因为ARMv8架构CPU可以处理所有LDR/STR类内存访问指令的非对齐访问,因此没有实现该机制。这样就会导致如果在arm64架构上运行64位Linux内核,而用户态为32位应用程序时,如果发生非对齐访问,则会触发异常。
|
值 |
宏定义 |
说明 |
|
0 |
UM_WARM |
内核打印Alignment Trap警告,打印进程名,pid,pc,指令,地址,和异常错误码等 |
|
1 |
UM_FIXUP |
内核尝试修复用户代码非对齐访问 |
|
2 |
UM_SIGNAL |
发生非对齐访问时,内核发送SIGBUS信号量给对应进程 |
ARMv5架构,该节点默认值为0,即忽略非对齐访问。
ARMv6及以上架构,CPU本身部分支持非对齐访问,因此基本不关心该节点值,但是对于LDM, STM, LDRD和STRD等复合指令进行非对齐访问时,仍然需要软件处理,此时,可以将该节点值设置为1,即fix up模式。
上述三种值是位映射,也可以是组合值。
4.2、非对齐访问异常处理流程
4.2.1、ARM架构
Linux内核初始化时,通过fs_initcall(alignment_init)加载非对齐访问处理模块。alignment_init()函数中首先创建/proc/cpu/alignment节点,然后通过hook_fault_code(FAULT_CODE_ALIGNMENT, do_alignment, SIGBUS, BUS_ADRALN, “alignment exception”)挂do_alignment钩子函数。当发生非对齐访问异常时,进入do_alignment中处理异常,根据获取的/proc/cpu/alignment节点值,分别进入不同的处理分支。
该部分代码位于arch/arm/mm/alignment.c中。
4.2.2、ARM64架构
Linux内核在fault_info[]中注册钩子函数,当发生非对齐访问(BUS_ADRALN)时,进入do_bad()函数,给对应进程发送SIGBUS信号量。
该部分代码位于arch/arm64/mm/fault.c中。
5、什么情况下容易发生非对齐访问?
出现alignment fault问题,通常是用户编写的代码导致。估计很多程序猿在编写代码(特别是c/c++代码)时,从未考虑过这样的问题,那是因为多数可能都在X86架构下的进行代码开发,而且没有考虑过代码的移植性,如前面所说X86硬件会自动处理非对齐问题,用户感知不到,但这种情况下,由此带来的性能损耗,用户可能也关注不到了。另一方面,部分情况下,编译器也会自动做padding处理(如对结构体的自动填充对齐),这也进一步让程序猿们减少了对alignment fault的关注。
最常见的可能导致alignment fault的代码编写方式如:
1) 指针转换
将低位宽类型的指针转换为高位宽类型的指针,如:将char * 转为int *,或将void *转为结构体指针。这类操作是导致alignment fault的最主要的来源,在分析定位问题时,需要特别关注。对于出现异常却又必须这样使用的场景,对这类转换后的指针进行访问时,如果不能确认其对应的地址是对齐的,则应该使用memcpy访问(memcpy方式不存在对齐问题)。另外,建议转换后立即使用,不要将其传递到其他函数和模块,防止扩展,带来潜在的问题。
2) 使用packed属性或者编译选项
这样的操作会关闭编译器的自动填充功能,从而使结构体中各个字段紧凑排列,如果排列时未处理好对齐,则可能导致alignment fault。一些场景下(内核中也较常见)确实需要用户自行紧凑排列结构体,可节省空间(在内存资源稀缺的场景下,很有用),此时需要特别关注对齐问题,建议通过填充的方法尽量对齐,如此可能会导致空间浪费,但是会提升访问性能,典型的“以空间换时间”的思路。如果对空间有强烈要求,而可以接受性能损失,也可以不考虑对齐,不做padding,但在访问这些结构体的数据时,需要全部使用memcpy的方式。
6、测试代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void sigbus_handler(int sno)
{
printf("signal %d captured\n", sno);
exit();
}
int main(int argc, char *argv[])
{
char intarray[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
signal(SIGBUS, sigbus_handler);
printf("int1 = 0x%08x, int2 = 0x%08x, int3 = 0x%08x, int4 = 0x%08x\n",
*((int *)(intarray + )),
*((int *)(intarray + )),
*((int *)(intarray + )),
*((int *)(intarray + )));
return ;
}
ARM非对齐访问和Alignment Fault的更多相关文章
- 非对齐访问(unaligned accesses)
从CPU角度看内存访问对齐 结构体成员非对齐访问所带来的思考 ARM体系中存储系统非对齐的存储访问操作 什么是cache line? cache line就是处理器从RAM load/store数据到 ...
- ARM 非对齐的数据访问操作
I’m confused about unaligned memory accesses on ARM. My understanding was that they’re not allowed — ...
- ARM非对齐操作异常解决过程
在测试MF固件时,发生一个非常诡异的异常,代码如下: CLR_DBG_Commands::Monitor_EraseMemory* cmd = (CLR_DBG_Commands::Monitor_E ...
- 从硬件到语言,详解C++的内存对齐(memory alignment)
转载请保留以下声明 作者:赵宗晟 出处:https://www.cnblogs.com/zhao-zongsheng/p/9099603.html 很多写C/C++的人都知道“内存对齐”的概念以及规则 ...
- 从硬件到语言,详解C++的内存对齐(memory alignment)(一)
作者:赵宗晟 出处:https://www.cnblogs.com/zhao-zongsheng/p/9099603.html 很多写C/C++的人都知道“内存对齐”的概念以及规则,但不一定对他有很深 ...
- ARM字节对齐问题详解
一.什么是字节对齐,为什么要对齐? 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这 ...
- CM3中数据传输对齐/非对齐方式
在CM3中,非对齐的数据传输只发生在常规的数据传送指令中,如LDR.LDRH.LDRSH.其他指令则不支持,包括: 1.多个数据的加载.存储(LDM/STM). 2.堆栈操作PUSH.POP. 3.互 ...
- C++成员变量内存对齐问题,ndk下非对齐的内存访问导致BUS_ADRALN
同样的代码,在vs下运行正常,在android ndk下却崩溃: signal 7(SIGBUS),code 1 (BUS_ADRALN),fault addr 0xe6b82793 Func(sho ...
- Best Coder Round#25 1003 树的非递归访问
虽然官方解释是这题目里的树看作无向无环图,从答案来看还是在“以1作为根节点”这一前提下进行的,这棵树搭建好以后,从叶节点开始访问,一直推到根节点即可——很像动态规划的“自底向上”. 但这棵树的搭建堪忧 ...
随机推荐
- windows上安装 包管理工具choco及scoop
1.安装 choco: 1.1.使用管理员方式打开 PowerShell 1.2.输入 Set-ExecutionPolicy RemoteSigned,输入 Y 1.3.安装 choco输入:iwr ...
- C++自学教程第一课——你好世界,我是柠檬鲸。
C++系列教程现在在自己学校的一个博客平台发布,几个朋友一起搭建的 [C++基础教程系列](https://blog.ytmaxoj.org/cpp_basic_liuary-0/) 下面是原来的正文 ...
- P4556 [Vani有约会]雨天的尾巴(线段树合并+lca)
P4556 [Vani有约会]雨天的尾巴 每个操作拆成4个进行树上差分,动态开点线段树维护每个点的操作. 离线处理完向上合并就好了 luogu倍增lca被卡了5分.....于是用rmq维护.... 常 ...
- C语言--一维数组,字符数组
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/zuoyou1314/article/details/30799519 watermark/2/tex ...
- Springboot+Mybatis AOP注解动态切换数据源
在开发中因需求在项目中需要实现多数据源(虽然项目框架是SpringCloud,但是因其中只是单独的查询操作,觉得没必要开发一个项目,所以采用多数据源来进行实现) 1.在配置文件中创建多个数据连接配置 ...
- 使用form表单提交请求如何获取后台返回的数据?
问题描述 一般的form表单提交是单向的:只能给服务器发送数据,但是无法获取服务器返回的数据,也就是无法读取HTTP应答包. 想要真正的半双工通讯一般需要使用Ajax, 但是Ajax对文件传输也很麻烦 ...
- 锋利的jQuery ——jQuery中的DOM操作(三)
一.DOM的操作分类 1>DOM Core 2>HTML-DOM 3>CSS-DOM 二.jQuery中的DOM操作 DOM树 ①查找节点 1)查找元素节点 利用jQuery ...
- js中的object类型
特点: 每个Object类型的实例共有的属性和方法: constructor: 保存着用于创建当前对象的函数. hasOwnProperty:用于检测给定的属性在当前对象的实例中是否存在. isPro ...
- Maven下载清除jar包
maven jar包下载命令行方式 在STS中下载JAR包时经常卡住无法继续下载,这时可以用命令行方式进行下载.在终端中今入到该项目的根目录下,然后mvn clean;mvn install;等待下载 ...
- 数据仓库Hive(一)——hive简介,产生,安装
1.Hive简介 数据仓库 解释器.编译器.优化器等 运行时,元数据存储在关系型数据库里面 1.1数据库和数据仓库的区别 数据库需要立即返回结果,数据仓库不需要 数据仓库能收纳各种数据源,而数据库只能 ...