diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2015-12-30 11:57:38 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2015-12-30 11:57:38 +0000 |
commit | 5a5c549fe9a3fef595297bd21d36bed8409dc37d (patch) | |
tree | a964c8f5ac85b7b641cac022c5f9bf4eed3d2b9b /lib/ReaderWriter/ELF | |
parent | fb911942f1434f3d1750f83f25f5e42c80e60638 (diff) |
Notes
Diffstat (limited to 'lib/ReaderWriter/ELF')
150 files changed, 12149 insertions, 9435 deletions
diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64DynamicLibraryWriter.h b/lib/ReaderWriter/ELF/AArch64/AArch64DynamicLibraryWriter.h index 12ba52a38f380..73864d2b4c38d 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64DynamicLibraryWriter.h +++ b/lib/ReaderWriter/ELF/AArch64/AArch64DynamicLibraryWriter.h @@ -16,51 +16,27 @@ namespace lld { namespace elf { -template <class ELFT> -class AArch64DynamicLibraryWriter : public DynamicLibraryWriter<ELFT> { +class AArch64DynamicLibraryWriter : public DynamicLibraryWriter<ELF64LE> { public: - AArch64DynamicLibraryWriter(AArch64LinkingContext &context, - AArch64TargetLayout<ELFT> &layout); + AArch64DynamicLibraryWriter(AArch64LinkingContext &ctx, + TargetLayout<ELF64LE> &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; + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; }; -template <class ELFT> -AArch64DynamicLibraryWriter<ELFT>::AArch64DynamicLibraryWriter( - AArch64LinkingContext &context, AArch64TargetLayout<ELFT> &layout) - : DynamicLibraryWriter<ELFT>(context, layout), - _gotFile(new GOTFile(context)), _context(context), - _AArch64Layout(layout) {} +AArch64DynamicLibraryWriter::AArch64DynamicLibraryWriter( + AArch64LinkingContext &ctx, TargetLayout<ELF64LE> &layout) + : DynamicLibraryWriter(ctx, layout) {} -template <class ELFT> -bool AArch64DynamicLibraryWriter<ELFT>::createImplicitFiles( +void AArch64DynamicLibraryWriter::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; + DynamicLibraryWriter::createImplicitFiles(result); + auto gotFile = llvm::make_unique<SimpleFile>("GOTFile"); + gotFile->addAtom(*new (gotFile->allocator()) GlobalOffsetTableAtom(*gotFile)); + gotFile->addAtom(*new (gotFile->allocator()) DynamicAtom(*gotFile)); + result.push_back(std::move(gotFile)); } } // namespace elf diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64ELFFile.h b/lib/ReaderWriter/ELF/AArch64/AArch64ELFFile.h deleted file mode 100644 index 9d5207c1c4b49..0000000000000 --- a/lib/ReaderWriter/ELF/AArch64/AArch64ELFFile.h +++ /dev/null @@ -1,41 +0,0 @@ -//===- 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 deleted file mode 100644 index 05f312db3e7b7..0000000000000 --- a/lib/ReaderWriter/ELF/AArch64/AArch64ELFReader.h +++ /dev/null @@ -1,62 +0,0 @@ -//===- 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.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.cpp new file mode 100644 index 0000000000000..9a9ec6cba12bb --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.cpp @@ -0,0 +1,52 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.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 "AArch64ExecutableWriter.h" +#include "AArch64TargetHandler.h" +#include "AArch64SectionChunks.h" + +namespace lld { +namespace elf { + +AArch64ExecutableWriter::AArch64ExecutableWriter(AArch64LinkingContext &ctx, + AArch64TargetLayout &layout) + : ExecutableWriter(ctx, layout), _targetLayout(layout) {} + +void AArch64ExecutableWriter::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + ExecutableWriter::createImplicitFiles(result); + auto gotFile = llvm::make_unique<SimpleFile>("GOTFile"); + gotFile->addAtom(*new (gotFile->allocator()) GlobalOffsetTableAtom(*gotFile)); + if (this->_ctx.isDynamic()) + gotFile->addAtom(*new (gotFile->allocator()) DynamicAtom(*gotFile)); + result.push_back(std::move(gotFile)); +} + +void AArch64ExecutableWriter::buildDynamicSymbolTable(const File &file) { + for (auto sec : this->_layout.sections()) { + if (auto section = dyn_cast<AtomSection<ELF64LE>>(sec)) { + for (const auto &atom : section->atoms()) { + // Add all globals GOT symbols (in both .got and .got.plt sections) + // on dynamic symbol table. + for (const auto §ion : _targetLayout.getGOTSections()) { + if (section->hasGlobalGOTEntry(atom->_atom)) + _dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), + atom->_virtualAddr, atom); + } + } + } + } + + ExecutableWriter<ELF64LE>::buildDynamicSymbolTable(file); +} + +} // namespace elf +} // namespace lld + diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.h b/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.h index 73963f56ef70f..eef825040ffaf 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.h +++ b/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.h @@ -9,59 +9,29 @@ #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> { +class AArch64TargetLayout; +class AArch64LinkingContext; + +class AArch64ExecutableWriter : public ExecutableWriter<ELF64LE> { public: - AArch64ExecutableWriter(AArch64LinkingContext &context, - AArch64TargetLayout<ELFT> &layout); + AArch64ExecutableWriter(AArch64LinkingContext &ctx, + AArch64TargetLayout &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 createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; - void addDefaultAtoms() override{ - return ExecutableWriter<ELFT>::addDefaultAtoms(); - } + void buildDynamicSymbolTable(const File &file) override; 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; + AArch64TargetLayout &_targetLayout; }; -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 diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.cpp index 9eb98f4477098..ba883f7f59db6 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.cpp +++ b/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.cpp @@ -12,22 +12,34 @@ #include "AArch64TargetHandler.h" using namespace lld; +using namespace lld::elf; std::unique_ptr<ELFLinkingContext> -elf::AArch64LinkingContext::create(llvm::Triple triple) { +elf::createAArch64LinkingContext(llvm::Triple triple) { if (triple.getArch() == llvm::Triple::aarch64) - return std::unique_ptr<ELFLinkingContext>( - new elf::AArch64LinkingContext(triple)); + return llvm::make_unique<AArch64LinkingContext>(triple); return nullptr; } -elf::AArch64LinkingContext::AArch64LinkingContext(llvm::Triple triple) - : ELFLinkingContext(triple, std::unique_ptr<TargetHandlerBase>( - new AArch64TargetHandler(*this))) {} +AArch64LinkingContext::AArch64LinkingContext(llvm::Triple triple) + : ELFLinkingContext(triple, std::unique_ptr<TargetHandler>( + new AArch64TargetHandler(*this))) {} -void elf::AArch64LinkingContext::addPasses(PassManager &pm) { +void AArch64LinkingContext::addPasses(PassManager &pm) { auto pass = createAArch64RelocationPass(*this); if (pass) pm.add(std::move(pass)); ELFLinkingContext::addPasses(pm); } + +static const Registry::KindStrings kindStrings[] = { +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), +#include "llvm/Support/ELFRelocs/AArch64.def" +#undef ELF_RELOC + LLD_KIND_STRING_END +}; + +void AArch64LinkingContext::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, + Reference::KindArch::AArch64, kindStrings); +} diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.h b/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.h index ebd91fe0a95b8..25a1731583183 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.h +++ b/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.h @@ -24,10 +24,11 @@ enum { class AArch64LinkingContext final : public ELFLinkingContext { public: - static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + int getMachineType() const override { return llvm::ELF::EM_AARCH64; } AArch64LinkingContext(llvm::Triple); void addPasses(PassManager &) override; + void registerRelocationNames(Registry &r) override; uint64_t getBaseAddress() const override { if (_baseAddress == 0) @@ -88,6 +89,11 @@ public: return false; } } + + /// \brief The path to the dynamic interpreter + StringRef getDefaultInterpreter() const override { + return "/lib/ld-linux-aarch64.so.1"; + } }; } // end namespace elf } // end namespace lld diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.cpp index d1ecc7fa884b0..ac7c769ec26d0 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.cpp +++ b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.cpp @@ -13,11 +13,14 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/MathExtras.h" +#define DEBUG_TYPE "AArch64" + using namespace lld; using namespace lld::elf; +using namespace llvm; using namespace llvm::support::endian; -#define PAGE(X) ((X) & ~0x0FFFL) +static int64_t page(int64_t v) { return v & ~int64_t(0xFFF); } /// \brief Check X is in the interval (-2^(bits-1), 2^bits] static bool withinSignedUnsignedRange(int64_t X, int bits) { @@ -28,77 +31,130 @@ static bool withinSignedUnsignedRange(int64_t X, int bits) { 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"); + DEBUG(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"); + DEBUG(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, +/// \brief R_AARCH64_ABS16 - word16: S + A +static std::error_code relocR_AARCH64_ABS16(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { - uint64_t result = (PAGE(S + A) - PAGE(P)); + int64_t result = S + A; + if (!withinSignedUnsignedRange(result, 16)) + return make_out_of_range_reloc_error(); + DEBUG(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"); + write16le(location, result | read16le(location)); + return std::error_code(); +} + +/// \brief R_AARCH64_PREL64 - word64: S + A - P +static void relocR_AARCH64_PREL64(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int64_t result = S + A - P; + DEBUG(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 std::error_code relocR_AARCH64_PREL32(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int64_t result = S + A - P; + // ELF for the ARM 64-bit architecture manual states the overflow + // for R_AARCH64_PREL32 to be -2^(-31) <= X < 2^32 + if (!withinSignedUnsignedRange(result, 32)) + return make_out_of_range_reloc_error(); + DEBUG(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_PREL16 - word16: S + A - P +static std::error_code relocR_AARCH64_PREL16(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int64_t result = S + A - P; + if (!withinSignedUnsignedRange(result, 16)) + return make_out_of_range_reloc_error(); + DEBUG(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"); + write16le(location, result + read16le(location)); + return std::error_code(); +} + +/// \brief R_AARCH64_ADR_PREL_PG_HI21 - Page(S+A) - Page(P) +static std::error_code relocR_AARCH64_ADR_PREL_PG_HI21(uint8_t *location, + uint64_t P, uint64_t S, + int64_t A) { + int64_t result = page(S + A) - page(P); + if (!isInt<32>(result)) + return make_out_of_range_reloc_error(); 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"); + DEBUG(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! + return std::error_code(); } /// \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; +static std::error_code relocR_AARCH64_ADR_PREL_LO21(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + uint64_t result = S + A - P; + if (!isInt<20>(result)) + return make_out_of_range_reloc_error(); 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"); + DEBUG(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! + return std::error_code(); } /// \brief R_AARCH64_ADD_ABS_LO12_NC @@ -106,41 +162,46 @@ 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"); + DEBUG(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); +/// \brief R_AARCH64_CALL26 and R_AARCH64_JUMP26 +static std::error_code relocJump26(uint8_t *location, uint64_t P, uint64_t S, + int64_t A) { + int64_t result = S + A - P; + if (!isInt<27>(result)) + return make_out_of_range_reloc_error(); 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"); + DEBUG(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)); + return std::error_code(); } /// \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); +static std::error_code relocR_AARCH64_CONDBR19(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int64_t result = S + A - P; + if (!isInt<20>(result)) + return make_out_of_range_reloc_error(); 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"); + DEBUG(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)); + return std::error_code(); } /// \brief R_AARCH64_LDST8_ABS_LO12_NC - S + A @@ -148,12 +209,11 @@ 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"); + DEBUG(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)); } @@ -163,12 +223,11 @@ static void relocR_AARCH64_LDST16_ABS_LO12_NC(uint8_t *location, uint64_t P, 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"); + DEBUG(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)); } @@ -178,12 +237,11 @@ static void relocR_AARCH64_LDST32_ABS_LO12_NC(uint8_t *location, uint64_t P, 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"); + DEBUG(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)); } @@ -193,12 +251,11 @@ static void relocR_AARCH64_LDST64_ABS_LO12_NC(uint8_t *location, uint64_t P, 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"); + DEBUG(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)); } @@ -208,83 +265,89 @@ static void relocR_AARCH64_LDST128_ABS_LO12_NC(uint8_t *location, uint64_t P, 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"); + DEBUG(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; +static std::error_code 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); + if (!isInt<32>(result)) + return make_out_of_range_reloc_error(); + result = (result >> 12) & 0x3FFFF; 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)); + DEBUG(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)); + return std::error_code(); } // 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) { +static std::error_code 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"); + DEBUG(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"); + if ((result & 0x7) != 0) + return make_unaligned_range_reloc_error(); result &= 0xFF8; result <<= 7; write32le(location, result | read32le(location)); + return std::error_code(); } // 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"); + DEBUG(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); +static std::error_code 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); + if (!isInt<32>(result)) + return make_out_of_range_reloc_error(); 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"); + DEBUG(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)); + return std::error_code(); } // R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC @@ -294,28 +357,31 @@ static void relocR_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC(uint8_t *location, 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"); + DEBUG(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; +static std::error_code relocR_AARCH64_TLSLE_ADD_TPREL_HI12(uint8_t *location, + uint64_t P, + uint64_t S, + int64_t A) { + int64_t result = S + A; + if (!isUInt<24>(result)) + return make_out_of_range_reloc_error(); 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"); + DEBUG(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)); + return std::error_code(); } /// \brief R_AARCH64_TLSLE_ADD_TPREL_LO12_NC @@ -325,22 +391,76 @@ static void relocR_AARCH64_TLSLE_ADD_TPREL_LO12_NC(uint8_t *location, 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"); + DEBUG(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_TLSDESC_ADR_PAGE21 - Page(G(GTLSDESC(S+A))) - Page(P) +static std::error_code relocR_AARCH64_TLSDESC_ADR_PAGE21(uint8_t *location, + uint64_t P, uint64_t S, + int64_t A) { + int64_t result = page(S + A) - page(P); + if (!isInt<32>(result)) + return make_out_of_range_reloc_error(); + result = result >> 12; + uint32_t immlo = result & 0x3; + uint32_t immhi = result & 0x1FFFFC; + immlo = immlo << 29; + immhi = immhi << 3; + DEBUG(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)); + return std::error_code(); +} + +/// \brief R_AARCH64_TLSDESC_LD64_LO12_NC - G(GTLSDESC(S+A)) -> S + A +static std::error_code relocR_AARCH64_TLSDESC_LD64_LO12_NC(uint8_t *location, + uint64_t P, + uint64_t S, + int64_t A) { + int32_t result = S + A; + DEBUG(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"); + if ((result & 0x7) != 0) + return make_unaligned_range_reloc_error(); + result &= 0xFF8; + result <<= 7; + write32le(location, result | read32le(location)); + return std::error_code(); +} + +/// \brief R_AARCH64_TLSDESC_ADD_LO12_NC - G(GTLSDESC(S+A)) -> S + A +static void relocR_AARCH64_TLSDESC_ADD_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(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, + ELFWriter &writer, llvm::FileOutputBuffer &buf, const 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(); + uint8_t *loc = atomContent + ref.offsetInAtom(); + uint64_t target = writer.addressOfAtom(ref.target()); + uint64_t reloc = atom._virtualAddr + ref.offsetInAtom(); + int64_t addend = ref.addend(); if (ref.kindNamespace() != Reference::KindNamespace::ELF) return std::error_code(); @@ -349,92 +469,88 @@ std::error_code AArch64TargetRelocationHandler::applyRelocation( 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()); + relocR_AARCH64_ABS64(loc, reloc, target, 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: + return relocR_AARCH64_ABS32(loc, reloc, target, addend); + case R_AARCH64_ABS16: + return relocR_AARCH64_ABS16(loc, reloc, target, addend); + case R_AARCH64_PREL64: + relocR_AARCH64_PREL64(loc, reloc, target, addend); break; + case R_AARCH64_PREL32: + return relocR_AARCH64_PREL32(loc, reloc, target, addend); + case R_AARCH64_PREL16: + return relocR_AARCH64_PREL16(loc, reloc, target, addend); case R_AARCH64_ADR_PREL_PG_HI21: - relocR_AARCH64_ADR_PREL_PG_HI21(location, relocVAddress, targetVAddress, - ref.addend()); - break; + return relocR_AARCH64_ADR_PREL_PG_HI21(loc, reloc, target, addend); case R_AARCH64_ADR_PREL_LO21: - relocR_AARCH64_ADR_PREL_LO21(location, relocVAddress, targetVAddress, - ref.addend()); - break; + return relocR_AARCH64_ADR_PREL_LO21(loc, reloc, target, addend); case R_AARCH64_ADD_ABS_LO12_NC: - relocR_AARCH64_ADD_ABS_LO12_NC(location, relocVAddress, targetVAddress, - ref.addend()); + relocR_AARCH64_ADD_ABS_LO12_NC(loc, reloc, target, addend); break; case R_AARCH64_CALL26: case R_AARCH64_JUMP26: - relocJump26(location, relocVAddress, targetVAddress, ref.addend()); - break; + return relocJump26(loc, reloc, target, addend); case R_AARCH64_CONDBR19: - relocR_AARCH64_CONDBR19(location, relocVAddress, targetVAddress, - ref.addend()); - break; + return relocR_AARCH64_CONDBR19(loc, reloc, target, addend); case R_AARCH64_ADR_GOT_PAGE: - relocR_AARCH64_ADR_GOT_PAGE(location, relocVAddress, targetVAddress, - ref.addend()); - break; + return relocR_AARCH64_ADR_GOT_PAGE(loc, reloc, target, addend); case R_AARCH64_LD64_GOT_LO12_NC: - relocR_AARCH64_LD64_GOT_LO12_NC(location, relocVAddress, targetVAddress, - ref.addend()); - break; + return relocR_AARCH64_LD64_GOT_LO12_NC(loc, reloc, target, addend); case R_AARCH64_LDST8_ABS_LO12_NC: - relocR_AARCH64_LDST8_ABS_LO12_NC(location, relocVAddress, targetVAddress, - ref.addend()); + relocR_AARCH64_LDST8_ABS_LO12_NC(loc, reloc, target, addend); break; case R_AARCH64_LDST16_ABS_LO12_NC: - relocR_AARCH64_LDST16_ABS_LO12_NC(location, relocVAddress, targetVAddress, - ref.addend()); + relocR_AARCH64_LDST16_ABS_LO12_NC(loc, reloc, target, addend); break; case R_AARCH64_LDST32_ABS_LO12_NC: - relocR_AARCH64_LDST32_ABS_LO12_NC(location, relocVAddress, targetVAddress, - ref.addend()); + relocR_AARCH64_LDST32_ABS_LO12_NC(loc, reloc, target, addend); break; case R_AARCH64_LDST64_ABS_LO12_NC: - relocR_AARCH64_LDST64_ABS_LO12_NC(location, relocVAddress, targetVAddress, - ref.addend()); + relocR_AARCH64_LDST64_ABS_LO12_NC(loc, reloc, target, addend); break; case R_AARCH64_LDST128_ABS_LO12_NC: - relocR_AARCH64_LDST128_ABS_LO12_NC(location, relocVAddress, targetVAddress, - ref.addend()); + relocR_AARCH64_LDST128_ABS_LO12_NC(loc, reloc, target, addend); break; case ADD_AARCH64_GOTRELINDEX: - relocADD_AARCH64_GOTRELINDEX(location, relocVAddress, targetVAddress, - ref.addend()); + relocADD_AARCH64_GOTRELINDEX(loc, reloc, target, addend); break; case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: - relocR_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21(location, relocVAddress, - targetVAddress, ref.addend()); - break; + return relocR_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21(loc, reloc, target, addend); case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: - relocR_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC(location, relocVAddress, - targetVAddress, ref.addend()); + relocR_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC(loc, reloc, target, addend); break; case R_AARCH64_TLSLE_ADD_TPREL_HI12: - relocR_AARCH64_TLSLE_ADD_TPREL_HI12(location, relocVAddress, targetVAddress, - ref.addend()); + case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: { + auto tpoffset = _layout.getTPOffset(); + if (ref.kindValue() == R_AARCH64_TLSLE_ADD_TPREL_HI12) + return relocR_AARCH64_TLSLE_ADD_TPREL_HI12(loc, reloc, target + tpoffset, + addend); + else + relocR_AARCH64_TLSLE_ADD_TPREL_LO12_NC(loc, reloc, target + tpoffset, + addend); + } break; + case R_AARCH64_TLSDESC_ADR_PAGE21: + return relocR_AARCH64_TLSDESC_ADR_PAGE21(loc, reloc, target, addend); + case R_AARCH64_TLSDESC_LD64_LO12_NC: + return relocR_AARCH64_TLSDESC_LD64_LO12_NC(loc, reloc, target, addend); + case R_AARCH64_TLSDESC_ADD_LO12_NC: + relocR_AARCH64_TLSDESC_ADD_LO12_NC(loc, reloc, target, addend); + break; + case R_AARCH64_TLSDESC_CALL: + // Relaxation only to optimize TLS access. Ignore for now. break; - case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: - relocR_AARCH64_TLSLE_ADD_TPREL_LO12_NC(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: + case R_AARCH64_TLS_TPREL64: + case R_AARCH64_TLSDESC: 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 index b1d3c09dc936c..8cde7a03e51ae 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.h +++ b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.h @@ -10,21 +10,24 @@ #ifndef AARCH64_RELOCATION_HANDLER_H #define AARCH64_RELOCATION_HANDLER_H -#include "AArch64TargetHandler.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" namespace lld { namespace elf { -typedef llvm::object::ELFType<llvm::support::little, 2, true> AArch64ELFType; -template <class ELFT> class AArch64TargetLayout; +class AArch64TargetLayout; class AArch64TargetRelocationHandler final : public TargetRelocationHandler { public: + AArch64TargetRelocationHandler(AArch64TargetLayout &layout) + : _layout(layout) {} + std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, - const lld::AtomLayout &, + const AtomLayout &, const Reference &) const override; - static const Registry::KindStrings kindStrings[]; +private: + AArch64TargetLayout &_layout; }; } // end namespace elf diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp index 0bd12958b27bd..4d94a793665c9 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp +++ b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp @@ -28,52 +28,79 @@ using namespace lld; using namespace lld::elf; using namespace llvm::ELF; -namespace { // .got values -const uint8_t AArch64GotAtomContent[8] = {0}; +static const uint8_t AArch64GotAtomContent[8] = {0}; + +// tls descriptor .got values, the layout is: +// struct tlsdesc { +// ptrdiff_t (*entry) (struct tlsdesc *); +// void *arg; +// }; +static const uint8_t AArch64TlsdescGotAtomContent[16] = {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 +static 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 +static 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 +}; + +// .plt tlsdesc values +static const uint8_t AArch64PltTlsdescAtomContent[32] = { + 0xe2, 0x0f, 0xbf, 0xa9, // stp x2, x3, [sp, #-16] + 0x02, 0x00, 0x00, 0x90, // adpr x2, 0 + 0x03, 0x00, 0x00, 0x90, // adpr x3, 0 + 0x42, 0x00, 0x40, 0xf9, // ldr x2, [x2, #0] + 0x63, 0x00, 0x00, 0x91, // add x3, x3, 0 + 0x40, 0x00, 0x1f, 0xd6, // br x2 + 0x1f, 0x20, 0x03, 0xd5, // nop + 0x1f, 0x20, 0x03, 0xd5 // nop }; +namespace { + /// \brief Atoms that are used by AArch64 dynamic linking class AArch64GOTAtom : public GOTAtom { public: - AArch64GOTAtom(const File &f, StringRef secName) : GOTAtom(f, secName) {} + AArch64GOTAtom(const File &f) : GOTAtom(f, ".got") {} ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(AArch64GotAtomContent, 8); } + +protected: + // Constructor for AArch64GOTAtom + AArch64GOTAtom(const File &f, StringRef secName) : GOTAtom(f, secName) {} +}; + +class AArch64GOTPLTAtom : public AArch64GOTAtom { +public: + AArch64GOTPLTAtom(const File &f) : AArch64GOTAtom(f, ".got.plt") {} }; +class AArch64TLSDESCGOTAtom : public AArch64GOTPLTAtom { +public: + AArch64TLSDESCGOTAtom(const File &f) : AArch64GOTPLTAtom(f) {} + + ArrayRef<uint8_t> rawContent() const override { + return ArrayRef<uint8_t>(AArch64TlsdescGotAtomContent, 16); + } +}; + + class AArch64PLT0Atom : public PLT0Atom { public: AArch64PLT0Atom(const File &f) : PLT0Atom(f) {} @@ -84,13 +111,22 @@ public: class AArch64PLTAtom : public PLTAtom { public: - AArch64PLTAtom(const File &f, StringRef secName) : PLTAtom(f, secName) {} + AArch64PLTAtom(const File &f) : PLTAtom(f, ".plt") {} ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(AArch64PltAtomContent, 16); } }; +class AArch64PLTTLSDESCAtom : public PLTAtom { +public: + AArch64PLTTLSDESCAtom(const File &f) : PLTAtom(f, ".plt") {} + + ArrayRef<uint8_t> rawContent() const override { + return ArrayRef<uint8_t>(AArch64PltTlsdescAtomContent, 32); + } +}; + class ELFPassFile : public SimpleFile { public: ELFPassFile(const ELFLinkingContext &eti) : SimpleFile("ELFPassFile") { @@ -149,9 +185,16 @@ template <class Derived> class AArch64RelocationPass : public Pass { break; case R_AARCH64_ADR_GOT_PAGE: case R_AARCH64_LD64_GOT_LO12_NC: + static_cast<Derived *>(this)->handleGOT(ref); + break; case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: - static_cast<Derived *>(this)->handleGOT(ref); + static_cast<Derived *>(this)->handleGOTTPREL(ref); + break; + case R_AARCH64_TLSDESC_ADR_PAGE21: + case R_AARCH64_TLSDESC_LD64_LO12_NC: + case R_AARCH64_TLSDESC_ADD_LO12_NC: + static_cast<Derived *>(this)->handleTLSDESC(ref); break; } } @@ -164,9 +207,9 @@ protected: auto plt = _pltMap.find(da); if (plt != _pltMap.end()) return plt->second; - auto ga = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); + auto ga = new (_file._alloc) AArch64GOTPLTAtom(_file); ga->addReferenceELF_AArch64(R_AARCH64_IRELATIVE, 0, da, 0); - auto pa = new (_file._alloc) AArch64PLTAtom(_file, ".plt"); + auto pa = new (_file._alloc) AArch64PLTAtom(_file); pa->addReferenceELF_AArch64(R_AARCH64_PREL32, 2, ga, -4); #ifndef NDEBUG ga->_name = "__got_ifunc_"; @@ -193,11 +236,11 @@ protected: } /// \brief Create a GOT entry for the TP offset of a TLS atom. - const GOTAtom *getGOTTPOFF(const Atom *atom) { + const GOTAtom *getGOTTPREL(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); + auto g = new (_file._alloc) AArch64GOTAtom(_file); + g->addReferenceELF_AArch64(R_AARCH64_TLS_TPREL64, 0, atom, 0); #ifndef NDEBUG g->_name = "__got_tls_"; g->_name += atom->name(); @@ -209,17 +252,53 @@ protected: 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 TPREL entry to local or external TLS variable. + std::error_code handleGOTTPREL(const Reference &ref) { + if (isa<DefinedAtom>(ref.target()) || + isa<SharedLibraryAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getGOTTPREL(ref.target())); + return std::error_code(); + } + + /// \brief Generates a double GOT entry with R_AARCH64_TLSDESC dynamic + /// relocation reference. Since the dynamic relocation is resolved + /// lazily so the GOT associated should be in .got.plt. + const GOTAtom *getTLSDESCPLTEntry(const Atom *da) { + auto got = _gotMap.find(da); + if (got != _gotMap.end()) + return got->second; + auto ga = new (_file._alloc) AArch64TLSDESCGOTAtom(_file); + ga->addReferenceELF_AArch64(R_AARCH64_TLSDESC, 0, da, 0); + auto pa = new (_file._alloc) AArch64PLTTLSDESCAtom(_file); + pa->addReferenceELF_AArch64(R_AARCH64_ADR_PREL_PG_HI21, 4, ga, 0); + pa->addReferenceELF_AArch64(R_AARCH64_ADR_PREL_PG_HI21, 8, ga, 0); + pa->addReferenceELF_AArch64(R_AARCH64_LDST64_ABS_LO12_NC, 12, ga, 0); + pa->addReferenceELF_AArch64(R_AARCH64_ADD_ABS_LO12_NC, 16, ga, 0); +#ifndef NDEBUG + ga->_name = "__got_tlsdesc_"; + ga->_name += da->name(); + pa->_name = "__plt_tlsdesc_"; + pa->_name += da->name(); +#endif + _gotMap[da] = ga; + _pltMap[da] = pa; + _tlsdescVector.push_back(ga); + _pltVector.push_back(pa); + return ga; + } + + std::error_code handleTLSDESC(const Reference &ref) { + if (isa<DefinedAtom>(ref.target()) || + isa<SharedLibraryAtom>(ref.target())) { + const_cast<Reference &>(ref).setTarget(getTLSDESCPLTEntry(ref.target())); + } + return std::error_code(); } /// \brief Create a GOT entry containing 0. const GOTAtom *getNullGOT() { if (!_null) { - _null = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); + _null = new (_file._alloc) AArch64GOTPLTAtom(_file); #ifndef NDEBUG _null->_name = "__got_null"; #endif @@ -230,7 +309,7 @@ protected: const GOTAtom *getGOT(const DefinedAtom *da) { auto got = _gotMap.find(da); if (got == _gotMap.end()) { - auto g = new (_file._alloc) AArch64GOTAtom(_file, ".got"); + auto g = new (_file._alloc) AArch64GOTAtom(_file); g->addReferenceELF_AArch64(R_AARCH64_ABS64, 0, da, 0); #ifndef NDEBUG g->_name = "__got_"; @@ -244,9 +323,7 @@ protected: } public: - AArch64RelocationPass(const ELFLinkingContext &ctx) - : _file(ctx), _ctx(ctx), _null(nullptr), _PLT0(nullptr), _got0(nullptr), - _got1(nullptr) {} + AArch64RelocationPass(const ELFLinkingContext &ctx) : _file(ctx), _ctx(ctx) {} /// \brief Do the pass. /// @@ -256,32 +333,32 @@ public: /// /// After all references are handled, the atoms created during that are all /// added to mf. - void perform(std::unique_ptr<MutableFile> &mf) override { + std::error_code perform(SimpleFile &mf) override { ScopedTask task(getDefaultDomain(), "AArch64 GOT/PLT Pass"); DEBUG_WITH_TYPE( "AArch64", llvm::dbgs() << "Undefined Atoms" << "\n"; for (const auto &atom - : mf->undefined()) { + : mf.undefined()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; } llvm::dbgs() << "Shared Library Atoms" << "\n"; for (const auto &atom - : mf->sharedLibrary()) { + : mf.sharedLibrary()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; } llvm::dbgs() << "Absolute Atoms" << "\n"; for (const auto &atom - : mf->absolute()) { + : 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 &atom : mf.defined()) { for (const auto &ref : *atom) { handleReference(*atom, *ref); } @@ -289,32 +366,39 @@ public: // Add all created atoms to the link. uint64_t ordinal = 0; - if (_PLT0) { - _PLT0->setOrdinal(ordinal++); - mf->addAtom(*_PLT0); + if (_plt0) { + _plt0->setOrdinal(ordinal++); + mf.addAtom(*_plt0); } for (auto &plt : _pltVector) { plt->setOrdinal(ordinal++); - mf->addAtom(*plt); + mf.addAtom(*plt); } if (_null) { _null->setOrdinal(ordinal++); - mf->addAtom(*_null); + mf.addAtom(*_null); } - if (_PLT0) { + if (_plt0) { _got0->setOrdinal(ordinal++); _got1->setOrdinal(ordinal++); - mf->addAtom(*_got0); - mf->addAtom(*_got1); + mf.addAtom(*_got0); + mf.addAtom(*_got1); } for (auto &got : _gotVector) { got->setOrdinal(ordinal++); - mf->addAtom(*got); + mf.addAtom(*got); + } + // Add any tlsdesc GOT relocation after default PLT and iFUNC entries. + for (auto &tlsdesc : _tlsdescVector) { + tlsdesc->setOrdinal(ordinal++); + mf.addAtom(*tlsdesc); } for (auto obj : _objectVector) { obj->setOrdinal(ordinal++); - mf->addAtom(*obj); + mf.addAtom(*obj); } + + return std::error_code(); } protected: @@ -333,18 +417,19 @@ protected: /// \brief the list of GOT/PLT atoms std::vector<GOTAtom *> _gotVector; + std::vector<GOTAtom *> _tlsdescVector; std::vector<PLTAtom *> _pltVector; std::vector<ObjectAtom *> _objectVector; /// \brief GOT entry that is always 0. Used for undefined weaks. - GOTAtom *_null; + GOTAtom *_null = nullptr; /// \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; + PLT0Atom *_plt0 = nullptr; + GOTAtom *_got0 = nullptr; + GOTAtom *_got1 = nullptr; /// @} }; @@ -394,31 +479,31 @@ public: : AArch64RelocationPass(ctx) {} const PLT0Atom *getPLT0() { - if (_PLT0) - return _PLT0; + 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); + _plt0 = new (_file._alloc) AArch64PLT0Atom(_file); + _got0 = new (_file._alloc) AArch64GOTPLTAtom(_file); + _got1 = new (_file._alloc) AArch64GOTPLTAtom(_file); + _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"; + _plt0->_name = "__PLT0"; _got0->_name = "__got0"; _got1->_name = "__got1"; #endif - return _PLT0; + 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"); + auto ga = new (_file._alloc) AArch64GOTPLTAtom(_file); ga->addReferenceELF_AArch64(R_AARCH64_JUMP_SLOT, 0, a, 0); - auto pa = new (_file._alloc) AArch64PLTAtom(_file, ".plt"); + auto pa = new (_file._alloc) AArch64PLTAtom(_file); 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); @@ -485,7 +570,7 @@ public: const GOTAtom *getSharedGOT(const SharedLibraryAtom *sla) { auto got = _gotMap.find(sla); if (got == _gotMap.end()) { - auto g = new (_file._alloc) AArch64GOTAtom(_file, ".got"); + auto g = new (_file._alloc) AArch64GOTAtom(_file); g->addReferenceELF_AArch64(R_AARCH64_GLOB_DAT, 0, sla, 0); #ifndef NDEBUG g->_name = "__got_"; diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64SectionChunks.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64SectionChunks.cpp new file mode 100644 index 0000000000000..2734bcdbda5fd --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64SectionChunks.cpp @@ -0,0 +1,39 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64SectionChunks.cpp --------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AArch64SectionChunks.h" +#include "TargetLayout.h" + +namespace lld { +namespace elf { + +AArch64GOTSection::AArch64GOTSection(const ELFLinkingContext &ctx, + StringRef name, int32_t order) + : AtomSection<ELF64LE>(ctx, name, DefinedAtom::typeGOT, DefinedAtom::permRW_, + order) { + _alignment = 8; +} + +const AtomLayout *AArch64GOTSection::appendAtom(const Atom *atom) { + const DefinedAtom *da = dyn_cast<DefinedAtom>(atom); + + for (const auto &r : *da) { + if (r->kindNamespace() != Reference::KindNamespace::ELF) + continue; + assert(r->kindArch() == Reference::KindArch::AArch64); + if ((r->kindValue() == R_AARCH64_TLS_TPREL64) || + (r->kindValue() == R_AARCH64_TLSDESC)) + _tlsMap[r->target()] = _tlsMap.size(); + } + + return AtomSection<ELF64LE>::appendAtom(atom); +} + +} // elf +} // lld diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64SectionChunks.h b/lib/ReaderWriter/ELF/AArch64/AArch64SectionChunks.h new file mode 100644 index 0000000000000..2b7594c2db84b --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64SectionChunks.h @@ -0,0 +1,37 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64SectionChunks.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_SECTION_CHUNKS_H +#define LLD_READER_WRITER_ELF_AARCH64_AARCH64_SECTION_CHUNKS_H + +#include "TargetLayout.h" + +namespace lld { +namespace elf { + +class AArch64GOTSection : public AtomSection<ELF64LE> { +public: + AArch64GOTSection(const ELFLinkingContext &ctx, StringRef name, + int32_t order); + + bool hasGlobalGOTEntry(const Atom *a) const { + return _tlsMap.count(a); + } + + const AtomLayout *appendAtom(const Atom *atom) override; + +private: + /// \brief Map TLS Atoms to their GOT entry index. + llvm::DenseMap<const Atom *, std::size_t> _tlsMap; +}; + +} // elf +} // lld + +#endif diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.cpp index 607f767f8b8a4..083b492c1607c 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.cpp +++ b/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.cpp @@ -12,41 +12,40 @@ #include "AArch64ExecutableWriter.h" #include "AArch64LinkingContext.h" #include "AArch64TargetHandler.h" +#include "AArch64SectionChunks.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); +AArch64TargetLayout::AArch64TargetLayout(ELFLinkingContext &ctx) : + TargetLayout(ctx) {} + +AtomSection<ELF64LE> *AArch64TargetLayout::createSection( + StringRef name, int32_t type, DefinedAtom::ContentPermissions permissions, + TargetLayout<ELF64LE>::SectionOrder order) { + if (type == DefinedAtom::typeGOT && (name == ".got" || name == ".got.plt")) { + auto section = new (this->_allocator) AArch64GOTSection(this->_ctx, name, + order); + _gotSections.push_back(section); + return section; + } + return TargetLayout<ELF64LE>::createSection(name, type, permissions, order); } + +AArch64TargetHandler::AArch64TargetHandler(AArch64LinkingContext &ctx) + : _ctx(ctx), _targetLayout(new AArch64TargetLayout(ctx)), + _relocationHandler(new AArch64TargetRelocationHandler(*_targetLayout)) {} + std::unique_ptr<Writer> AArch64TargetHandler::getWriter() { - switch (this->_context.getOutputELFType()) { + switch (this->_ctx.getOutputELFType()) { case llvm::ELF::ET_EXEC: - return std::unique_ptr<Writer>(new AArch64ExecutableWriter<AArch64ELFType>( - _context, *_AArch64TargetLayout.get())); + return llvm::make_unique<AArch64ExecutableWriter>(_ctx, *_targetLayout); case llvm::ELF::ET_DYN: - return std::unique_ptr<Writer>( - new AArch64DynamicLibraryWriter<AArch64ELFType>( - _context, *_AArch64TargetLayout.get())); + return llvm::make_unique<AArch64DynamicLibraryWriter>(_ctx, *_targetLayout); 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 index 4eb6786cdf1fb..c0ecbfa9e44b6 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.h +++ b/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.h @@ -10,52 +10,78 @@ #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 "ELFReader.h" #include "TargetLayout.h" #include "lld/Core/Simple.h" namespace lld { namespace elf { + class AArch64LinkingContext; +class AArch64GOTSection; -template <class ELFT> class AArch64TargetLayout : public TargetLayout<ELFT> { -public: - AArch64TargetLayout(AArch64LinkingContext &context) - : TargetLayout<ELFT>(context) {} -}; +class AArch64TargetLayout final : public TargetLayout<ELF64LE> { + typedef llvm::object::Elf_Shdr_Impl<ELF64LE> Elf_Shdr; -class AArch64TargetHandler final : public DefaultTargetHandler<AArch64ELFType> { public: - AArch64TargetHandler(AArch64LinkingContext &context); + AArch64TargetLayout(ELFLinkingContext &ctx); + + AtomSection<ELF64LE> * + createSection(StringRef name, int32_t type, + DefinedAtom::ContentPermissions permissions, + TargetLayout<ELF64LE>::SectionOrder order) override; + + const std::vector<AArch64GOTSection *> &getGOTSections() const { + return _gotSections; + } - AArch64TargetLayout<AArch64ELFType> &getTargetLayout() override { - return *(_AArch64TargetLayout.get()); + uint64_t getTPOffset() { + std::call_once(_tpOffOnce, [this]() { + for (const auto &phdr : *_programHeader) { + if (phdr->p_type == llvm::ELF::PT_TLS) { + _tpOff = llvm::RoundUpToAlignment(TCB_SIZE, phdr->p_align); + break; + } + } + assert(_tpOff != 0 && "TLS segment not found"); + }); + return _tpOff; } - void registerRelocationNames(Registry ®istry) override; +private: + enum { + TCB_SIZE = 16, + }; + +private: + std::vector<AArch64GOTSection *> _gotSections; + uint64_t _tpOff = 0; + std::once_flag _tpOffOnce; +}; + +class AArch64TargetHandler final : public TargetHandler { +public: + AArch64TargetHandler(AArch64LinkingContext &ctx); - const AArch64TargetRelocationHandler &getRelocationHandler() const override { - return *(_AArch64RelocationHandler.get()); + const TargetRelocationHandler &getRelocationHandler() const override { + return *_relocationHandler; } std::unique_ptr<Reader> getObjReader() override { - return std::unique_ptr<Reader>(new AArch64ELFObjectReader(_context)); + return llvm::make_unique<ELFReader<ELFFile<ELF64LE>>>(_ctx); } std::unique_ptr<Reader> getDSOReader() override { - return std::unique_ptr<Reader>(new AArch64ELFDSOReader(_context)); + return llvm::make_unique<ELFReader<DynamicFile<ELF64LE>>>(_ctx); } 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; + AArch64LinkingContext &_ctx; + std::unique_ptr<AArch64TargetLayout> _targetLayout; + std::unique_ptr<AArch64TargetRelocationHandler> _relocationHandler; }; } // end namespace elf diff --git a/lib/ReaderWriter/ELF/AArch64/CMakeLists.txt b/lib/ReaderWriter/ELF/AArch64/CMakeLists.txt index de94a4df5078a..2347dda9adb09 100644 --- a/lib/ReaderWriter/ELF/AArch64/CMakeLists.txt +++ b/lib/ReaderWriter/ELF/AArch64/CMakeLists.txt @@ -3,6 +3,8 @@ add_llvm_library(lldAArch64ELFTarget AArch64TargetHandler.cpp AArch64RelocationHandler.cpp AArch64RelocationPass.cpp + AArch64ExecutableWriter.cpp + AArch64SectionChunks.cpp LINK_LIBS lldELF lldReaderWriter diff --git a/lib/ReaderWriter/ELF/AArch64/Makefile b/lib/ReaderWriter/ELF/AArch64/Makefile deleted file mode 100644 index 02cff4747d0d2..0000000000000 --- a/lib/ReaderWriter/ELF/AArch64/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- 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/AMDGPU/AMDGPUExecutableWriter.cpp b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUExecutableWriter.cpp new file mode 100644 index 0000000000000..89efeb23d6f88 --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUExecutableWriter.cpp @@ -0,0 +1,34 @@ +//===- lib/ReaderWriter/ELF/AMDGPU/AMDGPUExecutableWriter.cpp -------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AMDGPUExecutableWriter.h" + +using namespace lld; +using namespace lld::elf; + +AMDGPUExecutableWriter::AMDGPUExecutableWriter(AMDGPULinkingContext &ctx, + AMDGPUTargetLayout &layout) + : ExecutableWriter(ctx, layout), _ctx(ctx) {} + +void AMDGPUExecutableWriter::createImplicitFiles( + std::vector<std::unique_ptr<File>> &Result) { + // ExecutableWriter::createImplicitFiles() adds C runtime symbols that we + // don't need, so we use the OutputELFWriter implementation instead. + OutputELFWriter<ELF64LE>::createImplicitFiles(Result); +} + +void AMDGPUExecutableWriter::finalizeDefaultAtomValues() { + + // ExecutableWriter::finalizeDefaultAtomValues() assumes the presence of + // C runtime symbols. However, since we skip the call to + // ExecutableWriter::createImplicitFiles(), these symbols are never added + // and ExectuableWriter::finalizeDefaultAtomValues() will crash if we call + // it. + OutputELFWriter<ELF64LE>::finalizeDefaultAtomValues(); +} diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPUExecutableWriter.h b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUExecutableWriter.h new file mode 100644 index 0000000000000..accc00b8a0548 --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUExecutableWriter.h @@ -0,0 +1,41 @@ +//===- lib/ReaderWriter/ELF/AMDGPU/AMDGPUExecutableWriter.h ---------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef AMDGPU_EXECUTABLE_WRITER_H +#define AMDGPU_EXECUTABLE_WRITER_H + +#include "ExecutableWriter.h" +#include "AMDGPULinkingContext.h" +#include "AMDGPUSymbolTable.h" +#include "AMDGPUTargetHandler.h" + +namespace lld { +namespace elf { + +class AMDGPUTargetLayout; + +class AMDGPUExecutableWriter : public ExecutableWriter<ELF64LE> { +public: + AMDGPUExecutableWriter(AMDGPULinkingContext &ctx, AMDGPUTargetLayout &layout); + + unique_bump_ptr<SymbolTable<ELF64LE>> createSymbolTable() override { + return unique_bump_ptr<SymbolTable<ELF64LE>>(new (this->_alloc) + AMDGPUSymbolTable(_ctx)); + } + + void createImplicitFiles(std::vector<std::unique_ptr<File>> &Result) override; + void finalizeDefaultAtomValues() override; + +private: + AMDGPULinkingContext &_ctx; +}; + +} // namespace elf +} // namespace lld + +#endif // AMDGPU_EXECUTABLE_WRITER_H diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPULinkingContext.cpp b/lib/ReaderWriter/ELF/AMDGPU/AMDGPULinkingContext.cpp new file mode 100644 index 0000000000000..b1e83641fa82b --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPULinkingContext.cpp @@ -0,0 +1,41 @@ +//===- lib/ReaderWriter/ELF/AMDGPU/AMDGPULinkingContext.cpp ---------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===------------------------------------------------------------------------===// + +#include "AMDGPULinkingContext.h" +#include "AMDGPUTargetHandler.h" + +namespace lld { +namespace elf { + +std::unique_ptr<ELFLinkingContext> +createAMDGPULinkingContext(llvm::Triple triple) { + if (triple.getArch() == llvm::Triple::amdgcn) + return llvm::make_unique<AMDGPULinkingContext>(triple); + return nullptr; +} + +AMDGPULinkingContext::AMDGPULinkingContext(llvm::Triple triple) + : ELFLinkingContext(triple, llvm::make_unique<AMDGPUTargetHandler>(*this)) { +} + +static const Registry::KindStrings kindStrings[] = {LLD_KIND_STRING_END}; + +void AMDGPULinkingContext::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, + Reference::KindArch::AMDGPU, kindStrings); +} + +void setAMDGPUELFHeader(ELFHeader<ELF64LE> &elfHeader) { + elfHeader.e_ident(llvm::ELF::EI_OSABI, ELFOSABI_AMDGPU_HSA); +} + +StringRef AMDGPULinkingContext::entrySymbolName() const { return ""; } + +} // namespace elf +} // namespace lld diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPULinkingContext.h b/lib/ReaderWriter/ELF/AMDGPU/AMDGPULinkingContext.h new file mode 100644 index 0000000000000..1cc7a3c7694fb --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPULinkingContext.h @@ -0,0 +1,36 @@ +//===- lib/ReaderWriter/ELF/AMDGPU/AMDGPULinkingContext.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_AMDGPU_AMDGPU_LINKING_CONTEXT_H +#define LLD_READER_WRITER_ELF_AMDGPU_AMDGPU_LINKING_CONTEXT_H + +#include "OutputELFWriter.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { + +class AMDGPULinkingContext final : public ELFLinkingContext { +public: + AMDGPULinkingContext(llvm::Triple triple); + int getMachineType() const override { return llvm::ELF::EM_AMDGPU; } + + void registerRelocationNames(Registry &r) override; + + StringRef entrySymbolName() const override; +}; + +void setAMDGPUELFHeader(ELFHeader<ELF64LE> &elfHeader); + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_AMDGPU_AMDGPU_LINKING_CONTEXT_H diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPURelocationHandler.cpp b/lib/ReaderWriter/ELF/AMDGPU/AMDGPURelocationHandler.cpp new file mode 100644 index 0000000000000..ca5a77db91776 --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPURelocationHandler.cpp @@ -0,0 +1,19 @@ +//===- lib/ReaderWriter/ELF/AMDGPU/AMDGPURelocationHandler.cpp -----------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AMDGPURelocationHandler.h" + +using namespace lld; +using namespace lld::elf; + +std::error_code AMDGPUTargetRelocationHandler::applyRelocation( + ELFWriter &writer, llvm::FileOutputBuffer &buf, const AtomLayout &atom, + const Reference &ref) const { + return std::error_code(); +} diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPURelocationHandler.h b/lib/ReaderWriter/ELF/AMDGPU/AMDGPURelocationHandler.h new file mode 100644 index 0000000000000..90d37274aebfd --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPURelocationHandler.h @@ -0,0 +1,31 @@ +//===- lld/ReaderWriter/ELF/AMDGPU/AMDGPURelocationHandler.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_AMDGPU_AMDGPU_RELOCATION_HANDLER_H +#define LLD_READER_WRITER_ELF_AMDGPU_AMDGPU_RELOCATION_HANDLER_H + +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include <system_error> + +namespace lld { +namespace elf { +class AMDGPUTargetHandler; +class AMDGPUTargetLayout; + +class AMDGPUTargetRelocationHandler final : public TargetRelocationHandler { +public: + AMDGPUTargetRelocationHandler(AMDGPUTargetLayout &layout) { } + + std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, + const AtomLayout &, + const Reference &) const override; + +}; +} // elf +} // lld +#endif diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPUSymbolTable.cpp b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUSymbolTable.cpp new file mode 100644 index 0000000000000..0824974d46022 --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUSymbolTable.cpp @@ -0,0 +1,32 @@ +//===--------- lib/ReaderWriter/ELF/AMDGPU/AMDGPUSymbolTable.cpp ----------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AMDGPUSymbolTable.h" +#include "ELFFile.h" +#include "Atoms.h" +#include "SectionChunks.h" + +using namespace lld; +using namespace lld::elf; + +AMDGPUSymbolTable::AMDGPUSymbolTable(const ELFLinkingContext &ctx) + : SymbolTable(ctx, ".symtab", TargetLayout<ELF64LE>::ORDER_SYMBOL_TABLE) {} + +void AMDGPUSymbolTable::addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr) { + SymbolTable::addDefinedAtom(sym, da, addr); + + // FIXME: Only do this for kernel functions. + sym.setType(STT_AMDGPU_HSA_KERNEL); + + // Make st_value section relative. + // FIXME: This is hack to give kernel symbols a section relative offset. + // Because of this hack only on kernel can be included in a binary file. + sym.st_value = 0; +} diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPUSymbolTable.h b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUSymbolTable.h new file mode 100644 index 0000000000000..41c3be5cb38fe --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUSymbolTable.h @@ -0,0 +1,32 @@ +//===--------- lib/ReaderWriter/ELF/AMDGPU/AMDGPUSymbolTable.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_AMDGPU_AMDGPU_SYMBOL_TABLE_H +#define LLD_READER_WRITER_ELF_AMDGPU_AMDGPU_SYMBOL_TABLE_H + +#include "TargetLayout.h" + +namespace lld { +namespace elf { + +/// \brief The SymbolTable class represents the symbol table in a ELF file +class AMDGPUSymbolTable : public SymbolTable<ELF64LE> { +public: + typedef llvm::object::Elf_Sym_Impl<ELF64LE> Elf_Sym; + + AMDGPUSymbolTable(const ELFLinkingContext &ctx); + + void addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr) override; +}; + +} // elf +} // lld + +#endif diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPUTargetHandler.cpp b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUTargetHandler.cpp new file mode 100644 index 0000000000000..ff4b600158bd4 --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUTargetHandler.cpp @@ -0,0 +1,65 @@ +//===- lib/ReaderWriter/ELF/AMDGPU/AMDGPUTargetHandler.cpp -------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TargetLayout.h" +#include "AMDGPUExecutableWriter.h" +#include "AMDGPULinkingContext.h" +#include "AMDGPUTargetHandler.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { + +AMDGPUTargetHandler::AMDGPUTargetHandler(AMDGPULinkingContext &ctx) + : _ctx(ctx), _targetLayout(new AMDGPUTargetLayout(ctx)), + _relocationHandler(new AMDGPUTargetRelocationHandler(*_targetLayout)) {} + +std::unique_ptr<Writer> AMDGPUTargetHandler::getWriter() { + switch (_ctx.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + return llvm::make_unique<AMDGPUExecutableWriter>(_ctx, *_targetLayout); + case llvm::ELF::ET_DYN: + llvm_unreachable("TODO: support dynamic libraries"); + case llvm::ELF::ET_REL: + llvm_unreachable("TODO: support -r mode"); + default: + llvm_unreachable("unsupported output type"); + } +} + +HSATextSection::HSATextSection(const ELFLinkingContext &ctx) + : AtomSection(ctx, ".hsatext", DefinedAtom::typeCode, 0, 0) { + _type = SHT_PROGBITS; + _flags = SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR | SHF_AMDGPU_HSA_AGENT | + SHF_AMDGPU_HSA_CODE; + + // FIXME: What alignment should we use here? + _alignment = 4096; +} + +void AMDGPUTargetLayout::assignSectionsToSegments() { + + TargetLayout::assignSectionsToSegments(); + for (OutputSection<ELF64LE> *osi : _outputSections) { + for (Section<ELF64LE> *section : osi->sections()) { + StringRef InputSectionName = section->inputSectionName(); + if (InputSectionName != ".hsatext") + continue; + + auto *segment = new (_allocator) Segment<ELF64LE>( + _ctx, "PT_AMDGPU_HSA_LOAD_CODE_AGENT", PT_AMDGPU_HSA_LOAD_CODE_AGENT); + _segments.push_back(segment); + assert(segment); + segment->append(section); + } + } +} + +} // namespace elf +} // namespace lld diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPUTargetHandler.h b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUTargetHandler.h new file mode 100644 index 0000000000000..8d0f70b6e7f7b --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUTargetHandler.h @@ -0,0 +1,80 @@ +//===- lib/ReaderWriter/ELF/AMDGPU/AMDGPUTargetHandler.h ------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef AMDGPU_TARGET_HANDLER_H +#define AMDGPU_TARGET_HANDLER_H + +#include "ELFFile.h" +#include "ELFReader.h" +#include "AMDGPURelocationHandler.h" +#include "TargetLayout.h" + +namespace lld { +namespace elf { +class AMDGPULinkingContext; + +class HSATextSection : public AtomSection<ELF64LE> { +public: + HSATextSection(const ELFLinkingContext &ctx); +}; + +/// \brief TargetLayout for AMDGPU +class AMDGPUTargetLayout final : public TargetLayout<ELF64LE> { +public: + AMDGPUTargetLayout(AMDGPULinkingContext &ctx) : TargetLayout(ctx) {} + + void assignSectionsToSegments() override; + + /// \brief Gets or creates a section. + AtomSection<ELF64LE> * + createSection(StringRef name, int32_t contentType, + DefinedAtom::ContentPermissions contentPermissions, + TargetLayout::SectionOrder sectionOrder) override { + if (name == ".hsatext") + return new (_allocator) HSATextSection(_ctx); + + if (name == ".note") + contentType = DefinedAtom::typeRONote; + + return TargetLayout::createSection(name, contentType, contentPermissions, + sectionOrder); + } +}; + +/// \brief TargetHandler for AMDGPU +class AMDGPUTargetHandler final : public TargetHandler { +public: + AMDGPUTargetHandler(AMDGPULinkingContext &targetInfo); + + const TargetRelocationHandler &getRelocationHandler() const override { + return *_relocationHandler; + } + + std::unique_ptr<Reader> getObjReader() override { + return llvm::make_unique<ELFReader<ELFFile<ELF64LE>>>(_ctx); + } + + std::unique_ptr<Reader> getDSOReader() override { + return llvm::make_unique<ELFReader<DynamicFile<ELF64LE>>>(_ctx); + } + + std::unique_ptr<Writer> getWriter() override; + +private: + AMDGPULinkingContext &_ctx; + std::unique_ptr<AMDGPUTargetLayout> _targetLayout; + std::unique_ptr<AMDGPUTargetRelocationHandler> _relocationHandler; +}; + +void finalizeAMDGPURuntimeAtomValues(AMDGPUTargetLayout &layout); + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/AMDGPU/CMakeLists.txt b/lib/ReaderWriter/ELF/AMDGPU/CMakeLists.txt new file mode 100644 index 0000000000000..9c9cc10fe3972 --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/CMakeLists.txt @@ -0,0 +1,13 @@ +add_llvm_library(lldAMDGPUELFTarget + AMDGPUExecutableWriter.cpp + AMDGPULinkingContext.cpp + AMDGPURelocationHandler.cpp + AMDGPUSymbolTable.cpp + AMDGPUTargetHandler.cpp + LINK_LIBS + lldELF + lldReaderWriter + lldCore + LLVMObject + LLVMSupport + ) diff --git a/lib/ReaderWriter/ELF/ARM/ARMDynamicLibraryWriter.h b/lib/ReaderWriter/ELF/ARM/ARMDynamicLibraryWriter.h new file mode 100644 index 0000000000000..da843b97abc0c --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/ARMDynamicLibraryWriter.h @@ -0,0 +1,49 @@ +//===- lib/ReaderWriter/ELF/ARM/ARMDynamicLibraryWriter.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_DYNAMIC_LIBRARY_WRITER_H +#define LLD_READER_WRITER_ELF_ARM_ARM_DYNAMIC_LIBRARY_WRITER_H + +#include "DynamicLibraryWriter.h" +#include "ARMELFWriters.h" +#include "ARMLinkingContext.h" +#include "ARMTargetHandler.h" + +namespace lld { +namespace elf { + +class ARMDynamicLibraryWriter + : public ARMELFWriter<DynamicLibraryWriter<ELF32LE>> { +public: + ARMDynamicLibraryWriter(ARMLinkingContext &ctx, ARMTargetLayout &layout); + +protected: + // Add any runtime files and their atoms to the output + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; + +private: + ARMLinkingContext &_ctx; +}; + +ARMDynamicLibraryWriter::ARMDynamicLibraryWriter(ARMLinkingContext &ctx, + ARMTargetLayout &layout) + : ARMELFWriter(ctx, layout), _ctx(ctx) {} + +void ARMDynamicLibraryWriter::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + DynamicLibraryWriter::createImplicitFiles(result); + auto file = llvm::make_unique<RuntimeFile<ELF32LE>>(_ctx, "ARM dynamic file"); + file->addAbsoluteAtom(gotSymbol); + file->addAbsoluteAtom(dynamicSymbol); + result.push_back(std::move(file)); +} + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_ELF_ARM_ARM_DYNAMIC_LIBRARY_WRITER_H diff --git a/lib/ReaderWriter/ELF/ARM/ARMELFFile.h b/lib/ReaderWriter/ELF/ARM/ARMELFFile.h index bc5ee35b8213b..8f5477017e551 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMELFFile.h +++ b/lib/ReaderWriter/ELF/ARM/ARMELFFile.h @@ -17,53 +17,95 @@ 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; +class ARMELFBaseDefinedAtom : public ELFDefinedAtom<ELF32LE> { +public: + /// The values of custom content type enum must not interfere + /// with ones in base defined atom class' enum. + enum ARMContentType { + typeARMExidx = 0x1000, // Identifies ARM_EXIDX section + }; + + template <typename... T> + ARMELFBaseDefinedAtom(T &&... args) + : ELFDefinedAtom<ELF32LE>(std::forward<T>(args)...) {} + + DefinedAtom::ContentPermissions permissions() const override { + if (_permissions != DefinedAtom::permUnknown) + return _permissions; + + switch (_section->sh_type) { + case llvm::ELF::SHT_ARM_EXIDX: + return _permissions = permR__; + } + return ELFDefinedAtom::permissions(); + } + + DefinedAtom::ContentType contentType() const override { + if (_contentType != DefinedAtom::typeUnknown) + return _contentType; + + switch (_section->sh_type) { + case llvm::ELF::SHT_ARM_EXIDX: + return _contentType = (DefinedAtom::ContentType)typeARMExidx; + } + return ELFDefinedAtom::contentType(); + } +}; + +class ARMELFMappingAtom : public ARMELFBaseDefinedAtom { +public: + template <typename... T> + ARMELFMappingAtom(DefinedAtom::CodeModel model, T &&... args) + : ARMELFBaseDefinedAtom(std::forward<T>(args)...), _model(model) {} + + DefinedAtom::CodeModel codeModel() const override { return _model; } + +private: + DefinedAtom::CodeModel _model; +}; +class ARMELFDefinedAtom : public ARMELFBaseDefinedAtom { 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 { + template <typename... T> + ARMELFDefinedAtom(T &&... args) + : ARMELFBaseDefinedAtom(std::forward<T>(args)...) {} + + bool isThumbFunc() const { + const auto *symbol = _symbol; return symbol->getType() == llvm::ELF::STT_FUNC && - (static_cast<uint64_t>(symbol->st_value) & 0x1); + (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; + uint64_t getSymbolValue() const override { + const auto value = static_cast<uint64_t>(_symbol->st_value); + return isThumbFunc() ? value & ~0x1 : value; } DefinedAtom::CodeModel codeModel() const override { - if (isThumbFunc(this->_symbol)) - return DefinedAtom::codeARMThumb; - return DefinedAtom::codeNA; + return isThumbFunc() ? DefinedAtom::codeARMThumb : 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) {} +class ARMELFFile : public ELFFile<ELF32LE> { + typedef llvm::object::Elf_Rel_Impl<ELF32LE, false> Elf_Rel; - 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)); +public: + ARMELFFile(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx) + : ELFFile(std::move(mb), ctx) {} + +protected: + /// Returns initial addend; for ARM it is 0, because it is read + /// during the relocations applying + Reference::Addend getInitialAddend(ArrayRef<uint8_t>, uint64_t, + const Elf_Rel &) const override { + return 0; } private: - typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; - typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + typedef llvm::object::Elf_Sym_Impl<ELF32LE> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELF32LE> Elf_Shdr; /// Correct st_value for symbols addressing Thumb instructions /// by removing its zero bit. @@ -73,24 +115,39 @@ private: } /// 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>( + ELFDefinedAtom<ELF32LE> *createDefinedAtom( + 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<ELF32LE> *> &referenceList) override { + if (symName.size() >= 2 && symName[0] == '$') { + switch (symName[1]) { + case 'a': + return new (_readerStorage) + ARMELFMappingAtom(DefinedAtom::codeARM_a, *this, symName, + sectionName, sym, sectionHdr, contentData, + referenceStart, referenceEnd, referenceList); + case 'd': + return new (_readerStorage) + ARMELFMappingAtom(DefinedAtom::codeARM_d, *this, symName, + sectionName, sym, sectionHdr, contentData, + referenceStart, referenceEnd, referenceList); + case 't': + return new (_readerStorage) + ARMELFMappingAtom(DefinedAtom::codeARM_t, *this, symName, + sectionName, sym, sectionHdr, contentData, + referenceStart, referenceEnd, referenceList); + default: + // Fall through and create regular defined atom. + break; + } + } + return new (_readerStorage) ARMELFDefinedAtom( *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 diff --git a/lib/ReaderWriter/ELF/ARM/ARMELFReader.h b/lib/ReaderWriter/ELF/ARM/ARMELFReader.h deleted file mode 100644 index 31af531563ea5..0000000000000 --- a/lib/ReaderWriter/ELF/ARM/ARMELFReader.h +++ /dev/null @@ -1,62 +0,0 @@ -//===--------- 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/ARMELFWriters.h b/lib/ReaderWriter/ELF/ARM/ARMELFWriters.h new file mode 100644 index 0000000000000..a842ebe530380 --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/ARMELFWriters.h @@ -0,0 +1,120 @@ +//===- lib/ReaderWriter/ELF/ARM/ARMELFWriters.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_WRITERS_H +#define LLD_READER_WRITER_ELF_ARM_ARM_ELF_WRITERS_H + +#include "ARMLinkingContext.h" +#include "ARMSymbolTable.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { + +template <class WriterT> class ARMELFWriter : public WriterT { +public: + ARMELFWriter(ARMLinkingContext &ctx, TargetLayout<ELF32LE> &layout); + + void finalizeDefaultAtomValues() override; + + /// \brief Create symbol table. + unique_bump_ptr<SymbolTable<ELF32LE>> createSymbolTable() override; + + // Setup the ELF header. + std::error_code setELFHeader() override; + +protected: + static const char *gotSymbol; + static const char *dynamicSymbol; + +private: + ARMLinkingContext &_ctx; + TargetLayout<ELF32LE> &_armLayout; +}; + +template <class WriterT> +const char *ARMELFWriter<WriterT>::gotSymbol = "_GLOBAL_OFFSET_TABLE_"; +template <class WriterT> +const char *ARMELFWriter<WriterT>::dynamicSymbol = "_DYNAMIC"; + +template <class WriterT> +ARMELFWriter<WriterT>::ARMELFWriter(ARMLinkingContext &ctx, + TargetLayout<ELF32LE> &layout) + : WriterT(ctx, layout), _ctx(ctx), _armLayout(layout) {} + +template <class WriterT> +void ARMELFWriter<WriterT>::finalizeDefaultAtomValues() { + // Finalize the atom values that are part of the parent. + WriterT::finalizeDefaultAtomValues(); + + if (auto *gotAtom = _armLayout.findAbsoluteAtom(gotSymbol)) { + 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; + } + + if (auto *dynamicAtom = _armLayout.findAbsoluteAtom(dynamicSymbol)) { + if (auto dynamicSection = _armLayout.findOutputSection(".dynamic")) + dynamicAtom->_virtualAddr = dynamicSection->virtualAddr(); + else + dynamicAtom->_virtualAddr = 0; + } + + // Set required by gcc libc __ehdr_start symbol with pointer to ELF header + if (auto ehdr = _armLayout.findAbsoluteAtom("__ehdr_start")) + ehdr->_virtualAddr = this->_elfHeader->virtualAddr(); + + // Set required by gcc libc symbols __exidx_start/__exidx_end + this->updateScopeAtomValues("exidx", ".ARM.exidx"); +} + +template <class WriterT> +unique_bump_ptr<SymbolTable<ELF32LE>> +ARMELFWriter<WriterT>::createSymbolTable() { + return unique_bump_ptr<SymbolTable<ELF32LE>>(new (this->_alloc) + ARMSymbolTable(_ctx)); +} + +template <class WriterT> std::error_code ARMELFWriter<WriterT>::setELFHeader() { + if (std::error_code ec = WriterT::setELFHeader()) + return ec; + + // Set ARM-specific flags. + this->_elfHeader->e_flags(llvm::ELF::EF_ARM_EABI_VER5 | + llvm::ELF::EF_ARM_VFP_FLOAT); + + StringRef entryName = _ctx.entrySymbolName(); + if (const AtomLayout *al = _armLayout.findAtomLayoutByName(entryName)) { + if (const auto *ea = dyn_cast<DefinedAtom>(al->_atom)) { + switch (ea->codeModel()) { + case DefinedAtom::codeNA: + if (al->_virtualAddr & 0x3) { + llvm::report_fatal_error( + "Two least bits must be zero for ARM entry point"); + } + break; + case DefinedAtom::codeARMThumb: + // Fixup entry point for Thumb code. + this->_elfHeader->e_entry(al->_virtualAddr | 0x1); + break; + default: + llvm_unreachable("Wrong code model of entry point atom"); + } + } + } + + return std::error_code(); +} + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_ELF_ARM_ARM_ELF_WRITERS_H diff --git a/lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h b/lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h index 19311d516e4de..974dab63a1265 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h +++ b/lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h @@ -10,111 +10,58 @@ #define LLD_READER_WRITER_ELF_ARM_ARM_EXECUTABLE_WRITER_H #include "ExecutableWriter.h" +#include "ARMELFWriters.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> { +class ARMExecutableWriter : public ARMELFWriter<ExecutableWriter<ELF32LE>> { public: - ARMExecutableWriter(ARMLinkingContext &context, - ARMTargetLayout<ELFT> &layout); + ARMExecutableWriter(ARMLinkingContext &ctx, ARMTargetLayout &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 createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; void processUndefinedSymbol(StringRef symName, - RuntimeFile<ELFT> &file) const override; - - // Setup the ELF header. - std::error_code setELFHeader() override; + RuntimeFile<ELF32LE> &file) const override; private: - ARMLinkingContext &_context; - ARMTargetLayout<ELFT> &_armLayout; + ARMLinkingContext &_ctx; }; -template <class ELFT> -ARMExecutableWriter<ELFT>::ARMExecutableWriter(ARMLinkingContext &context, - ARMTargetLayout<ELFT> &layout) - : ExecutableWriter<ELFT>(context, layout), _context(context), - _armLayout(layout) {} +ARMExecutableWriter::ARMExecutableWriter(ARMLinkingContext &ctx, + ARMTargetLayout &layout) + : ARMELFWriter(ctx, layout), _ctx(ctx) {} -template <class ELFT> -bool ARMExecutableWriter<ELFT>::createImplicitFiles( +void ARMExecutableWriter::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; + ExecutableWriter::createImplicitFiles(result); + // Add default atoms for ARM. + if (_ctx.isDynamic()) { + auto file = llvm::make_unique<RuntimeFile<ELF32LE>>(_ctx, "ARM exec file"); + file->addAbsoluteAtom(gotSymbol); + file->addAbsoluteAtom(dynamicSymbol); + result.push_back(std::move(file)); } - // 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 { +void ARMExecutableWriter::processUndefinedSymbol( + StringRef symName, RuntimeFile<ELF32LE> &file) const { + ARMELFWriter<ExecutableWriter<ELF32LE>>::processUndefinedSymbol(symName, + file); if (symName == gotSymbol) { file.addAbsoluteAtom(gotSymbol); } else if (symName.startswith("__exidx")) { file.addAbsoluteAtom("__exidx_start"); file.addAbsoluteAtom("__exidx_end"); + } else if (symName == "__ehdr_start") { + file.addAbsoluteAtom("__ehdr_start"); } } -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 diff --git a/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.cpp b/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.cpp index 5f24366742684..74905b47820fb 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.cpp +++ b/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.cpp @@ -11,24 +11,54 @@ #include "ARMRelocationPass.h" #include "ARMTargetHandler.h" -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { std::unique_ptr<ELFLinkingContext> -elf::ARMLinkingContext::create(llvm::Triple triple) { +createARMLinkingContext(llvm::Triple triple) { if (triple.getArch() == llvm::Triple::arm) - return std::unique_ptr<ELFLinkingContext>( - new elf::ARMLinkingContext(triple)); + return llvm::make_unique<ARMLinkingContext>(triple); return nullptr; } -elf::ARMLinkingContext::ARMLinkingContext(llvm::Triple triple) - : ELFLinkingContext(triple, std::unique_ptr<TargetHandlerBase>( - new ARMTargetHandler(*this))) {} +ARMLinkingContext::ARMLinkingContext(llvm::Triple triple) + : ELFLinkingContext(triple, llvm::make_unique<ARMTargetHandler>(*this)) {} -void elf::ARMLinkingContext::addPasses(PassManager &pm) { +void ARMLinkingContext::addPasses(PassManager &pm) { auto pass = createARMRelocationPass(*this); if (pass) pm.add(std::move(pass)); ELFLinkingContext::addPasses(pm); } + +bool isARMCode(const DefinedAtom *atom) { + return isARMCode(atom->codeModel()); +} + +bool isARMCode(DefinedAtom::CodeModel codeModel) { + return !isThumbCode(codeModel); +} + +bool isThumbCode(const DefinedAtom *atom) { + return isThumbCode(atom->codeModel()); +} + +bool isThumbCode(DefinedAtom::CodeModel codeModel) { + return codeModel == DefinedAtom::codeARMThumb || + codeModel == DefinedAtom::codeARM_t; +} + +static const Registry::KindStrings kindStrings[] = { +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), +#include "llvm/Support/ELFRelocs/ARM.def" +#undef ELF_RELOC + LLD_KIND_STRING_END +}; + +void ARMLinkingContext::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, Reference::KindArch::ARM, + kindStrings); +} + +} // namespace elf +} // namespace lld diff --git a/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h b/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h index 249b79c4f07d0..f687713b25b8c 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h +++ b/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h @@ -19,17 +19,61 @@ namespace elf { class ARMLinkingContext final : public ELFLinkingContext { public: - static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + int getMachineType() const override { return llvm::ELF::EM_ARM; } ARMLinkingContext(llvm::Triple); void addPasses(PassManager &) override; + void registerRelocationNames(Registry &r) override; + + bool isRelaOutputFormat() const override { return false; } 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::ARM); + switch (r.kindValue()) { + case llvm::ELF::R_ARM_GLOB_DAT: + case llvm::ELF::R_ARM_TLS_TPOFF32: + case llvm::ELF::R_ARM_COPY: + 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::ARM); + return r.kindValue() == llvm::ELF::R_ARM_COPY; + } + + bool isPLTRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::ARM); + switch (r.kindValue()) { + case llvm::ELF::R_ARM_JUMP_SLOT: + case llvm::ELF::R_ARM_IRELATIVE: + return true; + default: + return false; + } + } }; + +// Special methods to check code model of atoms. +bool isARMCode(const DefinedAtom *atom); +bool isARMCode(DefinedAtom::CodeModel codeModel); +bool isThumbCode(const DefinedAtom *atom); +bool isThumbCode(DefinedAtom::CodeModel codeModel); + } // end namespace elf } // end namespace lld diff --git a/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp b/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp index d24fdf0fa410a..97b149133ff29 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp +++ b/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp @@ -14,6 +14,8 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/MathExtras.h" +#define DEBUG_TYPE "ARM" + using namespace lld; using namespace lld::elf; using namespace llvm::support::endian; @@ -74,7 +76,7 @@ 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); + return llvm::SignExtend64<12>(imm11 << 1); } static Reference::Addend readAddend(const uint8_t *location, @@ -82,11 +84,15 @@ static Reference::Addend readAddend(const uint8_t *location, switch (kindValue) { case R_ARM_ABS32: case R_ARM_REL32: + case R_ARM_TARGET1: + case R_ARM_GOT_BREL: + case R_ARM_BASE_PREL: case R_ARM_TLS_IE32: case R_ARM_TLS_LE32: + case R_ARM_TLS_TPOFF32: return (int32_t)read32le(location); case R_ARM_PREL31: - return (int32_t)(read32le(location) & 0x7FFFFFFF); + return llvm::SignExtend64<31>(read32le(location) & 0x7FFFFFFF); case R_ARM_THM_CALL: case R_ARM_THM_JUMP24: return readAddend_THM_CALL(location); @@ -106,81 +112,98 @@ static Reference::Addend readAddend(const uint8_t *location, } } -static inline void applyArmReloc(uint8_t *location, uint32_t result, - uint32_t mask = 0xFFFFFFFF) { +static inline void report_unsupported_range_group_reloc_error() { + llvm::report_fatal_error( + "Negative offsets for group relocations are not implemented"); +} + +static inline std::error_code applyArmReloc(uint8_t *location, uint32_t result, + uint32_t mask = 0xFFFFFFFF) { assert(!(result & ~mask)); write32le(location, (read32le(location) & ~mask) | (result & mask)); + return std::error_code(); } -static inline void applyThmReloc(uint8_t *location, uint16_t resHi, - uint16_t resLo, uint16_t maskHi, - uint16_t maskLo = 0xFFFF) { +static inline std::error_code applyThumb32Reloc(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)); + return std::error_code(); } -static inline void applyThumb16Reloc(uint8_t *location, uint16_t result, - uint16_t mask = 0xFFFF) { +static inline std::error_code +applyThumb16Reloc(uint8_t *location, uint16_t result, uint16_t mask = 0xFFFF) { assert(!(result & ~mask)); write16le(location, (read16le(location) & ~mask) | (result & mask)); + return std::error_code(); } /// \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) { +static std::error_code 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); + DEBUG(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 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) { +static std::error_code 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); + DEBUG(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 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) { +static std::error_code 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); + if (!llvm::isInt<31>((int32_t)result)) + return make_out_of_range_reloc_error(); + 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"); + DEBUG(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); + return 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) { +static std::error_code relocR_ARM_THM_B_L(uint8_t *location, uint32_t result, + bool useJs) { + if ((useJs && !llvm::isInt<25>((int32_t)result)) || + (!useJs && !llvm::isInt<23>((int32_t)result))) + return make_out_of_range_reloc_error(); + result = (result & 0x01FFFFFE) >> 1; const uint16_t imm10 = (result >> 11) & 0x3FF; @@ -194,12 +217,13 @@ static void relocR_ARM_THM_B_L(uint8_t *location, uint32_t result, bool useJs) { const uint16_t bitI1 = (~(bitJ1 ^ bitS)) & 0x1; const uint16_t resLo = (bitI1 << 13) | (bitI2 << 11) | imm11; - applyThmReloc(location, resHi, resLo, 0x7FF, 0x2FFF); + return applyThumb32Reloc(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) { +static std::error_code 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; @@ -209,137 +233,171 @@ static void relocR_ARM_THM_CALL(uint8_t *location, uint64_t P, uint64_t S, 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); + DEBUG(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"); + if (auto ec = relocR_ARM_THM_B_L(location, result, useJs)) + return ec; if (switchMode) { - applyThmReloc(location, 0, 0, 0, 0x1001); + return applyThumb32Reloc(location, 0, 0, 0, 0x1001); } + return std::error_code(); } /// \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) { +static std::error_code 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); + DEBUG(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_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) { +static std::error_code 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"); + DEBUG(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 + if (!llvm::isInt<12>((int32_t)result)) + return make_out_of_range_reloc_error(); + + // we cut off first bit because it is always 1 according to p. 4.5.3 result = (result & 0x0FFE) >> 1; + return applyThumb16Reloc(location, result, 0x7FF); +} + +/// \brief R_ARM_BASE_PREL - B(S) + A - P => S + A - P +static std::error_code relocR_ARM_BASE_PREL(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + uint32_t result = (uint32_t)(S + A - P); + DEBUG(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 applyArmReloc(location, result); +} - applyThumb16Reloc(location, result, 0x7FF); +/// \brief R_ARM_GOT_BREL - GOT(S) + A - GOT_ORG => S + A - GOT_ORG +static std::error_code relocR_ARM_GOT_BREL(uint8_t *location, uint64_t P, + uint64_t S, int64_t A, + uint64_t GOT_ORG) { + uint32_t result = (uint32_t)(S + A - GOT_ORG); + DEBUG(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 applyArmReloc(location, result); } /// \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) { +static std::error_code 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); + if (!llvm::isInt<26>((int32_t)result)) + return make_out_of_range_reloc_error(); + 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); + DEBUG(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"); + if (auto ec = applyArmReloc(location, imm24, 0xFFFFFF)) + return ec; if (switchMode) { const uint32_t bitH = (result & 0x2) >> 1; - applyArmReloc(location, (0xFA | bitH) << 24, 0xFF000000); + return applyArmReloc(location, (0xFA | bitH) << 24, 0xFF000000); } + return std::error_code(); } /// \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) { +static std::error_code 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); + if (!llvm::isInt<26>((int32_t)result)) + return make_out_of_range_reloc_error(); + 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); + DEBUG(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 applyArmReloc(location, imm24, 0xFFFFFF); } /// \brief Relocate ARM MOVW/MOVT instructions -static void relocR_ARM_MOV(uint8_t *location, uint32_t result) { +static std::error_code 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); + return 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) { +static std::error_code 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"); + DEBUG(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) { +static std::error_code 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"); + DEBUG(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) { +static std::error_code 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; @@ -348,153 +406,275 @@ static void relocR_ARM_THM_MOV(uint8_t *location, uint32_t result) { const uint16_t bitI = (result >> 11) & 0x1; const uint16_t resHi = (bitI << 10) | imm4; - applyThmReloc(location, resHi, resLo, 0x40F, 0x70FF); + return applyThumb32Reloc(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) { +static std::error_code 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"); + DEBUG(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) { +static std::error_code 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"); + DEBUG(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) { +static std::error_code 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); + DEBUG(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 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) { +static std::error_code 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); + DEBUG(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 applyArmReloc(location, result); +} + +/// \brief R_ARM_TLS_TPOFF32 - S + A - tp => S + A (offset within TLS block) +static std::error_code relocR_ARM_TLS_TPOFF32(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + uint32_t result = (uint32_t)(S + A); + + DEBUG(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 applyArmReloc(location, result); +} + +template <uint32_t lshift> +static std::error_code relocR_ARM_ALU_PC_GN_NC(uint8_t *location, + uint32_t result) { + static_assert(lshift < 32 && lshift % 2 == 0, + "lshift must be even and less than word size"); + + const uint32_t rshift = 32 - lshift; + result = ((result >> lshift) & 0xFF) | ((rshift / 2) << 8); + + return applyArmReloc(location, result, 0xFFF); +} + +/// \brief R_ARM_ALU_PC_G0_NC - ((S + A) | T) - P => S + A - P +static std::error_code relocR_ARM_ALU_PC_G0_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)(S + A - P); + if (result < 0) + report_unsupported_range_group_reloc_error(); + + DEBUG(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((uint32_t)result) + << "\n"); + + return relocR_ARM_ALU_PC_GN_NC<20>(location, (uint32_t)result); +} + +/// \brief R_ARM_ALU_PC_G1_NC - ((S + A) | T) - P => S + A - P +static std::error_code relocR_ARM_ALU_PC_G1_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)(S + A - P); + if (result < 0) + report_unsupported_range_group_reloc_error(); + + DEBUG(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((uint32_t)result) + << "\n"); + + return relocR_ARM_ALU_PC_GN_NC<12>(location, (uint32_t)result); +} + +/// \brief R_ARM_LDR_PC_G2 - S + A - P +static std::error_code relocR_ARM_LDR_PC_G2(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)(S + A - P); + if (result < 0) + report_unsupported_range_group_reloc_error(); + + DEBUG(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((uint32_t)result) + << "\n"); + + const uint32_t mask = 0xFFF; + return applyArmReloc(location, (uint32_t)result & mask, mask); +} + +/// \brief Fixup unresolved weak reference with NOP instruction +static bool fixupUnresolvedWeakCall(uint8_t *location, + Reference::KindValue kindValue) { + // TODO: workaround for archs without NOP instruction + switch (kindValue) { + case R_ARM_THM_CALL: + case R_ARM_THM_JUMP24: + // Thumb32 NOP.W + write32le(location, 0x8000F3AF); + break; + case R_ARM_THM_JUMP11: + // Thumb16 NOP + write16le(location, 0xBF00); + break; + case R_ARM_CALL: + case R_ARM_JUMP24: + // A1 NOP<c>, save condition bits + applyArmReloc(location, 0x320F000, 0xFFFFFFF); + break; + default: + return false; + } + + return true; } std::error_code ARMTargetRelocationHandler::applyRelocation( - ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom, + ELFWriter &writer, llvm::FileOutputBuffer &buf, const 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(); + uint8_t *loc = atomContent + ref.offsetInAtom(); + uint64_t target = writer.addressOfAtom(ref.target()); + uint64_t reloc = atom._virtualAddr + ref.offsetInAtom(); if (ref.kindNamespace() != Reference::KindNamespace::ELF) return std::error_code(); assert(ref.kindArch() == Reference::KindArch::ARM); + // Fixup unresolved weak references + if (!target) { + bool isCallFixed = fixupUnresolvedWeakCall(loc, ref.kindValue()); + + if (isCallFixed) { + DEBUG(llvm::dbgs() << "\t\tFixup unresolved weak reference '"; + llvm::dbgs() << ref.target()->name() << "'"; + llvm::dbgs() << " at address: 0x" << Twine::utohexstr(reloc); + llvm::dbgs() << (isCallFixed ? "\n" : " isn't possible\n")); + return std::error_code(); + } + } + // Calculate proper initial addend for the relocation const Reference::Addend addend = - readAddend(location, ref.kindValue()); + readAddend(loc, ref.kindValue()) + ref.addend(); // Flags that the relocation addresses Thumb instruction - bool addressesThumb = false; - + bool thumb = false; if (const auto *definedAtom = dyn_cast<DefinedAtom>(ref.target())) { - addressesThumb = (DefinedAtom::codeARMThumb == definedAtom->codeModel()); + thumb = isThumbCode(definedAtom); } switch (ref.kindValue()) { case R_ARM_NONE: - break; + return std::error_code(); case R_ARM_ABS32: - relocR_ARM_ABS32(location, relocVAddress, targetVAddress, addend, - addressesThumb); - break; + return relocR_ARM_ABS32(loc, reloc, target, addend, thumb); case R_ARM_REL32: - relocR_ARM_REL32(location, relocVAddress, targetVAddress, addend, - addressesThumb); - break; + return relocR_ARM_REL32(loc, reloc, target, addend, thumb); + case R_ARM_TARGET1: + if (_armLayout.target1Rel()) + return relocR_ARM_REL32(loc, reloc, target, addend, thumb); + else + return relocR_ARM_ABS32(loc, reloc, target, addend, thumb); 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; + return relocR_ARM_THM_CALL(loc, reloc, target, addend, true, thumb); case R_ARM_CALL: - relocR_ARM_CALL(location, relocVAddress, targetVAddress, addend, - addressesThumb); - break; + return relocR_ARM_CALL(loc, reloc, target, addend, thumb); case R_ARM_JUMP24: - relocR_ARM_JUMP24(location, relocVAddress, targetVAddress, addend, - addressesThumb); - break; + return relocR_ARM_JUMP24(loc, reloc, target, addend, thumb); case R_ARM_THM_JUMP24: - relocR_ARM_THM_JUMP24(location, relocVAddress, targetVAddress, addend, - addressesThumb); - break; + return relocR_ARM_THM_JUMP24(loc, reloc, target, addend, thumb); case R_ARM_THM_JUMP11: - relocR_ARM_THM_JUMP11(location, relocVAddress, targetVAddress, addend); - break; + return relocR_ARM_THM_JUMP11(loc, reloc, target, addend); case R_ARM_MOVW_ABS_NC: - relocR_ARM_MOVW_ABS_NC(location, relocVAddress, targetVAddress, addend, - addressesThumb); - break; + return relocR_ARM_MOVW_ABS_NC(loc, reloc, target, addend, thumb); case R_ARM_MOVT_ABS: - relocR_ARM_MOVT_ABS(location, relocVAddress, targetVAddress, addend); - break; + return relocR_ARM_MOVT_ABS(loc, reloc, target, addend); case R_ARM_THM_MOVW_ABS_NC: - relocR_ARM_THM_MOVW_ABS_NC(location, relocVAddress, targetVAddress, addend, - addressesThumb); - break; + return relocR_ARM_THM_MOVW_ABS_NC(loc, reloc, target, addend, thumb); case R_ARM_THM_MOVT_ABS: - relocR_ARM_THM_MOVT_ABS(location, relocVAddress, targetVAddress, addend); - break; + return relocR_ARM_THM_MOVT_ABS(loc, reloc, target, addend); case R_ARM_PREL31: - relocR_ARM_PREL31(location, relocVAddress, targetVAddress, addend, - addressesThumb); - break; + return relocR_ARM_PREL31(loc, reloc, target, addend, thumb); case R_ARM_TLS_IE32: - relocR_ARM_TLS_IE32(location, relocVAddress, targetVAddress, addend); - break; + return relocR_ARM_TLS_IE32(loc, reloc, target, addend); case R_ARM_TLS_LE32: - relocR_ARM_TLS_LE32(location, relocVAddress, targetVAddress, addend, - _armLayout.getTPOffset()); - break; + return relocR_ARM_TLS_LE32(loc, reloc, target, addend, + _armLayout.getTPOffset()); + case R_ARM_TLS_TPOFF32: + return relocR_ARM_TLS_TPOFF32(loc, reloc, target, addend); + case R_ARM_GOT_BREL: + return relocR_ARM_GOT_BREL(loc, reloc, target, addend, + _armLayout.getGOTSymAddr()); + case R_ARM_BASE_PREL: + // GOT origin is used for NULL symbol and when explicitly specified + if (!target || ref.target()->name().equals("_GLOBAL_OFFSET_TABLE_")) { + target = _armLayout.getGOTSymAddr(); + } else { + return make_dynamic_error_code( + "Segment-base relative addressing is not supported"); + } + return relocR_ARM_BASE_PREL(loc, reloc, target, addend); + case R_ARM_ALU_PC_G0_NC: + return relocR_ARM_ALU_PC_G0_NC(loc, reloc, target, addend); + case R_ARM_ALU_PC_G1_NC: + return relocR_ARM_ALU_PC_G1_NC(loc, reloc, target, addend); + case R_ARM_LDR_PC_G2: + return relocR_ARM_LDR_PC_G2(loc, reloc, target, addend); + case R_ARM_JUMP_SLOT: + case R_ARM_GLOB_DAT: + case R_ARM_IRELATIVE: + // Runtime only relocations. Ignore here. + return std::error_code(); + case R_ARM_V4BX: + // TODO implement + return std::error_code(); default: return make_unhandled_reloc_error(); } - return std::error_code(); + llvm_unreachable("All switch cases must return directly"); } diff --git a/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.h b/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.h index 227d68617bf98..a1f3d091f204b 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.h +++ b/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.h @@ -10,26 +10,23 @@ #ifndef LLD_READER_WRITER_ELF_ARM_ARM_RELOCATION_HANDLER_H #define LLD_READER_WRITER_ELF_ARM_ARM_RELOCATION_HANDLER_H -#include "ARMTargetHandler.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" namespace lld { namespace elf { -typedef llvm::object::ELFType<llvm::support::little, 2, false> ARMELFType; -template <class ELFT> class ARMTargetLayout; +class ARMTargetLayout; -class ARMTargetRelocationHandler final - : public TargetRelocationHandler { +class ARMTargetRelocationHandler final : public TargetRelocationHandler { public: - ARMTargetRelocationHandler(ARMTargetLayout<ARMELFType> &layout) - : _armLayout(layout) {} + ARMTargetRelocationHandler(ARMTargetLayout &layout) : _armLayout(layout) {} std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, - const lld::AtomLayout &, + const AtomLayout &, const Reference &) const override; private: - ARMTargetLayout<ARMELFType> &_armLayout; + ARMTargetLayout &_armLayout; }; } // end namespace elf diff --git a/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp b/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp index 27ec66ac55572..fc2ae75cd7a78 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp +++ b/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp @@ -20,7 +20,7 @@ #include "ARMLinkingContext.h" #include "Atoms.h" #include "lld/Core/Simple.h" -#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Debug.h" @@ -28,34 +28,77 @@ using namespace lld; using namespace lld::elf; using namespace llvm::ELF; -// ARM B/BL instructions of static relocation veneer. +namespace { +// ARM B/BL instructions of absolute 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] +static const uint8_t Veneer_ARM_B_BL_Abs_a_AtomContent[4] = { + 0x04, 0xf0, 0x1f, 0xe5 // ldr pc, [pc, #-4] +}; +static const uint8_t Veneer_ARM_B_BL_Abs_d_AtomContent[4] = { 0x00, 0x00, 0x00, 0x00 // <target_symbol_address> }; -// Thumb B/BL instructions of static relocation veneer. +// Thumb B/BL instructions of absolute 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] = { +static const uint8_t Veneer_THM_B_BL_Abs_t_AtomContent[4] = { 0x78, 0x47, // bx pc - 0x00, 0x00, // nop + 0x00, 0x00 // nop +}; +static const uint8_t Veneer_THM_B_BL_Abs_a_AtomContent[4] = { 0xfe, 0xff, 0xff, 0xea // b <target_symbol_address> }; // .got values static const uint8_t ARMGotAtomContent[4] = {0}; -namespace { +// .plt value (entry 0) +static const uint8_t ARMPlt0_a_AtomContent[16] = { + 0x04, 0xe0, 0x2d, 0xe5, // push {lr} + 0x04, 0xe0, 0x9f, 0xe5, // ldr lr, [pc, #4] + 0x0e, 0xe0, 0x8f, 0xe0, // add lr, pc, lr + 0x00, 0xf0, 0xbe, 0xe5 // ldr pc, [lr, #0]! +}; +static const uint8_t ARMPlt0_d_AtomContent[4] = { + 0x00, 0x00, 0x00, 0x00 // <got1_symbol_address> +}; + +// .plt values (other entries) +static const uint8_t ARMPltAtomContent[12] = { + 0x00, 0xc0, 0x8f, 0xe2, // add ip, pc, #offset[G0] + 0x00, 0xc0, 0x8c, 0xe2, // add ip, ip, #offset[G1] + 0x00, 0xf0, 0xbc, 0xe5 // ldr pc, [ip, #offset[G2]]! +}; + +// Veneer for switching from Thumb to ARM code for PLT entries. +static const uint8_t ARMPltVeneerAtomContent[4] = { + 0x78, 0x47, // bx pc + 0x00, 0x00 // nop +}; + +// Determine proper names for mapping symbols. +static std::string getMappingAtomName(DefinedAtom::CodeModel model, + const std::string &part) { + switch (model) { + case DefinedAtom::codeARM_a: + return part.empty() ? "$a" : "$a." + part; + case DefinedAtom::codeARM_d: + return part.empty() ? "$d" : "$d." + part; + case DefinedAtom::codeARM_t: + return part.empty() ? "$t" : "$t." + part; + default: + llvm_unreachable("Wrong code model of mapping atom"); + } +} + /// \brief Atoms that hold veneer code. class VeneerAtom : public SimpleELFDefinedAtom { StringRef _section; public: - VeneerAtom(const File &f, StringRef secName) - : SimpleELFDefinedAtom(f), _section(secName) {} + VeneerAtom(const File &f, StringRef secName, const std::string &name = "") + : SimpleELFDefinedAtom(f), _section(secName), _name(name) {} Scope scope() const override { return DefinedAtom::scopeTranslationUnit; } @@ -65,58 +108,208 @@ public: StringRef customSectionName() const override { return _section; } - ContentType contentType() const override { - return DefinedAtom::typeCode; - } + 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); } + Alignment alignment() const override { return 4; } StringRef name() const override { return _name; } + +private: std::string _name; }; -/// \brief Atoms that hold veneer for statically relocated -/// ARM B/BL instructions. -class Veneer_ARM_B_BL_StaticAtom : public VeneerAtom { +/// \brief Atoms that hold veneer for relocated ARM B/BL instructions +/// in absolute code. +class Veneer_ARM_B_BL_Abs_a_Atom : public VeneerAtom { public: - Veneer_ARM_B_BL_StaticAtom(const File &f, StringRef secName) - : VeneerAtom(f, secName) {} + Veneer_ARM_B_BL_Abs_a_Atom(const File &f, StringRef secName, + const std::string &name) + : VeneerAtom(f, secName, name) {} ArrayRef<uint8_t> rawContent() const override { - return llvm::makeArrayRef(Veneer_ARM_B_BL_StaticAtomContent); + return llvm::makeArrayRef(Veneer_ARM_B_BL_Abs_a_AtomContent); } }; -/// \brief Atoms that hold veneer for statically relocated -/// Thumb B/BL instructions. -class Veneer_THM_B_BL_StaticAtom : public VeneerAtom { +class Veneer_ARM_B_BL_Abs_d_Atom : public VeneerAtom { public: - Veneer_THM_B_BL_StaticAtom(const File &f, StringRef secName) + Veneer_ARM_B_BL_Abs_d_Atom(const File &f, StringRef secName) : VeneerAtom(f, secName) {} + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(Veneer_ARM_B_BL_Abs_d_AtomContent); + } +}; + +/// \brief Atoms that hold veneer for relocated Thumb B/BL instructions +/// in absolute code. +class Veneer_THM_B_BL_Abs_t_Atom : public VeneerAtom { +public: + Veneer_THM_B_BL_Abs_t_Atom(const File &f, StringRef secName, + const std::string &name) + : VeneerAtom(f, secName, name) {} + DefinedAtom::CodeModel codeModel() const override { return DefinedAtom::codeARMThumb; } ArrayRef<uint8_t> rawContent() const override { - return llvm::makeArrayRef(Veneer_THM_B_BL_StaticAtomContent); + return llvm::makeArrayRef(Veneer_THM_B_BL_Abs_t_AtomContent); } }; +class Veneer_THM_B_BL_Abs_a_Atom : public VeneerAtom { +public: + Veneer_THM_B_BL_Abs_a_Atom(const File &f, StringRef secName) + : VeneerAtom(f, secName) {} + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(Veneer_THM_B_BL_Abs_a_AtomContent); + } +}; + +template <DefinedAtom::CodeModel Model> +class ARMVeneerMappingAtom : public VeneerAtom { +public: + ARMVeneerMappingAtom(const File &f, StringRef secName, StringRef name) + : VeneerAtom(f, secName, getMappingAtomName(Model, name)) { + static_assert((Model == DefinedAtom::codeARM_a || + Model == DefinedAtom::codeARM_d || + Model == DefinedAtom::codeARM_t), + "Only mapping atom types are allowed"); + } + + uint64_t size() const override { return 0; } + + ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } + + DefinedAtom::CodeModel codeModel() const override { return Model; } +}; + +template <class BaseAtom, DefinedAtom::CodeModel Model> +class BaseMappingAtom : public BaseAtom { +public: + BaseMappingAtom(const File &f, StringRef secName, StringRef name) + : BaseAtom(f, secName) { + static_assert((Model == DefinedAtom::codeARM_a || + Model == DefinedAtom::codeARM_d || + Model == DefinedAtom::codeARM_t), + "Only mapping atom types are allowed"); +#ifndef NDEBUG + _name = name; +#else + _name = getMappingAtomName(Model, name); +#endif + } + + DefinedAtom::CodeModel codeModel() const override { +#ifndef NDEBUG + return isThumbCode(Model) ? DefinedAtom::codeARMThumb : DefinedAtom::codeNA; +#else + return Model; +#endif + } + + StringRef name() const override { return _name; } + +private: + std::string _name; +}; + /// \brief Atoms that are used by ARM dynamic linking class ARMGOTAtom : public GOTAtom { public: - ARMGOTAtom(const File &f, StringRef secName) : GOTAtom(f, secName) {} + ARMGOTAtom(const File &f) : GOTAtom(f, ".got") {} ArrayRef<uint8_t> rawContent() const override { return llvm::makeArrayRef(ARMGotAtomContent); } - Alignment alignment() const override { return Alignment(2); } + Alignment alignment() const override { return 4; } + +protected: + // Constructor for PLTGOT atom. + ARMGOTAtom(const File &f, StringRef secName) : GOTAtom(f, secName) {} +}; + +class ARMGOTPLTAtom : public ARMGOTAtom { +public: + ARMGOTPLTAtom(const File &f) : ARMGOTAtom(f, ".got.plt") {} +}; + +/// \brief Proxy class to keep type compatibility with PLT0Atom. +class ARMPLT0Atom : public PLT0Atom { +public: + ARMPLT0Atom(const File &f, StringRef) : PLT0Atom(f) {} +}; + +/// \brief PLT0 entry atom. +/// Serves as a mapping symbol in the release mode. +class ARMPLT0_a_Atom + : public BaseMappingAtom<ARMPLT0Atom, DefinedAtom::codeARM_a> { +public: + ARMPLT0_a_Atom(const File &f, const std::string &name) + : BaseMappingAtom(f, ".plt", name) {} + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(ARMPlt0_a_AtomContent); + } + + Alignment alignment() const override { return 4; } +}; + +class ARMPLT0_d_Atom + : public BaseMappingAtom<ARMPLT0Atom, DefinedAtom::codeARM_d> { +public: + ARMPLT0_d_Atom(const File &f, const std::string &name) + : BaseMappingAtom(f, ".plt", name) {} + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(ARMPlt0_d_AtomContent); + } + + Alignment alignment() const override { return 4; } +}; + +/// \brief PLT entry atom. +/// Serves as a mapping symbol in the release mode. +class ARMPLTAtom : public BaseMappingAtom<PLTAtom, DefinedAtom::codeARM_a> { +public: + ARMPLTAtom(const File &f, const std::string &name) + : BaseMappingAtom(f, ".plt", name) {} + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(ARMPltAtomContent); + } + + Alignment alignment() const override { return 4; } +}; + +/// \brief Veneer atom for PLT entry. +/// Serves as a mapping symbol in the release mode. +class ARMPLTVeneerAtom + : public BaseMappingAtom<PLTAtom, DefinedAtom::codeARM_t> { +public: + ARMPLTVeneerAtom(const File &f, const std::string &name) + : BaseMappingAtom(f, ".plt", name) {} + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(ARMPltVeneerAtomContent); + } + + Alignment alignment() const override { return 4; } +}; + +/// \brief Atom which represents an object for which a COPY relocation will +/// be generated. +class ARMObjectAtom : public ObjectAtom { +public: + ARMObjectAtom(const File &f) : ObjectAtom(f) {} + Alignment alignment() const override { return 4; } }; class ELFPassFile : public SimpleFile { @@ -140,30 +333,92 @@ template <class Derived> class ARMRelocationPass : public Pass { return; assert(ref.kindArch() == Reference::KindArch::ARM); switch (ref.kindValue()) { + case R_ARM_ABS32: + case R_ARM_REL32: + case R_ARM_TARGET1: + case R_ARM_MOVW_ABS_NC: + case R_ARM_MOVT_ABS: + case R_ARM_THM_MOVW_ABS_NC: + case R_ARM_THM_MOVT_ABS: + static_cast<Derived *>(this)->handlePlain(isThumbCode(&atom), ref); + break; + case R_ARM_THM_CALL: + case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_THM_JUMP24: - static_cast<Derived *>(this)->handleVeneer(atom, ref); - break; + case R_ARM_THM_JUMP11: { + const auto actualModel = actualSourceCodeModel(atom, ref); + const bool fromThumb = isThumbCode(actualModel); + static_cast<Derived *>(this)->handlePlain(fromThumb, ref); + static_cast<Derived *>(this)->handleVeneer(atom, fromThumb, ref); + } break; case R_ARM_TLS_IE32: static_cast<Derived *>(this)->handleTLSIE32(ref); break; + case R_ARM_GOT_BREL: + static_cast<Derived *>(this)->handleGOT(ref); + break; + default: + break; } } protected: - std::error_code handleVeneer(const DefinedAtom &atom, const Reference &ref) { + /// \brief Determine source atom's actual code model. + /// + /// Actual code model may differ from the existing one if fixup + /// is possible on the later stages for given relocation type. + DefinedAtom::CodeModel actualSourceCodeModel(const DefinedAtom &atom, + const Reference &ref) { + const auto kindValue = ref.kindValue(); + if (kindValue != R_ARM_CALL && kindValue != R_ARM_THM_CALL) + return atom.codeModel(); + + // TODO: For unconditional jump instructions (R_ARM_CALL and R_ARM_THM_CALL) + // fixup isn't possible without veneer generation for archs below ARMv5. + + auto actualModel = atom.codeModel(); + if (const auto *da = dyn_cast<DefinedAtom>(ref.target())) { + actualModel = da->codeModel(); + } else if (const auto *sla = dyn_cast<SharedLibraryAtom>(ref.target())) { + if (sla->type() == SharedLibraryAtom::Type::Code) { + // PLT entry will be generated here - assume we don't want a veneer + // on top of it and prefer instruction fixup if needed. + actualModel = DefinedAtom::codeNA; + } + } + return actualModel; + } + + std::error_code handleVeneer(const DefinedAtom &atom, bool fromThumb, + const Reference &ref) { + // Actual instruction mode differs meaning that further fixup will be + // applied. + if (isThumbCode(&atom) != fromThumb) + return std::error_code(); + + const VeneerAtom *(Derived::*getVeneer)(const DefinedAtom *, StringRef) = + nullptr; + const auto kindValue = ref.kindValue(); + switch (kindValue) { + case R_ARM_JUMP24: + getVeneer = &Derived::getVeneer_ARM_B_BL; + break; + case R_ARM_THM_JUMP24: + getVeneer = &Derived::getVeneer_THM_B_BL; + break; + default: + return std::error_code(); + } + // 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()) + if (!target || isThumbCode(target) == isThumbCode(&atom)) 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)) { @@ -182,29 +437,69 @@ protected: 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(getVeneer && "The veneer handler is missing"); + const Atom *veneer = + (static_cast<Derived *>(this)->*getVeneer)(target, secName); assert(veneer && "The veneer is not set"); const_cast<Reference &>(ref).setTarget(veneer); return std::error_code(); } + /// \brief Get the veneer for ARM B/BL instructions + /// in absolute code. + const VeneerAtom *getVeneer_ARM_B_BL_Abs(const DefinedAtom *da, + StringRef secName) { + auto veneer = _veneerAtoms.lookup(da); + if (!veneer.empty()) + return veneer._veneer; + + std::string name = "__"; + name += da->name(); + name += "_from_arm"; + // Create parts of veneer with mapping symbols. + auto v_a = + new (_file._alloc) Veneer_ARM_B_BL_Abs_a_Atom(_file, secName, name); + addVeneerWithMapping<DefinedAtom::codeARM_a>(da, v_a, name); + auto v_d = new (_file._alloc) Veneer_ARM_B_BL_Abs_d_Atom(_file, secName); + addVeneerWithMapping<DefinedAtom::codeARM_d>(v_a, v_d, name); + + // Fake reference to show connection between parts of veneer. + v_a->addReferenceELF_ARM(R_ARM_NONE, 0, v_d, 0); + // Real reference to fixup. + v_d->addReferenceELF_ARM(R_ARM_ABS32, 0, da, 0); + return v_a; + } + + /// \brief Get the veneer for Thumb B/BL instructions + /// in absolute code. + const VeneerAtom *getVeneer_THM_B_BL_Abs(const DefinedAtom *da, + StringRef secName) { + auto veneer = _veneerAtoms.lookup(da); + if (!veneer.empty()) + return veneer._veneer; + + std::string name = "__"; + name += da->name(); + name += "_from_thumb"; + // Create parts of veneer with mapping symbols. + auto v_t = + new (_file._alloc) Veneer_THM_B_BL_Abs_t_Atom(_file, secName, name); + addVeneerWithMapping<DefinedAtom::codeARM_t>(da, v_t, name); + auto v_a = new (_file._alloc) Veneer_THM_B_BL_Abs_a_Atom(_file, secName); + addVeneerWithMapping<DefinedAtom::codeARM_a>(v_t, v_a, name); + + // Fake reference to show connection between parts of veneer. + v_t->addReferenceELF_ARM(R_ARM_NONE, 0, v_a, 0); + // Real reference to fixup. + v_a->addReferenceELF_ARM(R_ARM_JUMP24, 0, da, 0); + return v_t; + } + 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)); + 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"); @@ -213,20 +508,160 @@ protected: /// \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); + StringRef source; #ifndef NDEBUG - g->_name = "__got_tls_"; + source = "_tls_"; +#endif + return getGOT<R_ARM_TLS, A>(da, source); + } + + /// \brief Add veneer with mapping symbol. + template <DefinedAtom::CodeModel Model> + void addVeneerWithMapping(const DefinedAtom *da, VeneerAtom *va, + const std::string &name) { + assert(_veneerAtoms.lookup(da).empty() && + "Veneer or mapping already exists"); + auto *ma = new (_file._alloc) + ARMVeneerMappingAtom<Model>(_file, va->customSectionName(), name); + + // Fake reference to show connection between the mapping symbol and veneer. + va->addReferenceELF_ARM(R_ARM_NONE, 0, ma, 0); + _veneerAtoms[da] = VeneerWithMapping(va, ma); + } + + /// \brief get a veneer for a PLT entry. + const PLTAtom *getPLTVeneer(const Atom *da, PLTAtom *pa, StringRef source) { + std::string name = "__plt_from_thumb"; + name += source.empty() ? "_" : source; + name += da->name(); + // Create veneer for PLT entry. + auto va = new (_file._alloc) ARMPLTVeneerAtom(_file, name); + // Fake reference to show connection between veneer and PLT entry. + va->addReferenceELF_ARM(R_ARM_NONE, 0, pa, 0); + + _pltAtoms[da] = PLTWithVeneer(pa, va); + return va; + } + + typedef const GOTAtom *(Derived::*GOTFactory)(const Atom *); + + /// \brief get a PLT entry referencing PLTGOT entry. + /// + /// If the entry does not exist, both GOT and PLT entry are created. + const PLTAtom *getPLT(const Atom *da, bool fromThumb, GOTFactory gotFactory, + StringRef source = "") { + auto pltVeneer = _pltAtoms.lookup(da); + if (!pltVeneer.empty()) { + // Return clean PLT entry provided it is ARM code. + if (!fromThumb) + return pltVeneer._plt; + + // Check if veneer is present for Thumb to ARM transition. + if (pltVeneer._veneer) + return pltVeneer._veneer; + + // Create veneer for existing PLT entry. + return getPLTVeneer(da, pltVeneer._plt, source); + } + + // Create specific GOT entry. + const auto *ga = (static_cast<Derived *>(this)->*gotFactory)(da); + assert(_gotpltAtoms.lookup(da) == ga && + "GOT entry should be added to the PLTGOT map"); + assert(ga->customSectionName() == ".got.plt" && + "GOT entry should be in a special section"); + + std::string name = "__plt"; + name += source.empty() ? "_" : source; + name += da->name(); + // Create PLT entry for the GOT entry. + auto pa = new (_file._alloc) ARMPLTAtom(_file, name); + pa->addReferenceELF_ARM(R_ARM_ALU_PC_G0_NC, 0, ga, -8); + pa->addReferenceELF_ARM(R_ARM_ALU_PC_G1_NC, 4, ga, -4); + pa->addReferenceELF_ARM(R_ARM_LDR_PC_G2, 8, ga, 0); + + // Since all PLT entries are in ARM code, Thumb to ARM + // switching should be added if the relocated place contais Thumb code. + if (fromThumb) + return getPLTVeneer(da, pa, source); + + // Otherwise just add PLT entry and return it to the caller. + _pltAtoms[da] = PLTWithVeneer(pa); + return pa; + } + + /// \brief Create the GOT entry for a given IFUNC Atom. + const GOTAtom *createIFUNCGOT(const Atom *da) { + assert(!_gotpltAtoms.lookup(da) && "IFUNC GOT entry already exists"); + auto g = new (_file._alloc) ARMGOTPLTAtom(_file); + g->addReferenceELF_ARM(R_ARM_ABS32, 0, da, 0); + g->addReferenceELF_ARM(R_ARM_IRELATIVE, 0, da, 0); +#ifndef NDEBUG + g->_name = "__got_ifunc_"; g->_name += da->name(); #endif - _gotMap[da] = g; - _gotVector.push_back(g); + _gotpltAtoms[da] = g; return g; } + /// \brief get the PLT entry for a given IFUNC Atom. + const PLTAtom *getIFUNCPLTEntry(const DefinedAtom *da, bool fromThumb) { + return getPLT(da, fromThumb, &Derived::createIFUNCGOT, "_ifunc_"); + } + + /// \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(bool fromThumb, const Reference &ref) { + auto target = dyn_cast<const DefinedAtom>(ref.target()); + if (target && target->contentType() == DefinedAtom::typeResolver) { + const_cast<Reference &>(ref) + .setTarget(getIFUNCPLTEntry(target, fromThumb)); + } + return std::error_code(); + } + + /// \brief Create a GOT entry containing 0. + const GOTAtom *getNullGOT() { + if (!_null) { + _null = new (_file._alloc) ARMGOTPLTAtom(_file); +#ifndef NDEBUG + _null->_name = "__got_null"; +#endif + } + return _null; + } + + /// \brief Create regular GOT entry which cannot be used in PLTGOT operation. + template <Reference::KindValue R_ARM_REL, Reference::Addend A = 0> + const GOTAtom *getGOT(const Atom *da, StringRef source = "") { + if (auto got = _gotAtoms.lookup(da)) + return got; + auto g = new (_file._alloc) ARMGOTAtom(_file); + g->addReferenceELF_ARM(R_ARM_REL, 0, da, A); +#ifndef NDEBUG + g->_name = "__got"; + g->_name += source.empty() ? "_" : source; + g->_name += da->name(); +#endif + _gotAtoms[da] = g; + return g; + } + + /// \brief get GOT entry for a regular defined atom. + const GOTAtom *getGOTEntry(const DefinedAtom *da) { + return getGOT<R_ARM_ABS32>(da); + } + + std::error_code handleGOT(const Reference &ref) { + if (isa<UndefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getNullGOT()); + else if (const auto *da = dyn_cast<DefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getGOTEntry(da)); + return std::error_code(); + } + public: ARMRelocationPass(const ELFLinkingContext &ctx) : _file(ctx), _ctx(ctx) {} @@ -238,35 +673,35 @@ public: /// /// After all references are handled, the atoms created during that are all /// added to mf. - void perform(std::unique_ptr<MutableFile> &mf) override { + std::error_code perform(SimpleFile &mf) override { ScopedTask task(getDefaultDomain(), "ARM GOT/PLT Pass"); DEBUG_WITH_TYPE( "ARM", llvm::dbgs() << "Undefined Atoms" << "\n"; for (const auto &atom - : mf->undefined()) { + : mf.undefined()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; } llvm::dbgs() << "Shared Library Atoms" << "\n"; for (const auto &atom - : mf->sharedLibrary()) { + : mf.sharedLibrary()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; } llvm::dbgs() << "Absolute Atoms" << "\n"; for (const auto &atom - : mf->absolute()) { + : mf.absolute()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; } llvm::dbgs() << "Defined Atoms" << "\n"; for (const auto &atom - : mf->defined()) { + : mf.defined()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; }); // Process all references. - for (const auto &atom : mf->defined()) { + for (const auto &atom : mf.defined()) { for (const auto &ref : *atom) { handleReference(*atom, *ref); } @@ -274,14 +709,58 @@ public: // Add all created atoms to the link. uint64_t ordinal = 0; - for (auto &got : _gotVector) { + if (_plt0) { + _plt0->setOrdinal(ordinal++); + mf.addAtom(*_plt0); + _plt0_d->setOrdinal(ordinal++); + mf.addAtom(*_plt0_d); + } + for (auto &pltKV : _pltAtoms) { + auto &plt = pltKV.second; + if (auto *v = plt._veneer) { + v->setOrdinal(ordinal++); + mf.addAtom(*v); + } + auto *p = plt._plt; + p->setOrdinal(ordinal++); + mf.addAtom(*p); + } + if (_null) { + _null->setOrdinal(ordinal++); + mf.addAtom(*_null); + } + if (_plt0) { + _got0->setOrdinal(ordinal++); + mf.addAtom(*_got0); + _got1->setOrdinal(ordinal++); + mf.addAtom(*_got1); + } + for (auto &gotKV : _gotAtoms) { + auto &got = gotKV.second; got->setOrdinal(ordinal++); - mf->addAtom(*got); + mf.addAtom(*got); + } + for (auto &gotKV : _gotpltAtoms) { + auto &got = gotKV.second; + got->setOrdinal(ordinal++); + mf.addAtom(*got); + } + for (auto &objectKV : _objectAtoms) { + auto &obj = objectKV.second; + obj->setOrdinal(ordinal++); + mf.addAtom(*obj); } - for (auto &veneer : _veneerVector) { - veneer->setOrdinal(ordinal++); - mf->addAtom(*veneer); + for (auto &veneerKV : _veneerAtoms) { + auto &veneer = veneerKV.second; + auto *m = veneer._mapping; + m->setOrdinal(ordinal++); + mf.addAtom(*m); + auto *v = veneer._veneer; + v->setOrdinal(ordinal++); + mf.addAtom(*v); } + + return std::error_code(); } protected: @@ -290,16 +769,56 @@ protected: const ELFLinkingContext &_ctx; /// \brief Map Atoms to their GOT entries. - llvm::DenseMap<const Atom *, GOTAtom *> _gotMap; + llvm::MapVector<const Atom *, GOTAtom *> _gotAtoms; - /// \brief Map Atoms to their veneers. - llvm::DenseMap<const Atom *, VeneerAtom *> _veneerMap; + /// \brief Map Atoms to their PLTGOT entries. + llvm::MapVector<const Atom *, GOTAtom *> _gotpltAtoms; + + /// \brief Map Atoms to their Object entries. + llvm::MapVector<const Atom *, ObjectAtom *> _objectAtoms; - /// \brief the list of GOT/PLT atoms - std::vector<GOTAtom *> _gotVector; + /// \brief Map Atoms to their PLT entries depending on the code model. + struct PLTWithVeneer { + PLTWithVeneer(PLTAtom *p = nullptr, PLTAtom *v = nullptr) + : _plt(p), _veneer(v) {} - /// \brief the list of veneer atoms. - std::vector<VeneerAtom *> _veneerVector; + bool empty() const { + assert((_plt || !_veneer) && "Veneer appears without PLT entry"); + return !_plt && !_veneer; + } + + PLTAtom *_plt; + PLTAtom *_veneer; + }; + llvm::MapVector<const Atom *, PLTWithVeneer> _pltAtoms; + + /// \brief Map Atoms to their veneers. + struct VeneerWithMapping { + VeneerWithMapping(VeneerAtom *v = nullptr, VeneerAtom *m = nullptr) + : _veneer(v), _mapping(m) {} + + bool empty() const { + assert(((bool)_veneer == (bool)_mapping) && + "Mapping symbol should always be paired with veneer"); + return !_veneer && !_mapping; + } + + VeneerAtom *_veneer; + VeneerAtom *_mapping; + }; + llvm::MapVector<const Atom *, VeneerWithMapping> _veneerAtoms; + + /// \brief GOT entry that is always 0. Used for undefined weaks. + GOTAtom *_null = nullptr; + + /// \brief The got and plt entries for .PLT0. This is used to call into the + /// dynamic linker for symbol resolution. + /// @{ + PLT0Atom *_plt0 = nullptr; + PLT0Atom *_plt0_d = nullptr; + GOTAtom *_got0 = nullptr; + GOTAtom *_got1 = nullptr; + /// @} }; /// This implements the static relocation model. Meaning GOT and PLT entries are @@ -314,47 +833,138 @@ public: ARMStaticRelocationPass(const elf::ARMLinkingContext &ctx) : ARMRelocationPass(ctx) {} + /// \brief Handle ordinary relocation references. + std::error_code handlePlain(bool fromThumb, const Reference &ref) { + return handleIFUNC(fromThumb, ref); + } + /// \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; + return getVeneer_ARM_B_BL_Abs(da, secName); + } + + /// \brief Get the veneer for Thumb B/BL instructions. + const VeneerAtom *getVeneer_THM_B_BL(const DefinedAtom *da, + StringRef secName) { + return getVeneer_THM_B_BL_Abs(da, secName); + } + + /// \brief Create a GOT entry for R_ARM_TLS_TPOFF32 reloc. + const GOTAtom *getTLSTPOFF32(const DefinedAtom *da) { + return getGOTTLSEntry<R_ARM_TLS_LE32>(da); + } +}; + +/// This implements the dynamic relocation model. GOT and PLT entries are +/// created for references that cannot be directly resolved. +class ARMDynamicRelocationPass final + : public ARMRelocationPass<ARMDynamicRelocationPass> { +public: + ARMDynamicRelocationPass(const elf::ARMLinkingContext &ctx) + : ARMRelocationPass(ctx) {} + + /// \brief get the PLT entry for a given atom. + const PLTAtom *getPLTEntry(const SharedLibraryAtom *sla, bool fromThumb) { + return getPLT(sla, fromThumb, &ARMDynamicRelocationPass::createPLTGOT); + } + + /// \brief Create the GOT entry for a given atom. + const GOTAtom *createPLTGOT(const Atom *da) { + assert(!_gotpltAtoms.lookup(da) && "PLTGOT entry already exists"); + auto g = new (_file._alloc) ARMGOTPLTAtom(_file); + g->addReferenceELF_ARM(R_ARM_ABS32, 0, getPLT0(), 0); + g->addReferenceELF_ARM(R_ARM_JUMP_SLOT, 0, da, 0); +#ifndef NDEBUG + g->_name = "__got_plt0_"; + g->_name += da->name(); +#endif + _gotpltAtoms[da] = g; + return g; + } + + const ObjectAtom *getObjectEntry(const SharedLibraryAtom *a) { + if (auto obj = _objectAtoms.lookup(a)) + return obj; - auto v = new (_file._alloc) Veneer_ARM_B_BL_StaticAtom(_file, secName); - v->addReferenceELF_ARM(R_ARM_ABS32, 4, da, 0); + auto oa = new (_file._alloc) ARMObjectAtom(_file); + oa->addReferenceELF_ARM(R_ARM_COPY, 0, oa, 0); - v->_name = "__"; - v->_name += da->name(); - v->_name += "_from_arm"; + oa->_name = a->name(); + oa->_size = a->size(); + + _objectAtoms[a] = oa; + return oa; + } - _veneerMap[da] = v; - _veneerVector.push_back(v); - return v; + /// \brief Handle ordinary relocation references. + std::error_code handlePlain(bool fromThumb, const Reference &ref) { + if (auto sla = dyn_cast<SharedLibraryAtom>(ref.target())) { + if (sla->type() == SharedLibraryAtom::Type::Data && + _ctx.getOutputELFType() == llvm::ELF::ET_EXEC) { + const_cast<Reference &>(ref).setTarget(getObjectEntry(sla)); + } else if (sla->type() == SharedLibraryAtom::Type::Code) { + const_cast<Reference &>(ref).setTarget(getPLTEntry(sla, fromThumb)); + } + return std::error_code(); + } + return handleIFUNC(fromThumb, ref); + } + + /// \brief Get the veneer for ARM B/BL instructions. + const VeneerAtom *getVeneer_ARM_B_BL(const DefinedAtom *da, + StringRef secName) { + if (_ctx.getOutputELFType() == llvm::ELF::ET_EXEC) { + return getVeneer_ARM_B_BL_Abs(da, secName); + } + llvm_unreachable("Handle ARM veneer for DSOs"); } /// \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; + if (_ctx.getOutputELFType() == llvm::ELF::ET_EXEC) { + return getVeneer_THM_B_BL_Abs(da, secName); + } + llvm_unreachable("Handle Thumb veneer for DSOs"); + } - auto v = new (_file._alloc) Veneer_THM_B_BL_StaticAtom(_file, secName); - v->addReferenceELF_ARM(R_ARM_JUMP24, 4, da, 0); + /// \brief Create a GOT entry for R_ARM_TLS_TPOFF32 reloc. + const GOTAtom *getTLSTPOFF32(const DefinedAtom *da) { + return getGOTTLSEntry<R_ARM_TLS_TPOFF32>(da); + } - v->_name = "__"; - v->_name += da->name(); - v->_name += "_from_thumb"; + const PLT0Atom *getPLT0() { + if (_plt0) + return _plt0; + // Fill in the null entry. + getNullGOT(); + _plt0 = new (_file._alloc) ARMPLT0_a_Atom(_file, "__PLT0"); + _plt0_d = new (_file._alloc) ARMPLT0_d_Atom(_file, "__PLT0_d"); + _got0 = new (_file._alloc) ARMGOTPLTAtom(_file); + _got1 = new (_file._alloc) ARMGOTPLTAtom(_file); + _plt0_d->addReferenceELF_ARM(R_ARM_REL32, 0, _got1, 0); + // Fake reference to show connection between the GOT and PLT entries. + _plt0->addReferenceELF_ARM(R_ARM_NONE, 0, _got0, 0); + // Fake reference to show connection between parts of PLT entry. + _plt0->addReferenceELF_ARM(R_ARM_NONE, 0, _plt0_d, 0); +#ifndef NDEBUG + _got0->_name = "__got0"; + _got1->_name = "__got1"; +#endif + return _plt0; + } - _veneerMap[da] = v; - _veneerVector.push_back(v); - return v; + const GOTAtom *getSharedGOTEntry(const SharedLibraryAtom *sla) { + return getGOT<R_ARM_GLOB_DAT>(sla); } - /// \brief Create a GOT entry for R_ARM_TLS_TPOFF32 reloc. - const GOTAtom *getTLSTPOFF32(const DefinedAtom *da) { - return getGOTTLSEntry<R_ARM_TLS_LE32>(da); + std::error_code handleGOT(const Reference &ref) { + if (const auto sla = dyn_cast<const SharedLibraryAtom>(ref.target())) { + const_cast<Reference &>(ref).setTarget(getSharedGOTEntry(sla)); + return std::error_code(); + } + return ARMRelocationPass::handleGOT(ref); } }; @@ -365,8 +975,10 @@ 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<ARMDynamicRelocationPass>(ctx); return llvm::make_unique<ARMStaticRelocationPass>(ctx); + case llvm::ELF::ET_DYN: + return llvm::make_unique<ARMDynamicRelocationPass>(ctx); default: llvm_unreachable("Unhandled output file type"); } diff --git a/lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h b/lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h index 540a480421a8a..85b9c9162589c 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h +++ b/lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h @@ -10,34 +10,47 @@ #ifndef LLD_READER_WRITER_ELF_ARM_ARM_SYMBOL_TABLE_H #define LLD_READER_WRITER_ELF_ARM_ARM_SYMBOL_TABLE_H +#include "SectionChunks.h" +#include "TargetLayout.h" +#include "ARMELFFile.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> { +class ARMSymbolTable : public SymbolTable<ELF32LE> { public: - typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + typedef llvm::object::Elf_Sym_Impl<ELF32LE> Elf_Sym; - ARMSymbolTable(const ELFLinkingContext &context); + ARMSymbolTable(const ELFLinkingContext &ctx); 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) {} +ARMSymbolTable::ARMSymbolTable(const ELFLinkingContext &ctx) + : SymbolTable(ctx, ".symtab", TargetLayout<ELF32LE>::ORDER_SYMBOL_TABLE) {} + +void ARMSymbolTable::addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr) { + SymbolTable::addDefinedAtom(sym, da, addr); -template <class ELFT> -void ARMSymbolTable<ELFT>::addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, - int64_t addr) { - SymbolTable<ELFT>::addDefinedAtom(sym, da, addr); + if ((ARMELFDefinedAtom::ARMContentType)da->contentType() == + ARMELFDefinedAtom::typeARMExidx) + sym.st_value = addr; - // Set zero bit to distinguish symbols addressing Thumb instructions + // Set zero bit to distinguish real symbols addressing Thumb instructions. + // Don't care about mapping symbols like $t and others. if (DefinedAtom::codeARMThumb == da->codeModel()) sym.st_value = static_cast<int64_t>(sym.st_value) | 0x1; + + // Mapping symbols should have special values of binding, type and size set. + if ((DefinedAtom::codeARM_a == da->codeModel()) || + (DefinedAtom::codeARM_d == da->codeModel()) || + (DefinedAtom::codeARM_t == da->codeModel())) { + sym.setBindingAndType(llvm::ELF::STB_LOCAL, llvm::ELF::STT_NOTYPE); + sym.st_size = 0; + } } } // elf diff --git a/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.cpp b/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.cpp index de90f490f621b..e1f5eadbe7895 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.cpp +++ b/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.cpp @@ -9,36 +9,24 @@ #include "Atoms.h" #include "ARMExecutableWriter.h" +#include "ARMDynamicLibraryWriter.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); -} +ARMTargetHandler::ARMTargetHandler(ARMLinkingContext &ctx) + : _ctx(ctx), _targetLayout(new ARMTargetLayout(ctx)), + _relocationHandler(new ARMTargetRelocationHandler(*_targetLayout)) {} std::unique_ptr<Writer> ARMTargetHandler::getWriter() { - switch (this->_context.getOutputELFType()) { + switch (this->_ctx.getOutputELFType()) { case llvm::ELF::ET_EXEC: - return std::unique_ptr<Writer>( - new ARMExecutableWriter<ARMELFType>(_context, *_armTargetLayout.get())); + return llvm::make_unique<ARMExecutableWriter>(_ctx, *_targetLayout); + case llvm::ELF::ET_DYN: + return llvm::make_unique<ARMDynamicLibraryWriter>(_ctx, *_targetLayout); 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 index 10641954da25d..0352e81a1f610 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.h +++ b/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.h @@ -11,75 +11,161 @@ #define LLD_READER_WRITER_ELF_ARM_ARM_TARGET_HANDLER_H #include "ARMELFFile.h" -#include "ARMELFReader.h" #include "ARMRelocationHandler.h" -#include "DefaultTargetHandler.h" +#include "ELFReader.h" #include "TargetLayout.h" -#include "lld/Core/Simple.h" -#include "llvm/ADT/Optional.h" -#include <map> - namespace lld { +class ELFLinkingContext; + namespace elf { -typedef llvm::object::ELFType<llvm::support::little, 2, false> ARMELFType; -class ARMLinkingContext; -template <class ELFT> class ARMTargetLayout : public TargetLayout<ELFT> { +/// \brief ARM specific section (.ARM.exidx) with indexes to exception handlers +class ARMExidxSection : public AtomSection<ELF32LE> { + typedef AtomSection<ELF32LE> Base; + public: - ARMTargetLayout(ARMLinkingContext &context) - : TargetLayout<ELFT>(context) {} + ARMExidxSection(const ELFLinkingContext &ctx, StringRef sectionName, + int32_t permissions, int32_t order) + : Base(ctx, sectionName, ARMELFDefinedAtom::typeARMExidx, permissions, + order) { + this->_type = SHT_ARM_EXIDX; + this->_isLoadedInMemory = true; + } - uint64_t getTPOffset() { - if (_tpOff.hasValue()) - return *_tpOff; + bool hasOutputSegment() const override { return true; } - for (const auto &phdr : *this->_programHeader) { - if (phdr->p_type == llvm::ELF::PT_TLS) { - _tpOff = llvm::RoundUpToAlignment(TCB_SIZE, phdr->p_align); - return *_tpOff; - } + const AtomLayout *appendAtom(const Atom *atom) override { + const DefinedAtom *definedAtom = cast<DefinedAtom>(atom); + assert((ARMELFDefinedAtom::ARMContentType)definedAtom->contentType() == + ARMELFDefinedAtom::typeARMExidx && + "atom content type for .ARM.exidx section has to be typeARMExidx"); + + DefinedAtom::Alignment atomAlign = definedAtom->alignment(); + uint64_t fOffset = alignOffset(this->fileSize(), atomAlign); + uint64_t mOffset = alignOffset(this->memSize(), atomAlign); + + _atoms.push_back(new (_alloc) 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"); + + uint64_t alignment = atomAlign.value; + if (this->_alignment < alignment) + this->_alignment = alignment; + + return _atoms.back(); + } +}; + +class ARMTargetLayout : public TargetLayout<ELF32LE> { +public: + enum ARMSectionOrder { + ORDER_ARM_EXIDX = TargetLayout::ORDER_EH_FRAME + 1, + }; + + ARMTargetLayout(ELFLinkingContext &ctx) : TargetLayout(ctx) {} + + SectionOrder getSectionOrder(StringRef name, int32_t contentType, + int32_t contentPermissions) override { + switch (contentType) { + case ARMELFDefinedAtom::typeARMExidx: + return ORDER_ARM_EXIDX; + default: + return TargetLayout::getSectionOrder(name, contentType, + contentPermissions); } - llvm_unreachable("TLS segment not found"); } + StringRef getOutputSectionName(StringRef archivePath, StringRef memberPath, + StringRef inputSectionName) const override { + return llvm::StringSwitch<StringRef>(inputSectionName) + .StartsWith(".ARM.exidx", ".ARM.exidx") + .StartsWith(".ARM.extab", ".ARM.extab") + .Default(TargetLayout::getOutputSectionName(archivePath, memberPath, + inputSectionName)); + } + + SegmentType getSegmentType(const Section<ELF32LE> *section) const override { + switch (section->order()) { + case ORDER_ARM_EXIDX: + return llvm::ELF::PT_ARM_EXIDX; + default: + return TargetLayout::getSegmentType(section); + } + } + + AtomSection<ELF32LE> * + createSection(StringRef name, int32_t contentType, + DefinedAtom::ContentPermissions contentPermissions, + SectionOrder sectionOrder) override { + if ((ARMELFDefinedAtom::ARMContentType)contentType == + ARMELFDefinedAtom::typeARMExidx) + return new ARMExidxSection(_ctx, name, contentPermissions, sectionOrder); + + return TargetLayout::createSection(name, contentType, contentPermissions, + sectionOrder); + } + + uint64_t getGOTSymAddr() { + std::call_once(_gotSymOnce, [this]() { + if (AtomLayout *gotAtom = findAbsoluteAtom("_GLOBAL_OFFSET_TABLE_")) + _gotSymAddr = gotAtom->_virtualAddr; + }); + return _gotSymAddr; + } + + uint64_t getTPOffset() { + std::call_once(_tpOffOnce, [this]() { + for (const auto &phdr : *_programHeader) { + if (phdr->p_type == llvm::ELF::PT_TLS) { + _tpOff = llvm::RoundUpToAlignment(TCB_SIZE, phdr->p_align); + break; + } + } + assert(_tpOff != 0 && "TLS segment not found"); + }); + return _tpOff; + } + + bool target1Rel() const { return _ctx.armTarget1Rel(); } + 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; +private: + uint64_t _gotSymAddr = 0; + uint64_t _tpOff = 0; + std::once_flag _gotSymOnce; + std::once_flag _tpOffOnce; }; -class ARMTargetHandler final : public DefaultTargetHandler<ARMELFType> { +class ARMTargetHandler final : public TargetHandler { public: - ARMTargetHandler(ARMLinkingContext &context); - - ARMTargetLayout<ARMELFType> &getTargetLayout() override { - return *(_armTargetLayout.get()); - } - - void registerRelocationNames(Registry ®istry) override; + ARMTargetHandler(ARMLinkingContext &ctx); - const ARMTargetRelocationHandler &getRelocationHandler() const override { - return *(_armRelocationHandler.get()); + const TargetRelocationHandler &getRelocationHandler() const override { + return *_relocationHandler; } std::unique_ptr<Reader> getObjReader() override { - return std::unique_ptr<Reader>(new ARMELFObjectReader(_context)); + return llvm::make_unique<ELFReader<ARMELFFile>>(_ctx); } std::unique_ptr<Reader> getDSOReader() override { - return std::unique_ptr<Reader>(new ARMELFDSOReader(_context)); + return llvm::make_unique<ELFReader<DynamicFile<ELF32LE>>>(_ctx); } 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; + ARMLinkingContext &_ctx; + std::unique_ptr<ARMTargetLayout> _targetLayout; + std::unique_ptr<ARMTargetRelocationHandler> _relocationHandler; }; } // end namespace elf diff --git a/lib/ReaderWriter/ELF/ARM/Makefile b/lib/ReaderWriter/ELF/ARM/Makefile deleted file mode 100644 index f67d36a1b612d..0000000000000 --- a/lib/ReaderWriter/ELF/ARM/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===------ 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 index d05419decb786..61b585ae698c2 100644 --- a/lib/ReaderWriter/ELF/ARM/TODO.rst +++ b/lib/ReaderWriter/ELF/ARM/TODO.rst @@ -4,14 +4,15 @@ 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 +* C++ code linking +* PLT entries' generation for images larger than 2^28 bytes (see Sec. A.3 of the ARM ELF reference) +* ARM/Thumb interwork veneers in position-independent code +* .ARM.exidx section (exception handling) * -init/-fini options -* Lots of relocations +* Proper debug information (DWARF data) +* TLS relocations for dynamic models +* Lots of other relocations Unimplemented Relocations ######################### diff --git a/lib/ReaderWriter/ELF/Atoms.cpp b/lib/ReaderWriter/ELF/Atoms.cpp new file mode 100644 index 0000000000000..639633393161e --- /dev/null +++ b/lib/ReaderWriter/ELF/Atoms.cpp @@ -0,0 +1,297 @@ +//===- lib/ReaderWriter/ELF/Atoms.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 "DynamicFile.h" +#include "ELFFile.h" +#include "TargetHandler.h" + +namespace lld { +namespace elf { + +template <class ELFT> AbsoluteAtom::Scope ELFAbsoluteAtom<ELFT>::scope() const { + if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN) + return scopeLinkageUnit; + if (_symbol->getBinding() == llvm::ELF::STB_LOCAL) + return scopeTranslationUnit; + return scopeGlobal; +} + +template <class ELFT> +UndefinedAtom::CanBeNull ELFUndefinedAtom<ELFT>::canBeNull() const { + if (_symbol->getBinding() == llvm::ELF::STB_WEAK) + return CanBeNull::canBeNullAtBuildtime; + return CanBeNull::canBeNullNever; +} + +template <class ELFT> uint64_t ELFDefinedAtom<ELFT>::size() const { + // 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(); +} + +template <class ELFT> AbsoluteAtom::Scope ELFDefinedAtom<ELFT>::scope() const { + if (!_symbol) + return scopeGlobal; + if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN) + return scopeLinkageUnit; + if (_symbol->getBinding() != llvm::ELF::STB_LOCAL) + return scopeGlobal; + return scopeTranslationUnit; +} + +template <class ELFT> DefinedAtom::Merge ELFDefinedAtom<ELFT>::merge() const { + 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; +} + +template <class ELFT> +DefinedAtom::ContentType ELFDefinedAtom<ELFT>::doContentType() const { + using namespace llvm::ELF; + + if (_section->sh_type == SHT_GROUP) + return typeGroupComdat; + if (!_symbol && _sectionName.startswith(".gnu.linkonce")) + return typeGnuLinkOnce; + + uint64_t flags = _section->sh_flags; + + if (!(flags & SHF_ALLOC)) { + if (_section->sh_type == SHT_NOTE) + return (flags == SHF_WRITE) ? typeRWNote : typeRONote; + return _contentType = typeNoAlloc; + } + + if (_section->sh_flags == (SHF_ALLOC | SHF_WRITE | SHF_TLS)) + return _section->sh_type == SHT_NOBITS ? typeThreadZeroFill + : typeThreadData; + + if (_section->sh_flags == SHF_ALLOC && _section->sh_type == SHT_PROGBITS) + return _contentType = typeConstant; + if (_symbol->getType() == STT_GNU_IFUNC) + return _contentType = typeResolver; + if (_symbol->st_shndx == SHN_COMMON) + return _contentType = typeZeroFill; + + if (_section->sh_type == SHT_PROGBITS) { + flags &= ~SHF_ALLOC; + flags &= ~SHF_GROUP; + if ((flags & SHF_STRINGS) || (flags & SHF_MERGE)) + return typeConstant; + if (flags == SHF_WRITE) + return typeData; + return typeCode; + } + if (_section->sh_type == SHT_NOTE) { + flags &= ~SHF_ALLOC; + return (flags == SHF_WRITE) ? typeRWNote : typeRONote; + } + if (_section->sh_type == SHT_NOBITS) + return typeZeroFill; + + if (_section->sh_type == SHT_NULL) + if (_symbol->getType() == STT_COMMON || _symbol->st_shndx == SHN_COMMON) + return typeZeroFill; + + if (_section->sh_type == SHT_INIT_ARRAY || + _section->sh_type == SHT_FINI_ARRAY) + return typeData; + return typeUnknown; +} + +template <class ELFT> +DefinedAtom::ContentType ELFDefinedAtom<ELFT>::contentType() const { + if (_contentType != typeUnknown) + return _contentType; + _contentType = doContentType(); + return _contentType; +} + +template <class ELFT> +DefinedAtom::Alignment ELFDefinedAtom<ELFT>::alignment() const { + if (!_symbol) + return 1; + + // Obtain proper value of st_value field. + const auto symValue = getSymbolValue(); + + // 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 symValue; + } + if (_section->sh_addralign == 0) { + // sh_addralign of 0 means no alignment + return Alignment(1, symValue); + } + return Alignment(_section->sh_addralign, symValue % _section->sh_addralign); +} + +// Do we have a choice for ELF? All symbols live in explicit sections. +template <class ELFT> +DefinedAtom::SectionChoice ELFDefinedAtom<ELFT>::sectionChoice() const { + 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; +} + +template <class ELFT> +StringRef ELFDefinedAtom<ELFT>::customSectionName() const { + if ((contentType() == typeZeroFill) || + (_symbol && _symbol->st_shndx == llvm::ELF::SHN_COMMON)) + return ".bss"; + return _sectionName; +} + +template <class ELFT> +DefinedAtom::ContentPermissions ELFDefinedAtom<ELFT>::permissions() const { + 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___; + } +} + +template <class ELFT> +DefinedAtom::reference_iterator ELFDefinedAtom<ELFT>::begin() const { + uintptr_t index = _referenceStartIndex; + const void *it = reinterpret_cast<const void *>(index); + return reference_iterator(*this, it); +} + +template <class ELFT> +DefinedAtom::reference_iterator ELFDefinedAtom<ELFT>::end() const { + uintptr_t index = _referenceEndIndex; + const void *it = reinterpret_cast<const void *>(index); + return reference_iterator(*this, it); +} + +template <class ELFT> +const Reference *ELFDefinedAtom<ELFT>::derefIterator(const void *It) const { + uintptr_t index = reinterpret_cast<uintptr_t>(It); + assert(index >= _referenceStartIndex); + assert(index < _referenceEndIndex); + return ((_referenceList)[index]); +} + +template <class ELFT> +void ELFDefinedAtom<ELFT>::incrementIterator(const void *&It) const { + uintptr_t index = reinterpret_cast<uintptr_t>(It); + ++index; + It = reinterpret_cast<const void *>(index); +} + +template <class ELFT> +void ELFDefinedAtom<ELFT>::addReference(ELFReference<ELFT> *reference) { + _referenceList.push_back(reference); + _referenceEndIndex = _referenceList.size(); +} + +template <class ELFT> AbsoluteAtom::Scope ELFDynamicAtom<ELFT>::scope() const { + if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN) + return scopeLinkageUnit; + if (_symbol->getBinding() != llvm::ELF::STB_LOCAL) + return scopeGlobal; + return scopeTranslationUnit; +} + +template <class ELFT> +SharedLibraryAtom::Type ELFDynamicAtom<ELFT>::type() const { + 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; + } +} + +#define INSTANTIATE(klass) \ + template class klass<ELF32LE>; \ + template class klass<ELF32BE>; \ + template class klass<ELF64LE>; \ + template class klass<ELF64BE> + +INSTANTIATE(ELFAbsoluteAtom); +INSTANTIATE(ELFDefinedAtom); +INSTANTIATE(ELFDynamicAtom); +INSTANTIATE(ELFUndefinedAtom); + +} // end namespace elf +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/Atoms.h b/lib/ReaderWriter/ELF/Atoms.h index 6a506d21d9385..390c0e16baf83 100644 --- a/lib/ReaderWriter/ELF/Atoms.h +++ b/lib/ReaderWriter/ELF/Atoms.h @@ -13,6 +13,7 @@ #include "TargetHandler.h" #include "lld/Core/LLVM.h" #include "lld/Core/Simple.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringSwitch.h" #include <memory> @@ -41,19 +42,16 @@ 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) {} + _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) {} + _targetSymbolIndex(idx), _offsetInAtom(off) {} ELFReference(uint32_t edgeKind) : Reference(Reference::KindNamespace::all, Reference::KindArch::all, - edgeKind), - _target(nullptr), _targetSymbolIndex(0), _offsetInAtom(0), _addend(0) {} + edgeKind) {} uint64_t offsetInAtom() const override { return _offsetInAtom; } @@ -73,10 +71,10 @@ public: void setTarget(const Atom *newAtom) override { _target = newAtom; } private: - const Atom *_target; - uint64_t _targetSymbolIndex; - uint64_t _offsetInAtom; - Addend _addend; + const Atom *_target = nullptr; + uint64_t _targetSymbolIndex = 0; + uint64_t _offsetInAtom = 0; + Addend _addend = 0; }; /// \brief These atoms store symbols that are fixed to a particular address. @@ -88,21 +86,11 @@ template <class ELFT> class ELFAbsoluteAtom : public AbsoluteAtom { public: ELFAbsoluteAtom(const ELFFile<ELFT> &file, StringRef name, const Elf_Sym *symbol, uint64_t value) - : _owningFile(file), _name(name), _symbol(symbol), _value(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; - } - + Scope scope() const override; StringRef name() const override { return _name; } - uint64_t value() const override { return _value; } private: @@ -114,7 +102,7 @@ private: /// \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 { +template <class ELFT> class ELFUndefinedAtom : public UndefinedAtom { typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; public: @@ -122,16 +110,11 @@ public: : _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; - } + CanBeNull canBeNull() const override; private: const File &_owningFile; @@ -157,283 +140,49 @@ public: _referenceList(referenceList), _contentType(typeUnknown), _permissions(permUnknown) {} - ~ELFDefinedAtom() {} + ~ELFDefinedAtom() override = default; 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; - } + uint64_t size() const override; + Scope scope() const override; // 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; - } + Merge merge() const override; + ContentType contentType() const override; + Alignment alignment() const override; + SectionChoice sectionChoice() const override; + StringRef customSectionName() const override; // 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___; - } - } - + ContentPermissions permissions() const override; 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(); - } + DefinedAtom::reference_iterator begin() const override; + DefinedAtom::reference_iterator end() const override; + const Reference *derefIterator(const void *It) const override; + void incrementIterator(const void *&It) const override; + void addReference(ELFReference<ELFT> *reference); 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; + virtual uint64_t getSymbolValue() const { + return _symbol->st_value; } -protected: + ContentType doContentType() const; + const ELFFile<ELFT> &_owningFile; StringRef _symbolName; StringRef _sectionName; @@ -463,39 +212,25 @@ public: } 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)); + return Alignment(_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 { @@ -517,7 +252,6 @@ public: void incrementIterator(const void *&It) const override {} private: - const ELFFile<ELFT> &_owningFile; StringRef _sectionName; const Elf_Shdr *_section; @@ -530,21 +264,14 @@ private: template <class ELFT> class ELFCommonAtom : public DefinedAtom { typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; public: - ELFCommonAtom(const ELFFile<ELFT> &file, - StringRef symbolName, + ELFCommonAtom(const ELFFile<ELFT> &file, StringRef symbolName, const Elf_Sym *symbol) - : _owningFile(file), - _symbolName(symbolName), - _symbol(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 { @@ -556,23 +283,13 @@ public: } 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)); - } - + Alignment alignment() const override { return Alignment(_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 { @@ -608,42 +325,19 @@ public: ELFDynamicAtom(const DynamicFile<ELFT> &file, StringRef symbolName, StringRef loadName, const Elf_Sym *symbol) : _owningFile(file), _symbolName(symbolName), _loadName(loadName), - _symbol(symbol) { - } + _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; - } - + virtual Scope scope() const; 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; - } + Type type() const override; + uint64_t size() const override { return _symbol->st_size; } private: @@ -658,35 +352,33 @@ 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); + uint64_t off, const Atom *t, Reference::Addend a) { + addReference(Reference::KindNamespace::ELF, arch, kindValue, off, t, a); } void addReferenceELF_Hexagon(Reference::KindValue relocType, uint64_t off, const Atom *t, Reference::Addend a) { - this->addReferenceELF(Reference::KindArch::Hexagon, relocType, off, t, a); + 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); + 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); + 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); + 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); + addReferenceELF(Reference::KindArch::ARM, relocType, off, t, a); } }; @@ -695,26 +387,14 @@ public: 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); - } - + Alignment alignment() const override { return 8; } StringRef name() const override { return _name; } std::string _name; @@ -729,21 +409,12 @@ public: : 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); - } + Alignment alignment() const override { return 8; } #ifndef NDEBUG StringRef name() const override { return _name; } @@ -761,20 +432,12 @@ public: : 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 - } + Alignment alignment() const override { return 16; } #ifndef NDEBUG StringRef name() const override { return _name; } @@ -793,57 +456,38 @@ public: } }; -class GLOBAL_OFFSET_TABLEAtom : public SimpleELFDefinedAtom { +class GlobalOffsetTableAtom : public SimpleELFDefinedAtom { public: - GLOBAL_OFFSET_TABLEAtom(const File &f) : SimpleELFDefinedAtom(f) {} + GlobalOffsetTableAtom(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); - } - + Alignment alignment() const override { return 8; } ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } }; -class DYNAMICAtom : public SimpleELFDefinedAtom { +class DynamicAtom : public SimpleELFDefinedAtom { public: - DYNAMICAtom(const File &f) : SimpleELFDefinedAtom(f) {} + 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); } - + Alignment alignment() const override { return 1; } ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } }; + } // end namespace elf } // end namespace lld -#endif +#endif // LLD_READER_WRITER_ELF_ATOMS_H diff --git a/lib/ReaderWriter/ELF/CMakeLists.txt b/lib/ReaderWriter/ELF/CMakeLists.txt index fd4cb669904d9..e3e4a02b28106 100644 --- a/lib/ReaderWriter/ELF/CMakeLists.txt +++ b/lib/ReaderWriter/ELF/CMakeLists.txt @@ -1,11 +1,21 @@ add_llvm_library(lldELF + Atoms.cpp + DynamicFile.cpp + ELFFile.cpp ELFLinkingContext.cpp + FileCommon.cpp + HeaderChunks.cpp + OutputELFWriter.cpp Reader.cpp + SectionChunks.cpp + SegmentChunks.cpp + TargetLayout.cpp Writer.cpp LINK_LIBS lldReaderWriter lldCore lldYAML + LLVMObject LLVMSupport ) @@ -17,3 +27,4 @@ add_subdirectory(Mips) add_subdirectory(Hexagon) add_subdirectory(AArch64) add_subdirectory(ARM) +add_subdirectory(AMDGPU) diff --git a/lib/ReaderWriter/ELF/Chunk.h b/lib/ReaderWriter/ELF/Chunk.h index 2658d023b3a9b..f223b6c54163c 100644 --- a/lib/ReaderWriter/ELF/Chunk.h +++ b/lib/ReaderWriter/ELF/Chunk.h @@ -25,32 +25,33 @@ 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 { +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 + 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 }; + enum ContentType : uint8_t { Unknown, Header, Code, Data, Note, TLS }; + + Chunk(StringRef name, Kind kind, const ELFLinkingContext &ctx) + : _name(name), _kind(kind), _ctx(ctx) {} - 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; } @@ -59,41 +60,49 @@ public: 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;} + 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; } + 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; } + 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? + + // Returns 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; + virtual void doPreFlight() {} + // Finalize the chunk before writing - virtual void finalize() = 0; + virtual void finalize() {} 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; + const ELFLinkingContext &_ctx; + uint64_t _fsize = 0; + uint64_t _msize = 0; + uint64_t _alignment = 1; + uint32_t _order = 0; + uint64_t _ordinal = 1; + uint64_t _start = 0; + uint64_t _fileoffset = 0; }; } // end namespace elf diff --git a/lib/ReaderWriter/ELF/CreateELF.h b/lib/ReaderWriter/ELF/CreateELF.h deleted file mode 100644 index ad34dddb24d3d..0000000000000 --- a/lib/ReaderWriter/ELF/CreateELF.h +++ /dev/null @@ -1,118 +0,0 @@ -//===- 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 deleted file mode 100644 index 9af3b8eb8dc63..0000000000000 --- a/lib/ReaderWriter/ELF/DefaultLayout.h +++ /dev/null @@ -1,1050 +0,0 @@ -//===- 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 deleted file mode 100644 index 16668f2df6182..0000000000000 --- a/lib/ReaderWriter/ELF/DefaultTargetHandler.h +++ /dev/null @@ -1,38 +0,0 @@ -//===- 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.cpp b/lib/ReaderWriter/ELF/DynamicFile.cpp new file mode 100644 index 0000000000000..5339c7d66577e --- /dev/null +++ b/lib/ReaderWriter/ELF/DynamicFile.cpp @@ -0,0 +1,146 @@ +//===- lib/ReaderWriter/ELF/DynamicFile.cpp -------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DynamicFile.h" +#include "FileCommon.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Path.h" + +namespace lld { +namespace elf { + +template <class ELFT> +DynamicFile<ELFT>::DynamicFile(std::unique_ptr<MemoryBuffer> mb, + ELFLinkingContext &ctx) + : SharedLibraryFile(mb->getBufferIdentifier()), _mb(std::move(mb)), + _ctx(ctx), _useShlibUndefines(ctx.useShlibUndefines()) {} + +template <typename ELFT> +std::error_code DynamicFile<ELFT>::isCompatible(MemoryBufferRef mb, + ELFLinkingContext &ctx) { + return elf::isCompatible<ELFT>(mb, ctx); +} + +template <class ELFT> +const SharedLibraryAtom *DynamicFile<ELFT>::exports(StringRef name, + bool dataSymbolOnly) const { + 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); +} + +template <class ELFT> StringRef DynamicFile<ELFT>::getDSOName() const { + return _soname; +} + +template <class ELFT> bool DynamicFile<ELFT>::canParse(file_magic magic) { + return magic == file_magic::elf_shared_object; +} + +template <class ELFT> std::error_code DynamicFile<ELFT>::doParse() { + typedef llvm::object::ELFFile<ELFT> ELFO; + typedef typename ELFO::Elf_Shdr Elf_Shdr; + typedef typename ELFO::Elf_Dyn Elf_Dyn; + + std::error_code ec; + _objFile.reset(new ELFO(_mb->getBuffer(), ec)); + if (ec) + return ec; + + ELFO &obj = *_objFile; + + const char *base = _mb->getBuffer().data(); + const Elf_Dyn *dynStart = nullptr; + const Elf_Dyn *dynEnd = nullptr; + + const Elf_Shdr *dynSymSec = nullptr; + for (const Elf_Shdr &sec : obj.sections()) { + switch (sec.sh_type) { + case llvm::ELF::SHT_DYNAMIC: { + dynStart = reinterpret_cast<const Elf_Dyn *>(base + sec.sh_offset); + uint64_t size = sec.sh_size; + if (size % sizeof(Elf_Dyn)) + return llvm::object::object_error::parse_failed; + dynEnd = dynStart + size / sizeof(Elf_Dyn); + break; + } + case llvm::ELF::SHT_DYNSYM: + dynSymSec = &sec; + break; + } + } + + ErrorOr<StringRef> strTableOrErr = obj.getStringTableForSymtab(*dynSymSec); + if (std::error_code ec = strTableOrErr.getError()) + return ec; + StringRef stringTable = *strTableOrErr; + + for (const Elf_Dyn &dyn : llvm::make_range(dynStart, dynEnd)) { + if (dyn.d_tag == llvm::ELF::DT_SONAME) { + uint64_t offset = dyn.getVal(); + if (offset >= stringTable.size()) + return llvm::object::object_error::parse_failed; + _soname = StringRef(stringTable.data() + offset); + break; + } + } + + 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.symbol_begin(dynSymSec), e = obj.symbol_end(dynSymSec); + i != e; ++i) { + auto name = i->getName(stringTable); + 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.push_back(newAtom); + } + continue; + } + _nameToSym[*name]._symbol = &*i; + } + return std::error_code(); +} + +template class DynamicFile<ELF32LE>; +template class DynamicFile<ELF32BE>; +template class DynamicFile<ELF64LE>; +template class DynamicFile<ELF64BE>; + +} // end namespace elf +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/DynamicFile.h b/lib/ReaderWriter/ELF/DynamicFile.h index c4e3e7165efd2..a155900de7814 100644 --- a/lib/ReaderWriter/ELF/DynamicFile.h +++ b/lib/ReaderWriter/ELF/DynamicFile.h @@ -12,96 +12,39 @@ #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 { +class ELFLinkingContext; + 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; + DynamicFile(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx); - _soname = obj.getLoadName(); - if (_soname.empty()) - _soname = llvm::sys::path::filename(path()); + static std::error_code isCompatible(MemoryBufferRef mb, + ELFLinkingContext &ctx); - // 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; + const SharedLibraryAtom *exports(StringRef name, + bool dataSymbolOnly) const override; - // 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; + StringRef getDSOName() const override; - // TODO: Add absolute symbols - if (i->st_shndx == llvm::ELF::SHN_ABS) - continue; + static bool canParse(file_magic magic); - 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(); - } +protected: + std::error_code doParse() override; 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; + const typename llvm::object::ELFFile<ELFT>::Elf_Sym *_symbol = nullptr; + const SharedLibraryAtom *_atom = nullptr; }; std::unique_ptr<MemoryBuffer> _mb; @@ -110,13 +53,6 @@ private: 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 diff --git a/lib/ReaderWriter/ELF/DynamicLibraryWriter.h b/lib/ReaderWriter/ELF/DynamicLibraryWriter.h index f97514b525c0b..5f2c1d1a8288e 100644 --- a/lib/ReaderWriter/ELF/DynamicLibraryWriter.h +++ b/lib/ReaderWriter/ELF/DynamicLibraryWriter.h @@ -16,27 +16,19 @@ 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(); + DynamicLibraryWriter(ELFLinkingContext &ctx, TargetLayout<ELFT> &layout) + : OutputELFWriter<ELFT>(ctx, layout) {} protected: - std::unique_ptr<RuntimeFile<ELFT> > _runtimeFile; + void buildDynamicSymbolTable(const File &file) override; + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; + void finalizeDefaultAtomValues() override; }; //===----------------------------------------------------------------------===// @@ -62,30 +54,28 @@ void DynamicLibraryWriter<ELFT>::buildDynamicSymbolTable(const File &file) { 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( +void 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; + // Add the default atoms as defined by executables + auto file = llvm::make_unique<RuntimeFile<ELFT>>(this->_ctx, "C runtime"); + file->addAbsoluteAtom("_end"); + result.push_back(std::move(file)); } template <class ELFT> void DynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues() { - auto underScoreEndAtomIter = this->_layout.findAbsoluteAtom("_end"); + OutputELFWriter<ELFT>::finalizeDefaultAtomValues(); + AtomLayout *underScoreEndAtom = this->_layout.findAbsoluteAtom("_end"); + assert(underScoreEndAtom); if (auto bssSection = this->_layout.findOutputSection(".bss")) { - (*underScoreEndAtomIter)->_virtualAddr = + underScoreEndAtom->_virtualAddr = bssSection->virtualAddr() + bssSection->memSize(); } else if (auto dataSection = this->_layout.findOutputSection(".data")) { - (*underScoreEndAtomIter)->_virtualAddr = + underScoreEndAtom->_virtualAddr = dataSection->virtualAddr() + dataSection->memSize(); } } diff --git a/lib/ReaderWriter/ELF/ELFFile.cpp b/lib/ReaderWriter/ELF/ELFFile.cpp new file mode 100644 index 0000000000000..1488f1862b8df --- /dev/null +++ b/lib/ReaderWriter/ELF/ELFFile.cpp @@ -0,0 +1,829 @@ +//===- lib/ReaderWriter/ELF/ELFFile.cpp -------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ELFFile.h" +#include "FileCommon.h" +#include "llvm/ADT/STLExtras.h" + +namespace lld { +namespace elf { + +template <typename ELFT> +ELFFile<ELFT>::ELFFile(StringRef name, ELFLinkingContext &ctx) + : SimpleFile(name), _ordinal(0), _doStringsMerge(ctx.mergeCommonStrings()), + _useWrap(false), _ctx(ctx) { + setLastError(std::error_code()); +} + +template <typename ELFT> +ELFFile<ELFT>::ELFFile(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx) + : SimpleFile(mb->getBufferIdentifier()), _mb(std::move(mb)), _ordinal(0), + _doStringsMerge(ctx.mergeCommonStrings()), + _useWrap(ctx.wrapCalls().size()), _ctx(ctx) {} + +template <typename ELFT> +std::error_code ELFFile<ELFT>::isCompatible(MemoryBufferRef mb, + ELFLinkingContext &ctx) { + return elf::isCompatible<ELFT>(mb, ctx); +} + +template <typename ELFT> +Atom *ELFFile<ELFT>::findAtom(const Elf_Sym *sourceSym, + const Elf_Sym *targetSym) { + // Return the atom for targetSym if we can do so. + Atom *target = _symbolToAtomMapping.lookup(targetSym); + if (!target) + // Some realocations (R_ARM_V4BX) do not have a defined + // target. For this cases make it points to itself. + target = _symbolToAtomMapping.lookup(sourceSym); + + if (target->definition() != Atom::definitionRegular) + return target; + Atom::Scope scope = llvm::cast<DefinedAtom>(target)->scope(); + if (scope == DefinedAtom::scopeTranslationUnit) + return target; + if (!redirectReferenceUsingUndefAtom(sourceSym, targetSym)) + return target; + + // Otherwise, create a new undefined symbol and returns it. + StringRef targetName = target->name(); + auto it = _undefAtomsForGroupChild.find(targetName); + if (it != _undefAtomsForGroupChild.end()) + return it->getValue(); + auto atom = new (_readerStorage) SimpleUndefinedAtom(*this, targetName); + _undefAtomsForGroupChild[targetName] = atom; + addAtom(*atom); + return atom; +} + +template <typename ELFT> +ErrorOr<StringRef> ELFFile<ELFT>::getSectionName(const Elf_Shdr *shdr) const { + if (!shdr) + return StringRef(); + return _objFile->getSectionName(shdr); +} + +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()) { + switch (section.sh_type) { + case llvm::ELF::SHT_SYMTAB: + _symtab = §ion; + continue; + case llvm::ELF::SHT_SYMTAB_SHNDX: { + ErrorOr<ArrayRef<Elf_Word>> tableOrErr = _objFile->getSHNDXTable(section); + if (std::error_code ec = tableOrErr.getError()) + return ec; + _shndxTable = *tableOrErr; + continue; + } + } + + if (isIgnoredSection(§ion)) + continue; + + if (isMergeableStringSection(§ion)) { + _mergeStringSections.push_back(§ion); + continue; + } + + if (section.sh_type == llvm::ELF::SHT_RELA) { + auto sHdrOrErr = _objFile->getSection(section.sh_info); + if (std::error_code ec = sHdrOrErr.getError()) + return ec; + auto sHdr = *sHdrOrErr; + auto rai = _objFile->rela_begin(§ion); + auto rae = _objFile->rela_end(§ion); + _relocationAddendReferences[sHdr] = make_range(rai, rae); + totalRelocs += std::distance(rai, rae); + } else if (section.sh_type == llvm::ELF::SHT_REL) { + auto sHdrOrErr = _objFile->getSection(section.sh_info); + if (std::error_code ec = sHdrOrErr.getError()) + return ec; + auto sHdr = *sHdrOrErr; + auto ri = _objFile->rel_begin(§ion); + auto re = _objFile->rel_end(§ion); + _relocationReferences[sHdr] = §ion; + totalRelocs += std::distance(ri, re); + } else { + auto sectionName = _objFile->getSectionName(§ion); + if (std::error_code ec = sectionName.getError()) + return ec; + _ctx.notifyInputSectionName(*sectionName); + _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()); + ELFMergeAtom<ELFT> *atom = createMergedString(tai->_sectionName, tai->_shdr, + content, tai->_offset); + atom->setOrdinal(++_ordinal); + addAtom(*atom); + _mergeAtoms.push_back(atom); + } + 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. + if (!_symtab) + return std::error_code(); + + ErrorOr<StringRef> strTableOrErr = + _objFile->getStringTableForSymtab(*_symtab); + if (std::error_code ec = strTableOrErr.getError()) + return ec; + StringRef strTable = *strTableOrErr; + + auto SymI = _objFile->symbol_begin(_symtab), + SymE = _objFile->symbol_end(_symtab); + // Skip over dummy sym. + ++SymI; + + for (; SymI != SymE; ++SymI) { + ErrorOr<const Elf_Shdr *> section = + _objFile->getSection(SymI, _symtab, _shndxTable); + if (std::error_code ec = section.getError()) + return ec; + + auto symbolName = SymI->getName(strTable); + if (std::error_code ec = symbolName.getError()) + return ec; + + if (SymI->isAbsolute()) { + ELFAbsoluteAtom<ELFT> *absAtom = createAbsoluteAtom( + *symbolName, &*SymI, (int64_t)getSymbolValue(&*SymI)); + addAtom(*absAtom); + _symbolToAtomMapping.insert(std::make_pair(&*SymI, absAtom)); + } else if (SymI->isUndefined()) { + if (_useWrap && + (_wrapSymbolMap.find(*symbolName) != _wrapSymbolMap.end())) { + auto wrapAtom = _wrapSymbolMap.find(*symbolName); + _symbolToAtomMapping.insert( + std::make_pair(&*SymI, wrapAtom->getValue())); + continue; + } + ELFUndefinedAtom<ELFT> *undefAtom = + createUndefinedAtom(*symbolName, &*SymI); + addAtom(*undefAtom); + _symbolToAtomMapping.insert(std::make_pair(&*SymI, undefAtom)); + } else if (isCommonSymbol(&*SymI)) { + ELFCommonAtom<ELFT> *commonAtom = createCommonAtom(*symbolName, &*SymI); + commonAtom->setOrdinal(++_ordinal); + addAtom(*commonAtom); + _symbolToAtomMapping.insert(std::make_pair(&*SymI, commonAtom)); + } else if (SymI->isDefined()) { + _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; + + // Contains a list of comdat sections for a group. + for (auto &i : _sectionSymbols) { + const Elf_Shdr *section = i.first; + std::vector<const Elf_Sym *> &symbols = i.second; + + // Sort symbols by position. + std::stable_sort(symbols.begin(), symbols.end(), + [this](const Elf_Sym *a, const Elf_Sym *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; + + // SHT_GROUP sections are handled in the following loop. + if (isGroupSection(section)) + continue; + + bool addAtoms = (!isGnuLinkOnceSection(*sectionName) && + !isSectionMemberOfGroup(section)); + + if (handleSectionWithNoSymbols(section, symbols)) { + ELFDefinedAtom<ELFT> *newAtom = + createSectionAtom(section, *sectionName, *sectionContents); + newAtom->setOrdinal(++_ordinal); + if (addAtoms) + addAtom(*newAtom); + else + atomsForSection[*sectionName].push_back(newAtom); + continue; + } + + ELFDefinedAtom<ELFT> *previousAtom = nullptr; + ELFReference<ELFT> *anonFollowedBy = nullptr; + + if (!_symtab) + continue; + ErrorOr<StringRef> strTableOrErr = + _objFile->getStringTableForSymtab(*_symtab); + if (std::error_code ec = strTableOrErr.getError()) + return ec; + StringRef strTable = *strTableOrErr; + 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 = symbol->getName(strTable); + 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>(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) + continue; + ELFDefinedAtom<ELFT> *atom = createDefinedAtom( + symbolName, *sectionName, &**si, section, symbolData, + _references.size(), _references.size(), _references); + atom->setOrdinal(++_ordinal); + if (addAtoms) + addAtom(*atom); + else + atomsForSection[*sectionName].push_back(atom); + 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>(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) + addAtom(*newAtom); + else + atomsForSection[*sectionName].push_back(newAtom); + + _symbolToAtomMapping.insert(std::make_pair(&*symbol, newAtom)); + if (anonAtom) { + anonAtom->setOrdinal(++_ordinal); + if (addAtoms) + addAtom(*anonAtom); + else + atomsForSection[*sectionName].push_back(anonAtom); + } + } + } + + for (auto &i : _sectionSymbols) + if (std::error_code ec = handleSectionGroup(i.first, atomsForSection)) + return ec; + for (auto &i : _sectionSymbols) + if (std::error_code ec = handleGnuLinkOnceSection(i.first, atomsForSection)) + return ec; + + updateReferences(); + return std::error_code(); +} + +template <class ELFT> +std::error_code ELFFile<ELFT>::handleGnuLinkOnceSection( + const Elf_Shdr *section, + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection) { + ErrorOr<StringRef> sectionName = this->getSectionName(section); + if (std::error_code ec = sectionName.getError()) + return ec; + if (!isGnuLinkOnceSection(*sectionName)) + return std::error_code(); + + unsigned int referenceStart = _references.size(); + std::vector<ELFReference<ELFT> *> refs; + for (auto ha : atomsForSection[*sectionName]) { + _groupChild[ha->symbol()] = std::make_pair(*sectionName, section); + auto *ref = + new (_readerStorage) ELFReference<ELFT>(Reference::kindGroupChild); + ref->setTarget(ha); + refs.push_back(ref); + } + atomsForSection[*sectionName].clear(); + // Create a gnu linkonce atom. + ELFDefinedAtom<ELFT> *atom = createDefinedAtom( + *sectionName, *sectionName, nullptr, section, ArrayRef<uint8_t>(), + referenceStart, _references.size(), _references); + atom->setOrdinal(++_ordinal); + addAtom(*atom); + for (auto reference : refs) + atom->addReference(reference); + return std::error_code(); +} + +template <class ELFT> +std::error_code ELFFile<ELFT>::handleSectionGroup( + const Elf_Shdr *section, + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection) { + ErrorOr<StringRef> sectionName = this->getSectionName(section); + if (std::error_code ec = sectionName.getError()) + return ec; + if (!isGroupSection(section)) + return std::error_code(); + + auto sectionContents = getSectionContents(section); + if (std::error_code ec = sectionContents.getError()) + return ec; + + // 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. + std::vector<StringRef> sectionNames; + const Elf_Word *groupMembers = + reinterpret_cast<const Elf_Word *>(sectionContents->data()); + const size_t count = section->sh_size / sizeof(Elf_Word); + for (size_t i = 1; i < count; i++) { + ErrorOr<const Elf_Shdr *> shdr = _objFile->getSection(groupMembers[i]); + if (std::error_code ec = shdr.getError()) + return ec; + ErrorOr<StringRef> sectionName = _objFile->getSectionName(*shdr); + if (std::error_code ec = sectionName.getError()) + return ec; + sectionNames.push_back(*sectionName); + } + ErrorOr<const Elf_Shdr *> symtab = _objFile->getSection(section->sh_link); + if (std::error_code ec = symtab.getError()) + return ec; + const Elf_Sym *symbol = _objFile->getSymbol(*symtab, section->sh_info); + ErrorOr<const Elf_Shdr *> strtab_sec = + _objFile->getSection((*symtab)->sh_link); + if (std::error_code ec = strtab_sec.getError()) + return ec; + ErrorOr<StringRef> strtab_or_err = _objFile->getStringTable(*strtab_sec); + if (std::error_code ec = strtab_or_err.getError()) + return ec; + StringRef strtab = *strtab_or_err; + ErrorOr<StringRef> symbolName = symbol->getName(strtab); + if (std::error_code ec = symbolName.getError()) + return ec; + + unsigned int referenceStart = _references.size(); + std::vector<ELFReference<ELFT> *> refs; + for (auto name : sectionNames) { + for (auto ha : atomsForSection[name]) { + _groupChild[ha->symbol()] = std::make_pair(*symbolName, section); + auto *ref = + new (_readerStorage) ELFReference<ELFT>(Reference::kindGroupChild); + ref->setTarget(ha); + refs.push_back(ref); + } + atomsForSection[name].clear(); + } + + // Create an atom for comdat signature. + ELFDefinedAtom<ELFT> *atom = createDefinedAtom( + *symbolName, *sectionName, nullptr, section, ArrayRef<uint8_t>(), + referenceStart, _references.size(), _references); + atom->setOrdinal(++_ordinal); + addAtom(*atom); + for (auto reference : refs) + atom->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)); + addAtom(*wrapSymAtom); + addAtom(*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(section); + if (rari != _relocationAddendReferences.end()) + createRelocationReferences(symbol, symContent, rari->second); + + // Add Rel references. + auto rri = _relocationReferences.find(section); + if (rri != _relocationReferences.end()) + createRelocationReferences(symbol, symContent, secContent, rri->second); + + // Create the DefinedAtom and add it to the list of DefinedAtoms. + return createDefinedAtom(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<const Elf_Rela *> 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, + const Elf_Shdr *relSec) { + auto rels = _objFile->rels(relSec); + 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)); + Reference::Addend addend = getInitialAddend(symContent, symValue, rel); + 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() != Reference::KindNamespace::ELF) + continue; + const Elf_Sym *symbol = + _objFile->getSymbol(_symtab, ri->targetSymbolIndex()); + ErrorOr<const Elf_Shdr *> shdr = + _objFile->getSection(symbol, _symtab, _shndxTable); + + // 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) { + auto *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; +} + +template <class ELFT> +void RuntimeFile<ELFT>::addAbsoluteAtom(StringRef symbolName, bool isHidden) { + assert(!symbolName.empty() && "AbsoluteAtoms must have a name"); + auto *sym = new (this->_readerStorage) Elf_Sym; + sym->st_name = 0; + sym->st_value = 0; + sym->st_shndx = llvm::ELF::SHN_ABS; + sym->setBindingAndType(llvm::ELF::STB_GLOBAL, llvm::ELF::STT_OBJECT); + if (isHidden) + sym->setVisibility(llvm::ELF::STV_HIDDEN); + else + sym->setVisibility(llvm::ELF::STV_DEFAULT); + sym->st_size = 0; + ELFAbsoluteAtom<ELFT> *atom = this->createAbsoluteAtom(symbolName, sym, -1); + this->addAtom(*atom); +} + +template <class ELFT> +void RuntimeFile<ELFT>::addUndefinedAtom(StringRef symbolName) { + assert(!symbolName.empty() && "UndefinedAtoms must have a name"); + auto *sym = new (this->_readerStorage) Elf_Sym; + sym->st_name = 0; + sym->st_value = 0; + sym->st_shndx = llvm::ELF::SHN_UNDEF; + sym->setBindingAndType(llvm::ELF::STB_GLOBAL, llvm::ELF::STT_NOTYPE); + sym->setVisibility(llvm::ELF::STV_DEFAULT); + sym->st_size = 0; + ELFUndefinedAtom<ELFT> *atom = this->createUndefinedAtom(symbolName, sym); + this->addAtom(*atom); +} + +template class ELFFile<ELF32LE>; +template class ELFFile<ELF32BE>; +template class ELFFile<ELF64LE>; +template class ELFFile<ELF64BE>; + +template class RuntimeFile<ELF32LE>; +template class RuntimeFile<ELF32BE>; +template class RuntimeFile<ELF64LE>; +template class RuntimeFile<ELF64BE>; + +} // end namespace elf +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/ELFFile.h b/lib/ReaderWriter/ELF/ELFFile.h index 11f4ee4fc633f..5e0c2fc75a87c 100644 --- a/lib/ReaderWriter/ELF/ELFFile.h +++ b/lib/ReaderWriter/ELF/ELFFile.h @@ -1,4 +1,4 @@ -//===- lib/ReaderWriter/ELF/ELFFile.h -------------------------------------===// +//===- lib/ReaderWriter/ELF/ELFFile.h ---------------------------*- C++ -*-===// // // The LLVM Linker // @@ -11,7 +11,8 @@ #define LLD_READER_WRITER_ELF_FILE_H #include "Atoms.h" -#include <llvm/ADT/MapVector.h> +#include "FileCommon.h" +#include "llvm/ADT/MapVector.h" #include <map> #include <unordered_map> @@ -20,26 +21,20 @@ 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 { - +template <class ELFT> class ELFFile : public SimpleFile { 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), @@ -71,23 +66,15 @@ template <class ELFT> class ELFFile : public File { // 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) { + ELFMergeAtom<ELFT> *findMergeAtom(const Elf_Shdr *shdr, int64_t offset) { auto it = std::find_if(_mergeAtoms.begin(), _mergeAtoms.end(), - FindByOffset(shdr, offset)); + [=](const ELFMergeAtom<ELFT> *a) { + int64_t off = a->offset(); + return shdr->sh_name == a->section() && + offset >= off && + offset <= off + (int64_t)a->size(); + }); assert(it != _mergeAtoms.end()); return *it; } @@ -97,19 +84,15 @@ template <class ELFT> class ELFFile : public File { 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(StringRef name, ELFLinkingContext &ctx); + ELFFile(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx); - 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 std::error_code isCompatible(MemoryBufferRef mb, + ELFLinkingContext &ctx); - static ErrorOr<std::unique_ptr<ELFFile>> - create(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx); + static bool canParse(file_magic magic) { + return magic == file_magic::elf_relocatable; + } virtual Reference::KindArch kindArch(); @@ -132,41 +115,14 @@ public: /// \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); - } + // Assuming sourceSymbol has a reference to targetSym, find an atom + // for targetSym. Usually it's just the atom for targetSym. + // However, if an atom is in a section group, we may want to return an + // undefined atom for targetSym to let the resolver to resolve the + // symbol. (It's because if targetSym is in a section group A, and the + // group A is not linked in because other file already provides a + // section group B, we want to resolve references to B, not to A.) + Atom *findAtom(const Elf_Sym *sourceSym, const Elf_Sym *targetSym); protected: ELFDefinedAtom<ELFT> *createDefinedAtomAndAssignRelocations( @@ -179,13 +135,13 @@ protected: /// \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); + range<const Elf_Rela *> 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); + const Elf_Shdr *relSec); /// \brief After all the Atoms and References are created, update each /// Reference's target with the Atom pointer it refers to. @@ -224,11 +180,7 @@ protected: 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); - } + ErrorOr<StringRef> getSectionName(const Elf_Shdr *shdr) const; /// Determines if the section occupy memory space. bool sectionOccupiesMemorySpace(const Elf_Shdr *shdr) const { @@ -242,45 +194,38 @@ protected: 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(); + bool + handleSectionWithNoSymbols(const Elf_Shdr *shdr, + std::vector<const Elf_Sym *> &syms) const { + return shdr && + (shdr->sh_type == llvm::ELF::SHT_PROGBITS || + shdr->sh_type == llvm::ELF::SHT_INIT_ARRAY || + shdr->sh_type == llvm::ELF::SHT_FINI_ARRAY || + shdr->sh_type == llvm::ELF::SHT_NOTE) && + 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); + const Elf_Shdr *section, + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection); - // Handle Section groups/COMDAT scetions. + // Handle 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); + const Elf_Shdr *section, + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection); /// Process the Undefined symbol and create an atom for it. - ErrorOr<ELFUndefinedAtom<ELFT> *> - handleUndefinedSymbol(StringRef symName, const Elf_Sym *sym) { + ELFUndefinedAtom<ELFT> *createUndefinedAtom(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) { + ELFAbsoluteAtom<ELFT> *createAbsoluteAtom(StringRef symName, + const Elf_Sym *sym, int64_t value) { return new (_readerStorage) ELFAbsoluteAtom<ELFT>(*this, symName, sym, value); } @@ -316,42 +261,39 @@ protected: 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 initial addend + virtual Reference::Addend getInitialAddend(ArrayRef<uint8_t> symContent, + uint64_t symbolValue, + const Elf_Rel& reference) const { + return *(symContent.data() + reference.r_offset - symbolValue); } - /// 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 common symbol and create an atom for it. + virtual ELFCommonAtom<ELFT> *createCommonAtom(StringRef symName, + const Elf_Sym *sym) { + return new (_readerStorage) ELFCommonAtom<ELFT>(*this, symName, sym); } - /// 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) { + /// Creates an atom for a given defined symbol. + virtual ELFDefinedAtom<ELFT> * + createDefinedAtom(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> *createMergedString(StringRef sectionName, + const Elf_Shdr *sectionHdr, + ArrayRef<uint8_t> contentData, + unsigned int offset) { + auto *mergeAtom = new (_readerStorage) ELFMergeAtom<ELFT>(*this, sectionName, sectionHdr, contentData, offset); - const MergeSectionKey mergedSectionKey(sectionHdr, offset); + const MergeSectionKey mergedSectionKey = {sectionHdr, offset}; if (_mergedSectionMap.find(mergedSectionKey) == _mergedSectionMap.end()) _mergedSectionMap.insert(std::make_pair(mergedSectionKey, mergeAtom)); return mergeAtom; @@ -380,19 +322,17 @@ protected: 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; + const Elf_Shdr *_symtab = nullptr; + ArrayRef<Elf_Word> _shndxTable; /// \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; + std::unordered_map<const Elf_Shdr *, range<const Elf_Rela *>> + _relocationAddendReferences; MergedSectionMapT _mergedSectionMap; - std::unordered_map<StringRef, range<Elf_Rel_Iter>> _relocationReferences; + std::unordered_map<const Elf_Shdr *, const Elf_Shdr *> _relocationReferences; std::vector<ELFReference<ELFT> *> _references; llvm::DenseMap<const Elf_Sym *, Atom *> _symbolToAtomMapping; llvm::DenseMap<const ELFReference<ELFT> *, const Elf_Sym *> @@ -409,7 +349,8 @@ protected: /// \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; + llvm::MapVector<const Elf_Shdr *, std::vector<const Elf_Sym *>> + _sectionSymbols; /// \brief Sections that have merge string property std::vector<const Elf_Shdr *> _mergeStringSections; @@ -438,741 +379,16 @@ protected: 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) {} + RuntimeFile(ELFLinkingContext &ctx, StringRef name) + : ELFFile<ELFT>(name, ctx) {} /// \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; - } + virtual void addAbsoluteAtom(StringRef symbolName, bool isHidden = false); /// \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"); - } + virtual void addUndefinedAtom(StringRef symbolName); }; -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 diff --git a/lib/ReaderWriter/ELF/ELFLinkingContext.cpp b/lib/ReaderWriter/ELF/ELFLinkingContext.cpp index c7dffda8a463e..2904c7b0dae08 100644 --- a/lib/ReaderWriter/ELF/ELFLinkingContext.cpp +++ b/lib/ReaderWriter/ELF/ELFLinkingContext.cpp @@ -25,6 +25,9 @@ #include <cxxabi.h> #endif +using llvm::sys::fs::exists; +using llvm::sys::path::is_absolute; + namespace lld { class CommandLineUndefinedAtom : public SimpleUndefinedAtom { @@ -37,18 +40,6 @@ public: } }; -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>()); } @@ -61,13 +52,17 @@ uint16_t ELFLinkingContext::getOutputMachine() const { return llvm::ELF::EM_X86_64; case llvm::Triple::hexagon: return llvm::ELF::EM_HEXAGON; + case llvm::Triple::mips: case llvm::Triple::mipsel: + case llvm::Triple::mips64: 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; + case llvm::Triple::amdgcn: + return llvm::ELF::EM_AMDGPU; default: llvm_unreachable("Unhandled arch"); } @@ -84,11 +79,8 @@ bool ELFLinkingContext::validateImpl(raw_ostream &diagnostics) { case LinkingContext::OutputFileType::YAML: _writer = createWriterYAML(*this); break; - case LinkingContext::OutputFileType::Native: - llvm_unreachable("Unimplemented"); - break; default: - _writer = createWriterELF(this->targetHandler()); + _writer = createWriterELF(*this); break; } @@ -116,11 +108,13 @@ Writer &ELFLinkingContext::writer() const { return *_writer; } static void buildSearchPath(SmallString<128> &path, StringRef dir, StringRef sysRoot) { - if (!dir.startswith("=/")) - path.assign(dir); - else { + if (dir.startswith("=/")) { + // If a search directory begins with "=", "=" is replaced + // with the sysroot path. path.assign(sysRoot); path.append(dir.substr(1)); + } else { + path.assign(dir); } } @@ -134,18 +128,18 @@ ErrorOr<StringRef> ELFLinkingContext::searchLibrary(StringRef libName) const { 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())); + if (exists(path.str())) + return path.str().copy(_allocator); } // 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 (exists(path.str())) + return path.str().copy(_allocator); } - if (hasColonPrefix && llvm::sys::fs::exists(libName.drop_front())) + if (hasColonPrefix && exists(libName.drop_front())) return libName.drop_front(); return make_error_code(llvm::errc::no_such_file_or_directory); @@ -154,22 +148,23 @@ ErrorOr<StringRef> ELFLinkingContext::searchLibrary(StringRef libName) const { ErrorOr<StringRef> ELFLinkingContext::searchFile(StringRef fileName, bool isSysRooted) const { SmallString<128> path; - if (llvm::sys::path::is_absolute(fileName) && isSysRooted) { + if (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)) + if (exists(path.str())) + return path.str().copy(_allocator); + } else if (exists(fileName)) { return fileName; + } - if (llvm::sys::path::is_absolute(fileName)) + if (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())); + if (exists(path.str())) + return path.str().copy(_allocator); } return make_error_code(llvm::errc::no_such_file_or_directory); } @@ -227,6 +222,7 @@ void ELFLinkingContext::notifySymbolTableCoalesce(const Atom *existingAtom, } std::string ELFLinkingContext::demangle(StringRef symbolName) const { +#if defined(HAVE_CXXABI_H) if (!demangleSymbols()) return symbolName; @@ -234,21 +230,20 @@ std::string ELFLinkingContext::demangle(StringRef symbolName) const { 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 - + if (!demangled) + return symbolName; + std::string result(demangled); + // __cxa_demangle() always uses a malloc'ed buffer to return the result. + free(demangled); + return result; +#else return symbolName; +#endif } void ELFLinkingContext::setUndefinesResolver(std::unique_ptr<File> resolver) { @@ -256,4 +251,15 @@ void ELFLinkingContext::setUndefinesResolver(std::unique_ptr<File> resolver) { _resolver = std::move(resolver); } +void ELFLinkingContext::notifyInputSectionName(StringRef name) { + // Save sections names which can be represented as a C identifier. + if (name.find_first_not_of("0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "_") == StringRef::npos) { + std::lock_guard<std::mutex> lock(_cidentMutex); + _cidentSections.insert(name); + } +} + } // end namespace lld diff --git a/lib/ReaderWriter/ELF/ELFReader.h b/lib/ReaderWriter/ELF/ELFReader.h index 43f218115c666..60af6dff99802 100644 --- a/lib/ReaderWriter/ELF/ELFReader.h +++ b/lib/ReaderWriter/ELF/ELFReader.h @@ -10,90 +10,35 @@ #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/File.h" #include "lld/Core/Reader.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Object/ELF.h" namespace lld { namespace elf { -template <typename ELFT, typename ELFTraitsT, typename ContextT> -class ELFObjectReader : public Reader { +template <typename FileT> class ELFReader : public Reader { public: - typedef llvm::object::Elf_Ehdr_Impl<ELFT> Elf_Ehdr; + ELFReader(ELFLinkingContext &ctx) : _ctx(ctx) {} - 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); + bool canParse(file_magic magic, MemoryBufferRef mb) const override { + return FileT::canParse(magic); } - 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()) + ErrorOr<std::unique_ptr<File>> + loadFile(std::unique_ptr<MemoryBuffer> mb, + const class Registry &) const override { + if (std::error_code ec = FileT::isCompatible(mb->getMemBufferRef(), _ctx)) 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)); + std::unique_ptr<File> ret = llvm::make_unique<FileT>(std::move(mb), _ctx); + return std::move(ret); } -protected: - ContextT &_ctx; - uint64_t _machine; +private: + ELFLinkingContext &_ctx; }; } // namespace elf diff --git a/lib/ReaderWriter/ELF/ExecutableWriter.h b/lib/ReaderWriter/ELF/ExecutableWriter.h index 477e3920abaee..9d9f4d9ce0a5a 100644 --- a/lib/ReaderWriter/ELF/ExecutableWriter.h +++ b/lib/ReaderWriter/ELF/ExecutableWriter.h @@ -16,32 +16,29 @@ 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")) {} + ExecutableWriter(ELFLinkingContext &ctx, TargetLayout<ELFT> &layout) + : OutputELFWriter<ELFT>(ctx, layout) {} 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(); + void buildDynamicSymbolTable(const File &file) override; + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; + void finalizeDefaultAtomValues() override; + void createDefaultSections() override; - virtual bool isNeededTagRequired(const SharedLibraryAtom *sla) const { + bool isNeededTagRequired(const SharedLibraryAtom *sla) const override { return this->_layout.isCopied(sla); } unique_bump_ptr<InterpSection<ELFT>> _interpSection; - std::unique_ptr<RuntimeFile<ELFT> > _runtimeFile; + +private: + std::unique_ptr<RuntimeFile<ELFT>> createRuntimeFile(); }; //===----------------------------------------------------------------------===// @@ -56,8 +53,8 @@ void ExecutableWriter<ELFT>::buildDynamicSymbolTable(const File &file) { if (!da) continue; if (da->dynamicExport() != DefinedAtom::dynamicExportAlways && - !this->_context.isDynamicallyExportedSymbol(da->name()) && - !(this->_context.shouldExportDynamic() && + !this->_ctx.isDynamicallyExportedSymbol(da->name()) && + !(this->_ctx.shouldExportDynamic() && da->scope() == Atom::Scope::scopeGlobal)) continue; this->_dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), @@ -65,7 +62,7 @@ void ExecutableWriter<ELFT>::buildDynamicSymbolTable(const File &file) { } // Put weak symbols in the dynamic symbol table. - if (this->_context.isDynamic()) { + if (this->_ctx.isDynamic()) { for (const UndefinedAtom *a : file.undefined()) { if (this->_layout.isReferencedByDefinedAtom(a) && a->canBeNull() != UndefinedAtom::canBeNullNever) @@ -76,48 +73,44 @@ void ExecutableWriter<ELFT>::buildDynamicSymbolTable(const File &file) { 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"); +std::unique_ptr<RuntimeFile<ELFT>> ExecutableWriter<ELFT>::createRuntimeFile() { + auto file = llvm::make_unique<RuntimeFile<ELFT>>(this->_ctx, "C runtime"); + file->addUndefinedAtom(this->_ctx.entrySymbolName()); + file->addAbsoluteAtom("__bss_start"); + file->addAbsoluteAtom("__bss_end"); + file->addAbsoluteAtom("_end"); + file->addAbsoluteAtom("end"); + file->addAbsoluteAtom("__preinit_array_start", true); + file->addAbsoluteAtom("__preinit_array_end", true); + file->addAbsoluteAtom("__init_array_start", true); + file->addAbsoluteAtom("__init_array_end", true); + if (this->_ctx.isRelaOutputFormat()) { + file->addAbsoluteAtom("__rela_iplt_start"); + file->addAbsoluteAtom("__rela_iplt_end"); } else { - _runtimeFile->addAbsoluteAtom("__rel_iplt_start"); - _runtimeFile->addAbsoluteAtom("__rel_iplt_end"); + file->addAbsoluteAtom("__rel_iplt_start"); + file->addAbsoluteAtom("__rel_iplt_end"); } - _runtimeFile->addAbsoluteAtom("__fini_array_start"); - _runtimeFile->addAbsoluteAtom("__fini_array_end"); + file->addAbsoluteAtom("__fini_array_start", true); + file->addAbsoluteAtom("__fini_array_end", true); + return file; } /// \brief Hook in lld to add CRuntime file template <class ELFT> -bool ExecutableWriter<ELFT>::createImplicitFiles( +void 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; + result.push_back(createRuntimeFile()); } template <class ELFT> void ExecutableWriter<ELFT>::createDefaultSections() { OutputELFWriter<ELFT>::createDefaultSections(); - if (this->_context.isDynamic()) { + if (this->_ctx.isDynamic()) { _interpSection.reset(new (this->_alloc) InterpSection<ELFT>( - this->_context, ".interp", DefaultLayout<ELFT>::ORDER_INTERP, - this->_context.getInterpreter())); + this->_ctx, ".interp", TargetLayout<ELFT>::ORDER_INTERP, + this->_ctx.getInterpreter())); this->_layout.addSection(_interpSection.get()); } } @@ -126,53 +119,35 @@ template <class ELFT> void ExecutableWriter<ELFT>::createDefaultSections() { /// 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; - } - }; + AtomLayout *bssStartAtom = this->_layout.findAbsoluteAtom("__bss_start"); + AtomLayout *bssEndAtom = this->_layout.findAbsoluteAtom("__bss_end"); + AtomLayout *underScoreEndAtom = this->_layout.findAbsoluteAtom("_end"); + AtomLayout *endAtom = this->_layout.findAbsoluteAtom("end"); - 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()) && + assert((bssStartAtom || bssEndAtom || underScoreEndAtom || endAtom) && "Unable to find the absolute atoms that have been added by lld"); + this->updateScopeAtomValues("preinit_array", ".preinit_array"); + this->updateScopeAtomValues("init_array", ".init_array"); + if (this->_ctx.isRelaOutputFormat()) + this->updateScopeAtomValues("rela_iplt", ".rela.plt"); + else + this->updateScopeAtomValues("rel_iplt", ".rel.plt"); + this->updateScopeAtomValues("fini_array", ".fini_array"); + 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 = + bssStartAtom->_virtualAddr = bssSection->virtualAddr(); + bssEndAtom->_virtualAddr = bssSection->virtualAddr() + bssSection->memSize(); - (*underScoreEndAtomIter)->_virtualAddr = (*bssEndAtomIter)->_virtualAddr; - (*endAtomIter)->_virtualAddr = (*bssEndAtomIter)->_virtualAddr; + underScoreEndAtom->_virtualAddr = bssEndAtom->_virtualAddr; + endAtom->_virtualAddr = bssEndAtom->_virtualAddr; } else if (auto dataSection = this->_layout.findOutputSection(".data")) { - (*underScoreEndAtomIter)->_virtualAddr = + underScoreEndAtom->_virtualAddr = dataSection->virtualAddr() + dataSection->memSize(); - (*endAtomIter)->_virtualAddr = (*underScoreEndAtomIter)->_virtualAddr; + endAtom->_virtualAddr = underScoreEndAtom->_virtualAddr; } } diff --git a/lib/ReaderWriter/ELF/FileCommon.cpp b/lib/ReaderWriter/ELF/FileCommon.cpp new file mode 100644 index 0000000000000..c23e3f6656cd5 --- /dev/null +++ b/lib/ReaderWriter/ELF/FileCommon.cpp @@ -0,0 +1,66 @@ +//===- lib/ReaderWriter/ELF/FileCommon.cpp --------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ELFFile.h" +#include "FileCommon.h" + +using namespace llvm::ELF; + +namespace lld { +namespace elf { + +static const char *elf32_expected = "ELF32 expected, but got ELF64"; +static const char *elf64_expected = "ELF64 expected, but got ELF32"; +static const char *le_expected = + "Little endian files are expected, but got a big endian file."; +static const char *be_expected = + "Big endian files are expected, but got a little endian file."; + +template <> +std::error_code checkCompatibility<ELF32LE>(unsigned char size, + unsigned char endian) { + if (size == ELFCLASS64) + return make_dynamic_error_code(elf32_expected); + if (endian == ELFDATA2MSB) + return make_dynamic_error_code(le_expected); + return std::error_code(); +} + +template <> +std::error_code checkCompatibility<ELF32BE>(unsigned char size, + unsigned char endian) { + if (size == ELFCLASS64) + return make_dynamic_error_code(elf32_expected); + if (endian == ELFDATA2LSB) + return make_dynamic_error_code(be_expected); + return std::error_code(); +} + +template <> +std::error_code checkCompatibility<ELF64LE>(unsigned char size, + unsigned char endian) { + if (size == ELFCLASS32) + return make_dynamic_error_code(elf64_expected); + if (endian == ELFDATA2MSB) + return make_dynamic_error_code(le_expected); + return std::error_code(); +} + +template <> +std::error_code checkCompatibility<ELF64BE>(unsigned char size, + unsigned char endian) { + if (size == ELFCLASS32) + return make_dynamic_error_code(elf64_expected); + if (endian == ELFDATA2LSB) + return make_dynamic_error_code(be_expected); + return std::error_code(); +} + +} // end namespace elf +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/FileCommon.h b/lib/ReaderWriter/ELF/FileCommon.h new file mode 100644 index 0000000000000..eaff12afe72fe --- /dev/null +++ b/lib/ReaderWriter/ELF/FileCommon.h @@ -0,0 +1,45 @@ +//===- lib/ReaderWriter/ELF/FileCommon.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_COMMON_H +#define LLD_READER_WRITER_ELF_FILE_COMMON_H + +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { + +template <class ELFT> +std::error_code checkCompatibility(unsigned char size, unsigned char endian); + +template <typename ELFT> +std::error_code isCompatible(MemoryBufferRef mb, ELFLinkingContext &ctx) { + typedef llvm::object::Elf_Ehdr_Impl<ELFT> Elf_Ehdr; + + if (uintptr_t(mb.getBufferStart()) & 1) + return make_dynamic_error_code("invalid alignment"); + + auto *hdr = reinterpret_cast<const Elf_Ehdr *>(mb.getBuffer().data()); + if (hdr->e_machine != ctx.getMachineType()) + return make_dynamic_error_code("incompatible machine type"); + + unsigned char size; + unsigned char endian; + std::tie(size, endian) = llvm::object::getElfArchType(mb.getBuffer()); + if (std::error_code ec = checkCompatibility<ELFT>(size, endian)) + return ec; + return std::error_code(); +} + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/HeaderChunks.cpp b/lib/ReaderWriter/ELF/HeaderChunks.cpp new file mode 100644 index 0000000000000..193937c180614 --- /dev/null +++ b/lib/ReaderWriter/ELF/HeaderChunks.cpp @@ -0,0 +1,205 @@ +//===- lib/ReaderWriter/ELF/HeaderChunks.cpp --------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "HeaderChunks.h" +#include "TargetLayout.h" +#include "llvm/ADT/STLExtras.h" + +namespace lld { +namespace elf { + +template <class ELFT> void ELFHeader<ELFT>::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->_ctx.getOutputELFType(); + _eh.e_machine = this->_ctx.getOutputMachine(); +} + +template <class ELFT> +ELFHeader<ELFT>::ELFHeader(const ELFLinkingContext &ctx) + : Chunk<ELFT>("elfhdr", Chunk<ELFT>::Kind::ELFHeader, ctx) { + 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()); +} + +template <class ELFT> +bool ProgramHeader<ELFT>::addSegment(Segment<ELFT> *segment) { + bool allocatedNew = false; + ELFLinkingContext::OutputMagic outputMagic = this->_ctx.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); + } +} + +template <class ELFT> +typename ProgramHeader<ELFT>::Elf_Phdr * +ProgramHeader<ELFT>::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; +} + +template <class ELFT> +SectionHeader<ELFT>::SectionHeader(const ELFLinkingContext &ctx, int32_t order) + : Chunk<ELFT>("shdr", Chunk<ELFT>::Kind::SectionHeader, ctx) { + this->_fsize = 0; + this->_alignment = 8; + this->setOrder(order); + // The first element in the list is always NULL + auto *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) { + auto *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); +} + +template class ELFHeader<ELF32LE>; +template class ELFHeader<ELF32BE>; +template class ELFHeader<ELF64LE>; +template class ELFHeader<ELF64BE>; + +template class ProgramHeader<ELF32LE>; +template class ProgramHeader<ELF32BE>; +template class ProgramHeader<ELF64LE>; +template class ProgramHeader<ELF64BE>; + +template class SectionHeader<ELF32LE>; +template class SectionHeader<ELF32BE>; +template class SectionHeader<ELF64LE>; +template class SectionHeader<ELF64BE>; + +} // end namespace elf +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/HeaderChunks.h b/lib/ReaderWriter/ELF/HeaderChunks.h index eab132b9b2f69..51fbe38f1a008 100644 --- a/lib/ReaderWriter/ELF/HeaderChunks.h +++ b/lib/ReaderWriter/ELF/HeaderChunks.h @@ -11,18 +11,18 @@ #define LLD_READER_WRITER_ELF_HEADER_CHUNKS_H #include "SegmentChunks.h" +#include "llvm/ADT/STLExtras.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; @@ -33,9 +33,9 @@ public: 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_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; } @@ -43,57 +43,25 @@ public: 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); } + uint64_t fileSize() const override { return sizeof(Elf_Ehdr); } static bool classof(const Chunk<ELFT> *c) { - return c->Kind() == Chunk<ELFT>::Kind::ELFHeader; + return c->kind() == Chunk<ELFT>::Kind::ELFHeader; } - int getContentType() const { return Chunk<ELFT>::ContentType::Header; } + int getContentType() const override { + 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(); - } + llvm::FileOutputBuffer &buffer) override; + + void finalize() override; 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> @@ -103,160 +71,43 @@ public: 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) { + ProgramHeader(const ELFLinkingContext &ctx) + : Chunk<ELFT>("elfphdr", Chunk<ELFT>::Kind::ProgramHeader, ctx) { 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(); } + uint64_t fileSize() const override { return sizeof(Elf_Phdr) * _ph.size(); } static bool classof(const Chunk<ELFT> *c) { - return c->Kind() == Chunk<ELFT>::Kind::ProgramHeader; + 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(); - } + llvm::FileOutputBuffer &buffer) override; + 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(); } - int64_t numHeaders() { - return _ph.size(); + int getContentType() const override { + return Chunk<ELFT>::ContentType::Header; } - 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; - } + Elf_Phdr *allocateProgramHeader(bool &allocatedNew); 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> @@ -265,13 +116,11 @@ 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; + return c->kind() == Chunk<ELFT>::Kind::SectionHeader; } void setStringSection(StringTable<ELFT> *s) { @@ -279,85 +128,26 @@ public: } void write(ELFWriter *writer, TargetLayout<ELFT> &layout, - llvm::FileOutputBuffer &buffer); + llvm::FileOutputBuffer &buffer) override; - virtual void doPreFlight() {} - - void finalize() {} - - uint64_t fileSize() const { return sizeof(Elf_Shdr) * _sectionInfo.size(); } + uint64_t fileSize() const override { + return sizeof(Elf_Shdr) * _sectionInfo.size(); + } uint64_t entsize() { return sizeof(Elf_Shdr); } - int getContentType() const { return Chunk<ELFT>::ContentType::Header; } + int getContentType() const override { + return Chunk<ELFT>::ContentType::Header; + } uint64_t numHeaders() { return _sectionInfo.size(); } private: StringTable<ELFT> *_stringSection; - std::vector<Elf_Shdr*> _sectionInfo; - llvm::BumpPtrAllocator _sectionAllocate; + 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 diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonDynamicLibraryWriter.h b/lib/ReaderWriter/ELF/Hexagon/HexagonDynamicLibraryWriter.h index e2d3193045b75..84415b273f441 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonDynamicLibraryWriter.h +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonDynamicLibraryWriter.h @@ -10,67 +10,55 @@ #define HEXAGON_DYNAMIC_LIBRARY_WRITER_H #include "DynamicLibraryWriter.h" -#include "HexagonExecutableAtoms.h" #include "HexagonLinkingContext.h" namespace lld { namespace elf { -template <typename ELFT> class HexagonTargetLayout; +class HexagonTargetLayout; -template <class ELFT> -class HexagonDynamicLibraryWriter : public DynamicLibraryWriter<ELFT>, - public HexagonELFWriter<ELFT> { +class HexagonDynamicLibraryWriter : public DynamicLibraryWriter<ELF32LE> { public: - HexagonDynamicLibraryWriter(HexagonLinkingContext &context, - HexagonTargetLayout<ELFT> &layout); + HexagonDynamicLibraryWriter(HexagonLinkingContext &ctx, + HexagonTargetLayout &layout); protected: // Add any runtime files and their atoms to the output - virtual bool createImplicitFiles(std::vector<std::unique_ptr<File>> &); + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; - virtual void finalizeDefaultAtomValues(); + void finalizeDefaultAtomValues() override; - virtual std::error_code setELFHeader() { - DynamicLibraryWriter<ELFT>::setELFHeader(); - HexagonELFWriter<ELFT>::setELFHeader(*this->_elfHeader); + std::error_code setELFHeader() override { + DynamicLibraryWriter::setELFHeader(); + setHexagonELFHeader(*_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; + HexagonLinkingContext &_ctx; + HexagonTargetLayout &_targetLayout; }; -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)) {} +HexagonDynamicLibraryWriter::HexagonDynamicLibraryWriter( + HexagonLinkingContext &ctx, HexagonTargetLayout &layout) + : DynamicLibraryWriter(ctx, layout), _ctx(ctx), _targetLayout(layout) {} -template <class ELFT> -bool HexagonDynamicLibraryWriter<ELFT>::createImplicitFiles( +void HexagonDynamicLibraryWriter::createImplicitFiles( std::vector<std::unique_ptr<File>> &result) { - DynamicLibraryWriter<ELFT>::createImplicitFiles(result); + DynamicLibraryWriter::createImplicitFiles(result); // Add the default atoms as defined for hexagon - addDefaultAtoms(); - result.push_back(std::move(_hexagonRuntimeFile)); - return true; + auto file = + llvm::make_unique<RuntimeFile<ELF32LE>>(_ctx, "Hexagon runtime file"); + file->addAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); + file->addAbsoluteAtom("_DYNAMIC"); + result.push_back(std::move(file)); } -template <class ELFT> -void HexagonDynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues() { +void HexagonDynamicLibraryWriter::finalizeDefaultAtomValues() { // Finalize the atom values that are part of the parent. - DynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues(); - HexagonELFWriter<ELFT>::finalizeHexagonRuntimeAtomValues(); + DynamicLibraryWriter::finalizeDefaultAtomValues(); + if (_ctx.isDynamic()) + finalizeHexagonRuntimeAtomValues(_targetLayout); } } // namespace elf diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonELFFile.h b/lib/ReaderWriter/ELF/Hexagon/HexagonELFFile.h index ab0b9b432b430..3d0d38f387dd1 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonELFFile.h +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonELFFile.h @@ -16,53 +16,46 @@ namespace lld { namespace elf { -template <class ELFT> class HexagonELFFile; +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; +class HexagonELFDefinedAtom : public ELFDefinedAtom<ELF32LE> { + typedef llvm::object::Elf_Sym_Impl<ELF32LE> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELF32LE> 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); + template <typename... T> + HexagonELFDefinedAtom(T &&... args) + : ELFDefinedAtom(std::forward<T>(args)...) {} + + DefinedAtom::ContentType contentType() const override { + if (_contentType != DefinedAtom::typeUnknown) + return _contentType; + if (_section->sh_flags & llvm::ELF::SHF_HEX_GPREL) { + if (_section->sh_type == llvm::ELF::SHT_NOBITS) + return (_contentType = DefinedAtom::typeZeroFillFast); + return (_contentType = DefinedAtom::typeDataFast); } - return ELFDefinedAtom<ELFT>::contentType(); + return ELFDefinedAtom::contentType(); } - virtual DefinedAtom::ContentPermissions permissions() const { - if (this->_section->sh_flags & llvm::ELF::SHF_HEX_GPREL) + DefinedAtom::ContentPermissions permissions() const override { + if (_section->sh_flags & llvm::ELF::SHF_HEX_GPREL) return DefinedAtom::permRW_; - return ELFDefinedAtom<ELFT>::permissions(); + return ELFDefinedAtom::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; +class HexagonELFCommonAtom : public ELFCommonAtom<ELF32LE> { + typedef llvm::object::Elf_Sym_Impl<ELF32LE> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELF32LE> Elf_Shdr; public: - HexagonELFCommonAtom(const HexagonELFFile<ELFT> &file, StringRef symbolName, + HexagonELFCommonAtom(const ELFFile<ELF32LE> &file, StringRef symbolName, const Elf_Sym *symbol) - : ELFCommonAtom<ELFT>(file, symbolName, symbol) {} + : ELFCommonAtom(file, symbolName, symbol) {} virtual bool isSmallCommonSymbol() const { - switch (this->_symbol->st_shndx) { + switch (_symbol->st_shndx) { // Common symbols case llvm::ELF::SHN_HEXAGON_SCOMMON: case llvm::ELF::SHN_HEXAGON_SCOMMON_1: @@ -76,52 +69,46 @@ public: return false; } - virtual uint64_t size() const { + uint64_t size() const override { if (isSmallCommonSymbol()) - return this->_symbol->st_size; - return ELFCommonAtom<ELFT>::size(); + return _symbol->st_size; + return ELFCommonAtom::size(); } - virtual DefinedAtom::Merge merge() const { - if (this->_symbol->getBinding() == llvm::ELF::STB_WEAK) + DefinedAtom::Merge merge() const override { + if (_symbol->getBinding() == llvm::ELF::STB_WEAK) return DefinedAtom::mergeAsWeak; if (isSmallCommonSymbol()) return DefinedAtom::mergeAsTentative; - return ELFCommonAtom<ELFT>::merge(); + return ELFCommonAtom::merge(); } - virtual DefinedAtom::ContentType contentType() const { + DefinedAtom::ContentType contentType() const override { if (isSmallCommonSymbol()) return DefinedAtom::typeZeroFillFast; - return ELFCommonAtom<ELFT>::contentType(); + return ELFCommonAtom::contentType(); } - virtual DefinedAtom::Alignment alignment() const { + DefinedAtom::Alignment alignment() const override { if (isSmallCommonSymbol()) - return DefinedAtom::Alignment(llvm::Log2_64(this->_symbol->st_value)); - return ELFCommonAtom<ELFT>::alignment(); + return DefinedAtom::Alignment(_symbol->st_value); + return 1; } - virtual DefinedAtom::ContentPermissions permissions() const { + DefinedAtom::ContentPermissions permissions() const override { if (isSmallCommonSymbol()) return DefinedAtom::permRW_; - return ELFCommonAtom<ELFT>::permissions(); + return ELFCommonAtom::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; +class HexagonELFFile : public ELFFile<ELF32LE> { + typedef llvm::object::Elf_Sym_Impl<ELF32LE> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELF32LE> 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)); - } + HexagonELFFile(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx) + : ELFFile(std::move(mb), ctx) {} bool isCommonSymbol(const Elf_Sym *symbol) const override { switch (symbol->st_shndx) { @@ -135,35 +122,27 @@ public: default: break; } - return ELFFile<ELFT>::isCommonSymbol(symbol); + return ELFFile::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>( + ELFDefinedAtom<ELF32LE> *createDefinedAtom( + 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<ELF32LE> *> &referenceList) override { + return new (_readerStorage) HexagonELFDefinedAtom( *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); + ELFCommonAtom<ELF32LE> *createCommonAtom(StringRef symName, + const Elf_Sym *sym) override { + return new (_readerStorage) HexagonELFCommonAtom(*this, symName, sym); } }; -template <class ELFT> class HexagonDynamicFile : public DynamicFile<ELFT> { -public: - HexagonDynamicFile(const HexagonLinkingContext &context, StringRef name) - : DynamicFile<ELFT>(context, name) {} -}; - } // elf } // lld diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonELFReader.h b/lib/ReaderWriter/ELF/Hexagon/HexagonELFReader.h deleted file mode 100644 index 1a4f891df7997..0000000000000 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonELFReader.h +++ /dev/null @@ -1,62 +0,0 @@ -//===- 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 deleted file mode 100644 index 96c74f72222dc..0000000000000 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonELFWriters.h +++ /dev/null @@ -1,61 +0,0 @@ -//===- 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 index 3e12786704a25..6af43d88afbb7 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonEncodings.h +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonEncodings.h @@ -6,7 +6,27 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/ErrorHandling.h" + +namespace lld { +namespace elf { + +/// \brief Applying fixup on Hexagon requires the relocator to fetch the fixup +/// mask from the instruction. To fetch the fixup encoding, the linker uses a +/// static array that contains the instruction mask, the compare mask and the +/// relocation mask. +typedef struct { + uint32_t insnMask; // Instruction mask. + uint32_t insnCmpMask; // Compare mask. + uint32_t insnBitMask; // Relocation mask. + bool isDuplex; // Indicates if the instruction is a duplex instruction. +} Instruction; + Instruction insn_encodings[] = { + // InsnMask CompareMask BitMask IsDuplexInstruction { 0xffe00004, 0x40000000, 0x20f8, 0x0 }, { 0xffe03080, 0x9ca03080, 0xf60, 0x0 }, { 0xf9e00000, 0x48c00000, 0x61f20ff, 0x0 }, @@ -599,3 +619,20 @@ Instruction insn_encodings[] = { { 0xff602060, 0x3f000060, 0x1f80, 0x0 }, { 0xf7c02000, 0x11000000, 0x3000fe, 0x0 }, }; + +/// \brief finds the scatter Bits that need to be used to apply relocations +inline uint32_t findv4bitmask(uint8_t *location) { + uint32_t insn = llvm::support::endian::read32le(location); + for (int32_t i = 0, e = llvm::array_lengthof(insn_encodings); i < e; i++) { + if ((insn & 0xc000) == 0 && !insn_encodings[i].isDuplex) + continue; + if ((insn & 0xc000) != 0 && insn_encodings[i].isDuplex) + continue; + if ((insn_encodings[i].insnMask & insn) == insn_encodings[i].insnCmpMask) + return insn_encodings[i].insnBitMask; + } + llvm_unreachable("found unknown Hexagon instruction"); +} + +} // namespace elf +} // namespace lld diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableAtoms.h b/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableAtoms.h deleted file mode 100644 index a2505aa460c5b..0000000000000 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableAtoms.h +++ /dev/null @@ -1,29 +0,0 @@ -//===- 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 index 0848e64166faa..390694954a751 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableWriter.h +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableWriter.h @@ -10,74 +10,61 @@ #define HEXAGON_EXECUTABLE_WRITER_H #include "ExecutableWriter.h" -#include "HexagonELFWriters.h" -#include "HexagonExecutableAtoms.h" #include "HexagonLinkingContext.h" +#include "HexagonTargetHandler.h" namespace lld { namespace elf { -template <typename ELFT> class HexagonTargetLayout; +class HexagonTargetLayout; -template <class ELFT> -class HexagonExecutableWriter : public ExecutableWriter<ELFT>, - public HexagonELFWriter<ELFT> { +class HexagonExecutableWriter : public ExecutableWriter<ELF32LE> { public: - HexagonExecutableWriter(HexagonLinkingContext &context, - HexagonTargetLayout<ELFT> &layout); + HexagonExecutableWriter(HexagonLinkingContext &ctx, + HexagonTargetLayout &layout); protected: // Add any runtime files and their atoms to the output - virtual bool createImplicitFiles(std::vector<std::unique_ptr<File>> &); + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; - virtual void finalizeDefaultAtomValues(); + void finalizeDefaultAtomValues() override; - virtual std::error_code setELFHeader() { - ExecutableWriter<ELFT>::setELFHeader(); - HexagonELFWriter<ELFT>::setELFHeader(*this->_elfHeader); + std::error_code setELFHeader() override { + ExecutableWriter::setELFHeader(); + setHexagonELFHeader(*_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; + HexagonLinkingContext &_ctx; + HexagonTargetLayout &_targetLayout; }; -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)) {} +HexagonExecutableWriter::HexagonExecutableWriter(HexagonLinkingContext &ctx, + HexagonTargetLayout &layout) + : ExecutableWriter(ctx, layout), _ctx(ctx), _targetLayout(layout) {} -template <class ELFT> -bool HexagonExecutableWriter<ELFT>::createImplicitFiles( +void HexagonExecutableWriter::createImplicitFiles( std::vector<std::unique_ptr<File>> &result) { - ExecutableWriter<ELFT>::createImplicitFiles(result); + ExecutableWriter::createImplicitFiles(result); // Add the default atoms as defined for hexagon - addDefaultAtoms(); - result.push_back(std::move(_hexagonRuntimeFile)); - return true; + auto file = + llvm::make_unique<RuntimeFile<ELF32LE>>(_ctx, "Hexagon runtime file"); + file->addAbsoluteAtom("_SDA_BASE_"); + if (_ctx.isDynamic()) { + file->addAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); + file->addAbsoluteAtom("_DYNAMIC"); + } + result.push_back(std::move(file)); } -template <class ELFT> -void HexagonExecutableWriter<ELFT>::finalizeDefaultAtomValues() { +void HexagonExecutableWriter::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(); + ExecutableWriter::finalizeDefaultAtomValues(); + AtomLayout *sdabaseAtom = _targetLayout.findAbsoluteAtom("_SDA_BASE_"); + sdabaseAtom->_virtualAddr = _targetLayout.getSDataSection()->virtualAddr(); + if (_ctx.isDynamic()) + finalizeHexagonRuntimeAtomValues(_targetLayout); } } // namespace elf diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.cpp b/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.cpp index 7eacb2b44c3b2..11eabf7e26fc4 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.cpp +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.cpp @@ -10,16 +10,38 @@ #include "HexagonLinkingContext.h" #include "HexagonTargetHandler.h" -using namespace lld::elf; +namespace lld { +namespace elf { -std::unique_ptr<lld::ELFLinkingContext> -HexagonLinkingContext::create(llvm::Triple triple) { +std::unique_ptr<ELFLinkingContext> +createHexagonLinkingContext(llvm::Triple triple) { if (triple.getArch() == llvm::Triple::hexagon) - return std::unique_ptr<lld::ELFLinkingContext>( - new HexagonLinkingContext(triple)); + return llvm::make_unique<HexagonLinkingContext>(triple); return nullptr; } HexagonLinkingContext::HexagonLinkingContext(llvm::Triple triple) - : ELFLinkingContext(triple, std::unique_ptr<TargetHandlerBase>( + : ELFLinkingContext(triple, std::unique_ptr<TargetHandler>( new HexagonTargetHandler(*this))) {} + +static const Registry::KindStrings kindStrings[] = { +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), +#include "llvm/Support/ELFRelocs/Hexagon.def" +#undef ELF_RELOC + LLD_KIND_STRING_END +}; + +void HexagonLinkingContext::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, + Reference::KindArch::Hexagon, kindStrings); +} + +void setHexagonELFHeader(ELFHeader<ELF32LE> &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); +} + +} // namespace elf +} // namespace lld diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.h b/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.h index c920cdf153aaf..ab91e405fd56e 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.h +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.h @@ -10,6 +10,7 @@ #ifndef LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_LINKING_CONTEXT_H #define LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_LINKING_CONTEXT_H +#include "OutputELFWriter.h" #include "lld/ReaderWriter/ELFLinkingContext.h" #include "llvm/Object/ELF.h" #include "llvm/Support/ELF.h" @@ -17,14 +18,13 @@ 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); + int getMachineType() const override { return llvm::ELF::EM_HEXAGON; } HexagonLinkingContext(llvm::Triple triple); void addPasses(PassManager &) override; + void registerRelocationNames(Registry &r) override; bool isDynamicRelocation(const Reference &r) const override { if (r.kindNamespace() != Reference::KindNamespace::ELF) @@ -41,12 +41,7 @@ public: 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; - } + return r.kindValue() == llvm::ELF::R_HEX_JMP_SLOT; } /// \brief Hexagon has only one relative relocation @@ -54,15 +49,12 @@ public: 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; - } + return r.kindValue() == llvm::ELF::R_HEX_RELATIVE; } }; +void setHexagonELFHeader(ELFHeader<ELF32LE> &elfHeader); + } // elf } // lld diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationFunctions.h b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationFunctions.h deleted file mode 100644 index 2b9e25ce363b5..0000000000000 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationFunctions.h +++ /dev/null @@ -1,49 +0,0 @@ -//===- 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 index 21967d356a311..0a201b32b5f10 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.cpp +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.cpp @@ -7,10 +7,10 @@ // //===----------------------------------------------------------------------===// +#include "HexagonEncodings.h" #include "HexagonLinkingContext.h" -#include "HexagonRelocationFunctions.h" -#include "HexagonTargetHandler.h" #include "HexagonRelocationHandler.h" +#include "HexagonTargetHandler.h" #include "llvm/Support/Endian.h" using namespace lld; @@ -18,263 +18,250 @@ using namespace lld::elf; using namespace llvm::ELF; using namespace llvm::support::endian; -#define APPLY_RELOC(result) \ - write32le(location, result | read32le(location)); +// Scatter val's bits as specified by the mask. Example: +// +// Val: 0bABCDEFG +// Mask: 0b10111100001011 +// Output: 0b00ABCD0000E0FG +static uint32_t scatterBits(uint32_t val, uint32_t mask) { + uint32_t result = 0; + size_t off = 0; + for (size_t bit = 0; bit < 32; ++bit) { + if ((mask >> bit) & 1) { + uint32_t valBit = (val >> off) & 1; + result |= valBit << bit; + ++off; + } + } + return result; +} -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); +static void relocBNPCREL(uint8_t *loc, uint64_t P, uint64_t S, uint64_t A, + int32_t nBits) { + int32_t result = (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; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } - 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; +static void relocLO16(uint8_t *loc, uint64_t P, uint64_t S, uint64_t A) { + uint32_t result = S + A; + result = scatterBits(result, 0x00c03fff); + write32le(loc, result | read32le(loc)); } /// \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; +static void relocHI16(uint8_t *loc, uint64_t P, uint64_t S, uint64_t A) { + uint32_t result = (S + A) >> 16; + result = scatterBits(result, 0x00c03fff); + write32le(loc, result | read32le(loc)); } /// \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 void reloc32(uint8_t *loc, uint64_t P, uint64_t S, uint64_t A) { + uint32_t result = S + A; + write32le(loc, result | read32le(loc)); } -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; +static void reloc32_6_X(uint8_t *loc, 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; + result = scatterBits(result, 0xfff3fff); + write32le(loc, result | read32le(loc)); } // 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; +static void relocHexB32PCRELX(uint8_t *loc, uint64_t P, uint64_t S, + uint64_t A) { + int64_t result = (S + A - P) >> 6; + result = scatterBits(result, 0xfff3fff); + write32le(loc, result | read32le(loc)); } // 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); +static void relocHexBNPCRELX(uint8_t *loc, 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; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } - 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; +static void relocHex6PCRELX(uint8_t *loc, uint64_t P, uint64_t S, uint64_t A) { + int32_t result = S + A - P; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } // 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; +static void relocHex_N_X(uint8_t *loc, uint64_t P, uint64_t S, uint64_t A) { + uint32_t result = S + A; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } -// 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); +// GP REL relocs +static void relocHexGPRELN(uint8_t *loc, uint64_t P, uint64_t S, uint64_t A, + uint64_t GP, int nShiftBits) { + int32_t result = (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; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } - 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; +static void relocHexGOTLO16(uint8_t *loc, uint64_t A, uint64_t GOT) { + int32_t result = A - GOT; + result = scatterBits(result, 0x00c03fff); + write32le(loc, result | read32le(loc)); } /// \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; +static void relocHexGOTHI16(uint8_t *loc, uint64_t A, uint64_t GOT) { + int32_t result = (A - GOT) >> 16; + result = scatterBits(result, 0x00c03fff); + write32le(loc, result | read32le(loc)); } /// \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; +static void relocHexGOT32(uint8_t *loc, uint64_t A, uint64_t GOT) { + int32_t result = GOT - A; + write32le(loc, result | read32le(loc)); } /// \brief Word32_U16 : (G) : Truncate -static int relocHexGOT16(uint8_t *location, uint64_t A, uint64_t GOT) { - int32_t result = (int32_t)(GOT-A); +static void relocHexGOT16(uint8_t *loc, uint64_t A, uint64_t GOT) { + int32_t result = GOT - A; int32_t range = 1L << 16; if (result <= range) { - result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); - APPLY_RELOC(result); - return 0; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } - 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 void relocHexGOT32_6_X(uint8_t *loc, uint64_t A, uint64_t GOT) { + int32_t result = (A - GOT) >> 6; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } -static int relocHexGOT16_X(uint8_t *location, uint64_t A, uint64_t GOT) { - int32_t result = (int32_t)(A-GOT); +static void relocHexGOT16_X(uint8_t *loc, uint64_t A, uint64_t GOT) { + int32_t result = A - GOT; int32_t range = 1L << 6; if (result <= range) { - result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); - APPLY_RELOC(result); - return 0; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } - 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 void relocHexGOT11_X(uint8_t *loc, uint64_t A, uint64_t GOT) { + uint32_t result = A - GOT; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } -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 void relocHexGOTRELSigned(uint8_t *loc, uint64_t P, uint64_t S, + uint64_t A, uint64_t GOT, int shiftBits) { + int32_t result = (S + A - GOT) >> shiftBits; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } -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 void relocHexGOTRELUnsigned(uint8_t *loc, uint64_t P, uint64_t S, + uint64_t A, uint64_t GOT) { + uint32_t result = S + A - GOT; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } -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 void relocHexGOTREL_HILO16(uint8_t *loc, uint64_t P, uint64_t S, + uint64_t A, uint64_t GOT, int shiftBits) { + int32_t result = (S + A - GOT) >> shiftBits; + result = scatterBits(result, 0x00c03fff); + write32le(loc, result | read32le(loc)); } -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; +static void relocHexGOTREL_32(uint8_t *loc, uint64_t P, uint64_t S, uint64_t A, + uint64_t GOT) { + int32_t result = S + A - GOT; + write32le(loc, result | read32le(loc)); } std::error_code HexagonTargetRelocationHandler::applyRelocation( - ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom, + ELFWriter &writer, llvm::FileOutputBuffer &buf, const 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(); + uint8_t *loc = atomContent + ref.offsetInAtom(); + uint64_t target = writer.addressOfAtom(ref.target()); + uint64_t reloc = atom._virtualAddr + ref.offsetInAtom(); if (ref.kindNamespace() != Reference::KindNamespace::ELF) return std::error_code(); assert(ref.kindArch() == Reference::KindArch::Hexagon); switch (ref.kindValue()) { case R_HEX_B22_PCREL: - relocBNPCREL(location, relocVAddress, targetVAddress, ref.addend(), 21); + relocBNPCREL(loc, reloc, target, ref.addend(), 21); break; case R_HEX_B15_PCREL: - relocBNPCREL(location, relocVAddress, targetVAddress, ref.addend(), 14); + relocBNPCREL(loc, reloc, target, ref.addend(), 14); break; case R_HEX_B9_PCREL: - relocBNPCREL(location, relocVAddress, targetVAddress, ref.addend(), 8); + relocBNPCREL(loc, reloc, target, ref.addend(), 8); break; case R_HEX_LO16: - relocLO16(location, relocVAddress, targetVAddress, ref.addend()); + relocLO16(loc, reloc, target, ref.addend()); break; case R_HEX_HI16: - relocHI16(location, relocVAddress, targetVAddress, ref.addend()); + relocHI16(loc, reloc, target, ref.addend()); break; case R_HEX_32: - reloc32(location, relocVAddress, targetVAddress, ref.addend()); + reloc32(loc, reloc, target, ref.addend()); break; case R_HEX_32_6_X: - reloc32_6_X(location, relocVAddress, targetVAddress, ref.addend()); + reloc32_6_X(loc, reloc, target, ref.addend()); break; case R_HEX_B32_PCREL_X: - relocHexB32PCRELX(location, relocVAddress, targetVAddress, ref.addend()); + relocHexB32PCRELX(loc, reloc, target, ref.addend()); break; case R_HEX_B22_PCREL_X: - relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 21); + relocHexBNPCRELX(loc, reloc, target, ref.addend(), 21); break; case R_HEX_B15_PCREL_X: - relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 14); + relocHexBNPCRELX(loc, reloc, target, ref.addend(), 14); break; case R_HEX_B13_PCREL_X: - relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 12); + relocHexBNPCRELX(loc, reloc, target, ref.addend(), 12); break; case R_HEX_B9_PCREL_X: - relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 8); + relocHexBNPCRELX(loc, reloc, target, ref.addend(), 8); break; case R_HEX_B7_PCREL_X: - relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 6); + relocHexBNPCRELX(loc, reloc, target, ref.addend(), 6); break; case R_HEX_GPREL16_0: - relocHexGPRELN(location, relocVAddress, targetVAddress, ref.addend(), - _hexagonTargetLayout.getSDataSection()->virtualAddr(), 0); + relocHexGPRELN(loc, reloc, target, ref.addend(), + _targetLayout.getSDataSection()->virtualAddr(), 0); break; case R_HEX_GPREL16_1: - relocHexGPRELN(location, relocVAddress, targetVAddress, ref.addend(), - _hexagonTargetLayout.getSDataSection()->virtualAddr(), 1); + relocHexGPRELN(loc, reloc, target, ref.addend(), + _targetLayout.getSDataSection()->virtualAddr(), 1); break; case R_HEX_GPREL16_2: - relocHexGPRELN(location, relocVAddress, targetVAddress, ref.addend(), - _hexagonTargetLayout.getSDataSection()->virtualAddr(), 2); + relocHexGPRELN(loc, reloc, target, ref.addend(), + _targetLayout.getSDataSection()->virtualAddr(), 2); break; case R_HEX_GPREL16_3: - relocHexGPRELN(location, relocVAddress, targetVAddress, ref.addend(), - _hexagonTargetLayout.getSDataSection()->virtualAddr(), 3); + relocHexGPRELN(loc, reloc, target, ref.addend(), + _targetLayout.getSDataSection()->virtualAddr(), 3); break; case R_HEX_16_X: case R_HEX_12_X: @@ -284,62 +271,55 @@ std::error_code HexagonTargetRelocationHandler::applyRelocation( case R_HEX_8_X: case R_HEX_7_X: case R_HEX_6_X: - relocHex_N_X(location, relocVAddress, targetVAddress, ref.addend()); + relocHex_N_X(loc, reloc, target, ref.addend()); break; case R_HEX_6_PCREL_X: - relocHex6PCRELX(location, relocVAddress, targetVAddress, ref.addend()); + relocHex6PCRELX(loc, reloc, target, 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()); + relocHexGOTREL_32(loc, reloc, target, ref.addend(), + _targetLayout.getGOTSymAddr()); break; case R_HEX_GOTREL_LO16: - relocHexGOTREL_HILO16(location, relocVAddress, targetVAddress, ref.addend(), - _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOTREL_HILO16(loc, reloc, target, ref.addend(), + _targetLayout.getGOTSymAddr(), 0); break; case R_HEX_GOTREL_HI16: - relocHexGOTREL_HILO16(location, relocVAddress, targetVAddress, ref.addend(), - _hexagonTargetLayout.getGOTSymAddr(), 16); + relocHexGOTREL_HILO16(loc, reloc, target, ref.addend(), + _targetLayout.getGOTSymAddr(), 16); break; case R_HEX_GOT_LO16: - relocHexGOTLO16(location, targetVAddress, - _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOTLO16(loc, target, _targetLayout.getGOTSymAddr()); break; case R_HEX_GOT_HI16: - relocHexGOTHI16(location, targetVAddress, - _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOTHI16(loc, target, _targetLayout.getGOTSymAddr()); break; case R_HEX_GOT_32: - relocHexGOT32(location, targetVAddress, - _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOT32(loc, target, _targetLayout.getGOTSymAddr()); break; case R_HEX_GOT_16: - relocHexGOT16(location, targetVAddress, - _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOT16(loc, target, _targetLayout.getGOTSymAddr()); break; case R_HEX_GOT_32_6_X: - relocHexGOT32_6_X(location, targetVAddress, - _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOT32_6_X(loc, target, _targetLayout.getGOTSymAddr()); break; case R_HEX_GOT_16_X: - relocHexGOT16_X(location, targetVAddress, - _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOT16_X(loc, target, _targetLayout.getGOTSymAddr()); break; case R_HEX_GOT_11_X: - relocHexGOT11_X(location, targetVAddress, - _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOT11_X(loc, target, _targetLayout.getGOTSymAddr()); break; case R_HEX_GOTREL_32_6_X: - relocHexGOTRELSigned(location, relocVAddress, targetVAddress, ref.addend(), - _hexagonTargetLayout.getGOTSymAddr(), 6); + relocHexGOTRELSigned(loc, reloc, target, ref.addend(), + _targetLayout.getGOTSymAddr(), 6); break; case R_HEX_GOTREL_16_X: case R_HEX_GOTREL_11_X: - relocHexGOTRELUnsigned(location, relocVAddress, targetVAddress, - ref.addend(), _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOTRELUnsigned(loc, reloc, target, ref.addend(), + _targetLayout.getGOTSymAddr()); break; default: diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.h b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.h index 4795d0264b9cd..6afba0ddb8f3a 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.h +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.h @@ -9,26 +9,24 @@ #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" +#include "lld/ReaderWriter/ELFLinkingContext.h" namespace lld { namespace elf { - class HexagonTargetHandler; +class HexagonTargetLayout; class HexagonTargetRelocationHandler final : public TargetRelocationHandler { public: - HexagonTargetRelocationHandler(HexagonTargetLayout<HexagonELFType> &layout) - : _hexagonTargetLayout(layout) {} + HexagonTargetRelocationHandler(HexagonTargetLayout &layout) + : _targetLayout(layout) {} std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, - const lld::AtomLayout &, + const AtomLayout &, const Reference &) const override; private: - HexagonTargetLayout<HexagonELFType> &_hexagonTargetLayout; + HexagonTargetLayout &_targetLayout; }; } // elf } // lld diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonSectionChunks.h b/lib/ReaderWriter/ELF/Hexagon/HexagonSectionChunks.h deleted file mode 100644 index 5b3fbbbd899be..0000000000000 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonSectionChunks.h +++ /dev/null @@ -1,86 +0,0 @@ -//===- 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 index 9b10c2f160f41..6c0360c310f45 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp @@ -12,29 +12,23 @@ #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())) {} +namespace lld { +namespace elf { + +HexagonTargetHandler::HexagonTargetHandler(HexagonLinkingContext &ctx) + : _ctx(ctx), _targetLayout(new HexagonTargetLayout(ctx)), + _relocationHandler(new HexagonTargetRelocationHandler(*_targetLayout)) {} std::unique_ptr<Writer> HexagonTargetHandler::getWriter() { - switch (_hexagonLinkingContext.getOutputELFType()) { + switch (_ctx.getOutputELFType()) { case llvm::ELF::ET_EXEC: - return std::unique_ptr<Writer>( - new elf::HexagonExecutableWriter<HexagonELFType>( - _hexagonLinkingContext, *_hexagonTargetLayout.get())); + return llvm::make_unique<HexagonExecutableWriter>(_ctx, *_targetLayout); case llvm::ELF::ET_DYN: - return std::unique_ptr<Writer>( - new elf::HexagonDynamicLibraryWriter<HexagonELFType>( - _hexagonLinkingContext, *_hexagonTargetLayout.get())); + return llvm::make_unique<HexagonDynamicLibraryWriter>(_ctx, *_targetLayout); case llvm::ELF::ET_REL: llvm_unreachable("TODO: support -r mode"); default: @@ -77,7 +71,7 @@ public: return makeArrayRef(hexagonGotAtomContent); } - Alignment alignment() const override { return Alignment(2); } + Alignment alignment() const override { return 4; } }; class HexagonGOTPLTAtom : public GOTAtom { @@ -88,7 +82,7 @@ public: return makeArrayRef(hexagonGotPltAtomContent); } - Alignment alignment() const override { return Alignment(2); } + Alignment alignment() const override { return 4; } }; class HexagonGOTPLT0Atom : public GOTAtom { @@ -99,7 +93,7 @@ public: return makeArrayRef(hexagonGotPlt0AtomContent); } - Alignment alignment() const override { return Alignment(3); } + Alignment alignment() const override { return 8; } }; class HexagonPLT0Atom : public PLT0Atom { @@ -165,8 +159,7 @@ protected: } public: - GOTPLTPass(const ELFLinkingContext &ctx) - : _file(ctx), _null(nullptr), _PLT0(nullptr), _got0(nullptr) {} + GOTPLTPass(const ELFLinkingContext &ctx) : _file(ctx) {} /// \brief Do the pass. /// @@ -176,34 +169,36 @@ public: /// /// After all references are handled, the atoms created during that are all /// added to mf. - void perform(std::unique_ptr<MutableFile> &mf) override { + std::error_code perform(SimpleFile &mf) override { // Process all references. - for (const auto &atom : mf->defined()) + 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); + if (_plt0) { + _plt0->setOrdinal(ordinal++); + mf.addAtom(*_plt0); } for (auto &plt : _pltVector) { plt->setOrdinal(ordinal++); - mf->addAtom(*plt); + mf.addAtom(*plt); } if (_null) { _null->setOrdinal(ordinal++); - mf->addAtom(*_null); + mf.addAtom(*_null); } if (_got0) { _got0->setOrdinal(ordinal++); - mf->addAtom(*_got0); + mf.addAtom(*_got0); } for (auto &got : _gotVector) { got->setOrdinal(ordinal++); - mf->addAtom(*got); + mf.addAtom(*got); } + + return std::error_code(); } protected: @@ -221,19 +216,19 @@ protected: std::vector<PLTAtom *> _pltVector; /// \brief GOT entry that is always 0. Used for undefined weaks. - GOTAtom *_null; + GOTAtom *_null = nullptr; /// \brief The got and plt entries for .PLT0. This is used to call into the /// dynamic linker for symbol resolution. /// @{ - PLT0Atom *_PLT0; - GOTAtom *_got0; + PLT0Atom *_plt0 = nullptr; + GOTAtom *_got0 = nullptr; /// @} }; class DynamicGOTPLTPass final : public GOTPLTPass<DynamicGOTPLTPass> { public: - DynamicGOTPLTPass(const elf::HexagonLinkingContext &ctx) : GOTPLTPass(ctx) { + DynamicGOTPLTPass(const HexagonLinkingContext &ctx) : GOTPLTPass(ctx) { _got0 = new (_file._alloc) HexagonGOTPLT0Atom(_file); #ifndef NDEBUG _got0->_name = "__got0"; @@ -241,14 +236,14 @@ public: } 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); + 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; + return _plt0; } const PLTAtom *getPLTEntry(const Atom *a) { @@ -313,22 +308,75 @@ public: } }; -void elf::HexagonLinkingContext::addPasses(PassManager &pm) { +void 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); +void SDataSection::doPreFlight() { + // sort the atoms on the alignments they have been set + std::stable_sort(_atoms.begin(), _atoms.end(), [](const AtomLayout *A, + const AtomLayout *B) { + const DefinedAtom *definedAtomA = cast<DefinedAtom>(A->_atom); + const DefinedAtom *definedAtomB = cast<DefinedAtom>(B->_atom); + int64_t alignmentA = definedAtomA->alignment().value; + int64_t alignmentB = definedAtomB->alignment().value; + 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 : _atoms) { + const DefinedAtom *definedAtom = cast<DefinedAtom>(ai->_atom); + DefinedAtom::Alignment atomAlign = definedAtom->alignment(); + uint64_t fOffset = alignOffset(fileSize(), atomAlign); + uint64_t mOffset = alignOffset(memSize(), atomAlign); + ai->_fileOffset = fOffset; + _fsize = fOffset + definedAtom->size(); + _msize = mOffset + definedAtom->size(); + } +} // finalize + +SDataSection::SDataSection(const HexagonLinkingContext &ctx) + : AtomSection(ctx, ".sdata", DefinedAtom::typeDataFast, 0, + HexagonTargetLayout::ORDER_SDATA) { + _type = SHT_PROGBITS; + _flags = SHF_ALLOC | SHF_WRITE; + _alignment = 4096; } -#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), +const AtomLayout *SDataSection::appendAtom(const Atom *atom) { + const DefinedAtom *definedAtom = cast<DefinedAtom>(atom); + DefinedAtom::Alignment atomAlign = definedAtom->alignment(); + uint64_t alignment = atomAlign.value; + _atoms.push_back(new (_alloc) AtomLayout(atom, 0, 0)); + // Set the section alignment to the largest alignment + // std::max doesn't support uint64_t + if (_alignment < alignment) + _alignment = alignment; + return _atoms.back(); +} -const Registry::KindStrings HexagonTargetHandler::kindStrings[] = { -#include "llvm/Support/ELFRelocs/Hexagon.def" - LLD_KIND_STRING_END -}; +void finalizeHexagonRuntimeAtomValues(HexagonTargetLayout &layout) { + AtomLayout *gotAtom = layout.findAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); + OutputSection<ELF32LE> *gotpltSection = layout.findOutputSection(".got.plt"); + if (gotpltSection) + gotAtom->_virtualAddr = gotpltSection->virtualAddr(); + else + gotAtom->_virtualAddr = 0; + AtomLayout *dynamicAtom = layout.findAbsoluteAtom("_DYNAMIC"); + OutputSection<ELF32LE> *dynamicSection = layout.findOutputSection(".dynamic"); + if (dynamicSection) + dynamicAtom->_virtualAddr = dynamicSection->virtualAddr(); + else + dynamicAtom->_virtualAddr = 0; +} -#undef ELF_RELOC +} // namespace elf +} // namespace lld diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h b/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h index f4315f710ec7c..b1366bed09eac 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h @@ -10,44 +10,51 @@ #ifndef HEXAGON_TARGET_HANDLER_H #define HEXAGON_TARGET_HANDLER_H -#include "DefaultTargetHandler.h" -#include "HexagonELFReader.h" -#include "HexagonExecutableAtoms.h" +#include "ELFReader.h" +#include "HexagonELFFile.h" #include "HexagonRelocationHandler.h" -#include "HexagonSectionChunks.h" #include "TargetLayout.h" namespace lld { namespace elf { class HexagonLinkingContext; +/// \brief Handle Hexagon SData section +class SDataSection : public AtomSection<ELF32LE> { +public: + SDataSection(const HexagonLinkingContext &ctx); + + /// \brief Finalize the section contents before writing + void doPreFlight() override; + + /// \brief Does this section have an output segment. + bool hasOutputSegment() const override { return true; } + + const AtomLayout *appendAtom(const Atom *atom) override; +}; + /// \brief TargetLayout for Hexagon -template <class HexagonELFType> -class HexagonTargetLayout final : public TargetLayout<HexagonELFType> { +class HexagonTargetLayout final : public TargetLayout<ELF32LE> { public: enum HexagonSectionOrder { ORDER_SDATA = 205 }; - HexagonTargetLayout(HexagonLinkingContext &hti) - : TargetLayout<HexagonELFType>(hti), _sdataSection(nullptr), - _gotSymAtom(nullptr), _cachedGotSymAtom(false) { - _sdataSection = new (_alloc) SDataSection<HexagonELFType>(hti); - } + HexagonTargetLayout(HexagonLinkingContext &ctx) + : TargetLayout(ctx), _sdataSection(ctx) {} /// \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)) + TargetLayout::SectionOrder + getSectionOrder(StringRef name, int32_t contentType, + int32_t contentPermissions) override { + if (contentType == DefinedAtom::typeDataFast || + contentType == DefinedAtom::typeZeroFillFast) return ORDER_SDATA; - - return DefaultLayout<HexagonELFType>::getSectionOrder(name, contentType, - contentPermissions); + return TargetLayout::getSectionOrder(name, contentType, contentPermissions); } /// \brief Return the appropriate input section name. - virtual StringRef getInputSectionName(const DefinedAtom *da) const { + StringRef getInputSectionName(const DefinedAtom *da) const override { switch (da->contentType()) { case DefinedAtom::typeDataFast: case DefinedAtom::typeZeroFillFast: @@ -55,88 +62,72 @@ public: default: break; } - return DefaultLayout<HexagonELFType>::getInputSectionName(da); + return TargetLayout::getInputSectionName(da); } /// \brief Gets or creates a section. - virtual AtomSection<HexagonELFType> * + AtomSection<ELF32LE> * 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); + TargetLayout::SectionOrder sectionOrder) override { + if (contentType == DefinedAtom::typeDataFast || + contentType == DefinedAtom::typeZeroFillFast) + return &_sdataSection; + return TargetLayout::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 { + TargetLayout::SegmentType + getSegmentType(const Section<ELF32LE> *section) const override { if (section->order() == ORDER_SDATA) return PT_LOAD; - - return DefaultLayout<HexagonELFType>::getSegmentType(section); + return TargetLayout::getSegmentType(section); } - Section<HexagonELFType> *getSDataSection() const { - return _sdataSection; - } + Section<ELF32LE> *getSDataSection() { 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; + std::call_once(_gotOnce, [this]() { + if (AtomLayout *got = findAbsoluteAtom("_GLOBAL_OFFSET_TABLE_")) + _gotAddr = got->_virtualAddr; + }); + return _gotAddr; } private: - llvm::BumpPtrAllocator _alloc; - SDataSection<HexagonELFType> *_sdataSection; - AtomLayout *_gotSymAtom; - bool _cachedGotSymAtom; + SDataSection _sdataSection; + uint64_t _gotAddr = 0; + std::once_flag _gotOnce; }; /// \brief TargetHandler for Hexagon -class HexagonTargetHandler final : - public DefaultTargetHandler<HexagonELFType> { +class HexagonTargetHandler final : public TargetHandler { public: HexagonTargetHandler(HexagonLinkingContext &targetInfo); - void registerRelocationNames(Registry ®istry) override; - - const HexagonTargetRelocationHandler &getRelocationHandler() const override { - return *(_hexagonRelocationHandler.get()); - } - - HexagonTargetLayout<HexagonELFType> &getTargetLayout() override { - return *(_hexagonTargetLayout.get()); + const TargetRelocationHandler &getRelocationHandler() const override { + return *_relocationHandler; } std::unique_ptr<Reader> getObjReader() override { - return std::unique_ptr<Reader>( - new HexagonELFObjectReader(_hexagonLinkingContext)); + return llvm::make_unique<ELFReader<HexagonELFFile>>(_ctx); } std::unique_ptr<Reader> getDSOReader() override { - return std::unique_ptr<Reader>( - new HexagonELFDSOReader(_hexagonLinkingContext)); + return llvm::make_unique<ELFReader<DynamicFile<ELF32LE>>>(_ctx); } 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; + HexagonLinkingContext &_ctx; + std::unique_ptr<HexagonTargetLayout> _targetLayout; + std::unique_ptr<HexagonTargetRelocationHandler> _relocationHandler; }; + +void finalizeHexagonRuntimeAtomValues(HexagonTargetLayout &layout); + } // end namespace elf } // end namespace lld diff --git a/lib/ReaderWriter/ELF/Hexagon/Makefile b/lib/ReaderWriter/ELF/Hexagon/Makefile deleted file mode 100644 index 8d6f1a0a3b1ed..0000000000000 --- a/lib/ReaderWriter/ELF/Hexagon/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -##===- 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 deleted file mode 100644 index 826cf5035d59a..0000000000000 --- a/lib/ReaderWriter/ELF/Layout.h +++ /dev/null @@ -1,59 +0,0 @@ -//===- 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 deleted file mode 100644 index 5791ecb9733d1..0000000000000 --- a/lib/ReaderWriter/ELF/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -##===- 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 index d982508b7ddcc..434e310640bd0 100644 --- a/lib/ReaderWriter/ELF/Mips/CMakeLists.txt +++ b/lib/ReaderWriter/ELF/Mips/CMakeLists.txt @@ -1,10 +1,14 @@ add_llvm_library(lldMipsELFTarget + MipsAbiInfoHandler.cpp MipsCtorsOrderPass.cpp - MipsELFFlagsMerger.cpp + MipsELFFile.cpp + MipsELFWriters.cpp MipsLinkingContext.cpp MipsRelocationHandler.cpp MipsRelocationPass.cpp + MipsSectionChunks.cpp MipsTargetHandler.cpp + MipsTargetLayout.cpp LINK_LIBS lldELF lldReaderWriter diff --git a/lib/ReaderWriter/ELF/Mips/Makefile b/lib/ReaderWriter/ELF/Mips/Makefile deleted file mode 100644 index 0b2f4ff82279a..0000000000000 --- a/lib/ReaderWriter/ELF/Mips/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- 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/MipsAbiInfoHandler.cpp b/lib/ReaderWriter/ELF/Mips/MipsAbiInfoHandler.cpp new file mode 100644 index 0000000000000..ad4e62e646803 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsAbiInfoHandler.cpp @@ -0,0 +1,675 @@ +//===- lib/ReaderWriter/ELF/MipsAbiInfoHandler.cpp ------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsAbiInfoHandler.h" +#include "lld/Core/Error.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/MipsABIFlags.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lld; +using namespace lld::elf; +using namespace llvm; +using namespace llvm::ELF; +using namespace llvm::Mips; + +namespace { + +// The joined set of MIPS ISAs and MIPS ISA extensions. +enum MipsISAs { + ArchNone, + + // General ISAs + Arch1, + Arch2, + Arch3, + Arch4, + Arch5, + Arch32, + Arch32r2, + Arch32r3, + Arch32r5, + Arch32r6, + Arch64, + Arch64r2, + Arch64r3, + Arch64r5, + Arch64r6, + + // CPU specific ISAs + Arch3900, + Arch4010, + Arch4100, + Arch4111, + Arch4120, + Arch4650, + Arch5400, + Arch5500, + Arch5900, + Arch9000, + Arch10000, + ArchLs2e, + ArchLs2f, + ArchLs3a, + ArchOcteon, + ArchOcteonP, + ArchOcteon2, + ArchOcteon3, + ArchSB1, + ArchXLR +}; + +struct MipsISATreeEdge { + MipsISAs child; + MipsISAs parent; +}; + +struct ElfArchPair { + uint32_t _elfFlag; + MipsISAs _arch; +}; + +struct AbiIsaArchPair { + uint8_t _isaLevel; + uint8_t _isaRev; + uint8_t _isaExt; + MipsISAs _arch; +}; +} + +static const MipsISATreeEdge isaTree[] = { + // MIPS32R6 and MIPS64R6 are not compatible with other extensions + + // MIPS64R2 extensions. + {ArchOcteon3, ArchOcteon2}, + {ArchOcteon2, ArchOcteonP}, + {ArchOcteonP, ArchOcteon}, + {ArchOcteon, Arch64r2}, + {ArchLs3a, Arch64r2}, + + // MIPS64 extensions. + {Arch64r2, Arch64}, + {ArchSB1, Arch64}, + {ArchXLR, Arch64}, + + // MIPS V extensions. + {Arch64, Arch5}, + + // R5000 extensions. + {Arch5500, Arch5400}, + + // MIPS IV extensions. + {Arch5, Arch4}, + {Arch5400, Arch4}, + {Arch9000, Arch4}, + + // VR4100 extensions. + {Arch4120, Arch4100}, + {Arch4111, Arch4100}, + + // MIPS III extensions. + {ArchLs2e, Arch3}, + {ArchLs2f, Arch3}, + {Arch4650, Arch3}, + {Arch4100, Arch3}, + {Arch4010, Arch3}, + {Arch5900, Arch3}, + {Arch4, Arch3}, + + // MIPS32 extensions. + {Arch32r2, Arch32}, + + // MIPS II extensions. + {Arch3, Arch2}, + {Arch32, Arch2}, + + // MIPS I extensions. + {Arch3900, Arch1}, + {Arch2, Arch1}, +}; + +// Conversion ELF arch flags => MipsISAs +static const ElfArchPair elfArchPairs[] = { + {EF_MIPS_ARCH_1 | EF_MIPS_MACH_3900, Arch3900}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4010, Arch4010}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100, Arch4100}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4111, Arch4111}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4120, Arch4120}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4650, Arch4650}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400, Arch5400}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5500, Arch5500}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_5900, Arch5900}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_9000, Arch9000}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2E, ArchLs2e}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2F, ArchLs2f}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_LS3A, ArchLs3a}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON, ArchOcteon}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON2, ArchOcteon2}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON3, ArchOcteon3}, + {EF_MIPS_ARCH_64 | EF_MIPS_MACH_SB1, ArchSB1}, + {EF_MIPS_ARCH_64 | EF_MIPS_MACH_XLR, ArchXLR}, + {EF_MIPS_ARCH_1, Arch1}, + {EF_MIPS_ARCH_2, Arch2}, + {EF_MIPS_ARCH_3, Arch3}, + {EF_MIPS_ARCH_4, Arch4}, + {EF_MIPS_ARCH_5, Arch5}, + {EF_MIPS_ARCH_32, Arch32}, + {EF_MIPS_ARCH_32R2, Arch32r2}, + {EF_MIPS_ARCH_32R6, Arch32r6}, + {EF_MIPS_ARCH_64, Arch64}, + {EF_MIPS_ARCH_64R2, Arch64r2}, + {EF_MIPS_ARCH_64R6, Arch64r6} +}; + +// Conversion MipsISAs => ELF arch flags +static const ElfArchPair archElfPairs[] = { + {EF_MIPS_ARCH_1, Arch1}, + {EF_MIPS_ARCH_2, Arch2}, + {EF_MIPS_ARCH_3, Arch3}, + {EF_MIPS_ARCH_4, Arch4}, + {EF_MIPS_ARCH_5, Arch5}, + {EF_MIPS_ARCH_32, Arch32}, + {EF_MIPS_ARCH_32R2, Arch32r2}, + {EF_MIPS_ARCH_32R2, Arch32r3}, + {EF_MIPS_ARCH_32R2, Arch32r5}, + {EF_MIPS_ARCH_32R6, Arch32r6}, + {EF_MIPS_ARCH_64, Arch64}, + {EF_MIPS_ARCH_64R2, Arch64r2}, + {EF_MIPS_ARCH_64R2, Arch64r3}, + {EF_MIPS_ARCH_64R2, Arch64r5}, + {EF_MIPS_ARCH_64R6, Arch64r6}, + {EF_MIPS_ARCH_1 | EF_MIPS_MACH_3900, Arch3900}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4010, Arch4010}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100, Arch4100}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4111, Arch4111}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4120, Arch4120}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4650, Arch4650}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400, Arch5400}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5500, Arch5500}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_5900, Arch5900}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_9000, Arch9000}, + {EF_MIPS_ARCH_4, Arch10000}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2E, ArchLs2e}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2F, ArchLs2f}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_LS3A, ArchLs3a}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON, ArchOcteon}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON, ArchOcteonP}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON2, ArchOcteon2}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON3, ArchOcteon3}, + {EF_MIPS_ARCH_64 | EF_MIPS_MACH_SB1, ArchSB1}, + {EF_MIPS_ARCH_64 | EF_MIPS_MACH_SB1, ArchXLR} +}; + +// Conversion .MIPS.abiflags isa/level/extension <=> MipsISAs +static const AbiIsaArchPair abiIsaArchPair[] = { + { 0, 0, 0, ArchNone}, + { 1, 0, 0, Arch1}, + { 2, 0, 0, Arch2}, + { 3, 0, 0, Arch3}, + { 4, 0, 0, Arch4}, + { 5, 0, 0, Arch5}, + {32, 1, 0, Arch32}, + {32, 2, 0, Arch32r2}, + {32, 3, 0, Arch32r3}, + {32, 5, 0, Arch32r5}, + {32, 6, 0, Arch32r6}, + {64, 1, 0, Arch64}, + {64, 2, 0, Arch64r2}, + {64, 3, 0, Arch64r3}, + {64, 5, 0, Arch64r5}, + {64, 6, 0, Arch64r6}, + { 1, 0, AFL_EXT_3900, Arch3900}, + { 3, 0, AFL_EXT_4010, Arch4010}, + { 3, 0, AFL_EXT_4100, Arch4100}, + { 3, 0, AFL_EXT_4111, Arch4111}, + { 3, 0, AFL_EXT_4120, Arch4120}, + { 3, 0, AFL_EXT_4650, Arch4650}, + { 4, 0, AFL_EXT_5400, Arch5400}, + { 4, 0, AFL_EXT_5500, Arch5500}, + { 3, 0, AFL_EXT_5900, Arch5900}, + { 4, 0, AFL_EXT_10000, Arch10000}, + { 3, 0, AFL_EXT_LOONGSON_2E, ArchLs2e}, + { 3, 0, AFL_EXT_LOONGSON_2F, ArchLs2f}, + {64, 2, AFL_EXT_LOONGSON_3A, ArchLs3a}, + {64, 2, AFL_EXT_OCTEON, ArchOcteon}, + {64, 2, AFL_EXT_OCTEON2, ArchOcteon2}, + {64, 2, AFL_EXT_OCTEON3, ArchOcteon3}, + {64, 1, AFL_EXT_SB1, ArchSB1}, + {64, 1, AFL_EXT_XLR, ArchXLR} +}; + +static bool matchMipsISA(MipsISAs base, MipsISAs ext) { + if (base == ext) + return true; + if (base == Arch32 && matchMipsISA(Arch64, ext)) + return true; + if (base == Arch32r2 && matchMipsISA(Arch64r2, ext)) + return true; + for (const auto &edge : isaTree) { + if (ext == edge.child) { + ext = edge.parent; + if (ext == base) + return true; + } + } + return false; +} + +static bool is32BitElfFlags(unsigned flags) { + if (flags & EF_MIPS_32BITMODE) + return true; + + unsigned arch = flags & EF_MIPS_ARCH; + if (arch == EF_MIPS_ARCH_1 || arch == EF_MIPS_ARCH_2 || + arch == EF_MIPS_ARCH_32 || arch == EF_MIPS_ARCH_32R2 || + arch == EF_MIPS_ARCH_32R6) + return true; + + unsigned abi = flags & EF_MIPS_ABI; + if (abi == EF_MIPS_ABI_O32 || abi == EF_MIPS_ABI_EABI32) + return true; + + return false; +} + +static ErrorOr<MipsISAs> headerFlagsToIsa(uint32_t flags) { + uint32_t arch = flags & (EF_MIPS_ARCH | EF_MIPS_MACH); + for (const auto &p : elfArchPairs) + if (p._elfFlag == arch) + return p._arch; + return make_dynamic_error_code( + StringRef("Unknown EF_MIPS_ARCH | EF_MIPS_MACH flags (0x") + + Twine::utohexstr(arch) + ")"); +} + +static uint32_t isaToHeaderFlags(unsigned isa) { + for (const auto &p : archElfPairs) + if (p._arch == isa) + return p._elfFlag; + llvm_unreachable("Unknown MIPS ISA"); +} + +static ErrorOr<uint32_t> flagsToAses(uint32_t flags) { + uint32_t ases = flags & EF_MIPS_ARCH_ASE; + switch (ases) { + case 0: + return 0; + case EF_MIPS_MICROMIPS: + return AFL_ASE_MICROMIPS; + case EF_MIPS_ARCH_ASE_M16: + return AFL_ASE_MIPS16; + case EF_MIPS_ARCH_ASE_MDMX: + return AFL_ASE_MDMX; + default: + return make_dynamic_error_code( + StringRef("Unknown EF_MIPS_ARCH_ASE flag (0x") + + Twine::utohexstr(ases) + ")"); + } +} + +static uint32_t asesToFlags(uint32_t ases) { + switch (ases) { + case AFL_ASE_MICROMIPS: + return EF_MIPS_MICROMIPS; + case AFL_ASE_MIPS16: + return EF_MIPS_ARCH_ASE_M16; + case AFL_ASE_MDMX: + return EF_MIPS_ARCH_ASE_MDMX; + default: + return 0; + } +} + +static ErrorOr<MipsISAs> sectionFlagsToIsa(uint8_t isaLevel, uint8_t isaRev, + uint8_t isaExt) { + for (const auto &p : abiIsaArchPair) + if (p._isaLevel == isaLevel && p._isaRev == isaRev && p._isaExt == isaExt) + return p._arch; + return make_dynamic_error_code( + StringRef("Unknown ISA level/revision/extension ") + Twine(isaLevel) + + "/" + Twine(isaRev) + "/" + Twine(isaExt)); +} + +static std::tuple<uint8_t, uint8_t, uint32_t> isaToSectionFlags(unsigned isa) { + for (const auto &p : abiIsaArchPair) + if (p._arch == isa) + return std::make_tuple(p._isaLevel, p._isaRev, p._isaExt); + llvm_unreachable("Unknown MIPS ISA"); +} + +static bool checkCompatibility(const MipsAbiFlags &hdr, + const MipsAbiFlags &sec) { + uint32_t secIsa = ArchNone; + switch (sec._isa) { + case Arch32r3: + case Arch32r5: + secIsa = Arch32r2; + break; + case Arch64r3: + case Arch64r5: + secIsa = Arch64r2; + break; + default: + secIsa = sec._isa; + break; + } + if (secIsa != hdr._isa) { + llvm::errs() << "inconsistent ISA between .MIPS.abiflags " + "and ELF header e_flags field\n"; + return false; + } + if ((sec._ases & hdr._ases) != hdr._ases) { + llvm::errs() << "inconsistent ASEs between .MIPS.abiflags " + "and ELF header e_flags field\n"; + return false; + } + return true; +} + +static int compareFpAbi(uint32_t fpA, uint32_t fpB) { + if (fpA == fpB) + return 0; + if (fpB == Val_GNU_MIPS_ABI_FP_ANY) + return 1; + if (fpB == Val_GNU_MIPS_ABI_FP_64A && fpA == Val_GNU_MIPS_ABI_FP_64) + return 1; + if (fpB != Val_GNU_MIPS_ABI_FP_XX) + return -1; + if (fpA == Val_GNU_MIPS_ABI_FP_DOUBLE || fpA == Val_GNU_MIPS_ABI_FP_64 || + fpA == Val_GNU_MIPS_ABI_FP_64A) + return 1; + return -1; +} + +static StringRef getFpAbiName(uint32_t fpAbi) { + switch (fpAbi) { + case Val_GNU_MIPS_ABI_FP_ANY: + return "<any>"; + case Val_GNU_MIPS_ABI_FP_DOUBLE: + return "-mdouble-float"; + case Val_GNU_MIPS_ABI_FP_SINGLE: + return "-msingle-float"; + case Val_GNU_MIPS_ABI_FP_SOFT: + return "-msoft-float"; + case Val_GNU_MIPS_ABI_FP_OLD_64: + return "-mips32r2 -mfp64 (old)"; + case Val_GNU_MIPS_ABI_FP_XX: + return "-mfpxx"; + case Val_GNU_MIPS_ABI_FP_64: + return "-mgp32 -mfp64"; + case Val_GNU_MIPS_ABI_FP_64A: + return "-mgp32 -mfp64 -mno-odd-spreg"; + default: + return "<unknown>"; + } +} + +static uint32_t selectFpAbiFlag(uint32_t oldFp, uint32_t newFp) { + if (compareFpAbi(newFp, oldFp) >= 0) + return newFp; + if (compareFpAbi(oldFp, newFp) < 0) + llvm::errs() << "FP ABI " << getFpAbiName(oldFp) << " is incompatible with " + << getFpAbiName(newFp) << "\n"; + return oldFp; +} + +namespace lld { +namespace elf { + +template <class ELFT> bool MipsAbiInfoHandler<ELFT>::isMicroMips() const { + assert(_abiFlags.hasValue()); + return _abiFlags->_ases & AFL_ASE_MICROMIPS; +} + +template <class ELFT> bool MipsAbiInfoHandler<ELFT>::isMipsR6() const { + assert(_abiFlags.hasValue()); + return _abiFlags->_isa == Arch32r6 || _abiFlags->_isa == Arch64r6; +} + +template <class ELFT> bool MipsAbiInfoHandler<ELFT>::isFp64() const { + assert(_abiFlags.hasValue()); + return _abiFlags->_fpAbi == Val_GNU_MIPS_ABI_FP_64 || + _abiFlags->_fpAbi == Val_GNU_MIPS_ABI_FP_64A; +} + +template <class ELFT> bool MipsAbiInfoHandler<ELFT>::isCPicOnly() const { + assert(_abiFlags.hasValue()); + return _abiFlags->_isCPic && !_abiFlags->_isPic; +} + +template <class ELFT> uint32_t MipsAbiInfoHandler<ELFT>::getFlags() const { + std::lock_guard<std::mutex> lock(_mutex); + uint32_t flags = 0; + if (_abiFlags.hasValue()) { + flags |= isaToHeaderFlags(_abiFlags->_isa); + flags |= asesToFlags(_abiFlags->_ases); + flags |= _abiFlags->_abi; + flags |= _abiFlags->_isPic ? EF_MIPS_PIC : 0u; + flags |= _abiFlags->_isCPic ? EF_MIPS_CPIC : 0u; + flags |= _abiFlags->_isNoReorder ? EF_MIPS_NOREORDER : 0u; + flags |= _abiFlags->_is32BitMode ? EF_MIPS_32BITMODE : 0u; + flags |= _abiFlags->_isNan2008 ? EF_MIPS_NAN2008 : 0u; + } + return flags; +} + +template <class ELFT> +llvm::Optional<typename MipsAbiInfoHandler<ELFT>::Elf_Mips_RegInfo> +MipsAbiInfoHandler<ELFT>::getRegistersMask() const { + std::lock_guard<std::mutex> lock(_mutex); + return _regMask; +} + +template <class ELFT> +llvm::Optional<typename MipsAbiInfoHandler<ELFT>::Elf_Mips_ABIFlags> +MipsAbiInfoHandler<ELFT>::getAbiFlags() const { + std::lock_guard<std::mutex> lock(_mutex); + if (!_hasAbiSection) + return llvm::Optional<Elf_Mips_ABIFlags>(); + + Elf_Mips_ABIFlags sec; + sec.version = 0; + std::tie(sec.isa_level, sec.isa_rev, sec.isa_ext) = + isaToSectionFlags(_abiFlags->_isa); + sec.gpr_size = _abiFlags->_gprSize; + sec.cpr1_size = _abiFlags->_cpr1Size; + sec.cpr2_size = _abiFlags->_cpr2Size; + sec.fp_abi = _abiFlags->_fpAbi; + sec.ases = _abiFlags->_ases; + sec.flags1 = _abiFlags->_flags1; + sec.flags2 = 0; + return sec; +} + +template <class ELFT> MipsAbi MipsAbiInfoHandler<ELFT>::getAbi() const { + if (!_abiFlags.hasValue()) + return ELFT::Is64Bits ? MipsAbi::N64 : MipsAbi::O32; + switch (_abiFlags->_abi & (EF_MIPS_ABI_O32 | EF_MIPS_ABI2)) { + case EF_MIPS_ABI_O32: + return MipsAbi::O32; + case EF_MIPS_ABI2: + return MipsAbi::N32; + case 0: + return MipsAbi::N64; + default: + llvm_unreachable("Unknown ABI flag"); + } +} + +template <class ELFT> +std::error_code +MipsAbiInfoHandler<ELFT>::mergeFlags(uint32_t newFlags, + const Elf_Mips_ABIFlags *newSec) { + std::lock_guard<std::mutex> lock(_mutex); + + ErrorOr<MipsAbiFlags> abiFlags = createAbiFlags(newFlags, newSec); + if (auto ec = abiFlags.getError()) + return ec; + + // We support three ABI: O32, N32, and N64. The last one does not have + // the corresponding ELF flag. + if (ELFT::Is64Bits) { + if (abiFlags->_abi) + return make_dynamic_error_code("Unsupported ABI"); + } else { + if (!(abiFlags->_abi & (EF_MIPS_ABI_O32 | EF_MIPS_ABI2))) + return make_dynamic_error_code("Unsupported ABI"); + } + + // ... and still do not support MIPS-16 extension. + if (abiFlags->_ases & AFL_ASE_MIPS16) + return make_dynamic_error_code("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 (abiFlags->_isPic) + abiFlags->_isCPic = true; + + // If the old set of flags is empty, use the new one as a result. + if (!_abiFlags.hasValue()) { + _abiFlags = *abiFlags; + return std::error_code(); + } + + // Check ABI compatibility. + if (abiFlags->_abi != _abiFlags->_abi) + return make_dynamic_error_code("Linking modules with incompatible ABI"); + + // Check PIC / CPIC flags compatibility. + if (abiFlags->_isCPic != _abiFlags->_isCPic) + llvm::errs() << "lld warning: linking abicalls and non-abicalls files\n"; + + if (!abiFlags->_isPic) + _abiFlags->_isPic = false; + if (abiFlags->_isCPic) + _abiFlags->_isCPic = true; + + // Check mixing -mnan=2008 / -mnan=legacy modules. + if (abiFlags->_isNan2008 != _abiFlags->_isNan2008) + return make_dynamic_error_code( + "Linking -mnan=2008 and -mnan=legacy modules"); + + // Check ISA compatibility and update the extension flag. + if (!matchMipsISA(MipsISAs(abiFlags->_isa), MipsISAs(_abiFlags->_isa))) { + if (!matchMipsISA(MipsISAs(_abiFlags->_isa), MipsISAs(abiFlags->_isa))) + return make_dynamic_error_code("Linking modules with incompatible ISA"); + _abiFlags->_isa = abiFlags->_isa; + } + + _abiFlags->_ases |= abiFlags->_ases; + _abiFlags->_isNoReorder = _abiFlags->_isNoReorder || abiFlags->_isNoReorder; + _abiFlags->_is32BitMode = _abiFlags->_is32BitMode || abiFlags->_is32BitMode; + + _abiFlags->_fpAbi = selectFpAbiFlag(_abiFlags->_fpAbi, abiFlags->_fpAbi); + _abiFlags->_gprSize = std::max(_abiFlags->_gprSize, abiFlags->_gprSize); + _abiFlags->_cpr1Size = std::max(_abiFlags->_cpr1Size, abiFlags->_cpr1Size); + _abiFlags->_cpr2Size = std::max(_abiFlags->_cpr2Size, abiFlags->_cpr2Size); + _abiFlags->_flags1 |= abiFlags->_flags1; + + return std::error_code(); +} + +template <class ELFT> +void MipsAbiInfoHandler<ELFT>::mergeRegistersMask( + const Elf_Mips_RegInfo &info) { + std::lock_guard<std::mutex> lock(_mutex); + if (!_regMask.hasValue()) { + _regMask = info; + return; + } + _regMask->ri_gprmask = _regMask->ri_gprmask | info.ri_gprmask; + _regMask->ri_cprmask[0] = _regMask->ri_cprmask[0] | info.ri_cprmask[0]; + _regMask->ri_cprmask[1] = _regMask->ri_cprmask[1] | info.ri_cprmask[1]; + _regMask->ri_cprmask[2] = _regMask->ri_cprmask[2] | info.ri_cprmask[2]; + _regMask->ri_cprmask[3] = _regMask->ri_cprmask[3] | info.ri_cprmask[3]; +} + +template <class ELFT> +ErrorOr<MipsAbiFlags> +MipsAbiInfoHandler<ELFT>::createAbiFlags(uint32_t flags, + const Elf_Mips_ABIFlags *sec) { + ErrorOr<MipsAbiFlags> hdrFlags = createAbiFromHeaderFlags(flags); + if (auto ec = hdrFlags.getError()) + return ec; + if (!sec) + return *hdrFlags; + ErrorOr<MipsAbiFlags> secFlags = createAbiFromSection(*sec); + if (auto ec = secFlags.getError()) + return ec; + if (!checkCompatibility(*hdrFlags, *secFlags)) + return *hdrFlags; + + _hasAbiSection = true; + + secFlags->_abi = hdrFlags->_abi; + secFlags->_isPic = hdrFlags->_isPic; + secFlags->_isCPic = hdrFlags->_isCPic; + secFlags->_isNoReorder = hdrFlags->_isNoReorder; + secFlags->_is32BitMode = hdrFlags->_is32BitMode; + secFlags->_isNan2008 = hdrFlags->_isNan2008; + return *secFlags; +} + +template <class ELFT> +ErrorOr<MipsAbiFlags> +MipsAbiInfoHandler<ELFT>::createAbiFromHeaderFlags(uint32_t flags) { + MipsAbiFlags abi; + ErrorOr<MipsISAs> isa = headerFlagsToIsa(flags); + if (auto ec = isa.getError()) + return ec; + abi._isa = *isa; + + abi._fpAbi = Val_GNU_MIPS_ABI_FP_ANY; + abi._cpr1Size = AFL_REG_NONE; + abi._cpr2Size = AFL_REG_NONE; + abi._gprSize = is32BitElfFlags(flags) ? AFL_REG_32 : AFL_REG_64; + + ErrorOr<uint32_t> ases = flagsToAses(flags); + if (auto ec = ases.getError()) + return ec; + abi._ases = *ases; + abi._flags1 = 0; + abi._abi = flags & (EF_MIPS_ABI | EF_MIPS_ABI2); + abi._isPic = flags & EF_MIPS_PIC; + abi._isCPic = flags & EF_MIPS_CPIC; + abi._isNoReorder = flags & EF_MIPS_NOREORDER; + abi._is32BitMode = flags & EF_MIPS_32BITMODE; + abi._isNan2008 = flags & EF_MIPS_NAN2008; + return abi; +} + +template <class ELFT> +ErrorOr<MipsAbiFlags> +MipsAbiInfoHandler<ELFT>::createAbiFromSection(const Elf_Mips_ABIFlags &sec) { + MipsAbiFlags abi; + ErrorOr<MipsISAs> isa = + sectionFlagsToIsa(sec.isa_level, sec.isa_rev, sec.isa_ext); + if (auto ec = isa.getError()) + return ec; + abi._isa = *isa; + abi._fpAbi = sec.fp_abi; + abi._cpr1Size = sec.cpr1_size; + abi._cpr2Size = sec.cpr2_size; + abi._gprSize = sec.gpr_size; + abi._ases = sec.ases; + abi._flags1 = sec.flags1; + if (sec.flags2 != 0) + return make_dynamic_error_code("unexpected non-zero 'flags2' value"); + return abi; +} + +template class MipsAbiInfoHandler<ELF32BE>; +template class MipsAbiInfoHandler<ELF32LE>; +template class MipsAbiInfoHandler<ELF64BE>; +template class MipsAbiInfoHandler<ELF64LE>; + +} +} diff --git a/lib/ReaderWriter/ELF/Mips/MipsAbiInfoHandler.h b/lib/ReaderWriter/ELF/Mips/MipsAbiInfoHandler.h new file mode 100644 index 0000000000000..44da29f09214f --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsAbiInfoHandler.h @@ -0,0 +1,83 @@ +//===- lib/ReaderWriter/ELF/MipsAbiInfoHandler.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_ABI_INFO_HANDLER_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_ABI_INFO_HANDLER_H + +#include "llvm/ADT/Optional.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Support/ErrorOr.h" +#include <mutex> +#include <system_error> + +namespace lld { +namespace elf { + +enum class MipsAbi { O32, N32, N64 }; + +struct MipsAbiFlags { + unsigned _isa = 0; + unsigned _fpAbi = 0; + unsigned _ases = 0; + unsigned _flags1 = 0; + unsigned _gprSize = 0; + unsigned _cpr1Size = 0; + unsigned _cpr2Size = 0; + + unsigned _abi = 0; + + bool _isPic = false; + bool _isCPic = false; + bool _isNoReorder = false; + bool _is32BitMode = false; + bool _isNan2008 = false; +}; + +template <class ELFT> class MipsAbiInfoHandler { +public: + typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo; + typedef llvm::object::Elf_Mips_ABIFlags<ELFT> Elf_Mips_ABIFlags; + + MipsAbiInfoHandler() = default; + + bool hasMipsAbiSection() const { return _hasAbiSection; } + bool isMicroMips() const; + bool isMipsR6() const; + bool isFp64() const; + bool isCPicOnly() const; + + uint32_t getFlags() const; + llvm::Optional<Elf_Mips_RegInfo> getRegistersMask() const; + llvm::Optional<Elf_Mips_ABIFlags> getAbiFlags() const; + + MipsAbi getAbi() const; + + /// \brief Merge saved ELF header flags and the new set of flags. + std::error_code mergeFlags(uint32_t newFlags, + const Elf_Mips_ABIFlags *newAbi); + + /// \brief Merge saved and new sets of registers usage masks. + void mergeRegistersMask(const Elf_Mips_RegInfo &info); + +private: + mutable std::mutex _mutex; + bool _hasAbiSection = false; + llvm::Optional<MipsAbiFlags> _abiFlags; + llvm::Optional<Elf_Mips_RegInfo> _regMask; + + llvm::ErrorOr<MipsAbiFlags> createAbiFlags(uint32_t flags, + const Elf_Mips_ABIFlags *sec); + static llvm::ErrorOr<MipsAbiFlags> createAbiFromHeaderFlags(uint32_t flags); + static llvm::ErrorOr<MipsAbiFlags> + createAbiFromSection(const Elf_Mips_ABIFlags &sec); +}; + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.cpp b/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.cpp index 8bf80257fc89f..a7062813df42d 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.cpp +++ b/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "MipsCtorsOrderPass.h" +#include "lld/Core/Simple.h" #include <algorithm> #include <climits> @@ -48,8 +49,8 @@ static int32_t getSectionPriority(StringRef path, StringRef sectionName) { return priority; } -void MipsCtorsOrderPass::perform(std::unique_ptr<MutableFile> &f) { - auto definedAtoms = f->definedAtoms(); +std::error_code MipsCtorsOrderPass::perform(SimpleFile &f) { + auto definedAtoms = f.definedAtoms(); auto last = std::stable_partition(definedAtoms.begin(), definedAtoms.end(), [](const DefinedAtom *atom) { @@ -70,4 +71,6 @@ void MipsCtorsOrderPass::perform(std::unique_ptr<MutableFile> &f) { return leftPriority < rightPriority; }); + + return std::error_code(); } diff --git a/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.h b/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.h index eeb1a194f9c77..5b12b7de0fa25 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.h +++ b/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.h @@ -17,7 +17,7 @@ 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; + std::error_code perform(SimpleFile &mergedFile) override; }; } } diff --git a/lib/ReaderWriter/ELF/Mips/MipsDynamicLibraryWriter.h b/lib/ReaderWriter/ELF/Mips/MipsDynamicLibraryWriter.h deleted file mode 100644 index 30b5b0ba6dae9..0000000000000 --- a/lib/ReaderWriter/ELF/Mips/MipsDynamicLibraryWriter.h +++ /dev/null @@ -1,101 +0,0 @@ -//===- 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 index 2b9562f42b57d..480c69cf46002 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsDynamicTable.h +++ b/lib/ReaderWriter/ELF/Mips/MipsDynamicTable.h @@ -9,83 +9,89 @@ #ifndef LLD_READER_WRITER_ELF_MIPS_MIPS_DYNAMIC_TABLE_H #define LLD_READER_WRITER_ELF_MIPS_MIPS_DYNAMIC_TABLE_H -#include "DefaultLayout.h" +#include "TargetLayout.h" #include "SectionChunks.h" namespace lld { namespace elf { -template <class ELFType> class MipsTargetLayout; +template <class ELFT> class MipsTargetLayout; -template <class MipsELFType> -class MipsDynamicTable : public DynamicTable<MipsELFType> { +template <class ELFT> class MipsDynamicTable : public DynamicTable<ELFT> { public: - MipsDynamicTable(const ELFLinkingContext &ctx, - MipsTargetLayout<MipsELFType> &layout) - : DynamicTable<MipsELFType>(ctx, layout, ".dynamic", - DefaultLayout<MipsELFType>::ORDER_DYNAMIC), - _mipsTargetLayout(layout) {} + MipsDynamicTable(const ELFLinkingContext &ctx, MipsTargetLayout<ELFT> &layout) + : DynamicTable<ELFT>(ctx, layout, ".dynamic", + TargetLayout<ELFT>::ORDER_DYNAMIC), + _targetLayout(layout) {} void createDefaultEntries() override { - DynamicTable<MipsELFType>::createDefaultEntries(); - - typename DynamicTable<MipsELFType>::Elf_Dyn dyn; + DynamicTable<ELFT>::createDefaultEntries(); // Version id for the Runtime Linker Interface. - dyn.d_un.d_val = 1; - dyn.d_tag = DT_MIPS_RLD_VERSION; - this->addEntry(dyn); + this->addEntry(DT_MIPS_RLD_VERSION, 1); + + // The .rld_map section address. + if (this->_ctx.getOutputELFType() == ET_EXEC) { + _dt_rldmap = this->addEntry(DT_MIPS_RLD_MAP, 0); + _dt_rldmaprel = this->addEntry(DT_MIPS_RLD_MAP_REL, 0); + } // MIPS flags. - dyn.d_un.d_val = RHF_NOTPOT; - dyn.d_tag = DT_MIPS_FLAGS; - this->addEntry(dyn); + this->addEntry(DT_MIPS_FLAGS, RHF_NOTPOT); // The base address of the segment. - dyn.d_un.d_ptr = 0; - dyn.d_tag = DT_MIPS_BASE_ADDRESS; - _dt_baseaddr = this->addEntry(dyn); + _dt_baseaddr = this->addEntry(DT_MIPS_BASE_ADDRESS, 0); // 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); + _dt_localgot = this->addEntry(DT_MIPS_LOCAL_GOTNO, 0); // Number of entries in the .dynsym section. - dyn.d_un.d_val = 0; - dyn.d_tag = DT_MIPS_SYMTABNO; - _dt_symtabno = this->addEntry(dyn); + _dt_symtabno = this->addEntry(DT_MIPS_SYMTABNO, 0); // 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); + _dt_gotsym = this->addEntry(DT_MIPS_GOTSYM, 0); // Address of the .got section. - dyn.d_un.d_val = 0; - dyn.d_tag = DT_PLTGOT; - _dt_pltgot = this->addEntry(dyn); + _dt_pltgot = this->addEntry(DT_PLTGOT, 0); + } + + void doPreFlight() override { + DynamicTable<ELFT>::doPreFlight(); + + if (_targetLayout.findOutputSection(".MIPS.options")) { + _dt_options = this->addEntry(DT_MIPS_OPTIONS, 0); + } } void updateDynamicTable() override { - DynamicTable<MipsELFType>::updateDynamicTable(); + DynamicTable<ELFT>::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()) + for (auto si : _targetLayout.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(); + auto &got = _targetLayout.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->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(); + this->_entries[_dt_pltgot].d_un.d_ptr = got.virtualAddr(); + + if (const auto *sec = _targetLayout.findOutputSection(".MIPS.options")) + this->_entries[_dt_options].d_un.d_ptr = sec->virtualAddr(); + + if (const auto *sec = _targetLayout.findOutputSection(".rld_map")) { + this->_entries[_dt_rldmap].d_un.d_ptr = sec->virtualAddr(); + this->_entries[_dt_rldmaprel].d_un.d_ptr = + sec->virtualAddr() - + (this->virtualAddr() + + _dt_rldmaprel * sizeof(typename DynamicTable<ELFT>::Elf_Dyn)); + } } int64_t getGotPltTag() override { return DT_MIPS_PLTGOT; } @@ -106,7 +112,10 @@ private: std::size_t _dt_gotsym; std::size_t _dt_pltgot; std::size_t _dt_baseaddr; - MipsTargetLayout<MipsELFType> &_mipsTargetLayout; + std::size_t _dt_options; + std::size_t _dt_rldmap; + std::size_t _dt_rldmaprel; + MipsTargetLayout<ELFT> &_targetLayout; }; } // end namespace elf diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFFile.cpp b/lib/ReaderWriter/ELF/Mips/MipsELFFile.cpp new file mode 100644 index 0000000000000..b081b63d77f77 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsELFFile.cpp @@ -0,0 +1,348 @@ +//===- lib/ReaderWriter/ELF/MipsELFFile.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 "MipsTargetHandler.h" +#include "llvm/ADT/StringExtras.h" + +namespace lld { +namespace elf { + +template <class ELFT> +MipsELFDefinedAtom<ELFT>::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) {} + +template <class ELFT> +const MipsELFFile<ELFT> &MipsELFDefinedAtom<ELFT>::file() const { + return static_cast<const MipsELFFile<ELFT> &>(this->_owningFile); +} + +template <class ELFT> +DefinedAtom::CodeModel MipsELFDefinedAtom<ELFT>::codeModel() const { + 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> bool MipsELFDefinedAtom<ELFT>::isPIC() const { + return file().isPIC() || codeModel() == DefinedAtom::codeMipsMicroPIC || + codeModel() == DefinedAtom::codeMipsPIC; +} + +template class MipsELFDefinedAtom<ELF32BE>; +template class MipsELFDefinedAtom<ELF32LE>; +template class MipsELFDefinedAtom<ELF64BE>; +template class MipsELFDefinedAtom<ELF64LE>; + +template <class ELFT> static bool isMips64EL() { + return ELFT::Is64Bits && ELFT::TargetEndianness == llvm::support::little; +} + +template <class ELFT, bool isRela> +static uint32_t +extractTag(const llvm::object::Elf_Rel_Impl<ELFT, isRela> &rel) { + return (rel.getType(isMips64EL<ELFT>()) & 0xffffff00) >> 8; +} + +template <class ELFT> +MipsELFReference<ELFT>::MipsELFReference(uint64_t symValue, const Elf_Rela &rel) + : ELFReference<ELFT>(&rel, rel.r_offset - symValue, + Reference::KindArch::Mips, + rel.getType(isMips64EL<ELFT>()) & 0xff, + rel.getSymbol(isMips64EL<ELFT>())), + _tag(extractTag(rel)) {} + +template <class ELFT> +MipsELFReference<ELFT>::MipsELFReference(uint64_t symValue, const Elf_Rel &rel) + : ELFReference<ELFT>(rel.r_offset - symValue, Reference::KindArch::Mips, + rel.getType(isMips64EL<ELFT>()) & 0xff, + rel.getSymbol(isMips64EL<ELFT>())), + _tag(extractTag(rel)) {} + +template class MipsELFReference<ELF32BE>; +template class MipsELFReference<ELF32LE>; +template class MipsELFReference<ELF64BE>; +template class MipsELFReference<ELF64LE>; + +template <class ELFT> +MipsELFFile<ELFT>::MipsELFFile(std::unique_ptr<MemoryBuffer> mb, + ELFLinkingContext &ctx) + : ELFFile<ELFT>(std::move(mb), ctx) {} + +template <class ELFT> bool MipsELFFile<ELFT>::isPIC() const { + return this->_objFile->getHeader()->e_flags & llvm::ELF::EF_MIPS_PIC; +} + +template <class ELFT> std::error_code MipsELFFile<ELFT>::doParse() { + 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(); +} + +template <class ELFT> +ELFDefinedAtom<ELFT> *MipsELFFile<ELFT>::createDefinedAtom( + 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 (this->_readerStorage) MipsELFDefinedAtom<ELFT>( + *this, symName, sectionName, sym, sectionHdr, contentData, referenceStart, + referenceEnd, referenceList); +} + +template <class ELFT> +const typename MipsELFFile<ELFT>::Elf_Shdr * +MipsELFFile<ELFT>::findSectionByType(uint64_t type) const { + for (const Elf_Shdr §ion : this->_objFile->sections()) + if (section.sh_type == type) + return §ion; + return nullptr; +} + +template <class ELFT> +const typename MipsELFFile<ELFT>::Elf_Shdr * +MipsELFFile<ELFT>::findSectionByFlags(uint64_t flags) const { + for (const Elf_Shdr §ion : this->_objFile->sections()) + if (section.sh_flags & flags) + return §ion; + return nullptr; +} + +template <class ELFT> +ErrorOr<const typename MipsELFFile<ELFT>::Elf_Mips_RegInfo *> +MipsELFFile<ELFT>::findRegInfoSec() const { + using namespace llvm::ELF; + 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) + return &opt->getRegInfo(); + 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_Mips_RegInfo)) + return make_dynamic_error_code( + StringRef("Invalid size of MIPS_REGINFO section")); + + return reinterpret_cast<const Elf_Mips_RegInfo *>(raw.data()); + } + return nullptr; +} + +template <class ELFT> +ErrorOr<const typename MipsELFFile<ELFT>::Elf_Mips_ABIFlags *> +MipsELFFile<ELFT>::findAbiFlagsSec() const { + const Elf_Shdr *sec = findSectionByType(SHT_MIPS_ABIFLAGS); + if (!sec) + return nullptr; + + 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_Mips_ABIFlags)) + return make_dynamic_error_code( + StringRef("Invalid size of MIPS_ABIFLAGS section")); + + const auto *abi = reinterpret_cast<const Elf_Mips_ABIFlags *>(raw.data()); + if (abi->version != 0) + return make_dynamic_error_code( + StringRef(".MIPS.abiflags section has unsupported version '") + + llvm::utostr(abi->version) + "'"); + + return abi; +} + +template <class ELFT> std::error_code MipsELFFile<ELFT>::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; + } + + auto &handler = + static_cast<MipsTargetHandler<ELFT> &>(this->_ctx.getTargetHandler()); + auto &abi = handler.getAbiInfoHandler(); + + ErrorOr<const Elf_Mips_RegInfo *> regInfoSec = findRegInfoSec(); + if (auto ec = regInfoSec.getError()) + return ec; + if (const Elf_Mips_RegInfo *regInfo = regInfoSec.get()) { + abi.mergeRegistersMask(*regInfo); + _gp0 = regInfo->ri_gp_value; + } + + ErrorOr<const Elf_Mips_ABIFlags *> abiFlagsSec = findAbiFlagsSec(); + if (auto ec = abiFlagsSec.getError()) + return ec; + + const Elf_Ehdr *hdr = this->_objFile->getHeader(); + if (std::error_code ec = abi.mergeFlags(hdr->e_flags, abiFlagsSec.get())) + return ec; + + return std::error_code(); +} + +template <class ELFT> +void MipsELFFile<ELFT>::createRelocationReferences( + const Elf_Sym *symbol, ArrayRef<uint8_t> content, + range<const Elf_Rela *> rels) { + const auto value = this->getSymbolValue(symbol); + unsigned numInGroup = 0; + for (const auto &rel : rels) { + if (rel.r_offset < value || value + content.size() <= rel.r_offset) { + numInGroup = 0; + continue; + } + if (numInGroup > 0) { + auto &last = + *static_cast<MipsELFReference<ELFT> *>(this->_references.back()); + if (last.offsetInAtom() + value == rel.r_offset) { + last.setTag(last.tag() | + (rel.getType(isMips64EL<ELFT>()) << 8 * (numInGroup - 1))); + ++numInGroup; + continue; + } + } + auto r = new (this->_readerStorage) MipsELFReference<ELFT>(value, rel); + this->addReferenceToSymbol(r, symbol); + this->_references.push_back(r); + numInGroup = 1; + } +} + +template <class ELFT> +void MipsELFFile<ELFT>::createRelocationReferences(const Elf_Sym *symbol, + ArrayRef<uint8_t> symContent, + ArrayRef<uint8_t> secContent, + const Elf_Shdr *relSec) { + const Elf_Shdr *symtab = *this->_objFile->getSection(relSec->sh_link); + auto rels = this->_objFile->rels(relSec); + const auto value = this->getSymbolValue(symbol); + for (const Elf_Rel *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(symtab, *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); + } +} + +template <class ELFT> +static uint8_t +getPrimaryType(const llvm::object::Elf_Rel_Impl<ELFT, false> &rel) { + return rel.getType(isMips64EL<ELFT>()) & 0xff; +} + +template <class ELFT> +Reference::Addend +MipsELFFile<ELFT>::readAddend(const Elf_Rel &ri, + const ArrayRef<uint8_t> content) const { + return readMipsRelocAddend<ELFT>(getPrimaryType(ri), + content.data() + ri.r_offset); +} + +template <class ELFT> +uint32_t MipsELFFile<ELFT>::getPairRelocation(const Elf_Shdr *symtab, + 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(symtab, 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(symtab, rel)) + return llvm::ELF::R_MICROMIPS_LO16; + break; + default: + // Nothing to do. + break; + } + return llvm::ELF::R_MIPS_NONE; +} + +template <class ELFT> +const typename MipsELFFile<ELFT>::Elf_Rel * +MipsELFFile<ELFT>::findMatchingRelocation(uint32_t pairRelType, + const Elf_Rel *rit, + const Elf_Rel *eit) const { + return std::find_if(rit, eit, [&](const Elf_Rel &rel) { + return getPrimaryType(rel) == pairRelType && + rel.getSymbol(isMips64EL<ELFT>()) == + rit->getSymbol(isMips64EL<ELFT>()); + }); +} + +template <class ELFT> +bool MipsELFFile<ELFT>::isLocalBinding(const Elf_Shdr *symtab, + const Elf_Rel &rel) const { + return this->_objFile->getSymbol(symtab, rel.getSymbol(isMips64EL<ELFT>())) + ->getBinding() == llvm::ELF::STB_LOCAL; +} + +template class MipsELFFile<ELF32BE>; +template class MipsELFFile<ELF32LE>; +template class MipsELFFile<ELF64BE>; +template class MipsELFFile<ELF64LE>; + +} // elf +} // lld diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFFile.h b/lib/ReaderWriter/ELF/Mips/MipsELFFile.h index 7381c7e977bf2..934934b539ccd 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsELFFile.h +++ b/lib/ReaderWriter/ELF/Mips/MipsELFFile.h @@ -12,42 +12,7 @@ #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. +#include "llvm/ADT/STLExtras.h" namespace lld { namespace elf { @@ -64,50 +29,21 @@ public: 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) {} + std::vector<ELFReference<ELFT> *> &referenceList); - const MipsELFFile<ELFT>& file() const override { - return static_cast<const MipsELFFile<ELFT> &>(this->_owningFile); - } + const MipsELFFile<ELFT>& file() const override; + DefinedAtom::CodeModel codeModel() const override; - 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; - } - } + bool isPIC() const; }; 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) {} + MipsELFReference(uint64_t symValue, const Elf_Rela &rel); + MipsELFReference(uint64_t symValue, const Elf_Rel &rel); uint32_t tag() const override { return _tag; } void setTag(uint32_t tag) { _tag = tag; } @@ -118,211 +54,70 @@ private: template <class ELFT> class MipsELFFile : public ELFFile<ELFT> { public: - MipsELFFile(std::unique_ptr<MemoryBuffer> mb, MipsLinkingContext &ctx) - : ELFFile<ELFT>(std::move(mb), ctx) {} + MipsELFFile(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &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; - } + bool isPIC() const; /// \brief gp register value stored in the .reginfo section. - int64_t getGP0() const { return _gp0 ? *_gp0 : 0; } + int64_t getGP0() const { return _gp0; } /// \brief .tdata section address plus fixed offset. - uint64_t getTPOffset() const { return *_tpOff; } - uint64_t getDTPOffset() const { return *_dtpOff; } + 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(); - } + std::error_code doParse() override; 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; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Rel Elf_Rel; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Rela Elf_Rela; 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; + int64_t _gp0 = 0; + uint64_t _tpOff = 0; + uint64_t _dtpOff = 0; - 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(); - } + ELFDefinedAtom<ELFT> * + createDefinedAtom(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; 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); - } - } - + range<const Elf_Rela *> rels) override; 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; + const Elf_Shdr *RelSec) override; - auto r = new (this->_readerStorage) MipsELFReference<ELFT>(value, *rit); - this->addReferenceToSymbol(r, symbol); - this->_references.push_back(r); + const Elf_Shdr *findSectionByType(uint64_t type) const; + const Elf_Shdr *findSectionByFlags(uint64_t flags) const; - 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); - } - } + typedef typename llvm::object::ELFFile<ELFT>::Elf_Ehdr Elf_Ehdr; + typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo; + typedef llvm::object::Elf_Mips_Options<ELFT> Elf_Mips_Options; + typedef llvm::object::Elf_Mips_ABIFlags<ELFT> Elf_Mips_ABIFlags; - 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); - } + ErrorOr<const Elf_Mips_RegInfo *> findRegInfoSec() const; + ErrorOr<const Elf_Mips_ABIFlags*> findAbiFlagsSec() const; - 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; - } + std::error_code readAuxData(); - 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); - }); - } + Reference::Addend readAddend(const Elf_Rel &ri, + const ArrayRef<uint8_t> content) const; - 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; - } -}; + uint32_t getPairRelocation(const Elf_Shdr *Symtab, const Elf_Rel &rel) const; -template <class ELFT> class MipsDynamicFile : public DynamicFile<ELFT> { -public: - MipsDynamicFile(const MipsLinkingContext &context, StringRef name) - : DynamicFile<ELFT>(context, name) {} + const Elf_Rel *findMatchingRelocation(uint32_t pairRelType, + const Elf_Rel *rit, + const Elf_Rel *eit) const; + + bool isLocalBinding(const Elf_Shdr *Symtab, const Elf_Rel &rel) const; }; } // elf diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp b/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp deleted file mode 100644 index 0ef2c70b81564..0000000000000 --- a/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp +++ /dev/null @@ -1,149 +0,0 @@ -//===- 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 deleted file mode 100644 index 6ade86f0163cc..0000000000000 --- a/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.h +++ /dev/null @@ -1,36 +0,0 @@ -//===- 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 deleted file mode 100644 index 8b325b38bb522..0000000000000 --- a/lib/ReaderWriter/ELF/Mips/MipsELFReader.h +++ /dev/null @@ -1,93 +0,0 @@ -//===- 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.cpp b/lib/ReaderWriter/ELF/Mips/MipsELFWriters.cpp new file mode 100644 index 0000000000000..b97a4f5a90704 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsELFWriters.cpp @@ -0,0 +1,292 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsELFWriters.cpp -----------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsDynamicTable.h" +#include "MipsELFWriters.h" +#include "MipsLinkingContext.h" +#include "MipsTargetHandler.h" +#include "MipsTargetLayout.h" + +namespace { +class MipsDynamicAtom : public lld::elf::DynamicAtom { +public: + MipsDynamicAtom(const lld::File &f) : DynamicAtom(f) {} + + ContentPermissions permissions() const override { return permR__; } +}; +} + +namespace lld { +namespace elf { + +template <class ELFT> +MipsELFWriter<ELFT>::MipsELFWriter(MipsLinkingContext &ctx, + MipsTargetLayout<ELFT> &targetLayout, + const MipsAbiInfoHandler<ELFT> &abiInfo) + : _ctx(ctx), _targetLayout(targetLayout), _abiInfo(abiInfo) {} + +template <class ELFT> +void MipsELFWriter<ELFT>::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); + + unsigned char abiVer = 0; + if (_ctx.getOutputELFType() == ET_EXEC && _abiInfo.isCPicOnly()) + abiVer = 1; + if (_abiInfo.isFp64()) + abiVer = 3; + + elfHeader.e_ident(llvm::ELF::EI_ABIVERSION, abiVer); + elfHeader.e_flags(_abiInfo.getFlags()); +} + +template <class ELFT> +void MipsELFWriter<ELFT>::finalizeMipsRuntimeAtomValues() { + auto gotSection = _targetLayout.findOutputSection(".got"); + auto got = gotSection ? gotSection->virtualAddr() : 0; + auto gp = gotSection ? got + _targetLayout.getGPOffset() : 0; + + setAtomValue("_gp", gp); + setAtomValue("_gp_disp", gp); + setAtomValue("__gnu_local_gp", gp); + + if (_ctx.isDynamic() && _ctx.getOutputELFType() == ET_EXEC) + setAtomValue("_DYNAMIC_LINKING", 1); +} + +template <class ELFT> +std::unique_ptr<RuntimeFile<ELFT>> MipsELFWriter<ELFT>::createRuntimeFile() { + auto file = llvm::make_unique<RuntimeFile<ELFT>>(_ctx, "Mips runtime file"); + file->addAbsoluteAtom("_gp"); + file->addAbsoluteAtom("_gp_disp"); + file->addAbsoluteAtom("__gnu_local_gp"); + if (_ctx.isDynamic()) { + file->addAtom(*new (file->allocator()) MipsDynamicAtom(*file)); + if (_ctx.getOutputELFType() == ET_EXEC) + file->addAbsoluteAtom("_DYNAMIC_LINKING"); + } + return file; +} + +template <class ELFT> +unique_bump_ptr<Section<ELFT>> +MipsELFWriter<ELFT>::createOptionsSection(llvm::BumpPtrAllocator &alloc) { + typedef unique_bump_ptr<Section<ELFT>> Ptr; + const auto ®Mask = _abiInfo.getRegistersMask(); + if (!regMask.hasValue()) + return Ptr(); + return ELFT::Is64Bits + ? Ptr(new (alloc) + MipsOptionsSection<ELFT>(_ctx, _targetLayout, *regMask)) + : Ptr(new (alloc) + MipsReginfoSection<ELFT>(_ctx, _targetLayout, *regMask)); +} + +template <class ELFT> +unique_bump_ptr<Section<ELFT>> +MipsELFWriter<ELFT>::createAbiFlagsSection(llvm::BumpPtrAllocator &alloc) { + typedef unique_bump_ptr<Section<ELFT>> Ptr; + const auto &abi = _abiInfo.getAbiFlags(); + if (!abi.hasValue()) + return Ptr(); + return Ptr(new (alloc) MipsAbiFlagsSection<ELFT>(_ctx, _targetLayout, *abi)); +} + +template <class ELFT> +void MipsELFWriter<ELFT>::setAtomValue(StringRef name, uint64_t value) { + AtomLayout *atom = _targetLayout.findAbsoluteAtom(name); + assert(atom); + atom->_virtualAddr = value; +} + +template <class ELFT> +MipsDynamicLibraryWriter<ELFT>::MipsDynamicLibraryWriter( + MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &layout, + const MipsAbiInfoHandler<ELFT> &abiInfo) + : DynamicLibraryWriter<ELFT>(ctx, layout), + _writeHelper(ctx, layout, abiInfo), _targetLayout(layout) {} + +template <class ELFT> +void MipsDynamicLibraryWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + DynamicLibraryWriter<ELFT>::createImplicitFiles(result); + result.push_back(_writeHelper.createRuntimeFile()); +} + +template <class ELFT> +void MipsDynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues() { + DynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues(); + _writeHelper.finalizeMipsRuntimeAtomValues(); +} + +template <class ELFT> +void MipsDynamicLibraryWriter<ELFT>::createDefaultSections() { + DynamicLibraryWriter<ELFT>::createDefaultSections(); + _reginfo = _writeHelper.createOptionsSection(this->_alloc); + if (_reginfo) + this->_layout.addSection(_reginfo.get()); + _abiFlags = _writeHelper.createAbiFlagsSection(this->_alloc); + if (_abiFlags) + this->_layout.addSection(_abiFlags.get()); +} + +template <class ELFT> +std::error_code MipsDynamicLibraryWriter<ELFT>::setELFHeader() { + DynamicLibraryWriter<ELFT>::setELFHeader(); + _writeHelper.setELFHeader(*this->_elfHeader); + return std::error_code(); +} + +template <class ELFT> +unique_bump_ptr<SymbolTable<ELFT>> +MipsDynamicLibraryWriter<ELFT>::createSymbolTable() { + return unique_bump_ptr<SymbolTable<ELFT>>( + new (this->_alloc) MipsSymbolTable<ELFT>(this->_ctx)); +} + +template <class ELFT> +unique_bump_ptr<DynamicTable<ELFT>> +MipsDynamicLibraryWriter<ELFT>::createDynamicTable() { + return unique_bump_ptr<DynamicTable<ELFT>>( + new (this->_alloc) MipsDynamicTable<ELFT>(this->_ctx, _targetLayout)); +} + +template <class ELFT> +unique_bump_ptr<DynamicSymbolTable<ELFT>> +MipsDynamicLibraryWriter<ELFT>::createDynamicSymbolTable() { + return unique_bump_ptr<DynamicSymbolTable<ELFT>>(new ( + this->_alloc) MipsDynamicSymbolTable<ELFT>(this->_ctx, _targetLayout)); +} + +template class MipsDynamicLibraryWriter<ELF32BE>; +template class MipsDynamicLibraryWriter<ELF32LE>; +template class MipsDynamicLibraryWriter<ELF64BE>; +template class MipsDynamicLibraryWriter<ELF64LE>; + +template <class ELFT> +MipsExecutableWriter<ELFT>::MipsExecutableWriter( + MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &layout, + const MipsAbiInfoHandler<ELFT> &abiInfo) + : ExecutableWriter<ELFT>(ctx, layout), _writeHelper(ctx, layout, abiInfo), + _targetLayout(layout) {} + +template <class ELFT> +std::error_code MipsExecutableWriter<ELFT>::setELFHeader() { + std::error_code ec = ExecutableWriter<ELFT>::setELFHeader(); + if (ec) + return ec; + + StringRef entryName = this->_ctx.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 (_targetLayout.getGOTSection().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->_ctx.isDynamicallyExportedSymbol(da->name()) && + !(this->_ctx.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 (_targetLayout.getGOTSection().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> +void MipsExecutableWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + ExecutableWriter<ELFT>::createImplicitFiles(result); + result.push_back(_writeHelper.createRuntimeFile()); +} + +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> void MipsExecutableWriter<ELFT>::createDefaultSections() { + ExecutableWriter<ELFT>::createDefaultSections(); + _reginfo = _writeHelper.createOptionsSection(this->_alloc); + if (_reginfo) + this->_layout.addSection(_reginfo.get()); + _abiFlags = _writeHelper.createAbiFlagsSection(this->_alloc); + if (_abiFlags) + this->_layout.addSection(_abiFlags.get()); +} + +template <class ELFT> +unique_bump_ptr<SymbolTable<ELFT>> +MipsExecutableWriter<ELFT>::createSymbolTable() { + return unique_bump_ptr<SymbolTable<ELFT>>( + new (this->_alloc) MipsSymbolTable<ELFT>(this->_ctx)); +} + +/// \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->_ctx, _targetLayout)); +} + +/// \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->_ctx, _targetLayout)); +} + +template class MipsExecutableWriter<ELF32BE>; +template class MipsExecutableWriter<ELF32LE>; +template class MipsExecutableWriter<ELF64BE>; +template class MipsExecutableWriter<ELF64LE>; + +} // elf +} // lld diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFWriters.h b/lib/ReaderWriter/ELF/Mips/MipsELFWriters.h index d94dd757a0f30..31b84f947c951 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsELFWriters.h +++ b/lib/ReaderWriter/ELF/Mips/MipsELFWriters.h @@ -9,71 +9,91 @@ #ifndef LLD_READER_WRITER_ELF_MIPS_MIPS_ELF_WRITERS_H #define LLD_READER_WRITER_ELF_MIPS_MIPS_ELF_WRITERS_H +#include "DynamicLibraryWriter.h" +#include "ExecutableWriter.h" +#include "MipsAbiInfoHandler.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; - } + MipsELFWriter(MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &targetLayout, + const MipsAbiInfoHandler<ELFT> &abiInfo); + + void setELFHeader(ELFHeader<ELFT> &elfHeader); + + void finalizeMipsRuntimeAtomValues(); + + std::unique_ptr<RuntimeFile<ELFT>> createRuntimeFile(); + unique_bump_ptr<Section<ELFT>> + createOptionsSection(llvm::BumpPtrAllocator &alloc); + unique_bump_ptr<Section<ELFT>> + createAbiFlagsSection(llvm::BumpPtrAllocator &alloc); private: MipsLinkingContext &_ctx; MipsTargetLayout<ELFT> &_targetLayout; + const MipsAbiInfoHandler<ELFT> &_abiInfo; + + void setAtomValue(StringRef name, uint64_t value); +}; + +template <class ELFT> +class MipsDynamicLibraryWriter : public DynamicLibraryWriter<ELFT> { +public: + MipsDynamicLibraryWriter(MipsLinkingContext &ctx, + MipsTargetLayout<ELFT> &layout, + const MipsAbiInfoHandler<ELFT> &abiInfo); + +protected: + // Add any runtime files and their atoms to the output + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; + + void finalizeDefaultAtomValues() override; + void createDefaultSections() 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> &_targetLayout; + unique_bump_ptr<Section<ELFT>> _reginfo; + unique_bump_ptr<Section<ELFT>> _abiFlags; +}; + +template <class ELFT> +class MipsExecutableWriter : public ExecutableWriter<ELFT> { +public: + MipsExecutableWriter(MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &layout, + const MipsAbiInfoHandler<ELFT> &abiInfo); - void setAtomValue(StringRef name, uint64_t value) { - auto atom = _targetLayout.findAbsoluteAtom(name); - assert(atom != _targetLayout.absoluteAtoms().end()); - (*atom)->_virtualAddr = value; - } +protected: + void buildDynamicSymbolTable(const File &file) override; + + // Add any runtime files and their atoms to the output + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; + + void finalizeDefaultAtomValues() override; + void createDefaultSections() 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> &_targetLayout; + unique_bump_ptr<Section<ELFT>> _reginfo; + unique_bump_ptr<Section<ELFT>> _abiFlags; }; } // elf diff --git a/lib/ReaderWriter/ELF/Mips/MipsExecutableWriter.h b/lib/ReaderWriter/ELF/Mips/MipsExecutableWriter.h deleted file mode 100644 index 1a85bba3bd0f6..0000000000000 --- a/lib/ReaderWriter/ELF/Mips/MipsExecutableWriter.h +++ /dev/null @@ -1,154 +0,0 @@ -//===- 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 index 7bffcbeb5c085..b6cdd5c1487c3 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp +++ b/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp @@ -17,43 +17,46 @@ using namespace lld; using namespace lld::elf; std::unique_ptr<ELFLinkingContext> -MipsLinkingContext::create(llvm::Triple triple) { - if (triple.getArch() == llvm::Triple::mipsel || +elf::createMipsLinkingContext(llvm::Triple triple) { + if (triple.getArch() == llvm::Triple::mips || + triple.getArch() == llvm::Triple::mipsel || + triple.getArch() == llvm::Triple::mips64 || triple.getArch() == llvm::Triple::mips64el) - return std::unique_ptr<ELFLinkingContext>(new MipsLinkingContext(triple)); + return llvm::make_unique<MipsLinkingContext>(triple); return nullptr; } -typedef std::unique_ptr<TargetHandlerBase> TargetHandlerBasePtr; - -static TargetHandlerBasePtr createTarget(llvm::Triple triple, - MipsLinkingContext &ctx) { +static std::unique_ptr<TargetHandler> createTarget(llvm::Triple triple, + MipsLinkingContext &ctx) { switch (triple.getArch()) { + case llvm::Triple::mips: + return llvm::make_unique<MipsTargetHandler<ELF32BE>>(ctx); case llvm::Triple::mipsel: - return TargetHandlerBasePtr(new MipsTargetHandler<Mips32ELType>(ctx)); + return llvm::make_unique<MipsTargetHandler<ELF32LE>>(ctx); + case llvm::Triple::mips64: + return llvm::make_unique<MipsTargetHandler<ELF64BE>>(ctx); case llvm::Triple::mips64el: - return TargetHandlerBasePtr(new MipsTargetHandler<Mips64ELType>(ctx)); + return llvm::make_unique<MipsTargetHandler<ELF64LE>>(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; -} + : ELFLinkingContext(triple, createTarget(triple, *this)) {} uint64_t MipsLinkingContext::getBaseAddress() const { - if (_baseAddress == 0 && getOutputELFType() == llvm::ELF::ET_EXEC) - return getTriple().isArch64Bit() ? 0x120000000 : 0x400000; - return _baseAddress; + if (_baseAddress != 0 || getOutputELFType() != llvm::ELF::ET_EXEC) + return _baseAddress; + switch (getAbi()) { + case MipsAbi::O32: + return 0x0400000; + case MipsAbi::N32: + return 0x10000000; + case MipsAbi::N64: + return 0x120000000; + } + llvm_unreachable("unknown MIPS ABI flag"); } StringRef MipsLinkingContext::entrySymbolName() const { @@ -63,7 +66,15 @@ StringRef MipsLinkingContext::entrySymbolName() const { } StringRef MipsLinkingContext::getDefaultInterpreter() const { - return getTriple().isArch64Bit() ? "/lib64/ld.so.1" : "/lib/ld.so.1"; + switch (getAbi()) { + case MipsAbi::O32: + return "/lib/ld.so.1"; + case MipsAbi::N32: + return "/lib32/ld.so.1"; + case MipsAbi::N64: + return "/lib64/ld.so.1"; + } + llvm_unreachable("unknown MIPS ABI flag"); } void MipsLinkingContext::addPasses(PassManager &pm) { @@ -81,13 +92,14 @@ bool MipsLinkingContext::isDynamicRelocation(const Reference &r) const { switch (r.kindValue()) { case llvm::ELF::R_MIPS_COPY: case llvm::ELF::R_MIPS_REL32: + return true; 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; + return isDynamic(); default: return false; } @@ -113,3 +125,40 @@ bool MipsLinkingContext::isPLTRelocation(const Reference &r) const { return false; } } + +bool MipsLinkingContext::isRelativeReloc(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_REL32: + case llvm::ELF::R_MIPS_GPREL16: + case llvm::ELF::R_MIPS_GPREL32: + return true; + default: + return false; + } +} + +MipsAbi MipsLinkingContext::getAbi() const { + auto &handler = static_cast<MipsBaseTargetHandler &>(getTargetHandler()); + return handler.getAbi(); +} + +const Registry::KindStrings kindStrings[] = { +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), +#include "llvm/Support/ELFRelocs/Mips.def" +#undef ELF_RELOC + 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_STO_PLT), + LLD_KIND_STRING_ENTRY(LLD_R_MICROMIPS_GLOBAL_26_S1), + LLD_KIND_STRING_END +}; + +void MipsLinkingContext::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, + Reference::KindArch::Mips, kindStrings); +} diff --git a/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.h b/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.h index 824605f5fa7f2..414d2c785e172 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.h +++ b/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.h @@ -9,7 +9,7 @@ #ifndef LLD_READER_WRITER_ELF_MIPS_MIPS_LINKING_CONTEXT_H #define LLD_READER_WRITER_ELF_MIPS_MIPS_LINKING_CONTEXT_H -#include "MipsELFFlagsMerger.h" +#include "MipsAbiInfoHandler.h" #include "lld/ReaderWriter/ELFLinkingContext.h" namespace lld { @@ -23,10 +23,6 @@ enum { 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. @@ -35,20 +31,12 @@ enum { 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 + void registerRelocationNames(Registry &r) override; + int getMachineType() const override { return llvm::ELF::EM_MIPS; } uint64_t getBaseAddress() const override; StringRef entrySymbolName() const override; StringRef getDefaultInterpreter() const override; @@ -57,9 +45,9 @@ public: bool isDynamicRelocation(const Reference &r) const override; bool isCopyRelocation(const Reference &r) const override; bool isPLTRelocation(const Reference &r) const override; + bool isRelativeReloc(const Reference &r) const override; -private: - MipsELFFlagsMerger _flagsMerger; + MipsAbi getAbi() const; }; } // elf diff --git a/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp b/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp index 173ce0e6b1a87..c55a7a4116e68 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp +++ b/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp @@ -7,9 +7,10 @@ // //===----------------------------------------------------------------------===// -#include "MipsTargetHandler.h" #include "MipsLinkingContext.h" #include "MipsRelocationHandler.h" +#include "MipsTargetLayout.h" +#include "llvm/Support/Format.h" using namespace lld; using namespace elf; @@ -20,306 +21,265 @@ 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 + ToMicro, // cross isa jump to microMips symbol + ToMicroJalr// cross isa jump to microMips symbol referenced by R_MIPS_JALR }; +typedef std::function<std::error_code(int64_t, bool)> OverflowChecker; + +static std::error_code dummyCheck(int64_t, bool) { + return std::error_code(); +} + +template <int BITS> static std::error_code signedCheck(int64_t res, bool) { + if (llvm::isInt<BITS>(res)) + return std::error_code(); + return make_out_of_range_reloc_error(); +} + +template <int BITS> +static std::error_code gpDispCheck(int64_t res, bool isGpDisp) { + if (!isGpDisp || llvm::isInt<BITS>(res)) + return std::error_code(); + return make_out_of_range_reloc_error(); +} + 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 + OverflowChecker _overflow; // Check the relocation result }; -template <class ELFT> class RelocationHandler : public MipsRelocationHandler { +template <class ELFT> class RelocationHandler : public TargetRelocationHandler { public: - RelocationHandler(MipsLinkingContext &ctx) : _ctx(ctx) {} + RelocationHandler(MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &layout) + : _ctx(ctx), _targetLayout(layout) {} std::error_code applyRelocation(ELFWriter &writer, llvm::FileOutputBuffer &buf, - const lld::AtomLayout &atom, + const AtomLayout &atom, const Reference &ref) const override; - Reference::Addend readAddend(Reference::KindValue kind, - const uint8_t *content) const override; - private: MipsLinkingContext &_ctx; + MipsTargetLayout<ELFT> &_targetLayout; }; } static MipsRelocationParams getRelocationParams(uint32_t rType) { switch (rType) { case R_MIPS_NONE: - return {4, 0x0, 0, false}; + return {4, 0x0, 0, false, dummyCheck}; case R_MIPS_64: case R_MIPS_SUB: - return {8, 0xffffffffffffffffull, 0, false}; + return {8, 0xffffffffffffffffull, 0, false, dummyCheck}; + case R_MICROMIPS_SUB: + return {8, 0xffffffffffffffffull, 0, true, dummyCheck}; case R_MIPS_32: case R_MIPS_GPREL32: + case R_MIPS_REL32: case R_MIPS_PC32: - return {4, 0xffffffff, 0, false}; + case R_MIPS_EH: + return {4, 0xffffffff, 0, false, dummyCheck}; case LLD_R_MIPS_32_HI16: - return {4, 0xffff0000, 0, false}; + return {4, 0xffff0000, 0, false, dummyCheck}; case LLD_R_MIPS_64_HI16: - return {8, 0xffffffffffff0000ull, 0, false}; + return {8, 0xffffffffffff0000ull, 0, false, dummyCheck}; case R_MIPS_26: case LLD_R_MIPS_GLOBAL_26: - return {4, 0x3ffffff, 2, false}; + return {4, 0x3ffffff, 2, false, dummyCheck}; + case R_MIPS_PC16: + return {4, 0xffff, 2, false, signedCheck<18>}; case R_MIPS_PC18_S3: - return {4, 0x3ffff, 3, false}; + return {4, 0x3ffff, 3, false, signedCheck<21>}; case R_MIPS_PC19_S2: - return {4, 0x7ffff, 2, false}; + return {4, 0x7ffff, 2, false, signedCheck<21>}; case R_MIPS_PC21_S2: - return {4, 0x1fffff, 2, false}; + return {4, 0x1fffff, 2, false, signedCheck<23>}; case R_MIPS_PC26_S2: - return {4, 0x3ffffff, 2, false}; + return {4, 0x3ffffff, 2, false, signedCheck<28>}; case R_MIPS_HI16: + return {4, 0xffff, 0, false, gpDispCheck<16>}; case R_MIPS_LO16: + case R_MIPS_HIGHER: + case R_MIPS_HIGHEST: + return {4, 0xffff, 0, false, dummyCheck}; + case R_MIPS_16: case R_MIPS_PCHI16: case R_MIPS_PCLO16: - case R_MIPS_GPREL16: case R_MIPS_GOT16: + case R_MIPS_CALL16: case R_MIPS_GOT_DISP: case R_MIPS_GOT_PAGE: case R_MIPS_GOT_OFST: + case R_MIPS_GPREL16: + case R_MIPS_TLS_GD: + case R_MIPS_TLS_LDM: + case R_MIPS_TLS_GOTTPREL: + case R_MIPS_LITERAL: + return {4, 0xffff, 0, false, signedCheck<16>}; + case R_MIPS_GOT_HI16: + case R_MIPS_GOT_LO16: + case R_MIPS_CALL_HI16: + case R_MIPS_CALL_LO16: 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}; + return {4, 0xffff, 0, false, dummyCheck}; + case R_MICROMIPS_GPREL16: + case R_MICROMIPS_LITERAL: + return {4, 0xffff, 0, true, signedCheck<16>}; + case R_MICROMIPS_GPREL7_S2: + return {4, 0x7f, 2, false, signedCheck<9>}; + case R_MICROMIPS_GOT_HI16: + case R_MICROMIPS_GOT_LO16: + case R_MICROMIPS_CALL_HI16: + case R_MICROMIPS_CALL_LO16: 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}; + return {4, 0xffff, 0, true, dummyCheck}; case R_MICROMIPS_26_S1: case LLD_R_MICROMIPS_GLOBAL_26_S1: - return {4, 0x3ffffff, 1, true}; + return {4, 0x3ffffff, 1, true, dummyCheck}; case R_MICROMIPS_HI16: + return {4, 0xffff, 0, true, gpDispCheck<16>}; case R_MICROMIPS_LO16: - case R_MICROMIPS_GOT16: - return {4, 0xffff, 0, true}; + case R_MICROMIPS_HI0_LO16: + case R_MICROMIPS_HIGHER: + case R_MICROMIPS_HIGHEST: + return {4, 0xffff, 0, true, dummyCheck}; case R_MICROMIPS_PC16_S1: - return {4, 0xffff, 1, true}; + return {4, 0xffff, 1, true, signedCheck<17>}; case R_MICROMIPS_PC7_S1: - return {4, 0x7f, 1, false}; + return {4, 0x7f, 1, false, signedCheck<8>}; case R_MICROMIPS_PC10_S1: - return {4, 0x3ff, 1, false}; + return {4, 0x3ff, 1, false, signedCheck<11>}; 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}; + return {4, 0x7fffff, 2, true, signedCheck<25>}; + case R_MICROMIPS_PC18_S3: + return {4, 0x3ffff, 3, true, signedCheck<21>}; + case R_MICROMIPS_PC19_S2: + return {4, 0x7ffff, 2, true, signedCheck<21>}; + case R_MICROMIPS_PC21_S2: + return {4, 0x1fffff, 2, true, signedCheck<23>}; + case R_MICROMIPS_PC26_S2: + return {4, 0x3ffffff, 2, true, signedCheck<28>}; + case R_MICROMIPS_GOT16: 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_MICROMIPS_GOT_DISP: + case R_MICROMIPS_GOT_PAGE: + case R_MICROMIPS_GOT_OFST: + return {4, 0xffff, 0, true, signedCheck<16>}; case R_MIPS_JALR: - return {4, 0x0, 0, false}; + return {4, 0xffffffff, 0, false, dummyCheck}; case R_MICROMIPS_JALR: - return {4, 0x0, 0, true}; - case R_MIPS_REL32: + return {4, 0x0, 0, true, dummyCheck}; 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}; + return {4, 0xffffffff, 0, false, dummyCheck}; case R_MIPS_TLS_DTPMOD64: case R_MIPS_TLS_DTPREL64: case R_MIPS_TLS_TPREL64: - return {8, 0x0, 0, false}; + return {8, 0xffffffffffffffffull, 0, false, dummyCheck}; case LLD_R_MIPS_GLOBAL_GOT: case LLD_R_MIPS_STO_PLT: // Do nothing. - return {4, 0x0, 0, false}; + return {4, 0x0, 0, false, dummyCheck}; 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; -} +template <class ELFT> +static uint64_t relocRead(const MipsRelocationParams ¶ms, + const uint8_t *loc); -/// \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; +static int64_t getHi16(int64_t value) { + return ((value + 0x8000) >> 16) & 0xffff; } -/// \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; +static int64_t getHigher16(int64_t value) { + return ((value + 0x80008000ull) >> 32) & 0xffff; } -/// \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; +static int64_t getHighest16(int64_t value) { + return ((value + 0x800080008000ull) >> 48) & 0xffff; } -/// \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; +static int64_t maskLow16(int64_t value) { + return (value + 0x8000) & ~0xffff; } -/// R_MIPS_GOT_OFST +/// R_MIPS_GOT_OFST, R_MICROMIPS_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; +static int32_t relocGOTOfst(uint64_t S, int64_t A) { + int64_t page = maskLow16(S + A); 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 +/// \brief R_MIPS_PC16 /// 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; +static ErrorOr<int64_t> relocPc16(uint64_t P, uint64_t S, int64_t A) { + if ((S + A) & 3) + return make_unaligned_range_reloc_error(); + return S + A - P; } -/// \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_PC18_S3, R_MICROMIPS_PC18_S3 +/// local/external: (S + A - P) >> 3 (P with cleared 3 less significant bits) +static ErrorOr<int64_t> relocPc18(uint64_t P, uint64_t S, int64_t A) { + if ((S + A) & 6) + return make_unaligned_range_reloc_error(); + return S + A - ((P | 7) ^ 7); } -/// \brief R_MIPS_PC26_S2 +/// \brief R_MIPS_PC19_S2, R_MICROMIPS_PC19_S2, R_MIPS_PC21_S2, +/// R_MICROMIPS_PC21_S2, R_MIPS_PC26_S2, R_MICROMIPS_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; +static ErrorOr<int64_t> relocPcS2(uint64_t P, uint64_t S, int64_t A) { + if ((S + A) & 2) + return make_unaligned_range_reloc_error(); + return S + A - P; } -/// \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; +template <class ELFT> +static ErrorOr<int64_t> relocJalr(uint64_t P, uint64_t S, bool isCrossJump, + uint8_t *location) { + uint64_t ins = relocRead<ELFT>(getRelocationParams(R_MIPS_JALR), location); + if (isCrossJump) + return ins; + int64_t off = S - P - 4; + if (!llvm::isInt<18>(off)) + return ins; + if (ins == 0x0320f809) // jalr t9 + return 0x04110000 | ((off >> 2) & 0xffff); + if (ins == 0x03200008) // jr t9 + return 0x10000000 | ((off >> 2) & 0xffff); + return ins; +} + +static int64_t relocRel32(uint64_t S, int64_t A, bool isLocal) { + // If output relocation format is REL and the input one is RELA, the only + // method to transfer the relocation addend from the input relocation + // to the output dynamic relocation is to save this addend to the location + // modified by R_MIPS_REL32. + return isLocal ? S + A : A; } static std::error_code adjustJumpOpCode(uint64_t &ins, uint64_t tgt, CrossJumpMode mode) { - if (mode == CrossJumpMode::None) + if (mode == CrossJumpMode::None || mode == CrossJumpMode::ToMicroJalr) return std::error_code(); bool toMicro = mode == CrossJumpMode::ToMicro; @@ -327,8 +287,7 @@ static std::error_code adjustJumpOpCode(uint64_t &ins, uint64_t tgt, uint32_t opCross = toMicro ? 0x1d : 0x3c; if ((tgt & 1) != toMicro) - return make_dynamic_error_code( - Twine("Incorrect bit 0 for the jalx target")); + return make_dynamic_error_code("Incorrect bit 0 for the jalx target"); if (tgt & 2) return make_dynamic_error_code(Twine("The jalx target 0x") + @@ -356,6 +315,8 @@ static CrossJumpMode getCrossJumpMode(const Reference &ref) { return CrossJumpMode::None; bool isTgtMicro = isMicroMipsAtom(ref.target()); switch (ref.kindValue()) { + case R_MIPS_JALR: + return isTgtMicro ? CrossJumpMode::ToMicroJalr : CrossJumpMode::None; case R_MIPS_26: case LLD_R_MIPS_GLOBAL_26: return isTgtMicro ? CrossJumpMode::ToMicro : CrossJumpMode::None; @@ -367,44 +328,64 @@ static CrossJumpMode getCrossJumpMode(const Reference &ref) { } } -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; +template <class ELFT> +static ErrorOr<int64_t> +calculateRelocation(Reference::KindValue kind, Reference::Addend addend, + uint64_t tgtAddr, uint64_t relAddr, uint64_t gpAddr, + uint8_t *location, bool isGP, bool isCrossJump, + bool isDynamic, bool isLocalSym) { switch (kind) { case R_MIPS_NONE: return 0; + case R_MIPS_16: case R_MIPS_32: - return reloc32(tgtAddr, addend); case R_MIPS_64: - return reloc64(tgtAddr, addend); + case R_MIPS_TLS_DTPREL_LO16: + case R_MIPS_TLS_TPREL_LO16: + case R_MICROMIPS_TLS_DTPREL_LO16: + case R_MICROMIPS_TLS_TPREL_LO16: + case LLD_R_MIPS_GLOBAL_26: + case LLD_R_MICROMIPS_GLOBAL_26_S1: + return tgtAddr + addend; case R_MIPS_SUB: - return relocSub(tgtAddr, addend); + case R_MICROMIPS_SUB: + return tgtAddr - addend; case R_MIPS_26: - return reloc26loc(relAddr, tgtAddr, addend, 2); + return tgtAddr + (addend | (relAddr & 0xf0000000)); case R_MICROMIPS_26_S1: - return reloc26loc(relAddr, tgtAddr, addend, isCrossJump ? 2 : 1); + return tgtAddr + (addend | (relAddr & 0xf8000000)); case R_MIPS_HI16: case R_MICROMIPS_HI16: - return relocHi16(relAddr, tgtAddr, addend, isGP); + return getHi16(tgtAddr + addend - (isGP ? relAddr : 0)); case R_MIPS_PCHI16: - return relocPcHi16(relAddr, tgtAddr, addend); + return getHi16(tgtAddr + addend - relAddr); case R_MIPS_LO16: - return relocLo16(relAddr, tgtAddr, addend, isGP, false); - case R_MIPS_PCLO16: - return relocPcLo16(relAddr, tgtAddr, addend); + return tgtAddr + addend - (isGP ? relAddr - 4 : 0); case R_MICROMIPS_LO16: - return relocLo16(relAddr, tgtAddr, addend, isGP, true); + case R_MICROMIPS_HI0_LO16: + return tgtAddr + addend - (isGP ? relAddr - 3 : 0); + case R_MIPS_GOT_HI16: + case R_MIPS_CALL_HI16: + case R_MICROMIPS_GOT_HI16: + case R_MICROMIPS_CALL_HI16: + return getHi16(tgtAddr - gpAddr); + case R_MIPS_HIGHER: + case R_MICROMIPS_HIGHER: + return getHigher16(tgtAddr + addend); + case R_MIPS_HIGHEST: + case R_MICROMIPS_HIGHEST: + return getHighest16(tgtAddr + addend); + case R_MIPS_GOT_LO16: + case R_MIPS_CALL_LO16: + case R_MICROMIPS_GOT_LO16: + case R_MICROMIPS_CALL_LO16: + case R_MIPS_EH: case R_MIPS_GOT16: case R_MIPS_CALL16: case R_MIPS_GOT_DISP: case R_MIPS_GOT_PAGE: + case R_MICROMIPS_GOT_DISP: + case R_MICROMIPS_GOT_PAGE: case R_MICROMIPS_GOT16: case R_MICROMIPS_CALL16: case R_MIPS_TLS_GD: @@ -413,71 +394,66 @@ static ErrorOr<uint64_t> calculateRelocation(Reference::KindValue kind, case R_MICROMIPS_TLS_GD: case R_MICROMIPS_TLS_LDM: case R_MICROMIPS_TLS_GOTTPREL: - return relocGOT(tgtAddr, gpAddr); + return tgtAddr - gpAddr; + case R_MIPS_GPREL16: + case R_MIPS_GPREL32: + case R_MIPS_LITERAL: + case R_MICROMIPS_GPREL16: + case R_MICROMIPS_GPREL7_S2: + case R_MICROMIPS_LITERAL: + return tgtAddr + addend - gpAddr; case R_MIPS_GOT_OFST: + case R_MICROMIPS_GOT_OFST: return relocGOTOfst(tgtAddr, addend); + case R_MIPS_PC16: + return relocPc16(relAddr, tgtAddr, addend); case R_MIPS_PC18_S3: + case R_MICROMIPS_PC18_S3: return relocPc18(relAddr, tgtAddr, addend); case R_MIPS_PC19_S2: - return relocPc19(relAddr, tgtAddr, addend); + case R_MICROMIPS_PC19_S2: case R_MIPS_PC21_S2: - return relocPc21(relAddr, tgtAddr, addend); + case R_MICROMIPS_PC21_S2: case R_MIPS_PC26_S2: - return relocPc26(relAddr, tgtAddr, addend); + case R_MICROMIPS_PC26_S2: + return relocPcS2(relAddr, tgtAddr, addend); + case R_MIPS_PC32: + case R_MIPS_PCLO16: 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); + return tgtAddr + addend - relAddr; 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); + return getHi16(tgtAddr + addend); case R_MIPS_JALR: + return relocJalr<ELFT>(relAddr, tgtAddr, isCrossJump, location); case R_MICROMIPS_JALR: // We do not do JALR optimization now. return 0; case R_MIPS_REL32: + return relocRel32(tgtAddr, addend, isLocalSym); case R_MIPS_JUMP_SLOT: case R_MIPS_COPY: + // Ignore runtime relocations. + return 0; case R_MIPS_TLS_DTPMOD32: - case R_MIPS_TLS_DTPREL32: - case R_MIPS_TLS_TPREL32: case R_MIPS_TLS_DTPMOD64: + return isDynamic ? 0 : 1; + case R_MIPS_TLS_DTPREL32: case R_MIPS_TLS_DTPREL64: + return isDynamic ? 0 : tgtAddr + addend - 0x8000; + case R_MIPS_TLS_TPREL32: 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. + return isDynamic ? 0 : tgtAddr + addend - 0x7000; 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); + return maskLow16(tgtAddr + addend); case LLD_R_MIPS_STO_PLT: + case LLD_R_MIPS_GLOBAL_GOT: // Do nothing. return 0; default: @@ -488,27 +464,29 @@ static ErrorOr<uint64_t> calculateRelocation(Reference::KindValue kind, template <class ELFT> static uint64_t relocRead(const MipsRelocationParams ¶ms, const uint8_t *loc) { - uint64_t data; + assert((params._size == 4 || params._size == 8) && "Unexpected size"); + uint64_t data = 0; + memcpy(&data, loc, params._size); + if (params._shuffle) { + using namespace endian; + auto p = reinterpret_cast<const uint8_t *>(&data); + uint32_t a = readNext<uint16_t, ELFT::TargetEndianness, unaligned>(p); + uint32_t b = read<uint16_t, ELFT::TargetEndianness, unaligned>(p); + write<uint32_t, ELFT::TargetEndianness, unaligned>(&data, a << 16 | b); + } switch (params._size) { case 4: - data = endian::read<uint32_t, ELFT::TargetEndianness, unaligned>(loc); - break; + return endian::read<uint32_t, ELFT::TargetEndianness, unaligned>(&data); case 8: - data = endian::read<uint64_t, ELFT::TargetEndianness, unaligned>(loc); - break; + return endian::read<uint64_t, ELFT::TargetEndianness, unaligned>(&data); 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); @@ -519,24 +497,50 @@ static void relocWrite(uint64_t data, const MipsRelocationParams ¶ms, default: llvm_unreachable("Unexpected size"); } + if (params._shuffle) { + uint32_t v = endian::read<uint32_t, ELFT::TargetEndianness, unaligned>(loc); + uint16_t a = v >> 16; + uint16_t b = v & 0xffff; + endian::write<uint16_t, ELFT::TargetEndianness, unaligned>(loc, a); + endian::write<uint16_t, ELFT::TargetEndianness, unaligned>(loc + 2, b); + } +} + +static uint32_t getRelKind(const Reference &ref, size_t num) { + if (num == 0) + return ref.kindValue(); + if (num > 2) + return R_MIPS_NONE; + return (ref.tag() >> (8 * (num - 1))) & 0xff; +} + +static uint8_t getRelShift(Reference::KindValue kind, + const MipsRelocationParams ¶ms, + bool isCrossJump) { + uint8_t shift = params._shift; + if (isCrossJump && + (kind == R_MICROMIPS_26_S1 || kind == LLD_R_MICROMIPS_GLOBAL_26_S1)) + return 2; + return shift; +} + +static bool isLocalTarget(const Atom *a) { + if (auto *da = dyn_cast<DefinedAtom>(a)) + return da->scope() == Atom::scopeTranslationUnit; + return false; } template <class ELFT> std::error_code RelocationHandler<ELFT>::applyRelocation( - ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom, + ELFWriter &writer, llvm::FileOutputBuffer &buf, const AtomLayout &atom, const Reference &ref) const { - if (ref.kindNamespace() != lld::Reference::KindNamespace::ELF) + if (ref.kindNamespace() != 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; + uint64_t gpAddr = _targetLayout.getGPAddr(); + bool isGpDisp = ref.target()->name() == "_gp_disp"; + bool isLocalSym = isLocalTarget(ref.target()); uint8_t *atomContent = buf.getBufferStart() + atom._fileOffset; uint8_t *location = atomContent + ref.offsetInAtom(); @@ -547,26 +551,37 @@ std::error_code RelocationHandler<ELFT>::applyRelocation( 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); + bool isCrossJump = jumpMode != CrossJumpMode::None; + + uint64_t sym = tgtAddr; + ErrorOr<int64_t> res = ref.addend(); + Reference::KindValue lastRel = R_MIPS_NONE; + + for (size_t relNum = 0; relNum < 3; ++relNum) { + Reference::KindValue kind = getRelKind(ref, relNum); + if (kind == R_MIPS_NONE) + break; + auto params = getRelocationParams(kind); + res = calculateRelocation<ELFT>(kind, *res, sym, relAddr, gpAddr, location, + isGpDisp, isCrossJump, _ctx.isDynamic(), + isLocalSym); if (auto ec = res.getError()) return ec; + // Check result for the last relocation only. + if (getRelKind(ref, relNum + 1) == R_MIPS_NONE) { + if (auto ec = params._overflow(*res, isGpDisp)) + return ec; + } + res = *res >> getRelShift(kind, params, isCrossJump); + // FIXME (simon): Handle r_ssym value. + sym = 0; + isGpDisp = false; + isCrossJump = false; + lastRel = kind; } - auto params = getRelocationParams(op); + auto params = getRelocationParams(lastRel); uint64_t ins = relocRead<ELFT>(params, location); - if (auto ec = adjustJumpOpCode(ins, tgtAddr, jumpMode)) return ec; @@ -576,31 +591,97 @@ std::error_code RelocationHandler<ELFT>::applyRelocation( 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)); +createMipsRelocationHandler<ELF32BE>(MipsLinkingContext &ctx, + MipsTargetLayout<ELF32BE> &layout) { + return llvm::make_unique<RelocationHandler<ELF32BE>>(ctx, layout); } template <> std::unique_ptr<TargetRelocationHandler> -createMipsRelocationHandler<Mips64ELType>(MipsLinkingContext &ctx) { - return std::unique_ptr<TargetRelocationHandler>( - new RelocationHandler<Mips64ELType>(ctx)); +createMipsRelocationHandler<ELF32LE>(MipsLinkingContext &ctx, + MipsTargetLayout<ELF32LE> &layout) { + return llvm::make_unique<RelocationHandler<ELF32LE>>(ctx, layout); } +template <> +std::unique_ptr<TargetRelocationHandler> +createMipsRelocationHandler<ELF64BE>(MipsLinkingContext &ctx, + MipsTargetLayout<ELF64BE> &layout) { + return llvm::make_unique<RelocationHandler<ELF64BE>>(ctx, layout); +} + +template <> +std::unique_ptr<TargetRelocationHandler> +createMipsRelocationHandler<ELF64LE>(MipsLinkingContext &ctx, + MipsTargetLayout<ELF64LE> &layout) { + return llvm::make_unique<RelocationHandler<ELF64LE>>(ctx, layout); +} + +template <class ELFT> +Reference::Addend readMipsRelocAddend(Reference::KindValue kind, + const uint8_t *content) { + auto params = getRelocationParams(kind); + uint64_t ins = relocRead<ELFT>(params, content); + int64_t res = (ins & params._mask) << params._shift; + switch (kind) { + case R_MIPS_GPREL16: + case R_MICROMIPS_GPREL16: + case R_MIPS_PCLO16: + case R_MIPS_LITERAL: + case R_MICROMIPS_LITERAL: + return llvm::SignExtend32<16>(res); + case R_MIPS_PC16: + return llvm::SignExtend32<18>(res); + case R_MICROMIPS_GPREL7_S2: + return llvm::SignExtend32<9>(res); + case R_MICROMIPS_PC7_S1: + return llvm::SignExtend32<8>(res); + case R_MICROMIPS_PC10_S1: + return llvm::SignExtend32<11>(res); + case R_MIPS_16: + return llvm::SignExtend32<16>(res); + case R_MICROMIPS_PC16_S1: + return llvm::SignExtend32<17>(res); + case R_MIPS_PC18_S3: + case R_MIPS_PC19_S2: + case R_MICROMIPS_PC18_S3: + case R_MICROMIPS_PC19_S2: + return llvm::SignExtend32<21>(res); + case R_MIPS_PC21_S2: + case R_MICROMIPS_PC21_S2: + return llvm::SignExtend32<23>(res); + case R_MICROMIPS_PC23_S2: + return llvm::SignExtend32<25>(res); + case R_MICROMIPS_26_S1: + return llvm::SignExtend32<27>(res); + case R_MIPS_26: + case R_MIPS_PC26_S2: + case R_MICROMIPS_PC26_S2: + return llvm::SignExtend32<28>(res); + default: + // Nothing to do + break; + } + return res; +} + +template +Reference::Addend readMipsRelocAddend<ELF32BE>(Reference::KindValue kind, + const uint8_t *content); +template +Reference::Addend readMipsRelocAddend<ELF32LE>(Reference::KindValue kind, + const uint8_t *content); +template +Reference::Addend readMipsRelocAddend<ELF64BE>(Reference::KindValue kind, + const uint8_t *content); +template +Reference::Addend readMipsRelocAddend<ELF64LE>(Reference::KindValue kind, + const uint8_t *content); + } // elf } // lld diff --git a/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.h b/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.h index 87066b2b5c101..62a7aee34496b 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.h +++ b/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.h @@ -9,22 +9,22 @@ #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; -}; +class MipsLinkingContext; +template<typename ELFT> class MipsTargetLayout; template <class ELFT> std::unique_ptr<TargetRelocationHandler> -createMipsRelocationHandler(MipsLinkingContext &ctx); +createMipsRelocationHandler(MipsLinkingContext &ctx, + MipsTargetLayout<ELFT> &layout); +template <class ELFT> +Reference::Addend readMipsRelocAddend(Reference::KindValue kind, + const uint8_t *content); } // elf } // lld diff --git a/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.cpp b/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.cpp index a1b3530dfcdf6..b47c7d2210db3 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.cpp +++ b/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.cpp @@ -37,20 +37,69 @@ static const uint8_t mipsGotTlsGdAtomContent[] = { 0x00, 0x00, 0x00, 0x00 }; -// Regular PLT0 entry -static const uint8_t mipsPlt0AtomContent[] = { +// Regular big-endian PLT0 entry +static const uint8_t mipsBePlt0AtomContent[] = { + 0x3c, 0x1c, 0x00, 0x00, // lui $28, %hi(&GOTPLT[0]) + 0x8f, 0x99, 0x00, 0x00, // lw $25, %lo(&GOTPLT[0])($28) + 0x27, 0x9c, 0x00, 0x00, // addiu $28, $28, %lo(&GOTPLT[0]) + 0x03, 0x1c, 0xc0, 0x23, // subu $24, $24, $28 + 0x03, 0xe0, 0x78, 0x25, // move $15, $31 + 0x00, 0x18, 0xc0, 0x82, // srl $24, $24, 2 + 0x03, 0x20, 0xf8, 0x09, // jalr $25 + 0x27, 0x18, 0xff, 0xfe // subu $24, $24, 2 +}; + +// Regular little-endian PLT0 entry +static const uint8_t mipsLePlt0AtomContent[] = { 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 + 0x25, 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[] = { +// N32 big-endian PLT0 entry +static const uint8_t mipsN32BePlt0AtomContent[] = { + 0x3c, 0x0e, 0x00, 0x00, // lui $14, %hi(&GOTPLT[0]) + 0x8d, 0xd9, 0x00, 0x00, // lw $25, %lo(&GOTPLT[0])($14) + 0x25, 0xce, 0x00, 0x00, // addiu $14, $14, %lo(&GOTPLT[0]) + 0x03, 0x0e, 0xc0, 0x23, // subu $24, $24, $14 + 0x03, 0xe0, 0x78, 0x25, // move $15, $31 + 0x00, 0x18, 0xc0, 0x82, // srl $24, $24, 2 + 0x03, 0x20, 0xf8, 0x09, // jalr $25 + 0x27, 0x18, 0xff, 0xfe // subu $24, $24, 2 +}; + +// N32 little-endian PLT0 entry +static const uint8_t mipsN32LePlt0AtomContent[] = { + 0x00, 0x00, 0x0e, 0x3c, // lui $14, %hi(&GOTPLT[0]) + 0x00, 0x00, 0xd9, 0x8d, // lw $25, %lo(&GOTPLT[0])($14) + 0x00, 0x00, 0xce, 0x25, // addiu $14, $14, %lo(&GOTPLT[0]) + 0x23, 0xc0, 0x0e, 0x03, // subu $24, $24, $14 + 0x25, 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 big-endian PLT0 entry +static const uint8_t microMipsBePlt0AtomContent[] = { + 0x79, 0x80, 0x00, 0x00, // addiupc $3, (&GOTPLT[0]) - . + 0xff, 0x23, 0x00, 0x00, // lw $25, 0($3) + 0x05, 0x35, // subu $2, $2, $3 + 0x25, 0x25, // srl $2, $2, 2 + 0x33, 0x02, 0xff, 0xfe, // subu $24, $2, 2 + 0x0d, 0xff, // move $15, $31 + 0x45, 0xf9, // jalrs $25 + 0x0f, 0x83, // move $28, $3 + 0x0c, 0x00 // nop +}; + +// microMIPS little-endian PLT0 entry +static const uint8_t microMipsLePlt0AtomContent[] = { 0x80, 0x79, 0x00, 0x00, // addiupc $3, (&GOTPLT[0]) - . 0x23, 0xff, 0x00, 0x00, // lw $25, 0($3) 0x35, 0x05, // subu $2, $2, $3 @@ -62,40 +111,80 @@ static const uint8_t micromipsPlt0AtomContent[] = { 0x00, 0x0c // nop }; -// Regular PLT entry -static const uint8_t mipsPltAAtomContent[] = { +// Regular big-endian PLT entry +static const uint8_t mipsBePltAAtomContent[] = { + 0x3c, 0x0f, 0x00, 0x00, // lui $15, %hi(.got.plt entry) + 0x8d, 0xf9, 0x00, 0x00, // l[wd] $25, %lo(.got.plt entry)($15) + 0x03, 0x20, 0x00, 0x08, // jr $25 + 0x25, 0xf8, 0x00, 0x00 // addiu $24, $15, %lo(.got.plt entry) +}; + +// Regular little-endian PLT entry +static const uint8_t mipsLePltAAtomContent[] = { 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[] = { +// microMIPS big-endian PLT entry +static const uint8_t microMipsBePltAAtomContent[] = { + 0x79, 0x00, 0x00, 0x00, // addiupc $2, (.got.plt entry) - . + 0xff, 0x22, 0x00, 0x00, // lw $25, 0($2) + 0x45, 0x99, // jr $25 + 0x0f, 0x02 // move $24, $2 +}; + +// microMIPS little-endian PLT entry +static const uint8_t microMipsLePltAAtomContent[] = { 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[] = { +// R6 big-endian PLT entry +static const uint8_t mipsR6BePltAAtomContent[] = { + 0x3c, 0x0f, 0x00, 0x00, // lui $15, %hi(.got.plt entry) + 0x8d, 0xf9, 0x00, 0x00, // l[wd] $25, %lo(.got.plt entry)($15) + 0x03, 0x20, 0x00, 0x09, // jr $25 + 0x25, 0xf8, 0x00, 0x00 // addiu $24, $15, %lo(.got.plt entry) +}; + +// R6 little-endian PLT entry +static const uint8_t mipsR6LePltAAtomContent[] = { 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[] = { +// LA25 big-endian stub entry +static const uint8_t mipsBeLA25AtomContent[] = { + 0x3c, 0x19, 0x00, 0x00, // lui $25, %hi(func) + 0x08, 0x00, 0x00, 0x00, // j func + 0x27, 0x39, 0x00, 0x00, // addiu $25, $25, %lo(func) + 0x00, 0x00, 0x00, 0x00 // nop +}; + +// LA25 little-endian stub entry +static const uint8_t mipsLeLA25AtomContent[] = { 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[] = { +// microMIPS LA25 big-endian stub entry +static const uint8_t microMipsBeLA25AtomContent[] = { + 0x41, 0xbe, 0x00, 0x00, // lui $25, %hi(func) + 0xd4, 0x00, 0x00, 0x00, // j func + 0x33, 0x39, 0x00, 0x00, // addiu $25, $25, %lo(func) + 0x00, 0x00, 0x00, 0x00 // nop +}; + +// microMIPS LA25 little-endian stub entry +static const uint8_t microMipsLeLA25AtomContent[] = { 0xb9, 0x41, 0x00, 0x00, // lui $25, %hi(func) 0x00, 0xd4, 0x00, 0x00, // j func 0x39, 0x33, 0x00, 0x00, // addiu $25, $25, %lo(func) @@ -109,7 +198,7 @@ class MipsGOTAtom : public GOTAtom { public: MipsGOTAtom(const File &f) : GOTAtom(f, ".got") {} - Alignment alignment() const override { return Alignment(2); } + Alignment alignment() const override { return 4; } }; /// \brief MIPS GOT entry initialized by zero. @@ -120,10 +209,16 @@ public: ArrayRef<uint8_t> rawContent() const override; }; -template <> ArrayRef<uint8_t> GOT0Atom<Mips32ELType>::rawContent() const { +template <> ArrayRef<uint8_t> GOT0Atom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(mipsGot0AtomContent).slice(4); +} +template <> ArrayRef<uint8_t> GOT0Atom<ELF32LE>::rawContent() const { return llvm::makeArrayRef(mipsGot0AtomContent).slice(4); } -template <> ArrayRef<uint8_t> GOT0Atom<Mips64ELType>::rawContent() const { +template <> ArrayRef<uint8_t> GOT0Atom<ELF64BE>::rawContent() const { + return llvm::makeArrayRef(mipsGot0AtomContent); +} +template <> ArrayRef<uint8_t> GOT0Atom<ELF64LE>::rawContent() const { return llvm::makeArrayRef(mipsGot0AtomContent); } @@ -136,11 +231,19 @@ public: }; template <> -ArrayRef<uint8_t> GOTModulePointerAtom<Mips32ELType>::rawContent() const { +ArrayRef<uint8_t> GOTModulePointerAtom<ELF32BE>::rawContent() const { return llvm::makeArrayRef(mipsGotModulePointerAtomContent).slice(4); } template <> -ArrayRef<uint8_t> GOTModulePointerAtom<Mips64ELType>::rawContent() const { +ArrayRef<uint8_t> GOTModulePointerAtom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(mipsGotModulePointerAtomContent).slice(4); +} +template <> +ArrayRef<uint8_t> GOTModulePointerAtom<ELF64BE>::rawContent() const { + return llvm::makeArrayRef(mipsGotModulePointerAtomContent); +} +template <> +ArrayRef<uint8_t> GOTModulePointerAtom<ELF64LE>::rawContent() const { return llvm::makeArrayRef(mipsGotModulePointerAtomContent); } @@ -152,12 +255,17 @@ public: 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<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(mipsGotTlsGdAtomContent).slice(8); } - -template <> ArrayRef<uint8_t> GOTTLSGdAtom<Mips64ELType>::rawContent() const { - return llvm::makeArrayRef(mipsGotTlsGdAtomContent); +template <> ArrayRef<uint8_t> GOTTLSGdAtom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(mipsGotTlsGdAtomContent).slice(8); +} +template <> ArrayRef<uint8_t> GOTTLSGdAtom<ELF64BE>::rawContent() const { + return llvm::makeArrayRef(mipsGotTlsGdAtomContent); +} +template <> ArrayRef<uint8_t> GOTTLSGdAtom<ELF64LE>::rawContent() const { + return llvm::makeArrayRef(mipsGotTlsGdAtomContent); } class GOTPLTAtom : public GOTAtom { @@ -173,28 +281,56 @@ public: addReferenceELF_Mips(R_MIPS_32, 0, plt0, 0); } - Alignment alignment() const override { return Alignment(2); } + Alignment alignment() const override { return 4; } ArrayRef<uint8_t> rawContent() const override { return llvm::makeArrayRef(mipsGot0AtomContent).slice(4); } }; -class PLT0Atom : public PLTAtom { +template <class ELFT> 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); + addReferenceELF_Mips(R_MIPS_HI16, 0, got, 0); + addReferenceELF_Mips(R_MIPS_LO16, 4, got, 0); + addReferenceELF_Mips(R_MIPS_LO16, 8, got, 0); + } + + ArrayRef<uint8_t> rawContent() const override { + llvm_unreachable("PLT0 is not applicable for this target"); + } +}; + +template <> ArrayRef<uint8_t> PLT0Atom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(mipsBePlt0AtomContent); +} +template <> ArrayRef<uint8_t> PLT0Atom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(mipsLePlt0AtomContent); +} + +template <class ELFT> class PLT0N32Atom : public PLTAtom { +public: + PLT0N32Atom(const Atom *got, const File &f) : PLTAtom(f, ".plt") { + // Setup reference to fixup the PLT0 entry. + addReferenceELF_Mips(R_MIPS_HI16, 0, got, 0); + addReferenceELF_Mips(R_MIPS_LO16, 4, got, 0); + addReferenceELF_Mips(R_MIPS_LO16, 8, got, 0); } ArrayRef<uint8_t> rawContent() const override { - return llvm::makeArrayRef(mipsPlt0AtomContent); + llvm_unreachable("PLT0 is not applicable for this target"); } }; -class PLT0MicroAtom : public PLTAtom { +template <> ArrayRef<uint8_t> PLT0N32Atom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(mipsN32BePlt0AtomContent); +} +template <> ArrayRef<uint8_t> PLT0N32Atom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(mipsN32LePlt0AtomContent); +} + +template <class ELFT> class PLT0MicroAtom : public PLTAtom { public: PLT0MicroAtom(const Atom *got, const File &f) : PLTAtom(f, ".plt") { // Setup reference to fixup the PLT0 entry. @@ -204,54 +340,87 @@ public: CodeModel codeModel() const override { return codeMipsMicro; } ArrayRef<uint8_t> rawContent() const override { - return llvm::makeArrayRef(micromipsPlt0AtomContent); + llvm_unreachable("PLT0 is not applicable for this target"); } }; +template <> ArrayRef<uint8_t> PLT0MicroAtom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(microMipsBePlt0AtomContent); +} +template <> ArrayRef<uint8_t> PLT0MicroAtom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(microMipsLePlt0AtomContent); +} + 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); + addReferenceELF_Mips(R_MIPS_HI16, 0, got, 0); + addReferenceELF_Mips(R_MIPS_LO16, 4, got, 0); + addReferenceELF_Mips(R_MIPS_LO16, 12, got, 0); } +}; + +template <class ELFT> class PLTARegAtom : public PLTAAtom { +public: + PLTARegAtom(const GOTPLTAtom *got, const File &f) : PLTAAtom(got, f) {} ArrayRef<uint8_t> rawContent() const override { - return llvm::makeArrayRef(mipsPltAAtomContent); + llvm_unreachable("PLT is not applicable for this target"); } }; -class PLTR6Atom : public PLTAAtom { +template <> ArrayRef<uint8_t> PLTARegAtom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(mipsBePltAAtomContent); +} +template <> ArrayRef<uint8_t> PLTARegAtom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(mipsLePltAAtomContent); +} + +template <class ELFT> 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); + llvm_unreachable("PLT is not applicable for this target"); } }; -class PLTMicroAtom : public PLTAtom { +template <> ArrayRef<uint8_t> PLTR6Atom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(mipsR6BePltAAtomContent); +} +template <> ArrayRef<uint8_t> PLTR6Atom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(mipsR6LePltAAtomContent); +} + +template <class ELFT> 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); } + Alignment alignment() const override { return 2; } CodeModel codeModel() const override { return codeMipsMicro; } ArrayRef<uint8_t> rawContent() const override { - return llvm::makeArrayRef(micromipsPltAtomContent); + llvm_unreachable("PLT is not applicable for this target"); } }; +template <> ArrayRef<uint8_t> PLTMicroAtom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(microMipsBePltAAtomContent); +} +template <> ArrayRef<uint8_t> PLTMicroAtom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(microMipsLePltAAtomContent); +} + class LA25Atom : public PLTAtom { public: LA25Atom(const File &f) : PLTAtom(f, ".text") {} }; -class LA25RegAtom : public LA25Atom { +template <typename ELFT> class LA25RegAtom : public LA25Atom { public: LA25RegAtom(const Atom *a, const File &f) : LA25Atom(f) { // Setup reference to fixup the LA25 stub entry. @@ -261,11 +430,18 @@ public: } ArrayRef<uint8_t> rawContent() const override { - return llvm::makeArrayRef(mipsLA25AtomContent); + llvm_unreachable("LA25 stubs are not applicable for this target"); } }; -class LA25MicroAtom : public LA25Atom { +template <> ArrayRef<uint8_t> LA25RegAtom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(mipsBeLA25AtomContent); +} +template <> ArrayRef<uint8_t> LA25RegAtom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(mipsLeLA25AtomContent); +} + +template <typename ELFT> class LA25MicroAtom : public LA25Atom { public: LA25MicroAtom(const Atom *a, const File &f) : LA25Atom(f) { // Setup reference to fixup the microMIPS LA25 stub entry. @@ -277,7 +453,39 @@ public: CodeModel codeModel() const override { return codeMipsMicro; } ArrayRef<uint8_t> rawContent() const override { - return llvm::makeArrayRef(micromipsLA25AtomContent); + llvm_unreachable("LA25 stubs are not applicable for this target"); + } +}; + +template <> ArrayRef<uint8_t> LA25MicroAtom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(microMipsBeLA25AtomContent); +} +template <> ArrayRef<uint8_t> LA25MicroAtom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(microMipsLeLA25AtomContent); +} + +class MipsGlobalOffsetTableAtom : public GlobalOffsetTableAtom { +public: + MipsGlobalOffsetTableAtom(const File &f) : GlobalOffsetTableAtom(f) {} + + StringRef customSectionName() const override { return ".got"; } +}; + +template <typename ELFT> class MipsRldAtom : public SimpleELFDefinedAtom { +public: + MipsRldAtom(const File &f) : SimpleELFDefinedAtom(f) {} + + Scope scope() const override { return scopeGlobal; } + SectionChoice sectionChoice() const override { return sectionCustomRequired; } + StringRef customSectionName() const override { return ".rld_map"; } + ContentType contentType() const override { return typeData; } + uint64_t size() const override { return rawContent().size(); } + ContentPermissions permissions() const override { return permRW_; } + Alignment alignment() const override { return rawContent().size(); } + StringRef name() const override { return "__RLD_MAP"; } + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(mipsGot0AtomContent) + .slice(ELFT::Is64Bits ? 0 : 4); } }; @@ -295,7 +503,7 @@ template <typename ELFT> class RelocationPass : public Pass { public: RelocationPass(MipsLinkingContext &ctx); - void perform(std::unique_ptr<MutableFile> &mf) override; + std::error_code perform(SimpleFile &mf) override; private: /// \brief Reference to the linking context. @@ -319,7 +527,7 @@ private: llvm::DenseMap<const Atom *, GOTAtom *> _gotTLSGdMap; /// \brief GOT entry for the R_xxxMIPS_TLS_LDM relocations. - GOTTLSGdAtom<ELFT> *_gotLDMEntry; + GOTTLSGdAtom<ELFT> *_gotLDMEntry = nullptr; /// \brief the list of local GOT atoms. std::vector<GOTAtom *> _localGotVector; @@ -335,14 +543,14 @@ private: /// \brief Map Atoms to their PLT entries. llvm::DenseMap<const Atom *, PLTAAtom *> _pltRegMap; - llvm::DenseMap<const Atom *, PLTMicroAtom *> _pltMicroMap; + llvm::DenseMap<const Atom *, PLTMicroAtom<ELFT> *> _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; + llvm::DenseMap<const Atom *, LA25Atom *> _la25RegMap; + llvm::DenseMap<const Atom *, LA25Atom *> _la25MicroMap; /// \brief Atoms referenced by static relocations. llvm::DenseSet<const Atom *> _hasStaticRelocations; @@ -372,24 +580,27 @@ private: /// \brief Collect information about the reference to use it /// later in the handleReference() routine. - void collectReferenceInfo(const MipsELFDefinedAtom<ELFT> &atom, - Reference &ref); + std::error_code collectReferenceInfo(const MipsELFDefinedAtom<ELFT> &atom, + Reference &ref); + + /// \brief Check that the relocation is valid for the current linking mode. + std::error_code validateRelocation(const DefinedAtom &atom, + const Reference &ref) const; void handlePlain(const MipsELFDefinedAtom<ELFT> &atom, Reference &ref); - void handle26(const MipsELFDefinedAtom<ELFT> &atom, Reference &ref); + void handleBranch(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 *getTLSGOTEntry(const Atom *a, Reference::Addend addend); + const GOTAtom *getTLSGdGOTEntry(const Atom *a, Reference::Addend addend); 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); @@ -399,40 +610,47 @@ private: 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 requireLA25Stub(const MipsELFDefinedAtom<ELFT> &atom, + const Reference &ref) 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; + /// \brief Linked files contain microMIPS code. + bool isMicroMips(); + /// \brief Linked files contain MIPS R6 code. + bool isMipsR6(); }; template <typename ELFT> RelocationPass<ELFT>::RelocationPass(MipsLinkingContext &ctx) - : _ctx(ctx), _file(ctx), _gotLDMEntry(nullptr) { + : _ctx(ctx), _file(ctx) { _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)); +std::error_code RelocationPass<ELFT>::perform(SimpleFile &mf) { + for (const auto &atom : mf.defined()) + for (const auto &ref : *atom) { + const auto &da = *cast<MipsELFDefinedAtom<ELFT>>(atom); + if (auto ec = collectReferenceInfo(da, const_cast<Reference &>(*ref))) + return ec; + } // Process all references. - for (const auto &atom : mf->defined()) + 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())) + bool forceRel = isLocal(ref->target()) && _ctx.getOutputELFType() == ET_DYN; + if (!forceRel && (!isDynamic(ref->target()) || hasPLTEntry(ref->target()))) continue; ref->setKindValue(R_MIPS_REL32); if (ELFT::Is64Bits) @@ -443,19 +661,32 @@ void RelocationPass<ELFT>::perform(std::unique_ptr<MutableFile> &mf) { uint64_t ordinal = 0; + if (_ctx.isDynamic() && _ctx.getOutputELFType() == ET_EXEC) { + auto rlda = new (_file._alloc) MipsRldAtom<ELFT>(_file); + rlda->setOrdinal(ordinal++); + mf.addAtom(*rlda); + } + + if (!_localGotVector.empty() || !_globalGotVector.empty() || + !_tlsGotVector.empty()) { + SimpleDefinedAtom *ga = new (_file._alloc) MipsGlobalOffsetTableAtom(_file); + ga->setOrdinal(ordinal++); + mf.addAtom(*ga); + } + for (auto &got : _localGotVector) { got->setOrdinal(ordinal++); - mf->addAtom(*got); + mf.addAtom(*got); } for (auto &got : _globalGotVector) { got->setOrdinal(ordinal++); - mf->addAtom(*got); + mf.addAtom(*got); } for (auto &got : _tlsGotVector) { got->setOrdinal(ordinal++); - mf->addAtom(*got); + mf.addAtom(*got); } // Create and emit PLT0 entry. @@ -467,19 +698,19 @@ void RelocationPass<ELFT>::perform(std::unique_ptr<MutableFile> &mf) { if (plt0Atom) { plt0Atom->setOrdinal(ordinal++); - mf->addAtom(*plt0Atom); + mf.addAtom(*plt0Atom); } // Emit regular PLT entries firts. for (auto &plt : _pltRegVector) { plt->setOrdinal(ordinal++); - mf->addAtom(*plt); + mf.addAtom(*plt); } // microMIPS PLT entries come after regular ones. for (auto &plt : _pltMicroVector) { plt->setOrdinal(ordinal++); - mf->addAtom(*plt); + mf.addAtom(*plt); } // Assign PLT0 to GOTPLT entries. @@ -489,18 +720,96 @@ void RelocationPass<ELFT>::perform(std::unique_ptr<MutableFile> &mf) { for (auto &gotplt : _gotpltVector) { gotplt->setOrdinal(ordinal++); - mf->addAtom(*gotplt); + mf.addAtom(*gotplt); } for (auto obj : _objectVector) { obj->setOrdinal(ordinal++); - mf->addAtom(*obj); + mf.addAtom(*obj); } for (auto la25 : _la25Vector) { la25->setOrdinal(ordinal++); - mf->addAtom(*la25); + mf.addAtom(*la25); } + + return std::error_code(); +} + +static bool isMicroMipsReloc(Reference::KindValue kind) { + return R_MICROMIPS_26_S1 <= kind && kind <= R_MICROMIPS_PC19_S2; +} + +static bool isHiLo16Reloc(Reference::KindValue kind) { + return kind == R_MIPS_HI16 || kind == R_MIPS_LO16 || kind == R_MIPS_PCHI16 || + kind == R_MIPS_PCLO16 || kind == R_MICROMIPS_HI16 || + kind == R_MICROMIPS_LO16 || kind == R_MICROMIPS_HI0_LO16; +} + +static bool isBranchReloc(Reference::KindValue kind) { + return kind == R_MIPS_26 || kind == R_MICROMIPS_26_S1 || + kind == R_MIPS_PC16 || kind == R_MIPS_PC21_S2 || + kind == R_MIPS_PC26_S2 || kind == R_MICROMIPS_PC7_S1 || + kind == R_MICROMIPS_PC10_S1 || kind == R_MICROMIPS_PC16_S1 || + kind == R_MICROMIPS_PC23_S2; +} + +static bool isGotReloc(Reference::KindValue kind) { + return kind == R_MIPS_GOT16 || kind == R_MICROMIPS_GOT16; +} + +static bool isAllGotReloc(Reference::KindValue kind) { + return isGotReloc(kind) || kind == R_MIPS_GOT_HI16 || + kind == R_MIPS_GOT_LO16 || kind == R_MICROMIPS_GOT_HI16 || + kind == R_MICROMIPS_GOT_LO16; +} + +static bool isCallReloc(Reference::KindValue kind) { + return kind == R_MIPS_CALL16 || kind == R_MICROMIPS_CALL16; +} + +static bool isAllCallReloc(Reference::KindValue kind) { + return isCallReloc(kind) || kind == R_MIPS_CALL_HI16 || + kind == R_MIPS_CALL_LO16 || kind == R_MICROMIPS_CALL_HI16 || + kind == R_MICROMIPS_CALL_LO16; +} + +static bool isGotDispReloc(Reference::KindValue kind) { + return kind == R_MIPS_GOT_DISP || kind == R_MICROMIPS_GOT_DISP; +} + +static bool isGotPageReloc(Reference::KindValue kind) { + return kind == R_MIPS_GOT_PAGE || kind == R_MICROMIPS_GOT_PAGE; +} + +static bool isTlsDtpReloc(Reference::KindValue kind) { + return kind == R_MIPS_TLS_DTPREL_HI16 || kind == R_MIPS_TLS_DTPREL_LO16 || + kind == R_MICROMIPS_TLS_DTPREL_HI16 || + kind == R_MICROMIPS_TLS_DTPREL_LO16; +} + +static bool isTlsTpReloc(Reference::KindValue kind) { + return kind == R_MIPS_TLS_TPREL_HI16 || kind == R_MIPS_TLS_TPREL_LO16 || + kind == R_MICROMIPS_TLS_TPREL_HI16 || + kind == R_MICROMIPS_TLS_TPREL_LO16; +} + +static bool isTlsGdReloc(Reference::KindValue kind) { + return kind == R_MIPS_TLS_GD || kind == R_MICROMIPS_TLS_GD; +} + +static bool isTlsLdmReloc(Reference::KindValue kind) { + return kind == R_MIPS_TLS_LDM || kind == R_MICROMIPS_TLS_LDM; +} + +static bool isTlsGotTpReloc(Reference::KindValue kind) { + return kind == R_MIPS_TLS_GOTTPREL || kind == R_MICROMIPS_TLS_GOTTPREL; +} + +static bool isGpRelReloc(Reference::KindValue kind) { + return kind == R_MIPS_GPREL32 || kind == R_MIPS_GPREL16 || + kind == R_MICROMIPS_GPREL16 || kind == R_MICROMIPS_GPREL7_S2 || + kind == R_MIPS_LITERAL || kind == R_MICROMIPS_LITERAL; } template <typename ELFT> @@ -508,67 +817,33 @@ void RelocationPass<ELFT>::handleReference(const MipsELFDefinedAtom<ELFT> &atom, Reference &ref) { if (!ref.target()) return; - if (ref.kindNamespace() != lld::Reference::KindNamespace::ELF) + if (ref.kindNamespace() != 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. + Reference::KindValue kind = ref.kindValue(); + if (isHiLo16Reloc(kind) || kind == R_MIPS_32 || kind == R_MIPS_PC32) 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: + else if (isBranchReloc(kind)) + handleBranch(atom, ref); + else if (isAllGotReloc(kind) || isAllCallReloc(kind) || + isGotDispReloc(kind) || isGotPageReloc(kind) || kind == R_MIPS_EH) 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: + else if (isTlsDtpReloc(kind)) 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: + else if (isTlsTpReloc(kind)) 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: + else if (isTlsGdReloc(kind)) + ref.setTarget(getTLSGdGOTEntry(ref.target(), ref.addend())); + else if (isTlsLdmReloc(kind)) ref.setTarget(getTLSLdmGOTEntry(ref.target())); - break; - case R_MIPS_TLS_GOTTPREL: - case R_MICROMIPS_TLS_GOTTPREL: - ref.setTarget(getTLSGOTEntry(ref.target())); - break; + else if (isTlsGotTpReloc(kind)) + ref.setTarget(getTLSGOTEntry(ref.target(), ref.addend())); + else if (kind == R_MIPS_GPREL32 || (isLocal(ref.target()) && isGpRelReloc(kind))) + ref.setAddend(ref.addend() + atom.file().getGP0()); + else if (kind == R_MIPS_JALR) { + if (_ctx.getOutputELFType() != ET_EXEC || !isLocalCall(ref.target())) + ref.setKindValue(R_MIPS_NONE); } } @@ -583,6 +858,10 @@ static bool isConstrainSym(const MipsELFDefinedAtom<ELFT> &atom, case R_MICROMIPS_JALR: case R_MIPS_GPREL16: case R_MIPS_GPREL32: + case R_MICROMIPS_GPREL16: + case R_MICROMIPS_GPREL7_S2: + case R_MIPS_LITERAL: + case R_MICROMIPS_LITERAL: return false; default: return true; @@ -590,25 +869,96 @@ static bool isConstrainSym(const MipsELFDefinedAtom<ELFT> &atom, } template <typename ELFT> -void RelocationPass<ELFT>::collectReferenceInfo( - const MipsELFDefinedAtom<ELFT> &atom, Reference &ref) { +std::error_code +RelocationPass<ELFT>::collectReferenceInfo(const MipsELFDefinedAtom<ELFT> &atom, + Reference &ref) { if (!ref.target()) - return; - if (ref.kindNamespace() != lld::Reference::KindNamespace::ELF) - return; + return std::error_code(); + if (ref.kindNamespace() != Reference::KindNamespace::ELF) + return std::error_code(); auto refKind = ref.kindValue(); + if (refKind == R_MIPS_EH && this->_ctx.mipsPcRelEhRel()) + ref.setKindValue(R_MIPS_PC32); + + if (auto ec = validateRelocation(atom, ref)) + return ec; + if (!isConstrainSym(atom, refKind)) - return; + return std::error_code(); - if (mightBeDynamic(atom, refKind)) - _rel32Candidates.push_back(&ref); - else + if (!mightBeDynamic(atom, refKind)) _hasStaticRelocations.insert(ref.target()); + else if (refKind == R_MIPS_32 || refKind == R_MIPS_64) + _rel32Candidates.push_back(&ref); - if (refKind != R_MIPS_CALL16 && refKind != R_MICROMIPS_CALL16 && - refKind != R_MIPS_26 && refKind != R_MICROMIPS_26_S1) + if (!isBranchReloc(refKind) && !isAllCallReloc(refKind) && + refKind != R_MIPS_EH) _requiresPtrEquality.insert(ref.target()); + + return std::error_code(); +} + +static std::error_code +make_reject_for_shared_lib_reloc_error(const ELFLinkingContext &ctx, + const DefinedAtom &atom, + const Reference &ref) { + StringRef kindValStr = "unknown"; + ctx.registry().referenceKindToString(ref.kindNamespace(), ref.kindArch(), + ref.kindValue(), kindValStr); + + return make_dynamic_error_code(Twine(kindValStr) + " (" + + Twine(ref.kindValue()) + + ") relocation cannot be used " + "when making a shared object, recompile " + + atom.file().path() + " with -fPIC"); +} + +static std::error_code +make_local_call16_reloc_error(const ELFLinkingContext &ctx, + const DefinedAtom &atom, const Reference &ref) { + return make_dynamic_error_code("R_MIPS_CALL16 (11) relocation cannot be used " + "against local symbol " + + ref.target()->name() + " in file " + + atom.file().path()); +} + +template <typename ELFT> +std::error_code +RelocationPass<ELFT>::validateRelocation(const DefinedAtom &atom, + const Reference &ref) const { + if (!ref.target()) + return std::error_code(); + + if (isCallReloc(ref.kindValue()) && isLocal(ref.target())) + return make_local_call16_reloc_error(this->_ctx, atom, ref); + + if (this->_ctx.getOutputELFType() != ET_DYN) + return std::error_code(); + + switch (ref.kindValue()) { + case R_MIPS16_HI16: + case R_MIPS_HI16: + case R_MIPS_HIGHER: + case R_MIPS_HIGHEST: + case R_MICROMIPS_HI16: + case R_MICROMIPS_HIGHER: + case R_MICROMIPS_HIGHEST: + // For shared object we accepts "high" relocations + // against the "_gp_disp" symbol only. + if (ref.target()->name() != "_gp_disp") + return make_reject_for_shared_lib_reloc_error(this->_ctx, atom, ref); + break; + case R_MIPS16_26: + case R_MIPS_26: + case R_MICROMIPS_26_S1: + // These relocations are position dependent + // and not acceptable in a shared object. + return make_reject_for_shared_lib_reloc_error(this->_ctx, atom, ref); + default: + break; + } + return std::error_code(); } template <typename ELFT> @@ -635,8 +985,7 @@ static bool isMipsReadonly(const MipsELFDefinedAtom<ELFT> &atom) { 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) + if (isAllGotReloc(refKind) || isAllCallReloc(refKind)) return true; if (refKind != R_MIPS_32 && refKind != R_MIPS_64) @@ -648,7 +997,7 @@ bool RelocationPass<ELFT>::mightBeDynamic(const MipsELFDefinedAtom<ELFT> &atom, return true; if (!isMipsReadonly(atom)) return true; - if (atom.file().isPIC()) + if (atom.isPIC()) return true; return false; @@ -659,14 +1008,18 @@ 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>::isMicroMips() { + TargetHandler &handler = this->_ctx.getTargetHandler(); + return static_cast<MipsTargetHandler<ELFT> &>(handler) + .getAbiInfoHandler() + .isMicroMips(); +} + +template <typename ELFT> bool RelocationPass<ELFT>::isMipsR6() { + TargetHandler &handler = this->_ctx.getTargetHandler(); + return static_cast<MipsTargetHandler<ELFT> &>(handler) + .getAbiInfoHandler() + .isMipsR6(); } template <typename ELFT> @@ -697,21 +1050,13 @@ 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) + if (isa<SharedLibraryAtom>(atom)) 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; + if (_ctx.getOutputELFType() != ET_DYN) + return false; + if (da && da->scope() != DefinedAtom::scopeTranslationUnit) + return true; + return isa<UndefinedAtom>(atom); } template <typename ELFT> @@ -721,17 +1066,9 @@ static bool isMicroMips(const MipsELFDefinedAtom<ELFT> &atom) { } 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) { + if (isMicroMips()) { auto microPLT = _pltMicroMap.find(a); if (microPLT != _pltMicroMap.end()) return microPLT->second; @@ -743,7 +1080,7 @@ const PLTAtom *RelocationPass<ELFT>::getPLTEntry(const Atom *a) { return regPLT->second; // ... and finally prefer to create new compressed PLT entry. - return hasMicroCode ? getPLTMicroEntry(a) : getPLTRegEntry(a); + return isMicroMips() ? getPLTMicroEntry(a) : getPLTRegEntry(a); } template <typename ELFT> @@ -759,37 +1096,34 @@ void RelocationPass<ELFT>::handlePlain(const MipsELFDefinedAtom<ELFT> &atom, } 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)); +void RelocationPass<ELFT>::handleBranch(const MipsELFDefinedAtom<ELFT> &atom, + Reference &ref) { + bool isMicro = isMicroMipsReloc(ref.kindValue()); + if (const auto *sla = dyn_cast<SharedLibraryAtom>(ref.target())) { + if (sla->type() == SharedLibraryAtom::Type::Code) + ref.setTarget(isMicro ? getPLTMicroEntry(sla) : getPLTRegEntry(sla)); + } else if (requireLA25Stub(atom, ref)) { + if (isMicro) + ref.setTarget(getLA25MicroEntry(ref.target())); + else + ref.setTarget(getLA25RegEntry(ref.target())); + } if (!isLocal(ref.target())) { - if (isMicro) + if (ref.kindValue() == R_MICROMIPS_26_S1) ref.setKindValue(LLD_R_MICROMIPS_GLOBAL_26_S1); - else + else if (ref.kindValue() == R_MIPS_26) ref.setKindValue(LLD_R_MIPS_GLOBAL_26); } } template <typename ELFT> void RelocationPass<ELFT>::handleGOT(Reference &ref) { - if (!isLocalCall(ref.target())) { + if (!isLocalCall(ref.target())) ref.setTarget(getGlobalGOTEntry(ref.target())); - return; - } - - if (ref.kindValue() == R_MIPS_GOT_PAGE) + else if (isGotPageReloc(ref.kindValue())) ref.setTarget(getLocalGOTPageEntry(ref)); - else if (ref.kindValue() == R_MIPS_GOT_DISP) - ref.setTarget(getLocalGOTEntry(ref)); - else if (isLocal(ref.target())) + else if (isLocal(ref.target()) && + (isCallReloc(ref.kindValue()) || isGotReloc(ref.kindValue()))) ref.setTarget(getLocalGOTPageEntry(ref)); else ref.setTarget(getLocalGOTEntry(ref)); @@ -817,11 +1151,12 @@ bool RelocationPass<ELFT>::isLocalCall(const Atom *a) const { } template <typename ELFT> -bool RelocationPass<ELFT>::requireLA25Stub(const Atom *a) const { - if (isLocal(a)) +bool RelocationPass<ELFT>::requireLA25Stub(const MipsELFDefinedAtom<ELFT> &atom, + const Reference &ref) const { + if (atom.file().isPIC()) return false; - if (auto *da = dyn_cast<DefinedAtom>(a)) - return static_cast<const MipsELFDefinedAtom<ELFT> *>(da)->file().isPIC(); + if (auto *da = dyn_cast<DefinedAtom>(ref.target())) + return static_cast<const MipsELFDefinedAtom<ELFT> *>(da)->isPIC(); return false; } @@ -886,7 +1221,8 @@ const GOTAtom *RelocationPass<ELFT>::getGlobalGOTEntry(const Atom *a) { } template <typename ELFT> -const GOTAtom *RelocationPass<ELFT>::getTLSGOTEntry(const Atom *a) { +const GOTAtom *RelocationPass<ELFT>::getTLSGOTEntry(const Atom *a, + Reference::Addend addend) { auto got = _gotTLSMap.find(a); if (got != _gotTLSMap.end()) return got->second; @@ -897,13 +1233,15 @@ const GOTAtom *RelocationPass<ELFT>::getTLSGOTEntry(const Atom *a) { _tlsGotVector.push_back(ga); Reference::KindValue relKind = ELFT::Is64Bits ? R_MIPS_TLS_TPREL64 : R_MIPS_TLS_TPREL32; - ga->addReferenceELF_Mips(relKind, 0, a, 0); + ga->addReferenceELF_Mips(relKind, 0, a, addend); return ga; } template <typename ELFT> -const GOTAtom *RelocationPass<ELFT>::getTLSGdGOTEntry(const Atom *a) { +const GOTAtom * +RelocationPass<ELFT>::getTLSGdGOTEntry(const Atom *a, + Reference::Addend addend) { auto got = _gotTLSGdMap.find(a); if (got != _gotTLSGdMap.end()) return got->second; @@ -913,11 +1251,11 @@ const GOTAtom *RelocationPass<ELFT>::getTLSGdGOTEntry(const Atom *a) { _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); + ga->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD64, 0, a, addend); + ga->addReferenceELF_Mips(R_MIPS_TLS_DTPREL64, 8, a, addend); } else { - ga->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD32, 0, a, 0); - ga->addReferenceELF_Mips(R_MIPS_TLS_DTPREL32, 4, a, 0); + ga->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD32, 0, a, addend); + ga->addReferenceELF_Mips(R_MIPS_TLS_DTPREL32, 4, a, addend); } return ga; @@ -946,9 +1284,10 @@ PLTAtom *RelocationPass<ELFT>::createPLTHeader(bool isMicroMips) { _gotpltVector.insert(_gotpltVector.begin(), ga0); if (isMicroMips) - return new (_file._alloc) PLT0MicroAtom(ga0, _file); - else - return new (_file._alloc) PLT0Atom(ga0, _file); + return new (_file._alloc) PLT0MicroAtom<ELFT>(ga0, _file); + if (_ctx.getAbi() == MipsAbi::N32) + return new (_file._alloc) PLT0N32Atom<ELFT>(ga0, _file); + return new (_file._alloc) PLT0Atom<ELFT>(ga0, _file); } template <typename ELFT> @@ -969,9 +1308,11 @@ const PLTAtom *RelocationPass<ELFT>::getPLTRegEntry(const Atom *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); + PLTAAtom *pa = nullptr; + if (isMipsR6()) + pa = new (_file._alloc) PLTR6Atom<ELFT>(getGOTPLTEntry(a), _file); + else + pa = new (_file._alloc) PLTARegAtom<ELFT>(getGOTPLTEntry(a), _file); _pltRegMap[a] = pa; _pltRegVector.push_back(pa); @@ -988,7 +1329,7 @@ const PLTAtom *RelocationPass<ELFT>::getPLTMicroEntry(const Atom *a) { if (plt != _pltMicroMap.end()) return plt->second; - auto pa = new (_file._alloc) PLTMicroAtom(getGOTPLTEntry(a), _file); + auto pa = new (_file._alloc) PLTMicroAtom<ELFT>(getGOTPLTEntry(a), _file); _pltMicroMap[a] = pa; _pltMicroVector.push_back(pa); @@ -1005,7 +1346,7 @@ const LA25Atom *RelocationPass<ELFT>::getLA25RegEntry(const Atom *a) { if (la25 != _la25RegMap.end()) return la25->second; - auto sa = new (_file._alloc) LA25RegAtom(a, _file); + auto sa = new (_file._alloc) LA25RegAtom<ELFT>(a, _file); _la25RegMap[a] = sa; _la25Vector.push_back(sa); @@ -1018,7 +1359,7 @@ const LA25Atom *RelocationPass<ELFT>::getLA25MicroEntry(const Atom *a) { if (la25 != _la25MicroMap.end()) return la25->second; - auto sa = new (_file._alloc) LA25MicroAtom(a, _file); + auto sa = new (_file._alloc) LA25MicroAtom<ELFT>(a, _file); _la25MicroMap[a] = sa; _la25Vector.push_back(sa); @@ -1047,10 +1388,14 @@ RelocationPass<ELFT>::getObjectEntry(const SharedLibraryAtom *a) { static std::unique_ptr<Pass> createPass(MipsLinkingContext &ctx) { switch (ctx.getTriple().getArch()) { + case llvm::Triple::mips: + return llvm::make_unique<RelocationPass<ELF32BE>>(ctx); case llvm::Triple::mipsel: - return llvm::make_unique<RelocationPass<Mips32ELType>>(ctx); + return llvm::make_unique<RelocationPass<ELF32LE>>(ctx); + case llvm::Triple::mips64: + return llvm::make_unique<RelocationPass<ELF64BE>>(ctx); case llvm::Triple::mips64el: - return llvm::make_unique<RelocationPass<Mips64ELType>>(ctx); + return llvm::make_unique<RelocationPass<ELF64LE>>(ctx); default: llvm_unreachable("Unhandled arch"); } diff --git a/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.cpp b/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.cpp new file mode 100644 index 0000000000000..98cc059787efc --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.cpp @@ -0,0 +1,264 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsSectionChunks.cpp --------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsLinkingContext.h" +#include "MipsSectionChunks.h" +#include "MipsTargetLayout.h" + +namespace lld { +namespace elf { + +template <class ELFT> +MipsReginfoSection<ELFT>::MipsReginfoSection( + const ELFLinkingContext &ctx, MipsTargetLayout<ELFT> &targetLayout, + const Elf_Mips_RegInfo ®info) + : Section<ELFT>(ctx, ".reginfo", "MipsReginfo"), _reginfo(reginfo), + _targetLayout(targetLayout) { + this->setOrder(MipsTargetLayout<ELFT>::ORDER_MIPS_REGINFO); + this->_entSize = sizeof(Elf_Mips_RegInfo); + this->_fsize = sizeof(Elf_Mips_RegInfo); + this->_msize = sizeof(Elf_Mips_RegInfo); + this->_alignment = 4; + this->_type = SHT_MIPS_REGINFO; + this->_flags = SHF_ALLOC; +} + +template <class ELFT> +void MipsReginfoSection<ELFT>::write(ELFWriter *writer, + TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *dest = buffer.getBufferStart() + this->fileOffset(); + std::memcpy(dest, &_reginfo, this->_fsize); +} + +template <class ELFT> void MipsReginfoSection<ELFT>::finalize() { + _reginfo.ri_gp_value = _targetLayout.getGPAddr(); + + if (this->_outputSection) + this->_outputSection->setType(this->_type); +} + +template class MipsReginfoSection<ELF32BE>; +template class MipsReginfoSection<ELF32LE>; +template class MipsReginfoSection<ELF64BE>; +template class MipsReginfoSection<ELF64LE>; + +template <class ELFT> +MipsOptionsSection<ELFT>::MipsOptionsSection( + const ELFLinkingContext &ctx, MipsTargetLayout<ELFT> &targetLayout, + const Elf_Mips_RegInfo ®info) + : Section<ELFT>(ctx, ".MIPS.options", "MipsOptions"), _reginfo(reginfo), + _targetLayout(targetLayout) { + this->setOrder(MipsTargetLayout<ELFT>::ORDER_MIPS_OPTIONS); + this->_entSize = 1; + this->_alignment = 8; + this->_fsize = llvm::RoundUpToAlignment( + sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo), this->_alignment); + this->_msize = this->_fsize; + this->_type = SHT_MIPS_OPTIONS; + this->_flags = SHF_ALLOC | SHF_MIPS_NOSTRIP; + + _header.kind = ODK_REGINFO; + _header.size = this->_fsize; + _header.section = 0; + _header.info = 0; +} + +template <class ELFT> +void MipsOptionsSection<ELFT>::write(ELFWriter *writer, + TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *dest = buffer.getBufferStart() + this->fileOffset(); + std::memset(dest, 0, this->_fsize); + std::memcpy(dest, &_header, sizeof(_header)); + std::memcpy(dest + sizeof(_header), &_reginfo, sizeof(_reginfo)); +} + +template <class ELFT> void MipsOptionsSection<ELFT>::finalize() { + _reginfo.ri_gp_value = _targetLayout.getGPAddr(); + + if (this->_outputSection) + this->_outputSection->setType(this->_type); +} + +template class MipsOptionsSection<ELF32BE>; +template class MipsOptionsSection<ELF32LE>; +template class MipsOptionsSection<ELF64BE>; +template class MipsOptionsSection<ELF64LE>; + +template <class ELFT> +MipsAbiFlagsSection<ELFT>::MipsAbiFlagsSection( + const ELFLinkingContext &ctx, MipsTargetLayout<ELFT> &targetLayout, + const Elf_Mips_ABIFlags &abiFlags) + : Section<ELFT>(ctx, ".MIPS.abiflags", "MipsAbiFlags"), _abiFlags(abiFlags), + _targetLayout(targetLayout) { + this->setOrder(MipsTargetLayout<ELFT>::ORDER_MIPS_ABI_FLAGS); + this->_alignment = 8; + this->_fsize = llvm::RoundUpToAlignment(sizeof(_abiFlags), this->_alignment); + this->_msize = this->_fsize; + this->_entSize = this->_fsize; + this->_type = SHT_MIPS_ABIFLAGS; + this->_flags = SHF_ALLOC; +} + +template <class ELFT> +void MipsAbiFlagsSection<ELFT>::write(ELFWriter *writer, + TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *dest = buffer.getBufferStart() + this->fileOffset(); + std::memcpy(dest, &_abiFlags, this->_fsize); +} + +template <class ELFT> void MipsAbiFlagsSection<ELFT>::finalize() { + if (this->_outputSection) + this->_outputSection->setType(this->_type); +} + +template class MipsAbiFlagsSection<ELF32BE>; +template class MipsAbiFlagsSection<ELF32LE>; +template class MipsAbiFlagsSection<ELF64BE>; +template class MipsAbiFlagsSection<ELF64LE>; + +template <class ELFT> +MipsGOTSection<ELFT>::MipsGOTSection(const MipsLinkingContext &ctx) + : AtomSection<ELFT>(ctx, ".got", DefinedAtom::typeGOT, DefinedAtom::permRW_, + MipsTargetLayout<ELFT>::ORDER_GOT), + _hasNonLocal(false), _localCount(0) { + this->_flags |= SHF_MIPS_GPREL; + this->_alignment = 4; +} + +template <class ELFT> +bool MipsGOTSection<ELFT>::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(); +} + +template <class ELFT> +const AtomLayout *MipsGOTSection<ELFT>::appendAtom(const Atom *atom) { + const DefinedAtom *da = dyn_cast<DefinedAtom>(atom); + + if (atom->name() == "_GLOBAL_OFFSET_TABLE_") + return AtomSection<ELFT>::appendAtom(atom); + + for (const auto &r : *da) { + if (r->kindNamespace() != 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<ELFT>::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<ELFT>::appendAtom(atom); + case R_MIPS_TLS_DTPMOD32: + case R_MIPS_TLS_DTPMOD64: + _hasNonLocal = true; + break; + } + } + + if (!_hasNonLocal) + ++_localCount; + + return AtomSection<ELFT>::appendAtom(atom); +} + +template class MipsGOTSection<ELF32BE>; +template class MipsGOTSection<ELF32LE>; +template class MipsGOTSection<ELF64BE>; +template class MipsGOTSection<ELF64LE>; + +template <class ELFT> +MipsPLTSection<ELFT>::MipsPLTSection(const MipsLinkingContext &ctx) + : AtomSection<ELFT>(ctx, ".plt", DefinedAtom::typeGOT, DefinedAtom::permR_X, + MipsTargetLayout<ELFT>::ORDER_PLT) {} + +template <class ELFT> +const AtomLayout *MipsPLTSection<ELFT>::findPLTLayout(const Atom *plt) const { + auto it = _pltLayoutMap.find(plt); + return it != _pltLayoutMap.end() ? it->second : nullptr; +} + +template <class ELFT> +const AtomLayout *MipsPLTSection<ELFT>::appendAtom(const Atom *atom) { + const auto *layout = AtomSection<ELFT>::appendAtom(atom); + + const DefinedAtom *da = cast<DefinedAtom>(atom); + + for (const auto &r : *da) { + if (r->kindNamespace() != 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; +} + +template class MipsPLTSection<ELF32BE>; +template class MipsPLTSection<ELF32LE>; +template class MipsPLTSection<ELF64BE>; +template class MipsPLTSection<ELF64LE>; + +template <class ELFT> static bool isMips64EL() { + return ELFT::Is64Bits && ELFT::TargetEndianness == llvm::support::little; +} + +template <class ELFT> +MipsRelocationTable<ELFT>::MipsRelocationTable(const ELFLinkingContext &ctx, + StringRef str, int32_t order) + : RelocationTable<ELFT>(ctx, str, order) {} + +template <class ELFT> +void MipsRelocationTable<ELFT>::writeRela(ELFWriter *writer, Elf_Rela &r, + const DefinedAtom &atom, + const Reference &ref) { + uint32_t rType = ref.kindValue() | (ref.tag() << 8); + r.setSymbolAndType(this->getSymbolIndex(ref.target()), rType, + isMips64EL<ELFT>()); + r.r_offset = writer->addressOfAtom(&atom) + ref.offsetInAtom(); + // The addend is used only by relative relocations + if (this->_ctx.isRelativeReloc(ref)) + r.r_addend = writer->addressOfAtom(ref.target()) + ref.addend(); + else + r.r_addend = 0; +} + +template <class ELFT> +void MipsRelocationTable<ELFT>::writeRel(ELFWriter *writer, Elf_Rel &r, + const DefinedAtom &atom, + const Reference &ref) { + uint32_t rType = ref.kindValue() | (ref.tag() << 8); + r.setSymbolAndType(this->getSymbolIndex(ref.target()), rType, + isMips64EL<ELFT>()); + r.r_offset = writer->addressOfAtom(&atom) + ref.offsetInAtom(); +} + +template class MipsRelocationTable<ELF32BE>; +template class MipsRelocationTable<ELF32LE>; +template class MipsRelocationTable<ELF64BE>; +template class MipsRelocationTable<ELF64LE>; + +} // elf +} // lld diff --git a/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.h b/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.h index de9390f2b3074..e545f65dc419a 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.h +++ b/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.h @@ -9,23 +9,81 @@ #ifndef LLD_READER_WRITER_ELF_MIPS_MIPS_SECTION_CHUNKS_H #define LLD_READER_WRITER_ELF_MIPS_MIPS_SECTION_CHUNKS_H +#include "SectionChunks.h" + namespace lld { namespace elf { template <typename ELFT> class MipsTargetLayout; class MipsLinkingContext; +/// \brief Handle Mips .reginfo section +template <class ELFT> class MipsReginfoSection : public Section<ELFT> { +public: + typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo; + + MipsReginfoSection(const ELFLinkingContext &ctx, + MipsTargetLayout<ELFT> &targetLayout, + const Elf_Mips_RegInfo ®info); + + StringRef segmentKindToStr() const override { return "REGINFO"; } + bool hasOutputSegment() const override { return true; } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override; + void finalize() override; + +private: + Elf_Mips_RegInfo _reginfo; + MipsTargetLayout<ELFT> &_targetLayout; +}; + +/// \brief Handle .MIPS.options section +template <class ELFT> class MipsOptionsSection : public Section<ELFT> { +public: + typedef llvm::object::Elf_Mips_Options<ELFT> Elf_Mips_Options; + typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo; + + MipsOptionsSection(const ELFLinkingContext &ctx, + MipsTargetLayout<ELFT> &targetLayout, + const Elf_Mips_RegInfo ®info); + + bool hasOutputSegment() const override { return true; } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override; + void finalize() override; + +private: + Elf_Mips_Options _header; + Elf_Mips_RegInfo _reginfo; + MipsTargetLayout<ELFT> &_targetLayout; +}; + +/// \brief Handle .MIPS.abiflags section +template <class ELFT> class MipsAbiFlagsSection : public Section<ELFT> { +public: + typedef llvm::object::Elf_Mips_ABIFlags<ELFT> Elf_Mips_ABIFlags; + + MipsAbiFlagsSection(const ELFLinkingContext &ctx, + MipsTargetLayout<ELFT> &targetLayout, + const Elf_Mips_ABIFlags &abiFlags); + + bool hasOutputSegment() const override { return true; } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override; + void finalize() override; + +private: + Elf_Mips_ABIFlags _abiFlags; + MipsTargetLayout<ELFT> &_targetLayout; +}; + /// \brief Handle Mips GOT section -template <class ELFType> class MipsGOTSection : public AtomSection<ELFType> { +template <class ELFT> class MipsGOTSection : public AtomSection<ELFT> { 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; - } + MipsGOTSection(const MipsLinkingContext &ctx); /// \brief Number of local GOT entries. std::size_t getLocalCount() const { return _localCount; } @@ -39,47 +97,9 @@ public: } /// \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); + bool compare(const Atom *a, const Atom *b) const; - 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); - } + const AtomLayout *appendAtom(const Atom *atom) override; private: /// \brief True if the GOT contains non-local entries. @@ -96,35 +116,13 @@ private: }; /// \brief Handle Mips PLT section -template <class ELFType> class MipsPLTSection : public AtomSection<ELFType> { +template <class ELFT> class MipsPLTSection : public AtomSection<ELFT> { 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); + MipsPLTSection(const MipsLinkingContext &ctx); - const DefinedAtom *da = cast<DefinedAtom>(atom); + const AtomLayout *findPLTLayout(const Atom *plt) const; - 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; - } + const AtomLayout *appendAtom(const Atom *atom) override; private: /// \brief Map PLT Atoms to their layouts. @@ -135,33 +133,15 @@ 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) {} + MipsRelocationTable(const ELFLinkingContext &ctx, StringRef str, + int32_t 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; - } - + const Reference &ref) override; 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(); - } + const Reference &ref) override; }; } // elf diff --git a/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.cpp b/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.cpp index f60ab63c6af7f..817e294446662 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.cpp +++ b/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.cpp @@ -1,4 +1,4 @@ -//===- lib/ReaderWriter/ELF/Mips/MipsTargetHandler.cpp --------------------===// +//===- lib/ReaderWriter/ELF/Mips/MipsTargetHandler32EL.cpp ----------------===// // // The LLVM Linker // @@ -7,29 +7,160 @@ // //===----------------------------------------------------------------------===// +#include "ELFReader.h" +#include "MipsELFFile.h" +#include "MipsELFWriters.h" #include "MipsTargetHandler.h" -using namespace lld; -using namespace elf; +namespace lld { +namespace elf { -void MipsRelocationStringTable::registerTable(Registry ®istry) { - registry.addKindTable(Reference::KindNamespace::ELF, - Reference::KindArch::Mips, kindStrings); +template <class ELFT> +MipsTargetHandler<ELFT>::MipsTargetHandler(MipsLinkingContext &ctx) + : _ctx(ctx), _targetLayout(new MipsTargetLayout<ELFT>(ctx, _abiInfoHandler)), + _relocationHandler( + createMipsRelocationHandler<ELFT>(ctx, *_targetLayout)) {} + +template <class ELFT> +std::unique_ptr<Reader> MipsTargetHandler<ELFT>::getObjReader() { + return llvm::make_unique<ELFReader<MipsELFFile<ELFT>>>(_ctx); +} + +template <class ELFT> +std::unique_ptr<Reader> MipsTargetHandler<ELFT>::getDSOReader() { + return llvm::make_unique<ELFReader<DynamicFile<ELFT>>>(_ctx); +} + +template <class ELFT> +const TargetRelocationHandler & +MipsTargetHandler<ELFT>::getRelocationHandler() const { + return *_relocationHandler; +} + +template <class ELFT> +std::unique_ptr<Writer> MipsTargetHandler<ELFT>::getWriter() { + switch (_ctx.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + return llvm::make_unique<MipsExecutableWriter<ELFT>>(_ctx, *_targetLayout, + _abiInfoHandler); + case llvm::ELF::ET_DYN: + return llvm::make_unique<MipsDynamicLibraryWriter<ELFT>>( + _ctx, *_targetLayout, _abiInfoHandler); + case llvm::ELF::ET_REL: + llvm_unreachable("TODO: support -r mode"); + default: + llvm_unreachable("unsupported output type"); + } +} + +template <class ELFT> MipsAbi MipsTargetHandler<ELFT>::getAbi() const { + return _abiInfoHandler.getAbi(); +} + +template class MipsTargetHandler<ELF32BE>; +template class MipsTargetHandler<ELF32LE>; +template class MipsTargetHandler<ELF64BE>; +template class MipsTargetHandler<ELF64LE>; + +template <class ELFT> +MipsSymbolTable<ELFT>::MipsSymbolTable(const ELFLinkingContext &ctx) + : SymbolTable<ELFT>(ctx, ".symtab", + TargetLayout<ELFT>::ORDER_SYMBOL_TABLE) {} + +template <class ELFT> +void MipsSymbolTable<ELFT>::addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr) { + 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; + } } -#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), +template <class ELFT> void MipsSymbolTable<ELFT>::finalize(bool sort) { + SymbolTable<ELFT>::finalize(sort); -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 -}; + 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 MipsSymbolTable<ELF32BE>; +template class MipsSymbolTable<ELF32LE>; +template class MipsSymbolTable<ELF64BE>; +template class MipsSymbolTable<ELF64LE>; + +template <class ELFT> +MipsDynamicSymbolTable<ELFT>::MipsDynamicSymbolTable( + const ELFLinkingContext &ctx, MipsTargetLayout<ELFT> &layout) + : DynamicSymbolTable<ELFT>(ctx, layout, ".dynsym", + TargetLayout<ELFT>::ORDER_DYNAMIC_SYMBOLS), + _targetLayout(layout) {} + +template <class ELFT> void MipsDynamicSymbolTable<ELFT>::sortSymbols() { + 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); + }); +} + +template <class ELFT> void MipsDynamicSymbolTable<ELFT>::finalize() { + DynamicSymbolTable<ELFT>::finalize(); -#undef ELF_RELOC + 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; + } + } + } +} + +template class MipsDynamicSymbolTable<ELF32BE>; +template class MipsDynamicSymbolTable<ELF32LE>; +template class MipsDynamicSymbolTable<ELF64BE>; +template class MipsDynamicSymbolTable<ELF64LE>; + +} +} diff --git a/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h b/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h index 79509addf40b4..e4a35bdd323dd 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h +++ b/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h @@ -9,146 +9,36 @@ #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 "MipsAbiInfoHandler.h" #include "MipsLinkingContext.h" -#include "MipsRelocationHandler.h" -#include "MipsSectionChunks.h" -#include "TargetLayout.h" -#include "llvm/ADT/DenseSet.h" +#include "MipsTargetLayout.h" +#include "TargetHandler.h" namespace lld { namespace elf { -/// \brief TargetLayout for Mips -template <class ELFT> class MipsTargetLayout final : public TargetLayout<ELFT> { +class MipsBaseTargetHandler : public TargetHandler { 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); + virtual MipsAbi getAbi() const = 0; }; /// \brief TargetHandler for Mips template <class ELFT> -class MipsTargetHandler final : public DefaultTargetHandler<ELFT> { +class MipsTargetHandler final : public MipsBaseTargetHandler { 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)); - } + MipsTargetHandler(MipsLinkingContext &ctx); - std::unique_ptr<Reader> getDSOReader() override { - return std::unique_ptr<Reader>(new MipsELFDSOReader<ELFT>(_ctx)); - } + MipsAbiInfoHandler<ELFT> &getAbiInfoHandler() { return _abiInfoHandler; } - 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); - } + std::unique_ptr<Reader> getObjReader() override; + std::unique_ptr<Reader> getDSOReader() override; + const TargetRelocationHandler &getRelocationHandler() const override; + std::unique_ptr<Writer> getWriter() override; + MipsAbi getAbi() const override; private: MipsLinkingContext &_ctx; - std::unique_ptr<MipsRuntimeFile<ELFT>> _runtimeFile; + MipsAbiInfoHandler<ELFT> _abiInfoHandler; std::unique_ptr<MipsTargetLayout<ELFT>> _targetLayout; std::unique_ptr<TargetRelocationHandler> _relocationHandler; }; @@ -157,95 +47,21 @@ 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) {} + MipsSymbolTable(const ELFLinkingContext &ctx); 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; - } - } - } - } + int64_t addr) override; + void finalize(bool sort) override; }; 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; - } + MipsTargetLayout<ELFT> &layout); - 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; - } - } - } - } + void sortSymbols() override; + void finalize() override; private: MipsTargetLayout<ELFT> &_targetLayout; diff --git a/lib/ReaderWriter/ELF/Mips/MipsTargetLayout.cpp b/lib/ReaderWriter/ELF/Mips/MipsTargetLayout.cpp new file mode 100644 index 0000000000000..710f8320a8b9c --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsTargetLayout.cpp @@ -0,0 +1,111 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsTargetLayout.cpp ---------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsLinkingContext.h" +#include "MipsTargetLayout.h" + +namespace lld { +namespace elf { + +template <class ELFT> +MipsTargetLayout<ELFT>::MipsTargetLayout(MipsLinkingContext &ctx, + MipsAbiInfoHandler<ELFT> &abi) + : TargetLayout<ELFT>(ctx), _abiInfo(abi), + _gotSection(new (this->_allocator) MipsGOTSection<ELFT>(ctx)), + _pltSection(new (this->_allocator) MipsPLTSection<ELFT>(ctx)) {} + +template <class ELFT> +AtomSection<ELFT> *MipsTargetLayout<ELFT>::createSection( + StringRef name, int32_t type, DefinedAtom::ContentPermissions permissions, + typename TargetLayout<ELFT>::SectionOrder order) { + if (type == DefinedAtom::typeGOT && name == ".got") + return _gotSection; + if (type == DefinedAtom::typeStub && name == ".plt") + return _pltSection; + return TargetLayout<ELFT>::createSection(name, type, permissions, order); +} + +template <class ELFT> +typename TargetLayout<ELFT>::SegmentType +MipsTargetLayout<ELFT>::getSegmentType(const Section<ELFT> *section) const { + switch (section->order()) { + case ORDER_MIPS_REGINFO: + return _abiInfo.hasMipsAbiSection() ? llvm::ELF::PT_LOAD + : llvm::ELF::PT_MIPS_REGINFO; + case ORDER_MIPS_OPTIONS: + return llvm::ELF::PT_LOAD; + case ORDER_MIPS_ABI_FLAGS: + return llvm::ELF::PT_MIPS_ABIFLAGS; + default: + return TargetLayout<ELFT>::getSegmentType(section); + } +} + +template <class ELFT> uint64_t MipsTargetLayout<ELFT>::getGPAddr() { + std::call_once(_gpOnce, [this]() { + if (AtomLayout *a = this->findAbsoluteAtom("_gp")) + _gpAddr = a->_virtualAddr; + }); + return _gpAddr; +} + +template <class ELFT> +typename TargetLayout<ELFT>::SectionOrder +MipsTargetLayout<ELFT>::getSectionOrder(StringRef name, int32_t contentType, + int32_t contentPermissions) { + if ((contentType == DefinedAtom::typeStub) && (name.startswith(".text"))) + return TargetLayout<ELFT>::ORDER_TEXT; + + return TargetLayout<ELFT>::getSectionOrder(name, contentType, + contentPermissions); +} + +template <class ELFT> +unique_bump_ptr<RelocationTable<ELFT>> +MipsTargetLayout<ELFT>::createRelocationTable(StringRef name, int32_t order) { + return unique_bump_ptr<RelocationTable<ELFT>>(new ( + this->_allocator) MipsRelocationTable<ELFT>(this->_ctx, name, order)); +} + +template <class ELFT> +uint64_t MipsTargetLayout<ELFT>::getLookupSectionFlags( + const OutputSection<ELFT> *os) const { + uint64_t flags = TargetLayout<ELFT>::getLookupSectionFlags(os); + return flags & ~llvm::ELF::SHF_MIPS_NOSTRIP; +} + +template <class ELFT> void MipsTargetLayout<ELFT>::sortSegments() { + using namespace llvm::ELF; + TargetLayout<ELFT>::sortSegments(); + // Move PT_MIPS_ABIFLAGS or PT_MIPS_REGINFO right after PT_INTERP. + auto abiIt = + std::find_if(this->_segments.begin(), this->_segments.end(), + [](const Segment<ELFT> *s) { + auto typ = s->segmentType(); + return typ == PT_MIPS_ABIFLAGS || typ == PT_MIPS_REGINFO; + }); + if (abiIt == this->_segments.end()) + return; + Segment<ELFT> *abiSeg = *abiIt; + this->_segments.erase(abiIt); + auto outIt = std::find_if(this->_segments.begin(), this->_segments.end(), + [](const Segment<ELFT> *s) { + auto typ = s->segmentType(); + return typ != PT_PHDR && typ != PT_INTERP; + }); + this->_segments.insert(outIt, abiSeg); +} + +template class MipsTargetLayout<ELF32BE>; +template class MipsTargetLayout<ELF32LE>; +template class MipsTargetLayout<ELF64BE>; +template class MipsTargetLayout<ELF64LE>; + +} // end namespace elf +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/Mips/MipsTargetLayout.h b/lib/ReaderWriter/ELF/Mips/MipsTargetLayout.h new file mode 100644 index 0000000000000..08855438d20e6 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsTargetLayout.h @@ -0,0 +1,71 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsTargetLayout.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_LAYOUT_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_TARGET_LAYOUT_H + +#include "MipsAbiInfoHandler.h" +#include "MipsSectionChunks.h" +#include "TargetLayout.h" + +namespace lld { +namespace elf { + +class MipsLinkingContext; + +/// \brief TargetLayout for Mips +template <class ELFT> class MipsTargetLayout final : public TargetLayout<ELFT> { +public: + enum MipsSectionOrder { + ORDER_MIPS_ABI_FLAGS = TargetLayout<ELFT>::ORDER_RO_NOTE + 1, + ORDER_MIPS_REGINFO, + ORDER_MIPS_OPTIONS, + }; + + MipsTargetLayout(MipsLinkingContext &ctx, MipsAbiInfoHandler<ELFT> &abi); + + 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, + typename TargetLayout<ELFT>::SectionOrder order) override; + + typename TargetLayout<ELFT>::SegmentType + getSegmentType(const Section<ELFT> *section) const override; + + /// \brief GP offset relative to .got section. + uint64_t getGPOffset() const { return 0x7FF0; } + + /// \brief Get '_gp' symbol address. + uint64_t getGPAddr(); + + /// \brief Return the section order for a input section + typename TargetLayout<ELFT>::SectionOrder + getSectionOrder(StringRef name, int32_t contentType, + int32_t contentPermissions) override; + +protected: + unique_bump_ptr<RelocationTable<ELFT>> + createRelocationTable(StringRef name, int32_t order) override; + uint64_t getLookupSectionFlags(const OutputSection<ELFT> *os) const override; + void sortSegments() override; + +private: + MipsAbiInfoHandler<ELFT> &_abiInfo; + MipsGOTSection<ELFT> *_gotSection; + MipsPLTSection<ELFT> *_pltSection; + uint64_t _gpAddr = 0; + std::once_flag _gpOnce; +}; + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/OrderPass.h b/lib/ReaderWriter/ELF/OrderPass.h index d126b830db963..11f88056c8c40 100644 --- a/lib/ReaderWriter/ELF/OrderPass.h +++ b/lib/ReaderWriter/ELF/OrderPass.h @@ -19,9 +19,10 @@ 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(), + std::error_code perform(SimpleFile &file) override { + parallel_sort(file.definedAtoms().begin(), file.definedAtoms().end(), DefinedAtom::compareByPosition); + return std::error_code(); } }; } diff --git a/lib/ReaderWriter/ELF/OutputELFWriter.cpp b/lib/ReaderWriter/ELF/OutputELFWriter.cpp new file mode 100644 index 0000000000000..4f8b0eac655f5 --- /dev/null +++ b/lib/ReaderWriter/ELF/OutputELFWriter.cpp @@ -0,0 +1,514 @@ +//===- lib/ReaderWriter/ELF/OutputELFWriter.cpp --------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "OutputELFWriter.h" +#include "lld/Core/SharedLibraryFile.h" +#include "lld/Core/Simple.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Support/Path.h" + +namespace lld { +namespace elf { + +namespace { + +template <class ELFT> class SymbolFile : public RuntimeFile<ELFT> { +public: + SymbolFile(ELFLinkingContext &ctx) + : RuntimeFile<ELFT>(ctx, "Dynamic absolute symbols") {} + + void addUndefinedAtom(StringRef) override { + llvm_unreachable("Cannot add undefined atoms to resolve undefined symbols"); + } + + bool hasAtoms() const { return this->absolute().size(); } +}; + +template <class ELFT> +class DynamicSymbolFile : public SimpleArchiveLibraryFile { + typedef std::function<void(StringRef, RuntimeFile<ELFT> &)> Resolver; + +public: + DynamicSymbolFile(ELFLinkingContext &ctx, Resolver resolver) + : SimpleArchiveLibraryFile("Dynamically added runtime symbols"), + _ctx(ctx), _resolver(resolver) {} + + File *find(StringRef sym, bool dataSymbolOnly) override { + if (!_file) + _file.reset(new (_alloc) SymbolFile<ELFT>(_ctx)); + + assert(!_file->hasAtoms() && "The file shouldn't have atoms yet"); + _resolver(sym, *_file); + + if (!_file->hasAtoms()) + return nullptr; + + // If atoms were added - return the file but also store it for later + // destruction. + File *result = _file.get(); + _returnedFiles.push_back(std::move(_file)); + return result; + } + +private: + ELFLinkingContext &_ctx; + Resolver _resolver; + + // The allocator should go before bump pointers because of + // reversed destruction order. + llvm::BumpPtrAllocator _alloc; + unique_bump_ptr<SymbolFile<ELFT>> _file; + std::vector<unique_bump_ptr<SymbolFile<ELFT>>> _returnedFiles; +}; + +} // end anon namespace + +template <class ELFT> +OutputELFWriter<ELFT>::OutputELFWriter(ELFLinkingContext &ctx, + TargetLayout<ELFT> &layout) + : _ctx(ctx), _targetHandler(ctx.getTargetHandler()), _layout(layout) {} + +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 : _ctx.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(_ctx.getInterpreter())); + for (const auto &loadName : _soNeeded) + _dynamicTable->addEntry(DT_NEEDED, + _dynamicStringTable->addString(loadName.getKey())); + const auto &rpathList = _ctx.getRpathList(); + if (!rpathList.empty()) { + auto rpath = + new (_alloc) std::string(join(rpathList.begin(), rpathList.end(), ":")); + _dynamicTable->addEntry(_ctx.getEnableNewDtags() ? DT_RUNPATH : DT_RPATH, + _dynamicStringTable->addString(*rpath)); + } + StringRef soname = _ctx.sharedObjectName(); + if (!soname.empty() && _ctx.getOutputELFType() == llvm::ELF::ET_DYN) + _dynamicTable->addEntry(DT_SONAME, _dynamicStringTable->addString(soname)); + + // Add DT_FLAGS/DT_FLAGS_1 entries if necessary. + uint32_t dtflags = 0, dt1flags = 0; + if (_ctx.getDTFlag(ELFLinkingContext::DTFlag::DT_NOW)) { + dtflags |= DF_BIND_NOW; + dt1flags |= DF_1_NOW; + } + if (_ctx.getDTFlag(ELFLinkingContext::DTFlag::DT_ORIGIN)) { + dtflags |= DF_ORIGIN; + dt1flags |= DF_1_ORIGIN; + } + if (dtflags != 0) + _dynamicTable->addEntry(DT_FLAGS, dtflags); + if (dt1flags != 0) + _dynamicTable->addEntry(DT_FLAGS_1, dt1flags); + + // 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. + // There's no such thing as symbol table if we're stripping all the symbols + if (!_ctx.stripSymbols()) + _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 (!TargetLayout<ELFT>::hasOutputSegment(section)) + _shdrtab->updateSection(section); +} + +template <class ELFT> +void 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); + }; + _ctx.setUndefinesResolver( + llvm::make_unique<DynamicSymbolFile<ELFT>>(_ctx, std::move(callback))); + // Add script defined symbols + auto file = + llvm::make_unique<RuntimeFile<ELFT>>(_ctx, "Linker script runtime"); + for (auto &sym : this->_ctx.linkerScriptSema().getScriptDefinedSymbols()) + file->addAbsoluteAtom(sym.getKey()); + result.push_back(std::move(file)); +} + +template <class ELFT> void OutputELFWriter<ELFT>::finalizeDefaultAtomValues() { + const llvm::StringSet<> &symbols = + _ctx.linkerScriptSema().getScriptDefinedSymbols(); + for (auto &sym : symbols) { + uint64_t res = + _ctx.linkerScriptSema().getLinkerScriptExprValue(sym.getKey()); + AtomLayout *a = _layout.findAbsoluteAtom(sym.getKey()); + assert(a); + a->_virtualAddr = res; + } + // If there is a section named XXX, and XXX is a valid C identifier, + // and there are undefined or weak __start_XXX/__stop_XXX symbols, + // set the symbols values to the begin/end of the XXX section + // correspondingly. + for (const auto &name : _ctx.cidentSectionNames()) + updateScopeAtomValues((Twine("__start_") + name.getKey()).str(), + (Twine("__stop_") + name.getKey()).str(), + name.getKey()); +} + +template <class ELFT> void OutputELFWriter<ELFT>::createDefaultSections() { + _elfHeader.reset(new (_alloc) ELFHeader<ELFT>(_ctx)); + _programHeader.reset(new (_alloc) ProgramHeader<ELFT>(_ctx)); + _layout.setHeader(_elfHeader.get()); + _layout.setProgramHeader(_programHeader.get()); + + // Don't create .symtab and .strtab sections if we're going to + // strip all the symbols. + if (!_ctx.stripSymbols()) { + _symtab = this->createSymbolTable(); + _strtab.reset(new (_alloc) StringTable<ELFT>( + _ctx, ".strtab", TargetLayout<ELFT>::ORDER_STRING_TABLE)); + _layout.addSection(_symtab.get()); + _layout.addSection(_strtab.get()); + _symtab->setStringSection(_strtab.get()); + } + + _shstrtab.reset(new (_alloc) StringTable<ELFT>( + _ctx, ".shstrtab", TargetLayout<ELFT>::ORDER_SECTION_STRINGS)); + _shdrtab.reset(new (_alloc) SectionHeader<ELFT>( + _ctx, TargetLayout<ELFT>::ORDER_SECTION_HEADERS)); + _layout.addSection(_shstrtab.get()); + _shdrtab->setStringSection(_shstrtab.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>( + _ctx, ".eh_frame_hdr", _layout, TargetLayout<ELFT>::ORDER_EH_FRAMEHDR)); + _layout.addSection(_ehFrameHeader.get()); + break; + } + + if (_ctx.isDynamic()) { + _dynamicTable = createDynamicTable(); + _dynamicStringTable.reset(new (_alloc) StringTable<ELFT>( + _ctx, ".dynstr", TargetLayout<ELFT>::ORDER_DYNAMIC_STRINGS, true)); + _dynamicSymbolTable = createDynamicSymbolTable(); + _hashTable.reset(new (_alloc) HashSection<ELFT>( + _ctx, ".hash", TargetLayout<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->_ctx, ".symtab", TargetLayout<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->_ctx, _layout, ".dynamic", TargetLayout<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->_ctx, _layout, ".dynsym", + TargetLayout<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 (_ctx.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 (!_ctx.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 (_ctx.isDynamic()) + _dynamicTable->updateDynamicTable(); + + return std::error_code(); +} + +template <class ELFT> std::error_code OutputELFWriter<ELFT>::setELFHeader() { + _elfHeader->e_type(_ctx.getOutputELFType()); + _elfHeader->e_machine(_ctx.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(_ctx.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) { + + ScopedTask createOutputTask(getDefaultDomain(), "ELF Writer Create Output"); + ErrorOr<std::unique_ptr<FileOutputBuffer>> bufferOrErr = + FileOutputBuffer::create(path, outputFileSize(), + FileOutputBuffer::F_executable); + if (std::error_code ec = bufferOrErr.getError()) + return ec; + std::unique_ptr<FileOutputBuffer> &buffer = *bufferOrErr; + createOutputTask.end(); + + 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) { + if (std::error_code ec = buildOutput(file)) + return ec; + if (std::error_code ec = setELFHeader()) + return ec; + return writeOutput(file, path); +} + +template <class ELFT> +void OutputELFWriter<ELFT>::processUndefinedSymbol( + StringRef symName, RuntimeFile<ELFT> &file) const { + if (symName.startswith("__start_")) { + if (_ctx.cidentSectionNames().count(symName.drop_front(8))) + file.addAbsoluteAtom(symName); + } else if (symName.startswith("__stop_")) { + if (_ctx.cidentSectionNames().count(symName.drop_front(7))) + file.addAbsoluteAtom(symName); + } +} + +template <class ELFT> +void OutputELFWriter<ELFT>::updateScopeAtomValues(StringRef sym, + StringRef sec) { + updateScopeAtomValues(("__" + sym + "_start").str().c_str(), + ("__" + sym + "_end").str().c_str(), sec); +} + +template <class ELFT> +void OutputELFWriter<ELFT>::updateScopeAtomValues(StringRef start, + StringRef end, + StringRef sec) { + AtomLayout *s = _layout.findAbsoluteAtom(start); + AtomLayout *e = _layout.findAbsoluteAtom(end); + const OutputSection<ELFT> *section = _layout.findOutputSection(sec); + if (s) + s->_virtualAddr = section ? section->virtualAddr() : 0; + if (e) + e->_virtualAddr = section ? section->virtualAddr() + section->memSize() : 0; +} + +template class OutputELFWriter<ELF32LE>; +template class OutputELFWriter<ELF32BE>; +template class OutputELFWriter<ELF64LE>; +template class OutputELFWriter<ELF64BE>; + +} // namespace elf +} // namespace lld diff --git a/lib/ReaderWriter/ELF/OutputELFWriter.h b/lib/ReaderWriter/ELF/OutputELFWriter.h index c137905b936b6..bb3901010634f 100644 --- a/lib/ReaderWriter/ELF/OutputELFWriter.h +++ b/lib/ReaderWriter/ELF/OutputELFWriter.h @@ -6,88 +6,24 @@ // 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 { +class ELFLinkingContext; + 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 @@ -99,7 +35,7 @@ public: typedef Elf_Sym_Impl<ELFT> Elf_Sym; typedef Elf_Dyn_Impl<ELFT> Elf_Dyn; - OutputELFWriter(ELFLinkingContext &context, TargetLayout<ELFT> &layout); + OutputELFWriter(ELFLinkingContext &ctx, TargetLayout<ELFT> &layout); protected: // build the sections that need to be created @@ -140,11 +76,8 @@ protected: // 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; + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; // Finalize the default atom values virtual void finalizeDefaultAtomValues(); @@ -180,12 +113,15 @@ protected: /// \brief Process undefined symbols that left after resolution step. virtual void processUndefinedSymbol(StringRef symName, - RuntimeFile<ELFT> &file) const {} + RuntimeFile<ELFT> &file) const; + + /// \brief Assign addresses to atoms marking section's start and end. + void updateScopeAtomValues(StringRef sym, StringRef sec); llvm::BumpPtrAllocator _alloc; - ELFLinkingContext &_context; - TargetHandler<ELFT> &_targetHandler; + ELFLinkingContext &_ctx; + TargetHandler &_targetHandler; typedef llvm::DenseMap<const Atom *, uint64_t> AtomToAddress; AtomToAddress _atomToAddressMap; @@ -205,410 +141,12 @@ protected: unique_bump_ptr<HashSection<ELFT>> _hashTable; llvm::StringSet<> _soNeeded; /// @} - std::unique_ptr<RuntimeFile<ELFT>> _scriptFile; private: static StringRef maybeGetSOName(Node *node); + void updateScopeAtomValues(StringRef start, StringRef end, StringRef sec); }; -//===----------------------------------------------------------------------===// -// 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 diff --git a/lib/ReaderWriter/ELF/Reader.cpp b/lib/ReaderWriter/ELF/Reader.cpp index fc113d4789132..801f1abaed7a1 100644 --- a/lib/ReaderWriter/ELF/Reader.cpp +++ b/lib/ReaderWriter/ELF/Reader.cpp @@ -29,15 +29,15 @@ namespace lld { void Registry::addSupportELFObjects(ELFLinkingContext &ctx) { // Tell registry about the ELF object file parser. - add(std::move(ctx.targetHandler()->getObjReader())); + add(ctx.getTargetHandler().getObjReader()); // Tell registry about the relocation name to number mapping for this arch. - ctx.targetHandler()->registerRelocationNames(*this); + ctx.registerRelocationNames(*this); } void Registry::addSupportELFDynamicSharedObjects(ELFLinkingContext &ctx) { // Tell registry about the ELF dynamic shared library file parser. - add(ctx.targetHandler()->getDSOReader()); + add(ctx.getTargetHandler().getDSOReader()); } } // end namespace lld diff --git a/lib/ReaderWriter/ELF/SectionChunks.cpp b/lib/ReaderWriter/ELF/SectionChunks.cpp new file mode 100644 index 0000000000000..520fbe24af3b6 --- /dev/null +++ b/lib/ReaderWriter/ELF/SectionChunks.cpp @@ -0,0 +1,996 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// + +#include "SectionChunks.h" +#include "TargetLayout.h" +#include "lld/Core/Parallel.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Dwarf.h" + +namespace lld { +namespace elf { + +template <class ELFT> +Section<ELFT>::Section(const ELFLinkingContext &ctx, StringRef sectionName, + StringRef chunkName, typename Chunk<ELFT>::Kind k) + : Chunk<ELFT>(chunkName, k, ctx), _inputSectionName(sectionName), + _outputSectionName(sectionName) {} + +template <class ELFT> int Section<ELFT>::getContentType() const { + 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; +} + +template <class ELFT> +AtomSection<ELFT>::AtomSection(const ELFLinkingContext &ctx, + StringRef sectionName, int32_t contentType, + int32_t permissions, int32_t order) + : Section<ELFT>(ctx, sectionName, "AtomSection", + Chunk<ELFT>::Kind::AtomSection), + _contentType(contentType), _contentPermissions(permissions) { + 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; + } +} + +template <class ELFT> +void AtomSection<ELFT>::assignVirtualAddress(uint64_t addr) { + parallel_for_each(_atoms.begin(), _atoms.end(), [&](AtomLayout *ai) { + ai->_virtualAddr = addr + ai->_fileOffset; + }); +} + +template <class ELFT> +void AtomSection<ELFT>::assignFileOffsets(uint64_t offset) { + parallel_for_each(_atoms.begin(), _atoms.end(), [&](AtomLayout *ai) { + ai->_fileOffset = offset + ai->_fileOffset; + }); +} + +template <class ELFT> +const AtomLayout * +AtomSection<ELFT>::findAtomLayoutByName(StringRef name) const { + for (auto ai : _atoms) + if (ai->_atom->name() == name) + return ai; + return nullptr; +} + +template <class ELFT> +std::string AtomSection<ELFT>::formatError(const std::string &errorStr, + const AtomLayout &atom, + const Reference &ref) const { + StringRef kindValStr; + if (!this->_ctx.registry().referenceKindToString( + ref.kindNamespace(), ref.kindArch(), ref.kindValue(), kindValStr)) { + kindValStr = "unknown"; + } + + return + (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(); +} + +/// 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 = atomAlign.value; + 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 AtomLayout *AtomSection<ELFT>::appendAtom(const Atom *atom) { + const DefinedAtom *definedAtom = cast<DefinedAtom>(atom); + + DefinedAtom::Alignment atomAlign = definedAtom->alignment(); + uint64_t alignment = atomAlign.value; + // 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) 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) 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) 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() doesn't have deterministic order. To guarantee + // deterministic error output, collect errors in this vector and sort it + // by atom file offset before printing all errors. + std::vector<std::pair<size_t, std::string>> errors; + parallel_for_each(_atoms.begin(), _atoms.end(), [&](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->_ctx.getTargetHandler().getRelocationHandler(); + for (const auto ref : *definedAtom) { + if (std::error_code ec = + relHandler.applyRelocation(*writer, buffer, *ai, *ref)) { + std::lock_guard<std::mutex> lock(_outputMutex); + errors.push_back(std::make_pair(ai->_fileOffset, + formatError(ec.message(), *ai, *ref))); + success = false; + } + } + }); + if (!success) { + std::sort(errors.begin(), errors.end()); + for (auto &&error : errors) + llvm::errs() << error.second; + llvm::report_fatal_error("relocating output"); + } +} + +template <class ELFT> +void OutputSection<ELFT>::appendSection(Section<ELFT> *section) { + if (section->alignment() > _alignment) + _alignment = section->alignment(); + 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 = section->kind(); + _sections.push_back(section); +} + +template <class ELFT> +StringTable<ELFT>::StringTable(const ELFLinkingContext &ctx, const char *str, + int32_t order, bool dynamic) + : Section<ELFT>(ctx, 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; + } +} + +/// ELF Symbol Table +template <class ELFT> +SymbolTable<ELFT>::SymbolTable(const ELFLinkingContext &ctx, const char *str, + int32_t order) + : Section<ELFT>(ctx, 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 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)); + + // If --discard-all is on, don't add to the symbol table + // symbols with local binding. + if (this->_ctx.discardLocals() && symbol.getBinding() == llvm::ELF::STB_LOCAL) + return; + + // Temporary locals are all the symbols which name starts with .L. + // This is defined by the ELF standard. + if (this->_ctx.discardTempLocals() && atom->name().startswith(".L")) + return; + + _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> +DynamicSymbolTable<ELFT>::DynamicSymbolTable(const ELFLinkingContext &ctx, + TargetLayout<ELFT> &layout, + const char *str, int32_t order) + : SymbolTable<ELFT>(ctx, str, order), _layout(layout) { + this->_type = SHT_DYNSYM; + this->_flags = SHF_ALLOC; + this->_msize = this->_fsize; +} + +template <class ELFT> void DynamicSymbolTable<ELFT>::addSymbolsToHashTable() { + int index = 0; + for (auto &ste : this->_symbolTable) { + if (!ste._atom) + _hashTable->addSymbol("", index); + else + _hashTable->addSymbol(ste._atom->name(), index); + ++index; + } +} + +template <class ELFT> void DynamicSymbolTable<ELFT>::finalize() { + // 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 AtomLayout *atomLayout = ste._atomLayout; + if (!atomLayout) + continue; + ste._symbol.st_value = atomLayout->_virtualAddr; + } + + // Don't sort the symbols + SymbolTable<ELFT>::finalize(false); +} + +template <class ELFT> +RelocationTable<ELFT>::RelocationTable(const ELFLinkingContext &ctx, + StringRef str, int32_t order) + : Section<ELFT>(ctx, str, "RelocationTable") { + this->setOrder(order); + this->_flags = SHF_ALLOC; + // Set the alignment properly depending on the target architecture + this->_alignment = ELFT::Is64Bits ? 8 : 4; + if (ctx.isRelaOutputFormat()) { + this->_entSize = sizeof(Elf_Rela); + this->_type = SHT_RELA; + } else { + this->_entSize = sizeof(Elf_Rel); + this->_type = SHT_REL; + } +} + +template <class ELFT> +uint32_t RelocationTable<ELFT>::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; +} + +template <class ELFT> +bool RelocationTable<ELFT>::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; +} + +template <class ELFT> +bool RelocationTable<ELFT>::canModifyReadonlySection() const { + for (const auto &rel : _relocs) { + const DefinedAtom *atom = rel.first; + if ((atom->permissions() & DefinedAtom::permRW_) != DefinedAtom::permRW_) + return true; + } + return false; +} + +template <class ELFT> void RelocationTable<ELFT>::finalize() { + this->_link = _symbolTable ? _symbolTable->ordinal() : 0; + if (this->_outputSection) + this->_outputSection->setLink(this->_link); +} + +template <class ELFT> +void RelocationTable<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + for (const auto &rel : _relocs) { + if (this->_ctx.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; + } +} + +template <class ELFT> +void RelocationTable<ELFT>::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->_ctx.isRelativeReloc(ref)) + r.r_addend = writer->addressOfAtom(ref.target()) + ref.addend(); + else + r.r_addend = 0; +} + +template <class ELFT> +void RelocationTable<ELFT>::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(); +} + +template <class ELFT> +uint32_t RelocationTable<ELFT>::getSymbolIndex(const Atom *a) { + return _symbolTable ? _symbolTable->getSymbolTableIndex(a) + : (uint32_t)STN_UNDEF; +} + +template <class ELFT> +DynamicTable<ELFT>::DynamicTable(const ELFLinkingContext &ctx, + TargetLayout<ELFT> &layout, StringRef str, + int32_t order) + : Section<ELFT>(ctx, 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; +} + +template <class ELFT> +std::size_t DynamicTable<ELFT>::addEntry(int64_t tag, uint64_t val) { + Elf_Dyn dyn; + dyn.d_tag = tag; + dyn.d_un.d_val = val; + _entries.push_back(dyn); + this->_fsize = (_entries.size() * sizeof(Elf_Dyn)) + sizeof(Elf_Dyn); + this->_msize = this->_fsize; + return _entries.size() - 1; +} + +template <class ELFT> +void DynamicTable<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + 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); +} + +template <class ELFT> void DynamicTable<ELFT>::createDefaultEntries() { + bool isRela = this->_ctx.isRelaOutputFormat(); + _dt_hash = addEntry(DT_HASH, 0); + _dt_strtab = addEntry(DT_STRTAB, 0); + _dt_symtab = addEntry(DT_SYMTAB, 0); + _dt_strsz = addEntry(DT_STRSZ, 0); + _dt_syment = addEntry(DT_SYMENT, 0); + if (_layout.hasDynamicRelocationTable()) { + _dt_rela = addEntry(isRela ? DT_RELA : DT_REL, 0); + _dt_relasz = addEntry(isRela ? DT_RELASZ : DT_RELSZ, 0); + _dt_relaent = addEntry(isRela ? DT_RELAENT : DT_RELENT, 0); + if (_layout.getDynamicRelocationTable()->canModifyReadonlySection()) + _dt_textrel = addEntry(DT_TEXTREL, 0); + } + if (_layout.hasPLTRelocationTable()) { + _dt_pltrelsz = addEntry(DT_PLTRELSZ, 0); + _dt_pltgot = addEntry(getGotPltTag(), 0); + _dt_pltrel = addEntry(DT_PLTREL, isRela ? DT_RELA : DT_REL); + _dt_jmprel = addEntry(DT_JMPREL, 0); + } +} + +template <class ELFT> void DynamicTable<ELFT>::doPreFlight() { + auto initArray = _layout.findOutputSection(".init_array"); + auto finiArray = _layout.findOutputSection(".fini_array"); + if (initArray) { + _dt_init_array = addEntry(DT_INIT_ARRAY, 0); + _dt_init_arraysz = addEntry(DT_INIT_ARRAYSZ, 0); + } + if (finiArray) { + _dt_fini_array = addEntry(DT_FINI_ARRAY, 0); + _dt_fini_arraysz = addEntry(DT_FINI_ARRAYSZ, 0); + } + if (getInitAtomLayout()) + _dt_init = addEntry(DT_INIT, 0); + if (getFiniAtomLayout()) + _dt_fini = addEntry(DT_FINI, 0); +} + +template <class ELFT> void DynamicTable<ELFT>::finalize() { + 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); + } +} + +template <class ELFT> void DynamicTable<ELFT>::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(); + } +} + +template <class ELFT> +const AtomLayout *DynamicTable<ELFT>::getInitAtomLayout() { + auto al = _layout.findAtomLayoutByName(this->_ctx.initFunction()); + if (al && isa<DefinedAtom>(al->_atom)) + return al; + return nullptr; +} + +template <class ELFT> +const AtomLayout *DynamicTable<ELFT>::getFiniAtomLayout() { + auto al = _layout.findAtomLayoutByName(this->_ctx.finiFunction()); + if (al && isa<DefinedAtom>(al->_atom)) + return al; + return nullptr; +} + +template <class ELFT> +InterpSection<ELFT>::InterpSection(const ELFLinkingContext &ctx, StringRef str, + int32_t order, StringRef interp) + : Section<ELFT>(ctx, 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; +} + +template <class ELFT> +void InterpSection<ELFT>::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()); +} + +template <class ELFT> +HashSection<ELFT>::HashSection(const ELFLinkingContext &ctx, StringRef name, + int32_t order) + : Section<ELFT>(ctx, name, "Dynamic:Hash") { + 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; +} + +template <class ELFT> +void HashSection<ELFT>::addSymbol(StringRef name, uint32_t index) { + SymbolTableEntry ste; + ste._name = name; + ste._index = index; + _entries.push_back(ste); +} + +/// \brief Set the dynamic symbol table +template <class ELFT> +void HashSection<ELFT>::setSymbolTable( + const DynamicSymbolTable<ELFT> *symbolTable) { + _symbolTable = symbolTable; +} + +template <class ELFT> void HashSection<ELFT>::doPreFlight() { + // 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; +} + +template <class ELFT> void HashSection<ELFT>::finalize() { + this->_link = _symbolTable ? _symbolTable->ordinal() : 0; + if (this->_outputSection) + this->_outputSection->setLink(this->_link); +} + +template <class ELFT> +void HashSection<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + Elf_Word bucketChainCounts[2]; + bucketChainCounts[0] = _buckets.size(); + bucketChainCounts[1] = _chains.size(); + std::memcpy(dest, bucketChainCounts, sizeof(bucketChainCounts)); + dest += sizeof(bucketChainCounts); + // write bucket values + std::memcpy(dest, _buckets.data(), _buckets.size() * sizeof(Elf_Word)); + dest += _buckets.size() * sizeof(Elf_Word); + // write chain values + std::memcpy(dest, _chains.data(), _chains.size() * sizeof(Elf_Word)); +} + +template <class ELFT> +EHFrameHeader<ELFT>::EHFrameHeader(const ELFLinkingContext &ctx, StringRef name, + TargetLayout<ELFT> &layout, int32_t order) + : Section<ELFT>(ctx, name, "EHFrameHeader"), _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; +} + +template <class ELFT> void EHFrameHeader<ELFT>::doPreFlight() { + // TODO: Generate a proper binary search table. +} + +template <class ELFT> void EHFrameHeader<ELFT>::finalize() { + OutputSection<ELFT> *s = _layout.findOutputSection(".eh_frame"); + OutputSection<ELFT> *h = _layout.findOutputSection(".eh_frame_hdr"); + if (s && h) + _ehFrameOffset = s->virtualAddr() - (h->virtualAddr() + 4); +} + +template <class ELFT> +void EHFrameHeader<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + 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; +} + +#define INSTANTIATE(klass) \ + template class klass<ELF32LE>; \ + template class klass<ELF32BE>; \ + template class klass<ELF64LE>; \ + template class klass<ELF64BE> + +INSTANTIATE(AtomSection); +INSTANTIATE(DynamicSymbolTable); +INSTANTIATE(DynamicTable); +INSTANTIATE(EHFrameHeader); +INSTANTIATE(HashSection); +INSTANTIATE(InterpSection); +INSTANTIATE(OutputSection); +INSTANTIATE(RelocationTable); +INSTANTIATE(Section); +INSTANTIATE(StringTable); +INSTANTIATE(SymbolTable); + +} // end namespace elf +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/SectionChunks.h b/lib/ReaderWriter/ELF/SectionChunks.h index 03bdb59e65681..b10ba05237ff7 100644 --- a/lib/ReaderWriter/ELF/SectionChunks.h +++ b/lib/ReaderWriter/ELF/SectionChunks.h @@ -11,19 +11,16 @@ #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 "lld/ReaderWriter/AtomLayout.h" +#include "lld/ReaderWriter/ELFLinkingContext.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" @@ -35,29 +32,22 @@ namespace elf { template <class> class OutputSection; using namespace llvm::ELF; template <class ELFT> class Segment; +template <class ELFT> class TargetLayout; /// \brief An ELF section. template <class ELFT> class Section : public Chunk<ELFT> { public: - Section(const ELFLinkingContext &context, StringRef sectionName, + Section(const ELFLinkingContext &ctx, 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) {} + typename Chunk<ELFT>::Kind k = Chunk<ELFT>::Kind::ELFSection); /// \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; - } + virtual bool hasOutputSegment() const { return false; } /// Return if the section is a loadable section that occupies memory virtual bool isLoadableSection() const { return false; } @@ -73,26 +63,21 @@ public: 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; + typename TargetLayout<ELFT>::SegmentType getSegmentType() const { + return _segmentType; } + /// \brief Return the type of content that the section contains + int getContentType() const override; + /// \brief convert the segment type to a String for diagnostics and printing /// purposes - StringRef segmentKindToStr() const; + virtual StringRef segmentKindToStr() const; /// \brief Records the segmentType, that this section belongs to - void setSegmentType(const Layout::SegmentType segmentType) { + void + setSegmentType(const typename TargetLayout<ELFT>::SegmentType segmentType) { this->_segmentType = segmentType; } @@ -100,6 +85,10 @@ public: return nullptr; } + const OutputSection<ELFT> *getOutputSection() const { + return _outputSection; + } + void setOutputSection(OutputSection<ELFT> *os, bool isFirst = false) { _outputSection = os; _isFirstSectionInOutputSection = isFirst; @@ -133,21 +122,21 @@ public: protected: /// \brief OutputSection this Section is a member of, or nullptr. - OutputSection<ELFT> *_outputSection; + OutputSection<ELFT> *_outputSection = nullptr; /// \brief ELF SHF_* flags. - uint64_t _flags; + uint64_t _flags = 0; /// \brief The size of each entity. - uint64_t _entSize; + uint64_t _entSize = 0; /// \brief ELF SHT_* type. - uint32_t _type; + uint32_t _type = 0; /// \brief sh_link field. - uint32_t _link; + uint32_t _link = 0; /// \brief the sh_info field. - uint32_t _info; + uint32_t _info = 0; /// \brief Is this the first section in the output section. - bool _isFirstSectionInOutputSection; + bool _isFirstSectionInOutputSection = false; /// \brief the output ELF segment type of this section. - Layout::SegmentType _segmentType; + typename TargetLayout<ELFT>::SegmentType _segmentType = SHT_NULL; /// \brief Input section name. StringRef _inputSectionName; /// \brief Output section name. @@ -159,65 +148,8 @@ protected: /// \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; - } - } + AtomSection(const ELFLinkingContext &ctx, StringRef sectionName, + int32_t contentType, int32_t permissions, int32_t order); /// Align the offset to the required modulus defined by the atom alignment uint64_t alignOffset(uint64_t offset, DefinedAtom::Alignment &atomAlign); @@ -228,40 +160,27 @@ public: // \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); + virtual const 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; - }); - } + void assignVirtualAddress(uint64_t addr) override; /// \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; - }); - } + void assignFileOffsets(uint64_t offset) override; /// \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; - } + const AtomLayout *findAtomLayoutByName(StringRef name) const override; /// \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; + typedef typename std::vector<AtomLayout *>::iterator atom_iter; range<atom_iter> atoms() { return _atoms; } @@ -276,185 +195,27 @@ protected: llvm::BumpPtrAllocator _alloc; int32_t _contentType; int32_t _contentPermissions; - bool _isLoadedInMemory; - std::vector<lld::AtomLayout *> _atoms; + bool _isLoadedInMemory = true; + std::vector<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; - } + std::string formatError(const std::string &errorStr, const AtomLayout &atom, + const Reference &ref) const; }; -/// 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; + typedef typename std::vector<Section<ELFT> *>::iterator SectionIter; - OutputSection(StringRef name); + OutputSection(StringRef name) : _name(name) {} // Appends a section into the list of sections that are part of this Output // Section - void appendSection(Chunk<ELFT> *c); + void appendSection(Section<ELFT> *c); // Set the OutputSection is associated with a segment void setHasSegment() { _hasSegment = true; } @@ -484,90 +245,48 @@ public: } 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; } + void setType(int64_t type) { _type = type; } + range<SectionIter> 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; } + uint64_t flags() const { return _flags; } + uint64_t memSize() const { 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; + bool _hasSegment = false; + uint64_t _ordinal = 0; + uint64_t _flags = 0; + uint64_t _size = 0; + uint64_t _memSize = 0; + uint64_t _fileOffset = 0; + uint64_t _virtualAddr = 0; + int64_t _shInfo = 0; + int64_t _entSize = 0; + int64_t _link = 0; + uint64_t _alignment = 1; + int64_t _kind = 0; + int64_t _type = 0; + bool _isLoadableSection = false; + std::vector<Section<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> { +template <class ELFT> class StringTable : public Section<ELFT> { public: StringTable(const ELFLinkingContext &, const char *str, int32_t order, bool dynamic = false); @@ -592,68 +311,21 @@ private: return lhs.equals(rhs); } }; - typedef typename llvm::DenseMap<StringRef, uint64_t, - StringRefMappingInfo> StringMapT; + 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; +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); + SymbolTable(const ELFLinkingContext &ctx, const char *str, int32_t order); /// \brief set the number of entries that would exist in the symbol /// table for the current link @@ -666,7 +338,7 @@ public: 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); + const AtomLayout *layout = nullptr); /// \brief Get the symbol table index for an Atom. If it's not in the symbol /// table, return STN_UNDEF. @@ -681,9 +353,9 @@ public: virtual void sortSymbols() { std::stable_sort(_symbolTable.begin(), _symbolTable.end(), - [](const SymbolEntry & A, const SymbolEntry & B) { - return A._symbol.getBinding() < B._symbol.getBinding(); - }); + [](const SymbolEntry &A, const SymbolEntry &B) { + return A._symbol.getBinding() < B._symbol.getBinding(); + }); } virtual void addAbsoluteAtom(Elf_Sym &sym, const AbsoluteAtom *aa, @@ -707,12 +379,11 @@ public: protected: struct SymbolEntry { - SymbolEntry(const Atom *a, const Elf_Sym &sym, - const lld::AtomLayout *layout) + SymbolEntry(const Atom *a, const Elf_Sym &sym, const AtomLayout *layout) : _atom(a), _atomLayout(layout), _symbol(sym) {} const Atom *_atom; - const lld::AtomLayout *_atomLayout; + const AtomLayout *_atomLayout; Elf_Sym _symbol; }; @@ -721,233 +392,23 @@ protected: 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; - } + DynamicSymbolTable(const ELFLinkingContext &ctx, TargetLayout<ELFT> &layout, + const char *str, int32_t order); // 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 addSymbolsToHashTable(); - 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); - } + void finalize() override; protected: - HashSection<ELFT> *_hashTable; + HashSection<ELFT> *_hashTable = nullptr; TargetLayout<ELFT> &_layout; }; @@ -956,118 +417,36 @@ 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; - } - } + RelocationTable(const ELFLinkingContext &ctx, StringRef str, int32_t order); /// \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; - } + uint32_t addRelocation(const DefinedAtom &da, const Reference &r); - 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; - } + bool getRelocationIndex(const Reference &r, uint32_t &res); 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; - } + bool canModifyReadonlySection() const; - void finalize() override { - this->_link = _symbolTable ? _symbolTable->ordinal() : 0; - if (this->_outputSection) - this->_outputSection->setLink(this->_link); - } + void finalize() override; 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; - } - } + llvm::FileOutputBuffer &buffer) override; protected: - const DynamicSymbolTable<ELFT> *_symbolTable; + const DynamicSymbolTable<ELFT> *_symbolTable = nullptr; 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; - } - + const DefinedAtom &atom, const Reference &ref); 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; - } + const Reference &ref); + uint32_t getSymbolIndex(const Atom *a); private: - std::vector<std::pair<const DefinedAtom *, const Reference *> > _relocs; + std::vector<std::pair<const DefinedAtom *, const Reference *>> _relocs; }; template <class ELFT> class HashSection; @@ -1077,125 +456,25 @@ 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; - } + DynamicTable(const ELFLinkingContext &ctx, TargetLayout<ELFT> &layout, + StringRef str, int32_t order); 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; - } + std::size_t addEntry(int64_t tag, uint64_t val); 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); - } - } + llvm::FileOutputBuffer &buffer) override; - 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); - } - } + virtual void createDefaultEntries(); + void doPreFlight() override; /// \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 finalize() override; void setSymbolTable(DynamicSymbolTable<ELFT> *dynsym) { _dynamicSymbolTable = dynsym; @@ -1207,42 +486,7 @@ public: 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(); - } - } + virtual void updateDynamicTable(); protected: EntriesT _entries; @@ -1279,41 +523,18 @@ private: 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 *getInitAtomLayout(); - const AtomLayout *getFiniAtomLayout() { - auto al = _layout.findAtomLayoutByName(this->_context.finiFunction()); - if (al && isa<DefinedAtom>(al->_atom)) - return al; - return nullptr; - } + const AtomLayout *getFiniAtomLayout(); }; 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; - } + InterpSection(const ELFLinkingContext &ctx, StringRef str, int32_t order, + StringRef interp); 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()); - } + llvm::FileOutputBuffer &buffer) override; private: StringRef _interp; @@ -1338,7 +559,6 @@ private: /// * 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; @@ -1346,153 +566,51 @@ template <class ELFT> class HashSection : public Section<ELFT> { }; 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; - } + HashSection(const ELFLinkingContext &ctx, StringRef name, int32_t order); /// \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); - } + void addSymbol(StringRef name, uint32_t index); /// \brief Set the dynamic symbol table - void setSymbolTable(const DynamicSymbolTable<ELFT> *symbolTable) { - _symbolTable = symbolTable; - } + void setSymbolTable(const DynamicSymbolTable<ELFT> *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; - } + void doPreFlight() override; - 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 finalize() override; 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); - } - } + llvm::FileOutputBuffer &buffer) override; private: + typedef + typename llvm::object::ELFDataTypeTypedefHelper<ELFT>::Elf_Word Elf_Word; + std::vector<SymbolTableEntry> _entries; - std::vector<uint32_t> _buckets; - std::vector<uint32_t> _chains; - const DynamicSymbolTable<ELFT> *_symbolTable; + std::vector<Elf_Word> _buckets; + std::vector<Elf_Word> _chains; + const DynamicSymbolTable<ELFT> *_symbolTable = nullptr; }; 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); - } - + EHFrameHeader(const ELFLinkingContext &ctx, StringRef name, + TargetLayout<ELFT> &layout, int32_t order); + void doPreFlight() override; + void finalize() override; 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; - } + llvm::FileOutputBuffer &buffer) override; private: - int32_t _ehFrameOffset; + int32_t _ehFrameOffset = 0; TargetLayout<ELFT> &_layout; }; + } // end namespace elf } // end namespace lld -#endif +#endif // LLD_READER_WRITER_ELF_SECTION_CHUNKS_H diff --git a/lib/ReaderWriter/ELF/SegmentChunks.cpp b/lib/ReaderWriter/ELF/SegmentChunks.cpp new file mode 100644 index 0000000000000..99449f7d45aa5 --- /dev/null +++ b/lib/ReaderWriter/ELF/SegmentChunks.cpp @@ -0,0 +1,519 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// + +#include "SegmentChunks.h" +#include "TargetLayout.h" + +namespace lld { +namespace elf { + +template <class ELFT> +bool SegmentSlice<ELFT>::compare_slices(SegmentSlice<ELFT> *a, + SegmentSlice<ELFT> *b) { + return a->startSection() < b->startSection(); +} + +template <class ELFT> +Segment<ELFT>::Segment(const ELFLinkingContext &ctx, StringRef name, + const typename TargetLayout<ELFT>::SegmentType type) + : Chunk<ELFT>(name, Chunk<ELFT>::Kind::ELFSegment, ctx), _segmentType(type), + _flags(0), _atomflags(0), _segmentFlags(false) { + this->_alignment = 1; + this->_fsize = 0; + _outputMagic = ctx.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; + } +} + +// This function actually is used, but not in all instantiations of Segment. +LLVM_ATTRIBUTE_UNUSED +static DefinedAtom::ContentPermissions toAtomPermsSegment(uint64_t flags) { + switch (flags & (llvm::ELF::PF_R | llvm::ELF::PF_W | llvm::ELF::PF_X)) { + case llvm::ELF::PF_R | llvm::ELF::PF_W | llvm::ELF::PF_X: + return DefinedAtom::permRWX; + case llvm::ELF::PF_R | llvm::ELF::PF_X: + return DefinedAtom::permR_X; + case llvm::ELF::PF_R: + return DefinedAtom::permR__; + case llvm::ELF::PF_R | llvm::ELF::PF_W: + 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 (this->_alignment < section->alignment()) + this->_alignment = section->alignment(); + + if (_segmentFlags) + return; + if (_flags < section->getFlags()) + _flags |= section->getFlags(); + if (_atomflags < toAtomPerms(_flags)) + _atomflags = toAtomPerms(_flags); +} + +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->_ctx.alignSegments(); + uint64_t p_align = this->_ctx.getPageSize(); + uint64_t lastVirtualAddress = 0; + + this->setFileOffset(startOffset); + bool changeOffset = false; + uint64_t newOffset = 0; + for (auto &slice : slices()) { + bool isFirstSection = true; + for (auto section : slice->sections()) { + // Handle linker script expressions, which may change the offset + if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(section)) { + if (!isFirstSection) { + changeOffset = true; + newOffset = fileOffset + expr->virtualAddr() - lastVirtualAddress; + } + continue; + } + if (changeOffset) { + changeOffset = false; + fileOffset = newOffset; + } + // 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); + // 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->_ctx.getPageSize()); + isDataPageAlignedForNMagic = true; + } + if (isFirstSection) { + slice->setFileOffset(fileOffset); + isFirstSection = false; + curSliceFileOffset = fileOffset; + } + section->setFileOffset(fileOffset); + fileOffset += section->fileSize(); + lastVirtualAddress = section->virtualAddr() + section->memSize(); + } + changeOffset = false; + 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 isDataPageAlignedForNMagic = false; + uint64_t startAddr = addr; + SegmentSlice<ELFT> *slice = nullptr; + uint64_t tlsStartAddr = 0; + bool alignSegments = this->_ctx.alignSegments(); + StringRef prevOutputSectionName = StringRef(); + uint64_t tbssMemsize = 0; + + // 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. + auto si = _sections.begin(); + if (si != _sections.end()) { + 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->_ctx.getPageSize()); + } else if (needAlign(*si)) { + // If the linker outputmagic is set to OutputMagic::NMAGIC, align the + // Data to a page boundary. + startAddr = llvm::RoundUpToAlignment(startAddr, this->_ctx.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() != TargetLayout<ELFT>::ORDER_TBSS) { + curSliceSize = (*si)->memSize(); + tbssMemsize = 0; + } else { + tbssMemsize = (*si)->memSize(); + } + ++currSection; + ++si; + } + + uint64_t scriptAddr = 0; + bool forceScriptAddr = false; + for (auto e = _sections.end(); si != e; ++si) { + 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->_ctx.getPageSize()); + isDataPageAlignedForNMagic = true; + } + uint64_t newAddr = llvm::RoundUpToAlignment( + forceScriptAddr ? scriptAddr : curAddr, (*si)->alignment()); + forceScriptAddr = false; + + // Handle linker script expressions, which may force an address change if + // the expression assigns to "." + if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(*si)) { + uint64_t oldAddr = newAddr; + expr->evalExpr(newAddr); + if (oldAddr != newAddr) { + forceScriptAddr = true; + scriptAddr = newAddr; + } + (*si)->setVirtualAddr(newAddr); + continue; + } + Section<ELFT> *sec = dyn_cast<Section<ELFT>>(*si); + StringRef curOutputSectionName = + sec ? sec->outputSectionName() : (*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->_ctx.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; + if ((*si)->order() == TargetLayout<ELFT>::ORDER_TBSS) + curSliceAddress += tbssMemsize; + (*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(); + if ((*si)->order() == TargetLayout<ELFT>::ORDER_TBSS) + newAddr += tbssMemsize; + (*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() != TargetLayout<ELFT>::ORDER_TBSS) { + curSliceSize = newAddr - curSliceAddress + (*si)->memSize(); + tbssMemsize = 0; + } else { + // Although TBSS section does not contribute to memory of any segment, + // we still need to keep track its total size to correct write it + // down. Since it is done based on curSliceAddress, we need to add + // add it to virtual address. + tbssMemsize = (*si)->memSize(); + } + } + 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(startAddr); + 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 { + if (_segmentFlags) + return (int64_t)_flags; + + 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; +} + +template <class ELFT> void Segment<ELFT>::setSegmentFlags(uint64_t flags) { + assert(!_segmentFlags && "Segment flags have already been set"); + _segmentFlags = true; + _flags = flags; + _atomflags = toAtomPermsSegment(flags); +} + +template <class ELFT> void Segment<ELFT>::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; + } +} + +template <class ELFT> int Segment<ELFT>::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; + } +} + +template <class ELFT> int64_t Segment<ELFT>::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; + } +} + +/// \brief Check if the chunk needs to be aligned +template <class ELFT> bool Segment<ELFT>::needAlign(Chunk<ELFT> *chunk) const { + if (chunk->getContentType() == Chunk<ELFT>::ContentType::Data && + _outputMagic == ELFLinkingContext::OutputMagic::NMAGIC) + return true; + return false; +} + +template <class ELFT> void ProgramHeaderSegment<ELFT>::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(); +} + +#define INSTANTIATE(klass) \ + template class klass<ELF32LE>; \ + template class klass<ELF32BE>; \ + template class klass<ELF64LE>; \ + template class klass<ELF64BE> + +INSTANTIATE(ExpressionChunk); +INSTANTIATE(ProgramHeaderSegment); +INSTANTIATE(Segment); +INSTANTIATE(SegmentSlice); + +} // end namespace elf +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/SegmentChunks.h b/lib/ReaderWriter/ELF/SegmentChunks.h index f2a975aaeed0b..b444f44c04eb5 100644 --- a/lib/ReaderWriter/ELF/SegmentChunks.h +++ b/lib/ReaderWriter/ELF/SegmentChunks.h @@ -11,16 +11,13 @@ #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" @@ -29,7 +26,7 @@ namespace lld { namespace elf { -template <typename ELFT> class DefaultLayout; +template <typename ELFT> class TargetLayout; /// \brief A segment can be divided into segment slices /// depending on how the segments can be split @@ -38,8 +35,6 @@ 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; } @@ -49,12 +44,10 @@ public: // 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 @@ -75,9 +68,7 @@ public: void setAlign(uint64_t align) { _alignment = align; } - static bool compare_slices(SegmentSlice<ELFT> *a, SegmentSlice<ELFT> *b) { - return a->startSection() < b->startSection(); - } + static bool compare_slices(SegmentSlice<ELFT> *a, SegmentSlice<ELFT> *b); range<SectionIter> sections() { return _sections; } @@ -100,8 +91,8 @@ 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); + Segment(const ELFLinkingContext &ctx, StringRef name, + const typename TargetLayout<ELFT>::SegmentType type); /// \brief the Order of segments that appear in the output file enum SegmentOrder { @@ -144,37 +135,21 @@ public: // Write the Segment void write(ELFWriter *writer, TargetLayout<ELFT> &layout, - llvm::FileOutputBuffer &buffer); + llvm::FileOutputBuffer &buffer) override; int64_t flags() const; + // Set segment flags directly. + void setSegmentFlags(uint64_t flags); + /// 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; - } - } + void finalize() override; // For LLVM RTTI static bool classof(const Chunk<ELFT> *c) { @@ -185,76 +160,26 @@ public: int32_t sectionCount() const { return _sections.size(); } /// \brief, this function returns the type of segment (PT_*) - Layout::SegmentType segmentType() { return _segmentType; } + typename TargetLayout<ELFT>::SegmentType segmentType() const { + 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 getContentType() const override; + int pageSize() const { return this->_ctx.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 atomflags() const; 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; - } + bool needAlign(Chunk<ELFT> *chunk) const; // Cached value of outputMagic ELFLinkingContext::OutputMagic _outputMagic; @@ -263,9 +188,10 @@ protected: /// \brief Section or some other chunk type. std::vector<Chunk<ELFT> *> _sections; std::vector<SegmentSlice<ELFT> *> _segmentSlices; - Layout::SegmentType _segmentType; + typename TargetLayout<ELFT>::SegmentType _segmentType; uint64_t _flags; int64_t _atomflags; + bool _segmentFlags; llvm::BumpPtrAllocator _segmentAllocate; }; @@ -286,10 +212,9 @@ public: 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); @@ -304,383 +229,18 @@ private: /// 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) { + ProgramHeaderSegment(const ELFLinkingContext &ctx) + : Segment<ELFT>(ctx, "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(); - } - + void finalize() override; }; -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 +#endif // LLD_READER_WRITER_ELF_SEGMENT_CHUNKS_H diff --git a/lib/ReaderWriter/ELF/TargetHandler.h b/lib/ReaderWriter/ELF/TargetHandler.h index ca7a442276d18..406ac670fc836 100644 --- a/lib/ReaderWriter/ELF/TargetHandler.h +++ b/lib/ReaderWriter/ELF/TargetHandler.h @@ -6,78 +6,27 @@ // 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 "lld/Core/Error.h" +#include "llvm/ADT/Twine.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")); + return make_dynamic_error_code("Unhandled reference type"); } inline std::error_code make_out_of_range_reloc_error() { - return make_dynamic_error_code(Twine("Relocation out of range")); + return make_dynamic_error_code("Relocation out of range"); +} + +inline std::error_code make_unaligned_range_reloc_error() { + return make_dynamic_error_code("Relocation not aligned"); } } // end namespace elf diff --git a/lib/ReaderWriter/ELF/TargetLayout.cpp b/lib/ReaderWriter/ELF/TargetLayout.cpp new file mode 100644 index 0000000000000..09c49f62d64f1 --- /dev/null +++ b/lib/ReaderWriter/ELF/TargetLayout.cpp @@ -0,0 +1,747 @@ +//===- lib/ReaderWriter/ELF/TargetLayout.cpp ------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TargetLayout.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Errc.h" + +namespace lld { +namespace elf { + +template <class ELFT> +typename TargetLayout<ELFT>::SectionOrder +TargetLayout<ELFT>::getSectionOrder(StringRef name, int32_t contentType, + int32_t contentPermissions) { + switch (contentType) { + case DefinedAtom::typeResolver: + case DefinedAtom::typeCode: + return llvm::StringSwitch<typename TargetLayout<ELFT>::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<typename TargetLayout<ELFT>::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<typename TargetLayout<ELFT>::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 TargetLayout<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 +TargetLayout<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> +typename TargetLayout<ELFT>::SegmentType +TargetLayout<ELFT>::getSegmentType(const 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 TargetLayout<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> * +TargetLayout<ELFT>::createSection(StringRef sectionName, int32_t contentType, + DefinedAtom::ContentPermissions permissions, + SectionOrder sectionOrder) { + return new (_allocator) AtomSection<ELFT>(_ctx, sectionName, contentType, + permissions, sectionOrder); +} + +template <class ELFT> +AtomSection<ELFT> * +TargetLayout<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 AtomLayout *> TargetLayout<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 (_ctx.isDynamicRelocation(*reloc)) { + getDynamicRelocationTable()->addRelocation(*definedAtom, *reloc); + isLocalReloc = false; + } else if (_ctx.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 (_ctx.isCopyRelocation(*reloc)) { + _copiedDynSymNames.insert(definedAtom->name()); + continue; + } + + _referencedDynAtoms.insert(reloc->target()); + } + return section->appendAtom(atom); + } + + const AbsoluteAtom *absoluteAtom = cast<AbsoluteAtom>(atom); + // Absolute atoms are not part of any section, they are global for the whole + // link + _absoluteAtoms.push_back( + new (_allocator) AtomLayout(absoluteAtom, 0, absoluteAtom->value())); + return _absoluteAtoms.back(); +} + +/// Output sections with the same name into a OutputSection +template <class ELFT> void TargetLayout<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(section); + } +} + +template <class ELFT> +std::vector<typename TargetLayout<ELFT>::SegmentKey> +TargetLayout<ELFT>::getSegmentsForSection(const OutputSection<ELFT> *os, + const Section<ELFT> *sec) const { + std::vector<SegmentKey> segKeys; + auto phdrs = _linkerScriptSema.getPHDRsForOutputSection(os->name()); + if (!phdrs.empty()) { + if (phdrs.size() == 1 && phdrs[0]->isNone()) { + segKeys.emplace_back("NONE", llvm::ELF::PT_NULL, 0, false); + return segKeys; + } + + for (auto phdr : phdrs) { + segKeys.emplace_back(phdr->name(), phdr->type(), phdr->flags(), true); + } + return segKeys; + } + + uint64_t flags = getLookupSectionFlags(os); + int64_t segmentType = getSegmentType(sec); + StringRef segmentName = sec->segmentKindToStr(); + + // We need a separate segment for sections that don't have + // the segment type to be PT_LOAD + if (segmentType != llvm::ELF::PT_LOAD) + segKeys.emplace_back(segmentName, segmentType, flags, false); + + if (segmentType == llvm::ELF::PT_NULL) + return segKeys; + + // If the output magic is set to OutputMagic::NMAGIC or + // OutputMagic::OMAGIC, Place the data alongside text in one single + // segment + ELFLinkingContext::OutputMagic outputMagic = _ctx.getOutputMagic(); + if (outputMagic == ELFLinkingContext::OutputMagic::NMAGIC || + outputMagic == ELFLinkingContext::OutputMagic::OMAGIC) + flags = + llvm::ELF::SHF_EXECINSTR | llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE; + + segKeys.emplace_back("LOAD", llvm::ELF::PT_LOAD, flags, false); + return segKeys; +} + +template <class ELFT> +uint64_t +TargetLayout<ELFT>::getLookupSectionFlags(const OutputSection<ELFT> *os) const { + uint64_t flags = os->flags(); + if (!(flags & llvm::ELF::SHF_WRITE) && _ctx.mergeRODataToTextSegment()) + flags &= ~llvm::ELF::SHF_EXECINSTR; + + // Merge string sections into Data segment itself + flags &= ~(llvm::ELF::SHF_STRINGS | llvm::ELF::SHF_MERGE); + + // Merge the TLS section into the DATA segment itself + flags &= ~(llvm::ELF::SHF_TLS); + return flags; +} + +template <class ELFT> void TargetLayout<ELFT>::assignSectionsToSegments() { + ScopedTask task(getDefaultDomain(), "assignSectionsToSegments"); + // 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 section : osi->sections()) { + if (!hasOutputSegment(section)) + continue; + + osi->setLoadableSection(section->isLoadableSection()); + osi->setHasSegment(); + + auto segKeys = getSegmentsForSection(osi, section); + assert(!segKeys.empty() && "Must always be at least one segment"); + section->setSegmentType(segKeys[0]._type); + + for (auto key : segKeys) { + // Try to find non-load (real) segment type if possible + if (key._type != llvm::ELF::PT_LOAD) + section->setSegmentType(key._type); + + const std::pair<SegmentKey, Segment<ELFT> *> currentSegment(key, + nullptr); + std::pair<typename SegmentMapT::iterator, bool> segmentInsert( + _segmentMap.insert(currentSegment)); + Segment<ELFT> *segment; + if (!segmentInsert.second) { + segment = segmentInsert.first->second; + } else { + segment = new (_allocator) Segment<ELFT>(_ctx, key._name, key._type); + if (key._segmentFlags) + segment->setSegmentFlags(key._flags); + segmentInsert.first->second = segment; + _segments.push_back(segment); + } + if (key._type == llvm::ELF::PT_LOAD) { + // 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); + } + } + } + + // Default values if no linker script is available + bool hasProgramSegment = _ctx.isDynamic() && !_ctx.isDynamicLibrary(); + bool hasElfHeader = true; + bool hasProgramHeader = true; + uint64_t segmentFlags = 0; + + // Check if linker script has PHDRS and program segment defined + if (_linkerScriptSema.hasPHDRs()) { + if (auto p = _linkerScriptSema.getProgramPHDR()) { + hasProgramSegment = true; + hasElfHeader = p->hasFileHdr(); + hasProgramHeader = p->hasPHDRs(); + segmentFlags = p->flags(); + } else { + hasProgramSegment = false; + hasElfHeader = false; + hasProgramHeader = false; + } + } + + if (hasProgramSegment) { + Segment<ELFT> *segment = new (_allocator) ProgramHeaderSegment<ELFT>(_ctx); + _segments.push_back(segment); + if (segmentFlags) + segment->setSegmentFlags(segmentFlags); + if (hasElfHeader) + segment->append(_elfHeader); + if (hasProgramHeader) + segment->append(_programHeader); + } +} + +template <class ELFT> void TargetLayout<ELFT>::sortSegments() { + std::sort(_segments.begin(), _segments.end(), Segment<ELFT>::compareSegments); +} + +template <class ELFT> void TargetLayout<ELFT>::assignVirtualAddress() { + if (_segments.empty()) + return; + + sortSegments(); + + uint64_t baseAddress = _ctx.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 && TargetLayout<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 TargetLayout<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 && TargetLayout<ELFT>::hasOutputSegment(section)) + continue; + fileoffset = llvm::RoundUpToAlignment(fileoffset, si->alignment()); + si->setFileOffset(fileoffset); + si->setVirtualAddr(0); + fileoffset += si->fileSize(); + } +} + +template <class ELFT> void TargetLayout<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> +const AtomLayout * +TargetLayout<ELFT>::findAtomLayoutByName(StringRef name) const { + for (auto sec : _sections) + if (auto section = dyn_cast<Section<ELFT>>(sec)) + if (auto *al = section->findAtomLayoutByName(name)) + return al; + return nullptr; +} + +template <class ELFT> +void TargetLayout<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->_ctx, expr); + segment->append(expChunk); + } +} + +template <class ELFT> +RelocationTable<ELFT> *TargetLayout<ELFT>::getDynamicRelocationTable() { + if (!_dynamicRelocationTable) { + _dynamicRelocationTable = createRelocationTable( + _ctx.isRelaOutputFormat() ? ".rela.dyn" : ".rel.dyn", + ORDER_DYNAMIC_RELOCS); + addSection(_dynamicRelocationTable.get()); + } + return _dynamicRelocationTable.get(); +} + +template <class ELFT> +RelocationTable<ELFT> *TargetLayout<ELFT>::getPLTRelocationTable() { + if (!_pltRelocationTable) { + _pltRelocationTable = createRelocationTable( + _ctx.isRelaOutputFormat() ? ".rela.plt" : ".rel.plt", + ORDER_DYNAMIC_PLT_RELOCS); + addSection(_pltRelocationTable.get()); + } + return _pltRelocationTable.get(); +} + +template <class ELFT> uint64_t TargetLayout<ELFT>::getTLSSize() const { + for (const auto &phdr : *_programHeader) + if (phdr->p_type == llvm::ELF::PT_TLS) + return phdr->p_memsz; + return 0; +} + +template class TargetLayout<ELF32LE>; +template class TargetLayout<ELF32BE>; +template class TargetLayout<ELF64LE>; +template class TargetLayout<ELF64BE>; + +} // end namespace elf +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/TargetLayout.h b/lib/ReaderWriter/ELF/TargetLayout.h index ab7a7890a2747..52512f8e279e2 100644 --- a/lib/ReaderWriter/ELF/TargetLayout.h +++ b/lib/ReaderWriter/ELF/TargetLayout.h @@ -7,21 +7,320 @@ // //===----------------------------------------------------------------------===// -#ifndef LLD_READER_WRITER_ELF_TARGET_LAYOUT_H -#define LLD_READER_WRITER_ELF_TARGET_LAYOUT_H +#ifndef LLD_READER_WRITER_ELF_DEFAULT_LAYOUT_H +#define LLD_READER_WRITER_ELF_DEFAULT_LAYOUT_H -#include "DefaultLayout.h" -#include "lld/Core/LLVM.h" +#include "Atoms.h" +#include "HeaderChunks.h" +#include "SectionChunks.h" +#include "SegmentChunks.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include <unordered_map> 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> { + +/// \brief The TargetLayout 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 TargetLayout +/// class +template <class ELFT> class TargetLayout { +public: + typedef uint32_t SectionOrder; + typedef uint32_t SegmentType; + + // 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: - TargetLayout(ELFLinkingContext &context) : DefaultLayout<ELFT>(context) {} + + // 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; + + // Properties used during segment creation + struct SegmentKey { + SegmentKey(StringRef name, int64_t type, uint64_t flags, bool segFlags) + : _name(name), _type(type), _flags(flags), + _segmentFlags(segFlags && flags != 0) {} + StringRef _name = ""; + int64_t _type = 0; + uint64_t _flags = 0; + bool _segmentFlags = false; + }; + + struct SegmentKeyHash { + int64_t operator()(const SegmentKey &k) const { + return llvm::hash_combine(k._name, k._type, k._flags); + } + }; + + struct SegmentKeyEq { + bool operator()(const SegmentKey &lhs, const SegmentKey &rhs) const { + return ((lhs._name == rhs._name) && (lhs._type == rhs._type) && + (lhs._flags == rhs._flags)); + } + }; + + // Output Sections contain the map of Section names 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<SegmentKey, Segment<ELFT> *, SegmentKeyHash, + SegmentKeyEq> SegmentMapT; + + typedef typename std::vector<AtomLayout *>::iterator AbsoluteAtomIterT; + + typedef llvm::DenseSet<const Atom *> AtomSetT; + + TargetLayout(ELFLinkingContext &ctx) + : _ctx(ctx), _linkerScriptSema(ctx.linkerScriptSema()) {} + + virtual ~TargetLayout() = default; + + /// \brief Return the section order for a input section + virtual SectionOrder getSectionOrder(StringRef name, int32_t contentType, + int32_t contentPermissions); + + /// \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 SegmentType getSegmentType(const 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); + + /// \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 AtomLayout *> addAtom(const Atom *atom); + + /// \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 + AtomLayout *findAbsoluteAtom(StringRef name) { + auto iter = std::find_if( + _absoluteAtoms.begin(), _absoluteAtoms.end(), + [=](const AtomLayout *a) { return a->_atom->name() == name; }); + if (iter == _absoluteAtoms.end()) + return nullptr; + return *iter; + } + + // Output sections with the same name into a OutputSection + void createOutputSections(); + + // Query for segments based on output and input sections + std::vector<SegmentKey> getSegmentsForSection(const OutputSection<ELFT> *os, + const Section<ELFT> *sec) const; + + /// \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); + + /// \brief associates a section to a segment + virtual void assignSectionsToSegments(); + + /// \brief associates a virtual address to the segment, section, and the atom + virtual void assignVirtualAddress(); + + 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(); + } + + /// \brief find the Atom in the current layout + virtual const AtomLayout *findAtomLayoutByName(StringRef name) const; + + 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(); + + /// \brief Get or create the PLT relocation table. Referenced by DT_JMPREL. + RelocationTable<ELFT> *getPLTRelocationTable(); + + uint64_t getTLSSize() const; + + bool isReferencedByDefinedAtom(const Atom *a) const { + return _referencedDynAtoms.count(a); + } + + bool isCopied(const SharedLibraryAtom *sla) const { + return _copiedDynSymNames.count(sla->name()); + } + +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>(_ctx, name, order)); + } + + virtual uint64_t getLookupSectionFlags(const OutputSection<ELFT> *os) const; + + /// \brief Sort segements stored in the _segments + virtual void sortSegments(); + +protected: + llvm::BumpPtrAllocator _allocator; + SectionMapT _sectionMap; + OutputSectionMapT _outputSectionMap; + 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<AtomLayout *> _absoluteAtoms; + AtomSetT _referencedDynAtoms; + llvm::StringSet<> _copiedDynSymNames; + ELFLinkingContext &_ctx; + script::Sema &_linkerScriptSema; }; + } // end namespace elf } // end namespace lld diff --git a/lib/ReaderWriter/ELF/Writer.cpp b/lib/ReaderWriter/ELF/Writer.cpp index 3071827e07d07..1c5d9766e9c52 100644 --- a/lib/ReaderWriter/ELF/Writer.cpp +++ b/lib/ReaderWriter/ELF/Writer.cpp @@ -16,8 +16,8 @@ using namespace llvm::object; namespace lld { -std::unique_ptr<Writer> createWriterELF(TargetHandlerBase *handler) { - return std::move(handler->getWriter()); +std::unique_ptr<Writer> createWriterELF(const ELFLinkingContext &ctx) { + return ctx.getTargetHandler().getWriter(); } } // namespace lld diff --git a/lib/ReaderWriter/ELF/Writer.h b/lib/ReaderWriter/ELF/Writer.h index 1e819467c558f..8b3e8f90638a2 100644 --- a/lib/ReaderWriter/ELF/Writer.h +++ b/lib/ReaderWriter/ELF/Writer.h @@ -19,15 +19,12 @@ namespace elf { /// 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; + std::error_code writeFile(const File &file, StringRef path) override = 0; /// \brief Get the virtual address of \p atom after layout. virtual uint64_t addressOfAtom(const Atom *atom) = 0; @@ -35,4 +32,4 @@ public: } // end namespace elf } // end namespace lld -#endif +#endif // LLD_READER_WRITER_ELF_WRITER_H diff --git a/lib/ReaderWriter/ELF/X86/Makefile b/lib/ReaderWriter/ELF/X86/Makefile deleted file mode 100644 index 058d5133eaba2..0000000000000 --- a/lib/ReaderWriter/ELF/X86/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- 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 index 86376295bec43..dd2184d7201ee 100644 --- a/lib/ReaderWriter/ELF/X86/X86DynamicLibraryWriter.h +++ b/lib/ReaderWriter/ELF/X86/X86DynamicLibraryWriter.h @@ -15,50 +15,27 @@ namespace lld { namespace elf { -template <class ELFT> -class X86DynamicLibraryWriter : public DynamicLibraryWriter<ELFT> { +class X86DynamicLibraryWriter : public DynamicLibraryWriter<ELF32LE> { public: - X86DynamicLibraryWriter(X86LinkingContext &context, - X86TargetLayout<ELFT> &layout); + X86DynamicLibraryWriter(X86LinkingContext &ctx, + TargetLayout<ELF32LE> &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; + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; }; -template <class ELFT> -X86DynamicLibraryWriter<ELFT>::X86DynamicLibraryWriter( - X86LinkingContext &context, X86TargetLayout<ELFT> &layout) - : DynamicLibraryWriter<ELFT>(context, layout), - _gotFile(new GOTFile(context)), _context(context), _x86Layout(layout) {} +X86DynamicLibraryWriter::X86DynamicLibraryWriter(X86LinkingContext &ctx, + TargetLayout<ELF32LE> &layout) + : DynamicLibraryWriter(ctx, layout) {} -template <class ELFT> -bool X86DynamicLibraryWriter<ELFT>::createImplicitFiles( +void X86DynamicLibraryWriter::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; + DynamicLibraryWriter::createImplicitFiles(result); + auto gotFile = llvm::make_unique<SimpleFile>("GOTFile"); + gotFile->addAtom(*new (gotFile->allocator()) GlobalOffsetTableAtom(*gotFile)); + gotFile->addAtom(*new (gotFile->allocator()) DynamicAtom(*gotFile)); + result.push_back(std::move(gotFile)); } } // namespace elf diff --git a/lib/ReaderWriter/ELF/X86/X86ELFFile.h b/lib/ReaderWriter/ELF/X86/X86ELFFile.h deleted file mode 100644 index 621c38c435054..0000000000000 --- a/lib/ReaderWriter/ELF/X86/X86ELFFile.h +++ /dev/null @@ -1,41 +0,0 @@ -//===- 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 deleted file mode 100644 index 96186c5eb0248..0000000000000 --- a/lib/ReaderWriter/ELF/X86/X86ELFReader.h +++ /dev/null @@ -1,62 +0,0 @@ -//===- 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 index 68acc06c2261e..70aabde3ad2c8 100644 --- a/lib/ReaderWriter/ELF/X86/X86ExecutableWriter.h +++ b/lib/ReaderWriter/ELF/X86/X86ExecutableWriter.h @@ -15,40 +15,22 @@ namespace lld { namespace elf { -template <class ELFT> -class X86ExecutableWriter : public ExecutableWriter<ELFT> { +class X86ExecutableWriter : public ExecutableWriter<ELF32LE> { public: - X86ExecutableWriter(X86LinkingContext &context, - X86TargetLayout<ELFT> &layout); + X86ExecutableWriter(X86LinkingContext &ctx, TargetLayout<ELF32LE> &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; + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; }; -template <class ELFT> -X86ExecutableWriter<ELFT>::X86ExecutableWriter(X86LinkingContext &context, - X86TargetLayout<ELFT> &layout) - : ExecutableWriter<ELFT>(context, layout), _context(context), - _x86Layout(layout) {} +X86ExecutableWriter::X86ExecutableWriter(X86LinkingContext &ctx, + TargetLayout<ELF32LE> &layout) + : ExecutableWriter(ctx, layout) {} -template <class ELFT> -bool X86ExecutableWriter<ELFT>::createImplicitFiles( +void X86ExecutableWriter::createImplicitFiles( std::vector<std::unique_ptr<File>> &result) { - ExecutableWriter<ELFT>::createImplicitFiles(result); - return true; + ExecutableWriter::createImplicitFiles(result); } } // namespace elf diff --git a/lib/ReaderWriter/ELF/X86/X86LinkingContext.cpp b/lib/ReaderWriter/ELF/X86/X86LinkingContext.cpp index 26d715cf29534..dc45efc390fd9 100644 --- a/lib/ReaderWriter/ELF/X86/X86LinkingContext.cpp +++ b/lib/ReaderWriter/ELF/X86/X86LinkingContext.cpp @@ -14,15 +14,26 @@ #include "llvm/Support/ErrorOr.h" using namespace lld; +using namespace lld::elf; std::unique_ptr<ELFLinkingContext> -elf::X86LinkingContext::create(llvm::Triple triple) { +elf::createX86LinkingContext(llvm::Triple triple) { if (triple.getArch() == llvm::Triple::x86) - return std::unique_ptr<ELFLinkingContext>( - new elf::X86LinkingContext(triple)); + return llvm::make_unique<X86LinkingContext>(triple); return nullptr; } -elf::X86LinkingContext::X86LinkingContext(llvm::Triple triple) - : ELFLinkingContext(triple, std::unique_ptr<TargetHandlerBase>( - new X86TargetHandler(*this))) {} +X86LinkingContext::X86LinkingContext(llvm::Triple triple) + : ELFLinkingContext(triple, llvm::make_unique<X86TargetHandler>(*this)) {} + +static const Registry::KindStrings kindStrings[] = { +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), +#include "llvm/Support/ELFRelocs/i386.def" +#undef ELF_RELOC + LLD_KIND_STRING_END +}; + +void X86LinkingContext::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, Reference::KindArch::x86, + kindStrings); +} diff --git a/lib/ReaderWriter/ELF/X86/X86LinkingContext.h b/lib/ReaderWriter/ELF/X86/X86LinkingContext.h index ff424f411aae5..f6ab3e980d7d4 100644 --- a/lib/ReaderWriter/ELF/X86/X86LinkingContext.h +++ b/lib/ReaderWriter/ELF/X86/X86LinkingContext.h @@ -19,7 +19,9 @@ namespace elf { class X86LinkingContext final : public ELFLinkingContext { public: static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + int getMachineType() const override { return llvm::ELF::EM_386; } X86LinkingContext(llvm::Triple); + void registerRelocationNames(Registry &r) override; /// \brief X86 has only two relative relocation /// a) for supporting IFUNC relocs - R_386_IRELATIVE diff --git a/lib/ReaderWriter/ELF/X86/X86RelocationHandler.cpp b/lib/ReaderWriter/ELF/X86/X86RelocationHandler.cpp index da5a24c6ec37d..15774bc331234 100644 --- a/lib/ReaderWriter/ELF/X86/X86RelocationHandler.cpp +++ b/lib/ReaderWriter/ELF/X86/X86RelocationHandler.cpp @@ -15,7 +15,6 @@ 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); @@ -25,33 +24,31 @@ static int reloc32(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { /// \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); + 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, + ELFWriter &writer, llvm::FileOutputBuffer &buf, const 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(); + uint8_t *loc = atomContent + ref.offsetInAtom(); + uint64_t target = writer.addressOfAtom(ref.target()); + uint64_t reloc = atom._virtualAddr + ref.offsetInAtom(); if (ref.kindNamespace() != Reference::KindNamespace::ELF) return std::error_code(); assert(ref.kindArch() == Reference::KindArch::x86); switch (ref.kindValue()) { case R_386_32: - reloc32(location, relocVAddress, targetVAddress, ref.addend()); + reloc32(loc, reloc, target, ref.addend()); break; case R_386_PC32: - relocPC32(location, relocVAddress, targetVAddress, ref.addend()); + relocPC32(loc, reloc, target, 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 index f161cdd55983d..1131635c6735a 100644 --- a/lib/ReaderWriter/ELF/X86/X86RelocationHandler.h +++ b/lib/ReaderWriter/ELF/X86/X86RelocationHandler.h @@ -10,16 +10,15 @@ #ifndef X86_X86_RELOCATION_HANDLER_H #define X86_X86_RELOCATION_HANDLER_H -#include "X86TargetHandler.h" +#include "lld/ReaderWriter/ELFLinkingContext.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 AtomLayout &, const Reference &) const override; }; diff --git a/lib/ReaderWriter/ELF/X86/X86TargetHandler.cpp b/lib/ReaderWriter/ELF/X86/X86TargetHandler.cpp index 22d9182314248..c01ed7258f1c5 100644 --- a/lib/ReaderWriter/ELF/X86/X86TargetHandler.cpp +++ b/lib/ReaderWriter/ELF/X86/X86TargetHandler.cpp @@ -19,13 +19,11 @@ using namespace elf; using namespace llvm::ELF; std::unique_ptr<Writer> X86TargetHandler::getWriter() { - switch (_x86LinkingContext.getOutputELFType()) { + switch (_ctx.getOutputELFType()) { case llvm::ELF::ET_EXEC: - return std::unique_ptr<Writer>(new X86ExecutableWriter<X86ELFType>( - _x86LinkingContext, *_x86TargetLayout.get())); + return llvm::make_unique<X86ExecutableWriter>(_ctx, *_targetLayout); case llvm::ELF::ET_DYN: - return std::unique_ptr<Writer>(new X86DynamicLibraryWriter<X86ELFType>( - _x86LinkingContext, *_x86TargetLayout.get())); + return llvm::make_unique<X86DynamicLibraryWriter>(_ctx, *_targetLayout); case llvm::ELF::ET_REL: llvm_unreachable("TODO: support -r mode"); default: @@ -33,21 +31,6 @@ std::unique_ptr<Writer> X86TargetHandler::getWriter() { } } -#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()) {} +X86TargetHandler::X86TargetHandler(X86LinkingContext &ctx) + : _ctx(ctx), _targetLayout(new TargetLayout<ELF32LE>(ctx)), + _relocationHandler(new X86TargetRelocationHandler()) {} diff --git a/lib/ReaderWriter/ELF/X86/X86TargetHandler.h b/lib/ReaderWriter/ELF/X86/X86TargetHandler.h index 6c40267354192..fecf9abfc6781 100644 --- a/lib/ReaderWriter/ELF/X86/X86TargetHandler.h +++ b/lib/ReaderWriter/ELF/X86/X86TargetHandler.h @@ -10,10 +10,8 @@ #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 "ELFReader.h" #include "X86RelocationHandler.h" namespace lld { @@ -21,41 +19,28 @@ namespace elf { class X86LinkingContext; -template <class ELFT> class X86TargetLayout : public TargetLayout<ELFT> { +class X86TargetHandler final : public TargetHandler { 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; + X86TargetHandler(X86LinkingContext &ctx); - const X86TargetRelocationHandler &getRelocationHandler() const override { - return *(_x86RelocationHandler.get()); + const TargetRelocationHandler &getRelocationHandler() const override { + return *_relocationHandler; } std::unique_ptr<Reader> getObjReader() override { - return std::unique_ptr<Reader>(new X86ELFObjectReader(_x86LinkingContext)); + return llvm::make_unique<ELFReader<ELFFile<ELF32LE>>>(_ctx); } std::unique_ptr<Reader> getDSOReader() override { - return std::unique_ptr<Reader>(new X86ELFDSOReader(_x86LinkingContext)); + return llvm::make_unique<ELFReader<DynamicFile<ELF32LE>>>(_ctx); } 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; + X86LinkingContext &_ctx; + std::unique_ptr<TargetLayout<ELF32LE>> _targetLayout; + std::unique_ptr<X86TargetRelocationHandler> _relocationHandler; }; } // end namespace elf } // end namespace lld diff --git a/lib/ReaderWriter/ELF/X86_64/CMakeLists.txt b/lib/ReaderWriter/ELF/X86_64/CMakeLists.txt index a85d2b5046306..36ea839aa6743 100644 --- a/lib/ReaderWriter/ELF/X86_64/CMakeLists.txt +++ b/lib/ReaderWriter/ELF/X86_64/CMakeLists.txt @@ -3,6 +3,7 @@ add_llvm_library(lldX86_64ELFTarget X86_64TargetHandler.cpp X86_64RelocationHandler.cpp X86_64RelocationPass.cpp + X86_64SectionChunks.cpp LINK_LIBS lldELF lldReaderWriter diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.cpp b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.cpp index dbbb3ad3bc90d..cb3e819aff6b7 100644 --- a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.cpp +++ b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.cpp @@ -14,14 +14,14 @@ using namespace lld; using namespace elf; std::unique_ptr<ELFLinkingContext> -ExampleLinkingContext::create(llvm::Triple triple) { +elf::createExampleLinkingContext(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>( + : X86_64LinkingContext(triple, std::unique_ptr<TargetHandler>( new ExampleTargetHandler(*this))) { _outputELFType = llvm::ELF::ET_LOPROC; } diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.cpp b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.cpp index b66b0d903f6a4..89ec6671f3a0c 100644 --- a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.cpp +++ b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.cpp @@ -15,9 +15,8 @@ using namespace lld; using namespace elf; ExampleTargetHandler::ExampleTargetHandler(ExampleLinkingContext &c) - : X86_64TargetHandler(c), _exampleContext(c) {} + : X86_64TargetHandler(c), _ctx(c) {} std::unique_ptr<Writer> ExampleTargetHandler::getWriter() { - return std::unique_ptr<Writer>( - new X86_64ExecutableWriter(_exampleContext, *_x86_64TargetLayout)); + return llvm::make_unique<X86_64ExecutableWriter>(_ctx, *_targetLayout); } diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.h b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.h index 19a642113359f..46eade5864f9f 100644 --- a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.h +++ b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.h @@ -23,7 +23,7 @@ public: std::unique_ptr<Writer> getWriter() override; private: - ExampleLinkingContext &_exampleContext; + ExampleLinkingContext &_ctx; }; } // end namespace elf } // end namespace lld diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/Makefile b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/Makefile deleted file mode 100644 index 8f0b0fead1f69..0000000000000 --- a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- 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 deleted file mode 100644 index dbeb4d2270507..0000000000000 --- a/lib/ReaderWriter/ELF/X86_64/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -##===- 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/X86_64DynamicLibraryWriter.h b/lib/ReaderWriter/ELF/X86_64/X86_64DynamicLibraryWriter.h index b996186115b66..f84f85223bfb9 100644 --- a/lib/ReaderWriter/ELF/X86_64/X86_64DynamicLibraryWriter.h +++ b/lib/ReaderWriter/ELF/X86_64/X86_64DynamicLibraryWriter.h @@ -10,51 +10,33 @@ #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> { +class X86_64DynamicLibraryWriter : public DynamicLibraryWriter<ELF64LE> { public: - X86_64DynamicLibraryWriter(X86_64LinkingContext &context, + X86_64DynamicLibraryWriter(X86_64LinkingContext &ctx, 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; + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; }; X86_64DynamicLibraryWriter::X86_64DynamicLibraryWriter( - X86_64LinkingContext &context, X86_64TargetLayout &layout) - : DynamicLibraryWriter(context, layout), _gotFile(new GOTFile(context)) {} + X86_64LinkingContext &ctx, X86_64TargetLayout &layout) + : DynamicLibraryWriter(ctx, layout) {} -bool X86_64DynamicLibraryWriter::createImplicitFiles( +void 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; + auto gotFile = llvm::make_unique<SimpleFile>("GOTFile"); + gotFile->addAtom(*new (gotFile->allocator()) GlobalOffsetTableAtom(*gotFile)); + gotFile->addAtom(*new (gotFile->allocator()) DynamicAtom(*gotFile)); + result.push_back(std::move(gotFile)); } } // namespace elf diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64ELFFile.h b/lib/ReaderWriter/ELF/X86_64/X86_64ELFFile.h deleted file mode 100644 index d43840a63e7e0..0000000000000 --- a/lib/ReaderWriter/ELF/X86_64/X86_64ELFFile.h +++ /dev/null @@ -1,41 +0,0 @@ -//===- 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 deleted file mode 100644 index 9b1284c6dfa8c..0000000000000 --- a/lib/ReaderWriter/ELF/X86_64/X86_64ELFReader.h +++ /dev/null @@ -1,62 +0,0 @@ -//===- 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 deleted file mode 100644 index 0b982e7754e21..0000000000000 --- a/lib/ReaderWriter/ELF/X86_64/X86_64ElfType.h +++ /dev/null @@ -1,21 +0,0 @@ -//===- 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 index f549ed6dcfcbb..930a2de2a9e81 100644 --- a/lib/ReaderWriter/ELF/X86_64/X86_64ExecutableWriter.h +++ b/lib/ReaderWriter/ELF/X86_64/X86_64ExecutableWriter.h @@ -10,49 +10,45 @@ #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> { +class X86_64ExecutableWriter : public ExecutableWriter<ELF64LE> { public: - X86_64ExecutableWriter(X86_64LinkingContext &context, - X86_64TargetLayout &layout) - : ExecutableWriter(context, layout), _gotFile(new GOTFile(context)), - _context(context) {} + X86_64ExecutableWriter(X86_64LinkingContext &ctx, X86_64TargetLayout &layout) + : ExecutableWriter(ctx, layout), _targetLayout(layout) {} protected: // Add any runtime files and their atoms to the output - virtual bool - createImplicitFiles(std::vector<std::unique_ptr<File>> &result) { + void + createImplicitFiles(std::vector<std::unique_ptr<File>> &result) override { 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; + auto gotFile = llvm::make_unique<SimpleFile>("GOTFile"); + gotFile->addAtom(*new (gotFile->allocator()) + GlobalOffsetTableAtom(*gotFile)); + if (this->_ctx.isDynamic()) + gotFile->addAtom(*new (gotFile->allocator()) DynamicAtom(*gotFile)); + result.push_back(std::move(gotFile)); } - virtual void finalizeDefaultAtomValues() { - return ExecutableWriter::finalizeDefaultAtomValues(); + void buildDynamicSymbolTable(const File &file) override { + for (auto sec : this->_layout.sections()) { + if (auto section = dyn_cast<AtomSection<ELF64LE>>(sec)) { + for (const auto &atom : section->atoms()) { + if (_targetLayout.getGOTSection().hasGlobalGOTEntry(atom->_atom)) { + this->_dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), + atom->_virtualAddr, atom); + } + } + } + } + + ExecutableWriter<ELF64LE>::buildDynamicSymbolTable(file); } - 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; + X86_64TargetLayout &_targetLayout; }; } // namespace elf diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.cpp b/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.cpp index 6a8ce8bd64967..0dcd6ac6fbed3 100644 --- a/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.cpp +++ b/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.cpp @@ -12,10 +12,10 @@ #include "X86_64TargetHandler.h" using namespace lld; -using namespace elf; +using namespace lld::elf; X86_64LinkingContext::X86_64LinkingContext( - llvm::Triple triple, std::unique_ptr<TargetHandlerBase> handler) + llvm::Triple triple, std::unique_ptr<TargetHandler> handler) : ELFLinkingContext(triple, std::move(handler)) {} X86_64LinkingContext::X86_64LinkingContext(llvm::Triple triple) @@ -30,9 +30,21 @@ void X86_64LinkingContext::addPasses(PassManager &pm) { } std::unique_ptr<ELFLinkingContext> -X86_64LinkingContext::create(llvm::Triple triple) { +elf::createX86_64LinkingContext(llvm::Triple triple) { if (triple.getArch() == llvm::Triple::x86_64) - return std::unique_ptr<ELFLinkingContext>( - new elf::X86_64LinkingContext(triple)); + return llvm::make_unique<X86_64LinkingContext>(triple); return nullptr; } + +static const Registry::KindStrings kindStrings[] = { +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), +#include "llvm/Support/ELFRelocs/x86_64.def" +#undef ELF_RELOC + LLD_KIND_STRING_ENTRY(LLD_R_X86_64_GOTRELINDEX), + LLD_KIND_STRING_END +}; + +void X86_64LinkingContext::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, + Reference::KindArch::x86_64, kindStrings); +} diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.h b/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.h index 2cc799a9c8102..a5a7b3d21f4e3 100644 --- a/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.h +++ b/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.h @@ -26,12 +26,15 @@ enum { class X86_64LinkingContext : public ELFLinkingContext { protected: - X86_64LinkingContext(llvm::Triple, std::unique_ptr<TargetHandlerBase>); + X86_64LinkingContext(llvm::Triple, std::unique_ptr<TargetHandler>); + public: static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + int getMachineType() const override { return llvm::ELF::EM_X86_64; } X86_64LinkingContext(llvm::Triple); void addPasses(PassManager &) override; + void registerRelocationNames(Registry &r) override; uint64_t getBaseAddress() const override { if (_baseAddress == 0) @@ -65,7 +68,7 @@ public: return false; } - virtual bool isPLTRelocation(const Reference &r) const override { + bool isPLTRelocation(const Reference &r) const override { if (r.kindNamespace() != Reference::KindNamespace::ELF) return false; assert(r.kindArch() == Reference::KindArch::x86_64); diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp index 8fd74f43bbd28..d56983d1e382a 100644 --- a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp +++ b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp @@ -23,7 +23,7 @@ static void reloc64(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { /// \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); + uint32_t result = (uint32_t)(S + A - P); write32le(location, result + read32le(location)); } @@ -50,24 +50,24 @@ static void reloc16(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { /// \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); + 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); + 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, + ELFWriter &writer, llvm::FileOutputBuffer &buf, const 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(); + uint8_t *loc = atomContent + ref.offsetInAtom(); + uint64_t target = writer.addressOfAtom(ref.target()); + uint64_t reloc = atom._virtualAddr + ref.offsetInAtom(); if (ref.kindNamespace() != Reference::KindNamespace::ELF) return std::error_code(); @@ -76,38 +76,34 @@ std::error_code X86_64TargetRelocationHandler::applyRelocation( case R_X86_64_NONE: break; case R_X86_64_64: - reloc64(location, relocVAddress, targetVAddress, ref.addend()); + reloc64(loc, reloc, target, ref.addend()); break; case R_X86_64_PC32: case R_X86_64_GOTPCREL: - relocPC32(location, relocVAddress, targetVAddress, ref.addend()); + relocPC32(loc, reloc, target, ref.addend()); break; case R_X86_64_32: - reloc32(location, relocVAddress, targetVAddress, ref.addend()); + reloc32(loc, reloc, target, ref.addend()); break; case R_X86_64_32S: - reloc32S(location, relocVAddress, targetVAddress, ref.addend()); + reloc32S(loc, reloc, target, ref.addend()); break; case R_X86_64_16: - reloc16(location, relocVAddress, targetVAddress, ref.addend()); + reloc16(loc, reloc, target, ref.addend()); break; case R_X86_64_PC16: - relocPC16(location, relocVAddress, targetVAddress, ref.addend()); + relocPC16(loc, reloc, target, 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); - } + case R_X86_64_TPOFF32: + _tlsSize = _layout.getTLSSize(); + write32le(loc, target - _tlsSize); + break; + case R_X86_64_GOTTPOFF: + relocPC32(loc, reloc, target, ref.addend()); break; - } case R_X86_64_TLSGD: { - relocPC32(location, relocVAddress, targetVAddress, ref.addend()); + relocPC32(loc, reloc, target, ref.addend()); break; } case R_X86_64_TLSLD: { @@ -115,21 +111,20 @@ std::error_code X86_64TargetRelocationHandler::applyRelocation( // 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)); + std::memcpy(loc - 3, instr, sizeof(instr)); break; } case R_X86_64_PC64: - relocPC64(location, relocVAddress, targetVAddress, ref.addend()); + relocPC64(loc, reloc, target, ref.addend()); break; case LLD_R_X86_64_GOTRELINDEX: { const DefinedAtom *target = cast<const DefinedAtom>(ref.target()); for (const Reference *r : *target) { if (r->kindValue() == R_X86_64_JUMP_SLOT) { uint32_t index; - if (!_x86_64Layout.getPLTRelocationTable()->getRelocationIndex(*r, - index)) + if (!_layout.getPLTRelocationTable()->getRelocationIndex(*r, index)) llvm_unreachable("Relocation doesn't exist"); - reloc32(location, 0, index, 0); + reloc32(loc, 0, index, 0); break; } } @@ -142,6 +137,7 @@ std::error_code X86_64TargetRelocationHandler::applyRelocation( case R_X86_64_GLOB_DAT: case R_X86_64_DTPMOD64: case R_X86_64_DTPOFF64: + case R_X86_64_TPOFF64: break; default: return make_unhandled_reloc_error(); diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.h b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.h index 9e2c2171015d1..26382804549ba 100644 --- a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.h +++ b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.h @@ -10,27 +10,25 @@ #ifndef X86_64_RELOCATION_HANDLER_H #define X86_64_RELOCATION_HANDLER_H -#include "X86_64TargetHandler.h" +#include "lld/ReaderWriter/ELFLinkingContext.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) {} + : _tlsSize(0), _layout(layout) {} std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, - const lld::AtomLayout &, + const AtomLayout &, const Reference &) const override; private: // Cached size of the TLS segment. mutable uint64_t _tlsSize; - X86_64TargetLayout &_x86_64Layout; + X86_64TargetLayout &_layout; }; } // end namespace elf diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.cpp b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.cpp index 0703927fd56c3..a2f10dc08a4e6 100644 --- a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.cpp +++ b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.cpp @@ -188,11 +188,12 @@ protected: 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 TPOFF64 GOT entry. + std::error_code handleGOTTPOFF(const Reference &ref) { + if (isa<DefinedAtom>(ref.target())) { + const_cast<Reference &>(ref).setTarget(getGOTTPOFF(ref.target())); + } + return std::error_code(); } /// \brief Create a TLS GOT entry with DTPMOD64/DTPOFF64 dynamic relocations. @@ -243,9 +244,7 @@ protected: } public: - RelocationPass(const ELFLinkingContext &ctx) - : _file(ctx), _ctx(ctx), _null(nullptr), _PLT0(nullptr), _got0(nullptr), - _got1(nullptr) {} + RelocationPass(const ELFLinkingContext &ctx) : _file(ctx), _ctx(ctx) {} /// \brief Do the pass. /// @@ -255,45 +254,46 @@ public: /// /// After all references are handled, the atoms created during that are all /// added to mf. - void perform(std::unique_ptr<MutableFile> &mf) override { + std::error_code perform(SimpleFile &mf) override { ScopedTask task(getDefaultDomain(), "X86-64 GOT/PLT Pass"); // Process all references. - for (const auto &atom : mf->defined()) + 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); + if (_plt0) { + _plt0->setOrdinal(ordinal++); + mf.addAtom(*_plt0); } for (auto &plt : _pltVector) { plt->setOrdinal(ordinal++); - mf->addAtom(*plt); + mf.addAtom(*plt); } if (_null) { _null->setOrdinal(ordinal++); - mf->addAtom(*_null); + mf.addAtom(*_null); } - if (_PLT0) { + if (_plt0) { _got0->setOrdinal(ordinal++); _got1->setOrdinal(ordinal++); - mf->addAtom(*_got0); - mf->addAtom(*_got1); + mf.addAtom(*_got0); + mf.addAtom(*_got1); } for (auto &got : _gotVector) { got->setOrdinal(ordinal++); - mf->addAtom(*got); + mf.addAtom(*got); } for (auto &got : _tlsGotVector) { got->setOrdinal(ordinal++); - mf->addAtom(*got); + mf.addAtom(*got); } for (auto obj : _objectVector) { obj->setOrdinal(ordinal++); - mf->addAtom(*obj); + mf.addAtom(*obj); } + return std::error_code(); } protected: @@ -322,14 +322,14 @@ protected: std::vector<GOTAtom *> _tlsGotVector; /// \brief GOT entry that is always 0. Used for undefined weaks. - GOTAtom *_null; + GOTAtom *_null = nullptr; /// \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; + PLT0Atom *_plt0 = nullptr; + GOTAtom *_got0 = nullptr; + GOTAtom *_got1 = nullptr; /// @} }; @@ -379,20 +379,20 @@ public: : RelocationPass(ctx) {} const PLT0Atom *getPLT0() { - if (_PLT0) - return _PLT0; + if (_plt0) + return _plt0; // Fill in the null entry. getNullGOT(); - _PLT0 = new (_file._alloc) X86_64PLT0Atom(_file); + _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); + _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; + return _plt0; } const PLTAtom *getPLTEntry(const Atom *a) { diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64SectionChunks.cpp b/lib/ReaderWriter/ELF/X86_64/X86_64SectionChunks.cpp new file mode 100644 index 0000000000000..28eb3e4244b66 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64SectionChunks.cpp @@ -0,0 +1,37 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64SectionChunks.cpp --------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "X86_64SectionChunks.h" +#include "TargetLayout.h" + +namespace lld { +namespace elf { + +X86_64GOTSection::X86_64GOTSection(const ELFLinkingContext &ctx) + : AtomSection<ELF64LE>(ctx, ".got", DefinedAtom::typeGOT, DefinedAtom::permRW_, + TargetLayout<ELF64LE>::ORDER_GOT) { + this->_alignment = 8; +} + +const AtomLayout *X86_64GOTSection::appendAtom(const Atom *atom) { + const DefinedAtom *da = dyn_cast<DefinedAtom>(atom); + + for (const auto &r : *da) { + if (r->kindNamespace() != Reference::KindNamespace::ELF) + continue; + assert(r->kindArch() == Reference::KindArch::x86_64); + if (r->kindValue() == R_X86_64_TPOFF64) + _tlsMap[r->target()] = _tlsMap.size(); + } + + return AtomSection<ELF64LE>::appendAtom(atom); +} + +} // elf +} // lld diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64SectionChunks.h b/lib/ReaderWriter/ELF/X86_64/X86_64SectionChunks.h new file mode 100644 index 0000000000000..5208491eee55c --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64SectionChunks.h @@ -0,0 +1,36 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64SectionChunks.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_SECTION_CHUNKS_H +#define LLD_READER_WRITER_ELF_X86_64_X86_64_SECTION_CHUNKS_H + +#include "TargetLayout.h" + +namespace lld { +namespace elf { + +class X86_64GOTSection : public AtomSection<ELF64LE> { +public: + X86_64GOTSection(const ELFLinkingContext &ctx); + + bool hasGlobalGOTEntry(const Atom *a) const { + return _tlsMap.count(a); + } + + const AtomLayout *appendAtom(const Atom *atom) override; + +private: + /// \brief Map TLS Atoms to their GOT entry index. + llvm::DenseMap<const Atom *, std::size_t> _tlsMap; +}; + +} // elf +} // lld + +#endif diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp b/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp index f35330eb25c05..599077ac33c5c 100644 --- a/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp +++ b/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp @@ -16,37 +16,19 @@ 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); -} +X86_64TargetHandler::X86_64TargetHandler(X86_64LinkingContext &ctx) + : _ctx(ctx), _targetLayout(new X86_64TargetLayout(ctx)), + _relocationHandler(new X86_64TargetRelocationHandler(*_targetLayout)) {} std::unique_ptr<Writer> X86_64TargetHandler::getWriter() { - switch (this->_context.getOutputELFType()) { + switch (this->_ctx.getOutputELFType()) { case llvm::ELF::ET_EXEC: - return std::unique_ptr<Writer>( - new X86_64ExecutableWriter(_context, *_x86_64TargetLayout.get())); + return llvm::make_unique<X86_64ExecutableWriter>(_ctx, *_targetLayout); case llvm::ELF::ET_DYN: - return std::unique_ptr<Writer>( - new X86_64DynamicLibraryWriter(_context, *_x86_64TargetLayout.get())); + return llvm::make_unique<X86_64DynamicLibraryWriter>(_ctx, *_targetLayout); 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 index 57da7bca01e67..6e3e58f8aed6e 100644 --- a/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.h +++ b/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.h @@ -10,57 +10,92 @@ #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 "ELFReader.h" #include "TargetLayout.h" -#include "X86_64ELFFile.h" -#include "X86_64ELFReader.h" #include "X86_64LinkingContext.h" #include "X86_64RelocationHandler.h" +#include "X86_64SectionChunks.h" #include "lld/Core/Simple.h" namespace lld { namespace elf { -class X86_64TargetLayout : public TargetLayout<X86_64ELFType> { + + +class X86_64TargetLayout : public TargetLayout<ELF64LE> { public: - X86_64TargetLayout(X86_64LinkingContext &context) - : TargetLayout(context) {} + X86_64TargetLayout(X86_64LinkingContext &ctx) : TargetLayout(ctx), + _gotSection(new (this->_allocator) X86_64GOTSection(ctx)) {} + + AtomSection<ELF64LE> * + createSection(StringRef name, int32_t type, + DefinedAtom::ContentPermissions permissions, + TargetLayout<ELF64LE>::SectionOrder order) override { + if (type == DefinedAtom::typeGOT && name == ".got") + return _gotSection; + return TargetLayout<ELF64LE>::createSection(name, type, permissions, order); + } void finalizeOutputSectionLayout() override { - sortOutputSectionByPriority(".init_array", ".init_array"); - sortOutputSectionByPriority(".fini_array", ".fini_array"); + sortOutputSectionByPriority<ELF64LE>(".init_array"); + sortOutputSectionByPriority<ELF64LE>(".fini_array"); } -}; -class X86_64TargetHandler - : public DefaultTargetHandler<X86_64ELFType> { -public: - X86_64TargetHandler(X86_64LinkingContext &context); + const X86_64GOTSection &getGOTSection() const { return *_gotSection; } - X86_64TargetLayout &getTargetLayout() override { - return *(_x86_64TargetLayout.get()); +private: + uint32_t getPriority(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; } - void registerRelocationNames(Registry ®istry) override; + template <typename T> void sortOutputSectionByPriority(StringRef prefix) { + OutputSection<T> *section = findOutputSection(prefix); + if (!section) + return; + auto sections = section->sections(); + std::sort(sections.begin(), sections.end(), + [&](Chunk<T> *lhs, Chunk<T> *rhs) { + Section<T> *lhsSection = dyn_cast<Section<T>>(lhs); + Section<T> *rhsSection = dyn_cast<Section<T>>(rhs); + if (!lhsSection || !rhsSection) + return false; + StringRef lhsName = lhsSection->inputSectionName(); + StringRef rhsName = rhsSection->inputSectionName(); + if (!lhsName.startswith(prefix) || !rhsName.startswith(prefix)) + return false; + return getPriority(lhsName) < getPriority(rhsName); + }); + } + +private: + X86_64GOTSection *_gotSection; +}; + +class X86_64TargetHandler : public TargetHandler { +public: + X86_64TargetHandler(X86_64LinkingContext &ctx); - const X86_64TargetRelocationHandler &getRelocationHandler() const override { - return *(_x86_64RelocationHandler.get()); + const TargetRelocationHandler &getRelocationHandler() const override { + return *_relocationHandler; } std::unique_ptr<Reader> getObjReader() override { - return std::unique_ptr<Reader>(new X86_64ELFObjectReader(_context)); + return llvm::make_unique<ELFReader<ELFFile<ELF64LE>>>(_ctx); } std::unique_ptr<Reader> getDSOReader() override { - return std::unique_ptr<Reader>(new X86_64ELFDSOReader(_context)); + return llvm::make_unique<ELFReader<DynamicFile<ELF64LE>>>(_ctx); } 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; + X86_64LinkingContext &_ctx; + std::unique_ptr<X86_64TargetLayout> _targetLayout; + std::unique_ptr<X86_64TargetRelocationHandler> _relocationHandler; }; } // end namespace elf |