| /* Base/prototype target for default child (native) targets. | 
 |  | 
 |    Copyright (C) 1988-2022 Free Software Foundation, Inc. | 
 |  | 
 |    This file is part of GDB. | 
 |  | 
 |    This program is free software; you can redistribute it and/or modify | 
 |    it under the terms of the GNU General Public License as published by | 
 |    the Free Software Foundation; either version 3 of the License, or | 
 |    (at your option) any later version. | 
 |  | 
 |    This program is distributed in the hope that it will be useful, | 
 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |    GNU General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU General Public License | 
 |    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | /* This file provides a common base class/target that all native | 
 |    target implementations extend, by calling inf_child_target to get a | 
 |    new prototype target and then overriding target methods as | 
 |    necessary.  */ | 
 |  | 
 | #include "defs.h" | 
 | #include "regcache.h" | 
 | #include "memattr.h" | 
 | #include "symtab.h" | 
 | #include "target.h" | 
 | #include "inferior.h" | 
 | #include <sys/stat.h> | 
 | #include "inf-child.h" | 
 | #include "gdbsupport/fileio.h" | 
 | #include "gdbsupport/agent.h" | 
 | #include "gdbsupport/gdb_wait.h" | 
 | #include "gdbsupport/filestuff.h" | 
 |  | 
 | #include <sys/types.h> | 
 | #include <fcntl.h> | 
 | #include <unistd.h> | 
 |  | 
 | static const target_info inf_child_target_info = { | 
 |   "native", | 
 |   N_("Native process"), | 
 |   N_("Native process (started by the \"run\" command).") | 
 | }; | 
 |  | 
 | const target_info & | 
 | inf_child_target::info () const | 
 | { | 
 |   return inf_child_target_info; | 
 | } | 
 |  | 
 | /* See inf-child.h.  */ | 
 |  | 
 | target_waitstatus | 
 | host_status_to_waitstatus (int hoststatus) | 
 | { | 
 |   if (WIFEXITED (hoststatus)) | 
 |     return target_waitstatus ().set_exited (WEXITSTATUS (hoststatus)); | 
 |   else if (!WIFSTOPPED (hoststatus)) | 
 |     return target_waitstatus ().set_signalled | 
 |       (gdb_signal_from_host (WTERMSIG (hoststatus))); | 
 |   else | 
 |     return target_waitstatus ().set_stopped | 
 |       (gdb_signal_from_host (WSTOPSIG (hoststatus))); | 
 | } | 
 |  | 
 | inf_child_target::~inf_child_target () | 
 | {} | 
 |  | 
 | void | 
 | inf_child_target::post_attach (int pid) | 
 | { | 
 |   /* This target doesn't require a meaningful "post attach" operation | 
 |      by a debugger.  */ | 
 | } | 
 |  | 
 | /* Get ready to modify the registers array.  On machines which store | 
 |    individual registers, this doesn't need to do anything.  On | 
 |    machines which store all the registers in one fell swoop, this | 
 |    makes sure that registers contains all the registers from the | 
 |    program being debugged.  */ | 
 |  | 
 | void | 
 | inf_child_target::prepare_to_store (struct regcache *regcache) | 
 | { | 
 | } | 
 |  | 
 | bool | 
 | inf_child_target::supports_terminal_ours () | 
 | { | 
 |   return true; | 
 | } | 
 |  | 
 | void | 
 | inf_child_target::terminal_init () | 
 | { | 
 |   child_terminal_init (this); | 
 | } | 
 |  | 
 | void | 
 | inf_child_target::terminal_inferior () | 
 | { | 
 |   child_terminal_inferior (this); | 
 | } | 
 |  | 
 | void | 
 | inf_child_target::terminal_save_inferior () | 
 | { | 
 |   child_terminal_save_inferior (this); | 
 | } | 
 |  | 
 | void | 
 | inf_child_target::terminal_ours_for_output () | 
 | { | 
 |   child_terminal_ours_for_output (this); | 
 | } | 
 |  | 
 | void | 
 | inf_child_target::terminal_ours () | 
 | { | 
 |   child_terminal_ours (this); | 
 | } | 
 |  | 
 | void | 
 | inf_child_target::interrupt () | 
 | { | 
 |   child_interrupt (this); | 
 | } | 
 |  | 
 | void | 
 | inf_child_target::pass_ctrlc () | 
 | { | 
 |   child_pass_ctrlc (this); | 
 | } | 
 |  | 
 | void | 
 | inf_child_target::terminal_info (const char *args, int from_tty) | 
 | { | 
 |   child_terminal_info (this, args, from_tty); | 
 | } | 
 |  | 
 | /* True if the user did "target native".  In that case, we won't | 
 |    unpush the child target automatically when the last inferior is | 
 |    gone.  */ | 
 | static int inf_child_explicitly_opened; | 
 |  | 
 | /* See inf-child.h.  */ | 
 |  | 
 | void | 
 | inf_child_open_target (const char *arg, int from_tty) | 
 | { | 
 |   target_ops *target = get_native_target (); | 
 |  | 
 |   /* There's always only ever one native target, and if we get here, | 
 |      it better be an inf-child target.  */ | 
 |   gdb_assert (dynamic_cast<inf_child_target *> (target) != NULL); | 
 |  | 
 |   target_preopen (from_tty); | 
 |   current_inferior ()->push_target (target); | 
 |   inf_child_explicitly_opened = 1; | 
 |   if (from_tty) | 
 |     gdb_printf ("Done.  Use the \"run\" command to start a process.\n"); | 
 | } | 
 |  | 
 | /* Implement the to_disconnect target_ops method.  */ | 
 |  | 
 | void | 
 | inf_child_target::disconnect (const char *args, int from_tty) | 
 | { | 
 |   if (args != NULL) | 
 |     error (_("Argument given to \"disconnect\".")); | 
 |  | 
 |   /* This offers to detach/kill current inferiors, and then pops all | 
 |      targets.  */ | 
 |   target_preopen (from_tty); | 
 | } | 
 |  | 
 | /* Implement the to_close target_ops method.  */ | 
 |  | 
 | void | 
 | inf_child_target::close () | 
 | { | 
 |   /* In case we were forcibly closed.  */ | 
 |   inf_child_explicitly_opened = 0; | 
 | } | 
 |  | 
 | void | 
 | inf_child_target::mourn_inferior () | 
 | { | 
 |   generic_mourn_inferior (); | 
 |   maybe_unpush_target (); | 
 | } | 
 |  | 
 | /* See inf-child.h.  */ | 
 |  | 
 | void | 
 | inf_child_target::maybe_unpush_target () | 
 | { | 
 |   if (!inf_child_explicitly_opened) | 
 |     current_inferior ()->unpush_target (this); | 
 | } | 
 |  | 
 | bool | 
 | inf_child_target::can_run () | 
 | { | 
 |   return true; | 
 | } | 
 |  | 
 | bool | 
 | inf_child_target::can_create_inferior () | 
 | { | 
 |   return true; | 
 | } | 
 |  | 
 | bool | 
 | inf_child_target::can_attach () | 
 | { | 
 |   return true; | 
 | } | 
 |  | 
 | const char * | 
 | inf_child_target::pid_to_exec_file (int pid) | 
 | { | 
 |   /* This target doesn't support translation of a process ID to the | 
 |      filename of the executable file.  */ | 
 |   return NULL; | 
 | } | 
 |  | 
 | /* Implementation of to_fileio_open.  */ | 
 |  | 
 | int | 
 | inf_child_target::fileio_open (struct inferior *inf, const char *filename, | 
 | 			       int flags, int mode, int warn_if_slow, | 
 | 			       fileio_error *target_errno) | 
 | { | 
 |   int nat_flags; | 
 |   mode_t nat_mode; | 
 |   int fd; | 
 |  | 
 |   if (fileio_to_host_openflags (flags, &nat_flags) == -1 | 
 |       || fileio_to_host_mode (mode, &nat_mode) == -1) | 
 |     { | 
 |       *target_errno = FILEIO_EINVAL; | 
 |       return -1; | 
 |     } | 
 |  | 
 |   fd = gdb_open_cloexec (filename, nat_flags, nat_mode).release (); | 
 |   if (fd == -1) | 
 |     *target_errno = host_to_fileio_error (errno); | 
 |  | 
 |   return fd; | 
 | } | 
 |  | 
 | /* Implementation of to_fileio_pwrite.  */ | 
 |  | 
 | int | 
 | inf_child_target::fileio_pwrite (int fd, const gdb_byte *write_buf, int len, | 
 | 				 ULONGEST offset, fileio_error *target_errno) | 
 | { | 
 |   int ret; | 
 |  | 
 | #ifdef HAVE_PWRITE | 
 |   ret = pwrite (fd, write_buf, len, (long) offset); | 
 | #else | 
 |   ret = -1; | 
 | #endif | 
 |   /* If we have no pwrite or it failed for this file, use lseek/write.  */ | 
 |   if (ret == -1) | 
 |     { | 
 |       ret = lseek (fd, (long) offset, SEEK_SET); | 
 |       if (ret != -1) | 
 | 	ret = write (fd, write_buf, len); | 
 |     } | 
 |  | 
 |   if (ret == -1) | 
 |     *target_errno = host_to_fileio_error (errno); | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | /* Implementation of to_fileio_pread.  */ | 
 |  | 
 | int | 
 | inf_child_target::fileio_pread (int fd, gdb_byte *read_buf, int len, | 
 | 				ULONGEST offset, fileio_error *target_errno) | 
 | { | 
 |   int ret; | 
 |  | 
 | #ifdef HAVE_PREAD | 
 |   ret = pread (fd, read_buf, len, (long) offset); | 
 | #else | 
 |   ret = -1; | 
 | #endif | 
 |   /* If we have no pread or it failed for this file, use lseek/read.  */ | 
 |   if (ret == -1) | 
 |     { | 
 |       ret = lseek (fd, (long) offset, SEEK_SET); | 
 |       if (ret != -1) | 
 | 	ret = read (fd, read_buf, len); | 
 |     } | 
 |  | 
 |   if (ret == -1) | 
 |     *target_errno = host_to_fileio_error (errno); | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | /* Implementation of to_fileio_fstat.  */ | 
 |  | 
 | int | 
 | inf_child_target::fileio_fstat (int fd, struct stat *sb, fileio_error *target_errno) | 
 | { | 
 |   int ret; | 
 |  | 
 |   ret = fstat (fd, sb); | 
 |   if (ret == -1) | 
 |     *target_errno = host_to_fileio_error (errno); | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | /* Implementation of to_fileio_close.  */ | 
 |  | 
 | int | 
 | inf_child_target::fileio_close (int fd, fileio_error *target_errno) | 
 | { | 
 |   int ret; | 
 |  | 
 |   ret = ::close (fd); | 
 |   if (ret == -1) | 
 |     *target_errno = host_to_fileio_error (errno); | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | /* Implementation of to_fileio_unlink.  */ | 
 |  | 
 | int | 
 | inf_child_target::fileio_unlink (struct inferior *inf, const char *filename, | 
 | 				 fileio_error *target_errno) | 
 | { | 
 |   int ret; | 
 |  | 
 |   ret = unlink (filename); | 
 |   if (ret == -1) | 
 |     *target_errno = host_to_fileio_error (errno); | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | /* Implementation of to_fileio_readlink.  */ | 
 |  | 
 | gdb::optional<std::string> | 
 | inf_child_target::fileio_readlink (struct inferior *inf, const char *filename, | 
 | 				   fileio_error *target_errno) | 
 | { | 
 |   /* We support readlink only on systems that also provide a compile-time | 
 |      maximum path length (PATH_MAX), at least for now.  */ | 
 | #if defined (PATH_MAX) | 
 |   char buf[PATH_MAX]; | 
 |   int len; | 
 |  | 
 |   len = readlink (filename, buf, sizeof buf); | 
 |   if (len < 0) | 
 |     { | 
 |       *target_errno = host_to_fileio_error (errno); | 
 |       return {}; | 
 |     } | 
 |  | 
 |   return std::string (buf, len); | 
 | #else | 
 |   *target_errno = FILEIO_ENOSYS; | 
 |   return {}; | 
 | #endif | 
 | } | 
 |  | 
 | bool | 
 | inf_child_target::use_agent (bool use) | 
 | { | 
 |   if (agent_loaded_p ()) | 
 |     { | 
 |       ::use_agent = use; | 
 |       return true; | 
 |     } | 
 |   else | 
 |     return false; | 
 | } | 
 |  | 
 | bool | 
 | inf_child_target::can_use_agent () | 
 | { | 
 |   return agent_loaded_p (); | 
 | } | 
 |  | 
 | void | 
 | inf_child_target::follow_exec (inferior *follow_inf, ptid_t ptid, | 
 | 			       const char *execd_pathname) | 
 | { | 
 |   inferior *orig_inf = current_inferior (); | 
 |  | 
 |   process_stratum_target::follow_exec (follow_inf, ptid, execd_pathname); | 
 |  | 
 |   if (orig_inf != follow_inf) | 
 |     { | 
 |       /* If the target was implicitly push in the original inferior, unpush | 
 |          it.  */ | 
 |       scoped_restore_current_thread restore_thread; | 
 |       switch_to_inferior_no_thread (orig_inf); | 
 |       maybe_unpush_target (); | 
 |     } | 
 | } | 
 |  | 
 | /* See inf-child.h.  */ | 
 |  | 
 | void | 
 | add_inf_child_target (inf_child_target *target) | 
 | { | 
 |   set_native_target (target); | 
 |   add_target (inf_child_target_info, inf_child_open_target); | 
 | } |