非对齐内存访问的害处
#pragma pack(1) 会造成内存非对齐访问,某些架构下会触发硬件非对齐访问错误,进而导致进程 coredump。有跨不同型号 cpu 运行需求的程序需要特别关注。
微软网站上,关于 pack(1) 就有如下描述:
https://docs.microsoft.com/en-us/cpp/preprocessor/pack?view=vs-2019
最近在分析 armhf(armv7)架构下程序的 coredump,碰巧遇到了两起这样的问题。
经过分析,简化出下面的代码,能够稳定复现问题。
1、非对齐的浮点变量内存访问会造成程序coredump
代码 test.c
1 |
float aligned[2]; |
2 |
float *unaligned = ( float *)((( char *)aligned) + 2); // 构造非对齐访问地址 |
3 |
4 |
int main( int argc, char **argv) |
5 |
{ |
6 |
float f = unaligned[0]; |
7 |
return ( int )f; |
8 |
} |
编译 /bin/arm-linux-gnueabihf/gcc -O2 -o test test.c
1 |
000102e0 <main>: |
2 |
102e0: f241 0324 movw r3, #4132 ; 0x1024 |
3 |
102e4: f2c0 0302 movt r3, #2 |
4 |
102e8: 681b ldr r3, [r3, #0] |
5 |
102ea: edd3 7a00 vldr s15, [r3] // coredump |
6 |
102ee: eefd 7ae7 vcvt.s32.f32 s15, s15 |
7 |
102f2: ee17 0a90 vmov r0, s15 |
8 |
102f6: 4770 bx lr |
2、非对齐的原子操作内存访问会造成程序coredump
代码 test.c
01 |
#include <pthread.h> |
02 |
03 |
pthread_mutex_t buf[2]; |
04 |
pthread_mutex_t *lock = (pthread_mutex_t*)(( char *)buf + 2); // 构造非对齐访问地址 |
05 |
06 |
void * thrfun( void * args) |
07 |
{ |
08 |
pthread_mutex_lock(lock); |
09 |
return NULL; |
10 |
} |
11 |
12 |
int main( int argc, char **argv) |
13 |
{ |
14 |
pthread_t thr; |
15 |
void *ret; |
16 |
pthread_create(&thr, NULL, thrfun, NULL); |
17 |
pthread_join(thr, &ret); |
18 |
return 0; |
19 |
} |
编译 /bin/arm-linux-gnueabihf/gcc -O2 -o test test.c -lpthread
01 |
00007350 <__pthread_mutex_lock>: |
02 |
7350: b570 push {r4, r5, r6, lr} |
03 |
7352: f240 137f movw r3, #383 ; 0x17f |
04 |
7356: 68c2 ldr r2, [r0, #12] |
05 |
7358: b082 sub sp, #8 |
06 |
735a: 4013 ands r3, r2 |
07 |
735c: bf00 nop |
08 |
735e: f012 017c ands.w r1, r2, #124 ; 0x7c |
09 |
7362: d128 bne.n 73b6 <__pthread_mutex_lock+0x66> |
10 |
7364: 4604 mov r4, r0 |
11 |
7366: 2b00 cmp r3, #0 |
12 |
7368: d129 bne.n 73be <__pthread_mutex_lock+0x6e> |
13 |
736a: 9301 str r3, [sp, #4] |
14 |
736c: 2301 movs r3, #1 |
15 |
736e: e854 2f00 ldrex r2, [r4] // coredump |
16 |
7372: 2a00 cmp r2, #0 |
17 |
7374: d105 bne.n 7382 <__pthread_mutex_lock+0x32> |
18 |
7376: e844 3100 strex r1, r3, [r4] |
19 |
737a: 2900 cmp r1, #0 |
20 |
737c: d1f7 bne.n 736e <__pthread_mutex_lock+0x1e> |
21 |
737e: f3bf 8f5b dmb ish |
22 |
... |
汇编指令 vldr 和 ldrex 会要求被操作的内存地址必须按照操作数大小对齐(int 和 float 地址按4字节对齐),所以上面两个程序在 armhf 架构下编译运行,必 core。
https://www.keil.com/support/man/docs/armasm/armasm_dom1359731171041.htm
经过上述分析,总结了下面的几条实用指南:
1、pack(1) 数据结构中,只允许出现非浮点数的 POD 类型(非float、double、long double等);
2、pack(1) 数据结构的成员不能参与浮点运算;
3、为了安全起见,pack(1) 数据结构中,最好也不出现 8 字节类型(比如 uint64_t),最大 uint32_t;
4、非对齐内存访问还是挺危险的,pack(1) 能不用就不用。
网上关于非对齐内存访问,有如下几篇可以参考的帖子:
- 点赞
- 收藏
- 关注作者
评论(0)