Re: [RFA] Add interface for registering JITed code

Reid Kleckner
2009-07-24T16:47:33Z

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

Here's an updated patch. I still need to write the manual
documentation and switch to the iovec, though. I wrote this message
and the patch yesterday before Ulrich's message arrived.

On Thu, Jul 23, 2009 at 10:42 AM, Tom Tromey<tromey@redhat.com> wrote:
>>>>>> "Reid" == Reid Kleckner <rnk@mit.edu> writes:
>
> Reid> So that means we need LLVM to generate dwarf debug info, and we
> Reid> need to register it with GDB.
>
> Nice.
>
> Your overall approach seems good to me.

Glad to hear I'm on the right track.

> [...]
>
> Reid> If LLVM frees machine code, then it sets the action enum in the
> Reid> descriptor to JIT_UNREGISTER, points the descriptor at the relevant
> Reid> code entry, and calls __jit_debug_register_code again.
>
> Given that this will be a supported way for GDB to connect to JITs, I
> think that the official interface (symbol names, types, enum values,
> etc) should be documented in the GDB manual somewhere.

Where should this go? It doesn't really fit under any of the
top-level topics, so far as I can tell. I only have about a page to
write about it.

> I wonder what happens if the ELF data in the inferior is corrupted.
> Perhaps there should be a way to disable the feature.  What do you
> think?
>
> This patch also deserves an entry in NEWS.

Done.

> I have a few comments on the details of the patch itself.
>
> Reid> +struct breakpoint *
> Reid> +create_jit_event_breakpoint (CORE_ADDR address)
> Reid> +{
> Reid> +  struct breakpoint *b;
> Reid> +
> Reid> +  b = create_internal_breakpoint (address, bp_jit_event);
> Reid> +  update_global_location_list_nothrow (1);
> Reid> +  return b;
> Reid> +}
>
> I am not sure but I suspect this should be called from
> breakpoint_re_set -- that is, follow the longjmp model a bit more.
>
> Does the current code work ok if LLVM is a .so linked into the
> application?  I suspect, but do not know for sure, that in this
> situation gdb will not see the JIT symbols when the inferior-created
> hook is run.q

I don't know, since we statically link to LLVM. I'm not an expert in
how dynamic loaders work, so I don't know how to fix this.

> There is also the somewhat more pathological case of a program with
> multiple JITs linked in.  I'm on the fence about whether this is really
> needed.

How would this work? Would they both have separate functions
__jit_debug_register at different addresses? One way to solve both of
these problems might be to create a single libgdbjitagent library
similar to oprofile's agent library. That way we have some control
over both the inferior side and the GDB side.

> Reid> +/* This is a linked list mapping code entry addresses to objfiles.  */
> Reid> +static struct jit_obj_link *jit_objfiles = NULL;
>
> You can store arbitrary module-specific data in an objfile using the
> objfile_data API.  This would be better because it is an already
> supported way of dealing with the lifetime of the associated data.
>
> This would mean getting rid of the linked list here and just looping
> over objfiles when needed.

Ah, that's much better. Unfortunately because the data for other
objfiles is uninitialized (it's returned from calloc), there's no way
for me to check which objfiles contain JITed code. Do I actually need
to have this inferior_exit hook? I just noticed that when I kill and
restart the inferior it doesn't seem to free these objfiles, so I
added the hook.

> Reid> +      error (_("Unable to read JIT descriptor from remote memory!\n"));
>
> Don't use \n at the end of error text.  (There is code in gdb that does
> this but I think the rule is not to.)

Done.

> Reid> +  descriptor->version = extract_unsigned_integer (&desc_buf[0], 4);
>
> You'll need to update to head ... a few of these functions have an extra
> argument now.
>
> I am not sure if using target_gdbarch is ok -- maybe passing the arch to
> jit_read_descriptor as an argument would be better.  Maybe Ulrich could
> weigh in here.

I'll use target_gdbarch until anyone else says otherwise.

> Reid> +  descriptor->first_entry = extract_typed_address
> Reid> +      (&desc_buf[8 + ptr_size], ptr_type);
>
> A formatting nit ... I think it is more typical to split before the '='
> than before the '('.

Done.

> Reid> +static void
> Reid> +jit_register_code (CORE_ADDR entry_addr, struct jit_code_entry *code_entry)
> [...]
> Reid> +      free (buffer);
>
> xfree, but...

Switched to xfree.

> Reid> +  objfile = symbol_file_add_from_local_memory (templ, buffer, size);
> Reid> +  if (objfile == NULL)
> Reid> +    free (buffer);
>
> symbol_file_add_from_local_memory can call error, so you need a cleanup
> here rather than explicit calls to free.

