//===- lib/ReaderWriter/ELF/WriterELF.cpp ---------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lld/ReaderWriter/ELFTargetInfo.h" #include "DefaultELFLayout.h" #include "ExecutableAtoms.h" using namespace llvm; using namespace llvm::object; namespace lld { namespace elf { template class ELFExecutableWriter; //===----------------------------------------------------------------------===// // ELFExecutableWriter Class //===----------------------------------------------------------------------===// template class ELFExecutableWriter : public ELFWriter { public: typedef Elf_Shdr_Impl Elf_Shdr; typedef Elf_Sym_Impl Elf_Sym; ELFExecutableWriter(const ELFTargetInfo &ti); private: // build the sections that need to be created void buildChunks(const lld::File &file); virtual error_code writeFile(const lld::File &File, StringRef path); void buildAtomToAddressMap(); void buildSymbolTable (); void buildSectionHeaderTable(); void assignSectionsWithNoSegments(); void addAbsoluteUndefinedSymbols(const lld::File &File); void addDefaultAtoms(); void addFiles(InputFiles&); void finalizeDefaultAtomValues(); uint64_t addressOfAtom(const Atom *atom) { return _atomToAddressMap[atom]; } KindHandler *kindHandler() { return _referenceKindHandler.get(); } void createDefaultSections(); const ELFTargetInfo &_targetInfo; typedef llvm::DenseMap AtomToAddress; std::unique_ptr _referenceKindHandler; AtomToAddress _atomToAddressMap; llvm::BumpPtrAllocator _chunkAllocate; DefaultELFLayout *_layout; ELFHeader *_elfHeader; ELFProgramHeader *_programHeader; ELFSymbolTable * _symtab; ELFStringTable *_strtab; ELFStringTable *_shstrtab; ELFSectionHeader *_shdrtab; CRuntimeFileELF _runtimeFile; }; //===----------------------------------------------------------------------===// // ELFExecutableWriter //===----------------------------------------------------------------------===// template ELFExecutableWriter::ELFExecutableWriter(const ELFTargetInfo &ti) : _targetInfo(ti) , _referenceKindHandler(KindHandler::makeHandler( ti.getTriple().getArch(), ti.isLittleEndian())) , _runtimeFile(ti) { _layout = new DefaultELFLayout(ti); } template void ELFExecutableWriter::buildChunks(const lld::File &file){ for (const DefinedAtom *definedAtom : file.defined() ) { _layout->addAtom(definedAtom); } /// Add all the absolute atoms to the layout for (const AbsoluteAtom *absoluteAtom : file.absolute()) { _layout->addAtom(absoluteAtom); } } template void ELFExecutableWriter::buildSymbolTable () { for (auto sec : _layout->sections()) if (auto section = dyn_cast>(sec)) for (const auto &atom : section->atoms()) _symtab->addSymbol(atom._atom, section->ordinal(), atom._virtualAddr); } template void ELFExecutableWriter::addAbsoluteUndefinedSymbols(const lld::File &file) { // add all the absolute symbols that the layout contains to the output symbol // table for (auto &atom : _layout->absoluteAtoms()) _symtab->addSymbol(atom.absoluteAtom(), ELF::SHN_ABS, atom.value()); for (const UndefinedAtom *a : file.undefined()) _symtab->addSymbol(a, ELF::SHN_UNDEF); } template void ELFExecutableWriter::buildAtomToAddressMap () { for (auto sec : _layout->sections()) if (auto section = dyn_cast>(sec)) for (const auto &atom : section->atoms()) _atomToAddressMap[atom._atom] = atom._virtualAddr; // build the atomToAddressMap that contains absolute symbols too for (auto &atom : _layout->absoluteAtoms()) _atomToAddressMap[atom.absoluteAtom()] = atom.value(); } template void ELFExecutableWriter::buildSectionHeaderTable() { for (auto mergedSec : _layout->mergedSections()) { if (mergedSec->kind() != Chunk::K_ELFSection) continue; if (mergedSec->hasSegment()) _shdrtab->appendSection(mergedSec); } } template void ELFExecutableWriter::assignSectionsWithNoSegments() { for (auto mergedSec : _layout->mergedSections()) { if (mergedSec->kind() != Chunk::K_ELFSection) continue; if (!mergedSec->hasSegment()) _shdrtab->appendSection(mergedSec); } _layout->assignOffsetsForMiscSections(); for (auto sec : _layout->sections()) if (auto section = dyn_cast>(sec)) if (!DefaultELFLayout::hasOutputSegment(section)) _shdrtab->updateSection(section); } /// \brief Add absolute symbols by default. These are linker added /// absolute symbols template void ELFExecutableWriter::addDefaultAtoms() { _runtimeFile.addUndefinedAtom(_targetInfo.getEntry()); _runtimeFile.addAbsoluteAtom("__bss_start"); _runtimeFile.addAbsoluteAtom("__bss_end"); _runtimeFile.addAbsoluteAtom("_end"); _runtimeFile.addAbsoluteAtom("end"); _runtimeFile.addAbsoluteAtom("__init_array_start"); _runtimeFile.addAbsoluteAtom("__init_array_end"); } /// \brief Hook in lld to add CRuntime file template void ELFExecutableWriter::addFiles(InputFiles &inputFiles) { addDefaultAtoms(); inputFiles.prependFile(_runtimeFile); } /// Finalize the value of all the absolute symbols that we /// created template void ELFExecutableWriter::finalizeDefaultAtomValues() { auto bssStartAtomIter = _layout->findAbsoluteAtom("__bss_start"); auto bssEndAtomIter = _layout->findAbsoluteAtom("__bss_end"); auto underScoreEndAtomIter = _layout->findAbsoluteAtom("_end"); auto endAtomIter = _layout->findAbsoluteAtom("end"); auto initArrayStartIter = _layout->findAbsoluteAtom("__init_array_start"); auto initArrayEndIter = _layout->findAbsoluteAtom("__init_array_end"); auto section = _layout->findOutputSection(".init_array"); if (section) { initArrayStartIter->setValue(section->virtualAddr()); initArrayEndIter->setValue(section->virtualAddr() + section->memSize()); } else { initArrayStartIter->setValue(0); initArrayEndIter->setValue(0); } assert(!(bssStartAtomIter == _layout->absoluteAtoms().end() || bssEndAtomIter == _layout->absoluteAtoms().end() || underScoreEndAtomIter == _layout->absoluteAtoms().end() || endAtomIter == _layout->absoluteAtoms().end()) && "Unable to find the absolute atoms that have been added by lld"); auto phe = _programHeader->findProgramHeader( llvm::ELF::PT_LOAD, llvm::ELF::PF_W, llvm::ELF::PF_X); assert(!(phe == _programHeader->end()) && "Can't find a data segment in the program header!"); bssStartAtomIter->setValue((*phe)->p_vaddr+(*phe)->p_filesz); bssEndAtomIter->setValue((*phe)->p_vaddr+(*phe)->p_memsz); underScoreEndAtomIter->setValue((*phe)->p_vaddr+(*phe)->p_memsz); endAtomIter->setValue((*phe)->p_vaddr+(*phe)->p_memsz); } template error_code ELFExecutableWriter::writeFile(const lld::File &file, StringRef path) { buildChunks(file); // Create the default sections like the symbol table, string table, and the // section string table createDefaultSections(); // Set the Layout _layout->assignSectionsToSegments(); _layout->assignFileOffsets(); _layout->assignVirtualAddress(); // Finalize the default value of symbols that the linker adds finalizeDefaultAtomValues(); // Build the Atom To Address map for applying relocations buildAtomToAddressMap(); // Create symbol table and section string table buildSymbolTable(); // add other symbols addAbsoluteUndefinedSymbols(file); // Finalize the layout by calling the finalize() functions _layout->finalize(); // build Section Header table buildSectionHeaderTable(); // assign Offsets and virtual addresses // for sections with no segments assignSectionsWithNoSegments(); uint64_t totalSize = _shdrtab->fileOffset() + _shdrtab->fileSize(); OwningPtr buffer; error_code ec = FileOutputBuffer::create(path, totalSize, buffer, FileOutputBuffer::F_executable); if (ec) return ec; _elfHeader->e_ident(ELF::EI_CLASS, _targetInfo.is64Bits() ? ELF::ELFCLASS64 : ELF::ELFCLASS32); _elfHeader->e_ident(ELF::EI_DATA, _targetInfo.isLittleEndian() ? ELF::ELFDATA2LSB : ELF::ELFDATA2MSB); _elfHeader->e_ident(ELF::EI_VERSION, 1); _elfHeader->e_ident(ELF::EI_OSABI, 0); _elfHeader->e_type(_targetInfo.getOutputType()); _elfHeader->e_machine(_targetInfo.getOutputMachine()); _elfHeader->e_version(1); _elfHeader->e_entry(0ULL); _elfHeader->e_phoff(_programHeader->fileOffset()); _elfHeader->e_shoff(_shdrtab->fileOffset()); _elfHeader->e_phentsize(_programHeader->entsize()); _elfHeader->e_phnum(_programHeader->numHeaders()); _elfHeader->e_shentsize(_shdrtab->entsize()); _elfHeader->e_shnum(_shdrtab->numHeaders()); _elfHeader->e_shstrndx(_shstrtab->ordinal()); uint64_t virtualAddr = 0; _layout->findAtomAddrByName(_targetInfo.getEntry(), virtualAddr); _elfHeader->e_entry(virtualAddr); // HACK: We have to write out the header and program header here even though // they are a member of a segment because only sections are written in the // following loop. _elfHeader->write(this, buffer); _programHeader->write(this, buffer); for (auto section : _layout->sections()) section->write(this, buffer); return buffer->commit(); } template void ELFExecutableWriter::createDefaultSections() { _elfHeader = new ELFHeader(); _programHeader = new ELFProgramHeader(); _layout->setELFHeader(_elfHeader); _layout->setProgramHeader(_programHeader); _symtab = new ELFSymbolTable( ".symtab", DefaultELFLayout::ORDER_SYMBOL_TABLE); _strtab = new ELFStringTable( ".strtab", DefaultELFLayout::ORDER_STRING_TABLE); _shstrtab = new ELFStringTable( ".shstrtab", DefaultELFLayout::ORDER_SECTION_STRINGS); _shdrtab = new ELFSectionHeader( DefaultELFLayout::ORDER_SECTION_HEADERS); _layout->addSection(_symtab); _layout->addSection(_strtab); _layout->addSection(_shstrtab); _shdrtab->setStringSection(_shstrtab); _symtab->setStringSection(_strtab); _layout->addSection(_shdrtab); } } // namespace elf std::unique_ptr createWriterELF(const ELFTargetInfo &TI) { using llvm::object::ELFType; // Set the default layout to be the static executable layout // We would set the layout to a dynamic executable layout // if we came across any shared libraries in the process if (!TI.is64Bits() && TI.isLittleEndian()) return std::unique_ptr(new elf::ELFExecutableWriter>(TI)); else if (TI.is64Bits() && TI.isLittleEndian()) return std::unique_ptr(new elf::ELFExecutableWriter>(TI)); else if (!TI.is64Bits() && !TI.isLittleEndian()) return std::unique_ptr(new elf::ELFExecutableWriter>(TI)); else if (TI.is64Bits() && !TI.isLittleEndian()) return std::unique_ptr(new elf::ELFExecutableWriter>(TI)); llvm_unreachable("Invalid Options!"); } } // namespace lld