[RFA] Add interface for registering JITed code

Reid Kleckner
2009-07-22T23:16:18Z

[-- Attachment #1: Type: text/plain, Size: 4449 bytes --]

Hi,

I'm working on unladen swallow, and we're trying to speed up Python by
using the LLVM JIT. However, when we merge back to mainline, we want
developers to be able to debug CPython with GDB. So that means we
need LLVM to generate dwarf debug info, and we need to register it
with GDB. After talking with some GDB developers here at Google, we
decided the best way to do this was to mirror the dlopen/dlclose
interface.

On the LLVM side, for each JITed function we create a small ELF in
memory with the debug info and symbol. LLVM then writes a little code
entry struct describing the ELF, adds it to a linked list, and calls
__jit_debug_register_code.

I've added a breakpoint at __jit_debug_register_code and a
corresponding event handler on the GDB side, which then reads the code
entry out of another special global symbol (__jit_debug_descriptor).
GDB then copies over the ELF and creates a BFD with it in memory, as
is done in symbol-file-from-memory. Then it can call
add_symbol_file_from_bfd with the BFD, and life is good.

If GDB attaches while the program is running, it reads the linked list
of code entries from the descriptor and registers each ELF as above.

If LLVM frees machine code, then it sets the action enum in the
descriptor to JIT_UNREGISTER, points the descriptor at the relevant
code entry, and calls __jit_debug_register_code again. This way, GDB
can turn around and free the corresponding object file. It's a nicer
interface than the shared library interface because it actually passes
the relevant entry, so you don't have to iterate over the linked list
in the inferior.

Finally, if the inferior exits, GDB goes through and tosses out all
the JITed object files.

One nice thing about this interface is that we don't have to reinvent
another "file" format to encode the debug information, but it is
annoying that it requires the JIT to link in an object file writer.
Right now LLVM only has call frame information support (which they use
for dwarf exception handling), but they have plans to add more after
this summer. With this interface, we don't have to change anything on
the GDB side when that happens.

Here is a demo of what this does on x86_64, which relies on call frame
information to produce a backtrace.

Without the interface, the backtrace is totally garbled (it has way
too many frames) in addition to not having symbols:

[rnk@knuckles llvm-gdb-64]$ gdb Debug/bin/lli
GNU gdb 6.8-gg16
...
(gdb) run t.bc
...
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff7fd86f0 (LWP 15017)]
(gdb) bt
#0 0x00007ffff61441a4 in ?? ()
#1 0x0000000000000003 in ?? ()
#2 0x0000000000000004 in ?? ()
#3 0x00037ffff5f43fd0 in ?? ()
#4 0x00007ffff614411c in ?? ()
#5 0x00027fff00000003 in ?? ()
#6 0x00007ffff61440aa in ?? ()
#7 0x01000002f5f43ff0 in ?? ()
#8 0x00007ffff614402c in ?? ()
#9 0x0100000000000001 in ?? ()
#10 0x0000000001438a40 in ?? ()
#11 0x00007fff00000001 in ?? ()
#12 0x0000000000b84d63 in llvm::JIT::runFunction (this=0x1405900, F=0x1402e10,
ArgValues=@0x7fffffffdfd0)
at /home/rnk/llvm-gdb/lib/ExecutionEngine/JIT/JIT.cpp:411
#13 0x0000000000ba8985 in llvm::ExecutionEngine::runFunctionAsMain (
this=0x1405900, Fn=0x1402e10, argv=@0x13efab8, envp=0x7fffffffe3a0)
at /home/rnk/llvm-gdb/lib/ExecutionEngine/ExecutionEngine.cpp:378
#14 0x00000000007e8635 in main (argc=2, argv=0x7fffffffe388,
envp=0x7fffffffe3a0) at /home/rnk/llvm-gdb/tools/lli/lli.cpp:220
(gdb)

With the interface, it has symbols and no extra frames:

[rnk@knuckles llvm-gdb-64]$ ../gdb-jit-64/gdb/gdb Debug/bin/lli
...
(gdb) run t.bc
...
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff61441a4 in baz ()
(gdb) bt
#0 0x00007ffff61441a4 in baz ()
#1 0x00007ffff614411c in bar ()
#2 0x00007ffff61440aa in foo ()
#3 0x00007ffff614402c in main ()
#4 0x0000000000b84d63 in llvm::JIT::runFunction (this=0x1405900, F=0x1402e10,
ArgValues=...) at /home/rnk/llvm-gdb/lib/ExecutionEngine/JIT/JIT.cpp:411
#5 0x0000000000ba8985 in llvm::ExecutionEngine::runFunctionAsMain (
this=0x1405900, Fn=0x1402e10, argv=..., envp=0x7fffffffe390)
at /home/rnk/llvm-gdb/lib/ExecutionEngine/ExecutionEngine.cpp:378
#6 0x00000000007e8635 in main (argc=2, argv=0x7fffffffe378,
envp=0x7fffffffe390) at /home/rnk/llvm-gdb/tools/lli/lli.cpp:220
(gdb)

