/* Native-dependent code for NetBSD Copyright (C) 2006-2019 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 . */ #include "defs.h" #include "gdbcore.h" #include "inferior.h" #include "regcache.h" #include "regset.h" #include "gdbcmd.h" #include "gdbthread.h" #include "common/gdb_wait.h" #include #include #include #ifdef HAVE_KINFO_GETVMMAP #include #endif #include "elf-bfd.h" #include "nbsd-nat.h" /* Return the name of a file that can be opened to get the symbols for the child process identified by PID. */ char * nbsd_nat_target::pid_to_exec_file (int pid) { ssize_t len; static char buf[PATH_MAX]; char name[PATH_MAX]; size_t buflen; int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC_ARGS; mib[2] = pid; mib[3] = KERN_PROC_PATHNAME; buflen = sizeof buf; if (sysctl (mib, 4, buf, &buflen, NULL, 0) == 0) return buf; xsnprintf (name, PATH_MAX, "/proc/%d/exe", pid); len = readlink (name, buf, PATH_MAX - 1); if (len != -1) { buf[len] = '\0'; return buf; } return NULL; } /* Iterate over all the memory regions in the current inferior, calling FUNC for each memory region. OBFD is passed as the last argument to FUNC. */ int nbsd_nat_target::find_memory_regions (find_memory_region_ftype func, void *obfd) { pid_t pid = inferior_ptid.pid (); struct kinfo_vmentry *vmentl, *kve; uint64_t size; struct cleanup *cleanup; int i; size_t nitems; vmentl = kinfo_getvmmap (pid, &nitems); if (vmentl == NULL) perror_with_name (_("Couldn't fetch VM map entries.")); cleanup = make_cleanup (free, vmentl); for (i = 0; i < nitems; i++) { kve = &vmentl[i]; /* Skip unreadable segments and those where MAP_NOCORE has been set. */ if (!(kve->kve_protection & KVME_PROT_READ) || kve->kve_flags & KVME_FLAG_NOCOREDUMP) continue; /* Skip segments with an invalid type. */ switch (kve->kve_type) { case KVME_TYPE_VNODE: case KVME_TYPE_ANON: case KVME_TYPE_SUBMAP: case KVME_TYPE_OBJECT: break; default: continue; } size = kve->kve_end - kve->kve_start; if (info_verbose) { fprintf_filtered (gdb_stdout, "Save segment, %ld bytes at %s (%c%c%c)\n", (long) size, paddress (target_gdbarch (), kve->kve_start), kve->kve_protection & KVME_PROT_READ ? 'r' : '-', kve->kve_protection & KVME_PROT_WRITE ? 'w' : '-', kve->kve_protection & KVME_PROT_EXEC ? 'x' : '-'); } /* Invoke the callback function to create the corefile segment. Pass MODIFIED as true, we do not know the real modification state. */ func (kve->kve_start, size, kve->kve_protection & KVME_PROT_READ, kve->kve_protection & KVME_PROT_WRITE, kve->kve_protection & KVME_PROT_EXEC, 1, obfd); } do_cleanups (cleanup); return 0; } static int debug_nbsd_lwp; static void show_nbsd_lwp_debug (struct ui_file *file, int from_tty, struct cmd_list_element *c, const char *value) { fprintf_filtered (file, _("Debugging of NetBSD lwp module is %s.\n"), value); } /* Return true if PTID is still active in the inferior. */ bool nbsd_nat_target::thread_alive (ptid_t ptid) { if (ptid.lwp_p ()) { struct ptrace_lwpinfo pl; pl.pl_lwpid = ptid.lwp (); if (ptrace (PT_LWPINFO, ptid.pid (), (caddr_t) &pl, sizeof pl) == -1) return 0; } return 1; } /* Convert PTID to a string. Returns the string in a static buffer. */ const char * nbsd_nat_target::pid_to_str (ptid_t ptid) { lwpid_t lwp; lwp = ptid.lwp (); if (lwp != 0) { static char buf[64]; int pid = ptid.pid (); xsnprintf (buf, sizeof buf, "LWP %d of process %d", lwp, pid); return buf; } return normal_pid_to_str (ptid); } /* Return the name assigned to a thread by an application. Returns the string in a static buffer. */ const char * nbsd_nat_target::thread_name (struct thread_info *thr) { struct kinfo_lwp *kl; pid_t pid = thr->ptid.pid (); lwpid_t lwp = thr->ptid.lwp (); static char buf[KI_LNAMELEN]; int mib[5]; size_t i, nlwps; size_t size; mib[0] = CTL_KERN; mib[1] = KERN_LWP; mib[2] = pid; mib[3] = sizeof(struct kinfo_lwp); mib[4] = 0; if (sysctl(mib, 5, NULL, &size, NULL, 0) == -1 || size == 0) perror_with_name (("sysctl")); mib[4] = size / sizeof(size_t); kl = (struct kinfo_lwp *) xmalloc (size); if (kl == NULL) perror_with_name (("malloc")); if (sysctl(mib, 5, kl, &size, NULL, 0) == -1 || size == 0) perror_with_name (("sysctl")); nlwps = size / sizeof(struct kinfo_lwp); buf[0] = '\0'; for (i = 0; i < nlwps; i++) { if (kl[i].l_lid == lwp) { xsnprintf (buf, sizeof buf, "%s", kl[i].l_name); break; } } xfree(kl); return buf; } /* Enable additional event reporting on new processes. */ static void nbsd_enable_proc_events (pid_t pid) { int events; if (ptrace (PT_GET_EVENT_MASK, pid, (PTRACE_TYPE_ARG3)&events, sizeof (events)) == -1) perror_with_name (("ptrace")); events |= PTRACE_FORK; events |= PTRACE_VFORK; events |= PTRACE_VFORK_DONE; events |= PTRACE_LWP_CREATE; events |= PTRACE_LWP_EXIT; #if notyet events |= PTRACE_POSIX_SPAWN; #endif if (ptrace (PT_SET_EVENT_MASK, pid, (PTRACE_TYPE_ARG3)&events, sizeof (events)) == -1) perror_with_name (("ptrace")); } /* Add threads for any new LWPs in a process. When LWP events are used, this function is only used to detect existing threads when attaching to a process. On older systems, this function is called to discover new threads each time the thread list is updated. */ static void nbsd_add_threads (pid_t pid) { int val; struct ptrace_lwpinfo pl; pl.pl_lwpid = 0; while ((val = ptrace (PT_LWPINFO, pid, (void *)&pl, sizeof(pl))) != -1 && pl.pl_lwpid != 0) { ptid_t ptid = ptid_t (pid, pl.pl_lwpid, 0); if (!in_thread_list (ptid)) { if (inferior_ptid.lwp () == 0) thread_change_ptid (inferior_ptid, ptid); else add_thread (ptid); } } } /* Implement the "to_update_thread_list" target_ops method. */ void nbsd_nat_target::update_thread_list () { prune_threads (); nbsd_add_threads (inferior_ptid.pid ()); } struct nbsd_fork_info { struct nbsd_fork_info *next; ptid_t ptid; }; void nbsd_nat_target::resume (ptid_t ptid, int step, enum gdb_signal signo) { if (debug_nbsd_lwp) fprintf_unfiltered (gdb_stdlog, "NLWP: nbsd_resume for ptid (%d, %ld, %ld)\n", ptid.pid (), ptid.lwp (), ptid.tid ()); if (ptid.pid () == -1) ptid = inferior_ptid; inf_ptrace_target::resume (ptid, step, signo); } /* Wait for the child specified by PTID to do something. Return the process ID of the child, or MINUS_ONE_PTID in case of error; store the status in *OURSTATUS. */ ptid_t nbsd_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus, int target_options) { ptid_t wptid; /* * Always perform polling on exact PID, overwrite the default polling on * WAIT_ANY. * * This avoids events reported in random order reported for FORK / VFORK. * * Polling on traced parent always simplifies the code. */ ptid = inferior_ptid; if (debug_nbsd_lwp) fprintf_unfiltered (gdb_stdlog, "NLWP: calling super_wait (%d, %ld, %ld) target_options=%#x\n", ptid.pid (), ptid.lwp (), ptid.tid (), target_options); wptid = inf_ptrace_target::wait (ptid, ourstatus, target_options); if (debug_nbsd_lwp) fprintf_unfiltered (gdb_stdlog, "NLWP: returned from super_wait (%d, %ld, %ld) target_options=%#x with ourstatus->kind=%d\n", ptid.pid (), ptid.lwp (), ptid.tid (), target_options, ourstatus->kind); if (ourstatus->kind == TARGET_WAITKIND_STOPPED) { ptrace_state_t pst; ptrace_siginfo_t psi, child_psi; int status; pid_t pid, child, wchild; ptid_t child_ptid; lwpid_t lwp; pid = wptid.pid (); // Find the lwp that caused the wait status change if (ptrace(PT_GET_SIGINFO, pid, &psi, sizeof(psi)) == -1) perror_with_name (("ptrace")); /* For whole-process signals pick random thread */ if (psi.psi_lwpid == 0) { // XXX: Is this always valid? lwp = inferior_ptid.lwp (); } else { lwp = psi.psi_lwpid; } wptid = ptid_t (pid, lwp, 0); /* Set LWP in the process */ if (in_thread_list (ptid_t (pid))) { if (debug_nbsd_lwp) fprintf_unfiltered (gdb_stdlog, "NLWP: using LWP %d for first thread\n", lwp); thread_change_ptid (ptid_t (pid), wptid); } if (debug_nbsd_lwp) fprintf_unfiltered (gdb_stdlog, "NLWP: received signal=%d si_code=%d in process=%d lwp=%d\n", psi.psi_siginfo.si_signo, psi.psi_siginfo.si_code, pid, lwp); switch (psi.psi_siginfo.si_signo) { case SIGTRAP: switch (psi.psi_siginfo.si_code) { case TRAP_BRKPT: // lp->stop_reason = TARGET_STOPPED_BY_SW_BREAKPOINT; break; case TRAP_DBREG: // if (hardware_breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc)) // lp->stop_reason = TARGET_STOPPED_BY_HW_BREAKPOINT; // else // lp->stop_reason = TARGET_STOPPED_BY_WATCHPOINT; break; case TRAP_TRACE: // lp->stop_reason = TARGET_STOPPED_BY_SINGLE_STEP; break; case TRAP_SCE: ourstatus->kind = TARGET_WAITKIND_SYSCALL_ENTRY; ourstatus->value.syscall_number = psi.psi_siginfo.si_sysnum; break; case TRAP_SCX: ourstatus->kind = TARGET_WAITKIND_SYSCALL_RETURN; ourstatus->value.syscall_number = psi.psi_siginfo.si_sysnum; break; case TRAP_EXEC: ourstatus->kind = TARGET_WAITKIND_EXECD; ourstatus->value.execd_pathname = xstrdup(pid_to_exec_file (pid)); break; case TRAP_LWP: case TRAP_CHLD: if (ptrace(PT_GET_PROCESS_STATE, pid, &pst, sizeof(pst)) == -1) perror_with_name (("ptrace")); switch (pst.pe_report_event) { case PTRACE_FORK: case PTRACE_VFORK: if (pst.pe_report_event == PTRACE_FORK) ourstatus->kind = TARGET_WAITKIND_FORKED; else ourstatus->kind = TARGET_WAITKIND_VFORKED; child = pst.pe_other_pid; if (debug_nbsd_lwp) fprintf_unfiltered (gdb_stdlog, "NLWP: registered %s event for PID %d\n", (pst.pe_report_event == PTRACE_FORK) ? "FORK" : "VFORK", child); wchild = waitpid (child, &status, 0); if (wchild == -1) perror_with_name (("waitpid")); gdb_assert (wchild == child); if (!WIFSTOPPED(status)) { /* Abnormal situation (SIGKILLed?).. bail out */ ourstatus->kind = TARGET_WAITKIND_SPURIOUS; return wptid; } if (ptrace(PT_GET_SIGINFO, child, &child_psi, sizeof(child_psi)) == -1) perror_with_name (("ptrace")); if (child_psi.psi_siginfo.si_signo != SIGTRAP) { /* Abnormal situation.. bail out */ ourstatus->kind = TARGET_WAITKIND_SPURIOUS; return wptid; } if (child_psi.psi_siginfo.si_code != TRAP_CHLD) { /* Abnormal situation.. bail out */ ourstatus->kind = TARGET_WAITKIND_SPURIOUS; return wptid; } child_ptid = ptid_t (child, child_psi.psi_lwpid, 0); nbsd_enable_proc_events (child_ptid.pid ()); ourstatus->value.related_pid = child_ptid; break; case PTRACE_VFORK_DONE: ourstatus->kind = TARGET_WAITKIND_VFORK_DONE; if (debug_nbsd_lwp) fprintf_unfiltered (gdb_stdlog, "NLWP: reported VFORK_DONE parent=%d child=%d\n", pid, pst.pe_other_pid); break; case PTRACE_LWP_CREATE: wptid = ptid_t (pid, pst.pe_lwp, 0); if (in_thread_list (wptid)) { /* Newborn reported after attach? */ ourstatus->kind = TARGET_WAITKIND_SPURIOUS; return wptid; } if (inferior_ptid.lwp () == 0) thread_change_ptid (inferior_ptid, wptid); else add_thread (wptid); ourstatus->kind = TARGET_WAITKIND_THREAD_CREATED; if (debug_nbsd_lwp) fprintf_unfiltered (gdb_stdlog, "NLWP: created LWP %d\n", pst.pe_lwp); break; case PTRACE_LWP_EXIT: wptid = ptid_t (pid, pst.pe_lwp, 0); if (!in_thread_list (wptid)) { /* Dead child reported after attach? */ ourstatus->kind = TARGET_WAITKIND_SPURIOUS; return wptid; } delete_thread (find_thread_ptid (wptid)); ourstatus->kind = TARGET_WAITKIND_THREAD_EXITED; if (debug_nbsd_lwp) fprintf_unfiltered (gdb_stdlog, "NLWP: exited LWP %d\n", pst.pe_lwp); if (ptrace (PT_CONTINUE, pid, (void *)1, 0) == -1) perror_with_name (("ptrace")); break; } break; } break; } if (debug_nbsd_lwp) fprintf_unfiltered (gdb_stdlog, "NLWP: nbsd_wait returned (%d, %ld, %ld)\n", wptid.pid (), wptid.lwp (), wptid.tid ()); inferior_ptid = wptid; } return wptid; } /* Target hook for follow_fork. On entry and at return inferior_ptid is the ptid of the followed inferior. */ int nbsd_nat_target::follow_fork (int follow_child, int detach_fork) { if (!follow_child && detach_fork) { struct thread_info *tp = inferior_thread (); pid_t child_pid = tp->pending_follow.value.related_pid.pid (); /* Breakpoints have already been detached from the child by infrun.c. */ if (ptrace (PT_DETACH, child_pid, (PTRACE_TYPE_ARG3)1, 0) == -1) perror_with_name (("ptrace")); } return 0; } void nbsd_nat_target::post_startup_inferior (ptid_t pid) { nbsd_enable_proc_events (pid.pid ()); } void nbsd_nat_target::post_attach (int pid) { nbsd_enable_proc_events (pid); nbsd_add_threads (pid); } /* Provide a prototype to silence -Wmissing-prototypes. */ extern initialize_file_ftype _initialize_nbsd_nat; void _initialize_nbsd_nat (void) { add_setshow_boolean_cmd ("nbsd-lwp", class_maintenance, &debug_nbsd_lwp, _("\ Set debugging of NetBSD lwp module."), _("\ Show debugging of NetBSD lwp module."), _("\ Enables printf debugging output."), NULL, &show_nbsd_lwp_debug, &setdebuglist, &showdebuglist); }