Fix "info frame" in the outermost frame.
authorPedro Alves <palves@redhat.com>
Fri, 6 Dec 2013 19:48:00 +0000 (19:48 +0000)
committerPedro Alves <palves@redhat.com>
Fri, 6 Dec 2013 19:50:10 +0000 (19:50 +0000)
Doing "info frame" in the outermost frame, when that was indicated by
the next frame saying the unwound PC is undefined/not saved, results
in error and incomplete output:

 (gdb) bt
 #0  thread_function0 (arg=0x0) at threads.c:63
 #1  0x00000034cf407d14 in start_thread (arg=0x7ffff7fcb700) at pthread_create.c:309
 #2  0x000000323d4f168d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:115

 (gdb) frame 2
 #2  0x000000323d4f168d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:115
 115             call    *%rax

 (gdb) info frame
 Stack level 2, frame at 0x0:
  rip = 0x323d4f168d in clone (../sysdeps/unix/sysv/linux/x86_64/clone.S:115); saved rip Register 16 was not saved
 (gdb)

Not saved register values are treated as optimized out values
internally throughout.  stack.c:frame_info is handing unvailable
values, but not optimized out ones.  The patch deletes the
frame_unwind_caller_pc_if_available wrapper function and instead lets
errors propagate to frame_info (it's only user).

As frame_unwind_pc now needs to be able to handle and cache two
different error scenarios, the prev_pc.p variable is replaced with an
enumeration.

(FWIW, I looked into making gdbarch_unwind_pc or a variant return
struct value's instead, but it results in lots of boxing and unboxing
for no real gain -- e.g., the mips and arm implementations need to do
computation on the unboxed PC value.  Might as well throw an error on
first attempt to get at invalid contents.)

After the patch, we get:

 (gdb) info frame
 Stack level 2, frame at 0x0:
  rip = 0x323d4f168d in clone (../sysdeps/unix/sysv/linux/x86_64/clone.S:115); saved rip = <not saved>
  Outermost frame: outermost
  caller of frame at 0x7ffff7fcafc0
  source language asm.
  Arglist at 0x7ffff7fcafb8, args:
  Locals at 0x7ffff7fcafb8, Previous frame's sp is 0x7ffff7fcafc8
 (gdb)

A new test is added.  It's based off dw2-reg-undefined.exp, and tweaked to
mark the return address (rip) of "stop_frame" as undefined.

Tested on x86_64 Fedora 17.

gdb/
2013-12-06  Pedro Alves  <palves@redhat.com>

* frame.c (enum cached_copy_status): New enum.
(struct frame_info) <prev_pc.p>: Change type to enum
cached_copy_status.
(fprint_frame): Handle not saved and unavailable prev_pc values.
(frame_unwind_pc_if_available): Delete and merge contents into ...
(frame_unwind_pc): ... here.  Handle OPTIMIZED_OUT_ERROR.  Adjust
to use enum cached_copy_status.
(frame_unwind_caller_pc_if_available): Delete.
(create_new_frame): Adjust.
* frame.h (frame_unwind_caller_pc_if_available): Delete
declaration.
* stack.c (frame_info): Use frame_unwind_caller_pc instead of
frame_unwind_caller_pc_if_available, and handle
NOT_AVAILABLE_ERROR and OPTIMIZED_OUT_ERROR errors.
* valprint.c (val_print_optimized_out): Use val_print_not_saved.
(val_print_not_saved): New function.
* valprint.h (val_print_not_saved): Declare.

gdb/testsuite/
2013-12-06  Pedro Alves  <palves@redhat.com>

* gdb.dwarf2/dw2-undefined-ret-addr.S: New file.
* gdb.dwarf2/dw2-undefined-ret-addr.c: New file.
* gdb.dwarf2/dw2-undefined-ret-addr.exp: New file.

gdb/ChangeLog
gdb/frame.c
gdb/frame.h
gdb/stack.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.dwarf2/dw2-undefined-ret-addr.S [new file with mode: 0644]
gdb/testsuite/gdb.dwarf2/dw2-undefined-ret-addr.c [new file with mode: 0644]
gdb/testsuite/gdb.dwarf2/dw2-undefined-ret-addr.exp [new file with mode: 0644]
gdb/valprint.c
gdb/valprint.h

index 911131410a17060dd435bb986c696cb177cc13c3..474839254dedf6e23c18e7b80ebdbb2629d783d0 100644 (file)
@@ -1,3 +1,23 @@
+2013-12-06  Pedro Alves  <palves@redhat.com>
+
+       * frame.c (enum cached_copy_status): New enum.
+       (struct frame_info) <prev_pc.p>: Change type to enum
+       cached_copy_status.
+       (fprint_frame): Handle not saved and unavailable prev_pc values.
+       (frame_unwind_pc_if_available): Delete and merge contents into ...
+       (frame_unwind_pc): ... here.  Handle OPTIMIZED_OUT_ERROR.  Adjust
+       to use enum cached_copy_status.
+       (frame_unwind_caller_pc_if_available): Delete.
+       (create_new_frame): Adjust.
+       * frame.h (frame_unwind_caller_pc_if_available): Delete
+       declaration.
+       * stack.c (frame_info): Use frame_unwind_caller_pc instead of
+       frame_unwind_caller_pc_if_available, and handle
+       NOT_AVAILABLE_ERROR and OPTIMIZED_OUT_ERROR errors.
+       * valprint.c (val_print_optimized_out): Use val_print_not_saved.
+       (val_print_not_saved): New function.
+       * valprint.h (val_print_not_saved): Declare.
+
 2013-12-06  Andrew Burgess  <aburgess@broadcom.com>
            Pedro Alves  <palves@redhat.com>
 
index 0ba1d4dfb415f202137c370405de7160000623d2..6a8b5ae58bcf829f8632ecd13907c22029c5833a 100644 (file)
@@ -50,6 +50,23 @@ static struct frame_info *get_prev_frame_1 (struct frame_info *this_frame);
 static struct frame_info *get_prev_frame_raw (struct frame_info *this_frame);
 static const char *frame_stop_reason_symbol_string (enum unwind_stop_reason reason);
 
+/* Status of some values cached in the frame_info object.  */
+
+enum cached_copy_status
+{
+  /* Value is unknown.  */
+  CC_UNKNOWN,
+
+  /* We have a value.  */
+  CC_VALUE,
+
+  /* Value was not saved.  */
+  CC_NOT_SAVED,
+
+  /* Value is unavailable.  */
+  CC_UNAVAILABLE
+};
+
 /* We keep a cache of stack frames, each of which is a "struct
    frame_info".  The innermost one gets allocated (in
    wait_for_inferior) each time the inferior stops; current_frame
@@ -96,7 +113,7 @@ struct frame_info
 
   /* Cached copy of the previous frame's resume address.  */
   struct {
-    int p;
+    enum cached_copy_status status;
     CORE_ADDR value;
   } prev_pc;
   
@@ -366,10 +383,15 @@ fprint_frame (struct ui_file *file, struct frame_info *fi)
     fprintf_unfiltered (file, "<unknown>");
   fprintf_unfiltered (file, ",");
   fprintf_unfiltered (file, "pc=");
-  if (fi->next != NULL && fi->next->prev_pc.p)
-    fprintf_unfiltered (file, "%s", hex_string (fi->next->prev_pc.value));
-  else
+  if (fi->next == NULL || fi->next->prev_pc.status == CC_UNKNOWN)
     fprintf_unfiltered (file, "<unknown>");
+  else if (fi->next->prev_pc.status == CC_VALUE)
+    fprintf_unfiltered (file, "%s",
+                       hex_string (fi->next->prev_pc.value));
+  else if (fi->next->prev_pc.status == CC_NOT_SAVED)
+    val_print_not_saved (file);
+  else if (fi->next->prev_pc.status == CC_UNAVAILABLE)
+    val_print_unavailable (file);
   fprintf_unfiltered (file, ",");
   fprintf_unfiltered (file, "id=");
   if (fi->this_id.p)
@@ -707,10 +729,10 @@ frame_find_by_id (struct frame_id id)
   return NULL;
 }
 
