| /* libunwind - a platform-independent unwind library |
| Copyright (c) 2003 Hewlett-Packard Development Company, L.P. |
| 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. */ |
| |
| #include "dwarf_i.h" |
| #include "tdep.h" |
| |
| /* The "pick" operator provides an index range of 0..255 indicating |
| that the stack could at least have a depth of up to 256 elements, |
| but the GCC unwinder restricts the depth to 64, which seems |
| reasonable so we use the same value here. */ |
| #define MAX_EXPR_STACK_SIZE 64 |
| |
| #define NUM_OPERANDS(signature) (((signature) >> 6) & 0x3) |
| #define OPND1_TYPE(signature) (((signature) >> 3) & 0x7) |
| #define OPND2_TYPE(signature) (((signature) >> 0) & 0x7) |
| |
| #define OPND_SIGNATURE(n, t1, t2) (((n) << 6) | ((t1) << 3) | ((t2) << 0)) |
| #define OPND1(t1) OPND_SIGNATURE(1, t1, 0) |
| #define OPND2(t1, t2) OPND_SIGNATURE(2, t1, t2) |
| |
| #define VAL8 0x0 |
| #define VAL16 0x1 |
| #define VAL32 0x2 |
| #define VAL64 0x3 |
| #define ULEB128 0x4 |
| #define SLEB128 0x5 |
| #define OFFSET 0x6 /* 32-bit offset for 32-bit DWARF, 64-bit otherwise */ |
| |
| static uint8_t operands[256] = |
| { |
| [DW_OP_addr] = OPND1 (sizeof (unw_word_t) == 4 ? VAL32 : VAL64), |
| [DW_OP_const1u] = OPND1 (VAL8), |
| [DW_OP_const1s] = OPND1 (VAL8), |
| [DW_OP_const2u] = OPND1 (VAL16), |
| [DW_OP_const2s] = OPND1 (VAL16), |
| [DW_OP_const4u] = OPND1 (VAL32), |
| [DW_OP_const4s] = OPND1 (VAL32), |
| [DW_OP_const8u] = OPND1 (VAL64), |
| [DW_OP_const8s] = OPND1 (VAL64), |
| [DW_OP_pick] = OPND1 (VAL8), |
| [DW_OP_plus_uconst] = OPND1 (ULEB128), |
| [DW_OP_skip] = OPND1 (VAL16), |
| [DW_OP_bra] = OPND1 (VAL16), |
| [DW_OP_breg0 + 0] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 1] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 2] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 3] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 4] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 5] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 6] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 7] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 8] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 9] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 10] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 11] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 12] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 13] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 14] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 15] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 16] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 17] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 18] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 19] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 20] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 21] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 22] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 23] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 24] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 25] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 26] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 27] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 28] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 29] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 30] = OPND1 (SLEB128), |
| [DW_OP_breg0 + 31] = OPND1 (SLEB128), |
| [DW_OP_regx] = OPND1 (ULEB128), |
| [DW_OP_fbreg] = OPND1 (SLEB128), |
| [DW_OP_bregx] = OPND2 (ULEB128, SLEB128), |
| [DW_OP_piece] = OPND1 (ULEB128), |
| [DW_OP_deref_size] = OPND1 (VAL8), |
| [DW_OP_xderef_size] = OPND1 (VAL8), |
| [DW_OP_call2] = OPND1 (VAL16), |
| [DW_OP_call4] = OPND1 (VAL32), |
| [DW_OP_call_ref] = OPND1 (OFFSET) |
| }; |
| |
| static inline unw_word_t |
| sword (unw_word_t val) |
| { |
| switch (sizeof (unw_word_t)) |
| { |
| case 1: return (int8_t) val; |
| case 2: return (int16_t) val; |
| case 4: return (int32_t) val; |
| case 8: return (int64_t) val; |
| default: abort (); |
| } |
| } |
| |
| static inline unw_word_t |
| read_operand (unw_addr_space_t as, unw_accessors_t *a, |
| unw_word_t *addr, int operand_type, unw_word_t *val, void *arg) |
| { |
| uint8_t u8; |
| uint16_t u16; |
| uint32_t u32; |
| uint64_t u64; |
| int ret; |
| |
| switch (operand_type) |
| { |
| case VAL8: |
| ret = dwarf_readu8 (as, a, addr, &u8, arg); |
| *val = u8; |
| break; |
| |
| case VAL16: |
| ret = dwarf_readu16 (as, a, addr, &u16, arg); |
| *val = u16; |
| break; |
| |
| case VAL32: |
| ret = dwarf_readu32 (as, a, addr, &u32, arg); |
| *val = u32; |
| break; |
| |
| case VAL64: |
| ret = dwarf_readu64 (as, a, addr, &u64, arg); |
| *val = u64; |
| break; |
| |
| case ULEB128: |
| ret = dwarf_read_uleb128 (as, a, addr, val, arg); |
| break; |
| |
| case SLEB128: |
| ret = dwarf_read_sleb128 (as, a, addr, val, arg); |
| break; |
| |
| case OFFSET: /* only used by DW_OP_call_ref, which we don't implement */ |
| default: |
| Debug (1, "Unexpected operand type %d", operand_type); |
| ret = -UNW_EINVAL; |
| } |
| return ret; |
| } |
| |
| HIDDEN int |
| dwarf_eval_expr (struct dwarf_cursor *c, unw_word_t *addr, unw_word_t len, |
| unw_word_t *valp, int *is_register) |
| { |
| unw_word_t operand1 = 0, operand2 = 0, tmp1, tmp2, tmp3, end_addr; |
| uint8_t opcode, operands_signature, u8; |
| unw_addr_space_t as; |
| unw_accessors_t *a; |
| void *arg; |
| unw_word_t stack[MAX_EXPR_STACK_SIZE]; |
| unsigned int tos = 0; |
| uint16_t u16; |
| uint32_t u32; |
| uint64_t u64; |
| int ret; |
| # define pop() \ |
| ({ \ |
| if (tos >= MAX_EXPR_STACK_SIZE) \ |
| { \ |
| Debug (1, "Stack underflow\n"); \ |
| return -UNW_EINVAL; \ |
| } \ |
| stack[tos--]; \ |
| }) |
| # define push(x) \ |
| do { \ |
| if (tos >= MAX_EXPR_STACK_SIZE) \ |
| { \ |
| Debug (1, "Stack overflow\n"); \ |
| return -UNW_EINVAL; \ |
| } \ |
| stack[tos++] = (x); \ |
| } while (0) |
| # define pick(n) \ |
| ({ \ |
| unsigned int _index = tos - (n); \ |
| if (_index >= MAX_EXPR_STACK_SIZE) \ |
| { \ |
| Debug (1, "Out-of-stack pick\n"); \ |
| return -UNW_EINVAL; \ |
| } \ |
| stack[_index]; \ |
| }) |
| |
| as = c->as; |
| arg = c->as_arg; |
| a = unw_get_accessors (as); |
| end_addr = *addr + len; |
| |
| push (c->cfa); /* push current CFA as required by DWARF spec */ |
| |
| while (*addr < len) |
| { |
| if ((ret = dwarf_readu8 (as, a, addr, &opcode, arg)) < 0) |
| return ret; |
| |
| operands_signature = operands[opcode]; |
| |
| if (unlikely (NUM_OPERANDS (operands_signature) > 0)) |
| { |
| if ((ret = read_operand (as, a, addr, |
| OPND1_TYPE (operands_signature), |
| &operand1, arg)) < 0) |
| return ret; |
| if (NUM_OPERANDS (operands_signature > 1)) |
| if ((ret = read_operand (as, a, addr, |
| OPND2_TYPE (operands_signature), |
| &operand2, arg)) < 0) |
| return ret; |
| } |
| |
| switch ((dwarf_expr_op_t) opcode) |
| { |
| case DW_OP_lit0: case DW_OP_lit1: case DW_OP_lit2: |
| case DW_OP_lit3: case DW_OP_lit4: case DW_OP_lit5: |
| case DW_OP_lit6: case DW_OP_lit7: case DW_OP_lit8: |
| case DW_OP_lit9: case DW_OP_lit10: case DW_OP_lit11: |
| case DW_OP_lit12: case DW_OP_lit13: case DW_OP_lit14: |
| case DW_OP_lit15: case DW_OP_lit16: case DW_OP_lit17: |
| case DW_OP_lit18: case DW_OP_lit19: case DW_OP_lit20: |
| case DW_OP_lit21: case DW_OP_lit22: case DW_OP_lit23: |
| case DW_OP_lit24: case DW_OP_lit25: case DW_OP_lit26: |
| case DW_OP_lit27: case DW_OP_lit28: case DW_OP_lit29: |
| case DW_OP_lit30: case DW_OP_lit31: |
| push (opcode = DW_OP_lit0); |
| break; |
| |
| case DW_OP_breg0: case DW_OP_breg1: case DW_OP_breg2: |
| case DW_OP_breg3: case DW_OP_breg4: case DW_OP_breg5: |
| case DW_OP_breg6: case DW_OP_breg7: case DW_OP_breg8: |
| case DW_OP_breg9: case DW_OP_breg10: case DW_OP_breg11: |
| case DW_OP_breg12: case DW_OP_breg13: case DW_OP_breg14: |
| case DW_OP_breg15: case DW_OP_breg16: case DW_OP_breg17: |
| case DW_OP_breg18: case DW_OP_breg19: case DW_OP_breg20: |
| case DW_OP_breg21: case DW_OP_breg22: case DW_OP_breg23: |
| case DW_OP_breg24: case DW_OP_breg25: case DW_OP_breg26: |
| case DW_OP_breg27: case DW_OP_breg28: case DW_OP_breg29: |
| case DW_OP_breg30: case DW_OP_breg31: |
| if ((ret = unw_get_reg (dwarf_to_cursor (c), |
| dwarf_to_unw_regnum (opcode - DW_OP_breg0), |
| &tmp1)) < 0) |
| return ret; |
| push (tmp1 + operand1); |
| break; |
| |
| case DW_OP_bregx: |
| if ((ret = unw_get_reg (dwarf_to_cursor (c), |
| dwarf_to_unw_regnum (operand1), &tmp1)) < 0) |
| return ret; |
| push (tmp1 + operand2); |
| break; |
| |
| case DW_OP_reg0: case DW_OP_reg1: case DW_OP_reg2: |
| case DW_OP_reg3: case DW_OP_reg4: case DW_OP_reg5: |
| case DW_OP_reg6: case DW_OP_reg7: case DW_OP_reg8: |
| case DW_OP_reg9: case DW_OP_reg10: case DW_OP_reg11: |
| case DW_OP_reg12: case DW_OP_reg13: case DW_OP_reg14: |
| case DW_OP_reg15: case DW_OP_reg16: case DW_OP_reg17: |
| case DW_OP_reg18: case DW_OP_reg19: case DW_OP_reg20: |
| case DW_OP_reg21: case DW_OP_reg22: case DW_OP_reg23: |
| case DW_OP_reg24: case DW_OP_reg25: case DW_OP_reg26: |
| case DW_OP_reg27: case DW_OP_reg28: case DW_OP_reg29: |
| case DW_OP_reg30: case DW_OP_reg31: |
| *valp = dwarf_to_unw_regnum (opcode - DW_OP_reg0); |
| *is_register = 1; |
| return 0; |
| |
| case DW_OP_regx: |
| *valp = dwarf_to_unw_regnum (operand1); |
| *is_register = 1; |
| return 0; |
| |
| case DW_OP_addr: |
| case DW_OP_const1u: |
| case DW_OP_const2u: |
| case DW_OP_const4u: |
| case DW_OP_const8u: |
| case DW_OP_constu: |
| case DW_OP_const8s: |
| case DW_OP_consts: |
| push (operand1); |
| break; |
| |
| case DW_OP_const1s: |
| if (operand1 & 0x80) |
| operand1 |= ((unw_word_t) -1) << 8; |
| push (operand1); |
| break; |
| |
| case DW_OP_const2s: |
| if (operand1 & 0x8000) |
| operand1 |= ((unw_word_t) -1) << 16; |
| push (operand1); |
| break; |
| |
| case DW_OP_const4s: |
| if (operand1 & 0x80000000) |
| operand1 |= (((unw_word_t) -1) << 16) << 16; |
| push (operand1); |
| break; |
| |
| case DW_OP_deref: |
| tmp1 = pop (); |
| if ((ret = dwarf_readw (as, a, &tmp1, &tmp2, arg)) < 0) |
| return ret; |
| push (tmp2); |
| break; |
| |
| case DW_OP_deref_size: |
| tmp1 = pop (); |
| switch (operand1) |
| { |
| case 0: |
| break; |
| |
| case 1: |
| if ((ret = dwarf_readu8 (as, a, &tmp1, &u8, arg)) < 0) |
| return ret; |
| tmp2 = u8; |
| break; |
| |
| case 2: |
| if ((ret = dwarf_readu16 (as, a, &tmp1, &u16, arg)) < 0) |
| return ret; |
| tmp2 = u16; |
| break; |
| |
| case 3: |
| case 4: |
| if ((ret = dwarf_readu32 (as, a, &tmp1, &u32, arg)) < 0) |
| return ret; |
| tmp2 = u32; |
| if (operand1 == 3) |
| { |
| if (dwarf_is_big_endian (as)) |
| tmp2 >>= 8; |
| else |
| tmp2 &= 0xffffff; |
| } |
| break; |
| case 5: |
| case 6: |
| case 7: |
| case 8: |
| if ((ret = dwarf_readu64 (as, a, &tmp1, &u64, arg)) < 0) |
| return ret; |
| tmp2 = u16; |
| if (operand1 != 8) |
| { |
| if (dwarf_is_big_endian (as)) |
| tmp2 >>= 64 - 8 * operand1; |
| else |
| tmp2 &= (~ (unw_word_t) 0) << (8 * operand1); |
| } |
| break; |
| } |
| push (tmp2); |
| break; |
| |
| case DW_OP_dup: |
| push (pick (0)); |
| break; |
| |
| case DW_OP_drop: |
| pop (); |
| break; |
| |
| case DW_OP_pick: |
| push (pick (operand1)); |
| break; |
| |
| case DW_OP_over: |
| push (pick (1)); |
| break; |
| |
| case DW_OP_swap: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| push (tmp1); |
| push (tmp2); |
| break; |
| |
| case DW_OP_rot: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| tmp3 = pop (); |
| push (tmp1); |
| push (tmp3); |
| push (tmp2); |
| break; |
| |
| case DW_OP_abs: |
| tmp1 = pop (); |
| if (tmp1 & ((unw_word_t) 1 << (8 * sizeof (unw_word_t) - 1))) |
| tmp1 = -tmp1; |
| push (tmp1); |
| break; |
| |
| case DW_OP_and: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| push (tmp1 & tmp2); |
| break; |
| |
| case DW_OP_div: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| if (tmp1) |
| tmp1 = sword (tmp2) / sword (tmp1); |
| push (tmp1); |
| break; |
| |
| case DW_OP_minus: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| tmp1 = tmp2 - tmp1; |
| push (tmp1); |
| break; |
| |
| case DW_OP_mod: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| if (tmp1) |
| tmp1 = tmp2 % tmp1; |
| push (tmp1); |
| break; |
| |
| case DW_OP_mul: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| if (tmp1) |
| tmp1 = tmp2 * tmp1; |
| push (tmp1); |
| break; |
| |
| case DW_OP_neg: |
| push (-pop ()); |
| break; |
| |
| case DW_OP_not: |
| push (~pop ()); |
| break; |
| |
| case DW_OP_or: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| push (tmp1 | tmp2); |
| break; |
| |
| case DW_OP_plus: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| push (tmp1 + tmp2); |
| break; |
| |
| case DW_OP_plus_uconst: |
| tmp1 = pop (); |
| push (tmp1 + operand1); |
| break; |
| |
| case DW_OP_shl: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| push (tmp2 << tmp1); |
| break; |
| |
| case DW_OP_shr: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| push (tmp2 >> tmp1); |
| break; |
| |
| case DW_OP_shra: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| push (sword (tmp2) >> tmp1); |
| break; |
| |
| case DW_OP_xor: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| push (tmp1 ^ tmp2); |
| break; |
| |
| case DW_OP_le: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| push (sword (tmp1) <= sword (tmp2)); |
| break; |
| |
| case DW_OP_ge: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| push (sword (tmp1) >= sword (tmp2)); |
| break; |
| |
| case DW_OP_eq: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| push (sword (tmp1) == sword (tmp2)); |
| break; |
| |
| case DW_OP_lt: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| push (sword (tmp1) < sword (tmp2)); |
| break; |
| |
| case DW_OP_gt: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| push (sword (tmp1) > sword (tmp2)); |
| break; |
| |
| case DW_OP_ne: |
| tmp1 = pop (); |
| tmp2 = pop (); |
| push (sword (tmp1) != sword (tmp2)); |
| break; |
| |
| case DW_OP_skip: |
| *addr += (int16_t) operand1; |
| break; |
| |
| case DW_OP_bra: |
| tmp1 = pop (); |
| if (tmp1) |
| *addr += (int16_t) operand1; |
| break; |
| |
| case DW_OP_nop: |
| break; |
| |
| case DW_OP_call2: |
| case DW_OP_call4: |
| case DW_OP_call_ref: |
| case DW_OP_fbreg: |
| case DW_OP_piece: |
| case DW_OP_push_object_address: |
| case DW_OP_xderef: |
| case DW_OP_xderef_size: |
| default: |
| Debug (1, "Unexpected opcode 0x%x\n", opcode); |
| return -UNW_EINVAL; |
| } |
| } |
| *valp = pop (); |
| return 0; |
| } |