libunwind-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Libunwind-devel] cursor.is_interrupted() or is_...


From: Mark Wielaard
Subject: Re: [Libunwind-devel] cursor.is_interrupted() or is_...
Date: Tue, 29 Jan 2008 11:36:50 +0100
User-agent: Thunderbird 2.0.0.9 (X11/20071115)

Andrew Cagney wrote:
Arun Sharma wrote:
On Jan 8, 2008 7:42 AM, Andrew Cagney <address@hidden <mailto:address@hidden>> wrote:


    what thoughts are there on adding a cursor method such as
    is_interrupted() or is_caller() or ...?  The implementation would
    likely
    involve the addition of an extra bit field to the cursor.


This sounds analogous to unw_is_signal_frame() - which is implemented without an extra bit.

It is related yes; consider the back-trace:

  <signal>     is_interrupted=1 is_signal=1 -- inner
  handler()    is_interrupted=1 is_signal=0
  <signal>     is_interrupted=0 is_signal=1
  foo()        is_interrupted=1 is_signal=0
  main()       is_interrupted=0 is_signal=0 -- outer

(Note that, in the above, the inner most frame has both is_interrupted and is_signal as will occure when single stepping through the signal trampoline. More typically just is_interrupted would be set.)

And we can see that is_interrupted() can be computed thus:

  if (cursor is inner-most)
     return 1; // innermost frame (implicitly interrupted)
  else if (cursor's inner is_signal)
     return 1; // interrupted by signal
  else
     return 0;

Unfortunatly the inner frame's state isn't available, so instead we implement this by caching the computed value during the step vis:

  init:
     cursor.is_interrupted = 1;

  step:
     cursor.is_interrupted = inner.is_signal

we can then use  cursor.is_interrupted to correctly handle:

 From DWARF 3:
In most cases the return address is in the same context as the calling address, but that need not be the case, especially if the producer knows in some way the call never will return. The context of the 'return address' might be on a different line, in a different lexical block, or past the end of the calling subroutine. If a consumer were to assume that it was in the same context as the calling address, the unwind might fail.

Also related to this discussion are the following set of patches in the frysk libunwind tree to better support signal frames. They don't expose a new interface though, they just handle the above issue better. Do read the following two bug reports which contain some more discussion on the subject (and show that older versions of glibc didn't provide full unwind info in all cases, Jan also pushed a fix for that upstream).

    RESOLVES=http://sourceware.org/bugzilla/show_bug.cgi?id=3529
    RELATED=https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=217087

2006-11-24  Jan Kratochvil  <address@hidden>

    * include/dwarf.h (dwarf_cie_info): New `signal_frame' marker.
    (dwarf_cursor): New `decrease_ip' marker.
    * src/dwarf/Gfde.c (parse_cie): Set the `signal_frame' marker.
    * src/dwarf/Gparser.c (run_cfi_program): Fixed occassionally
    offbyone `ip' missed CFI instructions, related to `fetch_proc_info'.
    (fetch_proc_info): Decrease parent `ip' only by `decrease_ip'
    marker.
    (is_signal_frame): Detect signal frames by the `signal_frame'
    marker.
    (uncached_dwarf_find_save_locs): Update the `decrease_ip' marker.
    (dwarf_find_save_locs): Likewise.
    * src/hppa/init.h (common_init): Initialize the `decrease_ip'
    marker.
    * src/ppc64/init.h (common_init): Likewise.
    * src/x86/init.h (common_init): Likewise.
    * src/x86_64/init.h (common_init): Likewise.
    * tests/Makefile.in: New `run-ptrace-stepper' and
    `test-ptrace-stepper'.
    * tests/test-ptrace.c (backtrace_check): New for `-b'.
    (do_exit): Variable to support breaking of the `main' mainloop.
    (do_backtrace): Implemented `-b' based on `backtrace_check'.
    Fixed compile warning for 32-bit platforms
    (main): Fixed segfault for some `argv's.
    Support backtrace option `-b' - `,'-delimited backtrace list to
    check. Support `do_exit'.
    * test-ptrace-stepper.c: New, test corner backtrace conditions.
    * run-ptrace-stepper: New, test `test-ptrace' on
    `test-ptrace-stepper'.

2006-12-10  Jan Kratochvil  <address@hidden>

    * src/dwarf/Gparser.c (apply_reg_state): Handle undefined PC by
    `c->ip = 0' as in the case of the outermost frame of clone(3).

