2014-09-18 13 views
5

Bei Verwendung von Clang 3.5.0 mit -flto und Verknüpfung mit einer gemeinsam genutzten Bibliothek scheint es, dass Aufrufe an operator delete in der gemeinsam genutzten Bibliothek nicht Folgen Sie der gleichen Reihenfolge der Symbolauflösung wie Aufrufe an operator new von den Hauptobjekten. Beispiel:Clang Link-Zeit-Optimierung mit ersetzten Operator neue Ursachen mismatched frei()/löschen in Valgrind

shared.cpp:

void deleteIt(int* ptr) { 
    delete ptr; 
} 

main.cpp:

#include <cstdlib> 
#include <new> 

void* operator new(size_t size) { 
    void* result = std::malloc(size); 
    if (result == nullptr) { 
    throw std::bad_alloc(); 
    } 
    return result; 
} 

void operator delete(void* ptr) noexcept { 
    std::free(ptr); 
} 

void deleteIt(int* ptr); 

int main() { 
    deleteIt(new int); 
    return 0; 
} 

Hier ist, was passiert, wenn ich es bauen und führen Sie es durch valgrind:

$ clang++ -std=c++11 -g -O3 -flto -fuse-ld=gold -fPIC -shared shared.cpp -o libshared.so 
$ clang++ -std=c++11 -g -O3 -flto -fuse-ld=gold main.cpp -L. -lshared -o main 
$ LD_LIBRARY_PATH=. valgrind --quiet ./main 
==20557== Mismatched free()/delete/delete [] 
==20557== at 0x4C2B6D0: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==20557== by 0x4009F7: main (main.cpp:19) 
==20557== Address 0x5a03040 is 0 bytes inside a block of size 4 alloc'd 
==20557== at 0x4C29F90: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==20557== by 0x4009EA: operator new (main.cpp:5) 
==20557== by 0x4009EA: main (main.cpp:19) 
==20557== 

Sie können sehen dass es die operator delete des Valgrind findet, aber unter Verwendung operator new von main.cpp. Im Gegensatz dazu funktioniert der gleiche Build mit gcc (ersetzen Sie einfach clang++ mit g++) gut. Irgendwelche Ideen warum oder wie man es umgehen kann?

EDIT: Symbol importiert und exportiert, wie von @Deduplicator angefordert.

$ objdump -T main | c++filt | grep operator 
0000000000400990 g DF .text 0000000000000033 Base  operator new(unsigned long) 
0000000000000000  DF *UND* 0000000000000000 Base  operator delete(void*) 
$ objdump -T libshared.so | c++filt | grep operator 
0000000000000000  DF *UND* 0000000000000000 GLIBCXX_3.4 operator delete(void*) 
+0

Haben Sie tatsächlich durch den Code getreten (ich erwarte irgendwie, dass die Shared Library einen eigenen Heap hat, also bin ich nicht ganz überrascht) –

+0

Ich denke, der Operator-delete-line für Haupt zeigt das Problem an. Aus irgendeinem Grund exportiert main nicht delete ... Tatsächlich wurde delete gelöscht. – Deduplicator

+0

Guter Fang. Interessanterweise, wenn ich die C++ 14-Überladung von 'operator delete' hinzufüge und' -std = C++ 14' verwende, wird diese exportiert und alles funktioniert.Aber 'operator delete (void *)' wird immer noch nicht exportiert. –

Antwort

5

Mit Blick auf die object-dump, ist es offensichtlich, operator delete(void*) nicht von main exportiert wird.

$ objdump -T main | c++filt | grep operator 
0000000000400990 g DF .text 0000000000000033 Base  operator new(unsigned long) 
0000000000000000  DF *UND* 0000000000000000 Base  operator delete(void*) 

Sehen Sie, dass der Abschnitt, wo operator delete(void*) gespeichert *UND* ist: Es ist nicht da!

Nun, das ist ein offensichtlicher Fehler auf der Seite des Klangs, könnte einen guten Bug-Bericht machen, da wir bereits einen minimalen Testfall haben.

Nun, wie Clang zu halten und zu exportieren operator delete(void*) als ein Pflaster?
Die Antwort ist looking at the possible attributes, gibt es eine gute:

verwendet
Dieses Attribut, auf eine Funktion angebracht ist, bedeutet, dass Code muss auch für die Funktion emittiert werden, wenn es scheint, dass die Funktion nicht referenziert . Dies ist beispielsweise nützlich, wenn die Funktion nur in der Inline-Assembly referenziert wird. Wenn das Attribut auf eine Elementfunktion einer C++ - Klassenvorlage angewendet wird, bedeutet dies auch, dass die Funktion instanziiert wird, wenn die Klasse selbst instanziiert wird.

Putting, dass in dem Code:

void operator delete(void* ptr) noexcept __attribute__((used)) { 

Und voilá, Klirren nicht unsachgemäß mehr beschneidet es.

+0

Fehlerbericht: http://llvm.org/bugs/show_bug.cgi?id=21001 –