【问题标题】:Why isn't my new operator called为什么我的新操作员不叫
【发布时间】:2010-11-06 11:36:03
【问题描述】:

我想看看一个动态加载的库(用 dlopen 等加载)真的使用它自己的新删除操作符,而不是调用程序中定义的这些操作符。于是我写了如下library.cpp

#include <exception>
#include <new>
#include <cstdlib>
#include <cstdio>
#include "base.hpp"
void* operator new(size_t size) {
    std::printf("New of library called\n");
    void *p=std::malloc(size); 
    if (p == 0) // did malloc succeed?
        throw std::bad_alloc(); // ANSI/ISO compliant behavior
    return p;
}
void operator delete(void* p) {
    std::printf("Delete of library called\n");
    std::free(p);
}
class Derived : public Base {
public:
    Derived() : Base(10) { }
};
extern "C" {
    Base* create() {
        return new Derived;
    }
    void destroy(Base* p) {
        delete p;
    }
}

并用它编译

g++ -g -Wall -fPIC -shared library.cpp -o library.so

或者按照俄罗斯人的建议尝试(但最终没有任何改变)

g++ -g -Wall -fPIC -shared -Wl,-Bsymbolic library.cpp -o library.so

类 Base 只保存一个 int 值和一个函数 get_value() 来获取这个值。之后我就这样写了client.cpp

#include <exception>
#include <new>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <dlfcn.h>
#include "base.hpp"
void* operator new(size_t size) {
    std::printf("New of client called\n");
    void *p=std::malloc(size); 
    if (p == 0) // did malloc succeed?
        throw std::bad_alloc(); // ANSI/ISO compliant behavior
    return p;
}
void operator delete(void* p) {
    std::printf("Delete of client called\n");
    std::free(p);
}
typedef Base* create_module_t();
typedef void destroy_module_t(Base *);

int main() {
    void* handle = dlopen("./library.so", 
        RTLD_LAZY);
    if (handle == NULL) {
        std::cout << dlerror() << std::endl;
        return 1;
    }
    create_module_t* create_module = NULL;
    void* func = dlsym(handle, "create");
    if (func == NULL) {
        std::cout << dlerror() << std::endl;
        return 1;
    } else create_module = (create_module_t *)func;
    destroy_module_t* destroy_module = NULL;
    func = dlsym(handle, "destroy");
    if (func == NULL) {
        std::cout << dlerror() << std::endl;
        return 1;
    } else destroy_module = (destroy_module_t *)func;
    Base* a = create_module();
    std::cout << "Value: " << a->get_value() << std::endl;
    destroy_module(a);
    return 0;
}

并用它编译

g++ -Wall -g -o client -ldl client.cpp

执行客户端我只得到一个“新的客户端调用”和一个“删除客户端调用”。即使我像 Employed Russian 建议的那样为库使用编译器开关 -Bsymbolic。

现在:出了什么问题?我认为共享库正在使用他们自己的 new/delete,因此您必须在工厂旁边提供在库代码中创建析构函数销毁。

补充问题:为什么需要destroy(Base* p)函数?如果这个函数只调用客户端的delete-operator,我也可以自己做,即“delete a”而不是最后一行的destroy_module(a)。

我找到的答案:该库还可以提供一个新/删除操作符对。因此,如果我首先使用库的新功能,然后使用客户端的删除功能,我可能会陷入陷阱。可悲的是,直到现在我从未见过我的图书馆使用它自己的新的或删除的......所以最初的问题仍然没有得到回答。

补充:我只指Linux平台。

编辑:重要部分在就业俄罗斯人答案的 cmets 中。所以我简单地给出主要线索:如果有人以这种方式调用 gcc

g++ -Wall -g -fPIC -shared library.cpp -o library.so -Wl,-Bsymbolic

库将使用它自己的新/删除操作符。否则结果

g++ -Wall -g -fPIC -shared library.cpp -o library.so

在使用调用程序的 new/delete 运算符的库中。感谢受雇的俄罗斯人!

【问题讨论】:

  • 你是如何“在我的测试程序中使用它”的?你试过 LD_PRELOAD 了吗?
  • 我在我的帖子中更准确地说明了“在我的测试程序中使用它”。但是我还是不明白你说的 LD_PRELOAD 是什么意思。
  • 为什么这么难过?这不是证明是在使用自己的 new 和 delete 吗?这就是你要测试的不是吗?
  • 我认为您需要更新您的原始问题以避免我在下面看到的混淆。清楚地表明新/删除存在于您的 .dll 中,并显示您的“创建”功能的实现(我想象它只是在某些东西上调用“新”)。另外,你在什么平台上运行?如果这是我所怀疑的, printf 将不起作用。
  • 好的,我编辑了我的问题并添加了更多信息。我希望现在更清楚了。

标签: c++ g++ overriding allocation


【解决方案1】:

