2011-03-08 Maxim Grigoriev <maxim2405@gmail.com>

* xtensa-tdep.c (xtensa_read_register): New function.
	(xtensa_write_register): New function.
	(xtensa_find_register_by_name): New function.
	(xtensa_windowed_frame_cache): Update comments in type description.
	(xtensa_frame_cache): Likewise.
	(xtensa_window_interrupt_insn): New function.
	(xtensa_frame_cache): Add analysis for Xtensa Window Exception frames.
	(xtensa_insn_kind): Add new instructions.
	(rwx_special_register): New function.
	(call0_classify_opcode): Add new instructions to the analysis.
	(a0_saved, a7_saved, a11_saved): New variables.
	(a0_was_saved, a7_was_saved, a11_was_saved): New variables.
	(execute_l32e): New function.
	(execute_s32e): New function.
	(xtensa_exception_handler_t): New type.
	(execute_code): New function.
	(xtensa_window_interrupt_frame_cache): New function to conduct frame
	analysis for Xtensa Window Exception handlers.
This commit is contained in:
Maxim Grigoriev 2011-03-09 02:25:12 +00:00
parent ecb351e919
commit 08b9c608aa
2 changed files with 397 additions and 19 deletions

View File

@ -1,3 +1,24 @@
2011-03-08 Maxim Grigoriev <maxim2405@gmail.com>
* xtensa-tdep.c (xtensa_read_register): New function.
(xtensa_write_register): New function.
(xtensa_find_register_by_name): New function.
(xtensa_windowed_frame_cache): Update comments in type description.
(xtensa_frame_cache): Likewise.
(xtensa_window_interrupt_insn): New function.
(xtensa_frame_cache): Add analysis for Xtensa Window Exception frames.
(xtensa_insn_kind): Add new instructions.
(rwx_special_register): New function.
(call0_classify_opcode): Add new instructions to the analysis.
(a0_saved, a7_saved, a11_saved): New variables.
(a0_was_saved, a7_was_saved, a11_was_saved): New variables.
(execute_l32e): New function.
(execute_s32e): New function.
(xtensa_exception_handler_t): New type.
(execute_code): New function.
(xtensa_window_interrupt_frame_cache): New function to conduct frame
analysis for Xtensa Window Exception handlers.
2011-03-08 Maxim Grigoriev <maxim2405@gmail.com>
* xtensa-tdep.c (TX_PS): New.

View File

