¿Qué debo hacer si dos bibliotecas proporcionan una función con el mismo nombre que genera un conflicto?

94

¿Qué debo hacer si tengo dos bibliotecas que proporcionan funciones con nombres equivalentes?

qeek
fuente
2
¿Son estas bibliotecas estáticas o están vinculadas dinámicamente?
Alnitak
necesitamos más detalles ... ¿se exportan esos nombres? ¿O se usan solo internamente? ¿Puedes cambiar los nombres?
Johannes Schaub - litb
Ambos están vinculados dinámicamente. No puedo cambiar los nombres, ya que no soy dueño de las bibliotecas.
qeek
Gran pregunta. Por supuesto que no sería un problema con estas dos bibliotecas si todos los símbolos se prefijan con un identificador único (por ejemplo vorbis_..., sf_..., sdl_...). Esto es esencialmente lo que C ++ hace con los nombres de símbolos para funciones con espacios de nombres.
Vortico
Esta es una pregunta muy interesante, pero lamentablemente es demasiado imprecisa, razón por la cual las respuestas son demasiado amplias.
yugr

Respuestas:

52
  • Si controla uno o ambos: edite uno para cambiar el nombre y recompilar O, de manera equivalente, vea las respuestas de Ben y desconocido , que funcionarán sin acceso al código fuente.
  • Si no controla ninguno de ellos, puede envolver uno de ellos. Eso es compilar otra biblioteca ( ¡ enlazada estáticamente !) Que no hace nada más que reexportar todos los símbolos del original excepto el infractor, al que se llega a través de un contenedor con un nombre alternativo. Qué lío.
  • Agregado más tarde: dado que qeek dice que está hablando de bibliotecas dinámicas, las soluciones sugeridas por Ferruccio y mouviciel son probablemente las mejores. (Parece que vivo en días lejanos cuando la vinculación estática era la predeterminada. Colorea mi pensamiento).

A propósito de los comentarios: Por "exportar" me refiero a hacer visibles los módulos que enlazan con la biblioteca --- equivalente a la externpalabra clave en el alcance del archivo. Cómo se controla esto depende del sistema operativo y del enlazador. Y es algo que siempre tengo que buscar.

dmckee --- ex-gatito moderador
fuente
Ese fue mi primer pensamiento también, pero ¿no terminarás con el mismo problema de colisión? Al final, todo el proyecto tiene que vincularse, en el momento de la compilación / vinculación o en el tiempo de ejecución, momento en el que ambas bibliotecas infractoras deben cargarse tal cual.
Sniggerfardimungus
@unknown: el contenedor debe compilarse con un enlace estático y no debe exportar el símbolo ofensivo. Entonces aún puede vincular dinámicamente el contenedor. Editado para mayor claridad, gracias.
dmckee --- ex-moderador gatito
Si el problema de qeek es con las bibliotecas ddl y no estáticas, ¿cómo es posible crear una nueva biblioteca con un contenedor? Dado que, la biblioteca contenedora tendría que envolver dinámicamente una función en la biblioteca con la que no desea vincular en primer lugar.
jeffD
@dmckee - ¿Qué quieres decir con "exportar"?
4
¿Quizás alguien podría proporcionar un ejemplo simple de esta técnica? Un exe, dos bibliotecas, cada una con una función con el mismo nombre.
53

Es posible cambiar el nombre de los símbolos en un archivo de objeto usando objcopy --redefine-sym old=new file(ver man objcopy).

Luego simplemente llame a las funciones usando sus nuevos nombres y vincule con el nuevo archivo de objeto.

Ben
fuente
2
Agradable. Sería trivial agregarlo a un Makefile. Si las bibliotecas se actualizan alguna vez, un encantamiento objcopy sería mucho más fácil de actualizar que algunas de las otras soluciones.
sigjuice
9
No olvide cambiar el nombre de los símbolos en los archivos de encabezado también.
mouviciel
^ sed / awk / perl también sería útil para automatizar el cambio de nombre de los símbolos en el encabezado
Alex Reinking
16

En Windows, puede usar LoadLibrary () para cargar una de esas bibliotecas en la memoria y luego usar GetProcAddress () para obtener la dirección de cada función que necesita llamar y llamar a las funciones a través de un puntero de función.

