是gcc的__attribute __((包装))/包的#pragma不安全? [英] Is gcc's __attribute__((packed)) / #pragma pack unsafe?

查看:123
本文介绍了是gcc的__attribute __((包装))/包的#pragma不安全?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C,编译器会铺陈在他们所声明的顺序一个结构的成员,成员之间可以插入填充字节,或之后的最后一个成员,以确保每个部件正确对齐。

In C, the compiler will lay out members of a struct in the order in which they're declared, with possible padding bytes inserted between members, or after the last member, to ensure that each member is aligned properly.

GCC提供了语言扩展, __ __属性(包装),它告诉编译器不要插入填充,使结构成员无法对齐。例如,如果系统通常需要将所有 INT 对象有4个字节对齐,`__attribute __(包装) CAN事业 INT 在奇数偏移分配结构成员。

gcc provides a language extension, __attribute__(packed), which tells the compiler not to insert padding, allowing struct members to be misaligned. For example, if the system normally requires all int objects to have 4-byte alignment, `__attribute__(packed) can cause int struct members to be allocated at odd offsets.

引用gcc的文档:

在'包装'属性指定变量或结构字段
  应该有尽可能小的对齐方式 - 一个字节一个变量,
  和一个位的字段,除非你指定的一个较大的值
  '对齐'属性。

The `packed' attribute specifies that a variable or structure field should have the smallest possible alignment--one byte for a variable, and one bit for a field, unless you specify a larger value with the `aligned' attribute.

显然,使用这个扩展可以导致较小的数据要求,但速度慢code,因为编译器必须(在某些平台上)生成code在同一时间访问一个对齐成员的字节。

Obviously the use of this extension can result in smaller data requirements but slower code, as the compiler must (on some platforms) generate code to access a misaligned member a byte at a time.

但是否有任何情况下,这是不安全的?难道编译器总是生成正确的(虽然速度较慢)code访问包装结构的错位成员?它甚至有可能为它在所有情况下这样做呢?

But are there any cases where this is unsafe? Does the compiler always generate correct (though slower) code to access misaligned members of packed structs? Is it even possible for it to do so in all cases?

推荐答案

__ __属性((包装))是在某些系统上可能不安全。症状可能不会在x86上,这只是使问题更加阴险的出现;测试在x86系统上不会发现问题。 (在x86,未对齐的访问是通过硬件来处理;如果取消引用指向一个奇地址的为int * 指针,它会比如果它是正确的慢一点对齐,但你会得到正确的结果。)

Yes, __attribute__((packed)) is potentially unsafe on some systems. The symptom probably won't show up on an x86, which just makes the problem more insidious; testing on x86 systems won't reveal the problem. (On the x86, misaligned accesses are handled in hardware; if you dereference an int* pointer that points to an odd address, it will be a little slower than if it were properly aligned, but you'll get the correct result.)

在其他一些系统,如SPARC,试图访问未对齐的 INT 对象会导致总线错误,崩溃的程序。

On some other systems, such as SPARC, attempting to access a misaligned int object causes a bus error, crashing the program.

也有一些系统中一个未对齐的访问悄悄地忽略地址的低位,导致它访问的内存错误的块。

There have also been systems where a misaligned access quietly ignores the low-order bits of the address, causing it to access the wrong chunk of memory.

考虑下面的程序:

#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;
}

在86 Ubuntu的使用GCC 4.5.2,它产生以下输出:

On x86 Ubuntu with gcc 4.5.2, it produces the following output:

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

在SPARC Solaris的9用gcc 4.5.1,它会产生以下内容:

On SPARC Solaris 9 with gcc 4.5.1, it produces the following:

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 -o packed.c包装

In both cases, the program is compiled with no extra options, just gcc packed.c -o packed.

(即使用一个单一的结构,而不是阵列不能可靠地出现问题,因为编译器可以在一个奇怪的地址,以便 X 成员分配结构的程序正确对齐。有两个结构美孚的对象,至少一个或另一个将有一个错位的数组x 成员。)

(A program that uses a single struct rather than array doesn't reliably exhibit the problem, since the compiler can allocate the struct on an odd address so the x member is properly aligned. With an array of two struct foo objects, at least one or the other will have a misaligned x member.)

(在这种情况下, P0 指向未对齐的地址,因为它指向一个包装 INT 成员之后一个字符成员。 P1 恰好是正确对齐,因为它指向同一成员中的第二个元素阵,所以有两个字符对象preceding它 - 对SPARC Solaris的数组改编出现在是偶数的地址被分配,而不是4的倍数)。

(In this case, p0 points to a misaligned address, because it points to a packed int member following a char member. p1 happens to be correctly aligned, since it points to the same member in the second element of the array, so there are two char objects preceding it -- and on SPARC Solaris the array arr appears to be allocated at an address that is even, but not a multiple of 4.)

当提及成员 X 结构美孚按名称,编译器知道 X 是潜在的对齐,并会产生额外的code正确地访问它。

When referring to the member x of a struct foo by name, the compiler knows that x is potentially misaligned, and will generate additional code to access it correctly.

的改编地址[0] .X 改编[1] .X 已被存储在一个指针对象,无论是编译器和运行程序知道它指向一个对齐 INT 对象。它只是假设它是在一个总线错误或类似的其他故障正确对齐,导致(在某些系统上)。

Once the address of arr[0].x or arr[1].x has been stored in a pointer object, neither the compiler nor the running program knows that it points to a misaligned int object. It just assumes that it's properly aligned, resulting (on some systems) in a bus error or similar other failure.

在GCC修复这会,我相信,是不切实际的。一般的解决方案需要为每个尝试取消引用指针,任何类型的非平凡的对齐要求是(a)在编译时证明指针不指向包装结构的错位成员,或(b)产生笨重和$ C较慢$ C,可以处理任何对齐或不对齐的对象。

Fixing this in gcc would, I believe, be impractical. A general solution would require, for each attempt to dereference a pointer to any type with non-trivial alignment requirements either (a) proving at compile time that the pointer doesn't point to a misaligned member of a packed struct, or (b) generating bulkier and slower code that can handle either aligned or misaligned objects.

我提交了 gcc的bug报告。正如我所说,我不相信它的实用性来解决它,但文档应该提到它(它目前没有)。

I've submitted a gcc bug report. As I said, I don't believe it's practical to fix it, but the documentation should mention it (it currently doesn't).

这篇关于是gcc的__attribute __((包装))/包的#pragma不安全?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