一、问题引入
什么是对齐异常呢,为什么ARM处理器下会有对齐异常?这个要先从arm 32位处理器下的两条指令LDR与STR说起。作为A32指令集的两条最基本指令,STR/LDR 可以用于寄存器与内存之间的数据交换,LDR是将内存中的数载入到寄存器,STR是将寄存器中的数载入内存。A32下LDR与STR一次操作4个字节,例如如下汇编程序:
LDR R1, 0xE0000000
该指令将0xE0000000地址的内容(4个字节)载入到R1寄存器,这个0xE0000000地址显然是4的倍数,也就是对齐的,当这个地址为0xE0000001/0xE0000002/0xE0000003时,就是非对齐的。在arm 32位处理器下,某些指令对内存地址进行非对齐的访问,就会触发对齐异常,比如vldr/vstr、ldrd/strd、vldm/vstrm、ldm/stm指令等,在arm64位处理器下,除了运行同样32位的非对齐指令外,也有些指令会触发对齐异常,比如device mem下的ldr/str相关指令。
二、验证平台
该系列博客会选用多个arm架构下处理器平台进行对齐异常的验证、分析,并探讨出对应异常处理的解决方案,以下是示例平台:
arm32平台
imx6Quad处理器:cortex-a9,4核,1.2GHz,运行32位linux系统,内核版本位3.14
树莓派4-BCM2411处理器:cortex-a72,4核,1.5GHz,运行32位linux系统,内核版本为4.19
arm64平台
某国产处理器:armv8兼容架构,4核,1.5GHz,运行64位linux系统,内核版本为4.4
…
三、举例介绍
下面先用vldr指令对齐异常做个举例介绍,通过运行信息及内核日志,对异常有个初步的认识。
3.1 vldr指令对齐异常
首先是一段异常简单的代码,这段代码中定义了一个浮点指针,然后更改了它的地址为一个未对齐的地址,给它赋值然后打印出来。
#include
void main()
{
char __attribute__ ((aligned(32))) buff[8] = {0};
float *flap = (float *)(buff+1);
*flap = 0.12345;
printf("float addr is %p\n", flap);
printf("float at &buff[1] is %f\n", *flap);
}
接下来编译,并运行。
imx6下:
树莓派下:
arm64下:
3.2 异常代码分析
从上一节看出,只有arm64平台下面vldr程序可以正常运行。从应用程序打印信息看,在arm32平台下,由于是32位寻址空间,flap指针的地址,分别对应为0x7edf79e1和0xbee0b541,在arm64平台下,flap指针的地址为0x7fdc5b8c61(超过32位)。在运行程序出现异常后,通过dmesg命令查得异常信息如下。
imx6:
root@imx6q-sbc:~/alignment# dmesg | tail
...
[ 4594.164653] Alignment trap: not handling instruction edd37a00 at [<00010410>]
[ 4594.170503] Unhandled fault: alignment exception (0x011) at 0x7edf79e1
树莓派:
pi@raspberrypi:~/alignment $ dmesg | tail
...
[ 9688.413139] Alignment trap: not handling instruction edd37a00 at [<0001045c>]
[ 9688.413155] Unhandled fault: alignment exception (0x221) at 0xbee0b541
[ 9688.413177] pgd = 36a06f3e
[ 9688.413193] [bee0b541] *pgd=19bd2003, *pmd=7fd3c003
从内核日志中可以看出,在0x7edf79e1地址与0xbee0b541分别产生了对齐异常,异常指令均为edd37a00 。然而,同样的程序,arm64平台并没有产生异常,接下来对比与arm32平台下的指令差异。
首先,用objdump工具对vldr程序进行反汇编,然后用compare工具对arm32平台(树莓派)和arm64平台下汇编代码进行对比分析。
可以看出,arm32平台下,用了vldr s15, [r3]指令,。其中s15为arm架构处理器下浮点寄存器,vldr指令为将r3寄存器中对应地址存储的数据取出,放到浮点寄存器s15中。根据上面程序分析,r3寄存器存储的地址为0xbee0b541,通过vldr指令取该地址的数据时,因为地址未对齐,产生了异常。
而在arm64平台下,因为编译器的不同,编译生成的是用ldr s0, [x0]指令,则没有这个问题。假如在arm64平台下,运行同样的非对齐vldr指令,是否会产生异常呢?实测是跟arm32情况一致,同样产生异常。如下图所示,用32位编译器编译同样代码后,在arm64平台下运行,同样报Bus error。
对应dmesg信息如下。
root@ARMv8:~/alignment# dmesg | tail
...
[ 6594.336469] Unhandled fault: alignment fault (0x92000021) at 0x00000000ffc50e81
文章中如有错误,请指正。