/* DWARF2 exception handling and frame unwind runtime interface routines.
Copyright (C) 1997, 1998, 1999, 2000, 2001 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.
+ 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 "dwarf2.h"
#include "unwind.h"
+#include "unwind-pe.h"
#include "unwind-dw2-fde.h"
#include "gthr.h"
{
struct {
union {
- unsigned int reg;
+ _Unwind_Word reg;
_Unwind_Sword offset;
- unsigned char *exp;
+ const unsigned char *exp;
} loc;
enum {
REG_UNSAVED,
location expression. */
_Unwind_Sword cfa_offset;
_Unwind_Word cfa_reg;
- unsigned char *cfa_exp;
+ const unsigned char *cfa_exp;
enum {
CFA_UNSET,
CFA_REG_OFFSET,
/* The information we care about from the CIE/FDE. */
_Unwind_Personality_Fn personality;
- signed int data_align;
- unsigned int code_align;
+ _Unwind_Sword data_align;
+ _Unwind_Word code_align;
unsigned char retaddr_column;
- unsigned char addr_encoding;
+ unsigned char fde_encoding;
+ unsigned char lsda_encoding;
unsigned char saw_z;
- unsigned char saw_lsda;
+ void *eh_ptr;
} _Unwind_FrameState;
\f
-/* Decode the unsigned LEB128 constant at BUF into the variable pointed to
- by R, and return the new value of BUF. */
-
-static unsigned char *
-read_uleb128 (unsigned char *buf, _Unwind_Word *r)
-{
- unsigned shift = 0;
- _Unwind_Word result = 0;
-
- while (1)
- {
- unsigned char byte = *buf++;
- result |= (byte & 0x7f) << shift;
- if ((byte & 0x80) == 0)
- break;
- shift += 7;
- }
- *r = result;
- return buf;
-}
-
-/* Decode the signed LEB128 constant at BUF into the variable pointed to
- by R, and return the new value of BUF. */
-
-static unsigned char *
-read_sleb128 (unsigned char *buf, _Unwind_Sword *r)
-{
- unsigned shift = 0;
- _Unwind_Sword result = 0;
- unsigned char byte;
-
- while (1)
- {
- byte = *buf++;
- result |= (byte & 0x7f) << shift;
- shift += 7;
- if ((byte & 0x80) == 0)
- break;
- }
- if (shift < (sizeof (*r) * 8) && (byte & 0x40) != 0)
- result |= - (1 << shift);
-
- *r = result;
- return buf;
-}
-
/* Read unaligned data from the instruction buffer. */
union unaligned
} __attribute__ ((packed));
static inline void *
-read_pointer (void *p) { union unaligned *up = p; return up->p; }
+read_pointer (const void *p) { const union unaligned *up = p; return up->p; }
static inline int
-read_1u (void *p) { return *(unsigned char *)p; }
+read_1u (const void *p) { return *(const unsigned char *)p; }
static inline int
-read_1s (void *p) { return *(signed char *)p; }
+read_1s (const void *p) { return *(const signed char *)p; }
static inline int
-read_2u (void *p) { union unaligned *up = p; return up->u2; }
+read_2u (const void *p) { const union unaligned *up = p; return up->u2; }
static inline int
-read_2s (void *p) { union unaligned *up = p; return up->s2; }
+read_2s (const void *p) { const union unaligned *up = p; return up->s2; }
static inline unsigned int
-read_4u (void *p) { union unaligned *up = p; return up->u4; }
+read_4u (const void *p) { const union unaligned *up = p; return up->u4; }
static inline int
-read_4s (void *p) { union unaligned *up = p; return up->s4; }
+read_4s (const void *p) { const union unaligned *up = p; return up->s4; }
static inline unsigned long
-read_8u (void *p) { union unaligned *up = p; return up->u8; }
+read_8u (const void *p) { const union unaligned *up = p; return up->u8; }
static inline unsigned long
-read_8s (void *p) { union unaligned *up = p; return up->s8; }
-
-static unsigned char *
-read_encoded_pointer (unsigned char *p, unsigned char encoding,
- struct dwarf_eh_bases *bases, void **pptr)
-{
- signed long val;
- unsigned char *ret;
-
- switch (encoding & 0x0f)
- {
- case DW_EH_PE_absptr:
- val = (_Unwind_Ptr) read_pointer (p);
- ret = p + sizeof (void *);
- break;
-
- case DW_EH_PE_uleb128:
- ret = read_uleb128 (p, &val);
- break;
- case DW_EH_PE_sleb128:
- ret = read_sleb128 (p, &val);
- break;
-
- case DW_EH_PE_udata2:
- val = read_2u (p);
- ret = p + 2;
- break;
- case DW_EH_PE_udata4:
- val = read_4u (p);
- ret = p + 4;
- break;
- case DW_EH_PE_udata8:
- val = read_8u (p);
- ret = p + 8;
- break;
-
- case DW_EH_PE_sdata2:
- val = read_2s (p);
- ret = p + 2;
- break;
- case DW_EH_PE_sdata4:
- val = read_4s (p);
- ret = p + 4;
- break;
- case DW_EH_PE_sdata8:
- val = read_8s (p);
- ret = p + 8;
- break;
-
- default:
- abort ();
- }
-
- if (val != 0)
- switch (encoding & 0xf0)
- {
- case DW_EH_PE_absptr:
- break;
- case DW_EH_PE_pcrel:
- val += (_Unwind_Ptr) p;
- break;
- case DW_EH_PE_textrel:
- val += (_Unwind_Ptr) bases->tbase;
- break;
- case DW_EH_PE_datarel:
- val += (_Unwind_Ptr) bases->dbase;
- break;
- case DW_EH_PE_funcrel:
- val += (_Unwind_Ptr) bases->func;
- break;
- default:
- abort ();
- }
-
- *pptr = (void *) (_Unwind_Ptr) val;
- return ret;
-}
+read_8s (const void *p) { const union unaligned *up = p; return up->s8; }
\f
/* Get the value of register REG as saved in CONTEXT. */
unit F belongs to. Return a pointer to the byte after the augmentation,
or NULL if we encountered an undecipherable augmentation. */
-static unsigned char *
+static const unsigned char *
extract_cie_info (struct dwarf_cie *cie, struct _Unwind_Context *context,
_Unwind_FrameState *fs)
{
- unsigned char *aug = cie->augmentation;
- unsigned char *p = aug + strlen (aug) + 1;
- unsigned char *ret = NULL;
- _Unwind_Word code_align;
- _Unwind_Sword data_align;
+ const unsigned char *aug = cie->augmentation;
+ const unsigned char *p = aug + strlen (aug) + 1;
+ const unsigned char *ret = NULL;
+ _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, &code_align);
- p = read_sleb128 (p, &data_align);
- fs->code_align = code_align;
- fs->data_align = data_align;
+ p = read_uleb128 (p, &fs->code_align);
+ p = read_sleb128 (p, &fs->data_align);
fs->retaddr_column = *p++;
+ fs->lsda_encoding = DW_EH_PE_omit;
/* If the augmentation starts with 'z', then a uleb128 immediately
follows containing the length of the augmentation field following
the size. */
if (*aug == 'z')
{
- _Unwind_Word i;
- p = read_uleb128 (p, &i);
- ret = p + i;
+ p = read_uleb128 (p, &utmp);
+ ret = p + utmp;
fs->saw_z = 1;
++aug;
/* Iterate over recognized augmentation subsequences. */
while (*aug != '\0')
{
- /* "eh" was used by g++ v2; recognize and skip. */
- if (aug[0] == 'e' && aug[1] == 'h')
+ /* "L" indicates a byte showing how the LSDA pointer is encoded. */
+ if (aug[0] == 'L')
{
- p += sizeof (void *);
- aug += 2;
+ fs->lsda_encoding = *p++;
+ aug += 1;
}
- /* "R" indicates a byte indicating how addresses are encoded. */
+ /* "R" indicates a byte indicating how FDE addresses are encoded. */
else if (aug[0] == 'R')
{
- fs->addr_encoding = *p++;
+ fs->fde_encoding = *p++;
aug += 1;
}
- /* "P" indicates a personality routine in the CIE augmentation
- and an lsda pointer in the FDE augmentation. */
+ /* "P" indicates a personality routine in the CIE augmentation. */
else if (aug[0] == 'P')
{
- p = read_encoded_pointer (p, fs->addr_encoding, &context->bases,
- (void **) &fs->personality);
- fs->saw_lsda = 1;
+ p = read_encoded_value (context, *p, p + 1,
+ (_Unwind_Ptr *) &fs->personality);
aug += 1;
}
onto the stack to start. */
static _Unwind_Word
-execute_stack_op (unsigned char *op_ptr, unsigned char *op_end,
+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;
while (op_ptr < op_end)
{
enum dwarf_location_atom op = *op_ptr++;
- _Unwind_Word result, reg;
- _Unwind_Sword offset;
+ _Unwind_Word result, reg, utmp;
+ _Unwind_Sword offset, stmp;
switch (op)
{
op_ptr = read_uleb128 (op_ptr, &result);
break;
case DW_OP_consts:
- op_ptr = read_sleb128 (op_ptr, &offset);
- result = offset;
+ op_ptr = read_sleb128 (op_ptr, &stmp);
+ result = stmp;
break;
case DW_OP_reg0:
result = ~result;
break;
case DW_OP_plus_uconst:
- op_ptr = read_uleb128 (op_ptr, ®);
- result += reg;
+ op_ptr = read_uleb128 (op_ptr, &utmp);
+ result += utmp;
break;
+
+ default:
+ abort ();
}
break;
case DW_OP_ne:
result = (_Unwind_Sword)first != (_Unwind_Sword)second;
break;
+
+ default:
+ abort ();
}
}
break;
CIE info, and the PC range to evaluate. */
static void
-execute_cfa_program (unsigned char *insn_ptr, unsigned char *insn_end,
- struct _Unwind_Context *context, _Unwind_FrameState *fs)
+execute_cfa_program (const unsigned char *insn_ptr,
+ const unsigned char *insn_end,
+ struct _Unwind_Context *context,
+ _Unwind_FrameState *fs)
{
struct frame_state_reg_info *unused_rs = NULL;
while (insn_ptr < insn_end && fs->pc < context->ra)
{
unsigned char insn = *insn_ptr++;
- _Unwind_Word reg, uoffset;
- _Unwind_Sword offset;
+ _Unwind_Word reg, utmp;
+ _Unwind_Sword offset, stmp;
if (insn & DW_CFA_advance_loc)
fs->pc += (insn & 0x3f) * fs->code_align;
else if (insn & DW_CFA_offset)
{
reg = insn & 0x3f;
- insn_ptr = read_uleb128 (insn_ptr, &uoffset);
- offset = (_Unwind_Sword)uoffset * fs->data_align;
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
+ offset = (_Unwind_Sword)utmp * fs->data_align;
fs->regs.reg[reg].how = REG_SAVED_OFFSET;
fs->regs.reg[reg].loc.offset = offset;
}
else switch (insn)
{
case DW_CFA_set_loc:
- insn_ptr = read_encoded_pointer (insn_ptr, fs->addr_encoding,
- &context->bases, &fs->pc);
+ insn_ptr = read_encoded_value (context, fs->fde_encoding,
+ insn_ptr, (_Unwind_Ptr *) &fs->pc);
break;
case DW_CFA_advance_loc1:
- fs->pc += read_1u (insn_ptr);
+ fs->pc += read_1u (insn_ptr) * fs->code_align;
insn_ptr += 1;
break;
case DW_CFA_advance_loc2:
- fs->pc += read_2u (insn_ptr);
+ fs->pc += read_2u (insn_ptr) * fs->code_align;
insn_ptr += 2;
break;
case DW_CFA_advance_loc4:
- fs->pc += read_4u (insn_ptr);
+ fs->pc += read_4u (insn_ptr) * fs->code_align;
insn_ptr += 4;
break;
case DW_CFA_offset_extended:
insn_ptr = read_uleb128 (insn_ptr, ®);
- insn_ptr = read_uleb128 (insn_ptr, &uoffset);
- offset = (_Unwind_Sword)uoffset * fs->data_align;
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
+ offset = (_Unwind_Sword)utmp * fs->data_align;
fs->regs.reg[reg].how = REG_SAVED_OFFSET;
fs->regs.reg[reg].loc.offset = offset;
break;
unused_rs = unused_rs->prev;
}
else
- new_rs = alloca (sizeof (struct frame_state_reg_info));
+ new_rs = __builtin_alloca (sizeof (struct frame_state_reg_info));
*new_rs = fs->regs;
fs->regs.prev = new_rs;
case DW_CFA_def_cfa:
insn_ptr = read_uleb128 (insn_ptr, &fs->cfa_reg);
- insn_ptr = read_uleb128 (insn_ptr, &uoffset);
- fs->cfa_offset = uoffset;
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
+ fs->cfa_offset = utmp;
fs->cfa_how = CFA_REG_OFFSET;
break;
break;
case DW_CFA_def_cfa_offset:
- insn_ptr = read_uleb128 (insn_ptr, &uoffset);
- fs->cfa_offset = uoffset;
+ 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, &uoffset);
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
fs->cfa_exp = insn_ptr;
fs->cfa_how = CFA_EXP;
- insn_ptr += uoffset;
+ insn_ptr += utmp;
break;
case DW_CFA_expression:
insn_ptr = read_uleb128 (insn_ptr, ®);
- insn_ptr = read_uleb128 (insn_ptr, &uoffset);
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
fs->regs.reg[reg].how = REG_SAVED_EXP;
fs->regs.reg[reg].loc.exp = insn_ptr;
- insn_ptr += uoffset;
+ insn_ptr += utmp;
break;
/* From the 2.1 draft. */
case DW_CFA_offset_extended_sf:
insn_ptr = read_uleb128 (insn_ptr, ®);
- insn_ptr = read_sleb128 (insn_ptr, &offset);
- offset *= fs->data_align;
+ insn_ptr = read_sleb128 (insn_ptr, &stmp);
+ offset = stmp * fs->data_align;
fs->regs.reg[reg].how = REG_SAVED_OFFSET;
fs->regs.reg[reg].loc.offset = offset;
break;
break;
case DW_CFA_def_cfa_offset_sf:
- insn_ptr = read_uleb128 (insn_ptr, &fs->cfa_offset);
+ insn_ptr = read_sleb128 (insn_ptr, &fs->cfa_offset);
/* cfa_how deliberately not set. */
break;
/* Obsoleted by DW_CFA_offset_extended_sf, but used by
older PowerPC code. */
insn_ptr = read_uleb128 (insn_ptr, ®);
- insn_ptr = read_uleb128 (insn_ptr, &uoffset);
- offset = (_Unwind_Sword)uoffset * fs->data_align;
+ insn_ptr = read_uleb128 (insn_ptr, &utmp);
+ offset = (_Unwind_Word)utmp * fs->data_align;
fs->regs.reg[reg].how = REG_SAVED_OFFSET;
fs->regs.reg[reg].loc.offset = -offset;
break;
{
struct dwarf_fde *fde;
struct dwarf_cie *cie;
- unsigned char *aug, *insn, *end;
+ const unsigned char *aug, *insn, *end;
memset (fs, 0, sizeof (*fs));
context->args_size = 0;
{
/* 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. */
+ not provide a personality routine or LSDA. */
#ifdef MD_FALLBACK_FRAME_STATE_FOR
MD_FALLBACK_FRAME_STATE_FOR (context, fs, success);
return _URC_END_OF_STACK;
#endif
}
- context->bases.func = fde->pc_begin;
- fs->pc = fde->pc_begin;
+ fs->pc = context->bases.func;
cie = get_cie (fde);
insn = extract_cie_info (cie, context, fs);
/* Locate augmentation for the fde. */
aug = (unsigned char *)fde + sizeof (*fde);
+ aug += 2 * size_of_encoded_value (fs->fde_encoding);
insn = NULL;
if (fs->saw_z)
{
aug = read_uleb128 (aug, &i);
insn = aug + i;
}
- if (fs->saw_lsda)
- aug = read_encoded_pointer (aug, fs->addr_encoding,
- &context->bases, &context->lsda);
+ if (fs->lsda_encoding != DW_EH_PE_omit)
+ aug = read_encoded_value (context, fs->lsda_encoding, aug,
+ (_Unwind_Ptr *) &context->lsda);
/* Then the insns in the FDE up to our target PC. */
if (insn == NULL)
return _URC_NO_REASON;
}
+\f
+typedef struct frame_state
+{
+ void *cfa;
+ void *eh_ptr;
+ long cfa_offset;
+ long args_size;
+ long reg_or_offset[DWARF_FRAME_REGISTERS+1];
+ unsigned short cfa_reg;
+ unsigned short retaddr_column;
+ char saved[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 < 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;
+}
+\f
static void
uw_update_context_1 (struct _Unwind_Context *context, _Unwind_FrameState *fs)
{
CFA calculation is so complicated as to require a stack program
that this will not be a problem. */
{
- unsigned char *exp = fs->cfa_exp;
+ const unsigned char *exp = fs->cfa_exp;
_Unwind_Word len;
exp = read_uleb128 (exp, &len);
break;
case REG_SAVED_EXP:
{
- unsigned char *exp = fs->regs.reg[i].loc.exp;
+ const unsigned char *exp = fs->regs.reg[i].loc.exp;
_Unwind_Word len;
_Unwind_Ptr val;