I took a shot at putting cleanups in, but I'm not sure I did it
correctly. In particular, I'm not sure about the discard_cleanups
logic.

> Reid> +  if (reg_addr == (CORE_ADDR)NULL)
> Reid> +    return;
>
> Just write `0' instead of `(CORE_ADDR)NULL'.  There are 2 instances of
> this.

Done.

> Reid> +  if (err) return;
>
> Newline before the return.  There are a few of these.

Done.

> Reid> +  /* Check that the version number agrees with that we support.  */
> Reid> +  if (descriptor.version != 1)
> Reid> +    {
> Reid> +      error (_("Unsupported JIT protocol version in descriptor!\n"));
> Reid> +      return;
>
> error calls longjmp, so the return is redundant.  There are a couple of
> these in the patch.

Done. I made some functions just return void instead of error codes
and also call error.

> Reid> +/* When the JIT breakpoint fires, the inferior wants us to take one of these
> Reid> +   actions.  */
> Reid> +
> Reid> +typedef enum
> Reid> +{
> Reid> +  JIT_NOACTION = 0,
> Reid> +  JIT_REGISTER,
> Reid> +  JIT_UNREGISTER
> Reid> +} jit_actions_t;
>
> Please update the comment here to explain that these values are exported
> and used by the inferior, so they cannot be changed.

Done. The same is true about the rest of the structs, the ordering of
the fields cannot be changed without updating the protocol version.

> Reid> +      /* Hack to work around the fact that BFD does not take ownership of the
> Reid> +         memory for files allocated in memory.  */
>
> Is it possible to fix this directly in BFD?  Since...
>
> Reid> +        bim = (struct bfd_in_memory *) objfile->obfd->iostream;
>
> ... this is definitely fishy :-)

I talked to Doug Evans about it, and he said he'd look into getting a
patch into BFD. The basic problem is that BFD doesn't take ownership
of any memory buffers that you give it. If you look in bfd_close, it
does a check for flags & BFD_IN_MEMORY and calls iovec->bclose for
BFD's that aren't in memory. It doesn't do anything with iostream
(bim) or bim->buffer. Other BFD clients may rely on this or also have
workarounds, so that will need to be sorted out before we can just go
ahead and free it on the BFD side.

> Reid> +struct objfile *
> Reid> +symbol_file_add_from_local_memory (struct bfd *templ, bfd_byte *buffer,
> Reid> +                                   bfd_size_type size)
> Reid> +{
> [...]
> Reid> +  filename = xstrdup ("<in-memory>");
>
> Because this function can call error, you need a cleanup here.

Done.

Reid

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

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

Add interface for JIT code generation.
* NEWS: Announce JIT interface.
* 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.1091
diff -u -p -u -r1.1091 Makefile.in
--- gdb/Makefile.in 3 Jul 2009 12:06:35 -0000 1.1091
+++ gdb/Makefile.in 24 Jul 2009 16:45:06 -0000
@@ -677,7 +677,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

@@ -746,7 +747,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.

@@ -828,7 +829,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/NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.319
diff -u -p -u -r1.319 NEWS
--- gdb/NEWS 20 Jul 2009 18:51:41 -0000 1.319
+++ gdb/NEWS 24 Jul 2009 16:45:06 -0000
@@ -3,6 +3,11 @@

*** Changes since GDB 6.8

+* GDB now has an interface for JIT compilation. Applications that
+dynamically generate code can create object files with debug info in
+memory and register them with GDB. The interface also supports
+attaching to a running process.
+
* Tracepoints may now be conditional. The syntax is as for
breakpoints; either an "if" clause appended to the "trace" command,
or the "condition" command is available. GDB sends the condition to
Index: gdb/breakpoint.c
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.c,v
retrieving revision 1.413
diff -u -p -u -r1.413 breakpoint.c
--- gdb/breakpoint.c 7 Jul 2009 22:21:09 -0000 1.413
+++ gdb/breakpoint.c 24 Jul 2009 16:45:07 -0000
@@ -2573,6 +2573,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;
@@ -3298,6 +3299,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
};
@@ -3313,6 +3317,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
@@ -3333,12 +3338,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 <

@@ -3356,28 +3362,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
@@ -3390,6 +3386,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;

@@ -3460,6 +3457,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:
case bp_longjmp_master:
@@ -3593,6 +3593,7 @@ print_one_breakpoint_location (struct br
{bp_longjmp_master, "longjmp master"},
{bp_catchpoint, "catchpoint"},
{bp_tracepoint, "tracepoint"},
+ {bp_jit_event, "jit events"},
};

