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?
This question is related to
c
eclipse
memory-management
gcc
embedded
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 specifiedld
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
Often, this error means your program is too large, and often it's too large because it contains one or more very large data objects. For example,
char large_array[1ul << 31];
int other_global;
int main(void) { return other_global; }
will produce a "relocation truncated to fit" error on x86-64/Linux, if compiled in the default mode and without optimization. (If you turn on optimization, it could, at least theoretically, figure out that large_array
is unused and/or that other_global
is never written, and thus generate code that doesn't trigger the problem.)
What's going on is that, by default, GCC uses its "small code model" on this architecture, in which all of the program's code and statically allocated data must fit into the lowest 2GB of the address space. (The precise upper limit is something like 2GB - 2MB, because the very lowest 2MB of any program's address space is permanently unusable. If you are compiling a shared library or position-independent executable, all of the code and data must still fit into two gigabytes, but they're not nailed to the bottom of the address space anymore.) large_array
consumes all of that space by itself, so other_global
is assigned an address above the limit, and the code generated for main
cannot reach it. You get a cryptic error from the linker, rather than a helpful "large_array
is too large" error from the compiler, because in more complex cases the compiler can't know that other_global
will be out of reach, so it doesn't even try for the simple cases.
Most of the time, the correct response to getting this error is to refactor your program so that it doesn't need gigantic static arrays and/or gigabytes of machine code. However, if you really have to have them for some reason, you can use the "medium" or "large" code models to lift the limits, at the price of somewhat less efficient code generation. These code models are x86-64-specific; something similar exists for most other architectures, but the exact set of "models" and the associated limits will vary. (On a 32-bit architecture, for instance, you might have a "small" model in which the total amount of code and data was limited to something like 224 bytes.)
I ran into the exact same issue. After compiling without the -fexceptions
build flag, the file compiled with no issue
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.
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.
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.
Source: Stackoverflow.com