#ifndef unwind_i_h
#define unwind_i_h
#include <string.h>
#include <inttypes.h>
#include <libunwind-ia64.h>
#include "rse.h"
#include "libunwind_i.h"
#define IA64_UNW_VER(x) ((x) >> 48)
#define IA64_UNW_FLAG_MASK ((unw_word_t) 0x0000ffff00000000ULL)
#define IA64_UNW_FLAG_OSMASK ((unw_word_t) 0x0000f00000000000ULL)
#define IA64_UNW_FLAG_EHANDLER(x) ((x) & (unw_word_t) 0x0000000100000000ULL)
#define IA64_UNW_FLAG_UHANDLER(x) ((x) & (unw_word_t) 0x0000000200000000ULL)
#define IA64_UNW_LENGTH(x) ((x) & (unw_word_t) 0x00000000ffffffffULL)
#ifdef MIN
# undef MIN
#endif
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#if !defined(HAVE_SYS_UC_ACCESS_H) && !defined(UNW_REMOTE_ONLY)
static ALWAYS_INLINE void *
inlined_uc_addr (ucontext_t *uc, int reg, uint8_t *nat_bitnr)
{
unw_word_t reg_addr;
void *addr;
switch (reg)
{
case UNW_IA64_GR + 0: addr = &unw.read_only.r0; break;
case UNW_IA64_NAT + 0: addr = &unw.read_only.r0; break;
case UNW_IA64_FR + 0: addr = &unw.read_only.f0; break;
case UNW_IA64_FR + 1:
if (target_is_big_endian())
addr = &unw.read_only.f1_be;
else
addr = &unw.read_only.f1_le;
break;
case UNW_IA64_IP: addr = &uc->uc_mcontext.sc_br[0]; break;
case UNW_IA64_CFM: addr = &uc->uc_mcontext.sc_ar_pfs; break;
case UNW_IA64_AR_RNAT: addr = &uc->uc_mcontext.sc_ar_rnat; break;
case UNW_IA64_AR_UNAT: addr = &uc->uc_mcontext.sc_ar_unat; break;
case UNW_IA64_AR_LC: addr = &uc->uc_mcontext.sc_ar_lc; break;
case UNW_IA64_AR_FPSR: addr = &uc->uc_mcontext.sc_ar_fpsr; break;
case UNW_IA64_PR: addr = &uc->uc_mcontext.sc_pr; break;
case UNW_IA64_AR_BSPSTORE: addr = &uc->uc_mcontext.sc_ar_bsp; break;
case UNW_IA64_GR + 4 ... UNW_IA64_GR + 7:
case UNW_IA64_GR + 12:
addr = &uc->uc_mcontext.sc_gr[reg - UNW_IA64_GR];
break;
case UNW_IA64_NAT + 4 ... UNW_IA64_NAT + 7:
case UNW_IA64_NAT + 12:
addr = &uc->uc_mcontext.sc_nat;
reg_addr = (unw_word_t) &uc->uc_mcontext.sc_gr[reg - UNW_IA64_NAT];
*nat_bitnr = reg - UNW_IA64_NAT;
break;
case UNW_IA64_BR + 1 ... UNW_IA64_BR + 5:
addr = &uc->uc_mcontext.sc_br[reg - UNW_IA64_BR];
break;
case UNW_IA64_FR+ 2 ... UNW_IA64_FR+ 5:
case UNW_IA64_FR+16 ... UNW_IA64_FR+31:
addr = &uc->uc_mcontext.sc_fr[reg - UNW_IA64_FR];
break;
default:
addr = NULL;
}
return addr;
}
static inline void *
uc_addr (ucontext_t *uc, int reg, uint8_t *nat_bitnr)
{
if (__builtin_constant_p (reg))
return inlined_uc_addr (uc, reg, nat_bitnr);
else
return tdep_uc_addr (uc, reg, nat_bitnr);
}
static inline long
ia64_read_only_reg (void *addr)
{
return ((unsigned long) ((char *) addr - (char *) &unw.read_only)
< sizeof (unw.read_only));
}
#endif
#define IA64_LOC_TYPE_FP (1 << 0)
#define IA64_LOC_TYPE_MEMSTK_NAT (1 << 1)
#ifdef UNW_LOCAL_ONLY
#define IA64_LOC_REG(r,t) (((r) << 2) | (t))
#define IA64_LOC_ADDR(a,t) (((a) & ~0x3) | (t))
#define IA64_LOC_UC_ADDR(a,t) IA64_LOC_ADDR(a, t)
#define IA64_NULL_LOC (0)
#define IA64_GET_REG(l) ((l) >> 2)
#define IA64_GET_ADDR(l) ((l) & ~0x3)
#define IA64_IS_NULL_LOC(l) ((l) == 0)
#define IA64_IS_FP_LOC(l) (((l) & IA64_LOC_TYPE_FP) != 0)
#define IA64_IS_MEMSTK_NAT(l) (((l) & IA64_LOC_TYPE_MEMSTK_NAT) != 0)
#define IA64_IS_REG_LOC(l) 0
#define IA64_IS_UC_LOC(l) 0
#define IA64_REG_LOC(c,r) ((unw_word_t) uc_addr((c)->as_arg, r, NULL))
#define IA64_REG_NAT_LOC(c,r,n) ((unw_word_t) uc_addr((c)->as_arg, r, n))
#define IA64_FPREG_LOC(c,r) \
((unw_word_t) uc_addr((c)->as_arg, (r), NULL) | IA64_LOC_TYPE_FP)
# define ia64_find_proc_info(c,ip,n) \
tdep_find_proc_info(unw_local_addr_space, (ip), &(c)->pi, (n), \
(c)->as_arg)
# define ia64_put_unwind_info(c, pi) do { ; } while (0)
static inline int
ia64_getfp (struct cursor *c, unw_word_t loc, unw_fpreg_t *val)
{
if (IA64_IS_NULL_LOC (loc))
{
Debug (16, "access to unsaved register\n");
return -UNW_EBADREG;
}
*val = *(unw_fpreg_t *) IA64_GET_ADDR (loc);
return 0;
}
static inline int
ia64_putfp (struct cursor *c, unw_word_t loc, unw_fpreg_t val)
{
unw_fpreg_t *addr = (unw_fpreg_t *) IA64_GET_ADDR (loc);
if (IA64_IS_NULL_LOC (loc))
{
Debug (16, "access to unsaved register\n");
return -UNW_EBADREG;
}
else if (ia64_read_only_reg (addr))
{
Debug (16, "attempt to read-only register\n");
return -UNW_EREADONLYREG;
}
*addr = val;
return 0;
}
static inline int
ia64_get (struct cursor *c, unw_word_t loc, unw_word_t *val)
{
if (IA64_IS_NULL_LOC (loc))
{
Debug (16, "access to unsaved register\n");
return -UNW_EBADREG;
}
*val = *(unw_word_t *) IA64_GET_ADDR (loc);
return 0;
}
static inline int
ia64_put (struct cursor *c, unw_word_t loc, unw_word_t val)
{
unw_word_t *addr = (unw_word_t *) IA64_GET_ADDR (loc);
if (IA64_IS_NULL_LOC (loc))
{
Debug (16, "access to unsaved register\n");
return -UNW_EBADREG;
}
else if (ia64_read_only_reg (addr))
{
Debug (16, "attempt to read-only register\n");
return -UNW_EREADONLYREG;
}
*addr = val;
return 0;
}
#else
#define IA64_LOC_TYPE_REG (1 << 0)
#define IA64_LOC_TYPE_UC (1 << 1)
#define IA64_LOC_REG(r,t) ((ia64_loc_t) { ((r) << 2) | (t), \
IA64_LOC_TYPE_REG })
#define IA64_LOC_ADDR(a,t) ((ia64_loc_t) { ((a) & ~0x3) | (t), 0 })
#define IA64_LOC_UC_ADDR(a,t) ((ia64_loc_t) { ((a) & ~0x3) | (t), \
IA64_LOC_TYPE_UC })
#define IA64_LOC_UC_REG(r,a) ((ia64_loc_t) { ((r) << 2), \
((a) | IA64_LOC_TYPE_REG \
| IA64_LOC_TYPE_UC) })
#define IA64_NULL_LOC ((ia64_loc_t) { 0, 0 })
#define IA64_GET_REG(l) ((l).w0 >> 2)
#define IA64_GET_ADDR(l) ((l).w0 & ~0x3)
#define IA64_GET_AUX_ADDR(l) ((l).w1 & ~0x3)
#define IA64_IS_NULL_LOC(l) (((l).w0 | (l).w1) == 0)
#define IA64_IS_FP_LOC(l) (((l).w0 & IA64_LOC_TYPE_FP) != 0)
#define IA64_IS_MEMSTK_NAT(l) (((l).w0 & IA64_LOC_TYPE_MEMSTK_NAT) != 0)
#define IA64_IS_REG_LOC(l) (((l).w1 & IA64_LOC_TYPE_REG) != 0)
#define IA64_IS_UC_LOC(l) (((l).w1 & IA64_LOC_TYPE_UC) != 0)
#define IA64_REG_LOC(c,r) IA64_LOC_REG ((r), 0)
#define IA64_REG_NAT_LOC(c,r,n) IA64_LOC_REG ((r), 0)
#define IA64_FPREG_LOC(c,r) IA64_LOC_REG ((r), IA64_LOC_TYPE_FP)
# define ia64_find_proc_info(c,ip,n) \
(*(c)->as->acc.find_proc_info)((c)->as, (ip), &(c)->pi, (n), \
(c)->as_arg)
# define ia64_put_unwind_info(c,pi) \
(*(c)->as->acc.put_unwind_info)((c)->as, (pi), (c)->as_arg)
#define ia64_uc_access_reg UNW_OBJ(uc_access_reg)
#define ia64_uc_access_fpreg UNW_OBJ(uc_access_fpreg)
extern int ia64_uc_access_reg (struct cursor *c, ia64_loc_t loc,
unw_word_t *valp, int write);
extern int ia64_uc_access_fpreg (struct cursor *c, ia64_loc_t loc,
unw_fpreg_t *valp, int write);
static inline int
ia64_getfp (struct cursor *c, ia64_loc_t loc, unw_fpreg_t *val)
{
unw_word_t addr;
int ret;
if (IA64_IS_NULL_LOC (loc))
{
Debug (16, "access to unsaved register\n");
return -UNW_EBADREG;
}
if (IA64_IS_UC_LOC (loc))
return ia64_uc_access_fpreg (c, loc, val, 0);
if (IA64_IS_REG_LOC (loc))
return (*c->as->acc.access_fpreg) (c->as, IA64_GET_REG (loc),
val, 0, c->as_arg);
addr = IA64_GET_ADDR (loc);
ret = (*c->as->acc.access_mem) (c->as, addr + 0, &val->raw.bits[0], 0,
c->as_arg);
if (ret < 0)
return ret;
return (*c->as->acc.access_mem) (c->as, addr + 8, &val->raw.bits[1], 0,
c->as_arg);
}
static inline int
ia64_putfp (struct cursor *c, ia64_loc_t loc, unw_fpreg_t val)
{
unw_word_t addr;
int ret;
if (IA64_IS_NULL_LOC (loc))
{
Debug (16, "access to unsaved register\n");
return -UNW_EBADREG;
}
if (IA64_IS_UC_LOC (loc))
return ia64_uc_access_fpreg (c, loc, &val, 1);
if (IA64_IS_REG_LOC (loc))
return (*c->as->acc.access_fpreg) (c->as, IA64_GET_REG (loc), &val, 1,
c->as_arg);
addr = IA64_GET_ADDR (loc);
ret = (*c->as->acc.access_mem) (c->as, addr + 0, &val.raw.bits[0], 1,
c->as_arg);
if (ret < 0)
return ret;
return (*c->as->acc.access_mem) (c->as, addr + 8, &val.raw.bits[1], 1,
c->as_arg);
}
static inline int
ia64_get (struct cursor *c, ia64_loc_t loc, unw_word_t *val)
{
if (IA64_IS_NULL_LOC (loc))
{
Debug (16, "access to unsaved register\n");
return -UNW_EBADREG;
}
if (IA64_IS_FP_LOC (loc))
{
unw_fpreg_t tmp;
int ret;
ret = ia64_getfp (c, loc, &tmp);
if (ret < 0)
return ret;
if (c->as->big_endian)
*val = tmp.raw.bits[1];
else
*val = tmp.raw.bits[0];
return 0;
}
if (IA64_IS_UC_LOC (loc))
return ia64_uc_access_reg (c, loc, val, 0);
if (IA64_IS_REG_LOC (loc))
return (*c->as->acc.access_reg)(c->as, IA64_GET_REG (loc), val, 0,
c->as_arg);
else
return (*c->as->acc.access_mem)(c->as, IA64_GET_ADDR (loc), val, 0,
c->as_arg);
}
static inline int
ia64_put (struct cursor *c, ia64_loc_t loc, unw_word_t val)
{
if (IA64_IS_NULL_LOC (loc))
{
Debug (16, "access to unsaved register\n");
return -UNW_EBADREG;
}
if (IA64_IS_FP_LOC (loc))
{
unw_fpreg_t tmp;
memset (&tmp, 0, sizeof (tmp));
if (c->as->big_endian)
tmp.raw.bits[1] = val;
else
tmp.raw.bits[0] = val;
return ia64_putfp (c, loc, tmp);
}
if (IA64_IS_UC_LOC (loc))
return ia64_uc_access_reg (c, loc, &val, 1);
if (IA64_IS_REG_LOC (loc))
return (*c->as->acc.access_reg)(c->as, IA64_GET_REG (loc), &val, 1,
c->as_arg);
else
return (*c->as->acc.access_mem)(c->as, IA64_GET_ADDR (loc), &val, 1,
c->as_arg);
}
#endif
struct ia64_unwind_block
{
unw_word_t header;
unw_word_t desc[0];
};
enum ia64_where
{
IA64_WHERE_NONE,
IA64_WHERE_GR,
IA64_WHERE_FR,
IA64_WHERE_BR,
IA64_WHERE_SPREL,
IA64_WHERE_PSPREL,
IA64_WHERE_SPILL_HOME,
IA64_WHERE_GR_SAVE
};
#define IA64_WHEN_NEVER 0x7fffffff
struct ia64_reg_info
{
unw_word_t val;
enum ia64_where where;
int when;
};
struct ia64_labeled_state;
struct ia64_reg_state
{
struct ia64_reg_state *next;
struct ia64_reg_info reg[IA64_NUM_PREGS];
};
struct ia64_state_record
{
unsigned int first_region : 1;
unsigned int done : 1;
unsigned int any_spills : 1;
unsigned int in_body : 1;
uint8_t *imask;
uint16_t abi_marker;
unw_word_t pr_val;
unw_word_t pr_mask;
long spill_offset;
int region_start;
int region_len;
int when_sp_restored;
int epilogue_count;
int when_target;
uint8_t gr_save_loc;
uint8_t return_link_reg;
struct ia64_labeled_state *labeled_states;
struct ia64_reg_state curr;
};
struct ia64_labeled_state
{
struct ia64_labeled_state *next;
unsigned long label;
struct ia64_reg_state saved_state;
};
#define ia64_make_proc_info UNW_OBJ(make_proc_info)
#define ia64_fetch_proc_info UNW_OBJ(fetch_proc_info)
#define ia64_create_state_record UNW_OBJ(create_state_record)
#define ia64_free_state_record UNW_OBJ(free_state_record)
#define ia64_find_save_locs UNW_OBJ(find_save_locs)
#define ia64_validate_cache UNW_OBJ(ia64_validate_cache)
#define ia64_local_validate_cache UNW_OBJ(ia64_local_validate_cache)
#define ia64_per_thread_cache UNW_OBJ(per_thread_cache)
#define ia64_scratch_loc UNW_OBJ(scratch_loc)
#define ia64_local_resume UNW_OBJ(local_resume)
#define ia64_local_addr_space_init UNW_OBJ(local_addr_space_init)
#define ia64_strloc UNW_OBJ(strloc)
#define ia64_install_cursor UNW_OBJ(install_cursor)
#define rbs_switch UNW_OBJ(rbs_switch)
#define rbs_find_stacked UNW_OBJ(rbs_find_stacked)
extern int ia64_make_proc_info (struct cursor *c);
extern int ia64_fetch_proc_info (struct cursor *c, unw_word_t ip,
int need_unwind_info);
extern int ia64_create_state_record (struct cursor *c,
struct ia64_state_record *sr);
extern int ia64_free_state_record (struct ia64_state_record *sr);
extern int ia64_find_save_locs (struct cursor *c);
extern void ia64_validate_cache (unw_addr_space_t as, void *arg);
extern int ia64_local_validate_cache (unw_addr_space_t as, void *arg);
extern void ia64_local_addr_space_init (void);
extern ia64_loc_t ia64_scratch_loc (struct cursor *c, unw_regnum_t reg,
uint8_t *nat_bitnr);
extern NORETURN void ia64_install_cursor (struct cursor *c,
unw_word_t pri_unat,
unw_word_t *extra,
unw_word_t bspstore,
unw_word_t dirty_size,
unw_word_t *dirty_partition,
unw_word_t dirty_rnat);
extern int ia64_local_resume (unw_addr_space_t as, unw_cursor_t *cursor,
void *arg);
extern int rbs_switch (struct cursor *c,
unw_word_t saved_bsp, unw_word_t saved_bspstore,
ia64_loc_t saved_rnat_loc);
extern int rbs_find_stacked (struct cursor *c, unw_word_t regs_to_skip,
ia64_loc_t *locp, ia64_loc_t *rnat_locp);
#ifndef UNW_REMOTE_ONLY
# define NEED_RBS_COVER_AND_FLUSH
# define rbs_cover_and_flush UNW_OBJ(rbs_cover_and_flush)
extern int rbs_cover_and_flush (struct cursor *c, unw_word_t nregs,
unw_word_t *dirty_partition,
unw_word_t *dirty_rnat,
unw_word_t *bspstore);
#endif
extern const char *ia64_strloc (ia64_loc_t loc);
static inline int
rbs_on_uc (struct rbs_area *rbs)
{
return IA64_IS_UC_LOC (rbs->rnat_loc) && !IA64_IS_REG_LOC (rbs->rnat_loc);
}
static inline int
rbs_contains (struct rbs_area *rbs, unw_word_t bsp)
{
int result;
result = bsp - rbs->end > ~rbs->size;
Debug (16, "0x%lx in [0x%lx-0x%lx) => %d\n",
(long) bsp, (long) (rbs->end - rbs->size), (long) rbs->end, result);
return result;
}
static inline ia64_loc_t
rbs_get_rnat_loc (struct rbs_area *rbs, unw_word_t bsp)
{
unw_word_t rnat_addr = rse_rnat_addr (bsp);
ia64_loc_t rnat_loc;
if (rbs_contains (rbs, rnat_addr))
{
if (rbs_on_uc (rbs))
rnat_loc = IA64_LOC_UC_ADDR (rnat_addr, 0);
else
rnat_loc = IA64_LOC_ADDR (rnat_addr, 0);
}
else
rnat_loc = rbs->rnat_loc;
return rnat_loc;
}
static inline ia64_loc_t
rbs_loc (struct rbs_area *rbs, unw_word_t bsp)
{
if (rbs_on_uc (rbs))
return IA64_LOC_UC_ADDR (bsp, 0);
else
return IA64_LOC_ADDR (bsp, 0);
}
static inline int
ia64_get_stacked (struct cursor *c, unw_word_t reg,
ia64_loc_t *locp, ia64_loc_t *rnat_locp)
{
struct rbs_area *rbs = c->rbs_area + c->rbs_curr;
unw_word_t addr, regs_to_skip = reg - 32;
int ret = 0;
assert (reg >= 32 && reg < 128);
addr = rse_skip_regs (c->bsp, regs_to_skip);
if (locp)
*locp = rbs_loc (rbs, addr);
if (rnat_locp)
*rnat_locp = rbs_get_rnat_loc (rbs, addr);
if (!rbs_contains (rbs, addr))
ret = rbs_find_stacked (c, regs_to_skip, locp, rnat_locp);
return ret;
}
#define ia64_unat_slot_num(addr) rse_slot_num(addr)
#define ia64_is_big_endian(c) ((c)->as->big_endian)
#define ia64_get_abi(c) ((c)->as->abi)
#define ia64_set_abi(c, v) ((c)->as->abi = (v))
#define ia64_get_abi_marker(c) ((c)->last_abi_marker)
#ifndef IA64_SC_FLAG_ONSTACK
# define IA64_SC_FLAG_ONSTACK_BIT 0
# define IA64_SC_FLAG_IN_SYSCALL_BIT 1
# define IA64_SC_FLAG_FPH_VALID_BIT 2
# define IA64_SC_FLAG_ONSTACK (1 << IA64_SC_FLAG_ONSTACK_BIT)
# define IA64_SC_FLAG_IN_SYSCALL (1 << IA64_SC_FLAG_IN_SYSCALL_BIT)
# define IA64_SC_FLAG_FPH_VALID (1 << IA64_SC_FLAG_FPH_VALID_BIT)
#endif
#endif