/* Target-dependent code for Ubicom IP2K, for GDB. Copyright (C) 2000, 2001, 2002 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 "frame.h" #include "frame-unwind.h" #include "frame-base.h" #include "trad-frame.h" #include "gdbcore.h" #include "inferior.h" #include "symfile.h" #include "arch-utils.h" #include "regcache.h" #include "gdb_string.h" /* Threads. */ #define IP2K_ISR_THREAD 1 #define IP2K_MAIN_THREAD 2 #define IP2K_VM_THREAD 3 /* Registers. */ #define IP2K_NUM_NATIVE_REGS 256 #define IP2K_NUM_PSEUDO_REGS 5 #define IP2K_IPH_REGNUM 0x0004 #define IP2K_IPL_REGNUM 0x0005 #define IP2K_SPH_REGNUM 0x0006 #define IP2K_SPL_REGNUM 0x0007 #define IP2K_PCH_REGNUM 0x0008 #define IP2K_PCL_REGNUM 0x0009 #define IP2K_DPH_REGNUM 0x000C #define IP2K_DPL_REGNUM 0x000D #define IP2K_CALLH_REGNUM 0x007E #define IP2K_CALLL_REGNUM 0x007F #define IP2K_RETVAL_REGNUM 0x0080 #define IP2K_PC_REGNUM (IP2K_NUM_NATIVE_REGS + 0) #define IP2K_SP_REGNUM (IP2K_NUM_NATIVE_REGS + 1) #define IP2K_DP_REGNUM (IP2K_NUM_NATIVE_REGS + 2) #define IP2K_IP_REGNUM (IP2K_NUM_NATIVE_REGS + 3) #define IP2K_CALL_REGNUM (IP2K_NUM_NATIVE_REGS + 4) char *ip2k_register_names[] = { " ", " ", "addrsel", "addrx", "iph", "ipl", "sph", "spl", "pch", "pcl", "wreg", "status", "dph", "dpl", "spdreg", "mulh", "addrh", "addrl", "datah", "datal", "intvech", "intvecl", "intspd", "intf", "inte", "inted", "fcfg", "tctrl", "xcfg", "emcfg", "ipch", "ipcl", "rain", "raout", "radir", "lfsrh", "rbin", "rbout", "rbdir", "lfsrl", "rcin", "rcout", "rcdir", "lfsra", "rdin", "rdout", "rddir", " ", "rein", "reout", "redir", " ", "rfin", "rfout", "rfdir", " ", " ", "rgout", "rgdir", " ", " ", " ", " ", " ", "rttmr", "rtcfg", "t0tmr", "t0cfg", "t1cnth", "t1cntl", "t1cap1h", "t1cap1l", "t1cap2h", "t1cap2l", "t1cmp1h", "t1cmp1l", "t1cfg1h", "t1cfg1l", "t1cfg2h", "t1cfg2l", "adch", "adcl", "adccfg", "adctmr", "t2cnth", "t2cntl", "t2cap1h", "t2cap1l", "t2cap2h", "t2cap2l", "t2cmp1h", "t2cmp1l", "t2cfg1h", "t2cfg1l", "t2cfg2h", "t2cfg2l", "s1tmrh", "s1tmrl", "s1tbufh", "s1tbufl", "s1tcfg", "s1rcnt", "s1rbufh", "s1rbufl", "s1rcfg", "s1rsync", "s1intf", "s1inte", "s1mode", "s1smask", "pspcfg", "cmpcfg", "s2tmrh", "s2tmrl", "s2tbufh", "s2tbufl", "s2tcfg", "s2rcnt", "s2rbufh", "s2rbufl", "s2rcfg", "s2rsync", "s2intf", "s2inte", "s2mode", "s2smask", "callh", "calll", "r80", "r81", "r82", "r83", "r84", "r85", "r86", "r87", "r88", "r89", "r8A", "r8B", "r8C", "r8D", "r8E", "r8F", "r90", "r91", "r92", "r93", "r94", "r95", "r96", "r97", "r98", "r99", "r9A", "r9B", "r9C", "r9D", "r9E", "r9F", "rA0", "rA1", "rA2", "rA3", "rA4", "rA5", "rA6", "rA7", "rA8", "rA9", "rAA", "rAB", "rAC", "rAD", "rAE", "rAF", "rB0", "rB1", "rB2", "rB3", "rB4", "rB5", "rB6", "rB7", "rB8", "rB9", "rBA", "rBB", "rBC", "rBD", "rBE", "rBF", "rC0", "rC1", "rC2", "rC3", "rC4", "rC5", "rC6", "rC7", "rC8", "rC9", "rCA", "rCB", "rCC", "rCD", "rCE", "rCF", "rD0", "rD1", "rD2", "rD3", "rD4", "rD5", "rD6", "rD7", "rD8", "rD9", "rDA", "rDB", "rDC", "rDD", "rDE", "rDF", "rE0", "rE1", "rE2", "rE3", "rE4", "rE5", "rE6", "rE7", "rE8", "rE9", "rEA", "rEB", "rEC", "rED", "rEE", "rEF", "rF0", "rF1", "rF2", "rF3", "rF4", "rF5", "rF6", "rF7", "rF8", "rF9", "rFA", "rFB", "rFC", "rFD", "rFE", "rFF", "pc", "sp", "dp", "ip", "call" }; enum ip2k_func_type { ip2k_func_type_normal = 0, ip2k_func_type_libgcc_stub, ip2k_func_type_libgcc_epilogue, ip2k_func_type_libgcc_indcall }; /* Debug. */ #define DEBUG_NONE 0 #define DEBUG_NORMAL 1 #define DEBUG_EXTRA 2 #define DEBUG_MAX 3 #define DEBUG_FRAME DEBUG_MAX static int debug_level = DEBUG_NORMAL; /* Frame info. */ struct ip2k_unwind_cache { CORE_ADDR entry_pc; CORE_ADDR current_pc; CORE_ADDR return_pc; CORE_ADDR entry_sp; CORE_ADDR normal_sp; CORE_ADDR current_sp; /* Table indicating the location of each and every register. */ struct trad_frame_saved_reg *saved_regs; }; /* Prologue instructions - return address. push CALLL (0x447f) push CALLH (0x447e) */ static unsigned char ip2k_call_prologue_insns[] = { 0x44, 0x7f, 0x44, 0x7e }; /* Prologue instructions - frame pointer. push $fe (0x44fe) mov W,SPL (0x2007) mov $fe,W (0x02fe) mov W,SPH (0x2006) push $fd (0x44fd) mov $fd,W (0x02fd) */ static unsigned char ip2k_fp_prologue_insns[] = { 0x44, 0xfe, 0x20, 0x07, 0x02, 0xfe, 0x20, 0x06, 0x44, 0xfd, 0x02, 0xfd }; /* Prologue instructions - local variables (single). dec spl (0x0e07) */ static unsigned char ip2k_local_1_prologue_insns[] = { 0x0e, 0x07 }; /* Prologue - local variables (multiple). mov W,#$xx (0x7cxx) sub SPL,W (0x0a07) */ static unsigned char ip2k_local_n_prologue_insns[] = { 0x7c, 0x00, 0x0a, 0x07 }; /* ip2k_try_pc_function_start. */ static CORE_ADDR ip2k_try_pc_function_start (CORE_ADDR pc) { CORE_ADDR temp = get_pc_function_start (pc); if (temp != 0) return temp; printf_filtered ("warning: unable to find start of function of pc: 0x%08lx\n", pc); return pc; } /* ip2k_dptr_to_addr. */ static CORE_ADDR ip2k_dptr_to_addr (CORE_ADDR addr) { if (addr == 0) return 0; if (addr & 0xFFFF0000) { warning ("invalid raw data pointer: 0x%08lx", addr); return 0; } if (ptid_get_pid (inferior_ptid) == IP2K_VM_THREAD) return (0x01400000 | (addr & 0x0000FFFF)); else return (0x01000000 | (addr & 0x0000FFFF)); } /* ip2k_iptr_to_addr. */ static CORE_ADDR ip2k_iptr_to_addr (CORE_ADDR addr) { /* Note: 0 is a valid address! */ if (addr & 0xFFFF0000) { warning ("invalid raw instruction pointer: 0x%08lx", addr); return 0; } if (ptid_get_pid (inferior_ptid) == IP2K_VM_THREAD) return (0x02400000 | ((addr & 0x0000FFFF) << 1)); else return (0x02000000 | ((addr & 0x0000FFFF) << 1)); } /* ip2k_addr_to_dptr. */ static CORE_ADDR ip2k_addr_to_dptr (CORE_ADDR addr) { if (addr == 0) return 0; if ((addr & 0xFF000000) != 0x01000000) { warning ("invalid data pointer: 0x%08lx", addr); return 0; } return (addr & 0x0000FFFF); } /* ip2k_addr_to_iptr. */ static CORE_ADDR ip2k_addr_to_iptr (CORE_ADDR addr) { if (addr == 0) return 0; if ((addr & 0xFF000000) != 0x02000000) { warning ("invalid instruction pointer: 0x%08lx", addr); return 0; } return ((addr >> 1) & 0x0000FFFF); } /* ip2k_pointer_to_address. */ static CORE_ADDR ip2k_pointer_to_address (struct type *type, const void *buf) { enum type_code target = TYPE_CODE (TYPE_TARGET_TYPE (type)); CORE_ADDR addr = extract_unsigned_integer (buf, TYPE_LENGTH (type)); if (debug_level >= DEBUG_MAX) printf_filtered ("ip2k_pointer_to_address\n"); if ((target == TYPE_CODE_FUNC) || (target == TYPE_CODE_METHOD)) addr = ip2k_iptr_to_addr (addr); else addr = ip2k_dptr_to_addr (addr); return addr; } /* ip2k_address_to_pointer. */ static void ip2k_address_to_pointer (struct type *type, void *buf, CORE_ADDR addr) { enum type_code target = TYPE_CODE (TYPE_TARGET_TYPE (type)); if (debug_level >= DEBUG_MAX) printf_filtered ("ip2k_address_to_pointer\n"); if ((target == TYPE_CODE_FUNC) || (target == TYPE_CODE_METHOD)) addr = ip2k_addr_to_iptr (addr); else addr = ip2k_addr_to_dptr (addr); store_unsigned_integer (buf, TYPE_LENGTH (type), addr); } /* ip2k_read_pc. */ static CORE_ADDR ip2k_read_pc (ptid_t ptid) { ptid_t save_ptid; CORE_ADDR val; save_ptid = inferior_ptid; inferior_ptid = ptid; val = read_register (IP2K_PC_REGNUM); inferior_ptid = save_ptid; if (debug_level >= DEBUG_MAX) printf_filtered ("ip2k_read_pc (0x%08lx)\n", ip2k_iptr_to_addr (val)); return ip2k_iptr_to_addr (val); } /* ip2k_write_pc. */ static void ip2k_write_pc (CORE_ADDR val, ptid_t ptid) { ptid_t save_ptid; if (debug_level >= DEBUG_MAX) printf_filtered ("ip2k_write_pc\n"); save_ptid = inferior_ptid; inferior_ptid = ptid; write_register (IP2K_PC_REGNUM, ip2k_addr_to_iptr (val)); inferior_ptid = save_ptid; } /* ip2k_unwind_pc. */ static CORE_ADDR ip2k_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame) { ULONGEST pc; frame_unwind_unsigned_register (next_frame, IP2K_PC_REGNUM, &pc); return ip2k_iptr_to_addr (pc); } /* ip2k_unwind_sp. */ static CORE_ADDR ip2k_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame) { ULONGEST sp; frame_unwind_unsigned_register (next_frame, IP2K_SP_REGNUM, &sp); return ip2k_dptr_to_addr (sp); } /* ip2k_register_name. Given a register index, return the name of the corresponding register. */ static const char * ip2k_register_name (int regnum) { if ((regnum < 0) || (regnum >= IP2K_NUM_NATIVE_REGS + IP2K_NUM_PSEUDO_REGS)) { warning ("internal error: ip2k_register_name: invalid register: 0x%08x", regnum); return (NULL); } return (ip2k_register_names[regnum]); } /* Return the GDB type object for the "standard" data type of data in register N. */ static struct type * ip2k_register_type (struct gdbarch *gdbarch, int reg_nr) { switch (reg_nr) { case IP2K_SP_REGNUM: case IP2K_DP_REGNUM: case IP2K_IP_REGNUM: return builtin_type_void_data_ptr; case IP2K_PC_REGNUM: case IP2K_CALL_REGNUM: return builtin_type_void_func_ptr; default: return builtin_type_uint8; } } /* ip2k_get_function_type_internal. */ static inline enum ip2k_func_type ip2k_get_function_type_internal (char *name) { if (!strstr (name, "libgcc")) return ip2k_func_type_normal; if (strstr (name, "indcall")) return ip2k_func_type_libgcc_indcall; if (strstr (name, "add_sp")) return ip2k_func_type_libgcc_epilogue; return ip2k_func_type_libgcc_stub; } /* ip2k_get_function_type. */ static enum ip2k_func_type ip2k_get_function_type (CORE_ADDR pc, char *name) { enum ip2k_func_type type; if (!name) find_pc_partial_function (pc, &name, NULL, NULL); if (name) type = ip2k_get_function_type_internal(name); else type = ip2k_func_type_normal; if (debug_level >= DEBUG_FRAME) { char *typestr; switch (type) { case ip2k_func_type_normal: typestr = "normal"; break; case ip2k_func_type_libgcc_stub: typestr = "libgcc stub"; break; case ip2k_func_type_libgcc_epilogue: typestr = "libgcc epilogue"; break; case ip2k_func_type_libgcc_indcall: typestr = "libgcc indcall"; break; default: typestr = "internal error!"; break; } printf_filtered("ip2k_get_function_type: %s (%s)\n", typestr, name); } return type; } /* ip2k_pseudo_register_read. */ static void ip2k_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache, int regnum, void *buf) { unsigned char *data = (unsigned char *) buf; if (debug_level >= DEBUG_MAX) printf_filtered ("ip2k_pseudo_register_read: %04x\n", regnum); switch (regnum) { case IP2K_PC_REGNUM: regcache_raw_read (regcache, IP2K_PCH_REGNUM, data++); regcache_raw_read (regcache, IP2K_PCL_REGNUM, data++); break; case IP2K_SP_REGNUM: regcache_raw_read (regcache, IP2K_SPH_REGNUM, data++); regcache_raw_read (regcache, IP2K_SPL_REGNUM, data++); break; case IP2K_DP_REGNUM: regcache_raw_read (regcache, IP2K_DPH_REGNUM, data++); regcache_raw_read (regcache, IP2K_DPL_REGNUM, data++); break; case IP2K_IP_REGNUM: regcache_raw_read (regcache, IP2K_IPH_REGNUM, data++); regcache_raw_read (regcache, IP2K_IPL_REGNUM, data++); break; case IP2K_CALL_REGNUM: regcache_raw_read (regcache, IP2K_CALLH_REGNUM, data++); regcache_raw_read (regcache, IP2K_CALLL_REGNUM, data++); break; default: warning ("internal error: ip2k_pseudo_register_read: invalid register: 0x%08x", regnum); } } /* ip2k_pseudo_register_write. */ static void ip2k_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache, int regnum, const void *buf) { unsigned char *data = (unsigned char *) buf; if (debug_level >= DEBUG_MAX) printf_filtered ("ip2k_pseudo_register_write\n"); switch (regnum) { case IP2K_PC_REGNUM: regcache_raw_write (regcache, IP2K_PCH_REGNUM, data++); regcache_raw_write (regcache, IP2K_PCL_REGNUM, data++); break; case IP2K_SP_REGNUM: regcache_raw_write (regcache, IP2K_SPH_REGNUM, data++); regcache_raw_write (regcache, IP2K_SPL_REGNUM, data++); break; case IP2K_DP_REGNUM: regcache_raw_write (regcache, IP2K_DPH_REGNUM, data++); regcache_raw_write (regcache, IP2K_DPL_REGNUM, data++); break; case IP2K_IP_REGNUM: regcache_raw_write (regcache, IP2K_IPH_REGNUM, data++); regcache_raw_write (regcache, IP2K_IPL_REGNUM, data++); break; case IP2K_CALL_REGNUM: regcache_raw_write (regcache, IP2K_CALLH_REGNUM, data++); regcache_raw_write (regcache, IP2K_CALLL_REGNUM, data++); break; default: warning ("internal error: ip2k_pseudo_register_write: invalid register: 0x%08x", regnum); } } /* ip2k_extract_return_value. Extract from an array REGBUF containing the (raw) register state a function return value of type TYPE, and copy that, in virtual format, into VALBUF. */ static void ip2k_extract_return_value (struct type *type, struct regcache *regcache, void *valbuf) { int type_len, i; if (debug_level >= DEBUG_MAX) printf_filtered ("ip2k_extract_return_value\n"); type_len = TYPE_LENGTH (type); if (type_len > 8) { printf_unfiltered ("Error: Return values longer than 8 bytes are not supported.\n"); memset (valbuf, 0, type_len); return; } if (type_len == 1) regcache_raw_read (regcache, IP2K_RETVAL_REGNUM + 1, valbuf); else { for (i = 0; i < type_len; i++) { regcache_raw_read (regcache, IP2K_RETVAL_REGNUM + i, ((char *) valbuf)++); } } } /* ip2k_store_return_value. Write into appropriate registers a function return value of type TYPE, given in virtual format. */ static void ip2k_store_return_value (struct type *type, struct regcache *regcache, const void *valbuf) { int type_len, i; if (debug_level >= DEBUG_MAX) printf_filtered ("ip2k_store_return_value\n"); type_len = TYPE_LENGTH (type); if (type_len > 8) { printf_unfiltered ("Error: Return values longer than 8 bytes are not supported.\n"); return; } if (type_len == 1) { unsigned char zero = 0; regcache_raw_write (regcache, IP2K_RETVAL_REGNUM + 0, &zero); regcache_raw_write (regcache, IP2K_RETVAL_REGNUM + 1, valbuf); } else { for (i = 0; i < type_len; i++) { regcache_raw_write (regcache, IP2K_RETVAL_REGNUM + i, ((char *) valbuf)++); } } } /* ip2k_use_struct_convention Return 1 if we should we use EXTRACT_STRUCT_VALUE_ADDRESS to extract the return value. Return 0 if we should use EXTRACT_RETURN_VALUE. GCC_P is true if compiled with GCC and TYPE is the type (which is known to be a struct, union, or array). For the ip2k, gcc is the only compiler and anything 8 bytes or smaller in size is returned in registers. */ static int ip2k_use_struct_convention (int gcc_p, struct type *type) { if (debug_level >= DEBUG_MAX) printf_filtered ("ip2k_use_struct_convention\n"); return (TYPE_LENGTH (type) > 8); } /* ip2k_extract_struct_value_address Extract from an array REGBUF containing the (raw) register state the address in which a function should return its structure value, as a CORE_ADDR (or an expression that can be used as one). */ static CORE_ADDR ip2k_extract_struct_value_address (struct regcache *regcache) { CORE_ADDR sp; if (debug_level >= DEBUG_MAX) printf_filtered ("ip2k_extract_struct_value_address\n"); /* The called function has returned... And the place to store the structure return value was stored on the stack as a "hidden" first argument to the function called... All stack adjustments made by the called function should have been undone already, and the first argument should therefore be located at sp+1,sp+2. */ sp = ip2k_dptr_to_addr (read_register (IP2K_SP_REGNUM)); return ip2k_dptr_to_addr (read_memory_unsigned_integer (sp + 1, 2)); } /* ip2k_breakpoint_from_pc. */ static const unsigned char * ip2k_breakpoint_from_pc (CORE_ADDR * pc, int *len) { static unsigned char break_insn[] = { 0x00, 0x01 }; static unsigned char breakx_insn[] = { 0x00, 0x05 }; unsigned short insn; if (debug_level >= DEBUG_MAX) printf_filtered ("ip2k_breakpoint_from_pc\n"); insn = read_memory_unsigned_integer (*pc, 2); if (((insn & 0xFF00) == 0x7000) || ((insn & 0xFF00) == 0x7100) || ((insn & 0xFFF8) == 0x0010)) { *len = sizeof (breakx_insn); return breakx_insn; } else { *len = sizeof (break_insn); return break_insn; } } /* ip2k_ignore_helper. Return non-zero if the PC is in a library helper function that should be ignored. This implements the IGNORE_HELPER_CALL macro. */ int ip2k_ignore_helper (CORE_ADDR pc) { enum ip2k_func_type type; if (debug_level >= DEBUG_FRAME) printf_filtered ("ip2k_ignore_helper\n"); type = ip2k_get_function_type (pc, 0); switch (type) { case ip2k_func_type_libgcc_stub: case ip2k_func_type_libgcc_epilogue: return 1; case ip2k_func_type_normal: case ip2k_func_type_libgcc_indcall: default: return 0; } } /* ip2k_in_call_trampoline. */ static int ip2k_in_call_trampoline (CORE_ADDR pc, char *name) { if (debug_level >= DEBUG_FRAME) printf_filtered ("ip2k_in_call_trampoline\n"); if (ip2k_get_function_type (pc, name) == ip2k_func_type_libgcc_indcall) return 1; return 0; } /* ip2k_in_return_trampoline. */ static int ip2k_in_return_trampoline (CORE_ADDR pc, char *name) { if (debug_level >= DEBUG_FRAME) printf_filtered ("ip2k_in_return_trampoline\n"); if (ip2k_get_function_type (pc, name) == ip2k_func_type_libgcc_epilogue) return 1; return 0; } /* ip2k_skip_trampoline. */ static CORE_ADDR ip2k_skip_trampoline (CORE_ADDR pc) { enum ip2k_func_type type; unsigned short insn; if (debug_level >= DEBUG_FRAME) printf_filtered ("ip2k_skip_trampoline\n"); type = ip2k_get_function_type (pc, 0); if ((type != ip2k_func_type_libgcc_indcall) && (type != ip2k_func_type_libgcc_epilogue)) return 0; /* If we are at a ret instruction then the destination address can be found in CALLH/CALLL. */ insn = read_memory_unsigned_integer (pc, 2); if (insn == 0x0007) return ip2k_iptr_to_addr (read_register (IP2K_CALL_REGNUM)); /* We are not at a ret instuction so CALLH/CALLL may not be valid... search for the ret instruction and lie and say that the pc of the ret instruction is the way out. When it stops again it will recheck and discover that we are stil in the trampoline but by then CALLH/CALLL will be valid. */ while (1) { pc += 2; insn = read_memory_unsigned_integer (pc, 2); if (insn == 0x0007) /* Return pc of ret instruction */ return pc; } } /* ip2k_skip_prologue. Analyze instructions to find the end of the prologue, return the pc of the end of the prologue */ static CORE_ADDR ip2k_skip_prologue (CORE_ADDR pc) { unsigned char buf[20]; unsigned char *pos = buf; CORE_ADDR start_pc; int length; if (debug_level >= DEBUG_MAX) printf_filtered ("ip2k_skip_prologue\n"); /* Find entry_pc. */ start_pc = ip2k_try_pc_function_start (pc); /* Read prologue. */ if (target_read_memory (start_pc, buf, sizeof (buf)) != 0) memset (buf, 0, sizeof (buf)); /* push CALLH/L will always be first (if present). */ if (memcmp (buf, ip2k_call_prologue_insns, sizeof (ip2k_call_prologue_insns)) == 0) { start_pc += sizeof (ip2k_call_prologue_insns); pos += sizeof (ip2k_call_prologue_insns); } /* FP code will be next (if present). */ if (memcmp (buf, ip2k_fp_prologue_insns, sizeof (ip2k_fp_prologue_insns)) == 0) { start_pc += sizeof (ip2k_fp_prologue_insns); pos += sizeof (ip2k_fp_prologue_insns); } /* Local variable code will be next (if present). */ if (memcmp (buf, ip2k_local_1_prologue_insns, sizeof (ip2k_local_1_prologue_insns)) == 0) { start_pc += sizeof (ip2k_local_1_prologue_insns); pos += sizeof (ip2k_local_1_prologue_insns); } buf[1] = 0x00; if (memcmp (buf, ip2k_local_n_prologue_insns, sizeof (ip2k_local_n_prologue_insns)) == 0) { start_pc += sizeof (ip2k_local_n_prologue_insns); pos += sizeof (ip2k_local_n_prologue_insns); } return start_pc; } /* ip2k_frame_unwind_cache. */ struct ip2k_unwind_cache * ip2k_frame_unwind_cache (struct frame_info *next_frame, void **this_prologue_cache) { struct ip2k_unwind_cache *info; CORE_ADDR return_pc, return_verify, temp_sp, addr; enum ip2k_func_type type; unsigned short insn; unsigned char buf[20]; unsigned char *pos = buf; int local_stack = 0; int executed_stack = 0; int ret_saved = 0; int position, temp; if ((*this_prologue_cache)) return (*this_prologue_cache); if (debug_level >= DEBUG_FRAME) printf_filtered ("ip2k_frame_unwind_cache:\n"); info = FRAME_OBSTACK_ZALLOC (struct ip2k_unwind_cache); (*this_prologue_cache) = info; info->saved_regs = trad_frame_alloc_saved_regs (next_frame); /* Starting point. */ info->current_pc = frame_pc_unwind (next_frame); info->entry_pc = frame_func_unwind (next_frame); info->return_pc = 0; info->entry_sp = 0; info->normal_sp = 0; info->current_sp = frame_sp_unwind (next_frame); type = ip2k_get_function_type (info->current_pc, 0); /* Read prologue. */ position = (info->current_pc - info->entry_pc) / 2; if (target_read_memory (info->entry_pc, buf, sizeof (buf)) != 0) memset (buf, 0, sizeof (buf)); /* push CALLL (0x447f) push CALLH (0x447e) */ if (memcmp (pos, ip2k_call_prologue_insns, sizeof (ip2k_call_prologue_insns)) == 0) { ret_saved = 1; local_stack += 2; if (position >= 2) executed_stack += 2; else if (position >= 1) executed_stack += 1; pos += sizeof (ip2k_call_prologue_insns); position -= sizeof (ip2k_call_prologue_insns) / 2; } /* push $fe (0x44fe) mov W,SPL (0x2007) mov $fe,W (0x02fe) mov W,SPH (0x2006) push $fd (0x44fd) mov $fd,W (0x02fd) */ if (memcmp (pos, ip2k_fp_prologue_insns, sizeof (ip2k_fp_prologue_insns)) == 0) { local_stack += 2; if (position >= 5) executed_stack += 2; else if (position >= 1) executed_stack += 1; pos += sizeof (ip2k_fp_prologue_insns); position -= sizeof (ip2k_fp_prologue_insns) / 2; } /* mov W,#$xx (0x7cxx) sub SPL,W (0x0a07) */ temp = pos[1]; pos[1] = 0x00; if (memcmp (pos, ip2k_local_n_prologue_insns, sizeof (ip2k_local_n_prologue_insns)) == 0) { local_stack += temp; if (position >= 2) executed_stack += temp; pos += sizeof (ip2k_local_n_prologue_insns); position -= sizeof (ip2k_local_n_prologue_insns) / 2; } /* Special handling if we are in the middle of the prologue. */ if (position < 0) { info->return_pc = ip2k_iptr_to_addr (read_register (IP2K_CALL_REGNUM)); info->entry_sp = info->current_sp + executed_stack; info->normal_sp = info->entry_sp - local_stack; goto finish; } /* Apply stack estimate. */ info->normal_sp = info->current_sp; info->entry_sp = info->current_sp + local_stack; /* Impossible to search for adjust if this is a leaf function. */ if ((ret_saved == 0) && (type != ip2k_func_type_libgcc_epilogue)) { info->return_pc = ip2k_iptr_to_addr (read_register (IP2K_CALL_REGNUM)); goto finish; } /* Check to see if we are in a critical part of an epilogue. */ insn = read_memory_unsigned_integer (info->current_pc, 2); if (insn == 0x0007) { printf_filtered ("\tepilogue (ret)\n"); info->return_pc = ip2k_iptr_to_addr (read_register (IP2K_CALL_REGNUM)); if (debug_level >= DEBUG_FRAME) goto finish; } if (insn == 0x467f) { printf_filtered ("\tepilogue (pop calll)\n"); if (target_read_memory (info->current_sp + 1, buf, 1) != 0) { if (debug_level >= DEBUG_FRAME) printf_filtered ("\tnot found on stack (invalid sp in epilogue)!\n"); goto finish; } info->return_pc = ip2k_iptr_to_addr( (read_register (IP2K_CALL_REGNUM) & 0x0000FF00) | (buf[0] & 0x0FF)); info->entry_sp++; goto finish; } /* Search for frame on stack. Return address should be at entry_sp-2 (+1 for actual read as 1(SP) points to stack). */ temp_sp = info->entry_sp - 2; while (1) { /* Limit back search to 128 bytes. */ if (temp_sp > info->entry_sp + 128) { if (debug_level >= DEBUG_FRAME) printf_filtered ("\tnot found on stack (too far)!\n"); break; } /* Read memory the manual way so that we can handle it nicely when it fails. */ if (target_read_memory (temp_sp + 1, buf, 2) != 0) /* reached/exceeded stack memory. */ { if (debug_level >= DEBUG_FRAME) printf_filtered ("\tnot found on stack (mem err)!\n"); break; } /* Increment temp_sp now so that continues can work. */ temp_sp += 1; /* Process potential return pc value from stack. */ return_pc = extract_unsigned_integer (buf, 2); if (return_pc < 4) continue; return_pc = ip2k_iptr_to_addr (return_pc); if (target_read_memory (return_pc - 4, buf, 4) != 0) continue; /* Verify instruction at return_pc is a call instruction. */ insn = extract_unsigned_integer (buf + 2, 2); if ((insn & 0xE000) != 0xC000) continue; /* Found a possible call instruction. */ return_verify = insn & 0x1FFF; insn = extract_unsigned_integer (buf + 0, 2); if ((insn & 0xFFF8) == 0x0010) return_verify |= (insn << 13) & 0x0E000; else return_verify |= ip2k_addr_to_iptr(return_pc) & 0x0E000; return_verify = ip2k_iptr_to_addr(return_verify); /* Verify that this is the expected call instruction. */ if ((return_verify != info->entry_pc) && (type != ip2k_func_type_libgcc_epilogue) && (ip2k_get_function_type (return_verify, 0) != ip2k_func_type_libgcc_indcall)) continue; /* Found! */ info->return_pc = return_pc; /* Apply stack adjust. */ temp_sp += 2 - 1; if (temp_sp != info->entry_sp) { if (debug_level >= DEBUG_FRAME) printf_filtered ("\tcorrection of %ld applied\n", temp_sp - info->entry_sp); info->normal_sp += (temp_sp - info->entry_sp); info->entry_sp = temp_sp; } else { if (debug_level >= DEBUG_FRAME) printf_filtered ("\tno correction needed\n"); } break; } finish: if (info->return_pc == 0) info->entry_sp = 0; addr = ip2k_addr_to_dptr(info->entry_sp); trad_frame_register_value (info->saved_regs, IP2K_SP_REGNUM, addr); trad_frame_register_value (info->saved_regs, IP2K_SPH_REGNUM, addr >> 8); trad_frame_register_value (info->saved_regs, IP2K_SPL_REGNUM, addr & 0x0FF); addr = ip2k_addr_to_iptr(info->return_pc); trad_frame_register_value (info->saved_regs, IP2K_PC_REGNUM, addr); trad_frame_register_value (info->saved_regs, IP2K_PCH_REGNUM, addr >> 8); trad_frame_register_value (info->saved_regs, IP2K_PCL_REGNUM, addr & 0x0FF); if (debug_level >= DEBUG_FRAME) { printf_filtered ("\tentry_pc = 0x%08lx\n", info->entry_pc); printf_filtered ("\tcurrent_pc = 0x%08lx\n", info->current_pc); printf_filtered ("\treturn_pc = 0x%08lx\n", info->return_pc); printf_filtered ("\tentry_sp = 0x%08lx\n", info->entry_sp); printf_filtered ("\tnormal_sp = 0x%08lx\n", info->normal_sp); printf_filtered ("\tcurrent_sp = 0x%08lx\n", info->current_sp); } return info; } /* Assuming NEXT_FRAME->prev is a dummy, return the frame ID of that dummy frame. The frame ID's base needs to match the TOS value saved by save_dummy_frame_tos(), and the PC match the dummy frame's breakpoint. */ static struct frame_id ip2k_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame) { ULONGEST base; if (debug_level >= DEBUG_FRAME) printf_filtered ("ip2k_unwind_dummy_id\n"); frame_unwind_unsigned_register (next_frame, IP2K_SP_REGNUM, &base); return frame_id_build (ip2k_dptr_to_addr(base), frame_pc_unwind (next_frame)); } /* 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 ip2k_frame_this_id (struct frame_info *next_frame, void **this_prologue_cache, struct frame_id *this_id) { struct ip2k_unwind_cache *info; CORE_ADDR base; CORE_ADDR func; struct frame_id id; if (debug_level >= DEBUG_FRAME) printf_filtered ("ip2k_frame_this_id\n"); info = ip2k_frame_unwind_cache (next_frame, this_prologue_cache); /* The FUNC is easy. */ func = frame_func_unwind (next_frame); /* This is meant to halt the backtrace at "_start". Make sure we don't halt it at a generic dummy frame. */ if (inside_entry_file (func)) return; base = info->normal_sp; if (base == 0) return; id = frame_id_build (base, func); /* Check that we're not going round in circles with the same frame ID (but avoid applying the test to sentinel frames which do go round in circles). Can't use frame_id_eq() as that doesn't yet compare the frame's PC value. */ if (frame_relative_level (next_frame) >= 0 && get_frame_type (next_frame) != DUMMY_FRAME && frame_id_eq (get_frame_id (next_frame), id)) { if (debug_level >= DEBUG_FRAME) printf_filtered ("ip2k_frame_this_id: circular\n"); return; } (*this_id) = id; } static void ip2k_frame_prev_register (struct frame_info *next_frame, void **this_prologue_cache, int regnum, int *optimizedp, enum lval_type *lvalp, CORE_ADDR *addrp, int *realnump, void *bufferp) { struct ip2k_unwind_cache *info = ip2k_frame_unwind_cache (next_frame, this_prologue_cache); trad_frame_prev_register (next_frame, info->saved_regs, regnum, optimizedp, lvalp, addrp, realnump, bufferp); } static CORE_ADDR ip2k_frame_base_address (struct frame_info *next_frame, void **this_cache) { struct ip2k_unwind_cache *info; if (debug_level >= DEBUG_FRAME) printf_filtered ("ip2k_frame_base_address\n"); info = ip2k_frame_unwind_cache (next_frame, this_cache); /* Note that gcc provides all debug information relative to the post-locals SP, not the entry_sp! */ return info->normal_sp; } static const struct frame_unwind ip2k_frame_unwind = { NORMAL_FRAME, ip2k_frame_this_id, ip2k_frame_prev_register }; static const struct frame_base ip2k_frame_base = { &ip2k_frame_unwind, ip2k_frame_base_address, ip2k_frame_base_address, ip2k_frame_base_address }; static const struct frame_unwind * ip2k_frame_p (CORE_ADDR pc) { return &ip2k_frame_unwind; } /* ip2k_gdbarch_init. */ static struct gdbarch * ip2k_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) { struct gdbarch *gdbarch; /* Find a candidate among the list of pre-declared architectures. */ arches = gdbarch_list_lookup_by_info (arches, &info); if (arches != NULL) return (arches->gdbarch); /* No architecture found, create one. */ gdbarch = gdbarch_alloc (&info, 0); /* Integer types. */ set_gdbarch_short_bit (gdbarch, 2 * TARGET_CHAR_BIT); set_gdbarch_int_bit (gdbarch, 2 * TARGET_CHAR_BIT); set_gdbarch_long_bit (gdbarch, 4 * TARGET_CHAR_BIT); set_gdbarch_long_long_bit (gdbarch, 8 * TARGET_CHAR_BIT); /* Floating point types (not supported). */ set_gdbarch_float_bit (gdbarch, 4 * TARGET_CHAR_BIT); set_gdbarch_double_bit (gdbarch, 4 * TARGET_CHAR_BIT); set_gdbarch_long_double_bit (gdbarch, 4 * TARGET_CHAR_BIT); set_gdbarch_float_format (gdbarch, &floatformat_ieee_single_little); set_gdbarch_double_format (gdbarch, &floatformat_ieee_single_little); set_gdbarch_long_double_format (gdbarch, &floatformat_ieee_single_little); /* Pointers types - pointers on target are 16-bits but addresses in GDB are 32-bits. */ set_gdbarch_ptr_bit (gdbarch, 2 * TARGET_CHAR_BIT); set_gdbarch_addr_bit (gdbarch, 32); set_gdbarch_address_to_pointer (gdbarch, ip2k_address_to_pointer); set_gdbarch_pointer_to_address (gdbarch, ip2k_pointer_to_address); /* Core register functions. */ set_gdbarch_read_pc (gdbarch, ip2k_read_pc); set_gdbarch_write_pc (gdbarch, ip2k_write_pc); set_gdbarch_unwind_pc (gdbarch, ip2k_unwind_pc); set_gdbarch_unwind_sp (gdbarch, ip2k_unwind_sp); /* General register functions. */ set_gdbarch_num_regs (gdbarch, IP2K_NUM_NATIVE_REGS); set_gdbarch_num_pseudo_regs (gdbarch, IP2K_NUM_PSEUDO_REGS); set_gdbarch_register_name (gdbarch, ip2k_register_name); set_gdbarch_register_type (gdbarch, ip2k_register_type); set_gdbarch_pseudo_register_read (gdbarch, ip2k_pseudo_register_read); set_gdbarch_pseudo_register_write (gdbarch, ip2k_pseudo_register_write); /* C data. */ set_gdbarch_extract_return_value (gdbarch, ip2k_extract_return_value); set_gdbarch_store_return_value (gdbarch, ip2k_store_return_value); set_gdbarch_use_struct_convention (gdbarch, ip2k_use_struct_convention); set_gdbarch_extract_struct_value_address (gdbarch, ip2k_extract_struct_value_address); /* Code. */ set_gdbarch_print_insn (gdbarch, print_insn_ip2k); set_gdbarch_call_dummy_address (gdbarch, entry_point_address); // set_gdbarch_push_dummy_call (gdbarch, ip2k_push_dummy_call); set_gdbarch_decr_pc_after_break (gdbarch, 0); set_gdbarch_function_start_offset (gdbarch, 0); set_gdbarch_breakpoint_from_pc (gdbarch, ip2k_breakpoint_from_pc); set_gdbarch_in_solib_call_trampoline (gdbarch, ip2k_in_call_trampoline); set_gdbarch_in_solib_return_trampoline (gdbarch, ip2k_in_return_trampoline); set_gdbarch_skip_trampoline_code (gdbarch, ip2k_skip_trampoline); set_gdbarch_skip_prologue (gdbarch, ip2k_skip_prologue); set_gdbarch_inner_than (gdbarch, core_addr_lessthan); set_gdbarch_frame_args_skip (gdbarch, 0); set_gdbarch_frameless_function_invocation (gdbarch, frameless_look_for_prologue); set_gdbarch_unwind_dummy_id (gdbarch, ip2k_unwind_dummy_id); frame_unwind_append_predicate (gdbarch, ip2k_frame_p); frame_base_set_default (gdbarch, &ip2k_frame_base); set_gdbarch_print_insn (gdbarch, print_insn_ip2k); /* Complete. */ return (gdbarch); } /* _initialize_ip2k_tdep. */ void _initialize_ip2k_tdep (void) { register_gdbarch_init (bfd_arch_ip2k, ip2k_gdbarch_init); }