Add interface for JIT code generation.
authorDoug Evans <dje@google.com>
Thu, 20 Aug 2009 18:02:48 +0000 (18:02 +0000)
committerDoug Evans <dje@google.com>
Thu, 20 Aug 2009 18:02:48 +0000 (18:02 +0000)
* 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.
* doc/gdb.texinfo: Add chapter on JIT interface.

gdb/ChangeLog
gdb/Makefile.in
gdb/NEWS
gdb/breakpoint.c
gdb/breakpoint.h
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/infrun.c
gdb/jit.c [new file with mode: 0644]
gdb/jit.h [new file with mode: 0644]

index 1c4c56d29713149975d2282779d27fcbfdd0a02e..291c9658501f81ec3157df43021679671618ee8d 100644 (file)
@@ -1,3 +1,27 @@
+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.
+
 2009-08-19  Ulrich Weigand  <uweigand@de.ibm.com>
 
        * value.c (enum internalvar_kind): Replace INTERNALVAR_SCALAR by
index 9c2b9c7c2ca647eb514df3575565016c67eb52cb..90c285fc653ff0f90479c790ef192ab57d20d4e2 100644 (file)
@@ -676,7 +676,8 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
        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
 
@@ -745,7 +746,7 @@ config/rs6000/nm-rs6000.h top.h bsd-kvm.h gdb-stabs.h reggroups.h \
 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.
 
@@ -827,7 +828,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
        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 0b8e3af074ab9c5de58788cc785b2e69d5501d02..bf98b15e1b97bad34f3ce67a8aab04965b101fc8 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,12 @@
 
 *** Changes since GDB 6.8
 
+* GDB now has an interface for JIT compilation.  Applications that
+dynamically generate code can create symbol files in memory and register
+them with GDB.  For users, the feature should work transparently, and
+for JIT developers, the interface is documented in the GDB manual in the
+"JIT Compilation Interface" chapter.
+
 * 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 f3940e1f6425cc65183f3131088ea6a366158fd2..1f799b0f688780aa4a753a4e7c51f4139687d8c7 100644 (file)
@@ -59,6 +59,7 @@
 #include "top.h"
 #include "wrapper.h"
 #include "valprint.h"
+#include "jit.h"
 
 /* readline include files */
 #include "readline/readline.h"
@@ -1592,6 +1593,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
@@ -2583,6 +2591,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;
@@ -3308,6 +3317,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
     };
@@ -3323,6 +3335,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
@@ -3343,12 +3356,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   <
 
@@ -3366,28 +3380,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
@@ -3400,6 +3404,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;
 
@@ -3470,6 +3475,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:
@@ -3603,6 +3611,7 @@ print_one_breakpoint_location (struct breakpoint *b,
     {bp_longjmp_master, "longjmp master"},
     {bp_catchpoint, "catchpoint"},
     {bp_tracepoint, "tracepoint"},
+    {bp_jit_event, "jit events"},
   };
   
   static char bpenables[] = "nynny";
@@ -3731,6 +3740,7 @@ print_one_breakpoint_location (struct breakpoint *b,
       case bp_overlay_event:
       case bp_longjmp_master:
       case bp_tracepoint:
+      case bp_jit_event:
        if (opts.addressprint)
          {
            annotate_field (4);
@@ -4375,6 +4385,7 @@ allocate_bp_location (struct breakpoint *bpt)
     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;
@@ -4657,6 +4668,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)
@@ -5338,6 +5360,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;
       }
@@ -7654,6 +7677,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
@@ -7673,6 +7697,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)
@@ -7999,6 +8024,7 @@ breakpoint_re_set_one (void *bint)
     case bp_step_resume:
     case bp_longjmp:
     case bp_longjmp_resume:
+    case bp_jit_event:
       break;
     }
 
@@ -8027,6 +8053,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 d93c6b664abd6ff7fb927cca18e13465f781c2fa..70b1398aef68e9cc98f930b23708810559c8911b 100644 (file)
@@ -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. */
@@ -554,6 +557,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
   };
@@ -865,6 +871,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 2801a5c160557f78ab37fde805bb4096f0a67596..33732166c72a14ddbaa3e2b029d8dfcb75187322 100644 (file)
@@ -1,3 +1,7 @@
+2009-08-20  Reid Kleckner  <reid@kleckner.net>
+
+       * gdb.texinfo: Add chapter on JIT interface.
+
 2009-08-07  Nick Roberts  <nickrob@snap.net.nz>
 
        * gdb.texinfo (Server Prefix): Explain that server prefix suppresses
