Index: include/lld/Core/File.h =================================================================== --- include/lld/Core/File.h +++ include/lld/Core/File.h @@ -16,6 +16,7 @@ #include "lld/Core/SharedLibraryAtom.h" #include "lld/Core/UndefinedAtom.h" #include "lld/Core/range.h" +#include "llvm/ADT/Optional.h" #include "llvm/Support/ErrorHandling.h" #include #include @@ -153,9 +154,19 @@ /// all AbsoluteAtoms in this File. virtual const atom_collection &absolute() const = 0; + virtual std::error_code doParse() { return std::error_code(); } + void setLastError(std::error_code err) { _lastError = err; } + + std::error_code parse() { + if (!_lastError.hasValue()) + _lastError = doParse(); + return _lastError.getValue(); + } + protected: /// \brief only subclasses of File can be instantiated - File(StringRef p, Kind kind) : _path(p), _kind(kind), _ordinal(UINT64_MAX) {} + File(StringRef p, Kind kind) + : _path(p), _kind(kind), _ordinal(UINT64_MAX) {} /// \brief This is a convenience class for File subclasses which manage their /// atoms as a simple std::vector<>. @@ -211,6 +222,7 @@ static atom_collection_empty _noUndefinedAtoms; static atom_collection_empty _noSharedLibraryAtoms; static atom_collection_empty _noAbsoluteAtoms; + llvm::Optional _lastError; mutable llvm::BumpPtrAllocator _allocator; private: Index: include/lld/ReaderWriter/MachOLinkingContext.h =================================================================== --- include/lld/ReaderWriter/MachOLinkingContext.h +++ include/lld/ReaderWriter/MachOLinkingContext.h @@ -26,6 +26,7 @@ namespace mach_o { class ArchHandler; class MachODylibFile; +class MachOFile; } class MachOLinkingContext : public LinkingContext { Index: lib/ReaderWriter/ELF/AArch64/AArch64ELFFile.h =================================================================== --- lib/ReaderWriter/ELF/AArch64/AArch64ELFFile.h +++ lib/ReaderWriter/ELF/AArch64/AArch64ELFFile.h @@ -24,11 +24,8 @@ static ErrorOr> create(std::unique_ptr mb, bool atomizeStrings) { - std::unique_ptr> file( + return std::unique_ptr>( new AArch64ELFFile(std::move(mb), atomizeStrings)); - if (std::error_code ec = file->parse()) - return ec; - return std::move(file); } }; Index: lib/ReaderWriter/ELF/DynamicFile.h =================================================================== --- lib/ReaderWriter/ELF/DynamicFile.h +++ lib/ReaderWriter/ELF/DynamicFile.h @@ -55,8 +55,51 @@ *this, name, _soname, sym->second._symbol); } + std::error_code doParse() override { + std::error_code ec; + _objFile.reset( + new llvm::object::ELFFile(_mb.release()->getBuffer(), ec)); + if (ec) + return ec; + + llvm::object::ELFFile &obj = *_objFile; + + _soname = obj.getLoadName(); + if (_soname.empty()) + _soname = llvm::sys::path::filename(path()); + + // Create a map from names to dynamic symbol table entries. + // TODO: This should use the object file's build in hash table instead if + // it exists. + for (auto i = obj.begin_dynamic_symbols(), e = obj.end_dynamic_symbols(); + i != e; ++i) { + auto name = obj.getSymbolName(i); + if ((ec = name.getError())) + return ec; + + // TODO: Add absolute symbols + if (i->st_shndx == llvm::ELF::SHN_ABS) + continue; + + if (i->st_shndx == llvm::ELF::SHN_UNDEF) { + if (!_useShlibUndefines) + continue; + // Create an undefined atom. + if (!name->empty()) { + auto *newAtom = new (_alloc) ELFUndefinedAtom(*this, *name, &*i); + _undefinedAtoms._atoms.push_back(newAtom); + } + continue; + } + _nameToSym[*name]._symbol = &*i; + } + return std::error_code(); + } + private: - DynamicFile(StringRef name) : SharedLibraryFile(name) {} + DynamicFile(std::unique_ptr mb, bool useShlibUndefines) + : SharedLibraryFile(mb->getBufferIdentifier()), + _mb(std::move(mb)), _useShlibUndefines(useShlibUndefines) {} mutable llvm::BumpPtrAllocator _alloc; std::unique_ptr> _objFile; @@ -73,6 +116,8 @@ const SharedLibraryAtom *_atom; }; + std::unique_ptr _mb; + bool _useShlibUndefines; mutable std::unordered_map _nameToSym; }; @@ -80,48 +125,8 @@ ErrorOr>> DynamicFile::create(std::unique_ptr mb, bool useShlibUndefines) { - std::unique_ptr file(new DynamicFile(mb->getBufferIdentifier())); - - std::error_code ec; - file->_objFile.reset( - new llvm::object::ELFFile(mb.release()->getBuffer(), ec)); - - if (ec) - return ec; - - llvm::object::ELFFile &obj = *file->_objFile; - - file->_soname = obj.getLoadName(); - if (file->_soname.empty()) - file->_soname = llvm::sys::path::filename(file->path()); - - // Create a map from names to dynamic symbol table entries. - // TODO: This should use the object file's build in hash table instead if - // it exists. - for (auto i = obj.begin_dynamic_symbols(), e = obj.end_dynamic_symbols(); - i != e; ++i) { - auto name = obj.getSymbolName(i); - if ((ec = name.getError())) - return ec; - - // TODO: Add absolute symbols - if (i->st_shndx == llvm::ELF::SHN_ABS) - continue; - - if (i->st_shndx == llvm::ELF::SHN_UNDEF) { - if (!useShlibUndefines) - continue; - // Create an undefined atom. - if (!name->empty()) { - auto *newAtom = - new (file->_alloc) ELFUndefinedAtom(*file.get(), *name, &*i); - file->_undefinedAtoms._atoms.push_back(newAtom); - } - continue; - } - file->_nameToSym[*name]._symbol = &*i; - } - + std::unique_ptr file( + new DynamicFile(std::move(mb), useShlibUndefines)); return std::move(file); } Index: lib/ReaderWriter/ELF/ELFFile.h =================================================================== --- lib/ReaderWriter/ELF/ELFFile.h +++ lib/ReaderWriter/ELF/ELFFile.h @@ -122,7 +122,7 @@ : File(mb->getBufferIdentifier(), kindObject), _mb(std::move(mb)), _ordinal(0), _doStringsMerge(atomizeStrings) {} - virtual std::error_code parse(); + virtual std::error_code doParse() override; static ErrorOr> create(std::unique_ptr mb, bool atomizeStrings); @@ -417,19 +417,15 @@ template ErrorOr>> ELFFile::create(std::unique_ptr mb, bool atomizeStrings) { - std::error_code ec; std::unique_ptr> file( new ELFFile(std::move(mb), atomizeStrings)); - if (std::error_code ec = file->parse()) - return ec; return std::move(file); } template -std::error_code ELFFile::parse() { +std::error_code ELFFile::doParse() { std::error_code ec; - _objFile.reset( - new llvm::object::ELFFile(_mb.release()->getBuffer(), ec)); + _objFile.reset(new llvm::object::ELFFile(_mb.release()->getBuffer(), ec)); if (ec) return ec; Index: lib/ReaderWriter/ELF/Hexagon/HexagonELFFile.h =================================================================== --- lib/ReaderWriter/ELF/Hexagon/HexagonELFFile.h +++ lib/ReaderWriter/ELF/Hexagon/HexagonELFFile.h @@ -119,11 +119,8 @@ static ErrorOr> create(std::unique_ptr mb, bool atomizeStrings) { - std::unique_ptr> file( + return std::unique_ptr>( new HexagonELFFile(std::move(mb), atomizeStrings)); - if (std::error_code ec = file->parse()) - return ec; - return std::move(file); } virtual bool isCommonSymbol(const Elf_Sym *symbol) const { Index: lib/ReaderWriter/ELF/Mips/MipsELFFile.h =================================================================== --- lib/ReaderWriter/ELF/Mips/MipsELFFile.h +++ lib/ReaderWriter/ELF/Mips/MipsELFFile.h @@ -85,11 +85,8 @@ static ErrorOr> create(std::unique_ptr mb, bool atomizeStrings) { - std::unique_ptr> file( + return std::unique_ptr>( new MipsELFFile(std::move(mb), atomizeStrings)); - if (std::error_code ec = file->parse()) - return ec; - return std::move(file); } bool isPIC() const { @@ -103,8 +100,8 @@ uint64_t getTPOffset() const { return *_tpOff; } uint64_t getDTPOffset() const { return *_dtpOff; } - std::error_code parse() override { - if (std::error_code ec = ELFFile::parse()) + std::error_code doParse() override { + if (std::error_code ec = ELFFile::doParse()) return ec; // Retrieve some auxiliary data like GP value, TLS section address etc // from the object file. Index: lib/ReaderWriter/ELF/PPC/PPCELFFile.h =================================================================== --- lib/ReaderWriter/ELF/PPC/PPCELFFile.h +++ lib/ReaderWriter/ELF/PPC/PPCELFFile.h @@ -19,11 +19,14 @@ template class PPCELFFile : public ELFFile { public: - PPCELFFile(StringRef name) : ELFFile(name) {} - - PPCELFFile(std::unique_ptr mb, bool atomizeStrings, - TargetHandlerBase *handler, std::error_code &ec) - : ELFFile(std::move(mb), atomizeStrings, handler, ec) {} + PPCELFFile(std::unique_ptr mb, bool atomizeStrings) + : ELFFile(std::move(mb), atomizeStrings) {} + + static ErrorOr> + create(std::unique_ptr mb, bool atomizeStrings) { + return std::unique_ptr>( + new PPCELFFile(std::move(mb), atomizeStrings)); + } }; template class PPCDynamicFile : public DynamicFile { Index: lib/ReaderWriter/ELF/X86/X86ELFFile.h =================================================================== --- lib/ReaderWriter/ELF/X86/X86ELFFile.h +++ lib/ReaderWriter/ELF/X86/X86ELFFile.h @@ -24,11 +24,8 @@ static ErrorOr> create(std::unique_ptr mb, bool atomizeStrings) { - std::unique_ptr> file( + return std::unique_ptr>( new X86ELFFile(std::move(mb), atomizeStrings)); - if (std::error_code ec = file->parse()) - return ec; - return std::move(file); } }; Index: lib/ReaderWriter/ELF/X86_64/X86_64ELFFile.h =================================================================== --- lib/ReaderWriter/ELF/X86_64/X86_64ELFFile.h +++ lib/ReaderWriter/ELF/X86_64/X86_64ELFFile.h @@ -24,11 +24,8 @@ static ErrorOr> create(std::unique_ptr mb, bool atomizeStrings) { - std::unique_ptr> file( + return std::unique_ptr>( new X86_64ELFFile(std::move(mb), atomizeStrings)); - if (std::error_code ec = file->parse()) - return ec; - return std::move(file); } }; Index: lib/ReaderWriter/FileArchive.cpp =================================================================== --- lib/ReaderWriter/FileArchive.cpp +++ lib/ReaderWriter/FileArchive.cpp @@ -33,12 +33,22 @@ /// \brief The FileArchive class represents an Archive Library file class FileArchive : public lld::ArchiveLibraryFile { public: - FileArchive(const Registry ®istry, Archive *archive, StringRef path, - bool isWholeArchive, bool logLoading) - : ArchiveLibraryFile(path), _registry(registry), - _archive(std::move(archive)), _isWholeArchive(isWholeArchive), + FileArchive(std::unique_ptr &mb, const Registry ®, + StringRef path, bool logLoading) + : ArchiveLibraryFile(path), _mb(mb), _registry(reg), _logLoading(logLoading) {} + std::error_code doParse() override { + // Make Archive object which will be owned by FileArchive object. + std::error_code ec; + _archive.reset(new Archive(_mb->getMemBufferRef(), ec)); + if (ec) + return ec; + if ((ec = buildTableOfContents())) + return ec; + return std::error_code(); + } + virtual ~FileArchive() {} /// \brief Check if any member of the archive contains an Atom with the @@ -204,6 +214,7 @@ typedef std::unordered_map MemberMap; typedef std::set InstantiatedSet; + std::unique_ptr &_mb; const Registry &_registry; std::unique_ptr _archive; mutable MemberMap _symbolMemberMap; @@ -229,20 +240,8 @@ std::error_code parseFile(std::unique_ptr &mb, const Registry ®, std::vector> &result) const override { - MemoryBuffer &buff = *mb; - // Make Archive object which will be owned by FileArchive object. - std::error_code ec; - Archive *archive = new Archive(mb->getMemBufferRef(), ec); - if (ec) - return ec; - StringRef path = buff.getBufferIdentifier(); - // Construct FileArchive object. std::unique_ptr file( - new FileArchive(reg, archive, path, false, _logLoading)); - ec = file->buildTableOfContents(); - if (ec) - return ec; - + new FileArchive(mb, reg, mb->getBufferIdentifier(), _logLoading)); result.push_back(std::move(file)); return std::error_code(); } Index: lib/ReaderWriter/MachO/File.h =================================================================== --- lib/ReaderWriter/MachO/File.h +++ lib/ReaderWriter/MachO/File.h @@ -24,6 +24,9 @@ class MachOFile : public SimpleFile { public: + MachOFile(MemoryBuffer *mb, MachOLinkingContext *ctx) + : SimpleFile(mb->getBufferIdentifier()), _mb(mb), _ctx(ctx) {} + MachOFile(StringRef path) : SimpleFile(path) {} void addDefinedAtom(StringRef name, Atom::Scope scope, @@ -172,6 +175,19 @@ visitor(offAndAtom.atom, offAndAtom.offset); } + std::error_code doParse() override { + // Convert binary file to normalized mach-o. + std::unique_ptrmb(_mb); + auto normFile = normalized::readBinary(mb, _ctx->arch()); + mb.release(); + if (std::error_code ec = normFile.getError()) + return ec; + // Convert normalized mach-o to atoms. + if (std::error_code ec = normalized::normalizedObjectToAtoms( + this, **normFile, false)) + return ec; + return std::error_code(); + } private: struct SectionOffsetAndAtom { uint64_t offset; MachODefinedAtom *atom; }; @@ -190,17 +206,18 @@ std::vector> SectionToAtoms; typedef llvm::StringMap NameToAtom; + MemoryBuffer *_mb; + MachOLinkingContext *_ctx; SectionToAtoms _sectionAtoms; NameToAtom _undefAtoms; }; class MachODylibFile : public SharedLibraryFile { public: - MachODylibFile(StringRef path, StringRef installName, uint32_t compatVersion, - uint32_t currentVersion) - : SharedLibraryFile(path), _installName(installName), - _currentVersion(currentVersion), _compatVersion(compatVersion) { - } + MachODylibFile(MemoryBuffer *mb, MachOLinkingContext *ctx) + : SharedLibraryFile(mb->getBufferIdentifier()), _mb(mb), _ctx(ctx) {} + + MachODylibFile(StringRef path) : SharedLibraryFile(path) {} const SharedLibraryAtom *exports(StringRef name, bool isData) const override { // Pass down _installName so that if this requested symbol @@ -241,11 +258,13 @@ } StringRef installName() { return _installName; } - uint32_t currentVersion() { return _currentVersion; } - uint32_t compatVersion() { return _compatVersion; } + void setInstallName(StringRef name) { _installName = name; } + void setCompatVersion(uint32_t version) { _compatVersion = version; } + void setCurrentVersion(uint32_t version) { _currentVersion = version; } + typedef std::function FindDylib; void loadReExportedDylibs(FindDylib find) { @@ -254,6 +273,20 @@ } } + std::error_code doParse() override { + // Convert binary file to normalized mach-o. + std::unique_ptrmb(_mb); + auto normFile = normalized::readBinary(mb, _ctx->arch()); + mb.release(); + if (std::error_code ec = normFile.getError()) + return ec; + // Convert normalized mach-o to atoms. + if (std::error_code ec = normalized::normalizedDylibToAtoms( + this, **normFile, false)) + return ec; + return std::error_code(); + } + private: const SharedLibraryAtom *exports(StringRef name, StringRef installName) const { @@ -295,6 +328,8 @@ bool weakDef; }; + MemoryBuffer *_mb; + MachOLinkingContext *_ctx; StringRef _installName; uint32_t _currentVersion; uint32_t _compatVersion; Index: lib/ReaderWriter/MachO/MachONormalizedFile.h =================================================================== --- lib/ReaderWriter/MachO/MachONormalizedFile.h +++ lib/ReaderWriter/MachO/MachONormalizedFile.h @@ -285,6 +285,16 @@ /// Writes a yaml encoded mach-o files given an in-memory normalized view. std::error_code writeYaml(const NormalizedFile &file, raw_ostream &out); +std::error_code +normalizedObjectToAtoms(MachOFile *file, + const NormalizedFile &normalizedFile, + bool copyRefs); + +std::error_code +normalizedDylibToAtoms(MachODylibFile *file, + const NormalizedFile &normalizedFile, + bool copyRefs); + /// Takes in-memory normalized dylib or object and parses it into lld::File ErrorOr> normalizedToAtoms(const NormalizedFile &normalizedFile, StringRef path, Index: lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp =================================================================== --- lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp +++ lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp @@ -505,16 +505,14 @@ return std::move(f); } -class MachOReader : public Reader { +class MachOObjectReader : public Reader { public: - MachOReader(MachOLinkingContext &ctx) : _ctx(ctx) {} + MachOObjectReader(MachOLinkingContext &ctx) : _ctx(ctx) {} bool canParse(file_magic magic, StringRef ext, const MemoryBuffer &mb) const override { switch (magic) { case llvm::sys::fs::file_magic::macho_object: - case llvm::sys::fs::file_magic::macho_dynamically_linked_shared_lib: - case llvm::sys::fs::file_magic::macho_dynamically_linked_shared_lib_stub: return (mb.getBufferSize() > 32); default: return false; @@ -524,30 +522,49 @@ std::error_code parseFile(std::unique_ptr &mb, const Registry ®istry, std::vector> &result) const override { - // Convert binary file to normalized mach-o. - auto normFile = readBinary(mb, _ctx.arch()); - if (std::error_code ec = normFile.getError()) - return ec; - // Convert normalized mach-o to atoms. - auto file = normalizedToAtoms(**normFile, mb->getBufferIdentifier(), false); - if (std::error_code ec = file.getError()) - return ec; + auto *file = new MachOFile(mb.get(), &_ctx); + result.push_back(std::unique_ptr(file)); + return std::error_code(); + } - result.push_back(std::move(*file)); +private: + MachOLinkingContext &_ctx; +}; +class MachODylibReader : public Reader { +public: + MachODylibReader(MachOLinkingContext &ctx) : _ctx(ctx) {} + + bool canParse(file_magic magic, StringRef ext, + const MemoryBuffer &mb) const override { + switch (magic) { + case llvm::sys::fs::file_magic::macho_dynamically_linked_shared_lib: + case llvm::sys::fs::file_magic::macho_dynamically_linked_shared_lib_stub: + return (mb.getBufferSize() > 32); + default: + return false; + } + } + + std::error_code + parseFile(std::unique_ptr &mb, const Registry ®istry, + std::vector> &result) const override { + auto *file = new MachODylibFile(mb.get(), &_ctx); + result.push_back(std::unique_ptr(file)); return std::error_code(); } + private: MachOLinkingContext &_ctx; }; - } // namespace normalized } // namespace mach_o void Registry::addSupportMachOObjects(MachOLinkingContext &ctx) { MachOLinkingContext::Arch arch = ctx.arch(); - add(std::unique_ptr(new mach_o::normalized::MachOReader(ctx))); + add(std::unique_ptr(new mach_o::normalized::MachOObjectReader(ctx))); + add(std::unique_ptr(new mach_o::normalized::MachODylibReader(ctx))); addKindTable(Reference::KindNamespace::mach_o, ctx.archHandler().kindArch(), ctx.archHandler().kindStrings()); add(std::unique_ptr( Index: lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp =================================================================== --- lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp +++ lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp @@ -712,9 +712,32 @@ /// Converts normalized mach-o file into an lld::File and lld::Atoms. ErrorOr> -normalizedObjectToAtoms(const NormalizedFile &normalizedFile, StringRef path, - bool copyRefs) { +objectToAtoms(const NormalizedFile &normalizedFile, StringRef path, + bool copyRefs) { std::unique_ptr file(new MachOFile(path)); + if (std::error_code ec = normalizedObjectToAtoms( + file.get(), normalizedFile, copyRefs)) + return ec; + return std::unique_ptr(std::move(file)); +} + +ErrorOr> +dylibToAtoms(const NormalizedFile &normalizedFile, StringRef path, + bool copyRefs) { + // Instantiate SharedLibraryFile object. + std::unique_ptr file(new MachODylibFile(path)); + normalizedDylibToAtoms(file.get(), normalizedFile, copyRefs); + return std::unique_ptr(std::move(file)); +} + +} // anonymous namespace + +namespace normalized { + +std::error_code +normalizedObjectToAtoms(MachOFile *file, + const NormalizedFile &normalizedFile, + bool copyRefs) { bool scatterable = ((normalizedFile.flags & MH_SUBSECTIONS_VIA_SYMBOLS) != 0); // Create atoms from each section. @@ -811,18 +834,17 @@ for (const DefinedAtom* defAtom : file->defined()) { reinterpret_cast(defAtom)->sortReferences(); } - - return std::unique_ptr(std::move(file)); + return std::error_code(); } -ErrorOr> -normalizedDylibToAtoms(const NormalizedFile &normalizedFile, StringRef path, +std::error_code +normalizedDylibToAtoms(MachODylibFile *file, + const NormalizedFile &normalizedFile, bool copyRefs) { - // Instantiate SharedLibraryFile object. - std::unique_ptr file( - new MachODylibFile(path, normalizedFile.installName, - normalizedFile.compatVersion, - normalizedFile.currentVersion)); + file->setInstallName(normalizedFile.installName); + file->setCompatVersion(normalizedFile.compatVersion); + file->setCurrentVersion(normalizedFile.currentVersion); + // Tell MachODylibFile object about all symbols it exports. if (!normalizedFile.exportInfo.empty()) { // If exports trie exists, use it instead of traditional symbol table. @@ -843,14 +865,9 @@ if (dep.kind == llvm::MachO::LC_REEXPORT_DYLIB) file->addReExportedDylib(dep.path); } - - return std::unique_ptr(std::move(file)); + return std::error_code(); } -} // anonymous namespace - -namespace normalized { - void relocatableSectionInfoForContentType(DefinedAtom::ContentType atomType, StringRef &segmentName, StringRef §ionName, @@ -881,9 +898,9 @@ switch (normalizedFile.fileType) { case MH_DYLIB: case MH_DYLIB_STUB: - return normalizedDylibToAtoms(normalizedFile, path, copyRefs); + return dylibToAtoms(normalizedFile, path, copyRefs); case MH_OBJECT: - return normalizedObjectToAtoms(normalizedFile, path, copyRefs); + return objectToAtoms(normalizedFile, path, copyRefs); default: llvm_unreachable("unhandled MachO file type!"); } Index: lib/ReaderWriter/Native/ReaderNative.cpp =================================================================== --- lib/ReaderWriter/Native/ReaderNative.cpp +++ lib/ReaderWriter/Native/ReaderNative.cpp @@ -259,14 +259,21 @@ // class File : public lld::File { public: + File(std::unique_ptr mb) + : lld::File(mb->getBufferIdentifier(), kindObject), + _mb(std::move(mb)), // Reader now takes ownership of buffer + _header(nullptr), _targetsTable(nullptr), _targetsTableCount(0), + _strings(nullptr), _stringsMaxOffset(0), _addends(nullptr), + _addendsMaxIndex(0), _contentStart(nullptr), _contentEnd(nullptr) { + _header = + reinterpret_cast(_mb->getBufferStart()); + } - /// Instantiates a File object from a native object file. Ownership - /// of the MemoryBuffer is transferred to the resulting File object. - static std::error_code make(std::unique_ptr mb, - std::vector> &result) { + /// Parses a File object from a native object file. + std::error_code doParse() override { const uint8_t *const base = - reinterpret_cast(mb->getBufferStart()); - StringRef path(mb->getBufferIdentifier()); + reinterpret_cast(_mb->getBufferStart()); + StringRef path(_mb->getBufferIdentifier()); const NativeFileHeader *const header = reinterpret_cast(base); const NativeChunk *const chunks = @@ -277,7 +284,7 @@ return make_error_code(NativeReaderError::unknown_file_format); // make sure mapped file contains all needed data - const size_t fileSize = mb->getBufferSize(); + const size_t fileSize = _mb->getBufferSize(); if (header->fileSize > fileSize) return make_error_code(NativeReaderError::file_too_short); @@ -286,9 +293,6 @@ << header->fileSize << " chunkCount=" << header->chunkCount << "\n"); - // instantiate NativeFile object and add values to it as found - std::unique_ptr file(new File(std::move(mb), path)); - // process each chunk for (uint32_t i = 0; i < header->chunkCount; ++i) { std::error_code ec; @@ -301,40 +305,40 @@ // process chunk, based on signature switch ( chunk->signature ) { case NCS_DefinedAtomsV1: - ec = file->processDefinedAtomsV1(base, chunk); + ec = processDefinedAtomsV1(base, chunk); break; case NCS_AttributesArrayV1: - ec = file->processAttributesV1(base, chunk); + ec = processAttributesV1(base, chunk); break; case NCS_UndefinedAtomsV1: - ec = file->processUndefinedAtomsV1(base, chunk); + ec = processUndefinedAtomsV1(base, chunk); break; case NCS_SharedLibraryAtomsV1: - ec = file->processSharedLibraryAtomsV1(base, chunk); + ec = processSharedLibraryAtomsV1(base, chunk); break; case NCS_AbsoluteAtomsV1: - ec = file->processAbsoluteAtomsV1(base, chunk); + ec = processAbsoluteAtomsV1(base, chunk); break; case NCS_AbsoluteAttributesV1: - ec = file->processAbsoluteAttributesV1(base, chunk); + ec = processAbsoluteAttributesV1(base, chunk); break; case NCS_ReferencesArrayV1: - ec = file->processReferencesV1(base, chunk); + ec = processReferencesV1(base, chunk); break; case NCS_ReferencesArrayV2: - ec = file->processReferencesV2(base, chunk); + ec = processReferencesV2(base, chunk); break; case NCS_TargetsTable: - ec = file->processTargetsTable(base, chunk); + ec = processTargetsTable(base, chunk); break; case NCS_AddendsTable: - ec = file->processAddendsTable(base, chunk); + ec = processAddendsTable(base, chunk); break; case NCS_Content: - ec = file->processContent(base, chunk); + ec = processContent(base, chunk); break; case NCS_Strings: - ec = file->processStrings(base, chunk); + ec = processStrings(base, chunk); break; default: return make_error_code(NativeReaderError::unknown_chunk_type); @@ -347,7 +351,7 @@ DEBUG_WITH_TYPE("ReaderNative", { llvm::dbgs() << " ReaderNative DefinedAtoms:\n"; - for (const DefinedAtom *a : file->defined()) { + for (const DefinedAtom *a : defined()) { llvm::dbgs() << llvm::format(" 0x%09lX", a) << ", name=" << a->name() << ", size=" << a->size() << "\n"; @@ -359,12 +363,11 @@ } } }); - result.push_back(std::move(file)); return make_error_code(NativeReaderError::success); } virtual ~File() { - // _buffer is automatically deleted because of std::unique_ptr<> + // _mb is automatically deleted because of std::unique_ptr<> // All other ivar pointers are pointers into the MemoryBuffer, except // the _definedAtoms array which was allocated to contain an array @@ -785,17 +788,6 @@ _targetsTable[index] = newAtom; } - // private constructor, only called by make() - File(std::unique_ptr mb, StringRef path) - : lld::File(path, kindObject), - _buffer(std::move(mb)), // Reader now takes ownership of buffer - _header(nullptr), _targetsTable(nullptr), _targetsTableCount(0), - _strings(nullptr), _stringsMaxOffset(0), _addends(nullptr), - _addendsMaxIndex(0), _contentStart(nullptr), _contentEnd(nullptr) { - _header = - reinterpret_cast(_buffer->getBufferStart()); - } - template class AtomArray : public File::atom_collection { public: @@ -836,7 +828,7 @@ uint32_t elementCount; }; - std::unique_ptr _buffer; + std::unique_ptr _mb; const NativeFileHeader* _header; AtomArray _definedAtoms; AtomArray _undefinedAtoms; @@ -1009,9 +1001,12 @@ virtual std::error_code parseFile(std::unique_ptr &mb, const class Registry &, std::vector> &result) const override { - return lld::native::File::make(std::move(mb), result); + auto *file = new lld::native::File(std::move(mb)); + result.push_back(std::unique_ptr(file)); + return std::error_code(); } }; + } void Registry::addSupportNativeObjects() { Index: lib/ReaderWriter/PECOFF/ReaderCOFF.cpp =================================================================== --- lib/ReaderWriter/PECOFF/ReaderCOFF.cpp +++ lib/ReaderWriter/PECOFF/ReaderCOFF.cpp @@ -55,6 +55,21 @@ namespace { +class BumpPtrStringSaver : public llvm::cl::StringSaver { +public: + const char *SaveString(const char *str) override { + size_t len = strlen(str); + std::lock_guard lock(_allocMutex); + char *copy = _alloc.Allocate(len + 1); + memcpy(copy, str, len + 1); + return copy; + } + +private: + llvm::BumpPtrAllocator _alloc; + std::mutex _allocMutex; +}; + class FileCOFF : public File { private: typedef std::vector SymbolVectorT; @@ -66,10 +81,12 @@ public: typedef const std::map StringMap; - FileCOFF(std::unique_ptr mb, std::error_code &ec); + FileCOFF(std::unique_ptr mb, PECOFFLinkingContext &ctx) + : File(mb->getBufferIdentifier(), kindObject), _mb(std::move(mb)), + _compatibleWithSEH(false), _ordinal(0), + _machineType(llvm::COFF::MT_Invalid), _ctx(ctx) {} - std::error_code parse(); - StringRef getLinkerDirectives() const { return _directives; } + std::error_code doParse() override; bool isCompatibleWithSEH() const { return _compatibleWithSEH; } llvm::COFF::MachineTypes getMachineType() { return _machineType; } @@ -98,6 +115,11 @@ _undefinedAtoms._atoms.push_back(new (_alloc) COFFUndefinedAtom(*this, sym)); } + AliasAtom *createAlias(StringRef name, const DefinedAtom *target); + void createAlternateNameAtoms(); + std::error_code parseDirectiveSection( + StringRef directives, std::set *undefinedSymbols); + mutable llvm::BumpPtrAllocator _alloc; private: @@ -155,9 +177,6 @@ // The target type of the object. Reference::KindArch _referenceArch; - // The contents of .drectve section. - StringRef _directives; - // True if the object has "@feat.00" symbol. bool _compatibleWithSEH; @@ -192,21 +211,8 @@ uint64_t _ordinal; llvm::COFF::MachineTypes _machineType; -}; - -class BumpPtrStringSaver : public llvm::cl::StringSaver { -public: - const char *SaveString(const char *str) override { - size_t len = strlen(str); - std::lock_guard lock(_allocMutex); - char *copy = _alloc.Allocate(len + 1); - memcpy(copy, str, len + 1); - return copy; - } - -private: - llvm::BumpPtrAllocator _alloc; - std::mutex _allocMutex; + PECOFFLinkingContext &_ctx; + mutable BumpPtrStringSaver _stringSaver; }; // Converts the COFF symbol attribute to the LLD's atom attribute. @@ -290,33 +296,54 @@ } } -FileCOFF::FileCOFF(std::unique_ptr mb, std::error_code &ec) - : File(mb->getBufferIdentifier(), kindObject), _mb(std::move(mb)), - _compatibleWithSEH(false), _ordinal(0), - _machineType(llvm::COFF::MT_Invalid) { +StringRef getMachineName(llvm::COFF::MachineTypes Type) { + switch (Type) { + default: llvm_unreachable("unsupported machine type"); + case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: + return "ARM"; + case llvm::COFF::IMAGE_FILE_MACHINE_I386: + return "X86"; + case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: + return "X64"; + } +} + +std::error_code FileCOFF::doParse() { auto binaryOrErr = llvm::object::createBinary(_mb->getMemBufferRef()); - if ((ec = binaryOrErr.getError())) - return; + if (std::error_code ec = binaryOrErr.getError()) + return ec; std::unique_ptr bin = std::move(binaryOrErr.get()); _obj.reset(dyn_cast(bin.get())); - if (!_obj) { - ec = make_error_code(llvm::object::object_error::invalid_file_type); - return; - } + if (!_obj) + return make_error_code(llvm::object::object_error::invalid_file_type); bin.release(); _machineType = static_cast(_obj->getMachine()); + if (getMachineType() != llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN && + getMachineType() != _ctx.getMachineType()) { + llvm::errs() << "module machine type '" + << getMachineName(getMachineType()) + << "' conflicts with target machine type '" + << getMachineName(_ctx.getMachineType()) << "'\n"; + return NativeReaderError::conflicting_target_machine; + } + + // The set to contain the symbols specified as arguments of + // /INCLUDE option. + std::set undefinedSymbols; + + // Interpret .drectve section if the section has contents. // Read .drectve section if exists. ArrayRef directives; - if ((ec = getSectionContents(".drectve", directives))) - return; + if (std::error_code ec = getSectionContents(".drectve", directives)) + return ec; if (!directives.empty()) - _directives = ArrayRefToString(directives); -} + if (std::error_code ec = parseDirectiveSection( + ArrayRefToString(directives), &undefinedSymbols)) + return ec; -std::error_code FileCOFF::parse() { if (std::error_code ec = getReferenceArch(_referenceArch)) return ec; @@ -329,7 +356,7 @@ createAbsoluteAtoms(symbols, _absoluteAtoms._atoms); if (std::error_code ec = - createUndefinedAtoms(symbols, _undefinedAtoms._atoms)) + createUndefinedAtoms(symbols, _undefinedAtoms._atoms)) return ec; if (std::error_code ec = createDefinedSymbols(symbols, _definedAtoms._atoms)) return ec; @@ -337,6 +364,37 @@ return ec; if (std::error_code ec = maybeCreateSXDataAtoms()) return ec; + + // Check for /SAFESEH. + if (_ctx.requireSEH() && !isCompatibleWithSEH()) { + llvm::errs() << "/SAFESEH is specified, but " + << _mb->getBufferIdentifier() + << " is not compatible with SEH.\n"; + return llvm::object::object_error::parse_failed; + } + + // Add /INCLUDE'ed symbols to the file as if they existed in the + // file as undefined symbols. + for (StringRef sym : undefinedSymbols) + addUndefinedSymbol(sym); + + // One can define alias symbols using /alternatename:= option. + // The mapping for /alternatename is in the context object. This helper + // function iterate over defined atoms and create alias atoms if needed. + createAlternateNameAtoms(); + + // Acquire the mutex to mutate _ctx. + std::lock_guard lock(_ctx.getMutex()); + + // In order to emit SEH table, all input files need to be compatible with + // SEH. Disable SEH if the file being read is not compatible. + if (!isCompatibleWithSEH()) + _ctx.setSafeSEH(false); + + if (_ctx.deadStrip()) + for (StringRef sym : undefinedSymbols) + _ctx.addDeadStripRoot(sym); + return std::error_code(); } @@ -804,6 +862,67 @@ return std::error_code(); } +AliasAtom *FileCOFF::createAlias(StringRef name, + const DefinedAtom *target) { + AliasAtom *alias = new (_alloc) AliasAtom(*this, name); + alias->addReference(Reference::KindNamespace::all, Reference::KindArch::all, + Reference::kindLayoutAfter, 0, target, 0); + alias->setMerge(DefinedAtom::mergeAsWeak); + if (target->contentType() == DefinedAtom::typeCode) + alias->setDeadStrip(DefinedAtom::deadStripNever); + return alias; +} + +void FileCOFF::createAlternateNameAtoms() { + std::vector aliases; + for (const DefinedAtom *atom : defined()) { + auto it = _ctx.alternateNames().find(atom->name()); + if (it != _ctx.alternateNames().end()) + aliases.push_back(createAlias(it->second, atom)); + } + for (AliasAtom *alias : aliases) + addDefinedAtom(alias); +} + +// Interpret the contents of .drectve section. If exists, the section contains +// a string containing command line options. The linker is expected to +// interpret the options as if they were given via the command line. +// +// The section mainly contains /defaultlib (-l in Unix), but can contain any +// options as long as they are valid. +std::error_code +FileCOFF::parseDirectiveSection(StringRef directives, + std::set *undefinedSymbols) { + DEBUG(llvm::dbgs() << ".drectve: " << directives << "\n"); + + // Split the string into tokens, as the shell would do for argv. + SmallVector tokens; + tokens.push_back("link"); // argv[0] is the command name. Will be ignored. + llvm::cl::TokenizeWindowsCommandLine(directives, _stringSaver, tokens); + tokens.push_back(nullptr); + + // Calls the command line parser to interpret the token string as if they + // were given via the command line. + int argc = tokens.size() - 1; + const char **argv = &tokens[0]; + std::string errorMessage; + llvm::raw_string_ostream stream(errorMessage); + bool parseFailed = !WinLinkDriver::parse(argc, argv, _ctx, stream, + /*isDirective*/ true, + undefinedSymbols); + stream.flush(); + // Print error message if error. + if (parseFailed) { + auto msg = Twine("Failed to parse '") + directives + "'\n" + + "Reason: " + errorMessage; + return make_dynamic_error_code(msg); + } + if (!errorMessage.empty()) { + llvm::errs() << "lld warning: " << errorMessage << "\n"; + } + return std::error_code(); +} + /// Returns the target machine type of the current object file. std::error_code FileCOFF::getReferenceArch(Reference::KindArch &result) { switch (_obj->getMachine()) { @@ -945,18 +1064,6 @@ return StringRef(*contents).trim(); } -StringRef getMachineName(llvm::COFF::MachineTypes Type) { - switch (Type) { - default: llvm_unreachable("unsupported machine type"); - case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: - return "ARM"; - case llvm::COFF::IMAGE_FILE_MACHINE_I386: - return "X86"; - case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: - return "X64"; - } -} - class COFFObjectReader : public Reader { public: COFFObjectReader(PECOFFLinkingContext &ctx) : _ctx(ctx) {} @@ -967,136 +1074,16 @@ } std::error_code - parseFile(std::unique_ptr &mb, const Registry ®istry, + parseFile(std::unique_ptr &mb, const Registry &, std::vector> &result) const override { // Parse the memory buffer as PECOFF file. - const char *mbName = mb->getBufferIdentifier(); - std::error_code ec; - std::unique_ptr file(new FileCOFF(std::move(mb), ec)); - if (ec) - return ec; - - if (file->getMachineType() != llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN && - file->getMachineType() != _ctx.getMachineType()) { - llvm::errs() << "module machine type '" - << getMachineName(file->getMachineType()) - << "' conflicts with target machine type '" - << getMachineName(_ctx.getMachineType()) << "'\n"; - return NativeReaderError::conflicting_target_machine; - } - - // The set to contain the symbols specified as arguments of - // /INCLUDE option. - std::set undefinedSymbols; - - // Interpret .drectve section if the section has contents. - StringRef directives = file->getLinkerDirectives(); - if (!directives.empty()) - if (std::error_code ec = handleDirectiveSection( - directives, &undefinedSymbols)) - return ec; - - if (std::error_code ec = file->parse()) - return ec; - - // Check for /SAFESEH. - if (_ctx.requireSEH() && !file->isCompatibleWithSEH()) { - llvm::errs() << "/SAFESEH is specified, but " << mbName - << " is not compatible with SEH.\n"; - return llvm::object::object_error::parse_failed; - } - - // Add /INCLUDE'ed symbols to the file as if they existed in the - // file as undefined symbols. - for (StringRef sym : undefinedSymbols) - file->addUndefinedSymbol(sym); - - // One can define alias symbols using /alternatename:= option. - // The mapping for /alternatename is in the context object. This helper - // function iterate over defined atoms and create alias atoms if needed. - createAlternateNameAtoms(*file); - - // Acquire the mutex to mutate _ctx. - std::lock_guard lock(_ctx.getMutex()); - - // In order to emit SEH table, all input files need to be compatible with - // SEH. Disable SEH if the file being read is not compatible. - if (!file->isCompatibleWithSEH()) - _ctx.setSafeSEH(false); - - if (_ctx.deadStrip()) - for (StringRef sym : undefinedSymbols) - _ctx.addDeadStripRoot(sym); - - result.push_back(std::move(file)); + auto *file = new FileCOFF(std::move(mb), _ctx); + result.push_back(std::unique_ptr(file)); return std::error_code(); } private: - // Interpret the contents of .drectve section. If exists, the section contains - // a string containing command line options. The linker is expected to - // interpret the options as if they were given via the command line. - // - // The section mainly contains /defaultlib (-l in Unix), but can contain any - // options as long as they are valid. - std::error_code handleDirectiveSection(StringRef directives, - std::set *undefinedSymbols) const { - DEBUG(llvm::dbgs() << ".drectve: " << directives << "\n"); - - // Split the string into tokens, as the shell would do for argv. - SmallVector tokens; - tokens.push_back("link"); // argv[0] is the command name. Will be ignored. - llvm::cl::TokenizeWindowsCommandLine(directives, _stringSaver, tokens); - tokens.push_back(nullptr); - - // Calls the command line parser to interpret the token string as if they - // were given via the command line. - int argc = tokens.size() - 1; - const char **argv = &tokens[0]; - std::string errorMessage; - llvm::raw_string_ostream stream(errorMessage); - bool parseFailed = !WinLinkDriver::parse(argc, argv, _ctx, stream, - /*isDirective*/ true, - undefinedSymbols); - stream.flush(); - // Print error message if error. - if (parseFailed) { - auto msg = Twine("Failed to parse '") + directives + "'\n" - + "Reason: " + errorMessage; - return make_dynamic_error_code(msg); - } - if (!errorMessage.empty()) { - llvm::errs() << "lld warning: " << errorMessage << "\n"; - } - return std::error_code(); - } - - AliasAtom *createAlias(FileCOFF &file, StringRef name, - const DefinedAtom *target) const { - AliasAtom *alias = new (file._alloc) AliasAtom(file, name); - alias->addReference(Reference::KindNamespace::all, Reference::KindArch::all, - Reference::kindLayoutAfter, 0, target, 0); - alias->setMerge(DefinedAtom::mergeAsWeak); - if (target->contentType() == DefinedAtom::typeCode) - alias->setDeadStrip(DefinedAtom::deadStripNever); - return alias; - } - - // Iterates over defined atoms and create alias atoms if needed. - void createAlternateNameAtoms(FileCOFF &file) const { - std::vector aliases; - for (const DefinedAtom *atom : file.defined()) { - auto it = _ctx.alternateNames().find(atom->name()); - if (it != _ctx.alternateNames().end()) - aliases.push_back(createAlias(file, it->second, atom)); - } - for (AliasAtom *alias : aliases) { - file.addDefinedAtom(alias); - } - } - PECOFFLinkingContext &_ctx; - mutable BumpPtrStringSaver _stringSaver; }; using namespace llvm::COFF; Index: lib/ReaderWriter/PECOFF/ReaderImportHeader.cpp =================================================================== --- lib/ReaderWriter/PECOFF/ReaderImportHeader.cpp +++ lib/ReaderWriter/PECOFF/ReaderImportHeader.cpp @@ -197,31 +197,31 @@ class FileImportLibrary : public File { public: - FileImportLibrary(std::unique_ptr mb, std::error_code &ec, - MachineTypes machine) - : File(mb->getBufferIdentifier(), kindSharedLibrary), _machine(machine) { - const char *buf = mb->getBufferStart(); - const char *end = mb->getBufferEnd(); + FileImportLibrary(std::unique_ptr mb, MachineTypes machine) + : File(mb->getBufferIdentifier(), kindSharedLibrary), + _mb(std::move(mb)), _machine(machine) {} + + std::error_code doParse() override { + const char *buf = _mb->getBufferStart(); + const char *end = _mb->getBufferEnd(); // The size of the string that follows the header. uint32_t dataSize = *reinterpret_cast( - buf + offsetof(COFF::ImportHeader, SizeOfData)); + buf + offsetof(COFF::ImportHeader, SizeOfData)); // Check if the total size is valid. - if (std::size_t(end - buf) != sizeof(COFF::ImportHeader) + dataSize) { - ec = make_error_code(NativeReaderError::unknown_file_format); - return; - } + if (std::size_t(end - buf) != sizeof(COFF::ImportHeader) + dataSize) + return make_error_code(NativeReaderError::unknown_file_format); uint16_t hint = *reinterpret_cast( - buf + offsetof(COFF::ImportHeader, OrdinalHint)); + buf + offsetof(COFF::ImportHeader, OrdinalHint)); StringRef symbolName(buf + sizeof(COFF::ImportHeader)); StringRef dllName(buf + sizeof(COFF::ImportHeader) + symbolName.size() + 1); // TypeInfo is a bitfield. The least significant 2 bits are import // type, followed by 3 bit import name type. uint16_t typeInfo = *reinterpret_cast( - buf + offsetof(COFF::ImportHeader, TypeInfo)); + buf + offsetof(COFF::ImportHeader, TypeInfo)); int type = typeInfo & 0x3; int nameType = (typeInfo >> 2) & 0x7; @@ -235,7 +235,7 @@ if (type == llvm::COFF::IMPORT_CODE) addFuncAtom(symbolName, dllName, dataAtom); - ec = std::error_code(); + return std::error_code(); } const atom_collection &defined() const override { @@ -309,6 +309,7 @@ return *str; } + std::unique_ptr _mb; MachineTypes _machine; }; @@ -326,12 +327,8 @@ std::error_code parseFile(std::unique_ptr &mb, const class Registry &, std::vector > &result) const override { - std::error_code ec; - auto file = std::unique_ptr( - new FileImportLibrary(std::move(mb), ec, _machine)); - if (ec) - return ec; - result.push_back(std::move(file)); + auto *file = new FileImportLibrary(std::move(mb), _machine); + result.push_back(std::unique_ptr(file)); return std::error_code(); } Index: lib/ReaderWriter/Reader.cpp =================================================================== --- lib/ReaderWriter/Reader.cpp +++ lib/ReaderWriter/Reader.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "lld/Core/File.h" #include "lld/ReaderWriter/Reader.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Errc.h" @@ -38,9 +39,16 @@ StringRef extension = llvm::sys::path::extension(mb->getBufferIdentifier()); // Ask each registered reader if it can handle this file type or extension. - for (const std::unique_ptr &reader : _readers) - if (reader->canParse(fileType, extension, *mb)) - return reader->parseFile(mb, *this, result); + for (const std::unique_ptr &reader : _readers) { + if (!reader->canParse(fileType, extension, *mb)) + continue; + if (std::error_code ec = reader->parseFile(mb, *this, result)) + return ec; + for (std::unique_ptr &file : result) + if (std::error_code ec = file->parse()) + return ec; + return std::error_code(); + } // No Reader could parse this file. return make_error_code(llvm::errc::executable_format_error); Index: lib/ReaderWriter/YAML/ReaderWriterYAML.cpp =================================================================== --- lib/ReaderWriter/YAML/ReaderWriterYAML.cpp +++ lib/ReaderWriter/YAML/ReaderWriterYAML.cpp @@ -1351,6 +1351,7 @@ for (const File *file : createdFiles) { // Note: parseFile() should return vector of *const* File File *f = const_cast(file); + f->setLastError(std::error_code()); result.emplace_back(f); } return make_error_code(lld::YamlReaderError::success);