From d7066cce3b65c18d68611bfa95407a3df69bd2df Mon Sep 17 00:00:00 2001 From: Anthony Green Date: Fri, 24 Apr 2009 02:26:01 +0000 Subject: [PATCH] Add moxie-elf port. --- gdb/ChangeLog | 7 + gdb/MAINTAINERS | 3 + gdb/configure.tgt | 6 + gdb/moxie-tdep.c | 531 ++++++++++++++++++++++++++++++++++++++++++++++ gdb/moxie-tdep.h | 43 ++++ 5 files changed, 590 insertions(+) create mode 100644 gdb/moxie-tdep.c create mode 100644 gdb/moxie-tdep.h diff --git a/gdb/ChangeLog b/gdb/ChangeLog index f70787cc1b..918fd50176 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,10 @@ +2009-04-23 Anthony Green + + * MAINTAINERS: Add moxie target. + * configure.tgt: Add moxie-*-elf target. + * moxie-tdep.c: New file. + * moxie-tdep.h: New file. + 2009-04-23 Joel Brobecker * ada-lang.c: Remove an extra empty line. No code change. diff --git a/gdb/MAINTAINERS b/gdb/MAINTAINERS index 874f4b9836..3d728e2b63 100644 --- a/gdb/MAINTAINERS +++ b/gdb/MAINTAINERS @@ -294,6 +294,9 @@ the native maintainer when resolving ABI issues. (sim/ dies with make -j) Michael Snyder msnyder@vmware.com + moxie --target=moxie-elf ,-Werror + Anthony Green green@moxielogic.com + ms1 --target=ms1-elf ,-Werror Kevin Buettner kevinb@redhat.com diff --git a/gdb/configure.tgt b/gdb/configure.tgt index e766d68f16..f06f5f0f90 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -114,6 +114,12 @@ frv-*-*) gdb_sim=../sim/frv/libsim.a ;; +moxie-*-elf) + gdb_target_obs="moxie-tdep.o" + # Add this once the moxie sim has been committed. + # gdb_sim=../sim/moxie/libsim.a + ;; + h8300-*-*) # Target: H8300 with HMS monitor and H8 simulator gdb_target_obs="h8300-tdep.o monitor.o dsrec.o" diff --git a/gdb/moxie-tdep.c b/gdb/moxie-tdep.c new file mode 100644 index 0000000000..884416ce49 --- /dev/null +++ b/gdb/moxie-tdep.c @@ -0,0 +1,531 @@ +/* Target-dependent code for Moxie. + + Copyright (C) 2009 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 . */ + +#include "defs.h" +#include "frame.h" +#include "frame-unwind.h" +#include "frame-base.h" +#include "symtab.h" +#include "gdbtypes.h" +#include "gdbcmd.h" +#include "gdbcore.h" +#include "gdb_string.h" +#include "value.h" +#include "inferior.h" +#include "symfile.h" +#include "objfiles.h" +#include "osabi.h" +#include "language.h" +#include "arch-utils.h" +#include "regcache.h" +#include "trad-frame.h" +#include "dis-asm.h" + +#include "gdb_assert.h" + +#include "moxie-tdep.h" + +/* Local functions. */ + +extern void _initialize_moxie_tdep (void); + +/* Use an invalid address value as 'not available' marker. */ +enum { REG_UNAVAIL = (CORE_ADDR) -1 }; + +struct moxie_frame_cache +{ + /* Base address. */ + CORE_ADDR base; + CORE_ADDR pc; + LONGEST framesize; + CORE_ADDR saved_regs[MOXIE_NUM_REGS]; + CORE_ADDR saved_sp; +}; + +/* Implement the "frame_align" gdbarch method. */ + +static CORE_ADDR +moxie_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp) +{ + /* Align to the size of an instruction (so that they can safely be + pushed onto the stack. */ + return sp & ~1; +} + +/* Implement the "breakpoint_from_pc" gdbarch method. */ + +const static unsigned char * +moxie_breakpoint_from_pc (struct gdbarch *gdbarch, + CORE_ADDR *pcptr, int *lenptr) +{ + static unsigned char breakpoint[] = { 0x35, 0x00 }; + + *lenptr = sizeof (breakpoint); + return breakpoint; +} + +/* Moxie register names. */ + +char *moxie_register_names[] = { + "$fp", "$sp", "$r0", "$r1", "$r2", + "$r3", "$r4", "$r5", "$r6", "$r7", + "$r8", "$r9", "$r10", "$r11", "$r12", + "$r13", "$pc", "$cc" }; + +/* Implement the "register_name" gdbarch method. */ + +static const char * +moxie_register_name (struct gdbarch *gdbarch, int reg_nr) +{ + if (reg_nr < 0) + return NULL; + if (reg_nr >= MOXIE_NUM_REGS) + return NULL; + return moxie_register_names[reg_nr]; +} + +/* Implement the "register_type" gdbarch method. */ + +static struct type * +moxie_register_type (struct gdbarch *gdbarch, int reg_nr) +{ + if (reg_nr == MOXIE_PC_REGNUM) + return builtin_type (gdbarch)->builtin_func_ptr; + else if (reg_nr == MOXIE_SP_REGNUM || reg_nr == MOXIE_FP_REGNUM) + return builtin_type (gdbarch)->builtin_data_ptr; + else + return builtin_type_int32; +} + +/* Write into appropriate registers a function return value + of type TYPE, given in virtual format. */ + +static void +moxie_store_return_value (struct type *type, struct regcache *regcache, + const void *valbuf) +{ + CORE_ADDR regval; + int len = TYPE_LENGTH (type); + + /* Things always get returned in RET1_REGNUM, RET2_REGNUM. */ + regval = extract_unsigned_integer (valbuf, len > 4 ? 4 : len); + regcache_cooked_write_unsigned (regcache, RET1_REGNUM, regval); + if (len > 4) + { + regval = extract_unsigned_integer ((gdb_byte *) valbuf + 4, len - 4); + regcache_cooked_write_unsigned (regcache, RET1_REGNUM + 1, regval); + } +} + +/* Decode the instructions within the given address range. Decide + when we must have reached the end of the function prologue. If a + frame_info pointer is provided, fill in its saved_regs etc. + + Returns the address of the first instruction after the prologue. */ + +static CORE_ADDR +moxie_analyze_prologue (CORE_ADDR start_addr, CORE_ADDR end_addr, + struct moxie_frame_cache *cache, + struct frame_info *this_frame) +{ + CORE_ADDR next_addr; + ULONGEST inst, inst2; + LONGEST offset; + int regnum; + + /* Record where the jsra instruction saves the PC and FP. */ + cache->saved_regs[MOXIE_PC_REGNUM] = -4; + cache->saved_regs[MOXIE_FP_REGNUM] = 0; + cache->framesize = 0; + + if (start_addr >= end_addr) + return end_addr; + + for (next_addr = start_addr; next_addr < end_addr; ) + { + inst = read_memory_unsigned_integer (next_addr, 2); + + /* Match "push $rN" where N is between 2 and 13 inclusive. */ + if (inst >= 0x0614 && inst <= 0x061f) + { + regnum = inst & 0x000f; + cache->framesize += 4; + cache->saved_regs[regnum] = cache->framesize; + next_addr += 2; + } + + /* Optional stack allocation for args and local vars <= 4 + byte. */ + else if (inst == 0x01f0) /* ldi.l $r12, X */ + { + offset = read_memory_integer (next_addr + 2, 4); + inst2 = read_memory_unsigned_integer (next_addr + 6, 2); + + if (inst2 == 0x051f) /* add.l $sp, $r12 */ + { + cache->framesize += offset; + } + + return (next_addr + 8); + } + else /* This is not a prologue instruction. */ + break; + } + + return next_addr; +} + +/* Find the end of function prologue. */ + +static CORE_ADDR +moxie_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) +{ + CORE_ADDR func_addr = 0, func_end = 0; + char *func_name; + + /* See if we can determine the end of the prologue via the symbol table. + If so, then return either PC, or the PC after the prologue, whichever + is greater. */ + if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end)) + { + CORE_ADDR post_prologue_pc = skip_prologue_using_sal (func_addr); + if (post_prologue_pc != 0) + return max (pc, post_prologue_pc); + else + { + /* Can't determine prologue from the symbol table, need to examine + instructions. */ + struct symtab_and_line sal; + struct symbol *sym; + struct moxie_frame_cache cache; + CORE_ADDR plg_end; + + memset (&cache, 0, sizeof cache); + + plg_end = moxie_analyze_prologue (func_addr, + func_end, &cache, NULL); + /* Found a function. */ + sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL); + /* Don't use line number debug info for assembly source + files. */ + if (sym && SYMBOL_LANGUAGE (sym) != language_asm) + { + sal = find_pc_line (func_addr, 0); + if (sal.end && sal.end < func_end) + { + /* Found a line number, use it as end of + prologue. */ + return sal.end; + } + } + /* No useable line symbol. Use result of prologue parsing + method. */ + return plg_end; + } + } + + /* No function symbol -- just return the PC. */ + return (CORE_ADDR) pc; +} + +struct moxie_unwind_cache +{ + /* The previous frame's inner most stack address. Used as this + frame ID's stack_addr. */ + CORE_ADDR prev_sp; + /* The frame's base, optionally used by the high-level debug info. */ + CORE_ADDR base; + int size; + /* How far the SP and r13 (FP) have been offset from the start of + the stack frame (as defined by the previous frame's stack + pointer). */ + LONGEST sp_offset; + LONGEST r13_offset; + int uses_frame; + /* Table indicating the location of each and every register. */ + struct trad_frame_saved_reg *saved_regs; +}; + +/* Implement the "read_pc" gdbarch method. */ + +static CORE_ADDR +moxie_read_pc (struct regcache *regcache) +{ + ULONGEST pc; + + regcache_cooked_read_unsigned (regcache, MOXIE_PC_REGNUM, &pc); + return pc; +} + +/* Implement the "write_pc" gdbarch method. */ + +static void +moxie_write_pc (struct regcache *regcache, CORE_ADDR val) +{ + regcache_cooked_write_unsigned (regcache, MOXIE_PC_REGNUM, val); +} + +/* Implement the "unwind_pc" gdbarch method. */ + +static CORE_ADDR +moxie_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame) +{ + return frame_unwind_register_unsigned (next_frame, MOXIE_SP_REGNUM); +} + +/* Given a return value in `regbuf' with a type `valtype', + extract and copy its value into `valbuf'. */ + +static void +moxie_extract_return_value (struct type *type, struct regcache *regcache, + void *dst) +{ + bfd_byte *valbuf = dst; + int len = TYPE_LENGTH (type); + ULONGEST tmp; + + /* By using store_unsigned_integer we avoid having to do + anything special for small big-endian values. */ + regcache_cooked_read_unsigned (regcache, RET1_REGNUM, &tmp); + store_unsigned_integer (valbuf, (len > 4 ? len - 4 : len), tmp); + + /* Ignore return values more than 8 bytes in size because the moxie + returns anything more than 8 bytes in the stack. */ + if (len > 4) + { + regcache_cooked_read_unsigned (regcache, RET1_REGNUM + 1, &tmp); + store_unsigned_integer (valbuf + len - 4, 4, tmp); + } +} + +/* Implement the "return_value" gdbarch method. */ + +static enum return_value_convention +moxie_return_value (struct gdbarch *gdbarch, struct type *func_type, + struct type *valtype, struct regcache *regcache, + gdb_byte *readbuf, const gdb_byte *writebuf) +{ + if (TYPE_LENGTH (valtype) > 8) + return RETURN_VALUE_STRUCT_CONVENTION; + else + { + if (readbuf != NULL) + moxie_extract_return_value (valtype, regcache, readbuf); + if (writebuf != NULL) + moxie_store_return_value (valtype, regcache, writebuf); + return RETURN_VALUE_REGISTER_CONVENTION; + } +} + +/* Allocate and initialize a moxie_frame_cache object. */ + +static struct moxie_frame_cache * +moxie_alloc_frame_cache (void) +{ + struct moxie_frame_cache *cache; + int i; + + cache = FRAME_OBSTACK_ZALLOC (struct moxie_frame_cache); + + cache->base = 0; + cache->saved_sp = 0; + cache->pc = 0; + cache->framesize = 0; + for (i = 0; i < MOXIE_NUM_REGS; ++i) + cache->saved_regs[i] = REG_UNAVAIL; + + return cache; +} + +/* Populate a moxie_frame_cache object for this_frame. */ + +static struct moxie_frame_cache * +moxie_frame_cache (struct frame_info *this_frame, void **this_cache) +{ + struct moxie_frame_cache *cache; + CORE_ADDR current_pc; + int i; + + if (*this_cache) + return *this_cache; + + cache = moxie_alloc_frame_cache (); + *this_cache = cache; + + cache->base = get_frame_register_unsigned (this_frame, MOXIE_FP_REGNUM); + if (cache->base == 0) + return cache; + + cache->pc = get_frame_func (this_frame); + current_pc = get_frame_pc (this_frame); + if (cache->pc) + moxie_analyze_prologue (cache->pc, current_pc, cache, this_frame); + + cache->saved_sp = cache->base - cache->framesize; + + for (i = 0; i < MOXIE_NUM_REGS; ++i) + if (cache->saved_regs[i] != REG_UNAVAIL) + cache->saved_regs[i] = cache->base - cache->saved_regs[i]; + + return cache; +} + +/* Implement the "unwind_pc" gdbarch method. */ + +static CORE_ADDR +moxie_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame) +{ + return frame_unwind_register_unsigned (next_frame, MOXIE_PC_REGNUM); +} + +/* Given a GDB frame, determine the address of the calling function's + frame. This will be used to create a new GDB frame struct. */ + +static void +moxie_frame_this_id (struct frame_info *this_frame, + void **this_prologue_cache, struct frame_id *this_id) +{ + struct moxie_frame_cache *cache = moxie_frame_cache (this_frame, + this_prologue_cache); + + /* This marks the outermost frame. */ + if (cache->base == 0) + return; + + *this_id = frame_id_build (cache->saved_sp, cache->pc); +} + +/* Get the value of register regnum in the previous stack frame. */ + +static struct value * +moxie_frame_prev_register (struct frame_info *this_frame, + void **this_prologue_cache, int regnum) +{ + struct moxie_frame_cache *cache = moxie_frame_cache (this_frame, + this_prologue_cache); + + gdb_assert (regnum >= 0); + + if (regnum == MOXIE_SP_REGNUM && cache->saved_sp) + return frame_unwind_got_constant (this_frame, regnum, cache->saved_sp); + + if (regnum < MOXIE_NUM_REGS && cache->saved_regs[regnum] != REG_UNAVAIL) + return frame_unwind_got_memory (this_frame, regnum, + cache->saved_regs[regnum]); + + return frame_unwind_got_register (this_frame, regnum, regnum); +} + +static const struct frame_unwind moxie_frame_unwind = { + NORMAL_FRAME, + moxie_frame_this_id, + moxie_frame_prev_register, + NULL, + default_frame_sniffer +}; + +/* Return the base address of this_frame. */ + +static CORE_ADDR +moxie_frame_base_address (struct frame_info *this_frame, void **this_cache) +{ + struct moxie_frame_cache *cache = moxie_frame_cache (this_frame, + this_cache); + + return cache->base; +} + +static const struct frame_base moxie_frame_base = { + &moxie_frame_unwind, + moxie_frame_base_address, + moxie_frame_base_address, + moxie_frame_base_address +}; + +static struct frame_id +moxie_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame) +{ + CORE_ADDR sp = get_frame_register_unsigned (this_frame, MOXIE_SP_REGNUM); + + return frame_id_build (sp, get_frame_pc (this_frame)); +} + +/* Allocate and initialize the moxie gdbarch object. */ + +static struct gdbarch * +moxie_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) +{ + struct gdbarch *gdbarch; + struct gdbarch_tdep *tdep; + + /* If there is already a candidate, use it. */ + arches = gdbarch_list_lookup_by_info (arches, &info); + if (arches != NULL) + return arches->gdbarch; + + /* Allocate space for the new architecture. */ + tdep = XMALLOC (struct gdbarch_tdep); + gdbarch = gdbarch_alloc (&info, tdep); + + set_gdbarch_read_pc (gdbarch, moxie_read_pc); + set_gdbarch_write_pc (gdbarch, moxie_write_pc); + set_gdbarch_unwind_sp (gdbarch, moxie_unwind_sp); + + set_gdbarch_num_regs (gdbarch, MOXIE_NUM_REGS); + set_gdbarch_sp_regnum (gdbarch, MOXIE_SP_REGNUM); + set_gdbarch_register_name (gdbarch, moxie_register_name); + set_gdbarch_register_type (gdbarch, moxie_register_type); + + set_gdbarch_return_value (gdbarch, moxie_return_value); + + set_gdbarch_skip_prologue (gdbarch, moxie_skip_prologue); + set_gdbarch_inner_than (gdbarch, core_addr_lessthan); + set_gdbarch_breakpoint_from_pc (gdbarch, moxie_breakpoint_from_pc); + set_gdbarch_frame_align (gdbarch, moxie_frame_align); + + frame_base_set_default (gdbarch, &moxie_frame_base); + + /* Methods for saving / extracting a dummy frame's ID. The ID's + stack address must match the SP value returned by + PUSH_DUMMY_CALL, and saved by generic_save_dummy_frame_tos. */ + set_gdbarch_dummy_id (gdbarch, moxie_dummy_id); + + set_gdbarch_unwind_pc (gdbarch, moxie_unwind_pc); + + set_gdbarch_print_insn (gdbarch, print_insn_moxie); + + /* Hook in ABI-specific overrides, if they have been registered. */ + gdbarch_init_osabi (info, gdbarch); + + /* Hook in the default unwinders. */ + frame_unwind_append_unwinder (gdbarch, &moxie_frame_unwind); + + /* Support simple overlay manager. */ + set_gdbarch_overlay_update (gdbarch, simple_overlay_update); + + return gdbarch; +} + +/* Register this machine's init routine. */ + +void +_initialize_moxie_tdep (void) +{ + register_gdbarch_init (bfd_arch_moxie, moxie_gdbarch_init); +} diff --git a/gdb/moxie-tdep.h b/gdb/moxie-tdep.h new file mode 100644 index 0000000000..cec44e6f1a --- /dev/null +++ b/gdb/moxie-tdep.h @@ -0,0 +1,43 @@ +/* Target-dependent code for Moxie + + Copyright (C) 2009 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 . */ + +#ifndef MOXIE_TDEP_H +#define MOXIE_TDEP_H + +struct gdbarch_tdep +{ + /* gdbarch target dependent data here. Currently unused for MOXIE. */ +}; + +enum moxie_regnum +{ + MOXIE_FP_REGNUM = 0, + MOXIE_SP_REGNUM = 1, + R0_REGNUM = 2, + R1_REGNUM = 3, + MOXIE_PC_REGNUM = 16, + MOXIE_CC_REGNUM = 17, + RET1_REGNUM = R0_REGNUM, + ARG1_REGNUM = R0_REGNUM, + ARGN_REGNUM = R1_REGNUM, +}; + +#define MOXIE_NUM_REGS 18 + +#endif /* moxie-tdep.h */ -- 2.34.1