| //===- ARMLDBackend.cpp ---------------------------------------------------===// |
| // |
| // The MCLinker Project |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| #include "ARM.h" |
| #include "ARMGNUInfo.h" |
| #include "ARMELFAttributeData.h" |
| #include "ARMELFDynamic.h" |
| #include "ARMException.h" |
| #include "ARMLDBackend.h" |
| #include "ARMRelocator.h" |
| #include "ARMToARMStub.h" |
| #include "ARMToTHMStub.h" |
| #include "THMToTHMStub.h" |
| #include "THMToARMStub.h" |
| |
| #include "mcld/IRBuilder.h" |
| #include "mcld/LinkerConfig.h" |
| #include "mcld/ADT/ilist_sort.h" |
| #include "mcld/Fragment/AlignFragment.h" |
| #include "mcld/Fragment/FillFragment.h" |
| #include "mcld/Fragment/NullFragment.h" |
| #include "mcld/Fragment/RegionFragment.h" |
| #include "mcld/Fragment/Stub.h" |
| #include "mcld/LD/BranchIslandFactory.h" |
| #include "mcld/LD/ELFFileFormat.h" |
| #include "mcld/LD/ELFSegment.h" |
| #include "mcld/LD/ELFSegmentFactory.h" |
| #include "mcld/LD/LDContext.h" |
| #include "mcld/LD/StubFactory.h" |
| #include "mcld/Object/ObjectBuilder.h" |
| #include "mcld/Support/MemoryArea.h" |
| #include "mcld/Support/MemoryRegion.h" |
| #include "mcld/Support/MsgHandling.h" |
| #include "mcld/Support/TargetRegistry.h" |
| #include "mcld/Target/ELFAttribute.h" |
| #include "mcld/Target/GNUInfo.h" |
| |
| #include <llvm/ADT/StringRef.h> |
| #include <llvm/ADT/Triple.h> |
| #include <llvm/ADT/Twine.h> |
| #include <llvm/Support/Casting.h> |
| #include <llvm/Support/ELF.h> |
| |
| #include <cstring> |
| #include <vector> |
| |
| namespace mcld { |
| |
| /// Fragment data for EXIDX_CANTUNWIND. |
| static const char g_CantUnwindEntry[8] = { |
| // Relocation to text section. |
| 0, 0, 0, 0, |
| // EXIDX_CANTUNWIND (little endian.) |
| 1, 0, 0, 0, |
| }; |
| |
| /// Helper function to create a local symbol at the end of the fragment. |
| static mcld::ResolveInfo* |
| CreateLocalSymbolToFragmentEnd(mcld::Module& pModule, mcld::Fragment& pFrag) { |
| // Create and add symbol to the name pool. |
| mcld::ResolveInfo* resolveInfo = |
| pModule.getNamePool().createSymbol(/* pName */"", |
| /* pIsDyn */false, |
| mcld::ResolveInfo::Section, |
| mcld::ResolveInfo::Define, |
| mcld::ResolveInfo::Local, |
| /* pSize */0, |
| mcld::ResolveInfo::Hidden); |
| if (resolveInfo == nullptr) { |
| return nullptr; |
| } |
| |
| // Create input symbol. |
| mcld::LDSymbol* inputSym = mcld::LDSymbol::Create(*resolveInfo); |
| if (inputSym == nullptr) { |
| return nullptr; |
| } |
| |
| inputSym->setFragmentRef(mcld::FragmentRef::Create(pFrag, pFrag.size())); |
| inputSym->setValue(/* pValue */0); |
| |
| // The output symbol is simply an alias to the input symbol. |
| resolveInfo->setSymPtr(inputSym); |
| |
| return resolveInfo; |
| } |
| |
| /// Comparator to sort .ARM.exidx fragments according to the address of the |
| /// corresponding .text fragment. |
| class ExIdxFragmentComparator { |
| private: |
| const ARMExData& m_pExData; |
| |
| public: |
| explicit ExIdxFragmentComparator(const ARMExData& pExData) |
| : m_pExData(pExData) { |
| } |
| |
| bool operator()(const Fragment& a, const Fragment& b) { |
| ARMExSectionTuple* tupleA = m_pExData.getTupleByExIdx(&a); |
| ARMExSectionTuple* tupleB = m_pExData.getTupleByExIdx(&b); |
| |
| Fragment* textFragA = tupleA->getTextFragment(); |
| Fragment* textFragB = tupleB->getTextFragment(); |
| |
| uint64_t addrA = textFragA->getParent()->getSection().addr() + |
| textFragA->getOffset(); |
| uint64_t addrB = textFragB->getParent()->getSection().addr() + |
| textFragB->getOffset(); |
| return (addrA < addrB); |
| } |
| }; |
| |
| //===----------------------------------------------------------------------===// |
| // ARMGNULDBackend |
| //===----------------------------------------------------------------------===// |
| ARMGNULDBackend::ARMGNULDBackend(const LinkerConfig& pConfig, GNUInfo* pInfo) |
| : GNULDBackend(pConfig, pInfo), |
| m_pRelocator(NULL), |
| m_pGOT(NULL), |
| m_pPLT(NULL), |
| m_pRelDyn(NULL), |
| m_pRelPLT(NULL), |
| m_pAttrData(NULL), |
| m_pDynamic(NULL), |
| m_pGOTSymbol(NULL), |
| m_pEXIDXStart(NULL), |
| m_pEXIDXEnd(NULL), |
| m_pEXIDX(NULL), |
| m_pEXTAB(NULL), |
| m_pAttributes(NULL) { |
| } |
| |
| ARMGNULDBackend::~ARMGNULDBackend() { |
| delete m_pRelocator; |
| delete m_pGOT; |
| delete m_pPLT; |
| delete m_pRelDyn; |
| delete m_pRelPLT; |
| delete m_pDynamic; |
| delete m_pAttrData; |
| } |
| |
| void ARMGNULDBackend::initTargetSections(Module& pModule, |
| ObjectBuilder& pBuilder) { |
| // FIXME: Currently we set exidx and extab to "Exception" and directly emit |
| // them from input |
| m_pEXIDX = |
| pBuilder.CreateSection(".ARM.exidx", |
| LDFileFormat::Target, |
| llvm::ELF::SHT_ARM_EXIDX, |
| llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_LINK_ORDER, |
| config().targets().bitclass() / 8); |
| m_pEXTAB = pBuilder.CreateSection(".ARM.extab", |
| LDFileFormat::Target, |
| llvm::ELF::SHT_PROGBITS, |
| llvm::ELF::SHF_ALLOC, |
| 0x1); |
| m_pAttributes = pBuilder.CreateSection(".ARM.attributes", |
| LDFileFormat::Target, |
| llvm::ELF::SHT_ARM_ATTRIBUTES, |
| 0x0, |
| 0x1); |
| |
| // initialize "aeabi" attributes subsection |
| m_pAttrData = new ARMELFAttributeData(); |
| attribute().registerAttributeData(*m_pAttrData); |
| |
| if (LinkerConfig::Object != config().codeGenType()) { |
| ELFFileFormat* file_format = getOutputFormat(); |
| |
| // initialize .got |
| LDSection& got = file_format->getGOT(); |
| m_pGOT = new ARMGOT(got); |
| |
| // initialize .plt |
| LDSection& plt = file_format->getPLT(); |
| m_pPLT = new ARMPLT(plt, *m_pGOT); |
| |
| // initialize .rel.plt |
| LDSection& relplt = file_format->getRelPlt(); |
| relplt.setLink(&plt); |
| // create SectionData and ARMRelDynSection |
| m_pRelPLT = new OutputRelocSection(pModule, relplt); |
| |
| // initialize .rel.dyn |
| LDSection& reldyn = file_format->getRelDyn(); |
| m_pRelDyn = new OutputRelocSection(pModule, reldyn); |
| } |
| } |
| |
| void ARMGNULDBackend::initTargetSymbols(IRBuilder& pBuilder, Module& pModule) { |
| // Define the symbol _GLOBAL_OFFSET_TABLE_ if there is a symbol with the |
| // same name in input |
| if (LinkerConfig::Object != config().codeGenType()) { |
| m_pGOTSymbol = |
| pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>( |
| "_GLOBAL_OFFSET_TABLE_", |
| ResolveInfo::Object, |
| ResolveInfo::Define, |
| ResolveInfo::Local, |
| 0x0, // size |
| 0x0, // value |
| FragmentRef::Null(), |
| ResolveInfo::Hidden); |
| } |
| if (m_pEXIDX != NULL && m_pEXIDX->size() != 0x0) { |
| FragmentRef* exidx_start = |
| FragmentRef::Create(m_pEXIDX->getSectionData()->front(), 0x0); |
| FragmentRef* exidx_end = FragmentRef::Create( |
| m_pEXIDX->getSectionData()->front(), m_pEXIDX->size()); |
| m_pEXIDXStart = |
| pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>( |
| "__exidx_start", |
| ResolveInfo::Object, |
| ResolveInfo::Define, |
| ResolveInfo::Local, |
| 0x0, // size |
| 0x0, // value |
| exidx_start, // FragRef |
| ResolveInfo::Default); |
| |
| m_pEXIDXEnd = pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>( |
| "__exidx_end", |
| ResolveInfo::Object, |
| ResolveInfo::Define, |
| ResolveInfo::Local, |
| 0x0, // size |
| 0x0, // value |
| exidx_end, // FragRef |
| ResolveInfo::Default); |
| // change __exidx_start/_end to local dynamic category |
| if (m_pEXIDXStart != NULL) |
| pModule.getSymbolTable().changeToDynamic(*m_pEXIDXStart); |
| if (m_pEXIDXEnd != NULL) |
| pModule.getSymbolTable().changeToDynamic(*m_pEXIDXEnd); |
| } else { |
| m_pEXIDXStart = |
| pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>( |
| "__exidx_start", |
| ResolveInfo::NoType, |
| ResolveInfo::Define, |
| ResolveInfo::Absolute, |
| 0x0, // size |
| 0x0, // value |
| FragmentRef::Null(), |
| ResolveInfo::Default); |
| |
| m_pEXIDXEnd = pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>( |
| "__exidx_end", |
| ResolveInfo::NoType, |
| ResolveInfo::Define, |
| ResolveInfo::Absolute, |
| 0x0, // size |
| 0x0, // value |
| FragmentRef::Null(), |
| ResolveInfo::Default); |
| } |
| } |
| |
| bool ARMGNULDBackend::initRelocator() { |
| if (m_pRelocator == NULL) { |
| m_pRelocator = new ARMRelocator(*this, config()); |
| } |
| return true; |
| } |
| |
| const Relocator* ARMGNULDBackend::getRelocator() const { |
| assert(m_pRelocator != NULL); |
| return m_pRelocator; |
| } |
| |
| Relocator* ARMGNULDBackend::getRelocator() { |
| assert(m_pRelocator != NULL); |
| return m_pRelocator; |
| } |
| |
| void ARMGNULDBackend::doPreLayout(IRBuilder& pBuilder) { |
| // initialize .dynamic data |
| if (!config().isCodeStatic() && m_pDynamic == NULL) |
| m_pDynamic = new ARMELFDynamic(*this, config()); |
| |
| // set attribute section size |
| m_pAttributes->setSize(attribute().sizeOutput()); |
| |
| // set .got size |
| // when building shared object, the .got section is must |
| if (LinkerConfig::Object != config().codeGenType()) { |
| if (LinkerConfig::DynObj == config().codeGenType() || m_pGOT->hasGOT1() || |
| m_pGOTSymbol != NULL) { |
| m_pGOT->finalizeSectionSize(); |
| defineGOTSymbol(pBuilder); |
| } |
| |
| // set .plt size |
| if (m_pPLT->hasPLT1()) |
| m_pPLT->finalizeSectionSize(); |
| |
| ELFFileFormat* file_format = getOutputFormat(); |
| // set .rel.dyn size |
| if (!m_pRelDyn->empty()) { |
| assert( |
| !config().isCodeStatic() && |
| "static linkage should not result in a dynamic relocation section"); |
| file_format->getRelDyn().setSize(m_pRelDyn->numOfRelocs() * |
| getRelEntrySize()); |
| } |
| |
| // set .rel.plt size |
| if (!m_pRelPLT->empty()) { |
| assert( |
| !config().isCodeStatic() && |
| "static linkage should not result in a dynamic relocation section"); |
| file_format->getRelPlt().setSize(m_pRelPLT->numOfRelocs() * |
| getRelEntrySize()); |
| } |
| } |
| } |
| |
| void ARMGNULDBackend::doPostLayout(Module& pModule, IRBuilder& pBuilder) { |
| const ELFFileFormat* file_format = getOutputFormat(); |
| |
| // apply PLT |
| if (file_format->hasPLT()) { |
| // Since we already have the size of LDSection PLT, m_pPLT should not be |
| // NULL. |
| assert(m_pPLT != NULL); |
| m_pPLT->applyPLT0(); |
| m_pPLT->applyPLT1(); |
| } |
| |
| // apply GOT |
| if (file_format->hasGOT()) { |
| // Since we already have the size of GOT, m_pGOT should not be NULL. |
| assert(m_pGOT != NULL); |
| if (LinkerConfig::DynObj == config().codeGenType()) |
| m_pGOT->applyGOT0(file_format->getDynamic().addr()); |
| else { |
| // executable file and object file? should fill with zero. |
| m_pGOT->applyGOT0(0); |
| } |
| } |
| } |
| |
| /// dynamic - the dynamic section of the target machine. |
| /// Use co-variant return type to return its own dynamic section. |
| ARMELFDynamic& ARMGNULDBackend::dynamic() { |
| assert(m_pDynamic != NULL); |
| return *m_pDynamic; |
| } |
| |
| /// dynamic - the dynamic section of the target machine. |
| /// Use co-variant return type to return its own dynamic section. |
| const ARMELFDynamic& ARMGNULDBackend::dynamic() const { |
| assert(m_pDynamic != NULL); |
| return *m_pDynamic; |
| } |
| |
| void ARMGNULDBackend::defineGOTSymbol(IRBuilder& pBuilder) { |
| // define symbol _GLOBAL_OFFSET_TABLE_ when .got create |
| if (m_pGOTSymbol != NULL) { |
| pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Unresolve>( |
| "_GLOBAL_OFFSET_TABLE_", |
| ResolveInfo::Object, |
| ResolveInfo::Define, |
| ResolveInfo::Local, |
| 0x0, // size |
| 0x0, // value |
| FragmentRef::Create(*(m_pGOT->begin()), 0x0), |
| ResolveInfo::Hidden); |
| } else { |
| m_pGOTSymbol = pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Resolve>( |
| "_GLOBAL_OFFSET_TABLE_", |
| ResolveInfo::Object, |
| ResolveInfo::Define, |
| ResolveInfo::Local, |
| 0x0, // size |
| 0x0, // value |
| FragmentRef::Create(*(m_pGOT->begin()), 0x0), |
| ResolveInfo::Hidden); |
| } |
| } |
| |
| uint64_t ARMGNULDBackend::emitSectionData(const LDSection& pSection, |
| MemoryRegion& pRegion) const { |
| assert(pRegion.size() && "Size of MemoryRegion is zero!"); |
| |
| const ELFFileFormat* file_format = getOutputFormat(); |
| |
| if (file_format->hasPLT() && (&pSection == &(file_format->getPLT()))) { |
| uint64_t result = m_pPLT->emit(pRegion); |
| return result; |
| } |
| |
| if (file_format->hasGOT() && (&pSection == &(file_format->getGOT()))) { |
| uint64_t result = m_pGOT->emit(pRegion); |
| return result; |
| } |
| |
| if (&pSection == m_pAttributes) { |
| return attribute().emit(pRegion); |
| } |
| |
| // FIXME: Currently Emitting .ARM.attributes, .ARM.exidx, and .ARM.extab |
| // directly from the input file. |
| const SectionData* sect_data = pSection.getSectionData(); |
| SectionData::const_iterator frag_iter, frag_end = sect_data->end(); |
| uint8_t* out_offset = pRegion.begin(); |
| for (frag_iter = sect_data->begin(); frag_iter != frag_end; ++frag_iter) { |
| size_t size = frag_iter->size(); |
| switch (frag_iter->getKind()) { |
| case Fragment::Fillment: { |
| const FillFragment& fill_frag = llvm::cast<FillFragment>(*frag_iter); |
| if (fill_frag.getValueSize() == 0) { |
| // virtual fillment, ignore it. |
| break; |
| } |
| |
| memset(out_offset, fill_frag.getValue(), fill_frag.size()); |
| break; |
| } |
| case Fragment::Region: { |
| const RegionFragment& region_frag = |
| llvm::cast<RegionFragment>(*frag_iter); |
| const char* start = region_frag.getRegion().begin(); |
| memcpy(out_offset, start, size); |
| break; |
| } |
| case Fragment::Alignment: { |
| const AlignFragment& align_frag = llvm::cast<AlignFragment>(*frag_iter); |
| uint64_t count = size / align_frag.getValueSize(); |
| switch (align_frag.getValueSize()) { |
| case 1u: |
| std::memset(out_offset, align_frag.getValue(), count); |
| break; |
| default: |
| llvm::report_fatal_error( |
| "unsupported value size for align fragment emission yet.\n"); |
| break; |
| } // end switch |
| break; |
| } |
| case Fragment::Null: { |
| assert(0x0 == size); |
| break; |
| } |
| default: |
| llvm::report_fatal_error("unsupported fragment type.\n"); |
| break; |
| } // end switch |
| out_offset += size; |
| } // end for |
| return pRegion.size(); |
| } |
| |
| /// finalizeSymbol - finalize the symbol value |
| bool ARMGNULDBackend::finalizeTargetSymbols() { |
| return true; |
| } |
| |
| |
| /// preMergeSections - hooks to be executed before merging sections |
| void ARMGNULDBackend::preMergeSections(Module& pModule) { |
| // Since the link relationship between .text and .ARM.exidx will be discarded |
| // after merging sections, we have to build the exception handling section |
| // mapping before section merge. |
| m_pExData = ARMExData::create(pModule); |
| } |
| |
| /// postMergeSections - hooks to be executed after merging sections |
| void ARMGNULDBackend::postMergeSections(Module& pModule) { |
| if (m_pEXIDX->hasSectionData()) { |
| // Append the NullFragment so that __exidx_end can be correctly inserted. |
| NullFragment* null = new NullFragment(m_pEXIDX->getSectionData()); |
| null->setOffset(m_pEXIDX->size()); |
| } |
| } |
| |
| bool ARMGNULDBackend::mergeSection(Module& pModule, |
| const Input& pInput, |
| LDSection& pSection) { |
| switch (pSection.type()) { |
| case llvm::ELF::SHT_ARM_ATTRIBUTES: { |
| return attribute().merge(pInput, pSection); |
| } |
| |
| case llvm::ELF::SHT_ARM_EXIDX: { |
| assert(pSection.getLink() != NULL); |
| if ((pSection.getLink()->kind() == LDFileFormat::Ignore) || |
| (pSection.getLink()->kind() == LDFileFormat::Folded)) { |
| // if the target section of the .ARM.exidx is Ignore, then it should be |
| // ignored as well |
| pSection.setKind(LDFileFormat::Ignore); |
| return true; |
| } |
| |
| if (!m_pEXIDX->hasSectionData()) { |
| // Create SectionData for m_pEXIDX. |
| SectionData* sectData = IRBuilder::CreateSectionData(*m_pEXIDX); |
| |
| // Initialize the alignment of m_pEXIDX. |
| const size_t alignExIdx = 4; |
| m_pEXIDX->setAlign(alignExIdx); |
| |
| // Insert an AlignFragment to the beginning of m_pEXIDX. |
| AlignFragment* frag = |
| new AlignFragment(/*alignment*/alignExIdx, |
| /*the filled value*/0x0, |
| /*the size of filled value*/1u, |
| /*max bytes to emit*/alignExIdx - 1); |
| frag->setOffset(0); |
| frag->setParent(sectData); |
| sectData->getFragmentList().push_back(frag); |
| m_pEXIDX->setSize(frag->size()); |
| } |
| |
| // Move RegionFragment from pSection to m_pEXIDX. |
| uint64_t offset = m_pEXIDX->size(); |
| SectionData::FragmentListType& src = |
| pSection.getSectionData()->getFragmentList(); |
| SectionData::FragmentListType& dst = |
| m_pEXIDX->getSectionData()->getFragmentList(); |
| SectionData::FragmentListType::iterator frag = src.begin(); |
| SectionData::FragmentListType::iterator fragEnd = src.end(); |
| while (frag != fragEnd) { |
| if (frag->getKind() != Fragment::Region) { |
| ++frag; |
| } else { |
| frag->setParent(m_pEXIDX->getSectionData()); |
| frag->setOffset(offset); |
| offset += frag->size(); |
| dst.splice(dst.end(), src, frag++); |
| } |
| } |
| |
| // Update the size of m_pEXIDX. |
| m_pEXIDX->setSize(offset); |
| return true; |
| } |
| |
| default: { |
| ObjectBuilder builder(pModule); |
| builder.MergeSection(pInput, pSection); |
| return true; |
| } |
| } // end of switch |
| return true; |
| } |
| |
| void ARMGNULDBackend::setUpReachedSectionsForGC( |
| const Module& pModule, |
| GarbageCollection::SectionReachedListMap& pSectReachedListMap) const { |
| // traverse all the input relocations to find the relocation sections applying |
| // .ARM.exidx sections |
| Module::const_obj_iterator input, inEnd = pModule.obj_end(); |
| for (input = pModule.obj_begin(); input != inEnd; ++input) { |
| LDContext::const_sect_iterator rs, |
| rsEnd = (*input)->context()->relocSectEnd(); |
| for (rs = (*input)->context()->relocSectBegin(); rs != rsEnd; ++rs) { |
| // bypass the discarded relocation section |
| // 1. its section kind is changed to Ignore. (The target section is a |
| // discarded group section.) |
| // 2. it has no reloc data. (All symbols in the input relocs are in the |
| // discarded group sections) |
| LDSection* reloc_sect = *rs; |
| LDSection* apply_sect = reloc_sect->getLink(); |
| if ((LDFileFormat::Ignore == reloc_sect->kind()) || |
| (!reloc_sect->hasRelocData())) |
| continue; |
| |
| if (llvm::ELF::SHT_ARM_EXIDX == apply_sect->type()) { |
| // 1. set up the reference according to relocations |
| bool add_first = false; |
| GarbageCollection::SectionListTy* reached_sects = NULL; |
| RelocData::iterator reloc_it, rEnd = reloc_sect->getRelocData()->end(); |
| for (reloc_it = reloc_sect->getRelocData()->begin(); reloc_it != rEnd; |
| ++reloc_it) { |
| Relocation* reloc = llvm::cast<Relocation>(reloc_it); |
| ResolveInfo* sym = reloc->symInfo(); |
| // only the target symbols defined in the input fragments can make the |
| // reference |
| if (sym == NULL) |
| continue; |
| if (!sym->isDefine() || !sym->outSymbol()->hasFragRef()) |
| continue; |
| |
| // only the target symbols defined in the concerned sections can make |
| // the reference |
| const LDSection* target_sect = |
| &sym->outSymbol()->fragRef()->frag()->getParent()->getSection(); |
| if (target_sect->kind() != LDFileFormat::TEXT && |
| target_sect->kind() != LDFileFormat::DATA && |
| target_sect->kind() != LDFileFormat::BSS) |
| continue; |
| |
| // setup the reached list, if we first add the element to reached list |
| // of this section, create an entry in ReachedSections map |
| if (!add_first) { |
| reached_sects = &pSectReachedListMap.getReachedList(*apply_sect); |
| add_first = true; |
| } |
| reached_sects->insert(target_sect); |
| } |
| reached_sects = NULL; |
| add_first = false; |
| // 2. set up the reference from XXX to .ARM.exidx.XXX |
| assert(apply_sect->getLink() != NULL); |
| pSectReachedListMap.addReference(*apply_sect->getLink(), *apply_sect); |
| } |
| } |
| } |
| } |
| |
| bool ARMGNULDBackend::readSection(Input& pInput, SectionData& pSD) { |
| Fragment* frag = NULL; |
| uint32_t offset = pInput.fileOffset() + pSD.getSection().offset(); |
| uint32_t size = pSD.getSection().size(); |
| |
| llvm::StringRef region = pInput.memArea()->request(offset, size); |
| if (region.size() == 0) { |
| // If the input section's size is zero, we got a NULL region. |
| // use a virtual fill fragment |
| frag = new FillFragment(0x0, 0, 0); |
| } else { |
| frag = new RegionFragment(region); |
| } |
| |
| ObjectBuilder::AppendFragment(*frag, pSD); |
| return true; |
| } |
| |
| ARMGOT& ARMGNULDBackend::getGOT() { |
| assert(m_pGOT != NULL && "GOT section not exist"); |
| return *m_pGOT; |
| } |
| |
| const ARMGOT& ARMGNULDBackend::getGOT() const { |
| assert(m_pGOT != NULL && "GOT section not exist"); |
| return *m_pGOT; |
| } |
| |
| ARMPLT& ARMGNULDBackend::getPLT() { |
| assert(m_pPLT != NULL && "PLT section not exist"); |
| return *m_pPLT; |
| } |
| |
| const ARMPLT& ARMGNULDBackend::getPLT() const { |
| assert(m_pPLT != NULL && "PLT section not exist"); |
| return *m_pPLT; |
| } |
| |
| OutputRelocSection& ARMGNULDBackend::getRelDyn() { |
| assert(m_pRelDyn != NULL && ".rel.dyn section not exist"); |
| return *m_pRelDyn; |
| } |
| |
| const OutputRelocSection& ARMGNULDBackend::getRelDyn() const { |
| assert(m_pRelDyn != NULL && ".rel.dyn section not exist"); |
| return *m_pRelDyn; |
| } |
| |
| OutputRelocSection& ARMGNULDBackend::getRelPLT() { |
| assert(m_pRelPLT != NULL && ".rel.plt section not exist"); |
| return *m_pRelPLT; |
| } |
| |
| const OutputRelocSection& ARMGNULDBackend::getRelPLT() const { |
| assert(m_pRelPLT != NULL && ".rel.plt section not exist"); |
| return *m_pRelPLT; |
| } |
| |
| ARMELFAttributeData& ARMGNULDBackend::getAttributeData() { |
| assert(m_pAttrData != NULL && ".ARM.attributes section not exist"); |
| return *m_pAttrData; |
| } |
| |
| const ARMELFAttributeData& ARMGNULDBackend::getAttributeData() const { |
| assert(m_pAttrData != NULL && ".ARM.attributes section not exist"); |
| return *m_pAttrData; |
| } |
| |
| unsigned int ARMGNULDBackend::getTargetSectionOrder( |
| const LDSection& pSectHdr) const { |
| const ELFFileFormat* file_format = getOutputFormat(); |
| |
| if (file_format->hasGOT() && (&pSectHdr == &file_format->getGOT())) { |
| if (config().options().hasNow()) |
| return SHO_RELRO_LAST; |
| return SHO_DATA; |
| } |
| |
| if (file_format->hasPLT() && (&pSectHdr == &file_format->getPLT())) |
| return SHO_PLT; |
| |
| if (&pSectHdr == m_pEXIDX || &pSectHdr == m_pEXTAB) { |
| // put ARM.exidx and ARM.extab in the same order of .eh_frame |
| return SHO_EXCEPTION; |
| } |
| |
| return SHO_UNDEFINED; |
| } |
| |
| void ARMGNULDBackend::rewriteARMExIdxSection(Module& pModule) { |
| if (!m_pEXIDX->hasSectionData()) { |
| // Return if this is empty section. |
| return; |
| } |
| |
| SectionData* sectData = m_pEXIDX->getSectionData(); |
| SectionData::FragmentListType& list = sectData->getFragmentList(); |
| |
| // Move the first fragment (align fragment) and last fragment (null fragment) |
| // to temporary list because we would only like to sort the region fragment. |
| SectionData::FragmentListType tmp; |
| { |
| SectionData::iterator first = sectData->begin(); |
| SectionData::iterator last = sectData->end(); |
| --last; |
| |
| assert(first->getKind() == Fragment::Alignment); |
| assert(last->getKind() == Fragment::Null); |
| |
| tmp.splice(tmp.end(), list, first); |
| tmp.splice(tmp.end(), list, last); |
| } |
| |
| // Sort the region fragments in the .ARM.exidx output section. |
| sort(list, ExIdxFragmentComparator(*m_pExData)); |
| |
| // Fix the coverage of the .ARM.exidx table. |
| llvm::StringRef cantUnwindRegion(g_CantUnwindEntry, |
| sizeof(g_CantUnwindEntry)); |
| |
| SectionData::FragmentListType::iterator it = list.begin(); |
| if (it != list.end()) { |
| Fragment* prevTextFrag = m_pExData->getTupleByExIdx(&*it)->getTextFragment(); |
| uint64_t prevTextEnd = prevTextFrag->getParent()->getSection().addr() + |
| prevTextFrag->getOffset() + |
| prevTextFrag->size(); |
| ++it; |
| while (it != list.end()) { |
| Fragment* currTextFrag = |
| m_pExData->getTupleByExIdx(&*it)->getTextFragment(); |
| uint64_t currTextBegin = currTextFrag->getParent()->getSection().addr() + |
| currTextFrag->getOffset(); |
| |
| if (currTextBegin > prevTextEnd) { |
| // Found a gap. Insert a can't unwind entry. |
| RegionFragment* frag = new RegionFragment(cantUnwindRegion, nullptr); |
| frag->setParent(sectData); |
| list.insert(it, frag); |
| |
| // Add PREL31 reference to the beginning of the uncovered region. |
| Relocation* reloc = |
| Relocation::Create(static_cast<uint32_t>(llvm::ELF::R_ARM_PREL31), |
| *FragmentRef::Create(*frag, /* pOffset */0), |
| /* pAddend */0); |
| reloc->setSymInfo( |
| CreateLocalSymbolToFragmentEnd(pModule, *prevTextFrag)); |
| addExtraRelocation(reloc); |
| } |
| |
| prevTextEnd = currTextBegin + currTextFrag->size(); |
| prevTextFrag = currTextFrag; |
| ++it; |
| } |
| |
| // Add a can't unwind entry to terminate .ARM.exidx section. |
| RegionFragment* frag = new RegionFragment(cantUnwindRegion, nullptr); |
| frag->setParent(sectData); |
| list.push_back(frag); |
| |
| // Add PREL31 reference to the end of the .text section. |
| Relocation* reloc = |
| Relocation::Create(static_cast<uint32_t>(llvm::ELF::R_ARM_PREL31), |
| *FragmentRef::Create(*frag, /* pOffset */0), |
| /* pAddend */0); |
| reloc->setSymInfo(CreateLocalSymbolToFragmentEnd(pModule, *prevTextFrag)); |
| addExtraRelocation(reloc); |
| } |
| |
| // Add the first and the last fragment back. |
| list.splice(list.begin(), tmp, tmp.begin()); |
| list.splice(list.end(), tmp, tmp.begin()); |
| |
| // Update the fragment offsets. |
| uint64_t offset = 0; |
| for (SectionData::iterator it = sectData->begin(), end = sectData->end(); |
| it != end; ++it) { |
| it->setOffset(offset); |
| offset += it->size(); |
| } |
| |
| // Update the section size. |
| m_pEXIDX->setSize(offset); |
| |
| // Rebuild the section header. |
| setOutputSectionAddress(pModule); |
| } |
| |
| /// relax - the relaxation pass |
| bool ARMGNULDBackend::relax(Module& pModule, IRBuilder& pBuilder) { |
| if (!GNULDBackend::relax(pModule, pBuilder)) { |
| return false; |
| } |
| rewriteARMExIdxSection(pModule); |
| return true; |
| } |
| |
| /// doRelax |
| bool ARMGNULDBackend::doRelax(Module& pModule, |
| IRBuilder& pBuilder, |
| bool& pFinished) { |
| assert(getStubFactory() != NULL && getBRIslandFactory() != NULL); |
| |
| bool isRelaxed = false; |
| ELFFileFormat* file_format = getOutputFormat(); |
| // check branch relocs and create the related stubs if needed |
| Module::obj_iterator input, inEnd = pModule.obj_end(); |
| for (input = pModule.obj_begin(); input != inEnd; ++input) { |
| LDContext::sect_iterator rs, rsEnd = (*input)->context()->relocSectEnd(); |
| for (rs = (*input)->context()->relocSectBegin(); rs != rsEnd; ++rs) { |
| if (LDFileFormat::Ignore == (*rs)->kind() || !(*rs)->hasRelocData()) |
| continue; |
| RelocData::iterator reloc, rEnd = (*rs)->getRelocData()->end(); |
| for (reloc = (*rs)->getRelocData()->begin(); reloc != rEnd; ++reloc) { |
| Relocation* relocation = llvm::cast<Relocation>(reloc); |
| |
| switch (relocation->type()) { |
| case llvm::ELF::R_ARM_PC24: |
| case llvm::ELF::R_ARM_CALL: |
| case llvm::ELF::R_ARM_JUMP24: |
| case llvm::ELF::R_ARM_PLT32: |
| case llvm::ELF::R_ARM_THM_CALL: |
| case llvm::ELF::R_ARM_THM_XPC22: |
| case llvm::ELF::R_ARM_THM_JUMP24: |
| case llvm::ELF::R_ARM_THM_JUMP19: { |
| // calculate the possible symbol value |
| uint64_t sym_value = 0x0; |
| LDSymbol* symbol = relocation->symInfo()->outSymbol(); |
| if (symbol->hasFragRef()) { |
| uint64_t value = symbol->fragRef()->getOutputOffset(); |
| uint64_t addr = |
| symbol->fragRef()->frag()->getParent()->getSection().addr(); |
| sym_value = addr + value; |
| } |
| if ((relocation->symInfo()->reserved() & |
| ARMRelocator::ReservePLT) != 0x0) { |
| // FIXME: we need to find out the address of the specific plt |
| // entry |
| assert(file_format->hasPLT()); |
| sym_value = file_format->getPLT().addr(); |
| } |
| Stub* stub = getStubFactory()->create(*relocation, // relocation |
| sym_value, // symbol value |
| pBuilder, |
| *getBRIslandFactory()); |
| if (stub != NULL) { |
| assert(stub->symInfo() != NULL); |
| // reset the branch target of the reloc to this stub instead |
| relocation->setSymInfo(stub->symInfo()); |
| |
| switch (config().options().getStripSymbolMode()) { |
| case GeneralOptions::StripSymbolMode::StripAllSymbols: |
| case GeneralOptions::StripSymbolMode::StripLocals: |
| break; |
| default: { |
| // a stub symbol should be local |
| assert(stub->symInfo() != NULL && stub->symInfo()->isLocal()); |
| LDSection& symtab = file_format->getSymTab(); |
| LDSection& strtab = file_format->getStrTab(); |
| |
| // increase the size of .symtab and .strtab if needed |
| symtab.setSize(symtab.size() + sizeof(llvm::ELF::Elf32_Sym)); |
| symtab.setInfo(symtab.getInfo() + 1); |
| strtab.setSize(strtab.size() + stub->symInfo()->nameSize() + |
| 1); |
| } |
| } // end of switch |
| isRelaxed = true; |
| } |
| break; |
| } |
| case llvm::ELF::R_ARM_V4BX: |
| /* FIXME: bypass R_ARM_V4BX relocation now */ |
| break; |
| default: |
| break; |
| } // end of switch |
| } // for all relocations |
| } // for all relocation section |
| } // for all inputs |
| |
| // find the first fragment w/ invalid offset due to stub insertion |
| std::vector<Fragment*> invalid_frags; |
| pFinished = true; |
| for (BranchIslandFactory::iterator island = getBRIslandFactory()->begin(), |
| island_end = getBRIslandFactory()->end(); |
| island != island_end; |
| ++island) { |
| if ((*island).size() > stubGroupSize()) { |
| error(diag::err_no_space_to_place_stubs) << stubGroupSize(); |
| return false; |
| } |
| |
| if ((*island).numOfStubs() == 0) { |
| continue; |
| } |
| |
| Fragment* exit = &*(*island).end(); |
| if (exit == (*island).begin()->getParent()->end()) { |
| continue; |
| } |
| |
| if (((*island).offset() + (*island).size()) > exit->getOffset()) { |
| if (invalid_frags.empty() || |
| (invalid_frags.back()->getParent() != (*island).getParent())) { |
| invalid_frags.push_back(exit); |
| pFinished = false; |
| } |
| continue; |
| } |
| } |
| |
| // reset the offset of invalid fragments |
| for (auto it = invalid_frags.begin(), ie = invalid_frags.end(); it != ie; |
| ++it) { |
| Fragment* invalid = *it; |
| while (invalid != NULL) { |
| invalid->setOffset(invalid->getPrevNode()->getOffset() + |
| invalid->getPrevNode()->size()); |
| invalid = invalid->getNextNode(); |
| } |
| } |
| |
| // reset the size of section that has stubs inserted. |
| if (isRelaxed) { |
| SectionData* prev = NULL; |
| for (BranchIslandFactory::iterator island = getBRIslandFactory()->begin(), |
| island_end = getBRIslandFactory()->end(); |
| island != island_end; |
| ++island) { |
| SectionData* sd = (*island).begin()->getParent(); |
| if ((*island).numOfStubs() != 0) { |
| if (sd != prev) { |
| sd->getSection().setSize(sd->back().getOffset() + sd->back().size()); |
| } |
| } |
| prev = sd; |
| } |
| } |
| return isRelaxed; |
| } |
| |
| /// initTargetStubs |
| bool ARMGNULDBackend::initTargetStubs() { |
| if (getStubFactory() != NULL) { |
| getStubFactory()->addPrototype(new ARMToARMStub(config().isCodeIndep())); |
| getStubFactory()->addPrototype(new ARMToTHMStub(config().isCodeIndep())); |
| getStubFactory()->addPrototype( |
| new THMToTHMStub(config().isCodeIndep(), m_pAttrData->usingThumb2())); |
| getStubFactory()->addPrototype( |
| new THMToARMStub(config().isCodeIndep(), m_pAttrData->usingThumb2())); |
| return true; |
| } |
| return false; |
| } |
| |
| /// maxFwdBranchOffset |
| int64_t ARMGNULDBackend::maxFwdBranchOffset() const { |
| if (m_pAttrData->usingThumb2()) { |
| return THM2_MAX_FWD_BRANCH_OFFSET; |
| } else { |
| return THM_MAX_FWD_BRANCH_OFFSET; |
| } |
| } |
| |
| /// maxBwdBranchOffset |
| int64_t ARMGNULDBackend::maxBwdBranchOffset() const { |
| if (m_pAttrData->usingThumb2()) { |
| return THM2_MAX_BWD_BRANCH_OFFSET; |
| } else { |
| return THM_MAX_BWD_BRANCH_OFFSET; |
| } |
| } |
| |
| /// doCreateProgramHdrs - backend can implement this function to create the |
| /// target-dependent segments |
| void ARMGNULDBackend::doCreateProgramHdrs(Module& pModule) { |
| if (m_pEXIDX != NULL && m_pEXIDX->size() != 0x0) { |
| // make PT_ARM_EXIDX |
| ELFSegment* exidx_seg = |
| elfSegmentTable().produce(llvm::ELF::PT_ARM_EXIDX, llvm::ELF::PF_R); |
| exidx_seg->append(m_pEXIDX); |
| } |
| } |
| |
| /// mayHaveUnsafeFunctionPointerAccess - check if the section may have unsafe |
| /// function pointer access |
| bool ARMGNULDBackend::mayHaveUnsafeFunctionPointerAccess( |
| const LDSection& pSection) const { |
| llvm::StringRef name(pSection.name()); |
| return !name.startswith(".ARM.exidx") && !name.startswith(".ARM.extab") && |
| GNULDBackend::mayHaveUnsafeFunctionPointerAccess(pSection); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| /// createARMLDBackend - the help funtion to create corresponding ARMLDBackend |
| /// |
| TargetLDBackend* createARMLDBackend(const LinkerConfig& pConfig) { |
| if (pConfig.targets().triple().isOSDarwin()) { |
| assert(0 && "MachO linker is not supported yet"); |
| /** |
| return new ARMMachOLDBackend(createARMMachOArchiveReader, |
| createARMMachOObjectReader, |
| createARMMachOObjectWriter); |
| **/ |
| } |
| if (pConfig.targets().triple().isOSWindows()) { |
| assert(0 && "COFF linker is not supported yet"); |
| /** |
| return new ARMCOFFLDBackend(createARMCOFFArchiveReader, |
| createARMCOFFObjectReader, |
| createARMCOFFObjectWriter); |
| **/ |
| } |
| return new ARMGNULDBackend(pConfig, |
| new ARMGNUInfo(pConfig.targets().triple())); |
| } |
| |
| } // namespace mcld |
| |
| //===----------------------------------------------------------------------===// |
| // Force static initialization. |
| //===----------------------------------------------------------------------===// |
| extern "C" void MCLDInitializeARMLDBackend() { |
| // Register the linker backend |
| mcld::TargetRegistry::RegisterTargetLDBackend(mcld::TheARMTarget, |
| mcld::createARMLDBackend); |
| mcld::TargetRegistry::RegisterTargetLDBackend(mcld::TheThumbTarget, |
| mcld::createARMLDBackend); |
| } |