* [PATCH 2/2] Add symbol-handling callbacks to the jit-reader interface.
2011-07-13 20:23 [PATCH 1/2] jit-reader.h: describe interface implemented by the JIT readers Sanjoy Das
@ 2011-07-13 20:23 ` Sanjoy Das
2011-07-14 17:54 ` Tom Tromey
2011-07-14 17:43 ` [PATCH 1/2] jit-reader.h: describe interface implemented by the JIT readers Tom Tromey
1 sibling, 1 reply; 6+ messages in thread
From: Sanjoy Das @ 2011-07-13 20:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Sanjoy Das
Implements the callbacks in `struct gdbjit_symtab_callbacks'. This
will allow the JIT debug info reader to be able to parse the debug
info and actually push the symbols into GDB.
gdb/ChangeLog:
* jit.c: Add symbol handling callbacks for the JIT reader
interface.
---
gdb/ChangeLog | 4 +
gdb/jit.c | 518 +++++++++++++++++++++++++++++++++++++++++++++++++++------
2 files changed, 471 insertions(+), 51 deletions(-)
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index e067f04..ac207d1 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,9 @@
2011-07-13 Sanjoy Das <sanjoy@playingwithpointers.com>
+ * jit.c: Add symbol handling callbacks for the JIT reader interface.
+
+2011-07-13 Sanjoy Das <sanjoy@playingwithpointers.com>
+
* jit-reader.h: Add (new) JIT reader interface.
* jit.c: Include jit-reader.h in source file
* Makefile.in: Add jit-reader.h to HFILES_NO_SRCDIR.
diff --git a/gdb/jit.c b/gdb/jit.c
index 6c07a57..2899469 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -24,8 +24,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"
@@ -34,8 +36,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";
@@ -50,6 +55,115 @@ static void jit_inferior_init (struct gdbarch *gdbarch);
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)
@@ -140,6 +254,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
@@ -238,6 +355,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;
+
+ /* 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;
+};
+
+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 (CORE_ADDR target_mem, void *gdb_buf, int len)
+{
+ return target_read_memory (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,
+ CORE_ADDR begin, CORE_ADDR 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 = 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 = stab->blocks->begin;
+ end = 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) = blck_iter->begin;
+ BLOCK_END (bl) = 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) = begin;
+ BLOCK_END (bl) = 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;
+ }
+
+ status = inf_data->reader->read (inf_data->reader->private_data, &callbacks,
+ gdb_mem, sz) == GDB_JIT_SUCCESS;
+
+ 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
@@ -247,14 +640,9 @@ static void
jit_register_code (struct gdbarch *gdbarch,
CORE_ADDR entry_addr, struct jit_code_entry *code_entry)
{
- bfd *nbfd;
- struct section_addr_info *sai;
- struct bfd_section *sec;
struct objfile *objfile;
- struct cleanup *old_cleanups, *my_cleanups;
- int i;
- const struct bfd_arch_info *b;
- CORE_ADDR *entry_addr_ptr;
+ int i, success;
+ struct jit_inferior_data *inf_data = get_jit_inferior_data ();
if (jit_debug)
fprintf_unfiltered (gdb_stdlog,
@@ -263,53 +651,68 @@ 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;
+ bfd *nbfd;
+ struct section_addr_info *sai;
+ struct bfd_section *sec;
+ const struct bfd_arch_info *b;
+ CORE_ADDR *entry_addr_ptr;
+
+ nbfd = bfd_open_from_target_memory (code_entry->symfile_addr,
+ code_entry->symfile_size, gnutarget);
+ old_cleanups = make_cleanup_bfd_close (nbfd);
+
+ /* Check the format. NOTE: This initializes important data that GDB uses!
+ We would segfault later without this line. */
+ if (!bfd_check_format (nbfd, bfd_object))
+ {
+ printf_unfiltered (_("\
JITed symbol file is not an object file, ignoring it.\n"));
- do_cleanups (old_cleanups);
- return;
+ 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
@@ -395,6 +798,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;
@@ -474,6 +879,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);
}
@@ -494,6 +904,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.4
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/2] jit-reader.h: describe interface implemented by the JIT readers.
@ 2011-07-13 20:23 Sanjoy Das
2011-07-13 20:23 ` [PATCH 2/2] Add symbol-handling callbacks to the jit-reader interface Sanjoy Das
2011-07-14 17:43 ` [PATCH 1/2] jit-reader.h: describe interface implemented by the JIT readers Tom Tromey
0 siblings, 2 replies; 6+ messages in thread
From: Sanjoy Das @ 2011-07-13 20:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Sanjoy Das
Generating ELF files in memory is usually too much work for JIT
compilers, since they usually don't have the related routines (as
opposed to a regular static compiler). One way to simplify this is to
have the JIT vendor themselves provide a shared object which is loaded
and used to parse the debug information. This patch exposes a
simplified interface for the debug info parser.
The interface is _simplified_ in the sense that it does not expose all
of GDB's functionality. The idea is that if the user wishes to use GDB
for full-on debugging of generated code, it will probably be simpler
to generate the full DWARF info.
The interface does not expose more than the minimum amount of GDB
internals; both to prevent versioning problems, and to keep writing
the debug-info reader simple. For the same reason, GDB specific
typedefs (like CORE_ADDR) have been kept out of `jit-reader.h'.
This patch adds a header file (`jit-reader.h') which can be included
and implemented in a shared object to build a working JIT
debug-reader.
gdb/ChangeLog:
* jit-reader.h: Add (new) JIT reader interface.
* jit.c: Include jit-reader.h in source file
* Makefile.in: Add jit-reader.h to HFILES_NO_SRCDIR.
---
gdb/ChangeLog | 6 ++
gdb/Makefile.in | 2 +-
gdb/jit-reader.h | 189 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/jit.c | 4 +
4 files changed, 200 insertions(+), 1 deletions(-)
create mode 100644 gdb/jit-reader.h
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 8ed7eaf..e067f04 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,9 @@
+2011-07-13 Sanjoy Das <sanjoy@playingwithpointers.com>
+
+ * jit-reader.h: Add (new) JIT reader interface.
+ * jit.c: Include jit-reader.h in source file
+ * Makefile.in: Add jit-reader.h to HFILES_NO_SRCDIR.
+
2011-07-12 Jan Kratochvil <jan.kratochvil@redhat.com>
Code cleanup making also optimized out values lazy.
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 494f24f..ca2cbcb 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -816,7 +816,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..15980bd
--- /dev/null
+++ b/gdb/jit-reader.h
@@ -0,0 +1,189 @@
+/* Interface for JIT debug-info readers.
+
+ 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
+
+/* 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;
+
+#ifndef __GDB__INTERNAL /* Only defined when being compiled in GDB. */
+typedef long long CORE_ADDR;
+#endif
+
+struct gdbjit_line_mapping
+{
+ int line;
+ CORE_ADDR 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, CORE_ADDR begin_addr, CORE_ADDR 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) (CORE_ADDR 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
+{
+ CORE_ADDR code_address;
+ CORE_ADDR 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,
+ CORE_ADDR 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 eb1bcc7..6c07a57 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -19,7 +19,11 @@
#include "defs.h"
+/* To stop jit-reader.h from re-defining CORE_ADDR */
+#define __GDB__INTERNAL
+
#include "jit.h"
+#include "jit-reader.h"
#include "breakpoint.h"
#include "command.h"
#include "gdbcmd.h"
--
1.7.5.4
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] jit-reader.h: describe interface implemented by the JIT readers.
2011-07-13 20:23 [PATCH 1/2] jit-reader.h: describe interface implemented by the JIT readers Sanjoy Das
2011-07-13 20:23 ` [PATCH 2/2] Add symbol-handling callbacks to the jit-reader interface Sanjoy Das
@ 2011-07-14 17:43 ` Tom Tromey
1 sibling, 0 replies; 6+ messages in thread
From: Tom Tromey @ 2011-07-14 17:43 UTC (permalink / raw)
To: Sanjoy Das; +Cc: gdb-patches
>>>>> "Sanjoy" == Sanjoy Das <sanjoy@playingwithpointers.com> writes:
Sanjoy> Generating ELF files in memory is usually too much work for JIT
Sanjoy> compilers, since they usually don't have the related routines (as
Sanjoy> opposed to a regular static compiler). One way to simplify this is to
Sanjoy> have the JIT vendor themselves provide a shared object which is loaded
Sanjoy> and used to parse the debug information. This patch exposes a
Sanjoy> simplified interface for the debug info parser.
The idea makes sense to me. I'd like this to go in.
Sanjoy> This patch adds a header file (`jit-reader.h') which can be included
Sanjoy> and implemented in a shared object to build a working JIT
Sanjoy> debug-reader.
This header should be installed somewhere, I think $includedir/gdb/.
The whole header should have conditional 'extern "C"' wrapping, just in case.
Sanjoy> +#define GDB_JIT_SUCCESS 1
Sanjoy> +#define GDB_JIT_FAIL 0
Not an enum?
Sanjoy> +#ifndef __GDB__INTERNAL /* Only defined when being compiled in GDB. */
Sanjoy> +typedef long long CORE_ADDR;
Sanjoy> +#endif
I think long long isn't portable. One approach would be to take the
configury stuff from gdb and put it into a new header with the
appropriate namespace prefixes. The auto-configured bits could be done
at build time by extracting stuff from config.h.
We should be namespace-safe always, so it should be GDB_CORE_ADDR or
GDB_JIT_CORE_ADDR.
Sanjoy> +typedef void (gdbjit_symtab_add_line_mapping)
Sanjoy> +(struct gdbjit_symtab_callbacks * callbacks, struct gdbjit_symtab *symtab,
Indentation.
Sanjoy> +typedef struct gdbjit_reg_value (gdbjit_unwind_reg_get)
Sanjoy> + (struct gdbjit_unwind_callbacks *callback, int regnum);
I would have expected this to take a frame as well.
Why doesn't it?
Maybe I don't understand the overall plan here.
Sanjoy> +/* Called once for each new inferior program. It should also initialize the
Sanjoy> + private pointer (to which a pointer is passed here) if the it needs one.
s/the//
Sanjoy> + Should return GDB_JIT_SUCESS on success and GDB_JIT_FAIL on error. */
Typo, "GDB_JIT_SUCCESS"
Sanjoy> +extern int gdbjit_read_debug_info (void *private,
Sanjoy> + struct gdbjit_symtab_callbacks *callbacks,
Sanjoy> + CORE_ADDR memory, long memory_sz);
I don't understand the point of this one.
This change needs a patch for the manual.
I already forgot... did we get you started on the copyright assignment
process?
Tom
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 2/2] Add symbol-handling callbacks to the jit-reader interface.
2011-07-13 20:23 ` [PATCH 2/2] Add symbol-handling callbacks to the jit-reader interface Sanjoy Das
@ 2011-07-14 17:54 ` Tom Tromey
2011-07-22 16:42 ` Joel Brobecker
0 siblings, 1 reply; 6+ messages in thread
From: Tom Tromey @ 2011-07-14 17:54 UTC (permalink / raw)
To: Sanjoy Das; +Cc: gdb-patches
>>>>> "Sanjoy" == Sanjoy Das <sanjoy@playingwithpointers.com> writes:
Sanjoy> Implements the callbacks in `struct gdbjit_symtab_callbacks'. This
Sanjoy> will allow the JIT debug info reader to be able to parse the debug
Sanjoy> info and actually push the symbols into GDB.
Sanjoy> +#include <dlfcn.h>
I assume this isn't portable.
You could either lift the needed code from GCC's plugin interface, or I
suppose use libtool's libltdl.
Sanjoy> +/* The environment variable we poke at to get the file name of the shared object
Sanjoy> + to load. */
Sanjoy> +
Sanjoy> +static const char *jit_dbg_reader_so_name_env = "JIT_DEBUG_READER";
I don't think environment variables are a good way to go.
Either it should be automatic in a trusted-library scenario, or it
should require a command from the user.
Sanjoy> +static const char *jit_init_reader_sym = "gdbjit_init_reader";
Instead of a separate symbol for each function, how about just a single
function that we call that returns a struct full of function pointers?
Sanjoy> +struct jit_dbg_reader
Sanjoy> +{
Sanjoy> + jit_init_reader_fn *init;
Sanjoy> + jit_read_debug_info_fn *read;
Sanjoy> + jit_unwind_frame_fn *unwind;
Sanjoy> + jit_get_frame_id_fn *get_frame_id;
Sanjoy> + jit_destroy_reader_fn *destroy;
Sanjoy> +
Sanjoy> + void *handle, *private_data;
Sanjoy> +};
A struct and all its fields should have comments.
Sanjoy> + handle = dlopen (file_name, RTLD_NOW);
Sanjoy> + if (!handle)
Sanjoy> + goto cleanup;
I think there is a GNU requirement that plugins should export a symbol
like "plugin_is_GPL_compatible". See GCC.
Sanjoy> +static struct gdbjit_symtab *
Sanjoy> +jit_symtab_open_impl (struct gdbjit_symtab_callbacks *cb, const char *file_name)
Sanjoy> +{
Sanjoy> + struct gdbjit_symtab *ret = XZALLOC (struct gdbjit_symtab);
Does anything ever free this? I couldn't find it.
Sanjoy> +/* Returns true if the block corrensponding to old should be placed before the
Typo, "corresponding".
Sanjoy> + block corrensponding to new in the final blockvector. */
Likewise.
Sanjoy> +static struct gdbjit_block *
Sanjoy> +jit_new_block_impl (struct gdbjit_symtab_callbacks *cb,
Sanjoy> + struct gdbjit_symtab *symtab,
Sanjoy> + struct gdbjit_block *parent,
Sanjoy> + CORE_ADDR begin, CORE_ADDR end, const char *name)
Sanjoy> +{
Sanjoy> + struct gdbjit_block *block = XZALLOC (struct gdbjit_block);
Sanjoy> +
Sanjoy> + (void) cb;
Don't do that.
Sanjoy> + if (stab->linetable) {
Brace placement.
Sanjoy> + if (target_read_memory (target_mem, gdb_mem, sz))
This has a QUIT in it, so it can presumably throw an exception if the
user C-c's at exactly the wrong moment.
So I think this needs some kind of TRY_CATCH wrapper. There's another
case of this as well.
I found it quite hard to read this patch for some reason; maybe I'm just
having a bad day. Anyway I feel certain I have missed various important
things.
Tom
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 2/2] Add symbol-handling callbacks to the jit-reader interface.
2011-07-14 17:54 ` Tom Tromey
@ 2011-07-22 16:42 ` Joel Brobecker
2011-07-22 17:12 ` Tom Tromey
0 siblings, 1 reply; 6+ messages in thread
From: Joel Brobecker @ 2011-07-22 16:42 UTC (permalink / raw)
To: Tom Tromey; +Cc: Sanjoy Das, gdb-patches
> Sanjoy> +#include <dlfcn.h>
>
> I assume this isn't portable.
>
> You could either lift the needed code from GCC's plugin interface, or I
> suppose use libtool's libltdl.
There was a similar need for the VxWorks port, that I have yet
to rebase, retest, and commit :-(. In any case, I thought it would
be very easy to create gdb_dlfcn.h/c:
| #ifdef HAVE_LIBDL
| #include <dlfcn.h>
| #elif __MINGW32__
| #include <windows.h>
| #else
| /* Unsupported configuration. See Eg. gdb_dlopen for details. */
| #error API to load shared library missing (Eg. libdl)
| #endif
|
| /* Load the dynamic library file named FILENAME, and return a handle
| for that dynamic library. Return NULL if the loading fails for
| any reason. */
|
| static void *
| gdb_dlopen (const char *filename)
| {
| #ifdef HAVE_LIBDL
| return dlopen (filename, RTLD_NOW);
| #elif __MINGW32__
| return (void *) LoadLibrary (filename);
| #endif
| }
|
| /* 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. */
|
| static void *
| gdb_dlsym(void *handle, const char *symbol)
| {
| #ifdef HAVE_LIBDL
| return dlsym (handle, symbol);
| #elif __MINGW32__
| return (void *) GetProcAddress (handle, symbol);
| #endif
| }
--
Joel
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 2/2] Add symbol-handling callbacks to the jit-reader interface.
2011-07-22 16:42 ` Joel Brobecker
@ 2011-07-22 17:12 ` Tom Tromey
0 siblings, 0 replies; 6+ messages in thread
From: Tom Tromey @ 2011-07-22 17:12 UTC (permalink / raw)
To: Joel Brobecker; +Cc: Sanjoy Das, gdb-patches
Tom> You could either lift the needed code from GCC's plugin interface, or I
Tom> suppose use libtool's libltdl.
Joel> There was a similar need for the VxWorks port, that I have yet
Joel> to rebase, retest, and commit :-(. In any case, I thought it would
Joel> be very easy to create gdb_dlfcn.h/c:
This approach is fine by me.
If we run into problems in practice we can always import libltdl later
on.
Tom
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2011-07-22 16:53 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-07-13 20:23 [PATCH 1/2] jit-reader.h: describe interface implemented by the JIT readers Sanjoy Das
2011-07-13 20:23 ` [PATCH 2/2] Add symbol-handling callbacks to the jit-reader interface Sanjoy Das
2011-07-14 17:54 ` Tom Tromey
2011-07-22 16:42 ` Joel Brobecker
2011-07-22 17:12 ` Tom Tromey
2011-07-14 17:43 ` [PATCH 1/2] jit-reader.h: describe interface implemented by the JIT readers Tom Tromey
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).