1 //===- lib/ReaderWriter/MachO/ShimPass.cpp -------------------------------===//
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This linker pass updates branch-sites whose target is a different mode
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.
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.
24 //===----------------------------------------------------------------------===//
26 #include "ArchHandler.h"
28 #include "MachOPasses.h"
29 #include "lld/Core/DefinedAtom.h"
30 #include "lld/Core/File.h"
31 #include "lld/Core/LLVM.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"
41 class ShimPass : public Pass {
43 ShimPass(const MachOLinkingContext &context)
45 , _archHandler(_context.archHandler())
46 , _stubInfo(_archHandler.stubInfo())
47 , _file("<mach-o shim pass>") {
51 void perform(std::unique_ptr<MutableFile> &mergedFile) override {
52 // Scan all references in all atoms.
53 for (const DefinedAtom *atom : mergedFile->defined()) {
54 for (const Reference *ref : *atom) {
55 // Look at non-call branches.
56 if (!_archHandler.isNonCallBranch(*ref))
58 const Atom *target = ref->target();
59 assert(target != nullptr);
60 if (const lld::DefinedAtom *daTarget = dyn_cast<DefinedAtom>(target)) {
61 bool atomIsThumb = _archHandler.isThumbFunction(*atom);
62 bool targetIsThumb = _archHandler.isThumbFunction(*daTarget);
63 if (atomIsThumb != targetIsThumb)
64 updateBranchToUseShim(atomIsThumb, *daTarget, ref);
68 // Exit early if no shims needed.
69 if (_targetToShim.empty())
72 // Sort shim atoms so the layout order is stable.
73 std::vector<const DefinedAtom *> shims;
74 shims.reserve(_targetToShim.size());
75 for (auto element : _targetToShim) {
76 shims.push_back(element.second);
78 std::sort(shims.begin(), shims.end(),
79 [](const DefinedAtom *l, const DefinedAtom *r) {
80 return (l->name() < r->name());
83 // Add all shims to master file.
84 for (const DefinedAtom *shim : shims) {
85 mergedFile->addAtom(*shim);
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);
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);
107 // There is no existing shim, so create a new one.
108 const DefinedAtom *shim = _archHandler.createShim(_file, thumbToArm,
110 _targetToShim[&target] = shim;
115 const MachOLinkingContext &_context;
116 mach_o::ArchHandler &_archHandler;
117 const ArchHandler::StubInfo &_stubInfo;
119 llvm::DenseMap<const Atom*, const DefinedAtom*> _targetToShim;
124 void addShimPass(PassManager &pm, const MachOLinkingContext &ctx) {
125 pm.add(llvm::make_unique<ShimPass>(ctx));
128 } // end namespace mach_o
129 } // end namespace lld