p.ej

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

obtendría la dirección de una función llamada bar en foo.dll y la llamaría.

Sé que los sistemas Unix admiten una funcionalidad similar, pero no puedo pensar en sus nombres.

Ferruccio
fuente
dlopen dlsymy dlclose. Sin embargo, la encapsulación en Unix puede no ser tan efectiva como en Windows.
user877329
8

He aquí un pensamiento. Abra una de las bibliotecas infractoras en un editor hexadecimal y cambie todas las apariciones de las cadenas infractoras por otra. Entonces debería poder usar los nuevos nombres en todas las llamadas futuras.

ACTUALIZACIÓN: Lo acabo de hacer en este extremo y parece funcionar. Por supuesto, no lo he probado a fondo; puede que no sea más que una buena manera de volar tu pierna con una escopeta de edición hexadecimal.

Sniggerfardimungus
fuente
en realidad no es una solución terrible. Un poco hacker, pero todo lo que harías es cambiar las cadenas en la tabla de símbolos. No hay ningún daño funcional real en eso.
Evan Teran
Probablemente también desee cambiar el nombre de la biblioteca, no sea que aparezca alguien más, tratando de cargar la cosa nuevamente. Pasarías de un conflicto a docenas o cientos. =] Me encanta esto de stackoverflow: tenemos una respuesta probada a una pregunta y tiene 3 votos. La primera respuesta (incompleta): 17. =]
Sniggerfardimungus
1
Las oportunidades de cambio de nombre son limitadas, ya que solo podrá acortar los nombres . También en Linux tendrá dificultades para actualizar las tablas hash de ELF.
yugr
7

Suponiendo que usa linux, primero debe agregar

#include <dlfcn.h>

Declare la variable de puntero de función en el contexto adecuado, por ejemplo,

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

Como dijo Ferruccio en https://stackoverflow.com/a/678453/1635364 , cargue explícitamente la biblioteca que desea usar ejecutando (elija sus banderas favoritas)

void* dlhandle;
void* sym;

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

Lea la dirección de la función a la que desea llamar más tarde

sym = dlsym(dlhandle, "conflicting_server_init");

asignar y emitir de la siguiente manera

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

Llame de forma similar a la original. Finalmente, descargue ejecutando

dlclose(dlhandle);
vraa
fuente
6

No debe usarlos juntos. Si no recuerdo mal, el vinculador emite un error en tal caso.

No he intentado, pero una solución puede ser con dlopen(), dlsym()y dlclose()que le permiten manipular mediante programación bibliotecas dinámicas. Si no necesita las dos funciones al mismo tiempo, puede abrir la primera biblioteca, usar la primera función y cerrar la primera biblioteca antes de usar la segunda biblioteca / función.

Mouviciel
fuente
Gracias. No pensé en esto. Aunque me gustaría tener ambos al mismo tiempo.
qeek
¿Qué pasa si me gustaría usar ambos al mismo tiempo?
QZHua
@QZHua: Otras respuestas (por ejemplo, relacionadas con el cambio de nombre de símbolos) deberían resolver su problema.
mouviciel
6

Si tiene archivos .o allí, una buena respuesta aquí: https://stackoverflow.com/a/6940389/4705766

Resumen:

  1. objcopy --prefix-symbols=pre_string test.o para cambiar el nombre de los símbolos en el archivo .o

o

  1. objcopy --redefine-sym old_str=new_str test.o para cambiar el nombre del símbolo específico en el archivo .o.
Jee lee
fuente
4

Este problema es la razón por la que c ++ tiene espacios de nombres. Realmente no hay una gran solución en c para 2 bibliotecas de terceros con el mismo nombre.

Si se trata de un objeto dinámico, es posible que pueda cargar explícitamente los objetos compartidos (LoadLibrary / dlopen / etc.) y llamarlo de esa manera. Alternativamente, si no necesita ambas bibliotecas al mismo tiempo en el mismo código, quizás pueda hacer algo con enlaces estáticos (si tiene los archivos .lib / .a).

Ninguna de estas soluciones se aplica a todos los proyectos, por supuesto.

