diff options
Diffstat (limited to 'gdb/tramp-frame.c')
-rw-r--r-- | gdb/tramp-frame.c | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/gdb/tramp-frame.c b/gdb/tramp-frame.c new file mode 100644 index 0000000..7a8057e --- /dev/null +++ b/gdb/tramp-frame.c @@ -0,0 +1,162 @@ +/* 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" + +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] != 0; ti++) + { + CORE_ADDR func = pc - tramp->insn_size * ti; + int i; + for (i = 0; 1; i++) + { + bfd_byte buf[sizeof (LONGEST)]; + CORE_ADDR insn; + if (tramp->insn[i] == 0) + 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; + + 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); +} |