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:
ShimPass(const MachOLinkingContext & context)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
perform(SimpleFile & mergedFile)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
updateBranchToUseShim(bool thumbToArm,const DefinedAtom & target,const Reference * ref)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
getShim(bool thumbToArm,const DefinedAtom & target)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
addShimPass(PassManager & pm,const MachOLinkingContext & ctx)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