[RFC] JIT Debug info reader

Sanjoy Das
2011-06-30T15:48:16Z

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

Hi!

I've been working on easing JIT interaction with GDB. The current
situation of generating ELF files in memory is usually too much work for
existing JIT compilers; especially since they don't otherwise contain
the ELF / DWARF manipulation routines (as opposed to a more traditional
compiler).

One way to solve this (which is what the attached patches try to do) is
to have the JIT vendor themselves decide on a "format" for the debug
info. Then the JIT compiler can register the debug info objects by
calling `__jit_debug_register_code', and also provide a shared object
that can be loaded and used to parse the emitted debug info.

With the current patch, the JIT compiler writers can create an SO which
implements the interface defined in `jit-reader.h', and call GDB with
`JIT_DEBUG_READER' set to the SO to load as the debug info reader.

The current version only allows the JIT debug info reader to parse out
some minimal amount of information, which will then allow GDB to display
a meaningful stack trace. The idea is that in case the user wants to use
GDB for full fledged debugging of generated code, it is probably better
to bite the bullet and generate DWARF.

I've done some preliminary testing by writing a debug info emitter and
reader for a Javascript VM; and things seem to work. I have attached the
patches for general comments and review.

Thanks!

--
Sanjoy Das
http://playingwithpointers.com

[-- Attachment #2: 0001-Adds-a-jit-reader-interface.patch --]
[-- Type: text/x-diff, Size: 8131 bytes --]

From aea762b9ce0660ce8117490c9c3f91de4e8578a3 Mon Sep 17 00:00:00 2001
From: Sanjoy Das <sanjoy@playingwithpointers.com>
Date: Thu, 30 Jun 2011 15:30:06 +0530
Subject: [PATCH 1/3] Adds a jit-reader interface.

Adds a jit-reader.h, which JIT debug info readers can include and implement.
---
gdb/Makefile.in | 2 +-
gdb/jit-reader.h | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/jit.c | 1 +
3 files changed, 170 insertions(+), 1 deletions(-)
create mode 100644 gdb/jit-reader.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 9b6e913..7bdec38 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -817,7 +817,7 @@ osdata.h procfs.h python/py-event.h python/py-events.h python/py-stopevent.h \
python/python-internal.h python/python.h ravenscar-thread.h record.h \
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
+gnulib/stddef.in.h inline-frame.h jit-reader.h

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

diff --git a/gdb/jit-reader.h b/gdb/jit-reader.h
new file mode 100644
index 0000000..0380234
--- /dev/null
+++ b/gdb/jit-reader.h
@@ -0,0 +1,168 @@
+/* Defines the interface for JIT debug-info readers. This file needs to be
+ self-sufficient since it will be included in the JIT debug-info readers
+ directly.
+
+ This is a minimum-functionality version - the JIT reader can only read in
+ enough debug information to allow GDB to display a meaningful stack trace. */
+
+#ifndef GDB_JIT_READER_H
+#define GDB_JIT_READER_H
+
+/* Versioning: for the SO to be correctly recognized and loaded, put the macro
+ GDB_JIT_DECLARE_API_VERSION in a source file. */
+
+#define GDB_JIT_INTERFACE_VERSION 1
+#define GDB_JIT_DECLARE_API_VERSION \
+ extern int __gdbjit_so_api_version (void) \
+ { \
+ return GDB_JIT_INTERFACE_VERSION; \
+ }
+
+#define GDB_JIT_SUCCESS 1
+#define GDB_JIT_FAIL 0
+#define GDBJIT_MAX_REGISTER_SIZE 64 /* Mirrors the internal GDB definition. */
+
+struct gdbjit_symtab;
+struct gdbjit_block;
+struct gdbjit_symbol;
+struct gdbjit_symtab_callbacks;
+
+struct gdbjit_line_mapping {
+ int line;
+ void *pc;
+};
+
+typedef struct gdbjit_symtab *(gdbjit_symtab_open)
+ (struct gdbjit_symtab_callbacks *callbacks,
+ const char *file_name);
+
+typedef struct gdbjit_block *(gdbjit_new_block)
+ (struct gdbjit_symtab_callbacks *callbacks, struct gdbjit_symtab *symtab,
+ struct gdbjit_block *parent, void *begin_addr, void *end_addr,
+ const char *name);
+
+typedef void (gdbjit_symtab_close) (struct gdbjit_symtab_callbacks *callbacks,
+ struct gdbjit_symtab *symtab);
+
+typedef void (gdbjit_symtab_add_line_mapping)
+(struct gdbjit_symtab_callbacks * callbacks, struct gdbjit_symtab *symtab,
+ int nlines, struct gdbjit_line_mapping *lines);
+
+typedef int (gdbjit_target_read) (void *target_mem, void *gdb_buf, int len);
+
+struct gdbjit_symtab_callbacks {
+ /* Create a new symbol table. A symbol table consists of a forest of block
+ hierachies. Blocks are added by a call to new_block. This needs to be
+ paired with a call to symtab_close.
+
+ An optional FILE_NAME can be passed, which will be associated with the
+ symbol table as the source file from which this code was compiled.
+
+ Returns a new struct gdbjit_symtab which can be further passed to
+ new_block and symtab_close. */
+ gdbjit_symtab_open *symtab_open;
+
+ /* Creates a new block. A block is an optionally named contiguous block of
+ code in memory. Creating a new block automatically adds it to the symbol
+ table in which it is created.
+
+ SYMTAB is the symbol table this block is added to. Each block may have an
+ optionally non-NULL PARENT. BEGIN_ADDR and END_ADDR are the first and last
+ addresses spanned by this piece of compiled code. This block may have an
+ optional NAME, in case it corresponds to a function.
+
+ Returns a gdbjit_block instance, which, at this point, is useless. */
+ gdbjit_new_block *new_block;
+
+ /* Adds PC <-> line number mapping for the symbol table SYMTAB. Pass an array
+ of NLINES gdbjit_line_mapping s in LINES. */
+ gdbjit_symtab_add_line_mapping *add_line_mapping;
+
+ /* Reads LEN bytes of memory from the target's address space, beginning from
+ TARGET_MEM into the buffer GDB_BUF points to. */
+ gdbjit_target_read *target_read;
+
+ /* Closes the symbol table SYMTAB, and add it to the internal GDB list. */
+ gdbjit_symtab_close *symtab_close;
+
+ /* For internal use. */
+ void *private;
+};
+
+/* Denotes a register value. */
+struct gdbjit_reg_value {
+ /* Set to non-zero if this register has a valid value. Otherwise 0. */
+ int defined;
+ /* The actual value of the register (this is considered valid only if defined
+ is non-zero). */
+ unsigned char value[GDBJIT_MAX_REGISTER_SIZE];
+};
+
+/* Unique frame identifier. This should remain constant throughout the lifetime
+ of the frame concerned. */
+struct gdbjit_frame_id {
+ void *code_address;
+ void *stack_address;
+};
+
+struct gdbjit_unwind_callbacks;
+
+typedef struct gdbjit_reg_value (gdbjit_unwind_reg_get)
+ (struct gdbjit_unwind_callbacks *callback, int regnum);
+
+typedef void (gdbjit_unwind_reg_set) (struct gdbjit_unwind_callbacks *callback,
+ int regnum, struct gdbjit_reg_value val);
+
+struct gdbjit_unwind_callbacks {
+ /* Gets the current value for the register REGNUM (DWARF numbering). */
+ gdbjit_unwind_reg_get *reg_get;
+
+ /* Sets the previous value of the REGNUM register (i.e. its value in the
+ previous frame) to VAL. */
+ gdbjit_unwind_reg_set *reg_set;
+
+ /* Function to read memory off the inferior process being debugged, as
+ documented in gdbjit_symtab_callbacks. */
+ gdbjit_target_read *target_read;
+
+ /* For internal use. */
+ void *private;
+};
+
+/* Called once for each new inferior program. It should also initialize the
+ private pointer (to which a pointer is passed here) if the it needs one.
+
+ return: GDB_SUCCESS on success, or GDB_JIT_FAIL on error,
+ in which this plugin will be discarded (gdbjit_destroy_reader will not be
+ called in such a case). */
+extern int gdbjit_init_reader (void **private);
+
+/* Called to have the reader try to read a block of memory. PRIVATE is the
+ private pointer, (possibly) initialized by gdbjit_init_reader.
+
+ The function is expected to use CALLBACKS (documented above) to build a
+ symbol table for this particular block of memory (pointed to by MEMORY,
+ already copied from the target address space to GDB's address space).
+ MEMORY_SZ is the size of the block of memory.
+
+ Should return GDB_JIT_SUCESS on success and GDB_JIT_FAIL on error. */
+extern int gdbjit_read_debug_info (void *private,
+ struct gdbjit_symtab_callbacks *callbacks,
+ void *memory, long memory_sz);
+
+/* Called to unwind one frame. PRIVATE is the private pointer. Expected to use
+ CALLBACKS to unwind the registers to the older frame.
+
+ Should return GDB_JIT_SUCESS for success, GDB_JIT_FAIL for fail. */
+extern int gdbjit_unwind_frame (void *private,
+ struct gdbjit_unwind_callbacks *callbacks);
+
+/* Called to get a unique identifier for the current stack frame (as reported by
+ callbacks). */
+struct gdbjit_frame_id gdbjit_get_frame_id (void *private, struct
+ gdbjit_unwind_callbacks *callbacks);
+
+/* Called once the inferior process exits. */
+extern void gdbjit_destroy_reader (void *private);
+
+#endif
diff --git a/gdb/jit.c b/gdb/jit.c
index be123a5..4ce5814 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.5.3

[-- Attachment #3: 0002-Symbol-handling-code.patch --]
[-- Type: text/x-diff, Size: 19452 bytes --]

From d6dfb791614d34f1919f276b668b79c5eb278044 Mon Sep 17 00:00:00 2001
From: Sanjoy Das <sanjoy@playingwithpointers.com>
Date: Thu, 30 Jun 2011 16:57:46 +0530
Subject: [PATCH 2/3] Symbol handling code.

Implements
* Plugin loading code.
* Symbol table creation callbacks.
---
gdb/jit.c | 506 +++++++++++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 460 insertions(+), 46 deletions(-)

diff --git a/gdb/jit.c b/gdb/jit.c
index 4ce5814..d259e04 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -21,8 +21,10 @@

#include "jit.h"
#include "jit-reader.h"
+#include "block.h"
#include "breakpoint.h"
#include "command.h"
+#include "dictionary.h"
#include "gdbcmd.h"
#include "gdbcore.h"
#include "inferior.h"
@@ -31,8 +33,11 @@
#include "symfile.h"
#include "symtab.h"
#include "target.h"
+#include "gdb_dirent.h"
#include "gdb_stat.h"

+#include <dlfcn.h>
+
static const struct objfile_data *jit_objfile_data;

static const char *const jit_break_name = "__jit_debug_register_code";
@@ -45,6 +50,114 @@ static const struct inferior_data *jit_inferior_data = NULL;

static int jit_debug = 0;

+/* The environment variable we poke at to get the file name of the shared object
+ to load. */
+
+static const char *jit_dbg_reader_so_name_env = "JIT_DEBUG_READER";
+
+/* Symbols (and the corresponding types) to be loaded from the shared object
+ supposed to do the debug-info parsing. */
+typedef int (jit_init_reader_fn) (void **);
+static const char *jit_init_reader_sym = "gdbjit_init_reader";
+
+typedef int (jit_read_debug_info_fn) (void *, struct gdbjit_symtab_callbacks *,
+ void *mem, long sz);
+static const char *jit_read_debug_info_sym = "gdbjit_read_debug_info";
+
+typedef int (jit_unwind_frame_fn) (void *, struct gdbjit_unwind_callbacks *);
+static const char *jit_unwind_frame_sym = "gdbjit_unwind_frame";
+
+typedef void (jit_destroy_reader_fn) (void *);
+static const char *jit_destroy_reader_sym = "gdbjit_destroy_reader";
+
+typedef struct gdbjit_frame_id (jit_get_frame_id_fn) (void *, struct
+ gdbjit_unwind_callbacks *);
+static const char *jit_get_frame_id_sym = "gdbjit_get_frame_id";
+
+static const char *jit_so_api_version_sym = "__gdbjit_so_api_version";
+
+struct jit_dbg_reader {
+ jit_init_reader_fn *init;
+ jit_read_debug_info_fn *read;
+ jit_unwind_frame_fn *unwind;
+ jit_get_frame_id_fn *get_frame_id;
+ jit_destroy_reader_fn *destroy;
+
+ void *handle, *private_data;
+};
+
+static struct jit_dbg_reader *
+jit_dbg_reader_load (void)
+{
+ /* Check if a reader has been provided. */
+ const char *file_name = getenv (jit_dbg_reader_so_name_env);
+ void *handle = NULL;
+ int (*jit_reader_so_api_version) (void) = NULL;
+ struct jit_dbg_reader *reader = NULL;
+
+ if (file_name == NULL)
+ return NULL;
+
+ handle = dlopen (file_name, RTLD_NOW);
+ if (!handle)
+ goto cleanup;
+
+ reader = XZALLOC (struct jit_dbg_reader);
+ reader->init = dlsym (handle, jit_init_reader_sym);
+ reader->handle = handle;
+
+ if (!reader->init)
+ goto cleanup;
+
+ reader->read = dlsym (handle, jit_read_debug_info_sym);
+ if (!reader->read)
+ goto cleanup;
+
+ reader->unwind = dlsym (handle, jit_unwind_frame_sym);
+ if (!reader->unwind)
+ goto cleanup;
+
+ reader->get_frame_id = dlsym (handle, jit_get_frame_id_sym);
+ if (!reader->get_frame_id)
+ goto cleanup;
+
+ reader->destroy = dlsym (handle, jit_destroy_reader_sym);
+ if (!reader->destroy)
+ goto cleanup;
+
+ jit_reader_so_api_version = dlsym (handle, jit_so_api_version_sym);
+ if (!jit_reader_so_api_version ||
+ jit_reader_so_api_version () != GDB_JIT_INTERFACE_VERSION)
+ {
+ goto cleanup;
+ }
+
+ if (reader->init (&reader->private_data) != GDB_JIT_SUCCESS)
+ goto cleanup;
+
+ return reader;
+
+ cleanup:
+ xfree (reader);
+ if (handle)
+ {
+ dlclose (handle);
+ fprintf (stderr, "Could not load reader object %s\n", file_name);
+ }
+ else /* also report why the shared object could not be loaded */
+ fprintf (stderr, "Could not load reader object %s (%s)\n", file_name,
+ dlerror());
+ return NULL;
+}
+
+static void
+jit_dbg_reader_destroy (struct jit_dbg_reader *reader)
+{
+ if (reader)
+ reader->destroy (reader->private_data);
+ xfree (reader);
+}
+
static void
show_jit_debug (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
@@ -135,6 +248,9 @@ struct jit_inferior_data
{
CORE_ADDR breakpoint_addr; /* &__jit_debug_register_code() */
CORE_ADDR descriptor_addr; /* &__jit_debug_descriptor */
+ struct jit_dbg_reader *reader; /* If a debug info reader was found. */
+ struct objfile *jit_objfile; /* The object file where all the JIT symbols
+ will be added. */
};

/* Return jit_inferior_data for current inferior. Allocate if not already
@@ -233,6 +349,282 @@ jit_read_code_entry (struct gdbarch *gdbarch,
extract_unsigned_integer (&entry_buf[3 * ptr_size], 8, byte_order);
}

+struct gdbjit_block
+{
+ struct gdbjit_block *next, *parent;
+ struct block *real_block;
+
+ void *begin, *end;
+ const char *name;
+};
+
+struct gdbjit_symtab
+{
+ struct gdbjit_block *blocks;
+ struct linetable *linetable;
+ int nblocks;
+
+ const char *file_name;
+};
+
+struct jit_dbg_reader_data
+{
+ struct objfile **objf;
+};
+
+static int
+jit_target_read_impl (void *target_mem, void *gdb_buf, int len)
+{
+ return target_read_memory ((CORE_ADDR) target_mem, gdb_buf, len);
+}
+
+static struct gdbjit_symtab *
+jit_symtab_open_impl (struct gdbjit_symtab_callbacks *cb, const char *file_name)
+{
+ struct gdbjit_symtab *ret = XZALLOC (struct gdbjit_symtab);
+ ret->file_name = xstrdup (file_name);
+ return ret;
+}
+
+/* Returns true if the block corrensponding to old should be placed before the
+ block corrensponding to new in the final blockvector. */
+static int
+compare_block (struct gdbjit_block *old, struct gdbjit_block *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;
+}
+
+static struct gdbjit_block *
+jit_new_block_impl (struct gdbjit_symtab_callbacks *cb,
+ struct gdbjit_symtab *symtab,
+ struct gdbjit_block *parent,
+ void *begin, void *end, const char *name)
+{
+ struct gdbjit_block *block = XZALLOC (struct gdbjit_block);
+
+ (void) cb;
+
+ block->next = symtab->blocks;
+ block->begin = begin;
+ block->end = 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 gdbjit_block *i = symtab->blocks;
+ for (;; i = i->next)
+ {
+ /* Guranteed 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;
+}
+
+static void
+jit_symtab_add_line_mapping_impl (struct gdbjit_symtab_callbacks *cb,
+ struct gdbjit_symtab *stab,
+ int nlines,
+ struct gdbjit_line_mapping *map)
+{
+ int i;
+ /* Must copy */
+ if (!nlines)
+ 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;
+ }
+}
+
+static void
+jit_symtab_close_impl (struct gdbjit_symtab_callbacks *cb,
+ struct gdbjit_symtab *stab)
+{
+ struct jit_dbg_reader_data *dat = cb->private;
+ struct symtab *symtab;
+ struct gdbjit_block *blck_iter, *blck_iter_tmp;
+ struct block *block_iter;
+ struct objfile *objfile;
+ int actual_nblocks = FIRST_LOCAL_BLOCK + stab->nblocks, i;
+ CORE_ADDR begin, end;
+
+ if (*dat->objf == NULL)
+ {
+ objfile = allocate_objfile (NULL, 0);
+ *dat->objf = objfile;
+ objfile->gdbarch = target_gdbarch;
+ objfile->msymbols = obstack_alloc (&objfile->objfile_obstack,
+ sizeof (struct minimal_symbol));
+ objfile->msymbols[0].ginfo.name = NULL;
+ objfile->msymbols[0].ginfo.value.address = 0;
+ objfile->name = xstrdup ("JIT");
+ }
+ else
+ objfile = *dat->objf;
+
+ symtab = allocate_symtab (stab->file_name, objfile);
+ symtab->dirname = NULL; /* JIT compilers compile in memory */
+
+ 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;
+ }
+
+ symtab->blockvector = obstack_alloc (&objfile->objfile_obstack,
+ (sizeof (struct blockvector)
+ + (actual_nblocks - 1) *
+ sizeof (struct block *)));
+
+ /* (begin, end) will contain the PC range this entire blockvector spans. */
+ symtab->primary = 1;
+ BLOCKVECTOR_MAP (symtab->blockvector) = NULL;
+ begin = (CORE_ADDR) stab->blocks->begin;
+ end = (CORE_ADDR) stab->blocks->end;
+ BLOCKVECTOR_NBLOCKS (symtab->blockvector) = actual_nblocks;
+
+ for (i = (actual_nblocks - 1), blck_iter = stab->blocks;
+ i >= FIRST_LOCAL_BLOCK; i--)
+ {
+ struct block *bl = allocate_block (&objfile->objfile_obstack);
+ struct symbol *block_name = obstack_alloc (&objfile->objfile_obstack,
+ sizeof (struct symbol));
+
+ BLOCK_DICT (bl) = dict_create_linear (&objfile->objfile_obstack, NULL);
+ BLOCK_START (bl) = (CORE_ADDR) blck_iter->begin;
+ BLOCK_END (bl) = (CORE_ADDR) blck_iter->end;
+ BLOCK_FUNCTION (bl) = block_name;
+
+ memset (block_name, 0, sizeof (struct symbol));
+ SYMBOL_DOMAIN (block_name) = VAR_DOMAIN;
+ SYMBOL_CLASS (block_name) = LOC_BLOCK;
+ block_name->symtab = symtab;
+ SYMBOL_BLOCK_VALUE (block_name) = bl;
+
+ block_name->ginfo.name = obstack_alloc (&objfile->objfile_obstack,
+ 1 + strlen (blck_iter->name));
+ strcpy (block_name->ginfo.name, blck_iter->name);
+ BLOCK_FUNCTION (bl) = block_name;
+
+ BLOCKVECTOR_BLOCK (symtab->blockvector, i) = bl;
+ if (begin < (long) blck_iter->begin)
+ begin = (long) blck_iter->begin;
+ if (end > (long) blck_iter->end)
+ end = (long) blck_iter->end;
+ }
+
+ blck_iter = blck_iter->next;
+
+ /* Now add the special blocks. */
+ block_iter = NULL;
+ for (i = 0; i < FIRST_LOCAL_BLOCK; i++)
+ {
+ struct block *bl = allocate_block (&objfile->objfile_obstack);
+ BLOCK_DICT (bl) = dict_create_linear (&objfile->objfile_obstack, NULL);
+ BLOCK_SUPERBLOCK (bl) = block_iter;
+ block_iter = bl;
+
+ BLOCK_START (bl) = (CORE_ADDR) begin;
+ BLOCK_END (bl) = (CORE_ADDR) end;
+
+ BLOCKVECTOR_BLOCK (symtab->blockvector, i) = bl;
+ }
+
+ /* Fill up the superblock fields. */
+ for (blck_iter = stab->blocks; blck_iter; blck_iter = blck_iter->next)
+ {
+ if (blck_iter->parent != NULL)
+ BLOCK_SUPERBLOCK (blck_iter->real_block) =
+ blck_iter->parent->real_block;
+ }
+
+ /* Free memory. */
+ blck_iter = stab->blocks;
+ for (blck_iter = stab->blocks, blck_iter_tmp = blck_iter->next;
+ blck_iter; blck_iter = blck_iter_tmp)
+ {
+ xfree (blck_iter);
+ }
+}
+
+static int
+jit_try_read_symtab (struct jit_inferior_data *inf_data,
+ CORE_ADDR target_mem, long sz)
+{
+ void *gdb_mem;
+ int status = 0;
+ struct jit_dbg_reader *i;
+ struct jit_dbg_reader_data priv_data;
+ struct gdbjit_symtab_callbacks callbacks =
+ {
+ jit_symtab_open_impl,
+ jit_new_block_impl,
+ jit_symtab_add_line_mapping_impl,
+ jit_target_read_impl,
+ jit_symtab_close_impl,
+
+ &priv_data
+ };
+
+ if (!inf_data->reader)
+ return 0;
+
+ priv_data.objf = &inf_data->jit_objfile;
+
+ gdb_mem = xmalloc (sz);
+ if (target_read_memory (target_mem, gdb_mem, sz))
+ {
+ status = 0;
+ goto cleanup;
+ }
+
+ if (inf_data->reader->read (inf_data->reader->private_data, &callbacks,
+ gdb_mem, sz) == GDB_JIT_SUCCESS)
+ {
+ status = 1;
+ goto cleanup;
+ }
+
+ cleanup:
+ xfree (gdb_mem);
+ return status;
+}
+
/* 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
@@ -246,10 +638,10 @@ jit_register_code (struct gdbarch *gdbarch,
struct section_addr_info *sai;
struct bfd_section *sec;
struct objfile *objfile;
- struct cleanup *old_cleanups, *my_cleanups;
- int i;
+ int i, success;
const struct bfd_arch_info *b;
CORE_ADDR *entry_addr_ptr;
+ struct jit_inferior_data *inf_data = get_jit_inferior_data ();

if (jit_debug)
fprintf_unfiltered (gdb_stdlog,
@@ -258,53 +650,62 @@ jit_register_code (struct gdbarch *gdbarch,
paddress (gdbarch, code_entry->symfile_addr),
pulongest (code_entry->symfile_size));

- 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))
+ if (jit_try_read_symtab (inf_data, code_entry->symfile_addr,
+ code_entry->symfile_size))
{
- printf_unfiltered (_("\
+ objfile = inf_data->jit_objfile;
+ }
+ else
+ {
+ struct cleanup *old_cleanups;
+ 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;
+ do_cleanups (old_cleanups);
+ return;
+ }
+
+ /* Check bfd arch. */
+ b = gdbarch_bfd_arch_info (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 = xstrdup (bfd_get_section_name (nbfd, sec));
+ sai->other[i].sectindex = sec->index;
+ ++i;
+ }
+
+ /* This call takes ownership of sai. */
+ objfile = symbol_file_add_from_bfd (nbfd, 0, sai, OBJF_SHARED, 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);
+
+ discard_cleanups (old_cleanups);
}
-
- /* Check bfd arch. */
- b = gdbarch_bfd_arch_info (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 = xstrdup (bfd_get_section_name (nbfd, sec));
- sai->other[i].sectindex = sec->index;
- ++i;
- }
-
- /* This call takes ownership of sai. */
- objfile = symbol_file_add_from_bfd (nbfd, 0, sai, OBJF_SHARED, 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);
-
- discard_cleanups (old_cleanups);
}

/* This function unregisters JITed code and frees the corresponding
@@ -385,6 +786,8 @@ jit_inferior_init (struct gdbarch *gdbarch)
if (jit_breakpoint_re_set_internal (gdbarch, inf_data) != 0)
return;

+ inf_data->reader = jit_dbg_reader_load ();
+
if (inf_data->descriptor_addr == 0)
{
struct minimal_symbol *desc_symbol;
@@ -464,6 +867,11 @@ jit_reset_inferior_data_and_breakpoints (void)
/* Remove any existing JIT breakpoint(s). */
remove_jit_event_breakpoints ();

+ /* Destroy JIT reader objects. */
+ jit_dbg_reader_destroy (inf_data->reader);
+ if (inf_data->jit_objfile)
+ free_objfile (inf_data->jit_objfile);
+
jit_inferior_init (target_gdbarch);
}

@@ -484,6 +892,12 @@ jit_inferior_exit_hook (struct inferior *inf)
{
struct objfile *objf;
struct objfile *temp;
+ struct jit_inferior_data *inf_data;
+
+ inf_data = inferior_data (inf, jit_inferior_data);
+ jit_dbg_reader_destroy (inf_data->reader);
+ if (inf_data->jit_objfile)
+ free_objfile (inf_data->jit_objfile);

ALL_OBJFILES_SAFE (objf, temp)
if (objfile_data (objf, jit_objfile_data) != NULL)
--
1.7.5.3

[-- Attachment #4: 0003-JIT-unwinder.patch --]
[-- Type: text/x-diff, Size: 6387 bytes --]

From b2eefe94d050278420b92ec02ec0a3a233b71a6a Mon Sep 17 00:00:00 2001
From: Sanjoy Das <sanjoy@playingwithpointers.com>
Date: Thu, 30 Jun 2011 17:32:35 +0530
Subject: [PATCH 3/3] JIT unwinder.

Adds a new unwinder which relays all the unwinding action to the JIT reader loaded in memory.
---
gdb/i386-tdep.c | 4 ++
gdb/jit.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/jit.h | 2 +
3 files changed, 159 insertions(+), 0 deletions(-)

diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 366d0fa..66626ab 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -30,6 +30,7 @@
#include "frame-base.h"
#include "frame-unwind.h"
#include "inferior.h"
+#include "jit.h"
#include "gdbcmd.h"
#include "gdbcore.h"
#include "gdbtypes.h"
@@ -7330,6 +7331,9 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
CFI info will be used if it is available. */
dwarf2_append_unwinders (gdbarch);

+ /* JIT reader pseudo-unwinder. */
+ jit_prepend_unwinder (gdbarch);
+
frame_base_set_default (gdbarch, &i386_frame_base);

/* Pseudo registers may be changed by amd64_init_abi. */
diff --git a/gdb/jit.c b/gdb/jit.c
index d259e04..1a00a91 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -25,6 +25,7 @@
#include "breakpoint.h"
#include "command.h"
#include "dictionary.h"
+#include "frame-unwind.h"
#include "gdbcmd.h"
#include "gdbcore.h"
#include "inferior.h"
@@ -769,6 +770,152 @@ jit_breakpoint_re_set_internal (struct gdbarch *gdbarch,
return 0;
}

+struct jit_unwind_private {
+ struct gdbjit_reg_value *registers;
+ struct frame_info *this_frame;
+};
+
+static void
+jit_unwind_reg_set_impl (struct gdbjit_unwind_callbacks *cb, int regnum,
+ struct gdbjit_reg_value value)
+{
+ struct jit_unwind_private *priv = cb->private;
+ int gdb_reg = gdbarch_dwarf2_reg_to_regnum (target_gdbarch, regnum);
+ priv->registers[gdb_reg] = value;
+}
+
+static struct gdbjit_reg_value
+jit_unwind_reg_get_impl (struct gdbjit_unwind_callbacks *cb, int regnum)
+{
+ struct jit_unwind_private *priv = cb->private;
+ struct gdbjit_reg_value val;
+ int gdb_reg = gdbarch_dwarf2_reg_to_regnum (target_gdbarch, regnum);
+ val.defined = frame_register_read (priv->this_frame, gdb_reg, val.value);
+ return val;
+}
+
+static int
+jit_frame_sniffer (const struct frame_unwind *self,
+ struct frame_info *this_frame, void **cache)
+{
+ /* First look up the PC, and check if the code address has been registered or
+ not. */
+ CORE_ADDR pc = get_frame_pc (this_frame);
+ struct jit_inferior_data *inf_data = get_jit_inferior_data ();
+ struct jit_unwind_private *priv_data;
+ struct jit_dbg_reader *iter;
+ struct gdbjit_unwind_callbacks callbacks =
+ {
+ jit_unwind_reg_get_impl,
+ jit_unwind_reg_set_impl,
+ jit_target_read_impl
+ };
+
+ if (inf_data->reader == NULL)
+ return 0;
+
+ /* All the unwinding happens here, and the unwound registers are written to
+ this block of memory, which we then sneakily read back in
+ jit_frame_prev_register. */
+ if (!*cache)
+ {
+ *cache = XZALLOC (struct jit_unwind_private);
+ priv_data = *cache;
+ priv_data->registers = XCALLOC (gdbarch_num_regs (target_gdbarch),
+ struct gdbjit_reg_value);
+ priv_data->this_frame = this_frame;
+ }
+ else
+ {
+ priv_data = *cache;
+ priv_data->this_frame = this_frame;
+ }
+
+ callbacks.private = priv_data;
+
+ /* Try to coax the provided unwinder to unwind the stack, and hope it
+ succeeds. */
+ if (inf_data->reader->unwind (inf_data->reader->private_data, &callbacks)
+ == GDB_JIT_SUCCESS)
+ return 1;
+ xfree (priv_data->registers);
+ xfree (priv_data);
+ *cache = NULL;
+
+ return 0;
+}
+
+static enum unwind_stop_reason
+jit_frame_unwind_stop_reason (struct frame_info *this_frame, void **cache)
+{
+ /* TODO: This needs to be fixed. */
+ return UNWIND_NO_REASON;
+}
+
+static void
+jit_frame_this_id (struct frame_info *this_frame, void **cache,
+ struct frame_id *this_id)
+{
+ struct jit_inferior_data *inf_data = get_jit_inferior_data ();
+ struct jit_unwind_private private =
+ {
+ NULL,
+ this_frame
+ };
+ struct gdbjit_frame_id frame_id;
+ struct gdbjit_unwind_callbacks callbacks =
+ {
+ jit_unwind_reg_get_impl,
+ NULL,
+ jit_target_read_impl,
+
+ &private
+ };
+
+ frame_id = inf_data->reader->get_frame_id (inf_data->reader->private_data,
+ &callbacks);
+ this_id->stack_addr = (CORE_ADDR) frame_id.stack_address;
+ this_id->code_addr = (CORE_ADDR) frame_id.stack_address;
+ this_id->stack_addr_p = this_id->code_addr_p = 1;
+}
+
+static struct value *
+jit_frame_prev_register (struct frame_info *this_frame, void **cache, int reg)
+{
+ struct jit_unwind_private *priv = *cache;
+ struct gdbjit_reg_value value;
+
+ if (priv == NULL)
+ return frame_unwind_got_optimized (this_frame, reg);
+
+ value = priv->registers[reg];
+ if (value.defined)
+ return frame_unwind_got_bytes (this_frame, reg, value.value);
+ else
+ return frame_unwind_got_optimized (this_frame, reg);
+}
+
+static void
+jit_dealloc_cache (struct frame_info *this_frame, void *cache)
+{
+ (void) this_frame;
+
+ xfree (cache);
+}
+
+/* Simply relays everything back to the unwinder registered by the jit debug
+ info reader.*/
+static const struct frame_unwind jit_frame_unwind =
+{
+ NORMAL_FRAME,
+ jit_frame_unwind_stop_reason,
+ jit_frame_this_id,
+ jit_frame_prev_register,
+ NULL,
+ jit_frame_sniffer,
+ jit_dealloc_cache
+};
+
/* Register any already created translations. */

static void
@@ -948,6 +1095,12 @@ jit_event_handler (struct gdbarch *gdbarch)
}
}

+void
+jit_prepend_unwinder (struct gdbarch *gdbarch)
+{
+ frame_unwind_prepend_unwinder (gdbarch, &jit_frame_unwind);
+}
+
/* Provide a prototype to silence -Wmissing-prototypes. */

extern void _initialize_jit (void);
diff --git a/gdb/jit.h b/gdb/jit.h
index 73a1414..9dd8f5a 100644
--- a/gdb/jit.h
+++ b/gdb/jit.h
@@ -80,4 +80,6 @@ extern void jit_breakpoint_re_set (void);

extern void jit_event_handler (struct gdbarch *gdbarch);

+extern void jit_prepend_unwinder (struct gdbarch *gdbarch);
+
#endif /* JIT_H */
--
1.7.5.3