plugin - mcu eclipse
¿Qué significa este error de GCC "... relocation truncated to fit..."? (6)
A menudo, lo que significa este error es que su programa es demasiado grande y, a menudo, es demasiado grande porque contiene uno o más objetos de datos muy grandes. Por ejemplo,
char large_array[1ul << 31];
int other_global;
int main(void) { return other_global; }
producirá un error de "reubicación truncada para ajustarse" en x86-64 / Linux, si se compila en el modo predeterminado y sin optimización. (Si large_array
optimización, podría, al menos en teoría, descubrir que large_array
no se utiliza y / o que other_global
nunca se escribe, y así generar código que no desencadene el problema).
Lo que sucede es que, de forma predeterminada, GCC usa su "modelo de código pequeño" en esta arquitectura, en la que todo el código del programa y los datos asignados estáticamente deben caber en los 2 GB más bajos del espacio de direcciones. (El límite superior exacto es algo así como 2GB - 2MB, porque los 2MB más bajos del espacio de direcciones de cualquier programa permanecen inutilizables. Si está compilando una biblioteca compartida o un ejecutable independiente de la posición, todo el código y los datos aún deben caber en dos gigabytes, pero ya no están clavados en el fondo del espacio de direcciones). large_array
consume todo ese espacio por sí mismo, por lo que a other_global
se le asigna una dirección por encima del límite, y el código generado para main
no puede alcanzarlo. Se obtiene un error críptico del enlazador, en lugar de un error útil " large_array
es demasiado grande" del compilador, porque en casos más complejos el compilador no puede saber que other_global
estará fuera de su alcance, por lo que ni siquiera intenta para los casos simples.
La mayoría de las veces, la respuesta correcta para obtener este error es refactorizar su programa para que no necesite arreglos estáticos gigantescos y / o gigabytes de código máquina. Sin embargo, si realmente los necesita por alguna razón, puede usar los modelos de código "mediano" o "grande" para elevar los límites, al precio de una generación de código algo menos eficiente. Estos modelos de código son específicos de x86-64; algo similar existe para la mayoría de las otras arquitecturas, pero el conjunto exacto de "modelos" y los límites asociados variarán. (En una arquitectura de 32 bits, por ejemplo, es posible que tenga un modelo "pequeño" en el que la cantidad total de código y datos se limitó a algo así como 2 24 bytes).
Estoy programando el lado del host de un sistema acelerador de host. El host se ejecuta en la PC bajo Ubuntu Linux y se comunica con el hardware integrado a través de una conexión USB. La comunicación se realiza copiando fragmentos de memoria hacia y desde la memoria del hardware incrustado.
En la memoria de la placa hay una región de memoria que utilizo como un buzón donde escribo y leo los datos. El buzón se define como una estructura y yo uso la misma definición para asignar un buzón espejo en mi espacio de host.
Utilicé esta técnica con éxito en el pasado, así que ahora copié el proyecto Eclipse del host en el espacio de trabajo de mi proyecto actual e hice los cambios de nombre apropiados. Lo extraño es que al construir el proyecto de host ahora recibo el siguiente mensaje:
Objetivo de compilación: fft2d_host
Invocando: GCC C Linker
gcc -L / opt / adapteva / esdk / tools / host / x86_64 / lib -o "fft2d_host" ./src/fft2d_host.o -le_host -lrt./src/fft2d_host.o: en la función `main '':
fft2d_host.c :(. text + 0x280): reubicación truncada para ajustarse: R_X86_64_PC32 contra el símbolo `Buzón ''definido en la sección COMÚN en ./src/fft2d_host.o
¿Qué significa este error y por qué no se basará en el proyecto actual, mientras que está bien con el proyecto anterior?
En Cygwin -mcmodel=medium
ya está predeterminado y no ayuda. Para mí, agregar -Wl,--image-base -Wl,0x10000000
al engarce de GCC solucionó el error.
Está intentando vincular su proyecto de tal manera que el objetivo de un esquema de direccionamiento relativo está más lejos de lo que puede admitirse con el desplazamiento de 32 bits del modo de direccionamiento relativo elegido. Esto podría deberse a que el proyecto actual es más grande porque está vinculando archivos de objetos en un orden diferente, o porque hay un esquema de mapeo expansivo innecesario en juego.
Esta pregunta es un ejemplo perfecto de por qué a menudo es productivo hacer una búsqueda en la web en la parte genérica de un mensaje de error: encuentras cosas como esta:
http://www.technovelty.org/code/c/relocation-truncated.html
Que ofrece algunas sugerencias curativas.
Me encontré con este problema al construir un programa que requiere una gran cantidad de espacio de pila (más de 2 GiB). La solución fue agregar el indicador -mcmodel=medium
, que es compatible con los compiladores de GCC e Intel.
Recuerde abordar los mensajes de error en orden. En mi caso, el error por encima de este fue "referencia indefinida", y visualmente omití el error "truncamiento de reubicación" más interesante. De hecho, mi problema era una biblioteca antigua que causaba el mensaje de "referencia no definida". Una vez que lo arreglé, la "reubicación truncada" se fue también.
Ejemplo mínimo que genera el error
main.S
: mueve una dirección a %eax
(32 bits):
_start:
mov $_start, %eax
linker.ld
:
SECTIONS
{
/* This says where `.text` will go in the executable. */
. = 0x100000000;
.text :
{
*(*)
}
}
Compilar en x86-64:
as -o main.o main.S
ld -o main.out -T linker.ld main.o
Resultado de ld
:
(.text+0x1): relocation truncated to fit: R_X86_64_32 against `.text''
Manten eso en mente:
-
as
pone todo en.text
si no se especifica ninguna otra sección -
ld
usa.text
como el punto de entrada predeterminado siENTRY
. Por lo tanto_start
es el primer byte de.text
.
Cómo solucionarlo: use este linker.ld
en linker.ld
lugar, y reste 1 desde el inicio:
SECTIONS
{
. = 0xFFFFFFFF;
.text :
{
*(*)
}
}
Notas:
no podemos hacer
_start
global en este ejemplo con.global _start
, de lo contrario, sigue fallando. Creo que esto sucede porque los símbolos globales tienen restricciones de alineación (0xFFFFFFF0
funciona). ¿TODO dónde está eso documentado en el estándar ELF?el segmento
.text
también tiene una restricción de alineación dep_align == 2M
. Pero nuestro enlazador es lo suficientemente inteligente como para colocar el segmento en0xFFE00000
, rellenarlo con ceros hasta0xFFFFFFFF
y establecere_entry == 0xFFFFFFFF
. Esto funciona, pero genera un ejecutable de gran tamaño.
Probado en Ubuntu 14.04 AMD64, Binutils 2.24.
Explicación
Primero debe comprender qué es la reubicación con un ejemplo mínimo: https://.com/a/30507725/895245
A continuación, eche un vistazo a objdump -Sr main.o
:
0000000000000000 <_start>:
0: b8 00 00 00 00 mov $0x0,%eax
1: R_X86_64_32 .text
Si miramos cómo están codificadas las instrucciones en el manual de Intel, vemos que:
-
b8
dice que esto es unmov
a%eax
-
0
es un valor inmediato que se moverá a%eax
. La reubicación lo modificará para que contenga la dirección de_start
.
Cuando se mueve a registros de 32 bits, el inmediato también debe ser de 32 bits.
Pero aquí, la reubicación tiene que modificar esos 32 bits para poner la dirección de _start
en ellos después de que se _start
vinculación.
0x100000000
no encaja en 32 bits, pero 0xFFFFFFFF
sí. Por lo tanto, el error.
Este error solo puede ocurrir en reubicaciones que generan truncamiento, por ejemplo, R_X86_64_32
(8 bytes a 4 bytes), pero nunca en R_X86_64_64
.
Y hay algunos tipos de reubicación que requieren extensión de signo en lugar de extensión cero, como se muestra aquí, por ejemplo, R_X86_64_32S
. Ver también: https://.com/a/33289761/895245