Que dois-je faire si deux bibliothèques fournissent une fonction avec le même nom générant un conflit?

94

Que dois-je faire si j'ai deux bibliothèques qui fournissent des fonctions avec des noms équivalents?

qeek
la source
2
sont ces bibliothèques statiques ou liées dynamiquement?
Alnitak
nous avons besoin de plus de détails ... ces noms sont-ils exportés? ou sont-ils utilisés uniquement en interne? Pouvez-vous changer les noms?
Johannes Schaub - litb
Ils sont liés dynamiquement, les deux. Je ne peux pas changer les noms, car je ne possède pas les bibliothèques.
qeek
Excellente question. Bien sûr , il ne serait pas un problème avec ces deux bibliothèques si tous les symboles ont été préfixées avec un identifiant unique (par exemple vorbis_..., sf_..., sdl_...). C'est essentiellement ce que fait C ++ aux noms de symboles pour les fonctions d'espacement de noms.
Vortico du
C'est une question très intéressante mais malheureusement trop imprécise, raison pour laquelle il y a trop de réponses trop larges.
yugr

Réponses:

52
  • Si vous contrôlez un ou les deux: éditez-en un pour changer le nom et recompilez Ou voyez de manière équivalente les réponses de Ben et d' inconnu qui fonctionneront sans accès au code source.
  • Si vous ne contrôlez aucun d'entre eux, vous pouvez en conclure un. C'est compiler une autre bibliothèque ( liée statiquement !) Qui ne fait rien d'autre que réexporter tous les symboles de l'original à l'exception de celui incriminé, qui est atteint via un wrapper avec un autre nom. Quelle galère.
  • Ajouté plus tard: Puisque qeek dit qu'il parle de bibliothèques dynamiques, les solutions suggérées par Ferruccio et mouviciel sont probablement les meilleures. (Il me semble vivre il y a longtemps lorsque le lien statique était la valeur par défaut. Cela colore ma pensée.)

À propos des commentaires: Par "exporter", j'entends rendre visible les modules liés à la bibliothèque --- équivalent au externmot - clé à portée de fichier. La façon dont cela est contrôlé dépend du système d'exploitation et de l'éditeur de liens. Et c'est quelque chose que je dois toujours rechercher.

dmckee --- chaton ex-modérateur
la source
C'était aussi ma première pensée, mais ne vous retrouverez-vous pas avec le même problème de collision? En fin de compte, l'ensemble du projet doit être lié - au moment de la compilation / liaison ou au moment de l'exécution - auquel moment les deux bibliothèques incriminées doivent se charger telles quelles.
Sniggerfardimungus
@unknown: Le wrapper doit être compilé avec une liaison statique et ne doit pas exporter le symbole incriminé. Ensuite, vous pouvez toujours lier dynamiquement le wrapper. Édité pour plus de clarté, merci.
dmckee --- ex-moderator chaton
Si le problème de qeek concerne les bibliothèques ddl et non statiques, comment est-il possible de créer une nouvelle bibliothèque avec un wrapper? Depuis, la bibliothèque d'encapsuleur devrait envelopper dynamiquement une fonction de la bibliothèque avec laquelle vous ne souhaitez pas établir de lien en premier lieu.
jeffD
@dmckee - qu'entendez-vous par "exportation"?
4
peut-être que quelqu'un pourrait donner un exemple simple de cette technique? Un exe, deux bibliothèques contenant chacune une fonction avec le même nom.
52

Il est possible de renommer des symboles dans un fichier objet en utilisant objcopy --redefine-sym old=new file(voir man objcopy).

Ensuite, appelez simplement les fonctions en utilisant leurs nouveaux noms et créez un lien avec le nouveau fichier objet.

Ben
la source
1
Agréable. Ce serait trivial à ajouter à un Makefile. Si les bibliothèques sont jamais mises à jour, une incantation objcopy serait beaucoup plus facile à mettre à jour que certaines des autres solutions.
sigjuice
8
N'oubliez pas de renommer également les symboles dans les fichiers d'en-tête.
mouviciel
^ sed / awk / perl serait également utile pour automatiser le changement de nom des symboles dans l'en-tête
Alex Reinking
16

Sous Windows, vous pouvez utiliser LoadLibrary () pour charger l'une de ces bibliothèques en mémoire, puis utiliser GetProcAddress () pour obtenir l'adresse de chaque fonction que vous devez appeler et appeler les fonctions via un pointeur de fonction.

par exemple

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);

