O que devo fazer se duas bibliotecas fornecem uma função com o mesmo nome, gerando um conflito?

93

O que devo fazer se eu tiver duas bibliotecas que fornecem funções com nomes equivalentes?

qeek
fonte
2
são bibliotecas estáticas ou vinculadas dinamicamente?
Alnitak
precisamos de mais detalhes ... esses nomes são exportados? ou são usados ​​apenas internamente? Você pode mudar os nomes?
Johannes Schaub - litb
Eles estão dinamicamente ligados, ambos. Não posso alterar os nomes, pois não sou proprietário das bibliotecas.
qeek
Ótima pergunta. Claro que não seria um problema com estas duas bibliotecas se todos os símbolos foram prefixados com um ID único (por exemplo vorbis_..., sf_..., sdl_...). Isso é essencialmente o que C ++ faz com os nomes dos símbolos para funções com espaço de nomes.
Vortico
Esta é uma questão muito interessante, mas infelizmente muito imprecisa, razão pela qual se tem respostas muito amplas.
yugr

Respostas:

52
  • Se você controlar um ou ambos: edite um para alterar o nome e recompile Ou, de forma equivalente, veja as respostas de Ben e de desconhecidos que funcionarão sem acesso ao código-fonte.
  • Se você não controlar nenhum deles, você pode embrulhar um deles. Isto é, compilar outra biblioteca ( vinculada estaticamente !) Que não faz nada exceto reexportar todos os símbolos do original, exceto o ofensivo, que é alcançado por meio de um invólucro com um nome alternativo. O que é um aborrecimento.
  • Adicionado mais tarde: Como qeek diz que está falando sobre bibliotecas dinâmicas, as soluções sugeridas por Ferruccio e mouviciel são provavelmente as melhores. (Parece que vivi em dias antigos, quando a ligação estática era o padrão. Isso influencia meu pensamento.)

A propósito dos comentários: Por "exportar", quero dizer tornar visíveis os módulos vinculados à biblioteca --- equivalente à externpalavra-chave no escopo do arquivo. O modo como isso é controlado depende do sistema operacional e do vinculador. E é algo que sempre tenho que procurar.

dmckee --- gatinho ex-moderador
fonte
Esse foi meu primeiro pensamento também, mas você não vai acabar com o mesmo problema de colisão? No final, todo o projeto precisa ser vinculado - em tempo de compilação / vinculação ou em tempo de execução - em que ambas as bibliotecas problemáticas devem ser carregadas no estado em que se encontram.
Sniggerfardimungus
@unknown: O wrapper deve ser compilado com ligação estática e não deve exportar o símbolo ofensivo. Em seguida, você ainda pode vincular dinamicamente o wrapper. Editado para maior clareza, obrigado.
dmckee --- ex-moderador gatinho
Se o problema de qeek é com ddl's e não com bibliotecas estáticas, como é possível fazer uma nova biblioteca com um wrapper? Desde então, a biblioteca wrapper teria que envolver dinamicamente uma função na biblioteca que você não deseja vincular em primeiro lugar.
jeffD
@dmckee - o que você quer dizer com "exportar"?
4
talvez alguém pudesse fornecer um exemplo simples desta técnica? Um exe, duas bibliotecas, cada uma contendo uma função com o mesmo nome.
52

É possível renomear símbolos em um arquivo de objeto usando objcopy --redefine-sym old=new file(ver man objcopy).

Em seguida, basta chamar as funções usando seus novos nomes e vinculá-las ao novo arquivo objeto.

Ben
fonte
1
Agradável. Isso seria trivial para adicionar a um Makefile. Se as bibliotecas forem atualizadas, um encantamento de objcopy seria muito mais fácil de atualizar do que algumas das outras soluções.
sigjuice
8
Não se esqueça de renomear os símbolos nos arquivos de cabeçalho também.
mouviciel,
^ sed / awk / perl também seria útil para automatizar a renomeação dos símbolos no cabeçalho
Alex Reinking
16

No Windows, você pode usar LoadLibrary () para carregar uma dessas bibliotecas na memória e, em seguida, usar GetProcAddress () para obter o endereço de cada função necessária para chamar e chamar as funções por meio de um ponteiro de função.