2006-12-10  Jan Kratochvil  <address@hidden>

    * src/x86_64/Gfetch_proc_info_post.c: Set `cursor->sigcontext_addr'
    even for CFI-unwinded signal frames.
    * src/dwarf/Gparser.c (fetch_proc_info): Call it.
    * src/x86_64/Make-arch.in: Include `Gfetch_proc_info_post.o'.
    * include/tdep-x86_64/libunwind_i.h (tdep_fetch_proc_info_post): New
    (declaration).
    * include/tdep-hppa/libunwind_i.h (tdep_fetch_proc_info_post): New
    (empty definition).
    * include/tdep-ia64/libunwind_i.h (tdep_fetch_proc_info_post):
    Likewise.
    * include/tdep-ppc64/libunwind_i.h (tdep_fetch_proc_info_post):
    Likewise.
    * include/tdep-x86/libunwind_i.h (tdep_fetch_proc_info_post):
    Likewise.

2006-12-10  Jan Kratochvil  <address@hidden>

    * src/x86/Gstep.c (unw_step): Handle the case of `c->dwarf.ip == 0'.
    * src/x86_64/Gstep.c (code_descriptor_trap): Handle the cases of
    signal
    frame leaving stale address due to page fault on an instruction
    fetch.
    (unw_step): Call `code_descriptor_trap' above.
    Debug dump the content of signal frame set from CFI.
    Handle the case of `c->dwarf.ip == 0'.
    * src/x86_64/ucontext_i.h: New signal frames fields `err' and
    `trapno'.
    * tests/Makefile.in: New `run-ptrace-signull'.
    * tests/run-ptrace-signull: New (NULL handling: gdb `signull'
    testcase).
    * tests/test-ptrace-signull.c: Likewise.

2006-12-10  Jan Kratochvil  <address@hidden>

    * src/x86_64/Gstep.c (unw_step): Fixed debug message exhanged
    values.
diff --git a/include/dwarf.h b/include/dwarf.h
index 3dbf520..47896f6 100644
--- a/include/dwarf.h
+++ b/include/dwarf.h
@@ -266,6 +266,7 @@ typedef struct dwarf_cie_info
     uint8_t lsda_encoding;
     unsigned int sized_augmentation : 1;
     unsigned int have_abi_marker : 1;
+    unsigned int signal_frame : 1;
   }
 dwarf_cie_info_t;
 
@@ -293,6 +294,7 @@ typedef struct dwarf_cursor
 
     dwarf_loc_t loc[DWARF_NUM_PRESERVED_REGS];
 
+    unsigned int decrease_ip :1; /* decrease `ip' back into the `call' instr */
     unsigned int pi_valid :1;  /* is proc_info valid? */
     unsigned int pi_is_dynamic :1; /* proc_info found via dynamic proc info? */
     unw_proc_info_t pi;                /* info about current procedure */
diff --git a/include/tdep-hppa/libunwind_i.h b/include/tdep-hppa/libunwind_i.h
index 52cfc56..ca25328 100644
--- a/include/tdep-hppa/libunwind_i.h
+++ b/include/tdep-hppa/libunwind_i.h
@@ -237,6 +237,7 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, 
unw_word_t val)
 # define tdep_put_unwind_info(c,pi)                                    \
        (*(c)->as->acc.put_unwind_info)((c)->as, (pi), (c)->as_arg)
 #endif
+#define tdep_fetch_proc_info_post(c, ip, need_unwind_info) 0
 
 #define tdep_get_as(c)                 ((c)->dwarf.as)
 #define tdep_get_as_arg(c)             ((c)->dwarf.as_arg)
diff --git a/include/tdep-ia64/libunwind_i.h b/include/tdep-ia64/libunwind_i.h
index 552c949..b851a03 100644
--- a/include/tdep-ia64/libunwind_i.h
+++ b/include/tdep-ia64/libunwind_i.h
@@ -239,6 +239,7 @@ extern void tdep_init (void);
 extern int tdep_find_proc_info (unw_addr_space_t as, unw_word_t ip,
                                unw_proc_info_t *pi, int need_unwind_info,
                                void *arg);
+#define tdep_fetch_proc_info_post(c, ip, need_unwind_info) 0
 extern void tdep_put_unwind_info (unw_addr_space_t as,
                                  unw_proc_info_t *pi, void *arg);
 extern void *tdep_uc_addr (ucontext_t *uc, unw_regnum_t regnum,
diff --git a/include/tdep-ppc64/libunwind_i.h b/include/tdep-ppc64/libunwind_i.h
index 8b5c4de..c19644e 100644
--- a/include/tdep-ppc64/libunwind_i.h
+++ b/include/tdep-ppc64/libunwind_i.h
@@ -270,6 +270,7 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, 
unw_word_t val)
 # define tdep_put_unwind_info(as,pi,arg)                       \
        (*(as)->acc.put_unwind_info)((as), (pi), (arg))
 #endif
