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 
21 namespace lld {
22 namespace mach_o {
23 
24 //
25 // TLVP Entry Atom created by the TLV pass.
26 //
27 class TLVPEntryAtom : public SimpleDefinedAtom {
28 public:
TLVPEntryAtom(const File & file,bool is64,StringRef name)29   TLVPEntryAtom(const File &file, bool is64, StringRef name)
30       : SimpleDefinedAtom(file), _is64(is64), _name(name) {}
31 
32   ~TLVPEntryAtom() override = default;
33 
contentType() const34   ContentType contentType() const override {
35     return DefinedAtom::typeTLVInitializerPtr;
36   }
37 
alignment() const38   Alignment alignment() const override {
39     return _is64 ? 8 : 4;
40   }
41 
size() const42   uint64_t size() const override {
43     return _is64 ? 8 : 4;
44   }
45 
permissions() const46   ContentPermissions permissions() const override {
47     return DefinedAtom::permRW_;
48   }
49 
rawContent() const50   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 
slotName() const56   StringRef slotName() const {
57     return _name;
58   }
59 
60 private:
61   const bool _is64;
62   StringRef _name;
63 };
64 
65 class TLVPass : public Pass {
66 public:
TLVPass(const MachOLinkingContext & context)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 
73 private:
perform(SimpleFile & mergedFile)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 
makeTLVPEntry(const Atom * target)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 
addTLVPass(PassManager & pm,const MachOLinkingContext & ctx)134 void addTLVPass(PassManager &pm, const MachOLinkingContext &ctx) {
135   assert(ctx.needsTLVPass());
136   pm.add(std::make_unique<TLVPass>(ctx));
137 }
138 
139 } // end namespace mach_o
140 } // end namespace lld
141