When using clang 3.5.0 with -flto and linking with a shared library, it seems that calls to operator delete
in the shared library don't follow the same symbol resolution order as calls to operator new
from the main objects. Example:
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;
}
Here's what happens when I build it and run it through 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==
You can see that it's finding the valgrind's operator delete
, but using operator new
from main.cpp
. In contrast, the exact same build with gcc (just replace clang++
with g++
) works fine. Any ideas why, or how to work around it?
EDIT: Symbol imports and exports, as requested by @Deduplicator.
$ 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*)
Looking at the object-dump, it is obvious operator delete(void*)
is not exported by main
.
$ 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*)
See that the section where operator delete(void*)
is stored is *UND*
: It is not there!
Now, that's an obvious failure on clang's part, might make a good bug-report, as we already have a minimal test-case.
Now, how to force clang to keep and export operator delete(void*)
as a band-aid?
The answer is looking at the possible attributes, there's a good one:
used
This attribute, attached to a function, means that code must be emitted for the function even if it appears that the function is not referenced. This is useful, for example, when the function is referenced only in inline assembly. When applied to a member function of a C++ class template, the attribute also means that the function is instantiated if the class itself is instantiated.
Putting that in the code:
void operator delete(void* ptr) noexcept __attribute__((used)) {
And voilá, clang no longer improperly prunes it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With