This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: JIT Reader


Hi,

New patches attached.  Fixed the formatting errors, refactored
jit_reader_try_read_symtab to remove the label and the gotos
altogether, and fixed a small typo in the docs that I had overlooked.
Everything else is the same.

Thank you for your infinite patience! :)
-- 
Sanjoy Das
http://playingwithpointers.com
>From 9144218a00928583e4085f9d63f6413e73758218 Mon Sep 17 00:00:00 2001
From: Sanjoy Das <sanjoy@playingwithpointers.com>
Date: Tue, 25 Oct 2011 18:50:53 +0530
Subject: [PATCH 1/7] Introduce jit-reader.in and modify build system.

jit-reader.in will host the interface to be implemented and the API to
be used by the reader.  The file needs to be processed by ./configure
to produce `jit.reader.h'; so that GDB_CORE_ADDR is defined correctly.

This commit arranges for `jit-reader.h' to be installed in the global
include directory.

gdb/ChangeLog:
	* gdb/Makefile.in: Add jit-reader.h as a header.  Have it
	installed in $(includedir)/gdb.
	* gdb/configure.ac: Genereate a correct value for TARGET_PTR for
	jit-reader.h.  Tell configure to generate jit-reader.h from
	jit-reader.in.
	* gdb/configure: Re-generated by autoconf.
	* gdb/jit-reader.in: New file.
	* gdb/jit.c: Include jit-reader.h.
---
 gdb/Makefile.in   |   13 ++-
 gdb/configure     |  124 +++++++++++++++++++
 gdb/configure.ac  |   22 ++++
 gdb/jit-reader.in |  344 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/jit.c         |    1 +
 5 files changed, 500 insertions(+), 4 deletions(-)
 create mode 100644 gdb/jit-reader.in

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 7a7ff9f..ff5f246 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -833,7 +833,7 @@ common/linux-osdata.h
 
 # Header files that already have srcdir in them, or which are in objdir.
 
-HFILES_WITH_SRCDIR = ../bfd/bfd.h
+HFILES_WITH_SRCDIR = ../bfd/bfd.h jit-reader.h
 
 
 # GDB "info" files, which should be included in their entirety
@@ -950,7 +950,7 @@ DISTSTUFF = $(YYFILES)
 
 
 # All generated files which can be included by another file.
-generated_files = config.h observer.h observer.inc ada-lex.c \
+generated_files = config.h observer.h observer.inc ada-lex.c jit-reader.h \
 	$(GNULIB_H) $(NAT_GENERATED_FILES)
 
 .c.o:
@@ -1035,7 +1035,9 @@ install-only: $(CONFIG_INSTALL)
 		$(SHELL) $(srcdir)/../mkinstalldirs \
 			$(DESTDIR)$(man1dir) ; \
 		$(INSTALL_DATA) $(srcdir)/gdb.1 \
-			$(DESTDIR)$(man1dir)/$$transformed_name.1
+			$(DESTDIR)$(man1dir)/$$transformed_name.1 ; \
+		$(SHELL) $(srcdir)/../mkinstalldirs $(DESTDIR)$(includedir)/gdb ; \
+		$(INSTALL_DATA) jit-reader.h $(DESTDIR)$(includedir)/gdb/jit-reader.h
 	@$(MAKE) DO=install "DODIRS=$(SUBDIRS)" $(FLAGS_TO_PASS) subdir_do
 .PHONY: install-tui
 install-tui:
@@ -1263,7 +1265,7 @@ distclean: clean
 	rm -f gdbserver/config.status gdbserver/config.log
 	rm -f gdbserver/tm.h gdbserver/xm.h gdbserver/nm.h
 	rm -f gdbserver/Makefile gdbserver/config.cache
-	rm -f nm.h config.status config.h stamp-h .gdbinit
+	rm -f nm.h config.status config.h stamp-h .gdbinit jit-reader.h
 	rm -f y.output yacc.acts yacc.tmp y.tab.h
 	rm -f config.log config.cache
 	rm -f Makefile
@@ -1329,6 +1331,9 @@ data-directory/Makefile: data-directory/Makefile.in config.status @frags@
 	  CONFIG_LINKS= \
 	  $(SHELL) config.status
 
+jit-reader.h: $(srcdir)/jit-reader.in
+	$(SHELL) config.status $@
+
 config.h: stamp-h ; @true
 stamp-h: $(srcdir)/config.in config.status
 	CONFIG_HEADERS=config.h:config.in \
diff --git a/gdb/configure b/gdb/configure
index aec9d43..a206ee0 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -666,6 +666,7 @@ python_prog_path
 LTLIBEXPAT
 LIBEXPAT
 HAVE_LIBEXPAT
+TARGET_PTR
 READLINE_TEXI_INCFLAG
 READLINE_CFLAGS
 READLINE_DEPS
@@ -9884,6 +9885,128 @@ fi
 
 
 
