1 //===- Core/File.h - A Container of Atoms ---------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef LLD_CORE_FILE_H 10 #define LLD_CORE_FILE_H 11 12 #include "lld/Core/AbsoluteAtom.h" 13 #include "lld/Core/DefinedAtom.h" 14 #include "lld/Core/SharedLibraryAtom.h" 15 #include "lld/Core/UndefinedAtom.h" 16 #include "llvm/ADT/Optional.h" 17 #include "llvm/ADT/STLExtras.h" 18 #include "llvm/ADT/Twine.h" 19 #include "llvm/Support/Allocator.h" 20 #include "llvm/Support/ErrorHandling.h" 21 #include <functional> 22 #include <memory> 23 #include <mutex> 24 #include <vector> 25 26 namespace lld { 27 28 class LinkingContext; 29 30 /// Every Atom is owned by some File. A common scenario is for a single 31 /// object file (.o) to be parsed by some reader and produce a single 32 /// File object that represents the content of that object file. 33 /// 34 /// To iterate through the Atoms in a File there are four methods that 35 /// return collections. For instance to iterate through all the DefinedAtoms 36 /// in a File object use: 37 /// for (const DefinedAtoms *atom : file->defined()) { 38 /// } 39 /// 40 /// The Atom objects in a File are owned by the File object. The Atom objects 41 /// are destroyed when the File object is destroyed. 42 class File { 43 public: 44 virtual ~File(); 45 46 /// Kinds of files that are supported. 47 enum Kind { 48 kindErrorObject, ///< a error object file (.o) 49 kindNormalizedObject, ///< a normalized file (.o) 50 kindMachObject, ///< a MachO object file (.o) 51 kindCEntryObject, ///< a file for CEntries 52 kindHeaderObject, ///< a file for file headers 53 kindEntryObject, ///< a file for the entry 54 kindUndefinedSymsObject, ///< a file for undefined symbols 55 kindStubHelperObject, ///< a file for stub helpers 56 kindResolverMergedObject, ///< the resolver merged file. 57 kindSectCreateObject, ///< a sect create object file (.o) 58 kindSharedLibrary, ///< shared library (.so) 59 kindArchiveLibrary ///< archive (.a) 60 }; 61 62 /// Returns file kind. Need for dyn_cast<> on File objects. kind()63 Kind kind() const { 64 return _kind; 65 } 66 67 /// This returns the path to the file which was used to create this object 68 /// (e.g. "/tmp/foo.o"). If the file is a member of an archive file, the 69 /// returned string includes the archive file name. path()70 StringRef path() const { 71 if (_archivePath.empty()) 72 return _path; 73 if (_archiveMemberPath.empty()) 74 _archiveMemberPath = (_archivePath + "(" + _path + ")").str(); 75 return _archiveMemberPath; 76 } 77 78 /// Returns the path of the archive file name if this file is instantiated 79 /// from an archive file. Otherwise returns the empty string. archivePath()80 StringRef archivePath() const { return _archivePath; } setArchivePath(StringRef path)81 void setArchivePath(StringRef path) { _archivePath = std::string(path); } 82 83 /// Returns the path name of this file. It doesn't include archive file name. memberPath()84 StringRef memberPath() const { return _path; } 85 86 /// Returns the command line order of the file. ordinal()87 uint64_t ordinal() const { 88 assert(_ordinal != UINT64_MAX); 89 return _ordinal; 90 } 91 92 /// Returns true/false depending on whether an ordinal has been set. hasOrdinal()93 bool hasOrdinal() const { return (_ordinal != UINT64_MAX); } 94 95 /// Sets the command line order of the file. setOrdinal(uint64_t ordinal)96 void setOrdinal(uint64_t ordinal) const { _ordinal = ordinal; } 97 98 /// Returns the ordinal for the next atom to be defined in this file. getNextAtomOrdinalAndIncrement()99 uint64_t getNextAtomOrdinalAndIncrement() const { 100 return _nextAtomOrdinal++; 101 } 102 103 /// For allocating any objects owned by this File. allocator()104 llvm::BumpPtrAllocator &allocator() const { 105 return _allocator; 106 } 107 108 /// The type of atom mutable container. 109 template <typename T> using AtomVector = std::vector<OwningAtomPtr<T>>; 110 111 /// The range type for the atoms. 112 template <typename T> class AtomRange { 113 public: AtomRange(AtomVector<T> & v)114 AtomRange(AtomVector<T> &v) : _v(v) {} AtomRange(const AtomVector<T> & v)115 AtomRange(const AtomVector<T> &v) : _v(const_cast<AtomVector<T> &>(v)) {} 116 117 using ConstDerefFn = const T* (*)(const OwningAtomPtr<T>&); 118 using DerefFn = T* (*)(OwningAtomPtr<T>&); 119 120 typedef llvm::mapped_iterator<typename AtomVector<T>::const_iterator, 121 ConstDerefFn> ConstItTy; 122 typedef llvm::mapped_iterator<typename AtomVector<T>::iterator, 123 DerefFn> ItTy; 124 DerefConst(const OwningAtomPtr<T> & p)125 static const T* DerefConst(const OwningAtomPtr<T> &p) { 126 return p.get(); 127 } 128 Deref(OwningAtomPtr<T> & p)129 static T* Deref(OwningAtomPtr<T> &p) { 130 return p.get(); 131 } 132 begin()133 ConstItTy begin() const { 134 return ConstItTy(_v.begin(), ConstDerefFn(DerefConst)); 135 } end()136 ConstItTy end() const { 137 return ConstItTy(_v.end(), ConstDerefFn(DerefConst)); 138 } 139 begin()140 ItTy begin() { 141 return ItTy(_v.begin(), DerefFn(Deref)); 142 } end()143 ItTy end() { 144 return ItTy(_v.end(), DerefFn(Deref)); 145 } 146 owning_ptrs()147 llvm::iterator_range<typename AtomVector<T>::iterator> owning_ptrs() { 148 return llvm::make_range(_v.begin(), _v.end()); 149 } 150 owning_ptrs()151 llvm::iterator_range<typename AtomVector<T>::iterator> owning_ptrs() const { 152 return llvm::make_range(_v.begin(), _v.end()); 153 } 154 empty()155 bool empty() const { 156 return _v.empty(); 157 } 158 size()159 size_t size() const { 160 return _v.size(); 161 } 162 163 const OwningAtomPtr<T> &operator[](size_t idx) const { 164 return _v[idx]; 165 } 166 167 OwningAtomPtr<T> &operator[](size_t idx) { 168 return _v[idx]; 169 } 170 171 private: 172 AtomVector<T> &_v; 173 }; 174 175 /// Must be implemented to return the AtomVector object for 176 /// all DefinedAtoms in this File. 177 virtual const AtomRange<DefinedAtom> defined() const = 0; 178 179 /// Must be implemented to return the AtomVector object for 180 /// all UndefinedAtomw in this File. 181 virtual const AtomRange<UndefinedAtom> undefined() const = 0; 182 183 /// Must be implemented to return the AtomVector object for 184 /// all SharedLibraryAtoms in this File. 185 virtual const AtomRange<SharedLibraryAtom> sharedLibrary() const = 0; 186 187 /// Must be implemented to return the AtomVector object for 188 /// all AbsoluteAtoms in this File. 189 virtual const AtomRange<AbsoluteAtom> absolute() const = 0; 190 191 /// Drop all of the atoms owned by this file. This will result in all of 192 /// the atoms running their destructors. 193 /// This is required because atoms may be allocated on a BumpPtrAllocator 194 /// of a different file. We need to destruct all atoms before any files. 195 virtual void clearAtoms() = 0; 196 197 /// If a file is parsed using a different method than doParse(), 198 /// one must use this method to set the last error status, so that 199 /// doParse will not be called twice. Only YAML reader uses this 200 /// (because YAML reader does not read blobs but structured data). setLastError(std::error_code err)201 void setLastError(std::error_code err) { _lastError = err; } 202 203 std::error_code parse(); 204 205 // Usually each file owns a std::unique_ptr<MemoryBuffer>. 206 // However, there's one special case. If a file is an archive file, 207 // the archive file and its children all shares the same memory buffer. 208 // This method is used by the ArchiveFile to give its children 209 // co-ownership of the buffer. setSharedMemoryBuffer(std::shared_ptr<MemoryBuffer> mb)210 void setSharedMemoryBuffer(std::shared_ptr<MemoryBuffer> mb) { 211 _sharedMemoryBuffer = mb; 212 } 213 214 protected: 215 /// only subclasses of File can be instantiated File(StringRef p,Kind kind)216 File(StringRef p, Kind kind) 217 : _path(p), _kind(kind), _ordinal(UINT64_MAX), 218 _nextAtomOrdinal(0) {} 219 220 /// Subclasses should override this method to parse the 221 /// memory buffer passed to this file's constructor. doParse()222 virtual std::error_code doParse() { return std::error_code(); } 223 224 static AtomVector<DefinedAtom> _noDefinedAtoms; 225 static AtomVector<UndefinedAtom> _noUndefinedAtoms; 226 static AtomVector<SharedLibraryAtom> _noSharedLibraryAtoms; 227 static AtomVector<AbsoluteAtom> _noAbsoluteAtoms; 228 mutable llvm::BumpPtrAllocator _allocator; 229 230 private: 231 StringRef _path; 232 std::string _archivePath; 233 mutable std::string _archiveMemberPath; 234 Kind _kind; 235 mutable uint64_t _ordinal; 236 mutable uint64_t _nextAtomOrdinal; 237 std::shared_ptr<MemoryBuffer> _sharedMemoryBuffer; 238 llvm::Optional<std::error_code> _lastError; 239 std::mutex _parseMutex; 240 }; 241 242 /// An ErrorFile represents a file that doesn't exist. 243 /// If you try to parse a file which doesn't exist, an instance of this 244 /// class will be returned. That's parse method always returns an error. 245 /// This is useful to delay erroring on non-existent files, so that we 246 /// can do unit testing a driver using non-existing file paths. 247 class ErrorFile : public File { 248 public: ErrorFile(StringRef path,std::error_code ec)249 ErrorFile(StringRef path, std::error_code ec) 250 : File(path, kindErrorObject), _ec(ec) {} 251 doParse()252 std::error_code doParse() override { return _ec; } 253 defined()254 const AtomRange<DefinedAtom> defined() const override { 255 llvm_unreachable("internal error"); 256 } undefined()257 const AtomRange<UndefinedAtom> undefined() const override { 258 llvm_unreachable("internal error"); 259 } sharedLibrary()260 const AtomRange<SharedLibraryAtom> sharedLibrary() const override { 261 llvm_unreachable("internal error"); 262 } absolute()263 const AtomRange<AbsoluteAtom> absolute() const override { 264 llvm_unreachable("internal error"); 265 } 266 clearAtoms()267 void clearAtoms() override { 268 } 269 270 private: 271 std::error_code _ec; 272 }; 273 274 } // end namespace lld 275 276 #endif 277