This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: PATCH: Add __frame_state_for for gcc 3.0.1


From: Joe Buck <jbuck@synopsys.COM>
Date: Fri, 6 Jul 2001 11:50:55 -0700 (PDT)

> > I think it is a doable. Basically, we have to put a complete set of
> > frame unwind functions in glibc, which supports the mixed v2 and v3
> > frames. It may take a while. I wish they were in gcc :-(.
>
> I'm sure it's doable, but you then have to carry around the cruft forever.
> Given the fact that we won't support mixing gcc2 and gcc3 C++ code, how
> does this benefit a user that has gcc3 and a gcc3-compiled glibc?  He/she
> would have to have some other C code, compiled with gcc2, that has
> exception handling, and then mix it with gcc3 C++ code.  Are we solving
> a problem that really exists, or only a theoretical problem?

__frame_state_for in glibc is there for the benefit of C++ code
compiled with gcc2.  Therefore it needs to understand the full EH info
generated by gcc2 plus the frame info generated by gcc3 for C code
compiled with -fexceptions.  We have such C code in glibc.  Functions
like qsort() that use a callback function need to be compiled with
-fexceptions, otherwise throwing exceptions from the comparison
function invoked by qsort() won't work.

This doesn't involve carrying around much cruft.  The only stuff
that's missing in the gcc3 frame state code is looking up gcc2
exception handlers in "eh" augmentations.  Adding support for that is
almost trivial.

I think we base __frame_state_info on code from gcc3.  Attached is an
implementation based on unwind-dw2.c from gcc3.  It uses some headers
from gcc3 which I didn't include in this message.

Mark


/* 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.

   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
   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.

   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.  */

#include <stdlib.h>
#include <string.h>

#include "dwarf2.h"
#include "unwind.h"
#include "unwind-pe.h"
#include "unwind-dw2-fde.h"

/* FIXME: This is for the i386.  When this code is integrated in
   glibc, this should be moved in a system dependent header of some
   sort.  */
#define FIRST_PSEUDO_REGISTER 17

/* 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
#endif

/* This is the register and unwind state for a particular frame.  */
struct _Unwind_Context
{
  void *reg[DWARF_FRAME_REGISTERS+1];
  void *cfa;
  void *ra;
  void *lsda;
  struct dwarf_eh_bases bases;
  _Unwind_Word args_size;
  void *eh_ptr;			/* Not in GCC 3.0.  */
};


/* 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
{
  void *p;
  unsigned u2 __attribute__ ((mode (HI)));
  unsigned u4 __attribute__ ((mode (SI)));
  unsigned u8 __attribute__ ((mode (DI)));
  signed s2 __attribute__ ((mode (HI)));
  signed s4 __attribute__ ((mode (SI)));
  signed s8 __attribute__ ((mode (DI)));
} __attribute__ ((packed));

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; }

static inline int
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; }

static inline int
read_2s (const void *p) { const union unaligned *up = p; return up->s2; }

static inline unsigned int
read_4u (const void *p) { const union unaligned *up = p; return up->u4; }

static inline int
read_4s (const void *p) { const union unaligned *up = p; return up->s4; }

static inline unsigned long
read_8u (const void *p) { const union unaligned *up = p; return up->u8; }

static inline unsigned long
read_8s (const void *p) { const union unaligned *up = p; return up->s8; }

_Unwind_Ptr
_Unwind_GetRegionStart (struct _Unwind_Context *context)
{
  return (_Unwind_Ptr) context->bases.func;
}

#ifndef __ia64__
_Unwind_Ptr
_Unwind_GetDataRelBase (struct _Unwind_Context *context)
{
  return (_Unwind_Ptr) context->bases.dbase;
}

_Unwind_Ptr
_Unwind_GetTextRelBase (struct _Unwind_Context *context)
{
  return (_Unwind_Ptr) context->bases.tbase;
}
#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,
		  _Unwind_FrameState *fs)
{
  const unsigned char *aug = cie->augmentation;
  const unsigned char *p = aug + strlen (aug) + 1;
  const unsigned char *ret = NULL;
  _Unwind_Ptr tmp;

  /* We must properly recognize "eh" to support g++ v2.  */
  if (aug[0] == 'e' && aug[1] == 'h')
    {
      context->eh_ptr = read_pointer (p);
      p += sizeof (void *);
      aug += 2;
    }
  else
    context->eh_ptr = 0;

  /* 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++;
  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')
    {
      p = read_uleb128 (p, &tmp);
      ret = p + tmp;

      fs->saw_z = 1;
      ++aug;
    }

  /* Iterate over recognized augmentation subsequences.  */
  while (*aug != '\0')
    {
      /* "L" indicates a byte showing how the LSDA pointer is encoded.  */
      if (aug[0] == 'L')
	{
	  fs->lsda_encoding = *p++;
	  aug += 1;
	}

      /* "R" indicates a byte indicating how FDE addresses are encoded.  */
      else if (aug[0] == 'R')
	{
	  fs->fde_encoding = *p++;
	  aug += 1;
	}

      /* "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);
	  aug += 1;
	}

      /* Otherwise we have an unknown augmentation string.
	 Bail unless we saw a 'z' prefix.  */
      else
	return ret;
    }

  return ret ? ret : p;
}


