diff options
Diffstat (limited to 'gnu/llvm/tools/lld/lib/ReaderWriter/MachO/ShimPass.cpp')
| -rw-r--r-- | gnu/llvm/tools/lld/lib/ReaderWriter/MachO/ShimPass.cpp | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/gnu/llvm/tools/lld/lib/ReaderWriter/MachO/ShimPass.cpp b/gnu/llvm/tools/lld/lib/ReaderWriter/MachO/ShimPass.cpp new file mode 100644 index 00000000000..cd536714665 --- /dev/null +++ b/gnu/llvm/tools/lld/lib/ReaderWriter/MachO/ShimPass.cpp @@ -0,0 +1,129 @@ +//===- lib/ReaderWriter/MachO/ShimPass.cpp -------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This linker pass updates branch-sites whose target is a different mode +// (thumb vs arm). +// +// Arm code has two instruction encodings thumb and arm. When branching from +// one code encoding to another, you need to use an instruction that switches +// the instruction mode. Usually the transition only happens at call sites, and +// the linker can transform a BL instruction in BLX (or vice versa). But if the +// compiler did a tail call optimization and a function ends with a branch (not +// branch and link), there is no pc-rel BX instruction. +// +// The ShimPass looks for pc-rel B instructions that will need to switch mode. +// For those cases it synthesizes a shim which does the transition, then +// modifies the original atom with the B instruction to target to the shim atom. +// +//===----------------------------------------------------------------------===// + +#include "ArchHandler.h" +#include "File.h" +#include "MachOPasses.h" +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/File.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/Reference.h" +#include "lld/Core/Simple.h" +#include "lld/ReaderWriter/MachOLinkingContext.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" + +namespace lld { +namespace mach_o { + +class ShimPass : public Pass { +public: + ShimPass(const MachOLinkingContext &context) + : _ctx(context), _archHandler(_ctx.archHandler()), + _stubInfo(_archHandler.stubInfo()), + _file(*_ctx.make_file<MachOFile>("<mach-o shim pass>")) { + _file.setOrdinal(_ctx.getNextOrdinalAndIncrement()); + } + + llvm::Error perform(SimpleFile &mergedFile) override { + // Scan all references in all atoms. + for (const DefinedAtom *atom : mergedFile.defined()) { + for (const Reference *ref : *atom) { + // Look at non-call branches. + if (!_archHandler.isNonCallBranch(*ref)) + continue; + const Atom *target = ref->target(); + assert(target != nullptr); + if (const lld::DefinedAtom *daTarget = dyn_cast<DefinedAtom>(target)) { + bool atomIsThumb = _archHandler.isThumbFunction(*atom); + bool targetIsThumb = _archHandler.isThumbFunction(*daTarget); + if (atomIsThumb != targetIsThumb) + updateBranchToUseShim(atomIsThumb, *daTarget, ref); + } + } + } + // Exit early if no shims needed. + if (_targetToShim.empty()) + return llvm::Error(); + + // Sort shim atoms so the layout order is stable. + std::vector<const DefinedAtom *> shims; + shims.reserve(_targetToShim.size()); + for (auto element : _targetToShim) { + shims.push_back(element.second); + } + std::sort(shims.begin(), shims.end(), + [](const DefinedAtom *l, const DefinedAtom *r) { + return (l->name() < r->name()); + }); + + // Add all shims to master file. + for (const DefinedAtom *shim : shims) + mergedFile.addAtom(*shim); + + return llvm::Error(); + } + +private: + + void updateBranchToUseShim(bool thumbToArm, const DefinedAtom& target, + const Reference *ref) { + // Make file-format specific stub and other support atoms. + const DefinedAtom *shim = this->getShim(thumbToArm, target); + assert(shim != nullptr); + // Switch branch site to target shim atom. + const_cast<Reference *>(ref)->setTarget(shim); + } + + const DefinedAtom* getShim(bool thumbToArm, const DefinedAtom& target) { + auto pos = _targetToShim.find(&target); + if ( pos != _targetToShim.end() ) { + // Reuse an existing shim. + assert(pos->second != nullptr); + return pos->second; + } else { + // There is no existing shim, so create a new one. + const DefinedAtom *shim = _archHandler.createShim(_file, thumbToArm, + target); + _targetToShim[&target] = shim; + return shim; + } + } + + const MachOLinkingContext &_ctx; + mach_o::ArchHandler &_archHandler; + const ArchHandler::StubInfo &_stubInfo; + MachOFile &_file; + llvm::DenseMap<const Atom*, const DefinedAtom*> _targetToShim; +}; + + + +void addShimPass(PassManager &pm, const MachOLinkingContext &ctx) { + pm.add(llvm::make_unique<ShimPass>(ctx)); +} + +} // end namespace mach_o +} // end namespace lld |
