X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Ftarget.c;h=e8d4ae7ea8e906a55a112001dbca8cb296e42345;hb=632e107b32c0fe8aede62e070b00756e9fdd2c01;hp=3e2b4d08fc2f56362f9c8cc3f23d016334e8b56d;hpb=cb85b21ba1c0014787129d3f53cb8755aba64930;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/target.c b/gdb/target.c index 3e2b4d08fc..e8d4ae7ea8 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-2017 Free Software Foundation, Inc. + Copyright (C) 1990-2018 Free Software Foundation, Inc. Contributed by Cygnus Support. @@ -47,8 +47,8 @@ #include "event-top.h" #include #include "byte-vector.h" - -static void info_target_command (char *, int); +#include "terminal.h" +#include static void generic_tls_error (void) ATTRIBUTE_NORETURN; @@ -90,8 +90,6 @@ 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 (const char *); static struct gdbarch *default_thread_architecture (struct target_ops *ops, @@ -171,7 +169,7 @@ int may_stop = 1; 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 (); } @@ -188,7 +186,7 @@ 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); @@ -349,7 +347,7 @@ complete_target_initialization (struct target_ops *t) /* 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); @@ -386,7 +384,7 @@ 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->to_shortname, no_class, t->to_doc, &targetlist); set_cmd_sfunc (c, open_target); set_cmd_context (c, t); if (completer != NULL) @@ -411,7 +409,7 @@ add_deprecated_target_alias (struct target_ops *t, const char *alias) /* 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, t->to_doc, &targetlist); set_cmd_sfunc (c, open_target); set_cmd_context (c, t); alt = xstrprintf ("target %s", t->to_shortname); @@ -433,53 +431,25 @@ target_load (const char *arg, int from_tty) (*current_target.to_load) (¤t_target, arg, from_tty); } -/* Possible terminal states. */ - -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, +/* Define it. */ - /* Our terminal settings are in effect, for output and input. */ - terminal_is_ours = 2 - }; +target_terminal_state target_terminal::m_terminal_state + = target_terminal_state::is_ours; -static enum terminal_state terminal_state = terminal_is_ours; - -/* See target.h. */ +/* See target/target.h. */ void -target_terminal_init (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) -{ - return (terminal_state == terminal_is_inferior); + m_terminal_state = target_terminal_state::is_ours; } -/* See target.h. */ - -int -target_terminal_is_ours (void) -{ - return (terminal_state == terminal_is_ours); -} - -/* See target.h. */ +/* See target/target.h. */ void -target_terminal_inferior (void) +target_terminal::inferior (void) { struct ui *ui = current_ui; @@ -490,18 +460,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_target.to_terminal_inferior) (¤t_target); + 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. */ @@ -509,39 +484,130 @@ target_terminal_inferior (void) target_pass_ctrlc (); } -/* See target.h. */ +/* See target/target.h. */ + +void +target_terminal::restore_inferior (void) +{ + struct ui *ui = current_ui; + + /* See target_terminal::inferior(). */ + if (ui->prompt_state != PROMPT_BLOCKED || ui != main_ui) + 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; + struct inferior *inf; + + ALL_INFERIORS (inf) + { + if (inf->terminal_state == target_terminal_state::is_ours_for_output) + { + set_current_inferior (inf); + (*current_target.to_terminal_inferior) (¤t_target); + 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; + struct inferior *inf; + + /* 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. */ + + ALL_INFERIORS (inf) + { + if (inf->terminal_state == target_terminal_state::is_inferior) + { + set_current_inferior (inf); + (*current_target.to_terminal_save_inferior) (¤t_target); + } + } + + ALL_INFERIORS (inf) + { + /* 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_target.to_terminal_ours) (¤t_target); + else if (desired_state == target_terminal_state::is_ours_for_output) + (*current_target.to_terminal_ours_for_output) (¤t_target); + else + gdb_assert_not_reached ("unhandled desired state"); + inf->terminal_state = desired_state; + } + } +} + +/* See target/target.h. */ void -target_terminal_ours (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_ours) + if (m_terminal_state == target_terminal_state::is_ours) return; - (*current_target.to_terminal_ours) (¤t_target); - terminal_state = terminal_is_ours; + 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. */ void -target_terminal_ours_for_output (void) +target_terminal::ours_for_output () { 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 (!target_terminal::is_inferior ()) 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_for_output); + target_terminal::m_terminal_state = target_terminal_state::is_ours_for_output; +} + +/* See target/target.h. */ + +void +target_terminal::info (const char *arg, int from_tty) +{ + (*current_target.to_terminal_info) (¤t_target, arg, from_tty); } /* See target.h. */ @@ -561,40 +627,6 @@ target_supports_terminal_ours (void) return 0; } -/* Restore the terminal to its previous state (helper for - make_cleanup_restore_target_terminal). */ - -static void -cleanup_restore_target_terminal (void *arg) -{ - 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; - } -} - -/* See target.h. */ - -struct cleanup * -make_cleanup_restore_target_terminal (void) -{ - enum terminal_state *ts = XNEW (enum terminal_state); - - *ts = terminal_state; - - return make_cleanup_dtor (cleanup_restore_target_terminal, ts, xfree); -} - static void tcomplain (void) { @@ -962,7 +994,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]; @@ -1022,7 +1055,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; @@ -1272,6 +1305,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. */ @@ -1535,34 +1570,31 @@ 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_target.to_memory_map (¤t_target); + 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; } @@ -1684,43 +1716,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. @@ -1750,7 +1776,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); @@ -1777,47 +1803,27 @@ 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) -{ - VEC(memory_read_result_s) **v = (VEC(memory_read_result_s) **) x; - memory_read_result_s *current; - int ix; - - 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) * +std::vector read_memory_robust (struct target_ops *ops, const ULONGEST offset, const LONGEST len) { - VEC(memory_read_result_s) *result = 0; + std::vector result; 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) @@ -1843,19 +1849,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); @@ -1863,20 +1867,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; } @@ -1939,18 +1938,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 @@ -1961,86 +1959,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; + gdb::optional buf + = target_read_alloc_1 (ops, object, annex); - transferred = target_read_alloc_1 (ops, object, annex, &buffer, 1); - bufstr = (char *) buffer; + if (!buf) + return {}; - if (transferred < 0) - return NULL; - - if (transferred == 0) - return xstrdup (""); - - bufstr[transferred] = 0; + if (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"), @@ -2048,7 +2024,7 @@ target_read_stralloc (struct target_ops *ops, enum target_object object, break; } - return bufstr; + return buf; } /* Memory transfer methods. */ @@ -2115,7 +2091,7 @@ target_remove_breakpoint (struct gdbarch *gdbarch, } static void -info_target_command (char *args, int from_tty) +info_target_command (const char *args, int from_tty) { struct target_ops *t; int has_all_mem = 0; @@ -2204,7 +2180,7 @@ dispose_inferior (struct inferior *inf, void *args) if (target_has_execution) target_kill (); else - target_detach (NULL, 0); + target_detach (inf, 0); } return 0; @@ -2237,11 +2213,18 @@ 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) { + /* 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. */ @@ -2253,7 +2236,7 @@ target_detach (const char *args, int from_tty) prepare_for_detach (); - current_target.to_detach (¤t_target, args, from_tty); + current_target.to_detach (¤t_target, inf, from_tty); } void @@ -2298,6 +2281,15 @@ target_thread_name (struct thread_info *info) return current_target.to_thread_name (¤t_target, info); } +struct thread_info * +target_thread_handle_to_thread_info (const gdb_byte *thread_handle, + int handle_len, + struct inferior *inf) +{ + return current_target.to_thread_handle_to_thread_info + (¤t_target, thread_handle, handle_len, inf); +} + void target_resume (ptid_t ptid, int step, enum gdb_signal signal) { @@ -2320,8 +2312,6 @@ static int defer_target_commit_resume; void target_commit_resume (void) { - struct target_ops *t; - if (defer_target_commit_resume) return; @@ -2330,14 +2320,10 @@ target_commit_resume (void) /* See target.h. */ -struct cleanup * -make_cleanup_defer_target_commit_resume (void) +scoped_restore_tmpl +make_scoped_defer_target_commit_resume () { - struct cleanup *old_chain; - - old_chain = make_cleanup_restore_integer (&defer_target_commit_resume); - defer_target_commit_resume = 1; - return old_chain; + return make_scoped_restore (&defer_target_commit_resume, 1); } void @@ -2738,7 +2724,9 @@ target_supports_multi_process (void) return (*current_target.to_supports_multi_process) (¤t_target); } -char * +/* See target.h. */ + +gdb::optional target_get_osdata (const char *type) { struct target_ops *t; @@ -2752,7 +2740,7 @@ target_get_osdata (const char *type) t = find_default_run_target ("get OS data"); if (!t) - return NULL; + return {}; return target_read_stralloc (t, TARGET_OBJECT_OSDATA, type); } @@ -2803,56 +2791,70 @@ default_fileio_target (void) /* 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. */ @@ -2864,14 +2866,17 @@ 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]; +} /* Helper for target_fileio_open and target_fileio_open_warn_if_slow. */ @@ -2941,11 +2946,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->to_fileio_pwrite (fh->target, fh->target_fd, write_buf, + len, offset, target_errno); if (targetdebug) fprintf_unfiltered (gdb_stdlog, @@ -2965,11 +2972,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->to_fileio_pread (fh->target, fh->target_fd, read_buf, + len, offset, target_errno); if (targetdebug) fprintf_unfiltered (gdb_stdlog, @@ -2988,10 +2997,13 @@ 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->to_fileio_fstat (fh->target, fh->target_fd, + sb, target_errno); if (targetdebug) fprintf_unfiltered (gdb_stdlog, @@ -3008,11 +3020,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->to_fileio_close (fh->target, fh->target_fd, + target_errno); + else + ret = 0; release_fileio_fd (fd, fh); } @@ -3054,7 +3070,7 @@ 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) { @@ -3064,32 +3080,54 @@ target_fileio_readlink (struct inferior *inf, const char *filename, { if (t->to_fileio_readlink != NULL) { - char *ret = t->to_fileio_readlink (t, inf, filename, - target_errno); + gdb::optional ret + = t->to_fileio_readlink (t, 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); + 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 @@ -3103,20 +3141,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; @@ -3124,20 +3158,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 @@ -3169,7 +3201,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; @@ -3180,10 +3212,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 gdb::unique_xmalloc_ptr (xstrdup ("")); bufstr[transferred] = 0; @@ -3197,7 +3229,7 @@ target_fileio_read_stralloc (struct inferior *inf, const char *filename) break; } - return bufstr; + return gdb::unique_xmalloc_ptr (bufstr); } @@ -3219,7 +3251,9 @@ default_watchpoint_addr_within_range (struct target_ops *target, static struct gdbarch * default_thread_architecture (struct target_ops *ops, ptid_t ptid) { - return target_gdbarch (); + inferior *inf = find_inferior_ptid (ptid); + gdb_assert (inf != NULL); + return inf->gdbarch; } static int @@ -3380,6 +3414,8 @@ target_close (struct target_ops *targ) { gdb_assert (!target_is_pushed (targ)); + fileio_handles_invalidate_target (targ); + if (targ->to_xclose != NULL) targ->to_xclose (targ); else if (targ->to_close != NULL) @@ -3414,7 +3450,7 @@ target_stop (ptid_t ptid) } void -target_interrupt (ptid_t ptid) +target_interrupt () { if (!may_stop) { @@ -3422,7 +3458,7 @@ target_interrupt (ptid_t ptid) return; } - (*current_target.to_interrupt) (¤t_target, ptid); + (*current_target.to_interrupt) (¤t_target); } /* See target.h. */ @@ -3438,7 +3474,7 @@ target_pass_ctrlc (void) void default_target_pass_ctrlc (struct target_ops *ops) { - target_interrupt (inferior_ptid); + target_interrupt (); } /* See target/target.h. */ @@ -3638,14 +3674,6 @@ target_ranged_break_num_registers (void) /* See target.h. */ -int -target_supports_btrace (enum btrace_format format) -{ - return current_target.to_supports_btrace (¤t_target, format); -} - -/* See target.h. */ - struct btrace_target_info * target_enable_btrace (ptid_t ptid, const struct btrace_config *conf) { @@ -3810,7 +3838,7 @@ target_insn_history_range (ULONGEST begin, ULONGEST end, /* 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); } @@ -3818,7 +3846,7 @@ target_call_history (int size, int 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); } @@ -3826,7 +3854,7 @@ target_call_history_from (ULONGEST begin, int size, int 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); } @@ -3885,8 +3913,7 @@ 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); } @@ -3895,34 +3922,29 @@ do_monitor_command (char *cmd, ignored. */ void -flash_erase_command (char *cmd, int from_tty) +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 mem_region *m; struct gdbarch *gdbarch = target_gdbarch (); - VEC(mem_region_s) *mem_regions = target_memory_map (); + std::vector mem_regions = target_memory_map (); /* Iterate over all memory regions. */ - for (int i = 0; VEC_iterate (mem_region_s, mem_regions, i, m); i++) + for (const mem_region &m : mem_regions) { - /* Fetch the memory attribute. */ - struct mem_attrib *attrib = &m->attrib; - /* Is this a flash memory region? */ - if (attrib->mode == MEM_FLASH) + if (m.attrib.mode == MEM_FLASH) { found_flash_region = true; - target_flash_erase (m->lo, m->hi - m->lo); + 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_fmt ("address", "%s", paddress (gdbarch, - m->lo)); + current_uiout->field_fmt ("address", "%s", paddress (gdbarch, m.lo)); current_uiout->message (", size = "); - current_uiout->field_fmt ("size", "%s", hex_string (m->hi - m->lo)); + current_uiout->field_fmt ("size", "%s", hex_string (m.hi - m.lo)); current_uiout->message ("\n"); } } @@ -3937,7 +3959,7 @@ flash_erase_command (char *cmd, int from_tty) /* 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; @@ -3975,7 +3997,7 @@ int target_async_permitted = 1; static int target_async_permitted_1 = 1; 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 ()) @@ -4028,7 +4050,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 ()) @@ -4084,7 +4106,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) @@ -4105,7 +4127,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. */ @@ -4113,6 +4135,53 @@ set_write_memory_permission (char *args, int from_tty, update_observer_mode (); } +#if GDB_SELF_TEST +namespace selftests { + +static int +test_target_has_registers (target_ops *self) +{ + return 1; +} + +static int +test_target_has_stack (target_ops *self) +{ + return 1; +} + +static int +test_target_has_memory (target_ops *self) +{ + return 1; +} + +static void +test_target_prepare_to_store (target_ops *self, regcache *regs) +{ +} + +static void +test_target_store_registers (target_ops *self, regcache *regs, int regno) +{ +} + +test_target_ops::test_target_ops () + : target_ops {} +{ + to_magic = OPS_MAGIC; + to_stratum = process_stratum; + to_has_memory = test_target_has_memory; + to_has_stack = test_target_has_stack; + to_has_registers = test_target_has_registers; + to_prepare_to_store = test_target_prepare_to_store; + to_store_registers = test_target_store_registers; + + complete_target_initialization (this); +} + +} // namespace selftests +#endif /* GDB_SELF_TEST */ void initialize_targets (void)