2009-07-24 Reid Kleckner Add interface for JIT code generation. * NEWS: Announce JIT interface. * doc/gdb.texinfo: Add chapter on JIT interface. * Makefile.in (SFILES): Add jit.c. (HFILES_NO_SRCDIR): Add jit.h. (COMMON_OBS): Add jit.o. * jit.c: New file. * jit.h: New file. * breakpoint.h: (enum bptype): Add bp_jit_event to enum. * breakpoint.c: (update_breakpoints_after_exec): Delete jit breakpoints after exec. (bpstat_what): Update event table for bp_jit_event. (print_it_typical): Added case for bp_jit_event. (print_one_breakpoint_location): Added case for bp_jit_event. (allocate_bp_location): Added case for bp_jit_event. (mention): Added case for bp_jit_event. (delete_command): Added case for bp_jit_event. (breakpoint_re_set_one): Added case for bp_jit_event. (breakpoint_re_set): Added call to jit_inferior_created_hook. (create_jit_event_breakpoint): New. * infrun.c (handle_inferior_event): Add handler for jit event. (follow_exec): Add call to jit_inferior_created_hook. * objfiles.c (free_objfile): Fixed a memory leak. Index: gdb/Makefile.in =================================================================== RCS file: /cvs/src/src/gdb/Makefile.in,v retrieving revision 1.1091 diff -u -p -u -r1.1091 Makefile.in --- gdb/Makefile.in 3 Jul 2009 12:06:35 -0000 1.1091 +++ gdb/Makefile.in 27 Jul 2009 20:34:06 -0000 @@ -677,7 +677,8 @@ SFILES = ada-exp.y ada-lang.c ada-typepr wrapper.c \ xml-tdesc.c xml-support.c \ inferior.c gdb_usleep.c \ - record.c + record.c \ + jit.c LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c @@ -746,7 +747,7 @@ config/rs6000/nm-rs6000.h top.h bsd-kvm. annotate.h sim-regno.h dictionary.h dfp.h main.h frame-unwind.h \ remote-fileio.h i386-linux-tdep.h vax-tdep.h objc-lang.h \ sentinel-frame.h bcache.h symfile.h windows-tdep.h linux-tdep.h \ -gdb_usleep.h +gdb_usleep.h jit.h # Header files that already have srcdir in them, or which are in objdir. @@ -828,7 +829,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $ solib.o solib-null.o \ prologue-value.o memory-map.o xml-support.o \ target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \ - inferior.o osdata.o gdb_usleep.o record.o + inferior.o osdata.o gdb_usleep.o record.o \ + jit.o TSOBS = inflow.o Index: gdb/NEWS =================================================================== RCS file: /cvs/src/src/gdb/NEWS,v retrieving revision 1.319 diff -u -p -u -r1.319 NEWS --- gdb/NEWS 20 Jul 2009 18:51:41 -0000 1.319 +++ gdb/NEWS 27 Jul 2009 20:34:06 -0000 @@ -3,6 +3,11 @@ *** Changes since GDB 6.8 +* GDB now has an interface for JIT compilation. Applications that +dynamically generate code can create symbol files in memory and register +them with GDB. For users, the feature should work transparently, and +for JIT developers, the interface is documented in the GDB manual. + * Tracepoints may now be conditional. The syntax is as for breakpoints; either an "if" clause appended to the "trace" command, or the "condition" command is available. GDB sends the condition to Index: gdb/breakpoint.c =================================================================== RCS file: /cvs/src/src/gdb/breakpoint.c,v retrieving revision 1.413 diff -u -p -u -r1.413 breakpoint.c --- gdb/breakpoint.c 7 Jul 2009 22:21:09 -0000 1.413 +++ gdb/breakpoint.c 27 Jul 2009 20:34:06 -0000 @@ -59,6 +59,7 @@ #include "top.h" #include "wrapper.h" #include "valprint.h" +#include "jit.h" /* readline include files */ #include "readline/readline.h" @@ -1575,6 +1576,13 @@ update_breakpoints_after_exec (void) continue; } + /* JIT breakpoints must be explicitly reset after an exec(). */ + if (b->type == bp_jit_event) + { + delete_breakpoint (b); + continue; + } + /* Thread event breakpoints must be set anew after an exec(), as must overlay event and longjmp master breakpoints. */ if (b->type == bp_thread_event || b->type == bp_overlay_event @@ -2573,6 +2581,7 @@ print_it_typical (bpstat bs) case bp_watchpoint_scope: case bp_call_dummy: case bp_tracepoint: + case bp_jit_event: default: result = PRINT_UNKNOWN; break; @@ -3298,6 +3307,9 @@ bpstat_what (bpstat bs) /* We hit the shared library event breakpoint. */ shlib_event, + /* We hit the jit event breakpoint. */ + jit_event, + /* This is just used to count how many enums there are. */ class_last }; @@ -3313,6 +3325,7 @@ bpstat_what (bpstat bs) #define clr BPSTAT_WHAT_CLEAR_LONGJMP_RESUME #define sr BPSTAT_WHAT_STEP_RESUME #define shl BPSTAT_WHAT_CHECK_SHLIBS +#define jit BPSTAT_WHAT_CHECK_JIT /* "Can't happen." Might want to print an error message. abort() is not out of the question, but chances are GDB is just @@ -3333,12 +3346,13 @@ bpstat_what (bpstat bs) back and decide something of a lower priority is better. The ordering is: - kc < clr sgl shl slr sn sr ss - sgl < shl slr sn sr ss - slr < err shl sn sr ss - clr < err shl sn sr ss - ss < shl sn sr - sn < shl sr + kc < jit clr sgl shl slr sn sr ss + sgl < jit shl slr sn sr ss + slr < jit err shl sn sr ss + clr < jit err shl sn sr ss + ss < jit shl sn sr + sn < jit shl sr + jit < shl sr shl < sr sr < @@ -3356,28 +3370,18 @@ bpstat_what (bpstat bs) table[(int) class_last][(int) BPSTAT_WHAT_LAST] = { /* old action */ - /* kc ss sn sgl slr clr sr shl - */ -/*no_effect */ - {kc, ss, sn, sgl, slr, clr, sr, shl}, -/*wp_silent */ - {ss, ss, sn, ss, ss, ss, sr, shl}, -/*wp_noisy */ - {sn, sn, sn, sn, sn, sn, sr, shl}, -/*bp_nostop */ - {sgl, ss, sn, sgl, slr, slr, sr, shl}, -/*bp_silent */ - {ss, ss, sn, ss, ss, ss, sr, shl}, -/*bp_noisy */ - {sn, sn, sn, sn, sn, sn, sr, shl}, -/*long_jump */ - {slr, ss, sn, slr, slr, err, sr, shl}, -/*long_resume */ - {clr, ss, sn, err, err, err, sr, shl}, -/*step_resume */ - {sr, sr, sr, sr, sr, sr, sr, sr}, -/*shlib */ - {shl, shl, shl, shl, shl, shl, sr, shl} + /* kc ss sn sgl slr clr sr shl jit */ +/* no_effect */ {kc, ss, sn, sgl, slr, clr, sr, shl, jit}, +/* wp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit}, +/* wp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit}, +/* bp_nostop */ {sgl, ss, sn, sgl, slr, slr, sr, shl, jit}, +/* bp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit}, +/* bp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit}, +/* long_jump */ {slr, ss, sn, slr, slr, err, sr, shl, jit}, +/* long_resume */ {clr, ss, sn, err, err, err, sr, shl, jit}, +/* step_resume */ {sr, sr, sr, sr, sr, sr, sr, sr, sr }, +/* shlib */ {shl, shl, shl, shl, shl, shl, sr, shl, shl}, +/* jit_event */ {jit, jit, jit, jit, jit, jit, sr, jit, jit} }; #undef kc @@ -3390,6 +3394,7 @@ bpstat_what (bpstat bs) #undef sr #undef ts #undef shl +#undef jit enum bpstat_what_main_action current_action = BPSTAT_WHAT_KEEP_CHECKING; struct bpstat_what retval; @@ -3460,6 +3465,9 @@ bpstat_what (bpstat bs) case bp_shlib_event: bs_class = shlib_event; break; + case bp_jit_event: + bs_class = jit_event; + break; case bp_thread_event: case bp_overlay_event: case bp_longjmp_master: @@ -3593,6 +3601,7 @@ print_one_breakpoint_location (struct br {bp_longjmp_master, "longjmp master"}, {bp_catchpoint, "catchpoint"}, {bp_tracepoint, "tracepoint"}, + {bp_jit_event, "jit events"}, }; static char bpenables[] = "nynny"; @@ -3721,6 +3730,7 @@ print_one_breakpoint_location (struct br case bp_overlay_event: case bp_longjmp_master: case bp_tracepoint: + case bp_jit_event: if (opts.addressprint) { annotate_field (4); @@ -4362,6 +4372,7 @@ allocate_bp_location (struct breakpoint case bp_shlib_event: case bp_thread_event: case bp_overlay_event: + case bp_jit_event: case bp_longjmp_master: loc->loc_type = bp_loc_software_breakpoint; break; @@ -4644,6 +4655,17 @@ struct lang_and_radix int radix; }; +/* Create a breakpoint for JIT code registration and unregistration. */ + +struct breakpoint * +create_jit_event_breakpoint (struct gdbarch *gdbarch, CORE_ADDR address) +{ + struct breakpoint *b; + + b = create_internal_breakpoint (gdbarch, address, bp_jit_event); + update_global_location_list_nothrow (1); + return b; +} void remove_solib_event_breakpoints (void) @@ -5279,6 +5301,7 @@ mention (struct breakpoint *b) case bp_shlib_event: case bp_thread_event: case bp_overlay_event: + case bp_jit_event: case bp_longjmp_master: break; } @@ -7585,6 +7608,7 @@ delete_command (char *arg, int from_tty) { if (b->type != bp_call_dummy && b->type != bp_shlib_event + && b->type != bp_jit_event && b->type != bp_thread_event && b->type != bp_overlay_event && b->type != bp_longjmp_master @@ -7604,6 +7628,7 @@ delete_command (char *arg, int from_tty) if (b->type != bp_call_dummy && b->type != bp_shlib_event && b->type != bp_thread_event + && b->type != bp_jit_event && b->type != bp_overlay_event && b->type != bp_longjmp_master && b->number >= 0) @@ -7926,6 +7951,7 @@ breakpoint_re_set_one (void *bint) case bp_step_resume: case bp_longjmp: case bp_longjmp_resume: + case bp_jit_event: break; } @@ -7954,6 +7980,8 @@ breakpoint_re_set (void) set_language (save_language); input_radix = save_input_radix; + jit_inferior_created_hook (); + create_overlay_event_breakpoint ("_ovly_debug_event"); create_longjmp_master_breakpoint ("longjmp"); create_longjmp_master_breakpoint ("_longjmp"); Index: gdb/breakpoint.h =================================================================== RCS file: /cvs/src/src/gdb/breakpoint.h,v retrieving revision 1.95 diff -u -p -u -r1.95 breakpoint.h --- gdb/breakpoint.h 2 Jul 2009 17:12:24 -0000 1.95 +++ gdb/breakpoint.h 27 Jul 2009 20:34:06 -0000 @@ -120,6 +120,9 @@ enum bptype bp_catchpoint, bp_tracepoint, + + /* Event for JIT compiled code generation or deletion. */ + bp_jit_event, }; /* States of enablement of breakpoint. */ @@ -548,6 +551,9 @@ enum bpstat_what_main_action keep checking. */ BPSTAT_WHAT_CHECK_SHLIBS, + /* Check for new JITed code. */ + BPSTAT_WHAT_CHECK_JIT, + /* This is just used to keep track of how many enums there are. */ BPSTAT_WHAT_LAST }; @@ -841,6 +847,9 @@ extern void mark_breakpoints_out (void); extern void make_breakpoint_permanent (struct breakpoint *); +extern struct breakpoint *create_jit_event_breakpoint (struct gdbarch *, + CORE_ADDR); + extern struct breakpoint *create_solib_event_breakpoint (struct gdbarch *, CORE_ADDR); Index: gdb/infrun.c =================================================================== RCS file: /cvs/src/src/gdb/infrun.c,v retrieving revision 1.402 diff -u -p -u -r1.402 infrun.c --- gdb/infrun.c 20 Jul 2009 15:05:12 -0000 1.402 +++ gdb/infrun.c 27 Jul 2009 20:34:07 -0000 @@ -50,6 +50,7 @@ #include "event-top.h" #include "record.h" #include "inline-frame.h" +#include "jit.h" /* Prototypes for local functions */ @@ -544,6 +545,8 @@ follow_exec (ptid_t pid, char *execd_pat solib_create_inferior_hook (); #endif + jit_inferior_created_hook (); + /* Reinsert all breakpoints. (Those which were symbolic have been reset to the proper address in the new a.out, thanks to symbol_file_command...) */ @@ -3529,6 +3532,22 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME ( } break; + case BPSTAT_WHAT_CHECK_JIT: + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_CHECK_JIT\n"); + + /* Switch terminal for any messages produced by breakpoint_re_set. */ + target_terminal_ours_for_output (); + + jit_event_handler (); + + target_terminal_inferior (); + + /* We want to step over this breakpoint, then keep going. */ + ecs->event_thread->stepping_over_breakpoint = 1; + + break; + case BPSTAT_WHAT_LAST: /* Not a real code, but listed here to shut up gcc -Wall. */ Index: gdb/jit.c =================================================================== RCS file: gdb/jit.c diff -N gdb/jit.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ gdb/jit.c 27 Jul 2009 20:34:07 -0000 @@ -0,0 +1,393 @@ +/* Handle JIT code generation in the inferior for GDB, the GNU Debugger. + + Copyright (C) 2009 + Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "defs.h" + +#include "jit.h" +#include "breakpoint.h" +#include "gdbcore.h" +#include "observer.h" +#include "objfiles.h" +#include "symfile.h" +#include "symtab.h" +#include "target.h" +#include "gdb_stat.h" + +//#include "assert.h" +//#include "string.h" + +static const struct objfile_data *jit_objfile_data; + +static const char *const jit_break_name = "__jit_debug_register_code"; + +static const char *const jit_descriptor_name = "__jit_debug_descriptor"; + +/* This is the address of the JIT descriptor in the inferior. */ + +static CORE_ADDR jit_descriptor_addr = 0; + +struct target_buffer +{ + CORE_ADDR base; + size_t size; +}; + +/* Openning the file is a no-op. */ + +static void * +mem_bfd_iovec_open (struct bfd *abfd, void *open_closure) +{ + return open_closure; +} + +/* Closing the file is just freeing the base/size pair on our side. */ + +static int +mem_bfd_iovec_close (struct bfd *abfd, void *stream) +{ + xfree (stream); + return 1; +} + +/* For reading the file, we just need to pass through to target_read_memory and + fix up the arguments and return values. */ + +static file_ptr +mem_bfd_iovec_pread (struct bfd *abfd, void *stream, void *buf, + file_ptr nbytes, file_ptr offset) +{ + int err; + struct target_buffer *buffer = (struct target_buffer *) stream; + + /* If this read will read all of the file, limit it to just the rest. */ + if (offset + nbytes > buffer->size) + nbytes = buffer->size - offset; + + /* If there are no more bytes left, we've reached EOF. */ + if (nbytes == 0) + return 0; + + err = target_read_memory (buffer->base + offset, (gdb_byte *) buf, nbytes); + if (err) + return -1; + + return nbytes; +} + +/* For statting the file, we only support the st_size attribute. */ + +static int +mem_bfd_iovec_stat (struct bfd *abfd, void *stream, struct stat *sb) +{ + struct target_buffer *buffer = (struct target_buffer*) stream; + + sb->st_size = buffer->size; + return 0; +} + +/* Open a BFD from the target's memory. */ + +static struct bfd * +bfd_open_from_target_memory (CORE_ADDR addr, size_t size, char *target) +{ + const char *filename = xstrdup (""); + struct target_buffer *buffer = xmalloc (sizeof (struct target_buffer)); + + buffer->base = addr; + buffer->size = size; + return bfd_openr_iovec (filename, target, + mem_bfd_iovec_open, + buffer, + mem_bfd_iovec_pread, + mem_bfd_iovec_close, + mem_bfd_iovec_stat); +} + +/* Helper function for reading the global JIT descriptor from remote memory. */ + +static void +jit_read_descriptor (struct jit_descriptor *descriptor) +{ + int err; + struct type *ptr_type; + int ptr_size; + int desc_size; + gdb_byte *desc_buf; + enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch); + + /* Figure out how big the descriptor is on the remote and how to read it. */ + ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr; + ptr_size = TYPE_LENGTH (ptr_type); + desc_size = 8 + 2 * ptr_size; /* Two 32-bit ints and two pointers. */ + desc_buf = alloca (desc_size); + + /* Read the descriptor. */ + err = target_read_memory (jit_descriptor_addr, desc_buf, desc_size); + if (err) + error (_("Unable to read JIT descriptor from remote memory!")); + + /* Fix the endianness to match the host. */ + descriptor->version = extract_unsigned_integer (&desc_buf[0], 4, byte_order); + descriptor->action_flag = + extract_unsigned_integer (&desc_buf[4], 4, byte_order); + descriptor->relevant_entry = extract_typed_address (&desc_buf[8], ptr_type); + descriptor->first_entry = + extract_typed_address (&desc_buf[8 + ptr_size], ptr_type); +} + +/* Helper function for reading a JITed code entry from remote memory. */ + +static void +jit_read_code_entry (CORE_ADDR code_addr, struct jit_code_entry *code_entry) +{ + int err; + struct type *ptr_type; + int ptr_size; + int entry_size; + gdb_byte *entry_buf; + enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch); + + /* Figure out how big the entry is on the remote and how to read it. */ + ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr; + ptr_size = TYPE_LENGTH (ptr_type); + entry_size = 3 * ptr_size + 8; /* Three pointers and one 64-bit int. */ + entry_buf = alloca (entry_size); + + /* Read the entry. */ + err = target_read_memory (code_addr, entry_buf, entry_size); + if (err) + error (_("Unable to read JIT code entry from remote memory!")); + + /* Fix the endianness to match the host. */ + ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr; + code_entry->next_entry = extract_typed_address (&entry_buf[0], ptr_type); + code_entry->prev_entry = + extract_typed_address (&entry_buf[ptr_size], ptr_type); + code_entry->symfile_addr = + extract_typed_address (&entry_buf[2 * ptr_size], ptr_type); + code_entry->symfile_size = + extract_unsigned_integer (&entry_buf[3 * ptr_size], 8, byte_order); +} + +/* This function registers code associated with a JIT code entry. It uses the + pointer and size pair in the entry to read the symbol file from the remote + and then calls symbol_file_add_from_local_memory to add it as though it were + a symbol file added by the user. */ + +static void +jit_register_code (CORE_ADDR entry_addr, struct jit_code_entry *code_entry) +{ + bfd *nbfd; + struct section_addr_info *sai; + struct bfd_section *sec; + struct objfile *objfile; + struct cleanup *old_cleanups; + int i; + const struct bfd_arch_info *b; + + nbfd = bfd_open_from_target_memory (code_entry->symfile_addr, + code_entry->symfile_size, gnutarget); + old_cleanups = make_cleanup_bfd_close (nbfd); + + /* Check the format. NOTE: This initializes important data that GDB uses! + We would segfault later without this line. */ + if (!bfd_check_format (nbfd, bfd_object)) + { + printf_unfiltered (_("\ +JITed symbol file is not an object file, ignoring it.\n")); + do_cleanups (old_cleanups); + return; + } + + /* Check bfd arch. */ + b = gdbarch_bfd_arch_info (target_gdbarch); + if (b->compatible (b, bfd_get_arch_info (nbfd)) != b) + warning (_("JITed object file architecture %s is not compatible " + "with target architecture %s."), bfd_get_arch_info + (nbfd)->printable_name, b->printable_name); + + /* Read the section address information out of the symbol file. Since the + file is generated by the JIT at runtime, it should all of the absolute + addresses that we care about. */ + sai = alloc_section_addr_info (bfd_count_sections (nbfd)); + make_cleanup_free_section_addr_info (sai); + i = 0; + for (sec = nbfd->sections; sec != NULL; sec = sec->next) + if ((bfd_get_section_flags (nbfd, sec) & (SEC_ALLOC|SEC_LOAD)) != 0) + { + /* We assume that these virtual addresses are absolute, and do not + treat them as offsets. */ + sai->other[i].addr = bfd_get_section_vma (nbfd, sec); + sai->other[i].name = (char *) bfd_get_section_name (nbfd, sec); + sai->other[i].sectindex = sec->index; + ++i; + } + + /* This call takes ownership of sai. */ + objfile = symbol_file_add_from_bfd (nbfd, 0, sai, OBJF_SHARED); + + /* Remember a mapping from entry_addr to objfile. */ + set_objfile_data (objfile, jit_objfile_data, (void *) entry_addr); + + discard_cleanups (old_cleanups); +} + +/* This function unregisters JITed code and frees the corresponding objfile. */ + +static void +jit_unregister_code (struct objfile *objfile) +{ + free_objfile (objfile); +} + +void +jit_inferior_created_hook (void) +{ + struct minimal_symbol *reg_symbol; + struct minimal_symbol *desc_symbol; + CORE_ADDR reg_addr; + struct jit_descriptor descriptor; + struct jit_code_entry cur_entry; + CORE_ADDR cur_entry_addr; + + /* If we already found the symbols and successfully set the breakpoint, don't + do it again. */ + if (jit_descriptor_addr != 0) + return; + + /* Lookup the registration symbol. If it is missing, then we assume we are + not attached to a JIT. */ + reg_symbol = lookup_minimal_symbol (jit_break_name, NULL, NULL); + if (reg_symbol == NULL) + return; + reg_addr = SYMBOL_VALUE_ADDRESS (reg_symbol); + if (reg_addr == 0) + return; + + /* Lookup the descriptor symbol and cache the addr. If it is missing, we + assume we are not attached to a JIT and return early. */ + desc_symbol = lookup_minimal_symbol (jit_descriptor_name, NULL, NULL); + if (desc_symbol == NULL) + return; + jit_descriptor_addr = SYMBOL_VALUE_ADDRESS (desc_symbol); + if (jit_descriptor_addr == 0) + return; + + /* Read the descriptor so we can check the version number and load any already + JITed functions. */ + jit_read_descriptor (&descriptor); + + /* Check that the version number agrees with that we support. */ + if (descriptor.version != 1) + error (_("Unsupported JIT protocol version in descriptor!")); + + /* Put a breakpoint in the registration symbol. */ + create_jit_event_breakpoint (target_gdbarch, reg_addr); + + /* If we've attached to a running program, we need to check the descriptor to + register any functions that were already generated. */ + for (cur_entry_addr = descriptor.first_entry; + cur_entry_addr != 0; + cur_entry_addr = cur_entry.next_entry) + { + jit_read_code_entry (cur_entry_addr, &cur_entry); + jit_register_code (cur_entry_addr, &cur_entry); + } +} + +/* Wrapper to match the observer function pointer prototype. */ + +static void +jit_inferior_created_hook1 (struct target_ops *objfile, int from_tty) +{ + jit_inferior_created_hook (); +} + +/* This function cleans up any code entries left over when the inferior exits. + We get left over code when the inferior exits without unregistering its code, + for example when it crashes. */ + +static void +jit_inferior_exit_hook (int pid) +{ + struct objfile *objf; + struct objfile *temp; + + /* We need to reset the descriptor addr so that next time we load up the + inferior we look for it again. */ + jit_descriptor_addr = 0; + + ALL_OBJFILES_SAFE (objf, temp) + if (objfile_data (objf, jit_objfile_data) != NULL) + jit_unregister_code (objf); +} + +void +jit_event_handler (void) +{ + struct jit_descriptor descriptor; + struct jit_code_entry code_entry; + CORE_ADDR entry_addr; + struct objfile *objf; + struct objfile *temp; + + /* Read the descriptor from remote memory. */ + jit_read_descriptor (&descriptor); + entry_addr = descriptor.relevant_entry; + + /* Do the corresponding action. */ + switch (descriptor.action_flag) + { + case JIT_NOACTION: + break; + case JIT_REGISTER: + jit_read_code_entry (entry_addr, &code_entry); + jit_register_code (entry_addr, &code_entry); + break; + case JIT_UNREGISTER: + /* Look up the objfile with this code entry address. */ + ALL_OBJFILES_SAFE (objf, temp) + { + if (objfile_data (objf, jit_objfile_data) == (void *) entry_addr) + { + jit_unregister_code (objf); + return; + } + } + printf_unfiltered ("Unable to find JITed code entry at address: %p\n", + (void *) entry_addr); + break; + default: + error (_("Unknown action_flag value in JIT descriptor!")); + break; + } +} + +/* Provide a prototype to silence -Wmissing-prototypes. */ + +extern void _initialize_jit (void); + +void +_initialize_jit (void) +{ + observer_attach_inferior_created (jit_inferior_created_hook1); + observer_attach_inferior_exit (jit_inferior_exit_hook); + jit_objfile_data = register_objfile_data (); +} Index: gdb/jit.h =================================================================== RCS file: gdb/jit.h diff -N gdb/jit.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ gdb/jit.h 27 Jul 2009 20:34:07 -0000 @@ -0,0 +1,77 @@ +/* JIT declarations for GDB, the GNU Debugger. + + Copyright (C) 2009 + Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef JIT_H +#define JIT_H + +/* When the JIT breakpoint fires, the inferior wants us to take one of these + actions. These values are used by the inferior, so the values of these enums + cannot be changed. */ + +typedef enum +{ + JIT_NOACTION = 0, + JIT_REGISTER, + JIT_UNREGISTER +} jit_actions_t; + +/* This struct describes a single symbol file in a linked list of symbol files + describing generated code. As the inferior generates code, it adds these + entries to the list, and when we attach to the inferior, we read them all. + For the first element prev_entry should be NULL, and for the last element + next_entry should be NULL. */ + +struct jit_code_entry +{ + CORE_ADDR next_entry; + CORE_ADDR prev_entry; + CORE_ADDR symfile_addr; + uint64_t symfile_size; +}; + +/* This is the global descriptor that the inferior uses to communicate + information to the debugger. To alert the debugger to take an action, the + inferior sets the action_flag to the appropriate enum value, updates + relevant_entry to point to the relevant code entry, and calls the function at + the well-known symbol with our breakpoint. We then read this descriptor from + another global well-known symbol. */ + +struct jit_descriptor +{ + uint32_t version; + /* This should be jit_actions_t, but we want to be specific about the + bit-width. */ + uint32_t action_flag; + CORE_ADDR relevant_entry; + CORE_ADDR first_entry; +}; + +/* Looks for the descriptor and registration symbols and breakpoints the + registration function. If it finds both, it registers all the already JITed + code. If it has already found the symbols, then it doesn't try again. */ + +extern void jit_inferior_created_hook (void); + +/* This function is called by handle_inferior_event when it decides that the JIT + event breakpoint has fired. */ + +extern void jit_event_handler (void); + +#endif /* JIT_H */ Index: gdb/objfiles.c =================================================================== RCS file: /cvs/src/src/gdb/objfiles.c,v retrieving revision 1.87 diff -u -p -u -r1.87 objfiles.c --- gdb/objfiles.c 22 Jul 2009 19:21:31 -0000 1.87 +++ gdb/objfiles.c 27 Jul 2009 20:34:07 -0000 @@ -25,6 +25,7 @@ #include "defs.h" #include "bfd.h" /* Binary File Description */ +#include "libbfd.h" #include "symtab.h" #include "symfile.h" #include "objfiles.h" @@ -438,9 +439,19 @@ free_objfile (struct objfile *objfile) if (objfile->obfd != NULL && !(objfile->flags & OBJF_KEEPBFD)) { char *name = bfd_get_filename (objfile->obfd); + struct bfd_in_memory *bim = NULL; + /* Hack to work around the fact that BFD does not take ownership of the + memory for files allocated in memory. */ + if (objfile->obfd->flags & BFD_IN_MEMORY) + bim = (struct bfd_in_memory *) objfile->obfd->iostream; if (!bfd_close (objfile->obfd)) warning (_("cannot close \"%s\": %s"), name, bfd_errmsg (bfd_get_error ())); + if (bim != NULL) + { + xfree (bim->buffer); + xfree (bim); + } xfree (name); } Index: gdb/doc/gdb.texinfo =================================================================== RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v retrieving revision 1.608 diff -u -p -u -r1.608 gdb.texinfo --- gdb/doc/gdb.texinfo 20 Jul 2009 18:51:41 -0000 1.608 +++ gdb/doc/gdb.texinfo 27 Jul 2009 20:34:08 -0000 @@ -159,6 +159,7 @@ software in general. We will miss him. * Emacs:: Using @value{GDBN} under @sc{gnu} Emacs * GDB/MI:: @value{GDBN}'s Machine Interface. * Annotations:: @value{GDBN}'s annotation interface. +* JIT Interface:: Using the JIT debugging interface. * GDB Bugs:: Reporting bugs in @value{GDBN} @@ -25822,6 +25823,115 @@ source which is being displayed. @var{a followed by one or more lowercase hex digits (note that this does not depend on the language). +@node JIT Interface +@chapter @value{GDBN}'s JIT Interface + +This chapter documents @value{GDBN}'s just-in-time compiler (JIT) interface. +Just-in-time compilers (JITs) are normally difficult to debug because they +generate code at runtime, and GDB normally gets all of its symbols from object +files. In order to debug executables that use JITs, GDB has an interface that +allows the JIT to register in-memory symbol files with GDB at runtime. If you +are using GDB to debug a program using this interface, then it should just work +so long as you have not stripped the binary. If you are developing a JIT, then +the interface is documented in the next section. At this time, the only known +client of this interface is the LLVM JIT. + +@section Using the JIT Interface + +Broadly speaking, the JIT interface mirrors the dynamic loader interface, which +means that the JIT communicates with GDB by writing some global data and calling +a fuction at a well-known symbol. When GDB attaches, it puts a breakpoint in +that function so that it can read the debug information. + +These are the relevant struct declarations that a C program should include to +implement the interface: + +@smallexample +typedef enum +@{ + JIT_NOACTION = 0, + JIT_REGISTER_FN, + JIT_UNREGISTER_FN +@} jit_actions_t; + +struct jit_code_entry +@{ + struct jit_code_entry *next_entry; + struct jit_code_entry *prev_entry; + const char *symfile_addr; + uint64_t symfile_size; +@}; + +struct jit_descriptor +@{ + uint32_t version; + /* This type should be jit_actions_t, but we use uint32_t + to be explicit about the bitwidth. */ + uint32_t action_flag; + struct jit_code_entry *relevant_entry; + struct jit_code_entry *first_entry; +@}; + +/* GDB puts a breakpoint in this function. */ +void __attribute__((noinline)) __jit_debug_register_code() @{ @}; + +/* Make sure to specify the version statically, because the + debugger may check the version before we can set it. */ +struct jit_descriptor __jit_debug_descriptor = @{ 1, 0, 0, 0 @}; +@end smallexample + +If the JIT is multi-threaded, then it is important that the JIT synchronize any +modifications to this global data properly, mostly likely by putting a global +mutex around these routines. + +@section Registering Code + +To register code with GDB, the JIT should follow this protocol: + +@itemize @bullet +@item +Generate an object file in memory with symbols and other desired debug +information. The file must include the virtual addresses of the sections. + +@item +Create a code entry for the file, which gives the start and size of the symbol +file. + +@item +Add it to the linked list in the JIT descriptor. + +@item +Point the relevant_entry field of the descriptor at the entry. + +@item +Set @code{action_flag} to JIT_REGISTER and call +@code{__jit_debug_register_code}. +@end itemize + +When GDB is attached, it uses the relevant_entry pointer so it doesn't have to +walk the list looking for new code. However, the linked list must still be +maintained in order to allow GDB to attach to a running process and properly +find all of the debugging information. + +@section Unregistering Code + +If code is freed, then the JIT should use the following protocol: + +@itemize @bullet +@item +Remove the code entry corresponding to the code from the linked list. + +@item +Point the @code{relevant_entry} field of the descriptor at the code entry. + +@item +Set @code{action_flag} to JIT_UNREGISTER and call +@code{__jit_debug_register_code}. +@end itemize + +If the JIT recompiles code throughout its lifetime without unregistering it, +then GDB and the JIT will continue to use extra memory for these symbol files. + @node GDB Bugs @chapter Reporting Bugs in @value{GDBN} @cindex bugs in @value{GDBN}