llvm-project/lld/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp
Adhemerval Zanella b19f5cfee6 [ELF/x86_64] Fix initial-exec TLS access
Current approach for initial-exec in ELF/x86_64 is to create a GOT entry
and change the relocation to R_X86_64_PC32 to be handled as a GOT offfset.
However there are two issues with this approach: 1. the R_X86_64_PC32 is
not really required since the GOT relocation will be handle dynamically and
2. the TLS symbols are not being exported externally and then correct
realocation are not being applied.

This patch fixes the R_X86_64_GOTTPOFF handling by just emitting a
R_X86_64_TPOFF64 dynamically one; it also sets R_X86_64_TPOFF64 to be
handled by runtime one.  For second part, the patches uses a similar
strategy used for aarch64, by reimplementing buildDynamicSymbolTable
from X86_64ExecutableWriter and adding the TLS symbols in the dynamic
symbol table.

Some tests had to be adjusted due the now missing R_X86_64_PC32 relocation.
With this test the simple testcase:

* t1.c:

__thread int t0;
__thread int t1;
__thread int t2;
__thread int t3;

* t0.c:

extern __thread int t0;
extern __thread int t1;
extern __thread int t2;
extern __thread int t3;

__thread int t4;
__thread int t5;
__thread int t6;
__thread int t7;

int main ()
{
  t0 = 1;
  t1 = 2;
  t2 = 3;
  t3 = 4;

  t4 = 5;
  t5 = 6;
  t6 = 7;
  t7 = 8;

  printf ("%i %i %i %i\n", t0, t1, t2, t3);
  printf ("%i %i %i %i\n", t4, t5, t6, t7);

  return 0;
}

Shows correct output for x86_64.

llvm-svn: 239908
2015-06-17 14:00:12 +00:00

148 lines
4.7 KiB
C++

//===- lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp ------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "X86_64LinkingContext.h"
#include "X86_64TargetHandler.h"
#include "llvm/Support/Endian.h"
using namespace lld;
using namespace lld::elf;
using namespace llvm::support::endian;
/// \brief R_X86_64_64 - word64: S + A
static void reloc64(uint8_t *location, uint64_t P, uint64_t S, int64_t A) {
uint64_t result = S + A;
write64le(location, result | read64le(location));
}
/// \brief R_X86_64_PC32 - word32: S + A - P
static void relocPC32(uint8_t *location, uint64_t P, uint64_t S, int64_t A) {
uint32_t result = (uint32_t)(S + A - P);
write32le(location, result + read32le(location));
}
/// \brief R_X86_64_32 - word32: S + A
static void reloc32(uint8_t *location, uint64_t P, uint64_t S, int64_t A) {
int32_t result = (uint32_t)(S + A);
write32le(location, result | read32le(location));
// TODO: Make sure that the result zero extends to the 64bit value.
}
/// \brief R_X86_64_32S - word32: S + A
static void reloc32S(uint8_t *location, uint64_t P, uint64_t S, int64_t A) {
int32_t result = (int32_t)(S + A);
write32le(location, result | read32le(location));
// TODO: Make sure that the result sign extends to the 64bit value.
}
/// \brief R_X86_64_16 - word16: S + A
static void reloc16(uint8_t *location, uint64_t P, uint64_t S, int64_t A) {
uint16_t result = (uint16_t)(S + A);
write16le(location, result | read16le(location));
// TODO: Check for overflow.
}
/// \brief R_X86_64_PC16 - word16: S + A - P
static void relocPC16(uint8_t *location, uint64_t P, uint64_t S, int64_t A) {
uint16_t result = (uint16_t)(S + A - P);
write16le(location, result | read16le(location));
// TODO: Check for overflow.
}
/// \brief R_X86_64_PC64 - word64: S + A - P
static void relocPC64(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) {
int64_t result = (uint64_t)(S + A - P);
write64le(location, result | read64le(location));
}
std::error_code X86_64TargetRelocationHandler::applyRelocation(
ELFWriter &writer, llvm::FileOutputBuffer &buf, const AtomLayout &atom,
const Reference &ref) const {
uint8_t *atomContent = buf.getBufferStart() + atom._fileOffset;
uint8_t *loc = atomContent + ref.offsetInAtom();
uint64_t target = writer.addressOfAtom(ref.target());
uint64_t reloc = atom._virtualAddr + ref.offsetInAtom();
if (ref.kindNamespace() != Reference::KindNamespace::ELF)
return std::error_code();
assert(ref.kindArch() == Reference::KindArch::x86_64);
switch (ref.kindValue()) {
case R_X86_64_NONE:
break;
case R_X86_64_64:
reloc64(loc, reloc, target, ref.addend());
break;
case R_X86_64_PC32:
case R_X86_64_GOTPCREL:
relocPC32(loc, reloc, target, ref.addend());
break;
case R_X86_64_32:
reloc32(loc, reloc, target, ref.addend());
break;
case R_X86_64_32S:
reloc32S(loc, reloc, target, ref.addend());
break;
case R_X86_64_16:
reloc16(loc, reloc, target, ref.addend());
break;
case R_X86_64_PC16:
relocPC16(loc, reloc, target, ref.addend());
break;
case R_X86_64_DTPOFF32:
case R_X86_64_TPOFF32:
_tlsSize = _layout.getTLSSize();
write32le(loc, target - _tlsSize);
break;
case R_X86_64_GOTTPOFF:
relocPC32(loc, reloc, target, ref.addend());
break;
case R_X86_64_TLSGD: {
relocPC32(loc, reloc, target, ref.addend());
break;
}
case R_X86_64_TLSLD: {
// Rewrite to move %fs:0 into %rax. Technically we should verify that the
// next relocation is a PC32 to __tls_get_addr...
static uint8_t instr[] = { 0x66, 0x66, 0x66, 0x64, 0x48, 0x8b, 0x04, 0x25,
0x00, 0x00, 0x00, 0x00 };
std::memcpy(loc - 3, instr, sizeof(instr));
break;
}
case R_X86_64_PC64:
relocPC64(loc, reloc, target, ref.addend());
break;
case LLD_R_X86_64_GOTRELINDEX: {
const DefinedAtom *target = cast<const DefinedAtom>(ref.target());
for (const Reference *r : *target) {
if (r->kindValue() == R_X86_64_JUMP_SLOT) {
uint32_t index;
if (!_layout.getPLTRelocationTable()->getRelocationIndex(*r, index))
llvm_unreachable("Relocation doesn't exist");
reloc32(loc, 0, index, 0);
break;
}
}
break;
}
// Runtime only relocations. Ignore here.
case R_X86_64_RELATIVE:
case R_X86_64_IRELATIVE:
case R_X86_64_JUMP_SLOT:
case R_X86_64_GLOB_DAT:
case R_X86_64_DTPMOD64:
case R_X86_64_DTPOFF64:
case R_X86_64_TPOFF64:
break;
default:
return make_unhandled_reloc_error();
}
return std::error_code();
}