1277323Sdim//===----- GDBRegistrationListener.cpp - Registers objects with GDB -------===// 2277323Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6277323Sdim// 7277323Sdim//===----------------------------------------------------------------------===// 8277323Sdim 9341825Sdim#include "llvm-c/ExecutionEngine.h" 10277323Sdim#include "llvm/ADT/DenseMap.h" 11277323Sdim#include "llvm/ExecutionEngine/JITEventListener.h" 12277323Sdim#include "llvm/Object/ObjectFile.h" 13277323Sdim#include "llvm/Support/Compiler.h" 14277323Sdim#include "llvm/Support/ErrorHandling.h" 15277323Sdim#include "llvm/Support/ManagedStatic.h" 16277323Sdim#include "llvm/Support/Mutex.h" 17360784Sdim#include <mutex> 18277323Sdim 19277323Sdimusing namespace llvm; 20277323Sdimusing namespace llvm::object; 21277323Sdim 22277323Sdim// This must be kept in sync with gdb/gdb/jit.h . 23277323Sdimextern "C" { 24277323Sdim 25277323Sdim typedef enum { 26277323Sdim JIT_NOACTION = 0, 27277323Sdim JIT_REGISTER_FN, 28277323Sdim JIT_UNREGISTER_FN 29277323Sdim } jit_actions_t; 30277323Sdim 31277323Sdim struct jit_code_entry { 32277323Sdim struct jit_code_entry *next_entry; 33277323Sdim struct jit_code_entry *prev_entry; 34277323Sdim const char *symfile_addr; 35277323Sdim uint64_t symfile_size; 36277323Sdim }; 37277323Sdim 38277323Sdim struct jit_descriptor { 39277323Sdim uint32_t version; 40277323Sdim // This should be jit_actions_t, but we want to be specific about the 41277323Sdim // bit-width. 42277323Sdim uint32_t action_flag; 43277323Sdim struct jit_code_entry *relevant_entry; 44277323Sdim struct jit_code_entry *first_entry; 45277323Sdim }; 46277323Sdim 47277323Sdim // We put information about the JITed function in this global, which the 48277323Sdim // debugger reads. Make sure to specify the version statically, because the 49277323Sdim // debugger checks the version before we can set it during runtime. 50277323Sdim struct jit_descriptor __jit_debug_descriptor = { 1, 0, nullptr, nullptr }; 51277323Sdim 52277323Sdim // Debuggers puts a breakpoint in this function. 53277323Sdim LLVM_ATTRIBUTE_NOINLINE void __jit_debug_register_code() { 54277323Sdim // The noinline and the asm prevent calls to this function from being 55277323Sdim // optimized out. 56277323Sdim#if !defined(_MSC_VER) 57277323Sdim asm volatile("":::"memory"); 58277323Sdim#endif 59277323Sdim } 60277323Sdim 61277323Sdim} 62277323Sdim 63277323Sdimnamespace { 64277323Sdim 65277323Sdimstruct RegisteredObjectInfo { 66277323Sdim RegisteredObjectInfo() {} 67277323Sdim 68277323Sdim RegisteredObjectInfo(std::size_t Size, jit_code_entry *Entry, 69277323Sdim OwningBinary<ObjectFile> Obj) 70277323Sdim : Size(Size), Entry(Entry), Obj(std::move(Obj)) {} 71277323Sdim 72277323Sdim std::size_t Size; 73277323Sdim jit_code_entry *Entry; 74277323Sdim OwningBinary<ObjectFile> Obj; 75277323Sdim}; 76277323Sdim 77277323Sdim// Buffer for an in-memory object file in executable memory 78344779Sdimtypedef llvm::DenseMap<JITEventListener::ObjectKey, RegisteredObjectInfo> 79344779Sdim RegisteredObjectBufferMap; 80277323Sdim 81277323Sdim/// Global access point for the JIT debugging interface designed for use with a 82277323Sdim/// singleton toolbox. Handles thread-safe registration and deregistration of 83277323Sdim/// object files that are in executable memory managed by the client of this 84277323Sdim/// class. 85277323Sdimclass GDBJITRegistrationListener : public JITEventListener { 86277323Sdim /// A map of in-memory object files that have been registered with the 87277323Sdim /// JIT interface. 88277323Sdim RegisteredObjectBufferMap ObjectBufferMap; 89277323Sdim 90277323Sdimpublic: 91277323Sdim /// Instantiates the JIT service. 92277323Sdim GDBJITRegistrationListener() : ObjectBufferMap() {} 93277323Sdim 94277323Sdim /// Unregisters each object that was previously registered and releases all 95277323Sdim /// internal resources. 96288943Sdim ~GDBJITRegistrationListener() override; 97277323Sdim 98277323Sdim /// Creates an entry in the JIT registry for the buffer @p Object, 99277323Sdim /// which must contain an object file in executable memory with any 100277323Sdim /// debug information for the debugger. 101344779Sdim void notifyObjectLoaded(ObjectKey K, const ObjectFile &Obj, 102344779Sdim const RuntimeDyld::LoadedObjectInfo &L) override; 103277323Sdim 104277323Sdim /// Removes the internal registration of @p Object, and 105277323Sdim /// frees associated resources. 106277323Sdim /// Returns true if @p Object was found in ObjectBufferMap. 107344779Sdim void notifyFreeingObject(ObjectKey K) override; 108277323Sdim 109277323Sdimprivate: 110277323Sdim /// Deregister the debug info for the given object file from the debugger 111277323Sdim /// and delete any temporary copies. This private method does not remove 112277323Sdim /// the function from Map so that it can be called while iterating over Map. 113277323Sdim void deregisterObjectInternal(RegisteredObjectBufferMap::iterator I); 114277323Sdim}; 115277323Sdim 116277323Sdim/// Lock used to serialize all jit registration events, since they 117277323Sdim/// modify global variables. 118277323SdimManagedStatic<sys::Mutex> JITDebugLock; 119277323Sdim 120277323Sdim/// Do the registration. 121277323Sdimvoid NotifyDebugger(jit_code_entry* JITCodeEntry) { 122277323Sdim __jit_debug_descriptor.action_flag = JIT_REGISTER_FN; 123277323Sdim 124277323Sdim // Insert this entry at the head of the list. 125277323Sdim JITCodeEntry->prev_entry = nullptr; 126277323Sdim jit_code_entry* NextEntry = __jit_debug_descriptor.first_entry; 127277323Sdim JITCodeEntry->next_entry = NextEntry; 128277323Sdim if (NextEntry) { 129277323Sdim NextEntry->prev_entry = JITCodeEntry; 130277323Sdim } 131277323Sdim __jit_debug_descriptor.first_entry = JITCodeEntry; 132277323Sdim __jit_debug_descriptor.relevant_entry = JITCodeEntry; 133277323Sdim __jit_debug_register_code(); 134277323Sdim} 135277323Sdim 136277323SdimGDBJITRegistrationListener::~GDBJITRegistrationListener() { 137277323Sdim // Free all registered object files. 138360784Sdim std::lock_guard<llvm::sys::Mutex> locked(*JITDebugLock); 139277323Sdim for (RegisteredObjectBufferMap::iterator I = ObjectBufferMap.begin(), 140277323Sdim E = ObjectBufferMap.end(); 141277323Sdim I != E; ++I) { 142277323Sdim // Call the private method that doesn't update the map so our iterator 143277323Sdim // doesn't break. 144277323Sdim deregisterObjectInternal(I); 145277323Sdim } 146277323Sdim ObjectBufferMap.clear(); 147277323Sdim} 148277323Sdim 149344779Sdimvoid GDBJITRegistrationListener::notifyObjectLoaded( 150344779Sdim ObjectKey K, const ObjectFile &Obj, 151344779Sdim const RuntimeDyld::LoadedObjectInfo &L) { 152277323Sdim 153344779Sdim OwningBinary<ObjectFile> DebugObj = L.getObjectForDebug(Obj); 154277323Sdim 155277323Sdim // Bail out if debug objects aren't supported. 156277323Sdim if (!DebugObj.getBinary()) 157277323Sdim return; 158277323Sdim 159277323Sdim const char *Buffer = DebugObj.getBinary()->getMemoryBufferRef().getBufferStart(); 160277323Sdim size_t Size = DebugObj.getBinary()->getMemoryBufferRef().getBufferSize(); 161277323Sdim 162360784Sdim std::lock_guard<llvm::sys::Mutex> locked(*JITDebugLock); 163344779Sdim assert(ObjectBufferMap.find(K) == ObjectBufferMap.end() && 164277323Sdim "Second attempt to perform debug registration."); 165277323Sdim jit_code_entry* JITCodeEntry = new jit_code_entry(); 166277323Sdim 167277323Sdim if (!JITCodeEntry) { 168277323Sdim llvm::report_fatal_error( 169277323Sdim "Allocation failed when registering a JIT entry!\n"); 170277323Sdim } else { 171277323Sdim JITCodeEntry->symfile_addr = Buffer; 172277323Sdim JITCodeEntry->symfile_size = Size; 173277323Sdim 174344779Sdim ObjectBufferMap[K] = 175344779Sdim RegisteredObjectInfo(Size, JITCodeEntry, std::move(DebugObj)); 176277323Sdim NotifyDebugger(JITCodeEntry); 177277323Sdim } 178277323Sdim} 179277323Sdim 180344779Sdimvoid GDBJITRegistrationListener::notifyFreeingObject(ObjectKey K) { 181360784Sdim std::lock_guard<llvm::sys::Mutex> locked(*JITDebugLock); 182344779Sdim RegisteredObjectBufferMap::iterator I = ObjectBufferMap.find(K); 183277323Sdim 184277323Sdim if (I != ObjectBufferMap.end()) { 185277323Sdim deregisterObjectInternal(I); 186277323Sdim ObjectBufferMap.erase(I); 187277323Sdim } 188277323Sdim} 189277323Sdim 190277323Sdimvoid GDBJITRegistrationListener::deregisterObjectInternal( 191277323Sdim RegisteredObjectBufferMap::iterator I) { 192277323Sdim 193277323Sdim jit_code_entry*& JITCodeEntry = I->second.Entry; 194277323Sdim 195277323Sdim // Do the unregistration. 196277323Sdim { 197277323Sdim __jit_debug_descriptor.action_flag = JIT_UNREGISTER_FN; 198277323Sdim 199277323Sdim // Remove the jit_code_entry from the linked list. 200277323Sdim jit_code_entry* PrevEntry = JITCodeEntry->prev_entry; 201277323Sdim jit_code_entry* NextEntry = JITCodeEntry->next_entry; 202277323Sdim 203277323Sdim if (NextEntry) { 204277323Sdim NextEntry->prev_entry = PrevEntry; 205277323Sdim } 206277323Sdim if (PrevEntry) { 207277323Sdim PrevEntry->next_entry = NextEntry; 208277323Sdim } 209277323Sdim else { 210277323Sdim assert(__jit_debug_descriptor.first_entry == JITCodeEntry); 211277323Sdim __jit_debug_descriptor.first_entry = NextEntry; 212277323Sdim } 213277323Sdim 214277323Sdim // Tell the debugger which entry we removed, and unregister the code. 215277323Sdim __jit_debug_descriptor.relevant_entry = JITCodeEntry; 216277323Sdim __jit_debug_register_code(); 217277323Sdim } 218277323Sdim 219277323Sdim delete JITCodeEntry; 220277323Sdim JITCodeEntry = nullptr; 221277323Sdim} 222277323Sdim 223277323Sdimllvm::ManagedStatic<GDBJITRegistrationListener> GDBRegListener; 224277323Sdim 225277323Sdim} // end namespace 226277323Sdim 227277323Sdimnamespace llvm { 228277323Sdim 229277323SdimJITEventListener* JITEventListener::createGDBRegistrationListener() { 230277323Sdim return &*GDBRegListener; 231277323Sdim} 232277323Sdim 233277323Sdim} // namespace llvm 234341825Sdim 235341825SdimLLVMJITEventListenerRef LLVMCreateGDBRegistrationListener(void) 236341825Sdim{ 237341825Sdim return wrap(JITEventListener::createGDBRegistrationListener()); 238341825Sdim} 239