문제

나는 동적으로 로드된 라이브러리(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

클라이언트를 실행하면 "새 클라이언트 호출"과 "클라이언트 삭제 호출"만 표시됩니다.Employed Russian가 제안한 것과 같은 라이브러리에 대해 컴파일러 스위치 -Bsymbolic을 사용하더라도 마찬가지입니다.

지금:무엇이 잘못되었나요?나는 공유 라이브러리가 자체적인 신규/삭제를 사용하고 있으므로 팩토리 옆에 라이브러리 코드에서 소멸자 삭제를 생성하도록 제공해야 한다고 생각했습니다.

보충 질문:destroy(Base* p) 기능이 필요한 이유는 무엇입니까?이 함수가 클라이언트의 삭제 연산자만 호출하는 경우 혼자서도 수행할 수 있습니다. 즉, 마지막 줄 옆의 destroy_module(a) 대신 "delete a"를 수행할 수도 있습니다.

내가 찾은 답변:라이브러리는 신규/삭제-연산자 쌍을 제공할 수도 있습니다.따라서 라이브러리의 새 항목을 먼저 사용하고 나중에 클라이언트의 삭제 항목을 사용하면 아마도 함정에 빠질 수 있습니다.안타깝게도 지금까지 내 라이브러리가 자신의 새 라이브러리 또는 삭제된 라이브러리를 사용하는 것을 본 적이 없습니다...따라서 원래 질문에는 여전히 답변이 없습니다.

보충:저는 Linux 플랫폼만을 언급하고 있습니다.

편집하다:중요한 부분은 Employed Russian's Answer에 대한 설명에 있습니다.그래서 저는 간단히 말해서 주요 단서를 제공하고 있습니다.이런 식으로 gcc를 호출하면

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

라이브러리는 자체적인 신규/삭제 연산자를 사용합니다.그렇지 않으면 결과

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

호출 프로그램의 신규/삭제 연산자를 사용하는 라이브러리에서.러시아어 고용자에게 감사드립니다!

도움이 되었습니까?

해결책

문제는 대부분의 경우 UNIX 플랫폼(다른 플랫폼과 달리 Win32 그리고 AIX) 기본적으로 모든 기호 참조는 첫 번째 런타임 로더에 표시되는 기호 정의.

정의한다면 'operator new' 메인에 a.out, 모든 것이 해당 정의에 바인딩됩니다(Neil Butterworth의 예에서 볼 수 있듯이). a.out 최초의 이미지 런타임 로더 검색입니다.

다음에 로드되는 라이브러리에서 정의하는 경우 libC.so (또는 libstdc++.so 당신이 사용하는 경우 GCC), 그러면 정의가 사용되지 않습니다.당신이 이래로 dlopen()프로그램이 시작된 후 도서관을 방문하세요. libC 해당 시점에 이미 로드되었으며 라이브러리는 런타임 로더가 검색할 마지막 라이브러리입니다.그래서 당신은 잃습니다.

~에 ELF 플랫폼에서는 다음을 사용하여 기본 동작을 변경할 수 있습니다. -Bsymbolic.에서 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

다른 팁

다음 코드는 예상대로 작동합니다.동적 라이브러리 코드가 귀하가 제공한 신규/삭제를 사용할 것으로 예상하십니까?내 생각엔 당신이 실망할 것 같아요.

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

RTLD_DEEPBIND를 살펴보세요.

문제는 C++의 연산자 오버로딩이 링크 타임이 아닌 컴파일 타임 기능이라는 것입니다.DLL은 오버로드된 new()에 대한 지식 없이 컴파일되었으므로 올바르게 작동하지 않습니다.

또 다른 가능성은 귀하의 플랫폼에서 링크와 함께 작동하지만(예에서와 같이) DLL이 실행 파일의 기호를 확인하지 않는다는 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top