por exemplo

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

obteria o endereço de uma função chamada bar em foo.dll e a chamaria.

Eu sei que os sistemas Unix oferecem suporte a funcionalidades semelhantes, mas não consigo pensar em seus nomes.

Ferruccio
fonte
dlopen dlsym, e dlclose. No entanto, o encapsulamento no Unix pode não ser tão eficaz quanto no Windows.
user877329
8

Aqui está uma ideia. Abra uma das bibliotecas ofensivas em um editor hexadecimal e altere todas as ocorrências das strings ofensivas para outra coisa. Você poderá então usar os novos nomes em todas as chamadas futuras.

ATUALIZAÇÃO: acabei de fazer isso e parece funcionar. Claro, eu não testei isso completamente - pode não ser mais do que uma boa maneira de explodir sua perna com uma espingarda hexedit.

Sniggerfardimungus
fonte
na verdade, não é uma solução terrível. Um pouco hackeado, mas tudo o que você faria é mudar as strings na tabela de símbolos. Nenhum dano funcional real nisso.
Evan Teran
Você provavelmente também gostaria de renomear a biblioteca - para que não aparecesse outra pessoa tentando carregar a coisa novamente. Você iria de um conflito para dezenas ou centenas. =] Eu amo isso sobre stackoverflow: nós temos uma resposta testada para uma pergunta e ela tem 3 votos. A primeira resposta (incompleta): 17. =]
Sniggerfardimungus
As oportunidades de renomeação são limitadas, pois você só poderá encurtar os nomes . Também no Linux, você terá dificuldade em atualizar as tabelas hash ELF.
yugr
7

Assumindo que você usa o Linux, você primeiro precisa adicionar

#include <dlfcn.h>

Declare a variável de ponteiro de função no contexto adequado, por exemplo,

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

Como Ferruccio afirmou em https://stackoverflow.com/a/678453/1635364 , carregue explicitamente a biblioteca que deseja usar executando (escolha seus sinalizadores favoritos)

void* dlhandle;
void* sym;

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

Leia o endereço da função que deseja chamar mais tarde

sym = dlsym(dlhandle, "conflicting_server_init");

atribuir e lançar da seguinte forma

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

Ligue de forma semelhante ao original. Finalmente, descarregue executando

dlclose(dlhandle);
vraa
fonte
6

Você não deve usá-los juntos. Se bem me lembro, o vinculador emite um erro nesse caso.

Eu não tentei, mas uma solução pode estar com dlopen(), dlsym()e dlclose()que permite que você lide programaticamente com bibliotecas dinâmicas. Se você não precisa das duas funções ao mesmo tempo, pode abrir a primeira biblioteca, usar a primeira função e fechar a primeira biblioteca antes de usar a segunda biblioteca / função.

mouviciel
fonte
Obrigado. Não pensei sobre isso. Embora, eu gostaria de ter os dois ao mesmo tempo.
qeek
E se eu quiser usar os dois ao mesmo tempo?
QZHua
@QZHua: Outras respostas (por exemplo, envolvendo renomeação de símbolo) devem resolver seu problema.
mouviciel de
6

Se você tiver arquivos .o lá, uma boa resposta aqui: https://stackoverflow.com/a/6940389/4705766

Resumo:

  1. objcopy --prefix-symbols=pre_string test.o para renomear os símbolos no arquivo .o

ou

  1. objcopy --redefine-sym old_str=new_str test.o para renomear o símbolo específico no arquivo .o.
Jee Lee
fonte
4

Esse problema é o motivo pelo qual c ++ tem namespaces. Não há realmente uma grande solução em c para 2 libs de terceiros com o mesmo nome.

Se for um objeto dinâmico, você poderá carregar explicitamente os objetos compartilhados (LoadLibrary / dlopen / etc) e chamá-lo dessa maneira. Alternativamente, se você não precisa de ambas as bibliotecas ao mesmo tempo no mesmo código, você pode fazer algo com link estático (se você tiver os arquivos .lib / .a).

Nenhuma dessas soluções se aplica a todos os projetos, é claro.

