| //===- MipsLDBackend.cpp --------------------------------------------------===// |
| // |
| // The MCLinker Project |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| #include "Mips.h" |
| #include "MipsGNUInfo.h" |
| #include "MipsELFDynamic.h" |
| #include "MipsLA25Stub.h" |
| #include "MipsLDBackend.h" |
| #include "MipsRelocator.h" |
| |
| #include "mcld/IRBuilder.h" |
| #include "mcld/LinkerConfig.h" |
| #include "mcld/Module.h" |
| #include "mcld/Fragment/AlignFragment.h" |
| #include "mcld/Fragment/FillFragment.h" |
| #include "mcld/LD/BranchIslandFactory.h" |
| #include "mcld/LD/LDContext.h" |
| #include "mcld/LD/StubFactory.h" |
| #include "mcld/LD/ELFFileFormat.h" |
| #include "mcld/LD/ELFSegment.h" |
| #include "mcld/LD/ELFSegmentFactory.h" |
| #include "mcld/MC/Attribute.h" |
| #include "mcld/Object/ObjectBuilder.h" |
| #include "mcld/Support/MemoryRegion.h" |
| #include "mcld/Support/MemoryArea.h" |
| #include "mcld/Support/MsgHandling.h" |
| #include "mcld/Support/TargetRegistry.h" |
| #include "mcld/Target/OutputRelocSection.h" |
| |
| #include <llvm/ADT/Triple.h> |
| #include <llvm/Object/ELFTypes.h> |
| #include <llvm/Support/Casting.h> |
| #include <llvm/Support/ELF.h> |
| #include <llvm/Support/Host.h> |
| #include <llvm/Support/MipsABIFlags.h> |
| |
| #include <vector> |
| |
| namespace mcld { |
| |
| //===----------------------------------------------------------------------===// |
| // MipsGNULDBackend |
| //===----------------------------------------------------------------------===// |
| MipsGNULDBackend::MipsGNULDBackend(const LinkerConfig& pConfig, |
| MipsGNUInfo* pInfo) |
| : GNULDBackend(pConfig, pInfo), |
| m_pRelocator(NULL), |
| m_pGOT(NULL), |
| m_pPLT(NULL), |
| m_pGOTPLT(NULL), |
| m_pInfo(*pInfo), |
| m_pRelPlt(NULL), |
| m_pRelDyn(NULL), |
| m_pDynamic(NULL), |
| m_pAbiFlags(NULL), |
| m_pGOTSymbol(NULL), |
| m_pPLTSymbol(NULL), |
| m_pGpDispSymbol(NULL) { |
| } |
| |
| MipsGNULDBackend::~MipsGNULDBackend() { |
| delete m_pRelocator; |
| delete m_pPLT; |
| delete m_pRelPlt; |
| delete m_pRelDyn; |
| delete m_pDynamic; |
| } |
| |
| bool MipsGNULDBackend::needsLA25Stub(Relocation::Type pType, |
| const mcld::ResolveInfo* pSym) { |
| if (config().isCodeIndep()) |
| return false; |
| |
| if (llvm::ELF::R_MIPS_26 != pType) |
| return false; |
| |
| if (pSym->isLocal()) |
| return false; |
| |
| return true; |
| } |
| |
| void MipsGNULDBackend::addNonPICBranchSym(ResolveInfo* rsym) { |
| m_HasNonPICBranchSyms.insert(rsym); |
| } |
| |
| bool MipsGNULDBackend::hasNonPICBranch(const ResolveInfo* rsym) const { |
| return m_HasNonPICBranchSyms.count(rsym); |
| } |
| |
| void MipsGNULDBackend::initTargetSections(Module& pModule, |
| ObjectBuilder& pBuilder) { |
| if (LinkerConfig::Object == config().codeGenType()) |
| return; |
| |
| ELFFileFormat* file_format = getOutputFormat(); |
| |
| // initialize .rel.plt |
| LDSection& relplt = file_format->getRelPlt(); |
| m_pRelPlt = new OutputRelocSection(pModule, relplt); |
| |
| // initialize .rel.dyn |
| LDSection& reldyn = file_format->getRelDyn(); |
| m_pRelDyn = new OutputRelocSection(pModule, reldyn); |
| |
| // initialize .sdata |
| m_psdata = pBuilder.CreateSection( |
| ".sdata", LDFileFormat::Target, llvm::ELF::SHT_PROGBITS, |
| llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE | llvm::ELF::SHF_MIPS_GPREL, |
| 4); |
| |
| // initialize .MIPS.abiflags |
| m_pAbiFlags = pBuilder.CreateSection(".MIPS.abiflags", LDFileFormat::Target, |
| llvm::ELF::SHT_MIPS_ABIFLAGS, |
| llvm::ELF::SHF_ALLOC, 4); |
| } |
| |
| void MipsGNULDBackend::initTargetSymbols(IRBuilder& pBuilder, Module& pModule) { |
| // Define the symbol _GLOBAL_OFFSET_TABLE_ if there is a symbol with the |
| // same name in input |
| m_pGOTSymbol = pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>( |
| "_GLOBAL_OFFSET_TABLE_", |
| ResolveInfo::Object, |
| ResolveInfo::Define, |
| ResolveInfo::Local, |
| 0x0, // size |
| 0x0, // value |
| FragmentRef::Null(), // FragRef |
| ResolveInfo::Hidden); |
| |
| // Define the symbol _PROCEDURE_LINKAGE_TABLE_ if there is a symbol with the |
| // same name in input |
| m_pPLTSymbol = pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>( |
| "_PROCEDURE_LINKAGE_TABLE_", |
| ResolveInfo::Object, |
| ResolveInfo::Define, |
| ResolveInfo::Local, |
| 0x0, // size |
| 0x0, // value |
| FragmentRef::Null(), // FragRef |
| ResolveInfo::Hidden); |
| |
| m_pGpDispSymbol = |
| pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>( |
| "_gp_disp", |
| ResolveInfo::Section, |
| ResolveInfo::Define, |
| ResolveInfo::Absolute, |
| 0x0, // size |
| 0x0, // value |
| FragmentRef::Null(), // FragRef |
| ResolveInfo::Default); |
| |
| pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Unresolve>( |
| "_gp", |
| ResolveInfo::NoType, |
| ResolveInfo::Define, |
| ResolveInfo::Absolute, |
| 0x0, // size |
| 0x0, // value |
| FragmentRef::Null(), // FragRef |
| ResolveInfo::Default); |
| } |
| |
| const Relocator* MipsGNULDBackend::getRelocator() const { |
| assert(m_pRelocator != NULL); |
| return m_pRelocator; |
| } |
| |
| Relocator* MipsGNULDBackend::getRelocator() { |
| assert(m_pRelocator != NULL); |
| return m_pRelocator; |
| } |
| |
| void MipsGNULDBackend::doPreLayout(IRBuilder& pBuilder) { |
| // initialize .dynamic data |
| if (!config().isCodeStatic() && m_pDynamic == NULL) |
| m_pDynamic = new MipsELFDynamic(*this, config()); |
| |
| if (m_pAbiInfo.hasValue()) |
| m_pAbiFlags->setSize(m_pAbiInfo->size()); |
| |
| // 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->finalizeScanning(*m_pRelDyn); |
| m_pGOT->finalizeSectionSize(); |
| |
| defineGOTSymbol(pBuilder); |
| } |
| |
| if (m_pGOTPLT->hasGOT1()) { |
| m_pGOTPLT->finalizeSectionSize(); |
| |
| defineGOTPLTSymbol(pBuilder); |
| } |
| |
| if (m_pPLT->hasPLT1()) |
| m_pPLT->finalizeSectionSize(); |
| |
| ELFFileFormat* file_format = getOutputFormat(); |
| |
| // 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()); |
| } |
| |
| // 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()); |
| } |
| } |
| } |
| |
| void MipsGNULDBackend::doPostLayout(Module& pModule, IRBuilder& pBuilder) { |
| const ELFFileFormat* format = getOutputFormat(); |
| |
| if (format->hasGOTPLT()) { |
| assert(m_pGOTPLT != NULL && "doPostLayout failed, m_pGOTPLT is NULL!"); |
| m_pGOTPLT->applyAllGOTPLT(m_pPLT->addr()); |
| } |
| |
| if (format->hasPLT()) { |
| assert(m_pPLT != NULL && "doPostLayout failed, m_pPLT is NULL!"); |
| m_pPLT->applyAllPLT(*m_pGOTPLT); |
| } |
| |
| m_pInfo.setABIVersion(m_pPLT && m_pPLT->hasPLT1() ? 1 : 0); |
| } |
| |
| /// dynamic - the dynamic section of the target machine. |
| /// Use co-variant return type to return its own dynamic section. |
| MipsELFDynamic& MipsGNULDBackend::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 MipsELFDynamic& MipsGNULDBackend::dynamic() const { |
| assert(m_pDynamic != NULL); |
| return *m_pDynamic; |
| } |
| |
| uint64_t MipsGNULDBackend::emitSectionData(const LDSection& pSection, |
| MemoryRegion& pRegion) const { |
| assert(pRegion.size() && "Size of MemoryRegion is zero!"); |
| |
| const ELFFileFormat* file_format = getOutputFormat(); |
| |
| if (file_format->hasGOT() && (&pSection == &(file_format->getGOT()))) { |
| return m_pGOT->emit(pRegion); |
| } |
| |
| if (file_format->hasPLT() && (&pSection == &(file_format->getPLT()))) { |
| return m_pPLT->emit(pRegion); |
| } |
| |
| if (file_format->hasGOTPLT() && (&pSection == &(file_format->getGOTPLT()))) { |
| return m_pGOTPLT->emit(pRegion); |
| } |
| |
| if (&pSection == m_pAbiFlags && m_pAbiInfo.hasValue()) |
| return MipsAbiFlags::emit(*m_pAbiInfo, pRegion); |
| |
| if (&pSection == m_psdata && m_psdata->hasSectionData()) { |
| 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; |
| } |
| return pRegion.size(); |
| } |
| |
| fatal(diag::unrecognized_output_sectoin) << pSection.name() |
| << "[email protected]"; |
| return 0; |
| } |
| |
| bool MipsGNULDBackend::hasEntryInStrTab(const LDSymbol& pSym) const { |
| return ResolveInfo::Section != pSym.type() || m_pGpDispSymbol == &pSym; |
| } |
| |
| namespace { |
| struct DynsymGOTCompare { |
| const MipsGOT& m_pGOT; |
| |
| explicit DynsymGOTCompare(const MipsGOT& pGOT) : m_pGOT(pGOT) {} |
| |
| bool operator()(const LDSymbol* X, const LDSymbol* Y) const { |
| return m_pGOT.dynSymOrderCompare(X, Y); |
| } |
| }; |
| } // anonymous namespace |
| |
| void MipsGNULDBackend::orderSymbolTable(Module& pModule) { |
| if (config().options().hasGNUHash()) { |
| // The MIPS ABI and .gnu.hash require .dynsym to be sorted |
| // in different ways. The MIPS ABI requires a mapping between |
| // the GOT and the symbol table. At the same time .gnu.hash |
| // needs symbols to be grouped by hash code. |
| llvm::errs() << ".gnu.hash is incompatible with the MIPS ABI\n"; |
| } |
| |
| Module::SymbolTable& symbols = pModule.getSymbolTable(); |
| |
| std::stable_sort( |
| symbols.dynamicBegin(), symbols.dynamicEnd(), DynsymGOTCompare(*m_pGOT)); |
| } |
| |
| } // namespace mcld |
| |
| namespace llvm { |
| namespace ELF { |
| // SHT_MIPS_OPTIONS section's block descriptor. |
| struct Elf_Options { |
| unsigned char kind; // Determines interpretation of variable |
| // part of descriptor. See ODK_xxx enumeration. |
| unsigned char size; // Byte size of descriptor, including this header. |
| Elf64_Half section; // Section header index of section affected, |
| // or 0 for global options. |
| Elf64_Word info; // Kind-specific information. |
| }; |
| |
| // Content of ODK_REGINFO block in SHT_MIPS_OPTIONS section on 32 bit ABI. |
| struct Elf32_RegInfo { |
| Elf32_Word ri_gprmask; // Mask of general purpose registers used. |
| Elf32_Word ri_cprmask[4]; // Mask of co-processor registers used. |
| Elf32_Addr ri_gp_value; // GP register value for this object file. |
| }; |
| |
| // Content of ODK_REGINFO block in SHT_MIPS_OPTIONS section on 64 bit ABI. |
| struct Elf64_RegInfo { |
| Elf32_Word ri_gprmask; // Mask of general purpose registers used. |
| Elf32_Word ri_pad; // Padding. |
| Elf32_Word ri_cprmask[4]; // Mask of co-processor registers used. |
| Elf64_Addr ri_gp_value; // GP register value for this object file. |
| }; |
| |
| } // namespace ELF |
| } // namespace llvm |
| |
| namespace mcld { |
| |
| static const char* ArchName(uint64_t flagBits) { |
| switch (flagBits) { |
| case llvm::ELF::EF_MIPS_ARCH_1: |
| return "mips1"; |
| case llvm::ELF::EF_MIPS_ARCH_2: |
| return "mips2"; |
| case llvm::ELF::EF_MIPS_ARCH_3: |
| return "mips3"; |
| case llvm::ELF::EF_MIPS_ARCH_4: |
| return "mips4"; |
| case llvm::ELF::EF_MIPS_ARCH_5: |
| return "mips5"; |
| case llvm::ELF::EF_MIPS_ARCH_32: |
| return "mips32"; |
| case llvm::ELF::EF_MIPS_ARCH_64: |
| return "mips64"; |
| case llvm::ELF::EF_MIPS_ARCH_32R2: |
| return "mips32r2"; |
| case llvm::ELF::EF_MIPS_ARCH_64R2: |
| return "mips64r2"; |
| case llvm::ELF::EF_MIPS_ARCH_32R6: |
| return "mips32r6"; |
| case llvm::ELF::EF_MIPS_ARCH_64R6: |
| return "mips64r6"; |
| default: |
| return "Unknown Arch"; |
| } |
| } |
| |
| void MipsGNULDBackend::mergeFlags(Input& pInput, const char* ELF_hdr) { |
| bool isTarget64Bit = config().targets().triple().isArch64Bit(); |
| bool isInput64Bit = ELF_hdr[llvm::ELF::EI_CLASS] == llvm::ELF::ELFCLASS64; |
| |
| if (isTarget64Bit != isInput64Bit) { |
| fatal(diag::error_Mips_incompatible_class) |
| << (isTarget64Bit ? "ELFCLASS64" : "ELFCLASS32") |
| << (isInput64Bit ? "ELFCLASS64" : "ELFCLASS32") << pInput.name(); |
| return; |
| } |
| |
| m_ElfFlagsMap[&pInput] = |
| isInput64Bit ? |
| reinterpret_cast<const llvm::ELF::Elf64_Ehdr*>(ELF_hdr)->e_flags : |
| reinterpret_cast<const llvm::ELF::Elf32_Ehdr*>(ELF_hdr)->e_flags; |
| } |
| |
| bool MipsGNULDBackend::readSection(Input& pInput, SectionData& pSD) { |
| if ((pSD.getSection().flag() & llvm::ELF::SHF_MIPS_GPREL) || |
| (pSD.getSection().type() == llvm::ELF::SHT_MIPS_ABIFLAGS)) { |
| uint64_t offset = pInput.fileOffset() + pSD.getSection().offset(); |
| uint64_t size = pSD.getSection().size(); |
| |
| Fragment* frag = IRBuilder::CreateRegion(pInput, offset, size); |
| ObjectBuilder::AppendFragment(*frag, pSD); |
| return true; |
| } |
| |
| if (pSD.getSection().type() == llvm::ELF::SHT_MIPS_OPTIONS) { |
| 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) { |
| const llvm::ELF::Elf_Options* optb = |
| reinterpret_cast<const llvm::ELF::Elf_Options*>(region.begin()); |
| const llvm::ELF::Elf_Options* opte = |
| reinterpret_cast<const llvm::ELF::Elf_Options*>(region.begin() + |
| size); |
| |
| for (const llvm::ELF::Elf_Options* opt = optb; opt < opte; |
| opt += opt->size) { |
| switch (opt->kind) { |
| default: |
| // Nothing to do. |
| break; |
| case llvm::ELF::ODK_REGINFO: |
| if (config().targets().triple().isArch32Bit()) { |
| const llvm::ELF::Elf32_RegInfo* reg = |
| reinterpret_cast<const llvm::ELF::Elf32_RegInfo*>(opt + 1); |
| m_GP0Map[&pInput] = reg->ri_gp_value; |
| } else { |
| const llvm::ELF::Elf64_RegInfo* reg = |
| reinterpret_cast<const llvm::ELF::Elf64_RegInfo*>(opt + 1); |
| m_GP0Map[&pInput] = reg->ri_gp_value; |
| } |
| break; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| return GNULDBackend::readSection(pInput, pSD); |
| } |
| |
| MipsGOT& MipsGNULDBackend::getGOT() { |
| assert(m_pGOT != NULL); |
| return *m_pGOT; |
| } |
| |
| const MipsGOT& MipsGNULDBackend::getGOT() const { |
| assert(m_pGOT != NULL); |
| return *m_pGOT; |
| } |
| |
| MipsPLT& MipsGNULDBackend::getPLT() { |
| assert(m_pPLT != NULL); |
| return *m_pPLT; |
| } |
| |
| const MipsPLT& MipsGNULDBackend::getPLT() const { |
| assert(m_pPLT != NULL); |
| return *m_pPLT; |
| } |
| |
| MipsGOTPLT& MipsGNULDBackend::getGOTPLT() { |
| assert(m_pGOTPLT != NULL); |
| return *m_pGOTPLT; |
| } |
| |
| const MipsGOTPLT& MipsGNULDBackend::getGOTPLT() const { |
| assert(m_pGOTPLT != NULL); |
| return *m_pGOTPLT; |
| } |
| |
| OutputRelocSection& MipsGNULDBackend::getRelPLT() { |
| assert(m_pRelPlt != NULL); |
| return *m_pRelPlt; |
| } |
| |
| const OutputRelocSection& MipsGNULDBackend::getRelPLT() const { |
| assert(m_pRelPlt != NULL); |
| return *m_pRelPlt; |
| } |
| |
| OutputRelocSection& MipsGNULDBackend::getRelDyn() { |
| assert(m_pRelDyn != NULL); |
| return *m_pRelDyn; |
| } |
| |
| const OutputRelocSection& MipsGNULDBackend::getRelDyn() const { |
| assert(m_pRelDyn != NULL); |
| return *m_pRelDyn; |
| } |
| |
| unsigned int MipsGNULDBackend::getTargetSectionOrder( |
| const LDSection& pSectHdr) const { |
| const ELFFileFormat* file_format = getOutputFormat(); |
| |
| if (file_format->hasGOT() && (&pSectHdr == &file_format->getGOT())) |
| return SHO_DATA; |
| |
| if (file_format->hasGOTPLT() && (&pSectHdr == &file_format->getGOTPLT())) |
| return SHO_DATA; |
| |
| if (file_format->hasPLT() && (&pSectHdr == &file_format->getPLT())) |
| return SHO_PLT; |
| |
| if (&pSectHdr == m_psdata) |
| return SHO_SMALL_DATA; |
| |
| if (&pSectHdr == m_pAbiFlags) |
| return SHO_RO_NOTE; |
| |
| return SHO_UNDEFINED; |
| } |
| |
| /// finalizeSymbol - finalize the symbol value |
| bool MipsGNULDBackend::finalizeTargetSymbols() { |
| if (m_pGpDispSymbol != NULL) |
| m_pGpDispSymbol->setValue(m_pGOT->getGPDispAddress()); |
| |
| return true; |
| } |
| |
| /// allocateCommonSymbols - allocate common symbols in the corresponding |
| /// sections. This is called at pre-layout stage. |
| /// FIXME: Mips needs to allocate small common symbol |
| bool MipsGNULDBackend::allocateCommonSymbols(Module& pModule) { |
| SymbolCategory& symbol_list = pModule.getSymbolTable(); |
| |
| if (symbol_list.emptyCommons() && symbol_list.emptyFiles() && |
| symbol_list.emptyLocals() && symbol_list.emptyLocalDyns()) |
| return true; |
| |
| SymbolCategory::iterator com_sym, com_end; |
| |
| // FIXME: If the order of common symbols is defined, then sort common symbols |
| // std::sort(com_sym, com_end, some kind of order); |
| |
| // get corresponding BSS LDSection |
| ELFFileFormat* file_format = getOutputFormat(); |
| LDSection& bss_sect = file_format->getBSS(); |
| LDSection& tbss_sect = file_format->getTBSS(); |
| |
| // get or create corresponding BSS SectionData |
| SectionData* bss_sect_data = NULL; |
| if (bss_sect.hasSectionData()) |
| bss_sect_data = bss_sect.getSectionData(); |
| else |
| bss_sect_data = IRBuilder::CreateSectionData(bss_sect); |
| |
| SectionData* tbss_sect_data = NULL; |
| if (tbss_sect.hasSectionData()) |
| tbss_sect_data = tbss_sect.getSectionData(); |
| else |
| tbss_sect_data = IRBuilder::CreateSectionData(tbss_sect); |
| |
| // remember original BSS size |
| uint64_t bss_offset = bss_sect.size(); |
| uint64_t tbss_offset = tbss_sect.size(); |
| |
| // allocate all local common symbols |
| com_end = symbol_list.localEnd(); |
| |
| for (com_sym = symbol_list.localBegin(); com_sym != com_end; ++com_sym) { |
| if (ResolveInfo::Common == (*com_sym)->desc()) { |
| // We have to reset the description of the symbol here. When doing |
| // incremental linking, the output relocatable object may have common |
| // symbols. Therefore, we can not treat common symbols as normal symbols |
| // when emitting the regular name pools. We must change the symbols' |
| // description here. |
| (*com_sym)->resolveInfo()->setDesc(ResolveInfo::Define); |
| Fragment* frag = new FillFragment(0x0, 1, (*com_sym)->size()); |
| |
| if (ResolveInfo::ThreadLocal == (*com_sym)->type()) { |
| // allocate TLS common symbol in tbss section |
| tbss_offset += ObjectBuilder::AppendFragment( |
| *frag, *tbss_sect_data, (*com_sym)->value()); |
| ObjectBuilder::UpdateSectionAlign(tbss_sect, (*com_sym)->value()); |
| (*com_sym)->setFragmentRef(FragmentRef::Create(*frag, 0)); |
| } else { |
| // FIXME: how to identify small and large common symbols? |
| bss_offset += ObjectBuilder::AppendFragment( |
| *frag, *bss_sect_data, (*com_sym)->value()); |
| ObjectBuilder::UpdateSectionAlign(bss_sect, (*com_sym)->value()); |
| (*com_sym)->setFragmentRef(FragmentRef::Create(*frag, 0)); |
| } |
| } |
| } |
| |
| // allocate all global common symbols |
| com_end = symbol_list.commonEnd(); |
| for (com_sym = symbol_list.commonBegin(); com_sym != com_end; ++com_sym) { |
| // We have to reset the description of the symbol here. When doing |
| // incremental linking, the output relocatable object may have common |
| // symbols. Therefore, we can not treat common symbols as normal symbols |
| // when emitting the regular name pools. We must change the symbols' |
| // description here. |
| (*com_sym)->resolveInfo()->setDesc(ResolveInfo::Define); |
| Fragment* frag = new FillFragment(0x0, 1, (*com_sym)->size()); |
| |
| if (ResolveInfo::ThreadLocal == (*com_sym)->type()) { |
| // allocate TLS common symbol in tbss section |
| tbss_offset += ObjectBuilder::AppendFragment( |
| *frag, *tbss_sect_data, (*com_sym)->value()); |
| ObjectBuilder::UpdateSectionAlign(tbss_sect, (*com_sym)->value()); |
| (*com_sym)->setFragmentRef(FragmentRef::Create(*frag, 0)); |
| } else { |
| // FIXME: how to identify small and large common symbols? |
| bss_offset += ObjectBuilder::AppendFragment( |
| *frag, *bss_sect_data, (*com_sym)->value()); |
| ObjectBuilder::UpdateSectionAlign(bss_sect, (*com_sym)->value()); |
| (*com_sym)->setFragmentRef(FragmentRef::Create(*frag, 0)); |
| } |
| } |
| |
| bss_sect.setSize(bss_offset); |
| tbss_sect.setSize(tbss_offset); |
| symbol_list.changeCommonsToGlobal(); |
| return true; |
| } |
| |
| uint64_t MipsGNULDBackend::getTPOffset(const Input& pInput) const { |
| return m_TpOffsetMap.lookup(&pInput); |
| } |
| |
| uint64_t MipsGNULDBackend::getDTPOffset(const Input& pInput) const { |
| return m_DtpOffsetMap.lookup(&pInput); |
| } |
| |
| uint64_t MipsGNULDBackend::getGP0(const Input& pInput) const { |
| return m_GP0Map.lookup(&pInput); |
| } |
| |
| void MipsGNULDBackend::defineGOTSymbol(IRBuilder& pBuilder) { |
| // If we do not reserve any GOT entries, we do not need to re-define GOT |
| // symbol. |
| if (!m_pGOT->hasGOT1()) |
| return; |
| |
| // define symbol _GLOBAL_OFFSET_TABLE_ |
| 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); |
| } |
| } |
| |
| void MipsGNULDBackend::defineGOTPLTSymbol(IRBuilder& pBuilder) { |
| // define symbol _PROCEDURE_LINKAGE_TABLE_ |
| if (m_pPLTSymbol != NULL) { |
| pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Unresolve>( |
| "_PROCEDURE_LINKAGE_TABLE_", |
| ResolveInfo::Object, |
| ResolveInfo::Define, |
| ResolveInfo::Local, |
| 0x0, // size |
| 0x0, // value |
| FragmentRef::Create(*(m_pPLT->begin()), 0x0), |
| ResolveInfo::Hidden); |
| } else { |
| m_pPLTSymbol = pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Resolve>( |
| "_PROCEDURE_LINKAGE_TABLE_", |
| ResolveInfo::Object, |
| ResolveInfo::Define, |
| ResolveInfo::Local, |
| 0x0, // size |
| 0x0, // value |
| FragmentRef::Create(*(m_pPLT->begin()), 0x0), |
| ResolveInfo::Hidden); |
| } |
| } |
| |
| /// doCreateProgramHdrs - backend can implement this function to create the |
| /// target-dependent segments |
| void MipsGNULDBackend::doCreateProgramHdrs(Module& pModule) { |
| if (!m_pAbiFlags || m_pAbiFlags->size() == 0) |
| return; |
| |
| // create PT_MIPS_ABIFLAGS segment |
| ELFSegmentFactory::iterator sit = |
| elfSegmentTable().find(llvm::ELF::PT_INTERP, 0x0, 0x0); |
| if (sit == elfSegmentTable().end()) |
| sit = elfSegmentTable().find(llvm::ELF::PT_PHDR, 0x0, 0x0); |
| if (sit == elfSegmentTable().end()) |
| sit = elfSegmentTable().begin(); |
| else |
| ++sit; |
| |
| ELFSegment* abiSeg = elfSegmentTable().insert(sit, |
| llvm::ELF::PT_MIPS_ABIFLAGS, |
| llvm::ELF::PF_R); |
| abiSeg->setAlign(8); |
| abiSeg->append(m_pAbiFlags); |
| } |
| |
| bool MipsGNULDBackend::relaxRelocation(IRBuilder& pBuilder, Relocation& pRel) { |
| uint64_t sym_value = 0x0; |
| |
| LDSymbol* symbol = pRel.symInfo()->outSymbol(); |
| if (symbol->hasFragRef()) { |
| uint64_t value = symbol->fragRef()->getOutputOffset(); |
| uint64_t addr = symbol->fragRef()->frag()->getParent()->getSection().addr(); |
| sym_value = addr + value; |
| } |
| |
| Stub* stub = getStubFactory()->create( |
| pRel, sym_value, pBuilder, *getBRIslandFactory()); |
| |
| if (stub == NULL) |
| return false; |
| |
| assert(stub->symInfo() != NULL); |
| // reset the branch target of the reloc to this stub instead |
| pRel.setSymInfo(stub->symInfo()); |
| |
| // increase the size of .symtab and .strtab |
| LDSection& symtab = getOutputFormat()->getSymTab(); |
| LDSection& strtab = getOutputFormat()->getStrTab(); |
| symtab.setSize(symtab.size() + sizeof(llvm::ELF::Elf32_Sym)); |
| strtab.setSize(strtab.size() + stub->symInfo()->nameSize() + 1); |
| |
| return true; |
| } |
| |
| bool MipsGNULDBackend::doRelax(Module& pModule, |
| IRBuilder& pBuilder, |
| bool& pFinished) { |
| assert(getStubFactory() != NULL && getBRIslandFactory() != NULL); |
| |
| bool isRelaxed = false; |
| |
| for (Module::obj_iterator input = pModule.obj_begin(); |
| input != pModule.obj_end(); |
| ++input) { |
| LDContext* context = (*input)->context(); |
| |
| for (LDContext::sect_iterator rs = context->relocSectBegin(); |
| rs != context->relocSectEnd(); |
| ++rs) { |
| LDSection* sec = *rs; |
| |
| if (LDFileFormat::Ignore == sec->kind() || !sec->hasRelocData()) |
| continue; |
| |
| for (RelocData::iterator reloc = sec->getRelocData()->begin(); |
| reloc != sec->getRelocData()->end(); |
| ++reloc) { |
| if (llvm::ELF::R_MIPS_26 != reloc->type()) |
| continue; |
| |
| if (relaxRelocation(pBuilder, *llvm::cast<Relocation>(reloc))) |
| isRelaxed = true; |
| } |
| } |
| } |
| |
| // find the first fragment w/ invalid offset due to stub insertion |
| std::vector<Fragment*> invalid_frags; |
| pFinished = true; |
| for (BranchIslandFactory::iterator ii = getBRIslandFactory()->begin(), |
| ie = getBRIslandFactory()->end(); |
| ii != ie; |
| ++ii) { |
| BranchIsland& island = *ii; |
| 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; |
| } |
| |
| bool MipsGNULDBackend::initTargetStubs() { |
| if (getStubFactory() == NULL) |
| return false; |
| |
| getStubFactory()->addPrototype(new MipsLA25Stub(*this)); |
| return true; |
| } |
| |
| bool MipsGNULDBackend::readRelocation(const llvm::ELF::Elf32_Rel& pRel, |
| Relocation::Type& pType, |
| uint32_t& pSymIdx, |
| uint32_t& pOffset) const { |
| return GNULDBackend::readRelocation(pRel, pType, pSymIdx, pOffset); |
| } |
| |
| bool MipsGNULDBackend::readRelocation(const llvm::ELF::Elf32_Rela& pRel, |
| Relocation::Type& pType, |
| uint32_t& pSymIdx, |
| uint32_t& pOffset, |
| int32_t& pAddend) const { |
| return GNULDBackend::readRelocation(pRel, pType, pSymIdx, pOffset, pAddend); |
| } |
| |
| bool MipsGNULDBackend::readRelocation(const llvm::ELF::Elf64_Rel& pRel, |
| Relocation::Type& pType, |
| uint32_t& pSymIdx, |
| uint64_t& pOffset) const { |
| uint64_t r_info = 0x0; |
| if (llvm::sys::IsLittleEndianHost) { |
| pOffset = pRel.r_offset; |
| r_info = pRel.r_info; |
| } else { |
| pOffset = mcld::bswap64(pRel.r_offset); |
| r_info = mcld::bswap64(pRel.r_info); |
| } |
| |
| // MIPS 64 little endian (we do not support big endian now) |
| // has a "special" encoding of r_info relocation |
| // field. Instead of one 64 bit little endian number, it is a little |
| // endian 32 bit number followed by a 32 bit big endian number. |
| pType = mcld::bswap32(r_info >> 32); |
| pSymIdx = r_info & 0xffffffff; |
| return true; |
| } |
| |
| bool MipsGNULDBackend::readRelocation(const llvm::ELF::Elf64_Rela& pRel, |
| Relocation::Type& pType, |
| uint32_t& pSymIdx, |
| uint64_t& pOffset, |
| int64_t& pAddend) const { |
| uint64_t r_info = 0x0; |
| if (llvm::sys::IsLittleEndianHost) { |
| pOffset = pRel.r_offset; |
| r_info = pRel.r_info; |
| pAddend = pRel.r_addend; |
| } else { |
| pOffset = mcld::bswap64(pRel.r_offset); |
| r_info = mcld::bswap64(pRel.r_info); |
| pAddend = mcld::bswap64(pRel.r_addend); |
| } |
| |
| pType = mcld::bswap32(r_info >> 32); |
| pSymIdx = r_info & 0xffffffff; |
| return true; |
| } |
| |
| void MipsGNULDBackend::emitRelocation(llvm::ELF::Elf32_Rel& pRel, |
| Relocation::Type pType, |
| uint32_t pSymIdx, |
| uint32_t pOffset) const { |
| GNULDBackend::emitRelocation(pRel, pType, pSymIdx, pOffset); |
| } |
| |
| void MipsGNULDBackend::emitRelocation(llvm::ELF::Elf32_Rela& pRel, |
| Relocation::Type pType, |
| uint32_t pSymIdx, |
| uint32_t pOffset, |
| int32_t pAddend) const { |
| GNULDBackend::emitRelocation(pRel, pType, pSymIdx, pOffset, pAddend); |
| } |
| |
| void MipsGNULDBackend::emitRelocation(llvm::ELF::Elf64_Rel& pRel, |
| Relocation::Type pType, |
| uint32_t pSymIdx, |
| uint64_t pOffset) const { |
| uint64_t r_info = mcld::bswap32(pType); |
| r_info <<= 32; |
| r_info |= pSymIdx; |
| |
| pRel.r_info = r_info; |
| pRel.r_offset = pOffset; |
| } |
| |
| void MipsGNULDBackend::emitRelocation(llvm::ELF::Elf64_Rela& pRel, |
| Relocation::Type pType, |
| uint32_t pSymIdx, |
| uint64_t pOffset, |
| int64_t pAddend) const { |
| uint64_t r_info = mcld::bswap32(pType); |
| r_info <<= 32; |
| r_info |= pSymIdx; |
| |
| pRel.r_info = r_info; |
| pRel.r_offset = pOffset; |
| pRel.r_addend = pAddend; |
| } |
| |
| namespace { |
| struct ISATreeEdge { |
| unsigned child; |
| unsigned parent; |
| }; |
| } |
| |
| static ISATreeEdge isaTree[] = { |
| // MIPS32R6 and MIPS64R6 are not compatible with other extensions |
| |
| // MIPS64 extensions. |
| {llvm::ELF::EF_MIPS_ARCH_64R2, llvm::ELF::EF_MIPS_ARCH_64}, |
| // MIPS V extensions. |
| {llvm::ELF::EF_MIPS_ARCH_64, llvm::ELF::EF_MIPS_ARCH_5}, |
| // MIPS IV extensions. |
| {llvm::ELF::EF_MIPS_ARCH_5, llvm::ELF::EF_MIPS_ARCH_4}, |
| // MIPS III extensions. |
| {llvm::ELF::EF_MIPS_ARCH_4, llvm::ELF::EF_MIPS_ARCH_3}, |
| // MIPS32 extensions. |
| {llvm::ELF::EF_MIPS_ARCH_32R2, llvm::ELF::EF_MIPS_ARCH_32}, |
| // MIPS II extensions. |
| {llvm::ELF::EF_MIPS_ARCH_3, llvm::ELF::EF_MIPS_ARCH_2}, |
| {llvm::ELF::EF_MIPS_ARCH_32, llvm::ELF::EF_MIPS_ARCH_2}, |
| // MIPS I extensions. |
| {llvm::ELF::EF_MIPS_ARCH_2, llvm::ELF::EF_MIPS_ARCH_1}, |
| }; |
| |
| static bool isIsaMatched(uint32_t base, uint32_t ext) { |
| if (base == ext) |
| return true; |
| if (base == llvm::ELF::EF_MIPS_ARCH_32 && |
| isIsaMatched(llvm::ELF::EF_MIPS_ARCH_64, ext)) |
| return true; |
| if (base == llvm::ELF::EF_MIPS_ARCH_32R2 && |
| isIsaMatched(llvm::ELF::EF_MIPS_ARCH_64R2, ext)) |
| return true; |
| for (const auto &edge : isaTree) { |
| if (ext == edge.child) { |
| ext = edge.parent; |
| if (ext == base) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| static bool getAbiFlags(const Input& pInput, uint64_t elfFlags, bool& hasFlags, |
| MipsAbiFlags& pFlags) { |
| MipsAbiFlags pElfFlags = {}; |
| if (!MipsAbiFlags::fillByElfFlags(pInput, elfFlags, pElfFlags)) |
| return false; |
| |
| const LDContext* ctx = pInput.context(); |
| for (auto it = ctx->sectBegin(), ie = ctx->sectEnd(); it != ie; ++it) |
| if ((*it)->type() == llvm::ELF::SHT_MIPS_ABIFLAGS) { |
| if (!MipsAbiFlags::fillBySection(pInput, **it, pFlags)) |
| return false; |
| if (!MipsAbiFlags::isCompatible(pInput, pElfFlags, pFlags)) |
| return false; |
| hasFlags = true; |
| return true; |
| } |
| |
| pFlags = pElfFlags; |
| return true; |
| } |
| |
| static const char* getNanName(uint64_t flags) { |
| return flags & llvm::ELF::EF_MIPS_NAN2008 ? "2008" : "legacy"; |
| } |
| |
| static bool mergeElfFlags(const Input& pInput, uint64_t& oldElfFlags, |
| uint64_t newElfFlags) { |
| // PIC code is inherently CPIC and may not set CPIC flag explicitly. |
| // Ensure that this flag will exist in the linked file. |
| if (newElfFlags & llvm::ELF::EF_MIPS_PIC) |
| newElfFlags |= llvm::ELF::EF_MIPS_CPIC; |
| |
| if (newElfFlags & llvm::ELF::EF_MIPS_ARCH_ASE_M16) { |
| error(diag::error_Mips_m16_unsupported) << pInput.name(); |
| return false; |
| } |
| |
| if (!oldElfFlags) { |
| oldElfFlags = newElfFlags; |
| return true; |
| } |
| |
| uint64_t newPic = |
| newElfFlags & (llvm::ELF::EF_MIPS_PIC | llvm::ELF::EF_MIPS_CPIC); |
| uint64_t oldPic = |
| oldElfFlags & (llvm::ELF::EF_MIPS_PIC | llvm::ELF::EF_MIPS_CPIC); |
| |
| // Check PIC / CPIC flags compatibility. |
| if ((newPic != 0) != (oldPic != 0)) |
| warning(diag::warn_Mips_abicalls_linking) << pInput.name(); |
| |
| if (!(newPic & llvm::ELF::EF_MIPS_PIC)) |
| oldElfFlags &= ~llvm::ELF::EF_MIPS_PIC; |
| if (newPic) |
| oldElfFlags |= llvm::ELF::EF_MIPS_CPIC; |
| |
| // Check ISA compatibility. |
| uint64_t newArch = newElfFlags & llvm::ELF::EF_MIPS_ARCH; |
| uint64_t oldArch = oldElfFlags & llvm::ELF::EF_MIPS_ARCH; |
| if (!isIsaMatched(newArch, oldArch)) { |
| if (!isIsaMatched(oldArch, newArch)) { |
| error(diag::error_Mips_inconsistent_arch) |
| << ArchName(oldArch) << ArchName(newArch) << pInput.name(); |
| return false; |
| } |
| oldElfFlags &= ~llvm::ELF::EF_MIPS_ARCH; |
| oldElfFlags |= newArch; |
| } |
| |
| // Check ABI compatibility. |
| uint32_t newAbi = newElfFlags & llvm::ELF::EF_MIPS_ABI; |
| uint32_t oldAbi = oldElfFlags & llvm::ELF::EF_MIPS_ABI; |
| if (newAbi != oldAbi && newAbi && oldAbi) { |
| error(diag::error_Mips_inconsistent_abi) << pInput.name(); |
| return false; |
| } |
| |
| // Check -mnan flags compatibility. |
| if ((newElfFlags & llvm::ELF::EF_MIPS_NAN2008) != |
| (oldElfFlags & llvm::ELF::EF_MIPS_NAN2008)) { |
| // Linking -mnan=2008 and -mnan=legacy modules |
| error(diag::error_Mips_inconsistent_mnan) |
| << getNanName(oldElfFlags) << getNanName(newElfFlags) << pInput.name(); |
| return false; |
| } |
| |
| // Check ASE compatibility. |
| uint64_t newAse = newElfFlags & llvm::ELF::EF_MIPS_ARCH_ASE; |
| uint64_t oldAse = oldElfFlags & llvm::ELF::EF_MIPS_ARCH_ASE; |
| if (newAse != oldAse) |
| oldElfFlags |= newAse; |
| |
| // Check FP64 compatibility. |
| if ((newElfFlags & llvm::ELF::EF_MIPS_FP64) != |
| (oldElfFlags & llvm::ELF::EF_MIPS_FP64)) { |
| // Linking -mnan=2008 and -mnan=legacy modules |
| error(diag::error_Mips_inconsistent_fp64) << pInput.name(); |
| return false; |
| } |
| |
| oldElfFlags |= newElfFlags & llvm::ELF::EF_MIPS_NOREORDER; |
| oldElfFlags |= newElfFlags & llvm::ELF::EF_MIPS_MICROMIPS; |
| oldElfFlags |= newElfFlags & llvm::ELF::EF_MIPS_NAN2008; |
| oldElfFlags |= newElfFlags & llvm::ELF::EF_MIPS_32BITMODE; |
| |
| return true; |
| } |
| |
| void MipsGNULDBackend::saveTPOffset(const Input& pInput) { |
| const LDContext* ctx = pInput.context(); |
| for (auto it = ctx->sectBegin(), ie = ctx->sectEnd(); it != ie; ++it) { |
| LDSection* sect = *it; |
| if (sect->flag() & llvm::ELF::SHF_TLS) { |
| m_TpOffsetMap[&pInput] = sect->addr() + 0x7000; |
| m_DtpOffsetMap[&pInput] = sect->addr() + 0x8000; |
| break; |
| } |
| } |
| } |
| |
| void MipsGNULDBackend::preMergeSections(Module& pModule) { |
| uint64_t elfFlags = 0; |
| bool hasAbiFlags = false; |
| MipsAbiFlags abiFlags = {}; |
| for (const Input *input : pModule.getObjectList()) { |
| if (input->type() != Input::Object) |
| continue; |
| |
| uint64_t newElfFlags = m_ElfFlagsMap[input]; |
| |
| MipsAbiFlags newAbiFlags = {}; |
| if (!getAbiFlags(*input, newElfFlags, hasAbiFlags, newAbiFlags)) |
| continue; |
| |
| if (!mergeElfFlags(*input, elfFlags, newElfFlags)) |
| continue; |
| |
| if (!MipsAbiFlags::merge(*input, abiFlags, newAbiFlags)) |
| continue; |
| |
| saveTPOffset(*input); |
| } |
| |
| m_pInfo.setElfFlags(elfFlags); |
| if (hasAbiFlags) |
| m_pAbiInfo = abiFlags; |
| } |
| |
| bool MipsGNULDBackend::mergeSection(Module& pModule, const Input& pInput, |
| LDSection& pSection) { |
| if (pSection.flag() & llvm::ELF::SHF_MIPS_GPREL) { |
| SectionData* sd = NULL; |
| if (!m_psdata->hasSectionData()) { |
| sd = IRBuilder::CreateSectionData(*m_psdata); |
| m_psdata->setSectionData(sd); |
| } |
| sd = m_psdata->getSectionData(); |
| moveSectionData(*pSection.getSectionData(), *sd); |
| } else if (pSection.type() == llvm::ELF::SHT_MIPS_ABIFLAGS) { |
| // Nothing to do because we handle all .MIPS.abiflags sections |
| // in the preMergeSections method. |
| } else { |
| ObjectBuilder builder(pModule); |
| builder.MergeSection(pInput, pSection); |
| } |
| return true; |
| } |
| |
| void MipsGNULDBackend::moveSectionData(SectionData& pFrom, SectionData& pTo) { |
| assert(&pFrom != &pTo && "Cannot move section data to itself!"); |
| |
| uint64_t offset = pTo.getSection().size(); |
| AlignFragment* align = NULL; |
| if (pFrom.getSection().align() > 1) { |
| // if the align constraint is larger than 1, append an alignment |
| unsigned int alignment = pFrom.getSection().align(); |
| align = new AlignFragment(/*alignment*/ alignment, |
| /*the filled value*/ 0x0, |
| /*the size of filled value*/ 1u, |
| /*max bytes to emit*/ alignment - 1); |
| align->setOffset(offset); |
| align->setParent(&pTo); |
| pTo.getFragmentList().push_back(align); |
| offset += align->size(); |
| } |
| |
| // move fragments from pFrom to pTO |
| SectionData::FragmentListType& from_list = pFrom.getFragmentList(); |
| SectionData::FragmentListType& to_list = pTo.getFragmentList(); |
| SectionData::FragmentListType::iterator frag, fragEnd = from_list.end(); |
| for (frag = from_list.begin(); frag != fragEnd; ++frag) { |
| frag->setParent(&pTo); |
| frag->setOffset(offset); |
| offset += frag->size(); |
| } |
| to_list.splice(to_list.end(), from_list); |
| |
| // set up pTo's header |
| pTo.getSection().setSize(offset); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Mips32GNULDBackend |
| //===----------------------------------------------------------------------===// |
| Mips32GNULDBackend::Mips32GNULDBackend(const LinkerConfig& pConfig, |
| MipsGNUInfo* pInfo) |
| : MipsGNULDBackend(pConfig, pInfo) { |
| } |
| |
| bool Mips32GNULDBackend::initRelocator() { |
| if (m_pRelocator == NULL) |
| m_pRelocator = new Mips32Relocator(*this, config()); |
| |
| return true; |
| } |
| |
| void Mips32GNULDBackend::initTargetSections(Module& pModule, |
| ObjectBuilder& pBuilder) { |
| MipsGNULDBackend::initTargetSections(pModule, pBuilder); |
| |
| if (LinkerConfig::Object == config().codeGenType()) |
| return; |
| |
| ELFFileFormat* fileFormat = getOutputFormat(); |
| |
| // initialize .got |
| LDSection& got = fileFormat->getGOT(); |
| m_pGOT = new Mips32GOT(got); |
| |
| // initialize .got.plt |
| LDSection& gotplt = fileFormat->getGOTPLT(); |
| m_pGOTPLT = new MipsGOTPLT(gotplt); |
| |
| // initialize .plt |
| LDSection& plt = fileFormat->getPLT(); |
| m_pPLT = new MipsPLT(plt); |
| } |
| |
| size_t Mips32GNULDBackend::getRelEntrySize() { |
| return 8; |
| } |
| |
| size_t Mips32GNULDBackend::getRelaEntrySize() { |
| return 12; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Mips64GNULDBackend |
| //===----------------------------------------------------------------------===// |
| Mips64GNULDBackend::Mips64GNULDBackend(const LinkerConfig& pConfig, |
| MipsGNUInfo* pInfo) |
| : MipsGNULDBackend(pConfig, pInfo) { |
| } |
| |
| bool Mips64GNULDBackend::initRelocator() { |
| if (m_pRelocator == NULL) |
| m_pRelocator = new Mips64Relocator(*this, config()); |
| |
| return true; |
| } |
| |
| void Mips64GNULDBackend::initTargetSections(Module& pModule, |
| ObjectBuilder& pBuilder) { |
| MipsGNULDBackend::initTargetSections(pModule, pBuilder); |
| |
| if (LinkerConfig::Object == config().codeGenType()) |
| return; |
| |
| ELFFileFormat* fileFormat = getOutputFormat(); |
| |
| // initialize .got |
| LDSection& got = fileFormat->getGOT(); |
| m_pGOT = new Mips64GOT(got); |
| |
| // initialize .got.plt |
| LDSection& gotplt = fileFormat->getGOTPLT(); |
| m_pGOTPLT = new MipsGOTPLT(gotplt); |
| |
| // initialize .plt |
| LDSection& plt = fileFormat->getPLT(); |
| m_pPLT = new MipsPLT(plt); |
| } |
| |
| size_t Mips64GNULDBackend::getRelEntrySize() { |
| return 16; |
| } |
| |
| size_t Mips64GNULDBackend::getRelaEntrySize() { |
| return 24; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| /// createMipsLDBackend - the help funtion to create corresponding MipsLDBackend |
| /// |
| static TargetLDBackend* createMipsLDBackend(const LinkerConfig& pConfig) { |
| const llvm::Triple& triple = pConfig.targets().triple(); |
| |
| if (triple.isOSDarwin()) { |
| assert(0 && "MachO linker is not supported yet"); |
| } |
| if (triple.isOSWindows()) { |
| assert(0 && "COFF linker is not supported yet"); |
| } |
| |
| llvm::Triple::ArchType arch = triple.getArch(); |
| |
| if (llvm::Triple::mips64el == arch) |
| return new Mips64GNULDBackend(pConfig, new MipsGNUInfo(triple)); |
| |
| assert(arch == llvm::Triple::mipsel); |
| return new Mips32GNULDBackend(pConfig, new MipsGNUInfo(triple)); |
| } |
| |
| } // namespace mcld |
| |
| //===----------------------------------------------------------------------===// |
| // Force static initialization. |
| //===----------------------------------------------------------------------===// |
| extern "C" void MCLDInitializeMipsLDBackend() { |
| mcld::TargetRegistry::RegisterTargetLDBackend(mcld::TheMipselTarget, |
| mcld::createMipsLDBackend); |
| mcld::TargetRegistry::RegisterTargetLDBackend(mcld::TheMips64elTarget, |
| mcld::createMipsLDBackend); |
| } |