Brian Mitchell
fuente
1
Oh si. Para esta pregunta general, esta parece una buena respuesta. Sin embargo, los espacios de nombres son geniales si compila todo junto en el mismo compilador. Hurra, ningún nombre choca. Pero si obtiene una biblioteca en forma binaria y desea integrarla con otro compilador, entonces, buena suerte. Las reglas de alteración de nombres en archivos de objetos son solo el primer obstáculo (la "C" externa puede ayudar, lo que deshace el efecto de los espacios de nombres).
Tomasz Gandor
3

¿Jurar? Hasta donde yo sé, no hay mucho que pueda hacer si tiene dos bibliotecas que exponen puntos de enlace con el mismo nombre y necesita vincular ambos.

Vatine
fuente
12
Jurar es definitivamente el primer paso. No hay duda de eso.
dmckee --- ex-moderador gatito
1
"No hay mucho que puedas hacer". ¿Sigue siendo relevante? Otras respuestas proporcionan numerosas soluciones diferentes.
yugr
2

Debería escribir una biblioteca contenedora alrededor de uno de ellos. Su biblioteca contenedora debe exponer símbolos con nombres únicos y no exponer los símbolos de los nombres no únicos.

Su otra opción es cambiar el nombre de la función en el archivo de encabezado y cambiar el nombre del símbolo en el archivo de objetos de la biblioteca.

De cualquier manera, para usar ambos, será un truco.

James Caccese
fuente
1

La pregunta se acerca a una década, pero hay nuevas búsquedas todo el tiempo ...

Como ya se respondió, objcopy con el indicador --redefine-sym es una buena opción en Linux. Consulte, por ejemplo, https://linux.die.net/man/1/objcopy para obtener la documentación completa. Es un poco torpe porque esencialmente está copiando toda la biblioteca mientras realiza cambios y cada actualización requiere que este trabajo se repita. Pero al menos debería funcionar.

Para Windows, cargar dinámicamente la biblioteca es una solución y una permanente como lo sería la alternativa dlopen en Linux. Sin embargo, tanto dlopen () como LoadLibrary () agregan código adicional que puede evitarse si el único problema son los nombres duplicados. Aquí, la solución de Windows es más elegante que el enfoque objcopy: simplemente dígale al enlazador que los símbolos en una biblioteca se conocen por algún otro nombre y use ese nombre. Hay algunos pasos para hacerlo. Debe crear un archivo def y proporcionar la traducción del nombre en la sección EXPORTACIONES. Consulte https://msdn.microsoft.com/en-us/library/hyx1zcd3.aspx (VS2015, eventualmente será reemplazado por versiones más nuevas) o http://www.digitalmars.com/ctg/ctgDefFiles.html(probablemente más permanente) para obtener detalles de sintaxis completos de un archivo def. El proceso sería crear un archivo def para una de las bibliotecas, luego usar este archivo def para construir un archivo lib y luego vincularlo con ese archivo lib. (Para las DLL de Windows, los archivos lib solo se utilizan para vincular, no para ejecutar código). Consulte Cómo crear un archivo .lib cuando tenga un archivo .dll y un archivo de encabezado para el proceso de creación del archivo lib. Aquí la única diferencia es agregar los alias.

Tanto para Linux como para Windows, cambie el nombre de las funciones en los encabezados de la biblioteca a cuyos nombres se les asigna un alias. Otra opción que debería funcionar sería, en archivos que se refieran a los nuevos nombres, #define old_name new_name, #incluya los encabezados de la biblioteca cuyas exportaciones están siendo alias, y luego #undef old_name en el llamador. Si hay muchos archivos que utilizan la biblioteca, una alternativa más sencilla es crear un encabezado o encabezados que envuelvan las definiciones, las inclusiones y las indefs y luego usar ese encabezado.

¡Espero que esta información haya sido útil!

Jim Monte
fuente
0

Nunca he usado dlsym, dlopen, dlerror, dlclose, dlvsym, etc., pero estoy mirando la página del manual y me da un ejemplo de cómo abrir libm.so y extraer la función cos. ¿Dlopen pasa por el proceso de búsqueda de colisiones? Si no es así, el OP podría simplemente cargar ambas bibliotecas manualmente y asignar nuevos nombres a todas las funciones que brindan sus bibliotecas.

Sniggerfardimungus
fuente