index 4016accdbdfdb2fb6fcdd9f996572277f3b8f9e9..e5fe6ac3c66627f65552b6675f7231e1014e9d0a 100644 (file)
@@ -159,6 +159,7 @@ software in general.  We will miss him.
 * Emacs::                       Using @value{GDBN} under @sc{gnu} Emacs
 * GDB/MI::                      @value{GDBN}'s Machine Interface.
 * Annotations::                 @value{GDBN}'s annotation interface.
+* JIT Interface::               Using the JIT debugging interface.
 
 * GDB Bugs::                    Reporting bugs in @value{GDBN}
 
@@ -25921,6 +25922,136 @@ source which is being displayed.  @var{addr} is in the form @samp{0x}
 followed by one or more lowercase hex digits (note that this does not
 depend on the language).
 
+@node JIT Interface
+@chapter JIT Compilation Interface
+@cindex just-in-time compilation
+@cindex JIT compilation interface
+
+This chapter documents @value{GDBN}'s @dfn{just-in-time} (JIT) compilation
+interface.  A JIT compiler is a program or library that generates native
+executable code at runtime and executes it, usually in order to achieve good
+performance while maintaining platform independence. 
+
+Programs that use JIT compilation are normally difficult to debug because
+portions of their code are generated at runtime, instead of being loaded from
+object files, which is where @value{GDBN} normally finds the program's symbols
+and debug information.  In order to debug programs that use JIT compilation,
+@value{GDBN} has an interface that allows the program to register in-memory
+symbol files with @value{GDBN} at runtime.
+
+If you are using @value{GDBN} to debug a program that uses this interface, then
+it should work transparently so long as you have not stripped the binary.  If
+you are developing a JIT compiler, then the interface is documented in the rest
+of this chapter.  At this time, the only known client of this interface is the
+LLVM JIT.
+
+Broadly speaking, the JIT interface mirrors the dynamic loader interface.  The
+JIT compiler communicates with @value{GDBN} by writing data into a global
+variable and calling a fuction at a well-known symbol.  When @value{GDBN}
+attaches, it reads a linked list of symbol files from the global variable to
+find existing code, and puts a breakpoint in the function so that it can find
+out about additional code.
+
+@menu
+* Declarations::                Relevant C struct declarations
+* Registering Code::            Steps to register code
+* Unregistering Code::          Steps to unregister code
+@end menu
+
+@node Declarations
+@section JIT Declarations
+
+These are the relevant struct declarations that a C program should include to
+implement the interface:
+
+@smallexample
+typedef enum
+@{
+  JIT_NOACTION = 0,
+  JIT_REGISTER_FN,
+  JIT_UNREGISTER_FN
+@} jit_actions_t;
+
+struct jit_code_entry
+@{
+  struct jit_code_entry *next_entry;
+  struct jit_code_entry *prev_entry;
+  const char *symfile_addr;
+  uint64_t symfile_size;
+@};
+
+struct jit_descriptor
+@{
+  uint32_t version;
+  /* This type should be jit_actions_t, but we use uint32_t
+     to be explicit about the bitwidth.  */
+  uint32_t action_flag;
+  struct jit_code_entry *relevant_entry;
+  struct jit_code_entry *first_entry;
+@};
+
+/* GDB puts a breakpoint in this function.  */
+void __attribute__((noinline)) __jit_debug_register_code() @{ @};
+
+/* Make sure to specify the version statically, because the
+   debugger may check the version before we can set it.  */
+struct jit_descriptor __jit_debug_descriptor = @{ 1, 0, 0, 0 @};
+@end smallexample
+
+If the JIT is multi-threaded, then it is important that the JIT synchronize any
+modifications to this global data properly, which can easily be done by putting
+a global mutex around modifications to these structures.
+
+@node Registering Code
+@section Registering Code
+
+To register code with @value{GDBN}, the JIT should follow this protocol:
+
+@itemize @bullet
+@item
+Generate an object file in memory with symbols and other desired debug
+information.  The file must include the virtual addresses of the sections.
+
+@item
+Create a code entry for the file, which gives the start and size of the symbol
+file.
+
+@item
+Add it to the linked list in the JIT descriptor.
+
+@item
+Point the relevant_entry field of the descriptor at the entry.
+
+@item
+Set @code{action_flag} to @code{JIT_REGISTER} and call
+@code{__jit_debug_register_code}.
+@end itemize
+
+When @value{GDBN} is attached and the breakpoint fires, @value{GDBN} uses the
+@code{relevant_entry} pointer so it doesn't have to walk the list looking for
+new code.  However, the linked list must still be maintained in order to allow
+@value{GDBN} to attach to a running process and still find the symbol files.
+
+@node Unregistering Code
+@section Unregistering Code
+
+If code is freed, then the JIT should use the following protocol:
+
+@itemize @bullet
+@item
+Remove the code entry corresponding to the code from the linked list.
+
+@item
+Point the @code{relevant_entry} field of the descriptor at the code entry.
+
+@item
+Set @code{action_flag} to @code{JIT_UNREGISTER} and call
+@code{__jit_debug_register_code}.
+@end itemize
+
+If the JIT frees or recompiles code without unregistering it, then @value{GDBN}
+and the JIT will leak the memory used for the associated symbol files.
+
 @node GDB Bugs
 @chapter Reporting Bugs in @value{GDBN}
 @cindex bugs in @value{GDBN}
