Cosa devo fare se due librerie forniscono una funzione con lo stesso nome che genera un conflitto?


93

Cosa devo fare se ho due librerie che forniscono funzioni con nomi equivalenti?


2
sono queste librerie statiche o collegate dinamicamente?
Alnitak

abbiamo bisogno di maggiori dettagli ... quei nomi vengono esportati? o sono usati solo internamente? Puoi cambiare i nomi?
Johannes Schaub - litb

Sono collegati dinamicamente, entrambi. Non posso modificare i nomi, poiché non possiedo le librerie.
qeek

Ottima domanda. Naturalmente non sarebbe un problema con queste due librerie se tutti i simboli sono stati preceduti da un ID univoco (ad esempio vorbis_..., sf_..., sdl_...). Questo è essenzialmente ciò che fa C ++ ai nomi dei simboli per le funzioni con spazio dei nomi.
Vortico

Questa è una domanda molto interessante ma è purtroppo troppo imprecisa, motivo per cui si hanno troppe risposte troppo ampie.
yugr

Risposte:


52
  • Se ne controlli uno o entrambi: modificane uno per cambiare il nome e ricompilare Oppure vedi in modo equivalente le risposte di Ben e unknown che funzioneranno senza accesso al codice sorgente.
  • Se non controlli nessuno dei due, puoi avvolgerne uno. Cioè compilare un'altra libreria ( linkata staticamente !) Che non fa altro che riesportare tutti i simboli dell'originale tranne quello incriminato, che si raggiunge tramite un wrapper con un nome alternativo. Che seccatura.
  • Aggiunto più tardi: poiché qeek dice che sta parlando di librerie dinamiche, le soluzioni suggerite da Ferruccio e mouviciel sono probabilmente le migliori. (Mi sembra di vivere molto tempo fa, quando il collegamento statico era l'impostazione predefinita. Colora il mio pensiero.)

A proposito dei commenti: Con "export" intendo rendere visibile ai moduli che si collegano alla libreria --- equivalente alla externparola chiave nell'ambito del file. Il modo in cui viene controllato dipende dal sistema operativo e dal linker. Ed è qualcosa che devo sempre cercare.


Anche questo è stato il mio primo pensiero, ma non ti ritroverai con lo stesso problema di collisione? Alla fine, l'intero progetto deve collegarsi - in fase di compilazione / collegamento o in fase di esecuzione - momento in cui entrambe le librerie incriminate devono essere caricate così come sono.
Sniggerfardimungus

@unknown: il wrapper deve essere compilato con collegamento statico e non deve esportare il simbolo offensivo. Quindi puoi ancora collegare dinamicamente il wrapper. Modificato per maggiore chiarezza, grazie.
dmckee --- gattino ex moderatore

Se il problema di qeek è con le librerie ddl e non statiche, come è possibile creare una nuova libreria con un wrapper? Poiché, la libreria wrapper dovrebbe avvolgere dinamicamente intorno a una funzione nella libreria a cui non vuoi collegarti in primo luogo.
jeffD

@dmckee - cosa intendi per "esportazione"?

4
forse qualcuno potrebbe fornire un semplice esempio di questa tecnica? Un exe, due librerie contenenti ciascuna una funzione con lo stesso nome.

52

È possibile rinominare i simboli in un file oggetto usando objcopy --redefine-sym old=new file(vedi man objcopy).

Quindi chiama le funzioni usando i loro nuovi nomi e collegali al nuovo file oggetto.


1
Bello. Questo sarebbe banale da aggiungere a un Makefile. Se le biblioteche venissero aggiornate, un incantesimo di objcopy sarebbe molto più facile da aggiornare rispetto ad altre soluzioni.
sigjuice

8
Non dimenticare di rinominare anche i simboli nei file di intestazione.
mouviciel

^ sed / awk / perl sarebbe utile anche per automatizzare la ridenominazione dei simboli nell'intestazione
Alex Reinking

16

In Windows, è possibile utilizzare LoadLibrary () per caricare una di quelle librerie in memoria e quindi utilizzare GetProcAddress () per ottenere l'indirizzo di ciascuna funzione necessaria per chiamare e chiamare le funzioni tramite un puntatore a funzione.

per esempio

HMODULE lib = LoadLibrary("foo.dll");
void *p = GetProcAddress(lib, "bar");
// cast p to the approriate function pointer type (fp) and call it
(*fp)(arg1, arg2...);
FreeLibrary(lib);

otterrebbe l'indirizzo di una funzione chiamata bar in foo.dll e la chiamerebbe.

So che i sistemi Unix supportano funzionalità simili, ma non riesco a pensare ai loro nomi.


dlopen dlsym, e dlclose. Tuttavia, l'incapsulamento su Unix potrebbe non essere efficace come su Windows.
user877329


8

Ecco un pensiero. Apri una delle librerie incriminate in un editor esadecimale e modifica tutte le occorrenze delle stringhe incriminate in qualcos'altro. Dovresti quindi essere in grado di utilizzare i nuovi nomi in tutte le chiamate future.

AGGIORNAMENTO: l' ho appena fatto su questa parte e sembra funzionare. Ovviamente, non l'ho testato a fondo: potrebbe essere solo un ottimo modo per far saltare una gamba con un fucile hexedit.


in realtà non è una soluzione terribile. Un po 'hacker, ma tutto ciò che faresti è cambiare le stringhe nella tabella dei simboli. Nessun vero danno funzionale in questo.
Evan Teran

Probabilmente vorrai anche rinominare la libreria, per evitare che qualcun altro si avvicini, cercando di caricare di nuovo la cosa. Passeresti da un conflitto a dozzine o centinaia. =] Adoro questo aspetto di stackoverflow: abbiamo una risposta testata a una domanda e ha 3 voti. La prima risposta (incompleta): 17. =]
Sniggerfardimungus

