GDBRegistrationListener.cpp [plain text]
#include "llvm/ADT/DenseMap.h"
#include "llvm/ExecutionEngine/JITEventListener.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/MutexGuard.h"
using namespace llvm;
using namespace llvm::object;
extern "C" {
typedef enum {
JIT_NOACTION = 0,
JIT_REGISTER_FN,
JIT_UNREGISTER_FN
} jit_actions_t;
struct jit_code_entry {
struct jit_code_entry *next_entry;
struct jit_code_entry *prev_entry;
const char *symfile_addr;
uint64_t symfile_size;
};
struct jit_descriptor {
uint32_t version;
uint32_t action_flag;
struct jit_code_entry *relevant_entry;
struct jit_code_entry *first_entry;
};
struct jit_descriptor __jit_debug_descriptor = { 1, 0, nullptr, nullptr };
LLVM_ATTRIBUTE_NOINLINE void __jit_debug_register_code() {
#if !defined(_MSC_VER)
asm volatile("":::"memory");
#endif
}
}
namespace {
struct RegisteredObjectInfo {
RegisteredObjectInfo() {}
RegisteredObjectInfo(std::size_t Size, jit_code_entry *Entry,
OwningBinary<ObjectFile> Obj)
: Size(Size), Entry(Entry), Obj(std::move(Obj)) {}
RegisteredObjectInfo(RegisteredObjectInfo &&Other)
: Size(Other.Size), Entry(Other.Entry), Obj(std::move(Other.Obj)) {}
RegisteredObjectInfo& operator=(RegisteredObjectInfo &&Other) {
Size = Other.Size;
Entry = Other.Entry;
Obj = std::move(Other.Obj);
return *this;
}
std::size_t Size;
jit_code_entry *Entry;
OwningBinary<ObjectFile> Obj;
};
typedef llvm::DenseMap< const char*, RegisteredObjectInfo>
RegisteredObjectBufferMap;
class GDBJITRegistrationListener : public JITEventListener {
RegisteredObjectBufferMap ObjectBufferMap;
public:
GDBJITRegistrationListener() : ObjectBufferMap() {}
virtual ~GDBJITRegistrationListener();
void NotifyObjectEmitted(const ObjectFile &Object,
const RuntimeDyld::LoadedObjectInfo &L) override;
void NotifyFreeingObject(const ObjectFile &Object) override;
private:
void deregisterObjectInternal(RegisteredObjectBufferMap::iterator I);
};
ManagedStatic<sys::Mutex> JITDebugLock;
void NotifyDebugger(jit_code_entry* JITCodeEntry) {
__jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
JITCodeEntry->prev_entry = nullptr;
jit_code_entry* NextEntry = __jit_debug_descriptor.first_entry;
JITCodeEntry->next_entry = NextEntry;
if (NextEntry) {
NextEntry->prev_entry = JITCodeEntry;
}
__jit_debug_descriptor.first_entry = JITCodeEntry;
__jit_debug_descriptor.relevant_entry = JITCodeEntry;
__jit_debug_register_code();
}
GDBJITRegistrationListener::~GDBJITRegistrationListener() {
llvm::MutexGuard locked(*JITDebugLock);
for (RegisteredObjectBufferMap::iterator I = ObjectBufferMap.begin(),
E = ObjectBufferMap.end();
I != E; ++I) {
deregisterObjectInternal(I);
}
ObjectBufferMap.clear();
}
void GDBJITRegistrationListener::NotifyObjectEmitted(
const ObjectFile &Object,
const RuntimeDyld::LoadedObjectInfo &L) {
OwningBinary<ObjectFile> DebugObj = L.getObjectForDebug(Object);
if (!DebugObj.getBinary())
return;
const char *Buffer = DebugObj.getBinary()->getMemoryBufferRef().getBufferStart();
size_t Size = DebugObj.getBinary()->getMemoryBufferRef().getBufferSize();
const char *Key = Object.getMemoryBufferRef().getBufferStart();
assert(Key && "Attempt to register a null object with a debugger.");
llvm::MutexGuard locked(*JITDebugLock);
assert(ObjectBufferMap.find(Key) == ObjectBufferMap.end() &&
"Second attempt to perform debug registration.");
jit_code_entry* JITCodeEntry = new jit_code_entry();
if (!JITCodeEntry) {
llvm::report_fatal_error(
"Allocation failed when registering a JIT entry!\n");
} else {
JITCodeEntry->symfile_addr = Buffer;
JITCodeEntry->symfile_size = Size;
ObjectBufferMap[Key] = RegisteredObjectInfo(Size, JITCodeEntry,
std::move(DebugObj));
NotifyDebugger(JITCodeEntry);
}
}
void GDBJITRegistrationListener::NotifyFreeingObject(const ObjectFile& Object) {
const char *Key = Object.getMemoryBufferRef().getBufferStart();
llvm::MutexGuard locked(*JITDebugLock);
RegisteredObjectBufferMap::iterator I = ObjectBufferMap.find(Key);
if (I != ObjectBufferMap.end()) {
deregisterObjectInternal(I);
ObjectBufferMap.erase(I);
}
}
void GDBJITRegistrationListener::deregisterObjectInternal(
RegisteredObjectBufferMap::iterator I) {
jit_code_entry*& JITCodeEntry = I->second.Entry;
{
__jit_debug_descriptor.action_flag = JIT_UNREGISTER_FN;
jit_code_entry* PrevEntry = JITCodeEntry->prev_entry;
jit_code_entry* NextEntry = JITCodeEntry->next_entry;
if (NextEntry) {
NextEntry->prev_entry = PrevEntry;
}
if (PrevEntry) {
PrevEntry->next_entry = NextEntry;
}
else {
assert(__jit_debug_descriptor.first_entry == JITCodeEntry);
__jit_debug_descriptor.first_entry = NextEntry;
}
__jit_debug_descriptor.relevant_entry = JITCodeEntry;
__jit_debug_register_code();
}
delete JITCodeEntry;
JITCodeEntry = nullptr;
}
llvm::ManagedStatic<GDBJITRegistrationListener> GDBRegListener;
}
namespace llvm {
JITEventListener* JITEventListener::createGDBRegistrationListener() {
return &*GDBRegListener;
}
}