2002-12-19 08:16:50 +01:00
|
|
|
|
/* libunwind - a platform-independent unwind library
|
2003-01-21 18:41:20 +01:00
|
|
|
|
Copyright (C) 2001-2003 Hewlett-Packard Co
|
2002-12-19 08:16:50 +01:00
|
|
|
|
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
|
|
|
|
|
|
|
|
|
|
This file is part of libunwind.
|
|
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
|
a copy of this software and associated documentation files (the
|
|
|
|
|
"Software"), to deal in the Software without restriction, including
|
|
|
|
|
without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
|
permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
|
the following conditions:
|
|
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
|
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
|
|
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
|
|
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
|
|
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
#include "unwind_i.h"
|
|
|
|
|
|
|
|
|
|
/* forward declaration: */
|
|
|
|
|
static int create_state_record_for (struct cursor *c,
|
|
|
|
|
struct ia64_state_record *sr,
|
|
|
|
|
unw_word_t ip);
|
|
|
|
|
|
|
|
|
|
typedef unsigned long unw_word;
|
|
|
|
|
|
|
|
|
|
#define alloc_reg_state() (mempool_alloc (&unw.state_record_pool))
|
|
|
|
|
#define free_reg_state(rs) (mempool_free (&unw.state_record_pool, rs))
|
|
|
|
|
#define alloc_labeled_state() (mempool_alloc (&unw.labeled_state_pool))
|
|
|
|
|
#define free_labeled_state(s) (mempool_free (&unw.labeled_state_pool, s))
|
|
|
|
|
|
|
|
|
|
/* Routines to manipulate the state stack. */
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
push (struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
struct ia64_reg_state *rs;
|
|
|
|
|
|
|
|
|
|
rs = alloc_reg_state ();
|
|
|
|
|
if (!rs)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, "unwind: cannot stack reg state!\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
memcpy (rs, &sr->curr, sizeof (*rs));
|
|
|
|
|
sr->curr.next = rs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
pop (struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
struct ia64_reg_state *rs = sr->curr.next;
|
|
|
|
|
|
|
|
|
|
if (!rs)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, "unwind: stack underflow!\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
memcpy (&sr->curr, rs, sizeof (*rs));
|
|
|
|
|
free_reg_state (rs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make a copy of the state stack. Non-recursive to avoid stack overflows. */
|
|
|
|
|
static struct ia64_reg_state *
|
|
|
|
|
dup_state_stack (struct ia64_reg_state *rs)
|
|
|
|
|
{
|
|
|
|
|
struct ia64_reg_state *copy, *prev = NULL, *first = NULL;
|
|
|
|
|
|
|
|
|
|
while (rs)
|
|
|
|
|
{
|
|
|
|
|
copy = alloc_reg_state ();
|
|
|
|
|
if (!copy)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, "unwind.dup_state_stack: out of memory\n");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
memcpy (copy, rs, sizeof (*copy));
|
|
|
|
|
if (first)
|
|
|
|
|
prev->next = copy;
|
|
|
|
|
else
|
|
|
|
|
first = copy;
|
|
|
|
|
rs = rs->next;
|
|
|
|
|
prev = copy;
|
|
|
|
|
}
|
|
|
|
|
return first;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Free all stacked register states (but not RS itself). */
|
|
|
|
|
static void
|
|
|
|
|
free_state_stack (struct ia64_reg_state *rs)
|
|
|
|
|
{
|
|
|
|
|
struct ia64_reg_state *p, *next;
|
|
|
|
|
|
|
|
|
|
for (p = rs->next; p != NULL; p = next)
|
|
|
|
|
{
|
|
|
|
|
next = p->next;
|
|
|
|
|
free_reg_state (p);
|
|
|
|
|
}
|
|
|
|
|
rs->next = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Unwind decoder routines */
|
|
|
|
|
|
|
|
|
|
static enum ia64_pregnum __attribute__ ((const))
|
|
|
|
|
decode_abreg (unsigned char abreg, int memory)
|
|
|
|
|
{
|
|
|
|
|
switch (abreg)
|
|
|
|
|
{
|
|
|
|
|
case 0x04 ... 0x07:
|
|
|
|
|
return IA64_REG_R4 + (abreg - 0x04);
|
|
|
|
|
case 0x22 ... 0x25:
|
|
|
|
|
return IA64_REG_F2 + (abreg - 0x22);
|
|
|
|
|
case 0x30 ... 0x3f:
|
|
|
|
|
return IA64_REG_F16 + (abreg - 0x30);
|
|
|
|
|
case 0x41 ... 0x45:
|
|
|
|
|
return IA64_REG_B1 + (abreg - 0x41);
|
|
|
|
|
case 0x60:
|
|
|
|
|
return IA64_REG_PR;
|
|
|
|
|
case 0x61:
|
|
|
|
|
return IA64_REG_PSP;
|
|
|
|
|
case 0x62:
|
|
|
|
|
return memory ? IA64_REG_PRI_UNAT_MEM : IA64_REG_PRI_UNAT_GR;
|
|
|
|
|
case 0x63:
|
|
|
|
|
return IA64_REG_RP;
|
|
|
|
|
case 0x64:
|
|
|
|
|
return IA64_REG_BSP;
|
|
|
|
|
case 0x65:
|
|
|
|
|
return IA64_REG_BSPSTORE;
|
|
|
|
|
case 0x66:
|
|
|
|
|
return IA64_REG_RNAT;
|
|
|
|
|
case 0x67:
|
|
|
|
|
return IA64_REG_UNAT;
|
|
|
|
|
case 0x68:
|
|
|
|
|
return IA64_REG_FPSR;
|
|
|
|
|
case 0x69:
|
|
|
|
|
return IA64_REG_PFS;
|
|
|
|
|
case 0x6a:
|
|
|
|
|
return IA64_REG_LC;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
dprintf ("unwind: bad abreg=0x%x\n", abreg);
|
|
|
|
|
return IA64_REG_LC;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
set_reg (struct ia64_reg_info *reg, enum ia64_where where, int when,
|
|
|
|
|
unsigned long val)
|
|
|
|
|
{
|
|
|
|
|
reg->val = val;
|
|
|
|
|
reg->where = where;
|
|
|
|
|
if (reg->when == IA64_WHEN_NEVER)
|
|
|
|
|
reg->when = when;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
alloc_spill_area (unsigned long *offp, unsigned long regsize,
|
|
|
|
|
struct ia64_reg_info *lo, struct ia64_reg_info *hi)
|
|
|
|
|
{
|
|
|
|
|
struct ia64_reg_info *reg;
|
|
|
|
|
|
|
|
|
|
for (reg = hi; reg >= lo; --reg)
|
|
|
|
|
{
|
|
|
|
|
if (reg->where == IA64_WHERE_SPILL_HOME)
|
|
|
|
|
{
|
|
|
|
|
reg->where = IA64_WHERE_PSPREL;
|
|
|
|
|
*offp -= regsize;
|
|
|
|
|
reg->val = *offp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
spill_next_when (struct ia64_reg_info **regp, struct ia64_reg_info *lim,
|
|
|
|
|
unw_word t)
|
|
|
|
|
{
|
|
|
|
|
struct ia64_reg_info *reg;
|
|
|
|
|
|
|
|
|
|
for (reg = *regp; reg <= lim; ++reg)
|
|
|
|
|
{
|
|
|
|
|
if (reg->where == IA64_WHERE_SPILL_HOME)
|
|
|
|
|
{
|
|
|
|
|
reg->when = t;
|
|
|
|
|
*regp = reg + 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dprintf ("unwind: excess spill!\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
finish_prologue (struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
struct ia64_reg_info *reg;
|
|
|
|
|
unsigned long off;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/* First, resolve implicit register save locations (see Section
|
|
|
|
|
"11.4.2.3 Rules for Using Unwind Descriptors", rule 3). */
|
2003-02-08 11:10:59 +01:00
|
|
|
|
for (i = 0; i < (int) NELEMS (unw.save_order); ++i)
|
2002-12-19 08:16:50 +01:00
|
|
|
|
{
|
|
|
|
|
reg = sr->curr.reg + unw.save_order[i];
|
|
|
|
|
if (reg->where == IA64_WHERE_GR_SAVE)
|
|
|
|
|
{
|
|
|
|
|
reg->where = IA64_WHERE_GR;
|
|
|
|
|
reg->val = sr->gr_save_loc++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Next, compute when the fp, general, and branch registers get
|
|
|
|
|
saved. This must come before alloc_spill_area() because we need
|
|
|
|
|
to know which registers are spilled to their home locations. */
|
|
|
|
|
|
|
|
|
|
if (sr->imask)
|
|
|
|
|
{
|
|
|
|
|
unsigned char kind, mask = 0, *cp = sr->imask;
|
|
|
|
|
unsigned long t;
|
|
|
|
|
static const unsigned char limit[3] =
|
|
|
|
|
{
|
|
|
|
|
IA64_REG_F31, IA64_REG_R7, IA64_REG_B5
|
|
|
|
|
};
|
|
|
|
|
struct ia64_reg_info *(regs[3]);
|
|
|
|
|
|
|
|
|
|
regs[0] = sr->curr.reg + IA64_REG_F2;
|
|
|
|
|
regs[1] = sr->curr.reg + IA64_REG_R4;
|
|
|
|
|
regs[2] = sr->curr.reg + IA64_REG_B1;
|
|
|
|
|
|
2003-02-08 11:10:59 +01:00
|
|
|
|
for (t = 0; (int) t < sr->region_len; ++t)
|
2002-12-19 08:16:50 +01:00
|
|
|
|
{
|
|
|
|
|
if ((t & 3) == 0)
|
|
|
|
|
mask = *cp++;
|
|
|
|
|
kind = (mask >> 2 * (3 - (t & 3))) & 3;
|
|
|
|
|
if (kind > 0)
|
|
|
|
|
spill_next_when (®s[kind - 1], sr->curr.reg + limit[kind - 1],
|
|
|
|
|
sr->region_start + t);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Next, lay out the memory stack spill area. */
|
|
|
|
|
|
|
|
|
|
if (sr->any_spills)
|
|
|
|
|
{
|
|
|
|
|
off = sr->spill_offset;
|
|
|
|
|
alloc_spill_area (&off, 16, sr->curr.reg + IA64_REG_F2,
|
|
|
|
|
sr->curr.reg + IA64_REG_F31);
|
|
|
|
|
alloc_spill_area (&off, 8, sr->curr.reg + IA64_REG_B1,
|
|
|
|
|
sr->curr.reg + IA64_REG_B5);
|
|
|
|
|
alloc_spill_area (&off, 8, sr->curr.reg + IA64_REG_R4,
|
|
|
|
|
sr->curr.reg + IA64_REG_R7);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Region header descriptors. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
desc_prologue (int body, unw_word rlen, unsigned char mask,
|
|
|
|
|
unsigned char grsave, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (!(sr->in_body || sr->first_region))
|
|
|
|
|
finish_prologue (sr);
|
|
|
|
|
sr->first_region = 0;
|
|
|
|
|
|
|
|
|
|
/* check if we're done: */
|
|
|
|
|
if (sr->when_target < sr->region_start + sr->region_len)
|
|
|
|
|
{
|
|
|
|
|
sr->done = 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < sr->epilogue_count; ++i)
|
|
|
|
|
pop (sr);
|
|
|
|
|
sr->epilogue_count = 0;
|
|
|
|
|
sr->epilogue_start = IA64_WHEN_NEVER;
|
|
|
|
|
|
|
|
|
|
if (!body)
|
|
|
|
|
push (sr);
|
|
|
|
|
|
|
|
|
|
sr->region_start += sr->region_len;
|
|
|
|
|
sr->region_len = rlen;
|
|
|
|
|
sr->in_body = body;
|
|
|
|
|
|
|
|
|
|
if (!body)
|
|
|
|
|
{
|
|
|
|
|
if (mask)
|
|
|
|
|
for (i = 0; i < 4; ++i)
|
|
|
|
|
{
|
|
|
|
|
if (mask & 0x8)
|
|
|
|
|
set_reg (sr->curr.reg + unw.save_order[i], IA64_WHERE_GR,
|
|
|
|
|
sr->region_start + sr->region_len - 1, grsave++);
|
|
|
|
|
mask <<= 1;
|
|
|
|
|
}
|
|
|
|
|
sr->gr_save_loc = grsave;
|
|
|
|
|
sr->any_spills = 0;
|
|
|
|
|
sr->imask = 0;
|
|
|
|
|
sr->spill_offset = 0x10; /* default to psp+16 */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Prologue descriptors. */
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_abi (unsigned char abi, unsigned char context,
|
|
|
|
|
struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
if (abi == 0 && context == 's')
|
|
|
|
|
sr->is_signal_frame = 1;
|
|
|
|
|
else
|
|
|
|
|
dprintf ("unwind: ignoring unwabi(abi=0x%x,context=0x%x)\n", abi, context);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_br_gr (unsigned char brmask, unsigned char gr,
|
|
|
|
|
struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 5; ++i)
|
|
|
|
|
{
|
|
|
|
|
if (brmask & 1)
|
|
|
|
|
set_reg (sr->curr.reg + IA64_REG_B1 + i, IA64_WHERE_GR,
|
|
|
|
|
sr->region_start + sr->region_len - 1, gr++);
|
|
|
|
|
brmask >>= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_br_mem (unsigned char brmask, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 5; ++i)
|
|
|
|
|
{
|
|
|
|
|
if (brmask & 1)
|
|
|
|
|
{
|
|
|
|
|
set_reg (sr->curr.reg + IA64_REG_B1 + i, IA64_WHERE_SPILL_HOME,
|
|
|
|
|
sr->region_start + sr->region_len - 1, 0);
|
|
|
|
|
sr->any_spills = 1;
|
|
|
|
|
}
|
|
|
|
|
brmask >>= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_frgr_mem (unsigned char grmask, unw_word frmask,
|
|
|
|
|
struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 4; ++i)
|
|
|
|
|
{
|
|
|
|
|
if ((grmask & 1) != 0)
|
|
|
|
|
{
|
|
|
|
|
set_reg (sr->curr.reg + IA64_REG_R4 + i, IA64_WHERE_SPILL_HOME,
|
|
|
|
|
sr->region_start + sr->region_len - 1, 0);
|
|
|
|
|
sr->any_spills = 1;
|
|
|
|
|
}
|
|
|
|
|
grmask >>= 1;
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < 20; ++i)
|
|
|
|
|
{
|
|
|
|
|
if ((frmask & 1) != 0)
|
|
|
|
|
{
|
|
|
|
|
int base = (i < 4) ? IA64_REG_F2 : IA64_REG_F16 - 4;
|
|
|
|
|
set_reg (sr->curr.reg + base + i, IA64_WHERE_SPILL_HOME,
|
|
|
|
|
sr->region_start + sr->region_len - 1, 0);
|
|
|
|
|
sr->any_spills = 1;
|
|
|
|
|
}
|
|
|
|
|
frmask >>= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_fr_mem (unsigned char frmask, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 4; ++i)
|
|
|
|
|
{
|
|
|
|
|
if ((frmask & 1) != 0)
|
|
|
|
|
{
|
|
|
|
|
set_reg (sr->curr.reg + IA64_REG_F2 + i, IA64_WHERE_SPILL_HOME,
|
|
|
|
|
sr->region_start + sr->region_len - 1, 0);
|
|
|
|
|
sr->any_spills = 1;
|
|
|
|
|
}
|
|
|
|
|
frmask >>= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_gr_gr (unsigned char grmask, unsigned char gr,
|
|
|
|
|
struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 4; ++i)
|
|
|
|
|
{
|
|
|
|
|
if ((grmask & 1) != 0)
|
|
|
|
|
set_reg (sr->curr.reg + IA64_REG_R4 + i, IA64_WHERE_GR,
|
|
|
|
|
sr->region_start + sr->region_len - 1, gr++);
|
|
|
|
|
grmask >>= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_gr_mem (unsigned char grmask, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 4; ++i)
|
|
|
|
|
{
|
|
|
|
|
if ((grmask & 1) != 0)
|
|
|
|
|
{
|
|
|
|
|
set_reg (sr->curr.reg + IA64_REG_R4 + i, IA64_WHERE_SPILL_HOME,
|
|
|
|
|
sr->region_start + sr->region_len - 1, 0);
|
|
|
|
|
sr->any_spills = 1;
|
|
|
|
|
}
|
|
|
|
|
grmask >>= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_mem_stack_f (unw_word t, unw_word size, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
set_reg (sr->curr.reg + IA64_REG_PSP, IA64_WHERE_NONE,
|
|
|
|
|
sr->region_start + MIN ((int) t, sr->region_len - 1), 16 * size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_mem_stack_v (unw_word t, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
sr->curr.reg[IA64_REG_PSP].when =
|
|
|
|
|
sr->region_start + MIN ((int) t, sr->region_len - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_reg_gr (unsigned char reg, unsigned char dst,
|
|
|
|
|
struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
set_reg (sr->curr.reg + reg, IA64_WHERE_GR,
|
|
|
|
|
sr->region_start + sr->region_len - 1, dst);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_reg_psprel (unsigned char reg, unw_word pspoff,
|
|
|
|
|
struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
set_reg (sr->curr.reg + reg, IA64_WHERE_PSPREL,
|
|
|
|
|
sr->region_start + sr->region_len - 1, 0x10 - 4 * pspoff);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_reg_sprel (unsigned char reg, unw_word spoff,
|
|
|
|
|
struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
set_reg (sr->curr.reg + reg, IA64_WHERE_SPREL,
|
|
|
|
|
sr->region_start + sr->region_len - 1, 4 * spoff);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_rp_br (unsigned char dst, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
sr->return_link_reg = dst;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_reg_when (unsigned char regnum, unw_word t, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
struct ia64_reg_info *reg = sr->curr.reg + regnum;
|
|
|
|
|
|
|
|
|
|
if (reg->where == IA64_WHERE_NONE)
|
|
|
|
|
reg->where = IA64_WHERE_GR_SAVE;
|
|
|
|
|
reg->when = sr->region_start + MIN ((int) t, sr->region_len - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_spill_base (unw_word pspoff, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
sr->spill_offset = 0x10 - 4 * pspoff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline unsigned char *
|
|
|
|
|
desc_spill_mask (unsigned char *imaskp, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
sr->imask = imaskp;
|
|
|
|
|
return imaskp + (2 * sr->region_len + 7) / 8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Body descriptors. */
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_epilogue (unw_word t, unw_word ecount, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
sr->epilogue_start = sr->region_start + sr->region_len - 1 - t;
|
|
|
|
|
sr->epilogue_count = ecount + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_copy_state (unw_word label, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
struct ia64_labeled_state *ls;
|
|
|
|
|
|
|
|
|
|
for (ls = sr->labeled_states; ls; ls = ls->next)
|
|
|
|
|
{
|
|
|
|
|
if (ls->label == label)
|
|
|
|
|
{
|
|
|
|
|
free_state_stack (&sr->curr);
|
|
|
|
|
memcpy (&sr->curr, &ls->saved_state, sizeof (sr->curr));
|
|
|
|
|
sr->curr.next = dup_state_stack (ls->saved_state.next);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fprintf (stderr, "unwind: failed to find state labeled 0x%lx\n", label);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_label_state (unw_word label, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
struct ia64_labeled_state *ls;
|
|
|
|
|
|
|
|
|
|
ls = alloc_labeled_state ();
|
|
|
|
|
if (!ls)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, "unwind.desc_label_state(): out of memory\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ls->label = label;
|
|
|
|
|
memcpy (&ls->saved_state, &sr->curr, sizeof (ls->saved_state));
|
|
|
|
|
ls->saved_state.next = dup_state_stack (sr->curr.next);
|
|
|
|
|
|
|
|
|
|
/* insert into list of labeled states: */
|
|
|
|
|
ls->next = sr->labeled_states;
|
|
|
|
|
sr->labeled_states = ls;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* General descriptors. */
|
|
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
|
desc_is_active (unsigned char qp, unw_word t, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
if (sr->when_target <= sr->region_start + MIN ((int) t, sr->region_len - 1))
|
|
|
|
|
return 0;
|
|
|
|
|
if (qp > 0)
|
|
|
|
|
{
|
|
|
|
|
if ((sr->pr_val & (1UL << qp)) == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
sr->pr_mask |= (1UL << qp);
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_restore_p (unsigned char qp, unw_word t, unsigned char abreg,
|
|
|
|
|
struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
struct ia64_reg_info *r;
|
|
|
|
|
|
|
|
|
|
if (!desc_is_active (qp, t, sr))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
r = sr->curr.reg + decode_abreg (abreg, 0);
|
|
|
|
|
r->where = IA64_WHERE_NONE;
|
|
|
|
|
r->when = IA64_WHEN_NEVER;
|
|
|
|
|
r->val = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_spill_reg_p (unsigned char qp, unw_word t, unsigned char abreg,
|
|
|
|
|
unsigned char x, unsigned char ytreg,
|
|
|
|
|
struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
enum ia64_where where = IA64_WHERE_GR;
|
|
|
|
|
struct ia64_reg_info *r;
|
|
|
|
|
|
|
|
|
|
if (!desc_is_active (qp, t, sr))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (x)
|
|
|
|
|
where = IA64_WHERE_BR;
|
|
|
|
|
else if (ytreg & 0x80)
|
|
|
|
|
where = IA64_WHERE_FR;
|
|
|
|
|
|
|
|
|
|
r = sr->curr.reg + decode_abreg (abreg, 0);
|
|
|
|
|
r->where = where;
|
|
|
|
|
r->when = sr->region_start + MIN ((int) t, sr->region_len - 1);
|
|
|
|
|
r->val = (ytreg & 0x7f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_spill_psprel_p (unsigned char qp, unw_word t, unsigned char abreg,
|
|
|
|
|
unw_word pspoff, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
struct ia64_reg_info *r;
|
|
|
|
|
|
|
|
|
|
if (!desc_is_active (qp, t, sr))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
r = sr->curr.reg + decode_abreg (abreg, 1);
|
|
|
|
|
r->where = IA64_WHERE_PSPREL;
|
|
|
|
|
r->when = sr->region_start + MIN ((int) t, sr->region_len - 1);
|
|
|
|
|
r->val = 0x10 - 4 * pspoff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
desc_spill_sprel_p (unsigned char qp, unw_word t, unsigned char abreg,
|
|
|
|
|
unw_word spoff, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
struct ia64_reg_info *r;
|
|
|
|
|
|
|
|
|
|
if (!desc_is_active (qp, t, sr))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
r = sr->curr.reg + decode_abreg (abreg, 1);
|
|
|
|
|
r->where = IA64_WHERE_SPREL;
|
|
|
|
|
r->when = sr->region_start + MIN ((int) t, sr->region_len - 1);
|
|
|
|
|
r->val = 4 * spoff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define UNW_DEC_BAD_CODE(code) fprintf (stderr, "unwind: unknown code 0x%02x\n", code);
|
|
|
|
|
|
|
|
|
|
/* Register names. */
|
|
|
|
|
#define UNW_REG_BSP IA64_REG_BSP
|
|
|
|
|
#define UNW_REG_BSPSTORE IA64_REG_BSPSTORE
|
|
|
|
|
#define UNW_REG_FPSR IA64_REG_FPSR
|
|
|
|
|
#define UNW_REG_LC IA64_REG_LC
|
|
|
|
|
#define UNW_REG_PFS IA64_REG_PFS
|
|
|
|
|
#define UNW_REG_PR IA64_REG_PR
|
|
|
|
|
#define UNW_REG_RNAT IA64_REG_RNAT
|
|
|
|
|
#define UNW_REG_PSP IA64_REG_PSP
|
|
|
|
|
#define UNW_REG_RP IA64_REG_RP
|
|
|
|
|
#define UNW_REG_UNAT IA64_REG_UNAT
|
|
|
|
|
|
|
|
|
|
/* Region headers. */
|
|
|
|
|
#define UNW_DEC_PROLOGUE_GR(fmt,r,m,gr,arg) desc_prologue(0,r,m,gr,arg)
|
|
|
|
|
#define UNW_DEC_PROLOGUE(fmt,b,r,arg) desc_prologue(b,r,0,32,arg)
|
|
|
|
|
|
|
|
|
|
/* Prologue descriptors. */
|
|
|
|
|
#define UNW_DEC_ABI(fmt,a,c,arg) desc_abi(a,c,arg)
|
|
|
|
|
#define UNW_DEC_BR_GR(fmt,b,g,arg) desc_br_gr(b,g,arg)
|
|
|
|
|
#define UNW_DEC_BR_MEM(fmt,b,arg) desc_br_mem(b,arg)
|
|
|
|
|
#define UNW_DEC_FRGR_MEM(fmt,g,f,arg) desc_frgr_mem(g,f,arg)
|
|
|
|
|
#define UNW_DEC_FR_MEM(fmt,f,arg) desc_fr_mem(f,arg)
|
|
|
|
|
#define UNW_DEC_GR_GR(fmt,m,g,arg) desc_gr_gr(m,g,arg)
|
|
|
|
|
#define UNW_DEC_GR_MEM(fmt,m,arg) desc_gr_mem(m,arg)
|
|
|
|
|
#define UNW_DEC_MEM_STACK_F(fmt,t,s,arg) desc_mem_stack_f(t,s,arg)
|
|
|
|
|
#define UNW_DEC_MEM_STACK_V(fmt,t,arg) desc_mem_stack_v(t,arg)
|
|
|
|
|
#define UNW_DEC_REG_GR(fmt,r,d,arg) desc_reg_gr(r,d,arg)
|
|
|
|
|
#define UNW_DEC_REG_PSPREL(fmt,r,o,arg) desc_reg_psprel(r,o,arg)
|
|
|
|
|
#define UNW_DEC_REG_SPREL(fmt,r,o,arg) desc_reg_sprel(r,o,arg)
|
|
|
|
|
#define UNW_DEC_REG_WHEN(fmt,r,t,arg) desc_reg_when(r,t,arg)
|
|
|
|
|
#define UNW_DEC_PRIUNAT_WHEN_GR(fmt,t,arg) \
|
|
|
|
|
desc_reg_when(IA64_REG_PRI_UNAT_GR,t,arg)
|
|
|
|
|
#define UNW_DEC_PRIUNAT_WHEN_MEM(fmt,t,arg) \
|
|
|
|
|
desc_reg_when(IA64_REG_PRI_UNAT_MEM,t,arg)
|
|
|
|
|
#define UNW_DEC_PRIUNAT_GR(fmt,r,arg) \
|
|
|
|
|
desc_reg_gr(IA64_REG_PRI_UNAT_GR,r,arg)
|
|
|
|
|
#define UNW_DEC_PRIUNAT_PSPREL(fmt,o,arg) \
|
|
|
|
|
desc_reg_psprel(IA64_REG_PRI_UNAT_MEM,o,arg)
|
|
|
|
|
#define UNW_DEC_PRIUNAT_SPREL(fmt,o,arg) \
|
|
|
|
|
desc_reg_sprel(IA64_REG_PRI_UNAT_MEM,o,arg)
|
|
|
|
|
#define UNW_DEC_RP_BR(fmt,d,arg) desc_rp_br(d,arg)
|
|
|
|
|
#define UNW_DEC_SPILL_BASE(fmt,o,arg) desc_spill_base(o,arg)
|
|
|
|
|
#define UNW_DEC_SPILL_MASK(fmt,m,arg) (m = desc_spill_mask(m,arg))
|
|
|
|
|
|
|
|
|
|
/* Body descriptors. */
|
|
|
|
|
#define UNW_DEC_EPILOGUE(fmt,t,c,arg) desc_epilogue(t,c,arg)
|
|
|
|
|
#define UNW_DEC_COPY_STATE(fmt,l,arg) desc_copy_state(l,arg)
|
|
|
|
|
#define UNW_DEC_LABEL_STATE(fmt,l,arg) desc_label_state(l,arg)
|
|
|
|
|
|
|
|
|
|
/* General unwind descriptors. */
|
|
|
|
|
#define UNW_DEC_SPILL_REG_P(f,p,t,a,x,y,arg) desc_spill_reg_p(p,t,a,x,y,arg)
|
|
|
|
|
#define UNW_DEC_SPILL_REG(f,t,a,x,y,arg) desc_spill_reg_p(0,t,a,x,y,arg)
|
|
|
|
|
#define UNW_DEC_SPILL_PSPREL_P(f,p,t,a,o,arg) \
|
|
|
|
|
desc_spill_psprel_p(p,t,a,o,arg)
|
|
|
|
|
#define UNW_DEC_SPILL_PSPREL(f,t,a,o,arg) \
|
|
|
|
|
desc_spill_psprel_p(0,t,a,o,arg)
|
|
|
|
|
#define UNW_DEC_SPILL_SPREL_P(f,p,t,a,o,arg) desc_spill_sprel_p(p,t,a,o,arg)
|
|
|
|
|
#define UNW_DEC_SPILL_SPREL(f,t,a,o,arg) desc_spill_sprel_p(0,t,a,o,arg)
|
|
|
|
|
#define UNW_DEC_RESTORE_P(f,p,t,a,arg) desc_restore_p(p,t,a,arg)
|
|
|
|
|
#define UNW_DEC_RESTORE(f,t,a,arg) desc_restore_p(0,t,a,arg)
|
|
|
|
|
|
|
|
|
|
#include "unwind_decoder.h"
|
|
|
|
|
|
|
|
|
|
/* parse dynamic unwind info */
|
|
|
|
|
|
|
|
|
|
static struct ia64_reg_info *
|
|
|
|
|
lookup_preg (int regnum, int memory, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
int preg;
|
|
|
|
|
|
|
|
|
|
switch (regnum)
|
|
|
|
|
{
|
|
|
|
|
case UNW_IA64_AR_BSP: preg = IA64_REG_BSP; break;
|
|
|
|
|
case UNW_IA64_AR_BSPSTORE: preg = IA64_REG_BSPSTORE; break;
|
|
|
|
|
case UNW_IA64_AR_FPSR: preg = IA64_REG_FPSR; break;
|
|
|
|
|
case UNW_IA64_AR_LC: preg = IA64_REG_LC; break;
|
|
|
|
|
case UNW_IA64_AR_PFS: preg = IA64_REG_PFS; break;
|
|
|
|
|
case UNW_IA64_AR_RNAT: preg = IA64_REG_RNAT; break;
|
|
|
|
|
case UNW_IA64_AR_UNAT: preg = IA64_REG_UNAT; break;
|
|
|
|
|
case UNW_IA64_BR + 0: preg = IA64_REG_RP; break;
|
|
|
|
|
case UNW_IA64_PR: preg = IA64_REG_PR; break;
|
|
|
|
|
case UNW_IA64_SP: preg = IA64_REG_PSP; break;
|
|
|
|
|
|
|
|
|
|
case UNW_IA64_NAT:
|
|
|
|
|
if (memory)
|
|
|
|
|
preg = IA64_REG_PRI_UNAT_MEM;
|
|
|
|
|
else
|
|
|
|
|
preg = IA64_REG_PRI_UNAT_GR;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case UNW_IA64_GR + 4 ... UNW_IA64_GR + 7:
|
|
|
|
|
preg = IA64_REG_R4 + (regnum - (UNW_IA64_GR + 4));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case UNW_IA64_BR + 1 ... UNW_IA64_BR + 5:
|
|
|
|
|
preg = IA64_REG_B1 + (regnum - UNW_IA64_BR);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case UNW_IA64_FR + 2 ... UNW_IA64_FR + 5:
|
|
|
|
|
preg = IA64_REG_F2 + (regnum - (UNW_IA64_FR + 2));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case UNW_IA64_FR + 16 ... UNW_IA64_FR + 31:
|
|
|
|
|
preg = IA64_REG_F16 + (regnum - (UNW_IA64_FR + 16));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
dprintf ("unwind.%s: invalid register number %d\n",
|
|
|
|
|
__FUNCTION__, regnum);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return sr->curr.reg + preg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
parse_dynamic (struct cursor *c, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
unw_dyn_info_t *di = c->pi.unwind_info;
|
|
|
|
|
unw_dyn_proc_info_t *proc = &di->u.pi;
|
|
|
|
|
unw_dyn_region_info_t *r;
|
|
|
|
|
struct ia64_reg_info *ri;
|
|
|
|
|
unw_word_t val, new_ip;
|
|
|
|
|
enum ia64_where where;
|
|
|
|
|
unw_dyn_op_t *op;
|
|
|
|
|
int32_t when;
|
|
|
|
|
int8_t qp;
|
|
|
|
|
int memory;
|
|
|
|
|
|
|
|
|
|
for (r = proc->regions; r; r = r->next)
|
|
|
|
|
{
|
|
|
|
|
/* all regions are treated as prologue regions: */
|
|
|
|
|
desc_prologue (0, r->insn_count, 0, 0, sr);
|
|
|
|
|
|
|
|
|
|
for (op = r->op; 1; ++op)
|
|
|
|
|
{
|
|
|
|
|
when = op->when;
|
|
|
|
|
val = op->val;
|
|
|
|
|
qp = op->qp;
|
|
|
|
|
|
|
|
|
|
if (!desc_is_active (qp, when, sr))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
switch (op->tag)
|
|
|
|
|
{
|
|
|
|
|
case UNW_DYN_SAVE_REG:
|
|
|
|
|
memory = 0;
|
|
|
|
|
if ((unsigned) (val - UNW_IA64_GR) < 128)
|
|
|
|
|
where = IA64_WHERE_GR;
|
|
|
|
|
else if ((unsigned) (val - UNW_IA64_FR) < 128)
|
|
|
|
|
where = IA64_WHERE_FR;
|
|
|
|
|
else if ((unsigned) (val - UNW_IA64_BR) < 8)
|
|
|
|
|
where = IA64_WHERE_BR;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dprintf ("unwind.%s: can't save to register number %d\n",
|
|
|
|
|
__FUNCTION__, (int) op->reg);
|
|
|
|
|
return -UNW_EBADREG;
|
|
|
|
|
}
|
|
|
|
|
/* fall through */
|
|
|
|
|
update_reg_info:
|
|
|
|
|
ri = lookup_preg (op->reg, memory, sr);
|
|
|
|
|
if (!ri)
|
|
|
|
|
return -UNW_EBADREG;
|
|
|
|
|
ri->where = where;
|
|
|
|
|
ri->when = when;
|
|
|
|
|
ri->val = val;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case UNW_DYN_SPILL_FP_REL:
|
|
|
|
|
memory = 1;
|
|
|
|
|
where = IA64_WHERE_PSPREL;
|
|
|
|
|
goto update_reg_info;
|
|
|
|
|
|
|
|
|
|
case UNW_DYN_SPILL_SP_REL:
|
|
|
|
|
memory = 1;
|
|
|
|
|
where = IA64_WHERE_SPREL;
|
|
|
|
|
goto update_reg_info;
|
|
|
|
|
|
|
|
|
|
case UNW_DYN_ADD:
|
|
|
|
|
if (op->reg == UNW_IA64_SP)
|
|
|
|
|
{
|
|
|
|
|
if (val & 0xf)
|
|
|
|
|
{
|
|
|
|
|
dprintf ("unwind.%s: frame-size %ld not an integer "
|
|
|
|
|
"multiple of 16\n",
|
|
|
|
|
__FUNCTION__, (long) op->val);
|
|
|
|
|
return -UNW_EINVAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dprintf ("unwind.%s: can only ADD to stack-pointer\n",
|
|
|
|
|
__FUNCTION__);
|
|
|
|
|
return -UNW_EBADREG;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case UNW_DYN_POP_FRAMES:
|
|
|
|
|
sr->epilogue_start
|
|
|
|
|
= sr->region_start + sr->region_len - 1 - op->when;
|
|
|
|
|
sr->epilogue_count = op->val;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case UNW_DYN_LABEL_STATE:
|
|
|
|
|
desc_label_state(op->val, sr);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case UNW_DYN_COPY_STATE:
|
|
|
|
|
desc_copy_state(op->val, sr);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case UNW_DYN_ALIAS:
|
|
|
|
|
while (sr->curr.next)
|
|
|
|
|
pop (sr);
|
|
|
|
|
new_ip = op->val + ((sr->when_target / 3) * 16
|
|
|
|
|
+ (sr->when_target % 3));
|
|
|
|
|
return create_state_record_for (c, sr, new_ip);
|
|
|
|
|
|
|
|
|
|
case UNW_DYN_STOP:
|
|
|
|
|
goto end_of_ops;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
while (!sr->done);
|
|
|
|
|
|
|
|
|
|
end_of_ops:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
get_proc_info (struct cursor *c, unw_word_t ip, int need_unwind_info)
|
|
|
|
|
{
|
|
|
|
|
int ret, dynamic = 1;
|
|
|
|
|
|
|
|
|
|
if (c->pi_valid && !need_unwind_info)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* check dynamic info first --- it overrides everything else */
|
|
|
|
|
ret = unwi_find_dynamic_proc_info (c->as, ip, &c->pi, need_unwind_info,
|
|
|
|
|
c->as_arg);
|
|
|
|
|
if (ret == -UNW_ENOINFO)
|
|
|
|
|
{
|
|
|
|
|
dynamic = 0;
|
|
|
|
|
ret = ia64_find_proc_info (c, ip, need_unwind_info);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c->pi_valid = 1;
|
|
|
|
|
c->pi_is_dynamic = dynamic;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
put_unwind_info (struct cursor *c, unw_proc_info_t *pi)
|
|
|
|
|
{
|
|
|
|
|
if (!c->pi_valid)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if ((c)->pi_is_dynamic)
|
|
|
|
|
unwi_put_dynamic_unwind_info (c->as, pi, c->as_arg);
|
|
|
|
|
else
|
|
|
|
|
ia64_put_unwind_info(c, pi);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
create_state_record_for (struct cursor *c, struct ia64_state_record *sr,
|
|
|
|
|
unw_word_t ip)
|
|
|
|
|
{
|
|
|
|
|
unw_word_t predicates = c->pr;
|
|
|
|
|
struct ia64_reg_info *r;
|
|
|
|
|
uint8_t *dp, *desc_end;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
/* build state record */
|
|
|
|
|
memset (sr, 0, sizeof (*sr));
|
|
|
|
|
for (r = sr->curr.reg; r < sr->curr.reg + IA64_NUM_PREGS; ++r)
|
|
|
|
|
r->when = IA64_WHEN_NEVER;
|
|
|
|
|
sr->pr_val = predicates;
|
|
|
|
|
sr->first_region = 1;
|
|
|
|
|
|
|
|
|
|
ret = get_proc_info (c, ip, 1);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
if (!c->pi.unwind_info)
|
|
|
|
|
{
|
|
|
|
|
/* No info, return default unwinder (leaf proc, no mem stack, no
|
|
|
|
|
saved regs), rp in b0, pfs in ar.pfs. */
|
2003-02-08 11:10:59 +01:00
|
|
|
|
dprintf ("unwind.parser: no unwind info for ip=0x%lx (gp=%lx)\n",
|
|
|
|
|
(long) ip, (long) c->pi.gp);
|
2002-12-19 08:16:50 +01:00
|
|
|
|
sr->curr.reg[IA64_REG_RP].where = IA64_WHERE_BR;
|
|
|
|
|
sr->curr.reg[IA64_REG_RP].when = -1;
|
|
|
|
|
sr->curr.reg[IA64_REG_RP].val = 0;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sr->when_target = (3 * ((ip & ~0xfUL) - c->pi.start_ip) / 16 + (ip & 0xfUL));
|
|
|
|
|
|
|
|
|
|
if (c->pi.format == UNW_INFO_FORMAT_TABLE)
|
|
|
|
|
{
|
|
|
|
|
dp = c->pi.unwind_info;
|
|
|
|
|
desc_end = dp + c->pi.unwind_info_size;
|
|
|
|
|
while (!sr->done && dp < desc_end)
|
|
|
|
|
dp = unw_decode (dp, sr->in_body, sr);
|
|
|
|
|
ret = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
ret = parse_dynamic (c, sr);
|
|
|
|
|
|
|
|
|
|
put_unwind_info (c, &c->pi);
|
|
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
if (sr->when_target > sr->epilogue_start)
|
|
|
|
|
{
|
|
|
|
|
/* sp has been restored and all values on the memory stack below
|
|
|
|
|
psp also have been restored. */
|
|
|
|
|
sr->curr.reg[IA64_REG_PSP].val = 0;
|
|
|
|
|
sr->curr.reg[IA64_REG_PSP].where = IA64_WHERE_NONE;
|
|
|
|
|
sr->curr.reg[IA64_REG_PSP].when = IA64_WHEN_NEVER;
|
|
|
|
|
for (r = sr->curr.reg; r < sr->curr.reg + IA64_NUM_PREGS; ++r)
|
|
|
|
|
if ((r->where == IA64_WHERE_PSPREL && r->val <= 0x10)
|
|
|
|
|
|| r->where == IA64_WHERE_SPREL)
|
|
|
|
|
{
|
|
|
|
|
r->val = 0;
|
|
|
|
|
r->where = IA64_WHERE_NONE;
|
|
|
|
|
r->when = IA64_WHEN_NEVER;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If RP did't get saved, generate entry for the return link
|
|
|
|
|
register. */
|
|
|
|
|
if (sr->curr.reg[IA64_REG_RP].when >= sr->when_target)
|
|
|
|
|
{
|
|
|
|
|
sr->curr.reg[IA64_REG_RP].where = IA64_WHERE_BR;
|
|
|
|
|
sr->curr.reg[IA64_REG_RP].when = -1;
|
|
|
|
|
sr->curr.reg[IA64_REG_RP].val = sr->return_link_reg;
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-23 11:04:09 +01:00
|
|
|
|
if (sr->when_target > sr->curr.reg[IA64_REG_BSP].when
|
|
|
|
|
&& sr->when_target > sr->curr.reg[IA64_REG_BSPSTORE].when
|
|
|
|
|
&& sr->when_target > sr->curr.reg[IA64_REG_RNAT].when)
|
|
|
|
|
{
|
2003-01-21 18:41:20 +01:00
|
|
|
|
debug (10, "unwind: func 0x%lx may switch the register-backing-store\n",
|
|
|
|
|
c->pi.start_ip);
|
|
|
|
|
c->pi.flags |= UNW_PI_FLAG_IA64_RBS_SWITCH;
|
|
|
|
|
}
|
2002-12-19 08:16:50 +01:00
|
|
|
|
out:
|
|
|
|
|
#if UNW_DEBUG
|
|
|
|
|
if (unw.debug_level > 0)
|
|
|
|
|
{
|
|
|
|
|
printf ("unwind: state record for func 0x%lx, t=%u (flags=0x%lx):\n",
|
|
|
|
|
(long) c->pi.start_ip, sr->when_target, (long) c->pi.flags);
|
|
|
|
|
for (r = sr->curr.reg; r < sr->curr.reg + IA64_NUM_PREGS; ++r)
|
|
|
|
|
{
|
|
|
|
|
if (r->where != IA64_WHERE_NONE || r->when != IA64_WHEN_NEVER)
|
|
|
|
|
{
|
|
|
|
|
printf (" %s <- ", unw.preg_name[r - sr->curr.reg]);
|
|
|
|
|
switch (r->where)
|
|
|
|
|
{
|
|
|
|
|
case IA64_WHERE_GR:
|
|
|
|
|
printf ("r%lu", (long) r->val);
|
|
|
|
|
break;
|
|
|
|
|
case IA64_WHERE_FR:
|
|
|
|
|
printf ("f%lu", (long) r->val);
|
|
|
|
|
break;
|
|
|
|
|
case IA64_WHERE_BR:
|
|
|
|
|
printf ("b%lu", (long) r->val);
|
|
|
|
|
break;
|
|
|
|
|
case IA64_WHERE_SPREL:
|
|
|
|
|
printf ("[sp+0x%lx]", (long) r->val);
|
|
|
|
|
break;
|
|
|
|
|
case IA64_WHERE_PSPREL:
|
|
|
|
|
printf ("[psp+0x%lx]", (long) r->val);
|
|
|
|
|
break;
|
|
|
|
|
case IA64_WHERE_NONE:
|
|
|
|
|
printf ("%s+0x%lx",
|
|
|
|
|
unw.preg_name[r - sr->curr.reg], (long) r->val);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
printf ("BADWHERE(%d)", r->where);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
printf ("\t\t%d\n", r->when);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HIDDEN int
|
|
|
|
|
ia64_create_state_record (struct cursor *c, struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
return create_state_record_for(c, sr, c->ip);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HIDDEN int
|
|
|
|
|
ia64_free_state_record (struct ia64_state_record *sr)
|
|
|
|
|
{
|
|
|
|
|
struct ia64_labeled_state *ls, *next;
|
|
|
|
|
|
|
|
|
|
/* free labeled register states & stack: */
|
|
|
|
|
|
|
|
|
|
for (ls = sr->labeled_states; ls; ls = next)
|
|
|
|
|
{
|
|
|
|
|
next = ls->next;
|
|
|
|
|
free_state_stack (&ls->saved_state);
|
|
|
|
|
free_labeled_state (ls);
|
|
|
|
|
}
|
|
|
|
|
free_state_stack (&sr->curr);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HIDDEN int
|
|
|
|
|
ia64_make_proc_info (struct cursor *c)
|
|
|
|
|
{
|
|
|
|
|
if (c->as->caching_policy != UNW_CACHE_NONE)
|
|
|
|
|
{
|
|
|
|
|
struct ia64_script *script = ia64_script_lookup (c);
|
|
|
|
|
|
|
|
|
|
if (script)
|
|
|
|
|
{
|
|
|
|
|
c->pi = script->pi;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return get_proc_info (c, c->ip, 0);
|
|
|
|
|
}
|