What do R_X86_64_32S and R_X86_64_64 relocation mean?
Asked Answered
P

6

63

Got the following error when I tried to compile a C application in 64-bit FreeBSD:

relocation R_X86_64_32S can not be used when making a shared object; recompile with -fPIC

What is R_X86_64_32S relocation and what is R_X86_64_64?

I've googled about the error, and it's possible causes - It'd be great if anyone could tell what R_X86_64_32S really means.

Pyle answered 23/5, 2011 at 6:23 Comment(3)
Related: Linux distros have recently started to enable position-independent executables by default for gcc. You will get this error if trying to link any non-PIC code into an executable, including asm that uses something like mov $symbol, %edi instead of lea symbol(%rip), %rdi. See 32-bit absolute addresses no longer allowed in x86-64 Linux? for info on using gcc -no-pie -fno-pie to make traditional position-dependent executables if that's what you want.Receptor
@PeterCordes I think your comment would deserve an answer on its ownTardy
@ManuelSelva: The link in my comment is to my answer on the subject. It's not exactly an answer to this question (about what relocations are). The existing answers here are good.Receptor
R
42

The R_X86_64_32S and R_X86_64_64 are names of relocation types, for code compiled for the amd64 architecture. You can look all of them up in the amd64 ABI. According to it, R_X86_64_64 is broken down to:

  • R_X86_64 - all names are prefixed with this
  • 64 - Direct 64 bit relocation

and R_X86_64_32S to:

  • R_X86_64 - prefix
  • 32S - truncate value to 32 bits and sign-extend

which basically means "the value of the symbol pointed to by this relocation, plus any addend", in both cases. For R_X86_64_32S the linker then verifies that the generated value sign-extends to the original 64-bit value.

Now, in an executable file, the code and data segments are given a specified virtual base address. The executable code is not shared, and each executable gets its own fresh address space. This means that the compiler knows exactly where the data section will be, and can reference it directly. Libraries, on the other hand, can only know that their data section will be at a specified offset from the base address; the value of that base address can only be known at runtime. Hence, all libraries must be produced with code that can execute no matter where it is put into memory, known as position independent code (or PIC for short).

Now when it comes to resolving your problem, the error message speaks for itself.

Repulsive answered 23/5, 2011 at 7:3 Comment(0)
G
35

For any of this to make sense, you must first:

Standards

R_X86_64_64, R_X86_64_32 and R_X86_64_32S are all defined by the System V AMD ABI, which contains the AMD64 specifics of the ELF file format.

They are all possible values for the ELF32_R_TYPE field of a relocation entry, specified in the System V ABI 4.1 (1997) which specifies the architecture neutral parts of the ELF format. That standard only specifies the field, but not it's arch dependant values.

Under 4.4.1 "Relocation Types" we see the summary table:

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

We will explain this table later.

And the note:

The R_X86_64_32 and R_X86_64_32S relocations truncate the computed value to 32-bits. The linker must verify that the generated value for the R_X86_64_32 (R_X86_64_32S) relocation zero-extends (sign-extends) to the original 64-bit value.

Example of R_X86_64_64 and R_X86_64_32

Let's first look into R_X86_64_64 and R_X86_64_32:

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

Then:

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

Contains:

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)

Tested on Ubuntu 14.04, Binutils 2.24.

Ignore the disassembly for now (which is meaningless since this is data), and look only to the labels, bytes and relocations.

The first relocation:

0: R_X86_64_32  .text+0xc

Which means:

  • 0: acts on byte 0 (label a)
  • R_X86_64_: prefix used by all relocation types of the AMD64 system V ABI
  • 32: the 64-bit address of the label s is truncated to a 32 bit address because we only specified a .long (4 bytes)
  • .text: we are on the .text section
  • 0xc: this is the addend, which is a field of the relocation entry

The address of the relocation is calculated as:

A + S

Where:

  • A: the addend, here 0xC
  • S: the value of the symbol before relocation, here 00 00 00 00 == 0

Therefore, after relocation, the new address will be 0xC == 12 bytes into the .text section.

This is exactly what we expect, since s comes after a .long (4 bytes) and a .quad (8 bytes).

R_X86_64_64 is analogous, but simpler, since here there is no need to truncate the address of s. This is indicated by the standard through word64 instead of word32 on the Field column.

R_X86_64_32S vs R_X86_64_32

The difference between R_X86_64_32S vs R_X86_64_32 is when the linker will complain "with relocation truncated to fit":

  • 32: complains if the truncated after relocation value does not zero extend the old value, i.e. the truncated bytes must be zero:

    E.g.: FF FF FF FF 80 00 00 00 to 80 00 00 00 generates a complaint because FF FF FF FF is not zero.

  • 32S: complains if the truncated after relocation value does not sign extend the old value.

    E.g.: FF FF FF FF 80 00 00 00 to 80 00 00 00 is fine, because the last bit of 80 00 00 00 and the truncated bits are all 1.

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

R_X86_64_32S can be generated with:

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

Then:

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

Gives:

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

Now we can observe the "relocation" truncated to fit on 32S with a linker script:

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

Now:

ld -Tlink.ld a.o

Is fine, because: 0xFFFFFFFF80000000 gets truncated into 80000000, which is a sign extension.