+# Generate jit-reader.h
+
+# This is typedeffed to GDB_CORE_ADDR in jit-reader.h
+TARGET_PTR=
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned long long" >&5
+$as_echo_n "checking size of unsigned long long... " >&6; }
+if test "${ac_cv_sizeof_unsigned_long_long+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long long))" "ac_cv_sizeof_unsigned_long_long"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type_unsigned_long_long" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ as_fn_set_status 77
+as_fn_error "cannot compute sizeof (unsigned long long)
+See \`config.log' for more details." "$LINENO" 5; }; }
+   else
+     ac_cv_sizeof_unsigned_long_long=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long_long" >&5
+$as_echo "$ac_cv_sizeof_unsigned_long_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_UNSIGNED_LONG_LONG $ac_cv_sizeof_unsigned_long_long
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned long" >&5
+$as_echo_n "checking size of unsigned long... " >&6; }
+if test "${ac_cv_sizeof_unsigned_long+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long))" "ac_cv_sizeof_unsigned_long"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type_unsigned_long" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ as_fn_set_status 77
+as_fn_error "cannot compute sizeof (unsigned long)
+See \`config.log' for more details." "$LINENO" 5; }; }
+   else
+     ac_cv_sizeof_unsigned_long=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long" >&5
+$as_echo "$ac_cv_sizeof_unsigned_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_UNSIGNED_LONG $ac_cv_sizeof_unsigned_long
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned __int128" >&5
+$as_echo_n "checking size of unsigned __int128... " >&6; }
+if test "${ac_cv_sizeof_unsigned___int128+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned __int128))" "ac_cv_sizeof_unsigned___int128"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type_unsigned___int128" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ as_fn_set_status 77
+as_fn_error "cannot compute sizeof (unsigned __int128)
+See \`config.log' for more details." "$LINENO" 5; }; }
+   else
+     ac_cv_sizeof_unsigned___int128=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned___int128" >&5
+$as_echo "$ac_cv_sizeof_unsigned___int128" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_UNSIGNED___INT128 $ac_cv_sizeof_unsigned___int128
+_ACEOF
+
+
+
+if test "x${ac_cv_sizeof_unsigned_long}" = "x8"; then
+  TARGET_PTR="unsigned long"
+elif test "x${ac_cv_sizeof_unsigned_long_long}" = "x8"; then
+  TARGET_PTR="unsigned long long"
+elif test "x${ac_cv_sizeof_unsigned___int128}" = "x16"; then
+  TARGET_PTR="unsigned __int128"
+else
+  TARGET_PTR="unsigned long"
+fi
+
+
+ac_config_files="$ac_config_files jit-reader.h:jit-reader.in"
+
+
 
 # Check whether --with-expat was given.
 if test "${with_expat+set}" = set; then :
@@ -16794,6 +16917,7 @@ do
     "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h:config.in" ;;
     "depdir") CONFIG_COMMANDS="$CONFIG_COMMANDS depdir" ;;
     "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
+    "jit-reader.h") CONFIG_FILES="$CONFIG_FILES jit-reader.h:jit-reader.in" ;;
     "$ac_config_links_1") CONFIG_LINKS="$CONFIG_LINKS $ac_config_links_1" ;;
     "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
     ".gdbinit") CONFIG_FILES="$CONFIG_FILES .gdbinit:gdbinit.in" ;;
diff --git a/gdb/configure.ac b/gdb/configure.ac
index fbbb2de..ac8707a 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -605,6 +605,28 @@ AC_SUBST(READLINE_DEPS)
 AC_SUBST(READLINE_CFLAGS)
 AC_SUBST(READLINE_TEXI_INCFLAG)
 
+# Generate jit-reader.h
+
+# This is typedeffed to GDB_CORE_ADDR in jit-reader.h
+TARGET_PTR=
+
+AC_CHECK_SIZEOF(unsigned long long)
+AC_CHECK_SIZEOF(unsigned long)
+AC_CHECK_SIZEOF(unsigned __int128)
+
+if test "x${ac_cv_sizeof_unsigned_long}" = "x8"; then
+  TARGET_PTR="unsigned long"
+elif test "x${ac_cv_sizeof_unsigned_long_long}" = "x8"; then
+  TARGET_PTR="unsigned long long"
+elif test "x${ac_cv_sizeof_unsigned___int128}" = "x16"; then
+  TARGET_PTR="unsigned __int128"
+else
+  TARGET_PTR="unsigned long"
+fi
+
+AC_SUBST(TARGET_PTR)
+AC_CONFIG_FILES([jit-reader.h:jit-reader.in])
+
 AC_ARG_WITH(expat,
   AS_HELP_STRING([--with-expat], [include expat support (auto/yes/no)]),
   [], [with_expat=auto])
diff --git a/gdb/jit-reader.in b/gdb/jit-reader.in
new file mode 100644
index 0000000..3efe722
--- /dev/null
+++ b/gdb/jit-reader.in
@@ -0,0 +1,344 @@
+/* JIT declarations for GDB, the GNU Debugger.
+
+   Copyright (C) 2011 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 GDB_JIT_READER_H
+#define GDB_JIT_READER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Versioning information.  See gdb_reader_funcs.  */
+
+#define GDB_READER_INTERFACE_VERSION 1
+
+/* Readers must be released under a GPL compatible license.  To
+   declare that the reader is indeed released under a GPL compatible
+   license, invoke the macro GDB_DECLARE_GPL_COMPATIBLE in a source
+   file.  */
+
+#ifdef __cplusplus
+#define GDB_DECLARE_GPL_COMPATIBLE_READER       \
+  extern "C" {                                  \
+  extern int plugin_is_GPL_compatible (void)    \
+  {                                             \
+    return 0;                                   \
+  }                                             \
+  }
+
+#else
+
+#define GDB_DECLARE_GPL_COMPATIBLE_READER
+  extern int plugin_is_GPL_compatible (void)    \
+  {                                             \
+    return 0;                                   \
+  }
+
+#endif
+
+/* Represents an address on the target system.  */
+
+typedef @TARGET_PTR@ GDB_CORE_ADDR;
+
+/* Return status codes.  */
+
+enum gdb_status {
+  GDB_FAIL = 0,
+  GDB_SUCCESS = 1
+};
+
+struct gdb_object;
+struct gdb_symtab;
+struct gdb_block;
+struct gdb_symbol_callbacks;
+
+/* An array of these are used to represent a map from code addresses to line
+   numbers in the source file.  */
+
+struct gdb_line_mapping
+{
+  int line;
+  GDB_CORE_ADDR pc;
+};
+
+/* Create a new GDB code object.  Each code object can have one or
+   more symbol tables, each representing a compiled source file.  */
+
+typedef struct gdb_object *(gdb_object_open) (struct gdb_symbol_callbacks *cb);
+
+/* The callback used to create new symbol table.  CB is the
+   gdb_symbol_callbacks which the structure is part of.  FILE_NAME is
+   an (optionally NULL) file name to associate with this new symbol
+   table.
+
+   Returns a new instance to gdb_symtab that can later be passed to
+   gdb_block_new, gdb_symtab_add_line_mapping and gdb_symtab_close.  */
+
+typedef struct gdb_symtab *(gdb_symtab_open) (struct gdb_symbol_callbacks *cb,
+                                              struct gdb_object *obj,
+                                              const char *file_name);
+
+/* Creates a new block in a given symbol table.  A symbol table is a
+   forest of blocks, each block representing an code address range and
+   a corresponding (optionally NULL) NAME.  In case the block
+   corresponds to a function, the NAME passed should be the name of
+   the function.
+
+   If the new block to be created is a child of (i.e. is nested in)
+   another block, the parent block can be passed in PARENT.  SYMTAB is
+   the symbol table the new block is to belong in.  BEGIN, END is the
+   code address range the block corresponds to.
+
+   Returns a new instance of gdb_block, which, as of now, has no use.
+   Note that the gdb_block returned must not be freed by the
+   caller.  */
+
+typedef struct gdb_block *(gdb_block_open) (struct gdb_symbol_callbacks *cb,
+                                            struct gdb_symtab *symtab,
+                                            struct gdb_block *parent,
+                                            GDB_CORE_ADDR begin,
+                                            GDB_CORE_ADDR end,
+                                            const char *name);
+
+/* Adds a PC to line number mapping for the symbol table SYMTAB.
+   NLINES is the number of elements in LINES, each element
+   corresponding to one (PC, line) pair.  */
+
+typedef void (gdb_symtab_add_line_mapping) (struct gdb_symbol_callbacks *cb,
+                                            struct gdb_symtab *symtab,
+                                            int nlines,
+                                            struct gdb_line_mapping *lines);
+
+/* Close the symtab SYMTAB.  This signals to GDB that no more blocks
+   will be opened on this symtab.  */
+
+typedef void (gdb_symtab_close) (struct gdb_symbol_callbacks *cb,
+                                 struct gdb_symtab *symtab);
+
+
+/* Closes the gdb_object OBJ and adds the emitted information into
+   GDB's internal structures.  Once this is done, the debug
+   information will be picked up and used; this will usually be the
+   last operation in gdb_read_debug_info.  */
+
+typedef void (gdb_object_close) (struct gdb_symbol_callbacks *cb,
+                                 struct gdb_object *obj);
+
+/* Reads LEN bytes from TARGET_MEM in the target's virtual address
+   space into GDB_BUF.
+
+   Returns GDB_FAIL on failure, and GDB_SUCCESS on success.  */
+
+typedef enum gdb_status (gdb_target_read) (GDB_CORE_ADDR target_mem,
+                                           void *gdb_buf, int len);
+
+/* The list of callbacks that are passed to read.  These callbacks are
+   to be used to construct the symbol table.  The functions have been
+   described above.  */
+
+struct gdb_symbol_callbacks
+{
+  gdb_object_open *object_open;
+  gdb_symtab_open *symtab_open;
+  gdb_block_open *block_open;
+  gdb_symtab_close *symtab_close;
+  gdb_object_close *object_close;
+
+  gdb_symtab_add_line_mapping *line_mapping_add;
+  gdb_target_read *target_read;
+
+  /* For internal use by GDB.  */
+  void *priv_data;
+};
+
+/* Forward declaration.  */
+
+struct gdb_reg_value;
+
+/* A function of this type is used to free a gdb_reg_value.  See the
+   comment on `free' in struct gdb_reg_value.  */
+
+typedef void (gdb_reg_value_free) (struct gdb_reg_value *);
+
+/* Denotes the value of a register.  */
+
+struct gdb_reg_value
+{
+  /* The size of the register in bytes.  The reader need not set this
+     field.  This will be set for (defined) register values being read
+     from GDB using reg_get.  */
+  int size;
+
+  /* Set to non-zero if the value for the register is known.  The
+     registers for which the reader does not call reg_set are also
+     assumed to be undefined */
+  int defined;
+
+  /* Since gdb_reg_value is a variable sized structure, it will
+     usually be allocated on the heap.  This function is expected to
+     contain the corresponding "free" function.
+
+     When a pointer to gdb_reg_value is being sent from GDB to the
+     reader (via gdb_unwind_reg_get), the reader is expected to call
+     this function (with the same gdb_reg_value as argument) once it
+     is done with the value.
+
+     When the function sends the a gdb_reg_value to GDB (via
+     gdb_unwind_reg_set), it is expected to set this field to point to
+     an appropriate cleanup routine (or to NULL if no cleanup is
+     required).  */
+  gdb_reg_value_free *free;
+
+  /* The value of the register.  */
+  unsigned char value[1];
+};
+
+/* get_frame_id in gdb_reader_funcs is to return a gdb_frame_id
+   corresponding to the current frame.  The registers corresponding to
+   the current frame can be read using reg_get.  Calling get_frame_id
+   on a particular frame should return the same gdb_frame_id
+   throughout its lifetime (i.e. till before it gets unwound).  One
+   way to do this is by having the CODE_ADDRESS point to the
+   function's first instruction and STACK_ADDRESS point to the value
+   of the stack pointer when entering the function.  */
+
+struct gdb_frame_id
+{
+  GDB_CORE_ADDR code_address;
+  GDB_CORE_ADDR stack_address;
+};
+
+/* Forward declaration.  */
+
+struct gdb_unwind_callbacks;
+
+/* Returns the value of a particular register in the current frame.
+   The current frame is the frame that needs to be unwound into the
+   outer (earlier) frame.
+
+   CB is the struct gdb_unwind_callbacks * the callback belongs to.
+   REGNUM is the DWARF register number of the register that needs to
+   be unwound.
+
+   Returns the gdb_reg_value corresponding to the register requested.
+   In case the value of the register has been optimized away or
+   otherwise unavailable, the defined flag in the returned
+   gdb_reg_value will be zero.  */
+
+typedef struct gdb_reg_value *(gdb_unwind_reg_get)
+                              (struct gdb_unwind_callbacks *cb, int regnum);
+
+/* Sets the previous value of a particular register.  REGNUM is the
+   (DWARF) register number whose value is to be set.  VAL is the value
+   the register is to be set to.
+
+   VAL is *not* copied, so the memory allocated to it cannot be
+   reused.  Once GDB no longer needs the value, it is deallocated
+   using the FREE function (see gdb_reg_value).
+
+   A register can also be "set" to an undefined value by setting the
+   defined in VAL to zero.  */
+
+typedef void (gdb_unwind_reg_set) (struct gdb_unwind_callbacks *cb, int regnum,
+                                   struct gdb_reg_value *val);
+
+/* This struct is passed to unwind in gdb_reader_funcs, and is to be
+   used to unwind the current frame (current being the frame whose
+   registers can be read using reg_get) into the earlier frame.  The
+   functions have been described above.  */
+
+struct gdb_unwind_callbacks
+{
+  gdb_unwind_reg_get *reg_get;
+  gdb_unwind_reg_set *reg_set;
+  gdb_target_read *target_read;
+
+  /* For internal use by GDB.  */
+  void *priv_data;
+};
+
+/* Forward declaration.  */
+
+struct gdb_reader_funcs;
+
+/* Parse the debug info off a block of memory, pointed to by MEMORY
+   (already copied to GDB's address space) and MEMORY_SZ bytes long.
+   The implementation has to use the functions in CB to actually emit
+   the parsed data into GDB.  SELF is the same structure returned by
+   gdb_init_reader.
+
+   Return GDB_FAIL on failure and GDB_SUCCESS on success.  */
+
+typedef enum gdb_status (gdb_read_debug_info) (struct gdb_reader_funcs *self,
+                                               struct gdb_symbol_callbacks *cb,
+                                               void *memory, long memory_sz);
+
+/* Unwind the current frame, CB is the set of unwind callbacks that
+   are to be used to do this.
+
+   Return GDB_FAIL on failure and GDB_SUCCESS on success.  */
+
+typedef enum gdb_status (gdb_unwind_frame) (struct gdb_reader_funcs *self,
+                                            struct gdb_unwind_callbacks *cb);
+
+/* Return the frame ID corresponding to the current frame, using C to
+   read the current register values.  See the comment on struct
+   gdb_frame_id.  */
+
+typedef struct gdb_frame_id (gdb_get_frame_id) (struct gdb_reader_funcs *self,
+                                                struct gdb_unwind_callbacks *c);
+
+/* Called when a reader is being unloaded.  This function should also
+   free SELF, if required.  */
+
+typedef void (gdb_destroy_reader) (struct gdb_reader_funcs *self);
+
+/* Called when the reader is loaded.  Must either return a properly
+   populated gdb_reader_funcs or NULL.  The memory allocated for the
+   gdb_reader_funcs is to be managed by the reader itself (i.e. if it
+   is allocated from the heap, it must also be freed in
+   gdb_destroy_reader).  */
+
+extern struct gdb_reader_funcs *gdb_init_reader (void);
+
+/* Pointer to the functions which implement the reader's
+   functionality.  The individual functions have been documented
+   above.
+
+   None of the fields are optional.  */
+
+struct gdb_reader_funcs
+{
+  /* Must be set to GDB_READER_INTERFACE_VERSION.  */
+  int reader_version;
+
+  /* For use by the reader.  */
+  void *priv_data;
+
+  gdb_read_debug_info *read;
+  gdb_unwind_frame *unwind;
+  gdb_get_frame_id *get_frame_id;
+  gdb_destroy_reader *destroy;
+};
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/gdb/jit.c b/gdb/jit.c
index 954d297..85e40ac 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -20,6 +20,7 @@
 #include "defs.h"
 
 #include "jit.h"
