diff options
| author | Nick Kledzik <kledzik@apple.com> | 2014-07-09 21:04:24 +0000 |
|---|---|---|
| committer | Nick Kledzik <kledzik@apple.com> | 2014-07-09 21:04:24 +0000 |
| commit | 0edfdeb0be83dc9fac8d11c51fc59eb1ab73512e (patch) | |
| tree | 0ddd2e9aeae75654568baabe5cf9654bdd1d75a4 /lld/lib/ReaderWriter | |
| parent | 79d4ffbc8fb57a6362da83868952fbe3cb591260 (diff) | |
| download | bcm5719-llvm-0edfdeb0be83dc9fac8d11c51fc59eb1ab73512e.tar.gz bcm5719-llvm-0edfdeb0be83dc9fac8d11c51fc59eb1ab73512e.zip | |
Move GOTPass and StubsPass from Core to MachO
llvm-svn: 212652
Diffstat (limited to 'lld/lib/ReaderWriter')
| -rw-r--r-- | lld/lib/ReaderWriter/CoreLinkingContext.cpp | 65 | ||||
| -rw-r--r-- | lld/lib/ReaderWriter/MachO/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | lld/lib/ReaderWriter/MachO/GOTPass.cpp | 108 | ||||
| -rw-r--r-- | lld/lib/ReaderWriter/MachO/GOTPass.hpp | 1 | ||||
| -rw-r--r-- | lld/lib/ReaderWriter/MachO/MachOPasses.h | 101 | ||||
| -rw-r--r-- | lld/lib/ReaderWriter/MachO/StubsPass.cpp | 67 | ||||
| -rw-r--r-- | lld/lib/ReaderWriter/MachO/StubsPass.hpp | 1 |
7 files changed, 280 insertions, 65 deletions
diff --git a/lld/lib/ReaderWriter/CoreLinkingContext.cpp b/lld/lib/ReaderWriter/CoreLinkingContext.cpp index 0adaa017672..9fd97ed067a 100644 --- a/lld/lib/ReaderWriter/CoreLinkingContext.cpp +++ b/lld/lib/ReaderWriter/CoreLinkingContext.cpp @@ -184,67 +184,6 @@ private: }; -class TestingStubsPass : public StubsPass { -public: - TestingStubsPass(const LinkingContext &ctx) : _file(ctx) {} - - bool noTextRelocs() override { return true; } - - bool isCallSite(const Reference &ref) override { - if (ref.kindNamespace() != Reference::KindNamespace::testing) - return false; - return (ref.kindValue() == CoreLinkingContext::TEST_RELOC_CALL32); - } - - const DefinedAtom *getStub(const Atom &target) override { - const DefinedAtom *result = new TestingStubAtom(_file, target); - _file.addAtom(*result); - return result; - } - - void addStubAtoms(MutableFile &mergedFile) override { - for (const DefinedAtom *stub : _file.defined()) { - mergedFile.addAtom(*stub); - } - } - -private: - TestingPassFile _file; -}; - -class TestingGOTPass : public GOTPass { -public: - TestingGOTPass(const LinkingContext &ctx) : _file(ctx) {} - - bool noTextRelocs() override { return true; } - - bool isGOTAccess(const Reference &ref, bool &canBypassGOT) override { - if (ref.kindNamespace() != Reference::KindNamespace::testing) - return false; - switch (ref.kindValue()) { - case CoreLinkingContext::TEST_RELOC_GOT_LOAD32: - canBypassGOT = true; - return true; - case CoreLinkingContext::TEST_RELOC_GOT_USE32: - canBypassGOT = false; - return true; - } - return false; - } - - void updateReferenceToGOT(const Reference *ref, bool targetIsNowGOT) override { - const_cast<Reference *>(ref)->setKindValue( - targetIsNowGOT ? CoreLinkingContext::TEST_RELOC_PCREL32 - : CoreLinkingContext::TEST_RELOC_LEA32_WAS_GOT); - } - - const DefinedAtom *makeGOTEntry(const Atom &target) override { - return new TestingGOTAtom(_file, target); - } - -private: - TestingPassFile _file; -}; } // anonymous namespace @@ -259,10 +198,6 @@ void CoreLinkingContext::addPasses(PassManager &pm) { for (StringRef name : _passNames) { if (name.equals("layout")) pm.add(std::unique_ptr<Pass>(new LayoutPass(registry()))); - else if (name.equals("GOT")) - pm.add(std::unique_ptr<Pass>(new TestingGOTPass(*this))); - else if (name.equals("stubs")) - pm.add(std::unique_ptr<Pass>(new TestingStubsPass(*this))); else llvm_unreachable("bad pass name"); } diff --git a/lld/lib/ReaderWriter/MachO/CMakeLists.txt b/lld/lib/ReaderWriter/MachO/CMakeLists.txt index 0b9b13545e0..51a1f2077a2 100644 --- a/lld/lib/ReaderWriter/MachO/CMakeLists.txt +++ b/lld/lib/ReaderWriter/MachO/CMakeLists.txt @@ -1,4 +1,5 @@ add_lld_library(lldMachO + GOTPass.cpp MachOLinkingContext.cpp MachONormalizedFileBinaryReader.cpp MachONormalizedFileBinaryWriter.cpp @@ -6,6 +7,7 @@ add_lld_library(lldMachO MachONormalizedFileToAtoms.cpp MachONormalizedFileYAML.cpp ReferenceKinds.cpp + StubsPass.cpp WriterMachO.cpp ) diff --git a/lld/lib/ReaderWriter/MachO/GOTPass.cpp b/lld/lib/ReaderWriter/MachO/GOTPass.cpp new file mode 100644 index 00000000000..1d5c6eeeb85 --- /dev/null +++ b/lld/lib/ReaderWriter/MachO/GOTPass.cpp @@ -0,0 +1,108 @@ +//===- lib/ReaderWriter/MachO/GOTPass.cpp ---------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This linker pass transforms all GOT kind references to real references. +/// That is, in assembly you can write something like: +/// movq foo@GOTPCREL(%rip), %rax +/// which means you want to load a pointer to "foo" out of the GOT (global +/// Offsets Table). In the object file, the Atom containing this instruction +/// has a Reference whose target is an Atom named "foo" and the Reference +/// kind is a GOT load. The linker needs to instantiate a pointer sized +/// GOT entry. This is done be creating a GOT Atom to represent that pointer +/// sized data in this pass, and altering the Atom graph so the Reference now +/// points to the GOT Atom entry (corresponding to "foo") and changing the +/// Reference Kind to reflect it is now pointing to a GOT entry (rather +/// then needing a GOT entry). +/// +/// There is one optimization the linker can do here. If the target of the GOT +/// is in the same linkage unit and does not need to be interposable, and +/// the GOT use is just a load (not some other operation), this pass can +/// transform that load into an LEA (add). This optimizes away one memory load +/// which at runtime that could stall the pipeline. This optimization only +/// works for architectures in which a (GOT) load instruction can be change to +/// an LEA instruction that is the same size. The method isGOTAccess() should +/// only return true for "canBypassGOT" if this optimization is supported. +/// +//===----------------------------------------------------------------------===// + +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/File.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/Reference.h" +#include "llvm/ADT/DenseMap.h" + +#include "MachOPasses.h" + +namespace lld { + +static bool shouldReplaceTargetWithGOTAtom(const Atom *target, + bool canBypassGOT) { + // Accesses to shared library symbols must go through GOT. + if (target->definition() == Atom::definitionSharedLibrary) + return true; + // Accesses to interposable symbols in same linkage unit must also go + // through GOT. + const DefinedAtom *defTarget = dyn_cast<DefinedAtom>(target); + if (defTarget != nullptr && + defTarget->interposable() != DefinedAtom::interposeNo) { + assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit); + return true; + } + // Target does not require indirection. So, if instruction allows GOT to be + // by-passed, do that optimization and don't create GOT entry. + return !canBypassGOT; +} + +static const DefinedAtom * +findGOTAtom(const Atom *target, + llvm::DenseMap<const Atom *, const DefinedAtom *> &targetToGOT) { + auto pos = targetToGOT.find(target); + return (pos == targetToGOT.end()) ? nullptr : pos->second; +} + +void GOTPass::perform(std::unique_ptr<MutableFile> &mergedFile) { + // Use map so all pointers to same symbol use same GOT entry. + llvm::DenseMap<const Atom*, const DefinedAtom*> targetToGOT; + + // Scan all references in all atoms. + for (const DefinedAtom *atom : mergedFile->defined()) { + for (const Reference *ref : *atom) { + // Look at instructions accessing the GOT. + bool canBypassGOT; + if (!isGOTAccess(*ref, canBypassGOT)) + continue; + const Atom *target = ref->target(); + assert(target != nullptr); + + if (!shouldReplaceTargetWithGOTAtom(target, canBypassGOT)) { + // Update reference kind to reflect that target is a direct accesss. + updateReferenceToGOT(ref, false); + continue; + } + // Replace the target with a reference to a GOT entry. + const DefinedAtom *gotEntry = findGOTAtom(target, targetToGOT); + if (!gotEntry) { + gotEntry = makeGOTEntry(*target); + assert(gotEntry != nullptr); + assert(gotEntry->contentType() == DefinedAtom::typeGOT); + targetToGOT[target] = gotEntry; + } + const_cast<Reference *>(ref)->setTarget(gotEntry); + // Update reference kind to reflect that target is now a GOT entry. + updateReferenceToGOT(ref, true); + } + } + + // add all created GOT Atoms to master file + for (auto &it : targetToGOT) + mergedFile->addAtom(*it.second); +} + +} // end namesapce lld diff --git a/lld/lib/ReaderWriter/MachO/GOTPass.hpp b/lld/lib/ReaderWriter/MachO/GOTPass.hpp index f66d6a76de8..a7ab2543555 100644 --- a/lld/lib/ReaderWriter/MachO/GOTPass.hpp +++ b/lld/lib/ReaderWriter/MachO/GOTPass.hpp @@ -16,6 +16,7 @@ #include "lld/Core/Reference.h" #include "lld/Core/Pass.h" +#include "MachOPasses.h" #include "ReferenceKinds.h" #include "StubAtoms.hpp" diff --git a/lld/lib/ReaderWriter/MachO/MachOPasses.h b/lld/lib/ReaderWriter/MachO/MachOPasses.h new file mode 100644 index 00000000000..d55fdf5fac8 --- /dev/null +++ b/lld/lib/ReaderWriter/MachO/MachOPasses.h @@ -0,0 +1,101 @@ +//===- lib/ReaderWriter/MachO/MachOPasses.h -------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_MACHO_PASSES_H +#define LLD_READER_WRITER_MACHO_PASSES_H + +#include "lld/Core/Atom.h" +#include "lld/Core/File.h" +#include "lld/Core/Pass.h" +#include "lld/Core/range.h" +#include "lld/Core/Reference.h" + +#include <vector> + +namespace lld { +class DefinedAtom; +class MutableFile; + + +/// Pass for adding stubs (PLT entries) for calls to functions +/// outside the linkage unit. This class is subclassed by each +/// file format Writer which implements the pure virtual methods. +class StubsPass : public Pass { +public: + StubsPass() : Pass() {} + + /// Scans all Atoms looking for call-site uses of SharedLibraryAtoms + /// and transfroms the call-site to call a stub instead using the + /// helper methods below. + void perform(std::unique_ptr<MutableFile> &mergedFile) override; + + /// If true, the pass should use stubs for references + /// to shared library symbols. If false, the pass + /// will generate relocations on the text segment which the + /// runtime loader will use to patch the program at runtime. + virtual bool noTextRelocs() = 0; + + /// Returns whether the Reference kind is for a call site. The pass + /// uses this to find calls that need to be indirected through a stub. + virtual bool isCallSite(const Reference &) = 0; + + /// Returns a file format specific atom for a stub/PLT entry which contains + /// instructions which jump to the specified atom. May be called multiple + /// times for the same target atom, in which case this method should return + /// the same stub atom. + virtual const DefinedAtom *getStub(const Atom &target) = 0; + + /// After the default implementation of perform() is done calling getStub(), + /// it will call this method to add all the stub (and support) atoms to the + /// master file object. + virtual void addStubAtoms(MutableFile &masterFile) = 0; + +private: + void replaceCalleeWithStub(const Atom *target, const Reference *ref); +}; + +/// Pass for adding GOT entries for pointers to functions/data +/// outside the linkage unit. This class is subclassed by each +/// file format Writer which implements the pure virtual methods. +class GOTPass : public Pass { +public: + GOTPass() : Pass() {} + + /// Scans all Atoms looking for pointer to SharedLibraryAtoms + /// and transfroms them to a pointer to a GOT entry using the + /// helper methods below. + void perform(std::unique_ptr<MutableFile> &mergedFile) override; + + /// If true, the pass will use GOT entries for references + /// to shared library symbols. If false, the pass + /// will generate relocations on the text segment which the + /// runtime loader will use to patch the program at runtime. + virtual bool noTextRelocs() = 0; + + /// Returns whether the Reference kind is a pre-instantiated GOT access. + /// The default implementation of perform() uses this to figure out + /// what GOT entries to instantiate. + virtual bool isGOTAccess(const Reference &, bool &canBypassGOT) = 0; + + /// The file format Writer needs to alter the reference kind from a + /// pre-instantiated GOT access to an actual access. If targetIsNowGOT is + /// true, the pass has instantiated a GOT atom and altered the reference's + /// target to point to that atom. If targetIsNowGOT is false, the pass + /// determined a GOT entry is not needed because the reference site can + /// directly access the target. + virtual void updateReferenceToGOT(const Reference*, bool targetIsNowGOT) = 0; + + /// Returns a file format specific atom for a GOT entry targeting + /// the specified atom. + virtual const DefinedAtom *makeGOTEntry(const Atom &target) = 0; +}; + +} // namespace lld + +#endif // LLD_READER_WRITER_MACHO_PASSES_H diff --git a/lld/lib/ReaderWriter/MachO/StubsPass.cpp b/lld/lib/ReaderWriter/MachO/StubsPass.cpp new file mode 100644 index 00000000000..630e00be179 --- /dev/null +++ b/lld/lib/ReaderWriter/MachO/StubsPass.cpp @@ -0,0 +1,67 @@ +//===- lib/ReaderWriter/MachO/StubsPass.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 call-sites which have references to shared library +// atoms to instead have a reference to a stub (PLT entry) for the specified +// symbol. Each file format defines a subclass of StubsPass which implements +// the abstract methods for creating the file format specific StubAtoms. +// +//===----------------------------------------------------------------------===// + +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/File.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/Reference.h" +#include "llvm/ADT/DenseMap.h" + +#include "MachOPasses.h" + +namespace lld { + +void StubsPass::perform(std::unique_ptr<MutableFile> &mergedFile) { + // Skip this pass if output format uses text relocations instead of stubs. + if (!this->noTextRelocs()) + return; + + // Scan all references in all atoms. + for (const DefinedAtom *atom : mergedFile->defined()) { + for (const Reference *ref : *atom) { + // Look at call-sites. + if (!this->isCallSite(*ref)) + continue; + const Atom *target = ref->target(); + assert(target != nullptr); + if (target->definition() == Atom::definitionSharedLibrary) { + // Calls to shared libraries go through stubs. + replaceCalleeWithStub(target, ref); + continue; + } + const DefinedAtom *defTarget = dyn_cast<DefinedAtom>(target); + if (defTarget && defTarget->interposable() != DefinedAtom::interposeNo) { + // Calls to interposable functions in same linkage unit must also go + // through a stub. + assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit); + replaceCalleeWithStub(target, ref); + } + } + } + // Add all created stubs and support Atoms. + this->addStubAtoms(*mergedFile); +} + +void StubsPass::replaceCalleeWithStub(const Atom *target, + const Reference *ref) { + // Make file-format specific stub and other support atoms. + const DefinedAtom *stub = this->getStub(*target); + assert(stub != nullptr); + // Switch call site to reference stub atom instead. + const_cast<Reference *>(ref)->setTarget(stub); +} + +} // end namespace lld diff --git a/lld/lib/ReaderWriter/MachO/StubsPass.hpp b/lld/lib/ReaderWriter/MachO/StubsPass.hpp index 8dac613f0b0..0e705896c7f 100644 --- a/lld/lib/ReaderWriter/MachO/StubsPass.hpp +++ b/lld/lib/ReaderWriter/MachO/StubsPass.hpp @@ -19,6 +19,7 @@ #include "lld/Core/SharedLibraryAtom.h" #include "lld/Core/Simple.h" +#include "MachOPasses.h" #include "ReferenceKinds.h" #include "StubAtoms.hpp" |

