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.