2009-10-24 19 views
62

estoy escribiendo una bastante grande biblioteca de C++-objeto compartido, y se han encontrado con un pequeño problema que hace que la depuración de un dolor:¿Comprobación fácil de símbolos no resueltos en bibliotecas compartidas?

Si defino una función/método en un archivo de cabecera, y se olvide de crear un esbozo de (durante el desarrollo), dado que estoy compilando como una biblioteca de objetos compartidos en lugar de como un archivo ejecutable, no aparecen errores en el tiempo de compilación diciéndome que he olvidado 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 comprobar si tengo todos los símbolos que necesito en tiempo de 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 solicitada 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 a 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.

+1

'nm -C -u' me ha salvado varias veces! (fíjese en minúscula '-u' en mi sistema.) Deje este comentario aquí para que pueda encontrarlo la próxima vez que lo necesite. – dpritch

Respuesta

73

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 se está empleando gcc para invocar el enlazador, va a utilizar el compilador -Wl opción 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 se construye de que en un objeto compartido, que tendrá éxito:

> gcc -shared -fPIC -o libsilly.so silly.c && echo succeeded || echo failed 
succeeded 

embargo, si se agrega -z defs, el enlace se producirá un error y le informará sobre el símbolo de su falta:

> 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 
+2

+1 esta respuesta podría ayudar a los chicos que provienen de Windows de fondo en el que los * DLL * símbolos externos deben resolverse durante el tiempo de compilación. ¡Bien hecho por el bonito fragmento de código! –

+0

@ShmilTheCat: Hola, He intentado poner -Wl, -z, defs en mi archivo make, aún estoy obteniendo un error de símbolo indefinido durante la ejecución, pero no durante la compilación. ¿Que puedo hacer?. En mi caso, tengo dos carpetas, una dentro de otra (por ejemplo, A/B, i, e B está dentro de A), y puedo ver algunos símbolos o B, en "libA.so". No estoy seguro de que cada símbolo en B esté disponible en "libA.so". ¿Cómo me aseguro de eso? – Vinay

+0

@ShmilTheCat Para que las cosas se parezcan más a una DLL de Windows, puede agregar '-Bsymbolic' a la línea de comando del enlazador. Incluso si 'forgot_to_define' ahora existe en la biblioteca gracias a la comprobación' -z', el ejecutable aún puede anularlo con su propia definición y las propias definiciones de la biblioteca irán a esa anulación; '-Bsymbolic' obliga a las cosas a que las propias definiciones de la biblioteca a sus funciones cumplan sus funciones. – Kaz

7

¿Qué tal un testsuite? 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.

3

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é:

  1. 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.
  2. Agrupe sus funciones en secciones bien conocidas y bien entendidas, para 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.
  3. Realice 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.

+0

en las líneas de RTLD_NOW, ¿existe un env var para forzar el enlace inmediato (no perezoso)? –

+1

yup. aquí es LD_BIND_NOW (libc5; glibc desde 2.1.1) Si se establece en cadena no vacía, hace que el enlazador dinámico resuelva todos los símbolos al inicio del programa en lugar de diferir la resolución de llamada de función al punto cuando se hace referencia por primera vez. Esto es útil cuando se usa un depurador. –

+0

Esto también puede ser útil para el propósito del OP: LD_WARN (ELF solamente) (glibc desde 2.1.3) Si se establece en una cadena no vacía, advierta sobre símbolos no resueltos. –

8

En Linux (que parece estar usando) ldd -r a.out debería darle exactamente la respuesta que está buscando.

ACTUALIZACIÓN: una manera trivial para crear a.out contra el que comprobar:

echo "int main() { return 0; }" | g++ -xc++ - ./libMySharedLib.so 
ldd -r ./a.out 
+3

Eso hace casi exactamente lo mismo que nm -CU, que sugerí en la publicación original. El problema es que no hay una aplicación 'a.out' de la que hablar, es una biblioteca compartida, por lo que ldd -r mylibrary.so da un montón de resultados, porque usa símbolos de otras bibliotecas dinámicas ... Estoy particularmente interesado en símbolos que faltan definidos en * my * archivos de encabezado, en lugar de bibliotecas externas. –

Cuestiones relacionadas