-static int
-frame_unwind_pc_if_available (struct frame_info *this_frame, CORE_ADDR *pc)
+static CORE_ADDR
+frame_unwind_pc (struct frame_info *this_frame)
 {
-  if (!this_frame->prev_pc.p)
+  if (this_frame->prev_pc.status == CC_UNKNOWN)
     {
       if (gdbarch_unwind_pc_p (frame_unwind_arch (this_frame)))
        {
@@ -740,24 +762,35 @@ frame_unwind_pc_if_available (struct frame_info *this_frame, CORE_ADDR *pc)
            {
              pc = gdbarch_unwind_pc (prev_gdbarch, this_frame);
            }
-         if (ex.reason < 0 && ex.error == NOT_AVAILABLE_ERROR)
+         if (ex.reason < 0)
            {
-             this_frame->prev_pc.p = -1;
-
-             if (frame_debug)
-               fprintf_unfiltered (gdb_stdlog,
-                                   "{ frame_unwind_pc (this_frame=%d)"
-                                   " -> <unavailable> }\n",
-                                   this_frame->level);
-           }
-         else if (ex.reason < 0)
-           {
-             throw_exception (ex);
+             if (ex.error == NOT_AVAILABLE_ERROR)
+               {
+                 this_frame->prev_pc.status = CC_UNAVAILABLE;
+
+                 if (frame_debug)
+                   fprintf_unfiltered (gdb_stdlog,
+                                       "{ frame_unwind_pc (this_frame=%d)"
+                                       " -> <unavailable> }\n",
+                                       this_frame->level);
+               }
+             else if (ex.error == OPTIMIZED_OUT_ERROR)
+               {
+                 this_frame->prev_pc.status = CC_NOT_SAVED;
+
+                 if (frame_debug)
+                   fprintf_unfiltered (gdb_stdlog,
+                                       "{ frame_unwind_pc (this_frame=%d)"
+                                       " -> <not saved> }\n",
+                                       this_frame->level);
+               }
+             else
+               throw_exception (ex);
            }
          else
            {
              this_frame->prev_pc.value = pc;
-             this_frame->prev_pc.p = 1;
+             this_frame->prev_pc.status = CC_VALUE;
              if (frame_debug)
                fprintf_unfiltered (gdb_stdlog,
                                    "{ frame_unwind_pc (this_frame=%d) "
@@ -769,27 +802,17 @@ frame_unwind_pc_if_available (struct frame_info *this_frame, CORE_ADDR *pc)
       else
        internal_error (__FILE__, __LINE__, _("No unwind_pc method"));
     }
-  if (this_frame->prev_pc.p < 0)
-    {
-      *pc = -1;
-      return 0;
-    }
-  else
-    {
-      *pc = this_frame->prev_pc.value;
-      return 1;
-    }
-}
 
-static CORE_ADDR
-frame_unwind_pc (struct frame_info *this_frame)
-{
-  CORE_ADDR pc;
-
-  if (!frame_unwind_pc_if_available (this_frame, &pc))
+  if (this_frame->prev_pc.status == CC_VALUE)
+    return this_frame->prev_pc.value;
+  else if (this_frame->prev_pc.status == CC_UNAVAILABLE)
     throw_error (NOT_AVAILABLE_ERROR, _("PC not available"));
+  else if (this_frame->prev_pc.status == CC_NOT_SAVED)
+    throw_error (OPTIMIZED_OUT_ERROR, _("PC not saved"));
   else
-    return pc;
+    internal_error (__FILE__, __LINE__,
+                   "unexpected prev_pc status: %d",
+                   (int) this_frame->prev_pc.status);
 }
 
 CORE_ADDR
@@ -798,13 +821,6 @@ frame_unwind_caller_pc (struct frame_info *this_frame)
   return frame_unwind_pc (skip_artificial_frames (this_frame));
 }
 
-int
-frame_unwind_caller_pc_if_available (struct frame_info *this_frame,
-                                    CORE_ADDR *pc)
-{
-  return frame_unwind_pc_if_available (skip_artificial_frames (this_frame), pc);
-}
-
 int
 get_frame_func_if_available (struct frame_info *this_frame, CORE_ADDR *pc)
 {
@@ -1572,7 +1588,7 @@ create_new_frame (CORE_ADDR addr, CORE_ADDR pc)
      very likely to read this, and the corresponding unwinder is
      entitled to rely that the PC doesn't magically change.  */
   fi->next->prev_pc.value = pc;
-  fi->next->prev_pc.p = 1;
+  fi->next->prev_pc.status = CC_VALUE;
 
   /* We currently assume that frame chain's can't cross spaces.  */
   fi->pspace = fi->next->pspace;
index a5e1629f98419648e831e9b73a602b2e4e166f73..f8d5bc17a71576f4bd0b1feaa9eb9c75a4eb8d44 100644 (file)
@@ -548,14 +548,6 @@ extern void put_frame_register_bytes (struct frame_info *frame, int regnum,
 
 extern CORE_ADDR frame_unwind_caller_pc (struct frame_info *frame);
 
-/* Same as frame_unwind_caller_pc, but returns a boolean indication of
-   whether the caller PC is determinable (when the PC is unavailable,
-   it will not be), instead of possibly throwing an error trying to
-   read unavailable memory or registers.  */
-
-extern int frame_unwind_caller_pc_if_available (struct frame_info *this_frame,
-                                               CORE_ADDR *pc);
-
 /* Discard the specified frame.  Restoring the registers to the state
    of the caller.  */
 extern void frame_pop (struct frame_info *frame);
index 9a12eb378b7446669c6d3b72fbdebe45f376d096..848bcb0a2626c81d69a10bd03801549d3fdc2e1e 100644 (file)
@@ -1408,6 +1408,7 @@ frame_info (char *addr_exp, int from_tty)
   CORE_ADDR frame_pc;
   int frame_pc_p;
   CORE_ADDR caller_pc;
+  volatile struct gdb_exception ex;
 
   fi = parse_frame_specification_1 (addr_exp, "No stack.", &selected_frame_p);
   gdbarch = get_frame_arch (fi);
@@ -1493,11 +1494,29 @@ frame_info (char *addr_exp, int from_tty)
                     sal.line);
   puts_filtered ("; ");
   wrap_here ("    ");
-  printf_filtered ("saved %s ", pc_regname);
-  if (frame_unwind_caller_pc_if_available (fi, &caller_pc))
-    fputs_filtered (paddress (gdbarch, caller_pc), gdb_stdout);
+  printf_filtered ("saved %s = ", pc_regname);
+
+  TRY_CATCH (ex, RETURN_MASK_ERROR)
+    {
+      caller_pc = frame_unwind_caller_pc (fi);
+    }
+  if (ex.reason < 0)
+    {
+      switch (ex.error)
+       {
+       case NOT_AVAILABLE_ERROR:
+         val_print_unavailable (gdb_stdout);
+         break;
+       case OPTIMIZED_OUT_ERROR:
+         val_print_not_saved (gdb_stdout);
+         break;
+       default:
+         fprintf_filtered (gdb_stdout, _("<error: %s>"), ex.message);
+         break;
+       }
+    }
   else
-    fputs_filtered ("<unavailable>", gdb_stdout);
+    fputs_filtered (paddress (gdbarch, caller_pc), gdb_stdout);
   printf_filtered ("\n");
 
   if (calling_frame_info == NULL)
index 14bf46c386fbbdcc024ebac644058f57054df700..52e7ddc9bfd2a75aa147b4cd4d361a425cce9bb9 100644 (file)
@@ -1,3 +1,9 @@
+2013-12-06  Pedro Alves  <palves@redhat.com>
+
+       * gdb.dwarf2/dw2-undefined-ret-addr.S: New file.
+       * gdb.dwarf2/dw2-undefined-ret-addr.c: New file.
+       * gdb.dwarf2/dw2-undefined-ret-addr.exp: New file.
+
 2013-12-06  Doug Evans  <dje@google.com>
 
        * gdb.base/break.exp: Fix setting of $baz.
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-undefined-ret-addr.S b/gdb/testsuite/gdb.dwarf2/dw2-undefined-ret-addr.S
new file mode 100644 (file)
index 0000000..fc92016
--- /dev/null
@@ -0,0 +1,508 @@
+/*
+   Copyright 2013 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+       /* The FDE entry for "stop_frame" in the .debug_frame section has
+       been hand modified to mark the return address (rip) as undefined.
+       Otherwise this file is as generated by gcc 4.7.2 for x86_64.  */
+       .file   "dw2-undefined-ret-addr.c"
+       .text
+.Ltext0:
+       .globl  stop_frame
+       .type   stop_frame, @function
+stop_frame:
+.LFB0:
+       .file 1 "dw2-undefined-ret-addr.c"
+       .loc 1 19 0
+       pushq   %rbp
+.LCFI0:
+       movq    %rsp, %rbp
+.LCFI1:
+       .loc 1 22 0
+       popq    %rbp
+.LCFI2:
+       ret
+.LFE0:
+       .size   stop_frame, .-stop_frame
+       .globl  first_frame
+       .type   first_frame, @function
+first_frame:
+.LFB1:
+       .loc 1 26 0
+       pushq   %rbp
+.LCFI3:
+       movq    %rsp, %rbp
+.LCFI4:
+       .loc 1 27 0
+       movl    $0, %eax
+       call    stop_frame
+       .loc 1 28 0
+       popq    %rbp
+.LCFI5:
+       ret
+.LFE1:
+       .size   first_frame, .-first_frame
+       .globl  main
+       .type   main, @function
+main:
+.LFB2:
+       .loc 1 32 0
+       pushq   %rbp
+.LCFI6:
+       movq    %rsp, %rbp
+.LCFI7:
+       .loc 1 33 0
+       movl    $0, %eax
+       call    first_frame
+       .loc 1 35 0
+       movl    $0, %eax
+       .loc 1 36 0
+       popq    %rbp
+.LCFI8:
+       ret
+.LFE2:
+       .size   main, .-main
+       .section        .debug_frame,"",@progbits
+.Lframe0:
+       .long   .LECIE0-.LSCIE0
+.LSCIE0:
+       .long   0xffffffff
+       .byte   0x1
+       .string ""
+       .uleb128 0x1
+       .sleb128 -8
+       .byte   0x10
+       .byte   0xc
+       .uleb128 0x7
+       .uleb128 0x8
+       .byte   0x90
+       .uleb128 0x1
+       .align 8
+.LECIE0:
+        /* This FDE entry, for stop_frame was modified to mark
+           registers 0 -> 6 as being undefined.  */
+.LSFDE0:
+       .long   .LEFDE0-.LASFDE0
+.LASFDE0:
+       .long   .Lframe0
+       .quad   .LFB0
+       .quad   .LFE0-.LFB0
+
+               /* START OF NEW CONTENT.  */
+        .byte   0x7                     /* DW_CFA_undefined */
+        .uleb128 0x10                   /*   ULEB128 register */
+               /* END OF NEW CONTENT.  */
+
+       .byte   0x4
+       .long   .LCFI0-.LFB0
+       .byte   0xe
+       .uleb128 0x10
+       .byte   0x86
+       .uleb128 0x2
+       .byte   0x4
+       .long   .LCFI1-.LCFI0
+       .byte   0xd
+       .uleb128 0x6
+       .byte   0x4
+       .long   .LCFI2-.LCFI1
+       .byte   0xc
+       .uleb128 0x7
+       .uleb128 0x8
+       .align 8
+.LEFDE0:
+.LSFDE2:
+       .long   .LEFDE2-.LASFDE2
+.LASFDE2:
+       .long   .Lframe0
+       .quad   .LFB1
+       .quad   .LFE1-.LFB1
+       .byte   0x4
+       .long   .LCFI3-.LFB1
+       .byte   0xe
+       .uleb128 0x10
+       .byte   0x86
+       .uleb128 0x2
+       .byte   0x4
+       .long   .LCFI4-.LCFI3
+       .byte   0xd
+       .uleb128 0x6
+       .byte   0x4
+       .long   .LCFI5-.LCFI4
+       .byte   0xc
+       .uleb128 0x7
+       .uleb128 0x8
+       .align 8
+.LEFDE2:
+.LSFDE4:
+       .long   .LEFDE4-.LASFDE4
+.LASFDE4:
+       .long   .Lframe0
+       .quad   .LFB2
+       .quad   .LFE2-.LFB2
+       .byte   0x4
+       .long   .LCFI6-.LFB2
+       .byte   0xe
+       .uleb128 0x10
+       .byte   0x86
+       .uleb128 0x2
+       .byte   0x4
+       .long   .LCFI7-.LCFI6
+       .byte   0xd
+       .uleb128 0x6
+       .byte   0x4
+       .long   .LCFI8-.LCFI7
+       .byte   0xc
+       .uleb128 0x7
+       .uleb128 0x8
+       .align 8
+.LEFDE4:
+       .section        .eh_frame,"a",@progbits
+.Lframe1:
+       .long   .LECIE1-.LSCIE1
+.LSCIE1:
+       .long   0
+       .byte   0x1
+       .string "zR"
+       .uleb128 0x1
+       .sleb128 -8
+       .byte   0x10
+       .uleb128 0x1
+       .byte   0x3
+       .byte   0xc
+       .uleb128 0x7
+       .uleb128 0x8
+       .byte   0x90
+       .uleb128 0x1
+       .align 8
+.LECIE1:
+.LSFDE7:
+       .long   .LEFDE7-.LASFDE7
+.LASFDE7:
+       .long   .LASFDE7-.Lframe1
+       .long   .LFB0
+       .long   .LFE0-.LFB0
+       .uleb128 0
+       .byte   0x4
+       .long   .LCFI0-.LFB0
+       .byte   0xe
+       .uleb128 0x10
+       .byte   0x86
+       .uleb128 0x2
+       .byte   0x4
+       .long   .LCFI1-.LCFI0
+       .byte   0xd
+       .uleb128 0x6
+       .byte   0x4
+       .long   .LCFI2-.LCFI1
+       .byte   0xc
+       .uleb128 0x7
+       .uleb128 0x8
+       .align 8
+.LEFDE7:
+.LSFDE9:
+       .long   .LEFDE9-.LASFDE9
+.LASFDE9:
+       .long   .LASFDE9-.Lframe1
+       .long   .LFB1
+       .long   .LFE1-.LFB1
+       .uleb128 0
+       .byte   0x4
+       .long   .LCFI3-.LFB1
+       .byte   0xe
+       .uleb128 0x10
+       .byte   0x86
+       .uleb128 0x2
+       .byte   0x4
+       .long   .LCFI4-.LCFI3
+       .byte   0xd
+       .uleb128 0x6
+       .byte   0x4
+       .long   .LCFI5-.LCFI4
+       .byte   0xc
+       .uleb128 0x7
+       .uleb128 0x8
+       .align 8
+.LEFDE9:
+.LSFDE11:
+       .long   .LEFDE11-.LASFDE11
+.LASFDE11:
+       .long   .LASFDE11-.Lframe1
+       .long   .LFB2
+       .long   .LFE2-.LFB2
+       .uleb128 0
+       .byte   0x4
+       .long   .LCFI6-.LFB2
+       .byte   0xe
+       .uleb128 0x10
+       .byte   0x86
+       .uleb128 0x2
+       .byte   0x4
+       .long   .LCFI7-.LCFI6
+       .byte   0xd
+       .uleb128 0x6
+       .byte   0x4
+       .long   .LCFI8-.LCFI7
+       .byte   0xc
+       .uleb128 0x7
+       .uleb128 0x8
+       .align 8
+.LEFDE11:
+       .text
+.Letext0:
+       .section        .debug_info,"",@progbits
+.Ldebug_info0:
+       .long   0x8c
+       .value  0x2
+       .long   .Ldebug_abbrev0
+       .byte   0x8
+       .uleb128 0x1
+       .long   .LASF2
+       .byte   0x1
+       .long   .LASF3
+       .long   .LASF4
+       .quad   .Ltext0
+       .quad   .Letext0
+       .long   .Ldebug_line0
+       .uleb128 0x2
+       .byte   0x1
+       .long   .LASF0
+       .byte   0x1
+       .byte   0x12
+       .quad   .LFB0
+       .quad   .LFE0
+       .long   .LLST0
+       .byte   0x1
+       .uleb128 0x3
+       .byte   0x1
+       .long   .LASF1
+       .byte   0x1
+       .byte   0x19
+       .quad   .LFB1
+       .quad   .LFE1
+       .long   .LLST1
+       .byte   0x1
+       .uleb128 0x4
+       .byte   0x1
+       .long   .LASF5
+       .byte   0x1
+       .byte   0x1f
+       .long   0x88
+       .quad   .LFB2
+       .quad   .LFE2
+       .long   .LLST2
+       .byte   0x1
+       .uleb128 0x5
+       .byte   0x4
+       .byte   0x5
+       .string "int"
+       .byte   0
+       .section        .debug_abbrev,"",@progbits
+.Ldebug_abbrev0:
+       .uleb128 0x1
+       .uleb128 0x11
+       .byte   0x1
+       .uleb128 0x25
+       .uleb128 0xe
+       .uleb128 0x13
+       .uleb128 0xb
+       .uleb128 0x3
+       .uleb128 0xe
+       .uleb128 0x1b
+       .uleb128 0xe
+       .uleb128 0x11
+       .uleb128 0x1
+       .uleb128 0x12
+       .uleb128 0x1
+       .uleb128 0x10
+       .uleb128 0x6
+       .byte   0
+       .byte   0
+       .uleb128 0x2
+       .uleb128 0x2e
+       .byte   0
+       .uleb128 0x3f
+       .uleb128 0xc
+       .uleb128 0x3
+       .uleb128 0xe
+       .uleb128 0x3a
+       .uleb128 0xb
+       .uleb128 0x3b
+       .uleb128 0xb
+       .uleb128 0x11
+       .uleb128 0x1
+       .uleb128 0x12
+       .uleb128 0x1
+       .uleb128 0x40
+       .uleb128 0x6
+       .uleb128 0x2117
+       .uleb128 0xc
+       .byte   0
+       .byte   0
+       .uleb128 0x3
+       .uleb128 0x2e
+       .byte   0
+       .uleb128 0x3f
+       .uleb128 0xc
+       .uleb128 0x3
+       .uleb128 0xe
+       .uleb128 0x3a
+       .uleb128 0xb
+       .uleb128 0x3b
+       .uleb128 0xb
+       .uleb128 0x11
+       .uleb128 0x1
+       .uleb128 0x12
+       .uleb128 0x1
+       .uleb128 0x40
+       .uleb128 0x6
+       .uleb128 0x2116
+       .uleb128 0xc
+       .byte   0
+       .byte   0
+       .uleb128 0x4
+       .uleb128 0x2e
+       .byte   0
+       .uleb128 0x3f
+       .uleb128 0xc
+       .uleb128 0x3
+       .uleb128 0xe
+       .uleb128 0x3a
+       .uleb128 0xb
+       .uleb128 0x3b
+       .uleb128 0xb
+       .uleb128 0x49
+       .uleb128 0x13
+       .uleb128 0x11
+       .uleb128 0x1
+       .uleb128 0x12
+       .uleb128 0x1
+       .uleb128 0x40
+       .uleb128 0x6
+       .uleb128 0x2116
+       .uleb128 0xc
+       .byte   0
+       .byte   0
+       .uleb128 0x5
+       .uleb128 0x24
+       .byte   0
+       .uleb128 0xb
+       .uleb128 0xb
+       .uleb128 0x3e
+       .uleb128 0xb
+       .uleb128 0x3
+       .uleb128 0x8
+       .byte   0
+       .byte   0
+       .byte   0
+       .section        .debug_loc,"",@progbits
+.Ldebug_loc0:
+.LLST0:
+       .quad   .LFB0-.Ltext0
+       .quad   .LCFI0-.Ltext0
+       .value  0x2
+       .byte   0x77
+       .sleb128 8
+       .quad   .LCFI0-.Ltext0
+       .quad   .LCFI1-.Ltext0
+       .value  0x2
+       .byte   0x77
+       .sleb128 16
+       .quad   .LCFI1-.Ltext0
+       .quad   .LCFI2-.Ltext0
+       .value  0x2
+       .byte   0x76
+       .sleb128 16
+       .quad   .LCFI2-.Ltext0
+       .quad   .LFE0-.Ltext0
+       .value  0x2
+       .byte   0x77
+       .sleb128 8
+       .quad   0
+       .quad   0
+.LLST1:
+       .quad   .LFB1-.Ltext0
+       .quad   .LCFI3-.Ltext0
+       .value  0x2
+       .byte   0x77
+       .sleb128 8
+       .quad   .LCFI3-.Ltext0
+       .quad   .LCFI4-.Ltext0
+       .value  0x2
+       .byte   0x77
+       .sleb128 16
+       .quad   .LCFI4-.Ltext0
+       .quad   .LCFI5-.Ltext0
+       .value  0x2
+       .byte   0x76
+       .sleb128 16
+       .quad   .LCFI5-.Ltext0
+       .quad   .LFE1-.Ltext0
+       .value  0x2
+       .byte   0x77
+       .sleb128 8
+       .quad   0
+       .quad   0
+.LLST2:
+       .quad   .LFB2-.Ltext0
+       .quad   .LCFI6-.Ltext0
+       .value  0x2
+       .byte   0x77
+       .sleb128 8
+       .quad   .LCFI6-.Ltext0
+       .quad   .LCFI7-.Ltext0
+       .value  0x2
+       .byte   0x77
+       .sleb128 16
+       .quad   .LCFI7-.Ltext0
+       .quad   .LCFI8-.Ltext0
+       .value  0x2
+       .byte   0x76
+       .sleb128 16
+       .quad   .LCFI8-.Ltext0
+       .quad   .LFE2-.Ltext0
+       .value  0x2
+       .byte   0x77
+       .sleb128 8
+       .quad   0
+       .quad   0
+       .section        .debug_aranges,"",@progbits
+       .long   0x2c
+       .value  0x2
+       .long   .Ldebug_info0
+       .byte   0x8
+       .byte   0
+       .value  0
+       .value  0
+       .quad   .Ltext0
+       .quad   .Letext0-.Ltext0
+       .quad   0
+       .quad   0
+       .section        .debug_line,"",@progbits
+.Ldebug_line0:
+       .section        .debug_str,"MS",@progbits,1
+.LASF0:
+       .string "stop_frame"
+.LASF3:
+       .string "dw2-undefined-ret-addr.c"
+.LASF2:
+       .string "GNU C 4.7.2"
+.LASF1:
+       .string "first_frame"
+.LASF5:
+       .string "main"
+.LASF4:
+       .string "/home/username/src/gdb/testsuite/gdb.dwarf2"
+       .ident  "GCC: (GNU) 4.7.2"
+       .section        .note.GNU-stack,"",@progbits
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-undefined-ret-addr.c b/gdb/testsuite/gdb.dwarf2/dw2-undefined-ret-addr.c
new file mode 100644 (file)
index 0000000..6c5b06a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+   Copyright 2013 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+void
+stop_frame ()
+{
+  /* The debug information for this frame is modified in the accompanying
+     .S file, to mark the return address as undefined.  */
+}
+
+void
+first_frame ()
+{
+  stop_frame ();
+}
+
+int
+main ()
+{
+  first_frame ();
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-undefined-ret-addr.exp b/gdb/testsuite/gdb.dwarf2/dw2-undefined-ret-addr.exp
new file mode 100644 (file)
index 0000000..40ac356
--- /dev/null
@@ -0,0 +1,58 @@
+# Copyright 2013 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+load_lib dwarf.exp
+
+standard_testfile .S
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if ![dwarf2_support] {
+    return 0
+}
+
+# This test can only be run on x86-64 targets.
+if {![istarget x86_64-*] || ![is_lp64_target]} {
+    return 0
+}
+
+if {[prepare_for_testing "$testfile.exp" "$testfile" $srcfile {nodebug}]} {
+    return -1
+}
+
+if ![runto "stop_frame"] {
+    return -1
+}
+
+# stop_frame should be the outermost frame.
+
+# Check that backtrace shows only frame #0.
+gdb_test "bt" "#0\[ \]\+stop_frame \[^\r\n\]\+"
+
+# And that "up" doesn't work.
+gdb_test "up" \
+    "Initial frame selected; you cannot go up\\." \
+    "up refuses to go up"
+
+# "info frame" unwinds the PC for "saved ... = ".  Make sure that
+# doesn't cause an error, and shows "<not saved>".
+gdb_test "info frame" [join [list \
+    "Stack level 0, frame at $hex\:" \
+    " rip = $hex in stop_frame \\(dw2-undefined-ret-addr\\.c:22\\); saved rip = <not saved>" \
+    " Outermost frame: outermost" \
+    " source language c\\." \
+    " Arglist at $hex, args\: " \
+    " Locals at $hex, Previous frame's sp is $hex" \
+    " Saved registers\:" \
+    "  rbp at $hex.*" \
+] "\r\n"]
index 0124934cb7a21d1eb9e74f173e1256797f6e1818..7ebcdfd99106dc3ecdf5a6d927ce4c9b3e823f69 100644 (file)
@@ -339,11 +339,17 @@ void
 val_print_optimized_out (const struct value *val, struct ui_file *stream)
 {
   if (val != NULL && value_lval_const (val) == lval_register)
-    fprintf_filtered (stream, _("<not saved>"));
+    val_print_not_saved (stream);
   else
     fprintf_filtered (stream, _("<optimized out>"));
 }
 
+void
+val_print_not_saved (struct ui_file *stream)
+{
+  fprintf_filtered (stream, _("<not saved>"));
+}
+
 void
 val_print_unavailable (struct ui_file *stream)
 {
index 1d86ed73da801ae43e594202dbbbf3bcee64e4e6..8ed259d6b7486f7615ba4158c9c0f33d2599235a 100644 (file)
@@ -163,6 +163,9 @@ extern int read_string (CORE_ADDR addr, int len, int width,
 extern void val_print_optimized_out (const struct value *val,
                                     struct ui_file *stream);
 
+/* Prints "<not saved>" to STREAM.  */
+extern void val_print_not_saved (struct ui_file *stream);
+
 extern void val_print_unavailable (struct ui_file *stream);
 
 extern void val_print_invalid_address (struct ui_file *stream);