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