gpt4 book ai didi

c++ - 为什么不叫我的新接线员

转载 作者:可可西里 更新时间:2023-11-01 17:36:29 26 4
gpt4 key购买 nike

我想看到一个动态加载的库(用 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

或者按照 Employed Russian 的建议尝试(但最终没有任何改变)

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。

现在:出了什么问题?我认为共享库正在使用他们自己的新/删除,因此你必须在工厂旁边提供在库代码中创建一个析构函数 destroy。

补充问题:为什么需要destroy(Base* p)函数?如果这个函数只调用客户端的 delete-operator 我也可以自己完成,即在倒数第二行中“删除 a”而不是 destroy_module(a)。

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

补充:我这里指的只是Linux平台。

编辑:重要部分在对 Employed Russian 的回答的评论中。所以我简要地给出了主要线索:如果以这种方式调用 gcc

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

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

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

在使用调用程序的新建/删除运算符的库中。感谢 Employed Russian!

最佳答案

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

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

如果您在 libC.so 之后加载的库中定义它(或者 libstdc++.so 如果您使用的是 GCC) ,那么您的定义将永远不会被使用。由于您在程序启动后 dlopen()ing 您的库,此时 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

关于c++ - 为什么不叫我的新接线员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1054697/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com