c++ - serie - compile dynamic library
¿Comprobación sencilla de símbolos no resueltos en bibliotecas compartidas? (4)
Estoy escribiendo una biblioteca bastante grande de objetos compartidos en C ++, y me he encontrado con un pequeño problema que hace que la depuración sea difícil:
Si defino una función / método en un archivo de encabezado y olvido crear un apéndice para él (durante el desarrollo), dado que estoy compilando como una biblioteca de objetos compartidos en lugar de un ejecutable, no aparecen errores en tiempo de compilación diciéndome que tengo olvidado de implementar esa función. La única forma en que descubro que algo está mal es en tiempo de ejecución, cuando al final una aplicación que enlaza con esta biblioteca se cae con un error de ''símbolo indefinido''.
Estoy buscando una manera fácil de verificar si tengo todos los símbolos que necesito en el momento de la compilación, tal vez algo que pueda agregar a mi Makefile.
Una solución que se me ocurrió es ejecutar la biblioteca compilada a través de nm -C -U
para obtener una lista exigida de todas las referencias indefinidas. El problema es que esto también aparece con la lista de todas las referencias que se encuentran en otras bibliotecas, como GLibC, que por supuesto se vinculará junto con esta biblioteca cuando se prepare la aplicación final. Sería posible usar la salida de nm
a grep
través de todos mis archivos de encabezado y ver si alguno de los nombres corresponde ... pero esto parece una locura. Sin duda, este no es un problema poco común y hay una mejor manera de resolverlo.
¿Qué tal un testuite? Usted crea ejecutables simulados que se vinculan a los símbolos que necesita. Si el enlace falla, significa que la interfaz de su biblioteca está incompleta.
En Linux (que parece estar usando) ldd -r a.out
debería darle exactamente la respuesta que está buscando.
ACTUALIZACIÓN: una forma trivial de crear a.out
contra la cual verificar:
echo "int main() { return 0; }" | g++ -xc++ - ./libMySharedLib.so
ldd -r ./a.out
Mira la opción del vinculador -z defs
/ --no-undefined
. Al crear un objeto compartido, hará que el enlace falle si hay símbolos no resueltos.
Si está utilizando gcc para invocar el enlazador, usará la opción compilador -Wl
para pasar la opción al enlazador:
gcc -shared ... -Wl,-z,defs
Como ejemplo, considere el siguiente archivo:
#include <stdio.h>
void forgot_to_define(FILE *fp);
void doit(const char *filename)
{
FILE *fp = fopen(filename, "r");
if (fp != NULL)
{
forgot_to_define(fp);
fclose(fp);
}
}
Ahora, si compila eso en un objeto compartido, tendrá éxito:
> gcc -shared -fPIC -o libsilly.so silly.c && echo succeeded || echo failed
succeeded
Pero si agrega -z defs
, el enlace fallará y le informará sobre su símbolo perdido:
> gcc -shared -fPIC -o libsilly.so silly.c -Wl,-z,defs && echo succeeded || echo failed
/tmp/cccIwwbn.o: In function `doit'':
silly.c:(.text+0x2c): undefined reference to `forgot_to_define''
collect2: ld returned 1 exit status
failed
Tuve el mismo problema una vez. Estaba desarrollando un modelo de componentes en C ++ y, por supuesto, los componentes deberían cargarse en tiempo de ejecución dinámicamente. Me vienen a la mente tres soluciones, que fueron las que apliqué:
- Tómese su tiempo para definir un sistema de compilación que pueda compilar estáticamente. Perderá algo de tiempo en la ingeniería, pero le ahorrará mucho tiempo capturar estos molestos errores de tiempo de ejecución.
- Agrupe sus funciones en secciones bien conocidas y bien entendidas, de modo que pueda agrupar las funciones / stubs para asegurarse de que cada función correspondiente tenga su stub. Si se toma el tiempo de documentarlo bien, puede escribir tal vez un script que verifique las definiciones (a través de, por ejemplo, sus comentarios doxygen) y verifique el archivo .cpp correspondiente.
- Haga varios ejecutables de prueba que carguen el mismo conjunto de bibliotecas y especifique el indicador RTLD_NOW para dlopen (si tiene menos de * NIX). Señalarán los símbolos faltantes.
Espero que ayude.