Clang link-time optimization with replaced operator new causes mismatched free()/delete in valgrind
Asked Answered
T

1

5

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*)
Telephony answered 18/9, 2014 at 21:45 Comment(5)
Have you actually stepped through the code (I do kind of expect the shared library to have it's own heap, so I'm not entirely surprised)Ward
I think the operator-delete-line for main indicates the problem. For some reason, main is not exporting delete... In fact, delete was pruned.Dosi
Good catch. Interestingly, if I add the C++14 overload of operator delete and use -std=c++14, that one is exported and everything works. But operator delete(void*) is still not exported.Telephony
try attaching __attribute__((used)) after noexcept to the definition of operator delete?Dosi
@Dosi Works great, thanks! Feel free to write it as an answer and I'll accept.Telephony
D
5

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.

Dosi answered 18/9, 2014 at 22:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.