Le possibilità di rinominare sono limitate in quanto potrai solo abbreviare i nomi . Anche su Linux avrai difficoltà ad aggiornare le tabelle hash ELF.
yugr

7

Supponendo che tu usi Linux devi prima aggiungere

#include <dlfcn.h>

Dichiarare la variabile del puntatore a funzione nel contesto appropriato, ad esempio,

int (*alternative_server_init)(int, char **, char **);

Come ha dichiarato Ferruccio in https://stackoverflow.com/a/678453/1635364 , carica esplicitamente la libreria che desideri utilizzare eseguendo (scegli i tuoi flag preferiti)

void* dlhandle;
void* sym;

dlhandle = dlopen("/home/jdoe/src/libwhatnot.so.10", RTLD_NOW|RTLD_LOCAL);

Leggi l'indirizzo della funzione che desideri chiamare in seguito

sym = dlsym(dlhandle, "conflicting_server_init");

assegnare e lanciare come segue

alternative_server_init = (int (*)(int, char**, char**))sym;

Chiama in modo simile all'originale. Infine, scarica eseguendo

dlclose(dlhandle);


6

Non dovresti usarli insieme. Se ricordo bene, il linker genera un errore in questo caso.

Non ho provato, ma una soluzione potrebbe essere con dlopen(), dlsym()e dlclose()che permettono di gestire a livello di programmazione librerie dinamiche. Se non sono necessarie le due funzioni contemporaneamente, è possibile aprire la prima libreria, utilizzare la prima funzione e chiudere la prima libreria prima di utilizzare la seconda libreria / funzione.


Grazie. Non ci ho pensato. Anche se mi piacerebbe averli entrambi allo stesso tempo.
qeek

E se volessi usarli entrambi contemporaneamente?
QZHua

@QZHua: altre risposte (ad esempio, che riguardano la ridenominazione dei simboli) dovrebbero risolvere il tuo problema.
mouviciel

6

Se hai file .o lì, una buona risposta qui: https://stackoverflow.com/a/6940389/4705766

Sommario:

  1. objcopy --prefix-symbols=pre_string test.o per rinominare i simboli nel file .o

o

  1. objcopy --redefine-sym old_str=new_str test.o per rinominare il simbolo specifico nel file .o.

4

Questo problema è la ragione per cui c ++ ha spazi dei nomi. Non c'è davvero una grande soluzione in c per 2 librerie di terze parti con lo stesso nome.

Se è un oggetto dinamico, potresti essere in grado di caricare esplicitamente gli oggetti condivisi (LoadLibrary / dlopen / ecc.) E chiamarli in quel modo. In alternativa, se non hai bisogno di entrambe le librerie contemporaneamente nello stesso codice, puoi forse fare qualcosa con il collegamento statico (se hai i file .lib / .a).