index e3eddce7b8941f0bcc4cfdde5d32eb53bb1042e7..892e0d4ec1f777caea58d9a6fa1999782c078541 100644 (file)
@@ -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_pathname)
   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...) */
@@ -3540,6 +3543,22 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
        }
        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.  */
 
diff --git a/gdb/jit.c b/gdb/jit.c
new file mode 100644 (file)
index 0000000..0c50060
--- /dev/null
+++ b/gdb/jit.c
@@ -0,0 +1,438 @@
+/* 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"
+
+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;
+
+/* This is a boolean indicating whether we're currently registering code.  This
+   is used to avoid re-entering the registration code.  We want to check for
+   new JITed every time a new object file is loaded, but we want to avoid
+   checking for new code while we're registering object files for JITed code.
+   Therefore, we flip this variable to 1 before registering new object files,
+   and set it to 0 before returning.  */
+
+static int registering_code = 0;
+
+/* Helper cleanup function to clear an integer flag like the one above.  */
+
+static void
+clear_int (void *int_addr)
+{
+  *((int *) int_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, *my_cleanups;
+  int i;
+  const struct bfd_arch_info *b;
+  CORE_ADDR *entry_addr_ptr;
+
+  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;
+      }
+
+  /* Raise this flag while we register code so we won't trigger any
+     re-registration.  */
+  registering_code = 1;
+  my_cleanups = make_cleanup (clear_int, &registering_code);
+
+  /* This call takes ownership of sai.  */
+  objfile = symbol_file_add_from_bfd (nbfd, 0, sai, OBJF_SHARED);
+
+  /* Clear the registering_code flag.  */
+  do_cleanups (my_cleanups);
+
+  /* Remember a mapping from entry_addr to objfile.  */
+  entry_addr_ptr = xmalloc (sizeof (CORE_ADDR));
+  *entry_addr_ptr = entry_addr;
+  set_objfile_data (objfile, jit_objfile_data, entry_addr_ptr);
+
+  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);
+}
+
+/* Look up the objfile with this code entry address.  */
+
+static struct objfile *
+jit_find_objf_with_entry_addr (CORE_ADDR entry_addr)
+{
+  struct objfile *objf;
+  CORE_ADDR *objf_entry_addr;
+
+  ALL_OBJFILES (objf)
+    {
+      objf_entry_addr = (CORE_ADDR *) objfile_data (objf, jit_objfile_data);
+      if (objf_entry_addr != NULL && *objf_entry_addr == entry_addr)
+        return objf;
+    }
+  return NULL;
+}
+
+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;
+  struct cleanup *old_cleanups;
+
+  /* When we register code, GDB resets its breakpoints in case symbols have
+     changed.  That in turn calls this handler, which makes us look for new
+     code again.  To avoid being re-entered, we check this flag.  */
+  if (registering_code)
+    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);
+
+      /* This hook may be called many times during setup, so make sure we don't
+         add the same symbol file twice.  */
+      if (jit_find_objf_with_entry_addr (cur_entry_addr) != NULL)
+        continue;
+
+      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;
+
+  /* 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:
+      objf = jit_find_objf_with_entry_addr (entry_addr);
+      if (objf == NULL)
+        printf_unfiltered ("Unable to find JITed code entry at address: %p\n",
+                           (void *) entry_addr);
+      else
+        jit_unregister_code (objf);
+
+      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 ();
+}
diff --git a/gdb/jit.h b/gdb/jit.h
new file mode 100644 (file)
index 0000000..6473d25
--- /dev/null
+++ b/gdb/jit.h
@@ -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 */