I've tested this on x86_64 debugging both 64-bit and 32-bit inferiors.

Please review!

Thanks,
Reid

[-- Attachment #2: jit-patch.txt --]
[-- Type: text/plain, Size: 31372 bytes --]

2009-07-22 Reid Kleckner <reid@kleckner.net>

Add interface for JIT code generation.
* Makefile.in (SFILES): Add jit.c.
(HFILES_NO_SRCDIR): Add jit.h.
(COMMON_OBS): Add jit.o.
* jit.c: New file.
* jit.h: New file.
* breakpoint.h:
(enum bptype): Add bp_jit_event to enum.
* breakpoint.c:
(bpstat_what): Update event table for bp_jit_event.
(print_it_typical): Added case for bp_jit_event.
(print_one_breakpoint_location): Added case for bp_jit_event.
(mention): Added case for bp_jit_event.
(delete_command): Added case for bp_jit_event.
(breakpoint_re_set_one): Added case for bp_jit_event.
(create_jit_event_breakpoint): New.
* infrun.c (handle_inferior_event): Add handler for jit event.
* symfile.h:
(symbol_file_add_from_local_memory): New.
(symbol_file_add_from_memory): Added declaration.
(symbol_file_guess_template): New.
* symfile-mem.c:
(bfd_open_from_memory): New.
(symbol_file_add_from_local_memory): New.
(symbol_file_add_from_memory): Modified to use new common code.
(symbol_file_add_from_memory_common): New.
(symbol_file_guess_template): New.
* objfiles.c (free_objfile): Fixed a memory leak.

Index: gdb/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.1086
diff -u -p -u -r1.1086 Makefile.in
--- gdb/Makefile.in 28 May 2009 01:05:13 -0000 1.1086
+++ gdb/Makefile.in 22 Jul 2009 22:26:07 -0000
@@ -673,7 +673,8 @@ SFILES = ada-exp.y ada-lang.c ada-typepr
wrapper.c \
xml-tdesc.c xml-support.c \
inferior.c gdb_usleep.c \
- record.c
+ record.c \
+ jit.c

LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c

@@ -744,7 +745,7 @@ config/rs6000/nm-rs6000.h top.h bsd-kvm.
annotate.h sim-regno.h dictionary.h dfp.h main.h frame-unwind.h \
remote-fileio.h i386-linux-tdep.h vax-tdep.h objc-lang.h \
sentinel-frame.h bcache.h symfile.h windows-tdep.h linux-tdep.h \
-gdb_usleep.h
+gdb_usleep.h jit.h

# Header files that already have srcdir in them, or which are in objdir.

@@ -825,7 +826,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $
solib.o solib-null.o \
prologue-value.o memory-map.o xml-support.o \
target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
- inferior.o osdata.o gdb_usleep.o record.o
+ inferior.o osdata.o gdb_usleep.o record.o \
+ jit.o

TSOBS = inflow.o

Index: gdb/breakpoint.c
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.c,v
retrieving revision 1.403
diff -u -p -u -r1.403 breakpoint.c
--- gdb/breakpoint.c 8 Jun 2009 16:05:14 -0000 1.403
+++ gdb/breakpoint.c 22 Jul 2009 22:26:08 -0000
@@ -2510,6 +2510,7 @@ print_it_typical (bpstat bs)
case bp_watchpoint_scope:
case bp_call_dummy:
case bp_tracepoint:
+ case bp_jit_event:
default:
result = PRINT_UNKNOWN;
break;
@@ -3232,6 +3233,9 @@ bpstat_what (bpstat bs)
/* We hit the shared library event breakpoint. */
shlib_event,

+ /* We hit the jit event breakpoint. */
+ jit_event,
+
/* This is just used to count how many enums there are. */
class_last
};
@@ -3247,6 +3251,7 @@ bpstat_what (bpstat bs)
#define clr BPSTAT_WHAT_CLEAR_LONGJMP_RESUME
#define sr BPSTAT_WHAT_STEP_RESUME
#define shl BPSTAT_WHAT_CHECK_SHLIBS
+#define jit BPSTAT_WHAT_CHECK_JIT

