c gcc linker ld

¿Por qué es importante el orden de la opción ''-l'' en gcc?



linker ld (3)

Estoy intentando compilar un programa que usa la biblioteca udis86 . En realidad estoy usando un programa de ejemplo dado en el user-manual de user-manual de la biblioteca. Pero al compilar, da error. Los errores que obtengo son:

example.c:(.text+0x7): undefined reference to ''ud_init'' example.c:(.text+0x7): undefined reference to ''ud_set_input_file'' . . example.c:(.text+0x7): undefined reference to ''ud_insn_asm''

El comando que estoy usando es:

$ gcc -ludis86 example.c -o example

como se indica en el manual de usuario.

Claramente, el enlazador no puede vincular la biblioteca de libudis. Pero si cambio mi comando a:

$ gcc example.c -ludis86 -o example

Comienza a funcionar. Entonces, ¿puede alguien explicar cuál es el problema con el primer comando?


Golpeé este mismo problema hace un tiempo. La conclusión es que las herramientas gnu no siempre "buscarán" en la lista de la biblioteca para resolver los símbolos que faltan. Las soluciones fáciles son cualquiera de las siguientes:

  1. Simplemente especifique las libs y objs en el orden de dependencia (como ha descubierto anteriormente)

  2. O si tiene una dependencia circular (donde libA hace referencia a una función en libB, pero libB hace referencia a una función en libA), simplemente especifique las libs en la línea de comando dos veces. Esto es lo que sugiere la página del manual también. P.ej

    gcc foo.c -lfoo -lbar -lfoo

  3. Utilice los parámetros -( y -) para especificar un grupo de archivos comprimidos que tengan tales dependencias circulares. Mire el manual del enlazador GNU para --start-group y --end-group . Mira here para más detalles.

Cuando usas la opción 2 o 3, es probable que introduzcas un costo de rendimiento para vincular. Si no tienes mucho para vincular, puede que no importe.


O usa el reescaneo

de la página 41 de la Guía de enlaces y bibliotecas de Oracle Solaris 11.1 :

Pueden existir interdependencias entre archivos, de modo que la extracción de miembros de un archivo se debe resolver extrayendo miembros de otro archivo. Si estas dependencias son cíclicas, los archivos se deben especificar repetidamente en la línea de comando para satisfacer las referencias anteriores.

$ cc -o prog .... -lA -lB -lC -lA -lB -lC -lA

La determinación y el mantenimiento de las repetidas especificaciones de archivo pueden ser tediosos.

La opción -z rescan-now simplifica este proceso. La opción -z rescan-now es procesada por el editor de enlaces inmediatamente cuando la opción se encuentra en la línea de comando. Todos los archivos comprimidos que se procesaron desde la línea de comandos antes de esta opción se reprocesan inmediatamente. Este proceso intenta ubicar miembros de archivo adicionales que resuelven referencias de símbolos. Esta nueva búsqueda de archivos continúa hasta que se produce un pase sobre la lista de archivos en el que no se extraen nuevos miembros. El ejemplo anterior se puede simplificar de la siguiente manera.

$ cc -o prog .... -lA -lB -lC -z rescan-now

Alternativamente, las opciones -z rescan-start y -z rescan-end se pueden usar para agrupar archivos mutuamente dependientes en un grupo de archivos. Estos grupos son reprocesados ​​por el editor de enlaces inmediatamente cuando se encuentra el delimitador de cierre en la línea de comando. Los archivos encontrados dentro del grupo se reprocesan en un intento de localizar miembros de archivo adicionales que resuelvan las referencias de símbolos. Esta nueva exploración de archivo continúa hasta que se produce un pase sobre el grupo de archivo en el que no se extraen nuevos miembros. Usando grupos de archivos, el ejemplo anterior se puede escribir de la siguiente manera.

$ cc -o prog .... -z rescan-start -lA -lB -lC -z rescan-end


Porque así es como funciona el algoritmo de enlace utilizado por el enlazador GNU (al menos cuando se trata de vincular bibliotecas estáticas). El enlazador es un enlazador de paso único y no revisita las bibliotecas una vez que se han visto.

Una biblioteca es una colección (un archivo) de archivos de objeto. Cuando agrega una biblioteca usando la opción -l , el enlazador no toma incondicionalmente todos los archivos de objetos de la biblioteca. Solo toma los archivos de objetos que actualmente se necesitan , es decir, archivos que resuelven algunos símbolos actualmente no resueltos (pendientes). Después de eso, el vinculador olvida por completo esa biblioteca.

El vinculador mantiene continuamente la lista de símbolos pendientes a medida que el vinculador procesa los archivos del objeto de entrada, uno tras otro, de izquierda a derecha. A medida que procesa cada archivo objeto, algunos símbolos se resuelven y eliminan de la lista, otros símbolos no resueltos recientemente descubiertos se agregan a la lista.

Por lo tanto, si incluyó alguna biblioteca mediante -l , el vinculador usa esa biblioteca para resolver tantos símbolos pendientes actualmente como pueda, y luego se olvida por completo de esa biblioteca. Si más tarde descubre de repente que ahora necesita algunos archivos de objetos adicionales de esa biblioteca, el enlazador no "devolverá" esa biblioteca para recuperar esos archivos de objetos adicionales. Ya es demasiado tarde.

Por esta razón, siempre es una buena idea usar la opción -l tarde en la línea de comando del enlazador, de modo que cuando el enlazador llegue a ese -l pueda determinar de manera confiable qué archivos de objeto necesita y cuáles no necesita. . Colocar la opción -l como el primer parámetro del enlazador generalmente no tiene ningún sentido: al principio la lista de símbolos pendientes está vacía (o, más precisamente, consiste en un solo símbolo main ), lo que significa que el enlazador no tomar algo de la biblioteca en absoluto.

En su caso, su archivo de objeto example.o contiene referencias a los símbolos ud_init , ud_set_input_file , etc. El enlazador debe recibir ese archivo de objeto primero. Agregará estos símbolos a la lista de símbolos pendientes. Después de eso, puede usar la opción -l para agregar su biblioteca: -ludis86 . El vinculador buscará en su biblioteca y tomará todo lo que resuelva esos símbolos pendientes.

Si coloca la opción -ludis86 primero en la línea de comando, el enlazador ignorará efectivamente su biblioteca, ya que al principio no sabe que necesitará ud_init , ud_set_input_file , etc. Luego, al procesar example.o , descubrirá estos símbolos y agrégalos a la lista de símbolos pendientes. Pero estos símbolos permanecerán sin resolver hasta el final, ya que -ludis86 ya fue procesado (y efectivamente ignorado).

A veces, cuando dos (o más) bibliotecas se refieren entre sí de forma circular, incluso podría ser necesario utilizar la opción -l dos veces con la misma biblioteca, para dar dos posibilidades al enlazador de recuperar los archivos objeto necesarios de esa biblioteca.