diff options
Diffstat (limited to 'lib/ReaderWriter/ELF/ARM')
-rw-r--r-- | lib/ReaderWriter/ELF/ARM/ARMDynamicLibraryWriter.h | 49 | ||||
-rw-r--r-- | lib/ReaderWriter/ELF/ARM/ARMELFFile.h | 143 | ||||
-rw-r--r-- | lib/ReaderWriter/ELF/ARM/ARMELFReader.h | 62 | ||||
-rw-r--r-- | lib/ReaderWriter/ELF/ARM/ARMELFWriters.h | 120 | ||||
-rw-r--r-- | lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h | 99 | ||||
-rw-r--r-- | lib/ReaderWriter/ELF/ARM/ARMLinkingContext.cpp | 48 | ||||
-rw-r--r-- | lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h | 46 | ||||
-rw-r--r-- | lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp | 578 | ||||
-rw-r--r-- | lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.h | 15 | ||||
-rw-r--r-- | lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp | 816 | ||||
-rw-r--r-- | lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h | 39 | ||||
-rw-r--r-- | lib/ReaderWriter/ELF/ARM/ARMTargetHandler.cpp | 28 | ||||
-rw-r--r-- | lib/ReaderWriter/ELF/ARM/ARMTargetHandler.h | 162 | ||||
-rw-r--r-- | lib/ReaderWriter/ELF/ARM/Makefile | 15 | ||||
-rw-r--r-- | lib/ReaderWriter/ELF/ARM/TODO.rst | 13 |
15 files changed, 1640 insertions, 593 deletions
diff --git a/lib/ReaderWriter/ELF/ARM/ARMDynamicLibraryWriter.h b/lib/ReaderWriter/ELF/ARM/ARMDynamicLibraryWriter.h new file mode 100644 index 000000000000..da843b97abc0 --- /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 bc5ee35b8213..8f5477017e55 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 31af531563ea..000000000000 --- 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 000000000000..a842ebe53038 --- /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 19311d516e4d..974dab63a126 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 5f2436674268..74905b47820f 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 249b79c4f07d..f687713b25b8 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 d24fdf0fa410..97b149133ff2 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 227d68617bf9..a1f3d091f204 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 27ec66ac5557..fc2ae75cd7a7 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 540a480421a8..85b9c9162589 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 de90f490f621..e1f5eadbe789 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 10641954da25..0352e81a1f61 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 f67d36a1b612..000000000000 --- 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 d05419decb78..61b585ae698c 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 ######################### |