static int restore_pc_queue PARAMS ((struct frame_saved_regs *fsr));
static int hppa_alignof PARAMS ((struct type *arg));
+static FRAME_ADDR dig_fp_from_stack PARAMS ((FRAME frame,
+ struct unwind_table_entry *u));
CORE_ADDR frame_saved_pc PARAMS ((FRAME frame));
\f
(word & 0x1) << 16, 17) << 2;
}
\f
-static int use_unwind = 0;
-
/* Lookup the unwind (stack backtrace) info for the given PC. We search all
of the objfiles seeking the unwind table entry for this PC. Each objfile
contains a sorted list of struct unwind_table_entry. Since we do a binary
return NULL;
}
+/* Called when no unwind descriptor was found for PC. Returns 1 if it
+ appears that PC is in a linker stub. */
+static int pc_in_linker_stub PARAMS ((CORE_ADDR));
+
+static int
+pc_in_linker_stub (pc)
+ CORE_ADDR pc;
+{
+ int found_magic_instruction = 0;
+ int i;
+ char buf[4];
+
+ /* If unable to read memory, assume pc is not in a linker stub. */
+ if (target_read_memory (pc, buf, 4) != 0)
+ return 0;
+
+ /* We are looking for something like
+
+ ; $$dyncall jams RP into this special spot in the frame (RP')
+ ; before calling the "call stub"
+ ldw -18(sp),rp
+
+ ldsid (rp),r1 ; Get space associated with RP into r1
+ mtsp r1,sp ; Move it into space register 0
+ be,n 0(sr0),rp) ; back to your regularly scheduled program
+ */
+
+ /* Maximum known linker stub size is 4 instructions. Search forward
+ from the given PC, then backward. */
+ for (i = 0; i < 4; i++)
+ {
+ /* If we hit something with an unwind, stop searching this direction. */
+
+ if (find_unwind_entry (pc + i * 4) != 0)
+ break;
+
+ /* Check for ldsid (rp),r1 which is the magic instruction for a
+ return from a cross-space function call. */
+ if (read_memory_integer (pc + i * 4, 4) == 0x004010a1)
+ {
+ found_magic_instruction = 1;
+ break;
+ }
+ /* Add code to handle long call/branch and argument relocation stubs
+ here. */
+ }
+
+ if (found_magic_instruction != 0)
+ return 1;
+
+ /* Now look backward. */
+ for (i = 0; i < 4; i++)
+ {
+ /* If we hit something with an unwind, stop searching this direction. */
+
+ if (find_unwind_entry (pc - i * 4) != 0)
+ break;
+
+ /* Check for ldsid (rp),r1 which is the magic instruction for a
+ return from a cross-space function call. */
+ if (read_memory_integer (pc - i * 4, 4) == 0x004010a1)
+ {
+ found_magic_instruction = 1;
+ break;
+ }
+ /* Add code to handle long call/branch and argument relocation stubs
+ here. */
+ }
+ return found_magic_instruction;
+}
+
static int
find_return_regnum(pc)
CORE_ADDR pc;
return RP_REGNUM;
}
+/* Return size of frame, or -1 if we should use a frame pointer. */
int
find_proc_framesize(pc)
CORE_ADDR pc;
{
struct unwind_table_entry *u;
- if (!use_unwind)
- return -1;
-
u = find_unwind_entry (pc);
if (!u)
+ {
+ if (pc_in_linker_stub (pc))
+ /* Linker stubs have a zero size frame. */
+ return 0;
+ else
+ return -1;
+ }
+
+ if (u->Save_SP)
+ /* If this bit is set, it means there is a frame pointer and we should
+ use it. */
return -1;
return u->Total_frame_size << 3;
}
-int
-rp_saved(pc)
+/* Return offset from sp at which rp is saved, or 0 if not saved. */
+static int rp_saved PARAMS ((CORE_ADDR));
+
+static int
+rp_saved (pc)
+ CORE_ADDR pc;
{
struct unwind_table_entry *u;
u = find_unwind_entry (pc);
if (!u)
- return 0;
+ {
+ if (pc_in_linker_stub (pc))
+ /* This is the so-called RP'. */
+ return -24;
+ else
+ return 0;
+ }
if (u->Save_RP)
- return 1;
+ return -20;
else
return 0;
}
frameless_function_invocation (frame)
FRAME frame;
{
+ struct unwind_table_entry *u;
- if (use_unwind)
- {
- struct unwind_table_entry *u;
-
- u = find_unwind_entry (frame->pc);
-
- if (u == 0)
- return 0;
+ u = find_unwind_entry (frame->pc);
- return (u->Total_frame_size == 0);
- }
- else
+ if (u == 0)
return frameless_look_for_prologue (frame);
+
+ return (u->Total_frame_size == 0);
}
CORE_ADDR
return read_register (ret_regnum) & ~0x3;
}
- else if (rp_saved (pc))
- return read_memory_integer (frame->frame - 20, 4) & ~0x3;
else
- return read_register (RP_REGNUM) & ~0x3;
+ {
+ int rp_offset = rp_saved (pc);
+
+ if (rp_offset == 0)
+ return read_register (RP_REGNUM) & ~0x3;
+ else
+ return read_memory_integer (frame->frame + rp_offset, 4) & ~0x3;
+ }
}
\f
/* We need to correct the PC and the FP for the outermost frame when we are
frame->frame -= framesize;
}
\f
+/* Given a GDB frame, determine the address of the calling function's frame.
+ This will be used to create a new GDB frame struct, and then
+ INIT_EXTRA_FRAME_INFO and INIT_FRAME_PC will be called for the new frame.
+
+ This may involve searching through prologues for several functions
+ at boundaries where GCC calls HP C code, or where code which has
+ a frame pointer calls code without a frame pointer. */
+
+
FRAME_ADDR
frame_chain (frame)
struct frame_info *frame;
{
- int framesize;
+ int my_framesize, caller_framesize;
+ struct unwind_table_entry *u;
- framesize = find_proc_framesize(FRAME_SAVED_PC(frame));
+ /* Get frame sizes for the current frame and the frame of the
+ caller. */
+ my_framesize = find_proc_framesize (frame->pc);
+ caller_framesize = find_proc_framesize (FRAME_SAVED_PC(frame));
- if (framesize != -1)
- return frame->frame - framesize;
+ /* If caller does not have a frame pointer, then its frame
+ can be found at current_frame - caller_framesize. */
+ if (caller_framesize != -1)
+ return frame->frame - caller_framesize;
+
+ /* Both caller and callee have frame pointers and are GCC compiled
+ (SAVE_SP bit in unwind descriptor is on for both functions.
+ The previous frame pointer is found at the top of the current frame. */
+ if (caller_framesize == -1 && my_framesize == -1)
+ return read_memory_integer (frame->frame, 4);
- return read_memory_integer (frame->frame, 4);
+ /* Caller has a frame pointer, but callee does not. This is a little
+ more difficult as GCC and HP C lay out locals and callee register save
+ areas very differently.
+
+ The previous frame pointer could be in a register, or in one of
+ several areas on the stack.
+
+ Walk from the current frame to the innermost frame examining
+ unwind descriptors to determine if %r4 ever gets saved into the
+ stack. If so return whatever value got saved into the stack.
+ If it was never saved in the stack, then the value in %r4 is still
+ valid, so use it.
+
+ We use information from unwind descriptors to determine if %r4
+ is saved into the stack (Entry_GR field has this information). */
+
+ while (frame)
+ {
+ u = find_unwind_entry (frame->pc);
+
+ if (!u)
+ {
+ /* We could find this information by examining prologues. I don't
+ think anyone has actually written any tools (not even "strip")
+ which leave them out of an executable, so maybe this is a moot
+ point. */
+ warning ("Unable to find unwind for PC 0x%x -- Help!", frame->pc);
+ return 0;
+ }
+
+ /* Entry_GR specifies the number of callee-saved general registers
+ saved in the stack. It starts at %r3, so %r4 would be 2. */
+ if (u->Entry_GR >= 2 || u->Save_SP)
+ break;
+ else
+ frame = frame->next;
+ }
+
+ if (frame)
+ {
+ /* We may have walked down the chain into a function with a frame
+ pointer. */
+ if (u->Save_SP)
+ return read_memory_integer (frame->frame, 4);
+ /* %r4 was saved somewhere in the stack. Dig it out. */
+ else
+ return dig_fp_from_stack (frame, u);
+ }
+ else
+ {
+ /* The value in %r4 was never saved into the stack (thus %r4 still
+ holds the value of the previous frame pointer). */
+ return read_register (4);
+ }
+}
+
+/* Given a frame and an unwind descriptor return the value for %fr (aka fp)
+ which was saved into the stack. FIXME: Why can't we just use the standard
+ saved_regs stuff? */
+
+static FRAME_ADDR
+dig_fp_from_stack (frame, u)
+ FRAME frame;
+ struct unwind_table_entry *u;
+{
+ CORE_ADDR pc = u->region_start;
+
+ /* Search the function for the save of %r4. */
+ while (pc != u->region_end)
+ {
+ char buf[4];
+ unsigned long inst;
+ int status;
+
+ /* We need only look for the standard stw %r4,X(%sp) instruction,
+ the other variants (eg stwm) are only used on the first register
+ save (eg %r3). */
+ status = target_read_memory (pc, buf, 4);
+ inst = extract_unsigned_integer (buf, 4);
+
+ if (status != 0)
+ memory_error (status, pc);
+
+ /* Check for stw %r4,X(%sp). */
+ if ((inst & 0xffffc000) == 0x6bc40000)
+ {
+ /* Found the instruction which saves %r4. The offset (relative
+ to this frame) is framesize + immed14 (derived from the
+ store instruction). */
+ int offset = (u->Total_frame_size << 3) + extract_14 (inst);
+
+ return read_memory_integer (frame->frame + offset, 4);
+ }
+
+ /* Keep looking. */
+ pc += 4;
+ }
+
+ warning ("Unable to find %%r4 in stack.\n");
+ return 0;
}
+
\f
/* To see if a frame chain is valid, see if the caller looks like it
was compiled with gcc. */
FRAME_ADDR chain;
FRAME thisframe;
{
- struct minimal_symbol *msym;
+ struct minimal_symbol *msym_us;
+ struct minimal_symbol *msym_start;
+ struct unwind_table_entry *u;
if (!chain)
return 0;
- if (use_unwind)
- {
+ u = find_unwind_entry (thisframe->pc);
+
+ /* We can't just check that the same of msym_us is "_start", because
+ someone idiotically decided that they were going to make a Ltext_end
+ symbol with the same address. This Ltext_end symbol is totally
+ indistinguishable (as nearly as I can tell) from the symbol for a function
+ which is (legitimately, since it is in the user's namespace)
+ named Ltext_end, so we can't just ignore it. */
+ msym_us = lookup_minimal_symbol_by_pc (FRAME_SAVED_PC (thisframe));
+ msym_start = lookup_minimal_symbol ("_start", NULL);
+ if (msym_us
+ && msym_start
+ && SYMBOL_VALUE_ADDRESS (msym_us) == SYMBOL_VALUE_ADDRESS (msym_start))
+ return 0;
- struct unwind_table_entry *u;
+ if (u == NULL)
+ return 1;
- u = find_unwind_entry (thisframe->pc);
+ if (u->Save_SP || u->Total_frame_size)
+ return 1;
- if (u && (u->Save_SP || u->Total_frame_size))
- return 1;
- else
- return 0;
- }
- else
- {
- msym = lookup_minimal_symbol_by_pc (FRAME_SAVED_PC (thisframe));
+ if (pc_in_linker_stub (thisframe->pc))
+ return 1;
- if (msym
- && (strcmp (SYMBOL_NAME (msym), "_start") == 0))
- return 0;
- else
- return 1;
- }
+ return 0;
}
/*
write_register (SAR_REGNUM,
read_memory_integer (fsr.regs[SAR_REGNUM], 4));
+ /* If the PC was explicitly saved, then just restore it. */
if (fsr.regs[PCOQ_TAIL_REGNUM])
write_register (PCOQ_TAIL_REGNUM,
read_memory_integer (fsr.regs[PCOQ_TAIL_REGNUM], 4));
+ /* Else use the value in %rp to set the new PC. */
+ else
+ target_write_pc (read_register (RP_REGNUM));
+
write_register (FP_REGNUM, read_memory_integer (fp, 4));
if (fsr.regs[IPSW_REGNUM]) /* call dummy */
for (insn_count = 0; insn_count < 3; insn_count++)
{
+ /* FIXME: What if the inferior gets a signal right now? Want to
+ merge this into wait_for_inferior (as a special kind of
+ watchpoint? By setting a breakpoint at the end? Is there
+ any other choice? Is there *any* way to do this stuff with
+ ptrace() or some equivalent?). */
resume (1, 0);
- target_wait(&w);
+ target_wait(inferior_pid, &w);
if (!WIFSTOPPED (w))
{
return 0;
}
}
+ target_terminal_ours ();
fetch_inferior_registers (-1);
return 1;
}
cum = (cum + alignment) & -alignment;
offset[i] = -cum;
}
- sp += min ((cum + 7) & -8, 16);
+ sp += max ((cum + 7) & -8, 16);
for (i = 0; i < nargs; i++)
write_memory (sp + offset[i], VALUE_CONTENTS (args[i]),
{
CORE_ADDR dyncall_addr, sr4export_addr;
struct minimal_symbol *msymbol;
+ int flags = read_register (FLAGS_REGNUM);
msymbol = lookup_minimal_symbol ("$$dyncall", (struct objfile *) NULL);
if (msymbol == NULL)
write_register (22, pc);
- return dyncall_addr;
+ /* If we are in a syscall, then we should call the stack dummy
+ directly. $$dyncall is not needed as the kernel sets up the
+ space id registers properly based on the value in %r31. In
+ fact calling $$dyncall will not work because the value in %r22
+ will be clobbered on the syscall exit path. */
+ if (flags & 2)
+ return pc;
+ else
+ return dyncall_addr;
+
+}
+
+/* Get the PC from %r31 if currently in a syscall. Also mask out privilege
+ bits. */
+CORE_ADDR
+target_read_pc ()
+{
+ int flags = read_register (FLAGS_REGNUM);
+
+ if (flags & 2)
+ return read_register (31) & ~0x3;
+ return read_register (PC_REGNUM) & ~0x3;
+}
+
+/* Write out the PC. If currently in a syscall, then also write the new
+ PC value into %r31. */
+void
+target_write_pc (v)
+ CORE_ADDR v;
+{
+ int flags = read_register (FLAGS_REGNUM);
+
+ /* If in a syscall, then set %r31. Also make sure to get the
+ privilege bits set correctly. */
+ if (flags & 2)
+ write_register (31, (long) (v | 0x3));
+
+ write_register (PC_REGNUM, (long) v);
+ write_register (NPC_REGNUM, (long) v + 4);
}
/* return the alignment of a type in bytes. Structures have the maximum
skip_prologue(pc)
CORE_ADDR pc;
{
- int inst;
+ char buf[4];
+ unsigned long inst;
int status;
- status = target_read_memory (pc, (char *)&inst, 4);
- SWAP_TARGET_AND_HOST (&inst, sizeof (inst));
+ status = target_read_memory (pc, buf, 4);
+ inst = extract_unsigned_integer (buf, 4);
if (status != 0)
return pc;
return pc;
}
+#ifdef MAINTENANCE_CMDS
+
static void
unwind_command (exp, from_tty)
char *exp;
}
void
-_initialize_hppah_tdep ()
+_initialize_hppa_tdep ()
{
- add_com ("unwind", class_obscure, unwind_command, "Print unwind info\n");
- add_show_from_set
- (add_set_cmd ("use_unwind", class_obscure, var_boolean,
- (char *)&use_unwind,
- "Set the usage of unwind info", &setlist),
- &showlist);
+ add_cmd ("unwind", class_maintenance, unwind_command,
+ "Print unwind table entry at given address.",
+ &maintenanceprintlist);
}
+
+#endif /* MAINTENANCE_CMDS */