gcc、严格别名和恐怖故事 [英] gcc, strict-aliasing, and horror stories

查看:35
本文介绍了gcc、严格别名和恐怖故事的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

gcc-strict-aliasing-and-casting-through-a-union 我问是否有人遇到过通过指针进行联合双关语的问题.到目前为止,答案似乎是.

In gcc-strict-aliasing-and-casting-through-a-union I asked whether anyone had encountered problems with union punning through pointers. So far, the answer seems to be No.

这个问题更广泛:你有任何关于 gcc 和严格别名的恐怖故事吗?

This question is broader: Do you have any horror stories about gcc and strict-aliasing?

背景:引用 AndreyT 在 c99-strict 中的回答-aliasing-rules-in-c-gcc:

"严格的别名规则植根于自 [标准化] 时代开始以来 C 和 C++ 中存在的部分标准.禁止通过另一种类型的左值访问一种类型的对象的条款存在于 C89/90 (6.3) 以及 C++98 (3.10/15)....只是并非所有编译器都希望(或敢于)强制执行或依赖它."

"Strict aliasing rules are rooted in parts of the standard that were present in C and C++ since the beginning of [standardized] times. The clause that prohibits accessing object of one type through a lvalue of another type is present in C89/90 (6.3) as well as in C++98 (3.10/15). ... It is just that not all compilers wanted (or dared) to enforce it or rely on it."

好吧,gcc 现在敢于这样做,它的 -fstrict-aliasing 开关.这造成了一些问题.例如,请参阅优秀文章 http://davmac.wordpress.com/2009/10/ 关于 Mysql 错误,以及 http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html.

Well, gcc is now daring to do so, with its -fstrict-aliasing switch. And this has caused some problems. See, for example, the excellent article http://davmac.wordpress.com/2009/10/ about a Mysql bug, and the equally excellent discussion in http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html.

其他一些不太相关的链接:

Some other less-relevant links:

再说一遍,你有自己的恐怖故事吗?由 -Wstrict-aliasing 指出的问题当然是首选.也欢迎使用其他 C 编译器.

So to repeat, do you have a horror story of your own? Problems not indicated by -Wstrict-aliasing would, of course, be preferred. And other C compilers are also welcome.

6 月 2 日添加:Michael Burr 的回答中的第一个链接a>,它确实符合恐怖故事的标准,可能有点过时(从 2003 年开始).我做了一个快速测试,但问题显然已经消失了.

Added June 2nd: The first link in Michael Burr's answer, which does indeed qualify as a horror story, is perhaps a bit dated (from 2003). I did a quick test, but the problem has apparently gone away.

来源:

#include <string.h>
struct iw_event {               /* dummy! */
    int len;
};
char *iwe_stream_add_event(
    char *stream,               /* Stream of events */
    char *ends,                 /* End of stream */
    struct iw_event *iwe,       /* Payload */
    int event_len)              /* Real size of payload */
{
    /* Check if it's possible */
    if ((stream + event_len) < ends) {
            iwe->len = event_len;
            memcpy(stream, (char *) iwe, event_len);
            stream += event_len;
    }
    return stream;
}

具体投诉是:

一些用户抱怨说,当在没有 -fno-strict-aliasing 的情况下编译 [above] 代码时,write 和 memcpy 的顺序是颠倒的(这意味着一个伪造的 len 被 mem 复制到流中).

Some users have complained that when the [above] code is compiled without the -fno-strict-aliasing, the order of the write and memcpy is inverted (which means a bogus len is mem-copied into the stream).

编译代码,在 CYGWIN 上使用 gcc 4.3.4 和 -O3(如果我错了,请纠正我——我的汇编程序有点生疏!):

Compiled code, using gcc 4.3.4 on CYGWIN wih -O3 (please correct me if I am wrong--my assembler is a bit rusty!):

