tag etiqueta descripcion c compiler-construction linker elf relocation

etiqueta - ¿Qué significa la reubicación R_X86_64_32S y R_X86_64_64?



meta title y meta descripcion (5)

Obtuve el siguiente error cuando intenté compilar una aplicación C en FreeBSD de 64 bits:

reubicación R_X86_64_32S no se puede usar cuando se hace un objeto compartido; recompilar con -fPIC

¿Qué es la reubicación de R_X86_64_32S y qué es R_X86_64_64 ?

Busqué en Google el error y sus posibles causas. Sería fantástico si alguien pudiera decir qué significa realmente R_X86_64_32S.


En mi caso el problema surgió porque el programa para compilar esperaba encontrar bibliotecas compartidas en un directorio remoto, mientras que solo las bibliotecas estáticas correspondientes estaban allí en un error.

En realidad, este error de reubicación fue un error de archivo no encontrado en el disfraz.

He detallado cómo me las arreglé en este otro hilo https://.com/a/42388145/5459638


Eso significa que compiló un objeto compartido sin usar el indicador -fPIC como debería:

gcc -shared foo.c -o libfoo.so # Wrong

Tienes que llamar

gcc -shared -fPIC foo.c -o libfoo.so # Right

En la plataforma ELF (Linux), los objetos compartidos se compilan con código independiente de posición: código que puede ejecutarse desde cualquier ubicación en la memoria; si no se proporciona este indicador, el código generado depende de la posición, por lo que no es posible utilizarlo objeto.


Me encontré con este problema y encontré que esta respuesta no me ayudó. Intentaba vincular una biblioteca estática con una biblioteca compartida. También investigué colocando el modificador -fPIC anteriormente en la línea de comando (como se aconseja en las respuestas en otro lugar). Lo único que solucionó el problema, para mí, fue cambiar la biblioteca estática a compartida. Sospecho que el mensaje de error sobre -fPIC puede ocurrir debido a una serie de causas, pero fundamentalmente lo que desea ver es cómo se están construyendo sus bibliotecas, y desconfíe de las bibliotecas que se están construyendo de diferentes maneras.


Para que algo de esto tenga sentido, primero debe:

  • vea un ejemplo mínimo de reubicación: https://.com/a/30507725/895245
  • comprender la estructura básica de un archivo ELF: https://.com/a/30648229/895245

Estándares

R_X86_64_64 , R_X86_64_32 y R_X86_64_32S están todos definidos por System V AMD ABI , que contiene las características específicas de AMD64 del formato de archivo ELF.

Todos son valores posibles para el campo ELF32_R_TYPE de una entrada de reubicación, especificada en el Sistema V ABI 4.1 (1997) que especifica las partes neutrales de la arquitectura del formato ELF. Ese estándar solo especifica el campo, pero no sus valores dependientes del arco.

En 4.4.1 "Tipos de reubicación", vemos la tabla de resumen:

Name Field Calculation ------------ ------ ----------- R_X86_64_64 word64 A + S R_X86_64_32 word32 A + S R_X86_64_32S word32 A + S

Explicaremos esta tabla más adelante.

Y la nota:

Las reubicaciones R_X86_64_32 y R_X86_64_32S truncan el valor calculado a 32 bits. El vinculador debe verificar que el valor generado para la reubicación R_X86_64_32 (R_X86_64_32S) se extienda en cero (extensión de signo) al valor original de 64 bits.

Ejemplo de R_X86_64_64 y R_X86_64_32

Primero veamos en R_X86_64_64 y R_X86_64_32 :

.section .text /* Both a and b contain the address of s. */ a: .long s b: .quad s s:

Entonces:

as --64 -o main.o main.S objdump -dzr main.o

Contiene:

0000000000000000 <a>: 0: 00 00 add %al,(%rax) 0: R_X86_64_32 .text+0xc 2: 00 00 add %al,(%rax) 0000000000000004 <b>: 4: 00 00 add %al,(%rax) 4: R_X86_64_64 .text+0xc 6: 00 00 add %al,(%rax) 8: 00 00 add %al,(%rax) a: 00 00 add %al,(%rax)

Probado en Ubuntu 14.04, Binutils 2.24.

Ignore el desmontaje por el momento (que no tiene sentido ya que son datos), y observe únicamente las etiquetas, los bytes y las reubicaciones.

La primera reubicación:

0: R_X86_64_32 .text+0xc