+#define tdep_fetch_proc_info_post(c, ip, need_unwind_info) 0
 
 extern int tdep_fetch_proc_info_post (struct dwarf_cursor *c, unw_word_t ip,
                                      int need_unwind_info);
diff --git a/include/tdep-x86/libunwind_i.h b/include/tdep-x86/libunwind_i.h
index 1a43882..8180ecd 100644
--- a/include/tdep-x86/libunwind_i.h
+++ b/include/tdep-x86/libunwind_i.h
@@ -240,6 +240,7 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, 
unw_word_t val)
 # define tdep_put_unwind_info(as,pi,arg)               \
        (*(as)->acc.put_unwind_info)((as), (pi), (arg))
 #endif
+#define tdep_fetch_proc_info_post(c, ip, need_unwind_info) 0
 
 #define tdep_get_as(c)                 ((c)->dwarf.as)
 #define tdep_get_as_arg(c)             ((c)->dwarf.as_arg)
diff --git a/include/tdep-x86_64/libunwind_i.h 
b/include/tdep-x86_64/libunwind_i.h
index 04a3c37..affe760 100644
--- a/include/tdep-x86_64/libunwind_i.h
+++ b/include/tdep-x86_64/libunwind_i.h
@@ -166,6 +166,8 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, 
unw_word_t val)
 # define tdep_put_unwind_info(as,pi,arg)                       \
        (*(as)->acc.put_unwind_info)((as), (pi), (arg))
 #endif
+extern int tdep_fetch_proc_info_post (struct dwarf_cursor *c, unw_word_t ip,
+                                     int need_unwind_info);
 
 #define tdep_get_as(c)                 ((c)->dwarf.as)
 #define tdep_get_as_arg(c)             ((c)->dwarf.as_arg)
diff --git a/src/Makefile.am b/src/Makefile.am
index 88bb20b..0f97008 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -165,7 +165,7 @@ libunwind_la_SOURCES_x86 = 
$(libunwind_la_SOURCES_x86_common)               \
        x86/Lcreate_addr_space.c x86/Lget_save_loc.c x86/Lglobal.c      \
        x86/Linit.c x86/Linit_local.c x86/Linit_remote.c                \
        x86/Lis_signal_frame.c x86/Lget_proc_info.c x86/Lregs.c         \
-       x86/Lresume.c x86/Lstep.c
+       x86/Lresume.c x86/Lstep.c x86/Lfetch_proc_info_post.c
 
 # The list of files that go into libunwind-x86:
 libunwind_x86_la_SOURCES_x86 = $(libunwind_la_SOURCES_x86_common)      \
@@ -175,7 +175,7 @@ libunwind_x86_la_SOURCES_x86 = 
$(libunwind_la_SOURCES_x86_common)   \
        x86/Gcreate_addr_space.c x86/Gget_save_loc.c x86/Gglobal.c      \
        x86/Ginit.c x86/Ginit_local.c x86/Ginit_remote.c                \
        x86/Gis_signal_frame.c x86/Gget_proc_info.c x86/Gregs.c         \
-       x86/Gresume.c x86/Gstep.c
+       x86/Gresume.c x86/Gstep.c x86/Gfetch_proc_info_post.c
 
 # The list of files that go both into libunwind and libunwind-x86_64:
 libunwind_la_SOURCES_x86_64_common = $(libunwind_la_SOURCES_common)    \
@@ -189,11 +189,10 @@ libunwind_la_SOURCES_x86_64 = 
$(libunwind_la_SOURCES_x86_64_common)           \
        $(libunwind_la_SOURCES_local)                                       \
        $(dwarf_SOURCES_local)                                              \
        dwarf/Lfind_proc_info-lsb.c                                         \
-       x86_64/setcontext.S                                             \
        x86_64/Lcreate_addr_space.c x86_64/Lget_save_loc.c x86_64/Lglobal.c \
        x86_64/Linit.c x86_64/Linit_local.c x86_64/Linit_remote.c           \
        x86_64/Lis_signal_frame.c x86_64/Lget_proc_info.c x86_64/Lregs.c    \
-       x86_64/Lresume.c x86_64/Lstep.c
+       x86_64/Lresume.c x86_64/Lstep.c x86_64/Lfetch_proc_info_post.c
 
 # The list of files that go into libunwind-x86_64:
 libunwind_x86_64_la_SOURCES_x86_64 = $(libunwind_la_SOURCES_x86_64_common)  \
