aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog7
-rw-r--r--gdb/dwarf2/frame-tailcall.c39
-rw-r--r--gdb/frame.c8
-rw-r--r--gdb/frame.h4
4 files changed, 55 insertions, 3 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 51f4d95..b076a45 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,10 @@
+2020-04-27 Luis Machado <luis.machado@linaro.org>
+
+ * dwarf2/frame-tailcall.c (dwarf2_tailcall_sniffer_first): Handle
+ problematic inline frame unwinding situation.
+ * frame.c (frame_id_computed_p): New function.
+ * frame.h (frame_id_computed_p): New prototype.
+
2020-04-26 Tom Tromey <tom@tromey.com>
* command.h (enum command_class) <class_pseudo>: Remove.
diff --git a/gdb/dwarf2/frame-tailcall.c b/gdb/dwarf2/frame-tailcall.c
index 01bb134..16dba2b 100644
--- a/gdb/dwarf2/frame-tailcall.c
+++ b/gdb/dwarf2/frame-tailcall.c
@@ -384,10 +384,43 @@ dwarf2_tailcall_sniffer_first (struct frame_info *this_frame,
prev_gdbarch = frame_unwind_arch (this_frame);
+ /* The dwarf2 tailcall sniffer runs early, at the end of populating the
+ dwarf2 frame cache for the current frame. If there exists inline
+ frames inner (next) to the current frame, there is a good possibility
+ of that inline frame not having a computed frame id yet.
+
+ This is because computing such a frame id requires us to walk through
+ the frame chain until we find the first normal frame after the inline
+ frame and then compute the normal frame's id first.
+
+ Some architectures' compilers generate enough register location
+ information for a dwarf unwinder to fetch PC without relying on inner
+ frames (x86_64 for example). In this case the PC is retrieved
+ according to dwarf rules.
+
+ But others generate less strict dwarf data for which assumptions are
+ made (like interpreting DWARF2_FRAME_REG_UNSPECIFIED as
+ DWARF2_FRAME_REG_SAME_VALUE). For such cases, GDB may attempt to
+ create lazy values for registers, and those lazy values must be
+ created with a valid frame id, but we potentially have no valid id.
+
+ So, to avoid breakage, if we see a dangerous situation with inline
+ frames without a computed id, use safer functions to retrieve the
+ current frame's PC. Otherwise use the provided dwarf rules. */
+ frame_info *next_frame = get_next_frame (this_frame);
+
/* Simulate frame_unwind_pc without setting this_frame->prev_pc.p. */
- get_frame_register (this_frame, gdbarch_pc_regnum (prev_gdbarch),
- (gdb_byte *) &prev_pc);
- prev_pc = gdbarch_addr_bits_remove (prev_gdbarch, prev_pc);
+ if (next_frame != nullptr && get_frame_type (next_frame) == INLINE_FRAME
+ && !frame_id_computed_p (next_frame))
+ {
+ /* The next frame is an inline frame and its frame id has not been
+ computed yet. */
+ get_frame_register (this_frame, gdbarch_pc_regnum (prev_gdbarch),
+ (gdb_byte *) &prev_pc);
+ prev_pc = gdbarch_addr_bits_remove (prev_gdbarch, prev_pc);
+ }
+ else
+ prev_pc = gdbarch_unwind_pc (prev_gdbarch, this_frame);
/* call_site_find_chain can throw an exception. */
chain = call_site_find_chain (prev_gdbarch, prev_pc, this_pc);
diff --git a/gdb/frame.c b/gdb/frame.c
index ac1016b..ff27b9f 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -687,6 +687,14 @@ frame_id_build_wild (CORE_ADDR stack_addr)
return id;
}
+bool
+frame_id_computed_p (struct frame_info *frame)
+{
+ gdb_assert (frame != nullptr);
+
+ return frame->this_id.p != 0;
+}
+
int
frame_id_p (struct frame_id l)
{
diff --git a/gdb/frame.h b/gdb/frame.h
index cfc1502..e835d49 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -236,6 +236,10 @@ extern struct frame_id
as the special identifier address are set to indicate wild cards. */
extern struct frame_id frame_id_build_wild (CORE_ADDR stack_addr);
+/* Returns true if FRAME's id has been computed.
+ Returns false otherwise. */
+extern bool frame_id_computed_p (struct frame_info *frame);
+
/* Returns non-zero when L is a valid frame (a valid frame has a
non-zero .base). The outermost frame is valid even without an
ID. */