_iwe_stream_add_event:
        pushl       %ebp
        movl        %esp, %ebp
        pushl       %ebx
        subl        $20, %esp
        movl        8(%ebp), %eax       # stream    --> %eax
        movl        20(%ebp), %edx      # event_len --> %edx
        leal        (%eax,%edx), %ebx   # sum       --> %ebx
        cmpl        12(%ebp), %ebx      # compare sum with ends
        jae L2
        movl        16(%ebp), %ecx      # iwe       --> %ecx
        movl        %edx, (%ecx)        # event_len --> iwe->len (!!)
        movl        %edx, 8(%esp)       # event_len --> stack
        movl        %ecx, 4(%esp)       # iwe       --> stack
        movl        %eax, (%esp)        # stream    --> stack
        call        _memcpy
        movl        %ebx, %eax          # sum       --> retval
L2:
        addl        $20, %esp
        popl        %ebx
        leave
        ret

对于迈克尔回答中的第二个链接,

And for the second link in Michael's answer,

*(unsigned short *)&a = 4;

gcc 通常(总是?)给出警告.但我相信一个有效的解决方案(对于gcc)是使用:

gcc will usually (always?) give a warning. But I believe a valid solution to this (for gcc) is to use:

#define CAST(type, x) (((union {typeof(x) src; type dst;}*)&(x))->dst)
// ...
CAST(unsigned short, a) = 4;

我在 gcc-strict 中问过这样是否可以-aliasing-and-casting-through-a-union,但到目前为止没有人不同意.

I've asked SO whether this is OK in gcc-strict-aliasing-and-casting-through-a-union, but so far nobody disagrees.

推荐答案

没有我自己的恐怖故事,但这里有一些来自 Linus Torvalds 的引述(抱歉,如果这些已经在问题的链接参考文献之一中):

No horror story of my own, but here are some quotes from Linus Torvalds (sorry if these are already in one of the linked references in the question):

http://lkml.org/lkml/2003/2/26/158:

日期 2003 年 2 月 26 日,星期三 09:22:15 -0800主题回复:没有 -fno-strict-aliasing 的无效编译来自 Jean Tourrilhes <>

Date Wed, 26 Feb 2003 09:22:15 -0800 Subject Re: Invalid compilation without -fno-strict-aliasing From Jean Tourrilhes <>

在 2003 年 2 月 26 日星期三下午 04:38:10 +0100,Horst von Brand 写道:

On Wed, Feb 26, 2003 at 04:38:10PM +0100, Horst von Brand wrote:

Jean Tourrilhes <> 说:

Jean Tourrilhes <> said:

对我来说它看起来像是一个编译器错误...一些用户抱怨当下面的代码是在没有 -fno-strict-aliasing 的情况下编译,写入的顺序和memcpy 被倒置(这意味着一个伪造的 len 被 mem 复制到溪流).代码(来自 linux/include/net/iw_handler.h):

It looks like a compiler bug to me... Some users have complained that when the following code is compiled without the -fno-strict-aliasing, the order of the write and memcpy is inverted (which mean a bogus len is mem-copied into the stream). Code (from linux/include/net/iw_handler.h) :

static inline char *
iwe_stream_add_event(char *   stream,     /* Stream of events */
                     char *   ends,       /* End of stream */
                    struct iw_event *iwe, /* Payload */
                     int      event_len)  /* Real size of payload */
{
  /* Check if it's possible */
  if((stream + event_len) < ends) {
      iwe->len = event_len;
      memcpy(stream, (char *) iwe, event_len);
      stream += event_len;
  }
  return stream;
}

恕我直言,编译器应该有足够的上下文来知道重新排序是危险的.任何使这个简单的代码变得更多的建议欢迎使用防弹工具.

IMHO, the compiler should have enough context to know that the reordering is dangerous. Any suggestion to make this simple code more bullet proof is welcomed.

编译器可以自由假设 char *stream 和 struct iw_event *iwe 点由于严格的别名,分隔内存区域.

The compiler is free to assume char *stream and struct iw_event *iwe point to separate areas of memory, due to strict aliasing.

哪个是对的,哪个不是我要抱怨的问题.

Which is true and which is not the problem I'm complaining about.