obtiendrait l'adresse d'une fonction nommée bar dans foo.dll et l'appelait.

Je sais que les systèmes Unix prennent en charge des fonctionnalités similaires, mais je ne peux pas penser à leurs noms.

Ferruccio
la source
dlopen dlsymet dlclose. Cependant, l'encapsulation sous Unix peut ne pas être aussi efficace que sous Windows.
user877329
8

Voici une pensée. Ouvrez l'une des bibliothèques incriminées dans un éditeur hexadécimal et remplacez toutes les occurrences des chaînes incriminées par autre chose. Vous devriez alors pouvoir utiliser les nouveaux noms dans tous les futurs appels.

MISE À JOUR: Je viens de le faire à cette fin et cela semble fonctionner. Bien sûr, je n'ai pas testé cela à fond - ce n'est peut-être qu'un très bon moyen de vous faire sauter la jambe avec un fusil de chasse hexedit.

Sniggerfardimungus
la source
en fait pas une solution terrible. Un peu hackish, mais tout ce que vous feriez est de changer les chaînes de la table des symboles. Aucun problème fonctionnel réel là-dedans.
Evan Teran
Vous voudrez probablement aussi renommer la bibliothèque - de peur que quelqu'un d'autre ne vienne essayer de charger à nouveau la chose. Vous passeriez d'un conflit à des dizaines ou des centaines. =] J'adore ça à propos de stackoverflow: nous avons une réponse testée à une question et elle a 3 votes. La première réponse (incomplète): 17. =]
Sniggerfardimungus
Les opportunités de changement de nom sont limitées car vous ne pourrez que raccourcir les noms . Sous Linux également, vous aurez du mal à mettre à jour les tables de hachage ELF.
yugr
7

En supposant que vous utilisez Linux, vous devez d'abord ajouter

#include <dlfcn.h>

Déclarez la variable de pointeur de fonction dans le bon contexte, par exemple,

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

Comme Ferruccio indiqué dans https://stackoverflow.com/a/678453/1635364 , chargez explicitement la bibliothèque que vous souhaitez utiliser en exécutant (choisissez vos indicateurs préférés)

void* dlhandle;
void* sym;

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

Lisez l'adresse de la fonction que vous souhaitez appeler ultérieurement

sym = dlsym(dlhandle, "conflicting_server_init");

attribuer et cast comme suit

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

Appelez de la même manière que l'original. Enfin, déchargez en exécutant

dlclose(dlhandle);
vraa
la source
6

Vous ne devez pas les utiliser ensemble. Si je me souviens bien, l'éditeur de liens émet une erreur dans un tel cas.

Je n'ai pas essayé, mais une solution peut être avec dlopen(), dlsym()et dlclose()qui vous permet de gérer par programmation des bibliothèques dynamiques. Si vous n'avez pas besoin des deux fonctions en même temps, vous pouvez ouvrir la première bibliothèque, utiliser la première fonction et fermer la première bibliothèque avant d'utiliser la deuxième bibliothèque / fonction.

mouviciel
la source
Merci. Je n'ai pas pensé à ça. Bien que j'aimerais avoir les deux en même temps.
qeek
Et si j'aimerais utiliser les deux en même temps?
QZHua
@QZHua: D'autres réponses (par exemple, impliquant le changement de nom de symbole) devraient résoudre votre problème.
mouviciel
6

Si vous avez des fichiers .o, une bonne réponse ici: https://stackoverflow.com/a/6940389/4705766

Résumé:

  1. objcopy --prefix-symbols=pre_string test.o pour renommer les symboles dans le fichier .o

ou

  1. objcopy --redefine-sym old_str=new_str test.o pour renommer le symbole spécifique dans le fichier .o.
Jee lee
la source
4

Ce problème est la raison pour laquelle c ++ a des espaces de noms. Il n'y a pas vraiment de bonne solution en c pour 2 bibliothèques tierces ayant le même nom.

S'il s'agit d'un objet dynamique, vous pourrez peut-être charger explicitement les objets partagés (LoadLibrary / dlopen / etc) et l'appeler de cette manière. Alternativement, si vous n'avez pas besoin des deux bibliothèques en même temps dans le même code, vous pouvez peut-être faire quelque chose avec des liens statiques (si vous avez les fichiers .lib / .a).

Aucune de ces solutions ne s'applique à tous les projets, bien entendu.

