我正在通过将文件映射到内存并通过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)进行编译。
答案 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
。