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); 0x00000000004005e4 <+0>: fd 7b bd a9 stp x29, x30, [sp, #-48]! 0x00000000004005e8 <+4>: fd 03 00 91 mov x29, sp 0x00000000004005ec <+8>: f3 53 01 a9 stp x19, x20, [sp, #16] 0x00000000004005f0 <+12>: 94 00 00 b0 adrp x20, 0x411000 <__libc_start_main@got.plt> 0x00000000004005f4 <+16>: 13 00 00 90 adrp x19, 0x400000 0x00000000004005f8 <+20>: 81 32 40 b9 ldr w1, [x20, #48] 0x00000000004005fc <+24>: 73 c2 1b 91 add x19, x19, #0x6f0 0x0000000000400600 <+28>: f5 13 00 f9 str x21, [sp, #32] 0x0000000000400604 <+32>: 95 c2 00 91 add x21, x20, #0x30 0x0000000000400608 <+36>: e0 03 13 aa mov x0, x19 0x000000000040060c <+40>: a1 ff ff 97 bl 0x400490 <printf@plt> 22 g_status.idx++; 0x0000000000400610 <+44>: a0 0a 40 39 ldrb w0, [x21, #2] 0x0000000000400614 <+48>: 00 1c 00 12 and w0, w0, #0xff 0x0000000000400618 <+52>: 00 04 00 11 add w0, w0, #0x1 0x000000000040061c <+56>: 00 1c 00 12 and w0, w0, #0xff 0x0000000000400620 <+60>: a0 0a 00 39 strb w0, [x21, #2] 23 printf("status.value = 0x%08x\n", g_status.value); 0x0000000000400624 <+64>: e0 03 13 aa mov x0, x19 0x0000000000400628 <+68>: 81 32 40 b9 ldr w1, [x20, #48] 0x000000000040062c <+72>: f3 53 41 a9 ldp x19, x20, [sp, #16] 0x0000000000400630 <+76>: f5 13 40 f9 ldr x21, [sp, #32] 0x0000000000400634 <+80>: fd 7b c3 a8 ldp x29, x30, [sp], #48 0x0000000000400638 <+84>: 96 ff ff 17 b 0x400490 <printf@plt> End of assembler dump.
从上边的汇编代码可以看到,在生成的代码中,系统调用了 ldur指令,这个指令是不带对齐的.
0x0000000000400610 <+44>: a0 0a 40 39 ldrb w0, [x21, #2] 0x0000000000400614 <+48>: 00 1c 00 12 and w0, w0, #0xff 0x0000000000400618 <+52>: 00 04 00 11 add w0, w0, #0x1 0x000000000040061c <+56>: 00 1c 00 12 and w0, w0, #0xff 0x0000000000400620 <+60>: a0 0a 00 39 strb w0, [x21, #2]
上面这个片段就是从内存中读取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); 0x00000000004005e4 <+0>: fd 7b be a9 stp x29, x30, [sp, #-32]! 0x00000000004005e8 <+4>: fd 03 00 91 mov x29, sp 0x00000000004005ec <+8>: f3 53 01 a9 stp x19, x20, [sp, #16] 0x00000000004005f0 <+12>: 93 00 00 b0 adrp x19, 0x411000 <__libc_start_main@got.plt> 0x00000000004005f4 <+16>: 14 00 00 90 adrp x20, 0x400000 0x00000000004005f8 <+20>: 61 32 40 b9 ldr w1, [x19, #48] 0x00000000004005fc <+24>: 94 a2 1b 91 add x20, x20, #0x6e8 0x0000000000400600 <+28>: e0 03 14 aa mov x0, x20 0x0000000000400604 <+32>: a3 ff ff 97 bl 0x400490 <printf@plt> 22 g_status.idx++; 0x0000000000400608 <+36>: 60 32 40 b9 ldr w0, [x19, #48] 0x000000000040060c <+40>: 61 32 40 b9 ldr w1, [x19, #48] 0x0000000000400610 <+44>: 00 5c 50 d3 ubfx x0, x0, #16, #8 0x0000000000400614 <+48>: 00 04 00 11 add w0, w0, #0x1 0x0000000000400618 <+52>: 01 1c 10 33 bfi w1, w0, #16, #8 0x000000000040061c <+56>: 61 32 00 b9 str w1, [x19, #48] 23 printf("status.value = 0x%08x\n", g_status.value); 0x0000000000400620 <+60>: e0 03 14 aa mov x0, x20 0x0000000000400624 <+64>: 61 32 40 b9 ldr w1, [x19, #48] 0x0000000000400628 <+68>: f3 53 41 a9 ldp x19, x20, [sp, #16] 0x000000000040062c <+72>: fd 7b c2 a8 ldp x29, x30, [sp], #32 0x0000000000400630 <+76>: 98 ff ff 17 b 0x400490 <printf@plt> End of assembler dump.
同样的操作,g_status.idx++;在这里就变成了使用ldr指令的对齐的访问: