Coverage Report

Created: 2019-07-24 05:18

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/lld/lib/ReaderWriter/MachO/ShimPass.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- lib/ReaderWriter/MachO/ShimPass.cpp -------------------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// This linker pass updates branch-sites whose target is a different mode
10
// (thumb vs arm).
11
//
12
// Arm code has two instruction encodings thumb and arm.  When branching from
13
// one code encoding to another, you need to use an instruction that switches
14
// the instruction mode.  Usually the transition only happens at call sites, and
15
// the linker can transform a BL instruction in BLX (or vice versa).  But if the
16
// compiler did a tail call optimization and a function ends with a branch (not
17
// branch and link), there is no pc-rel BX instruction.
18
//
19
// The ShimPass looks for pc-rel B instructions that will need to switch mode.
20
// For those cases it synthesizes a shim which does the transition, then
21
// modifies the original atom with the B instruction to target to the shim atom.
22
//
23
//===----------------------------------------------------------------------===//
24
25
#include "ArchHandler.h"
26
#include "File.h"
27
#include "MachOPasses.h"
28
#include "lld/Common/LLVM.h"
29
#include "lld/Core/DefinedAtom.h"
30
#include "lld/Core/File.h"
31
#include "lld/Core/Reference.h"
32
#include "lld/Core/Simple.h"
33
#include "lld/ReaderWriter/MachOLinkingContext.h"
34
#include "llvm/ADT/DenseMap.h"
35
#include "llvm/ADT/STLExtras.h"
36
37
namespace lld {
38
namespace mach_o {
39
40
class ShimPass : public Pass {
41
public:
42
  ShimPass(const MachOLinkingContext &context)
43
      : _ctx(context), _archHandler(_ctx.archHandler()),
44
        _stubInfo(_archHandler.stubInfo()),
45
6
        _file(*_ctx.make_file<MachOFile>("<mach-o shim pass>")) {
46
6
    _file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
47
6
  }
48
49
6
  llvm::Error perform(SimpleFile &mergedFile) override {
50
6
    // Scan all references in all atoms.
51
45
    for (const DefinedAtom *atom : mergedFile.defined()) {
52
82
      for (const Reference *ref : *atom) {
53
82
        // Look at non-call branches.
54
82
        if (!_archHandler.isNonCallBranch(*ref))
55
76
          continue;
56
6
        const Atom *target = ref->target();
57
6
        assert(target != nullptr);
58
6
        if (const lld::DefinedAtom *daTarget = dyn_cast<DefinedAtom>(target)) {
59
6
          bool atomIsThumb = _archHandler.isThumbFunction(*atom);
60
6
          bool targetIsThumb = _archHandler.isThumbFunction(*daTarget);
61
6
          if (atomIsThumb != targetIsThumb)
62
4
            updateBranchToUseShim(atomIsThumb, *daTarget, ref);
63
6
        }
64
6
      }
65
45
    }
66
6
    // Exit early if no shims needed.
67
6
    if (_targetToShim.empty())
68
5
      return llvm::Error::success();
69
1
70
1
    // Sort shim atoms so the layout order is stable.
71
1
    std::vector<const DefinedAtom *> shims;
72
1
    shims.reserve(_targetToShim.size());
73
4
    for (auto element : _targetToShim) {
74
4
      shims.push_back(element.second);
75
4
    }
76
1
    std::sort(shims.begin(), shims.end(),
77
4
              [](const DefinedAtom *l, const DefinedAtom *r) {
78
4
                return (l->name() < r->name());
79
4
              });
80
1
81
1
    // Add all shims to master file.
82
1
    for (const DefinedAtom *shim : shims)
83
4
      mergedFile.addAtom(*shim);
84
1
85
1
    return llvm::Error::success();
86
1
  }
87
88
private:
89
90
  void updateBranchToUseShim(bool thumbToArm, const DefinedAtom& target,
91
4
                             const Reference *ref) {
92
4
    // Make file-format specific stub and other support atoms.
93
4
    const DefinedAtom *shim = this->getShim(thumbToArm, target);
94
4
    assert(shim != nullptr);
95
4
    // Switch branch site to target shim atom.
96
4
    const_cast<Reference *>(ref)->setTarget(shim);
97
4
  }
98
99
4
  const DefinedAtom* getShim(bool thumbToArm, const DefinedAtom& target) {
100
4
    auto pos = _targetToShim.find(&target);
101
4
    if ( pos != _targetToShim.end() ) {
102
0
      // Reuse an existing shim.
103
0
      assert(pos->second != nullptr);
104
0
      return pos->second;
105
4
    } else {
106
4
      // There is no existing shim, so create a new one.
107
4
      const DefinedAtom *shim = _archHandler.createShim(_file, thumbToArm,
108
4
                                                        target);
109
4
       _targetToShim[&target] = shim;
110
4
       return shim;
111
4
    }
112
4
  }
113
114
  const MachOLinkingContext &_ctx;
115
  mach_o::ArchHandler                            &_archHandler;
116
  const ArchHandler::StubInfo                    &_stubInfo;
117
  MachOFile                                      &_file;
118
  llvm::DenseMap<const Atom*, const DefinedAtom*> _targetToShim;
119
};
120
121
122
123
6
void addShimPass(PassManager &pm, const MachOLinkingContext &ctx) {
124
6
  pm.add(llvm::make_unique<ShimPass>(ctx));
125
6
}
126
127
} // end namespace mach_o
128
} // end namespace lld