aboutsummaryrefslogtreecommitdiff
path: root/gdb/dwarf2/frame-tailcall.c
diff options
context:
space:
mode:
authorTom Tromey <tom@tromey.com>2020-02-08 13:40:54 -0700
committerTom Tromey <tom@tromey.com>2020-02-08 13:40:59 -0700
commit82ca895718becbd1be898a86e4a6d1217fa496c6 (patch)
tree9678baae2a5c7c9772cb39b917bc87114d3261fc /gdb/dwarf2/frame-tailcall.c
parent9e35d4996169bc7fa833e8449585d517b8ccc5f8 (diff)
downloadfsf-binutils-gdb-82ca895718becbd1be898a86e4a6d1217fa496c6.zip
fsf-binutils-gdb-82ca895718becbd1be898a86e4a6d1217fa496c6.tar.gz
fsf-binutils-gdb-82ca895718becbd1be898a86e4a6d1217fa496c6.tar.bz2
Move DWARF code to dwarf2/ subdirectory
This moves all the remaining DWARF code to the new dwarf2 subdirectory. This is just a simple renaming, with updates to includes as needed. gdb/ChangeLog 2020-02-08 Tom Tromey <tom@tromey.com> * dwarf2/expr.c: Rename from dwarf2expr.c. * dwarf2/expr.h: Rename from dwarf2expr.h. * dwarf2/frame-tailcall.c: Rename from dwarf2-frame-tailcall.c. * dwarf2/frame-tailcall.h: Rename from dwarf2-frame-tailcall.h. * dwarf2/frame.c: Rename from dwarf2-frame.c. * dwarf2/frame.h: Rename from dwarf2-frame.h. * dwarf2/index-cache.c: Rename from dwarf-index-cache.c. * dwarf2/index-cache.h: Rename from dwarf-index-cache.h. * dwarf2/index-common.c: Rename from dwarf-index-common.c. * dwarf2/index-common.h: Rename from dwarf-index-common.h. * dwarf2/index-write.c: Rename from dwarf-index-write.c. * dwarf2/index-write.h: Rename from dwarf-index-write.h. * dwarf2/loc.c: Rename from dwarf2loc.c. * dwarf2/loc.h: Rename from dwarf2loc.h. * dwarf2/read.c: Rename from dwarf2read.c. * dwarf2/read.h: Rename from dwarf2read.h. * dwarf2/abbrev.c, aarch64-tdep.c, alpha-tdep.c, amd64-darwin-tdep.c, arc-tdep.c, arm-tdep.c, bfin-tdep.c, compile/compile-c-symbols.c, compile/compile-cplus-symbols.c, compile/compile-loc2c.c, cris-tdep.c, csky-tdep.c, findvar.c, gdbtypes.c, guile/scm-type.c, h8300-tdep.c, hppa-bsd-tdep.c, hppa-linux-tdep.c, i386-darwin-tdep.c, i386-linux-tdep.c, i386-tdep.c, iq2000-tdep.c, m32c-tdep.c, m68hc11-tdep.c, m68k-tdep.c, microblaze-tdep.c, mips-tdep.c, mn10300-tdep.c, msp430-tdep.c, nds32-tdep.c, nios2-tdep.c, or1k-tdep.c, riscv-tdep.c, rl78-tdep.c, rs6000-tdep.c, rx-tdep.c, s12z-tdep.c, s390-tdep.c, score-tdep.c, sh-tdep.c, sparc-linux-tdep.c, sparc-tdep.c, sparc64-linux-tdep.c, sparc64-tdep.c, tic6x-tdep.c, tilegx-tdep.c, v850-tdep.c, xstormy16-tdep.c, xtensa-tdep.c: Update. * Makefile.in (COMMON_SFILES): Update. (HFILES_NO_SRCDIR): Update. Change-Id: Ied9ce1436cd27ac4a4cffef10ec92e396f181928
Diffstat (limited to 'gdb/dwarf2/frame-tailcall.c')
-rw-r--r--gdb/dwarf2/frame-tailcall.c476
1 files changed, 476 insertions, 0 deletions
diff --git a/gdb/dwarf2/frame-tailcall.c b/gdb/dwarf2/frame-tailcall.c
new file mode 100644
index 0000000..3dc300d
--- /dev/null
+++ b/gdb/dwarf2/frame-tailcall.c
@@ -0,0 +1,476 @@
+/* Virtual tail call frames unwinder for GDB.
+
+ Copyright (C) 2010-2020 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 "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. */
+ struct 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 (struct frame_info *next_bottom_frame)
+{
+ struct tailcall_cache *cache = XCNEW (struct tailcall_cache);
+ void **slot;
+
+ cache->next_bottom_frame = next_bottom_frame;
+ 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 (struct frame_info *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 (struct frame_info *fi)
+{
+ struct tailcall_cache *cache;
+ void **slot;
+
+ while (frame_is_tailcall (fi))
+ {
+ fi = get_next_frame (fi);
+ gdb_assert (fi != NULL);
+ }
+
+ slot = htab_find_slot (cache_htab, &fi, 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 (struct frame_info *this_frame,
+ struct tailcall_cache *cache)
+{
+ int retval = (frame_relative_level (this_frame)
+ - frame_relative_level (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 (struct frame_info *this_frame, void **this_cache,
+ struct frame_id *this_id)
+{
+ struct tailcall_cache *cache = (struct tailcall_cache *) *this_cache;
+ struct frame_info *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 = 1;
+ (*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 (struct frame_info *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 (struct frame_info *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 (struct frame_info *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,
+ struct frame_info *this_frame, void **this_cache)
+{
+ struct frame_info *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 (struct frame_info *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;
+ struct call_site_chain *chain = NULL;
+ 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);
+
+ /* Catch any unwinding errors. */
+ 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);
+ return;
+ }
+
+ /* Ambiguous unwind or unambiguous unwind verified as matching. */
+ if (chain == NULL || chain->length == 0)
+ {
+ xfree (chain);
+ return;
+ }
+
+ cache = cache_new_ref1 (this_frame);
+ *tailcall_cachep = cache;
+ cache->chain = chain;
+ cache->prev_pc = prev_pc;
+ cache->chain_levels = pretended_chain_levels (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 (struct 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 (struct frame_info *this_frame,
+ void **this_prologue_cache)
+{
+ struct tailcall_cache *cache = (struct tailcall_cache *) *this_prologue_cache;
+
+ return get_frame_arch (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 =
+{
+ 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);
+}