+#include "jit-reader.h"
 #include "breakpoint.h"
 #include "command.h"
 #include "gdbcmd.h"
-- 
1.7.7

>From 42fd7d24efe4cc547cf4cdeac14eeed6ecab7df8 Mon Sep 17 00:00:00 2001
From: Sanjoy Das <sanjoy@playingwithpointers.com>
Date: Tue, 25 Oct 2011 18:50:53 +0530
Subject: [PATCH 2/7] Relocatable directory for loading JIT readers.

Add a new directory `jit_reader_dir' to jit.c, which is relocated
during initialization.  The value of the directory can be set by using
--with-jit-reader-dir on configure and defaults to `${libdir}/gdb'.

gdb/ChangeLog:
	* gdb/config.in: Add new #defines: JIT_READER_DIR and
	JIT_READER_DIR_RELOCATABLE.
	* gdb/configure.ac: New GDB directory entry for jit-reader-dir.
	* gdb/configure: Re-generated by autoconf.
	* gdb/jit.c: New static variable: const char *jit_reader_dir.
	(_initialize_jit): Relocate jit_reader_dir.
---
 gdb/config.in    |    6 ++++++
 gdb/configure    |   51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/configure.ac |    4 ++++
 gdb/jit.c        |    4 ++++
 4 files changed, 65 insertions(+), 0 deletions(-)

diff --git a/gdb/config.in b/gdb/config.in
index c1d7c68..27a4e2c 100644
--- a/gdb/config.in
+++ b/gdb/config.in
@@ -982,3 +982,9 @@
 
 /* Define as `fork' if `vfork' does not work. */
 #undef vfork
+
+/* The directory from which JIT readers should be loaded.  */
+#undef JIT_READER_DIR
+
+/* Define if JIT_READER_DIR should be relocated when GDB is moved.  */
+#undef JIT_READER_DIR_RELOCATABLE
diff --git a/gdb/configure b/gdb/configure
index a206ee0..78cd9e9 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -666,6 +666,7 @@ python_prog_path
 LTLIBEXPAT
 LIBEXPAT
 HAVE_LIBEXPAT
+JIT_READER_DIR
 TARGET_PTR
 READLINE_TEXI_INCFLAG
 READLINE_CFLAGS
@@ -966,6 +967,7 @@ with_zlib
 with_libiconv_prefix
 with_iconv_bin
 with_system_readline
+with_jit_reader_dir
 with_expat
 with_gnu_ld
 enable_rpath
@@ -1666,6 +1668,8 @@ Optional Packages:
                           search for libiconv in DIR/include and DIR/lib
   --with-iconv-bin=PATH   specify where to find the iconv program
   --with-system-readline  use installed readline library
+  --with-jit-reader-dir=PATH
+                          directory to load the JIT readers from
   --with-expat            include expat support (auto/yes/no)
   --with-gnu-ld           assume the C compiler uses GNU ld default=no
   --with-libexpat-prefix[=DIR]  search for libexpat in DIR/include and DIR/lib
@@ -10008,6 +10012,53 @@ ac_config_files="$ac_config_files jit-reader.h:jit-reader.in"
 
 
 
+
+# Check whether --with-jit-reader-dir was given.
+if test "${with_jit_reader_dir+set}" = set; then :
+  withval=$with_jit_reader_dir;
+    JIT_READER_DIR=$withval
+else
+  JIT_READER_DIR=${libdir}/gdb
+fi
+
+
+  test "x$prefix" = xNONE && prefix="$ac_default_prefix"
+  test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+  ac_define_dir=`eval echo $JIT_READER_DIR`
+  ac_define_dir=`eval echo $ac_define_dir`
+
+cat >>confdefs.h <<_ACEOF
+#define JIT_READER_DIR "$ac_define_dir"
+_ACEOF
+
+
+
+
+  if test "x$exec_prefix" = xNONE || test "x$exec_prefix" = 'x${prefix}'; then
+     if test "x$prefix" = xNONE; then
+     	test_prefix=/usr/local
+     else
+	test_prefix=$prefix
+     fi
+  else
+     test_prefix=$exec_prefix
+  fi
+  value=0
+  case ${ac_define_dir} in
+     "${test_prefix}"|"${test_prefix}/"*|\
+	'${exec_prefix}'|'${exec_prefix}/'*)
+     value=1
+     ;;
+  esac
+
+cat >>confdefs.h <<_ACEOF
+#define JIT_READER_DIR_RELOCATABLE $value
+_ACEOF
+
+
+
+
+
 # Check whether --with-expat was given.
 if test "${with_expat+set}" = set; then :
   withval=$with_expat;
diff --git a/gdb/configure.ac b/gdb/configure.ac
index ac8707a..4ecb5c9 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -627,6 +627,10 @@ fi
 AC_SUBST(TARGET_PTR)
 AC_CONFIG_FILES([jit-reader.h:jit-reader.in])
 
+GDB_AC_WITH_DIR([JIT_READER_DIR], [jit-reader-dir],
+                [directory to load the JIT readers from],
+                [${libdir}/gdb])
+
 AC_ARG_WITH(expat,
   AS_HELP_STRING([--with-expat], [include expat support (auto/yes/no)]),
   [], [with_expat=auto])
diff --git a/gdb/jit.c b/gdb/jit.c
index 85e40ac..af33a91 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -33,6 +33,8 @@
 #include "target.h"
 #include "gdb_stat.h"
 
+static const char *jit_reader_dir = NULL;
+
 static const struct objfile_data *jit_objfile_data;
 
 static const char *const jit_break_name = "__jit_debug_register_code";
@@ -558,6 +560,8 @@ extern void _initialize_jit (void);
 void
 _initialize_jit (void)
 {
+  jit_reader_dir = relocate_gdb_directory (JIT_READER_DIR,
+                                           JIT_READER_DIR_RELOCATABLE);
   add_setshow_zinteger_cmd ("jit", class_maintenance, &jit_debug,
 			    _("Set JIT debugging."),
 			    _("Show JIT debugging."),
-- 
1.7.7

>From 889e0bad82e809f9bae87f518f4178f4d1a7c660 Mon Sep 17 00:00:00 2001
From: Sanjoy Das <sanjoy@playingwithpointers.com>
Date: Tue, 25 Oct 2011 18:50:53 +0530
Subject: [PATCH 3/7] Platform agnostic dynamic loading code.

gdb-dlfcn.h and gdb-dlfcn.c are added, which implement the (cross
platform) functions is_dl_available, gdb_dlopen, gdb_dlsym and
gdb_dlclose. They should work correctly on POSIX and windows systems.

gdb/ChangeLog:
	* gdb/Makefile.in: Add gdb-dlfcn.c and gdb-dlfcn.h to build
	system.
	* gdb/config.in: Add new #define HAVE_DLFCN_H.
	* gdb/configure.ac: Add check for dlopen and for dlfcn.h
	* gdb/configure: Re-generated by autoconf.
---
 gdb/Makefile.in  |    6 +-
 gdb/config.in    |    3 +
 gdb/configure    |   60 ++++++++++++++++++++++-
 gdb/configure.ac |    5 ++-
 gdb/gdb-dlfcn.c  |  143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/gdb-dlfcn.h  |   51 +++++++++++++++++++
 6 files changed, 263 insertions(+), 5 deletions(-)
 create mode 100644 gdb/gdb-dlfcn.c
 create mode 100644 gdb/gdb-dlfcn.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index ff5f246..48221f2 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -748,7 +748,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
 	annotate.c common/signals.c copying.c dfp.c gdb.c inf-child.c \
 	regset.c sol-thread.c windows-termcap.c \
 	common/common-utils.c common/xml-utils.c \
-	common/ptid.c common/buffer.c
+	common/ptid.c common/buffer.c gdb-dlfcn.c
 
 LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
 
@@ -829,7 +829,7 @@ solib-darwin.h solib-ia64-hpux.h solib-spu.h windows-nat.h xcoffread.h \
 gnulib/extra/arg-nonnull.h gnulib/extra/c++defs.h gnulib/extra/warn-on-use.h \
 gnulib/stddef.in.h inline-frame.h skip.h \
 common/common-utils.h common/xml-utils.h common/buffer.h common/ptid.h \
-common/linux-osdata.h
+common/linux-osdata.h gdb-dlfcn.h
 
 # Header files that already have srcdir in them, or which are in objdir.
 
@@ -916,7 +916,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
 	inferior.o osdata.o gdb_usleep.o record.o gcore.o \
 	jit.o progspace.o skip.o \
-	common-utils.o buffer.o ptid.o
+	common-utils.o buffer.o ptid.o gdb-dlfcn.o
 
 TSOBS = inflow.o
 
diff --git a/gdb/config.in b/gdb/config.in
index 27a4e2c..4af4e69 100644
--- a/gdb/config.in
+++ b/gdb/config.in
@@ -988,3 +988,6 @@
 
 /* Define if JIT_READER_DIR should be relocated when GDB is moved.  */
 #undef JIT_READER_DIR_RELOCATABLE
+
+/* Define if the platform has dlfcn.h.  */
+#undef HAVE_DLFCN_H
diff --git a/gdb/configure b/gdb/configure
index 78cd9e9..f8d038c 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -10011,6 +10011,63 @@ fi
 ac_config_files="$ac_config_files jit-reader.h:jit-reader.in"
 
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dlopen" >&5
+$as_echo_n "checking for library containing dlopen... " >&6; }
+if test "${ac_cv_search_dlopen+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' dl; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_search_dlopen=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if test "${ac_cv_search_dlopen+set}" = set; then :
+  break
+fi
+done
+if test "${ac_cv_search_dlopen+set}" = set; then :
+
+else
+  ac_cv_search_dlopen=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlopen" >&5
+$as_echo "$ac_cv_search_dlopen" >&6; }
+ac_res=$ac_cv_search_dlopen
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+
 
 
 # Check whether --with-jit-reader-dir was given.
