summaryrefslogtreecommitdiffstats
path: root/contrib/gdb/gdb/alpha-tdep.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/gdb/gdb/alpha-tdep.c')
-rw-r--r--contrib/gdb/gdb/alpha-tdep.c254
1 files changed, 195 insertions, 59 deletions
diff --git a/contrib/gdb/gdb/alpha-tdep.c b/contrib/gdb/gdb/alpha-tdep.c
index 7fbf642..0201016 100644
--- a/contrib/gdb/gdb/alpha-tdep.c
+++ b/contrib/gdb/gdb/alpha-tdep.c
@@ -1,5 +1,5 @@
/* Target-dependent code for the ALPHA architecture, for GDB, the GNU Debugger.
- Copyright 1993, 1994, 1995 Free Software Foundation, Inc.
+ Copyright 1993, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
This file is part of GDB.
@@ -31,11 +31,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/* FIXME: Some of this code should perhaps be merged with mips-tdep.c. */
-/* FIXME: Put this declaration in frame.h. */
-extern struct obstack frame_cache_obstack;
-
+/* Prototypes for local functions. */
-/* 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));
@@ -132,6 +130,113 @@ struct linked_proc_info
} *linked_proc_desc_table = NULL;
+/* Under GNU/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;
+}
+
+
+/* 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;
+}
+
+
+/* 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);
+}
+
+
/* Guaranteed to set frame->saved_regs to some values (it never leaves it
NULL). */
@@ -145,9 +250,7 @@ alpha_find_saved_regs (frame)
alpha_extra_func_info_t proc_desc;
int returnreg;
- frame->saved_regs = (struct frame_saved_regs *)
- obstack_alloc (&frame_cache_obstack, sizeof(struct frame_saved_regs));
- memset (frame->saved_regs, 0, sizeof (struct frame_saved_regs));
+ frame_saved_regs_zalloc (frame);
/* If it is the frame for __sigtramp, the saved registers are located
in a sigcontext structure somewhere on the stack. __sigtramp
@@ -161,25 +264,20 @@ alpha_find_saved_regs (frame)
#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;
- frame->saved_regs->regs[ireg] = reg_position;
+ frame->saved_regs[ireg] = reg_position;
}
for (ireg = 0; ireg < 32; ireg++)
{
reg_position = sigcontext_addr + SIGFRAME_FPREGSAVE_OFF + ireg * 8;
- frame->saved_regs->regs[FP0_REGNUM + ireg] = reg_position;
+ frame->saved_regs[FP0_REGNUM + ireg] = reg_position;
}
- frame->saved_regs->regs[PC_REGNUM] = sigcontext_addr + SIGFRAME_PC_OFF;
+ frame->saved_regs[PC_REGNUM] = sigcontext_addr + SIGFRAME_PC_OFF;
return;
}
@@ -202,7 +300,7 @@ alpha_find_saved_regs (frame)
register number. */
if (mask & (1 << returnreg))
{
- frame->saved_regs->regs[returnreg] = reg_position;
+ frame->saved_regs[returnreg] = reg_position;
reg_position += 8;
mask &= ~(1 << returnreg); /* Clear bit for RA so we
don't save again later. */
@@ -211,7 +309,7 @@ alpha_find_saved_regs (frame)
for (ireg = 0; ireg <= 31 ; ++ireg)
if (mask & (1 << ireg))
{
- frame->saved_regs->regs[ireg] = reg_position;
+ frame->saved_regs[ireg] = reg_position;
reg_position += 8;
}
@@ -224,11 +322,11 @@ alpha_find_saved_regs (frame)
for (ireg = 0; ireg <= 31 ; ++ireg)
if (mask & (1 << ireg))
{
- frame->saved_regs->regs[FP0_REGNUM+ireg] = reg_position;
+ frame->saved_regs[FP0_REGNUM+ireg] = reg_position;
reg_position += 8;
}
- frame->saved_regs->regs[PC_REGNUM] = frame->saved_regs->regs[returnreg];
+ frame->saved_regs[PC_REGNUM] = frame->saved_regs[returnreg];
}
static CORE_ADDR
@@ -246,8 +344,8 @@ read_next_frame_reg(fi, regno)
{
if (fi->saved_regs == NULL)
alpha_find_saved_regs (fi);
- if (fi->saved_regs->regs[regno])
- return read_memory_integer(fi->saved_regs->regs[regno], 8);
+ if (fi->saved_regs[regno])
+ return read_memory_integer(fi->saved_regs[regno], 8);
}
}
return read_register(regno);
@@ -285,13 +383,28 @@ alpha_saved_pc_after_call (frame)
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);
}
static struct alpha_extra_func_info temp_proc_desc;
static struct frame_saved_regs temp_saved_regs;
+/* Nonzero if instruction at PC is a return instruction. "ret
+ $zero,($ra),1" on alpha. */
+
+static int
+alpha_about_to_return (pc)
+ CORE_ADDR pc;
+{
+ return read_memory_integer (pc, 4) == 0x6bfa8001;
+}
+
+
+
/* This fencepost looks highly suspicious to me. Removing it also
seems suspicious as it could affect remote debugging across serial
lines. */
@@ -342,8 +455,8 @@ Otherwise, you told GDB there was a function where there isn't one, or\n\
return 0;
}
- else if (ABOUT_TO_RETURN(start_pc))
- break;
+ else if (alpha_about_to_return (start_pc))
+ break;
start_pc += 4; /* skip return */
return start_pc;
@@ -382,7 +495,15 @@ heuristic_proc_desc(start_pc, limit_pc, next_frame)
word = extract_unsigned_integer (buf, 4);
if ((word & 0xffff0000) == 0x23de0000) /* lda $sp,n($sp) */
- frame_size += (-word) & 0xffff;
+ {
+ if (word & 0x8000)
+ frame_size += (-word) & 0xffff;
+ else
+ /* Exit loop if a positive stack adjustment is found, which
+ usually means that the stack cleanup code in the function
+ epilogue is reached. */
+ break;
+ }
else if ((word & 0xfc1f0000) == 0xb41e0000 /* stq reg,n($sp) */
&& (word & 0xffff0000) != 0xb7fe0000) /* reg != $zero */
{
@@ -404,14 +525,18 @@ heuristic_proc_desc(start_pc, limit_pc, next_frame)
rearrange the register saves.
So we recognize only a few registers (t7, t9, ra) within
the procedure prologue as valid return address registers.
+ If we encounter a return instruction, we extract the
+ the return address register from it.
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)
+ && cur_pc < (start_pc + 80)
&& (reg == T7_REGNUM || reg == T9_REGNUM || reg == RA_REGNUM))
pcreg = reg;
}
+ else if ((word & 0xffe0ffff) == 0x6be08001) /* ret zero,reg,1 */
+ pcreg = (word >> 16) & 0x1f;
else if (word == 0x47de040f) /* bis sp,sp fp */
has_frame_reg = 1;
}
@@ -419,15 +544,13 @@ heuristic_proc_desc(start_pc, limit_pc, next_frame)
{
/* 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))
+ while (cur_pc < (limit_pc + 80) && cur_pc < (start_pc + 80))
{
char buf[4];
unsigned long word;
- int status;
- status = read_memory_nobpt (cur_pc, buf, 4);
- if (status)
- memory_error (status, cur_pc);
+ if (read_memory_nobpt (cur_pc, buf, 4))
+ break;
cur_pc += 4;
word = extract_unsigned_integer (buf, 4);
@@ -441,6 +564,11 @@ heuristic_proc_desc(start_pc, limit_pc, next_frame)
break;
}
}
+ else if ((word & 0xffe0ffff) == 0x6be08001) /* ret zero,reg,1 */
+ {
+ pcreg = (word >> 16) & 0x1f;
+ break;
+ }
}
}
@@ -471,6 +599,9 @@ after_prologue (pc, 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
@@ -493,7 +624,7 @@ after_prologue (pc, proc_desc)
}
/* 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)
@@ -599,6 +730,8 @@ find_proc_desc (pc, next_frame)
}
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,
@@ -610,7 +743,19 @@ find_proc_desc (pc, next_frame)
&& PROC_HIGH_ADDR(&link->info) > pc)
return &link->info;
- if (startaddr == 0)
+ /* 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 heuristic_fence_post is non-zero, determine the procedure
+ start address by examining the instructions.
+ This allows us to find the start address of static functions which
+ have no symbolic information, as startaddr would have been set to
+ the preceding global function start address by the
+ find_pc_partial_function call above. */
+ if (startaddr == 0 || heuristic_fence_post != 0)
startaddr = heuristic_proc_start (pc);
proc_desc =
@@ -650,17 +795,7 @@ alpha_frame_chain(frame)
/* 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);
@@ -696,7 +831,8 @@ init_extra_frame_info (frame)
/* 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))
@@ -713,12 +849,11 @@ init_extra_frame_info (frame)
(CORE_ADDR *)NULL,(CORE_ADDR *)NULL);
if (!IN_SIGTRAMP (frame->pc, name))
{
- frame->saved_regs = (struct frame_saved_regs*)
- obstack_alloc (&frame_cache_obstack,
- sizeof (struct frame_saved_regs));
- *frame->saved_regs = temp_saved_regs;
- frame->saved_regs->regs[PC_REGNUM]
- = frame->saved_regs->regs[RA_REGNUM];
+ frame->saved_regs = (CORE_ADDR*)
+ frame_obstack_alloc (SIZEOF_FRAME_SAVED_REGS);
+ memcpy (frame->saved_regs, temp_saved_regs.regs, SIZEOF_FRAME_SAVED_REGS);
+ frame->saved_regs[PC_REGNUM]
+ = frame->saved_regs[RA_REGNUM];
}
}
}
@@ -878,7 +1013,7 @@ alpha_push_dummy_frame()
*/
/* MASK(i,j) == (1<<i) + (1<<(i+1)) + ... + (1<<j)). Assume i<=j<31. */
-#define MASK(i,j) (((1L << ((j)+1)) - 1) ^ ((1L << (i)) - 1))
+#define MASK(i,j) ((((LONGEST)1 << ((j)+1)) - 1) ^ (((LONGEST)1 << (i)) - 1))
#define GEN_REG_SAVE_MASK (MASK(0,8) | MASK(16,29))
#define GEN_REG_SAVE_COUNT 24
#define FLOAT_REG_SAVE_MASK (MASK(0,1) | MASK(10,30))
@@ -974,17 +1109,18 @@ alpha_pop_frame()
for (regnum = 32; --regnum >= 0; )
if (PROC_REG_MASK(proc_desc) & (1 << regnum))
write_register (regnum,
- read_memory_integer (frame->saved_regs->regs[regnum],
+ read_memory_integer (frame->saved_regs[regnum],
8));
for (regnum = 32; --regnum >= 0; )
if (PROC_FREG_MASK(proc_desc) & (1 << regnum))
write_register (regnum + FP0_REGNUM,
- read_memory_integer (frame->saved_regs->regs[regnum + FP0_REGNUM], 8));
+ read_memory_integer (frame->saved_regs[regnum + FP0_REGNUM], 8));
}
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;
@@ -1129,7 +1265,7 @@ alpha_register_convert_to_virtual (regnum, valtype, raw_buffer, virtual_buffer)
}
else if (TYPE_CODE (valtype) == TYPE_CODE_INT && TYPE_LENGTH (valtype) <= 4)
{
- unsigned LONGEST l;
+ ULONGEST l;
l = extract_unsigned_integer (raw_buffer, REGISTER_RAW_SIZE (regnum));
l = ((l >> 32) & 0xc0000000) | ((l >> 29) & 0x3fffffff);
store_unsigned_integer (virtual_buffer, TYPE_LENGTH (valtype), l);
@@ -1158,7 +1294,7 @@ alpha_register_convert_to_raw (valtype, regnum, virtual_buffer, raw_buffer)
}
else if (TYPE_CODE (valtype) == TYPE_CODE_INT && TYPE_LENGTH (valtype) <= 4)
{
- unsigned LONGEST l;
+ ULONGEST l;
if (TYPE_UNSIGNED (valtype))
l = extract_unsigned_integer (virtual_buffer, TYPE_LENGTH (valtype));
else
OpenPOWER on IntegriCloud