【问题标题】:How to replace operator new/delete and not interfere with libraries?如何替换 operator new/delete 而不干扰库?
【发布时间】:2019-05-31 08:15:21
【问题描述】:

假设我想向分配的对象添加一些元信息以跟踪分配/解除分配。 我了解到,替换一个版本的operator new 和两个版本的operator delete 就足以处理自C++11 以来的所有分配。

这是我写的:

#include <cstdlib>
#include <FreeImage.h>

#include <new>
#include <iostream>

void *operator new(size_t size)
{
    std::cout << "allocation of size " << size << '\n';
    void *allocated = malloc(size + sizeof(size_t));
    *reinterpret_cast<size_t *>(allocated) = size;
    return reinterpret_cast<void *>(reinterpret_cast<size_t *>(allocated) + 1);
}

void _delete(void *ptr) {
    void *allocated = reinterpret_cast<void *>(reinterpret_cast<size_t *>(ptr) - 1);
    size_t size = *reinterpret_cast<size_t *>(allocated);
    std::cout << "deallocation of size " << size << '\n';
    free(allocated);
}

void operator delete(void *ptr) noexcept
{
    _delete(ptr);
}

void operator delete(void *ptr, std::align_val_t al) noexcept
{
    _delete(ptr);
}

int main()
{
    auto str = new char[1337];
    delete str;
    FreeImage_Initialise();
}

// compiled with `g++ -std=c++17 reproduce.cpp -lfreeimage`

如果我不使用任何第三方功能,这个程序就可以工作,但如果我这样做,就会失败。

free(): invalid pointer 的程序崩溃,gdb 说它在 _delete 内部失败,我猜这意味着某些东西是用标准分配器分配的,但用我自己的分配器释放。

GDB 输出:

(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x00007ffff79a9535 in __GI_abort () at abort.c:79
#2  0x00007ffff7a10516 in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7ffff7b34c00 "%s\n") at ../sysdeps/posix/libc_fatal.c:181
#3  0x00007ffff7a173aa in malloc_printerr (str=str@entry=0x7ffff7b32d85 "free(): invalid pointer") at malloc.c:5336
#4  0x00007ffff7a191fc in _int_free (av=<optimized out>, p=<optimized out>, have_lock=<optimized out>) at malloc.c:4143
#5  0x00005555555552e8 in _delete(void*) ()
#6  0x0000555555555303 in operator delete(void*) ()
#7  0x00007ffff7d7edc2 in ?? () from /usr/lib/x86_64-linux-gnu/libfreeimage.so.3
#8  0x00007ffff79cba77 in __cxa_finalize (d=0x7ffff7fa0000) at cxa_finalize.c:83
#9  0x00007ffff7d17003 in ?? () from /usr/lib/x86_64-linux-gnu/libfreeimage.so.3
#10 0x00007fffffffdec0 in ?? ()
#11 0x00007ffff7fe3d16 in _dl_fini () at dl-fini.c:138

那么问题来了:如何正确替换新/删除?

【问题讨论】:

  • 全局命名空间中所有以下划线开头的标识符都是保留的,不能使用。 _delete 违反了这一点。它可能不是您的问题的原因,但最好修复它并将其作为可能的原因消除。编辑:您可以找到受限标识符列表here
  • 建议:打印出 malloc() 返回并传递给 free() 的地址,以验证它们是否正确。我同意:“_delete()”是一个不明智的名称选择:(
  • objcopy 库并用标准名称替换新名称并用删除替换新名称。在您的翻译单元中提供它们。覆盖另一个中的默认值。关联。没有其他选择 - 您需要替换库代码中的函数名称。
  • 严格来说,您应该使用placement new 将大小写入额外分配的内存中。
  • 感谢您指出 _delete,但即使我将其重命名为 my_delete 之类的名称,它的行为也是一样的。

标签: c++ linux


【解决方案1】:

我找到了我的案例的答案。

我替换了这些表单以使其正常工作:

void *operator new(size_t size);

void *operator new(size_t size, std::align_val_t al);

void *operator new(size_t size, const std::nothrow_t &tag);

void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &);

void operator delete(void *ptr);

虽然我没有替换对齐删除运算符,但程序停止了崩溃。 但我想像 cppreference.com 建议的那样替换对齐的删除仍然是一个好主意。

【讨论】:

  • 换句话说,问题是有一些你没有预料到的超载,对吗?请随时“接受”您的回答。
  • @paulsm4 是的,我认为这是问题所在。我太信任 cppreference.com。或者问题可能在于该网站的这条经验法则仅适用于 C++17 应用程序,并且该库可能是使用较旧的 C++ 标准构建的。不过我不想深入挖掘。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-03
  • 2023-04-04
  • 1970-01-01
  • 2011-08-30
  • 2021-07-28
  • 1970-01-01
相关资源
最近更新 更多