static char bpenables[] = "nynny";
@@ -3721,6 +3722,7 @@ print_one_breakpoint_location (struct br
case bp_overlay_event:
case bp_longjmp_master:
case bp_tracepoint:
+ case bp_jit_event:
if (opts.addressprint)
{
annotate_field (4);
@@ -4362,6 +4364,7 @@ allocate_bp_location (struct breakpoint
case bp_shlib_event:
case bp_thread_event:
case bp_overlay_event:
+ case bp_jit_event:
case bp_longjmp_master:
loc->loc_type = bp_loc_software_breakpoint;
break;
@@ -4644,6 +4647,17 @@ struct lang_and_radix
int radix;
};

+/* Create a breakpoint for JIT code registration and unregistration. */
+
+struct breakpoint *
+create_jit_event_breakpoint (struct gdbarch *gdbarch, CORE_ADDR address)
+{
+ struct breakpoint *b;
+
+ b = create_internal_breakpoint (gdbarch, address, bp_jit_event);
+ update_global_location_list_nothrow (1);
+ return b;
+}

void
remove_solib_event_breakpoints (void)
@@ -5279,6 +5293,7 @@ mention (struct breakpoint *b)
case bp_shlib_event:
case bp_thread_event:
case bp_overlay_event:
+ case bp_jit_event:
case bp_longjmp_master:
break;
}
@@ -7585,6 +7600,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->type != bp_longjmp_master
@@ -7604,6 +7620,7 @@ delete_command (char *arg, int from_tty)
if (b->type != bp_call_dummy
&& b->type != bp_shlib_event
&& b->type != bp_thread_event
+ && b->type != bp_jit_event
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
&& b->number >= 0)
@@ -7926,6 +7943,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.95
diff -u -p -u -r1.95 breakpoint.h
--- gdb/breakpoint.h 2 Jul 2009 17:12:24 -0000 1.95
+++ gdb/breakpoint.h 24 Jul 2009 16:45:07 -0000
@@ -120,6 +120,9 @@ enum bptype
bp_catchpoint,

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

/* States of enablement of breakpoint. */
@@ -548,6 +551,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
};
@@ -841,6 +847,9 @@ extern void mark_breakpoints_out (void);

extern void make_breakpoint_permanent (struct breakpoint *);

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

Index: gdb/infrun.c
===================================================================
RCS file: /cvs/src/src/gdb/infrun.c,v
retrieving revision 1.402
diff -u -p -u -r1.402 infrun.c
--- gdb/infrun.c 20 Jul 2009 15:05:12 -0000 1.402
+++ gdb/infrun.c 24 Jul 2009 16:45:07 -0000
@@ -50,6 +50,7 @@
#include "event-top.h"
#include "record.h"
#include "inline-frame.h"
+#include "jit.h"

/* Prototypes for local functions */

