#include "defs.h"
#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"
#include "observer.h"
#include "objfiles.h"
+#include "regcache.h"
#include "symfile.h"
#include "symtab.h"
#include "target.h"
+#include "gdb-dlfcn.h"
#include "gdb_stat.h"
+#include "exceptions.h"
+
+static const char *jit_reader_dir = NULL;
static const struct objfile_data *jit_objfile_data;
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;
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 *
CORE_ADDR descriptor_addr; /* &__jit_debug_descriptor */
};
+/* Remember OBJFILE has been created for struct jit_code_entry located
+ at inferior address ENTRY. */
+
+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. */
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;
+
+ terminate_minimal_symbol_table (objfile);
+
+ 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).
+ ENTRY_ADDR is the address of the struct jit_code_entry in the
+ inferior address space. */
+
+static int
+jit_reader_try_read_symtab (struct jit_code_entry *code_entry,
+ CORE_ADDR entry_addr)
+{
+ 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 = entry_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. ENTRY_ADDR is the address of the
+ struct jit_code_entry in the inferior address space. */
+
+static void
+jit_bfd_try_read_symtab (struct jit_code_entry *code_entry,
+ CORE_ADDR entry_addr,
+ struct gdbarch *gdbarch)
{
bfd *nbfd;
struct section_addr_info *sai;
struct bfd_section *sec;
struct objfile *objfile;
- struct cleanup *old_cleanups, *my_cleanups;
+ struct cleanup *old_cleanups;
int i;
const struct bfd_arch_info *b;
- CORE_ADDR *entry_addr_ptr;
if (jit_debug)
fprintf_unfiltered (gdb_stdlog,
nbfd = bfd_open_from_target_memory (code_entry->symfile_addr,
code_entry->symfile_size, gnutarget);
- old_cleanups = make_cleanup_bfd_close (nbfd);
+ if (nbfd == NULL)
+ {
+ puts_unfiltered (_("Error opening JITed symbol file, ignoring it.\n"));
+ return;
+ }
/* Check the format. NOTE: This initializes important data that GDB uses!
We would segfault later without this line. */
{
printf_unfiltered (_("\
JITed symbol file is not an object file, ignoring it.\n"));
- do_cleanups (old_cleanups);
+ bfd_close (nbfd);
return;
}
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);
+ old_cleanups = 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)
++i;
}
- /* This call takes ownership of sai. */
+ /* 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, entry_addr);
+}
- discard_cleanups (old_cleanups);
+/* 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, entry_addr);
+
+ if (!success)
+ jit_bfd_try_read_symtab (code_entry, entry_addr, gdbarch);
}
/* This function unregisters JITed code and frees the corresponding
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
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;
}
}
+/* Called to free the data allocated to the jit_inferior_data slot. */
+
+static void
+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);
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."),
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);
+ 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, _("\
+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."));
+ }
}