Pregunta

Quería ver que una biblioteca cargada dinámicamente (cargado con dlopen etc.) realmente utiliza su propio nuevo una de eliminar a los operadores y no de esos que se definen en el programa de llamada.Así que escribí la siguiente 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;
    }
}

y compilado con

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

o como Empleado de rusia propuso para intentar (pero en el fondo nada ha cambiado)

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

La clase Base es sólo la celebración de un int valor y una función get_value() para obtener este valor.Después de que escribí client.cpp como este

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

y compilado con

g++ -Wall -g -o client -ldl client.cpp

La ejecución de cliente solo me dan un "Nuevo cliente llamado" y "Eliminar de cliente llama".Incluso si puedo usar el modificador de compilador -Bsymbolic para la biblioteca como Empleado ruso sugirió.

Ahora:¿Qué salió mal?Pensé biblioteca compartida que se están utilizando su propio nuevo/eliminar y por lo tanto, usted tiene que proporcionar al lado de la fábrica crear un destructor destroy en el código de la biblioteca.

Pregunta complementaria:¿Por qué tengo la necesidad de destruir(Base* p) la función?Si esta función sólo llama al eliminar-operador de el cliente, yo también podía hacerlo por mí mismo, yo.e "eliminar" en lugar de destroy_module(a) en la siguiente a la última línea.

Respuesta que he encontrado:La biblioteca también puede proporcionar un nuevo/borrar-operador de par.Así que si yo uso primero de la biblioteca nueva y más tarde del cliente eliminar seguramente puedo paso en un escollo.Lamentablemente hasta ahora nunca vi a mi biblioteca utilizando la propia nueva o eliminar...Así que la pregunta original aún no ha sido respondido.

Suplemento:Sólo estoy refiriendo a la plataforma Linux.

Editar:Las partes importantes son en los comentarios de los Empleados del ruso Respuesta.Así que me voy a dar la clave principal en una cáscara de nuez:Si uno llama a la gcc de esta manera

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

la biblioteca va a usar la propia nueva/eliminar de los operadores.De lo contrario, los resultados de la

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

en una biblioteca que utiliza el nuevo/eliminar de operadores del programa de llamada.Gracias a los Empleados de rusia!

¿Fue útil?

Solución

El problema es que en la mayoría de los UNIX plataformas (a diferencia de en Win32 y AIX) todas las referencias de símbolos por defecto se unen a la primero definición del símbolo visible para el tiempo de ejecución del cargador.

Si se define 'operator new' en la principal a.out, todo va a obligar a que la definición (como Neil Butterworth muestra el ejemplo de la), porque a.out la primera imagen de tiempo de ejecución del gestor de búsquedas.

Si se define en una biblioteca que se carga después de libC.so (o libstdc++.so en caso de que usted está usando GCC), entonces su definición no será utilizada.Puesto que usted está dlopen()ing de la biblioteca después de que el programa ha iniciado, libC ya está cargado en ese momento, y su biblioteca es el último en el tiempo de ejecución de cargador de búsqueda;de lo que pierdes.

En ELF plataformas, usted puede ser capaz de cambiar el comportamiento predeterminado mediante el uso de -Bsymbolic.De man ld en 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.

Tenga en cuenta que -Bsymbolic es un enlazador de la bandera, no es un indicador de compilador.Si el uso de g++, usted debe pasar la bandera a la enlazador como este:

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

Otros consejos

El siguiente código funciona como se esperaba. ¿Espera que su código de biblioteca dinámica use el nuevo / eliminar que proporciona? Creo que te decepcionará.

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

Buscar en RTLD_DEEPBIND

Creo que el problema es que la sobrecarga del operador en C ++ es una característica de tiempo de compilación, no de tiempo de enlace. La DLL se ha compilado sin el conocimiento de new () sobrecargado, por lo que no funcionará correctamente.

Otra posibilidad es que en su plataforma funcione con enlaces (como en su ejemplo) pero que la DLL no resuelva los símbolos de su archivo ejecutable.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top