X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Funwind-dw2.c;h=4ffdd02cd20aa1838323b5f6ac4fbffd9ab3469e;hb=300ed5f295507ceff5167189acfb606174cf2a9c;hp=3011bc7341f396fad76227b90f2794c9b450911a;hpb=b92c85dcf12a7c7474590b5d3b085c3b875ca36f;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/unwind-dw2.c b/gcc/unwind-dw2.c index 3011bc7341f..4ffdd02cd20 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, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, 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 -/* This is the register and unwind state for a particular frame. */ +#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 + 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,60 +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; -} _Unwind_FrameState; - /* Read unaligned data from the instruction buffer. */ union unaligned @@ -125,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; } @@ -153,8 +122,42 @@ 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); + if (index >= (int) sizeof(dwarf_reg_size_table)) + abort (); + 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; + + if (size == sizeof(_Unwind_Word)) + return * (_Unwind_Word *) ptr; + + abort (); +} + +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. */ @@ -162,7 +165,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); + if (index >= (int) sizeof(dwarf_reg_size_table)) + abort (); + size = dwarf_reg_size_table[index]; + ptr = context->reg[index]; + + if (size == sizeof(_Unwind_Ptr)) + * (_Unwind_Ptr *) ptr = val; + else if (size == sizeof(_Unwind_Word)) + * (_Unwind_Word *) ptr = val; + else + abort (); +} + +/* 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. */ @@ -193,6 +228,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) @@ -206,25 +252,41 @@ _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. */ + if (aug[0] == 'e' && aug[1] == 'h') + { + fs->eh_ptr = read_pointer (p); + p += sizeof (void *); + aug += 2; + } /* 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 @@ -232,8 +294,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; @@ -242,15 +304,8 @@ extract_cie_info (struct dwarf_cie *cie, struct _Unwind_Context *context, /* Iterate over recognized augmentation subsequences. */ while (*aug != '\0') { - /* "eh" was used by g++ v2; recognize and skip. */ - if (aug[0] == 'e' && aug[1] == 'h') - { - p += sizeof (void *); - aug += 2; - } - /* "L" indicates a byte showing how the LSDA pointer is encoded. */ - else if (aug[0] == 'L') + if (aug[0] == 'L') { fs->lsda_encoding = *p++; aug += 1; @@ -288,7 +343,7 @@ static _Unwind_Word execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end, struct _Unwind_Context *context, _Unwind_Word initial) { - _Unwind_Word stack[64]; /* ??? Assume this is enough. */ + _Unwind_Word stack[64]; /* ??? Assume this is enough. */ int stack_elt; stack[0] = initial; @@ -297,9 +352,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) { @@ -376,12 +430,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: @@ -419,7 +472,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; @@ -455,12 +508,12 @@ 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; @@ -518,14 +571,14 @@ execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end, { 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: @@ -557,9 +610,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: + abort (); } break; @@ -570,6 +626,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: @@ -579,65 +639,68 @@ 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]; + if ((stack_elt -= 2) < 0) + abort (); + 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: + abort (); + } } break; @@ -666,7 +729,7 @@ execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end, /* Most things push a result value. */ if ((size_t) stack_elt >= sizeof(stack)/sizeof(*stack)) abort (); - stack[++stack_elt] = result; + stack[stack_elt++] = result; no_push:; } @@ -693,27 +756,34 @@ 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) { @@ -736,33 +806,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; @@ -789,60 +866,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; @@ -856,18 +929,18 @@ 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: @@ -876,28 +949,33 @@ execute_cfa_program (const unsigned char *insn_ptr, } } +/* 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 @@ -916,12 +994,12 @@ 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; } @@ -937,7 +1015,85 @@ uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs) return _URC_NO_REASON; } + +typedef struct frame_state +{ + void *cfa; + void *eh_ptr; + long cfa_offset; + long args_size; + long reg_or_offset[PRE_GCC3_DWARF_FRAME_REGISTERS+1]; + unsigned short cfa_reg; + unsigned short retaddr_column; + char saved[PRE_GCC3_DWARF_FRAME_REGISTERS+1]; +} frame_state; + +struct frame_state * __frame_state_for (void *, struct frame_state *); + +/* Called from pre-G++ 3.0 __throw to find the registers to restore for + a given PC_TARGET. The caller should allocate a local variable of + `struct frame_state' and pass its address to STATE_IN. */ + +struct frame_state * +__frame_state_for (void *pc_target, struct frame_state *state_in) +{ + struct _Unwind_Context context; + _Unwind_FrameState fs; + int reg; + + memset (&context, 0, sizeof (struct _Unwind_Context)); + context.ra = pc_target + 1; + + if (uw_frame_state_for (&context, &fs) != _URC_NO_REASON) + return 0; + + /* We have no way to pass a location expression for the CFA to our + caller. It wouldn't understand it anyway. */ + if (fs.cfa_how == CFA_EXP) + return 0; + + 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]) + { + case REG_SAVED_REG: + state_in->reg_or_offset[reg] = fs.regs.reg[reg].loc.reg; + break; + case REG_SAVED_OFFSET: + state_in->reg_or_offset[reg] = fs.regs.reg[reg].loc.offset; + break; + default: + state_in->reg_or_offset[reg] = 0; + break; + } + } + + state_in->cfa_offset = fs.cfa_offset; + state_in->cfa_reg = fs.cfa_reg; + state_in->retaddr_column = fs.retaddr_column; + state_in->args_size = context.args_size; + state_in->eh_ptr = fs.eh_ptr; + 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 if (size == sizeof(_Unwind_Word)) + tmp_sp->word = (_Unwind_Ptr) cfa; + else + abort (); + _Unwind_SetGRPtr (context, __builtin_dwarf_sp_column (), tmp_sp); +} static void uw_update_context_1 (struct _Unwind_Context *context, _Unwind_FrameState *fs) @@ -946,34 +1102,45 @@ 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; } @@ -988,27 +1155,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) { @@ -1017,20 +1199,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, @@ -1038,6 +1228,7 @@ 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; memset (context, 0, sizeof (struct _Unwind_Context)); context->ra = ra; @@ -1045,10 +1236,22 @@ uw_init_context_1 (struct _Unwind_Context *context, if (uw_frame_state_for (context, &fs) != _URC_NO_REASON) abort (); +#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); @@ -1064,50 +1267,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 @@ -1119,4 +1324,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 */