]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/ReaderWriter/MachO/ShimPass.cpp
Vendor import of lld trunk r233088:
[FreeBSD/FreeBSD.git] / 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     : _context(context)
45     , _archHandler(_context.archHandler())
46     , _stubInfo(_archHandler.stubInfo())
47     , _file("<mach-o shim pass>") {
48   }
49
50
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))
57           continue;
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);
65         }
66       }
67     }
68     // Exit early if no shims needed.
69     if (_targetToShim.empty())
70       return;
71
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);
77     }
78     std::sort(shims.begin(), shims.end(),
79               [](const DefinedAtom *l, const DefinedAtom *r) {
80                 return (l->name() < r->name());
81               });
82
83     // Add all shims to master file.
84     for (const DefinedAtom *shim : shims) {
85       mergedFile->addAtom(*shim);
86     }
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                      &_context;
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