This is the mail archive of the
libc-alpha@sources.redhat.com
mailing list for the glibc project.
PATCH: Re: GCC vs GLIBC: why this stance, Drepper ?!?
- To: Jakub Jelinek <jakub at redhat dot com>
- Subject: PATCH: Re: GCC vs GLIBC: why this stance, Drepper ?!?
- From: "H . J . Lu" <hjl at lucon dot org>
- Date: Mon, 2 Jul 2001 12:45:38 -0700
- Cc: Justin Guyett <jfg at sonicity dot com>, gcc at gcc dot gnu dot org,libc-alpha at sources dot redhat dot com
- References: <20010630162231.A18131@lucon.org> <Pine.LNX.4.30.0106301526270.318-100000@straylight.int.sonicity.com> <20010701120217.F737@sunsite.ms.mff.cuni.cz>
On Sun, Jul 01, 2001 at 12:02:18PM +0200, Jakub Jelinek wrote:
>
> You can surely recompile it, but it won't be binary compatible.
> __frame_state_for will be missing, plus glibc won't export the needed new
> __register_frame_info_bases etc. symbols, so if you mixed such glibc and
> some G++ 3.0 compiled library, you'd use two different registration points
> for shared libraries, one in glibc, one in probably libstdc++. So things
> would or would not work properly depending on the particular library order
> (e.g. try dlopening a library written in C++ from a C only main program and
> throw exceptions through sort).
>
> GLIBC definitely needs a __frame_state_for implementation, plus its
> interaction with libgcc_s.so needs to be decided (see
> http://sources.redhat.com/ml/libc-hacker/2001-06/msg00020.html).
>
Here is an UNTESTED patch. I don't believe it is even correct. It
only compiles. I think unwind-dw2.c is the best place to add
__frame_state_for. Any comments?
Thanks.
H.J.
-----
2001-07-02 H.J. Lu (hjl@gnu.org)
* config/t-linux (TARGET_LIBGCC2_CFLAGS): Add
-DNEED__frame_state_for.
* unwind-dw2.c (_Unwind_FrameState): Add eh_ptr for g++ v2.
(extract_cie_info): Extract eh_ptr for g++ v2.
(__frame_state_for): New. Define if NEED__frame_state_for is
defined.
--- gcc/config/t-linux.frame Wed May 30 07:50:06 2001
+++ gcc/config/t-linux Mon Jul 2 12:01:33 2001
@@ -4,7 +4,7 @@ STMP_FIXPROTO =
# Compile crtbeginS.o and crtendS.o with pic.
CRTSTUFF_T_CFLAGS_S = -fPIC
# Compile libgcc2.a with pic.
-TARGET_LIBGCC2_CFLAGS = -fPIC
+TARGET_LIBGCC2_CFLAGS = -fPIC -DNEED__frame_state_for
# Override t-slibgcc-elf-ver to export some libgcc symbols with
# the symbol versions that glibc used.
--- gcc/unwind-dw2.c.frame Sat May 19 17:31:42 2001
+++ gcc/unwind-dw2.c Mon Jul 2 12:06:00 2001
@@ -98,6 +98,9 @@ typedef struct
/* The PC described by the current frame state. */
void *pc;
+ /* Used by g++ v2. */
+ void *eh_ptr;
+
/* The information we care about from the CIE/FDE. */
_Unwind_Personality_Fn personality;
signed int data_align;
@@ -242,9 +245,10 @@ extract_cie_info (struct dwarf_cie *cie,
/* Iterate over recognized augmentation subsequences. */
while (*aug != '\0')
{
- /* "eh" was used by g++ v2; recognize and skip. */
+ /* "eh" was used by g++ v2; store it in eh_ptr. */
if (aug[0] == 'e' && aug[1] == 'h')
{
+ fs->eh_ptr = read_pointer (p);
p += sizeof (void *);
aug += 2;
}
@@ -1118,5 +1122,242 @@ uw_identify_context (struct _Unwind_Cont
#include "unwind.inc"
+
+#ifdef NEED__frame_state_for
+/* This provides the backward compatibility for existing binaries
+ compiled against g++ v2 dwarf 2 frame based exception.
+
+ FIXME: We really need this in libgcc.a only. */
+
+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];
+ long base_offset;
+ char indirect;
+};
+
+/* 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;
+};
+
+/* 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. */
+
+#if defined(__linux__) && defined(__sparc__) && !defined(__arch64__)
+/* Grab some info from parent register window so that we don't
+ have to flush register windows and look it up on the stack. */
+asm (" .text \n\
+ .globl __frame_state_for \n\
+ .type __frame_state_for, #function \n\
+__frame_state_for: \n\
+ mov %fp, %o2 \n\
+ b ___frame_state_for \n\
+ mov %i0, %o3 \n\
+ ");
+
+static struct frame_state *
+___frame_state_for (void *pc_target, struct frame_state *state_in, void *pfp,
+ int i0)
+#elif defined(__linux__) && defined(__alpha__)
+struct frame_state *
+__frame_state_for (void *pc_target, struct frame_state *state_in, long a2)
+#else
+struct frame_state *
+__frame_state_for (void *pc_target, struct frame_state *state_in)
+#endif
+{
+ struct dwarf_fde *fde;
+ struct dwarf_cie *cie;
+ const unsigned char *aug, *insn, *end;
+ struct _Unwind_Context context;
+ _Unwind_FrameState fs;
+ struct frame_state_internal state;
+ size_t s;
+
+ memset (&context, 0, sizeof (context));
+
+ fde = _Unwind_Find_FDE (pc_target, &context.bases);
+ if (fde == NULL)
+ return NULL;
+
+ context.ra = pc_target;
+
+ memset (&fs, 0, sizeof (fs));
+ fs.pc = context.bases.func;
+
+ cie = get_cie (fde);
+ insn = extract_cie_info (cie, &context, &fs);
+ if (insn == NULL)
+ return NULL;
+
+ /* 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);
+
+ /* Now we convert to the old g++ v2 frame format. */
+ memset (&state, 0, sizeof (state));
+ state.s.retaddr_column = fs.retaddr_column;
+ state.s.eh_ptr = fs.eh_ptr;
+ state.s.cfa_reg = fs.cfa_reg;
+ state.s.cfa_offset = fs.cfa_offset;
+ state.s.base_offset = context.cfa;
+ state.s.args_size = context.args_size;
+
+ for (s = 0; s <= DWARF_FRAME_REGISTERS; s++)
+ {
+ switch (fs.regs.reg[s].how)
+ {
+ case REG_UNSAVED:
+ state.s.saved[s] = REG_UNSAVED;
+ break;
+ case REG_SAVED_OFFSET:
+ state.s.saved[s] = REG_SAVED_OFFSET;
+ state.s.reg_or_offset[s] = fs.regs.reg[s].loc.offset;
+ break;
+ case REG_SAVED_REG:
+ state.s.saved[s] = REG_SAVED_REG;
+ state.s.reg_or_offset[s] = fs.regs.reg[s].loc.reg;
+ break;
+ default:
+ /* g++ v2 doesn't support REG_SAVED_EXP. */
+ abort ();
+ }
+ }
+
+#ifdef __linux__
+ /* Binary compatibility problem: In June 2000, 2 fields
+ were added at the end of struct frame_state. If for some reason
+ __throw (or __rethrow) comes from binary/shared lib compiled
+ with egcs 1.x.y or gcc-2.95.x and __frame_state_for comes from
+ glibc compiled with gcc-2.96-RH up (gcc-3_0-branch in Apr 2001
+ works that way still), then we can overflow __throw's stack.
+ To fix this, we try to find out who calls us.
+ __frame_state_for is called by next_stack_level and __throw/__rethrow.
+ Of these, __throw/__rethrow does not care about indirect/base_offset
+ fields and next_stack_level can be found out because that's the only
+ routine which where state_in does not point into its stackframe. */
+ s = (size_t) &((struct frame_state *)0)->base_offset;
+# ifdef __i386__
+ {
+ unsigned long pbp, bp;
+ unsigned char *ppc;
+
+ asm ("movl (%%ebp), %0; movl 4(%%ebp), %1; movl %%ebp, %2"
+ : "=r" (pbp), "=r" (ppc), "=r" (bp));
+ if (pbp < (unsigned long)state_in && pbp - bp == 0x40)
+ /* We've been called from next_stack_level function.
+ All gcc-2.96-RH rpms shipped by Red Hat satisfy pbp - bp == 64,
+ none egcs 1.x.y Red Hat shipped do. */
+ {
+ unsigned char *end;
+ int indirect = 0;
+
+ /* Skim next_stack_level to see if it compares 0x74(base),0
+ and reads from 0x70(base). Stop at ret instruction. */
+ for (end = ppc + 512; ppc < end && *ppc != 0xc3 && indirect < 2;
+ ppc++)
+ {
+ if (*ppc == 0x74 && ppc[1] == 0)
+ indirect++;
+ else if (*ppc == 0x70)
+ indirect++;
+ }
+ if (indirect == 2)
+ s = sizeof (state.s);
+ }
+ }
+# elif defined(__sparc__) && !defined(__arch64__)
+ if (pfp < (unsigned long)state_in && i0 == 0)
+ /* We've been called from next_stack_level function.
+ All gcc-2.96-RH rpms shipped by Red Hat clear %i0 in next_stack_level
+ before calling __frame_state_for, all egcs 1.x.y just copy
+ %i0 into %o0, so it is guaranteed to be non-NULL. */
+ {
+ unsigned int *ppc, *end;
+ int indirect = 0;
+
+ ppc = (unsigned int) __builtin_return_address (0);
+
+ /* Skim next_stack_level to see if it does a ld?b [? + 0x218]
+ and ld?w [? + 0x214]. Stop at ret instruction. */
+ for (end = ppc + 128; ppc < end && indirect < 2
+ && (*ppc | 0x00080000) != 0x81cfe008; ppc++)
+ switch (*ppc & 0xc1b83fff)
+ {
+ case 0xc0082218: /* ld?b [? + 0x218], ? */
+ case 0xc0002214: /* ld?w [? + 0x214], ? */
+ indirect++;
+ break;
+ }
+ if (indirect == 2)
+ s = sizeof (state.s);
+ }
+# elif defined(__alpha__)
+ if ((long)state_in == a2)
+ /* We've been called most probably from next_stack_level (it has 3 arguments
+ and passes its third argument as second argument to __frame_state_for). */
+ {
+ unsigned int *ppc, *end;
+ int indirect = 0;
+
+ /* Skim next_stack_level to see if it does a ldq ?,624(?)
+ and ldl ?,632(?) resp. ldbu ?,632(?). Stop at ret instruction. */
+ for (end = ppc + 128; ppc < end && indirect < 2
+ && *ppc != 0x6bfa8001; ppc++)
+ switch (*ppc & 0xfc00ffff)
+ {
+ case 0xa4000270: /* ldq ?,624(?) */
+ case 0xa0000278: /* ldl ?,632(?) */
+ case 0xa8000278: /* ldbu ?,632(?) */
+ indirect++;
+ break;
+ }
+ if (indirect == 2)
+ s = sizeof (state.s);
+ }
+# else
+ s = sizeof (state.s);
+# endif
+#else
+ s = sizeof (state.s);
+#endif
+ if (state.s.indirect && s < sizeof (state.s))
+ abort ();
+ memcpy (state_in, &state.s, s);
+ return state_in;
+}
+#endif /* NEED__frame_state_for */
#endif /* !USING_SJLJ_EXCEPTIONS */