//===- X86LDBackend.cpp ---------------------------------------------------===// // // The MCLinker Project // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "X86.h" #include "X86ELFDynamic.h" #include "X86LDBackend.h" #include "X86RelocationFactory.h" #include #include #include #include #include #include #include #include #include using namespace mcld; X86GNULDBackend::X86GNULDBackend() : m_pRelocFactory(NULL), m_pGOT(NULL), m_pPLT(NULL), m_pRelDyn(NULL), m_pRelPLT(NULL), m_pDynamic(NULL) { } X86GNULDBackend::~X86GNULDBackend() { if (NULL != m_pRelocFactory) delete m_pRelocFactory; if (NULL != m_pGOT) delete m_pGOT; if (NULL != m_pPLT) delete m_pPLT; if (NULL !=m_pRelDyn) delete m_pRelDyn; if (NULL != m_pRelPLT) delete m_pRelPLT; if (NULL != m_pDynamic) delete m_pDynamic; } RelocationFactory* X86GNULDBackend::getRelocFactory() { assert(NULL != m_pRelocFactory); return m_pRelocFactory; } bool X86GNULDBackend::initRelocFactory(const MCLinker& pLinker) { if (NULL == m_pRelocFactory) { m_pRelocFactory = new X86RelocationFactory(1024, *this); m_pRelocFactory->setLayout(pLinker.getLayout()); } return true; } void X86GNULDBackend::doPreLayout(const Output& pOutput, const MCLDInfo& pInfo, MCLinker& pLinker) { // when building shared object, the .got section is needed if(pOutput.type() == Output::DynObj && (NULL == m_pGOT)) createX86GOT(pLinker, pOutput); } void X86GNULDBackend::doPostLayout(const Output& pOutput, const MCLDInfo& pInfo, MCLinker& pLinker) { // emit program headers if(pOutput.type() == Output::DynObj || pOutput.type() == Output::Exec) emitProgramHdrs(pLinker.getLDInfo().output()); } /// dynamic - the dynamic section of the target machine. /// Use co-variant return type to return its own dynamic section. X86ELFDynamic& X86GNULDBackend::dynamic() { if (NULL == m_pDynamic) m_pDynamic = new X86ELFDynamic(*this); return *m_pDynamic; } /// dynamic - the dynamic section of the target machine. /// Use co-variant return type to return its own dynamic section. const X86ELFDynamic& X86GNULDBackend::dynamic() const { assert( NULL != m_pDynamic); return *m_pDynamic; } void X86GNULDBackend::createX86GOT(MCLinker& pLinker, const Output& pOutput) { // get .got LDSection and create MCSectionData ELFFileFormat* file_format = getOutputFormat(pOutput); LDSection& got = file_format->getGOT(); m_pGOT = new X86GOT(got, pLinker.getOrCreateSectData(got)); // define symbol _GLOBAL_OFFSET_TABLE_ when .got create if( m_pGOTSymbol != NULL ) { pLinker.defineSymbol( "_GLOBAL_OFFSET_TABLE_", false, ResolveInfo::Object, ResolveInfo::Define, ResolveInfo::Local, 0x0, // size 0x0, // value pLinker.getLayout().getFragmentRef(*(m_pGOT->begin()), 0x0), ResolveInfo::Hidden); } else { m_pGOTSymbol = pLinker.defineSymbol( "_GLOBAL_OFFSET_TABLE_", false, ResolveInfo::Object, ResolveInfo::Define, ResolveInfo::Local, 0x0, // size 0x0, // value pLinker.getLayout().getFragmentRef(*(m_pGOT->begin()), 0x0), ResolveInfo::Hidden); } } void X86GNULDBackend::createX86PLTandRelPLT(MCLinker& pLinker, const Output& pOutput) { ELFFileFormat* file_format = getOutputFormat(pOutput); LDSection& plt = file_format->getPLT(); LDSection& relplt = file_format->getRelPlt(); // create MCSectionData and X86PLT m_pPLT = new X86PLT(plt, pLinker.getOrCreateSectData(plt), *m_pGOT, pOutput); // set info of .rel.plt to .plt relplt.setLink(&plt); // create MCSectionData and X86RelDynSection m_pRelPLT = new OutputRelocSection(relplt, pLinker.getOrCreateSectData(relplt), 8); } void X86GNULDBackend::createX86RelDyn(MCLinker& pLinker, const Output& pOutput) { // get .rel.dyn LDSection and create MCSectionData ELFFileFormat* file_format = getOutputFormat(pOutput); LDSection& reldyn = file_format->getRelDyn(); // create MCSectionData and X86RelDynSection m_pRelDyn = new OutputRelocSection(reldyn, pLinker.getOrCreateSectData(reldyn), 8); } ELFFileFormat* X86GNULDBackend::getOutputFormat(const Output& pOutput) const { switch (pOutput.type()) { case Output::DynObj: return getDynObjFileFormat(); case Output::Exec: return getExecFileFormat(); // FIXME: We do not support building .o now case Output::Object: default: llvm::report_fatal_error(llvm::Twine("Unsupported output file format: ") + llvm::Twine(pOutput.type())); return NULL; } } bool X86GNULDBackend::isSymbolNeedsPLT(const ResolveInfo& pSym, const MCLDInfo& pLDInfo, const Output& pOutput) const { return((Output::DynObj == pOutput.type()) &&(ResolveInfo::Function == pSym.type()) &&(pSym.isDyn() || pSym.isUndef() || isSymbolPreemptible(pSym, pLDInfo, pOutput)) ); } bool X86GNULDBackend::isSymbolNeedsDynRel(const ResolveInfo& pSym, const Output& pOutput, bool isAbsReloc) const { if(pSym.isUndef() && (pOutput.type()==Output::Exec)) return false; if(pSym.isAbsolute()) return false; if(pOutput.type()==Output::DynObj && isAbsReloc) return true; if(pSym.isDyn() || pSym.isUndef()) return true; return false; } bool X86GNULDBackend::isSymbolPreemptible(const ResolveInfo& pSym, const MCLDInfo& pLDInfo, const Output& pOutput) const { if(pSym.other() != ResolveInfo::Default) return false; if(pOutput.type() != Output::DynObj) return false; if(pLDInfo.options().Bsymbolic()) return false; return true; } void X86GNULDBackend::updateAddend(Relocation& pReloc, const LDSymbol& pInputSym, const Layout& pLayout) const { // Update value keep in addend if we meet a section symbol if(pReloc.symInfo()->type() == ResolveInfo::Section) { pReloc.setAddend(pLayout.getOutputOffset( *pInputSym.fragRef()) + pReloc.addend()); } } void X86GNULDBackend::scanLocalReloc(Relocation& pReloc, const LDSymbol& pInputSym, MCLinker& pLinker, const MCLDInfo& pLDInfo, const Output& pOutput) { // rsym - The relocation target symbol ResolveInfo* rsym = pReloc.symInfo(); updateAddend(pReloc, pInputSym, pLinker.getLayout()); switch(pReloc.type()){ case llvm::ELF::R_386_32: // If buiding PIC object (shared library or PIC executable), // a dynamic relocations with RELATIVE type to this location is needed. // Reserve an entry in .rel.dyn if(Output::DynObj == pOutput.type()) { // create .rel.dyn section if not exist if(NULL == m_pRelDyn) createX86RelDyn(pLinker, pOutput); m_pRelDyn->reserveEntry(*m_pRelocFactory); // set Rel bit rsym->setReserved(rsym->reserved() | ReserveRel); } return; case llvm::ELF::R_386_GOTOFF: case llvm::ELF::R_386_GOTPC: // A GOT section is needed if(NULL == m_pGOT) createX86GOT(pLinker, pOutput); return; case llvm::ELF::R_386_PC32: return; default: llvm::report_fatal_error(llvm::Twine("unexpected reloc ") + llvm::Twine((int) pReloc.type()) + llvm::Twine(" in object file")); break; } // end switch } void X86GNULDBackend::scanGlobalReloc(Relocation& pReloc, const LDSymbol& pInputSym, MCLinker& pLinker, const MCLDInfo& pLDInfo, const Output& pOutput) { // rsym - The relocation target symbol ResolveInfo* rsym = pReloc.symInfo(); switch(pReloc.type()) { case llvm::ELF::R_386_32: // Absolute relocation type, symbol may needs PLT entry or // dynamic relocation entry if(isSymbolNeedsPLT(*rsym, pLDInfo, pOutput)) { // create plt for this symbol if it does not have one if(!(rsym->reserved() & ReservePLT)){ // Create .got section if it dosen't exist if(NULL == m_pGOT) createX86GOT(pLinker, pOutput); // create .plt and .rel.plt if not exist if(NULL == m_pPLT) createX86PLTandRelPLT(pLinker, pOutput); // Symbol needs PLT entry, we need to reserve a PLT entry // and the corresponding GOT and dynamic relocation entry // in .got and .rel.plt. (GOT entry will be reserved simultaneously // when calling X86PLT->reserveEntry()) m_pPLT->reserveEntry(); m_pRelPLT->reserveEntry(*m_pRelocFactory); // set PLT bit rsym->setReserved(rsym->reserved() | ReservePLT); } } if(isSymbolNeedsDynRel(*rsym, pOutput, true)) { // symbol needs dynamic relocation entry, reserve an entry in .rel.dyn // create .rel.dyn section if not exist if(NULL == m_pRelDyn) createX86RelDyn(pLinker, pOutput); m_pRelDyn->reserveEntry(*m_pRelocFactory); // set Rel bit rsym->setReserved(rsym->reserved() | ReserveRel); } return; case llvm::ELF::R_386_GOTOFF: case llvm::ELF::R_386_GOTPC: { // A GOT section is needed if(NULL == m_pGOT) createX86GOT(pLinker, pOutput); return; } case llvm::ELF::R_386_PLT32: // A PLT entry is needed when building shared library // return if we already create plt for this symbol if(rsym->reserved() & ReservePLT) return; // if symbol is defined in the ouput file and it's not // preemptible, no need plt if(rsym->isDefine() && !rsym->isDyn() && !isSymbolPreemptible(*rsym, pLDInfo, pOutput)) { return; } // Create .got section if it dosen't exist if(NULL == m_pGOT) createX86GOT(pLinker, pOutput); // create .plt and .rel.plt if not exist if(NULL == m_pPLT) createX86PLTandRelPLT(pLinker, pOutput); // Symbol needs PLT entry, we need to reserve a PLT entry // and the corresponding GOT and dynamic relocation entry // in .got and .rel.plt. (GOT entry will be reserved simultaneously // when calling X86PLT->reserveEntry()) m_pPLT->reserveEntry(); m_pRelPLT->reserveEntry(*m_pRelocFactory); // set PLT bit rsym->setReserved(rsym->reserved() | ReservePLT); return; case llvm::ELF::R_386_GOT32: // Symbol needs GOT entry, reserve entry in .got // return if we already create GOT for this symbol if(rsym->reserved() & (ReserveGOT | GOTRel)) return; if(NULL == m_pGOT) createX86GOT(pLinker, pOutput); m_pGOT->reserveEntry(); // If building shared object or the symbol is undefined, a dynamic // relocation is needed to relocate this GOT entry. Reserve an // entry in .rel.dyn if(Output::DynObj == pOutput.type() || rsym->isUndef() || rsym->isDyn()) { // create .rel.dyn section if not exist if(NULL == m_pRelDyn) createX86RelDyn(pLinker, pOutput); m_pRelDyn->reserveEntry(*m_pRelocFactory); // set GOTRel bit rsym->setReserved(rsym->reserved() | GOTRel); return; } // set GOT bit rsym->setReserved(rsym->reserved() | ReserveGOT); return; case llvm::ELF::R_386_PC32: // We allow R_386_PC32 only if it isn't preemptible. Otherwise // we will generate writable text section in output. if (!isSymbolPreemptible(*rsym, pLDInfo, pOutput)) return; default: { llvm::report_fatal_error(llvm::Twine("Unexpected reloc ") + llvm::Twine((int) pReloc.type()) + llvm::Twine(" in object file")); break; } } // end switch } void X86GNULDBackend::scanRelocation(Relocation& pReloc, const LDSymbol& pInputSym, MCLinker& pLinker, const MCLDInfo& pLDInfo, const Output& pOutput) { // rsym - The relocation target symbol ResolveInfo* rsym = pReloc.symInfo(); assert(NULL != rsym && "ResolveInfo of relocation not set while scanRelocation"); // Scan relocation type to determine if an GOT/PLT/Dynamic Relocation // entries should be created. // FIXME: Below judgements concern only .so is generated as output // FIXME: Below judgements concren nothing about TLS related relocation // A refernece to symbol _GLOBAL_OFFSET_TABLE_ implies that a .got section // is needed if(NULL == m_pGOT && NULL != m_pGOTSymbol) { if(rsym == m_pGOTSymbol->resolveInfo()) { createX86GOT(pLinker, pOutput); } } // rsym is local if(rsym->isLocal()) scanLocalReloc(pReloc, pInputSym, pLinker, pLDInfo, pOutput); // rsym is external else scanGlobalReloc(pReloc, pInputSym ,pLinker, pLDInfo, pOutput); } uint64_t X86GNULDBackend::emitSectionData(const Output& pOutput, const LDSection& pSection, const MCLDInfo& pInfo, MemoryRegion& pRegion) const { assert(pRegion.size() && "Size of MemoryRegion is zero!"); ELFFileFormat* FileFormat = getOutputFormat(pOutput); assert(FileFormat && "ELFFileFormat is NULL in X86GNULDBackend::emitSectionData!"); unsigned int EntrySize = 0; uint64_t RegionSize = 0; if (&pSection == &(FileFormat->getPLT())) { assert(m_pPLT && "emitSectionData failed, m_pPLT is NULL!"); unsigned char* buffer = pRegion.getBuffer(); m_pPLT->applyPLT0(); m_pPLT->applyPLT1(); X86PLT::iterator it = m_pPLT->begin(); unsigned int plt0_size = llvm::cast((*it)).getEntrySize(); memcpy(buffer, llvm::cast((*it)).getContent(), plt0_size); RegionSize += plt0_size; ++it; X86PLT1* plt1 = 0; X86PLT::iterator ie = m_pPLT->end(); while (it != ie) { plt1 = &(llvm::cast(*it)); EntrySize = plt1->getEntrySize(); memcpy(buffer + RegionSize, plt1->getContent(), EntrySize); RegionSize += EntrySize; ++it; } } else if (&pSection == &(FileFormat->getGOT())) { assert(m_pGOT && "emitSectionData failed, m_pGOT is NULL!"); m_pGOT->applyGOT0(FileFormat->getDynamic().addr()); uint32_t* buffer = reinterpret_cast(pRegion.getBuffer()); GOTEntry* got = 0; EntrySize = m_pGOT->getEntrySize(); for (X86GOT::iterator it = m_pGOT->begin(), ie = m_pGOT->end(); it != ie; ++it, ++buffer) { got = &(llvm::cast((*it))); *buffer = static_cast(got->getContent()); RegionSize += EntrySize; } } else llvm::report_fatal_error("unsupported section name " + pSection.name() + " !"); return RegionSize; } uint32_t X86GNULDBackend::machine() const { return llvm::ELF::EM_386; } X86GOT& X86GNULDBackend::getGOT() { assert(NULL != m_pGOT); return *m_pGOT; } const X86GOT& X86GNULDBackend::getGOT() const { assert(NULL != m_pGOT); return *m_pGOT; } X86PLT& X86GNULDBackend::getPLT() { assert(NULL != m_pPLT && "PLT section not exist"); return *m_pPLT; } const X86PLT& X86GNULDBackend::getPLT() const { assert(NULL != m_pPLT && "PLT section not exist"); return *m_pPLT; } OutputRelocSection& X86GNULDBackend::getRelDyn() { assert(NULL != m_pRelDyn && ".rel.dyn section not exist"); return *m_pRelDyn; } const OutputRelocSection& X86GNULDBackend::getRelDyn() const { assert(NULL != m_pRelDyn && ".rel.dyn section not exist"); return *m_pRelDyn; } OutputRelocSection& X86GNULDBackend::getRelPLT() { assert(NULL != m_pRelPLT && ".rel.plt section not exist"); return *m_pRelPLT; } const OutputRelocSection& X86GNULDBackend::getRelPLT() const { assert(NULL != m_pRelPLT && ".rel.plt section not exist"); return *m_pRelPLT; } unsigned int X86GNULDBackend::getTargetSectionOrder(const Output& pOutput, const LDSection& pSectHdr) const { ELFFileFormat* file_format = getOutputFormat(pOutput); // FIXME: if command line option, "-z now", is given, we can let the order of // .got and .got.plt be the same as RELRO sections if (&pSectHdr == &file_format->getGOT()) return SHO_RELRO_LAST; if (&pSectHdr == &file_format->getGOTPLT()) return SHO_NON_RELRO_FIRST; if (&pSectHdr == &file_format->getPLT()) return SHO_PLT; return SHO_UNDEFINED; } unsigned int X86GNULDBackend::bitclass() const { return 32; } bool X86GNULDBackend::initTargetSectionMap(SectionMap& pSectionMap) { return true; } void X86GNULDBackend::initTargetSections(MCLinker& pLinker) { } void X86GNULDBackend::initTargetSymbols(MCLinker& pLinker) { // Define the symbol _GLOBAL_OFFSET_TABLE_ if there is a symbol with the // same name in input m_pGOTSymbol = pLinker.defineSymbol( "_GLOBAL_OFFSET_TABLE_", false, ResolveInfo::Object, ResolveInfo::Define, ResolveInfo::Local, 0x0, // size 0x0, // value NULL, // FragRef ResolveInfo::Hidden); } /// finalizeSymbol - finalize the symbol value /// If the symbol's reserved field is not zero, MCLinker will call back this /// function to ask the final value of the symbol bool X86GNULDBackend::finalizeSymbol(LDSymbol& pSymbol) const { return false; } /// allocateCommonSymbols - allocate common symbols in the corresponding /// sections. /// @refer Google gold linker: common.cc: 214 bool X86GNULDBackend::allocateCommonSymbols(const MCLDInfo& pInfo, MCLinker& pLinker) const { // SymbolCategory contains all symbols that must emit to the output files. // We are not like Google gold linker, we don't remember symbols before symbol // resolution. All symbols in SymbolCategory are already resolved. Therefore, we // don't need to care about some symbols may be changed its category due to symbol // resolution. SymbolCategory& symbol_list = pLinker.getOutputSymbols(); if (symbol_list.emptyCommons() && symbol_list.emptyLocals()) return true; // addralign := max value of all common symbols uint64_t addralign = 0x0; // Due to the visibility, some common symbols may be forcefully local. SymbolCategory::iterator com_sym, com_end = symbol_list.localEnd(); for (com_sym = symbol_list.localBegin(); com_sym != com_end; ++com_sym) { if (ResolveInfo::Common == (*com_sym)->desc()) { if ((*com_sym)->value() > addralign) addralign = (*com_sym)->value(); } } // global common symbols. com_end = symbol_list.commonEnd(); for (com_sym = symbol_list.commonBegin(); com_sym != com_end; ++com_sym) { if ((*com_sym)->value() > addralign) addralign = (*com_sym)->value(); } // FIXME: If the order of common symbols is defined, then sort common symbols // com_sym = symbol_list.commonBegin(); // std::sort(com_sym, com_end, some kind of order); // get or create corresponding BSS LDSection LDSection* bss_sect_hdr = NULL; if (ResolveInfo::ThreadLocal == (*com_sym)->type()) { bss_sect_hdr = &pLinker.getOrCreateOutputSectHdr( ".tbss", LDFileFormat::BSS, llvm::ELF::SHT_NOBITS, llvm::ELF::SHF_WRITE | llvm::ELF::SHF_ALLOC); } else { bss_sect_hdr = &pLinker.getOrCreateOutputSectHdr(".bss", LDFileFormat::BSS, llvm::ELF::SHT_NOBITS, llvm::ELF::SHF_WRITE | llvm::ELF::SHF_ALLOC); } // get or create corresponding BSS MCSectionData assert(NULL != bss_sect_hdr); llvm::MCSectionData& bss_section = pLinker.getOrCreateSectData(*bss_sect_hdr); // allocate all common symbols uint64_t offset = bss_sect_hdr->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); llvm::MCFragment* frag = new llvm::MCFillFragment(0x0, 1, (*com_sym)->size()); (*com_sym)->setFragmentRef(new MCFragmentRef(*frag, 0)); uint64_t size = pLinker.getLayout().appendFragment(*frag, bss_section, (*com_sym)->value()); offset += size; } } // 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); llvm::MCFragment* frag = new llvm::MCFillFragment(0x0, 1, (*com_sym)->size()); (*com_sym)->setFragmentRef(new MCFragmentRef(*frag, 0)); uint64_t size = pLinker.getLayout().appendFragment(*frag, bss_section, (*com_sym)->value()); offset += size; } bss_sect_hdr->setSize(offset); symbol_list.changeCommonsToGlobal(); return true; } namespace mcld { //===----------------------------------------------------------------------===// /// createX86LDBackend - the help funtion to create corresponding X86LDBackend /// TargetLDBackend* createX86LDBackend(const llvm::Target& pTarget, const std::string& pTriple) { Triple theTriple(pTriple); if (theTriple.isOSDarwin()) { assert(0 && "MachO linker is not supported yet"); /** return new X86MachOLDBackend(createX86MachOArchiveReader, createX86MachOObjectReader, createX86MachOObjectWriter); **/ } if (theTriple.isOSWindows()) { assert(0 && "COFF linker is not supported yet"); /** return new X86COFFLDBackend(createX86COFFArchiveReader, createX86COFFObjectReader, createX86COFFObjectWriter); **/ } return new X86GNULDBackend(); } } // namespace of mcld //============================= // Force static initialization. extern "C" void LLVMInitializeX86LDBackend() { // Register the linker backend mcld::TargetRegistry::RegisterTargetLDBackend(TheX86Target, createX86LDBackend); }