]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/lld/lib/ReaderWriter/MachO/ShimPass.cpp
MFV r310622:
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / lld / lib / ReaderWriter / MachO / ShimPass.cpp
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/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"
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();
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();
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