mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-21 04:42:53 +08:00
6d7aa59270
Running the testsuite against an Asan-enabled build of GDB makes gdb.base/multi-target.exp expose this bug. scoped_restore_current_thread's ctor calls get_frame_id to record the selected frame's ID to restore later. If the frame ID hasn't been computed yet, it will be computed on the spot, and that will usually require accessing the target's memory and registers, which requires remote accesses. If the remote connection closes while we're computing the frame ID, the remote target exits its inferiors, unpushes itself, and throws a TARGET_CLOSE_ERROR error. If that happens, GDB can currently crash, here: > ==18555==ERROR: AddressSanitizer: heap-use-after-free on address 0x621004670aa8 at pc 0x0000007ab125 bp 0x7ffdecaecd20 sp 0x7ffdecaecd10 > READ of size 4 at 0x621004670aa8 thread T0 > #0 0x7ab124 in dwarf2_frame_this_id src/binutils-gdb/gdb/dwarf2/frame.c:1228 > #1 0x983ec5 in compute_frame_id src/binutils-gdb/gdb/frame.c:550 > #2 0x9841ee in get_frame_id(frame_info*) src/binutils-gdb/gdb/frame.c:582 > #3 0x1093faa in scoped_restore_current_thread::scoped_restore_current_thread() src/binutils-gdb/gdb/thread.c:1462 > #4 0xaee5ba in fetch_inferior_event(void*) src/binutils-gdb/gdb/infrun.c:3968 > #5 0xaa990b in inferior_event_handler(inferior_event_type, void*) src/binutils-gdb/gdb/inf-loop.c:43 > #6 0xea61b6 in remote_async_serial_handler src/binutils-gdb/gdb/remote.c:14161 > #7 0xefca8a in run_async_handler_and_reschedule src/binutils-gdb/gdb/ser-base.c:137 > #8 0xefcd23 in fd_event src/binutils-gdb/gdb/ser-base.c:188 > #9 0x15a7416 in handle_file_event src/binutils-gdb/gdbsupport/event-loop.cc:548 > #10 0x15a7c36 in gdb_wait_for_event src/binutils-gdb/gdbsupport/event-loop.cc:673 > #11 0x15a5dbb in gdb_do_one_event() src/binutils-gdb/gdbsupport/event-loop.cc:215 > #12 0xbfe62d in start_event_loop src/binutils-gdb/gdb/main.c:356 > #13 0xbfe935 in captured_command_loop src/binutils-gdb/gdb/main.c:416 > #14 0xc01d39 in captured_main src/binutils-gdb/gdb/main.c:1253 > #15 0xc01dc9 in gdb_main(captured_main_args*) src/binutils-gdb/gdb/main.c:1268 > #16 0x414ddd in main src/binutils-gdb/gdb/gdb.c:32 > #17 0x7f590110b82f in __libc_start_main ../csu/libc-start.c:291 > #18 0x414bd8 in _start (build/binutils-gdb/gdb/gdb+0x414bd8) What happens is that above, we're in dwarf2_frame_this_id, just after the dwarf2_frame_cache call. The "cache" variable that the dwarf2_frame_cache function returned is already stale. It's been released here, from within the dwarf2_frame_cache: (top-gdb) bt #0 reinit_frame_cache () at src/gdb/frame.c:1855 #1 0x00000000014ff7b0 in switch_to_no_thread () at src/gdb/thread.c:1301 #2 0x0000000000f66d3e in switch_to_inferior_no_thread (inf=0x615000338180) at src/gdb/inferior.c:626 #3 0x00000000012f3826 in remote_unpush_target (target=0x6170000c5900) at src/gdb/remote.c:5521 #4 0x00000000013097e0 in remote_target::readchar (this=0x6170000c5900, timeout=2) at src/gdb/remote.c:9137 #5 0x000000000130be4d in remote_target::getpkt_or_notif_sane_1 (this=0x6170000c5900, buf=0x6170000c5918, forever=0, expecting_notif=0, is_notif=0x0) at src/gdb/remote.c:9683 #6 0x000000000130c8ab in remote_target::getpkt_sane (this=0x6170000c5900, buf=0x6170000c5918, forever=0) at src/gdb/remote.c:9790 #7 0x000000000130bc0d in remote_target::getpkt (this=0x6170000c5900, buf=0x6170000c5918, forever=0) at src/gdb/remote.c:9623 #8 0x000000000130838e in remote_target::remote_read_bytes_1 (this=0x6170000c5900, memaddr=0x7fffffffcdc0, myaddr=0x6080000ad3bc "", len_units=64, unit_size=1, xfered_len_units=0x7fff6a29b9a0) at src/gdb/remote.c:8860 #9 0x0000000001308bd2 in remote_target::remote_read_bytes (this=0x6170000c5900, memaddr=0x7fffffffcdc0, myaddr=0x6080000ad3bc "", len=64, unit_size=1, xfered_len=0x7fff6a29b9a0) at src/gdb/remote.c:8987 #10 0x0000000001311ed1 in remote_target::xfer_partial (this=0x6170000c5900, object=TARGET_OBJECT_MEMORY, annex=0x0, readbuf=0x6080000ad3bc "", writebuf=0x0, offset=140737488342464, len=64, xfered_len=0x7fff6a29b9a0) at src/gdb/remote.c:10988 #11 0x00000000014ba969 in raw_memory_xfer_partial (ops=0x6170000c5900, readbuf=0x6080000ad3bc "", writebuf=0x0, memaddr=140737488342464, len=64, xfered_len=0x7fff6a29b9a0) at src/gdb/target.c:918 #12 0x00000000014bb720 in target_xfer_partial (ops=0x6170000c5900, object=TARGET_OBJECT_RAW_MEMORY, annex=0x0, readbuf=0x6080000ad3bc "", writebuf=0x0, offset=140737488342464, len=64, xfered_len=0x7fff6a29b9a0) at src/gdb/target.c:1148 #13 0x00000000014bc3b5 in target_read_partial (ops=0x6170000c5900, object=TARGET_OBJECT_RAW_MEMORY, annex=0x0, buf=0x6080000ad3bc "", offset=140737488342464, len=64, xfered_len=0x7fff6a29b9a0) at src/gdb/target.c:1380 #14 0x00000000014bc593 in target_read (ops=0x6170000c5900, object=TARGET_OBJECT_RAW_MEMORY, annex=0x0, buf=0x6080000ad3bc "", offset=140737488342464, len=64) at src/gdb/target.c:1419 #15 0x00000000014bbd4d in target_read_raw_memory (memaddr=0x7fffffffcdc0, myaddr=0x6080000ad3bc "", len=64) at src/gdb/target.c:1252 #16 0x0000000000bf27df in dcache_read_line (dcache=0x6060001eddc0, db=0x6080000ad3a0) at src/gdb/dcache.c:336 #17 0x0000000000bf2b72 in dcache_peek_byte (dcache=0x6060001eddc0, addr=0x7fffffffcdd8, ptr=0x6020001231b0 "") at src/gdb/dcache.c:403 #18 0x0000000000bf3103 in dcache_read_memory_partial (ops=0x6170000c5900, dcache=0x6060001eddc0, memaddr=0x7fffffffcdd8, myaddr=0x6020001231b0 "", len=8, xfered_len=0x7fff6a29bf20) at src/gdb/dcache.c:484 #19 0x00000000014bafe9 in memory_xfer_partial_1 (ops=0x6170000c5900, object=TARGET_OBJECT_STACK_MEMORY, readbuf=0x6020001231b0 "", writebuf=0x0, memaddr=140737488342488, len=8, xfered_len=0x7fff6a29bf20) at src/gdb/target.c:1034 #20 0x00000000014bb212 in memory_xfer_partial (ops=0x6170000c5900, object=TARGET_OBJECT_STACK_MEMORY, readbuf=0x6020001231b0 "", writebuf=0x0, memaddr=140737488342488, len=8, xfered_len=0x7fff6a29bf20) at src/gdb/target.c:1076 #21 0x00000000014bb6b3 in target_xfer_partial (ops=0x6170000c5900, object=TARGET_OBJECT_STACK_MEMORY, annex=0x0, readbuf=0x6020001231b0 "", writebuf=0x0, offset=140737488342488, len=8, xfered_len=0x7fff6a29bf20) at src/gdb/target.c:1133 #22 0x000000000164564d in read_value_memory (val=0x60f000029440, bit_offset=0, stack=1, memaddr=0x7fffffffcdd8, buffer=0x6020001231b0 "", length=8) at src/gdb/valops.c:956 #23 0x0000000001680fff in value_fetch_lazy_memory (val=0x60f000029440) at src/gdb/value.c:3764 #24 0x0000000001681efd in value_fetch_lazy (val=0x60f000029440) at src/gdb/value.c:3910 #25 0x0000000001676143 in value_optimized_out (value=0x60f000029440) at src/gdb/value.c:1411 #26 0x0000000000e0fcb8 in frame_register_unwind (next_frame=0x6210066bfde0, regnum=16, optimizedp=0x7fff6a29c200, unavailablep=0x7fff6a29c240, lvalp=0x7fff6a29c2c0, addrp=0x7fff6a29c300, realnump=0x7fff6a29c280, bufferp=0x7fff6a29c3a0 "@\304)j\377\177") at src/gdb/frame.c:1144 #27 0x0000000000e10418 in frame_unwind_register (next_frame=0x6210066bfde0, regnum=16, buf=0x7fff6a29c3a0 "@\304)j\377\177") at src/gdb/frame.c:1196 #28 0x0000000000f00431 in i386_unwind_pc (gdbarch=0x6210043d0110, next_frame=0x6210066bfde0) at src/gdb/i386-tdep.c:1969 #29 0x0000000000e39724 in gdbarch_unwind_pc (gdbarch=0x6210043d0110, next_frame=0x6210066bfde0) at src/gdb/gdbarch.c:3056 #30 0x0000000000c2ea90 in dwarf2_tailcall_sniffer_first (this_frame=0x6210066bfde0, tailcall_cachep=0x6210066bfee0, entry_cfa_sp_offsetp=0x0) at src/gdb/dwarf2/frame-tailcall.c:423 #31 0x0000000000c36bdb in dwarf2_frame_cache (this_frame=0x6210066bfde0, this_cache=0x6210066bfdf8) at src/gdb/dwarf2/frame.c:1198 #32 0x0000000000c36eb3 in dwarf2_frame_this_id (this_frame=0x6210066bfde0, this_cache=0x6210066bfdf8, this_id=0x6210066bfe40) at src/gdb/dwarf2/frame.c:1226 Note that remote_target::readchar in frame #4 throws TARGET_CLOSE_ERROR after the remote_unpush_target in frame #3 returns. The problem is that the TARGET_CLOSE_ERROR is swallowed by value_optimized_out in frame #25. If we fix that one, then we run into dwarf2_tailcall_sniffer_first swallowing the exception in frame #30 too. The attached patch fixes it by making those spots swallow fewer kinds of errors. gdb/ChangeLog: * frame-tailcall.c (dwarf2_tailcall_sniffer_first): Only swallow NO_ENTRY_VALUE_ERROR / MEMORY_ERROR / OPTIMIZED_OUT_ERROR / NOT_AVAILABLE_ERROR. * value.c (value_optimized_out): Only swallow MEMORY_ERROR / OPTIMIZED_OUT_ERROR / NOT_AVAILABLE_ERROR.
488 lines
14 KiB
C
488 lines
14 KiB
C
/* 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;
|
|
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 (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);
|
|
}
|