Nessuna di queste soluzioni si applica a tutti i progetti, ovviamente.


1
Oh si. Per questa domanda generale questa sembra una buona risposta. Tuttavia, gli spazi dei nomi sono interessanti se si compila tutto insieme nello stesso compilatore. Evviva, nessun nome si scontra. Ma se ottieni una libreria in formato binario e desideri integrarla con un altro compilatore, allora - buona fortuna. Le regole di alterazione dei nomi nei file oggetto sono solo il primo ostacolo (la "C" esterna può aiutare, il che annulla l'effetto degli spazi dei nomi).
Tomasz Gandor

3

Giurare? Per quanto ne so, non c'è molto che puoi fare se hai due librerie che espongono punti di collegamento con lo stesso nome e devi collegarti a entrambi.


12
Lo giuro è sicuramente il primo passo. Nessun dubbio su questo.
dmckee --- gattino ex moderatore

1
"non puoi fare molto" - è ancora rilevante? Altre risposte forniscono numerose soluzioni diverse.
yugr

2

Dovresti scrivere una libreria wrapper attorno a uno di essi. La tua libreria wrapper dovrebbe esporre simboli con nomi univoci e non esporre i simboli dei nomi non univoci.

L'altra opzione è rinominare il nome della funzione nel file di intestazione e rinominare il simbolo nell'archivio oggetti della libreria.

Ad ogni modo, per usarli entrambi, sarà un lavoro da hacker.



1

La domanda si avvicina a un decennio fa, ma ci sono sempre nuove ricerche ...

Come già risposto, objcopy con il flag --redefine-sym è una buona scelta in Linux. Vedere, ad esempio, https://linux.die.net/man/1/objcopy per la documentazione completa. È un po 'goffo perché stai essenzialmente copiando l'intera libreria mentre apporti modifiche e ogni aggiornamento richiede che questo lavoro venga ripetuto. Ma almeno dovrebbe funzionare.

Per Windows, caricare dinamicamente la libreria è una soluzione e permanente come l'alternativa dlopen in Linux sarebbe. Tuttavia, sia dlopen () che LoadLibrary () aggiungono codice extra che può essere evitato se l'unico problema sono i nomi duplicati. Qui la soluzione Windows è più elegante rispetto all'approccio objcopy: basta dire al linker che i simboli in una libreria sono conosciuti con un altro nome e usare quel nome. Ci sono alcuni passaggi per farlo. È necessario creare un file def e fornire la traduzione del nome nella sezione ESPORTAZIONI. Vedi https://msdn.microsoft.com/en-us/library/hyx1zcd3.aspx (VS2015, alla fine verrà sostituito da versioni più recenti) o http://www.digitalmars.com/ctg/ctgDefFiles.html(probabilmente più permanente) per i dettagli completi della sintassi di un file def. Il processo sarebbe creare un file def per una delle librerie, quindi utilizzare questo file def per creare un file lib e quindi collegarlo a quel file lib. (Per le DLL di Windows, i file lib vengono utilizzati solo per il collegamento, non per l'esecuzione del codice.) Vedere Come creare un file .lib quando si dispone di un file .dll e di un file di intestazione per il processo di creazione del file lib. Qui l'unica differenza è l'aggiunta degli alias.

Sia per Linux che per Windows, rinominare le funzioni nelle intestazioni della libreria i cui nomi sono stati alias. Un'altra opzione che dovrebbe funzionare sarebbe, nei file che fanno riferimento ai nuovi nomi, #define old_name new_name, #include le intestazioni della libreria le cui esportazioni sono sottoposte ad alias e quindi #undef old_name nel chiamante. Se sono presenti molti file che utilizzano la libreria, un'alternativa più semplice è creare un'intestazione o più intestazioni che racchiudono le definizioni, le inclusioni e l'annullamento delle definizioni e quindi utilizzare tale intestazione.

Spero che questa informazione sia stata utile!


0

Non ho mai usato dlsym, dlopen, dlerror, dlclose, dlvsym, ecc., Ma sto guardando la pagina man e fornisce un esempio di apertura di libm.so ed estrazione della funzione cos. Dlopen passa attraverso il processo di ricerca delle collisioni? In caso contrario, l'OP potrebbe caricare manualmente entrambe le librerie e assegnare nuovi nomi a tutte le funzioni fornite dalle sue librerie.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.