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