diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 51f4d9595e..b076a45a9a 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,10 @@ +2020-04-27 Luis Machado + + * 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 * command.h (enum command_class) : Remove. diff --git a/gdb/dwarf2/frame-tailcall.c b/gdb/dwarf2/frame-tailcall.c index 01bb134a5c..16dba2b201 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 ac1016b083..ff27b9f00e 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 cfc15022ed..e835d49f9c 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. */