Coverage Report

Created: 2017-09-19 22:28

/Users/buildslave/jenkins/sharedspace/clang-stage2-coverage-R@2/llvm/tools/lld/lib/ReaderWriter/MachO/ShimPass.cpp
Line
Count
Source (jump to first uncovered line)
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
6
        _file(*_ctx.make_file<MachOFile>("<mach-o shim pass>")) {
47
6
    _file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
48
6
  }
49
50
6
  llvm::Error perform(SimpleFile &mergedFile) override {
51
6
    // Scan all references in all atoms.
52
45
    for (const DefinedAtom *atom : mergedFile.defined()) {
53
82
      for (const Reference *ref : *atom) {
54
82
        // Look at non-call branches.
55
82
        if (!_archHandler.isNonCallBranch(*ref))
56
76
          continue;
57
6
        const Atom *target = ref->target();
58
6
        assert(target != nullptr);
59
6
        if (const lld::DefinedAtom *
daTarget6
= dyn_cast<DefinedAtom>(target)) {
60
6
          bool atomIsThumb = _archHandler.isThumbFunction(*atom);
61
6
          bool targetIsThumb = _archHandler.isThumbFunction(*daTarget);
62
6
          if (atomIsThumb != targetIsThumb)
63
4
            updateBranchToUseShim(atomIsThumb, *daTarget, ref);
64
6
        }
65
82
      }
66
45
    }
67
6
    // Exit early if no shims needed.
68
6
    if (_targetToShim.empty())
69
5
      return llvm::Error::success();
70
1
71
1
    // Sort shim atoms so the layout order is stable.
72
1
    std::vector<const DefinedAtom *> shims;
73
1
    shims.reserve(_targetToShim.size());
74
4
    for (auto element : _targetToShim) {
75
4
      shims.push_back(element.second);
76
4
    }
77
1
    std::sort(shims.begin(), shims.end(),
78
5
              [](const DefinedAtom *l, const DefinedAtom *r) {
79
5
                return (l->name() < r->name());
80
5
              });
81
1
82
1
    // Add all shims to master file.
83
1
    for (const DefinedAtom *shim : shims)
84
4
      mergedFile.addAtom(*shim);
85
6
86
6
    return llvm::Error::success();
87
6
  }
88
89
private:
90
91
  void updateBranchToUseShim(bool thumbToArm, const DefinedAtom& target,
92
4
                             const Reference *ref) {
93
4
    // Make file-format specific stub and other support atoms.
94
4
    const DefinedAtom *shim = this->getShim(thumbToArm, target);
95
4
    assert(shim != nullptr);
96
4
    // Switch branch site to target shim atom.
97
4
    const_cast<Reference *>(ref)->setTarget(shim);
98
4
  }
99
100
4
  const DefinedAtom* getShim(bool thumbToArm, const DefinedAtom& target) {
101
4
    auto pos = _targetToShim.find(&target);
102
4
    if ( 
pos != _targetToShim.end()4
) {
103
0
      // Reuse an existing shim.
104
0
      assert(pos->second != nullptr);
105
0
      return pos->second;
106
0
    } else {
107
4
      // There is no existing shim, so create a new one.
108
4
      const DefinedAtom *shim = _archHandler.createShim(_file, thumbToArm,
109
4
                                                        target);
110
4
       _targetToShim[&target] = shim;
111
4
       return shim;
112
4
    }
113
0
  }
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
6
void addShimPass(PassManager &pm, const MachOLinkingContext &ctx) {
125
6
  pm.add(llvm::make_unique<ShimPass>(ctx));
126
6
}
127
128
} // end namespace mach_o
129
} // end namespace lld