diff options
author | Markus Metzger <markus.t.metzger@intel.com> | 2016-02-05 09:32:53 +0100 |
---|---|---|
committer | Markus Metzger <markus.t.metzger@intel.com> | 2016-03-17 11:48:49 +0100 |
commit | 2ef34d11f61d79dcb152713aa059051d8cd3295d (patch) | |
tree | fcba0ae0b1c2a8d90449061210892c64220c0020 /gdb/frame.c | |
parent | 6c410d3b60cfdab8211565db240fe783e18a73a4 (diff) | |
download | binutils-2ef34d11f61d79dcb152713aa059051d8cd3295d.zip binutils-2ef34d11f61d79dcb152713aa059051d8cd3295d.tar.gz binutils-2ef34d11f61d79dcb152713aa059051d8cd3295d.tar.bz2 |
btrace: fix PR gdb/19829
This is a backport of
33b4777ca1b7 btrace, frame: fix crash in get_frame_type
a038fa3e14a4 stack: check frame_unwind_caller_id
2f3ef606b912 frame: add skip_tailcall_frames
In skip_artificial_frames we repeatedly call get_prev_frame_always until we get
a non-inline and non-tailcall frame assuming that there must be such a frame
eventually.
For record targets, however, we may have a frame chain that consists only of
artificial frames. This leads to a crash in get_frame_type when dereferencing a
NULL frame pointer.
Change skip_artificial_frames and skip_tailcall_frames to return NULL in such a
case and modify each caller to cope with a NULL return.
In frame_unwind_caller_pc and frame_unwind_caller_arch, we simply assert that
the returned value is not NULL. Their caller was supposed to check
frame_unwind_caller_id before calling those functions.
In other cases, we thrown an error.
In infcmd further move the skip_tailcall_frames call to the forward-stepping
case since we don't need a frame for reverse execution and we don't want to fail
because of that. Reverse-finish does make sense for a tailcall frame.
gdb/
* frame.h (skip_tailcall_frames): New.
* infcmd.c (finish_command): Call skip_tailcall_frames.
* frame.c (skip_artificial_frames): Return NULL if only artificial frames
are found. Update comment.
(frame_pop): Call skip_tailcall_frames.
(frame_unwind_caller_id): Handle NULL return.
(frame_unwind_caller_pc, frame_unwind_caller_arch): Assert that
skip_artificial_frames does not return NULL.
(frame_pop): Add an error if only tailcall frames are found.
* infcmd.c (finish_command): Move skip_tailcall_frames call into forward-
execution case. Add an error if only tailcall frames are found.
* stack.c (frame_info): Check frame_unwind_caller_id.
testsuite/
* gdb.btrace/tailcall-only.exp: New.
* gdb.btrace/tailcall-only.c: New.
* gdb.btrace/x86_64-tailcall-only.S: New.
* gdb.btrace/i686-tailcall-only.S: New.
Diffstat (limited to 'gdb/frame.c')
-rw-r--r-- | gdb/frame.c | 60 |
1 files changed, 51 insertions, 9 deletions
diff --git a/gdb/frame.c b/gdb/frame.c index 48c9b33..d621dd7 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -420,7 +420,8 @@ fprint_frame (struct ui_file *file, struct frame_info *fi) /* Given FRAME, return the enclosing frame as found in real frames read-in from inferior memory. Skip any previous frames which were made up by GDB. - Return the original frame if no immediate previous frames exist. */ + Return FRAME if FRAME is a non-artificial frame. + Return NULL if FRAME is the start of an artificial-only chain. */ static struct frame_info * skip_artificial_frames (struct frame_info *frame) @@ -428,12 +429,34 @@ skip_artificial_frames (struct frame_info *frame) /* Note we use get_prev_frame_always, and not get_prev_frame. The latter will truncate the frame chain, leading to this function unintentionally returning a null_frame_id (e.g., when the user - sets a backtrace limit). This is safe, because as these frames - are made up by GDB, there must be a real frame in the chain - below. */ + sets a backtrace limit). + + Note that for record targets we may get a frame chain that consists + of artificial frames only. */ while (get_frame_type (frame) == INLINE_FRAME || get_frame_type (frame) == TAILCALL_FRAME) - frame = get_prev_frame_always (frame); + { + frame = get_prev_frame_always (frame); + if (frame == NULL) + break; + } + + return frame; +} + +/* See frame.h. */ + +struct frame_info * +skip_tailcall_frames (struct frame_info *frame) +{ + while (get_frame_type (frame) == TAILCALL_FRAME) + { + /* Note that for record targets we may get a frame chain that consists of + tailcall frames only. */ + frame = get_prev_frame (frame); + if (frame == NULL) + break; + } return frame; } @@ -496,6 +519,9 @@ frame_unwind_caller_id (struct frame_info *next_frame) requests the frame ID of "main()"s caller. */ next_frame = skip_artificial_frames (next_frame); + if (next_frame == NULL) + return null_frame_id; + this_frame = get_prev_frame_always (next_frame); if (this_frame) return get_frame_id (skip_artificial_frames (this_frame)); @@ -869,7 +895,14 @@ frame_unwind_pc (struct frame_info *this_frame) CORE_ADDR frame_unwind_caller_pc (struct frame_info *this_frame) { - return frame_unwind_pc (skip_artificial_frames (this_frame)); + this_frame = skip_artificial_frames (this_frame); + + /* We must have a non-artificial frame. The caller is supposed to check + the result of frame_unwind_caller_id (), which returns NULL_FRAME_ID + in this case. */ + gdb_assert (this_frame != NULL); + + return frame_unwind_pc (this_frame); } int @@ -972,8 +1005,10 @@ frame_pop (struct frame_info *this_frame) /* Ignore TAILCALL_FRAME type frames, they were executed already before entering THISFRAME. */ - while (get_frame_type (prev_frame) == TAILCALL_FRAME) - prev_frame = get_prev_frame (prev_frame); + prev_frame = skip_tailcall_frames (prev_frame); + + if (prev_frame == NULL) + error (_("Cannot find the caller frame.")); /* Make a copy of all the register values unwound from this frame. Save them in a scratch buffer so that there isn't a race between @@ -2561,7 +2596,14 @@ frame_unwind_arch (struct frame_info *next_frame) struct gdbarch * frame_unwind_caller_arch (struct frame_info *next_frame) { - return frame_unwind_arch (skip_artificial_frames (next_frame)); + next_frame = skip_artificial_frames (next_frame); + + /* We must have a non-artificial frame. The caller is supposed to check + the result of frame_unwind_caller_id (), which returns NULL_FRAME_ID + in this case. */ + gdb_assert (next_frame != NULL); + + return frame_unwind_arch (next_frame); } /* Gets the language of FRAME. */ |