解码器非对齐内存访问的问题

内存对齐是什么?

比如说要访问一个字节,那么要求访问的地址必须是字节对齐的。

如果要访问2个字节,那么要求地址必须是2字节对齐。

以此类推,neon指令有32 64 128甚至256bit的对齐。

PC 的SSE要求128bit对齐,所以看到一些变量或数据被申明为16字节(128bit)对齐,AVX2则申明为32字节对齐。

不对齐会发生什么?

按理来说,不对齐大不了把不对齐的地方多用几次load指令,就是速度慢一些;但是对于一些arm v5和之下的机器,如果操作系统被设置为不能自动纠正非对齐,访问非对齐地址时就会触发 SIGBUS 中断。

绝大部分机器是支持自动纠正的,所以我们的代码之前也没有遇到过非对齐问题,但是一些比较旧又不支持自动纠正的手机会有这个问题,所以我们得修复。

最终我们的解决方案是?

首先有一点需要注意,C语言被编译成汇编时,有一些指令有LDRH(访问内存中2字节),这个指令如果要是字节对齐的话需要2字节对齐,在ARM V5的一个手机中将 /proc/cpu/alignment 设为5时,当执行到这条指令时会触发中断,然而在ARM V7的一个chrome笔记本上则根本不会触发中断,于是我们将C语言中所有要求数据对齐的指令改为只需1个字节对齐的指令,比如将LDRH改为2个LDRB(修改的是C语言,一些C语言数据结构是uint8_t的,一些C语言操作是将2个这样的数据结构同时加载,导致汇编出现了LDRH,我们将这些操作独立写就没有LDRH了)。

全部改完后,在ARM V5的机器上没有问题了,但是ARM V5不支持NEON指令,我们的一些NEON指令中一些手写的汇编显然没有字节对齐,而在ARM V5上也测不到NEON。

手写的NEON汇编中一些NEON执行也没有4字节对齐,查文档后知道,如果[rx]没有加@32的话,则不要求32bit对齐,如果是[rx@32]的话,则指明RX的地址是32bit对齐的,在指定对齐后,如果RX中的地址没有对齐,而且 /proc/cpu/alignment 为2的话,发现自动纠正的结果是不对的,如果RX中的地址没有对齐,且 /proc/cpu/alignment 为5的话,会触发 SIGBUS 中断。结论是,我们的NEON加载指令寄存器中的地址可以不对齐。

一些疑问

但是还有一些疑问,为什么NEON不对齐会被触发,普通的汇编则不会触发 SIGBUS 中断?

补充

如何连接Android手机

adb shell,连到android手机上,进入手机的shell,与linux操作类似;

但其实有时候链接不上,通过 adb devices 看能否看到设备,如果插了设备却看不到设备,说明没有驱动;

windows则装驱动,linux则需要自己建立一个文件,在 /etc/udev/rules.d/ 下创建一个配置文件: 50-android.rules ,写入

SUBSYSTEM=="usb", SYSFS{idVendor}=="22b8", MODE="0666",

中间的字符通过lsusb可以查到;

sudo chmod a+rx /etc/udev/rules.d/50-android.rules
sudo /etc/init.d/udev restart
sudo adb kill-server sudo adb devices

操作系统内存对齐的设置

cat /proc/cpu/alignment 可以查看操作系统对对齐访问的设置。

如果要操作系统纠正非对齐的访问,echo 2 > /proc/cpu/alignment

如果要操作系统对非对齐的内存访问发出中断信号,echo 5 > /proc/cpu/alignment

这里 kernel doc 有对 /proc/cpu/alignment 的详细介绍。

查看崩溃位置

dmesg,查看系统日志。

如果内存对齐没有被系统纠正,并且发出中断信号的话,会在这里记录一个PC地址,通过这个PC地址与程序基址相减,可以得到程序中触发中断的代码地址;

比如0Xfa32,我们可以通过反汇编查看这个地址的汇编语句,比如 objdump -dS libxxx.so > libxxx.so.txt 打开 libxxx.so.txt搜索fa32,应该就可以看到对应的汇编语句和相应的C语句,可以做出相应的修复调整。

Loading Disqus comments...
Table of Contents