c++ makefile linker g++

c++ - Forzar a un vinculador a fallar con un error de definición múltiple, incluso si se incluye--whole-archive



makefile linker (2)

Creo que la única forma correcta de resolver el problema es mover el contenido de bar.a solución a alguna biblioteca compartida y, por lo tanto, resolver el problema de la herencia de diamante ... pero creo que está fuera del alcance de este problema.

Lo único que puedo pensar es crear un validador "personalizado" ejecutado justo antes de vincular el archivo ejecutable final. Stg de esta manera:

nm *.so | / # get all symbols grep " [B|T] " | / # only exported ones egrep -v "__bss_start|_end|_fini|_init" | / # filter out some commons, probably to be extended awk ''{print $3}'' | / # get only symbol name sort | uniq -c | / # sort and count egrep -v "^[ ]+1 " # get only those that have multiple definitions

Esto imprime todos los símbolos fuertes (exportados) de las bibliotecas que se definen más de una vez. Puede envolverlo fácilmente en una secuencia de comandos que devuelva el código de estado de error si la salida no está vacía con un mensaje significativo.

La versión experimental de tu makefile parcheado se vería así:

run: main.cxx foo.so bar.so ! nm foo.so bar.so | grep " [B|T] " | egrep -v "__bss_start|_end|_fini|_init" | awk ''{print $3}'' | sort | uniq -c | egrep -v "^[ ]+1 " g++ -std=c++11 $^ -o $@

( ! nota ! para revertir el código de salida de grep final que busca cualquier salida uniq -c que no comience con 1)

Sé que es una solución realmente hackista, fea y no portátil, pero pensé que podría ser de algún valor en casos de esquina como el suyo.

Este ejemplo consta de varios archivos:

// baz.cxx int wat = 0; int counter = ++wat;

// foo.cxx (empty)

// bar.cxx (empty)

// main.cxx #include <iostream> extern int wat; int main() { std::cout << wat << ''/n''; }

// makefile run : main.cxx foo.so bar.so g++ -std=c++11 $^ -o $@ baz.a : baz.cxx g++ -std=c++11 -c $^ -o baz.o -fPIC ar rcs $@ baz.o %.so : %.cxx baz.a g++ -std=c++11 $< -Wl,--whole-archive baz.a -Wl,--no-whole-archive -o $@ -shared -fPIC

Como está, si ejecuta make && LD_LIBRARY_PATH=. ./run make && LD_LIBRARY_PATH=. ./run , todo con compile, build, link, run y output 2 . Esto se debe a que tanto foo.so como bar.so proporcionan wat , y la inicialización del counter se ejecuta dos veces.

¿Hay alguna manera de forzar de alguna manera la run para que no se vincule con un error de definición múltiple en este caso, al tiempo que se asegura de que tanto foo.so como bar.so tengan una definición de wat ?


Podría usar los scripts del enlazador libfoo.so y libbar.so con una parte estática que crea un conflicto de símbolos (e instale los DSO reales en algún lugar no obvio, para que -lfoo y -lbar no los -lbar ).

Algo como esto:

  • libfoo.so

    INPUT(conflict.o) INPUT(./foo.so)

  • libbar.so

    INPUT(conflict.o) INPUT(./bar.so)

  • conflict.cxx

    int conflict;

Esto resulta en:

g++ -std=c++11 main.cxx -o run -L. -lfoo -lbar conflict.o:(.bss+0x0): multiple definition of `conflict'' conflict.o:(.bss+0x0): first defined here collect2: error: ld returned 1 exit status

Esto solo detectará conflictos dentro de la misma ejecución del editor de enlaces. Aún podría vincular dos DSO diferentes con -lfoo y -lbar , y vincular esos dos a la aplicación, sin un error del editor de enlaces.

Un problema similar se resuelve con las anotaciones ABI, pero creo que necesitan cambios específicos de ld .

Si una verificación de tiempo de ejecución es aceptable, podría tener una lista de símbolos débiles definidos por cada DSO que debe entrar en conflicto e implementar un constructor ELF que aborta el proceso si se define más de uno de ellos. No tengo conocimiento de nada que logre algo igualmente confiable en el tiempo de enlace estático con la cadena de herramientas GNU. Tal vez esto vale la pena un error de RFE contra binutils.