//===- 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 #include #include #include #include #include #include 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( "_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( "_PROCEDURE_LINKAGE_TABLE_", ResolveInfo::Object, ResolveInfo::Define, ResolveInfo::Local, 0x0, // size 0x0, // value FragmentRef::Null(), // FragRef ResolveInfo::Hidden); m_pGpDispSymbol = pBuilder.AddSymbol( "_gp_disp", ResolveInfo::Section, ResolveInfo::Define, ResolveInfo::Absolute, 0x0, // size 0x0, // value FragmentRef::Null(), // FragRef ResolveInfo::Default); pBuilder.AddSymbol( "_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(*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(*frag_iter); const char* start = region_frag.getRegion().begin(); memcpy(out_offset, start, size); break; } case Fragment::Alignment: { const AlignFragment& align_frag = llvm::cast(*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() << "mclinker@googlegroups.com"; 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-speciļ¬c 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(ELF_hdr)->e_flags : reinterpret_cast(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(region.begin()); const llvm::ELF::Elf_Options* opte = reinterpret_cast(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(opt + 1); m_GP0Map[&pInput] = reg->ri_gp_value; } else { const llvm::ELF::Elf64_RegInfo* reg = reinterpret_cast(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( "_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( "_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( "_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( "_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(reloc))) isRelaxed = true; } } } // find the first fragment w/ invalid offset due to stub insertion std::vector 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); }