2009-07-24 Reid Kleckner Add interface for JIT code generation. * NEWS: Announce 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: (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. (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. (create_jit_event_breakpoint): New. * infrun.c (handle_inferior_event): Add handler for jit event. * symfile.h: (symbol_file_add_from_local_memory): New. (symbol_file_add_from_memory): Added declaration. (symbol_file_guess_template): New. * symfile-mem.c: (bfd_open_from_memory): New. (symbol_file_add_from_local_memory): New. (symbol_file_add_from_memory): Modified to use new common code. (symbol_file_add_from_memory_common): New. (symbol_file_guess_template): New. * 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 24 Jul 2009 16:45: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 24 Jul 2009 16:45: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 object files with debug info in +memory and register them with GDB. The interface also supports +attaching to a running process. + * 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 24 Jul 2009 16:45:07 -0000 @@ -2573,6 +2573,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 +3299,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 +3317,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 +3338,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 +3362,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 +3386,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 +3457,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 +3593,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 +3722,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 +4364,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 +4647,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 +5293,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 +7600,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 +7620,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 +7943,7 @@ breakpoint_re_set_one (void *bint) case bp_step_resume: case bp_longjmp: case bp_longjmp_resume: + case bp_jit_event: break; } 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 24 Jul 2009 16:45:07 -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 24 Jul 2009 16:45:07 -0000 @@ -50,6 +50,7 @@ #include "event-top.h" #include "record.h" #include "inline-frame.h" +#include "jit.h" /* Prototypes for local functions */ @@ -3529,6 +3530,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 24 Jul 2009 16:45:08 -0000 @@ -0,0 +1,277 @@ +/* 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" + +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; + +/* Helper method 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 method 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 *templ; + bfd *nbfd; + bfd_byte *buffer; + bfd_size_type size; + int err; + struct section_addr_info *sai; + struct objfile *objfile; + struct cleanup *old_cleanups; + + /* We need some representative bfd to know the target we are looking at. */ + templ = symbol_file_guess_template (); + if (templ == NULL) + error (_("\ +Cannot add symbol file for JITed code without an object file template.")); + + /* Read the symfile from remote memory. */ + size = code_entry->symfile_size; + buffer = xmalloc (size); + old_cleanups = make_cleanup (xfree, buffer); + err = target_read_memory (code_entry->symfile_addr, buffer, size); + if (err) + error (_("Unable to read JITed symfile from remote memory!")); + + /* Create a new BFD from the buffer in memory and add it as a symbol file. */ + objfile = symbol_file_add_from_local_memory (templ, buffer, size); + + /* 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); +} + +/* This function reads any existing code entries in the linked list and inserts + the breakpoint so we can listen for any more. */ + +static void +jit_inferior_created_hook (struct target_ops *objfile, int from_tty) +{ + 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; + int err; + + /* 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); + } +} + +/* 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; + + 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; + int err; + 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_hook); + 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 24 Jul 2009 16:45:08 -0000 @@ -0,0 +1,75 @@ +/* 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. The ordering of this struct must be consistent + with the ordering in the client, so it cannot be changed without updating the + protocol version. */ + +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. The ordering of this struct must be + consistent with the ordering in the client, so it cannot be changed without + updating the protocol version. */ + +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; +}; + +/* 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 24 Jul 2009 16:45:08 -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/symfile-mem.c =================================================================== RCS file: /cvs/src/src/gdb/symfile-mem.c,v retrieving revision 1.19 diff -u -p -u -r1.19 symfile-mem.c --- gdb/symfile-mem.c 2 Jul 2009 17:21:06 -0000 1.19 +++ gdb/symfile-mem.c 24 Jul 2009 16:45:08 -0000 @@ -54,36 +54,23 @@ #include "observer.h" #include "auxv.h" #include "elf/common.h" +#include "bfd.h" +#include "libbfd.h" +#include -/* Read inferior memory at ADDR to find the header of a loaded object file - and read its in-core symbols out of inferior memory. TEMPL is a bfd - representing the target's format. NAME is the name to use for this - symbol file in messages; it can be NULL or a malloc-allocated string - which will be attached to the BFD. */ +/* Common code for symbol_file_add_from_memory and + symbol_file_add_from_local_memory. */ + static struct objfile * -symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr, char *name, - int from_tty) +symbol_file_add_from_memory_common (struct bfd *nbfd, bfd_vma loadbase, + int from_tty) { struct objfile *objf; - struct bfd *nbfd; struct bfd_section *sec; - bfd_vma loadbase; struct section_addr_info *sai; unsigned int i; - - if (bfd_get_flavour (templ) != bfd_target_elf_flavour) - error (_("add-symbol-file-from-memory not supported for this target")); - - nbfd = bfd_elf_bfd_from_remote_memory (templ, addr, &loadbase, - target_read_memory); - if (nbfd == NULL) - error (_("Failed to read a valid object file image from memory.")); - - if (name == NULL) - nbfd->filename = xstrdup ("shared object read from target memory"); - else - nbfd->filename = name; + struct cleanup *old_cleanups; if (!bfd_check_format (nbfd, bfd_object)) { @@ -96,7 +83,7 @@ symbol_file_add_from_memory (struct bfd } sai = alloc_section_addr_info (bfd_count_sections (nbfd)); - make_cleanup (xfree, sai); + old_cleanups = make_cleanup (xfree, sai); i = 0; for (sec = nbfd->sections; sec != NULL; sec = sec->next) if ((bfd_get_section_flags (nbfd, sec) & (SEC_ALLOC|SEC_LOAD)) != 0) @@ -113,9 +100,127 @@ symbol_file_add_from_memory (struct bfd /* This might change our ideas about frames already looked at. */ reinit_frame_cache (); + discard_cleanups (old_cleanups); return objf; } +/* Read inferior memory at ADDR to find the header of a loaded object file + and read its in-core symbols out of inferior memory. TEMPL is a bfd + representing the target's format. NAME is the name to use for this + symbol file in messages; it can be NULL or a malloc-allocated string + which will be attached to the BFD. */ + +struct objfile * +symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr, char *name, + int from_tty) +{ + struct bfd *nbfd; + bfd_vma loadbase; + + if (bfd_get_flavour (templ) != bfd_target_elf_flavour) + error (_("add-symbol-file-from-memory not supported for this target")); + + nbfd = bfd_elf_bfd_from_remote_memory (templ, addr, &loadbase, + target_read_memory); + if (nbfd == NULL) + error (_("Failed to read a valid object file image from memory.")); + + if (name == NULL) + nbfd->filename = xstrdup ("shared object read from target memory"); + else + nbfd->filename = name; + + return symbol_file_add_from_memory_common (nbfd, loadbase, from_tty); +} + +/* Create a new BFD for a file that is already loaded into memory. This does + not take ownership of buffer or filename, and the caller must free them. + This code is based on bfd_from_remote_memory in bfd/elfcode.h, and should + probably live inside of the bfd library. */ + +static bfd * +bfd_open_from_memory (struct bfd *templ, bfd_byte *buffer, + bfd_size_type size, const char *filename) +{ + bfd *nbfd; + struct bfd_in_memory *bim; + + bim = bfd_malloc (sizeof (struct bfd_in_memory)); + if (bim == NULL) + return NULL; + bim->buffer = buffer; + bim->size = size; + + nbfd = _bfd_new_bfd (); + if (nbfd == NULL) + { + free (bim); + return NULL; + } + nbfd->filename = filename; + nbfd->xvec = templ->xvec; + nbfd->iostream = bim; + nbfd->flags = BFD_IN_MEMORY; + nbfd->direction = read_direction; + nbfd->mtime = time (NULL); + nbfd->mtime_set = TRUE; + + return nbfd; +} + +/* Add a symbol file from local memory. This differs from + symbol_file_add_from_memory in that it assumes that we have the entire + symbol file laid out in memory and we don't need to use + bfd_elf_bfd_from_remote_memory to reconstruct the ELF from the program + headers. */ + +struct objfile * +symbol_file_add_from_local_memory (struct bfd *templ, bfd_byte *buffer, + bfd_size_type size) +{ + bfd *nbfd; + int err; + struct section_addr_info *sai; + bfd_vma loadbase; + const char *filename; + struct cleanup *old_cleanups; + struct objfile *objfile; + + /* The filename must be strdup'd or GDB will crash when freeing the object + file. */ + filename = xstrdup (""); + old_cleanups = make_cleanup (xfree, (void*)filename); + + /* Create a new BFD from the buffer in memory and add it as a symbol file. */ + nbfd = bfd_open_from_memory (templ, buffer, size, filename); + if (nbfd == NULL) + error (_("Unable to create BFD from local memory: %s"), + bfd_errmsg (bfd_get_error ())); + + /* We set loadbase to 0 and assume that the symbol file we're loading has the + absolute addresses set in the ELF. */ + loadbase = 0; + objfile = symbol_file_add_from_memory_common (nbfd, loadbase, 0); + discard_cleanups (old_cleanups); + return objfile; +} + +/* Use the BFD of the main executable to guess what kind of object file we want + to add. This is used for adding symbol files from memory, where we don't + know the format of the file. */ + +struct bfd * +symbol_file_guess_template (void) +{ + struct bfd *templ; + + if (symfile_objfile != NULL) + templ = symfile_objfile->obfd; + else + templ = exec_bfd; + + return templ; +} static void add_symbol_file_from_memory_command (char *args, int from_tty) @@ -129,10 +234,7 @@ add_symbol_file_from_memory_command (cha addr = parse_and_eval_address (args); /* We need some representative bfd to know the target we are looking at. */ - if (symfile_objfile != NULL) - templ = symfile_objfile->obfd; - else - templ = exec_bfd; + templ = symbol_file_guess_template (); if (templ == NULL) error (_("\ Must use symbol-file or exec-file before add-symbol-file-from-memory.")); Index: gdb/symfile.h =================================================================== RCS file: /cvs/src/src/gdb/symfile.h,v retrieving revision 1.51 diff -u -p -u -r1.51 symfile.h --- gdb/symfile.h 16 Jun 2009 18:49:25 -0000 1.51 +++ gdb/symfile.h 24 Jul 2009 16:45:08 -0000 @@ -408,4 +408,17 @@ extern void elfmdebug_build_psymtabs (st const struct ecoff_debug_swap *, asection *); +/* From symfile-mem.c */ + +extern struct bfd *symbol_file_guess_template (void); + +extern struct objfile *symbol_file_add_from_memory (struct bfd *templ, + CORE_ADDR addr, + char *name, + int from_tty); + +extern struct objfile *symbol_file_add_from_local_memory (struct bfd *templ, + bfd_byte *buffer, + bfd_size_type size); + #endif /* !defined(SYMFILE_H) */