/* "Can't happen." Might want to print an error message.
abort() is not out of the question, but chances are GDB is just
@@ -3267,12 +3272,13 @@ bpstat_what (bpstat bs)
back and decide something of a lower priority is better. The
ordering is:

- kc < clr sgl shl slr sn sr ss
- sgl < shl slr sn sr ss
- slr < err shl sn sr ss
- clr < err shl sn sr ss
- ss < shl sn sr
- sn < shl sr
+ kc < jit clr sgl shl slr sn sr ss
+ sgl < jit shl slr sn sr ss
+ slr < jit err shl sn sr ss
+ clr < jit err shl sn sr ss
+ ss < jit shl sn sr
+ sn < jit shl sr
+ jit < shl sr
shl < sr
sr <

@@ -3290,28 +3296,18 @@ bpstat_what (bpstat bs)
table[(int) class_last][(int) BPSTAT_WHAT_LAST] =
{
/* old action */
- /* kc ss sn sgl slr clr sr shl
- */
-/*no_effect */
- {kc, ss, sn, sgl, slr, clr, sr, shl},
-/*wp_silent */
- {ss, ss, sn, ss, ss, ss, sr, shl},
-/*wp_noisy */
- {sn, sn, sn, sn, sn, sn, sr, shl},
-/*bp_nostop */
- {sgl, ss, sn, sgl, slr, slr, sr, shl},
-/*bp_silent */
- {ss, ss, sn, ss, ss, ss, sr, shl},
-/*bp_noisy */
- {sn, sn, sn, sn, sn, sn, sr, shl},
-/*long_jump */
- {slr, ss, sn, slr, slr, err, sr, shl},
-/*long_resume */
- {clr, ss, sn, err, err, err, sr, shl},
-/*step_resume */
- {sr, sr, sr, sr, sr, sr, sr, sr},
-/*shlib */
- {shl, shl, shl, shl, shl, shl, sr, shl}
+ /* kc ss sn sgl slr clr sr shl jit */
+/* no_effect */ {kc, ss, sn, sgl, slr, clr, sr, shl, jit},
+/* wp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit},
+/* wp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit},
+/* bp_nostop */ {sgl, ss, sn, sgl, slr, slr, sr, shl, jit},
+/* bp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit},
+/* bp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit},
+/* long_jump */ {slr, ss, sn, slr, slr, err, sr, shl, jit},
+/* long_resume */ {clr, ss, sn, err, err, err, sr, shl, jit},
+/* step_resume */ {sr, sr, sr, sr, sr, sr, sr, sr, sr },
+/* shlib */ {shl, shl, shl, shl, shl, shl, sr, shl, shl},
+/* jit_event */ {jit, jit, jit, jit, jit, jit, sr, jit, jit}
};

#undef kc
@@ -3324,6 +3320,7 @@ bpstat_what (bpstat bs)
#undef sr
#undef ts
#undef shl
+#undef jit
enum bpstat_what_main_action current_action = BPSTAT_WHAT_KEEP_CHECKING;
struct bpstat_what retval;

@@ -3394,6 +3391,9 @@ bpstat_what (bpstat bs)
case bp_shlib_event:
bs_class = shlib_event;
break;
+ case bp_jit_event:
+ bs_class = jit_event;
+ break;
case bp_thread_event:
case bp_overlay_event:
bs_class = bp_nostop;
@@ -3524,6 +3524,7 @@ print_one_breakpoint_location (struct br
{bp_overlay_event, "overlay events"},
{bp_catchpoint, "catchpoint"},
{bp_tracepoint, "tracepoint"},
+ {bp_jit_event, "jit events"},
};

