|  | /* Code dealing with dummy stack frames, for GDB, the GNU debugger. | 
|  |  | 
|  | Copyright (C) 1986-2021 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/>.  */ | 
|  |  | 
|  |  | 
|  | #include "defs.h" | 
|  | #include "dummy-frame.h" | 
|  | #include "regcache.h" | 
|  | #include "frame.h" | 
|  | #include "inferior.h" | 
|  | #include "frame-unwind.h" | 
|  | #include "command.h" | 
|  | #include "gdbcmd.h" | 
|  | #include "observable.h" | 
|  | #include "gdbthread.h" | 
|  | #include "infcall.h" | 
|  | #include "gdbarch.h" | 
|  |  | 
|  | struct dummy_frame_id | 
|  | { | 
|  | /* This frame's ID.  Must match the value returned by | 
|  | gdbarch_dummy_id.  */ | 
|  | struct frame_id id; | 
|  |  | 
|  | /* The thread this dummy_frame relates to.  */ | 
|  | thread_info *thread; | 
|  | }; | 
|  |  | 
|  | /* Return whether dummy_frame_id *ID1 and *ID2 are equal.  */ | 
|  |  | 
|  | static int | 
|  | dummy_frame_id_eq (struct dummy_frame_id *id1, | 
|  | struct dummy_frame_id *id2) | 
|  | { | 
|  | return frame_id_eq (id1->id, id2->id) && id1->thread == id2->thread; | 
|  | } | 
|  |  | 
|  | /* List of dummy_frame destructors.  */ | 
|  |  | 
|  | struct dummy_frame_dtor_list | 
|  | { | 
|  | /* Next element in the list or NULL if this is the last element.  */ | 
|  | struct dummy_frame_dtor_list *next; | 
|  |  | 
|  | /* If non-NULL, a destructor that is run when this dummy frame is freed.  */ | 
|  | dummy_frame_dtor_ftype *dtor; | 
|  |  | 
|  | /* Arbitrary data that is passed to DTOR.  */ | 
|  | void *dtor_data; | 
|  | }; | 
|  |  | 
|  | /* Dummy frame.  This saves the processor state just prior to setting | 
|  | up the inferior function call.  Older targets save the registers | 
|  | on the target stack (but that really slows down function calls).  */ | 
|  |  | 
|  | struct dummy_frame | 
|  | { | 
|  | struct dummy_frame *next; | 
|  |  | 
|  | /* An id represents a dummy frame.  */ | 
|  | struct dummy_frame_id id; | 
|  |  | 
|  | /* The caller's state prior to the call.  */ | 
|  | struct infcall_suspend_state *caller_state; | 
|  |  | 
|  | /* First element of destructors list or NULL if there are no | 
|  | destructors registered for this dummy_frame.  */ | 
|  | struct dummy_frame_dtor_list *dtor_list; | 
|  | }; | 
|  |  | 
|  | static struct dummy_frame *dummy_frame_stack = NULL; | 
|  |  | 
|  | /* Push the caller's state, along with the dummy frame info, onto the | 
|  | dummy-frame stack.  */ | 
|  |  | 
|  | void | 
|  | dummy_frame_push (struct infcall_suspend_state *caller_state, | 
|  | const frame_id *dummy_id, thread_info *thread) | 
|  | { | 
|  | struct dummy_frame *dummy_frame; | 
|  |  | 
|  | dummy_frame = XCNEW (struct dummy_frame); | 
|  | dummy_frame->caller_state = caller_state; | 
|  | dummy_frame->id.id = (*dummy_id); | 
|  | dummy_frame->id.thread = thread; | 
|  | dummy_frame->next = dummy_frame_stack; | 
|  | dummy_frame_stack = dummy_frame; | 
|  | } | 
|  |  | 
|  | /* Remove *DUMMY_PTR from the dummy frame stack.  */ | 
|  |  | 
|  | static void | 
|  | remove_dummy_frame (struct dummy_frame **dummy_ptr) | 
|  | { | 
|  | struct dummy_frame *dummy = *dummy_ptr; | 
|  |  | 
|  | while (dummy->dtor_list != NULL) | 
|  | { | 
|  | struct dummy_frame_dtor_list *list = dummy->dtor_list; | 
|  |  | 
|  | dummy->dtor_list = list->next; | 
|  | list->dtor (list->dtor_data, 0); | 
|  | xfree (list); | 
|  | } | 
|  |  | 
|  | *dummy_ptr = dummy->next; | 
|  | discard_infcall_suspend_state (dummy->caller_state); | 
|  | xfree (dummy); | 
|  | } | 
|  |  | 
|  | /* Delete any breakpoint B which is a momentary breakpoint for return from | 
|  | inferior call matching DUMMY_VOIDP.  */ | 
|  |  | 
|  | static bool | 
|  | pop_dummy_frame_bpt (struct breakpoint *b, struct dummy_frame *dummy) | 
|  | { | 
|  | if (b->thread == dummy->id.thread->global_num | 
|  | && b->disposition == disp_del && frame_id_eq (b->frame_id, dummy->id.id)) | 
|  | { | 
|  | while (b->related_breakpoint != b) | 
|  | delete_breakpoint (b->related_breakpoint); | 
|  |  | 
|  | delete_breakpoint (b); | 
|  |  | 
|  | /* Stop the traversal.  */ | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* Continue the traversal.  */ | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* Pop *DUMMY_PTR, restoring program state to that before the | 
|  | frame was created.  */ | 
|  |  | 
|  | static void | 
|  | pop_dummy_frame (struct dummy_frame **dummy_ptr) | 
|  | { | 
|  | struct dummy_frame *dummy = *dummy_ptr; | 
|  |  | 
|  | gdb_assert (dummy->id.thread == inferior_thread ()); | 
|  |  | 
|  | while (dummy->dtor_list != NULL) | 
|  | { | 
|  | struct dummy_frame_dtor_list *list = dummy->dtor_list; | 
|  |  | 
|  | dummy->dtor_list = list->next; | 
|  | list->dtor (list->dtor_data, 1); | 
|  | xfree (list); | 
|  | } | 
|  |  | 
|  | restore_infcall_suspend_state (dummy->caller_state); | 
|  |  | 
|  | for (breakpoint *bp : all_breakpoints_safe ()) | 
|  | if (pop_dummy_frame_bpt (bp, dummy)) | 
|  | break; | 
|  |  | 
|  | /* restore_infcall_control_state frees inf_state, | 
|  | all that remains is to pop *dummy_ptr.  */ | 
|  | *dummy_ptr = dummy->next; | 
|  | xfree (dummy); | 
|  |  | 
|  | /* We've made right mess of GDB's local state, just discard | 
|  | everything.  */ | 
|  | reinit_frame_cache (); | 
|  | } | 
|  |  | 
|  | /* Look up DUMMY_ID. | 
|  | Return NULL if not found.  */ | 
|  |  | 
|  | static struct dummy_frame ** | 
|  | lookup_dummy_frame (struct dummy_frame_id *dummy_id) | 
|  | { | 
|  | struct dummy_frame **dp; | 
|  |  | 
|  | for (dp = &dummy_frame_stack; *dp != NULL; dp = &(*dp)->next) | 
|  | { | 
|  | if (dummy_frame_id_eq (&(*dp)->id, dummy_id)) | 
|  | return dp; | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Find the dummy frame by DUMMY_ID and THREAD, and pop it, restoring | 
|  | program state to that before the frame was created. | 
|  | On return reinit_frame_cache has been called. | 
|  | If the frame isn't found, flag an internal error.  */ | 
|  |  | 
|  | void | 
|  | dummy_frame_pop (frame_id dummy_id, thread_info *thread) | 
|  | { | 
|  | struct dummy_frame **dp; | 
|  | struct dummy_frame_id id = { dummy_id, thread }; | 
|  |  | 
|  | dp = lookup_dummy_frame (&id); | 
|  | gdb_assert (dp != NULL); | 
|  |  | 
|  | pop_dummy_frame (dp); | 
|  | } | 
|  |  | 
|  | /* Find the dummy frame by DUMMY_ID and PTID and drop it.  Do nothing | 
|  | if it is not found.  Do not restore its state into inferior, just | 
|  | free its memory.  */ | 
|  |  | 
|  | void | 
|  | dummy_frame_discard (struct frame_id dummy_id, thread_info *thread) | 
|  | { | 
|  | struct dummy_frame **dp; | 
|  | struct dummy_frame_id id = { dummy_id, thread }; | 
|  |  | 
|  | dp = lookup_dummy_frame (&id); | 
|  | if (dp) | 
|  | remove_dummy_frame (dp); | 
|  | } | 
|  |  | 
|  | /* See dummy-frame.h.  */ | 
|  |  | 
|  | void | 
|  | register_dummy_frame_dtor (frame_id dummy_id, thread_info *thread, | 
|  | dummy_frame_dtor_ftype *dtor, void *dtor_data) | 
|  | { | 
|  | struct dummy_frame_id id = { dummy_id, thread }; | 
|  | struct dummy_frame **dp, *d; | 
|  | struct dummy_frame_dtor_list *list; | 
|  |  | 
|  | dp = lookup_dummy_frame (&id); | 
|  | gdb_assert (dp != NULL); | 
|  | d = *dp; | 
|  | list = XNEW (struct dummy_frame_dtor_list); | 
|  | list->next = d->dtor_list; | 
|  | d->dtor_list = list; | 
|  | list->dtor = dtor; | 
|  | list->dtor_data = dtor_data; | 
|  | } | 
|  |  | 
|  | /* See dummy-frame.h.  */ | 
|  |  | 
|  | int | 
|  | find_dummy_frame_dtor (dummy_frame_dtor_ftype *dtor, void *dtor_data) | 
|  | { | 
|  | struct dummy_frame *d; | 
|  |  | 
|  | for (d = dummy_frame_stack; d != NULL; d = d->next) | 
|  | { | 
|  | struct dummy_frame_dtor_list *list; | 
|  |  | 
|  | for (list = d->dtor_list; list != NULL; list = list->next) | 
|  | if (list->dtor == dtor && list->dtor_data == dtor_data) | 
|  | return 1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* There may be stale dummy frames, perhaps left over from when an uncaught | 
|  | longjmp took us out of a function that was called by the debugger.  Clean | 
|  | them up at least once whenever we start a new inferior.  */ | 
|  |  | 
|  | static void | 
|  | cleanup_dummy_frames (inferior *inf) | 
|  | { | 
|  | while (dummy_frame_stack != NULL) | 
|  | remove_dummy_frame (&dummy_frame_stack); | 
|  | } | 
|  |  | 
|  | /* Return the dummy frame cache, it contains both the ID, and a | 
|  | pointer to the regcache.  */ | 
|  | struct dummy_frame_cache | 
|  | { | 
|  | struct frame_id this_id; | 
|  | readonly_detached_regcache *prev_regcache; | 
|  | }; | 
|  |  | 
|  | static int | 
|  | dummy_frame_sniffer (const struct frame_unwind *self, | 
|  | struct frame_info *this_frame, | 
|  | void **this_prologue_cache) | 
|  | { | 
|  | /* When unwinding a normal frame, the stack structure is determined | 
|  | by analyzing the frame's function's code (be it using brute force | 
|  | prologue analysis, or the dwarf2 CFI).  In the case of a dummy | 
|  | frame, that simply isn't possible.  The PC is either the program | 
|  | entry point, or some random address on the stack.  Trying to use | 
|  | that PC to apply standard frame ID unwind techniques is just | 
|  | asking for trouble.  */ | 
|  |  | 
|  | /* Don't bother unless there is at least one dummy frame.  */ | 
|  | if (dummy_frame_stack != NULL) | 
|  | { | 
|  | struct dummy_frame *dummyframe; | 
|  | /* Use an architecture specific method to extract this frame's | 
|  | dummy ID, assuming it is a dummy frame.  */ | 
|  | struct frame_id this_id | 
|  | = gdbarch_dummy_id (get_frame_arch (this_frame), this_frame); | 
|  | struct dummy_frame_id dummy_id = { this_id, inferior_thread () }; | 
|  |  | 
|  | /* Use that ID to find the corresponding cache entry.  */ | 
|  | for (dummyframe = dummy_frame_stack; | 
|  | dummyframe != NULL; | 
|  | dummyframe = dummyframe->next) | 
|  | { | 
|  | if (dummy_frame_id_eq (&dummyframe->id, &dummy_id)) | 
|  | { | 
|  | struct dummy_frame_cache *cache; | 
|  |  | 
|  | cache = FRAME_OBSTACK_ZALLOC (struct dummy_frame_cache); | 
|  | cache->prev_regcache = get_infcall_suspend_state_regcache | 
|  | (dummyframe->caller_state); | 
|  | cache->this_id = this_id; | 
|  | (*this_prologue_cache) = cache; | 
|  | return 1; | 
|  | } | 
|  | } | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Given a call-dummy dummy-frame, return the registers.  Here the | 
|  | register value is taken from the local copy of the register buffer.  */ | 
|  |  | 
|  | static struct value * | 
|  | dummy_frame_prev_register (struct frame_info *this_frame, | 
|  | void **this_prologue_cache, | 
|  | int regnum) | 
|  | { | 
|  | struct dummy_frame_cache *cache | 
|  | = (struct dummy_frame_cache *) *this_prologue_cache; | 
|  | struct gdbarch *gdbarch = get_frame_arch (this_frame); | 
|  | struct value *reg_val; | 
|  |  | 
|  | /* The dummy-frame sniffer always fills in the cache.  */ | 
|  | gdb_assert (cache != NULL); | 
|  |  | 
|  | /* Describe the register's location.  Generic dummy frames always | 
|  | have the register value in an ``expression''.  */ | 
|  | reg_val = value_zero (register_type (gdbarch, regnum), not_lval); | 
|  |  | 
|  | /* Use the regcache_cooked_read() method so that it, on the fly, | 
|  | constructs either a raw or pseudo register from the raw | 
|  | register cache.  */ | 
|  | cache->prev_regcache->cooked_read | 
|  | (regnum, value_contents_writeable (reg_val).data ()); | 
|  | return reg_val; | 
|  | } | 
|  |  | 
|  | /* Assuming that THIS_FRAME is a dummy, return its ID.  That ID is | 
|  | determined by examining the NEXT frame's unwound registers using | 
|  | the method dummy_id().  As a side effect, THIS dummy frame's | 
|  | dummy cache is located and saved in THIS_PROLOGUE_CACHE.  */ | 
|  |  | 
|  | static void | 
|  | dummy_frame_this_id (struct frame_info *this_frame, | 
|  | void **this_prologue_cache, | 
|  | struct frame_id *this_id) | 
|  | { | 
|  | /* The dummy-frame sniffer always fills in the cache.  */ | 
|  | struct dummy_frame_cache *cache | 
|  | = (struct dummy_frame_cache *) *this_prologue_cache; | 
|  |  | 
|  | gdb_assert (cache != NULL); | 
|  | (*this_id) = cache->this_id; | 
|  | } | 
|  |  | 
|  | const struct frame_unwind dummy_frame_unwind = | 
|  | { | 
|  | "dummy", | 
|  | DUMMY_FRAME, | 
|  | default_frame_unwind_stop_reason, | 
|  | dummy_frame_this_id, | 
|  | dummy_frame_prev_register, | 
|  | NULL, | 
|  | dummy_frame_sniffer, | 
|  | }; | 
|  |  | 
|  | /* See dummy-frame.h.  */ | 
|  |  | 
|  | struct frame_id | 
|  | default_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame) | 
|  | { | 
|  | CORE_ADDR sp, pc; | 
|  |  | 
|  | sp = get_frame_sp (this_frame); | 
|  | pc = get_frame_pc (this_frame); | 
|  | return frame_id_build (sp, pc); | 
|  | } | 
|  |  | 
|  | static void | 
|  | fprint_dummy_frames (struct ui_file *file) | 
|  | { | 
|  | struct dummy_frame *s; | 
|  |  | 
|  | for (s = dummy_frame_stack; s != NULL; s = s->next) | 
|  | { | 
|  | gdb_print_host_address (s, file); | 
|  | fprintf_unfiltered (file, ":"); | 
|  | fprintf_unfiltered (file, " id=%s", s->id.id.to_string ().c_str ()); | 
|  | fprintf_unfiltered (file, ", ptid=%s", | 
|  | target_pid_to_str (s->id.thread->ptid).c_str ()); | 
|  | fprintf_unfiltered (file, "\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | maintenance_print_dummy_frames (const char *args, int from_tty) | 
|  | { | 
|  | if (args == NULL) | 
|  | fprint_dummy_frames (gdb_stdout); | 
|  | else | 
|  | { | 
|  | stdio_file file; | 
|  |  | 
|  | if (!file.open (args, "w")) | 
|  | perror_with_name (_("maintenance print dummy-frames")); | 
|  | fprint_dummy_frames (&file); | 
|  | } | 
|  | } | 
|  |  | 
|  | void _initialize_dummy_frame (); | 
|  | void | 
|  | _initialize_dummy_frame () | 
|  | { | 
|  | add_cmd ("dummy-frames", class_maintenance, maintenance_print_dummy_frames, | 
|  | _("Print the contents of the internal dummy-frame stack."), | 
|  | &maintenanceprintlist); | 
|  |  | 
|  | gdb::observers::inferior_created.attach (cleanup_dummy_frames, "dummy-frame"); | 
|  | } |