2009-07-22 Reid Kleckner Add interface for JIT code generation. * 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.1086 diff -u -p -u -r1.1086 Makefile.in --- gdb/Makefile.in 28 May 2009 01:05:13 -0000 1.1086 +++ gdb/Makefile.in 22 Jul 2009 22:26:07 -0000 @@ -673,7 +673,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 @@ -744,7 +745,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. @@ -825,7 +826,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/breakpoint.c =================================================================== RCS file: /cvs/src/src/gdb/breakpoint.c,v retrieving revision 1.403 diff -u -p -u -r1.403 breakpoint.c --- gdb/breakpoint.c 8 Jun 2009 16:05:14 -0000 1.403 +++ gdb/breakpoint.c 22 Jul 2009 22:26:08 -0000 @@ -2510,6 +2510,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; @@ -3232,6 +3233,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 }; @@ -3247,6 +3251,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 @@ -3267,12 +3272,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 < @@ -3290,28 +3296,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 @@ -3324,6 +3320,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; @@ -3394,6 +3391,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: bs_class = bp_nostop; @@ -3524,6 +3524,7 @@ print_one_breakpoint_location (struct br {bp_overlay_event, "overlay events"}, {bp_catchpoint, "catchpoint"}, {bp_tracepoint, "tracepoint"}, + {bp_jit_event, "jit events"}, }; static char bpenables[] = "nynny"; @@ -3651,6 +3652,7 @@ print_one_breakpoint_location (struct br case bp_thread_event: case bp_overlay_event: case bp_tracepoint: + case bp_jit_event: if (opts.addressprint) { annotate_field (4); @@ -4266,6 +4268,7 @@ allocate_bp_location (struct breakpoint case bp_shlib_event: case bp_thread_event: case bp_overlay_event: + case bp_jit_event: loc->loc_type = bp_loc_software_breakpoint; break; case bp_hardware_breakpoint: @@ -4530,6 +4533,16 @@ struct lang_and_radix }; +struct breakpoint * +create_jit_event_breakpoint (CORE_ADDR address) +{ + struct breakpoint *b; + + b = create_internal_breakpoint (address, bp_jit_event); + update_global_location_list_nothrow (1); + return b; +} + void remove_solib_event_breakpoints (void) { @@ -5155,6 +5168,7 @@ mention (struct breakpoint *b) case bp_shlib_event: case bp_thread_event: case bp_overlay_event: + case bp_jit_event: break; } @@ -7398,6 +7412,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->number >= 0) @@ -7415,6 +7430,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->number >= 0) @@ -7736,6 +7752,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.92 diff -u -p -u -r1.92 breakpoint.h --- gdb/breakpoint.h 24 May 2009 18:00:08 -0000 1.92 +++ gdb/breakpoint.h 22 Jul 2009 22:26:08 -0000 @@ -113,6 +113,9 @@ enum bptype bp_catchpoint, bp_tracepoint, + + /* Event for JIT compiled code generation or deletion. */ + bp_jit_event, }; /* States of enablement of breakpoint. */ @@ -535,6 +538,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 }; @@ -827,6 +833,8 @@ extern void mark_breakpoints_out (void); extern void make_breakpoint_permanent (struct breakpoint *); +extern struct breakpoint *create_jit_event_breakpoint (CORE_ADDR); + extern struct breakpoint *create_solib_event_breakpoint (CORE_ADDR); extern struct breakpoint *create_thread_event_breakpoint (CORE_ADDR); Index: gdb/infrun.c =================================================================== RCS file: /cvs/src/src/gdb/infrun.c,v retrieving revision 1.385 diff -u -p -u -r1.385 infrun.c --- gdb/infrun.c 7 Jun 2009 16:46:48 -0000 1.385 +++ gdb/infrun.c 22 Jul 2009 22:26:08 -0000 @@ -49,6 +49,7 @@ #include "mi/mi-common.h" #include "event-top.h" #include "record.h" +#include "jit.h" /* Prototypes for local functions */ @@ -3462,6 +3463,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 22 Jul 2009 22:26:08 -0000 @@ -0,0 +1,300 @@ +/* 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 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; + +/* This is a linked list mapping code entry addresses to objfiles. */ + +static struct jit_obj_link *jit_objfiles = NULL; + +/* Helper method for reading the global JIT descriptor from remote memory. */ + +static int +jit_read_descriptor (struct jit_descriptor *descriptor) +{ + int err; + struct type *ptr_type; + int ptr_size; + int desc_size; + gdb_byte *desc_buf; + + /* 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!\n")); + return err; + } + + /* Fix the endianness to match the host. */ + descriptor->version = extract_unsigned_integer (&desc_buf[0], 4); + descriptor->action_flag = extract_unsigned_integer (&desc_buf[4], 4); + descriptor->relevant_entry = extract_typed_address (&desc_buf[8], ptr_type); + descriptor->first_entry = extract_typed_address + (&desc_buf[8 + ptr_size], ptr_type); + + return 0; +} + +/* Helper method for reading a JITed code entry from remote memory. */ + +static int +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; + + /* 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!\n")); + return err; + } + + /* 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); + + return 0; +} + +/* 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 jit_obj_link *link; + + /* 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); + err = target_read_memory (code_entry->symfile_addr, buffer, size); + if (err) + { + free (buffer); + error (_("Unable to read JITed symfile from remote memory.")); + return; + } + + /* 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); + if (objfile == NULL) + free (buffer); + + /* Remember a mapping from entry_addr to objfile. */ + link = xmalloc (sizeof (struct jit_obj_link)); + link->next = jit_objfiles; + link->entry_addr = entry_addr; + link->objfile = objfile; + jit_objfiles = link; +} + +/* This function unregisters JITed code and frees the corresponding objfile. */ + +static void +jit_unregister_code (struct jit_obj_link **last_ptr, struct jit_obj_link *link) +{ + /* Cut the link from the chain, and free both the link and the objfile. */ + *last_ptr = link->next; + free_objfile (link->objfile); + xfree (link); +} + +/* 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 == (CORE_ADDR)NULL) + 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 == (CORE_ADDR)NULL) + return; + + /* Read the descriptor so we can check the version number and load any already + JITed functions. */ + err = jit_read_descriptor (&descriptor); + if (err) return; + + /* Check that the version number agrees with that we support. */ + if (descriptor.version != 1) + { + error (_("Unsupported JIT protocol version in descriptor!\n")); + return; + } + + /* Put a breakpoint in the registration symbol. */ + create_jit_event_breakpoint (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) + { + err = jit_read_code_entry (cur_entry_addr, &cur_entry); + if (err) return; + 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) +{ + while (jit_objfiles != NULL) + { + jit_unregister_code (&jit_objfiles, jit_objfiles); + } +} + +void +jit_event_handler (void) +{ + struct jit_descriptor descriptor; + struct jit_code_entry code_entry; + int err; + CORE_ADDR entry_addr; + struct jit_obj_link *link; + struct jit_obj_link **last_ptr; + + /* 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: + err = jit_read_code_entry (entry_addr, &code_entry); + if (err) return; + jit_register_code (entry_addr, &code_entry); + break; + case JIT_UNREGISTER: + /* Look up the link with this code entry address. */ + for (last_ptr = &jit_objfiles, link = *last_ptr; link != NULL; + last_ptr = &link->next, link = *last_ptr) + { + if (link->entry_addr == entry_addr) + { + jit_unregister_code (last_ptr, link); + 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!\n")); + 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); +} 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 22 Jul 2009 22:26:08 -0000 @@ -0,0 +1,81 @@ +/* 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. */ + +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. + The list is singly linked. 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; +}; + +/* This struct is used to form a linked list mapping the address of the JIT code + entry in the inferior's address space to the corresponding object file in + GDB. These are used to unregister objfiles for code that has been freed. */ + +struct jit_obj_link +{ + struct jit_obj_link *next; + CORE_ADDR entry_addr; + struct objfile *objfile; +}; + +/* 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.83 diff -u -p -u -r1.83 objfiles.c --- gdb/objfiles.c 14 May 2009 23:33:08 -0000 1.83 +++ gdb/objfiles.c 22 Jul 2009 22:26: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" @@ -430,9 +431,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.17 diff -u -p -u -r1.17 symfile-mem.c --- gdb/symfile-mem.c 21 Feb 2009 16:14:49 -0000 1.17 +++ gdb/symfile-mem.c 22 Jul 2009 22:26:08 -0000 @@ -54,37 +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; - if (!bfd_check_format (nbfd, bfd_object)) { /* FIXME: should be checking for errors from bfd_close (for one thing, @@ -116,6 +102,121 @@ symbol_file_add_from_memory (struct bfd 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; + + /* The filename must be strdup'd or GDB will crash when freeing the object + file. */ + filename = xstrdup (""); + + /* 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 ())); + return NULL; + } + + /* We set loadbase to 0 and assume that the symbol file we're loading has the + absolute addresses set in the ELF. */ + loadbase = 0; + return symbol_file_add_from_memory_common (nbfd, loadbase, 0); +} + +/* 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 +230,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.50 diff -u -p -u -r1.50 symfile.h --- gdb/symfile.h 22 May 2009 23:49:14 -0000 1.50 +++ gdb/symfile.h 22 Jul 2009 22:26:08 -0000 @@ -392,4 +392,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) */