dynamic_cast non riesce se utilizzato con dlopen / dlsym
-
23-09-2019 - |
Domanda
Introduzione
mi scuso in anticipo per la lunga domanda. E 'il più breve ho potuto farlo, che è, purtroppo, non è molto breve.
Configurazione
Ho definito due interfacce, A e B:
class A // An interface
{
public:
virtual ~A() {}
virtual void whatever_A()=0;
};
class B // Another interface
{
public:
virtual ~B() {}
virtual void whatever_B()=0;
};
Poi, ho una libreria condivisa "TestC" costruire oggetti della classe C, implementazione sia A che B, e poi svenire puntatori a loro A-Interfaccia:
class C: public A, public B
{
public:
C();
~C();
virtual void whatever_A();
virtual void whatever_B();
};
A* create()
{
return new C();
}
Infine, ho una seconda una libreria condivisa "testd", che prende un A*
come input, e cerca di lanciarlo ad un B*
, utilizzando dynamic_cast
void process(A* a)
{
B* b = dynamic_cast<B*>(a);
if(b)
b->whatever_B();
else
printf("Failed!\n");
}
Infine, ho ricorso principale, passando A*
di tra le librerie:
A* a = create();
process(a);
Domanda
Se io costruisco la mia domanda principale, che collega contro la 'TestC' e biblioteche testd ', tutto funziona come previsto. Se, tuttavia, modifico l'applicazione principale di non linkare 'TestC' e 'testd', ma invece caricarli in fase di esecuzione utilizzando dlopen
/ dlsym
, poi il dynamic_cast
fallisce.
Non capisco perché. Eventuali indizi?
Ulteriori informazioni
- Testato con gcc 4.4.1, libc6 2.10.1 (Ubuntu 9.10)
- codice di esempio disponibili
Soluzione
ho trovato la risposta alla mia domanda qui . A quanto ho capito, ho bisogno di fare il typeinfo disponibile in 'TestC' a disposizione del 'testd' biblioteca. Per fare questo quando si utilizza dlopen()
, due cose in più devono essere fatte:
- Quando si collega la biblioteca, passare il linker l'opzione
-E
, per assicurarsi che esporta tutti i simboli all'eseguibile, non solo quelli che sono irrisolti in esso (perché ci sono nessuno) - Quando si carica biblioteca
dlopen()
, aggiungere l'opzioneRTLD_GLOBAL
, per assicurarsi che i simboli esportati datestc
sono disponibili anche pertestd
Altri suggerimenti
In generale, gcc non supporta RTTI attraverso i confini dlopen. Ho esperienza personale con questo rovinare try / catch, ma il problema sembra che più dello stesso. Purtroppo, temo che è necessario attenersi alle cose semplici attraverso dlopen.
Devo aggiungere a questa domanda da quando ho incontrato questo problema pure.
Anche quando fornisce -Wl,-E
e utilizzando RTLD_GLOBAL
, i dynamic_casts ancora non è riuscito. Tuttavia, passando -Wl,-E
in linkage dell'applicazione reale così e non solo nella libreria sembrano aver riparato.
Se uno non hanno alcun controllo sulla fonte del ricorso principale, -Wl, -E non è applicabile. Passando -Wl, -E al linker, mentre la costruzione propri binari (il padrone di casa così e i plugin) non aiuta neanche. Nel mio caso l'unica soluzione di lavoro era per caricare e scaricare il mio ospite così dalla funzione _init dell'ospite modo stesso utilizzando RTLD_GLOBAL bandiera (Vedi codice qui sotto). Questa soluzione funziona in entrambi i casi:
- i principali collegamenti di applicazioni contro l'host in modo.
- le principali carichi di applicazioni ospita in modo da utilizzare dlopen (senza RTLD_GLOBAL).
In entrambi i casi si deve seguire le istruzioni indicate da gcc wiki visibilità .
Se si fa simboli del plugin e l'host in modo visibile tra loro (utilizzando #pragma GCC visibilità push / pop o attributo corrispondente) e carica i plugin (dall'host così) utilizzando RTLD_GLOBAL 1. funzionerà anche senza carico e scarico proprio modo (come detto da collegamento sopra riportato). Questa soluzione rende 2. lavorano anche che non è stato il caso prima.
// get the path to the module itself
static std::string get_module_path() {
Dl_info info;
int res = dladdr( (void*)&get_module_path, &info);
assert(res != 0); //failure...
std::string module_path(info.dli_fname);
assert(!module_path.empty()); // no name? should not happen!
return module_path;
}
void __attribute__ ((constructor)) init_module() {
std::string module = get_module_path();
// here the magic happens :)
// without this 2. fails
dlclose(dlopen(module.c_str(), RTLD_LAZY | RTLD_GLOBAL));
}