@ -161,6 +161,21 @@ areg_number (struct gdbarch *gdbarch, int ar_regnum, unsigned int wb)
return (areg > 15) ? -1 : areg;
}
static inline unsigned long
xtensa_read_register (int regnum)
{
ULONGEST value;
regcache_raw_read_unsigned (get_current_regcache (), regnum, &value);
return (unsigned long) value;
}
static inline void
xtensa_write_register (int regnum, ULONGEST value)
{
regcache_raw_write_unsigned (get_current_regcache (), regnum, value);
}
/* Return the window size of the previous call to the function from which we
have just returned.
@ -210,6 +225,22 @@ extract_call_winsize (struct gdbarch *gdbarch, CORE_ADDR pc)
/* REGISTER INFORMATION */
/* Find register by name. */
static int
xtensa_find_register_by_name (struct gdbarch *gdbarch, char *name)
{
int i;
for (i = 0; i < gdbarch_num_regs (gdbarch)
+ gdbarch_num_pseudo_regs (gdbarch);
i++)
if (strcasecmp (gdbarch_tdep (gdbarch)->regmap[i].name, name) == 0)
return i;
return -1;
}
/* Returns the name of a register. */
static const char *
xtensa_register_name (struct gdbarch *gdbarch, int regnum)
@ -907,14 +938,13 @@ typedef struct xtensa_windowed_frame_cache
{
int wb; /* WINDOWBASE of the previous frame. */
int callsize; /* Call size of this frame. */
int ws; /* WINDOWSTART of the previous frame. It
keeps track of life windows only. If there
is no bit set for the window, that means it
had been already spilled because of window
overflow. */
int ws; /* WINDOWSTART of the previous frame. It keeps track of
life windows only. If there is no bit set for the
window, that means it had been already spilled
because of window overflow. */
/* Spilled A-registers from the previous frame.
AREGS[i] == -1, if corresponding AR is alive. */
/* Addresses of spilled A-registers.
AREGS[i] == -1, if corresponding AR is alive. */
CORE_ADDR aregs[XTENSA_NUM_SAVED_AREGS];
} xtensa_windowed_frame_cache_t;
@ -968,10 +998,10 @@ typedef struct xtensa_call0_frame_cache
typedef struct xtensa_frame_cache
{
CORE_ADDR base; /* Stack pointer of this frame. */
CORE_ADDR pc; /* PC at the entry point to the function. */
CORE_ADDR ra; /* The raw return address (without CALLINC). */
CORE_ADDR ps; /* The PS register of this frame. */
CORE_ADDR prev_sp; /* Stack Pointer of the previous frame. */
CORE_ADDR pc; /* PC of this frame at the function entry point. */
CORE_ADDR ra; /* The raw return address of this frame. */
CORE_ADDR ps; /* The PS register of the previous (older) frame. */
CORE_ADDR prev_sp; /* Stack Pointer of the previous (older) frame. */
int call0; /* It's a call0 framework (else windowed). */
union
{
@ -1064,6 +1094,38 @@ xtensa_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
return frame_id_build (fp + SP_ALIGNMENT, pc);
}
/* Returns true, if instruction to execute next is unique to Xtensa Window
Interrupt Handlers. It can only be one of L32E, S32E, RFWO, or RFWU. */
static int
xtensa_window_interrupt_insn (struct gdbarch *gdbarch, CORE_ADDR pc)
{
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
unsigned int insn = read_memory_integer (pc, 4, byte_order);
unsigned int code;
if (byte_order == BFD_ENDIAN_BIG)
{
/* Check, if this is L32E or S32E. */
code = insn & 0xf000ff00;
if ((code == 0x00009000) || (code == 0x00009400))
return 1;
/* Check, if this is RFWU or RFWO. */
code = insn & 0xffffff00;
return ((code == 0x00430000) || (code == 0x00530000));
}
else
{
/* Check, if this is L32E or S32E. */
code = insn & 0x00ff000f;
if ((code == 0x090000) || (code == 0x490000))
return 1;
/* Check, if this is RFWU or RFWO. */
code = insn & 0x00ffffff;
return ((code == 0x00003400) || (code == 0x00003500));
}
}
/* Returns the best guess about which register is a frame pointer
for the function containing CURRENT_PC. */
@ -1195,6 +1257,11 @@ call0_frame_cache (struct frame_info *this_frame,
xtensa_frame_cache_t *cache,
CORE_ADDR pc, CORE_ADDR litbase);
static void
xtensa_window_interrupt_frame_cache (struct frame_info *this_frame,
xtensa_frame_cache_t *cache,
CORE_ADDR pc);
static struct xtensa_frame_cache *
xtensa_frame_cache (struct frame_info *this_frame, void **this_cache)
{
@ -1302,9 +1369,8 @@ xtensa_frame_cache (struct frame_info *this_frame, void **this_cache)
}
if ((cache->prev_sp == 0) && ( ra != 0 ))
/* If RA is equal to 0 this frame is an outermost frame.
Leave cache->prev_sp unchanged marking the boundary of the
frame stack. */
/* If RA is equal to 0 this frame is an outermost frame. Leave
cache->prev_sp unchanged marking the boundary of the frame stack. */
{
if ((cache->wd.ws & (1 << cache->wd.wb)) == 0)
{
@ -1321,11 +1387,18 @@ xtensa_frame_cache (struct frame_info *this_frame, void **this_cache)
(gdbarch, gdbarch_tdep (gdbarch)->a0_base + 1,
cache->wd.wb);
cache->prev_sp = get_frame_register_unsigned (this_frame,
regnum);
cache->prev_sp = xtensa_read_register (regnum);
}
}
}
else if (xtensa_window_interrupt_insn (gdbarch, pc))
{
/* Execution stopped inside Xtensa Window Interrupt Handler. */
xtensa_window_interrupt_frame_cache (this_frame, cache, pc);
/* Everything was set already, including cache->base. */
return cache;
}
else /* Call0 framework. */
{
unsigned int litbase_regnum = gdbarch_tdep (gdbarch)->litbase_regnum;
@ -1941,11 +2014,33 @@ typedef enum {
c0opc_mov, /* Moving a register to a register. */
c0opc_movi, /* Moving an immediate to a register. */
c0opc_l32r, /* Loading a literal. */
c0opc_s32i, /* Storing word at fixed offset from a base
register. */
c0opc_s32i, /* Storing word at fixed offset from a base register. */
c0opc_rwxsr, /* RSR, WRS, or XSR instructions. */
c0opc_l32e, /* L32E instruction. */
c0opc_s32e, /* S32E instruction. */
c0opc_rfwo, /* RFWO instruction. */
c0opc_rfwu, /* RFWU instruction. */
c0opc_NrOf /* Number of opcode classifications. */
} xtensa_insn_kind;
/* Return true, if OPCNAME is RSR, WRS, or XSR instruction. */
static int
rwx_special_register (const char *opcname)
{
char ch = *opcname++;
if ((ch != 'r') && (ch != 'w') && (ch != 'x'))
return 0;
if (*opcname++ != 's')
return 0;
if (*opcname++ != 'r')
return 0;
if (*opcname++ != '.')
return 0;
return 1;
}
/* Classify an opcode based on what it means for Call0 prologue analysis. */
@ -1970,6 +2065,10 @@ call0_classify_opcode (xtensa_isa isa, xtensa_opcode opc)
opclass = c0opc_break;
else if (strcasecmp (opcname, "entry") == 0)
opclass = c0opc_entry;
else if (strcasecmp (opcname, "rfwo") == 0)
opclass = c0opc_rfwo;
else if (strcasecmp (opcname, "rfwu") == 0)
opclass = c0opc_rfwu;
else if (xtensa_opcode_is_branch (isa, opc) > 0
|| xtensa_opcode_is_jump (isa, opc) > 0
|| xtensa_opcode_is_loop (isa, opc) > 0
@ -1999,6 +2098,12 @@ call0_classify_opcode (xtensa_isa isa, xtensa_opcode opc)
else if (strcasecmp (opcname, "s32i") == 0
|| strcasecmp (opcname, "s32i.n") == 0)
opclass = c0opc_s32i;
else if (strcasecmp (opcname, "l32e") == 0)
opclass = c0opc_l32e;
else if (strcasecmp (opcname, "s32e") == 0)
opclass = c0opc_s32e;
else if (rwx_special_register (opcname))
opclass = c0opc_rwxsr;
return opclass;
}
@ -2028,7 +2133,7 @@ call0_track_op (struct gdbarch *gdbarch,
break;
case c0opc_add:
/* 3 operands: dst, src1, src2. */
gdb_assert (nods == 3);
gdb_assert (nods == 3);
if (src[odv[1]].fr_reg == C0_CONST)
{
dst[odv[0]].fr_reg = src[odv[2]].fr_reg;
@ -2458,6 +2563,258 @@ analysis failed in this frame. GDB command execution stopped."));
cache->c0.c0_fp = fp;
}
static CORE_ADDR a0_saved;
static CORE_ADDR a7_saved;
static CORE_ADDR a11_saved;
static int a0_was_saved;
static int a7_was_saved;
static int a11_was_saved;
/* Simulate L32E insn: AT <-- ref (AS + offset). */
static void
execute_l32e (struct gdbarch *gdbarch, int at, int as, int offset, CORE_ADDR wb)
{
int atreg = arreg_number (gdbarch, gdbarch_tdep (gdbarch)->a0_base + at, wb);
int asreg = arreg_number (gdbarch, gdbarch_tdep (gdbarch)->a0_base + as, wb);
CORE_ADDR addr = xtensa_read_register (asreg) + offset;
unsigned int spilled_value
= read_memory_unsigned_integer (addr, 4, gdbarch_byte_order (gdbarch));
if ((at == 0) && !a0_was_saved)
{
a0_saved = xtensa_read_register (atreg);
a0_was_saved = 1;
}
else if ((at == 7) && !a7_was_saved)
{
a7_saved = xtensa_read_register (atreg);
a7_was_saved = 1;
}
else if ((at == 11) && !a11_was_saved)
{
a11_saved = xtensa_read_register (atreg);
a11_was_saved = 1;
}
xtensa_write_register (atreg, spilled_value);
}
/* Simulate S32E insn: AT --> ref (AS + offset). */
static void
execute_s32e (struct gdbarch *gdbarch, int at, int as, int offset, CORE_ADDR wb)
{
int atreg = arreg_number (gdbarch, gdbarch_tdep (gdbarch)->a0_base + at, wb);
int asreg = arreg_number (gdbarch, gdbarch_tdep (gdbarch)->a0_base + as, wb);
CORE_ADDR addr = xtensa_read_register (asreg) + offset;
ULONGEST spilled_value = xtensa_read_register (atreg);
write_memory_unsigned_integer (addr, 4,
gdbarch_byte_order (gdbarch),
spilled_value);
}
#define XTENSA_MAX_WINDOW_INTERRUPT_HANDLER_LEN 200
typedef enum {
xtWindowOverflow,
xtWindowUnderflow,
xtNoExceptionHandler
} xtensa_exception_handler_t;
/* Execute insn stream from current PC until hitting RFWU or RFWO.
Return type of Xtensa Window Interrupt Handler on success. */
static xtensa_exception_handler_t
execute_code (struct gdbarch *gdbarch, CORE_ADDR current_pc, CORE_ADDR wb)
{
xtensa_isa isa;
xtensa_insnbuf ins, slot;
char ibuf[XTENSA_ISA_BSZ];
CORE_ADDR ia, bt, ba;
xtensa_format ifmt;
int ilen, islots, is;
xtensa_opcode opc;
int insn_num = 0;
int fail = 0;
void (*func) (struct gdbarch *, int, int, int, CORE_ADDR);
int at, as, offset;
int num_operands;
/* WindowUnderflow12 = true, when inside _WindowUnderflow12. */
int WindowUnderflow12 = (current_pc & 0x1ff) >= 0x140;
isa = xtensa_default_isa;
gdb_assert (XTENSA_ISA_BSZ >= xtensa_isa_maxlength (isa));
ins = xtensa_insnbuf_alloc (isa);
slot = xtensa_insnbuf_alloc (isa);
ba = 0;
ia = current_pc;
bt = ia;
a0_was_saved = 0;
a7_was_saved = 0;
a11_was_saved = 0;
while (insn_num++ < XTENSA_MAX_WINDOW_INTERRUPT_HANDLER_LEN)
{
if (ia + xtensa_isa_maxlength (isa) > bt)
{
ba = ia;
bt = (ba + XTENSA_ISA_BSZ);
if (target_read_memory (ba, ibuf, bt - ba) != 0)
return xtNoExceptionHandler;
}
xtensa_insnbuf_from_chars (isa, ins, &ibuf[ia-ba], 0);
ifmt = xtensa_format_decode (isa, ins);
if (ifmt == XTENSA_UNDEFINED)
return xtNoExceptionHandler;
ilen = xtensa_format_length (isa, ifmt);
if (ilen == XTENSA_UNDEFINED)
return xtNoExceptionHandler;
islots = xtensa_format_num_slots (isa, ifmt);
if (islots == XTENSA_UNDEFINED)
return xtNoExceptionHandler;
for (is = 0; is < islots; ++is)
{
if (xtensa_format_get_slot (isa, ifmt, is, ins, slot))
return xtNoExceptionHandler;
opc = xtensa_opcode_decode (isa, ifmt, is, slot);
if (opc == XTENSA_UNDEFINED)
return xtNoExceptionHandler;
switch (call0_classify_opcode (isa, opc))
{
case c0opc_illegal:
case c0opc_flow:
case c0opc_entry:
case c0opc_break:
/* We expect none of them here. */
return xtNoExceptionHandler;
case c0opc_l32e:
func = execute_l32e;
break;
case c0opc_s32e:
func = execute_s32e;
break;
case c0opc_rfwo: /* RFWO. */
/* Here, we return from WindowOverflow handler and,
if we stopped at the very beginning, which means
A0 was saved, we have to restore it now. */
if (a0_was_saved)
{
int arreg = arreg_number (gdbarch,
gdbarch_tdep (gdbarch)->a0_base,
wb);
xtensa_write_register (arreg, a0_saved);
}
return xtWindowOverflow;
case c0opc_rfwu: /* RFWU. */
/* Here, we return from WindowUnderflow handler.
Let's see if either A7 or A11 has to be restored. */
if (WindowUnderflow12)
{
if (a11_was_saved)
{
int arreg = arreg_number (gdbarch,
gdbarch_tdep (gdbarch)->a0_base + 11,
wb);
xtensa_write_register (arreg, a11_saved);
}
}
else if (a7_was_saved)
{
int arreg = arreg_number (gdbarch,
gdbarch_tdep (gdbarch)->a0_base + 7,
wb);
xtensa_write_register (arreg, a7_saved);
}
return xtWindowUnderflow;
default: /* Simply skip this insns. */
continue;
}
/* Decode arguments for L32E / S32E and simulate their execution. */
if ( xtensa_opcode_num_operands (isa, opc) != 3 )
return xtNoExceptionHandler;
if (xtensa_operand_get_field (isa, opc, 0, ifmt, is, slot, &at))
return xtNoExceptionHandler;
if (xtensa_operand_decode (isa, opc, 0, &at))
return xtNoExceptionHandler;
if (xtensa_operand_get_field (isa, opc, 1, ifmt, is, slot, &as))
return xtNoExceptionHandler;
if (xtensa_operand_decode (isa, opc, 1, &as))
return xtNoExceptionHandler;
if (xtensa_operand_get_field (isa, opc, 2, ifmt, is, slot, &offset))
return xtNoExceptionHandler;
if (xtensa_operand_decode (isa, opc, 2, &offset))
return xtNoExceptionHandler;
(*func) (gdbarch, at, as, offset, wb);
}
ia += ilen;
}
return xtNoExceptionHandler;
}
/* Handle Window Overflow / Underflow exception frames. */
static void
xtensa_window_interrupt_frame_cache (struct frame_info *this_frame,
xtensa_frame_cache_t *cache,
CORE_ADDR pc)
{
struct gdbarch *gdbarch = get_frame_arch (this_frame);
CORE_ADDR ps, wb, ws, ra;
int epc1_regnum, i, regnum;
xtensa_exception_handler_t eh_type;
/* Read PS, WB, and WS from the hardware. Note that PS register
must be present, if Windowed ABI is supported. */
ps = xtensa_read_register (gdbarch_ps_regnum (gdbarch));
wb = xtensa_read_register (gdbarch_tdep (gdbarch)->wb_regnum);
ws = xtensa_read_register (gdbarch_tdep (gdbarch)->ws_regnum);
/* Execute all the remaining instructions from Window Interrupt Handler
by simulating them on the remote protocol level. On return, set the
type of Xtensa Window Interrupt Handler, or report an error. */
eh_type = execute_code (gdbarch, pc, wb);
if (eh_type == xtNoExceptionHandler)
error (_("\
Unable to decode Xtensa Window Interrupt Handler's code."));
cache->ps = ps ^ PS_EXC; /* Clear the exception bit in PS. */
cache->call0 = 0; /* It's Windowed ABI. */
/* All registers for the cached frame will be alive. */
for (i = 0; i < XTENSA_NUM_SAVED_AREGS; i++)
cache->wd.aregs[i] = -1;
if (eh_type == xtWindowOverflow)
cache->wd.ws = ws ^ (1 << wb);
else /* eh_type == xtWindowUnderflow. */
cache->wd.ws = ws | (1 << wb);
cache->wd.wb = (ps & 0xf00) >> 8; /* Set WB to OWB. */
regnum = arreg_number (gdbarch, gdbarch_tdep (gdbarch)->a0_base,
cache->wd.wb);
ra = xtensa_read_register (regnum);
cache->wd.callsize = WINSIZE (ra);
cache->prev_sp = xtensa_read_register (regnum + 1);
/* Set regnum to a frame pointer of the frame being cached. */
regnum = xtensa_scan_prologue (gdbarch, pc);
regnum = arreg_number (gdbarch,
gdbarch_tdep (gdbarch)->a0_base + regnum,
cache->wd.wb);
cache->base = get_frame_register_unsigned (this_frame, regnum);
/* Read PC of interrupted function from EPC1 register. */
epc1_regnum = xtensa_find_register_by_name (gdbarch,"epc1");
if (epc1_regnum < 0)
error(_("Unable to read Xtensa register EPC1"));
cache->ra = xtensa_read_register (epc1_regnum);
cache->pc = get_frame_func (this_frame);
}
/* Skip function prologue.