diff options
Diffstat (limited to 'gdb/progspace.c')
-rw-r--r-- | gdb/progspace.c | 625 |
1 files changed, 625 insertions, 0 deletions
diff --git a/gdb/progspace.c b/gdb/progspace.c new file mode 100644 index 0000000..1a465ce --- /dev/null +++ b/gdb/progspace.c @@ -0,0 +1,625 @@ +/* Program and address space management, 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 <http://www.gnu.org/licenses/>. */ + +#include "defs.h" +#include "gdbcmd.h" +#include "objfiles.h" +#include "arch-utils.h" +#include "gdbcore.h" +#include "solib.h" +#include "gdbthread.h" + +/* The last program space number assigned. */ +int last_program_space_num = 0; + +/* The head of the program spaces list. */ +struct program_space *program_spaces; + +/* Pointer to the current program space. */ +struct program_space *current_program_space; + +/* The last address space number assigned. */ +static int highest_address_space_num; + +/* Prototypes for local functions */ + +static void program_space_alloc_data (struct program_space *); +static void program_space_free_data (struct program_space *); + + +/* An address space. Currently this is not used for much other than + for comparing if pspaces/inferior/threads see the same address + space. */ + +struct address_space +{ + int num; +}; + +/* Create a new address space object, and add it to the list. */ + +struct address_space * +new_address_space (void) +{ + struct address_space *aspace; + + aspace = XZALLOC (struct address_space); + aspace->num = ++highest_address_space_num; + + return aspace; +} + +/* Maybe create a new address space object, and add it to the list, or + return a pointer to an existing address space, in case inferiors + share an address space on this target system. */ + +struct address_space * +maybe_new_address_space (void) +{ + int shared_aspace = gdbarch_has_shared_address_space (target_gdbarch); + + if (shared_aspace) + { + /* Just return the first in the list. */ + return program_spaces->aspace; + } + + return new_address_space (); +} + +static void +free_address_space (struct address_space *aspace) +{ + xfree (aspace); +} + +/* Start counting over from scratch. */ + +static void +init_address_spaces (void) +{ + highest_address_space_num = 0; +} + + + +/* Adds a new empty program space to the program space list, and binds + it to ASPACE. Returns the pointer to the new object. */ + +struct program_space * +add_program_space (struct address_space *aspace) +{ + struct program_space *pspace; + + pspace = XZALLOC (struct program_space); + + pspace->num = ++last_program_space_num; + pspace->aspace = aspace; + + program_space_alloc_data (pspace); + + pspace->next = program_spaces; + program_spaces = pspace; + + return pspace; +} + +/* Releases program space PSPACE, and all its contents (shared + libraries, objfiles, and any other references to the PSPACE in + other modules). It is an internal error to call this when PSPACE + is the current program space, since there should always be a + program space. */ + +static void +release_program_space (struct program_space *pspace) +{ + struct cleanup *old_chain = save_current_program_space (); + + gdb_assert (pspace != current_program_space); + + set_current_program_space (pspace); + + breakpoint_program_space_exit (pspace); + no_shared_libraries (NULL, 0); + exec_close (); + free_all_objfiles (); + if (!gdbarch_has_shared_address_space (target_gdbarch)) + free_address_space (pspace->aspace); + resize_section_table (&pspace->target_sections, + -resize_section_table (&pspace->target_sections, 0)); + /* Discard any data modules have associated with the PSPACE. */ + program_space_free_data (pspace); + xfree (pspace); + + do_cleanups (old_chain); +} + +/* Unlinks PSPACE from the pspace list, and releases it. */ + +void +remove_program_space (struct program_space *pspace) +{ + struct program_space *ss, **ss_link; + + ss = program_spaces; + ss_link = &program_spaces; + while (ss) + { + if (ss != pspace) + { + ss_link = &ss->next; + ss = *ss_link; + continue; + } + + *ss_link = ss->next; + release_program_space (ss); + ss = *ss_link; + } +} + +/* Copies program space SRC to DEST. Copies the main executable file, + and the main symbol file. Returns DEST. */ + +struct program_space * +clone_program_space (struct program_space *dest, struct program_space *src) +{ + struct program_space *new_pspace; + struct cleanup *old_chain; + + old_chain = save_current_program_space (); + + set_current_program_space (dest); + + if (src->ebfd != NULL) + exec_file_attach (bfd_get_filename (src->ebfd), 0); + + if (src->symfile_object_file != NULL) + symbol_file_add_main (src->symfile_object_file->name, 0); + + do_cleanups (old_chain); + return dest; +} + +/* Sets PSPACE as the current program space. It is the caller's + responsibility to make sure that the currently selected + inferior/thread matches the selected program space. */ + +void +set_current_program_space (struct program_space *pspace) +{ + if (current_program_space == pspace) + return; + + gdb_assert (pspace != NULL); + + current_program_space = pspace; + + /* Different symbols change our view of the frame chain. */ + reinit_frame_cache (); +} + +/* A cleanups callback, helper for save_current_program_space + below. */ + +static void +restore_program_space (void *arg) +{ + struct program_space *saved_pspace = arg; + set_current_program_space (saved_pspace); +} + +/* Save the current program space so that it may be restored by a later + call to do_cleanups. Returns the struct cleanup pointer needed for + later doing the cleanup. */ + +struct cleanup * +save_current_program_space (void) +{ + struct cleanup *old_chain = make_cleanup (restore_program_space, + current_program_space); + return old_chain; +} + +/* Find program space number NUM; returns NULL if not found. */ + +static struct program_space * +find_program_space_by_num (int num) +{ + struct program_space *pspace; + + ALL_PSPACES (pspace) + if (pspace->num == num) + return pspace; + + return NULL; +} + +/* Returns true iff there's no inferior bound to PSPACE. */ + +static int +pspace_empty_p (struct program_space *pspace) +{ + struct inferior *inf; + + if (find_inferior_for_program_space (pspace) != NULL) + return 0; + + return 1; +} + +/* Prune away automatically added program spaces that aren't required + anymore. */ + +void +prune_program_spaces (void) +{ + struct program_space *ss, **ss_link; + struct program_space *current = current_program_space; + + ss = program_spaces; + ss_link = &program_spaces; + while (ss) + { + if (ss == current || !pspace_empty_p (ss)) + { + ss_link = &ss->next; + ss = *ss_link; + continue; + } + + *ss_link = ss->next; + release_program_space (ss); + ss = *ss_link; + } +} + +/* Prints the list of program spaces and their details on UIOUT. If + REQUESTED is not -1, it's the ID of the pspace that should be + printed. Otherwise, all spaces are printed. */ + +static void +print_program_space (struct ui_out *uiout, int requested) +{ + struct program_space *pspace; + int count = 0; + struct cleanup *old_chain; + + /* Might as well prune away unneeded ones, so the user doesn't even + seem them. */ + prune_program_spaces (); + + /* Compute number of pspaces we will print. */ + ALL_PSPACES (pspace) + { + if (requested != -1 && pspace->num != requested) + continue; + + ++count; + } + + /* There should always be at least one. */ + gdb_assert (count > 0); + + old_chain = make_cleanup_ui_out_table_begin_end (uiout, 3, count, "pspaces"); + ui_out_table_header (uiout, 1, ui_left, "current", ""); + ui_out_table_header (uiout, 4, ui_left, "id", "Id"); + ui_out_table_header (uiout, 17, ui_left, "exec", "Executable"); + ui_out_table_body (uiout); + + ALL_PSPACES (pspace) + { + struct cleanup *chain2; + struct inferior *inf; + int printed_header; + + if (requested != -1 && requested != pspace->num) + continue; + + chain2 = make_cleanup_ui_out_tuple_begin_end (uiout, NULL); + + if (pspace == current_program_space) + ui_out_field_string (uiout, "current", "*"); + else + ui_out_field_skip (uiout, "current"); + + ui_out_field_int (uiout, "id", pspace->num); + + if (pspace->ebfd) + ui_out_field_string (uiout, "exec", + bfd_get_filename (pspace->ebfd)); + else + ui_out_field_skip (uiout, "exec"); + + /* Print extra info that doesn't really fit in tabular form. + Currently, we print the list of inferiors bound to a pspace. + There can be more than one inferior bound to the same pspace, + e.g., both parent/child inferiors in a vfork, or, on targets + that share pspaces between inferiors. */ + printed_header = 0; + for (inf = inferior_list; inf; inf = inf->next) + if (inf->pspace == pspace) + { + if (!printed_header) + { + printed_header = 1; + printf_filtered ("\n\tBound inferiors: ID %d (%s)", + inf->num, + target_pid_to_str (pid_to_ptid (inf->pid))); + } + else + printf_filtered (", ID %d (%s)", + inf->num, + target_pid_to_str (pid_to_ptid (inf->pid))); + } + + ui_out_text (uiout, "\n"); + do_cleanups (chain2); + } + + do_cleanups (old_chain); +} + +/* Boolean test for an already-known program space id. */ + +static int +valid_program_space_id (int num) +{ + struct program_space *pspace; + + ALL_PSPACES (pspace) + if (pspace->num == num) + return 1; + + return 0; +} + +/* If ARGS is NULL or empty, print information about all program + spaces. Otherwise, ARGS is a text representation of a LONG + indicating which the program space to print information about. */ + +static void +maintenance_info_program_spaces_command (char *args, int from_tty) +{ + int requested = -1; + + if (args && *args) + { + requested = parse_and_eval_long (args); + if (!valid_program_space_id (requested)) + error (_("program space ID %d not known."), requested); + } + + print_program_space (uiout, requested); +} + +/* Simply returns the count of program spaces. */ + +int +number_of_program_spaces (void) +{ + struct program_space *pspace; + int count = 0; + + ALL_PSPACES (pspace) + count++; + + return count; +} + +/* Update all program spaces matching to address spaces. The user may + have created several program spaces, and loaded executables into + them before connecting to the target interface that will create the + inferiors. All that happens before GDB has a chance to know if the + inferiors will share an address space or not. Call this after + having connected to the target interface and having fetched the + target description, to fixup the program/address spaces mappings. + + It is assumed that there are no bound inferiors yet, otherwise, + they'd be left with stale referenced to released aspaces. */ + +void +update_address_spaces (void) +{ + int shared_aspace = gdbarch_has_shared_address_space (target_gdbarch); + struct address_space *aspace = NULL; + struct program_space *pspace; + + init_address_spaces (); + + ALL_PSPACES (pspace) + { + free_address_space (pspace->aspace); + + if (shared_aspace) + { + if (aspace == NULL) + aspace = new_address_space (); + pspace->aspace = aspace; + } + else + pspace->aspace = new_address_space (); + } +} + +/* Save the current program space so that it may be restored by a later + call to do_cleanups. Returns the struct cleanup pointer needed for + later doing the cleanup. */ + +struct cleanup * +save_current_space_and_thread (void) +{ + struct cleanup *old_chain; + + /* If restoring to null thread, we need to restore the pspace as + well, hence, we need to save the current program space first. */ + old_chain = save_current_program_space (); + save_current_inferior (); + make_cleanup_restore_current_thread (); + + return old_chain; +} + +/* Switches full context to program space PSPACE. Switches to the + first thread found bound to PSPACE. */ + +void +switch_to_program_space_and_thread (struct program_space *pspace) +{ + struct inferior *inf; + + inf = find_inferior_for_program_space (pspace); + if (inf != NULL) + { + struct thread_info *tp; + + tp = any_live_thread_of_process (inf->pid); + if (tp != NULL) + { + switch_to_thread (tp->ptid); + /* Switching thread switches pspace implicitly. We're + done. */ + return; + } + } + + switch_to_thread (null_ptid); + set_current_program_space (pspace); +} + + + +/* Keep a registry of per-program_space data-pointers required by other GDB + modules. */ + +struct program_space_data +{ + unsigned index; + void (*cleanup) (struct program_space *, void *); +}; + +struct program_space_data_registration +{ + struct program_space_data *data; + struct program_space_data_registration *next; +}; + +struct program_space_data_registry +{ + struct program_space_data_registration *registrations; + unsigned num_registrations; +}; + +static struct program_space_data_registry program_space_data_registry + = { NULL, 0 }; + +const struct program_space_data * +register_program_space_data_with_cleanup + (void (*cleanup) (struct program_space *, void *)) +{ + struct program_space_data_registration **curr; + + /* Append new registration. */ + for (curr = &program_space_data_registry.registrations; + *curr != NULL; curr = &(*curr)->next); + + *curr = XMALLOC (struct program_space_data_registration); + (*curr)->next = NULL; + (*curr)->data = XMALLOC (struct program_space_data); + (*curr)->data->index = program_space_data_registry.num_registrations++; + (*curr)->data->cleanup = cleanup; + + return (*curr)->data; +} + +const struct program_space_data * +register_program_space_data (void) +{ + return register_program_space_data_with_cleanup (NULL); +} + +static void +program_space_alloc_data (struct program_space *pspace) +{ + gdb_assert (pspace->data == NULL); + pspace->num_data = program_space_data_registry.num_registrations; + pspace->data = XCALLOC (pspace->num_data, void *); +} + +static void +program_space_free_data (struct program_space *pspace) +{ + gdb_assert (pspace->data != NULL); + clear_program_space_data (pspace); + xfree (pspace->data); + pspace->data = NULL; +} + +void +clear_program_space_data (struct program_space *pspace) +{ + struct program_space_data_registration *registration; + int i; + + gdb_assert (pspace->data != NULL); + + for (registration = program_space_data_registry.registrations, i = 0; + i < pspace->num_data; + registration = registration->next, i++) + if (pspace->data[i] != NULL && registration->data->cleanup) + registration->data->cleanup (pspace, pspace->data[i]); + + memset (pspace->data, 0, pspace->num_data * sizeof (void *)); +} + +void +set_program_space_data (struct program_space *pspace, + const struct program_space_data *data, + void *value) +{ + gdb_assert (data->index < pspace->num_data); + pspace->data[data->index] = value; +} + +void * +program_space_data (struct program_space *pspace, const struct program_space_data *data) +{ + gdb_assert (data->index < pspace->num_data); + return pspace->data[data->index]; +} + + + +void +initialize_progspace (void) +{ + add_cmd ("program-spaces", class_maintenance, + maintenance_info_program_spaces_command, _("\ +Info about currently known program spaces."), + &maintenanceinfolist); + + /* There's always one program space. Note that this function isn't + an automatic _initialize_foo function, since other + _initialize_foo routines may need to install their per-pspace + data keys. We can only allocate a progspace when all those + modules have done that. Do this before + initialize_current_architecture, because that accesses exec_bfd, + which in turn dereferences current_program_space. */ + current_program_space = add_program_space (new_address_space ()); +} |