【问题标题】:Clang link-time optimization with replaced operator new causes mismatched free()/delete in valgrind使用替换的运算符 new 进行 Clang 链接时优化导致 valgrind 中的 free()/delete 不匹配
【发布时间】:2014-09-18 21:45:04
【问题描述】:

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

shared.cpp

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

ma​​in.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;
}

当我构建它并通过 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== 

您可以看到它正在查找 valgrind 的 operator delete,但使用来自 main.cppoperator new。相比之下,与 gcc 完全相同的构建(只需将 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*)

【问题讨论】:

  • 您是否真的单步执行了代码(我确实希望共享库有自己的堆,所以我并不完全感到惊讶)
  • 我认为 main 的 operator-delete-line 表明了问题。出于某种原因,main 没有导出 delete...事实上,delete 被修剪了。
  • 好收获。有趣的是,如果我添加 operator delete 的 C++14 重载并使用 -std=c++14,则将导出该重载并且一切正常。但是operator delete(void*) 仍然没有导出。
  • 尝试在noexcept 之后将__attribute__((used)) 附加到operator delete 的定义中?
  • @Deduplicator 效果很好,谢谢!随意写它作为答案,我会接受。

标签: c++ clang valgrind lto


【解决方案1】:

查看object-dump,很明显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*)

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

现在,这显然是 clang 的失败,可能是一个很好的错误报告,因为我们已经有了一个最小的测试用例。

现在,如何强制 clang 保留和导出 operator delete(void*) 作为创可贴?
答案是looking at the possible attributes,有个不错的:

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

将其放入代码中:

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

瞧,clang 不再不恰当地修剪它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-07
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 2021-07-30
    • 2012-01-01
    相关资源
    最近更新 更多