But if we change the linker script to:

. = 0xFFFF0FFF80000000;

It now generates the error, because that 0 made it not be a sign extension anymore.

Rationale for using 32S for memory access but 32 for immediates: When is it better for an assembler to use sign extended relocation like R_X86_64_32S instead of zero extension like R_X86_64_32?

R_X86_64_32S and PIE (position independent executables

R_X86_64_32S cannot be used in position independent executables, e.g. done with gcc -pie, otherwise link fails with:

relocation R_X86_64_32S against `.text' can not be used when making a PIE object; recompile with -fPIC

l

I have provided a minimal example explaining it at: What is the -fPIE option for position-independent executables in gcc and ld?

Geometric answered 22/10, 2015 at 20:7 Comment(6)
mov s, %eax is a slightly confusing example for a signed 32-bit absolute relocation, because you'd normally never write that except by accident. It's easy to miss the fact that you left out the (%rip), and that it's a load not a mov-immediate ($s, which would be zero-extended absolute, or signed with mov $s, %rax). Better would be mov s(%rdi), %eax (or LEA), or sub $s, %rdi (or cmp $s, %rdi). Using a static address with register offsets in one addressing mode is the one of the major use-cases for 32-bit absolute addresses.Receptor
related: 32-bit absolute addresses no longer allowed in x86-64 Linux?: configuring gcc with -pie -fpie as the default is now common on Linux distros, so this will bite people making executables as well as intentionally making shared libs.Receptor
@Ciro, does -mcmodel=large instruct the compiler to use R_X86_64_64 by default?Perch
@DmitryMikushin sorry, I don't know, let me know if you find out.Geometric
@CiroSantilliПутлерКапут六四事 I think it does. The funny thing though is that GCC crt startup objects remain R_X86_64_32. One needs to re-create them manually with the -mcmode=large flag, as shown here: sourceware.org/bugzilla/show_bug.cgi?id=26386Perch
@DmitryMikushin OK, I'm not surprised unfortunately. crt objects are evil.Geometric
U
5

That means that compiled a shared object without using -fPIC flag as you should:

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

You need to call

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

Under ELF platform (Linux) shared objects are compiled with position independent code - code that can run from any location in memory, if this flag is not given, the code that is generated is position dependent, so it is not possible to use this shared object.

Unaesthetic answered 23/5, 2011 at 6:28 Comment(3)
I have quite the same problem, but I still get the error even with the -fPIC added: gcc -std=c99 -Wall -pedantic -shared -fopenmp -fPIC -static test.c -o libtest.so. Any ideas why? Thanks!Pliny
this example clearly shows how to use the gcc -fPIC flag to fix "x86-64-32s" and "r-x86-64-64" related errors.Wentzel
@CiprianTomoiagă late to the party but in my case ... there was a static library buried in my dependency chain that was not compiled with -fPIC. So I got this error, even though the outermost library was compiled with -fPIC in the instructions. No miracles I guess. Fix was to go back down the chain and find the static lib that was compiled without -fPIC and change it.Sway
K
3

I ran into this problem and found this answer didn't help me. I was trying to link a static library together with a shared library. I also investigated putting the -fPIC switch earlier on the command line (as advised in answers elsewhere). The only thing that fixed the problem, for me, was changing the static library to shared. I suspect the error message about -fPIC can happen due to a number of causes but fundamentally what you want to look at is how your libraries are being built, and be suspicious of libraries that are being built in different ways.

Kolo answered 4/8, 2015 at 20:53 Comment(1)
It makes sense that a static library would have been built without -fPIC, and thus would use some absolute relocations. Thus you can't link that code into a relocatable shared object.Receptor
Y
2

In my case the issue arose because the program to compile expected to find shared libraries in a remote directory, while only the corresponding static libraries were there in a mistake.

Actually, this relocation error was a file-not-found error in disguise.

I have detailed how I coped with it in this other thread https://mcmap.net/q/14713/-compilation-fails-with-quot-relocation-r_x86_64_32-against-rodata-str1-8-39-can-not-be-used-when-making-a-shared-object-quot

Yukoyukon answered 22/2, 2017 at 10:0 Comment(0)
S
2

The above answer demonstrates what these relocations are, and I found building x86_64 objects with GCC -mcmodel=large flag can prevent R_X86_64_32S because the compiler has no assumption on the relocated address in this model.

In the following case:

extern int myarr[];

int test(int i)
{
  return myarr[i];
}

Built with gcc -O2 -fno-pie -c test_array.c and disassemble with objdump -drz test_array.o, we have:

 0: 48 63 ff                movslq %edi,%rdi
 3: 8b 04 bd 00 00 00 00    mov    0x0(,%rdi,4),%eax
        6: R_X86_64_32S myarr
 a: c3                      ret    

With -mcmodel=large, i.e. gcc -mcmodel=large -O2 -fno-pie -c test_array.c, we have:

 0: 48 b8 00 00 00 00 00    movabs $0x0,%rax
 7: 00 00 00 
        2: R_X86_64_64  myarr
 a: 48 63 ff                movslq %edi,%rdi
 d: 8b 04 b8                mov    (%rax,%rdi,4),%eax
10: c3                      ret    
Stretchy answered 16/6, 2021 at 3:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.