@@ -11607,7 +11664,8 @@ for ac_header in nlist.h machine/reg.h poll.h sys/poll.h proc_service.h \
 		  sys/resource.h sys/procfs.h sys/ptrace.h ptrace.h \
 		  sys/reg.h sys/debugreg.h sys/select.h sys/syscall.h \
 		  sys/types.h sys/wait.h wait.h termios.h termio.h \
-		  sgtty.h unistd.h elf_hp.h ctype.h time.h locale.h
+		  sgtty.h unistd.h elf_hp.h ctype.h time.h locale.h \
+                  dlfcn.h
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
 ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
diff --git a/gdb/configure.ac b/gdb/configure.ac
index 4ecb5c9..cbbf647 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -627,6 +627,8 @@ fi
 AC_SUBST(TARGET_PTR)
 AC_CONFIG_FILES([jit-reader.h:jit-reader.in])
 
+AC_SEARCH_LIBS(dlopen, dl)
+
 GDB_AC_WITH_DIR([JIT_READER_DIR], [jit-reader-dir],
                 [directory to load the JIT readers from],
                 [${libdir}/gdb])
@@ -981,7 +983,8 @@ AC_CHECK_HEADERS([nlist.h machine/reg.h poll.h sys/poll.h proc_service.h \
 		  sys/resource.h sys/procfs.h sys/ptrace.h ptrace.h \
 		  sys/reg.h sys/debugreg.h sys/select.h sys/syscall.h \
 		  sys/types.h sys/wait.h wait.h termios.h termio.h \
-		  sgtty.h unistd.h elf_hp.h ctype.h time.h locale.h])
+		  sgtty.h unistd.h elf_hp.h ctype.h time.h locale.h \
+		  dlfcn.h])
 AC_CHECK_HEADERS(link.h, [], [],
 [#if HAVE_SYS_TYPES_H
 # include <sys/types.h>
diff --git a/gdb/gdb-dlfcn.c b/gdb/gdb-dlfcn.c
new file mode 100644
index 0000000..604876e
--- /dev/null
+++ b/gdb/gdb-dlfcn.c
@@ -0,0 +1,143 @@
+/* Platform independent shared object routines for GDB.
+
+   Copyright (C) 2011 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 "gdb_assert.h"
+
+#include "gdb-dlfcn.h"
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#elif __MINGW32__
+#include <windows.h>
+#else
+/* Unsupported configuration. */
+#define NO_SHARED_LIB
+#endif
+
+#ifdef NO_SHARED_LIB
+
+void *
+gdb_dlopen (const char *filename)
+{
+  gdb_assert_not_reached ("gdb_dlopen should not be called on this platform.");
+  return NULL;
+}
+
+void *
+gdb_dlsym (void *handle, const char *symbol)
+{
+  gdb_assert_not_reached ("gdb_dlsym should not be called on this platform.");
+  return NULL;
+}
+
+struct cleanup *
+make_cleanup_dlclose (void *handle)
+{
+  gdb_assert_not_reached ("make_cleanup_dlclose should not be called on this "
+                          "platform.");
+  return NULL;
+}
+
+int
+gdb_dlclose (void *handle)
+{
+  gdb_assert_not_reached ("gdb_dlclose should not be called on this platform.");
+  return 0;
+}
+
+int
+is_dl_available (void)
+{
+  return 0;
+}
+
+#else /* NO_SHARED_LIB */
+
+void *
+gdb_dlopen (const char *filename)
+{
+  void *result;
+#ifdef HAVE_DLFCN_H
+  result = dlopen (filename, RTLD_NOW);
+#elif __MINGW32__
+  result = (void *) LoadLibrary (filename);
+#endif
+  if (result != NULL)
+    return result;
+
+#ifdef HAVE_DLFCN_H
+  error (_("Could not load %s: %s"), filename, dlerror());
+#else
+  {
+    LPVOID buffer;
+    DWORD dw;
+
+    dw = GetLastError();
+
+    FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+                   FORMAT_MESSAGE_IGNORE_INSERTS,
+                   NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                   (LPTSTR) &buffer
+                   0, NULL);
+
+    error (_("Could not load %s: %s"), filename, (char *) buffer);
+  }
+#endif
+}
+
+void *
+gdb_dlsym (void *handle, const char *symbol)
+{
+#ifdef HAVE_DLFCN_H
+  return dlsym (handle, symbol);
+#elif __MINGW32__
+  return (void *) GetProcAddress (handle, symbol);
+#endif
+}
+
+int
+gdb_dlclose (void *handle)
+{
+#ifdef HAVE_DLFCN_H
+  return dlclose (handle);
+#elif __MINGW32__
+  return !((int) FreeLibrary (handle));
+#endif
+}
+
+static void
+do_dlclose_cleanup (void *handle)
+{
+  gdb_dlclose (handle);
+}
+
+struct cleanup *
+make_cleanup_dlclose (void *handle)
+{
+  return make_cleanup (do_dlclose_cleanup, handle);
+}
+
+int
+is_dl_available (void)
+{
+  return 1;
+}
+
+#endif /* NO_SHARED_LIB */
diff --git a/gdb/gdb-dlfcn.h b/gdb/gdb-dlfcn.h
new file mode 100644
index 0000000..0a9d1eb
--- /dev/null
+++ b/gdb/gdb-dlfcn.h
@@ -0,0 +1,51 @@
+/* Platform independent shared object routines for GDB.
+
+   Copyright (C) 2011 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 GDB_DLFCN_H
+#define GDB_DLFCN_H
+
+#include "defs.h"
+
+/* Load the dynamic library file named FILENAME, and return a handle
+   for that dynamic library.  Return NULL if the loading fails for any
+   reason.  */
+
+void *gdb_dlopen (const char *filename);
+
+/* Return the address of the symbol named SYMBOL inside the shared
+   library whose handle is HANDLE.  Return NULL when the symbol could
+   not be found.  */
+
+void *gdb_dlsym (void *handle, const char *symbol);
+
+/* Install a cleanup routine which closes the handle HANDLE.  */
+
+struct cleanup *make_cleanup_dlclose (void *handle);
+
+/* Cleanup the shared object pointed to by HANDLE. Return 0 on success
+   and nonzero on failure.  */
+
+int gdb_dlclose (void *handle);
+
+/* Return non-zero if the dynamic library functions are available on
+   this platform.  */
+
+int is_dl_available(void);
+
+#endif /* GDB_DLFCN_H */
-- 
1.7.7

>From 37080394ab35ec20f24fb7b7b294630090642562 Mon Sep 17 00:00:00 2001
From: Sanjoy Das <sanjoy@playingwithpointers.com>
Date: Tue, 25 Oct 2011 18:50:53 +0530
Subject: [PATCH 4/7] New commands for loading and unloading a reader.

Introduces two new GDB commands - `load-jit-reader' and
`unload-jit-reader'.  The commands are not added if the platform does
not support loading dynamic libraries.

gdb/ChangeLog:
	* gdb/jit.c: Include gdb-dlfcn.h.
	(loaded_jit_reader, reader_init_fn_sym): New static variables.
	(jit_reader_load, jit_reader_load_command)
	(jit_reader_unload_command): New functions.
	(_initialize_jit): Add commands "jit-reader-load" and
	"jit-reader-unload".
---
 gdb/jit.c |  100 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 100 insertions(+), 0 deletions(-)

diff --git a/gdb/jit.c b/gdb/jit.c
index af33a91..7cd70d0 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -31,6 +31,7 @@
 #include "symfile.h"
 #include "symtab.h"
 #include "target.h"
+#include "gdb-dlfcn.h"
 #include "gdb_stat.h"
 
 static const char *jit_reader_dir = NULL;
@@ -115,6 +116,92 @@ mem_bfd_iovec_stat (struct bfd *abfd, void *stream, struct stat *sb)
   return 0;
 }
 
