1 //===- lib/ReaderWriter/MachO/GOTPass.cpp -----------------------*- C++ -*-===// 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 /// \file 11 /// This linker pass transforms all GOT kind references to real references. 12 /// That is, in assembly you can write something like: 13 /// movq foo@GOTPCREL(%rip), %rax 14 /// which means you want to load a pointer to "foo" out of the GOT (global 15 /// Offsets Table). In the object file, the Atom containing this instruction 16 /// has a Reference whose target is an Atom named "foo" and the Reference 17 /// kind is a GOT load. The linker needs to instantiate a pointer sized 18 /// GOT entry. This is done be creating a GOT Atom to represent that pointer 19 /// sized data in this pass, and altering the Atom graph so the Reference now 20 /// points to the GOT Atom entry (corresponding to "foo") and changing the 21 /// Reference Kind to reflect it is now pointing to a GOT entry (rather 22 /// then needing a GOT entry). 23 /// 24 /// There is one optimization the linker can do here. If the target of the GOT 25 /// is in the same linkage unit and does not need to be interposable, and 26 /// the GOT use is just a load (not some other operation), this pass can 27 /// transform that load into an LEA (add). This optimizes away one memory load 28 /// which at runtime that could stall the pipeline. This optimization only 29 /// works for architectures in which a (GOT) load instruction can be change to 30 /// an LEA instruction that is the same size. The method isGOTAccess() should 31 /// only return true for "canBypassGOT" if this optimization is supported. 32 /// 33 //===----------------------------------------------------------------------===// 34 35 #include "ArchHandler.h" 36 #include "File.h" 37 #include "MachOPasses.h" 38 #include "lld/Common/LLVM.h" 39 #include "lld/Core/DefinedAtom.h" 40 #include "lld/Core/File.h" 41 #include "lld/Core/Reference.h" 42 #include "lld/Core/Simple.h" 43 #include "llvm/ADT/DenseMap.h" 44 #include "llvm/ADT/STLExtras.h" 45 46 namespace lld { 47 namespace mach_o { 48 49 // 50 // GOT Entry Atom created by the GOT pass. 51 // 52 class GOTEntryAtom : public SimpleDefinedAtom { 53 public: 54 GOTEntryAtom(const File &file, bool is64, StringRef name) 55 : SimpleDefinedAtom(file), _is64(is64), _name(name) { } 56 57 ~GOTEntryAtom() override = default; 58 59 ContentType contentType() const override { 60 return DefinedAtom::typeGOT; 61 } 62 63 Alignment alignment() const override { 64 return _is64 ? 8 : 4; 65 } 66 67 uint64_t size() const override { 68 return _is64 ? 8 : 4; 69 } 70 71 ContentPermissions permissions() const override { 72 return DefinedAtom::permRW_; 73 } 74 75 ArrayRef<uint8_t> rawContent() const override { 76 static const uint8_t zeros[] = 77 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 78 return llvm::makeArrayRef(zeros, size()); 79 } 80 81 StringRef slotName() const { 82 return _name; 83 } 84 85 private: 86 const bool _is64; 87 StringRef _name; 88 }; 89 90 /// Pass for instantiating and optimizing GOT slots. 91 /// 92 class GOTPass : public Pass { 93 public: 94 GOTPass(const MachOLinkingContext &context) 95 : _ctx(context), _archHandler(_ctx.archHandler()), 96 _file(*_ctx.make_file<MachOFile>("<mach-o GOT Pass>")) { 97 _file.setOrdinal(_ctx.getNextOrdinalAndIncrement()); 98 } 99 100 private: 101 llvm::Error perform(SimpleFile &mergedFile) override { 102 // Scan all references in all atoms. 103 for (const DefinedAtom *atom : mergedFile.defined()) { 104 for (const Reference *ref : *atom) { 105 // Look at instructions accessing the GOT. 106 bool canBypassGOT; 107 if (!_archHandler.isGOTAccess(*ref, canBypassGOT)) 108 continue; 109 const Atom *target = ref->target(); 110 assert(target != nullptr); 111 112 if (!shouldReplaceTargetWithGOTAtom(target, canBypassGOT)) { 113 // Update reference kind to reflect that target is a direct accesss. 114 _archHandler.updateReferenceToGOT(ref, false); 115 } else { 116 // Replace the target with a reference to a GOT entry. 117 const DefinedAtom *gotEntry = makeGOTEntry(target); 118 const_cast<Reference *>(ref)->setTarget(gotEntry); 119 // Update reference kind to reflect that target is now a GOT entry. 120 _archHandler.updateReferenceToGOT(ref, true); 121 } 122 } 123 } 124 125 // Sort and add all created GOT Atoms to master file 126 std::vector<const GOTEntryAtom *> entries; 127 entries.reserve(_targetToGOT.size()); 128 for (auto &it : _targetToGOT) 129 entries.push_back(it.second); 130 std::sort(entries.begin(), entries.end(), 131 [](const GOTEntryAtom *left, const GOTEntryAtom *right) { 132 return (left->slotName().compare(right->slotName()) < 0); 133 }); 134 for (const GOTEntryAtom *slot : entries) 135 mergedFile.addAtom(*slot); 136 137 return llvm::Error::success(); 138 } 139 140 bool shouldReplaceTargetWithGOTAtom(const Atom *target, bool canBypassGOT) { 141 // Accesses to shared library symbols must go through GOT. 142 if (isa<SharedLibraryAtom>(target)) 143 return true; 144 // Accesses to interposable symbols in same linkage unit must also go 145 // through GOT. 146 const DefinedAtom *defTarget = dyn_cast<DefinedAtom>(target); 147 if (defTarget != nullptr && 148 defTarget->interposable() != DefinedAtom::interposeNo) { 149 assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit); 150 return true; 151 } 152 // Target does not require indirection. So, if instruction allows GOT to be 153 // by-passed, do that optimization and don't create GOT entry. 154 return !canBypassGOT; 155 } 156 157 const DefinedAtom *makeGOTEntry(const Atom *target) { 158 auto pos = _targetToGOT.find(target); 159 if (pos == _targetToGOT.end()) { 160 auto *gotEntry = new (_file.allocator()) 161 GOTEntryAtom(_file, _ctx.is64Bit(), target->name()); 162 _targetToGOT[target] = gotEntry; 163 const ArchHandler::ReferenceInfo &nlInfo = _archHandler.stubInfo(). 164 nonLazyPointerReferenceToBinder; 165 gotEntry->addReference(Reference::KindNamespace::mach_o, nlInfo.arch, 166 nlInfo.kind, 0, target, 0); 167 return gotEntry; 168 } 169 return pos->second; 170 } 171 172 const MachOLinkingContext &_ctx; 173 mach_o::ArchHandler &_archHandler; 174 MachOFile &_file; 175 llvm::DenseMap<const Atom*, const GOTEntryAtom*> _targetToGOT; 176 }; 177 178 void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx) { 179 assert(ctx.needsGOTPass()); 180 pm.add(llvm::make_unique<GOTPass>(ctx)); 181 } 182 183 } // end namesapce mach_o 184 } // end namesapce lld 185