/* Native support code for HPUX PA-RISC.
- Copyright 1986, 1987, 1989, 1990, 1991, 1992, 1993, 1998, 1999,
- 2001
+ Copyright 1986, 1987, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996,
+ 1998, 1999, 2000, 2001
Free Software Foundation, Inc.
Contributed by the Center for Software Science at the
extern CORE_ADDR text_end;
+extern int hpux_has_forked (int pid, int *childpid);
+extern int hpux_has_vforked (int pid, int *childpid);
+extern int hpux_has_execd (int pid, char **execd_pathname);
+extern int hpux_has_syscall_event (int pid, enum target_waitkind *kind,
+ int *syscall_id);
+
static void fetch_register (int);
void
{
CORE_ADDR temp;
- temp = *(CORE_ADDR *)®isters[REGISTER_BYTE (regno)];
+ temp = *(CORE_ADDR *)&deprecated_registers[REGISTER_BYTE (regno)];
/* Set the priv level (stored in the low two bits of the PC. */
temp |= 0x3;
- ttrace_write_reg_64 (inferior_pid, (CORE_ADDR)addr, (CORE_ADDR)&temp);
+ ttrace_write_reg_64 (PIDGET (inferior_ptid), (CORE_ADDR)addr,
+ (CORE_ADDR)&temp);
/* If we fail to write the PC, give a true error instead of
just a warning. */
the high part of IPSW. What will it take for HP to catch a
clue about building sensible interfaces? */
if (regno == IPSW_REGNUM && len == 8)
- *(int *)®isters[REGISTER_BYTE (regno)] = 0;
+ *(int *)&deprecated_registers[REGISTER_BYTE (regno)] = 0;
#endif
for (i = 0; i < len; i += sizeof (int))
{
errno = 0;
- call_ptrace (PT_WUREGS, inferior_pid, (PTRACE_ARG3_TYPE) addr + i,
- *(int *) ®isters[REGISTER_BYTE (regno) + i]);
+ call_ptrace (PT_WUREGS, PIDGET (inferior_ptid),
+ (PTRACE_ARG3_TYPE) addr + i,
+ *(int *) &deprecated_registers[REGISTER_BYTE (regno) + i]);
if (errno != 0)
{
/* Warning, not error, in case we are attached; sometimes
/* Copy an int from the U area to buf. Fill the least
significant end if len != raw_size. */
* (int *) &buf[offset + i] =
- call_ptrace (PT_RUREGS, inferior_pid,
+ call_ptrace (PT_RUREGS, PIDGET (inferior_ptid),
(PTRACE_ARG3_TYPE) addr + i, 0);
if (errno != 0)
{
{
/* Need part of initial word -- fetch it. */
buffer[0] = call_ptrace (addr < text_end ? PT_RIUSER : PT_RDUSER,
- inferior_pid, (PTRACE_ARG3_TYPE) addr, 0);
+ PIDGET (inferior_ptid),
+ (PTRACE_ARG3_TYPE) addr, 0);
}
if (count > 1) /* FIXME, avoid if even boundary */
{
buffer[count - 1]
= call_ptrace (addr < text_end ? PT_RIUSER : PT_RDUSER,
- inferior_pid,
+ PIDGET (inferior_ptid),
(PTRACE_ARG3_TYPE) (addr
+ (count - 1) * sizeof (int)),
0);
errno = 0;
pt_request = (addr < text_end) ? PT_WIUSER : PT_WDUSER;
pt_status = call_ptrace (pt_request,
- inferior_pid,
+ PIDGET (inferior_ptid),
(PTRACE_ARG3_TYPE) addr,
buffer[i]);
errno = 0;
pt_request = (pt_request == PT_WIUSER) ? PT_WDUSER : PT_WIUSER;
pt_status = call_ptrace (pt_request,
- inferior_pid,
+ PIDGET (inferior_ptid),
(PTRACE_ARG3_TYPE) addr,
buffer[i]);
{
errno = 0;
buffer[i] = call_ptrace (addr < text_end ? PT_RIUSER : PT_RDUSER,
- inferior_pid, (PTRACE_ARG3_TYPE) addr, 0);
+ PIDGET (inferior_ptid),
+ (PTRACE_ARG3_TYPE) addr, 0);
if (errno)
{
xfree (buffer);
return len;
}
+char *saved_child_execd_pathname = NULL;
+int saved_vfork_pid;
+enum {
+ STATE_NONE,
+ STATE_GOT_CHILD,
+ STATE_GOT_EXEC,
+ STATE_GOT_PARENT,
+ STATE_FAKE_EXEC
+} saved_vfork_state = STATE_NONE;
-void
-child_post_follow_inferior_by_clone (void)
+int
+child_follow_fork (int follow_child)
{
- int status;
+ ptid_t last_ptid;
+ struct target_waitstatus last_status;
+ int has_vforked;
+ int parent_pid, child_pid;
+
+ get_last_target_status (&last_ptid, &last_status);
+ has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
+ parent_pid = ptid_get_pid (last_ptid);
+ child_pid = last_status.value.related_pid;
+
+ /* At this point, if we are vforking, breakpoints were already
+ detached from the child in child_wait; and the child has already
+ called execve(). If we are forking, both the parent and child
+ have breakpoints inserted. */
+
+ if (! follow_child)
+ {
+ if (! has_vforked)
+ {
+ detach_breakpoints (child_pid);
+#ifdef SOLIB_REMOVE_INFERIOR_HOOK
+ SOLIB_REMOVE_INFERIOR_HOOK (child_pid);
+#endif
+ }
- /* This function is used when following both the parent and child
- of a fork. In this case, the debugger clones itself. The original
- debugger follows the parent, the clone follows the child. The
- original detaches from the child, delivering a SIGSTOP to it to
- keep it from running away until the clone can attach itself.
+ /* Detach from the child. */
+ printf_unfiltered ("Detaching after fork from %s\n",
+ target_pid_to_str (pid_to_ptid (child_pid)));
+ hppa_require_detach (child_pid, 0);
+
+ /* The parent and child of a vfork share the same address space.
+ Also, on some targets the order in which vfork and exec events
+ are received for parent in child requires some delicate handling
+ of the events.
+
+ For instance, on ptrace-based HPUX we receive the child's vfork
+ event first, at which time the parent has been suspended by the
+ OS and is essentially untouchable until the child's exit or second
+ exec event arrives. At that time, the parent's vfork event is
+ delivered to us, and that's when we see and decide how to follow
+ the vfork. But to get to that point, we must continue the child
+ until it execs or exits. To do that smoothly, all breakpoints
+ must be removed from the child, in case there are any set between
+ the vfork() and exec() calls. But removing them from the child
+ also removes them from the parent, due to the shared-address-space
+ nature of a vfork'd parent and child. On HPUX, therefore, we must
+ take care to restore the bp's to the parent before we continue it.
+ Else, it's likely that we may not stop in the expected place. (The
+ worst scenario is when the user tries to step over a vfork() call;
+ the step-resume bp must be restored for the step to properly stop
+ in the parent after the call completes!)
+
+ Sequence of events, as reported to gdb from HPUX:
+
+ Parent Child Action for gdb to take
+ -------------------------------------------------------
+ 1 VFORK Continue child
+ 2 EXEC
+ 3 EXEC or EXIT
+ 4 VFORK
+
+ Now that the child has safely exec'd or exited, we must restore
+ the parent's breakpoints before we continue it. Else, we may
+ cause it run past expected stopping points. */
+
+ if (has_vforked)
+ reattach_breakpoints (parent_pid);
+ }
+ else
+ {
+ /* Needed to keep the breakpoint lists in sync. */
+ if (! has_vforked)
+ detach_breakpoints (child_pid);
- At this point, the clone has attached to the child. Because of
- the SIGSTOP, we must now deliver a SIGCONT to the child, or it
- won't behave properly. */
- status = kill (inferior_pid, SIGCONT);
-}
+ /* Before detaching from the parent, remove all breakpoints from it. */
+ remove_breakpoints ();
+ /* Also reset the solib inferior hook from the parent. */
+#ifdef SOLIB_REMOVE_INFERIOR_HOOK
+ SOLIB_REMOVE_INFERIOR_HOOK (PIDGET (inferior_ptid));
+#endif
-void
-child_post_follow_vfork (int parent_pid, int followed_parent, int child_pid,
- int followed_child)
-{
- /* Are we a debugger that followed the parent of a vfork? If so,
- then recall that the child's vfork event was delivered to us
- first. And, that the parent was suspended by the OS until the
- child's exec or exit events were received.
-
- Upon receiving that child vfork, then, we were forced to remove
- all breakpoints in the child and continue it so that it could
- reach the exec or exit point.
-
- But also recall that the parent and child of a vfork share the
- same address space. Thus, removing bp's in the child also
- removed them from the parent.
-
- Now that the child has safely exec'd or exited, we must restore
- the parent's breakpoints before we continue it. Else, we may
- cause it run past expected stopping points. */
- if (followed_parent)
- {
- reattach_breakpoints (parent_pid);
+ /* Detach from the parent. */
+ target_detach (NULL, 1);
+
+ /* Attach to the child. */
+ printf_unfiltered ("Attaching after fork to %s\n",
+ target_pid_to_str (pid_to_ptid (child_pid)));
+ hppa_require_attach (child_pid);
+ inferior_ptid = pid_to_ptid (child_pid);
+
+ /* If we vforked, then we've also execed by now. The exec will be
+ reported momentarily. follow_exec () will handle breakpoints, so
+ we don't have to.. */
+ if (!has_vforked)
+ follow_inferior_reset_breakpoints ();
}
- /* Are we a debugger that followed the child of a vfork? If so,
- then recall that we don't actually acquire control of the child
- until after it has exec'd or exited. */
- if (followed_child)
+ if (has_vforked)
{
- /* If the child has exited, then there's nothing for us to do.
- In the case of an exec event, we'll let that be handled by
- the normal mechanism that notices and handles exec events, in
- resume(). */
+ /* If we followed the parent, don't try to follow the child's exec. */
+ if (saved_vfork_state != STATE_GOT_PARENT
+ && saved_vfork_state != STATE_FAKE_EXEC)
+ fprintf_unfiltered (gdb_stdout,
+ "hppa: post follow vfork: confused state\n");
+
+ if (! follow_child || saved_vfork_state == STATE_GOT_PARENT)
+ saved_vfork_state = STATE_NONE;
+ else
+ return 1;
}
+ return 0;
}
/* Format a process id, given PID. Be sure to terminate
this with a null--it's going to be printed via a "%s". */
char *
-child_pid_to_str (pid_t pid)
+child_pid_to_str (ptid_t ptid)
{
/* Static because address returned */
static char buf[30];
+ pid_t pid = PIDGET (ptid);
- /* Extra NULLs for paranoia's sake */
- sprintf (buf, "process %d\0\0\0\0", pid);
+ /* Extra NUL for paranoia's sake */
+ sprintf (buf, "process %d%c", pid, '\0');
return buf;
}
Note: This is a core-gdb tid, not the actual system tid.
See infttrace.c for details. */
char *
-hppa_tid_to_str (pid_t tid)
+hppa_tid_to_str (ptid_t ptid)
{
/* Static because address returned */
static char buf[30];
+ /* This seems strange, but when I did the ptid conversion, it looked
+ as though a pid was always being passed. - Kevin Buettner */
+ pid_t tid = PIDGET (ptid);
/* Extra NULLs for paranoia's sake */
- sprintf (buf, "system thread %d\0\0\0\0", tid);
+ sprintf (buf, "system thread %d%c", tid, '\0');
return buf;
}
+/*## */
+/* Enable HACK for ttrace work. In
+ * infttrace.c/require_notification_of_events,
+ * this is set to 0 so that the loop in child_wait
+ * won't loop.
+ */
+int not_same_real_pid = 1;
+/*## */
+
+/* Wait for child to do something. Return pid of child, or -1 in case
+ of error; store status through argument pointer OURSTATUS. */
+
+ptid_t
+child_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
+{
+ int save_errno;
+ int status;
+ char *execd_pathname = NULL;
+ int exit_status;
+ int related_pid;
+ int syscall_id;
+ enum target_waitkind kind;
+ int pid;
+
+ if (saved_vfork_state == STATE_FAKE_EXEC)
+ {
+ saved_vfork_state = STATE_NONE;
+ ourstatus->kind = TARGET_WAITKIND_EXECD;
+ ourstatus->value.execd_pathname = saved_child_execd_pathname;
+ return inferior_ptid;
+ }
+
+ do
+ {
+ set_sigint_trap (); /* Causes SIGINT to be passed on to the
+ attached process. */
+ set_sigio_trap ();
+
+ pid = ptrace_wait (inferior_ptid, &status);
+
+ save_errno = errno;
+
+ clear_sigio_trap ();
+
+ clear_sigint_trap ();
+
+ if (pid == -1)
+ {
+ if (save_errno == EINTR)
+ continue;
+
+ fprintf_unfiltered (gdb_stderr, "Child process unexpectedly missing: %s.\n",
+ safe_strerror (save_errno));
+
+ /* Claim it exited with unknown signal. */
+ ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+ ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
+ return pid_to_ptid (-1);
+ }
+
+ /* Did it exit?
+ */
+ if (target_has_exited (pid, status, &exit_status))
+ {
+ /* ??rehrauer: For now, ignore this. */
+ continue;
+ }
+
+ if (!target_thread_alive (pid_to_ptid (pid)))
+ {
+ ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+ return pid_to_ptid (pid);
+ }
+
+ if (hpux_has_forked (pid, &related_pid))
+ {
+ /* Ignore the parent's fork event. */
+ if (pid == PIDGET (inferior_ptid))
+ {
+ ourstatus->kind = TARGET_WAITKIND_IGNORE;
+ return inferior_ptid;
+ }
+
+ /* If this is the child's fork event, report that the
+ process has forked. */
+ if (related_pid == PIDGET (inferior_ptid))
+ {
+ ourstatus->kind = TARGET_WAITKIND_FORKED;
+ ourstatus->value.related_pid = pid;
+ return inferior_ptid;
+ }
+ }
+
+ if (hpux_has_vforked (pid, &related_pid))
+ {
+ if (pid == PIDGET (inferior_ptid))
+ {
+ if (saved_vfork_state == STATE_GOT_CHILD)
+ saved_vfork_state = STATE_GOT_PARENT;
+ else if (saved_vfork_state == STATE_GOT_EXEC)
+ saved_vfork_state = STATE_FAKE_EXEC;
+ else
+ fprintf_unfiltered (gdb_stdout,
+ "hppah: parent vfork: confused\n");
+ }
+ else if (related_pid == PIDGET (inferior_ptid))
+ {
+ if (saved_vfork_state == STATE_NONE)
+ saved_vfork_state = STATE_GOT_CHILD;
+ else
+ fprintf_unfiltered (gdb_stdout,
+ "hppah: child vfork: confused\n");
+ }
+ else
+ fprintf_unfiltered (gdb_stdout,
+ "hppah: unknown vfork: confused\n");
+
+ if (saved_vfork_state == STATE_GOT_CHILD)
+ {
+ child_post_startup_inferior (pid_to_ptid (pid));
+ detach_breakpoints (pid);
+#ifdef SOLIB_REMOVE_INFERIOR_HOOK
+ SOLIB_REMOVE_INFERIOR_HOOK (pid);
+#endif
+ child_resume (pid_to_ptid (pid), 0, TARGET_SIGNAL_0);
+ ourstatus->kind = TARGET_WAITKIND_IGNORE;
+ return pid_to_ptid (related_pid);
+ }
+ else if (saved_vfork_state == STATE_FAKE_EXEC)
+ {
+ ourstatus->kind = TARGET_WAITKIND_VFORKED;
+ ourstatus->value.related_pid = related_pid;
+ return pid_to_ptid (pid);
+ }
+ else
+ {
+ /* We saw the parent's vfork, but we haven't seen the exec yet.
+ Wait for it, for simplicity's sake. It should be pending. */
+ saved_vfork_pid = related_pid;
+ ourstatus->kind = TARGET_WAITKIND_IGNORE;
+ return pid_to_ptid (pid);
+ }
+ }
+
+ if (hpux_has_execd (pid, &execd_pathname))
+ {
+ /* On HP-UX, events associated with a vforking inferior come in
+ threes: a vfork event for the child (always first), followed
+ a vfork event for the parent and an exec event for the child.
+ The latter two can come in either order. Make sure we get
+ both. */
+ if (saved_vfork_state != STATE_NONE)
+ {
+ if (saved_vfork_state == STATE_GOT_CHILD)
+ {
+ saved_vfork_state = STATE_GOT_EXEC;
+ /* On HP/UX with ptrace, the child must be resumed before
+ the parent vfork event is delivered. A single-step
+ suffices. */
+ if (RESUME_EXECD_VFORKING_CHILD_TO_GET_PARENT_VFORK ())
+ target_resume (pid_to_ptid (pid), 1, TARGET_SIGNAL_0);
+ ourstatus->kind = TARGET_WAITKIND_IGNORE;
+ }
+ else if (saved_vfork_state == STATE_GOT_PARENT)
+ {
+ saved_vfork_state = STATE_FAKE_EXEC;
+ ourstatus->kind = TARGET_WAITKIND_VFORKED;
+ ourstatus->value.related_pid = saved_vfork_pid;
+ }
+ else
+ fprintf_unfiltered (gdb_stdout,
+ "hppa: exec: unexpected state\n");
+
+ saved_child_execd_pathname = execd_pathname;
+
+ return inferior_ptid;
+ }
+
+ /* Are we ignoring initial exec events? (This is likely because
+ we're in the process of starting up the inferior, and another
+ (older) mechanism handles those.) If so, we'll report this
+ as a regular stop, not an exec.
+ */
+ if (inferior_ignoring_startup_exec_events)
+ {
+ inferior_ignoring_startup_exec_events--;
+ }
+ else
+ {
+ ourstatus->kind = TARGET_WAITKIND_EXECD;
+ ourstatus->value.execd_pathname = execd_pathname;
+ return pid_to_ptid (pid);
+ }
+ }
+
+ /* All we must do with these is communicate their occurrence
+ to wait_for_inferior...
+ */
+ if (hpux_has_syscall_event (pid, &kind, &syscall_id))
+ {
+ ourstatus->kind = kind;
+ ourstatus->value.syscall_id = syscall_id;
+ return pid_to_ptid (pid);
+ }
+
+ /*## } while (pid != PIDGET (inferior_ptid)); ## *//* Some other child died or stopped */
+/* hack for thread testing */
+ }
+ while ((pid != PIDGET (inferior_ptid)) && not_same_real_pid);
+/*## */
+
+ store_waitstatus (ourstatus, status);
+ return pid_to_ptid (pid);
+}
+
#if !defined (GDB_NATIVE_HPUX_11)
/* The following code is a substitute for the infttrace.c versions used
}
char *
-hppa_pid_or_tid_to_str (pid_t id)
+hppa_pid_or_tid_to_str (ptid_t id)
{
/* In the ptrace world, there are only processes. */
return child_pid_to_str (id);
}
void
-child_post_startup_inferior (int pid)
+child_post_startup_inferior (ptid_t ptid)
{
- require_notification_of_events (pid);
+ require_notification_of_events (PIDGET (ptid));
}
void
}
int
-child_has_forked (int pid, int *childpid)
+hpux_has_forked (int pid, int *childpid)
{
/* This request is only available on HPUX 10.0 and later. */
#if !defined(PT_GET_PROCESS_STATE)
}
int
-child_has_vforked (int pid, int *childpid)
+hpux_has_vforked (int pid, int *childpid)
{
/* This request is only available on HPUX 10.0 and later. */
#if !defined(PT_GET_PROCESS_STATE)
#endif
}
-int
-child_can_follow_vfork_prior_to_exec (void)
-{
- /* ptrace doesn't allow this. */
- return 0;
-}
-
int
child_insert_exec_catchpoint (int pid)
{
}
int
-child_has_execd (int pid, char **execd_pathname)
+hpux_has_execd (int pid, char **execd_pathname)
{
/* This request is only available on HPUX 10.0 and later. */
#if !defined(PT_GET_PROCESS_STATE)
}
int
-child_has_syscall_event (int pid, enum target_waitkind *kind, int *syscall_id)
+hpux_has_syscall_event (int pid, enum target_waitkind *kind, int *syscall_id)
{
/* This request is only available on HPUX 10.30 and later, via
the ttrace interface. */
char four_chars[4];
int name_index;
int i;
- int saved_inferior_pid;
+ ptid_t saved_inferior_ptid;
boolean done;
#ifdef PT_GET_PROCESS_PATHNAME
name_index = 0;
done = 0;
- /* On the chance that pid != inferior_pid, set inferior_pid
- to pid, so that (grrrr!) implicit uses of inferior_pid get
+ /* On the chance that pid != inferior_ptid, set inferior_ptid
+ to pid, so that (grrrr!) implicit uses of inferior_ptid get
the right id. */
- saved_inferior_pid = inferior_pid;
- inferior_pid = pid;
+ saved_inferior_ptid = inferior_ptid;
+ inferior_ptid = pid_to_ptid (pid);
/* Try to grab a null-terminated string. */
while (!done)
{
if (target_read_memory (top_of_stack, four_chars, 4) != 0)
{
- inferior_pid = saved_inferior_pid;
+ inferior_ptid = saved_inferior_ptid;
return NULL;
}
for (i = 0; i < 4; i++)
if (exec_file_buffer[0] == '\0')
{
- inferior_pid = saved_inferior_pid;
+ inferior_ptid = saved_inferior_ptid;
return NULL;
}
- inferior_pid = saved_inferior_pid;
+ inferior_ptid = saved_inferior_ptid;
return exec_file_buffer;
}
return "TRUE". */
int
-child_thread_alive (int pid)
+child_thread_alive (ptid_t ptid)
{
return 1;
}