2002-11-23 03:12:30 +01:00
|
|
|
|
/* libunwind - a platform-independent unwind library
|
|
|
|
|
Copyright (C) 2002 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. */
|
|
|
|
|
|
|
|
|
|
/* This file defines the runtime-support routines for dynamically
|
2002-12-03 09:19:58 +01:00
|
|
|
|
generated code. Even though it is implemented as part of libunwind,
|
|
|
|
|
it is logically separate from the interface to perform the actual
|
|
|
|
|
unwinding. In particular, this interface is always used in the
|
|
|
|
|
context of the unwind target, whereas the rest of the unwind API is
|
|
|
|
|
used in context of the process that is doing the unwind (which may be
|
|
|
|
|
a debugger running on another machine, for example).
|
|
|
|
|
|
|
|
|
|
Note that the data-structures declared here server a dual purpose:
|
|
|
|
|
when a program registers a dynamically generated procedure, it uses
|
|
|
|
|
these structures directly. On the other hand, with remote-unwinding,
|
|
|
|
|
the data-structures are read from the remote process's memory and
|
2002-12-12 10:17:41 +01:00
|
|
|
|
translated into internalized versions. To facilitate remote-access,
|
|
|
|
|
the following rules should be followed in declaring these structures:
|
|
|
|
|
|
|
|
|
|
(1) Declare a member as a pointer only if the the information the
|
|
|
|
|
member points to needs to be internalized as well (e.g., a
|
|
|
|
|
string representing a procedure name should be declared as
|
|
|
|
|
"const char *", but the instruction pointer should be declared
|
|
|
|
|
as unw_word_t).
|
|
|
|
|
|
|
|
|
|
(2) Provide sufficient padding to ensure that no implicit padding
|
|
|
|
|
will be needed on any of the supported target architectures. For
|
|
|
|
|
the time being, padding data structures with the assumption that
|
|
|
|
|
sizeof (unw_word_t) == 8 should be sufficient. (Note: it's not
|
|
|
|
|
impossible to internalize structures with internal padding, but
|
|
|
|
|
it does make the process a bit harder).
|
|
|
|
|
|
|
|
|
|
(3) Don't declare members that contain bitfields or floating-point
|
|
|
|
|
values.
|
|
|
|
|
|
|
|
|
|
(4) Don't declare members with enumeration types. Declare them as
|
|
|
|
|
int32_t instead. */
|
2002-11-23 03:12:30 +01:00
|
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
|
{
|
2002-12-03 09:19:58 +01:00
|
|
|
|
UNW_DYN_STOP = 0, /* end-of-unwind-info marker */
|
|
|
|
|
UNW_DYN_SAVE_REG, /* save register to another register */
|
|
|
|
|
UNW_DYN_SPILL_FP_REL, /* frame-pointer-relative register spill */
|
|
|
|
|
UNW_DYN_SPILL_SP_REL, /* stack-pointer-relative register spill */
|
|
|
|
|
UNW_DYN_ADD, /* add constant value to a register */
|
|
|
|
|
UNW_DYN_POP_FRAMES, /* drop one or more stack frames */
|
|
|
|
|
UNW_DYN_LABEL_STATE, /* name the current state */
|
|
|
|
|
UNW_DYN_COPY_STATE, /* set the region's entry-state */
|
|
|
|
|
UNW_DYN_ALIAS /* get unwind info from an alias */
|
|
|
|
|
}
|
|
|
|
|
unw_dyn_operation_t;
|
|
|
|
|
|
2002-12-12 10:17:41 +01:00
|
|
|
|
typedef enum
|
|
|
|
|
{
|
|
|
|
|
UNW_INFO_FORMAT_DYNAMIC, /* unw_dyn_proc_info_t */
|
|
|
|
|
UNW_INFO_FORMAT_TABLE /* unw_dyn_table_t */
|
|
|
|
|
}
|
|
|
|
|
unw_dyn_info_format_t;
|
|
|
|
|
|
2002-12-03 09:19:58 +01:00
|
|
|
|
typedef struct unw_dyn_op
|
|
|
|
|
{
|
2002-12-12 10:17:41 +01:00
|
|
|
|
int8_t tag; /* what operation? */
|
|
|
|
|
int8_t qp; /* qualifying predicate register */
|
2002-12-03 09:19:58 +01:00
|
|
|
|
int16_t reg; /* what register */
|
|
|
|
|
int32_t when; /* when does it take effect? */
|
|
|
|
|
unw_word_t val; /* auxiliary value */
|
2002-11-23 03:12:30 +01:00
|
|
|
|
}
|
2002-12-03 09:19:58 +01:00
|
|
|
|
unw_dyn_op_t;
|
2002-11-23 03:12:30 +01:00
|
|
|
|
|
2002-12-03 09:19:58 +01:00
|
|
|
|
typedef struct unw_dyn_region_info
|
2002-11-23 03:12:30 +01:00
|
|
|
|
{
|
2002-12-03 09:19:58 +01:00
|
|
|
|
struct unw_dyn_region_info *next; /* linked list of regions */
|
|
|
|
|
uint32_t insn_count; /* region length (# of instructions) */
|
|
|
|
|
uint32_t op_count; /* length of op-array */
|
|
|
|
|
unw_dyn_op_t op[1]; /* variable-length op-array */
|
2002-11-23 03:12:30 +01:00
|
|
|
|
}
|
2002-12-03 09:19:58 +01:00
|
|
|
|
unw_dyn_region_info_t;
|
2002-11-23 03:12:30 +01:00
|
|
|
|
|
2002-12-03 09:19:58 +01:00
|
|
|
|
typedef struct unw_dyn_proc_info
|
2002-11-23 03:12:30 +01:00
|
|
|
|
{
|
2002-12-12 10:17:41 +01:00
|
|
|
|
unw_word_t name_ptr; /* address of human-readable procedure name */
|
2002-12-03 09:19:58 +01:00
|
|
|
|
unw_word_t handler; /* address of personality routine */
|
|
|
|
|
uint32_t flags;
|
2002-12-12 10:17:41 +01:00
|
|
|
|
int32_t pad0;
|
2002-12-03 09:19:58 +01:00
|
|
|
|
unw_dyn_region_info_t *regions;
|
|
|
|
|
}
|
|
|
|
|
unw_dyn_proc_info_t;
|
|
|
|
|
|
|
|
|
|
typedef struct unw_dyn_table_info
|
|
|
|
|
{
|
2002-12-12 10:17:41 +01:00
|
|
|
|
unw_word_t name_ptr; /* addr. of table name (e.g., library name) */
|
2002-12-03 09:19:58 +01:00
|
|
|
|
unw_word_t segbase; /* segment base */
|
2002-12-12 10:17:41 +01:00
|
|
|
|
unw_word_t table_len; /* must be a multiple of sizeof(unw_word_t)! */
|
|
|
|
|
unw_word_t *table_data;
|
2002-12-03 09:19:58 +01:00
|
|
|
|
}
|
|
|
|
|
unw_dyn_table_info_t;
|
|
|
|
|
|
|
|
|
|
typedef struct unw_dyn_info
|
|
|
|
|
{
|
2002-12-12 10:17:41 +01:00
|
|
|
|
/* doubly-linked list of dyn-info structures: */
|
|
|
|
|
struct unw_dyn_info *next;
|
|
|
|
|
struct unw_dyn_info *prev;
|
2002-12-03 09:19:58 +01:00
|
|
|
|
unw_word_t start_ip; /* first IP covered by this entry */
|
|
|
|
|
unw_word_t end_ip; /* first IP NOT covered by this entry */
|
|
|
|
|
unw_word_t gp; /* global-pointer in effect for this entry */
|
2002-12-12 10:17:41 +01:00
|
|
|
|
int32_t format; /* real type: unw_dyn_info_format_t */
|
|
|
|
|
int32_t pad;
|
2002-12-03 09:19:58 +01:00
|
|
|
|
union
|
2002-11-23 03:12:30 +01:00
|
|
|
|
{
|
2002-12-03 09:19:58 +01:00
|
|
|
|
unw_dyn_proc_info_t pi;
|
|
|
|
|
unw_dyn_table_info_t ti;
|
2002-11-23 03:12:30 +01:00
|
|
|
|
}
|
2002-12-03 09:19:58 +01:00
|
|
|
|
u;
|
|
|
|
|
}
|
|
|
|
|
unw_dyn_info_t;
|
|
|
|
|
|
|
|
|
|
typedef struct unw_dyn_info_list
|
|
|
|
|
{
|
2002-12-12 10:17:41 +01:00
|
|
|
|
uint32_t version;
|
|
|
|
|
uint32_t generation;
|
2002-12-03 09:19:58 +01:00
|
|
|
|
unw_dyn_info_t *first;
|
2002-11-23 03:12:30 +01:00
|
|
|
|
}
|
2002-12-03 09:19:58 +01:00
|
|
|
|
unw_dyn_info_list_t;
|
2002-11-23 03:12:30 +01:00
|
|
|
|
|
2002-12-03 09:19:58 +01:00
|
|
|
|
/* Return the size (in bytes) of an unw_dyn_region_info_t structure that can
|
2002-11-23 03:12:30 +01:00
|
|
|
|
hold OP_COUNT ops. */
|
2002-12-03 09:19:58 +01:00
|
|
|
|
#define _U_dyn_region_info_size(op_count) \
|
|
|
|
|
(sizeof (unw_dyn_region_info_t) \
|
|
|
|
|
+ (op_count > 0) ? ((op_count) - 1) * sizeof (unw_dyn_op_t) : 0)
|
2002-11-23 03:12:30 +01:00
|
|
|
|
|
|
|
|
|
/* Register the unwind info for a single procedure.
|
|
|
|
|
This routine is NOT signal-safe. */
|
2002-12-12 10:17:41 +01:00
|
|
|
|
extern void _U_dyn_register (unw_dyn_info_t *di);
|
2002-11-23 03:12:30 +01:00
|
|
|
|
|
|
|
|
|
/* Cancel the unwind info for a single procedure.
|
|
|
|
|
This routine is NOT signal-safe. */
|
2002-12-12 10:17:41 +01:00
|
|
|
|
extern void _U_dyn_cancel (unw_dyn_info_t *di);
|
2002-11-23 03:12:30 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Convenience routines. */
|
|
|
|
|
|
2002-12-03 09:19:58 +01:00
|
|
|
|
#define _U_dyn_op(_t, _q, _w, _r, _v) \
|
|
|
|
|
((unw_dyn_op_t) { \
|
|
|
|
|
.tag = (_t), \
|
|
|
|
|
.qp = (_q), \
|
|
|
|
|
.when = (_w), \
|
|
|
|
|
.reg = (_r), \
|
|
|
|
|
.val = (_v) \
|
|
|
|
|
})
|
2002-11-23 03:12:30 +01:00
|
|
|
|
|
2002-12-03 09:19:58 +01:00
|
|
|
|
#define _U_dyn_op_save_reg(op, qp, when, reg, dst) \
|
|
|
|
|
(*(op) = _U_dyn_op (UNW_DYN_SAVE_REG, (qp), (when), (reg), (dst)))
|
2002-11-23 03:12:30 +01:00
|
|
|
|
|
2002-12-03 09:19:58 +01:00
|
|
|
|
#define _U_dyn_op_spill_fp_rel(op, qp, when, reg, offset) \
|
|
|
|
|
(*(op) = _U_dyn_op (UNW_DYN_SPILL_FP_REL, (qp), (when), (reg), \
|
|
|
|
|
(offset)))
|
2002-11-23 03:12:30 +01:00
|
|
|
|
|
2002-12-03 09:19:58 +01:00
|
|
|
|
#define _U_dyn_op_spill_sp_rel(op, qp, when, reg, offset) \
|
|
|
|
|
(*(op) = _U_dyn_op (UNW_DYN_SPILL_SP_REL, (qp), (when), (reg), \
|
|
|
|
|
(offset)))
|
2002-11-23 03:12:30 +01:00
|
|
|
|
|
2002-12-03 09:19:58 +01:00
|
|
|
|
#define _U_dyn_op_add(op, qp, when, reg, value) \
|
|
|
|
|
(*(op) = _U_dyn_op (UNW_DYN_ADD, (qp), (when), (reg), (value)))
|
2002-11-23 03:12:30 +01:00
|
|
|
|
|
2002-12-03 09:19:58 +01:00
|
|
|
|
#define _U_dyn_op_pop_frames(op, qp, when, num_frames) \
|
|
|
|
|
(*(op) = _U_dyn_op (UNW_DYN_POP_frames, (qp), (when), 0, (num_frames)))
|
2002-11-23 03:12:30 +01:00
|
|
|
|
|
2002-12-03 09:19:58 +01:00
|
|
|
|
#define _U_dyn_op_label_state(op, qp, when, label) \
|
|
|
|
|
(*(op) = _U_dyn_op (UNW_DYN_LABEL_STATE, (qp), (when), 0, (label)))
|
2002-11-23 03:12:30 +01:00
|
|
|
|
|
2002-12-03 09:19:58 +01:00
|
|
|
|
#define _U_dyn_op_copy_state(op, qp, when, label) \
|
|
|
|
|
(*(op) = _U_dyn_op (UNW_DYN_COPY_STATE, (qp), (when), 0, (label)))
|
2002-11-23 03:12:30 +01:00
|
|
|
|
|
2002-12-03 09:19:58 +01:00
|
|
|
|
#define _U_dyn_op_alias(op, qp, when, addr) \
|
|
|
|
|
(*(op) = _U_dyn_op (UNW_DYN_ALIAS, (qp), (when), 0, (addr)))
|
2002-11-23 03:12:30 +01:00
|
|
|
|
|
2002-12-03 09:19:58 +01:00
|
|
|
|
#define _U_dyn_op_stop(op) \
|
|
|
|
|
((op)->tag = UNW_DYN_STOP)
|