IT이야기

gcc, 엄격한 별칭 및 공포 이야기

cyworld 2022. 6. 4. 08:13
반응형

gcc, 엄격한 별칭 및 공포 이야기

gcc-strict-aliasing-and-casting-a-union에서 나는 포인터를 통해 유니언 펀닝에 문제가 생긴 적이 있는지 물었다.지금까지의 대답은 '아니오'인 것 같다.

이 질문은 더 광범위합니다.gcc와 엄격한 별칭에 대한 공포 이야기가 있나요?

배경:Andrey로부터의 인용t의 응답은 c99-strict-aliasing-rules-in-c-gcc:

"엄격한 에일리어스 규칙은 [표준화] 초기부터 C와 C++에 존재했던 표준의 일부에 뿌리를 두고 있습니다.다른 타입의 l값을 통해 어떤 타입의 오브젝트에 액세스 할 수 없도록 하는 절이 C89/90(6.3) 및 C++98(3.10/15)에 존재합니다.다만 모든 컴파일러가 이를 실행(또는 감히)하거나 신뢰하는 것은 아닙니다."

, gcc는 이제 대담하게 그렇게 하고 있습니다.-fstrict-aliasing전환합니다.그리고 이것은 몇 가지 문제를 야기했다.예를 들어, Mysql 버그에 대한 훌륭한 기사 http://davmac.wordpress.com/2009/10/과 마찬가지로 훌륭한 토론 http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html을 참조해 주세요.

관련성이 낮은 기타 링크:

다시 한 번 말하지만, 당신만의 무서운 이야기가 있나요?에 의해 표시되지 않는 문제-Wstrict-aliasing론론선선선선선선C를 사용하다

6월 2일 추가:마이클 버의 대답의 첫 번째 연결고리는 2003년부터의 다소 오래된 이야기일 수 있다.간단한 테스트를 해봤는데 문제가 없어졌나 봐요.

출처:

#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 없이 컴파일되면 쓰기 순서와 memcpy 순서가 반전된다고 불평했습니다(즉, 가짜 len이 스트림에 복사된다는 의미).

CYGWIN wih - O3에서 gcc 4.3.4를 사용하여 컴파일된 코드(어셈블러가 약간 녹슬어 있는 것 같으면 정정해 주세요) :

_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

그리고 마이클의 답변의 두 번째 링크는

*(unsigned short *)&a = 4;

gcc는 보통 (항상?) 경고를 보냅니다.단, (gcc의 경우) 유효한 솔루션은 다음과 같습니다.

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

SO에게 gcc-strict-aliasing-and-casting-through-a-union에서 이것이 괜찮은지 물어봤지만, 아직까지 아무도 동의하지 않는다.

저만의 공포 이야기는 아니지만, 여기 Linus Torvalds의 인용구가 있습니다(이것들이 이미 질문의 링크된 레퍼런스 중 하나에 포함되어 있다면 죄송합니다).

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

2003년 2월 26일 수요일 09:22:15-0800 제목 Re:Jean Tourriles에서 -fno-strict-aliasing을 사용하지 않는 잘못된 컴파일 <>

2003년 2월 26일 수요일 오후 4시 38분 10초 +0100에 Horst von Brand는 다음과 같이 썼다.

Jean Torrilhes << 고객명>> 님은 다음과 같이 말했습니다.