static char bpenables[] = "nynny";
@@ -3651,6 +3652,7 @@ print_one_breakpoint_location (struct br
case bp_thread_event:
case bp_overlay_event:
case bp_tracepoint:
+ case bp_jit_event:
if (opts.addressprint)
{
annotate_field (4);
@@ -4266,6 +4268,7 @@ allocate_bp_location (struct breakpoint
case bp_shlib_event:
case bp_thread_event:
case bp_overlay_event:
+ case bp_jit_event:
loc->loc_type = bp_loc_software_breakpoint;
break;
case bp_hardware_breakpoint:
@@ -4530,6 +4533,16 @@ struct lang_and_radix
};

+struct breakpoint *
+create_jit_event_breakpoint (CORE_ADDR address)
+{
+ struct breakpoint *b;
+
+ b = create_internal_breakpoint (address, bp_jit_event);
+ update_global_location_list_nothrow (1);
+ return b;
+}
+
void
remove_solib_event_breakpoints (void)
{
@@ -5155,6 +5168,7 @@ mention (struct breakpoint *b)
case bp_shlib_event:
case bp_thread_event:
case bp_overlay_event:
+ case bp_jit_event:
break;
}

@@ -7398,6 +7412,7 @@ delete_command (char *arg, int from_tty)
{
if (b->type != bp_call_dummy &&
b->type != bp_shlib_event &&
+ b->type != bp_jit_event &&
b->type != bp_thread_event &&
b->type != bp_overlay_event &&
b->number >= 0)
@@ -7415,6 +7430,7 @@ delete_command (char *arg, int from_tty)
{
if (b->type != bp_call_dummy &&
b->type != bp_shlib_event &&
+ b->type != bp_jit_event &&
b->type != bp_thread_event &&
b->type != bp_overlay_event &&
b->number >= 0)
@@ -7736,6 +7752,7 @@ breakpoint_re_set_one (void *bint)
case bp_step_resume:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_jit_event:
break;
}

Index: gdb/breakpoint.h
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.h,v
retrieving revision 1.92
diff -u -p -u -r1.92 breakpoint.h
--- gdb/breakpoint.h 24 May 2009 18:00:08 -0000 1.92
+++ gdb/breakpoint.h 22 Jul 2009 22:26:08 -0000
@@ -113,6 +113,9 @@ enum bptype
bp_catchpoint,

bp_tracepoint,
+
+ /* Event for JIT compiled code generation or deletion. */
+ bp_jit_event,
};

/* States of enablement of breakpoint. */
@@ -535,6 +538,9 @@ enum bpstat_what_main_action
keep checking. */
BPSTAT_WHAT_CHECK_SHLIBS,

+ /* Check for new JITed code. */
+ BPSTAT_WHAT_CHECK_JIT,
+
/* This is just used to keep track of how many enums there are. */
BPSTAT_WHAT_LAST
};
@@ -827,6 +833,8 @@ extern void mark_breakpoints_out (void);

extern void make_breakpoint_permanent (struct breakpoint *);

+extern struct breakpoint *create_jit_event_breakpoint (CORE_ADDR);
+
extern struct breakpoint *create_solib_event_breakpoint (CORE_ADDR);

extern struct breakpoint *create_thread_event_breakpoint (CORE_ADDR);
Index: gdb/infrun.c
===================================================================
RCS file: /cvs/src/src/gdb/infrun.c,v
retrieving revision 1.385
diff -u -p -u -r1.385 infrun.c
--- gdb/infrun.c 7 Jun 2009 16:46:48 -0000 1.385
+++ gdb/infrun.c 22 Jul 2009 22:26:08 -0000
@@ -49,6 +49,7 @@
#include "mi/mi-common.h"
#include "event-top.h"
#include "record.h"
+#include "jit.h"

/* Prototypes for local functions */

@@ -3462,6 +3463,22 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
}
break;

+ case BPSTAT_WHAT_CHECK_JIT:
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_CHECK_JIT\n");
+
+ /* Switch terminal for any messages produced by breakpoint_re_set. */
+ target_terminal_ours_for_output ();
+
+ jit_event_handler ();
+
+ target_terminal_inferior ();
+
+ /* We want to step over this breakpoint, then keep going. */
+ ecs->event_thread->stepping_over_breakpoint = 1;
+
+ break;
+
case BPSTAT_WHAT_LAST:
/* Not a real code, but listed here to shut up gcc -Wall. */