Brian Mitchell
la source
1
Oh oui. Pour cette question générale, cela semble être une bonne réponse. Cependant, les espaces de noms sont cool si vous compilez tout ensemble dans le même compilateur. Hourra, aucun nom ne se heurte. Mais si vous obtenez une bibliothèque sous forme binaire et que vous souhaitez l'intégrer à un autre compilateur, alors - bonne chance. Les règles de modification des noms dans les fichiers objets ne sont que le premier obstacle (extern "C" peut aider, ce qui annule l'effet des espaces de noms).
Tomasz Gandor
3

Jurer? Pour autant que je sache, vous ne pouvez pas faire grand-chose si vous avez deux bibliothèques qui exposent des points de liaison avec le même nom et que vous devez établir un lien entre les deux.

Vatine
la source
12
Jurer est définitivement la première étape. Aucun doute là dessus.
dmckee --- ex-moderator chaton
1
«vous ne pouvez pas faire grand-chose» - est-ce toujours d'actualité? D'autres réponses apportent de nombreuses solutions différentes.
yugr
2

Vous devriez écrire une bibliothèque de wrapper autour de l'un d'eux. Votre bibliothèque d'encapsuleurs doit exposer des symboles avec des noms uniques et ne pas exposer les symboles des noms non uniques.

Votre autre option consiste à renommer le nom de la fonction dans le fichier d'en-tête et à renommer le symbole dans l'archive d'objets de bibliothèque.

Quoi qu'il en soit, pour utiliser les deux, ça va être un travail de piratage.

James Caccese
la source
1

La question approche une décennie, mais il y a de nouvelles recherches tout le temps ...

Comme déjà répondu, objcopy avec l'indicateur --redefine-sym est un bon choix sous Linux. Voir, par exemple, https://linux.die.net/man/1/objcopy pour une documentation complète. C'est un peu maladroit car vous copiez essentiellement la bibliothèque entière tout en apportant des modifications et chaque mise à jour nécessite que ce travail soit répété. Mais au moins ça devrait marcher.

Pour Windows, le chargement dynamique de la bibliothèque est une solution et une solution permanente comme l'alternative dlopen sous Linux le serait. Cependant, dlopen () et LoadLibrary () ajoutent du code supplémentaire qui peut être évité si le seul problème est la duplication des noms. Ici, la solution Windows est plus élégante que l'approche objcopy: dites simplement à l'éditeur de liens que les symboles d'une bibliothèque sont connus sous un autre nom et utilisez ce nom. Il y a quelques étapes pour le faire. Vous devez créer un fichier def et fournir la traduction du nom dans la section EXPORTS. Voir https://msdn.microsoft.com/en-us/library/hyx1zcd3.aspx (VS2015, il sera éventuellement remplacé par des versions plus récentes) ou http://www.digitalmars.com/ctg/ctgDefFiles.html(probablement plus permanent) pour les détails complets de la syntaxe d'un fichier def. Le processus consisterait à créer un fichier def pour l'une des bibliothèques, puis à utiliser ce fichier def pour créer un fichier lib, puis à créer un lien avec ce fichier lib. (Pour les DLL Windows, seuls les fichiers lib sont utilisés pour la liaison, pas pour l'exécution de code.) Consultez Comment créer un fichier .lib lorsque vous disposez d'un fichier .dll et d'un fichier d'en-tête pour le processus de création du fichier lib. Ici, la seule différence est l'ajout des alias.

Pour Linux et Windows, renommez les fonctions dans les en-têtes de la bibliothèque dont les noms sont aliasés. Une autre option qui devrait fonctionner serait, dans les fichiers faisant référence aux nouveaux noms, de #define old_name new_name, #include les en-têtes de la bibliothèque dont les exportations sont aliasées, puis #undef old_name dans l'appelant. S'il y a beaucoup de fichiers utilisant la bibliothèque, une alternative plus simple est de créer un en-tête ou des en-têtes qui encapsulent les définitions, les inclus et les undefs, puis d'utiliser cet en-tête.

J'espère que cette information vous a été utile!

Jim Monte
la source
0

Je n'ai jamais utilisé dlsym, dlopen, dlerror, dlclose, dlvsym, etc., mais je regarde la page de manuel, et elle donne un exemple d'ouverture de libm.so et d'extraction de la fonction cos. Dlopen passe-t-il par le processus de recherche de collisions? Si ce n'est pas le cas, l'OP pourrait simplement charger les deux bibliothèques manuellement et attribuer de nouveaux noms à toutes les fonctions fournies par ses bibliothèques.

Sniggerfardimungus
la source