指示对Clang的未对齐访问以实现ARM兼容性

时间:2012-02-07 23:44:14

标签: c gcc arm clang

我正在通过将文件映射到内存并通过C结构定义访问它来解析文件格式。文件格式使用压缩结构,因此我无法保证字段将与字边界对齐。

解析工作正常,遗憾的是在某些情况下优化器可能会造成严重破坏。特别是,为armv7编译时,某些加载指令需要字对齐,而其他指令则不需要。请考虑以下代码段:

#define PACKED __attribute__((packed))

typedef struct PACKED _Box_mvhd {
    union {
        struct {
            int32_t creation_time;
            int32_t modification_time;
            int32_t time_scale;
            int32_t duration;
            ...
        } v0;
    } data;
} Box_mvhd;

Container mvhd = find_single_box(&moov, 'mvhd');
if (mvhd.boxStart) {
    Box_mvhd *mvhdBox = mvhd.mvhd;
    if (0 == mvhdBox.box.version) {
        uint32_t ts = ntohl(mvhdBox->data.v0.time_scale);
        uint32_t dur = ntohl(mvhdBox->data.v0.duration);
        ...
    }
}

-O0(调试)中,最里面的块作为以下程序集发出,它可以正常工作:

ldr r1, [r0, #24]
ldr r2, [r0, #20]

-O2中,编译器会意识到这些字段是相邻的并生成此程序集:

ldrdeq  r2, r3, [r0, #20]

不幸的是LDRD始终会产生对齐错误(按规格并在实践中)。所以我需要一种方法来有效地告知编译器这个问题。理想情况下,这可以通过结构上的属性来完成。这也可能是编译器或ARM后端的错误,但我会给他们带来疑问的好处。

我正在使用针对iPhone的armv7的Xcode 4.2(clang 3.0)进行编译。

2 个答案:

答案 0 :(得分:4)

问题不在于结构的字段没有必需的对齐,而是你正在向指向结构的指针投射任意指针,而你正在投射的指针没有需要对齐结构。严格来说,这是未定义的行为。

相反,memcpy您的数据从源缓冲区到您的结构。 memcpy速度很快,并保证能够处理你抛出的任何对齐。

答案 1 :(得分:0)

实际上,对齐打包结构的未对齐指针是完全正常的。但是您需要确保在编译期间正确设置了-mcpu-march参数。某些ARM CPU支持未对齐ldrd,有些则不支持。

是的,未对齐访问是非标准的和编译器扩展,但除非您将指向打包结构的成员的指针偶然传递给另一个函数,否则它是完全安全的。

这里的问题似乎是union中的struct没有被声明为packed。您可以使用alignof运算符验证结构成员的对齐方式。它类似于sizeof运算符。如果alignof运算符告诉您,结构及其成员的对齐是1并且gcc仍然使用ldrd发出代码,那么您要么面临编译器错误或命令行选项错误。

另请注意,-O0的代码可能错误开头! (除非您的目标arm架构支持未对齐ldr)。使用packed属性会将struct成员的对齐重置为1,这意味着clang应该发出八条ldrb指令而不是两条ldr

是的,正如其他人已经告诉过你的那样,从普通C访问未对齐数据的唯一安全且可移植的方式,没有任何编译器扩展,并且没有难以发现错误的风险(比如将未对齐的指针传递给函数)是使用memcpy