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/ManagedStatic.h" 16 #include "llvm/Support/Mutex.h" 17 #include <mutex> 18 19 using namespace llvm; 20 using namespace llvm::object; 21 22 // This must be kept in sync with gdb/gdb/jit.h . 23 extern "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 struct jit_descriptor __jit_debug_descriptor = { 1, 0, nullptr, nullptr }; 51 52 // Debuggers puts a breakpoint in this function. 53 LLVM_ATTRIBUTE_NOINLINE void __jit_debug_register_code() { 54 // The noinline and the asm prevent calls to this function from being 55 // optimized out. 56 #if !defined(_MSC_VER) 57 asm volatile("":::"memory"); 58 #endif 59 } 60 61 } 62 63 namespace { 64 65 struct RegisteredObjectInfo { 66 RegisteredObjectInfo() {} 67 68 RegisteredObjectInfo(std::size_t Size, jit_code_entry *Entry, 69 OwningBinary<ObjectFile> Obj) 70 : Size(Size), Entry(Entry), Obj(std::move(Obj)) {} 71 72 std::size_t Size; 73 jit_code_entry *Entry; 74 OwningBinary<ObjectFile> Obj; 75 }; 76 77 // Buffer for an in-memory object file in executable memory 78 typedef llvm::DenseMap<JITEventListener::ObjectKey, RegisteredObjectInfo> 79 RegisteredObjectBufferMap; 80 81 /// Global access point for the JIT debugging interface designed for use with a 82 /// singleton toolbox. Handles thread-safe registration and deregistration of 83 /// object files that are in executable memory managed by the client of this 84 /// class. 85 class GDBJITRegistrationListener : public JITEventListener { 86 /// A map of in-memory object files that have been registered with the 87 /// JIT interface. 88 RegisteredObjectBufferMap ObjectBufferMap; 89 90 public: 91 /// Instantiates the JIT service. 92 GDBJITRegistrationListener() : ObjectBufferMap() {} 93 94 /// Unregisters each object that was previously registered and releases all 95 /// internal resources. 96 ~GDBJITRegistrationListener() override; 97 98 /// Creates an entry in the JIT registry for the buffer @p Object, 99 /// which must contain an object file in executable memory with any 100 /// debug information for the debugger. 101 void notifyObjectLoaded(ObjectKey K, const ObjectFile &Obj, 102 const RuntimeDyld::LoadedObjectInfo &L) override; 103 104 /// Removes the internal registration of @p Object, and 105 /// frees associated resources. 106 /// Returns true if @p Object was found in ObjectBufferMap. 107 void notifyFreeingObject(ObjectKey K) override; 108 109 private: 110 /// Deregister the debug info for the given object file from the debugger 111 /// and delete any temporary copies. This private method does not remove 112 /// the function from Map so that it can be called while iterating over Map. 113 void deregisterObjectInternal(RegisteredObjectBufferMap::iterator I); 114 }; 115 116 /// Lock used to serialize all jit registration events, since they 117 /// modify global variables. 118 ManagedStatic<sys::Mutex> JITDebugLock; 119 120 /// Do the registration. 121 void NotifyDebugger(jit_code_entry* JITCodeEntry) { 122 __jit_debug_descriptor.action_flag = JIT_REGISTER_FN; 123 124 // Insert this entry at the head of the list. 125 JITCodeEntry->prev_entry = nullptr; 126 jit_code_entry* NextEntry = __jit_debug_descriptor.first_entry; 127 JITCodeEntry->next_entry = NextEntry; 128 if (NextEntry) { 129 NextEntry->prev_entry = JITCodeEntry; 130 } 131 __jit_debug_descriptor.first_entry = JITCodeEntry; 132 __jit_debug_descriptor.relevant_entry = JITCodeEntry; 133 __jit_debug_register_code(); 134 } 135 136 GDBJITRegistrationListener::~GDBJITRegistrationListener() { 137 // Free all registered object files. 138 std::lock_guard<llvm::sys::Mutex> locked(*JITDebugLock); 139 for (RegisteredObjectBufferMap::iterator I = ObjectBufferMap.begin(), 140 E = ObjectBufferMap.end(); 141 I != E; ++I) { 142 // Call the private method that doesn't update the map so our iterator 143 // doesn't break. 144 deregisterObjectInternal(I); 145 } 146 ObjectBufferMap.clear(); 147 } 148 149 void GDBJITRegistrationListener::notifyObjectLoaded( 150 ObjectKey K, const ObjectFile &Obj, 151 const RuntimeDyld::LoadedObjectInfo &L) { 152 153 OwningBinary<ObjectFile> DebugObj = L.getObjectForDebug(Obj); 154 155 // Bail out if debug objects aren't supported. 156 if (!DebugObj.getBinary()) 157 return; 158 159 const char *Buffer = DebugObj.getBinary()->getMemoryBufferRef().getBufferStart(); 160 size_t Size = DebugObj.getBinary()->getMemoryBufferRef().getBufferSize(); 161 162 std::lock_guard<llvm::sys::Mutex> locked(*JITDebugLock); 163 assert(ObjectBufferMap.find(K) == ObjectBufferMap.end() && 164 "Second attempt to perform debug registration."); 165 jit_code_entry* JITCodeEntry = new jit_code_entry(); 166 167 if (!JITCodeEntry) { 168 llvm::report_fatal_error( 169 "Allocation failed when registering a JIT entry!\n"); 170 } else { 171 JITCodeEntry->symfile_addr = Buffer; 172 JITCodeEntry->symfile_size = Size; 173 174 ObjectBufferMap[K] = 175 RegisteredObjectInfo(Size, JITCodeEntry, std::move(DebugObj)); 176 NotifyDebugger(JITCodeEntry); 177 } 178 } 179 180 void GDBJITRegistrationListener::notifyFreeingObject(ObjectKey K) { 181 std::lock_guard<llvm::sys::Mutex> locked(*JITDebugLock); 182 RegisteredObjectBufferMap::iterator I = ObjectBufferMap.find(K); 183 184 if (I != ObjectBufferMap.end()) { 185 deregisterObjectInternal(I); 186 ObjectBufferMap.erase(I); 187 } 188 } 189 190 void GDBJITRegistrationListener::deregisterObjectInternal( 191 RegisteredObjectBufferMap::iterator I) { 192 193 jit_code_entry*& JITCodeEntry = I->second.Entry; 194 195 // Do the unregistration. 196 { 197 __jit_debug_descriptor.action_flag = JIT_UNREGISTER_FN; 198 199 // Remove the jit_code_entry from the linked list. 200 jit_code_entry* PrevEntry = JITCodeEntry->prev_entry; 201 jit_code_entry* NextEntry = JITCodeEntry->next_entry; 202 203 if (NextEntry) { 204 NextEntry->prev_entry = PrevEntry; 205 } 206 if (PrevEntry) { 207 PrevEntry->next_entry = NextEntry; 208 } 209 else { 210 assert(__jit_debug_descriptor.first_entry == JITCodeEntry); 211 __jit_debug_descriptor.first_entry = NextEntry; 212 } 213 214 // Tell the debugger which entry we removed, and unregister the code. 215 __jit_debug_descriptor.relevant_entry = JITCodeEntry; 216 __jit_debug_register_code(); 217 } 218 219 delete JITCodeEntry; 220 JITCodeEntry = nullptr; 221 } 222 223 llvm::ManagedStatic<GDBJITRegistrationListener> GDBRegListener; 224 225 } // end namespace 226 227 namespace llvm { 228 229 JITEventListener* JITEventListener::createGDBRegistrationListener() { 230 return &*GDBRegListener; 231 } 232 233 } // namespace llvm 234 235 LLVMJITEventListenerRef LLVMCreateGDBRegistrationListener(void) 236 { 237 return wrap(JITEventListener::createGDBRegistrationListener()); 238 } 239