X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Ftarget.c;h=e8d4ae7ea8e906a55a112001dbca8cb296e42345;hb=632e107b32c0fe8aede62e070b00756e9fdd2c01;hp=2e02a774e6223dbdeb4892d0a81f9c0eea47a4e2;hpb=eb4c3f4aaae2ee1b27c210e951260a7e699133b4;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/target.c b/gdb/target.c index 2e02a774e6..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,6 +47,8 @@ #include "event-top.h" #include #include "byte-vector.h" +#include "terminal.h" +#include static void generic_tls_error (void) ATTRIBUTE_NORETURN; @@ -431,8 +433,8 @@ target_load (const char *arg, int from_tty) /* Define it. */ -enum target_terminal::terminal_state target_terminal::terminal_state - = target_terminal::terminal_is_ours; +target_terminal_state target_terminal::m_terminal_state + = target_terminal_state::is_ours; /* See target/target.h. */ @@ -441,7 +443,7 @@ target_terminal::init (void) { (*current_target.to_terminal_init) (¤t_target); - terminal_state = terminal_is_ours; + m_terminal_state = target_terminal_state::is_ours; } /* See target/target.h. */ @@ -463,13 +465,56 @@ target_terminal::inferior (void) 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. */ + if (check_quit_flag ()) + target_pass_ctrlc (); +} + +/* 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. */ @@ -477,6 +522,50 @@ target_terminal::inferior (void) 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 @@ -488,11 +577,11 @@ target_terminal::ours () 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/target.h. */ @@ -506,10 +595,11 @@ target_terminal::ours_for_output () 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. */ @@ -904,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]; @@ -964,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; @@ -1214,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. */ @@ -1845,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 @@ -1867,82 +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); } /* See target.h. */ -gdb::unique_xmalloc_ptr +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; + gdb::optional buf + = target_read_alloc_1 (ops, object, annex); - if (transferred < 0) - return NULL; + if (!buf) + return {}; - if (transferred == 0) - return gdb::unique_xmalloc_ptr (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"), @@ -1950,7 +2024,7 @@ target_read_stralloc (struct target_ops *ops, enum target_object object, break; } - return gdb::unique_xmalloc_ptr (bufstr); + return buf; } /* Memory transfer methods. */ @@ -2106,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; @@ -2139,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. */ @@ -2155,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 @@ -2231,8 +2312,6 @@ static int defer_target_commit_resume; void target_commit_resume (void) { - struct target_ops *t; - if (defer_target_commit_resume) return; @@ -2647,7 +2726,7 @@ target_supports_multi_process (void) /* See target.h. */ -gdb::unique_xmalloc_ptr +gdb::optional target_get_osdata (const char *type) { struct target_ops *t; @@ -2661,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); } @@ -2712,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. */ @@ -2773,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. */ @@ -2850,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, @@ -2874,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, @@ -2897,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, @@ -2917,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); } @@ -2963,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) { @@ -2973,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 @@ -3012,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; @@ -3033,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 @@ -3291,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) @@ -3325,7 +3450,7 @@ target_stop (ptid_t ptid) } void -target_interrupt (ptid_t ptid) +target_interrupt () { if (!may_stop) { @@ -3333,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. */ @@ -3349,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. */ @@ -3549,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) { @@ -3721,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); } @@ -3729,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); } @@ -3737,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); } @@ -4018,6 +4135,53 @@ set_write_memory_permission (const 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)