(事后注意:这段代码很好,但 Linux 的 memcpy 实现 是一个转换为 long * 以复制更大块的宏.使用正确定义的 memcpygcc -fstrict-aliasing 不允许破坏此代码.但这意味着如果您的编译器不知道如何转换字节,则您需要内联 asm 来定义内核 memcpy将循环复制到高效的 asm 中,gcc7 之前的 gcc 就是这种情况)

(Note with hindsight: this code is fine, but Linux's implementation of memcpy was a macro that cast to long * to copy in larger chunks. With a correctly-defined memcpy, gcc -fstrict-aliasing isn't allowed to break this code. But it means you need inline asm to define a kernel memcpy if your compiler doesn't know how turn a byte-copy loop into efficient asm, which was the case for gcc before gcc7)

以及 Linus Torvald 对上述内容的评论:

And Linus Torvald's comment on the above:

Jean Tourrilhes 写道:>

Jean Tourrilhes wrote: >

对我来说这看起来像是一个编译器错误...

It looks like a compiler bug to me...

你认为内核为什么使用-fno-strict-aliasing"?

Why do you think the kernel uses "-fno-strict-aliasing"?

gcc 的人更感兴趣的是试图找出什么可以c99 规范所允许的,而不是让事情实际工作.这特别是别名代码甚至不值得启用,它只是不当某些东西可以别名时,可以理智地告诉 gcc.

The gcc people are more interested in trying to find out what can be allowed by the c99 specs than about making things actually work. The aliasing code in particular is not even worth enabling, it's just not possible to sanely tell gcc when some things can alias.

有些用户抱怨当下面的代码被在没有 -fno-strict-aliasing 的情况下编译,写入的顺序和memcpy 被倒置(这意味着一个伪造的 len 被 mem 复制到流).

Some users have complained that when the following code is compiled without the -fno-strict-aliasing, the order of the write and memcpy is inverted (which mean a bogus len is mem-copied into the stream).

问题"在于我们内联了 memcpy(),此时 gcc 不会关心它可以别名的事实,所以他们只会重新排序一切,并声称这是自己的错.即使没有理智我们甚至可以告诉 gcc 的方式.

The "problem" is that we inline the memcpy(), at which point gcc won't care about the fact that it can alias, so they'll just re-order everything and claim it's out own fault. Even though there is no sane way for us to even tell gcc about it.

几年前我试图用一种理智的方式,而 gcc 开发者真的不关心这个领域的现实世界.如果那样的话我会很惊讶已经改变了,从我已经看到的回复来看.

I tried to get a sane way a few years ago, and the gcc developers really didn't care about the real world in this area. I'd be surprised if that had changed, judging by the replies I have already seen.

我不会费心去对抗它.

莱纳斯

http://www.mail-archive.com/linux-btrfs@vger.kernel.org/msg01647.html:

基于类型的别名愚蠢.它是如此的愚蠢,以至于它甚至都不好笑.它坏了.gcc 接受了这个破碎的概念,并通过使它成为一个毫无意义的法律规定"的东西来使它更是如此.

Type-based aliasing is stupid. It's so incredibly stupid that it's not even funny. It's broken. And gcc took the broken notion, and made it more so by making it a "by-the-letter-of-the-law" thing that makes no sense.

...

我知道事实 gcc 会重新排序明显(静态)相同地址的写访问.gcc 会突然想到

I know for a fact that gcc would re-order write accesses that were clearly to (statically) the same address. Gcc would suddenly think that

unsigned long a;

a = 5;
*(unsigned short *)&a = 4;

可以重新排序以首先将其设置为 4(因为显然它们没有别名 - 通过阅读标准),然后因为现在 'a=5' 的赋值较晚,4 的赋值可以是完全省略!如果有人抱怨编译器疯了,编译器的人会说不,不,人们说我们可以做到这一点",完全没有反省过它是否有任何意义.

could be re-ordered to set it to 4 first (because clearly they don't alias - by reading the standard), and then because now the assignment of 'a=5' was later, the assignment of 4 could be elided entirely! And if somebody complains that the compiler is insane, the compiler people would say "nyaah, nyaah, the standards people said we can do this", with absolutely no introspection to ask whether it made any SENSE.

这篇关于gcc、严格别名和恐怖故事的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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