X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Ftarget.c;h=7e7e875e68c4a357160e687d23a05c7ab06175c0;hb=5018ce90c1205d79f29adf954b0fd5e613d08430;hp=cb89e750866556d15ffa618dd1e03d54527b767f;hpb=1fb77080fd74d11c0dbccf812ed98ffa0b3edc4e;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/target.c b/gdb/target.c index cb89e75086..7e7e875e68 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -1,6 +1,6 @@ /* Select target systems and architectures at runtime for GDB. - Copyright (C) 1990-2016 Free Software Foundation, Inc. + Copyright (C) 1990-2020 Free Software Foundation, Inc. Contributed by Cygnus Support. @@ -40,14 +40,15 @@ #include "inline-frame.h" #include "tracepoint.h" #include "gdb/fileio.h" -#include "agent.h" +#include "gdbsupport/agent.h" #include "auxv.h" #include "target-debug.h" #include "top.h" #include "event-top.h" #include - -static void target_info (char *, int); +#include "gdbsupport/byte-vector.h" +#include "terminal.h" +#include static void generic_tls_error (void) ATTRIBUTE_NORETURN; @@ -80,21 +81,9 @@ static int default_verify_memory (struct target_ops *self, const gdb_byte *data, CORE_ADDR memaddr, ULONGEST size); -static struct address_space *default_thread_address_space - (struct target_ops *self, ptid_t ptid); - static void tcomplain (void) ATTRIBUTE_NORETURN; -static int return_zero (struct target_ops *); - -static int return_zero_has_execution (struct target_ops *, ptid_t); - -static void target_command (char *, int); - -static struct target_ops *find_default_run_target (char *); - -static struct gdbarch *default_thread_architecture (struct target_ops *ops, - ptid_t ptid); +static struct target_ops *find_default_run_target (const char *); static int dummy_find_memory_regions (struct target_ops *self, find_memory_region_ftype ignore1, @@ -103,46 +92,46 @@ static int dummy_find_memory_regions (struct target_ops *self, static char *dummy_make_corefile_notes (struct target_ops *self, bfd *ignore1, int *ignore2); -static char *default_pid_to_str (struct target_ops *ops, ptid_t ptid); +static std::string default_pid_to_str (struct target_ops *ops, ptid_t ptid); static enum exec_direction_kind default_execution_direction (struct target_ops *self); -static struct target_ops debug_target; - -#include "target-delegates.c" - -static void init_dummy_target (void); +/* Mapping between target_info objects (which have address identity) + and corresponding open/factory function/callback. Each add_target + call adds one entry to this map, and registers a "target + TARGET_NAME" command that when invoked calls the factory registered + here. The target_info object is associated with the command via + the command's context. */ +static std::unordered_map + target_factories; -static void update_current_target (void); +/* The singleton debug target. */ -/* Vector of existing target structures. */ -typedef struct target_ops *target_ops_p; -DEF_VEC_P (target_ops_p); -static VEC (target_ops_p) *target_structs; +static struct target_ops *the_debug_target; -/* The initial current target, so that there is always a semi-valid - current target. */ +/* The target stack. */ -static struct target_ops dummy_target; +static target_stack g_target_stack; /* Top of target stack. */ - -static struct target_ops *target_stack; - /* The target structure we are currently using to talk to a process or file or whatever "inferior" we have. */ -struct target_ops current_target; +target_ops * +current_top_target () +{ + return g_target_stack.top (); +} /* Command list for target. */ static struct cmd_list_element *targetlist = NULL; -/* Nonzero if we should trust readonly sections from the +/* True if we should trust readonly sections from the executable when reading memory. */ -static int trust_readonly = 0; +static bool trust_readonly = false; /* Nonzero if we should show true memory content including memory breakpoint inserted by gdb. */ @@ -151,28 +140,31 @@ static int show_memory_breakpoints = 0; /* These globals control whether GDB attempts to perform these operations; they are useful for targets that need to prevent - inadvertant disruption, such as in non-stop mode. */ + inadvertent disruption, such as in non-stop mode. */ -int may_write_registers = 1; +bool may_write_registers = true; -int may_write_memory = 1; +bool may_write_memory = true; -int may_insert_breakpoints = 1; +bool may_insert_breakpoints = true; -int may_insert_tracepoints = 1; +bool may_insert_tracepoints = true; -int may_insert_fast_tracepoints = 1; +bool may_insert_fast_tracepoints = true; -int may_stop = 1; +bool may_stop = true; /* Non-zero if we want to see trace of target level stuff. */ static unsigned int targetdebug = 0; static void -set_targetdebug (char *args, int from_tty, struct cmd_list_element *c) +set_targetdebug (const char *args, int from_tty, struct cmd_list_element *c) { - update_current_target (); + if (targetdebug) + push_target (the_debug_target); + else + unpush_target (the_debug_target); } static void @@ -182,78 +174,20 @@ show_targetdebug (struct ui_file *file, int from_tty, fprintf_filtered (file, _("Target debugging is %s.\n"), value); } -static void setup_target_debug (void); - /* The user just typed 'target' without the name of a target. */ static void -target_command (char *arg, int from_tty) +target_command (const char *arg, int from_tty) { fputs_filtered ("Argument required (target name). Try `help target'\n", gdb_stdout); } -/* Default target_has_* methods for process_stratum targets. */ - -int -default_child_has_all_memory (struct target_ops *ops) -{ - /* If no inferior selected, then we can't read memory here. */ - if (ptid_equal (inferior_ptid, null_ptid)) - return 0; - - return 1; -} - -int -default_child_has_memory (struct target_ops *ops) -{ - /* If no inferior selected, then we can't read memory here. */ - if (ptid_equal (inferior_ptid, null_ptid)) - return 0; - - return 1; -} - -int -default_child_has_stack (struct target_ops *ops) -{ - /* If no inferior selected, there's no stack. */ - if (ptid_equal (inferior_ptid, null_ptid)) - return 0; - - return 1; -} - -int -default_child_has_registers (struct target_ops *ops) -{ - /* Can't read registers from no inferior. */ - if (ptid_equal (inferior_ptid, null_ptid)) - return 0; - - return 1; -} - -int -default_child_has_execution (struct target_ops *ops, ptid_t the_ptid) -{ - /* If there's no thread selected, then we can't make it run through - hoops. */ - if (ptid_equal (the_ptid, null_ptid)) - return 0; - - return 1; -} - - int target_has_all_memory_1 (void) { - struct target_ops *t; - - for (t = current_target.beneath; t != NULL; t = t->beneath) - if (t->to_has_all_memory (t)) + for (target_ops *t = current_top_target (); t != NULL; t = t->beneath ()) + if (t->has_all_memory ()) return 1; return 0; @@ -262,10 +196,8 @@ target_has_all_memory_1 (void) int target_has_memory_1 (void) { - struct target_ops *t; - - for (t = current_target.beneath; t != NULL; t = t->beneath) - if (t->to_has_memory (t)) + for (target_ops *t = current_top_target (); t != NULL; t = t->beneath ()) + if (t->has_memory ()) return 1; return 0; @@ -274,10 +206,8 @@ target_has_memory_1 (void) int target_has_stack_1 (void) { - struct target_ops *t; - - for (t = current_target.beneath; t != NULL; t = t->beneath) - if (t->to_has_stack (t)) + for (target_ops *t = current_top_target (); t != NULL; t = t->beneath ()) + if (t->has_stack ()) return 1; return 0; @@ -286,96 +216,61 @@ target_has_stack_1 (void) int target_has_registers_1 (void) { - struct target_ops *t; - - for (t = current_target.beneath; t != NULL; t = t->beneath) - if (t->to_has_registers (t)) + for (target_ops *t = current_top_target (); t != NULL; t = t->beneath ()) + if (t->has_registers ()) return 1; return 0; } -int -target_has_execution_1 (ptid_t the_ptid) +bool +target_has_execution_1 (inferior *inf) { - struct target_ops *t; - - for (t = current_target.beneath; t != NULL; t = t->beneath) - if (t->to_has_execution (t, the_ptid)) - return 1; + for (target_ops *t = current_top_target (); t != NULL; t = t->beneath ()) + if (t->has_execution (inf)) + return true; - return 0; + return false; } int target_has_execution_current (void) { - return target_has_execution_1 (inferior_ptid); -} - -/* Complete initialization of T. This ensures that various fields in - T are set, if needed by the target implementation. */ - -void -complete_target_initialization (struct target_ops *t) -{ - /* Provide default values for all "must have" methods. */ - - if (t->to_has_all_memory == NULL) - t->to_has_all_memory = return_zero; - - if (t->to_has_memory == NULL) - t->to_has_memory = return_zero; - - if (t->to_has_stack == NULL) - t->to_has_stack = return_zero; - - if (t->to_has_registers == NULL) - t->to_has_registers = return_zero; - - if (t->to_has_execution == NULL) - t->to_has_execution = return_zero_has_execution; - - /* These methods can be called on an unpushed target and so require - a default implementation if the target might plausibly be the - default run target. */ - gdb_assert (t->to_can_run == NULL || (t->to_can_async_p != NULL - && t->to_supports_non_stop != NULL)); - - install_delegators (t); + return target_has_execution_1 (current_inferior ()); } /* This is used to implement the various target commands. */ static void -open_target (char *args, int from_tty, struct cmd_list_element *command) +open_target (const char *args, int from_tty, struct cmd_list_element *command) { - struct target_ops *ops = (struct target_ops *) get_cmd_context (command); + auto *ti = static_cast (get_cmd_context (command)); + target_open_ftype *func = target_factories[ti]; if (targetdebug) - fprintf_unfiltered (gdb_stdlog, "-> %s->to_open (...)\n", - ops->to_shortname); + fprintf_unfiltered (gdb_stdlog, "-> %s->open (...)\n", + ti->shortname); - ops->to_open (args, from_tty); + func (args, from_tty); if (targetdebug) - fprintf_unfiltered (gdb_stdlog, "<- %s->to_open (%s, %d)\n", - ops->to_shortname, args, from_tty); + fprintf_unfiltered (gdb_stdlog, "<- %s->open (%s, %d)\n", + ti->shortname, args, from_tty); } -/* Add possible target architecture T to the list and add a new - command 'target T->to_shortname'. Set COMPLETER as the command's - completer if not NULL. */ +/* See target.h. */ void -add_target_with_completer (struct target_ops *t, - completer_ftype *completer) +add_target (const target_info &t, target_open_ftype *func, + completer_ftype *completer) { struct cmd_list_element *c; - complete_target_initialization (t); - - VEC_safe_push (target_ops_p, target_structs, t); + auto &func_slot = target_factories[&t]; + if (func_slot != nullptr) + internal_error (__FILE__, __LINE__, + _("target already added (\"%s\")."), t.shortname); + func_slot = func; if (targetlist == NULL) add_prefix_cmd ("target", class_run, target_command, _("\ @@ -385,35 +280,27 @@ Remaining arguments are interpreted by the target protocol. For more\n\ information on the arguments for a particular protocol, type\n\ `help target ' followed by the protocol name."), &targetlist, "target ", 0, &cmdlist); - c = add_cmd (t->to_shortname, no_class, NULL, t->to_doc, &targetlist); + c = add_cmd (t.shortname, no_class, t.doc, &targetlist); + set_cmd_context (c, (void *) &t); set_cmd_sfunc (c, open_target); - set_cmd_context (c, t); if (completer != NULL) set_cmd_completer (c, completer); } -/* Add a possible target architecture to the list. */ - -void -add_target (struct target_ops *t) -{ - add_target_with_completer (t, NULL); -} - /* See target.h. */ void -add_deprecated_target_alias (struct target_ops *t, char *alias) +add_deprecated_target_alias (const target_info &tinfo, const char *alias) { struct cmd_list_element *c; char *alt; /* If we use add_alias_cmd, here, we do not get the deprecated warning, see PR cli/15104. */ - c = add_cmd (alias, no_class, NULL, t->to_doc, &targetlist); + c = add_cmd (alias, no_class, tinfo.doc, &targetlist); set_cmd_sfunc (c, open_target); - set_cmd_context (c, t); - alt = xstrprintf ("target %s", t->to_shortname); + set_cmd_context (c, (void *) &tinfo); + alt = xstrprintf ("target %s", tinfo.shortname); deprecate_cmd (c, alt); } @@ -422,63 +309,35 @@ add_deprecated_target_alias (struct target_ops *t, char *alias) void target_kill (void) { - current_target.to_kill (¤t_target); + current_top_target ()->kill (); } void target_load (const char *arg, int from_tty) { target_dcache_invalidate (); - (*current_target.to_load) (¤t_target, arg, from_tty); + current_top_target ()->load (arg, from_tty); } -/* Possible terminal states. */ +/* Define it. */ -enum terminal_state - { - /* The inferior's terminal settings are in effect. */ - terminal_is_inferior = 0, - - /* Some of our terminal settings are in effect, enough to get - proper output. */ - terminal_is_ours_for_output = 1, - - /* Our terminal settings are in effect, for output and input. */ - terminal_is_ours = 2 - }; - -static enum terminal_state terminal_state = terminal_is_ours; +target_terminal_state target_terminal::m_terminal_state + = target_terminal_state::is_ours; -/* See target.h. */ +/* See target/target.h. */ void -target_terminal_init (void) -{ - (*current_target.to_terminal_init) (¤t_target); - - terminal_state = terminal_is_ours; -} - -/* See target.h. */ - -int -target_terminal_is_inferior (void) +target_terminal::init (void) { - return (terminal_state == terminal_is_inferior); -} - -/* See target.h. */ + current_top_target ()->terminal_init (); -int -target_terminal_is_ours (void) -{ - return (terminal_state == terminal_is_ours); + m_terminal_state = target_terminal_state::is_ours; } -/* See target.h. */ +/* See target/target.h. */ void -target_terminal_inferior (void) +target_terminal::inferior (void) { struct ui *ui = current_ui; @@ -489,18 +348,23 @@ target_terminal_inferior (void) /* Since we always run the inferior in the main console (unless "set inferior-tty" is in effect), when some UI other than the main one - calls target_terminal_inferior/target_terminal_inferior, then we - leave the main UI's terminal settings as is. */ + calls target_terminal::inferior, then we leave the main UI's + terminal settings as is. */ if (ui != main_ui) return; - if (terminal_state == terminal_is_inferior) - return; - /* If GDB is resuming the inferior in the foreground, install inferior's terminal modes. */ - (*current_target.to_terminal_inferior) (¤t_target); - terminal_state = terminal_is_inferior; + + struct inferior *inf = current_inferior (); + + if (inf->terminal_state != target_terminal_state::is_inferior) + { + current_top_target ()->terminal_inferior (); + inf->terminal_state = target_terminal_state::is_inferior; + } + + m_terminal_state = target_terminal_state::is_inferior; /* If the user hit C-c before, pretend that it was hit right here. */ @@ -508,97 +372,149 @@ target_terminal_inferior (void) target_pass_ctrlc (); } -/* See target.h. */ +/* See target/target.h. */ void -target_terminal_ours (void) +target_terminal::restore_inferior (void) { struct ui *ui = current_ui; - /* See target_terminal_inferior. */ - if (ui != main_ui) + /* See target_terminal::inferior(). */ + if (ui->prompt_state != PROMPT_BLOCKED || ui != main_ui) return; - if (terminal_state == terminal_is_ours) - return; + /* Restore the terminal settings of inferiors that were in the + foreground but are now ours_for_output due to a temporary + target_target::ours_for_output() call. */ + + { + scoped_restore_current_inferior restore_inferior; + + for (::inferior *inf : all_inferiors ()) + { + if (inf->terminal_state == target_terminal_state::is_ours_for_output) + { + set_current_inferior (inf); + current_top_target ()->terminal_inferior (); + inf->terminal_state = target_terminal_state::is_inferior; + } + } + } + + m_terminal_state = target_terminal_state::is_inferior; + + /* If the user hit C-c before, pretend that it was hit right + here. */ + if (check_quit_flag ()) + target_pass_ctrlc (); +} + +/* Switch terminal state to DESIRED_STATE, either is_ours, or + is_ours_for_output. */ + +static void +target_terminal_is_ours_kind (target_terminal_state desired_state) +{ + scoped_restore_current_inferior restore_inferior; + + /* Must do this in two passes. First, have all inferiors save the + current terminal settings. Then, after all inferiors have add a + chance to safely save the terminal settings, restore GDB's + terminal settings. */ - (*current_target.to_terminal_ours) (¤t_target); - terminal_state = terminal_is_ours; + for (inferior *inf : all_inferiors ()) + { + if (inf->terminal_state == target_terminal_state::is_inferior) + { + set_current_inferior (inf); + current_top_target ()->terminal_save_inferior (); + } + } + + for (inferior *inf : all_inferiors ()) + { + /* Note we don't check is_inferior here like above because we + need to handle 'is_ours_for_output -> is_ours' too. Careful + to never transition from 'is_ours' to 'is_ours_for_output', + though. */ + if (inf->terminal_state != target_terminal_state::is_ours + && inf->terminal_state != desired_state) + { + set_current_inferior (inf); + if (desired_state == target_terminal_state::is_ours) + current_top_target ()->terminal_ours (); + else if (desired_state == target_terminal_state::is_ours_for_output) + current_top_target ()->terminal_ours_for_output (); + else + gdb_assert_not_reached ("unhandled desired state"); + inf->terminal_state = desired_state; + } + } } -/* See target.h. */ +/* See target/target.h. */ void -target_terminal_ours_for_output (void) +target_terminal::ours () { struct ui *ui = current_ui; - /* See target_terminal_inferior. */ + /* See target_terminal::inferior. */ if (ui != main_ui) return; - if (terminal_state != terminal_is_inferior) + if (m_terminal_state == target_terminal_state::is_ours) return; - (*current_target.to_terminal_ours_for_output) (¤t_target); - terminal_state = terminal_is_ours_for_output; + + target_terminal_is_ours_kind (target_terminal_state::is_ours); + m_terminal_state = target_terminal_state::is_ours; } -/* See target.h. */ +/* See target/target.h. */ -int -target_supports_terminal_ours (void) +void +target_terminal::ours_for_output () { - struct target_ops *t; + struct ui *ui = current_ui; - for (t = current_target.beneath; t != NULL; t = t->beneath) - { - if (t->to_terminal_ours != delegate_terminal_ours - && t->to_terminal_ours != tdefault_terminal_ours) - return 1; - } + /* See target_terminal::inferior. */ + if (ui != main_ui) + return; - return 0; + if (!target_terminal::is_inferior ()) + return; + + target_terminal_is_ours_kind (target_terminal_state::is_ours_for_output); + target_terminal::m_terminal_state = target_terminal_state::is_ours_for_output; } -/* Restore the terminal to its previous state (helper for - make_cleanup_restore_target_terminal). */ +/* See target/target.h. */ -static void -cleanup_restore_target_terminal (void *arg) +void +target_terminal::info (const char *arg, int from_tty) { - enum terminal_state *previous_state = (enum terminal_state *) arg; - - switch (*previous_state) - { - case terminal_is_ours: - target_terminal_ours (); - break; - case terminal_is_ours_for_output: - target_terminal_ours_for_output (); - break; - case terminal_is_inferior: - target_terminal_inferior (); - break; - } + current_top_target ()->terminal_info (arg, from_tty); } -/* See target.h. */ +/* See target.h. */ -struct cleanup * -make_cleanup_restore_target_terminal (void) +bool +target_supports_terminal_ours (void) { - enum terminal_state *ts = XNEW (enum terminal_state); - - *ts = terminal_state; + /* This can be called before there is any target, so we must check + for nullptr here. */ + target_ops *top = current_top_target (); - return make_cleanup_dtor (cleanup_restore_target_terminal, ts, xfree); + if (top == nullptr) + return false; + return top->supports_terminal_ours (); } static void tcomplain (void) { error (_("You can't do that when your target is `%s'"), - current_target.to_shortname); + current_top_target ()->shortname ()); } void @@ -622,7 +538,7 @@ default_terminal_info (struct target_ops *self, const char *args, int from_tty) static ptid_t default_get_ada_task_ptid (struct target_ops *self, long lwp, long tid) { - return ptid_build (ptid_get_pid (inferior_ptid), lwp, tid); + return ptid_t (inferior_ptid.pid (), lwp, tid); } static enum exec_direction_kind @@ -637,148 +553,84 @@ default_execution_direction (struct target_ops *self) to_execution_direction must be implemented for reverse async"); } -/* Go through the target stack from top to bottom, copying over zero - entries in current_target, then filling in still empty entries. In - effect, we are doing class inheritance through the pushed target - vectors. - - NOTE: cagney/2003-10-17: The problem with this inheritance, as it - is currently implemented, is that it discards any knowledge of - which target an inherited method originally belonged to. - Consequently, new new target methods should instead explicitly and - locally search the target stack for the target that can handle the - request. */ +/* See target.h. */ -static void -update_current_target (void) +void +target_stack::push (target_ops *t) { - struct target_ops *t; - - /* First, reset current's contents. */ - memset (¤t_target, 0, sizeof (current_target)); - - /* Install the delegators. */ - install_delegators (¤t_target); - - current_target.to_stratum = target_stack->to_stratum; + /* If there's already a target at this stratum, remove it. */ + strata stratum = t->stratum (); -#define INHERIT(FIELD, TARGET) \ - if (!current_target.FIELD) \ - current_target.FIELD = (TARGET)->FIELD + if (m_stack[stratum] != NULL) + unpush (m_stack[stratum]); - /* Do not add any new INHERITs here. Instead, use the delegation - mechanism provided by make-target-delegates. */ - for (t = target_stack; t; t = t->beneath) - { - INHERIT (to_shortname, t); - INHERIT (to_longname, t); - INHERIT (to_attach_no_wait, t); - INHERIT (to_have_steppable_watchpoint, t); - INHERIT (to_have_continuable_watchpoint, t); - INHERIT (to_has_thread_control, t); - } -#undef INHERIT + /* Now add the new one. */ + m_stack[stratum] = t; - /* Finally, position the target-stack beneath the squashed - "current_target". That way code looking for a non-inherited - target method can quickly and simply find it. */ - current_target.beneath = target_stack; - - if (targetdebug) - setup_target_debug (); + if (m_top < stratum) + m_top = stratum; } -/* Push a new target type into the stack of the existing target accessors, - possibly superseding some of the existing accessors. - - Rather than allow an empty stack, we always have the dummy target at - the bottom stratum, so we can call the function vectors without - checking them. */ +/* See target.h. */ void push_target (struct target_ops *t) { - struct target_ops **cur; - - /* Check magic number. If wrong, it probably means someone changed - the struct definition, but not all the places that initialize one. */ - if (t->to_magic != OPS_MAGIC) - { - fprintf_unfiltered (gdb_stderr, - "Magic number of %s target struct wrong\n", - t->to_shortname); - internal_error (__FILE__, __LINE__, - _("failed internal consistency check")); - } - - /* Find the proper stratum to install this target in. */ - for (cur = &target_stack; (*cur) != NULL; cur = &(*cur)->beneath) - { - if ((int) (t->to_stratum) >= (int) (*cur)->to_stratum) - break; - } - - /* If there's already targets at this stratum, remove them. */ - /* FIXME: cagney/2003-10-15: I think this should be popping all - targets to CUR, and not just those at this stratum level. */ - while ((*cur) != NULL && t->to_stratum == (*cur)->to_stratum) - { - /* There's already something at this stratum level. Close it, - and un-hook it from the stack. */ - struct target_ops *tmp = (*cur); - - (*cur) = (*cur)->beneath; - tmp->beneath = NULL; - target_close (tmp); - } + g_target_stack.push (t); +} - /* We have removed all targets in our stratum, now add the new one. */ - t->beneath = (*cur); - (*cur) = t; +/* See target.h */ - update_current_target (); +void +push_target (target_ops_up &&t) +{ + g_target_stack.push (t.get ()); + t.release (); } -/* Remove a target_ops vector from the stack, wherever it may be. - Return how many times it was removed (0 or 1). */ +/* See target.h. */ int unpush_target (struct target_ops *t) { - struct target_ops **cur; - struct target_ops *tmp; + return g_target_stack.unpush (t); +} + +/* See target.h. */ + +bool +target_stack::unpush (target_ops *t) +{ + gdb_assert (t != NULL); - if (t->to_stratum == dummy_stratum) + strata stratum = t->stratum (); + + if (stratum == dummy_stratum) internal_error (__FILE__, __LINE__, _("Attempt to unpush the dummy target")); - /* Look for the specified target. Note that we assume that a target - can only occur once in the target stack. */ + /* Look for the specified target. Note that a target can only occur + once in the target stack. */ - for (cur = &target_stack; (*cur) != NULL; cur = &(*cur)->beneath) + if (m_stack[stratum] != t) { - if ((*cur) == t) - break; + /* If T wasn't pushed, quit. Only open targets should be + closed. */ + return false; } - /* If we don't find target_ops, quit. Only open targets should be - closed. */ - if ((*cur) == NULL) - return 0; - /* Unchain the target. */ - tmp = (*cur); - (*cur) = (*cur)->beneath; - tmp->beneath = NULL; + m_stack[stratum] = NULL; - update_current_target (); + if (m_top == stratum) + m_top = t->beneath ()->stratum (); /* Finally close the target. Note we do this after unchaining, so any target method calls from within the target_close implementation don't end up in T anymore. */ target_close (t); - return 1; + return true; } /* Unpush TARGET and assert that it worked. */ @@ -790,7 +642,7 @@ unpush_target_and_assert (struct target_ops *target) { fprintf_unfiltered (gdb_stderr, "pop_all_targets couldn't find target %s\n", - target->to_shortname); + target->shortname ()); internal_error (__FILE__, __LINE__, _("failed internal consistency check")); } @@ -799,8 +651,8 @@ unpush_target_and_assert (struct target_ops *target) void pop_all_targets_above (enum strata above_stratum) { - while ((int) (current_target.to_stratum) > (int) above_stratum) - unpush_target_and_assert (target_stack); + while ((int) (current_top_target ()->stratum ()) > (int) above_stratum) + unpush_target_and_assert (current_top_target ()); } /* See target.h. */ @@ -808,8 +660,8 @@ pop_all_targets_above (enum strata above_stratum) void pop_all_targets_at_and_above (enum strata stratum) { - while ((int) (current_target.to_stratum) >= (int) stratum) - unpush_target_and_assert (target_stack); + while ((int) (current_top_target ()->stratum ()) >= (int) stratum) + unpush_target_and_assert (current_top_target ()); } void @@ -823,24 +675,7 @@ pop_all_targets (void) int target_is_pushed (struct target_ops *t) { - struct target_ops *cur; - - /* Check magic number. If wrong, it probably means someone changed - the struct definition, but not all the places that initialize one. */ - if (t->to_magic != OPS_MAGIC) - { - fprintf_unfiltered (gdb_stderr, - "Magic number of %s target struct wrong\n", - t->to_shortname); - internal_error (__FILE__, __LINE__, - _("failed internal consistency check")); - } - - for (cur = target_stack; cur != NULL; cur = cur->beneath) - if (cur == t) - return 1; - - return 0; + return g_target_stack.is_pushed (t); } /* Default implementation of to_get_thread_local_address. */ @@ -858,26 +693,30 @@ CORE_ADDR target_translate_tls_address (struct objfile *objfile, CORE_ADDR offset) { volatile CORE_ADDR addr = 0; - struct target_ops *target = ¤t_target; + struct target_ops *target = current_top_target (); + struct gdbarch *gdbarch = target_gdbarch (); - if (gdbarch_fetch_tls_load_module_address_p (target_gdbarch ())) + if (gdbarch_fetch_tls_load_module_address_p (gdbarch)) { ptid_t ptid = inferior_ptid; - TRY + try { CORE_ADDR lm_addr; /* Fetch the load module address for this objfile. */ - lm_addr = gdbarch_fetch_tls_load_module_address (target_gdbarch (), + lm_addr = gdbarch_fetch_tls_load_module_address (gdbarch, objfile); - addr = target->to_get_thread_local_address (target, ptid, - lm_addr, offset); + if (gdbarch_get_thread_local_address_p (gdbarch)) + addr = gdbarch_get_thread_local_address (gdbarch, ptid, lm_addr, + offset); + else + addr = target->get_thread_local_address (ptid, lm_addr, offset); } /* If an error occurred, print TLS related messages here. Otherwise, throw the error to some higher catcher. */ - CATCH (ex, RETURN_MASK_ALL) + catch (const gdb_exception &ex) { int objfile_is_library = (objfile->flags & OBJF_SHARED); @@ -901,35 +740,34 @@ target_translate_tls_address (struct objfile *objfile, CORE_ADDR offset) " thread-local variables in\n" "the shared library `%s'\n" "for %s"), - objfile_name (objfile), target_pid_to_str (ptid)); + objfile_name (objfile), + target_pid_to_str (ptid).c_str ()); else error (_("The inferior has not yet allocated storage for" " thread-local variables in\n" "the executable `%s'\n" "for %s"), - objfile_name (objfile), target_pid_to_str (ptid)); + objfile_name (objfile), + target_pid_to_str (ptid).c_str ()); break; case TLS_GENERIC_ERROR: if (objfile_is_library) error (_("Cannot find thread-local storage for %s, " "shared library %s:\n%s"), - target_pid_to_str (ptid), - objfile_name (objfile), ex.message); + target_pid_to_str (ptid).c_str (), + objfile_name (objfile), ex.what ()); else error (_("Cannot find thread-local storage for %s, " "executable file %s:\n%s"), - target_pid_to_str (ptid), - objfile_name (objfile), ex.message); + target_pid_to_str (ptid).c_str (), + objfile_name (objfile), ex.what ()); break; default: - throw_exception (ex); + throw; break; } } - END_CATCH } - /* It wouldn't be wrong here to try a gdbarch method, too; finding - TLS is an ABI-specific thing. But we don't do that yet. */ else error (_("Cannot find thread-local variables on this target")); @@ -961,7 +799,8 @@ target_xfer_status_to_string (enum target_xfer_status status) read. */ int -target_read_string (CORE_ADDR memaddr, char **string, int len, int *errnop) +target_read_string (CORE_ADDR memaddr, gdb::unique_xmalloc_ptr *string, + int len, int *errnop) { int tlen, offset, i; gdb_byte buf[4]; @@ -1021,7 +860,7 @@ target_read_string (CORE_ADDR memaddr, char **string, int len, int *errnop) nbytes_read += tlen; } done: - *string = buffer; + string->reset (buffer); if (errnop != NULL) *errnop = errcode; return nbytes_read; @@ -1030,7 +869,7 @@ done: struct target_section_table * target_get_section_table (struct target_ops *target) { - return (*target->to_get_section_table) (target); + return target->get_section_table (); } /* Find a section containing ADDR. */ @@ -1118,9 +957,9 @@ raw_memory_xfer_partial (struct target_ops *ops, gdb_byte *readbuf, do { - res = ops->to_xfer_partial (ops, TARGET_OBJECT_MEMORY, NULL, - readbuf, writebuf, memaddr, len, - xfered_len); + res = ops->xfer_partial (TARGET_OBJECT_MEMORY, NULL, + readbuf, writebuf, memaddr, len, + xfered_len); if (res == TARGET_XFER_OK) break; @@ -1130,10 +969,10 @@ raw_memory_xfer_partial (struct target_ops *ops, gdb_byte *readbuf, /* We want to continue past core files to executables, but not past a running target's memory. */ - if (ops->to_has_all_memory (ops)) + if (ops->has_all_memory ()) break; - ops = ops->beneath; + ops = ops->beneath (); } while (ops != NULL); @@ -1143,7 +982,7 @@ raw_memory_xfer_partial (struct target_ops *ops, gdb_byte *readbuf, first, so that if it fails, we don't write to the cache contents that never made it to the target. */ if (writebuf != NULL - && !ptid_equal (inferior_ptid, null_ptid) + && inferior_ptid != null_ptid && target_dcache_init_p () && (stack_cache_enabled_p () || code_cache_enabled_p ())) { @@ -1199,9 +1038,7 @@ memory_xfer_partial_1 (struct target_ops *ops, enum target_object object, secp = target_section_by_addr (ops, memaddr); if (secp != NULL - && (bfd_get_section_flags (secp->the_bfd_section->owner, - secp->the_bfd_section) - & SEC_READONLY)) + && (bfd_section_flags (secp->the_bfd_section) & SEC_READONLY)) { table = target_get_section_table (ops); return section_table_xfer_memory_partial (readbuf, writebuf, @@ -1218,8 +1055,8 @@ memory_xfer_partial_1 (struct target_ops *ops, enum target_object object, ®ion)) return TARGET_XFER_E_IO; - if (!ptid_equal (inferior_ptid, null_ptid)) - inf = find_inferior_ptid (inferior_ptid); + if (inferior_ptid != null_ptid) + inf = current_inferior (); else inf = NULL; @@ -1271,6 +1108,8 @@ memory_xfer_partial (struct target_ops *ops, enum target_object object, if (len == 0) return TARGET_XFER_EOF; + memaddr = address_significant (target_gdbarch (), memaddr); + /* Fill in READBUF with breakpoint shadows, or WRITEBUF with breakpoint insns, thus hiding out from higher layers whether there are software breakpoints inserted in the code stream. */ @@ -1284,45 +1123,27 @@ memory_xfer_partial (struct target_ops *ops, enum target_object object, } else { - gdb_byte *buf; - struct cleanup *old_chain; - /* A large write request is likely to be partially satisfied by memory_xfer_partial_1. We will continually malloc and free a copy of the entire write request for breakpoint shadow handling even though we only end up writing a small subset of it. Cap writes to a limit specified by the target to mitigate this. */ - len = std::min (ops->to_get_memory_xfer_limit (ops), len); - - buf = (gdb_byte *) xmalloc (len); - old_chain = make_cleanup (xfree, buf); - memcpy (buf, writebuf, len); + len = std::min (ops->get_memory_xfer_limit (), len); - breakpoint_xfer_memory (NULL, buf, writebuf, memaddr, len); - res = memory_xfer_partial_1 (ops, object, NULL, buf, memaddr, len, + gdb::byte_vector buf (writebuf, writebuf + len); + breakpoint_xfer_memory (NULL, buf.data (), writebuf, memaddr, len); + res = memory_xfer_partial_1 (ops, object, NULL, buf.data (), memaddr, len, xfered_len); - - do_cleanups (old_chain); } return res; } -static void -restore_show_memory_breakpoints (void *arg) -{ - show_memory_breakpoints = (uintptr_t) arg; -} - -struct cleanup * -make_show_memory_breakpoints_cleanup (int show) +scoped_restore_tmpl +make_scoped_restore_show_memory_breakpoints (int show) { - int current = show_memory_breakpoints; - - show_memory_breakpoints = show; - return make_cleanup (restore_show_memory_breakpoints, - (void *) (uintptr_t) current); + return make_scoped_restore (&show_memory_breakpoints, show); } /* For docs see target.h, to_xfer_partial. */ @@ -1336,8 +1157,6 @@ target_xfer_partial (struct target_ops *ops, { enum target_xfer_status retval; - gdb_assert (ops->to_xfer_partial != NULL); - /* Transfer is done when LEN is zero. */ if (len == 0) return TARGET_XFER_EOF; @@ -1372,8 +1191,8 @@ target_xfer_partial (struct target_ops *ops, xfered_len); } else - retval = ops->to_xfer_partial (ops, object, annex, readbuf, - writebuf, offset, len, xfered_len); + retval = ops->xfer_partial (object, annex, readbuf, + writebuf, offset, len, xfered_len); if (targetdebug) { @@ -1382,7 +1201,7 @@ target_xfer_partial (struct target_ops *ops, fprintf_unfiltered (gdb_stdlog, "%s:target_xfer_partial " "(%d, %s, %s, %s, %s, %s) = %d, %s", - ops->to_shortname, + ops->shortname (), (int) object, (annex ? annex : "(null)"), host_address_to_string (readbuf), @@ -1442,10 +1261,7 @@ target_xfer_partial (struct target_ops *ops, int target_read_memory (CORE_ADDR memaddr, gdb_byte *myaddr, ssize_t len) { - /* Dispatch to the topmost target, not the flattened current_target. - Memory accesses check target->to_has_(all_)memory, and the - flattened target doesn't inherit those. */ - if (target_read (current_target.beneath, TARGET_OBJECT_MEMORY, NULL, + if (target_read (current_top_target (), TARGET_OBJECT_MEMORY, NULL, myaddr, memaddr, len) == len) return 0; else @@ -1475,9 +1291,7 @@ target_read_uint32 (CORE_ADDR memaddr, uint32_t *result) int target_read_raw_memory (CORE_ADDR memaddr, gdb_byte *myaddr, ssize_t len) { - /* See comment in target_read_memory about why the request starts at - current_target.beneath. */ - if (target_read (current_target.beneath, TARGET_OBJECT_RAW_MEMORY, NULL, + if (target_read (current_top_target (), TARGET_OBJECT_RAW_MEMORY, NULL, myaddr, memaddr, len) == len) return 0; else @@ -1490,9 +1304,7 @@ target_read_raw_memory (CORE_ADDR memaddr, gdb_byte *myaddr, ssize_t len) int target_read_stack (CORE_ADDR memaddr, gdb_byte *myaddr, ssize_t len) { - /* See comment in target_read_memory about why the request starts at - current_target.beneath. */ - if (target_read (current_target.beneath, TARGET_OBJECT_STACK_MEMORY, NULL, + if (target_read (current_top_target (), TARGET_OBJECT_STACK_MEMORY, NULL, myaddr, memaddr, len) == len) return 0; else @@ -1505,9 +1317,7 @@ target_read_stack (CORE_ADDR memaddr, gdb_byte *myaddr, ssize_t len) int target_read_code (CORE_ADDR memaddr, gdb_byte *myaddr, ssize_t len) { - /* See comment in target_read_memory about why the request starts at - current_target.beneath. */ - if (target_read (current_target.beneath, TARGET_OBJECT_CODE_MEMORY, NULL, + if (target_read (current_top_target (), TARGET_OBJECT_CODE_MEMORY, NULL, myaddr, memaddr, len) == len) return 0; else @@ -1523,9 +1333,7 @@ target_read_code (CORE_ADDR memaddr, gdb_byte *myaddr, ssize_t len) int target_write_memory (CORE_ADDR memaddr, const gdb_byte *myaddr, ssize_t len) { - /* See comment in target_read_memory about why the request starts at - current_target.beneath. */ - if (target_write (current_target.beneath, TARGET_OBJECT_MEMORY, NULL, + if (target_write (current_top_target (), TARGET_OBJECT_MEMORY, NULL, myaddr, memaddr, len) == len) return 0; else @@ -1541,9 +1349,7 @@ target_write_memory (CORE_ADDR memaddr, const gdb_byte *myaddr, ssize_t len) int target_write_raw_memory (CORE_ADDR memaddr, const gdb_byte *myaddr, ssize_t len) { - /* See comment in target_read_memory about why the request starts at - current_target.beneath. */ - if (target_write (current_target.beneath, TARGET_OBJECT_RAW_MEMORY, NULL, + if (target_write (current_top_target (), TARGET_OBJECT_RAW_MEMORY, NULL, myaddr, memaddr, len) == len) return 0; else @@ -1552,34 +1358,30 @@ target_write_raw_memory (CORE_ADDR memaddr, const gdb_byte *myaddr, ssize_t len) /* Fetch the target's memory map. */ -VEC(mem_region_s) * +std::vector target_memory_map (void) { - VEC(mem_region_s) *result; - struct mem_region *last_one, *this_one; - int ix; - result = current_target.to_memory_map (¤t_target); - if (result == NULL) - return NULL; + std::vector result = current_top_target ()->memory_map (); + if (result.empty ()) + return result; - qsort (VEC_address (mem_region_s, result), - VEC_length (mem_region_s, result), - sizeof (struct mem_region), mem_region_cmp); + std::sort (result.begin (), result.end ()); /* Check that regions do not overlap. Simultaneously assign a numbering for the "mem" commands to use to refer to each region. */ - last_one = NULL; - for (ix = 0; VEC_iterate (mem_region_s, result, ix, this_one); ix++) + mem_region *last_one = NULL; + for (size_t ix = 0; ix < result.size (); ix++) { + mem_region *this_one = &result[ix]; this_one->number = ix; - if (last_one && last_one->hi > this_one->lo) + if (last_one != NULL && last_one->hi > this_one->lo) { warning (_("Overlapping regions in memory map: ignoring")); - VEC_free (mem_region_s, result); - return NULL; + return std::vector (); } + last_one = this_one; } @@ -1589,13 +1391,13 @@ target_memory_map (void) void target_flash_erase (ULONGEST address, LONGEST length) { - current_target.to_flash_erase (¤t_target, address, length); + current_top_target ()->flash_erase (address, length); } void target_flash_done (void) { - current_target.to_flash_done (¤t_target); + current_top_target ()->flash_done (); } static void @@ -1701,43 +1503,37 @@ static void read_whatever_is_readable (struct target_ops *ops, const ULONGEST begin, const ULONGEST end, int unit_size, - VEC(memory_read_result_s) **result) + std::vector *result) { - gdb_byte *buf = (gdb_byte *) xmalloc (end - begin); ULONGEST current_begin = begin; ULONGEST current_end = end; int forward; - memory_read_result_s r; ULONGEST xfered_len; /* If we previously failed to read 1 byte, nothing can be done here. */ if (end - begin <= 1) - { - xfree (buf); - return; - } + return; + + gdb::unique_xmalloc_ptr buf ((gdb_byte *) xmalloc (end - begin)); /* Check that either first or the last byte is readable, and give up if not. This heuristic is meant to permit reading accessible memory at the boundary of accessible region. */ if (target_read_partial (ops, TARGET_OBJECT_MEMORY, NULL, - buf, begin, 1, &xfered_len) == TARGET_XFER_OK) + buf.get (), begin, 1, &xfered_len) == TARGET_XFER_OK) { forward = 1; ++current_begin; } else if (target_read_partial (ops, TARGET_OBJECT_MEMORY, NULL, - buf + (end - begin) - 1, end - 1, 1, + buf.get () + (end - begin) - 1, end - 1, 1, &xfered_len) == TARGET_XFER_OK) { forward = 0; --current_end; } else - { - xfree (buf); - return; - } + return; /* Loop invariant is that the [current_begin, current_end) was previously found to be not readable as a whole. @@ -1767,7 +1563,7 @@ read_whatever_is_readable (struct target_ops *ops, } xfer = target_read (ops, TARGET_OBJECT_MEMORY, NULL, - buf + (first_half_begin - begin) * unit_size, + buf.get () + (first_half_begin - begin) * unit_size, first_half_begin, first_half_end - first_half_begin); @@ -1794,50 +1590,30 @@ read_whatever_is_readable (struct target_ops *ops, if (forward) { /* The [begin, current_begin) range has been read. */ - r.begin = begin; - r.end = current_begin; - r.data = buf; + result->emplace_back (begin, current_end, std::move (buf)); } else { /* The [current_end, end) range has been read. */ LONGEST region_len = end - current_end; - r.data = (gdb_byte *) xmalloc (region_len * unit_size); - memcpy (r.data, buf + (current_end - begin) * unit_size, + gdb::unique_xmalloc_ptr data + ((gdb_byte *) xmalloc (region_len * unit_size)); + memcpy (data.get (), buf.get () + (current_end - begin) * unit_size, region_len * unit_size); - r.begin = current_end; - r.end = end; - xfree (buf); + result->emplace_back (current_end, end, std::move (data)); } - VEC_safe_push(memory_read_result_s, (*result), &r); } -void -free_memory_read_result_vector (void *x) +std::vector +read_memory_robust (struct target_ops *ops, + const ULONGEST offset, const LONGEST len) { - VEC(memory_read_result_s) **v = (VEC(memory_read_result_s) **) x; - memory_read_result_s *current; - int ix; + std::vector result; + int unit_size = gdbarch_addressable_memory_unit_size (target_gdbarch ()); - for (ix = 0; VEC_iterate (memory_read_result_s, *v, ix, current); ++ix) - { - xfree (current->data); - } - VEC_free (memory_read_result_s, *v); -} - -VEC(memory_read_result_s) * -read_memory_robust (struct target_ops *ops, - const ULONGEST offset, const LONGEST len) -{ - VEC(memory_read_result_s) *result = 0; - int unit_size = gdbarch_addressable_memory_unit_size (target_gdbarch ()); - struct cleanup *cleanup = make_cleanup (free_memory_read_result_vector, - &result); - - LONGEST xfered_total = 0; - while (xfered_total < len) + LONGEST xfered_total = 0; + while (xfered_total < len) { struct mem_region *region = lookup_mem_region (offset + xfered_total); LONGEST region_len; @@ -1860,19 +1636,17 @@ read_memory_robust (struct target_ops *ops, else { LONGEST to_read = std::min (len - xfered_total, region_len); - gdb_byte *buffer = (gdb_byte *) xmalloc (to_read * unit_size); - struct cleanup *inner_cleanup = make_cleanup (xfree, buffer); + gdb::unique_xmalloc_ptr buffer + ((gdb_byte *) xmalloc (to_read * unit_size)); LONGEST xfered_partial = - target_read (ops, TARGET_OBJECT_MEMORY, NULL, - (gdb_byte *) buffer, + target_read (ops, TARGET_OBJECT_MEMORY, NULL, buffer.get (), offset + xfered_total, to_read); /* Call an observer, notifying them of the xfer progress? */ if (xfered_partial <= 0) { /* Got an error reading full chunk. See if maybe we can read some subrange. */ - do_cleanups (inner_cleanup); read_whatever_is_readable (ops, offset + xfered_total, offset + xfered_total + to_read, unit_size, &result); @@ -1880,20 +1654,15 @@ read_memory_robust (struct target_ops *ops, } else { - struct memory_read_result r; - - discard_cleanups (inner_cleanup); - r.data = buffer; - r.begin = offset + xfered_total; - r.end = r.begin + xfered_partial; - VEC_safe_push (memory_read_result_s, result, &r); + result.emplace_back (offset + xfered_total, + offset + xfered_total + xfered_partial, + std::move (buffer)); xfered_total += xfered_partial; } QUIT; } } - discard_cleanups (cleanup); return result; } @@ -1956,18 +1725,17 @@ target_write (struct target_ops *ops, NULL, NULL); } -/* Read OBJECT/ANNEX using OPS. Store the result in *BUF_P and return - the size of the transferred data. PADDING additional bytes are - available in *BUF_P. This is a helper function for - target_read_alloc; see the declaration of that function for more - information. */ +/* Help for target_read_alloc and target_read_stralloc. See their comments + for details. */ -static LONGEST +template +gdb::optional> target_read_alloc_1 (struct target_ops *ops, enum target_object object, - const char *annex, gdb_byte **buf_p, int padding) + const char *annex) { - size_t buf_alloc, buf_pos; - gdb_byte *buf; + gdb::def_vector buf; + size_t buf_pos = 0; + const int chunk = 4096; /* This function does not have a length parameter; it reads the entire OBJECT). Also, it doesn't support objects fetched partly @@ -1978,86 +1746,64 @@ target_read_alloc_1 (struct target_ops *ops, enum target_object object, /* Start by reading up to 4K at a time. The target will throttle this number down if necessary. */ - buf_alloc = 4096; - buf = (gdb_byte *) xmalloc (buf_alloc); - buf_pos = 0; while (1) { ULONGEST xfered_len; enum target_xfer_status status; - status = target_read_partial (ops, object, annex, &buf[buf_pos], - buf_pos, buf_alloc - buf_pos - padding, + buf.resize (buf_pos + chunk); + + status = target_read_partial (ops, object, annex, + (gdb_byte *) &buf[buf_pos], + buf_pos, chunk, &xfered_len); if (status == TARGET_XFER_EOF) { /* Read all there was. */ - if (buf_pos == 0) - xfree (buf); - else - *buf_p = buf; - return buf_pos; + buf.resize (buf_pos); + return buf; } else if (status != TARGET_XFER_OK) { /* An error occurred. */ - xfree (buf); - return TARGET_XFER_E_IO; + return {}; } buf_pos += xfered_len; - /* If the buffer is filling up, expand it. */ - if (buf_alloc < buf_pos * 2) - { - buf_alloc *= 2; - buf = (gdb_byte *) xrealloc (buf, buf_alloc); - } - QUIT; } } -/* Read OBJECT/ANNEX using OPS. Store the result in *BUF_P and return - the size of the transferred data. See the declaration in "target.h" - function for more information about the return value. */ +/* See target.h */ -LONGEST +gdb::optional target_read_alloc (struct target_ops *ops, enum target_object object, - const char *annex, gdb_byte **buf_p) + const char *annex) { - return target_read_alloc_1 (ops, object, annex, buf_p, 0); + return target_read_alloc_1 (ops, object, annex); } -/* Read OBJECT/ANNEX using OPS. The result is NUL-terminated and - returned as a string, allocated using xmalloc. If an error occurs - or the transfer is unsupported, NULL is returned. Empty objects - are returned as allocated but empty strings. A warning is issued - if the result contains any embedded NUL bytes. */ +/* See target.h. */ -char * +gdb::optional target_read_stralloc (struct target_ops *ops, enum target_object object, const char *annex) { - gdb_byte *buffer; - char *bufstr; - LONGEST i, transferred; - - transferred = target_read_alloc_1 (ops, object, annex, &buffer, 1); - bufstr = (char *) buffer; - - if (transferred < 0) - return NULL; + gdb::optional buf + = target_read_alloc_1 (ops, object, annex); - if (transferred == 0) - return xstrdup (""); + if (!buf) + return {}; - bufstr[transferred] = 0; + if (buf->empty () || buf->back () != '\0') + buf->push_back ('\0'); /* Check for embedded NUL bytes; but allow trailing NULs. */ - for (i = strlen (bufstr); i < transferred; i++) - if (bufstr[i] != 0) + for (auto it = std::find (buf->begin (), buf->end (), '\0'); + it != buf->end (); it++) + if (*it != '\0') { warning (_("target object %d, annex %s, " "contained unexpected null characters"), @@ -2065,7 +1811,7 @@ target_read_stralloc (struct target_ops *ops, enum target_object object, break; } - return bufstr; + return buf; } /* Memory transfer methods. */ @@ -2106,8 +1852,7 @@ target_insert_breakpoint (struct gdbarch *gdbarch, return 1; } - return current_target.to_insert_breakpoint (¤t_target, - gdbarch, bp_tgt); + return current_top_target ()->insert_breakpoint (gdbarch, bp_tgt); } /* See target.h. */ @@ -2127,33 +1872,31 @@ target_remove_breakpoint (struct gdbarch *gdbarch, return 1; } - return current_target.to_remove_breakpoint (¤t_target, - gdbarch, bp_tgt, reason); + return current_top_target ()->remove_breakpoint (gdbarch, bp_tgt, reason); } static void -target_info (char *args, int from_tty) +info_target_command (const char *args, int from_tty) { - struct target_ops *t; int has_all_mem = 0; if (symfile_objfile != NULL) printf_unfiltered (_("Symbols from \"%s\".\n"), objfile_name (symfile_objfile)); - for (t = target_stack; t != NULL; t = t->beneath) + for (target_ops *t = current_top_target (); t != NULL; t = t->beneath ()) { - if (!(*t->to_has_memory) (t)) + if (!t->has_memory ()) continue; - if ((int) (t->to_stratum) <= (int) dummy_stratum) + if ((int) (t->stratum ()) <= (int) dummy_stratum) continue; if (has_all_mem) printf_unfiltered (_("\tWhile running this, " "GDB does not access memory from...\n")); - printf_unfiltered ("%s:\n", t->to_longname); - (t->to_files_info) (t); - has_all_mem = (*t->to_has_all_memory) (t); + printf_unfiltered ("%s:\n", t->longname ()); + t->files_info (); + has_all_mem = t->has_all_memory (); } } @@ -2210,18 +1953,22 @@ target_pre_inferior (int from_tty) static int dispose_inferior (struct inferior *inf, void *args) { - struct thread_info *thread; + /* Not all killed inferiors can, or will ever be, removed from the + inferior list. Killed inferiors clearly don't need to be killed + again, so, we're done. */ + if (inf->pid == 0) + return 0; - thread = any_thread_of_process (inf->pid); - if (thread) + thread_info *thread = any_thread_of_inferior (inf); + if (thread != NULL) { - switch_to_thread (thread->ptid); + switch_to_thread (thread); /* Core inferiors actually should be detached, not killed. */ if (target_has_execution) target_kill (); else - target_detach (NULL, 0); + target_detach (inf, 0); } return 0; @@ -2254,23 +2001,43 @@ target_preopen (int from_tty) target_pre_inferior (from_tty); } -/* Detach a target after doing deferred register stores. */ +/* See target.h. */ void -target_detach (const char *args, int from_tty) +target_detach (inferior *inf, int from_tty) { + /* After we have detached, we will clear the register cache for this inferior + by calling registers_changed_ptid. We must save the pid_ptid before + detaching, as the target detach method will clear inf->pid. */ + ptid_t save_pid_ptid = ptid_t (inf->pid); + + /* As long as some to_detach implementations rely on the current_inferior + (either directly, or indirectly, like through target_gdbarch or by + reading memory), INF needs to be the current inferior. When that + requirement will become no longer true, then we can remove this + assertion. */ + gdb_assert (inf == current_inferior ()); + if (gdbarch_has_global_breakpoints (target_gdbarch ())) /* Don't remove global breakpoints here. They're removed on disconnection from the target. */ ; else /* If we're in breakpoints-always-inserted mode, have to remove - them before detaching. */ - remove_breakpoints_pid (ptid_get_pid (inferior_ptid)); + breakpoints before detaching. */ + remove_breakpoints_inf (current_inferior ()); prepare_for_detach (); - current_target.to_detach (¤t_target, args, from_tty); + current_top_target ()->detach (inf, from_tty); + + registers_changed_ptid (save_pid_ptid); + + /* We have to ensure we have no frame cache left. Normally, + registers_changed_ptid (save_pid_ptid) calls reinit_frame_cache when + inferior_ptid matches save_pid_ptid, but in our case, it does not + call it, as inferior_ptid has been reset. */ + reinit_frame_cache (); } void @@ -2281,7 +2048,7 @@ target_disconnect (const char *args, int from_tty) disconnecting. */ remove_breakpoints (); - current_target.to_disconnect (¤t_target, args, from_tty); + current_top_target ()->disconnect (args, from_tty); } /* See target/target.h. */ @@ -2289,7 +2056,7 @@ target_disconnect (const char *args, int from_tty) ptid_t target_wait (ptid_t ptid, struct target_waitstatus *status, int options) { - return (current_target.to_wait) (¤t_target, ptid, status, options); + return current_top_target ()->wait (ptid, status, options); } /* See target.h. */ @@ -2303,16 +2070,33 @@ default_target_wait (struct target_ops *ops, return minus_one_ptid; } -char * +std::string target_pid_to_str (ptid_t ptid) { - return (*current_target.to_pid_to_str) (¤t_target, ptid); + return current_top_target ()->pid_to_str (ptid); } const char * target_thread_name (struct thread_info *info) { - return current_target.to_thread_name (¤t_target, info); + return current_top_target ()->thread_name (info); +} + +struct thread_info * +target_thread_handle_to_thread_info (const gdb_byte *thread_handle, + int handle_len, + struct inferior *inf) +{ + return current_top_target ()->thread_handle_to_thread_info (thread_handle, + handle_len, inf); +} + +/* See target.h. */ + +gdb::byte_vector +target_thread_info_to_thread_handle (struct thread_info *tip) +{ + return current_top_target ()->thread_info_to_thread_handle (tip); } void @@ -2320,26 +2104,48 @@ target_resume (ptid_t ptid, int step, enum gdb_signal signal) { target_dcache_invalidate (); - current_target.to_resume (¤t_target, ptid, step, signal); + current_top_target ()->resume (ptid, step, signal); registers_changed_ptid (ptid); /* We only set the internal executing state here. The user/frontend - running state is set at a higher level. */ + running state is set at a higher level. This also clears the + thread's stop_pc as side effect. */ set_executing (ptid, 1); clear_inline_frame_state (ptid); } +/* If true, target_commit_resume is a nop. */ +static int defer_target_commit_resume; + +/* See target.h. */ + void -target_pass_signals (int numsigs, unsigned char *pass_signals) +target_commit_resume (void) { - (*current_target.to_pass_signals) (¤t_target, numsigs, pass_signals); + if (defer_target_commit_resume) + return; + + current_top_target ()->commit_resume (); +} + +/* See target.h. */ + +scoped_restore_tmpl +make_scoped_defer_target_commit_resume () +{ + return make_scoped_restore (&defer_target_commit_resume, 1); +} + +void +target_pass_signals (gdb::array_view pass_signals) +{ + current_top_target ()->pass_signals (pass_signals); } void -target_program_signals (int numsigs, unsigned char *program_signals) +target_program_signals (gdb::array_view program_signals) { - (*current_target.to_program_signals) (¤t_target, - numsigs, program_signals); + current_top_target ()->program_signals (program_signals); } static int @@ -2357,16 +2163,15 @@ default_follow_fork (struct target_ops *self, int follow_child, int target_follow_fork (int follow_child, int detach_fork) { - return current_target.to_follow_fork (¤t_target, - follow_child, detach_fork); + return current_top_target ()->follow_fork (follow_child, detach_fork); } /* Target wrapper for follow exec hook. */ void -target_follow_exec (struct inferior *inf, char *execd_pathname) +target_follow_exec (struct inferior *inf, const char *execd_pathname) { - current_target.to_follow_exec (¤t_target, inf, execd_pathname); + current_top_target ()->follow_exec (inf, execd_pathname); } static void @@ -2379,8 +2184,8 @@ default_mourn_inferior (struct target_ops *self) void target_mourn_inferior (ptid_t ptid) { - gdb_assert (ptid_equal (ptid, inferior_ptid)); - current_target.to_mourn_inferior (¤t_target); + gdb_assert (ptid == inferior_ptid); + current_top_target ()->mourn_inferior (); /* We no longer need to keep handles on any of the object files. Make sure to release them to avoid unnecessarily locking any @@ -2394,7 +2199,7 @@ target_mourn_inferior (ptid_t ptid) const struct target_desc * target_read_description (struct target_ops *target) { - return target->to_read_description (target); + return target->read_description (); } /* This implements a basic search of memory, reading target memory and @@ -2411,9 +2216,7 @@ simple_search_memory (struct target_ops *ops, #define SEARCH_CHUNK_SIZE 16000 const unsigned chunk_size = SEARCH_CHUNK_SIZE; /* Buffer to hold memory contents for searching. */ - gdb_byte *search_buf; unsigned search_buf_size; - struct cleanup *old_cleanups; search_buf_size = chunk_size + pattern_len - 1; @@ -2421,20 +2224,17 @@ simple_search_memory (struct target_ops *ops, if (search_space_len < search_buf_size) search_buf_size = search_space_len; - search_buf = (gdb_byte *) malloc (search_buf_size); - if (search_buf == NULL) - error (_("Unable to allocate memory to perform the search.")); - old_cleanups = make_cleanup (free_current_contents, &search_buf); + gdb::byte_vector search_buf (search_buf_size); /* Prime the search buffer. */ if (target_read (ops, TARGET_OBJECT_MEMORY, NULL, - search_buf, start_addr, search_buf_size) != search_buf_size) + search_buf.data (), start_addr, search_buf_size) + != search_buf_size) { warning (_("Unable to access %s bytes of target " "memory at %s, halting search."), pulongest (search_buf_size), hex_string (start_addr)); - do_cleanups (old_cleanups); return -1; } @@ -2450,15 +2250,14 @@ simple_search_memory (struct target_ops *ops, unsigned nr_search_bytes = std::min (search_space_len, (ULONGEST) search_buf_size); - found_ptr = (gdb_byte *) memmem (search_buf, nr_search_bytes, + found_ptr = (gdb_byte *) memmem (search_buf.data (), nr_search_bytes, pattern, pattern_len); if (found_ptr != NULL) { - CORE_ADDR found_addr = start_addr + (found_ptr - search_buf); + CORE_ADDR found_addr = start_addr + (found_ptr - search_buf.data ()); *found_addrp = found_addr; - do_cleanups (old_cleanups); return 1; } @@ -2479,20 +2278,19 @@ simple_search_memory (struct target_ops *ops, /* Copy the trailing part of the previous iteration to the front of the buffer for the next iteration. */ gdb_assert (keep_len == pattern_len - 1); - memcpy (search_buf, search_buf + chunk_size, keep_len); + memcpy (&search_buf[0], &search_buf[chunk_size], keep_len); nr_to_read = std::min (search_space_len - keep_len, (ULONGEST) chunk_size); if (target_read (ops, TARGET_OBJECT_MEMORY, NULL, - search_buf + keep_len, read_addr, + &search_buf[keep_len], read_addr, nr_to_read) != nr_to_read) { warning (_("Unable to access %s bytes of target " "memory at %s, halting search."), plongest (nr_to_read), hex_string (read_addr)); - do_cleanups (old_cleanups); return -1; } @@ -2502,7 +2300,6 @@ simple_search_memory (struct target_ops *ops, /* Not found. */ - do_cleanups (old_cleanups); return 0; } @@ -2515,7 +2312,7 @@ default_search_memory (struct target_ops *self, CORE_ADDR *found_addrp) { /* Start over from the top of the target stack. */ - return simple_search_memory (current_target.beneath, + return simple_search_memory (current_top_target (), start_addr, search_space_len, pattern, pattern_len, found_addrp); } @@ -2532,9 +2329,8 @@ target_search_memory (CORE_ADDR start_addr, ULONGEST search_space_len, const gdb_byte *pattern, ULONGEST pattern_len, CORE_ADDR *found_addrp) { - return current_target.to_search_memory (¤t_target, start_addr, - search_space_len, - pattern, pattern_len, found_addrp); + return current_top_target ()->search_memory (start_addr, search_space_len, + pattern, pattern_len, found_addrp); } /* Look through the currently pushed targets. If none of them will @@ -2544,28 +2340,24 @@ target_search_memory (CORE_ADDR start_addr, ULONGEST search_space_len, void target_require_runnable (void) { - struct target_ops *t; - - for (t = target_stack; t != NULL; t = t->beneath) + for (target_ops *t = current_top_target (); t != NULL; t = t->beneath ()) { /* If this target knows how to create a new program, then assume we will still be able to after killing the current one. Either killing and mourning will not pop T, or else find_default_run_target will find it again. */ - if (t->to_create_inferior != NULL) + if (t->can_create_inferior ()) return; /* Do not worry about targets at certain strata that can not create inferiors. Assume they will be pushed again if necessary, and continue to the process_stratum. */ - if (t->to_stratum == thread_stratum - || t->to_stratum == record_stratum - || t->to_stratum == arch_stratum) + if (t->stratum () > process_stratum) continue; error (_("The \"%s\" target does not support \"run\". " "Try \"help target\" or \"continue\"."), - t->to_shortname); + t->shortname ()); } /* This function is only called if the target is running. In that @@ -2576,7 +2368,7 @@ target_require_runnable (void) /* Whether GDB is allowed to fall back to the default run target for "run", "attach", etc. when no target is connected yet. */ -static int auto_connect_native_target = 1; +static bool auto_connect_native_target = true; static void show_auto_connect_native_target (struct ui_file *file, int from_tty, @@ -2588,6 +2380,32 @@ show_auto_connect_native_target (struct ui_file *file, int from_tty, value); } +/* A pointer to the target that can respond to "run" or "attach". + Native targets are always singletons and instantiated early at GDB + startup. */ +static target_ops *the_native_target; + +/* See target.h. */ + +void +set_native_target (target_ops *target) +{ + if (the_native_target != NULL) + internal_error (__FILE__, __LINE__, + _("native target already set (\"%s\")."), + the_native_target->longname ()); + + the_native_target = target; +} + +/* See target.h. */ + +target_ops * +get_native_target () +{ + return the_native_target; +} + /* Look through the list of possible targets for a target that can execute a run or attach command without any other data. This is used to locate the default process stratum. @@ -2596,38 +2414,14 @@ show_auto_connect_native_target (struct ui_file *file, int from_tty, called for errors); else, return NULL on error. */ static struct target_ops * -find_default_run_target (char *do_mesg) +find_default_run_target (const char *do_mesg) { - struct target_ops *runable = NULL; - - if (auto_connect_native_target) - { - struct target_ops *t; - int count = 0; - int i; - - for (i = 0; VEC_iterate (target_ops_p, target_structs, i, t); ++i) - { - if (t->to_can_run != delegate_can_run && target_can_run (t)) - { - runable = t; - ++count; - } - } + if (auto_connect_native_target && the_native_target != NULL) + return the_native_target; - if (count != 1) - runable = NULL; - } - - if (runable == NULL) - { - if (do_mesg) - error (_("Don't know how to %s. Try \"help target\"."), do_mesg); - else - return NULL; - } - - return runable; + if (do_mesg != NULL) + error (_("Don't know how to %s. Try \"help target\"."), do_mesg); + return NULL; } /* See target.h. */ @@ -2635,20 +2429,15 @@ find_default_run_target (char *do_mesg) struct target_ops * find_attach_target (void) { - struct target_ops *t; - /* If a target on the current stack can attach, use it. */ - for (t = current_target.beneath; t != NULL; t = t->beneath) + for (target_ops *t = current_top_target (); t != NULL; t = t->beneath ()) { - if (t->to_attach != NULL) - break; + if (t->can_attach ()) + return t; } /* Otherwise, use the default run target for attaching. */ - if (t == NULL) - t = find_default_run_target ("attach"); - - return t; + return find_default_run_target ("attach"); } /* See target.h. */ @@ -2656,20 +2445,21 @@ find_attach_target (void) struct target_ops * find_run_target (void) { - struct target_ops *t; - - /* If a target on the current stack can attach, use it. */ - for (t = current_target.beneath; t != NULL; t = t->beneath) + /* If a target on the current stack can run, use it. */ + for (target_ops *t = current_top_target (); t != NULL; t = t->beneath ()) { - if (t->to_create_inferior != NULL) - break; + if (t->can_create_inferior ()) + return t; } /* Otherwise, use the default run target. */ - if (t == NULL) - t = find_default_run_target ("run"); + return find_default_run_target ("run"); +} - return t; +bool +target_ops::info_proc (const char *args, enum info_proc_what what) +{ + return false; } /* Implement the "info proc" command. */ @@ -2682,17 +2472,14 @@ target_info_proc (const char *args, enum info_proc_what what) /* If we're already connected to something that can get us OS related data, use it. Otherwise, try using the native target. */ - if (current_target.to_stratum >= process_stratum) - t = current_target.beneath; - else + t = find_target_at (process_stratum); + if (t == NULL) t = find_default_run_target (NULL); - for (; t != NULL; t = t->beneath) + for (; t != NULL; t = t->beneath ()) { - if (t->to_info_proc != NULL) + if (t->info_proc (args, what)) { - t->to_info_proc (t, args, what); - if (targetdebug) fprintf_unfiltered (gdb_stdlog, "target_info_proc (\"%s\", %d)\n", args, what); @@ -2710,21 +2497,15 @@ find_default_supports_disable_randomization (struct target_ops *self) struct target_ops *t; t = find_default_run_target (NULL); - if (t && t->to_supports_disable_randomization) - return (t->to_supports_disable_randomization) (t); + if (t != NULL) + return t->supports_disable_randomization (); return 0; } int target_supports_disable_randomization (void) { - struct target_ops *t; - - for (t = ¤t_target; t != NULL; t = t->beneath) - if (t->to_supports_disable_randomization) - return t->to_supports_disable_randomization (t); - - return 0; + return current_top_target ()->supports_disable_randomization (); } /* See target/target.h. */ @@ -2732,10 +2513,12 @@ target_supports_disable_randomization (void) int target_supports_multi_process (void) { - return (*current_target.to_supports_multi_process) (¤t_target); + return current_top_target ()->supports_multi_process (); } -char * +/* See target.h. */ + +gdb::optional target_get_osdata (const char *type) { struct target_ops *t; @@ -2743,33 +2526,16 @@ target_get_osdata (const char *type) /* If we're already connected to something that can get us OS related data, use it. Otherwise, try using the native target. */ - if (current_target.to_stratum >= process_stratum) - t = current_target.beneath; - else + t = find_target_at (process_stratum); + if (t == NULL) t = find_default_run_target ("get OS data"); if (!t) - return NULL; + return {}; return target_read_stralloc (t, TARGET_OBJECT_OSDATA, type); } -static struct address_space * -default_thread_address_space (struct target_ops *self, ptid_t ptid) -{ - struct inferior *inf; - - /* Fall-back to the "main" address space of the inferior. */ - inf = find_inferior_ptid (ptid); - - if (inf == NULL || inf->aspace == NULL) - internal_error (__FILE__, __LINE__, - _("Can't determine the current " - "address space of thread %s\n"), - target_pid_to_str (ptid)); - - return inf->aspace; -} /* Determine the current address space of thread PTID. */ @@ -2778,78 +2544,149 @@ target_thread_address_space (ptid_t ptid) { struct address_space *aspace; - aspace = current_target.to_thread_address_space (¤t_target, ptid); + aspace = current_top_target ()->thread_address_space (ptid); gdb_assert (aspace != NULL); return aspace; } +/* See target.h. */ + +target_ops * +target_ops::beneath () const +{ + return g_target_stack.find_beneath (this); +} + +void +target_ops::close () +{ +} + +bool +target_ops::can_attach () +{ + return 0; +} + +void +target_ops::attach (const char *, int) +{ + gdb_assert_not_reached ("target_ops::attach called"); +} + +bool +target_ops::can_create_inferior () +{ + return 0; +} + +void +target_ops::create_inferior (const char *, const std::string &, + char **, int) +{ + gdb_assert_not_reached ("target_ops::create_inferior called"); +} + +bool +target_ops::can_run () +{ + return false; +} + +int +target_can_run () +{ + for (target_ops *t = current_top_target (); t != NULL; t = t->beneath ()) + { + if (t->can_run ()) + return 1; + } + + return 0; +} /* Target file operations. */ static struct target_ops * default_fileio_target (void) { + struct target_ops *t; + /* If we're already connected to something that can perform file I/O, use it. Otherwise, try using the native target. */ - if (current_target.to_stratum >= process_stratum) - return current_target.beneath; - else - return find_default_run_target ("file I/O"); + t = find_target_at (process_stratum); + if (t != NULL) + return t; + return find_default_run_target ("file I/O"); } /* File handle for target file operations. */ -typedef struct +struct fileio_fh_t { - /* The target on which this file is open. */ - struct target_ops *t; + /* The target on which this file is open. NULL if the target is + meanwhile closed while the handle is open. */ + target_ops *target; /* The file descriptor on the target. */ - int fd; -} fileio_fh_t; + int target_fd; -DEF_VEC_O (fileio_fh_t); + /* Check whether this fileio_fh_t represents a closed file. */ + bool is_closed () + { + return target_fd < 0; + } +}; /* Vector of currently open file handles. The value returned by target_fileio_open and passed as the FD argument to other target_fileio_* functions is an index into this vector. This vector's entries are never freed; instead, files are marked as closed, and the handle becomes available for reuse. */ -static VEC (fileio_fh_t) *fileio_fhandles; - -/* Macro to check whether a fileio_fh_t represents a closed file. */ -#define is_closed_fileio_fh(fd) ((fd) < 0) +static std::vector fileio_fhandles; /* Index into fileio_fhandles of the lowest handle that might be closed. This permits handle reuse without searching the whole list each time a new file is opened. */ static int lowest_closed_fd; -/* Acquire a target fileio file descriptor. */ +/* Invalidate the target associated with open handles that were open + on target TARG, since we're about to close (and maybe destroy) the + target. The handles remain open from the client's perspective, but + trying to do anything with them other than closing them will fail + with EIO. */ -static int -acquire_fileio_fd (struct target_ops *t, int fd) +static void +fileio_handles_invalidate_target (target_ops *targ) { - fileio_fh_t *fh; + for (fileio_fh_t &fh : fileio_fhandles) + if (fh.target == targ) + fh.target = NULL; +} - gdb_assert (!is_closed_fileio_fh (fd)); +/* Acquire a target fileio file descriptor. */ +static int +acquire_fileio_fd (target_ops *target, int target_fd) +{ /* Search for closed handles to reuse. */ - for (; - VEC_iterate (fileio_fh_t, fileio_fhandles, - lowest_closed_fd, fh); - lowest_closed_fd++) - if (is_closed_fileio_fh (fh->fd)) - break; + for (; lowest_closed_fd < fileio_fhandles.size (); lowest_closed_fd++) + { + fileio_fh_t &fh = fileio_fhandles[lowest_closed_fd]; + + if (fh.is_closed ()) + break; + } /* Push a new handle if no closed handles were found. */ - if (lowest_closed_fd == VEC_length (fileio_fh_t, fileio_fhandles)) - fh = VEC_safe_push (fileio_fh_t, fileio_fhandles, NULL); + if (lowest_closed_fd == fileio_fhandles.size ()) + fileio_fhandles.push_back (fileio_fh_t {target, target_fd}); + else + fileio_fhandles[lowest_closed_fd] = {target, target_fd}; - /* Fill in the handle. */ - fh->t = t; - fh->fd = fd; + /* Should no longer be marked closed. */ + gdb_assert (!fileio_fhandles[lowest_closed_fd].is_closed ()); /* Return its index, and start the next lookup at the next index. */ @@ -2861,14 +2698,78 @@ acquire_fileio_fd (struct target_ops *t, int fd) static void release_fileio_fd (int fd, fileio_fh_t *fh) { - fh->fd = -1; + fh->target_fd = -1; lowest_closed_fd = std::min (lowest_closed_fd, fd); } /* Return a pointer to the fileio_fhandle_t corresponding to FD. */ -#define fileio_fd_to_fh(fd) \ - VEC_index (fileio_fh_t, fileio_fhandles, (fd)) +static fileio_fh_t * +fileio_fd_to_fh (int fd) +{ + return &fileio_fhandles[fd]; +} + + +/* Default implementations of file i/o methods. We don't want these + to delegate automatically, because we need to know which target + supported the method, in order to call it directly from within + pread/pwrite, etc. */ + +int +target_ops::fileio_open (struct inferior *inf, const char *filename, + int flags, int mode, int warn_if_slow, + int *target_errno) +{ + *target_errno = FILEIO_ENOSYS; + return -1; +} + +int +target_ops::fileio_pwrite (int fd, const gdb_byte *write_buf, int len, + ULONGEST offset, int *target_errno) +{ + *target_errno = FILEIO_ENOSYS; + return -1; +} + +int +target_ops::fileio_pread (int fd, gdb_byte *read_buf, int len, + ULONGEST offset, int *target_errno) +{ + *target_errno = FILEIO_ENOSYS; + return -1; +} + +int +target_ops::fileio_fstat (int fd, struct stat *sb, int *target_errno) +{ + *target_errno = FILEIO_ENOSYS; + return -1; +} + +int +target_ops::fileio_close (int fd, int *target_errno) +{ + *target_errno = FILEIO_ENOSYS; + return -1; +} + +int +target_ops::fileio_unlink (struct inferior *inf, const char *filename, + int *target_errno) +{ + *target_errno = FILEIO_ENOSYS; + return -1; +} + +gdb::optional +target_ops::fileio_readlink (struct inferior *inf, const char *filename, + int *target_errno) +{ + *target_errno = FILEIO_ENOSYS; + return {}; +} /* Helper for target_fileio_open and target_fileio_open_warn_if_slow. */ @@ -2878,30 +2779,28 @@ target_fileio_open_1 (struct inferior *inf, const char *filename, int flags, int mode, int warn_if_slow, int *target_errno) { - struct target_ops *t; - - for (t = default_fileio_target (); t != NULL; t = t->beneath) + for (target_ops *t = default_fileio_target (); t != NULL; t = t->beneath ()) { - if (t->to_fileio_open != NULL) - { - int fd = t->to_fileio_open (t, inf, filename, flags, mode, - warn_if_slow, target_errno); + int fd = t->fileio_open (inf, filename, flags, mode, + warn_if_slow, target_errno); - if (fd < 0) - fd = -1; - else - fd = acquire_fileio_fd (t, fd); + if (fd == -1 && *target_errno == FILEIO_ENOSYS) + continue; - if (targetdebug) - fprintf_unfiltered (gdb_stdlog, + if (fd < 0) + fd = -1; + else + fd = acquire_fileio_fd (t, fd); + + if (targetdebug) + fprintf_unfiltered (gdb_stdlog, "target_fileio_open (%d,%s,0x%x,0%o,%d)" " = %d (%d)\n", inf == NULL ? 0 : inf->num, filename, flags, mode, warn_if_slow, fd, fd != -1 ? 0 : *target_errno); - return fd; - } + return fd; } *target_errno = FILEIO_ENOSYS; @@ -2938,11 +2837,13 @@ target_fileio_pwrite (int fd, const gdb_byte *write_buf, int len, fileio_fh_t *fh = fileio_fd_to_fh (fd); int ret = -1; - if (is_closed_fileio_fh (fh->fd)) + if (fh->is_closed ()) *target_errno = EBADF; + else if (fh->target == NULL) + *target_errno = EIO; else - ret = fh->t->to_fileio_pwrite (fh->t, fh->fd, write_buf, - len, offset, target_errno); + ret = fh->target->fileio_pwrite (fh->target_fd, write_buf, + len, offset, target_errno); if (targetdebug) fprintf_unfiltered (gdb_stdlog, @@ -2962,11 +2863,13 @@ target_fileio_pread (int fd, gdb_byte *read_buf, int len, fileio_fh_t *fh = fileio_fd_to_fh (fd); int ret = -1; - if (is_closed_fileio_fh (fh->fd)) + if (fh->is_closed ()) *target_errno = EBADF; + else if (fh->target == NULL) + *target_errno = EIO; else - ret = fh->t->to_fileio_pread (fh->t, fh->fd, read_buf, - len, offset, target_errno); + ret = fh->target->fileio_pread (fh->target_fd, read_buf, + len, offset, target_errno); if (targetdebug) fprintf_unfiltered (gdb_stdlog, @@ -2985,10 +2888,12 @@ target_fileio_fstat (int fd, struct stat *sb, int *target_errno) fileio_fh_t *fh = fileio_fd_to_fh (fd); int ret = -1; - if (is_closed_fileio_fh (fh->fd)) + if (fh->is_closed ()) *target_errno = EBADF; + else if (fh->target == NULL) + *target_errno = EIO; else - ret = fh->t->to_fileio_fstat (fh->t, fh->fd, sb, target_errno); + ret = fh->target->fileio_fstat (fh->target_fd, sb, target_errno); if (targetdebug) fprintf_unfiltered (gdb_stdlog, @@ -3005,11 +2910,15 @@ target_fileio_close (int fd, int *target_errno) fileio_fh_t *fh = fileio_fd_to_fh (fd); int ret = -1; - if (is_closed_fileio_fh (fh->fd)) + if (fh->is_closed ()) *target_errno = EBADF; else { - ret = fh->t->to_fileio_close (fh->t, fh->fd, target_errno); + if (fh->target != NULL) + ret = fh->target->fileio_close (fh->target_fd, + target_errno); + else + ret = 0; release_fileio_fd (fd, fh); } @@ -3026,23 +2935,20 @@ int target_fileio_unlink (struct inferior *inf, const char *filename, int *target_errno) { - struct target_ops *t; - - for (t = default_fileio_target (); t != NULL; t = t->beneath) + for (target_ops *t = default_fileio_target (); t != NULL; t = t->beneath ()) { - if (t->to_fileio_unlink != NULL) - { - int ret = t->to_fileio_unlink (t, inf, filename, - target_errno); + int ret = t->fileio_unlink (inf, filename, target_errno); - if (targetdebug) - fprintf_unfiltered (gdb_stdlog, - "target_fileio_unlink (%d,%s)" - " = %d (%d)\n", - inf == NULL ? 0 : inf->num, filename, - ret, ret != -1 ? 0 : *target_errno); - return ret; - } + if (ret == -1 && *target_errno == FILEIO_ENOSYS) + continue; + + if (targetdebug) + fprintf_unfiltered (gdb_stdlog, + "target_fileio_unlink (%d,%s)" + " = %d (%d)\n", + inf == NULL ? 0 : inf->num, filename, + ret, ret != -1 ? 0 : *target_errno); + return ret; } *target_errno = FILEIO_ENOSYS; @@ -3051,42 +2957,62 @@ target_fileio_unlink (struct inferior *inf, const char *filename, /* See target.h. */ -char * +gdb::optional target_fileio_readlink (struct inferior *inf, const char *filename, int *target_errno) { - struct target_ops *t; - - for (t = default_fileio_target (); t != NULL; t = t->beneath) + for (target_ops *t = default_fileio_target (); t != NULL; t = t->beneath ()) { - if (t->to_fileio_readlink != NULL) - { - char *ret = t->to_fileio_readlink (t, inf, filename, - target_errno); + gdb::optional ret + = t->fileio_readlink (inf, filename, target_errno); - if (targetdebug) - fprintf_unfiltered (gdb_stdlog, - "target_fileio_readlink (%d,%s)" - " = %s (%d)\n", - inf == NULL ? 0 : inf->num, - filename, ret? ret : "(nil)", - ret? 0 : *target_errno); - return ret; - } + if (!ret.has_value () && *target_errno == FILEIO_ENOSYS) + continue; + + if (targetdebug) + fprintf_unfiltered (gdb_stdlog, + "target_fileio_readlink (%d,%s)" + " = %s (%d)\n", + inf == NULL ? 0 : inf->num, + filename, ret ? ret->c_str () : "(nil)", + ret ? 0 : *target_errno); + return ret; } *target_errno = FILEIO_ENOSYS; - return NULL; + return {}; } -static void -target_fileio_close_cleanup (void *opaque) +/* Like scoped_fd, but specific to target fileio. */ + +class scoped_target_fd { - int fd = *(int *) opaque; - int target_errno; +public: + explicit scoped_target_fd (int fd) noexcept + : m_fd (fd) + { + } - target_fileio_close (fd, &target_errno); -} + ~scoped_target_fd () + { + if (m_fd >= 0) + { + int target_errno; + + target_fileio_close (m_fd, &target_errno); + } + } + + DISABLE_COPY_AND_ASSIGN (scoped_target_fd); + + int get () const noexcept + { + return m_fd; + } + +private: + int m_fd; +}; /* Read target file FILENAME, in the filesystem as seen by INF. If INF is NULL, use the filesystem seen by the debugger (GDB or, for @@ -3100,20 +3026,16 @@ static LONGEST target_fileio_read_alloc_1 (struct inferior *inf, const char *filename, gdb_byte **buf_p, int padding) { - struct cleanup *close_cleanup; size_t buf_alloc, buf_pos; gdb_byte *buf; LONGEST n; - int fd; int target_errno; - fd = target_fileio_open (inf, filename, FILEIO_O_RDONLY, 0700, - &target_errno); - if (fd == -1) + scoped_target_fd fd (target_fileio_open (inf, filename, FILEIO_O_RDONLY, + 0700, &target_errno)); + if (fd.get () == -1) return -1; - close_cleanup = make_cleanup (target_fileio_close_cleanup, &fd); - /* Start by reading up to 4K at a time. The target will throttle this number down if necessary. */ buf_alloc = 4096; @@ -3121,20 +3043,18 @@ target_fileio_read_alloc_1 (struct inferior *inf, const char *filename, buf_pos = 0; while (1) { - n = target_fileio_pread (fd, &buf[buf_pos], + n = target_fileio_pread (fd.get (), &buf[buf_pos], buf_alloc - buf_pos - padding, buf_pos, &target_errno); if (n < 0) { /* An error occurred. */ - do_cleanups (close_cleanup); xfree (buf); return -1; } else if (n == 0) { /* Read all there was. */ - do_cleanups (close_cleanup); if (buf_pos == 0) xfree (buf); else @@ -3166,7 +3086,7 @@ target_fileio_read_alloc (struct inferior *inf, const char *filename, /* See target.h. */ -char * +gdb::unique_xmalloc_ptr target_fileio_read_stralloc (struct inferior *inf, const char *filename) { gdb_byte *buffer; @@ -3177,10 +3097,10 @@ target_fileio_read_stralloc (struct inferior *inf, const char *filename) bufstr = (char *) buffer; if (transferred < 0) - return NULL; + return gdb::unique_xmalloc_ptr (nullptr); if (transferred == 0) - return xstrdup (""); + return make_unique_xstrdup (""); bufstr[transferred] = 0; @@ -3194,7 +3114,7 @@ target_fileio_read_stralloc (struct inferior *inf, const char *filename) break; } - return bufstr; + return gdb::unique_xmalloc_ptr (bufstr); } @@ -3213,32 +3133,17 @@ default_watchpoint_addr_within_range (struct target_ops *target, return addr >= start && addr < start + length; } -static struct gdbarch * -default_thread_architecture (struct target_ops *ops, ptid_t ptid) -{ - return target_gdbarch (); -} +/* See target.h. */ -static int -return_zero (struct target_ops *ignore) +target_ops * +target_stack::find_beneath (const target_ops *t) const { - return 0; -} + /* Look for a non-empty slot at stratum levels beneath T's. */ + for (int stratum = t->stratum () - 1; stratum >= 0; --stratum) + if (m_stack[stratum] != NULL) + return m_stack[stratum]; -static int -return_zero_has_execution (struct target_ops *ignore, ptid_t ignore2) -{ - return 0; -} - -/* - * Find the next target down the stack from the specified target. - */ - -struct target_ops * -find_target_beneath (struct target_ops *t) -{ - return t->beneath; + return NULL; } /* See target.h. */ @@ -3246,13 +3151,7 @@ find_target_beneath (struct target_ops *t) struct target_ops * find_target_at (enum strata stratum) { - struct target_ops *t; - - for (t = current_target.beneath; t != NULL; t = t->beneath) - if (t->to_stratum == stratum) - return t; - - return NULL; + return g_target_stack.at (stratum); } @@ -3263,7 +3162,7 @@ void target_announce_detach (int from_tty) { pid_t pid; - char *exec_file; + const char *exec_file; if (!from_tty) return; @@ -3272,10 +3171,9 @@ target_announce_detach (int from_tty) if (exec_file == NULL) exec_file = ""; - pid = ptid_get_pid (inferior_ptid); + pid = inferior_ptid.pid (); printf_unfiltered (_("Detaching from program: %s, %s\n"), exec_file, - target_pid_to_str (pid_to_ptid (pid))); - gdb_flush (gdb_stdout); + target_pid_to_str (ptid_t (pid)).c_str ()); } /* The inferior process has died. Long live the inferior! */ @@ -3283,9 +3181,8 @@ target_announce_detach (int from_tty) void generic_mourn_inferior (void) { - ptid_t ptid; + inferior *inf = current_inferior (); - ptid = inferior_ptid; inferior_ptid = null_ptid; /* Mark breakpoints uninserted in case something tries to delete a @@ -3293,11 +3190,8 @@ generic_mourn_inferior (void) fail, since the inferior is long gone). */ mark_breakpoints_out (); - if (!ptid_equal (ptid, null_ptid)) - { - int pid = ptid_get_pid (ptid); - exit_inferior (pid); - } + if (inf->pid != 0) + exit_inferior (inf); /* Note this wipes step-resume breakpoints, so needs to be done after exit_inferior, which ends up referencing the step-resume @@ -3316,16 +3210,13 @@ generic_mourn_inferior (void) /* Convert a normal process ID to a string. Returns the string in a static buffer. */ -char * +std::string normal_pid_to_str (ptid_t ptid) { - static char buf[32]; - - xsnprintf (buf, sizeof buf, "process %d", ptid_get_pid (ptid)); - return buf; + return string_printf ("process %d", ptid.pid ()); } -static char * +static std::string default_pid_to_str (struct target_ops *ops, ptid_t ptid) { return normal_pid_to_str (ptid); @@ -3349,27 +3240,43 @@ dummy_make_corefile_notes (struct target_ops *self, return NULL; } -/* Set up the handful of non-empty slots needed by the dummy target - vector. */ +#include "target-delegates.c" -static void -init_dummy_target (void) -{ - dummy_target.to_shortname = "None"; - dummy_target.to_longname = "None"; - dummy_target.to_doc = ""; - dummy_target.to_supports_disable_randomization - = find_default_supports_disable_randomization; - dummy_target.to_stratum = dummy_stratum; - dummy_target.to_has_all_memory = return_zero; - dummy_target.to_has_memory = return_zero; - dummy_target.to_has_stack = return_zero; - dummy_target.to_has_registers = return_zero; - dummy_target.to_has_execution = return_zero_has_execution; - dummy_target.to_magic = OPS_MAGIC; - - install_dummy_methods (&dummy_target); +/* The initial current target, so that there is always a semi-valid + current target. */ + +static dummy_target the_dummy_target; + +static const target_info dummy_target_info = { + "None", + N_("None"), + "" +}; + +strata +dummy_target::stratum () const +{ + return dummy_stratum; } + +strata +debug_target::stratum () const +{ + return debug_stratum; +} + +const target_info & +dummy_target::info () const +{ + return dummy_target_info; +} + +const target_info & +debug_target::info () const +{ + return beneath ()->info (); +} + void @@ -3377,10 +3284,9 @@ target_close (struct target_ops *targ) { gdb_assert (!target_is_pushed (targ)); - if (targ->to_xclose != NULL) - targ->to_xclose (targ); - else if (targ->to_close != NULL) - targ->to_close (targ); + fileio_handles_invalidate_target (targ); + + targ->close (); if (targetdebug) fprintf_unfiltered (gdb_stdlog, "target_close ()\n"); @@ -3389,13 +3295,13 @@ target_close (struct target_ops *targ) int target_thread_alive (ptid_t ptid) { - return current_target.to_thread_alive (¤t_target, ptid); + return current_top_target ()->thread_alive (ptid); } void target_update_thread_list (void) { - current_target.to_update_thread_list (¤t_target); + current_top_target ()->update_thread_list (); } void @@ -3407,11 +3313,11 @@ target_stop (ptid_t ptid) return; } - (*current_target.to_stop) (¤t_target, ptid); + current_top_target ()->stop (ptid); } void -target_interrupt (ptid_t ptid) +target_interrupt () { if (!may_stop) { @@ -3419,7 +3325,7 @@ target_interrupt (ptid_t ptid) return; } - (*current_target.to_interrupt) (¤t_target, ptid); + current_top_target ()->interrupt (); } /* See target.h. */ @@ -3427,7 +3333,7 @@ target_interrupt (ptid_t ptid) void target_pass_ctrlc (void) { - (*current_target.to_pass_ctrlc) (¤t_target); + current_top_target ()->pass_ctrlc (); } /* See target.h. */ @@ -3435,7 +3341,7 @@ target_pass_ctrlc (void) void default_target_pass_ctrlc (struct target_ops *ops) { - target_interrupt (inferior_ptid); + target_interrupt (); } /* See target/target.h. */ @@ -3444,9 +3350,9 @@ void target_stop_and_wait (ptid_t ptid) { struct target_waitstatus status; - int was_non_stop = non_stop; + bool was_non_stop = non_stop; - non_stop = 1; + non_stop = true; target_stop (ptid); memset (&status, 0, sizeof (status)); @@ -3471,97 +3377,56 @@ target_continue (ptid_t ptid, enum gdb_signal signal) target_resume (ptid, 0, signal); } -/* Concatenate ELEM to LIST, a comma separate list, and return the - result. The LIST incoming argument is released. */ +/* Concatenate ELEM to LIST, a comma-separated list. */ -static char * -str_comma_list_concat_elem (char *list, const char *elem) +static void +str_comma_list_concat_elem (std::string *list, const char *elem) { - if (list == NULL) - return xstrdup (elem); - else - return reconcat (list, list, ", ", elem, (char *) NULL); + if (!list->empty ()) + list->append (", "); + + list->append (elem); } /* Helper for target_options_to_string. If OPT is present in TARGET_OPTIONS, append the OPT_STR (string version of OPT) in RET. - Returns the new resulting string. OPT is removed from - TARGET_OPTIONS. */ + OPT is removed from TARGET_OPTIONS. */ -static char * -do_option (int *target_options, char *ret, - int opt, char *opt_str) +static void +do_option (int *target_options, std::string *ret, + int opt, const char *opt_str) { if ((*target_options & opt) != 0) { - ret = str_comma_list_concat_elem (ret, opt_str); + str_comma_list_concat_elem (ret, opt_str); *target_options &= ~opt; } - - return ret; } -char * +/* See target.h. */ + +std::string target_options_to_string (int target_options) { - char *ret = NULL; + std::string ret; #define DO_TARG_OPTION(OPT) \ - ret = do_option (&target_options, ret, OPT, #OPT) + do_option (&target_options, &ret, OPT, #OPT) DO_TARG_OPTION (TARGET_WNOHANG); if (target_options != 0) - ret = str_comma_list_concat_elem (ret, "unknown???"); + str_comma_list_concat_elem (&ret, "unknown???"); - if (ret == NULL) - ret = xstrdup (""); return ret; } -static void -debug_print_register (const char * func, - struct regcache *regcache, int regno) -{ - struct gdbarch *gdbarch = get_regcache_arch (regcache); - - fprintf_unfiltered (gdb_stdlog, "%s ", func); - if (regno >= 0 && regno < gdbarch_num_regs (gdbarch) - && gdbarch_register_name (gdbarch, regno) != NULL - && gdbarch_register_name (gdbarch, regno)[0] != '\0') - fprintf_unfiltered (gdb_stdlog, "(%s)", - gdbarch_register_name (gdbarch, regno)); - else - fprintf_unfiltered (gdb_stdlog, "(%d)", regno); - if (regno >= 0 && regno < gdbarch_num_regs (gdbarch)) - { - enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); - int i, size = register_size (gdbarch, regno); - gdb_byte buf[MAX_REGISTER_SIZE]; - - regcache_raw_collect (regcache, regno, buf); - fprintf_unfiltered (gdb_stdlog, " = "); - for (i = 0; i < size; i++) - { - fprintf_unfiltered (gdb_stdlog, "%02x", buf[i]); - } - if (size <= sizeof (LONGEST)) - { - ULONGEST val = extract_unsigned_integer (buf, size, byte_order); - - fprintf_unfiltered (gdb_stdlog, " %s %s", - core_addr_to_string_nz (val), plongest (val)); - } - } - fprintf_unfiltered (gdb_stdlog, "\n"); -} - void target_fetch_registers (struct regcache *regcache, int regno) { - current_target.to_fetch_registers (¤t_target, regcache, regno); + current_top_target ()->fetch_registers (regcache, regno); if (targetdebug) - debug_print_register ("target_fetch_registers", regcache, regno); + regcache->debug_print_register ("target_fetch_registers", regno); } void @@ -3570,17 +3435,17 @@ target_store_registers (struct regcache *regcache, int regno) if (!may_write_registers) error (_("Writing to registers is not allowed (regno %d)"), regno); - current_target.to_store_registers (¤t_target, regcache, regno); + current_top_target ()->store_registers (regcache, regno); if (targetdebug) { - debug_print_register ("target_store_registers", regcache, regno); + regcache->debug_print_register ("target_store_registers", regno); } } int target_core_of_thread (ptid_t ptid) { - return current_target.to_core_of_thread (¤t_target, ptid); + return current_top_target ()->core_of_thread (ptid); } int @@ -3618,15 +3483,14 @@ default_verify_memory (struct target_ops *self, const gdb_byte *data, CORE_ADDR memaddr, ULONGEST size) { /* Start over from the top of the target stack. */ - return simple_verify_memory (current_target.beneath, + return simple_verify_memory (current_top_target (), data, memaddr, size); } int target_verify_memory (const gdb_byte *data, CORE_ADDR memaddr, ULONGEST size) { - return current_target.to_verify_memory (¤t_target, - data, memaddr, size); + return current_top_target ()->verify_memory (data, memaddr, size); } /* The documentation for this function is in its prototype declaration in @@ -3636,8 +3500,7 @@ int target_insert_mask_watchpoint (CORE_ADDR addr, CORE_ADDR mask, enum target_hw_bp_type rw) { - return current_target.to_insert_mask_watchpoint (¤t_target, - addr, mask, rw); + return current_top_target ()->insert_mask_watchpoint (addr, mask, rw); } /* The documentation for this function is in its prototype declaration in @@ -3647,8 +3510,7 @@ int target_remove_mask_watchpoint (CORE_ADDR addr, CORE_ADDR mask, enum target_hw_bp_type rw) { - return current_target.to_remove_mask_watchpoint (¤t_target, - addr, mask, rw); + return current_top_target ()->remove_mask_watchpoint (addr, mask, rw); } /* The documentation for this function is in its prototype declaration @@ -3657,8 +3519,7 @@ target_remove_mask_watchpoint (CORE_ADDR addr, CORE_ADDR mask, int target_masked_watch_num_registers (CORE_ADDR addr, CORE_ADDR mask) { - return current_target.to_masked_watch_num_registers (¤t_target, - addr, mask); + return current_top_target ()->masked_watch_num_registers (addr, mask); } /* The documentation for this function is in its prototype declaration @@ -3667,15 +3528,7 @@ target_masked_watch_num_registers (CORE_ADDR addr, CORE_ADDR mask) int target_ranged_break_num_registers (void) { - return current_target.to_ranged_break_num_registers (¤t_target); -} - -/* See target.h. */ - -int -target_supports_btrace (enum btrace_format format) -{ - return current_target.to_supports_btrace (¤t_target, format); + return current_top_target ()->ranged_break_num_registers (); } /* See target.h. */ @@ -3683,7 +3536,7 @@ target_supports_btrace (enum btrace_format format) struct btrace_target_info * target_enable_btrace (ptid_t ptid, const struct btrace_config *conf) { - return current_target.to_enable_btrace (¤t_target, ptid, conf); + return current_top_target ()->enable_btrace (ptid, conf); } /* See target.h. */ @@ -3691,7 +3544,7 @@ target_enable_btrace (ptid_t ptid, const struct btrace_config *conf) void target_disable_btrace (struct btrace_target_info *btinfo) { - current_target.to_disable_btrace (¤t_target, btinfo); + current_top_target ()->disable_btrace (btinfo); } /* See target.h. */ @@ -3699,7 +3552,7 @@ target_disable_btrace (struct btrace_target_info *btinfo) void target_teardown_btrace (struct btrace_target_info *btinfo) { - current_target.to_teardown_btrace (¤t_target, btinfo); + current_top_target ()->teardown_btrace (btinfo); } /* See target.h. */ @@ -3709,7 +3562,7 @@ target_read_btrace (struct btrace_data *btrace, struct btrace_target_info *btinfo, enum btrace_read_type type) { - return current_target.to_read_btrace (¤t_target, btrace, btinfo, type); + return current_top_target ()->read_btrace (btrace, btinfo, type); } /* See target.h. */ @@ -3717,7 +3570,7 @@ target_read_btrace (struct btrace_data *btrace, const struct btrace_config * target_btrace_conf (const struct btrace_target_info *btinfo) { - return current_target.to_btrace_conf (¤t_target, btinfo); + return current_top_target ()->btrace_conf (btinfo); } /* See target.h. */ @@ -3725,7 +3578,7 @@ target_btrace_conf (const struct btrace_target_info *btinfo) void target_stop_recording (void) { - current_target.to_stop_recording (¤t_target); + current_top_target ()->stop_recording (); } /* See target.h. */ @@ -3733,22 +3586,15 @@ target_stop_recording (void) void target_save_record (const char *filename) { - current_target.to_save_record (¤t_target, filename); + current_top_target ()->save_record (filename); } /* See target.h. */ int -target_supports_delete_record (void) +target_supports_delete_record () { - struct target_ops *t; - - for (t = current_target.beneath; t != NULL; t = t->beneath) - if (t->to_delete_record != delegate_delete_record - && t->to_delete_record != tdefault_delete_record) - return 1; - - return 0; + return current_top_target ()->supports_delete_record (); } /* See target.h. */ @@ -3756,7 +3602,15 @@ target_supports_delete_record (void) void target_delete_record (void) { - current_target.to_delete_record (¤t_target); + current_top_target ()->delete_record (); +} + +/* See target.h. */ + +enum record_method +target_record_method (ptid_t ptid) +{ + return current_top_target ()->record_method (ptid); } /* See target.h. */ @@ -3764,7 +3618,7 @@ target_delete_record (void) int target_record_is_replaying (ptid_t ptid) { - return current_target.to_record_is_replaying (¤t_target, ptid); + return current_top_target ()->record_is_replaying (ptid); } /* See target.h. */ @@ -3772,7 +3626,7 @@ target_record_is_replaying (ptid_t ptid) int target_record_will_replay (ptid_t ptid, int dir) { - return current_target.to_record_will_replay (¤t_target, ptid, dir); + return current_top_target ()->record_will_replay (ptid, dir); } /* See target.h. */ @@ -3780,7 +3634,7 @@ target_record_will_replay (ptid_t ptid, int dir) void target_record_stop_replaying (void) { - current_target.to_record_stop_replaying (¤t_target); + current_top_target ()->record_stop_replaying (); } /* See target.h. */ @@ -3788,7 +3642,7 @@ target_record_stop_replaying (void) void target_goto_record_begin (void) { - current_target.to_goto_record_begin (¤t_target); + current_top_target ()->goto_record_begin (); } /* See target.h. */ @@ -3796,7 +3650,7 @@ target_goto_record_begin (void) void target_goto_record_end (void) { - current_target.to_goto_record_end (¤t_target); + current_top_target ()->goto_record_end (); } /* See target.h. */ @@ -3804,55 +3658,57 @@ target_goto_record_end (void) void target_goto_record (ULONGEST insn) { - current_target.to_goto_record (¤t_target, insn); + current_top_target ()->goto_record (insn); } /* See target.h. */ void -target_insn_history (int size, int flags) +target_insn_history (int size, gdb_disassembly_flags flags) { - current_target.to_insn_history (¤t_target, size, flags); + current_top_target ()->insn_history (size, flags); } /* See target.h. */ void -target_insn_history_from (ULONGEST from, int size, int flags) +target_insn_history_from (ULONGEST from, int size, + gdb_disassembly_flags flags) { - current_target.to_insn_history_from (¤t_target, from, size, flags); + current_top_target ()->insn_history_from (from, size, flags); } /* See target.h. */ void -target_insn_history_range (ULONGEST begin, ULONGEST end, int flags) +target_insn_history_range (ULONGEST begin, ULONGEST end, + gdb_disassembly_flags flags) { - current_target.to_insn_history_range (¤t_target, begin, end, flags); + current_top_target ()->insn_history_range (begin, end, flags); } /* See target.h. */ void -target_call_history (int size, int flags) +target_call_history (int size, record_print_flags flags) { - current_target.to_call_history (¤t_target, size, flags); + current_top_target ()->call_history (size, flags); } /* See target.h. */ void -target_call_history_from (ULONGEST begin, int size, int flags) +target_call_history_from (ULONGEST begin, int size, record_print_flags flags) { - current_target.to_call_history_from (¤t_target, begin, size, flags); + current_top_target ()->call_history_from (begin, size, flags); } /* See target.h. */ void -target_call_history_range (ULONGEST begin, ULONGEST end, int flags) +target_call_history_range (ULONGEST begin, ULONGEST end, record_print_flags flags) { - current_target.to_call_history_range (¤t_target, begin, end, flags); + current_top_target ()->call_history_range (begin, end, flags); } /* See target.h. */ @@ -3860,7 +3716,7 @@ target_call_history_range (ULONGEST begin, ULONGEST end, int flags) const struct frame_unwind * target_get_unwinder (void) { - return current_target.to_get_unwinder (¤t_target); + return current_top_target ()->get_unwinder (); } /* See target.h. */ @@ -3868,7 +3724,7 @@ target_get_unwinder (void) const struct frame_unwind * target_get_tailcall_unwinder (void) { - return current_target.to_get_tailcall_unwinder (¤t_target); + return current_top_target ()->get_tailcall_unwinder (); } /* See target.h. */ @@ -3876,7 +3732,7 @@ target_get_tailcall_unwinder (void) void target_prepare_to_generate_core (void) { - current_target.to_prepare_to_generate_core (¤t_target); + current_top_target ()->prepare_to_generate_core (); } /* See target.h. */ @@ -3884,16 +3740,9 @@ target_prepare_to_generate_core (void) void target_done_generating_core (void) { - current_target.to_done_generating_core (¤t_target); + current_top_target ()->done_generating_core (); } -static void -setup_target_debug (void) -{ - memcpy (&debug_target, ¤t_target, sizeof debug_target); - - init_debug_target (¤t_target); -} static char targ_desc[] = @@ -3909,24 +3758,61 @@ default_rcmd (struct target_ops *self, const char *command, } static void -do_monitor_command (char *cmd, - int from_tty) +do_monitor_command (const char *cmd, int from_tty) { target_rcmd (cmd, gdb_stdtarg); } +/* Erases all the memory regions marked as flash. CMD and FROM_TTY are + ignored. */ + +void +flash_erase_command (const char *cmd, int from_tty) +{ + /* Used to communicate termination of flash operations to the target. */ + bool found_flash_region = false; + struct gdbarch *gdbarch = target_gdbarch (); + + std::vector mem_regions = target_memory_map (); + + /* Iterate over all memory regions. */ + for (const mem_region &m : mem_regions) + { + /* Is this a flash memory region? */ + if (m.attrib.mode == MEM_FLASH) + { + found_flash_region = true; + target_flash_erase (m.lo, m.hi - m.lo); + + ui_out_emit_tuple tuple_emitter (current_uiout, "erased-regions"); + + current_uiout->message (_("Erasing flash memory region at address ")); + current_uiout->field_core_addr ("address", gdbarch, m.lo); + current_uiout->message (", size = "); + current_uiout->field_string ("size", hex_string (m.hi - m.lo)); + current_uiout->message ("\n"); + } + } + + /* Did we do any flash operations? If so, we need to finalize them. */ + if (found_flash_region) + target_flash_done (); + else + current_uiout->message (_("No flash memory regions found.\n")); +} + /* Print the name of each layers of our target stack. */ static void -maintenance_print_target_stack (char *cmd, int from_tty) +maintenance_print_target_stack (const char *cmd, int from_tty) { - struct target_ops *t; - printf_filtered (_("The current target stack is:\n")); - for (t = target_stack; t != NULL; t = t->beneath) + for (target_ops *t = current_top_target (); t != NULL; t = t->beneath ()) { - printf_filtered (" - %s (%s)\n", t->to_shortname, t->to_longname); + if (t->stratum () == debug_stratum) + continue; + printf_filtered (" - %s (%s)\n", t->shortname (), t->longname ()); } } @@ -3936,7 +3822,7 @@ void target_async (int enable) { infrun_async (enable); - current_target.to_async (¤t_target, enable); + current_top_target ()->async (enable); } /* See target.h. */ @@ -3944,19 +3830,19 @@ target_async (int enable) void target_thread_events (int enable) { - current_target.to_thread_events (¤t_target, enable); + current_top_target ()->thread_events (enable); } /* Controls if targets can report that they can/are async. This is just for maintainers to use when debugging gdb. */ -int target_async_permitted = 1; +bool target_async_permitted = true; /* The set command writes to this variable. If the inferior is executing, target_async_permitted is *not* updated. */ -static int target_async_permitted_1 = 1; +static bool target_async_permitted_1 = true; static void -maint_set_target_async_command (char *args, int from_tty, +maint_set_target_async_command (const char *args, int from_tty, struct cmd_list_element *c) { if (have_live_inferiors ()) @@ -3984,7 +3870,7 @@ maint_show_target_async_command (struct ui_file *file, int from_tty, static int target_always_non_stop_p (void) { - return current_target.to_always_non_stop_p (¤t_target); + return current_top_target ()->always_non_stop_p (); } /* See target.h. */ @@ -4009,7 +3895,7 @@ static enum auto_boolean target_non_stop_enabled_1 = AUTO_BOOLEAN_AUTO; /* Implementation of "maint set target-non-stop". */ static void -maint_set_target_non_stop_command (char *args, int from_tty, +maint_set_target_non_stop_command (const char *args, int from_tty, struct cmd_list_element *c) { if (have_live_inferiors ()) @@ -4041,12 +3927,12 @@ maint_show_target_non_stop_command (struct ui_file *file, int from_tty, /* Temporary copies of permission settings. */ -static int may_write_registers_1 = 1; -static int may_write_memory_1 = 1; -static int may_insert_breakpoints_1 = 1; -static int may_insert_tracepoints_1 = 1; -static int may_insert_fast_tracepoints_1 = 1; -static int may_stop_1 = 1; +static bool may_write_registers_1 = true; +static bool may_write_memory_1 = true; +static bool may_insert_breakpoints_1 = true; +static bool may_insert_tracepoints_1 = true; +static bool may_insert_fast_tracepoints_1 = true; +static bool may_stop_1 = true; /* Make the user-set values match the real values again. */ @@ -4065,7 +3951,7 @@ update_target_permissions (void) way. */ static void -set_target_permissions (char *args, int from_tty, +set_target_permissions (const char *args, int from_tty, struct cmd_list_element *c) { if (target_has_execution) @@ -4086,7 +3972,7 @@ set_target_permissions (char *args, int from_tty, /* Set memory write permission independently of observer mode. */ static void -set_write_memory_permission (char *args, int from_tty, +set_write_memory_permission (const char *args, int from_tty, struct cmd_list_element *c) { /* Make the real values match the user-changed values. */ @@ -4094,15 +3980,15 @@ set_write_memory_permission (char *args, int from_tty, update_observer_mode (); } - void initialize_targets (void) { - init_dummy_target (); - push_target (&dummy_target); + push_target (&the_dummy_target); + + the_debug_target = new debug_target (); - add_info ("target", target_info, targ_desc); - add_info ("files", target_info, targ_desc); + add_info ("target", info_target_command, targ_desc); + add_info ("files", info_target_command, targ_desc); add_setshow_zuinteger_cmd ("target", class_maintenance, &targetdebug, _("\ Set target debugging."), _("\ @@ -4205,6 +4091,9 @@ Otherwise, any attempt to interrupt or stop will be ignored."), set_target_permissions, NULL, &setlist, &showlist); + add_com ("flash-erase", no_class, flash_erase_command, + _("Erase all flash memory regions.")); + add_setshow_boolean_cmd ("auto-connect-native-target", class_support, &auto_connect_native_target, _("\ Set whether GDB may automatically connect to the native target."), _("\