@@ -203,17 +202,19 @@ libunwind_x86_64_la_SOURCES_x86_64 = 
$(libunwind_la_SOURCES_x86_64_common)  \
        x86_64/Gcreate_addr_space.c x86_64/Gget_save_loc.c x86_64/Gglobal.c \
        x86_64/Ginit.c x86_64/Ginit_local.c x86_64/Ginit_remote.c           \
        x86_64/Gis_signal_frame.c x86_64/Gget_proc_info.c x86_64/Gregs.c    \
-       x86_64/Gresume.c x86_64/Gstep.c
+       x86_64/Gresume.c x86_64/Gstep.c x86_64/Gfetch_proc_info_post.c
 
 # The list of local files that go to Power 64 and 32:
 libunwind_la_SOURCES_ppc = ppc/Lcreate_addr_space.c                    \
        ppc/Lget_proc_info.c ppc/Lget_save_loc.c ppc/Linit_local.c      \
-       ppc/Linit_remote.c ppc/Lis_signal_frame.c
+       ppc/Linit_remote.c ppc/Lis_signal_frame.c                       \
+       ppc/Lfetch_proc_info_post.c
 
 # The list of generic files that go to Power 64 and 32:
 libunwind_ppc_la_SOURCES_ppc_generic = ppc/Gcreate_addr_space.c                
\
        ppc/Gget_proc_info.c ppc/Gget_save_loc.c ppc/Ginit_local.c      \
-       ppc/Ginit_remote.c ppc/Gis_signal_frame.c
+       ppc/Ginit_remote.c ppc/Gis_signal_frame.c                       \
+       ppc/Gfetch_proc_info_post.c
 
 # The list of files that go both into libunwind and libunwind-ppc32:
 libunwind_la_SOURCES_ppc32_common = $(libunwind_la_SOURCES_common)      \
diff --git a/src/dwarf/Gfde.c b/src/dwarf/Gfde.c
index 8a85685..d6b4987 100644
--- a/src/dwarf/Gfde.c
+++ b/src/dwarf/Gfde.c
@@ -179,6 +179,8 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, 
unw_word_t addr,
        break;
 
       case 'S':
+       /* Original meaning of this augmentation marker.  */
+       dci->signal_frame = 1;
        /* Temporarily set it to one so dwarf_parse_fde() knows that
           it should fetch the actual ABI/TAG pair from the FDE.  */
        dci->have_abi_marker = 1;
diff --git a/src/dwarf/Gparser.c b/src/dwarf/Gparser.c
index aa87d61..33cd2ac 100644
--- a/src/dwarf/Gparser.c
+++ b/src/dwarf/Gparser.c
@@ -76,7 +76,10 @@ run_cfi_program (struct dwarf_cursor *c, 
dwarf_state_record_t *sr,
   a = unw_get_accessors (as);
   curr_ip = c->pi.start_ip;
 
-  while (curr_ip < ip && *addr < end_addr)
+  /* Process all the instructions including the ones after `DW_CFA_advance_loc'
+     up to the next advance higher than the current `ip' (therefore `<=').
+     See also the `c->decrease_ip' handling in `fetch_proc_info'.  */
+  while (curr_ip <= ip && *addr < end_addr)
     {
       if ((ret = dwarf_readu8 (as, a, addr, &op, arg)) < 0)
        return ret;
@@ -381,7 +384,14 @@ fetch_proc_info (struct dwarf_cursor *c, unw_word_t ip, 
int need_unwind_info)
 {
   int ret, dynamic = 1;
 
-  --ip;
+  /* In the current (lowest) frame we must not touch `ip' as the current
+     address is where we stand.  On the other hand any upper frames will stand
+     on the next instruction behind our call which may have a different stack
+     DWARF information (for `stdcall' called functions) or the next instruction
+     even may belong already to a different continuing function.
+     Also signal frames got invoked from the instruction we want to analyze.  
*/
+  if (c->decrease_ip)
+    --ip;
 
   if (c->pi_valid && !need_unwind_info)
     return 0;
@@ -400,6 +410,10 @@ fetch_proc_info (struct dwarf_cursor *c, unw_word_t ip, 
int need_unwind_info)
 
   c->pi_valid = 1;
   c->pi_is_dynamic = dynamic;
+
+  if (ret >= 0)
+    tdep_fetch_proc_info_post (c, ip, need_unwind_info);
+
   return ret;
 }
 
@@ -763,10 +777,18 @@ apply_reg_state (struct dwarf_cursor *c, struct 
dwarf_reg_state *rs)
        }
     }
   c->cfa = cfa;
