PATCH: Support gcc v3 unwind frame in gcc pre 3.0
H . J . Lu
hjl@lucon.org
Mon Jul 9 01:07:00 GMT 2001
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.
H.J.
----
2001-07-08 H.J. Lu (hjl@gnu.org)
* dwarf2.h: Updated from gcc 3.0.
* frame-dwarf2.c: Include glibc heade files instead if _LIBC
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 Sun Jul 2 01:37:02 2000
+++ gcc/dwarf2.h Sun Jul 8 23:48:33 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
@@ -499,9 +501,14 @@ enum dwarf_call_frame_info
DW_CFA_def_cfa_offset = 0x0e,
DW_CFA_def_cfa_expression = 0x0f,
DW_CFA_expression = 0x10,
+ /* Dwarf 2.1 */
+ DW_CFA_offset_extended_sf = 0x11,
+ DW_CFA_def_cfa_sf = 0x12,
+ DW_CFA_def_cfa_offset_sf = 0x13,
+
/* SGI/MIPS specific */
DW_CFA_MIPS_advance_loc8 = 0x1d,
-
+
/* GNU extensions */
DW_CFA_GNU_window_save = 0x2d,
DW_CFA_GNU_args_size = 0x2e,
@@ -534,6 +541,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
};
@@ -551,3 +559,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-dwarf2.c.mixed Sun Jul 8 23:32:48 2001
+++ gcc/frame-dwarf2.c Mon Jul 9 00:54:57 2001
@@ -34,6 +34,24 @@ 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
+#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"
#include "tsystem.h"
@@ -54,7 +72,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)));
@@ -102,6 +123,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. */
@@ -113,19 +135,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()
@@ -152,7 +170,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. */
@@ -223,6 +250,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. */
@@ -232,14 +268,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));
-}
-
#include "frame.c"
static size_t
@@ -418,6 +446,7 @@ find_fde (void *pc)
}
return 0;
}
+#endif /* !_LIBC */
static inline struct dwarf_cie *
get_cie (fde *f)
@@ -431,39 +460,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 a DW_OP stack operation. */
@@ -576,14 +648,14 @@ decode_stack_op (unsigned char *buf, str
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);
@@ -607,19 +679,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;
@@ -757,15 +829,21 @@ __frame_state_for (void *pc_target, stru
#endif
{
fde *f;
- void *insn, *end, *pc;
+ void *insn;
struct cie_info info;
size_t s;
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;
@@ -773,26 +851,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
#ifdef __linux__
/* Binary compatibility problem: In June 2000, 2 fields
More information about the Libc-alpha
mailing list