¿Por qué importa el orden de la opción ‘-l’ en gcc? [duplicate]

8 minutos de lectura

Estoy tratando de compilar un programa que utiliza udis86 biblioteca. En realidad, estoy usando un programa de ejemplo dado en el manual de usuario 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 del usuario.

Claramente, el enlazador no puede vincular la biblioteca 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?

  • ¿Qué versión de gcc? Podría ser un error relacionado con la versión.

    – Enabren Tane

    10 de agosto de 2012 a las 0:52

  • ¡¡No es un error!! La versión es: gcc (Ubuntu/Linaro 4.4.4-14ubuntu5.1) 4.4.5

    usuario1129237

    10 de agosto de 2012 a las 1:10

avatar de usuario
Hormiga

Porque así es como funciona el algoritmo de vinculación utilizado por GNU linker (al menos cuando se trata de vincular bibliotecas estáticas). El enlazador es un enlazador de un solo paso y no vuelve a visitar las bibliotecas una vez que se han visto.

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

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

Entonces, si incluyeste alguna biblioteca usando -l, el enlazador usa esa biblioteca para resolver tantos símbolos actualmente pendientes como pueda, y luego se olvida por completo de esa biblioteca. Si se luego de repente descubre que ahora necesita algunos archivos de objetos adicionales de esa biblioteca, el enlazador no “regresará” a esa biblioteca para recuperar esos archivos de objetos adicionales. Ya es demasiado tarde.

Por esta razón, siempre es una buena idea utilizar -l opción tarde en la línea de comando del enlazador, para que cuando el enlazador llegue a ese -l puede determinar de manera confiable qué archivos de objetos necesita y cuáles no. Colocando el -l La opción 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, consta de un solo símbolo main), lo que significa que el enlazador no tomará nada de la biblioteca.

En su caso, su archivo de objeto example.o contiene referencias a 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 puedes usar -l opción para agregar la biblioteca: -ludis86. El enlazador buscará en su biblioteca y tomará todo lo que resuelva esos símbolos pendientes.

Si colocas el -ludis86 opción primero en la línea de comando, el enlazador efectivamente ignorar su biblioteca, ya que al principio no sabe que va a necesitar ud_init, ud_set_input_file etc. Más tarde, al procesar example.o descubrirá estos símbolos y los agregará a la lista de símbolos pendientes. Pero estos símbolos quedará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 manera circular, es posible que incluso sea necesario usar el -l dos veces con la misma biblioteca, para dar al enlazador dos oportunidades de recuperar los archivos de objeto necesarios de esa biblioteca.

  • No es sólo una cosa de GNU. Este es el comportamiento estándar requerido por POSIX: -l biblioteca Busca la biblioteca llamada liblibrary.a. Se buscará una biblioteca cuando se encuentre su nombre, por lo que la colocación de una opción -l es significativa. Varias bibliotecas estándar se pueden especificar de esta manera, como se describe en la sección DESCRIPCIÓN EXTENDIDA. Las implementaciones pueden reconocer sufijos definidos por la implementación que no sean .a para indicar bibliotecas. Ver pubs.opengroup.org/onlinepubs/9699919799/utilities/c99.html

    – R.. GitHub DEJA DE AYUDAR A ICE

    10 de agosto de 2012 a las 2:37

  • @R.. Esto plantea la pregunta, ¿por qué el estándar requiere este comportamiento? ¿Se puede tener alguna ventaja usando este enfoque? Otras herramientas de compilación como msvc y borland no siguen este enfoque y funcionan bien. En muchos sentidos, parece mejor ya que es menos propenso a errores para los usuarios de esta herramienta.

    – gran lobo

    4 de agosto de 2013 a las 1:44


  • @greatwolf: MSVC es casi lo contrario de “funciona bien” cuando se trata de C. De todos modos, la motivación para que el orden importe es que puede tener los mismos símbolos definidos en más de una biblioteca, en cuyo caso usted quiero poder controlar cuál se usa.

    – R.. GitHub DEJA DE AYUDAR A ICE

    4 de agosto de 2013 a las 2:15

  • Mi impresión es que este no es solo un problema de biblioteca estática, si especifica explícitamente -l: libwhatever.so, por ejemplo, el error del enlazador de referencia indefinido persiste siempre que el token -l: libwhatever.so ocurra antes en el comando gcc que el ficha object_file.o

    – alexandre iolov

    27 de diciembre de 2014 a las 13:16

  • Es posible que desee agregar un párrafo sobre grupos en GNU ld. Ver --start-group y --end-group en el ld(1) página man. Le dice efectivamente al enlazador que vuelva a visitar los archivos en el grupo.

    – jww

    18 de febrero de 2018 a las 7:11


avatar de usuario
selbie

Me encontré con este mismo problema hace un tiempo. La conclusión es que las herramientas gnu no siempre “buscarán hacia atrás” 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 libs y objs en el orden de dependencia (como descubrió 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. Utilizar el -( y -) params para especificar un grupo de archivos que tienen tales dependencias circulares. Mire el manual del enlazador GNU para --start-group y --end-group. Ver aquí para más detalles.

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

O usar volver a escanear

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

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

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

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

La opción -z rescan-now simplifica este proceso. El editor de enlaces procesa la opción -z rescan-now inmediatamente cuando se encuentra la opción en la línea de comando. Todos los archivos comprimidos que se han procesado desde la línea de comandos antes de esta opción se reprocesan inmediatamente. Este procesamiento intenta ubicar miembros de archivo adicionales que resuelven referencias de símbolos. Esta nueva exploración del archivo continúa hasta que se pasa por la lista de archivos en la 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 dependientes entre sí 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 del archivo continúa hasta que se produce un paso por el grupo de archivos en el que no se extraen nuevos miembros. Usando grupos de archivo, el ejemplo anterior se puede escribir de la siguiente manera.

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

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad