diff options
Diffstat (limited to 'lld/lib/ReaderWriter/ELF/Writer.cpp')
| -rw-r--r-- | lld/lib/ReaderWriter/ELF/Writer.cpp | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/lld/lib/ReaderWriter/ELF/Writer.cpp b/lld/lib/ReaderWriter/ELF/Writer.cpp new file mode 100644 index 00000000000..2318cd8ebef --- /dev/null +++ b/lld/lib/ReaderWriter/ELF/Writer.cpp @@ -0,0 +1,341 @@ +//===- 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/Writer.h" + +#include "DefaultLayout.h" +#include "ExecutableAtoms.h" + +#include "lld/ReaderWriter/ELFTargetInfo.h" + +using namespace llvm; +using namespace llvm::object; +namespace lld { +namespace elf { +template<class ELFT> +class ExecutableWriter; + +//===----------------------------------------------------------------------===// +// ExecutableWriter Class +//===----------------------------------------------------------------------===// +template<class ELFT> +class ExecutableWriter : public ELFWriter { +public: + typedef Elf_Shdr_Impl<ELFT> Elf_Shdr; + typedef Elf_Sym_Impl<ELFT> Elf_Sym; + + ExecutableWriter(const ELFTargetInfo &ti); + +private: + // build the sections that need to be created + void buildChunks(const File &file); + virtual error_code writeFile(const File &File, StringRef path); + void buildAtomToAddressMap(); + void buildSymbolTable (); + void buildSectionHeaderTable(); + void assignSectionsWithNoSegments(); + void addAbsoluteUndefinedSymbols(const 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<const Atom*, uint64_t> AtomToAddress; + std::unique_ptr<KindHandler> _referenceKindHandler; + AtomToAddress _atomToAddressMap; + llvm::BumpPtrAllocator _chunkAllocate; + DefaultLayout<ELFT> *_layout; + Header<ELFT> *_Header; + ProgramHeader<ELFT> *_programHeader; + SymbolTable<ELFT> * _symtab; + StringTable<ELFT> *_strtab; + StringTable<ELFT> *_shstrtab; + SectionHeader<ELFT> *_shdrtab; + CRuntimeFile<ELFT> _runtimeFile; +}; + +//===----------------------------------------------------------------------===// +// ExecutableWriter +//===----------------------------------------------------------------------===// +template<class ELFT> +ExecutableWriter<ELFT>::ExecutableWriter(const ELFTargetInfo &ti) + : _targetInfo(ti) + , _referenceKindHandler(KindHandler::makeHandler( + ti.getTriple().getArch(), ti.isLittleEndian())) + , _runtimeFile(ti) { + _layout = new DefaultLayout<ELFT>(ti); +} + +template<class ELFT> +void ExecutableWriter<ELFT>::buildChunks(const 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<class ELFT> +void ExecutableWriter<ELFT>::buildSymbolTable () { + for (auto sec : _layout->sections()) + if (auto section = dyn_cast<Section<ELFT>>(sec)) + for (const auto &atom : section->atoms()) + _symtab->addSymbol(atom._atom, section->ordinal(), atom._virtualAddr); +} + +template<class ELFT> +void +ExecutableWriter<ELFT>::addAbsoluteUndefinedSymbols(const 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<class ELFT> +void ExecutableWriter<ELFT>::buildAtomToAddressMap () { + for (auto sec : _layout->sections()) + if (auto section = dyn_cast<Section<ELFT>>(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<class ELFT> +void ExecutableWriter<ELFT>::buildSectionHeaderTable() { + for (auto mergedSec : _layout->mergedSections()) { + if (mergedSec->kind() != Chunk<ELFT>::K_ELFSection) + continue; + if (mergedSec->hasSegment()) + _shdrtab->appendSection(mergedSec); + } +} + +template<class ELFT> +void ExecutableWriter<ELFT>::assignSectionsWithNoSegments() { + for (auto mergedSec : _layout->mergedSections()) { + if (mergedSec->kind() != Chunk<ELFT>::K_ELFSection) + continue; + if (!mergedSec->hasSegment()) + _shdrtab->appendSection(mergedSec); + } + _layout->assignOffsetsForMiscSections(); + for (auto sec : _layout->sections()) + if (auto section = dyn_cast<Section<ELFT>>(sec)) + if (!DefaultLayout<ELFT>::hasOutputSegment(section)) + _shdrtab->updateSection(section); +} + +/// \brief Add absolute symbols by default. These are linker added +/// absolute symbols +template<class ELFT> +void ExecutableWriter<ELFT>::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"); + _runtimeFile.addAbsoluteAtom("__rela_iplt_start"); + _runtimeFile.addAbsoluteAtom("__rela_iplt_end"); +} + +/// \brief Hook in lld to add CRuntime file +template<class ELFT> +void ExecutableWriter<ELFT>::addFiles(InputFiles &inputFiles) { + addDefaultAtoms(); + inputFiles.prependFile(_runtimeFile); +} + +/// Finalize the value of all the absolute symbols that we +/// created +template<class ELFT> +void ExecutableWriter<ELFT>::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 realIpltStartIter = _layout->findAbsoluteAtom("__rela_iplt_start"); + auto realIpltEndIter = _layout->findAbsoluteAtom("__rela_iplt_end"); + + auto startEnd = [&](typename DefaultLayout<ELFT>::AbsoluteAtomIterT start, + typename DefaultLayout<ELFT>::AbsoluteAtomIterT end, + StringRef sec) -> void { + auto section = _layout->findOutputSection(sec); + if (section) { + start->setValue(section->virtualAddr()); + end->setValue(section->virtualAddr() + section->memSize()); + } else { + start->setValue(0); + end->setValue(0); + } + }; + + startEnd(initArrayStartIter, initArrayEndIter, ".init_array"); + startEnd(realIpltStartIter, realIpltEndIter, ".rela.plt"); + + 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<class ELFT> +error_code +ExecutableWriter<ELFT>::writeFile(const 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<FileOutputBuffer> buffer; + error_code ec = FileOutputBuffer::create(path, + totalSize, buffer, + FileOutputBuffer::F_executable); + if (ec) + return ec; + + _Header->e_ident(ELF::EI_CLASS, _targetInfo.is64Bits() ? ELF::ELFCLASS64 + : ELF::ELFCLASS32); + _Header->e_ident(ELF::EI_DATA, _targetInfo.isLittleEndian() + ? ELF::ELFDATA2LSB : ELF::ELFDATA2MSB); + _Header->e_ident(ELF::EI_VERSION, 1); + _Header->e_ident(ELF::EI_OSABI, 0); + _Header->e_type(_targetInfo.getOutputType()); + _Header->e_machine(_targetInfo.getOutputMachine()); + _Header->e_version(1); + _Header->e_entry(0ULL); + _Header->e_phoff(_programHeader->fileOffset()); + _Header->e_shoff(_shdrtab->fileOffset()); + _Header->e_phentsize(_programHeader->entsize()); + _Header->e_phnum(_programHeader->numHeaders()); + _Header->e_shentsize(_shdrtab->entsize()); + _Header->e_shnum(_shdrtab->numHeaders()); + _Header->e_shstrndx(_shstrtab->ordinal()); + uint64_t virtualAddr = 0; + _layout->findAtomAddrByName(_targetInfo.getEntry(), virtualAddr); + _Header->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. + _Header->write(this, *buffer); + _programHeader->write(this, *buffer); + + for (auto section : _layout->sections()) + section->write(this, *buffer); + + return buffer->commit(); +} + +template<class ELFT> +void ExecutableWriter<ELFT>::createDefaultSections() { + _Header = new Header<ELFT>(_targetInfo); + _programHeader = new ProgramHeader<ELFT>(_targetInfo); + _layout->setHeader(_Header); + _layout->setProgramHeader(_programHeader); + + _symtab = new SymbolTable< + ELFT>(_targetInfo, ".symtab", DefaultLayout<ELFT>::ORDER_SYMBOL_TABLE); + _strtab = new StringTable< + ELFT>(_targetInfo, ".strtab", DefaultLayout<ELFT>::ORDER_STRING_TABLE); + _shstrtab = new StringTable<ELFT>( + _targetInfo, ".shstrtab", DefaultLayout<ELFT>::ORDER_SECTION_STRINGS); + _shdrtab = new SectionHeader< + ELFT>(_targetInfo, DefaultLayout<ELFT>::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<Writer> 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<Writer>(new + elf::ExecutableWriter<ELFType<support::little, 4, false>>(TI)); + else if (TI.is64Bits() && TI.isLittleEndian()) + return std::unique_ptr<Writer>(new + elf::ExecutableWriter<ELFType<support::little, 8, true>>(TI)); + else if (!TI.is64Bits() && !TI.isLittleEndian()) + return std::unique_ptr<Writer>(new + elf::ExecutableWriter<ELFType<support::big, 4, false>>(TI)); + else if (TI.is64Bits() && !TI.isLittleEndian()) + return std::unique_ptr<Writer>(new + elf::ExecutableWriter<ELFType<support::big, 8, true>>(TI)); + + llvm_unreachable("Invalid Options!"); +} +} // namespace lld |

