8

動的にロードされたライブラリ(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

クライアントを実行すると、「呼び出されたクライアントの新規」と「呼び出されたクライアントの削除」のみが表示されます。コンパイラスイッチ-Bsymbolicをライブラリに使用しても、EmployedRussianが提案しました。

今:何が悪かったのですか?共有ライブラリは独自のnew/deleteを使用していると思ったので、ファクトリの隣にライブラリコードでデストラクタdestroyを作成するように指定する必要があります。

補足質問:destroy(Base * p)関数が必要なのはなぜですか?この関数がクライアントのdelete-operatorのみを呼び出す場合は、自分でそれを行うこともできます。つまり、最後の行の次のdestroy_module(a)の代わりに「deletea」を実行します。

私が見つけた回答:ライブラリは、new/delete-operatorのペアも提供できます。したがって、最初にライブラリの新しいものを使用し、後でクライアントの削除を使用すると、おそらく落とし穴に踏み込むことができます。悲しいことに、これまで、自分のライブラリが独自の新規または削除を使用しているのを見たことがありませんでした...したがって、元の質問にはまだ回答されていません。

補足:私は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演算子を使用しているライブラリー内。雇用されたロシア人に感謝します!

4

4 に答える 4

11

問題は、ほとんどのUNIXプラットフォーム(Win32およびとは異なりAIX)では、すべてのシンボル参照がデフォルトでランタイムローダーに表示されるシンボルの最初の定義にバインドされることです。

'operator new'mainで定義するa.outと、(Neil Butterworthの例が示すように)すべてがその定義にバインドされます。これa.outは、が最初のイメージランタイムローダー検索であるためです。

後にロードされるライブラリで定義した場合libC.so(またはlibstdc++.soを使用している場合GCC)、定義は使用されません。dlopen()プログラムの開始後にライブラリを実行しているのでlibC、その時点ですでにロードされており、ライブラリはランタイムローダーが検索する最後のライブラリです。だからあなたは負けます。

プラットフォームELFでは、を使用してデフォルトの動作を変更できる場合があります-Bsymbolicman ldLinuxから:

 -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
于 2009-06-29T05:50:39.940 に答える
1

次のコードは期待どおりに機能します。ダイナミックライブラリコードが提供された新規/削除を使用することを期待していますか?がっかりすると思います。

#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;
}
于 2009-06-28T10:55:58.067 に答える
1

RTLD_DEEPBINDを調べます

于 2009-10-06T22:57:16.827 に答える
0

問題は、C ++での演算子のオーバーロードが、リンク時ではなくコンパイル時の機能であるということだと思います。DLLは、オーバーロードされたnew()の知識なしにコンパイルされているため、正しく機能しません。

もう1つの可能性は、プラットフォームではリンクで使用されますが(例のように)、DLLが実行可能ファイルからシンボルを解決しないことです。

于 2009-06-28T16:49:10.137 に答える