/* Decode DWARF 2 call frame information. Takes pointers the
   instruction sequence to decode, current register information and
   CIE info, and the PC range to evaluate.  */

static void
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;

  /* Don't allow remember/restore between CIE and FDE programs.  */
  fs->regs.prev = NULL;

  while (insn_ptr < insn_end && fs->pc < context->ra)
    {
      unsigned char insn = *insn_ptr++;
      _Unwind_Word reg;
      _Unwind_Sword offset;
      _Unwind_Ptr ptrtmp;

      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, &ptrtmp);
	  offset = ptrtmp * fs->data_align;
	  fs->regs.reg[reg].how = REG_SAVED_OFFSET;
	  fs->regs.reg[reg].loc.offset = offset;
	}
      else if (insn & DW_CFA_restore)
	{
	  reg = insn & 0x3f;
	  fs->regs.reg[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);
	  break;

	case DW_CFA_advance_loc1:
	  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->code_align;
	  insn_ptr += 2;
	  break;
	case DW_CFA_advance_loc4:
	  fs->pc += read_4u (insn_ptr) * fs->code_align;
	  insn_ptr += 4;
	  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;
	  break;

	case DW_CFA_restore_extended:
	  insn_ptr = read_uleb128 (insn_ptr, &ptrtmp); reg = ptrtmp;
	  fs->regs.reg[reg].how = REG_UNSAVED;
	  break;

	case DW_CFA_undefined:
	case DW_CFA_same_value:
	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;
	  }
	  break;
      
	case DW_CFA_remember_state:
	  {
	    struct frame_state_reg_info *new_rs;
	    if (unused_rs)
	      {
		new_rs = unused_rs;
		unused_rs = unused_rs->prev;
	      }
	    else
	      new_rs = alloca (sizeof (struct frame_state_reg_info));

	    *new_rs = fs->regs;
	    fs->regs.prev = new_rs;
	  }
	  break;

	case DW_CFA_restore_state:
	  {
	    struct frame_state_reg_info *old_rs = fs->regs.prev;
	    fs->regs = *old_rs;
	    old_rs->prev = unused_rs;
	    unused_rs = old_rs;
	  }
	  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;
	  fs->cfa_how = CFA_REG_OFFSET;
	  break;

	case DW_CFA_def_cfa_register:
	  insn_ptr = read_uleb128 (insn_ptr, &ptrtmp);
	  fs->cfa_reg = ptrtmp;
	  fs->cfa_how = CFA_REG_OFFSET;
	  break;

	case DW_CFA_def_cfa_offset:
	  insn_ptr = read_uleb128 (insn_ptr, &ptrtmp);
	  fs->cfa_offset = ptrtmp;
	  /* 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;
	  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;
	  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;
	  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;
	  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;
	  /* cfa_how deliberately not set.  */
	  break;

	case DW_CFA_GNU_window_save:
	  /* ??? Hardcoded for SPARC register window configuration.  */
	  for (reg = 16; reg < 32; ++reg)
	    {
	      fs->regs.reg[reg].how = REG_SAVED_OFFSET;
	      fs->regs.reg[reg].loc.offset = (reg - 16) * sizeof (void *);
	    }
	  break;

	case DW_CFA_GNU_args_size:
	  insn_ptr = read_uleb128 (insn_ptr, &ptrtmp);
	  context->args_size = ptrtmp;
	  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;
	  break;

	default:
	  abort ();
	}
    }
}

static _Unwind_Reason_Code
uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs)
{
  struct dwarf_fde *fde;
  struct dwarf_cie *cie;
  const unsigned char *aug, *insn, *end;

  memset (fs, 0, sizeof (*fs));
  context->args_size = 0;
  context->lsda = 0;

  fde = _Unwind_Find_FDE (context->ra - 1, &context->bases);
  if (fde == NULL)
    {
      /* 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;
#else
      return _URC_END_OF_STACK;
#endif
    }

  fs->pc = context->bases.func;

  cie = get_cie (fde);
  insn = extract_cie_info (cie, context, fs);
  if (insn == NULL)
    /* CIE contained unknown augmentation.  */
    return _URC_FATAL_PHASE1_ERROR;

  /* First decode all the insns in the CIE.  */
  end = (unsigned char *) next_fde ((struct dwarf_fde *) cie);
  execute_cfa_program (insn, end, 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)
    {
      _Unwind_Ptr 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);

  /* Then the insns in the FDE up to our target PC.  */
  if (insn == NULL)
    insn = aug;
  end = (unsigned char *) next_fde (fde);
  execute_cfa_program (insn, end, context, fs);

  return _URC_NO_REASON;
}


typedef struct frame_state
{
  void *cfa;
  void *eh_ptr;
  long cfa_offset;
  long args_size;
  long reg_or_offset[FIRST_PSEUDO_REGISTER+1];
  unsigned short cfa_reg;
  unsigned short retaddr_column;
  char saved[FIRST_PSEUDO_REGISTER+1];
} frame_state;


/* Called from __throw to find the registers to restore for a given
   PC_TARGET.  The caller should allocate a local variable of `struct
   frame_state' (declared in frame.h) 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;

  for (reg = 0; reg < FIRST_PSEUDO_REGISTER + 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 = context.eh_ptr;

  return state_in;
}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]