Yes, __attribute__((packed))
在某些系统上可能不安全。该症状可能不会出现在 x86 上,这只会使问题变得更加隐蔽;在 x86 系统上进行测试不会揭示该问题。 (在 x86 上,未对齐的访问由硬件处理;如果您取消引用int*
指向奇数地址的指针,它会比正确对齐时慢一点,但您会得到正确的结果。)
在某些其他系统(例如 SPARC)上,尝试访问未对齐的int
对象导致总线错误,导致程序崩溃。
还有一些系统,未对齐的访问会悄悄地忽略地址的低位,导致它访问错误的内存块。
考虑以下程序:
#include <stdio.h>
#include <stddef.h>
int main(void)
{
struct foo {
char c;
int x;
} __attribute__((packed));
struct foo arr[2] = { { 'a', 10 }, {'b', 20 } };
int *p0 = &arr[0].x;
int *p1 = &arr[1].x;
printf("sizeof(struct foo) = %d\n", (int)sizeof(struct foo));
printf("offsetof(struct foo, c) = %d\n", (int)offsetof(struct foo, c));
printf("offsetof(struct foo, x) = %d\n", (int)offsetof(struct foo, x));
printf("arr[0].x = %d\n", arr[0].x);
printf("arr[1].x = %d\n", arr[1].x);
printf("p0 = %p\n", (void*)p0);
printf("p1 = %p\n", (void*)p1);
printf("*p0 = %d\n", *p0);
printf("*p1 = %d\n", *p1);
return 0;
}
在使用 gcc 4.5.2 的 x86 Ubuntu 上,它会生成以下输出:
sizeof(struct foo) = 5
offsetof(struct foo, c) = 0
offsetof(struct foo, x) = 1
arr[0].x = 10
arr[1].x = 20
p0 = 0xbffc104f
p1 = 0xbffc1054
*p0 = 10
*p1 = 20
在带有 gcc 4.5.1 的 SPARC Solaris 9 上,它会生成以下内容:
sizeof(struct foo) = 5
offsetof(struct foo, c) = 0
offsetof(struct foo, x) = 1
arr[0].x = 10
arr[1].x = 20
p0 = ffbff317
p1 = ffbff31c
Bus error
在这两种情况下,程序的编译都没有额外的选项,只是gcc packed.c -o packed
.
(使用单个结构而不是数组的程序不能可靠地表现出该问题,因为编译器可以在奇数地址上分配结构,因此x
成员已正确对齐。有两个数组struct foo
物体,至少其中一个会出现未对准的情况x
成员。)
(在这种情况下,p0
指向未对齐的地址,因为它指向压缩的int
会员关注char
成员。p1
恰好是正确对齐的,因为它指向数组第二个元素中的同一个成员,所以有两个char
其前面的对象 — 以及 SPARC Solaris 上的阵列arr
似乎分配在偶数地址,但不是 4 的倍数。)
提及会员时x
of a struct foo
通过名称,编译器知道x
可能未对齐,并将生成额外的代码以正确访问它。
一旦地址为arr[0].x
or arr[1].x
已存储在指针对象中,编译器和正在运行的程序都不知道它指向未对齐的int
目的。它只是假设它已正确对齐,从而导致(在某些系统上)总线错误或类似的其他故障。
我认为在 gcc 中解决这个问题是不切实际的。对于每次尝试取消引用具有重要对齐要求的任何类型的指针,通用解决方案都需要(a)在编译时证明该指针不指向打包结构的未对齐成员,或者(b)生成更庞大、更慢的代码,可以处理对齐或未对齐的对象。
我已经提交了一份海湾合作委员会错误报告。正如我所说,我不认为修复它是实际的,但文档应该提到它(目前没有)。
UPDATE:截至 2018 年 12 月 20 日,此错误已标记为“已修复”。该补丁将出现在 gcc 9 中,并添加新的-Waddress-of-packed-member
选项,默认启用。
当获取结构体或联合体的压缩成员的地址时,可以
导致未对齐的指针值。这个补丁增加了
-Waddress-of-packed-member 检查指针分配时的对齐情况并警告未对齐的地址以及未对齐的指针
我刚刚从源代码构建了该版本的 gcc。对于上述程序,它会生成以下诊断信息:
c.c: In function ‘main’:
c.c:10:15: warning: taking address of packed member of ‘struct foo’ may result in an unaligned pointer value [-Waddress-of-packed-member]
10 | int *p0 = &arr[0].x;
| ^~~~~~~~~
c.c:11:15: warning: taking address of packed member of ‘struct foo’ may result in an unaligned pointer value [-Waddress-of-packed-member]
11 | int *p1 = &arr[1].x;
| ^~~~~~~~~