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