|  | /* Virtual tail call frames unwinder for GDB. | 
|  |  | 
|  | Copyright (C) 2010-2024 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 "exceptions.h" | 
|  | #include "frame.h" | 
|  | #include "dwarf2/frame-tailcall.h" | 
|  | #include "dwarf2/loc.h" | 
|  | #include "frame-unwind.h" | 
|  | #include "block.h" | 
|  | #include "hashtab.h" | 
|  | #include "gdbtypes.h" | 
|  | #include "regcache.h" | 
|  | #include "value.h" | 
|  | #include "dwarf2/frame.h" | 
|  | #include "gdbarch.h" | 
|  |  | 
|  | /* Contains struct tailcall_cache indexed by next_bottom_frame.  */ | 
|  | static htab_t cache_htab; | 
|  |  | 
|  | /* Associate structure of the unwinder to call_site_chain.  Lifetime of this | 
|  | structure is maintained by REFC decremented by dealloc_cache, all of them | 
|  | get deleted during reinit_frame_cache.  */ | 
|  | struct tailcall_cache | 
|  | { | 
|  | /* It must be the first one of this struct.  It is the furthest callee.  */ | 
|  | frame_info *next_bottom_frame; | 
|  |  | 
|  | /* Reference count.  The whole chain of virtual tail call frames shares one | 
|  | tailcall_cache.  */ | 
|  | int refc; | 
|  |  | 
|  | /* Associated found virtual tail call frames chain, it is never NULL.  */ | 
|  | struct call_site_chain *chain; | 
|  |  | 
|  | /* Cached pretended_chain_levels result.  */ | 
|  | int chain_levels; | 
|  |  | 
|  | /* Unwound PC from the top (caller) frame, as it is not contained | 
|  | in CHAIN.  */ | 
|  | CORE_ADDR prev_pc; | 
|  |  | 
|  | /* Compensate SP in caller frames appropriately.  prev_sp and | 
|  | entry_cfa_sp_offset are valid only if PREV_SP_P.  PREV_SP is SP at the top | 
|  | (caller) frame.  ENTRY_CFA_SP_OFFSET is shift of SP in tail call frames | 
|  | against next_bottom_frame SP.  */ | 
|  | unsigned prev_sp_p : 1; | 
|  | CORE_ADDR prev_sp; | 
|  | LONGEST entry_cfa_sp_offset; | 
|  | }; | 
|  |  | 
|  | /* hash_f for htab_create_alloc of cache_htab.  */ | 
|  |  | 
|  | static hashval_t | 
|  | cache_hash (const void *arg) | 
|  | { | 
|  | const struct tailcall_cache *cache = (const struct tailcall_cache *) arg; | 
|  |  | 
|  | return htab_hash_pointer (cache->next_bottom_frame); | 
|  | } | 
|  |  | 
|  | /* eq_f for htab_create_alloc of cache_htab.  */ | 
|  |  | 
|  | static int | 
|  | cache_eq (const void *arg1, const void *arg2) | 
|  | { | 
|  | const struct tailcall_cache *cache1 = (const struct tailcall_cache *) arg1; | 
|  | const struct tailcall_cache *cache2 = (const struct tailcall_cache *) arg2; | 
|  |  | 
|  | return cache1->next_bottom_frame == cache2->next_bottom_frame; | 
|  | } | 
|  |  | 
|  | /* Create new tailcall_cache for NEXT_BOTTOM_FRAME, NEXT_BOTTOM_FRAME must not | 
|  | yet have been indexed by cache_htab.  Caller holds one reference of the new | 
|  | tailcall_cache.  */ | 
|  |  | 
|  | static struct tailcall_cache * | 
|  | cache_new_ref1 (const frame_info_ptr &next_bottom_frame) | 
|  | { | 
|  | struct tailcall_cache *cache = XCNEW (struct tailcall_cache); | 
|  | void **slot; | 
|  |  | 
|  | cache->next_bottom_frame = next_bottom_frame.get (); | 
|  | cache->refc = 1; | 
|  |  | 
|  | slot = htab_find_slot (cache_htab, cache, INSERT); | 
|  | gdb_assert (*slot == NULL); | 
|  | *slot = cache; | 
|  |  | 
|  | return cache; | 
|  | } | 
|  |  | 
|  | /* Create new reference to CACHE.  */ | 
|  |  | 
|  | static void | 
|  | cache_ref (struct tailcall_cache *cache) | 
|  | { | 
|  | gdb_assert (cache->refc > 0); | 
|  |  | 
|  | cache->refc++; | 
|  | } | 
|  |  | 
|  | /* Drop reference to CACHE, possibly fully freeing it and unregistering it from | 
|  | cache_htab.  */ | 
|  |  | 
|  | static void | 
|  | cache_unref (struct tailcall_cache *cache) | 
|  | { | 
|  | gdb_assert (cache->refc > 0); | 
|  |  | 
|  | if (!--cache->refc) | 
|  | { | 
|  | gdb_assert (htab_find_slot (cache_htab, cache, NO_INSERT) != NULL); | 
|  | htab_remove_elt (cache_htab, cache); | 
|  |  | 
|  | xfree (cache->chain); | 
|  | xfree (cache); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Return 1 if FI is a non-bottom (not the callee) tail call frame.  Otherwise | 
|  | return 0.  */ | 
|  |  | 
|  | static int | 
|  | frame_is_tailcall (const frame_info_ptr &fi) | 
|  | { | 
|  | return frame_unwinder_is (fi, &dwarf2_tailcall_frame_unwind); | 
|  | } | 
|  |  | 
|  | /* Try to find tailcall_cache in cache_htab if FI is a part of its virtual tail | 
|  | call chain.  Otherwise return NULL.  No new reference is created.  */ | 
|  |  | 
|  | static struct tailcall_cache * | 
|  | cache_find (const frame_info_ptr &initial_fi) | 
|  | { | 
|  | struct tailcall_cache *cache; | 
|  | struct tailcall_cache search; | 
|  | void **slot; | 
|  |  | 
|  | frame_info_ptr fi = initial_fi; | 
|  | while (frame_is_tailcall (fi)) | 
|  | { | 
|  | fi = get_next_frame (fi); | 
|  | gdb_assert (fi != NULL); | 
|  | } | 
|  |  | 
|  | search.next_bottom_frame = fi.get(); | 
|  | search.refc = 1; | 
|  | slot = htab_find_slot (cache_htab, &search, NO_INSERT); | 
|  | if (slot == NULL) | 
|  | return NULL; | 
|  |  | 
|  | cache = (struct tailcall_cache *) *slot; | 
|  | gdb_assert (cache != NULL); | 
|  | return cache; | 
|  | } | 
|  |  | 
|  | /* Number of virtual frames between THIS_FRAME and CACHE->NEXT_BOTTOM_FRAME. | 
|  | If THIS_FRAME is CACHE-> NEXT_BOTTOM_FRAME return -1.  */ | 
|  |  | 
|  | static int | 
|  | existing_next_levels (const frame_info_ptr &this_frame, | 
|  | struct tailcall_cache *cache) | 
|  | { | 
|  | int retval = (frame_relative_level (this_frame) | 
|  | - frame_relative_level (frame_info_ptr (cache->next_bottom_frame)) - 1); | 
|  |  | 
|  | gdb_assert (retval >= -1); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* The number of virtual tail call frames in CHAIN.  With no virtual tail call | 
|  | frames the function would return 0 (but CHAIN does not exist in such | 
|  | case).  */ | 
|  |  | 
|  | static int | 
|  | pretended_chain_levels (struct call_site_chain *chain) | 
|  | { | 
|  | int chain_levels; | 
|  |  | 
|  | gdb_assert (chain != NULL); | 
|  |  | 
|  | if (chain->callers == chain->length && chain->callees == chain->length) | 
|  | return chain->length; | 
|  |  | 
|  | chain_levels = chain->callers + chain->callees; | 
|  | gdb_assert (chain_levels <= chain->length); | 
|  |  | 
|  | return chain_levels; | 
|  | } | 
|  |  | 
|  | /* Implementation of frame_this_id_ftype.  THIS_CACHE must be already | 
|  | initialized with tailcall_cache, THIS_FRAME must be a part of THIS_CACHE. | 
|  |  | 
|  | Specific virtual tail call frames are tracked by INLINE_DEPTH.  */ | 
|  |  | 
|  | static void | 
|  | tailcall_frame_this_id (const frame_info_ptr &this_frame, void **this_cache, | 
|  | struct frame_id *this_id) | 
|  | { | 
|  | struct tailcall_cache *cache = (struct tailcall_cache *) *this_cache; | 
|  | frame_info_ptr next_frame; | 
|  |  | 
|  | /* Tail call does not make sense for a sentinel frame.  */ | 
|  | next_frame = get_next_frame (this_frame); | 
|  | gdb_assert (next_frame != NULL); | 
|  |  | 
|  | *this_id = get_frame_id (next_frame); | 
|  | (*this_id).code_addr = get_frame_pc (this_frame); | 
|  | (*this_id).code_addr_p = true; | 
|  | (*this_id).artificial_depth = (cache->chain_levels | 
|  | - existing_next_levels (this_frame, cache)); | 
|  | gdb_assert ((*this_id).artificial_depth > 0); | 
|  | } | 
|  |  | 
|  | /* Find PC to be unwound from THIS_FRAME.  THIS_FRAME must be a part of | 
|  | CACHE.  */ | 
|  |  | 
|  | static CORE_ADDR | 
|  | pretend_pc (const frame_info_ptr &this_frame, struct tailcall_cache *cache) | 
|  | { | 
|  | int next_levels = existing_next_levels (this_frame, cache); | 
|  | struct call_site_chain *chain = cache->chain; | 
|  |  | 
|  | gdb_assert (chain != NULL); | 
|  |  | 
|  | next_levels++; | 
|  | gdb_assert (next_levels >= 0); | 
|  |  | 
|  | if (next_levels < chain->callees) | 
|  | return chain->call_site[chain->length - next_levels - 1]->pc (); | 
|  | next_levels -= chain->callees; | 
|  |  | 
|  | /* Otherwise CHAIN->CALLEES are already covered by CHAIN->CALLERS.  */ | 
|  | if (chain->callees != chain->length) | 
|  | { | 
|  | if (next_levels < chain->callers) | 
|  | return chain->call_site[chain->callers - next_levels - 1]->pc (); | 
|  | next_levels -= chain->callers; | 
|  | } | 
|  |  | 
|  | gdb_assert (next_levels == 0); | 
|  | return cache->prev_pc; | 
|  | } | 
|  |  | 
|  | /* Implementation of frame_prev_register_ftype.  If no specific register | 
|  | override is supplied NULL is returned (this is incompatible with | 
|  | frame_prev_register_ftype semantics).  next_bottom_frame and tail call | 
|  | frames unwind the NULL case differently.  */ | 
|  |  | 
|  | struct value * | 
|  | dwarf2_tailcall_prev_register_first (const frame_info_ptr &this_frame, | 
|  | void **tailcall_cachep, int regnum) | 
|  | { | 
|  | struct gdbarch *this_gdbarch = get_frame_arch (this_frame); | 
|  | struct tailcall_cache *cache = (struct tailcall_cache *) *tailcall_cachep; | 
|  | CORE_ADDR addr; | 
|  |  | 
|  | if (regnum == gdbarch_pc_regnum (this_gdbarch)) | 
|  | addr = pretend_pc (this_frame, cache); | 
|  | else if (cache->prev_sp_p && regnum == gdbarch_sp_regnum (this_gdbarch)) | 
|  | { | 
|  | int next_levels = existing_next_levels (this_frame, cache); | 
|  |  | 
|  | if (next_levels == cache->chain_levels - 1) | 
|  | addr = cache->prev_sp; | 
|  | else | 
|  | addr = dwarf2_frame_cfa (this_frame) - cache->entry_cfa_sp_offset; | 
|  | } | 
|  | else | 
|  | return NULL; | 
|  |  | 
|  | return frame_unwind_got_address (this_frame, regnum, addr); | 
|  | } | 
|  |  | 
|  | /* Implementation of frame_prev_register_ftype for tail call frames.  Register | 
|  | set of virtual tail call frames is assumed to be the one of the top (caller) | 
|  | frame - assume unchanged register value for NULL from | 
|  | dwarf2_tailcall_prev_register_first.  */ | 
|  |  | 
|  | static struct value * | 
|  | tailcall_frame_prev_register (const frame_info_ptr &this_frame, | 
|  | void **this_cache, int regnum) | 
|  | { | 
|  | struct tailcall_cache *cache = (struct tailcall_cache *) *this_cache; | 
|  | struct value *val; | 
|  |  | 
|  | gdb_assert (this_frame != cache->next_bottom_frame); | 
|  |  | 
|  | val = dwarf2_tailcall_prev_register_first (this_frame, this_cache, regnum); | 
|  | if (val) | 
|  | return val; | 
|  |  | 
|  | return frame_unwind_got_register (this_frame, regnum, regnum); | 
|  | } | 
|  |  | 
|  | /* Implementation of frame_sniffer_ftype.  It will never find a new chain, use | 
|  | dwarf2_tailcall_sniffer_first for the bottom (callee) frame.  It will find | 
|  | all the predecessing virtual tail call frames, it will return false when | 
|  | there exist no more tail call frames in this chain.  */ | 
|  |  | 
|  | static int | 
|  | tailcall_frame_sniffer (const struct frame_unwind *self, | 
|  | const frame_info_ptr &this_frame, void **this_cache) | 
|  | { | 
|  | frame_info_ptr next_frame; | 
|  | int next_levels; | 
|  | struct tailcall_cache *cache; | 
|  |  | 
|  | if (!dwarf2_frame_unwinders_enabled_p) | 
|  | return 0; | 
|  |  | 
|  | /* Inner tail call element does not make sense for a sentinel frame.  */ | 
|  | next_frame = get_next_frame (this_frame); | 
|  | if (next_frame == NULL) | 
|  | return 0; | 
|  |  | 
|  | cache = cache_find (next_frame); | 
|  | if (cache == NULL) | 
|  | return 0; | 
|  |  | 
|  | cache_ref (cache); | 
|  |  | 
|  | next_levels = existing_next_levels (this_frame, cache); | 
|  |  | 
|  | /* NEXT_LEVELS is -1 only in dwarf2_tailcall_sniffer_first.  */ | 
|  | gdb_assert (next_levels >= 0); | 
|  | gdb_assert (next_levels <= cache->chain_levels); | 
|  |  | 
|  | if (next_levels == cache->chain_levels) | 
|  | { | 
|  | cache_unref (cache); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | *this_cache = cache; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* The initial "sniffer" whether THIS_FRAME is a bottom (callee) frame of a new | 
|  | chain to create.  Keep TAILCALL_CACHEP NULL if it did not find any chain, | 
|  | initialize it otherwise.  No tail call chain is created if there are no | 
|  | unambiguous virtual tail call frames to report. | 
|  |  | 
|  | ENTRY_CFA_SP_OFFSETP is NULL if no special SP handling is possible, | 
|  | otherwise *ENTRY_CFA_SP_OFFSETP is the number of bytes to subtract from tail | 
|  | call frames frame base to get the SP value there - to simulate return | 
|  | address pushed on the stack.  */ | 
|  |  | 
|  | void | 
|  | dwarf2_tailcall_sniffer_first (const frame_info_ptr &this_frame, | 
|  | void **tailcall_cachep, | 
|  | const LONGEST *entry_cfa_sp_offsetp) | 
|  | { | 
|  | CORE_ADDR prev_pc = 0, prev_sp = 0;	/* GCC warning.  */ | 
|  | int prev_sp_p = 0; | 
|  | CORE_ADDR this_pc; | 
|  | struct gdbarch *prev_gdbarch; | 
|  | gdb::unique_xmalloc_ptr<call_site_chain> chain; | 
|  | struct tailcall_cache *cache; | 
|  |  | 
|  | gdb_assert (*tailcall_cachep == NULL); | 
|  |  | 
|  | /* PC may be after the function if THIS_FRAME calls noreturn function, | 
|  | get_frame_address_in_block will decrease it by 1 in such case.  */ | 
|  | this_pc = get_frame_address_in_block (this_frame); | 
|  |  | 
|  | try | 
|  | { | 
|  | int sp_regnum; | 
|  |  | 
|  | prev_gdbarch = frame_unwind_arch (this_frame); | 
|  |  | 
|  | /* Simulate frame_unwind_pc without setting this_frame->prev_pc.p.  */ | 
|  | 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); | 
|  |  | 
|  | if (entry_cfa_sp_offsetp != NULL) | 
|  | { | 
|  | sp_regnum = gdbarch_sp_regnum (prev_gdbarch); | 
|  | if (sp_regnum != -1) | 
|  | { | 
|  | prev_sp = frame_unwind_register_unsigned (this_frame, sp_regnum); | 
|  | prev_sp_p = 1; | 
|  | } | 
|  | } | 
|  | } | 
|  | catch (const gdb_exception_error &except) | 
|  | { | 
|  | if (entry_values_debug) | 
|  | exception_print (gdb_stdout, except); | 
|  |  | 
|  | switch (except.error) | 
|  | { | 
|  | case NO_ENTRY_VALUE_ERROR: | 
|  | /* Thrown by call_site_find_chain.  */ | 
|  | case MEMORY_ERROR: | 
|  | case OPTIMIZED_OUT_ERROR: | 
|  | case NOT_AVAILABLE_ERROR: | 
|  | /* These can normally happen when we try to access an | 
|  | optimized out or unavailable register, either in a | 
|  | physical register or spilled to memory.  */ | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Let unexpected errors propagate.  */ | 
|  | throw; | 
|  | } | 
|  |  | 
|  | /* Ambiguous unwind or unambiguous unwind verified as matching.  */ | 
|  | if (chain == NULL || chain->length == 0) | 
|  | return; | 
|  |  | 
|  | cache = cache_new_ref1 (this_frame); | 
|  | *tailcall_cachep = cache; | 
|  | cache->chain = chain.release (); | 
|  | cache->prev_pc = prev_pc; | 
|  | cache->chain_levels = pretended_chain_levels (cache->chain); | 
|  | cache->prev_sp_p = prev_sp_p; | 
|  | if (cache->prev_sp_p) | 
|  | { | 
|  | cache->prev_sp = prev_sp; | 
|  | cache->entry_cfa_sp_offset = *entry_cfa_sp_offsetp; | 
|  | } | 
|  | gdb_assert (cache->chain_levels > 0); | 
|  | } | 
|  |  | 
|  | /* Implementation of frame_dealloc_cache_ftype.  It can be called even for the | 
|  | bottom chain frame from dwarf2_frame_dealloc_cache which is not a real | 
|  | TAILCALL_FRAME.  */ | 
|  |  | 
|  | static void | 
|  | tailcall_frame_dealloc_cache (frame_info *self, void *this_cache) | 
|  | { | 
|  | struct tailcall_cache *cache = (struct tailcall_cache *) this_cache; | 
|  |  | 
|  | cache_unref (cache); | 
|  | } | 
|  |  | 
|  | /* Implementation of frame_prev_arch_ftype.  We assume all the virtual tail | 
|  | call frames have gdbarch of the bottom (callee) frame.  */ | 
|  |  | 
|  | static struct gdbarch * | 
|  | tailcall_frame_prev_arch (const frame_info_ptr &this_frame, | 
|  | void **this_prologue_cache) | 
|  | { | 
|  | struct tailcall_cache *cache = (struct tailcall_cache *) *this_prologue_cache; | 
|  |  | 
|  | return get_frame_arch (frame_info_ptr (cache->next_bottom_frame)); | 
|  | } | 
|  |  | 
|  | /* Virtual tail call frame unwinder if dwarf2_tailcall_sniffer_first finds | 
|  | a chain to create.  */ | 
|  |  | 
|  | const struct frame_unwind dwarf2_tailcall_frame_unwind = | 
|  | { | 
|  | "dwarf2 tailcall", | 
|  | TAILCALL_FRAME, | 
|  | default_frame_unwind_stop_reason, | 
|  | tailcall_frame_this_id, | 
|  | tailcall_frame_prev_register, | 
|  | NULL, | 
|  | tailcall_frame_sniffer, | 
|  | tailcall_frame_dealloc_cache, | 
|  | tailcall_frame_prev_arch | 
|  | }; | 
|  |  | 
|  | void _initialize_tailcall_frame (); | 
|  | void | 
|  | _initialize_tailcall_frame () | 
|  | { | 
|  | cache_htab = htab_create_alloc (50, cache_hash, cache_eq, NULL, xcalloc, | 
|  | xfree); | 
|  | } |