/* Target-dependent code for FT32. Copyright (C) 2009-2015 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 "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 "record.h" #include "ft32-tdep.h" #include "gdb/sim-ft32.h" #define RAM_BIAS 0x800000 /* Bias added to RAM addresses. */ /* Local functions. */ extern void _initialize_ft32_tdep (void); /* Use an invalid address -1 as 'not available' marker. */ enum { REG_UNAVAIL = (CORE_ADDR) (-1) }; struct ft32_frame_cache { /* Base address of the frame */ CORE_ADDR base; /* Function this frame belongs to */ CORE_ADDR pc; /* Total size of this frame */ LONGEST framesize; /* Saved registers in this frame */ CORE_ADDR saved_regs[FT32_NUM_REGS]; /* Saved SP in this frame */ CORE_ADDR saved_sp; /* Has the new frame been LINKed. */ bfd_boolean established; }; /* Implement the "frame_align" gdbarch method. */ static CORE_ADDR ft32_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. */ static const unsigned char * ft32_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, int *lenptr) { static const gdb_byte breakpoint[] = { 0x02, 0x00, 0x34, 0x00 }; *lenptr = sizeof (breakpoint); return breakpoint; } /* FT32 register names. */ static const char *const ft32_register_names[] = { "fp", "sp", "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "cc", "pc" }; /* Implement the "register_name" gdbarch method. */ static const char * ft32_register_name (struct gdbarch *gdbarch, int reg_nr) { if (reg_nr < 0) return NULL; if (reg_nr >= FT32_NUM_REGS) return NULL; return ft32_register_names[reg_nr]; } /* Implement the "register_type" gdbarch method. */ static struct type * ft32_register_type (struct gdbarch *gdbarch, int reg_nr) { if (reg_nr == FT32_PC_REGNUM) return builtin_type (gdbarch)->builtin_func_ptr; else if (reg_nr == FT32_SP_REGNUM || reg_nr == FT32_FP_REGNUM) return builtin_type (gdbarch)->builtin_data_ptr; else return builtin_type (gdbarch)->builtin_int32; } /* Write into appropriate registers a function return value of type TYPE, given in virtual format. */ static void ft32_store_return_value (struct type *type, struct regcache *regcache, const gdb_byte *valbuf) { struct gdbarch *gdbarch = get_regcache_arch (regcache); enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); 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, byte_order); regcache_cooked_write_unsigned (regcache, FT32_R0_REGNUM, regval); if (len > 4) { regval = extract_unsigned_integer (valbuf + 4, len - 4, byte_order); regcache_cooked_write_unsigned (regcache, FT32_R1_REGNUM, 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. */ #define IS_PUSH(inst) (((inst) & 0xfff00000) == 0x84000000) #define PUSH_REG(inst) (FT32_R0_REGNUM + (((inst) >> 15) & 0x1f)) #define IS_LINK(inst) (((inst) & 0xffff0000) == 0x95d00000) #define LINK_SIZE(inst) ((inst) & 0xffff) static CORE_ADDR ft32_analyze_prologue (CORE_ADDR start_addr, CORE_ADDR end_addr, struct ft32_frame_cache *cache, struct gdbarch *gdbarch) { enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); CORE_ADDR next_addr; ULONGEST inst, inst2; LONGEST offset; int regnum; cache->saved_regs[FT32_PC_REGNUM] = 0; cache->framesize = 0; if (start_addr >= end_addr) return end_addr; cache->established = 0; for (next_addr = start_addr; next_addr < end_addr; ) { inst = read_memory_unsigned_integer (next_addr, 4, byte_order); if (IS_PUSH (inst)) { regnum = PUSH_REG (inst); cache->framesize += 4; cache->saved_regs[regnum] = cache->framesize; next_addr += 4; } else break; } for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++) { if (cache->saved_regs[regnum] != REG_UNAVAIL) cache->saved_regs[regnum] = cache->framesize - cache->saved_regs[regnum]; } cache->saved_regs[FT32_PC_REGNUM] = cache->framesize; /* It is a LINK? */ if (next_addr < end_addr) { inst = read_memory_unsigned_integer (next_addr, 4, byte_order); if (IS_LINK (inst)) { cache->established = 1; for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++) { if (cache->saved_regs[regnum] != REG_UNAVAIL) cache->saved_regs[regnum] += 4; } cache->saved_regs[FT32_PC_REGNUM] = cache->framesize + 4; cache->saved_regs[FT32_FP_REGNUM] = 0; cache->framesize += LINK_SIZE (inst); next_addr += 4; } } return next_addr; } /* Find the end of function prologue. */ static CORE_ADDR ft32_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) { CORE_ADDR func_addr = 0, func_end = 0; const 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 (gdbarch, 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 ft32_frame_cache cache; CORE_ADDR plg_end; memset (&cache, 0, sizeof cache); plg_end = ft32_analyze_prologue (func_addr, func_end, &cache, gdbarch); /* Found a function. */ sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL).symbol; /* Don't use line number debug info for assembly source files. */ if ((sym != NULL) && 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 pc; } /* Implement the "read_pc" gdbarch method. */ static CORE_ADDR ft32_read_pc (struct regcache *regcache) { ULONGEST pc; regcache_cooked_read_unsigned (regcache, FT32_PC_REGNUM, &pc); return pc; } /* Implement the "write_pc" gdbarch method. */ static void ft32_write_pc (struct regcache *regcache, CORE_ADDR val) { regcache_cooked_write_unsigned (regcache, FT32_PC_REGNUM, val); } /* Implement the "unwind_sp" gdbarch method. */ static CORE_ADDR ft32_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame) { return frame_unwind_register_unsigned (next_frame, FT32_SP_REGNUM); } /* Given a return value in `regbuf' with a type `valtype', extract and copy its value into `valbuf'. */ static void ft32_extract_return_value (struct type *type, struct regcache *regcache, gdb_byte *dst) { struct gdbarch *gdbarch = get_regcache_arch (regcache); enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); 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, FT32_R0_REGNUM, &tmp); store_unsigned_integer (valbuf, (len > 4 ? len - 4 : len), byte_order, tmp); /* Ignore return values more than 8 bytes in size because the ft32 returns anything more than 8 bytes in the stack. */ if (len > 4) { regcache_cooked_read_unsigned (regcache, FT32_R1_REGNUM, &tmp); store_unsigned_integer (valbuf + len - 4, 4, byte_order, tmp); } } /* Implement the "return_value" gdbarch method. */ static enum return_value_convention ft32_return_value (struct gdbarch *gdbarch, struct value *function, 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) ft32_extract_return_value (valtype, regcache, readbuf); if (writebuf != NULL) ft32_store_return_value (valtype, regcache, writebuf); return RETURN_VALUE_REGISTER_CONVENTION; } } /* Allocate and initialize a ft32_frame_cache object. */ static struct ft32_frame_cache * ft32_alloc_frame_cache (void) { struct ft32_frame_cache *cache; int i; cache = FRAME_OBSTACK_ZALLOC (struct ft32_frame_cache); for (i = 0; i < FT32_NUM_REGS; ++i) cache->saved_regs[i] = REG_UNAVAIL; return cache; } /* Populate a ft32_frame_cache object for this_frame. */ static struct ft32_frame_cache * ft32_frame_cache (struct frame_info *this_frame, void **this_cache) { struct ft32_frame_cache *cache; CORE_ADDR current_pc; int i; if (*this_cache) return *this_cache; cache = ft32_alloc_frame_cache (); *this_cache = cache; cache->base = get_frame_register_unsigned (this_frame, FT32_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) { struct gdbarch *gdbarch = get_frame_arch (this_frame); ft32_analyze_prologue (cache->pc, current_pc, cache, gdbarch); if (!cache->established) cache->base = get_frame_register_unsigned (this_frame, FT32_SP_REGNUM); } cache->saved_sp = cache->base - 4; for (i = 0; i < FT32_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 ft32_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame) { return frame_unwind_register_unsigned (next_frame, FT32_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 ft32_frame_this_id (struct frame_info *this_frame, void **this_prologue_cache, struct frame_id *this_id) { struct ft32_frame_cache *cache = ft32_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 * ft32_frame_prev_register (struct frame_info *this_frame, void **this_prologue_cache, int regnum) { struct ft32_frame_cache *cache = ft32_frame_cache (this_frame, this_prologue_cache); gdb_assert (regnum >= 0); if (regnum == FT32_SP_REGNUM && cache->saved_sp) return frame_unwind_got_constant (this_frame, regnum, cache->saved_sp); if (regnum < FT32_NUM_REGS && cache->saved_regs[regnum] != REG_UNAVAIL) return frame_unwind_got_memory (this_frame, regnum, RAM_BIAS | cache->saved_regs[regnum]); return frame_unwind_got_register (this_frame, regnum, regnum); } static const struct frame_unwind ft32_frame_unwind = { NORMAL_FRAME, default_frame_unwind_stop_reason, ft32_frame_this_id, ft32_frame_prev_register, NULL, default_frame_sniffer }; /* Return the base address of this_frame. */ static CORE_ADDR ft32_frame_base_address (struct frame_info *this_frame, void **this_cache) { struct ft32_frame_cache *cache = ft32_frame_cache (this_frame, this_cache); return cache->base; } static const struct frame_base ft32_frame_base = { &ft32_frame_unwind, ft32_frame_base_address, ft32_frame_base_address, ft32_frame_base_address }; static struct frame_id ft32_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame) { CORE_ADDR sp = get_frame_register_unsigned (this_frame, FT32_SP_REGNUM); return frame_id_build (sp, get_frame_pc (this_frame)); } /* Allocate and initialize the ft32 gdbarch object. */ static struct gdbarch * ft32_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 = XNEW (struct gdbarch_tdep); gdbarch = gdbarch_alloc (&info, tdep); set_gdbarch_read_pc (gdbarch, ft32_read_pc); set_gdbarch_write_pc (gdbarch, ft32_write_pc); set_gdbarch_unwind_sp (gdbarch, ft32_unwind_sp); set_gdbarch_num_regs (gdbarch, FT32_NUM_REGS); set_gdbarch_sp_regnum (gdbarch, FT32_SP_REGNUM); set_gdbarch_pc_regnum (gdbarch, FT32_PC_REGNUM); set_gdbarch_register_name (gdbarch, ft32_register_name); set_gdbarch_register_type (gdbarch, ft32_register_type); set_gdbarch_return_value (gdbarch, ft32_return_value); set_gdbarch_skip_prologue (gdbarch, ft32_skip_prologue); set_gdbarch_inner_than (gdbarch, core_addr_lessthan); set_gdbarch_breakpoint_from_pc (gdbarch, ft32_breakpoint_from_pc); set_gdbarch_frame_align (gdbarch, ft32_frame_align); frame_base_set_default (gdbarch, &ft32_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, ft32_dummy_id); set_gdbarch_unwind_pc (gdbarch, ft32_unwind_pc); set_gdbarch_print_insn (gdbarch, print_insn_ft32); /* 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, &ft32_frame_unwind); /* Support simple overlay manager. */ set_gdbarch_overlay_update (gdbarch, simple_overlay_update); return gdbarch; } /* Register this machine's init routine. */ void _initialize_ft32_tdep (void) { register_gdbarch_init (bfd_arch_ft32, ft32_gdbarch_init); }