컴파일러 버그 같은데...일부 사용자는 다음 코드가 -fno-strict-aliasing 없이 컴파일되면 쓰기 순서와 memcpy 순서가 반전된다고 불평했습니다(즉, 가짜 len이 스트림에 복사된다는 의미).코드(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, 컴파일러는 정렬이 위험하다는 것을 알기에 충분한 컨텍스트를 가지고 있어야 합니다.이 간단한 코드를 더 방탄으로 만들기 위한 어떠한 제안도 환영한다.

컴파일러는 엄밀한 에일리어싱으로 인해 char *stream 및 structure iw_event *i우리는 메모리 영역을 가리킬 수 있습니다.

그건 사실이고 내가 불평하는 문제는 그게 아니야.

: 이의( 「 」: 「 」 「 」 「 」 ), 「 」 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」,memcpy 큰 덩어리로 복사할 매크로입니다.올바르게 정의되어 있는memcpy,gcc -fstrict-aliasing이 코드를 해독할 수 없습니다., 을 정의하려면 합니다.memcpy컴파일러가 바이트 복사 루프를 효율적인 asm으로 바꾸는 방법을 모르는 경우(gcc7 이전 gcc의 경우)

그리고 위에 대한 Linus Torvald의 코멘트:

Jean Torrilhes는 다음과 같이 썼다.

컴파일러 버그 같은데...

커널이 "-fno-strict-aliasing"을 사용하는 이유는 무엇이라고 생각하십니까?

gcc 사람들은 실제로 작동시키는 것보다 c99 사양에 의해 무엇이 허용되는지 알아내는 데 더 관심이 있습니다.특히 에일리어싱 코드는 유효하게 할 가치가 없습니다.일부 에일리어스가 가능한 경우 gcc를 적절하게 구분할 수 없습니다.

일부 사용자는 다음 코드가 -fno-strict-aliasing 없이 컴파일되면 쓰기 순서와 memcpy 순서가 반전된다고 불평했습니다(즉, 가짜 len이 스트림에 복사된다는 의미).

문제는 memcpy()를 인라인화한다는 것입니다.이 시점에서 gcc는 에일리어스를 사용할 수 있다는 사실에 개의치 않기 때문에 모든 것을 재배열하여 자신의 장애라고 주장합니다.비록 우리가 그것에 대해 gcc를 말할 수 있는 적절한 방법은 없지만.

몇 년 전만 해도 제대로 된 방법을 찾으려고 했는데, gcc 개발자들은 이 지역의 현실세계에 대해 전혀 신경 쓰지 않았어요.제가 이미 본 답변으로 판단하건대, 만약 그것이 바뀌었으면 놀랐을 것입니다.

굳이 싸울 생각은 없어요.

라이너스

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

타입 베이스의 에일리어스는 바보같습니다.너무 멍청해서 재미도 없어.그건 부러졌어요.그리고 gcc는 잘못된 개념을 받아들였고, 그것을 "법률에 의한" 것으로 만들어 더 의미 없는 것으로 만들었습니다.

...

gcc는 분명히 같은 주소로(정적으로) 쓰기 액세스를 재정렬한다는 것을 알고 있습니다.GCC는 갑자기 이렇게 생각할 것이다.

unsigned long a;

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

먼저 4로 설정하도록 순서를 변경할 수 있습니다(표준을 읽음으로써 별칭이 명확하게 지정되지 않기 때문에). 그리고 이제 'a=5'의 할당이 늦었기 때문에 4의 할당을 완전히 생략할 수 있습니다.그리고 만약 누군가가 컴파일러가 미쳤다고 불평한다면, 컴파일러 사람들은 "nyaah, nyaah, 사람들이 우리가 이것을 할 수 있다고 말한 표준들"이라고 말할 것이고, 그것이 말이 되는지에 대해서는 전혀 자기성찰하지 않을 것이다.

여기 내 것이 있다.

http://forum.openscad.org/CGAL-3-6-1-causing-errors-but-CGAL-3-6-0-OK-tt2050.html

CAD 프로그램의 특정 도형이 잘못 그려졌습니다.프로젝트 리더들이 회귀 테스트 스위트를 만드는 데 힘써줘서 정말 다행이에요.

이 버그는 특정 플랫폼에서만 발생하며 이전 버전의 GCC와 이전 버전의 특정 라이브러리에서만 나타납니다.-O2만 켜면 -fno-strict-aliasing이 해결되었습니다.

gcc, 에일리어싱 및 2-D 가변 길이 배열:다음 샘플 코드는 2x2 매트릭스를 복사합니다.

#include <stdio.h>

static void copy(int n, int a[][n], int b[][n]) {
   int i, j;
   for (i = 0; i < 2; i++)    // 'n' not used in this example
      for (j = 0; j < 2; j++) // 'n' hard-coded to 2 for simplicity
         b[i][j] = a[i][j];
}

int main(int argc, char *argv[]) {
   int a[2][2] = {{1, 2},{3, 4}};
   int b[2][2];
   copy(2, a, b);    
   printf("%d %d %d %d\n", b[0][0], b[0][1], b[1][0], b[1][1]);
   return 0;
}

CentOS의 gcc 4.1.2에서는 다음과 같은 이점을 얻을 수 있습니다.

$ gcc -O1 test.c && a.out
1 2 3 4
$ gcc -O2 test.c && a.out
10235717 -1075970308 -1075970456 11452404 (random)

이것이 일반적으로 알려진 것인지, 그리고 이것이 버그인지 기능인지 알 수 없습니다.Cygwin에서 gcc 4.3.4의 문제를 재현할 없기 때문에 수정되었을 수 있습니다.몇 가지 회피책:

  • __attribute__((noinline))
  • switch gcc를 합니다.-fno-strict-aliasing.
  • copy의 세 를 copy()에서 합니다.b[][n]로로 합니다.b[][2].
  • 마세요-O2 ★★★★★★★★★★★★★★★★★」-O3.

기타 주의사항:

  • 이 답변은 1년 1일 후 제 질문에 대한 답변입니다(다른 답변이 두 개밖에 없다는 것이 조금 놀랍습니다).
  • 내 실제 코드인 칼만 필터로 몇 시간을 허비했어gcc의 자동 인라이닝이 변경되었기 때문에 사소한 변경에도 큰 영향이 있습니다(이것은 추측입니다만, 아직 확실하지 않습니다).하지만 그것은 아마도 공포 소설로 적합하지 않을 것이다.
  • copy()(그리고 여담이지만 gcc가 더블루프를 풀지 않는 것을 보고 조금 놀랐습니다.)
  • 경고.include gcc " " " , " "-Wstrict-aliasing=여기서 무슨 짓이든 했어
  • 1차원 가변 길이 어레이는 정상인 것 같습니다.

업데이트: 위 내용은 OP의 질문에 대한 답변이 되지 않습니다.그것은, 엄밀한 에일리어스가 「합법적으로」 당신의 코드를 망가뜨린 케이스에 대해 질문하고 있었기 때문입니다만, 위 내용은 단지 정원 버라이어티 컴파일러의 버그인 것 같습니다.

GCC Bugzilla에 보고했지만, 구 4.1.2는 10억 RHEL5의 열쇠라고 생각합니다만, 그들은 관심이 없었습니다.4.2.4 버전에서는 발생하지 않습니다.

그리고 비슷한 버그의 간단한 예를 하나 가지고 있습니다. 매트릭스는 하나뿐입니다.코드:

static void zero(int n, int a[][n]) {
   int i, j;
   for (i = 0; i < n; i++)
   for (j = 0; j < n; j++)
      a[i][j] = 0;
}

int main(void) {
   int a[2][2] = {{1, 2},{3, 4}};
   zero(2, a);    
   printf("%d\n", a[1][1]);
   return 0;
}

는 다음과 같은 결과를 생성합니다.

gcc -O1 test.c && a.out
0
gcc -O1 -fstrict-aliasing test.c && a.out
4

인 것 -fstrict-aliasing-finline버그의 원인이 됩니다.

C의 공통 초기 시퀀스 규칙은 다양한 구조 유형의 선두 부분에 대해 기능할 수 있는 함수를 작성할 수 있도록 하는 것으로 해석되어 왔습니다.다만, 이 함수는 일치하는 유형의 요소로 시작됩니다.C99에서는 관련된 구조 유형이 사용 시점에서 완전한 선언을 볼 수 있는 동일한 결합의 구성원인 경우에만 적용되도록 규칙이 변경되었습니다.

gcc의 저자들은 다음과 같은 사실에도 불구하고 해당 언어는 유니언 유형을 통해 접속이 수행되는 경우에만 적용 가능하다고 주장한다.

  1. union 유형을 통해 액세스를 수행해야 하는 경우 완전한 선언이 표시되어야 한다고 지정할 이유가 없습니다.

  2. CIS 규칙은 조합의 관점에서 설명되었지만, 그 주된 유용성은 구조물을 배치하고 접근하는 방법에 대해 암시하는 것에 있다.S1과 S2가 CIS를 공유하는 구조라면 외부 소스로부터 S1과 S2에 대한 포인터를 받아들인 함수가 실제로는 유니언 오브젝트 내부에 없는 구조물에 대한 포인터에 대해 같은 동작을 하지 않고 C89의 CIS 규칙을 준수할 수 있는 방법은 없다.구조물에 대한 CIS 지원을 지정한다.따라서 d는 조합에 대해 이미 명시되어 있기 때문에 중복되었다.

다음 코드는 gcc 4.4.4에서 10을 반환합니다.union 방식 또는 gcc 4.4.4에 문제가 있습니까?

int main()
{
  int v = 10;

  union vv {
    int v;
    short q;
  } *s = (union vv *)&v;

  s->v = 1;

  return v;
}

SWIG는 완전 에일리어스가 꺼지는 것에 의존하는 코드를 생성합니다.이는 모든 종류의 문제를 일으킬 수 있습니다.

SWIGEXPORT jlong JNICALL Java_com_mylibJNI_make_1mystruct_1_1SWIG_12(
       JNIEnv *jenv, jclass jcls, jint jarg1, jint jarg2) {
  jlong jresult = 0 ;
  int arg1 ;
  int arg2 ;
  my_struct_t *result = 0 ;

  (void)jenv;
  (void)jcls;
  arg1 = (int)jarg1; 
  arg2 = (int)jarg2; 
  result = (my_struct_t *)make_my_struct(arg1,arg2);
  *(my_struct_t **)&jresult = result;              /* <<<< horror*/
  return jresult;
}

언급URL : https://stackoverflow.com/questions/2958633/gcc-strict-aliasing-and-horror-stories

반응형