--- /dev/null
+/* Definitions for dealing with stack frames, for GDB, the GNU debugger.
+
+ Copyright (C) 1986-2022 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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/>. */
+
+#ifndef GDB_FRAME_ID_H
+#define GDB_FRAME_ID_H 1
+
+/* Status of a given frame's stack. */
+
+enum frame_id_stack_status
+{
+ /* Stack address is invalid. */
+ FID_STACK_INVALID = 0,
+
+ /* Stack address is valid, and is found in the stack_addr field. */
+ FID_STACK_VALID = 1,
+
+ /* Sentinel frame. */
+ FID_STACK_SENTINEL = 2,
+
+ /* Outer frame. Since a frame's stack address is typically defined as the
+ value the stack pointer had prior to the activation of the frame, an outer
+ frame doesn't have a stack address. The frame ids of frames inlined in the
+ outer frame are also of this type. */
+ FID_STACK_OUTER = 3,
+
+ /* Stack address is unavailable. I.e., there's a valid stack, but
+ we don't know where it is (because memory or registers we'd
+ compute it from were not collected). */
+ FID_STACK_UNAVAILABLE = -1
+};
+
+/* The frame object's ID. This provides a per-frame unique identifier
+ that can be used to relocate a `struct frame_info' after a target
+ resume or a frame cache destruct. It of course assumes that the
+ inferior hasn't unwound the stack past that frame. */
+
+struct frame_id
+{
+ /* The frame's stack address. This shall be constant through out
+ the lifetime of a frame. Note that this requirement applies to
+ not just the function body, but also the prologue and (in theory
+ at least) the epilogue. Since that value needs to fall either on
+ the boundary, or within the frame's address range, the frame's
+ outer-most address (the inner-most address of the previous frame)
+ is used. Watch out for all the legacy targets that still use the
+ function pointer register or stack pointer register. They are
+ wrong.
+
+ This field is valid only if frame_id.stack_status is
+ FID_STACK_VALID. It will be 0 for other
+ FID_STACK_... statuses. */
+ CORE_ADDR stack_addr;
+
+ /* The frame's code address. This shall be constant through out the
+ lifetime of the frame. While the PC (a.k.a. resume address)
+ changes as the function is executed, this code address cannot.
+ Typically, it is set to the address of the entry point of the
+ frame's function (as returned by get_frame_func).
+
+ For inlined functions (INLINE_DEPTH != 0), this is the address of
+ the first executed instruction in the block corresponding to the
+ inlined function.
+
+ This field is valid only if code_addr_p is true. Otherwise, this
+ frame is considered to have a wildcard code address, i.e. one that
+ matches every address value in frame comparisons. */
+ CORE_ADDR code_addr;
+
+ /* The frame's special address. This shall be constant through out the
+ lifetime of the frame. This is used for architectures that may have
+ frames that do not change the stack but are still distinct and have
+ some form of distinct identifier (e.g. the ia64 which uses a 2nd
+ stack for registers). This field is treated as unordered - i.e. will
+ not be used in frame ordering comparisons.
+
+ This field is valid only if special_addr_p is true. Otherwise, this
+ frame is considered to have a wildcard special address, i.e. one that
+ matches every address value in frame comparisons. */
+ CORE_ADDR special_addr;
+
+ /* Flags to indicate the above fields have valid contents. */
+ ENUM_BITFIELD(frame_id_stack_status) stack_status : 3;
+ unsigned int code_addr_p : 1;
+ unsigned int special_addr_p : 1;
+
+ /* It is non-zero for a frame made up by GDB without stack data
+ representation in inferior, such as INLINE_FRAME or TAILCALL_FRAME.
+ Caller of inlined function will have it zero, each more inner called frame
+ will have it increasingly one, two etc. Similarly for TAILCALL_FRAME. */
+ int artificial_depth;
+
+ /* Return a string representation of this frame id. */
+ std::string to_string () const;
+
+ /* Returns true when this frame_id and R identify the same
+ frame. */
+ bool operator== (const frame_id &r) const;
+
+ /* Inverse of ==. */
+ bool operator!= (const frame_id &r) const
+ {
+ return !(*this == r);
+ }
+};
+
+/* Methods for constructing and comparing Frame IDs. */
+
+/* For convenience. All fields are zero. This means "there is no frame". */
+extern const struct frame_id null_frame_id;
+
+/* Sentinel frame. */
+extern const struct frame_id sentinel_frame_id;
+
+/* This means "there is no frame ID, but there is a frame". It should be
+ replaced by best-effort frame IDs for the outermost frame, somehow.
+ The implementation is only special_addr_p set. */
+extern const struct frame_id outer_frame_id;
+
+#endif /* ifdef GDB_FRAME_ID_H */
#define GDB_FRAME_INFO_H
#include "gdbsupport/intrusive_list.h"
+#include "frame-id.h"
struct frame_info;
+/* Forward declarations of functions, needed for the frame_info_ptr
+ to work correctly. */
extern void reinit_frame_cache ();
+extern struct frame_id get_frame_id (frame_info_ptr);
+extern frame_info_ptr frame_find_by_id (struct frame_id id);
/* A wrapper for "frame_info *". frame_info objects are invalidated
whenever reinit_frame_cache is called. This class arranges to
}
frame_info_ptr (const frame_info_ptr &other)
- : m_ptr (other.m_ptr)
+ : m_ptr (other.m_ptr), m_cached_id (other.m_cached_id)
{
frame_list.push_back (*this);
}
frame_info_ptr (frame_info_ptr &&other)
- : m_ptr (other.m_ptr)
+ : m_ptr (other.m_ptr), m_cached_id (other.m_cached_id)
{
other.m_ptr = nullptr;
frame_list.push_back (*this);
frame_info_ptr &operator= (const frame_info_ptr &other)
{
m_ptr = other.m_ptr;
+ m_cached_id = other.m_cached_id;
return *this;
}
frame_info_ptr &operator= (std::nullptr_t)
{
m_ptr = nullptr;
+ m_cached_id = null_frame_id;
return *this;
}
frame_info_ptr &operator= (frame_info_ptr &&other)
{
m_ptr = other.m_ptr;
+ m_cached_id = other.m_cached_id;
other.m_ptr = nullptr;
+ other.m_cached_id = null_frame_id;
return *this;
}
m_ptr = nullptr;
}
+ /* Cache the frame_id that the pointer will use to reinflate. */
+ void prepare_reinflate ()
+ {
+ m_cached_id = get_frame_id (*this);
+ }
+
+ /* Use the cached frame_id to reinflate the pointer. */
+ void reinflate ()
+ {
+ gdb_assert (m_cached_id != null_frame_id);
+
+ if (m_ptr == nullptr)
+ m_ptr = frame_find_by_id (m_cached_id).get ();
+ gdb_assert (m_ptr != nullptr);
+ }
+
private:
/* The underlying pointer. */
frame_info *m_ptr = nullptr;
+ /* The frame_id of the underlying pointer. */
+ frame_id m_cached_id = null_frame_id;
/* All frame_info_ptr objects are kept on an intrusive list.
This keeps their construction and destruction costs
struct ui_out;
struct frame_print_options;
-/* Status of a given frame's stack. */
-
-enum frame_id_stack_status
-{
- /* Stack address is invalid. */
- FID_STACK_INVALID = 0,
-
- /* Stack address is valid, and is found in the stack_addr field. */
- FID_STACK_VALID = 1,
-
- /* Sentinel frame. */
- FID_STACK_SENTINEL = 2,
-
- /* Outer frame. Since a frame's stack address is typically defined as the
- value the stack pointer had prior to the activation of the frame, an outer
- frame doesn't have a stack address. The frame ids of frames inlined in the
- outer frame are also of this type. */
- FID_STACK_OUTER = 3,
-
- /* Stack address is unavailable. I.e., there's a valid stack, but
- we don't know where it is (because memory or registers we'd
- compute it from were not collected). */
- FID_STACK_UNAVAILABLE = -1
-};
-
/* The frame object. */
class frame_info_ptr;
-/* The frame object's ID. This provides a per-frame unique identifier
- that can be used to relocate a `struct frame_info' after a target
- resume or a frame cache destruct. It of course assumes that the
- inferior hasn't unwound the stack past that frame. */
-
-struct frame_id
-{
- /* The frame's stack address. This shall be constant through out
- the lifetime of a frame. Note that this requirement applies to
- not just the function body, but also the prologue and (in theory
- at least) the epilogue. Since that value needs to fall either on
- the boundary, or within the frame's address range, the frame's
- outer-most address (the inner-most address of the previous frame)
- is used. Watch out for all the legacy targets that still use the
- function pointer register or stack pointer register. They are
- wrong.
-
- This field is valid only if frame_id.stack_status is
- FID_STACK_VALID. It will be 0 for other
- FID_STACK_... statuses. */
- CORE_ADDR stack_addr;
-
- /* The frame's code address. This shall be constant through out the
- lifetime of the frame. While the PC (a.k.a. resume address)
- changes as the function is executed, this code address cannot.
- Typically, it is set to the address of the entry point of the
- frame's function (as returned by get_frame_func).
-
- For inlined functions (INLINE_DEPTH != 0), this is the address of
- the first executed instruction in the block corresponding to the
- inlined function.
-
- This field is valid only if code_addr_p is true. Otherwise, this
- frame is considered to have a wildcard code address, i.e. one that
- matches every address value in frame comparisons. */
- CORE_ADDR code_addr;
-
- /* The frame's special address. This shall be constant through out the
- lifetime of the frame. This is used for architectures that may have
- frames that do not change the stack but are still distinct and have
- some form of distinct identifier (e.g. the ia64 which uses a 2nd
- stack for registers). This field is treated as unordered - i.e. will
- not be used in frame ordering comparisons.
-
- This field is valid only if special_addr_p is true. Otherwise, this
- frame is considered to have a wildcard special address, i.e. one that
- matches every address value in frame comparisons. */
- CORE_ADDR special_addr;
-
- /* Flags to indicate the above fields have valid contents. */
- ENUM_BITFIELD(frame_id_stack_status) stack_status : 3;
- unsigned int code_addr_p : 1;
- unsigned int special_addr_p : 1;
-
- /* It is non-zero for a frame made up by GDB without stack data
- representation in inferior, such as INLINE_FRAME or TAILCALL_FRAME.
- Caller of inlined function will have it zero, each more inner called frame
- will have it increasingly one, two etc. Similarly for TAILCALL_FRAME. */
- int artificial_depth;
-
- /* Return a string representation of this frame id. */
- std::string to_string () const;
-
- /* Returns true when this frame_id and R identify the same
- frame. */
- bool operator== (const frame_id &r) const;
-
- /* Inverse of ==. */
- bool operator!= (const frame_id &r) const
- {
- return !(*this == r);
- }
-};
-
/* Save and restore the currently selected frame. */
class scoped_restore_selected_frame
enum language m_lang;
};
-/* Methods for constructing and comparing Frame IDs. */
-
-/* For convenience. All fields are zero. This means "there is no frame". */
-extern const struct frame_id null_frame_id;
-
-/* Sentinel frame. */
-extern const struct frame_id sentinel_frame_id;
-
-/* This means "there is no frame ID, but there is a frame". It should be
- replaced by best-effort frame IDs for the outermost frame, somehow.
- The implementation is only special_addr_p set. */
-extern const struct frame_id outer_frame_id;
-
/* Flag to control debugging. */
extern bool frame_debug;
frame. */
extern frame_info_ptr get_prev_frame_always (frame_info_ptr );
-/* Given a frame's ID, relocate the frame. Returns NULL if the frame
- is not found. */
-extern frame_info_ptr frame_find_by_id (struct frame_id id);
-
/* Base attributes of a frame: */
/* The frame's `resume' address. Where the program will resume in
frame = get_prev_frame (get_selected_frame (_("No selected frame.")));
if (frame == 0)
error (_("\"finish\" not meaningful in the outermost frame."));
+ frame.prepare_reinflate ();
clear_proceed_status (0);
print_stack_frame (get_selected_frame (NULL), 1, LOCATION, 0);
}
+ frame.reinflate ();
if (execution_direction == EXEC_REVERSE)
finish_backward (sm);
i++, fi = get_prev_frame (fi))
{
QUIT;
+ fi.prepare_reinflate ();
/* Print the location and the address always, even for level 0.
If args is 0, don't print the arguments. */
print_frame_info (user_frame_print_options,
fi, 1, LOC_AND_ADDRESS, 0 /* args */, 0);
+ fi.reinflate ();
}
}
}
if (current_uiout->is_mi_like_p ())
print_what = LOC_AND_ADDRESS;
+ frame.prepare_reinflate ();
try
{
print_frame_info (user_frame_print_options,
frame, print_level, print_what, 1 /* print_args */,
set_current_sal);
+ frame.reinflate ();
if (set_current_sal)
set_current_sal_from_frame (frame);
}
= (print_names
&& fp_opts.print_frame_arguments != print_frame_arguments_none);
+ /* If one of the arguments has a pretty printer that calls a
+ function of the inferior to print it, the pointer must be
+ reinflatable. */
+ frame.prepare_reinflate ();
+
/* Temporarily change the selected frame to the given FRAME.
This allows routines that rely on the selected frame instead
of being given a frame as parameter to use the correct frame. */
}
first = 0;
+ frame.reinflate ();
}
}
print_source_lines (sal.symtab, sal.line, sal.line + 1, 0);
}
+ frame.reinflate ();
/* If disassemble-next-line is set to on and there is line debug
messages, output assembly codes for next line. */
for (fi = trailing; fi && count--; fi = get_prev_frame (fi))
{
QUIT;
+ fi.prepare_reinflate ();
/* Don't use print_stack_frame; if an error() occurs it probably
means further attempts to backtrace would fail (on the other
}
/* Save the last frame to check for error conditions. */
+ fi.reinflate ();
trailing = fi;
}
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2022 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/>. */
+
+struct mytype
+{
+ char *x;
+};
+
+void
+rec (int i)
+{
+ if (i <= 0)
+ return;
+ rec (i-1);
+}
+
+int
+f ()
+{
+ rec (100);
+ return 2;
+}
+
+void
+g (struct mytype mt, int depth)
+{
+ if (depth <= 0)
+ return; /* TAG: final frame */
+ g (mt, depth - 1); /* TAG: first frame */
+}
+
+int
+main ()
+{
+ struct mytype mt;
+ mt.x = "hello world";
+ g (mt, 10); /* TAG: outside the frame */
+ return 0;
+}
--- /dev/null
+# Copyright (C) 2022 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/>.
+
+# This file is part of the GDB testsuite. It tests a pretty printer that
+# calls an inferior function by hand, triggering a Use-after-Free bug
+# (PR gdb/28856).
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+# gdb needs to be started here for skip_python_tests to work.
+# prepare_for_testing could be used instead, but it could compile the program
+# unnecessarily, so starting GDB like this is preferable.
+gdb_start
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile debug] } {
+ untested "failed to compile"
+ return -1
+}
+
+# This proc restarts GDB, makes the inferior reach the desired spot - marked
+# by a comment in the .c file - and turns on the pretty printer for testing.
+# Starting with a new GDB is important because the test may crash GDB. The
+# return values are here to avoid us trying to test the pretty printer if
+# there was a problem getting to main.
+proc start_test { breakpoint_comment } {
+ global srcdir subdir testfile binfile
+
+ # Start with a fresh gdb.
+ # This is important because the test can crash GDB.
+
+ clean_restart ${binfile}
+
+ if { ![runto_main] } then {
+ untested "couldn't run to breakpoint"
+ return -1
+ }
+
+ # Let GDB get to the return line.
+ gdb_breakpoint [gdb_get_line_number ${breakpoint_comment} ${testfile}.c ]
+ gdb_continue_to_breakpoint ${breakpoint_comment} ".*"
+
+ gdb_test_no_output "set print pretty on" "starting to pretty print"
+
+ set remote_python_file [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+ gdb_test_no_output "source ${remote_python_file}" "load python file"
+
+ return 0
+}
+
+# Start by testing the "run" command, it can't leverage start_test
+with_test_prefix "run to frame" {
+ if { ![runto_main] } then {
+ untested "couldn't run to main"
+ }
+
+ gdb_test_no_output "set print pretty on" "starting to pretty print"
+
+ set remote_python_file [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+ gdb_test_no_output "source ${remote_python_file}" "load python file"
+
+ gdb_breakpoint [gdb_get_line_number "TAG: final frame" ${testfile}.c]
+ gdb_continue_to_breakpoint "TAG: final frame" ".*"
+}
+
+# Testing the backtrace command.
+with_test_prefix "frame print" {
+ if { [start_test "TAG: final frame"] == 0 } {
+ gdb_test "backtrace -frame-arguments all" [multi_line \
+ "#0 .*g \\(mt=mytype is .*\\, depth=0\\).*"\
+ "#1 .*g \\(mt=mytype is .*\\, depth=1\\).*"\
+ "#2 .*g \\(mt=mytype is .*\\, depth=2\\).*"\
+ "#3 .*g \\(mt=mytype is .*\\, depth=3\\).*"\
+ "#4 .*g \\(mt=mytype is .*\\, depth=4\\).*"\
+ "#5 .*g \\(mt=mytype is .*\\, depth=5\\).*"\
+ "#6 .*g \\(mt=mytype is .*\\, depth=6\\).*"\
+ "#7 .*g \\(mt=mytype is .*\\, depth=7\\).*"\
+ "#8 .*g \\(mt=mytype is .*\\, depth=8\\).*"\
+ "#9 .*g \\(mt=mytype is .*\\, depth=9\\).*"\
+ "#10 .*g \\(mt=mytype is .*\\, depth=10\\).*"\
+ "#11 .*main \\(\\).*"] \
+ "backtrace test"
+ }
+}
+# Testing the down command.
+with_test_prefix "frame movement down" {
+ if { [start_test "TAG: first frame"] == 0 } {
+ gdb_test "up" [multi_line "#1 .*in main \\(\\) at .*" ".*outside the frame.*"]
+ gdb_test "down" [multi_line "#0\\s+g \\(mt=mytype is .*\\, depth=10\\).*" ".*first frame.*"]
+ }
+}
+
+# Testing the up command.
+with_test_prefix "frame movement up" {
+ if { [start_test "TAG: final frame"] == 0 } {
+ gdb_test "up" [multi_line "#1 .*in g \\(mt=mytype is .*\\, depth=1\\).*" ".*first frame.*"]
+ }
+}
+
+# Testing the finish command.
+with_test_prefix "frame exit through finish" {
+ if { [start_test "TAG: final frame"] == 0 } {
+ gdb_test "finish" [multi_line ".*.*g \\(mt=mytype is .*\\, depth=0\\).*" ".*g \\(mt=mytype is .*\\, depth=1\\).*" ".*"]
+ }
+}
+
+# Testing the step command.
+with_test_prefix "frame enter through step" {
+ if { [start_test "TAG: outside the frame"] == 0 } {
+ gdb_test "step" [multi_line "g \\(mt=mytype is .*\\, depth=10\\).*" "41.*if \\(depth \\<= 0\\)"]
+ }
+}
+
+# Testing the continue command.
+with_test_prefix "frame enter through continue" {
+ if { [start_test "TAG: outside the frame"] == 0 } {
+ gdb_breakpoint [gdb_get_line_number "TAG: first frame" ${testfile}.c ]
+ gdb_continue_to_breakpoint "TAG: first frame" ".*TAG: first frame.*"
+ }
+}
--- /dev/null
+# Copyright (C) 2022 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/>.
+
+
+class MytypePrinter:
+ """pretty print my type"""
+
+ def __init__(self, val):
+ self.val = val
+
+ def to_string(self):
+ calls = gdb.parse_and_eval('f()')
+ return "mytype is %s" % self.val['x']
+
+def ec_lookup_function(val):
+ typ = val.type
+ if typ.code == gdb.TYPE_CODE_REF:
+ typ = typ.target()
+ if str(typ) == 'struct mytype':
+ return MytypePrinter(val)
+ return None
+
+def disable_lookup_function():
+ ec_lookup_function.enabled = False
+
+def enable_lookup_function():
+ ec_lookup_function.enabled = True
+
+gdb.pretty_printers.append(ec_lookup_function)