1 //===- Core/File.h - A Container of Atoms ---------------------------------===// 2 // 3 // The LLVM Linker 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #ifndef LLD_CORE_FILE_H 11 #define LLD_CORE_FILE_H 12 13 #include "lld/Core/AbsoluteAtom.h" 14 #include "lld/Core/DefinedAtom.h" 15 #include "lld/Core/SharedLibraryAtom.h" 16 #include "lld/Core/UndefinedAtom.h" 17 #include "llvm/ADT/Optional.h" 18 #include "llvm/ADT/STLExtras.h" 19 #include "llvm/ADT/Twine.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 /// \brief 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 /// \brief Returns file kind. Need for dyn_cast<> on File objects. 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. 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. 80 StringRef archivePath() const { return _archivePath; } 81 void setArchivePath(StringRef path) { _archivePath = path; } 82 83 /// Returns the path name of this file. It doesn't include archive file name. 84 StringRef memberPath() const { return _path; } 85 86 /// Returns the command line order of the file. 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. 93 bool hasOrdinal() const { return (_ordinal != UINT64_MAX); } 94 95 /// Sets the command line order of the file. 96 void setOrdinal(uint64_t ordinal) const { _ordinal = ordinal; } 97 98 /// Returns the ordinal for the next atom to be defined in this file. 99 uint64_t getNextAtomOrdinalAndIncrement() const { 100 return _nextAtomOrdinal++; 101 } 102 103 /// For allocating any objects owned by this File. 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: 114 AtomRange(AtomVector<T> &v) : _v(v) {} 115 AtomRange(const AtomVector<T> &v) : _v(const_cast<AtomVector<T> &>(v)) {} 116 117 typedef std::pointer_to_unary_function<const OwningAtomPtr<T>&, 118 const T*> ConstDerefFn; 119 120 typedef std::pointer_to_unary_function<OwningAtomPtr<T>&, T*> DerefFn; 121 122 typedef llvm::mapped_iterator<typename AtomVector<T>::const_iterator, 123 ConstDerefFn> ConstItTy; 124 typedef llvm::mapped_iterator<typename AtomVector<T>::iterator, 125 DerefFn> ItTy; 126 127 static const T* DerefConst(const OwningAtomPtr<T> &p) { 128 return p.get(); 129 } 130 131 static T* Deref(OwningAtomPtr<T> &p) { 132 return p.get(); 133 } 134 135 ConstItTy begin() const { 136 return ConstItTy(_v.begin(), ConstDerefFn(DerefConst)); 137 } 138 ConstItTy end() const { 139 return ConstItTy(_v.end(), ConstDerefFn(DerefConst)); 140 } 141 142 ItTy begin() { 143 return ItTy(_v.begin(), DerefFn(Deref)); 144 } 145 ItTy end() { 146 return ItTy(_v.end(), DerefFn(Deref)); 147 } 148 149 llvm::iterator_range<typename AtomVector<T>::iterator> owning_ptrs() { 150 return llvm::make_range(_v.begin(), _v.end()); 151 } 152 153 llvm::iterator_range<typename AtomVector<T>::iterator> owning_ptrs() const { 154 return llvm::make_range(_v.begin(), _v.end()); 155 } 156 157 bool empty() const { 158 return _v.empty(); 159 } 160 161 size_t size() const { 162 return _v.size(); 163 } 164 165 const OwningAtomPtr<T> &operator[](size_t idx) const { 166 return _v[idx]; 167 } 168 169 OwningAtomPtr<T> &operator[](size_t idx) { 170 return _v[idx]; 171 } 172 173 private: 174 AtomVector<T> &_v; 175 }; 176 177 /// \brief Must be implemented to return the AtomVector object for 178 /// all DefinedAtoms in this File. 179 virtual const AtomRange<DefinedAtom> defined() const = 0; 180 181 /// \brief Must be implemented to return the AtomVector object for 182 /// all UndefinedAtomw in this File. 183 virtual const AtomRange<UndefinedAtom> undefined() const = 0; 184 185 /// \brief Must be implemented to return the AtomVector object for 186 /// all SharedLibraryAtoms in this File. 187 virtual const AtomRange<SharedLibraryAtom> sharedLibrary() const = 0; 188 189 /// \brief Must be implemented to return the AtomVector object for 190 /// all AbsoluteAtoms in this File. 191 virtual const AtomRange<AbsoluteAtom> absolute() const = 0; 192 193 /// Drop all of the atoms owned by this file. This will result in all of 194 /// the atoms running their destructors. 195 /// This is required because atoms may be allocated on a BumpPtrAllocator 196 /// of a different file. We need to destruct all atoms before any files. 197 virtual void clearAtoms() = 0; 198 199 /// \brief If a file is parsed using a different method than doParse(), 200 /// one must use this method to set the last error status, so that 201 /// doParse will not be called twice. Only YAML reader uses this 202 /// (because YAML reader does not read blobs but structured data). 203 void setLastError(std::error_code err) { _lastError = err; } 204 205 std::error_code parse(); 206 207 // Usually each file owns a std::unique_ptr<MemoryBuffer>. 208 // However, there's one special case. If a file is an archive file, 209 // the archive file and its children all shares the same memory buffer. 210 // This method is used by the ArchiveFile to give its children 211 // co-ownership of the buffer. 212 void setSharedMemoryBuffer(std::shared_ptr<MemoryBuffer> mb) { 213 _sharedMemoryBuffer = mb; 214 } 215 216 protected: 217 /// \brief only subclasses of File can be instantiated 218 File(StringRef p, Kind kind) 219 : _path(p), _kind(kind), _ordinal(UINT64_MAX), 220 _nextAtomOrdinal(0) {} 221 222 /// \brief Subclasses should override this method to parse the 223 /// memory buffer passed to this file's constructor. 224 virtual std::error_code doParse() { return std::error_code(); } 225 226 static AtomVector<DefinedAtom> _noDefinedAtoms; 227 static AtomVector<UndefinedAtom> _noUndefinedAtoms; 228 static AtomVector<SharedLibraryAtom> _noSharedLibraryAtoms; 229 static AtomVector<AbsoluteAtom> _noAbsoluteAtoms; 230 mutable llvm::BumpPtrAllocator _allocator; 231 232 private: 233 StringRef _path; 234 std::string _archivePath; 235 mutable std::string _archiveMemberPath; 236 Kind _kind; 237 mutable uint64_t _ordinal; 238 mutable uint64_t _nextAtomOrdinal; 239 std::shared_ptr<MemoryBuffer> _sharedMemoryBuffer; 240 llvm::Optional<std::error_code> _lastError; 241 std::mutex _parseMutex; 242 }; 243 244 /// An ErrorFile represents a file that doesn't exist. 245 /// If you try to parse a file which doesn't exist, an instance of this 246 /// class will be returned. That's parse method always returns an error. 247 /// This is useful to delay erroring on non-existent files, so that we 248 /// can do unit testing a driver using non-existing file paths. 249 class ErrorFile : public File { 250 public: 251 ErrorFile(StringRef path, std::error_code ec) 252 : File(path, kindErrorObject), _ec(ec) {} 253 254 std::error_code doParse() override { return _ec; } 255 256 const AtomRange<DefinedAtom> defined() const override { 257 llvm_unreachable("internal error"); 258 } 259 const AtomRange<UndefinedAtom> undefined() const override { 260 llvm_unreachable("internal error"); 261 } 262 const AtomRange<SharedLibraryAtom> sharedLibrary() const override { 263 llvm_unreachable("internal error"); 264 } 265 const AtomRange<AbsoluteAtom> absolute() const override { 266 llvm_unreachable("internal error"); 267 } 268 269 void clearAtoms() override { 270 } 271 272 private: 273 std::error_code _ec; 274 }; 275 276 } // end namespace lld 277 278 #endif 279