Brian Mitchell
fonte
1
Ai sim. Para esta pergunta geral, parece uma boa resposta. No entanto - os namespaces são legais se você compilar tudo junto no mesmo compilador. Hooray, nenhum conflito de nomes. Mas se você obtiver uma biblioteca na forma binária e quiser integrá-la a outro compilador, então - boa sorte. As regras de mutilação de nomes em arquivos de objeto são apenas o primeiro obstáculo (extern "C" pode ajudar, que desfaz o efeito dos namespaces).
Tomasz Gandor
3

Xingar? Pelo que eu sei, não há muito que você possa fazer se tiver duas bibliotecas que expõem pontos de link com o mesmo nome e você precisa vincular a ambos.

Vatine
fonte
12
Jurar é definitivamente o primeiro passo. Nenhuma dúvida sobre isso.
dmckee --- ex-moderador gatinho
1
"não há muito que você possa fazer" - isso ainda é relevante? Outras respostas fornecem várias soluções diferentes.
yugr
2

Você deve escrever uma biblioteca de wrapper em torno de um deles. Sua biblioteca de wrapper deve expor símbolos com nomes exclusivos e não expor os símbolos de nomes não exclusivos.

Sua outra opção é renomear o nome da função no arquivo de cabeçalho e renomear o símbolo no arquivo do objeto da biblioteca.

De qualquer forma, para usar os dois, será um trabalho de hack.

James Caccese
fonte
1

A questão está se aproximando de uma década, mas há novas pesquisas o tempo todo ...

Como já respondido, objcopy com o sinalizador --redefine-sym é uma boa escolha no Linux. Veja, por exemplo, https://linux.die.net/man/1/objcopy para documentação completa. É um pouco desajeitado porque você está essencialmente copiando a biblioteca inteira enquanto faz alterações e cada atualização requer que esse trabalho seja repetido. Mas pelo menos deve funcionar.

Para o Windows, carregar dinamicamente a biblioteca é uma solução e permanente, como a alternativa dlopen no Linux seria. No entanto, tanto dlopen () quanto LoadLibrary () adicionam código extra que pode ser evitado se o único problema for nomes duplicados. Aqui, a solução do Windows é mais elegante do que a abordagem objcopy: basta dizer ao vinculador que os símbolos em uma biblioteca são conhecidos por algum outro nome e usar esse nome. Existem algumas etapas para fazer isso. Você precisa fazer um arquivo def e fornecer a tradução do nome na seção EXPORTS. Consulte https://msdn.microsoft.com/en-us/library/hyx1zcd3.aspx (VS2015, ele será eventualmente substituído por versões mais recentes) ou http://www.digitalmars.com/ctg/ctgDefFiles.html(provavelmente mais permanente) para detalhes completos da sintaxe de um arquivo def. O processo seria fazer um arquivo def para uma das bibliotecas e então usar esse arquivo def para construir um arquivo lib e então vincular a esse arquivo lib. (Para DLLs do Windows, os arquivos lib são usados ​​apenas para vinculação, não para execução de código.) Consulte Como fazer um arquivo .lib quando tiver um arquivo .dll e um arquivo de cabeçalho para o processo de construção do arquivo lib. Aqui, a única diferença é adicionar os aliases.

Para Linux e Windows, renomeie as funções nos cabeçalhos da biblioteca cujos nomes estão sendo aliasados. Outra opção que deveria funcionar seria, em arquivos referentes aos novos nomes, # definir nome_antigo nome_novo, # incluir os cabeçalhos da biblioteca cujas exportações estão sendo alteradas, e então #undef nome_antigo no responsável pela chamada. Se houver muitos arquivos usando a biblioteca, uma alternativa mais fácil é fazer um cabeçalho ou cabeçalhos que envolvam os define, inclui e undefs e, em seguida, usar esse cabeçalho.

Espero que esta informação tenha sido útil!

Jim Monte
fonte
0

Nunca usei dlsym, dlopen, dlerror, dlclose, dlvsym, etc., mas estou olhando a página de manual e ela dá um exemplo de como abrir libm.so e extrair a função cos. O dlopen passa pelo processo de procura de colisões? Do contrário, o OP poderia simplesmente carregar ambas as bibliotecas manualmente e atribuir novos nomes a todas as funções que suas bibliotecas fornecem.

Sniggerfardimungus
fonte