/* Forward declarations. */
+static alpha_extra_func_info_t push_sigtramp_desc PARAMS ((CORE_ADDR low_addr));
+
static CORE_ADDR read_next_frame_reg PARAMS ((struct frame_info *, int));
static CORE_ADDR heuristic_proc_start PARAMS ((CORE_ADDR));
struct linked_proc_info *next;
} *linked_proc_desc_table = NULL;
+\f
+/* Under Linux, signal handler invocations can be identified by the
+ designated code sequence that is used to return from a signal
+ handler. In particular, the return address of a signal handler
+ points to the following sequence (the first instruction is quadword
+ aligned):
+
+ bis $30,$30,$16
+ addq $31,0x67,$0
+ call_pal callsys
+
+ Each instruction has a unique encoding, so we simply attempt to
+ match the instruction the pc is pointing to with any of the above
+ instructions. If there is a hit, we know the offset to the start
+ of the designated sequence and can then check whether we really are
+ executing in a designated sequence. If not, -1 is returned,
+ otherwise the offset from the start of the desingated sequence is
+ returned.
+
+ There is a slight chance of false hits: code could jump into the
+ middle of the designated sequence, in which case there is no
+ guarantee that we are in the middle of a sigreturn syscall. Don't
+ think this will be a problem in praxis, though.
+*/
+
+long
+alpha_linux_sigtramp_offset (CORE_ADDR pc)
+{
+ unsigned int i[3], w;
+ long off;
+
+ if (read_memory_nobpt(pc, (char *) &w, 4) != 0)
+ return -1;
+
+ off = -1;
+ switch (w)
+ {
+ case 0x47de0410: off = 0; break; /* bis $30,$30,$16 */
+ case 0x43ecf400: off = 4; break; /* addq $31,0x67,$0 */
+ case 0x00000083: off = 8; break; /* call_pal callsys */
+ default: return -1;
+ }
+ pc -= off;
+ if (pc & 0x7)
+ {
+ /* designated sequence is not quadword aligned */
+ return -1;
+ }
+
+ if (read_memory_nobpt(pc, (char *) i, sizeof(i)) != 0)
+ return -1;
+
+ if (i[0] == 0x47de0410 && i[1] == 0x43ecf400 && i[2] == 0x00000083)
+ return off;
+
+ return -1;
+}
+
+\f
+/* Under OSF/1, the __sigtramp routine is frameless and has a frame
+ size of zero, but we are able to backtrace through it. */
+CORE_ADDR
+alpha_osf_skip_sigtramp_frame (frame, pc)
+ struct frame_info *frame;
+ CORE_ADDR pc;
+{
+ char *name;
+ find_pc_partial_function (pc, &name, (CORE_ADDR *)NULL, (CORE_ADDR *)NULL);
+ if (IN_SIGTRAMP (pc, name))
+ return frame->frame;
+ else
+ return 0;
+}
+
+\f
+/* Dynamically create a signal-handler caller procedure descriptor for
+ the signal-handler return code starting at address LOW_ADDR. The
+ descriptor is added to the linked_proc_desc_table. */
+
+static alpha_extra_func_info_t
+push_sigtramp_desc (low_addr)
+ CORE_ADDR low_addr;
+{
+ struct linked_proc_info *link;
+ alpha_extra_func_info_t proc_desc;
+
+ link = (struct linked_proc_info *)
+ xmalloc (sizeof (struct linked_proc_info));
+ link->next = linked_proc_desc_table;
+ linked_proc_desc_table = link;
+
+ proc_desc = &link->info;
+
+ proc_desc->numargs = 0;
+ PROC_LOW_ADDR (proc_desc) = low_addr;
+ PROC_HIGH_ADDR (proc_desc) = low_addr + 3 * 4;
+ PROC_DUMMY_FRAME (proc_desc) = 0;
+ PROC_FRAME_OFFSET (proc_desc) = 0x298; /* sizeof(struct sigcontext_struct) */
+ PROC_FRAME_REG (proc_desc) = SP_REGNUM;
+ PROC_REG_MASK (proc_desc) = 0xffff;
+ PROC_FREG_MASK (proc_desc) = 0xffff;
+ PROC_PC_REG (proc_desc) = 26;
+ PROC_LOCALOFF (proc_desc) = 0;
+ SET_PROC_DESC_IS_DYN_SIGTRAMP (proc_desc);
+ return (proc_desc);
+}
+
\f
/* Guaranteed to set frame->saved_regs to some values (it never leaves it
NULL). */
#endif
if (frame->signal_handler_caller)
{
- CORE_ADDR sigcontext_pointer_addr;
CORE_ADDR sigcontext_addr;
- if (frame->next)
- sigcontext_pointer_addr = frame->next->frame;
- else
- sigcontext_pointer_addr = frame->frame;
- sigcontext_addr = read_memory_integer(sigcontext_pointer_addr, 8);
+ sigcontext_addr = SIGCONTEXT_ADDR (frame);
for (ireg = 0; ireg < 32; ireg++)
{
reg_position = sigcontext_addr + SIGFRAME_REGSAVE_OFF + ireg * 8;
proc_desc = find_proc_desc (pc, frame->next);
pcreg = proc_desc ? PROC_PC_REG (proc_desc) : RA_REGNUM;
- return read_register (pcreg);
+ if (frame->signal_handler_caller)
+ return alpha_frame_saved_pc (frame);
+ else
+ return read_register (pcreg);
}
int frame_size;
int has_frame_reg = 0;
unsigned long reg_mask = 0;
+ int pcreg = -1;
if (start_pc == 0)
return NULL;
int reg = (word & 0x03e00000) >> 21;
reg_mask |= 1 << reg;
temp_saved_regs.regs[reg] = sp + (short)word;
+
+ /* Starting with OSF/1-3.2C, the system libraries are shipped
+ without local symbols, but they still contain procedure
+ descriptors without a symbol reference. GDB is currently
+ unable to find these procedure descriptors and uses
+ heuristic_proc_desc instead.
+ As some low level compiler support routines (__div*, __add*)
+ use a non-standard return address register, we have to
+ add some heuristics to determine the return address register,
+ or stepping over these routines will fail.
+ Usually the return address register is the first register
+ saved on the stack, but assembler optimization might
+ rearrange the register saves.
+ So we recognize only a few registers (t7, t9, ra) within
+ the procedure prologue as valid return address registers.
+
+ FIXME: Rewriting GDB to access the procedure descriptors,
+ e.g. via the minimal symbol table, might obviate this hack. */
+ if (pcreg == -1
+ && cur_pc < (start_pc + 20)
+ && (reg == T7_REGNUM || reg == T9_REGNUM || reg == RA_REGNUM))
+ pcreg = reg;
}
else if (word == 0x47de040f) /* bis sp,sp fp */
has_frame_reg = 1;
}
+ if (pcreg == -1)
+ {
+ /* If we haven't found a valid return address register yet,
+ keep searching in the procedure prologue. */
+ while (cur_pc < (limit_pc + 20) && cur_pc < (start_pc + 20))
+ {
+ char buf[4];
+ unsigned long word;
+ int status;
+
+ status = read_memory_nobpt (cur_pc, buf, 4);
+ if (status)
+ memory_error (status, cur_pc);
+ cur_pc += 4;
+ word = extract_unsigned_integer (buf, 4);
+
+ if ((word & 0xfc1f0000) == 0xb41e0000 /* stq reg,n($sp) */
+ && (word & 0xffff0000) != 0xb7fe0000) /* reg != $zero */
+ {
+ int reg = (word & 0x03e00000) >> 21;
+ if (reg == T7_REGNUM || reg == T9_REGNUM || reg == RA_REGNUM)
+ {
+ pcreg = reg;
+ break;
+ }
+ }
+ }
+ }
+
if (has_frame_reg)
PROC_FRAME_REG(&temp_proc_desc) = GCC_FP_REGNUM;
else
PROC_FRAME_REG(&temp_proc_desc) = SP_REGNUM;
PROC_FRAME_OFFSET(&temp_proc_desc) = frame_size;
PROC_REG_MASK(&temp_proc_desc) = reg_mask;
- PROC_PC_REG(&temp_proc_desc) = RA_REGNUM;
+ PROC_PC_REG(&temp_proc_desc) = (pcreg == -1) ? RA_REGNUM : pcreg;
PROC_LOCALOFF(&temp_proc_desc) = 0; /* XXX - bogus */
return &temp_proc_desc;
}
if (proc_desc)
{
+ if (PROC_DESC_IS_DYN_SIGTRAMP (proc_desc))
+ return PROC_LOW_ADDR (proc_desc); /* "prologue" is in kernel */
+
/* If function is frameless, then we need to do it the hard way. I
strongly suspect that frameless always means prologueless... */
if (PROC_FRAME_REG (proc_desc) == SP_REGNUM
}
/* Return non-zero if we *might* be in a function prologue. Return zero if we
- are definatly *not* in a function prologue. */
+ are definitively *not* in a function prologue. */
static int
alpha_in_prologue (pc, proc_desc)
}
else
{
+ long offset;
+
/* Is linked_proc_desc_table really necessary? It only seems to be used
by procedure call dummys. However, the procedures being called ought
to have their own proc_descs, and even if they don't,
&& PROC_HIGH_ADDR(&link->info) > pc)
return &link->info;
+ /* If PC is inside a dynamically generated sigtramp handler,
+ create and push a procedure descriptor for that code: */
+ offset = DYNAMIC_SIGTRAMP_OFFSET (pc);
+ if (offset >= 0)
+ return push_sigtramp_desc (pc - offset);
+
if (startaddr == 0)
startaddr = heuristic_proc_start (pc);
/* The previous frame from a sigtramp frame might be frameless
and have frame size zero. */
&& !frame->signal_handler_caller)
- {
- /* The alpha __sigtramp routine is frameless and has a frame size
- of zero, but we are able to backtrace through it. */
- char *name;
- find_pc_partial_function (saved_pc, &name,
- (CORE_ADDR *)NULL, (CORE_ADDR *)NULL);
- if (IN_SIGTRAMP (saved_pc, name))
- return frame->frame;
- else
- return 0;
- }
+ return FRAME_PAST_SIGTRAMP_FRAME (frame, saved_pc);
else
return read_next_frame_reg(frame, PROC_FRAME_REG(proc_desc))
+ PROC_FRAME_OFFSET(proc_desc);
/* This may not be quite right, if proc has a real frame register.
Get the value of the frame relative sp, procedure might have been
interrupted by a signal at it's very start. */
- else if (frame->pc == PROC_LOW_ADDR (proc_desc) && !PROC_DESC_IS_DUMMY (proc_desc))
+ else if (frame->pc == PROC_LOW_ADDR (proc_desc)
+ && !PROC_DESC_IS_DYN_SIGTRAMP (proc_desc))
frame->frame = read_next_frame_reg (frame->next, SP_REGNUM);
else
frame->frame = read_next_frame_reg (frame->next, PROC_FRAME_REG (proc_desc))
write_register (SP_REGNUM, new_sp);
flush_cached_frames ();
- if (proc_desc && PROC_DESC_IS_DUMMY(proc_desc))
+ if (proc_desc && (PROC_DESC_IS_DUMMY(proc_desc)
+ || PROC_DESC_IS_DYN_SIGTRAMP (proc_desc)))
{
struct linked_proc_info *pi_ptr, *prev_ptr;