-  ret = dwarf_get (c, c->loc[c->ret_addr_column], &ip);
-  if (ret < 0)
-    return ret;
-  c->ip = ip;
+  /* After fixing glibc's `__restore_rt' unwinding by CFI in:
+       
http://sources.redhat.com/cgi-bin/cvsweb.cgi/libc/sysdeps/unix/sysv/linux/x86_64/sigaction.c.diff?cvsroot=glibc&r1=text&tr1=1.10&r2=text&tr2=1.12&f=u
+     we need to check for the frame stop (indicated by `ip == 0').  */
+  if (DWARF_IS_NULL_LOC (c->loc[c->ret_addr_column]))
+    c->ip = 0;
+  else
+    {
+      ret = dwarf_get (c, c->loc[c->ret_addr_column], &ip);
+      if (ret < 0)
+       return ret;
+      c->ip = ip;
+    }
   /* XXX: check for ip to be code_aligned */
 
   if (c->ip == prev_ip && c->cfa == prev_cfa)
@@ -779,6 +801,17 @@ apply_reg_state (struct dwarf_cursor *c, struct 
dwarf_reg_state *rs)
 }
 
 static int
+is_signal_frame (struct dwarf_cursor *c)
+{
+  struct dwarf_cie_info *dci;
+
+  assert (c->pi_valid);
+
+  dci = c->pi.unwind_info;
+  return dci->signal_frame;
+}
+
+static int
 uncached_dwarf_find_save_locs (struct dwarf_cursor *c)
 {
   dwarf_state_record_t sr;
@@ -787,6 +820,8 @@ uncached_dwarf_find_save_locs (struct dwarf_cursor *c)
   if ((ret = fetch_proc_info (c, c->ip, 1)) < 0)
     return ret;
 
+  c->decrease_ip = !is_signal_frame (c);
+
   if ((ret = create_state_record_for (c, &sr, c->ip)) < 0)
     return ret;
 
@@ -825,6 +860,8 @@ dwarf_find_save_locs (struct dwarf_cursor *c)
   if ((ret = fetch_proc_info (c, c->ip, 1)) < 0)
     goto out;
 
+  c->decrease_ip = !is_signal_frame (c);
+
   if ((ret = create_state_record_for (c, &sr, c->ip)) < 0)
     goto out;
 
diff --git a/src/ppc64/init.h b/src/ppc64/init.h
index 886f14c..0c7cf2b 100644
--- a/src/ppc64/init.h
+++ b/src/ppc64/init.h
@@ -70,6 +70,7 @@ common_init_ppc64 (struct cursor *c)
   c->sigcontext_format = PPC_SCF_NONE;
   c->sigcontext_addr = 0;
 
+  c->dwarf.decrease_ip = 0;
   c->dwarf.args_size = 0;
   c->dwarf.ret_addr_column = 0;
   c->dwarf.pi_valid = 0;
diff --git a/src/x86/Gstep.c b/src/x86/Gstep.c
index e0e681d..fe3ea49 100644
--- a/src/x86/Gstep.c
+++ b/src/x86/Gstep.c
@@ -47,11 +47,18 @@ unw_step (unw_cursor_t *cursor)
     {
       /* DWARF failed, let's see if we can follow the frame-chain
         or skip over the signal trampoline.  */
-      struct dwarf_loc ebp_loc, eip_loc;
+      struct dwarf_loc eip_loc;
 
       Debug (13, "dwarf_step() failed (ret=%d), trying frame-chain\n", ret);
 
-      if (unw_is_signal_frame (cursor))
+      if (c->dwarf.ip == 0)
+        {
+         Debug (13, "[EIP=0]\n");
+
+         eip_loc = DWARF_LOC (c->dwarf.cfa, 0);
+         c->dwarf.cfa += 4;
+       }
+      else if (unw_is_signal_frame (cursor))
        {
          /* XXX This code is Linux-specific! */
 
@@ -96,7 +103,6 @@ unw_step (unw_cursor_t *cursor)
              sc_addr = sigcontext_ptr + LINUX_UC_MCONTEXT_OFF;
            }
          esp_loc = DWARF_LOC (sc_addr + LINUX_SC_ESP_OFF, 0);
-         ebp_loc = DWARF_LOC (sc_addr + LINUX_SC_EBP_OFF, 0);
          eip_loc = DWARF_LOC (sc_addr + LINUX_SC_EIP_OFF, 0);
          ret = dwarf_get (&c->dwarf, esp_loc, &c->dwarf.cfa);
          if (ret < 0)
@@ -128,17 +134,16 @@ unw_step (unw_cursor_t *cursor)
          Debug (13, "[EBP=0x%x] = 0x%x\n", DWARF_GET_LOC (c->dwarf.loc[EBP]),
                 c->dwarf.cfa);
 
-         ebp_loc = DWARF_LOC (c->dwarf.cfa, 0);
-         eip_loc = DWARF_LOC (c->dwarf.cfa + 4, 0);
-         c->dwarf.cfa += 8;
-
          /* Mark all registers unsaved, since we don't know where
             they are saved (if at all), except for the EBP and
             EIP.  */
          for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
            c->dwarf.loc[i] = DWARF_NULL_LOC;
+
+         c->dwarf.loc[EBP] = DWARF_LOC (c->dwarf.cfa, 0);
+         eip_loc = DWARF_LOC (c->dwarf.cfa + 4, 0);
+         c->dwarf.cfa += 8;
        }
-      c->dwarf.loc[EBP] = ebp_loc;
       c->dwarf.loc[EIP] = eip_loc;
       c->dwarf.ret_addr_column = EIP;
 
diff --git a/src/x86/init.h b/src/x86/init.h
index 675b77e..de97f5e 100644
--- a/src/x86/init.h
+++ b/src/x86/init.h
@@ -57,6 +57,7 @@ common_init (struct cursor *c)
   c->sigcontext_format = X86_SCF_NONE;
   c->sigcontext_addr = 0;
 
+  c->dwarf.decrease_ip = 0;
   c->dwarf.args_size = 0;
   c->dwarf.ret_addr_column = 0;
   c->dwarf.pi_valid = 0;
diff --git a/src/x86_64/Gresume.c b/src/x86_64/Gresume.c
index 4edc4da..2fc51aa 100644
--- a/src/x86_64/Gresume.c
+++ b/src/x86_64/Gresume.c
@@ -71,7 +71,7 @@ x86_64_local_resume (unw_addr_space_t as, unw_cursor_t 
*cursor, void *arg)
     {
       Debug (8, "resuming at ip=%llx via setcontext()\n",
             (unsigned long long) c->dwarf.ip);
-      _x86_64_setcontext (uc);
+      setcontext (uc);
     }
 #else
 # warning Implement me!
diff --git a/src/x86_64/Gstep.c b/src/x86_64/Gstep.c
index 75f796f..fe69929 100644
--- a/src/x86_64/Gstep.c
+++ b/src/x86_64/Gstep.c
@@ -29,11 +29,68 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
SOFTWARE.  */
 #include "ucontext_i.h"
 #include <signal.h>
 
+/* Try to skip single-word (8 bytes) return address on stack left there in some
+   cases of the signal frame.
+   Unsure if the real return address gets pushed to the stack exactly when this
+   bit is set.  If it is not detectable we would need to keep the return
+   address on stack and improve the code path
+       dwarf_step() failed (ret=%d), trying frame-chain
+   below to disassemble the return address and not to pop two words
+   automatically as no stack frame pointer is present in these cases there:
+       ./test-ptrace-signull code_entry_point: err=0x14
+       ./test-ptrace-signull code_descriptor:  err=0x15  */
+
+static int
+code_descriptor_trap (struct cursor *c, struct dwarf_loc *rip_loc_pointer)
+{
+  unw_word_t trapno, err;
+  int i;
+
+  if (c->sigcontext_format != X86_64_SCF_LINUX_RT_SIGFRAME)
+    return -UNW_EBADFRAME;
+
+  i = dwarf_get (&c->dwarf, DWARF_LOC (c->sigcontext_addr + 
UC_MCONTEXT_GREGS_TRAPNO, 0), &trapno);
+  if (i < 0)
+    {
+      Debug (2, "failed to query [sigframe:trapno]; %d\n", i);
+      return i;
+    }
+  /* page fault */
+  if (trapno != UC_MCONTEXT_GREGS_TRAPNO_PF)
+    {
+      Debug (2, "[sigframe:trapno] %d not Page-Fault Exception\n", (int) 
trapno);
+      return -UNW_EBADFRAME;
+    }
+
+  i = dwarf_get (&c->dwarf, DWARF_LOC (c->sigcontext_addr + 
UC_MCONTEXT_GREGS_ERR, 0), &err);
+  if (i < 0)
+    {
+      Debug (2, "failed to query [sigframe:err]; %d\n", i);
+      return i;
+    }
+  if (!(err & (1 << UC_MCONTEXT_GREGS_ERR_ID_BIT)))
+    {
+      Debug (2, "[sigframe:err] 0x%x not &0x%x (instruction fetch)\n",
+            (int) err, (1 << UC_MCONTEXT_GREGS_ERR_ID_BIT));
+      return -UNW_EBADFRAME;
+    }
+
+  Debug (1, "[sigframe] Page-Fault Exception, instruction fetch (err = 
0x%x)\n", (int) err);
+
+  *rip_loc_pointer = DWARF_LOC (c->dwarf.cfa, 0);
+  c->dwarf.cfa += 8;
+
+  return 1;
+}
+
 PROTECTED int
 unw_step (unw_cursor_t *cursor)
 {
   struct cursor *c = (struct cursor *) cursor;
   int ret, i;
+  struct dwarf_loc rip_loc;
+  int rip_loc_set = 0;
+  unw_word_t prev_ip = c->dwarf.ip, prev_cfa = c->dwarf.cfa;
 
   Debug (1, "(cursor=%p, ip=0x%016llx)\n",
         c, (unsigned long long) c->dwarf.ip);
@@ -41,20 +98,30 @@ unw_step (unw_cursor_t *cursor)
   /* Try DWARF-based unwinding... */
   ret = dwarf_step (&c->dwarf);
 
+  /* Skip the faulty address left alone on the stack.  */
+  if (ret >= 0 && code_descriptor_trap (c, &rip_loc) > 0)
+    rip_loc_set = 1;
+
   if (ret < 0 && ret != -UNW_ENOINFO)
     {
       Debug (2, "returning %d\n", ret);
       return ret;
     }
 
-  if (likely (ret >= 0))
+  if (c->sigcontext_format == X86_64_SCF_LINUX_RT_SIGFRAME)
     {
-      /* x86_64 ABI specifies that end of call-chain is marked with a
-        NULL RBP.  */
-      if (DWARF_IS_NULL_LOC (c->dwarf.loc[RBP]))
-       c->dwarf.ip = 0;
+      unw_word_t trapno, err;
+      int trapno_ret, err_ret;
+
+      trapno_ret = dwarf_get (&c->dwarf, DWARF_LOC (c->sigcontext_addr + 
UC_MCONTEXT_GREGS_TRAPNO, 0), &trapno);
+      err_ret = dwarf_get (&c->dwarf, DWARF_LOC (c->sigcontext_addr + 
UC_MCONTEXT_GREGS_ERR, 0), &err);
+
+      Debug (2, "x86_64 sigcontext (post-step): CFA = 0x%lx, trapno = %d, err 
= 0x%x\n",
+            (unsigned long) c->dwarf.cfa, (trapno_ret < 0 ? -1 : (int) trapno),
+            (err_ret < 0 ? -1 : (int) err));
     }
-  else
+
+  if (unlikely (ret < 0))
     {
       /* DWARF failed.  There isn't much of a usable frame-chain on x86-64,
         but we do need to handle two special-cases:
@@ -68,12 +135,18 @@ unw_step (unw_cursor_t *cursor)
              via CALLQ.  Try this for all non-signal trampoline
              code.  */
 
-      unw_word_t prev_ip = c->dwarf.ip, prev_cfa = c->dwarf.cfa;
-      struct dwarf_loc rbp_loc, rsp_loc, rip_loc;
+      struct dwarf_loc rbp_loc, rsp_loc;
 
       Debug (13, "dwarf_step() failed (ret=%d), trying frame-chain\n", ret);
 
-      if (unw_is_signal_frame (cursor))
+      if (c->dwarf.ip == 0)
+        {
+         Debug (1, "[RIP=0]\n");
+
+         rip_loc = DWARF_LOC (c->dwarf.cfa, 0);
+         c->dwarf.cfa += 8;
+       }
+      else if (unw_is_signal_frame (cursor))
        {
          unw_word_t ucontext = c->dwarf.cfa;
 
@@ -109,6 +182,9 @@ unw_step (unw_cursor_t *cursor)
          c->dwarf.loc[R14] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_R14, 0);
          c->dwarf.loc[R15] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_R15, 0);
          c->dwarf.loc[RIP] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RIP, 0);
+
+         c->dwarf.loc[RBP] = rbp_loc;
+         c->dwarf.loc[RSP] = rsp_loc;
        }
       else
        {
@@ -149,13 +225,19 @@ unw_step (unw_cursor_t *cursor)
          /* Mark all registers unsaved */
          for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
            c->dwarf.loc[i] = DWARF_NULL_LOC;
-       }
 
