昨天,当我尝试为 arm 编译代码时,我在clang
中发现了一个灾难性的问题(至少在 android arm-v7a 中(。请参阅此小代码:
void init_c_32(uint8_t *ptr)
{
uint32_t tmp[SIZE];
memcpy(tmp, ptr, 33);
}
下面是在此处调用memcpy
生成的程序集代码:
0x7903d714 <+20>: ldr r0, [sp, #0x10]
0x7903d716 <+22>: add r3, sp, #0x14
0x7903d718 <+24>: mov.w r12, #0x20
0x7903d71c <+28>: str r0, [sp, #0xc]
0x7903d71e <+30>: mov r0, r3
0x7903d720 <+32>: ldr r3, [sp, #0xc]
0x7903d722 <+34>: str r1, [sp, #0x8]
0x7903d724 <+36>: mov r1, r3
0x7903d726 <+38>: str r2, [sp, #0x4]
0x7903d728 <+40>: mov r2, r12
0x7903d72a <+42>: blx 0x7903d658 ; symbol stub for: __aeabi_memcpy
它使用__aeabi_memcpy
,对于任何ptr
地址来说一切都会好起来的。现在,如果我们将参数类型更改为uint32_t *
,生成的汇编代码将更改如下:
void init_c_32(uint32_t *ptr)
{
uint32_t tmp[SIZE];
memcpy(tmp, ptr, 33);
}
0x790456dc <+20>: ldr r0, [sp, #0x8]
0x790456de <+22>: add r3, sp, #0xc
0x790456e0 <+24>: ldm.w r0!, {r4, r5, r12, lr}
0x790456e4 <+28>: stm.w r3!, {r4, r5, r12, lr}
0x790456e8 <+32>: ldm.w r0, {r4, r5, r12, lr}
0x790456ec <+36>: stm.w r3, {r4, r5, r12, lr}
这段代码经过大量优化,使用ldm.w
和stm.w
而不是memcpy
。结果是一个更快的代码,但有一个缺点。此代码将无法在奇数ptr
地址中正常工作SIGBUS
并创建基于生成的汇编代码的正确异常。.w
寻址将寻址模型限制为偶数值,但也许我们可以说这是设计使然,因为我们将参数定义为unit32_t *
并且我们说此参数必须对齐。
但主要问题发生在这里。检查以下代码:
void init_c_32(__packed uint32_t *ptr)
{
uint32_t tmp[SIZE];
memcpy(tmp, ptr, 33);
}
如您所见,事件虽然我们已指定uint32_t *
作为输入参数,但我们使用了__packed
说明符。按照标准的规定,__packed
说:
打包类型的对象使用未对齐的访问进行读取或写入。
但是当我们看到生成的汇编代码时,我们看到以下内容:
0x78ec56dc <+20>: ldr r0, [sp, #0x8]
0x78ec56de <+22>: add r3, sp, #0xc
0x78ec56e0 <+24>: ldm.w r0!, {r4, r5, r12, lr}
0x78ec56e4 <+28>: stm.w r3!, {r4, r5, r12, lr}
0x78ec56e8 <+32>: ldm.w r0, {r4, r5, r12, lr}
0x78ec56ec <+36>: stm.w r3, {r4, r5, r12, lr}
如您所见,生成的代码与非__packed
模式没有区别,这与ARM
标准冲突。您仍然无法使用奇数地址进行引用,并且会得到SIGBUS
例外。我认为在这种情况下生成的代码应该类似于我们使用uint8_t *
作为参数时。
我认为这是一个非常严重的错误,可能会产生意想不到的结果,欢迎任何好的解决方案。
我为此使用了 ndk 16 来创建这个问题,它使用clang 5.0.3
作为其编译器。
当前的解决方法是始终使用uint8_t *
作为输入,从而创建正确的代码。但从效率上讲,如果这个问题得到解决会更好。
FWIW,与 ARM C 编译器不同,clang不允许__packed
指针。对于clang,__packed
是__attribute__((__packed__))
的同义词,仅适用于枚举、结构或联合:http://gcc.gnu.org/onlinedocs/gcc-3.3/gcc/Type-Attributes.html。