1 //===- lib/ReaderWriter/MachO/ShimPass.cpp -------------------------------===// 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 // This linker pass updates branch-sites whose target is a different mode 11 // (thumb vs arm). 12 // 13 // Arm code has two instruction encodings thumb and arm. When branching from 14 // one code encoding to another, you need to use an instruction that switches 15 // the instruction mode. Usually the transition only happens at call sites, and 16 // the linker can transform a BL instruction in BLX (or vice versa). But if the 17 // compiler did a tail call optimization and a function ends with a branch (not 18 // branch and link), there is no pc-rel BX instruction. 19 // 20 // The ShimPass looks for pc-rel B instructions that will need to switch mode. 21 // For those cases it synthesizes a shim which does the transition, then 22 // modifies the original atom with the B instruction to target to the shim atom. 23 // 24 //===----------------------------------------------------------------------===// 25 26 #include "ArchHandler.h" 27 #include "File.h" 28 #include "MachOPasses.h" 29 #include "lld/Common/LLVM.h" 30 #include "lld/Core/DefinedAtom.h" 31 #include "lld/Core/File.h" 32 #include "lld/Core/Reference.h" 33 #include "lld/Core/Simple.h" 34 #include "lld/ReaderWriter/MachOLinkingContext.h" 35 #include "llvm/ADT/DenseMap.h" 36 #include "llvm/ADT/STLExtras.h" 37 38 namespace lld { 39 namespace mach_o { 40 41 class ShimPass : public Pass { 42 public: 43 ShimPass(const MachOLinkingContext &context) 44 : _ctx(context), _archHandler(_ctx.archHandler()), 45 _stubInfo(_archHandler.stubInfo()), 46 _file(*_ctx.make_file<MachOFile>("<mach-o shim pass>")) { 47 _file.setOrdinal(_ctx.getNextOrdinalAndIncrement()); 48 } 49 50 llvm::Error perform(SimpleFile &mergedFile) override { 51 // Scan all references in all atoms. 52 for (const DefinedAtom *atom : mergedFile.defined()) { 53 for (const Reference *ref : *atom) { 54 // Look at non-call branches. 55 if (!_archHandler.isNonCallBranch(*ref)) 56 continue; 57 const Atom *target = ref->target(); 58 assert(target != nullptr); 59 if (const lld::DefinedAtom *daTarget = dyn_cast<DefinedAtom>(target)) { 60 bool atomIsThumb = _archHandler.isThumbFunction(*atom); 61 bool targetIsThumb = _archHandler.isThumbFunction(*daTarget); 62 if (atomIsThumb != targetIsThumb) 63 updateBranchToUseShim(atomIsThumb, *daTarget, ref); 64 } 65 } 66 } 67 // Exit early if no shims needed. 68 if (_targetToShim.empty()) 69 return llvm::Error::success(); 70 71 // Sort shim atoms so the layout order is stable. 72 std::vector<const DefinedAtom *> shims; 73 shims.reserve(_targetToShim.size()); 74 for (auto element : _targetToShim) { 75 shims.push_back(element.second); 76 } 77 std::sort(shims.begin(), shims.end(), 78 [](const DefinedAtom *l, const DefinedAtom *r) { 79 return (l->name() < r->name()); 80 }); 81 82 // Add all shims to master file. 83 for (const DefinedAtom *shim : shims) 84 mergedFile.addAtom(*shim); 85 86 return llvm::Error::success(); 87 } 88 89 private: 90 91 void updateBranchToUseShim(bool thumbToArm, const DefinedAtom& target, 92 const Reference *ref) { 93 // Make file-format specific stub and other support atoms. 94 const DefinedAtom *shim = this->getShim(thumbToArm, target); 95 assert(shim != nullptr); 96 // Switch branch site to target shim atom. 97 const_cast<Reference *>(ref)->setTarget(shim); 98 } 99 100 const DefinedAtom* getShim(bool thumbToArm, const DefinedAtom& target) { 101 auto pos = _targetToShim.find(&target); 102 if ( pos != _targetToShim.end() ) { 103 // Reuse an existing shim. 104 assert(pos->second != nullptr); 105 return pos->second; 106 } else { 107 // There is no existing shim, so create a new one. 108 const DefinedAtom *shim = _archHandler.createShim(_file, thumbToArm, 109 target); 110 _targetToShim[&target] = shim; 111 return shim; 112 } 113 } 114 115 const MachOLinkingContext &_ctx; 116 mach_o::ArchHandler &_archHandler; 117 const ArchHandler::StubInfo &_stubInfo; 118 MachOFile &_file; 119 llvm::DenseMap<const Atom*, const DefinedAtom*> _targetToShim; 120 }; 121 122 123 124 void addShimPass(PassManager &pm, const MachOLinkingContext &ctx) { 125 pm.add(llvm::make_unique<ShimPass>(ctx)); 126 } 127 128 } // end namespace mach_o 129 } // end namespace lld 130