summaryrefslogtreecommitdiff
path: root/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp')
-rw-r--r--lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp151
1 files changed, 151 insertions, 0 deletions
diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp
new file mode 100644
index 0000000000000..8fd74f43bbd28
--- /dev/null
+++ b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp
@@ -0,0 +1,151 @@
+//===- 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 lld::AtomLayout &atom,
+ const Reference &ref) const {
+ uint8_t *atomContent = buf.getBufferStart() + atom._fileOffset;
+ uint8_t *location = atomContent + ref.offsetInAtom();
+ uint64_t targetVAddress = writer.addressOfAtom(ref.target());
+ uint64_t relocVAddress = 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(location, relocVAddress, targetVAddress, ref.addend());
+ break;
+ case R_X86_64_PC32:
+ case R_X86_64_GOTPCREL:
+ relocPC32(location, relocVAddress, targetVAddress, ref.addend());
+ break;
+ case R_X86_64_32:
+ reloc32(location, relocVAddress, targetVAddress, ref.addend());
+ break;
+ case R_X86_64_32S:
+ reloc32S(location, relocVAddress, targetVAddress, ref.addend());
+ break;
+ case R_X86_64_16:
+ reloc16(location, relocVAddress, targetVAddress, ref.addend());
+ break;
+ case R_X86_64_PC16:
+ relocPC16(location, relocVAddress, targetVAddress, ref.addend());
+ break;
+ case R_X86_64_TPOFF64:
+ case R_X86_64_DTPOFF32:
+ case R_X86_64_TPOFF32: {
+ _tlsSize = _x86_64Layout.getTLSSize();
+ if (ref.kindValue() == R_X86_64_TPOFF32 ||
+ ref.kindValue() == R_X86_64_DTPOFF32) {
+ write32le(location, targetVAddress - _tlsSize);
+ } else {
+ write64le(location, targetVAddress - _tlsSize);
+ }
+ break;
+ }
+ case R_X86_64_TLSGD: {
+ relocPC32(location, relocVAddress, targetVAddress, 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(location - 3, instr, sizeof(instr));
+ break;
+ }
+ case R_X86_64_PC64:
+ relocPC64(location, relocVAddress, targetVAddress, 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 (!_x86_64Layout.getPLTRelocationTable()->getRelocationIndex(*r,
+ index))
+ llvm_unreachable("Relocation doesn't exist");
+ reloc32(location, 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:
+ break;
+ default:
+ return make_unhandled_reloc_error();
+ }
+
+ return std::error_code();
+}