1//===----- GDBRegistrationListener.cpp - Registers objects with GDB -------===//
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#include "llvm-c/ExecutionEngine.h"
10#include "llvm/ADT/DenseMap.h"
11#include "llvm/ExecutionEngine/JITEventListener.h"
12#include "llvm/Object/ObjectFile.h"
13#include "llvm/Support/Compiler.h"
14#include "llvm/Support/ErrorHandling.h"
15#include "llvm/Support/MemoryBuffer.h"
16#include "llvm/Support/Mutex.h"
17#include <mutex>
18
19using namespace llvm;
20using namespace llvm::object;
21
22// This must be kept in sync with gdb/gdb/jit.h .
23extern "C" {
24
25 typedef enum {
26 JIT_NOACTION = 0,
27 JIT_REGISTER_FN,
28 JIT_UNREGISTER_FN
29 } jit_actions_t;
30
31 struct jit_code_entry {
32 struct jit_code_entry *next_entry;
33 struct jit_code_entry *prev_entry;
34 const char *symfile_addr;
35 uint64_t symfile_size;
36 };
37
38 struct jit_descriptor {
39 uint32_t version;
40 // This should be jit_actions_t, but we want to be specific about the
41 // bit-width.
42 uint32_t action_flag;
43 struct jit_code_entry *relevant_entry;
44 struct jit_code_entry *first_entry;
45 };
46
47 // We put information about the JITed function in this global, which the
48 // debugger reads. Make sure to specify the version statically, because the
49 // debugger checks the version before we can set it during runtime.
50 extern struct jit_descriptor __jit_debug_descriptor;
51
52 // Debuggers puts a breakpoint in this function.
53 extern "C" void __jit_debug_register_code();
54}
55
56namespace {
57
58// FIXME: lli aims to provide both, RuntimeDyld and JITLink, as the dynamic
59// loaders for its JIT implementations. And they both offer debugging via the
60// GDB JIT interface, which builds on the two well-known symbol names below.
61// As these symbols must be unique across the linked executable, we can only
62// define them in one of the libraries and make the other depend on it.
63// OrcTargetProcess is a minimal stub for embedding a JIT client in remote
64// executors. For the moment it seems reasonable to have the definition there
65// and let ExecutionEngine depend on it, until we find a better solution.
66//
67LLVM_ATTRIBUTE_USED void requiredSymbolDefinitionsFromOrcTargetProcess() {
68 errs() << (void *)&__jit_debug_register_code
69 << (void *)&__jit_debug_descriptor;
70}
71
72struct RegisteredObjectInfo {
73 RegisteredObjectInfo() = default;
74
75 RegisteredObjectInfo(std::size_t Size, jit_code_entry *Entry,
76 OwningBinary<ObjectFile> Obj)
77 : Size(Size), Entry(Entry), Obj(std::move(Obj)) {}
78
79 std::size_t Size;
80 jit_code_entry *Entry;
81 OwningBinary<ObjectFile> Obj;
82};
83
84// Buffer for an in-memory object file in executable memory
85typedef llvm::DenseMap<JITEventListener::ObjectKey, RegisteredObjectInfo>
86 RegisteredObjectBufferMap;
87
88/// Global access point for the JIT debugging interface designed for use with a
89/// singleton toolbox. Handles thread-safe registration and deregistration of
90/// object files that are in executable memory managed by the client of this
91/// class.
92class GDBJITRegistrationListener : public JITEventListener {
93 /// Lock used to serialize all jit registration events, since they
94 /// modify global variables.
95 ///
96 /// Only a single instance of GDBJITRegistrationListener is ever created,
97 /// and so the lock can be a member variable of that instance. This ensures
98 /// destructors are run in the correct order.
99 sys::Mutex JITDebugLock;
100
101 /// A map of in-memory object files that have been registered with the
102 /// JIT interface.
103 RegisteredObjectBufferMap ObjectBufferMap;
104
105 /// Instantiates the JIT service.
106 GDBJITRegistrationListener() = default;
107
108 /// Unregisters each object that was previously registered and releases all
109 /// internal resources.
110 ~GDBJITRegistrationListener() override;
111
112public:
113 static GDBJITRegistrationListener &instance() {
114 static GDBJITRegistrationListener Instance;
115 return Instance;
116 }
117
118 /// Creates an entry in the JIT registry for the buffer @p Object,
119 /// which must contain an object file in executable memory with any
120 /// debug information for the debugger.
121 void notifyObjectLoaded(ObjectKey K, const ObjectFile &Obj,
122 const RuntimeDyld::LoadedObjectInfo &L) override;
123
124 /// Removes the internal registration of @p Object, and
125 /// frees associated resources.
126 /// Returns true if @p Object was found in ObjectBufferMap.
127 void notifyFreeingObject(ObjectKey K) override;
128
129private:
130 /// Deregister the debug info for the given object file from the debugger
131 /// and delete any temporary copies. This private method does not remove
132 /// the function from Map so that it can be called while iterating over Map.
133 void deregisterObjectInternal(RegisteredObjectBufferMap::iterator I);
134};
135
136/// Do the registration.
137void NotifyDebugger(jit_code_entry* JITCodeEntry) {
138 __jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
139
140 // Insert this entry at the head of the list.
141 JITCodeEntry->prev_entry = nullptr;
142 jit_code_entry* NextEntry = __jit_debug_descriptor.first_entry;
143 JITCodeEntry->next_entry = NextEntry;
144 if (NextEntry) {
145 NextEntry->prev_entry = JITCodeEntry;
146 }
147 __jit_debug_descriptor.first_entry = JITCodeEntry;
148 __jit_debug_descriptor.relevant_entry = JITCodeEntry;
149 __jit_debug_register_code();
150}
151
152GDBJITRegistrationListener::~GDBJITRegistrationListener() {
153 // Free all registered object files.
154 std::lock_guard<llvm::sys::Mutex> locked(JITDebugLock);
155 for (RegisteredObjectBufferMap::iterator I = ObjectBufferMap.begin(),
156 E = ObjectBufferMap.end();
157 I != E; ++I) {
158 // Call the private method that doesn't update the map so our iterator
159 // doesn't break.
160 deregisterObjectInternal(I);
161 }
162 ObjectBufferMap.clear();
163}
164
165void GDBJITRegistrationListener::notifyObjectLoaded(
166 ObjectKey K, const ObjectFile &Obj,
167 const RuntimeDyld::LoadedObjectInfo &L) {
168
169 OwningBinary<ObjectFile> DebugObj = L.getObjectForDebug(Obj);
170
171 // Bail out if debug objects aren't supported.
172 if (!DebugObj.getBinary())
173 return;
174
175 const char *Buffer = DebugObj.getBinary()->getMemoryBufferRef().getBufferStart();
176 size_t Size = DebugObj.getBinary()->getMemoryBufferRef().getBufferSize();
177
178 std::lock_guard<llvm::sys::Mutex> locked(JITDebugLock);
179 assert(!ObjectBufferMap.contains(K) &&
180 "Second attempt to perform debug registration.");
181 jit_code_entry* JITCodeEntry = new jit_code_entry();
182
183 if (!JITCodeEntry) {
184 llvm::report_fatal_error(
185 reason: "Allocation failed when registering a JIT entry!\n");
186 } else {
187 JITCodeEntry->symfile_addr = Buffer;
188 JITCodeEntry->symfile_size = Size;
189
190 ObjectBufferMap[K] =
191 RegisteredObjectInfo(Size, JITCodeEntry, std::move(DebugObj));
192 NotifyDebugger(JITCodeEntry);
193 }
194}
195
196void GDBJITRegistrationListener::notifyFreeingObject(ObjectKey K) {
197 std::lock_guard<llvm::sys::Mutex> locked(JITDebugLock);
198 RegisteredObjectBufferMap::iterator I = ObjectBufferMap.find(Val: K);
199
200 if (I != ObjectBufferMap.end()) {
201 deregisterObjectInternal(I);
202 ObjectBufferMap.erase(I);
203 }
204}
205
206void GDBJITRegistrationListener::deregisterObjectInternal(
207 RegisteredObjectBufferMap::iterator I) {
208
209 jit_code_entry*& JITCodeEntry = I->second.Entry;
210
211 // Do the unregistration.
212 {
213 __jit_debug_descriptor.action_flag = JIT_UNREGISTER_FN;
214
215 // Remove the jit_code_entry from the linked list.
216 jit_code_entry* PrevEntry = JITCodeEntry->prev_entry;
217 jit_code_entry* NextEntry = JITCodeEntry->next_entry;
218
219 if (NextEntry) {
220 NextEntry->prev_entry = PrevEntry;
221 }
222 if (PrevEntry) {
223 PrevEntry->next_entry = NextEntry;
224 }
225 else {
226 assert(__jit_debug_descriptor.first_entry == JITCodeEntry);
227 __jit_debug_descriptor.first_entry = NextEntry;
228 }
229
230 // Tell the debugger which entry we removed, and unregister the code.
231 __jit_debug_descriptor.relevant_entry = JITCodeEntry;
232 __jit_debug_register_code();
233 }
234
235 delete JITCodeEntry;
236 JITCodeEntry = nullptr;
237}
238
239} // end namespace
240
241namespace llvm {
242
243JITEventListener* JITEventListener::createGDBRegistrationListener() {
244 return &GDBJITRegistrationListener::instance();
245}
246
247} // namespace llvm
248
249LLVMJITEventListenerRef LLVMCreateGDBRegistrationListener(void)
250{
251 return wrap(P: JITEventListener::createGDBRegistrationListener());
252}
253