-      c->dwarf.loc[RBP] = rbp_loc;
-      c->dwarf.loc[RSP] = rsp_loc;
+         c->dwarf.loc[RBP] = rbp_loc;
+         c->dwarf.loc[RSP] = rsp_loc;
+       }
+      rip_loc_set = 1;
+    }
+  if (rip_loc_set)
+    {
       c->dwarf.loc[RIP] = rip_loc;
       c->dwarf.ret_addr_column = RIP;
 
+      /* x86_64 ABI specifies that end of call-chain is marked with a
+        NULL RBP.  */
       if (!DWARF_IS_NULL_LOC (c->dwarf.loc[RBP]))
        {
          ret = dwarf_get (&c->dwarf, c->dwarf.loc[RIP], &c->dwarf.ip);
@@ -170,10 +252,11 @@ unw_step (unw_cursor_t *cursor)
        }
       else
        c->dwarf.ip = 0;
-
-      if (c->dwarf.ip == prev_ip && c->dwarf.cfa == prev_cfa)
-       return -UNW_EBADFRAME;
     }
+
+  if (c->dwarf.ip == prev_ip && c->dwarf.cfa == prev_cfa)
+    return -UNW_EBADFRAME;
+
   ret = (c->dwarf.ip == 0) ? 0 : 1;
   Debug (2, "returning %d\n", ret);
   return ret;
