unwind_i.h revision 8fee91bd1365ad85e524144645ca2a59a45e91d4
1/* libunwind - a platform-independent unwind library
2   Copyright (C) 2001-2003 Hewlett-Packard Co
3	Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
4
5This file is part of libunwind.
6
7Permission is hereby granted, free of charge, to any person obtaining
8a copy of this software and associated documentation files (the
9"Software"), to deal in the Software without restriction, including
10without limitation the rights to use, copy, modify, merge, publish,
11distribute, sublicense, and/or sell copies of the Software, and to
12permit persons to whom the Software is furnished to do so, subject to
13the following conditions:
14
15The above copyright notice and this permission notice shall be
16included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
25
26#ifndef unwind_i_h
27#define unwind_i_h
28
29#include <memory.h>
30#include <inttypes.h>
31
32#include <libunwind-ia64.h>
33
34#include "config.h"
35#include "internal.h"
36#include "rse.h"
37
38#define IA64_UNW_VER(x)			((x) >> 48)
39#define IA64_UNW_FLAG_MASK		0x0000ffff00000000
40#define IA64_UNW_FLAG_OSMASK		0x0000f00000000000
41#define IA64_UNW_FLAG_EHANDLER(x)	((x) & 0x0000000100000000L)
42#define IA64_UNW_FLAG_UHANDLER(x)	((x) & 0x0000000200000000L)
43#define IA64_UNW_LENGTH(x)		((x) & 0x00000000ffffffffL)
44
45#ifdef MIN
46# undef MIN
47#endif
48#define MIN(a,b)	((a) < (b) ? (a) : (b))
49
50#include "tdep.h"
51
52/* Bits 0 and 1 of an location are used to encode its type:
53	bit 0: set if location uses floating-point format.
54	bit 1: set if location is a NaT bit on memory stack.  */
55
56#define IA64_LOC_TYPE_FP		(1 << 0)
57#define IA64_LOC_TYPE_MEMSTK_NAT	(1 << 1)
58
59#ifdef UNW_LOCAL_ONLY
60
61#define IA64_LOC_REG(r,t)	(((r) << 2) | (t))
62#define IA64_LOC_ADDR(a,t)	(((a) & ~0x3) | (t))
63#define IA64_LOC_UC_ADDR(a,t)	IA64_LOC_ADDR(a, t)
64#define IA64_LOC_UC_REG(a,t)
65#define IA64_NULL_LOC		(0)
66
67#define IA64_GET_REG(l)		((l) >> 2)
68#define IA64_GET_ADDR(l)	((l) & ~0x3)
69#define IA64_IS_NULL_LOC(l)	((l) == 0)
70#define IA64_IS_FP_LOC(l)	(((l) & IA64_LOC_TYPE_FP) != 0)
71#define IA64_IS_MEMSTK_NAT(l)	(((l) & IA64_LOC_TYPE_MEMSTK_NAT) != 0)
72#define IA64_IS_REG_LOC(l)	0
73#define IA64_IS_UC_LOC(l)	0
74
75#define IA64_REG_LOC(c,r)	((unw_word_t) tdep_uc_addr((c)->as_arg, (r)))
76#define IA64_FPREG_LOC(c,r)						 \
77	((unw_word_t) tdep_uc_addr((c)->as_arg, (r)) | IA64_LOC_TYPE_FP)
78
79# define ia64_find_proc_info(c,ip,n)					\
80	tdep_find_proc_info(unw_local_addr_space, (ip), &(c)->pi, (n),	\
81			    (c)->as_arg)
82# define ia64_put_unwind_info(c, pi)	do { ; } while (0)
83
84/* Note: the register accessors (ia64_{get,set}{,fp}()) must check for
85   NULL locations because tdep_uc_addr() returns NULL for unsaved
86   registers.  */
87
88static inline int
89ia64_getfp (struct cursor *c, unw_word_t loc, unw_fpreg_t *val)
90{
91  if (!loc)
92    {
93      debug (150, "%s: access to unsaved register\n", __FUNCTION__);
94      return -UNW_EBADREG;
95    }
96  *val = *(unw_fpreg_t *) IA64_GET_ADDR (loc);
97  return 0;
98}
99
100static inline int
101ia64_putfp (struct cursor *c, unw_word_t loc, unw_fpreg_t val)
102{
103  if (!loc)
104    {
105      debug (150, "%s: access to unsaved register\n", __FUNCTION__);
106      return -UNW_EBADREG;
107    }
108  *(unw_fpreg_t *) IA64_GET_ADDR (loc) = val;
109  return 0;
110}
111
112static inline int
113ia64_get (struct cursor *c, unw_word_t loc, unw_word_t *val)
114{
115  if (!loc)
116    {
117      debug (150, "%s: access to unsaved register\n", __FUNCTION__);
118      return -UNW_EBADREG;
119    }
120  *val = *(unw_word_t *) IA64_GET_ADDR (loc);
121  return 0;
122}
123
124static inline int
125ia64_put (struct cursor *c, unw_word_t loc, unw_word_t val)
126{
127  if (!loc)
128    {
129      debug (150, "%s: access to unsaved register\n", __FUNCTION__);
130      return -UNW_EBADREG;
131    }
132  *(unw_word_t *) IA64_GET_ADDR (loc) = (val);
133  return 0;
134}
135
136#else /* !UNW_LOCAL_ONLY */
137
138/* Bits 0 and 1 of the second word (w1) of a location are used
139   to further distinguish what location we're dealing with:
140
141   	bit 0: set if the location is a register
142	bit 1: set of the location is accessed via uc_access(3)  */
143#define IA64_LOC_TYPE_REG	(1 << 0)
144#define IA64_LOC_TYPE_UC	(1 << 1)
145
146#define IA64_LOC_REG(r,t)	((ia64_loc_t) { ((r) << 2) | (t),	\
147						IA64_LOC_TYPE_REG })
148#define IA64_LOC_ADDR(a,t)	((ia64_loc_t) { ((a) & ~0x3) | (t), 0 })
149#define IA64_LOC_UC_ADDR(a,t)	((ia64_loc_t) { ((a) & ~0x3) | (t),	\
150						IA64_LOC_TYPE_UC })
151#define IA64_LOC_UC_REG(r,a)	((ia64_loc_t) { ((r) << 2),		 \
152						((a) | IA64_LOC_TYPE_REG \
153					         | IA64_LOC_TYPE_UC) })
154#define IA64_NULL_LOC		((ia64_loc_t) { 0, 0 })
155
156#define IA64_GET_REG(l)		((l).w0 >> 2)
157#define IA64_GET_ADDR(l)	((l).w0 & ~0x3)
158#define IA64_GET_AUX_ADDR(l)	((l).w1 & ~0x3)
159#define IA64_IS_NULL_LOC(l)	(((l).w0 | (l).w1) == 0)
160#define IA64_IS_FP_LOC(l)	(((l).w0 & IA64_LOC_TYPE_FP) != 0)
161#define IA64_IS_MEMSTK_NAT(l)	(((l).w0 & IA64_LOC_TYPE_MEMSTK_NAT) != 0)
162#define IA64_IS_REG_LOC(l)	(((l).w1 & IA64_LOC_TYPE_REG) != 0)
163#define IA64_IS_UC_LOC(l)	(((l).w1 & IA64_LOC_TYPE_UC) != 0)
164
165#define IA64_REG_LOC(c,r)	IA64_LOC_REG ((r), 0)
166#define IA64_FPREG_LOC(c,r)	IA64_LOC_REG ((r), IA64_LOC_TYPE_FP)
167
168# define ia64_find_proc_info(c,ip,n)					\
169	(*(c)->as->acc.find_proc_info)((c)->as, (ip), &(c)->pi, (n),	\
170				       (c)->as_arg)
171# define ia64_put_unwind_info(c,pi)					\
172	(*(c)->as->acc.put_unwind_info)((c)->as, (pi), (c)->as_arg)
173
174#define ia64_uc_access_reg	UNW_OBJ(uc_access_reg)
175#define ia64_uc_access_fpreg	UNW_OBJ(uc_access_fpreg)
176
177extern int ia64_uc_access_reg (struct cursor *c, ia64_loc_t loc,
178			       unw_word_t *valp, int write);
179extern int ia64_uc_access_fpreg (struct cursor *c, ia64_loc_t loc,
180				 unw_fpreg_t *valp, int write);
181
182static inline int
183ia64_getfp (struct cursor *c, ia64_loc_t loc, unw_fpreg_t *val)
184{
185  unw_word_t addr;
186  int ret;
187
188  if (IA64_IS_UC_LOC (loc))
189    return ia64_uc_access_fpreg (c, loc, val, 0);
190
191  if (IA64_IS_REG_LOC (loc))
192    return (*c->as->acc.access_fpreg) (c->as, IA64_GET_REG (loc),
193				       val, 0, c->as_arg);
194
195  addr = IA64_GET_ADDR (loc);
196  ret = (*c->as->acc.access_mem) (c->as, addr + 0, &val->raw.bits[0], 0,
197				  c->as_arg);
198  if (ret < 0)
199    return ret;
200
201  return (*c->as->acc.access_mem) (c->as, addr + 8, &val->raw.bits[1], 0,
202				   c->as_arg);
203}
204
205static inline int
206ia64_putfp (struct cursor *c, ia64_loc_t loc, unw_fpreg_t val)
207{
208  unw_word_t addr;
209  int ret;
210
211  if (IA64_IS_UC_LOC (loc))
212    return ia64_uc_access_fpreg (c, loc, &val, 1);
213
214  if (IA64_IS_REG_LOC (loc))
215    return (*c->as->acc.access_fpreg) (c->as, IA64_GET_REG (loc), &val, 1,
216				       c->as_arg);
217
218  addr = IA64_GET_ADDR (loc);
219  ret = (*c->as->acc.access_mem) (c->as, addr + 0, &val.raw.bits[0], 1,
220				  c->as_arg);
221  if (ret < 0)
222    return ret;
223
224  return (*c->as->acc.access_mem) (c->as, addr + 8, &val.raw.bits[1], 1,
225				   c->as_arg);
226}
227
228/* Get the 64 data bits from location LOC.  If bit 0 is cleared, LOC
229   is a memory address, otherwise it is a register number.  If the
230   register is a floating-point register, the 64 bits are read from
231   the significand bits.  */
232
233static inline int
234ia64_get (struct cursor *c, ia64_loc_t loc, unw_word_t *val)
235{
236  if (IA64_IS_FP_LOC (loc))
237    {
238      unw_fpreg_t tmp;
239      int ret;
240
241      ret = ia64_getfp (c, loc, &tmp);
242      if (ret < 0)
243	return ret;
244
245      if (c->as->big_endian)
246	*val = tmp.raw.bits[1];
247      else
248	*val = tmp.raw.bits[0];
249      return 0;
250    }
251
252  if (IA64_IS_UC_LOC (loc))
253    return ia64_uc_access_reg (c, loc, val, 0);
254
255  if (IA64_IS_REG_LOC (loc))
256    return (*c->as->acc.access_reg)(c->as, IA64_GET_REG (loc), val, 0,
257				    c->as_arg);
258  else
259    return (*c->as->acc.access_mem)(c->as, IA64_GET_ADDR (loc), val, 0,
260				    c->as_arg);
261}
262
263static inline int
264ia64_put (struct cursor *c, ia64_loc_t loc, unw_word_t val)
265{
266  if (IA64_IS_FP_LOC (loc))
267    {
268      unw_fpreg_t tmp;
269
270      memset (&tmp, 0, sizeof (tmp));
271      if (c->as->big_endian)
272	tmp.raw.bits[1] = val;
273      else
274	tmp.raw.bits[0] = val;
275      return ia64_putfp (c, loc, tmp);
276    }
277
278  if (IA64_IS_UC_LOC (loc))
279    return ia64_uc_access_reg (c, loc, &val, 1);
280
281  if (IA64_IS_REG_LOC (loc))
282    return (*c->as->acc.access_reg)(c->as, IA64_GET_REG (loc), &val, 1,
283				    c->as_arg);
284  else
285    return (*c->as->acc.access_mem)(c->as, IA64_GET_ADDR (loc), &val, 1,
286				    c->as_arg);
287}
288
289#endif /* !UNW_LOCAL_ONLY */
290
291struct ia64_unwind_block
292  {
293    unw_word_t header;
294    unw_word_t desc[0];			/* unwind descriptors */
295
296    /* Personality routine and language-specific data follow behind
297       descriptors.  */
298  };
299
300enum ia64_where
301  {
302    IA64_WHERE_NONE,	/* register isn't saved at all */
303    IA64_WHERE_GR,	/* register is saved in a general register */
304    IA64_WHERE_FR,	/* register is saved in a floating-point register */
305    IA64_WHERE_BR,	/* register is saved in a branch register */
306    IA64_WHERE_SPREL,	/* register is saved on memstack (sp-relative) */
307    IA64_WHERE_PSPREL,	/* register is saved on memstack (psp-relative) */
308
309    /* At the end of each prologue these locations get resolved to
310       IA64_WHERE_PSPREL and IA64_WHERE_GR, respectively:  */
311
312    IA64_WHERE_SPILL_HOME, /* register is saved in its spill home */
313    IA64_WHERE_GR_SAVE	/* register is saved in next general register */
314  };
315
316#define IA64_WHEN_NEVER	0x7fffffff
317
318struct ia64_reg_info
319  {
320    unw_word_t val;		/* save location: register number or offset */
321    enum ia64_where where;	/* where the register gets saved */
322    int when;			/* when the register gets saved */
323  };
324
325struct ia64_labeled_state;	/* opaque structure */
326
327struct ia64_reg_state
328  {
329    struct ia64_reg_state *next;    /* next (outer) element on state stack */
330    struct ia64_reg_info reg[IA64_NUM_PREGS];	/* register save locations */
331  };
332
333struct ia64_state_record
334  {
335    unsigned int first_region : 1;	/* is this the first region? */
336    unsigned int done : 1;		/* are we done scanning descriptors? */
337    unsigned int any_spills : 1;	/* got any register spills? */
338    unsigned int in_body : 1;		/* are we inside prologue or body? */
339    uint8_t *imask;		/* imask of spill_mask record or NULL */
340    uint16_t abi_marker;
341
342    unw_word_t pr_val;		/* predicate values */
343    unw_word_t pr_mask;		/* predicate mask */
344
345    long spill_offset;		/* psp-relative offset for spill base */
346    int region_start;
347    int region_len;
348    int when_sp_restored;
349    int epilogue_count;
350    int when_target;
351
352    uint8_t gr_save_loc;	/* next save register */
353    uint8_t return_link_reg;	/* branch register used as return pointer */
354
355    struct ia64_labeled_state *labeled_states;
356    struct ia64_reg_state curr;
357  };
358
359struct ia64_labeled_state
360  {
361    struct ia64_labeled_state *next;	/* next label (or NULL) */
362    unsigned long label;			/* label for this state */
363    struct ia64_reg_state saved_state;
364  };
365
366/* Convenience macros: */
367#define ia64_make_proc_info		UNW_OBJ(make_proc_info)
368#define ia64_create_state_record	UNW_OBJ(create_state_record)
369#define ia64_free_state_record		UNW_OBJ(free_state_record)
370#define ia64_find_save_locs		UNW_OBJ(find_save_locs)
371#define ia64_per_thread_cache		UNW_OBJ(per_thread_cache)
372#define ia64_script_cache_init		UNW_OBJ(script_cache_init)
373#define ia64_access_reg			UNW_OBJ(access_reg)
374#define ia64_access_fpreg		UNW_OBJ(access_fpreg)
375#define ia64_scratch_loc		UNW_OBJ(scratch_loc)
376#define ia64_local_resume		UNW_OBJ(local_resume)
377#define ia64_local_addr_space_init	UNW_OBJ(local_addr_space_init)
378#define ia64_strloc			UNW_OBJ(strloc)
379#define ia64_install_cursor		UNW_OBJ(install_cursor)
380#define rbs_switch			UNW_OBJ(rbs_switch)
381#define rbs_find_stacked		UNW_OBJ(rbs_find_stacked)
382#define rbs_cover_and_flush		UNW_OBJ(rbs_cover_and_flush)
383#define ia64_init			UNW_ARCH_OBJ(init)
384
385extern int ia64_make_proc_info (struct cursor *c);
386extern int ia64_create_state_record (struct cursor *c,
387				     struct ia64_state_record *sr);
388extern int ia64_free_state_record (struct ia64_state_record *sr);
389extern int ia64_find_save_locs (struct cursor *c);
390extern void ia64_script_cache_init (struct ia64_script_cache *cache);
391extern void ia64_local_addr_space_init (void);
392extern void ia64_init (void);
393extern int ia64_access_reg (struct cursor *c, unw_regnum_t reg,
394			    unw_word_t *valp, int write);
395extern int ia64_access_fpreg (struct cursor *c, unw_regnum_t reg,
396			      unw_fpreg_t *valp, int write);
397extern ia64_loc_t ia64_scratch_loc (struct cursor *c, unw_regnum_t reg);
398
399extern void ia64_install_cursor (struct cursor *c, unw_word_t pri_unat,
400				 unw_word_t *extra)
401	__attribute__ ((noreturn));
402extern int ia64_local_resume (unw_addr_space_t as, unw_cursor_t *cursor,
403			      void *arg);
404extern int rbs_switch (struct cursor *c,
405		       unw_word_t saved_bsp, unw_word_t saved_bspstore,
406		       ia64_loc_t saved_rnat_loc);
407extern int rbs_find_stacked (struct cursor *c, unw_word_t regs_to_skip,
408			     ia64_loc_t *locp, ia64_loc_t *rnat_locp);
409extern int rbs_cover_and_flush (struct cursor *c, unw_word_t nregs);
410
411/* Warning: ia64_strloc() is for debugging only and it is NOT re-entrant! */
412extern const char *ia64_strloc (ia64_loc_t loc);
413
414/* Return true if the register-backing store is inside a ucontext_t
415   that needs to be accessed via uc_access(3).  */
416
417static inline int
418rbs_on_uc (struct rbs_area *rbs)
419{
420  return IA64_IS_UC_LOC (rbs->rnat_loc) && !IA64_IS_REG_LOC (rbs->rnat_loc);
421}
422
423/* Return true if BSP points to a word that's stored on register
424   backing-store RBS.  */
425static inline int
426rbs_contains (struct rbs_area *rbs, unw_word_t bsp)
427{
428  int result;
429
430  /* Caveat: this takes advantage of unsigned arithmetic.  The full
431     test is (bsp >= rbs->end - rbs->size) && (bsp < rbs->end).  We
432     take advantage of the fact that -n == ~n + 1.  */
433  result = bsp - rbs->end > ~rbs->size;
434  debug (150, "%s: 0x%lx in [0x%lx-0x%lx) => %d\n", __FUNCTION__,
435	 (long) bsp, (long) (rbs->end - rbs->size), (long) rbs->end, result);
436  return result;
437}
438
439static ia64_loc_t
440rbs_get_rnat_loc (struct rbs_area *rbs, unw_word_t bsp)
441{
442  unw_word_t rnat_addr = ia64_rse_rnat_addr (bsp);
443  ia64_loc_t rnat_loc;
444
445  if (rbs_contains (rbs, rnat_addr))
446    {
447      if (rbs_on_uc (rbs))
448	rnat_loc = IA64_LOC_UC_ADDR (rnat_addr, 0);
449      else
450	rnat_loc = IA64_LOC_ADDR (rnat_addr, 0);
451    }
452  else
453    rnat_loc = rbs->rnat_loc;
454  return rnat_loc;
455}
456
457static ia64_loc_t
458rbs_loc (struct rbs_area *rbs, unw_word_t bsp)
459{
460  if (rbs_on_uc (rbs))
461    return IA64_LOC_UC_ADDR (bsp, 0);
462  else
463    return IA64_LOC_ADDR (bsp, 0);
464}
465
466static inline int
467ia64_get_stacked (struct cursor *c, unw_word_t reg,
468		  ia64_loc_t *locp, ia64_loc_t *rnat_locp)
469{
470  struct rbs_area *rbs = c->rbs_area + c->rbs_curr;
471  unw_word_t addr, regs_to_skip = reg - 32;
472  int ret = 0;
473
474  assert (reg >= 32 && reg < 128);
475
476  addr = ia64_rse_skip_regs (c->bsp, regs_to_skip);
477  if (locp)
478    *locp = rbs_loc (rbs, addr);
479  if (rnat_locp)
480    *rnat_locp = rbs_get_rnat_loc (rbs, addr);
481
482  if (!rbs_contains (rbs, addr))
483    ret = rbs_find_stacked (c, regs_to_skip, locp, rnat_locp);
484  return ret;
485}
486
487/* XXX should be in glibc: */
488#ifndef IA64_SC_FLAG_ONSTACK
489# define IA64_SC_FLAG_ONSTACK_BIT    0 /* running on signal stack? */
490# define IA64_SC_FLAG_IN_SYSCALL_BIT 1 /* did signal interrupt a syscall? */
491# define IA64_SC_FLAG_FPH_VALID_BIT  2 /* is state in f[32]-f[127] valid? */
492
493# define IA64_SC_FLAG_ONSTACK		(1 << IA64_SC_FLAG_ONSTACK_BIT)
494# define IA64_SC_FLAG_IN_SYSCALL	(1 << IA64_SC_FLAG_IN_SYSCALL_BIT)
495# define IA64_SC_FLAG_FPH_VALID		(1 << IA64_SC_FLAG_FPH_VALID_BIT)
496#endif
497
498#endif /* unwind_i_h */
499