工作中arm架构手机程序遇到崩溃,查看android logcat,显示“signal 7 (SIGBUS), code 1 (BUS_ADRALN), fault addr 0xab6b1f3a”,这是典型的c++非对齐内存访问导致的崩溃。
在ARM中,有ARM和Thumb两种指令。 ARM指令:CPU每执行一条指令,PC的值加4个字节(32bits),一次访问4字节内容,该字节的起始地址必须是4字节对齐的位置。 Thumb指令:CPU每执行一条指令,PC的值加4个字节(32bits),一次访问4字节内容,该字节的起始地址必须是4字节对齐的位置上。
在大多数情况下,对齐是编译器和CPU的事情,和程序员没什么关系。但在某些情况下,程序员又必须考虑对齐问题,否则会有一些麻烦。如果程序员在非对齐内存访问,可能会导致数据读取异常或崩溃,为什么说可能呢,因为有些CPU有对非对齐内存访问做了保护和处理,有些则没有。为了更直观地说明这个问题,请看下面的两个例子。
例子1:非对齐内存访问long long(8字节)
const int payloadlen = 16; unsigned char* payload = new unsigned char[payloadlen]; for (int i = 0; i < payloadlen; i++) payload[i] = i; for (int i = 0; i < payloadlen - sizeof(long long); i++) { long long* plonglong = (long long*)(payload + i); printf("plonglong:0x%x, *plonglong:%lld\n", plonglong, *plonglong); } delete[] payload;编译:android-ndk-r10d gcc 4.9 CPU:高通MSM8939,ARM 64位 8核 结果:崩溃,抛出文章开头的异常BUS_ADRALN。此款CPU对非对齐内存的8字节访问没有做保护,直接抛出异常。
例子2:非对齐内存访问int(4字节)
const int payloadlen = 16; unsigned char* payload = new unsigned char[payloadlen]; for (int i = 0; i < payloadlen; i++) payload[i] = i; for (int i = 0; i < payloadlen -sizeof(int); i++) { int* pint = (int*)(payload + i); printf("pint:0x%x, *pint:0x%x\n", pint, *pint); } delete[] payload;编译:android-ndk-r10d gcc 4.9 CPU:高通MSM8939,ARM 64位 8核 结果:数据正常显示输出。此款CPU对非对齐内存的4字节访问做了保护和处理。
那么,我们对于确实需要进行非对齐内存的访问怎么办呢,当然是乖乖按1字节进行内存访问了。
const int payloadlen = 16; unsigned char* payload = new unsigned char[payloadlen]; for (int i = 0; i < payloadlen; i++) payload[i] = i; for (int i = 0; i < payloadlen - sizeof(long long); i++) { long long* plonglong = (long long*)(payload + i); long long templonglong; memcpy(&templonglong, plonglong, sizeof(long long)); printf("plonglong:0x%x, *plonglong:%lld\n", plonglong, templonglong); } delete[] payload;