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?
mov $symbol, %edi
instead oflea symbol(%rip), %rdi
. See 32-bit absolute addresses no longer allowed in x86-64 Linux? for info on usinggcc -no-pie -fno-pie
to make traditional position-dependent executables if that's what you want. – Receptor