Clang link-time optimization with replaced operato

2019-02-17 18:25发布

问题:

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*)

回答1:

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.