2016-12-02 9 views
8

역 참조 포인터를 포인터로 캐스팅하여 NULL 포인터로 대규모 검사를 사용하는 매우 오래된 (거대한) Win32 프로젝트가 있습니다. 이처럼 :강제로 NULL 포인터의 역 참조 허용

int* x = NULL; //somewhere 
//... code 
if (NULL == &(*(int*)x) //somewhere else 
    return; 

그리고 네, 이 코드는 바보임을 알고을 리팩토링 할 필요가있다. 그러나 엄청난 양의 코드로 인해 불가능합니다. 바로 지금 Xcode의 MacOS Sierra에서이 프로젝트를 컴파일해야합니다. 문제가 발생합니다 ... 릴리스 모드 (코드 최적화)에서는 조건이 잘못된 동작으로 실행됩니다. 따라서 NULL을 참조 해제하기 때문에 정의되지 않은 동작이 발생합니다 바늘).

this document for GCC에 따르면 옵션 -fno-삭제 - 널 포인터 검사이 있지만, O1, O2 또는 O3 최적화가 활성화 된 경우는 LLVM 작동하지 않는 것 같다. 그래서 질문은 : 어떻게 그러한 역 참조를 허용하도록 LLVM 8.0 컴파일러를 강제 할 수 있습니까?

업데이트. 문제를 확인하기위한 실제 작업 예제입니다.

//somewhere 1 
class carr 
{ 
public: 
    carr(int length) 
    { 
     xarr = new void*[length]; 

     for (int i = 0; i < length; i++) 
      xarr[i] = NULL; 
    } 

    //some other fields and methods 

    void** xarr; 
    int& operator[](int i) 
    { 
     return *(int*)xarr[i]; 
    } 
}; 

//somewhere 2 
carr m(5); 

bool something(int i) 
{ 
    int* el = &m[i]; 
    if (el == NULL) 
     return FALSE; //executes in debug mode (no optimization) 

    //other code 
    return TRUE; //executes in release mode (optimization enabled) 
} 
-O0-O1, something keeps the null check에서

, 그리고 코드는 "작동"

something(int):       # @something(int) 
    pushq %rax 
    movl %edi, %eax 
    movl $m, %edi 
    movl %eax, %esi 
    callq carr::operator[](int) 
    movb $1, %al 
    popq %rcx 
    retq 

그러나 -O2에서 이상, the check is optimized out :

something(int):       # @something(int) 
    movb $1, %al 
    retq 
+5

[해당 버그 신고] (https://llvm.org/bugs/show_bug.cgi?id=9251). 유망하지 않습니다. 깃발은 현재 무시됩니다 (처음에는 인식되지 않았습니다). – Quentin

+1

'-fno-delete-null-pointer-checks'은'& * (int *) x'에 영향을 미치지 않습니다. 여전히'NULL'이 될 수 있습니다. http://gcc.godbolt.org/에서 clang으로 확인하면 간단히 'bool b (short * p) {return 0 == & * (int *) p; }', clang은 올바른 코드를 생성합니다. 컴파일러가 잘못된 코드를 생성하는 최소한의 완전한 프로그램을 게시하십시오. – hvd

+0

@hvd 실제 사례를 게시했습니다. 나는이 문제가 GCC와 관련이 있는지 확신 할 수 없다. 애플 LLVM 8.0에서 이것을 보았을 뿐이다. –

답변

0

에 텍스트 기반 검색을 수행을 없는. 그런 다음 경고 모드에서 컴파일러를 실행하고 종이에 모든 경고를 인쇄합니다 (그래도 그런 기술이있는 경우). 각 null에 대해 문제가있는 null 또는 좋은 것입니까? 문제가 있으면 XNULL로 이름을 변경하십시오.

이제는 640k가 설치된 소형 시스템에서 640k가 충분하기 때문에 C++ 검사가 실패 할 수 있습니다. 그러나 현대 시스템에서는 많은 GB가 필요하지 않습니다. 한 번 라벨을 다시 달았습니다. 그게 아니라면. XNULL을 C++ 눈에 유효한 주소를 가진 "더미 객체"로 만듭니다.

(이 예제에서 코드는 Lisp 인터프리터처럼 보입니다 .Lisp에는 null 포인터와 더미 포인터가 필요합니다. 다른 쉬운 방법은 없습니다.)

+0

그건 해결책이 아닙니다. –