+/* One reader that has been loaded successfully, and can potentially be used to
+   parse debug info.  */
+
+static struct jit_reader
+{
+  struct gdb_reader_funcs *functions;
+  void *handle;
+} *loaded_jit_reader = NULL;
+
+typedef struct gdb_reader_funcs * (reader_init_fn_type) (void);
+static const char *reader_init_fn_sym = "gdb_init_reader";
+
+/* Try to load FILE_NAME as a JIT debug info reader.  */
+
+static struct jit_reader *
+jit_reader_load (const char *file_name)
+{
+  void *so;
+  reader_init_fn_type *init_fn;
+  struct jit_reader *new_reader = NULL;
+  struct gdb_reader_funcs *funcs = NULL;
+  struct cleanup *old_cleanups;
+
+  if (jit_debug)
+    fprintf_unfiltered (gdb_stdlog, _("Opening shared object %s.\n"),
+                        file_name);
+  so = gdb_dlopen (file_name);
+  old_cleanups = make_cleanup_dlclose (so);
+
+  init_fn = gdb_dlsym (so, reader_init_fn_sym);
+  if (!init_fn)
+    error (_("Could not locate initialization function: %s."),
+          reader_init_fn_sym);
+
+  if (gdb_dlsym (so, "plugin_is_GPL_compatible") == NULL)
+    error (_("Reader not GPL compatible."));
+
+  funcs = init_fn ();
+  if (funcs->reader_version != GDB_READER_INTERFACE_VERSION)
+    error (_("Reader version does not match GDB version."));
+
+  new_reader = XZALLOC (struct jit_reader);
+  new_reader->functions = funcs;
+  new_reader->handle = so;
+
+  discard_cleanups (old_cleanups);
+  return new_reader;
+}
+
+/* Provides the jit-reader-load command.  */
+
+static void
+jit_reader_load_command (char *args, int from_tty)
+{
+  char *so_name;
+  int len;
+  struct cleanup *prev_cleanup;
+
+  if (args == NULL)
+    error (_("No reader name provided."));
+
+  if (loaded_jit_reader != NULL)
+    error (_("JIT reader already loaded.  Run jit-reader-unload first."));
+
+  so_name = xstrprintf ("%s/%s", jit_reader_dir, args);
+  prev_cleanup = make_cleanup (xfree, so_name);
+
+  loaded_jit_reader = jit_reader_load (so_name);
+  do_cleanups (prev_cleanup);
+}
+
+/* Provides the jit-reader-unload command.  */
+
+static void
+jit_reader_unload_command (char *args, int from_tty)
+{
+  if (!loaded_jit_reader)
+    error (_("No JIT reader loaded."));
+
+  loaded_jit_reader->functions->destroy (loaded_jit_reader->functions);
+
+  gdb_dlclose (loaded_jit_reader->handle);
+  xfree (loaded_jit_reader);
+  loaded_jit_reader = NULL;
+}
+
 /* Open a BFD from the target's memory.  */
 
 static struct bfd *
@@ -576,4 +663,17 @@ _initialize_jit (void)
   jit_objfile_data = register_objfile_data ();
   jit_inferior_data =
     register_inferior_data_with_cleanup (jit_inferior_data_cleanup);
+  if (is_dl_available ())
+    {
+      add_com ("jit-reader-load", no_class, jit_reader_load_command, _("\
+Load FILE as debug info reader and unwinder for JIT compiled code.\n\
+Usage: jit-reader-load FILE\n\
+Try to load file FILE as a debug info reader (and unwinder) for\n\
+JIT compiled code.  The file is loaded from " JIT_READER_DIR ",\n\
+relocated relative to the GDB executable if required."));
+      add_com ("jit-reader-unload", no_class, jit_reader_unload_command, _("\
+Unload the currently loaded JIT debug info reader.\n\
+Usage: jit-reader-unload FILE\n\n\
+Do \"help jit-reader-load\" for info on loading debug info readers."));
+    }
 }
-- 
1.7.7

>From d53e49d4ed80d9abb35752a92a628cb422b0b8a3 Mon Sep 17 00:00:00 2001
From: Sanjoy Das <sanjoy@playingwithpointers.com>
Date: Tue, 25 Oct 2011 18:50:53 +0530
Subject: [PATCH 5/7] Use the loaded reader.

Invoke the loaded JIT debug info reader to parse the registered symbol
files.

gdb/ChangeLog:
	* gdb/jit.c: Include block.h, dictionary.h and frame-unwind.h.
	(add_objfile_entry, jit_target_read_impl, jit_object_open_impl)
	(jit_symtab_open_impl, compare_block, jit_block_open_impl)
	(jit_symtab_line_mapping_add_impl, jit_symtab_close_impl)
	(finalize_symtab, jit_object_close_impl)
	(jit_reader_try_read_symtab, jit_bfd_try_read_symtab)
	(free_objfile_data): New functions.
	(_initialize_jit): Register jit_objfile_data with a proper cleanup
	function.
---
 gdb/jit.c |  481 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 468 insertions(+), 13 deletions(-)

diff --git a/gdb/jit.c b/gdb/jit.c
index 7cd70d0..6010a36 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -21,8 +21,11 @@
 
 #include "jit.h"
 #include "jit-reader.h"
+#include "block.h"
 #include "breakpoint.h"
 #include "command.h"
+#include "dictionary.h"
+#include "frame-unwind.h"
 #include "gdbcmd.h"
 #include "gdbcore.h"
 #include "inferior.h"
@@ -33,6 +36,7 @@
 #include "target.h"
 #include "gdb-dlfcn.h"
 #include "gdb_stat.h"
+#include "exceptions.h"
 
 static const char *jit_reader_dir = NULL;
 
@@ -228,6 +232,18 @@ struct jit_inferior_data
   CORE_ADDR descriptor_addr;  /* &__jit_debug_descriptor  */
 };
 
+/* Remember a mapping from entry_addr to objfile.  */
+
+static void
+add_objfile_entry (struct objfile *objfile, CORE_ADDR entry)
+{
+  CORE_ADDR *entry_addr_ptr;
+
+  entry_addr_ptr = xmalloc (sizeof (CORE_ADDR));
+  *entry_addr_ptr = entry;
+  set_objfile_data (objfile, jit_objfile_data, entry_addr_ptr);
+}
+
 /* Return jit_inferior_data for current inferior.  Allocate if not already
    present.  */
 
