summaryrefslogtreecommitdiff
path: root/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp')
-rw-r--r--lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp334
1 files changed, 334 insertions, 0 deletions
diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp b/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp
new file mode 100644
index 000000000000..9b10c2f160f4
--- /dev/null
+++ b/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp
@@ -0,0 +1,334 @@
+//===- lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp --------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "HexagonExecutableWriter.h"
+#include "HexagonDynamicLibraryWriter.h"
+#include "HexagonLinkingContext.h"
+#include "HexagonTargetHandler.h"
+
+using namespace lld;
+using namespace elf;
+using namespace llvm::ELF;
+
+using llvm::makeArrayRef;
+
+HexagonTargetHandler::HexagonTargetHandler(HexagonLinkingContext &context)
+ : _hexagonLinkingContext(context),
+ _hexagonRuntimeFile(new HexagonRuntimeFile<HexagonELFType>(context)),
+ _hexagonTargetLayout(new HexagonTargetLayout<HexagonELFType>(context)),
+ _hexagonRelocationHandler(new HexagonTargetRelocationHandler(
+ *_hexagonTargetLayout.get())) {}
+
+std::unique_ptr<Writer> HexagonTargetHandler::getWriter() {
+ switch (_hexagonLinkingContext.getOutputELFType()) {
+ case llvm::ELF::ET_EXEC:
+ return std::unique_ptr<Writer>(
+ new elf::HexagonExecutableWriter<HexagonELFType>(
+ _hexagonLinkingContext, *_hexagonTargetLayout.get()));
+ case llvm::ELF::ET_DYN:
+ return std::unique_ptr<Writer>(
+ new elf::HexagonDynamicLibraryWriter<HexagonELFType>(
+ _hexagonLinkingContext, *_hexagonTargetLayout.get()));
+ case llvm::ELF::ET_REL:
+ llvm_unreachable("TODO: support -r mode");
+ default:
+ llvm_unreachable("unsupported output type");
+ }
+}
+
+using namespace llvm::ELF;
+
+// .got atom
+const uint8_t hexagonGotAtomContent[4] = { 0 };
+// .got.plt atom (entry 0)
+const uint8_t hexagonGotPlt0AtomContent[16] = { 0 };
+// .got.plt atom (all other entries)
+const uint8_t hexagonGotPltAtomContent[4] = { 0 };
+// .plt (entry 0)
+const uint8_t hexagonPlt0AtomContent[28] = {
+ 0x00, 0x40, 0x00, 0x00, // { immext (#0)
+ 0x1c, 0xc0, 0x49, 0x6a, // r28 = add (pc, ##GOT0@PCREL) } # address of GOT0
+ 0x0e, 0x42, 0x9c, 0xe2, // { r14 -= add (r28, #16) # offset of GOTn from GOTa
+ 0x4f, 0x40, 0x9c, 0x91, // r15 = memw (r28 + #8) # object ID at GOT2
+ 0x3c, 0xc0, 0x9c, 0x91, // r28 = memw (r28 + #4) }# dynamic link at GOT1
+ 0x0e, 0x42, 0x0e, 0x8c, // { r14 = asr (r14, #2) # index of PLTn
+ 0x00, 0xc0, 0x9c, 0x52, // jumpr r28 } # call dynamic linker
+};
+
+// .plt (other entries)
+const uint8_t hexagonPltAtomContent[16] = {
+ 0x00, 0x40, 0x00, 0x00, // { immext (#0)
+ 0x0e, 0xc0, 0x49, 0x6a, // r14 = add (pc, ##GOTn@PCREL) } # address of GOTn
+ 0x1c, 0xc0, 0x8e, 0x91, // r28 = memw (r14) # contents of GOTn
+ 0x00, 0xc0, 0x9c, 0x52, // jumpr r28 # call it
+};
+
+class HexagonGOTAtom : public GOTAtom {
+public:
+ HexagonGOTAtom(const File &f) : GOTAtom(f, ".got") {}
+
+ ArrayRef<uint8_t> rawContent() const override {
+ return makeArrayRef(hexagonGotAtomContent);
+ }
+
+ Alignment alignment() const override { return Alignment(2); }
+};
+
+class HexagonGOTPLTAtom : public GOTAtom {
+public:
+ HexagonGOTPLTAtom(const File &f) : GOTAtom(f, ".got.plt") {}
+
+ ArrayRef<uint8_t> rawContent() const override {
+ return makeArrayRef(hexagonGotPltAtomContent);
+ }
+
+ Alignment alignment() const override { return Alignment(2); }
+};
+
+class HexagonGOTPLT0Atom : public GOTAtom {
+public:
+ HexagonGOTPLT0Atom(const File &f) : GOTAtom(f, ".got.plt") {}
+
+ ArrayRef<uint8_t> rawContent() const override {
+ return makeArrayRef(hexagonGotPlt0AtomContent);
+ }
+
+ Alignment alignment() const override { return Alignment(3); }
+};
+
+class HexagonPLT0Atom : public PLT0Atom {
+public:
+ HexagonPLT0Atom(const File &f) : PLT0Atom(f) {}
+
+ ArrayRef<uint8_t> rawContent() const override {
+ return makeArrayRef(hexagonPlt0AtomContent);
+ }
+};
+
+class HexagonPLTAtom : public PLTAtom {
+
+public:
+ HexagonPLTAtom(const File &f, StringRef secName) : PLTAtom(f, secName) {}
+
+ ArrayRef<uint8_t> rawContent() const override {
+ return makeArrayRef(hexagonPltAtomContent);
+ }
+};
+
+class ELFPassFile : public SimpleFile {
+public:
+ ELFPassFile(const ELFLinkingContext &eti) : SimpleFile("ELFPassFile") {
+ setOrdinal(eti.getNextOrdinalAndIncrement());
+ }
+
+ llvm::BumpPtrAllocator _alloc;
+};
+
+/// \brief Create GOT and PLT entries for relocations. Handles standard GOT/PLT
+template <class Derived> class GOTPLTPass : public Pass {
+ /// \brief Handle a specific reference.
+ void handleReference(const DefinedAtom &atom, const Reference &ref) {
+ if (ref.kindNamespace() != Reference::KindNamespace::ELF)
+ return;
+ assert(ref.kindArch() == Reference::KindArch::Hexagon);
+ switch (ref.kindValue()) {
+ case R_HEX_PLT_B22_PCREL:
+ case R_HEX_B22_PCREL:
+ static_cast<Derived *>(this)->handlePLT32(ref);
+ break;
+ case R_HEX_GOT_LO16:
+ case R_HEX_GOT_HI16:
+ case R_HEX_GOT_32_6_X:
+ case R_HEX_GOT_16_X:
+ case R_HEX_GOT_11_X:
+ static_cast<Derived *>(this)->handleGOTREL(ref);
+ break;
+ }
+ }
+
+protected:
+ /// \brief Create a GOT entry containing 0.
+ const GOTAtom *getNullGOT() {
+ if (!_null) {
+ _null = new (_file._alloc) HexagonGOTPLTAtom(_file);
+#ifndef NDEBUG
+ _null->_name = "__got_null";
+#endif
+ }
+ return _null;
+ }
+
+public:
+ GOTPLTPass(const ELFLinkingContext &ctx)
+ : _file(ctx), _null(nullptr), _PLT0(nullptr), _got0(nullptr) {}
+
+ /// \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 {
+ // 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;
+ if (_PLT0) {
+ _PLT0->setOrdinal(ordinal++);
+ mf->addAtom(*_PLT0);
+ }
+ for (auto &plt : _pltVector) {
+ plt->setOrdinal(ordinal++);
+ mf->addAtom(*plt);
+ }
+ if (_null) {
+ _null->setOrdinal(ordinal++);
+ mf->addAtom(*_null);
+ }
+ if (_got0) {
+ _got0->setOrdinal(ordinal++);
+ mf->addAtom(*_got0);
+ }
+ for (auto &got : _gotVector) {
+ got->setOrdinal(ordinal++);
+ mf->addAtom(*got);
+ }
+ }
+
+protected:
+ /// \brief Owner of all the Atoms created by this pass.
+ ELFPassFile _file;
+
+ /// \brief Map Atoms to their GOT entries.
+ llvm::DenseMap<const Atom *, GOTAtom *> _gotMap;
+
+ /// \brief Map Atoms to their PLT entries.
+ llvm::DenseMap<const Atom *, PLTAtom *> _pltMap;
+
+ /// \brief the list of GOT/PLT atoms
+ std::vector<GOTAtom *> _gotVector;
+ std::vector<PLTAtom *> _pltVector;
+
+ /// \brief GOT entry that is always 0. Used for undefined weaks.
+ GOTAtom *_null;
+
+ /// \brief The got and plt entries for .PLT0. This is used to call into the
+ /// dynamic linker for symbol resolution.
+ /// @{
+ PLT0Atom *_PLT0;
+ GOTAtom *_got0;
+ /// @}
+};
+
+class DynamicGOTPLTPass final : public GOTPLTPass<DynamicGOTPLTPass> {
+public:
+ DynamicGOTPLTPass(const elf::HexagonLinkingContext &ctx) : GOTPLTPass(ctx) {
+ _got0 = new (_file._alloc) HexagonGOTPLT0Atom(_file);
+#ifndef NDEBUG
+ _got0->_name = "__got0";
+#endif
+ }
+
+ const PLT0Atom *getPLT0() {
+ if (_PLT0)
+ return _PLT0;
+ _PLT0 = new (_file._alloc) HexagonPLT0Atom(_file);
+ _PLT0->addReferenceELF_Hexagon(R_HEX_B32_PCREL_X, 0, _got0, 0);
+ _PLT0->addReferenceELF_Hexagon(R_HEX_6_PCREL_X, 4, _got0, 4);
+ DEBUG_WITH_TYPE("PLT", llvm::dbgs() << "[ PLT0/GOT0 ] "
+ << "Adding plt0/got0 \n");
+ return _PLT0;
+ }
+
+ const PLTAtom *getPLTEntry(const Atom *a) {
+ auto plt = _pltMap.find(a);
+ if (plt != _pltMap.end())
+ return plt->second;
+ auto ga = new (_file._alloc) HexagonGOTPLTAtom(_file);
+ ga->addReferenceELF_Hexagon(R_HEX_JMP_SLOT, 0, a, 0);
+ auto pa = new (_file._alloc) HexagonPLTAtom(_file, ".plt");
+ pa->addReferenceELF_Hexagon(R_HEX_B32_PCREL_X, 0, ga, 0);
+ pa->addReferenceELF_Hexagon(R_HEX_6_PCREL_X, 4, ga, 4);
+
+ // Point the got entry to the PLT0 atom initially
+ ga->addReferenceELF_Hexagon(R_HEX_32, 0, getPLT0(), 0);
+#ifndef NDEBUG
+ ga->_name = "__got_";
+ ga->_name += a->name();
+ pa->_name = "__plt_";
+ pa->_name += a->name();
+ DEBUG_WITH_TYPE("PLT", llvm::dbgs() << "[" << a->name() << "] "
+ << "Adding plt/got: " << pa->_name
+ << "/" << ga->_name << "\n");
+#endif
+ _gotMap[a] = ga;
+ _pltMap[a] = pa;
+ _gotVector.push_back(ga);
+ _pltVector.push_back(pa);
+ return pa;
+ }
+
+ const GOTAtom *getGOTEntry(const Atom *a) {
+ auto got = _gotMap.find(a);
+ if (got != _gotMap.end())
+ return got->second;
+ auto ga = new (_file._alloc) HexagonGOTAtom(_file);
+ ga->addReferenceELF_Hexagon(R_HEX_GLOB_DAT, 0, a, 0);
+
+#ifndef NDEBUG
+ ga->_name = "__got_";
+ ga->_name += a->name();
+ DEBUG_WITH_TYPE("GOT", llvm::dbgs() << "[" << a->name() << "] "
+ << "Adding got: " << ga->_name << "\n");
+#endif
+ _gotMap[a] = ga;
+ _gotVector.push_back(ga);
+ return ga;
+ }
+
+ std::error_code handleGOTREL(const Reference &ref) {
+ // Turn this so that the target is set to the GOT entry
+ const_cast<Reference &>(ref).setTarget(getGOTEntry(ref.target()));
+ return std::error_code();
+ }
+
+ std::error_code handlePLT32(const Reference &ref) {
+ // Turn this into a PC32 to the PLT entry.
+ assert(ref.kindNamespace() == Reference::KindNamespace::ELF);
+ assert(ref.kindArch() == Reference::KindArch::Hexagon);
+ const_cast<Reference &>(ref).setKindValue(R_HEX_B22_PCREL);
+ const_cast<Reference &>(ref).setTarget(getPLTEntry(ref.target()));
+ return std::error_code();
+ }
+};
+
+void elf::HexagonLinkingContext::addPasses(PassManager &pm) {
+ if (isDynamic())
+ pm.add(llvm::make_unique<DynamicGOTPLTPass>(*this));
+ ELFLinkingContext::addPasses(pm);
+}
+
+void HexagonTargetHandler::registerRelocationNames(Registry &registry) {
+ registry.addKindTable(Reference::KindNamespace::ELF,
+ Reference::KindArch::Hexagon, kindStrings);
+}
+
+#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name),
+
+const Registry::KindStrings HexagonTargetHandler::kindStrings[] = {
+#include "llvm/Support/ELFRelocs/Hexagon.def"
+ LLD_KIND_STRING_END
+};
+
+#undef ELF_RELOC