xref: /freebsd/contrib/llvm-project/llvm/lib/ExecutionEngine/JITLink/JITLinkGeneric.h (revision a0ca4af9455b844c5e094fc1b09b1390ffa979fc)
1 //===------ JITLinkGeneric.h - Generic JIT linker utilities -----*- C++ -*-===//
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 // Generic JITLinker utilities. E.g. graph pruning, eh-frame parsing.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef LIB_EXECUTIONENGINE_JITLINK_JITLINKGENERIC_H
14 #define LIB_EXECUTIONENGINE_JITLINK_JITLINKGENERIC_H
15 
16 #include "llvm/ExecutionEngine/JITLink/JITLink.h"
17 
18 #define DEBUG_TYPE "jitlink"
19 
20 namespace llvm {
21 namespace jitlink {
22 
23 /// Base class for a JIT linker.
24 ///
25 /// A JITLinkerBase instance links one object file into an ongoing JIT
26 /// session. Symbol resolution and finalization operations are pluggable,
27 /// and called using continuation passing (passing a continuation for the
28 /// remaining linker work) to allow them to be performed asynchronously.
29 class JITLinkerBase {
30 public:
31   JITLinkerBase(std::unique_ptr<JITLinkContext> Ctx,
32                 std::unique_ptr<LinkGraph> G, PassConfiguration Passes)
33       : Ctx(std::move(Ctx)), G(std::move(G)), Passes(std::move(Passes)) {
34     assert(this->Ctx && "Ctx can not be null");
35     assert(this->G && "G can not be null");
36   }
37 
38   virtual ~JITLinkerBase();
39 
40 protected:
41   using InFlightAlloc = JITLinkMemoryManager::InFlightAlloc;
42   using AllocResult = Expected<std::unique_ptr<InFlightAlloc>>;
43   using FinalizeResult = Expected<JITLinkMemoryManager::FinalizedAlloc>;
44 
45   // Returns a reference to the graph being linked.
46   LinkGraph &getGraph() { return *G; }
47 
48   // Returns true if the context says that the linker should add default
49   // passes. This can be used by JITLinkerBase implementations when deciding
50   // whether they should add default passes.
51   bool shouldAddDefaultTargetPasses(const Triple &TT) {
52     return Ctx->shouldAddDefaultTargetPasses(TT);
53   }
54 
55   // Returns the PassConfiguration for this instance. This can be used by
56   // JITLinkerBase implementations to add late passes that reference their
57   // own data structures (e.g. for ELF implementations to locate / construct
58   // a GOT start symbol prior to fixup).
59   PassConfiguration &getPassConfig() { return Passes; }
60 
61   // Phase 1:
62   //   1.1: Run pre-prune passes
63   //   1.2: Prune graph
64   //   1.3: Run post-prune passes
65   //   1.4: Allocate memory.
66   void linkPhase1(std::unique_ptr<JITLinkerBase> Self);
67 
68   // Phase 2:
69   //   2.2: Run post-allocation passes
70   //   2.3: Notify context of final assigned symbol addresses
71   //   2.4: Identify external symbols and make an async call to resolve
72   void linkPhase2(std::unique_ptr<JITLinkerBase> Self, AllocResult AR);
73 
74   // Phase 3:
75   //   3.1: Apply resolution results
76   //   3.2: Run pre-fixup passes
77   //   3.3: Fix up block contents
78   //   3.4: Run post-fixup passes
79   //   3.5: Make an async call to transfer and finalize memory.
80   void linkPhase3(std::unique_ptr<JITLinkerBase> Self,
81                   Expected<AsyncLookupResult> LookupResult);
82 
83   // Phase 4:
84   //   4.1: Call OnFinalized callback, handing off allocation.
85   void linkPhase4(std::unique_ptr<JITLinkerBase> Self, FinalizeResult FR);
86 
87 private:
88   // Run all passes in the given pass list, bailing out immediately if any pass
89   // returns an error.
90   Error runPasses(LinkGraphPassList &Passes);
91 
92   // Copy block contents and apply relocations.
93   // Implemented in JITLinker.
94   virtual Error fixUpBlocks(LinkGraph &G) const = 0;
95 
96   JITLinkContext::LookupMap getExternalSymbolNames() const;
97   void applyLookupResult(AsyncLookupResult LR);
98   void abandonAllocAndBailOut(std::unique_ptr<JITLinkerBase> Self, Error Err);
99 
100   std::unique_ptr<JITLinkContext> Ctx;
101   std::unique_ptr<LinkGraph> G;
102   PassConfiguration Passes;
103   std::unique_ptr<InFlightAlloc> Alloc;
104 };
105 
106 template <typename LinkerImpl> class JITLinker : public JITLinkerBase {
107 public:
108   using JITLinkerBase::JITLinkerBase;
109 
110   /// Link constructs a LinkerImpl instance and calls linkPhase1.
111   /// Link should be called with the constructor arguments for LinkerImpl, which
112   /// will be forwarded to the constructor.
113   template <typename... ArgTs> static void link(ArgTs &&... Args) {
114     auto L = std::make_unique<LinkerImpl>(std::forward<ArgTs>(Args)...);
115 
116     // Ownership of the linker is passed into the linker's doLink function to
117     // allow it to be passed on to async continuations.
118     //
119     // FIXME: Remove LTmp once we have c++17.
120     // C++17 sequencing rules guarantee that function name expressions are
121     // sequenced before arguments, so L->linkPhase1(std::move(L), ...) will be
122     // well formed.
123     auto &LTmp = *L;
124     LTmp.linkPhase1(std::move(L));
125   }
126 
127 private:
128   const LinkerImpl &impl() const {
129     return static_cast<const LinkerImpl &>(*this);
130   }
131 
132   Error fixUpBlocks(LinkGraph &G) const override {
133     LLVM_DEBUG(dbgs() << "Fixing up blocks:\n");
134 
135     for (auto &Sec : G.sections()) {
136       bool NoAllocSection = Sec.getMemLifetime() == orc::MemLifetime::NoAlloc;
137 
138       for (auto *B : Sec.blocks()) {
139         LLVM_DEBUG(dbgs() << "  " << *B << ":\n");
140 
141         // Copy Block data and apply fixups.
142         LLVM_DEBUG(dbgs() << "    Applying fixups.\n");
143         assert((!B->isZeroFill() || all_of(B->edges(),
144                                            [](const Edge &E) {
145                                              return E.getKind() ==
146                                                     Edge::KeepAlive;
147                                            })) &&
148                "Non-KeepAlive edges in zero-fill block?");
149 
150         // If this is a no-alloc section then copy the block content into
151         // memory allocated on the Graph's allocator (if it hasn't been
152         // already).
153         if (NoAllocSection)
154           (void)B->getMutableContent(G);
155 
156         for (auto &E : B->edges()) {
157 
158           // Skip non-relocation edges.
159           if (!E.isRelocation())
160             continue;
161 
162           // If B is a block in a Standard or Finalize section then make sure
163           // that no edges point to symbols in NoAlloc sections.
164           assert((NoAllocSection || !E.getTarget().isDefined() ||
165                   E.getTarget().getBlock().getSection().getMemLifetime() !=
166                       orc::MemLifetime::NoAlloc) &&
167                  "Block in allocated section has edge pointing to no-alloc "
168                  "section");
169 
170           // Dispatch to LinkerImpl for fixup.
171           if (auto Err = impl().applyFixup(G, *B, E))
172             return Err;
173         }
174       }
175     }
176 
177     return Error::success();
178   }
179 };
180 
181 /// Removes dead symbols/blocks/addressables.
182 ///
183 /// Finds the set of symbols and addressables reachable from any symbol
184 /// initially marked live. All symbols/addressables not marked live at the end
185 /// of this process are removed.
186 void prune(LinkGraph &G);
187 
188 } // end namespace jitlink
189 } // end namespace llvm
190 
191 #undef DEBUG_TYPE // "jitlink"
192 
193 #endif // LIB_EXECUTIONENGINE_JITLINK_JITLINKGENERIC_H
194