summaryrefslogtreecommitdiff
path: root/lib/ExecutionEngine/JIT/JITDebugRegisterer.cpp
blob: 2e9096883be3372ec2c196917e75bcaabace9b7a (plain)
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->getName() << ".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