|
|
/* libunwind - a platform-independent unwind library |
|
|
Copyright (C) 2002-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. */ |
|
|
|
|
|
/* This file defines the runtime-support routines for dynamically |
|
|
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 |
|
|
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. */ |
|
|
|
|
|
typedef enum |
|
|
{ |
|
|
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; |
|
|
|
|
|
typedef enum |
|
|
{ |
|
|
UNW_INFO_FORMAT_DYNAMIC, /* unw_dyn_proc_info_t */ |
|
|
UNW_INFO_FORMAT_TABLE, /* unw_dyn_table_t */ |
|
|
UNW_INFO_FORMAT_REMOTE_TABLE, /* unw_dyn_remote_table_t */ |
|
|
UNW_INFO_FORMAT_ARM_EXIDX /* ARM specific unwind info */ |
|
|
} |
|
|
unw_dyn_info_format_t; |
|
|
|
|
|
typedef struct unw_dyn_op |
|
|
{ |
|
|
int8_t tag; /* what operation? */ |
|
|
int8_t qp; /* qualifying predicate register */ |
|
|
int16_t reg; /* what register */ |
|
|
int32_t when; /* when does it take effect? */ |
|
|
unw_word_t val; /* auxiliary value */ |
|
|
} |
|
|
unw_dyn_op_t; |
|
|
|
|
|
typedef struct unw_dyn_region_info |
|
|
{ |
|
|
struct unw_dyn_region_info *next; /* linked list of regions */ |
|
|
int32_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 */ |
|
|
} |
|
|
unw_dyn_region_info_t; |
|
|
|
|
|
typedef struct unw_dyn_proc_info |
|
|
{ |
|
|
unw_word_t name_ptr; /* address of human-readable procedure name */ |
|
|
unw_word_t handler; /* address of personality routine */ |
|
|
uint32_t flags; |
|
|
int32_t pad0; |
|
|
unw_dyn_region_info_t *regions; |
|
|
} |
|
|
unw_dyn_proc_info_t; |
|
|
|
|
|
typedef struct unw_dyn_table_info |
|
|
{ |
|
|
unw_word_t name_ptr; /* addr. of table name (e.g., library name) */ |
|
|
unw_word_t segbase; /* segment base */ |
|
|
unw_word_t table_len; /* must be a multiple of sizeof(unw_word_t)! */ |
|
|
unw_word_t *table_data; |
|
|
} |
|
|
unw_dyn_table_info_t; |
|
|
|
|
|
typedef struct unw_dyn_remote_table_info |
|
|
{ |
|
|
unw_word_t name_ptr; /* addr. of table name (e.g., library name) */ |
|
|
unw_word_t segbase; /* segment base */ |
|
|
unw_word_t table_len; /* must be a multiple of sizeof(unw_word_t)! */ |
|
|
unw_word_t table_data; |
|
|
} |
|
|
unw_dyn_remote_table_info_t; |
|
|
|
|
|
typedef struct unw_dyn_info |
|
|
{ |
|
|
/* doubly-linked list of dyn-info structures: */ |
|
|
struct unw_dyn_info *next; |
|
|
struct unw_dyn_info *prev; |
|
|
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 */ |
|
|
int32_t format; /* real type: unw_dyn_info_format_t */ |
|
|
int32_t pad; |
|
|
union |
|
|
{ |
|
|
unw_dyn_proc_info_t pi; |
|
|
unw_dyn_table_info_t ti; |
|
|
unw_dyn_remote_table_info_t rti; |
|
|
} |
|
|
u; |
|
|
} |
|
|
unw_dyn_info_t; |
|
|
|
|
|
typedef struct unw_dyn_info_list |
|
|
{ |
|
|
uint32_t version; |
|
|
uint32_t generation; |
|
|
unw_dyn_info_t *first; |
|
|
} |
|
|
unw_dyn_info_list_t; |
|
|
|
|
|
/* Return the size (in bytes) of an unw_dyn_region_info_t structure that can |
|
|
hold OP_COUNT ops. */ |
|
|
#define _U_dyn_region_info_size(op_count) \ |
|
|
((char *) (((unw_dyn_region_info_t *) NULL)->op + (op_count)) \ |
|
|
- (char *) NULL) |
|
|
|
|
|
/* Register the unwind info for a single procedure. |
|
|
This routine is NOT signal-safe. */ |
|
|
extern void _U_dyn_register (unw_dyn_info_t *); |
|
|
|
|
|
/* Cancel the unwind info for a single procedure. |
|
|
This routine is NOT signal-safe. */ |
|
|
extern void _U_dyn_cancel (unw_dyn_info_t *); |
|
|
|
|
|
|
|
|
/* Convenience routines. */ |
|
|
|
|
|
#define _U_dyn_op(_tag, _qp, _when, _reg, _val) \ |
|
|
((unw_dyn_op_t) { (_tag), (_qp), (_reg), (_when), (_val) }) |
|
|
|
|
|
#define _U_dyn_op_save_reg(op, qp, when, reg, dst) \ |
|
|
(*(op) = _U_dyn_op (UNW_DYN_SAVE_REG, (qp), (when), (reg), (dst))) |
|
|
|
|
|
#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))) |
|
|
|
|
|
#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))) |
|
|
|
|
|
#define _U_dyn_op_add(op, qp, when, reg, value) \ |
|
|
(*(op) = _U_dyn_op (UNW_DYN_ADD, (qp), (when), (reg), (value))) |
|
|
|
|
|
#define _U_dyn_op_pop_frames(op, qp, when, num_frames) \ |
|
|
(*(op) = _U_dyn_op (UNW_DYN_POP_FRAMES, (qp), (when), 0, (num_frames))) |
|
|
|
|
|
#define _U_dyn_op_label_state(op, label) \ |
|
|
(*(op) = _U_dyn_op (UNW_DYN_LABEL_STATE, _U_QP_TRUE, -1, 0, (label))) |
|
|
|
|
|
#define _U_dyn_op_copy_state(op, label) \ |
|
|
(*(op) = _U_dyn_op (UNW_DYN_COPY_STATE, _U_QP_TRUE, -1, 0, (label))) |
|
|
|
|
|
#define _U_dyn_op_alias(op, qp, when, addr) \ |
|
|
(*(op) = _U_dyn_op (UNW_DYN_ALIAS, (qp), (when), 0, (addr))) |
|
|
|
|
|
#define _U_dyn_op_stop(op) \ |
|
|
(*(op) = _U_dyn_op (UNW_DYN_STOP, _U_QP_TRUE, -1, 0, 0)) |
|
|
|
|
|
/* The target-dependent qualifying predicate which is always TRUE. On |
|
|
IA-64, that's p0 (0), on non-predicated architectures, the value is |
|
|
ignored. */ |
|
|
#define _U_QP_TRUE _U_TDEP_QP_TRUE
|
|
|
|