X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=blobdiff_plain;f=gcc%2Funwind-dw2.c;h=5a4375fa92779a08163d30364ad9b749326d724f;hp=16f6bceb5c936e3d039a5e065192a0f5f43e3ff2;hb=3c83a68a5d471d8c81aa722b7959d6c3633853be;hpb=1be87b7252bb4b5936fc2ebebdcb9f18bed35bae diff --git a/gcc/unwind-dw2.c b/gcc/unwind-dw2.c index 16f6bceb5c9..5a4375fa927 100644 --- a/gcc/unwind-dw2.c +++ b/gcc/unwind-dw2.c @@ -1,33 +1,48 @@ /* DWARF2 exception handling and frame unwind runtime interface routines. - Copyright (C) 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. - This file is part of GNU CC. + This file is part of GCC. - GNU CC is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. - GNU CC is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + In addition to the permissions in the GNU General Public License, the + Free Software Foundation gives you unlimited permission to link the + compiled version of this file into combinations with other programs, + and to distribute those combinations without any restriction coming + from the use of this file. (The General Public License restrictions + do apply in other respects; for example, they cover modification of + the file, and distribution when not linked into a combined + executable.) + + GCC is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. You should have received a copy of the GNU General Public License - along with GNU CC; see the file COPYING. If not, write to - the Free Software Foundation, 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ + along with GCC; see the file COPYING. If not, write to the Free + Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. */ #include "tconfig.h" #include "tsystem.h" +#include "coretypes.h" +#include "tm.h" #include "dwarf2.h" #include "unwind.h" +#ifdef __USING_SJLJ_EXCEPTIONS__ +# define NO_SIZE_OF_ENCODED_VALUE +#endif #include "unwind-pe.h" #include "unwind-dw2-fde.h" #include "gthr.h" +#include "unwind-dw2.h" - -#if !USING_SJLJ_EXCEPTIONS +#ifndef __USING_SJLJ_EXCEPTIONS__ #ifndef STACK_GROWS_DOWNWARD #define STACK_GROWS_DOWNWARD 0 @@ -36,13 +51,18 @@ #define STACK_GROWS_DOWNWARD 1 #endif -/* A target can override (perhaps for backward compatibility) how - many dwarf2 columns are unwound. */ -#ifndef DWARF_FRAME_REGISTERS -#define DWARF_FRAME_REGISTERS FIRST_PSEUDO_REGISTER +/* Dwarf frame registers used for pre gcc 3.0 compiled glibc. */ +#ifndef PRE_GCC3_DWARF_FRAME_REGISTERS +#define PRE_GCC3_DWARF_FRAME_REGISTERS DWARF_FRAME_REGISTERS +#endif + +#ifndef DWARF_REG_TO_UNWIND_COLUMN +#define DWARF_REG_TO_UNWIND_COLUMN(REGNO) (REGNO) #endif -/* This is the register and unwind state for a particular frame. */ +/* This is the register and unwind state for a particular frame. This + provides the information necessary to unwind up past a frame and return + to its caller. */ struct _Unwind_Context { void *reg[DWARF_FRAME_REGISTERS+1]; @@ -54,61 +74,9 @@ struct _Unwind_Context }; /* Byte size of every register managed by these routines. */ -static unsigned char dwarf_reg_size_table[DWARF_FRAME_REGISTERS]; +static unsigned char dwarf_reg_size_table[DWARF_FRAME_REGISTERS+1]; -/* The result of interpreting the frame unwind info for a frame. - This is all symbolic at this point, as none of the values can - be resolved until the target pc is located. */ -typedef struct -{ - /* Each register save state can be described in terms of a CFA slot, - another register, or a location expression. */ - struct frame_state_reg_info - { - struct { - union { - unsigned int reg; - _Unwind_Sword offset; - const unsigned char *exp; - } loc; - enum { - REG_UNSAVED, - REG_SAVED_OFFSET, - REG_SAVED_REG, - REG_SAVED_EXP, - } how; - } reg[DWARF_FRAME_REGISTERS+1]; - - /* Used to implement DW_CFA_remember_state. */ - struct frame_state_reg_info *prev; - } regs; - - /* The CFA can be described in terms of a reg+offset or a - location expression. */ - _Unwind_Sword cfa_offset; - _Unwind_Word cfa_reg; - const unsigned char *cfa_exp; - enum { - CFA_UNSET, - CFA_REG_OFFSET, - CFA_EXP, - } cfa_how; - - /* The PC described by the current frame state. */ - void *pc; - - /* The information we care about from the CIE/FDE. */ - _Unwind_Personality_Fn personality; - signed int data_align; - unsigned int code_align; - unsigned char retaddr_column; - unsigned char fde_encoding; - unsigned char lsda_encoding; - unsigned char saw_z; - void *eh_ptr; -} _Unwind_FrameState; - /* Read unaligned data from the instruction buffer. */ union unaligned @@ -126,10 +94,10 @@ static inline void * read_pointer (const void *p) { const union unaligned *up = p; return up->p; } static inline int -read_1u (const void *p) { return *(const unsigned char *)p; } +read_1u (const void *p) { return *(const unsigned char *) p; } static inline int -read_1s (const void *p) { return *(const signed char *)p; } +read_1s (const void *p) { return *(const signed char *) p; } static inline int read_2u (const void *p) { const union unaligned *up = p; return up->u2; } @@ -154,8 +122,41 @@ read_8s (const void *p) { const union unaligned *up = p; return up->s8; } inline _Unwind_Word _Unwind_GetGR (struct _Unwind_Context *context, int index) { + int size; + void *ptr; + +#ifdef DWARF_ZERO_REG + if (index == DWARF_ZERO_REG) + return 0; +#endif + + index = DWARF_REG_TO_UNWIND_COLUMN (index); + gcc_assert (index < (int) sizeof(dwarf_reg_size_table)); + size = dwarf_reg_size_table[index]; + ptr = context->reg[index]; + /* This will segfault if the register hasn't been saved. */ - return * (_Unwind_Word *) context->reg[index]; + if (size == sizeof(_Unwind_Ptr)) + return * (_Unwind_Ptr *) ptr; + else + { + gcc_assert (size == sizeof(_Unwind_Word)); + return * (_Unwind_Word *) ptr; + } +} + +static inline void * +_Unwind_GetPtr (struct _Unwind_Context *context, int index) +{ + return (void *)(_Unwind_Ptr) _Unwind_GetGR (context, index); +} + +/* Get the value of the CFA as saved in CONTEXT. */ + +_Unwind_Word +_Unwind_GetCFA (struct _Unwind_Context *context) +{ + return (_Unwind_Ptr) context->cfa; } /* Overwrite the saved value for register REG in CONTEXT with VAL. */ @@ -163,7 +164,39 @@ _Unwind_GetGR (struct _Unwind_Context *context, int index) inline void _Unwind_SetGR (struct _Unwind_Context *context, int index, _Unwind_Word val) { - * (_Unwind_Word *) context->reg[index] = val; + int size; + void *ptr; + + index = DWARF_REG_TO_UNWIND_COLUMN (index); + gcc_assert (index < (int) sizeof(dwarf_reg_size_table)); + size = dwarf_reg_size_table[index]; + ptr = context->reg[index]; + + if (size == sizeof(_Unwind_Ptr)) + * (_Unwind_Ptr *) ptr = val; + else + { + gcc_assert (size == sizeof(_Unwind_Word)); + * (_Unwind_Word *) ptr = val; + } +} + +/* Get the pointer to a register INDEX as saved in CONTEXT. */ + +static inline void * +_Unwind_GetGRPtr (struct _Unwind_Context *context, int index) +{ + index = DWARF_REG_TO_UNWIND_COLUMN (index); + return context->reg[index]; +} + +/* Set the pointer to a register INDEX as saved in CONTEXT. */ + +static inline void +_Unwind_SetGRPtr (struct _Unwind_Context *context, int index, void *p) +{ + index = DWARF_REG_TO_UNWIND_COLUMN (index); + context->reg[index] = p; } /* Retrieve the return address for CONTEXT. */ @@ -194,6 +227,17 @@ _Unwind_GetRegionStart (struct _Unwind_Context *context) return (_Unwind_Ptr) context->bases.func; } +void * +_Unwind_FindEnclosingFunction (void *pc) +{ + struct dwarf_eh_bases bases; + const struct dwarf_fde *fde = _Unwind_Find_FDE (pc-1, &bases); + if (fde) + return bases.func; + else + return NULL; +} + #ifndef __ia64__ _Unwind_Ptr _Unwind_GetDataRelBase (struct _Unwind_Context *context) @@ -207,19 +251,23 @@ _Unwind_GetTextRelBase (struct _Unwind_Context *context) return (_Unwind_Ptr) context->bases.tbase; } #endif + +#ifdef MD_UNWIND_SUPPORT +#include MD_UNWIND_SUPPORT +#endif /* Extract any interesting information from the CIE for the translation unit F belongs to. Return a pointer to the byte after the augmentation, or NULL if we encountered an undecipherable augmentation. */ static const unsigned char * -extract_cie_info (struct dwarf_cie *cie, struct _Unwind_Context *context, +extract_cie_info (const struct dwarf_cie *cie, struct _Unwind_Context *context, _Unwind_FrameState *fs) { const unsigned char *aug = cie->augmentation; - const unsigned char *p = aug + strlen (aug) + 1; + const unsigned char *p = aug + strlen ((const char *)aug) + 1; const unsigned char *ret = NULL; - _Unwind_Ptr tmp; + _Unwind_Word utmp; /* g++ v2 "eh" has pointer immediately following augmentation string, so it must be handled first. */ @@ -232,9 +280,12 @@ extract_cie_info (struct dwarf_cie *cie, struct _Unwind_Context *context, /* Immediately following the augmentation are the code and data alignment and return address column. */ - p = read_uleb128 (p, &tmp); fs->code_align = tmp; - p = read_sleb128 (p, &tmp); fs->data_align = (saddr) tmp; - fs->retaddr_column = *p++; + p = read_uleb128 (p, &fs->code_align); + p = read_sleb128 (p, &fs->data_align); + if (cie->version == 1) + fs->retaddr_column = *p++; + else + p = read_uleb128 (p, &fs->retaddr_column); fs->lsda_encoding = DW_EH_PE_omit; /* If the augmentation starts with 'z', then a uleb128 immediately @@ -242,8 +293,8 @@ extract_cie_info (struct dwarf_cie *cie, struct _Unwind_Context *context, the size. */ if (*aug == 'z') { - p = read_uleb128 (p, &tmp); - ret = p + tmp; + p = read_uleb128 (p, &utmp); + ret = p + utmp; fs->saw_z = 1; ++aug; @@ -269,8 +320,10 @@ extract_cie_info (struct dwarf_cie *cie, struct _Unwind_Context *context, /* "P" indicates a personality routine in the CIE augmentation. */ else if (aug[0] == 'P') { - p = read_encoded_value (context, *p, p + 1, - (_Unwind_Ptr *) &fs->personality); + _Unwind_Ptr personality; + + p = read_encoded_value (context, *p, p + 1, &personality); + fs->personality = (_Unwind_Personality_Fn) personality; aug += 1; } @@ -300,9 +353,8 @@ execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end, while (op_ptr < op_end) { enum dwarf_location_atom op = *op_ptr++; - _Unwind_Word result, reg; - _Unwind_Sword offset; - _Unwind_Ptr ptrtmp; + _Unwind_Word result, reg, utmp; + _Unwind_Sword offset, stmp; switch (op) { @@ -379,12 +431,11 @@ execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end, op_ptr += 8; break; case DW_OP_constu: - op_ptr = read_uleb128 (op_ptr, &ptrtmp); - result = ptrtmp; + op_ptr = read_uleb128 (op_ptr, &result); break; case DW_OP_consts: - op_ptr = read_sleb128 (op_ptr, &ptrtmp); - result = (saddr)ptrtmp; + op_ptr = read_sleb128 (op_ptr, &stmp); + result = stmp; break; case DW_OP_reg0: @@ -422,7 +473,7 @@ execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end, result = _Unwind_GetGR (context, op - DW_OP_reg0); break; case DW_OP_regx: - op_ptr = read_uleb128 (op_ptr, &ptrtmp); reg = ptrtmp; + op_ptr = read_uleb128 (op_ptr, ®); result = _Unwind_GetGR (context, reg); break; @@ -458,36 +509,33 @@ execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end, case DW_OP_breg29: case DW_OP_breg30: case DW_OP_breg31: - op_ptr = read_sleb128 (op_ptr, &ptrtmp); offset = (saddr)ptrtmp; + op_ptr = read_sleb128 (op_ptr, &offset); result = _Unwind_GetGR (context, op - DW_OP_breg0) + offset; break; case DW_OP_bregx: - op_ptr = read_uleb128 (op_ptr, &ptrtmp); reg = ptrtmp; - op_ptr = read_sleb128 (op_ptr, &ptrtmp); offset = (saddr)ptrtmp; + op_ptr = read_uleb128 (op_ptr, ®); + op_ptr = read_sleb128 (op_ptr, &offset); result = _Unwind_GetGR (context, reg) + offset; break; case DW_OP_dup: - if (stack_elt < 1) - abort (); + gcc_assert (stack_elt); result = stack[stack_elt - 1]; break; case DW_OP_drop: - if (--stack_elt < 0) - abort (); + gcc_assert (stack_elt); + stack_elt -= 1; goto no_push; case DW_OP_pick: offset = *op_ptr++; - if (offset >= stack_elt - 1) - abort (); + gcc_assert (offset < stack_elt - 1); result = stack[stack_elt - 1 - offset]; break; case DW_OP_over: - if (stack_elt < 2) - abort (); + gcc_assert (stack_elt >= 2); result = stack[stack_elt - 2]; break; @@ -495,8 +543,7 @@ execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end, { _Unwind_Word t1, t2, t3; - if (stack_elt < 3) - abort (); + gcc_assert (stack_elt >= 3); t1 = stack[stack_elt - 1]; t2 = stack[stack_elt - 2]; t3 = stack[stack_elt - 3]; @@ -513,22 +560,23 @@ execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end, case DW_OP_not: case DW_OP_plus_uconst: /* Unary operations. */ - if (--stack_elt < 0) - abort (); + gcc_assert (stack_elt); + stack_elt -= 1; + result = stack[stack_elt]; switch (op) { case DW_OP_deref: { - void *ptr = (void *)(_Unwind_Ptr) result; + void *ptr = (void *) (_Unwind_Ptr) result; result = (_Unwind_Ptr) read_pointer (ptr); } break; case DW_OP_deref_size: { - void *ptr = (void *)(_Unwind_Ptr) result; + void *ptr = (void *) (_Unwind_Ptr) result; switch (*op_ptr++) { case 1: @@ -544,7 +592,7 @@ execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end, result = read_8u (ptr); break; default: - abort (); + gcc_unreachable (); } } break; @@ -560,9 +608,12 @@ execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end, result = ~result; break; case DW_OP_plus_uconst: - op_ptr = read_uleb128 (op_ptr, &ptrtmp); reg = ptrtmp; - result += reg; + op_ptr = read_uleb128 (op_ptr, &utmp); + result += utmp; break; + + default: + gcc_unreachable (); } break; @@ -573,6 +624,10 @@ execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end, case DW_OP_mul: case DW_OP_or: case DW_OP_plus: + case DW_OP_shl: + case DW_OP_shr: + case DW_OP_shra: + case DW_OP_xor: case DW_OP_le: case DW_OP_ge: case DW_OP_eq: @@ -582,65 +637,69 @@ execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end, { /* Binary operations. */ _Unwind_Word first, second; - if ((stack_elt -= 2) < 0) - abort (); - second = stack[stack_elt]; - first = stack[stack_elt + 1]; + gcc_assert (stack_elt >= 2); + stack_elt -= 2; + + second = stack[stack_elt]; + first = stack[stack_elt + 1]; - switch (op) - { - case DW_OP_and: - result = second & first; - break; - case DW_OP_div: - result = (_Unwind_Sword)second / (_Unwind_Sword)first; - break; - case DW_OP_minus: - result = second - first; - break; - case DW_OP_mod: - result = (_Unwind_Sword)second % (_Unwind_Sword)first; - break; - case DW_OP_mul: - result = second * first; - break; - case DW_OP_or: - result = second | first; - break; - case DW_OP_plus: - result = second + first; - break; - case DW_OP_shl: - result = second << first; - break; - case DW_OP_shr: - result = second >> first; - break; - case DW_OP_shra: - result = (_Unwind_Sword)second >> first; - break; - case DW_OP_xor: - result = second ^ first; - break; - case DW_OP_le: - result = (_Unwind_Sword)first <= (_Unwind_Sword)second; - break; - case DW_OP_ge: - result = (_Unwind_Sword)first >= (_Unwind_Sword)second; - break; - case DW_OP_eq: - result = (_Unwind_Sword)first == (_Unwind_Sword)second; - break; - case DW_OP_lt: - result = (_Unwind_Sword)first < (_Unwind_Sword)second; - break; - case DW_OP_gt: - result = (_Unwind_Sword)first > (_Unwind_Sword)second; - break; - case DW_OP_ne: - result = (_Unwind_Sword)first != (_Unwind_Sword)second; - break; - } + switch (op) + { + case DW_OP_and: + result = second & first; + break; + case DW_OP_div: + result = (_Unwind_Sword) second / (_Unwind_Sword) first; + break; + case DW_OP_minus: + result = second - first; + break; + case DW_OP_mod: + result = (_Unwind_Sword) second % (_Unwind_Sword) first; + break; + case DW_OP_mul: + result = second * first; + break; + case DW_OP_or: + result = second | first; + break; + case DW_OP_plus: + result = second + first; + break; + case DW_OP_shl: + result = second << first; + break; + case DW_OP_shr: + result = second >> first; + break; + case DW_OP_shra: + result = (_Unwind_Sword) second >> first; + break; + case DW_OP_xor: + result = second ^ first; + break; + case DW_OP_le: + result = (_Unwind_Sword) first <= (_Unwind_Sword) second; + break; + case DW_OP_ge: + result = (_Unwind_Sword) first >= (_Unwind_Sword) second; + break; + case DW_OP_eq: + result = (_Unwind_Sword) first == (_Unwind_Sword) second; + break; + case DW_OP_lt: + result = (_Unwind_Sword) first < (_Unwind_Sword) second; + break; + case DW_OP_gt: + result = (_Unwind_Sword) first > (_Unwind_Sword) second; + break; + case DW_OP_ne: + result = (_Unwind_Sword) first != (_Unwind_Sword) second; + break; + + default: + gcc_unreachable (); + } } break; @@ -651,8 +710,9 @@ execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end, goto no_push; case DW_OP_bra: - if (--stack_elt < 0) - abort (); + gcc_assert (stack_elt); + stack_elt -= 1; + offset = read_2s (op_ptr); op_ptr += 2; if (stack[stack_elt] != 0) @@ -663,20 +723,19 @@ execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end, goto no_push; default: - abort (); + gcc_unreachable (); } /* Most things push a result value. */ - if ((size_t) stack_elt >= sizeof(stack)/sizeof(*stack)) - abort (); - stack[++stack_elt] = result; + gcc_assert ((size_t) stack_elt < sizeof(stack)/sizeof(*stack)); + stack[stack_elt++] = result; no_push:; } /* We were executing this program to get a value. It should be at top of stack. */ - if (--stack_elt < 0) - abort (); + gcc_assert (stack_elt); + stack_elt -= 1; return stack[stack_elt]; } @@ -696,33 +755,45 @@ execute_cfa_program (const unsigned char *insn_ptr, /* Don't allow remember/restore between CIE and FDE programs. */ fs->regs.prev = NULL; + /* The comparison with the return address uses < rather than <= because + we are only interested in the effects of code before the call; for a + noreturn function, the return address may point to unrelated code with + a different stack configuration that we are not interested in. We + assume that the call itself is unwind info-neutral; if not, or if + there are delay instructions that adjust the stack, these must be + reflected at the point immediately before the call insn. */ while (insn_ptr < insn_end && fs->pc < context->ra) { unsigned char insn = *insn_ptr++; - _Unwind_Word reg; - _Unwind_Sword offset; - _Unwind_Ptr ptrtmp; + _Unwind_Word reg, utmp; + _Unwind_Sword offset, stmp; - if (insn & DW_CFA_advance_loc) + if ((insn & 0xc0) == DW_CFA_advance_loc) fs->pc += (insn & 0x3f) * fs->code_align; - else if (insn & DW_CFA_offset) + else if ((insn & 0xc0) == DW_CFA_offset) { reg = insn & 0x3f; - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); - offset = ptrtmp * fs->data_align; - fs->regs.reg[reg].how = REG_SAVED_OFFSET; - fs->regs.reg[reg].loc.offset = offset; + insn_ptr = read_uleb128 (insn_ptr, &utmp); + offset = (_Unwind_Sword) utmp * fs->data_align; + fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].how + = REG_SAVED_OFFSET; + fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].loc.offset = offset; } - else if (insn & DW_CFA_restore) + else if ((insn & 0xc0) == DW_CFA_restore) { reg = insn & 0x3f; - fs->regs.reg[reg].how = REG_UNSAVED; + fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].how = REG_UNSAVED; } else switch (insn) { case DW_CFA_set_loc: - insn_ptr = read_encoded_value (context, fs->fde_encoding, - insn_ptr, (_Unwind_Ptr *) &fs->pc); + { + _Unwind_Ptr pc; + + insn_ptr = read_encoded_value (context, fs->fde_encoding, + insn_ptr, &pc); + fs->pc = (void *) pc; + } break; case DW_CFA_advance_loc1: @@ -739,33 +810,40 @@ execute_cfa_program (const unsigned char *insn_ptr, break; case DW_CFA_offset_extended: - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); reg = ptrtmp; - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); - offset = ptrtmp * fs->data_align; - fs->regs.reg[reg].how = REG_SAVED_OFFSET; - fs->regs.reg[reg].loc.offset = offset; + insn_ptr = read_uleb128 (insn_ptr, ®); + insn_ptr = read_uleb128 (insn_ptr, &utmp); + offset = (_Unwind_Sword) utmp * fs->data_align; + fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].how + = REG_SAVED_OFFSET; + fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].loc.offset = offset; break; case DW_CFA_restore_extended: - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); reg = ptrtmp; - fs->regs.reg[reg].how = REG_UNSAVED; + insn_ptr = read_uleb128 (insn_ptr, ®); + /* FIXME, this is wrong; the CIE might have said that the + register was saved somewhere. */ + fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN(reg)].how = REG_UNSAVED; break; case DW_CFA_undefined: case DW_CFA_same_value: + insn_ptr = read_uleb128 (insn_ptr, ®); + fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN(reg)].how = REG_UNSAVED; + break; + case DW_CFA_nop: break; case DW_CFA_register: { _Unwind_Word reg2; - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); reg = ptrtmp; - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); reg2 = ptrtmp; - fs->regs.reg[reg].how = REG_SAVED_REG; - fs->regs.reg[reg].loc.reg = reg2; + insn_ptr = read_uleb128 (insn_ptr, ®); + insn_ptr = read_uleb128 (insn_ptr, ®2); + fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].how = REG_SAVED_REG; + fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].loc.reg = reg2; } break; - + case DW_CFA_remember_state: { struct frame_state_reg_info *new_rs; @@ -792,60 +870,56 @@ execute_cfa_program (const unsigned char *insn_ptr, break; case DW_CFA_def_cfa: - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); - fs->cfa_reg = ptrtmp; - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); - fs->cfa_offset = ptrtmp; + insn_ptr = read_uleb128 (insn_ptr, &fs->cfa_reg); + insn_ptr = read_uleb128 (insn_ptr, &utmp); + fs->cfa_offset = utmp; fs->cfa_how = CFA_REG_OFFSET; break; case DW_CFA_def_cfa_register: - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); - fs->cfa_reg = ptrtmp; + insn_ptr = read_uleb128 (insn_ptr, &fs->cfa_reg); fs->cfa_how = CFA_REG_OFFSET; break; case DW_CFA_def_cfa_offset: - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); - fs->cfa_offset = ptrtmp; + insn_ptr = read_uleb128 (insn_ptr, &utmp); + fs->cfa_offset = utmp; /* cfa_how deliberately not set. */ break; case DW_CFA_def_cfa_expression: - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); fs->cfa_exp = insn_ptr; fs->cfa_how = CFA_EXP; - insn_ptr += ptrtmp; + insn_ptr = read_uleb128 (insn_ptr, &utmp); + insn_ptr += utmp; break; case DW_CFA_expression: - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); reg = ptrtmp; - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); - fs->regs.reg[reg].how = REG_SAVED_EXP; - fs->regs.reg[reg].loc.exp = insn_ptr; - insn_ptr += ptrtmp; + insn_ptr = read_uleb128 (insn_ptr, ®); + fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].how = REG_SAVED_EXP; + fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].loc.exp = insn_ptr; + insn_ptr = read_uleb128 (insn_ptr, &utmp); + insn_ptr += utmp; break; /* From the 2.1 draft. */ case DW_CFA_offset_extended_sf: - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); reg = ptrtmp; - insn_ptr = read_sleb128 (insn_ptr, &ptrtmp); - offset = (saddr)ptrtmp * fs->data_align; - fs->regs.reg[reg].how = REG_SAVED_OFFSET; - fs->regs.reg[reg].loc.offset = offset; + insn_ptr = read_uleb128 (insn_ptr, ®); + insn_ptr = read_sleb128 (insn_ptr, &stmp); + offset = stmp * fs->data_align; + fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].how + = REG_SAVED_OFFSET; + fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].loc.offset = offset; break; - + case DW_CFA_def_cfa_sf: - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); - fs->cfa_reg = ptrtmp; - insn_ptr = read_sleb128 (insn_ptr, &ptrtmp); - fs->cfa_offset = (saddr)ptrtmp; + insn_ptr = read_uleb128 (insn_ptr, &fs->cfa_reg); + insn_ptr = read_sleb128 (insn_ptr, &fs->cfa_offset); fs->cfa_how = CFA_REG_OFFSET; break; case DW_CFA_def_cfa_offset_sf: - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); - fs->cfa_offset = ptrtmp; + insn_ptr = read_sleb128 (insn_ptr, &fs->cfa_offset); /* cfa_how deliberately not set. */ break; @@ -859,48 +933,53 @@ execute_cfa_program (const unsigned char *insn_ptr, break; case DW_CFA_GNU_args_size: - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); - context->args_size = ptrtmp; + insn_ptr = read_uleb128 (insn_ptr, &context->args_size); break; case DW_CFA_GNU_negative_offset_extended: /* Obsoleted by DW_CFA_offset_extended_sf, but used by older PowerPC code. */ - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); reg = ptrtmp; - insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); - offset = ptrtmp * fs->data_align; - fs->regs.reg[reg].how = REG_SAVED_OFFSET; - fs->regs.reg[reg].loc.offset = -offset; + insn_ptr = read_uleb128 (insn_ptr, ®); + insn_ptr = read_uleb128 (insn_ptr, &utmp); + offset = (_Unwind_Word) utmp * fs->data_align; + fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].how + = REG_SAVED_OFFSET; + fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].loc.offset = -offset; break; default: - abort (); + gcc_unreachable (); } } } +/* Given the _Unwind_Context CONTEXT for a stack frame, look up the FDE for + its caller and decode it into FS. This function also sets the + args_size and lsda members of CONTEXT, as they are really information + about the caller's frame. */ + static _Unwind_Reason_Code uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs) { - struct dwarf_fde *fde; - struct dwarf_cie *cie; + const struct dwarf_fde *fde; + const struct dwarf_cie *cie; const unsigned char *aug, *insn, *end; memset (fs, 0, sizeof (*fs)); context->args_size = 0; context->lsda = 0; + if (context->ra == 0) + return _URC_END_OF_STACK; + fde = _Unwind_Find_FDE (context->ra - 1, &context->bases); if (fde == NULL) { +#ifdef MD_FALLBACK_FRAME_STATE_FOR /* Couldn't find frame unwind info for this function. Try a target-specific fallback mechanism. This will necessarily - not profide a personality routine or LSDA. */ -#ifdef MD_FALLBACK_FRAME_STATE_FOR - MD_FALLBACK_FRAME_STATE_FOR (context, fs, success); - return _URC_END_OF_STACK; - success: - return _URC_NO_REASON; + not provide a personality routine or LSDA. */ + return MD_FALLBACK_FRAME_STATE_FOR (context, fs); #else return _URC_END_OF_STACK; #endif @@ -919,18 +998,22 @@ uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs) execute_cfa_program (insn, end, context, fs); /* Locate augmentation for the fde. */ - aug = (unsigned char *)fde + sizeof (*fde); + aug = (unsigned char *) fde + sizeof (*fde); aug += 2 * size_of_encoded_value (fs->fde_encoding); insn = NULL; if (fs->saw_z) { - _Unwind_Ptr i; + _Unwind_Word i; aug = read_uleb128 (aug, &i); insn = aug + i; } if (fs->lsda_encoding != DW_EH_PE_omit) - aug = read_encoded_value (context, fs->lsda_encoding, aug, - (_Unwind_Ptr *) &context->lsda); + { + _Unwind_Ptr lsda; + + aug = read_encoded_value (context, fs->lsda_encoding, aug, &lsda); + context->lsda = (void *) lsda; + } /* Then the insns in the FDE up to our target PC. */ if (insn == NULL) @@ -947,10 +1030,10 @@ typedef struct frame_state void *eh_ptr; long cfa_offset; long args_size; - long reg_or_offset[DWARF_FRAME_REGISTERS+1]; + long reg_or_offset[PRE_GCC3_DWARF_FRAME_REGISTERS+1]; unsigned short cfa_reg; unsigned short retaddr_column; - char saved[DWARF_FRAME_REGISTERS+1]; + char saved[PRE_GCC3_DWARF_FRAME_REGISTERS+1]; } frame_state; struct frame_state * __frame_state_for (void *, struct frame_state *); @@ -977,7 +1060,7 @@ __frame_state_for (void *pc_target, struct frame_state *state_in) if (fs.cfa_how == CFA_EXP) return 0; - for (reg = 0; reg < DWARF_FRAME_REGISTERS + 1; reg++) + for (reg = 0; reg < PRE_GCC3_DWARF_FRAME_REGISTERS + 1; reg++) { state_in->saved[reg] = fs.regs.reg[reg].how; switch (state_in->saved[reg]) @@ -1003,6 +1086,24 @@ __frame_state_for (void *pc_target, struct frame_state *state_in) return state_in; } +typedef union { _Unwind_Ptr ptr; _Unwind_Word word; } _Unwind_SpTmp; + +static inline void +_Unwind_SetSpColumn (struct _Unwind_Context *context, void *cfa, + _Unwind_SpTmp *tmp_sp) +{ + int size = dwarf_reg_size_table[__builtin_dwarf_sp_column ()]; + + if (size == sizeof(_Unwind_Ptr)) + tmp_sp->ptr = (_Unwind_Ptr) cfa; + else + { + gcc_assert (size == sizeof(_Unwind_Word)); + tmp_sp->word = (_Unwind_Ptr) cfa; + } + _Unwind_SetGRPtr (context, __builtin_dwarf_sp_column (), tmp_sp); +} + static void uw_update_context_1 (struct _Unwind_Context *context, _Unwind_FrameState *fs) { @@ -1010,39 +1111,50 @@ uw_update_context_1 (struct _Unwind_Context *context, _Unwind_FrameState *fs) void *cfa; long i; +#ifdef EH_RETURN_STACKADJ_RTX + /* Special handling here: Many machines do not use a frame pointer, + and track the CFA only through offsets from the stack pointer from + one frame to the next. In this case, the stack pointer is never + stored, so it has no saved address in the context. What we do + have is the CFA from the previous stack frame. + + In very special situations (such as unwind info for signal return), + there may be location expressions that use the stack pointer as well. + + Do this conditionally for one frame. This allows the unwind info + for one frame to save a copy of the stack pointer from the previous + frame, and be able to use much easier CFA mechanisms to do it. + Always zap the saved stack pointer value for the next frame; carrying + the value over from one frame to another doesn't make sense. */ + + _Unwind_SpTmp tmp_sp; + + if (!_Unwind_GetGRPtr (&orig_context, __builtin_dwarf_sp_column ())) + _Unwind_SetSpColumn (&orig_context, context->cfa, &tmp_sp); + _Unwind_SetGRPtr (context, __builtin_dwarf_sp_column (), NULL); +#endif + /* Compute this frame's CFA. */ switch (fs->cfa_how) { case CFA_REG_OFFSET: - /* Special handling here: Many machines do not use a frame pointer, - and track the CFA only through offsets from the stack pointer from - one frame to the next. In this case, the stack pointer is never - stored, so it has no saved address in the context. What we do - have is the CFA from the previous stack frame. */ - if (context->reg[fs->cfa_reg] == NULL) - cfa = context->cfa; - else - cfa = (void *) (_Unwind_Ptr) _Unwind_GetGR (context, fs->cfa_reg); + cfa = _Unwind_GetPtr (&orig_context, fs->cfa_reg); cfa += fs->cfa_offset; break; case CFA_EXP: - /* ??? No way of knowing what register number is the stack pointer - to do the same sort of handling as above. Assume that if the - CFA calculation is so complicated as to require a stack program - that this will not be a problem. */ { const unsigned char *exp = fs->cfa_exp; - _Unwind_Ptr len; + _Unwind_Word len; exp = read_uleb128 (exp, &len); cfa = (void *) (_Unwind_Ptr) - execute_stack_op (exp, exp + len, context, 0); + execute_stack_op (exp, exp + len, &orig_context, 0); break; } default: - abort (); + gcc_unreachable (); } context->cfa = cfa; @@ -1052,27 +1164,42 @@ uw_update_context_1 (struct _Unwind_Context *context, _Unwind_FrameState *fs) { case REG_UNSAVED: break; + case REG_SAVED_OFFSET: - context->reg[i] = cfa + fs->regs.reg[i].loc.offset; + _Unwind_SetGRPtr (context, i, + (void *) (cfa + fs->regs.reg[i].loc.offset)); break; + case REG_SAVED_REG: - context->reg[i] = orig_context.reg[fs->regs.reg[i].loc.reg]; + _Unwind_SetGRPtr + (context, i, + _Unwind_GetGRPtr (&orig_context, fs->regs.reg[i].loc.reg)); break; + case REG_SAVED_EXP: { const unsigned char *exp = fs->regs.reg[i].loc.exp; - _Unwind_Ptr len; + _Unwind_Word len; _Unwind_Ptr val; exp = read_uleb128 (exp, &len); val = execute_stack_op (exp, exp + len, &orig_context, (_Unwind_Ptr) cfa); - context->reg[i] = (void *) val; + _Unwind_SetGRPtr (context, i, (void *) val); } break; } + +#ifdef MD_FROB_UPDATE_CONTEXT + MD_FROB_UPDATE_CONTEXT (context, fs); +#endif } +/* CONTEXT describes the unwind state for a frame, and FS describes the FDE + of its caller. Update CONTEXT to refer to the caller as well. Note + that the args_size and lsda members are not updated here, but later in + uw_frame_state_for. */ + static void uw_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs) { @@ -1081,20 +1208,28 @@ uw_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs) /* Compute the return address now, since the return address column can change from frame to frame. */ context->ra = __builtin_extract_return_addr - ((void *) (_Unwind_Ptr) _Unwind_GetGR (context, fs->retaddr_column)); + (_Unwind_GetPtr (context, fs->retaddr_column)); } /* Fill in CONTEXT for top-of-stack. The only valid registers at this level will be the return address and the CFA. */ - -#define uw_init_context(CONTEXT) \ -do { \ - /* Do any necessary initialization to access arbitrary stack frames. \ - On the SPARC, this means flushing the register windows. */ \ - __builtin_unwind_init (); \ - uw_init_context_1 (CONTEXT, __builtin_dwarf_cfa (), \ - __builtin_return_address (0)); \ -} while (0) + +#define uw_init_context(CONTEXT) \ + do \ + { \ + /* Do any necessary initialization to access arbitrary stack frames. \ + On the SPARC, this means flushing the register windows. */ \ + __builtin_unwind_init (); \ + uw_init_context_1 (CONTEXT, __builtin_dwarf_cfa (), \ + __builtin_return_address (0)); \ + } \ + while (0) + +static inline void +init_dwarf_reg_size_table (void) +{ + __builtin_init_dwarf_reg_size_table (dwarf_reg_size_table); +} static void uw_init_context_1 (struct _Unwind_Context *context, @@ -1102,17 +1237,31 @@ uw_init_context_1 (struct _Unwind_Context *context, { void *ra = __builtin_extract_return_addr (__builtin_return_address (0)); _Unwind_FrameState fs; + _Unwind_SpTmp sp_slot; + _Unwind_Reason_Code code; memset (context, 0, sizeof (struct _Unwind_Context)); context->ra = ra; - if (uw_frame_state_for (context, &fs) != _URC_NO_REASON) - abort (); + code = uw_frame_state_for (context, &fs); + gcc_assert (code == _URC_NO_REASON); + +#if __GTHREADS + { + static __gthread_once_t once_regsizes = __GTHREAD_ONCE_INIT; + if (__gthread_once (&once_regsizes, init_dwarf_reg_size_table) != 0 + || dwarf_reg_size_table[0] == 0) + init_dwarf_reg_size_table (); + } +#else + if (dwarf_reg_size_table[0] == 0) + init_dwarf_reg_size_table (); +#endif /* Force the frame state to use the known cfa value. */ - context->cfa = outer_cfa; + _Unwind_SetSpColumn (context, outer_cfa, &sp_slot); fs.cfa_how = CFA_REG_OFFSET; - fs.cfa_reg = 0; + fs.cfa_reg = __builtin_dwarf_sp_column (); fs.cfa_offset = 0; uw_update_context_1 (context, &fs); @@ -1128,50 +1277,52 @@ uw_init_context_1 (struct _Unwind_Context *context, macro because __builtin_eh_return must be invoked in the context of our caller. */ -#define uw_install_context(CURRENT, TARGET) \ -do { \ - long offset = uw_install_context_1 ((CURRENT), (TARGET)); \ - void *handler = __builtin_frob_return_addr ((TARGET)->ra); \ - __builtin_eh_return (offset, handler); \ -} while (0) - -static inline void -init_dwarf_reg_size_table (void) -{ - __builtin_init_dwarf_reg_size_table (dwarf_reg_size_table); -} +#define uw_install_context(CURRENT, TARGET) \ + do \ + { \ + long offset = uw_install_context_1 ((CURRENT), (TARGET)); \ + void *handler = __builtin_frob_return_addr ((TARGET)->ra); \ + __builtin_eh_return (offset, handler); \ + } \ + while (0) static long uw_install_context_1 (struct _Unwind_Context *current, struct _Unwind_Context *target) { long i; + _Unwind_SpTmp sp_slot; -#if __GTHREADS - { - static __gthread_once_t once_regsizes = __GTHREAD_ONCE_INIT; - if (__gthread_once (&once_regsizes, init_dwarf_reg_size_table) != 0 - || dwarf_reg_size_table[0] == 0) - init_dwarf_reg_size_table (); - } -#else - if (dwarf_reg_size_table[0] == 0) - init_dwarf_reg_size_table (); -#endif + /* If the target frame does not have a saved stack pointer, + then set up the target's CFA. */ + if (!_Unwind_GetGRPtr (target, __builtin_dwarf_sp_column ())) + _Unwind_SetSpColumn (target, target->cfa, &sp_slot); for (i = 0; i < DWARF_FRAME_REGISTERS; ++i) { void *c = current->reg[i]; void *t = target->reg[i]; + if (t && c && t != c) memcpy (c, t, dwarf_reg_size_table[i]); } - /* We adjust SP by the difference between CURRENT and TARGET's CFA. */ - if (STACK_GROWS_DOWNWARD) - return target->cfa - current->cfa + target->args_size; - else - return current->cfa - target->cfa - target->args_size; + /* If the current frame doesn't have a saved stack pointer, then we + need to rely on EH_RETURN_STACKADJ_RTX to get our target stack + pointer value reloaded. */ + if (!_Unwind_GetGRPtr (current, __builtin_dwarf_sp_column ())) + { + void *target_cfa; + + target_cfa = _Unwind_GetPtr (target, __builtin_dwarf_sp_column ()); + + /* We adjust SP by the difference between CURRENT and TARGET's CFA. */ + if (STACK_GROWS_DOWNWARD) + return target_cfa - current->cfa + target->args_size; + else + return current->cfa - target_cfa - target->args_size; + } + return 0; } static inline _Unwind_Ptr @@ -1183,4 +1334,23 @@ uw_identify_context (struct _Unwind_Context *context) #include "unwind.inc" +#if defined (USE_GAS_SYMVER) && defined (SHARED) && defined (USE_LIBUNWIND_EXCEPTIONS) +alias (_Unwind_Backtrace); +alias (_Unwind_DeleteException); +alias (_Unwind_FindEnclosingFunction); +alias (_Unwind_ForcedUnwind); +alias (_Unwind_GetDataRelBase); +alias (_Unwind_GetTextRelBase); +alias (_Unwind_GetCFA); +alias (_Unwind_GetGR); +alias (_Unwind_GetIP); +alias (_Unwind_GetLanguageSpecificData); +alias (_Unwind_GetRegionStart); +alias (_Unwind_RaiseException); +alias (_Unwind_Resume); +alias (_Unwind_Resume_or_Rethrow); +alias (_Unwind_SetGR); +alias (_Unwind_SetIP); +#endif + #endif /* !USING_SJLJ_EXCEPTIONS */