mirror of
https://github.com/tobast/libunwind-eh_elf.git
synced 2024-11-05 01:09:27 +01:00
746fbe08e7
(Logical change 1.257)
1124 lines
29 KiB
C
1124 lines
29 KiB
C
/* libunwind - a platform-independent unwind library
|
||
Copyright (C) 2001-2004 Hewlett-Packard Co
|
||
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.reg_state_pool))
|
||
#define free_reg_state(rs) (mempool_free (&unw.reg_state_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)
|
||
{
|
||
print_error ("libunwind: 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)
|
||
{
|
||
print_error ("libunwind: 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)
|
||
{
|
||
print_error ("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_IP;
|
||
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 ("libunwind: 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 ("libunwind: 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). */
|
||
for (i = 0; i < (int) NELEMS (unw.save_order); ++i)
|
||
{
|
||
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;
|
||
|
||
for (t = 0; (int) t < sr->region_len; ++t)
|
||
{
|
||
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, region_start;
|
||
|
||
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;
|
||
}
|
||
|
||
region_start = sr->region_start + sr->region_len;
|
||
|
||
for (i = 0; i < sr->epilogue_count; ++i)
|
||
pop (sr);
|
||
sr->epilogue_count = 0;
|
||
sr->when_sp_restored = IA64_WHEN_NEVER;
|
||
|
||
sr->region_start = region_start;
|
||
sr->region_len = rlen;
|
||
sr->in_body = body;
|
||
|
||
if (!body)
|
||
{
|
||
push (sr);
|
||
|
||
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)
|
||
{
|
||
sr->abi_marker = (abi << 8) | 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->when_sp_restored = 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;
|
||
}
|
||
}
|
||
print_error ("libunwind: failed to find labeled state\n");
|
||
}
|
||
|
||
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)
|
||
{
|
||
print_error ("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 & ((unw_word_t) 1 << qp)) == 0)
|
||
return 0;
|
||
sr->pr_mask |= ((unw_word_t) 1 << 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) \
|
||
print_error ("libunwind: unknown code encountered\n")
|
||
|
||
/* 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_IP
|
||
#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_IP; 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 ("%s: invalid register number %d\n", __FUNCTION__, regnum);
|
||
return NULL;
|
||
}
|
||
return sr->curr.reg + preg;
|
||
}
|
||
|
||
/* An alias directive inside a region of length RLEN is interpreted to
|
||
mean that the region behaves exactly like the first RLEN
|
||
instructions at the aliased IP. RLEN=0 implies that the current
|
||
state matches exactly that of the aliased IP. */
|
||
|
||
static int
|
||
desc_alias (unw_dyn_op_t *op, struct cursor *c, struct ia64_state_record *sr)
|
||
{
|
||
struct ia64_state_record orig_sr = *sr;
|
||
int i, ret, when, rlen = sr->region_len;
|
||
unw_word_t new_ip;
|
||
|
||
when = MIN(sr->when_target, rlen - 1);
|
||
new_ip = op->val + ((when / 3) * 16 + (when % 3));
|
||
|
||
if ((ret = ia64_fetch_proc_info (c, new_ip, 1)) < 0)
|
||
return ret;
|
||
|
||
if ((ret = create_state_record_for (c, sr, new_ip)) < 0)
|
||
return ret;
|
||
|
||
sr->first_region = orig_sr.first_region;
|
||
sr->done = 0;
|
||
sr->any_spills |= orig_sr.any_spills;
|
||
sr->in_body = orig_sr.in_body;
|
||
sr->region_start = orig_sr.region_start;
|
||
sr->region_len = orig_sr.region_len;
|
||
if (sr->when_sp_restored != IA64_WHEN_NEVER)
|
||
sr->when_sp_restored = op->when + MIN (orig_sr.when_sp_restored, rlen - 1);
|
||
sr->epilogue_count = orig_sr.epilogue_count;
|
||
sr->when_target = orig_sr.when_target;
|
||
|
||
for (i = 0; i < IA64_NUM_PREGS; ++i)
|
||
if (sr->curr.reg[i].when != IA64_WHEN_NEVER)
|
||
sr->curr.reg[i].when = op->when + MIN (sr->curr.reg[i].when, rlen - 1);
|
||
|
||
ia64_free_state_record (sr);
|
||
sr->labeled_states = orig_sr.labeled_states;
|
||
sr->curr.next = orig_sr.curr.next;
|
||
return 0;
|
||
}
|
||
|
||
static inline 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;
|
||
enum ia64_where where;
|
||
int32_t when, len;
|
||
unw_dyn_op_t *op;
|
||
unw_word_t val;
|
||
int memory, ret;
|
||
int8_t qp;
|
||
|
||
for (r = proc->regions; r; r = r->next)
|
||
{
|
||
len = r->insn_count;
|
||
if (len < 0)
|
||
{
|
||
if (r->next)
|
||
{
|
||
Debug (1, "negative region length allowed in last region only!");
|
||
return -UNW_EINVAL;
|
||
}
|
||
len = -len;
|
||
/* hack old region info to set the start where we need it: */
|
||
sr->region_start = (di->end_ip - di->start_ip) / 0x10 * 3 - len;
|
||
sr->region_len = 0;
|
||
}
|
||
/* all regions are treated as prologue regions: */
|
||
desc_prologue (0, len, 0, 0, sr);
|
||
|
||
if (sr->done)
|
||
return 0;
|
||
|
||
for (op = r->op; op < r->op + r->op_count; ++op)
|
||
{
|
||
when = op->when;
|
||
val = op->val;
|
||
qp = op->qp;
|
||
|
||
if (!desc_is_active (qp, when, sr))
|
||
continue;
|
||
|
||
when = sr->region_start + MIN ((int) when, sr->region_len - 1);
|
||
|
||
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 ("%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;
|
||
val = 0x10 - val;
|
||
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 ("%s: frame-size %ld not an integer "
|
||
"multiple of 16\n",
|
||
__FUNCTION__, (long) op->val);
|
||
return -UNW_EINVAL;
|
||
}
|
||
desc_mem_stack_f (when, -((int64_t) val / 16), sr);
|
||
}
|
||
else
|
||
{
|
||
dprintf ("%s: can only ADD to stack-pointer\n",
|
||
__FUNCTION__);
|
||
return -UNW_EBADREG;
|
||
}
|
||
break;
|
||
|
||
case UNW_DYN_POP_FRAMES:
|
||
sr->when_sp_restored = 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:
|
||
if ((ret = desc_alias (op, c, sr)) < 0)
|
||
return ret;
|
||
|
||
case UNW_DYN_STOP:
|
||
goto end_of_ops;
|
||
}
|
||
}
|
||
end_of_ops:
|
||
;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
HIDDEN int
|
||
ia64_fetch_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;
|
||
|
||
assert (c->pi_valid);
|
||
|
||
/* 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;
|
||
|
||
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. */
|
||
Debug (1, "no unwind info for ip=0x%lx (gp=%lx)\n",
|
||
(long) ip, (long) c->pi.gp);
|
||
sr->curr.reg[IA64_REG_IP].where = IA64_WHERE_BR;
|
||
sr->curr.reg[IA64_REG_IP].when = -1;
|
||
sr->curr.reg[IA64_REG_IP].val = 0;
|
||
goto out;
|
||
}
|
||
|
||
sr->when_target = (3 * ((ip & ~(unw_word_t) 0xf) - c->pi.start_ip) / 16
|
||
+ (ip & 0xf));
|
||
|
||
switch (c->pi.format)
|
||
{
|
||
case UNW_INFO_FORMAT_TABLE:
|
||
case UNW_INFO_FORMAT_REMOTE_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;
|
||
break;
|
||
|
||
case UNW_INFO_FORMAT_DYNAMIC:
|
||
ret = parse_dynamic (c, sr);
|
||
break;
|
||
|
||
default:
|
||
ret = -UNW_EINVAL;
|
||
}
|
||
|
||
put_unwind_info (c, &c->pi);
|
||
|
||
if (ret < 0)
|
||
return ret;
|
||
|
||
if (sr->when_target > sr->when_sp_restored)
|
||
{
|
||
/* 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_IP].when >= sr->when_target)
|
||
{
|
||
sr->curr.reg[IA64_REG_IP].where = IA64_WHERE_BR;
|
||
sr->curr.reg[IA64_REG_IP].when = -1;
|
||
sr->curr.reg[IA64_REG_IP].val = sr->return_link_reg;
|
||
}
|
||
|
||
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)
|
||
{
|
||
Debug (8, "func 0x%lx may switch the register-backing-store\n",
|
||
c->pi.start_ip);
|
||
c->pi.flags |= UNW_PI_FLAG_IA64_RBS_SWITCH;
|
||
}
|
||
out:
|
||
#if UNW_DEBUG
|
||
if (unwi_debug_level > 2)
|
||
{
|
||
dprintf ("%s: state record for func 0x%lx, t=%u (flags=0x%lx):\n",
|
||
__FUNCTION__,
|
||
(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)
|
||
{
|
||
dprintf (" %s <- ", unw.preg_name[r - sr->curr.reg]);
|
||
switch (r->where)
|
||
{
|
||
case IA64_WHERE_GR:
|
||
dprintf ("r%lu", (long) r->val);
|
||
break;
|
||
case IA64_WHERE_FR:
|
||
dprintf ("f%lu", (long) r->val);
|
||
break;
|
||
case IA64_WHERE_BR:
|
||
dprintf ("b%lu", (long) r->val);
|
||
break;
|
||
case IA64_WHERE_SPREL:
|
||
dprintf ("[sp+0x%lx]", (long) r->val);
|
||
break;
|
||
case IA64_WHERE_PSPREL:
|
||
dprintf ("[psp+0x%lx]", (long) r->val);
|
||
break;
|
||
case IA64_WHERE_NONE:
|
||
dprintf ("%s+0x%lx",
|
||
unw.preg_name[r - sr->curr.reg], (long) r->val);
|
||
break;
|
||
default:
|
||
dprintf ("BADWHERE(%d)", r->where);
|
||
break;
|
||
}
|
||
dprintf ("\t\t%d\n", r->when);
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
return 0;
|
||
}
|
||
|
||
/* The proc-info must be valid for IP before this routine can be
|
||
called. */
|
||
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
|
||
|| ia64_get_cached_proc_info (c) < 0)
|
||
/* Lookup it up the slow way... */
|
||
return ia64_fetch_proc_info (c, c->ip, 0);
|
||
return 0;
|
||
}
|