Generate Aligned Access Code
Table of Contents
用途
当使用C中的bit field时,编译器在编译时会生成按byte,字长等来访问的指令,对于普通的内存空间,这个都是可以正常使用的。
但是对于ARM平台中的寄存器,由于总线的限制,访问时必须按照32bit来访问, 当使用ldrb访问时,系统会发生异常.
例子
源代码
#include <stdio.h>#include <stdint.h>struct device_status {union {struct {uint32_t length : 12;uint32_t is_valid : 4;uint32_t idx: 8;};uint32_t value;};} __attribute__((packed, aligned(4)));volatile struct device_status g_status = {.value = 0x1238,};void bitfield_test(){printf("status.value = 0x%08x\n", g_status.value);g_status.idx++;printf("status.value = 0x%08x\n", g_status.value);}int main(int argc, char *argv[]){bitfield_test();return 0;}
先看一下数据结构
/* offset | size */ type = volatile struct device_status {/* 0 | 4 */ union {/* 4 */ struct {/* 0: 0 | 4 */ uint32_t length : 12;/* 1: 4 | 4 */ uint32_t is_valid : 4;/* 2: 0 | 4 */ uint32_t idx : 8;/* XXX 1-byte padding *//* total size (bytes): 4 */};/* 4 */ uint32_t value;/* total size (bytes): 4 */};/* total size (bytes): 4 */}
Normal Compile
在编译时通过指定 -mno-strict-align -fno-strict-volatile-bitfields 的编译参数,来防止编译器版本的不同而进行自动优化。
Dump of assembler code for function bitfield_test:./test.c:20 {21 printf("status.value = 0x%08x\n", g_status.value);0x00000000004005b4 <+0>: fd 7b bd a9 stp x29, x30, [sp, #-48]!0x00000000004005b8 <+4>: fd 03 00 91 mov x29, sp0x00000000004005bc <+8>: f3 53 01 a9 stp x19, x20, [sp, #16]0x00000000004005c0 <+12>: 94 00 00 b0 adrp x20, 0x411000 <__libc_start_main@got.plt>0x00000000004005c4 <+16>: 13 00 00 90 adrp x19, 0x4000000x00000000004005c8 <+20>: 81 32 40 b9 ldr w1, [x20, #48]0x00000000004005cc <+24>: 73 02 1b 91 add x19, x19, #0x6c020 {0x00000000004005d0 <+28>: f5 13 00 f9 str x21, [sp, #32]21 printf("status.value = 0x%08x\n", g_status.value);0x00000000004005d4 <+32>: 95 c2 00 91 add x21, x20, #0x300x00000000004005d8 <+36>: e0 03 13 aa mov x0, x190x00000000004005dc <+40>: ad ff ff 97 bl 0x400490 <printf@plt>22 g_status.idx++;0x00000000004005e0 <+44>: a1 0a 40 39 ldrb w1, [x21, #2]23 printf("status.value = 0x%08x\n", g_status.value);0x00000000004005e4 <+48>: e0 03 13 aa mov x0, x1922 g_status.idx++;0x00000000004005e8 <+52>: 21 1c 00 12 and w1, w1, #0xff0x00000000004005ec <+56>: 21 04 00 11 add w1, w1, #0x10x00000000004005f0 <+60>: 21 1c 00 12 and w1, w1, #0xff0x00000000004005f4 <+64>: a1 0a 00 39 strb w1, [x21, #2]23 printf("status.value = 0x%08x\n", g_status.value);0x00000000004005f8 <+68>: f5 13 40 f9 ldr x21, [sp, #32]0x00000000004005fc <+72>: 81 32 40 b9 ldr w1, [x20, #48]24 }0x0000000000400600 <+76>: f3 53 41 a9 ldp x19, x20, [sp, #16]0x0000000000400604 <+80>: fd 7b c3 a8 ldp x29, x30, [sp], #4823 printf("status.value = 0x%08x\n", g_status.value);0x0000000000400608 <+84>: a2 ff ff 17 b 0x400490 <printf@plt>End of assembler dump.
从上边的汇编代码可以看到,在生成的代码中,系统调用了 ldrb,strb指令,这个指令是不带对齐的.
上面这个片段就是从内存中读取g_status.idx 并对其加1操作,最后存入内存中.
上述代码片段中使用了ldrb的指令,在访问AHB接口寄存器的时候,会出现问题。
Compile with aligned access
下面是通过 -mstrict-align -fstrict-volatile-bitfields 参数进行编译的结果:
Dump of assembler code for function bitfield_test:./test.c:20 {21 printf("status.value = 0x%08x\n", g_status.value);0x00000000004005b4 <+0>: fd 7b be a9 stp x29, x30, [sp, #-32]!0x00000000004005b8 <+4>: fd 03 00 91 mov x29, sp0x00000000004005bc <+8>: f3 53 01 a9 stp x19, x20, [sp, #16]0x00000000004005c0 <+12>: 93 00 00 b0 adrp x19, 0x411000 <__libc_start_main@got.plt>0x00000000004005c4 <+16>: 14 00 00 90 adrp x20, 0x4000000x00000000004005c8 <+20>: 61 32 40 b9 ldr w1, [x19, #48]0x00000000004005cc <+24>: 94 02 1b 91 add x20, x20, #0x6c00x00000000004005d0 <+28>: e0 03 14 aa mov x0, x200x00000000004005d4 <+32>: af ff ff 97 bl 0x400490 <printf@plt>22 g_status.idx++;0x00000000004005d8 <+36>: 61 32 40 b9 ldr w1, [x19, #48]0x00000000004005dc <+40>: 60 32 40 b9 ldr w0, [x19, #48]0x00000000004005e0 <+44>: 21 5c 50 d3 ubfx x1, x1, #16, #80x00000000004005e4 <+48>: 21 04 00 11 add w1, w1, #0x10x00000000004005e8 <+52>: 20 1c 10 33 bfi w0, w1, #16, #80x00000000004005ec <+56>: 60 32 00 b9 str w0, [x19, #48]23 printf("status.value = 0x%08x\n", g_status.value);0x00000000004005f0 <+60>: e0 03 14 aa mov x0, x200x00000000004005f4 <+64>: 61 32 40 b9 ldr w1, [x19, #48]24 }0x00000000004005f8 <+68>: f3 53 41 a9 ldp x19, x20, [sp, #16]0x00000000004005fc <+72>: fd 7b c2 a8 ldp x29, x30, [sp], #3223 printf("status.value = 0x%08x\n", g_status.value);0x0000000000400600 <+76>: a4 ff ff 17 b 0x400490 <printf@plt>End of assembler dump.
同样的操作,g_status.idx++;在这里就变成了使用ldr指令的对齐的访问: