/* Signal trampoline unwinder, for GDB the GNU Debugger. Copyright 2004 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "defs.h" #include "tramp-frame.h" #include "frame-unwind.h" #include "gdbcore.h" #include "symtab.h" #include "objfiles.h" #include "target.h" #include "trad-frame.h" #include "frame-base.h" #include "gdb_assert.h" struct frame_data { const struct tramp_frame *tramp_frame; }; struct tramp_frame_cache { CORE_ADDR func; const struct tramp_frame *tramp_frame; struct trad_frame_cache *trad_cache; }; static struct trad_frame_cache * tramp_frame_cache (struct frame_info *next_frame, void **this_cache) { CORE_ADDR pc = frame_pc_unwind (next_frame); struct tramp_frame_cache *tramp_cache = (*this_cache); if (tramp_cache->trad_cache == NULL) { tramp_cache->trad_cache = trad_frame_cache_zalloc (next_frame); tramp_cache->tramp_frame->init (tramp_cache->tramp_frame, next_frame, tramp_cache->trad_cache, tramp_cache->func); } return tramp_cache->trad_cache; } static void tramp_frame_this_id (struct frame_info *next_frame, void **this_cache, struct frame_id *this_id) { struct trad_frame_cache *trad_cache = tramp_frame_cache (next_frame, this_cache); trad_frame_get_id (trad_cache, this_id); } static void tramp_frame_prev_register (struct frame_info *next_frame, void **this_cache, int prev_regnum, int *optimizedp, enum lval_type * lvalp, CORE_ADDR *addrp, int *realnump, void *valuep) { struct trad_frame_cache *trad_cache = tramp_frame_cache (next_frame, this_cache); trad_frame_get_register (trad_cache, next_frame, prev_regnum, optimizedp, lvalp, addrp, realnump, valuep); } static CORE_ADDR tramp_frame_start (CORE_ADDR pc, const struct tramp_frame *tramp) { int ti; /* Search through the trampoline for one that matches the instruction sequence around PC. */ for (ti = 0; tramp->insn[ti] != TRAMP_SENTINEL_INSN; ti++) { CORE_ADDR func = pc - tramp->insn_size * ti; int i; for (i = 0; 1; i++) { bfd_byte buf[sizeof (tramp->insn[0])]; ULONGEST insn; if (tramp->insn[i] == TRAMP_SENTINEL_INSN) return func; if (target_read_memory (func + i * tramp->insn_size, buf, tramp->insn_size) != 0) break; insn = extract_unsigned_integer (buf, tramp->insn_size); if (tramp->insn[i] != insn) break; } } /* Trampoline doesn't match. */ return 0; } static int tramp_frame_sniffer (const struct frame_unwind *self, struct frame_info *next_frame, void **this_cache) { const struct tramp_frame *tramp = self->unwind_data->tramp_frame; CORE_ADDR pc = frame_pc_unwind (next_frame); CORE_ADDR func; char *name; struct tramp_frame_cache *tramp_cache; /* If the function has a valid symbol name, it isn't a trampoline. */ find_pc_partial_function (pc, &name, NULL, NULL); if (name != NULL) return 0; /* If the function lives in a valid section (even without a starting point) it isn't a trampoline. */ if (find_pc_section (pc) != NULL) return 0; /* Finally, check that the trampoline matches at PC. */ func = tramp_frame_start (pc, tramp); if (func == 0) return 0; tramp_cache = FRAME_OBSTACK_ZALLOC (struct tramp_frame_cache); tramp_cache->func = func; tramp_cache->tramp_frame = tramp; (*this_cache) = tramp_cache; return 1; } void tramp_frame_append (struct gdbarch *gdbarch, const struct tramp_frame *tramp_frame) { struct frame_data *data; struct frame_unwind *unwinder; int i; /* Check that the instruction sequence contains a sentinel. */ for (i = 0; i < ARRAY_SIZE (tramp_frame->insn); i++) { if (tramp_frame->insn[i] == TRAMP_SENTINEL_INSN) break; } gdb_assert (i < ARRAY_SIZE (tramp_frame->insn)); gdb_assert (tramp_frame->insn_size <= sizeof (tramp_frame->insn[0])); data = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct frame_data); unwinder = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct frame_unwind); data->tramp_frame = tramp_frame; unwinder->type = SIGTRAMP_FRAME; unwinder->unwind_data = data; unwinder->sniffer = tramp_frame_sniffer; unwinder->this_id = tramp_frame_this_id; unwinder->prev_register = tramp_frame_prev_register; frame_unwind_register_unwinder (gdbarch, unwinder); }