Index: gdb/jit.c
===================================================================
RCS file: gdb/jit.c
diff -N gdb/jit.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/jit.c 22 Jul 2009 22:26:08 -0000
@@ -0,0 +1,300 @@
+/* Handle JIT code generation in the inferior for GDB, the GNU Debugger.
+
+ Copyright (C) 2009
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+
+#include "jit.h"
+#include "breakpoint.h"
+#include "gdbcore.h"
+#include "observer.h"
+#include "objfiles.h"
+#include "symfile.h"
+#include "symtab.h"
+#include "target.h"
+
+static const char *const jit_break_name = "__jit_debug_register_code";
+
+static const char *const jit_descriptor_name = "__jit_debug_descriptor";
+
+/* This is the address of the JIT descriptor in the inferior. */
+
+static CORE_ADDR jit_descriptor_addr;
+
+/* This is a linked list mapping code entry addresses to objfiles. */
+
+static struct jit_obj_link *jit_objfiles = NULL;
+
+/* Helper method for reading the global JIT descriptor from remote memory. */
+
+static int
+jit_read_descriptor (struct jit_descriptor *descriptor)
+{
+ int err;
+ struct type *ptr_type;
+ int ptr_size;
+ int desc_size;
+ gdb_byte *desc_buf;
+
+ /* Figure out how big the descriptor is on the remote and how to read it. */
+ ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+ ptr_size = TYPE_LENGTH (ptr_type);
+ desc_size = 8 + 2 * ptr_size; /* Two 32-bit ints and two pointers. */
+ desc_buf = alloca (desc_size);
+
+ /* Read the descriptor. */
+ err = target_read_memory (jit_descriptor_addr, desc_buf, desc_size);
+ if (err)
+ {
+ error (_("Unable to read JIT descriptor from remote memory!\n"));
+ return err;
+ }
+
+ /* Fix the endianness to match the host. */
+ descriptor->version = extract_unsigned_integer (&desc_buf[0], 4);
+ descriptor->action_flag = extract_unsigned_integer (&desc_buf[4], 4);
+ descriptor->relevant_entry = extract_typed_address (&desc_buf[8], ptr_type);
+ descriptor->first_entry = extract_typed_address
+ (&desc_buf[8 + ptr_size], ptr_type);
+
+ return 0;
+}
+
+/* Helper method for reading a JITed code entry from remote memory. */
+
+static int
+jit_read_code_entry (CORE_ADDR code_addr, struct jit_code_entry *code_entry)
+{
+ int err;
+ struct type *ptr_type;
+ int ptr_size;
+ int entry_size;
+ gdb_byte *entry_buf;
+
+ /* Figure out how big the entry is on the remote and how to read it. */
+ ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+ ptr_size = TYPE_LENGTH (ptr_type);
+ entry_size = 3 * ptr_size + 8; /* Three pointers and one 64-bit int. */
+ entry_buf = alloca (entry_size);
+
+ /* Read the entry. */
+ err = target_read_memory (code_addr, entry_buf, entry_size);
+ if (err)
+ {
+ error (_("Unable to read JIT code entry from remote memory!\n"));
+ return err;
+ }
+
+ /* Fix the endianness to match the host. */
+ ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+ code_entry->next_entry = extract_typed_address (&entry_buf[0], ptr_type);
+ code_entry->prev_entry = extract_typed_address
+ (&entry_buf[ptr_size], ptr_type);
+ code_entry->symfile_addr = extract_typed_address
+ (&entry_buf[2 * ptr_size], ptr_type);
+ code_entry->symfile_size = extract_unsigned_integer
+ (&entry_buf[3 * ptr_size], 8);
+
+ return 0;
+}
+
+/* This function registers code associated with a JIT code entry. It uses the
+ pointer and size pair in the entry to read the symbol file from the remote
+ and then calls symbol_file_add_from_local_memory to add it as though it were
+ a symbol file added by the user. */
+
+static void
+jit_register_code (CORE_ADDR entry_addr, struct jit_code_entry *code_entry)
+{
+ bfd *templ;
+ bfd *nbfd;
+ bfd_byte *buffer;
+ bfd_size_type size;
+ int err;
+ struct section_addr_info *sai;
+ struct objfile *objfile;
+ struct jit_obj_link *link;
+
+ /* We need some representative bfd to know the target we are looking at. */
+ templ = symbol_file_guess_template ();
+ if (templ == NULL)
+ error (_("\
+Cannot add symbol file for JITed code without an object file template."));
+
+ /* Read the symfile from remote memory. */
+ size = code_entry->symfile_size;
+ buffer = xmalloc (size);
+ err = target_read_memory (code_entry->symfile_addr, buffer, size);
+ if (err)
+ {
+ free (buffer);
+ error (_("Unable to read JITed symfile from remote memory."));
+ return;
+ }
+
+ /* Create a new BFD from the buffer in memory and add it as a symbol file. */
+ objfile = symbol_file_add_from_local_memory (templ, buffer, size);
+ if (objfile == NULL)
+ free (buffer);
+
+ /* Remember a mapping from entry_addr to objfile. */
+ link = xmalloc (sizeof (struct jit_obj_link));
+ link->next = jit_objfiles;
+ link->entry_addr = entry_addr;
+ link->objfile = objfile;
+ jit_objfiles = link;
+}
+
+/* This function unregisters JITed code and frees the corresponding objfile. */
+
+static void
+jit_unregister_code (struct jit_obj_link **last_ptr, struct jit_obj_link *link)
+{
+ /* Cut the link from the chain, and free both the link and the objfile. */
+ *last_ptr = link->next;
+ free_objfile (link->objfile);
+ xfree (link);
+}
+
+/* This function reads any existing code entries in the linked list and inserts
+ the breakpoint so we can listen for any more. */
+
+static void
+jit_inferior_created_hook (struct target_ops *objfile, int from_tty)
+{
+ struct minimal_symbol *reg_symbol;
+ struct minimal_symbol *desc_symbol;
+ CORE_ADDR reg_addr;
+ struct jit_descriptor descriptor;
+ struct jit_code_entry cur_entry;
+ CORE_ADDR cur_entry_addr;
+ int err;
+
+ /* Lookup the registration symbol. If it is missing, then we assume we are
+ not attached to a JIT. */
+ reg_symbol = lookup_minimal_symbol (jit_break_name, NULL, NULL);
+ if (reg_symbol == NULL)
+ return;
+ reg_addr = SYMBOL_VALUE_ADDRESS (reg_symbol);
+ if (reg_addr == (CORE_ADDR)NULL)
+ return;
+
+ /* Lookup the descriptor symbol and cache the addr. If it is missing, we
+ assume we are not attached to a JIT and return early. */
+ desc_symbol = lookup_minimal_symbol (jit_descriptor_name, NULL, NULL);
+ if (desc_symbol == NULL)
+ return;
+ jit_descriptor_addr = SYMBOL_VALUE_ADDRESS (desc_symbol);
+ if (jit_descriptor_addr == (CORE_ADDR)NULL)
+ return;
+
+ /* Read the descriptor so we can check the version number and load any already
+ JITed functions. */
+ err = jit_read_descriptor (&descriptor);
+ if (err) return;
+
+ /* Check that the version number agrees with that we support. */
+ if (descriptor.version != 1)
+ {
+ error (_("Unsupported JIT protocol version in descriptor!\n"));
+ return;
+ }
+
+ /* Put a breakpoint in the registration symbol. */
+ create_jit_event_breakpoint (reg_addr);
+
+ /* If we've attached to a running program, we need to check the descriptor to
+ register any functions that were already generated. */
+ for (cur_entry_addr = descriptor.first_entry;
+ cur_entry_addr != 0;
+ cur_entry_addr = cur_entry.next_entry)
+ {
+ err = jit_read_code_entry (cur_entry_addr, &cur_entry);
+ if (err) return;
+ jit_register_code (cur_entry_addr, &cur_entry);
+ }
+}
+
+/* This function cleans up any code entries left over when the inferior exits.
+ We get left over code when the inferior exits without unregistering its code,
+ for example when it crashes. */
+
+static void
+jit_inferior_exit_hook (int pid)
+{
+ while (jit_objfiles != NULL)
+ {
+ jit_unregister_code (&jit_objfiles, jit_objfiles);
+ }
+}
+
+void
+jit_event_handler (void)
+{
+ struct jit_descriptor descriptor;
+ struct jit_code_entry code_entry;
+ int err;
+ CORE_ADDR entry_addr;
+ struct jit_obj_link *link;
+ struct jit_obj_link **last_ptr;
+
+ /* Read the descriptor from remote memory. */
+ jit_read_descriptor (&descriptor);
+ entry_addr = descriptor.relevant_entry;
+
+ /* Do the corresponding action. */
+ switch (descriptor.action_flag)
+ {
+ case JIT_NOACTION:
+ break;
+ case JIT_REGISTER:
+ err = jit_read_code_entry (entry_addr, &code_entry);
+ if (err) return;
+ jit_register_code (entry_addr, &code_entry);
+ break;
+ case JIT_UNREGISTER:
+ /* Look up the link with this code entry address. */
+ for (last_ptr = &jit_objfiles, link = *last_ptr; link != NULL;
+ last_ptr = &link->next, link = *last_ptr)
+ {
+ if (link->entry_addr == entry_addr)
+ {
+ jit_unregister_code (last_ptr, link);
+ return;
+ }
+ }
+ printf_unfiltered ("Unable to find JITed code entry at address: %p\n",
+ (void*)entry_addr);
+ break;
+ default:
+ error (_("Unknown action_flag value in JIT descriptor!\n"));
+ break;
+ }
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+
+extern void _initialize_jit (void);
+
+void
+_initialize_jit (void)
+{
+ observer_attach_inferior_created (jit_inferior_created_hook);
+ observer_attach_inferior_exit (jit_inferior_exit_hook);
+}
Index: gdb/jit.h
===================================================================
RCS file: gdb/jit.h
diff -N gdb/jit.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/jit.h 22 Jul 2009 22:26:08 -0000
@@ -0,0 +1,81 @@
+/* JIT declarations for GDB, the GNU Debugger.
+
+ Copyright (C) 2009
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef JIT_H
+#define JIT_H
+
+/* When the JIT breakpoint fires, the inferior wants us to take one of these
+ actions. */
+
+typedef enum
+{
+ JIT_NOACTION = 0,
+ JIT_REGISTER,
+ JIT_UNREGISTER
+} jit_actions_t;
+
+/* This struct describes a single symbol file in a linked list of symbol files
+ describing generated code. As the inferior generates code, it adds these
+ entries to the list, and when we attach to the inferior, we read them all.
+ The list is singly linked. For the first element prev_entry should be NULL,
+ and for the last element next_entry should be NULL. */
+
+struct jit_code_entry
+{
+ CORE_ADDR next_entry;
+ CORE_ADDR prev_entry;
+ CORE_ADDR symfile_addr;
+ uint64_t symfile_size;
+};
+
+/* This is the global descriptor that the inferior uses to communicate
+ information to the debugger. To alert the debugger to take an action, the
+ inferior sets the action_flag to the appropriate enum value, updates
+ relevant_entry to point to the relevant code entry, and calls the function at
+ the well-known symbol with our breakpoint. We then read this descriptor from
+ another global well-known symbol. */
+
+struct jit_descriptor
+{
+ uint32_t version;
+ /* This should be jit_actions_t, but we want to be specific about the
+ bit-width. */
+ uint32_t action_flag;
+ CORE_ADDR relevant_entry;
+ CORE_ADDR first_entry;
+};
+
+/* This struct is used to form a linked list mapping the address of the JIT code
+ entry in the inferior's address space to the corresponding object file in
+ GDB. These are used to unregister objfiles for code that has been freed. */
+
+struct jit_obj_link
+{
+ struct jit_obj_link *next;
+ CORE_ADDR entry_addr;
+ struct objfile *objfile;
+};
+
+/* This function is called by handle_inferior_event when it decides that the JIT
+ event breakpoint has fired. */
+
+extern void jit_event_handler (void);
+
+#endif /* JIT_H */
Index: gdb/objfiles.c
===================================================================
RCS file: /cvs/src/src/gdb/objfiles.c,v
retrieving revision 1.83
diff -u -p -u -r1.83 objfiles.c
--- gdb/objfiles.c 14 May 2009 23:33:08 -0000 1.83
+++ gdb/objfiles.c 22 Jul 2009 22:26:08 -0000
@@ -25,6 +25,7 @@

#include "defs.h"
#include "bfd.h" /* Binary File Description */
+#include "libbfd.h"
#include "symtab.h"
#include "symfile.h"
#include "objfiles.h"
@@ -430,9 +431,19 @@ free_objfile (struct objfile *objfile)
if (objfile->obfd != NULL && !(objfile->flags & OBJF_KEEPBFD))
{
char *name = bfd_get_filename (objfile->obfd);
+ struct bfd_in_memory *bim = NULL;
+ /* Hack to work around the fact that BFD does not take ownership of the
+ memory for files allocated in memory. */
+ if (objfile->obfd->flags & BFD_IN_MEMORY)
+ bim = (struct bfd_in_memory *) objfile->obfd->iostream;
if (!bfd_close (objfile->obfd))
warning (_("cannot close \"%s\": %s"),
name, bfd_errmsg (bfd_get_error ()));
+ if (bim != NULL)
+ {
+ xfree (bim->buffer);
+ xfree (bim);
+ }
xfree (name);
}

Index: gdb/symfile-mem.c
===================================================================
RCS file: /cvs/src/src/gdb/symfile-mem.c,v
retrieving revision 1.17
diff -u -p -u -r1.17 symfile-mem.c
--- gdb/symfile-mem.c 21 Feb 2009 16:14:49 -0000 1.17
+++ gdb/symfile-mem.c 22 Jul 2009 22:26:08 -0000
@@ -54,37 +54,23 @@
#include "observer.h"
#include "auxv.h"
#include "elf/common.h"
+#include "bfd.h"
+#include "libbfd.h"
+#include <time.h>

-/* Read inferior memory at ADDR to find the header of a loaded object file
- and read its in-core symbols out of inferior memory. TEMPL is a bfd
- representing the target's format. NAME is the name to use for this
- symbol file in messages; it can be NULL or a malloc-allocated string
- which will be attached to the BFD. */
+/* Common code for symbol_file_add_from_memory and
+ symbol_file_add_from_local_memory. */
+
static struct objfile *
-symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr, char *name,
- int from_tty)
+symbol_file_add_from_memory_common (struct bfd *nbfd, bfd_vma loadbase,
+ int from_tty)
{
struct objfile *objf;
- struct bfd *nbfd;
struct bfd_section *sec;
- bfd_vma loadbase;
struct section_addr_info *sai;
unsigned int i;

- if (bfd_get_flavour (templ) != bfd_target_elf_flavour)
- error (_("add-symbol-file-from-memory not supported for this target"));
-
- nbfd = bfd_elf_bfd_from_remote_memory (templ, addr, &loadbase,
- target_read_memory);
- if (nbfd == NULL)
- error (_("Failed to read a valid object file image from memory."));
-
- if (name == NULL)
- nbfd->filename = xstrdup ("shared object read from target memory");
- else
- nbfd->filename = name;
-
if (!bfd_check_format (nbfd, bfd_object))
{
/* FIXME: should be checking for errors from bfd_close (for one thing,
@@ -116,6 +102,121 @@ symbol_file_add_from_memory (struct bfd
return objf;
}

+/* Read inferior memory at ADDR to find the header of a loaded object file
+ and read its in-core symbols out of inferior memory. TEMPL is a bfd
+ representing the target's format. NAME is the name to use for this
+ symbol file in messages; it can be NULL or a malloc-allocated string
+ which will be attached to the BFD. */
+
+struct objfile *
+symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr, char *name,
+ int from_tty)
+{
+ struct bfd *nbfd;
+ bfd_vma loadbase;
+
+ if (bfd_get_flavour (templ) != bfd_target_elf_flavour)
+ error (_("add-symbol-file-from-memory not supported for this target"));
+
+ nbfd = bfd_elf_bfd_from_remote_memory (templ, addr, &loadbase,
+ target_read_memory);
+ if (nbfd == NULL)
+ error (_("Failed to read a valid object file image from memory."));
+
+ if (name == NULL)
+ nbfd->filename = xstrdup ("shared object read from target memory");
+ else
+ nbfd->filename = name;
+
+ return symbol_file_add_from_memory_common (nbfd, loadbase, from_tty);
+}
+
+/* Create a new BFD for a file that is already loaded into memory. This does
+ not take ownership of buffer or filename, and the caller must free them.
+ This code is based on bfd_from_remote_memory in bfd/elfcode.h, and should
+ probably live inside of the bfd library. */
+
+static bfd *
+bfd_open_from_memory (struct bfd *templ, bfd_byte *buffer,
+ bfd_size_type size, const char *filename)
+{
+ bfd *nbfd;
+ struct bfd_in_memory *bim;
+
+ bim = bfd_malloc (sizeof (struct bfd_in_memory));
+ if (bim == NULL)
+ return NULL;
+ bim->buffer = buffer;
+ bim->size = size;
+
+ nbfd = _bfd_new_bfd ();
+ if (nbfd == NULL)
+ {
+ free (bim);
+ return NULL;
+ }
+ nbfd->filename = filename;
+ nbfd->xvec = templ->xvec;
+ nbfd->iostream = bim;
+ nbfd->flags = BFD_IN_MEMORY;
+ nbfd->direction = read_direction;
+ nbfd->mtime = time (NULL);
+ nbfd->mtime_set = TRUE;
+
+ return nbfd;
+}
+
+/* Add a symbol file from local memory. This differs from
+ symbol_file_add_from_memory in that it assumes that we have the entire
+ symbol file laid out in memory and we don't need to use
+ bfd_elf_bfd_from_remote_memory to reconstruct the ELF from the program
+ headers. */
+
+struct objfile *
+symbol_file_add_from_local_memory (struct bfd *templ, bfd_byte *buffer,
+ bfd_size_type size)
+{
+ bfd *nbfd;
+ int err;
+ struct section_addr_info *sai;
+ bfd_vma loadbase;
+ const char *filename;
+
+ /* The filename must be strdup'd or GDB will crash when freeing the object
+ file. */
+ filename = xstrdup ("<in-memory>");
+
+ /* Create a new BFD from the buffer in memory and add it as a symbol file. */
+ nbfd = bfd_open_from_memory (templ, buffer, size, filename);
+ if (nbfd == NULL)
+ {
+ error (_("Unable to create BFD from local memory: %s"),
+ bfd_errmsg (bfd_get_error ()));
+ return NULL;
+ }
+
+ /* We set loadbase to 0 and assume that the symbol file we're loading has the
+ absolute addresses set in the ELF. */
+ loadbase = 0;
+ return symbol_file_add_from_memory_common (nbfd, loadbase, 0);
+}
+
+/* Use the BFD of the main executable to guess what kind of object file we want
+ to add. This is used for adding symbol files from memory, where we don't
+ know the format of the file. */
+
+struct bfd *
+symbol_file_guess_template (void)
+{
+ struct bfd *templ;
+
+ if (symfile_objfile != NULL)
+ templ = symfile_objfile->obfd;
+ else
+ templ = exec_bfd;
+
+ return templ;
+}

static void
add_symbol_file_from_memory_command (char *args, int from_tty)
@@ -129,10 +230,7 @@ add_symbol_file_from_memory_command (cha
addr = parse_and_eval_address (args);

/* We need some representative bfd to know the target we are looking at. */
- if (symfile_objfile != NULL)
- templ = symfile_objfile->obfd;
- else
- templ = exec_bfd;
+ templ = symbol_file_guess_template ();
if (templ == NULL)
error (_("\
Must use symbol-file or exec-file before add-symbol-file-from-memory."));
Index: gdb/symfile.h
===================================================================
RCS file: /cvs/src/src/gdb/symfile.h,v
retrieving revision 1.50
diff -u -p -u -r1.50 symfile.h
--- gdb/symfile.h 22 May 2009 23:49:14 -0000 1.50
+++ gdb/symfile.h 22 Jul 2009 22:26:08 -0000
@@ -392,4 +392,17 @@ extern void elfmdebug_build_psymtabs (st
const struct ecoff_debug_swap *,
asection *);

+/* From symfile-mem.c */
+
+extern struct bfd *symbol_file_guess_template (void);
+
+extern struct objfile *symbol_file_add_from_memory (struct bfd *templ,
+ CORE_ADDR addr,
+ char *name,
+ int from_tty);
+
+extern struct objfile *symbol_file_add_from_local_memory (struct bfd *templ,
+ bfd_byte *buffer,
+ bfd_size_type size);
+
#endif /* !defined(SYMFILE_H) */