1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
//===-- JITDebugRegisterer.cpp - Register debug symbols for JIT -----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines a JITDebugRegisterer object that is used by the JIT to
// register debug info with debuggers like GDB.
//
//===----------------------------------------------------------------------===//
#include "JITDebugRegisterer.h"
#include "../../CodeGen/ELF.h"
#include "../../CodeGen/ELFWriter.h"
#include "llvm/LLVMContext.h"
#include "llvm/Function.h"
#include "llvm/Module.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/MutexGuard.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Mutex.h"
#include <string>
namespace llvm {
// This must be kept in sync with gdb/gdb/jit.h .
extern "C" {
// Debuggers puts a breakpoint in this function.
LLVM_ATTRIBUTE_NOINLINE void __jit_debug_register_code() { }
// We put information about the JITed function in this global, which the
// debugger reads. Make sure to specify the version statically, because the
// debugger checks the version before we can set it during runtime.
struct jit_descriptor __jit_debug_descriptor = { 1, 0, 0, 0 };
}
namespace {
/// JITDebugLock - Used to serialize all code registration events, since they
/// modify global variables.
sys::Mutex JITDebugLock;
}
JITDebugRegisterer::JITDebugRegisterer(TargetMachine &tm) : TM(tm), FnMap() { }
JITDebugRegisterer::~JITDebugRegisterer() {
// Free all ELF memory.
for (RegisteredFunctionsMap::iterator I = FnMap.begin(), E = FnMap.end();
I != E; ++I) {
// Call the private method that doesn't update the map so our iterator
// doesn't break.
UnregisterFunctionInternal(I);
}
FnMap.clear();
}
std::string JITDebugRegisterer::MakeELF(const Function *F, DebugInfo &I) {
// Stack allocate an empty module with an empty LLVMContext for the ELFWriter
// API. We don't use the real module because then the ELFWriter would write
// out unnecessary GlobalValues during finalization.
LLVMContext Context;
Module M("", Context);
// Make a buffer for the ELF in memory.
std::string Buffer;
raw_string_ostream O(Buffer);
ELFWriter EW(O, TM);
EW.doInitialization(M);
// Copy the binary into the .text section. This isn't necessary, but it's
// useful to be able to disassemble the ELF by hand.
ELFSection &Text = EW.getTextSection(const_cast<Function *>(F));
Text.Addr = (uint64_t)I.FnStart;
// TODO: We could eliminate this copy if we somehow used a pointer/size pair
// instead of a vector.
Text.getData().assign(I.FnStart, I.FnEnd);
// Copy the exception handling call frame information into the .eh_frame
// section. This allows GDB to get a good stack trace, particularly on
// linux x86_64. Mark this as a PROGBITS section that needs to be loaded
// into memory at runtime.
ELFSection &EH = EW.getSection(".eh_frame", ELF::SHT_PROGBITS,
ELF::SHF_ALLOC);
// Pointers in the DWARF EH info are all relative to the EH frame start,
// which is stored here.
EH.Addr = (uint64_t)I.EhStart;
// TODO: We could eliminate this copy if we somehow used a pointer/size pair
// instead of a vector.
EH.getData().assign(I.EhStart, I.EhEnd);
// Add this single function to the symbol table, so the debugger prints the
// name instead of '???'. We give the symbol default global visibility.
ELFSym *FnSym = ELFSym::getGV(F,
ELF::STB_GLOBAL,
ELF::STT_FUNC,
ELF::STV_DEFAULT);
FnSym->SectionIdx = Text.SectionIdx;
FnSym->Size = I.FnEnd - I.FnStart;
FnSym->Value = 0; // Offset from start of section.
EW.SymbolList.push_back(FnSym);
EW.doFinalization(M);
O.flush();
// When trying to debug why GDB isn't getting the debug info right, it's
// awfully helpful to write the object file to disk so that it can be
// inspected with readelf and objdump.
if (JITEmitDebugInfoToDisk) {
std::string Filename;
raw_string_ostream O2(Filename);
O2 << "/tmp/llvm_function_" << I.FnStart << "_" << F->getNameStr() << ".o";
O2.flush();
std::string Errors;
raw_fd_ostream O3(Filename.c_str(), Errors);
O3 << Buffer;
O3.close();
}
return Buffer;
}
void JITDebugRegisterer::RegisterFunction(const Function *F, DebugInfo &I) {
// TODO: Support non-ELF platforms.
if (!TM.getELFWriterInfo())
return;
std::string Buffer = MakeELF(F, I);
jit_code_entry *JITCodeEntry = new jit_code_entry();
JITCodeEntry->symfile_addr = Buffer.c_str();
JITCodeEntry->symfile_size = Buffer.size();
// Add a mapping from F to the entry and buffer, so we can delete this
// info later.
FnMap[F] = std::make_pair(Buffer, JITCodeEntry);
// Acquire the lock and do the registration.
{
MutexGuard locked(JITDebugLock);
__jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
// Insert this entry at the head of the list.
JITCodeEntry->prev_entry = NULL;
jit_code_entry *NextEntry = __jit_debug_descriptor.first_entry;
JITCodeEntry->next_entry = NextEntry;
if (NextEntry != NULL) {
NextEntry->prev_entry = JITCodeEntry;
}
__jit_debug_descriptor.first_entry = JITCodeEntry;
__jit_debug_descriptor.relevant_entry = JITCodeEntry;
__jit_debug_register_code();
}
}
void JITDebugRegisterer::UnregisterFunctionInternal(
RegisteredFunctionsMap::iterator I) {
jit_code_entry *&JITCodeEntry = I->second.second;
// Acquire the lock and do the unregistration.
{
MutexGuard locked(JITDebugLock);
__jit_debug_descriptor.action_flag = JIT_UNREGISTER_FN;
// Remove the jit_code_entry from the linked list.
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;
}
// Tell GDB which entry we removed, and unregister the code.
__jit_debug_descriptor.relevant_entry = JITCodeEntry;
__jit_debug_register_code();
}
delete JITCodeEntry;
JITCodeEntry = NULL;
// Free the ELF file in memory.
std::string &Buffer = I->second.first;
Buffer.clear();
}
void JITDebugRegisterer::UnregisterFunction(const Function *F) {
// TODO: Support non-ELF platforms.
if (!TM.getELFWriterInfo())
return;
RegisteredFunctionsMap::iterator I = FnMap.find(F);
if (I == FnMap.end()) return;
UnregisterFunctionInternal(I);
FnMap.erase(I);
}
} // end namespace llvm
|