@@ -3529,6 +3530,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 24 Jul 2009 16:45:08 -0000
@@ -0,0 +1,277 @@
+/* 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 struct objfile_data *jit_objfile_data;
+
+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;
+
+/* Helper method for reading the global JIT descriptor from remote memory. */
+
+static void
+jit_read_descriptor (struct jit_descriptor *descriptor)
+{
+ int err;
+ struct type *ptr_type;
+ int ptr_size;
+ int desc_size;
+ gdb_byte *desc_buf;
+ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+
+ /* 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!"));
+
+ /* Fix the endianness to match the host. */
+ descriptor->version = extract_unsigned_integer (&desc_buf[0], 4, byte_order);
+ descriptor->action_flag =
+ extract_unsigned_integer (&desc_buf[4], 4, byte_order);
+ descriptor->relevant_entry = extract_typed_address (&desc_buf[8], ptr_type);
+ descriptor->first_entry =
+ extract_typed_address (&desc_buf[8 + ptr_size], ptr_type);
+}
+
+/* Helper method for reading a JITed code entry from remote memory. */
+
+static void
+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;
+ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+
+ /* 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!"));
+
+ /* 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, byte_order);
+}
+
+/* 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 cleanup *old_cleanups;
+
+ /* 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);
+ old_cleanups = make_cleanup (xfree, buffer);
+ err = target_read_memory (code_entry->symfile_addr, buffer, size);
+ if (err)
+ error (_("Unable to read JITed symfile from remote memory!"));
+
+ /* 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);
+
+ /* Remember a mapping from entry_addr to objfile. */
+ set_objfile_data (objfile, jit_objfile_data, (void*) entry_addr);
+
+ discard_cleanups (old_cleanups);
+}
+
+/* This function unregisters JITed code and frees the corresponding objfile. */
+
+static void
+jit_unregister_code (struct objfile *objfile)
+{
+ free_objfile (objfile);
+}
+
+/* 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 == 0)
+ 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 == 0)
+ return;
+
+ /* Read the descriptor so we can check the version number and load any already
+ JITed functions. */
+ jit_read_descriptor (&descriptor);
+
+ /* Check that the version number agrees with that we support. */
+ if (descriptor.version != 1)
+ error (_("Unsupported JIT protocol version in descriptor!"));
+
+ /* Put a breakpoint in the registration symbol. */
+ create_jit_event_breakpoint (target_gdbarch, 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)
+ {
+ jit_read_code_entry (cur_entry_addr, &cur_entry);
+ 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)
+{
+ struct objfile *objf;
+ struct objfile *temp;
+
+ ALL_OBJFILES_SAFE (objf, temp)
+ if (objfile_data (objf, jit_objfile_data) != NULL)
+ jit_unregister_code (objf);
+}
+
+void
+jit_event_handler (void)
+{
+ struct jit_descriptor descriptor;
+ struct jit_code_entry code_entry;
+ int err;
+ CORE_ADDR entry_addr;
+ struct objfile *objf;
+ struct objfile *temp;
+
+ /* 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:
+ jit_read_code_entry (entry_addr, &code_entry);
+ jit_register_code (entry_addr, &code_entry);
+ break;
+ case JIT_UNREGISTER:
+ /* Look up the objfile with this code entry address. */
+ ALL_OBJFILES_SAFE (objf, temp)
+ {
+ if (objfile_data (objf, jit_objfile_data) == (void*) entry_addr)
+ {
+ jit_unregister_code (objf);
+ 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!"));
+ 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);
+ jit_objfile_data = register_objfile_data ();
+}
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 24 Jul 2009 16:45:08 -0000
@@ -0,0 +1,75 @@
+/* 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. These values are used by the inferior, so the values of these enums
+ cannot be changed. */
+
+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.
+ For the first element prev_entry should be NULL, and for the last element
+ next_entry should be NULL. The ordering of this struct must be consistent
+ with the ordering in the client, so it cannot be changed without updating the
+ protocol version. */
+
+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. The ordering of this struct must be
+ consistent with the ordering in the client, so it cannot be changed without
+ updating the protocol version. */
+
+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 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.87
diff -u -p -u -r1.87 objfiles.c
--- gdb/objfiles.c 22 Jul 2009 19:21:31 -0000 1.87
+++ gdb/objfiles.c 24 Jul 2009 16:45: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"
@@ -438,9 +439,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.19
diff -u -p -u -r1.19 symfile-mem.c
--- gdb/symfile-mem.c 2 Jul 2009 17:21:06 -0000 1.19
+++ gdb/symfile-mem.c 24 Jul 2009 16:45:08 -0000
@@ -54,36 +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;
+ struct cleanup *old_cleanups;

if (!bfd_check_format (nbfd, bfd_object))
{
@@ -96,7 +83,7 @@ symbol_file_add_from_memory (struct bfd
}

sai = alloc_section_addr_info (bfd_count_sections (nbfd));
- make_cleanup (xfree, sai);
+ old_cleanups = make_cleanup (xfree, sai);
i = 0;
for (sec = nbfd->sections; sec != NULL; sec = sec->next)
if ((bfd_get_section_flags (nbfd, sec) & (SEC_ALLOC|SEC_LOAD)) != 0)
@@ -113,9 +100,127 @@ symbol_file_add_from_memory (struct bfd
/* This might change our ideas about frames already looked at. */
reinit_frame_cache ();

+ discard_cleanups (old_cleanups);
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;
+ struct cleanup *old_cleanups;
+ struct objfile *objfile;
+
+ /* The filename must be strdup'd or GDB will crash when freeing the object
+ file. */
+ filename = xstrdup ("<in-memory>");
+ old_cleanups = make_cleanup (xfree, (void*)filename);
+
+ /* 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 ()));
+
+ /* We set loadbase to 0 and assume that the symbol file we're loading has the
+ absolute addresses set in the ELF. */
+ loadbase = 0;
+ objfile = symbol_file_add_from_memory_common (nbfd, loadbase, 0);
+ discard_cleanups (old_cleanups);
+ return objfile;
+}
+
+/* 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 +234,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.51
diff -u -p -u -r1.51 symfile.h
--- gdb/symfile.h 16 Jun 2009 18:49:25 -0000 1.51
+++ gdb/symfile.h 24 Jul 2009 16:45:08 -0000
@@ -408,4 +408,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) */