What does this GCC error "... relocation truncated to fit..." mean?

145,659

Solution 1

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.

Solution 2

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 if ENTRY. 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 of p_align == 2M. But our linker is smart enough to place the segment at 0xFFE00000, fill with zeros until 0xFFFFFFFF and set e_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 a mov 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?

Solution 3

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.

Solution 4

Remember to tackle error messages in order. In my case, the error above this one was "undefined reference", and I visually skipped over it to the more interesting "relocation truncated" error. In fact, my problem was an old library that was causing the "undefined reference" message. Once I fixed that, the "relocation truncated" went away also.

Solution 5

I ran into this problem while building a program that requires a huge amount of stack space (over 2 GiB). The solution was to add the flag -mcmodel=medium, which is supported by both GCC and Intel compilers.

Share:
145,659
ysap
Author by

ysap

I've been a computers/computing enthusiast since many years ago. Started coding FORTRAN on punched cards, then moved to BASIC on my MC6809 based DRAGON-64 and then the x86 IBM-PC era. I had the opportunity of working on mainframes, minis, workstations, PC's and embedded hardware. Today I am doing mainly embedded coding - C and ASM on various processors, and on various programming environments and toolchains like MS Visual Studio, Eclipse CDT, ARM DS and more. Was lucky enough to be at the right time at the right place to get to work as a VLSI designer for a top tier chip company, working on a world class processor family. Always looking to solving problem in the most elegant way! - Yaniv Sapir

Updated on November 04, 2020

Comments

  • ysap
    ysap over 3 years

    I am programming the host side of a host-accelerator system. The host runs on the PC under Ubuntu Linux and communicates with the embedded hardware via a USB connection. The communication is performed by copying memory chunks to and from the embedded hardware's memory.

    On the board's memory there is a memory region which I use as a mailbox where I write and read the data. The mailbox is defined as a structure and I use the same definition to allocate a mirror mailbox in my host space.

    I used this technique successfully in the past so now I copied the host Eclipse project to my current project's workspace, and made the appropriate name changes. The strange thing is that when building the host project I now get the following message:

    Building target: fft2d_host
    Invoking: 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: In function `main':

    fft2d_host.c:(.text+0x280): relocation truncated to fit: R_X86_64_PC32 against symbol `Mailbox' defined in COMMON section in ./src/fft2d_host.o

    What does this error mean and why it won't build on the current project, while it is OK with the older project?