This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Handle DW_CFA_undefined retaddr as outermost frame


Hi!

This is an attempt to resurrect very long forgotten issue between gcc
unwinder and upstream i386/x86_64 glibc, see the thread starting at
http://gcc.gnu.org/ml/gcc/2006-12/msg00293.html
for more info.

The DWARF3 standard says in 6.4.4:
If a Return Address register is defined in the virtual unwind table, and its
rule is undefined (for example, by DW_CFA_undefined), then there is no return
address and no call address, and the virtual unwind of stack activations is
complete.

and based on this glibc was changed to have:
        cfi_startproc;
        /* Clearing frame pointer is insufficient, use CFI.  */
        cfi_undefined (rip);
...
        cfi_endproc;
around clone(2) userland landing pad (before that no CFI has been provided).
This unfortunately breaks the libgcc unwinder, so e.g.:
#include <pthread.h>
#include <execinfo.h>

void
to (void)
{
  void *buf[1024];
  int n = backtrace (buf, 1024);
  if (n > 0)
    backtrace_symbols_fd (buf, n, 2);
}

void *
tf (void *arg)
{
  to ();
  return arg;
}

int
main (void)
{
  pthread_t th;
  pthread_create (&th, NULL, tf, NULL);
  pthread_join (th, NULL);
  return 0;
}
prints 1020 frames with the same address inside clone.  For Fedora I've
reverted the glibc change temporarily, because it didn't work with
gcc's unwinder, and then unfortunately forgot about it.  I guess some other
distributions do the same.

Based on the discussion in above mentioned thread, here is a minimal patch
that handles explicit DW_CFA_undefined retaddr as outermost frame,
assumes DW_CFA_same_value is the default (so when retaddr doesn't have
anything specified, it isn't the outermost frame) and otherwise handles
DW_CFA_undefined and DW_CFA_same_value identically.

Bootstrapped/regtested on x86_64-linux and i686-linux, tested with the above
testcase on both vanilla upstream glibc and Fedora glibc (with the
DW_CFA_undefined patched out from clone).  Ok for trunk/4.4?

2009-04-02  Jakub Jelinek  <jakub@redhat.com>

	* unwind-dw2.h (_Unwind_FrameState): Add REG_UNDEFINED enum value.
	* unwind-dw2.c (execute_cfa_program): Set how to REG_UNDEFINED
	instead of REG_UNSAVED for DW_CFA_undefined.
	(uw_update_context_1): Handle REG_UNDEFINED the same as REG_UNSAVED.
	(uw_update_context): If RA column is REG_UNDEFINED, mark it as
	outermost frame.

--- gcc/unwind-dw2.h.jj	2008-09-30 16:57:11.000000000 +0200
+++ gcc/unwind-dw2.h	2009-04-01 21:38:34.000000000 +0200
@@ -1,5 +1,5 @@
 /* DWARF2 frame unwind data structure.
-   Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003
+   Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2009
    Free Software Foundation, Inc.
 
    This file is part of GCC.
@@ -55,7 +55,8 @@ typedef struct
 	REG_SAVED_REG,
 	REG_SAVED_EXP,
 	REG_SAVED_VAL_OFFSET,
-	REG_SAVED_VAL_EXP
+	REG_SAVED_VAL_EXP,
+	REG_UNDEFINED
       } how;
     } reg[DWARF_FRAME_REGISTERS+1];
 
--- gcc/unwind-dw2.c.jj	2009-03-02 16:22:17.000000000 +0100
+++ gcc/unwind-dw2.c	2009-04-01 21:52:57.000000000 +0200
@@ -1,6 +1,6 @@
 /* DWARF2 exception handling and frame unwind runtime interface routines.
    Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
-   2008  Free Software Foundation, Inc.
+   2008, 2009  Free Software Foundation, Inc.
 
    This file is part of GCC.
 
@@ -943,12 +943,16 @@ execute_cfa_program (const unsigned char
 	  fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN(reg)].how = REG_UNSAVED;
 	  break;
 
-	case DW_CFA_undefined:
 	case DW_CFA_same_value:
 	  insn_ptr = read_uleb128 (insn_ptr, &reg);
 	  fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN(reg)].how = REG_UNSAVED;
 	  break;
 
+	case DW_CFA_undefined:
+	  insn_ptr = read_uleb128 (insn_ptr, &reg);
+	  fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN(reg)].how = REG_UNDEFINED;
+	  break;
+
 	case DW_CFA_nop:
 	  break;
 
@@ -1319,6 +1323,7 @@ uw_update_context_1 (struct _Unwind_Cont
     switch (fs->regs.reg[i].how)
       {
       case REG_UNSAVED:
+      case REG_UNDEFINED:
 	break;
 
       case REG_SAVED_OFFSET:
@@ -1387,10 +1392,22 @@ uw_update_context (struct _Unwind_Contex
 {
   uw_update_context_1 (context, fs);
 
-  /* Compute the return address now, since the return address column
-     can change from frame to frame.  */
-  context->ra = __builtin_extract_return_addr
-    (_Unwind_GetPtr (context, fs->retaddr_column));
+  /* In general this unwinder doesn't make any distinction between
+     undefined and same_value rule.  Call-saved registers are assumed
+     to have same_value rule by default and explicit undefined
+     rule is handled like same_value.  The only exception is
+     DW_CFA_undefined on retaddr_column which is supposed to
+     mark outermost frame in DWARF 3.  */
+  if (fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (fs->retaddr_column)].how
+      == REG_UNDEFINED)
+    /* uw_frame_state_for uses context->ra == 0 check to find outermost
+       stack frame.  */
+    context->ra = 0;
+  else
+    /* Compute the return address now, since the return address column
+       can change from frame to frame.  */
+    context->ra = __builtin_extract_return_addr
+      (_Unwind_GetPtr (context, fs->retaddr_column));
 }
 
 static void


	Jakub


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