summaryrefslogtreecommitdiff
path: root/lib/ReaderWriter/ELF/ARM
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2015-03-24 21:31:36 +0000
committerDimitry Andric <dim@FreeBSD.org>2015-03-24 21:31:36 +0000
commitfb911942f1434f3d1750f83f25f5e42c80e60638 (patch)
tree1678c4a4f0182e4029a86d135aa4a1b7d09e3c41 /lib/ReaderWriter/ELF/ARM
Notes
Diffstat (limited to 'lib/ReaderWriter/ELF/ARM')
-rw-r--r--lib/ReaderWriter/ELF/ARM/ARMELFFile.h97
-rw-r--r--lib/ReaderWriter/ELF/ARM/ARMELFReader.h62
-rw-r--r--lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h121
-rw-r--r--lib/ReaderWriter/ELF/ARM/ARMLinkingContext.cpp34
-rw-r--r--lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h36
-rw-r--r--lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp500
-rw-r--r--lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.h38
-rw-r--r--lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp373
-rw-r--r--lib/ReaderWriter/ELF/ARM/ARMRelocationPass.h31
-rw-r--r--lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h46
-rw-r--r--lib/ReaderWriter/ELF/ARM/ARMTargetHandler.cpp44
-rw-r--r--lib/ReaderWriter/ELF/ARM/ARMTargetHandler.h88
-rw-r--r--lib/ReaderWriter/ELF/ARM/CMakeLists.txt12
-rw-r--r--lib/ReaderWriter/ELF/ARM/Makefile15
-rw-r--r--lib/ReaderWriter/ELF/ARM/TODO.rst20
15 files changed, 1517 insertions, 0 deletions
diff --git a/lib/ReaderWriter/ELF/ARM/ARMELFFile.h b/lib/ReaderWriter/ELF/ARM/ARMELFFile.h
new file mode 100644
index 000000000000..bc5ee35b8213
--- /dev/null
+++ b/lib/ReaderWriter/ELF/ARM/ARMELFFile.h
@@ -0,0 +1,97 @@
+//===--------- lib/ReaderWriter/ELF/ARM/ARMELFFile.h ----------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_ELF_ARM_ARM_ELF_FILE_H
+#define LLD_READER_WRITER_ELF_ARM_ARM_ELF_FILE_H
+
+#include "ELFReader.h"
+
+namespace lld {
+namespace elf {
+
+class ARMLinkingContext;
+
+template <class ELFT> class ARMELFDefinedAtom : public ELFDefinedAtom<ELFT> {
+ typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym;
+ typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr;
+
+public:
+ ARMELFDefinedAtom(const ELFFile<ELFT> &file, StringRef symbolName,
+ StringRef sectionName, const Elf_Sym *symbol,
+ const Elf_Shdr *section, ArrayRef<uint8_t> contentData,
+ unsigned int referenceStart, unsigned int referenceEnd,
+ std::vector<ELFReference<ELFT> *> &referenceList)
+ : ELFDefinedAtom<ELFT>(file, symbolName, sectionName, symbol, section,
+ contentData, referenceStart, referenceEnd,
+ referenceList) {}
+
+ bool isThumbFunc(const Elf_Sym *symbol) const {
+ return symbol->getType() == llvm::ELF::STT_FUNC &&
+ (static_cast<uint64_t>(symbol->st_value) & 0x1);
+ }
+
+ /// Correct st_value for symbols addressing Thumb instructions
+ /// by removing its zero bit.
+ uint64_t getSymbolValue(const Elf_Sym *symbol) const override {
+ const auto value = static_cast<uint64_t>(symbol->st_value);
+ return isThumbFunc(symbol) ? value & ~0x1 : value;
+ }
+
+ DefinedAtom::CodeModel codeModel() const override {
+ if (isThumbFunc(this->_symbol))
+ return DefinedAtom::codeARMThumb;
+ return DefinedAtom::codeNA;
+ }
+};
+
+template <class ELFT> class ARMELFFile : public ELFFile<ELFT> {
+public:
+ ARMELFFile(std::unique_ptr<MemoryBuffer> mb, ARMLinkingContext &ctx)
+ : ELFFile<ELFT>(std::move(mb), ctx) {}
+
+ static ErrorOr<std::unique_ptr<ARMELFFile>>
+ create(std::unique_ptr<MemoryBuffer> mb, ARMLinkingContext &ctx) {
+ return std::unique_ptr<ARMELFFile<ELFT>>(
+ new ARMELFFile<ELFT>(std::move(mb), ctx));
+ }
+
+private:
+ typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym;
+ typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr;
+
+ /// Correct st_value for symbols addressing Thumb instructions
+ /// by removing its zero bit.
+ uint64_t getSymbolValue(const Elf_Sym *symbol) const override {
+ const auto value = static_cast<uint64_t>(symbol->st_value);
+ return symbol->getType() == llvm::ELF::STT_FUNC ? value & ~0x1 : value;
+ }
+
+ /// Process the Defined symbol and create an atom for it.
+ ErrorOr<ELFDefinedAtom<ELFT> *> handleDefinedSymbol(StringRef symName,
+ StringRef sectionName,
+ const Elf_Sym *sym, const Elf_Shdr *sectionHdr,
+ ArrayRef<uint8_t> contentData,
+ unsigned int referenceStart, unsigned int referenceEnd,
+ std::vector<ELFReference<ELFT> *> &referenceList) override {
+ return new (this->_readerStorage) ARMELFDefinedAtom<ELFT>(
+ *this, symName, sectionName, sym, sectionHdr, contentData,
+ referenceStart, referenceEnd, referenceList);
+ }
+};
+
+template <class ELFT> class ARMDynamicFile : public DynamicFile<ELFT> {
+public:
+ ARMDynamicFile(const ARMLinkingContext &context, StringRef name)
+ : DynamicFile<ELFT>(context, name) {}
+};
+
+} // elf
+} // lld
+
+#endif // LLD_READER_WRITER_ELF_ARM_ARM_ELF_FILE_H
diff --git a/lib/ReaderWriter/ELF/ARM/ARMELFReader.h b/lib/ReaderWriter/ELF/ARM/ARMELFReader.h
new file mode 100644
index 000000000000..31af531563ea
--- /dev/null
+++ b/lib/ReaderWriter/ELF/ARM/ARMELFReader.h
@@ -0,0 +1,62 @@
+//===--------- lib/ReaderWriter/ELF/ARM/ARMELFReader.h --------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_ARM_ARM_ELF_READER_H
+#define LLD_READER_WRITER_ARM_ARM_ELF_READER_H
+
+#include "ARMELFFile.h"
+#include "ELFReader.h"
+
+namespace lld {
+namespace elf {
+
+typedef llvm::object::ELFType<llvm::support::little, 2, false> ARMELFType;
+
+struct ARMDynamicFileCreateELFTraits {
+ typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type;
+
+ template <class ELFT>
+ static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb,
+ ARMLinkingContext &ctx) {
+ return lld::elf::ARMDynamicFile<ELFT>::create(std::move(mb), ctx);
+ }
+};
+
+struct ARMELFFileCreateELFTraits {
+ typedef llvm::ErrorOr<std::unique_ptr<lld::File>> result_type;
+
+ template <class ELFT>
+ static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb,
+ ARMLinkingContext &ctx) {
+ return lld::elf::ARMELFFile<ELFT>::create(std::move(mb), ctx);
+ }
+};
+
+class ARMELFObjectReader
+ : public ELFObjectReader<ARMELFType, ARMELFFileCreateELFTraits,
+ ARMLinkingContext> {
+public:
+ ARMELFObjectReader(ARMLinkingContext &ctx)
+ : ELFObjectReader<ARMELFType, ARMELFFileCreateELFTraits,
+ ARMLinkingContext>(ctx, llvm::ELF::EM_ARM) {}
+};
+
+class ARMELFDSOReader
+ : public ELFDSOReader<ARMELFType, ARMDynamicFileCreateELFTraits,
+ ARMLinkingContext> {
+public:
+ ARMELFDSOReader(ARMLinkingContext &ctx)
+ : ELFDSOReader<ARMELFType, ARMDynamicFileCreateELFTraits,
+ ARMLinkingContext>(ctx, llvm::ELF::EM_ARM) {}
+};
+
+} // namespace elf
+} // namespace lld
+
+#endif // LLD_READER_WRITER_ARM_ARM_ELF_READER_H
diff --git a/lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h b/lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h
new file mode 100644
index 000000000000..19311d516e4d
--- /dev/null
+++ b/lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h
@@ -0,0 +1,121 @@
+//===--------- lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h -------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLD_READER_WRITER_ELF_ARM_ARM_EXECUTABLE_WRITER_H
+#define LLD_READER_WRITER_ELF_ARM_ARM_EXECUTABLE_WRITER_H
+
+#include "ExecutableWriter.h"
+#include "ARMLinkingContext.h"
+#include "ARMTargetHandler.h"
+#include "ARMSymbolTable.h"
+
+namespace {
+const char *gotSymbol = "_GLOBAL_OFFSET_TABLE_";
+}
+
+namespace lld {
+namespace elf {
+
+template <class ELFT>
+class ARMExecutableWriter : public ExecutableWriter<ELFT> {
+public:
+ ARMExecutableWriter(ARMLinkingContext &context,
+ ARMTargetLayout<ELFT> &layout);
+
+protected:
+ // Add any runtime files and their atoms to the output
+ bool createImplicitFiles(std::vector<std::unique_ptr<File>> &) override;
+
+ void finalizeDefaultAtomValues() override;
+
+ void addDefaultAtoms() override {
+ ExecutableWriter<ELFT>::addDefaultAtoms();
+ }
+
+ /// \brief Create symbol table.
+ unique_bump_ptr<SymbolTable<ELFT>> createSymbolTable() override;
+
+ void processUndefinedSymbol(StringRef symName,
+ RuntimeFile<ELFT> &file) const override;
+
+ // Setup the ELF header.
+ std::error_code setELFHeader() override;
+
+private:
+ ARMLinkingContext &_context;
+ ARMTargetLayout<ELFT> &_armLayout;
+};
+
+template <class ELFT>
+ARMExecutableWriter<ELFT>::ARMExecutableWriter(ARMLinkingContext &context,
+ ARMTargetLayout<ELFT> &layout)
+ : ExecutableWriter<ELFT>(context, layout), _context(context),
+ _armLayout(layout) {}
+
+template <class ELFT>
+bool ARMExecutableWriter<ELFT>::createImplicitFiles(
+ std::vector<std::unique_ptr<File>> &result) {
+ ExecutableWriter<ELFT>::createImplicitFiles(result);
+ return true;
+}
+
+template <class ELFT>
+void ARMExecutableWriter<ELFT>::finalizeDefaultAtomValues() {
+ // Finalize the atom values that are part of the parent.
+ ExecutableWriter<ELFT>::finalizeDefaultAtomValues();
+ auto gotAtomIter = _armLayout.findAbsoluteAtom(gotSymbol);
+ if (gotAtomIter != _armLayout.absoluteAtoms().end()) {
+ auto *gotAtom = *gotAtomIter;
+ if (auto gotpltSection = _armLayout.findOutputSection(".got.plt"))
+ gotAtom->_virtualAddr = gotpltSection->virtualAddr();
+ else if (auto gotSection = _armLayout.findOutputSection(".got"))
+ gotAtom->_virtualAddr = gotSection->virtualAddr();
+ else
+ gotAtom->_virtualAddr = 0;
+ }
+ // TODO: resolve addresses of __exidx_start/_end atoms
+}
+
+template <class ELFT>
+unique_bump_ptr<SymbolTable<ELFT>>
+ ARMExecutableWriter<ELFT>::createSymbolTable() {
+ return unique_bump_ptr<SymbolTable<ELFT>>(
+ new (this->_alloc) ARMSymbolTable<ELFT>(this->_context));
+}
+
+template <class ELFT>
+void ARMExecutableWriter<ELFT>::processUndefinedSymbol(
+ StringRef symName, RuntimeFile<ELFT> &file) const {
+ if (symName == gotSymbol) {
+ file.addAbsoluteAtom(gotSymbol);
+ } else if (symName.startswith("__exidx")) {
+ file.addAbsoluteAtom("__exidx_start");
+ file.addAbsoluteAtom("__exidx_end");
+ }
+}
+
+template <class ELFT>
+std::error_code ARMExecutableWriter<ELFT>::setELFHeader() {
+ if (std::error_code ec = ExecutableWriter<ELFT>::setELFHeader())
+ return ec;
+
+ // Fixup entry point for Thumb code.
+ StringRef entryName = _context.entrySymbolName();
+ if (const AtomLayout *al = _armLayout.findAtomLayoutByName(entryName)) {
+ const auto *ea = dyn_cast<DefinedAtom>(al->_atom);
+ if (ea && ea->codeModel() == DefinedAtom::codeARMThumb)
+ this->_elfHeader->e_entry(al->_virtualAddr | 0x1);
+ }
+
+ return std::error_code();
+}
+
+} // namespace elf
+} // namespace lld
+
+#endif // LLD_READER_WRITER_ELF_ARM_ARM_EXECUTABLE_WRITER_H
diff --git a/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.cpp b/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.cpp
new file mode 100644
index 000000000000..5f2436674268
--- /dev/null
+++ b/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.cpp
@@ -0,0 +1,34 @@
+//===--------- lib/ReaderWriter/ELF/ARM/ARMLinkingContext.cpp -------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ARMLinkingContext.h"
+#include "ARMRelocationPass.h"
+#include "ARMTargetHandler.h"
+
+using namespace lld;
+using namespace lld::elf;
+
+std::unique_ptr<ELFLinkingContext>
+elf::ARMLinkingContext::create(llvm::Triple triple) {
+ if (triple.getArch() == llvm::Triple::arm)
+ return std::unique_ptr<ELFLinkingContext>(
+ new elf::ARMLinkingContext(triple));
+ return nullptr;
+}
+
+elf::ARMLinkingContext::ARMLinkingContext(llvm::Triple triple)
+ : ELFLinkingContext(triple, std::unique_ptr<TargetHandlerBase>(
+ new ARMTargetHandler(*this))) {}
+
+void elf::ARMLinkingContext::addPasses(PassManager &pm) {
+ auto pass = createARMRelocationPass(*this);
+ if (pass)
+ pm.add(std::move(pass));
+ ELFLinkingContext::addPasses(pm);
+}
diff --git a/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h b/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h
new file mode 100644
index 000000000000..249b79c4f07d
--- /dev/null
+++ b/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h
@@ -0,0 +1,36 @@
+//===--------- lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h ---------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_ELF_ARM_ARM_LINKING_CONTEXT_H
+#define LLD_READER_WRITER_ELF_ARM_ARM_LINKING_CONTEXT_H
+
+#include "lld/ReaderWriter/ELFLinkingContext.h"
+#include "llvm/Object/ELF.h"
+#include "llvm/Support/ELF.h"
+
+namespace lld {
+namespace elf {
+
+class ARMLinkingContext final : public ELFLinkingContext {
+public:
+ static std::unique_ptr<ELFLinkingContext> create(llvm::Triple);
+ ARMLinkingContext(llvm::Triple);
+
+ void addPasses(PassManager &) override;
+
+ uint64_t getBaseAddress() const override {
+ if (_baseAddress == 0)
+ return 0x400000;
+ return _baseAddress;
+ }
+};
+} // end namespace elf
+} // end namespace lld
+
+#endif
diff --git a/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp b/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp
new file mode 100644
index 000000000000..d24fdf0fa410
--- /dev/null
+++ b/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp
@@ -0,0 +1,500 @@
+//===--------- lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp ----------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ARMTargetHandler.h"
+#include "ARMLinkingContext.h"
+
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/MathExtras.h"
+
+using namespace lld;
+using namespace lld::elf;
+using namespace llvm::support::endian;
+
+static Reference::Addend readAddend_THM_MOV(const uint8_t *location) {
+ const uint16_t halfHi = read16le(location);
+ const uint16_t halfLo = read16le(location + 2);
+
+ const uint16_t imm8 = halfLo & 0xFF;
+ const uint16_t imm3 = (halfLo >> 12) & 0x7;
+
+ const uint16_t imm4 = halfHi & 0xF;
+ const uint16_t bitI = (halfHi >> 10) & 0x1;
+
+ const auto result = int16_t((imm4 << 12) | (bitI << 11) | (imm3 << 8) | imm8);
+ return result;
+}
+
+static Reference::Addend readAddend_ARM_MOV(const uint8_t *location) {
+ const uint32_t value = read32le(location);
+
+ const uint32_t imm12 = value & 0xFFF;
+ const uint32_t imm4 = (value >> 16) & 0xF;
+
+ const auto result = int32_t((imm4 << 12) | imm12);
+ return result;
+}
+
+static Reference::Addend readAddend_THM_CALL(const uint8_t *location) {
+ const uint16_t halfHi = read16le(location);
+ const uint16_t halfLo = read16le(location + 2);
+
+ const uint16_t imm10 = halfHi & 0x3FF;
+ const uint16_t bitS = (halfHi >> 10) & 0x1;
+
+ const uint16_t imm11 = halfLo & 0x7FF;
+ const uint16_t bitJ2 = (halfLo >> 11) & 0x1;
+ const uint16_t bitI2 = (~(bitJ2 ^ bitS)) & 0x1;
+ const uint16_t bitJ1 = (halfLo >> 13) & 0x1;
+ const uint16_t bitI1 = (~(bitJ1 ^ bitS)) & 0x1;
+
+ const auto result = int32_t((bitS << 24) | (bitI1 << 23) | (bitI2 << 22) |
+ (imm10 << 12) | (imm11 << 1));
+ return llvm::SignExtend64<25>(result);
+}
+
+static Reference::Addend readAddend_ARM_CALL(const uint8_t *location) {
+ const uint32_t value = read32le(location);
+
+ const bool isBLX = (value & 0xF0000000) == 0xF0000000;
+ const uint32_t bitH = isBLX ? ((value & 0x1000000) >> 24) : 0;
+
+ const auto result = int32_t(((value & 0xFFFFFF) << 2) | (bitH << 1));
+ return llvm::SignExtend64<26>(result);
+}
+
+static Reference::Addend readAddend_THM_JUMP11(const uint8_t *location) {
+ const auto value = read16le(location);
+ const uint16_t imm11 = value & 0x7FF;
+
+ return llvm::SignExtend32<12>(imm11 << 1);
+}
+
+static Reference::Addend readAddend(const uint8_t *location,
+ Reference::KindValue kindValue) {
+ switch (kindValue) {
+ case R_ARM_ABS32:
+ case R_ARM_REL32:
+ case R_ARM_TLS_IE32:
+ case R_ARM_TLS_LE32:
+ return (int32_t)read32le(location);
+ case R_ARM_PREL31:
+ return (int32_t)(read32le(location) & 0x7FFFFFFF);
+ case R_ARM_THM_CALL:
+ case R_ARM_THM_JUMP24:
+ return readAddend_THM_CALL(location);
+ case R_ARM_THM_JUMP11:
+ return readAddend_THM_JUMP11(location);
+ case R_ARM_CALL:
+ case R_ARM_JUMP24:
+ return readAddend_ARM_CALL(location);
+ case R_ARM_MOVW_ABS_NC:
+ case R_ARM_MOVT_ABS:
+ return readAddend_ARM_MOV(location);
+ case R_ARM_THM_MOVW_ABS_NC:
+ case R_ARM_THM_MOVT_ABS:
+ return readAddend_THM_MOV(location);
+ default:
+ return 0;
+ }
+}
+
+static inline void applyArmReloc(uint8_t *location, uint32_t result,
+ uint32_t mask = 0xFFFFFFFF) {
+ assert(!(result & ~mask));
+ write32le(location, (read32le(location) & ~mask) | (result & mask));
+}
+
+static inline void applyThmReloc(uint8_t *location, uint16_t resHi,
+ uint16_t resLo, uint16_t maskHi,
+ uint16_t maskLo = 0xFFFF) {
+ assert(!(resHi & ~maskHi) && !(resLo & ~maskLo));
+ write16le(location, (read16le(location) & ~maskHi) | (resHi & maskHi));
+ location += 2;
+ write16le(location, (read16le(location) & ~maskLo) | (resLo & maskLo));
+}
+
+static inline void applyThumb16Reloc(uint8_t *location, uint16_t result,
+ uint16_t mask = 0xFFFF) {
+ assert(!(result & ~mask));
+ write16le(location, (read16le(location) & ~mask) | (result & mask));
+}
+
+/// \brief R_ARM_ABS32 - (S + A) | T
+static void relocR_ARM_ABS32(uint8_t *location, uint64_t P, uint64_t S,
+ int64_t A, bool addressesThumb) {
+ uint64_t T = addressesThumb;
+ uint32_t result = (uint32_t)((S + A) | T);
+
+ DEBUG_WITH_TYPE(
+ "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
+ llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
+ llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
+ llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
+ llvm::dbgs() << " T: 0x" << Twine::utohexstr(T);
+ llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
+ applyArmReloc(location, result);
+}
+
+/// \brief R_ARM_REL32 - ((S + A) | T) - P
+static void relocR_ARM_REL32(uint8_t *location, uint64_t P, uint64_t S,
+ int64_t A, bool addressesThumb) {
+ uint64_t T = addressesThumb;
+ uint32_t result = (uint32_t)(((S + A) | T) - P);
+
+ DEBUG_WITH_TYPE(
+ "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
+ llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
+ llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
+ llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
+ llvm::dbgs() << " T: 0x" << Twine::utohexstr(T);
+ llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
+ applyArmReloc(location, result);
+}
+
+/// \brief R_ARM_PREL31 - ((S + A) | T) - P
+static void relocR_ARM_PREL31(uint8_t *location, uint64_t P, uint64_t S,
+ int64_t A, bool addressesThumb) {
+ uint64_t T = addressesThumb;
+ uint32_t result = (uint32_t)(((S + A) | T) - P);
+ const uint32_t mask = 0x7FFFFFFF;
+ uint32_t rel31 = result & mask;
+
+ DEBUG_WITH_TYPE(
+ "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
+ llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
+ llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
+ llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
+ llvm::dbgs() << " T: 0x" << Twine::utohexstr(T);
+ llvm::dbgs() << " result: 0x" << Twine::utohexstr(result);
+ llvm::dbgs() << " rel31: 0x" << Twine::utohexstr(rel31) << "\n");
+
+ applyArmReloc(location, rel31, mask);
+}
+
+/// \brief Relocate B/BL instructions. useJs defines whether J1 & J2 are used
+static void relocR_ARM_THM_B_L(uint8_t *location, uint32_t result, bool useJs) {
+ result = (result & 0x01FFFFFE) >> 1;
+
+ const uint16_t imm10 = (result >> 11) & 0x3FF;
+ const uint16_t bitS = (result >> 23) & 0x1;
+ const uint16_t resHi = (bitS << 10) | imm10;
+
+ const uint16_t imm11 = result & 0x7FF;
+ const uint16_t bitJ2 = useJs ? ((result >> 21) & 0x1) : bitS;
+ const uint16_t bitI2 = (~(bitJ2 ^ bitS)) & 0x1;
+ const uint16_t bitJ1 = useJs ? ((result >> 22) & 0x1) : bitS;
+ const uint16_t bitI1 = (~(bitJ1 ^ bitS)) & 0x1;
+ const uint16_t resLo = (bitI1 << 13) | (bitI2 << 11) | imm11;
+
+ applyThmReloc(location, resHi, resLo, 0x7FF, 0x2FFF);
+}
+
+/// \brief R_ARM_THM_CALL - ((S + A) | T) - P
+static void relocR_ARM_THM_CALL(uint8_t *location, uint64_t P, uint64_t S,
+ int64_t A, bool useJs, bool addressesThumb) {
+ uint64_t T = addressesThumb;
+ const bool switchMode = !addressesThumb;
+
+ if (switchMode) {
+ P &= ~0x3; // Align(P, 4) by rounding down
+ }
+
+ uint32_t result = (uint32_t)(((S + A) | T) - P);
+
+ DEBUG_WITH_TYPE(
+ "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
+ llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
+ llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
+ llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
+ llvm::dbgs() << " T: 0x" << Twine::utohexstr(T);
+ llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
+ relocR_ARM_THM_B_L(location, result, useJs);
+
+ if (switchMode) {
+ applyThmReloc(location, 0, 0, 0, 0x1001);
+ }
+}
+
+/// \brief R_ARM_THM_JUMP24 - ((S + A) | T) - P
+static void relocR_ARM_THM_JUMP24(uint8_t *location, uint64_t P, uint64_t S,
+ int64_t A, bool addressesThumb) {
+ uint64_t T = addressesThumb;
+ uint32_t result = (uint32_t)(((S + A) | T) - P);
+
+ DEBUG_WITH_TYPE(
+ "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
+ llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
+ llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
+ llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
+ llvm::dbgs() << " T: 0x" << Twine::utohexstr(T);
+ llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
+ relocR_ARM_THM_B_L(location, result, true);
+}
+
+/// \brief R_ARM_THM_JUMP11 - S + A - P
+static void relocR_ARM_THM_JUMP11(uint8_t *location, uint64_t P, uint64_t S,
+ int64_t A) {
+ uint32_t result = (uint32_t)(S + A - P);
+
+ DEBUG_WITH_TYPE(
+ "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
+ llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
+ llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
+ llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
+ llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
+
+ //we cut off first bit because it is always 1 according to p. 4.5.3
+ result = (result & 0x0FFE) >> 1;
+
+ applyThumb16Reloc(location, result, 0x7FF);
+}
+
+/// \brief R_ARM_CALL - ((S + A) | T) - P
+static void relocR_ARM_CALL(uint8_t *location, uint64_t P, uint64_t S,
+ int64_t A, bool addressesThumb) {
+ uint64_t T = addressesThumb;
+ const bool switchMode = addressesThumb;
+
+ uint32_t result = (uint32_t)(((S + A) | T) - P);
+ const uint32_t imm24 = (result & 0x03FFFFFC) >> 2;
+
+ DEBUG_WITH_TYPE(
+ "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
+ llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
+ llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
+ llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
+ llvm::dbgs() << " T: 0x" << Twine::utohexstr(T);
+ llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
+ applyArmReloc(location, imm24, 0xFFFFFF);
+
+ if (switchMode) {
+ const uint32_t bitH = (result & 0x2) >> 1;
+ applyArmReloc(location, (0xFA | bitH) << 24, 0xFF000000);
+ }
+}
+
+/// \brief R_ARM_JUMP24 - ((S + A) | T) - P
+static void relocR_ARM_JUMP24(uint8_t *location, uint64_t P, uint64_t S,
+ int64_t A, bool addressesThumb) {
+ uint64_t T = addressesThumb;
+ uint32_t result = (uint32_t)(((S + A) | T) - P);
+ const uint32_t imm24 = (result & 0x03FFFFFC) >> 2;
+
+ DEBUG_WITH_TYPE(
+ "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
+ llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
+ llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
+ llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
+ llvm::dbgs() << " T: 0x" << Twine::utohexstr(T);
+ llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
+ applyArmReloc(location, imm24, 0xFFFFFF);
+}
+
+/// \brief Relocate ARM MOVW/MOVT instructions
+static void relocR_ARM_MOV(uint8_t *location, uint32_t result) {
+ const uint32_t imm12 = result & 0xFFF;
+ const uint32_t imm4 = (result >> 12) & 0xF;
+
+ applyArmReloc(location, (imm4 << 16) | imm12, 0xF0FFF);
+}
+
+/// \brief R_ARM_MOVW_ABS_NC - (S + A) | T
+static void relocR_ARM_MOVW_ABS_NC(uint8_t *location, uint64_t P, uint64_t S,
+ int64_t A, bool addressesThumb) {
+ uint64_t T = addressesThumb;
+ uint32_t result = (uint32_t)((S + A) | T);
+ const uint32_t arg = result & 0x0000FFFF;
+
+ DEBUG_WITH_TYPE(
+ "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
+ llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
+ llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
+ llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
+ llvm::dbgs() << " T: 0x" << Twine::utohexstr(T);
+ llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
+ return relocR_ARM_MOV(location, arg);
+}
+
+/// \brief R_ARM_MOVT_ABS - S + A
+static void relocR_ARM_MOVT_ABS(uint8_t *location, uint64_t P, uint64_t S,
+ int64_t A) {
+ uint32_t result = (uint32_t)(S + A);
+ const uint32_t arg = (result & 0xFFFF0000) >> 16;
+
+ DEBUG_WITH_TYPE(
+ "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
+ llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
+ llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
+ llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
+ llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
+ return relocR_ARM_MOV(location, arg);
+}
+
+/// \brief Relocate Thumb MOVW/MOVT instructions
+static void relocR_ARM_THM_MOV(uint8_t *location, uint32_t result) {
+ const uint16_t imm8 = result & 0xFF;
+ const uint16_t imm3 = (result >> 8) & 0x7;
+ const uint16_t resLo = (imm3 << 12) | imm8;
+
+ const uint16_t imm4 = (result >> 12) & 0xF;
+ const uint16_t bitI = (result >> 11) & 0x1;
+ const uint16_t resHi = (bitI << 10) | imm4;
+
+ applyThmReloc(location, resHi, resLo, 0x40F, 0x70FF);
+}
+
+/// \brief R_ARM_THM_MOVW_ABS_NC - (S + A) | T
+static void relocR_ARM_THM_MOVW_ABS_NC(uint8_t *location, uint64_t P,
+ uint64_t S, int64_t A,
+ bool addressesThumb) {
+ uint64_t T = addressesThumb;
+ uint32_t result = (uint32_t)((S + A) | T);
+ const uint32_t arg = result & 0x0000FFFF;
+
+ DEBUG_WITH_TYPE(
+ "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
+ llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
+ llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
+ llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
+ llvm::dbgs() << " T: 0x" << Twine::utohexstr(T);
+ llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
+ return relocR_ARM_THM_MOV(location, arg);
+}
+
+/// \brief R_ARM_THM_MOVT_ABS - S + A
+static void relocR_ARM_THM_MOVT_ABS(uint8_t *location, uint64_t P, uint64_t S,
+ int64_t A) {
+ uint32_t result = (uint32_t)(S + A);
+ const uint32_t arg = (result & 0xFFFF0000) >> 16;
+
+ DEBUG_WITH_TYPE(
+ "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
+ llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
+ llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
+ llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
+ llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
+ return relocR_ARM_THM_MOV(location, arg);
+}
+
+/// \brief R_ARM_TLS_IE32 - GOT(S) + A - P => S + A - P
+static void relocR_ARM_TLS_IE32(uint8_t *location, uint64_t P, uint64_t S,
+ int64_t A) {
+ uint32_t result = (uint32_t)(S + A - P);
+
+ DEBUG_WITH_TYPE(
+ "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
+ llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
+ llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
+ llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
+ llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
+ applyArmReloc(location, result);
+}
+
+/// \brief R_ARM_TLS_LE32 - S + A - tp => S + A + tpoff
+static void relocR_ARM_TLS_LE32(uint8_t *location, uint64_t P, uint64_t S,
+ int64_t A, uint64_t tpoff) {
+ uint32_t result = (uint32_t)(S + A + tpoff);
+
+ DEBUG_WITH_TYPE(
+ "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
+ llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
+ llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
+ llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
+ llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
+ applyArmReloc(location, result);
+}
+
+std::error_code ARMTargetRelocationHandler::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::ARM);
+
+ // Calculate proper initial addend for the relocation
+ const Reference::Addend addend =
+ readAddend(location, ref.kindValue());
+
+ // Flags that the relocation addresses Thumb instruction
+ bool addressesThumb = false;
+
+ if (const auto *definedAtom = dyn_cast<DefinedAtom>(ref.target())) {
+ addressesThumb = (DefinedAtom::codeARMThumb == definedAtom->codeModel());
+ }
+
+ switch (ref.kindValue()) {
+ case R_ARM_NONE:
+ break;
+ case R_ARM_ABS32:
+ relocR_ARM_ABS32(location, relocVAddress, targetVAddress, addend,
+ addressesThumb);
+ break;
+ case R_ARM_REL32:
+ relocR_ARM_REL32(location, relocVAddress, targetVAddress, addend,
+ addressesThumb);
+ break;
+ case R_ARM_THM_CALL:
+ // TODO: consider adding bool variable to disable J1 & J2 for archs
+ // before ARMv6
+ relocR_ARM_THM_CALL(location, relocVAddress, targetVAddress, addend, true,
+ addressesThumb);
+ break;
+ case R_ARM_CALL:
+ relocR_ARM_CALL(location, relocVAddress, targetVAddress, addend,
+ addressesThumb);
+ break;
+ case R_ARM_JUMP24:
+ relocR_ARM_JUMP24(location, relocVAddress, targetVAddress, addend,
+ addressesThumb);
+ break;
+ case R_ARM_THM_JUMP24:
+ relocR_ARM_THM_JUMP24(location, relocVAddress, targetVAddress, addend,
+ addressesThumb);
+ break;
+ case R_ARM_THM_JUMP11:
+ relocR_ARM_THM_JUMP11(location, relocVAddress, targetVAddress, addend);
+ break;
+ case R_ARM_MOVW_ABS_NC:
+ relocR_ARM_MOVW_ABS_NC(location, relocVAddress, targetVAddress, addend,
+ addressesThumb);
+ break;
+ case R_ARM_MOVT_ABS:
+ relocR_ARM_MOVT_ABS(location, relocVAddress, targetVAddress, addend);
+ break;
+ case R_ARM_THM_MOVW_ABS_NC:
+ relocR_ARM_THM_MOVW_ABS_NC(location, relocVAddress, targetVAddress, addend,
+ addressesThumb);
+ break;
+ case R_ARM_THM_MOVT_ABS:
+ relocR_ARM_THM_MOVT_ABS(location, relocVAddress, targetVAddress, addend);
+ break;
+ case R_ARM_PREL31:
+ relocR_ARM_PREL31(location, relocVAddress, targetVAddress, addend,
+ addressesThumb);
+ break;
+ case R_ARM_TLS_IE32:
+ relocR_ARM_TLS_IE32(location, relocVAddress, targetVAddress, addend);
+ break;
+ case R_ARM_TLS_LE32:
+ relocR_ARM_TLS_LE32(location, relocVAddress, targetVAddress, addend,
+ _armLayout.getTPOffset());
+ break;
+ default:
+ return make_unhandled_reloc_error();
+ }
+
+ return std::error_code();
+}
diff --git a/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.h b/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.h
new file mode 100644
index 000000000000..227d68617bf9
--- /dev/null
+++ b/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.h
@@ -0,0 +1,38 @@
+//===--------- lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.h ------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_ELF_ARM_ARM_RELOCATION_HANDLER_H
+#define LLD_READER_WRITER_ELF_ARM_ARM_RELOCATION_HANDLER_H
+
+#include "ARMTargetHandler.h"
+
+namespace lld {
+namespace elf {
+typedef llvm::object::ELFType<llvm::support::little, 2, false> ARMELFType;
+
+template <class ELFT> class ARMTargetLayout;
+
+class ARMTargetRelocationHandler final
+ : public TargetRelocationHandler {
+public:
+ ARMTargetRelocationHandler(ARMTargetLayout<ARMELFType> &layout)
+ : _armLayout(layout) {}
+
+ std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &,
+ const lld::AtomLayout &,
+ const Reference &) const override;
+
+private:
+ ARMTargetLayout<ARMELFType> &_armLayout;
+};
+
+} // end namespace elf
+} // end namespace lld
+
+#endif // LLD_READER_WRITER_ELF_ARM_ARM_RELOCATION_HANDLER_H
diff --git a/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp b/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp
new file mode 100644
index 000000000000..27ec66ac5557
--- /dev/null
+++ b/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp
@@ -0,0 +1,373 @@
+//===--------- lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp -------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Defines the relocation processing pass for ARM. This includes
+/// GOT and PLT entries, TLS, COPY, and ifunc.
+///
+/// This also includes additional behavior that gnu-ld and gold implement but
+/// which is not specified anywhere.
+///
+//===----------------------------------------------------------------------===//
+
+#include "ARMRelocationPass.h"
+#include "ARMLinkingContext.h"
+#include "Atoms.h"
+#include "lld/Core/Simple.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Debug.h"
+
+using namespace lld;
+using namespace lld::elf;
+using namespace llvm::ELF;
+
+// ARM B/BL instructions of static relocation veneer.
+// TODO: consider different instruction set for archs below ARMv5
+// (one as for Thumb may be used though it's less optimal).
+static const uint8_t Veneer_ARM_B_BL_StaticAtomContent[8] = {
+ 0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc, #-4]
+ 0x00, 0x00, 0x00, 0x00 // <target_symbol_address>
+};
+
+// Thumb B/BL instructions of static relocation veneer.
+// TODO: consider different instruction set for archs above ARMv5
+// (one as for ARM may be used since it's more optimal).
+static const uint8_t Veneer_THM_B_BL_StaticAtomContent[8] = {
+ 0x78, 0x47, // bx pc
+ 0x00, 0x00, // nop
+ 0xfe, 0xff, 0xff, 0xea // b <target_symbol_address>
+};
+
+// .got values
+static const uint8_t ARMGotAtomContent[4] = {0};
+
+namespace {
+/// \brief Atoms that hold veneer code.
+class VeneerAtom : public SimpleELFDefinedAtom {
+ StringRef _section;
+
+public:
+ VeneerAtom(const File &f, StringRef secName)
+ : SimpleELFDefinedAtom(f), _section(secName) {}
+
+ Scope scope() const override { return DefinedAtom::scopeTranslationUnit; }
+
+ SectionChoice sectionChoice() const override {
+ return DefinedAtom::sectionBasedOnContent;
+ }
+
+ StringRef customSectionName() const override { return _section; }
+
+ ContentType contentType() const override {
+ return DefinedAtom::typeCode;
+ }
+
+ uint64_t size() const override { return rawContent().size(); }
+
+ ContentPermissions permissions() const override { return permR_X; }
+
+ Alignment alignment() const override { return Alignment(2); }
+
+ StringRef name() const override { return _name; }
+ std::string _name;
+};
+
+/// \brief Atoms that hold veneer for statically relocated
+/// ARM B/BL instructions.
+class Veneer_ARM_B_BL_StaticAtom : public VeneerAtom {
+public:
+ Veneer_ARM_B_BL_StaticAtom(const File &f, StringRef secName)
+ : VeneerAtom(f, secName) {}
+
+ ArrayRef<uint8_t> rawContent() const override {
+ return llvm::makeArrayRef(Veneer_ARM_B_BL_StaticAtomContent);
+ }
+};
+
+/// \brief Atoms that hold veneer for statically relocated
+/// Thumb B/BL instructions.
+class Veneer_THM_B_BL_StaticAtom : public VeneerAtom {
+public:
+ Veneer_THM_B_BL_StaticAtom(const File &f, StringRef secName)
+ : VeneerAtom(f, secName) {}
+
+ DefinedAtom::CodeModel codeModel() const override {
+ return DefinedAtom::codeARMThumb;
+ }
+
+ ArrayRef<uint8_t> rawContent() const override {
+ return llvm::makeArrayRef(Veneer_THM_B_BL_StaticAtomContent);
+ }
+};
+
+/// \brief Atoms that are used by ARM dynamic linking
+class ARMGOTAtom : public GOTAtom {
+public:
+ ARMGOTAtom(const File &f, StringRef secName) : GOTAtom(f, secName) {}
+
+ ArrayRef<uint8_t> rawContent() const override {
+ return llvm::makeArrayRef(ARMGotAtomContent);
+ }
+
+ Alignment alignment() const override { return Alignment(2); }
+};
+
+class ELFPassFile : public SimpleFile {
+public:
+ ELFPassFile(const ELFLinkingContext &eti) : SimpleFile("ELFPassFile") {
+ setOrdinal(eti.getNextOrdinalAndIncrement());
+ }
+
+ llvm::BumpPtrAllocator _alloc;
+};
+
+/// \brief CRTP base for handling relocations.
+template <class Derived> class ARMRelocationPass : public Pass {
+ /// \brief Handle a specific reference.
+ void handleReference(const DefinedAtom &atom, const Reference &ref) {
+ DEBUG_WITH_TYPE(
+ "ARM", llvm::dbgs() << "\t" << LLVM_FUNCTION_NAME << "()"
+ << ": Name of Defined Atom: " << atom.name().str();
+ llvm::dbgs() << " kindValue: " << ref.kindValue() << "\n");
+ if (ref.kindNamespace() != Reference::KindNamespace::ELF)
+ return;
+ assert(ref.kindArch() == Reference::KindArch::ARM);
+ switch (ref.kindValue()) {
+ case R_ARM_JUMP24:
+ case R_ARM_THM_JUMP24:
+ static_cast<Derived *>(this)->handleVeneer(atom, ref);
+ break;
+ case R_ARM_TLS_IE32:
+ static_cast<Derived *>(this)->handleTLSIE32(ref);
+ break;
+ }
+ }
+
+protected:
+ std::error_code handleVeneer(const DefinedAtom &atom, const Reference &ref) {
+ // Target symbol and relocated place should have different
+ // instruction sets in order a veneer to be generated in between.
+ const auto *target = dyn_cast<DefinedAtom>(ref.target());
+ if (!target || target->codeModel() == atom.codeModel())
+ return std::error_code();
+
+ // TODO: For unconditional jump instructions (R_ARM_CALL and R_ARM_THM_CALL)
+ // fixup isn't possible without veneer generation for archs below ARMv5.
+
+ // Veneers may only be generated for STT_FUNC target symbols
+ // or for symbols located in sections different to the place of relocation.
+ const auto kindValue = ref.kindValue();
+ StringRef secName = atom.customSectionName();
+ if (DefinedAtom::typeCode != target->contentType() &&
+ !target->customSectionName().equals(secName)) {
+ StringRef kindValStr;
+ if (!this->_ctx.registry().referenceKindToString(
+ ref.kindNamespace(), ref.kindArch(), kindValue, kindValStr)) {
+ kindValStr = "unknown";
+ }
+
+ std::string errStr =
+ (Twine("Reference of type ") + Twine(kindValue) + " (" + kindValStr +
+ ") from " + atom.name() + "+" + Twine(ref.offsetInAtom()) + " to " +
+ ref.target()->name() + "+" + Twine(ref.addend()) +
+ " cannot be effected without a veneer").str();
+
+ llvm_unreachable(errStr.c_str());
+ }
+
+ const Atom *veneer = nullptr;
+ switch (kindValue) {
+ case R_ARM_JUMP24:
+ veneer = static_cast<Derived *>(this)
+ ->getVeneer_ARM_B_BL(target, secName);
+ break;
+ case R_ARM_THM_JUMP24:
+ veneer = static_cast<Derived *>(this)
+ ->getVeneer_THM_B_BL(target, secName);
+ break;
+ default:
+ llvm_unreachable("Unhandled reference type for veneer generation");
+ }
+
+ assert(veneer && "The veneer is not set");
+ const_cast<Reference &>(ref).setTarget(veneer);
+ return std::error_code();
+ }
+
+ std::error_code handleTLSIE32(const Reference &ref) {
+ if (const auto *target = dyn_cast<DefinedAtom>(ref.target())) {
+ const_cast<Reference &>(ref).setTarget(
+ static_cast<Derived *>(this)->getTLSTPOFF32(target));
+ return std::error_code();
+ }
+ llvm_unreachable("R_ARM_TLS_IE32 reloc targets wrong atom type");
+ }
+
+ /// \brief Create a GOT entry for TLS with reloc type and addend specified.
+ template <Reference::KindValue R_ARM_TLS, Reference::Addend A = 0>
+ const GOTAtom *getGOTTLSEntry(const DefinedAtom *da) {
+ auto got = _gotMap.find(da);
+ if (got != _gotMap.end())
+ return got->second;
+ auto g = new (_file._alloc) ARMGOTAtom(_file, ".got");
+ g->addReferenceELF_ARM(R_ARM_TLS, 0, da, A);
+#ifndef NDEBUG
+ g->_name = "__got_tls_";
+ g->_name += da->name();
+#endif
+ _gotMap[da] = g;
+ _gotVector.push_back(g);
+ return g;
+ }
+
+public:
+ ARMRelocationPass(const ELFLinkingContext &ctx) : _file(ctx), _ctx(ctx) {}
+
+ /// \brief Do the pass.
+ ///
+ /// The goal here is to first process each reference individually. Each call
+ /// to handleReference may modify the reference itself and/or create new
+ /// atoms which must be stored in one of the maps below.
+ ///
+ /// After all references are handled, the atoms created during that are all
+ /// added to mf.
+ void perform(std::unique_ptr<MutableFile> &mf) override {
+ ScopedTask task(getDefaultDomain(), "ARM GOT/PLT Pass");
+ DEBUG_WITH_TYPE(
+ "ARM", llvm::dbgs() << "Undefined Atoms" << "\n";
+ for (const auto &atom
+ : mf->undefined()) {
+ llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n";
+ }
+
+ llvm::dbgs() << "Shared Library Atoms" << "\n";
+ for (const auto &atom
+ : mf->sharedLibrary()) {
+ llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n";
+ }
+
+ llvm::dbgs() << "Absolute Atoms" << "\n";
+ for (const auto &atom
+ : mf->absolute()) {
+ llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n";
+ }
+
+ llvm::dbgs() << "Defined Atoms" << "\n";
+ for (const auto &atom
+ : mf->defined()) {
+ llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n";
+ });
+
+ // Process all references.
+ for (const auto &atom : mf->defined()) {
+ for (const auto &ref : *atom) {
+ handleReference(*atom, *ref);
+ }
+ }
+
+ // Add all created atoms to the link.
+ uint64_t ordinal = 0;
+ for (auto &got : _gotVector) {
+ got->setOrdinal(ordinal++);
+ mf->addAtom(*got);
+ }
+ for (auto &veneer : _veneerVector) {
+ veneer->setOrdinal(ordinal++);
+ mf->addAtom(*veneer);
+ }
+ }
+
+protected:
+ /// \brief Owner of all the Atoms created by this pass.
+ ELFPassFile _file;
+ const ELFLinkingContext &_ctx;
+
+ /// \brief Map Atoms to their GOT entries.
+ llvm::DenseMap<const Atom *, GOTAtom *> _gotMap;
+
+ /// \brief Map Atoms to their veneers.
+ llvm::DenseMap<const Atom *, VeneerAtom *> _veneerMap;
+
+ /// \brief the list of GOT/PLT atoms
+ std::vector<GOTAtom *> _gotVector;
+
+ /// \brief the list of veneer atoms.
+ std::vector<VeneerAtom *> _veneerVector;
+};
+
+/// This implements the static relocation model. Meaning GOT and PLT entries are
+/// not created for references that can be directly resolved. These are
+/// converted to a direct relocation. For entries that do require a GOT or PLT
+/// entry, that entry is statically bound.
+///
+/// TLS always assumes module 1 and attempts to remove indirection.
+class ARMStaticRelocationPass final
+ : public ARMRelocationPass<ARMStaticRelocationPass> {
+public:
+ ARMStaticRelocationPass(const elf::ARMLinkingContext &ctx)
+ : ARMRelocationPass(ctx) {}
+
+ /// \brief Get the veneer for ARM B/BL instructions.
+ const VeneerAtom *getVeneer_ARM_B_BL(const DefinedAtom *da,
+ StringRef secName) {
+ auto veneer = _veneerMap.find(da);
+ if (_veneerMap.end() != veneer)
+ return veneer->second;
+
+ auto v = new (_file._alloc) Veneer_ARM_B_BL_StaticAtom(_file, secName);
+ v->addReferenceELF_ARM(R_ARM_ABS32, 4, da, 0);
+
+ v->_name = "__";
+ v->_name += da->name();
+ v->_name += "_from_arm";
+
+ _veneerMap[da] = v;
+ _veneerVector.push_back(v);
+ return v;
+ }
+
+ /// \brief Get the veneer for Thumb B/BL instructions.
+ const VeneerAtom *getVeneer_THM_B_BL(const DefinedAtom *da,
+ StringRef secName) {
+ auto veneer = _veneerMap.find(da);
+ if (_veneerMap.end() != veneer)
+ return veneer->second;
+
+ auto v = new (_file._alloc) Veneer_THM_B_BL_StaticAtom(_file, secName);
+ v->addReferenceELF_ARM(R_ARM_JUMP24, 4, da, 0);
+
+ v->_name = "__";
+ v->_name += da->name();
+ v->_name += "_from_thumb";
+
+ _veneerMap[da] = v;
+ _veneerVector.push_back(v);
+ return v;
+ }
+
+ /// \brief Create a GOT entry for R_ARM_TLS_TPOFF32 reloc.
+ const GOTAtom *getTLSTPOFF32(const DefinedAtom *da) {
+ return getGOTTLSEntry<R_ARM_TLS_LE32>(da);
+ }
+};
+
+} // end of anon namespace
+
+std::unique_ptr<Pass>
+lld::elf::createARMRelocationPass(const ARMLinkingContext &ctx) {
+ switch (ctx.getOutputELFType()) {
+ case llvm::ELF::ET_EXEC:
+ if (ctx.isDynamic())
+ llvm_unreachable("Unhandled output file type");
+ return llvm::make_unique<ARMStaticRelocationPass>(ctx);
+ default:
+ llvm_unreachable("Unhandled output file type");
+ }
+}
diff --git a/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.h b/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.h
new file mode 100644
index 000000000000..651e798f33b1
--- /dev/null
+++ b/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.h
@@ -0,0 +1,31 @@
+//===--------- lib/ReaderWriter/ELF/ARM/ARMRelocationPass.h ---------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Declares the relocation processing pass for ARM. This includes
+/// GOT and PLT entries, TLS, COPY, and ifunc.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_ELF_ARM_ARM_RELOCATION_PASS_H
+#define LLD_READER_WRITER_ELF_ARM_ARM_RELOCATION_PASS_H
+
+#include <memory>
+
+namespace lld {
+class Pass;
+namespace elf {
+class ARMLinkingContext;
+
+/// \brief Create ARM relocation pass for the given linking context.
+std::unique_ptr<Pass> createARMRelocationPass(const ARMLinkingContext &);
+}
+}
+
+#endif
diff --git a/lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h b/lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h
new file mode 100644
index 000000000000..540a480421a8
--- /dev/null
+++ b/lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h
@@ -0,0 +1,46 @@
+//===--------- lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h ------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_ELF_ARM_ARM_SYMBOL_TABLE_H
+#define LLD_READER_WRITER_ELF_ARM_ARM_SYMBOL_TABLE_H
+
+namespace lld {
+namespace elf {
+
+/// \brief The SymbolTable class represents the symbol table in a ELF file
+template<class ELFT>
+class ARMSymbolTable : public SymbolTable<ELFT> {
+public:
+ typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym;
+
+ ARMSymbolTable(const ELFLinkingContext &context);
+
+ void addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da,
+ int64_t addr) override;
+};
+
+template <class ELFT>
+ARMSymbolTable<ELFT>::ARMSymbolTable(const ELFLinkingContext &context)
+ : SymbolTable<ELFT>(context, ".symtab",
+ DefaultLayout<ELFT>::ORDER_SYMBOL_TABLE) {}
+
+template <class ELFT>
+void ARMSymbolTable<ELFT>::addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da,
+ int64_t addr) {
+ SymbolTable<ELFT>::addDefinedAtom(sym, da, addr);
+
+ // Set zero bit to distinguish symbols addressing Thumb instructions
+ if (DefinedAtom::codeARMThumb == da->codeModel())
+ sym.st_value = static_cast<int64_t>(sym.st_value) | 0x1;
+}
+
+} // elf
+} // lld
+
+#endif // LLD_READER_WRITER_ELF_ARM_ARM_SYMBOL_TABLE_H
diff --git a/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.cpp b/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.cpp
new file mode 100644
index 000000000000..de90f490f621
--- /dev/null
+++ b/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.cpp
@@ -0,0 +1,44 @@
+//===--------- lib/ReaderWriter/ELF/ARM/ARMTargetHandler.cpp --------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Atoms.h"
+#include "ARMExecutableWriter.h"
+#include "ARMTargetHandler.h"
+#include "ARMLinkingContext.h"
+
+using namespace lld;
+using namespace elf;
+
+ARMTargetHandler::ARMTargetHandler(ARMLinkingContext &context)
+ : _context(context), _armTargetLayout(
+ new ARMTargetLayout<ARMELFType>(context)),
+ _armRelocationHandler(new ARMTargetRelocationHandler(
+ *_armTargetLayout.get())) {}
+
+void ARMTargetHandler::registerRelocationNames(Registry &registry) {
+ registry.addKindTable(Reference::KindNamespace::ELF, Reference::KindArch::ARM,
+ kindStrings);
+}
+
+std::unique_ptr<Writer> ARMTargetHandler::getWriter() {
+ switch (this->_context.getOutputELFType()) {
+ case llvm::ELF::ET_EXEC:
+ return std::unique_ptr<Writer>(
+ new ARMExecutableWriter<ARMELFType>(_context, *_armTargetLayout.get()));
+ default:
+ llvm_unreachable("unsupported output type");
+ }
+}
+
+#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name),
+
+const Registry::KindStrings ARMTargetHandler::kindStrings[] = {
+#include "llvm/Support/ELFRelocs/ARM.def"
+ LLD_KIND_STRING_END
+};
diff --git a/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.h b/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.h
new file mode 100644
index 000000000000..10641954da25
--- /dev/null
+++ b/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.h
@@ -0,0 +1,88 @@
+//===--------- lib/ReaderWriter/ELF/ARM/ARMTargetHandler.h ----------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_ELF_ARM_ARM_TARGET_HANDLER_H
+#define LLD_READER_WRITER_ELF_ARM_ARM_TARGET_HANDLER_H
+
+#include "ARMELFFile.h"
+#include "ARMELFReader.h"
+#include "ARMRelocationHandler.h"
+#include "DefaultTargetHandler.h"
+#include "TargetLayout.h"
+
+#include "lld/Core/Simple.h"
+#include "llvm/ADT/Optional.h"
+#include <map>
+
+namespace lld {
+namespace elf {
+typedef llvm::object::ELFType<llvm::support::little, 2, false> ARMELFType;
+class ARMLinkingContext;
+
+template <class ELFT> class ARMTargetLayout : public TargetLayout<ELFT> {
+public:
+ ARMTargetLayout(ARMLinkingContext &context)
+ : TargetLayout<ELFT>(context) {}
+
+ uint64_t getTPOffset() {
+ if (_tpOff.hasValue())
+ return *_tpOff;
+
+ for (const auto &phdr : *this->_programHeader) {
+ if (phdr->p_type == llvm::ELF::PT_TLS) {
+ _tpOff = llvm::RoundUpToAlignment(TCB_SIZE, phdr->p_align);
+ return *_tpOff;
+ }
+ }
+ llvm_unreachable("TLS segment not found");
+ }
+
+private:
+ // TCB block size of the TLS.
+ enum { TCB_SIZE = 0x8 };
+
+ // Cached value of the TLS offset from the $tp pointer.
+ llvm::Optional<uint64_t> _tpOff;
+};
+
+class ARMTargetHandler final : public DefaultTargetHandler<ARMELFType> {
+public:
+ ARMTargetHandler(ARMLinkingContext &context);
+
+ ARMTargetLayout<ARMELFType> &getTargetLayout() override {
+ return *(_armTargetLayout.get());
+ }
+
+ void registerRelocationNames(Registry &registry) override;
+
+ const ARMTargetRelocationHandler &getRelocationHandler() const override {
+ return *(_armRelocationHandler.get());
+ }
+
+ std::unique_ptr<Reader> getObjReader() override {
+ return std::unique_ptr<Reader>(new ARMELFObjectReader(_context));
+ }
+
+ std::unique_ptr<Reader> getDSOReader() override {
+ return std::unique_ptr<Reader>(new ARMELFDSOReader(_context));
+ }
+
+ std::unique_ptr<Writer> getWriter() override;
+
+private:
+ static const Registry::KindStrings kindStrings[];
+ ARMLinkingContext &_context;
+ std::unique_ptr<ARMTargetLayout<ARMELFType>> _armTargetLayout;
+ std::unique_ptr<ARMTargetRelocationHandler> _armRelocationHandler;
+};
+
+} // end namespace elf
+} // end namespace lld
+
+#endif // LLD_READER_WRITER_ELF_ARM_ARM_TARGET_HANDLER_H
diff --git a/lib/ReaderWriter/ELF/ARM/CMakeLists.txt b/lib/ReaderWriter/ELF/ARM/CMakeLists.txt
new file mode 100644
index 000000000000..2ccf9eb6266d
--- /dev/null
+++ b/lib/ReaderWriter/ELF/ARM/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_llvm_library(lldARMELFTarget
+ ARMLinkingContext.cpp
+ ARMTargetHandler.cpp
+ ARMRelocationHandler.cpp
+ ARMRelocationPass.cpp
+ LINK_LIBS
+ lldELF
+ lldReaderWriter
+ lldCore
+ LLVMObject
+ LLVMSupport
+ )
diff --git a/lib/ReaderWriter/ELF/ARM/Makefile b/lib/ReaderWriter/ELF/ARM/Makefile
new file mode 100644
index 000000000000..f67d36a1b612
--- /dev/null
+++ b/lib/ReaderWriter/ELF/ARM/Makefile
@@ -0,0 +1,15 @@
+##===------ lld/lib/ReaderWriter/ELF/ARM/Makefile ----------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+LLD_LEVEL := ../../../..
+LIBRARYNAME := lldARMELFTarget
+USEDLIBS = lldCore.a
+CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF/ARM -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF
+
+include $(LLD_LEVEL)/Makefile
diff --git a/lib/ReaderWriter/ELF/ARM/TODO.rst b/lib/ReaderWriter/ELF/ARM/TODO.rst
new file mode 100644
index 000000000000..d05419decb78
--- /dev/null
+++ b/lib/ReaderWriter/ELF/ARM/TODO.rst
@@ -0,0 +1,20 @@
+ELF ARM
+~~~~~~~~~~~
+
+Unimplemented Features
+######################
+
+* Static executable linking - in progress
+* Dynamic executable linking
+* DSO linking
+* PLT entries' generation for images larger than 2^28 bytes (see Sec. A.3 of the ELF reference)
+* ARM and Thumb interworking (see http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0203j/Bcghfebi.html)
+* .ARM.exidx section handling
+* -init/-fini options
+* Lots of relocations
+
+Unimplemented Relocations
+#########################
+
+All of these relocations are defined in:
+http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044e/IHI0044E_aaelf.pdf