X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fhppah-nat.c;h=ea4015a8d3201ed9aa0112763414140eef25013b;hb=fe61caabefd9459ff50481ce3276b0e7215c4b23;hp=5daa14dc77a1c36c8cbbdc96bcdf941158db073b;hpb=47932f85ce27bcf18f485c2492dfafb02132dce9;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/hppah-nat.c b/gdb/hppah-nat.c index 5daa14dc77..ea4015a8d3 100644 --- a/gdb/hppah-nat.c +++ b/gdb/hppah-nat.c @@ -31,16 +31,37 @@ #include "gdbcore.h" #include "gdb_wait.h" #include "regcache.h" +#include "gdb_string.h" #include -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 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; + } +} + + static void fetch_register (int); void @@ -63,9 +84,9 @@ fetch_inferior_registers (int regno) 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; @@ -77,7 +98,7 @@ store_inferior_registers (int regno) 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!" */ @@ -88,16 +109,17 @@ store_inferior_registers (int regno) 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) addr = (HPPAH_OFFSETOF (save_state_t, ss_fpblock) - + (REGISTER_BYTE (regno) - REGISTER_BYTE (FP0_REGNUM))); + + (DEPRECATED_REGISTER_BYTE (regno) - DEPRECATED_REGISTER_BYTE (FP0_REGNUM))); /* Wide registers come from the ss_wide area. I think it's more PC to test (ss_flags & SS_WIDEREGS) to select @@ -106,13 +128,13 @@ store_inferior_registers (int regno) 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"); @@ -127,7 +149,7 @@ store_inferior_registers (int regno) { 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; @@ -152,7 +174,7 @@ store_inferior_registers (int regno) 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; + *(int *)&deprecated_registers[DEPRECATED_REGISTER_BYTE (regno)] = 0; #endif for (i = 0; i < len; i += sizeof (int)) @@ -160,7 +182,7 @@ store_inferior_registers (int regno) 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 @@ -189,12 +211,12 @@ store_inferior_registers (int regno) 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!" */ @@ -205,17 +227,17 @@ fetch_register (int regno) 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) addr = (HPPAH_OFFSETOF (save_state_t, ss_fpblock) - + (REGISTER_BYTE (regno) - REGISTER_BYTE (FP0_REGNUM))); + + (DEPRECATED_REGISTER_BYTE (regno) - DEPRECATED_REGISTER_BYTE (FP0_REGNUM))); /* Wide registers come from the ss_wide area. I think it's more PC to test (ss_flags & SS_WIDEREGS) to select @@ -224,13 +246,13 @@ fetch_register (int regno) 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__, @@ -281,11 +303,11 @@ child_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write, 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. @@ -296,7 +318,7 @@ child_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write, 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) { @@ -384,60 +406,131 @@ child_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int 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_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 + } + + /* 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); - /* 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. + /* Before detaching from the parent, remove all breakpoints from it. */ + remove_breakpoints (); - 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 (PIDGET (inferior_ptid), SIGCONT); -} + /* 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); -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); + /* 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 @@ -484,7 +577,6 @@ hppa_tid_to_str (ptid_t ptid) 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. */ @@ -500,6 +592,14 @@ child_wait (ptid_t ptid, struct target_waitstatus *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 @@ -542,26 +642,110 @@ child_wait (ptid_t ptid, struct target_waitstatus *ourstatus) return pid_to_ptid (pid); } - if (hpux_has_forked (pid, &related_pid) - && ((pid == PIDGET (inferior_ptid)) - || (related_pid == PIDGET (inferior_ptid)))) + if (hpux_has_forked (pid, &related_pid)) { - ourstatus->kind = TARGET_WAITKIND_FORKED; - ourstatus->value.related_pid = related_pid; - return pid_to_ptid (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) - && ((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 @@ -623,8 +807,6 @@ startup_semaphore_t; 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, @@ -745,14 +927,13 @@ hppa_insert_hw_watchpoint (int pid, CORE_ADDR start, LONGEST len, int type) } 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; } @@ -770,16 +951,6 @@ hppa_pid_or_tid_to_str (ptid_t id) 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) { @@ -1172,7 +1343,7 @@ child_pid_to_exec_file (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. */