This is the mail archive of the
libc-alpha@sources.redhat.com
mailing list for the glibc project.
Re: PATCH: Support gcc v3 unwind frame in gcc pre 3.0
- To: gcc at gcc dot gnu dot org, GNU C Library <libc-alpha at sourceware dot cygnus dot com>
- Subject: Re: PATCH: Support gcc v3 unwind frame in gcc pre 3.0
- From: "H . J . Lu" <hjl at lucon dot org>
- Date: Mon, 9 Jul 2001 10:49:39 -0700
- References: <20010709010717.A2591@lucon.org>
On Mon, Jul 09, 2001 at 01:07:17AM -0700, H . J . Lu wrote:
> This is not a patch against gcc 2.95. It is for gcc 2.96 from RedHat
> 7.1. But the idea for gcc 2.95 is the same. Combining frame-dwarf2.c
> from gcc 2.96 as well as unwind-dw2-fde.c, unwind-dw2-fde.h,
> unwind-pe.h and unwind-dw2.c fro gcc 3.0, we can put a mixed gcc v2
> and v3 frame unwinder in glibc. I will post my updated glibc patch
> later.
>
Here is a patch for gcc 2.95. It seems to work for me.
H.J.
-----
2001-07-09 H.J. Lu (hjl@gnu.org)
* dwarf2.h: Updated from gcc 3.0.
* frame.c: Include glibc heade files instead if _LIBC is
defined. Support the frame unwinder only build in glibc.
(cie_info): Add pc, fde_encoding, lsda_encoding and saw_z.
(extract_cie_info): Recognize gcc v3 unwind frame.
(execute_cfa_insn): Remove the last argument. `pc' is passed
in `info' now.
(__frame_state_for): Call _Unwind_GetFrameForV2 for gcc v3
unwind frame if NEED__Unwind_GetFrameForV2 is defined.
2001-07-09 H.J. Lu (hjl@gnu.org)
* dwarf2.h: Updated from gcc 3.0.
* frame.c: Include glibc heade files instead if _LIBC is
defined. Support the frame unwinder only build in glibc.
(cie_info): Add pc, fde_encoding, lsda_encoding and saw_z.
(extract_cie_info): Recognize gcc v3 unwind frame.
(execute_cfa_insn): Remove the last argument. `pc' is passed
in `info' now.
(__frame_state_for): Call _Unwind_GetFrameForV2 for gcc v3
unwind frame if NEED__Unwind_GetFrameForV2 is defined.
--- gcc/dwarf2.h.mixed Fri Dec 22 06:24:59 2000
+++ gcc/dwarf2.h Mon Jul 9 09:15:09 2001
@@ -88,7 +88,9 @@ enum dwarf_tag
/* GNU extensions */
DW_TAG_format_label = 0x4101, /* for FORTRAN 77 and Fortran 90 */
DW_TAG_function_template = 0x4102, /* for C++ */
- DW_TAG_class_template = 0x4103 /* for C++ */
+ DW_TAG_class_template = 0x4103, /* for C++ */
+ DW_TAG_GNU_BINCL = 0x4104,
+ DW_TAG_GNU_EINCL = 0x4105
};
#define DW_TAG_lo_user 0x4080
@@ -532,6 +534,7 @@ enum dwarf_source_language
DW_LANG_Fortran90 = 0x0008,
DW_LANG_Pascal83 = 0x0009,
DW_LANG_Modula2 = 0x000a,
+ DW_LANG_Java = 0x000b,
DW_LANG_Mips_Assembler = 0x8001
};
@@ -549,3 +552,27 @@ enum dwarf_macinfo_record_type
DW_MACINFO_end_file = 4,
DW_MACINFO_vendor_ext = 255
};
+
+
+/* @@@ For use with GNU frame unwind information. */
+
+#define DW_EH_PE_absptr 0x00
+#define DW_EH_PE_omit 0xff
+
+#define DW_EH_PE_uleb128 0x01
+#define DW_EH_PE_udata2 0x02
+#define DW_EH_PE_udata4 0x03
+#define DW_EH_PE_udata8 0x04
+#define DW_EH_PE_sleb128 0x09
+#define DW_EH_PE_sdata2 0x0A
+#define DW_EH_PE_sdata4 0x0B
+#define DW_EH_PE_sdata8 0x0C
+#define DW_EH_PE_signed 0x08
+
+#define DW_EH_PE_pcrel 0x10
+#define DW_EH_PE_textrel 0x20
+#define DW_EH_PE_datarel 0x30
+#define DW_EH_PE_funcrel 0x40
+#define DW_EH_PE_aligned 0x50
+
+#define DW_EH_PE_indirect 0x80
--- gcc/frame.c.mixed Thu Jan 25 06:03:15 2001
+++ gcc/frame.c Mon Jul 9 10:13:42 2001
@@ -31,6 +31,25 @@ Boston, MA 02111-1307, USA. */
compiled for the target, and hence definitions concerning only the host
do not apply. */
+#ifdef _LIBC
+#include <stdlib.h>
+#include <string.h>
+#include <error.h>
+#include <libintl.h>
+#include <dwarf2.h>
+#define DWARF2_FRAME_UNWIND_V2
+#define NEED__Unwind_GetFrameForV2
+#define NEED_DWARF2_FRAME_UNWIND_OLD_OBJECT
+#include <unwind-pe.h>
+
+/* Values for the 'saved' field in struct frame_state defined in
+ unwind-pe.h. */
+#define REG_UNSAVED 0
+#define REG_SAVED_OFFSET 1
+#define REG_SAVED_REG 2
+
+#define DWARF2_UNWIND_INFO 1
+#else /* _LIBC */
#include "tconfig.h"
/* We disable this when inhibit_libc, so that gcc can still be built without
@@ -61,7 +80,10 @@ static __gthread_mutex_t object_mutex;
#ifdef abort
#undef abort
#endif
+#endif /* DWARF2_UNWIND_INFO */
+#endif /* _LIBC */
+#ifdef DWARF2_UNWIND_INFO
/* Some types used by the DWARF 2 spec. */
typedef int sword __attribute__ ((mode (SI)));
@@ -109,6 +131,7 @@ struct dwarf_fde {
uaddr pc_range;
} __attribute__ ((packed, aligned (__alignof__ (void *))));
+#ifndef _LIBC
typedef struct dwarf_fde fde;
/* Objects to be searched for frame unwind info. */
@@ -120,19 +143,15 @@ static struct object *objects;
struct cie_info {
char *augmentation;
void *eh_ptr;
+ void *pc;
int code_align;
int data_align;
unsigned ra_regno;
+ unsigned char fde_encoding;
+ unsigned char lsda_encoding;
+ unsigned char saw_z;
};
-/* The current unwind state, plus a saved copy for DW_CFA_remember_state. */
-
-struct frame_state_internal
-{
- struct frame_state s;
- struct frame_state_internal *saved_state;
-};
-
/* This is undefined below if we need it to be an actual function. */
#define init_object_mutex_once()
@@ -159,7 +178,16 @@ init_object_mutex_once (void)
#endif /* __GTHREAD_MUTEX_INIT_FUNCTION */
#endif /* __GTHREADS */
+#endif /* !_LIBC */
+/* The current unwind state, plus a saved copy for DW_CFA_remember_state. */
+
+struct frame_state_internal
+{
+ struct frame_state s;
+ struct frame_state_internal *saved_state;
+};
+
/* Decode the unsigned LEB128 constant at BUF into the variable pointed to
by R, and return the new value of BUF. */
@@ -230,6 +258,15 @@ static inline unsigned long
read_8byte (void *p)
{ union unaligned *up = p; return up->b8; }
+/* Return the address of the FDE after P. */
+
+static inline fde *
+next_fde (fde *p)
+{
+ return (fde *)(((char *)p) + p->length + sizeof (p->length));
+}
+
+#ifndef _LIBC
/* Ordering function for FDEs. Functions can't overlap, so we just compare
their starting addresses. */
@@ -239,14 +276,6 @@ fde_compare (fde *x, fde *y)
return (saddr)x->pc_begin - (saddr)y->pc_begin;
}
-/* Return the address of the FDE after P. */
-
-static inline fde *
-next_fde (fde *p)
-{
- return (fde *)(((char *)p) + p->length + sizeof (p->length));
-}
-
/* Sorting an array of FDEs by address.
(Ideally we would have the linker sort the FDEs so we don't have to do
it at run time. But the linkers are not yet prepared for this.) */
@@ -545,6 +574,7 @@ find_fde (void *pc)
return 0;
}
+#endif /* !_LIBC */
static inline struct dwarf_cie *
get_cie (fde *f)
@@ -558,39 +588,82 @@ get_cie (fde *f)
static void *
extract_cie_info (fde *f, struct cie_info *c)
{
+ unsigned char *aug;
void *p;
- int i;
+ void *ret = NULL;
c->augmentation = get_cie (f)->augmentation;
+ aug = c->augmentation;
+ p = aug + strlen (aug) + 1;
- if (strcmp (c->augmentation, "") != 0
- && strcmp (c->augmentation, "eh") != 0
- && c->augmentation[0] != 'z')
- return 0;
-
- p = c->augmentation + strlen (c->augmentation) + 1;
-
- if (strcmp (c->augmentation, "eh") == 0)
+ /* "eh" was used by g++ v2; recognize and store. */
+ if (aug[0] == 'e' && aug[1] == 'h')
{
c->eh_ptr = read_pointer (p);
p += sizeof (void *);
}
- else
- c->eh_ptr = 0;
+ /* Immediately following the augmentation are the code and
+ data alignment and return address column. */
p = decode_uleb128 (p, &c->code_align);
p = decode_sleb128 (p, &c->data_align);
c->ra_regno = *(unsigned char *)p++;
+ c->lsda_encoding = DW_EH_PE_omit;
- /* If the augmentation starts with 'z', we now see the length of the
- augmentation fields. */
- if (c->augmentation[0] == 'z')
+ if (c->eh_ptr != NULL)
+ return p;
+
+ /* If the augmentation starts with 'z', then a uleb128 immediately
+ follows containing the length of the augmentation field following
+ the size. */
+ if (*aug == 'z')
{
+ int i;
+
p = decode_uleb128 (p, &i);
- p += i;
+ ret = p + i;
+
+ c->saw_z = 1;
+ ++aug;
}
- return p;
+ /* Iterate over recognized augmentation subsequences. */
+ while (*aug != '\0')
+ {
+ /* "L" indicates a byte showing how the LSDA pointer is encoded. */
+ if (aug[0] == 'L')
+ {
+ c->lsda_encoding = *(unsigned char *)p++;
+ aug += 1;
+ }
+
+ /* "R" indicates a byte indicating how FDE addresses are encoded. */
+ else if (aug[0] == 'R')
+ {
+ c->fde_encoding = *(unsigned char *)p++;
+ aug += 1;
+ }
+
+ /* "P" indicates a personality routine in the CIE augmentation. */
+ else if (aug[0] == 'P')
+ {
+#ifdef NEED__Unwind_GetFrameForV2
+ error (EXIT_FAILURE, 0,
+ _("%c: Unsupported gcc v3 exception augmentation."),
+ aug[0]);
+#else
+ /* We don't support the V3 frame in gcc 2.9x. */
+ abort ();
+#endif
+ }
+
+ /* Otherwise we have an unknown augmentation string.
+ Bail unless we saw a 'z' prefix. */
+ else
+ return ret;
+ }
+
+ return ret ? ret : p;
}
/* Decode one instruction's worth of DWARF 2 call frame information.
@@ -601,14 +674,14 @@ extract_cie_info (fde *f, struct cie_inf
static void *
execute_cfa_insn (void *p, struct frame_state_internal *state,
- struct cie_info *info, void **pc)
+ struct cie_info *info)
{
unsigned insn = *(unsigned char *)p++;
unsigned reg;
int offset;
if (insn & DW_CFA_advance_loc)
- *pc += ((insn & 0x3f) * info->code_align);
+ info->pc += ((insn & 0x3f) * info->code_align);
else if (insn & DW_CFA_offset)
{
reg = (insn & 0x3f);
@@ -625,19 +698,19 @@ execute_cfa_insn (void *p, struct frame_
else switch (insn)
{
case DW_CFA_set_loc:
- *pc = read_pointer (p);
+ info->pc = read_pointer (p);
p += sizeof (void *);
break;
case DW_CFA_advance_loc1:
- *pc += read_1byte (p);
+ info->pc += read_1byte (p);
p += 1;
break;
case DW_CFA_advance_loc2:
- *pc += read_2byte (p);
+ info->pc += read_2byte (p);
p += 2;
break;
case DW_CFA_advance_loc4:
- *pc += read_4byte (p);
+ info->pc += read_4byte (p);
p += 4;
break;
@@ -728,6 +801,7 @@ execute_cfa_insn (void *p, struct frame_
return p;
}
+#ifndef _LIBC
/* Called from crtbegin.o to register the unwind info for an object. */
void
@@ -821,6 +895,7 @@ __deregister_frame (void *begin)
{
free (__deregister_frame_info (begin));
}
+#endif /* !_LIBC */
/* Called from __throw to find the registers to restore for a given
PC_TARGET. The caller should allocate a local variable of `struct
@@ -833,11 +908,17 @@ __frame_state_for (void *pc_target, stru
void *insn, *end, *pc;
struct cie_info info;
struct frame_state_internal state;
+#ifdef NEED__Unwind_GetFrameForV2
+ struct dwarf_eh_bases bases;
+ f = _Unwind_Find_FDE (pc_target, &bases);
+#else
f = find_fde (pc_target);
+#endif
if (f == 0)
return 0;
+ memset (&info, 0, sizeof (info));
insn = extract_cie_info (f, &info);
if (insn == 0)
return 0;
@@ -845,26 +926,39 @@ __frame_state_for (void *pc_target, stru
memset (&state, 0, sizeof (state));
state.s.retaddr_column = info.ra_regno;
state.s.eh_ptr = info.eh_ptr;
+
+ if (info.lsda_encoding == DW_EH_PE_omit
+ && info.fde_encoding == 0)
+ {
+ void *end;
- /* First decode all the insns in the CIE. */
- end = next_fde ((fde*) get_cie (f));
- while (insn < end)
- insn = execute_cfa_insn (insn, &state, &info, 0);
+ /* First decode all the insns in the CIE. */
+ end = next_fde ((fde*) get_cie (f));
+ while (insn < end)
+ insn = execute_cfa_insn (insn, &state, &info);
- insn = ((fde *)f) + 1;
+ insn = ((fde *)f) + 1;
- if (info.augmentation[0] == 'z')
- {
- int i;
- insn = decode_uleb128 (insn, &i);
- insn += i;
- }
+ if (info.augmentation[0] == 'z')
+ {
+ int i;
+ insn = decode_uleb128 (insn, &i);
+ insn += i;
+ }
- /* Then the insns in the FDE up to our target PC. */
- end = next_fde (f);
- pc = f->pc_begin;
- while (insn < end && pc <= pc_target)
- insn = execute_cfa_insn (insn, &state, &info, &pc);
+ /* Then the insns in the FDE up to our target PC. */
+ end = next_fde (f);
+ info.pc = f->pc_begin;
+ while (insn < end && info.pc <= pc_target)
+ insn = execute_cfa_insn (insn, &state, &info);
+ }
+ else
+#ifdef NEED__Unwind_GetFrameForV2
+ _Unwind_GetFrameForV2 (pc_target, insn, f, &bases, &state.s, &info);
+#else
+ /* We don't support the V3 frame in gcc 2.9x. */
+ abort ();
+#endif
memcpy (state_in, &state.s, sizeof (state.s));
return state_in;