@@ -330,14 +346,423 @@ jit_read_code_entry (struct gdbarch *gdbarch,
       extract_unsigned_integer (&entry_buf[off], 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.  */
+/* Proxy object for building a block.  */
+
+struct gdb_block
+{
+  /* gdb_blocks are linked into a tree structure.  Next points to the
+     next node at the same depth as this block and parent to the
+     parent gdb_block.  */
+  struct gdb_block *next, *parent;
+
+  /* Points to the "real" block that is being built out of this
+     instance.  This block will be added to a blockvector, which will
+     then be added to a symtab.  */
+  struct block *real_block;
+
+  /* The first and last code address corresponding to this block.  */
+  CORE_ADDR begin, end;
+
+  /* The name of this block (if any).  If this is non-NULL, the
+     FUNCTION symbol symbol is set to this value.  */
+  const char *name;
+};
+
+/* Proxy object for building a symtab.  */
+
+struct gdb_symtab
+{
+  /* The list of blocks in this symtab.  These will eventually be
+     converted to real blocks.  */
+  struct gdb_block *blocks;
+
+  /* The number of blocks inserted.  */
+  int nblocks;
+
+  /* A mapping between line numbers to PC.  */
+  struct linetable *linetable;
+
+  /* The source file for this symtab.  */
+  const char *file_name;
+  struct gdb_symtab *next;
+};
+
+/* Proxy object for building an object.  */
+
+struct gdb_object
+{
+  struct gdb_symtab *symtabs;
+};
+
+/* The type of the `private' data passed around by the callback
+   functions.  */
+
+typedef CORE_ADDR jit_dbg_reader_data;
+
+/* The reader calls into this function to read data off the targets
+   address space.  */
+
+static enum gdb_status
+jit_target_read_impl (GDB_CORE_ADDR target_mem, void *gdb_buf, int len)
+{
+  int result = target_read_memory ((CORE_ADDR) target_mem, gdb_buf, len);
+  if (result == 0)
+    return GDB_SUCCESS;
+  else
+    return GDB_FAIL;
+}
+
+/* The reader calls into this function to create a new gdb_object
+   which it can then pass around to the other callbacks.  Right now,
+   all that is required is allocating the memory.  */
+
+static struct gdb_object *
+jit_object_open_impl (struct gdb_symbol_callbacks *cb)
+{
+  /* CB is not required right now, but sometime in the future we might
+     need a handle to it, and we'd like to do that without breaking
+     the ABI.  */
+  return XZALLOC (struct gdb_object);
+}
+
+/* Readers call into this function to open a new gdb_symtab, which,
+   again, is passed around to other callbacks.  */
+
+static struct gdb_symtab *
+jit_symtab_open_impl (struct gdb_symbol_callbacks *cb,
+                      struct gdb_object *object,
+                      const char *file_name)
+{
+  struct gdb_symtab *ret;
+
+  /* CB stays unused.  See comment in jit_object_open_impl.  */
+
+  ret = XZALLOC (struct gdb_symtab);
+  ret->file_name = file_name ? xstrdup (file_name) : xstrdup ("");
+  ret->next = object->symtabs;
+  object->symtabs = ret;
+  return ret;
+}
+
+/* Returns true if the block corresponding to old should be placed
+   before the block corresponding to new in the final blockvector.  */
+
+static int
+compare_block (const struct gdb_block *const old,
+               const struct gdb_block *const new)
+{
+  if (old == NULL)
+    return 1;
+  if (old->begin < new->begin)
+    return 1;
+  else if (old->begin == new->begin)
+    {
+      if (old->end > new->end)
+        return 1;
+      else
+        return 0;
+    }
+  else
+    return 0;
+}
+
+/* Called by readers to open a new gdb_block.  This function also
+   inserts the new gdb_block in the correct place in the corresponding
+   gdb_symtab.  */
+
+static struct gdb_block *
+jit_block_open_impl (struct gdb_symbol_callbacks *cb,
+                     struct gdb_symtab *symtab, struct gdb_block *parent,
+                     GDB_CORE_ADDR begin, GDB_CORE_ADDR end, const char *name)
+{
+  struct gdb_block *block = XZALLOC (struct gdb_block);
+
+  block->next = symtab->blocks;
+  block->begin = (CORE_ADDR) begin;
+  block->end = (CORE_ADDR) end;
+  block->name = name ? xstrdup (name) : NULL;
+  block->parent = parent;
+
+  /* Ensure that the blocks are inserted in the correct (reverse of
+     the order expected by blockvector).  */
+  if (compare_block (symtab->blocks, block))
+    {
+      symtab->blocks = block;
+    }
+  else
+    {
+      struct gdb_block *i = symtab->blocks;
+
+      for (;; i = i->next)
+        {
+          /* Guaranteed to terminate, since compare_block (NULL, _)
+             returns 1.  */
+          if (compare_block (i->next, block))
+            {
+              block->next = i->next;
+              i->next = block;
+              break;
+            }
+        }
+    }
+  symtab->nblocks++;
+
+  return block;
+}
+
+/* Readers call this to add a line mapping (from PC to line number) to
+   a gdb_symtab.  */
 
 static void
-jit_register_code (struct gdbarch *gdbarch,
-		   CORE_ADDR entry_addr, struct jit_code_entry *code_entry)
+jit_symtab_line_mapping_add_impl (struct gdb_symbol_callbacks *cb,
+                                  struct gdb_symtab *stab, int nlines,
+                                  struct gdb_line_mapping *map)
+{
+  int i;
+
+  if (nlines < 1)
+    return;
+
+  stab->linetable = xmalloc (sizeof (struct linetable)
+                             + (nlines - 1) * sizeof (struct linetable_entry));
+  stab->linetable->nitems = nlines;
+  for (i = 0; i < nlines; i++)
+    {
+      stab->linetable->item[i].pc = (CORE_ADDR) map[i].pc;
+      stab->linetable->item[i].line = map[i].line;
+    }
+}
+
+/* Called by readers to close a gdb_symtab.  Does not need to do
+   anything as of now.  */
+
+static void
+jit_symtab_close_impl (struct gdb_symbol_callbacks *cb,
+                       struct gdb_symtab *stab)
+{
+  /* Right now nothing needs to be done here.  We may need to do some
+     cleanup here in the future (again, without breaking the plugin
+     ABI).  */
+}
+
+/* Transform STAB to a proper symtab, and add it it OBJFILE.  */
+
+static void
+finalize_symtab (struct gdb_symtab *stab, struct objfile *objfile)
+{
+  struct symtab *symtab;
+  struct gdb_block *gdb_block_iter, *gdb_block_iter_tmp;
+  struct block *block_iter;
+  int actual_nblocks, i, blockvector_size;
+  CORE_ADDR begin, end;
+
+  actual_nblocks = FIRST_LOCAL_BLOCK + stab->nblocks;
+
+  symtab = allocate_symtab (stab->file_name, objfile);
+  /* JIT compilers compile in memory.  */
+  symtab->dirname = NULL;
+
+  /* Copy over the linetable entry if one was provided.  */
+  if (stab->linetable)
+    {
+      int size = ((stab->linetable->nitems - 1)
+                  * sizeof (struct linetable_entry)
+                  + sizeof (struct linetable));
+      LINETABLE (symtab) = obstack_alloc (&objfile->objfile_obstack, size);
+      memcpy (LINETABLE (symtab), stab->linetable, size);
+    }
+  else
+    {
+      LINETABLE (symtab) = NULL;
+    }
+
+  blockvector_size = (sizeof (struct blockvector)
+                      + (actual_nblocks - 1) * sizeof (struct block *));
+  symtab->blockvector = obstack_alloc (&objfile->objfile_obstack,
+                                       blockvector_size);
+
+  /* (begin, end) will contain the PC range this entire blockvector
+     spans.  */
+  symtab->primary = 1;
+  BLOCKVECTOR_MAP (symtab->blockvector) = NULL;
+  begin = stab->blocks->begin;
+  end = stab->blocks->end;
+  BLOCKVECTOR_NBLOCKS (symtab->blockvector) = actual_nblocks;
+
+  /* First run over all the gdb_block objects, creating a real block
+     object for each.  Simultaneously, keep setting the real_block
+     fields.  */
+  for (i = (actual_nblocks - 1), gdb_block_iter = stab->blocks;
+       i >= FIRST_LOCAL_BLOCK;
+       i--, gdb_block_iter = gdb_block_iter->next)
+    {
+      struct block *new_block = allocate_block (&objfile->objfile_obstack);
+      struct symbol *block_name = obstack_alloc (&objfile->objfile_obstack,
+                                                 sizeof (struct symbol));
+
+      BLOCK_DICT (new_block) = dict_create_linear (&objfile->objfile_obstack,
+                                                   NULL);
+      /* The address range.  */
+      BLOCK_START (new_block) = (CORE_ADDR) gdb_block_iter->begin;
+      BLOCK_END (new_block) = (CORE_ADDR) gdb_block_iter->end;
+
+      /* The name.  */
+      memset (block_name, 0, sizeof (struct symbol));
+      SYMBOL_DOMAIN (block_name) = VAR_DOMAIN;
+      SYMBOL_CLASS (block_name) = LOC_BLOCK;
+      SYMBOL_SYMTAB (block_name) = symtab;
+      SYMBOL_BLOCK_VALUE (block_name) = new_block;
+
+      block_name->ginfo.name = obsavestring (gdb_block_iter->name,
+                                             strlen (gdb_block_iter->name),
+                                             &objfile->objfile_obstack);
+
+      BLOCK_FUNCTION (new_block) = block_name;
+
+      BLOCKVECTOR_BLOCK (symtab->blockvector, i) = new_block;
+      if (begin > BLOCK_START (new_block))
+        begin = BLOCK_START (new_block);
+      if (end < BLOCK_END (new_block))
+        end = BLOCK_END (new_block);
+
+      gdb_block_iter->real_block = new_block;
+    }
+
+  /* Now add the special blocks.  */
+  block_iter = NULL;
+  for (i = 0; i < FIRST_LOCAL_BLOCK; i++)
+    {
+      struct block *new_block = allocate_block (&objfile->objfile_obstack);
+      BLOCK_DICT (new_block) = dict_create_linear (&objfile->objfile_obstack,
+                                                   NULL);
+      BLOCK_SUPERBLOCK (new_block) = block_iter;
+      block_iter = new_block;
+
+      BLOCK_START (new_block) = (CORE_ADDR) begin;
+      BLOCK_END (new_block) = (CORE_ADDR) end;
+
+      BLOCKVECTOR_BLOCK (symtab->blockvector, i) = new_block;
+    }
+
+  /* Fill up the superblock fields for the real blocks, using the
+     real_block fields populated earlier.  */
+  for (gdb_block_iter = stab->blocks;
+       gdb_block_iter;
+       gdb_block_iter = gdb_block_iter->next)
+    {
+      if (gdb_block_iter->parent != NULL)
+        BLOCK_SUPERBLOCK (gdb_block_iter->real_block) =
+          gdb_block_iter->parent->real_block;
+    }
+
+  /* Free memory.  */
+  gdb_block_iter = stab->blocks;
+
+  for (gdb_block_iter = stab->blocks, gdb_block_iter_tmp = gdb_block_iter->next;
+       gdb_block_iter;
+       gdb_block_iter = gdb_block_iter_tmp)
+    {
+      xfree ((void *) gdb_block_iter->name);
+      xfree (gdb_block_iter);
+    }
+  xfree (stab->linetable);
+  xfree ((char *) stab->file_name);
+  xfree (stab);
+}
+
+/* Called when closing a gdb_objfile.  Converts OBJ to a proper
+   objfile.  */
+
+static void
+jit_object_close_impl (struct gdb_symbol_callbacks *cb,
+                       struct gdb_object *obj)
+{
+  struct gdb_symtab *i, *j;
+  struct objfile *objfile;
+  jit_dbg_reader_data *priv_data;
+
+  priv_data = cb->priv_data;
+
+  objfile = allocate_objfile (NULL, 0);
+  objfile->gdbarch = target_gdbarch;
+
+  objfile->msymbols = obstack_alloc (&objfile->objfile_obstack,
+                                     sizeof (struct minimal_symbol));
+  memset (objfile->msymbols, 0, sizeof (struct minimal_symbol));
+
+  xfree (objfile->name);
+  objfile->name = xstrdup ("<< JIT compiled code >>");
+
+  j = NULL;
+  for (i = obj->symtabs; i; i = j)
+    {
+      j = i->next;
+      finalize_symtab (i, objfile);
+    }
+  add_objfile_entry (objfile, *priv_data);
+  xfree (obj);
+}
+
+/* Try to read CODE_ENTRY using the loaded jit reader (if any).  */
+
+static int
+jit_reader_try_read_symtab (struct jit_code_entry *code_entry)
+{
+  void *gdb_mem;
+  int status;
+  struct jit_dbg_reader *i;
+  jit_dbg_reader_data priv_data;
+  struct gdb_reader_funcs *funcs;
+  volatile struct gdb_exception e;
+  struct gdb_symbol_callbacks callbacks =
+    {
+      jit_object_open_impl,
+      jit_symtab_open_impl,
+      jit_block_open_impl,
+      jit_symtab_close_impl,
+      jit_object_close_impl,
+
+      jit_symtab_line_mapping_add_impl,
+      jit_target_read_impl,
+
+      &priv_data
+    };
+
+  priv_data = code_entry->symfile_addr;
+
+  if (!loaded_jit_reader)
+    return 0;
+
+  gdb_mem = xmalloc (code_entry->symfile_size);
+
+  status = 1;
+  TRY_CATCH (e, RETURN_MASK_ALL)
+    if (target_read_memory (code_entry->symfile_addr, gdb_mem,
+                            code_entry->symfile_size))
+      status = 0;
+  if (e.reason < 0)
+    status = 0;
+
+  if (status)
+    {
+      funcs = loaded_jit_reader->functions;
+      if (funcs->read (funcs, &callbacks, gdb_mem, code_entry->symfile_size)
+          != GDB_SUCCESS)
+        status = 0;
+    }
+
+  xfree (gdb_mem);
+  if (jit_debug && status == 0)
+    fprintf_unfiltered (gdb_stdlog,
+                        "Could not read symtab using the loaded JIT reader.\n");
+  return status;
+}
+
+/* Try to read CODE_ENTRY using BFD.  */
+
+static void
+jit_bfd_try_read_symtab (struct jit_code_entry *code_entry,
+                         struct gdbarch *gdbarch)
 {
   bfd *nbfd;
   struct section_addr_info *sai;
@@ -346,7 +771,6 @@ jit_register_code (struct gdbarch *gdbarch,
   struct cleanup *old_cleanups;
   int i;
   const struct bfd_arch_info *b;
-  CORE_ADDR *entry_addr_ptr;
 
   if (jit_debug)
     fprintf_unfiltered (gdb_stdlog,
@@ -400,12 +824,34 @@ JITed symbol file is not an object file, ignoring it.\n"));
   /* This call takes ownership of NBFD.  It does not take ownership of SAI.  */
   objfile = symbol_file_add_from_bfd (nbfd, 0, sai, OBJF_SHARED, NULL);
 
-  /* 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);
-
   do_cleanups (old_cleanups);
+  add_objfile_entry (objfile, code_entry->symfile_addr);
+}
+
+/* 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 (struct gdbarch *gdbarch,
+                   CORE_ADDR entry_addr, struct jit_code_entry *code_entry)
+{
+  int i, success;
+  const struct bfd_arch_info *b;
+  struct jit_inferior_data *inf_data = get_jit_inferior_data ();
+
+  if (jit_debug)
+    fprintf_unfiltered (gdb_stdlog,
+                        "jit_register_code, symfile_addr = %s, "
+                        "symfile_size = %s\n",
+                        paddress (gdbarch, code_entry->symfile_addr),
+                        pulongest (code_entry->symfile_size));
+
+  success = jit_reader_try_read_symtab (code_entry);
+
+  if (!success)
+    jit_bfd_try_read_symtab (code_entry, gdbarch);
 }
 
 /* This function unregisters JITed code and frees the corresponding
@@ -640,6 +1086,14 @@ jit_event_handler (struct gdbarch *gdbarch)
     }
 }
 
+/* Called to free the data allocated to the jit_inferior_data slot.  */
+
+static void
+free_objfile_data (struct objfile *objfile, void *data)
+{
+  xfree (data);
+}
+
 /* Provide a prototype to silence -Wmissing-prototypes.  */
 
 extern void _initialize_jit (void);
@@ -660,7 +1114,8 @@ _initialize_jit (void)
   observer_attach_inferior_created (jit_inferior_created_observer);
   observer_attach_inferior_exit (jit_inferior_exit_hook);
   observer_attach_executable_changed (jit_executable_changed_observer);
-  jit_objfile_data = register_objfile_data ();
+  jit_objfile_data =
+    register_objfile_data_with_cleanup (NULL, free_objfile_data);
   jit_inferior_data =
     register_inferior_data_with_cleanup (jit_inferior_data_cleanup);
   if (is_dl_available ())
-- 
1.7.7

>From 0fc08965906571f94372b55e58e49da572d8e21e Mon Sep 17 00:00:00 2001
From: Sanjoy Das <sanjoy@playingwithpointers.com>
Date: Thu, 3 Nov 2011 12:22:32 +0530
Subject: [PATCH 6/7] New JIT unwinder.

Introduce a "proxy unwinder", whcih will pass down all calls to the
functions the JIT reader provides.

gdb/ChangeLog:
	* gdb/jit.c: Include regcache.h.
	(jit_gdbarch_data, jit_frame_unwind): New static variables.
	(jit_unwind_reg_set_impl, free_reg_value_impl)
	(jit_unwind_reg_get_impl, jit_frame_sniffer)
	(jit_frame_unwind_stop_reason, jit_frame_this_id)
	(jit_frame_prev_register, jit_dealloc_cache)
	(jit_prepend_unwinder, jit_gdbarch_data_init): New functions.
	(jit_inferior_init): Prepend (new) pseudo unwinder by calling
	jit_prepend_unwinder.
	(_initialize_jit): Register new gdbarch data jit_gdbarch_data.

Conflicts:

	gdb/jit.c
---
 gdb/jit.c |  259 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 259 insertions(+), 0 deletions(-)

diff --git a/gdb/jit.c b/gdb/jit.c
index 6010a36..7bd6b36 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -31,6 +31,7 @@
 #include "inferior.h"
 #include "observer.h"
 #include "objfiles.h"
+#include "regcache.h"
 #include "symfile.h"
 #include "symtab.h"
 #include "target.h"
@@ -50,6 +51,12 @@ static const struct inferior_data *jit_inferior_data = NULL;
 
 static void jit_inferior_init (struct gdbarch *gdbarch);
 
+/* An unwinder is registered for every gdbarch.  This key is used to
+   remember if the unwinder has been registered for a particular
+   gdbarch.  */
+
+static struct gdbarch_data *jit_gdbarch_data;
+
 /* Non-zero if we want to see trace of jit level stuff.  */
 
 static int jit_debug = 0;
@@ -920,6 +927,242 @@ jit_breakpoint_re_set_internal (struct gdbarch *gdbarch,
   return 0;
 }
 
+/* The private data passed around in the frame unwind callback
+   functions.  */
+
+struct jit_unwind_private
+{
+  /* Cached register values.  See jit_frame_sniffer to see how this
+     works.  */
+  struct gdb_reg_value **registers;
+
+  /* The frame being unwound.  */
+  struct frame_info *this_frame;
+};
+
+/* Sets the value of a particular register in this frame.  */
+
+static void
+jit_unwind_reg_set_impl (struct gdb_unwind_callbacks *cb, int dwarf_regnum,
+                         struct gdb_reg_value *value)
+{
+  struct jit_unwind_private *priv;
+  int gdb_reg;
+
+  priv = cb->priv_data;
+
+  gdb_reg = gdbarch_dwarf2_reg_to_regnum (get_frame_arch (priv->this_frame),
+                                          dwarf_regnum);
+  if (gdb_reg == -1)
+    {
+      if (jit_debug)
+        fprintf_unfiltered (gdb_stdlog,
+                            _("Could not recognize DWARF regnum %d"),
+                            dwarf_regnum);
+      return;
+    }
+
+  gdb_assert (priv->registers);
+  priv->registers[gdb_reg] = value;
+}
+
+static void
+reg_value_free_impl (struct gdb_reg_value *value)
+{
+  xfree (value);
+}
+
+/* Get the value of register REGNUM in the previous frame.  */
+
+static struct gdb_reg_value *
+jit_unwind_reg_get_impl (struct gdb_unwind_callbacks *cb, int regnum)
+{
+  struct jit_unwind_private *priv;
+  struct gdb_reg_value *value;
+  int gdb_reg, size;
+  struct gdbarch *frame_arch;
+
+  priv = cb->priv_data;
+  frame_arch = get_frame_arch (priv->this_frame);
+
+  gdb_reg = gdbarch_dwarf2_reg_to_regnum (frame_arch, regnum);
+  size = register_size (frame_arch, gdb_reg);
+  value = xmalloc (sizeof (struct gdb_reg_value) + size - 1);
+  value->defined = frame_register_read (priv->this_frame, gdb_reg,
+                                        value->value);
+  value->size = size;
+  value->free = reg_value_free_impl;
+  return value;
+}
+
+/* gdb_reg_value has a free function, which must be called on each
+   saved register value.  */
+
+static void
+jit_dealloc_cache (struct frame_info *this_frame, void *cache)
+{
+  struct jit_unwind_private *priv_data = cache;
+  struct gdbarch *frame_arch;
+  int i;
+
+  gdb_assert (priv_data->registers);
+  frame_arch = get_frame_arch (priv_data->this_frame);
+
+  for (i = 0; i < gdbarch_num_regs (frame_arch); i++)
+    if (priv_data->registers[i] && priv_data->registers[i]->free)
+      priv_data->registers[i]->free (priv_data->registers[i]);
+
+  xfree (priv_data->registers);
+  xfree (priv_data);
+}
+
+/* The frame sniffer for the pseudo unwinder.
+
+   While this is nominally a frame sniffer, in the case where the JIT
+   reader actually recognizes the frame, it does a lot more work -- it
+   unwinds the frame and saves the corresponding register values in
+   the cache.  jit_frame_prev_register simply returns the saved
+   register values.  */
+
+static int
+jit_frame_sniffer (const struct frame_unwind *self,
+                   struct frame_info *this_frame, void **cache)
+{
+  struct jit_inferior_data *inf_data;
+  struct jit_unwind_private *priv_data;
+  struct jit_dbg_reader *iter;
+  struct gdb_unwind_callbacks callbacks;
+  struct gdb_reader_funcs *funcs;
+
+  inf_data = get_jit_inferior_data ();
+
+  callbacks.reg_get = jit_unwind_reg_get_impl;
+  callbacks.reg_set = jit_unwind_reg_set_impl;
+  callbacks.target_read = jit_target_read_impl;
+
+  if (loaded_jit_reader == NULL)
+    return 0;
+
+  funcs = loaded_jit_reader->functions;
+
+  gdb_assert (!*cache);
+
+  *cache = XZALLOC (struct jit_unwind_private);
+  priv_data = *cache;
+  priv_data->registers =
+    XCALLOC (gdbarch_num_regs (get_frame_arch (this_frame)),
+             struct gdb_reg_value *);
+  priv_data->this_frame = this_frame;
+
+  callbacks.priv_data = priv_data;
+
+  /* Try to coax the provided unwinder to unwind the stack */
+  if (funcs->unwind (funcs, &callbacks) == GDB_SUCCESS)
+    {
+      if (jit_debug)
+        fprintf_unfiltered (gdb_stdlog, _("Successfully unwound frame using "
+                                          "JIT reader.\n"));
+      return 1;
+    }
+  if (jit_debug)
+    fprintf_unfiltered (gdb_stdlog, _("Could not unwind frame using "
+                                      "JIT reader.\n"));
+
+  jit_dealloc_cache (this_frame, *cache);
+  *cache = NULL;
+
+  return 0;
+}
+
+
+/* The frame_id function for the pseudo unwinder.  Relays the call to
+   the loaded plugin.  */
+
+static void
+jit_frame_this_id (struct frame_info *this_frame, void **cache,
+                   struct frame_id *this_id)
+{
+  struct jit_unwind_private private;
+  struct gdb_frame_id frame_id;
+  struct gdb_reader_funcs *funcs;
+  struct gdb_unwind_callbacks callbacks;
+
+  private.registers = NULL;
+  private.this_frame = this_frame;
+
+  /* We don't expect the frame_id function to set any registers, so we
+     set reg_set to NULL.  */
+  callbacks.reg_get = jit_unwind_reg_get_impl;
+  callbacks.reg_set = NULL;
+  callbacks.target_read = jit_target_read_impl;
+  callbacks.priv_data = &private;
+
+  gdb_assert (loaded_jit_reader);
+  funcs = loaded_jit_reader->functions;
+
+  frame_id = funcs->get_frame_id (funcs, &callbacks);
+  *this_id = frame_id_build (frame_id.stack_address, frame_id.code_address);
+}
+
+/* Pseudo unwinder function.  Reads the previously fetched value for
+   the register from the cache.  */
+
+static struct value *
+jit_frame_prev_register (struct frame_info *this_frame, void **cache, int reg)
+{
+  struct jit_unwind_private *priv = *cache;
+  struct gdb_reg_value *value;
+
+  if (priv == NULL)
+    return frame_unwind_got_optimized (this_frame, reg);
+
+  gdb_assert (priv->registers);
+  value = priv->registers[reg];
+  if (value && value->defined)
+    return frame_unwind_got_bytes (this_frame, reg, value->value);
+  else
+    return frame_unwind_got_optimized (this_frame, reg);
+}
+
+/* Relay everything back to the unwinder registered by the JIT debug
+   info reader.*/
+
+static const struct frame_unwind jit_frame_unwind =
+{
+  NORMAL_FRAME,
+  default_frame_unwind_stop_reason,
+  jit_frame_this_id,
+  jit_frame_prev_register,
+  NULL,
+  jit_frame_sniffer,
+  jit_dealloc_cache
+};
+
+
+/* This is the information that is stored at jit_gdbarch_data for each
+   architecture.  */
+
+struct jit_gdbarch_data_type
+{
+  /* Has the (pseudo) unwinder been prepended? */
+  int unwinder_registered;
+};
+
+/* Check GDBARCH and prepend the pseudo JIT unwinder if needed.  */
+
+static void
+jit_prepend_unwinder (struct gdbarch *gdbarch)
+{
+  struct jit_gdbarch_data_type *data;
+
+  data = gdbarch_data (gdbarch, jit_gdbarch_data);
+  if (!data->unwinder_registered)
+    {
+      frame_unwind_prepend_unwinder (gdbarch, &jit_frame_unwind);
+      data->unwinder_registered = 1;
+    }
+}
+
 /* Register any already created translations.  */
 
 static void
@@ -933,6 +1176,8 @@ jit_inferior_init (struct gdbarch *gdbarch)
   if (jit_debug)
     fprintf_unfiltered (gdb_stdlog, "jit_inferior_init\n");
 
+  jit_prepend_unwinder (gdbarch);
+
   inf_data = get_jit_inferior_data ();
   if (jit_breakpoint_re_set_internal (gdbarch, inf_data) != 0)
     return;
@@ -1094,6 +1339,19 @@ free_objfile_data (struct objfile *objfile, void *data)
   xfree (data);
 }
 
+/* Initialize the jit_gdbarch_data slot with an instance of struct
+   jit_gdbarch_data_type */
+
+static void *
+jit_gdbarch_data_init (struct obstack *obstack)
+{
+  struct jit_gdbarch_data_type *data;
+
+  data = obstack_alloc (obstack, sizeof (struct jit_gdbarch_data_type));
+  data->unwinder_registered = 0;
+  return data;
+}
+
 /* Provide a prototype to silence -Wmissing-prototypes.  */
 
 extern void _initialize_jit (void);
@@ -1118,6 +1376,7 @@ _initialize_jit (void)
     register_objfile_data_with_cleanup (NULL, free_objfile_data);
   jit_inferior_data =
     register_inferior_data_with_cleanup (jit_inferior_data_cleanup);
+  jit_gdbarch_data = gdbarch_data_register_pre_init (jit_gdbarch_data_init);
   if (is_dl_available ())
     {
       add_com ("jit-reader-load", no_class, jit_reader_load_command, _("\
-- 
1.7.7

>From 1b6d99afa919713457ab0e3b3f72086da68f0bda Mon Sep 17 00:00:00 2001
From: Sanjoy Das <sanjoy@playingwithpointers.com>
Date: Tue, 25 Oct 2011 18:50:53 +0530
Subject: [PATCH 7/7] Documentation.

Add some basic documentation to gdb.texinfo about the new JIT reader
functionality. Most of the actual documentation is still in
jit-reader.h.

gdb/doc/ChangeLog:
	* gdb.texinfo (JIT Interface): Add documentation on writing and
	usind JIT debug info readers.
	(Custom Debug Info, Using JIT Debug Info Readers, Writing JIT
	Debug Info Readers): New nodes.
---
 gdb/doc/gdb.texinfo |  117 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 117 insertions(+), 0 deletions(-)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 25942b3..a53d8be 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -31709,6 +31709,7 @@ out about additional code.
 * Declarations::                Relevant C struct declarations
 * Registering Code::            Steps to register code
 * Unregistering Code::          Steps to unregister code
+* Custom Debug Info::           Emit debug information in a custom format
 @end menu
 
 @node Declarations
@@ -31805,6 +31806,122 @@ Set @code{action_flag} to @code{JIT_UNREGISTER} and call
 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 Custom Debug Info
+@section Custom Debug Info
+@cindex custom JIT debug info
+@cindex JIT debug info reader
+
+Generating debug information in platform-native file formats (like ELF
+or COFF) may be an overkill for JIT compilers; especially if all the
+debug info is used for is displaying a meaningful backtrace.  The
+issue can be resolved by having the JIT writers decide on a debug info
+format and also provide a reader that parses the debug info generated
+by the JIT compiler.  This section gives a brief overview on writing
+such a parser.  More specific details can be found in the source file
+@file{gdb/jit-reader.in}, which is also installed as a header at
+@file{@var{includedir}/gdb/jit-reader.h} for easy inclusion.
+
+The reader is implemented as a shared object (so this functionality is
+not available on platforms which don't allow loading shared objects at
+runtime).  Two new @value{GDBN} commands, @code{jit-reader-load} and
+@code{jit-reader-unload} are provided, to be used to load and unload
+the readers from a preconfigured directory.  Once loaded, the shared
+object is used the parse the debug information emitted by the JIT
+compiler.
+
+@menu
+* Using JIT Debug Info Readers::       How to use supplied readers correctly
+* Writing JIT Debug Info Readers::     Creating a debug-info reader
+@end menu
+
+@node Using JIT Debug Info Readers
+@subsection Using JIT Debug Info Readers
+@kindex jit-reader-load
+@kindex jit-reader-unload
+
+Readers can be loaded and unloaded using the @code{jit-reader-load}
+and @code{jit-reader-unload} commands.
+
+@table @code
+@item jit-reader-load @var{reader-name}
+
+Load the JIT reader named @var{reader-name}.  On a UNIX system, this
+will usually result load @file{@var{libdir}/gdb/@var{reader-name}},
+where @var{libdir} is the system library directory, usually
+@file{/usr/local/lib}.  Only one reader can be active at a time;
+trying to load a second reader when one is already loaded will result
+in @value{GDBN} reporting an error.  A new JIT reader can be loaded by
+first unloading the current one using @code{jit-reader-load} and
+then invoking @code{jit-reader-load}.
+
+@item jit-reader-unload
+
+Unload the currently loaded JIT reader.
+
+@end table
+
+@node Writing JIT Debug Info Readers
+@subsection Writing JIT Debug Info Readers
+
+As mentioned, a reader is essentially a shared object conforming to a
+certain ABI.  This ABI is described in @file{jit-reader.h}.
+
+@file{jit-reader.h} defines the structures, macros and functions
+required to write a reader.  It is installed (along with
+@value{GDBN}), in @file{@var{includedir}/gdb} where @var{includedir} is
+the system include directory.
+
+Readers need to be released under a GPL compatible license.  A reader
+can be declared as released under such a license by placing the macro
+@code{GDB_DECLARE_GPL_COMPATIBLE_READER} in a source file.
+
+The entry point for readers is the symbol @code{gdb_init_reader},
+which is expected to be a function with the prototype
+
+@findex gdb_init_reader
+@smallexample
+extern struct gdb_reader_funcs *gdb_init_reader (void);
+@end smallexample
+
+@cindex @code{struct gdb_reader_funcs}
+
+@code{struct gdb_reader_funcs} contains a set of pointers to callback
+functions.  These functions are executed to read the debug info
+generated by the JIT compiler (@code{read}), to unwind stack frames
+(@code{unwind}) and to create canonical frame IDs
+(@code{get_Frame_id}).  It also has a callback that is called when the
+reader is being unloaded (@code{destroy}).  The struct looks like this
+
+@smallexample
+struct gdb_reader_funcs
+@{
+  /* Must be set to GDB_READER_INTERFACE_VERSION.  */
+  int reader_version;
+
+  /* For use by the reader.  */
+  void *priv_data;
+
+  gdb_read_debug_info *read;
+  gdb_unwind_frame *unwind;
+  gdb_get_frame_id *get_frame_id;
+  gdb_destroy_reader *destroy;
+@};
+@end smallexample
+
+@cindex @code{struct gdb_symbol_callbacks}
+@cindex @code{struct gdb_unwind_callbacks}
+
+The callbacks are provided with another set of callbacks by
+@value{GDBN} to do their job.  For @code{read}, these callbacks are
+passed in a @code{struct gdb_symbol_callbacks} and for @code{unwind}
+and @code{get_frame_id}, in a @code{struct gdb_unwind_callbacks}.
+@code{struct gdb_symbol_callbacks} has callbacks to create new object
+files and new symbol tables inside those object files.  @code{struct
+gdb_unwind_callbacks} has callbacks to read registers off the current
+frame and to write out the values of the registers in the previous
+frame.  Both have a callback (@code{target_read}) to read bytes off the
+target's address space.
+
 @node GDB Bugs
 @chapter Reporting Bugs in @value{GDBN}
 @cindex bugs in @value{GDBN}
-- 
1.7.7


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]