Re: [RFA] Add interface for registering JITed code

Reid Kleckner
2009-07-24T23:39:55Z

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

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

I'll figure it out on Monday, but for now, here's some code changes.

> Today I've also been wondering whether this works with core files.
> Have you tried that? (I can't imagine why it wouldn't work. And I
> don't think it is really a requirement; I'm just curious.)q

I have absolutely no idea. :)

> Also I was wondering about a multi-threaded JIT. I suppose it is
> sufficient to mention in the documentation that the JIT is responsible
> for making sure only a single thread can call __jit_debug_register_code
> at a time.

Yes, on the LLVM side I actually put a big fat global lock around the
whole thing. I don't know if it's possible for there to be multiple
symbols when there are dynamic libraries or who knows what, but only
one thread should be touching those globals at a time. If you had
multiple JITs that were unaware of each other in the binary trying to
JIT, then it would break down, but that seems like an uncommon use
case. If it comes to that, someone could write a library that they
both link in which handles the synchronization.

> Tom> Does the current code work ok if LLVM is a .so linked into the
> Tom> application? I suspect, but do not know for sure, that in this
> Tom> situation gdb will not see the JIT symbols when the inferior-created
> Tom> hook is run.
>
> Reid> I don't know, since we statically link to LLVM. I'm not an expert in
> Reid> how dynamic loaders work, so I don't know how to fix this.
>
> Ok. One way would be to stick some code in breakpoint_re_set and
> update_breakpoints_after_exec. (I am not sure if this is the best way or
> not.) Anyway the idea is that after a .so is noticed, re-do the searching,
> in case the new .so is a JIT. So, the work would be done per-objfile,
> and not once per inferior.
>
> Maybe this can be done via observers as well; I don't know.

I put it in breakpoint_re_set and follow_exec after the solib stuff,
so that should probably work, but I have no way of testing it.

> Tom> There is also the somewhat more pathological case of a program with
> Tom> multiple JITs linked in. I'm on the fence about whether this is really
> Tom> needed.
>
> Reid> How would this work? Would they both have separate functions
> Reid> __jit_debug_register at different addresses?
>
> Yeah. This would work if you had two JITs in a process, say loaded
> dynamically, and the various __jit symbols had hidden visibility.

I think if someone has that use case, then they can write a client
side library that wraps the __jit symbols and make the two JITs call
that.

