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