What does this GCC error "... relocation truncated to fit..." mean?
You are attempting to link your project in such a way that the target of a relative addressing scheme is further away than can be supported with the 32-bit displacement of the chosen relative addressing mode. This could be because the current project is larger, because it is linking object files in a different order, or because there's an unnecessarily expansive mapping scheme in play.
This question is a perfect example of why it's often productive to do a web search on the generic portion of an error message - you find things like this:
http://www.technovelty.org/code/c/relocation-truncated.html
Which offers some curative suggestions.
Minimal example that generates the error
main.S
moves an address into %eax
(32-bit).
main.S
_start:
mov $_start, %eax
linker.ld
SECTIONS
{
/* This says where `.text` will go in the executable. */
. = 0x100000000;
.text :
{
*(*)
}
}
Compile on x86-64:
as -o main.o main.S
ld -o main.out -T linker.ld main.o
Outcome of ld
:
(.text+0x1): relocation truncated to fit: R_X86_64_32 against `.text'
Keep in mind that:
-
as
puts everything on the.text
if no other section is specified -
ld
uses the.text
as the default entry point ifENTRY
. Thus_start
is the very first byte of.text
.
How to fix it: use this linker.ld
instead, and subtract 1 from the start:
SECTIONS
{
. = 0xFFFFFFFF;
.text :
{
*(*)
}
}
Notes:
we cannot make
_start
global in this example with.global _start
, otherwise it still fails. I think this happens because global symbols have alignment constraints (0xFFFFFFF0
works). TODO where is that documented in the ELF standard?the
.text
segment also has an alignment constraint ofp_align == 2M
. But our linker is smart enough to place the segment at0xFFE00000
, fill with zeros until0xFFFFFFFF
and sete_entry == 0xFFFFFFFF
. This works, but generates an oversized executable.
Tested on Ubuntu 14.04 AMD64, Binutils 2.24.
Explanation
First you must understand what relocation is with a minimal example: https://stackoverflow.com/a/30507725/895245
Next, take a look at objdump -Sr main.o
:
0000000000000000 <_start>:
0: b8 00 00 00 00 mov $0x0,%eax
1: R_X86_64_32 .text
If we look into how instructions are encoded in the Intel manual, we see that:
-
b8
says that this is amov
to%eax
-
0
is an immediate value to be moved to%eax
. Relocation will then modify it to contain the address of_start
.
When moving to 32-bit registers, the immediate must also be 32-bit.
But here, the relocation has to modify those 32-bit to put the address of _start
into them after linking happens.
0x100000000
does not fit into 32-bit, but 0xFFFFFFFF
does. Thus the error.
This error can only happen on relocations that generate truncation, e.g. R_X86_64_32
(8 bytes to 4 bytes), but never on R_X86_64_64
.
And there are some types of relocation that require sign extension instead of zero extension as shown here, e.g. R_X86_64_32S
. See also: https://stackoverflow.com/a/33289761/895245
R_AARCH64_PREL32
Asked at: How to prevent "main.o:(.eh_frame+0x1c): relocation truncated to fit: R_AARCH64_PREL32 against `.text'" when creating an aarch64 baremetal program?
On Cygwin -mcmodel=medium
is already default and doesn't help. To me adding -Wl,--image-base -Wl,0x10000000
to GCC linker did fixed the error.