为什么我的新接线员没有被叫到
我想看到动态加载的库(使用 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 。
现在:出了什么问题? 我认为共享库正在使用自己的 new/delete,因此您必须在工厂旁边提供在库代码中创建析构函数销毁。
补充问题:为什么需要 destroy(Base* p) 函数? 如果这个函数只调用客户端的删除操作符,我也可以自己做,即“删除a”而不是倒数第二行中的destroy_module(a)。
我发现的答案:该库还可以提供新/删除运算符对。 因此,如果我首先使用库的 new,然后使用客户端的删除,我可能会陷入陷阱。 可悲的是,直到现在我还没有看到我的库使用它自己的新的或删除的...所以原来的问题仍然没有得到解答。
补充:我只是指Linux平台。
编辑:重要部分位于对“雇佣俄罗斯人”的回答的评论中。 所以我简而言之给出了主要线索:如果以这种方式调用 gcc,
g++ -Wall -g -fPIC -shared library.cpp -o library.so -Wl,-Bsymbolic
则库将使用它自己的 new/delete 运算符。 否则会
g++ -Wall -g -fPIC -shared library.cpp -o library.so
导致库使用调用程序的 new/delete 运算符。 感谢俄罗斯雇员!
I wanted to see that a dynamically loaded library (loaded with dlopen etc.) really uses its own new an delete operators and not these ones defined in the calling program. So I wrote the following 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;
}
}
and compiled it with
g++ -g -Wall -fPIC -shared library.cpp -o library.so
or as Employed Russian suggested to try (but in the end nothing changed)
g++ -g -Wall -fPIC -shared -Wl,-Bsymbolic library.cpp -o library.so
The class Base is only holding an int value and a function get_value() to get this value. After that I wrote client.cpp like this
#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;
}
and compiled it with
g++ -Wall -g -o client -ldl client.cpp
Executing client I only get a "New of client called" and a "Delete of client called". Even if I use the compiler switch -Bsymbolic for the library like Employed Russian suggested.
Now: What went wrong? I thought shared library are using their own new/delete and therefore you have to provide next to the factory create a destructor destroy in the library code.
Supplementary question: Why do I need the destroy(Base* p) function? If this function only calls the delete-operator of the client I could also do it by myself, i.e "delete a" instead of destroy_module(a) in the next to last line.
Answer I found: The library can also provide a new/delete-operator pair. So if I use first the library's new and later the client's delete I can probably step into a pitfall. Sadly until now I never saw my library using it's own new or delete... So the original question still isn't answered.
Supplement: I'm only referring to the Linux platform.
Edit: The important parts are in the comments to Employed Russian's Answer. So I'm giving the main clue in a nutshell: If one calls the gcc this way
g++ -Wall -g -fPIC -shared library.cpp -o library.so -Wl,-Bsymbolic
the library will use it's own new/delete operators. Otherwise results
g++ -Wall -g -fPIC -shared library.cpp -o library.so
in a library that's using the new/delete operators of the calling program. Thanks to Employed Russian!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我认为问题在于 C++ 中的运算符重载是编译时功能,而不是链接时功能。 DLL 是在不知道重载 new() 的情况下编译的,因此它无法正常工作。
另一种可能性是,在您的平台上,它可以与链接一起使用(如您的示例),但 DLL 不会解析可执行文件中的符号。
I think the problem is that operator overloading in C++ is a compile-time, not link-time, feature. The DLL has been compiled without knowledge of overloaded new() so it won't work correctly.
Another possibility is that on your platform it would work used with linking (like in your example) but that the DLL does not resolve symbols from your executable file.
问题是,在大多数
UNIX
平台上(与Win32
和AIX
不同),所有符号引用默认绑定到第一个 em> 运行时加载程序可见的符号的定义。如果您在主
a.out
中定义'operator new'
,则所有内容都将绑定到该定义(如 Neil Butterworth 的示例所示),因为a.out< /code> 是第一个图像运行时加载器搜索。
如果您在
libC.so
之后加载的库中定义它(如果您使用的是GCC
,则为libstdc++.so
),那么您的永远不会使用定义。 由于您在程序启动后dlopen()
您的库,因此此时libC
已经加载,并且您的库是运行时加载器将搜索的最后一个库; 所以你输了。在
ELF
平台上,您可以使用-Bsymbolic
更改默认行为。 来自 Linux 上的man ld
:请注意,
-Bsymbolic
是链接器标志,而不是编译器标志。 如果使用g++
,您必须将标志传递给链接器,如下所示:The problem is that on most
UNIX
platforms (unlike onWin32
andAIX
) all symbol references by default bind to the first definition of the symbol visible to the runtime loader.If you define
'operator new'
in the maina.out
, everything will bind to that definition (as Neil Butterworth's example shows), becausea.out
is the very first image runtime loader searches.If you define it in a library which is loaded after
libC.so
(orlibstdc++.so
in case you are usingGCC
), then your definition will never be used. Since you aredlopen()
ing your library after the program has started,libC
is already loaded by that point, and your library is the very last one the runtime loader will search; so you lose.On
ELF
platforms, you may be able to change the default behavior by using-Bsymbolic
. Fromman ld
on Linux:Note that
-Bsymbolic
is a linker flag, not a compiler flag. If usingg++
, you must pass the flag to the linker like this:以下代码按预期工作。 您是否期望动态库代码使用您提供的 new/delete ? 我想你会失望的。
The following code works as expected. Are you expecting your dynamic library code to use the new/delete you provide? I think you will be disappointed.
查看 RTLD_DEEPBIND
Look into RTLD_DEEPBIND