Re: [PATCH v2 2/7] BaseTools-GenFw:Add new x86_64 Elf relocation types for PIC/PIE code


Ard Biesheuvel
 

On 1 August 2016 at 08:13, Shi, Steven <steven.shi@intel.com> wrote:

I am also concerned about the GOTPCRELX/REX_GOTPCRELX relocations.
Reading the x86_64 ABI docs, it appears that these may refer to
instructions that have been modified by the linker. In that case, how
do we deal with the relocation? Also, according to the doc, mov
instructions may be emitted by the linker in some cases that are only
valid in the lowest 2 GB of the address space.
[Steven]: Frankly to say, the x86_64 ABI docs is only good for compiler
domain developer and not very good for other domain developers to
understand it.
My overall understanding for these different relocation type is like this:
compiler generate PIC code with different "level of indirection to all global
data and function references in the code." And these different level of
indirection is implemented through GOT and PLT structure with different
addressing calculation pattern. The different calculation patterns are the
different relocation types which are defined by x86_64 ABI Table 4.9. We
don't need worry about how compiler correctly generate code to work with
these relocation types, we just need correctly understand their addressing
calculation pattern.

The GOTPCRELX/REX_GOTPCRELX has the same calculation definition in
x86_64 ABI Table 4.9 as "G + GOT + A - P". So, I assume their difference is not
in the relocation calculation pattern, but how to co-work with specific
instructions to finish these calculation in a hardware optimized way.
No, that is not what these are for. The special types mark
instructions that can be converted by the linker into simpler
sequences if the symbol turns out to be in the same module. From the
doc:

mov foo@GOTPCREL(%rip), %reg

could be converted by the linker into

lea foo(%rip), %reg

if the reference to 'foo' is satisfied by a non-preemptible local
definition. This is a useful optimization, since it eliminates a
memory load. The problem is that we cannot recalculate such
relocations in GenFw without checking whether the linker has applied
this optimization or not.
[Steven]: Do you mean the linker will apply above optimization but not remove the original GOTPCREL item? It sounds like a severe linker bug.
I checked quickly, and it appears the linker does the right thing
here, i.e., it performs the optimization and also modifies the
relocation emitted into the .rela.text section

So this:

.globl bar
.type bar, @function
bar:
mov foo@GOTPCREL(%rip), %eax
ret

.globl foo
foo:
.quad 0

compiles into

/tmp/pie.o: file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <bar>:
0: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 6 <bar+0x6>
2: R_X86_64_GOTPCRELX foo-0x4
6: c3 retq

0000000000000007 <foo>:
...

but after linking (ld -o /tmp/pie -e bar -q /tmp/pie.o)

/tmp/pie: file format elf64-x86-64


Disassembly of section .text:

00000000004000b0 <bar>:
4000b0: 8d 05 01 00 00 00 lea 0x1(%rip),%eax # 4000b7 <foo>
4000b2: R_X86_64_PC32 foo-0x4
4000b6: c3 retq

00000000004000b7 <foo>:
...



The fact that it works does not make it safe. Having multiple fixups
for the same symbol in the .reloc section is a problem, and so is
reapplying GOTPCRELX to places where the original instruction has been
replaced by the linker.
[Steven]: I still don't understand why there will be multiple fixups for the same symbol in the .reloc section?
Remember this example

int n;
int f () { return n; }
int g () { return n; }
int h () { return n; }
If every 'return n' results in a GOTPCREL relocation, how are you
going to make sure that the GOT entry for 'n' is only fixed up a
single time?

Join devel@edk2.groups.io to automatically receive all group messages.