//===- lib/ReaderWriter/ELF/ReaderELF.cpp ---------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief Defines the ELF Reader and all helper sub classes to consume an ELF /// file and produces atoms out of it. /// //===----------------------------------------------------------------------===// #include "lld/ReaderWriter/Reader.h" #include "lld/Core/File.h" #include "lld/Core/Reference.h" #include "lld/ReaderWriter/ELFTargetInfo.h" #include "lld/ReaderWriter/ReaderArchive.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/ELF.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/Path.h" #include "llvm/Support/ELF.h" #include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/Memory.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/system_error.h" #include "AtomsELF.h" #include #include using namespace lld; using llvm::support::endianness; using namespace llvm::object; namespace { /// \brief Read a binary, find out based on the symbol table contents what kind /// of symbol it is and create corresponding atoms for it template class FileELF : public File { typedef Elf_Sym_Impl Elf_Sym; typedef Elf_Shdr_Impl Elf_Shdr; typedef Elf_Rel_Impl Elf_Rel; typedef Elf_Rel_Impl Elf_Rela; public: FileELF(std::unique_ptr MB, llvm::error_code &EC) : File(MB->getBufferIdentifier()) { llvm::OwningPtr binaryFile; EC = createBinary(MB.release(), binaryFile); if (EC) return; // Point Obj to correct class and bitwidth ELF object _objFile.reset(llvm::dyn_cast>(binaryFile.get())); if (!_objFile) { EC = make_error_code(object_error::invalid_file_type); return; } binaryFile.take(); std::map> sectionSymbols; // Handle: SHT_REL and SHT_RELA sections: // Increment over the sections, when REL/RELA section types are found add // the contents to the RelocationReferences map. section_iterator sit(_objFile->begin_sections()); section_iterator sie(_objFile->end_sections()); for (; sit != sie; sit.increment(EC)) { if (EC) return; const Elf_Shdr *section = _objFile->getElfSection(sit); if (section->sh_type == llvm::ELF::SHT_RELA) { llvm::StringRef sectionName; if ((EC = _objFile->getSectionName(section, sectionName))) return; // Get rid of the leading .rela so Atoms can use their own section // name to find the relocs. sectionName = sectionName.drop_front(5); auto rai(_objFile->beginELFRela(section)); auto rae(_objFile->endELFRela(section)); auto &Ref = _relocationAddendRefences[sectionName]; for (; rai != rae; ++rai) { Ref.push_back(&*rai); } } if (section->sh_type == llvm::ELF::SHT_REL) { llvm::StringRef sectionName; if ((EC = _objFile->getSectionName(section, sectionName))) return; // Get rid of the leading .rel so Atoms can use their own section // name to find the relocs. sectionName = sectionName.drop_front(4); auto ri(_objFile->beginELFRel(section)); auto re(_objFile->endELFRel(section)); auto &Ref = _relocationReferences[sectionName]; for (; ri != re; ++ri) { Ref.push_back(&*ri); } } } // Increment over all the symbols collecting atoms and symbol names for // later use. symbol_iterator it(_objFile->begin_symbols()); symbol_iterator ie(_objFile->end_symbols()); for (; it != ie; it.increment(EC)) { if (EC) return; if ((EC = it->getSection(sit))) return; const Elf_Shdr *section = _objFile->getElfSection(sit); const Elf_Sym *symbol = _objFile->getElfSymbol(it); llvm::StringRef symbolName; if ((EC = _objFile->getSymbolName(section, symbol, symbolName))) return; if (symbol->st_shndx == llvm::ELF::SHN_ABS) { // Create an absolute atom. auto *newAtom = new (_readerStorage) ELFAbsoluteAtom(*this, symbolName, symbol, symbol->st_value); _absoluteAtoms._atoms.push_back(newAtom); _symbolToAtomMapping.insert(std::make_pair(symbol, newAtom)); } else if (symbol->st_shndx == llvm::ELF::SHN_UNDEF) { // Create an undefined atom. auto *newAtom = new (_readerStorage) ELFUndefinedAtom(*this, symbolName, symbol); _undefinedAtoms._atoms.push_back(newAtom); _symbolToAtomMapping.insert(std::make_pair(symbol, newAtom)); } else { // This is actually a defined symbol. Add it to its section's list of // symbols. if (symbol->getType() == llvm::ELF::STT_NOTYPE || symbol->getType() == llvm::ELF::STT_OBJECT || symbol->getType() == llvm::ELF::STT_FUNC || symbol->getType() == llvm::ELF::STT_GNU_IFUNC || symbol->getType() == llvm::ELF::STT_SECTION || symbol->getType() == llvm::ELF::STT_FILE || symbol->getType() == llvm::ELF::STT_TLS || symbol->getType() == llvm::ELF::STT_COMMON || symbol->st_shndx == llvm::ELF::SHN_COMMON) { sectionSymbols[section].push_back(symbol); } else { llvm::errs() << "Unable to create atom for: " << symbolName << "\n"; EC = object_error::parse_failed; return; } } } for (auto &i : sectionSymbols) { auto &symbols = i.second; // Sort symbols by position. std::stable_sort(symbols.begin(), symbols.end(), [](const Elf_Sym *A, const Elf_Sym *B) { return A->st_value < B->st_value; }); StringRef sectionContents; if ((EC = _objFile->getSectionContents(i.first, sectionContents))) return; llvm::StringRef sectionName; if ((EC = _objFile->getSectionName(i.first, sectionName))) return; // i.first is the section the symbol lives in for (auto si = symbols.begin(), se = symbols.end(); si != se; ++si) { llvm::StringRef symbolName; if ((EC = _objFile->getSymbolName(i.first, *si, symbolName))) return; bool isCommon = (*si)->getType() == llvm::ELF::STT_COMMON || (*si)->st_shndx == llvm::ELF::SHN_COMMON; // Get the symbol's content: uint64_t contentSize; if (si + 1 == se) { // if this is the last symbol, take up the remaining data. contentSize = isCommon ? 0 : i.first->sh_size - (*si)->st_value; } else { contentSize = isCommon ? 0 : (*(si + 1))->st_value - (*si)->st_value; } // Don't allocate content to a weak symbol, as they may be merged away. // Create an anonymous atom to hold the data. ELFDefinedAtom *anonAtom = nullptr; if ((*si)->getBinding() == llvm::ELF::STB_WEAK && contentSize != 0) { // Create a new non-weak ELF symbol. auto sym = new (_readerStorage) Elf_Sym; *sym = **si; sym->setBinding(llvm::ELF::STB_GLOBAL); anonAtom = createDefinedAtomAndAssignRelocations( "", sectionName, sym, i.first, ArrayRef((uint8_t *)sectionContents.data() + (*si)->st_value, contentSize)); contentSize = 0; } ArrayRef symbolData = ArrayRef( (uint8_t *)sectionContents.data() + (*si)->st_value, contentSize); auto newAtom = createDefinedAtomAndAssignRelocations( symbolName, sectionName, *si, i.first, symbolData); _definedAtoms._atoms.push_back(newAtom); _symbolToAtomMapping.insert(std::make_pair((*si), newAtom)); if (anonAtom) _definedAtoms._atoms.push_back(anonAtom); } } // All the Atoms and References are created. Now update each Reference's // target with the Atom pointer it refers to. for (auto &ri : _references) { const Elf_Sym *Symbol = _objFile->getElfSymbol(ri->targetSymbolIndex()); ri->setTarget(findAtom(Symbol)); } } virtual const atom_collection &defined() const { return _definedAtoms; } virtual const atom_collection &undefined() const { return _undefinedAtoms; } virtual const atom_collection &sharedLibrary() const { return _sharedLibraryAtoms; } virtual const atom_collection &absolute() const { return _absoluteAtoms; } Atom *findAtom(const Elf_Sym *symbol) { return _symbolToAtomMapping.lookup(symbol); } private: ELFDefinedAtom *createDefinedAtomAndAssignRelocations( StringRef symbolName, StringRef sectionName, const Elf_Sym *symbol, const Elf_Shdr *section, ArrayRef content) { unsigned int referenceStart = _references.size(); // Only relocations that are inside the domain of the atom are added. // Add Rela (those with r_addend) references: for (auto &rai : _relocationAddendRefences[sectionName]) { if (!((rai->r_offset >= symbol->st_value) && (rai->r_offset < symbol->st_value + content.size()))) continue; auto *ERef = new (_readerStorage) ELFReference(rai, rai->r_offset - symbol->st_value, nullptr); _references.push_back(ERef); } // Add Rel references. for (auto &ri : _relocationReferences[sectionName]) { if ((ri->r_offset >= symbol->st_value) && (ri->r_offset < symbol->st_value + content.size())) { auto *ERef = new (_readerStorage) ELFReference(ri, ri->r_offset - symbol->st_value, nullptr); _references.push_back(ERef); } } // Create the DefinedAtom and add it to the list of DefinedAtoms. return new (_readerStorage) ELFDefinedAtom(*this, symbolName, sectionName, symbol, section, content, referenceStart, _references.size(), _references); } std::unique_ptr> _objFile; atom_collection_vector _definedAtoms; atom_collection_vector _undefinedAtoms; atom_collection_vector _sharedLibraryAtoms; atom_collection_vector _absoluteAtoms; /// \brief _relocationAddendRefences and _relocationReferences contain the /// list of relocations references. In ELF, if a section named, ".text" has /// relocations will also have a section named ".rel.text" or ".rela.text" /// which will hold the entries. -- .rel or .rela is prepended to create /// the SHT_REL(A) section name. std::map> _relocationAddendRefences; std::map> _relocationReferences; std::vector *> _references; llvm::DenseMap _symbolToAtomMapping; llvm::BumpPtrAllocator _readerStorage; }; /// \brief A reader object that will instantiate correct FileELF by examining the /// memory buffer for ELF class and bit width class ReaderELF : public Reader { public: ReaderELF(const TargetInfo & ti, std::function read) : Reader(ti), _readerArchive(ti, read) {} error_code parseFile(std::unique_ptr mb, std::vector> &result) { using llvm::object::ELFType; llvm::sys::LLVMFileType fileType = llvm::sys::IdentifyFileType(mb->getBufferStart(), static_cast(mb->getBufferSize())); std::size_t MaxAlignment = 1ULL << llvm::CountTrailingZeros_64(uintptr_t(mb->getBufferStart())); llvm::error_code ec; switch (fileType) { case llvm::sys::ELF_Relocatable_FileType: { std::pair Ident = getElfArchType(&*mb); std::unique_ptr f; // Instantiate the correct FileELF template instance based on the Ident // pair. Once the File is created we push the file to the vector of files // already created during parser's life. if (Ident.first == llvm::ELF::ELFCLASS32 && Ident.second == llvm::ELF::ELFDATA2LSB) { if (MaxAlignment >= 4) f.reset(new FileELF>( std::move(mb), ec)); else if (MaxAlignment >= 2) f.reset(new FileELF>( std::move(mb), ec)); else llvm_unreachable("Invalid alignment for ELF file!"); } else if (Ident.first == llvm::ELF::ELFCLASS32 && Ident.second == llvm::ELF::ELFDATA2MSB) { if (MaxAlignment >= 4) f.reset(new FileELF>( std::move(mb), ec)); else if (MaxAlignment >= 2) f.reset(new FileELF>( std::move(mb), ec)); else llvm_unreachable("Invalid alignment for ELF file!"); } else if (Ident.first == llvm::ELF::ELFCLASS64 && Ident.second == llvm::ELF::ELFDATA2MSB) { if (MaxAlignment >= 8) f.reset(new FileELF>( std::move(mb), ec)); else if (MaxAlignment >= 2) f.reset(new FileELF>( std::move(mb), ec)); else llvm_unreachable("Invalid alignment for ELF file!"); } else if (Ident.first == llvm::ELF::ELFCLASS64 && Ident.second == llvm::ELF::ELFDATA2LSB) { if (MaxAlignment >= 8) f.reset(new FileELF>( std::move(mb), ec)); else if (MaxAlignment >= 2) f.reset(new FileELF>( std::move(mb), ec)); else llvm_unreachable("Invalid alignment for ELF file!"); } if (!ec) result.push_back(std::move(f)); break; } case llvm::sys::Archive_FileType: ec = _readerArchive.parseFile(std::move(mb), result); break; default: llvm_unreachable("not supported format"); break; } if (ec) return ec; return error_code::success(); } private: ReaderArchive _readerArchive; }; } // end anon namespace. namespace lld { std::unique_ptr createReaderELF(const TargetInfo & ti, std::function read) { return std::unique_ptr(new ReaderELF(ti, std::move(read))); } } // end namespace lld