This is the mail archive of the libc-alpha@sources.redhat.com mailing list for the glibc project.


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

Re: PATCH: Share the dwarf2 unwind code between glibc and gcc 3.0


Richard Henderson <rth@redhat.com> writes:

> > 	* unwind-pe.h: Include <gccframe.h> if _LIBC is defined.
> >	...
> 
> What in the world are you doing?  You shouldn't be touching this file
> at all.  Why are you moving things away from unwind-dw2-fde.h?

IMHO HJ is heading down the wrong track.  I don't see why we need to
rip bits of code from both GCC 3.x and GCC 2.x.  AFAICT the code in
GCC 3.x (unwind-dw.c) is perfectly able to handle the frame info
generated by GCC 2.x.  The only thing it doesn't do is recognizing
the info about GCC 2.x exception handlers.  It doesn't need to since
mixing C++ code from GCC 2.x with C++ code from GCC 3.x isn't going to
work, and just skips the GCC 2.x exception handler info.

Changing the code such that it does find the GCC 2.x exception handler
info is almost trivial.  I implemented it, and it works fine with GCC
2.95.2.  I already posted my code once.  Attached is a slightly
improved version.  The difference between this code and the origional
code from gcc-3_0-branch are an additional member of `struct
_Unwind_Context' and a bit of code to fetch the GCC 2.x exception
handler info in extract_cie_info().  The missing headers can be taken
verbatim from the GCC 3.x sources.  Of course integrating this into
glibc would still involve a bit of work, but I'm not going to invest
my time in it while HJ is still pushing his patch.

There is one thing my patch doesn't do: fixing up the binary
compatibility problem with the RedHat GCC 2.96.  HJ has a clever
solution for the problem but it's a wee bit fragile.  If it really is
a poblem, it can be fixed later.

>From a release engineering standpoint it would be great if my
implementation of __frame_state_for could be added back to libgcc.
That would avoid the problem of syncing up code in glibc with GCC, and
it doesn't add too much bloat.  We can even make sure that it is only
compiled on Linux and the Hurd.

I don't see any point in adding support for GCC 3.x frame info to GCC
2.95.x.  It has always been the case that if you mix code compiled
with an old and a new GCC, you should do your final link with the new
GCC.  And mixing code compiled with -fexceptions with a new version of
GCC with C++ code compiled with an older version of GCC cannot be
expected to work either; let's keep the special case of glibc the
exception to that rule.

Thanks,

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;

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