Coverage Report

Created: 2019-07-24 05:18

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/lld/lib/ReaderWriter/MachO/File.h
Line
Count
Source (jump to first uncovered line)
1
//===- lib/ReaderWriter/MachO/File.h ----------------------------*- C++ -*-===//
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_READER_WRITER_MACHO_FILE_H
10
#define LLD_READER_WRITER_MACHO_FILE_H
11
12
#include "Atoms.h"
13
#include "DebugInfo.h"
14
#include "MachONormalizedFile.h"
15
#include "lld/Core/SharedLibraryFile.h"
16
#include "lld/Core/Simple.h"
17
#include "llvm/ADT/DenseMap.h"
18
#include "llvm/ADT/StringMap.h"
19
#include "llvm/Support/Format.h"
20
#include <unordered_map>
21
22
namespace lld {
23
namespace mach_o {
24
25
using lld::mach_o::normalized::Section;
26
27
class MachOFile : public SimpleFile {
28
public:
29
30
  /// Real file constructor - for on-disk files.
31
  MachOFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx)
32
    : SimpleFile(mb->getBufferIdentifier(), File::kindMachObject),
33
27
      _mb(std::move(mb)), _ctx(ctx) {}
34
35
  /// Dummy file constructor - for virtual files.
36
  MachOFile(StringRef path)
37
530
    : SimpleFile(path, File::kindMachObject) {}
38
39
  void addDefinedAtom(StringRef name, Atom::Scope scope,
40
                      DefinedAtom::ContentType type, DefinedAtom::Merge merge,
41
                      uint64_t sectionOffset, uint64_t contentSize, bool thumb,
42
                      bool noDeadStrip, bool copyRefs,
43
469
                      const Section *inSection) {
44
469
    assert(sectionOffset+contentSize <= inSection->content.size());
45
469
    ArrayRef<uint8_t> content = inSection->content.slice(sectionOffset,
46
469
                                                        contentSize);
47
469
    if (copyRefs) {
48
364
      // Make a copy of the atom's name and content that is owned by this file.
49
364
      name = name.copy(allocator());
50
364
      content = content.copy(allocator());
51
364
    }
52
469
    DefinedAtom::Alignment align(
53
469
        inSection->alignment,
54
469
        sectionOffset % inSection->alignment);
55
469
    auto *atom =
56
469
        new (allocator()) MachODefinedAtom(*this, name, scope, type, merge,
57
469
                                           thumb, noDeadStrip, content, align);
58
469
    addAtomForSection(inSection, atom, sectionOffset);
59
469
  }
60
61
  void addDefinedAtomInCustomSection(StringRef name, Atom::Scope scope,
62
                      DefinedAtom::ContentType type, DefinedAtom::Merge merge,
63
                      bool thumb, bool noDeadStrip, uint64_t sectionOffset,
64
                      uint64_t contentSize, StringRef sectionName,
65
23
                      bool copyRefs, const Section *inSection) {
66
23
    assert(sectionOffset+contentSize <= inSection->content.size());
67
23
    ArrayRef<uint8_t> content = inSection->content.slice(sectionOffset,
68
23
                                                        contentSize);
69
23
   if (copyRefs) {
70
23
      // Make a copy of the atom's name and content that is owned by this file.
71
23
      name = name.copy(allocator());
72
23
      content = content.copy(allocator());
73
23
      sectionName = sectionName.copy(allocator());
74
23
    }
75
23
    DefinedAtom::Alignment align(
76
23
        inSection->alignment,
77
23
        sectionOffset % inSection->alignment);
78
23
    auto *atom =
79
23
        new (allocator()) MachODefinedCustomSectionAtom(*this, name, scope, type,
80
23
                                                        merge, thumb,
81
23
                                                        noDeadStrip, content,
82
23
                                                        sectionName, align);
83
23
    addAtomForSection(inSection, atom, sectionOffset);
84
23
  }
85
86
  void addZeroFillDefinedAtom(StringRef name, Atom::Scope scope,
87
                              uint64_t sectionOffset, uint64_t size,
88
                              bool noDeadStrip, bool copyRefs,
89
3
                              const Section *inSection) {
90
3
    if (copyRefs) {
91
3
      // Make a copy of the atom's name and content that is owned by this file.
92
3
      name = name.copy(allocator());
93
3
    }
94
3
    DefinedAtom::Alignment align(
95
3
        inSection->alignment,
96
3
        sectionOffset % inSection->alignment);
97
3
98
3
    DefinedAtom::ContentType type = DefinedAtom::typeUnknown;
99
3
    switch (inSection->type) {
100
3
    case llvm::MachO::S_ZEROFILL:
101
1
      type = DefinedAtom::typeZeroFill;
102
1
      break;
103
3
    case llvm::MachO::S_THREAD_LOCAL_ZEROFILL:
104
2
      type = DefinedAtom::typeTLVInitialZeroFill;
105
2
      break;
106
3
    default:
107
0
      llvm_unreachable("Unrecognized zero-fill section");
108
3
    }
109
3
110
3
    auto *atom =
111
3
        new (allocator()) MachODefinedAtom(*this, name, scope, type, size,
112
3
                                           noDeadStrip, align);
113
3
    addAtomForSection(inSection, atom, sectionOffset);
114
3
  }
115
116
110
  void addUndefinedAtom(StringRef name, bool copyRefs) {
117
110
    if (copyRefs) {
118
94
      // Make a copy of the atom's name that is owned by this file.
119
94
      name = name.copy(allocator());
120
94
    }
121
110
    auto *atom = new (allocator()) SimpleUndefinedAtom(*this, name);
122
110
    addAtom(*atom);
123
110
    _undefAtoms[name] = atom;
124
110
  }
125
126
  void addTentativeDefAtom(StringRef name, Atom::Scope scope, uint64_t size,
127
5
                           DefinedAtom::Alignment align, bool copyRefs) {
128
5
    if (copyRefs) {
129
5
      // Make a copy of the atom's name that is owned by this file.
130
5
      name = name.copy(allocator());
131
5
    }
132
5
    auto *atom =
133
5
        new (allocator()) MachOTentativeDefAtom(*this, name, scope, size, align);
134
5
    addAtom(*atom);
135
5
    _undefAtoms[name] = atom;
136
5
  }
137
138
  /// Search this file for an the atom from 'section' that covers
139
  /// 'offsetInSect'.  Returns nullptr is no atom found.
140
  MachODefinedAtom *findAtomCoveringAddress(const Section &section,
141
                                            uint64_t offsetInSect,
142
929
                                            uint32_t *foundOffsetAtom=nullptr) {
143
929
    const auto &pos = _sectionAtoms.find(&section);
144
929
    if (pos == _sectionAtoms.end())
145
0
      return nullptr;
146
929
    const auto &vec = pos->second;
147
929
    assert(offsetInSect < section.content.size());
148
929
    // Vector of atoms for section are already sorted, so do binary search.
149
929
    const auto &atomPos = std::lower_bound(vec.begin(), vec.end(), offsetInSect,
150
929
        [offsetInSect](const SectionOffsetAndAtom &ao,
151
1.54k
                       uint64_t targetAddr) -> bool {
152
1.54k
          // Each atom has a start offset of its slice of the
153
1.54k
          // section's content. This compare function must return true
154
1.54k
          // iff the atom's range is before the offset being searched for.
155
1.54k
          uint64_t atomsEndOffset = ao.offset+ao.atom->rawContent().size();
156
1.54k
          return (atomsEndOffset <= offsetInSect);
157
1.54k
        });
158
929
    if (atomPos == vec.end())
159
0
      return nullptr;
160
929
    if (foundOffsetAtom)
161
827
      *foundOffsetAtom = offsetInSect - atomPos->offset;
162
929
    return atomPos->atom;
163
929
  }
164
165
  /// Searches this file for an UndefinedAtom named 'name'. Returns
166
  /// nullptr is no such atom found.
167
189
  const lld::Atom *findUndefAtom(StringRef name) {
168
189
    auto pos = _undefAtoms.find(name);
169
189
    if (pos == _undefAtoms.end())
170
0
      return nullptr;
171
189
    return pos->second;
172
189
  }
173
174
  typedef std::function<void (MachODefinedAtom* atom)> DefinedAtomVisitor;
175
176
177
  void eachDefinedAtom(DefinedAtomVisitor vistor) {
177
295
    for (auto &sectAndAtoms : _sectionAtoms) {
178
491
      for (auto &offAndAtom : sectAndAtoms.second) {
179
491
        vistor(offAndAtom.atom);
180
491
      }
181
295
    }
182
177
  }
183
184
  typedef std::function<void(MachODefinedAtom *atom, uint64_t offset)>
185
      SectionAtomVisitor;
186
187
40
  void eachAtomInSection(const Section &section, SectionAtomVisitor visitor) {
188
40
    auto pos = _sectionAtoms.find(&section);
189
40
    if (pos == _sectionAtoms.end())
190
0
      return;
191
40
    auto vec = pos->second;
192
40
193
40
    for (auto &offAndAtom : vec)
194
75
      visitor(offAndAtom.atom, offAndAtom.offset);
195
40
  }
196
197
346
  MachOLinkingContext::Arch arch() const { return _arch; }
198
177
  void setArch(MachOLinkingContext::Arch arch) { _arch = arch; }
199
200
175
  MachOLinkingContext::OS OS() const { return _os; }
201
177
  void setOS(MachOLinkingContext::OS os) { _os = os; }
202
203
172
  MachOLinkingContext::ObjCConstraint objcConstraint() const {
204
172
    return _objcConstraint;
205
172
  }
206
6
  void setObjcConstraint(MachOLinkingContext::ObjCConstraint v) {
207
6
    _objcConstraint = v;
208
6
  }
209
210
596
  uint32_t minVersion() const { return _minVersion; }
211
177
  void setMinVersion(uint32_t v) { _minVersion = v; }
212
213
585
  LoadCommandType minVersionLoadCommandKind() const {
214
585
    return _minVersionLoadCommandKind;
215
585
  }
216
177
  void setMinVersionLoadCommandKind(LoadCommandType v) {
217
177
    _minVersionLoadCommandKind = v;
218
177
  }
219
220
171
  uint32_t swiftVersion() const { return _swiftVersion; }
221
6
  void setSwiftVersion(uint32_t v) { _swiftVersion = v; }
222
223
596
  bool subsectionsViaSymbols() const {
224
596
    return _flags & llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS;
225
596
  }
226
177
  void setFlags(normalized::FileFlags v) { _flags = v; }
227
228
  /// Methods for support type inquiry through isa, cast, and dyn_cast:
229
2.15k
  static inline bool classof(const File *F) {
230
2.15k
    return F->kind() == File::kindMachObject;
231
2.15k
  }
232
233
1
  void setDebugInfo(std::unique_ptr<DebugInfo> debugInfo) {
234
1
    _debugInfo = std::move(debugInfo);
235
1
  }
236
237
543
  DebugInfo* debugInfo() const { return _debugInfo.get(); }
238
0
  std::unique_ptr<DebugInfo> takeDebugInfo() { return std::move(_debugInfo); }
239
240
protected:
241
27
  std::error_code doParse() override {
242
27
    // Convert binary file to normalized mach-o.
243
27
    auto normFile = normalized::readBinary(_mb, _ctx->arch());
244
27
    if (auto ec = normFile.takeError())
245
0
      return llvm::errorToErrorCode(std::move(ec));
246
27
    // Convert normalized mach-o to atoms.
247
27
    if (auto ec = normalized::normalizedObjectToAtoms(this, **normFile, false))
248
0
      return llvm::errorToErrorCode(std::move(ec));
249
27
    return std::error_code();
250
27
  }
251
252
private:
253
  struct SectionOffsetAndAtom { uint64_t offset;  MachODefinedAtom *atom; };
254
255
  void addAtomForSection(const Section *inSection, MachODefinedAtom* atom,
256
495
                         uint64_t sectionOffset) {
257
495
    SectionOffsetAndAtom offAndAtom;
258
495
    offAndAtom.offset = sectionOffset;
259
495
    offAndAtom.atom   = atom;
260
495
     _sectionAtoms[inSection].push_back(offAndAtom);
261
495
    addAtom(*atom);
262
495
  }
263
264
  typedef llvm::DenseMap<const normalized::Section *,
265
                         std::vector<SectionOffsetAndAtom>>  SectionToAtoms;
266
  typedef llvm::StringMap<const lld::Atom *> NameToAtom;
267
268
  std::unique_ptr<MemoryBuffer> _mb;
269
  MachOLinkingContext          *_ctx;
270
  SectionToAtoms                _sectionAtoms;
271
  NameToAtom                     _undefAtoms;
272
  MachOLinkingContext::Arch      _arch = MachOLinkingContext::arch_unknown;
273
  MachOLinkingContext::OS        _os = MachOLinkingContext::OS::unknown;
274
  uint32_t                       _minVersion = 0;
275
  LoadCommandType               _minVersionLoadCommandKind = (LoadCommandType)0;
276
  MachOLinkingContext::ObjCConstraint _objcConstraint =
277
      MachOLinkingContext::objc_unknown;
278
  uint32_t                       _swiftVersion = 0;
279
  normalized::FileFlags        _flags = llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS;
280
  std::unique_ptr<DebugInfo>   _debugInfo;
281
};
282
283
class MachODylibFile : public SharedLibraryFile {
284
public:
285
  MachODylibFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx)
286
      : SharedLibraryFile(mb->getBufferIdentifier()),
287
4
        _mb(std::move(mb)), _ctx(ctx) {}
288
289
113
  MachODylibFile(StringRef path) : SharedLibraryFile(path) {}
290
291
160
  OwningAtomPtr<SharedLibraryAtom> exports(StringRef name) const override {
292
160
    // Pass down _installName so that if this requested symbol
293
160
    // is re-exported through this dylib, the SharedLibraryAtom's loadName()
294
160
    // is this dylib installName and not the implementation dylib's.
295
160
    // NOTE: isData is not needed for dylibs (it matters for static libs).
296
160
    return exports(name, _installName);
297
160
  }
298
299
  /// Adds symbol name that this dylib exports. The corresponding
300
  /// SharedLibraryAtom is created lazily (since most symbols are not used).
301
162
  void addExportedSymbol(StringRef name, bool weakDef, bool copyRefs) {
302
162
    if (copyRefs) {
303
162
      name = name.copy(allocator());
304
162
    }
305
162
    AtomAndFlags info(weakDef);
306
162
    _nameToAtom[name] = info;
307
162
  }
308
309
1
  void addReExportedDylib(StringRef dylibPath) {
310
1
    _reExportedDylibs.emplace_back(dylibPath);
311
1
  }
312
313
570
  StringRef installName() const { return _installName; }
314
111
  uint32_t currentVersion() { return _currentVersion; }
315
111
  uint32_t compatVersion() { return _compatVersion; }
316
317
117
  void setInstallName(StringRef name) { _installName = name; }
318
117
  void setCompatVersion(uint32_t version) { _compatVersion = version; }
319
117
  void setCurrentVersion(uint32_t version) { _currentVersion = version; }
320
321
  typedef std::function<MachODylibFile *(StringRef)> FindDylib;
322
323
117
  void loadReExportedDylibs(FindDylib find) {
324
117
    for (ReExportedDylib &entry : _reExportedDylibs) {
325
1
      entry.file = find(entry.path);
326
1
    }
327
117
  }
328
329
0
  StringRef getDSOName() const override { return _installName; }
330
331
4
  std::error_code doParse() override {
332
4
    // Convert binary file to normalized mach-o.
333
4
    auto normFile = normalized::readBinary(_mb, _ctx->arch());
334
4
    if (auto ec = normFile.takeError())
335
0
      return llvm::errorToErrorCode(std::move(ec));
336
4
    // Convert normalized mach-o to atoms.
337
4
    if (auto ec = normalized::normalizedDylibToAtoms(this, **normFile, false))
338
0
      return llvm::errorToErrorCode(std::move(ec));
339
4
    return std::error_code();
340
4
  }
341
342
private:
343
  OwningAtomPtr<SharedLibraryAtom> exports(StringRef name,
344
162
                                   StringRef installName) const {
345
162
    // First, check if requested symbol is directly implemented by this dylib.
346
162
    auto entry = _nameToAtom.find(name);
347
162
    if (entry != _nameToAtom.end()) {
348
133
      // FIXME: Make this map a set and only used in assert builds.
349
133
      // Note, its safe to assert here as the resolver is the only client of
350
133
      // this API and it only requests exports for undefined symbols.
351
133
      // If we return from here we are no longer undefined so we should never
352
133
      // get here again.
353
133
      assert(!entry->second.atom && "Duplicate shared library export");
354
133
      bool weakDef = entry->second.weakDef;
355
133
      auto *atom = new (allocator()) MachOSharedLibraryAtom(*this, name,
356
133
                                                            installName,
357
133
                                                            weakDef);
358
133
      entry->second.atom = atom;
359
133
      return atom;
360
133
    }
361
29
362
29
    // Next, check if symbol is implemented in some re-exported dylib.
363
29
    for (const ReExportedDylib &dylib : _reExportedDylibs) {
364
2
      assert(dylib.file);
365
2
      auto atom = dylib.file->exports(name, installName);
366
2
      if (atom.get())
367
1
        return atom;
368
2
    }
369
29
370
29
    // Symbol not exported or re-exported by this dylib.
371
29
    
return nullptr28
;
372
29
  }
373
374
  struct ReExportedDylib {
375
1
    ReExportedDylib(StringRef p) : path(p), file(nullptr) { }
376
    StringRef       path;
377
    MachODylibFile *file;
378
  };
379
380
  struct AtomAndFlags {
381
162
    AtomAndFlags() : atom(nullptr), weakDef(false) { }
382
162
    AtomAndFlags(bool weak) : atom(nullptr), weakDef(weak) { }
383
    const SharedLibraryAtom  *atom;
384
    bool                      weakDef;
385
  };
386
387
  std::unique_ptr<MemoryBuffer>              _mb;
388
  MachOLinkingContext                       *_ctx;
389
  StringRef                                  _installName;
390
  uint32_t                                   _currentVersion;
391
  uint32_t                                   _compatVersion;
392
  std::vector<ReExportedDylib>               _reExportedDylibs;
393
  mutable std::unordered_map<StringRef, AtomAndFlags> _nameToAtom;
394
};
395
396
} // end namespace mach_o
397
} // end namespace lld
398
399
#endif // LLD_READER_WRITER_MACHO_FILE_H