Lo que significa:

  • 0 : actúa sobre el byte 0 (etiqueta a )
  • R_X86_64_ : prefijo utilizado por todos los tipos de reubicación del sistema AMD64 V ABI
  • 32 : la dirección de 64 bits de la etiqueta s se trunca en una dirección de 32 bits porque solo especificamos un .long (4 bytes)
  • .text : estamos en la sección .text
  • 0xc : este es el sumando , que es un campo de la entrada de reubicación

La dirección de la reubicación se calcula como:

A + S

Dónde:

  • A : el sumando, aquí 0xC
  • S : el valor del símbolo antes de la reubicación, aquí 00 00 00 00 == 0

Por lo tanto, después de la reubicación, la nueva dirección será 0xC == 12 bytes en la sección .text .

Esto es exactamente lo que esperamos, ya que s viene después de .long (4 bytes) y .quad (8 bytes).

R_X86_64_64 es análogo, pero más simple, ya que aquí no hay necesidad de truncar la dirección de s . Esto se indica mediante el estándar a través de word64 lugar de word32 en la columna Field .

R_X86_64_32S vs R_X86_64_32

La diferencia entre R_X86_64_32S vs R_X86_64_32 es cuando el enlazador se quejará "con la reubicación truncada para encajar":

  • 32 : se queja si el valor truncado después de la reubicación no extiende cero el valor anterior, es decir, los bytes truncados deben ser cero:

    Por ejemplo: FF FF FF FF 80 00 00 00 a 80 00 00 00 genera una queja porque FF FF FF FF no es cero.

  • 32S : se queja si el valor truncado después de la reubicación no se firma extender el valor anterior.

    Por ejemplo: FF FF FF FF 80 00 00 00 a 80 00 00 00 está bien, porque el último bit de 80 00 00 00 y los bits truncados son todos 1.

Ver también: ¿Qué significa este error de GCC "... reubicación truncada para adaptarse ..."?

R_X86_64_32S se puede generar con:

.section .text .global _start _start: mov s, %eax s:

Entonces:

as --64 -o main.o main.S objdump -dzr main.o

Da:

0000000000000000 <_start>: 0: 8b 04 25 00 00 00 00 mov 0x0,%eax 3: R_X86_64_32S .text+0x7

Ahora podemos observar la "reubicación" truncada para encajar en 32S con una secuencia de comandos del enlazador:

SECTIONS { . = 0xFFFFFFFF80000000; .text : { *(*) } }

Ahora:

ld -Tlink.ld a.o

Está bien, porque: 0xFFFFFFFF80000000 se trunca en 80000000 , que es una extensión de signo.

Pero si cambiamos el script del enlazador a:

. = 0xFFFF0FFF80000000;

Ahora genera el error, porque ese 0 ya no es una extensión de signo.

Justificación para usar 32S para el acceso a la memoria pero 32 para las inmediatas: ¿ cuándo es mejor para un ensamblador usar la reubicación ampliada de la señal como R_X86_64_32S en lugar de la extensión cero como R_X86_64_32?


R_X86_64_32S y R_X86_64_64 son nombres de tipos de reubicación, para código compilado para la arquitectura amd64. Puedes buscarlos todos en el amd64 ABI . Según él, R_X86_64_64 se divide en:

  • R_X86_64 - todos los nombres están prefijados con este
  • 64 - Reubicación directa de 64 bit

y R_X86_64_32S a:

  • R_X86_64 - prefijo
  • 32S: truncar el valor a 32 bits y extender signo

lo que básicamente significa "el valor del símbolo apuntado por esta reubicación, más cualquier sumando", en ambos casos. Para R_X86_64_32S el vinculador verifica que el signo de valor generado se extiende al valor original de 64 bits.

Ahora, en un archivo ejecutable, los segmentos de código y datos reciben una dirección base virtual específica. El código ejecutable no se comparte y cada ejecutable obtiene su propio espacio de direcciones nuevo. Esto significa que el compilador sabe exactamente dónde estará la sección de datos y puede hacer referencia directamente a ella. Las bibliotecas, por otro lado, solo pueden saber que su sección de datos estará en un desplazamiento específico de la dirección base; el valor de esa dirección base solo puede conocerse en tiempo de ejecución. Por lo tanto, todas las bibliotecas deben producirse con código que pueda ejecutarse sin importar dónde se coloque en la memoria, conocido como código de posición independiente (o PIC para abreviar).

Ahora, cuando se trata de resolver su problema, el mensaje de error habla por sí mismo.