#include "gdbcore.h"
#include "gdb_wait.h"
#include "regcache.h"
+#include "gdb_string.h"
+#include "infttrace.h"
#include <signal.h>
-extern CORE_ADDR text_end;
+#include "hppa-tdep.h"
+
+static CORE_ADDR text_end;
+
+void
+deprecated_hpux_text_end (struct target_ops *exec_ops)
+{
+ struct section_table *p;
+
+ /* Set text_end to the highest address of the end of any readonly
+ code section. */
+ /* FIXME: The comment above does not match the code. The code
+ checks for sections with are either code *or* readonly. */
+ text_end = (CORE_ADDR) 0;
+ for (p = exec_ops->to_sections; p < exec_ops->to_sections_end; p++)
+ if (bfd_get_section_flags (p->bfd, p->the_bfd_section)
+ & (SEC_CODE | SEC_READONLY))
+ {
+ if (text_end < p->endaddr)
+ text_end = p->endaddr;
+ }
+}
-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
store_inferior_registers (int regno)
{
- register unsigned int regaddr;
+ unsigned int regaddr;
char buf[80];
- register int i;
+ int i;
unsigned int offset = U_REGS_OFFSET;
int scratch;
return;
offset = 0;
- len = REGISTER_RAW_SIZE (regno);
+ len = DEPRECATED_REGISTER_RAW_SIZE (regno);
/* Requests for register zero actually want the save_state's
ss_flags member. As RM says: "Oh, what a hack!" */
len = sizeof (ss.ss_flags);
/* Note that ss_flags is always an int, no matter what
- REGISTER_RAW_SIZE(0) says. Assuming all HP-UX PA machines
- are big-endian, put it at the least significant end of the
- value, and zap the rest of the buffer. */
- offset = REGISTER_RAW_SIZE (0) - len;
+ DEPRECATED_REGISTER_RAW_SIZE(0) says. Assuming all HP-UX
+ PA machines are big-endian, put it at the least
+ significant end of the value, and zap the rest of the
+ buffer. */
+ offset = DEPRECATED_REGISTER_RAW_SIZE (0) - len;
}
/* Floating-point registers come from the ss_fpblock area. */
- else if (regno >= FP0_REGNUM)
+ else if (regno >= HPPA_FP0_REGNUM)
addr = (HPPAH_OFFSETOF (save_state_t, ss_fpblock)
- + (REGISTER_BYTE (regno) - REGISTER_BYTE (FP0_REGNUM)));
+ + (DEPRECATED_REGISTER_BYTE (regno) - DEPRECATED_REGISTER_BYTE (HPPA_FP0_REGNUM)));
/* Wide registers come from the ss_wide area.
I think it's more PC to test (ss_flags & SS_WIDEREGS) to select
every register reference. Bleah. */
else if (len == 8)
addr = (HPPAH_OFFSETOF (save_state_t, ss_wide)
- + REGISTER_BYTE (regno));
+ + DEPRECATED_REGISTER_BYTE (regno));
/* Narrow registers come from the ss_narrow area. Note that
ss_narrow starts with gr1, not gr0. */
else if (len == 4)
addr = (HPPAH_OFFSETOF (save_state_t, ss_narrow)
- + (REGISTER_BYTE (regno) - REGISTER_BYTE (1)));
+ + (DEPRECATED_REGISTER_BYTE (regno) - DEPRECATED_REGISTER_BYTE (1)));
else
internal_error (__FILE__, __LINE__,
"hppah-nat.c (write_register): unexpected register size");
layering will not allow us to perform a 64bit register store.
What a crock. */
- if (regno == PCOQ_HEAD_REGNUM || regno == PCOQ_TAIL_REGNUM && len == 8)
+ if (regno == HPPA_PCOQ_HEAD_REGNUM || regno == HPPA_PCOQ_TAIL_REGNUM && len == 8)
{
CORE_ADDR temp;
- temp = *(CORE_ADDR *)&deprecated_registers[REGISTER_BYTE (regno)];
+ temp = *(CORE_ADDR *)&deprecated_registers[DEPRECATED_REGISTER_BYTE (regno)];
/* Set the priv level (stored in the low two bits of the PC. */
temp |= 0x3;
/* Another crock. HPUX complains if you write a nonzero value to
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 *)&deprecated_registers[REGISTER_BYTE (regno)] = 0;
+ if (regno == HPPA_IPSW_REGNUM && len == 8)
+ *(int *)&deprecated_registers[DEPRECATED_REGISTER_BYTE (regno)] = 0;
#endif
for (i = 0; i < len; i += sizeof (int))
errno = 0;
call_ptrace (PT_WUREGS, PIDGET (inferior_ptid),
(PTRACE_ARG3_TYPE) addr + i,
- *(int *) &deprecated_registers[REGISTER_BYTE (regno) + i]);
+ *(int *) &deprecated_registers[DEPRECATED_REGISTER_BYTE (regno) + i]);
if (errno != 0)
{
/* Warning, not error, in case we are attached; sometimes
REGISTER_NAME (regno), err);
/* If we fail to write the PC, give a true error instead of
just a warning. */
- if (regno == PCOQ_HEAD_REGNUM || regno == PCOQ_TAIL_REGNUM)
+ if (regno == HPPA_PCOQ_HEAD_REGNUM || regno == HPPA_PCOQ_TAIL_REGNUM)
perror_with_name (msg);
else
warning (msg);
static void
fetch_register (int regno)
{
- char buf[MAX_REGISTER_RAW_SIZE];
+ char buf[MAX_REGISTER_SIZE];
unsigned int addr, len, offset;
int i;
offset = 0;
- len = REGISTER_RAW_SIZE (regno);
+ len = DEPRECATED_REGISTER_RAW_SIZE (regno);
/* Requests for register zero actually want the save_state's
ss_flags member. As RM says: "Oh, what a hack!" */
len = sizeof (ss.ss_flags);
/* Note that ss_flags is always an int, no matter what
- REGISTER_RAW_SIZE(0) says. Assuming all HP-UX PA machines
- are big-endian, put it at the least significant end of the
- value, and zap the rest of the buffer. */
- offset = REGISTER_RAW_SIZE (0) - len;
+ DEPRECATED_REGISTER_RAW_SIZE(0) says. Assuming all HP-UX PA
+ machines are big-endian, put it at the least significant end
+ of the value, and zap the rest of the buffer. */
+ offset = DEPRECATED_REGISTER_RAW_SIZE (0) - len;
memset (buf, 0, sizeof (buf));
}
/* Floating-point registers come from the ss_fpblock area. */
- else if (regno >= FP0_REGNUM)
+ else if (regno >= HPPA_FP0_REGNUM)
addr = (HPPAH_OFFSETOF (save_state_t, ss_fpblock)
- + (REGISTER_BYTE (regno) - REGISTER_BYTE (FP0_REGNUM)));
+ + (DEPRECATED_REGISTER_BYTE (regno) - DEPRECATED_REGISTER_BYTE (HPPA_FP0_REGNUM)));
/* Wide registers come from the ss_wide area.
I think it's more PC to test (ss_flags & SS_WIDEREGS) to select
every register reference. Bleah. */
else if (len == 8)
addr = (HPPAH_OFFSETOF (save_state_t, ss_wide)
- + REGISTER_BYTE (regno));
+ + DEPRECATED_REGISTER_BYTE (regno));
/* Narrow registers come from the ss_narrow area. Note that
ss_narrow starts with gr1, not gr0. */
else if (len == 4)
addr = (HPPAH_OFFSETOF (save_state_t, ss_narrow)
- + (REGISTER_BYTE (regno) - REGISTER_BYTE (1)));
+ + (DEPRECATED_REGISTER_BYTE (regno) - DEPRECATED_REGISTER_BYTE (1)));
else
internal_error (__FILE__, __LINE__,
/* If we're reading an address from the instruction address queue,
mask out the bottom two bits --- they contain the privilege
level. */
- if (regno == PCOQ_HEAD_REGNUM || regno == PCOQ_TAIL_REGNUM)
+ if (regno == HPPA_PCOQ_HEAD_REGNUM || regno == HPPA_PCOQ_TAIL_REGNUM)
buf[len - 1] &= ~0x3;
supply_register (regno, buf);
struct mem_attrib *mem,
struct target_ops *target)
{
- register int i;
+ int i;
/* Round starting address down to longword boundary. */
- register CORE_ADDR addr = memaddr & - (CORE_ADDR)(sizeof (int));
+ CORE_ADDR addr = memaddr & - (CORE_ADDR)(sizeof (int));
/* Round ending address up; get number of longwords that makes. */
- register int count
+ int count
= (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int);
/* Allocate buffer of that many longwords.
this (in effect) would pile up all those alloca requests until a call
to alloca was made from a point higher than this routine in the
call chain. */
- register int *buffer = (int *) xmalloc (count * sizeof (int));
+ int *buffer = (int *) xmalloc (count * sizeof (int));
if (write)
{
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_vfork (int parent_pid, int followed_parent, int child_pid,
- int followed_child)
+int
+child_follow_fork (int follow_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)
+ 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)
{
- reattach_breakpoints (parent_pid);
+ if (! has_vforked)
+ {
+ detach_breakpoints (child_pid);
+#ifdef SOLIB_REMOVE_INFERIOR_HOOK
+ SOLIB_REMOVE_INFERIOR_HOOK (child_pid);
+#endif
+ }
+
+ /* 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);
- /* 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)
+ /* 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
+
+ /* 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 ();
+ }
+
+ 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
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. */
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
}
}
- if (hpux_has_vforked (pid, &related_pid)
- && ((pid == PIDGET (inferior_ptid))
- || (related_pid == PIDGET (inferior_ptid))))
+ if (hpux_has_vforked (pid, &related_pid))
{
- ourstatus->kind = TARGET_WAITKIND_VFORKED;
- ourstatus->value.related_pid = related_pid;
- return pid_to_ptid (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
static startup_semaphore_t startup_semaphore;
-extern int parent_attach_all (int, PTRACE_ARG3_TYPE, int);
-
#ifdef PT_SETTRC
/* This function causes the caller's process to be traced by its
parent. This is intended to be called after GDB forks itself,
}
int
-hppa_remove_hw_watchpoint (int pid, CORE_ADDR start, LONGEST len,
- enum bptype type)
+hppa_remove_hw_watchpoint (int pid, CORE_ADDR start, LONGEST len, int type)
{
error ("Hardware watchpoints not implemented on this platform.");
}
int
-hppa_can_use_hw_watchpoint (enum bptype type, int cnt, enum bptype ot)
+hppa_can_use_hw_watchpoint (int type, int cnt, int ot)
{
return 0;
}
return child_pid_to_str (id);
}
-/* This function has no meaning in a non-threaded world. Thus, we
- return 0 (FALSE). See the use of "hppa_prepare_to_proceed" in
- hppa-tdep.c. */
-
-pid_t
-hppa_switched_threads (pid_t pid)
-{
- return (pid_t) 0;
-}
-
void
hppa_ensure_vforking_parent_remains_stopped (int pid)
{
int name_index;
int i;
ptid_t saved_inferior_ptid;
- boolean done;
+ int done;
#ifdef PT_GET_PROCESS_PATHNAME
/* As of 10.x HP-UX, there's an explicit request to get the pathname. */