为什么我的新接线员没有被叫到

发布于 2024-07-25 20:12:21 字数 3280 浏览 7 评论 0原文

我想看到动态加载的库(使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

自由如风 2024-08-01 20:12:22

我认为问题在于 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.

离不开的别离 2024-08-01 20:12:21

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

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

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

The problem is that on most UNIX platforms (unlike on Win32 and AIX) 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 main a.out, everything will bind to that definition (as Neil Butterworth's example shows), because a.out is the very first image runtime loader searches.

If you define it in a library which is loaded after libC.so (or libstdc++.so in case you are using GCC), then your definition will never be used. Since you are dlopen()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. From man ld on Linux:

 -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.

Note that -Bsymbolic is a linker flag, not a compiler flag. If using g++, you must pass the flag to the linker like this:

  g++ -fPIC -shared library.cpp -o library.so -Wl,-Bsymbolic
末蓝 2024-08-01 20:12:21

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

#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;
}

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.

#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;
}
凉世弥音 2024-08-01 20:12:21

查看 RTLD_DEEPBIND

Look into RTLD_DEEPBIND

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文