> Tom> You can store arbitrary module-specific data in an objfile using the
> Tom> objfile_data API. This would be better because it is an already
> Tom> supported way of dealing with the lifetime of the associated data.
>
> Reid> Ah, that's much better. Unfortunately because the data for other
> Reid> objfiles is uninitialized (it's returned from calloc), there's no way
> Reid> for me to check which objfiles contain JITed code.
>
> I don't think I understand. I thought you meant that you couldn't
> detect whether or not the JIT datum was set on an objfile, but I see
> that jit_inferior_exit_hook already does this the way that I would
> expect.

Oh right, I wrote that code, and then was worried that the
objfile_data wouldn't be initialized to NULL. It's returned from
calloc, which according to Jeff is initialized to 0's, so the code I
wrote works.

> Tom> Please update the comment here to explain that these values are exported
> Tom> and used by the inferior, so they cannot be changed.
>
> Reid> Done. The same is true about the rest of the structs, the ordering of
> Reid> the fields cannot be changed without updating the protocol version.
>
> My reading of the code is that it unpacks target memory field-by-field
> into the host-side structure. So, it is ok for this to change. What
> cannot change is the definition of these structs on the target.
>
> Am I misreading this?
>
> These structs, in their target form, should probably all go in the
> manual as well.

That's true, but they should probably stay closely coupled. I started
off just copying over the whole struct with one target_read_mem call,
and having them magically line up. However, that doesn't work if
you're debugging a 32-bit process with a 64-bit debugger.

> Reid> + /* Remember a mapping from entry_addr to objfile. */
> Reid> + set_objfile_data (objfile, jit_objfile_data, (void*) entry_addr);
>
> I don't think you need the cast here. There are a few of these.

Actually, it is, because entry_addr is a CORE_ADDR. CORE_ADDR fits
into a void* right? I'm just doing it to avoid mallocing a single
CORE_ADDR.

> Reid> + printf_unfiltered ("Unable to find JITed code entry at address: %p\n",
> Reid> + (void*) entry_addr);
>
> A style nit, in GNU style the cast is written "(void *)".

Done.

> Reid> + nbfd = bfd_open_from_memory (templ, buffer, size, filename);
> Reid> + if (nbfd == NULL)
> Reid> + error (_("Unable to create BFD from local memory: %s"),
> Reid> + bfd_errmsg (bfd_get_error ()));
> Reid> +
> Reid> + /* We set loadbase to 0 and assume that the symbol file we're loading has the
> Reid> + absolute addresses set in the ELF. */
> Reid> + loadbase = 0;
> Reid> + objfile = symbol_file_add_from_memory_common (nbfd, loadbase, 0);
>
> I suspect this needs a cleanup to free the BFD in case
> symbol_file_add_from_memory_common errors. But I couldn't tell
> immediately if that is possible (in gdb I tend to assume that anything
> can throw...).

This code has been reverted, and I use bfd_openr_iovec now.

Reid

[-- Attachment #2: jit-patch.txt --]
[-- Type: text/plain, Size: 29288 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:
(update_breakpoints_after_exec): Delete jit breakpoints after exec.
(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.
(allocate_bp_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.
(breakpoint_re_set): Added call to jit_inferior_created_hook.
(create_jit_event_breakpoint): New.
* infrun.c (handle_inferior_event): Add handler for jit event.
(follow_exec): Add call to jit_inferior_created_hook.
* 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 23:34:27 -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 23:34:27 -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 23:34:33 -0000
@@ -59,6 +59,7 @@
#include "top.h"
#include "wrapper.h"
#include "valprint.h"
+#include "jit.h"

/* readline include files */
#include "readline/readline.h"
@@ -1575,6 +1576,13 @@ update_breakpoints_after_exec (void)
continue;
}

+ /* JIT breakpoints must be explicitly reset after an exec(). */
+ if (b->type == bp_jit_event)
+ {
+ delete_breakpoint (b);
+ continue;
+ }
+
/* Thread event breakpoints must be set anew after an exec(),
as must overlay event and longjmp master breakpoints. */
if (b->type == bp_thread_event || b->type == bp_overlay_event
@@ -2573,6 +2581,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 +3307,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 +3325,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 +3346,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 +3370,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 +3394,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 +3465,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 +3601,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 +3730,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 +4372,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 +4655,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 +5301,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 +7608,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 +7628,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 +7951,7 @@ breakpoint_re_set_one (void *bint)
case bp_step_resume:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_jit_event:
break;
}

@@ -7954,6 +7980,8 @@ breakpoint_re_set (void)
set_language (save_language);
input_radix = save_input_radix;

+ jit_inferior_created_hook ();
+
create_overlay_event_breakpoint ("_ovly_debug_event");
create_longjmp_master_breakpoint ("longjmp");
create_longjmp_master_breakpoint ("_longjmp");
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 23:34:36 -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 23:34:41 -0000
@@ -50,6 +50,7 @@
#include "event-top.h"
#include "record.h"
#include "inline-frame.h"
+#include "jit.h"

/* Prototypes for local functions */

@@ -544,6 +545,8 @@ follow_exec (ptid_t pid, char *execd_pat
solib_create_inferior_hook ();
#endif

+ jit_inferior_created_hook ();
+
/* Reinsert all breakpoints. (Those which were symbolic have
been reset to the proper address in the new a.out, thanks
to symbol_file_command...) */
@@ -3529,6 +3532,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 23:34:41 -0000
@@ -0,0 +1,393 @@
+/* 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"
+#include "gdb_stat.h"
+
+//#include "assert.h"
+//#include "string.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 = 0;
+
+struct target_buffer
+{
+ CORE_ADDR base;
+ size_t size;
+};
+
+/* Openning the file is a no-op. */
+
+static void *
+mem_bfd_iovec_open (struct bfd *abfd, void *open_closure)
+{
+ return open_closure;
+}
+
+/* Closing the file is just freeing the base/size pair on our side. */
+
+static int
+mem_bfd_iovec_close (struct bfd *abfd, void *stream)
+{
+ xfree (stream);
+ return 1;
+}
+
+/* For reading the file, we just need to pass through to target_read_memory and
+ fix up the arguments and return values. */
+
+static file_ptr
+mem_bfd_iovec_pread (struct bfd *abfd, void *stream, void *buf,
+ file_ptr nbytes, file_ptr offset)
+{
+ int err;
+ struct target_buffer *buffer = (struct target_buffer *) stream;
+
+ /* If this read will read all of the file, limit it to just the rest. */
+ if (offset + nbytes > buffer->size)
+ nbytes = buffer->size - offset;
+
+ /* If there are no more bytes left, we've reached EOF. */
+ if (nbytes == 0)
+ return 0;
+
+ err = target_read_memory (buffer->base + offset, (gdb_byte *) buf, nbytes);
+ if (err)
+ return -1;
+
+ return nbytes;
+}
+
+/* For statting the file, we only support the st_size attribute. */
+
+static int
+mem_bfd_iovec_stat (struct bfd *abfd, void *stream, struct stat *sb)
+{
+ struct target_buffer *buffer = (struct target_buffer*) stream;
+
+ sb->st_size = buffer->size;
+ return 0;
+}
+
+/* Open a BFD from the target's memory. */
+
+static struct bfd *
+bfd_open_from_target_memory (CORE_ADDR addr, size_t size, char *target)
+{
+ const char *filename = xstrdup ("<in-memory>");
+ struct target_buffer *buffer = xmalloc (sizeof (struct target_buffer));
+
+ buffer->base = addr;
+ buffer->size = size;
+ return bfd_openr_iovec (filename, target,
+ mem_bfd_iovec_open,
+ buffer,
+ mem_bfd_iovec_pread,
+ mem_bfd_iovec_close,
+ mem_bfd_iovec_stat);
+}
+
+/* Helper function 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 function 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 *nbfd;
+ struct section_addr_info *sai;
+ struct bfd_section *sec;
+ struct objfile *objfile;
+ struct cleanup *old_cleanups;
+ int i;
+ const struct bfd_arch_info *b;
+
+ nbfd = bfd_open_from_target_memory (code_entry->symfile_addr,
+ code_entry->symfile_size, gnutarget);
+ old_cleanups = make_cleanup_bfd_close (nbfd);
+
+ /* Check the format. NOTE: This initializes important data that GDB uses!
+ We would segfault later without this line. */
+ if (!bfd_check_format (nbfd, bfd_object))
+ {
+ printf_unfiltered (_("\
+JITed symbol file is not an object file, ignoring it.\n"));
+ do_cleanups (old_cleanups);
+ return;
+ }
+
+ /* Check bfd arch. */
+ b = gdbarch_bfd_arch_info (target_gdbarch);
+ if (b->compatible (b, bfd_get_arch_info (nbfd)) != b)
+ warning (_("JITed object file architecture %s is not compatible "
+ "with target architecture %s."), bfd_get_arch_info
+ (nbfd)->printable_name, b->printable_name);
+
+ /* Read the section address information out of the symbol file. Since the
+ file is generated by the JIT at runtime, it should all of the absolute
+ addresses that we care about. */
+ sai = alloc_section_addr_info (bfd_count_sections (nbfd));
+ make_cleanup_free_section_addr_info (sai);
+ i = 0;
+ for (sec = nbfd->sections; sec != NULL; sec = sec->next)
+ if ((bfd_get_section_flags (nbfd, sec) & (SEC_ALLOC|SEC_LOAD)) != 0)
+ {
+ /* We assume that these virtual addresses are absolute, and do not
+ treat them as offsets. */
+ sai->other[i].addr = bfd_get_section_vma (nbfd, sec);
+ sai->other[i].name = (char *) bfd_get_section_name (nbfd, sec);
+ sai->other[i].sectindex = sec->index;
+ ++i;
+ }
+
+ /* This call takes ownership of sai. */
+ objfile = symbol_file_add_from_bfd (nbfd, 0, sai, OBJF_SHARED);
+
+ /* 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);
+}
+
+void
+jit_inferior_created_hook (void)
+{
+ 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;
+
+ /* If we already found the symbols and successfully set the breakpoint, don't
+ do it again. */
+ if (jit_descriptor_addr != 0)
+ return;
+
+ /* 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);
+ }
+}
+
+/* Wrapper to match the observer function pointer prototype. */
+
+static void
+jit_inferior_created_hook1 (struct target_ops *objfile, int from_tty)
+{
+ jit_inferior_created_hook ();
+}
+
+/* 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;
+
+ /* We need to reset the descriptor addr so that next time we load up the
+ inferior we look for it again. */
+ jit_descriptor_addr = 0;
+
+ 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;
+ 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_hook1);
+ 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 23:34:41 -0000
@@ -0,0 +1,77 @@
+/* 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. */
+
+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;
+};
+
+/* Looks for the descriptor and registration symbols and breakpoints the
+ registration function. If it finds both, it registers all the already JITed
+ code. If it has already found the symbols, then it doesn't try again. */
+
+extern void jit_inferior_created_hook (void);
+
+/* 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 23:34:41 -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);
}