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