问题在于,在大多数UNIX 平台上(与Win32AIX 不同)默认情况下,所有符号引用都绑定到运行时加载程序可见的符号的first 定义。

如果您在主 a.out 中定义 'operator new',所有内容都将绑定到该定义(如 Neil Butterworth 的示例所示),因为 a.out 是运行时加载程序搜索的第一个图像。

如果您在libC.so(或libstdc++.so,如果您使用GCC)之后加载的库中定义它,那么您的定义将永远不会被使用。由于您在程序启动后dlopen() 正在调用您的库,因此此时已经加载了libC,并且您的库是运行时加载器将搜索的最后一个库;所以你输了。

ELF 平台上,您可以使用-Bsymbolic 更改默认行为。来自 Linux 上的man ld

 -Bsymbolic
   When creating a shared library, bind references to global symbols
   to the definition within the shared library, if any. Normally, it
   is possible for a program linked against a shared library to override
   the  definition within the shared library. This option is only meaningful
   on ELF platforms which support shared libraries.

请注意,-Bsymbolic 是链接器标志,而不是编译器标志。如果使用g++,则必须将标志传递给链接器,如下所示:

  g++ -fPIC -shared library.cpp -o library.so -Wl,-Bsymbolic

【讨论】:

  • 但是如果我在我的库中显式地重载 new/delete-operator 也是这种情况吗?我编辑了我原来的问题,所以问题更清楚了。
  • 是的,情况仍然如此。就运行时加载程序而言,您刚刚定义了名为 _Znwj 的全局函数的两个实例:一个在主可执行文件中,一个在 library.so 中。运行时加载程序看到的第一个是它将使用的那个。至于为什么 -Bsymbolic 不起作用;很可能你犯了一个错误:你做了'g++ -g -Bsymbolic -Wall -fPIC -shared library.cpp -o library.so'吗? '-Bsymbolic' 是一个链接器开关,要将其传递给链接器,请使用 '-Wl,-Bsymbolic';否则它可能会被 GCC 解释(对于 -B... 标志具有完全不同的含义)。
  • 好的,所以我在我的问题中写了“g++ -g -Wall -fPIC -shared -Wl,-Bsymbolic library.cpp -o library.so”和客户端。但话又说回来,只有客户的 new/delete 被调用。我动态加载了库,客户端根本没有链接到它。链接器开关“-Bsymbolic”是否也适用于这种情况?
  • 您从未透露过您的操作系统是什么。这是我在 Linux 上看到的内容: $ g++ -g -Wall -fPIC -shared library.cpp -o library.so $ g++ -g client.cpp -ldl $ ./a.out 新的客户端称为值:10 删除客户端调用 $ g++ -g -Wall -fPIC -shared library.cpp -o library.so -Wl,-Bsymbolic $ ./a.out New of library called Value: 10 Delete of library called
  • 就是这样,现在我也看到了!是的,我也在使用 Linux。感谢您的耐心等待。
【解决方案2】:

以下代码按预期工作。您是否希望您的动态库代码使用您提供的新/删除?我想你会失望的。

#include <memory>
#include <cstdio>
#include <cstdlib>
using namespace std;;

void* operator new(size_t size) {
        std::printf("New...\n");
        void *p=std::malloc(size); 
        if (p == 0) // did malloc succeed?
                throw std::bad_alloc(); // ANSI/ISO compliant behavior
        return p;
}

void operator delete(void* p) {
        std::printf("Delete...\n");
        std::free(p);
}

int main() {
    int * p = new int(42);
    delete p;
}

【讨论】:

  • 不,我没有在库的“客户端”程序中提供新的新建/删除。我在我的库文件中定义了 in 一个新的 new/delete,并希望看到该库使用它自己的,即这个新的 new/delete。
  • @ph,如果你不使用它,你怎么能指望 new 被调用呢?我没有看到这个,我今天早上一定站得不好。
  • @litb:认为库使用它自己的新/删除操作符,而不是客户端程序中定义的这些操作符。我在我的库和我的客户端程序中定义了不同的新的新/删除操作符。但唯一调用的新/删除操作符是客户端程序中的这些操作符。这是故意的吗?
【解决方案3】:

查看 RTLD_DEEPBIND

【讨论】:

    【解决方案4】:

    我认为问题在于 C++ 中的运算符重载是编译时而非链接时的功能。 DLL 是在不知道重载 new() 的情况下编译的,因此无法正常工作。

    另一种可能性是,在您的平台上,它可以与链接一起使用(就像在您的示例中一样),但 DLL 不会从您的可执行文件中解析符号。

    【讨论】:

    • 可能我的问题有点不清楚:我在我的库中定义了一个新的 new/delete-operator,并且惊讶于库无论如何使用客户端的 new/delete-operator。另请参阅我编辑的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-26
    相关资源
    最近更新 更多