| //===- Core/File.h - A Container of Atoms ---------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLD_CORE_FILE_H |
| #define LLD_CORE_FILE_H |
| |
| #include "lld/Core/AbsoluteAtom.h" |
| #include "lld/Core/DefinedAtom.h" |
| #include "lld/Core/SharedLibraryAtom.h" |
| #include "lld/Core/UndefinedAtom.h" |
| #include "llvm/ADT/Optional.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/Twine.h" |
| #include "llvm/Support/Allocator.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include <functional> |
| #include <memory> |
| #include <mutex> |
| #include <vector> |
| |
| namespace lld { |
| |
| class LinkingContext; |
| |
| /// Every Atom is owned by some File. A common scenario is for a single |
| /// object file (.o) to be parsed by some reader and produce a single |
| /// File object that represents the content of that object file. |
| /// |
| /// To iterate through the Atoms in a File there are four methods that |
| /// return collections. For instance to iterate through all the DefinedAtoms |
| /// in a File object use: |
| /// for (const DefinedAtoms *atom : file->defined()) { |
| /// } |
| /// |
| /// The Atom objects in a File are owned by the File object. The Atom objects |
| /// are destroyed when the File object is destroyed. |
| class File { |
| public: |
| virtual ~File(); |
| |
| /// Kinds of files that are supported. |
| enum Kind { |
| kindErrorObject, ///< a error object file (.o) |
| kindNormalizedObject, ///< a normalized file (.o) |
| kindMachObject, ///< a MachO object file (.o) |
| kindCEntryObject, ///< a file for CEntries |
| kindHeaderObject, ///< a file for file headers |
| kindEntryObject, ///< a file for the entry |
| kindUndefinedSymsObject, ///< a file for undefined symbols |
| kindStubHelperObject, ///< a file for stub helpers |
| kindResolverMergedObject, ///< the resolver merged file. |
| kindSectCreateObject, ///< a sect create object file (.o) |
| kindSharedLibrary, ///< shared library (.so) |
| kindArchiveLibrary ///< archive (.a) |
| }; |
| |
| /// Returns file kind. Need for dyn_cast<> on File objects. |
| Kind kind() const { |
| return _kind; |
| } |
| |
| /// This returns the path to the file which was used to create this object |
| /// (e.g. "/tmp/foo.o"). If the file is a member of an archive file, the |
| /// returned string includes the archive file name. |
| StringRef path() const { |
| if (_archivePath.empty()) |
| return _path; |
| if (_archiveMemberPath.empty()) |
| _archiveMemberPath = (_archivePath + "(" + _path + ")").str(); |
| return _archiveMemberPath; |
| } |
| |
| /// Returns the path of the archive file name if this file is instantiated |
| /// from an archive file. Otherwise returns the empty string. |
| StringRef archivePath() const { return _archivePath; } |
| void setArchivePath(StringRef path) { _archivePath = std::string(path); } |
| |
| /// Returns the path name of this file. It doesn't include archive file name. |
| StringRef memberPath() const { return _path; } |
| |
| /// Returns the command line order of the file. |
| uint64_t ordinal() const { |
| assert(_ordinal != UINT64_MAX); |
| return _ordinal; |
| } |
| |
| /// Returns true/false depending on whether an ordinal has been set. |
| bool hasOrdinal() const { return (_ordinal != UINT64_MAX); } |
| |
| /// Sets the command line order of the file. |
| void setOrdinal(uint64_t ordinal) const { _ordinal = ordinal; } |
| |
| /// Returns the ordinal for the next atom to be defined in this file. |
| uint64_t getNextAtomOrdinalAndIncrement() const { |
| return _nextAtomOrdinal++; |
| } |
| |
| /// For allocating any objects owned by this File. |
| llvm::BumpPtrAllocator &allocator() const { |
| return _allocator; |
| } |
| |
| /// The type of atom mutable container. |
| template <typename T> using AtomVector = std::vector<OwningAtomPtr<T>>; |
| |
| /// The range type for the atoms. |
| template <typename T> class AtomRange { |
| public: |
| AtomRange(AtomVector<T> &v) : _v(v) {} |
| AtomRange(const AtomVector<T> &v) : _v(const_cast<AtomVector<T> &>(v)) {} |
| |
| using ConstDerefFn = const T* (*)(const OwningAtomPtr<T>&); |
| using DerefFn = T* (*)(OwningAtomPtr<T>&); |
| |
| typedef llvm::mapped_iterator<typename AtomVector<T>::const_iterator, |
| ConstDerefFn> ConstItTy; |
| typedef llvm::mapped_iterator<typename AtomVector<T>::iterator, |
| DerefFn> ItTy; |
| |
| static const T* DerefConst(const OwningAtomPtr<T> &p) { |
| return p.get(); |
| } |
| |
| static T* Deref(OwningAtomPtr<T> &p) { |
| return p.get(); |
| } |
| |
| ConstItTy begin() const { |
| return ConstItTy(_v.begin(), ConstDerefFn(DerefConst)); |
| } |
| ConstItTy end() const { |
| return ConstItTy(_v.end(), ConstDerefFn(DerefConst)); |
| } |
| |
| ItTy begin() { |
| return ItTy(_v.begin(), DerefFn(Deref)); |
| } |
| ItTy end() { |
| return ItTy(_v.end(), DerefFn(Deref)); |
| } |
| |
| llvm::iterator_range<typename AtomVector<T>::iterator> owning_ptrs() { |
| return llvm::make_range(_v.begin(), _v.end()); |
| } |
| |
| llvm::iterator_range<typename AtomVector<T>::iterator> owning_ptrs() const { |
| return llvm::make_range(_v.begin(), _v.end()); |
| } |
| |
| bool empty() const { |
| return _v.empty(); |
| } |
| |
| size_t size() const { |
| return _v.size(); |
| } |
| |
| const OwningAtomPtr<T> &operator[](size_t idx) const { |
| return _v[idx]; |
| } |
| |
| OwningAtomPtr<T> &operator[](size_t idx) { |
| return _v[idx]; |
| } |
| |
| private: |
| AtomVector<T> &_v; |
| }; |
| |
| /// Must be implemented to return the AtomVector object for |
| /// all DefinedAtoms in this File. |
| virtual const AtomRange<DefinedAtom> defined() const = 0; |
| |
| /// Must be implemented to return the AtomVector object for |
| /// all UndefinedAtomw in this File. |
| virtual const AtomRange<UndefinedAtom> undefined() const = 0; |
| |
| /// Must be implemented to return the AtomVector object for |
| /// all SharedLibraryAtoms in this File. |
| virtual const AtomRange<SharedLibraryAtom> sharedLibrary() const = 0; |
| |
| /// Must be implemented to return the AtomVector object for |
| /// all AbsoluteAtoms in this File. |
| virtual const AtomRange<AbsoluteAtom> absolute() const = 0; |
| |
| /// Drop all of the atoms owned by this file. This will result in all of |
| /// the atoms running their destructors. |
| /// This is required because atoms may be allocated on a BumpPtrAllocator |
| /// of a different file. We need to destruct all atoms before any files. |
| virtual void clearAtoms() = 0; |
| |
| /// If a file is parsed using a different method than doParse(), |
| /// one must use this method to set the last error status, so that |
| /// doParse will not be called twice. Only YAML reader uses this |
| /// (because YAML reader does not read blobs but structured data). |
| void setLastError(std::error_code err) { _lastError = err; } |
| |
| std::error_code parse(); |
| |
| // Usually each file owns a std::unique_ptr<MemoryBuffer>. |
| // However, there's one special case. If a file is an archive file, |
| // the archive file and its children all shares the same memory buffer. |
| // This method is used by the ArchiveFile to give its children |
| // co-ownership of the buffer. |
| void setSharedMemoryBuffer(std::shared_ptr<MemoryBuffer> mb) { |
| _sharedMemoryBuffer = mb; |
| } |
| |
| protected: |
| /// only subclasses of File can be instantiated |
| File(StringRef p, Kind kind) |
| : _path(p), _kind(kind), _ordinal(UINT64_MAX), |
| _nextAtomOrdinal(0) {} |
| |
| /// Subclasses should override this method to parse the |
| /// memory buffer passed to this file's constructor. |
| virtual std::error_code doParse() { return std::error_code(); } |
| |
| static AtomVector<DefinedAtom> _noDefinedAtoms; |
| static AtomVector<UndefinedAtom> _noUndefinedAtoms; |
| static AtomVector<SharedLibraryAtom> _noSharedLibraryAtoms; |
| static AtomVector<AbsoluteAtom> _noAbsoluteAtoms; |
| mutable llvm::BumpPtrAllocator _allocator; |
| |
| private: |
| StringRef _path; |
| std::string _archivePath; |
| mutable std::string _archiveMemberPath; |
| Kind _kind; |
| mutable uint64_t _ordinal; |
| mutable uint64_t _nextAtomOrdinal; |
| std::shared_ptr<MemoryBuffer> _sharedMemoryBuffer; |
| llvm::Optional<std::error_code> _lastError; |
| std::mutex _parseMutex; |
| }; |
| |
| /// An ErrorFile represents a file that doesn't exist. |
| /// If you try to parse a file which doesn't exist, an instance of this |
| /// class will be returned. That's parse method always returns an error. |
| /// This is useful to delay erroring on non-existent files, so that we |
| /// can do unit testing a driver using non-existing file paths. |
| class ErrorFile : public File { |
| public: |
| ErrorFile(StringRef path, std::error_code ec) |
| : File(path, kindErrorObject), _ec(ec) {} |
| |
| std::error_code doParse() override { return _ec; } |
| |
| const AtomRange<DefinedAtom> defined() const override { |
| llvm_unreachable("internal error"); |
| } |
| const AtomRange<UndefinedAtom> undefined() const override { |
| llvm_unreachable("internal error"); |
| } |
| const AtomRange<SharedLibraryAtom> sharedLibrary() const override { |
| llvm_unreachable("internal error"); |
| } |
| const AtomRange<AbsoluteAtom> absolute() const override { |
| llvm_unreachable("internal error"); |
| } |
| |
| void clearAtoms() override { |
| } |
| |
| private: |
| std::error_code _ec; |
| }; |
| |
| } // end namespace lld |
| |
| #endif |