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)
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());
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))
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);
67 // Exit early if no shims needed.
68 if (_targetToShim.empty())
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);
77 std::sort(shims.begin(), shims.end(),
78 [](const DefinedAtom *l, const DefinedAtom *r) {
79 return (l->name() < r->name());
82 // Add all shims to master file.
83 for (const DefinedAtom *shim : shims)
84 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 &_ctx;
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