编译臂代码的严重铿锵声

时间:2018-05-23 09:18:12

标签: android-ndk arm clang

昨天,当我试图为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.wstm.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说明符。正如standard指定的那样,__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 *作为输入来创建正确的代码。但效率方面,如果这个问题得到解决会更好。

1 个答案:

答案 0 :(得分:4)

FWIW, clang ,与A​​RM C编译器不同,不允许__packed指针。对于 clang __packed__attribute__((__packed__))的同义词,仅适用于枚举,结构或联合:http://gcc.gnu.org/onlinedocs/gcc-3.3/gcc/Type-Attributes.html