From eb51b4294aa18ab13c9496378abb25162688235b Mon Sep 17 00:00:00 2001 From: "hp.com!davidm" Date: Sat, 20 Dec 2003 11:43:08 +0000 Subject: [PATCH] (Logical change 1.145) --- include/dwarf.h | 630 ++++++++++++++++++++++++++++++++ include/x86/dwarf-config.h | 59 +++ src/dwarf/Gexpr-dwarf.c | 578 +++++++++++++++++++++++++++++ src/dwarf/Gfde-dwarf.c | 287 +++++++++++++++ src/dwarf/Gfind_proc_info-lsb.c | 373 +++++++++++++++++++ src/dwarf/Gparser-dwarf.c | 610 +++++++++++++++++++++++++++++++ src/dwarf/Gpe-dwarf.c | 162 ++++++++ src/dwarf/Gstep-dwarf.c | 135 +++++++ src/dwarf/Lexpr-dwarf.c | 5 + src/dwarf/Lfde-dwarf.c | 5 + src/dwarf/Lfind_proc_info-lsb.c | 5 + src/dwarf/Lparser-dwarf.c | 5 + src/dwarf/Lpe-dwarf.c | 5 + src/dwarf/Lstep-dwarf.c | 5 + src/dwarf/dwarf-eh.h | 128 +++++++ src/dwarf/global-dwarf.c | 35 ++ 16 files changed, 3027 insertions(+) diff --git a/include/dwarf.h b/include/dwarf.h index e69de29b..938f2a2f 100644 --- a/include/dwarf.h +++ b/include/dwarf.h @@ -0,0 +1,630 @@ +/* libunwind - a platform-independent unwind library + Copyright (c) 2003 Hewlett-Packard Development Company, L.P. + Contributed by David Mosberger-Tang + +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. */ + +#ifndef dwarf_h +#define dwarf_h + +#include "internal.h" +#include "mempool.h" + +struct dwarf_cursor; /* forward-declaration */ + +#include "dwarf-config.h" + +/* DWARF expression opcodes. */ + +typedef enum + { + DW_OP_addr = 0x03, + DW_OP_deref = 0x06, + DW_OP_const1u = 0x08, + DW_OP_const1s = 0x09, + DW_OP_const2u = 0x0a, + DW_OP_const2s = 0x0b, + DW_OP_const4u = 0x0c, + DW_OP_const4s = 0x0d, + DW_OP_const8u = 0x0e, + DW_OP_const8s = 0x0f, + DW_OP_constu = 0x10, + DW_OP_consts = 0x11, + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, + DW_OP_swap = 0x16, + DW_OP_rot = 0x17, + DW_OP_xderef = 0x18, + DW_OP_abs = 0x19, + DW_OP_and = 0x1a, + DW_OP_div = 0x1b, + DW_OP_minus = 0x1c, + DW_OP_mod = 0x1d, + DW_OP_mul = 0x1e, + DW_OP_neg = 0x1f, + DW_OP_not = 0x20, + DW_OP_or = 0x21, + DW_OP_plus = 0x22, + DW_OP_plus_uconst = 0x23, + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_skip = 0x2f, + DW_OP_bra = 0x28, + DW_OP_eq = 0x29, + DW_OP_ge = 0x2a, + DW_OP_gt = 0x2b, + DW_OP_le = 0x2c, + DW_OP_lt = 0x2d, + DW_OP_ne = 0x2e, + DW_OP_lit0 = 0x30, + DW_OP_lit1, DW_OP_lit2, DW_OP_lit3, DW_OP_lit4, DW_OP_lit5, + DW_OP_lit6, DW_OP_lit7, DW_OP_lit8, DW_OP_lit9, DW_OP_lit10, + DW_OP_lit11, DW_OP_lit12, DW_OP_lit13, DW_OP_lit14, DW_OP_lit15, + DW_OP_lit16, DW_OP_lit17, DW_OP_lit18, DW_OP_lit19, DW_OP_lit20, + DW_OP_lit21, DW_OP_lit22, DW_OP_lit23, DW_OP_lit24, DW_OP_lit25, + DW_OP_lit26, DW_OP_lit27, DW_OP_lit28, DW_OP_lit29, DW_OP_lit30, + DW_OP_lit31, + DW_OP_reg0 = 0x50, + DW_OP_reg1, DW_OP_reg2, DW_OP_reg3, DW_OP_reg4, DW_OP_reg5, + DW_OP_reg6, DW_OP_reg7, DW_OP_reg8, DW_OP_reg9, DW_OP_reg10, + DW_OP_reg11, DW_OP_reg12, DW_OP_reg13, DW_OP_reg14, DW_OP_reg15, + DW_OP_reg16, DW_OP_reg17, DW_OP_reg18, DW_OP_reg19, DW_OP_reg20, + DW_OP_reg21, DW_OP_reg22, DW_OP_reg23, DW_OP_reg24, DW_OP_reg25, + DW_OP_reg26, DW_OP_reg27, DW_OP_reg28, DW_OP_reg29, DW_OP_reg30, + DW_OP_reg31, + DW_OP_breg0 = 0x70, + DW_OP_breg1, DW_OP_breg2, DW_OP_breg3, DW_OP_breg4, DW_OP_breg5, + DW_OP_breg6, DW_OP_breg7, DW_OP_breg8, DW_OP_breg9, DW_OP_breg10, + DW_OP_breg11, DW_OP_breg12, DW_OP_breg13, DW_OP_breg14, DW_OP_breg15, + DW_OP_breg16, DW_OP_breg17, DW_OP_breg18, DW_OP_breg19, DW_OP_breg20, + DW_OP_breg21, DW_OP_breg22, DW_OP_breg23, DW_OP_breg24, DW_OP_breg25, + DW_OP_breg26, DW_OP_breg27, DW_OP_breg28, DW_OP_breg29, DW_OP_breg30, + DW_OP_breg31, + DW_OP_regx = 0x90, + DW_OP_fbreg = 0x91, + DW_OP_bregx = 0x92, + DW_OP_piece = 0x93, + DW_OP_deref_size = 0x94, + DW_OP_xderef_size = 0x95, + DW_OP_nop = 0x96, + DW_OP_push_object_address = 0x97, + DW_OP_call2 = 0x98, + DW_OP_call4 = 0x99, + DW_OP_call_ref = 0x9a, + DW_OP_lo_user = 0xe0, + DW_OP_hi_user = 0xff + } +dwarf_expr_op_t; + +#define DWARF_CIE_VERSION 3 /* GCC emits version 1??? */ + +#define DWARF_CFA_OPCODE_MASK 0xc0 +#define DWARF_CFA_OPERAND_MASK 0x3f + +typedef enum + { + DW_CFA_advance_loc = 0x40, + DW_CFA_offset = 0x80, + DW_CFA_restore = 0xc0, + DW_CFA_nop = 0x00, + DW_CFA_set_loc = 0x01, + DW_CFA_advance_loc1 = 0x02, + DW_CFA_advance_loc2 = 0x03, + DW_CFA_advance_loc4 = 0x04, + DW_CFA_offset_extended = 0x05, + DW_CFA_restore_extended = 0x06, + DW_CFA_undefined = 0x07, + DW_CFA_same_value = 0x08, + DW_CFA_register = 0x09, + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, + DW_CFA_def_cfa_register = 0x0d, + DW_CFA_def_cfa_offset = 0x0e, + DW_CFA_def_cfa_expression = 0x0f, + DW_CFA_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_lo_user = 0x1c, + DW_CFA_MIPS_advance_loc8 = 0x1d, + DW_CFA_GNU_window_save = 0x2d, + DW_CFA_GNU_args_size = 0x2e, + DW_CFA_GNU_negative_offset_extended = 0x2f, + DW_CFA_hi_user = 0x3c + } +dwarf_cfa_t; + +/* DWARF Pointer-Encoding (PEs). + + Pointer-Encodings were invented for the GCC exception-handling + support for C++, but they represent a rather generic way of + describing the format in which an address/pointer is stored and + hence we include the definitions here, in the main dwarf.h file. + The Pointer-Encoding format is partially documented in Linux Base + Spec v1.3 (http://www.linuxbase.org/spec/). The rest is reverse + engineered from GCC. + +*/ +#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ +#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ +/* Flag bit. If set, the resulting pointer is the address of the word + that contains the final address. */ +#define DW_EH_PE_indirect 0x80 + +/* Pointer-encoding formats: */ +#define DW_EH_PE_omit 0xff +#define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */ +#define DW_EH_PE_uleb128 0x01 /* unsigned LE base-128 value */ +#define DW_EH_PE_udata2 0x02 /* unsigned 16-bit value */ +#define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */ +#define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */ +#define DW_EH_PE_sleb128 0x09 /* signed LE base-128 value */ +#define DW_EH_PE_sdata2 0x0a /* signed 16-bit value */ +#define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */ +#define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */ + +/* Pointer-encoding application: */ +#define DW_EH_PE_absptr 0x00 /* absolute value */ +#define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */ +#define DW_EH_PE_textrel 0x20 /* text-relative (GCC-specific???) */ +#define DW_EH_PE_datarel 0x30 /* data-relative */ +/* The following are not documented by LSB v1.3, yet they are used by + GCC, presumably they aren't documented by LSB since they aren't + used on Linux: */ +#define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */ +#define DW_EH_PE_aligned 0x50 /* aligned pointer */ + +extern HIDDEN struct mempool dwarf_reg_state_pool; + +#ifdef UNW_LOCAL_ONLY + +/* In the local-only case, we can let the compiler directly access + memory and don't need to worry about differing byte-order. */ + +typedef union + { + int8_t s8; + int16_t s16; + int32_t s32; + int64_t s64; + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + unw_word_t w; + void *ptr; + } +dwarf_misaligned_value_t __attribute__ ((packed)); + +static inline int +dwarf_reads8 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + int8_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = mvp->s8; + *addr += sizeof (mvp->s8); + return 0; +} + +static inline int +dwarf_reads16 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + int16_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = mvp->s16; + *addr += sizeof (mvp->s16); + return 0; +} + +static inline int +dwarf_reads32 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + int32_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = mvp->s32; + *addr += sizeof (mvp->s32); + return 0; +} + +static inline int +dwarf_reads64 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + int64_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = mvp->s64; + *addr += sizeof (mvp->s64); + return 0; +} + +static inline int +dwarf_readu8 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + uint8_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = mvp->u8; + *addr += sizeof (mvp->u8); + return 0; +} + +static inline int +dwarf_readu16 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + uint16_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = mvp->u16; + *addr += sizeof (mvp->u16); + return 0; +} + +static inline int +dwarf_readu32 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + uint32_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = mvp->u32; + *addr += sizeof (mvp->u32); + return 0; +} + +static inline int +dwarf_readu64 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + uint64_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = mvp->u64; + *addr += sizeof (mvp->u64); + return 0; +} + +static inline int +dwarf_readw (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + unw_word_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = mvp->w; + *addr += sizeof (mvp->w); + return 0; +} + +static inline int +dwarf_readptr (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + unw_word_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = (unw_word_t) mvp->ptr; + *addr += sizeof (mvp->ptr); + return 0; +} + +#else /* !UNW_LOCAL_ONLY */ + +/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + + FIX ME + + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + +typedef union + { + int8_t s8; + int16_t s16; + int32_t s32; + int64_t s64; + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + unw_word_t w; + void *ptr; + } +dwarf_misaligned_value_t __attribute__ ((packed)); + +static inline int +dwarf_reads8 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + int8_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = mvp->s8; + *addr += sizeof (mvp->s8); + return 0; +} + +static inline int +dwarf_reads16 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + int16_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = mvp->s16; + *addr += sizeof (mvp->s16); + return 0; +} + +static inline int +dwarf_reads32 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + int32_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = mvp->s32; + *addr += sizeof (mvp->s32); + return 0; +} + +static inline int +dwarf_reads64 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + int64_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = mvp->s64; + *addr += sizeof (mvp->s64); + return 0; +} + +static inline int +dwarf_readu8 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + uint8_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = mvp->u8; + *addr += sizeof (mvp->u8); + return 0; +} + +static inline int +dwarf_readu16 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + uint16_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = mvp->u16; + *addr += sizeof (mvp->u16); + return 0; +} + +static inline int +dwarf_readu32 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + uint32_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = mvp->u32; + *addr += sizeof (mvp->u32); + return 0; +} + +static inline int +dwarf_readu64 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + uint64_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = mvp->u64; + *addr += sizeof (mvp->u64); + return 0; +} + +static inline int +dwarf_readw (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + unw_word_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = mvp->w; + *addr += sizeof (mvp->w); + return 0; +} + +static inline int +dwarf_readptr (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + unw_word_t *val, void *arg) +{ + dwarf_misaligned_value_t *mvp = (void *) *addr; + + *val = (unw_word_t) mvp->ptr; + *addr += sizeof (mvp->ptr); + return 0; +} + +#endif /* !UNW_LOCAL_ONLY */ + +/* Read an unsigned "little-endian base 128" value. See Chapter 7.6 + of DWARF spec v3. */ + +static inline int +dwarf_read_uleb128 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + unw_word_t *valp, void *arg) +{ + unw_word_t val = 0, shift = 0; + unsigned char byte; + int ret; + + do + { + if ((ret = dwarf_readu8 (as, a, addr, &byte, arg)) < 0) + return ret; + + val |= ((unw_word_t) byte & 0x7f) << shift; + shift += 7; + } + while (byte & 0x80); + + *valp = val; + return 0; +} + +/* Read a signed "little-endian base 128" value. See Chapter 7.6 of + DWARF spec v3. */ + +static inline int +dwarf_read_sleb128 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + unw_word_t *valp, void *arg) +{ + unw_word_t val = 0, shift = 0; + unsigned char byte; + int ret; + + do + { + if ((ret = dwarf_readu8 (as, a, addr, &byte, arg)) < 0) + return ret; + + val |= ((unw_word_t) byte & 0x7f) << shift; + shift += 7; + } + while (byte & 0x80); + + if (shift < 8 * sizeof (unw_word_t) && (byte & 0x40) != 0) + /* sign-extend negative value */ + val |= ((unw_word_t) -1) << shift; + + *valp = val; + return 0; +} + +typedef enum + { + DWARF_WHERE_UNDEF, /* register isn't saved at all */ + DWARF_WHERE_SAME, /* register has same value as in prev. frame */ + DWARF_WHERE_CFAREL, /* register saved at CFA-relative address */ + DWARF_WHERE_REG, /* register saved in another register */ + DWARF_WHERE_EXPR, /* register saved */ + } +dwarf_where_t; + +typedef struct + { + dwarf_where_t where; /* how is the register saved? */ + unw_word_t val; /* where it's saved */ + } +dwarf_save_loc_t; + +/* For uniformity, we'd like to treat the CFA save-location like any + other register save-location, but this doesn't quite work, because + the CFA can be expressed as a (REGISTER,OFFSET) pair. To handle + this, we use two dwarf_save_loc structures to describe the CFA. + The first one (CFA_REG_COLUMN), tells us where the CFA is saved. + In the case of DWARF_WHERE_EXPR, the CFA is defined by a DWARF + location expression whose address is given by member "val". In the + case of DWARF_WHERE_REG, member "val" gives the number of the + base-register and the "val" member of DWARF_CFA_OFF_COLUMN gives + the offset value. */ +#define DWARF_CFA_REG_COLUMN DWARF_NUM_PRESERVED_REGS +#define DWARF_CFA_OFF_COLUMN (DWARF_NUM_PRESERVED_REGS + 1) + +typedef struct dwarf_reg_state + { + struct dwarf_reg_state *next; /* for rs_stack */ + dwarf_save_loc_t reg[DWARF_NUM_PRESERVED_REGS + 2]; + } +dwarf_reg_state_t; + +typedef struct dwarf_state_record + { + unsigned char fde_encoding; + unw_word_t args_size; + + dwarf_reg_state_t rs_initial; /* reg-state after CIE instructions */ + dwarf_reg_state_t rs_current; /* current reg-state */ + } +dwarf_state_record_t; + +typedef struct dwarf_cursor + { + void *as_arg; /* argument to address-space callbacks */ + unw_addr_space_t as; /* reference to per-address-space info */ + + unw_word_t cfa; /* canonical frame address; aka frame-/stack-pointer */ + unw_word_t ip; /* instruction pointer */ + unw_word_t args_size; /* size of arguments */ + unw_word_t ret_addr_column; /* column for return-address */ + + dwarf_loc_t loc[DWARF_NUM_PRESERVED_REGS]; + + unsigned int pi_valid :1; /* is proc_info valid? */ + unsigned int pi_is_dynamic :1; /* proc_info found via dynamic proc info? */ + unw_proc_info_t pi; /* info about current procedure */ + } +dwarf_cursor_t; + +/* Convenience macros: */ +#define dwarf_init UNW_ARCH_OBJ (dwarf_init) +#define dwarf_find_proc_info UNW_OBJ (dwarf_find_proc_info) +#define dwarf_search_unwind_table UNW_OBJ (dwarf_search_unwind_table) +#define dwarf_put_unwind_info UNW_OBJ (dwarf_put_unwind_info) +#define dwarf_put_unwind_info UNW_OBJ (dwarf_put_unwind_info) +#define dwarf_eval_expr UNW_OBJ (dwarf_eval_expr) +#define dwarf_parse_fde UNW_OBJ (dwarf_parse_fde) +#define dwarf_find_save_locs UNW_OBJ (dwarf_find_save_locs) +#define dwarf_create_state_record UNW_OBJ (dwarf_create_state_record) +#define dwarf_make_proc_info UNW_OBJ (dwarf_make_proc_info) +#define dwarf_read_encoded_pointer UNW_OBJ (dwarf_read_encoded_pointer) +#define dwarf_step UNW_OBJ (dwarf_step) + +extern HIDDEN int dwarf_init (void); +extern HIDDEN int dwarf_find_proc_info (unw_addr_space_t as, unw_word_t ip, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +extern HIDDEN int dwarf_search_unwind_table (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +extern HIDDEN void dwarf_put_unwind_info (unw_addr_space_t as, + unw_proc_info_t *pi, void *arg); +extern HIDDEN int dwarf_eval_expr (struct dwarf_cursor *c, unw_word_t *addr, + unw_word_t len, unw_word_t *valp, + int *is_register); +extern HIDDEN int dwarf_parse_fde (unw_addr_space_t as, unw_accessors_t *a, + unw_word_t *addr, unw_proc_info_t *pi, + unw_dyn_dwarf_fde_info_t *dfi, void *arg); +extern HIDDEN int dwarf_find_save_locs (struct dwarf_cursor *c); +extern HIDDEN int dwarf_create_state_record (struct dwarf_cursor *c, + dwarf_state_record_t *sr); +extern HIDDEN int dwarf_make_proc_info (struct dwarf_cursor *c); +extern HIDDEN int dwarf_read_encoded_pointer (unw_addr_space_t as, + unw_accessors_t *a, + unw_word_t *addr, + unsigned char encoding, + unw_proc_info_t *pi, + unw_word_t *valp, void *arg); +extern HIDDEN int dwarf_step (struct dwarf_cursor *c); + +#endif /* dwarf_h */ diff --git a/include/x86/dwarf-config.h b/include/x86/dwarf-config.h index e69de29b..5616445c 100644 --- a/include/x86/dwarf-config.h +++ b/include/x86/dwarf-config.h @@ -0,0 +1,59 @@ +/* libunwind - a platform-independent unwind library + Copyright (c) 2003 Hewlett-Packard Development Company, L.P. + Contributed by David Mosberger-Tang + +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. */ + +#ifndef dwarf_config_h +#define dwarf_config_h + +/* This matches the value used by GCC, which leaves plenty of room for + expansion. */ +#define DWARF_NUM_PRESERVED_REGS 17 + +/* Return TRUE if the ADDR_SPACE uses big-endian byte-order. */ +#define dwarf_is_big_endian(addr_space) 0 + +/* Convert a pointer to a dwarf_cursor structure to a pointer to + unw_cursor_t. */ +#define dwarf_to_cursor(c) ((unw_cursor_t *) (c)) + +HIDDEN extern uint8_t dwarf_to_unw_regnum_map[19]; + +static inline unw_regnum_t +dwarf_to_unw_regnum (unw_word_t regnum) +{ + if (regnum <= NELEMS (dwarf_to_unw_regnum_map)) + return dwarf_to_unw_regnum_map[regnum]; + return 0; +} + +typedef struct dwarf_loc + { + unw_word_t val; +#ifndef UNW_LOCAL_ONLY + unw_word_t type; /* see X86_LOC_TYPE_* macros. */ +#endif + } +dwarf_loc_t; + +#endif /* dwarf_config_h */ diff --git a/src/dwarf/Gexpr-dwarf.c b/src/dwarf/Gexpr-dwarf.c index e69de29b..ef27cab4 100644 --- a/src/dwarf/Gexpr-dwarf.c +++ b/src/dwarf/Gexpr-dwarf.c @@ -0,0 +1,578 @@ +/* libunwind - a platform-independent unwind library + Copyright (c) 2003 Hewlett-Packard Development Company, L.P. + Contributed by David Mosberger-Tang + +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.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; +} diff --git a/src/dwarf/Gfde-dwarf.c b/src/dwarf/Gfde-dwarf.c index e69de29b..957cc1ca 100644 --- a/src/dwarf/Gfde-dwarf.c +++ b/src/dwarf/Gfde-dwarf.c @@ -0,0 +1,287 @@ +/* libunwind - a platform-independent unwind library + Copyright (c) 2003 Hewlett-Packard Development Company, L.P. + Contributed by David Mosberger-Tang + +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 + +#include "dwarf.h" +#include "tdep.h" + +/* Note: we don't need to keep track of more than the first four + characters of the augmentation string, because we (a) ignore any + augmentation string contents once we find an unrecognized character + and (b) those characters that we do recognize, can't be + repeated. */ +static inline int +parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + unw_proc_info_t *pi, unw_dyn_dwarf_fde_info_t *dfi, + int *lsda_encodingp, void *arg) +{ + uint8_t version, ch, augstr[5], fde_encoding, handler_encoding; + unw_word_t len, cie_end_addr, aug_size, handler = 0; + uint32_t u32val; + uint64_t u64val; + size_t i; + int ret; +# define STR2(x) #x +# define STR(x) STR2(x) + + fde_encoding = DW_EH_PE_omit; + *lsda_encodingp = DW_EH_PE_omit; + dfi->flags = 0; + + if ((ret = dwarf_readu32 (as, a, addr, &u32val, arg)) < 0) + return ret; + + if (u32val != 0xffffffff) + { + /* the CIE is in the 32-bit DWARF format */ + uint32_t cie_id; + + len = u32val; + cie_end_addr = *addr + len; + if ((ret = dwarf_readu32 (as, a, addr, &cie_id, arg)) < 0) + return ret; + /* DWARF says CIE id should be 0xffffffff, but in .eh_frame, it's 0 */ + if (cie_id != 0) + { + Debug (1, "Unexpected CIE id %x\n", cie_id); + return -UNW_EINVAL; + } + } + else + { + /* the CIE is in the 64-bit DWARF format */ + uint64_t cie_id; + + if ((ret = dwarf_readu64 (as, a, addr, &u64val, arg)) < 0) + return ret; + len = u64val; + cie_end_addr = *addr + len; + if ((ret = dwarf_readu64 (as, a, addr, &cie_id, arg)) < 0) + return ret; + /* DWARF says CIE id should be 0xffffffffffffffff, but in + .eh_frame, it's 0 */ + if (cie_id != 0) + { + Debug (1, "Unexpected CIE id %llx\n", (long long) cie_id); + return -UNW_EINVAL; + } + } + dfi->cie_instr_end = cie_end_addr; + + if ((ret = dwarf_readu8 (as, a, addr, &version, arg)) < 0) + return ret; + + if (version != 1 && version != DWARF_CIE_VERSION) + { + Debug (1, "Got CIE version %u, expected version 1 or " + STR (DWARF_CIE_VERSION) "\n", version); + return -UNW_EBADVERSION; + } + + /* read and parse the augmentation string: */ + memset (augstr, 0, sizeof (augstr)); + for (i = 0;;) + { + if ((ret = dwarf_readu8 (as, a, addr, &ch, arg)) < 0) + return ret; + + if (!ch) + break; /* end of augmentation string */ + + if (i < sizeof (augstr) - 1) + augstr[i++] = ch; + } + + if ((ret = dwarf_read_uleb128 (as, a, addr, &dfi->code_align, arg)) < 0 + || (ret = dwarf_read_sleb128 (as, a, addr, &dfi->data_align, arg)) < 0) + return ret; + + /* Read the return-address column either as a u8 or as a uleb128. */ + if (version == 1) + { + if ((ret = dwarf_readu8 (as, a, addr, &ch, arg)) < 0) + return ret; + dfi->ret_addr_column = ch; + } + else if ((ret = dwarf_read_uleb128 (as, a, addr, &dfi->ret_addr_column, arg)) + < 0) + return ret; + + if (augstr[0] == 'z') + { + dfi->flags |= UNW_DYN_DFI_FLAG_AUGMENTATION_HAS_SIZE; + if ((ret = dwarf_read_uleb128 (as, a, addr, &aug_size, arg)) + < 0) + return ret; + } + + for (i = 1; i < sizeof (augstr) && augstr[i]; ++i) + switch (augstr[i]) + { + case 'L': + /* read the LSDA pointer-encoding format. */ + if ((ret = dwarf_readu8 (as, a, addr, &ch, arg)) < 0) + return ret; + *lsda_encodingp = ch; + break; + + case 'R': + /* read the FDE pointer-encoding format. */ + if ((ret = dwarf_readu8 (as, a, addr, &fde_encoding, arg)) < 0) + return ret; + break; + + case 'P': + /* read the personality-routine pointer-encoding format. */ + if ((ret = dwarf_readu8 (as, a, addr, &handler_encoding, arg)) < 0) + return ret; + if ((ret = dwarf_read_encoded_pointer (as, a, addr, handler_encoding, + pi, &handler, arg)) < 0) + return ret; + break; + + default: + if (dfi->flags & UNW_DYN_DFI_FLAG_AUGMENTATION_HAS_SIZE) + /* If we have the size of the augmentation body, we can skip + over the parts that we don't understand, so we're OK. */ + return 0; + else + { + Debug (1, "Unexpected augmentation string `%s'\n", augstr); + return -UNW_EINVAL; + } + } + dfi->flags |= fde_encoding & UNW_DYN_DFI_FLAG_FDE_PE_MASK; + pi->handler = handler; + Debug (16, "CIE parsed OK, augmentation = \"%s\", handler=0x%lx\n", + augstr, (long) handler); + dfi->cie_instr_start = *addr; + return 0; +} + +HIDDEN int +dwarf_parse_fde (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + unw_proc_info_t *pi, unw_dyn_dwarf_fde_info_t *dfi, void *arg) +{ + unw_word_t fde_end_addr, cie_addr, cie_offset_addr, aug_end_addr = 0; + int ret, fde_encoding, ip_range_encoding, lsda_encoding; + unw_word_t start_ip, ip_range, aug_size; + uint64_t u64val; + uint32_t u32val; + + Debug (12, "FDE @ 0x%lx\n", (long) *addr); + + /* Parse enough of the FDE to get the procedure info (LSDA and handler). */ + + if ((ret = dwarf_readu32 (as, a, addr, &u32val, arg)) < 0) + return ret; + + if (u32val != 0xffffffff) + { + uint32_t cie_offset; + + /* the FDE is in the 32-bit DWARF format */ + + fde_end_addr = *addr + u32val; + cie_offset_addr = *addr; + + if ((ret = dwarf_reads32 (as, a, addr, &cie_offset, arg)) < 0) + return ret; + + /* DWARF says that the CIE_pointer in the FDE is a + .debug_frame-relative offset, but the GCC-generated .eh_frame + sections instead store a "pcrelative" offset, which is just + as fine as it's self-contained. */ + cie_addr = cie_offset_addr - cie_offset; + } + else + { + uint64_t cie_offset; + + /* the FDE is in the 64-bit DWARF format */ + + if ((ret = dwarf_readu64 (as, a, addr, &u64val, arg)) < 0) + return ret; + + fde_end_addr = *addr + u64val; + cie_offset_addr = *addr; + + if ((ret = dwarf_reads64 (as, a, addr, &cie_offset, arg)) < 0) + return ret; + + /* DWARF says that the CIE_pointer in the FDE is a + .debug_frame-relative offset, but the GCC-generated .eh_frame + sections instead store a "pcrelative" offset, which is just + as fine as it's self-contained. */ + cie_addr = (unw_word_t) ((uint64_t) cie_offset_addr - cie_offset); + } + pi->extra.dwarf_info.fde_instr_end = fde_end_addr; + + if ((ret = parse_cie (as, a, &cie_addr, pi, &pi->extra.dwarf_info, + &lsda_encoding, arg)) < 0) + return ret; + + fde_encoding = pi->extra.dwarf_info.flags & UNW_DYN_DFI_FLAG_FDE_PE_MASK; + /* IP-range has same encoding as FDE pointers, except that it's + always an absolute value: */ + ip_range_encoding = fde_encoding & DW_EH_PE_FORMAT_MASK; + + if ((ret = dwarf_read_encoded_pointer (as, a, addr, fde_encoding, + pi, &start_ip, arg)) < 0 + || (ret = dwarf_read_encoded_pointer (as, a, addr, ip_range_encoding, + pi, &ip_range, arg)) < 0) + return ret; + pi->start_ip = start_ip; + pi->end_ip = start_ip + ip_range; + + if (pi->extra.dwarf_info.flags & UNW_DYN_DFI_FLAG_AUGMENTATION_HAS_SIZE) + { + if ((ret = dwarf_read_uleb128 (as, a, addr, &aug_size, arg)) + < 0) + return ret; + aug_end_addr = *addr + aug_size; + } + + if ((ret = dwarf_read_encoded_pointer (as, a, addr, lsda_encoding, + pi, &pi->lsda, arg)) < 0) + return ret; + + if (pi->extra.dwarf_info.flags & UNW_DYN_DFI_FLAG_AUGMENTATION_HAS_SIZE) + pi->extra.dwarf_info.fde_instr_start = aug_end_addr; + else + pi->extra.dwarf_info.fde_instr_start = *addr; + + /* Always set the unwind info, whether or not need_unwind_info is + set. We had to do all the work anyhow, so there is no point in + not doing so. */ + pi->format = UNW_INFO_FORMAT_DWARF_FDE; + pi->unwind_info_size = sizeof (pi->extra.dwarf_info) / sizeof (unw_word_t); + pi->unwind_info = &pi->extra.dwarf_info; + + Debug (16, "FDE covers IP 0x%lx-0x%lx, LSDA=0x%lx\n", + (long) pi->start_ip, (long) pi->end_ip, (long) pi->lsda); + return 0; +} diff --git a/src/dwarf/Gfind_proc_info-lsb.c b/src/dwarf/Gfind_proc_info-lsb.c index e69de29b..df160e44 100644 --- a/src/dwarf/Gfind_proc_info-lsb.c +++ b/src/dwarf/Gfind_proc_info-lsb.c @@ -0,0 +1,373 @@ +/* libunwind - a platform-independent unwind library + Copyright (c) 2003 Hewlett-Packard Development Company, L.P. + Contributed by David Mosberger-Tang + +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. */ + +/* Locate an FDE via the ELF data-structures defined by LSB v1.3 + (http://www.linuxbase.org/spec/). */ + +#include +#include +#include +#include + +#include "dwarf-eh.h" +#include "tdep.h" + +#ifndef UNW_REMOTE_ONLY + +/* Info is a pointer to a unw_dyn_info_t structure and, on entry, + member u.rti.segbase contains the instruction-pointer we're looking + for. */ +static int +callback (struct dl_phdr_info *info, size_t size, void *ptr) +{ + unw_dyn_info_t *di = ptr; + const Elf_W(Phdr) *phdr, *p_eh_hdr, *p_dynamic, *p_text; + unw_word_t addr, eh_frame_ptr, fde_count; + Elf_W(Addr) load_base, segbase = 0; + struct dwarf_eh_frame_hdr *hdr; + unw_proc_info_t pi; + unw_accessors_t *a; + int ret; + long n; + + /* Make sure struct dl_phdr_info is at least as big as we need. */ + if (size < offsetof (struct dl_phdr_info, dlpi_phnum) + + sizeof (info->dlpi_phnum)) + return -1; + + Debug (16, "checking %s, base=0x%lx)\n", + info->dlpi_name, (long) info->dlpi_addr); + + phdr = info->dlpi_phdr; + load_base = info->dlpi_addr; + p_text = NULL; + p_eh_hdr = NULL; + p_dynamic = NULL; + + /* See if PC falls into one of the loaded segments. Find the + eh-header segment at the same time. */ + for (n = info->dlpi_phnum; --n >= 0; phdr++) + { + if (phdr->p_type == PT_LOAD) + { + Elf_W(Addr) vaddr = phdr->p_vaddr + load_base; + if (di->u.rti.segbase >= vaddr + && di->u.rti.segbase < vaddr + phdr->p_memsz) + p_text = phdr; + } + else if (phdr->p_type == PT_GNU_EH_FRAME) + p_eh_hdr = phdr; + else if (phdr->p_type == PT_DYNAMIC) + p_dynamic = phdr; + } + if (!p_text || !p_eh_hdr) + return 0; + + if (likely (p_eh_hdr->p_vaddr >= p_text->p_vaddr + && p_eh_hdr->p_vaddr < p_text->p_vaddr + p_text->p_memsz)) + /* normal case: eh-hdr is inside text segment */ + segbase = p_text->p_vaddr + load_base; + else + { + /* Special case: eh-hdr is in some other segment; this may + happen, e.g., for the Linux kernel's gate DSO, for + example. */ + phdr = info->dlpi_phdr; + for (n = info->dlpi_phnum; --n >= 0; phdr++) + { + if (phdr->p_type == PT_LOAD && p_eh_hdr->p_vaddr >= phdr->p_vaddr + && p_eh_hdr->p_vaddr < phdr->p_vaddr + phdr->p_memsz) + { + segbase = phdr->p_vaddr + load_base; + break; + } + } + } + + if (p_dynamic) + { + /* For dynamicly linked executables and shared libraries, + DT_PLTGOT is the value that data-relative addresses are + relative to for that object. We call this the "gp". */ + Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(p_dynamic->p_vaddr + load_base); + for (; dyn->d_tag != DT_NULL; ++dyn) + if (dyn->d_tag == DT_PLTGOT) + { + /* Assume that _DYNAMIC is writable and GLIBC has + relocated it (true for x86 at least). */ + di->gp = dyn->d_un.d_ptr; + break; + } + } + else + /* Otherwise this is a static executable with no _DYNAMIC. Assume + that data-relative addresses are relative to 0, i.e., + absolute. */ + di->gp = 0; + + hdr = (struct dwarf_eh_frame_hdr *) (p_eh_hdr->p_vaddr + load_base); + if (hdr->version != DW_EH_VERSION) + { + Debug (1, "table `%s' has unexpected version %d\n", + info->dlpi_name, hdr->version); + return 0; + } + + if (hdr->table_enc == DW_EH_PE_omit) + { + Debug (1, "table `%s' doesn't have a binary search table\n", + info->dlpi_name); + return 0; + } + /* For now, only support binary-search tables which are + data-relative and whose entries have the size of a pointer. */ + if (!(hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_ptr) + || ((sizeof (unw_word_t) == 4 + && hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4))) + || ((sizeof (unw_word_t) == 8 + && hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata8))))) + { + Debug (1, "search table in `%s' has unexpected encoding 0x%x\n", + info->dlpi_name, hdr->table_enc); + return 0; + } + + addr = (unw_word_t) (hdr + 1); + a = unw_get_accessors (unw_local_addr_space); + pi.gp = di->gp; + + /* Read eh_frame_ptr: */ + if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a, + &addr, hdr->eh_frame_ptr_enc, &pi, + &eh_frame_ptr, NULL)) < 0) + return ret; + + /* Read fde_count: */ + if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a, + &addr, hdr->fde_count_enc, &pi, + &fde_count, NULL)) < 0) + return ret; + + di->format = UNW_INFO_FORMAT_REMOTE_TABLE; + di->start_ip = p_text->p_vaddr + load_base; + di->end_ip = p_text->p_vaddr + load_base + p_text->p_memsz; + di->u.rti.name_ptr = (unw_word_t) info->dlpi_name; + di->u.rti.table_data = addr; + di->u.rti.table_len = fde_count + 2 * sizeof (unw_word_t); + /* For the binary-search table in the eh_frame_hdr, data-relative + means relative to the start of that section... */ + di->u.rti.segbase = (unw_word_t) hdr; + + Debug (15, "found table `%s': segbase=0x%lx, len=%lu, gp=0x%lx, " + "table_data=0x%lx\n", (char *) di->u.rti.name_ptr, + (long) di->u.rti.segbase, (long) di->u.rti.table_len, + (long) di->gp, (long) di->u.rti.table_data); + return 1; +} + +HIDDEN int +dwarf_find_proc_info (unw_addr_space_t as, unw_word_t ip, + unw_proc_info_t *pi, int need_unwind_info, void *arg) +{ + unw_dyn_info_t di; + + Debug (14, "looking for IP=0x%lx\n", (long) ip); + di.u.rti.segbase = ip; /* this is cheap... */ + + if (dl_iterate_phdr (callback, &di) <= 0) + { + Debug (14, "IP=0x%lx not found\n", (long) ip); + return -UNW_ENOINFO; + } + + /* now search the table: */ + return dwarf_search_unwind_table (as, ip, &di, pi, need_unwind_info, arg); +} + +struct table_entry + { + unw_word_t start_ip_offset; + unw_word_t fde_offset; + }; + +static inline const struct table_entry * +lookup (struct table_entry *table, size_t table_size, unw_word_t rel_ip) +{ + unsigned long table_len = table_size / sizeof (struct table_entry); + const struct table_entry *e = 0; + unsigned long lo, hi, mid; + unw_word_t end = 0; + + /* do a binary search for right entry: */ + for (lo = 0, hi = table_len; lo < hi;) + { + mid = (lo + hi) / 2; + e = table + mid; + if (rel_ip < e->start_ip_offset) + hi = mid; + else + { + if (mid + 1 >= table_len) + break; + + end = table[mid + 1].start_ip_offset; + + if (rel_ip >= end) + lo = mid + 1; + else + break; + } + } + if (rel_ip < e->start_ip_offset || rel_ip >= end) + return NULL; + return e; +} + +#endif /* !UNW_REMOTE_ONLY */ + +/* Helper macro for reading an table_entry from remote memory. */ +#define remote_read(addr, member) \ + (*a->access_mem) (as, (addr) + offsetof (struct table_entry, \ + member), &member, 0, arg) + +#ifndef UNW_LOCAL_ONLY + +/* Lookup an unwind-table entry in remote memory. Returns 1 if an + entry is found, 0 if no entry is found, negative if an error + occurred reading remote memory. */ +static int +remote_lookup (unw_addr_space_t as, + unw_word_t table, size_t table_size, unw_word_t rel_ip, + struct table_entry *e, void *arg) +{ + unsigned long table_len = table_size / sizeof (struct table_entry); + unw_word_t e_addr = 0, start_ip_offset, fde_offset; + unw_word_t start = ~(unw_word_t) 0, end = 0; + unw_accessors_t *a = unw_get_accessors (as); + unsigned long lo, hi, mid; + int ret; + + /* do a binary search for right entry: */ + for (lo = 0, hi = table_len; lo < hi;) + { + mid = (lo + hi) / 2; + e_addr = table + mid * sizeof (struct table_entry); + if ((ret = remote_read (e_addr, start_ip_offset)) < 0) + return ret; + + start = start_ip_offset; + if (rel_ip < start) + hi = mid; + else + { + if (mid + 1 >= table_len) + break; + + if ((ret = remote_read (e_addr + sizeof (struct table_entry), + start_ip_offset)) < 0) + return ret; + end = start_ip_offset; + + if (rel_ip >= end) + lo = mid + 1; + else + break; + } + } + if (rel_ip < start || rel_ip >= end) + return 0; + e->start_ip_offset = start; + + if ((ret = remote_read (e_addr, fde_offset)) < 0) + return ret; + e->fde_offset = fde_offset; + return 1; +} + +#endif /* UNW_LOCAL_ONLY */ + +HIDDEN int +dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, + unw_dyn_info_t *di, unw_proc_info_t *pi, + int need_unwind_info, void *arg) +{ + const struct table_entry *e = NULL; + unw_word_t segbase = 0, fde_addr; + unw_accessors_t *a; +#ifndef UNW_LOCAL_ONLY + struct table_entry ent; + int ret; +#endif + + assert (di->format == UNW_INFO_FORMAT_REMOTE_TABLE + && (ip >= di->start_ip && ip < di->end_ip)); + + a = unw_get_accessors (as); + + pi->flags = 0; + pi->unwind_info = 0; + pi->handler = 0; + pi->gp = 0; + memset (&pi->extra, 0, sizeof (pi->extra)); + +#ifndef UNW_REMOTE_ONLY + if (as == unw_local_addr_space) + { + segbase = di->u.rti.segbase; + e = lookup ((struct table_entry *) di->u.rti.table_data, + di->u.rti.table_len * sizeof (unw_word_t), ip - segbase); + } + else +#endif + { +#ifndef UNW_LOCAL_ONLY + segbase = di->u.rti.segbase; + if ((ret = remote_lookup (as, di->u.rti.table_data, + di->u.rti.table_len * sizeof (unw_word_t), + ip - segbase, &ent, arg)) < 0) + return ret; + if (ret) + e = &ent; + else + e = NULL; /* no info found */ +#endif + } + if (!e) + { + /* IP is inside this table's range, but there is no explicit + unwind info. */ + return -UNW_ENOINFO; + } + Debug (16, "ip=0x%lx, start_ip=0x%lx\n", + (long) ip, (long) (e->start_ip_offset + segbase)); + fde_addr = e->fde_offset + segbase; + return dwarf_parse_fde (as, a, &fde_addr, pi, &pi->extra.dwarf_info, arg); +} + +HIDDEN void +dwarf_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *pi, void *arg) +{ + return; /* always a nop */ +} diff --git a/src/dwarf/Gparser-dwarf.c b/src/dwarf/Gparser-dwarf.c index e69de29b..b70443bb 100644 --- a/src/dwarf/Gparser-dwarf.c +++ b/src/dwarf/Gparser-dwarf.c @@ -0,0 +1,610 @@ +/* libunwind - a platform-independent unwind library + Copyright (c) 2003 Hewlett-Packard Development Company, L.P. + Contributed by David Mosberger-Tang + +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 + +#include "dwarf.h" +#include "tdep.h" + +#define alloc_reg_state() (mempool_alloc (&dwarf_reg_state_pool)) +#define free_reg_state(rs) (mempool_free (&dwarf_reg_state_pool, rs)) + +static inline int +read_regnum (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, + unw_word_t *valp, void *arg) +{ + int ret; + + if ((ret = dwarf_read_uleb128 (as, a, addr, valp, arg)) < 0) + return ret; + + if (*valp >= DWARF_NUM_PRESERVED_REGS) + { + Debug (1, "Invalid register number %u\n", *valp); + return -UNW_EBADREG; + } + return 0; +} + +static inline void +set_reg (dwarf_state_record_t *sr, unw_word_t regnum, dwarf_where_t where, + unw_word_t val) +{ + sr->rs_current.reg[regnum].where = where; + sr->rs_current.reg[regnum].val = val; +} + +/* Run a CFI program to update the register state. */ +static int +run_cfi_program (struct dwarf_cursor *c, dwarf_state_record_t *sr, + unw_word_t ip, unw_word_t *addr, unw_word_t end_addr, + unw_dyn_dwarf_fde_info_t *dfi) +{ + unw_word_t curr_ip, operand = 0, regnum, val, len, fde_encoding; + dwarf_reg_state_t *rs_stack = NULL, *new_rs, *old_rs; + unw_addr_space_t as; + unw_accessors_t *a; + uint8_t u8, op; + uint16_t u16; + uint32_t u32; + void *arg; + int ret; + + as = c->as; + arg = c->as_arg; + a = unw_get_accessors (as); + curr_ip = c->pi.start_ip; + + while (curr_ip < ip && *addr < end_addr) + { + if ((ret = dwarf_readu8 (as, a, addr, &op, arg)) < 0) + return ret; + + if (op & DWARF_CFA_OPCODE_MASK) + { + operand = op & DWARF_CFA_OPERAND_MASK; + op &= ~DWARF_CFA_OPERAND_MASK; + } + switch ((dwarf_cfa_t) op) + { + case DW_CFA_advance_loc: + curr_ip += operand * dfi->code_align; + Debug (16, "CFA_advance_loc to 0x%lx\n", (long) curr_ip); + break; + + case DW_CFA_advance_loc1: + if ((ret = dwarf_readu8 (as, a, addr, &u8, arg)) < 0) + goto fail; + curr_ip += u8 * dfi->code_align; + Debug (16, "CFA_advance_loc1 to 0x%lx\n", (long) curr_ip); + break; + + case DW_CFA_advance_loc2: + if ((ret = dwarf_readu16 (as, a, addr, &u16, arg)) < 0) + goto fail; + curr_ip += u16 * dfi->code_align; + Debug (16, "CFA_advance_loc2 to 0x%lx\n", (long) curr_ip); + break; + + case DW_CFA_advance_loc4: + if ((ret = dwarf_readu32 (as, a, addr, &u32, arg)) < 0) + goto fail; + curr_ip += u32 * dfi->code_align; + Debug (16, "CFA_advance_loc4 to 0x%lx\n", (long) curr_ip); + break; + + case DW_CFA_MIPS_advance_loc8: +#ifdef UNW_TARGET_MIPS + { + uint64_t u64; + + if ((ret = dwarf_readu64 (as, a, addr, &u64, arg)) < 0) + goto fail; + curr_ip += u64 * dfi->code_align; + Debug (16, "CFA_MIPS_advance_loc8\n"); + break; + } +#else + Debug (1, "DW_CFA_MIPS_advance_loc8 on non-MIPS target\n"); + ret = -UNW_EINVAL; + goto fail; +#endif + + case DW_CFA_offset: + regnum = operand; + if (regnum >= DWARF_NUM_PRESERVED_REGS) + { + Debug (1, "Invalid register number %u in DW_cfa_OFFSET\n", + regnum); + ret = -UNW_EBADREG; + goto fail; + } + if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0) + goto fail; + set_reg (sr, regnum, DWARF_WHERE_CFAREL, val * dfi->data_align); + Debug (16, "CFA_offset r%lu at cfa+0x%lx\n", + (long) regnum, (long) (val * dfi->data_align)); + break; + + case DW_CFA_offset_extended: + if (((ret = read_regnum (as, a, addr, ®num, arg)) < 0) + || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)) + goto fail; + set_reg (sr, regnum, DWARF_WHERE_CFAREL, val * dfi->data_align); + Debug (16, "CFA_offset_extended r%lu at cf+0x%lx\n", + (long) regnum, (long) (val * dfi->data_align)); + break; + + case DW_CFA_offset_extended_sf: + if (((ret = read_regnum (as, a, addr, ®num, arg)) < 0) + || ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0)) + goto fail; + set_reg (sr, regnum, DWARF_WHERE_CFAREL, val * dfi->data_align); + Debug (16, "CFA_offset_extended_sf r%lu at cf+0x%lx\n", + (long) regnum, (long) (val * dfi->data_align)); + break; + + case DW_CFA_restore: + regnum = operand; + if (regnum >= DWARF_NUM_PRESERVED_REGS) + { + Debug (1, "Invalid register number %u in DW_CFA_restore\n", + regnum); + ret = -UNW_EINVAL; + goto fail; + } + sr->rs_current.reg[regnum] = sr->rs_initial.reg[regnum]; + Debug (16, "CFA_restore r%lu\n", (long) regnum); + break; + + case DW_CFA_restore_extended: + if ((ret = dwarf_read_uleb128 (as, a, addr, ®num, arg)) < 0) + goto fail; + if (regnum >= DWARF_NUM_PRESERVED_REGS) + { + Debug (1, "Invalid register number %u in " + "DW_CFA_restore_extended\n", regnum); + ret = -UNW_EINVAL; + goto fail; + } + sr->rs_current.reg[regnum] = sr->rs_initial.reg[regnum]; + Debug (16, "CFA_restore_extended r%lu\n", (long) regnum); + break; + + case DW_CFA_nop: + break; + + case DW_CFA_set_loc: + fde_encoding = dfi->flags & UNW_DYN_DFI_FLAG_FDE_PE_MASK; + if ((ret = dwarf_read_encoded_pointer (as, a, addr, fde_encoding, + &c->pi, &curr_ip, + arg)) < 0) + goto fail; + Debug (16, "CFA_set_loc to 0x%lx\n", (long) curr_ip); + break; + + case DW_CFA_undefined: + if ((ret = read_regnum (as, a, addr, ®num, arg)) < 0) + goto fail; + set_reg (sr, regnum, DWARF_WHERE_UNDEF, 0); + Debug (16, "CFA_undefined r%lu\n", (long) regnum); + break; + + case DW_CFA_same_value: + if ((ret = read_regnum (as, a, addr, ®num, arg)) < 0) + goto fail; + set_reg (sr, regnum, DWARF_WHERE_SAME, 0); + Debug (16, "CFA_same_value r%lu\n", (long) regnum); + break; + + case DW_CFA_register: + if (((ret = read_regnum (as, a, addr, ®num, arg)) < 0) + || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)) + goto fail; + set_reg (sr, regnum, DWARF_WHERE_REG, val); + Debug (16, "CFA_register r%lu to r%lu\n", (long) regnum, (long) val); + break; + + case DW_CFA_remember_state: + new_rs = alloc_reg_state (); + if (!new_rs) + { + Debug (1, "Out of memory in DW_CFA_remember_state\n"); + ret = -UNW_ENOMEM; + goto fail; + } + + memcpy (new_rs->reg, sr->rs_current.reg, sizeof (new_rs->reg)); + new_rs->next = rs_stack; + rs_stack = new_rs; + Debug (16, "CFA_remember_state\n"); + break; + + case DW_CFA_restore_state: + if (!rs_stack) + { + Debug (1, "register-state stack underflow\n"); + ret = -UNW_EINVAL; + goto fail; + } + memcpy (&sr->rs_current.reg, &rs_stack->reg, sizeof (rs_stack->reg)); + old_rs = rs_stack; + rs_stack = rs_stack->next; + free_reg_state (old_rs); + Debug (16, "CFA_restore_state\n"); + break; + + case DW_CFA_def_cfa: + if (((ret = read_regnum (as, a, addr, ®num, arg)) < 0) + || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)) + goto fail; + set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum); + set_reg (sr, DWARF_CFA_OFF_COLUMN, 0, val); /* NOT factored! */ + Debug (16, "CFA_def_cfa r%lu+0x%lx\n", (long) regnum, (long) val); + break; + + case DW_CFA_def_cfa_sf: + if (((ret = read_regnum (as, a, addr, ®num, arg)) < 0) + || ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0)) + goto fail; + set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum); + set_reg (sr, DWARF_CFA_OFF_COLUMN, 0, + val * dfi->data_align); /* factored! */ + Debug (16, "CFA_def_cfa_sf r%lu+0x%lx\n", + (long) regnum, (long) (val * dfi->data_align)); + break; + + case DW_CFA_def_cfa_register: + if ((ret = read_regnum (as, a, addr, ®num, arg)) < 0) + goto fail; + set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum); + Debug (16, "CFA_def_cfa_register r%lu\n", (long) regnum); + break; + + case DW_CFA_def_cfa_offset: + if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0) + goto fail; + set_reg (sr, DWARF_CFA_OFF_COLUMN, 0, val); /* NOT factored! */ + Debug (16, "CFA_def_cfa_offsets 0x%lx\n", (long) val); + break; + + case DW_CFA_def_cfa_offset_sf: + if ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0) + goto fail; + set_reg (sr, DWARF_CFA_OFF_COLUMN, 0, + val * dfi->data_align); /* factored! */ + Debug (16, "CFA_def_cfa_offsets_sf 0x%lx\n", + (long) (val * dfi->data_align)); + break; + + case DW_CFA_def_cfa_expression: + /* Save the address of the DW_FORM_block for later evaluation. */ + set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_EXPR, *addr); + + if ((ret = dwarf_read_uleb128 (as, a, addr, &len, arg)) < 0) + goto fail; + + Debug (16, "CFA_def_cfa_expr @ 0x%lx [%lu bytes]\n", + (long) *addr, (long) len); + *addr += len; + break; + + case DW_CFA_CFA_expression: + if ((ret = read_regnum (as, a, addr, ®num, arg)) < 0) + goto fail; + + /* Save the address of the DW_FORM_block for later evaluation. */ + set_reg (sr, regnum, DWARF_WHERE_EXPR, *addr); + + if ((ret = dwarf_read_uleb128 (as, a, addr, &len, arg)) < 0) + goto fail; + + Debug (16, "CFA_expression r%lu @ 0x%lx [%lu bytes]\n", + (long) regnum, (long) addr, (long) len); + *addr += len; + break; + + case DW_CFA_GNU_args_size: + if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0) + goto fail; + sr->args_size = val; + Debug (16, "CFA_GNU_args_size %lu\n", (long) val); + break; + + case DW_CFA_GNU_negative_offset_extended: + /* A comment in GCC says that this is obsoleted by + DW_CFA_offset_extended_sf, but that it's used by older + PowerPC code. */ + if (((ret = read_regnum (as, a, addr, ®num, arg)) < 0) + || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)) + goto fail; + set_reg (sr, regnum, DWARF_WHERE_CFAREL, -(val * dfi->data_align)); + Debug (16, "CFA_GNU_negative_offsets_extended cfa+0x%lx\n", + (long) -(val * dfi->data_align)); + break; + + case DW_CFA_GNU_window_save: +#ifdef UNW_TARGET_SPARC + /* This is a special CFA to handle all 16 windowed registers + on SPARC. */ + for (regnum = 16; regnum < 32; ++regnum) + set_reg (sr, regnum, DWARF_WHERE_CFAREL, + (regnum - 16) * sizeof (unw_word_t)); + Debug (16, "CFA_GNU_window_save\n"); + break; +#else + /* FALL THROUGH */ +#endif + case DW_CFA_lo_user: + case DW_CFA_hi_user: + Debug (1, "Unexpected CFA opcode 0x%x", op); + ret = -UNW_EINVAL; + goto fail; + } + } + ret = 0; + + fail: + /* Free the register-state stack, if not empty already. */ + while (rs_stack) + { + old_rs = rs_stack; + rs_stack = rs_stack->next; + free_reg_state (old_rs); + } + return ret; +} + +static int +fetch_proc_info (struct dwarf_cursor *c, unw_word_t ip, int need_unwind_info) +{ + int ret, dynamic = 1; + + if (c->pi_valid && !need_unwind_info) + return 0; + + /* check dynamic info first --- it overrides everything else */ + ret = unwi_find_dynamic_proc_info (c->as, ip, &c->pi, need_unwind_info, + c->as_arg); + if (ret == -UNW_ENOINFO) + { + dynamic = 0; + if ((ret = dwarf_find_proc_info (c->as, ip, &c->pi, need_unwind_info, + c->as_arg)) < 0) + return ret; + } + + c->pi_valid = 1; + c->pi_is_dynamic = dynamic; + return ret; +} + +static int +parse_dynamic (struct dwarf_cursor *c, unw_word_t ip, dwarf_state_record_t *sr) +{ + Debug (1, "Not yet implemented\n"); +#if 0 + /* Don't forget to set the ret_addr_column! */ + c->ret_addr_column = XXX; +#endif + return -UNW_ENOINFO; +} + +static inline void +put_unwind_info (struct dwarf_cursor *c, unw_proc_info_t *pi) +{ + if (!c->pi_valid) + return; + + if (c->pi_is_dynamic) + unwi_put_dynamic_unwind_info (c->as, pi, c->as_arg); +} + +static inline int +parse_fde (struct dwarf_cursor *c, unw_word_t ip, dwarf_state_record_t *sr) +{ + unw_dyn_dwarf_fde_info_t *dfi; + unw_word_t addr; + int ret; + + dfi = c->pi.unwind_info; + c->ret_addr_column = dfi->ret_addr_column; + + addr = dfi->cie_instr_start; + if ((ret = run_cfi_program (c, sr, ~(unw_word_t) 0, &addr, + dfi->cie_instr_end, dfi)) < 0) + return ret; + + memcpy (&sr->rs_initial, &sr->rs_current, sizeof (sr->rs_initial)); + + addr = dfi->fde_instr_start; + if ((ret = run_cfi_program (c, sr, ip, &addr, dfi->fde_instr_end, dfi)) < 0) + return ret; + + return 0; +} + +static int +create_state_record_for (struct dwarf_cursor *c, dwarf_state_record_t *sr, + unw_word_t ip) +{ + int i, ret; + + assert (c->pi_valid); + + for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) + set_reg (sr, i, DWARF_WHERE_SAME, 0); + + switch (c->pi.format) + { + case UNW_INFO_FORMAT_DWARF_FDE: + ret = parse_fde (c, ip, sr); + break; + + case UNW_INFO_FORMAT_DYNAMIC: + ret = parse_dynamic (c, ip, sr); + break; + + case UNW_INFO_FORMAT_REMOTE_TABLE: + case UNW_INFO_FORMAT_TABLE: + default: + Debug (1, "Unexpected unwind-info format %d\n", c->pi.format); + ret = -UNW_EINVAL; + } + return ret; +} + +static inline int +eval_location_expr (struct dwarf_cursor *c, unw_addr_space_t as, + unw_accessors_t *a, unw_word_t addr, + dwarf_loc_t *locp, void *arg) +{ + int ret, is_register; + unw_word_t len, val; + + /* read the length of the expression: */ + if ((ret = dwarf_read_uleb128 (as, a, &addr, &len, arg)) < 0) + return ret; + + /* evaluate the expression: */ + if ((ret = dwarf_eval_expr (c, &addr, len, &val, &is_register)) < 0) + return ret; + + if (is_register) + *locp = DWARF_REG_LOC (c, dwarf_to_unw_regnum (val)); + else + *locp = DWARF_MEM_LOC (c, val); + + return 0; +} + +static int +apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs) +{ + unw_word_t regnum, addr, cfa; + unw_addr_space_t as; + dwarf_loc_t cfa_loc; + unw_accessors_t *a; + int i, ret; + void *arg; + + as = c->as; + arg = c->as_arg; + a = unw_get_accessors (as); + + /* Evaluate the CFA first, because it may be referred to be other + expressions. */ + + if (rs->reg[DWARF_CFA_REG_COLUMN].where == DWARF_WHERE_REG) + { + /* CFA is equal to [reg] + offset: */ + + regnum = dwarf_to_unw_regnum (rs->reg[DWARF_CFA_REG_COLUMN].val); + if ((ret = unw_get_reg ((unw_cursor_t *) c, regnum, &cfa)) < 0) + return ret; + + cfa += rs->reg[DWARF_CFA_OFF_COLUMN].val; + } + else + { + /* CFA is equal to EXPR: */ + + assert (rs->reg[DWARF_CFA_REG_COLUMN].where == DWARF_WHERE_EXPR); + + addr = rs->reg[DWARF_CFA_REG_COLUMN].val; + if ((ret = eval_location_expr (c, as, a, addr, &cfa_loc, arg)) < 0) + return ret; + if ((ret = dwarf_get (c, cfa_loc, &cfa)) < 0) + return ret; + } + c->cfa = cfa; + + for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) + { + switch ((dwarf_where_t) rs->reg[i].where) + { + case DWARF_WHERE_UNDEF: + c->loc[i] = DWARF_NULL_LOC; + break; + + case DWARF_WHERE_SAME: + break; + + case DWARF_WHERE_CFAREL: + c->loc[i] = DWARF_MEM_LOC (c, cfa + rs->reg[i].val); + break; + + case DWARF_WHERE_REG: + c->loc[i] = DWARF_REG_LOC (c, dwarf_to_unw_regnum (rs->reg[i].val)); + break; + + case DWARF_WHERE_EXPR: + addr = rs->reg[i].val; + if ((ret = eval_location_expr (c, as, a, addr, c->loc + i, arg)) , 0) + return ret; + break; + } + } + return 0; +} + +HIDDEN int +dwarf_find_save_locs (struct dwarf_cursor *c) +{ + dwarf_state_record_t sr; + int ret; + + if ((ret = fetch_proc_info (c, c->ip, 1)) < 0) + return ret; + + if ((ret = create_state_record_for (c, &sr, c->ip)) < 0) + return ret; + + if ((ret = apply_reg_state (c, &sr.rs_current)) < 0) + return ret; + + put_unwind_info (c, &c->pi); + return 0; +} + +/* The proc-info must be valid for IP before this routine can be + called. */ +HIDDEN int +dwarf_create_state_record (struct dwarf_cursor *c, dwarf_state_record_t *sr) +{ + return create_state_record_for (c, sr, c->ip); +} + +HIDDEN int +dwarf_make_proc_info (struct dwarf_cursor *c) +{ +#if 0 + if (c->as->caching_policy == UNW_CACHE_NONE + || get_cached_proc_info (c) < 0) +#endif + /* Lookup it up the slow way... */ + return fetch_proc_info (c, c->ip, 0); + return 0; +} diff --git a/src/dwarf/Gpe-dwarf.c b/src/dwarf/Gpe-dwarf.c index e69de29b..d1f271bb 100644 --- a/src/dwarf/Gpe-dwarf.c +++ b/src/dwarf/Gpe-dwarf.c @@ -0,0 +1,162 @@ +/* libunwind - a platform-independent unwind library + Copyright (c) 2003 Hewlett-Packard Development Company, L.P. + Contributed by David Mosberger-Tang + +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.h" +#include "tdep.h" + +HIDDEN int +dwarf_read_encoded_pointer (unw_addr_space_t as, unw_accessors_t *a, + unw_word_t *addr, unsigned char encoding, + unw_proc_info_t *pi, + unw_word_t *valp, void *arg) +{ + unw_word_t val, initial_addr = *addr; + uint16_t uval16; + uint32_t uval32; + uint64_t uval64; + int16_t sval16; + int32_t sval32; + int64_t sval64; + int ret; + + /* DW_EH_PE_omit and DW_EH_PE_aligned don't follow the normal + format/application encoding. Handle them first. */ + if (encoding == DW_EH_PE_omit) + { + *valp = 0; + return 0; + } + else if (encoding == DW_EH_PE_aligned) + { + *addr = (initial_addr + sizeof (unw_word_t) - 1) & -sizeof (unw_word_t); + return dwarf_readw (as, a, addr, valp, arg); + } + + switch (encoding & DW_EH_PE_FORMAT_MASK) + { + case DW_EH_PE_ptr: + if ((ret = dwarf_readw (as, a, addr, &val, arg)) < 0) + return ret; + break; + + case DW_EH_PE_uleb128: + if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0) + return ret; + break; + + case DW_EH_PE_udata2: + if ((ret = dwarf_readu16 (as, a, addr, &uval16, arg)) < 0) + return ret; + val = uval16; + break; + + case DW_EH_PE_udata4: + if ((ret = dwarf_readu32 (as, a, addr, &uval32, arg)) < 0) + return ret; + val = uval32; + break; + + case DW_EH_PE_udata8: + if ((ret = dwarf_readu64 (as, a, addr, &uval64, arg)) < 0) + return ret; + val = uval64; + break; + + case DW_EH_PE_sleb128: + if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0) + return ret; + break; + + case DW_EH_PE_sdata2: + if ((ret = dwarf_reads16 (as, a, addr, &sval16, arg)) < 0) + return ret; + val = sval16; + break; + + case DW_EH_PE_sdata4: + if ((ret = dwarf_reads32 (as, a, addr, &sval32, arg)) < 0) + return ret; + val = sval32; + break; + + case DW_EH_PE_sdata8: + if ((ret = dwarf_reads64 (as, a, addr, &sval64, arg)) < 0) + return ret; + val = sval64; + break; + + default: + Debug (1, "unexpected encoding format 0x%x\n", + encoding & DW_EH_PE_FORMAT_MASK); + return -UNW_EINVAL; + } + + if (val == 0) + { + /* 0 is a special value and always absolute. */ + *valp = 0; + return 0; + } + + switch (encoding & DW_EH_PE_APPL_MASK) + { + case DW_EH_PE_absptr: + break; + + case DW_EH_PE_pcrel: + val += initial_addr; + break; + + case DW_EH_PE_datarel: + /* XXX For now, assume that data-relative addresses are relative + to the global pointer. */ + val += pi->gp; + break; + + case DW_EH_PE_funcrel: + val += pi->start_ip; + break; + + case DW_EH_PE_textrel: + /* XXX For now we don't support text-rel values. If there is a + platform which needs this, we probably would have to add a + "segbase" member to unw_proc_info_t. */ + default: + Debug (1, "unexpected application type 0x%x\n", + encoding & DW_EH_PE_APPL_MASK); + return -UNW_EINVAL; + } + + if (encoding & DW_EH_PE_indirect) + { + unw_word_t indirect_addr = val; + + if ((ret = dwarf_readw (as, a, &indirect_addr, &val, arg)) < 0) + return ret; + } + + *valp = val; + return 0; +} diff --git a/src/dwarf/Gstep-dwarf.c b/src/dwarf/Gstep-dwarf.c index e69de29b..0911d519 100644 --- a/src/dwarf/Gstep-dwarf.c +++ b/src/dwarf/Gstep-dwarf.c @@ -0,0 +1,135 @@ +/* libunwind - a platform-independent unwind library + Copyright (c) 2003 Hewlett-Packard Development Company, L.P. + Contributed by David Mosberger-Tang + +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.h" +#include "tdep.h" + +static int +update_frame_state (struct dwarf_cursor *c) +{ + unw_word_t prev_ip, prev_cfa, ip; + int ret; + + prev_ip = c->ip; + prev_cfa = c->cfa; + + /* Update the IP cache (do this first: if we reach the end of the + frame-chain, the rest of the info may not be valid/useful + anymore. */ + ret = dwarf_get (c, c->loc[c->ret_addr_column], &ip); + if (ret < 0) + return ret; + c->ip = ip; + +#if 0 + /* ??? fix me---perhaps move to where we have convenient access to + code_align? */ + if ((ip & (c->code_align - 1)) != 0) + { + /* don't let obviously bad addresses pollute the cache */ + Debug (1, "rejecting bad ip=0x%lx\n", (long) c->ip); + return -UNW_EINVALIDIP; + } +#endif + if (ip == 0) + /* end of frame-chain reached */ + return 0; + +#if 0 + num_regs = 0; + if (unlikely (c->abi_marker)) + { + c->last_abi_marker = c->abi_marker; + switch (c->abi_marker) + { + case ABI_MARKER_LINUX_SIGTRAMP: + case ABI_MARKER_OLD_LINUX_SIGTRAMP: + c->as->abi = ABI_LINUX; + if ((ret = linux_sigtramp (c, &num_regs)) < 0) + return ret; + break; + + case ABI_MARKER_OLD_LINUX_INTERRUPT: + case ABI_MARKER_LINUX_INTERRUPT: + c->as->abi = ABI_LINUX; + if ((ret = linux_interrupt (c, &num_regs, c->abi_marker)) < 0) + return ret; + break; + + case ABI_MARKER_HP_UX_SIGTRAMP: + c->as->abi = ABI_HPUX; + if ((ret = hpux_sigtramp (c, &num_regs)) < 0) + return ret; + break; + + default: + Debug (1, "unknown ABI marker: ABI=%u, context=%u\n", + c->abi_marker >> 8, c->abi_marker & 0xff); + return -UNW_EINVAL; + } + Debug (10, "sigcontext_addr=%lx (ret=%d)\n", + (unsigned long) c->sigcontext_addr, ret); + + c->sigcontext_off = c->sigcontext_addr - c->cfa; + + /* update the IP cache: */ + if ((ret = ia64_get (c, c->loc[IA64_REG_IP], &ip)) < 0) + return ret; + c->ip = ip; + if (ip == 0) + /* end of frame-chain reached */ + return 0; + } + else + num_regs = (c->cfm >> 7) & 0x7f; /* size of locals */ + + c->abi_marker = 0; +#endif + + if (c->ip == prev_ip && c->cfa == prev_cfa) + { + dprintf ("%s: ip and cfa unchanged; stopping here (ip=0x%lx)\n", + __FUNCTION__, (long) ip); + return -UNW_EBADFRAME; + } + + c->pi_valid = 0; + return 0; +} + +HIDDEN int +dwarf_step (struct dwarf_cursor *c) +{ + int ret; + + if ((ret = dwarf_find_save_locs (c)) < 0) + return ret; + + if ((ret = update_frame_state (c)) < 0) + return ret; + + Debug (16, "done\n"); + return (c->ip == 0) ? 0 : 1; +} diff --git a/src/dwarf/Lexpr-dwarf.c b/src/dwarf/Lexpr-dwarf.c index e69de29b..b2eedc96 100644 --- a/src/dwarf/Lexpr-dwarf.c +++ b/src/dwarf/Lexpr-dwarf.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gexpr-dwarf.c" +#endif diff --git a/src/dwarf/Lfde-dwarf.c b/src/dwarf/Lfde-dwarf.c index e69de29b..9437a1a4 100644 --- a/src/dwarf/Lfde-dwarf.c +++ b/src/dwarf/Lfde-dwarf.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gfde-dwarf.c" +#endif diff --git a/src/dwarf/Lfind_proc_info-lsb.c b/src/dwarf/Lfind_proc_info-lsb.c index e69de29b..27a5eeac 100644 --- a/src/dwarf/Lfind_proc_info-lsb.c +++ b/src/dwarf/Lfind_proc_info-lsb.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gfind_proc_info-lsb.c" +#endif diff --git a/src/dwarf/Lparser-dwarf.c b/src/dwarf/Lparser-dwarf.c index e69de29b..c8599aa5 100644 --- a/src/dwarf/Lparser-dwarf.c +++ b/src/dwarf/Lparser-dwarf.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gparser-dwarf.c" +#endif diff --git a/src/dwarf/Lpe-dwarf.c b/src/dwarf/Lpe-dwarf.c index e69de29b..2810b9f3 100644 --- a/src/dwarf/Lpe-dwarf.c +++ b/src/dwarf/Lpe-dwarf.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gpe-dwarf.c" +#endif diff --git a/src/dwarf/Lstep-dwarf.c b/src/dwarf/Lstep-dwarf.c index e69de29b..728a0341 100644 --- a/src/dwarf/Lstep-dwarf.c +++ b/src/dwarf/Lstep-dwarf.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gstep-dwarf.c" +#endif diff --git a/src/dwarf/dwarf-eh.h b/src/dwarf/dwarf-eh.h index e69de29b..405e394a 100644 --- a/src/dwarf/dwarf-eh.h +++ b/src/dwarf/dwarf-eh.h @@ -0,0 +1,128 @@ +/* libunwind - a platform-independent unwind library + Copyright (c) 2003 Hewlett-Packard Development Company, L.P. + Contributed by David Mosberger-Tang + +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. */ + +#ifndef dwarf_eh_h +#define dwarf_eh_h + +#include "dwarf.h" + +/* This header file defines the format of a DWARF exception-header + section (.eh_frame_hdr, pointed to by program-header + PT_GNU_EH_FRAME). The exception-header is self-describing in the + sense that the format of the addresses contained in it is expressed + as a one-byte type-descriptor called a "pointer-encoding" (PE). + + The exception header encodes the address of the .eh_frame section + and optionally contains a binary search table for the + Frame Descriptor Entries (FDEs) in the .eh_frame. The contents of + .eh_frame has the format described by the DWARF v3 standard + (http://www.eagercon.com/dwarf/dwarf3std.htm), except that code + addresses may be encoded in different ways. Also, .eh_frame has + augmentations that allow encoding a language-specific data-area + (LSDA) pointer and a pointer to a personality-routine. + + Details: + + The Common Information Entry (CIE) associated with an FDE may + contain an augmentation string. Each character in this string has + a specific meaning and either one or two associated operands. The + operands are stored in an augmentation body which appears right + after the "return_address_register" member and before the + "initial_instructions" member. The operands appear in the order + in which the characters appear in the string. For example, if the + augmentation string is "zL", the operand for 'z' would be first in + the augmentation body and the operand for 'L' would be second. + The following characters are supported for the CIE augmentation + string: + + 'z': The operand for this character is a uleb128 value that gives the + length of the CIE augmentation body, not counting the length + of the uleb128 operand itself. If present, this code must + appear as the first character in the augmentation body. + + 'L': Indicates that the FDE's augmentation body contains an LSDA + pointer. The operand for this character is a single byte + that specifies the pointer-encoding (PE) that is used for + the LSDA pointer. + + 'R': Indicates that the code-pointers (FDE members + "initial_location" and "address_range" and the operand for + DW_CFA_set_loc) in the FDE have a non-default encoding. The + operand for this character is a single byte that specifies + the pointer-encoding (PE) that is used for the + code-pointers. Note: the "address_range" member is always + encoded as an absolute value. Apart from that, the specified + FDE pointer-encoding applies. + + 'P': Indicates the presence of a personality routine (handler). + The first operand for this character specifies the + pointer-encoding (PE) that is used for the second operand, + which specifies the address of the personality routine. + + If the augmentation string contains any other characters, the + remainder of the augmentation string should be ignored. + Furthermore, if the size of the augmentation body is unknown + (i.e., 'z' is not the first character of the augmentation string), + then the entire CIE as well all associated FDEs must be ignored. + + A Frame Descriptor Entries (FDE) may contain an augmentation body + which, if present, appears right after the "address_range" member + and before the "instructions" member. The contents of this body + is implicitly defined by the augmentation string of the associated + CIE. The meaning of the characters in the CIE's augmentation + string as far as FDEs are concerned is as follows: + + 'z': The first operand in the FDE's augmentation body specifies + the total length of the augmentation body as a uleb128 (not + counting the length of the uleb128 operand itself). + + 'L': The operand for this character is an LSDA pointer, encoded + in the format specified by the corresponding operand in the + CIE's augmentation body. + +*/ + +#define DW_EH_VERSION 1 /* The version we're implementing */ + +struct dwarf_eh_frame_hdr + { + unsigned char version; + unsigned char eh_frame_ptr_enc; + unsigned char fde_count_enc; + unsigned char table_enc; + /* The rest of the header is variable-length and consists of the + following members: + + encoded_t eh_frame_ptr; + encoded_t fde_count; + struct + { + encoded_t start_ip; // first address covered by this FDE + encoded_t fde_addr; // address of the FDE + } + binary_search_table[fde_count]; */ + }; + +#endif /* dwarf_eh_h */ diff --git a/src/dwarf/global-dwarf.c b/src/dwarf/global-dwarf.c index e69de29b..b2812af7 100644 --- a/src/dwarf/global-dwarf.c +++ b/src/dwarf/global-dwarf.c @@ -0,0 +1,35 @@ +/* libunwind - a platform-independent unwind library + Copyright (c) 2003 Hewlett-Packard Development Company, L.P. + Contributed by David Mosberger-Tang + +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.h" + +HIDDEN struct mempool dwarf_reg_state_pool; + +HIDDEN int +dwarf_init (void) +{ + mempool_init (&dwarf_reg_state_pool, sizeof (dwarf_reg_state_t), 0); + return 0; +}