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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With