2016-12-02 1 views
8

Ich habe das sehr alte (und sehr große) Win32-Projekt, das massive Prüfungen mit NULL-Zeiger verwendet, indem er den Zeiger auf den dereferenzierten Zeiger wirft. Wie folgt aus:Force erlauben Dereferenzierung von NULL-Zeiger

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

Und ja, weiß ich, dass dieser Code ist dumm und müssen Refactoring werden. Aber es ist unmöglich wegen der großen Menge an Code. Im Moment muss ich dieses Projekt unter MacOS Sierra in Xcode kompilieren, was zu großen Problemen führt ... Es stellt sich heraus, dass die Bedingung im Release-Modus (mit Code-Optimierung) mit falschem Verhalten ausgeführt wird (sogenanntes undefiniertes Verhalten wegen Dereferenzierung von NULL) Zeiger).

Nach this document for GCC gibt es eine Option -fno-delete-null-pointer-checks, aber es scheint, funktioniert nicht für LLVM wenn O1, O2 oder O3 Optimierung aktiviert. Die Frage ist also: Wie kann ich den LLVM 8.0-Compiler zwingen, solche Dereferenzierungen zuzulassen?

UPDATE. Das echte Arbeitsbeispiel, um das Problem zu überprüfen.

//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) 
} 

Bei -O0 und -O1, something keeps the null check, und der Code "funktioniert":

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 

Aber -O2 und oben, the check is optimized out:

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

[Entsprechender Fehlerbericht] (https://llvm.org/bugs/show_bug.cgi?id=9251). Es ist nicht vielversprechend: Die Flagge wird in der Tat ignoriert (es wurde zuerst nicht erkannt). – Quentin

+1

'-fno-delete-null-pointer-checks' soll nicht' & * (int *) x' beeinflussen, das immer noch 'NULL' sein darf. Mit clang auf http://gcc.godbolt.org/ überprüfen, mit einfach 'bool b (short * p) {zurückgeben 0 == & * (int *) p; } ', erzeugt clang korrekten Code. Bitte posten Sie ein minimales vollständiges Programm, in dem Ihr Compiler falschen Code erzeugt. – hvd

+0

@hvd Ich habe ein echtes Beispiel gepostet. Ich bin mir nicht sicher, ob dieses Problem mit GCC zu tun hat, ich habe nur in Apple LLVM 8.0 gesehen –

Antwort

0

Sie eine textbasierte Suche auf NULL. Dann führen Sie den Compiler im Warnmodus und drucken Sie alle Warnungen auf Papier (wenn Sie noch über eine solche Technologie verfügen). Jetzt für jede Null, ist es eine problematische null oder eine gute? Wenn problematisch, benennen Sie es um XNULL.

Jetzt ist es wahrscheinlich, dass die C++ - Überprüfungen auf einem kleinen System mit, sagen wir 640k, scheitern können, weil 640k für jeden ausreichen, aber nicht auf Ihrem modernen System mit vielen GB. Also streiche sie einfach einmal aus. Wenn das nicht der Fall ist. mache XNULL zu einem "Dummy-Objekt" mit einer gültigen Adresse in C++ - Augen.

(Aus dem Beispiel sieht es so aus, als wäre Code ein Lisp-Interpreter. Lisp benötigt sowohl einen Nullzeiger als auch einen Dummy-Zeiger, es gibt keine andere einfache Möglichkeit, den Interpreter zu schreiben).

+0

Das ist keine Lösung –