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