diff --git a/src/x86_64/init.h b/src/x86_64/init.h
index ae108b2..1be5ddb 100644
--- a/src/x86_64/init.h
+++ b/src/x86_64/init.h
@@ -62,6 +62,7 @@ common_init (struct cursor *c)
   c->sigcontext_format = X86_64_SCF_NONE;
   c->sigcontext_addr = 0;
 
+  c->dwarf.decrease_ip = 0;
   c->dwarf.args_size = 0;
   c->dwarf.ret_addr_column = RIP;
   c->dwarf.pi_valid = 0;
diff --git a/src/x86_64/ucontext_i.h b/src/x86_64/ucontext_i.h
index dfd93bc..7f26eaa 100644
--- a/src/x86_64/ucontext_i.h
+++ b/src/x86_64/ucontext_i.h
@@ -39,3 +39,15 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
SOFTWARE.  */
 #define UC_MCONTEXT_GREGS_RCX  0x98
 #define UC_MCONTEXT_GREGS_RSP  0xa0
 #define UC_MCONTEXT_GREGS_RIP  0xa8
+#define UC_MCONTEXT_GREGS_ERR  0xc0
+#define UC_MCONTEXT_GREGS_TRAPNO       0xc8
+
+#define UC_MCONTEXT_GREGS_TRAPNO_PF 14 /* Page-Fault Exception */
+
+/* 
http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/24593.pdf
+   Publication # 24593, Revision 3.12, page 219 (261/488):  */
+#define UC_MCONTEXT_GREGS_ERR_P_BIT   0
+#define UC_MCONTEXT_GREGS_ERR_RW_BIT  1
+#define UC_MCONTEXT_GREGS_ERR_US_BIT  2
+#define UC_MCONTEXT_GREGS_ERR_RSV_BIT 3
+#define UC_MCONTEXT_GREGS_ERR_ID_BIT  4
--- /dev/null   2008-01-29 08:10:39.761009532 +0100
+++ b/src/x86_64/Gfetch_proc_info_post.c        2008-01-29 10:32:15.000000000 
+0100
@@ -0,0 +1,51 @@
+/* libunwind - a platform-independent unwind library
+   Copyright (c) 2006 Hewlett-Packard Development Company, L.P.
+       Contributed by Jan Kratochvil <address@hidden>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
+
+#include "libunwind_i.h"
+
+HIDDEN int
+tdep_fetch_proc_info_post (struct dwarf_cursor *c, unw_word_t ip, int 
need_unwind_info)
+{
+  struct cursor *cursor = (struct cursor *) c;
+
+  /* Should happen only if `!need_unwind_info'.  */
+  if (!c->pi_valid)
+    return 0;
+  /* Should happen only if `!need_unwind_info'.  */
+  if (!c->pi.unwind_info)
+    return 0;
+
+  /* Reset the value for this frame.  */
+  cursor->sigcontext_format = X86_64_SCF_NONE;
+
+  /* Normal non-signal frames case.  */
+  if (!((struct dwarf_cie_info *) c->pi.unwind_info)->signal_frame)
+    return 0;
+
+  cursor->sigcontext_format = X86_64_SCF_LINUX_RT_SIGFRAME;
+  cursor->sigcontext_addr = c->cfa;
+
+  return 0;
+}
--- /dev/null   2008-01-29 08:10:39.761009532 +0100
+++ b/src/x86_64/Lfetch_proc_info_post.c        2008-01-29 10:59:31.000000000 
+0100
@@ -0,0 +1,5 @@
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Gfetch_proc_info_post.c"
+#endif

reply via email to

[Prev in Thread] Current Thread [Next in Thread]