1//===- lib/ReaderWriter/MachO/TLVPass.cpp -----------------------*- 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/// \file
10/// This linker pass transforms all TLV references to real references.
11///
12//===----------------------------------------------------------------------===//
13
14#include "ArchHandler.h"
15#include "File.h"
16#include "MachOPasses.h"
17#include "lld/Core/Simple.h"
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/Support/Debug.h"
20
21namespace lld {
22namespace mach_o {
23
24//
25// TLVP Entry Atom created by the TLV pass.
26//
27class TLVPEntryAtom : public SimpleDefinedAtom {
28public:
29  TLVPEntryAtom(const File &file, bool is64, StringRef name)
30      : SimpleDefinedAtom(file), _is64(is64), _name(name) {}
31
32  ~TLVPEntryAtom() override = default;
33
34  ContentType contentType() const override {
35    return DefinedAtom::typeTLVInitializerPtr;
36  }
37
38  Alignment alignment() const override {
39    return _is64 ? 8 : 4;
40  }
41
42  uint64_t size() const override {
43    return _is64 ? 8 : 4;
44  }
45
46  ContentPermissions permissions() const override {
47    return DefinedAtom::permRW_;
48  }
49
50  ArrayRef<uint8_t> rawContent() const override {
51    static const uint8_t zeros[] =
52      { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
53    return llvm::makeArrayRef(zeros, size());
54  }
55
56  StringRef slotName() const {
57    return _name;
58  }
59
60private:
61  const bool _is64;
62  StringRef _name;
63};
64
65class TLVPass : public Pass {
66public:
67  TLVPass(const MachOLinkingContext &context)
68      : _ctx(context), _archHandler(_ctx.archHandler()),
69        _file(*_ctx.make_file<MachOFile>("<mach-o TLV pass>")) {
70    _file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
71  }
72
73private:
74  llvm::Error perform(SimpleFile &mergedFile) override {
75    bool allowTLV = _ctx.minOS("10.7", "1.0");
76
77    for (const DefinedAtom *atom : mergedFile.defined()) {
78      for (const Reference *ref : *atom) {
79        if (!_archHandler.isTLVAccess(*ref))
80          continue;
81
82        if (!allowTLV)
83          return llvm::make_error<GenericError>(
84            "targeted OS version does not support use of thread local "
85            "variables in " + atom->name() + " for architecture " +
86            _ctx.archName());
87
88        const Atom *target = ref->target();
89        assert(target != nullptr);
90
91        const DefinedAtom *tlvpEntry = makeTLVPEntry(target);
92        const_cast<Reference*>(ref)->setTarget(tlvpEntry);
93        _archHandler.updateReferenceToTLV(ref);
94      }
95    }
96
97    std::vector<const TLVPEntryAtom*> entries;
98    entries.reserve(_targetToTLVP.size());
99    for (auto &it : _targetToTLVP)
100      entries.push_back(it.second);
101    std::sort(entries.begin(), entries.end(),
102              [](const TLVPEntryAtom *lhs, const TLVPEntryAtom *rhs) {
103                return (lhs->slotName().compare(rhs->slotName()) < 0);
104              });
105
106    for (const TLVPEntryAtom *slot : entries)
107      mergedFile.addAtom(*slot);
108
109    return llvm::Error::success();
110  }
111
112  const DefinedAtom *makeTLVPEntry(const Atom *target) {
113    auto pos = _targetToTLVP.find(target);
114
115    if (pos != _targetToTLVP.end())
116      return pos->second;
117
118    auto *tlvpEntry = new (_file.allocator())
119      TLVPEntryAtom(_file, _ctx.is64Bit(), target->name());
120    _targetToTLVP[target] = tlvpEntry;
121    const ArchHandler::ReferenceInfo &nlInfo =
122      _archHandler.stubInfo().nonLazyPointerReferenceToBinder;
123    tlvpEntry->addReference(Reference::KindNamespace::mach_o, nlInfo.arch,
124                            nlInfo.kind, 0, target, 0);
125    return tlvpEntry;
126  }
127
128  const MachOLinkingContext &_ctx;
129  mach_o::ArchHandler &_archHandler;
130  MachOFile           &_file;
131  llvm::DenseMap<const Atom*, const TLVPEntryAtom*> _targetToTLVP;
132};
133
134void addTLVPass(PassManager &pm, const MachOLinkingContext &ctx) {
135  assert(ctx.needsTLVPass());
136  pm.add(std::make_unique<TLVPass>(ctx));
137}
138
139} // end namesapce mach_o
140} // end namesapce lld
141