hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 1 | /* libunwind - a platform-independent unwind library |
| 2 | Copyright (C) 2002 Hewlett-Packard Co |
| 3 | Contributed by David Mosberger-Tang <davidm@hpl.hp.com> |
| 4 | |
| 5 | This file is part of libunwind. |
| 6 | |
| 7 | Permission is hereby granted, free of charge, to any person obtaining |
| 8 | a copy of this software and associated documentation files (the |
| 9 | "Software"), to deal in the Software without restriction, including |
| 10 | without limitation the rights to use, copy, modify, merge, publish, |
| 11 | distribute, sublicense, and/or sell copies of the Software, and to |
| 12 | permit persons to whom the Software is furnished to do so, subject to |
| 13 | the following conditions: |
| 14 | |
| 15 | The above copyright notice and this permission notice shall be |
| 16 | included in all copies or substantial portions of the Software. |
| 17 | |
| 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| 22 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
| 25 | |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 26 | /* This file defines the runtime-support routines for dynamically |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 27 | generated code. Even though it is implemented as part of libunwind, |
| 28 | it is logically separate from the interface to perform the actual |
| 29 | unwinding. In particular, this interface is always used in the |
| 30 | context of the unwind target, whereas the rest of the unwind API is |
| 31 | used in context of the process that is doing the unwind (which may be |
| 32 | a debugger running on another machine, for example). |
| 33 | |
| 34 | Note that the data-structures declared here server a dual purpose: |
| 35 | when a program registers a dynamically generated procedure, it uses |
| 36 | these structures directly. On the other hand, with remote-unwinding, |
| 37 | the data-structures are read from the remote process's memory and |
| 38 | translated into internalized versions. Because of this, care needs to |
| 39 | be taken when choosing the types of structure members: use a pointer |
| 40 | only if the member can be translated into an internalized equivalent |
| 41 | (such as a string). Similarly, for members that need to hold an |
| 42 | address in the unwindee, unw_word_t needs to be used. */ |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 43 | |
| 44 | typedef enum |
| 45 | { |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 46 | UNW_DYN_STOP = 0, /* end-of-unwind-info marker */ |
| 47 | UNW_DYN_SAVE_REG, /* save register to another register */ |
| 48 | UNW_DYN_SPILL_FP_REL, /* frame-pointer-relative register spill */ |
| 49 | UNW_DYN_SPILL_SP_REL, /* stack-pointer-relative register spill */ |
| 50 | UNW_DYN_ADD, /* add constant value to a register */ |
| 51 | UNW_DYN_POP_FRAMES, /* drop one or more stack frames */ |
| 52 | UNW_DYN_LABEL_STATE, /* name the current state */ |
| 53 | UNW_DYN_COPY_STATE, /* set the region's entry-state */ |
| 54 | UNW_DYN_ALIAS /* get unwind info from an alias */ |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 55 | } |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 56 | unw_dyn_operation_t; |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 57 | |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 58 | typedef struct unw_dyn_op |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 59 | { |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 60 | uint16_t tag; /* what operation? */ |
| 61 | int16_t reg; /* what register */ |
| 62 | int16_t qp; /* qualifying predicate register */ |
| 63 | int16_t pad0; |
| 64 | int32_t when; /* when does it take effect? */ |
| 65 | unw_word_t val; /* auxiliary value */ |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 66 | } |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 67 | unw_dyn_op_t; |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 68 | |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 69 | typedef struct unw_dyn_region_info |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 70 | { |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 71 | struct unw_dyn_region_info *next; /* linked list of regions */ |
| 72 | uint32_t insn_count; /* region length (# of instructions) */ |
| 73 | uint32_t op_count; /* length of op-array */ |
| 74 | unw_dyn_op_t op[1]; /* variable-length op-array */ |
| 75 | } |
| 76 | unw_dyn_region_info_t; |
| 77 | |
| 78 | typedef struct unw_dyn_proc_info |
| 79 | { |
| 80 | const char *name; /* unique & human-readable procedure name */ |
| 81 | unw_word_t handler; /* address of personality routine */ |
| 82 | uint32_t flags; |
| 83 | unw_dyn_region_info_t *regions; |
| 84 | } |
| 85 | unw_dyn_proc_info_t; |
| 86 | |
| 87 | typedef struct unw_dyn_table_info |
| 88 | { |
| 89 | const char *name; /* table name (e.g., name of library) */ |
| 90 | unw_word_t segbase; /* segment base */ |
| 91 | unw_word_t table_size; |
| 92 | void *table_data; |
| 93 | } |
| 94 | unw_dyn_table_info_t; |
| 95 | |
| 96 | typedef struct unw_dyn_info |
| 97 | { |
| 98 | struct unw_dyn_info *next; /* linked list of dyn-info structures */ |
| 99 | unw_word_t start_ip; /* first IP covered by this entry */ |
| 100 | unw_word_t end_ip; /* first IP NOT covered by this entry */ |
| 101 | unw_word_t gp; /* global-pointer in effect for this entry */ |
| 102 | enum |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 103 | { |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 104 | UNW_INFO_FORMAT_DYNAMIC, /* unw_dyn_proc_info_t */ |
| 105 | UNW_INFO_FORMAT_TABLE /* unw_dyn_table_t */ |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 106 | } |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 107 | format; |
| 108 | union |
| 109 | { |
| 110 | unw_dyn_proc_info_t pi; |
| 111 | unw_dyn_table_info_t ti; |
| 112 | } |
| 113 | u; |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 114 | } |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 115 | unw_dyn_info_t; |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 116 | |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 117 | typedef struct unw_dyn_info_list |
| 118 | { |
| 119 | unsigned long generation; |
| 120 | unw_dyn_info_t *first; |
| 121 | } |
| 122 | unw_dyn_info_list_t; |
| 123 | |
| 124 | /* Return the size (in bytes) of an unw_dyn_region_info_t structure that can |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 125 | hold OP_COUNT ops. */ |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 126 | #define _U_dyn_region_info_size(op_count) \ |
| 127 | (sizeof (unw_dyn_region_info_t) \ |
| 128 | + (op_count > 0) ? ((op_count) - 1) * sizeof (unw_dyn_op_t) : 0) |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 129 | |
| 130 | /* Register the unwind info for a single procedure. |
| 131 | This routine is NOT signal-safe. */ |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 132 | extern int _U_dyn_register (unw_dyn_info_t *di); |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 133 | |
| 134 | /* Cancel the unwind info for a single procedure. |
| 135 | This routine is NOT signal-safe. */ |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 136 | extern int _U_dyn_cancel (unw_dyn_info_t *di); |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 137 | |
| 138 | |
| 139 | /* Convenience routines. */ |
| 140 | |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 141 | #define _U_dyn_op(_t, _q, _w, _r, _v) \ |
| 142 | ((unw_dyn_op_t) { \ |
| 143 | .tag = (_t), \ |
| 144 | .qp = (_q), \ |
| 145 | .when = (_w), \ |
| 146 | .reg = (_r), \ |
| 147 | .val = (_v) \ |
| 148 | }) |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 149 | |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 150 | #define _U_dyn_op_save_reg(op, qp, when, reg, dst) \ |
| 151 | (*(op) = _U_dyn_op (UNW_DYN_SAVE_REG, (qp), (when), (reg), (dst))) |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 152 | |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 153 | #define _U_dyn_op_spill_fp_rel(op, qp, when, reg, offset) \ |
| 154 | (*(op) = _U_dyn_op (UNW_DYN_SPILL_FP_REL, (qp), (when), (reg), \ |
| 155 | (offset))) |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 156 | |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 157 | #define _U_dyn_op_spill_sp_rel(op, qp, when, reg, offset) \ |
| 158 | (*(op) = _U_dyn_op (UNW_DYN_SPILL_SP_REL, (qp), (when), (reg), \ |
| 159 | (offset))) |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 160 | |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 161 | #define _U_dyn_op_add(op, qp, when, reg, value) \ |
| 162 | (*(op) = _U_dyn_op (UNW_DYN_ADD, (qp), (when), (reg), (value))) |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 163 | |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 164 | #define _U_dyn_op_pop_frames(op, qp, when, num_frames) \ |
| 165 | (*(op) = _U_dyn_op (UNW_DYN_POP_frames, (qp), (when), 0, (num_frames))) |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 166 | |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 167 | #define _U_dyn_op_label_state(op, qp, when, label) \ |
| 168 | (*(op) = _U_dyn_op (UNW_DYN_LABEL_STATE, (qp), (when), 0, (label))) |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 169 | |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 170 | #define _U_dyn_op_copy_state(op, qp, when, label) \ |
| 171 | (*(op) = _U_dyn_op (UNW_DYN_COPY_STATE, (qp), (when), 0, (label))) |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 172 | |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 173 | #define _U_dyn_op_alias(op, qp, when, addr) \ |
| 174 | (*(op) = _U_dyn_op (UNW_DYN_ALIAS, (qp), (when), 0, (addr))) |
hp.com!davidm | d7e9f75 | 2002-11-23 02:12:30 +0000 | [diff] [blame] | 175 | |
mostang.com!davidm | dfc08ea | 2002-12-03 08:19:58 +0000 | [diff] [blame] | 176 | #define _U_dyn_op_stop(op) \ |
| 177 | ((op)->tag = UNW_DYN_STOP) |