Another latent issue exposed on IA-64 (both Linux and VMS) by GCC 4.7: the LC
(Loop Counter) register isn't preserved by the unwinder.
The compiler generates unwind info for LC and unwind-ia64.c:uw_install_context
restores it if this is deemed necessary. The hitch is that deemed necessary
means register saved at some point along the path between thrower and catcher
and going through _Unwind_RaiseException. Now if a register isn't saved along
this path but clobbered very late, namely in uw_install_context, then nothing
restores it before the longjmp.
unwind-ia64.c:uw_install_context reads:
static void __attribute__((noreturn))
uw_install_context (struct _Unwind_Context *current __attribute__((unused)),
struct _Unwind_Context *target)
{
unsigned long ireg_buf[4], ireg_nat = 0, ireg_pr = 0;
long i;
/* Copy integer register data from the target context to a
temporary buffer. Do this so that we can frob AR.UNAT
to get the NaT bits for these registers set properly. */
for (i = 4; i = 7; ++i)
{
char nat;
void *t = target-ireg[i - 2].loc;
if (t)
{
unw_access_gr (target, i, ireg_buf[i - 4], nat, 0);
ireg_nat |= (long)nat (((size_t)ireg_buf[i - 4] 3) 0x3f);
/* Set p6 - p9. */
ireg_pr |= 4L i;
}
}
and it clobbers LC because of the loop when compiled with GCC 4.7 and above.
Bootstrapped/regtested on IA-64/Linux, OK for the mainline? Do we also want it
for 4.7.1?
2012-03-21 Eric Botcazou ebotca...@adacore.com
* config/ia64/unwind-ia64.c (uw_install_context): Manually save LC
if it hasn't been previously saved.
--
Eric Botcazou
Index: libgcc/config/ia64/unwind-ia64.c
===
--- libgcc/config/ia64/unwind-ia64.c (revision 185395)
+++ libgcc/config/ia64/unwind-ia64.c (working copy)
@@ -2171,8 +2171,20 @@ uw_install_context (struct _Unwind_Conte
struct _Unwind_Context *target)
{
unsigned long ireg_buf[4], ireg_nat = 0, ireg_pr = 0;
+ unsigned long saved_lc;
long i;
+ /* ??? LC is a fixed register so the call to __builtin_unwind_init in
+ uw_init_context doesn't cause it to be saved. In case it isn't in
+ the user frames either, we need to manually do it here, lest it be
+ clobbered by the loop just below. */
+ if (target-lc_loc == NULL)
+{
+ register unsigned long lc asm (ar.lc);
+ saved_lc = lc;
+ target-lc_loc = saved_lc;
+}
+
/* Copy integer register data from the target context to a
temporary buffer. Do this so that we can frob AR.UNAT
to get the NaT bits for these registers set properly. */