|  | /* Machine independent support for QNX Neutrino /proc (process file system) | 
|  | for GDB.  Written by Colin Burgess at QNX Software Systems Limited. | 
|  |  | 
|  | Copyright (C) 2003-2023 Free Software Foundation, Inc. | 
|  |  | 
|  | Contributed by QNX Software Systems Ltd. | 
|  |  | 
|  | This file is part of GDB. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include "defs.h" | 
|  |  | 
|  | #include <fcntl.h> | 
|  | #include <spawn.h> | 
|  | #include <sys/debug.h> | 
|  | #include <sys/procfs.h> | 
|  | #include <sys/neutrino.h> | 
|  | #include <sys/syspage.h> | 
|  | #include <dirent.h> | 
|  | #include <sys/netmgr.h> | 
|  | #include <sys/auxv.h> | 
|  |  | 
|  | #include "gdbcore.h" | 
|  | #include "inferior.h" | 
|  | #include "target.h" | 
|  | #include "objfiles.h" | 
|  | #include "gdbthread.h" | 
|  | #include "nto-tdep.h" | 
|  | #include "command.h" | 
|  | #include "regcache.h" | 
|  | #include "solib.h" | 
|  | #include "inf-child.h" | 
|  | #include "gdbsupport/filestuff.h" | 
|  | #include "gdbsupport/scoped_fd.h" | 
|  |  | 
|  | #define NULL_PID		0 | 
|  | #define _DEBUG_FLAG_TRACE	(_DEBUG_FLAG_TRACE_EXEC|_DEBUG_FLAG_TRACE_RD|\ | 
|  | _DEBUG_FLAG_TRACE_WR|_DEBUG_FLAG_TRACE_MODIFY) | 
|  |  | 
|  | int ctl_fd; | 
|  |  | 
|  | static sighandler_t ofunc; | 
|  |  | 
|  | static procfs_run run; | 
|  |  | 
|  | /* Create the "native" and "procfs" targets.  */ | 
|  |  | 
|  | struct nto_procfs_target : public inf_child_target | 
|  | { | 
|  | void open (const char *arg, int from_tty) override; | 
|  |  | 
|  | void attach (const char *, int) override = 0; | 
|  |  | 
|  | void post_attach (int); | 
|  |  | 
|  | void detach (inferior *, int) override; | 
|  |  | 
|  | void resume (ptid_t, int, enum gdb_signal) override; | 
|  |  | 
|  | ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override; | 
|  |  | 
|  | void fetch_registers (struct regcache *, int) override; | 
|  | void store_registers (struct regcache *, int) override; | 
|  |  | 
|  | enum target_xfer_status xfer_partial (enum target_object object, | 
|  | const char *annex, | 
|  | gdb_byte *readbuf, | 
|  | const gdb_byte *writebuf, | 
|  | ULONGEST offset, ULONGEST len, | 
|  | ULONGEST *xfered_len) override; | 
|  |  | 
|  | void files_info () override; | 
|  |  | 
|  | int insert_breakpoint (struct gdbarch *, struct bp_target_info *) override; | 
|  |  | 
|  | int remove_breakpoint (struct gdbarch *, struct bp_target_info *, | 
|  | enum remove_bp_reason) override; | 
|  |  | 
|  | int can_use_hw_breakpoint (enum bptype, int, int) override; | 
|  |  | 
|  | int insert_hw_breakpoint (struct gdbarch *, struct bp_target_info *) override; | 
|  |  | 
|  | int remove_hw_breakpoint (struct gdbarch *, struct bp_target_info *) override; | 
|  |  | 
|  | int insert_watchpoint (CORE_ADDR, int, enum target_hw_bp_type, | 
|  | struct expression *) override; | 
|  |  | 
|  | int remove_watchpoint (CORE_ADDR, int, enum target_hw_bp_type, | 
|  | struct expression *) override; | 
|  |  | 
|  | bool stopped_by_watchpoint () override; | 
|  |  | 
|  | void kill () override; | 
|  |  | 
|  | void create_inferior (const char *, const std::string &, | 
|  | char **, int) override; | 
|  |  | 
|  | void mourn_inferior () override; | 
|  |  | 
|  | void pass_signals (gdb::array_view<const unsigned char>) override; | 
|  |  | 
|  | bool thread_alive (ptid_t ptid) override; | 
|  |  | 
|  | void update_thread_list () override; | 
|  |  | 
|  | std::string pid_to_str (ptid_t) override; | 
|  |  | 
|  | void interrupt () override; | 
|  |  | 
|  | const char *extra_thread_info (struct thread_info *) override; | 
|  |  | 
|  | const char *pid_to_exec_file (int pid) override; | 
|  | }; | 
|  |  | 
|  | /* For "target native".  */ | 
|  |  | 
|  | static const target_info nto_native_target_info = { | 
|  | "native", | 
|  | N_("QNX Neutrino local process"), | 
|  | N_("QNX Neutrino local process (started by the \"run\" command).") | 
|  | }; | 
|  |  | 
|  | class nto_procfs_target_native final : public nto_procfs_target | 
|  | { | 
|  | const target_info &info () const override | 
|  | { return nto_native_target_info; } | 
|  | }; | 
|  |  | 
|  | /* For "target procfs <node>".  */ | 
|  |  | 
|  | static const target_info nto_procfs_target_info = { | 
|  | "procfs", | 
|  | N_("QNX Neutrino local or remote process"), | 
|  | N_("QNX Neutrino process.  target procfs NODE") | 
|  | }; | 
|  |  | 
|  | struct nto_procfs_target_procfs final : public nto_procfs_target | 
|  | { | 
|  | const target_info &info () const override | 
|  | { return nto_procfs_target_info; } | 
|  | }; | 
|  |  | 
|  | static ptid_t do_attach (ptid_t ptid); | 
|  |  | 
|  | /* These two globals are only ever set in procfs_open_1, but are | 
|  | referenced elsewhere.  'nto_procfs_node' is a flag used to say | 
|  | whether we are local, or we should get the current node descriptor | 
|  | for the remote QNX node.  */ | 
|  | static char *nodestr; | 
|  | static unsigned nto_procfs_node = ND_LOCAL_NODE; | 
|  |  | 
|  | /* Return the current QNX Node, or error out.  This is a simple | 
|  | wrapper for the netmgr_strtond() function.  The reason this | 
|  | is required is because QNX node descriptors are transient so | 
|  | we have to re-acquire them every time.  */ | 
|  | static unsigned | 
|  | nto_node (void) | 
|  | { | 
|  | unsigned node; | 
|  |  | 
|  | if (ND_NODE_CMP (nto_procfs_node, ND_LOCAL_NODE) == 0 | 
|  | || nodestr == NULL) | 
|  | return ND_LOCAL_NODE; | 
|  |  | 
|  | node = netmgr_strtond (nodestr, 0); | 
|  | if (node == -1) | 
|  | error (_("Lost the QNX node.  Debug session probably over.")); | 
|  |  | 
|  | return (node); | 
|  | } | 
|  |  | 
|  | static enum gdb_osabi | 
|  | procfs_is_nto_target (bfd *abfd) | 
|  | { | 
|  | return GDB_OSABI_QNXNTO; | 
|  | } | 
|  |  | 
|  | /* This is called when we call 'target native' or 'target procfs | 
|  | <arg>' from the (gdb) prompt.  For QNX6 (nto), the only valid arg | 
|  | will be a QNX node string, eg: "/net/some_node".  If arg is not a | 
|  | valid QNX node, we will default to local.  */ | 
|  | void | 
|  | nto_procfs_target::open (const char *arg, int from_tty) | 
|  | { | 
|  | char *endstr; | 
|  | char buffer[50]; | 
|  | int total_size; | 
|  | procfs_sysinfo *sysinfo; | 
|  | char nto_procfs_path[PATH_MAX]; | 
|  |  | 
|  | /* Offer to kill previous inferiors before opening this target.  */ | 
|  | target_preopen (from_tty); | 
|  |  | 
|  | nto_is_nto_target = procfs_is_nto_target; | 
|  |  | 
|  | /* Set the default node used for spawning to this one, | 
|  | and only override it if there is a valid arg.  */ | 
|  |  | 
|  | xfree (nodestr); | 
|  | nodestr = NULL; | 
|  |  | 
|  | nto_procfs_node = ND_LOCAL_NODE; | 
|  | nodestr = (arg != NULL) ? xstrdup (arg) : NULL; | 
|  |  | 
|  | if (nodestr) | 
|  | { | 
|  | nto_procfs_node = netmgr_strtond (nodestr, &endstr); | 
|  | if (nto_procfs_node == -1) | 
|  | { | 
|  | if (errno == ENOTSUP) | 
|  | gdb_printf ("QNX Net Manager not found.\n"); | 
|  | gdb_printf ("Invalid QNX node %s: error %d (%s).\n", nodestr, | 
|  | errno, safe_strerror (errno)); | 
|  | xfree (nodestr); | 
|  | nodestr = NULL; | 
|  | nto_procfs_node = ND_LOCAL_NODE; | 
|  | } | 
|  | else if (*endstr) | 
|  | { | 
|  | if (*(endstr - 1) == '/') | 
|  | *(endstr - 1) = 0; | 
|  | else | 
|  | *endstr = 0; | 
|  | } | 
|  | } | 
|  | snprintf (nto_procfs_path, PATH_MAX - 1, "%s%s", | 
|  | (nodestr != NULL) ? nodestr : "", "/proc"); | 
|  |  | 
|  | scoped_fd fd (open (nto_procfs_path, O_RDONLY)); | 
|  | if (fd.get () == -1) | 
|  | { | 
|  | gdb_printf ("Error opening %s : %d (%s)\n", nto_procfs_path, errno, | 
|  | safe_strerror (errno)); | 
|  | error (_("Invalid procfs arg")); | 
|  | } | 
|  |  | 
|  | sysinfo = (void *) buffer; | 
|  | if (devctl (fd.get (), DCMD_PROC_SYSINFO, sysinfo, sizeof buffer, 0) != EOK) | 
|  | { | 
|  | gdb_printf ("Error getting size: %d (%s)\n", errno, | 
|  | safe_strerror (errno)); | 
|  | error (_("Devctl failed.")); | 
|  | } | 
|  | else | 
|  | { | 
|  | total_size = sysinfo->total_size; | 
|  | sysinfo = alloca (total_size); | 
|  | if (sysinfo == NULL) | 
|  | { | 
|  | gdb_printf ("Memory error: %d (%s)\n", errno, | 
|  | safe_strerror (errno)); | 
|  | error (_("alloca failed.")); | 
|  | } | 
|  | else | 
|  | { | 
|  | if (devctl (fd.get (), DCMD_PROC_SYSINFO, sysinfo, total_size, 0) | 
|  | != EOK) | 
|  | { | 
|  | gdb_printf ("Error getting sysinfo: %d (%s)\n", errno, | 
|  | safe_strerror (errno)); | 
|  | error (_("Devctl failed.")); | 
|  | } | 
|  | else | 
|  | { | 
|  | if (sysinfo->type != | 
|  | nto_map_arch_to_cputype | 
|  | (gdbarch_bfd_arch_info | 
|  | (current_inferior ()->arch ())->arch_name)) | 
|  | error (_("Invalid target CPU.")); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | inf_child_target::open (arg, from_tty); | 
|  | gdb_printf ("Debugging using %s\n", nto_procfs_path); | 
|  | } | 
|  |  | 
|  | static void | 
|  | procfs_set_thread (ptid_t ptid) | 
|  | { | 
|  | pid_t tid; | 
|  |  | 
|  | tid = ptid.tid (); | 
|  | devctl (ctl_fd, DCMD_PROC_CURTHREAD, &tid, sizeof (tid), 0); | 
|  | } | 
|  |  | 
|  | /*  Return true if the thread TH is still alive.  */ | 
|  |  | 
|  | bool | 
|  | nto_procfs_target::thread_alive (ptid_t ptid) | 
|  | { | 
|  | pid_t tid; | 
|  | pid_t pid; | 
|  | procfs_status status; | 
|  | int err; | 
|  |  | 
|  | tid = ptid.tid (); | 
|  | pid = ptid.pid (); | 
|  |  | 
|  | if (kill (pid, 0) == -1) | 
|  | return false; | 
|  |  | 
|  | status.tid = tid; | 
|  | if ((err = devctl (ctl_fd, DCMD_PROC_TIDSTATUS, | 
|  | &status, sizeof (status), 0)) != EOK) | 
|  | return false; | 
|  |  | 
|  | /* Thread is alive or dead but not yet joined, | 
|  | or dead and there is an alive (or dead unjoined) thread with | 
|  | higher tid. | 
|  |  | 
|  | If the tid is not the same as requested, requested tid is dead.  */ | 
|  | return (status.tid == tid) && (status.state != STATE_DEAD); | 
|  | } | 
|  |  | 
|  | static void | 
|  | update_thread_private_data_name (struct thread_info *new_thread, | 
|  | const char *newname) | 
|  | { | 
|  | nto_thread_info *pti = get_nto_thread_info (new_thread); | 
|  |  | 
|  | gdb_assert (newname != NULL); | 
|  | gdb_assert (new_thread != NULL); | 
|  |  | 
|  | if (pti) | 
|  | { | 
|  | pti = new nto_thread_info; | 
|  | new_thread->priv.reset (pti); | 
|  | } | 
|  |  | 
|  | pti->name = newname; | 
|  | } | 
|  |  | 
|  | static void | 
|  | update_thread_private_data (struct thread_info *new_thread, | 
|  | pthread_t tid, int state, int flags) | 
|  | { | 
|  | procfs_info pidinfo; | 
|  | struct _thread_name *tn; | 
|  | procfs_threadctl tctl; | 
|  |  | 
|  | #if _NTO_VERSION > 630 | 
|  | gdb_assert (new_thread != NULL); | 
|  |  | 
|  | if (devctl (ctl_fd, DCMD_PROC_INFO, &pidinfo, | 
|  | sizeof(pidinfo), 0) != EOK) | 
|  | return; | 
|  |  | 
|  | memset (&tctl, 0, sizeof (tctl)); | 
|  | tctl.cmd = _NTO_TCTL_NAME; | 
|  | tn = (struct _thread_name *) (&tctl.data); | 
|  |  | 
|  | /* Fetch name for the given thread.  */ | 
|  | tctl.tid = tid; | 
|  | tn->name_buf_len = sizeof (tctl.data) - sizeof (*tn); | 
|  | tn->new_name_len = -1; /* Getting, not setting.  */ | 
|  | if (devctl (ctl_fd, DCMD_PROC_THREADCTL, &tctl, sizeof (tctl), NULL) != EOK) | 
|  | tn->name_buf[0] = '\0'; | 
|  |  | 
|  | tn->name_buf[_NTO_THREAD_NAME_MAX] = '\0'; | 
|  |  | 
|  | update_thread_private_data_name (new_thread, tn->name_buf); | 
|  |  | 
|  | nto_thread_info *pti = get_nto_thread_info (new_thread); | 
|  | pti->tid = tid; | 
|  | pti->state = state; | 
|  | pti->flags = flags; | 
|  | #endif /* _NTO_VERSION */ | 
|  | } | 
|  |  | 
|  | void | 
|  | nto_procfs_target::update_thread_list () | 
|  | { | 
|  | procfs_status status; | 
|  | pid_t pid; | 
|  | ptid_t ptid; | 
|  | pthread_t tid; | 
|  | struct thread_info *new_thread; | 
|  |  | 
|  | if (ctl_fd == -1) | 
|  | return; | 
|  |  | 
|  | prune_threads (); | 
|  |  | 
|  | pid = current_inferior ()->pid; | 
|  |  | 
|  | status.tid = 1; | 
|  |  | 
|  | for (tid = 1;; ++tid) | 
|  | { | 
|  | if (status.tid == tid | 
|  | && (devctl (ctl_fd, DCMD_PROC_TIDSTATUS, &status, sizeof (status), 0) | 
|  | != EOK)) | 
|  | break; | 
|  | if (status.tid != tid) | 
|  | /* The reason why this would not be equal is that devctl might have | 
|  | returned different tid, meaning the requested tid no longer exists | 
|  | (e.g. thread exited).  */ | 
|  | continue; | 
|  | ptid = ptid_t (pid, 0, tid); | 
|  | new_thread = this->find_thread (ptid); | 
|  | if (!new_thread) | 
|  | new_thread = add_thread (ptid); | 
|  | update_thread_private_data (new_thread, tid, status.state, 0); | 
|  | status.tid++; | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | static void | 
|  | procfs_pidlist (const char *args, int from_tty) | 
|  | { | 
|  | struct dirent *dirp = NULL; | 
|  | char buf[PATH_MAX]; | 
|  | procfs_info *pidinfo = NULL; | 
|  | procfs_debuginfo *info = NULL; | 
|  | procfs_status *status = NULL; | 
|  | pid_t num_threads = 0; | 
|  | pid_t pid; | 
|  | char name[512]; | 
|  | char procfs_dir[PATH_MAX]; | 
|  |  | 
|  | snprintf (procfs_dir, sizeof (procfs_dir), "%s%s", | 
|  | (nodestr != NULL) ? nodestr : "", "/proc"); | 
|  |  | 
|  | gdb_dir_up dp (opendir (procfs_dir)); | 
|  | if (dp == NULL) | 
|  | { | 
|  | gdb_printf (gdb_stderr, "failed to opendir \"%s\" - %d (%s)", | 
|  | procfs_dir, errno, safe_strerror (errno)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Start scan at first pid.  */ | 
|  | rewinddir (dp.get ()); | 
|  |  | 
|  | do | 
|  | { | 
|  | /* Get the right pid and procfs path for the pid.  */ | 
|  | do | 
|  | { | 
|  | dirp = readdir (dp.get ()); | 
|  | if (dirp == NULL) | 
|  | return; | 
|  | snprintf (buf, sizeof (buf), "%s%s/%s/as", | 
|  | (nodestr != NULL) ? nodestr : "", | 
|  | "/proc", dirp->d_name); | 
|  | pid = atoi (dirp->d_name); | 
|  | } | 
|  | while (pid == 0); | 
|  |  | 
|  | /* Open the procfs path.  */ | 
|  | scoped_fd fd (open (buf, O_RDONLY)); | 
|  | if (fd.get () == -1) | 
|  | { | 
|  | gdb_printf (gdb_stderr, "failed to open %s - %d (%s)\n", | 
|  | buf, errno, safe_strerror (errno)); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | pidinfo = (procfs_info *) buf; | 
|  | if (devctl (fd.get (), DCMD_PROC_INFO, pidinfo, sizeof (buf), 0) != EOK) | 
|  | { | 
|  | gdb_printf (gdb_stderr, | 
|  | "devctl DCMD_PROC_INFO failed - %d (%s)\n", | 
|  | errno, safe_strerror (errno)); | 
|  | break; | 
|  | } | 
|  | num_threads = pidinfo->num_threads; | 
|  |  | 
|  | info = (procfs_debuginfo *) buf; | 
|  | if (devctl (fd.get (), DCMD_PROC_MAPDEBUG_BASE, info, sizeof (buf), 0) | 
|  | != EOK) | 
|  | strcpy (name, "unavailable"); | 
|  | else | 
|  | strcpy (name, info->path); | 
|  |  | 
|  | /* Collect state info on all the threads.  */ | 
|  | status = (procfs_status *) buf; | 
|  | for (status->tid = 1; status->tid <= num_threads; status->tid++) | 
|  | { | 
|  | const int err | 
|  | = devctl (fd.get (), DCMD_PROC_TIDSTATUS, status, sizeof (buf), 0); | 
|  | gdb_printf ("%s - %d", name, pid); | 
|  | if (err == EOK && status->tid != 0) | 
|  | gdb_printf ("/%d\n", status->tid); | 
|  | else | 
|  | { | 
|  | gdb_printf ("\n"); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | while (dirp != NULL); | 
|  | } | 
|  |  | 
|  | static void | 
|  | procfs_meminfo (const char *args, int from_tty) | 
|  | { | 
|  | procfs_mapinfo *mapinfos = NULL; | 
|  | static int num_mapinfos = 0; | 
|  | procfs_mapinfo *mapinfo_p, *mapinfo_p2; | 
|  | int flags = ~0, err, num, i, j; | 
|  |  | 
|  | struct | 
|  | { | 
|  | procfs_debuginfo info; | 
|  | char buff[_POSIX_PATH_MAX]; | 
|  | } map; | 
|  |  | 
|  | struct info | 
|  | { | 
|  | unsigned addr; | 
|  | unsigned size; | 
|  | unsigned flags; | 
|  | unsigned debug_vaddr; | 
|  | unsigned long long offset; | 
|  | }; | 
|  |  | 
|  | struct printinfo | 
|  | { | 
|  | unsigned long long ino; | 
|  | unsigned dev; | 
|  | struct info text; | 
|  | struct info data; | 
|  | char name[256]; | 
|  | } printme; | 
|  |  | 
|  | /* Get the number of map entrys.  */ | 
|  | err = devctl (ctl_fd, DCMD_PROC_MAPINFO, NULL, 0, &num); | 
|  | if (err != EOK) | 
|  | { | 
|  | printf ("failed devctl num mapinfos - %d (%s)\n", err, | 
|  | safe_strerror (err)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | mapinfos = XNEWVEC (procfs_mapinfo, num); | 
|  |  | 
|  | num_mapinfos = num; | 
|  | mapinfo_p = mapinfos; | 
|  |  | 
|  | /* Fill the map entrys.  */ | 
|  | err = devctl (ctl_fd, DCMD_PROC_MAPINFO, mapinfo_p, num | 
|  | * sizeof (procfs_mapinfo), &num); | 
|  | if (err != EOK) | 
|  | { | 
|  | printf ("failed devctl mapinfos - %d (%s)\n", err, safe_strerror (err)); | 
|  | xfree (mapinfos); | 
|  | return; | 
|  | } | 
|  |  | 
|  | num = std::min (num, num_mapinfos); | 
|  |  | 
|  | /* Run through the list of mapinfos, and store the data and text info | 
|  | so we can print it at the bottom of the loop.  */ | 
|  | for (mapinfo_p = mapinfos, i = 0; i < num; i++, mapinfo_p++) | 
|  | { | 
|  | if (!(mapinfo_p->flags & flags)) | 
|  | mapinfo_p->ino = 0; | 
|  |  | 
|  | if (mapinfo_p->ino == 0)	/* Already visited.  */ | 
|  | continue; | 
|  |  | 
|  | map.info.vaddr = mapinfo_p->vaddr; | 
|  |  | 
|  | err = devctl (ctl_fd, DCMD_PROC_MAPDEBUG, &map, sizeof (map), 0); | 
|  | if (err != EOK) | 
|  | continue; | 
|  |  | 
|  | memset (&printme, 0, sizeof printme); | 
|  | printme.dev = mapinfo_p->dev; | 
|  | printme.ino = mapinfo_p->ino; | 
|  | printme.text.addr = mapinfo_p->vaddr; | 
|  | printme.text.size = mapinfo_p->size; | 
|  | printme.text.flags = mapinfo_p->flags; | 
|  | printme.text.offset = mapinfo_p->offset; | 
|  | printme.text.debug_vaddr = map.info.vaddr; | 
|  | strcpy (printme.name, map.info.path); | 
|  |  | 
|  | /* Check for matching data.  */ | 
|  | for (mapinfo_p2 = mapinfos, j = 0; j < num; j++, mapinfo_p2++) | 
|  | { | 
|  | if (mapinfo_p2->vaddr != mapinfo_p->vaddr | 
|  | && mapinfo_p2->ino == mapinfo_p->ino | 
|  | && mapinfo_p2->dev == mapinfo_p->dev) | 
|  | { | 
|  | map.info.vaddr = mapinfo_p2->vaddr; | 
|  | err = | 
|  | devctl (ctl_fd, DCMD_PROC_MAPDEBUG, &map, sizeof (map), 0); | 
|  | if (err != EOK) | 
|  | continue; | 
|  |  | 
|  | if (strcmp (map.info.path, printme.name)) | 
|  | continue; | 
|  |  | 
|  | /* Lower debug_vaddr is always text, if necessary, swap.  */ | 
|  | if ((int) map.info.vaddr < (int) printme.text.debug_vaddr) | 
|  | { | 
|  | memcpy (&(printme.data), &(printme.text), | 
|  | sizeof (printme.data)); | 
|  | printme.text.addr = mapinfo_p2->vaddr; | 
|  | printme.text.size = mapinfo_p2->size; | 
|  | printme.text.flags = mapinfo_p2->flags; | 
|  | printme.text.offset = mapinfo_p2->offset; | 
|  | printme.text.debug_vaddr = map.info.vaddr; | 
|  | } | 
|  | else | 
|  | { | 
|  | printme.data.addr = mapinfo_p2->vaddr; | 
|  | printme.data.size = mapinfo_p2->size; | 
|  | printme.data.flags = mapinfo_p2->flags; | 
|  | printme.data.offset = mapinfo_p2->offset; | 
|  | printme.data.debug_vaddr = map.info.vaddr; | 
|  | } | 
|  | mapinfo_p2->ino = 0; | 
|  | } | 
|  | } | 
|  | mapinfo_p->ino = 0; | 
|  |  | 
|  | gdb_printf ("%s\n", printme.name); | 
|  | gdb_printf ("\ttext=%08x bytes @ 0x%08x\n", printme.text.size, | 
|  | printme.text.addr); | 
|  | gdb_printf ("\t\tflags=%08x\n", printme.text.flags); | 
|  | gdb_printf ("\t\tdebug=%08x\n", printme.text.debug_vaddr); | 
|  | gdb_printf ("\t\toffset=%s\n", phex (printme.text.offset, 8)); | 
|  | if (printme.data.size) | 
|  | { | 
|  | gdb_printf ("\tdata=%08x bytes @ 0x%08x\n", printme.data.size, | 
|  | printme.data.addr); | 
|  | gdb_printf ("\t\tflags=%08x\n", printme.data.flags); | 
|  | gdb_printf ("\t\tdebug=%08x\n", printme.data.debug_vaddr); | 
|  | gdb_printf ("\t\toffset=%s\n", phex (printme.data.offset, 8)); | 
|  | } | 
|  | gdb_printf ("\tdev=0x%x\n", printme.dev); | 
|  | gdb_printf ("\tino=0x%x\n", (unsigned int) printme.ino); | 
|  | } | 
|  | xfree (mapinfos); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Print status information about what we're accessing.  */ | 
|  | void | 
|  | nto_procfs_target::files_info () | 
|  | { | 
|  | struct inferior *inf = current_inferior (); | 
|  |  | 
|  | gdb_printf ("\tUsing the running image of %s %s via %s.\n", | 
|  | inf->attach_flag ? "attached" : "child", | 
|  | target_pid_to_str (ptid_t (inf->pid)).c_str (), | 
|  | (nodestr != NULL) ? nodestr : "local node"); | 
|  | } | 
|  |  | 
|  | /* Target to_pid_to_exec_file implementation.  */ | 
|  |  | 
|  | const char * | 
|  | nto_procfs_target::pid_to_exec_file (const int pid) | 
|  | { | 
|  | int proc_fd; | 
|  | static char proc_path[PATH_MAX]; | 
|  | ssize_t rd; | 
|  |  | 
|  | /* Read exe file name.  */ | 
|  | snprintf (proc_path, sizeof (proc_path), "%s/proc/%d/exefile", | 
|  | (nodestr != NULL) ? nodestr : "", pid); | 
|  | proc_fd = open (proc_path, O_RDONLY); | 
|  | if (proc_fd == -1) | 
|  | return NULL; | 
|  |  | 
|  | rd = read (proc_fd, proc_path, sizeof (proc_path) - 1); | 
|  | close (proc_fd); | 
|  | if (rd <= 0) | 
|  | { | 
|  | proc_path[0] = '\0'; | 
|  | return NULL; | 
|  | } | 
|  | proc_path[rd] = '\0'; | 
|  | return proc_path; | 
|  | } | 
|  |  | 
|  | /* Attach to process PID, then initialize for debugging it.  */ | 
|  | void | 
|  | nto_procfs_target::attach (const char *args, int from_tty) | 
|  | { | 
|  | int pid; | 
|  | struct inferior *inf; | 
|  |  | 
|  | pid = parse_pid_to_attach (args); | 
|  |  | 
|  | if (pid == getpid ()) | 
|  | error (_("Attaching GDB to itself is not a good idea...")); | 
|  |  | 
|  | target_announce_attach (from_tty, pid); | 
|  |  | 
|  | ptid_t ptid = do_attach (ptid_t (pid)); | 
|  | inf = current_inferior (); | 
|  | inferior_appeared (inf, pid); | 
|  | inf->attach_flag = true; | 
|  |  | 
|  | if (!inf->target_is_pushed (ops)) | 
|  | inf->push_target (ops); | 
|  |  | 
|  | update_thread_list (); | 
|  |  | 
|  | switch_to_thread (this->find_thread (ptid)); | 
|  | } | 
|  |  | 
|  | void | 
|  | nto_procfs_target::post_attach (pid_t pid) | 
|  | { | 
|  | if (current_program_space->exec_bfd ()) | 
|  | solib_create_inferior_hook (0); | 
|  | } | 
|  |  | 
|  | static ptid_t | 
|  | do_attach (ptid_t ptid) | 
|  | { | 
|  | procfs_status status; | 
|  | struct sigevent event; | 
|  | char path[PATH_MAX]; | 
|  |  | 
|  | snprintf (path, PATH_MAX - 1, "%s%s/%d/as", | 
|  | (nodestr != NULL) ? nodestr : "", "/proc", ptid.pid ()); | 
|  | ctl_fd = open (path, O_RDWR); | 
|  | if (ctl_fd == -1) | 
|  | error (_("Couldn't open proc file %s, error %d (%s)"), path, errno, | 
|  | safe_strerror (errno)); | 
|  | if (devctl (ctl_fd, DCMD_PROC_STOP, &status, sizeof (status), 0) != EOK) | 
|  | error (_("Couldn't stop process")); | 
|  |  | 
|  | /* Define a sigevent for process stopped notification.  */ | 
|  | event.sigev_notify = SIGEV_SIGNAL_THREAD; | 
|  | event.sigev_signo = SIGUSR1; | 
|  | event.sigev_code = 0; | 
|  | event.sigev_value.sival_ptr = NULL; | 
|  | event.sigev_priority = -1; | 
|  | devctl (ctl_fd, DCMD_PROC_EVENT, &event, sizeof (event), 0); | 
|  |  | 
|  | if (devctl (ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status), 0) == EOK | 
|  | && status.flags & _DEBUG_FLAG_STOPPED) | 
|  | SignalKill (nto_node (), ptid.pid (), 0, SIGCONT, 0, 0); | 
|  | nto_init_solib_absolute_prefix (); | 
|  | return ptid_t (ptid.pid (), 0, status.tid); | 
|  | } | 
|  |  | 
|  | /* Ask the user what to do when an interrupt is received.  */ | 
|  | static void | 
|  | interrupt_query (void) | 
|  | { | 
|  | if (query (_("Interrupted while waiting for the program.\n\ | 
|  | Give up (and stop debugging it)? "))) | 
|  | { | 
|  | target_mourn_inferior (inferior_ptid); | 
|  | quit (); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* The user typed ^C twice.  */ | 
|  | static void | 
|  | nto_handle_sigint_twice (int signo) | 
|  | { | 
|  | signal (signo, ofunc); | 
|  | interrupt_query (); | 
|  | signal (signo, nto_handle_sigint_twice); | 
|  | } | 
|  |  | 
|  | static void | 
|  | nto_handle_sigint (int signo) | 
|  | { | 
|  | /* If this doesn't work, try more severe steps.  */ | 
|  | signal (signo, nto_handle_sigint_twice); | 
|  |  | 
|  | target_interrupt (); | 
|  | } | 
|  |  | 
|  | sptid_t | 
|  | nto_procfs_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus, | 
|  | target_wait_flags options) | 
|  | { | 
|  | sigset_t set; | 
|  | siginfo_t info; | 
|  | procfs_status status; | 
|  | static int exit_signo = 0;	/* To track signals that cause termination.  */ | 
|  |  | 
|  | ourstatus->set_spurious (); | 
|  |  | 
|  | if (inferior_ptid == null_ptid) | 
|  | { | 
|  | ourstatus->set_stopped (GDB_SIGNAL_0); | 
|  | exit_signo = 0; | 
|  | return null_ptid; | 
|  | } | 
|  |  | 
|  | sigemptyset (&set); | 
|  | sigaddset (&set, SIGUSR1); | 
|  |  | 
|  | devctl (ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status), 0); | 
|  | while (!(status.flags & _DEBUG_FLAG_ISTOP)) | 
|  | { | 
|  | ofunc = signal (SIGINT, nto_handle_sigint); | 
|  | sigwaitinfo (&set, &info); | 
|  | signal (SIGINT, ofunc); | 
|  | devctl (ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status), 0); | 
|  | } | 
|  |  | 
|  | nto_inferior_data (NULL)->stopped_flags = status.flags; | 
|  | nto_inferior_data (NULL)->stopped_pc = status.ip; | 
|  |  | 
|  | if (status.flags & _DEBUG_FLAG_SSTEP) | 
|  | ourstatus->set_stopped (GDB_SIGNAL_TRAP); | 
|  | /* Was it a breakpoint?  */ | 
|  | else if (status.flags & _DEBUG_FLAG_TRACE) | 
|  | ourstatus->set_stopped (GDB_SIGNAL_TRAP); | 
|  | else if (status.flags & _DEBUG_FLAG_ISTOP) | 
|  | { | 
|  | switch (status.why) | 
|  | { | 
|  | case _DEBUG_WHY_SIGNALLED: | 
|  | ourstatus->set_stopped (gdb_signal_from_host (status.info.si_signo)); | 
|  | exit_signo = 0; | 
|  | break; | 
|  | case _DEBUG_WHY_FAULTED: | 
|  | if (status.info.si_signo == SIGTRAP) | 
|  | { | 
|  | ourstatus->set_stopped (0); | 
|  | exit_signo = 0; | 
|  | } | 
|  | else | 
|  | { | 
|  | ourstatus->set_stopped | 
|  | (gdb_signal_from_host (status.info.si_signo)); | 
|  | exit_signo = ourstatus->sig (); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case _DEBUG_WHY_TERMINATED: | 
|  | { | 
|  | int waitval = 0; | 
|  |  | 
|  | waitpid (inferior_ptid.pid (), &waitval, WNOHANG); | 
|  | if (exit_signo) | 
|  | { | 
|  | /* Abnormal death.  */ | 
|  | ourstatus->set_signalled (exit_signo); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Normal death.  */ | 
|  | ourstatus->set_exited (WEXITSTATUS (waitval)); | 
|  | } | 
|  | exit_signo = 0; | 
|  | break; | 
|  | } | 
|  |  | 
|  | case _DEBUG_WHY_REQUESTED: | 
|  | /* We are assuming a requested stop is due to a SIGINT.  */ | 
|  | ourstatus->set_stopped (GDB_SIGNAL_INT); | 
|  | exit_signo = 0; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | return ptid_t (status.pid, 0, status.tid); | 
|  | } | 
|  |  | 
|  | /* Read the current values of the inferior's registers, both the | 
|  | general register set and floating point registers (if supported) | 
|  | and update gdb's idea of their current values.  */ | 
|  | void | 
|  | nto_procfs_target::fetch_registers (struct regcache *regcache, int regno) | 
|  | { | 
|  | union | 
|  | { | 
|  | procfs_greg greg; | 
|  | procfs_fpreg fpreg; | 
|  | procfs_altreg altreg; | 
|  | } | 
|  | reg; | 
|  | int regsize; | 
|  |  | 
|  | procfs_set_thread (regcache->ptid ()); | 
|  | if (devctl (ctl_fd, DCMD_PROC_GETGREG, ®, sizeof (reg), ®size) == EOK) | 
|  | nto_supply_gregset (regcache, (char *) ®.greg); | 
|  | if (devctl (ctl_fd, DCMD_PROC_GETFPREG, ®, sizeof (reg), ®size) | 
|  | == EOK) | 
|  | nto_supply_fpregset (regcache, (char *) ®.fpreg); | 
|  | if (devctl (ctl_fd, DCMD_PROC_GETALTREG, ®, sizeof (reg), ®size) | 
|  | == EOK) | 
|  | nto_supply_altregset (regcache, (char *) ®.altreg); | 
|  | } | 
|  |  | 
|  | /* Helper for procfs_xfer_partial that handles memory transfers. | 
|  | Arguments are like target_xfer_partial.  */ | 
|  |  | 
|  | static enum target_xfer_status | 
|  | procfs_xfer_memory (gdb_byte *readbuf, const gdb_byte *writebuf, | 
|  | ULONGEST memaddr, ULONGEST len, ULONGEST *xfered_len) | 
|  | { | 
|  | int nbytes; | 
|  |  | 
|  | if (lseek (ctl_fd, (off_t) memaddr, SEEK_SET) != (off_t) memaddr) | 
|  | return TARGET_XFER_E_IO; | 
|  |  | 
|  | if (writebuf != NULL) | 
|  | nbytes = write (ctl_fd, writebuf, len); | 
|  | else | 
|  | nbytes = read (ctl_fd, readbuf, len); | 
|  | if (nbytes <= 0) | 
|  | return TARGET_XFER_E_IO; | 
|  | *xfered_len = nbytes; | 
|  | return TARGET_XFER_OK; | 
|  | } | 
|  |  | 
|  | /* Target to_xfer_partial implementation.  */ | 
|  |  | 
|  | enum target_xfer_status | 
|  | nto_procfs_target::xfer_partial (enum target_object object, | 
|  | const char *annex, gdb_byte *readbuf, | 
|  | const gdb_byte *writebuf, ULONGEST offset, | 
|  | ULONGEST len, ULONGEST *xfered_len) | 
|  | { | 
|  | switch (object) | 
|  | { | 
|  | case TARGET_OBJECT_MEMORY: | 
|  | return procfs_xfer_memory (readbuf, writebuf, offset, len, xfered_len); | 
|  | case TARGET_OBJECT_AUXV: | 
|  | if (readbuf != NULL) | 
|  | { | 
|  | int err; | 
|  | CORE_ADDR initial_stack; | 
|  | debug_process_t procinfo; | 
|  | /* For 32-bit architecture, size of auxv_t is 8 bytes.  */ | 
|  | const unsigned int sizeof_auxv_t = sizeof (auxv_t); | 
|  | const unsigned int sizeof_tempbuf = 20 * sizeof_auxv_t; | 
|  | int tempread; | 
|  | gdb_byte *const tempbuf = alloca (sizeof_tempbuf); | 
|  |  | 
|  | if (tempbuf == NULL) | 
|  | return TARGET_XFER_E_IO; | 
|  |  | 
|  | err = devctl (ctl_fd, DCMD_PROC_INFO, &procinfo, | 
|  | sizeof procinfo, 0); | 
|  | if (err != EOK) | 
|  | return TARGET_XFER_E_IO; | 
|  |  | 
|  | initial_stack = procinfo.initial_stack; | 
|  |  | 
|  | /* procfs is always 'self-hosted', no byte-order manipulation.  */ | 
|  | tempread = nto_read_auxv_from_initial_stack (initial_stack, tempbuf, | 
|  | sizeof_tempbuf, | 
|  | sizeof (auxv_t)); | 
|  | tempread = std::min (tempread, len) - offset; | 
|  | memcpy (readbuf, tempbuf + offset, tempread); | 
|  | *xfered_len = tempread; | 
|  | return tempread ? TARGET_XFER_OK : TARGET_XFER_EOF; | 
|  | } | 
|  | /* Fallthru */ | 
|  | default: | 
|  | return this->beneath ()->xfer_partial (object, annex, | 
|  | readbuf, writebuf, offset, len, | 
|  | xfered_len); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Take a program previously attached to and detaches it. | 
|  | The program resumes execution and will no longer stop | 
|  | on signals, etc.  We'd better not have left any breakpoints | 
|  | in the program or it'll die when it hits one.  */ | 
|  | void | 
|  | nto_procfs_target::detach (inferior *inf, int from_tty) | 
|  | { | 
|  | target_announce_detach (); | 
|  |  | 
|  | if (siggnal) | 
|  | SignalKill (nto_node (), inf->pid, 0, 0, 0, 0); | 
|  |  | 
|  | close (ctl_fd); | 
|  | ctl_fd = -1; | 
|  |  | 
|  | switch_to_no_thread (); | 
|  | detach_inferior (inf->pid); | 
|  | init_thread_list (); | 
|  | inf_child_maybe_unpush_target (ops); | 
|  | } | 
|  |  | 
|  | static int | 
|  | procfs_breakpoint (CORE_ADDR addr, int type, int size) | 
|  | { | 
|  | procfs_break brk; | 
|  |  | 
|  | brk.type = type; | 
|  | brk.addr = addr; | 
|  | brk.size = size; | 
|  | errno = devctl (ctl_fd, DCMD_PROC_BREAK, &brk, sizeof (brk), 0); | 
|  | if (errno != EOK) | 
|  | return 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int | 
|  | nto_procfs_target::insert_breakpoint (struct gdbarch *gdbarch, | 
|  | struct bp_target_info *bp_tgt) | 
|  | { | 
|  | bp_tgt->placed_address = bp_tgt->reqstd_address; | 
|  | return procfs_breakpoint (bp_tgt->placed_address, _DEBUG_BREAK_EXEC, 0); | 
|  | } | 
|  |  | 
|  | int | 
|  | nto_procfs_target::remove_breakpoint (struct gdbarch *gdbarch, | 
|  | struct bp_target_info *bp_tgt, | 
|  | enum remove_bp_reason reason) | 
|  | { | 
|  | return procfs_breakpoint (bp_tgt->placed_address, _DEBUG_BREAK_EXEC, -1); | 
|  | } | 
|  |  | 
|  | int | 
|  | nto_procfs_target::insert_hw_breakpoint (struct gdbarch *gdbarch, | 
|  | struct bp_target_info *bp_tgt) | 
|  | { | 
|  | bp_tgt->placed_address = bp_tgt->reqstd_address; | 
|  | return procfs_breakpoint (bp_tgt->placed_address, | 
|  | _DEBUG_BREAK_EXEC | _DEBUG_BREAK_HW, 0); | 
|  | } | 
|  |  | 
|  | int | 
|  | nto_procfs_target::remove_hw_breakpoint (struct gdbarch *gdbarch, | 
|  | struct bp_target_info *bp_tgt) | 
|  | { | 
|  | return procfs_breakpoint (bp_tgt->placed_address, | 
|  | _DEBUG_BREAK_EXEC | _DEBUG_BREAK_HW, -1); | 
|  | } | 
|  |  | 
|  | void | 
|  | nto_procfs_target::resume (ptid_t ptid, int step, enum gdb_signal signo) | 
|  | { | 
|  | int signal_to_pass; | 
|  | procfs_status status; | 
|  | sigset_t *run_fault = (sigset_t *) (void *) &run.fault; | 
|  |  | 
|  | if (inferior_ptid == null_ptid) | 
|  | return; | 
|  |  | 
|  | procfs_set_thread (ptid == minus_one_ptid ? inferior_ptid : | 
|  | ptid); | 
|  |  | 
|  | run.flags = _DEBUG_RUN_FAULT | _DEBUG_RUN_TRACE; | 
|  | if (step) | 
|  | run.flags |= _DEBUG_RUN_STEP; | 
|  |  | 
|  | sigemptyset (run_fault); | 
|  | sigaddset (run_fault, FLTBPT); | 
|  | sigaddset (run_fault, FLTTRACE); | 
|  | sigaddset (run_fault, FLTILL); | 
|  | sigaddset (run_fault, FLTPRIV); | 
|  | sigaddset (run_fault, FLTBOUNDS); | 
|  | sigaddset (run_fault, FLTIOVF); | 
|  | sigaddset (run_fault, FLTIZDIV); | 
|  | sigaddset (run_fault, FLTFPE); | 
|  | /* Peter V will be changing this at some point.  */ | 
|  | sigaddset (run_fault, FLTPAGE); | 
|  |  | 
|  | run.flags |= _DEBUG_RUN_ARM; | 
|  |  | 
|  | signal_to_pass = gdb_signal_to_host (signo); | 
|  |  | 
|  | if (signal_to_pass) | 
|  | { | 
|  | devctl (ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status), 0); | 
|  | signal_to_pass = gdb_signal_to_host (signo); | 
|  | if (status.why & (_DEBUG_WHY_SIGNALLED | _DEBUG_WHY_FAULTED)) | 
|  | { | 
|  | if (signal_to_pass != status.info.si_signo) | 
|  | { | 
|  | SignalKill (nto_node (), inferior_ptid.pid (), 0, | 
|  | signal_to_pass, 0, 0); | 
|  | run.flags |= _DEBUG_RUN_CLRFLT | _DEBUG_RUN_CLRSIG; | 
|  | } | 
|  | else		/* Let it kill the program without telling us.  */ | 
|  | sigdelset (&run.trace, signal_to_pass); | 
|  | } | 
|  | } | 
|  | else | 
|  | run.flags |= _DEBUG_RUN_CLRSIG | _DEBUG_RUN_CLRFLT; | 
|  |  | 
|  | errno = devctl (ctl_fd, DCMD_PROC_RUN, &run, sizeof (run), 0); | 
|  | if (errno != EOK) | 
|  | { | 
|  | perror (_("run error!\n")); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | nto_procfs_target::mourn_inferior () | 
|  | { | 
|  | if (inferior_ptid != null_ptid) | 
|  | { | 
|  | SignalKill (nto_node (), inferior_ptid.pid (), 0, SIGKILL, 0, 0); | 
|  | close (ctl_fd); | 
|  | } | 
|  | switch_to_no_thread (); | 
|  | init_thread_list (); | 
|  | inf_child_mourn_inferior (ops); | 
|  | } | 
|  |  | 
|  | /* This function breaks up an argument string into an argument | 
|  | vector suitable for passing to execvp(). | 
|  | E.g., on "run a b c d" this routine would get as input | 
|  | the string "a b c d", and as output it would fill in argv with | 
|  | the four arguments "a", "b", "c", "d".  The only additional | 
|  | functionality is simple quoting.  The gdb command: | 
|  | run a "b c d" f | 
|  | will fill in argv with the three args "a", "b c d", "e".  */ | 
|  | static void | 
|  | breakup_args (char *scratch, char **argv) | 
|  | { | 
|  | char *pp, *cp = scratch; | 
|  | char quoting = 0; | 
|  |  | 
|  | for (;;) | 
|  | { | 
|  | /* Scan past leading separators.  */ | 
|  | quoting = 0; | 
|  | while (*cp == ' ' || *cp == '\t' || *cp == '\n') | 
|  | cp++; | 
|  |  | 
|  | /* Break if at end of string.  */ | 
|  | if (*cp == '\0') | 
|  | break; | 
|  |  | 
|  | /* Take an arg.  */ | 
|  | if (*cp == '"') | 
|  | { | 
|  | cp++; | 
|  | quoting = strchr (cp, '"') ? 1 : 0; | 
|  | } | 
|  |  | 
|  | *argv++ = cp; | 
|  |  | 
|  | /* Scan for next arg separator.  */ | 
|  | pp = cp; | 
|  | if (quoting) | 
|  | cp = strchr (pp, '"'); | 
|  | if ((cp == NULL) || (!quoting)) | 
|  | cp = strchr (pp, ' '); | 
|  | if (cp == NULL) | 
|  | cp = strchr (pp, '\t'); | 
|  | if (cp == NULL) | 
|  | cp = strchr (pp, '\n'); | 
|  |  | 
|  | /* No separators => end of string => break.  */ | 
|  | if (cp == NULL) | 
|  | { | 
|  | pp = cp; | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* Replace the separator with a terminator.  */ | 
|  | *cp++ = '\0'; | 
|  | } | 
|  |  | 
|  | /* Execv requires a null-terminated arg vector.  */ | 
|  | *argv = NULL; | 
|  | } | 
|  |  | 
|  | void | 
|  | nto_procfs_target::create_inferior (const char *exec_file, | 
|  | const std::string &allargs, | 
|  | char **env, int from_tty) | 
|  | { | 
|  | struct inheritance inherit; | 
|  | pid_t pid; | 
|  | int flags, errn; | 
|  | char **argv, *args; | 
|  | const char *in = "", *out = "", *err = ""; | 
|  | int fd, fds[3]; | 
|  | sigset_t set; | 
|  | struct inferior *inf; | 
|  |  | 
|  | argv = xmalloc ((allargs.size () / (unsigned) 2 + 2) * | 
|  | sizeof (*argv)); | 
|  | argv[0] = const_cast<char *> (get_exec_file (1)); | 
|  | if (!argv[0]) | 
|  | { | 
|  | if (exec_file) | 
|  | argv[0] = exec_file; | 
|  | else | 
|  | return; | 
|  | } | 
|  |  | 
|  | args = xstrdup (allargs.c_str ()); | 
|  | breakup_args (args, (exec_file != NULL) ? &argv[1] : &argv[0]); | 
|  |  | 
|  | argv = nto_parse_redirection (argv, &in, &out, &err); | 
|  |  | 
|  | fds[0] = STDIN_FILENO; | 
|  | fds[1] = STDOUT_FILENO; | 
|  | fds[2] = STDERR_FILENO; | 
|  |  | 
|  | /* If the user specified I/O via gdb's --tty= arg, use it, but only | 
|  | if the i/o is not also being specified via redirection.  */ | 
|  | const char *inferior_tty = current_inferior ()->tty (); | 
|  | if (inferior_tty != nullptr) | 
|  | { | 
|  | if (!in[0]) | 
|  | in = inferior_tty; | 
|  | if (!out[0]) | 
|  | out = inferior_tty; | 
|  | if (!err[0]) | 
|  | err = inferior_tty; | 
|  | } | 
|  |  | 
|  | if (in[0]) | 
|  | { | 
|  | fd = open (in, O_RDONLY); | 
|  | if (fd == -1) | 
|  | perror (in); | 
|  | else | 
|  | fds[0] = fd; | 
|  | } | 
|  | if (out[0]) | 
|  | { | 
|  | fd = open (out, O_WRONLY); | 
|  | if (fd == -1) | 
|  | perror (out); | 
|  | else | 
|  | fds[1] = fd; | 
|  | } | 
|  | if (err[0]) | 
|  | { | 
|  | fd = open (err, O_WRONLY); | 
|  | if (fd == -1) | 
|  | perror (err); | 
|  | else | 
|  | fds[2] = fd; | 
|  | } | 
|  |  | 
|  | /* Clear any pending SIGUSR1's but keep the behavior the same.  */ | 
|  | signal (SIGUSR1, signal (SIGUSR1, SIG_IGN)); | 
|  |  | 
|  | sigemptyset (&set); | 
|  | sigaddset (&set, SIGUSR1); | 
|  | sigprocmask (SIG_UNBLOCK, &set, NULL); | 
|  |  | 
|  | memset (&inherit, 0, sizeof (inherit)); | 
|  |  | 
|  | if (ND_NODE_CMP (nto_procfs_node, ND_LOCAL_NODE) != 0) | 
|  | { | 
|  | inherit.nd = nto_node (); | 
|  | inherit.flags |= SPAWN_SETND; | 
|  | inherit.flags &= ~SPAWN_EXEC; | 
|  | } | 
|  | inherit.flags |= SPAWN_SETGROUP | SPAWN_HOLD; | 
|  | inherit.pgroup = SPAWN_NEWPGROUP; | 
|  | pid = spawnp (argv[0], 3, fds, &inherit, argv, | 
|  | ND_NODE_CMP (nto_procfs_node, ND_LOCAL_NODE) == 0 ? env : 0); | 
|  | xfree (args); | 
|  |  | 
|  | sigprocmask (SIG_BLOCK, &set, NULL); | 
|  |  | 
|  | if (pid == -1) | 
|  | error (_("Error spawning %s: %d (%s)"), argv[0], errno, | 
|  | safe_strerror (errno)); | 
|  |  | 
|  | if (fds[0] != STDIN_FILENO) | 
|  | close (fds[0]); | 
|  | if (fds[1] != STDOUT_FILENO) | 
|  | close (fds[1]); | 
|  | if (fds[2] != STDERR_FILENO) | 
|  | close (fds[2]); | 
|  |  | 
|  | ptid_t ptid = do_attach (ptid_t (pid)); | 
|  | update_thread_list (); | 
|  | switch_to_thread (this->find_thread (ptid)); | 
|  |  | 
|  | inf = current_inferior (); | 
|  | inferior_appeared (inf, pid); | 
|  | inf->attach_flag = false; | 
|  |  | 
|  | flags = _DEBUG_FLAG_KLC;	/* Kill-on-Last-Close flag.  */ | 
|  | errn = devctl (ctl_fd, DCMD_PROC_SET_FLAG, &flags, sizeof (flags), 0); | 
|  | if (errn != EOK) | 
|  | { | 
|  | /* FIXME: expected warning?  */ | 
|  | /* warning( "Failed to set Kill-on-Last-Close flag: errno = %d(%s)\n", | 
|  | errn, safe_strerror(errn) ); */ | 
|  | } | 
|  | if (!inf->target_is_pushed (ops)) | 
|  | inf->push_target (ops); | 
|  | target_terminal::init (); | 
|  |  | 
|  | if (current_program_space->exec_bfd () != NULL | 
|  | || (current_program_space->symfile_object_file != NULL | 
|  | && current_program_space->symfile_object_file->obfd != NULL)) | 
|  | solib_create_inferior_hook (0); | 
|  | } | 
|  |  | 
|  | void | 
|  | nto_procfs_target::interrupt () | 
|  | { | 
|  | devctl (ctl_fd, DCMD_PROC_STOP, NULL, 0, 0); | 
|  | } | 
|  |  | 
|  | void | 
|  | nto_procfs_target::kill () | 
|  | { | 
|  | target_mourn_inferior (inferior_ptid); | 
|  | } | 
|  |  | 
|  | /* Fill buf with regset and return devctl cmd to do the setting.  Return | 
|  | -1 if we fail to get the regset.  Store size of regset in regsize.  */ | 
|  | static int | 
|  | get_regset (int regset, char *buf, int bufsize, int *regsize) | 
|  | { | 
|  | int dev_get, dev_set; | 
|  | switch (regset) | 
|  | { | 
|  | case NTO_REG_GENERAL: | 
|  | dev_get = DCMD_PROC_GETGREG; | 
|  | dev_set = DCMD_PROC_SETGREG; | 
|  | break; | 
|  |  | 
|  | case NTO_REG_FLOAT: | 
|  | dev_get = DCMD_PROC_GETFPREG; | 
|  | dev_set = DCMD_PROC_SETFPREG; | 
|  | break; | 
|  |  | 
|  | case NTO_REG_ALT: | 
|  | dev_get = DCMD_PROC_GETALTREG; | 
|  | dev_set = DCMD_PROC_SETALTREG; | 
|  | break; | 
|  |  | 
|  | case NTO_REG_SYSTEM: | 
|  | default: | 
|  | return -1; | 
|  | } | 
|  | if (devctl (ctl_fd, dev_get, buf, bufsize, regsize) != EOK) | 
|  | return -1; | 
|  |  | 
|  | return dev_set; | 
|  | } | 
|  |  | 
|  | void | 
|  | nto_procfs_target::store_registers (struct regcache *regcache, int regno) | 
|  | { | 
|  | union | 
|  | { | 
|  | procfs_greg greg; | 
|  | procfs_fpreg fpreg; | 
|  | procfs_altreg altreg; | 
|  | } | 
|  | reg; | 
|  | unsigned off; | 
|  | int len, regset, regsize, dev_set, err; | 
|  | char *data; | 
|  | ptid_t ptid = regcache->ptid (); | 
|  |  | 
|  | if (ptid == null_ptid) | 
|  | return; | 
|  | procfs_set_thread (ptid); | 
|  |  | 
|  | if (regno == -1) | 
|  | { | 
|  | for (regset = NTO_REG_GENERAL; regset < NTO_REG_END; regset++) | 
|  | { | 
|  | dev_set = get_regset (regset, (char *) ®, | 
|  | sizeof (reg), ®size); | 
|  | if (dev_set == -1) | 
|  | continue; | 
|  |  | 
|  | if (nto_regset_fill (regcache, regset, (char *) ®) == -1) | 
|  | continue; | 
|  |  | 
|  | err = devctl (ctl_fd, dev_set, ®, regsize, 0); | 
|  | if (err != EOK) | 
|  | gdb_printf (gdb_stderr, | 
|  | "Warning unable to write regset %d: %s\n", | 
|  | regno, safe_strerror (err)); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | regset = nto_regset_id (regno); | 
|  | if (regset == -1) | 
|  | return; | 
|  |  | 
|  | dev_set = get_regset (regset, (char *) ®, sizeof (reg), ®size); | 
|  | if (dev_set == -1) | 
|  | return; | 
|  |  | 
|  | len = nto_register_area (regcache->arch (), | 
|  | regno, regset, &off); | 
|  |  | 
|  | if (len < 1) | 
|  | return; | 
|  |  | 
|  | regcache->raw_collect (regno, (char *) ® + off); | 
|  |  | 
|  | err = devctl (ctl_fd, dev_set, ®, regsize, 0); | 
|  | if (err != EOK) | 
|  | gdb_printf (gdb_stderr, | 
|  | "Warning unable to write regset %d: %s\n", regno, | 
|  | safe_strerror (err)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Set list of signals to be handled in the target.  */ | 
|  |  | 
|  | void | 
|  | nto_procfs_target::pass_signals | 
|  | (gdb::array_view<const unsigned char> pass_signals) | 
|  | { | 
|  | int signo; | 
|  |  | 
|  | sigfillset (&run.trace); | 
|  |  | 
|  | for (signo = 1; signo < NSIG; signo++) | 
|  | { | 
|  | int target_signo = gdb_signal_from_host (signo); | 
|  | if (target_signo < pass_signals.size () && pass_signals[target_signo]) | 
|  | sigdelset (&run.trace, signo); | 
|  | } | 
|  | } | 
|  |  | 
|  | std::string | 
|  | nto_procfs_target::pid_to_str (ptid_t ptid) | 
|  | { | 
|  | int pid, tid; | 
|  | struct tidinfo *tip; | 
|  |  | 
|  | pid = ptid.pid (); | 
|  | tid = ptid.tid (); | 
|  |  | 
|  | #if 0				/* NYI */ | 
|  | tip = procfs_thread_info (pid, tid); | 
|  | if (tip != NULL) | 
|  | snprintf (&buf[n], 1023, " (state = 0x%02x)", tip->state); | 
|  | #endif | 
|  |  | 
|  | return string_printf ("process %d", pid); | 
|  | } | 
|  |  | 
|  | /* to_can_run implementation for "target procfs".  Note this really | 
|  | means "can this target be the default run target", which there can | 
|  | be only one, and we make it be "target native" like other ports. | 
|  | "target procfs <node>" wouldn't make sense as default run target, as | 
|  | it needs <node>.  */ | 
|  |  | 
|  | int | 
|  | nto_procfs_target::can_run () | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* "target procfs".  */ | 
|  | static nto_procfs_target_procfs nto_procfs_ops; | 
|  |  | 
|  | /* "target native".  */ | 
|  | static nto_procfs_target_native nto_native_ops; | 
|  |  | 
|  | /* Create the "native" and "procfs" targets.  */ | 
|  |  | 
|  | static void | 
|  | init_procfs_targets (void) | 
|  | { | 
|  | /* Register "target native".  This is the default run target.  */ | 
|  | add_target (nto_native_target_info, inf_child_open_target); | 
|  | set_native_target (&nto_native_ops); | 
|  |  | 
|  | /* Register "target procfs <node>".  */ | 
|  | add_target (nto_procfs_target_info, inf_child_open_target); | 
|  | } | 
|  |  | 
|  | #define OSTYPE_NTO 1 | 
|  |  | 
|  | void _initialize_procfs (); | 
|  | void | 
|  | _initialize_procfs () | 
|  | { | 
|  | sigset_t set; | 
|  |  | 
|  | init_procfs_targets (); | 
|  |  | 
|  | /* We use SIGUSR1 to gain control after we block waiting for a process. | 
|  | We use sigwaitevent to wait.  */ | 
|  | sigemptyset (&set); | 
|  | sigaddset (&set, SIGUSR1); | 
|  | sigprocmask (SIG_BLOCK, &set, NULL); | 
|  |  | 
|  | /* Initially, make sure all signals are reported.  */ | 
|  | sigfillset (&run.trace); | 
|  |  | 
|  | /* Stuff some information.  */ | 
|  | nto_cpuinfo_flags = SYSPAGE_ENTRY (cpuinfo)->flags; | 
|  | nto_cpuinfo_valid = 1; | 
|  |  | 
|  | add_info ("pidlist", procfs_pidlist, _("pidlist")); | 
|  | add_info ("meminfo", procfs_meminfo, _("memory information")); | 
|  |  | 
|  | nto_is_nto_target = procfs_is_nto_target; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | procfs_hw_watchpoint (int addr, int len, enum target_hw_bp_type type) | 
|  | { | 
|  | procfs_break brk; | 
|  |  | 
|  | switch (type) | 
|  | { | 
|  | case hw_read: | 
|  | brk.type = _DEBUG_BREAK_RD; | 
|  | break; | 
|  | case hw_access: | 
|  | brk.type = _DEBUG_BREAK_RW; | 
|  | break; | 
|  | default:			/* Modify.  */ | 
|  | /* FIXME: brk.type = _DEBUG_BREAK_RWM gives EINVAL for some reason.  */ | 
|  | brk.type = _DEBUG_BREAK_RW; | 
|  | } | 
|  | brk.type |= _DEBUG_BREAK_HW;	/* Always ask for HW.  */ | 
|  | brk.addr = addr; | 
|  | brk.size = len; | 
|  |  | 
|  | errno = devctl (ctl_fd, DCMD_PROC_BREAK, &brk, sizeof (brk), 0); | 
|  | if (errno != EOK) | 
|  | { | 
|  | perror (_("Failed to set hardware watchpoint")); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | bool | 
|  | nto_procfs_target::can_use_hw_breakpoint (enum bptype type, | 
|  | int cnt, int othertype) | 
|  | { | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | int | 
|  | nto_procfs_target::remove_hw_watchpoint (CORE_ADDR addr, int len, | 
|  | enum target_hw_bp_type type, | 
|  | struct expression *cond) | 
|  | { | 
|  | return procfs_hw_watchpoint (addr, -1, type); | 
|  | } | 
|  |  | 
|  | int | 
|  | nto_procfs_target::insert_hw_watchpoint (CORE_ADDR addr, int len, | 
|  | enum target_hw_bp_type type, | 
|  | struct expression *cond) | 
|  | { | 
|  | return procfs_hw_watchpoint (addr, len, type); | 
|  | } | 
|  |  | 
|  | bool | 
|  | nto_procfs_target::stopped_by_watchpoint () | 
|  | { | 
|  | /* NOTE: nto_stopped_by_watchpoint will be called ONLY while we are | 
|  | stopped due to a SIGTRAP.  This assumes gdb works in 'all-stop' mode; | 
|  | future gdb versions will likely run in 'non-stop' mode in which case | 
|  | we will have to store/examine statuses per thread in question. | 
|  | Until then, this will work fine.  */ | 
|  |  | 
|  | struct inferior *inf = current_inferior (); | 
|  | struct nto_inferior_data *inf_data; | 
|  |  | 
|  | gdb_assert (inf != NULL); | 
|  |  | 
|  | inf_data = nto_inferior_data (inf); | 
|  |  | 
|  | return inf_data->stopped_flags | 
|  | & (_DEBUG_FLAG_TRACE_RD | 
|  | | _DEBUG_FLAG_TRACE_WR | 
|  | | _DEBUG_FLAG_TRACE_MODIFY); | 
|  | } |