]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/ReaderWriter/MachO/File.h
Vendor import of lld trunk r233088:
[FreeBSD/FreeBSD.git] / lib / ReaderWriter / MachO / File.h
1 //===- lib/ReaderWriter/MachO/File.h --------------------------------------===//
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_READER_WRITER_MACHO_FILE_H
11 #define LLD_READER_WRITER_MACHO_FILE_H
12
13 #include "Atoms.h"
14 #include "MachONormalizedFile.h"
15 #include "lld/Core/SharedLibraryFile.h"
16 #include "lld/Core/Simple.h"
17 #include "llvm/ADT/StringMap.h"
18 #include <unordered_map>
19
20 namespace lld {
21 namespace mach_o {
22
23 using lld::mach_o::normalized::Section;
24
25 class MachOFile : public SimpleFile {
26 public:
27   MachOFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx)
28       : SimpleFile(mb->getBufferIdentifier()), _mb(std::move(mb)), _ctx(ctx) {}
29
30   MachOFile(StringRef path) : SimpleFile(path) {}
31
32   void addDefinedAtom(StringRef name, Atom::Scope scope,
33                       DefinedAtom::ContentType type, DefinedAtom::Merge merge,
34                       uint64_t sectionOffset, uint64_t contentSize, bool thumb,
35                       bool noDeadStrip, bool copyRefs,
36                       const Section *inSection) {
37     assert(sectionOffset+contentSize <= inSection->content.size());
38     ArrayRef<uint8_t> content = inSection->content.slice(sectionOffset,
39                                                         contentSize);
40     if (copyRefs) {
41       // Make a copy of the atom's name and content that is owned by this file.
42       name = name.copy(allocator());
43       content = content.copy(allocator());
44     }
45     DefinedAtom::Alignment align(
46         inSection->alignment,
47         sectionOffset % ((uint64_t)1 << inSection->alignment));
48     MachODefinedAtom *atom =
49         new (allocator()) MachODefinedAtom(*this, name, scope, type, merge,
50                                            thumb, noDeadStrip, content, align);
51     addAtomForSection(inSection, atom, sectionOffset);
52   }
53
54   void addDefinedAtomInCustomSection(StringRef name, Atom::Scope scope,
55                       DefinedAtom::ContentType type, DefinedAtom::Merge merge,
56                       bool thumb, bool noDeadStrip, uint64_t sectionOffset,
57                       uint64_t contentSize, StringRef sectionName,
58                       bool copyRefs, const Section *inSection) {
59     assert(sectionOffset+contentSize <= inSection->content.size());
60     ArrayRef<uint8_t> content = inSection->content.slice(sectionOffset,
61                                                         contentSize);
62    if (copyRefs) {
63       // Make a copy of the atom's name and content that is owned by this file.
64       name = name.copy(allocator());
65       content = content.copy(allocator());
66       sectionName = sectionName.copy(allocator());
67     }
68     DefinedAtom::Alignment align(
69         inSection->alignment,
70         sectionOffset % ((uint64_t)1 << inSection->alignment));
71     MachODefinedCustomSectionAtom *atom =
72         new (allocator()) MachODefinedCustomSectionAtom(*this, name, scope, type,
73                                                         merge, thumb,
74                                                         noDeadStrip, content,
75                                                         sectionName, align);
76     addAtomForSection(inSection, atom, sectionOffset);
77   }
78
79   void addZeroFillDefinedAtom(StringRef name, Atom::Scope scope,
80                               uint64_t sectionOffset, uint64_t size,
81                               bool noDeadStrip, bool copyRefs,
82                               const Section *inSection) {
83     if (copyRefs) {
84       // Make a copy of the atom's name and content that is owned by this file.
85       name = name.copy(allocator());
86     }
87     DefinedAtom::Alignment align(
88         inSection->alignment,
89         sectionOffset % ((uint64_t)1 << inSection->alignment));
90     MachODefinedAtom *atom =
91        new (allocator()) MachODefinedAtom(*this, name, scope, size, noDeadStrip,
92                                           align);
93     addAtomForSection(inSection, atom, sectionOffset);
94   }
95
96   void addUndefinedAtom(StringRef name, bool copyRefs) {
97     if (copyRefs) {
98       // Make a copy of the atom's name that is owned by this file.
99       name = name.copy(allocator());
100     }
101     SimpleUndefinedAtom *atom =
102         new (allocator()) SimpleUndefinedAtom(*this, name);
103     addAtom(*atom);
104     _undefAtoms[name] = atom;
105   }
106
107   void addTentativeDefAtom(StringRef name, Atom::Scope scope, uint64_t size,
108                            DefinedAtom::Alignment align, bool copyRefs) {
109     if (copyRefs) {
110       // Make a copy of the atom's name that is owned by this file.
111       name = name.copy(allocator());
112     }
113     MachOTentativeDefAtom *atom =
114         new (allocator()) MachOTentativeDefAtom(*this, name, scope, size, align);
115     addAtom(*atom);
116     _undefAtoms[name] = atom;
117   }
118
119   /// Search this file for an the atom from 'section' that covers
120   /// 'offsetInSect'.  Returns nullptr is no atom found.
121   MachODefinedAtom *findAtomCoveringAddress(const Section &section,
122                                             uint64_t offsetInSect,
123                                             uint32_t *foundOffsetAtom=nullptr) {
124     const auto &pos = _sectionAtoms.find(&section);
125     if (pos == _sectionAtoms.end())
126       return nullptr;
127     const auto &vec = pos->second;
128     assert(offsetInSect < section.content.size());
129     // Vector of atoms for section are already sorted, so do binary search.
130     const auto &atomPos = std::lower_bound(vec.begin(), vec.end(), offsetInSect,
131         [offsetInSect](const SectionOffsetAndAtom &ao,
132                        uint64_t targetAddr) -> bool {
133           // Each atom has a start offset of its slice of the
134           // section's content. This compare function must return true
135           // iff the atom's range is before the offset being searched for.
136           uint64_t atomsEndOffset = ao.offset+ao.atom->rawContent().size();
137           return (atomsEndOffset <= offsetInSect);
138         });
139     if (atomPos == vec.end())
140       return nullptr;
141     if (foundOffsetAtom)
142       *foundOffsetAtom = offsetInSect - atomPos->offset;
143     return atomPos->atom;
144   }
145
146   /// Searches this file for an UndefinedAtom named 'name'. Returns
147   /// nullptr is no such atom found.
148   const lld::Atom *findUndefAtom(StringRef name) {
149     auto pos = _undefAtoms.find(name);
150     if (pos == _undefAtoms.end())
151       return nullptr;
152     return pos->second;
153   }
154
155   typedef std::function<void (MachODefinedAtom* atom)> DefinedAtomVisitor;
156
157   void eachDefinedAtom(DefinedAtomVisitor vistor) {
158     for (auto &sectAndAtoms : _sectionAtoms) {
159       for (auto &offAndAtom : sectAndAtoms.second) {
160         vistor(offAndAtom.atom);
161       }
162     }
163   }
164
165   typedef std::function<void(MachODefinedAtom *atom, uint64_t offset)>
166       SectionAtomVisitor;
167
168   void eachAtomInSection(const Section &section, SectionAtomVisitor visitor) {
169     auto pos = _sectionAtoms.find(&section);
170     if (pos == _sectionAtoms.end())
171       return;
172     auto vec = pos->second;
173
174     for (auto &offAndAtom : vec)
175       visitor(offAndAtom.atom, offAndAtom.offset);
176   }
177
178 protected:
179   std::error_code doParse() override {
180     // Convert binary file to normalized mach-o.
181     auto normFile = normalized::readBinary(_mb, _ctx->arch());
182     if (std::error_code ec = normFile.getError())
183       return ec;
184     // Convert normalized mach-o to atoms.
185     if (std::error_code ec = normalized::normalizedObjectToAtoms(
186             this, **normFile, false))
187       return ec;
188     return std::error_code();
189   }
190
191 private:
192   struct SectionOffsetAndAtom { uint64_t offset;  MachODefinedAtom *atom; };
193
194   void addAtomForSection(const Section *inSection, MachODefinedAtom* atom,
195                          uint64_t sectionOffset) {
196     SectionOffsetAndAtom offAndAtom;
197     offAndAtom.offset = sectionOffset;
198     offAndAtom.atom   = atom;
199      _sectionAtoms[inSection].push_back(offAndAtom);
200     addAtom(*atom);
201   }
202
203
204   typedef llvm::DenseMap<const normalized::Section *,
205                          std::vector<SectionOffsetAndAtom>>  SectionToAtoms;
206   typedef llvm::StringMap<const lld::Atom *> NameToAtom;
207
208   std::unique_ptr<MemoryBuffer> _mb;
209   MachOLinkingContext          *_ctx;
210   SectionToAtoms                _sectionAtoms;
211   NameToAtom                     _undefAtoms;
212 };
213
214 class MachODylibFile : public SharedLibraryFile {
215 public:
216   MachODylibFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx)
217       : SharedLibraryFile(mb->getBufferIdentifier()),
218         _mb(std::move(mb)), _ctx(ctx) {}
219
220   MachODylibFile(StringRef path) : SharedLibraryFile(path) {}
221
222   const SharedLibraryAtom *exports(StringRef name, bool isData) const override {
223     // Pass down _installName so that if this requested symbol
224     // is re-exported through this dylib, the SharedLibraryAtom's loadName()
225     // is this dylib installName and not the implementation dylib's.
226     // NOTE: isData is not needed for dylibs (it matters for static libs).
227     return exports(name, _installName);
228   }
229
230   /// Adds symbol name that this dylib exports. The corresponding
231   /// SharedLibraryAtom is created lazily (since most symbols are not used).
232   void addExportedSymbol(StringRef name, bool weakDef, bool copyRefs) {
233     if (copyRefs) {
234       name = name.copy(allocator());
235     }
236     AtomAndFlags info(weakDef);
237     _nameToAtom[name] = info;
238   }
239
240   void addReExportedDylib(StringRef dylibPath) {
241     _reExportedDylibs.emplace_back(dylibPath);
242   }
243
244   StringRef installName() { return _installName; }
245   uint32_t currentVersion() { return _currentVersion; }
246   uint32_t compatVersion() { return _compatVersion; }
247
248   void setInstallName(StringRef name) { _installName = name; }
249   void setCompatVersion(uint32_t version) { _compatVersion = version; }
250   void setCurrentVersion(uint32_t version) { _currentVersion = version; }
251
252   typedef std::function<MachODylibFile *(StringRef)> FindDylib;
253
254   void loadReExportedDylibs(FindDylib find) {
255     for (ReExportedDylib &entry : _reExportedDylibs) {
256       entry.file = find(entry.path);
257     }
258   }
259
260   StringRef getDSOName() const override { return _installName; }
261
262   std::error_code doParse() override {
263     // Convert binary file to normalized mach-o.
264     auto normFile = normalized::readBinary(_mb, _ctx->arch());
265     if (std::error_code ec = normFile.getError())
266       return ec;
267     // Convert normalized mach-o to atoms.
268     if (std::error_code ec = normalized::normalizedDylibToAtoms(
269             this, **normFile, false))
270       return ec;
271     return std::error_code();
272   }
273
274 private:
275   const SharedLibraryAtom *exports(StringRef name,
276                                    StringRef installName) const {
277     // First, check if requested symbol is directly implemented by this dylib.
278     auto entry = _nameToAtom.find(name);
279     if (entry != _nameToAtom.end()) {
280       if (!entry->second.atom) {
281         // Lazily create SharedLibraryAtom.
282         entry->second.atom =
283           new (allocator()) MachOSharedLibraryAtom(*this, name, installName,
284                                                    entry->second.weakDef);
285       }
286       return entry->second.atom;
287     }
288
289     // Next, check if symbol is implemented in some re-exported dylib.
290     for (const ReExportedDylib &dylib : _reExportedDylibs) {
291       assert(dylib.file);
292       auto atom = dylib.file->exports(name, installName);
293       if (atom)
294         return atom;
295     }
296
297     // Symbol not exported or re-exported by this dylib.
298     return nullptr;
299   }
300
301
302   struct ReExportedDylib {
303     ReExportedDylib(StringRef p) : path(p), file(nullptr) { }
304     StringRef       path;
305     MachODylibFile *file;
306   };
307
308   struct AtomAndFlags {
309     AtomAndFlags() : atom(nullptr), weakDef(false) { }
310     AtomAndFlags(bool weak) : atom(nullptr), weakDef(weak) { }
311     const SharedLibraryAtom  *atom;
312     bool                      weakDef;
313   };
314
315   std::unique_ptr<MemoryBuffer>              _mb;
316   MachOLinkingContext                       *_ctx;
317   StringRef                                  _installName;
318   uint32_t                                   _currentVersion;
319   uint32_t                                   _compatVersion;
320   std::vector<ReExportedDylib>               _reExportedDylibs;
321   mutable std::unordered_map<StringRef, AtomAndFlags> _nameToAtom;
322 };
323
324 } // end namespace mach_o
325 } // end namespace lld
326
327 #endif