使用替换的运算符new进行Clang链接时优化会导致valgrind中的free()/ delete不匹配

Tav*_*nes 5 c++ valgrind clang lto

当使用clang 3.5.0和-flto并使用共享库进行链接时,共享库中的调用似乎operator delete不会遵循与operator new主对象的调用相同的符号解析顺序.例:

shared.cpp:

void deleteIt(int* ptr) {
  delete ptr;
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

这是我构建它并通过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== 
Run Code Online (Sandbox Code Playgroud)

你可以看到它正在找到valgrind operator delete,但是使用operator newmain.cpp.相比之下,完全相同的构建与海湾合作委员会(只需更换clang++g++)工作正常.任何想法为什么,或如何解决它?

编辑:符号导入和导出,根据@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*)
Run Code Online (Sandbox Code Playgroud)

Ded*_*tor 5

查看对象转储,很明显operator delete(void*)不会导出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*)
Run Code Online (Sandbox Code Playgroud)

看到operator delete(void*)存储的部分是*UND*:它不在那里!

现在,这是clang的一个明显的失败,可能会成为一个好的bug报告,因为我们已经有了一个最小的测试用例.

现在,如何强迫clang保持和出口operator delete(void*)作为创可贴?
答案是看可能的属性,有一个很好的属性:

used
此属性附加到函数,表示即使看起来函数未被引用,也必须为函数发出代码.例如,仅在内联汇编中引用该函数时,这很有用.当应用于C++类模板的成员函数时,该属性还意味着如果实例化类本身,则实例化该函数.

把它放在代码中:

void operator delete(void* ptr) noexcept  __attribute__((used)) {
Run Code Online (Sandbox Code Playgroud)

而且,哇哇,铿锵不再不正当地修剪它.