diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2015-03-24 21:31:36 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2015-03-24 21:31:36 +0000 |
commit | fb911942f1434f3d1750f83f25f5e42c80e60638 (patch) | |
tree | 1678c4a4f0182e4029a86d135aa4a1b7d09e3c41 /lib/ReaderWriter/ELF |
Notes
Diffstat (limited to 'lib/ReaderWriter/ELF')
127 files changed, 18463 insertions, 0 deletions
diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64DynamicLibraryWriter.h b/lib/ReaderWriter/ELF/AArch64/AArch64DynamicLibraryWriter.h new file mode 100644 index 0000000000000..12ba52a38f380 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64DynamicLibraryWriter.h @@ -0,0 +1,69 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64DynamicLibraryWriter.h ---------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef AARCH64_DYNAMIC_LIBRARY_WRITER_H +#define AARCH64_DYNAMIC_LIBRARY_WRITER_H + +#include "AArch64LinkingContext.h" +#include "AArch64TargetHandler.h" +#include "DynamicLibraryWriter.h" + +namespace lld { +namespace elf { + +template <class ELFT> +class AArch64DynamicLibraryWriter : public DynamicLibraryWriter<ELFT> { +public: + AArch64DynamicLibraryWriter(AArch64LinkingContext &context, + AArch64TargetLayout<ELFT> &layout); + +protected: + // Add any runtime files and their atoms to the output + virtual bool createImplicitFiles(std::vector<std::unique_ptr<File>> &); + + virtual void finalizeDefaultAtomValues() { + return DynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues(); + } + + virtual void addDefaultAtoms() { + return DynamicLibraryWriter<ELFT>::addDefaultAtoms(); + } + +private: + class GOTFile : public SimpleFile { + public: + GOTFile(const ELFLinkingContext &eti) : SimpleFile("GOTFile") {} + llvm::BumpPtrAllocator _alloc; + }; + + std::unique_ptr<GOTFile> _gotFile; + AArch64LinkingContext &_context; + AArch64TargetLayout<ELFT> &_AArch64Layout; +}; + +template <class ELFT> +AArch64DynamicLibraryWriter<ELFT>::AArch64DynamicLibraryWriter( + AArch64LinkingContext &context, AArch64TargetLayout<ELFT> &layout) + : DynamicLibraryWriter<ELFT>(context, layout), + _gotFile(new GOTFile(context)), _context(context), + _AArch64Layout(layout) {} + +template <class ELFT> +bool AArch64DynamicLibraryWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + DynamicLibraryWriter<ELFT>::createImplicitFiles(result); + _gotFile->addAtom(*new (_gotFile->_alloc) GLOBAL_OFFSET_TABLEAtom(*_gotFile)); + _gotFile->addAtom(*new (_gotFile->_alloc) DYNAMICAtom(*_gotFile)); + result.push_back(std::move(_gotFile)); + return true; +} + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64ELFFile.h b/lib/ReaderWriter/ELF/AArch64/AArch64ELFFile.h new file mode 100644 index 0000000000000..9d5207c1c4b49 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64ELFFile.h @@ -0,0 +1,41 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64ELFFile.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_AARCH64_AARCH64_ELF_FILE_H +#define LLD_READER_WRITER_ELF_AARCH64_AARCH64_ELF_FILE_H + +#include "ELFReader.h" + +namespace lld { +namespace elf { + +class AArch64LinkingContext; + +template <class ELFT> class AArch64ELFFile : public ELFFile<ELFT> { +public: + AArch64ELFFile(std::unique_ptr<MemoryBuffer> mb, AArch64LinkingContext &ctx) + : ELFFile<ELFT>(std::move(mb), ctx) {} + + static ErrorOr<std::unique_ptr<AArch64ELFFile>> + create(std::unique_ptr<MemoryBuffer> mb, AArch64LinkingContext &ctx) { + return std::unique_ptr<AArch64ELFFile<ELFT>>( + new AArch64ELFFile<ELFT>(std::move(mb), ctx)); + } +}; + +template <class ELFT> class AArch64DynamicFile : public DynamicFile<ELFT> { +public: + AArch64DynamicFile(const AArch64LinkingContext &context, StringRef name) + : DynamicFile<ELFT>(context, name) {} +}; + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_AARCH64_AARCH64_ELF_FILE_H diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64ELFReader.h b/lib/ReaderWriter/ELF/AArch64/AArch64ELFReader.h new file mode 100644 index 0000000000000..05f312db3e7b7 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64ELFReader.h @@ -0,0 +1,62 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64ELFReader.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_AARCH64_AARCH64_ELF_READER_H +#define LLD_READER_WRITER_AARCH64_AARCH64_ELF_READER_H + +#include "AArch64ELFFile.h" +#include "ELFReader.h" + +namespace lld { +namespace elf { + +typedef llvm::object::ELFType<llvm::support::little, 2, true> AArch64ELFType; + +struct AArch64DynamicFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + AArch64LinkingContext &ctx) { + return lld::elf::AArch64DynamicFile<ELFT>::create(std::move(mb), ctx); + } +}; + +struct AArch64ELFFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::File>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + AArch64LinkingContext &ctx) { + return lld::elf::AArch64ELFFile<ELFT>::create(std::move(mb), ctx); + } +}; + +class AArch64ELFObjectReader + : public ELFObjectReader<AArch64ELFType, AArch64ELFFileCreateELFTraits, + AArch64LinkingContext> { +public: + AArch64ELFObjectReader(AArch64LinkingContext &ctx) + : ELFObjectReader<AArch64ELFType, AArch64ELFFileCreateELFTraits, + AArch64LinkingContext>(ctx, llvm::ELF::EM_AARCH64) {} +}; + +class AArch64ELFDSOReader + : public ELFDSOReader<AArch64ELFType, AArch64DynamicFileCreateELFTraits, + AArch64LinkingContext> { +public: + AArch64ELFDSOReader(AArch64LinkingContext &ctx) + : ELFDSOReader<AArch64ELFType, AArch64DynamicFileCreateELFTraits, + AArch64LinkingContext>(ctx, llvm::ELF::EM_AARCH64) {} +}; + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_AARCH64_AARCH64_ELF_READER_H diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.h b/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.h new file mode 100644 index 0000000000000..73963f56ef70f --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.h @@ -0,0 +1,68 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.h -------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef AARCH64_EXECUTABLE_WRITER_H +#define AARCH64_EXECUTABLE_WRITER_H + +#include "AArch64LinkingContext.h" +#include "ExecutableWriter.h" + +namespace lld { +namespace elf { + +template <class ELFT> +class AArch64ExecutableWriter : public ExecutableWriter<ELFT> { +public: + AArch64ExecutableWriter(AArch64LinkingContext &context, + AArch64TargetLayout<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 { + return ExecutableWriter<ELFT>::finalizeDefaultAtomValues(); + } + + void addDefaultAtoms() override{ + return ExecutableWriter<ELFT>::addDefaultAtoms(); + } + +private: + class GOTFile : public SimpleFile { + public: + GOTFile(const ELFLinkingContext &eti) : SimpleFile("GOTFile") {} + llvm::BumpPtrAllocator _alloc; + }; + + std::unique_ptr<GOTFile> _gotFile; + AArch64LinkingContext &_context; + AArch64TargetLayout<ELFT> &_AArch64Layout; +}; + +template <class ELFT> +AArch64ExecutableWriter<ELFT>::AArch64ExecutableWriter( + AArch64LinkingContext &context, AArch64TargetLayout<ELFT> &layout) + : ExecutableWriter<ELFT>(context, layout), _gotFile(new GOTFile(context)), + _context(context), _AArch64Layout(layout) {} + +template <class ELFT> +bool AArch64ExecutableWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + ExecutableWriter<ELFT>::createImplicitFiles(result); + _gotFile->addAtom(*new (_gotFile->_alloc) GLOBAL_OFFSET_TABLEAtom(*_gotFile)); + if (_context.isDynamic()) + _gotFile->addAtom(*new (_gotFile->_alloc) DYNAMICAtom(*_gotFile)); + result.push_back(std::move(_gotFile)); + return true; +} + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.cpp new file mode 100644 index 0000000000000..9eb98f4477098 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.cpp @@ -0,0 +1,33 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.cpp -------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AArch64LinkingContext.h" +#include "AArch64RelocationPass.h" +#include "AArch64TargetHandler.h" + +using namespace lld; + +std::unique_ptr<ELFLinkingContext> +elf::AArch64LinkingContext::create(llvm::Triple triple) { + if (triple.getArch() == llvm::Triple::aarch64) + return std::unique_ptr<ELFLinkingContext>( + new elf::AArch64LinkingContext(triple)); + return nullptr; +} + +elf::AArch64LinkingContext::AArch64LinkingContext(llvm::Triple triple) + : ELFLinkingContext(triple, std::unique_ptr<TargetHandlerBase>( + new AArch64TargetHandler(*this))) {} + +void elf::AArch64LinkingContext::addPasses(PassManager &pm) { + auto pass = createAArch64RelocationPass(*this); + if (pass) + pm.add(std::move(pass)); + ELFLinkingContext::addPasses(pm); +} diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.h b/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.h new file mode 100644 index 0000000000000..ebd91fe0a95b8 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.h @@ -0,0 +1,95 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.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_AARCH64_AARCH64_LINKING_CONTEXT_H +#define LLD_READER_WRITER_ELF_AARCH64_AARCH64_LINKING_CONTEXT_H + +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { + +enum { + /// \brief The offset to add operation for a R_AARCH64_ADR_GOT_PAGE + ADD_AARCH64_GOTRELINDEX = 0xE000, +}; + +class AArch64LinkingContext final : public ELFLinkingContext { +public: + static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + AArch64LinkingContext(llvm::Triple); + + void addPasses(PassManager &) override; + + uint64_t getBaseAddress() const override { + if (_baseAddress == 0) + return 0x400000; + return _baseAddress; + } + + bool isDynamicRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::AArch64); + switch (r.kindValue()) { + case llvm::ELF::R_AARCH64_COPY: + case llvm::ELF::R_AARCH64_GLOB_DAT: + case llvm::ELF::R_AARCH64_RELATIVE: + case llvm::ELF::R_AARCH64_TLS_DTPREL64: + case llvm::ELF::R_AARCH64_TLS_DTPMOD64: + case llvm::ELF::R_AARCH64_TLS_TPREL64: + case llvm::ELF::R_AARCH64_TLSDESC: + case llvm::ELF::R_AARCH64_IRELATIVE: + return true; + default: + return false; + } + } + + bool isCopyRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::AArch64); + if (r.kindValue() == llvm::ELF::R_AARCH64_COPY) + return true; + return false; + } + + bool isPLTRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::AArch64); + switch (r.kindValue()) { + case llvm::ELF::R_AARCH64_JUMP_SLOT: + case llvm::ELF::R_AARCH64_IRELATIVE: + return true; + default: + return false; + } + } + + bool isRelativeReloc(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::AArch64); + switch (r.kindValue()) { + case llvm::ELF::R_AARCH64_IRELATIVE: + case llvm::ELF::R_AARCH64_RELATIVE: + return true; + default: + return false; + } + } +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.cpp new file mode 100644 index 0000000000000..d1ecc7fa884b0 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.cpp @@ -0,0 +1,440 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.cpp ----------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AArch64TargetHandler.h" +#include "AArch64LinkingContext.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; + +#define PAGE(X) ((X) & ~0x0FFFL) + +/// \brief Check X is in the interval (-2^(bits-1), 2^bits] +static bool withinSignedUnsignedRange(int64_t X, int bits) { + return isIntN(bits - 1, X) || isUIntN(bits, X); +} + +/// \brief R_AARCH64_ABS64 - word64: S + A +static void relocR_AARCH64_ABS64(uint8_t *location, uint64_t P, uint64_t S, + int64_t A) { + int64_t result = (int64_t)S + A; + DEBUG_WITH_TYPE( + "AArch64", 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"); + write64le(location, result | read64le(location)); +} + +/// \brief R_AARCH64_PREL32 - word32: S + A - P +static void relocR_AARCH64_PREL32(uint8_t *location, uint64_t P, uint64_t S, + int64_t A) { + int32_t result = (int32_t)((S + A) - P); + write32le(location, result + (int32_t)read32le(location)); +} + +/// \brief R_AARCH64_ABS32 - word32: S + A +static std::error_code relocR_AARCH64_ABS32(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int64_t result = S + A; + if (!withinSignedUnsignedRange(result, 32)) + return make_out_of_range_reloc_error(); + DEBUG_WITH_TYPE( + "AArch64", 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"); + write32le(location, result | read32le(location)); + return std::error_code(); +} + +/// \brief R_AARCH64_ADR_PREL_PG_HI21 - Page(S+A) - Page(P) +static void relocR_AARCH64_ADR_PREL_PG_HI21(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + uint64_t result = (PAGE(S + A) - PAGE(P)); + result = result >> 12; + uint32_t immlo = result & 0x3; + uint32_t immhi = result & 0x1FFFFC; + immlo = immlo << 29; + immhi = immhi << 3; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " immhi: " << Twine::utohexstr(immhi); + llvm::dbgs() << " immlo: " << Twine::utohexstr(immlo); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, immlo | immhi | read32le(location)); + // TODO: Make sure this is correct! +} + +/// \brief R_AARCH64_ADR_PREL_LO21 - S + A - P +static void relocR_AARCH64_ADR_PREL_LO21(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + uint64_t result = (S + A) - P; + uint32_t immlo = result & 0x3; + uint32_t immhi = result & 0x1FFFFC; + immlo = immlo << 29; + immhi = immhi << 3; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " immhi: " << Twine::utohexstr(immhi); + llvm::dbgs() << " immlo: " << Twine::utohexstr(immlo); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, immlo | immhi | read32le(location)); + // TODO: Make sure this is correct! +} + +/// \brief R_AARCH64_ADD_ABS_LO12_NC +static void relocR_AARCH64_ADD_ABS_LO12_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)((S + A) & 0xFFF); + result <<= 10; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +static void relocJump26(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { + int32_t result = (int32_t)((S + A) - P); + result &= 0x0FFFFFFC; + result >>= 2; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +/// \brief R_AARCH64_CONDBR19 +static void relocR_AARCH64_CONDBR19(uint8_t *location, uint64_t P, uint64_t S, + int64_t A) { + int32_t result = (int32_t)((S + A) - P); + result &= 0x01FFFFC; + result <<= 3; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +/// \brief R_AARCH64_LDST8_ABS_LO12_NC - S + A +static void relocR_AARCH64_LDST8_ABS_LO12_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)((S + A) & 0xFFF); + result <<= 10; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +/// \brief R_AARCH64_LDST16_ABS_LO12_NC +static void relocR_AARCH64_LDST16_ABS_LO12_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)(S + A); + result &= 0x0FFC; + result <<= 9; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +/// \brief R_AARCH64_LDST32_ABS_LO12_NC +static void relocR_AARCH64_LDST32_ABS_LO12_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)(S + A); + result &= 0x0FFC; + result <<= 8; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +/// \brief R_AARCH64_LDST64_ABS_LO12_NC +static void relocR_AARCH64_LDST64_ABS_LO12_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)(S + A); + result &= 0x0FF8; + result <<= 7; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +/// \brief R_AARCH64_LDST128_ABS_LO12_NC +static void relocR_AARCH64_LDST128_ABS_LO12_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)(S + A); + result &= 0x0FF8; + result <<= 6; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +static void relocR_AARCH64_ADR_GOT_PAGE(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + uint64_t result = PAGE(S + A) - PAGE(P); + result >>= 12; + uint32_t immlo = result & 0x3; + uint32_t immhi = result & 0x1FFFFC; + immlo = immlo << 29; + immhi = immhi << 3; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " immhi: " << Twine::utohexstr(immhi); + llvm::dbgs() << " immlo: " << Twine::utohexstr(immlo); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +// R_AARCH64_LD64_GOT_LO12_NC +static void relocR_AARCH64_LD64_GOT_LO12_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = S + A; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + result &= 0xFF8; + result <<= 7; + write32le(location, result | read32le(location)); +} + +// ADD_AARCH64_GOTRELINDEX +static void relocADD_AARCH64_GOTRELINDEX(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = S + A; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + result &= 0xFFF; + result <<= 10; + write32le(location, result | read32le(location)); +} + +// R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 +static void relocR_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21(uint8_t *location, + uint64_t P, uint64_t S, + int64_t A) { + int64_t result = PAGE(S + A) - PAGE(P); + result >>= 12; + uint32_t immlo = result & 0x3; + uint32_t immhi = result & 0x1FFFFC; + immlo = immlo << 29; + immhi = immhi << 3; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " immhi: " << Twine::utohexstr(immhi); + llvm::dbgs() << " immlo: " << Twine::utohexstr(immlo); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, immlo | immhi | read32le(location)); +} + +// R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC +static void relocR_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC(uint8_t *location, + uint64_t P, uint64_t S, + int64_t A) { + int32_t result = S + A; + result &= 0xFF8; + result <<= 7; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +/// \brief R_AARCH64_TLSLE_ADD_TPREL_HI12 +static void relocR_AARCH64_TLSLE_ADD_TPREL_HI12(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = S + A; + result &= 0x0FFF000; + result >>= 2; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +/// \brief R_AARCH64_TLSLE_ADD_TPREL_LO12_NC +static void relocR_AARCH64_TLSLE_ADD_TPREL_LO12_NC(uint8_t *location, + uint64_t P, uint64_t S, + int64_t A) { + int32_t result = S + A; + result &= 0x0FFF; + result <<= 10; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +std::error_code AArch64TargetRelocationHandler::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::AArch64); + switch (ref.kindValue()) { + case R_AARCH64_NONE: + break; + case R_AARCH64_ABS64: + relocR_AARCH64_ABS64(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_AARCH64_PREL32: + relocR_AARCH64_PREL32(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_ABS32: + return relocR_AARCH64_ABS32(location, relocVAddress, targetVAddress, + ref.addend()); + // Runtime only relocations. Ignore here. + case R_AARCH64_RELATIVE: + case R_AARCH64_IRELATIVE: + case R_AARCH64_JUMP_SLOT: + case R_AARCH64_GLOB_DAT: + break; + case R_AARCH64_ADR_PREL_PG_HI21: + relocR_AARCH64_ADR_PREL_PG_HI21(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_ADR_PREL_LO21: + relocR_AARCH64_ADR_PREL_LO21(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_ADD_ABS_LO12_NC: + relocR_AARCH64_ADD_ABS_LO12_NC(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_CALL26: + case R_AARCH64_JUMP26: + relocJump26(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_AARCH64_CONDBR19: + relocR_AARCH64_CONDBR19(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_ADR_GOT_PAGE: + relocR_AARCH64_ADR_GOT_PAGE(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_LD64_GOT_LO12_NC: + relocR_AARCH64_LD64_GOT_LO12_NC(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_LDST8_ABS_LO12_NC: + relocR_AARCH64_LDST8_ABS_LO12_NC(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_LDST16_ABS_LO12_NC: + relocR_AARCH64_LDST16_ABS_LO12_NC(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_LDST32_ABS_LO12_NC: + relocR_AARCH64_LDST32_ABS_LO12_NC(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_LDST64_ABS_LO12_NC: + relocR_AARCH64_LDST64_ABS_LO12_NC(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_LDST128_ABS_LO12_NC: + relocR_AARCH64_LDST128_ABS_LO12_NC(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case ADD_AARCH64_GOTRELINDEX: + relocADD_AARCH64_GOTRELINDEX(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: + relocR_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21(location, relocVAddress, + targetVAddress, ref.addend()); + break; + case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: + relocR_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC(location, relocVAddress, + targetVAddress, ref.addend()); + break; + case R_AARCH64_TLSLE_ADD_TPREL_HI12: + relocR_AARCH64_TLSLE_ADD_TPREL_HI12(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: + relocR_AARCH64_TLSLE_ADD_TPREL_LO12_NC(location, relocVAddress, + targetVAddress, ref.addend()); + break; + default: + return make_unhandled_reloc_error(); + } + + return std::error_code(); +} diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.h b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.h new file mode 100644 index 0000000000000..b1d3c09dc936c --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.h @@ -0,0 +1,33 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.h ------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef AARCH64_RELOCATION_HANDLER_H +#define AARCH64_RELOCATION_HANDLER_H + +#include "AArch64TargetHandler.h" + +namespace lld { +namespace elf { +typedef llvm::object::ELFType<llvm::support::little, 2, true> AArch64ELFType; + +template <class ELFT> class AArch64TargetLayout; + +class AArch64TargetRelocationHandler final : public TargetRelocationHandler { +public: + std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, + const lld::AtomLayout &, + const Reference &) const override; + + static const Registry::KindStrings kindStrings[]; +}; + +} // end namespace elf +} // end namespace lld + +#endif // AArch64_RELOCATION_HANDLER_H diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp new file mode 100644 index 0000000000000..0bd12958b27bd --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp @@ -0,0 +1,527 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.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 AArch64. 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 "AArch64RelocationPass.h" +#include "AArch64LinkingContext.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; + +namespace { +// .got values +const uint8_t AArch64GotAtomContent[8] = {0}; + +// .plt value (entry 0) +const uint8_t AArch64Plt0AtomContent[32] = { + 0xf0, 0x7b, 0xbf, + 0xa9, // stp x16, x30, [sp,#-16]! + 0x10, 0x00, 0x00, + 0x90, // adrp x16, Page(eh_frame) + 0x11, 0x02, 0x40, + 0xf9, // ldr x17, [x16,#offset] + 0x10, 0x02, 0x00, + 0x91, // add x16, x16, #offset + 0x20, 0x02, 0x1f, + 0xd6, // br x17 + 0x1f, 0x20, 0x03, + 0xd5, // nop + 0x1f, 0x20, 0x03, + 0xd5, // nop + 0x1f, 0x20, 0x03, + 0xd5 // nop +}; + +// .plt values (other entries) +const uint8_t AArch64PltAtomContent[16] = { + 0x10, 0x00, 0x00, + 0x90, // adrp x16, PAGE(<GLOBAL_OFFSET_TABLE>) + 0x11, 0x02, 0x40, + 0xf9, // ldr x17, [x16,#offset] + 0x10, 0x02, 0x00, + 0x91, // add x16, x16, #offset + 0x20, 0x02, 0x1f, + 0xd6 // br x17 +}; + +/// \brief Atoms that are used by AArch64 dynamic linking +class AArch64GOTAtom : public GOTAtom { +public: + AArch64GOTAtom(const File &f, StringRef secName) : GOTAtom(f, secName) {} + + ArrayRef<uint8_t> rawContent() const override { + return ArrayRef<uint8_t>(AArch64GotAtomContent, 8); + } +}; + +class AArch64PLT0Atom : public PLT0Atom { +public: + AArch64PLT0Atom(const File &f) : PLT0Atom(f) {} + ArrayRef<uint8_t> rawContent() const override { + return ArrayRef<uint8_t>(AArch64Plt0AtomContent, 32); + } +}; + +class AArch64PLTAtom : public PLTAtom { +public: + AArch64PLTAtom(const File &f, StringRef secName) : PLTAtom(f, secName) {} + + ArrayRef<uint8_t> rawContent() const override { + return ArrayRef<uint8_t>(AArch64PltAtomContent, 16); + } +}; + +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 AArch64RelocationPass : public Pass { + /// \brief Handle a specific reference. + void handleReference(const DefinedAtom &atom, const Reference &ref) { + DEBUG_WITH_TYPE( + "AArch64", 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::AArch64); + switch (ref.kindValue()) { + case R_AARCH64_ABS32: + case R_AARCH64_ABS16: + case R_AARCH64_ABS64: + case R_AARCH64_PREL16: + case R_AARCH64_PREL32: + case R_AARCH64_PREL64: + static_cast<Derived *>(this)->handlePlain(ref); + break; + case R_AARCH64_GOTREL32: + case R_AARCH64_GOTREL64: + static_cast<Derived *>(this)->handleGOT(ref); + break; + case R_AARCH64_ADR_PREL_PG_HI21: + static_cast<Derived *>(this)->handlePlain(ref); + break; + case R_AARCH64_LDST8_ABS_LO12_NC: + case R_AARCH64_LDST16_ABS_LO12_NC: + case R_AARCH64_LDST32_ABS_LO12_NC: + case R_AARCH64_LDST64_ABS_LO12_NC: + case R_AARCH64_LDST128_ABS_LO12_NC: + static_cast<Derived *>(this)->handlePlain(ref); + break; + case R_AARCH64_ADD_ABS_LO12_NC: + static_cast<Derived *>(this)->handlePlain(ref); + break; + case R_AARCH64_CALL26: + case R_AARCH64_JUMP26: + case R_AARCH64_CONDBR19: + static_cast<Derived *>(this)->handlePlain(ref); + break; + case R_AARCH64_TLSLE_ADD_TPREL_HI12: + case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: + static_cast<Derived *>(this)->handlePlain(ref); + break; + case R_AARCH64_ADR_GOT_PAGE: + case R_AARCH64_LD64_GOT_LO12_NC: + case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: + case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: + static_cast<Derived *>(this)->handleGOT(ref); + break; + } + } + +protected: + /// \brief get the PLT entry for a given IFUNC Atom. + /// + /// If the entry does not exist. Both the GOT and PLT entry is created. + const PLTAtom *getIFUNCPLTEntry(const DefinedAtom *da) { + auto plt = _pltMap.find(da); + if (plt != _pltMap.end()) + return plt->second; + auto ga = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); + ga->addReferenceELF_AArch64(R_AARCH64_IRELATIVE, 0, da, 0); + auto pa = new (_file._alloc) AArch64PLTAtom(_file, ".plt"); + pa->addReferenceELF_AArch64(R_AARCH64_PREL32, 2, ga, -4); +#ifndef NDEBUG + ga->_name = "__got_ifunc_"; + ga->_name += da->name(); + pa->_name = "__plt_ifunc_"; + pa->_name += da->name(); +#endif + _gotMap[da] = ga; + _pltMap[da] = pa; + _gotVector.push_back(ga); + _pltVector.push_back(pa); + return pa; + } + + /// \brief Redirect the call to the PLT stub for the target IFUNC. + /// + /// This create a PLT and GOT entry for the IFUNC if one does not exist. The + /// GOT entry and a IRELATIVE relocation to the original target resolver. + std::error_code handleIFUNC(const Reference &ref) { + auto target = dyn_cast_or_null<const DefinedAtom>(ref.target()); + if (target && target->contentType() == DefinedAtom::typeResolver) + const_cast<Reference &>(ref).setTarget(getIFUNCPLTEntry(target)); + return std::error_code(); + } + + /// \brief Create a GOT entry for the TP offset of a TLS atom. + const GOTAtom *getGOTTPOFF(const Atom *atom) { + auto got = _gotMap.find(atom); + if (got == _gotMap.end()) { + auto g = new (_file._alloc) AArch64GOTAtom(_file, ".got"); + g->addReferenceELF_AArch64(R_AARCH64_GOTREL64, 0, atom, 0); +#ifndef NDEBUG + g->_name = "__got_tls_"; + g->_name += atom->name(); +#endif + _gotMap[atom] = g; + _gotVector.push_back(g); + return g; + } + return got->second; + } + + /// \brief Create a TPOFF64 GOT entry and change the relocation to a PC32 to + /// the GOT. + void handleGOTTPOFF(const Reference &ref) { + const_cast<Reference &>(ref).setTarget(getGOTTPOFF(ref.target())); + const_cast<Reference &>(ref).setKindValue(R_AARCH64_PREL32); + } + + /// \brief Create a GOT entry containing 0. + const GOTAtom *getNullGOT() { + if (!_null) { + _null = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); +#ifndef NDEBUG + _null->_name = "__got_null"; +#endif + } + return _null; + } + + const GOTAtom *getGOT(const DefinedAtom *da) { + auto got = _gotMap.find(da); + if (got == _gotMap.end()) { + auto g = new (_file._alloc) AArch64GOTAtom(_file, ".got"); + g->addReferenceELF_AArch64(R_AARCH64_ABS64, 0, da, 0); +#ifndef NDEBUG + g->_name = "__got_"; + g->_name += da->name(); +#endif + _gotMap[da] = g; + _gotVector.push_back(g); + return g; + } + return got->second; + } + +public: + AArch64RelocationPass(const ELFLinkingContext &ctx) + : _file(ctx), _ctx(ctx), _null(nullptr), _PLT0(nullptr), _got0(nullptr), + _got1(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 { + ScopedTask task(getDefaultDomain(), "AArch64 GOT/PLT Pass"); + DEBUG_WITH_TYPE( + "AArch64", 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"; + } + // Process all references. + llvm::dbgs() + << "Defined Atoms" + << "\n"); + 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 (_PLT0) { + _got0->setOrdinal(ordinal++); + _got1->setOrdinal(ordinal++); + mf->addAtom(*_got0); + mf->addAtom(*_got1); + } + for (auto &got : _gotVector) { + got->setOrdinal(ordinal++); + mf->addAtom(*got); + } + for (auto obj : _objectVector) { + obj->setOrdinal(ordinal++); + mf->addAtom(*obj); + } + } + +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 PLT entries. + llvm::DenseMap<const Atom *, PLTAtom *> _pltMap; + + /// \brief Map Atoms to their Object entries. + llvm::DenseMap<const Atom *, ObjectAtom *> _objectMap; + + /// \brief the list of GOT/PLT atoms + std::vector<GOTAtom *> _gotVector; + std::vector<PLTAtom *> _pltVector; + std::vector<ObjectAtom *> _objectVector; + + /// \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; + GOTAtom *_got1; + /// @} +}; + +/// 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 AArch64StaticRelocationPass final + : public AArch64RelocationPass<AArch64StaticRelocationPass> { +public: + AArch64StaticRelocationPass(const elf::AArch64LinkingContext &ctx) + : AArch64RelocationPass(ctx) {} + + std::error_code handlePlain(const Reference &ref) { return handleIFUNC(ref); } + + std::error_code handlePLT32(const Reference &ref) { + // __tls_get_addr is handled elsewhere. + if (ref.target() && ref.target()->name() == "__tls_get_addr") { + const_cast<Reference &>(ref).setKindValue(R_AARCH64_NONE); + return std::error_code(); + } + // Static code doesn't need PLTs. + const_cast<Reference &>(ref).setKindValue(R_AARCH64_PREL32); + // Handle IFUNC. + if (const DefinedAtom *da = + dyn_cast_or_null<const DefinedAtom>(ref.target())) + if (da->contentType() == DefinedAtom::typeResolver) + return handleIFUNC(ref); + return std::error_code(); + } + + std::error_code handleGOT(const Reference &ref) { + if (isa<UndefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getNullGOT()); + else if (const DefinedAtom *da = dyn_cast<const DefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getGOT(da)); + return std::error_code(); + } +}; + +class AArch64DynamicRelocationPass final + : public AArch64RelocationPass<AArch64DynamicRelocationPass> { +public: + AArch64DynamicRelocationPass(const elf::AArch64LinkingContext &ctx) + : AArch64RelocationPass(ctx) {} + + const PLT0Atom *getPLT0() { + if (_PLT0) + return _PLT0; + // Fill in the null entry. + getNullGOT(); + _PLT0 = new (_file._alloc) AArch64PLT0Atom(_file); + _got0 = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); + _got1 = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); + _PLT0->addReferenceELF_AArch64(R_AARCH64_ADR_GOT_PAGE, 4, _got0, 0); + _PLT0->addReferenceELF_AArch64(R_AARCH64_LD64_GOT_LO12_NC, 8, _got1, 0); + _PLT0->addReferenceELF_AArch64(ADD_AARCH64_GOTRELINDEX, 12, _got1, 0); +#ifndef NDEBUG + _PLT0->_name = "__PLT0"; + _got0->_name = "__got0"; + _got1->_name = "__got1"; +#endif + 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) AArch64GOTAtom(_file, ".got.plt"); + ga->addReferenceELF_AArch64(R_AARCH64_JUMP_SLOT, 0, a, 0); + auto pa = new (_file._alloc) AArch64PLTAtom(_file, ".plt"); + pa->addReferenceELF_AArch64(R_AARCH64_ADR_GOT_PAGE, 0, ga, 0); + pa->addReferenceELF_AArch64(R_AARCH64_LD64_GOT_LO12_NC, 4, ga, 0); + pa->addReferenceELF_AArch64(ADD_AARCH64_GOTRELINDEX, 8, ga, 0); + pa->addReferenceELF_AArch64(R_AARCH64_NONE, 12, getPLT0(), 0); + // Set the starting address of the got entry to the first instruction in + // the plt0 entry. + ga->addReferenceELF_AArch64(R_AARCH64_ABS32, 0, getPLT0(), 0); +#ifndef NDEBUG + ga->_name = "__got_"; + ga->_name += a->name(); + pa->_name = "__plt_"; + pa->_name += a->name(); +#endif + _gotMap[a] = ga; + _pltMap[a] = pa; + _gotVector.push_back(ga); + _pltVector.push_back(pa); + return pa; + } + + const ObjectAtom *getObjectEntry(const SharedLibraryAtom *a) { + auto obj = _objectMap.find(a); + if (obj != _objectMap.end()) + return obj->second; + + auto oa = new (_file._alloc) ObjectAtom(_file); + // This needs to point to the atom that we just created. + oa->addReferenceELF_AArch64(R_AARCH64_COPY, 0, oa, 0); + + oa->_name = a->name(); + oa->_size = a->size(); + + _objectMap[a] = oa; + _objectVector.push_back(oa); + return oa; + } + + std::error_code handlePlain(const Reference &ref) { + if (!ref.target()) + return std::error_code(); + if (auto sla = dyn_cast<SharedLibraryAtom>(ref.target())) { + if (sla->type() == SharedLibraryAtom::Type::Data) + const_cast<Reference &>(ref).setTarget(getObjectEntry(sla)); + else if (sla->type() == SharedLibraryAtom::Type::Code) + const_cast<Reference &>(ref).setTarget(getPLTEntry(sla)); + } else + return handleIFUNC(ref); + return std::error_code(); + } + + std::error_code handlePLT32(const Reference &ref) { + // Turn this into a PC32 to the PLT entry. + const_cast<Reference &>(ref).setKindValue(R_AARCH64_PREL32); + // Handle IFUNC. + if (const DefinedAtom *da = + dyn_cast_or_null<const DefinedAtom>(ref.target())) + if (da->contentType() == DefinedAtom::typeResolver) + return handleIFUNC(ref); + if (isa<const SharedLibraryAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getPLTEntry(ref.target())); + return std::error_code(); + } + + const GOTAtom *getSharedGOT(const SharedLibraryAtom *sla) { + auto got = _gotMap.find(sla); + if (got == _gotMap.end()) { + auto g = new (_file._alloc) AArch64GOTAtom(_file, ".got"); + g->addReferenceELF_AArch64(R_AARCH64_GLOB_DAT, 0, sla, 0); +#ifndef NDEBUG + g->_name = "__got_"; + g->_name += sla->name(); +#endif + _gotMap[sla] = g; + _gotVector.push_back(g); + return g; + } + return got->second; + } + + std::error_code handleGOT(const Reference &ref) { + if (isa<UndefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getNullGOT()); + else if (const DefinedAtom *da = dyn_cast<const DefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getGOT(da)); + else if (const auto sla = dyn_cast<const SharedLibraryAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getSharedGOT(sla)); + return std::error_code(); + } +}; +} // end anon namespace + +std::unique_ptr<Pass> +lld::elf::createAArch64RelocationPass(const AArch64LinkingContext &ctx) { + switch (ctx.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + if (ctx.isDynamic()) + return llvm::make_unique<AArch64DynamicRelocationPass>(ctx); + return llvm::make_unique<AArch64StaticRelocationPass>(ctx); + case llvm::ELF::ET_DYN: + return llvm::make_unique<AArch64DynamicRelocationPass>(ctx); + case llvm::ELF::ET_REL: + return nullptr; + default: + llvm_unreachable("Unhandled output file type"); + } +} diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.h b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.h new file mode 100644 index 0000000000000..73d784e3b52d8 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.h @@ -0,0 +1,32 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.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 AArch64. This includes +/// GOT and PLT entries, TLS, COPY, and ifunc. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_ELF_AARCH64_AARCH64_RELOCATION_PASS_H +#define LLD_READER_WRITER_ELF_AARCH64_AARCH64_RELOCATION_PASS_H + +#include <memory> + +namespace lld { +class Pass; +namespace elf { +class AArch64LinkingContext; + +/// \brief Create AArch64 relocation pass for the given linking context. +std::unique_ptr<Pass> +createAArch64RelocationPass(const AArch64LinkingContext &); +} +} + +#endif diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.cpp new file mode 100644 index 0000000000000..607f767f8b8a4 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.cpp @@ -0,0 +1,52 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.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 "AArch64DynamicLibraryWriter.h" +#include "AArch64ExecutableWriter.h" +#include "AArch64LinkingContext.h" +#include "AArch64TargetHandler.h" + +using namespace lld; +using namespace elf; + +AArch64TargetHandler::AArch64TargetHandler(AArch64LinkingContext &context) + : _context(context), + _AArch64TargetLayout(new AArch64TargetLayout<AArch64ELFType>(context)), + _AArch64RelocationHandler(new AArch64TargetRelocationHandler()) {} + +void AArch64TargetHandler::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, + Reference::KindArch::AArch64, kindStrings); +} + +std::unique_ptr<Writer> AArch64TargetHandler::getWriter() { + switch (this->_context.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + return std::unique_ptr<Writer>(new AArch64ExecutableWriter<AArch64ELFType>( + _context, *_AArch64TargetLayout.get())); + case llvm::ELF::ET_DYN: + return std::unique_ptr<Writer>( + new AArch64DynamicLibraryWriter<AArch64ELFType>( + _context, *_AArch64TargetLayout.get())); + case llvm::ELF::ET_REL: + llvm_unreachable("TODO: support -r mode"); + default: + llvm_unreachable("unsupported output type"); + } +} + +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), + +const Registry::KindStrings AArch64TargetHandler::kindStrings[] = { +#include "llvm/Support/ELFRelocs/AArch64.def" + LLD_KIND_STRING_END +}; + +#undef ELF_RELOC diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.h b/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.h new file mode 100644 index 0000000000000..4eb6786cdf1fb --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.h @@ -0,0 +1,64 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.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_AARCH64_AARCH64_TARGET_HANDLER_H +#define LLD_READER_WRITER_ELF_AARCH64_AARCH64_TARGET_HANDLER_H + +#include "AArch64ELFFile.h" +#include "AArch64ELFReader.h" +#include "AArch64RelocationHandler.h" +#include "DefaultTargetHandler.h" +#include "TargetLayout.h" +#include "lld/Core/Simple.h" + +namespace lld { +namespace elf { +class AArch64LinkingContext; + +template <class ELFT> class AArch64TargetLayout : public TargetLayout<ELFT> { +public: + AArch64TargetLayout(AArch64LinkingContext &context) + : TargetLayout<ELFT>(context) {} +}; + +class AArch64TargetHandler final : public DefaultTargetHandler<AArch64ELFType> { +public: + AArch64TargetHandler(AArch64LinkingContext &context); + + AArch64TargetLayout<AArch64ELFType> &getTargetLayout() override { + return *(_AArch64TargetLayout.get()); + } + + void registerRelocationNames(Registry ®istry) override; + + const AArch64TargetRelocationHandler &getRelocationHandler() const override { + return *(_AArch64RelocationHandler.get()); + } + + std::unique_ptr<Reader> getObjReader() override { + return std::unique_ptr<Reader>(new AArch64ELFObjectReader(_context)); + } + + std::unique_ptr<Reader> getDSOReader() override { + return std::unique_ptr<Reader>(new AArch64ELFDSOReader(_context)); + } + + std::unique_ptr<Writer> getWriter() override; + +private: + static const Registry::KindStrings kindStrings[]; + AArch64LinkingContext &_context; + std::unique_ptr<AArch64TargetLayout<AArch64ELFType>> _AArch64TargetLayout; + std::unique_ptr<AArch64TargetRelocationHandler> _AArch64RelocationHandler; +}; + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/AArch64/CMakeLists.txt b/lib/ReaderWriter/ELF/AArch64/CMakeLists.txt new file mode 100644 index 0000000000000..de94a4df5078a --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/CMakeLists.txt @@ -0,0 +1,12 @@ +add_llvm_library(lldAArch64ELFTarget + AArch64LinkingContext.cpp + AArch64TargetHandler.cpp + AArch64RelocationHandler.cpp + AArch64RelocationPass.cpp + LINK_LIBS + lldELF + lldReaderWriter + lldCore + LLVMObject + LLVMSupport + ) diff --git a/lib/ReaderWriter/ELF/AArch64/Makefile b/lib/ReaderWriter/ELF/AArch64/Makefile new file mode 100644 index 0000000000000..02cff4747d0d2 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/Makefile @@ -0,0 +1,15 @@ +##===- lld/lib/ReaderWriter/ELF/AArch64/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 := lldAArch64ELFTarget +USEDLIBS = lldCore.a +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF/AArch64 -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/AArch64/TODO.rst b/lib/ReaderWriter/ELF/AArch64/TODO.rst new file mode 100644 index 0000000000000..aa6f616ff33fb --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/TODO.rst @@ -0,0 +1,15 @@ +ELF AArch64 +~~~~~~~~~~~ + +Unimplemented Features +###################### + +* Just about everything! + +Unimplemented Relocations +######################### + +All of these relocations are defined in: +http://infocenter.arm.com/help/topic/com.arm.doc.ihi0056b/IHI0056B_aaelf64.pdf + + diff --git a/lib/ReaderWriter/ELF/ARM/ARMELFFile.h b/lib/ReaderWriter/ELF/ARM/ARMELFFile.h new file mode 100644 index 0000000000000..bc5ee35b8213b --- /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 0000000000000..31af531563ea5 --- /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 0000000000000..19311d516e4de --- /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 0000000000000..5f24366742684 --- /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 0000000000000..249b79c4f07d0 --- /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 0000000000000..d24fdf0fa410a --- /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 0000000000000..227d68617bf98 --- /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 0000000000000..27ec66ac55572 --- /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 0000000000000..651e798f33b1b --- /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 0000000000000..540a480421a8a --- /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 0000000000000..de90f490f621b --- /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 ®istry) { + 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 0000000000000..10641954da25d --- /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 ®istry) 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 0000000000000..2ccf9eb6266db --- /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 0000000000000..f67d36a1b612d --- /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 0000000000000..d05419decb786 --- /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 diff --git a/lib/ReaderWriter/ELF/Atoms.h b/lib/ReaderWriter/ELF/Atoms.h new file mode 100644 index 0000000000000..6a506d21d9385 --- /dev/null +++ b/lib/ReaderWriter/ELF/Atoms.h @@ -0,0 +1,849 @@ +//===- lib/ReaderWriter/ELF/Atoms.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_ATOMS_H +#define LLD_READER_WRITER_ELF_ATOMS_H + +#include "TargetHandler.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/Simple.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringSwitch.h" +#include <memory> +#include <vector> + +namespace lld { +namespace elf { +template <class ELFT> class DynamicFile; +template <typename ELFT> class ELFFile; + +/// \brief Relocation References: Defined Atoms may contain references that will +/// need to be patched before the executable is written. +/// +/// Construction of ELFReferences is two pass process. ELFReferences are +/// instantiated while we are iterating over symbol tables to atomize +/// symbols. At that time we only know the index of relocation target symbol +/// (not target atom) about a relocation, so we store the index to +/// ELFREference. In the second pass, ELFReferences are revisited to update +/// target atoms by target symbol indexes. +template <class ELFT> class ELFReference : public Reference { + typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel; + typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela; + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + +public: + ELFReference(const Elf_Rela *rela, uint64_t off, Reference::KindArch arch, + Reference::KindValue relocType, uint32_t idx) + : Reference(Reference::KindNamespace::ELF, arch, relocType), + _target(nullptr), _targetSymbolIndex(idx), _offsetInAtom(off), + _addend(rela->r_addend) {} + + ELFReference(uint64_t off, Reference::KindArch arch, + Reference::KindValue relocType, uint32_t idx) + : Reference(Reference::KindNamespace::ELF, arch, relocType), + _target(nullptr), _targetSymbolIndex(idx), _offsetInAtom(off), + _addend(0) {} + + ELFReference(uint32_t edgeKind) + : Reference(Reference::KindNamespace::all, Reference::KindArch::all, + edgeKind), + _target(nullptr), _targetSymbolIndex(0), _offsetInAtom(0), _addend(0) {} + + uint64_t offsetInAtom() const override { return _offsetInAtom; } + + const Atom *target() const override { return _target; } + + /// \brief The symbol table index that contains the target reference. + uint64_t targetSymbolIndex() const { + return _targetSymbolIndex; + } + + Addend addend() const override { return _addend; } + + virtual void setOffset(uint64_t off) { _offsetInAtom = off; } + + void setAddend(Addend A) override { _addend = A; } + + void setTarget(const Atom *newAtom) override { _target = newAtom; } + +private: + const Atom *_target; + uint64_t _targetSymbolIndex; + uint64_t _offsetInAtom; + Addend _addend; +}; + +/// \brief These atoms store symbols that are fixed to a particular address. +/// This atom has no content its address will be used by the writer to fixup +/// references that point to it. +template <class ELFT> class ELFAbsoluteAtom : public AbsoluteAtom { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + +public: + ELFAbsoluteAtom(const ELFFile<ELFT> &file, StringRef name, + const Elf_Sym *symbol, uint64_t value) + : _owningFile(file), _name(name), _symbol(symbol), _value(value) { + } + + const ELFFile<ELFT> &file() const override { return _owningFile; } + + Scope scope() const override { + if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN) + return scopeLinkageUnit; + if (_symbol->getBinding() == llvm::ELF::STB_LOCAL) + return scopeTranslationUnit; + return scopeGlobal; + } + + StringRef name() const override { return _name; } + + uint64_t value() const override { return _value; } + +private: + const ELFFile<ELFT> &_owningFile; + StringRef _name; + const Elf_Sym *_symbol; + uint64_t _value; +}; + +/// \brief ELFUndefinedAtom: These atoms store undefined symbols and are place +/// holders that will be replaced by defined atoms later in the linking process. +template <class ELFT> class ELFUndefinedAtom : public lld::UndefinedAtom { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + +public: + ELFUndefinedAtom(const File &file, StringRef name, const Elf_Sym *symbol) + : _owningFile(file), _name(name), _symbol(symbol) {} + + const File &file() const override { return _owningFile; } + + StringRef name() const override { return _name; } + + // A symbol in ELF can be undefined at build time if the symbol is a undefined + // weak symbol. + CanBeNull canBeNull() const override { + if (_symbol->getBinding() == llvm::ELF::STB_WEAK) + return CanBeNull::canBeNullAtBuildtime; + return CanBeNull::canBeNullNever; + } + +private: + const File &_owningFile; + StringRef _name; + const Elf_Sym *_symbol; +}; + +/// \brief This atom stores defined symbols and will contain either data or +/// code. +template <class ELFT> class ELFDefinedAtom : public DefinedAtom { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + +public: + ELFDefinedAtom(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) + : _owningFile(file), _symbolName(symbolName), _sectionName(sectionName), + _symbol(symbol), _section(section), _contentData(contentData), + _referenceStartIndex(referenceStart), _referenceEndIndex(referenceEnd), + _referenceList(referenceList), _contentType(typeUnknown), + _permissions(permUnknown) {} + + ~ELFDefinedAtom() {} + + const ELFFile<ELFT> &file() const override { return _owningFile; } + + StringRef name() const override { return _symbolName; } + + uint64_t ordinal() const override { return _ordinal; } + + const Elf_Sym *symbol() const { return _symbol; } + + const Elf_Shdr *section() const { return _section; } + + uint64_t size() const override { + // Common symbols are not allocated in object files, + // so use st_size to tell how many bytes are required. + if (_symbol && (_symbol->getType() == llvm::ELF::STT_COMMON || + _symbol->st_shndx == llvm::ELF::SHN_COMMON)) + return (uint64_t) _symbol->st_size; + + return _contentData.size(); + } + + Scope scope() const override { + if (!_symbol) + return scopeGlobal; + if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN) + return scopeLinkageUnit; + if (_symbol->getBinding() != llvm::ELF::STB_LOCAL) + return scopeGlobal; + return scopeTranslationUnit; + } + + // FIXME: Need to revisit this in future. + Interposable interposable() const override { return interposeNo; } + + Merge merge() const override { + if (!_symbol) + return mergeNo; + + if (_symbol->getBinding() == llvm::ELF::STB_WEAK) + return mergeAsWeak; + + if ((_symbol->getType() == llvm::ELF::STT_COMMON) || + _symbol->st_shndx == llvm::ELF::SHN_COMMON) + return mergeAsTentative; + + return mergeNo; + } + + ContentType contentType() const override { + if (_contentType != typeUnknown) + return _contentType; + + ContentType ret = typeUnknown; + uint64_t flags = _section->sh_flags; + + if (_section->sh_type == llvm::ELF::SHT_GROUP) + return typeGroupComdat; + + if (!_symbol && _sectionName.startswith(".gnu.linkonce")) + return typeGnuLinkOnce; + + if (!(flags & llvm::ELF::SHF_ALLOC)) + return _contentType = typeNoAlloc; + + if (_section->sh_flags == + (llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE | llvm::ELF::SHF_TLS)) { + return _contentType = _section->sh_type == llvm::ELF::SHT_NOBITS ? typeThreadZeroFill + : typeThreadData; + } + + if ((_section->sh_flags == llvm::ELF::SHF_ALLOC) && + (_section->sh_type == llvm::ELF::SHT_PROGBITS)) + return _contentType = typeConstant; + + if (_symbol->getType() == llvm::ELF::STT_GNU_IFUNC) + return _contentType = typeResolver; + + if (_symbol->st_shndx == llvm::ELF::SHN_COMMON) + return _contentType = typeZeroFill; + + switch (_section->sh_type) { + case llvm::ELF::SHT_PROGBITS: + flags &= ~llvm::ELF::SHF_ALLOC; + flags &= ~llvm::ELF::SHF_GROUP; + switch (flags) { + case llvm::ELF::SHF_EXECINSTR: + case (llvm::ELF::SHF_WRITE|llvm::ELF::SHF_EXECINSTR): + ret = typeCode; + break; + case llvm::ELF::SHF_WRITE: + ret = typeData; + break; + case (llvm::ELF::SHF_MERGE|llvm::ELF::SHF_STRINGS): + case llvm::ELF::SHF_STRINGS: + case llvm::ELF::SHF_MERGE: + ret = typeConstant; + break; + default: + ret = typeCode; + break; + } + break; + case llvm::ELF::SHT_NOTE: + flags &= ~llvm::ELF::SHF_ALLOC; + switch (flags) { + case llvm::ELF::SHF_WRITE: + ret = typeRWNote; + break; + default: + ret = typeRONote; + break; + } + break; + case llvm::ELF::SHT_NOBITS: + ret = typeZeroFill; + break; + case llvm::ELF::SHT_NULL: + if ((_symbol->getType() == llvm::ELF::STT_COMMON) + || _symbol->st_shndx == llvm::ELF::SHN_COMMON) + ret = typeZeroFill; + break; + case llvm::ELF::SHT_INIT_ARRAY: + case llvm::ELF::SHT_FINI_ARRAY: + ret = typeData; + break; + } + + return _contentType = ret; + } + + Alignment alignment() const override { + if (!_symbol) + return Alignment(0); + + // Obtain proper value of st_value field. + const auto symValue = getSymbolValue(_symbol); + + // Unallocated common symbols specify their alignment constraints in + // st_value. + if ((_symbol->getType() == llvm::ELF::STT_COMMON) || + _symbol->st_shndx == llvm::ELF::SHN_COMMON) { + return Alignment(llvm::Log2_64(symValue)); + } + if (_section->sh_addralign == 0) { + // sh_addralign of 0 means no alignment + return Alignment(0, symValue); + } + return Alignment(llvm::Log2_64(_section->sh_addralign), + symValue % _section->sh_addralign); + } + + // Do we have a choice for ELF? All symbols live in explicit sections. + SectionChoice sectionChoice() const override { + switch (contentType()) { + case typeCode: + case typeData: + case typeZeroFill: + case typeThreadZeroFill: + case typeThreadData: + case typeConstant: + if ((_sectionName == ".text") || (_sectionName == ".data") || + (_sectionName == ".bss") || (_sectionName == ".rodata") || + (_sectionName == ".tdata") || (_sectionName == ".tbss")) + return sectionBasedOnContent; + default: + break; + } + return sectionCustomRequired; + } + + StringRef customSectionName() const override { + if ((contentType() == typeZeroFill) || + (_symbol && _symbol->st_shndx == llvm::ELF::SHN_COMMON)) + return ".bss"; + return _sectionName; + } + + // It isn't clear that __attribute__((used)) is transmitted to the ELF object + // file. + DeadStripKind deadStrip() const override { return deadStripNormal; } + + ContentPermissions permissions() const override { + if (_permissions != permUnknown) + return _permissions; + + uint64_t flags = _section->sh_flags; + + if (!(flags & llvm::ELF::SHF_ALLOC)) + return _permissions = perm___; + + switch (_section->sh_type) { + // permRW_L is for sections modified by the runtime + // loader. + case llvm::ELF::SHT_REL: + case llvm::ELF::SHT_RELA: + return _permissions = permRW_L; + + case llvm::ELF::SHT_DYNAMIC: + case llvm::ELF::SHT_PROGBITS: + case llvm::ELF::SHT_NOTE: + flags &= ~llvm::ELF::SHF_ALLOC; + flags &= ~llvm::ELF::SHF_GROUP; + switch (flags) { + // Code + case llvm::ELF::SHF_EXECINSTR: + return _permissions = permR_X; + case (llvm::ELF::SHF_WRITE|llvm::ELF::SHF_EXECINSTR): + return _permissions = permRWX; + // Data + case llvm::ELF::SHF_WRITE: + return _permissions = permRW_; + // Strings + case llvm::ELF::SHF_MERGE: + case llvm::ELF::SHF_STRINGS: + return _permissions = permR__; + + default: + if (flags & llvm::ELF::SHF_WRITE) + return _permissions = permRW_; + return _permissions = permR__; + } + + case llvm::ELF::SHT_NOBITS: + return _permissions = permRW_; + + case llvm::ELF::SHT_INIT_ARRAY: + case llvm::ELF::SHT_FINI_ARRAY: + return _permissions = permRW_; + + default: + return _permissions = perm___; + } + } + + ArrayRef<uint8_t> rawContent() const override { return _contentData; } + + DefinedAtom::reference_iterator begin() const override { + uintptr_t index = _referenceStartIndex; + const void *it = reinterpret_cast<const void*>(index); + return reference_iterator(*this, it); + } + + DefinedAtom::reference_iterator end() const override { + uintptr_t index = _referenceEndIndex; + const void *it = reinterpret_cast<const void*>(index); + return reference_iterator(*this, it); + } + + const Reference *derefIterator(const void *It) const override { + uintptr_t index = reinterpret_cast<uintptr_t>(It); + assert(index >= _referenceStartIndex); + assert(index < _referenceEndIndex); + return ((_referenceList)[index]); + } + + void incrementIterator(const void *&It) const override { + uintptr_t index = reinterpret_cast<uintptr_t>(It); + ++index; + It = reinterpret_cast<const void *>(index); + } + + void addReference(ELFReference<ELFT> *reference) { + _referenceList.push_back(reference); + _referenceEndIndex = _referenceList.size(); + } + + virtual void setOrdinal(uint64_t ord) { _ordinal = ord; } + +protected: + /// Returns correct st_value for the symbol depending on the architecture. + /// For most architectures it's just a regular st_value with no changes. + virtual uint64_t getSymbolValue(const Elf_Sym *symbol) const { + return symbol->st_value; + } + +protected: + const ELFFile<ELFT> &_owningFile; + StringRef _symbolName; + StringRef _sectionName; + const Elf_Sym *_symbol; + const Elf_Shdr *_section; + /// \brief Holds the bits that make up the atom. + ArrayRef<uint8_t> _contentData; + + uint64_t _ordinal; + unsigned int _referenceStartIndex; + unsigned int _referenceEndIndex; + std::vector<ELFReference<ELFT> *> &_referenceList; + mutable ContentType _contentType; + mutable ContentPermissions _permissions; +}; + +/// \brief This atom stores mergeable Strings +template <class ELFT> class ELFMergeAtom : public DefinedAtom { + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + +public: + ELFMergeAtom(const ELFFile<ELFT> &file, StringRef sectionName, + const Elf_Shdr *section, ArrayRef<uint8_t> contentData, + uint64_t offset) + : _owningFile(file), _sectionName(sectionName), _section(section), + _contentData(contentData), _offset(offset) { + } + + const ELFFile<ELFT> &file() const override { return _owningFile; } + + StringRef name() const override { return ""; } + + virtual uint64_t section() const { return _section->sh_name; } + + virtual uint64_t offset() const { return _offset; } + + virtual void setOrdinal(uint64_t ord) { _ordinal = ord; } + + uint64_t ordinal() const override { return _ordinal; } + + uint64_t size() const override { return _contentData.size(); } + + Scope scope() const override { return scopeTranslationUnit; } + + Interposable interposable() const override { return interposeNo; } + + Merge merge() const override { return mergeByContent; } + + ContentType contentType() const override { return typeConstant; } + + Alignment alignment() const override { + return Alignment(llvm::Log2_64(_section->sh_addralign)); + } + + SectionChoice sectionChoice() const override { return sectionCustomRequired; } + + StringRef customSectionName() const override { return _sectionName; } + + DeadStripKind deadStrip() const override { return deadStripNormal; } + + ContentPermissions permissions() const override { return permR__; } + + ArrayRef<uint8_t> rawContent() const override { return _contentData; } + + DefinedAtom::reference_iterator begin() const override { + uintptr_t index = 0; + const void *it = reinterpret_cast<const void *>(index); + return reference_iterator(*this, it); + } + + DefinedAtom::reference_iterator end() const override { + uintptr_t index = 0; + const void *it = reinterpret_cast<const void *>(index); + return reference_iterator(*this, it); + } + + const Reference *derefIterator(const void *It) const override { + return nullptr; + } + + void incrementIterator(const void *&It) const override {} + +private: + + const ELFFile<ELFT> &_owningFile; + StringRef _sectionName; + const Elf_Shdr *_section; + /// \brief Holds the bits that make up the atom. + ArrayRef<uint8_t> _contentData; + uint64_t _ordinal; + uint64_t _offset; +}; + +template <class ELFT> class ELFCommonAtom : public DefinedAtom { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; +public: + ELFCommonAtom(const ELFFile<ELFT> &file, + StringRef symbolName, + const Elf_Sym *symbol) + : _owningFile(file), + _symbolName(symbolName), + _symbol(symbol) {} + + const ELFFile<ELFT> &file() const override { return _owningFile; } + + StringRef name() const override { return _symbolName; } + + uint64_t ordinal() const override { return _ordinal; } + + virtual void setOrdinal(uint64_t ord) { _ordinal = ord; } + + uint64_t size() const override { return _symbol->st_size; } + + Scope scope() const override { + if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN) + return scopeLinkageUnit; + if (_symbol->getBinding() != llvm::ELF::STB_LOCAL) + return scopeGlobal; + return scopeTranslationUnit; + } + + Interposable interposable() const override { return interposeNo; } + + Merge merge() const override { return mergeAsTentative; } + + ContentType contentType() const override { return typeZeroFill; } + + Alignment alignment() const override { + return Alignment(llvm::Log2_64(_symbol->st_value)); + } + + SectionChoice sectionChoice() const override { return sectionBasedOnContent; } + + StringRef customSectionName() const override { return ".bss"; } + + DeadStripKind deadStrip() const override { return deadStripNormal; } + + ContentPermissions permissions() const override { return permRW_; } + + ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } + + DefinedAtom::reference_iterator begin() const override { + uintptr_t index = 0; + const void *it = reinterpret_cast<const void *>(index); + return reference_iterator(*this, it); + } + + DefinedAtom::reference_iterator end() const override { + uintptr_t index = 0; + const void *it = reinterpret_cast<const void *>(index); + return reference_iterator(*this, it); + } + +protected: + const Reference *derefIterator(const void *iter) const override { + return nullptr; + } + + void incrementIterator(const void *&iter) const override {} + + const ELFFile<ELFT> &_owningFile; + StringRef _symbolName; + const Elf_Sym *_symbol; + uint64_t _ordinal; +}; + +/// \brief An atom from a shared library. +template <class ELFT> class ELFDynamicAtom : public SharedLibraryAtom { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + +public: + ELFDynamicAtom(const DynamicFile<ELFT> &file, StringRef symbolName, + StringRef loadName, const Elf_Sym *symbol) + : _owningFile(file), _symbolName(symbolName), _loadName(loadName), + _symbol(symbol) { + } + + const DynamicFile<ELFT> &file() const override { return _owningFile; } + + StringRef name() const override { return _symbolName; } + + virtual Scope scope() const { + if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN) + return scopeLinkageUnit; + if (_symbol->getBinding() != llvm::ELF::STB_LOCAL) + return scopeGlobal; + return scopeTranslationUnit; + } + + StringRef loadName() const override { return _loadName; } + + bool canBeNullAtRuntime() const override { + return _symbol->getBinding() == llvm::ELF::STB_WEAK; + } + + Type type() const override { + switch (_symbol->getType()) { + case llvm::ELF::STT_FUNC: + case llvm::ELF::STT_GNU_IFUNC: + return Type::Code; + case llvm::ELF::STT_OBJECT: + return Type::Data; + default: + return Type::Unknown; + } + } + + uint64_t size() const override { + return _symbol->st_size; + } + +private: + + const DynamicFile<ELFT> &_owningFile; + StringRef _symbolName; + StringRef _loadName; + const Elf_Sym *_symbol; +}; + +class SimpleELFDefinedAtom : public SimpleDefinedAtom { +public: + SimpleELFDefinedAtom(const File &f) : SimpleDefinedAtom(f) {} + + void addReferenceELF(Reference::KindArch arch, Reference::KindValue kindValue, + uint64_t off, const Atom *target, + Reference::Addend addend) { + this->addReference(Reference::KindNamespace::ELF, arch, kindValue, off, + target, addend); + } + + void addReferenceELF_Hexagon(Reference::KindValue relocType, uint64_t off, + const Atom *t, Reference::Addend a) { + this->addReferenceELF(Reference::KindArch::Hexagon, relocType, off, t, a); + } + + void addReferenceELF_x86_64(Reference::KindValue relocType, uint64_t off, + const Atom *t, Reference::Addend a) { + this->addReferenceELF(Reference::KindArch::x86_64, relocType, off, t, a); + } + + void addReferenceELF_Mips(Reference::KindValue relocType, uint64_t off, + const Atom *t, Reference::Addend a) { + this->addReferenceELF(Reference::KindArch::Mips, relocType, off, t, a); + } + + void addReferenceELF_AArch64(Reference::KindValue relocType, uint64_t off, + const Atom *t, Reference::Addend a) { + this->addReferenceELF(Reference::KindArch::AArch64, relocType, off, t, a); + } + + void addReferenceELF_ARM(Reference::KindValue relocType, uint64_t off, + const Atom *t, Reference::Addend a) { + this->addReferenceELF(Reference::KindArch::ARM, relocType, off, t, a); + } +}; + +/// \brief Atom which represents an object for which a COPY relocation will be +/// generated. +class ObjectAtom : public SimpleELFDefinedAtom { +public: + ObjectAtom(const File &f) : SimpleELFDefinedAtom(f) {} + + Scope scope() const override { return scopeGlobal; } + + SectionChoice sectionChoice() const override { return sectionBasedOnContent; } + + ContentType contentType() const override { return typeZeroFill; } + + uint64_t size() const override { return _size; } + + DynamicExport dynamicExport() const override { return dynamicExportAlways; } + + ContentPermissions permissions() const override { return permRW_; } + + ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } + + Alignment alignment() const override { + // The alignment should be 8 byte aligned + return Alignment(3); + } + + StringRef name() const override { return _name; } + + std::string _name; + uint64_t _size; +}; + +class GOTAtom : public SimpleELFDefinedAtom { + StringRef _section; + +public: + GOTAtom(const File &f, StringRef secName) + : SimpleELFDefinedAtom(f), _section(secName) {} + + Scope scope() const override { return scopeTranslationUnit; } + + SectionChoice sectionChoice() const override { return sectionCustomRequired; } + + StringRef customSectionName() const override { return _section; } + + ContentType contentType() const override { return typeGOT; } + + uint64_t size() const override { return rawContent().size(); } + + ContentPermissions permissions() const override { return permRW_; } + + Alignment alignment() const override { + // The alignment should be 8 byte aligned + return Alignment(3); + } + +#ifndef NDEBUG + StringRef name() const override { return _name; } + std::string _name; +#else + StringRef name() const override { return ""; } +#endif +}; + +class PLTAtom : public SimpleELFDefinedAtom { + StringRef _section; + +public: + PLTAtom(const File &f, StringRef secName) + : SimpleELFDefinedAtom(f), _section(secName) {} + + Scope scope() const override { return scopeTranslationUnit; } + + SectionChoice sectionChoice() const override { return sectionCustomRequired; } + + StringRef customSectionName() const override { return _section; } + + ContentType contentType() const override { return typeStub; } + + uint64_t size() const override { return rawContent().size(); } + + ContentPermissions permissions() const override { return permR_X; } + + Alignment alignment() const override { + return Alignment(4); // 16 + } + +#ifndef NDEBUG + StringRef name() const override { return _name; } + std::string _name; +#else + StringRef name() const override { return ""; } +#endif +}; + +class PLT0Atom : public PLTAtom { +public: + PLT0Atom(const File &f) : PLTAtom(f, ".plt") { +#ifndef NDEBUG + _name = ".PLT0"; +#endif + } +}; + +class GLOBAL_OFFSET_TABLEAtom : public SimpleELFDefinedAtom { +public: + GLOBAL_OFFSET_TABLEAtom(const File &f) : SimpleELFDefinedAtom(f) {} + + StringRef name() const override { return "_GLOBAL_OFFSET_TABLE_"; } + + Scope scope() const override { return scopeLinkageUnit; } + + SectionChoice sectionChoice() const override { return sectionCustomRequired; } + + StringRef customSectionName() const override { return ".got.plt"; } + + ContentType contentType() const override { return typeGOT; } + + uint64_t size() const override { return 0; } + + ContentPermissions permissions() const override { return permRW_; } + + Alignment alignment() const override { + // Needs 8 byte alignment + return Alignment(3); + } + + ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } +}; + +class DYNAMICAtom : public SimpleELFDefinedAtom { +public: + DYNAMICAtom(const File &f) : SimpleELFDefinedAtom(f) {} + + StringRef name() const override { return "_DYNAMIC"; } + + Scope scope() const override { return scopeLinkageUnit; } + + Merge merge() const override { return mergeNo; } + + SectionChoice sectionChoice() const override { return sectionCustomRequired; } + + StringRef customSectionName() const override { return ".dynamic"; } + + ContentType contentType() const override { return typeData; } + + uint64_t size() const override { return 0; } + + ContentPermissions permissions() const override { return permRW_; } + + Alignment alignment() const override { return Alignment(0); } + + ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/CMakeLists.txt b/lib/ReaderWriter/ELF/CMakeLists.txt new file mode 100644 index 0000000000000..fd4cb669904d9 --- /dev/null +++ b/lib/ReaderWriter/ELF/CMakeLists.txt @@ -0,0 +1,19 @@ +add_llvm_library(lldELF + ELFLinkingContext.cpp + Reader.cpp + Writer.cpp + LINK_LIBS + lldReaderWriter + lldCore + lldYAML + LLVMSupport + ) + +include_directories(.) + +add_subdirectory(X86) +add_subdirectory(X86_64) +add_subdirectory(Mips) +add_subdirectory(Hexagon) +add_subdirectory(AArch64) +add_subdirectory(ARM) diff --git a/lib/ReaderWriter/ELF/Chunk.h b/lib/ReaderWriter/ELF/Chunk.h new file mode 100644 index 0000000000000..2658d023b3a9b --- /dev/null +++ b/lib/ReaderWriter/ELF/Chunk.h @@ -0,0 +1,102 @@ +//===- lib/ReaderWriter/ELF/Chunks.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_CHUNKS_H +#define LLD_READER_WRITER_ELF_CHUNKS_H + +#include "lld/Core/LLVM.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileOutputBuffer.h" +#include <memory> + +namespace lld { +class ELFLinkingContext; + +namespace elf { +class ELFWriter; + +template <class ELFT> class TargetLayout; + +/// \brief A chunk is a contiguous region of space +template<class ELFT> +class Chunk { +public: + + /// \brief Describes the type of Chunk + enum Kind : uint8_t{ ELFHeader, ///< ELF Header + ProgramHeader, ///< Program Header + SectionHeader, ///< Section header + ELFSegment, ///< Segment + ELFSection, ///< Section + AtomSection, ///< A section containing atoms. + Expression ///< A linker script expression + }; + /// \brief the ContentType of the chunk + enum ContentType : uint8_t{ Unknown, Header, Code, Data, Note, TLS }; + + Chunk(StringRef name, Kind kind, const ELFLinkingContext &context) + : _name(name), _kind(kind), _fsize(0), _msize(0), _alignment(0), _order(0), + _ordinal(1), _start(0), _fileoffset(0), _context(context) {} + virtual ~Chunk() {} + // The name of the chunk + StringRef name() const { return _name; } + // Kind of chunk + Kind kind() const { return _kind; } + virtual uint64_t fileSize() const { return _fsize; } + virtual void setFileSize(uint64_t sz) { _fsize = sz; } + virtual void setAlign(uint64_t align) { _alignment = align; } + virtual uint64_t alignment() const { return _alignment; } + + // The ordinal value of the chunk + uint64_t ordinal() const { return _ordinal;} + void setOrdinal(uint64_t newVal) { _ordinal = newVal;} + // The order in which the chunk would appear in the output file + uint64_t order() const { return _order; } + void setOrder(uint32_t order) { _order = order; } + // Output file offset of the chunk + uint64_t fileOffset() const { return _fileoffset; } + void setFileOffset(uint64_t offset) { _fileoffset = offset; } + // Output start address of the chunk + virtual void setVirtualAddr(uint64_t start) { _start = start; } + virtual uint64_t virtualAddr() const { return _start; } + // Memory size of the chunk + uint64_t memSize() const { return _msize; } + void setMemSize(uint64_t msize) { _msize = msize; } + // Whats the contentType of the chunk? + virtual int getContentType() const = 0; + // Writer the chunk + virtual void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) = 0; + // Finalize the chunk before assigning offsets/virtual addresses + virtual void doPreFlight() = 0; + // Finalize the chunk before writing + virtual void finalize() = 0; + +protected: + StringRef _name; + Kind _kind; + uint64_t _fsize; + uint64_t _msize; + uint64_t _alignment; + uint32_t _order; + uint64_t _ordinal; + uint64_t _start; + uint64_t _fileoffset; + const ELFLinkingContext &_context; +}; + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/CreateELF.h b/lib/ReaderWriter/ELF/CreateELF.h new file mode 100644 index 0000000000000..ad34dddb24d3d --- /dev/null +++ b/lib/ReaderWriter/ELF/CreateELF.h @@ -0,0 +1,118 @@ +//===- lib/ReaderWriter/ELF/CreateELF.h -----------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file provides a simple way to create an object templated on +/// ELFType depending on the runtime type needed. +/// +//===----------------------------------------------------------------------===// +#ifndef LLD_READER_WRITER_ELF_CREATE_ELF_H +#define LLD_READER_WRITER_ELF_CREATE_ELF_H + +#include "llvm/Object/ELF.h" +#include "llvm/Support/Compiler.h" + +namespace { +using llvm::object::ELFType; + +/// \func createELF +/// \brief Create an object depending on the runtime attributes and alignment +/// of an ELF file. +/// +/// \param Traits +/// Traits::result_type must be a type convertable from what create returns. +/// Traits::create must be a template function which takes an ELFType and +/// returns something convertable to Traits::result_type. +/// +/// \param ident pair of EI_CLASS and EI_DATA. +/// \param maxAlignment the maximum alignment of the file. +/// \param args arguments forwarded to CreateELFTraits<T>::create. + +#define LLVM_CREATE_ELF_CreateELFTraits(endian, align, is64, ...) \ + Traits::template create<ELFType<llvm::support::endian, align, is64>>( \ + __VA_ARGS__); + +#if !LLVM_IS_UNALIGNED_ACCESS_FAST +# define LLVM_CREATE_ELF_MaxAlignCheck(normal, low, endian, is64, ...) \ + if (maxAlignment >= normal) \ + return LLVM_CREATE_ELF_CreateELFTraits(endian, normal, is64, __VA_ARGS__) \ + else if (maxAlignment >= low) \ + return LLVM_CREATE_ELF_CreateELFTraits(endian, low, is64, __VA_ARGS__) \ + else \ + llvm_unreachable("Invalid alignment for ELF file!"); +#else +# define LLVM_CREATE_ELF_MaxAlignCheck(normal, low, endian, is64, ...) \ + if (maxAlignment >= low) \ + return LLVM_CREATE_ELF_CreateELFTraits(endian, low, is64, __VA_ARGS__) \ + else \ + llvm_unreachable("Invalid alignment for ELF file!"); +#endif + +#define LLVM_CREATE_ELF_IMPL(...) \ + if (ident.first == llvm::ELF::ELFCLASS32 && \ + ident.second == llvm::ELF::ELFDATA2LSB) { \ + LLVM_CREATE_ELF_MaxAlignCheck(4, 2, little, false, __VA_ARGS__) \ + } else if (ident.first == llvm::ELF::ELFCLASS32 && \ + ident.second == llvm::ELF::ELFDATA2MSB) { \ + LLVM_CREATE_ELF_MaxAlignCheck(4, 2, big, false, __VA_ARGS__) \ + } else if (ident.first == llvm::ELF::ELFCLASS64 && \ + ident.second == llvm::ELF::ELFDATA2MSB) { \ + LLVM_CREATE_ELF_MaxAlignCheck(8, 2, big, true, __VA_ARGS__) \ + } else if (ident.first == llvm::ELF::ELFCLASS64 && \ + ident.second == llvm::ELF::ELFDATA2LSB) { \ + LLVM_CREATE_ELF_MaxAlignCheck(8, 2, little, true, __VA_ARGS__) \ + } \ + llvm_unreachable("Invalid ELF type!"); + +#if LLVM_HAS_VARIADIC_TEMPLATES +template <class Traits, class ...Args> +typename Traits::result_type createELF( + std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment, + Args &&...args) { + LLVM_CREATE_ELF_IMPL(std::forward<Args>(args)...) +} +#else +template <class Traits, class T1> +typename Traits::result_type createELF( + std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment, + T1 &&t1) { + LLVM_CREATE_ELF_IMPL(std::forward<T1>(t1)) +} + +template <class Traits, class T1, class T2> +typename Traits::result_type createELF( + std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment, + T1 &&t1, T2 &&t2) { + LLVM_CREATE_ELF_IMPL(std::forward<T1>(t1), std::forward<T2>(t2)) +} + +template <class Traits, class T1, class T2, class T3> +typename Traits::result_type createELF( + std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment, + T1 &&t1, T2 &&t2, T3 &&t3) { + LLVM_CREATE_ELF_IMPL(std::forward<T1>(t1), std::forward<T2>(t2), + std::forward<T3>(t3)) +} + +template <class Traits, class T1, class T2, class T3, class T4> +typename Traits::result_type createELF( + std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment, + T1 &&t1, T2 &&t2, T3 &&t3, T4 &&t4) { + LLVM_CREATE_ELF_IMPL(std::forward<T1>(t1), std::forward<T2>(t2), + std::forward<T3>(t3), std::forward<T4>(t4)) +} + +#endif // LLVM_HAS_VARIADIC_TEMPLATES +} // end anon namespace + +#undef LLVM_CREATE_ELF_CreateELFTraits +#undef LLVM_CREATE_ELF_MaxAlignCheck +#undef LLVM_CREATE_ELF_IMPL + +#endif diff --git a/lib/ReaderWriter/ELF/DefaultLayout.h b/lib/ReaderWriter/ELF/DefaultLayout.h new file mode 100644 index 0000000000000..9af3b8eb8dc63 --- /dev/null +++ b/lib/ReaderWriter/ELF/DefaultLayout.h @@ -0,0 +1,1050 @@ +//===- lib/ReaderWriter/ELF/DefaultLayout.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_DEFAULT_LAYOUT_H +#define LLD_READER_WRITER_ELF_DEFAULT_LAYOUT_H + +#include "Atoms.h" +#include "Chunk.h" +#include "HeaderChunks.h" +#include "Layout.h" +#include "SectionChunks.h" +#include "SegmentChunks.h" +#include "lld/Core/Instrumentation.h" +#include "lld/Core/STDExtras.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Format.h" +#include <map> +#include <unordered_map> + +namespace lld { +namespace elf { +/// \brief The DefaultLayout class is used by the Writer to arrange +/// sections and segments in the order determined by the target ELF +/// format. The writer creates a single instance of the DefaultLayout +/// class +template<class ELFT> +class DefaultLayout : public Layout { +public: + + // The order in which the sections appear in the output file + // If its determined, that the layout needs to change + // just changing the order of enumerations would essentially + // change the layout in the output file + // Change the enumerations so that Target can override and stick + // a section anywhere it wants to + enum DefaultSectionOrder { + ORDER_NOT_DEFINED = 0, + ORDER_INTERP = 10, + ORDER_RO_NOTE = 15, + ORDER_HASH = 30, + ORDER_DYNAMIC_SYMBOLS = 40, + ORDER_DYNAMIC_STRINGS = 50, + ORDER_DYNAMIC_RELOCS = 52, + ORDER_DYNAMIC_PLT_RELOCS = 54, + ORDER_INIT = 60, + ORDER_PLT = 70, + ORDER_TEXT = 80, + ORDER_FINI = 90, + ORDER_REL = 95, + ORDER_RODATA = 100, + ORDER_EH_FRAME = 110, + ORDER_EH_FRAMEHDR = 120, + ORDER_TDATA = 124, + ORDER_TBSS = 128, + ORDER_CTORS = 130, + ORDER_DTORS = 140, + ORDER_INIT_ARRAY = 150, + ORDER_FINI_ARRAY = 160, + ORDER_DYNAMIC = 170, + ORDER_GOT = 180, + ORDER_GOT_PLT = 190, + ORDER_DATA = 200, + ORDER_RW_NOTE = 205, + ORDER_BSS = 210, + ORDER_NOALLOC = 215, + ORDER_OTHER = 220, + ORDER_SECTION_STRINGS = 230, + ORDER_SYMBOL_TABLE = 240, + ORDER_STRING_TABLE = 250, + ORDER_SECTION_HEADERS = 260 + }; + +public: + + // The Key used for creating Sections + // The sections are created using + // SectionName, contentPermissions + struct SectionKey { + SectionKey(StringRef name, DefinedAtom::ContentPermissions perm, + StringRef path) + : _name(name), _perm(perm), _path(path) {} + + // Data members + StringRef _name; + DefinedAtom::ContentPermissions _perm; + StringRef _path; + }; + + struct SectionKeyHash { + int64_t operator()(const SectionKey &k) const { + return llvm::hash_combine(k._name, k._perm, k._path); + } + }; + + struct SectionKeyEq { + bool operator()(const SectionKey &lhs, const SectionKey &rhs) const { + return ((lhs._name == rhs._name) && (lhs._perm == rhs._perm) && + (lhs._path == rhs._path)); + } + }; + + typedef typename std::vector<Chunk<ELFT> *>::iterator ChunkIter; + typedef typename std::vector<Segment<ELFT> *>::iterator SegmentIter; + + // The additional segments are used to figure out + // if there is a segment by that type already created + // For example : PT_TLS, we have two sections .tdata/.tbss + // that are part of PT_TLS, we need to create this additional + // segment only once + typedef std::pair<int64_t, int64_t> AdditionalSegmentKey; + // The segments are created using + // SegmentName, Segment flags + typedef std::pair<StringRef, int64_t> SegmentKey; + + // HashKey for the Segment + class SegmentHashKey { + public: + int64_t operator() (const SegmentKey &k) const { + // k.first = SegmentName + // k.second = SegmentFlags + return llvm::hash_combine(k.first, k.second); + } + }; + + class AdditionalSegmentHashKey { + public: + int64_t operator()(const AdditionalSegmentKey &k) const { + // k.first = SegmentName + // k.second = SegmentFlags + return llvm::hash_combine(k.first, k.second); + } + }; + + // Output Sections contain the map of Sectionnames to a vector of sections, + // that have been merged to form a single section + typedef llvm::StringMap<OutputSection<ELFT> *> OutputSectionMapT; + typedef + typename std::vector<OutputSection<ELFT> *>::iterator OutputSectionIter; + + typedef std::unordered_map<SectionKey, AtomSection<ELFT> *, SectionKeyHash, + SectionKeyEq> SectionMapT; + typedef std::unordered_map<AdditionalSegmentKey, Segment<ELFT> *, + AdditionalSegmentHashKey> AdditionalSegmentMapT; + typedef std::unordered_map<SegmentKey, Segment<ELFT> *, SegmentHashKey> + SegmentMapT; + + /// \brief find a absolute atom pair given a absolute atom name + struct FindByName { + const std::string _name; + FindByName(StringRef name) : _name(name) {} + bool operator()(const lld::AtomLayout *j) { return j->_atom->name() == _name; } + }; + + typedef typename std::vector<lld::AtomLayout *>::iterator AbsoluteAtomIterT; + + typedef llvm::DenseSet<const Atom *> AtomSetT; + + DefaultLayout(ELFLinkingContext &context) + : _context(context), _linkerScriptSema(context.linkerScriptSema()) {} + + /// \brief Return the section order for a input section + SectionOrder getSectionOrder(StringRef name, int32_t contentType, + int32_t contentPermissions) override; + + /// \brief Return the name of the input section by decoding the input + /// sectionChoice. + virtual StringRef getInputSectionName(const DefinedAtom *da) const; + + /// \brief Return the name of the output section from the input section. + virtual StringRef getOutputSectionName(StringRef archivePath, + StringRef memberPath, + StringRef inputSectionName) const; + + /// \brief Gets or creates a section. + AtomSection<ELFT> * + getSection(StringRef name, int32_t contentType, + DefinedAtom::ContentPermissions contentPermissions, + const DefinedAtom *da); + + /// \brief Gets the segment for a output section + virtual Layout::SegmentType getSegmentType(Section<ELFT> *section) const; + + /// \brief Returns true/false depending on whether the section has a Output + // segment or not + static bool hasOutputSegment(Section<ELFT> *section); + + // Adds an atom to the section + ErrorOr<const lld::AtomLayout *> addAtom(const Atom *atom) override; + + /// \brief Find an output Section given a section name. + OutputSection<ELFT> *findOutputSection(StringRef name) { + auto iter = _outputSectionMap.find(name); + if (iter == _outputSectionMap.end()) + return nullptr; + return iter->second; + } + + /// \brief find a absolute atom given a name + AbsoluteAtomIterT findAbsoluteAtom(StringRef name) { + return std::find_if(_absoluteAtoms.begin(), _absoluteAtoms.end(), + FindByName(name)); + } + + // Output sections with the same name into a OutputSection + void createOutputSections(); + + /// \brief Sort the sections by their order as defined by the layout, + /// preparing all sections to be assigned to a segment. + virtual void sortInputSections(); + + /// \brief Add extra chunks to a segment just before including the input + /// section given by <archivePath, memberPath, sectionName>. This + /// is used to add linker script expressions before each section. + virtual void addExtraChunksToSegment(Segment<ELFT> *segment, + StringRef archivePath, + StringRef memberPath, + StringRef sectionName); + + void assignSectionsToSegments() override; + + void assignVirtualAddress() override; + + void assignFileOffsetsForMiscSections(); + + range<AbsoluteAtomIterT> absoluteAtoms() { return _absoluteAtoms; } + + void addSection(Chunk<ELFT> *c) { _sections.push_back(c); } + + void finalize() { + ScopedTask task(getDefaultDomain(), "Finalize layout"); + for (auto &si : _sections) + si->finalize(); + } + + void doPreFlight() { + for (auto &si : _sections) + si->doPreFlight(); + } + + const AtomLayout *findAtomLayoutByName(StringRef name) const override { + for (auto sec : _sections) + if (auto section = dyn_cast<Section<ELFT>>(sec)) + if (auto *al = section->findAtomLayoutByName(name)) + return al; + return nullptr; + } + + void setHeader(ELFHeader<ELFT> *elfHeader) { _elfHeader = elfHeader; } + + void setProgramHeader(ProgramHeader<ELFT> *p) { + _programHeader = p; + } + + range<OutputSectionIter> outputSections() { return _outputSections; } + + range<ChunkIter> sections() { return _sections; } + + range<SegmentIter> segments() { return _segments; } + + ELFHeader<ELFT> *getHeader() { return _elfHeader; } + + bool hasDynamicRelocationTable() const { return !!_dynamicRelocationTable; } + + bool hasPLTRelocationTable() const { return !!_pltRelocationTable; } + + /// \brief Get or create the dynamic relocation table. All relocations in this + /// table are processed at startup. + RelocationTable<ELFT> *getDynamicRelocationTable() { + if (!_dynamicRelocationTable) { + _dynamicRelocationTable = std::move(createRelocationTable( + _context.isRelaOutputFormat() ? ".rela.dyn" : ".rel.dyn", + ORDER_DYNAMIC_RELOCS)); + addSection(_dynamicRelocationTable.get()); + } + return _dynamicRelocationTable.get(); + } + + /// \brief Get or create the PLT relocation table. Referenced by DT_JMPREL. + RelocationTable<ELFT> *getPLTRelocationTable() { + if (!_pltRelocationTable) { + _pltRelocationTable = std::move(createRelocationTable( + _context.isRelaOutputFormat() ? ".rela.plt" : ".rel.plt", + ORDER_DYNAMIC_PLT_RELOCS)); + addSection(_pltRelocationTable.get()); + } + return _pltRelocationTable.get(); + } + + uint64_t getTLSSize() const { + for (const auto &phdr : *_programHeader) + if (phdr->p_type == llvm::ELF::PT_TLS) + return phdr->p_memsz; + return 0; + } + + bool isReferencedByDefinedAtom(const Atom *a) const { + return _referencedDynAtoms.count(a); + } + + bool isCopied(const SharedLibraryAtom *sla) const { + return _copiedDynSymNames.count(sla->name()); + } + + /// \brief Handle SORT_BY_PRIORITY. + void sortOutputSectionByPriority(StringRef outputSectionName, + StringRef prefix); + +protected: + /// \brief TargetLayouts may use these functions to reorder the input sections + /// in a order defined by their ABI. + virtual void finalizeOutputSectionLayout() {} + + /// \brief Allocate a new section. + virtual AtomSection<ELFT> *createSection( + StringRef name, int32_t contentType, + DefinedAtom::ContentPermissions contentPermissions, + SectionOrder sectionOrder); + + /// \brief Create a new relocation table. + virtual unique_bump_ptr<RelocationTable<ELFT>> + createRelocationTable(StringRef name, int32_t order) { + return unique_bump_ptr<RelocationTable<ELFT>>( + new (_allocator) RelocationTable<ELFT>(_context, name, order)); + } + +private: + /// Helper function that returns the priority value from an input section. + uint32_t getPriorityFromSectionName(StringRef sectionName) const; + +protected: + llvm::BumpPtrAllocator _allocator; + SectionMapT _sectionMap; + OutputSectionMapT _outputSectionMap; + AdditionalSegmentMapT _additionalSegmentMap; + SegmentMapT _segmentMap; + std::vector<Chunk<ELFT> *> _sections; + std::vector<Segment<ELFT> *> _segments; + std::vector<OutputSection<ELFT> *> _outputSections; + ELFHeader<ELFT> *_elfHeader; + ProgramHeader<ELFT> *_programHeader; + unique_bump_ptr<RelocationTable<ELFT>> _dynamicRelocationTable; + unique_bump_ptr<RelocationTable<ELFT>> _pltRelocationTable; + std::vector<lld::AtomLayout *> _absoluteAtoms; + AtomSetT _referencedDynAtoms; + llvm::StringSet<> _copiedDynSymNames; + ELFLinkingContext &_context; + script::Sema &_linkerScriptSema; +}; + +template <class ELFT> +Layout::SectionOrder DefaultLayout<ELFT>::getSectionOrder( + StringRef name, int32_t contentType, int32_t contentPermissions) { + switch (contentType) { + case DefinedAtom::typeResolver: + case DefinedAtom::typeCode: + return llvm::StringSwitch<Layout::SectionOrder>(name) + .StartsWith(".eh_frame_hdr", ORDER_EH_FRAMEHDR) + .StartsWith(".eh_frame", ORDER_EH_FRAME) + .StartsWith(".init", ORDER_INIT) + .StartsWith(".fini", ORDER_FINI) + .StartsWith(".hash", ORDER_HASH) + .Default(ORDER_TEXT); + + case DefinedAtom::typeConstant: + return ORDER_RODATA; + + case DefinedAtom::typeData: + case DefinedAtom::typeDataFast: + return llvm::StringSwitch<Layout::SectionOrder>(name) + .StartsWith(".init_array", ORDER_INIT_ARRAY) + .StartsWith(".fini_array", ORDER_FINI_ARRAY) + .StartsWith(".dynamic", ORDER_DYNAMIC) + .StartsWith(".ctors", ORDER_CTORS) + .StartsWith(".dtors", ORDER_DTORS) + .Default(ORDER_DATA); + + case DefinedAtom::typeZeroFill: + case DefinedAtom::typeZeroFillFast: + return ORDER_BSS; + + case DefinedAtom::typeGOT: + return llvm::StringSwitch<Layout::SectionOrder>(name) + .StartsWith(".got.plt", ORDER_GOT_PLT) + .Default(ORDER_GOT); + + case DefinedAtom::typeStub: + return ORDER_PLT; + + case DefinedAtom::typeRONote: + return ORDER_RO_NOTE; + + case DefinedAtom::typeRWNote: + return ORDER_RW_NOTE; + + case DefinedAtom::typeNoAlloc: + return ORDER_NOALLOC; + + case DefinedAtom::typeThreadData: + return ORDER_TDATA; + case DefinedAtom::typeThreadZeroFill: + return ORDER_TBSS; + default: + // If we get passed in a section push it to OTHER + if (contentPermissions == DefinedAtom::perm___) + return ORDER_OTHER; + + return ORDER_NOT_DEFINED; + } +} + +/// \brief This maps the input sections to the output section names +template <class ELFT> +StringRef +DefaultLayout<ELFT>::getInputSectionName(const DefinedAtom *da) const { + if (da->sectionChoice() == DefinedAtom::sectionBasedOnContent) { + switch (da->contentType()) { + case DefinedAtom::typeCode: + return ".text"; + case DefinedAtom::typeData: + return ".data"; + case DefinedAtom::typeConstant: + return ".rodata"; + case DefinedAtom::typeZeroFill: + return ".bss"; + case DefinedAtom::typeThreadData: + return ".tdata"; + case DefinedAtom::typeThreadZeroFill: + return ".tbss"; + default: + break; + } + } + return da->customSectionName(); +} + +/// \brief This maps the input sections to the output section names. +template <class ELFT> +StringRef +DefaultLayout<ELFT>::getOutputSectionName(StringRef archivePath, + StringRef memberPath, + StringRef inputSectionName) const { + StringRef outputSectionName; + if (_linkerScriptSema.hasLayoutCommands()) { + script::Sema::SectionKey key = {archivePath, memberPath, inputSectionName}; + outputSectionName = _linkerScriptSema.getOutputSection(key); + if (!outputSectionName.empty()) + return outputSectionName; + } + return llvm::StringSwitch<StringRef>(inputSectionName) + .StartsWith(".text", ".text") + .StartsWith(".ctors", ".ctors") + .StartsWith(".dtors", ".dtors") + .StartsWith(".rodata", ".rodata") + .StartsWith(".gcc_except_table", ".gcc_except_table") + .StartsWith(".data.rel.ro", ".data.rel.ro") + .StartsWith(".data.rel.local", ".data.rel.local") + .StartsWith(".data", ".data") + .StartsWith(".tdata", ".tdata") + .StartsWith(".tbss", ".tbss") + .StartsWith(".init_array", ".init_array") + .StartsWith(".fini_array", ".fini_array") + .Default(inputSectionName); +} + +/// \brief Gets the segment for a output section +template <class ELFT> +Layout::SegmentType DefaultLayout<ELFT>::getSegmentType( + Section<ELFT> *section) const { + + switch (section->order()) { + case ORDER_INTERP: + return llvm::ELF::PT_INTERP; + + case ORDER_TEXT: + case ORDER_HASH: + case ORDER_DYNAMIC_SYMBOLS: + case ORDER_DYNAMIC_STRINGS: + case ORDER_DYNAMIC_RELOCS: + case ORDER_DYNAMIC_PLT_RELOCS: + case ORDER_REL: + case ORDER_INIT: + case ORDER_PLT: + case ORDER_FINI: + case ORDER_RODATA: + case ORDER_EH_FRAME: + case ORDER_CTORS: + case ORDER_DTORS: + return llvm::ELF::PT_LOAD; + + case ORDER_RO_NOTE: + case ORDER_RW_NOTE: + return llvm::ELF::PT_NOTE; + + case ORDER_DYNAMIC: + return llvm::ELF::PT_DYNAMIC; + + case ORDER_EH_FRAMEHDR: + return llvm::ELF::PT_GNU_EH_FRAME; + + case ORDER_GOT: + case ORDER_GOT_PLT: + case ORDER_DATA: + case ORDER_BSS: + case ORDER_INIT_ARRAY: + case ORDER_FINI_ARRAY: + return llvm::ELF::PT_LOAD; + + case ORDER_TDATA: + case ORDER_TBSS: + return llvm::ELF::PT_TLS; + + default: + return llvm::ELF::PT_NULL; + } +} + +template <class ELFT> +bool DefaultLayout<ELFT>::hasOutputSegment(Section<ELFT> *section) { + switch (section->order()) { + case ORDER_INTERP: + case ORDER_HASH: + case ORDER_DYNAMIC_SYMBOLS: + case ORDER_DYNAMIC_STRINGS: + case ORDER_DYNAMIC_RELOCS: + case ORDER_DYNAMIC_PLT_RELOCS: + case ORDER_REL: + case ORDER_INIT: + case ORDER_PLT: + case ORDER_TEXT: + case ORDER_FINI: + case ORDER_RODATA: + case ORDER_EH_FRAME: + case ORDER_EH_FRAMEHDR: + case ORDER_TDATA: + case ORDER_TBSS: + case ORDER_RO_NOTE: + case ORDER_RW_NOTE: + case ORDER_DYNAMIC: + case ORDER_CTORS: + case ORDER_DTORS: + case ORDER_GOT: + case ORDER_GOT_PLT: + case ORDER_DATA: + case ORDER_INIT_ARRAY: + case ORDER_FINI_ARRAY: + case ORDER_BSS: + case ORDER_NOALLOC: + return true; + default: + return section->hasOutputSegment(); + } +} + +template <class ELFT> +AtomSection<ELFT> *DefaultLayout<ELFT>::createSection( + StringRef sectionName, int32_t contentType, + DefinedAtom::ContentPermissions permissions, SectionOrder sectionOrder) { + return new (_allocator) AtomSection<ELFT>(_context, sectionName, contentType, + permissions, sectionOrder); +} + +template <class ELFT> +AtomSection<ELFT> * +DefaultLayout<ELFT>::getSection(StringRef sectionName, int32_t contentType, + DefinedAtom::ContentPermissions permissions, + const DefinedAtom *da) { + const SectionKey sectionKey(sectionName, permissions, da->file().path()); + SectionOrder sectionOrder = getSectionOrder(sectionName, contentType, permissions); + auto sec = _sectionMap.find(sectionKey); + if (sec != _sectionMap.end()) + return sec->second; + AtomSection<ELFT> *newSec = + createSection(sectionName, contentType, permissions, sectionOrder); + + newSec->setOutputSectionName(getOutputSectionName( + da->file().archivePath(), da->file().memberPath(), sectionName)); + newSec->setOrder(sectionOrder); + newSec->setArchiveNameOrPath(da->file().archivePath()); + newSec->setMemberNameOrPath(da->file().memberPath()); + _sections.push_back(newSec); + _sectionMap.insert(std::make_pair(sectionKey, newSec)); + return newSec; +} + +template <class ELFT> +ErrorOr<const lld::AtomLayout *> +DefaultLayout<ELFT>::addAtom(const Atom *atom) { + if (const DefinedAtom *definedAtom = dyn_cast<DefinedAtom>(atom)) { + // HACK: Ignore undefined atoms. We need to adjust the interface so that + // undefined atoms can still be included in the output symbol table for + // -noinhibit-exec. + if (definedAtom->contentType() == DefinedAtom::typeUnknown) + return make_error_code(llvm::errc::invalid_argument); + const DefinedAtom::ContentPermissions permissions = + definedAtom->permissions(); + const DefinedAtom::ContentType contentType = definedAtom->contentType(); + + StringRef sectionName = getInputSectionName(definedAtom); + AtomSection<ELFT> *section = + getSection(sectionName, contentType, permissions, definedAtom); + + // Add runtime relocations to the .rela section. + for (const auto &reloc : *definedAtom) { + bool isLocalReloc = true; + if (_context.isDynamicRelocation(*reloc)) { + getDynamicRelocationTable()->addRelocation(*definedAtom, *reloc); + isLocalReloc = false; + } else if (_context.isPLTRelocation(*reloc)) { + getPLTRelocationTable()->addRelocation(*definedAtom, *reloc); + isLocalReloc = false; + } + + if (!reloc->target()) + continue; + + //Ignore undefined atoms that are not target of dynamic relocations + if (isa<UndefinedAtom>(reloc->target()) && isLocalReloc) + continue; + + if (_context.isCopyRelocation(*reloc)) { + _copiedDynSymNames.insert(definedAtom->name()); + continue; + } + + _referencedDynAtoms.insert(reloc->target()); + } + + return section->appendAtom(atom); + } else if (const AbsoluteAtom *absoluteAtom = dyn_cast<AbsoluteAtom>(atom)) { + // Absolute atoms are not part of any section, they are global for the whole + // link + _absoluteAtoms.push_back(new (_allocator) + lld::AtomLayout(absoluteAtom, 0, absoluteAtom->value())); + return _absoluteAtoms.back(); + } else { + llvm_unreachable("Only absolute / defined atoms can be added here"); + } +} + +/// Output sections with the same name into a OutputSection +template <class ELFT> void DefaultLayout<ELFT>::createOutputSections() { + OutputSection<ELFT> *outputSection; + + for (auto &si : _sections) { + Section<ELFT> *section = dyn_cast<Section<ELFT>>(si); + if (!section) + continue; + const std::pair<StringRef, OutputSection<ELFT> *> currentOutputSection( + section->outputSectionName(), nullptr); + std::pair<typename OutputSectionMapT::iterator, bool> outputSectionInsert( + _outputSectionMap.insert(currentOutputSection)); + if (!outputSectionInsert.second) { + outputSection = outputSectionInsert.first->second; + } else { + outputSection = new (_allocator.Allocate<OutputSection<ELFT>>()) + OutputSection<ELFT>(section->outputSectionName()); + _outputSections.push_back(outputSection); + outputSectionInsert.first->second = outputSection; + } + outputSection->appendSection(si); + } +} + +template <class ELFT> +uint32_t +DefaultLayout<ELFT>::getPriorityFromSectionName(StringRef sectionName) const { + StringRef priority = sectionName.drop_front().rsplit('.').second; + uint32_t prio; + if (priority.getAsInteger(10, prio)) + return std::numeric_limits<uint32_t>::max(); + return prio; +} + +template <class ELFT> +void DefaultLayout<ELFT>::sortOutputSectionByPriority( + StringRef outputSectionName, StringRef prefix) { + OutputSection<ELFT> *outputSection = findOutputSection(outputSectionName); + if (!outputSection) + return; + + auto sections = outputSection->sections(); + + std::sort(sections.begin(), sections.end(), + [&](Chunk<ELFT> *lhs, Chunk<ELFT> *rhs) { + Section<ELFT> *lhsSection = dyn_cast<Section<ELFT>>(lhs); + Section<ELFT> *rhsSection = dyn_cast<Section<ELFT>>(rhs); + if (!lhsSection || !rhsSection) + return false; + StringRef lhsSectionName = lhsSection->inputSectionName(); + StringRef rhsSectionName = rhsSection->inputSectionName(); + + if (!prefix.empty()) { + if (!lhsSectionName.startswith(prefix) || + !rhsSectionName.startswith(prefix)) + return false; + } + return getPriorityFromSectionName(lhsSectionName) < + getPriorityFromSectionName(rhsSectionName); + }); +} + +template <class ELFT> void DefaultLayout<ELFT>::assignSectionsToSegments() { + ScopedTask task(getDefaultDomain(), "assignSectionsToSegments"); + ELFLinkingContext::OutputMagic outputMagic = _context.getOutputMagic(); + // sort the sections by their order as defined by the layout + sortInputSections(); + + // Create output sections. + createOutputSections(); + + // Finalize output section layout. + finalizeOutputSectionLayout(); + + // Set the ordinal after sorting the sections + int ordinal = 1; + for (auto osi : _outputSections) { + osi->setOrdinal(ordinal); + for (auto ai : osi->sections()) { + ai->setOrdinal(ordinal); + } + ++ordinal; + } + for (auto osi : _outputSections) { + for (auto ai : osi->sections()) { + if (auto section = dyn_cast<Section<ELFT> >(ai)) { + if (!hasOutputSegment(section)) + continue; + + osi->setLoadableSection(section->isLoadableSection()); + + // Get the segment type for the section + int64_t segmentType = getSegmentType(section); + + osi->setHasSegment(); + section->setSegmentType(segmentType); + StringRef segmentName = section->segmentKindToStr(); + + int64_t lookupSectionFlag = osi->flags(); + if ((!(lookupSectionFlag & llvm::ELF::SHF_WRITE)) && + (_context.mergeRODataToTextSegment())) + lookupSectionFlag &= ~llvm::ELF::SHF_EXECINSTR; + + // Merge string sections into Data segment itself + lookupSectionFlag &= ~(llvm::ELF::SHF_STRINGS | llvm::ELF::SHF_MERGE); + + // Merge the TLS section into the DATA segment itself + lookupSectionFlag &= ~(llvm::ELF::SHF_TLS); + + Segment<ELFT> *segment; + // We need a separate segment for sections that don't have + // the segment type to be PT_LOAD + if (segmentType != llvm::ELF::PT_LOAD) { + const AdditionalSegmentKey key(segmentType, lookupSectionFlag); + const std::pair<AdditionalSegmentKey, Segment<ELFT> *> + additionalSegment(key, nullptr); + std::pair<typename AdditionalSegmentMapT::iterator, bool> + additionalSegmentInsert( + _additionalSegmentMap.insert(additionalSegment)); + if (!additionalSegmentInsert.second) { + segment = additionalSegmentInsert.first->second; + } else { + segment = new (_allocator) + Segment<ELFT>(_context, segmentName, segmentType); + additionalSegmentInsert.first->second = segment; + _segments.push_back(segment); + } + segment->append(section); + } + if (segmentType == llvm::ELF::PT_NULL) + continue; + + // If the output magic is set to OutputMagic::NMAGIC or + // OutputMagic::OMAGIC, Place the data alongside text in one single + // segment + if (outputMagic == ELFLinkingContext::OutputMagic::NMAGIC || + outputMagic == ELFLinkingContext::OutputMagic::OMAGIC) + lookupSectionFlag = llvm::ELF::SHF_EXECINSTR | llvm::ELF::SHF_ALLOC | + llvm::ELF::SHF_WRITE; + + // Use the flags of the merged Section for the segment + const SegmentKey key("PT_LOAD", lookupSectionFlag); + const std::pair<SegmentKey, Segment<ELFT> *> currentSegment(key, + nullptr); + std::pair<typename SegmentMapT::iterator, bool> segmentInsert( + _segmentMap.insert(currentSegment)); + if (!segmentInsert.second) { + segment = segmentInsert.first->second; + } else { + segment = new (_allocator) + Segment<ELFT>(_context, "PT_LOAD", llvm::ELF::PT_LOAD); + segmentInsert.first->second = segment; + _segments.push_back(segment); + } + // Insert chunks with linker script expressions that occur at this + // point, just before appending a new input section + addExtraChunksToSegment(segment, section->archivePath(), + section->memberPath(), + section->inputSectionName()); + segment->append(section); + } + } + } + if (_context.isDynamic() && !_context.isDynamicLibrary()) { + Segment<ELFT> *segment = + new (_allocator) ProgramHeaderSegment<ELFT>(_context); + _segments.push_back(segment); + segment->append(_elfHeader); + segment->append(_programHeader); + } +} + +template<class ELFT> +void +DefaultLayout<ELFT>::assignVirtualAddress() { + if (_segments.empty()) + return; + + std::sort(_segments.begin(), _segments.end(), Segment<ELFT>::compareSegments); + + uint64_t baseAddress = _context.getBaseAddress(); + + // HACK: This is a super dirty hack. The elf header and program header are + // not part of a section, but we need them to be loaded at the base address + // so that AT_PHDR is set correctly by the loader and so they are accessible + // at runtime. To do this we simply prepend them to the first loadable Segment + // and let the layout logic take care of it. + Segment<ELFT> *firstLoadSegment = nullptr; + for (auto si : _segments) { + if (si->segmentType() == llvm::ELF::PT_LOAD) { + firstLoadSegment = si; + si->firstSection()->setAlign(si->alignment()); + break; + } + } + assert(firstLoadSegment != nullptr && "No loadable segment!"); + firstLoadSegment->prepend(_programHeader); + firstLoadSegment->prepend(_elfHeader); + bool newSegmentHeaderAdded = true; + bool virtualAddressAssigned = false; + bool fileOffsetAssigned = false; + while (true) { + for (auto si : _segments) { + si->finalize(); + // Don't add PT_NULL segments into the program header + if (si->segmentType() != llvm::ELF::PT_NULL) + newSegmentHeaderAdded = _programHeader->addSegment(si); + } + if (!newSegmentHeaderAdded && virtualAddressAssigned) + break; + uint64_t address = baseAddress; + // start assigning virtual addresses + for (auto &si : _segments) { + if ((si->segmentType() != llvm::ELF::PT_LOAD) && + (si->segmentType() != llvm::ELF::PT_NULL)) + continue; + + if (si->segmentType() == llvm::ELF::PT_NULL) { + si->assignVirtualAddress(0 /*non loadable*/); + } else { + if (virtualAddressAssigned && (address != baseAddress) && + (address == si->virtualAddr())) + break; + si->assignVirtualAddress(address); + } + address = si->virtualAddr() + si->memSize(); + } + uint64_t baseFileOffset = 0; + uint64_t fileoffset = baseFileOffset; + for (auto &si : _segments) { + if ((si->segmentType() != llvm::ELF::PT_LOAD) && + (si->segmentType() != llvm::ELF::PT_NULL)) + continue; + if (fileOffsetAssigned && (fileoffset != baseFileOffset) && + (fileoffset == si->fileOffset())) + break; + si->assignFileOffsets(fileoffset); + fileoffset = si->fileOffset() + si->fileSize(); + } + virtualAddressAssigned = true; + fileOffsetAssigned = true; + _programHeader->resetProgramHeaders(); + } + Section<ELFT> *section; + // Fix the offsets of all the atoms within a section + for (auto &si : _sections) { + section = dyn_cast<Section<ELFT>>(si); + if (section && DefaultLayout<ELFT>::hasOutputSegment(section)) + section->assignFileOffsets(section->fileOffset()); + } + // Set the size of the merged Sections + for (auto osi : _outputSections) { + uint64_t sectionfileoffset = 0; + uint64_t startFileOffset = 0; + uint64_t sectionsize = 0; + bool isFirstSection = true; + for (auto si : osi->sections()) { + if (isFirstSection) { + startFileOffset = si->fileOffset(); + isFirstSection = false; + } + sectionfileoffset = si->fileOffset(); + sectionsize = si->fileSize(); + } + sectionsize = (sectionfileoffset - startFileOffset) + sectionsize; + osi->setFileOffset(startFileOffset); + osi->setSize(sectionsize); + } + // Set the virtual addr of the merged Sections + for (auto osi : _outputSections) { + uint64_t sectionstartaddr = 0; + uint64_t startaddr = 0; + uint64_t sectionsize = 0; + bool isFirstSection = true; + for (auto si : osi->sections()) { + if (isFirstSection) { + startaddr = si->virtualAddr(); + isFirstSection = false; + } + sectionstartaddr = si->virtualAddr(); + sectionsize = si->memSize(); + } + sectionsize = (sectionstartaddr - startaddr) + sectionsize; + osi->setMemSize(sectionsize); + osi->setAddr(startaddr); + } +} + +template <class ELFT> +void DefaultLayout<ELFT>::assignFileOffsetsForMiscSections() { + uint64_t fileoffset = 0; + uint64_t size = 0; + for (auto si : _segments) { + // Don't calculate offsets from non loadable segments + if ((si->segmentType() != llvm::ELF::PT_LOAD) && + (si->segmentType() != llvm::ELF::PT_NULL)) + continue; + fileoffset = si->fileOffset(); + size = si->fileSize(); + } + fileoffset = fileoffset + size; + Section<ELFT> *section; + for (auto si : _sections) { + section = dyn_cast<Section<ELFT>>(si); + if (section && DefaultLayout<ELFT>::hasOutputSegment(section)) + continue; + fileoffset = llvm::RoundUpToAlignment(fileoffset, si->alignment()); + si->setFileOffset(fileoffset); + si->setVirtualAddr(0); + fileoffset += si->fileSize(); + } +} + +template <class ELFT> void DefaultLayout<ELFT>::sortInputSections() { + // First, sort according to default layout's order + std::stable_sort( + _sections.begin(), _sections.end(), + [](Chunk<ELFT> *A, Chunk<ELFT> *B) { return A->order() < B->order(); }); + + if (!_linkerScriptSema.hasLayoutCommands()) + return; + + // Sort the sections by their order as defined by the linker script + std::stable_sort(this->_sections.begin(), this->_sections.end(), + [this](Chunk<ELFT> *A, Chunk<ELFT> *B) { + auto *a = dyn_cast<Section<ELFT>>(A); + auto *b = dyn_cast<Section<ELFT>>(B); + + if (a == nullptr) + return false; + if (b == nullptr) + return true; + + return _linkerScriptSema.less( + {a->archivePath(), a->memberPath(), + a->inputSectionName()}, + {b->archivePath(), b->memberPath(), + b->inputSectionName()}); + }); + // Now try to arrange sections with no mapping rules to sections with + // similar content + auto p = this->_sections.begin(); + // Find first section that has no assigned rule id + while (p != this->_sections.end()) { + auto *sect = dyn_cast<AtomSection<ELFT>>(*p); + if (!sect) + break; + + if (!_linkerScriptSema.hasMapping({sect->archivePath(), + sect->memberPath(), + sect->inputSectionName()})) + break; + + ++p; + } + // For all sections that have no assigned rule id, try to move them near a + // section with similar contents + if (p != this->_sections.begin()) { + for (; p != this->_sections.end(); ++p) { + auto q = p; + --q; + while (q != this->_sections.begin() && + (*q)->getContentType() != (*p)->getContentType()) + --q; + if ((*q)->getContentType() != (*p)->getContentType()) + continue; + ++q; + for (auto i = p; i != q;) { + auto next = i--; + std::iter_swap(i, next); + } + } + } +} + +template <class ELFT> +void DefaultLayout<ELFT>::addExtraChunksToSegment(Segment<ELFT> *segment, + StringRef archivePath, + StringRef memberPath, + StringRef sectionName) { + if (!_linkerScriptSema.hasLayoutCommands()) + return; + + std::vector<const script::SymbolAssignment *> exprs = + _linkerScriptSema.getExprs({archivePath, memberPath, sectionName}); + for (auto expr : exprs) { + auto expChunk = + new (this->_allocator) ExpressionChunk<ELFT>(this->_context, expr); + segment->append(expChunk); + } +} + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/DefaultTargetHandler.h b/lib/ReaderWriter/ELF/DefaultTargetHandler.h new file mode 100644 index 0000000000000..16668f2df6182 --- /dev/null +++ b/lib/ReaderWriter/ELF/DefaultTargetHandler.h @@ -0,0 +1,38 @@ +//===- lib/ReaderWriter/ELF/DefaultTargetHandler.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_DEFAULT_TARGET_HANDLER_H +#define LLD_READER_WRITER_ELF_DEFAULT_TARGET_HANDLER_H + +#include "DefaultLayout.h" +#include "DynamicLibraryWriter.h" +#include "ELFReader.h" +#include "ExecutableWriter.h" +#include "TargetHandler.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { +template <class ELFT> +class DefaultTargetHandler : public TargetHandler<ELFT> { +public: + const TargetRelocationHandler &getRelocationHandler() const = 0; + + virtual std::unique_ptr<Reader> getObjReader() = 0; + + virtual std::unique_ptr<Reader> getDSOReader() = 0; + + virtual std::unique_ptr<Writer> getWriter() = 0; +}; + +} // end namespace elf +} // end namespace lld +#endif diff --git a/lib/ReaderWriter/ELF/DynamicFile.h b/lib/ReaderWriter/ELF/DynamicFile.h new file mode 100644 index 0000000000000..c4e3e7165efd2 --- /dev/null +++ b/lib/ReaderWriter/ELF/DynamicFile.h @@ -0,0 +1,123 @@ +//===- lib/ReaderWriter/ELF/DynamicFile.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_DYNAMIC_FILE_H +#define LLD_READER_WRITER_ELF_DYNAMIC_FILE_H + +#include "Atoms.h" +#include "lld/Core/SharedLibraryFile.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Path.h" +#include <unordered_map> + +namespace lld { +namespace elf { +template <class ELFT> class DynamicFile : public SharedLibraryFile { +public: + static ErrorOr<std::unique_ptr<DynamicFile>> + create(std::unique_ptr<llvm::MemoryBuffer> mb, ELFLinkingContext &ctx); + + const SharedLibraryAtom *exports(StringRef name, + bool dataSymbolOnly) const override { + assert(!dataSymbolOnly && "Invalid option for ELF exports!"); + // See if we have the symbol. + auto sym = _nameToSym.find(name); + if (sym == _nameToSym.end()) + return nullptr; + // Have we already created a SharedLibraryAtom for it? + if (sym->second._atom) + return sym->second._atom; + // Create a SharedLibraryAtom for this symbol. + return sym->second._atom = new (_alloc) ELFDynamicAtom<ELFT>( + *this, name, _soname, sym->second._symbol); + } + + StringRef getDSOName() const override { return _soname; } + +protected: + std::error_code doParse() override { + std::error_code ec; + _objFile.reset( + new llvm::object::ELFFile<ELFT>(_mb->getBuffer(), ec)); + if (ec) + return ec; + + llvm::object::ELFFile<ELFT> &obj = *_objFile; + + _soname = obj.getLoadName(); + if (_soname.empty()) + _soname = llvm::sys::path::filename(path()); + + // Create a map from names to dynamic symbol table entries. + // TODO: This should use the object file's build in hash table instead if + // it exists. + for (auto i = obj.begin_dynamic_symbols(), e = obj.end_dynamic_symbols(); + i != e; ++i) { + auto name = obj.getSymbolName(i); + if ((ec = name.getError())) + return ec; + + // Dont add local symbols to dynamic entries. The first symbol in the + // dynamic symbol table is a local symbol. + if (i->getBinding() == llvm::ELF::STB_LOCAL) + continue; + + // TODO: Add absolute symbols + if (i->st_shndx == llvm::ELF::SHN_ABS) + continue; + + if (i->st_shndx == llvm::ELF::SHN_UNDEF) { + if (!_useShlibUndefines) + continue; + // Create an undefined atom. + if (!name->empty()) { + auto *newAtom = new (_alloc) ELFUndefinedAtom<ELFT>(*this, *name, &*i); + _undefinedAtoms._atoms.push_back(newAtom); + } + continue; + } + _nameToSym[*name]._symbol = &*i; + } + return std::error_code(); + } + +private: + DynamicFile(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx) + : SharedLibraryFile(mb->getBufferIdentifier()), _mb(std::move(mb)), + _ctx(ctx), _useShlibUndefines(ctx.useShlibUndefines()) {} + + mutable llvm::BumpPtrAllocator _alloc; + std::unique_ptr<llvm::object::ELFFile<ELFT>> _objFile; + /// \brief DT_SONAME + StringRef _soname; + + struct SymAtomPair { + SymAtomPair() : _symbol(nullptr), _atom(nullptr) {} + const typename llvm::object::ELFFile<ELFT>::Elf_Sym *_symbol; + const SharedLibraryAtom *_atom; + }; + + std::unique_ptr<MemoryBuffer> _mb; + ELFLinkingContext &_ctx; + bool _useShlibUndefines; + mutable std::unordered_map<StringRef, SymAtomPair> _nameToSym; +}; + +template <class ELFT> +ErrorOr<std::unique_ptr<DynamicFile<ELFT>>> +DynamicFile<ELFT>::create(std::unique_ptr<llvm::MemoryBuffer> mb, + ELFLinkingContext &ctx) { + return std::unique_ptr<DynamicFile>(new DynamicFile(std::move(mb), ctx)); +} + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/DynamicLibraryWriter.h b/lib/ReaderWriter/ELF/DynamicLibraryWriter.h new file mode 100644 index 0000000000000..f97514b525c0b --- /dev/null +++ b/lib/ReaderWriter/ELF/DynamicLibraryWriter.h @@ -0,0 +1,96 @@ +//===- lib/ReaderWriter/ELF/DynamicLibraryWriter.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_DYNAMIC_LIBRARY_WRITER_H +#define LLD_READER_WRITER_ELF_DYNAMIC_LIBRARY_WRITER_H + +#include "OutputELFWriter.h" + +namespace lld { +namespace elf { +using namespace llvm; +using namespace llvm::object; + +template<class ELFT> +class DynamicLibraryWriter; + +//===----------------------------------------------------------------------===// +// DynamicLibraryWriter Class +//===----------------------------------------------------------------------===// +template<class ELFT> +class DynamicLibraryWriter : public OutputELFWriter<ELFT> { +public: + DynamicLibraryWriter(ELFLinkingContext &context, TargetLayout<ELFT> &layout) + : OutputELFWriter<ELFT>(context, layout), + _runtimeFile(new RuntimeFile<ELFT>(context, "C runtime")) {} + +protected: + virtual void buildDynamicSymbolTable(const File &file); + virtual void addDefaultAtoms(); + virtual bool createImplicitFiles(std::vector<std::unique_ptr<File> > &); + virtual void finalizeDefaultAtomValues(); + +protected: + std::unique_ptr<RuntimeFile<ELFT> > _runtimeFile; +}; + +//===----------------------------------------------------------------------===// +// DynamicLibraryWriter +//===----------------------------------------------------------------------===// +template <class ELFT> +void DynamicLibraryWriter<ELFT>::buildDynamicSymbolTable(const File &file) { + // Add all the defined symbols to the dynamic symbol table + // we need hooks into the Atom to find out which atoms need + // to be exported + for (auto sec : this->_layout.sections()) + if (auto section = dyn_cast<AtomSection<ELFT>>(sec)) + for (const auto &atom : section->atoms()) { + const DefinedAtom *da = dyn_cast<const DefinedAtom>(atom->_atom); + if (da && (da->scope() == DefinedAtom::scopeGlobal)) + this->_dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), + atom->_virtualAddr, atom); + } + + for (const UndefinedAtom *a : file.undefined()) + this->_dynamicSymbolTable->addSymbol(a, ELF::SHN_UNDEF); + + OutputELFWriter<ELFT>::buildDynamicSymbolTable(file); +} + +template <class ELFT> void DynamicLibraryWriter<ELFT>::addDefaultAtoms() { + _runtimeFile->addAbsoluteAtom("_end"); +} + +/// \brief Hook in lld to add CRuntime file +template <class ELFT> +bool DynamicLibraryWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File> > &result) { + // Add the default atoms as defined by executables + DynamicLibraryWriter<ELFT>::addDefaultAtoms(); + OutputELFWriter<ELFT>::createImplicitFiles(result); + result.push_back(std::move(_runtimeFile)); + return true; +} + +template <class ELFT> +void DynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues() { + auto underScoreEndAtomIter = this->_layout.findAbsoluteAtom("_end"); + + if (auto bssSection = this->_layout.findOutputSection(".bss")) { + (*underScoreEndAtomIter)->_virtualAddr = + bssSection->virtualAddr() + bssSection->memSize(); + } else if (auto dataSection = this->_layout.findOutputSection(".data")) { + (*underScoreEndAtomIter)->_virtualAddr = + dataSection->virtualAddr() + dataSection->memSize(); + } +} + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_ELF_DYNAMIC_LIBRARY_WRITER_H diff --git a/lib/ReaderWriter/ELF/ELFFile.h b/lib/ReaderWriter/ELF/ELFFile.h new file mode 100644 index 0000000000000..11f4ee4fc633f --- /dev/null +++ b/lib/ReaderWriter/ELF/ELFFile.h @@ -0,0 +1,1179 @@ +//===- lib/ReaderWriter/ELF/ELFFile.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_FILE_H +#define LLD_READER_WRITER_ELF_FILE_H + +#include "Atoms.h" +#include <llvm/ADT/MapVector.h> +#include <map> +#include <unordered_map> + +namespace lld { + +namespace elf { +/// \brief Read a binary, find out based on the symbol table contents what kind +/// of symbol it is and create corresponding atoms for it +template <class ELFT> class ELFFile : public File { + + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel; + typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym_Iter Elf_Sym_Iter; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Rela_Iter Elf_Rela_Iter; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Rel_Iter Elf_Rel_Iter; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Word Elf_Word; + + // A Map is used to hold the atoms that have been divided up + // after reading the section that contains Merge String attributes + struct MergeSectionKey { + MergeSectionKey(const Elf_Shdr *shdr, int64_t offset) + : _shdr(shdr), _offset(offset) {} + // Data members + const Elf_Shdr *_shdr; + int64_t _offset; + }; + struct MergeSectionEq { + int64_t operator()(const MergeSectionKey &k) const { + return llvm::hash_combine((int64_t)(k._shdr->sh_name), + (int64_t)k._offset); + } + bool operator()(const MergeSectionKey &lhs, + const MergeSectionKey &rhs) const { + return ((lhs._shdr->sh_name == rhs._shdr->sh_name) && + (lhs._offset == rhs._offset)); + } + }; + + struct MergeString { + MergeString(int64_t offset, StringRef str, const Elf_Shdr *shdr, + StringRef sectionName) + : _offset(offset), _string(str), _shdr(shdr), + _sectionName(sectionName) {} + // the offset of this atom + int64_t _offset; + // The content + StringRef _string; + // Section header + const Elf_Shdr *_shdr; + // Section name + StringRef _sectionName; + }; + + // This is used to find the MergeAtom given a relocation + // offset + typedef std::vector<ELFMergeAtom<ELFT> *> MergeAtomsT; + + /// \brief find a mergeAtom given a start offset + struct FindByOffset { + const Elf_Shdr *_shdr; + int64_t _offset; + FindByOffset(const Elf_Shdr *shdr, int64_t offset) + : _shdr(shdr), _offset(offset) {} + bool operator()(const ELFMergeAtom<ELFT> *a) { + int64_t off = a->offset(); + return (_shdr->sh_name == a->section()) && + ((_offset >= off) && (_offset <= off + (int64_t)a->size())); + } + }; + + /// \brief find a merge atom given a offset + ELFMergeAtom<ELFT> *findMergeAtom(const Elf_Shdr *shdr, uint64_t offset) { + auto it = std::find_if(_mergeAtoms.begin(), _mergeAtoms.end(), + FindByOffset(shdr, offset)); + assert(it != _mergeAtoms.end()); + return *it; + } + + typedef std::unordered_map<MergeSectionKey, DefinedAtom *, MergeSectionEq, + MergeSectionEq> MergedSectionMapT; + typedef typename MergedSectionMapT::iterator MergedSectionMapIterT; + +public: + ELFFile(StringRef name, ELFLinkingContext &ctx) + : File(name, kindObject), _ordinal(0), + _doStringsMerge(ctx.mergeCommonStrings()), _useWrap(false), _ctx(ctx) { + setLastError(std::error_code()); + } + + ELFFile(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx) + : File(mb->getBufferIdentifier(), kindObject), _mb(std::move(mb)), + _ordinal(0), _doStringsMerge(ctx.mergeCommonStrings()), + _useWrap(ctx.wrapCalls().size()), _ctx(ctx) {} + + static ErrorOr<std::unique_ptr<ELFFile>> + create(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx); + + virtual Reference::KindArch kindArch(); + + /// \brief Create symbols from LinkingContext. + std::error_code createAtomsFromContext(); + + /// \brief Read input sections and populate necessary data structures + /// to read them later and create atoms + std::error_code createAtomizableSections(); + + /// \brief Create mergeable atoms from sections that have the merge attribute + /// set + std::error_code createMergeableAtoms(); + + /// \brief Add the symbols that the sections contain. The symbols will be + /// converted to atoms for + /// Undefined symbols, absolute symbols + std::error_code createSymbolsFromAtomizableSections(); + + /// \brief Create individual atoms + std::error_code createAtoms(); + + const atom_collection<DefinedAtom> &defined() const override { + return _definedAtoms; + } + + const atom_collection<UndefinedAtom> &undefined() const override { + return _undefinedAtoms; + } + + const atom_collection<SharedLibraryAtom> &sharedLibrary() const override { + return _sharedLibraryAtoms; + } + + const atom_collection<AbsoluteAtom> &absolute() const override { + return _absoluteAtoms; + } + + Atom *findAtom(const Elf_Sym *sourceSymbol, const Elf_Sym *targetSymbol) { + // All references to atoms inside a group are through undefined atoms. + Atom *targetAtom = _symbolToAtomMapping.lookup(targetSymbol); + StringRef targetSymbolName = targetAtom->name(); + if (targetAtom->definition() != Atom::definitionRegular) + return targetAtom; + if ((llvm::dyn_cast<DefinedAtom>(targetAtom))->scope() == + DefinedAtom::scopeTranslationUnit) + return targetAtom; + if (!redirectReferenceUsingUndefAtom(sourceSymbol, targetSymbol)) + return targetAtom; + auto undefForGroupchild = _undefAtomsForGroupChild.find(targetSymbolName); + if (undefForGroupchild != _undefAtomsForGroupChild.end()) + return undefForGroupchild->getValue(); + auto undefGroupChildAtom = + new (_readerStorage) SimpleUndefinedAtom(*this, targetSymbolName); + _undefinedAtoms._atoms.push_back(undefGroupChildAtom); + return (_undefAtomsForGroupChild[targetSymbolName] = undefGroupChildAtom); + } + +protected: + ELFDefinedAtom<ELFT> *createDefinedAtomAndAssignRelocations( + StringRef symbolName, StringRef sectionName, const Elf_Sym *symbol, + const Elf_Shdr *section, ArrayRef<uint8_t> symContent, + ArrayRef<uint8_t> secContent); + + std::error_code doParse() override; + + /// \brief Iterate over Elf_Rela relocations list and create references. + virtual void createRelocationReferences(const Elf_Sym *symbol, + ArrayRef<uint8_t> content, + range<Elf_Rela_Iter> rels); + + /// \brief Iterate over Elf_Rel relocations list and create references. + virtual void createRelocationReferences(const Elf_Sym *symbol, + ArrayRef<uint8_t> symContent, + ArrayRef<uint8_t> secContent, + range<Elf_Rel_Iter> rels); + + /// \brief After all the Atoms and References are created, update each + /// Reference's target with the Atom pointer it refers to. + void updateReferences(); + + /// \brief Update the reference if the access corresponds to a merge string + /// section. + void updateReferenceForMergeStringAccess(ELFReference<ELFT> *ref, + const Elf_Sym *symbol, + const Elf_Shdr *shdr); + + /// \brief Do we want to ignore the section. Ignored sections are + /// not processed to create atoms + bool isIgnoredSection(const Elf_Shdr *section); + + /// \brief Is the current section be treated as a mergeable string section. + /// The contents of a mergeable string section are null-terminated strings. + /// If the section have mergeable strings, the linker would need to split + /// the section into multiple atoms and mark them mergeByContent. + bool isMergeableStringSection(const Elf_Shdr *section); + + /// \brief Returns a new anonymous atom whose size is equal to the + /// section size. That atom will be used to represent the entire + /// section that have no symbols. + ELFDefinedAtom<ELFT> *createSectionAtom(const Elf_Shdr *section, + StringRef sectionName, + ArrayRef<uint8_t> contents); + + /// Returns the symbol's content size. The nextSymbol should be null if the + /// symbol is the last one in the section. + uint64_t symbolContentSize(const Elf_Shdr *section, + const Elf_Sym *symbol, + const Elf_Sym *nextSymbol); + + void createEdge(ELFDefinedAtom<ELFT> *from, ELFDefinedAtom<ELFT> *to, + uint32_t edgeKind); + + /// Get the section name for a section. + ErrorOr<StringRef> getSectionName(const Elf_Shdr *shdr) const { + if (!shdr) + return StringRef(); + return _objFile->getSectionName(shdr); + } + + /// Determines if the section occupy memory space. + bool sectionOccupiesMemorySpace(const Elf_Shdr *shdr) const { + return (shdr->sh_type != llvm::ELF::SHT_NOBITS); + } + + /// Return the section contents. + ErrorOr<ArrayRef<uint8_t>> getSectionContents(const Elf_Shdr *shdr) const { + if (!shdr || !sectionOccupiesMemorySpace(shdr)) + return ArrayRef<uint8_t>(); + return _objFile->getSectionContents(shdr); + } + + /// Returns true if the symbol is a undefined symbol. + bool isUndefinedSymbol(const Elf_Sym *sym) const { + return (sym->st_shndx == llvm::ELF::SHN_UNDEF); + } + + /// Determines if the target wants to create an atom for a section that has no + /// symbol references. + bool handleSectionWithNoSymbols(const Elf_Shdr *shdr, + std::vector<Elf_Sym_Iter> &syms) const { + return shdr && (shdr->sh_type == llvm::ELF::SHT_PROGBITS) && syms.empty(); + } + + /// Handle creation of atoms for .gnu.linkonce sections. + std::error_code handleGnuLinkOnceSection( + StringRef sectionName, + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection, + const Elf_Shdr *shdr); + + // Handle Section groups/COMDAT scetions. + std::error_code handleSectionGroup( + StringRef signature, StringRef groupSectionName, + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection, + llvm::DenseMap<const Elf_Shdr *, std::vector<StringRef>> &comdatSections, + const Elf_Shdr *shdr); + + /// Process the Undefined symbol and create an atom for it. + ErrorOr<ELFUndefinedAtom<ELFT> *> + handleUndefinedSymbol(StringRef symName, const Elf_Sym *sym) { + return new (_readerStorage) ELFUndefinedAtom<ELFT>(*this, symName, sym); + } + + /// Returns true if the symbol is a absolute symbol. + bool isAbsoluteSymbol(const Elf_Sym *sym) const { + return (sym->st_shndx == llvm::ELF::SHN_ABS); + } + + /// Process the Absolute symbol and create an atom for it. + ErrorOr<ELFAbsoluteAtom<ELFT> *> + handleAbsoluteSymbol(StringRef symName, const Elf_Sym *sym, int64_t value) { + return new (_readerStorage) + ELFAbsoluteAtom<ELFT>(*this, symName, sym, value); + } + + /// Returns true if the symbol is common symbol. A common symbol represents a + /// tentive definition in C. It has name, size and alignment constraint, but + /// actual storage has not yet been allocated. (The linker will allocate + /// storage for them in the later pass after coalescing tentative symbols by + /// name.) + virtual bool isCommonSymbol(const Elf_Sym *symbol) const { + return symbol->getType() == llvm::ELF::STT_COMMON || + symbol->st_shndx == llvm::ELF::SHN_COMMON; + } + + /// Returns true if the section is a gnulinkonce section. + bool isGnuLinkOnceSection(StringRef sectionName) const { + return sectionName.startswith(".gnu.linkonce."); + } + + /// Returns true if the section is a COMDAT group section. + bool isGroupSection(const Elf_Shdr *shdr) const { + return (shdr->sh_type == llvm::ELF::SHT_GROUP); + } + + /// Returns true if the section is a member of some group. + bool isSectionMemberOfGroup(const Elf_Shdr *shdr) const { + return (shdr->sh_flags & llvm::ELF::SHF_GROUP); + } + + /// Returns correct st_value for the symbol depending on the architecture. + /// For most architectures it's just a regular st_value with no changes. + virtual uint64_t getSymbolValue(const Elf_Sym *symbol) const { + return symbol->st_value; + } + + /// Process the common symbol and create an atom for it. + virtual ErrorOr<ELFCommonAtom<ELFT> *> + handleCommonSymbol(StringRef symName, const Elf_Sym *sym) { + return new (_readerStorage) ELFCommonAtom<ELFT>(*this, symName, sym); + } + + /// Returns true if the symbol is a defined symbol. + virtual bool isDefinedSymbol(const Elf_Sym *sym) const { + return (sym->getType() == llvm::ELF::STT_NOTYPE || + sym->getType() == llvm::ELF::STT_OBJECT || + sym->getType() == llvm::ELF::STT_FUNC || + sym->getType() == llvm::ELF::STT_GNU_IFUNC || + sym->getType() == llvm::ELF::STT_SECTION || + sym->getType() == llvm::ELF::STT_FILE || + sym->getType() == llvm::ELF::STT_TLS); + } + + /// Process the Defined symbol and create an atom for it. + virtual 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) { + return new (_readerStorage) ELFDefinedAtom<ELFT>( + *this, symName, sectionName, sym, sectionHdr, contentData, + referenceStart, referenceEnd, referenceList); + } + + /// Process the Merge string and create an atom for it. + ErrorOr<ELFMergeAtom<ELFT> *> + handleMergeString(StringRef sectionName, const Elf_Shdr *sectionHdr, + ArrayRef<uint8_t> contentData, unsigned int offset) { + ELFMergeAtom<ELFT> *mergeAtom = new (_readerStorage) + ELFMergeAtom<ELFT>(*this, sectionName, sectionHdr, contentData, offset); + const MergeSectionKey mergedSectionKey(sectionHdr, offset); + if (_mergedSectionMap.find(mergedSectionKey) == _mergedSectionMap.end()) + _mergedSectionMap.insert(std::make_pair(mergedSectionKey, mergeAtom)); + return mergeAtom; + } + + /// References to the sections comprising a group, from sections + /// outside the group, must be made via global UNDEF symbols, + /// referencing global symbols defined as addresses in the group + /// sections. They may not reference local symbols for addresses in + /// the group's sections, including section symbols. + /// ABI Doc : https://mentorembedded.github.io/cxx-abi/abi/prop-72-comdat.html + /// Does the atom need to be redirected using a separate undefined atom? + bool redirectReferenceUsingUndefAtom(const Elf_Sym *sourceSymbol, + const Elf_Sym *targetSymbol) const; + + void addReferenceToSymbol(const ELFReference<ELFT> *r, const Elf_Sym *sym) { + _referenceToSymbol[r] = sym; + } + + const Elf_Sym *findSymbolForReference(const ELFReference<ELFT> *r) const { + auto elfReferenceToSymbol = _referenceToSymbol.find(r); + if (elfReferenceToSymbol != _referenceToSymbol.end()) + return elfReferenceToSymbol->second; + return nullptr; + } + + llvm::BumpPtrAllocator _readerStorage; + std::unique_ptr<llvm::object::ELFFile<ELFT> > _objFile; + atom_collection_vector<DefinedAtom> _definedAtoms; + atom_collection_vector<UndefinedAtom> _undefinedAtoms; + atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms; + atom_collection_vector<AbsoluteAtom> _absoluteAtoms; + + /// \brief _relocationAddendReferences and _relocationReferences contain the + /// list of relocations references. In ELF, if a section named, ".text" has + /// relocations will also have a section named ".rel.text" or ".rela.text" + /// which will hold the entries. + std::unordered_map<StringRef, range<Elf_Rela_Iter>> + _relocationAddendReferences; + MergedSectionMapT _mergedSectionMap; + std::unordered_map<StringRef, range<Elf_Rel_Iter>> _relocationReferences; + std::vector<ELFReference<ELFT> *> _references; + llvm::DenseMap<const Elf_Sym *, Atom *> _symbolToAtomMapping; + llvm::DenseMap<const ELFReference<ELFT> *, const Elf_Sym *> + _referenceToSymbol; + // Group child atoms have a pair corresponding to the signature and the + // section header of the section that was used for generating the signature. + llvm::DenseMap<const Elf_Sym *, std::pair<StringRef, const Elf_Shdr *>> + _groupChild; + llvm::StringMap<Atom *> _undefAtomsForGroupChild; + + /// \brief Atoms that are created for a section that has the merge property + /// set + MergeAtomsT _mergeAtoms; + + /// \brief the section and the symbols that are contained within it to create + /// used to create atoms + llvm::MapVector<const Elf_Shdr *, std::vector<Elf_Sym_Iter>> _sectionSymbols; + + /// \brief Sections that have merge string property + std::vector<const Elf_Shdr *> _mergeStringSections; + + std::unique_ptr<MemoryBuffer> _mb; + int64_t _ordinal; + + /// \brief the cached options relevant while reading the ELF File + bool _doStringsMerge; + + /// \brief Is --wrap on? + bool _useWrap; + + /// \brief The LinkingContext. + ELFLinkingContext &_ctx; + + // Wrap map + llvm::StringMap<UndefinedAtom *> _wrapSymbolMap; +}; + +/// \brief All atoms are owned by a File. To add linker specific atoms +/// the atoms need to be inserted to a file called (RuntimeFile) which +/// are basically additional symbols required by libc and other runtime +/// libraries part of executing a program. This class provides support +/// for adding absolute symbols and undefined symbols +template <class ELFT> class RuntimeFile : public ELFFile<ELFT> { +public: + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + RuntimeFile(ELFLinkingContext &context, StringRef name) + : ELFFile<ELFT>(name, context) {} + + /// \brief add a global absolute atom + virtual Atom *addAbsoluteAtom(StringRef symbolName) { + assert(!symbolName.empty() && "AbsoluteAtoms must have a name"); + Elf_Sym *symbol = new (this->_readerStorage) Elf_Sym; + symbol->st_name = 0; + symbol->st_value = 0; + symbol->st_shndx = llvm::ELF::SHN_ABS; + symbol->setBindingAndType(llvm::ELF::STB_GLOBAL, llvm::ELF::STT_OBJECT); + symbol->setVisibility(llvm::ELF::STV_DEFAULT); + symbol->st_size = 0; + auto newAtom = this->handleAbsoluteSymbol(symbolName, symbol, -1); + this->_absoluteAtoms._atoms.push_back(*newAtom); + return *newAtom; + } + + /// \brief add an undefined atom + virtual Atom *addUndefinedAtom(StringRef symbolName) { + assert(!symbolName.empty() && "UndefinedAtoms must have a name"); + Elf_Sym *symbol = new (this->_readerStorage) Elf_Sym; + symbol->st_name = 0; + symbol->st_value = 0; + symbol->st_shndx = llvm::ELF::SHN_UNDEF; + symbol->setBindingAndType(llvm::ELF::STB_GLOBAL, llvm::ELF::STT_NOTYPE); + symbol->setVisibility(llvm::ELF::STV_DEFAULT); + symbol->st_size = 0; + auto newAtom = this->handleUndefinedSymbol(symbolName, symbol); + this->_undefinedAtoms._atoms.push_back(*newAtom); + return *newAtom; + } + + // cannot add atoms to Runtime file + virtual void addAtom(const Atom &) { + llvm_unreachable("cannot add atoms to Runtime files"); + } +}; + +template <class ELFT> +ErrorOr<std::unique_ptr<ELFFile<ELFT>>> +ELFFile<ELFT>::create(std::unique_ptr<MemoryBuffer> mb, + ELFLinkingContext &ctx) { + std::unique_ptr<ELFFile<ELFT>> file(new ELFFile<ELFT>(std::move(mb), ctx)); + return std::move(file); +} + +template <class ELFT> +std::error_code ELFFile<ELFT>::doParse() { + std::error_code ec; + _objFile.reset(new llvm::object::ELFFile<ELFT>(_mb->getBuffer(), ec)); + if (ec) + return ec; + + if ((ec = createAtomsFromContext())) + return ec; + + // Read input sections from the input file that need to be converted to + // atoms + if ((ec = createAtomizableSections())) + return ec; + + // For mergeable strings, we would need to split the section into various + // atoms + if ((ec = createMergeableAtoms())) + return ec; + + // Create the necessary symbols that are part of the section that we + // created in createAtomizableSections function + if ((ec = createSymbolsFromAtomizableSections())) + return ec; + + // Create the appropriate atoms from the file + if ((ec = createAtoms())) + return ec; + return std::error_code(); +} + +template <class ELFT> Reference::KindArch ELFFile<ELFT>::kindArch() { + switch (_objFile->getHeader()->e_machine) { + case llvm::ELF::EM_X86_64: + return Reference::KindArch::x86_64; + case llvm::ELF::EM_386: + return Reference::KindArch::x86; + case llvm::ELF::EM_ARM: + return Reference::KindArch::ARM; + case llvm::ELF::EM_HEXAGON: + return Reference::KindArch::Hexagon; + case llvm::ELF::EM_MIPS: + return Reference::KindArch::Mips; + case llvm::ELF::EM_AARCH64: + return Reference::KindArch::AArch64; + } + llvm_unreachable("unsupported e_machine value"); +} + +template <class ELFT> +std::error_code ELFFile<ELFT>::createAtomizableSections() { + // Handle: SHT_REL and SHT_RELA sections: + // Increment over the sections, when REL/RELA section types are found add + // the contents to the RelocationReferences map. + // Record the number of relocs to guess at preallocating the buffer. + uint64_t totalRelocs = 0; + for (const Elf_Shdr §ion : _objFile->sections()) { + if (isIgnoredSection(§ion)) + continue; + + if (isMergeableStringSection(§ion)) { + _mergeStringSections.push_back(§ion); + continue; + } + + if (section.sh_type == llvm::ELF::SHT_RELA) { + auto sHdr = _objFile->getSection(section.sh_info); + + auto sectionName = _objFile->getSectionName(sHdr); + if (std::error_code ec = sectionName.getError()) + return ec; + + auto rai(_objFile->begin_rela(§ion)); + auto rae(_objFile->end_rela(§ion)); + + _relocationAddendReferences[*sectionName] = make_range(rai, rae); + totalRelocs += std::distance(rai, rae); + } else if (section.sh_type == llvm::ELF::SHT_REL) { + auto sHdr = _objFile->getSection(section.sh_info); + + auto sectionName = _objFile->getSectionName(sHdr); + if (std::error_code ec = sectionName.getError()) + return ec; + + auto ri(_objFile->begin_rel(§ion)); + auto re(_objFile->end_rel(§ion)); + + _relocationReferences[*sectionName] = make_range(ri, re); + totalRelocs += std::distance(ri, re); + } else { + _sectionSymbols[§ion]; + } + } + _references.reserve(totalRelocs); + return std::error_code(); +} + +template <class ELFT> std::error_code ELFFile<ELFT>::createMergeableAtoms() { + // Divide the section that contains mergeable strings into tokens + // TODO + // a) add resolver support to recognize multibyte chars + // b) Create a separate section chunk to write mergeable atoms + std::vector<MergeString *> tokens; + for (const Elf_Shdr *msi : _mergeStringSections) { + auto sectionName = getSectionName(msi); + if (std::error_code ec = sectionName.getError()) + return ec; + + auto sectionContents = getSectionContents(msi); + if (std::error_code ec = sectionContents.getError()) + return ec; + + StringRef secCont(reinterpret_cast<const char *>(sectionContents->begin()), + sectionContents->size()); + + unsigned int prev = 0; + for (std::size_t i = 0, e = sectionContents->size(); i != e; ++i) { + if ((*sectionContents)[i] == '\0') { + tokens.push_back(new (_readerStorage) MergeString( + prev, secCont.slice(prev, i + 1), msi, *sectionName)); + prev = i + 1; + } + } + } + + // Create Mergeable atoms + for (const MergeString *tai : tokens) { + ArrayRef<uint8_t> content((const uint8_t *)tai->_string.data(), + tai->_string.size()); + ErrorOr<ELFMergeAtom<ELFT> *> mergeAtom = + handleMergeString(tai->_sectionName, tai->_shdr, content, tai->_offset); + (*mergeAtom)->setOrdinal(++_ordinal); + _definedAtoms._atoms.push_back(*mergeAtom); + _mergeAtoms.push_back(*mergeAtom); + } + return std::error_code(); +} + +template <class ELFT> +std::error_code ELFFile<ELFT>::createSymbolsFromAtomizableSections() { + // Increment over all the symbols collecting atoms and symbol names for + // later use. + auto SymI = _objFile->begin_symbols(), SymE = _objFile->end_symbols(); + + // Skip over dummy sym. + if (SymI != SymE) + ++SymI; + + for (; SymI != SymE; ++SymI) { + const Elf_Shdr *section = _objFile->getSection(&*SymI); + + auto symbolName = _objFile->getSymbolName(SymI); + if (std::error_code ec = symbolName.getError()) + return ec; + + if (isAbsoluteSymbol(&*SymI)) { + ErrorOr<ELFAbsoluteAtom<ELFT> *> absAtom = + handleAbsoluteSymbol(*symbolName, &*SymI, (int64_t)getSymbolValue(&*SymI)); + _absoluteAtoms._atoms.push_back(*absAtom); + _symbolToAtomMapping.insert(std::make_pair(&*SymI, *absAtom)); + } else if (isUndefinedSymbol(&*SymI)) { + if (_useWrap && + (_wrapSymbolMap.find(*symbolName) != _wrapSymbolMap.end())) { + auto wrapAtom = _wrapSymbolMap.find(*symbolName); + _symbolToAtomMapping.insert( + std::make_pair(&*SymI, wrapAtom->getValue())); + continue; + } + ErrorOr<ELFUndefinedAtom<ELFT> *> undefAtom = + handleUndefinedSymbol(*symbolName, &*SymI); + _undefinedAtoms._atoms.push_back(*undefAtom); + _symbolToAtomMapping.insert(std::make_pair(&*SymI, *undefAtom)); + } else if (isCommonSymbol(&*SymI)) { + ErrorOr<ELFCommonAtom<ELFT> *> commonAtom = + handleCommonSymbol(*symbolName, &*SymI); + (*commonAtom)->setOrdinal(++_ordinal); + _definedAtoms._atoms.push_back(*commonAtom); + _symbolToAtomMapping.insert(std::make_pair(&*SymI, *commonAtom)); + } else if (isDefinedSymbol(&*SymI)) { + _sectionSymbols[section].push_back(SymI); + } else { + llvm::errs() << "Unable to create atom for: " << *symbolName << "\n"; + return llvm::object::object_error::parse_failed; + } + } + + return std::error_code(); +} + +template <class ELFT> std::error_code ELFFile<ELFT>::createAtoms() { + // Holds all the atoms that are part of the section. They are the targets of + // the kindGroupChild reference. + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> atomsForSection; + // group sections have a mapping of the section header to the + // signature/section. + llvm::DenseMap<const Elf_Shdr *, std::pair<StringRef, StringRef>> + groupSections; + // Contains a list of comdat sections for a group. + llvm::DenseMap<const Elf_Shdr *, std::vector<StringRef>> comdatSections; + for (auto &i : _sectionSymbols) { + const Elf_Shdr *section = i.first; + std::vector<Elf_Sym_Iter> &symbols = i.second; + + // Sort symbols by position. + std::stable_sort(symbols.begin(), symbols.end(), + [this](Elf_Sym_Iter a, Elf_Sym_Iter b) { + return getSymbolValue(&*a) < getSymbolValue(&*b); + }); + + ErrorOr<StringRef> sectionName = this->getSectionName(section); + if (std::error_code ec = sectionName.getError()) + return ec; + + auto sectionContents = getSectionContents(section); + if (std::error_code ec = sectionContents.getError()) + return ec; + + bool addAtoms = true; + + // A section of type SHT_GROUP defines a grouping of sections. The name of a + // symbol from one of the containing object's symbol tables provides a + // signature + // for the section group. The section header of the SHT_GROUP section + // specifies + // the identifying symbol entry, as described : the sh_link member contains + // the section header index of the symbol table section that contains the + // entry. + // The sh_info member contains the symbol table index of the identifying + // entry. + // The sh_flags member of the section header contains 0. The name of the + // section + // (sh_name) is not specified. + if (isGroupSection(section)) { + const Elf_Word *groupMembers = + reinterpret_cast<const Elf_Word *>(sectionContents->data()); + const long count = (section->sh_size) / sizeof(Elf_Word); + for (int i = 1; i < count; i++) { + const Elf_Shdr *sHdr = _objFile->getSection(groupMembers[i]); + ErrorOr<StringRef> sectionName = _objFile->getSectionName(sHdr); + if (std::error_code ec = sectionName.getError()) + return ec; + comdatSections[section].push_back(*sectionName); + } + const Elf_Sym *symbol = _objFile->getSymbol(section->sh_info); + const Elf_Shdr *symtab = _objFile->getSection(section->sh_link); + ErrorOr<StringRef> symbolName = _objFile->getSymbolName(symtab, symbol); + if (std::error_code ec = symbolName.getError()) + return ec; + groupSections.insert( + std::make_pair(section, std::make_pair(*symbolName, *sectionName))); + continue; + } + + if (isGnuLinkOnceSection(*sectionName)) { + groupSections.insert( + std::make_pair(section, std::make_pair(*sectionName, *sectionName))); + addAtoms = false; + } + + if (isSectionMemberOfGroup(section)) + addAtoms = false; + + if (handleSectionWithNoSymbols(section, symbols)) { + ELFDefinedAtom<ELFT> *newAtom = + createSectionAtom(section, *sectionName, *sectionContents); + newAtom->setOrdinal(++_ordinal); + if (addAtoms) + _definedAtoms._atoms.push_back(newAtom); + else + atomsForSection[*sectionName].push_back(newAtom); + continue; + } + + ELFDefinedAtom<ELFT> *previousAtom = nullptr; + ELFReference<ELFT> *anonFollowedBy = nullptr; + + for (auto si = symbols.begin(), se = symbols.end(); si != se; ++si) { + auto symbol = *si; + StringRef symbolName = ""; + if (symbol->getType() != llvm::ELF::STT_SECTION) { + auto symName = _objFile->getSymbolName(symbol); + if (std::error_code ec = symName.getError()) + return ec; + symbolName = *symName; + } + + uint64_t contentSize = symbolContentSize( + section, &*symbol, (si + 1 == se) ? nullptr : &**(si + 1)); + + // Check to see if we need to add the FollowOn Reference + ELFReference<ELFT> *followOn = nullptr; + if (previousAtom) { + // Replace the followon atom with the anonymous atom that we created, + // so that the next symbol that we create is a followon from the + // anonymous atom. + if (anonFollowedBy) { + followOn = anonFollowedBy; + } else { + followOn = new (_readerStorage) + ELFReference<ELFT>(lld::Reference::kindLayoutAfter); + previousAtom->addReference(followOn); + } + } + + ArrayRef<uint8_t> symbolData((const uint8_t *)sectionContents->data() + + getSymbolValue(&*symbol), + contentSize); + + // If the linker finds that a section has global atoms that are in a + // mergeable section, treat them as defined atoms as they shouldn't be + // merged away as well as these symbols have to be part of symbol + // resolution + if (isMergeableStringSection(section)) { + if (symbol->getBinding() == llvm::ELF::STB_GLOBAL) { + auto definedMergeAtom = handleDefinedSymbol( + symbolName, *sectionName, &**si, section, symbolData, + _references.size(), _references.size(), _references); + (*definedMergeAtom)->setOrdinal(++_ordinal); + if (addAtoms) + _definedAtoms._atoms.push_back(*definedMergeAtom); + else + atomsForSection[*sectionName].push_back(*definedMergeAtom); + } + continue; + } + + // Don't allocate content to a weak symbol, as they may be merged away. + // Create an anonymous atom to hold the data. + ELFDefinedAtom<ELFT> *anonAtom = nullptr; + anonFollowedBy = nullptr; + if (symbol->getBinding() == llvm::ELF::STB_WEAK) { + // Create anonymous new non-weak ELF symbol that holds the symbol + // data. + auto sym = new (_readerStorage) Elf_Sym(*symbol); + sym->setBinding(llvm::ELF::STB_GLOBAL); + anonAtom = createDefinedAtomAndAssignRelocations( + "", *sectionName, sym, section, symbolData, *sectionContents); + symbolData = ArrayRef<uint8_t>(); + + // If this is the last atom, let's not create a followon reference. + if (anonAtom && (si + 1) != se) { + anonFollowedBy = new (_readerStorage) + ELFReference<ELFT>(lld::Reference::kindLayoutAfter); + anonAtom->addReference(anonFollowedBy); + } + } + + ELFDefinedAtom<ELFT> *newAtom = createDefinedAtomAndAssignRelocations( + symbolName, *sectionName, &*symbol, section, symbolData, + *sectionContents); + newAtom->setOrdinal(++_ordinal); + + // If the atom was a weak symbol, let's create a followon reference to + // the anonymous atom that we created. + if (anonAtom) + createEdge(newAtom, anonAtom, Reference::kindLayoutAfter); + + if (previousAtom) { + // Set the followon atom to the weak atom that we have created, so + // that they would alias when the file gets written. + followOn->setTarget(anonAtom ? anonAtom : newAtom); + } + + // The previous atom is always the atom created before unless the atom + // is a weak atom. + previousAtom = anonAtom ? anonAtom : newAtom; + + if (addAtoms) + _definedAtoms._atoms.push_back(newAtom); + else + atomsForSection[*sectionName].push_back(newAtom); + + _symbolToAtomMapping.insert(std::make_pair(&*symbol, newAtom)); + if (anonAtom) { + anonAtom->setOrdinal(++_ordinal); + if (addAtoms) + _definedAtoms._atoms.push_back(anonAtom); + else + atomsForSection[*sectionName].push_back(anonAtom); + } + } + } + + // Iterate over all the group sections to create parent atoms pointing to + // group-child atoms. + for (auto § : groupSections) { + StringRef signature = sect.second.first; + StringRef groupSectionName = sect.second.second; + if (isGnuLinkOnceSection(signature)) + handleGnuLinkOnceSection(signature, atomsForSection, sect.first); + else if (isGroupSection(sect.first)) + handleSectionGroup(signature, groupSectionName, atomsForSection, + comdatSections, sect.first); + } + + updateReferences(); + return std::error_code(); +} + +template <class ELFT> +std::error_code ELFFile<ELFT>::handleGnuLinkOnceSection( + StringRef signature, + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection, + const Elf_Shdr *shdr) { + // TODO: Check for errors. + unsigned int referenceStart = _references.size(); + std::vector<ELFReference<ELFT> *> refs; + for (auto ha : atomsForSection[signature]) { + _groupChild[ha->symbol()] = std::make_pair(signature, shdr); + ELFReference<ELFT> *ref = + new (_readerStorage) ELFReference<ELFT>(lld::Reference::kindGroupChild); + ref->setTarget(ha); + refs.push_back(ref); + } + atomsForSection[signature].clear(); + // Create a gnu linkonce atom. + auto gnuLinkOnceAtom = handleDefinedSymbol( + signature, signature, nullptr, shdr, ArrayRef<uint8_t>(), referenceStart, + _references.size(), _references); + (*gnuLinkOnceAtom)->setOrdinal(++_ordinal); + _definedAtoms._atoms.push_back(*gnuLinkOnceAtom); + for (auto reference : refs) + (*gnuLinkOnceAtom)->addReference(reference); + return std::error_code(); +} + +template <class ELFT> +std::error_code ELFFile<ELFT>::handleSectionGroup( + StringRef signature, StringRef groupSectionName, + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection, + llvm::DenseMap<const Elf_Shdr *, std::vector<StringRef>> &comdatSections, + const Elf_Shdr *shdr) { + // TODO: Check for errors. + unsigned int referenceStart = _references.size(); + std::vector<ELFReference<ELFT> *> refs; + auto sectionNamesInGroup = comdatSections[shdr]; + for (auto sectionName : sectionNamesInGroup) { + for (auto ha : atomsForSection[sectionName]) { + _groupChild[ha->symbol()] = std::make_pair(signature, shdr); + ELFReference<ELFT> *ref = new (_readerStorage) + ELFReference<ELFT>(lld::Reference::kindGroupChild); + ref->setTarget(ha); + refs.push_back(ref); + } + atomsForSection[sectionName].clear(); + } + // Create a gnu linkonce atom. + auto sectionGroupAtom = handleDefinedSymbol( + signature, groupSectionName, nullptr, shdr, ArrayRef<uint8_t>(), + referenceStart, _references.size(), _references); + (*sectionGroupAtom)->setOrdinal(++_ordinal); + _definedAtoms._atoms.push_back(*sectionGroupAtom); + for (auto reference : refs) + (*sectionGroupAtom)->addReference(reference); + return std::error_code(); +} + +template <class ELFT> std::error_code ELFFile<ELFT>::createAtomsFromContext() { + if (!_useWrap) + return std::error_code(); + // Steps :- + // a) Create an undefined atom for the symbol specified by the --wrap option, + // as that + // may be needed to be pulled from an archive. + // b) Create an undefined atom for __wrap_<symbolname>. + // c) All references to the symbol specified by wrap should point to + // __wrap_<symbolname> + // d) All references to __real_symbol should point to the <symbol> + for (auto &wrapsym : _ctx.wrapCalls()) { + StringRef wrapStr = wrapsym.getKey(); + // Create a undefined symbol fror the wrap symbol. + UndefinedAtom *wrapSymAtom = + new (_readerStorage) SimpleUndefinedAtom(*this, wrapStr); + StringRef wrapCallSym = + _ctx.allocateString((llvm::Twine("__wrap_") + wrapStr).str()); + StringRef realCallSym = + _ctx.allocateString((llvm::Twine("__real_") + wrapStr).str()); + UndefinedAtom *wrapCallAtom = + new (_readerStorage) SimpleUndefinedAtom(*this, wrapCallSym); + // Create maps, when there is call to sym, it should point to wrapCallSym. + _wrapSymbolMap.insert(std::make_pair(wrapStr, wrapCallAtom)); + // Whenever there is a reference to realCall it should point to the symbol + // created for each wrap usage. + _wrapSymbolMap.insert(std::make_pair(realCallSym, wrapSymAtom)); + _undefinedAtoms._atoms.push_back(wrapSymAtom); + _undefinedAtoms._atoms.push_back(wrapCallAtom); + } + return std::error_code(); +} + +template <class ELFT> +ELFDefinedAtom<ELFT> *ELFFile<ELFT>::createDefinedAtomAndAssignRelocations( + StringRef symbolName, StringRef sectionName, const Elf_Sym *symbol, + const Elf_Shdr *section, ArrayRef<uint8_t> symContent, + ArrayRef<uint8_t> secContent) { + unsigned int referenceStart = _references.size(); + + // Add Rela (those with r_addend) references: + auto rari = _relocationAddendReferences.find(sectionName); + if (rari != _relocationAddendReferences.end()) + createRelocationReferences(symbol, symContent, rari->second); + + // Add Rel references. + auto rri = _relocationReferences.find(sectionName); + if (rri != _relocationReferences.end()) + createRelocationReferences(symbol, symContent, secContent, rri->second); + + // Create the DefinedAtom and add it to the list of DefinedAtoms. + return *handleDefinedSymbol(symbolName, sectionName, symbol, section, + symContent, referenceStart, _references.size(), + _references); +} + +template <class ELFT> +void ELFFile<ELFT>::createRelocationReferences(const Elf_Sym *symbol, + ArrayRef<uint8_t> content, + range<Elf_Rela_Iter> rels) { + bool isMips64EL = _objFile->isMips64EL(); + const auto symValue = getSymbolValue(symbol); + for (const auto &rel : rels) { + if (rel.r_offset < symValue || + symValue + content.size() <= rel.r_offset) + continue; + auto elfRelocation = new (_readerStorage) + ELFReference<ELFT>(&rel, rel.r_offset - symValue, kindArch(), + rel.getType(isMips64EL), rel.getSymbol(isMips64EL)); + addReferenceToSymbol(elfRelocation, symbol); + _references.push_back(elfRelocation); + } +} + +template <class ELFT> +void ELFFile<ELFT>::createRelocationReferences(const Elf_Sym *symbol, + ArrayRef<uint8_t> symContent, + ArrayRef<uint8_t> secContent, + range<Elf_Rel_Iter> rels) { + bool isMips64EL = _objFile->isMips64EL(); + const auto symValue = getSymbolValue(symbol); + for (const auto &rel : rels) { + if (rel.r_offset < symValue || + symValue + symContent.size() <= rel.r_offset) + continue; + auto elfRelocation = new (_readerStorage) + ELFReference<ELFT>(rel.r_offset - symValue, kindArch(), + rel.getType(isMips64EL), rel.getSymbol(isMips64EL)); + int32_t addend = *(symContent.data() + rel.r_offset - symValue); + elfRelocation->setAddend(addend); + addReferenceToSymbol(elfRelocation, symbol); + _references.push_back(elfRelocation); + } +} + +template <class ELFT> +void ELFFile<ELFT>::updateReferenceForMergeStringAccess(ELFReference<ELFT> *ref, + const Elf_Sym *symbol, + const Elf_Shdr *shdr) { + // If the target atom is mergeable strefng atom, the atom might have been + // merged with other atom having the same contents. Try to find the + // merged one if that's the case. + int64_t addend = ref->addend(); + if (addend < 0) + addend = 0; + + const MergeSectionKey ms(shdr, addend); + auto msec = _mergedSectionMap.find(ms); + if (msec != _mergedSectionMap.end()) { + ref->setTarget(msec->second); + return; + } + + // The target atom was not merged. Mergeable atoms are not in + // _symbolToAtomMapping, so we cannot find it by calling findAtom(). We + // instead call findMergeAtom(). + if (symbol->getType() != llvm::ELF::STT_SECTION) + addend = getSymbolValue(symbol) + addend; + ELFMergeAtom<ELFT> *mergedAtom = findMergeAtom(shdr, addend); + ref->setOffset(addend - mergedAtom->offset()); + ref->setAddend(0); + ref->setTarget(mergedAtom); +} + +template <class ELFT> void ELFFile<ELFT>::updateReferences() { + for (auto &ri : _references) { + if (ri->kindNamespace() != lld::Reference::KindNamespace::ELF) + continue; + const Elf_Sym *symbol = _objFile->getSymbol(ri->targetSymbolIndex()); + const Elf_Shdr *shdr = _objFile->getSection(symbol); + + // If the atom is not in mergeable string section, the target atom is + // simply that atom. + if (isMergeableStringSection(shdr)) + updateReferenceForMergeStringAccess(ri, symbol, shdr); + else + ri->setTarget(findAtom(findSymbolForReference(ri), symbol)); + } +} + +template <class ELFT> +bool ELFFile<ELFT>::isIgnoredSection(const Elf_Shdr *section) { + switch (section->sh_type) { + case llvm::ELF::SHT_NULL: + case llvm::ELF::SHT_STRTAB: + case llvm::ELF::SHT_SYMTAB: + case llvm::ELF::SHT_SYMTAB_SHNDX: + return true; + default: + break; + } + return false; +} + +template <class ELFT> +bool ELFFile<ELFT>::isMergeableStringSection(const Elf_Shdr *section) { + if (_doStringsMerge && section) { + int64_t sectionFlags = section->sh_flags; + sectionFlags &= ~llvm::ELF::SHF_ALLOC; + // Mergeable string sections have both SHF_MERGE and SHF_STRINGS flags + // set. sh_entsize is the size of each character which is normally 1. + if ((section->sh_entsize < 2) && + (sectionFlags == (llvm::ELF::SHF_MERGE | llvm::ELF::SHF_STRINGS))) { + return true; + } + } + return false; +} + +template <class ELFT> +ELFDefinedAtom<ELFT> * +ELFFile<ELFT>::createSectionAtom(const Elf_Shdr *section, StringRef sectionName, + ArrayRef<uint8_t> content) { + Elf_Sym *sym = new (_readerStorage) Elf_Sym; + sym->st_name = 0; + sym->setBindingAndType(llvm::ELF::STB_LOCAL, llvm::ELF::STT_SECTION); + sym->st_other = 0; + sym->st_shndx = 0; + sym->st_value = 0; + sym->st_size = 0; + auto *newAtom = createDefinedAtomAndAssignRelocations( + "", sectionName, sym, section, content, content); + newAtom->setOrdinal(++_ordinal); + return newAtom; +} + +template <class ELFT> +uint64_t ELFFile<ELFT>::symbolContentSize(const Elf_Shdr *section, + const Elf_Sym *symbol, + const Elf_Sym *nextSymbol) { + const auto symValue = getSymbolValue(symbol); + // if this is the last symbol, take up the remaining data. + return nextSymbol ? getSymbolValue(nextSymbol) - symValue + : section->sh_size - symValue; +} + +template <class ELFT> +void ELFFile<ELFT>::createEdge(ELFDefinedAtom<ELFT> *from, + ELFDefinedAtom<ELFT> *to, uint32_t edgeKind) { + auto reference = new (_readerStorage) ELFReference<ELFT>(edgeKind); + reference->setTarget(to); + from->addReference(reference); +} + +/// Does the atom need to be redirected using a separate undefined atom? +template <class ELFT> +bool ELFFile<ELFT>::redirectReferenceUsingUndefAtom( + const Elf_Sym *sourceSymbol, const Elf_Sym *targetSymbol) const { + auto groupChildTarget = _groupChild.find(targetSymbol); + + // If the reference is not to a group child atom, there is no need to redirect + // using a undefined atom. Its also not needed if the source and target are + // from the same section. + if ((groupChildTarget == _groupChild.end()) || + (sourceSymbol->st_shndx == targetSymbol->st_shndx)) + return false; + + auto groupChildSource = _groupChild.find(sourceSymbol); + + // If the source symbol is not in a group, use a undefined symbol too. + if (groupChildSource == _groupChild.end()) + return true; + + // If the source and child are from the same group, we dont need the + // relocation to go through a undefined symbol. + if (groupChildSource->second.second == groupChildTarget->second.second) + return false; + + return true; +} + +} // end namespace elf +} // end namespace lld + +#endif // LLD_READER_WRITER_ELF_FILE_H diff --git a/lib/ReaderWriter/ELF/ELFLinkingContext.cpp b/lib/ReaderWriter/ELF/ELFLinkingContext.cpp new file mode 100644 index 0000000000000..c7dffda8a463e --- /dev/null +++ b/lib/ReaderWriter/ELF/ELFLinkingContext.cpp @@ -0,0 +1,259 @@ +//===- lib/ReaderWriter/ELF/ELFLinkingContext.cpp -------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "ELFFile.h" +#include "OrderPass.h" +#include "TargetHandler.h" +#include "lld/Core/Instrumentation.h" +#include "lld/Core/SharedLibraryFile.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Config/config.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +#if defined(HAVE_CXXABI_H) +#include <cxxabi.h> +#endif + +namespace lld { + +class CommandLineUndefinedAtom : public SimpleUndefinedAtom { +public: + CommandLineUndefinedAtom(const File &f, StringRef name) + : SimpleUndefinedAtom(f, name) {} + + CanBeNull canBeNull() const override { + return CanBeNull::canBeNullAtBuildtime; + } +}; + +ELFLinkingContext::ELFLinkingContext( + llvm::Triple triple, std::unique_ptr<TargetHandlerBase> targetHandler) + : _outputELFType(llvm::ELF::ET_EXEC), _triple(triple), + _targetHandler(std::move(targetHandler)), _baseAddress(0), + _isStaticExecutable(false), _noInhibitExec(false), _exportDynamic(false), + _mergeCommonStrings(false), _useShlibUndefines(true), + _dynamicLinkerArg(false), _noAllowDynamicLibraries(false), + _mergeRODataToTextSegment(true), _demangle(true), + _stripSymbols(false), _alignSegments(true), _collectStats(false), + _outputMagic(OutputMagic::DEFAULT), _initFunction("_init"), + _finiFunction("_fini"), _sysrootPath(""), _linkerScriptSema() {} + +void ELFLinkingContext::addPasses(PassManager &pm) { + pm.add(llvm::make_unique<elf::OrderPass>()); +} + +uint16_t ELFLinkingContext::getOutputMachine() const { + switch (getTriple().getArch()) { + case llvm::Triple::x86: + return llvm::ELF::EM_386; + case llvm::Triple::x86_64: + return llvm::ELF::EM_X86_64; + case llvm::Triple::hexagon: + return llvm::ELF::EM_HEXAGON; + case llvm::Triple::mipsel: + case llvm::Triple::mips64el: + return llvm::ELF::EM_MIPS; + case llvm::Triple::aarch64: + return llvm::ELF::EM_AARCH64; + case llvm::Triple::arm: + return llvm::ELF::EM_ARM; + default: + llvm_unreachable("Unhandled arch"); + } +} + +StringRef ELFLinkingContext::entrySymbolName() const { + if (_outputELFType == llvm::ELF::ET_EXEC && _entrySymbolName.empty()) + return "_start"; + return _entrySymbolName; +} + +bool ELFLinkingContext::validateImpl(raw_ostream &diagnostics) { + switch (outputFileType()) { + case LinkingContext::OutputFileType::YAML: + _writer = createWriterYAML(*this); + break; + case LinkingContext::OutputFileType::Native: + llvm_unreachable("Unimplemented"); + break; + default: + _writer = createWriterELF(this->targetHandler()); + break; + } + + // If -dead_strip, set up initial live symbols. + if (deadStrip()) + addDeadStripRoot(entrySymbolName()); + return true; +} + +bool ELFLinkingContext::isDynamic() const { + switch (_outputELFType) { + case llvm::ELF::ET_EXEC: + return !_isStaticExecutable; + case llvm::ELF::ET_DYN: + return true; + } + return false; +} + +bool ELFLinkingContext::isRelativeReloc(const Reference &) const { + return false; +} + +Writer &ELFLinkingContext::writer() const { return *_writer; } + +static void buildSearchPath(SmallString<128> &path, StringRef dir, + StringRef sysRoot) { + if (!dir.startswith("=/")) + path.assign(dir); + else { + path.assign(sysRoot); + path.append(dir.substr(1)); + } +} + +ErrorOr<StringRef> ELFLinkingContext::searchLibrary(StringRef libName) const { + bool hasColonPrefix = libName[0] == ':'; + SmallString<128> path; + for (StringRef dir : _inputSearchPaths) { + // Search for dynamic library + if (!_isStaticExecutable) { + buildSearchPath(path, dir, _sysrootPath); + llvm::sys::path::append(path, hasColonPrefix + ? libName.drop_front() + : Twine("lib", libName) + ".so"); + if (llvm::sys::fs::exists(path.str())) + return StringRef(*new (_allocator) std::string(path.str())); + } + // Search for static libraries too + buildSearchPath(path, dir, _sysrootPath); + llvm::sys::path::append(path, hasColonPrefix + ? libName.drop_front() + : Twine("lib", libName) + ".a"); + if (llvm::sys::fs::exists(path.str())) + return StringRef(*new (_allocator) std::string(path.str())); + } + if (hasColonPrefix && llvm::sys::fs::exists(libName.drop_front())) + return libName.drop_front(); + + return make_error_code(llvm::errc::no_such_file_or_directory); +} + +ErrorOr<StringRef> ELFLinkingContext::searchFile(StringRef fileName, + bool isSysRooted) const { + SmallString<128> path; + if (llvm::sys::path::is_absolute(fileName) && isSysRooted) { + path.assign(_sysrootPath); + path.append(fileName); + if (llvm::sys::fs::exists(path.str())) + return StringRef(*new (_allocator) std::string(path.str())); + } else if (llvm::sys::fs::exists(fileName)) + return fileName; + + if (llvm::sys::path::is_absolute(fileName)) + return make_error_code(llvm::errc::no_such_file_or_directory); + + for (StringRef dir : _inputSearchPaths) { + buildSearchPath(path, dir, _sysrootPath); + llvm::sys::path::append(path, fileName); + if (llvm::sys::fs::exists(path.str())) + return StringRef(*new (_allocator) std::string(path.str())); + } + return make_error_code(llvm::errc::no_such_file_or_directory); +} + +void ELFLinkingContext::createInternalFiles( + std::vector<std::unique_ptr<File>> &files) const { + std::unique_ptr<SimpleFile> file( + new SimpleFile("<internal file for --defsym>")); + for (auto &i : getAbsoluteSymbols()) { + StringRef sym = i.first; + uint64_t val = i.second; + file->addAtom(*(new (_allocator) SimpleAbsoluteAtom( + *file, sym, Atom::scopeGlobal, val))); + } + files.push_back(std::move(file)); + LinkingContext::createInternalFiles(files); +} + +void ELFLinkingContext::finalizeInputFiles() { + // Add virtual archive that resolves undefined symbols. + if (_resolver) + getNodes().push_back(llvm::make_unique<FileNode>(std::move(_resolver))); +} + +std::unique_ptr<File> ELFLinkingContext::createUndefinedSymbolFile() const { + if (_initialUndefinedSymbols.empty()) + return nullptr; + std::unique_ptr<SimpleFile> undefinedSymFile( + new SimpleFile("command line option -u")); + for (auto undefSymStr : _initialUndefinedSymbols) + undefinedSymFile->addAtom(*(new (_allocator) CommandLineUndefinedAtom( + *undefinedSymFile, undefSymStr))); + return std::move(undefinedSymFile); +} + +void ELFLinkingContext::notifySymbolTableCoalesce(const Atom *existingAtom, + const Atom *newAtom, + bool &useNew) { + // First suppose that the `existingAtom` is defined + // and the `newAtom` is undefined. + auto *da = dyn_cast<DefinedAtom>(existingAtom); + auto *ua = dyn_cast<UndefinedAtom>(newAtom); + if (!da && !ua) { + // Then try to reverse the assumption. + da = dyn_cast<DefinedAtom>(newAtom); + ua = dyn_cast<UndefinedAtom>(existingAtom); + } + + if (da && ua && da->scope() == Atom::scopeGlobal && + isa<SharedLibraryFile>(ua->file())) + // If strong defined atom coalesces away an atom declared + // in the shared object the strong atom needs to be dynamically exported. + // Save its name. + _dynamicallyExportedSymbols.insert(ua->name()); +} + +std::string ELFLinkingContext::demangle(StringRef symbolName) const { + if (!demangleSymbols()) + return symbolName; + + // Only try to demangle symbols that look like C++ symbols + if (!symbolName.startswith("_Z")) + return symbolName; + +#if defined(HAVE_CXXABI_H) + SmallString<256> symBuff; + StringRef nullTermSym = Twine(symbolName).toNullTerminatedStringRef(symBuff); + const char *cstr = nullTermSym.data(); + int status; + char *demangled = abi::__cxa_demangle(cstr, nullptr, nullptr, &status); + if (demangled != NULL) { + std::string result(demangled); + // __cxa_demangle() always uses a malloc'ed buffer to return the result. + free(demangled); + return result; + } +#endif + + return symbolName; +} + +void ELFLinkingContext::setUndefinesResolver(std::unique_ptr<File> resolver) { + assert(isa<ArchiveLibraryFile>(resolver.get()) && "Wrong resolver type"); + _resolver = std::move(resolver); +} + +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/ELFReader.h b/lib/ReaderWriter/ELF/ELFReader.h new file mode 100644 index 0000000000000..43f218115c666 --- /dev/null +++ b/lib/ReaderWriter/ELF/ELFReader.h @@ -0,0 +1,102 @@ +//===- lib/ReaderWriter/ELF/ELFReader.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_READER_H +#define LLD_READER_WRITER_ELF_READER_H + +#include "CreateELF.h" +#include "DynamicFile.h" +#include "ELFFile.h" +#include "lld/Core/Reader.h" + +namespace lld { +namespace elf { + +template <typename ELFT, typename ELFTraitsT, typename ContextT> +class ELFObjectReader : public Reader { +public: + typedef llvm::object::Elf_Ehdr_Impl<ELFT> Elf_Ehdr; + + ELFObjectReader(ContextT &ctx, uint64_t machine) + : _ctx(ctx), _machine(machine) {} + + bool canParse(file_magic magic, StringRef, + const MemoryBuffer &buf) const override { + return (magic == llvm::sys::fs::file_magic::elf_relocatable && + elfHeader(buf)->e_machine == _machine); + } + + std::error_code + loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &, + std::vector<std::unique_ptr<File>> &result) const override { + std::size_t maxAlignment = + 1ULL << llvm::countTrailingZeros(uintptr_t(mb->getBufferStart())); + auto f = + createELF<ELFTraitsT>(llvm::object::getElfArchType(mb->getBuffer()), + maxAlignment, std::move(mb), _ctx); + if (std::error_code ec = f.getError()) + return ec; + result.push_back(std::move(*f)); + return std::error_code(); + } + + const Elf_Ehdr *elfHeader(const MemoryBuffer &buf) const { + const uint8_t *data = + reinterpret_cast<const uint8_t *>(buf.getBuffer().data()); + return (reinterpret_cast<const Elf_Ehdr *>(data)); + } + +protected: + ContextT &_ctx; + uint64_t _machine; +}; + +template <typename ELFT, typename ELFTraitsT, typename ContextT> +class ELFDSOReader : public Reader { +public: + typedef llvm::object::Elf_Ehdr_Impl<ELFT> Elf_Ehdr; + + ELFDSOReader(ContextT &ctx, uint64_t machine) + : _ctx(ctx), _machine(machine) {} + + bool canParse(file_magic magic, StringRef, + const MemoryBuffer &buf) const override { + return (magic == llvm::sys::fs::file_magic::elf_shared_object && + elfHeader(buf)->e_machine == _machine); + } + + std::error_code + loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &, + std::vector<std::unique_ptr<File>> &result) const override { + std::size_t maxAlignment = + 1ULL << llvm::countTrailingZeros(uintptr_t(mb->getBufferStart())); + auto f = + createELF<ELFTraitsT>(llvm::object::getElfArchType(mb->getBuffer()), + maxAlignment, std::move(mb), _ctx); + if (std::error_code ec = f.getError()) + return ec; + result.push_back(std::move(*f)); + return std::error_code(); + } + + const Elf_Ehdr *elfHeader(const MemoryBuffer &buf) const { + const uint8_t *data = + reinterpret_cast<const uint8_t *>(buf.getBuffer().data()); + return (reinterpret_cast<const Elf_Ehdr *>(data)); + } + +protected: + ContextT &_ctx; + uint64_t _machine; +}; + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_ELF_READER_H diff --git a/lib/ReaderWriter/ELF/ExecutableWriter.h b/lib/ReaderWriter/ELF/ExecutableWriter.h new file mode 100644 index 0000000000000..477e3920abaee --- /dev/null +++ b/lib/ReaderWriter/ELF/ExecutableWriter.h @@ -0,0 +1,182 @@ +//===- lib/ReaderWriter/ELF/ExecutableWriter.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_EXECUTABLE_WRITER_H +#define LLD_READER_WRITER_ELF_EXECUTABLE_WRITER_H + +#include "OutputELFWriter.h" + +namespace lld { +namespace elf { +using namespace llvm; +using namespace llvm::object; + +template<class ELFT> +class ExecutableWriter; + +//===----------------------------------------------------------------------===// +// ExecutableWriter Class +//===----------------------------------------------------------------------===// +template<class ELFT> +class ExecutableWriter : public OutputELFWriter<ELFT> { +public: + ExecutableWriter(ELFLinkingContext &context, TargetLayout<ELFT> &layout) + : OutputELFWriter<ELFT>(context, layout), + _runtimeFile(new RuntimeFile<ELFT>(context, "C runtime")) {} + +protected: + virtual void buildDynamicSymbolTable(const File &file); + virtual void addDefaultAtoms(); + virtual bool createImplicitFiles(std::vector<std::unique_ptr<File> > &); + virtual void finalizeDefaultAtomValues(); + virtual void createDefaultSections(); + + virtual bool isNeededTagRequired(const SharedLibraryAtom *sla) const { + return this->_layout.isCopied(sla); + } + + unique_bump_ptr<InterpSection<ELFT>> _interpSection; + std::unique_ptr<RuntimeFile<ELFT> > _runtimeFile; +}; + +//===----------------------------------------------------------------------===// +// ExecutableWriter +//===----------------------------------------------------------------------===// +template<class ELFT> +void ExecutableWriter<ELFT>::buildDynamicSymbolTable(const File &file) { + for (auto sec : this->_layout.sections()) + if (auto section = dyn_cast<AtomSection<ELFT>>(sec)) + for (const auto &atom : section->atoms()) { + const DefinedAtom *da = dyn_cast<const DefinedAtom>(atom->_atom); + if (!da) + continue; + if (da->dynamicExport() != DefinedAtom::dynamicExportAlways && + !this->_context.isDynamicallyExportedSymbol(da->name()) && + !(this->_context.shouldExportDynamic() && + da->scope() == Atom::Scope::scopeGlobal)) + continue; + this->_dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), + atom->_virtualAddr, atom); + } + + // Put weak symbols in the dynamic symbol table. + if (this->_context.isDynamic()) { + for (const UndefinedAtom *a : file.undefined()) { + if (this->_layout.isReferencedByDefinedAtom(a) && + a->canBeNull() != UndefinedAtom::canBeNullNever) + this->_dynamicSymbolTable->addSymbol(a, ELF::SHN_UNDEF); + } + } + + OutputELFWriter<ELFT>::buildDynamicSymbolTable(file); +} + +/// \brief Add absolute symbols by default. These are linker added +/// absolute symbols +template<class ELFT> +void ExecutableWriter<ELFT>::addDefaultAtoms() { + OutputELFWriter<ELFT>::addDefaultAtoms(); + _runtimeFile->addUndefinedAtom(this->_context.entrySymbolName()); + _runtimeFile->addAbsoluteAtom("__bss_start"); + _runtimeFile->addAbsoluteAtom("__bss_end"); + _runtimeFile->addAbsoluteAtom("_end"); + _runtimeFile->addAbsoluteAtom("end"); + _runtimeFile->addAbsoluteAtom("__preinit_array_start"); + _runtimeFile->addAbsoluteAtom("__preinit_array_end"); + _runtimeFile->addAbsoluteAtom("__init_array_start"); + _runtimeFile->addAbsoluteAtom("__init_array_end"); + if (this->_context.isRelaOutputFormat()) { + _runtimeFile->addAbsoluteAtom("__rela_iplt_start"); + _runtimeFile->addAbsoluteAtom("__rela_iplt_end"); + } else { + _runtimeFile->addAbsoluteAtom("__rel_iplt_start"); + _runtimeFile->addAbsoluteAtom("__rel_iplt_end"); + } + _runtimeFile->addAbsoluteAtom("__fini_array_start"); + _runtimeFile->addAbsoluteAtom("__fini_array_end"); +} + +/// \brief Hook in lld to add CRuntime file +template <class ELFT> +bool ExecutableWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File> > &result) { + // Add the default atoms as defined by executables + ExecutableWriter<ELFT>::addDefaultAtoms(); + OutputELFWriter<ELFT>::createImplicitFiles(result); + result.push_back(std::move(_runtimeFile)); + return true; +} + +template <class ELFT> void ExecutableWriter<ELFT>::createDefaultSections() { + OutputELFWriter<ELFT>::createDefaultSections(); + if (this->_context.isDynamic()) { + _interpSection.reset(new (this->_alloc) InterpSection<ELFT>( + this->_context, ".interp", DefaultLayout<ELFT>::ORDER_INTERP, + this->_context.getInterpreter())); + this->_layout.addSection(_interpSection.get()); + } +} + +/// Finalize the value of all the absolute symbols that we +/// created +template <class ELFT> void ExecutableWriter<ELFT>::finalizeDefaultAtomValues() { + OutputELFWriter<ELFT>::finalizeDefaultAtomValues(); + auto bssStartAtomIter = this->_layout.findAbsoluteAtom("__bss_start"); + auto bssEndAtomIter = this->_layout.findAbsoluteAtom("__bss_end"); + auto underScoreEndAtomIter = this->_layout.findAbsoluteAtom("_end"); + auto endAtomIter = this->_layout.findAbsoluteAtom("end"); + + auto startEnd = [&](StringRef sym, StringRef sec) -> void { + std::string start = ("__" + sym + "_start").str(); + std::string end = ("__" + sym + "_end").str(); + auto s = this->_layout.findAbsoluteAtom(start); + auto e = this->_layout.findAbsoluteAtom(end); + auto section = this->_layout.findOutputSection(sec); + if (section) { + (*s)->_virtualAddr = section->virtualAddr(); + (*e)->_virtualAddr = section->virtualAddr() + section->memSize(); + } else { + (*s)->_virtualAddr = 0; + (*e)->_virtualAddr = 0; + } + }; + + startEnd("preinit_array", ".preinit_array"); + startEnd("init_array", ".init_array"); + if (this->_context.isRelaOutputFormat()) + startEnd("rela_iplt", ".rela.plt"); + else + startEnd("rel_iplt", ".rel.plt"); + startEnd("fini_array", ".fini_array"); + + assert(!(bssStartAtomIter == this->_layout.absoluteAtoms().end() || + bssEndAtomIter == this->_layout.absoluteAtoms().end() || + underScoreEndAtomIter == this->_layout.absoluteAtoms().end() || + endAtomIter == this->_layout.absoluteAtoms().end()) && + "Unable to find the absolute atoms that have been added by lld"); + + auto bssSection = this->_layout.findOutputSection(".bss"); + + // If we don't find a bss section, then don't set these values + if (bssSection) { + (*bssStartAtomIter)->_virtualAddr = bssSection->virtualAddr(); + (*bssEndAtomIter)->_virtualAddr = + bssSection->virtualAddr() + bssSection->memSize(); + (*underScoreEndAtomIter)->_virtualAddr = (*bssEndAtomIter)->_virtualAddr; + (*endAtomIter)->_virtualAddr = (*bssEndAtomIter)->_virtualAddr; + } else if (auto dataSection = this->_layout.findOutputSection(".data")) { + (*underScoreEndAtomIter)->_virtualAddr = + dataSection->virtualAddr() + dataSection->memSize(); + (*endAtomIter)->_virtualAddr = (*underScoreEndAtomIter)->_virtualAddr; + } +} + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_ELF_EXECUTABLE_WRITER_H diff --git a/lib/ReaderWriter/ELF/HeaderChunks.h b/lib/ReaderWriter/ELF/HeaderChunks.h new file mode 100644 index 0000000000000..eab132b9b2f69 --- /dev/null +++ b/lib/ReaderWriter/ELF/HeaderChunks.h @@ -0,0 +1,364 @@ +//===- lib/ReaderWriter/ELF/HeaderChunks.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_HEADER_CHUNKS_H +#define LLD_READER_WRITER_ELF_HEADER_CHUNKS_H + +#include "SegmentChunks.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/Format.h" + +/// \brief An Header represents the Elf[32/64]_Ehdr structure at the +/// start of an ELF executable file. +namespace lld { +namespace elf { +template <class ELFT> class ELFHeader : public Chunk<ELFT> { +public: + typedef llvm::object::Elf_Ehdr_Impl<ELFT> Elf_Ehdr; + + ELFHeader(const ELFLinkingContext &); + + void e_ident(int I, unsigned char C) { _eh.e_ident[I] = C; } + void e_type(uint16_t type) { _eh.e_type = type; } + void e_machine(uint16_t machine) { _eh.e_machine = machine; } + void e_version(uint32_t version) { _eh.e_version = version; } + void e_entry(int64_t entry) { _eh.e_entry = entry; } + void e_phoff(int64_t phoff) { _eh.e_phoff = phoff; } + void e_shoff(int64_t shoff) { _eh.e_shoff = shoff; } + void e_flags(uint32_t flags) { _eh.e_flags = flags; } + void e_ehsize(uint16_t ehsize) { _eh.e_ehsize = ehsize; } + void e_phentsize(uint16_t phentsize) { _eh.e_phentsize = phentsize; } + void e_phnum(uint16_t phnum) { _eh.e_phnum = phnum; } + void e_shentsize(uint16_t shentsize) { _eh.e_shentsize = shentsize; } + void e_shnum(uint16_t shnum) { _eh.e_shnum = shnum; } + void e_shstrndx(uint16_t shstrndx) { _eh.e_shstrndx = shstrndx; } + uint64_t fileSize() const { return sizeof(Elf_Ehdr); } + + static bool classof(const Chunk<ELFT> *c) { + return c->Kind() == Chunk<ELFT>::Kind::ELFHeader; + } + + int getContentType() const { return Chunk<ELFT>::ContentType::Header; } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer); + + virtual void doPreFlight() {} + + void finalize() { + _eh.e_ident[llvm::ELF::EI_CLASS] = + (ELFT::Is64Bits) ? llvm::ELF::ELFCLASS64 : llvm::ELF::ELFCLASS32; + _eh.e_ident[llvm::ELF::EI_DATA] = + (ELFT::TargetEndianness == llvm::support::little) + ? llvm::ELF::ELFDATA2LSB + : llvm::ELF::ELFDATA2MSB; + _eh.e_type = this->_context.getOutputELFType(); + _eh.e_machine = this->_context.getOutputMachine(); + } + +private: + Elf_Ehdr _eh; +}; + +template <class ELFT> +ELFHeader<ELFT>::ELFHeader(const ELFLinkingContext &context) + : Chunk<ELFT>("elfhdr", Chunk<ELFT>::Kind::ELFHeader, context) { + this->_alignment = ELFT::Is64Bits ? 8 : 4; + this->_fsize = sizeof(Elf_Ehdr); + this->_msize = sizeof(Elf_Ehdr); + memset(_eh.e_ident, 0, llvm::ELF::EI_NIDENT); + e_ident(llvm::ELF::EI_MAG0, 0x7f); + e_ident(llvm::ELF::EI_MAG1, 'E'); + e_ident(llvm::ELF::EI_MAG2, 'L'); + e_ident(llvm::ELF::EI_MAG3, 'F'); + e_ehsize(sizeof(Elf_Ehdr)); + e_flags(0); +} + +template <class ELFT> +void ELFHeader<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *atomContent = chunkBuffer + this->fileOffset(); + memcpy(atomContent, &_eh, fileSize()); +} + +/// \brief An ProgramHeader represents the Elf[32/64]_Phdr structure at the +/// start of an ELF executable file. +template<class ELFT> +class ProgramHeader : public Chunk<ELFT> { +public: + typedef llvm::object::Elf_Phdr_Impl<ELFT> Elf_Phdr; + typedef typename std::vector<Elf_Phdr *>::iterator PhIterT; + typedef typename std::reverse_iterator<PhIterT> ReversePhIterT; + + /// \brief Find a program header entry, given the type of entry that + /// we are looking for + class FindPhdr { + public: + FindPhdr(uint64_t type, uint64_t flags, uint64_t flagsClear) + : _type(type) + , _flags(flags) + , _flagsClear(flagsClear) { + } + + bool operator()(const llvm::object::Elf_Phdr_Impl<ELFT> *j) const { + return ((j->p_type == _type) && + ((j->p_flags & _flags) == _flags) && + (!(j->p_flags & _flagsClear))); + } + private: + uint64_t _type; + uint64_t _flags; + uint64_t _flagsClear; + }; + + ProgramHeader(const ELFLinkingContext &context) + : Chunk<ELFT>("elfphdr", Chunk<ELFT>::Kind::ProgramHeader, context) { + this->_alignment = ELFT::Is64Bits ? 8 : 4; + resetProgramHeaders(); + } + + bool addSegment(Segment<ELFT> *segment); + + void resetProgramHeaders() { _phi = _ph.begin(); } + + uint64_t fileSize() const { return sizeof(Elf_Phdr) * _ph.size(); } + + static bool classof(const Chunk<ELFT> *c) { + return c->Kind() == Chunk<ELFT>::Kind::ProgramHeader; + } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer); + + /// \brief find a program header entry in the list of program headers + ReversePhIterT + findProgramHeader(uint64_t type, uint64_t flags, uint64_t flagClear) { + return std::find_if(_ph.rbegin(), _ph.rend(), + FindPhdr(type, flags, flagClear)); + } + + PhIterT begin() { + return _ph.begin(); + } + + PhIterT end() { + return _ph.end(); + } + + ReversePhIterT rbegin() { return _ph.rbegin(); } + + ReversePhIterT rend() { return _ph.rend(); } + + virtual void doPreFlight() {} + + void finalize() {} + + int64_t entsize() { return sizeof(Elf_Phdr); } + + int64_t numHeaders() { + return _ph.size(); + } + + int getContentType() const { return Chunk<ELFT>::ContentType::Header; } + +private: + Elf_Phdr *allocateProgramHeader(bool &allocatedNew) { + Elf_Phdr *phdr; + if (_phi == _ph.end()) { + phdr = new (_allocator) Elf_Phdr; + _ph.push_back(phdr); + _phi = _ph.end(); + allocatedNew = true; + } else { + phdr = (*_phi); + ++_phi; + } + return phdr; + } + + std::vector<Elf_Phdr *> _ph; + PhIterT _phi; + llvm::BumpPtrAllocator _allocator; +}; + +template <class ELFT> +bool ProgramHeader<ELFT>::addSegment(Segment<ELFT> *segment) { + bool allocatedNew = false; + ELFLinkingContext::OutputMagic outputMagic = this->_context.getOutputMagic(); + // For segments that are not a loadable segment, we + // just pick the values directly from the segment as there + // wouldnt be any slices within that + if (segment->segmentType() != llvm::ELF::PT_LOAD) { + Elf_Phdr *phdr = allocateProgramHeader(allocatedNew); + phdr->p_type = segment->segmentType(); + phdr->p_offset = segment->fileOffset(); + phdr->p_vaddr = segment->virtualAddr(); + phdr->p_paddr = segment->virtualAddr(); + phdr->p_filesz = segment->fileSize(); + phdr->p_memsz = segment->memSize(); + phdr->p_flags = segment->flags(); + phdr->p_align = segment->alignment(); + this->_fsize = fileSize(); + this->_msize = this->_fsize; + return allocatedNew; + } + // For all other segments, use the slice + // to derive program headers + for (auto slice : segment->slices()) { + Elf_Phdr *phdr = allocateProgramHeader(allocatedNew); + phdr->p_type = segment->segmentType(); + phdr->p_offset = slice->fileOffset(); + phdr->p_vaddr = slice->virtualAddr(); + phdr->p_paddr = slice->virtualAddr(); + phdr->p_filesz = slice->fileSize(); + phdr->p_memsz = slice->memSize(); + phdr->p_flags = segment->flags(); + phdr->p_align = slice->alignment(); + uint64_t segPageSize = segment->pageSize(); + uint64_t sliceAlign = slice->alignment(); + // Alignment of PT_LOAD segments are set to the page size, but if the + // alignment of the slice is greater than the page size, set the alignment + // of the segment appropriately. + if (outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && + outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) { + phdr->p_align = (phdr->p_type == llvm::ELF::PT_LOAD) + ? (segPageSize < sliceAlign) ? sliceAlign : segPageSize + : sliceAlign; + } else + phdr->p_align = slice->alignment(); + } + this->_fsize = fileSize(); + this->_msize = this->_fsize; + + return allocatedNew; +} + +template <class ELFT> +void ProgramHeader<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + for (auto phi : _ph) { + memcpy(dest, phi, sizeof(Elf_Phdr)); + dest += sizeof(Elf_Phdr); + } +} + +/// \brief An SectionHeader represents the Elf[32/64]_Shdr structure +/// at the end of the file +template<class ELFT> +class SectionHeader : public Chunk<ELFT> { +public: + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + + SectionHeader(const ELFLinkingContext &, int32_t order); + + void appendSection(OutputSection<ELFT> *section); + + void updateSection(Section<ELFT> *section); + + static bool classof(const Chunk<ELFT> *c) { + return c->getChunkKind() == Chunk<ELFT>::Kind::SectionHeader; + } + + void setStringSection(StringTable<ELFT> *s) { + _stringSection = s; + } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer); + + virtual void doPreFlight() {} + + void finalize() {} + + uint64_t fileSize() const { return sizeof(Elf_Shdr) * _sectionInfo.size(); } + + uint64_t entsize() { return sizeof(Elf_Shdr); } + + int getContentType() const { return Chunk<ELFT>::ContentType::Header; } + + uint64_t numHeaders() { return _sectionInfo.size(); } + +private: + StringTable<ELFT> *_stringSection; + std::vector<Elf_Shdr*> _sectionInfo; + llvm::BumpPtrAllocator _sectionAllocate; +}; + +template <class ELFT> +SectionHeader<ELFT>::SectionHeader(const ELFLinkingContext &context, + int32_t order) + : Chunk<ELFT>("shdr", Chunk<ELFT>::Kind::SectionHeader, context) { + this->_fsize = 0; + this->_alignment = 8; + this->setOrder(order); + // The first element in the list is always NULL + Elf_Shdr *nullshdr = new (_sectionAllocate.Allocate<Elf_Shdr>()) Elf_Shdr; + ::memset(nullshdr, 0, sizeof (Elf_Shdr)); + _sectionInfo.push_back(nullshdr); + this->_fsize += sizeof (Elf_Shdr); +} + +template <class ELFT> +void SectionHeader<ELFT>::appendSection(OutputSection<ELFT> *section) { + Elf_Shdr *shdr = new (_sectionAllocate.Allocate<Elf_Shdr>()) Elf_Shdr; + shdr->sh_name = _stringSection->addString(section->name()); + shdr->sh_type = section->type(); + shdr->sh_flags = section->flags(); + shdr->sh_offset = section->fileOffset(); + shdr->sh_addr = section->virtualAddr(); + if (section->isLoadableSection()) + shdr->sh_size = section->memSize(); + else + shdr->sh_size = section->fileSize(); + shdr->sh_link = section->link(); + shdr->sh_info = section->shinfo(); + shdr->sh_addralign = section->alignment(); + shdr->sh_entsize = section->entsize(); + _sectionInfo.push_back(shdr); +} + +template<class ELFT> +void +SectionHeader<ELFT>::updateSection(Section<ELFT> *section) { + Elf_Shdr *shdr = _sectionInfo[section->ordinal()]; + shdr->sh_type = section->getType(); + shdr->sh_flags = section->getFlags(); + shdr->sh_offset = section->fileOffset(); + shdr->sh_addr = section->virtualAddr(); + shdr->sh_size = section->fileSize(); + shdr->sh_link = section->getLink(); + shdr->sh_info = section->getInfo(); + shdr->sh_addralign = section->alignment(); + shdr->sh_entsize = section->getEntSize(); +} + +template <class ELFT> +void SectionHeader<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + for (auto shi : _sectionInfo) { + memcpy(dest, shi, sizeof(Elf_Shdr)); + dest += sizeof(Elf_Shdr); + } + _stringSection->write(writer, layout, buffer); +} +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Hexagon/CMakeLists.txt b/lib/ReaderWriter/ELF/Hexagon/CMakeLists.txt new file mode 100644 index 0000000000000..6928f43c54592 --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/CMakeLists.txt @@ -0,0 +1,11 @@ +add_llvm_library(lldHexagonELFTarget + HexagonLinkingContext.cpp + HexagonRelocationHandler.cpp + HexagonTargetHandler.cpp + LINK_LIBS + lldELF + lldReaderWriter + lldCore + LLVMObject + LLVMSupport + ) diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonDynamicLibraryWriter.h b/lib/ReaderWriter/ELF/Hexagon/HexagonDynamicLibraryWriter.h new file mode 100644 index 0000000000000..e2d3193045b75 --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonDynamicLibraryWriter.h @@ -0,0 +1,79 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonDynamicLibraryWriter.h ---------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef HEXAGON_DYNAMIC_LIBRARY_WRITER_H +#define HEXAGON_DYNAMIC_LIBRARY_WRITER_H + +#include "DynamicLibraryWriter.h" +#include "HexagonExecutableAtoms.h" +#include "HexagonLinkingContext.h" + +namespace lld { +namespace elf { + +template <typename ELFT> class HexagonTargetLayout; + +template <class ELFT> +class HexagonDynamicLibraryWriter : public DynamicLibraryWriter<ELFT>, + public HexagonELFWriter<ELFT> { +public: + HexagonDynamicLibraryWriter(HexagonLinkingContext &context, + HexagonTargetLayout<ELFT> &layout); + +protected: + // Add any runtime files and their atoms to the output + virtual bool createImplicitFiles(std::vector<std::unique_ptr<File>> &); + + virtual void finalizeDefaultAtomValues(); + + virtual std::error_code setELFHeader() { + DynamicLibraryWriter<ELFT>::setELFHeader(); + HexagonELFWriter<ELFT>::setELFHeader(*this->_elfHeader); + return std::error_code(); + } + +private: + void addDefaultAtoms() { + _hexagonRuntimeFile->addAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); + _hexagonRuntimeFile->addAbsoluteAtom("_DYNAMIC"); + } + + HexagonLinkingContext &_hexagonLinkingContext; + HexagonTargetLayout<ELFT> &_hexagonTargetLayout; + std::unique_ptr<HexagonRuntimeFile<ELFT>> _hexagonRuntimeFile; +}; + +template <class ELFT> +HexagonDynamicLibraryWriter<ELFT>::HexagonDynamicLibraryWriter( + HexagonLinkingContext &context, HexagonTargetLayout<ELFT> &layout) + : DynamicLibraryWriter<ELFT>(context, layout), + HexagonELFWriter<ELFT>(context, layout), _hexagonLinkingContext(context), + _hexagonTargetLayout(layout), + _hexagonRuntimeFile(new HexagonRuntimeFile<ELFT>(context)) {} + +template <class ELFT> +bool HexagonDynamicLibraryWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + DynamicLibraryWriter<ELFT>::createImplicitFiles(result); + // Add the default atoms as defined for hexagon + addDefaultAtoms(); + result.push_back(std::move(_hexagonRuntimeFile)); + return true; +} + +template <class ELFT> +void HexagonDynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues() { + // Finalize the atom values that are part of the parent. + DynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues(); + HexagonELFWriter<ELFT>::finalizeHexagonRuntimeAtomValues(); +} + +} // namespace elf +} // namespace lld + +#endif // HEXAGON_DYNAMIC_LIBRARY_WRITER_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonELFFile.h b/lib/ReaderWriter/ELF/Hexagon/HexagonELFFile.h new file mode 100644 index 0000000000000..ab0b9b432b430 --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonELFFile.h @@ -0,0 +1,170 @@ +//===- lib/ReaderWriter/ELF/HexagonELFFile.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_HEXAGON_ELF_FILE_H +#define LLD_READER_WRITER_ELF_HEXAGON_ELF_FILE_H + +#include "ELFReader.h" +#include "HexagonLinkingContext.h" + +namespace lld { +namespace elf { + +template <class ELFT> class HexagonELFFile; + +template <class ELFT> +class HexagonELFDefinedAtom : public ELFDefinedAtom<ELFT> { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + +public: + HexagonELFDefinedAtom(const HexagonELFFile<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) {} + + virtual DefinedAtom::ContentType contentType() const { + if (this->_contentType != DefinedAtom::typeUnknown) + return this->_contentType; + else if (this->_section->sh_flags & llvm::ELF::SHF_HEX_GPREL) { + if (this->_section->sh_type == llvm::ELF::SHT_NOBITS) + return (this->_contentType = DefinedAtom::typeZeroFillFast); + else + return (this->_contentType = DefinedAtom::typeDataFast); + } + return ELFDefinedAtom<ELFT>::contentType(); + } + + virtual DefinedAtom::ContentPermissions permissions() const { + if (this->_section->sh_flags & llvm::ELF::SHF_HEX_GPREL) + return DefinedAtom::permRW_; + return ELFDefinedAtom<ELFT>::permissions(); + } +}; + +template <class ELFT> class HexagonELFCommonAtom : public ELFCommonAtom<ELFT> { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + +public: + HexagonELFCommonAtom(const HexagonELFFile<ELFT> &file, StringRef symbolName, + const Elf_Sym *symbol) + : ELFCommonAtom<ELFT>(file, symbolName, symbol) {} + + virtual bool isSmallCommonSymbol() const { + switch (this->_symbol->st_shndx) { + // Common symbols + case llvm::ELF::SHN_HEXAGON_SCOMMON: + case llvm::ELF::SHN_HEXAGON_SCOMMON_1: + case llvm::ELF::SHN_HEXAGON_SCOMMON_2: + case llvm::ELF::SHN_HEXAGON_SCOMMON_4: + case llvm::ELF::SHN_HEXAGON_SCOMMON_8: + return true; + default: + break; + } + return false; + } + + virtual uint64_t size() const { + if (isSmallCommonSymbol()) + return this->_symbol->st_size; + return ELFCommonAtom<ELFT>::size(); + } + + virtual DefinedAtom::Merge merge() const { + if (this->_symbol->getBinding() == llvm::ELF::STB_WEAK) + return DefinedAtom::mergeAsWeak; + if (isSmallCommonSymbol()) + return DefinedAtom::mergeAsTentative; + return ELFCommonAtom<ELFT>::merge(); + } + + virtual DefinedAtom::ContentType contentType() const { + if (isSmallCommonSymbol()) + return DefinedAtom::typeZeroFillFast; + return ELFCommonAtom<ELFT>::contentType(); + } + + virtual DefinedAtom::Alignment alignment() const { + if (isSmallCommonSymbol()) + return DefinedAtom::Alignment(llvm::Log2_64(this->_symbol->st_value)); + return ELFCommonAtom<ELFT>::alignment(); + } + + virtual DefinedAtom::ContentPermissions permissions() const { + if (isSmallCommonSymbol()) + return DefinedAtom::permRW_; + return ELFCommonAtom<ELFT>::permissions(); + } +}; + +template <class ELFT> class HexagonELFFile : public ELFFile<ELFT> { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + +public: + HexagonELFFile(std::unique_ptr<MemoryBuffer> mb, HexagonLinkingContext &ctx) + : ELFFile<ELFT>(std::move(mb), ctx) {} + + static ErrorOr<std::unique_ptr<HexagonELFFile>> + create(std::unique_ptr<MemoryBuffer> mb, HexagonLinkingContext &ctx) { + return std::unique_ptr<HexagonELFFile<ELFT>>( + new HexagonELFFile<ELFT>(std::move(mb), ctx)); + } + + bool isCommonSymbol(const Elf_Sym *symbol) const override { + switch (symbol->st_shndx) { + // Common symbols + case llvm::ELF::SHN_HEXAGON_SCOMMON: + case llvm::ELF::SHN_HEXAGON_SCOMMON_1: + case llvm::ELF::SHN_HEXAGON_SCOMMON_2: + case llvm::ELF::SHN_HEXAGON_SCOMMON_4: + case llvm::ELF::SHN_HEXAGON_SCOMMON_8: + return true; + default: + break; + } + return ELFFile<ELFT>::isCommonSymbol(symbol); + } + + /// 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) HexagonELFDefinedAtom<ELFT>( + *this, symName, sectionName, sym, sectionHdr, contentData, + referenceStart, referenceEnd, referenceList); + } + + /// Process the Common symbol and create an atom for it. + ErrorOr<ELFCommonAtom<ELFT> *> + handleCommonSymbol(StringRef symName, const Elf_Sym *sym) override { + return new (this->_readerStorage) + HexagonELFCommonAtom<ELFT>(*this, symName, sym); + } +}; + +template <class ELFT> class HexagonDynamicFile : public DynamicFile<ELFT> { +public: + HexagonDynamicFile(const HexagonLinkingContext &context, StringRef name) + : DynamicFile<ELFT>(context, name) {} +}; + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_HEXAGON_ELF_FILE_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonELFReader.h b/lib/ReaderWriter/ELF/Hexagon/HexagonELFReader.h new file mode 100644 index 0000000000000..1a4f891df7997 --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonELFReader.h @@ -0,0 +1,62 @@ +//===- lib/ReaderWriter/ELF/HexagonELFReader.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_HEXAGON_ELF_READER_H +#define LLD_READER_WRITER_HEXAGON_ELF_READER_H + +#include "ELFReader.h" +#include "HexagonELFFile.h" + +namespace lld { +namespace elf { + +typedef llvm::object::ELFType<llvm::support::little, 2, false> HexagonELFType; + +struct HexagonDynamicFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + HexagonLinkingContext &ctx) { + return lld::elf::HexagonDynamicFile<ELFT>::create(std::move(mb), ctx); + } +}; + +struct HexagonELFFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::File>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + HexagonLinkingContext &ctx) { + return lld::elf::HexagonELFFile<ELFT>::create(std::move(mb), ctx); + } +}; + +class HexagonELFObjectReader + : public ELFObjectReader<HexagonELFType, HexagonELFFileCreateELFTraits, + HexagonLinkingContext> { +public: + HexagonELFObjectReader(HexagonLinkingContext &ctx) + : ELFObjectReader<HexagonELFType, HexagonELFFileCreateELFTraits, + HexagonLinkingContext>(ctx, llvm::ELF::EM_HEXAGON) {} +}; + +class HexagonELFDSOReader + : public ELFDSOReader<HexagonELFType, HexagonDynamicFileCreateELFTraits, + HexagonLinkingContext> { +public: + HexagonELFDSOReader(HexagonLinkingContext &ctx) + : ELFDSOReader<HexagonELFType, HexagonDynamicFileCreateELFTraits, + HexagonLinkingContext>(ctx, llvm::ELF::EM_HEXAGON) {} +}; + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_ELF_READER_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonELFWriters.h b/lib/ReaderWriter/ELF/Hexagon/HexagonELFWriters.h new file mode 100644 index 0000000000000..96c74f72222dc --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonELFWriters.h @@ -0,0 +1,61 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonELFWriters.h -------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef HEXAGON_ELF_WRITERS_H +#define HEXAGON_ELF_WRITERS_H + +#include "HexagonLinkingContext.h" +#include "OutputELFWriter.h" + +namespace lld { +namespace elf { + +template <class ELFT> class HexagonTargetLayout; + +template <typename ELFT> class HexagonELFWriter { +public: + HexagonELFWriter(HexagonLinkingContext &context, + HexagonTargetLayout<ELFT> &targetLayout) + : _hexagonLinkingContext(context), _hexagonTargetLayout(targetLayout) {} + +protected: + bool setELFHeader(ELFHeader<ELFT> &elfHeader) { + elfHeader.e_ident(llvm::ELF::EI_VERSION, 1); + elfHeader.e_ident(llvm::ELF::EI_OSABI, 0); + elfHeader.e_version(1); + elfHeader.e_flags(0x3); + return true; + } + + void finalizeHexagonRuntimeAtomValues() { + if (_hexagonLinkingContext.isDynamic()) { + auto gotAtomIter = + _hexagonTargetLayout.findAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); + auto gotpltSection = _hexagonTargetLayout.findOutputSection(".got.plt"); + if (gotpltSection) + (*gotAtomIter)->_virtualAddr = gotpltSection->virtualAddr(); + else + (*gotAtomIter)->_virtualAddr = 0; + auto dynamicAtomIter = _hexagonTargetLayout.findAbsoluteAtom("_DYNAMIC"); + auto dynamicSection = _hexagonTargetLayout.findOutputSection(".dynamic"); + if (dynamicSection) + (*dynamicAtomIter)->_virtualAddr = dynamicSection->virtualAddr(); + else + (*dynamicAtomIter)->_virtualAddr = 0; + } + } + +private: + HexagonLinkingContext &_hexagonLinkingContext; + HexagonTargetLayout<ELFT> &_hexagonTargetLayout; +}; + +} // elf +} // lld +#endif // HEXAGON_ELF_WRITERS_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonEncodings.h b/lib/ReaderWriter/ELF/Hexagon/HexagonEncodings.h new file mode 100644 index 0000000000000..3e12786704a25 --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonEncodings.h @@ -0,0 +1,601 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonEncodings.h -------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +Instruction insn_encodings[] = { + { 0xffe00004, 0x40000000, 0x20f8, 0x0 }, + { 0xffe03080, 0x9ca03080, 0xf60, 0x0 }, + { 0xf9e00000, 0x48c00000, 0x61f20ff, 0x0 }, + { 0xf7c02300, 0x13802100, 0x3000fe, 0x0 }, + { 0xffe00000, 0x60c00000, 0x1f18, 0x0 }, + { 0xffe00000, 0x69c00000, 0x1f18, 0x0 }, + { 0xffe02000, 0x43000000, 0x7e0, 0x0 }, + { 0xff602060, 0x3e000060, 0x1f80, 0x0 }, + { 0xffe03000, 0x9ae01000, 0xf60, 0x0 }, + { 0xf9e00000, 0x91600000, 0x6003fe0, 0x0 }, + { 0xffe02084, 0xaf000084, 0x30078, 0x0 }, + { 0xff602060, 0x3e000020, 0x1f80, 0x0 }, + { 0xff602060, 0x3e200040, 0x1f80, 0x0 }, + { 0xf7c02000, 0x10c02000, 0x3000fe, 0x0 }, + { 0xffe00000, 0x60200000, 0x1f18, 0x0 }, + { 0xffe00000, 0x69200000, 0x1f18, 0x0 }, + { 0xffe038c0, 0xada00880, 0x3f, 0x0 }, + { 0xff602000, 0x73002000, 0x1fe0, 0x0 }, + { 0xf7c02000, 0x26c02000, 0x3000fe, 0x0 }, + { 0xffe03880, 0x9f403880, 0x1f0100, 0x0 }, + { 0xf9e00000, 0x48400000, 0x61f20ff, 0x0 }, + { 0xffe02000, 0x41600000, 0x7e0, 0x0 }, + { 0xffe02084, 0xaf000080, 0x30078, 0x0 }, + { 0xf7c02300, 0x13800100, 0x3000fe, 0x0 }, + { 0xffe01804, 0x46a00000, 0x20f8, 0x0 }, + { 0xffe00004, 0x42400000, 0x20f8, 0x0 }, + { 0xf7c02000, 0x22400000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x12402000, 0x3000fe, 0x0 }, + { 0xfc003d18, 0x28003c18, 0x3f00000, 0x1 }, + { 0xffe00000, 0x39000000, 0x201f, 0x0 }, + { 0xff601018, 0xdd400008, 0xfe0, 0x0 }, + { 0xffc0001c, 0x75400000, 0x203fe0, 0x0 }, + { 0xfc003fc7, 0x48003f47, 0x3f00000, 0x1 }, + { 0xffe03080, 0x9ca03000, 0xf60, 0x0 }, + { 0xf9e00000, 0x90800000, 0x6003fe0, 0x0 }, + { 0xf8003fc7, 0x40003fc4, 0x7f00000, 0x1 }, + { 0xfc003e00, 0x68003c00, 0x3f00000, 0x1 }, + { 0xf8003fc7, 0x40003fc5, 0x7f00000, 0x1 }, + { 0xf9e00000, 0x91800000, 0x6003fe0, 0x0 }, + { 0xff602060, 0x3e400060, 0x1f80, 0x0 }, + { 0xff602060, 0x3e000000, 0x1f80, 0x0 }, + { 0xf8003d18, 0x20003c18, 0x7f00000, 0x1 }, + { 0xf8003f00, 0x20003800, 0x7f00000, 0x1 }, + { 0xf8003d18, 0x20003c10, 0x7f00000, 0x1 }, + { 0xff602000, 0x73602000, 0x1fe0, 0x0 }, + { 0xffe03880, 0x9f002080, 0x1f0100, 0x0 }, + { 0xffe02000, 0x47000000, 0x7e0, 0x0 }, + { 0xf9e00000, 0x91400000, 0x6003fe0, 0x0 }, + { 0xffe02080, 0xabc00080, 0x3f, 0x0 }, + { 0xf7c02000, 0x20802000, 0x3000fe, 0x0 }, + { 0xf8003fc7, 0x40003f44, 0x7f00000, 0x1 }, + { 0xffe03884, 0xafa03084, 0x30078, 0x0 }, + { 0xffe03000, 0x9b001000, 0xf60, 0x0 }, + { 0xffe01804, 0x42a00800, 0x20f8, 0x0 }, + { 0xfc003f00, 0x28003100, 0x3f00000, 0x1 }, + { 0xffe02080, 0xab800080, 0x3f, 0x0 }, + { 0xf7c02000, 0x24c00000, 0x3000fe, 0x0 }, + { 0xffe00000, 0x39a00000, 0x201f, 0x0 }, + { 0xf7c02300, 0x13802300, 0x3000fe, 0x0 }, + { 0xffe01804, 0x46a00800, 0x20f8, 0x0 }, + { 0xffe020c0, 0xad602080, 0x3f, 0x0 }, + { 0xfc003f00, 0x28003500, 0x3f00000, 0x1 }, + { 0xfc003f00, 0x28003400, 0x3f00000, 0x1 }, + { 0xffe020c0, 0xad6000c0, 0x3f, 0x0 }, + { 0xffe00000, 0x60000000, 0x1f18, 0x0 }, + { 0xf8003000, 0x40000000, 0x7f00000, 0x1 }, + { 0xffe00000, 0x69000000, 0x1f18, 0x0 }, + { 0xffe03080, 0x9c601080, 0xf60, 0x0 }, + { 0xffe03080, 0x9ce01000, 0xf60, 0x0 }, + { 0xffe03080, 0x9c601000, 0xf60, 0x0 }, + { 0xf7c02000, 0x13402000, 0x3000fe, 0x0 }, + { 0xffe03080, 0x9c603000, 0xf60, 0x0 }, + { 0xf7c02000, 0x21c00000, 0x3000fe, 0x0 }, + { 0xfc003000, 0x68000000, 0x3f00000, 0x1 }, + { 0xf8003800, 0x60002000, 0x7f00000, 0x1 }, + { 0xffe02084, 0xaf802084, 0x30078, 0x0 }, + { 0xfc003000, 0x48000000, 0x3f00000, 0x1 }, + { 0xf7c02300, 0x11c02100, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x12800000, 0x3000fe, 0x0 }, + { 0xfc003e70, 0x28003a40, 0x3f00000, 0x1 }, + { 0xfc003f00, 0x28003300, 0x3f00000, 0x1 }, + { 0xff800000, 0xe0000000, 0x1fe0, 0x0 }, + { 0xff602060, 0x3f400000, 0x1f80, 0x0 }, + { 0xffe00004, 0x42000000, 0x20f8, 0x0 }, + { 0xf8003f00, 0x60003300, 0x7f00000, 0x1 }, + { 0xffe01804, 0x42a00000, 0x20f8, 0x0 }, + { 0xf7c02000, 0x12c00000, 0x3000fe, 0x0 }, + { 0xf0000000, 0x0, 0xfff3fff, 0x0 }, + { 0xff000016, 0xde000016, 0xe020e8, 0x0 }, + { 0xffe03000, 0x9b201000, 0xf60, 0x0 }, + { 0xffe03880, 0xaba00880, 0x3f, 0x0 }, + { 0xf8003e00, 0x40003c00, 0x7f00000, 0x1 }, + { 0xff602060, 0x3f200040, 0x1f80, 0x0 }, + { 0xffe03880, 0x9f203880, 0x1f0100, 0x0 }, + { 0xf7c02000, 0x20c00000, 0x3000fe, 0x0 }, + { 0xf9e01800, 0x48a00800, 0x61f20ff, 0x0 }, + { 0xf9e00000, 0x90a00000, 0x6003fe0, 0x0 }, + { 0xff802000, 0x74802000, 0x1fe0, 0x0 }, + { 0xffe03000, 0x9a401000, 0xf60, 0x0 }, + { 0xf7c02000, 0x10002000, 0x3000fe, 0x0 }, + { 0xf7c03000, 0x14803000, 0x3000fe, 0x0 }, + { 0xffe020c0, 0xad0020c0, 0x3f, 0x0 }, + { 0xffe0001c, 0x75800000, 0x3fe0, 0x0 }, + { 0xf9e01800, 0x48a01000, 0x61f20ff, 0x0 }, + { 0xffe03080, 0x9dc03000, 0xf60, 0x0 }, + { 0xffe03080, 0x9dc03080, 0xf60, 0x0 }, + { 0xffe03080, 0x9dc01000, 0xf60, 0x0 }, + { 0xffe03080, 0x9dc01080, 0xf60, 0x0 }, + { 0xffe03080, 0x9d601000, 0xf60, 0x0 }, + { 0xffe03080, 0x9d601080, 0xf60, 0x0 }, + { 0xffe03080, 0x9d603000, 0xf60, 0x0 }, + { 0xffe03080, 0x9d603080, 0xf60, 0x0 }, + { 0xfc003e00, 0x48003c00, 0x3f00000, 0x1 }, + { 0xffe02084, 0xaf402084, 0x30078, 0x0 }, + { 0xffe00004, 0x46600000, 0x20f8, 0x0 }, + { 0xffe03880, 0x9f203080, 0x1f0100, 0x0 }, + { 0xf8003f00, 0x20003100, 0x7f00000, 0x1 }, + { 0xf7c02000, 0x11402000, 0x3000fe, 0x0 }, + { 0xf8003d08, 0x20003d00, 0x7f00000, 0x1 }, + { 0xffe03080, 0x9ca01080, 0xf60, 0x0 }, + { 0xffe03080, 0x9ca01000, 0xf60, 0x0 }, + { 0xffe00000, 0x38a00000, 0x201f, 0x0 }, + { 0xf7c02300, 0x11800000, 0x3000fe, 0x0 }, + { 0xf7c02300, 0x13c02300, 0x3000fe, 0x0 }, + { 0xffe03080, 0x9ce03000, 0xf60, 0x0 }, + { 0xf9e00000, 0x90e00000, 0x6003fe0, 0x0 }, + { 0xffe02084, 0xaf400080, 0x30078, 0x0 }, + { 0xffe03080, 0x9ce03080, 0xf60, 0x0 }, + { 0xff000000, 0x78000000, 0xdf3fe0, 0x0 }, + { 0xffe03080, 0x9ce01080, 0xf60, 0x0 }, + { 0xffe03880, 0xaba01080, 0x3f, 0x0 }, + { 0xffe020c0, 0xad002080, 0x3f, 0x0 }, + { 0xffe020c0, 0xad0000c0, 0x3f, 0x0 }, + { 0xffe020c0, 0xad000080, 0x3f, 0x0 }, + { 0xf7c02000, 0x25000000, 0x3000fe, 0x0 }, + { 0xff602060, 0x3f200020, 0x1f80, 0x0 }, + { 0xffe02084, 0xafc00084, 0x30078, 0x0 }, + { 0xf7c02000, 0x24400000, 0x3000fe, 0x0 }, + { 0xfc003000, 0x48001000, 0x3f00000, 0x1 }, + { 0xf9e01800, 0xa1a01000, 0x60020ff, 0x0 }, + { 0xff602060, 0x3f000040, 0x1f80, 0x0 }, + { 0xffe02084, 0xaf602084, 0x30078, 0x0 }, + { 0xf8003f00, 0x20003400, 0x7f00000, 0x1 }, + { 0xffe02084, 0xaf400084, 0x30078, 0x0 }, + { 0xffe01804, 0x44a01000, 0x20f8, 0x0 }, + { 0xff602060, 0x3e200000, 0x1f80, 0x0 }, + { 0xf8003e70, 0x20003a70, 0x7f00000, 0x1 }, + { 0xf8003f00, 0x40003e00, 0x7f00000, 0x1 }, + { 0xf8003f00, 0x20003300, 0x7f00000, 0x1 }, + { 0xf7c02300, 0x13800300, 0x3000fe, 0x0 }, + { 0xffe038c0, 0xada00080, 0x3f, 0x0 }, + { 0xf9e00000, 0x49400000, 0x61f3fe0, 0x0 }, + { 0xf8003800, 0x40002800, 0x7f00000, 0x1 }, + { 0xffe038c0, 0xada020c0, 0x3f, 0x0 }, + { 0xffe03884, 0xafa00880, 0x30078, 0x0 }, + { 0xf9e00000, 0x49000000, 0x61f3fe0, 0x0 }, + { 0xff800000, 0xd7000000, 0x6020e0, 0x0 }, + { 0xffc00000, 0xda000000, 0x203fe0, 0x0 }, + { 0xf7c02000, 0x12802000, 0x3000fe, 0x0 }, + { 0xf9e00000, 0x49600000, 0x61f3fe0, 0x0 }, + { 0xffe02000, 0x47400000, 0x7e0, 0x0 }, + { 0xf9e00000, 0x49c00000, 0x61f3fe0, 0x0 }, + { 0xffe03000, 0x9bc01000, 0xf60, 0x0 }, + { 0xf7c02300, 0x13c00100, 0x3000fe, 0x0 }, + { 0xffe03880, 0x9f002880, 0x1f0100, 0x0 }, + { 0xffe03000, 0x9b601000, 0xf60, 0x0 }, + { 0xffe01804, 0x40a00800, 0x20f8, 0x0 }, + { 0xffe00004, 0x42800000, 0x20f8, 0x0 }, + { 0xf7c03000, 0x14800000, 0x3000fe, 0x0 }, + { 0xfc003000, 0x68001000, 0x3f00000, 0x1 }, + { 0xfc003fc7, 0x48003f44, 0x3f00000, 0x1 }, + { 0xfc003fc7, 0x48003f45, 0x3f00000, 0x1 }, + { 0xf7c02000, 0x10800000, 0x3000fe, 0x0 }, + { 0xf8003e70, 0x20003a50, 0x7f00000, 0x1 }, + { 0xf7c02000, 0x21002000, 0x3000fe, 0x0 }, + { 0xf8003fc4, 0x40003fc0, 0x7f00000, 0x1 }, + { 0xf9e00000, 0x48000000, 0x61f20ff, 0x0 }, + { 0xffc0001c, 0x75000010, 0x203fe0, 0x0 }, + { 0xf8003f00, 0x20003800, 0x7f00000, 0x1 }, + { 0xf9e00000, 0xa1800000, 0x60020ff, 0x0 }, + { 0xffc01000, 0x61c00000, 0x202ffe, 0x0 }, + { 0xffe02084, 0xaf402080, 0x30078, 0x0 }, + { 0xffe03880, 0x9f602880, 0x1f0100, 0x0 }, + { 0xfc003f00, 0x68003000, 0x3f00000, 0x1 }, + { 0xfc003f00, 0x68003100, 0x3f00000, 0x1 }, + { 0xff602060, 0x3f200000, 0x1f80, 0x0 }, + { 0xffe03000, 0x9a801000, 0xf60, 0x0 }, + { 0xf7c02000, 0x24802000, 0x3000fe, 0x0 }, + { 0xffe00004, 0x42c00000, 0x20f8, 0x0 }, + { 0xf7c02300, 0x11802000, 0x3000fe, 0x0 }, + { 0xffc01000, 0x61401000, 0x202ffe, 0x0 }, + { 0xffe02000, 0x43c00000, 0x7e0, 0x0 }, + { 0xf7c02000, 0x11400000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x21800000, 0x3000fe, 0x0 }, + { 0xfc003c00, 0x28002c00, 0x3f00000, 0x1 }, + { 0xfc003f00, 0x28003200, 0x3f00000, 0x1 }, + { 0xffe03080, 0x9c803080, 0xf60, 0x0 }, + { 0xf7c03000, 0x14c03000, 0x3000fe, 0x0 }, + { 0xff800000, 0xdb800000, 0x6020e0, 0x0 }, + { 0xf7c02000, 0x22402000, 0x3000fe, 0x0 }, + { 0xffe00004, 0x46800000, 0x20f8, 0x0 }, + { 0xffe00000, 0x69a00000, 0x1f18, 0x0 }, + { 0xfc003e00, 0x68002a00, 0x3f00000, 0x1 }, + { 0xffe00000, 0x60a00000, 0x1f18, 0x0 }, + { 0xf7c02000, 0x25400000, 0x3000fe, 0x0 }, + { 0xfc003e70, 0x28003a70, 0x3f00000, 0x1 }, + { 0xffe03080, 0x9c803000, 0xf60, 0x0 }, + { 0xffc01000, 0x61400000, 0x202ffe, 0x0 }, + { 0xffe01804, 0x42a01000, 0x20f8, 0x0 }, + { 0xffc0001c, 0x75000000, 0x203fe0, 0x0 }, + { 0xffe02084, 0xafc02080, 0x30078, 0x0 }, + { 0xffe03884, 0xafa00884, 0x30078, 0x0 }, + { 0xffe03884, 0xafa02080, 0x30078, 0x0 }, + { 0xffe00000, 0x38c00000, 0x201f, 0x0 }, + { 0xffc01000, 0x61001000, 0x202ffe, 0x0 }, + { 0xf9e00000, 0x48800000, 0x61f20ff, 0x0 }, + { 0xf8003800, 0x40003000, 0x7f00000, 0x1 }, + { 0xf7c03000, 0x15403000, 0x3000fe, 0x0 }, + { 0xf7c03000, 0x15400000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x21000000, 0x3000fe, 0x0 }, + { 0xffe00004, 0x40c00000, 0x20f8, 0x0 }, + { 0xffe01804, 0x46a01000, 0x20f8, 0x0 }, + { 0xf8003d08, 0x20003d08, 0x7f00000, 0x1 }, + { 0xffe038c0, 0xada02080, 0x3f, 0x0 }, + { 0xffe03080, 0x9c203000, 0xf60, 0x0 }, + { 0xfc003800, 0x68002000, 0x3f00000, 0x1 }, + { 0xf9e00000, 0x90600000, 0x6003fe0, 0x0 }, + { 0xf7c03000, 0x14000000, 0x3000fe, 0x0 }, + { 0xf8003e70, 0x20003a40, 0x7f00000, 0x1 }, + { 0xff201800, 0x5c000800, 0xdf20fe, 0x0 }, + { 0xffe02000, 0x41800000, 0x7e0, 0x0 }, + { 0xff800000, 0xdb000000, 0x6020e0, 0x0 }, + { 0xfc003f00, 0x48003e00, 0x3f00000, 0x1 }, + { 0xf7c03000, 0x14002000, 0x3000fe, 0x0 }, + { 0xf7c02300, 0x11800100, 0x3000fe, 0x0 }, + { 0xfc003e00, 0x68002800, 0x3f00000, 0x1 }, + { 0xffe00004, 0x44c00000, 0x20f8, 0x0 }, + { 0xffe03880, 0x9f003880, 0x1f0100, 0x0 }, + { 0xff602000, 0x73402000, 0x1fe0, 0x0 }, + { 0xffe00000, 0x38200000, 0x201f, 0x0 }, + { 0xf7c02000, 0x24800000, 0x3000fe, 0x0 }, + { 0xf7c03000, 0x15001000, 0x3000fe, 0x0 }, + { 0xff800000, 0x7c800000, 0x1f2000, 0x0 }, + { 0xf8003fc7, 0x40003fc6, 0x7f00000, 0x1 }, + { 0xf7c02000, 0x12000000, 0x3000fe, 0x0 }, + { 0xff602000, 0x73202000, 0x1fe0, 0x0 }, + { 0xf7c02300, 0x13c00000, 0x3000fe, 0x0 }, + { 0xff602060, 0x3f400040, 0x1f80, 0x0 }, + { 0xf7c02000, 0x24002000, 0x3000fe, 0x0 }, + { 0xffe02084, 0xaf800080, 0x30078, 0x0 }, + { 0xffe00000, 0x38800000, 0x201f, 0x0 }, + { 0xfc003f00, 0x28003800, 0x3f00000, 0x1 }, + { 0xffe03080, 0x9c801080, 0xf60, 0x0 }, + { 0xffe020c0, 0xad4000c0, 0x3f, 0x0 }, + { 0xffe00000, 0x39400000, 0x201f, 0x0 }, + { 0xf7c02300, 0x13c02100, 0x3000fe, 0x0 }, + { 0xffe020c0, 0xad400080, 0x3f, 0x0 }, + { 0xffe03880, 0x9f603880, 0x1f0100, 0x0 }, + { 0xff000016, 0xde000002, 0xe020e8, 0x0 }, + { 0xfc003d08, 0x28003d00, 0x3f00000, 0x1 }, + { 0xfc003f00, 0x28003000, 0x3f00000, 0x1 }, + { 0xffe03080, 0x9c401000, 0xf60, 0x0 }, + { 0xf7c02000, 0x21402000, 0x3000fe, 0x0 }, + { 0xff201800, 0x5c200800, 0xdf20fe, 0x0 }, + { 0xffe01804, 0x40a01000, 0x20f8, 0x0 }, + { 0xfc003f00, 0x68003300, 0x3f00000, 0x1 }, + { 0xfc003f00, 0x68003200, 0x3f00000, 0x1 }, + { 0xf7c03000, 0x15401000, 0x3000fe, 0x0 }, + { 0xffe01804, 0x44a00800, 0x20f8, 0x0 }, + { 0xf7c02000, 0x26000000, 0x3000fe, 0x0 }, + { 0xffc00000, 0xda400000, 0x203fe0, 0x0 }, + { 0xffe00004, 0x40600000, 0x20f8, 0x0 }, + { 0xffe02080, 0xab600080, 0x3f, 0x0 }, + { 0xf8003f00, 0x20003600, 0x7f00000, 0x1 }, + { 0xf7c02300, 0x11c00300, 0x3000fe, 0x0 }, + { 0xf8003f00, 0x20003700, 0x7f00000, 0x1 }, + { 0xf7c02000, 0x25c00000, 0x3000fe, 0x0 }, + { 0xf7c02300, 0x11800300, 0x3000fe, 0x0 }, + { 0xffe03880, 0x9f802880, 0x1f0100, 0x0 }, + { 0xfc003800, 0x48003000, 0x3f00000, 0x1 }, + { 0xf8003c00, 0x20002c00, 0x7f00000, 0x1 }, + { 0xf7c02000, 0x10400000, 0x3000fe, 0x0 }, + { 0xff602060, 0x3f400060, 0x1f80, 0x0 }, + { 0xffe03080, 0x9c801000, 0xf60, 0x0 }, + { 0xff602060, 0x3e400040, 0x1f80, 0x0 }, + { 0xf7c03000, 0x14402000, 0x3000fe, 0x0 }, + { 0xffe0001c, 0x75800010, 0x3fe0, 0x0 }, + { 0xff000016, 0xde000014, 0xe020e8, 0x0 }, + { 0xf7c02300, 0x11c02000, 0x3000fe, 0x0 }, + { 0xff600018, 0xdd200008, 0x1fe0, 0x0 }, + { 0xff602060, 0x3e200060, 0x1f80, 0x0 }, + { 0xff000016, 0xde000006, 0xe020e8, 0x0 }, + { 0xffe00004, 0x44600000, 0x20f8, 0x0 }, + { 0xf8003e00, 0x60002800, 0x7f00000, 0x1 }, + { 0xfe600000, 0x3c000000, 0x207f, 0x0 }, + { 0xffe03884, 0xafa02884, 0x30078, 0x0 }, + { 0xf7c02300, 0x11802300, 0x3000fe, 0x0 }, + { 0xffe00000, 0x38000000, 0x201f, 0x0 }, + { 0xff200800, 0x5c000000, 0xdf20fe, 0x0 }, + { 0xf7c02000, 0x13400000, 0x3000fe, 0x0 }, + { 0xff200800, 0x5c200000, 0xdf20fe, 0x0 }, + { 0xffe02000, 0x41000000, 0x7e0, 0x0 }, + { 0xffe03880, 0x9fc02880, 0x1f0100, 0x0 }, + { 0xffe00004, 0x46000000, 0x20f8, 0x0 }, + { 0xff602060, 0x3f000020, 0x1f80, 0x0 }, + { 0xfc003d08, 0x28003d08, 0x3f00000, 0x1 }, + { 0xff602060, 0x3f200060, 0x1f80, 0x0 }, + { 0xffe038c0, 0xada028c0, 0x3f, 0x0 }, + { 0xffe038c0, 0xada008c0, 0x3f, 0x0 }, + { 0xf8003f00, 0x20003500, 0x7f00000, 0x1 }, + { 0xfc003fc4, 0x48003f40, 0x3f00000, 0x1 }, + { 0xf9e01800, 0x48a00000, 0x61f20ff, 0x0 }, + { 0xf7c03000, 0x14802000, 0x3000fe, 0x0 }, + { 0xfc003f00, 0x28003900, 0x3f00000, 0x1 }, + { 0xf8003fc7, 0x40003fc7, 0x7f00000, 0x1 }, + { 0xffe02000, 0x45400000, 0x7e0, 0x0 }, + { 0xffe038c0, 0xada02880, 0x3f, 0x0 }, + { 0xffe02084, 0xaf002080, 0x30078, 0x0 }, + { 0xffe03880, 0x9f803880, 0x1f0100, 0x0 }, + { 0xf7c03000, 0x15000000, 0x3000fe, 0x0 }, + { 0xfc003f00, 0x28003700, 0x3f00000, 0x1 }, + { 0xfc003f00, 0x28003600, 0x3f00000, 0x1 }, + { 0xffe02000, 0x47200000, 0x7e0, 0x0 }, + { 0xffe03880, 0xaba00080, 0x3f, 0x0 }, + { 0xffe02084, 0xafc00080, 0x30078, 0x0 }, + { 0xff802000, 0x73800000, 0x1fe0, 0x0 }, + { 0xffe03880, 0x9f202880, 0x1f0100, 0x0 }, + { 0xf8003d18, 0x20003c00, 0x7f00000, 0x1 }, + { 0xf9e00000, 0xa1600000, 0x60020ff, 0x0 }, + { 0xffe00004, 0x44800000, 0x20f8, 0x0 }, + { 0xf7c02000, 0x21802000, 0x3000fe, 0x0 }, + { 0xff000000, 0xd8000000, 0x6020e0, 0x0 }, + { 0xf9e00000, 0xa1000000, 0x60020ff, 0x0 }, + { 0xffe03884, 0xafa00084, 0x30078, 0x0 }, + { 0xff201800, 0x5c201800, 0xdf20fe, 0x0 }, + { 0xff000016, 0xde000010, 0xe020e8, 0x0 }, + { 0xffe03880, 0x9f603080, 0x1f0100, 0x0 }, + { 0xffe02000, 0x41c00000, 0x7e0, 0x0 }, + { 0xf7c02000, 0x20402000, 0x3000fe, 0x0 }, + { 0xff800000, 0xe1000000, 0x1fe0, 0x0 }, + { 0xf9e00000, 0xa1400000, 0x60020ff, 0x0 }, + { 0xf7c03000, 0x14c00000, 0x3000fe, 0x0 }, + { 0xf8003fc7, 0x40003f47, 0x7f00000, 0x1 }, + { 0xffe00004, 0x40800000, 0x20f8, 0x0 }, + { 0xff800000, 0xe1800000, 0x1fe0, 0x0 }, + { 0xf7c02300, 0x11802100, 0x3000fe, 0x0 }, + { 0xf9e00000, 0x49800000, 0x61f3fe0, 0x0 }, + { 0xf7c02000, 0x26400000, 0x3000fe, 0x0 }, + { 0xf8003c00, 0x20002800, 0x7f00000, 0x1 }, + { 0xff902000, 0x7e002000, 0xf1fe0, 0x0 }, + { 0xff902000, 0x7e802000, 0xf1fe0, 0x0 }, + { 0xf9e00000, 0x91c00000, 0x6003fe0, 0x0 }, + { 0xffe03884, 0xafa02880, 0x30078, 0x0 }, + { 0xf7c02000, 0x22000000, 0x3000fe, 0x0 }, + { 0xffe03080, 0x9d203000, 0xf60, 0x0 }, + { 0xf7c02000, 0x26002000, 0x3000fe, 0x0 }, + { 0xff800000, 0xe2000000, 0x1fe0, 0x0 }, + { 0xf7c02000, 0x26c00000, 0x3000fe, 0x0 }, + { 0xff602060, 0x3e400000, 0x1f80, 0x0 }, + { 0xffe00000, 0x38400000, 0x201f, 0x0 }, + { 0xfc003800, 0x48002000, 0x3f00000, 0x1 }, + { 0xff000016, 0xde000000, 0xe020e8, 0x0 }, + { 0xf8003f00, 0x20003000, 0x7f00000, 0x1 }, + { 0xf8003e70, 0x20003a60, 0x7f00000, 0x1 }, + { 0xff902000, 0x7e800000, 0xf1fe0, 0x0 }, + { 0xffe020c0, 0xad6020c0, 0x3f, 0x0 }, + { 0xf7c02300, 0x13802000, 0x3000fe, 0x0 }, + { 0xffe020c0, 0xad600080, 0x3f, 0x0 }, + { 0xff902000, 0x7e000000, 0xf1fe0, 0x0 }, + { 0xf7000000, 0x17000000, 0x3000fe, 0x0 }, + { 0xf7000000, 0x16000000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x25002000, 0x3000fe, 0x0 }, + { 0xfc003fc7, 0x48003fc7, 0x3f00000, 0x1 }, + { 0xffc01000, 0x61801000, 0x202ffe, 0x0 }, + { 0xffe03884, 0xafa03080, 0x30078, 0x0 }, + { 0xf8003fc4, 0x40003f40, 0x7f00000, 0x1 }, + { 0xfc003e70, 0x28003a60, 0x3f00000, 0x1 }, + { 0xf7c02300, 0x13800000, 0x3000fe, 0x0 }, + { 0xffe03880, 0x9f802080, 0x1f0100, 0x0 }, + { 0xf0000000, 0xb0000000, 0xfe03fe0, 0x0 }, + { 0xffe03880, 0x9f402080, 0x1f0100, 0x0 }, + { 0xffe02000, 0x43200000, 0x7e0, 0x0 }, + { 0xffe00000, 0x39800000, 0x201f, 0x0 }, + { 0xffe03880, 0x9fc03880, 0x1f0100, 0x0 }, + { 0xffe02000, 0x45600000, 0x7e0, 0x0 }, + { 0xf9e00000, 0x91200000, 0x6003fe0, 0x0 }, + { 0xffe02000, 0x43600000, 0x7e0, 0x0 }, + { 0xfc003f00, 0x28003800, 0x3f00000, 0x1 }, + { 0xff802000, 0x74000000, 0x1fe0, 0x0 }, + { 0xffe02084, 0xaf002084, 0x30078, 0x0 }, + { 0xff802000, 0x74800000, 0x1fe0, 0x0 }, + { 0xf7c03000, 0x14c02000, 0x3000fe, 0x0 }, + { 0xfe000001, 0x5a000000, 0x1ff3ffe, 0x0 }, + { 0xff602060, 0x3f400020, 0x1f80, 0x0 }, + { 0xf7c02000, 0x10802000, 0x3000fe, 0x0 }, + { 0xffe02084, 0xaf802080, 0x30078, 0x0 }, + { 0xffe00004, 0x46400000, 0x20f8, 0x0 }, + { 0xffe020c0, 0xad800080, 0x3f, 0x0 }, + { 0xffe020c0, 0xad8000c0, 0x3f, 0x0 }, + { 0xf8003fc7, 0x40003f45, 0x7f00000, 0x1 }, + { 0xf8003e00, 0x60002a00, 0x7f00000, 0x1 }, + { 0xffe02084, 0xaf600084, 0x30078, 0x0 }, + { 0xffe03080, 0x9c201000, 0xf60, 0x0 }, + { 0xffe02000, 0x43400000, 0x7e0, 0x0 }, + { 0xffe03080, 0x9c203080, 0xf60, 0x0 }, + { 0xffe02000, 0x41200000, 0x7e0, 0x0 }, + { 0xffe03080, 0x9c201080, 0xf60, 0x0 }, + { 0xf7c02300, 0x11c02300, 0x3000fe, 0x0 }, + { 0xffe03880, 0x9fc03080, 0x1f0100, 0x0 }, + { 0xffe03880, 0x9f402880, 0x1f0100, 0x0 }, + { 0xf8003800, 0x40002000, 0x7f00000, 0x1 }, + { 0xf7c02000, 0x24402000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x20c02000, 0x3000fe, 0x0 }, + { 0xf7c02300, 0x11c00000, 0x3000fe, 0x0 }, + { 0xffe02000, 0x45200000, 0x7e0, 0x0 }, + { 0xf8003f00, 0x20003900, 0x7f00000, 0x1 }, + { 0xf7c02300, 0x11c00100, 0x3000fe, 0x0 }, + { 0xffe02084, 0xaf800084, 0x30078, 0x0 }, + { 0xfe600000, 0x3c200000, 0x207f, 0x0 }, + { 0xf7c02000, 0x26800000, 0x3000fe, 0x0 }, + { 0xffe03880, 0x9f003080, 0x1f0100, 0x0 }, + { 0xffe03884, 0xafa01084, 0x30078, 0x0 }, + { 0xffc00000, 0x76000000, 0x203fe0, 0x0 }, + { 0xff602060, 0x3e000040, 0x1f80, 0x0 }, + { 0xffe020c0, 0xadc020c0, 0x3f, 0x0 }, + { 0xffe00004, 0x44400000, 0x20f8, 0x0 }, + { 0xffe020c0, 0xadc02080, 0x3f, 0x0 }, + { 0xfe600000, 0x3c400000, 0x207f, 0x0 }, + { 0xf7c02000, 0x20400000, 0x3000fe, 0x0 }, + { 0xff800000, 0x7c000000, 0x1fe0, 0x0 }, + { 0xffe03884, 0xafa00080, 0x30078, 0x0 }, + { 0xff201800, 0x5c001800, 0xdf20fe, 0x0 }, + { 0xffe02000, 0x47800000, 0x7e0, 0x0 }, + { 0xff601018, 0xdd400000, 0xfe0, 0x0 }, + { 0xffe020c0, 0xad4020c0, 0x3f, 0x0 }, + { 0xffe020c0, 0xad402080, 0x3f, 0x0 }, + { 0xf8003000, 0x40001000, 0x7f00000, 0x1 }, + { 0xffe02084, 0xafc02084, 0x30078, 0x0 }, + { 0xffe03080, 0x9c403080, 0xf60, 0x0 }, + { 0xfc003e40, 0x28003a00, 0x3f00000, 0x1 }, + { 0xffe038c0, 0xada010c0, 0x3f, 0x0 }, + { 0xffe038c0, 0xada01080, 0x3f, 0x0 }, + { 0xffe038c0, 0xada030c0, 0x3f, 0x0 }, + { 0xffe038c0, 0xada03080, 0x3f, 0x0 }, + { 0xf7c02000, 0x20800000, 0x3000fe, 0x0 }, + { 0xfc003fc7, 0x48003f46, 0x3f00000, 0x1 }, + { 0xffe01804, 0x44a00000, 0x20f8, 0x0 }, + { 0xf7c02000, 0x20002000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x12c02000, 0x3000fe, 0x0 }, + { 0xffe03000, 0x9a601000, 0xf60, 0x0 }, + { 0xffc00000, 0xda800000, 0x203fe0, 0x0 }, + { 0xf9e00000, 0x90400000, 0x6003fe0, 0x0 }, + { 0xffe02000, 0x47600000, 0x7e0, 0x0 }, + { 0xffe03080, 0x9d403000, 0xf60, 0x0 }, + { 0xffe03080, 0x9d403080, 0xf60, 0x0 }, + { 0xffe03080, 0x9d401000, 0xf60, 0x0 }, + { 0xffe03080, 0x9d401080, 0xf60, 0x0 }, + { 0xffe02000, 0x41400000, 0x7e0, 0x0 }, + { 0xff800000, 0xdf800000, 0x6020e0, 0x0 }, + { 0xffc01000, 0x61000000, 0x202ffe, 0x0 }, + { 0xffe03880, 0x9f202080, 0x1f0100, 0x0 }, + { 0xfc003fc7, 0x48003fc6, 0x3f00000, 0x1 }, + { 0xfe000000, 0x7a000000, 0x1fe0, 0x0 }, + { 0xffff0000, 0x6a490000, 0x1f80, 0x0 }, + { 0xff802000, 0x73000000, 0x1fe0, 0x0 }, + { 0xff602060, 0x3e200020, 0x1f80, 0x0 }, + { 0xf7c02000, 0x24000000, 0x3000fe, 0x0 }, + { 0xf8003e40, 0x20003a00, 0x7f00000, 0x1 }, + { 0xf7c03000, 0x14401000, 0x3000fe, 0x0 }, + { 0xf8003f00, 0x20003200, 0x7f00000, 0x1 }, + { 0xffc00000, 0x76400000, 0x203fe0, 0x0 }, + { 0xf7c02000, 0x22002000, 0x3000fe, 0x0 }, + { 0xffc01000, 0x61c01000, 0x202ffe, 0x0 }, + { 0xf7c03000, 0x14801000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x12002000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x10402000, 0x3000fe, 0x0 }, + { 0xff201800, 0x5d200000, 0xdf20fe, 0x0 }, + { 0xf7c02000, 0x21400000, 0x3000fe, 0x0 }, + { 0xff201800, 0x5d000000, 0xdf20fe, 0x0 }, + { 0xffe02000, 0x45c00000, 0x7e0, 0x0 }, + { 0xf7c02000, 0x25802000, 0x3000fe, 0x0 }, + { 0xfc003e70, 0x28003a50, 0x3f00000, 0x1 }, + { 0xf7c02300, 0x13c00300, 0x3000fe, 0x0 }, + { 0xf9e01800, 0xa1a00800, 0x60020ff, 0x0 }, + { 0xffe02000, 0x43800000, 0x7e0, 0x0 }, + { 0xfc003fc4, 0x48003fc0, 0x3f00000, 0x1 }, + { 0xff800000, 0xe2800000, 0x1fe0, 0x0 }, + { 0xf7c02300, 0x13c02000, 0x3000fe, 0x0 }, + { 0xffe03080, 0x9d803080, 0xf60, 0x0 }, + { 0xffe03080, 0x9d803000, 0xf60, 0x0 }, + { 0xffe03080, 0x9d801080, 0xf60, 0x0 }, + { 0xf8003fc4, 0x40003f00, 0x7f00000, 0x1 }, + { 0xffe00000, 0x39c00000, 0x201f, 0x0 }, + { 0xffe03080, 0x9d203080, 0xf60, 0x0 }, + { 0xffe02080, 0xab000080, 0x3f, 0x0 }, + { 0xf8003e00, 0x60003c00, 0x7f00000, 0x1 }, + { 0xffe03880, 0x9f602080, 0x1f0100, 0x0 }, + { 0xffc00000, 0x76800000, 0x203fe0, 0x0 }, + { 0xffe03884, 0xafa02084, 0x30078, 0x0 }, + { 0xf7c02000, 0x13002000, 0x3000fe, 0x0 }, + { 0xf9e00000, 0x91000000, 0x6003fe0, 0x0 }, + { 0xffe03080, 0x9d201080, 0xf60, 0x0 }, + { 0xf7c03000, 0x15002000, 0x3000fe, 0x0 }, + { 0xf8003000, 0x60000000, 0x7f00000, 0x1 }, + { 0xffc01000, 0x61800000, 0x202ffe, 0x0 }, + { 0xf7c03000, 0x14400000, 0x3000fe, 0x0 }, + { 0xffe03000, 0x9b401000, 0xf60, 0x0 }, + { 0xf7c03000, 0x14003000, 0x3000fe, 0x0 }, + { 0xffe03880, 0x9fc02080, 0x1f0100, 0x0 }, + { 0xfc003fc4, 0x48003f00, 0x3f00000, 0x1 }, + { 0xffe02000, 0x45000000, 0x7e0, 0x0 }, + { 0xfc003800, 0x48002800, 0x3f00000, 0x1 }, + { 0xfc003fc7, 0x48003fc5, 0x3f00000, 0x1 }, + { 0xfc003d18, 0x28003c00, 0x3f00000, 0x1 }, + { 0xfc003fc7, 0x48003fc4, 0x3f00000, 0x1 }, + { 0xf8003f00, 0x60003200, 0x7f00000, 0x1 }, + { 0xffe02084, 0xaf600080, 0x30078, 0x0 }, + { 0xf9e01800, 0xa1a00000, 0x60020ff, 0x0 }, + { 0xf7c03000, 0x14001000, 0x3000fe, 0x0 }, + { 0xf7c03000, 0x14c01000, 0x3000fe, 0x0 }, + { 0xffe00004, 0x46c00000, 0x20f8, 0x0 }, + { 0xf7c03000, 0x15003000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x10000000, 0x3000fe, 0x0 }, + { 0xf8003d18, 0x20003c08, 0x7f00000, 0x1 }, + { 0xffc0001c, 0x75400010, 0x203fe0, 0x0 }, + { 0xf9e00000, 0x48600000, 0x61f20ff, 0x0 }, + { 0xffe03080, 0x9c603080, 0xf60, 0x0 }, + { 0xfe000000, 0x58000000, 0x1ff3ffe, 0x0 }, + { 0xffe03000, 0x9a201000, 0xf60, 0x0 }, + { 0xffe00000, 0x69e00000, 0x1f18, 0x0 }, + { 0xffe020c0, 0xad802080, 0x3f, 0x0 }, + { 0xffe02000, 0x47c00000, 0x7e0, 0x0 }, + { 0xffe00000, 0x60e00000, 0x1f18, 0x0 }, + { 0xf7c03000, 0x15402000, 0x3000fe, 0x0 }, + { 0xffe020c0, 0xad8020c0, 0x3f, 0x0 }, + { 0xff000016, 0xde000012, 0xe020e8, 0x0 }, + { 0xf7c02000, 0x25c02000, 0x3000fe, 0x0 }, + { 0xf8003f00, 0x60003100, 0x7f00000, 0x1 }, + { 0xf8003f00, 0x60003000, 0x7f00000, 0x1 }, + { 0xf7c02000, 0x25800000, 0x3000fe, 0x0 }, + { 0xf7c03000, 0x14403000, 0x3000fe, 0x0 }, + { 0xfc003d18, 0x28003c08, 0x3f00000, 0x1 }, + { 0xffe03880, 0x9f403080, 0x1f0100, 0x0 }, + { 0xf7c02000, 0x25402000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x10c00000, 0x3000fe, 0x0 }, + { 0xffe02000, 0x45800000, 0x7e0, 0x0 }, + { 0xffe03880, 0x9f803080, 0x1f0100, 0x0 }, + { 0xffe03080, 0x9d001000, 0xf60, 0x0 }, + { 0xffe03080, 0x9d001080, 0xf60, 0x0 }, + { 0xffe03080, 0x9d003000, 0xf60, 0x0 }, + { 0xffe03080, 0x9d003080, 0xf60, 0x0 }, + { 0xffe03080, 0x9d801000, 0xf60, 0x0 }, + { 0xf9e00000, 0x49200000, 0x61f3fe0, 0x0 }, + { 0xf9e00000, 0xa1c00000, 0x60020ff, 0x0 }, + { 0xf9e00000, 0x90200000, 0x6003fe0, 0x0 }, + { 0xffe03080, 0x9d201000, 0xf60, 0x0 }, + { 0xffe03884, 0xafa01080, 0x30078, 0x0 }, + { 0xffe02084, 0xaf602080, 0x30078, 0x0 }, + { 0xffe038c0, 0xada000c0, 0x3f, 0x0 }, + { 0xffe02080, 0xab400080, 0x3f, 0x0 }, + { 0xff000016, 0xde000004, 0xe020e8, 0x0 }, + { 0xffe00004, 0x44000000, 0x20f8, 0x0 }, + { 0xf7c02000, 0x20000000, 0x3000fe, 0x0 }, + { 0xfc003d18, 0x28003c10, 0x3f00000, 0x1 }, + { 0xff600018, 0xdd000008, 0x1fe0, 0x0 }, + { 0xffe020c0, 0xadc000c0, 0x3f, 0x0 }, + { 0xffe020c0, 0xadc00080, 0x3f, 0x0 }, + { 0xffe03000, 0x9b801000, 0xf60, 0x0 }, + { 0xf8003fc7, 0x40003f46, 0x7f00000, 0x1 }, + { 0xf7c02000, 0x21c02000, 0x3000fe, 0x0 }, + { 0xffe01804, 0x40a00000, 0x20f8, 0x0 }, + { 0xf7c02000, 0x26402000, 0x3000fe, 0x0 }, + { 0xffe03080, 0x9c401080, 0xf60, 0x0 }, + { 0xffe00000, 0x39200000, 0x201f, 0x0 }, + { 0xffe03080, 0x9c403000, 0xf60, 0x0 }, + { 0xf7c02000, 0x11002000, 0x3000fe, 0x0 }, + { 0xfc003c00, 0x28002800, 0x3f00000, 0x1 }, + { 0xffe00004, 0x40400000, 0x20f8, 0x0 }, + { 0xf7c02000, 0x26802000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x13000000, 0x3000fe, 0x0 }, + { 0xffe00004, 0x42600000, 0x20f8, 0x0 }, + { 0xf8003000, 0x60001000, 0x7f00000, 0x1 }, + { 0xff602060, 0x3e400020, 0x1f80, 0x0 }, + { 0xff602060, 0x3f000000, 0x1f80, 0x0 }, + { 0xf7c02000, 0x24c02000, 0x3000fe, 0x0 }, + { 0xff802000, 0x74002000, 0x1fe0, 0x0 }, + { 0xf8003800, 0x20002000, 0x7f00000, 0x1 }, + { 0xffe03000, 0x9aa01000, 0xf60, 0x0 }, + { 0xf7c02000, 0x12400000, 0x3000fe, 0x0 }, + { 0xff602060, 0x3f000060, 0x1f80, 0x0 }, + { 0xf7c02000, 0x11000000, 0x3000fe, 0x0 }, +}; diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableAtoms.h b/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableAtoms.h new file mode 100644 index 0000000000000..a2505aa460c5b --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableAtoms.h @@ -0,0 +1,29 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonExecutableAtoms.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_HEXAGON_HEXAGON_EXECUTABLE_ATOM_H +#define LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_EXECUTABLE_ATOM_H + +#include "ELFFile.h" + +namespace lld { +namespace elf { +typedef llvm::object::ELFType<llvm::support::little, 2, false> HexagonELFType; +class HexagonLinkingContext; + +template <class HexagonELFType> class HexagonRuntimeFile + : public RuntimeFile<HexagonELFType> { +public: + HexagonRuntimeFile(HexagonLinkingContext &context) + : RuntimeFile<HexagonELFType>(context, "Hexagon runtime file") {} +}; +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_EXECUTABLE_ATOM_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableWriter.h b/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableWriter.h new file mode 100644 index 0000000000000..0848e64166faa --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableWriter.h @@ -0,0 +1,86 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonExecutableWriter.h -------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef HEXAGON_EXECUTABLE_WRITER_H +#define HEXAGON_EXECUTABLE_WRITER_H + +#include "ExecutableWriter.h" +#include "HexagonELFWriters.h" +#include "HexagonExecutableAtoms.h" +#include "HexagonLinkingContext.h" + +namespace lld { +namespace elf { + +template <typename ELFT> class HexagonTargetLayout; + +template <class ELFT> +class HexagonExecutableWriter : public ExecutableWriter<ELFT>, + public HexagonELFWriter<ELFT> { +public: + HexagonExecutableWriter(HexagonLinkingContext &context, + HexagonTargetLayout<ELFT> &layout); + +protected: + // Add any runtime files and their atoms to the output + virtual bool createImplicitFiles(std::vector<std::unique_ptr<File>> &); + + virtual void finalizeDefaultAtomValues(); + + virtual std::error_code setELFHeader() { + ExecutableWriter<ELFT>::setELFHeader(); + HexagonELFWriter<ELFT>::setELFHeader(*this->_elfHeader); + return std::error_code(); + } + +private: + void addDefaultAtoms() { + _hexagonRuntimeFile->addAbsoluteAtom("_SDA_BASE_"); + if (this->_context.isDynamic()) { + _hexagonRuntimeFile->addAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); + _hexagonRuntimeFile->addAbsoluteAtom("_DYNAMIC"); + } + } + + HexagonLinkingContext &_hexagonLinkingContext; + HexagonTargetLayout<ELFT> &_hexagonTargetLayout; + std::unique_ptr<HexagonRuntimeFile<ELFT>> _hexagonRuntimeFile; +}; + +template <class ELFT> +HexagonExecutableWriter<ELFT>::HexagonExecutableWriter( + HexagonLinkingContext &context, HexagonTargetLayout<ELFT> &layout) + : ExecutableWriter<ELFT>(context, layout), + HexagonELFWriter<ELFT>(context, layout), _hexagonLinkingContext(context), + _hexagonTargetLayout(layout), + _hexagonRuntimeFile(new HexagonRuntimeFile<ELFT>(context)) {} + +template <class ELFT> +bool HexagonExecutableWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + ExecutableWriter<ELFT>::createImplicitFiles(result); + // Add the default atoms as defined for hexagon + addDefaultAtoms(); + result.push_back(std::move(_hexagonRuntimeFile)); + return true; +} + +template <class ELFT> +void HexagonExecutableWriter<ELFT>::finalizeDefaultAtomValues() { + // Finalize the atom values that are part of the parent. + ExecutableWriter<ELFT>::finalizeDefaultAtomValues(); + auto sdabaseAtomIter = _hexagonTargetLayout.findAbsoluteAtom("_SDA_BASE_"); + (*sdabaseAtomIter)->_virtualAddr = + _hexagonTargetLayout.getSDataSection()->virtualAddr(); + HexagonELFWriter<ELFT>::finalizeHexagonRuntimeAtomValues(); +} + +} // namespace elf +} // namespace lld + +#endif // HEXAGON_EXECUTABLE_WRITER_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.cpp b/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.cpp new file mode 100644 index 0000000000000..7eacb2b44c3b2 --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.cpp @@ -0,0 +1,25 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.cpp -------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "HexagonLinkingContext.h" +#include "HexagonTargetHandler.h" + +using namespace lld::elf; + +std::unique_ptr<lld::ELFLinkingContext> +HexagonLinkingContext::create(llvm::Triple triple) { + if (triple.getArch() == llvm::Triple::hexagon) + return std::unique_ptr<lld::ELFLinkingContext>( + new HexagonLinkingContext(triple)); + return nullptr; +} + +HexagonLinkingContext::HexagonLinkingContext(llvm::Triple triple) + : ELFLinkingContext(triple, std::unique_ptr<TargetHandlerBase>( + new HexagonTargetHandler(*this))) {} diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.h b/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.h new file mode 100644 index 0000000000000..c920cdf153aaf --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.h @@ -0,0 +1,69 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.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_HEXAGON_HEXAGON_LINKING_CONTEXT_H +#define LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_LINKING_CONTEXT_H + +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { + +typedef llvm::object::ELFType<llvm::support::little, 2, false> HexagonELFType; + +class HexagonLinkingContext final : public ELFLinkingContext { +public: + static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + HexagonLinkingContext(llvm::Triple triple); + + void addPasses(PassManager &) override; + + bool isDynamicRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + switch (r.kindValue()) { + case llvm::ELF::R_HEX_RELATIVE: + case llvm::ELF::R_HEX_GLOB_DAT: + return true; + default: + return false; + } + } + + bool isPLTRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + switch (r.kindValue()) { + case llvm::ELF::R_HEX_JMP_SLOT: + return true; + default: + return false; + } + } + + /// \brief Hexagon has only one relative relocation + /// a) for supporting relative relocs - R_HEX_RELATIVE + bool isRelativeReloc(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + switch (r.kindValue()) { + case llvm::ELF::R_HEX_RELATIVE: + return true; + default: + return false; + } + } +}; + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_LINKING_CONTEXT_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationFunctions.h b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationFunctions.h new file mode 100644 index 0000000000000..2b9e25ce363b5 --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationFunctions.h @@ -0,0 +1,49 @@ +//===- HexagonRelocationFunction.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_HEXAGON_HEXAGON_RELOCATION_FUNCTIONS_H +#define LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_RELOCATION_FUNCTIONS_H + +namespace lld { +namespace elf { + +/// \brief HexagonInstruction which is used to store various values +typedef struct { + uint32_t insnMask; + uint32_t insnCmpMask; + uint32_t insnBitMask; + bool isDuplex; +} Instruction; + +#include "HexagonEncodings.h" + +#define FINDV4BITMASK(INSN) \ + findBitMask((uint32_t) * ((llvm::support::ulittle32_t *) INSN), \ + insn_encodings, \ + sizeof(insn_encodings) / sizeof(Instruction)) + +/// \brief finds the scatter Bits that need to be used to apply relocations +inline uint32_t +findBitMask(uint32_t insn, Instruction *encodings, int32_t numInsns) { + for (int32_t i = 0; i < numInsns; i++) { + if (((insn & 0xc000) == 0) && !(encodings[i].isDuplex)) + continue; + + if (((insn & 0xc000) != 0) && (encodings[i].isDuplex)) + continue; + + if (((encodings[i].insnMask) & insn) == encodings[i].insnCmpMask) + return encodings[i].insnBitMask; + } + llvm_unreachable("found unknown instruction"); +} + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_RELOCATION_FUNCTIONS_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.cpp b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.cpp new file mode 100644 index 0000000000000..21967d356a311 --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.cpp @@ -0,0 +1,350 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.cpp ---------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "HexagonLinkingContext.h" +#include "HexagonRelocationFunctions.h" +#include "HexagonTargetHandler.h" +#include "HexagonRelocationHandler.h" +#include "llvm/Support/Endian.h" + +using namespace lld; +using namespace lld::elf; +using namespace llvm::ELF; +using namespace llvm::support::endian; + +#define APPLY_RELOC(result) \ + write32le(location, result | read32le(location)); + +static int relocBNPCREL(uint8_t *location, uint64_t P, uint64_t S, uint64_t A, + int32_t nBits) { + int32_t result = (uint32_t)(((S + A) - P) >> 2); + int32_t range = 1 << nBits; + if (result < range && result > -range) { + result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; + } + return 1; +} + +/// \brief Word32_LO: 0x00c03fff : (S + A) : Truncate +static int relocLO16(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { + uint32_t result = (uint32_t)(S + A); + result = lld::scatterBits<int32_t>(result, 0x00c03fff); + APPLY_RELOC(result); + return 0; +} + +/// \brief Word32_LO: 0x00c03fff : (S + A) >> 16 : Truncate +static int relocHI16(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { + uint32_t result = (uint32_t)((S + A) >> 16); + result = lld::scatterBits<int32_t>(result, 0x00c03fff); + APPLY_RELOC(result); + return 0; +} + +/// \brief Word32: 0xffffffff : (S + A) : Truncate +static int reloc32(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { + uint32_t result = (uint32_t)(S + A); + APPLY_RELOC(result); + return 0; +} + +static int reloc32_6_X(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { + int64_t result = ((S + A) >> 6); + int64_t range = ((int64_t)1) << 32; + if (result > range) + return 1; + result = lld::scatterBits<int32_t>(result, 0xfff3fff); + APPLY_RELOC(result); + return 0; +} + +// R_HEX_B32_PCREL_X +static int relocHexB32PCRELX(uint8_t *location, uint64_t P, uint64_t S, + uint64_t A) { + int64_t result = ((S + A - P) >> 6); + result = lld::scatterBits<int32_t>(result, 0xfff3fff); + APPLY_RELOC(result); + return 0; +} + +// R_HEX_BN_PCREL_X +static int relocHexBNPCRELX(uint8_t *location, uint64_t P, uint64_t S, + uint64_t A, int nbits) { + int32_t result = ((S + A - P) & 0x3f); + int32_t range = 1 << nbits; + if (result < range && result > -range) { + result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; + } + return 1; +} + +// R_HEX_6_PCREL_X +static int relocHex6PCRELX(uint8_t *location, uint64_t P, uint64_t S, + uint64_t A) { + int32_t result = (S + A - P); + result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; +} + +// R_HEX_N_X : Word32_U6 : (S + A) : Unsigned Truncate +static int relocHex_N_X(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { + uint32_t result = (S + A); + result = lld::scatterBits<uint32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; +} + +// GP REL relocations +static int relocHexGPRELN(uint8_t *location, uint64_t P, uint64_t S, uint64_t A, + uint64_t GP, int nShiftBits) { + int32_t result = (int64_t)((S + A - GP) >> nShiftBits); + int32_t range = 1L << 16; + if (result <= range) { + result = lld::scatterBits<uint32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; + } + return 1; +} + +/// \brief Word32_LO: 0x00c03fff : (G) : Truncate +static int relocHexGOTLO16(uint8_t *location, uint64_t A, uint64_t GOT) { + int32_t result = (int32_t)(A-GOT); + result = lld::scatterBits<int32_t>(result, 0x00c03fff); + APPLY_RELOC(result); + return 0; +} + +/// \brief Word32_LO: 0x00c03fff : (G) >> 16 : Truncate +static int relocHexGOTHI16(uint8_t *location, uint64_t A, uint64_t GOT) { + int32_t result = (int32_t)((A-GOT) >> 16); + result = lld::scatterBits<int32_t>(result, 0x00c03fff); + APPLY_RELOC(result); + return 0; +} + +/// \brief Word32: 0xffffffff : (G) : Truncate +static int relocHexGOT32(uint8_t *location, uint64_t A, uint64_t GOT) { + int32_t result = (int32_t)(GOT - A); + APPLY_RELOC(result); + return 0; +} + +/// \brief Word32_U16 : (G) : Truncate +static int relocHexGOT16(uint8_t *location, uint64_t A, uint64_t GOT) { + int32_t result = (int32_t)(GOT-A); + int32_t range = 1L << 16; + if (result <= range) { + result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; + } + return 1; +} + +static int relocHexGOT32_6_X(uint8_t *location, uint64_t A, uint64_t GOT) { + int32_t result = (int32_t)((A-GOT) >> 6); + result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; +} + +static int relocHexGOT16_X(uint8_t *location, uint64_t A, uint64_t GOT) { + int32_t result = (int32_t)(A-GOT); + int32_t range = 1L << 6; + if (result <= range) { + result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; + } + return 1; +} + +static int relocHexGOT11_X(uint8_t *location, uint64_t A, uint64_t GOT) { + uint32_t result = (uint32_t)(A-GOT); + result = lld::scatterBits<uint32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; +} + +static int relocHexGOTRELSigned(uint8_t *location, uint64_t P, uint64_t S, + uint64_t A, uint64_t GOT, int shiftBits = 0) { + int32_t result = (int32_t)((S + A - GOT) >> shiftBits); + result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; +} + +static int relocHexGOTRELUnsigned(uint8_t *location, uint64_t P, uint64_t S, + uint64_t A, uint64_t GOT, int shiftBits = 0) { + uint32_t result = (uint32_t)((S + A - GOT) >> shiftBits); + result = lld::scatterBits<uint32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; +} + +static int relocHexGOTREL_HILO16(uint8_t *location, uint64_t P, uint64_t S, + uint64_t A, uint64_t GOT, int shiftBits = 0) { + int32_t result = (int32_t)((S + A - GOT) >> shiftBits); + result = lld::scatterBits<int32_t>(result, 0x00c03fff); + APPLY_RELOC(result); + return 0; +} + +static int relocHexGOTREL_32(uint8_t *location, uint64_t P, uint64_t S, + uint64_t A, uint64_t GOT) { + int32_t result = (int32_t)(S + A - GOT); + APPLY_RELOC(result); + return 0; +} + +std::error_code HexagonTargetRelocationHandler::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::Hexagon); + switch (ref.kindValue()) { + case R_HEX_B22_PCREL: + relocBNPCREL(location, relocVAddress, targetVAddress, ref.addend(), 21); + break; + case R_HEX_B15_PCREL: + relocBNPCREL(location, relocVAddress, targetVAddress, ref.addend(), 14); + break; + case R_HEX_B9_PCREL: + relocBNPCREL(location, relocVAddress, targetVAddress, ref.addend(), 8); + break; + case R_HEX_LO16: + relocLO16(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_HEX_HI16: + relocHI16(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_HEX_32: + reloc32(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_HEX_32_6_X: + reloc32_6_X(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_HEX_B32_PCREL_X: + relocHexB32PCRELX(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_HEX_B22_PCREL_X: + relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 21); + break; + case R_HEX_B15_PCREL_X: + relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 14); + break; + case R_HEX_B13_PCREL_X: + relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 12); + break; + case R_HEX_B9_PCREL_X: + relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 8); + break; + case R_HEX_B7_PCREL_X: + relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 6); + break; + case R_HEX_GPREL16_0: + relocHexGPRELN(location, relocVAddress, targetVAddress, ref.addend(), + _hexagonTargetLayout.getSDataSection()->virtualAddr(), 0); + break; + case R_HEX_GPREL16_1: + relocHexGPRELN(location, relocVAddress, targetVAddress, ref.addend(), + _hexagonTargetLayout.getSDataSection()->virtualAddr(), 1); + break; + case R_HEX_GPREL16_2: + relocHexGPRELN(location, relocVAddress, targetVAddress, ref.addend(), + _hexagonTargetLayout.getSDataSection()->virtualAddr(), 2); + break; + case R_HEX_GPREL16_3: + relocHexGPRELN(location, relocVAddress, targetVAddress, ref.addend(), + _hexagonTargetLayout.getSDataSection()->virtualAddr(), 3); + break; + case R_HEX_16_X: + case R_HEX_12_X: + case R_HEX_11_X: + case R_HEX_10_X: + case R_HEX_9_X: + case R_HEX_8_X: + case R_HEX_7_X: + case R_HEX_6_X: + relocHex_N_X(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_HEX_6_PCREL_X: + relocHex6PCRELX(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_HEX_JMP_SLOT: + case R_HEX_GLOB_DAT: + break; + case R_HEX_GOTREL_32: + relocHexGOTREL_32(location, relocVAddress, targetVAddress, ref.addend(), + _hexagonTargetLayout.getGOTSymAddr()); + break; + case R_HEX_GOTREL_LO16: + relocHexGOTREL_HILO16(location, relocVAddress, targetVAddress, ref.addend(), + _hexagonTargetLayout.getGOTSymAddr()); + break; + case R_HEX_GOTREL_HI16: + relocHexGOTREL_HILO16(location, relocVAddress, targetVAddress, ref.addend(), + _hexagonTargetLayout.getGOTSymAddr(), 16); + break; + case R_HEX_GOT_LO16: + relocHexGOTLO16(location, targetVAddress, + _hexagonTargetLayout.getGOTSymAddr()); + break; + case R_HEX_GOT_HI16: + relocHexGOTHI16(location, targetVAddress, + _hexagonTargetLayout.getGOTSymAddr()); + break; + case R_HEX_GOT_32: + relocHexGOT32(location, targetVAddress, + _hexagonTargetLayout.getGOTSymAddr()); + break; + case R_HEX_GOT_16: + relocHexGOT16(location, targetVAddress, + _hexagonTargetLayout.getGOTSymAddr()); + break; + case R_HEX_GOT_32_6_X: + relocHexGOT32_6_X(location, targetVAddress, + _hexagonTargetLayout.getGOTSymAddr()); + break; + case R_HEX_GOT_16_X: + relocHexGOT16_X(location, targetVAddress, + _hexagonTargetLayout.getGOTSymAddr()); + break; + case R_HEX_GOT_11_X: + relocHexGOT11_X(location, targetVAddress, + _hexagonTargetLayout.getGOTSymAddr()); + break; + case R_HEX_GOTREL_32_6_X: + relocHexGOTRELSigned(location, relocVAddress, targetVAddress, ref.addend(), + _hexagonTargetLayout.getGOTSymAddr(), 6); + break; + case R_HEX_GOTREL_16_X: + case R_HEX_GOTREL_11_X: + relocHexGOTRELUnsigned(location, relocVAddress, targetVAddress, + ref.addend(), _hexagonTargetLayout.getGOTSymAddr()); + break; + + default: + return make_unhandled_reloc_error(); + } + + return std::error_code(); +} diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.h b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.h new file mode 100644 index 0000000000000..4795d0264b9cd --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.h @@ -0,0 +1,35 @@ +//===- lld/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.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_HEXAGON_HEXAGON_RELOCATION_HANDLER_H +#define LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_RELOCATION_HANDLER_H + +#include "HexagonSectionChunks.h" +#include "HexagonTargetHandler.h" +#include "lld/ReaderWriter/RelocationHelperFunctions.h" + +namespace lld { +namespace elf { + +class HexagonTargetHandler; + +class HexagonTargetRelocationHandler final : public TargetRelocationHandler { +public: + HexagonTargetRelocationHandler(HexagonTargetLayout<HexagonELFType> &layout) + : _hexagonTargetLayout(layout) {} + + std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, + const lld::AtomLayout &, + const Reference &) const override; + +private: + HexagonTargetLayout<HexagonELFType> &_hexagonTargetLayout; +}; +} // elf +} // lld +#endif diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonSectionChunks.h b/lib/ReaderWriter/ELF/Hexagon/HexagonSectionChunks.h new file mode 100644 index 0000000000000..5b3fbbbd899be --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonSectionChunks.h @@ -0,0 +1,86 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonSectionChunks.h-----------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef HEXAGON_SECTION_CHUNKS_H +#define HEXAGON_SECTION_CHUNKS_H + +#include "HexagonTargetHandler.h" + +namespace lld { +namespace elf { +template <typename ELFT> class HexagonTargetLayout; +class HexagonLinkingContext; + +/// \brief Handle Hexagon SData section +template <class HexagonELFType> +class SDataSection : public AtomSection<HexagonELFType> { +public: + SDataSection(const HexagonLinkingContext &context) + : AtomSection<HexagonELFType>( + context, ".sdata", DefinedAtom::typeDataFast, 0, + HexagonTargetLayout<HexagonELFType>::ORDER_SDATA) { + this->_type = SHT_PROGBITS; + this->_flags = SHF_ALLOC | SHF_WRITE; + this->_alignment = 4096; + } + + /// \brief Finalize the section contents before writing + virtual void doPreFlight(); + + /// \brief Does this section have an output segment. + virtual bool hasOutputSegment() { return true; } + + const lld::AtomLayout *appendAtom(const Atom *atom) { + const DefinedAtom *definedAtom = cast<DefinedAtom>(atom); + DefinedAtom::Alignment atomAlign = definedAtom->alignment(); + uint64_t alignment = 1u << atomAlign.powerOf2; + this->_atoms.push_back(new (this->_alloc) lld::AtomLayout(atom, 0, 0)); + // Set the section alignment to the largest alignment + // std::max doesn't support uint64_t + if (this->_alignment < alignment) + this->_alignment = alignment; + return (this->_atoms.back()); + } + +}; // SDataSection + +template <class HexagonELFType> +void SDataSection<HexagonELFType>::doPreFlight() { + // sort the atoms on the alignments they have been set + std::stable_sort(this->_atoms.begin(), this->_atoms.end(), + [](const lld::AtomLayout * A, + const lld::AtomLayout * B) { + const DefinedAtom *definedAtomA = cast<DefinedAtom>(A->_atom); + const DefinedAtom *definedAtomB = cast<DefinedAtom>(B->_atom); + int64_t alignmentA = 1 << definedAtomA->alignment().powerOf2; + int64_t alignmentB = 1 << definedAtomB->alignment().powerOf2; + if (alignmentA == alignmentB) { + if (definedAtomA->merge() == DefinedAtom::mergeAsTentative) + return false; + if (definedAtomB->merge() == DefinedAtom::mergeAsTentative) + return true; + } + return alignmentA < alignmentB; + }); + + // Set the fileOffset, and the appropriate size of the section + for (auto &ai : this->_atoms) { + const DefinedAtom *definedAtom = cast<DefinedAtom>(ai->_atom); + DefinedAtom::Alignment atomAlign = definedAtom->alignment(); + uint64_t fOffset = this->alignOffset(this->fileSize(), atomAlign); + uint64_t mOffset = this->alignOffset(this->memSize(), atomAlign); + ai->_fileOffset = fOffset; + this->_fsize = fOffset + definedAtom->size(); + this->_msize = mOffset + definedAtom->size(); + } +} // finalize + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_SECTION_CHUNKS_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp b/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp new file mode 100644 index 0000000000000..9b10c2f160f41 --- /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 ®istry) { + 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 diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h b/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h new file mode 100644 index 0000000000000..f4315f710ec7c --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h @@ -0,0 +1,143 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h ----------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef HEXAGON_TARGET_HANDLER_H +#define HEXAGON_TARGET_HANDLER_H + +#include "DefaultTargetHandler.h" +#include "HexagonELFReader.h" +#include "HexagonExecutableAtoms.h" +#include "HexagonRelocationHandler.h" +#include "HexagonSectionChunks.h" +#include "TargetLayout.h" + +namespace lld { +namespace elf { +class HexagonLinkingContext; + +/// \brief TargetLayout for Hexagon +template <class HexagonELFType> +class HexagonTargetLayout final : public TargetLayout<HexagonELFType> { +public: + enum HexagonSectionOrder { + ORDER_SDATA = 205 + }; + + HexagonTargetLayout(HexagonLinkingContext &hti) + : TargetLayout<HexagonELFType>(hti), _sdataSection(nullptr), + _gotSymAtom(nullptr), _cachedGotSymAtom(false) { + _sdataSection = new (_alloc) SDataSection<HexagonELFType>(hti); + } + + /// \brief Return the section order for a input section + virtual Layout::SectionOrder getSectionOrder( + StringRef name, int32_t contentType, int32_t contentPermissions) { + if ((contentType == DefinedAtom::typeDataFast) || + (contentType == DefinedAtom::typeZeroFillFast)) + return ORDER_SDATA; + + return DefaultLayout<HexagonELFType>::getSectionOrder(name, contentType, + contentPermissions); + } + + /// \brief Return the appropriate input section name. + virtual StringRef getInputSectionName(const DefinedAtom *da) const { + switch (da->contentType()) { + case DefinedAtom::typeDataFast: + case DefinedAtom::typeZeroFillFast: + return ".sdata"; + default: + break; + } + return DefaultLayout<HexagonELFType>::getInputSectionName(da); + } + + /// \brief Gets or creates a section. + virtual AtomSection<HexagonELFType> * + createSection(StringRef name, int32_t contentType, + DefinedAtom::ContentPermissions contentPermissions, + Layout::SectionOrder sectionOrder) { + if ((contentType == DefinedAtom::typeDataFast) || + (contentType == DefinedAtom::typeZeroFillFast)) + return _sdataSection; + return DefaultLayout<HexagonELFType>::createSection( + name, contentType, contentPermissions, sectionOrder); + } + + /// \brief get the segment type for the section thats defined by the target + virtual Layout::SegmentType + getSegmentType(Section<HexagonELFType> *section) const { + if (section->order() == ORDER_SDATA) + return PT_LOAD; + + return DefaultLayout<HexagonELFType>::getSegmentType(section); + } + + Section<HexagonELFType> *getSDataSection() const { + return _sdataSection; + } + + uint64_t getGOTSymAddr() { + if (!_cachedGotSymAtom) { + auto gotAtomIter = this->findAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); + _gotSymAtom = (*gotAtomIter); + _cachedGotSymAtom = true; + } + if (_gotSymAtom) + return _gotSymAtom->_virtualAddr; + return 0; + } + +private: + llvm::BumpPtrAllocator _alloc; + SDataSection<HexagonELFType> *_sdataSection; + AtomLayout *_gotSymAtom; + bool _cachedGotSymAtom; +}; + +/// \brief TargetHandler for Hexagon +class HexagonTargetHandler final : + public DefaultTargetHandler<HexagonELFType> { +public: + HexagonTargetHandler(HexagonLinkingContext &targetInfo); + + void registerRelocationNames(Registry ®istry) override; + + const HexagonTargetRelocationHandler &getRelocationHandler() const override { + return *(_hexagonRelocationHandler.get()); + } + + HexagonTargetLayout<HexagonELFType> &getTargetLayout() override { + return *(_hexagonTargetLayout.get()); + } + + std::unique_ptr<Reader> getObjReader() override { + return std::unique_ptr<Reader>( + new HexagonELFObjectReader(_hexagonLinkingContext)); + } + + std::unique_ptr<Reader> getDSOReader() override { + return std::unique_ptr<Reader>( + new HexagonELFDSOReader(_hexagonLinkingContext)); + } + + std::unique_ptr<Writer> getWriter() override; + +private: + llvm::BumpPtrAllocator _alloc; + static const Registry::KindStrings kindStrings[]; + HexagonLinkingContext &_hexagonLinkingContext; + std::unique_ptr<HexagonRuntimeFile<HexagonELFType> > _hexagonRuntimeFile; + std::unique_ptr<HexagonTargetLayout<HexagonELFType>> _hexagonTargetLayout; + std::unique_ptr<HexagonTargetRelocationHandler> _hexagonRelocationHandler; +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Hexagon/Makefile b/lib/ReaderWriter/ELF/Hexagon/Makefile new file mode 100644 index 0000000000000..8d6f1a0a3b1ed --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/Makefile @@ -0,0 +1,16 @@ +##===- lld/lib/ReaderWriter/ELF/Hexagon/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 := lldHexagonELFTarget +USEDLIBS = lldCore.a + +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF/Hexagon -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/Layout.h b/lib/ReaderWriter/ELF/Layout.h new file mode 100644 index 0000000000000..826cf5035d59a --- /dev/null +++ b/lib/ReaderWriter/ELF/Layout.h @@ -0,0 +1,59 @@ +//===- lib/ReaderWriter/ELF/Layout.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_LAYOUT_H +#define LLD_READER_WRITER_ELF_LAYOUT_H + +#include "lld/Core/DefinedAtom.h" +#include "lld/ReaderWriter/AtomLayout.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/ErrorOr.h" + +namespace lld { +namespace elf { + +/// \brief The ELFLayout is an abstract class for managing the final layout for +/// the kind of binaries(Shared Libraries / Relocatables / Executables 0 +/// Each architecture (Hexagon, MIPS) would have a concrete +/// subclass derived from Layout for generating each binary thats +// needed by the lld linker +class Layout { +public: + typedef uint32_t SectionOrder; + typedef uint32_t SegmentType; + typedef uint32_t Flags; + +public: + /// Return the order the section would appear in the output file + virtual SectionOrder getSectionOrder(StringRef name, int32_t contentType, + int32_t contentPerm) = 0; + /// \brief Append the Atom to the layout and create appropriate sections. + /// \returns A reference to the atom layout or an error. The atom layout will + /// be updated as linking progresses. + virtual ErrorOr<const lld::AtomLayout *> addAtom(const Atom *atom) = 0; + /// find the Atom in the current layout + virtual const AtomLayout *findAtomLayoutByName(StringRef name) const = 0; + /// associates a section to a segment + virtual void assignSectionsToSegments() = 0; + /// associates a virtual address to the segment, section, and the atom + virtual void assignVirtualAddress() = 0; + +public: + Layout() {} + + virtual ~Layout() { } +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Makefile b/lib/ReaderWriter/ELF/Makefile new file mode 100644 index 0000000000000..5791ecb9733d1 --- /dev/null +++ b/lib/ReaderWriter/ELF/Makefile @@ -0,0 +1,18 @@ +##===- lld/lib/ReaderWriter/ELF/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 := lldELF + +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF + +# these link against this lib +PARALLEL_DIRS := Hexagon X86 X86_64 Mips AArch64 ARM + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/Mips/CMakeLists.txt b/lib/ReaderWriter/ELF/Mips/CMakeLists.txt new file mode 100644 index 0000000000000..d982508b7ddcc --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/CMakeLists.txt @@ -0,0 +1,14 @@ +add_llvm_library(lldMipsELFTarget + MipsCtorsOrderPass.cpp + MipsELFFlagsMerger.cpp + MipsLinkingContext.cpp + MipsRelocationHandler.cpp + MipsRelocationPass.cpp + MipsTargetHandler.cpp + LINK_LIBS + lldELF + lldReaderWriter + lldCore + LLVMObject + LLVMSupport + ) diff --git a/lib/ReaderWriter/ELF/Mips/Makefile b/lib/ReaderWriter/ELF/Mips/Makefile new file mode 100644 index 0000000000000..0b2f4ff82279a --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/Makefile @@ -0,0 +1,15 @@ +##===- lld/lib/ReaderWriter/ELF/Mips/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 := lldMipsELFTarget +USEDLIBS = lldCore.a +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.cpp b/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.cpp new file mode 100644 index 0000000000000..8bf80257fc89f --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.cpp @@ -0,0 +1,73 @@ +//===- lib/ReaderWriter/ELF/Mips/Mips/CtorsOrderPass.cpp ------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsCtorsOrderPass.h" +#include <algorithm> +#include <climits> + +using namespace lld; +using namespace lld::elf; + +static bool matchCrtObjName(StringRef objName, StringRef objPath) { + if (!objPath.endswith(".o")) + return false; + + // check *<objName> case + objPath = objPath.drop_back(2); + if (objPath.endswith(objName)) + return true; + + // check *<objName>? case + return !objPath.empty() && objPath.drop_back(1).endswith(objName); +} + +static int32_t getSectionPriority(StringRef path, StringRef sectionName) { + // Arrange .ctors/.dtors sections in the following order: + // .ctors from crtbegin.o or crtbegin?.o + // .ctors from regular object files + // .ctors.* (sorted) from regular object files + // .ctors from crtend.o or crtend?.o + + if (matchCrtObjName("crtbegin", path)) + return std::numeric_limits<int32_t>::min(); + if (matchCrtObjName("crtend", path)) + return std::numeric_limits<int32_t>::max(); + + StringRef num = sectionName.drop_front().rsplit('.').second; + + int32_t priority = std::numeric_limits<int32_t>::min() + 1; + if (!num.empty()) + num.getAsInteger(10, priority); + + return priority; +} + +void MipsCtorsOrderPass::perform(std::unique_ptr<MutableFile> &f) { + auto definedAtoms = f->definedAtoms(); + + auto last = std::stable_partition(definedAtoms.begin(), definedAtoms.end(), + [](const DefinedAtom *atom) { + if (atom->sectionChoice() != DefinedAtom::sectionCustomRequired) + return false; + + StringRef name = atom->customSectionName(); + return name.startswith(".ctors") || name.startswith(".dtors"); + }); + + std::stable_sort(definedAtoms.begin(), last, + [](const DefinedAtom *left, const DefinedAtom *right) { + StringRef leftSec = left->customSectionName(); + StringRef rightSec = right->customSectionName(); + + int32_t leftPriority = getSectionPriority(left->file().path(), leftSec); + int32_t rightPriority = getSectionPriority(right->file().path(), rightSec); + + return leftPriority < rightPriority; + }); +} diff --git a/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.h b/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.h new file mode 100644 index 0000000000000..eeb1a194f9c77 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.h @@ -0,0 +1,25 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.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_MIPS_MIPS_CTORS_ORDER_PASS_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_CTORS_ORDER_PASS_H + +#include "lld/Core/Pass.h" + +namespace lld { +namespace elf { +/// \brief This pass sorts atoms in .{ctors,dtors}.<priority> sections. +class MipsCtorsOrderPass : public Pass { +public: + void perform(std::unique_ptr<MutableFile> &mergedFile) override; +}; +} +} + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsDynamicLibraryWriter.h b/lib/ReaderWriter/ELF/Mips/MipsDynamicLibraryWriter.h new file mode 100644 index 0000000000000..30b5b0ba6dae9 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsDynamicLibraryWriter.h @@ -0,0 +1,101 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsDynamicLibraryWriter.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_MIPS_MIPS_DYNAMIC_LIBRARY_WRITER_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_DYNAMIC_LIBRARY_WRITER_H + +#include "DynamicLibraryWriter.h" +#include "MipsDynamicTable.h" +#include "MipsELFWriters.h" +#include "MipsLinkingContext.h" + +namespace lld { +namespace elf { + +template <typename ELFT> class MipsSymbolTable; +template <typename ELFT> class MipsDynamicSymbolTable; +template <typename ELFT> class MipsTargetLayout; + +template <class ELFT> +class MipsDynamicLibraryWriter : public DynamicLibraryWriter<ELFT> { +public: + MipsDynamicLibraryWriter(MipsLinkingContext &ctx, + MipsTargetLayout<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; + + std::error_code setELFHeader() override { + DynamicLibraryWriter<ELFT>::setELFHeader(); + _writeHelper.setELFHeader(*this->_elfHeader); + return std::error_code(); + } + + unique_bump_ptr<SymbolTable<ELFT>> createSymbolTable() override; + unique_bump_ptr<DynamicTable<ELFT>> createDynamicTable() override; + + unique_bump_ptr<DynamicSymbolTable<ELFT>> + createDynamicSymbolTable() override; + +private: + MipsELFWriter<ELFT> _writeHelper; + MipsTargetLayout<ELFT> &_mipsTargetLayout; +}; + +template <class ELFT> +MipsDynamicLibraryWriter<ELFT>::MipsDynamicLibraryWriter( + MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &layout) + : DynamicLibraryWriter<ELFT>(ctx, layout), _writeHelper(ctx, layout), + _mipsTargetLayout(layout) {} + +template <class ELFT> +bool MipsDynamicLibraryWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + DynamicLibraryWriter<ELFT>::createImplicitFiles(result); + result.push_back(std::move(_writeHelper.createRuntimeFile())); + return true; +} + +template <class ELFT> +void MipsDynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues() { + // Finalize the atom values that are part of the parent. + DynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues(); + _writeHelper.finalizeMipsRuntimeAtomValues(); +} + +template <class ELFT> +unique_bump_ptr<SymbolTable<ELFT>> + MipsDynamicLibraryWriter<ELFT>::createSymbolTable() { + return unique_bump_ptr<SymbolTable<ELFT>>(new ( + this->_alloc) MipsSymbolTable<ELFT>(this->_context)); +} + +/// \brief create dynamic table +template <class ELFT> +unique_bump_ptr<DynamicTable<ELFT>> + MipsDynamicLibraryWriter<ELFT>::createDynamicTable() { + return unique_bump_ptr<DynamicTable<ELFT>>(new ( + this->_alloc) MipsDynamicTable<ELFT>(this->_context, _mipsTargetLayout)); +} + +/// \brief create dynamic symbol table +template <class ELFT> +unique_bump_ptr<DynamicSymbolTable<ELFT>> + MipsDynamicLibraryWriter<ELFT>::createDynamicSymbolTable() { + return unique_bump_ptr<DynamicSymbolTable<ELFT>>( + new (this->_alloc) MipsDynamicSymbolTable<ELFT>( + this->_context, _mipsTargetLayout)); +} + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsDynamicTable.h b/lib/ReaderWriter/ELF/Mips/MipsDynamicTable.h new file mode 100644 index 0000000000000..2b9562f42b57d --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsDynamicTable.h @@ -0,0 +1,115 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsDynamicTable.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_MIPS_MIPS_DYNAMIC_TABLE_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_DYNAMIC_TABLE_H + +#include "DefaultLayout.h" +#include "SectionChunks.h" + +namespace lld { +namespace elf { + +template <class ELFType> class MipsTargetLayout; + +template <class MipsELFType> +class MipsDynamicTable : public DynamicTable<MipsELFType> { +public: + MipsDynamicTable(const ELFLinkingContext &ctx, + MipsTargetLayout<MipsELFType> &layout) + : DynamicTable<MipsELFType>(ctx, layout, ".dynamic", + DefaultLayout<MipsELFType>::ORDER_DYNAMIC), + _mipsTargetLayout(layout) {} + + void createDefaultEntries() override { + DynamicTable<MipsELFType>::createDefaultEntries(); + + typename DynamicTable<MipsELFType>::Elf_Dyn dyn; + + // Version id for the Runtime Linker Interface. + dyn.d_un.d_val = 1; + dyn.d_tag = DT_MIPS_RLD_VERSION; + this->addEntry(dyn); + + // MIPS flags. + dyn.d_un.d_val = RHF_NOTPOT; + dyn.d_tag = DT_MIPS_FLAGS; + this->addEntry(dyn); + + // The base address of the segment. + dyn.d_un.d_ptr = 0; + dyn.d_tag = DT_MIPS_BASE_ADDRESS; + _dt_baseaddr = this->addEntry(dyn); + + // Number of local global offset table entries. + dyn.d_un.d_val = 0; + dyn.d_tag = DT_MIPS_LOCAL_GOTNO; + _dt_localgot = this->addEntry(dyn); + + // Number of entries in the .dynsym section. + dyn.d_un.d_val = 0; + dyn.d_tag = DT_MIPS_SYMTABNO; + _dt_symtabno = this->addEntry(dyn); + + // The index of the first dynamic symbol table entry that corresponds + // to an entry in the global offset table. + dyn.d_un.d_val = 0; + dyn.d_tag = DT_MIPS_GOTSYM; + _dt_gotsym = this->addEntry(dyn); + + // Address of the .got section. + dyn.d_un.d_val = 0; + dyn.d_tag = DT_PLTGOT; + _dt_pltgot = this->addEntry(dyn); + } + + void updateDynamicTable() override { + DynamicTable<MipsELFType>::updateDynamicTable(); + + // Assign the minimum segment address to the DT_MIPS_BASE_ADDRESS tag. + auto baseAddr = std::numeric_limits<uint64_t>::max(); + for (auto si : _mipsTargetLayout.segments()) + if (si->segmentType() != llvm::ELF::PT_NULL) + baseAddr = std::min(baseAddr, si->virtualAddr()); + this->_entries[_dt_baseaddr].d_un.d_val = baseAddr; + + auto &got = _mipsTargetLayout.getGOTSection(); + + this->_entries[_dt_symtabno].d_un.d_val = this->getSymbolTable()->size(); + this->_entries[_dt_gotsym].d_un.d_val = + this-> getSymbolTable()->size() - got.getGlobalCount(); + this->_entries[_dt_localgot].d_un.d_val = got.getLocalCount(); + this->_entries[_dt_pltgot].d_un.d_ptr = + _mipsTargetLayout.findOutputSection(".got")->virtualAddr(); + } + + int64_t getGotPltTag() override { return DT_MIPS_PLTGOT; } + +protected: + /// \brief Adjust the symbol's value for microMIPS code. + uint64_t getAtomVirtualAddress(const AtomLayout *al) const override { + if (const auto *da = dyn_cast<DefinedAtom>(al->_atom)) + if (da->codeModel() == DefinedAtom::codeMipsMicro || + da->codeModel() == DefinedAtom::codeMipsMicroPIC) + return al->_virtualAddr | 1; + return al->_virtualAddr; + } + +private: + std::size_t _dt_symtabno; + std::size_t _dt_localgot; + std::size_t _dt_gotsym; + std::size_t _dt_pltgot; + std::size_t _dt_baseaddr; + MipsTargetLayout<MipsELFType> &_mipsTargetLayout; +}; + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFFile.h b/lib/ReaderWriter/ELF/Mips/MipsELFFile.h new file mode 100644 index 0000000000000..7381c7e977bf2 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsELFFile.h @@ -0,0 +1,331 @@ +//===- lib/ReaderWriter/ELF/MipsELFFile.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_MIPS_MIPS_ELF_FILE_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_ELF_FILE_H + +#include "ELFReader.h" +#include "MipsLinkingContext.h" +#include "MipsRelocationHandler.h" + +namespace llvm { +namespace object { + +template <class ELFT> +struct Elf_RegInfo; + +template <llvm::support::endianness TargetEndianness, std::size_t MaxAlign> +struct Elf_RegInfo<ELFType<TargetEndianness, MaxAlign, false>> { + LLVM_ELF_IMPORT_TYPES(TargetEndianness, MaxAlign, false) + Elf_Word ri_gprmask; // bit-mask of used general registers + Elf_Word ri_cprmask[4]; // bit-mask of used co-processor registers + Elf_Addr ri_gp_value; // gp register value +}; + +template <llvm::support::endianness TargetEndianness, std::size_t MaxAlign> +struct Elf_RegInfo<ELFType<TargetEndianness, MaxAlign, true>> { + LLVM_ELF_IMPORT_TYPES(TargetEndianness, MaxAlign, true) + Elf_Word ri_gprmask; // bit-mask of used general registers + Elf_Word ri_pad; // unused padding field + Elf_Word ri_cprmask[4]; // bit-mask of used co-processor registers + Elf_Addr ri_gp_value; // gp register value +}; + +template <class ELFT> struct Elf_Mips_Options { + LLVM_ELF_IMPORT_TYPES(ELFT::TargetEndianness, ELFT::MaxAlignment, + ELFT::Is64Bits) + uint8_t kind; // Determines interpretation of variable part of descriptor + uint8_t size; // Byte size of descriptor, including this header + Elf_Half section; // Section header index of section affected, + // or 0 for global options + Elf_Word info; // Kind-specific information +}; + +} // end namespace object. +} // end namespace llvm. + +namespace lld { +namespace elf { + +template <class ELFT> class MipsELFFile; + +template <class ELFT> +class MipsELFDefinedAtom : public ELFDefinedAtom<ELFT> { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + +public: + MipsELFDefinedAtom(const MipsELFFile<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) {} + + const MipsELFFile<ELFT>& file() const override { + return static_cast<const MipsELFFile<ELFT> &>(this->_owningFile); + } + + DefinedAtom::CodeModel codeModel() const override { + switch (this->_symbol->st_other & llvm::ELF::STO_MIPS_MIPS16) { + case llvm::ELF::STO_MIPS_MIPS16: + return DefinedAtom::codeMips16; + case llvm::ELF::STO_MIPS_PIC: + return DefinedAtom::codeMipsPIC; + case llvm::ELF::STO_MIPS_MICROMIPS: + return DefinedAtom::codeMipsMicro; + case llvm::ELF::STO_MIPS_MICROMIPS | llvm::ELF::STO_MIPS_PIC: + return DefinedAtom::codeMipsMicroPIC; + default: + return DefinedAtom::codeNA; + } + } +}; + +template <class ELFT> class MipsELFReference : public ELFReference<ELFT> { + typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel; + typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela; + + static const bool _isMips64EL = + ELFT::Is64Bits && ELFT::TargetEndianness == llvm::support::little; + +public: + MipsELFReference(uint64_t symValue, const Elf_Rela &rel) + : ELFReference<ELFT>( + &rel, rel.r_offset - symValue, Reference::KindArch::Mips, + rel.getType(_isMips64EL) & 0xff, rel.getSymbol(_isMips64EL)), + _tag(uint32_t(rel.getType(_isMips64EL)) >> 8) {} + + MipsELFReference(uint64_t symValue, const Elf_Rel &rel) + : ELFReference<ELFT>(rel.r_offset - symValue, Reference::KindArch::Mips, + rel.getType(_isMips64EL) & 0xff, + rel.getSymbol(_isMips64EL)), + _tag(uint32_t(rel.getType(_isMips64EL)) >> 8) {} + + uint32_t tag() const override { return _tag; } + void setTag(uint32_t tag) { _tag = tag; } + +private: + uint32_t _tag; +}; + +template <class ELFT> class MipsELFFile : public ELFFile<ELFT> { +public: + MipsELFFile(std::unique_ptr<MemoryBuffer> mb, MipsLinkingContext &ctx) + : ELFFile<ELFT>(std::move(mb), ctx) {} + + static ErrorOr<std::unique_ptr<MipsELFFile>> + create(std::unique_ptr<MemoryBuffer> mb, MipsLinkingContext &ctx) { + return std::unique_ptr<MipsELFFile<ELFT>>( + new MipsELFFile<ELFT>(std::move(mb), ctx)); + } + + bool isPIC() const { + return this->_objFile->getHeader()->e_flags & llvm::ELF::EF_MIPS_PIC; + } + + /// \brief gp register value stored in the .reginfo section. + int64_t getGP0() const { return _gp0 ? *_gp0 : 0; } + + /// \brief .tdata section address plus fixed offset. + uint64_t getTPOffset() const { return *_tpOff; } + uint64_t getDTPOffset() const { return *_dtpOff; } + +protected: + std::error_code doParse() override { + if (std::error_code ec = ELFFile<ELFT>::doParse()) + return ec; + // Retrieve some auxiliary data like GP value, TLS section address etc + // from the object file. + return readAuxData(); + } + +private: + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Rel_Iter Elf_Rel_Iter; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Rela_Iter Elf_Rela_Iter; + + enum { TP_OFFSET = 0x7000, DTP_OFFSET = 0x8000 }; + + static const bool _isMips64EL = + ELFT::Is64Bits && ELFT::TargetEndianness == llvm::support::little; + + llvm::Optional<int64_t> _gp0; + llvm::Optional<uint64_t> _tpOff; + llvm::Optional<uint64_t> _dtpOff; + + 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) MipsELFDefinedAtom<ELFT>( + *this, symName, sectionName, sym, sectionHdr, contentData, + referenceStart, referenceEnd, referenceList); + } + + const Elf_Shdr *findSectionByType(uint64_t type) { + for (const Elf_Shdr §ion : this->_objFile->sections()) + if (section.sh_type == type) + return §ion; + return nullptr; + } + + const Elf_Shdr *findSectionByFlags(uint64_t flags) { + for (const Elf_Shdr §ion : this->_objFile->sections()) + if (section.sh_flags & flags) + return §ion; + return nullptr; + } + + std::error_code readAuxData() { + using namespace llvm::ELF; + if (const Elf_Shdr *sec = findSectionByFlags(SHF_TLS)) { + _tpOff = sec->sh_addr + TP_OFFSET; + _dtpOff = sec->sh_addr + DTP_OFFSET; + } + + typedef llvm::object::Elf_RegInfo<ELFT> Elf_RegInfo; + typedef llvm::object::Elf_Mips_Options<ELFT> Elf_Mips_Options; + + if (const Elf_Shdr *sec = findSectionByType(SHT_MIPS_OPTIONS)) { + auto contents = this->getSectionContents(sec); + if (std::error_code ec = contents.getError()) + return ec; + + ArrayRef<uint8_t> raw = contents.get(); + while (!raw.empty()) { + if (raw.size() < sizeof(Elf_Mips_Options)) + return make_dynamic_error_code( + StringRef("Invalid size of MIPS_OPTIONS section")); + + const auto *opt = reinterpret_cast<const Elf_Mips_Options *>(raw.data()); + if (opt->kind == ODK_REGINFO) { + _gp0 = reinterpret_cast<const Elf_RegInfo *>(opt + 1)->ri_gp_value; + break; + } + raw = raw.slice(opt->size); + } + } else if (const Elf_Shdr *sec = findSectionByType(SHT_MIPS_REGINFO)) { + auto contents = this->getSectionContents(sec); + if (std::error_code ec = contents.getError()) + return ec; + + ArrayRef<uint8_t> raw = contents.get(); + if (raw.size() != sizeof(Elf_RegInfo)) + return make_dynamic_error_code( + StringRef("Invalid size of MIPS_REGINFO section")); + + _gp0 = reinterpret_cast<const Elf_RegInfo *>(raw.data())->ri_gp_value; + } + return std::error_code(); + } + + void createRelocationReferences(const Elf_Sym *symbol, + ArrayRef<uint8_t> content, + range<Elf_Rela_Iter> rels) override { + const auto value = this->getSymbolValue(symbol); + for (const auto &rel : rels) { + if (rel.r_offset < value || value + content.size() <= rel.r_offset) + continue; + auto r = new (this->_readerStorage) MipsELFReference<ELFT>(value, rel); + this->addReferenceToSymbol(r, symbol); + this->_references.push_back(r); + } + } + + void createRelocationReferences(const Elf_Sym *symbol, + ArrayRef<uint8_t> symContent, + ArrayRef<uint8_t> secContent, + range<Elf_Rel_Iter> rels) override { + const auto value = this->getSymbolValue(symbol); + for (Elf_Rel_Iter rit = rels.begin(), eit = rels.end(); rit != eit; ++rit) { + if (rit->r_offset < value || value + symContent.size() <= rit->r_offset) + continue; + + auto r = new (this->_readerStorage) MipsELFReference<ELFT>(value, *rit); + this->addReferenceToSymbol(r, symbol); + this->_references.push_back(r); + + auto addend = readAddend(*rit, secContent); + auto pairRelType = getPairRelocation(*rit); + if (pairRelType != llvm::ELF::R_MIPS_NONE) { + addend <<= 16; + auto mit = findMatchingRelocation(pairRelType, rit, eit); + if (mit != eit) + addend += int16_t(readAddend(*mit, secContent)); + else + // FIXME (simon): Show detailed warning. + llvm::errs() << "lld warning: cannot matching LO16 relocation\n"; + } + this->_references.back()->setAddend(addend); + } + } + + Reference::Addend readAddend(const Elf_Rel &ri, + const ArrayRef<uint8_t> content) const { + const auto &rh = + this->_ctx.template getTargetHandler<ELFT>().getRelocationHandler(); + return static_cast<const MipsRelocationHandler &>(rh) + .readAddend(getPrimaryType(ri), content.data() + ri.r_offset); + } + + uint32_t getPairRelocation(const Elf_Rel &rel) const { + switch (getPrimaryType(rel)) { + case llvm::ELF::R_MIPS_HI16: + return llvm::ELF::R_MIPS_LO16; + case llvm::ELF::R_MIPS_PCHI16: + return llvm::ELF::R_MIPS_PCLO16; + case llvm::ELF::R_MIPS_GOT16: + if (isLocalBinding(rel)) + return llvm::ELF::R_MIPS_LO16; + break; + case llvm::ELF::R_MICROMIPS_HI16: + return llvm::ELF::R_MICROMIPS_LO16; + case llvm::ELF::R_MICROMIPS_GOT16: + if (isLocalBinding(rel)) + return llvm::ELF::R_MICROMIPS_LO16; + break; + default: + // Nothing to do. + break; + } + return llvm::ELF::R_MIPS_NONE; + } + + Elf_Rel_Iter findMatchingRelocation(uint32_t pairRelType, Elf_Rel_Iter rit, + Elf_Rel_Iter eit) const { + return std::find_if(rit, eit, [&](const Elf_Rel &rel) { + return getPrimaryType(rel) == pairRelType && + rel.getSymbol(_isMips64EL) == rit->getSymbol(_isMips64EL); + }); + } + + static uint8_t getPrimaryType(const Elf_Rel &rel) { + return rel.getType(_isMips64EL) & 0xff; + } + bool isLocalBinding(const Elf_Rel &rel) const { + return this->_objFile->getSymbol(rel.getSymbol(_isMips64EL)) + ->getBinding() == llvm::ELF::STB_LOCAL; + } +}; + +template <class ELFT> class MipsDynamicFile : public DynamicFile<ELFT> { +public: + MipsDynamicFile(const MipsLinkingContext &context, StringRef name) + : DynamicFile<ELFT>(context, name) {} +}; + +} // elf +} // lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp b/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp new file mode 100644 index 0000000000000..0ef2c70b81564 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp @@ -0,0 +1,149 @@ +//===- lib/ReaderWriter/ELF/MipsELFFlagsMerger.cpp ------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsELFFlagsMerger.h" +#include "lld/Core/Error.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lld; +using namespace lld::elf; +using namespace llvm::ELF; + +struct MipsISATreeEdge { + unsigned child; + unsigned parent; +}; + +static MipsISATreeEdge isaTree[] = { + // MIPS32R6 and MIPS64R6 are not compatible with other extensions + + // MIPS64 extensions. + {EF_MIPS_ARCH_64R2, EF_MIPS_ARCH_64}, + // MIPS V extensions. + {EF_MIPS_ARCH_64, EF_MIPS_ARCH_5}, + // MIPS IV extensions. + {EF_MIPS_ARCH_5, EF_MIPS_ARCH_4}, + // MIPS III extensions. + {EF_MIPS_ARCH_4, EF_MIPS_ARCH_3}, + // MIPS32 extensions. + {EF_MIPS_ARCH_32R2, EF_MIPS_ARCH_32}, + // MIPS II extensions. + {EF_MIPS_ARCH_3, EF_MIPS_ARCH_2}, + {EF_MIPS_ARCH_32, EF_MIPS_ARCH_2}, + // MIPS I extensions. + {EF_MIPS_ARCH_2, EF_MIPS_ARCH_1}, +}; + +static bool matchMipsISA(unsigned base, unsigned ext) { + if (base == ext) + return true; + if (base == EF_MIPS_ARCH_32 && matchMipsISA(EF_MIPS_ARCH_64, ext)) + return true; + if (base == EF_MIPS_ARCH_32R2 && matchMipsISA(EF_MIPS_ARCH_64R2, ext)) + return true; + for (const auto &edge : isaTree) { + if (ext == edge.child) { + ext = edge.parent; + if (ext == base) + return true; + } + } + return false; +} + +MipsELFFlagsMerger::MipsELFFlagsMerger(bool is64Bits) + : _is64Bit(is64Bits), _flags(0) {} + +uint32_t MipsELFFlagsMerger::getMergedELFFlags() const { return _flags; } + +std::error_code MipsELFFlagsMerger::merge(uint8_t newClass, uint32_t newFlags) { + // Check bitness. + if (_is64Bit != (newClass == ELFCLASS64)) + return make_dynamic_error_code( + Twine("Bitness is incompatible with that of the selected target")); + + // We support two ABI: O32 and N64. The last one does not have + // the corresponding ELF flag. + uint32_t inAbi = newFlags & EF_MIPS_ABI; + uint32_t supportedAbi = _is64Bit ? 0 : uint32_t(EF_MIPS_ABI_O32); + if (inAbi != supportedAbi) + return make_dynamic_error_code(Twine("Unsupported ABI")); + + // ... and reduced set of architectures ... + uint32_t newArch = newFlags & EF_MIPS_ARCH; + switch (newArch) { + case EF_MIPS_ARCH_1: + case EF_MIPS_ARCH_2: + case EF_MIPS_ARCH_3: + case EF_MIPS_ARCH_4: + case EF_MIPS_ARCH_5: + case EF_MIPS_ARCH_32: + case EF_MIPS_ARCH_64: + case EF_MIPS_ARCH_32R2: + case EF_MIPS_ARCH_64R2: + case EF_MIPS_ARCH_32R6: + case EF_MIPS_ARCH_64R6: + break; + default: + return make_dynamic_error_code(Twine("Unsupported instruction set")); + } + + // ... and still do not support MIPS-16 extension. + if (newFlags & EF_MIPS_ARCH_ASE_M16) + return make_dynamic_error_code(Twine("Unsupported extension: MIPS16")); + + // PIC code is inherently CPIC and may not set CPIC flag explicitly. + // Ensure that this flag will exist in the linked file. + if (newFlags & EF_MIPS_PIC) + newFlags |= EF_MIPS_CPIC; + + std::lock_guard<std::mutex> lock(_mutex); + + // If the old set of flags is empty, use the new one as a result. + if (!_flags) { + _flags = newFlags; + return std::error_code(); + } + + // Check PIC / CPIC flags compatibility. + uint32_t newPic = newFlags & (EF_MIPS_PIC | EF_MIPS_CPIC); + uint32_t oldPic = _flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + + if ((newPic != 0) != (oldPic != 0)) + llvm::errs() << "lld warning: linking abicalls and non-abicalls files\n"; + + if (!(newPic & EF_MIPS_PIC)) + _flags &= ~EF_MIPS_PIC; + if (newPic) + _flags |= EF_MIPS_CPIC; + + // Check mixing -mnan=2008 / -mnan=legacy modules. + if ((newFlags & EF_MIPS_NAN2008) != (_flags & EF_MIPS_NAN2008)) + return make_dynamic_error_code( + Twine("Linking -mnan=2008 and -mnan=legacy modules")); + + // Check ISA compatibility and update the extension flag. + uint32_t oldArch = _flags & EF_MIPS_ARCH; + if (!matchMipsISA(newArch, oldArch)) { + if (!matchMipsISA(oldArch, newArch)) + return make_dynamic_error_code( + Twine("Linking modules with incompatible ISA")); + _flags &= ~EF_MIPS_ARCH; + _flags |= newArch; + } + + _flags |= newFlags & EF_MIPS_NOREORDER; + _flags |= newFlags & EF_MIPS_MICROMIPS; + _flags |= newFlags & EF_MIPS_NAN2008; + _flags |= newFlags & EF_MIPS_32BITMODE; + + return std::error_code(); +} diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.h b/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.h new file mode 100644 index 0000000000000..6ade86f0163cc --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.h @@ -0,0 +1,36 @@ +//===- lib/ReaderWriter/ELF/MipsELFFlagsMerger.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_MIPS_MIPS_ELF_FLAGS_MERGER_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_ELF_FLAGS_MERGER_H + +#include <mutex> +#include <system_error> + +namespace lld { +namespace elf { + +class MipsELFFlagsMerger { +public: + MipsELFFlagsMerger(bool is64Bits); + + uint32_t getMergedELFFlags() const; + + /// \brief Merge saved ELF header flags and the new set of flags. + std::error_code merge(uint8_t newClass, uint32_t newFlags); + +private: + const bool _is64Bit; + std::mutex _mutex; + uint32_t _flags; +}; + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFReader.h b/lib/ReaderWriter/ELF/Mips/MipsELFReader.h new file mode 100644 index 0000000000000..8b325b38bb522 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsELFReader.h @@ -0,0 +1,93 @@ +//===- lib/ReaderWriter/ELF/MipsELFReader.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_MIPS_MIPS_ELF_READER_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_ELF_READER_H + +#include "ELFReader.h" +#include "MipsELFFile.h" +#include "MipsELFFlagsMerger.h" +#include "MipsLinkingContext.h" + +namespace lld { +namespace elf { + +struct MipsELFFileCreateTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::File>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + MipsLinkingContext &ctx) { + return lld::elf::MipsELFFile<ELFT>::create(std::move(mb), ctx); + } +}; + +struct MipsDynamicFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + MipsLinkingContext &ctx) { + return lld::elf::MipsDynamicFile<ELFT>::create(std::move(mb), ctx); + } +}; + +template <class ELFT> +class MipsELFObjectReader + : public ELFObjectReader<ELFT, MipsELFFileCreateTraits, + MipsLinkingContext> { + typedef ELFObjectReader<ELFT, MipsELFFileCreateTraits, MipsLinkingContext> + BaseReaderType; + +public: + MipsELFObjectReader(MipsLinkingContext &ctx) + : BaseReaderType(ctx, llvm::ELF::EM_MIPS), + _flagMerger(ctx.getELFFlagsMerger()) {} + + std::error_code + loadFile(std::unique_ptr<MemoryBuffer> mb, const Registry ®istry, + std::vector<std::unique_ptr<File>> &result) const override { + auto &hdr = *this->elfHeader(*mb); + if (std::error_code ec = _flagMerger.merge(hdr.getFileClass(), hdr.e_flags)) + return ec; + return BaseReaderType::loadFile(std::move(mb), registry, result); + } + +private: + MipsELFFlagsMerger &_flagMerger; +}; + +template <class ELFT> +class MipsELFDSOReader + : public ELFDSOReader<ELFT, MipsDynamicFileCreateELFTraits, + MipsLinkingContext> { + typedef ELFDSOReader<ELFT, MipsDynamicFileCreateELFTraits, MipsLinkingContext> + BaseReaderType; + +public: + MipsELFDSOReader(MipsLinkingContext &ctx) + : BaseReaderType(ctx, llvm::ELF::EM_MIPS), + _flagMerger(ctx.getELFFlagsMerger()) {} + + std::error_code + loadFile(std::unique_ptr<MemoryBuffer> mb, const Registry ®istry, + std::vector<std::unique_ptr<File>> &result) const override { + auto &hdr = *this->elfHeader(*mb); + if (std::error_code ec = _flagMerger.merge(hdr.getFileClass(), hdr.e_flags)) + return ec; + return BaseReaderType::loadFile(std::move(mb), registry, result); + } + +private: + MipsELFFlagsMerger &_flagMerger; +}; + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFWriters.h b/lib/ReaderWriter/ELF/Mips/MipsELFWriters.h new file mode 100644 index 0000000000000..d94dd757a0f30 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsELFWriters.h @@ -0,0 +1,82 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsELFWriters.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_MIPS_MIPS_ELF_WRITERS_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_ELF_WRITERS_H + +#include "MipsLinkingContext.h" +#include "OutputELFWriter.h" + +namespace lld { +namespace elf { + +template <class ELFT> class MipsRuntimeFile; + +template <class ELFT> class MipsTargetLayout; + +template <typename ELFT> class MipsELFWriter { +public: + MipsELFWriter(MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &targetLayout) + : _ctx(ctx), _targetLayout(targetLayout) {} + + void setELFHeader(ELFHeader<ELFT> &elfHeader) { + elfHeader.e_version(1); + elfHeader.e_ident(llvm::ELF::EI_VERSION, llvm::ELF::EV_CURRENT); + elfHeader.e_ident(llvm::ELF::EI_OSABI, llvm::ELF::ELFOSABI_NONE); + if (_targetLayout.findOutputSection(".got.plt")) + elfHeader.e_ident(llvm::ELF::EI_ABIVERSION, 1); + else + elfHeader.e_ident(llvm::ELF::EI_ABIVERSION, 0); + + elfHeader.e_flags(_ctx.getMergedELFFlags()); + } + + void finalizeMipsRuntimeAtomValues() { + if (!_ctx.isDynamic()) + return; + + auto gotSection = _targetLayout.findOutputSection(".got"); + auto got = gotSection ? gotSection->virtualAddr() : 0; + auto gp = gotSection ? got + _targetLayout.getGPOffset() : 0; + + setAtomValue("_GLOBAL_OFFSET_TABLE_", got); + setAtomValue("_gp", gp); + setAtomValue("_gp_disp", gp); + setAtomValue("__gnu_local_gp", gp); + } + + bool hasGlobalGOTEntry(const Atom *a) const { + return _targetLayout.getGOTSection().hasGlobalGOTEntry(a); + } + + std::unique_ptr<MipsRuntimeFile<ELFT>> createRuntimeFile() { + auto file = llvm::make_unique<MipsRuntimeFile<ELFT>>(_ctx); + if (_ctx.isDynamic()) { + file->addAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); + file->addAbsoluteAtom("_gp"); + file->addAbsoluteAtom("_gp_disp"); + file->addAbsoluteAtom("__gnu_local_gp"); + } + return file; + } + +private: + MipsLinkingContext &_ctx; + MipsTargetLayout<ELFT> &_targetLayout; + + void setAtomValue(StringRef name, uint64_t value) { + auto atom = _targetLayout.findAbsoluteAtom(name); + assert(atom != _targetLayout.absoluteAtoms().end()); + (*atom)->_virtualAddr = value; + } +}; + +} // elf +} // lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsExecutableWriter.h b/lib/ReaderWriter/ELF/Mips/MipsExecutableWriter.h new file mode 100644 index 0000000000000..1a85bba3bd0f6 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsExecutableWriter.h @@ -0,0 +1,154 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsExecutableWriter.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_MIPS_MIPS_EXECUTABLE_WRITER_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_EXECUTABLE_WRITER_H + +#include "ExecutableWriter.h" +#include "MipsDynamicTable.h" +#include "MipsELFWriters.h" +#include "MipsLinkingContext.h" + +namespace lld { +namespace elf { + +template <typename ELFT> class MipsTargetLayout; + +template <class ELFT> +class MipsExecutableWriter : public ExecutableWriter<ELFT> { +public: + MipsExecutableWriter(MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &layout); + +protected: + void buildDynamicSymbolTable(const File &file) override; + + // Add any runtime files and their atoms to the output + bool createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; + + void finalizeDefaultAtomValues() override; + std::error_code setELFHeader() override; + + unique_bump_ptr<SymbolTable<ELFT>> createSymbolTable() override; + unique_bump_ptr<DynamicTable<ELFT>> createDynamicTable() override; + + unique_bump_ptr<DynamicSymbolTable<ELFT>> + createDynamicSymbolTable() override; + +private: + MipsELFWriter<ELFT> _writeHelper; + MipsTargetLayout<ELFT> &_mipsTargetLayout; +}; + +template <class ELFT> +MipsExecutableWriter<ELFT>::MipsExecutableWriter(MipsLinkingContext &ctx, + MipsTargetLayout<ELFT> &layout) + : ExecutableWriter<ELFT>(ctx, layout), _writeHelper(ctx, layout), + _mipsTargetLayout(layout) {} + +template <class ELFT> +std::error_code MipsExecutableWriter<ELFT>::setELFHeader() { + std::error_code ec = ExecutableWriter<ELFT>::setELFHeader(); + if (ec) + return ec; + + StringRef entryName = this->_context.entrySymbolName(); + if (const AtomLayout *al = this->_layout.findAtomLayoutByName(entryName)) { + const auto *ea = cast<DefinedAtom>(al->_atom); + if (ea->codeModel() == DefinedAtom::codeMipsMicro || + ea->codeModel() == DefinedAtom::codeMipsMicroPIC) + // Adjust entry symbol value if this symbol is microMIPS encoded. + this->_elfHeader->e_entry(al->_virtualAddr | 1); + } + + _writeHelper.setELFHeader(*this->_elfHeader); + return std::error_code(); +} + +template <class ELFT> +void MipsExecutableWriter<ELFT>::buildDynamicSymbolTable(const File &file) { + // MIPS ABI requires to add to dynsym even undefined symbols + // if they have a corresponding entries in a global part of GOT. + for (auto sec : this->_layout.sections()) + if (auto section = dyn_cast<AtomSection<ELFT>>(sec)) + for (const auto &atom : section->atoms()) { + if (_writeHelper.hasGlobalGOTEntry(atom->_atom)) { + this->_dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), + atom->_virtualAddr, atom); + continue; + } + + const DefinedAtom *da = dyn_cast<const DefinedAtom>(atom->_atom); + if (!da) + continue; + + if (da->dynamicExport() != DefinedAtom::dynamicExportAlways && + !this->_context.isDynamicallyExportedSymbol(da->name()) && + !(this->_context.shouldExportDynamic() && + da->scope() == Atom::Scope::scopeGlobal)) + continue; + + this->_dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), + atom->_virtualAddr, atom); + } + + for (const UndefinedAtom *a : file.undefined()) + // FIXME (simon): Consider to move this check to the + // MipsELFUndefinedAtom class method. That allows to + // handle more complex coditions in the future. + if (_writeHelper.hasGlobalGOTEntry(a)) + this->_dynamicSymbolTable->addSymbol(a, ELF::SHN_UNDEF); + + // Skip our immediate parent class method + // ExecutableWriter<ELFT>::buildDynamicSymbolTable because we replaced it + // with our own version. Call OutputELFWriter directly. + OutputELFWriter<ELFT>::buildDynamicSymbolTable(file); +} + +template <class ELFT> +bool MipsExecutableWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + ExecutableWriter<ELFT>::createImplicitFiles(result); + result.push_back(std::move(_writeHelper.createRuntimeFile())); + return true; +} + +template <class ELFT> +void MipsExecutableWriter<ELFT>::finalizeDefaultAtomValues() { + // Finalize the atom values that are part of the parent. + ExecutableWriter<ELFT>::finalizeDefaultAtomValues(); + _writeHelper.finalizeMipsRuntimeAtomValues(); +} + +template <class ELFT> +unique_bump_ptr<SymbolTable<ELFT>> + MipsExecutableWriter<ELFT>::createSymbolTable() { + return unique_bump_ptr<SymbolTable<ELFT>>(new ( + this->_alloc) MipsSymbolTable<ELFT>(this->_context)); +} + +/// \brief create dynamic table +template <class ELFT> +unique_bump_ptr<DynamicTable<ELFT>> + MipsExecutableWriter<ELFT>::createDynamicTable() { + return unique_bump_ptr<DynamicTable<ELFT>>(new ( + this->_alloc) MipsDynamicTable<ELFT>(this->_context, _mipsTargetLayout)); +} + +/// \brief create dynamic symbol table +template <class ELFT> +unique_bump_ptr<DynamicSymbolTable<ELFT>> + MipsExecutableWriter<ELFT>::createDynamicSymbolTable() { + return unique_bump_ptr<DynamicSymbolTable<ELFT>>( + new (this->_alloc) MipsDynamicSymbolTable<ELFT>( + this->_context, _mipsTargetLayout)); +} + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp b/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp new file mode 100644 index 0000000000000..7bffcbeb5c085 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp @@ -0,0 +1,115 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsLinkingContext.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 "MipsCtorsOrderPass.h" +#include "MipsLinkingContext.h" +#include "MipsRelocationPass.h" +#include "MipsTargetHandler.h" + +using namespace lld; +using namespace lld::elf; + +std::unique_ptr<ELFLinkingContext> +MipsLinkingContext::create(llvm::Triple triple) { + if (triple.getArch() == llvm::Triple::mipsel || + triple.getArch() == llvm::Triple::mips64el) + return std::unique_ptr<ELFLinkingContext>(new MipsLinkingContext(triple)); + return nullptr; +} + +typedef std::unique_ptr<TargetHandlerBase> TargetHandlerBasePtr; + +static TargetHandlerBasePtr createTarget(llvm::Triple triple, + MipsLinkingContext &ctx) { + switch (triple.getArch()) { + case llvm::Triple::mipsel: + return TargetHandlerBasePtr(new MipsTargetHandler<Mips32ELType>(ctx)); + case llvm::Triple::mips64el: + return TargetHandlerBasePtr(new MipsTargetHandler<Mips64ELType>(ctx)); + default: + llvm_unreachable("Unhandled arch"); + } +} + +MipsLinkingContext::MipsLinkingContext(llvm::Triple triple) + : ELFLinkingContext(triple, createTarget(triple, *this)), + _flagsMerger(triple.isArch64Bit()) {} + +uint32_t MipsLinkingContext::getMergedELFFlags() const { + return _flagsMerger.getMergedELFFlags(); +} + +MipsELFFlagsMerger &MipsLinkingContext::getELFFlagsMerger() { + return _flagsMerger; +} + +uint64_t MipsLinkingContext::getBaseAddress() const { + if (_baseAddress == 0 && getOutputELFType() == llvm::ELF::ET_EXEC) + return getTriple().isArch64Bit() ? 0x120000000 : 0x400000; + return _baseAddress; +} + +StringRef MipsLinkingContext::entrySymbolName() const { + if (_outputELFType == elf::ET_EXEC && _entrySymbolName.empty()) + return "__start"; + return _entrySymbolName; +} + +StringRef MipsLinkingContext::getDefaultInterpreter() const { + return getTriple().isArch64Bit() ? "/lib64/ld.so.1" : "/lib/ld.so.1"; +} + +void MipsLinkingContext::addPasses(PassManager &pm) { + auto pass = createMipsRelocationPass(*this); + if (pass) + pm.add(std::move(pass)); + ELFLinkingContext::addPasses(pm); + pm.add(llvm::make_unique<elf::MipsCtorsOrderPass>()); +} + +bool MipsLinkingContext::isDynamicRelocation(const Reference &r) const { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::Mips); + switch (r.kindValue()) { + case llvm::ELF::R_MIPS_COPY: + case llvm::ELF::R_MIPS_REL32: + case llvm::ELF::R_MIPS_TLS_DTPMOD32: + case llvm::ELF::R_MIPS_TLS_DTPREL32: + case llvm::ELF::R_MIPS_TLS_TPREL32: + case llvm::ELF::R_MIPS_TLS_DTPMOD64: + case llvm::ELF::R_MIPS_TLS_DTPREL64: + case llvm::ELF::R_MIPS_TLS_TPREL64: + return true; + default: + return false; + } +} + +bool MipsLinkingContext::isCopyRelocation(const Reference &r) const { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::Mips); + if (r.kindValue() == llvm::ELF::R_MIPS_COPY) + return true; + return false; +} + +bool MipsLinkingContext::isPLTRelocation(const Reference &r) const { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::Mips); + switch (r.kindValue()) { + case llvm::ELF::R_MIPS_JUMP_SLOT: + return true; + default: + return false; + } +} diff --git a/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.h b/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.h new file mode 100644 index 0000000000000..824605f5fa7f2 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.h @@ -0,0 +1,68 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsLinkingContext.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_MIPS_MIPS_LINKING_CONTEXT_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_LINKING_CONTEXT_H + +#include "MipsELFFlagsMerger.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" + +namespace lld { +namespace elf { + +/// \brief Mips internal references. +enum { + /// \brief Do nothing but mark GOT entry as a global one. + LLD_R_MIPS_GLOBAL_GOT = 1024, + /// \brief Apply high 16 bits of symbol + addend. + LLD_R_MIPS_32_HI16 = 1025, + /// \brief The same as R_MIPS_26 but for global symbols. + LLD_R_MIPS_GLOBAL_26 = 1026, + /// \brief Setup hi 16 bits using the symbol this reference refers to. + LLD_R_MIPS_HI16 = 1027, + /// \brief Setup low 16 bits using the symbol this reference refers to. + LLD_R_MIPS_LO16 = 1028, + /// \brief Represents a reference between PLT and dynamic symbol. + LLD_R_MIPS_STO_PLT = 1029, + /// \brief The same as R_MICROMIPS_26_S1 but for global symbols. + LLD_R_MICROMIPS_GLOBAL_26_S1 = 1030, + /// \brief Apply high 32+16 bits of symbol + addend. + LLD_R_MIPS_64_HI16 = 1031, +}; + +typedef llvm::object::ELFType<llvm::support::little, 2, false> Mips32ELType; +typedef llvm::object::ELFType<llvm::support::little, 2, true> Mips64ELType; +typedef llvm::object::ELFType<llvm::support::big, 2, false> Mips32BEType; +typedef llvm::object::ELFType<llvm::support::big, 2, true> Mips64BEType; + +class MipsLinkingContext final : public ELFLinkingContext { +public: + static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + MipsLinkingContext(llvm::Triple triple); + + uint32_t getMergedELFFlags() const; + MipsELFFlagsMerger &getELFFlagsMerger(); + + // ELFLinkingContext + uint64_t getBaseAddress() const override; + StringRef entrySymbolName() const override; + StringRef getDefaultInterpreter() const override; + void addPasses(PassManager &pm) override; + bool isRelaOutputFormat() const override { return false; } + bool isDynamicRelocation(const Reference &r) const override; + bool isCopyRelocation(const Reference &r) const override; + bool isPLTRelocation(const Reference &r) const override; + +private: + MipsELFFlagsMerger _flagsMerger; +}; + +} // elf +} // lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp b/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp new file mode 100644 index 0000000000000..173ce0e6b1a87 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp @@ -0,0 +1,606 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp ----------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsTargetHandler.h" +#include "MipsLinkingContext.h" +#include "MipsRelocationHandler.h" + +using namespace lld; +using namespace elf; +using namespace llvm::ELF; +using namespace llvm::support; + +namespace { +enum class CrossJumpMode { + None, // Not a jump or non-isa-cross jump + ToRegular, // cross isa jump to regular symbol + ToMicro // cross isa jump to microMips symbol +}; + +struct MipsRelocationParams { + uint8_t _size; // Relocations's size in bytes + uint64_t _mask; // Read/write mask of relocation + uint8_t _shift; // Relocation's addendum left shift size + bool _shuffle; // Relocation's addendum/result needs to be shuffled +}; + +template <class ELFT> class RelocationHandler : public MipsRelocationHandler { +public: + RelocationHandler(MipsLinkingContext &ctx) : _ctx(ctx) {} + + std::error_code applyRelocation(ELFWriter &writer, + llvm::FileOutputBuffer &buf, + const lld::AtomLayout &atom, + const Reference &ref) const override; + + Reference::Addend readAddend(Reference::KindValue kind, + const uint8_t *content) const override; + +private: + MipsLinkingContext &_ctx; +}; +} + +static MipsRelocationParams getRelocationParams(uint32_t rType) { + switch (rType) { + case R_MIPS_NONE: + return {4, 0x0, 0, false}; + case R_MIPS_64: + case R_MIPS_SUB: + return {8, 0xffffffffffffffffull, 0, false}; + case R_MIPS_32: + case R_MIPS_GPREL32: + case R_MIPS_PC32: + return {4, 0xffffffff, 0, false}; + case LLD_R_MIPS_32_HI16: + return {4, 0xffff0000, 0, false}; + case LLD_R_MIPS_64_HI16: + return {8, 0xffffffffffff0000ull, 0, false}; + case R_MIPS_26: + case LLD_R_MIPS_GLOBAL_26: + return {4, 0x3ffffff, 2, false}; + case R_MIPS_PC18_S3: + return {4, 0x3ffff, 3, false}; + case R_MIPS_PC19_S2: + return {4, 0x7ffff, 2, false}; + case R_MIPS_PC21_S2: + return {4, 0x1fffff, 2, false}; + case R_MIPS_PC26_S2: + return {4, 0x3ffffff, 2, false}; + case R_MIPS_HI16: + case R_MIPS_LO16: + case R_MIPS_PCHI16: + case R_MIPS_PCLO16: + case R_MIPS_GPREL16: + case R_MIPS_GOT16: + case R_MIPS_GOT_DISP: + case R_MIPS_GOT_PAGE: + case R_MIPS_GOT_OFST: + case R_MIPS_TLS_DTPREL_HI16: + case R_MIPS_TLS_DTPREL_LO16: + case R_MIPS_TLS_TPREL_HI16: + case R_MIPS_TLS_TPREL_LO16: + case LLD_R_MIPS_HI16: + case LLD_R_MIPS_LO16: + return {4, 0xffff, 0, false}; + case R_MICROMIPS_TLS_DTPREL_HI16: + case R_MICROMIPS_TLS_DTPREL_LO16: + case R_MICROMIPS_TLS_TPREL_HI16: + case R_MICROMIPS_TLS_TPREL_LO16: + return {4, 0xffff, 0, true}; + case R_MICROMIPS_26_S1: + case LLD_R_MICROMIPS_GLOBAL_26_S1: + return {4, 0x3ffffff, 1, true}; + case R_MICROMIPS_HI16: + case R_MICROMIPS_LO16: + case R_MICROMIPS_GOT16: + return {4, 0xffff, 0, true}; + case R_MICROMIPS_PC16_S1: + return {4, 0xffff, 1, true}; + case R_MICROMIPS_PC7_S1: + return {4, 0x7f, 1, false}; + case R_MICROMIPS_PC10_S1: + return {4, 0x3ff, 1, false}; + case R_MICROMIPS_PC23_S2: + return {4, 0x7fffff, 2, true}; + case R_MIPS_CALL16: + case R_MIPS_TLS_GD: + case R_MIPS_TLS_LDM: + case R_MIPS_TLS_GOTTPREL: + return {4, 0xffff, 0, false}; + case R_MICROMIPS_CALL16: + case R_MICROMIPS_TLS_GD: + case R_MICROMIPS_TLS_LDM: + case R_MICROMIPS_TLS_GOTTPREL: + return {4, 0xffff, 0, true}; + case R_MIPS_JALR: + return {4, 0x0, 0, false}; + case R_MICROMIPS_JALR: + return {4, 0x0, 0, true}; + case R_MIPS_REL32: + case R_MIPS_JUMP_SLOT: + case R_MIPS_COPY: + case R_MIPS_TLS_DTPMOD32: + case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_TPREL32: + // Ignore runtime relocations. + return {4, 0x0, 0, false}; + case R_MIPS_TLS_DTPMOD64: + case R_MIPS_TLS_DTPREL64: + case R_MIPS_TLS_TPREL64: + return {8, 0x0, 0, false}; + case LLD_R_MIPS_GLOBAL_GOT: + case LLD_R_MIPS_STO_PLT: + // Do nothing. + return {4, 0x0, 0, false}; + default: + llvm_unreachable("Unknown relocation"); + } +} + +/// \brief R_MIPS_32 +/// local/external: word32 S + A (truncate) +static uint32_t reloc32(uint64_t S, int64_t A) { return S + A; } + +/// \brief R_MIPS_64 +/// local/external: word64 S + A (truncate) +static uint64_t reloc64(uint64_t S, int64_t A) { return S + A; } + +/// \brief R_MIPS_SUB +/// local/external: word64 S - A (truncate) +static uint64_t relocSub(uint64_t S, int64_t A) { return S - A; } + +/// \brief R_MIPS_PC32 +/// local/external: word32 S + A i- P (truncate) +static uint32_t relocpc32(uint64_t P, uint64_t S, int64_t A) { + return S + A - P; +} + +/// \brief R_MIPS_26, R_MICROMIPS_26_S1 +/// local : ((A | ((P + 4) & 0x3F000000)) + S) >> 2 +static uint32_t reloc26loc(uint64_t P, uint64_t S, int32_t A, uint32_t shift) { + uint32_t result = (A | ((P + 4) & (0xfc000000 << shift))) + S; + return result >> shift; +} + +/// \brief LLD_R_MIPS_GLOBAL_26, LLD_R_MICROMIPS_GLOBAL_26_S1 +/// external: (sign-extend(A) + S) >> 2 +static uint32_t reloc26ext(uint64_t S, int32_t A, uint32_t shift) { + int32_t result = + shift == 1 ? llvm::SignExtend32<27>(A) : llvm::SignExtend32<28>(A); + return (result + S) >> shift; +} + +/// \brief R_MIPS_HI16, R_MIPS_TLS_DTPREL_HI16, R_MIPS_TLS_TPREL_HI16, +/// R_MICROMIPS_HI16, R_MICROMIPS_TLS_DTPREL_HI16, R_MICROMIPS_TLS_TPREL_HI16, +/// LLD_R_MIPS_HI16 +/// local/external: hi16 (AHL + S) - (short)(AHL + S) (truncate) +/// _gp_disp : hi16 (AHL + GP - P) - (short)(AHL + GP - P) (verify) +static uint32_t relocHi16(uint64_t P, uint64_t S, int64_t AHL, bool isGPDisp) { + int32_t result = isGPDisp ? AHL + S - P : AHL + S; + return (result + 0x8000) >> 16; +} + +/// \brief R_MIPS_PCHI16 +/// local/external: hi16 (S + AHL - P) +static uint32_t relocPcHi16(uint64_t P, uint64_t S, int64_t AHL) { + int32_t result = S + AHL - P; + return (result + 0x8000) >> 16; +} + +/// \brief R_MIPS_LO16, R_MIPS_TLS_DTPREL_LO16, R_MIPS_TLS_TPREL_LO16, +/// R_MICROMIPS_LO16, R_MICROMIPS_TLS_DTPREL_LO16, R_MICROMIPS_TLS_TPREL_LO16, +/// LLD_R_MIPS_LO16 +/// local/external: lo16 AHL + S (truncate) +/// _gp_disp : lo16 AHL + GP - P + 4 (verify) +static uint32_t relocLo16(uint64_t P, uint64_t S, int64_t AHL, bool isGPDisp, + bool micro) { + int32_t result = isGPDisp ? AHL + S - P + (micro ? 3 : 4) : AHL + S; + return result; +} + +/// \brief R_MIPS_PCLO16 +/// local/external: lo16 (S + AHL - P) +static uint32_t relocPcLo16(uint64_t P, uint64_t S, int64_t AHL) { + AHL = llvm::SignExtend32<16>(AHL); + int32_t result = S + AHL - P; + return result; +} + +/// \brief R_MIPS_GOT16, R_MIPS_CALL16, R_MICROMIPS_GOT16, R_MICROMIPS_CALL16 +/// rel16 G (verify) +static uint64_t relocGOT(uint64_t S, uint64_t GP) { + int64_t G = (int64_t)(S - GP); + return G; +} + +/// R_MIPS_GOT_OFST +/// rel16 offset of (S+A) from the page pointer (verify) +static uint32_t relocGOTOfst(uint64_t S, int64_t A) { + uint64_t page = (S + A + 0x8000) & ~0xffff; + return S + A - page; +} + +/// \brief R_MIPS_GPREL16 +/// local: sign-extend(A) + S + GP0 - GP +/// external: sign-extend(A) + S - GP +static uint64_t relocGPRel16(uint64_t S, int64_t A, uint64_t GP) { + // We added GP0 to addendum for a local symbol during a Relocation pass. + return llvm::SignExtend32<16>(A) + S - GP; +} + +/// \brief R_MIPS_GPREL32 +/// local: rel32 A + S + GP0 - GP (truncate) +static uint64_t relocGPRel32(uint64_t S, int64_t A, uint64_t GP) { + // We added GP0 to addendum for a local symbol during a Relocation pass. + return A + S - GP; +} + +/// \brief R_MIPS_PC18_S3 +/// local/external: (S + A - P) >> 3 (P with cleared 3 less significant bits) +static uint32_t relocPc18(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<21>(A); + // FIXME (simon): Check that S + A has 8-byte alignment + int32_t result = S + A - ((P | 7) ^ 7); + return result >> 3; +} + +/// \brief R_MIPS_PC19_S2 +/// local/external: (S + A - P) >> 2 +static uint32_t relocPc19(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<21>(A); + // FIXME (simon): Check that S + A has 4-byte alignment + int32_t result = S + A - P; + return result >> 2; +} + +/// \brief R_MIPS_PC21_S2 +/// local/external: (S + A - P) >> 2 +static uint32_t relocPc21(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<23>(A); + // FIXME (simon): Check that S + A has 4-byte alignment + int32_t result = S + A - P; + return result >> 2; +} + +/// \brief R_MIPS_PC26_S2 +/// local/external: (S + A - P) >> 2 +static uint32_t relocPc26(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<28>(A); + // FIXME (simon): Check that S + A has 4-byte alignment + int32_t result = S + A - P; + return result >> 2; +} + +/// \brief R_MICROMIPS_PC7_S1 +static uint32_t relocPc7(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<8>(A); + int32_t result = S + A - P; + return result >> 1; +} + +/// \brief R_MICROMIPS_PC10_S1 +static uint32_t relocPc10(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<11>(A); + int32_t result = S + A - P; + return result >> 1; +} + +/// \brief R_MICROMIPS_PC16_S1 +static uint32_t relocPc16(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<17>(A); + int32_t result = S + A - P; + return result >> 1; +} + +/// \brief R_MICROMIPS_PC23_S2 +static uint32_t relocPc23(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<25>(A); + int32_t result = S + A - P; + + // Check addiupc 16MB range. + if (result + 0x1000000 >= 0x2000000) + llvm::errs() << "The addiupc instruction immediate " + << llvm::format_hex(result, 10) << " is out of range.\n"; + + return result >> 2; +} + +/// \brief LLD_R_MIPS_32_HI16, LLD_R_MIPS_64_HI16 +static uint64_t relocMaskLow16(uint64_t S, int64_t A) { + return S + A + 0x8000; +} + +static std::error_code adjustJumpOpCode(uint64_t &ins, uint64_t tgt, + CrossJumpMode mode) { + if (mode == CrossJumpMode::None) + return std::error_code(); + + bool toMicro = mode == CrossJumpMode::ToMicro; + uint32_t opNative = toMicro ? 0x03 : 0x3d; + uint32_t opCross = toMicro ? 0x1d : 0x3c; + + if ((tgt & 1) != toMicro) + return make_dynamic_error_code( + Twine("Incorrect bit 0 for the jalx target")); + + if (tgt & 2) + return make_dynamic_error_code(Twine("The jalx target 0x") + + Twine::utohexstr(tgt) + + " is not word-aligned"); + uint8_t op = ins >> 26; + if (op != opNative && op != opCross) + return make_dynamic_error_code(Twine("Unsupported jump opcode (0x") + + Twine::utohexstr(op) + + ") for ISA modes cross call"); + + ins = (ins & ~(0x3f << 26)) | (opCross << 26); + return std::error_code(); +} + +static bool isMicroMipsAtom(const Atom *a) { + if (const auto *da = dyn_cast<DefinedAtom>(a)) + return da->codeModel() == DefinedAtom::codeMipsMicro || + da->codeModel() == DefinedAtom::codeMipsMicroPIC; + return false; +} + +static CrossJumpMode getCrossJumpMode(const Reference &ref) { + if (!isa<DefinedAtom>(ref.target())) + return CrossJumpMode::None; + bool isTgtMicro = isMicroMipsAtom(ref.target()); + switch (ref.kindValue()) { + case R_MIPS_26: + case LLD_R_MIPS_GLOBAL_26: + return isTgtMicro ? CrossJumpMode::ToMicro : CrossJumpMode::None; + case R_MICROMIPS_26_S1: + case LLD_R_MICROMIPS_GLOBAL_26_S1: + return isTgtMicro ? CrossJumpMode::None : CrossJumpMode::ToRegular; + default: + return CrossJumpMode::None; + } +} + +static uint32_t microShuffle(uint32_t ins) { + return ((ins & 0xffff) << 16) | ((ins & 0xffff0000) >> 16); +} + +static ErrorOr<uint64_t> calculateRelocation(Reference::KindValue kind, + Reference::Addend addend, + uint64_t tgtAddr, uint64_t relAddr, + uint64_t gpAddr, bool isGP, + CrossJumpMode jumpMode) { + bool isCrossJump = jumpMode != CrossJumpMode::None; + switch (kind) { + case R_MIPS_NONE: + return 0; + case R_MIPS_32: + return reloc32(tgtAddr, addend); + case R_MIPS_64: + return reloc64(tgtAddr, addend); + case R_MIPS_SUB: + return relocSub(tgtAddr, addend); + case R_MIPS_26: + return reloc26loc(relAddr, tgtAddr, addend, 2); + case R_MICROMIPS_26_S1: + return reloc26loc(relAddr, tgtAddr, addend, isCrossJump ? 2 : 1); + case R_MIPS_HI16: + case R_MICROMIPS_HI16: + return relocHi16(relAddr, tgtAddr, addend, isGP); + case R_MIPS_PCHI16: + return relocPcHi16(relAddr, tgtAddr, addend); + case R_MIPS_LO16: + return relocLo16(relAddr, tgtAddr, addend, isGP, false); + case R_MIPS_PCLO16: + return relocPcLo16(relAddr, tgtAddr, addend); + case R_MICROMIPS_LO16: + return relocLo16(relAddr, tgtAddr, addend, isGP, true); + case R_MIPS_GOT16: + case R_MIPS_CALL16: + case R_MIPS_GOT_DISP: + case R_MIPS_GOT_PAGE: + case R_MICROMIPS_GOT16: + case R_MICROMIPS_CALL16: + case R_MIPS_TLS_GD: + case R_MIPS_TLS_LDM: + case R_MIPS_TLS_GOTTPREL: + case R_MICROMIPS_TLS_GD: + case R_MICROMIPS_TLS_LDM: + case R_MICROMIPS_TLS_GOTTPREL: + return relocGOT(tgtAddr, gpAddr); + case R_MIPS_GOT_OFST: + return relocGOTOfst(tgtAddr, addend); + case R_MIPS_PC18_S3: + return relocPc18(relAddr, tgtAddr, addend); + case R_MIPS_PC19_S2: + return relocPc19(relAddr, tgtAddr, addend); + case R_MIPS_PC21_S2: + return relocPc21(relAddr, tgtAddr, addend); + case R_MIPS_PC26_S2: + return relocPc26(relAddr, tgtAddr, addend); + case R_MICROMIPS_PC7_S1: + return relocPc7(relAddr, tgtAddr, addend); + case R_MICROMIPS_PC10_S1: + return relocPc10(relAddr, tgtAddr, addend); + case R_MICROMIPS_PC16_S1: + return relocPc16(relAddr, tgtAddr, addend); + case R_MICROMIPS_PC23_S2: + return relocPc23(relAddr, tgtAddr, addend); + case R_MIPS_TLS_DTPREL_HI16: + case R_MIPS_TLS_TPREL_HI16: + case R_MICROMIPS_TLS_DTPREL_HI16: + case R_MICROMIPS_TLS_TPREL_HI16: + return relocHi16(0, tgtAddr, addend, false); + case R_MIPS_TLS_DTPREL_LO16: + case R_MIPS_TLS_TPREL_LO16: + return relocLo16(0, tgtAddr, addend, false, false); + case R_MICROMIPS_TLS_DTPREL_LO16: + case R_MICROMIPS_TLS_TPREL_LO16: + return relocLo16(0, tgtAddr, addend, false, true); + case R_MIPS_GPREL16: + return relocGPRel16(tgtAddr, addend, gpAddr); + case R_MIPS_GPREL32: + return relocGPRel32(tgtAddr, addend, gpAddr); + case R_MIPS_JALR: + case R_MICROMIPS_JALR: + // We do not do JALR optimization now. + return 0; + case R_MIPS_REL32: + case R_MIPS_JUMP_SLOT: + case R_MIPS_COPY: + case R_MIPS_TLS_DTPMOD32: + case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_TPREL32: + case R_MIPS_TLS_DTPMOD64: + case R_MIPS_TLS_DTPREL64: + case R_MIPS_TLS_TPREL64: + // Ignore runtime relocations. + return 0; + case R_MIPS_PC32: + return relocpc32(relAddr, tgtAddr, addend); + case LLD_R_MIPS_GLOBAL_GOT: + // Do nothing. + case LLD_R_MIPS_32_HI16: + case LLD_R_MIPS_64_HI16: + return relocMaskLow16(tgtAddr, addend); + case LLD_R_MIPS_GLOBAL_26: + return reloc26ext(tgtAddr, addend, 2); + case LLD_R_MICROMIPS_GLOBAL_26_S1: + return reloc26ext(tgtAddr, addend, isCrossJump ? 2 : 1); + case LLD_R_MIPS_HI16: + return relocHi16(0, tgtAddr, 0, false); + case LLD_R_MIPS_LO16: + return relocLo16(0, tgtAddr, 0, false, false); + case LLD_R_MIPS_STO_PLT: + // Do nothing. + return 0; + default: + return make_unhandled_reloc_error(); + } +} + +template <class ELFT> +static uint64_t relocRead(const MipsRelocationParams ¶ms, + const uint8_t *loc) { + uint64_t data; + switch (params._size) { + case 4: + data = endian::read<uint32_t, ELFT::TargetEndianness, unaligned>(loc); + break; + case 8: + data = endian::read<uint64_t, ELFT::TargetEndianness, unaligned>(loc); + break; + default: + llvm_unreachable("Unexpected size"); + } + if (params._shuffle) + data = microShuffle(data); + return data; +} + +template <class ELFT> +static void relocWrite(uint64_t data, const MipsRelocationParams ¶ms, + uint8_t *loc) { + if (params._shuffle) + data = microShuffle(data); + switch (params._size) { + case 4: + endian::write<uint32_t, ELFT::TargetEndianness, unaligned>(loc, data); + break; + case 8: + endian::write<uint64_t, ELFT::TargetEndianness, unaligned>(loc, data); + break; + default: + llvm_unreachable("Unexpected size"); + } +} + +template <class ELFT> +std::error_code RelocationHandler<ELFT>::applyRelocation( + ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom, + const Reference &ref) const { + if (ref.kindNamespace() != lld::Reference::KindNamespace::ELF) + return std::error_code(); + assert(ref.kindArch() == Reference::KindArch::Mips); + + auto &targetLayout = static_cast<MipsTargetLayout<ELFT> &>( + _ctx.getTargetHandler<ELFT>().getTargetLayout()); + + AtomLayout *gpAtom = targetLayout.getGP(); + uint64_t gpAddr = gpAtom ? gpAtom->_virtualAddr : 0; + + AtomLayout *gpDispAtom = targetLayout.getGPDisp(); + bool isGpDisp = gpDispAtom && ref.target() == gpDispAtom->_atom; + + uint8_t *atomContent = buf.getBufferStart() + atom._fileOffset; + uint8_t *location = atomContent + ref.offsetInAtom(); + uint64_t tgtAddr = writer.addressOfAtom(ref.target()); + uint64_t relAddr = atom._virtualAddr + ref.offsetInAtom(); + + if (isMicroMipsAtom(ref.target())) + tgtAddr |= 1; + + CrossJumpMode jumpMode = getCrossJumpMode(ref); + + ErrorOr<uint64_t> res = + calculateRelocation(ref.kindValue(), ref.addend(), tgtAddr, relAddr, + gpAddr, isGpDisp, jumpMode); + if (auto ec = res.getError()) + return ec; + + Reference::KindValue op = ref.kindValue(); + + // FIXME (simon): Handle r_ssym value. + for (auto tag = (ref.tag() & 0xffff); tag & 0xff; tag >>= 8) { + op = tag & 0xff; + res = calculateRelocation(op, *res, 0, relAddr, gpAddr, isGpDisp, jumpMode); + if (auto ec = res.getError()) + return ec; + } + + auto params = getRelocationParams(op); + uint64_t ins = relocRead<ELFT>(params, location); + + if (auto ec = adjustJumpOpCode(ins, tgtAddr, jumpMode)) + return ec; + + ins = (ins & ~params._mask) | (*res & params._mask); + relocWrite<ELFT>(ins, params, location); + + return std::error_code(); +} + +template <class ELFT> +Reference::Addend +RelocationHandler<ELFT>::readAddend(Reference::KindValue kind, + const uint8_t *content) const { + auto params = getRelocationParams(kind); + uint64_t ins = relocRead<ELFT>(params, content); + return (ins & params._mask) << params._shift; +} + +namespace lld { +namespace elf { + +template <> +std::unique_ptr<TargetRelocationHandler> +createMipsRelocationHandler<Mips32ELType>(MipsLinkingContext &ctx) { + return std::unique_ptr<TargetRelocationHandler>( + new RelocationHandler<Mips32ELType>(ctx)); +} + +template <> +std::unique_ptr<TargetRelocationHandler> +createMipsRelocationHandler<Mips64ELType>(MipsLinkingContext &ctx) { + return std::unique_ptr<TargetRelocationHandler>( + new RelocationHandler<Mips64ELType>(ctx)); +} + +} // elf +} // lld diff --git a/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.h b/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.h new file mode 100644 index 0000000000000..87066b2b5c101 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.h @@ -0,0 +1,31 @@ +//===- lld/ReaderWriter/ELF/Mips/MipsRelocationHandler.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_MIPS_MIPS_RELOCATION_HANDLER_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_RELOCATION_HANDLER_H + +#include "TargetHandler.h" +#include "lld/Core/Reference.h" + +namespace lld { +namespace elf { + +class MipsRelocationHandler : public TargetRelocationHandler { +public: + virtual Reference::Addend readAddend(Reference::KindValue kind, + const uint8_t *content) const = 0; +}; + +template <class ELFT> +std::unique_ptr<TargetRelocationHandler> +createMipsRelocationHandler(MipsLinkingContext &ctx); + +} // elf +} // lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.cpp b/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.cpp new file mode 100644 index 0000000000000..a1b3530dfcdf6 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.cpp @@ -0,0 +1,1070 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsRelocationPass.cpp -------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsELFFile.h" +#include "MipsLinkingContext.h" +#include "MipsRelocationPass.h" +#include "MipsTargetHandler.h" +#include "llvm/ADT/DenseSet.h" + +using namespace lld; +using namespace lld::elf; +using namespace llvm::ELF; + +// Lazy resolver +static const uint8_t mipsGot0AtomContent[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +// Module pointer +static const uint8_t mipsGotModulePointerAtomContent[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80 +}; + +// TLS GD Entry +static const uint8_t mipsGotTlsGdAtomContent[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +// Regular PLT0 entry +static const uint8_t mipsPlt0AtomContent[] = { + 0x00, 0x00, 0x1c, 0x3c, // lui $28, %hi(&GOTPLT[0]) + 0x00, 0x00, 0x99, 0x8f, // lw $25, %lo(&GOTPLT[0])($28) + 0x00, 0x00, 0x9c, 0x27, // addiu $28, $28, %lo(&GOTPLT[0]) + 0x23, 0xc0, 0x1c, 0x03, // subu $24, $24, $28 + 0x21, 0x78, 0xe0, 0x03, // move $15, $31 + 0x82, 0xc0, 0x18, 0x00, // srl $24, $24, 2 + 0x09, 0xf8, 0x20, 0x03, // jalr $25 + 0xfe, 0xff, 0x18, 0x27 // subu $24, $24, 2 +}; + +// microMIPS PLT0 entry +static const uint8_t micromipsPlt0AtomContent[] = { + 0x80, 0x79, 0x00, 0x00, // addiupc $3, (&GOTPLT[0]) - . + 0x23, 0xff, 0x00, 0x00, // lw $25, 0($3) + 0x35, 0x05, // subu $2, $2, $3 + 0x25, 0x25, // srl $2, $2, 2 + 0x02, 0x33, 0xfe, 0xff, // subu $24, $2, 2 + 0xff, 0x0d, // move $15, $31 + 0xf9, 0x45, // jalrs $25 + 0x83, 0x0f, // move $28, $3 + 0x00, 0x0c // nop +}; + +// Regular PLT entry +static const uint8_t mipsPltAAtomContent[] = { + 0x00, 0x00, 0x0f, 0x3c, // lui $15, %hi(.got.plt entry) + 0x00, 0x00, 0xf9, 0x8d, // l[wd] $25, %lo(.got.plt entry)($15) + 0x08, 0x00, 0x20, 0x03, // jr $25 + 0x00, 0x00, 0xf8, 0x25 // addiu $24, $15, %lo(.got.plt entry) +}; + +// microMIPS PLT entry +static const uint8_t micromipsPltAtomContent[] = { + 0x00, 0x79, 0x00, 0x00, // addiupc $2, (.got.plt entry) - . + 0x22, 0xff, 0x00, 0x00, // lw $25, 0($2) + 0x99, 0x45, // jr $25 + 0x02, 0x0f // move $24, $2 +}; + +// R6 PLT entry +static const uint8_t mipsR6PltAAtomContent[] = { + 0x00, 0x00, 0x0f, 0x3c, // lui $15, %hi(.got.plt entry) + 0x00, 0x00, 0xf9, 0x8d, // l[wd] $25, %lo(.got.plt entry)($15) + 0x09, 0x00, 0x20, 0x03, // jr $25 + 0x00, 0x00, 0xf8, 0x25 // addiu $24, $15, %lo(.got.plt entry) +}; + +// LA25 stub entry +static const uint8_t mipsLA25AtomContent[] = { + 0x00, 0x00, 0x19, 0x3c, // lui $25, %hi(func) + 0x00, 0x00, 0x00, 0x08, // j func + 0x00, 0x00, 0x39, 0x27, // addiu $25, $25, %lo(func) + 0x00, 0x00, 0x00, 0x00 // nop +}; + +// microMIPS LA25 stub entry +static const uint8_t micromipsLA25AtomContent[] = { + 0xb9, 0x41, 0x00, 0x00, // lui $25, %hi(func) + 0x00, 0xd4, 0x00, 0x00, // j func + 0x39, 0x33, 0x00, 0x00, // addiu $25, $25, %lo(func) + 0x00, 0x00, 0x00, 0x00 // nop +}; + +namespace { + +/// \brief Abstract base class represent MIPS GOT entries. +class MipsGOTAtom : public GOTAtom { +public: + MipsGOTAtom(const File &f) : GOTAtom(f, ".got") {} + + Alignment alignment() const override { return Alignment(2); } +}; + +/// \brief MIPS GOT entry initialized by zero. +template <typename ELFT> class GOT0Atom : public MipsGOTAtom { +public: + GOT0Atom(const File &f) : MipsGOTAtom(f) {} + + ArrayRef<uint8_t> rawContent() const override; +}; + +template <> ArrayRef<uint8_t> GOT0Atom<Mips32ELType>::rawContent() const { + return llvm::makeArrayRef(mipsGot0AtomContent).slice(4); +} +template <> ArrayRef<uint8_t> GOT0Atom<Mips64ELType>::rawContent() const { + return llvm::makeArrayRef(mipsGot0AtomContent); +} + +/// \brief MIPS GOT entry initialized by zero. +template <typename ELFT> class GOTModulePointerAtom : public MipsGOTAtom { +public: + GOTModulePointerAtom(const File &f) : MipsGOTAtom(f) {} + + ArrayRef<uint8_t> rawContent() const override; +}; + +template <> +ArrayRef<uint8_t> GOTModulePointerAtom<Mips32ELType>::rawContent() const { + return llvm::makeArrayRef(mipsGotModulePointerAtomContent).slice(4); +} +template <> +ArrayRef<uint8_t> GOTModulePointerAtom<Mips64ELType>::rawContent() const { + return llvm::makeArrayRef(mipsGotModulePointerAtomContent); +} + +/// \brief MIPS GOT TLS GD entry. +template <typename ELFT> class GOTTLSGdAtom : public MipsGOTAtom { +public: + GOTTLSGdAtom(const File &f) : MipsGOTAtom(f) {} + + ArrayRef<uint8_t> rawContent() const override; +}; + +template <> ArrayRef<uint8_t> GOTTLSGdAtom<Mips32ELType>::rawContent() const { + return llvm::makeArrayRef(mipsGotTlsGdAtomContent).slice(8); +} + +template <> ArrayRef<uint8_t> GOTTLSGdAtom<Mips64ELType>::rawContent() const { + return llvm::makeArrayRef(mipsGotTlsGdAtomContent); +} + +class GOTPLTAtom : public GOTAtom { +public: + GOTPLTAtom(const File &f) : GOTAtom(f, ".got.plt") {} + GOTPLTAtom(const Atom *a, const File &f) : GOTAtom(f, ".got.plt") { + // Create dynamic relocation to adjust the .got.plt entry at runtime. + addReferenceELF_Mips(R_MIPS_JUMP_SLOT, 0, a, 0); + } + + /// Setup reference to assign initial value to the .got.plt entry. + void setPLT0(const PLTAtom *plt0) { + addReferenceELF_Mips(R_MIPS_32, 0, plt0, 0); + } + + Alignment alignment() const override { return Alignment(2); } + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(mipsGot0AtomContent).slice(4); + } +}; + +class PLT0Atom : public PLTAtom { +public: + PLT0Atom(const Atom *got, const File &f) : PLTAtom(f, ".plt") { + // Setup reference to fixup the PLT0 entry. + addReferenceELF_Mips(LLD_R_MIPS_HI16, 0, got, 0); + addReferenceELF_Mips(LLD_R_MIPS_LO16, 4, got, 0); + addReferenceELF_Mips(LLD_R_MIPS_LO16, 8, got, 0); + } + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(mipsPlt0AtomContent); + } +}; + +class PLT0MicroAtom : public PLTAtom { +public: + PLT0MicroAtom(const Atom *got, const File &f) : PLTAtom(f, ".plt") { + // Setup reference to fixup the PLT0 entry. + addReferenceELF_Mips(R_MICROMIPS_PC23_S2, 0, got, 0); + } + + CodeModel codeModel() const override { return codeMipsMicro; } + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(micromipsPlt0AtomContent); + } +}; + +class PLTAAtom : public PLTAtom { +public: + PLTAAtom(const GOTPLTAtom *got, const File &f) : PLTAtom(f, ".plt") { + // Setup reference to fixup the PLT entry. + addReferenceELF_Mips(LLD_R_MIPS_HI16, 0, got, 0); + addReferenceELF_Mips(LLD_R_MIPS_LO16, 4, got, 0); + addReferenceELF_Mips(LLD_R_MIPS_LO16, 12, got, 0); + } + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(mipsPltAAtomContent); + } +}; + +class PLTR6Atom : public PLTAAtom { +public: + PLTR6Atom(const GOTPLTAtom *got, const File &f) : PLTAAtom(got, f) {} + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(mipsR6PltAAtomContent); + } +}; + +class PLTMicroAtom : public PLTAtom { +public: + PLTMicroAtom(const GOTPLTAtom *got, const File &f) : PLTAtom(f, ".plt") { + // Setup reference to fixup the microMIPS PLT entry. + addReferenceELF_Mips(R_MICROMIPS_PC23_S2, 0, got, 0); + } + + Alignment alignment() const override { return Alignment(1); } + CodeModel codeModel() const override { return codeMipsMicro; } + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(micromipsPltAtomContent); + } +}; + +class LA25Atom : public PLTAtom { +public: + LA25Atom(const File &f) : PLTAtom(f, ".text") {} +}; + +class LA25RegAtom : public LA25Atom { +public: + LA25RegAtom(const Atom *a, const File &f) : LA25Atom(f) { + // Setup reference to fixup the LA25 stub entry. + addReferenceELF_Mips(R_MIPS_HI16, 0, a, 0); + addReferenceELF_Mips(R_MIPS_26, 4, a, 0); + addReferenceELF_Mips(R_MIPS_LO16, 8, a, 0); + } + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(mipsLA25AtomContent); + } +}; + +class LA25MicroAtom : public LA25Atom { +public: + LA25MicroAtom(const Atom *a, const File &f) : LA25Atom(f) { + // Setup reference to fixup the microMIPS LA25 stub entry. + addReferenceELF_Mips(R_MICROMIPS_HI16, 0, a, 0); + addReferenceELF_Mips(R_MICROMIPS_26_S1, 4, a, 0); + addReferenceELF_Mips(R_MICROMIPS_LO16, 8, a, 0); + } + + CodeModel codeModel() const override { return codeMipsMicro; } + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(micromipsLA25AtomContent); + } +}; + +class RelocationPassFile : public SimpleFile { +public: + RelocationPassFile(const ELFLinkingContext &ctx) + : SimpleFile("RelocationPassFile") { + setOrdinal(ctx.getNextOrdinalAndIncrement()); + } + + llvm::BumpPtrAllocator _alloc; +}; + +template <typename ELFT> class RelocationPass : public Pass { +public: + RelocationPass(MipsLinkingContext &ctx); + + void perform(std::unique_ptr<MutableFile> &mf) override; + +private: + /// \brief Reference to the linking context. + const MipsLinkingContext &_ctx; + + /// \brief Owner of all the Atoms created by this pass. + RelocationPassFile _file; + + /// \brief Map Atoms and addend to local GOT entries. + typedef std::pair<const Atom *, int64_t> LocalGotMapKeyT; + llvm::DenseMap<LocalGotMapKeyT, GOTAtom *> _gotLocalMap; + llvm::DenseMap<LocalGotMapKeyT, GOTAtom *> _gotLocalPageMap; + + /// \brief Map Atoms to global GOT entries. + llvm::DenseMap<const Atom *, GOTAtom *> _gotGlobalMap; + + /// \brief Map Atoms to TLS GOT entries. + llvm::DenseMap<const Atom *, GOTAtom *> _gotTLSMap; + + /// \brief Map Atoms to TLS GD GOT entries. + llvm::DenseMap<const Atom *, GOTAtom *> _gotTLSGdMap; + + /// \brief GOT entry for the R_xxxMIPS_TLS_LDM relocations. + GOTTLSGdAtom<ELFT> *_gotLDMEntry; + + /// \brief the list of local GOT atoms. + std::vector<GOTAtom *> _localGotVector; + + /// \brief the list of global GOT atoms. + std::vector<GOTAtom *> _globalGotVector; + + /// \brief the list of TLS GOT atoms. + std::vector<GOTAtom *> _tlsGotVector; + + /// \brief Map Atoms to their GOTPLT entries. + llvm::DenseMap<const Atom *, GOTPLTAtom *> _gotpltMap; + + /// \brief Map Atoms to their PLT entries. + llvm::DenseMap<const Atom *, PLTAAtom *> _pltRegMap; + llvm::DenseMap<const Atom *, PLTMicroAtom *> _pltMicroMap; + + /// \brief Map Atoms to their Object entries. + llvm::DenseMap<const Atom *, ObjectAtom *> _objectMap; + + /// \brief Map Atoms to their LA25 entries. + llvm::DenseMap<const Atom *, LA25RegAtom *> _la25RegMap; + llvm::DenseMap<const Atom *, LA25MicroAtom *> _la25MicroMap; + + /// \brief Atoms referenced by static relocations. + llvm::DenseSet<const Atom *> _hasStaticRelocations; + + /// \brief Atoms require pointers equality. + llvm::DenseSet<const Atom *> _requiresPtrEquality; + + /// \brief References which are candidates for converting + /// to the R_MIPS_REL32 relocation. + std::vector<Reference *> _rel32Candidates; + + /// \brief the list of PLT atoms. + std::vector<PLTAtom *> _pltRegVector; + std::vector<PLTAtom *> _pltMicroVector; + + /// \brief the list of GOTPLT atoms. + std::vector<GOTPLTAtom *> _gotpltVector; + + /// \brief the list of Object entries. + std::vector<ObjectAtom *> _objectVector; + + /// \brief the list of LA25 entries. + std::vector<LA25Atom *> _la25Vector; + + /// \brief Handle a specific reference. + void handleReference(const MipsELFDefinedAtom<ELFT> &atom, Reference &ref); + + /// \brief Collect information about the reference to use it + /// later in the handleReference() routine. + void collectReferenceInfo(const MipsELFDefinedAtom<ELFT> &atom, + Reference &ref); + + void handlePlain(const MipsELFDefinedAtom<ELFT> &atom, Reference &ref); + void handle26(const MipsELFDefinedAtom<ELFT> &atom, Reference &ref); + void handleGOT(Reference &ref); + + const GOTAtom *getLocalGOTEntry(const Reference &ref); + const GOTAtom *getLocalGOTPageEntry(const Reference &ref); + const GOTAtom *getGlobalGOTEntry(const Atom *a); + const GOTAtom *getTLSGOTEntry(const Atom *a); + const GOTAtom *getTLSGdGOTEntry(const Atom *a); + const GOTAtom *getTLSLdmGOTEntry(const Atom *a); + const GOTPLTAtom *getGOTPLTEntry(const Atom *a); + const PLTAtom *getPLTEntry(const Atom *a); + const PLTAtom *getPLTRegEntry(const Atom *a); + const PLTAtom *getPLTMicroEntry(const Atom *a); + const LA25Atom *getLA25Entry(const Atom *target, bool isMicroMips); + const LA25Atom *getLA25RegEntry(const Atom *a); + const LA25Atom *getLA25MicroEntry(const Atom *a); + const ObjectAtom *getObjectEntry(const SharedLibraryAtom *a); + + PLTAtom *createPLTHeader(bool isMicroMips); + + bool isLocal(const Atom *a) const; + bool isLocalCall(const Atom *a) const; + bool isDynamic(const Atom *atom) const; + bool requireLA25Stub(const Atom *a) const; + bool requirePLTEntry(const Atom *a) const; + bool requireCopy(const Atom *a) const; + bool mightBeDynamic(const MipsELFDefinedAtom<ELFT> &atom, + Reference::KindValue refKind) const; + bool hasPLTEntry(const Atom *atom) const; + + bool isR6Target() const; +}; + +template <typename ELFT> +RelocationPass<ELFT>::RelocationPass(MipsLinkingContext &ctx) + : _ctx(ctx), _file(ctx), _gotLDMEntry(nullptr) { + _localGotVector.push_back(new (_file._alloc) GOT0Atom<ELFT>(_file)); + _localGotVector.push_back(new (_file._alloc) + GOTModulePointerAtom<ELFT>(_file)); +} + +template <typename ELFT> +void RelocationPass<ELFT>::perform(std::unique_ptr<MutableFile> &mf) { + for (const auto &atom : mf->defined()) + for (const auto &ref : *atom) + collectReferenceInfo(*cast<MipsELFDefinedAtom<ELFT>>(atom), + const_cast<Reference &>(*ref)); + + // Process all references. + for (const auto &atom : mf->defined()) + for (const auto &ref : *atom) + handleReference(*cast<MipsELFDefinedAtom<ELFT>>(atom), + const_cast<Reference &>(*ref)); + + // Create R_MIPS_REL32 relocations. + for (auto *ref : _rel32Candidates) { + if (!isDynamic(ref->target()) || hasPLTEntry(ref->target())) + continue; + ref->setKindValue(R_MIPS_REL32); + if (ELFT::Is64Bits) + static_cast<MipsELFReference<ELFT> *>(ref)->setTag(R_MIPS_64); + if (!isLocalCall(ref->target())) + getGlobalGOTEntry(ref->target()); + } + + uint64_t ordinal = 0; + + for (auto &got : _localGotVector) { + got->setOrdinal(ordinal++); + mf->addAtom(*got); + } + + for (auto &got : _globalGotVector) { + got->setOrdinal(ordinal++); + mf->addAtom(*got); + } + + for (auto &got : _tlsGotVector) { + got->setOrdinal(ordinal++); + mf->addAtom(*got); + } + + // Create and emit PLT0 entry. + PLTAtom *plt0Atom = nullptr; + if (!_pltRegVector.empty()) + plt0Atom = createPLTHeader(false); + else if (!_pltMicroVector.empty()) + plt0Atom = createPLTHeader(true); + + if (plt0Atom) { + plt0Atom->setOrdinal(ordinal++); + mf->addAtom(*plt0Atom); + } + + // Emit regular PLT entries firts. + for (auto &plt : _pltRegVector) { + plt->setOrdinal(ordinal++); + mf->addAtom(*plt); + } + + // microMIPS PLT entries come after regular ones. + for (auto &plt : _pltMicroVector) { + plt->setOrdinal(ordinal++); + mf->addAtom(*plt); + } + + // Assign PLT0 to GOTPLT entries. + assert(_gotpltMap.empty() || plt0Atom); + for (auto &a: _gotpltMap) + a.second->setPLT0(plt0Atom); + + for (auto &gotplt : _gotpltVector) { + gotplt->setOrdinal(ordinal++); + mf->addAtom(*gotplt); + } + + for (auto obj : _objectVector) { + obj->setOrdinal(ordinal++); + mf->addAtom(*obj); + } + + for (auto la25 : _la25Vector) { + la25->setOrdinal(ordinal++); + mf->addAtom(*la25); + } +} + +template <typename ELFT> +void RelocationPass<ELFT>::handleReference(const MipsELFDefinedAtom<ELFT> &atom, + Reference &ref) { + if (!ref.target()) + return; + if (ref.kindNamespace() != lld::Reference::KindNamespace::ELF) + return; + assert(ref.kindArch() == Reference::KindArch::Mips); + switch (ref.kindValue()) { + case R_MIPS_32: + case R_MIPS_PC32: + case R_MIPS_HI16: + case R_MIPS_LO16: + case R_MIPS_PCHI16: + case R_MIPS_PCLO16: + case R_MICROMIPS_HI16: + case R_MICROMIPS_LO16: + // FIXME (simon): Handle dynamic/static linking differently. + handlePlain(atom, ref); + break; + case R_MIPS_26: + case R_MICROMIPS_26_S1: + handle26(atom, ref); + break; + case R_MIPS_GOT16: + case R_MIPS_CALL16: + case R_MICROMIPS_GOT16: + case R_MICROMIPS_CALL16: + case R_MIPS_GOT_DISP: + case R_MIPS_GOT_PAGE: + handleGOT(ref); + break; + case R_MIPS_GOT_OFST: + // Nothing to do. We create GOT page entry in the R_MIPS_GOT_PAGE handler. + break; + case R_MIPS_GPREL16: + if (isLocal(ref.target())) + ref.setAddend(ref.addend() + atom.file().getGP0()); + break; + case R_MIPS_GPREL32: + ref.setAddend(ref.addend() + atom.file().getGP0()); + break; + case R_MIPS_TLS_DTPREL_HI16: + case R_MIPS_TLS_DTPREL_LO16: + case R_MICROMIPS_TLS_DTPREL_HI16: + case R_MICROMIPS_TLS_DTPREL_LO16: + ref.setAddend(ref.addend() - atom.file().getDTPOffset()); + break; + case R_MIPS_TLS_TPREL_HI16: + case R_MIPS_TLS_TPREL_LO16: + case R_MICROMIPS_TLS_TPREL_HI16: + case R_MICROMIPS_TLS_TPREL_LO16: + ref.setAddend(ref.addend() - atom.file().getTPOffset()); + break; + case R_MIPS_TLS_GD: + case R_MICROMIPS_TLS_GD: + ref.setTarget(getTLSGdGOTEntry(ref.target())); + break; + case R_MIPS_TLS_LDM: + case R_MICROMIPS_TLS_LDM: + ref.setTarget(getTLSLdmGOTEntry(ref.target())); + break; + case R_MIPS_TLS_GOTTPREL: + case R_MICROMIPS_TLS_GOTTPREL: + ref.setTarget(getTLSGOTEntry(ref.target())); + break; + } +} + +template <typename ELFT> +static bool isConstrainSym(const MipsELFDefinedAtom<ELFT> &atom, + Reference::KindValue refKind) { + if ((atom.section()->sh_flags & SHF_ALLOC) == 0) + return false; + switch (refKind) { + case R_MIPS_NONE: + case R_MIPS_JALR: + case R_MICROMIPS_JALR: + case R_MIPS_GPREL16: + case R_MIPS_GPREL32: + return false; + default: + return true; + } +} + +template <typename ELFT> +void RelocationPass<ELFT>::collectReferenceInfo( + const MipsELFDefinedAtom<ELFT> &atom, Reference &ref) { + if (!ref.target()) + return; + if (ref.kindNamespace() != lld::Reference::KindNamespace::ELF) + return; + + auto refKind = ref.kindValue(); + if (!isConstrainSym(atom, refKind)) + return; + + if (mightBeDynamic(atom, refKind)) + _rel32Candidates.push_back(&ref); + else + _hasStaticRelocations.insert(ref.target()); + + if (refKind != R_MIPS_CALL16 && refKind != R_MICROMIPS_CALL16 && + refKind != R_MIPS_26 && refKind != R_MICROMIPS_26_S1) + _requiresPtrEquality.insert(ref.target()); +} + +template <typename ELFT> +bool RelocationPass<ELFT>::isLocal(const Atom *a) const { + if (auto *da = dyn_cast<DefinedAtom>(a)) + return da->scope() == Atom::scopeTranslationUnit; + return false; +} + +template <typename ELFT> +static bool isMipsReadonly(const MipsELFDefinedAtom<ELFT> &atom) { + auto secFlags = atom.section()->sh_flags; + auto secType = atom.section()->sh_type; + + if ((secFlags & SHF_ALLOC) == 0) + return false; + if (secType == SHT_NOBITS) + return false; + if ((secFlags & SHF_WRITE) != 0) + return false; + return true; +} + +template <typename ELFT> +bool RelocationPass<ELFT>::mightBeDynamic(const MipsELFDefinedAtom<ELFT> &atom, + Reference::KindValue refKind) const { + if (refKind == R_MIPS_CALL16 || refKind == R_MIPS_GOT16 || + refKind == R_MICROMIPS_CALL16 || refKind == R_MICROMIPS_GOT16) + return true; + + if (refKind != R_MIPS_32 && refKind != R_MIPS_64) + return false; + if ((atom.section()->sh_flags & SHF_ALLOC) == 0) + return false; + + if (_ctx.getOutputELFType() == ET_DYN) + return true; + if (!isMipsReadonly(atom)) + return true; + if (atom.file().isPIC()) + return true; + + return false; +} + +template <typename ELFT> +bool RelocationPass<ELFT>::hasPLTEntry(const Atom *atom) const { + return _pltRegMap.count(atom) || _pltMicroMap.count(atom); +} + +template <typename ELFT> bool RelocationPass<ELFT>::isR6Target() const { + switch (_ctx.getMergedELFFlags() & EF_MIPS_ARCH) { + case EF_MIPS_ARCH_32R6: + case EF_MIPS_ARCH_64R6: + return true; + default: + return false; + } +} + +template <typename ELFT> +bool RelocationPass<ELFT>::requirePLTEntry(const Atom *a) const { + if (!_hasStaticRelocations.count(a)) + return false; + const auto *sa = dyn_cast<ELFDynamicAtom<ELFT>>(a); + if (sa && sa->type() != SharedLibraryAtom::Type::Code) + return false; + const auto *da = dyn_cast<ELFDefinedAtom<ELFT>>(a); + if (da && da->contentType() != DefinedAtom::typeCode) + return false; + if (isLocalCall(a)) + return false; + return true; +} + +template <typename ELFT> +bool RelocationPass<ELFT>::requireCopy(const Atom *a) const { + if (!_hasStaticRelocations.count(a)) + return false; + const auto *sa = dyn_cast<ELFDynamicAtom<ELFT>>(a); + return sa && sa->type() == SharedLibraryAtom::Type::Data; +} + +template <typename ELFT> +bool RelocationPass<ELFT>::isDynamic(const Atom *atom) const { + const auto *da = dyn_cast<const DefinedAtom>(atom); + if (da && da->dynamicExport() == DefinedAtom::dynamicExportAlways) + return true; + + const auto *sa = dyn_cast<SharedLibraryAtom>(atom); + if (sa) + return true; + + if (_ctx.getOutputELFType() == ET_DYN) { + if (da && da->scope() != DefinedAtom::scopeTranslationUnit) + return true; + + const auto *ua = dyn_cast<UndefinedAtom>(atom); + if (ua) + return true; + } + + return false; +} + +template <typename ELFT> +static bool isMicroMips(const MipsELFDefinedAtom<ELFT> &atom) { + return atom.codeModel() == DefinedAtom::codeMipsMicro || + atom.codeModel() == DefinedAtom::codeMipsMicroPIC; +} + +template <typename ELFT> +const LA25Atom *RelocationPass<ELFT>::getLA25Entry(const Atom *target, + bool isMicroMips) { + return isMicroMips ? getLA25MicroEntry(target) : getLA25RegEntry(target); +} + +template <typename ELFT> +const PLTAtom *RelocationPass<ELFT>::getPLTEntry(const Atom *a) { + bool hasMicroCode = _ctx.getMergedELFFlags() & EF_MIPS_MICROMIPS; + + // If file contains microMIPS code try to reuse compressed PLT entry... + if (hasMicroCode) { + auto microPLT = _pltMicroMap.find(a); + if (microPLT != _pltMicroMap.end()) + return microPLT->second; + } + + // ... then try to reuse a regular PLT entry ... + auto regPLT = _pltRegMap.find(a); + if (regPLT != _pltRegMap.end()) + return regPLT->second; + + // ... and finally prefer to create new compressed PLT entry. + return hasMicroCode ? getPLTMicroEntry(a) : getPLTRegEntry(a); +} + +template <typename ELFT> +void RelocationPass<ELFT>::handlePlain(const MipsELFDefinedAtom<ELFT> &atom, + Reference &ref) { + if (!isDynamic(ref.target())) + return; + + if (requirePLTEntry(ref.target())) + ref.setTarget(getPLTEntry(ref.target())); + else if (requireCopy(ref.target())) + ref.setTarget(getObjectEntry(cast<SharedLibraryAtom>(ref.target()))); +} + +template <typename ELFT> +void RelocationPass<ELFT>::handle26(const MipsELFDefinedAtom<ELFT> &atom, + Reference &ref) { + bool isMicro = ref.kindValue() == R_MICROMIPS_26_S1; + assert((isMicro || ref.kindValue() == R_MIPS_26) && "Unexpected relocation"); + + const auto *sla = dyn_cast<SharedLibraryAtom>(ref.target()); + if (sla && sla->type() == SharedLibraryAtom::Type::Code) + ref.setTarget(isMicro ? getPLTMicroEntry(sla) : getPLTRegEntry(sla)); + + if (requireLA25Stub(ref.target())) + ref.setTarget(getLA25Entry(ref.target(), isMicro)); + + if (!isLocal(ref.target())) { + if (isMicro) + ref.setKindValue(LLD_R_MICROMIPS_GLOBAL_26_S1); + else + ref.setKindValue(LLD_R_MIPS_GLOBAL_26); + } +} + +template <typename ELFT> void RelocationPass<ELFT>::handleGOT(Reference &ref) { + if (!isLocalCall(ref.target())) { + ref.setTarget(getGlobalGOTEntry(ref.target())); + return; + } + + if (ref.kindValue() == R_MIPS_GOT_PAGE) + ref.setTarget(getLocalGOTPageEntry(ref)); + else if (ref.kindValue() == R_MIPS_GOT_DISP) + ref.setTarget(getLocalGOTEntry(ref)); + else if (isLocal(ref.target())) + ref.setTarget(getLocalGOTPageEntry(ref)); + else + ref.setTarget(getLocalGOTEntry(ref)); +} + +template <typename ELFT> +bool RelocationPass<ELFT>::isLocalCall(const Atom *a) const { + Atom::Scope scope; + if (auto *da = dyn_cast<DefinedAtom>(a)) + scope = da->scope(); + else if (auto *aa = dyn_cast<AbsoluteAtom>(a)) + scope = aa->scope(); + else + return false; + + // Local and hidden symbols must be local. + if (scope == Atom::scopeTranslationUnit || scope == Atom::scopeLinkageUnit) + return true; + + // Calls to external symbols defined in an executable file resolved locally. + if (_ctx.getOutputELFType() == ET_EXEC) + return true; + + return false; +} + +template <typename ELFT> +bool RelocationPass<ELFT>::requireLA25Stub(const Atom *a) const { + if (isLocal(a)) + return false; + if (auto *da = dyn_cast<DefinedAtom>(a)) + return static_cast<const MipsELFDefinedAtom<ELFT> *>(da)->file().isPIC(); + return false; +} + +template <typename ELFT> +const GOTAtom *RelocationPass<ELFT>::getLocalGOTEntry(const Reference &ref) { + const Atom *a = ref.target(); + LocalGotMapKeyT key(a, ref.addend()); + + auto got = _gotLocalMap.find(key); + if (got != _gotLocalMap.end()) + return got->second; + + auto ga = new (_file._alloc) GOT0Atom<ELFT>(_file); + _gotLocalMap[key] = ga; + + _localGotVector.push_back(ga); + + Reference::KindValue relKind = ELFT::Is64Bits ? R_MIPS_64 : R_MIPS_32; + ga->addReferenceELF_Mips(relKind, 0, a, 0); + + return ga; +} + +template <typename ELFT> +const GOTAtom * +RelocationPass<ELFT>::getLocalGOTPageEntry(const Reference &ref) { + const Atom *a = ref.target(); + LocalGotMapKeyT key(a, ref.addend()); + + auto got = _gotLocalPageMap.find(key); + if (got != _gotLocalPageMap.end()) + return got->second; + + auto ga = new (_file._alloc) GOT0Atom<ELFT>(_file); + _gotLocalPageMap[key] = ga; + + _localGotVector.push_back(ga); + + Reference::KindValue relKind = + ELFT::Is64Bits ? LLD_R_MIPS_64_HI16 : LLD_R_MIPS_32_HI16; + ga->addReferenceELF_Mips(relKind, 0, a, ref.addend()); + + return ga; +} + +template <typename ELFT> +const GOTAtom *RelocationPass<ELFT>::getGlobalGOTEntry(const Atom *a) { + auto got = _gotGlobalMap.find(a); + if (got != _gotGlobalMap.end()) + return got->second; + + auto ga = new (_file._alloc) GOT0Atom<ELFT>(_file); + _gotGlobalMap[a] = ga; + + _globalGotVector.push_back(ga); + ga->addReferenceELF_Mips(LLD_R_MIPS_GLOBAL_GOT, 0, a, 0); + + if (const DefinedAtom *da = dyn_cast<DefinedAtom>(a)) + ga->addReferenceELF_Mips(R_MIPS_32, 0, da, 0); + + return ga; +} + +template <typename ELFT> +const GOTAtom *RelocationPass<ELFT>::getTLSGOTEntry(const Atom *a) { + auto got = _gotTLSMap.find(a); + if (got != _gotTLSMap.end()) + return got->second; + + auto ga = new (_file._alloc) GOT0Atom<ELFT>(_file); + _gotTLSMap[a] = ga; + + _tlsGotVector.push_back(ga); + Reference::KindValue relKind = + ELFT::Is64Bits ? R_MIPS_TLS_TPREL64 : R_MIPS_TLS_TPREL32; + ga->addReferenceELF_Mips(relKind, 0, a, 0); + + return ga; +} + +template <typename ELFT> +const GOTAtom *RelocationPass<ELFT>::getTLSGdGOTEntry(const Atom *a) { + auto got = _gotTLSGdMap.find(a); + if (got != _gotTLSGdMap.end()) + return got->second; + + auto ga = new (_file._alloc) GOTTLSGdAtom<ELFT>(_file); + _gotTLSGdMap[a] = ga; + + _tlsGotVector.push_back(ga); + if (ELFT::Is64Bits) { + ga->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD64, 0, a, 0); + ga->addReferenceELF_Mips(R_MIPS_TLS_DTPREL64, 8, a, 0); + } else { + ga->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD32, 0, a, 0); + ga->addReferenceELF_Mips(R_MIPS_TLS_DTPREL32, 4, a, 0); + } + + return ga; +} + +template <typename ELFT> +const GOTAtom *RelocationPass<ELFT>::getTLSLdmGOTEntry(const Atom *a) { + if (_gotLDMEntry) + return _gotLDMEntry; + + _gotLDMEntry = new (_file._alloc) GOTTLSGdAtom<ELFT>(_file); + _tlsGotVector.push_back(_gotLDMEntry); + if (ELFT::Is64Bits) + _gotLDMEntry->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD64, 0, _gotLDMEntry, 0); + else + _gotLDMEntry->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD32, 0, _gotLDMEntry, 0); + + return _gotLDMEntry; +} + +template <typename ELFT> +PLTAtom *RelocationPass<ELFT>::createPLTHeader(bool isMicroMips) { + auto ga1 = new (_file._alloc) GOTPLTAtom(_file); + _gotpltVector.insert(_gotpltVector.begin(), ga1); + auto ga0 = new (_file._alloc) GOTPLTAtom(_file); + _gotpltVector.insert(_gotpltVector.begin(), ga0); + + if (isMicroMips) + return new (_file._alloc) PLT0MicroAtom(ga0, _file); + else + return new (_file._alloc) PLT0Atom(ga0, _file); +} + +template <typename ELFT> +const GOTPLTAtom *RelocationPass<ELFT>::getGOTPLTEntry(const Atom *a) { + auto it = _gotpltMap.find(a); + if (it != _gotpltMap.end()) + return it->second; + + auto ga = new (_file._alloc) GOTPLTAtom(a, _file); + _gotpltMap[a] = ga; + _gotpltVector.push_back(ga); + return ga; +} + +template <typename ELFT> +const PLTAtom *RelocationPass<ELFT>::getPLTRegEntry(const Atom *a) { + auto plt = _pltRegMap.find(a); + if (plt != _pltRegMap.end()) + return plt->second; + + PLTAAtom *pa = isR6Target() + ? new (_file._alloc) PLTR6Atom(getGOTPLTEntry(a), _file) + : new (_file._alloc) PLTAAtom(getGOTPLTEntry(a), _file); + _pltRegMap[a] = pa; + _pltRegVector.push_back(pa); + + // Check that 'a' dynamic symbol table record should point to the PLT. + if (_hasStaticRelocations.count(a) && _requiresPtrEquality.count(a)) + pa->addReferenceELF_Mips(LLD_R_MIPS_STO_PLT, 0, a, 0); + + return pa; +} + +template <typename ELFT> +const PLTAtom *RelocationPass<ELFT>::getPLTMicroEntry(const Atom *a) { + auto plt = _pltMicroMap.find(a); + if (plt != _pltMicroMap.end()) + return plt->second; + + auto pa = new (_file._alloc) PLTMicroAtom(getGOTPLTEntry(a), _file); + _pltMicroMap[a] = pa; + _pltMicroVector.push_back(pa); + + // Check that 'a' dynamic symbol table record should point to the PLT. + if (_hasStaticRelocations.count(a) && _requiresPtrEquality.count(a)) + pa->addReferenceELF_Mips(LLD_R_MIPS_STO_PLT, 0, a, 0); + + return pa; +} + +template <typename ELFT> +const LA25Atom *RelocationPass<ELFT>::getLA25RegEntry(const Atom *a) { + auto la25 = _la25RegMap.find(a); + if (la25 != _la25RegMap.end()) + return la25->second; + + auto sa = new (_file._alloc) LA25RegAtom(a, _file); + _la25RegMap[a] = sa; + _la25Vector.push_back(sa); + + return sa; +} + +template <typename ELFT> +const LA25Atom *RelocationPass<ELFT>::getLA25MicroEntry(const Atom *a) { + auto la25 = _la25MicroMap.find(a); + if (la25 != _la25MicroMap.end()) + return la25->second; + + auto sa = new (_file._alloc) LA25MicroAtom(a, _file); + _la25MicroMap[a] = sa; + _la25Vector.push_back(sa); + + return sa; +} + +template <typename ELFT> +const ObjectAtom * +RelocationPass<ELFT>::getObjectEntry(const SharedLibraryAtom *a) { + auto obj = _objectMap.find(a); + if (obj != _objectMap.end()) + return obj->second; + + auto oa = new (_file._alloc) ObjectAtom(_file); + oa->addReferenceELF_Mips(R_MIPS_COPY, 0, oa, 0); + oa->_name = a->name(); + oa->_size = a->size(); + + _objectMap[a] = oa; + _objectVector.push_back(oa); + + return oa; +} + +} // end anon namespace + +static std::unique_ptr<Pass> createPass(MipsLinkingContext &ctx) { + switch (ctx.getTriple().getArch()) { + case llvm::Triple::mipsel: + return llvm::make_unique<RelocationPass<Mips32ELType>>(ctx); + case llvm::Triple::mips64el: + return llvm::make_unique<RelocationPass<Mips64ELType>>(ctx); + default: + llvm_unreachable("Unhandled arch"); + } +} + +std::unique_ptr<Pass> +lld::elf::createMipsRelocationPass(MipsLinkingContext &ctx) { + switch (ctx.getOutputELFType()) { + case ET_EXEC: + case ET_DYN: + return createPass(ctx); + case ET_REL: + return nullptr; + default: + llvm_unreachable("Unhandled output file type"); + } +} diff --git a/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.h b/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.h new file mode 100644 index 0000000000000..af343de5f0276 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.h @@ -0,0 +1,25 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsRelocationPass.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_MIPS_MIPS_RELOCATION_PASS_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_RELOCATION_PASS_H + +#include <memory> + +namespace lld { +class Pass; + +namespace elf { +class MipsLinkingContext; + +std::unique_ptr<Pass> createMipsRelocationPass(MipsLinkingContext &ctx); + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.h b/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.h new file mode 100644 index 0000000000000..de9390f2b3074 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.h @@ -0,0 +1,170 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsSectionChunks.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_MIPS_MIPS_SECTION_CHUNKS_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_SECTION_CHUNKS_H + +namespace lld { +namespace elf { + +template <typename ELFT> class MipsTargetLayout; +class MipsLinkingContext; + +/// \brief Handle Mips GOT section +template <class ELFType> class MipsGOTSection : public AtomSection<ELFType> { +public: + MipsGOTSection(const MipsLinkingContext &ctx) + : AtomSection<ELFType>(ctx, ".got", DefinedAtom::typeGOT, + DefinedAtom::permRW_, + MipsTargetLayout<ELFType>::ORDER_GOT), + _hasNonLocal(false), _localCount(0) { + this->_flags |= SHF_MIPS_GPREL; + this->_alignment = 4; + } + + /// \brief Number of local GOT entries. + std::size_t getLocalCount() const { return _localCount; } + + /// \brief Number of global GOT entries. + std::size_t getGlobalCount() const { return _posMap.size(); } + + /// \brief Does the atom have a global GOT entry? + bool hasGlobalGOTEntry(const Atom *a) const { + return _posMap.count(a) || _tlsMap.count(a); + } + + /// \brief Compare two atoms accordingly theirs positions in the GOT. + bool compare(const Atom *a, const Atom *b) const { + auto ia = _posMap.find(a); + auto ib = _posMap.find(b); + + if (ia != _posMap.end() && ib != _posMap.end()) + return ia->second < ib->second; + + return ia == _posMap.end() && ib != _posMap.end(); + } + + const lld::AtomLayout *appendAtom(const Atom *atom) override { + const DefinedAtom *da = dyn_cast<DefinedAtom>(atom); + + for (const auto &r : *da) { + if (r->kindNamespace() != lld::Reference::KindNamespace::ELF) + continue; + assert(r->kindArch() == Reference::KindArch::Mips); + switch (r->kindValue()) { + case LLD_R_MIPS_GLOBAL_GOT: + _hasNonLocal = true; + _posMap[r->target()] = _posMap.size(); + return AtomSection<ELFType>::appendAtom(atom); + case R_MIPS_TLS_TPREL32: + case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_TPREL64: + case R_MIPS_TLS_DTPREL64: + _hasNonLocal = true; + _tlsMap[r->target()] = _tlsMap.size(); + return AtomSection<ELFType>::appendAtom(atom); + case R_MIPS_TLS_DTPMOD32: + case R_MIPS_TLS_DTPMOD64: + _hasNonLocal = true; + break; + } + } + + if (!_hasNonLocal) + ++_localCount; + + return AtomSection<ELFType>::appendAtom(atom); + } + +private: + /// \brief True if the GOT contains non-local entries. + bool _hasNonLocal; + + /// \brief Number of local GOT entries. + std::size_t _localCount; + + /// \brief Map TLS Atoms to their GOT entry index. + llvm::DenseMap<const Atom *, std::size_t> _tlsMap; + + /// \brief Map Atoms to their GOT entry index. + llvm::DenseMap<const Atom *, std::size_t> _posMap; +}; + +/// \brief Handle Mips PLT section +template <class ELFType> class MipsPLTSection : public AtomSection<ELFType> { +public: + MipsPLTSection(const MipsLinkingContext &ctx) + : AtomSection<ELFType>(ctx, ".plt", DefinedAtom::typeGOT, + DefinedAtom::permR_X, + MipsTargetLayout<ELFType>::ORDER_PLT) {} + + const AtomLayout *findPLTLayout(const Atom *plt) const { + auto it = _pltLayoutMap.find(plt); + return it != _pltLayoutMap.end() ? it->second : nullptr; + } + + const lld::AtomLayout *appendAtom(const Atom *atom) override { + const auto *layout = AtomSection<ELFType>::appendAtom(atom); + + const DefinedAtom *da = cast<DefinedAtom>(atom); + + for (const auto &r : *da) { + if (r->kindNamespace() != lld::Reference::KindNamespace::ELF) + continue; + assert(r->kindArch() == Reference::KindArch::Mips); + if (r->kindValue() == LLD_R_MIPS_STO_PLT) { + _pltLayoutMap[r->target()] = layout; + break; + } + } + + return layout; + } + +private: + /// \brief Map PLT Atoms to their layouts. + std::unordered_map<const Atom *, const AtomLayout *> _pltLayoutMap; +}; + +template <class ELFT> class MipsRelocationTable : public RelocationTable<ELFT> { + typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel; + typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela; + + static const bool _isMips64EL = + ELFT::Is64Bits && ELFT::TargetEndianness == llvm::support::little; + +public: + MipsRelocationTable(const ELFLinkingContext &context, StringRef str, + int32_t order) + : RelocationTable<ELFT>(context, str, order) {} + +protected: + void writeRela(ELFWriter *writer, Elf_Rela &r, const DefinedAtom &atom, + const Reference &ref) override { + uint32_t rType = ref.kindValue() | (ref.tag() << 8); + r.setSymbolAndType(this->getSymbolIndex(ref.target()), rType, _isMips64EL); + r.r_offset = writer->addressOfAtom(&atom) + ref.offsetInAtom(); + // The addend is used only by relative relocations + if (this->_context.isRelativeReloc(ref)) + r.r_addend = writer->addressOfAtom(ref.target()) + ref.addend(); + else + r.r_addend = 0; + } + + void writeRel(ELFWriter *writer, Elf_Rel &r, const DefinedAtom &atom, + const Reference &ref) override { + uint32_t rType = ref.kindValue() | (ref.tag() << 8); + r.setSymbolAndType(this->getSymbolIndex(ref.target()), rType, _isMips64EL); + r.r_offset = writer->addressOfAtom(&atom) + ref.offsetInAtom(); + } +}; + +} // elf +} // lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.cpp b/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.cpp new file mode 100644 index 0000000000000..f60ab63c6af7f --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.cpp @@ -0,0 +1,35 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsTargetHandler.cpp --------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsTargetHandler.h" + +using namespace lld; +using namespace elf; + +void MipsRelocationStringTable::registerTable(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, + Reference::KindArch::Mips, kindStrings); +} + +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), + +const Registry::KindStrings MipsRelocationStringTable::kindStrings[] = { +#include "llvm/Support/ELFRelocs/Mips.def" + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_GLOBAL_GOT), + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_32_HI16), + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_64_HI16), + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_GLOBAL_26), + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_HI16), + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_LO16), + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_STO_PLT), + LLD_KIND_STRING_ENTRY(LLD_R_MICROMIPS_GLOBAL_26_S1), + LLD_KIND_STRING_END +}; + +#undef ELF_RELOC diff --git a/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h b/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h new file mode 100644 index 0000000000000..79509addf40b4 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h @@ -0,0 +1,257 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsTargetHandler.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_MIPS_MIPS_TARGET_HANDLER_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_TARGET_HANDLER_H + +#include "DefaultTargetHandler.h" +#include "MipsDynamicLibraryWriter.h" +#include "MipsELFReader.h" +#include "MipsExecutableWriter.h" +#include "MipsLinkingContext.h" +#include "MipsRelocationHandler.h" +#include "MipsSectionChunks.h" +#include "TargetLayout.h" +#include "llvm/ADT/DenseSet.h" + +namespace lld { +namespace elf { + +/// \brief TargetLayout for Mips +template <class ELFT> class MipsTargetLayout final : public TargetLayout<ELFT> { +public: + MipsTargetLayout(MipsLinkingContext &ctx) + : TargetLayout<ELFT>(ctx), + _gotSection(new (this->_allocator) MipsGOTSection<ELFT>(ctx)), + _pltSection(new (this->_allocator) MipsPLTSection<ELFT>(ctx)) {} + + const MipsGOTSection<ELFT> &getGOTSection() const { return *_gotSection; } + const MipsPLTSection<ELFT> &getPLTSection() const { return *_pltSection; } + + AtomSection<ELFT> *createSection(StringRef name, int32_t type, + DefinedAtom::ContentPermissions permissions, + Layout::SectionOrder order) override { + if (type == DefinedAtom::typeGOT && name == ".got") + return _gotSection; + if (type == DefinedAtom::typeStub && name == ".plt") + return _pltSection; + return DefaultLayout<ELFT>::createSection(name, type, permissions, order); + } + + /// \brief GP offset relative to .got section. + uint64_t getGPOffset() const { return 0x7FF0; } + + /// \brief Get '_gp' symbol atom layout. + AtomLayout *getGP() { + if (!_gpAtom.hasValue()) { + auto atom = this->findAbsoluteAtom("_gp"); + _gpAtom = atom != this->absoluteAtoms().end() ? *atom : nullptr; + } + return *_gpAtom; + } + + /// \brief Get '_gp_disp' symbol atom layout. + AtomLayout *getGPDisp() { + if (!_gpDispAtom.hasValue()) { + auto atom = this->findAbsoluteAtom("_gp_disp"); + _gpDispAtom = atom != this->absoluteAtoms().end() ? *atom : nullptr; + } + return *_gpDispAtom; + } + + /// \brief Return the section order for a input section + Layout::SectionOrder getSectionOrder(StringRef name, int32_t contentType, + int32_t contentPermissions) override { + if ((contentType == DefinedAtom::typeStub) && (name.startswith(".text"))) + return DefaultLayout<ELFT>::ORDER_TEXT; + + return DefaultLayout<ELFT>::getSectionOrder(name, contentType, + contentPermissions); + } + +protected: + unique_bump_ptr<RelocationTable<ELFT>> + createRelocationTable(StringRef name, int32_t order) override { + return unique_bump_ptr<RelocationTable<ELFT>>( + new (this->_allocator) + MipsRelocationTable<ELFT>(this->_context, name, order)); + } + +private: + MipsGOTSection<ELFT> *_gotSection; + MipsPLTSection<ELFT> *_pltSection; + llvm::Optional<AtomLayout *> _gpAtom; + llvm::Optional<AtomLayout *> _gpDispAtom; +}; + +/// \brief Mips Runtime file. +template <class ELFT> class MipsRuntimeFile final : public RuntimeFile<ELFT> { +public: + MipsRuntimeFile(MipsLinkingContext &ctx) + : RuntimeFile<ELFT>(ctx, "Mips runtime file") {} +}; + +/// \brief Auxiliary class holds relocation's names table. +class MipsRelocationStringTable { + static const Registry::KindStrings kindStrings[]; + +public: + static void registerTable(Registry ®istry); +}; + +/// \brief TargetHandler for Mips +template <class ELFT> +class MipsTargetHandler final : public DefaultTargetHandler<ELFT> { +public: + MipsTargetHandler(MipsLinkingContext &ctx) + : _ctx(ctx), _runtimeFile(new MipsRuntimeFile<ELFT>(ctx)), + _targetLayout(new MipsTargetLayout<ELFT>(ctx)), + _relocationHandler(createMipsRelocationHandler<ELFT>(ctx)) {} + + MipsTargetLayout<ELFT> &getTargetLayout() override { return *_targetLayout; } + + std::unique_ptr<Reader> getObjReader() override { + return std::unique_ptr<Reader>(new MipsELFObjectReader<ELFT>(_ctx)); + } + + std::unique_ptr<Reader> getDSOReader() override { + return std::unique_ptr<Reader>(new MipsELFDSOReader<ELFT>(_ctx)); + } + + const TargetRelocationHandler &getRelocationHandler() const override { + return *_relocationHandler; + } + + std::unique_ptr<Writer> getWriter() override { + switch (_ctx.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + return std::unique_ptr<Writer>( + new MipsExecutableWriter<ELFT>(_ctx, *_targetLayout)); + case llvm::ELF::ET_DYN: + return std::unique_ptr<Writer>( + new MipsDynamicLibraryWriter<ELFT>(_ctx, *_targetLayout)); + case llvm::ELF::ET_REL: + llvm_unreachable("TODO: support -r mode"); + default: + llvm_unreachable("unsupported output type"); + } + } + + void registerRelocationNames(Registry ®istry) override { + MipsRelocationStringTable::registerTable(registry); + } + +private: + MipsLinkingContext &_ctx; + std::unique_ptr<MipsRuntimeFile<ELFT>> _runtimeFile; + std::unique_ptr<MipsTargetLayout<ELFT>> _targetLayout; + std::unique_ptr<TargetRelocationHandler> _relocationHandler; +}; + +template <class ELFT> class MipsSymbolTable : public SymbolTable<ELFT> { +public: + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + + MipsSymbolTable(const ELFLinkingContext &ctx) + : SymbolTable<ELFT>(ctx, ".symtab", + DefaultLayout<ELFT>::ORDER_SYMBOL_TABLE) {} + + void addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr) override { + SymbolTable<ELFT>::addDefinedAtom(sym, da, addr); + + switch (da->codeModel()) { + case DefinedAtom::codeMipsMicro: + sym.st_other |= llvm::ELF::STO_MIPS_MICROMIPS; + break; + case DefinedAtom::codeMipsMicroPIC: + sym.st_other |= llvm::ELF::STO_MIPS_MICROMIPS | llvm::ELF::STO_MIPS_PIC; + break; + default: + break; + } + } + + void finalize(bool sort) override { + SymbolTable<ELFT>::finalize(sort); + + for (auto &ste : this->_symbolTable) { + if (!ste._atom) + continue; + if (const auto *da = dyn_cast<DefinedAtom>(ste._atom)) { + if (da->codeModel() == DefinedAtom::codeMipsMicro || + da->codeModel() == DefinedAtom::codeMipsMicroPIC) { + // Adjust dynamic microMIPS symbol value. That allows a dynamic + // linker to recognize and handle this symbol correctly. + ste._symbol.st_value = ste._symbol.st_value | 1; + } + } + } + } +}; + +template <class ELFT> +class MipsDynamicSymbolTable : public DynamicSymbolTable<ELFT> { +public: + MipsDynamicSymbolTable(const ELFLinkingContext &ctx, + MipsTargetLayout<ELFT> &layout) + : DynamicSymbolTable<ELFT>(ctx, layout, ".dynsym", + DefaultLayout<ELFT>::ORDER_DYNAMIC_SYMBOLS), + _targetLayout(layout) {} + + void sortSymbols() override { + typedef typename DynamicSymbolTable<ELFT>::SymbolEntry SymbolEntry; + std::stable_sort(this->_symbolTable.begin(), this->_symbolTable.end(), + [this](const SymbolEntry &A, const SymbolEntry &B) { + if (A._symbol.getBinding() != STB_GLOBAL && + B._symbol.getBinding() != STB_GLOBAL) + return A._symbol.getBinding() < B._symbol.getBinding(); + + return _targetLayout.getGOTSection().compare(A._atom, B._atom); + }); + } + + void finalize() override { + DynamicSymbolTable<ELFT>::finalize(); + + const auto &pltSection = _targetLayout.getPLTSection(); + + for (auto &ste : this->_symbolTable) { + const Atom *a = ste._atom; + if (!a) + continue; + if (auto *layout = pltSection.findPLTLayout(a)) { + a = layout->_atom; + // Under some conditions a dynamic symbol table record should hold + // a symbol value of the corresponding PLT entry. For details look + // at the PLT entry creation code in the class MipsRelocationPass. + // Let's update atomLayout fields for such symbols. + assert(!ste._atomLayout); + ste._symbol.st_value = layout->_virtualAddr; + ste._symbol.st_other |= ELF::STO_MIPS_PLT; + } + + if (const auto *da = dyn_cast<DefinedAtom>(a)) { + if (da->codeModel() == DefinedAtom::codeMipsMicro || + da->codeModel() == DefinedAtom::codeMipsMicroPIC) { + // Adjust dynamic microMIPS symbol value. That allows a dynamic + // linker to recognize and handle this symbol correctly. + ste._symbol.st_value = ste._symbol.st_value | 1; + } + } + } + } + +private: + MipsTargetLayout<ELFT> &_targetLayout; +}; + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/OrderPass.h b/lib/ReaderWriter/ELF/OrderPass.h new file mode 100644 index 0000000000000..d126b830db963 --- /dev/null +++ b/lib/ReaderWriter/ELF/OrderPass.h @@ -0,0 +1,30 @@ +//===- lib/ReaderWriter/ELF/OrderPass.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_ORDER_PASS_H +#define LLD_READER_WRITER_ELF_ORDER_PASS_H + +#include "lld/Core/Parallel.h" +#include <limits> + +namespace lld { +namespace elf { + +/// \brief This pass sorts atoms by file and atom ordinals. +class OrderPass : public Pass { +public: + void perform(std::unique_ptr<MutableFile> &file) override { + parallel_sort(file->definedAtoms().begin(), file->definedAtoms().end(), + DefinedAtom::compareByPosition); + } +}; +} +} + +#endif diff --git a/lib/ReaderWriter/ELF/OutputELFWriter.h b/lib/ReaderWriter/ELF/OutputELFWriter.h new file mode 100644 index 0000000000000..c137905b936b6 --- /dev/null +++ b/lib/ReaderWriter/ELF/OutputELFWriter.h @@ -0,0 +1,615 @@ +//===- lib/ReaderWriter/ELF/OutputELFWriter.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_OUTPUT_WRITER_H +#define LLD_READER_WRITER_ELF_OUTPUT_WRITER_H + +#include "DefaultLayout.h" +#include "ELFFile.h" +#include "TargetLayout.h" +#include "lld/Core/Instrumentation.h" +#include "lld/Core/Parallel.h" +#include "lld/Core/SharedLibraryFile.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "lld/Core/Simple.h" +#include "lld/Core/Writer.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/Path.h" + +namespace lld { +namespace elf { +using namespace llvm; +using namespace llvm::object; + +template <class ELFT> class OutputELFWriter; +template <class ELFT> class TargetLayout; + +namespace { + +template<class ELFT> +class SymbolFile : public RuntimeFile<ELFT> { +public: + SymbolFile(ELFLinkingContext &context) + : RuntimeFile<ELFT>(context, "Dynamic absolute symbols"), + _atomsAdded(false) {} + + Atom *addAbsoluteAtom(StringRef symbolName) override { + auto *a = RuntimeFile<ELFT>::addAbsoluteAtom(symbolName); + if (a) _atomsAdded = true; + return a; + } + + Atom *addUndefinedAtom(StringRef) override { + llvm_unreachable("Cannot add undefined atoms to resolve undefined symbols"); + } + + bool hasAtoms() const { return _atomsAdded; } + +private: + bool _atomsAdded; +}; + +template<class ELFT> +class DynamicSymbolFile : public SimpleArchiveLibraryFile { + typedef std::function<void(StringRef, RuntimeFile<ELFT> &)> Resolver; +public: + DynamicSymbolFile(ELFLinkingContext &context, Resolver resolver) + : SimpleArchiveLibraryFile("Dynamically added runtime symbols"), + _context(context), _resolver(resolver) {} + + File *find(StringRef sym, bool dataSymbolOnly) override { + if (!_file) + _file.reset(new (_alloc) SymbolFile<ELFT>(_context)); + + assert(!_file->hasAtoms() && "The file shouldn't have atoms yet"); + _resolver(sym, *_file); + // If atoms were added - release the file to the caller. + return _file->hasAtoms() ? _file.release() : nullptr; + } + +private: + ELFLinkingContext &_context; + Resolver _resolver; + + // The allocator should go before bump pointers because of + // reversed destruction order. + llvm::BumpPtrAllocator _alloc; + unique_bump_ptr<SymbolFile<ELFT>> _file; +}; + +} // end anon namespace + +//===----------------------------------------------------------------------===// +// OutputELFWriter Class +//===----------------------------------------------------------------------===// +/// \brief This acts as the base class for all the ELF writers that are output +/// for emitting an ELF output file. This class also acts as a common class for +/// creating static and dynamic executables. All the function in this class +/// can be overridden and an appropriate writer be created +template<class ELFT> +class OutputELFWriter : public ELFWriter { +public: + typedef Elf_Shdr_Impl<ELFT> Elf_Shdr; + typedef Elf_Sym_Impl<ELFT> Elf_Sym; + typedef Elf_Dyn_Impl<ELFT> Elf_Dyn; + + OutputELFWriter(ELFLinkingContext &context, TargetLayout<ELFT> &layout); + +protected: + // build the sections that need to be created + virtual void createDefaultSections(); + + // Build all the output sections + void buildChunks(const File &file) override; + + // Build the output file + virtual std::error_code buildOutput(const File &file); + + // Setup the ELF header. + virtual std::error_code setELFHeader(); + + // Write the file to the path specified + std::error_code writeFile(const File &File, StringRef path) override; + + // Write to the output file. + virtual std::error_code writeOutput(const File &file, StringRef path); + + // Get the size of the output file that the linker would emit. + virtual uint64_t outputFileSize() const; + + // Build the atom to address map, this has to be called + // before applying relocations + virtual void buildAtomToAddressMap(const File &file); + + // Build the symbol table for static linking + virtual void buildStaticSymbolTable(const File &file); + + // Build the dynamic symbol table for dynamic linking + virtual void buildDynamicSymbolTable(const File &file); + + // Build the section header table + virtual void buildSectionHeaderTable(); + + // Assign sections that have no segments such as the symbol table, + // section header table, string table etc + virtual void assignSectionsWithNoSegments(); + + // Add default atoms that need to be present in the output file + virtual void addDefaultAtoms(); + + // Add any runtime files and their atoms to the output + bool createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; + + // Finalize the default atom values + virtual void finalizeDefaultAtomValues(); + + // This is called by the write section to apply relocations + uint64_t addressOfAtom(const Atom *atom) override { + auto addr = _atomToAddressMap.find(atom); + return addr == _atomToAddressMap.end() ? 0 : addr->second; + } + + // This is a hook for creating default dynamic entries + virtual void createDefaultDynamicEntries() {} + + /// \brief Create symbol table. + virtual unique_bump_ptr<SymbolTable<ELFT>> createSymbolTable(); + + /// \brief create dynamic table. + virtual unique_bump_ptr<DynamicTable<ELFT>> createDynamicTable(); + + /// \brief create dynamic symbol table. + virtual unique_bump_ptr<DynamicSymbolTable<ELFT>> + createDynamicSymbolTable(); + + /// \brief Create entry in the dynamic symbols table for this atom. + virtual bool isDynSymEntryRequired(const SharedLibraryAtom *sla) const { + return _layout.isReferencedByDefinedAtom(sla); + } + + /// \brief Create DT_NEEDED dynamic tage for the shared library. + virtual bool isNeededTagRequired(const SharedLibraryAtom *sla) const { + return false; + } + + /// \brief Process undefined symbols that left after resolution step. + virtual void processUndefinedSymbol(StringRef symName, + RuntimeFile<ELFT> &file) const {} + + llvm::BumpPtrAllocator _alloc; + + ELFLinkingContext &_context; + TargetHandler<ELFT> &_targetHandler; + + typedef llvm::DenseMap<const Atom *, uint64_t> AtomToAddress; + AtomToAddress _atomToAddressMap; + TargetLayout<ELFT> &_layout; + unique_bump_ptr<ELFHeader<ELFT>> _elfHeader; + unique_bump_ptr<ProgramHeader<ELFT>> _programHeader; + unique_bump_ptr<SymbolTable<ELFT>> _symtab; + unique_bump_ptr<StringTable<ELFT>> _strtab; + unique_bump_ptr<StringTable<ELFT>> _shstrtab; + unique_bump_ptr<SectionHeader<ELFT>> _shdrtab; + unique_bump_ptr<EHFrameHeader<ELFT>> _ehFrameHeader; + /// \name Dynamic sections. + /// @{ + unique_bump_ptr<DynamicTable<ELFT>> _dynamicTable; + unique_bump_ptr<DynamicSymbolTable<ELFT>> _dynamicSymbolTable; + unique_bump_ptr<StringTable<ELFT>> _dynamicStringTable; + unique_bump_ptr<HashSection<ELFT>> _hashTable; + llvm::StringSet<> _soNeeded; + /// @} + std::unique_ptr<RuntimeFile<ELFT>> _scriptFile; + +private: + static StringRef maybeGetSOName(Node *node); +}; + +//===----------------------------------------------------------------------===// +// OutputELFWriter +//===----------------------------------------------------------------------===// +template <class ELFT> +OutputELFWriter<ELFT>::OutputELFWriter(ELFLinkingContext &context, + TargetLayout<ELFT> &layout) + : _context(context), _targetHandler(context.getTargetHandler<ELFT>()), + _layout(layout), + _scriptFile(new RuntimeFile<ELFT>(context, "Linker script runtime")) {} + +template <class ELFT> +void OutputELFWriter<ELFT>::buildChunks(const File &file) { + ScopedTask task(getDefaultDomain(), "buildChunks"); + for (const DefinedAtom *definedAtom : file.defined()) { + DefinedAtom::ContentType contentType = definedAtom->contentType(); + // Dont add COMDAT group atoms and GNU linkonce atoms, as they are used for + // symbol resolution. + // TODO: handle partial linking. + if (contentType == DefinedAtom::typeGroupComdat || + contentType == DefinedAtom::typeGnuLinkOnce) + continue; + _layout.addAtom(definedAtom); + } + for (const AbsoluteAtom *absoluteAtom : file.absolute()) + _layout.addAtom(absoluteAtom); +} + +template <class ELFT> +void OutputELFWriter<ELFT>::buildStaticSymbolTable(const File &file) { + ScopedTask task(getDefaultDomain(), "buildStaticSymbolTable"); + for (auto sec : _layout.sections()) + if (auto section = dyn_cast<AtomSection<ELFT>>(sec)) + for (const auto &atom : section->atoms()) + _symtab->addSymbol(atom->_atom, section->ordinal(), atom->_virtualAddr); + for (auto &atom : _layout.absoluteAtoms()) + _symtab->addSymbol(atom->_atom, ELF::SHN_ABS, atom->_virtualAddr); + for (const UndefinedAtom *a : file.undefined()) + _symtab->addSymbol(a, ELF::SHN_UNDEF); +} + +// Returns the DSO name for a given input file if it's a shared library +// file and not marked as --as-needed. +template <class ELFT> +StringRef OutputELFWriter<ELFT>::maybeGetSOName(Node *node) { + if (auto *fnode = dyn_cast<FileNode>(node)) + if (!fnode->asNeeded()) + if (auto *file = dyn_cast<SharedLibraryFile>(fnode->getFile())) + return file->getDSOName(); + return ""; +} + +template <class ELFT> +void OutputELFWriter<ELFT>::buildDynamicSymbolTable(const File &file) { + ScopedTask task(getDefaultDomain(), "buildDynamicSymbolTable"); + for (const auto &sla : file.sharedLibrary()) { + if (isDynSymEntryRequired(sla)) { + _dynamicSymbolTable->addSymbol(sla, ELF::SHN_UNDEF); + _soNeeded.insert(sla->loadName()); + continue; + } + if (isNeededTagRequired(sla)) + _soNeeded.insert(sla->loadName()); + } + for (const std::unique_ptr<Node> &node : _context.getNodes()) { + StringRef soname = maybeGetSOName(node.get()); + if (!soname.empty()) + _soNeeded.insert(soname); + } + // Never mark the dynamic linker as DT_NEEDED + _soNeeded.erase(sys::path::filename(_context.getInterpreter())); + for (const auto &loadName : _soNeeded) { + Elf_Dyn dyn; + dyn.d_tag = DT_NEEDED; + dyn.d_un.d_val = _dynamicStringTable->addString(loadName.getKey()); + _dynamicTable->addEntry(dyn); + } + const auto &rpathList = _context.getRpathList(); + if (!rpathList.empty()) { + auto rpath = new (_alloc) std::string(join(rpathList.begin(), + rpathList.end(), ":")); + Elf_Dyn dyn; + dyn.d_tag = DT_RPATH; + dyn.d_un.d_val = _dynamicStringTable->addString(*rpath); + _dynamicTable->addEntry(dyn); + } + StringRef soname = _context.sharedObjectName(); + if (!soname.empty() && _context.getOutputELFType() == llvm::ELF::ET_DYN) { + Elf_Dyn dyn; + dyn.d_tag = DT_SONAME; + dyn.d_un.d_val = _dynamicStringTable->addString(soname); + _dynamicTable->addEntry(dyn); + } + // The dynamic symbol table need to be sorted earlier because the hash + // table needs to be built using the dynamic symbol table. It would be + // late to sort the symbols due to that in finalize. In the dynamic symbol + // table finalize, we call the symbol table finalize and we don't want to + // sort again + _dynamicSymbolTable->sortSymbols(); + + // Add the dynamic symbols into the hash table + _dynamicSymbolTable->addSymbolsToHashTable(); +} + +template <class ELFT> +void OutputELFWriter<ELFT>::buildAtomToAddressMap(const File &file) { + ScopedTask task(getDefaultDomain(), "buildAtomToAddressMap"); + int64_t totalAbsAtoms = _layout.absoluteAtoms().size(); + int64_t totalUndefinedAtoms = file.undefined().size(); + int64_t totalDefinedAtoms = 0; + for (auto sec : _layout.sections()) + if (auto section = dyn_cast<AtomSection<ELFT> >(sec)) { + totalDefinedAtoms += section->atoms().size(); + for (const auto &atom : section->atoms()) + _atomToAddressMap[atom->_atom] = atom->_virtualAddr; + } + // build the atomToAddressMap that contains absolute symbols too + for (auto &atom : _layout.absoluteAtoms()) + _atomToAddressMap[atom->_atom] = atom->_virtualAddr; + + // Set the total number of atoms in the symbol table, so that appropriate + // resizing of the string table can be done + _symtab->setNumEntries(totalDefinedAtoms + totalAbsAtoms + + totalUndefinedAtoms); +} + +template<class ELFT> +void OutputELFWriter<ELFT>::buildSectionHeaderTable() { + ScopedTask task(getDefaultDomain(), "buildSectionHeaderTable"); + for (auto outputSection : _layout.outputSections()) { + if (outputSection->kind() != Chunk<ELFT>::Kind::ELFSection && + outputSection->kind() != Chunk<ELFT>::Kind::AtomSection) + continue; + if (outputSection->hasSegment()) + _shdrtab->appendSection(outputSection); + } +} + +template<class ELFT> +void OutputELFWriter<ELFT>::assignSectionsWithNoSegments() { + ScopedTask task(getDefaultDomain(), "assignSectionsWithNoSegments"); + for (auto outputSection : _layout.outputSections()) { + if (outputSection->kind() != Chunk<ELFT>::Kind::ELFSection && + outputSection->kind() != Chunk<ELFT>::Kind::AtomSection) + continue; + if (!outputSection->hasSegment()) + _shdrtab->appendSection(outputSection); + } + _layout.assignFileOffsetsForMiscSections(); + for (auto sec : _layout.sections()) + if (auto section = dyn_cast<Section<ELFT>>(sec)) + if (!DefaultLayout<ELFT>::hasOutputSegment(section)) + _shdrtab->updateSection(section); +} + +template <class ELFT> void OutputELFWriter<ELFT>::addDefaultAtoms() { + const llvm::StringSet<> &symbols = + _context.linkerScriptSema().getScriptDefinedSymbols(); + for (auto &sym : symbols) + _scriptFile->addAbsoluteAtom(sym.getKey()); +} + +template <class ELFT> +bool OutputELFWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + // Add the virtual archive to resolve undefined symbols. + // The file will be added later in the linking context. + auto callback = [this](StringRef sym, RuntimeFile<ELFT> &file) { + processUndefinedSymbol(sym, file); + }; + auto &ctx = const_cast<ELFLinkingContext &>(_context); + ctx.setUndefinesResolver( + llvm::make_unique<DynamicSymbolFile<ELFT>>(ctx, std::move(callback))); + // Add script defined symbols + result.push_back(std::move(_scriptFile)); + return true; +} + +template <class ELFT> +void OutputELFWriter<ELFT>::finalizeDefaultAtomValues() { + const llvm::StringSet<> &symbols = + _context.linkerScriptSema().getScriptDefinedSymbols(); + for (auto &sym : symbols) { + uint64_t res = + _context.linkerScriptSema().getLinkerScriptExprValue(sym.getKey()); + auto a = _layout.findAbsoluteAtom(sym.getKey()); + (*a)->_virtualAddr = res; + } +} + +template <class ELFT> void OutputELFWriter<ELFT>::createDefaultSections() { + _elfHeader.reset(new (_alloc) ELFHeader<ELFT>(_context)); + _programHeader.reset(new (_alloc) ProgramHeader<ELFT>(_context)); + _layout.setHeader(_elfHeader.get()); + _layout.setProgramHeader(_programHeader.get()); + + _symtab = std::move(this->createSymbolTable()); + _strtab.reset(new (_alloc) StringTable<ELFT>( + _context, ".strtab", DefaultLayout<ELFT>::ORDER_STRING_TABLE)); + _shstrtab.reset(new (_alloc) StringTable<ELFT>( + _context, ".shstrtab", DefaultLayout<ELFT>::ORDER_SECTION_STRINGS)); + _shdrtab.reset(new (_alloc) SectionHeader<ELFT>( + _context, DefaultLayout<ELFT>::ORDER_SECTION_HEADERS)); + _layout.addSection(_symtab.get()); + _layout.addSection(_strtab.get()); + _layout.addSection(_shstrtab.get()); + _shdrtab->setStringSection(_shstrtab.get()); + _symtab->setStringSection(_strtab.get()); + _layout.addSection(_shdrtab.get()); + + for (auto sec : _layout.sections()) { + // TODO: use findOutputSection + auto section = dyn_cast<Section<ELFT>>(sec); + if (!section || section->outputSectionName() != ".eh_frame") + continue; + _ehFrameHeader.reset(new (_alloc) EHFrameHeader<ELFT>( + _context, ".eh_frame_hdr", _layout, + DefaultLayout<ELFT>::ORDER_EH_FRAMEHDR)); + _layout.addSection(_ehFrameHeader.get()); + break; + } + + if (_context.isDynamic()) { + _dynamicTable = std::move(createDynamicTable()); + _dynamicStringTable.reset(new (_alloc) StringTable<ELFT>( + _context, ".dynstr", DefaultLayout<ELFT>::ORDER_DYNAMIC_STRINGS, true)); + _dynamicSymbolTable = std::move(createDynamicSymbolTable()); + _hashTable.reset(new (_alloc) HashSection<ELFT>( + _context, ".hash", DefaultLayout<ELFT>::ORDER_HASH)); + // Set the hash table in the dynamic symbol table so that the entries in the + // hash table can be created + _dynamicSymbolTable->setHashTable(_hashTable.get()); + _hashTable->setSymbolTable(_dynamicSymbolTable.get()); + _layout.addSection(_dynamicTable.get()); + _layout.addSection(_dynamicStringTable.get()); + _layout.addSection(_dynamicSymbolTable.get()); + _layout.addSection(_hashTable.get()); + _dynamicSymbolTable->setStringSection(_dynamicStringTable.get()); + _dynamicTable->setSymbolTable(_dynamicSymbolTable.get()); + _dynamicTable->setHashTable(_hashTable.get()); + if (_layout.hasDynamicRelocationTable()) + _layout.getDynamicRelocationTable()->setSymbolTable( + _dynamicSymbolTable.get()); + if (_layout.hasPLTRelocationTable()) + _layout.getPLTRelocationTable()->setSymbolTable( + _dynamicSymbolTable.get()); + } +} + +template <class ELFT> +unique_bump_ptr<SymbolTable<ELFT>> + OutputELFWriter<ELFT>::createSymbolTable() { + return unique_bump_ptr<SymbolTable<ELFT>>(new (_alloc) SymbolTable<ELFT>( + this->_context, ".symtab", DefaultLayout<ELFT>::ORDER_SYMBOL_TABLE)); +} + +/// \brief create dynamic table +template <class ELFT> +unique_bump_ptr<DynamicTable<ELFT>> + OutputELFWriter<ELFT>::createDynamicTable() { + return unique_bump_ptr<DynamicTable<ELFT>>( + new (_alloc) DynamicTable<ELFT>( + this->_context, _layout, ".dynamic", DefaultLayout<ELFT>::ORDER_DYNAMIC)); +} + +/// \brief create dynamic symbol table +template <class ELFT> +unique_bump_ptr<DynamicSymbolTable<ELFT>> + OutputELFWriter<ELFT>::createDynamicSymbolTable() { + return unique_bump_ptr<DynamicSymbolTable<ELFT>>( + new (_alloc) DynamicSymbolTable<ELFT>( + this->_context, _layout, ".dynsym", + DefaultLayout<ELFT>::ORDER_DYNAMIC_SYMBOLS)); +} + +template <class ELFT> +std::error_code OutputELFWriter<ELFT>::buildOutput(const File &file) { + ScopedTask buildTask(getDefaultDomain(), "ELF Writer buildOutput"); + buildChunks(file); + + // Create the default sections like the symbol table, string table, and the + // section string table + createDefaultSections(); + + // Set the Layout + _layout.assignSectionsToSegments(); + + // Create the dynamic table entries + if (_context.isDynamic()) { + _dynamicTable->createDefaultEntries(); + buildDynamicSymbolTable(file); + } + + // Call the preFlight callbacks to modify the sections and the atoms + // contained in them, in anyway the targets may want + _layout.doPreFlight(); + + _layout.assignVirtualAddress(); + + // Finalize the default value of symbols that the linker adds + finalizeDefaultAtomValues(); + + // Build the Atom To Address map for applying relocations + buildAtomToAddressMap(file); + + // Create symbol table and section string table + // Do it only if -s is not specified. + if (!_context.stripSymbols()) + buildStaticSymbolTable(file); + + // Finalize the layout by calling the finalize() functions + _layout.finalize(); + + // build Section Header table + buildSectionHeaderTable(); + + // assign Offsets and virtual addresses + // for sections with no segments + assignSectionsWithNoSegments(); + + if (_context.isDynamic()) + _dynamicTable->updateDynamicTable(); + + return std::error_code(); +} + +template <class ELFT> std::error_code OutputELFWriter<ELFT>::setELFHeader() { + _elfHeader->e_type(_context.getOutputELFType()); + _elfHeader->e_machine(_context.getOutputMachine()); + _elfHeader->e_ident(ELF::EI_VERSION, 1); + _elfHeader->e_ident(ELF::EI_OSABI, 0); + _elfHeader->e_version(1); + _elfHeader->e_phoff(_programHeader->fileOffset()); + _elfHeader->e_shoff(_shdrtab->fileOffset()); + _elfHeader->e_phentsize(_programHeader->entsize()); + _elfHeader->e_phnum(_programHeader->numHeaders()); + _elfHeader->e_shentsize(_shdrtab->entsize()); + _elfHeader->e_shnum(_shdrtab->numHeaders()); + _elfHeader->e_shstrndx(_shstrtab->ordinal()); + if (const auto *al = _layout.findAtomLayoutByName(_context.entrySymbolName())) + _elfHeader->e_entry(al->_virtualAddr); + else + _elfHeader->e_entry(0); + + return std::error_code(); +} + +template <class ELFT> uint64_t OutputELFWriter<ELFT>::outputFileSize() const { + return _shdrtab->fileOffset() + _shdrtab->fileSize(); +} + +template <class ELFT> +std::error_code OutputELFWriter<ELFT>::writeOutput(const File &file, + StringRef path) { + std::unique_ptr<FileOutputBuffer> buffer; + ScopedTask createOutputTask(getDefaultDomain(), "ELF Writer Create Output"); + std::error_code ec = FileOutputBuffer::create(path, outputFileSize(), buffer, + FileOutputBuffer::F_executable); + createOutputTask.end(); + + if (ec) + return ec; + + ScopedTask writeTask(getDefaultDomain(), "ELF Writer write to memory"); + + // HACK: We have to write out the header and program header here even though + // they are a member of a segment because only sections are written in the + // following loop. + + // Finalize ELF Header / Program Headers. + _elfHeader->finalize(); + _programHeader->finalize(); + + _elfHeader->write(this, _layout, *buffer); + _programHeader->write(this, _layout, *buffer); + + auto sections = _layout.sections(); + parallel_for_each( + sections.begin(), sections.end(), + [&](Chunk<ELFT> *section) { section->write(this, _layout, *buffer); }); + writeTask.end(); + + ScopedTask commitTask(getDefaultDomain(), "ELF Writer commit to disk"); + return buffer->commit(); +} + +template <class ELFT> +std::error_code OutputELFWriter<ELFT>::writeFile(const File &file, + StringRef path) { + std::error_code ec = buildOutput(file); + if (ec) + return ec; + + ec = setELFHeader(); + if (ec) + return ec; + + return writeOutput(file, path); +} +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_ELF_OUTPUT_WRITER_H diff --git a/lib/ReaderWriter/ELF/Reader.cpp b/lib/ReaderWriter/ELF/Reader.cpp new file mode 100644 index 0000000000000..fc113d4789132 --- /dev/null +++ b/lib/ReaderWriter/ELF/Reader.cpp @@ -0,0 +1,43 @@ +//===- lib/ReaderWriter/ELF/Reader.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 ELF Reader and all helper sub classes to consume an ELF +/// file and produces atoms out of it. +/// +//===----------------------------------------------------------------------===// + +#include "ELFReader.h" +#include <map> +#include <vector> + +using llvm::support::endianness; +using namespace llvm::object; + +namespace lld { + +// This dynamic registration of a handler causes support for all ELF +// architectures to be pulled into the linker. If we want to support making a +// linker that only supports one ELF architecture, we'd need to change this +// to have a different registration method for each architecture. +void Registry::addSupportELFObjects(ELFLinkingContext &ctx) { + + // Tell registry about the ELF object file parser. + add(std::move(ctx.targetHandler()->getObjReader())); + + // Tell registry about the relocation name to number mapping for this arch. + ctx.targetHandler()->registerRelocationNames(*this); +} + +void Registry::addSupportELFDynamicSharedObjects(ELFLinkingContext &ctx) { + // Tell registry about the ELF dynamic shared library file parser. + add(ctx.targetHandler()->getDSOReader()); +} + +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/SectionChunks.h b/lib/ReaderWriter/ELF/SectionChunks.h new file mode 100644 index 0000000000000..03bdb59e65681 --- /dev/null +++ b/lib/ReaderWriter/ELF/SectionChunks.h @@ -0,0 +1,1498 @@ +//===- lib/ReaderWriter/ELF/SectionChunks.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_SECTION_CHUNKS_H +#define LLD_READER_WRITER_ELF_SECTION_CHUNKS_H + +#include "Chunk.h" +#include "Layout.h" +#include "TargetHandler.h" +#include "Writer.h" +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/Parallel.h" +#include "lld/Core/range.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Dwarf.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileOutputBuffer.h" +#include <memory> +#include <mutex> + +namespace lld { +namespace elf { +template <class> class OutputSection; +using namespace llvm::ELF; +template <class ELFT> class Segment; + +/// \brief An ELF section. +template <class ELFT> class Section : public Chunk<ELFT> { +public: + Section(const ELFLinkingContext &context, StringRef sectionName, + StringRef chunkName, + typename Chunk<ELFT>::Kind k = Chunk<ELFT>::Kind::ELFSection) + : Chunk<ELFT>(chunkName, k, context), _outputSection(nullptr), _flags(0), + _entSize(0), _type(0), _link(0), _info(0), + _isFirstSectionInOutputSection(false), _segmentType(SHT_NULL), + _inputSectionName(sectionName), _outputSectionName(sectionName) {} + + /// \brief Modify the section contents before assigning virtual addresses + // or assigning file offsets + void doPreFlight() override {} + + /// \brief Finalize the section contents before writing + void finalize() override {} + + /// \brief Does this section have an output segment. + virtual bool hasOutputSegment() { + return false; + } + + /// Return if the section is a loadable section that occupies memory + virtual bool isLoadableSection() const { return false; } + + /// \brief Assign file offsets starting at offset. + virtual void assignFileOffsets(uint64_t offset) {} + + /// \brief Assign virtual addresses starting at addr. + virtual void assignVirtualAddress(uint64_t addr) {} + + uint64_t getFlags() const { return _flags; } + uint64_t getEntSize() const { return _entSize; } + uint32_t getType() const { return _type; } + uint32_t getLink() const { return _link; } + uint32_t getInfo() const { return _info; } + Layout::SegmentType getSegmentType() const { return _segmentType; } + + /// \brief Return the type of content that the section contains + virtual int getContentType() const override { + if (_flags & llvm::ELF::SHF_EXECINSTR) + return Chunk<ELFT>::ContentType::Code; + else if (_flags & llvm::ELF::SHF_WRITE) + return Chunk<ELFT>::ContentType::Data; + else if (_flags & llvm::ELF::SHF_ALLOC) + return Chunk<ELFT>::ContentType::Code; + else + return Chunk<ELFT>::ContentType::Unknown; + } + + /// \brief convert the segment type to a String for diagnostics and printing + /// purposes + StringRef segmentKindToStr() const; + + /// \brief Records the segmentType, that this section belongs to + void setSegmentType(const Layout::SegmentType segmentType) { + this->_segmentType = segmentType; + } + + virtual const AtomLayout *findAtomLayoutByName(StringRef) const { + return nullptr; + } + + void setOutputSection(OutputSection<ELFT> *os, bool isFirst = false) { + _outputSection = os; + _isFirstSectionInOutputSection = isFirst; + } + + static bool classof(const Chunk<ELFT> *c) { + return c->kind() == Chunk<ELFT>::Kind::ELFSection || + c->kind() == Chunk<ELFT>::Kind::AtomSection; + } + + uint64_t alignment() const override { + return _isFirstSectionInOutputSection ? _outputSection->alignment() + : this->_alignment; + } + + virtual StringRef inputSectionName() const { return _inputSectionName; } + + virtual StringRef outputSectionName() const { return _outputSectionName; } + + virtual void setOutputSectionName(StringRef outputSectionName) { + _outputSectionName = outputSectionName; + } + + void setArchiveNameOrPath(StringRef name) { _archivePath = name; } + + void setMemberNameOrPath(StringRef name) { _memberPath = name; } + + StringRef archivePath() { return _archivePath; } + + StringRef memberPath() { return _memberPath; } + +protected: + /// \brief OutputSection this Section is a member of, or nullptr. + OutputSection<ELFT> *_outputSection; + /// \brief ELF SHF_* flags. + uint64_t _flags; + /// \brief The size of each entity. + uint64_t _entSize; + /// \brief ELF SHT_* type. + uint32_t _type; + /// \brief sh_link field. + uint32_t _link; + /// \brief the sh_info field. + uint32_t _info; + /// \brief Is this the first section in the output section. + bool _isFirstSectionInOutputSection; + /// \brief the output ELF segment type of this section. + Layout::SegmentType _segmentType; + /// \brief Input section name. + StringRef _inputSectionName; + /// \brief Output section name. + StringRef _outputSectionName; + StringRef _archivePath; + StringRef _memberPath; +}; + +/// \brief A section containing atoms. +template <class ELFT> class AtomSection : public Section<ELFT> { +public: + AtomSection(const ELFLinkingContext &context, StringRef sectionName, + int32_t contentType, int32_t permissions, int32_t order) + : Section<ELFT>(context, sectionName, "AtomSection", + Chunk<ELFT>::Kind::AtomSection), + _contentType(contentType), _contentPermissions(permissions), + _isLoadedInMemory(true) { + this->setOrder(order); + + switch (contentType) { + case DefinedAtom::typeCode: + case DefinedAtom::typeDataFast: + case DefinedAtom::typeData: + case DefinedAtom::typeConstant: + case DefinedAtom::typeGOT: + case DefinedAtom::typeStub: + case DefinedAtom::typeResolver: + case DefinedAtom::typeThreadData: + this->_type = SHT_PROGBITS; + break; + + case DefinedAtom::typeThreadZeroFill: + case DefinedAtom::typeZeroFillFast: + case DefinedAtom::typeZeroFill: + this->_type = SHT_NOBITS; + break; + + case DefinedAtom::typeRONote: + case DefinedAtom::typeRWNote: + this->_type = SHT_NOTE; + break; + + case DefinedAtom::typeNoAlloc: + this->_type = SHT_PROGBITS; + this->_isLoadedInMemory = false; + break; + } + + switch (permissions) { + case DefinedAtom::permR__: + this->_flags = SHF_ALLOC; + break; + case DefinedAtom::permR_X: + this->_flags = SHF_ALLOC | SHF_EXECINSTR; + break; + case DefinedAtom::permRW_: + case DefinedAtom::permRW_L: + this->_flags = SHF_ALLOC | SHF_WRITE; + if (_contentType == DefinedAtom::typeThreadData || + _contentType == DefinedAtom::typeThreadZeroFill) + this->_flags |= SHF_TLS; + break; + case DefinedAtom::permRWX: + this->_flags = SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR; + break; + case DefinedAtom::perm___: + this->_flags = 0; + break; + } + } + + /// Align the offset to the required modulus defined by the atom alignment + uint64_t alignOffset(uint64_t offset, DefinedAtom::Alignment &atomAlign); + + /// Return if the section is a loadable section that occupies memory + bool isLoadableSection() const override { return _isLoadedInMemory; } + + // \brief Append an atom to a Section. The atom gets pushed into a vector + // contains the atom, the atom file offset, the atom virtual address + // the atom file offset is aligned appropriately as set by the Reader + virtual const lld::AtomLayout *appendAtom(const Atom *atom); + + /// \brief Set the virtual address of each Atom in the Section. This + /// routine gets called after the linker fixes up the virtual address + /// of the section + virtual void assignVirtualAddress(uint64_t addr) override { + parallel_for_each(_atoms.begin(), _atoms.end(), [&](AtomLayout *ai) { + ai->_virtualAddr = addr + ai->_fileOffset; + }); + } + + /// \brief Set the file offset of each Atom in the section. This routine + /// gets called after the linker fixes up the section offset + void assignFileOffsets(uint64_t offset) override { + parallel_for_each(_atoms.begin(), _atoms.end(), [&](AtomLayout *ai) { + ai->_fileOffset = offset + ai->_fileOffset; + }); + } + + /// \brief Find the Atom address given a name, this is needed to properly + /// apply relocation. The section class calls this to find the atom address + /// to fix the relocation + const AtomLayout *findAtomLayoutByName(StringRef name) const override { + for (auto ai : _atoms) + if (ai->_atom->name() == name) + return ai; + return nullptr; + } + + /// \brief Return the raw flags, we need this to sort segments + int64_t atomflags() const { return _contentPermissions; } + + /// Atom Iterators + typedef typename std::vector<lld::AtomLayout *>::iterator atom_iter; + + range<atom_iter> atoms() { return _atoms; } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override; + + static bool classof(const Chunk<ELFT> *c) { + return c->kind() == Chunk<ELFT>::Kind::AtomSection; + } + +protected: + llvm::BumpPtrAllocator _alloc; + int32_t _contentType; + int32_t _contentPermissions; + bool _isLoadedInMemory; + std::vector<lld::AtomLayout *> _atoms; + mutable std::mutex _outputMutex; + + void printError(const std::string &errorStr, const AtomLayout &atom, + const Reference &ref) const { + StringRef kindValStr; + if (!this->_context.registry().referenceKindToString(ref.kindNamespace(), + ref.kindArch(), + ref.kindValue(), + kindValStr)) { + kindValStr = "unknown"; + } + + std::string errStr = (Twine(errorStr) + " in file " + + atom._atom->file().path() + + ": reference from " + atom._atom->name() + + "+" + Twine(ref.offsetInAtom()) + + " to " + ref.target()->name() + + "+" + Twine(ref.addend()) + + " of type " + Twine(ref.kindValue()) + + " (" + kindValStr + ")\n").str(); + + // Take the lock to prevent output getting interleaved between threads + std::lock_guard<std::mutex> lock(_outputMutex); + llvm::errs() << errStr; + } +}; + +/// Align the offset to the required modulus defined by the atom alignment +template <class ELFT> +uint64_t AtomSection<ELFT>::alignOffset(uint64_t offset, + DefinedAtom::Alignment &atomAlign) { + uint64_t requiredModulus = atomAlign.modulus; + uint64_t alignment = 1u << atomAlign.powerOf2; + uint64_t currentModulus = (offset % alignment); + uint64_t retOffset = offset; + if (currentModulus != requiredModulus) { + if (requiredModulus > currentModulus) + retOffset += requiredModulus - currentModulus; + else + retOffset += alignment + requiredModulus - currentModulus; + } + return retOffset; +} + +// \brief Append an atom to a Section. The atom gets pushed into a vector +// contains the atom, the atom file offset, the atom virtual address +// the atom file offset is aligned appropriately as set by the Reader +template <class ELFT> +const lld::AtomLayout *AtomSection<ELFT>::appendAtom(const Atom *atom) { + const DefinedAtom *definedAtom = cast<DefinedAtom>(atom); + + DefinedAtom::Alignment atomAlign = definedAtom->alignment(); + uint64_t alignment = 1u << atomAlign.powerOf2; + // Align the atom to the required modulus/ align the file offset and the + // memory offset separately this is required so that BSS symbols are handled + // properly as the BSS symbols only occupy memory size and not file size + uint64_t fOffset = alignOffset(this->fileSize(), atomAlign); + uint64_t mOffset = alignOffset(this->memSize(), atomAlign); + switch(definedAtom->contentType()) { + case DefinedAtom::typeCode: + case DefinedAtom::typeConstant: + case DefinedAtom::typeData: + case DefinedAtom::typeDataFast: + case DefinedAtom::typeZeroFillFast: + case DefinedAtom::typeGOT: + case DefinedAtom::typeStub: + case DefinedAtom::typeResolver: + case DefinedAtom::typeThreadData: + case DefinedAtom::typeRONote: + case DefinedAtom::typeRWNote: + _atoms.push_back(new (_alloc) lld::AtomLayout(atom, fOffset, 0)); + this->_fsize = fOffset + definedAtom->size(); + this->_msize = mOffset + definedAtom->size(); + DEBUG_WITH_TYPE("Section", + llvm::dbgs() << "[" << this->name() << " " << this << "] " + << "Adding atom: " << atom->name() << "@" + << fOffset << "\n"); + break; + case DefinedAtom::typeNoAlloc: + _atoms.push_back(new (_alloc) lld::AtomLayout(atom, fOffset, 0)); + this->_fsize = fOffset + definedAtom->size(); + DEBUG_WITH_TYPE("Section", llvm::dbgs() << "[" << this->name() << " " + << this << "] " + << "Adding atom: " << atom->name() + << "@" << fOffset << "\n"); + break; + case DefinedAtom::typeThreadZeroFill: + case DefinedAtom::typeZeroFill: + _atoms.push_back(new (_alloc) lld::AtomLayout(atom, mOffset, 0)); + this->_msize = mOffset + definedAtom->size(); + break; + default: + llvm::dbgs() << definedAtom->contentType() << "\n"; + llvm_unreachable("Uexpected content type."); + } + // Set the section alignment to the largest alignment + // std::max doesn't support uint64_t + if (this->_alignment < alignment) + this->_alignment = alignment; + + if (_atoms.size()) + return _atoms.back(); + return nullptr; +} + +/// \brief convert the segment type to a String for diagnostics +/// and printing purposes +template <class ELFT> StringRef Section<ELFT>::segmentKindToStr() const { + switch(_segmentType) { + case llvm::ELF::PT_DYNAMIC: + return "DYNAMIC"; + case llvm::ELF::PT_INTERP: + return "INTERP"; + case llvm::ELF::PT_LOAD: + return "LOAD"; + case llvm::ELF::PT_GNU_EH_FRAME: + return "EH_FRAME"; + case llvm::ELF::PT_GNU_RELRO: + return "GNU_RELRO"; + case llvm::ELF::PT_NOTE: + return "NOTE"; + case llvm::ELF::PT_NULL: + return "NULL"; + case llvm::ELF::PT_TLS: + return "TLS"; + default: + return "UNKNOWN"; + } +} + +/// \brief Write the section and the atom contents to the buffer +template <class ELFT> +void AtomSection<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + bool success = true; + parallel_for_each(_atoms.begin(), _atoms.end(), [&](lld::AtomLayout * ai) { + DEBUG_WITH_TYPE("Section", + llvm::dbgs() << "Writing atom: " << ai->_atom->name() + << " | " << ai->_fileOffset << "\n"); + const DefinedAtom *definedAtom = cast<DefinedAtom>(ai->_atom); + if (!definedAtom->occupiesDiskSpace()) + return; + // Copy raw content of atom to file buffer. + ArrayRef<uint8_t> content = definedAtom->rawContent(); + uint64_t contentSize = content.size(); + if (contentSize == 0) + return; + uint8_t *atomContent = chunkBuffer + ai->_fileOffset; + std::memcpy(atomContent, content.data(), contentSize); + const TargetRelocationHandler &relHandler = + this->_context.template getTargetHandler<ELFT>().getRelocationHandler(); + for (const auto ref : *definedAtom) { + if (std::error_code ec = relHandler.applyRelocation(*writer, buffer, + *ai, *ref)) { + printError(ec.message(), *ai, *ref); + success = false; + } + } + }); + if (!success) + llvm::report_fatal_error("relocating output"); +} + +/// \brief A OutputSection represents a set of sections grouped by the same +/// name. The output file that gets written by the linker has sections grouped +/// by similar names +template <class ELFT> class OutputSection { +public: + // Iterators + typedef typename std::vector<Chunk<ELFT> *>::iterator ChunkIter; + + OutputSection(StringRef name); + + // Appends a section into the list of sections that are part of this Output + // Section + void appendSection(Chunk<ELFT> *c); + + // Set the OutputSection is associated with a segment + void setHasSegment() { _hasSegment = true; } + + /// Sets the ordinal + void setOrdinal(uint64_t ordinal) { _ordinal = ordinal; } + + /// Sets the Memory size + void setMemSize(uint64_t memsz) { _memSize = memsz; } + + /// Sets the size fo the output Section. + void setSize(uint64_t fsiz) { _size = fsiz; } + + // The offset of the first section contained in the output section is + // contained here. + void setFileOffset(uint64_t foffset) { _fileOffset = foffset; } + + // Sets the starting address of the section + void setAddr(uint64_t addr) { _virtualAddr = addr; } + + // Is the section loadable? + bool isLoadableSection() const { return _isLoadableSection; } + + // Set section Loadable + void setLoadableSection(bool isLoadable) { + _isLoadableSection = isLoadable; + } + + void setLink(uint64_t link) { _link = link; } + + void setInfo(uint64_t info) { _shInfo = info; } + + void setFlag(uint64_t flags) { _flags = flags; } + + void setType(int16_t type) { _type = type; } + + range<ChunkIter> sections() { return _sections; } + + // The below functions returns the properties of the OutputSection. + bool hasSegment() const { return _hasSegment; } + + StringRef name() const { return _name; } + + int64_t shinfo() const { return _shInfo; } + + uint64_t alignment() const { return _alignment; } + + int64_t link() const { return _link; } + + int64_t type() const { return _type; } + + uint64_t virtualAddr() const { return _virtualAddr; } + + int64_t ordinal() const { return _ordinal; } + + int64_t kind() const { return _kind; } + + uint64_t fileSize() const { return _size; } + + int64_t entsize() const { return _entSize; } + + uint64_t fileOffset() const { return _fileOffset; } + + int64_t flags() const { return _flags; } + + uint64_t memSize() { return _memSize; } + +private: + StringRef _name; + bool _hasSegment; + uint64_t _ordinal; + uint64_t _flags; + uint64_t _size; + uint64_t _memSize; + uint64_t _fileOffset; + uint64_t _virtualAddr; + int64_t _shInfo; + int64_t _entSize; + int64_t _link; + uint64_t _alignment; + int64_t _kind; + int64_t _type; + bool _isLoadableSection; + std::vector<Chunk<ELFT> *> _sections; +}; + +/// OutputSection +template <class ELFT> +OutputSection<ELFT>::OutputSection(StringRef name) + : _name(name), _hasSegment(false), _ordinal(0), _flags(0), _size(0), + _memSize(0), _fileOffset(0), _virtualAddr(0), _shInfo(0), _entSize(0), + _link(0), _alignment(0), _kind(0), _type(0), _isLoadableSection(false) {} + +template <class ELFT> void OutputSection<ELFT>::appendSection(Chunk<ELFT> *c) { + if (c->alignment() > _alignment) + _alignment = c->alignment(); + if (const auto section = dyn_cast<Section<ELFT>>(c)) { + assert(!_link && "Section already has a link!"); + _link = section->getLink(); + _shInfo = section->getInfo(); + _entSize = section->getEntSize(); + _type = section->getType(); + if (_flags < section->getFlags()) + _flags = section->getFlags(); + section->setOutputSection(this, (_sections.size() == 0)); + } + _kind = c->kind(); + _sections.push_back(c); +} + +/// \brief The class represents the ELF String Table +template<class ELFT> +class StringTable : public Section<ELFT> { +public: + StringTable(const ELFLinkingContext &, const char *str, int32_t order, + bool dynamic = false); + + uint64_t addString(StringRef symname); + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override; + + void setNumEntries(int64_t numEntries) { _stringMap.resize(numEntries); } + +private: + std::vector<StringRef> _strings; + + struct StringRefMappingInfo { + static StringRef getEmptyKey() { return StringRef(); } + static StringRef getTombstoneKey() { return StringRef(" ", 1); } + static unsigned getHashValue(StringRef const val) { + return llvm::HashString(val); + } + static bool isEqual(StringRef const lhs, StringRef const rhs) { + return lhs.equals(rhs); + } + }; + typedef typename llvm::DenseMap<StringRef, uint64_t, + StringRefMappingInfo> StringMapT; + typedef typename StringMapT::iterator StringMapTIter; + StringMapT _stringMap; +}; + +template <class ELFT> +StringTable<ELFT>::StringTable(const ELFLinkingContext &context, + const char *str, int32_t order, bool dynamic) + : Section<ELFT>(context, str, "StringTable") { + // the string table has a NULL entry for which + // add an empty string + _strings.push_back(""); + this->_fsize = 1; + this->_alignment = 1; + this->setOrder(order); + this->_type = SHT_STRTAB; + if (dynamic) { + this->_flags = SHF_ALLOC; + this->_msize = this->_fsize; + } +} + +template <class ELFT> uint64_t StringTable<ELFT>::addString(StringRef symname) { + if (symname.empty()) + return 0; + StringMapTIter stringIter = _stringMap.find(symname); + if (stringIter == _stringMap.end()) { + _strings.push_back(symname); + uint64_t offset = this->_fsize; + this->_fsize += symname.size() + 1; + if (this->_flags & SHF_ALLOC) + this->_msize = this->_fsize; + _stringMap[symname] = offset; + return offset; + } + return stringIter->second; +} + +template <class ELFT> +void StringTable<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + for (auto si : _strings) { + memcpy(dest, si.data(), si.size()); + dest += si.size(); + memcpy(dest, "", 1); + dest += 1; + } +} + +/// \brief The SymbolTable class represents the symbol table in a ELF file +template<class ELFT> +class SymbolTable : public Section<ELFT> { + typedef typename llvm::object::ELFDataTypeTypedefHelper<ELFT>::Elf_Addr + Elf_Addr; + +public: + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + + SymbolTable(const ELFLinkingContext &context, const char *str, int32_t order); + + /// \brief set the number of entries that would exist in the symbol + /// table for the current link + void setNumEntries(int64_t numEntries) const { + if (_stringSection) + _stringSection->setNumEntries(numEntries); + } + + /// \brief return number of entries + std::size_t size() const { return _symbolTable.size(); } + + void addSymbol(const Atom *atom, int32_t sectionIndex, uint64_t addr = 0, + const lld::AtomLayout *layout = nullptr); + + /// \brief Get the symbol table index for an Atom. If it's not in the symbol + /// table, return STN_UNDEF. + uint32_t getSymbolTableIndex(const Atom *a) const { + for (size_t i = 0, e = _symbolTable.size(); i < e; ++i) + if (_symbolTable[i]._atom == a) + return i; + return STN_UNDEF; + } + + void finalize() override { finalize(true); } + + virtual void sortSymbols() { + std::stable_sort(_symbolTable.begin(), _symbolTable.end(), + [](const SymbolEntry & A, const SymbolEntry & B) { + return A._symbol.getBinding() < B._symbol.getBinding(); + }); + } + + virtual void addAbsoluteAtom(Elf_Sym &sym, const AbsoluteAtom *aa, + int64_t addr); + + virtual void addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr); + + virtual void addUndefinedAtom(Elf_Sym &sym, const UndefinedAtom *ua); + + virtual void addSharedLibAtom(Elf_Sym &sym, const SharedLibraryAtom *sla); + + virtual void finalize(bool sort); + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override; + + void setStringSection(StringTable<ELFT> *s) { _stringSection = s; } + + StringTable<ELFT> *getStringTable() const { return _stringSection; } + +protected: + struct SymbolEntry { + SymbolEntry(const Atom *a, const Elf_Sym &sym, + const lld::AtomLayout *layout) + : _atom(a), _atomLayout(layout), _symbol(sym) {} + + const Atom *_atom; + const lld::AtomLayout *_atomLayout; + Elf_Sym _symbol; + }; + + llvm::BumpPtrAllocator _symbolAllocate; + StringTable<ELFT> *_stringSection; + std::vector<SymbolEntry> _symbolTable; +}; + +/// ELF Symbol Table +template <class ELFT> +SymbolTable<ELFT>::SymbolTable(const ELFLinkingContext &context, + const char *str, int32_t order) + : Section<ELFT>(context, str, "SymbolTable") { + this->setOrder(order); + Elf_Sym symbol; + std::memset(&symbol, 0, sizeof(Elf_Sym)); + _symbolTable.push_back(SymbolEntry(nullptr, symbol, nullptr)); + this->_entSize = sizeof(Elf_Sym); + this->_fsize = sizeof(Elf_Sym); + this->_alignment = sizeof(Elf_Addr); + this->_type = SHT_SYMTAB; +} + +template <class ELFT> +void SymbolTable<ELFT>::addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr) { + unsigned char binding = 0, type = 0; + sym.st_size = da->size(); + DefinedAtom::ContentType ct; + switch (ct = da->contentType()) { + case DefinedAtom::typeCode: + case DefinedAtom::typeStub: + sym.st_value = addr; + type = llvm::ELF::STT_FUNC; + break; + case DefinedAtom::typeResolver: + sym.st_value = addr; + type = llvm::ELF::STT_GNU_IFUNC; + break; + case DefinedAtom::typeDataFast: + case DefinedAtom::typeData: + case DefinedAtom::typeConstant: + sym.st_value = addr; + type = llvm::ELF::STT_OBJECT; + break; + case DefinedAtom::typeGOT: + sym.st_value = addr; + type = llvm::ELF::STT_NOTYPE; + break; + case DefinedAtom::typeZeroFill: + case DefinedAtom::typeZeroFillFast: + type = llvm::ELF::STT_OBJECT; + sym.st_value = addr; + break; + case DefinedAtom::typeThreadData: + case DefinedAtom::typeThreadZeroFill: + type = llvm::ELF::STT_TLS; + sym.st_value = addr; + break; + default: + type = llvm::ELF::STT_NOTYPE; + } + if (da->customSectionName() == da->name()) + type = llvm::ELF::STT_SECTION; + + if (da->scope() == DefinedAtom::scopeTranslationUnit) + binding = llvm::ELF::STB_LOCAL; + else + binding = llvm::ELF::STB_GLOBAL; + + sym.setBindingAndType(binding, type); +} + +template <class ELFT> +void SymbolTable<ELFT>::addAbsoluteAtom(Elf_Sym &sym, const AbsoluteAtom *aa, + int64_t addr) { + unsigned char binding = 0, type = 0; + type = llvm::ELF::STT_OBJECT; + sym.st_shndx = llvm::ELF::SHN_ABS; + switch (aa->scope()) { + case AbsoluteAtom::scopeLinkageUnit: + sym.setVisibility(llvm::ELF::STV_HIDDEN); + binding = llvm::ELF::STB_LOCAL; + break; + case AbsoluteAtom::scopeTranslationUnit: + binding = llvm::ELF::STB_LOCAL; + break; + case AbsoluteAtom::scopeGlobal: + binding = llvm::ELF::STB_GLOBAL; + break; + } + sym.st_value = addr; + sym.setBindingAndType(binding, type); +} + +template <class ELFT> +void SymbolTable<ELFT>::addSharedLibAtom(Elf_Sym &sym, + const SharedLibraryAtom *aa) { + unsigned char binding = 0, type = 0; + if (aa->type() == SharedLibraryAtom::Type::Data) { + type = llvm::ELF::STT_OBJECT; + sym.st_size = aa->size(); + } else + type = llvm::ELF::STT_FUNC; + sym.st_shndx = llvm::ELF::SHN_UNDEF; + binding = llvm::ELF::STB_GLOBAL; + sym.setBindingAndType(binding, type); +} + +template <class ELFT> +void SymbolTable<ELFT>::addUndefinedAtom(Elf_Sym &sym, + const UndefinedAtom *ua) { + unsigned char binding = 0, type = 0; + sym.st_value = 0; + type = llvm::ELF::STT_NOTYPE; + if (ua->canBeNull()) + binding = llvm::ELF::STB_WEAK; + else + binding = llvm::ELF::STB_GLOBAL; + sym.setBindingAndType(binding, type); +} + +/// Add a symbol to the symbol Table, definedAtoms which get added to the symbol +/// section don't have their virtual addresses set at the time of adding the +/// symbol to the symbol table(Example: dynamic symbols), the addresses needs +/// to be updated in the table before writing the dynamic symbol table +/// information +template <class ELFT> +void SymbolTable<ELFT>::addSymbol(const Atom *atom, int32_t sectionIndex, + uint64_t addr, + const lld::AtomLayout *atomLayout) { + Elf_Sym symbol; + + if (atom->name().empty()) + return; + + symbol.st_name = _stringSection->addString(atom->name()); + symbol.st_size = 0; + symbol.st_shndx = sectionIndex; + symbol.st_value = 0; + symbol.st_other = 0; + symbol.setVisibility(llvm::ELF::STV_DEFAULT); + + // Add all the atoms + if (const DefinedAtom *da = dyn_cast<const DefinedAtom>(atom)) + addDefinedAtom(symbol, da, addr); + else if (const AbsoluteAtom *aa = dyn_cast<const AbsoluteAtom>(atom)) + addAbsoluteAtom(symbol, aa, addr); + else if (isa<const SharedLibraryAtom>(atom)) + addSharedLibAtom(symbol, dyn_cast<SharedLibraryAtom>(atom)); + else + addUndefinedAtom(symbol, dyn_cast<UndefinedAtom>(atom)); + + _symbolTable.push_back(SymbolEntry(atom, symbol, atomLayout)); + this->_fsize += sizeof(Elf_Sym); + if (this->_flags & SHF_ALLOC) + this->_msize = this->_fsize; +} + +template <class ELFT> void SymbolTable<ELFT>::finalize(bool sort) { + // sh_info should be one greater than last symbol with STB_LOCAL binding + // we sort the symbol table to keep all local symbols at the beginning + if (sort) + sortSymbols(); + + uint16_t shInfo = 0; + for (const auto &i : _symbolTable) { + if (i._symbol.getBinding() != llvm::ELF::STB_LOCAL) + break; + shInfo++; + } + this->_info = shInfo; + this->_link = _stringSection->ordinal(); + if (this->_outputSection) { + this->_outputSection->setInfo(this->_info); + this->_outputSection->setLink(this->_link); + } +} + +template <class ELFT> +void SymbolTable<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + for (const auto &sti : _symbolTable) { + memcpy(dest, &sti._symbol, sizeof(Elf_Sym)); + dest += sizeof(Elf_Sym); + } +} + +template <class ELFT> class HashSection; + +template <class ELFT> class DynamicSymbolTable : public SymbolTable<ELFT> { +public: + DynamicSymbolTable(const ELFLinkingContext &context, + TargetLayout<ELFT> &layout, const char *str, int32_t order) + : SymbolTable<ELFT>(context, str, order), _hashTable(nullptr), + _layout(layout) { + this->_type = SHT_DYNSYM; + this->_flags = SHF_ALLOC; + this->_msize = this->_fsize; + } + + // Set the dynamic hash table for symbols to be added into + void setHashTable(HashSection<ELFT> *hashTable) { _hashTable = hashTable; } + + // Add all the dynamic symbos to the hash table + void addSymbolsToHashTable() { + int index = 0; + for (auto &ste : this->_symbolTable) { + if (!ste._atom) + _hashTable->addSymbol("", index); + else + _hashTable->addSymbol(ste._atom->name(), index); + ++index; + } + } + + void finalize() override { + // Defined symbols which have been added into the dynamic symbol table + // don't have their addresses known until addresses have been assigned + // so let's update the symbol values after they have got assigned + for (auto &ste: this->_symbolTable) { + const lld::AtomLayout *atomLayout = ste._atomLayout; + if (!atomLayout) + continue; + ste._symbol.st_value = atomLayout->_virtualAddr; + } + + // Don't sort the symbols + SymbolTable<ELFT>::finalize(false); + } + +protected: + HashSection<ELFT> *_hashTable; + TargetLayout<ELFT> &_layout; +}; + +template <class ELFT> class RelocationTable : public Section<ELFT> { +public: + typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel; + typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela; + + RelocationTable(const ELFLinkingContext &context, StringRef str, + int32_t order) + : Section<ELFT>(context, str, "RelocationTable"), _symbolTable(nullptr) { + this->setOrder(order); + this->_flags = SHF_ALLOC; + // Set the alignment properly depending on the target architecture + this->_alignment = ELFT::Is64Bits ? 8 : 4; + if (context.isRelaOutputFormat()) { + this->_entSize = sizeof(Elf_Rela); + this->_type = SHT_RELA; + } else { + this->_entSize = sizeof(Elf_Rel); + this->_type = SHT_REL; + } + } + + /// \returns the index of the relocation added. + uint32_t addRelocation(const DefinedAtom &da, const Reference &r) { + _relocs.emplace_back(&da, &r); + this->_fsize = _relocs.size() * this->_entSize; + this->_msize = this->_fsize; + return _relocs.size() - 1; + } + + bool getRelocationIndex(const Reference &r, uint32_t &res) { + auto rel = std::find_if( + _relocs.begin(), _relocs.end(), + [&](const std::pair<const DefinedAtom *, const Reference *> &p) { + if (p.second == &r) + return true; + return false; + }); + if (rel == _relocs.end()) + return false; + res = std::distance(_relocs.begin(), rel); + return true; + } + + void setSymbolTable(const DynamicSymbolTable<ELFT> *symbolTable) { + _symbolTable = symbolTable; + } + + /// \brief Check if any relocation modifies a read-only section. + bool canModifyReadonlySection() const { + for (const auto &rel : _relocs) { + const DefinedAtom *atom = rel.first; + if ((atom->permissions() & DefinedAtom::permRW_) != DefinedAtom::permRW_) + return true; + } + return false; + } + + void finalize() override { + this->_link = _symbolTable ? _symbolTable->ordinal() : 0; + if (this->_outputSection) + this->_outputSection->setLink(this->_link); + } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + for (const auto &rel : _relocs) { + if (this->_context.isRelaOutputFormat()) { + auto &r = *reinterpret_cast<Elf_Rela *>(dest); + writeRela(writer, r, *rel.first, *rel.second); + DEBUG_WITH_TYPE("ELFRelocationTable", + llvm::dbgs() + << rel.second->kindValue() << " relocation at " + << rel.first->name() << "@" << r.r_offset << " to " + << rel.second->target()->name() << "@" << r.r_addend + << "\n";); + } else { + auto &r = *reinterpret_cast<Elf_Rel *>(dest); + writeRel(writer, r, *rel.first, *rel.second); + DEBUG_WITH_TYPE("ELFRelocationTable", + llvm::dbgs() << rel.second->kindValue() + << " relocation at " << rel.first->name() + << "@" << r.r_offset << " to " + << rel.second->target()->name() << "\n";); + } + dest += this->_entSize; + } + } + +protected: + const DynamicSymbolTable<ELFT> *_symbolTable; + + virtual void writeRela(ELFWriter *writer, Elf_Rela &r, + const DefinedAtom &atom, const Reference &ref) { + r.setSymbolAndType(getSymbolIndex(ref.target()), ref.kindValue(), false); + r.r_offset = writer->addressOfAtom(&atom) + ref.offsetInAtom(); + // The addend is used only by relative relocations + if (this->_context.isRelativeReloc(ref)) + r.r_addend = writer->addressOfAtom(ref.target()) + ref.addend(); + else + r.r_addend = 0; + } + + virtual void writeRel(ELFWriter *writer, Elf_Rel &r, const DefinedAtom &atom, + const Reference &ref) { + r.setSymbolAndType(getSymbolIndex(ref.target()), ref.kindValue(), false); + r.r_offset = writer->addressOfAtom(&atom) + ref.offsetInAtom(); + } + + uint32_t getSymbolIndex(const Atom *a) { + return _symbolTable ? _symbolTable->getSymbolTableIndex(a) + : (uint32_t)STN_UNDEF; + } + +private: + std::vector<std::pair<const DefinedAtom *, const Reference *> > _relocs; +}; + +template <class ELFT> class HashSection; + +template <class ELFT> class DynamicTable : public Section<ELFT> { +public: + typedef llvm::object::Elf_Dyn_Impl<ELFT> Elf_Dyn; + typedef std::vector<Elf_Dyn> EntriesT; + + DynamicTable(const ELFLinkingContext &context, TargetLayout<ELFT> &layout, + StringRef str, int32_t order) + : Section<ELFT>(context, str, "DynamicSection"), _layout(layout) { + this->setOrder(order); + this->_entSize = sizeof(Elf_Dyn); + this->_alignment = ELFT::Is64Bits ? 8 : 4; + // Reserve space for the DT_NULL entry. + this->_fsize = sizeof(Elf_Dyn); + this->_msize = sizeof(Elf_Dyn); + this->_type = SHT_DYNAMIC; + this->_flags = SHF_ALLOC; + } + + range<typename EntriesT::iterator> entries() { return _entries; } + + /// \returns the index of the entry. + std::size_t addEntry(Elf_Dyn e) { + _entries.push_back(e); + this->_fsize = (_entries.size() * sizeof(Elf_Dyn)) + sizeof(Elf_Dyn); + this->_msize = this->_fsize; + return _entries.size() - 1; + } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + // Add the null entry. + Elf_Dyn d; + d.d_tag = 0; + d.d_un.d_val = 0; + _entries.push_back(d); + std::memcpy(dest, _entries.data(), this->_fsize); + } + + virtual void createDefaultEntries() { + bool isRela = this->_context.isRelaOutputFormat(); + + Elf_Dyn dyn; + dyn.d_un.d_val = 0; + + dyn.d_tag = DT_HASH; + _dt_hash = addEntry(dyn); + dyn.d_tag = DT_STRTAB; + _dt_strtab = addEntry(dyn); + dyn.d_tag = DT_SYMTAB; + _dt_symtab = addEntry(dyn); + dyn.d_tag = DT_STRSZ; + _dt_strsz = addEntry(dyn); + dyn.d_tag = DT_SYMENT; + _dt_syment = addEntry(dyn); + if (_layout.hasDynamicRelocationTable()) { + dyn.d_tag = isRela ? DT_RELA : DT_REL; + _dt_rela = addEntry(dyn); + dyn.d_tag = isRela ? DT_RELASZ : DT_RELSZ; + _dt_relasz = addEntry(dyn); + dyn.d_tag = isRela ? DT_RELAENT : DT_RELENT; + _dt_relaent = addEntry(dyn); + + if (_layout.getDynamicRelocationTable()->canModifyReadonlySection()) { + dyn.d_tag = DT_TEXTREL; + _dt_textrel = addEntry(dyn); + } + } + if (_layout.hasPLTRelocationTable()) { + dyn.d_tag = DT_PLTRELSZ; + _dt_pltrelsz = addEntry(dyn); + dyn.d_tag = getGotPltTag(); + _dt_pltgot = addEntry(dyn); + dyn.d_tag = DT_PLTREL; + dyn.d_un.d_val = isRela ? DT_RELA : DT_REL; + _dt_pltrel = addEntry(dyn); + dyn.d_un.d_val = 0; + dyn.d_tag = DT_JMPREL; + _dt_jmprel = addEntry(dyn); + } + } + + void doPreFlight() override { + Elf_Dyn dyn; + dyn.d_un.d_val = 0; + auto initArray = _layout.findOutputSection(".init_array"); + auto finiArray = _layout.findOutputSection(".fini_array"); + if (initArray) { + dyn.d_tag = DT_INIT_ARRAY; + _dt_init_array = addEntry(dyn); + dyn.d_tag = DT_INIT_ARRAYSZ; + _dt_init_arraysz = addEntry(dyn); + } + if (finiArray) { + dyn.d_tag = DT_FINI_ARRAY; + _dt_fini_array = addEntry(dyn); + dyn.d_tag = DT_FINI_ARRAYSZ; + _dt_fini_arraysz = addEntry(dyn); + } + if (getInitAtomLayout()) { + dyn.d_tag = DT_INIT; + _dt_init = addEntry(dyn); + } + if (getFiniAtomLayout()) { + dyn.d_tag = DT_FINI; + _dt_fini = addEntry(dyn); + } + } + + /// \brief Dynamic table tag for .got.plt section referencing. + /// Usually but not always targets use DT_PLTGOT for that. + virtual int64_t getGotPltTag() { return DT_PLTGOT; } + + void finalize() override { + StringTable<ELFT> *dynamicStringTable = + _dynamicSymbolTable->getStringTable(); + this->_link = dynamicStringTable->ordinal(); + if (this->_outputSection) { + this->_outputSection->setType(this->_type); + this->_outputSection->setInfo(this->_info); + this->_outputSection->setLink(this->_link); + } + } + + void setSymbolTable(DynamicSymbolTable<ELFT> *dynsym) { + _dynamicSymbolTable = dynsym; + } + + const DynamicSymbolTable<ELFT> *getSymbolTable() const { + return _dynamicSymbolTable; + } + + void setHashTable(HashSection<ELFT> *hsh) { _hashTable = hsh; } + + virtual void updateDynamicTable() { + StringTable<ELFT> *dynamicStringTable = + _dynamicSymbolTable->getStringTable(); + _entries[_dt_hash].d_un.d_val = _hashTable->virtualAddr(); + _entries[_dt_strtab].d_un.d_val = dynamicStringTable->virtualAddr(); + _entries[_dt_symtab].d_un.d_val = _dynamicSymbolTable->virtualAddr(); + _entries[_dt_strsz].d_un.d_val = dynamicStringTable->memSize(); + _entries[_dt_syment].d_un.d_val = _dynamicSymbolTable->getEntSize(); + auto initArray = _layout.findOutputSection(".init_array"); + if (initArray) { + _entries[_dt_init_array].d_un.d_val = initArray->virtualAddr(); + _entries[_dt_init_arraysz].d_un.d_val = initArray->memSize(); + } + auto finiArray = _layout.findOutputSection(".fini_array"); + if (finiArray) { + _entries[_dt_fini_array].d_un.d_val = finiArray->virtualAddr(); + _entries[_dt_fini_arraysz].d_un.d_val = finiArray->memSize(); + } + if (const auto *al = getInitAtomLayout()) + _entries[_dt_init].d_un.d_val = getAtomVirtualAddress(al); + if (const auto *al = getFiniAtomLayout()) + _entries[_dt_fini].d_un.d_val = getAtomVirtualAddress(al); + if (_layout.hasDynamicRelocationTable()) { + auto relaTbl = _layout.getDynamicRelocationTable(); + _entries[_dt_rela].d_un.d_val = relaTbl->virtualAddr(); + _entries[_dt_relasz].d_un.d_val = relaTbl->memSize(); + _entries[_dt_relaent].d_un.d_val = relaTbl->getEntSize(); + } + if (_layout.hasPLTRelocationTable()) { + auto relaTbl = _layout.getPLTRelocationTable(); + _entries[_dt_jmprel].d_un.d_val = relaTbl->virtualAddr(); + _entries[_dt_pltrelsz].d_un.d_val = relaTbl->memSize(); + auto gotplt = _layout.findOutputSection(".got.plt"); + _entries[_dt_pltgot].d_un.d_val = gotplt->virtualAddr(); + } + } + +protected: + EntriesT _entries; + + /// \brief Return a virtual address (maybe adjusted) for the atom layout + /// Some targets like microMIPS and ARM Thumb use the last bit + /// of a symbol's value to mark 'compressed' code. This function allows + /// to adjust a virtal address before using it in the dynamic table tag. + virtual uint64_t getAtomVirtualAddress(const AtomLayout *al) const { + return al->_virtualAddr; + } + +private: + std::size_t _dt_hash; + std::size_t _dt_strtab; + std::size_t _dt_symtab; + std::size_t _dt_rela; + std::size_t _dt_relasz; + std::size_t _dt_relaent; + std::size_t _dt_strsz; + std::size_t _dt_syment; + std::size_t _dt_pltrelsz; + std::size_t _dt_pltgot; + std::size_t _dt_pltrel; + std::size_t _dt_jmprel; + std::size_t _dt_init_array; + std::size_t _dt_init_arraysz; + std::size_t _dt_fini_array; + std::size_t _dt_fini_arraysz; + std::size_t _dt_textrel; + std::size_t _dt_init; + std::size_t _dt_fini; + TargetLayout<ELFT> &_layout; + DynamicSymbolTable<ELFT> *_dynamicSymbolTable; + HashSection<ELFT> *_hashTable; + + const AtomLayout *getInitAtomLayout() { + auto al = _layout.findAtomLayoutByName(this->_context.initFunction()); + if (al && isa<DefinedAtom>(al->_atom)) + return al; + return nullptr; + } + + const AtomLayout *getFiniAtomLayout() { + auto al = _layout.findAtomLayoutByName(this->_context.finiFunction()); + if (al && isa<DefinedAtom>(al->_atom)) + return al; + return nullptr; + } +}; + +template <class ELFT> class InterpSection : public Section<ELFT> { +public: + InterpSection(const ELFLinkingContext &context, StringRef str, int32_t order, + StringRef interp) + : Section<ELFT>(context, str, "Dynamic:Interp"), _interp(interp) { + this->setOrder(order); + this->_alignment = 1; + // + 1 for null term. + this->_fsize = interp.size() + 1; + this->_msize = this->_fsize; + this->_type = SHT_PROGBITS; + this->_flags = SHF_ALLOC; + } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + std::memcpy(dest, _interp.data(), _interp.size()); + } + +private: + StringRef _interp; +}; + +/// The hash table in the dynamic linker is organized into +/// +/// [ nbuckets ] +/// [ nchains ] +/// [ buckets[0] ] +/// ......................... +/// [ buckets[nbuckets-1] ] +/// [ chains[0] ] +/// ......................... +/// [ chains[nchains - 1] ] +/// +/// nbuckets - total number of hash buckets +/// nchains is equal to the number of dynamic symbols. +/// +/// The symbol is searched by the dynamic linker using the below approach. +/// * Calculate the hash of the symbol that needs to be searched +/// * Take the value from the buckets[hash % nbuckets] as the index of symbol +/// * Compare the symbol's name, if true return, if false, look through the +/// * array since there was a collision + +template <class ELFT> class HashSection : public Section<ELFT> { + struct SymbolTableEntry { + StringRef _name; + uint32_t _index; + }; + +public: + HashSection(const ELFLinkingContext &context, StringRef name, int32_t order) + : Section<ELFT>(context, name, "Dynamic:Hash"), _symbolTable(nullptr) { + this->setOrder(order); + this->_entSize = 4; + this->_type = SHT_HASH; + this->_flags = SHF_ALLOC; + this->_alignment = ELFT::Is64Bits ? 8 : 4; + this->_fsize = 0; + this->_msize = 0; + } + + /// \brief add the dynamic symbol into the table so that the + /// hash could be calculated + void addSymbol(StringRef name, uint32_t index) { + SymbolTableEntry ste; + ste._name = name; + ste._index = index; + _entries.push_back(ste); + } + + /// \brief Set the dynamic symbol table + void setSymbolTable(const DynamicSymbolTable<ELFT> *symbolTable) { + _symbolTable = symbolTable; + } + + // The size of the section has to be determined so that fileoffsets + // may be properly assigned. Let's calculate the buckets and the chains + // and fill the chains and the buckets hash table used by the dynamic + // linker and update the filesize and memory size accordingly + void doPreFlight() override { + // The number of buckets to use for a certain number of symbols. + // If there are less than 3 symbols, 1 bucket will be used. If + // there are less than 17 symbols, 3 buckets will be used, and so + // forth. The bucket numbers are defined by GNU ld. We use the + // same rules here so we generate hash sections with the same + // size as those generated by GNU ld. + uint32_t hashBuckets[] = { 1, 3, 17, 37, 67, 97, 131, 197, 263, 521, 1031, + 2053, 4099, 8209, 16411, 32771, 65537, 131101, + 262147 }; + int hashBucketsCount = sizeof(hashBuckets) / sizeof(uint32_t); + + unsigned int bucketsCount = 0; + unsigned int dynSymCount = _entries.size(); + + // Get the number of buckes that we want to use + for (int i = 0; i < hashBucketsCount; ++i) { + if (dynSymCount < hashBuckets[i]) + break; + bucketsCount = hashBuckets[i]; + } + _buckets.resize(bucketsCount); + _chains.resize(_entries.size()); + + // Create the hash table for the dynamic linker + for (auto ai : _entries) { + unsigned int dynsymIndex = ai._index; + unsigned int bucketpos = llvm::object::elf_hash(ai._name) % bucketsCount; + _chains[dynsymIndex] = _buckets[bucketpos]; + _buckets[bucketpos] = dynsymIndex; + } + + this->_fsize = (2 + _chains.size() + _buckets.size()) * sizeof(uint32_t); + this->_msize = this->_fsize; + } + + void finalize() override { + this->_link = _symbolTable ? _symbolTable->ordinal() : 0; + if (this->_outputSection) + this->_outputSection->setLink(this->_link); + } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + uint32_t bucketChainCounts[2]; + bucketChainCounts[0] = _buckets.size(); + bucketChainCounts[1] = _chains.size(); + std::memcpy(dest, (char *)bucketChainCounts, sizeof(bucketChainCounts)); + dest += sizeof(bucketChainCounts); + // write bucket values + for (auto bi : _buckets) { + uint32_t val = (bi); + std::memcpy(dest, &val, sizeof(uint32_t)); + dest += sizeof(uint32_t); + } + // write chain values + for (auto ci : _chains) { + uint32_t val = (ci); + std::memcpy(dest, &val, sizeof(uint32_t)); + dest += sizeof(uint32_t); + } + } + +private: + std::vector<SymbolTableEntry> _entries; + std::vector<uint32_t> _buckets; + std::vector<uint32_t> _chains; + const DynamicSymbolTable<ELFT> *_symbolTable; +}; + +template <class ELFT> class EHFrameHeader : public Section<ELFT> { +public: + EHFrameHeader(const ELFLinkingContext &context, StringRef name, + TargetLayout<ELFT> &layout, int32_t order) + : Section<ELFT>(context, name, "EHFrameHeader"), _ehFrameOffset(0), + _layout(layout) { + this->setOrder(order); + this->_entSize = 0; + this->_type = SHT_PROGBITS; + this->_flags = SHF_ALLOC; + this->_alignment = ELFT::Is64Bits ? 8 : 4; + // Minimum size for empty .eh_frame_hdr. + this->_fsize = 1 + 1 + 1 + 1 + 4; + this->_msize = this->_fsize; + } + + void doPreFlight() override { + // TODO: Generate a proper binary search table. + } + + void finalize() override { + OutputSection<ELFT> *s = _layout.findOutputSection(".eh_frame"); + OutputSection<ELFT> *h = _layout.findOutputSection(".eh_frame_hdr"); + if (s && h) + _ehFrameOffset = s->virtualAddr() - (h->virtualAddr() + 4); + } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + int pos = 0; + dest[pos++] = 1; // version + dest[pos++] = llvm::dwarf::DW_EH_PE_pcrel | + llvm::dwarf::DW_EH_PE_sdata4; // eh_frame_ptr_enc + dest[pos++] = llvm::dwarf::DW_EH_PE_omit; // fde_count_enc + dest[pos++] = llvm::dwarf::DW_EH_PE_omit; // table_enc + *reinterpret_cast<typename llvm::object::ELFFile<ELFT>::Elf_Sword *>( + dest + pos) = _ehFrameOffset; + } + +private: + int32_t _ehFrameOffset; + TargetLayout<ELFT> &_layout; +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/SegmentChunks.h b/lib/ReaderWriter/ELF/SegmentChunks.h new file mode 100644 index 0000000000000..f2a975aaeed0b --- /dev/null +++ b/lib/ReaderWriter/ELF/SegmentChunks.h @@ -0,0 +1,686 @@ +//===- lib/ReaderWriter/ELF/SegmentChunks.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_SEGMENT_CHUNKS_H +#define LLD_READER_WRITER_ELF_SEGMENT_CHUNKS_H + +#include "Chunk.h" +#include "Layout.h" +#include "SectionChunks.h" +#include "Writer.h" +#include "lld/Core/range.h" +#include "lld/Core/Writer.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileOutputBuffer.h" +#include <memory> + +namespace lld { +namespace elf { + +template <typename ELFT> class DefaultLayout; + +/// \brief A segment can be divided into segment slices +/// depending on how the segments can be split +template<class ELFT> +class SegmentSlice { +public: + typedef typename std::vector<Chunk<ELFT> *>::iterator SectionIter; + + SegmentSlice() { } + + /// Set the start of the slice. + void setStart(int32_t s) { _startSection = s; } + + // Set the segment slice start and end iterators. This is used to walk through + // the sections that are part of the Segment slice + void setSections(range<SectionIter> sections) { _sections = sections; } + + // Return the fileOffset of the slice + uint64_t fileOffset() const { return _offset; } + + void setFileOffset(uint64_t offset) { _offset = offset; } + + // Return the size of the slice + uint64_t fileSize() const { return _fsize; } + + void setFileSize(uint64_t filesz) { _fsize = filesz; } + + // Return the start of the slice + int32_t startSection() const { return _startSection; } + + // Return the start address of the slice + uint64_t virtualAddr() const { return _addr; } + + // Return the memory size of the slice + uint64_t memSize() const { return _memSize; } + + // Return the alignment of the slice + uint64_t alignment() const { return _alignment; } + + void setMemSize(uint64_t memsz) { _memSize = memsz; } + + void setVirtualAddr(uint64_t addr) { _addr = addr; } + + void setAlign(uint64_t align) { _alignment = align; } + + static bool compare_slices(SegmentSlice<ELFT> *a, SegmentSlice<ELFT> *b) { + return a->startSection() < b->startSection(); + } + + range<SectionIter> sections() { return _sections; } + +private: + range<SectionIter> _sections; + int32_t _startSection; + uint64_t _addr; + uint64_t _offset; + uint64_t _alignment; + uint64_t _fsize; + uint64_t _memSize; +}; + +/// \brief A segment contains a set of sections, that have similar properties +// the sections are already separated based on different flags and properties +// the segment is just a way to concatenate sections to segments +template<class ELFT> +class Segment : public Chunk<ELFT> { +public: + typedef typename std::vector<SegmentSlice<ELFT> *>::iterator SliceIter; + typedef typename std::vector<Chunk<ELFT> *>::iterator SectionIter; + + Segment(const ELFLinkingContext &context, StringRef name, + const Layout::SegmentType type); + + /// \brief the Order of segments that appear in the output file + enum SegmentOrder { + permUnknown, + permRWX, + permRX, + permR, + permRWL, + permRW, + permNonAccess + }; + + /// append a section to a segment + virtual void append(Chunk<ELFT> *chunk); + + /// Sort segments depending on the property + /// If we have a Program Header segment, it should appear first + /// If we have a INTERP segment, that should appear after the Program Header + /// All Loadable segments appear next in this order + /// All Read Write Execute segments follow + /// All Read Execute segments appear next + /// All Read only segments appear first + /// All Write execute segments follow + static bool compareSegments(Segment<ELFT> *sega, Segment<ELFT> *segb); + + /// \brief Start assigning file offset to the segment chunks The fileoffset + /// needs to be page at the start of the segment and in addition the + /// fileoffset needs to be aligned to the max section alignment within the + /// segment. This is required so that the ELF property p_poffset % p_align = + /// p_vaddr mod p_align holds true. + /// The algorithm starts off by assigning the startOffset thats passed in as + /// parameter to the first section in the segment, if the difference between + /// the newly computed offset is greater than a page, then we create a segment + /// slice, as it would be a waste of virtual memory just to be filled with + /// zeroes + void assignFileOffsets(uint64_t startOffset); + + /// \brief Assign virtual addresses to the slices + void assignVirtualAddress(uint64_t addr); + + // Write the Segment + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer); + + int64_t flags() const; + + /// Prepend a generic chunk to the segment. + void prepend(Chunk<ELFT> *c) { + _sections.insert(_sections.begin(), c); + } + + /// Finalize the segment before assigning File Offsets / Virtual addresses + void doPreFlight() {} + + /// Finalize the segment, before we want to write the segment header + /// information + void finalize() { + // We want to finalize the segment values for now only for non loadable + // segments, since those values are not set in the Layout + if (_segmentType == llvm::ELF::PT_LOAD) + return; + // The size is the difference of the + // last section to the first section, especially for TLS because + // the TLS segment contains both .tdata/.tbss + this->setFileOffset(_sections.front()->fileOffset()); + this->setVirtualAddr(_sections.front()->virtualAddr()); + size_t startFileOffset = _sections.front()->fileOffset(); + size_t startAddr = _sections.front()->virtualAddr(); + for (auto ai : _sections) { + this->_fsize = ai->fileOffset() + ai->fileSize() - startFileOffset; + this->_msize = ai->virtualAddr() + ai->memSize() - startAddr; + } + } + + // For LLVM RTTI + static bool classof(const Chunk<ELFT> *c) { + return c->kind() == Chunk<ELFT>::Kind::ELFSegment; + } + + // Getters + int32_t sectionCount() const { return _sections.size(); } + + /// \brief, this function returns the type of segment (PT_*) + Layout::SegmentType segmentType() { return _segmentType; } + + /// \brief return the segment type depending on the content, + /// If the content corresponds to Code, this will return Segment::Code + /// If the content corresponds to Data, this will return Segment::Data + /// If the content corresponds to TLS, this will return Segment::TLS + virtual int getContentType() const { + int64_t fl = flags(); + switch (_segmentType) { + case llvm::ELF::PT_LOAD: { + if (fl && llvm::ELF::PF_X) + return Chunk<ELFT>::ContentType::Code; + if (fl && llvm::ELF::PF_W) + return Chunk<ELFT>::ContentType::Data; + } + case llvm::ELF::PT_TLS: + return Chunk<ELFT>::ContentType::TLS; + case llvm::ELF::PT_NOTE: + return Chunk<ELFT>::ContentType::Note; + default: + return Chunk<ELFT>::ContentType::Unknown; + } + } + + int pageSize() const { return this->_context.getPageSize(); } + + int rawflags() const { return _atomflags; } + + int64_t atomflags() const { + switch (_atomflags) { + + case DefinedAtom::permUnknown: + return permUnknown; + + case DefinedAtom::permRWX: + return permRWX; + + case DefinedAtom::permR_X: + return permRX; + + case DefinedAtom::permR__: + return permR; + + case DefinedAtom::permRW_L: + return permRWL; + + case DefinedAtom::permRW_: + return permRW; + + case DefinedAtom::perm___: + default: + return permNonAccess; + } + } + + int64_t numSlices() const { return _segmentSlices.size(); } + + range<SliceIter> slices() { return _segmentSlices; } + + Chunk<ELFT> *firstSection() { return _sections[0]; } + +private: + + /// \brief Check if the chunk needs to be aligned + bool needAlign(Chunk<ELFT> *chunk) const { + if (chunk->getContentType() == Chunk<ELFT>::ContentType::Data && + _outputMagic == ELFLinkingContext::OutputMagic::NMAGIC) + return true; + return false; + } + + // Cached value of outputMagic + ELFLinkingContext::OutputMagic _outputMagic; + +protected: + /// \brief Section or some other chunk type. + std::vector<Chunk<ELFT> *> _sections; + std::vector<SegmentSlice<ELFT> *> _segmentSlices; + Layout::SegmentType _segmentType; + uint64_t _flags; + int64_t _atomflags; + llvm::BumpPtrAllocator _segmentAllocate; +}; + +/// This chunk represents a linker script expression that needs to be calculated +/// at the time the virtual addresses for the parent segment are being assigned. +template <class ELFT> class ExpressionChunk : public Chunk<ELFT> { +public: + ExpressionChunk(ELFLinkingContext &ctx, const script::SymbolAssignment *expr) + : Chunk<ELFT>(StringRef(), Chunk<ELFT>::Kind::Expression, ctx), + _expr(expr), _linkerScriptSema(ctx.linkerScriptSema()) { + this->_alignment = 1; + } + + static bool classof(const Chunk<ELFT> *c) { + return c->kind() == Chunk<ELFT>::Kind::Expression; + } + + int getContentType() const override { + return Chunk<ELFT>::ContentType::Unknown; + } + void write(ELFWriter *, TargetLayout<ELFT> &, + llvm::FileOutputBuffer &) override {} + void doPreFlight() override {} + void finalize() override {} + + std::error_code evalExpr(uint64_t &curPos) { + return _linkerScriptSema.evalExpr(_expr, curPos); + } + +private: + const script::SymbolAssignment *_expr; + script::Sema &_linkerScriptSema; +}; + +/// \brief A Program Header segment contains a set of chunks instead of sections +/// The segment doesn't contain any slice +template <class ELFT> class ProgramHeaderSegment : public Segment<ELFT> { +public: + ProgramHeaderSegment(const ELFLinkingContext &context) + : Segment<ELFT>(context, "PHDR", llvm::ELF::PT_PHDR) { + this->_alignment = 8; + this->_flags = (llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR); + } + + /// Finalize the segment, before we want to write the segment header + /// information + void finalize() { + // If the segment is of type Program Header, then the values fileOffset + // and the fileSize need to be picked up from the last section, the first + // section points to the ELF header and the second chunk points to the + // actual program headers + this->setFileOffset(this->_sections.back()->fileOffset()); + this->setVirtualAddr(this->_sections.back()->virtualAddr()); + this->_fsize = this->_sections.back()->fileSize(); + this->_msize = this->_sections.back()->memSize(); + } + +}; + +template <class ELFT> +Segment<ELFT>::Segment(const ELFLinkingContext &context, StringRef name, + const Layout::SegmentType type) + : Chunk<ELFT>(name, Chunk<ELFT>::Kind::ELFSegment, context), + _segmentType(type), _flags(0), _atomflags(0) { + this->_alignment = 0; + this->_fsize = 0; + _outputMagic = context.getOutputMagic(); +} + +// This function actually is used, but not in all instantiations of Segment. +LLVM_ATTRIBUTE_UNUSED +static DefinedAtom::ContentPermissions toAtomPerms(uint64_t flags) { + switch (flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR)) { + case SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR: + return DefinedAtom::permRWX; + case SHF_ALLOC | SHF_EXECINSTR: + return DefinedAtom::permR_X; + case SHF_ALLOC: + return DefinedAtom::permR__; + case SHF_ALLOC | SHF_WRITE: + return DefinedAtom::permRW_; + default: + return DefinedAtom::permUnknown; + } +} + +template <class ELFT> void Segment<ELFT>::append(Chunk<ELFT> *chunk) { + _sections.push_back(chunk); + Section<ELFT> *section = dyn_cast<Section<ELFT>>(chunk); + if (!section) + return; + if (_flags < section->getFlags()) + _flags |= section->getFlags(); + if (_atomflags < toAtomPerms(_flags)) + _atomflags = toAtomPerms(_flags); + if (this->_alignment < section->alignment()) + this->_alignment = section->alignment(); +} + +template <class ELFT> +bool Segment<ELFT>::compareSegments(Segment<ELFT> *sega, Segment<ELFT> *segb) { + int64_t type1 = sega->segmentType(); + int64_t type2 = segb->segmentType(); + + if (type1 == type2) + return sega->atomflags() < segb->atomflags(); + + // The single PT_PHDR segment is required to precede any loadable + // segment. We simply make it always first. + if (type1 == llvm::ELF::PT_PHDR) + return true; + if (type2 == llvm::ELF::PT_PHDR) + return false; + + // The single PT_INTERP segment is required to precede any loadable + // segment. We simply make it always second. + if (type1 == llvm::ELF::PT_INTERP) + return true; + if (type2 == llvm::ELF::PT_INTERP) + return false; + + // We then put PT_LOAD segments before any other segments. + if (type1 == llvm::ELF::PT_LOAD) + return true; + if (type2 == llvm::ELF::PT_LOAD) + return false; + + // We put the PT_GNU_RELRO segment last, because that is where the + // dynamic linker expects to find it + if (type1 == llvm::ELF::PT_GNU_RELRO) + return false; + if (type2 == llvm::ELF::PT_GNU_RELRO) + return true; + + // We put the PT_TLS segment last except for the PT_GNU_RELRO + // segment, because that is where the dynamic linker expects to find + if (type1 == llvm::ELF::PT_TLS) + return false; + if (type2 == llvm::ELF::PT_TLS) + return true; + + // Otherwise compare the types to establish an arbitrary ordering. + // FIXME: Should figure out if we should just make all other types compare + // equal, but if so, we should probably do the same for atom flags and change + // users of this to use stable_sort. + return type1 < type2; +} + +template <class ELFT> +void Segment<ELFT>::assignFileOffsets(uint64_t startOffset) { + uint64_t fileOffset = startOffset; + uint64_t curSliceFileOffset = fileOffset; + bool isDataPageAlignedForNMagic = false; + bool alignSegments = this->_context.alignSegments(); + uint64_t p_align = this->_context.getPageSize(); + uint64_t lastVirtualAddress = 0; + + this->setFileOffset(startOffset); + for (auto &slice : slices()) { + bool isFirstSection = true; + for (auto section : slice->sections()) { + // Handle linker script expressions, which may change the offset + if (!isFirstSection) + if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(section)) + fileOffset += expr->virtualAddr() - lastVirtualAddress; + // Align fileoffset to the alignment of the section. + fileOffset = llvm::RoundUpToAlignment(fileOffset, section->alignment()); + // If the linker outputmagic is set to OutputMagic::NMAGIC, align the Data + // to a page boundary + if (isFirstSection && + _outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && + _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) { + // Align to a page only if the output is not + // OutputMagic::NMAGIC/OutputMagic::OMAGIC + if (alignSegments) + fileOffset = llvm::RoundUpToAlignment(fileOffset, p_align); + else { + // Align according to ELF spec. + // in p75, http://www.sco.com/developers/devspecs/gabi41.pdf + uint64_t virtualAddress = slice->virtualAddr(); + Section<ELFT> *sect = dyn_cast<Section<ELFT>>(section); + if (sect && sect->isLoadableSection() && + ((virtualAddress & (p_align - 1)) != + (fileOffset & (p_align - 1)))) + fileOffset = llvm::RoundUpToAlignment(fileOffset, p_align) + + (virtualAddress % p_align); + } + } else if (!isDataPageAlignedForNMagic && needAlign(section)) { + fileOffset = + llvm::RoundUpToAlignment(fileOffset, this->_context.getPageSize()); + isDataPageAlignedForNMagic = true; + } + if (isFirstSection) { + slice->setFileOffset(fileOffset); + isFirstSection = false; + curSliceFileOffset = fileOffset; + } + section->setFileOffset(fileOffset); + fileOffset += section->fileSize(); + lastVirtualAddress = section->virtualAddr() + section->memSize(); + } + slice->setFileSize(fileOffset - curSliceFileOffset); + } + this->setFileSize(fileOffset - startOffset); +} + +/// \brief Assign virtual addresses to the slices +template <class ELFT> void Segment<ELFT>::assignVirtualAddress(uint64_t addr) { + int startSection = 0; + int currSection = 0; + SectionIter startSectionIter; + + // slice align is set to the max alignment of the chunks that are + // contained in the slice + uint64_t sliceAlign = 0; + // Current slice size + uint64_t curSliceSize = 0; + // Current Slice File Offset + uint64_t curSliceAddress = 0; + + startSectionIter = _sections.begin(); + startSection = 0; + bool isFirstSection = true; + bool isDataPageAlignedForNMagic = false; + uint64_t startAddr = addr; + SegmentSlice<ELFT> *slice = nullptr; + uint64_t tlsStartAddr = 0; + bool alignSegments = this->_context.alignSegments(); + StringRef prevOutputSectionName = StringRef(); + + for (auto si = _sections.begin(); si != _sections.end(); ++si) { + // If this is first section in the segment, page align the section start + // address. The linker needs to align the data section to a page boundary + // only if NMAGIC is set. + if (isFirstSection) { + isFirstSection = false; + if (alignSegments && + _outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && + _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) + // Align to a page only if the output is not + // OutputMagic::NMAGIC/OutputMagic::OMAGIC + startAddr = + llvm::RoundUpToAlignment(startAddr, this->_context.getPageSize()); + else if (!isDataPageAlignedForNMagic && needAlign(*si)) { + // If the linker outputmagic is set to OutputMagic::NMAGIC, align the + // Data to a page boundary. + startAddr = + llvm::RoundUpToAlignment(startAddr, this->_context.getPageSize()); + isDataPageAlignedForNMagic = true; + } + // align the startOffset to the section alignment + uint64_t newAddr = llvm::RoundUpToAlignment(startAddr, (*si)->alignment()); + // Handle linker script expressions, which *may update newAddr* if the + // expression assigns to "." + if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(*si)) + expr->evalExpr(newAddr); + curSliceAddress = newAddr; + sliceAlign = (*si)->alignment(); + (*si)->setVirtualAddr(curSliceAddress); + + // Handle TLS. + if (auto section = dyn_cast<Section<ELFT>>(*si)) { + if (section->getSegmentType() == llvm::ELF::PT_TLS) { + tlsStartAddr = + llvm::RoundUpToAlignment(tlsStartAddr, (*si)->alignment()); + section->assignVirtualAddress(tlsStartAddr); + tlsStartAddr += (*si)->memSize(); + } else { + section->assignVirtualAddress(newAddr); + } + } + // TBSS section is special in that it doesn't contribute to memory of any + // segment. If we see a tbss section, don't add memory size to addr The + // fileOffset is automatically taken care of since TBSS section does not + // end up using file size + if ((*si)->order() != DefaultLayout<ELFT>::ORDER_TBSS) + curSliceSize = (*si)->memSize(); + } else { + uint64_t curAddr = curSliceAddress + curSliceSize; + if (!isDataPageAlignedForNMagic && needAlign(*si)) { + // If the linker outputmagic is set to OutputMagic::NMAGIC, align the + // Data + // to a page boundary + curAddr = + llvm::RoundUpToAlignment(curAddr, this->_context.getPageSize()); + isDataPageAlignedForNMagic = true; + } + uint64_t newAddr = llvm::RoundUpToAlignment(curAddr, (*si)->alignment()); + // Handle linker script expressions, which *may update newAddr* if the + // expression assigns to "." + if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(*si)) + expr->evalExpr(newAddr); + Section<ELFT> *sec = dyn_cast<Section<ELFT>>(*si); + StringRef curOutputSectionName; + if (sec) + curOutputSectionName = sec->outputSectionName(); + else { + // If this is a linker script expression, propagate the name of the + // previous section instead + if (isa<ExpressionChunk<ELFT>>(*si)) + curOutputSectionName = prevOutputSectionName; + else + curOutputSectionName = (*si)->name(); + } + bool autoCreateSlice = true; + if (curOutputSectionName == prevOutputSectionName) + autoCreateSlice = false; + // If the newAddress computed is more than a page away, let's create + // a separate segment, so that memory is not used up while running. + // Dont create a slice, if the new section falls in the same output + // section as the previous section. + if (autoCreateSlice && + ((newAddr - curAddr) > this->_context.getPageSize()) && + (_outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && + _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC)) { + auto sliceIter = + std::find_if(_segmentSlices.begin(), _segmentSlices.end(), + [startSection](SegmentSlice<ELFT> *s) -> bool { + return s->startSection() == startSection; + }); + if (sliceIter == _segmentSlices.end()) { + slice = new (_segmentAllocate.Allocate<SegmentSlice<ELFT>>()) + SegmentSlice<ELFT>(); + _segmentSlices.push_back(slice); + } else { + slice = (*sliceIter); + } + slice->setStart(startSection); + slice->setSections(make_range(startSectionIter, si)); + slice->setMemSize(curSliceSize); + slice->setAlign(sliceAlign); + slice->setVirtualAddr(curSliceAddress); + // Start new slice + curSliceAddress = newAddr; + (*si)->setVirtualAddr(curSliceAddress); + startSectionIter = si; + startSection = currSection; + if (auto section = dyn_cast<Section<ELFT>>(*si)) + section->assignVirtualAddress(newAddr); + curSliceSize = newAddr - curSliceAddress + (*si)->memSize(); + sliceAlign = (*si)->alignment(); + } else { + if (sliceAlign < (*si)->alignment()) + sliceAlign = (*si)->alignment(); + (*si)->setVirtualAddr(newAddr); + // Handle TLS. + if (auto section = dyn_cast<Section<ELFT>>(*si)) { + if (section->getSegmentType() == llvm::ELF::PT_TLS) { + tlsStartAddr = + llvm::RoundUpToAlignment(tlsStartAddr, (*si)->alignment()); + section->assignVirtualAddress(tlsStartAddr); + tlsStartAddr += (*si)->memSize(); + } else { + section->assignVirtualAddress(newAddr); + } + } + // TBSS section is special in that it doesn't contribute to memory of + // any segment. If we see a tbss section, don't add memory size to addr + // The fileOffset is automatically taken care of since TBSS section does + // not end up using file size. + if ((*si)->order() != DefaultLayout<ELFT>::ORDER_TBSS) + curSliceSize = newAddr - curSliceAddress + (*si)->memSize(); + else + curSliceSize = newAddr - curSliceAddress; + } + prevOutputSectionName = curOutputSectionName; + } + currSection++; + } + auto sliceIter = std::find_if(_segmentSlices.begin(), _segmentSlices.end(), + [startSection](SegmentSlice<ELFT> *s) -> bool { + return s->startSection() == startSection; + }); + if (sliceIter == _segmentSlices.end()) { + slice = new (_segmentAllocate.Allocate<SegmentSlice<ELFT>>()) + SegmentSlice<ELFT>(); + _segmentSlices.push_back(slice); + } else { + slice = (*sliceIter); + } + slice->setStart(startSection); + slice->setVirtualAddr(curSliceAddress); + slice->setMemSize(curSliceSize); + slice->setSections(make_range(startSectionIter, _sections.end())); + slice->setAlign(sliceAlign); + + // Set the segment memory size and the virtual address. + this->setMemSize(curSliceAddress - startAddr + curSliceSize); + this->setVirtualAddr(curSliceAddress); + std::stable_sort(_segmentSlices.begin(), _segmentSlices.end(), + SegmentSlice<ELFT>::compare_slices); +} + +// Write the Segment +template <class ELFT> +void Segment<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + for (auto slice : slices()) + for (auto section : slice->sections()) + section->write(writer, layout, buffer); +} + +template<class ELFT> +int64_t +Segment<ELFT>::flags() const { + int64_t fl = 0; + if (_flags & llvm::ELF::SHF_ALLOC) + fl |= llvm::ELF::PF_R; + if (_flags & llvm::ELF::SHF_WRITE) + fl |= llvm::ELF::PF_W; + if (_flags & llvm::ELF::SHF_EXECINSTR) + fl |= llvm::ELF::PF_X; + return fl; +} +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/TODO.txt b/lib/ReaderWriter/ELF/TODO.txt new file mode 100644 index 0000000000000..90c334b781ba1 --- /dev/null +++ b/lib/ReaderWriter/ELF/TODO.txt @@ -0,0 +1,18 @@ +lib/ReaderWriter/ELF +~~~~~~~~~~~~~~~~~~~~ + +- Implement processing of DT_NEEDED elements including -rpath-link / + -rpath processing. + +- _GLOBAL_OFFSET_TABLE should be hidden and normally dropped from the output. + +- Merge SHT_NOTE sections only if applicable. + +- Do not create __got_* / __plt_* symbol table entries by default. + +- Weak references to symbols defined in a DSO should remain weak. + +- Fix section flags as they appear in input (update content permissions) + +- Check for errors in the ELFReader when creating atoms for LinkOnce + sections/Group sections. Add tests to account for the change when it happens. diff --git a/lib/ReaderWriter/ELF/TargetHandler.h b/lib/ReaderWriter/ELF/TargetHandler.h new file mode 100644 index 0000000000000..ca7a442276d18 --- /dev/null +++ b/lib/ReaderWriter/ELF/TargetHandler.h @@ -0,0 +1,86 @@ +//===- lib/ReaderWriter/ELF/TargetHandler.h -------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief These interfaces provide target specific hooks to change the linker's +/// behaivor. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_ELF_TARGET_HANDLER_H +#define LLD_READER_WRITER_ELF_TARGET_HANDLER_H + +#include "Layout.h" +#include "lld/Core/Atom.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/LinkingContext.h" +#include "lld/Core/STDExtras.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileOutputBuffer.h" +#include <memory> +#include <vector> + +namespace lld { +namespace elf { +template <class ELFT> class DynamicTable; +template <class ELFT> class DynamicSymbolTable; +template <class ELFT> class ELFDefinedAtom; +template <class ELFT> class ELFReference; +class ELFWriter; +template <class ELFT> class ELFHeader; +template <class ELFT> class Section; +template <class ELFT> class TargetLayout; + +class TargetRelocationHandler { +public: + /// Constructor + TargetRelocationHandler() {} + virtual ~TargetRelocationHandler() {} + + virtual std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, + const lld::AtomLayout &, + const Reference &) const = 0; +}; + +/// \brief TargetHandler contains all the information responsible to handle a +/// a particular target on ELF. A target might wish to override implementation +/// of creating atoms and how the atoms are written to the output file. +template <class ELFT> class TargetHandler : public TargetHandlerBase { +public: + /// The layout determined completely by the Target. + virtual TargetLayout<ELFT> &getTargetLayout() = 0; + + /// Determine how relocations need to be applied. + virtual const TargetRelocationHandler &getRelocationHandler() const = 0; + + /// How does the target deal with reading input files. + virtual std::unique_ptr<Reader> getObjReader() = 0; + + /// How does the target deal with reading dynamic libraries. + virtual std::unique_ptr<Reader> getDSOReader() = 0; + + /// How does the target deal with writing ELF output. + virtual std::unique_ptr<Writer> getWriter() = 0; +}; + +inline std::error_code make_unhandled_reloc_error() { + return make_dynamic_error_code(Twine("Unhandled reference type")); +} + +inline std::error_code make_out_of_range_reloc_error() { + return make_dynamic_error_code(Twine("Relocation out of range")); +} + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/TargetLayout.h b/lib/ReaderWriter/ELF/TargetLayout.h new file mode 100644 index 0000000000000..ab7a7890a2747 --- /dev/null +++ b/lib/ReaderWriter/ELF/TargetLayout.h @@ -0,0 +1,28 @@ +//===- lib/ReaderWriter/ELF/TargetLayout.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_TARGET_LAYOUT_H +#define LLD_READER_WRITER_ELF_TARGET_LAYOUT_H + +#include "DefaultLayout.h" +#include "lld/Core/LLVM.h" + +namespace lld { +namespace elf { +/// \brief The target can override certain functions in the DefaultLayout +/// class so that the order, the name of the section and the segment type could +/// be changed in the final layout +template <class ELFT> class TargetLayout : public DefaultLayout<ELFT> { +public: + TargetLayout(ELFLinkingContext &context) : DefaultLayout<ELFT>(context) {} +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Writer.cpp b/lib/ReaderWriter/ELF/Writer.cpp new file mode 100644 index 0000000000000..3071827e07d07 --- /dev/null +++ b/lib/ReaderWriter/ELF/Writer.cpp @@ -0,0 +1,23 @@ +//===- lib/ReaderWriter/ELF/WriterELF.cpp ---------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lld/Core/Writer.h" +#include "DynamicLibraryWriter.h" +#include "ExecutableWriter.h" + +using namespace llvm; +using namespace llvm::object; + +namespace lld { + +std::unique_ptr<Writer> createWriterELF(TargetHandlerBase *handler) { + return std::move(handler->getWriter()); +} + +} // namespace lld diff --git a/lib/ReaderWriter/ELF/Writer.h b/lib/ReaderWriter/ELF/Writer.h new file mode 100644 index 0000000000000..1e819467c558f --- /dev/null +++ b/lib/ReaderWriter/ELF/Writer.h @@ -0,0 +1,38 @@ +//===- lib/ReaderWriter/ELF/Writer.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_WRITER_H +#define LLD_READER_WRITER_ELF_WRITER_H + +#include "lld/Core/File.h" +#include "lld/Core/Writer.h" + +namespace lld { +namespace elf { +/// \brief The Writer class is a base class for the linker to write +/// various kinds of ELF files. +class ELFWriter : public Writer { +public: + ELFWriter() { } + +public: + /// \brief builds the chunks that needs to be written to the output + /// ELF file + virtual void buildChunks(const File &file) = 0; + + /// \brief Writes the chunks into the output file specified by path + virtual std::error_code writeFile(const File &file, StringRef path) = 0; + + /// \brief Get the virtual address of \p atom after layout. + virtual uint64_t addressOfAtom(const Atom *atom) = 0; +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/X86/CMakeLists.txt b/lib/ReaderWriter/ELF/X86/CMakeLists.txt new file mode 100644 index 0000000000000..191f7ab3d61dc --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/CMakeLists.txt @@ -0,0 +1,11 @@ +add_llvm_library(lldX86ELFTarget + X86LinkingContext.cpp + X86TargetHandler.cpp + X86RelocationHandler.cpp + LINK_LIBS + lldELF + lldReaderWriter + lldCore + LLVMObject + LLVMSupport + ) diff --git a/lib/ReaderWriter/ELF/X86/Makefile b/lib/ReaderWriter/ELF/X86/Makefile new file mode 100644 index 0000000000000..058d5133eaba2 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/Makefile @@ -0,0 +1,15 @@ +##===- lld/lib/ReaderWriter/ELF/X86/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 := lldX86ELFTarget +USEDLIBS = lldCore.a +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/X86/X86DynamicLibraryWriter.h b/lib/ReaderWriter/ELF/X86/X86DynamicLibraryWriter.h new file mode 100644 index 0000000000000..86376295bec43 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86DynamicLibraryWriter.h @@ -0,0 +1,67 @@ +//===- lib/ReaderWriter/ELF/X86/X86DynamicLibraryWriter.h -----------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef X86_X86_DYNAMIC_LIBRARY_WRITER_H +#define X86_X86_DYNAMIC_LIBRARY_WRITER_H + +#include "DynamicLibraryWriter.h" +#include "X86LinkingContext.h" + +namespace lld { +namespace elf { + +template <class ELFT> +class X86DynamicLibraryWriter : public DynamicLibraryWriter<ELFT> { +public: + X86DynamicLibraryWriter(X86LinkingContext &context, + X86TargetLayout<ELFT> &layout); + +protected: + // Add any runtime files and their atoms to the output + virtual bool createImplicitFiles(std::vector<std::unique_ptr<File>> &); + + virtual void finalizeDefaultAtomValues() { + return DynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues(); + } + + virtual void addDefaultAtoms() { + return DynamicLibraryWriter<ELFT>::addDefaultAtoms(); + } + +private: + class GOTFile : public SimpleFile { + public: + GOTFile(const ELFLinkingContext &eti) : SimpleFile("GOTFile") {} + llvm::BumpPtrAllocator _alloc; + }; + + std::unique_ptr<GOTFile> _gotFile; + X86LinkingContext &_context; + X86TargetLayout<ELFT> &_x86Layout; +}; + +template <class ELFT> +X86DynamicLibraryWriter<ELFT>::X86DynamicLibraryWriter( + X86LinkingContext &context, X86TargetLayout<ELFT> &layout) + : DynamicLibraryWriter<ELFT>(context, layout), + _gotFile(new GOTFile(context)), _context(context), _x86Layout(layout) {} + +template <class ELFT> +bool X86DynamicLibraryWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + DynamicLibraryWriter<ELFT>::createImplicitFiles(result); + _gotFile->addAtom(*new (_gotFile->_alloc) GLOBAL_OFFSET_TABLEAtom(*_gotFile)); + _gotFile->addAtom(*new (_gotFile->_alloc) DYNAMICAtom(*_gotFile)); + result.push_back(std::move(_gotFile)); + return true; +} + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/X86/X86ELFFile.h b/lib/ReaderWriter/ELF/X86/X86ELFFile.h new file mode 100644 index 0000000000000..621c38c435054 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86ELFFile.h @@ -0,0 +1,41 @@ +//===- lib/ReaderWriter/ELF/X86/X86ELFFile.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_X86_X86_ELF_FILE_H +#define LLD_READER_WRITER_ELF_X86_X86_ELF_FILE_H + +#include "ELFReader.h" + +namespace lld { +namespace elf { + +class X86LinkingContext; + +template <class ELFT> class X86ELFFile : public ELFFile<ELFT> { +public: + X86ELFFile(std::unique_ptr<MemoryBuffer> mb, X86LinkingContext &ctx) + : ELFFile<ELFT>(std::move(mb), ctx) {} + + static ErrorOr<std::unique_ptr<X86ELFFile>> + create(std::unique_ptr<MemoryBuffer> mb, X86LinkingContext &ctx) { + return std::unique_ptr<X86ELFFile<ELFT>>( + new X86ELFFile<ELFT>(std::move(mb), ctx)); + } +}; + +template <class ELFT> class X86DynamicFile : public DynamicFile<ELFT> { +public: + X86DynamicFile(const X86LinkingContext &context, StringRef name) + : DynamicFile<ELFT>(context, name) {} +}; + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_X86_X86_ELF_FILE_H diff --git a/lib/ReaderWriter/ELF/X86/X86ELFReader.h b/lib/ReaderWriter/ELF/X86/X86ELFReader.h new file mode 100644 index 0000000000000..96186c5eb0248 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86ELFReader.h @@ -0,0 +1,62 @@ +//===- lib/ReaderWriter/ELF/X86/X86ELFReader.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_X86_X86_ELF_READER_H +#define LLD_READER_WRITER_X86_X86_ELF_READER_H + +#include "ELFReader.h" +#include "X86ELFFile.h" + +namespace lld { +namespace elf { + +typedef llvm::object::ELFType<llvm::support::little, 2, false> X86ELFType; + +struct X86DynamicFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + X86LinkingContext &ctx) { + return lld::elf::X86DynamicFile<ELFT>::create(std::move(mb), ctx); + } +}; + +struct X86ELFFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::File>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + X86LinkingContext &ctx) { + return lld::elf::X86ELFFile<ELFT>::create(std::move(mb), ctx); + } +}; + +class X86ELFObjectReader + : public ELFObjectReader<X86ELFType, X86ELFFileCreateELFTraits, + X86LinkingContext> { +public: + X86ELFObjectReader(X86LinkingContext &ctx) + : ELFObjectReader<X86ELFType, X86ELFFileCreateELFTraits, + X86LinkingContext>(ctx, llvm::ELF::EM_386) {} +}; + +class X86ELFDSOReader + : public ELFDSOReader<X86ELFType, X86DynamicFileCreateELFTraits, + X86LinkingContext> { +public: + X86ELFDSOReader(X86LinkingContext &ctx) + : ELFDSOReader<X86ELFType, X86DynamicFileCreateELFTraits, + X86LinkingContext>(ctx, llvm::ELF::EM_386) {} +}; + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_X86_X86_ELF_READER_H diff --git a/lib/ReaderWriter/ELF/X86/X86ExecutableWriter.h b/lib/ReaderWriter/ELF/X86/X86ExecutableWriter.h new file mode 100644 index 0000000000000..68acc06c2261e --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86ExecutableWriter.h @@ -0,0 +1,57 @@ +//===- lib/ReaderWriter/ELF/X86/X86ExecutableWriter.h ---------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef X86_X86_EXECUTABLE_WRITER_H +#define X86_X86_EXECUTABLE_WRITER_H + +#include "ExecutableWriter.h" +#include "X86LinkingContext.h" + +namespace lld { +namespace elf { + +template <class ELFT> +class X86ExecutableWriter : public ExecutableWriter<ELFT> { +public: + X86ExecutableWriter(X86LinkingContext &context, + X86TargetLayout<ELFT> &layout); + +protected: + // Add any runtime files and their atoms to the output + virtual bool createImplicitFiles(std::vector<std::unique_ptr<File>> &); + + virtual void finalizeDefaultAtomValues() { + return ExecutableWriter<ELFT>::finalizeDefaultAtomValues(); + } + + virtual void addDefaultAtoms() { + return ExecutableWriter<ELFT>::addDefaultAtoms(); + } + +private: + X86LinkingContext &_context; + X86TargetLayout<ELFT> &_x86Layout; +}; + +template <class ELFT> +X86ExecutableWriter<ELFT>::X86ExecutableWriter(X86LinkingContext &context, + X86TargetLayout<ELFT> &layout) + : ExecutableWriter<ELFT>(context, layout), _context(context), + _x86Layout(layout) {} + +template <class ELFT> +bool X86ExecutableWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + ExecutableWriter<ELFT>::createImplicitFiles(result); + return true; +} + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/X86/X86LinkingContext.cpp b/lib/ReaderWriter/ELF/X86/X86LinkingContext.cpp new file mode 100644 index 0000000000000..26d715cf29534 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86LinkingContext.cpp @@ -0,0 +1,28 @@ +//===- lib/ReaderWriter/ELF/X86/X86LinkingContext.cpp ---------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "X86LinkingContext.h" +#include "X86TargetHandler.h" +#include "lld/Core/LLVM.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/ErrorOr.h" + +using namespace lld; + +std::unique_ptr<ELFLinkingContext> +elf::X86LinkingContext::create(llvm::Triple triple) { + if (triple.getArch() == llvm::Triple::x86) + return std::unique_ptr<ELFLinkingContext>( + new elf::X86LinkingContext(triple)); + return nullptr; +} + +elf::X86LinkingContext::X86LinkingContext(llvm::Triple triple) + : ELFLinkingContext(triple, std::unique_ptr<TargetHandlerBase>( + new X86TargetHandler(*this))) {} diff --git a/lib/ReaderWriter/ELF/X86/X86LinkingContext.h b/lib/ReaderWriter/ELF/X86/X86LinkingContext.h new file mode 100644 index 0000000000000..ff424f411aae5 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86LinkingContext.h @@ -0,0 +1,42 @@ +//===- lib/ReaderWriter/ELF/X86/X86LinkingContext.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_X86_TARGETINFO_H +#define LLD_READER_WRITER_ELF_X86_TARGETINFO_H + +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { +class X86LinkingContext final : public ELFLinkingContext { +public: + static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + X86LinkingContext(llvm::Triple); + + /// \brief X86 has only two relative relocation + /// a) for supporting IFUNC relocs - R_386_IRELATIVE + /// b) for supporting relative relocs - R_386_RELATIVE + bool isRelativeReloc(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::x86); + switch (r.kindValue()) { + case llvm::ELF::R_386_IRELATIVE: + case llvm::ELF::R_386_RELATIVE: + return true; + default: + return false; + } + } +}; +} // end namespace elf +} // end namespace lld +#endif diff --git a/lib/ReaderWriter/ELF/X86/X86RelocationHandler.cpp b/lib/ReaderWriter/ELF/X86/X86RelocationHandler.cpp new file mode 100644 index 0000000000000..da5a24c6ec37d --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86RelocationHandler.cpp @@ -0,0 +1,57 @@ +//===- lib/ReaderWriter/ELF/X86/X86RelocationHandler.cpp ------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "X86LinkingContext.h" +#include "X86TargetHandler.h" +#include "llvm/Support/Endian.h" + +using namespace lld; +using namespace lld::elf; +using namespace llvm::support::endian; + +namespace { +/// \brief R_386_32 - word32: S + A +static int reloc32(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { + int32_t result = (uint32_t)(S + A); + write32le(location, result | read32le(location)); + return 0; +} + +/// \brief R_386_PC32 - word32: S + A - P +static int relocPC32(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { + uint32_t result = (uint32_t)((S + A) - P); + write32le(location, result + read32le(location)); + return 0; +} +} + +std::error_code X86TargetRelocationHandler::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); + switch (ref.kindValue()) { + case R_386_32: + reloc32(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_386_PC32: + relocPC32(location, relocVAddress, targetVAddress, ref.addend()); + break; + default: + return make_unhandled_reloc_error(); + } + + return std::error_code(); +} diff --git a/lib/ReaderWriter/ELF/X86/X86RelocationHandler.h b/lib/ReaderWriter/ELF/X86/X86RelocationHandler.h new file mode 100644 index 0000000000000..f161cdd55983d --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86RelocationHandler.h @@ -0,0 +1,29 @@ +//===- lib/ReaderWriter/ELF/X86/X86RelocationHandler.h --------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef X86_X86_RELOCATION_HANDLER_H +#define X86_X86_RELOCATION_HANDLER_H + +#include "X86TargetHandler.h" + +namespace lld { +namespace elf { +typedef llvm::object::ELFType<llvm::support::little, 2, false> X86ELFType; + +class X86TargetRelocationHandler final : public TargetRelocationHandler { +public: + std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, + const lld::AtomLayout &, + const Reference &) const override; +}; + +} // end namespace elf +} // end namespace lld + +#endif // X86_X86_RELOCATION_HANDLER_H diff --git a/lib/ReaderWriter/ELF/X86/X86TargetHandler.cpp b/lib/ReaderWriter/ELF/X86/X86TargetHandler.cpp new file mode 100644 index 0000000000000..22d9182314248 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86TargetHandler.cpp @@ -0,0 +1,53 @@ +//===- lib/ReaderWriter/ELF/X86/X86TargetHandler.cpp ----------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "X86TargetHandler.h" +#include "X86DynamicLibraryWriter.h" +#include "X86ExecutableWriter.h" +#include "X86LinkingContext.h" +#include "X86RelocationHandler.h" + +using namespace lld; +using namespace elf; + +using namespace llvm::ELF; + +std::unique_ptr<Writer> X86TargetHandler::getWriter() { + switch (_x86LinkingContext.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + return std::unique_ptr<Writer>(new X86ExecutableWriter<X86ELFType>( + _x86LinkingContext, *_x86TargetLayout.get())); + case llvm::ELF::ET_DYN: + return std::unique_ptr<Writer>(new X86DynamicLibraryWriter<X86ELFType>( + _x86LinkingContext, *_x86TargetLayout.get())); + case llvm::ELF::ET_REL: + llvm_unreachable("TODO: support -r mode"); + default: + llvm_unreachable("unsupported output type"); + } +} + +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), + +const Registry::KindStrings X86TargetHandler::kindStrings[] = { +#include "llvm/Support/ELFRelocs/i386.def" + LLD_KIND_STRING_END +}; + +#undef ELF_RELOC + +void X86TargetHandler::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, Reference::KindArch::x86, + kindStrings); +} + +X86TargetHandler::X86TargetHandler(X86LinkingContext &context) + : _x86LinkingContext(context), + _x86TargetLayout(new X86TargetLayout<X86ELFType>(context)), + _x86RelocationHandler(new X86TargetRelocationHandler()) {} diff --git a/lib/ReaderWriter/ELF/X86/X86TargetHandler.h b/lib/ReaderWriter/ELF/X86/X86TargetHandler.h new file mode 100644 index 0000000000000..6c40267354192 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86TargetHandler.h @@ -0,0 +1,63 @@ +//===- lib/ReaderWriter/ELF/X86/X86TargetHandler.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_X86_TARGET_HANDLER_H +#define LLD_READER_WRITER_ELF_X86_TARGET_HANDLER_H + +#include "DefaultTargetHandler.h" +#include "TargetLayout.h" +#include "X86ELFFile.h" +#include "X86ELFReader.h" +#include "X86RelocationHandler.h" + +namespace lld { +namespace elf { + +class X86LinkingContext; + +template <class ELFT> class X86TargetLayout : public TargetLayout<ELFT> { +public: + X86TargetLayout(X86LinkingContext &context) : TargetLayout<ELFT>(context) {} +}; + +class X86TargetHandler final + : public DefaultTargetHandler<X86ELFType> { +public: + X86TargetHandler(X86LinkingContext &context); + + X86TargetLayout<X86ELFType> &getTargetLayout() override { + return *(_x86TargetLayout.get()); + } + + void registerRelocationNames(Registry ®istry) override; + + const X86TargetRelocationHandler &getRelocationHandler() const override { + return *(_x86RelocationHandler.get()); + } + + std::unique_ptr<Reader> getObjReader() override { + return std::unique_ptr<Reader>(new X86ELFObjectReader(_x86LinkingContext)); + } + + std::unique_ptr<Reader> getDSOReader() override { + return std::unique_ptr<Reader>(new X86ELFDSOReader(_x86LinkingContext)); + } + + std::unique_ptr<Writer> getWriter() override; + +protected: + static const Registry::KindStrings kindStrings[]; + X86LinkingContext &_x86LinkingContext; + std::unique_ptr<X86TargetLayout<X86ELFType>> _x86TargetLayout; + std::unique_ptr<X86TargetRelocationHandler> _x86RelocationHandler; +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/X86_64/CMakeLists.txt b/lib/ReaderWriter/ELF/X86_64/CMakeLists.txt new file mode 100644 index 0000000000000..a85d2b5046306 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/CMakeLists.txt @@ -0,0 +1,16 @@ +add_llvm_library(lldX86_64ELFTarget + X86_64LinkingContext.cpp + X86_64TargetHandler.cpp + X86_64RelocationHandler.cpp + X86_64RelocationPass.cpp + LINK_LIBS + lldELF + lldReaderWriter + lldCore + LLVMObject + LLVMSupport + ) + +include_directories(.) + +add_subdirectory(ExampleSubTarget) diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/CMakeLists.txt b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/CMakeLists.txt new file mode 100644 index 0000000000000..d13c98008e55f --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/CMakeLists.txt @@ -0,0 +1,11 @@ +add_llvm_library(lldExampleSubTarget + ExampleLinkingContext.cpp + ExampleTargetHandler.cpp + LINK_LIBS + lldX86_64ELFTarget + lldELF + lldReaderWriter + lldCore + LLVMObject + LLVMSupport + ) diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.cpp b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.cpp new file mode 100644 index 0000000000000..dbbb3ad3bc90d --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.cpp @@ -0,0 +1,35 @@ +//===- lib/ReaderWriter/ELF/X86_64/ExampleTarget/ExampleLinkingContext.cpp ----===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ExampleLinkingContext.h" +#include "ExampleTargetHandler.h" + +using namespace lld; +using namespace elf; + +std::unique_ptr<ELFLinkingContext> +ExampleLinkingContext::create(llvm::Triple triple) { + if (triple.getVendorName() == "example") + return llvm::make_unique<ExampleLinkingContext>(triple); + return nullptr; +} + +ExampleLinkingContext::ExampleLinkingContext(llvm::Triple triple) + : X86_64LinkingContext(triple, std::unique_ptr<TargetHandlerBase>( + new ExampleTargetHandler(*this))) { + _outputELFType = llvm::ELF::ET_LOPROC; +} + +StringRef ExampleLinkingContext::entrySymbolName() const { + return "_start"; +} + +void ExampleLinkingContext::addPasses(PassManager &p) { + ELFLinkingContext::addPasses(p); +} diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.h b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.h new file mode 100644 index 0000000000000..5bb11cd35b41e --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.h @@ -0,0 +1,31 @@ +//===- lib/ReaderWriter/ELF/X86_64/ExampleTarget/ExampleLinkingContext.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_X86_64_EXAMPLE_TARGET_EXAMPLE_LINKING_CONTEXT +#define LLD_READER_WRITER_ELF_X86_64_EXAMPLE_TARGET_EXAMPLE_LINKING_CONTEXT + +#include "X86_64LinkingContext.h" +#include "X86_64TargetHandler.h" + +namespace lld { +namespace elf { + +class ExampleLinkingContext final : public X86_64LinkingContext { +public: + static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + ExampleLinkingContext(llvm::Triple triple); + + StringRef entrySymbolName() const override; + void addPasses(PassManager &) override; +}; + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.cpp b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.cpp new file mode 100644 index 0000000000000..b66b0d903f6a4 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.cpp @@ -0,0 +1,23 @@ +//===- lib/ReaderWriter/ELF/X86_64/ExampleTarget/ExampleTargetHandler.cpp -===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ExampleTargetHandler.h" +#include "X86_64ExecutableWriter.h" +#include "ExampleLinkingContext.h" + +using namespace lld; +using namespace elf; + +ExampleTargetHandler::ExampleTargetHandler(ExampleLinkingContext &c) + : X86_64TargetHandler(c), _exampleContext(c) {} + +std::unique_ptr<Writer> ExampleTargetHandler::getWriter() { + return std::unique_ptr<Writer>( + new X86_64ExecutableWriter(_exampleContext, *_x86_64TargetLayout)); +} diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.h b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.h new file mode 100644 index 0000000000000..19a642113359f --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.h @@ -0,0 +1,31 @@ +//===- lib/ReaderWriter/ELF/X86_64/ExampleTarget/ExampleTargetHandler.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_X86_64_EXAMPLE_TARGET_EXAMPLE_TARGET_HANDLER_H +#define LLD_READER_WRITER_ELF_X86_64_EXAMPLE_TARGET_EXAMPLE_TARGET_HANDLER_H + +#include "X86_64TargetHandler.h" + +namespace lld { +namespace elf { +class ExampleLinkingContext; + +class ExampleTargetHandler final : public X86_64TargetHandler { +public: + ExampleTargetHandler(ExampleLinkingContext &c); + + std::unique_ptr<Writer> getWriter() override; + +private: + ExampleLinkingContext &_exampleContext; +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/Makefile b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/Makefile new file mode 100644 index 0000000000000..8f0b0fead1f69 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/Makefile @@ -0,0 +1,15 @@ +##===- lld/lib/ReaderWriter/ELF/X86_64/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 := lldExampleSubTarget +USEDLIBS = lldX86_64ELFTarget.a +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/X86_64/Makefile b/lib/ReaderWriter/ELF/X86_64/Makefile new file mode 100644 index 0000000000000..dbeb4d2270507 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/Makefile @@ -0,0 +1,19 @@ +##===- lld/lib/ReaderWriter/ELF/X86_64/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 := lldX86_64ELFTarget +USEDLIBS = lldCore.a + +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF/X86_64/ + +PARALLEL_DIRS := ExampleSubTarget + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/X86_64/TODO.rst b/lib/ReaderWriter/ELF/X86_64/TODO.rst new file mode 100644 index 0000000000000..a2411a00d1ea1 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/TODO.rst @@ -0,0 +1,46 @@ +ELF x86-64 +~~~~~~~~~~ + +Unimplemented Features +###################### + +* Code models other than the small code model +* TLS strength reduction + +Unimplemented Relocations +######################### + +All of these relocations are defined in: +http://www.x86-64.org/documentation/abi.pdf + +Trivial Relocs +<<<<<<<<<<<<<< + +These are very simple relocation calculations to implement. +See lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp + +* R_X86_64_8 +* R_X86_64_PC8 +* R_X86_64_SIZE32 +* R_X86_64_SIZE64 +* R_X86_64_GOTPC32 (this relocation requires there to be a __GLOBAL_OFFSET_TABLE__) + +Global Offset Table Relocs +<<<<<<<<<<<<<<<<<<<<<<<<<< + +* R_X86_64_GOTOFF32 +* R_X86_64_GOTOFF64 + +Global Dynamic Thread Local Storage Relocs +<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +These relocations take more effort to implement, but some of them are done. +Their implementation lives in lib/ReaderWriter/ELF/X86_64/{X86_64RelocationPass.cpp,X86_64RelocationHandler.cpp}. + +Documentation on these relocations can be found in: +http://www.akkadia.org/drepper/tls.pdf +http://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-x86.txt + +* R_X86_64_GOTPC32_TLSDESC +* R_X86_64_TLSDESC_CALL +* R_X86_64_TLSDESC diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64DynamicLibraryWriter.h b/lib/ReaderWriter/ELF/X86_64/X86_64DynamicLibraryWriter.h new file mode 100644 index 0000000000000..b996186115b66 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64DynamicLibraryWriter.h @@ -0,0 +1,63 @@ +//===- lib/ReaderWriter/ELF/X86/X86_64DynamicLibraryWriter.h ---------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef X86_64_DYNAMIC_LIBRARY_WRITER_H +#define X86_64_DYNAMIC_LIBRARY_WRITER_H + +#include "DynamicLibraryWriter.h" +#include "X86_64ElfType.h" +#include "X86_64LinkingContext.h" +#include "X86_64TargetHandler.h" + +namespace lld { +namespace elf { + +class X86_64DynamicLibraryWriter : public DynamicLibraryWriter<X86_64ELFType> { +public: + X86_64DynamicLibraryWriter(X86_64LinkingContext &context, + X86_64TargetLayout &layout); + +protected: + // Add any runtime files and their atoms to the output + virtual bool createImplicitFiles(std::vector<std::unique_ptr<File>> &); + + virtual void finalizeDefaultAtomValues() { + return DynamicLibraryWriter::finalizeDefaultAtomValues(); + } + + virtual void addDefaultAtoms() { + return DynamicLibraryWriter::addDefaultAtoms(); + } + +private: + class GOTFile : public SimpleFile { + public: + GOTFile(const ELFLinkingContext &eti) : SimpleFile("GOTFile") {} + llvm::BumpPtrAllocator _alloc; + }; + + std::unique_ptr<GOTFile> _gotFile; +}; + +X86_64DynamicLibraryWriter::X86_64DynamicLibraryWriter( + X86_64LinkingContext &context, X86_64TargetLayout &layout) + : DynamicLibraryWriter(context, layout), _gotFile(new GOTFile(context)) {} + +bool X86_64DynamicLibraryWriter::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + DynamicLibraryWriter::createImplicitFiles(result); + _gotFile->addAtom(*new (_gotFile->_alloc) GLOBAL_OFFSET_TABLEAtom(*_gotFile)); + _gotFile->addAtom(*new (_gotFile->_alloc) DYNAMICAtom(*_gotFile)); + result.push_back(std::move(_gotFile)); + return true; +} + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64ELFFile.h b/lib/ReaderWriter/ELF/X86_64/X86_64ELFFile.h new file mode 100644 index 0000000000000..d43840a63e7e0 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64ELFFile.h @@ -0,0 +1,41 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64ELFFile.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_X86_64_ELF_FILE_H +#define LLD_READER_WRITER_ELF_X86_64_ELF_FILE_H + +#include "ELFReader.h" + +namespace lld { +namespace elf { + +class X86_64LinkingContext; + +template <class ELFT> class X86_64ELFFile : public ELFFile<ELFT> { +public: + X86_64ELFFile(std::unique_ptr<MemoryBuffer> mb, X86_64LinkingContext &ctx) + : ELFFile<ELFT>(std::move(mb), ctx) {} + + static ErrorOr<std::unique_ptr<X86_64ELFFile>> + create(std::unique_ptr<MemoryBuffer> mb, X86_64LinkingContext &ctx) { + return std::unique_ptr<X86_64ELFFile<ELFT>>( + new X86_64ELFFile<ELFT>(std::move(mb), ctx)); + } +}; + +template <class ELFT> class X86_64DynamicFile : public DynamicFile<ELFT> { +public: + X86_64DynamicFile(const X86_64LinkingContext &context, StringRef name) + : DynamicFile<ELFT>(context, name) {} +}; + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_X86_64_ELF_FILE_H diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64ELFReader.h b/lib/ReaderWriter/ELF/X86_64/X86_64ELFReader.h new file mode 100644 index 0000000000000..9b1284c6dfa8c --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64ELFReader.h @@ -0,0 +1,62 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64ELFReader.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_X86_64_X86_64_ELF_READER_H +#define LLD_READER_WRITER_X86_64_X86_64_ELF_READER_H + +#include "ELFReader.h" +#include "X86_64ELFFile.h" + +namespace lld { +namespace elf { + +typedef llvm::object::ELFType<llvm::support::little, 2, true> X86_64ELFType; + +struct X86_64DynamicFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + X86_64LinkingContext &ctx) { + return lld::elf::X86_64DynamicFile<ELFT>::create(std::move(mb), ctx); + } +}; + +struct X86_64ELFFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::File>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + X86_64LinkingContext &ctx) { + return lld::elf::X86_64ELFFile<ELFT>::create(std::move(mb), ctx); + } +}; + +class X86_64ELFObjectReader + : public ELFObjectReader<X86_64ELFType, X86_64ELFFileCreateELFTraits, + X86_64LinkingContext> { +public: + X86_64ELFObjectReader(X86_64LinkingContext &ctx) + : ELFObjectReader<X86_64ELFType, X86_64ELFFileCreateELFTraits, + X86_64LinkingContext>(ctx, llvm::ELF::EM_X86_64) {} +}; + +class X86_64ELFDSOReader + : public ELFDSOReader<X86_64ELFType, X86_64DynamicFileCreateELFTraits, + X86_64LinkingContext> { +public: + X86_64ELFDSOReader(X86_64LinkingContext &ctx) + : ELFDSOReader<X86_64ELFType, X86_64DynamicFileCreateELFTraits, + X86_64LinkingContext>(ctx, llvm::ELF::EM_X86_64) {} +}; + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_ELF_X86_64_X86_64_READER_H diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64ElfType.h b/lib/ReaderWriter/ELF/X86_64/X86_64ElfType.h new file mode 100644 index 0000000000000..0b982e7754e21 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64ElfType.h @@ -0,0 +1,21 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64ElfType.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_X86_64_X86_64_ELF_TYPE_H +#define LLD_READER_WRITER_ELF_X86_64_X86_64_ELF_TYPE_H + +#include "llvm/Object/ELF.h" + +namespace lld { +namespace elf { +typedef llvm::object::ELFType<llvm::support::little, 2, true> X86_64ELFType; +} +} + +#endif diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64ExecutableWriter.h b/lib/ReaderWriter/ELF/X86_64/X86_64ExecutableWriter.h new file mode 100644 index 0000000000000..f549ed6dcfcbb --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64ExecutableWriter.h @@ -0,0 +1,61 @@ +//===- lib/ReaderWriter/ELF/X86/X86_64ExecutableWriter.h ------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef X86_64_EXECUTABLE_WRITER_H +#define X86_64_EXECUTABLE_WRITER_H + +#include "ExecutableWriter.h" +#include "X86_64ElfType.h" +#include "X86_64LinkingContext.h" + +namespace lld { +namespace elf { + +class X86_64ExecutableWriter : public ExecutableWriter<X86_64ELFType> { +public: + X86_64ExecutableWriter(X86_64LinkingContext &context, + X86_64TargetLayout &layout) + : ExecutableWriter(context, layout), _gotFile(new GOTFile(context)), + _context(context) {} + +protected: + // Add any runtime files and their atoms to the output + virtual bool + createImplicitFiles(std::vector<std::unique_ptr<File>> &result) { + ExecutableWriter::createImplicitFiles(result); + _gotFile->addAtom(*new (_gotFile->_alloc) + GLOBAL_OFFSET_TABLEAtom(*_gotFile)); + if (_context.isDynamic()) + _gotFile->addAtom(*new (_gotFile->_alloc) DYNAMICAtom(*_gotFile)); + result.push_back(std::move(_gotFile)); + return true; + } + + virtual void finalizeDefaultAtomValues() { + return ExecutableWriter::finalizeDefaultAtomValues(); + } + + virtual void addDefaultAtoms() { + return ExecutableWriter::addDefaultAtoms(); + } + +private: + class GOTFile : public SimpleFile { + public: + GOTFile(const ELFLinkingContext &eti) : SimpleFile("GOTFile") {} + llvm::BumpPtrAllocator _alloc; + }; + + std::unique_ptr<GOTFile> _gotFile; + X86_64LinkingContext &_context; +}; + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.cpp b/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.cpp new file mode 100644 index 0000000000000..6a8ce8bd64967 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.cpp @@ -0,0 +1,38 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.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_64RelocationPass.h" +#include "X86_64TargetHandler.h" + +using namespace lld; +using namespace elf; + +X86_64LinkingContext::X86_64LinkingContext( + llvm::Triple triple, std::unique_ptr<TargetHandlerBase> handler) + : ELFLinkingContext(triple, std::move(handler)) {} + +X86_64LinkingContext::X86_64LinkingContext(llvm::Triple triple) + : X86_64LinkingContext(triple, + llvm::make_unique<X86_64TargetHandler>(*this)) {} + +void X86_64LinkingContext::addPasses(PassManager &pm) { + auto pass = createX86_64RelocationPass(*this); + if (pass) + pm.add(std::move(pass)); + ELFLinkingContext::addPasses(pm); +} + +std::unique_ptr<ELFLinkingContext> +X86_64LinkingContext::create(llvm::Triple triple) { + if (triple.getArch() == llvm::Triple::x86_64) + return std::unique_ptr<ELFLinkingContext>( + new elf::X86_64LinkingContext(triple)); + return nullptr; +} diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.h b/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.h new file mode 100644 index 0000000000000..2cc799a9c8102 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.h @@ -0,0 +1,100 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.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_X86_64_X86_64_LINKING_CONTEXT_H +#define LLD_READER_WRITER_ELF_X86_64_X86_64_LINKING_CONTEXT_H + +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { + +/// \brief x86-64 internal references. +enum { + /// \brief The 32 bit index of the relocation in the got this reference refers + /// to. + LLD_R_X86_64_GOTRELINDEX = 1024, +}; + +class X86_64LinkingContext : public ELFLinkingContext { +protected: + X86_64LinkingContext(llvm::Triple, std::unique_ptr<TargetHandlerBase>); +public: + static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + X86_64LinkingContext(llvm::Triple); + + void addPasses(PassManager &) override; + + uint64_t getBaseAddress() const override { + if (_baseAddress == 0) + return 0x400000; + return _baseAddress; + } + + bool isDynamicRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::x86_64); + switch (r.kindValue()) { + case llvm::ELF::R_X86_64_RELATIVE: + case llvm::ELF::R_X86_64_GLOB_DAT: + case llvm::ELF::R_X86_64_COPY: + case llvm::ELF::R_X86_64_DTPMOD64: + case llvm::ELF::R_X86_64_DTPOFF64: + case llvm::ELF::R_X86_64_TPOFF64: + return true; + default: + return false; + } + } + + bool isCopyRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::x86_64); + if (r.kindValue() == llvm::ELF::R_X86_64_COPY) + return true; + return false; + } + + virtual bool isPLTRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::x86_64); + switch (r.kindValue()) { + case llvm::ELF::R_X86_64_JUMP_SLOT: + case llvm::ELF::R_X86_64_IRELATIVE: + return true; + default: + return false; + } + } + + /// \brief X86_64 has two relative relocations + /// a) for supporting IFUNC - R_X86_64_IRELATIVE + /// b) for supporting relative relocs - R_X86_64_RELATIVE + bool isRelativeReloc(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::x86_64); + switch (r.kindValue()) { + case llvm::ELF::R_X86_64_IRELATIVE: + case llvm::ELF::R_X86_64_RELATIVE: + return true; + default: + return false; + } + } +}; +} // end namespace elf +} // end namespace lld + +#endif 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(); +} diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.h b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.h new file mode 100644 index 0000000000000..9e2c2171015d1 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.h @@ -0,0 +1,39 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.h --------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef X86_64_RELOCATION_HANDLER_H +#define X86_64_RELOCATION_HANDLER_H + +#include "X86_64TargetHandler.h" + +namespace lld { +namespace elf { +typedef llvm::object::ELFType<llvm::support::little, 2, true> X86_64ELFType; + +class X86_64TargetLayout; + +class X86_64TargetRelocationHandler final : public TargetRelocationHandler { +public: + X86_64TargetRelocationHandler(X86_64TargetLayout &layout) + : _tlsSize(0), _x86_64Layout(layout) {} + + std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, + const lld::AtomLayout &, + const Reference &) const override; + +private: + // Cached size of the TLS segment. + mutable uint64_t _tlsSize; + X86_64TargetLayout &_x86_64Layout; +}; + +} // end namespace elf +} // end namespace lld + +#endif // X86_64_RELOCATION_HANDLER_H diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.cpp b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.cpp new file mode 100644 index 0000000000000..0703927fd56c3 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.cpp @@ -0,0 +1,513 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.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 x86-64. This includes +/// GOT and PLT entries, TLS, COPY, and ifunc. +/// +/// This is based on section 4.4.1 of the AMD64 ABI (no stable URL as of Oct, +/// 2013). +/// +/// This also includes aditional behaivor that gnu-ld and gold implement but +/// which is not specified anywhere. +/// +//===----------------------------------------------------------------------===// + +#include "X86_64RelocationPass.h" +#include "Atoms.h" +#include "X86_64LinkingContext.h" +#include "lld/Core/Simple.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" + +using namespace lld; +using namespace lld::elf; +using namespace llvm::ELF; + +// .got values +static const uint8_t x86_64GotAtomContent[8] = {0}; + +// .plt value (entry 0) +static const uint8_t x86_64Plt0AtomContent[16] = { + 0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // pushq GOT+8(%rip) + 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *GOT+16(%rip) + 0x90, 0x90, 0x90, 0x90 // nopnopnop +}; + +// .plt values (other entries) +static const uint8_t x86_64PltAtomContent[16] = { + 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmpq *gotatom(%rip) + 0x68, 0x00, 0x00, 0x00, 0x00, // pushq reloc-index + 0xe9, 0x00, 0x00, 0x00, 0x00 // jmpq plt[-1] +}; + +// TLS GD Entry +static const uint8_t x86_64GotTlsGdAtomContent[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +namespace { +/// \brief Atoms that are used by X86_64 dynamic linking +class X86_64GOTAtom : public GOTAtom { +public: + X86_64GOTAtom(const File &f, StringRef secName) : GOTAtom(f, secName) {} + + ArrayRef<uint8_t> rawContent() const override { + return ArrayRef<uint8_t>(x86_64GotAtomContent, 8); + } +}; + +/// \brief X86_64 GOT TLS GD entry. +class GOTTLSGdAtom : public X86_64GOTAtom { +public: + GOTTLSGdAtom(const File &f, StringRef secName) : X86_64GOTAtom(f, secName) {} + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(x86_64GotTlsGdAtomContent); + } +}; + +class X86_64PLT0Atom : public PLT0Atom { +public: + X86_64PLT0Atom(const File &f) : PLT0Atom(f) {} + ArrayRef<uint8_t> rawContent() const override { + return ArrayRef<uint8_t>(x86_64Plt0AtomContent, 16); + } +}; + +class X86_64PLTAtom : public PLTAtom { +public: + X86_64PLTAtom(const File &f, StringRef secName) : PLTAtom(f, secName) {} + + ArrayRef<uint8_t> rawContent() const override { + return ArrayRef<uint8_t>(x86_64PltAtomContent, 16); + } +}; + +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 RelocationPass : 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::x86_64); + switch (ref.kindValue()) { + case R_X86_64_16: + case R_X86_64_32: + case R_X86_64_32S: + case R_X86_64_64: + case R_X86_64_PC16: + case R_X86_64_PC32: + case R_X86_64_PC64: + static_cast<Derived *>(this)->handlePlain(ref); + break; + case R_X86_64_PLT32: + static_cast<Derived *>(this)->handlePLT32(ref); + break; + case R_X86_64_GOT32: + case R_X86_64_GOTPC32: + case R_X86_64_GOTPCREL: + case R_X86_64_GOTOFF64: + static_cast<Derived *>(this)->handleGOT(ref); + break; + case R_X86_64_GOTTPOFF: // GOT Thread Pointer Offset + static_cast<Derived *>(this)->handleGOTTPOFF(ref); + break; + case R_X86_64_TLSGD: + static_cast<Derived *>(this)->handleTLSGd(ref); + break; + } + } + +protected: + /// \brief get the PLT entry for a given IFUNC Atom. + /// + /// If the entry does not exist. Both the GOT and PLT entry is created. + const PLTAtom *getIFUNCPLTEntry(const DefinedAtom *da) { + auto plt = _pltMap.find(da); + if (plt != _pltMap.end()) + return plt->second; + auto ga = new (_file._alloc) X86_64GOTAtom(_file, ".got.plt"); + ga->addReferenceELF_x86_64(R_X86_64_IRELATIVE, 0, da, 0); + auto pa = new (_file._alloc) X86_64PLTAtom(_file, ".plt"); + pa->addReferenceELF_x86_64(R_X86_64_PC32, 2, ga, -4); +#ifndef NDEBUG + ga->_name = "__got_ifunc_"; + ga->_name += da->name(); + pa->_name = "__plt_ifunc_"; + pa->_name += da->name(); +#endif + _gotMap[da] = ga; + _pltMap[da] = pa; + _gotVector.push_back(ga); + _pltVector.push_back(pa); + return pa; + } + + /// \brief Redirect the call to the PLT stub for the target IFUNC. + /// + /// This create a PLT and GOT entry for the IFUNC if one does not exist. The + /// GOT entry and a IRELATIVE relocation to the original target resolver. + std::error_code handleIFUNC(const Reference &ref) { + auto target = dyn_cast_or_null<const DefinedAtom>(ref.target()); + if (target && target->contentType() == DefinedAtom::typeResolver) + const_cast<Reference &>(ref).setTarget(getIFUNCPLTEntry(target)); + return std::error_code(); + } + + /// \brief Create a GOT entry for the TP offset of a TLS atom. + const GOTAtom *getGOTTPOFF(const Atom *atom) { + auto got = _gotMap.find(atom); + if (got == _gotMap.end()) { + auto g = new (_file._alloc) X86_64GOTAtom(_file, ".got"); + g->addReferenceELF_x86_64(R_X86_64_TPOFF64, 0, atom, 0); +#ifndef NDEBUG + g->_name = "__got_tls_"; + g->_name += atom->name(); +#endif + _gotMap[atom] = g; + _gotVector.push_back(g); + return g; + } + return got->second; + } + + /// \brief Create a TPOFF64 GOT entry and change the relocation to a PC32 to + /// the GOT. + void handleGOTTPOFF(const Reference &ref) { + const_cast<Reference &>(ref).setTarget(getGOTTPOFF(ref.target())); + const_cast<Reference &>(ref).setKindValue(R_X86_64_PC32); + } + + /// \brief Create a TLS GOT entry with DTPMOD64/DTPOFF64 dynamic relocations. + void handleTLSGd(const Reference &ref) { + const_cast<Reference &>(ref).setTarget(getTLSGdGOTEntry(ref.target())); + } + + /// \brief Create a GOT entry containing 0. + const GOTAtom *getNullGOT() { + if (!_null) { + _null = new (_file._alloc) X86_64GOTAtom(_file, ".got.plt"); +#ifndef NDEBUG + _null->_name = "__got_null"; +#endif + } + return _null; + } + + const GOTAtom *getGOT(const DefinedAtom *da) { + auto got = _gotMap.find(da); + if (got == _gotMap.end()) { + auto g = new (_file._alloc) X86_64GOTAtom(_file, ".got"); + g->addReferenceELF_x86_64(R_X86_64_64, 0, da, 0); +#ifndef NDEBUG + g->_name = "__got_"; + g->_name += da->name(); +#endif + _gotMap[da] = g; + _gotVector.push_back(g); + return g; + } + return got->second; + } + + const GOTAtom *getTLSGdGOTEntry(const Atom *a) { + auto got = _gotTLSGdMap.find(a); + if (got != _gotTLSGdMap.end()) + return got->second; + + auto ga = new (_file._alloc) GOTTLSGdAtom(_file, ".got"); + _gotTLSGdMap[a] = ga; + + _tlsGotVector.push_back(ga); + ga->addReferenceELF_x86_64(R_X86_64_DTPMOD64, 0, a, 0); + ga->addReferenceELF_x86_64(R_X86_64_DTPOFF64, 8, a, 0); + + return ga; + } + +public: + RelocationPass(const ELFLinkingContext &ctx) + : _file(ctx), _ctx(ctx), _null(nullptr), _PLT0(nullptr), _got0(nullptr), + _got1(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 { + ScopedTask task(getDefaultDomain(), "X86-64 GOT/PLT Pass"); + // 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 (_PLT0) { + _got0->setOrdinal(ordinal++); + _got1->setOrdinal(ordinal++); + mf->addAtom(*_got0); + mf->addAtom(*_got1); + } + for (auto &got : _gotVector) { + got->setOrdinal(ordinal++); + mf->addAtom(*got); + } + for (auto &got : _tlsGotVector) { + got->setOrdinal(ordinal++); + mf->addAtom(*got); + } + for (auto obj : _objectVector) { + obj->setOrdinal(ordinal++); + mf->addAtom(*obj); + } + } + +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 PLT entries. + llvm::DenseMap<const Atom *, PLTAtom *> _pltMap; + + /// \brief Map Atoms to TLS GD GOT entries. + llvm::DenseMap<const Atom *, GOTAtom *> _gotTLSGdMap; + + /// \brief Map Atoms to their Object entries. + llvm::DenseMap<const Atom *, ObjectAtom *> _objectMap; + + /// \brief the list of GOT/PLT atoms + std::vector<GOTAtom *> _gotVector; + std::vector<PLTAtom *> _pltVector; + std::vector<ObjectAtom *> _objectVector; + + /// \brief the list of TLS GOT atoms. + std::vector<GOTAtom *> _tlsGotVector; + + /// \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; + GOTAtom *_got1; + /// @} +}; + +/// 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 StaticRelocationPass final + : public RelocationPass<StaticRelocationPass> { +public: + StaticRelocationPass(const elf::X86_64LinkingContext &ctx) + : RelocationPass(ctx) {} + + std::error_code handlePlain(const Reference &ref) { return handleIFUNC(ref); } + + std::error_code handlePLT32(const Reference &ref) { + // __tls_get_addr is handled elsewhere. + if (ref.target() && ref.target()->name() == "__tls_get_addr") { + const_cast<Reference &>(ref).setKindValue(R_X86_64_NONE); + return std::error_code(); + } + // Static code doesn't need PLTs. + const_cast<Reference &>(ref).setKindValue(R_X86_64_PC32); + // Handle IFUNC. + if (const DefinedAtom *da = + dyn_cast_or_null<const DefinedAtom>(ref.target())) + if (da->contentType() == DefinedAtom::typeResolver) + return handleIFUNC(ref); + return std::error_code(); + } + + std::error_code handleGOT(const Reference &ref) { + if (isa<UndefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getNullGOT()); + else if (const DefinedAtom *da = dyn_cast<const DefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getGOT(da)); + return std::error_code(); + } +}; + +class DynamicRelocationPass final + : public RelocationPass<DynamicRelocationPass> { +public: + DynamicRelocationPass(const elf::X86_64LinkingContext &ctx) + : RelocationPass(ctx) {} + + const PLT0Atom *getPLT0() { + if (_PLT0) + return _PLT0; + // Fill in the null entry. + getNullGOT(); + _PLT0 = new (_file._alloc) X86_64PLT0Atom(_file); + _got0 = new (_file._alloc) X86_64GOTAtom(_file, ".got.plt"); + _got1 = new (_file._alloc) X86_64GOTAtom(_file, ".got.plt"); + _PLT0->addReferenceELF_x86_64(R_X86_64_PC32, 2, _got0, -4); + _PLT0->addReferenceELF_x86_64(R_X86_64_PC32, 8, _got1, -4); +#ifndef NDEBUG + _got0->_name = "__got0"; + _got1->_name = "__got1"; +#endif + 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) X86_64GOTAtom(_file, ".got.plt"); + ga->addReferenceELF_x86_64(R_X86_64_JUMP_SLOT, 0, a, 0); + auto pa = new (_file._alloc) X86_64PLTAtom(_file, ".plt"); + pa->addReferenceELF_x86_64(R_X86_64_PC32, 2, ga, -4); + pa->addReferenceELF_x86_64(LLD_R_X86_64_GOTRELINDEX, 7, ga, 0); + pa->addReferenceELF_x86_64(R_X86_64_PC32, 12, getPLT0(), -4); + // Set the starting address of the got entry to the second instruction in + // the plt entry. + ga->addReferenceELF_x86_64(R_X86_64_64, 0, pa, 6); +#ifndef NDEBUG + ga->_name = "__got_"; + ga->_name += a->name(); + pa->_name = "__plt_"; + pa->_name += a->name(); +#endif + _gotMap[a] = ga; + _pltMap[a] = pa; + _gotVector.push_back(ga); + _pltVector.push_back(pa); + return pa; + } + + const ObjectAtom *getObjectEntry(const SharedLibraryAtom *a) { + auto obj = _objectMap.find(a); + if (obj != _objectMap.end()) + return obj->second; + + auto oa = new (_file._alloc) ObjectAtom(_file); + // This needs to point to the atom that we just created. + oa->addReferenceELF_x86_64(R_X86_64_COPY, 0, oa, 0); + + oa->_name = a->name(); + oa->_size = a->size(); + + _objectMap[a] = oa; + _objectVector.push_back(oa); + return oa; + } + + std::error_code handlePlain(const Reference &ref) { + if (!ref.target()) + return std::error_code(); + if (auto sla = dyn_cast<SharedLibraryAtom>(ref.target())) { + if (sla->type() == SharedLibraryAtom::Type::Data) + const_cast<Reference &>(ref).setTarget(getObjectEntry(sla)); + else if (sla->type() == SharedLibraryAtom::Type::Code) + const_cast<Reference &>(ref).setTarget(getPLTEntry(sla)); + } else + return handleIFUNC(ref); + return std::error_code(); + } + + std::error_code handlePLT32(const Reference &ref) { + // Turn this into a PC32 to the PLT entry. + const_cast<Reference &>(ref).setKindValue(R_X86_64_PC32); + // Handle IFUNC. + if (const DefinedAtom *da = + dyn_cast_or_null<const DefinedAtom>(ref.target())) + if (da->contentType() == DefinedAtom::typeResolver) + return handleIFUNC(ref); + // If it is undefined at link time, push the work to the dynamic linker by + // creating a PLT entry + if (isa<SharedLibraryAtom>(ref.target()) || + isa<UndefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getPLTEntry(ref.target())); + return std::error_code(); + } + + const GOTAtom *getSharedGOT(const Atom *a) { + auto got = _gotMap.find(a); + if (got == _gotMap.end()) { + auto g = new (_file._alloc) X86_64GOTAtom(_file, ".got"); + g->addReferenceELF_x86_64(R_X86_64_GLOB_DAT, 0, a, 0); +#ifndef NDEBUG + g->_name = "__got_"; + g->_name += a->name(); +#endif + _gotMap[a] = g; + _gotVector.push_back(g); + return g; + } + return got->second; + } + + std::error_code handleGOT(const Reference &ref) { + if (const DefinedAtom *da = dyn_cast<const DefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getGOT(da)); + // Handle undefined atoms in the same way as shared lib atoms: to be + // resolved at run time. + else if (isa<SharedLibraryAtom>(ref.target()) || + isa<UndefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getSharedGOT(ref.target())); + return std::error_code(); + } +}; +} // end anon namespace + +std::unique_ptr<Pass> +lld::elf::createX86_64RelocationPass(const X86_64LinkingContext &ctx) { + switch (ctx.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + if (ctx.isDynamic()) + return llvm::make_unique<DynamicRelocationPass>(ctx); + return llvm::make_unique<StaticRelocationPass>(ctx); + case llvm::ELF::ET_DYN: + return llvm::make_unique<DynamicRelocationPass>(ctx); + case llvm::ELF::ET_REL: + return nullptr; + default: + llvm_unreachable("Unhandled output file type"); + } +} diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.h b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.h new file mode 100644 index 0000000000000..1635b5e5f57b1 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.h @@ -0,0 +1,32 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.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 x86-64. This includes +/// GOT and PLT entries, TLS, COPY, and ifunc. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_ELF_X86_64_X86_64_RELOCATION_PASS_H +#define LLD_READER_WRITER_ELF_X86_64_X86_64_RELOCATION_PASS_H + +#include <memory> + +namespace lld { +class Pass; +namespace elf { +class X86_64LinkingContext; + +/// \brief Create x86-64 relocation pass for the given linking context. +std::unique_ptr<Pass> +createX86_64RelocationPass(const X86_64LinkingContext &); +} +} + +#endif diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp b/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp new file mode 100644 index 0000000000000..f35330eb25c05 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp @@ -0,0 +1,52 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.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 "X86_64DynamicLibraryWriter.h" +#include "X86_64ExecutableWriter.h" +#include "X86_64LinkingContext.h" +#include "X86_64TargetHandler.h" + +using namespace lld; +using namespace elf; + +X86_64TargetHandler::X86_64TargetHandler(X86_64LinkingContext &context) + : _context(context), _x86_64TargetLayout(new X86_64TargetLayout(context)), + _x86_64RelocationHandler( + new X86_64TargetRelocationHandler(*_x86_64TargetLayout.get())) {} + +void X86_64TargetHandler::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, + Reference::KindArch::x86_64, kindStrings); +} + +std::unique_ptr<Writer> X86_64TargetHandler::getWriter() { + switch (this->_context.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + return std::unique_ptr<Writer>( + new X86_64ExecutableWriter(_context, *_x86_64TargetLayout.get())); + case llvm::ELF::ET_DYN: + return std::unique_ptr<Writer>( + new X86_64DynamicLibraryWriter(_context, *_x86_64TargetLayout.get())); + case llvm::ELF::ET_REL: + llvm_unreachable("TODO: support -r mode"); + default: + llvm_unreachable("unsupported output type"); + } +} + +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), + +const Registry::KindStrings X86_64TargetHandler::kindStrings[] = { +#include "llvm/Support/ELFRelocs/x86_64.def" + LLD_KIND_STRING_ENTRY(LLD_R_X86_64_GOTRELINDEX), + LLD_KIND_STRING_END +}; + +#undef ELF_RELOC diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.h b/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.h new file mode 100644 index 0000000000000..57da7bca01e67 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.h @@ -0,0 +1,69 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.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_X86_64_X86_64_TARGET_HANDLER_H +#define LLD_READER_WRITER_ELF_X86_64_X86_64_TARGET_HANDLER_H + +#include "DefaultTargetHandler.h" +#include "TargetLayout.h" +#include "X86_64ELFFile.h" +#include "X86_64ELFReader.h" +#include "X86_64LinkingContext.h" +#include "X86_64RelocationHandler.h" +#include "lld/Core/Simple.h" + +namespace lld { +namespace elf { +class X86_64TargetLayout : public TargetLayout<X86_64ELFType> { +public: + X86_64TargetLayout(X86_64LinkingContext &context) + : TargetLayout(context) {} + + void finalizeOutputSectionLayout() override { + sortOutputSectionByPriority(".init_array", ".init_array"); + sortOutputSectionByPriority(".fini_array", ".fini_array"); + } +}; + +class X86_64TargetHandler + : public DefaultTargetHandler<X86_64ELFType> { +public: + X86_64TargetHandler(X86_64LinkingContext &context); + + X86_64TargetLayout &getTargetLayout() override { + return *(_x86_64TargetLayout.get()); + } + + void registerRelocationNames(Registry ®istry) override; + + const X86_64TargetRelocationHandler &getRelocationHandler() const override { + return *(_x86_64RelocationHandler.get()); + } + + std::unique_ptr<Reader> getObjReader() override { + return std::unique_ptr<Reader>(new X86_64ELFObjectReader(_context)); + } + + std::unique_ptr<Reader> getDSOReader() override { + return std::unique_ptr<Reader>(new X86_64ELFDSOReader(_context)); + } + + std::unique_ptr<Writer> getWriter() override; + +protected: + static const Registry::KindStrings kindStrings[]; + X86_64LinkingContext &_context; + std::unique_ptr<X86_64TargetLayout> _x86_64TargetLayout; + std::unique_ptr<X86_64TargetRelocationHandler> _x86_64RelocationHandler; +}; + +} // end namespace elf +} // end namespace lld + +#endif |