| /* Target-dependent code for the MIPS architecture, for GDB, the GNU Debugger. |
| |
| Copyright (C) 1988-2016 Free Software Foundation, Inc. |
| |
| Contributed by Alessandro Forin(af@cs.cmu.edu) at CMU |
| and by Per Bothner(bothner@cs.wisc.edu) at U.Wisconsin. |
| |
| 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 <http://www.gnu.org/licenses/>. */ |
| |
| #include "defs.h" |
| #include "frame.h" |
| #include "inferior.h" |
| #include "symtab.h" |
| #include "value.h" |
| #include "gdbcmd.h" |
| #include "language.h" |
| #include "gdbcore.h" |
| #include "symfile.h" |
| #include "objfiles.h" |
| #include "gdbtypes.h" |
| #include "target.h" |
| #include "arch-utils.h" |
| #include "regcache.h" |
| #include "osabi.h" |
| #include "mips-tdep.h" |
| #include "block.h" |
| #include "reggroups.h" |
| #include "opcode/mips.h" |
| #include "elf/mips.h" |
| #include "elf-bfd.h" |
| #include "symcat.h" |
| #include "sim-regno.h" |
| #include "dis-asm.h" |
| #include "frame-unwind.h" |
| #include "frame-base.h" |
| #include "trad-frame.h" |
| #include "infcall.h" |
| #include "floatformat.h" |
| #include "remote.h" |
| #include "target-descriptions.h" |
| #include "dwarf2-frame.h" |
| #include "user-regs.h" |
| #include "valprint.h" |
| #include "ax.h" |
| |
| static const struct objfile_data *mips_pdr_data; |
| |
| static struct type *mips_register_type (struct gdbarch *gdbarch, int regnum); |
| |
| static int mips32_instruction_has_delay_slot (struct gdbarch *gdbarch, |
| ULONGEST inst); |
| static int micromips_instruction_has_delay_slot (ULONGEST insn, int mustbe32); |
| static int mips16_instruction_has_delay_slot (unsigned short inst, |
| int mustbe32); |
| |
| static int mips32_insn_at_pc_has_delay_slot (struct gdbarch *gdbarch, |
| CORE_ADDR addr); |
| static int micromips_insn_at_pc_has_delay_slot (struct gdbarch *gdbarch, |
| CORE_ADDR addr, int mustbe32); |
| static int mips16_insn_at_pc_has_delay_slot (struct gdbarch *gdbarch, |
| CORE_ADDR addr, int mustbe32); |
| |
| static void mips_print_float_info (struct gdbarch *, struct ui_file *, |
| struct frame_info *, const char *); |
| |
| /* A useful bit in the CP0 status register (MIPS_PS_REGNUM). */ |
| /* This bit is set if we are emulating 32-bit FPRs on a 64-bit chip. */ |
| #define ST0_FR (1 << 26) |
| |
| /* The sizes of floating point registers. */ |
| |
| enum |
| { |
| MIPS_FPU_SINGLE_REGSIZE = 4, |
| MIPS_FPU_DOUBLE_REGSIZE = 8 |
| }; |
| |
| enum |
| { |
| MIPS32_REGSIZE = 4, |
| MIPS64_REGSIZE = 8 |
| }; |
| |
| static const char *mips_abi_string; |
| |
| static const char *const mips_abi_strings[] = { |
| "auto", |
| "n32", |
| "o32", |
| "n64", |
| "o64", |
| "eabi32", |
| "eabi64", |
| NULL |
| }; |
| |
| /* For backwards compatibility we default to MIPS16. This flag is |
| overridden as soon as unambiguous ELF file flags tell us the |
| compressed ISA encoding used. */ |
| static const char mips_compression_mips16[] = "mips16"; |
| static const char mips_compression_micromips[] = "micromips"; |
| static const char *const mips_compression_strings[] = |
| { |
| mips_compression_mips16, |
| mips_compression_micromips, |
| NULL |
| }; |
| |
| static const char *mips_compression_string = mips_compression_mips16; |
| |
| /* The standard register names, and all the valid aliases for them. */ |
| struct register_alias |
| { |
| const char *name; |
| int regnum; |
| }; |
| |
| /* Aliases for o32 and most other ABIs. */ |
| const struct register_alias mips_o32_aliases[] = { |
| { "ta0", 12 }, |
| { "ta1", 13 }, |
| { "ta2", 14 }, |
| { "ta3", 15 } |
| }; |
| |
| /* Aliases for n32 and n64. */ |
| const struct register_alias mips_n32_n64_aliases[] = { |
| { "ta0", 8 }, |
| { "ta1", 9 }, |
| { "ta2", 10 }, |
| { "ta3", 11 } |
| }; |
| |
| /* Aliases for ABI-independent registers. */ |
| const struct register_alias mips_register_aliases[] = { |
| /* The architecture manuals specify these ABI-independent names for |
| the GPRs. */ |
| #define R(n) { "r" #n, n } |
| R(0), R(1), R(2), R(3), R(4), R(5), R(6), R(7), |
| R(8), R(9), R(10), R(11), R(12), R(13), R(14), R(15), |
| R(16), R(17), R(18), R(19), R(20), R(21), R(22), R(23), |
| R(24), R(25), R(26), R(27), R(28), R(29), R(30), R(31), |
| #undef R |
| |
| /* k0 and k1 are sometimes called these instead (for "kernel |
| temp"). */ |
| { "kt0", 26 }, |
| { "kt1", 27 }, |
| |
| /* This is the traditional GDB name for the CP0 status register. */ |
| { "sr", MIPS_PS_REGNUM }, |
| |
| /* This is the traditional GDB name for the CP0 BadVAddr register. */ |
| { "bad", MIPS_EMBED_BADVADDR_REGNUM }, |
| |
| /* This is the traditional GDB name for the FCSR. */ |
| { "fsr", MIPS_EMBED_FP0_REGNUM + 32 } |
| }; |
| |
| const struct register_alias mips_numeric_register_aliases[] = { |
| #define R(n) { #n, n } |
| R(0), R(1), R(2), R(3), R(4), R(5), R(6), R(7), |
| R(8), R(9), R(10), R(11), R(12), R(13), R(14), R(15), |
| R(16), R(17), R(18), R(19), R(20), R(21), R(22), R(23), |
| R(24), R(25), R(26), R(27), R(28), R(29), R(30), R(31), |
| #undef R |
| }; |
| |
| #ifndef MIPS_DEFAULT_FPU_TYPE |
| #define MIPS_DEFAULT_FPU_TYPE MIPS_FPU_DOUBLE |
| #endif |
| static int mips_fpu_type_auto = 1; |
| static enum mips_fpu_type mips_fpu_type = MIPS_DEFAULT_FPU_TYPE; |
| |
| static unsigned int mips_debug = 0; |
| |
| /* Properties (for struct target_desc) describing the g/G packet |
| layout. */ |
| #define PROPERTY_GP32 "internal: transfers-32bit-registers" |
| #define PROPERTY_GP64 "internal: transfers-64bit-registers" |
| |
| struct target_desc *mips_tdesc_gp32; |
| struct target_desc *mips_tdesc_gp64; |
| |
| const struct mips_regnum * |
| mips_regnum (struct gdbarch *gdbarch) |
| { |
| return gdbarch_tdep (gdbarch)->regnum; |
| } |
| |
| static int |
| mips_fpa0_regnum (struct gdbarch *gdbarch) |
| { |
| return mips_regnum (gdbarch)->fp0 + 12; |
| } |
| |
| /* Return 1 if REGNUM refers to a floating-point general register, raw |
| or cooked. Otherwise return 0. */ |
| |
| static int |
| mips_float_register_p (struct gdbarch *gdbarch, int regnum) |
| { |
| int rawnum = regnum % gdbarch_num_regs (gdbarch); |
| |
| return (rawnum >= mips_regnum (gdbarch)->fp0 |
| && rawnum < mips_regnum (gdbarch)->fp0 + 32); |
| } |
| |
| #define MIPS_EABI(gdbarch) (gdbarch_tdep (gdbarch)->mips_abi \ |
| == MIPS_ABI_EABI32 \ |
| || gdbarch_tdep (gdbarch)->mips_abi == MIPS_ABI_EABI64) |
| |
| #define MIPS_LAST_FP_ARG_REGNUM(gdbarch) \ |
| (gdbarch_tdep (gdbarch)->mips_last_fp_arg_regnum) |
| |
| #define MIPS_LAST_ARG_REGNUM(gdbarch) \ |
| (gdbarch_tdep (gdbarch)->mips_last_arg_regnum) |
| |
| #define MIPS_FPU_TYPE(gdbarch) (gdbarch_tdep (gdbarch)->mips_fpu_type) |
| |
| /* Return the MIPS ABI associated with GDBARCH. */ |
| enum mips_abi |
| mips_abi (struct gdbarch *gdbarch) |
| { |
| return gdbarch_tdep (gdbarch)->mips_abi; |
| } |
| |
| int |
| mips_isa_regsize (struct gdbarch *gdbarch) |
| { |
| struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
| |
| /* If we know how big the registers are, use that size. */ |
| if (tdep->register_size_valid_p) |
| return tdep->register_size; |
| |
| /* Fall back to the previous behavior. */ |
| return (gdbarch_bfd_arch_info (gdbarch)->bits_per_word |
| / gdbarch_bfd_arch_info (gdbarch)->bits_per_byte); |
| } |
| |
| /* Return the currently configured (or set) saved register size. */ |
| |
| unsigned int |
| mips_abi_regsize (struct gdbarch *gdbarch) |
| { |
| switch (mips_abi (gdbarch)) |
| { |
| case MIPS_ABI_EABI32: |
| case MIPS_ABI_O32: |
| return 4; |
| case MIPS_ABI_N32: |
| case MIPS_ABI_N64: |
| case MIPS_ABI_O64: |
| case MIPS_ABI_EABI64: |
| return 8; |
| case MIPS_ABI_UNKNOWN: |
| case MIPS_ABI_LAST: |
| default: |
| internal_error (__FILE__, __LINE__, _("bad switch")); |
| } |
| } |
| |
| /* MIPS16/microMIPS function addresses are odd (bit 0 is set). Here |
| are some functions to handle addresses associated with compressed |
| code including but not limited to testing, setting, or clearing |
| bit 0 of such addresses. */ |
| |
| /* Return one iff compressed code is the MIPS16 instruction set. */ |
| |
| static int |
| is_mips16_isa (struct gdbarch *gdbarch) |
| { |
| return gdbarch_tdep (gdbarch)->mips_isa == ISA_MIPS16; |
| } |
| |
| /* Return one iff compressed code is the microMIPS instruction set. */ |
| |
| static int |
| is_micromips_isa (struct gdbarch *gdbarch) |
| { |
| return gdbarch_tdep (gdbarch)->mips_isa == ISA_MICROMIPS; |
| } |
| |
| /* Return one iff ADDR denotes compressed code. */ |
| |
| static int |
| is_compact_addr (CORE_ADDR addr) |
| { |
| return ((addr) & 1); |
| } |
| |
| /* Return one iff ADDR denotes standard ISA code. */ |
| |
| static int |
| is_mips_addr (CORE_ADDR addr) |
| { |
| return !is_compact_addr (addr); |
| } |
| |
| /* Return one iff ADDR denotes MIPS16 code. */ |
| |
| static int |
| is_mips16_addr (struct gdbarch *gdbarch, CORE_ADDR addr) |
| { |
| return is_compact_addr (addr) && is_mips16_isa (gdbarch); |
| } |
| |
| /* Return one iff ADDR denotes microMIPS code. */ |
| |
| static int |
| is_micromips_addr (struct gdbarch *gdbarch, CORE_ADDR addr) |
| { |
| return is_compact_addr (addr) && is_micromips_isa (gdbarch); |
| } |
| |
| /* Strip the ISA (compression) bit off from ADDR. */ |
| |
| static CORE_ADDR |
| unmake_compact_addr (CORE_ADDR addr) |
| { |
| return ((addr) & ~(CORE_ADDR) 1); |
| } |
| |
| /* Add the ISA (compression) bit to ADDR. */ |
| |
| static CORE_ADDR |
| make_compact_addr (CORE_ADDR addr) |
| { |
| return ((addr) | (CORE_ADDR) 1); |
| } |
| |
| /* Extern version of unmake_compact_addr; we use a separate function |
| so that unmake_compact_addr can be inlined throughout this file. */ |
| |
| CORE_ADDR |
| mips_unmake_compact_addr (CORE_ADDR addr) |
| { |
| return unmake_compact_addr (addr); |
| } |
| |
| /* Functions for setting and testing a bit in a minimal symbol that |
| marks it as MIPS16 or microMIPS function. The MSB of the minimal |
| symbol's "info" field is used for this purpose. |
| |
| gdbarch_elf_make_msymbol_special tests whether an ELF symbol is |
| "special", i.e. refers to a MIPS16 or microMIPS function, and sets |
| one of the "special" bits in a minimal symbol to mark it accordingly. |
| The test checks an ELF-private flag that is valid for true function |
| symbols only; for synthetic symbols such as for PLT stubs that have |
| no ELF-private part at all the MIPS BFD backend arranges for this |
| information to be carried in the asymbol's udata field instead. |
| |
| msymbol_is_mips16 and msymbol_is_micromips test the "special" bit |
| in a minimal symbol. */ |
| |
| static void |
| mips_elf_make_msymbol_special (asymbol * sym, struct minimal_symbol *msym) |
| { |
| elf_symbol_type *elfsym = (elf_symbol_type *) sym; |
| unsigned char st_other; |
| |
| if ((sym->flags & BSF_SYNTHETIC) == 0) |
| st_other = elfsym->internal_elf_sym.st_other; |
| else if ((sym->flags & BSF_FUNCTION) != 0) |
| st_other = sym->udata.i; |
| else |
| return; |
| |
| if (ELF_ST_IS_MICROMIPS (st_other)) |
| { |
| MSYMBOL_TARGET_FLAG_MICROMIPS (msym) = 1; |
| SET_MSYMBOL_VALUE_ADDRESS (msym, MSYMBOL_VALUE_RAW_ADDRESS (msym) | 1); |
| } |
| else if (ELF_ST_IS_MIPS16 (st_other)) |
| { |
| MSYMBOL_TARGET_FLAG_MIPS16 (msym) = 1; |
| SET_MSYMBOL_VALUE_ADDRESS (msym, MSYMBOL_VALUE_RAW_ADDRESS (msym) | 1); |
| } |
| } |
| |
| /* Return one iff MSYM refers to standard ISA code. */ |
| |
| static int |
| msymbol_is_mips (struct minimal_symbol *msym) |
| { |
| return !(MSYMBOL_TARGET_FLAG_MIPS16 (msym) |
| | MSYMBOL_TARGET_FLAG_MICROMIPS (msym)); |
| } |
| |
| /* Return one iff MSYM refers to MIPS16 code. */ |
| |
| static int |
| msymbol_is_mips16 (struct minimal_symbol *msym) |
| { |
| return MSYMBOL_TARGET_FLAG_MIPS16 (msym); |
| } |
| |
| /* Return one iff MSYM refers to microMIPS code. */ |
| |
| static int |
| msymbol_is_micromips (struct minimal_symbol *msym) |
| { |
| return MSYMBOL_TARGET_FLAG_MICROMIPS (msym); |
| } |
| |
| /* Set the ISA bit in the main symbol too, complementing the corresponding |
| minimal symbol setting and reflecting the run-time value of the symbol. |
| The need for comes from the ISA bit having been cleared as code in |
| `_bfd_mips_elf_symbol_processing' separated it into the ELF symbol's |
| `st_other' STO_MIPS16 or STO_MICROMIPS annotation, making the values |
| of symbols referring to compressed code different in GDB to the values |
| used by actual code. That in turn makes them evaluate incorrectly in |
| expressions, producing results different to what the same expressions |
| yield when compiled into the program being debugged. */ |
| |
| static void |
| mips_make_symbol_special (struct symbol *sym, struct objfile *objfile) |
| { |
| if (SYMBOL_CLASS (sym) == LOC_BLOCK) |
| { |
| /* We are in symbol reading so it is OK to cast away constness. */ |
| struct block *block = (struct block *) SYMBOL_BLOCK_VALUE (sym); |
| CORE_ADDR compact_block_start; |
| struct bound_minimal_symbol msym; |
| |
| compact_block_start = BLOCK_START (block) | 1; |
| msym = lookup_minimal_symbol_by_pc (compact_block_start); |
| if (msym.minsym && !msymbol_is_mips (msym.minsym)) |
| { |
| BLOCK_START (block) = compact_block_start; |
| } |
| } |
| } |
| |
| /* XFER a value from the big/little/left end of the register. |
| Depending on the size of the value it might occupy the entire |
| register or just part of it. Make an allowance for this, aligning |
| things accordingly. */ |
| |
| static void |
| mips_xfer_register (struct gdbarch *gdbarch, struct regcache *regcache, |
| int reg_num, int length, |
| enum bfd_endian endian, gdb_byte *in, |
| const gdb_byte *out, int buf_offset) |
| { |
| int reg_offset = 0; |
| |
| gdb_assert (reg_num >= gdbarch_num_regs (gdbarch)); |
| /* Need to transfer the left or right part of the register, based on |
| the targets byte order. */ |
| switch (endian) |
| { |
| case BFD_ENDIAN_BIG: |
| reg_offset = register_size (gdbarch, reg_num) - length; |
| break; |
| case BFD_ENDIAN_LITTLE: |
| reg_offset = 0; |
| break; |
| case BFD_ENDIAN_UNKNOWN: /* Indicates no alignment. */ |
| reg_offset = 0; |
| break; |
| default: |
| internal_error (__FILE__, __LINE__, _("bad switch")); |
| } |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stderr, |
| "xfer $%d, reg offset %d, buf offset %d, length %d, ", |
| reg_num, reg_offset, buf_offset, length); |
| if (mips_debug && out != NULL) |
| { |
| int i; |
| fprintf_unfiltered (gdb_stdlog, "out "); |
| for (i = 0; i < length; i++) |
| fprintf_unfiltered (gdb_stdlog, "%02x", out[buf_offset + i]); |
| } |
| if (in != NULL) |
| regcache_cooked_read_part (regcache, reg_num, reg_offset, length, |
| in + buf_offset); |
| if (out != NULL) |
| regcache_cooked_write_part (regcache, reg_num, reg_offset, length, |
| out + buf_offset); |
| if (mips_debug && in != NULL) |
| { |
| int i; |
| fprintf_unfiltered (gdb_stdlog, "in "); |
| for (i = 0; i < length; i++) |
| fprintf_unfiltered (gdb_stdlog, "%02x", in[buf_offset + i]); |
| } |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, "\n"); |
| } |
| |
| /* Determine if a MIPS3 or later cpu is operating in MIPS{1,2} FPU |
| compatiblity mode. A return value of 1 means that we have |
| physical 64-bit registers, but should treat them as 32-bit registers. */ |
| |
| static int |
| mips2_fp_compat (struct frame_info *frame) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| /* MIPS1 and MIPS2 have only 32 bit FPRs, and the FR bit is not |
| meaningful. */ |
| if (register_size (gdbarch, mips_regnum (gdbarch)->fp0) == 4) |
| return 0; |
| |
| #if 0 |
| /* FIXME drow 2002-03-10: This is disabled until we can do it consistently, |
| in all the places we deal with FP registers. PR gdb/413. */ |
| /* Otherwise check the FR bit in the status register - it controls |
| the FP compatiblity mode. If it is clear we are in compatibility |
| mode. */ |
| if ((get_frame_register_unsigned (frame, MIPS_PS_REGNUM) & ST0_FR) == 0) |
| return 1; |
| #endif |
| |
| return 0; |
| } |
| |
| #define VM_MIN_ADDRESS (CORE_ADDR)0x400000 |
| |
| static CORE_ADDR heuristic_proc_start (struct gdbarch *, CORE_ADDR); |
| |
| static void reinit_frame_cache_sfunc (char *, int, struct cmd_list_element *); |
| |
| /* The list of available "set mips " and "show mips " commands. */ |
| |
| static struct cmd_list_element *setmipscmdlist = NULL; |
| static struct cmd_list_element *showmipscmdlist = NULL; |
| |
| /* Integer registers 0 thru 31 are handled explicitly by |
| mips_register_name(). Processor specific registers 32 and above |
| are listed in the following tables. */ |
| |
| enum |
| { NUM_MIPS_PROCESSOR_REGS = (90 - 32) }; |
| |
| /* Generic MIPS. */ |
| |
| static const char *mips_generic_reg_names[NUM_MIPS_PROCESSOR_REGS] = { |
| "sr", "lo", "hi", "bad", "cause", "pc", |
| "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", |
| "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", |
| "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", |
| "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", |
| "fsr", "fir", |
| }; |
| |
| /* Names of IDT R3041 registers. */ |
| |
| static const char *mips_r3041_reg_names[] = { |
| "sr", "lo", "hi", "bad", "cause", "pc", |
| "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", |
| "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", |
| "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", |
| "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", |
| "fsr", "fir", "", /*"fp" */ "", |
| "", "", "bus", "ccfg", "", "", "", "", |
| "", "", "port", "cmp", "", "", "epc", "prid", |
| }; |
| |
| /* Names of tx39 registers. */ |
| |
| static const char *mips_tx39_reg_names[NUM_MIPS_PROCESSOR_REGS] = { |
| "sr", "lo", "hi", "bad", "cause", "pc", |
| "", "", "", "", "", "", "", "", |
| "", "", "", "", "", "", "", "", |
| "", "", "", "", "", "", "", "", |
| "", "", "", "", "", "", "", "", |
| "", "", "", "", |
| "", "", "", "", "", "", "", "", |
| "", "", "config", "cache", "debug", "depc", "epc", |
| }; |
| |
| /* Names of IRIX registers. */ |
| static const char *mips_irix_reg_names[NUM_MIPS_PROCESSOR_REGS] = { |
| "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", |
| "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", |
| "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", |
| "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", |
| "pc", "cause", "bad", "hi", "lo", "fsr", "fir" |
| }; |
| |
| /* Names of registers with Linux kernels. */ |
| static const char *mips_linux_reg_names[NUM_MIPS_PROCESSOR_REGS] = { |
| "sr", "lo", "hi", "bad", "cause", "pc", |
| "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", |
| "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", |
| "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", |
| "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", |
| "fsr", "fir" |
| }; |
| |
| |
| /* Return the name of the register corresponding to REGNO. */ |
| static const char * |
| mips_register_name (struct gdbarch *gdbarch, int regno) |
| { |
| struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
| /* GPR names for all ABIs other than n32/n64. */ |
| static char *mips_gpr_names[] = { |
| "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", |
| "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", |
| "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", |
| "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra", |
| }; |
| |
| /* GPR names for n32 and n64 ABIs. */ |
| static char *mips_n32_n64_gpr_names[] = { |
| "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", |
| "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3", |
| "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", |
| "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra" |
| }; |
| |
| enum mips_abi abi = mips_abi (gdbarch); |
| |
| /* Map [gdbarch_num_regs .. 2*gdbarch_num_regs) onto the raw registers, |
| but then don't make the raw register names visible. This (upper) |
| range of user visible register numbers are the pseudo-registers. |
| |
| This approach was adopted accommodate the following scenario: |
| It is possible to debug a 64-bit device using a 32-bit |
| programming model. In such instances, the raw registers are |
| configured to be 64-bits wide, while the pseudo registers are |
| configured to be 32-bits wide. The registers that the user |
| sees - the pseudo registers - match the users expectations |
| given the programming model being used. */ |
| int rawnum = regno % gdbarch_num_regs (gdbarch); |
| if (regno < gdbarch_num_regs (gdbarch)) |
| return ""; |
| |
| /* The MIPS integer registers are always mapped from 0 to 31. The |
| names of the registers (which reflects the conventions regarding |
| register use) vary depending on the ABI. */ |
| if (0 <= rawnum && rawnum < 32) |
| { |
| if (abi == MIPS_ABI_N32 || abi == MIPS_ABI_N64) |
| return mips_n32_n64_gpr_names[rawnum]; |
| else |
| return mips_gpr_names[rawnum]; |
| } |
| else if (tdesc_has_registers (gdbarch_target_desc (gdbarch))) |
| return tdesc_register_name (gdbarch, rawnum); |
| else if (32 <= rawnum && rawnum < gdbarch_num_regs (gdbarch)) |
| { |
| gdb_assert (rawnum - 32 < NUM_MIPS_PROCESSOR_REGS); |
| if (tdep->mips_processor_reg_names[rawnum - 32]) |
| return tdep->mips_processor_reg_names[rawnum - 32]; |
| return ""; |
| } |
| else |
| internal_error (__FILE__, __LINE__, |
| _("mips_register_name: bad register number %d"), rawnum); |
| } |
| |
| /* Return the groups that a MIPS register can be categorised into. */ |
| |
| static int |
| mips_register_reggroup_p (struct gdbarch *gdbarch, int regnum, |
| struct reggroup *reggroup) |
| { |
| int vector_p; |
| int float_p; |
| int raw_p; |
| int rawnum = regnum % gdbarch_num_regs (gdbarch); |
| int pseudo = regnum / gdbarch_num_regs (gdbarch); |
| if (reggroup == all_reggroup) |
| return pseudo; |
| vector_p = TYPE_VECTOR (register_type (gdbarch, regnum)); |
| float_p = TYPE_CODE (register_type (gdbarch, regnum)) == TYPE_CODE_FLT; |
| /* FIXME: cagney/2003-04-13: Can't yet use gdbarch_num_regs |
| (gdbarch), as not all architectures are multi-arch. */ |
| raw_p = rawnum < gdbarch_num_regs (gdbarch); |
| if (gdbarch_register_name (gdbarch, regnum) == NULL |
| || gdbarch_register_name (gdbarch, regnum)[0] == '\0') |
| return 0; |
| if (reggroup == float_reggroup) |
| return float_p && pseudo; |
| if (reggroup == vector_reggroup) |
| return vector_p && pseudo; |
| if (reggroup == general_reggroup) |
| return (!vector_p && !float_p) && pseudo; |
| /* Save the pseudo registers. Need to make certain that any code |
| extracting register values from a saved register cache also uses |
| pseudo registers. */ |
| if (reggroup == save_reggroup) |
| return raw_p && pseudo; |
| /* Restore the same pseudo register. */ |
| if (reggroup == restore_reggroup) |
| return raw_p && pseudo; |
| return 0; |
| } |
| |
| /* Return the groups that a MIPS register can be categorised into. |
| This version is only used if we have a target description which |
| describes real registers (and their groups). */ |
| |
| static int |
| mips_tdesc_register_reggroup_p (struct gdbarch *gdbarch, int regnum, |
| struct reggroup *reggroup) |
| { |
| int rawnum = regnum % gdbarch_num_regs (gdbarch); |
| int pseudo = regnum / gdbarch_num_regs (gdbarch); |
| int ret; |
| |
| /* Only save, restore, and display the pseudo registers. Need to |
| make certain that any code extracting register values from a |
| saved register cache also uses pseudo registers. |
| |
| Note: saving and restoring the pseudo registers is slightly |
| strange; if we have 64 bits, we should save and restore all |
| 64 bits. But this is hard and has little benefit. */ |
| if (!pseudo) |
| return 0; |
| |
| ret = tdesc_register_in_reggroup_p (gdbarch, rawnum, reggroup); |
| if (ret != -1) |
| return ret; |
| |
| return mips_register_reggroup_p (gdbarch, regnum, reggroup); |
| } |
| |
| /* Map the symbol table registers which live in the range [1 * |
| gdbarch_num_regs .. 2 * gdbarch_num_regs) back onto the corresponding raw |
| registers. Take care of alignment and size problems. */ |
| |
| static enum register_status |
| mips_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache, |
| int cookednum, gdb_byte *buf) |
| { |
| int rawnum = cookednum % gdbarch_num_regs (gdbarch); |
| gdb_assert (cookednum >= gdbarch_num_regs (gdbarch) |
| && cookednum < 2 * gdbarch_num_regs (gdbarch)); |
| if (register_size (gdbarch, rawnum) == register_size (gdbarch, cookednum)) |
| return regcache_raw_read (regcache, rawnum, buf); |
| else if (register_size (gdbarch, rawnum) > |
| register_size (gdbarch, cookednum)) |
| { |
| if (gdbarch_tdep (gdbarch)->mips64_transfers_32bit_regs_p) |
| return regcache_raw_read_part (regcache, rawnum, 0, 4, buf); |
| else |
| { |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| LONGEST regval; |
| enum register_status status; |
| |
| status = regcache_raw_read_signed (regcache, rawnum, ®val); |
| if (status == REG_VALID) |
| store_signed_integer (buf, 4, byte_order, regval); |
| return status; |
| } |
| } |
| else |
| internal_error (__FILE__, __LINE__, _("bad register size")); |
| } |
| |
| static void |
| mips_pseudo_register_write (struct gdbarch *gdbarch, |
| struct regcache *regcache, int cookednum, |
| const gdb_byte *buf) |
| { |
| int rawnum = cookednum % gdbarch_num_regs (gdbarch); |
| gdb_assert (cookednum >= gdbarch_num_regs (gdbarch) |
| && cookednum < 2 * gdbarch_num_regs (gdbarch)); |
| if (register_size (gdbarch, rawnum) == register_size (gdbarch, cookednum)) |
| regcache_raw_write (regcache, rawnum, buf); |
| else if (register_size (gdbarch, rawnum) > |
| register_size (gdbarch, cookednum)) |
| { |
| if (gdbarch_tdep (gdbarch)->mips64_transfers_32bit_regs_p) |
| regcache_raw_write_part (regcache, rawnum, 0, 4, buf); |
| else |
| { |
| /* Sign extend the shortened version of the register prior |
| to placing it in the raw register. This is required for |
| some mips64 parts in order to avoid unpredictable behavior. */ |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| LONGEST regval = extract_signed_integer (buf, 4, byte_order); |
| regcache_raw_write_signed (regcache, rawnum, regval); |
| } |
| } |
| else |
| internal_error (__FILE__, __LINE__, _("bad register size")); |
| } |
| |
| static int |
| mips_ax_pseudo_register_collect (struct gdbarch *gdbarch, |
| struct agent_expr *ax, int reg) |
| { |
| int rawnum = reg % gdbarch_num_regs (gdbarch); |
| gdb_assert (reg >= gdbarch_num_regs (gdbarch) |
| && reg < 2 * gdbarch_num_regs (gdbarch)); |
| |
| ax_reg_mask (ax, rawnum); |
| |
| return 0; |
| } |
| |
| static int |
| mips_ax_pseudo_register_push_stack (struct gdbarch *gdbarch, |
| struct agent_expr *ax, int reg) |
| { |
| int rawnum = reg % gdbarch_num_regs (gdbarch); |
| gdb_assert (reg >= gdbarch_num_regs (gdbarch) |
| && reg < 2 * gdbarch_num_regs (gdbarch)); |
| if (register_size (gdbarch, rawnum) >= register_size (gdbarch, reg)) |
| { |
| ax_reg (ax, rawnum); |
| |
| if (register_size (gdbarch, rawnum) > register_size (gdbarch, reg)) |
| { |
| if (!gdbarch_tdep (gdbarch)->mips64_transfers_32bit_regs_p |
| || gdbarch_byte_order (gdbarch) != BFD_ENDIAN_BIG) |
| { |
| ax_const_l (ax, 32); |
| ax_simple (ax, aop_lsh); |
| } |
| ax_const_l (ax, 32); |
| ax_simple (ax, aop_rsh_signed); |
| } |
| } |
| else |
| internal_error (__FILE__, __LINE__, _("bad register size")); |
| |
| return 0; |
| } |
| |
| /* Table to translate 3-bit register field to actual register number. */ |
| static const signed char mips_reg3_to_reg[8] = { 16, 17, 2, 3, 4, 5, 6, 7 }; |
| |
| /* Heuristic_proc_start may hunt through the text section for a long |
| time across a 2400 baud serial line. Allows the user to limit this |
| search. */ |
| |
| static int heuristic_fence_post = 0; |
| |
| /* Number of bytes of storage in the actual machine representation for |
| register N. NOTE: This defines the pseudo register type so need to |
| rebuild the architecture vector. */ |
| |
| static int mips64_transfers_32bit_regs_p = 0; |
| |
| static void |
| set_mips64_transfers_32bit_regs (char *args, int from_tty, |
| struct cmd_list_element *c) |
| { |
| struct gdbarch_info info; |
| gdbarch_info_init (&info); |
| /* FIXME: cagney/2003-11-15: Should be setting a field in "info" |
| instead of relying on globals. Doing that would let generic code |
| handle the search for this specific architecture. */ |
| if (!gdbarch_update_p (info)) |
| { |
| mips64_transfers_32bit_regs_p = 0; |
| error (_("32-bit compatibility mode not supported")); |
| } |
| } |
| |
| /* Convert to/from a register and the corresponding memory value. */ |
| |
| /* This predicate tests for the case of an 8 byte floating point |
| value that is being transferred to or from a pair of floating point |
| registers each of which are (or are considered to be) only 4 bytes |
| wide. */ |
| static int |
| mips_convert_register_float_case_p (struct gdbarch *gdbarch, int regnum, |
| struct type *type) |
| { |
| return (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG |
| && register_size (gdbarch, regnum) == 4 |
| && mips_float_register_p (gdbarch, regnum) |
| && TYPE_CODE (type) == TYPE_CODE_FLT && TYPE_LENGTH (type) == 8); |
| } |
| |
| /* This predicate tests for the case of a value of less than 8 |
| bytes in width that is being transfered to or from an 8 byte |
| general purpose register. */ |
| static int |
| mips_convert_register_gpreg_case_p (struct gdbarch *gdbarch, int regnum, |
| struct type *type) |
| { |
| int num_regs = gdbarch_num_regs (gdbarch); |
| |
| return (register_size (gdbarch, regnum) == 8 |
| && regnum % num_regs > 0 && regnum % num_regs < 32 |
| && TYPE_LENGTH (type) < 8); |
| } |
| |
| static int |
| mips_convert_register_p (struct gdbarch *gdbarch, |
| int regnum, struct type *type) |
| { |
| return (mips_convert_register_float_case_p (gdbarch, regnum, type) |
| || mips_convert_register_gpreg_case_p (gdbarch, regnum, type)); |
| } |
| |
| static int |
| mips_register_to_value (struct frame_info *frame, int regnum, |
| struct type *type, gdb_byte *to, |
| int *optimizedp, int *unavailablep) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| |
| if (mips_convert_register_float_case_p (gdbarch, regnum, type)) |
| { |
| get_frame_register (frame, regnum + 0, to + 4); |
| get_frame_register (frame, regnum + 1, to + 0); |
| |
| if (!get_frame_register_bytes (frame, regnum + 0, 0, 4, to + 4, |
| optimizedp, unavailablep)) |
| return 0; |
| |
| if (!get_frame_register_bytes (frame, regnum + 1, 0, 4, to + 0, |
| optimizedp, unavailablep)) |
| return 0; |
| *optimizedp = *unavailablep = 0; |
| return 1; |
| } |
| else if (mips_convert_register_gpreg_case_p (gdbarch, regnum, type)) |
| { |
| int len = TYPE_LENGTH (type); |
| CORE_ADDR offset; |
| |
| offset = gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG ? 8 - len : 0; |
| if (!get_frame_register_bytes (frame, regnum, offset, len, to, |
| optimizedp, unavailablep)) |
| return 0; |
| |
| *optimizedp = *unavailablep = 0; |
| return 1; |
| } |
| else |
| { |
| internal_error (__FILE__, __LINE__, |
| _("mips_register_to_value: unrecognized case")); |
| } |
| } |
| |
| static void |
| mips_value_to_register (struct frame_info *frame, int regnum, |
| struct type *type, const gdb_byte *from) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| |
| if (mips_convert_register_float_case_p (gdbarch, regnum, type)) |
| { |
| put_frame_register (frame, regnum + 0, from + 4); |
| put_frame_register (frame, regnum + 1, from + 0); |
| } |
| else if (mips_convert_register_gpreg_case_p (gdbarch, regnum, type)) |
| { |
| gdb_byte fill[8]; |
| int len = TYPE_LENGTH (type); |
| |
| /* Sign extend values, irrespective of type, that are stored to |
| a 64-bit general purpose register. (32-bit unsigned values |
| are stored as signed quantities within a 64-bit register. |
| When performing an operation, in compiled code, that combines |
| a 32-bit unsigned value with a signed 64-bit value, a type |
| conversion is first performed that zeroes out the high 32 bits.) */ |
| if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) |
| { |
| if (from[0] & 0x80) |
| store_signed_integer (fill, 8, BFD_ENDIAN_BIG, -1); |
| else |
| store_signed_integer (fill, 8, BFD_ENDIAN_BIG, 0); |
| put_frame_register_bytes (frame, regnum, 0, 8 - len, fill); |
| put_frame_register_bytes (frame, regnum, 8 - len, len, from); |
| } |
| else |
| { |
| if (from[len-1] & 0x80) |
| store_signed_integer (fill, 8, BFD_ENDIAN_LITTLE, -1); |
| else |
| store_signed_integer (fill, 8, BFD_ENDIAN_LITTLE, 0); |
| put_frame_register_bytes (frame, regnum, 0, len, from); |
| put_frame_register_bytes (frame, regnum, len, 8 - len, fill); |
| } |
| } |
| else |
| { |
| internal_error (__FILE__, __LINE__, |
| _("mips_value_to_register: unrecognized case")); |
| } |
| } |
| |
| /* Return the GDB type object for the "standard" data type of data in |
| register REG. */ |
| |
| static struct type * |
| mips_register_type (struct gdbarch *gdbarch, int regnum) |
| { |
| gdb_assert (regnum >= 0 && regnum < 2 * gdbarch_num_regs (gdbarch)); |
| if (mips_float_register_p (gdbarch, regnum)) |
| { |
| /* The floating-point registers raw, or cooked, always match |
| mips_isa_regsize(), and also map 1:1, byte for byte. */ |
| if (mips_isa_regsize (gdbarch) == 4) |
| return builtin_type (gdbarch)->builtin_float; |
| else |
| return builtin_type (gdbarch)->builtin_double; |
| } |
| else if (regnum < gdbarch_num_regs (gdbarch)) |
| { |
| /* The raw or ISA registers. These are all sized according to |
| the ISA regsize. */ |
| if (mips_isa_regsize (gdbarch) == 4) |
| return builtin_type (gdbarch)->builtin_int32; |
| else |
| return builtin_type (gdbarch)->builtin_int64; |
| } |
| else |
| { |
| int rawnum = regnum - gdbarch_num_regs (gdbarch); |
| |
| /* The cooked or ABI registers. These are sized according to |
| the ABI (with a few complications). */ |
| if (rawnum == mips_regnum (gdbarch)->fp_control_status |
| || rawnum == mips_regnum (gdbarch)->fp_implementation_revision) |
| return builtin_type (gdbarch)->builtin_int32; |
| else if (gdbarch_osabi (gdbarch) != GDB_OSABI_IRIX |
| && gdbarch_osabi (gdbarch) != GDB_OSABI_LINUX |
| && rawnum >= MIPS_FIRST_EMBED_REGNUM |
| && rawnum <= MIPS_LAST_EMBED_REGNUM) |
| /* The pseudo/cooked view of the embedded registers is always |
| 32-bit. The raw view is handled below. */ |
| return builtin_type (gdbarch)->builtin_int32; |
| else if (gdbarch_tdep (gdbarch)->mips64_transfers_32bit_regs_p) |
| /* The target, while possibly using a 64-bit register buffer, |
| is only transfering 32-bits of each integer register. |
| Reflect this in the cooked/pseudo (ABI) register value. */ |
| return builtin_type (gdbarch)->builtin_int32; |
| else if (mips_abi_regsize (gdbarch) == 4) |
| /* The ABI is restricted to 32-bit registers (the ISA could be |
| 32- or 64-bit). */ |
| return builtin_type (gdbarch)->builtin_int32; |
| else |
| /* 64-bit ABI. */ |
| return builtin_type (gdbarch)->builtin_int64; |
| } |
| } |
| |
| /* Return the GDB type for the pseudo register REGNUM, which is the |
| ABI-level view. This function is only called if there is a target |
| description which includes registers, so we know precisely the |
| types of hardware registers. */ |
| |
| static struct type * |
| mips_pseudo_register_type (struct gdbarch *gdbarch, int regnum) |
| { |
| const int num_regs = gdbarch_num_regs (gdbarch); |
| int rawnum = regnum % num_regs; |
| struct type *rawtype; |
| |
| gdb_assert (regnum >= num_regs && regnum < 2 * num_regs); |
| |
| /* Absent registers are still absent. */ |
| rawtype = gdbarch_register_type (gdbarch, rawnum); |
| if (TYPE_LENGTH (rawtype) == 0) |
| return rawtype; |
| |
| if (mips_float_register_p (gdbarch, rawnum)) |
| /* Present the floating point registers however the hardware did; |
| do not try to convert between FPU layouts. */ |
| return rawtype; |
| |
| /* Use pointer types for registers if we can. For n32 we can not, |
| since we do not have a 64-bit pointer type. */ |
| if (mips_abi_regsize (gdbarch) |
| == TYPE_LENGTH (builtin_type (gdbarch)->builtin_data_ptr)) |
| { |
| if (rawnum == MIPS_SP_REGNUM |
| || rawnum == mips_regnum (gdbarch)->badvaddr) |
| return builtin_type (gdbarch)->builtin_data_ptr; |
| else if (rawnum == mips_regnum (gdbarch)->pc) |
| return builtin_type (gdbarch)->builtin_func_ptr; |
| } |
| |
| if (mips_abi_regsize (gdbarch) == 4 && TYPE_LENGTH (rawtype) == 8 |
| && ((rawnum >= MIPS_ZERO_REGNUM && rawnum <= MIPS_PS_REGNUM) |
| || rawnum == mips_regnum (gdbarch)->lo |
| || rawnum == mips_regnum (gdbarch)->hi |
| || rawnum == mips_regnum (gdbarch)->badvaddr |
| || rawnum == mips_regnum (gdbarch)->cause |
| || rawnum == mips_regnum (gdbarch)->pc |
| || (mips_regnum (gdbarch)->dspacc != -1 |
| && rawnum >= mips_regnum (gdbarch)->dspacc |
| && rawnum < mips_regnum (gdbarch)->dspacc + 6))) |
| return builtin_type (gdbarch)->builtin_int32; |
| |
| if (gdbarch_osabi (gdbarch) != GDB_OSABI_IRIX |
| && gdbarch_osabi (gdbarch) != GDB_OSABI_LINUX |
| && rawnum >= MIPS_EMBED_FP0_REGNUM + 32 |
| && rawnum <= MIPS_LAST_EMBED_REGNUM) |
| { |
| /* The pseudo/cooked view of embedded registers is always |
| 32-bit, even if the target transfers 64-bit values for them. |
| New targets relying on XML descriptions should only transfer |
| the necessary 32 bits, but older versions of GDB expected 64, |
| so allow the target to provide 64 bits without interfering |
| with the displayed type. */ |
| return builtin_type (gdbarch)->builtin_int32; |
| } |
| |
| /* For all other registers, pass through the hardware type. */ |
| return rawtype; |
| } |
| |
| /* Should the upper word of 64-bit addresses be zeroed? */ |
| enum auto_boolean mask_address_var = AUTO_BOOLEAN_AUTO; |
| |
| static int |
| mips_mask_address_p (struct gdbarch_tdep *tdep) |
| { |
| switch (mask_address_var) |
| { |
| case AUTO_BOOLEAN_TRUE: |
| return 1; |
| case AUTO_BOOLEAN_FALSE: |
| return 0; |
| break; |
| case AUTO_BOOLEAN_AUTO: |
| return tdep->default_mask_address_p; |
| default: |
| internal_error (__FILE__, __LINE__, |
| _("mips_mask_address_p: bad switch")); |
| return -1; |
| } |
| } |
| |
| static void |
| show_mask_address (struct ui_file *file, int from_tty, |
| struct cmd_list_element *c, const char *value) |
| { |
| struct gdbarch_tdep *tdep = gdbarch_tdep (target_gdbarch ()); |
| |
| deprecated_show_value_hack (file, from_tty, c, value); |
| switch (mask_address_var) |
| { |
| case AUTO_BOOLEAN_TRUE: |
| printf_filtered ("The 32 bit mips address mask is enabled\n"); |
| break; |
| case AUTO_BOOLEAN_FALSE: |
| printf_filtered ("The 32 bit mips address mask is disabled\n"); |
| break; |
| case AUTO_BOOLEAN_AUTO: |
| printf_filtered |
| ("The 32 bit address mask is set automatically. Currently %s\n", |
| mips_mask_address_p (tdep) ? "enabled" : "disabled"); |
| break; |
| default: |
| internal_error (__FILE__, __LINE__, _("show_mask_address: bad switch")); |
| break; |
| } |
| } |
| |
| /* Tell if the program counter value in MEMADDR is in a standard ISA |
| function. */ |
| |
| int |
| mips_pc_is_mips (CORE_ADDR memaddr) |
| { |
| struct bound_minimal_symbol sym; |
| |
| /* Flags indicating that this is a MIPS16 or microMIPS function is |
| stored by elfread.c in the high bit of the info field. Use this |
| to decide if the function is standard MIPS. Otherwise if bit 0 |
| of the address is clear, then this is a standard MIPS function. */ |
| sym = lookup_minimal_symbol_by_pc (make_compact_addr (memaddr)); |
| if (sym.minsym) |
| return msymbol_is_mips (sym.minsym); |
| else |
| return is_mips_addr (memaddr); |
| } |
| |
| /* Tell if the program counter value in MEMADDR is in a MIPS16 function. */ |
| |
| int |
| mips_pc_is_mips16 (struct gdbarch *gdbarch, CORE_ADDR memaddr) |
| { |
| struct bound_minimal_symbol sym; |
| |
| /* A flag indicating that this is a MIPS16 function is stored by |
| elfread.c in the high bit of the info field. Use this to decide |
| if the function is MIPS16. Otherwise if bit 0 of the address is |
| set, then ELF file flags will tell if this is a MIPS16 function. */ |
| sym = lookup_minimal_symbol_by_pc (make_compact_addr (memaddr)); |
| if (sym.minsym) |
| return msymbol_is_mips16 (sym.minsym); |
| else |
| return is_mips16_addr (gdbarch, memaddr); |
| } |
| |
| /* Tell if the program counter value in MEMADDR is in a microMIPS function. */ |
| |
| int |
| mips_pc_is_micromips (struct gdbarch *gdbarch, CORE_ADDR memaddr) |
| { |
| struct bound_minimal_symbol sym; |
| |
| /* A flag indicating that this is a microMIPS function is stored by |
| elfread.c in the high bit of the info field. Use this to decide |
| if the function is microMIPS. Otherwise if bit 0 of the address |
| is set, then ELF file flags will tell if this is a microMIPS |
| function. */ |
| sym = lookup_minimal_symbol_by_pc (make_compact_addr (memaddr)); |
| if (sym.minsym) |
| return msymbol_is_micromips (sym.minsym); |
| else |
| return is_micromips_addr (gdbarch, memaddr); |
| } |
| |
| /* Tell the ISA type of the function the program counter value in MEMADDR |
| is in. */ |
| |
| static enum mips_isa |
| mips_pc_isa (struct gdbarch *gdbarch, CORE_ADDR memaddr) |
| { |
| struct bound_minimal_symbol sym; |
| |
| /* A flag indicating that this is a MIPS16 or a microMIPS function |
| is stored by elfread.c in the high bit of the info field. Use |
| this to decide if the function is MIPS16 or microMIPS or normal |
| MIPS. Otherwise if bit 0 of the address is set, then ELF file |
| flags will tell if this is a MIPS16 or a microMIPS function. */ |
| sym = lookup_minimal_symbol_by_pc (make_compact_addr (memaddr)); |
| if (sym.minsym) |
| { |
| if (msymbol_is_micromips (sym.minsym)) |
| return ISA_MICROMIPS; |
| else if (msymbol_is_mips16 (sym.minsym)) |
| return ISA_MIPS16; |
| else |
| return ISA_MIPS; |
| } |
| else |
| { |
| if (is_mips_addr (memaddr)) |
| return ISA_MIPS; |
| else if (is_micromips_addr (gdbarch, memaddr)) |
| return ISA_MICROMIPS; |
| else |
| return ISA_MIPS16; |
| } |
| } |
| |
| /* Set the ISA bit correctly in the PC, used by DWARF-2 machinery. |
| The need for comes from the ISA bit having been cleared, making |
| addresses in FDE, range records, etc. referring to compressed code |
| different to those in line information, the symbol table and finally |
| the PC register. That in turn confuses many operations. */ |
| |
| static CORE_ADDR |
| mips_adjust_dwarf2_addr (CORE_ADDR pc) |
| { |
| pc = unmake_compact_addr (pc); |
| return mips_pc_is_mips (pc) ? pc : make_compact_addr (pc); |
| } |
| |
| /* Recalculate the line record requested so that the resulting PC has |
| the ISA bit set correctly, used by DWARF-2 machinery. The need for |
| this adjustment comes from some records associated with compressed |
| code having the ISA bit cleared, most notably at function prologue |
| ends. The ISA bit is in this context retrieved from the minimal |
| symbol covering the address requested, which in turn has been |
| constructed from the binary's symbol table rather than DWARF-2 |
| information. The correct setting of the ISA bit is required for |
| breakpoint addresses to correctly match against the stop PC. |
| |
| As line entries can specify relative address adjustments we need to |
| keep track of the absolute value of the last line address recorded |
| in line information, so that we can calculate the actual address to |
| apply the ISA bit adjustment to. We use PC for this tracking and |
| keep the original address there. |
| |
| As such relative address adjustments can be odd within compressed |
| code we need to keep track of the last line address with the ISA |
| bit adjustment applied too, as the original address may or may not |
| have had the ISA bit set. We use ADJ_PC for this tracking and keep |
| the adjusted address there. |
| |
| For relative address adjustments we then use these variables to |
| calculate the address intended by line information, which will be |
| PC-relative, and return an updated adjustment carrying ISA bit |
| information, which will be ADJ_PC-relative. For absolute address |
| adjustments we just return the same address that we store in ADJ_PC |
| too. |
| |
| As the first line entry can be relative to an implied address value |
| of 0 we need to have the initial address set up that we store in PC |
| and ADJ_PC. This is arranged with a call from `dwarf_decode_lines_1' |
| that sets PC to 0 and ADJ_PC accordingly, usually 0 as well. */ |
| |
| static CORE_ADDR |
| mips_adjust_dwarf2_line (CORE_ADDR addr, int rel) |
| { |
| static CORE_ADDR adj_pc; |
| static CORE_ADDR pc; |
| CORE_ADDR isa_pc; |
| |
| pc = rel ? pc + addr : addr; |
| isa_pc = mips_adjust_dwarf2_addr (pc); |
| addr = rel ? isa_pc - adj_pc : isa_pc; |
| adj_pc = isa_pc; |
| return addr; |
| } |
| |
| /* Various MIPS16 thunk (aka stub or trampoline) names. */ |
| |
| static const char mips_str_mips16_call_stub[] = "__mips16_call_stub_"; |
| static const char mips_str_mips16_ret_stub[] = "__mips16_ret_"; |
| static const char mips_str_call_fp_stub[] = "__call_stub_fp_"; |
| static const char mips_str_call_stub[] = "__call_stub_"; |
| static const char mips_str_fn_stub[] = "__fn_stub_"; |
| |
| /* This is used as a PIC thunk prefix. */ |
| |
| static const char mips_str_pic[] = ".pic."; |
| |
| /* Return non-zero if the PC is inside a call thunk (aka stub or |
| trampoline) that should be treated as a temporary frame. */ |
| |
| static int |
| mips_in_frame_stub (CORE_ADDR pc) |
| { |
| CORE_ADDR start_addr; |
| const char *name; |
| |
| /* Find the starting address of the function containing the PC. */ |
| if (find_pc_partial_function (pc, &name, &start_addr, NULL) == 0) |
| return 0; |
| |
| /* If the PC is in __mips16_call_stub_*, this is a call/return stub. */ |
| if (startswith (name, mips_str_mips16_call_stub)) |
| return 1; |
| /* If the PC is in __call_stub_*, this is a call/return or a call stub. */ |
| if (startswith (name, mips_str_call_stub)) |
| return 1; |
| /* If the PC is in __fn_stub_*, this is a call stub. */ |
| if (startswith (name, mips_str_fn_stub)) |
| return 1; |
| |
| return 0; /* Not a stub. */ |
| } |
| |
| /* MIPS believes that the PC has a sign extended value. Perhaps the |
| all registers should be sign extended for simplicity? */ |
| |
| static CORE_ADDR |
| mips_read_pc (struct regcache *regcache) |
| { |
| int regnum = gdbarch_pc_regnum (get_regcache_arch (regcache)); |
| LONGEST pc; |
| |
| regcache_cooked_read_signed (regcache, regnum, &pc); |
| return pc; |
| } |
| |
| static CORE_ADDR |
| mips_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame) |
| { |
| CORE_ADDR pc; |
| |
| pc = frame_unwind_register_signed (next_frame, gdbarch_pc_regnum (gdbarch)); |
| /* macro/2012-04-20: This hack skips over MIPS16 call thunks as |
| intermediate frames. In this case we can get the caller's address |
| from $ra, or if $ra contains an address within a thunk as well, then |
| it must be in the return path of __mips16_call_stub_{s,d}{f,c}_{0..10} |
| and thus the caller's address is in $s2. */ |
| if (frame_relative_level (next_frame) >= 0 && mips_in_frame_stub (pc)) |
| { |
| pc = frame_unwind_register_signed |
| (next_frame, gdbarch_num_regs (gdbarch) + MIPS_RA_REGNUM); |
| if (mips_in_frame_stub (pc)) |
| pc = frame_unwind_register_signed |
| (next_frame, gdbarch_num_regs (gdbarch) + MIPS_S2_REGNUM); |
| } |
| return pc; |
| } |
| |
| static CORE_ADDR |
| mips_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame) |
| { |
| return frame_unwind_register_signed |
| (next_frame, gdbarch_num_regs (gdbarch) + MIPS_SP_REGNUM); |
| } |
| |
| /* Assuming THIS_FRAME 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 |
| mips_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame) |
| { |
| return frame_id_build |
| (get_frame_register_signed (this_frame, |
| gdbarch_num_regs (gdbarch) |
| + MIPS_SP_REGNUM), |
| get_frame_pc (this_frame)); |
| } |
| |
| /* Implement the "write_pc" gdbarch method. */ |
| |
| void |
| mips_write_pc (struct regcache *regcache, CORE_ADDR pc) |
| { |
| int regnum = gdbarch_pc_regnum (get_regcache_arch (regcache)); |
| |
| regcache_cooked_write_unsigned (regcache, regnum, pc); |
| } |
| |
| /* Fetch and return instruction from the specified location. Handle |
| MIPS16/microMIPS as appropriate. */ |
| |
| static ULONGEST |
| mips_fetch_instruction (struct gdbarch *gdbarch, |
| enum mips_isa isa, CORE_ADDR addr, int *errp) |
| { |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| gdb_byte buf[MIPS_INSN32_SIZE]; |
| int instlen; |
| int err; |
| |
| switch (isa) |
| { |
| case ISA_MICROMIPS: |
| case ISA_MIPS16: |
| instlen = MIPS_INSN16_SIZE; |
| addr = unmake_compact_addr (addr); |
| break; |
| case ISA_MIPS: |
| instlen = MIPS_INSN32_SIZE; |
| break; |
| default: |
| internal_error (__FILE__, __LINE__, _("invalid ISA")); |
| break; |
| } |
| err = target_read_memory (addr, buf, instlen); |
| if (errp != NULL) |
| *errp = err; |
| if (err != 0) |
| { |
| if (errp == NULL) |
| memory_error (TARGET_XFER_E_IO, addr); |
| return 0; |
| } |
| return extract_unsigned_integer (buf, instlen, byte_order); |
| } |
| |
| /* These are the fields of 32 bit mips instructions. */ |
| #define mips32_op(x) (x >> 26) |
| #define itype_op(x) (x >> 26) |
| #define itype_rs(x) ((x >> 21) & 0x1f) |
| #define itype_rt(x) ((x >> 16) & 0x1f) |
| #define itype_immediate(x) (x & 0xffff) |
| |
| #define jtype_op(x) (x >> 26) |
| #define jtype_target(x) (x & 0x03ffffff) |
| |
| #define rtype_op(x) (x >> 26) |
| #define rtype_rs(x) ((x >> 21) & 0x1f) |
| #define rtype_rt(x) ((x >> 16) & 0x1f) |
| #define rtype_rd(x) ((x >> 11) & 0x1f) |
| #define rtype_shamt(x) ((x >> 6) & 0x1f) |
| #define rtype_funct(x) (x & 0x3f) |
| |
| /* MicroMIPS instruction fields. */ |
| #define micromips_op(x) ((x) >> 10) |
| |
| /* 16-bit/32-bit-high-part instruction formats, B and S refer to the lowest |
| bit and the size respectively of the field extracted. */ |
| #define b0s4_imm(x) ((x) & 0xf) |
| #define b0s5_imm(x) ((x) & 0x1f) |
| #define b0s5_reg(x) ((x) & 0x1f) |
| #define b0s7_imm(x) ((x) & 0x7f) |
| #define b0s10_imm(x) ((x) & 0x3ff) |
| #define b1s4_imm(x) (((x) >> 1) & 0xf) |
| #define b1s9_imm(x) (((x) >> 1) & 0x1ff) |
| #define b2s3_cc(x) (((x) >> 2) & 0x7) |
| #define b4s2_regl(x) (((x) >> 4) & 0x3) |
| #define b5s5_op(x) (((x) >> 5) & 0x1f) |
| #define b5s5_reg(x) (((x) >> 5) & 0x1f) |
| #define b6s4_op(x) (((x) >> 6) & 0xf) |
| #define b7s3_reg(x) (((x) >> 7) & 0x7) |
| |
| /* 32-bit instruction formats, B and S refer to the lowest bit and the size |
| respectively of the field extracted. */ |
| #define b0s6_op(x) ((x) & 0x3f) |
| #define b0s11_op(x) ((x) & 0x7ff) |
| #define b0s12_imm(x) ((x) & 0xfff) |
| #define b0s16_imm(x) ((x) & 0xffff) |
| #define b0s26_imm(x) ((x) & 0x3ffffff) |
| #define b6s10_ext(x) (((x) >> 6) & 0x3ff) |
| #define b11s5_reg(x) (((x) >> 11) & 0x1f) |
| #define b12s4_op(x) (((x) >> 12) & 0xf) |
| |
| /* Return the size in bytes of the instruction INSN encoded in the ISA |
| instruction set. */ |
| |
| static int |
| mips_insn_size (enum mips_isa isa, ULONGEST insn) |
| { |
| switch (isa) |
| { |
| case ISA_MICROMIPS: |
| if ((micromips_op (insn) & 0x4) == 0x4 |
| || (micromips_op (insn) & 0x7) == 0x0) |
| return 2 * MIPS_INSN16_SIZE; |
| else |
| return MIPS_INSN16_SIZE; |
| case ISA_MIPS16: |
| if ((insn & 0xf800) == 0xf000) |
| return 2 * MIPS_INSN16_SIZE; |
| else |
| return MIPS_INSN16_SIZE; |
| case ISA_MIPS: |
| return MIPS_INSN32_SIZE; |
| } |
| internal_error (__FILE__, __LINE__, _("invalid ISA")); |
| } |
| |
| static LONGEST |
| mips32_relative_offset (ULONGEST inst) |
| { |
| return ((itype_immediate (inst) ^ 0x8000) - 0x8000) << 2; |
| } |
| |
| /* Determine the address of the next instruction executed after the INST |
| floating condition branch instruction at PC. COUNT specifies the |
| number of the floating condition bits tested by the branch. */ |
| |
| static CORE_ADDR |
| mips32_bc1_pc (struct gdbarch *gdbarch, struct frame_info *frame, |
| ULONGEST inst, CORE_ADDR pc, int count) |
| { |
| int fcsr = mips_regnum (gdbarch)->fp_control_status; |
| int cnum = (itype_rt (inst) >> 2) & (count - 1); |
| int tf = itype_rt (inst) & 1; |
| int mask = (1 << count) - 1; |
| ULONGEST fcs; |
| int cond; |
| |
| if (fcsr == -1) |
| /* No way to handle; it'll most likely trap anyway. */ |
| return pc; |
| |
| fcs = get_frame_register_unsigned (frame, fcsr); |
| cond = ((fcs >> 24) & 0xfe) | ((fcs >> 23) & 0x01); |
| |
| if (((cond >> cnum) & mask) != mask * !tf) |
| pc += mips32_relative_offset (inst); |
| else |
| pc += 4; |
| |
| return pc; |
| } |
| |
| /* Return nonzero if the gdbarch is an Octeon series. */ |
| |
| static int |
| is_octeon (struct gdbarch *gdbarch) |
| { |
| const struct bfd_arch_info *info = gdbarch_bfd_arch_info (gdbarch); |
| |
| return (info->mach == bfd_mach_mips_octeon |
| || info->mach == bfd_mach_mips_octeonp |
| || info->mach == bfd_mach_mips_octeon2); |
| } |
| |
| /* Return true if the OP represents the Octeon's BBIT instruction. */ |
| |
| static int |
| is_octeon_bbit_op (int op, struct gdbarch *gdbarch) |
| { |
| if (!is_octeon (gdbarch)) |
| return 0; |
| /* BBIT0 is encoded as LWC2: 110 010. */ |
| /* BBIT032 is encoded as LDC2: 110 110. */ |
| /* BBIT1 is encoded as SWC2: 111 010. */ |
| /* BBIT132 is encoded as SDC2: 111 110. */ |
| if (op == 50 || op == 54 || op == 58 || op == 62) |
| return 1; |
| return 0; |
| } |
| |
| |
| /* Determine where to set a single step breakpoint while considering |
| branch prediction. */ |
| |
| static CORE_ADDR |
| mips32_next_pc (struct frame_info *frame, CORE_ADDR pc) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| unsigned long inst; |
| int op; |
| inst = mips_fetch_instruction (gdbarch, ISA_MIPS, pc, NULL); |
| op = itype_op (inst); |
| if ((inst & 0xe0000000) != 0) /* Not a special, jump or branch |
| instruction. */ |
| { |
| if (op >> 2 == 5) |
| /* BEQL, BNEL, BLEZL, BGTZL: bits 0101xx */ |
| { |
| switch (op & 0x03) |
| { |
| case 0: /* BEQL */ |
| goto equal_branch; |
| case 1: /* BNEL */ |
| goto neq_branch; |
| case 2: /* BLEZL */ |
| goto less_branch; |
| case 3: /* BGTZL */ |
| goto greater_branch; |
| default: |
| pc += 4; |
| } |
| } |
| else if (op == 17 && itype_rs (inst) == 8) |
| /* BC1F, BC1FL, BC1T, BC1TL: 010001 01000 */ |
| pc = mips32_bc1_pc (gdbarch, frame, inst, pc + 4, 1); |
| else if (op == 17 && itype_rs (inst) == 9 |
| && (itype_rt (inst) & 2) == 0) |
| /* BC1ANY2F, BC1ANY2T: 010001 01001 xxx0x */ |
| pc = mips32_bc1_pc (gdbarch, frame, inst, pc + 4, 2); |
| else if (op == 17 && itype_rs (inst) == 10 |
| && (itype_rt (inst) & 2) == 0) |
| /* BC1ANY4F, BC1ANY4T: 010001 01010 xxx0x */ |
| pc = mips32_bc1_pc (gdbarch, frame, inst, pc + 4, 4); |
| else if (op == 29) |
| /* JALX: 011101 */ |
| /* The new PC will be alternate mode. */ |
| { |
| unsigned long reg; |
| |
| reg = jtype_target (inst) << 2; |
| /* Add 1 to indicate 16-bit mode -- invert ISA mode. */ |
| pc = ((pc + 4) & ~(CORE_ADDR) 0x0fffffff) + reg + 1; |
| } |
| else if (is_octeon_bbit_op (op, gdbarch)) |
| { |
| int bit, branch_if; |
| |
| branch_if = op == 58 || op == 62; |
| bit = itype_rt (inst); |
| |
| /* Take into account the *32 instructions. */ |
| if (op == 54 || op == 62) |
| bit += 32; |
| |
| if (((get_frame_register_signed (frame, |
| itype_rs (inst)) >> bit) & 1) |
| == branch_if) |
| pc += mips32_relative_offset (inst) + 4; |
| else |
| pc += 8; /* After the delay slot. */ |
| } |
| |
| else |
| pc += 4; /* Not a branch, next instruction is easy. */ |
| } |
| else |
| { /* This gets way messy. */ |
| |
| /* Further subdivide into SPECIAL, REGIMM and other. */ |
| switch (op & 0x07) /* Extract bits 28,27,26. */ |
| { |
| case 0: /* SPECIAL */ |
| op = rtype_funct (inst); |
| switch (op) |
| { |
| case 8: /* JR */ |
| case 9: /* JALR */ |
| /* Set PC to that address. */ |
| pc = get_frame_register_signed (frame, rtype_rs (inst)); |
| break; |
| case 12: /* SYSCALL */ |
| { |
| struct gdbarch_tdep *tdep; |
| |
| tdep = gdbarch_tdep (get_frame_arch (frame)); |
| if (tdep->syscall_next_pc != NULL) |
| pc = tdep->syscall_next_pc (frame); |
| else |
| pc += 4; |
| } |
| break; |
| default: |
| pc += 4; |
| } |
| |
| break; /* end SPECIAL */ |
| case 1: /* REGIMM */ |
| { |
| op = itype_rt (inst); /* branch condition */ |
| switch (op) |
| { |
| case 0: /* BLTZ */ |
| case 2: /* BLTZL */ |
| case 16: /* BLTZAL */ |
| case 18: /* BLTZALL */ |
| less_branch: |
| if (get_frame_register_signed (frame, itype_rs (inst)) < 0) |
| pc += mips32_relative_offset (inst) + 4; |
| else |
| pc += 8; /* after the delay slot */ |
| break; |
| case 1: /* BGEZ */ |
| case 3: /* BGEZL */ |
| case 17: /* BGEZAL */ |
| case 19: /* BGEZALL */ |
| if (get_frame_register_signed (frame, itype_rs (inst)) >= 0) |
| pc += mips32_relative_offset (inst) + 4; |
| else |
| pc += 8; /* after the delay slot */ |
| break; |
| case 0x1c: /* BPOSGE32 */ |
| case 0x1e: /* BPOSGE64 */ |
| pc += 4; |
| if (itype_rs (inst) == 0) |
| { |
| unsigned int pos = (op & 2) ? 64 : 32; |
| int dspctl = mips_regnum (gdbarch)->dspctl; |
| |
| if (dspctl == -1) |
| /* No way to handle; it'll most likely trap anyway. */ |
| break; |
| |
| if ((get_frame_register_unsigned (frame, |
| dspctl) & 0x7f) >= pos) |
| pc += mips32_relative_offset (inst); |
| else |
| pc += 4; |
| } |
| break; |
| /* All of the other instructions in the REGIMM category */ |
| default: |
| pc += 4; |
| } |
| } |
| break; /* end REGIMM */ |
| case 2: /* J */ |
| case 3: /* JAL */ |
| { |
| unsigned long reg; |
| reg = jtype_target (inst) << 2; |
| /* Upper four bits get never changed... */ |
| pc = reg + ((pc + 4) & ~(CORE_ADDR) 0x0fffffff); |
| } |
| break; |
| case 4: /* BEQ, BEQL */ |
| equal_branch: |
| if (get_frame_register_signed (frame, itype_rs (inst)) == |
| get_frame_register_signed (frame, itype_rt (inst))) |
| pc += mips32_relative_offset (inst) + 4; |
| else |
| pc += 8; |
| break; |
| case 5: /* BNE, BNEL */ |
| neq_branch: |
| if (get_frame_register_signed (frame, itype_rs (inst)) != |
| get_frame_register_signed (frame, itype_rt (inst))) |
| pc += mips32_relative_offset (inst) + 4; |
| else |
| pc += 8; |
| break; |
| case 6: /* BLEZ, BLEZL */ |
| if (get_frame_register_signed (frame, itype_rs (inst)) <= 0) |
| pc += mips32_relative_offset (inst) + 4; |
| else |
| pc += 8; |
| break; |
| case 7: |
| default: |
| greater_branch: /* BGTZ, BGTZL */ |
| if (get_frame_register_signed (frame, itype_rs (inst)) > 0) |
| pc += mips32_relative_offset (inst) + 4; |
| else |
| pc += 8; |
| break; |
| } /* switch */ |
| } /* else */ |
| return pc; |
| } /* mips32_next_pc */ |
| |
| /* Extract the 7-bit signed immediate offset from the microMIPS instruction |
| INSN. */ |
| |
| static LONGEST |
| micromips_relative_offset7 (ULONGEST insn) |
| { |
| return ((b0s7_imm (insn) ^ 0x40) - 0x40) << 1; |
| } |
| |
| /* Extract the 10-bit signed immediate offset from the microMIPS instruction |
| INSN. */ |
| |
| static LONGEST |
| micromips_relative_offset10 (ULONGEST insn) |
| { |
| return ((b0s10_imm (insn) ^ 0x200) - 0x200) << 1; |
| } |
| |
| /* Extract the 16-bit signed immediate offset from the microMIPS instruction |
| INSN. */ |
| |
| static LONGEST |
| micromips_relative_offset16 (ULONGEST insn) |
| { |
| return ((b0s16_imm (insn) ^ 0x8000) - 0x8000) << 1; |
| } |
| |
| /* Return the size in bytes of the microMIPS instruction at the address PC. */ |
| |
| static int |
| micromips_pc_insn_size (struct gdbarch *gdbarch, CORE_ADDR pc) |
| { |
| ULONGEST insn; |
| |
| insn = mips_fetch_instruction (gdbarch, ISA_MICROMIPS, pc, NULL); |
| return mips_insn_size (ISA_MICROMIPS, insn); |
| } |
| |
| /* Calculate the address of the next microMIPS instruction to execute |
| after the INSN coprocessor 1 conditional branch instruction at the |
| address PC. COUNT denotes the number of coprocessor condition bits |
| examined by the branch. */ |
| |
| static CORE_ADDR |
| micromips_bc1_pc (struct gdbarch *gdbarch, struct frame_info *frame, |
| ULONGEST insn, CORE_ADDR pc, int count) |
| { |
| int fcsr = mips_regnum (gdbarch)->fp_control_status; |
| int cnum = b2s3_cc (insn >> 16) & (count - 1); |
| int tf = b5s5_op (insn >> 16) & 1; |
| int mask = (1 << count) - 1; |
| ULONGEST fcs; |
| int cond; |
| |
| if (fcsr == -1) |
| /* No way to handle; it'll most likely trap anyway. */ |
| return pc; |
| |
| fcs = get_frame_register_unsigned (frame, fcsr); |
| cond = ((fcs >> 24) & 0xfe) | ((fcs >> 23) & 0x01); |
| |
| if (((cond >> cnum) & mask) != mask * !tf) |
| pc += micromips_relative_offset16 (insn); |
| else |
| pc += micromips_pc_insn_size (gdbarch, pc); |
| |
| return pc; |
| } |
| |
| /* Calculate the address of the next microMIPS instruction to execute |
| after the instruction at the address PC. */ |
| |
| static CORE_ADDR |
| micromips_next_pc (struct frame_info *frame, CORE_ADDR pc) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| ULONGEST insn; |
| |
| insn = mips_fetch_instruction (gdbarch, ISA_MICROMIPS, pc, NULL); |
| pc += MIPS_INSN16_SIZE; |
| switch (mips_insn_size (ISA_MICROMIPS, insn)) |
| { |
| /* 32-bit instructions. */ |
| case 2 * MIPS_INSN16_SIZE: |
| insn <<= 16; |
| insn |= mips_fetch_instruction (gdbarch, ISA_MICROMIPS, pc, NULL); |
| pc += MIPS_INSN16_SIZE; |
| switch (micromips_op (insn >> 16)) |
| { |
| case 0x00: /* POOL32A: bits 000000 */ |
| if (b0s6_op (insn) == 0x3c |
| /* POOL32Axf: bits 000000 ... 111100 */ |
| && (b6s10_ext (insn) & 0x2bf) == 0x3c) |
| /* JALR, JALR.HB: 000000 000x111100 111100 */ |
| /* JALRS, JALRS.HB: 000000 010x111100 111100 */ |
| pc = get_frame_register_signed (frame, b0s5_reg (insn >> 16)); |
| break; |
| |
| case 0x10: /* POOL32I: bits 010000 */ |
| switch (b5s5_op (insn >> 16)) |
| { |
| case 0x00: /* BLTZ: bits 010000 00000 */ |
| case 0x01: /* BLTZAL: bits 010000 00001 */ |
| case 0x11: /* BLTZALS: bits 010000 10001 */ |
| if (get_frame_register_signed (frame, |
| b0s5_reg (insn >> 16)) < 0) |
| pc += micromips_relative_offset16 (insn); |
| else |
| pc += micromips_pc_insn_size (gdbarch, pc); |
| break; |
| |
| case 0x02: /* BGEZ: bits 010000 00010 */ |
| case 0x03: /* BGEZAL: bits 010000 00011 */ |
| case 0x13: /* BGEZALS: bits 010000 10011 */ |
| if (get_frame_register_signed (frame, |
| b0s5_reg (insn >> 16)) >= 0) |
| pc += micromips_relative_offset16 (insn); |
| else |
| pc += micromips_pc_insn_size (gdbarch, pc); |
| break; |
| |
| case 0x04: /* BLEZ: bits 010000 00100 */ |
| if (get_frame_register_signed (frame, |
| b0s5_reg (insn >> 16)) <= 0) |
| pc += micromips_relative_offset16 (insn); |
| else |
| pc += micromips_pc_insn_size (gdbarch, pc); |
| break; |
| |
| case 0x05: /* BNEZC: bits 010000 00101 */ |
| if (get_frame_register_signed (frame, |
| b0s5_reg (insn >> 16)) != 0) |
| pc += micromips_relative_offset16 (insn); |
| break; |
| |
| case 0x06: /* BGTZ: bits 010000 00110 */ |
| if (get_frame_register_signed (frame, |
| b0s5_reg (insn >> 16)) > 0) |
| pc += micromips_relative_offset16 (insn); |
| else |
| pc += micromips_pc_insn_size (gdbarch, pc); |
| break; |
| |
| case 0x07: /* BEQZC: bits 010000 00111 */ |
| if (get_frame_register_signed (frame, |
| b0s5_reg (insn >> 16)) == 0) |
| pc += micromips_relative_offset16 (insn); |
| break; |
| |
| case 0x14: /* BC2F: bits 010000 10100 xxx00 */ |
| case 0x15: /* BC2T: bits 010000 10101 xxx00 */ |
| if (((insn >> 16) & 0x3) == 0x0) |
| /* BC2F, BC2T: don't know how to handle these. */ |
| break; |
| break; |
| |
| case 0x1a: /* BPOSGE64: bits 010000 11010 */ |
| case 0x1b: /* BPOSGE32: bits 010000 11011 */ |
| { |
| unsigned int pos = (b5s5_op (insn >> 16) & 1) ? 32 : 64; |
| int dspctl = mips_regnum (gdbarch)->dspctl; |
| |
| if (dspctl == -1) |
| /* No way to handle; it'll most likely trap anyway. */ |
| break; |
| |
| if ((get_frame_register_unsigned (frame, |
| dspctl) & 0x7f) >= pos) |
| pc += micromips_relative_offset16 (insn); |
| else |
| pc += micromips_pc_insn_size (gdbarch, pc); |
| } |
| break; |
| |
| case 0x1c: /* BC1F: bits 010000 11100 xxx00 */ |
| /* BC1ANY2F: bits 010000 11100 xxx01 */ |
| case 0x1d: /* BC1T: bits 010000 11101 xxx00 */ |
| /* BC1ANY2T: bits 010000 11101 xxx01 */ |
| if (((insn >> 16) & 0x2) == 0x0) |
| pc = micromips_bc1_pc (gdbarch, frame, insn, pc, |
| ((insn >> 16) & 0x1) + 1); |
| break; |
| |
| case 0x1e: /* BC1ANY4F: bits 010000 11110 xxx01 */ |
| case 0x1f: /* BC1ANY4T: bits 010000 11111 xxx01 */ |
| if (((insn >> 16) & 0x3) == 0x1) |
| pc = micromips_bc1_pc (gdbarch, frame, insn, pc, 4); |
| break; |
| } |
| break; |
| |
| case 0x1d: /* JALS: bits 011101 */ |
| case 0x35: /* J: bits 110101 */ |
| case 0x3d: /* JAL: bits 111101 */ |
| pc = ((pc | 0x7fffffe) ^ 0x7fffffe) | (b0s26_imm (insn) << 1); |
| break; |
| |
| case 0x25: /* BEQ: bits 100101 */ |
| if (get_frame_register_signed (frame, b0s5_reg (insn >> 16)) |
| == get_frame_register_signed (frame, b5s5_reg (insn >> 16))) |
| pc += micromips_relative_offset16 (insn); |
| else |
| pc += micromips_pc_insn_size (gdbarch, pc); |
| break; |
| |
| case 0x2d: /* BNE: bits 101101 */ |
| if (get_frame_register_signed (frame, b0s5_reg (insn >> 16)) |
| != get_frame_register_signed (frame, b5s5_reg (insn >> 16))) |
| pc += micromips_relative_offset16 (insn); |
| else |
| pc += micromips_pc_insn_size (gdbarch, pc); |
| break; |
| |
| case 0x3c: /* JALX: bits 111100 */ |
| pc = ((pc | 0xfffffff) ^ 0xfffffff) | (b0s26_imm (insn) << 2); |
| break; |
| } |
| break; |
| |
| /* 16-bit instructions. */ |
| case MIPS_INSN16_SIZE: |
| switch (micromips_op (insn)) |
| { |
| case 0x11: /* POOL16C: bits 010001 */ |
| if ((b5s5_op (insn) & 0x1c) == 0xc) |
| /* JR16, JRC, JALR16, JALRS16: 010001 011xx */ |
| pc = get_frame_register_signed (frame, b0s5_reg (insn)); |
| else if (b5s5_op (insn) == 0x18) |
| /* JRADDIUSP: bits 010001 11000 */ |
| pc = get_frame_register_signed (frame, MIPS_RA_REGNUM); |
| break; |
| |
| case 0x23: /* BEQZ16: bits 100011 */ |
| { |
| int rs = mips_reg3_to_reg[b7s3_reg (insn)]; |
| |
| if (get_frame_register_signed (frame, rs) == 0) |
| pc += micromips_relative_offset7 (insn); |
| else |
| pc += micromips_pc_insn_size (gdbarch, pc); |
| } |
| break; |
| |
| case 0x2b: /* BNEZ16: bits 101011 */ |
| { |
| int rs = mips_reg3_to_reg[b7s3_reg (insn)]; |
| |
| if (get_frame_register_signed (frame, rs) != 0) |
| pc += micromips_relative_offset7 (insn); |
| else |
| pc += micromips_pc_insn_size (gdbarch, pc); |
| } |
| break; |
| |
| case 0x33: /* B16: bits 110011 */ |
| pc += micromips_relative_offset10 (insn); |
| break; |
| } |
| break; |
| } |
| |
| return pc; |
| } |
| |
| /* Decoding the next place to set a breakpoint is irregular for the |
| mips 16 variant, but fortunately, there fewer instructions. We have |
| to cope ith extensions for 16 bit instructions and a pair of actual |
| 32 bit instructions. We dont want to set a single step instruction |
| on the extend instruction either. */ |
| |
| /* Lots of mips16 instruction formats */ |
| /* Predicting jumps requires itype,ritype,i8type |
| and their extensions extItype,extritype,extI8type. */ |
| enum mips16_inst_fmts |
| { |
| itype, /* 0 immediate 5,10 */ |
| ritype, /* 1 5,3,8 */ |
| rrtype, /* 2 5,3,3,5 */ |
| rritype, /* 3 5,3,3,5 */ |
| rrrtype, /* 4 5,3,3,3,2 */ |
| rriatype, /* 5 5,3,3,1,4 */ |
| shifttype, /* 6 5,3,3,3,2 */ |
| i8type, /* 7 5,3,8 */ |
| i8movtype, /* 8 5,3,3,5 */ |
| i8mov32rtype, /* 9 5,3,5,3 */ |
| i64type, /* 10 5,3,8 */ |
| ri64type, /* 11 5,3,3,5 */ |
| jalxtype, /* 12 5,1,5,5,16 - a 32 bit instruction */ |
| exiItype, /* 13 5,6,5,5,1,1,1,1,1,1,5 */ |
| extRitype, /* 14 5,6,5,5,3,1,1,1,5 */ |
| extRRItype, /* 15 5,5,5,5,3,3,5 */ |
| extRRIAtype, /* 16 5,7,4,5,3,3,1,4 */ |
| EXTshifttype, /* 17 5,5,1,1,1,1,1,1,5,3,3,1,1,1,2 */ |
| extI8type, /* 18 5,6,5,5,3,1,1,1,5 */ |
| extI64type, /* 19 5,6,5,5,3,1,1,1,5 */ |
| extRi64type, /* 20 5,6,5,5,3,3,5 */ |
| extshift64type /* 21 5,5,1,1,1,1,1,1,5,1,1,1,3,5 */ |
| }; |
| /* I am heaping all the fields of the formats into one structure and |
| then, only the fields which are involved in instruction extension. */ |
| struct upk_mips16 |
| { |
| CORE_ADDR offset; |
| unsigned int regx; /* Function in i8 type. */ |
| unsigned int regy; |
| }; |
| |
| |
| /* The EXT-I, EXT-ri nad EXT-I8 instructions all have the same format |
| for the bits which make up the immediate extension. */ |
| |
| static CORE_ADDR |
| extended_offset (unsigned int extension) |
| { |
| CORE_ADDR value; |
| |
| value = (extension >> 16) & 0x1f; /* Extract 15:11. */ |
| value = value << 6; |
| value |= (extension >> 21) & 0x3f; /* Extract 10:5. */ |
| value = value << 5; |
| value |= extension & 0x1f; /* Extract 4:0. */ |
| |
| return value; |
| } |
| |
| /* Only call this function if you know that this is an extendable |
| instruction. It won't malfunction, but why make excess remote memory |
| references? If the immediate operands get sign extended or something, |
| do it after the extension is performed. */ |
| /* FIXME: Every one of these cases needs to worry about sign extension |
| when the offset is to be used in relative addressing. */ |
| |
| static unsigned int |
| fetch_mips_16 (struct gdbarch *gdbarch, CORE_ADDR pc) |
| { |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| gdb_byte buf[8]; |
| |
| pc = unmake_compact_addr (pc); /* Clear the low order bit. */ |
| target_read_memory (pc, buf, 2); |
| return extract_unsigned_integer (buf, 2, byte_order); |
| } |
| |
| static void |
| unpack_mips16 (struct gdbarch *gdbarch, CORE_ADDR pc, |
| unsigned int extension, |
| unsigned int inst, |
| enum mips16_inst_fmts insn_format, struct upk_mips16 *upk) |
| { |
| CORE_ADDR offset; |
| int regx; |
| int regy; |
| switch (insn_format) |
| { |
| case itype: |
| { |
| CORE_ADDR value; |
| if (extension) |
| { |
| value = extended_offset ((extension << 16) | inst); |
| value = (value ^ 0x8000) - 0x8000; /* Sign-extend. */ |
| } |
| else |
| { |
| value = inst & 0x7ff; |
| value = (value ^ 0x400) - 0x400; /* Sign-extend. */ |
| } |
| offset = value; |
| regx = -1; |
| regy = -1; |
| } |
| break; |
| case ritype: |
| case i8type: |
| { /* A register identifier and an offset. */ |
| /* Most of the fields are the same as I type but the |
| immediate value is of a different length. */ |
| CORE_ADDR value; |
| if (extension) |
| { |
| value = extended_offset ((extension << 16) | inst); |
| value = (value ^ 0x8000) - 0x8000; /* Sign-extend. */ |
| } |
| else |
| { |
| value = inst & 0xff; /* 8 bits */ |
| value = (value ^ 0x80) - 0x80; /* Sign-extend. */ |
| } |
| offset = value; |
| regx = (inst >> 8) & 0x07; /* i8 funct */ |
| regy = -1; |
| break; |
| } |
| case jalxtype: |
| { |
| unsigned long value; |
| unsigned int nexthalf; |
| value = ((inst & 0x1f) << 5) | ((inst >> 5) & 0x1f); |
| value = value << 16; |
| nexthalf = mips_fetch_instruction (gdbarch, ISA_MIPS16, pc + 2, NULL); |
| /* Low bit still set. */ |
| value |= nexthalf; |
| offset = value; |
| regx = -1; |
| regy = -1; |
| break; |
| } |
| default: |
| internal_error (__FILE__, __LINE__, _("bad switch")); |
| } |
| upk->offset = offset; |
| upk->regx = regx; |
| upk->regy = regy; |
| } |
| |
| |
| /* Calculate the destination of a branch whose 16-bit opcode word is at PC, |
| and having a signed 16-bit OFFSET. */ |
| |
| static CORE_ADDR |
| add_offset_16 (CORE_ADDR pc, int offset) |
| { |
| return pc + (offset << 1) + 2; |
| } |
| |
| static CORE_ADDR |
| extended_mips16_next_pc (struct frame_info *frame, CORE_ADDR pc, |
| unsigned int extension, unsigned int insn) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| int op = (insn >> 11); |
| switch (op) |
| { |
| case 2: /* Branch */ |
| { |
| struct upk_mips16 upk; |
| unpack_mips16 (gdbarch, pc, extension, insn, itype, &upk); |
| pc = add_offset_16 (pc, upk.offset); |
| break; |
| } |
| case 3: /* JAL , JALX - Watch out, these are 32 bit |
| instructions. */ |
| { |
| struct upk_mips16 upk; |
| unpack_mips16 (gdbarch, pc, extension, insn, jalxtype, &upk); |
| pc = ((pc + 2) & (~(CORE_ADDR) 0x0fffffff)) | (upk.offset << 2); |
| if ((insn >> 10) & 0x01) /* Exchange mode */ |
| pc = pc & ~0x01; /* Clear low bit, indicate 32 bit mode. */ |
| else |
| pc |= 0x01; |
| break; |
| } |
| case 4: /* beqz */ |
| { |
| struct upk_mips16 upk; |
| int reg; |
| unpack_mips16 (gdbarch, pc, extension, insn, ritype, &upk); |
| reg = get_frame_register_signed (frame, mips_reg3_to_reg[upk.regx]); |
| if (reg == 0) |
| pc = add_offset_16 (pc, upk.offset); |
| else |
| pc += 2; |
| break; |
| } |
| case 5: /* bnez */ |
| { |
| struct upk_mips16 upk; |
| int reg; |
| unpack_mips16 (gdbarch, pc, extension, insn, ritype, &upk); |
| reg = get_frame_register_signed (frame, mips_reg3_to_reg[upk.regx]); |
| if (reg != 0) |
| pc = add_offset_16 (pc, upk.offset); |
| else |
| pc += 2; |
| break; |
| } |
| case 12: /* I8 Formats btez btnez */ |
| { |
| struct upk_mips16 upk; |
| int reg; |
| unpack_mips16 (gdbarch, pc, extension, insn, i8type, &upk); |
| /* upk.regx contains the opcode */ |
| reg = get_frame_register_signed (frame, 24); /* Test register is 24 */ |
| if (((upk.regx == 0) && (reg == 0)) /* BTEZ */ |
| || ((upk.regx == 1) && (reg != 0))) /* BTNEZ */ |
| pc = add_offset_16 (pc, upk.offset); |
| else |
| pc += 2; |
| break; |
| } |
| case 29: /* RR Formats JR, JALR, JALR-RA */ |
| { |
| struct upk_mips16 upk; |
| /* upk.fmt = rrtype; */ |
| op = insn & 0x1f; |
| if (op == 0) |
| { |
| int reg; |
| upk.regx = (insn >> 8) & 0x07; |
| upk.regy = (insn >> 5) & 0x07; |
| if ((upk.regy & 1) == 0) |
| reg = mips_reg3_to_reg[upk.regx]; |
| else |
| reg = 31; /* Function return instruction. */ |
| pc = get_frame_register_signed (frame, reg); |
| } |
| else |
| pc += 2; |
| break; |
| } |
| case 30: |
| /* This is an instruction extension. Fetch the real instruction |
| (which follows the extension) and decode things based on |
| that. */ |
| { |
| pc += 2; |
| pc = extended_mips16_next_pc (frame, pc, insn, |
| fetch_mips_16 (gdbarch, pc)); |
| break; |
| } |
| default: |
| { |
| pc += 2; |
| break; |
| } |
| } |
| return pc; |
| } |
| |
| static CORE_ADDR |
| mips16_next_pc (struct frame_info *frame, CORE_ADDR pc) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| unsigned int insn = fetch_mips_16 (gdbarch, pc); |
| return extended_mips16_next_pc (frame, pc, 0, insn); |
| } |
| |
| /* The mips_next_pc function supports single_step when the remote |
| target monitor or stub is not developed enough to do a single_step. |
| It works by decoding the current instruction and predicting where a |
| branch will go. This isn't hard because all the data is available. |
| The MIPS32, MIPS16 and microMIPS variants are quite different. */ |
| static CORE_ADDR |
| mips_next_pc (struct frame_info *frame, CORE_ADDR pc) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| |
| if (mips_pc_is_mips16 (gdbarch, pc)) |
| return mips16_next_pc (frame, pc); |
| else if (mips_pc_is_micromips (gdbarch, pc)) |
| return micromips_next_pc (frame, pc); |
| else |
| return mips32_next_pc (frame, pc); |
| } |
| |
| /* Return non-zero if the MIPS16 instruction INSN is a compact branch |
| or jump. */ |
| |
| static int |
| mips16_instruction_is_compact_branch (unsigned short insn) |
| { |
| switch (insn & 0xf800) |
| { |
| case 0xe800: |
| return (insn & 0x009f) == 0x80; /* JALRC/JRC */ |
| case 0x6000: |
| return (insn & 0x0600) == 0; /* BTNEZ/BTEQZ */ |
| case 0x2800: /* BNEZ */ |
| case 0x2000: /* BEQZ */ |
| case 0x1000: /* B */ |
| return 1; |
| default: |
| return 0; |
| } |
| } |
| |
| /* Return non-zero if the microMIPS instruction INSN is a compact branch |
| or jump. */ |
| |
| static int |
| micromips_instruction_is_compact_branch (unsigned short insn) |
| { |
| switch (micromips_op (insn)) |
| { |
| case 0x11: /* POOL16C: bits 010001 */ |
| return (b5s5_op (insn) == 0x18 |
| /* JRADDIUSP: bits 010001 11000 */ |
| || b5s5_op (insn) == 0xd); |
| /* JRC: bits 010011 01101 */ |
| case 0x10: /* POOL32I: bits 010000 */ |
| return (b5s5_op (insn) & 0x1d) == 0x5; |
| /* BEQZC/BNEZC: bits 010000 001x1 */ |
| default: |
| return 0; |
| } |
| } |
| |
| struct mips_frame_cache |
| { |
| CORE_ADDR base; |
| struct trad_frame_saved_reg *saved_regs; |
| }; |
| |
| /* Set a register's saved stack address in temp_saved_regs. If an |
| address has already been set for this register, do nothing; this |
| way we will only recognize the first save of a given register in a |
| function prologue. |
| |
| For simplicity, save the address in both [0 .. gdbarch_num_regs) and |
| [gdbarch_num_regs .. 2*gdbarch_num_regs). |
| Strictly speaking, only the second range is used as it is only second |
| range (the ABI instead of ISA registers) that comes into play when finding |
| saved registers in a frame. */ |
| |
| static void |
| set_reg_offset (struct gdbarch *gdbarch, struct mips_frame_cache *this_cache, |
| int regnum, CORE_ADDR offset) |
| { |
| if (this_cache != NULL |
| && this_cache->saved_regs[regnum].addr == -1) |
| { |
| this_cache->saved_regs[regnum + 0 * gdbarch_num_regs (gdbarch)].addr |
| = offset; |
| this_cache->saved_regs[regnum + 1 * gdbarch_num_regs (gdbarch)].addr |
| = offset; |
| } |
| } |
| |
| |
| /* Fetch the immediate value from a MIPS16 instruction. |
| If the previous instruction was an EXTEND, use it to extend |
| the upper bits of the immediate value. This is a helper function |
| for mips16_scan_prologue. */ |
| |
| static int |
| mips16_get_imm (unsigned short prev_inst, /* previous instruction */ |
| unsigned short inst, /* current instruction */ |
| int nbits, /* number of bits in imm field */ |
| int scale, /* scale factor to be applied to imm */ |
| int is_signed) /* is the imm field signed? */ |
| { |
| int offset; |
| |
| if ((prev_inst & 0xf800) == 0xf000) /* prev instruction was EXTEND? */ |
| { |
| offset = ((prev_inst & 0x1f) << 11) | (prev_inst & 0x7e0); |
| if (offset & 0x8000) /* check for negative extend */ |
| offset = 0 - (0x10000 - (offset & 0xffff)); |
| return offset | (inst & 0x1f); |
| } |
| else |
| { |
| int max_imm = 1 << nbits; |
| int mask = max_imm - 1; |
| int sign_bit = max_imm >> 1; |
| |
| offset = inst & mask; |
| if (is_signed && (offset & sign_bit)) |
| offset = 0 - (max_imm - offset); |
| return offset * scale; |
| } |
| } |
| |
| |
| /* Analyze the function prologue from START_PC to LIMIT_PC. Builds |
| the associated FRAME_CACHE if not null. |
| Return the address of the first instruction past the prologue. */ |
| |
| static CORE_ADDR |
| mips16_scan_prologue (struct gdbarch *gdbarch, |
| CORE_ADDR start_pc, CORE_ADDR limit_pc, |
| struct frame_info *this_frame, |
| struct mips_frame_cache *this_cache) |
| { |
| int prev_non_prologue_insn = 0; |
| int this_non_prologue_insn; |
| int non_prologue_insns = 0; |
| CORE_ADDR prev_pc; |
| CORE_ADDR cur_pc; |
| CORE_ADDR frame_addr = 0; /* Value of $r17, used as frame pointer. */ |
| CORE_ADDR sp; |
| long frame_offset = 0; /* Size of stack frame. */ |
| long frame_adjust = 0; /* Offset of FP from SP. */ |
| int frame_reg = MIPS_SP_REGNUM; |
| unsigned short prev_inst = 0; /* saved copy of previous instruction. */ |
| unsigned inst = 0; /* current instruction */ |
| unsigned entry_inst = 0; /* the entry instruction */ |
| unsigned save_inst = 0; /* the save instruction */ |
| int prev_delay_slot = 0; |
| int in_delay_slot; |
| int reg, offset; |
| |
| int extend_bytes = 0; |
| int prev_extend_bytes = 0; |
| CORE_ADDR end_prologue_addr; |
| |
| /* Can be called when there's no process, and hence when there's no |
| THIS_FRAME. */ |
| if (this_frame != NULL) |
| sp = get_frame_register_signed (this_frame, |
| gdbarch_num_regs (gdbarch) |
| + MIPS_SP_REGNUM); |
| else |
| sp = 0; |
| |
| if (limit_pc > start_pc + 200) |
| limit_pc = start_pc + 200; |
| prev_pc = start_pc; |
| |
| /* Permit at most one non-prologue non-control-transfer instruction |
| in the middle which may have been reordered by the compiler for |
| optimisation. */ |
| for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += MIPS_INSN16_SIZE) |
| { |
| this_non_prologue_insn = 0; |
| in_delay_slot = 0; |
| |
| /* Save the previous instruction. If it's an EXTEND, we'll extract |
| the immediate offset extension from it in mips16_get_imm. */ |
| prev_inst = inst; |
| |
| /* Fetch and decode the instruction. */ |
| inst = (unsigned short) mips_fetch_instruction (gdbarch, ISA_MIPS16, |
| cur_pc, NULL); |
| |
| /* Normally we ignore extend instructions. However, if it is |
| not followed by a valid prologue instruction, then this |
| instruction is not part of the prologue either. We must |
| remember in this case to adjust the end_prologue_addr back |
| over the extend. */ |
| if ((inst & 0xf800) == 0xf000) /* extend */ |
| { |
| extend_bytes = MIPS_INSN16_SIZE; |
| continue; |
| } |
| |
| prev_extend_bytes = extend_bytes; |
| extend_bytes = 0; |
| |
| if ((inst & 0xff00) == 0x6300 /* addiu sp */ |
| || (inst & 0xff00) == 0xfb00) /* daddiu sp */ |
| { |
| offset = mips16_get_imm (prev_inst, inst, 8, 8, 1); |
| if (offset < 0) /* Negative stack adjustment? */ |
| frame_offset -= offset; |
| else |
| /* Exit loop if a positive stack adjustment is found, which |
| usually means that the stack cleanup code in the function |
| epilogue is reached. */ |
| break; |
| } |
| else if ((inst & 0xf800) == 0xd000) /* sw reg,n($sp) */ |
| { |
| offset = mips16_get_imm (prev_inst, inst, 8, 4, 0); |
| reg = mips_reg3_to_reg[(inst & 0x700) >> 8]; |
| set_reg_offset (gdbarch, this_cache, reg, sp + offset); |
| } |
| else if ((inst & 0xff00) == 0xf900) /* sd reg,n($sp) */ |
| { |
| offset = mips16_get_imm (prev_inst, inst, 5, 8, 0); |
| reg = mips_reg3_to_reg[(inst & 0xe0) >> 5]; |
| set_reg_offset (gdbarch, this_cache, reg, sp + offset); |
| } |
| else if ((inst & 0xff00) == 0x6200) /* sw $ra,n($sp) */ |
| { |
| offset = mips16_get_imm (prev_inst, inst, 8, 4, 0); |
| set_reg_offset (gdbarch, this_cache, MIPS_RA_REGNUM, sp + offset); |
| } |
| else if ((inst & 0xff00) == 0xfa00) /* sd $ra,n($sp) */ |
| { |
| offset = mips16_get_imm (prev_inst, inst, 8, 8, 0); |
| set_reg_offset (gdbarch, this_cache, MIPS_RA_REGNUM, sp + offset); |
| } |
| else if (inst == 0x673d) /* move $s1, $sp */ |
| { |
| frame_addr = sp; |
| frame_reg = 17; |
| } |
| else if ((inst & 0xff00) == 0x0100) /* addiu $s1,sp,n */ |
| { |
| offset = mips16_get_imm (prev_inst, inst, 8, 4, 0); |
| frame_addr = sp + offset; |
| frame_reg = 17; |
| frame_adjust = offset; |
| } |
| else if ((inst & 0xFF00) == 0xd900) /* sw reg,offset($s1) */ |
| { |
| offset = mips16_get_imm (prev_inst, inst, 5, 4, 0); |
| reg = mips_reg3_to_reg[(inst & 0xe0) >> 5]; |
| set_reg_offset (gdbarch, this_cache, reg, frame_addr + offset); |
| } |
| else if ((inst & 0xFF00) == 0x7900) /* sd reg,offset($s1) */ |
| { |
| offset = mips16_get_imm (prev_inst, inst, 5, 8, 0); |
| reg = mips_reg3_to_reg[(inst & 0xe0) >> 5]; |
| set_reg_offset (gdbarch, this_cache, reg, frame_addr + offset); |
| } |
| else if ((inst & 0xf81f) == 0xe809 |
| && (inst & 0x700) != 0x700) /* entry */ |
| entry_inst = inst; /* Save for later processing. */ |
| else if ((inst & 0xff80) == 0x6480) /* save */ |
| { |
| save_inst = inst; /* Save for later processing. */ |
| if (prev_extend_bytes) /* extend */ |
| save_inst |= prev_inst << 16; |
| } |
| else if ((inst & 0xff1c) == 0x6704) /* move reg,$a0-$a3 */ |
| { |
| /* This instruction is part of the prologue, but we don't |
| need to do anything special to handle it. */ |
| } |
| else if (mips16_instruction_has_delay_slot (inst, 0)) |
| /* JAL/JALR/JALX/JR */ |
| { |
| /* The instruction in the delay slot can be a part |
| of the prologue, so move forward once more. */ |
| in_delay_slot = 1; |
| if (mips16_instruction_has_delay_slot (inst, 1)) |
| /* JAL/JALX */ |
| { |
| prev_extend_bytes = MIPS_INSN16_SIZE; |
| cur_pc += MIPS_INSN16_SIZE; /* 32-bit instruction */ |
| } |
| } |
| else |
| { |
| this_non_prologue_insn = 1; |
| } |
| |
| non_prologue_insns += this_non_prologue_insn; |
| |
| /* A jump or branch, or enough non-prologue insns seen? If so, |
| then we must have reached the end of the prologue by now. */ |
| if (prev_delay_slot || non_prologue_insns > 1 |
| || mips16_instruction_is_compact_branch (inst)) |
| break; |
| |
| prev_non_prologue_insn = this_non_prologue_insn; |
| prev_delay_slot = in_delay_slot; |
| prev_pc = cur_pc - prev_extend_bytes; |
| } |
| |
| /* The entry instruction is typically the first instruction in a function, |
| and it stores registers at offsets relative to the value of the old SP |
| (before the prologue). But the value of the sp parameter to this |
| function is the new SP (after the prologue has been executed). So we |
| can't calculate those offsets until we've seen the entire prologue, |
| and can calculate what the old SP must have been. */ |
| if (entry_inst != 0) |
| { |
| int areg_count = (entry_inst >> 8) & 7; |
| int sreg_count = (entry_inst >> 6) & 3; |
| |
| /* The entry instruction always subtracts 32 from the SP. */ |
| frame_offset += 32; |
| |
| /* Now we can calculate what the SP must have been at the |
| start of the function prologue. */ |
| sp += frame_offset; |
| |
| /* Check if a0-a3 were saved in the caller's argument save area. */ |
| for (reg = 4, offset = 0; reg < areg_count + 4; reg++) |
| { |
| set_reg_offset (gdbarch, this_cache, reg, sp + offset); |
| offset += mips_abi_regsize (gdbarch); |
| } |
| |
| /* Check if the ra register was pushed on the stack. */ |
| offset = -4; |
| if (entry_inst & 0x20) |
| { |
| set_reg_offset (gdbarch, this_cache, MIPS_RA_REGNUM, sp + offset); |
| offset -= mips_abi_regsize (gdbarch); |
| } |
| |
| /* Check if the s0 and s1 registers were pushed on the stack. */ |
| for (reg = 16; reg < sreg_count + 16; reg++) |
| { |
| set_reg_offset (gdbarch, this_cache, reg, sp + offset); |
| offset -= mips_abi_regsize (gdbarch); |
| } |
| } |
| |
| /* The SAVE instruction is similar to ENTRY, except that defined by the |
| MIPS16e ASE of the MIPS Architecture. Unlike with ENTRY though, the |
| size of the frame is specified as an immediate field of instruction |
| and an extended variation exists which lets additional registers and |
| frame space to be specified. The instruction always treats registers |
| as 32-bit so its usefulness for 64-bit ABIs is questionable. */ |
| if (save_inst != 0 && mips_abi_regsize (gdbarch) == 4) |
| { |
| static int args_table[16] = { |
| 0, 0, 0, 0, 1, 1, 1, 1, |
| 2, 2, 2, 0, 3, 3, 4, -1, |
| }; |
| static int astatic_table[16] = { |
| 0, 1, 2, 3, 0, 1, 2, 3, |
| 0, 1, 2, 4, 0, 1, 0, -1, |
| }; |
| int aregs = (save_inst >> 16) & 0xf; |
| int xsregs = (save_inst >> 24) & 0x7; |
| int args = args_table[aregs]; |
| int astatic = astatic_table[aregs]; |
| long frame_size; |
| |
| if (args < 0) |
| { |
| warning (_("Invalid number of argument registers encoded in SAVE.")); |
| args = 0; |
| } |
| if (astatic < 0) |
| { |
| warning (_("Invalid number of static registers encoded in SAVE.")); |
| astatic = 0; |
| } |
| |
| /* For standard SAVE the frame size of 0 means 128. */ |
| frame_size = ((save_inst >> 16) & 0xf0) | (save_inst & 0xf); |
| if (frame_size == 0 && (save_inst >> 16) == 0) |
| frame_size = 16; |
| frame_size *= 8; |
| frame_offset += frame_size; |
| |
| /* Now we can calculate what the SP must have been at the |
| start of the function prologue. */ |
| sp += frame_offset; |
| |
| /* Check if A0-A3 were saved in the caller's argument save area. */ |
| for (reg = MIPS_A0_REGNUM, offset = 0; reg < args + 4; reg++) |
| { |
| set_reg_offset (gdbarch, this_cache, reg, sp + offset); |
| offset += mips_abi_regsize (gdbarch); |
| } |
| |
| offset = -4; |
| |
| /* Check if the RA register was pushed on the stack. */ |
| if (save_inst & 0x40) |
| { |
| set_reg_offset (gdbarch, this_cache, MIPS_RA_REGNUM, sp + offset); |
| offset -= mips_abi_regsize (gdbarch); |
| } |
| |
| /* Check if the S8 register was pushed on the stack. */ |
| if (xsregs > 6) |
| { |
| set_reg_offset (gdbarch, this_cache, 30, sp + offset); |
| offset -= mips_abi_regsize (gdbarch); |
| xsregs--; |
| } |
| /* Check if S2-S7 were pushed on the stack. */ |
| for (reg = 18 + xsregs - 1; reg > 18 - 1; reg--) |
| { |
| set_reg_offset (gdbarch, this_cache, reg, sp + offset); |
| offset -= mips_abi_regsize (gdbarch); |
| } |
| |
| /* Check if the S1 register was pushed on the stack. */ |
| if (save_inst & 0x10) |
| { |
| set_reg_offset (gdbarch, this_cache, 17, sp + offset); |
| offset -= mips_abi_regsize (gdbarch); |
| } |
| /* Check if the S0 register was pushed on the stack. */ |
| if (save_inst & 0x20) |
| { |
| set_reg_offset (gdbarch, this_cache, 16, sp + offset); |
| offset -= mips_abi_regsize (gdbarch); |
| } |
| |
| /* Check if A0-A3 were pushed on the stack. */ |
| for (reg = MIPS_A0_REGNUM + 3; reg > MIPS_A0_REGNUM + 3 - astatic; reg--) |
| { |
| set_reg_offset (gdbarch, this_cache, reg, sp + offset); |
| offset -= mips_abi_regsize (gdbarch); |
| } |
| } |
| |
| if (this_cache != NULL) |
| { |
| this_cache->base = |
| (get_frame_register_signed (this_frame, |
| gdbarch_num_regs (gdbarch) + frame_reg) |
| + frame_offset - frame_adjust); |
| /* FIXME: brobecker/2004-10-10: Just as in the mips32 case, we should |
| be able to get rid of the assignment below, evetually. But it's |
| still needed for now. */ |
| this_cache->saved_regs[gdbarch_num_regs (gdbarch) |
| + mips_regnum (gdbarch)->pc] |
| = this_cache->saved_regs[gdbarch_num_regs (gdbarch) + MIPS_RA_REGNUM]; |
| } |
| |
| /* Set end_prologue_addr to the address of the instruction immediately |
| after the last one we scanned. Unless the last one looked like a |
| non-prologue instruction (and we looked ahead), in which case use |
| its address instead. */ |
| end_prologue_addr = (prev_non_prologue_insn || prev_delay_slot |
| ? prev_pc : cur_pc - prev_extend_bytes); |
| |
| return end_prologue_addr; |
| } |
| |
| /* Heuristic unwinder for 16-bit MIPS instruction set (aka MIPS16). |
| Procedures that use the 32-bit instruction set are handled by the |
| mips_insn32 unwinder. */ |
| |
| static struct mips_frame_cache * |
| mips_insn16_frame_cache (struct frame_info *this_frame, void **this_cache) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (this_frame); |
| struct mips_frame_cache *cache; |
| |
| if ((*this_cache) != NULL) |
| return (struct mips_frame_cache *) (*this_cache); |
| cache = FRAME_OBSTACK_ZALLOC (struct mips_frame_cache); |
| (*this_cache) = cache; |
| cache->saved_regs = trad_frame_alloc_saved_regs (this_frame); |
| |
| /* Analyze the function prologue. */ |
| { |
| const CORE_ADDR pc = get_frame_address_in_block (this_frame); |
| CORE_ADDR start_addr; |
| |
| find_pc_partial_function (pc, NULL, &start_addr, NULL); |
| if (start_addr == 0) |
| start_addr = heuristic_proc_start (gdbarch, pc); |
| /* We can't analyze the prologue if we couldn't find the begining |
| of the function. */ |
| if (start_addr == 0) |
| return cache; |
| |
| mips16_scan_prologue (gdbarch, start_addr, pc, this_frame, |
| (struct mips_frame_cache *) *this_cache); |
| } |
| |
| /* gdbarch_sp_regnum contains the value and not the address. */ |
| trad_frame_set_value (cache->saved_regs, |
| gdbarch_num_regs (gdbarch) + MIPS_SP_REGNUM, |
| cache->base); |
| |
| return (struct mips_frame_cache *) (*this_cache); |
| } |
| |
| static void |
| mips_insn16_frame_this_id (struct frame_info *this_frame, void **this_cache, |
| struct frame_id *this_id) |
| { |
| struct mips_frame_cache *info = mips_insn16_frame_cache (this_frame, |
| this_cache); |
| /* This marks the outermost frame. */ |
| if (info->base == 0) |
| return; |
| (*this_id) = frame_id_build (info->base, get_frame_func (this_frame)); |
| } |
| |
| static struct value * |
| mips_insn16_frame_prev_register (struct frame_info *this_frame, |
| void **this_cache, int regnum) |
| { |
| struct mips_frame_cache *info = mips_insn16_frame_cache (this_frame, |
| this_cache); |
| return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum); |
| } |
| |
| static int |
| mips_insn16_frame_sniffer (const struct frame_unwind *self, |
| struct frame_info *this_frame, void **this_cache) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (this_frame); |
| CORE_ADDR pc = get_frame_pc (this_frame); |
| if (mips_pc_is_mips16 (gdbarch, pc)) |
| return 1; |
| return 0; |
| } |
| |
| static const struct frame_unwind mips_insn16_frame_unwind = |
| { |
| NORMAL_FRAME, |
| default_frame_unwind_stop_reason, |
| mips_insn16_frame_this_id, |
| mips_insn16_frame_prev_register, |
| NULL, |
| mips_insn16_frame_sniffer |
| }; |
| |
| static CORE_ADDR |
| mips_insn16_frame_base_address (struct frame_info *this_frame, |
| void **this_cache) |
| { |
| struct mips_frame_cache *info = mips_insn16_frame_cache (this_frame, |
| this_cache); |
| return info->base; |
| } |
| |
| static const struct frame_base mips_insn16_frame_base = |
| { |
| &mips_insn16_frame_unwind, |
| mips_insn16_frame_base_address, |
| mips_insn16_frame_base_address, |
| mips_insn16_frame_base_address |
| }; |
| |
| static const struct frame_base * |
| mips_insn16_frame_base_sniffer (struct frame_info *this_frame) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (this_frame); |
| CORE_ADDR pc = get_frame_pc (this_frame); |
| if (mips_pc_is_mips16 (gdbarch, pc)) |
| return &mips_insn16_frame_base; |
| else |
| return NULL; |
| } |
| |
| /* Decode a 9-bit signed immediate argument of ADDIUSP -- -2 is mapped |
| to -258, -1 -- to -257, 0 -- to 256, 1 -- to 257 and other values are |
| interpreted directly, and then multiplied by 4. */ |
| |
| static int |
| micromips_decode_imm9 (int imm) |
| { |
| imm = (imm ^ 0x100) - 0x100; |
| if (imm > -3 && imm < 2) |
| imm ^= 0x100; |
| return imm << 2; |
| } |
| |
| /* Analyze the function prologue from START_PC to LIMIT_PC. Return |
| the address of the first instruction past the prologue. */ |
| |
| static CORE_ADDR |
| micromips_scan_prologue (struct gdbarch *gdbarch, |
| CORE_ADDR start_pc, CORE_ADDR limit_pc, |
| struct frame_info *this_frame, |
| struct mips_frame_cache *this_cache) |
| { |
| CORE_ADDR end_prologue_addr; |
| int prev_non_prologue_insn = 0; |
| int frame_reg = MIPS_SP_REGNUM; |
| int this_non_prologue_insn; |
| int non_prologue_insns = 0; |
| long frame_offset = 0; /* Size of stack frame. */ |
| long frame_adjust = 0; /* Offset of FP from SP. */ |
| CORE_ADDR frame_addr = 0; /* Value of $30, used as frame pointer. */ |
| int prev_delay_slot = 0; |
| int in_delay_slot; |
| CORE_ADDR prev_pc; |
| CORE_ADDR cur_pc; |
| ULONGEST insn; /* current instruction */ |
| CORE_ADDR sp; |
| long offset; |
| long sp_adj; |
| long v1_off = 0; /* The assumption is LUI will replace it. */ |
| int reglist; |
| int breg; |
| int dreg; |
| int sreg; |
| int treg; |
| int loc; |
| int op; |
| int s; |
| int i; |
| |
| /* Can be called when there's no process, and hence when there's no |
| THIS_FRAME. */ |
| if (this_frame != NULL) |
| sp = get_frame_register_signed (this_frame, |
| gdbarch_num_regs (gdbarch) |
| + MIPS_SP_REGNUM); |
| else |
| sp = 0; |
| |
| if (limit_pc > start_pc + 200) |
| limit_pc = start_pc + 200; |
| prev_pc = start_pc; |
| |
| /* Permit at most one non-prologue non-control-transfer instruction |
| in the middle which may have been reordered by the compiler for |
| optimisation. */ |
| for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += loc) |
| { |
| this_non_prologue_insn = 0; |
| in_delay_slot = 0; |
| sp_adj = 0; |
| loc = 0; |
| insn = mips_fetch_instruction (gdbarch, ISA_MICROMIPS, cur_pc, NULL); |
| loc += MIPS_INSN16_SIZE; |
| switch (mips_insn_size (ISA_MICROMIPS, insn)) |
| { |
| /* 32-bit instructions. */ |
| case 2 * MIPS_INSN16_SIZE: |
| insn <<= 16; |
| insn |= mips_fetch_instruction (gdbarch, |
| ISA_MICROMIPS, cur_pc + loc, NULL); |
| loc += MIPS_INSN16_SIZE; |
| switch (micromips_op (insn >> 16)) |
| { |
| /* Record $sp/$fp adjustment. */ |
| /* Discard (D)ADDU $gp,$jp used for PIC code. */ |
| case 0x0: /* POOL32A: bits 000000 */ |
| case 0x16: /* POOL32S: bits 010110 */ |
| op = b0s11_op (insn); |
| sreg = b0s5_reg (insn >> 16); |
| treg = b5s5_reg (insn >> 16); |
| dreg = b11s5_reg (insn); |
| if (op == 0x1d0 |
| /* SUBU: bits 000000 00111010000 */ |
| /* DSUBU: bits 010110 00111010000 */ |
| && dreg == MIPS_SP_REGNUM && sreg == MIPS_SP_REGNUM |
| && treg == 3) |
| /* (D)SUBU $sp, $v1 */ |
| sp_adj = v1_off; |
| else if (op != 0x150 |
| /* ADDU: bits 000000 00101010000 */ |
| /* DADDU: bits 010110 00101010000 */ |
| || dreg != 28 || sreg != 28 || treg != MIPS_T9_REGNUM) |
| this_non_prologue_insn = 1; |
| break; |
| |
| case 0x8: /* POOL32B: bits 001000 */ |
| op = b12s4_op (insn); |
| breg = b0s5_reg (insn >> 16); |
| reglist = sreg = b5s5_reg (insn >> 16); |
| offset = (b0s12_imm (insn) ^ 0x800) - 0x800; |
| if ((op == 0x9 || op == 0xc) |
| /* SWP: bits 001000 1001 */ |
| /* SDP: bits 001000 1100 */ |
| && breg == MIPS_SP_REGNUM && sreg < MIPS_RA_REGNUM) |
| /* S[DW]P reg,offset($sp) */ |
| { |
| s = 4 << ((b12s4_op (insn) & 0x4) == 0x4); |
| set_reg_offset (gdbarch, this_cache, |
| sreg, sp + offset); |
| set_reg_offset (gdbarch, this_cache, |
| sreg + 1, sp + offset + s); |
| } |
| else if ((op == 0xd || op == 0xf) |
| /* SWM: bits 001000 1101 */ |
| /* SDM: bits 001000 1111 */ |
| && breg == MIPS_SP_REGNUM |
| /* SWM reglist,offset($sp) */ |
| && ((reglist >= 1 && reglist <= 9) |
| || (reglist >= 16 && reglist <= 25))) |
| { |
| int sreglist = min(reglist & 0xf, 8); |
| |
| s = 4 << ((b12s4_op (insn) & 0x2) == 0x2); |
| for (i = 0; i < sreglist; i++) |
| set_reg_offset (gdbarch, this_cache, 16 + i, sp + s * i); |
| if ((reglist & 0xf) > 8) |
| set_reg_offset (gdbarch, this_cache, 30, sp + s * i++); |
| if ((reglist & 0x10) == 0x10) |
| set_reg_offset (gdbarch, this_cache, |
| MIPS_RA_REGNUM, sp + s * i++); |
| } |
| else |
| this_non_prologue_insn = 1; |
| break; |
| |
| /* Record $sp/$fp adjustment. */ |
| /* Discard (D)ADDIU $gp used for PIC code. */ |
| case 0xc: /* ADDIU: bits 001100 */ |
| case 0x17: /* DADDIU: bits 010111 */ |
| sreg = b0s5_reg (insn >> 16); |
| dreg = b5s5_reg (insn >> 16); |
| offset = (b0s16_imm (insn) ^ 0x8000) - 0x8000; |
| if (sreg == MIPS_SP_REGNUM && dreg == MIPS_SP_REGNUM) |
| /* (D)ADDIU $sp, imm */ |
| sp_adj = offset; |
| else if (sreg == MIPS_SP_REGNUM && dreg == 30) |
| /* (D)ADDIU $fp, $sp, imm */ |
| { |
| frame_addr = sp + offset; |
| frame_adjust = offset; |
| frame_reg = 30; |
| } |
| else if (sreg != 28 || dreg != 28) |
| /* (D)ADDIU $gp, imm */ |
| this_non_prologue_insn = 1; |
| break; |
| |
| /* LUI $v1 is used for larger $sp adjustments. */ |
| /* Discard LUI $gp used for PIC code. */ |
| case 0x10: /* POOL32I: bits 010000 */ |
| if (b5s5_op (insn >> 16) == 0xd |
| /* LUI: bits 010000 001101 */ |
| && b0s5_reg (insn >> 16) == 3) |
| /* LUI $v1, imm */ |
| v1_off = ((b0s16_imm (insn) << 16) ^ 0x80000000) - 0x80000000; |
| else if (b5s5_op (insn >> 16) != 0xd |
| /* LUI: bits 010000 001101 */ |
| || b0s5_reg (insn >> 16) != 28) |
| /* LUI $gp, imm */ |
| this_non_prologue_insn = 1; |
| break; |
| |
| /* ORI $v1 is used for larger $sp adjustments. */ |
| case 0x14: /* ORI: bits 010100 */ |
| sreg = b0s5_reg (insn >> 16); |
| dreg = b5s5_reg (insn >> 16); |
| if (sreg == 3 && dreg == 3) |
| /* ORI $v1, imm */ |
| v1_off |= b0s16_imm (insn); |
| else |
| this_non_prologue_insn = 1; |
| break; |
| |
| case 0x26: /* SWC1: bits 100110 */ |
| case 0x2e: /* SDC1: bits 101110 */ |
| breg = b0s5_reg (insn >> 16); |
| if (breg != MIPS_SP_REGNUM) |
| /* S[DW]C1 reg,offset($sp) */ |
| this_non_prologue_insn = 1; |
| break; |
| |
| case 0x36: /* SD: bits 110110 */ |
| case 0x3e: /* SW: bits 111110 */ |
| breg = b0s5_reg (insn >> 16); |
| sreg = b5s5_reg (insn >> 16); |
| offset = (b0s16_imm (insn) ^ 0x8000) - 0x8000; |
| if (breg == MIPS_SP_REGNUM) |
| /* S[DW] reg,offset($sp) */ |
| set_reg_offset (gdbarch, this_cache, sreg, sp + offset); |
| else |
| this_non_prologue_insn = 1; |
| break; |
| |
| default: |
| /* The instruction in the delay slot can be a part |
| of the prologue, so move forward once more. */ |
| if (micromips_instruction_has_delay_slot (insn, 0)) |
| in_delay_slot = 1; |
| else |
| this_non_prologue_insn = 1; |
| break; |
| } |
| insn >>= 16; |
| break; |
| |
| /* 16-bit instructions. */ |
| case MIPS_INSN16_SIZE: |
| switch (micromips_op (insn)) |
| { |
| case 0x3: /* MOVE: bits 000011 */ |
| sreg = b0s5_reg (insn); |
| dreg = b5s5_reg (insn); |
| if (sreg == MIPS_SP_REGNUM && dreg == 30) |
| /* MOVE $fp, $sp */ |
| { |
| frame_addr = sp; |
| frame_reg = 30; |
| } |
| else if ((sreg & 0x1c) != 0x4) |
| /* MOVE reg, $a0-$a3 */ |
| this_non_prologue_insn = 1; |
| break; |
| |
| case 0x11: /* POOL16C: bits 010001 */ |
| if (b6s4_op (insn) == 0x5) |
| /* SWM: bits 010001 0101 */ |
| { |
| offset = ((b0s4_imm (insn) << 2) ^ 0x20) - 0x20; |
| reglist = b4s2_regl (insn); |
| for (i = 0; i <= reglist; i++) |
| set_reg_offset (gdbarch, this_cache, 16 + i, sp + 4 * i); |
| set_reg_offset (gdbarch, this_cache, |
| MIPS_RA_REGNUM, sp + 4 * i++); |
| } |
| else |
| this_non_prologue_insn = 1; |
| break; |
| |
| case 0x13: /* POOL16D: bits 010011 */ |
| if ((insn & 0x1) == 0x1) |
| /* ADDIUSP: bits 010011 1 */ |
| sp_adj = micromips_decode_imm9 (b1s9_imm (insn)); |
| else if (b5s5_reg (insn) == MIPS_SP_REGNUM) |
| /* ADDIUS5: bits 010011 0 */ |
| /* ADDIUS5 $sp, imm */ |
| sp_adj = (b1s4_imm (insn) ^ 8) - 8; |
| else |
| this_non_prologue_insn = 1; |
| break; |
| |
| case 0x32: /* SWSP: bits 110010 */ |
| offset = b0s5_imm (insn) << 2; |
| sreg = b5s5_reg (insn); |
| set_reg_offset (gdbarch, this_cache, sreg, sp + offset); |
| break; |
| |
| default: |
| /* The instruction in the delay slot can be a part |
| of the prologue, so move forward once more. */ |
| if (micromips_instruction_has_delay_slot (insn << 16, 0)) |
| in_delay_slot = 1; |
| else |
| this_non_prologue_insn = 1; |
| break; |
| } |
| break; |
| } |
| if (sp_adj < 0) |
| frame_offset -= sp_adj; |
| |
| non_prologue_insns += this_non_prologue_insn; |
| |
| /* A jump or branch, enough non-prologue insns seen or positive |
| stack adjustment? If so, then we must have reached the end |
| of the prologue by now. */ |
| if (prev_delay_slot || non_prologue_insns > 1 || sp_adj > 0 |
| || micromips_instruction_is_compact_branch (insn)) |
| break; |
| |
| prev_non_prologue_insn = this_non_prologue_insn; |
| prev_delay_slot = in_delay_slot; |
| prev_pc = cur_pc; |
| } |
| |
| if (this_cache != NULL) |
| { |
| this_cache->base = |
| (get_frame_register_signed (this_frame, |
| gdbarch_num_regs (gdbarch) + frame_reg) |
| + frame_offset - frame_adjust); |
| /* FIXME: brobecker/2004-10-10: Just as in the mips32 case, we should |
| be able to get rid of the assignment below, evetually. But it's |
| still needed for now. */ |
| this_cache->saved_regs[gdbarch_num_regs (gdbarch) |
| + mips_regnum (gdbarch)->pc] |
| = this_cache->saved_regs[gdbarch_num_regs (gdbarch) + MIPS_RA_REGNUM]; |
| } |
| |
| /* Set end_prologue_addr to the address of the instruction immediately |
| after the last one we scanned. Unless the last one looked like a |
| non-prologue instruction (and we looked ahead), in which case use |
| its address instead. */ |
| end_prologue_addr |
| = prev_non_prologue_insn || prev_delay_slot ? prev_pc : cur_pc; |
| |
| return end_prologue_addr; |
| } |
| |
| /* Heuristic unwinder for procedures using microMIPS instructions. |
| Procedures that use the 32-bit instruction set are handled by the |
| mips_insn32 unwinder. Likewise MIPS16 and the mips_insn16 unwinder. */ |
| |
| static struct mips_frame_cache * |
| mips_micro_frame_cache (struct frame_info *this_frame, void **this_cache) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (this_frame); |
| struct mips_frame_cache *cache; |
| |
| if ((*this_cache) != NULL) |
| return (struct mips_frame_cache *) (*this_cache); |
| |
| cache = FRAME_OBSTACK_ZALLOC (struct mips_frame_cache); |
| (*this_cache) = cache; |
| cache->saved_regs = trad_frame_alloc_saved_regs (this_frame); |
| |
| /* Analyze the function prologue. */ |
| { |
| const CORE_ADDR pc = get_frame_address_in_block (this_frame); |
| CORE_ADDR start_addr; |
| |
| find_pc_partial_function (pc, NULL, &start_addr, NULL); |
| if (start_addr == 0) |
| start_addr = heuristic_proc_start (get_frame_arch (this_frame), pc); |
| /* We can't analyze the prologue if we couldn't find the begining |
| of the function. */ |
| if (start_addr == 0) |
| return cache; |
| |
| micromips_scan_prologue (gdbarch, start_addr, pc, this_frame, |
| (struct mips_frame_cache *) *this_cache); |
| } |
| |
| /* gdbarch_sp_regnum contains the value and not the address. */ |
| trad_frame_set_value (cache->saved_regs, |
| gdbarch_num_regs (gdbarch) + MIPS_SP_REGNUM, |
| cache->base); |
| |
| return (struct mips_frame_cache *) (*this_cache); |
| } |
| |
| static void |
| mips_micro_frame_this_id (struct frame_info *this_frame, void **this_cache, |
| struct frame_id *this_id) |
| { |
| struct mips_frame_cache *info = mips_micro_frame_cache (this_frame, |
| this_cache); |
| /* This marks the outermost frame. */ |
| if (info->base == 0) |
| return; |
| (*this_id) = frame_id_build (info->base, get_frame_func (this_frame)); |
| } |
| |
| static struct value * |
| mips_micro_frame_prev_register (struct frame_info *this_frame, |
| void **this_cache, int regnum) |
| { |
| struct mips_frame_cache *info = mips_micro_frame_cache (this_frame, |
| this_cache); |
| return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum); |
| } |
| |
| static int |
| mips_micro_frame_sniffer (const struct frame_unwind *self, |
| struct frame_info *this_frame, void **this_cache) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (this_frame); |
| CORE_ADDR pc = get_frame_pc (this_frame); |
| |
| if (mips_pc_is_micromips (gdbarch, pc)) |
| return 1; |
| return 0; |
| } |
| |
| static const struct frame_unwind mips_micro_frame_unwind = |
| { |
| NORMAL_FRAME, |
| default_frame_unwind_stop_reason, |
| mips_micro_frame_this_id, |
| mips_micro_frame_prev_register, |
| NULL, |
| mips_micro_frame_sniffer |
| }; |
| |
| static CORE_ADDR |
| mips_micro_frame_base_address (struct frame_info *this_frame, |
| void **this_cache) |
| { |
| struct mips_frame_cache *info = mips_micro_frame_cache (this_frame, |
| this_cache); |
| return info->base; |
| } |
| |
| static const struct frame_base mips_micro_frame_base = |
| { |
| &mips_micro_frame_unwind, |
| mips_micro_frame_base_address, |
| mips_micro_frame_base_address, |
| mips_micro_frame_base_address |
| }; |
| |
| static const struct frame_base * |
| mips_micro_frame_base_sniffer (struct frame_info *this_frame) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (this_frame); |
| CORE_ADDR pc = get_frame_pc (this_frame); |
| |
| if (mips_pc_is_micromips (gdbarch, pc)) |
| return &mips_micro_frame_base; |
| else |
| return NULL; |
| } |
| |
| /* Mark all the registers as unset in the saved_regs array |
| of THIS_CACHE. Do nothing if THIS_CACHE is null. */ |
| |
| static void |
| reset_saved_regs (struct gdbarch *gdbarch, struct mips_frame_cache *this_cache) |
| { |
| if (this_cache == NULL || this_cache->saved_regs == NULL) |
| return; |
| |
| { |
| const int num_regs = gdbarch_num_regs (gdbarch); |
| int i; |
| |
| for (i = 0; i < num_regs; i++) |
| { |
| this_cache->saved_regs[i].addr = -1; |
| } |
| } |
| } |
| |
| /* Analyze the function prologue from START_PC to LIMIT_PC. Builds |
| the associated FRAME_CACHE if not null. |
| Return the address of the first instruction past the prologue. */ |
| |
| static CORE_ADDR |
| mips32_scan_prologue (struct gdbarch *gdbarch, |
| CORE_ADDR start_pc, CORE_ADDR limit_pc, |
| struct frame_info *this_frame, |
| struct mips_frame_cache *this_cache) |
| { |
| int prev_non_prologue_insn; |
| int this_non_prologue_insn; |
| int non_prologue_insns; |
| CORE_ADDR frame_addr = 0; /* Value of $r30. Used by gcc for |
| frame-pointer. */ |
| int prev_delay_slot; |
| CORE_ADDR prev_pc; |
| CORE_ADDR cur_pc; |
| CORE_ADDR sp; |
| long frame_offset; |
| int frame_reg = MIPS_SP_REGNUM; |
| |
| CORE_ADDR end_prologue_addr; |
| int seen_sp_adjust = 0; |
| int load_immediate_bytes = 0; |
| int in_delay_slot; |
| int regsize_is_64_bits = (mips_abi_regsize (gdbarch) == 8); |
| |
| /* Can be called when there's no process, and hence when there's no |
| THIS_FRAME. */ |
| if (this_frame != NULL) |
| sp = get_frame_register_signed (this_frame, |
| gdbarch_num_regs (gdbarch) |
| + MIPS_SP_REGNUM); |
| else |
| sp = 0; |
| |
| if (limit_pc > start_pc + 200) |
| limit_pc = start_pc + 200; |
| |
| restart: |
| prev_non_prologue_insn = 0; |
| non_prologue_insns = 0; |
| prev_delay_slot = 0; |
| prev_pc = start_pc; |
| |
| /* Permit at most one non-prologue non-control-transfer instruction |
| in the middle which may have been reordered by the compiler for |
| optimisation. */ |
| frame_offset = 0; |
| for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += MIPS_INSN32_SIZE) |
| { |
| unsigned long inst, high_word; |
| long offset; |
| int reg; |
| |
| this_non_prologue_insn = 0; |
| in_delay_slot = 0; |
| |
| /* Fetch the instruction. */ |
| inst = (unsigned long) mips_fetch_instruction (gdbarch, ISA_MIPS, |
| cur_pc, NULL); |
| |
| /* Save some code by pre-extracting some useful fields. */ |
| high_word = (inst >> 16) & 0xffff; |
| offset = ((inst & 0xffff) ^ 0x8000) - 0x8000; |
| reg = high_word & 0x1f; |
| |
| if (high_word == 0x27bd /* addiu $sp,$sp,-i */ |
| || high_word == 0x23bd /* addi $sp,$sp,-i */ |
| || high_word == 0x67bd) /* daddiu $sp,$sp,-i */ |
| { |
| if (offset < 0) /* Negative stack adjustment? */ |
| frame_offset -= offset; |
| else |
| /* Exit loop if a positive stack adjustment is found, which |
| usually means that the stack cleanup code in the function |
| epilogue is reached. */ |
| break; |
| seen_sp_adjust = 1; |
| } |
| else if (((high_word & 0xFFE0) == 0xafa0) /* sw reg,offset($sp) */ |
| && !regsize_is_64_bits) |
| { |
| set_reg_offset (gdbarch, this_cache, reg, sp + offset); |
| } |
| else if (((high_word & 0xFFE0) == 0xffa0) /* sd reg,offset($sp) */ |
| && regsize_is_64_bits) |
| { |
| /* Irix 6.2 N32 ABI uses sd instructions for saving $gp and $ra. */ |
| set_reg_offset (gdbarch, this_cache, reg, sp + offset); |
| } |
| else if (high_word == 0x27be) /* addiu $30,$sp,size */ |
| { |
| /* Old gcc frame, r30 is virtual frame pointer. */ |
| if (offset != frame_offset) |
| frame_addr = sp + offset; |
| else if (this_frame && frame_reg == MIPS_SP_REGNUM) |
| { |
| unsigned alloca_adjust; |
| |
| frame_reg = 30; |
| frame_addr = get_frame_register_signed |
| (this_frame, gdbarch_num_regs (gdbarch) + 30); |
| frame_offset = 0; |
| |
| alloca_adjust = (unsigned) (frame_addr - (sp + offset)); |
| if (alloca_adjust > 0) |
| { |
| /* FP > SP + frame_size. This may be because of |
| an alloca or somethings similar. Fix sp to |
| "pre-alloca" value, and try again. */ |
| sp += alloca_adjust; |
| /* Need to reset the status of all registers. Otherwise, |
| we will hit a guard that prevents the new address |
| for each register to be recomputed during the second |
| pass. */ |
| reset_saved_regs (gdbarch, this_cache); |
| goto restart; |
| } |
| } |
| } |
| /* move $30,$sp. With different versions of gas this will be either |
| `addu $30,$sp,$zero' or `or $30,$sp,$zero' or `daddu 30,sp,$0'. |
| Accept any one of these. */ |
| else if (inst == 0x03A0F021 || inst == 0x03a0f025 || inst == 0x03a0f02d) |
| { |
| /* New gcc frame, virtual frame pointer is at r30 + frame_size. */ |
| if (this_frame && frame_reg == MIPS_SP_REGNUM) |
| { |
| unsigned alloca_adjust; |
| |
| frame_reg = 30; |
| frame_addr = get_frame_register_signed |
| (this_frame, gdbarch_num_regs (gdbarch) + 30); |
| |
| alloca_adjust = (unsigned) (frame_addr - sp); |
| if (alloca_adjust > 0) |
| { |
| /* FP > SP + frame_size. This may be because of |
| an alloca or somethings similar. Fix sp to |
| "pre-alloca" value, and try again. */ |
| sp = frame_addr; |
| /* Need to reset the status of all registers. Otherwise, |
| we will hit a guard that prevents the new address |
| for each register to be recomputed during the second |
| pass. */ |
| reset_saved_regs (gdbarch, this_cache); |
| goto restart; |
| } |
| } |
| } |
| else if ((high_word & 0xFFE0) == 0xafc0 /* sw reg,offset($30) */ |
| && !regsize_is_64_bits) |
| { |
| set_reg_offset (gdbarch, this_cache, reg, frame_addr + offset); |
| } |
| else if ((high_word & 0xFFE0) == 0xE7A0 /* swc1 freg,n($sp) */ |
| || (high_word & 0xF3E0) == 0xA3C0 /* sx reg,n($s8) */ |
| || (inst & 0xFF9F07FF) == 0x00800021 /* move reg,$a0-$a3 */ |
| || high_word == 0x3c1c /* lui $gp,n */ |
| || high_word == 0x279c /* addiu $gp,$gp,n */ |
| || inst == 0x0399e021 /* addu $gp,$gp,$t9 */ |
| || inst == 0x033ce021 /* addu $gp,$t9,$gp */ |
| ) |
| { |
| /* These instructions are part of the prologue, but we don't |
| need to do anything special to handle them. */ |
| } |
| /* The instructions below load $at or $t0 with an immediate |
| value in preparation for a stack adjustment via |
| subu $sp,$sp,[$at,$t0]. These instructions could also |
| initialize a local variable, so we accept them only before |
| a stack adjustment instruction was seen. */ |
| else if (!seen_sp_adjust |
| && !prev_delay_slot |
| && (high_word == 0x3c01 /* lui $at,n */ |
| || high_word == 0x3c08 /* lui $t0,n */ |
| || high_word == 0x3421 /* ori $at,$at,n */ |
| || high_word == 0x3508 /* ori $t0,$t0,n */ |
| || high_word == 0x3401 /* ori $at,$zero,n */ |
| || high_word == 0x3408 /* ori $t0,$zero,n */ |
| )) |
| { |
| load_immediate_bytes += MIPS_INSN32_SIZE; /* FIXME! */ |
| } |
| /* Check for branches and jumps. The instruction in the delay |
| slot can be a part of the prologue, so move forward once more. */ |
| else if (mips32_instruction_has_delay_slot (gdbarch, inst)) |
| { |
| in_delay_slot = 1; |
| } |
| /* This instruction is not an instruction typically found |
| in a prologue, so we must have reached the end of the |
| prologue. */ |
| else |
| { |
| this_non_prologue_insn = 1; |
| } |
| |
| non_prologue_insns += this_non_prologue_insn; |
| |
| /* A jump or branch, or enough non-prologue insns seen? If so, |
| then we must have reached the end of the prologue by now. */ |
| if (prev_delay_slot || non_prologue_insns > 1) |
| break; |
| |
| prev_non_prologue_insn = this_non_prologue_insn; |
| prev_delay_slot = in_delay_slot; |
| prev_pc = cur_pc; |
| } |
| |
| if (this_cache != NULL) |
| { |
| this_cache->base = |
| (get_frame_register_signed (this_frame, |
| gdbarch_num_regs (gdbarch) + frame_reg) |
| + frame_offset); |
| /* FIXME: brobecker/2004-09-15: We should be able to get rid of |
| this assignment below, eventually. But it's still needed |
| for now. */ |
| this_cache->saved_regs[gdbarch_num_regs (gdbarch) |
| + mips_regnum (gdbarch)->pc] |
| = this_cache->saved_regs[gdbarch_num_regs (gdbarch) |
| + MIPS_RA_REGNUM]; |
| } |
| |
| /* Set end_prologue_addr to the address of the instruction immediately |
| after the last one we scanned. Unless the last one looked like a |
| non-prologue instruction (and we looked ahead), in which case use |
| its address instead. */ |
| end_prologue_addr |
| = prev_non_prologue_insn || prev_delay_slot ? prev_pc : cur_pc; |
| |
| /* In a frameless function, we might have incorrectly |
| skipped some load immediate instructions. Undo the skipping |
| if the load immediate was not followed by a stack adjustment. */ |
| if (load_immediate_bytes && !seen_sp_adjust) |
| end_prologue_addr -= load_immediate_bytes; |
| |
| return end_prologue_addr; |
| } |
| |
| /* Heuristic unwinder for procedures using 32-bit instructions (covers |
| both 32-bit and 64-bit MIPS ISAs). Procedures using 16-bit |
| instructions (a.k.a. MIPS16) are handled by the mips_insn16 |
| unwinder. Likewise microMIPS and the mips_micro unwinder. */ |
| |
| static struct mips_frame_cache * |
| mips_insn32_frame_cache (struct frame_info *this_frame, void **this_cache) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (this_frame); |
| struct mips_frame_cache *cache; |
| |
| if ((*this_cache) != NULL) |
| return (struct mips_frame_cache *) (*this_cache); |
| |
| cache = FRAME_OBSTACK_ZALLOC (struct mips_frame_cache); |
| (*this_cache) = cache; |
| cache->saved_regs = trad_frame_alloc_saved_regs (this_frame); |
| |
| /* Analyze the function prologue. */ |
| { |
| const CORE_ADDR pc = get_frame_address_in_block (this_frame); |
| CORE_ADDR start_addr; |
| |
| find_pc_partial_function (pc, NULL, &start_addr, NULL); |
| if (start_addr == 0) |
| start_addr = heuristic_proc_start (gdbarch, pc); |
| /* We can't analyze the prologue if we couldn't find the begining |
| of the function. */ |
| if (start_addr == 0) |
| return cache; |
| |
| mips32_scan_prologue (gdbarch, start_addr, pc, this_frame, |
| (struct mips_frame_cache *) *this_cache); |
| } |
| |
| /* gdbarch_sp_regnum contains the value and not the address. */ |
| trad_frame_set_value (cache->saved_regs, |
| gdbarch_num_regs (gdbarch) + MIPS_SP_REGNUM, |
| cache->base); |
| |
| return (struct mips_frame_cache *) (*this_cache); |
| } |
| |
| static void |
| mips_insn32_frame_this_id (struct frame_info *this_frame, void **this_cache, |
| struct frame_id *this_id) |
| { |
| struct mips_frame_cache *info = mips_insn32_frame_cache (this_frame, |
| this_cache); |
| /* This marks the outermost frame. */ |
| if (info->base == 0) |
| return; |
| (*this_id) = frame_id_build (info->base, get_frame_func (this_frame)); |
| } |
| |
| static struct value * |
| mips_insn32_frame_prev_register (struct frame_info *this_frame, |
| void **this_cache, int regnum) |
| { |
| struct mips_frame_cache *info = mips_insn32_frame_cache (this_frame, |
| this_cache); |
| return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum); |
| } |
| |
| static int |
| mips_insn32_frame_sniffer (const struct frame_unwind *self, |
| struct frame_info *this_frame, void **this_cache) |
| { |
| CORE_ADDR pc = get_frame_pc (this_frame); |
| if (mips_pc_is_mips (pc)) |
| return 1; |
| return 0; |
| } |
| |
| static const struct frame_unwind mips_insn32_frame_unwind = |
| { |
| NORMAL_FRAME, |
| default_frame_unwind_stop_reason, |
| mips_insn32_frame_this_id, |
| mips_insn32_frame_prev_register, |
| NULL, |
| mips_insn32_frame_sniffer |
| }; |
| |
| static CORE_ADDR |
| mips_insn32_frame_base_address (struct frame_info *this_frame, |
| void **this_cache) |
| { |
| struct mips_frame_cache *info = mips_insn32_frame_cache (this_frame, |
| this_cache); |
| return info->base; |
| } |
| |
| static const struct frame_base mips_insn32_frame_base = |
| { |
| &mips_insn32_frame_unwind, |
| mips_insn32_frame_base_address, |
| mips_insn32_frame_base_address, |
| mips_insn32_frame_base_address |
| }; |
| |
| static const struct frame_base * |
| mips_insn32_frame_base_sniffer (struct frame_info *this_frame) |
| { |
| CORE_ADDR pc = get_frame_pc (this_frame); |
| if (mips_pc_is_mips (pc)) |
| return &mips_insn32_frame_base; |
| else |
| return NULL; |
| } |
| |
| static struct trad_frame_cache * |
| mips_stub_frame_cache (struct frame_info *this_frame, void **this_cache) |
| { |
| CORE_ADDR pc; |
| CORE_ADDR start_addr; |
| CORE_ADDR stack_addr; |
| struct trad_frame_cache *this_trad_cache; |
| struct gdbarch *gdbarch = get_frame_arch (this_frame); |
| int num_regs = gdbarch_num_regs (gdbarch); |
| |
| if ((*this_cache) != NULL) |
| return (struct trad_frame_cache *) (*this_cache); |
| this_trad_cache = trad_frame_cache_zalloc (this_frame); |
| (*this_cache) = this_trad_cache; |
| |
| /* The return address is in the link register. */ |
| trad_frame_set_reg_realreg (this_trad_cache, |
| gdbarch_pc_regnum (gdbarch), |
| num_regs + MIPS_RA_REGNUM); |
| |
| /* Frame ID, since it's a frameless / stackless function, no stack |
| space is allocated and SP on entry is the current SP. */ |
| pc = get_frame_pc (this_frame); |
| find_pc_partial_function (pc, NULL, &start_addr, NULL); |
| stack_addr = get_frame_register_signed (this_frame, |
| num_regs + MIPS_SP_REGNUM); |
| trad_frame_set_id (this_trad_cache, frame_id_build (stack_addr, start_addr)); |
| |
| /* Assume that the frame's base is the same as the |
| stack-pointer. */ |
| trad_frame_set_this_base (this_trad_cache, stack_addr); |
| |
| return this_trad_cache; |
| } |
| |
| static void |
| mips_stub_frame_this_id (struct frame_info *this_frame, void **this_cache, |
| struct frame_id *this_id) |
| { |
| struct trad_frame_cache *this_trad_cache |
| = mips_stub_frame_cache (this_frame, this_cache); |
| trad_frame_get_id (this_trad_cache, this_id); |
| } |
| |
| static struct value * |
| mips_stub_frame_prev_register (struct frame_info *this_frame, |
| void **this_cache, int regnum) |
| { |
| struct trad_frame_cache *this_trad_cache |
| = mips_stub_frame_cache (this_frame, this_cache); |
| return trad_frame_get_register (this_trad_cache, this_frame, regnum); |
| } |
| |
| static int |
| mips_stub_frame_sniffer (const struct frame_unwind *self, |
| struct frame_info *this_frame, void **this_cache) |
| { |
| gdb_byte dummy[4]; |
| struct obj_section *s; |
| CORE_ADDR pc = get_frame_address_in_block (this_frame); |
| struct bound_minimal_symbol msym; |
| |
| /* Use the stub unwinder for unreadable code. */ |
| if (target_read_memory (get_frame_pc (this_frame), dummy, 4) != 0) |
| return 1; |
| |
| if (in_plt_section (pc) || in_mips_stubs_section (pc)) |
| return 1; |
| |
| /* Calling a PIC function from a non-PIC function passes through a |
| stub. The stub for foo is named ".pic.foo". */ |
| msym = lookup_minimal_symbol_by_pc (pc); |
| if (msym.minsym != NULL |
| && MSYMBOL_LINKAGE_NAME (msym.minsym) != NULL |
| && startswith (MSYMBOL_LINKAGE_NAME (msym.minsym), ".pic.")) |
| return 1; |
| |
| return 0; |
| } |
| |
| static const struct frame_unwind mips_stub_frame_unwind = |
| { |
| NORMAL_FRAME, |
| default_frame_unwind_stop_reason, |
| mips_stub_frame_this_id, |
| mips_stub_frame_prev_register, |
| NULL, |
| mips_stub_frame_sniffer |
| }; |
| |
| static CORE_ADDR |
| mips_stub_frame_base_address (struct frame_info *this_frame, |
| void **this_cache) |
| { |
| struct trad_frame_cache *this_trad_cache |
| = mips_stub_frame_cache (this_frame, this_cache); |
| return trad_frame_get_this_base (this_trad_cache); |
| } |
| |
| static const struct frame_base mips_stub_frame_base = |
| { |
| &mips_stub_frame_unwind, |
| mips_stub_frame_base_address, |
| mips_stub_frame_base_address, |
| mips_stub_frame_base_address |
| }; |
| |
| static const struct frame_base * |
| mips_stub_frame_base_sniffer (struct frame_info *this_frame) |
| { |
| if (mips_stub_frame_sniffer (&mips_stub_frame_unwind, this_frame, NULL)) |
| return &mips_stub_frame_base; |
| else |
| return NULL; |
| } |
| |
| /* mips_addr_bits_remove - remove useless address bits */ |
| |
| static CORE_ADDR |
| mips_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR addr) |
| { |
| struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
| |
| if (mips_mask_address_p (tdep) && (((ULONGEST) addr) >> 32 == 0xffffffffUL)) |
| /* This hack is a work-around for existing boards using PMON, the |
| simulator, and any other 64-bit targets that doesn't have true |
| 64-bit addressing. On these targets, the upper 32 bits of |
| addresses are ignored by the hardware. Thus, the PC or SP are |
| likely to have been sign extended to all 1s by instruction |
| sequences that load 32-bit addresses. For example, a typical |
| piece of code that loads an address is this: |
| |
| lui $r2, <upper 16 bits> |
| ori $r2, <lower 16 bits> |
| |
| But the lui sign-extends the value such that the upper 32 bits |
| may be all 1s. The workaround is simply to mask off these |
| bits. In the future, gcc may be changed to support true 64-bit |
| addressing, and this masking will have to be disabled. */ |
| return addr &= 0xffffffffUL; |
| else |
| return addr; |
| } |
| |
| |
| /* Checks for an atomic sequence of instructions beginning with a LL/LLD |
| instruction and ending with a SC/SCD instruction. If such a sequence |
| is found, attempt to step through it. A breakpoint is placed at the end of |
| the sequence. */ |
| |
| /* Instructions used during single-stepping of atomic sequences, standard |
| ISA version. */ |
| #define LL_OPCODE 0x30 |
| #define LLD_OPCODE 0x34 |
| #define SC_OPCODE 0x38 |
| #define SCD_OPCODE 0x3c |
| |
| static int |
| mips_deal_with_atomic_sequence (struct gdbarch *gdbarch, |
| struct address_space *aspace, CORE_ADDR pc) |
| { |
| CORE_ADDR breaks[2] = {-1, -1}; |
| CORE_ADDR loc = pc; |
| CORE_ADDR branch_bp; /* Breakpoint at branch instruction's destination. */ |
| ULONGEST insn; |
| int insn_count; |
| int index; |
| int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed). */ |
| const int atomic_sequence_length = 16; /* Instruction sequence length. */ |
| |
| insn = mips_fetch_instruction (gdbarch, ISA_MIPS, loc, NULL); |
| /* Assume all atomic sequences start with a ll/lld instruction. */ |
| if (itype_op (insn) != LL_OPCODE && itype_op (insn) != LLD_OPCODE) |
| return 0; |
| |
| /* Assume that no atomic sequence is longer than "atomic_sequence_length" |
| instructions. */ |
| for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count) |
| { |
| int is_branch = 0; |
| loc += MIPS_INSN32_SIZE; |
| insn = mips_fetch_instruction (gdbarch, ISA_MIPS, loc, NULL); |
| |
| /* Assume that there is at most one branch in the atomic |
| sequence. If a branch is found, put a breakpoint in its |
| destination address. */ |
| switch (itype_op (insn)) |
| { |
| case 0: /* SPECIAL */ |
| if (rtype_funct (insn) >> 1 == 4) /* JR, JALR */ |
| return 0; /* fallback to the standard single-step code. */ |
| break; |
| case 1: /* REGIMM */ |
| is_branch = ((itype_rt (insn) & 0xc) == 0 /* B{LT,GE}Z* */ |
| || ((itype_rt (insn) & 0x1e) == 0 |
| && itype_rs (insn) == 0)); /* BPOSGE* */ |
| break; |
| case 2: /* J */ |
| case 3: /* JAL */ |
| return 0; /* fallback to the standard single-step code. */ |
| case 4: /* BEQ */ |
| case 5: /* BNE */ |
| case 6: /* BLEZ */ |
| case 7: /* BGTZ */ |
| case 20: /* BEQL */ |
| case 21: /* BNEL */ |
| case 22: /* BLEZL */ |
| case 23: /* BGTTL */ |
| is_branch = 1; |
| break; |
| case 17: /* COP1 */ |
| is_branch = ((itype_rs (insn) == 9 || itype_rs (insn) == 10) |
| && (itype_rt (insn) & 0x2) == 0); |
| if (is_branch) /* BC1ANY2F, BC1ANY2T, BC1ANY4F, BC1ANY4T */ |
| break; |
| /* Fall through. */ |
| case 18: /* COP2 */ |
| case 19: /* COP3 */ |
| is_branch = (itype_rs (insn) == 8); /* BCzF, BCzFL, BCzT, BCzTL */ |
| break; |
| } |
| if (is_branch) |
| { |
| branch_bp = loc + mips32_relative_offset (insn) + 4; |
| if (last_breakpoint >= 1) |
| return 0; /* More than one branch found, fallback to the |
| standard single-step code. */ |
| breaks[1] = branch_bp; |
| last_breakpoint++; |
| } |
| |
| if (itype_op (insn) == SC_OPCODE || itype_op (insn) == SCD_OPCODE) |
| break; |
| } |
| |
| /* Assume that the atomic sequence ends with a sc/scd instruction. */ |
| if (itype_op (insn) != SC_OPCODE && itype_op (insn) != SCD_OPCODE) |
| return 0; |
| |
| loc += MIPS_INSN32_SIZE; |
| |
| /* Insert a breakpoint right after the end of the atomic sequence. */ |
| breaks[0] = loc; |
| |
| /* Check for duplicated breakpoints. Check also for a breakpoint |
| placed (branch instruction's destination) in the atomic sequence. */ |
| if (last_breakpoint && pc <= breaks[1] && breaks[1] <= breaks[0]) |
| last_breakpoint = 0; |
| |
| /* Effectively inserts the breakpoints. */ |
| for (index = 0; index <= last_breakpoint; index++) |
| insert_single_step_breakpoint (gdbarch, aspace, breaks[index]); |
| |
| return 1; |
| } |
| |
| static int |
| micromips_deal_with_atomic_sequence (struct gdbarch *gdbarch, |
| struct address_space *aspace, |
| CORE_ADDR pc) |
| { |
| const int atomic_sequence_length = 16; /* Instruction sequence length. */ |
| int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed). */ |
| CORE_ADDR breaks[2] = {-1, -1}; |
| CORE_ADDR branch_bp = 0; /* Breakpoint at branch instruction's |
| destination. */ |
| CORE_ADDR loc = pc; |
| int sc_found = 0; |
| ULONGEST insn; |
| int insn_count; |
| int index; |
| |
| /* Assume all atomic sequences start with a ll/lld instruction. */ |
| insn = mips_fetch_instruction (gdbarch, ISA_MICROMIPS, loc, NULL); |
| if (micromips_op (insn) != 0x18) /* POOL32C: bits 011000 */ |
| return 0; |
| loc += MIPS_INSN16_SIZE; |
| insn <<= 16; |
| insn |= mips_fetch_instruction (gdbarch, ISA_MICROMIPS, loc, NULL); |
| if ((b12s4_op (insn) & 0xb) != 0x3) /* LL, LLD: bits 011000 0x11 */ |
| return 0; |
| loc += MIPS_INSN16_SIZE; |
| |
| /* Assume all atomic sequences end with an sc/scd instruction. Assume |
| that no atomic sequence is longer than "atomic_sequence_length" |
| instructions. */ |
| for (insn_count = 0; |
| !sc_found && insn_count < atomic_sequence_length; |
| ++insn_count) |
| { |
| int is_branch = 0; |
| |
| insn = mips_fetch_instruction (gdbarch, ISA_MICROMIPS, loc, NULL); |
| loc += MIPS_INSN16_SIZE; |
| |
| /* Assume that there is at most one conditional branch in the |
| atomic sequence. If a branch is found, put a breakpoint in |
| its destination address. */ |
| switch (mips_insn_size (ISA_MICROMIPS, insn)) |
| { |
| /* 32-bit instructions. */ |
| case 2 * MIPS_INSN16_SIZE: |
| switch (micromips_op (insn)) |
| { |
| case 0x10: /* POOL32I: bits 010000 */ |
| if ((b5s5_op (insn) & 0x18) != 0x0 |
| /* BLTZ, BLTZAL, BGEZ, BGEZAL: 010000 000xx */ |
| /* BLEZ, BNEZC, BGTZ, BEQZC: 010000 001xx */ |
| && (b5s5_op (insn) & 0x1d) != 0x11 |
| /* BLTZALS, BGEZALS: bits 010000 100x1 */ |
| && ((b5s5_op (insn) & 0x1e) != 0x14 |
| || (insn & 0x3) != 0x0) |
| /* BC2F, BC2T: bits 010000 1010x xxx00 */ |
| && (b5s5_op (insn) & 0x1e) != 0x1a |
| /* BPOSGE64, BPOSGE32: bits 010000 1101x */ |
| && ((b5s5_op (insn) & 0x1e) != 0x1c |
| || (insn & 0x3) != 0x0) |
| /* BC1F, BC1T: bits 010000 1110x xxx00 */ |
| && ((b5s5_op (insn) & 0x1c) != 0x1c |
| || (insn & 0x3) != 0x1)) |
| /* BC1ANY*: bits 010000 111xx xxx01 */ |
| break; |
| /* Fall through. */ |
| |
| case 0x25: /* BEQ: bits 100101 */ |
| case 0x2d: /* BNE: bits 101101 */ |
| insn <<= 16; |
| insn |= mips_fetch_instruction (gdbarch, |
| ISA_MICROMIPS, loc, NULL); |
| branch_bp = (loc + MIPS_INSN16_SIZE |
| + micromips_relative_offset16 (insn)); |
| is_branch = 1; |
| break; |
| |
| case 0x00: /* POOL32A: bits 000000 */ |
| insn <<= 16; |
| insn |= mips_fetch_instruction (gdbarch, |
| ISA_MICROMIPS, loc, NULL); |
| if (b0s6_op (insn) != 0x3c |
| /* POOL32Axf: bits 000000 ... 111100 */ |
| || (b6s10_ext (insn) & 0x2bf) != 0x3c) |
| /* JALR, JALR.HB: 000000 000x111100 111100 */ |
| /* JALRS, JALRS.HB: 000000 010x111100 111100 */ |
| break; |
| /* Fall through. */ |
| |
| case 0x1d: /* JALS: bits 011101 */ |
| case 0x35: /* J: bits 110101 */ |
| case 0x3d: /* JAL: bits 111101 */ |
| case 0x3c: /* JALX: bits 111100 */ |
| return 0; /* Fall back to the standard single-step code. */ |
| |
| case 0x18: /* POOL32C: bits 011000 */ |
| if ((b12s4_op (insn) & 0xb) == 0xb) |
| /* SC, SCD: bits 011000 1x11 */ |
| sc_found = 1; |
| break; |
| } |
| loc += MIPS_INSN16_SIZE; |
| break; |
| |
| /* 16-bit instructions. */ |
| case MIPS_INSN16_SIZE: |
| switch (micromips_op (insn)) |
| { |
| case 0x23: /* BEQZ16: bits 100011 */ |
| case 0x2b: /* BNEZ16: bits 101011 */ |
| branch_bp = loc + micromips_relative_offset7 (insn); |
| is_branch = 1; |
| break; |
| |
| case 0x11: /* POOL16C: bits 010001 */ |
| if ((b5s5_op (insn) & 0x1c) != 0xc |
| /* JR16, JRC, JALR16, JALRS16: 010001 011xx */ |
| && b5s5_op (insn) != 0x18) |
| /* JRADDIUSP: bits 010001 11000 */ |
| break; |
| return 0; /* Fall back to the standard single-step code. */ |
| |
| case 0x33: /* B16: bits 110011 */ |
| return 0; /* Fall back to the standard single-step code. */ |
| } |
| break; |
| } |
| if (is_branch) |
| { |
| if (last_breakpoint >= 1) |
| return 0; /* More than one branch found, fallback to the |
| standard single-step code. */ |
| breaks[1] = branch_bp; |
| last_breakpoint++; |
| } |
| } |
| if (!sc_found) |
| return 0; |
| |
| /* Insert a breakpoint right after the end of the atomic sequence. */ |
| breaks[0] = loc; |
| |
| /* Check for duplicated breakpoints. Check also for a breakpoint |
| placed (branch instruction's destination) in the atomic sequence */ |
| if (last_breakpoint && pc <= breaks[1] && breaks[1] <= breaks[0]) |
| last_breakpoint = 0; |
| |
| /* Effectively inserts the breakpoints. */ |
| for (index = 0; index <= last_breakpoint; index++) |
| insert_single_step_breakpoint (gdbarch, aspace, breaks[index]); |
| |
| return 1; |
| } |
| |
| static int |
| deal_with_atomic_sequence (struct gdbarch *gdbarch, |
| struct address_space *aspace, CORE_ADDR pc) |
| { |
| if (mips_pc_is_mips (pc)) |
| return mips_deal_with_atomic_sequence (gdbarch, aspace, pc); |
| else if (mips_pc_is_micromips (gdbarch, pc)) |
| return micromips_deal_with_atomic_sequence (gdbarch, aspace, pc); |
| else |
| return 0; |
| } |
| |
| /* mips_software_single_step() is called just before we want to resume |
| the inferior, if we want to single-step it but there is no hardware |
| or kernel single-step support (MIPS on GNU/Linux for example). We find |
| the target of the coming instruction and breakpoint it. */ |
| |
| int |
| mips_software_single_step (struct frame_info *frame) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| struct address_space *aspace = get_frame_address_space (frame); |
| CORE_ADDR pc, next_pc; |
| |
| pc = get_frame_pc (frame); |
| if (deal_with_atomic_sequence (gdbarch, aspace, pc)) |
| return 1; |
| |
| next_pc = mips_next_pc (frame, pc); |
| |
| insert_single_step_breakpoint (gdbarch, aspace, next_pc); |
| return 1; |
| } |
| |
| /* Test whether the PC points to the return instruction at the |
| end of a function. */ |
| |
| static int |
| mips_about_to_return (struct gdbarch *gdbarch, CORE_ADDR pc) |
| { |
| ULONGEST insn; |
| ULONGEST hint; |
| |
| /* This used to check for MIPS16, but this piece of code is never |
| called for MIPS16 functions. And likewise microMIPS ones. */ |
| gdb_assert (mips_pc_is_mips (pc)); |
| |
| insn = mips_fetch_instruction (gdbarch, ISA_MIPS, pc, NULL); |
| hint = 0x7c0; |
| return (insn & ~hint) == 0x3e00008; /* jr(.hb) $ra */ |
| } |
| |
| |
| /* This fencepost looks highly suspicious to me. Removing it also |
| seems suspicious as it could affect remote debugging across serial |
| lines. */ |
| |
| static CORE_ADDR |
| heuristic_proc_start (struct gdbarch *gdbarch, CORE_ADDR pc) |
| { |
| CORE_ADDR start_pc; |
| CORE_ADDR fence; |
| int instlen; |
| int seen_adjsp = 0; |
| struct inferior *inf; |
| |
| pc = gdbarch_addr_bits_remove (gdbarch, pc); |
| start_pc = pc; |
| fence = start_pc - heuristic_fence_post; |
| if (start_pc == 0) |
| return 0; |
| |
| if (heuristic_fence_post == -1 || fence < VM_MIN_ADDRESS) |
| fence = VM_MIN_ADDRESS; |
| |
| instlen = mips_pc_is_mips (pc) ? MIPS_INSN32_SIZE : MIPS_INSN16_SIZE; |
| |
| inf = current_inferior (); |
| |
| /* Search back for previous return. */ |
| for (start_pc -= instlen;; start_pc -= instlen) |
| if (start_pc < fence) |
| { |
| /* It's not clear to me why we reach this point when |
| stop_soon, but with this test, at least we |
| don't print out warnings for every child forked (eg, on |
| decstation). 22apr93 rich@cygnus.com. */ |
| if (inf->control.stop_soon == NO_STOP_QUIETLY) |
| { |
| static int blurb_printed = 0; |
| |
| warning (_("GDB can't find the start of the function at %s."), |
| paddress (gdbarch, pc)); |
| |
| if (!blurb_printed) |
| { |
| /* This actually happens frequently in embedded |
| development, when you first connect to a board |
| and your stack pointer and pc are nowhere in |
| particular. This message needs to give people |
| in that situation enough information to |
| determine that it's no big deal. */ |
| printf_filtered ("\n\ |
| GDB is unable to find the start of the function at %s\n\ |
| and thus can't determine the size of that function's stack frame.\n\ |
| This means that GDB may be unable to access that stack frame, or\n\ |
| the frames below it.\n\ |
| This problem is most likely caused by an invalid program counter or\n\ |
| stack pointer.\n\ |
| However, if you think GDB should simply search farther back\n\ |
| from %s for code which looks like the beginning of a\n\ |
| function, you can increase the range of the search using the `set\n\ |
| heuristic-fence-post' command.\n", |
| paddress (gdbarch, pc), paddress (gdbarch, pc)); |
| blurb_printed = 1; |
| } |
| } |
| |
| return 0; |
| } |
| else if (mips_pc_is_mips16 (gdbarch, start_pc)) |
| { |
| unsigned short inst; |
| |
| /* On MIPS16, any one of the following is likely to be the |
| start of a function: |
| extend save |
| save |
| entry |
| addiu sp,-n |
| daddiu sp,-n |
| extend -n followed by 'addiu sp,+n' or 'daddiu sp,+n'. */ |
| inst = mips_fetch_instruction (gdbarch, ISA_MIPS16, start_pc, NULL); |
| if ((inst & 0xff80) == 0x6480) /* save */ |
| { |
| if (start_pc - instlen >= fence) |
| { |
| inst = mips_fetch_instruction (gdbarch, ISA_MIPS16, |
| start_pc - instlen, NULL); |
| if ((inst & 0xf800) == 0xf000) /* extend */ |
| start_pc -= instlen; |
| } |
| break; |
| } |
| else if (((inst & 0xf81f) == 0xe809 |
| && (inst & 0x700) != 0x700) /* entry */ |
| || (inst & 0xff80) == 0x6380 /* addiu sp,-n */ |
| || (inst & 0xff80) == 0xfb80 /* daddiu sp,-n */ |
| || ((inst & 0xf810) == 0xf010 && seen_adjsp)) /* extend -n */ |
| break; |
| else if ((inst & 0xff00) == 0x6300 /* addiu sp */ |
| || (inst & 0xff00) == 0xfb00) /* daddiu sp */ |
| seen_adjsp = 1; |
| else |
| seen_adjsp = 0; |
| } |
| else if (mips_pc_is_micromips (gdbarch, start_pc)) |
| { |
| ULONGEST insn; |
| int stop = 0; |
| long offset; |
| int dreg; |
| int sreg; |
| |
| /* On microMIPS, any one of the following is likely to be the |
| start of a function: |
| ADDIUSP -imm |
| (D)ADDIU $sp, -imm |
| LUI $gp, imm */ |
| insn = mips_fetch_instruction (gdbarch, ISA_MICROMIPS, pc, NULL); |
| switch (micromips_op (insn)) |
| { |
| case 0xc: /* ADDIU: bits 001100 */ |
| case 0x17: /* DADDIU: bits 010111 */ |
| sreg = b0s5_reg (insn); |
| dreg = b5s5_reg (insn); |
| insn <<= 16; |
| insn |= mips_fetch_instruction (gdbarch, ISA_MICROMIPS, |
| pc + MIPS_INSN16_SIZE, NULL); |
| offset = (b0s16_imm (insn) ^ 0x8000) - 0x8000; |
| if (sreg == MIPS_SP_REGNUM && dreg == MIPS_SP_REGNUM |
| /* (D)ADDIU $sp, imm */ |
| && offset < 0) |
| stop = 1; |
| break; |
| |
| case 0x10: /* POOL32I: bits 010000 */ |
| if (b5s5_op (insn) == 0xd |
| /* LUI: bits 010000 001101 */ |
| && b0s5_reg (insn >> 16) == 28) |
| /* LUI $gp, imm */ |
| stop = 1; |
| break; |
| |
| case 0x13: /* POOL16D: bits 010011 */ |
| if ((insn & 0x1) == 0x1) |
| /* ADDIUSP: bits 010011 1 */ |
| { |
| offset = micromips_decode_imm9 (b1s9_imm (insn)); |
| if (offset < 0) |
| /* ADDIUSP -imm */ |
| stop = 1; |
| } |
| else |
| /* ADDIUS5: bits 010011 0 */ |
| { |
| dreg = b5s5_reg (insn); |
| offset = (b1s4_imm (insn) ^ 8) - 8; |
| if (dreg == MIPS_SP_REGNUM && offset < 0) |
| /* ADDIUS5 $sp, -imm */ |
| stop = 1; |
| } |
| break; |
| } |
| if (stop) |
| break; |
| } |
| else if (mips_about_to_return (gdbarch, start_pc)) |
| { |
| /* Skip return and its delay slot. */ |
| start_pc += 2 * MIPS_INSN32_SIZE; |
| break; |
| } |
| |
| return start_pc; |
| } |
| |
| struct mips_objfile_private |
| { |
| bfd_size_type size; |
| char *contents; |
| }; |
| |
| /* According to the current ABI, should the type be passed in a |
| floating-point register (assuming that there is space)? When there |
| is no FPU, FP are not even considered as possible candidates for |
| FP registers and, consequently this returns false - forces FP |
| arguments into integer registers. */ |
| |
| static int |
| fp_register_arg_p (struct gdbarch *gdbarch, enum type_code typecode, |
| struct type *arg_type) |
| { |
| return ((typecode == TYPE_CODE_FLT |
| || (MIPS_EABI (gdbarch) |
| && (typecode == TYPE_CODE_STRUCT |
| || typecode == TYPE_CODE_UNION) |
| && TYPE_NFIELDS (arg_type) == 1 |
| && TYPE_CODE (check_typedef (TYPE_FIELD_TYPE (arg_type, 0))) |
| == TYPE_CODE_FLT)) |
| && MIPS_FPU_TYPE(gdbarch) != MIPS_FPU_NONE); |
| } |
| |
| /* On o32, argument passing in GPRs depends on the alignment of the type being |
| passed. Return 1 if this type must be aligned to a doubleword boundary. */ |
| |
| static int |
| mips_type_needs_double_align (struct type *type) |
| { |
| enum type_code typecode = TYPE_CODE (type); |
| |
| if (typecode == TYPE_CODE_FLT && TYPE_LENGTH (type) == 8) |
| return 1; |
| else if (typecode == TYPE_CODE_STRUCT) |
| { |
| if (TYPE_NFIELDS (type) < 1) |
| return 0; |
| return mips_type_needs_double_align (TYPE_FIELD_TYPE (type, 0)); |
| } |
| else if (typecode == TYPE_CODE_UNION) |
| { |
| int i, n; |
| |
| n = TYPE_NFIELDS (type); |
| for (i = 0; i < n; i++) |
| if (mips_type_needs_double_align (TYPE_FIELD_TYPE (type, i))) |
| return 1; |
| return 0; |
| } |
| return 0; |
| } |
| |
| /* Adjust the address downward (direction of stack growth) so that it |
| is correctly aligned for a new stack frame. */ |
| static CORE_ADDR |
| mips_frame_align (struct gdbarch *gdbarch, CORE_ADDR addr) |
| { |
| return align_down (addr, 16); |
| } |
| |
| /* Implement the "push_dummy_code" gdbarch method. */ |
| |
| static CORE_ADDR |
| mips_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp, |
| CORE_ADDR funaddr, struct value **args, |
| int nargs, struct type *value_type, |
| CORE_ADDR *real_pc, CORE_ADDR *bp_addr, |
| struct regcache *regcache) |
| { |
| static gdb_byte nop_insn[] = { 0, 0, 0, 0 }; |
| CORE_ADDR nop_addr; |
| CORE_ADDR bp_slot; |
| |
| /* Reserve enough room on the stack for our breakpoint instruction. */ |
| bp_slot = sp - sizeof (nop_insn); |
| |
| /* Return to microMIPS mode if calling microMIPS code to avoid |
| triggering an address error exception on processors that only |
| support microMIPS execution. */ |
| *bp_addr = (mips_pc_is_micromips (gdbarch, funaddr) |
| ? make_compact_addr (bp_slot) : bp_slot); |
| |
| /* The breakpoint layer automatically adjusts the address of |
| breakpoints inserted in a branch delay slot. With enough |
| bad luck, the 4 bytes located just before our breakpoint |
| instruction could look like a branch instruction, and thus |
| trigger the adjustement, and break the function call entirely. |
| So, we reserve those 4 bytes and write a nop instruction |
| to prevent that from happening. */ |
| nop_addr = bp_slot - sizeof (nop_insn); |
| write_memory (nop_addr, nop_insn, sizeof (nop_insn)); |
| sp = mips_frame_align (gdbarch, nop_addr); |
| |
| /* Inferior resumes at the function entry point. */ |
| *real_pc = funaddr; |
| |
| return sp; |
| } |
| |
| static CORE_ADDR |
| mips_eabi_push_dummy_call (struct gdbarch *gdbarch, struct value *function, |
| struct regcache *regcache, CORE_ADDR bp_addr, |
| int nargs, struct value **args, CORE_ADDR sp, |
| int struct_return, CORE_ADDR struct_addr) |
| { |
| int argreg; |
| int float_argreg; |
| int argnum; |
| int len = 0; |
| int stack_offset = 0; |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| CORE_ADDR func_addr = find_function_addr (function, NULL); |
| int regsize = mips_abi_regsize (gdbarch); |
| |
| /* For shared libraries, "t9" needs to point at the function |
| address. */ |
| regcache_cooked_write_signed (regcache, MIPS_T9_REGNUM, func_addr); |
| |
| /* Set the return address register to point to the entry point of |
| the program, where a breakpoint lies in wait. */ |
| regcache_cooked_write_signed (regcache, MIPS_RA_REGNUM, bp_addr); |
| |
| /* First ensure that the stack and structure return address (if any) |
| are properly aligned. The stack has to be at least 64-bit |
| aligned even on 32-bit machines, because doubles must be 64-bit |
| aligned. For n32 and n64, stack frames need to be 128-bit |
| aligned, so we round to this widest known alignment. */ |
| |
| sp = align_down (sp, 16); |
| struct_addr = align_down (struct_addr, 16); |
| |
| /* Now make space on the stack for the args. We allocate more |
| than necessary for EABI, because the first few arguments are |
| passed in registers, but that's OK. */ |
| for (argnum = 0; argnum < nargs; argnum++) |
| len += align_up (TYPE_LENGTH (value_type (args[argnum])), regsize); |
| sp -= align_up (len, 16); |
| |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "mips_eabi_push_dummy_call: sp=%s allocated %ld\n", |
| paddress (gdbarch, sp), (long) align_up (len, 16)); |
| |
| /* Initialize the integer and float register pointers. */ |
| argreg = MIPS_A0_REGNUM; |
| float_argreg = mips_fpa0_regnum (gdbarch); |
| |
| /* The struct_return pointer occupies the first parameter-passing reg. */ |
| if (struct_return) |
| { |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "mips_eabi_push_dummy_call: " |
| "struct_return reg=%d %s\n", |
| argreg, paddress (gdbarch, struct_addr)); |
| regcache_cooked_write_unsigned (regcache, argreg++, struct_addr); |
| } |
| |
| /* Now load as many as possible of the first arguments into |
| registers, and push the rest onto the stack. Loop thru args |
| from first to last. */ |
| for (argnum = 0; argnum < nargs; argnum++) |
| { |
| const gdb_byte *val; |
| gdb_byte valbuf[MAX_REGISTER_SIZE]; |
| struct value *arg = args[argnum]; |
| struct type *arg_type = check_typedef (value_type (arg)); |
| int len = TYPE_LENGTH (arg_type); |
| enum type_code typecode = TYPE_CODE (arg_type); |
| |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "mips_eabi_push_dummy_call: %d len=%d type=%d", |
| argnum + 1, len, (int) typecode); |
| |
| /* The EABI passes structures that do not fit in a register by |
| reference. */ |
| if (len > regsize |
| && (typecode == TYPE_CODE_STRUCT || typecode == TYPE_CODE_UNION)) |
| { |
| store_unsigned_integer (valbuf, regsize, byte_order, |
| value_address (arg)); |
| typecode = TYPE_CODE_PTR; |
| len = regsize; |
| val = valbuf; |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " push"); |
| } |
| else |
| val = value_contents (arg); |
| |
| /* 32-bit ABIs always start floating point arguments in an |
| even-numbered floating point register. Round the FP register |
| up before the check to see if there are any FP registers |
| left. Non MIPS_EABI targets also pass the FP in the integer |
| registers so also round up normal registers. */ |
| if (regsize < 8 && fp_register_arg_p (gdbarch, typecode, arg_type)) |
| { |
| if ((float_argreg & 1)) |
| float_argreg++; |
| } |
| |
| /* Floating point arguments passed in registers have to be |
| treated specially. On 32-bit architectures, doubles |
| are passed in register pairs; the even register gets |
| the low word, and the odd register gets the high word. |
| On non-EABI processors, the first two floating point arguments are |
| also copied to general registers, because MIPS16 functions |
| don't use float registers for arguments. This duplication of |
| arguments in general registers can't hurt non-MIPS16 functions |
| because those registers are normally skipped. */ |
| /* MIPS_EABI squeezes a struct that contains a single floating |
| point value into an FP register instead of pushing it onto the |
| stack. */ |
| if (fp_register_arg_p (gdbarch, typecode, arg_type) |
| && float_argreg <= MIPS_LAST_FP_ARG_REGNUM (gdbarch)) |
| { |
| /* EABI32 will pass doubles in consecutive registers, even on |
| 64-bit cores. At one time, we used to check the size of |
| `float_argreg' to determine whether or not to pass doubles |
| in consecutive registers, but this is not sufficient for |
| making the ABI determination. */ |
| if (len == 8 && mips_abi (gdbarch) == MIPS_ABI_EABI32) |
| { |
| int low_offset = gdbarch_byte_order (gdbarch) |
| == BFD_ENDIAN_BIG ? 4 : 0; |
| long regval; |
| |
| /* Write the low word of the double to the even register(s). */ |
| regval = extract_signed_integer (val + low_offset, |
| 4, byte_order); |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s", |
| float_argreg, phex (regval, 4)); |
| regcache_cooked_write_signed (regcache, float_argreg++, regval); |
| |
| /* Write the high word of the double to the odd register(s). */ |
| regval = extract_signed_integer (val + 4 - low_offset, |
| 4, byte_order); |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s", |
| float_argreg, phex (regval, 4)); |
| regcache_cooked_write_signed (regcache, float_argreg++, regval); |
| } |
| else |
| { |
| /* This is a floating point value that fits entirely |
| in a single register. */ |
| /* On 32 bit ABI's the float_argreg is further adjusted |
| above to ensure that it is even register aligned. */ |
| LONGEST regval = extract_signed_integer (val, len, byte_order); |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s", |
| float_argreg, phex (regval, len)); |
| regcache_cooked_write_signed (regcache, float_argreg++, regval); |
| } |
| } |
| else |
| { |
| /* Copy the argument to general registers or the stack in |
| register-sized pieces. Large arguments are split between |
| registers and stack. */ |
| /* Note: structs whose size is not a multiple of regsize |
| are treated specially: Irix cc passes |
| them in registers where gcc sometimes puts them on the |
| stack. For maximum compatibility, we will put them in |
| both places. */ |
| int odd_sized_struct = (len > regsize && len % regsize != 0); |
| |
| /* Note: Floating-point values that didn't fit into an FP |
| register are only written to memory. */ |
| while (len > 0) |
| { |
| /* Remember if the argument was written to the stack. */ |
| int stack_used_p = 0; |
| int partial_len = (len < regsize ? len : regsize); |
| |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " -- partial=%d", |
| partial_len); |
| |
| /* Write this portion of the argument to the stack. */ |
| if (argreg > MIPS_LAST_ARG_REGNUM (gdbarch) |
| || odd_sized_struct |
| || fp_register_arg_p (gdbarch, typecode, arg_type)) |
| { |
| /* Should shorter than int integer values be |
| promoted to int before being stored? */ |
| int longword_offset = 0; |
| CORE_ADDR addr; |
| stack_used_p = 1; |
| if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) |
| { |
| if (regsize == 8 |
| && (typecode == TYPE_CODE_INT |
| || typecode == TYPE_CODE_PTR |
| || typecode == TYPE_CODE_FLT) && len <= 4) |
| longword_offset = regsize - len; |
| else if ((typecode == TYPE_CODE_STRUCT |
| || typecode == TYPE_CODE_UNION) |
| && TYPE_LENGTH (arg_type) < regsize) |
| longword_offset = regsize - len; |
| } |
| |
| if (mips_debug) |
| { |
| fprintf_unfiltered (gdb_stdlog, " - stack_offset=%s", |
| paddress (gdbarch, stack_offset)); |
| fprintf_unfiltered (gdb_stdlog, " longword_offset=%s", |
| paddress (gdbarch, longword_offset)); |
| } |
| |
| addr = sp + stack_offset + longword_offset; |
| |
| if (mips_debug) |
| { |
| int i; |
| fprintf_unfiltered (gdb_stdlog, " @%s ", |
| paddress (gdbarch, addr)); |
| for (i = 0; i < partial_len; i++) |
| { |
| fprintf_unfiltered (gdb_stdlog, "%02x", |
| val[i] & 0xff); |
| } |
| } |
| write_memory (addr, val, partial_len); |
| } |
| |
| /* Note!!! This is NOT an else clause. Odd sized |
| structs may go thru BOTH paths. Floating point |
| arguments will not. */ |
| /* Write this portion of the argument to a general |
| purpose register. */ |
| if (argreg <= MIPS_LAST_ARG_REGNUM (gdbarch) |
| && !fp_register_arg_p (gdbarch, typecode, arg_type)) |
| { |
| LONGEST regval = |
| extract_signed_integer (val, partial_len, byte_order); |
| |
| if (mips_debug) |
| fprintf_filtered (gdb_stdlog, " - reg=%d val=%s", |
| argreg, |
| phex (regval, regsize)); |
| regcache_cooked_write_signed (regcache, argreg, regval); |
| argreg++; |
| } |
| |
| len -= partial_len; |
| val += partial_len; |
| |
| /* Compute the offset into the stack at which we will |
| copy the next parameter. |
| |
| In the new EABI (and the NABI32), the stack_offset |
| only needs to be adjusted when it has been used. */ |
| |
| if (stack_used_p) |
| stack_offset += align_up (partial_len, regsize); |
| } |
| } |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, "\n"); |
| } |
| |
| regcache_cooked_write_signed (regcache, MIPS_SP_REGNUM, sp); |
| |
| /* Return adjusted stack pointer. */ |
| return sp; |
| } |
| |
| /* Determine the return value convention being used. */ |
| |
| static enum return_value_convention |
| mips_eabi_return_value (struct gdbarch *gdbarch, struct value *function, |
| struct type *type, struct regcache *regcache, |
| gdb_byte *readbuf, const gdb_byte *writebuf) |
| { |
| struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
| int fp_return_type = 0; |
| int offset, regnum, xfer; |
| |
| if (TYPE_LENGTH (type) > 2 * mips_abi_regsize (gdbarch)) |
| return RETURN_VALUE_STRUCT_CONVENTION; |
| |
| /* Floating point type? */ |
| if (tdep->mips_fpu_type != MIPS_FPU_NONE) |
| { |
| if (TYPE_CODE (type) == TYPE_CODE_FLT) |
| fp_return_type = 1; |
| /* Structs with a single field of float type |
| are returned in a floating point register. */ |
| if ((TYPE_CODE (type) == TYPE_CODE_STRUCT |
| || TYPE_CODE (type) == TYPE_CODE_UNION) |
| && TYPE_NFIELDS (type) == 1) |
| { |
| struct type *fieldtype = TYPE_FIELD_TYPE (type, 0); |
| |
| if (TYPE_CODE (check_typedef (fieldtype)) == TYPE_CODE_FLT) |
| fp_return_type = 1; |
| } |
| } |
| |
| if (fp_return_type) |
| { |
| /* A floating-point value belongs in the least significant part |
| of FP0/FP1. */ |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stderr, "Return float in $fp0\n"); |
| regnum = mips_regnum (gdbarch)->fp0; |
| } |
| else |
| { |
| /* An integer value goes in V0/V1. */ |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stderr, "Return scalar in $v0\n"); |
| regnum = MIPS_V0_REGNUM; |
| } |
| for (offset = 0; |
| offset < TYPE_LENGTH (type); |
| offset += mips_abi_regsize (gdbarch), regnum++) |
| { |
| xfer = mips_abi_regsize (gdbarch); |
| if (offset + xfer > TYPE_LENGTH (type)) |
| xfer = TYPE_LENGTH (type) - offset; |
| mips_xfer_register (gdbarch, regcache, |
| gdbarch_num_regs (gdbarch) + regnum, xfer, |
| gdbarch_byte_order (gdbarch), readbuf, writebuf, |
| offset); |
| } |
| |
| return RETURN_VALUE_REGISTER_CONVENTION; |
| } |
| |
| |
| /* N32/N64 ABI stuff. */ |
| |
| /* Search for a naturally aligned double at OFFSET inside a struct |
| ARG_TYPE. The N32 / N64 ABIs pass these in floating point |
| registers. */ |
| |
| static int |
| mips_n32n64_fp_arg_chunk_p (struct gdbarch *gdbarch, struct type *arg_type, |
| int offset) |
| { |
| int i; |
| |
| if (TYPE_CODE (arg_type) != TYPE_CODE_STRUCT) |
| return 0; |
| |
| if (MIPS_FPU_TYPE (gdbarch) != MIPS_FPU_DOUBLE) |
| return 0; |
| |
| if (TYPE_LENGTH (arg_type) < offset + MIPS64_REGSIZE) |
| return 0; |
| |
| for (i = 0; i < TYPE_NFIELDS (arg_type); i++) |
| { |
| int pos; |
| struct type *field_type; |
| |
| /* We're only looking at normal fields. */ |
| if (field_is_static (&TYPE_FIELD (arg_type, i)) |
| || (TYPE_FIELD_BITPOS (arg_type, i) % 8) != 0) |
| continue; |
| |
| /* If we have gone past the offset, there is no double to pass. */ |
| pos = TYPE_FIELD_BITPOS (arg_type, i) / 8; |
| if (pos > offset) |
| return 0; |
| |
| field_type = check_typedef (TYPE_FIELD_TYPE (arg_type, i)); |
| |
| /* If this field is entirely before the requested offset, go |
| on to the next one. */ |
| if (pos + TYPE_LENGTH (field_type) <= offset) |
| continue; |
| |
| /* If this is our special aligned double, we can stop. */ |
| if (TYPE_CODE (field_type) == TYPE_CODE_FLT |
| && TYPE_LENGTH (field_type) == MIPS64_REGSIZE) |
| return 1; |
| |
| /* This field starts at or before the requested offset, and |
| overlaps it. If it is a structure, recurse inwards. */ |
| return mips_n32n64_fp_arg_chunk_p (gdbarch, field_type, offset - pos); |
| } |
| |
| return 0; |
| } |
| |
| static CORE_ADDR |
| mips_n32n64_push_dummy_call (struct gdbarch *gdbarch, struct value *function, |
| struct regcache *regcache, CORE_ADDR bp_addr, |
| int nargs, struct value **args, CORE_ADDR sp, |
| int struct_return, CORE_ADDR struct_addr) |
| { |
| int argreg; |
| int float_argreg; |
| int argnum; |
| int len = 0; |
| int stack_offset = 0; |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| CORE_ADDR func_addr = find_function_addr (function, NULL); |
| |
| /* For shared libraries, "t9" needs to point at the function |
| address. */ |
| regcache_cooked_write_signed (regcache, MIPS_T9_REGNUM, func_addr); |
| |
| /* Set the return address register to point to the entry point of |
| the program, where a breakpoint lies in wait. */ |
| regcache_cooked_write_signed (regcache, MIPS_RA_REGNUM, bp_addr); |
| |
| /* First ensure that the stack and structure return address (if any) |
| are properly aligned. The stack has to be at least 64-bit |
| aligned even on 32-bit machines, because doubles must be 64-bit |
| aligned. For n32 and n64, stack frames need to be 128-bit |
| aligned, so we round to this widest known alignment. */ |
| |
| sp = align_down (sp, 16); |
| struct_addr = align_down (struct_addr, 16); |
| |
| /* Now make space on the stack for the args. */ |
| for (argnum = 0; argnum < nargs; argnum++) |
| len += align_up (TYPE_LENGTH (value_type (args[argnum])), MIPS64_REGSIZE); |
| sp -= align_up (len, 16); |
| |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "mips_n32n64_push_dummy_call: sp=%s allocated %ld\n", |
| paddress (gdbarch, sp), (long) align_up (len, 16)); |
| |
| /* Initialize the integer and float register pointers. */ |
| argreg = MIPS_A0_REGNUM; |
| float_argreg = mips_fpa0_regnum (gdbarch); |
| |
| /* The struct_return pointer occupies the first parameter-passing reg. */ |
| if (struct_return) |
| { |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "mips_n32n64_push_dummy_call: " |
| "struct_return reg=%d %s\n", |
| argreg, paddress (gdbarch, struct_addr)); |
| regcache_cooked_write_unsigned (regcache, argreg++, struct_addr); |
| } |
| |
| /* Now load as many as possible of the first arguments into |
| registers, and push the rest onto the stack. Loop thru args |
| from first to last. */ |
| for (argnum = 0; argnum < nargs; argnum++) |
| { |
| const gdb_byte *val; |
| struct value *arg = args[argnum]; |
| struct type *arg_type = check_typedef (value_type (arg)); |
| int len = TYPE_LENGTH (arg_type); |
| enum type_code typecode = TYPE_CODE (arg_type); |
| |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "mips_n32n64_push_dummy_call: %d len=%d type=%d", |
| argnum + 1, len, (int) typecode); |
| |
| val = value_contents (arg); |
| |
| /* A 128-bit long double value requires an even-odd pair of |
| floating-point registers. */ |
| if (len == 16 |
| && fp_register_arg_p (gdbarch, typecode, arg_type) |
| && (float_argreg & 1)) |
| { |
| float_argreg++; |
| argreg++; |
| } |
| |
| if (fp_register_arg_p (gdbarch, typecode, arg_type) |
| && argreg <= MIPS_LAST_ARG_REGNUM (gdbarch)) |
| { |
| /* This is a floating point value that fits entirely |
| in a single register or a pair of registers. */ |
| int reglen = (len <= MIPS64_REGSIZE ? len : MIPS64_REGSIZE); |
| LONGEST regval = extract_unsigned_integer (val, reglen, byte_order); |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s", |
| float_argreg, phex (regval, reglen)); |
| regcache_cooked_write_unsigned (regcache, float_argreg, regval); |
| |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s", |
| argreg, phex (regval, reglen)); |
| regcache_cooked_write_unsigned (regcache, argreg, regval); |
| float_argreg++; |
| argreg++; |
| if (len == 16) |
| { |
| regval = extract_unsigned_integer (val + reglen, |
| reglen, byte_order); |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s", |
| float_argreg, phex (regval, reglen)); |
| regcache_cooked_write_unsigned (regcache, float_argreg, regval); |
| |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s", |
| argreg, phex (regval, reglen)); |
| regcache_cooked_write_unsigned (regcache, argreg, regval); |
| float_argreg++; |
| argreg++; |
| } |
| } |
| else |
| { |
| /* Copy the argument to general registers or the stack in |
| register-sized pieces. Large arguments are split between |
| registers and stack. */ |
| /* For N32/N64, structs, unions, or other composite types are |
| treated as a sequence of doublewords, and are passed in integer |
| or floating point registers as though they were simple scalar |
| parameters to the extent that they fit, with any excess on the |
| stack packed according to the normal memory layout of the |
| object. |
| The caller does not reserve space for the register arguments; |
| the callee is responsible for reserving it if required. */ |
| /* Note: Floating-point values that didn't fit into an FP |
| register are only written to memory. */ |
| while (len > 0) |
| { |
| /* Remember if the argument was written to the stack. */ |
| int stack_used_p = 0; |
| int partial_len = (len < MIPS64_REGSIZE ? len : MIPS64_REGSIZE); |
| |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " -- partial=%d", |
| partial_len); |
| |
| if (fp_register_arg_p (gdbarch, typecode, arg_type)) |
| gdb_assert (argreg > MIPS_LAST_ARG_REGNUM (gdbarch)); |
| |
| /* Write this portion of the argument to the stack. */ |
| if (argreg > MIPS_LAST_ARG_REGNUM (gdbarch)) |
| { |
| /* Should shorter than int integer values be |
| promoted to int before being stored? */ |
| int longword_offset = 0; |
| CORE_ADDR addr; |
| stack_used_p = 1; |
| if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) |
| { |
| if ((typecode == TYPE_CODE_INT |
| || typecode == TYPE_CODE_PTR) |
| && len <= 4) |
| longword_offset = MIPS64_REGSIZE - len; |
| } |
| |
| if (mips_debug) |
| { |
| fprintf_unfiltered (gdb_stdlog, " - stack_offset=%s", |
| paddress (gdbarch, stack_offset)); |
| fprintf_unfiltered (gdb_stdlog, " longword_offset=%s", |
| paddress (gdbarch, longword_offset)); |
| } |
| |
| addr = sp + stack_offset + longword_offset; |
| |
| if (mips_debug) |
| { |
| int i; |
| fprintf_unfiltered (gdb_stdlog, " @%s ", |
| paddress (gdbarch, addr)); |
| for (i = 0; i < partial_len; i++) |
| { |
| fprintf_unfiltered (gdb_stdlog, "%02x", |
| val[i] & 0xff); |
| } |
| } |
| write_memory (addr, val, partial_len); |
| } |
| |
| /* Note!!! This is NOT an else clause. Odd sized |
| structs may go thru BOTH paths. */ |
| /* Write this portion of the argument to a general |
| purpose register. */ |
| if (argreg <= MIPS_LAST_ARG_REGNUM (gdbarch)) |
| { |
| LONGEST regval; |
| |
| /* Sign extend pointers, 32-bit integers and signed |
| 16-bit and 8-bit integers; everything else is taken |
| as is. */ |
| |
| if ((partial_len == 4 |
| && (typecode == TYPE_CODE_PTR |
| || typecode == TYPE_CODE_INT)) |
| || (partial_len < 4 |
| && typecode == TYPE_CODE_INT |
| && !TYPE_UNSIGNED (arg_type))) |
| regval = extract_signed_integer (val, partial_len, |
| byte_order); |
| else |
| regval = extract_unsigned_integer (val, partial_len, |
| byte_order); |
| |
| /* A non-floating-point argument being passed in a |
| general register. If a struct or union, and if |
| the remaining length is smaller than the register |
| size, we have to adjust the register value on |
| big endian targets. |
| |
| It does not seem to be necessary to do the |
| same for integral types. */ |
| |
| if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG |
| && partial_len < MIPS64_REGSIZE |
| && (typecode == TYPE_CODE_STRUCT |
| || typecode == TYPE_CODE_UNION)) |
| regval <<= ((MIPS64_REGSIZE - partial_len) |
| * TARGET_CHAR_BIT); |
| |
| if (mips_debug) |
| fprintf_filtered (gdb_stdlog, " - reg=%d val=%s", |
| argreg, |
| phex (regval, MIPS64_REGSIZE)); |
| regcache_cooked_write_unsigned (regcache, argreg, regval); |
| |
| if (mips_n32n64_fp_arg_chunk_p (gdbarch, arg_type, |
| TYPE_LENGTH (arg_type) - len)) |
| { |
| if (mips_debug) |
| fprintf_filtered (gdb_stdlog, " - fpreg=%d val=%s", |
| float_argreg, |
| phex (regval, MIPS64_REGSIZE)); |
| regcache_cooked_write_unsigned (regcache, float_argreg, |
| regval); |
| } |
| |
| float_argreg++; |
| argreg++; |
| } |
| |
| len -= partial_len; |
| val += partial_len; |
| |
| /* Compute the offset into the stack at which we will |
| copy the next parameter. |
| |
| In N32 (N64?), the stack_offset only needs to be |
| adjusted when it has been used. */ |
| |
| if (stack_used_p) |
| stack_offset += align_up (partial_len, MIPS64_REGSIZE); |
| } |
| } |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, "\n"); |
| } |
| |
| regcache_cooked_write_signed (regcache, MIPS_SP_REGNUM, sp); |
| |
| /* Return adjusted stack pointer. */ |
| return sp; |
| } |
| |
| static enum return_value_convention |
| mips_n32n64_return_value (struct gdbarch *gdbarch, struct value *function, |
| struct type *type, struct regcache *regcache, |
| gdb_byte *readbuf, const gdb_byte *writebuf) |
| { |
| struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
| |
| /* From MIPSpro N32 ABI Handbook, Document Number: 007-2816-004 |
| |
| Function results are returned in $2 (and $3 if needed), or $f0 (and $f2 |
| if needed), as appropriate for the type. Composite results (struct, |
| union, or array) are returned in $2/$f0 and $3/$f2 according to the |
| following rules: |
| |
| * A struct with only one or two floating point fields is returned in $f0 |
| (and $f2 if necessary). This is a generalization of the Fortran COMPLEX |
| case. |
| |
| * Any other composite results of at most 128 bits are returned in |
| $2 (first 64 bits) and $3 (remainder, if necessary). |
| |
| * Larger composite results are handled by converting the function to a |
| procedure with an implicit first parameter, which is a pointer to an area |
| reserved by the caller to receive the result. [The o32-bit ABI requires |
| that all composite results be handled by conversion to implicit first |
| parameters. The MIPS/SGI Fortran implementation has always made a |
| specific exception to return COMPLEX results in the floating point |
| registers.] */ |
| |
| if (TYPE_LENGTH (type) > 2 * MIPS64_REGSIZE) |
| return RETURN_VALUE_STRUCT_CONVENTION; |
| else if (TYPE_CODE (type) == TYPE_CODE_FLT |
| && TYPE_LENGTH (type) == 16 |
| && tdep->mips_fpu_type != MIPS_FPU_NONE) |
| { |
| /* A 128-bit floating-point value fills both $f0 and $f2. The |
| two registers are used in the same as memory order, so the |
| eight bytes with the lower memory address are in $f0. */ |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stderr, "Return float in $f0 and $f2\n"); |
| mips_xfer_register (gdbarch, regcache, |
| (gdbarch_num_regs (gdbarch) |
| + mips_regnum (gdbarch)->fp0), |
| 8, gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, 0); |
| mips_xfer_register (gdbarch, regcache, |
| (gdbarch_num_regs (gdbarch) |
| + mips_regnum (gdbarch)->fp0 + 2), |
| 8, gdbarch_byte_order (gdbarch), |
| readbuf ? readbuf + 8 : readbuf, |
| writebuf ? writebuf + 8 : writebuf, 0); |
| return RETURN_VALUE_REGISTER_CONVENTION; |
| } |
| else if (TYPE_CODE (type) == TYPE_CODE_FLT |
| && tdep->mips_fpu_type != MIPS_FPU_NONE) |
| { |
| /* A single or double floating-point value that fits in FP0. */ |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stderr, "Return float in $fp0\n"); |
| mips_xfer_register (gdbarch, regcache, |
| (gdbarch_num_regs (gdbarch) |
| + mips_regnum (gdbarch)->fp0), |
| TYPE_LENGTH (type), |
| gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, 0); |
| return RETURN_VALUE_REGISTER_CONVENTION; |
| } |
| else if (TYPE_CODE (type) == TYPE_CODE_STRUCT |
| && TYPE_NFIELDS (type) <= 2 |
| && TYPE_NFIELDS (type) >= 1 |
| && ((TYPE_NFIELDS (type) == 1 |
| && (TYPE_CODE (check_typedef (TYPE_FIELD_TYPE (type, 0))) |
| == TYPE_CODE_FLT)) |
| || (TYPE_NFIELDS (type) == 2 |
| && (TYPE_CODE (check_typedef (TYPE_FIELD_TYPE (type, 0))) |
| == TYPE_CODE_FLT) |
| && (TYPE_CODE (check_typedef (TYPE_FIELD_TYPE (type, 1))) |
| == TYPE_CODE_FLT)))) |
| { |
| /* A struct that contains one or two floats. Each value is part |
| in the least significant part of their floating point |
| register (or GPR, for soft float). */ |
| int regnum; |
| int field; |
| for (field = 0, regnum = (tdep->mips_fpu_type != MIPS_FPU_NONE |
| ? mips_regnum (gdbarch)->fp0 |
| : MIPS_V0_REGNUM); |
| field < TYPE_NFIELDS (type); field++, regnum += 2) |
| { |
| int offset = (FIELD_BITPOS (TYPE_FIELDS (type)[field]) |
| / TARGET_CHAR_BIT); |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stderr, "Return float struct+%d\n", |
| offset); |
| if (TYPE_LENGTH (TYPE_FIELD_TYPE (type, field)) == 16) |
| { |
| /* A 16-byte long double field goes in two consecutive |
| registers. */ |
| mips_xfer_register (gdbarch, regcache, |
| gdbarch_num_regs (gdbarch) + regnum, |
| 8, |
| gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, offset); |
| mips_xfer_register (gdbarch, regcache, |
| gdbarch_num_regs (gdbarch) + regnum + 1, |
| 8, |
| gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, offset + 8); |
| } |
| else |
| mips_xfer_register (gdbarch, regcache, |
| gdbarch_num_regs (gdbarch) + regnum, |
| TYPE_LENGTH (TYPE_FIELD_TYPE (type, field)), |
| gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, offset); |
| } |
| return RETURN_VALUE_REGISTER_CONVENTION; |
| } |
| else if (TYPE_CODE (type) == TYPE_CODE_STRUCT |
| || TYPE_CODE (type) == TYPE_CODE_UNION |
| || TYPE_CODE (type) == TYPE_CODE_ARRAY) |
| { |
| /* A composite type. Extract the left justified value, |
| regardless of the byte order. I.e. DO NOT USE |
| mips_xfer_lower. */ |
| int offset; |
| int regnum; |
| for (offset = 0, regnum = MIPS_V0_REGNUM; |
| offset < TYPE_LENGTH (type); |
| offset += register_size (gdbarch, regnum), regnum++) |
| { |
| int xfer = register_size (gdbarch, regnum); |
| if (offset + xfer > TYPE_LENGTH (type)) |
| xfer = TYPE_LENGTH (type) - offset; |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stderr, "Return struct+%d:%d in $%d\n", |
| offset, xfer, regnum); |
| mips_xfer_register (gdbarch, regcache, |
| gdbarch_num_regs (gdbarch) + regnum, |
| xfer, BFD_ENDIAN_UNKNOWN, readbuf, writebuf, |
| offset); |
| } |
| return RETURN_VALUE_REGISTER_CONVENTION; |
| } |
| else |
| { |
| /* A scalar extract each part but least-significant-byte |
| justified. */ |
| int offset; |
| int regnum; |
| for (offset = 0, regnum = MIPS_V0_REGNUM; |
| offset < TYPE_LENGTH (type); |
| offset += register_size (gdbarch, regnum), regnum++) |
| { |
| int xfer = register_size (gdbarch, regnum); |
| if (offset + xfer > TYPE_LENGTH (type)) |
| xfer = TYPE_LENGTH (type) - offset; |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stderr, "Return scalar+%d:%d in $%d\n", |
| offset, xfer, regnum); |
| mips_xfer_register (gdbarch, regcache, |
| gdbarch_num_regs (gdbarch) + regnum, |
| xfer, gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, offset); |
| } |
| return RETURN_VALUE_REGISTER_CONVENTION; |
| } |
| } |
| |
| /* Which registers to use for passing floating-point values between |
| function calls, one of floating-point, general and both kinds of |
| registers. O32 and O64 use different register kinds for standard |
| MIPS and MIPS16 code; to make the handling of cases where we may |
| not know what kind of code is being used (e.g. no debug information) |
| easier we sometimes use both kinds. */ |
| |
| enum mips_fval_reg |
| { |
| mips_fval_fpr, |
| mips_fval_gpr, |
| mips_fval_both |
| }; |
| |
| /* O32 ABI stuff. */ |
| |
| static CORE_ADDR |
| mips_o32_push_dummy_call (struct gdbarch *gdbarch, struct value *function, |
| struct regcache *regcache, CORE_ADDR bp_addr, |
| int nargs, struct value **args, CORE_ADDR sp, |
| int struct_return, CORE_ADDR struct_addr) |
| { |
| int argreg; |
| int float_argreg; |
| int argnum; |
| int len = 0; |
| int stack_offset = 0; |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| CORE_ADDR func_addr = find_function_addr (function, NULL); |
| |
| /* For shared libraries, "t9" needs to point at the function |
| address. */ |
| regcache_cooked_write_signed (regcache, MIPS_T9_REGNUM, func_addr); |
| |
| /* Set the return address register to point to the entry point of |
| the program, where a breakpoint lies in wait. */ |
| regcache_cooked_write_signed (regcache, MIPS_RA_REGNUM, bp_addr); |
| |
| /* First ensure that the stack and structure return address (if any) |
| are properly aligned. The stack has to be at least 64-bit |
| aligned even on 32-bit machines, because doubles must be 64-bit |
| aligned. For n32 and n64, stack frames need to be 128-bit |
| aligned, so we round to this widest known alignment. */ |
| |
| sp = align_down (sp, 16); |
| struct_addr = align_down (struct_addr, 16); |
| |
| /* Now make space on the stack for the args. */ |
| for (argnum = 0; argnum < nargs; argnum++) |
| { |
| struct type *arg_type = check_typedef (value_type (args[argnum])); |
| |
| /* Align to double-word if necessary. */ |
| if (mips_type_needs_double_align (arg_type)) |
| len = align_up (len, MIPS32_REGSIZE * 2); |
| /* Allocate space on the stack. */ |
| len += align_up (TYPE_LENGTH (arg_type), MIPS32_REGSIZE); |
| } |
| sp -= align_up (len, 16); |
| |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "mips_o32_push_dummy_call: sp=%s allocated %ld\n", |
| paddress (gdbarch, sp), (long) align_up (len, 16)); |
| |
| /* Initialize the integer and float register pointers. */ |
| argreg = MIPS_A0_REGNUM; |
| float_argreg = mips_fpa0_regnum (gdbarch); |
| |
| /* The struct_return pointer occupies the first parameter-passing reg. */ |
| if (struct_return) |
| { |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "mips_o32_push_dummy_call: " |
| "struct_return reg=%d %s\n", |
| argreg, paddress (gdbarch, struct_addr)); |
| regcache_cooked_write_unsigned (regcache, argreg++, struct_addr); |
| stack_offset += MIPS32_REGSIZE; |
| } |
| |
| /* Now load as many as possible of the first arguments into |
| registers, and push the rest onto the stack. Loop thru args |
| from first to last. */ |
| for (argnum = 0; argnum < nargs; argnum++) |
| { |
| const gdb_byte *val; |
| struct value *arg = args[argnum]; |
| struct type *arg_type = check_typedef (value_type (arg)); |
| int len = TYPE_LENGTH (arg_type); |
| enum type_code typecode = TYPE_CODE (arg_type); |
| |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "mips_o32_push_dummy_call: %d len=%d type=%d", |
| argnum + 1, len, (int) typecode); |
| |
| val = value_contents (arg); |
| |
| /* 32-bit ABIs always start floating point arguments in an |
| even-numbered floating point register. Round the FP register |
| up before the check to see if there are any FP registers |
| left. O32 targets also pass the FP in the integer registers |
| so also round up normal registers. */ |
| if (fp_register_arg_p (gdbarch, typecode, arg_type)) |
| { |
| if ((float_argreg & 1)) |
| float_argreg++; |
| } |
| |
| /* Floating point arguments passed in registers have to be |
| treated specially. On 32-bit architectures, doubles are |
| passed in register pairs; the even FP register gets the |
| low word, and the odd FP register gets the high word. |
| On O32, the first two floating point arguments are also |
| copied to general registers, following their memory order, |
| because MIPS16 functions don't use float registers for |
| arguments. This duplication of arguments in general |
| registers can't hurt non-MIPS16 functions, because those |
| registers are normally skipped. */ |
| |
| if (fp_register_arg_p (gdbarch, typecode, arg_type) |
| && float_argreg <= MIPS_LAST_FP_ARG_REGNUM (gdbarch)) |
| { |
| if (register_size (gdbarch, float_argreg) < 8 && len == 8) |
| { |
| int freg_offset = gdbarch_byte_order (gdbarch) |
| == BFD_ENDIAN_BIG ? 1 : 0; |
| unsigned long regval; |
| |
| /* First word. */ |
| regval = extract_unsigned_integer (val, 4, byte_order); |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s", |
| float_argreg + freg_offset, |
| phex (regval, 4)); |
| regcache_cooked_write_unsigned (regcache, |
| float_argreg++ + freg_offset, |
| regval); |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s", |
| argreg, phex (regval, 4)); |
| regcache_cooked_write_unsigned (regcache, argreg++, regval); |
| |
| /* Second word. */ |
| regval = extract_unsigned_integer (val + 4, 4, byte_order); |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s", |
| float_argreg - freg_offset, |
| phex (regval, 4)); |
| regcache_cooked_write_unsigned (regcache, |
| float_argreg++ - freg_offset, |
| regval); |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s", |
| argreg, phex (regval, 4)); |
| regcache_cooked_write_unsigned (regcache, argreg++, regval); |
| } |
| else |
| { |
| /* This is a floating point value that fits entirely |
| in a single register. */ |
| /* On 32 bit ABI's the float_argreg is further adjusted |
| above to ensure that it is even register aligned. */ |
| LONGEST regval = extract_unsigned_integer (val, len, byte_order); |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s", |
| float_argreg, phex (regval, len)); |
| regcache_cooked_write_unsigned (regcache, |
| float_argreg++, regval); |
| /* Although two FP registers are reserved for each |
| argument, only one corresponding integer register is |
| reserved. */ |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s", |
| argreg, phex (regval, len)); |
| regcache_cooked_write_unsigned (regcache, argreg++, regval); |
| } |
| /* Reserve space for the FP register. */ |
| stack_offset += align_up (len, MIPS32_REGSIZE); |
| } |
| else |
| { |
| /* Copy the argument to general registers or the stack in |
| register-sized pieces. Large arguments are split between |
| registers and stack. */ |
| /* Note: structs whose size is not a multiple of MIPS32_REGSIZE |
| are treated specially: Irix cc passes |
| them in registers where gcc sometimes puts them on the |
| stack. For maximum compatibility, we will put them in |
| both places. */ |
| int odd_sized_struct = (len > MIPS32_REGSIZE |
| && len % MIPS32_REGSIZE != 0); |
| /* Structures should be aligned to eight bytes (even arg registers) |
| on MIPS_ABI_O32, if their first member has double precision. */ |
| if (mips_type_needs_double_align (arg_type)) |
| { |
| if ((argreg & 1)) |
| { |
| argreg++; |
| stack_offset += MIPS32_REGSIZE; |
| } |
| } |
| while (len > 0) |
| { |
| /* Remember if the argument was written to the stack. */ |
| int stack_used_p = 0; |
| int partial_len = (len < MIPS32_REGSIZE ? len : MIPS32_REGSIZE); |
| |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " -- partial=%d", |
| partial_len); |
| |
| /* Write this portion of the argument to the stack. */ |
| if (argreg > MIPS_LAST_ARG_REGNUM (gdbarch) |
| || odd_sized_struct) |
| { |
| /* Should shorter than int integer values be |
| promoted to int before being stored? */ |
| int longword_offset = 0; |
| CORE_ADDR addr; |
| stack_used_p = 1; |
| |
| if (mips_debug) |
| { |
| fprintf_unfiltered (gdb_stdlog, " - stack_offset=%s", |
| paddress (gdbarch, stack_offset)); |
| fprintf_unfiltered (gdb_stdlog, " longword_offset=%s", |
| paddress (gdbarch, longword_offset)); |
| } |
| |
| addr = sp + stack_offset + longword_offset; |
| |
| if (mips_debug) |
| { |
| int i; |
| fprintf_unfiltered (gdb_stdlog, " @%s ", |
| paddress (gdbarch, addr)); |
| for (i = 0; i < partial_len; i++) |
| { |
| fprintf_unfiltered (gdb_stdlog, "%02x", |
| val[i] & 0xff); |
| } |
| } |
| write_memory (addr, val, partial_len); |
| } |
| |
| /* Note!!! This is NOT an else clause. Odd sized |
| structs may go thru BOTH paths. */ |
| /* Write this portion of the argument to a general |
| purpose register. */ |
| if (argreg <= MIPS_LAST_ARG_REGNUM (gdbarch)) |
| { |
| LONGEST regval = extract_signed_integer (val, partial_len, |
| byte_order); |
| /* Value may need to be sign extended, because |
| mips_isa_regsize() != mips_abi_regsize(). */ |
| |
| /* A non-floating-point argument being passed in a |
| general register. If a struct or union, and if |
| the remaining length is smaller than the register |
| size, we have to adjust the register value on |
| big endian targets. |
| |
| It does not seem to be necessary to do the |
| same for integral types. |
| |
| Also don't do this adjustment on O64 binaries. |
| |
| cagney/2001-07-23: gdb/179: Also, GCC, when |
| outputting LE O32 with sizeof (struct) < |
| mips_abi_regsize(), generates a left shift |
| as part of storing the argument in a register |
| (the left shift isn't generated when |
| sizeof (struct) >= mips_abi_regsize()). Since |
| it is quite possible that this is GCC |
| contradicting the LE/O32 ABI, GDB has not been |
| adjusted to accommodate this. Either someone |
| needs to demonstrate that the LE/O32 ABI |
| specifies such a left shift OR this new ABI gets |
| identified as such and GDB gets tweaked |
| accordingly. */ |
| |
| if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG |
| && partial_len < MIPS32_REGSIZE |
| && (typecode == TYPE_CODE_STRUCT |
| || typecode == TYPE_CODE_UNION)) |
| regval <<= ((MIPS32_REGSIZE - partial_len) |
| * TARGET_CHAR_BIT); |
| |
| if (mips_debug) |
| fprintf_filtered (gdb_stdlog, " - reg=%d val=%s", |
| argreg, |
| phex (regval, MIPS32_REGSIZE)); |
| regcache_cooked_write_unsigned (regcache, argreg, regval); |
| argreg++; |
| |
| /* Prevent subsequent floating point arguments from |
| being passed in floating point registers. */ |
| float_argreg = MIPS_LAST_FP_ARG_REGNUM (gdbarch) + 1; |
| } |
| |
| len -= partial_len; |
| val += partial_len; |
| |
| /* Compute the offset into the stack at which we will |
| copy the next parameter. |
| |
| In older ABIs, the caller reserved space for |
| registers that contained arguments. This was loosely |
| refered to as their "home". Consequently, space is |
| always allocated. */ |
| |
| stack_offset += align_up (partial_len, MIPS32_REGSIZE); |
| } |
| } |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, "\n"); |
| } |
| |
| regcache_cooked_write_signed (regcache, MIPS_SP_REGNUM, sp); |
| |
| /* Return adjusted stack pointer. */ |
| return sp; |
| } |
| |
| static enum return_value_convention |
| mips_o32_return_value (struct gdbarch *gdbarch, struct value *function, |
| struct type *type, struct regcache *regcache, |
| gdb_byte *readbuf, const gdb_byte *writebuf) |
| { |
| CORE_ADDR func_addr = function ? find_function_addr (function, NULL) : 0; |
| int mips16 = mips_pc_is_mips16 (gdbarch, func_addr); |
| struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
| enum mips_fval_reg fval_reg; |
| |
| fval_reg = readbuf ? mips16 ? mips_fval_gpr : mips_fval_fpr : mips_fval_both; |
| if (TYPE_CODE (type) == TYPE_CODE_STRUCT |
| || TYPE_CODE (type) == TYPE_CODE_UNION |
| || TYPE_CODE (type) == TYPE_CODE_ARRAY) |
| return RETURN_VALUE_STRUCT_CONVENTION; |
| else if (TYPE_CODE (type) == TYPE_CODE_FLT |
| && TYPE_LENGTH (type) == 4 && tdep->mips_fpu_type != MIPS_FPU_NONE) |
| { |
| /* A single-precision floating-point value. If reading in or copying, |
| then we get it from/put it to FP0 for standard MIPS code or GPR2 |
| for MIPS16 code. If writing out only, then we put it to both FP0 |
| and GPR2. We do not support reading in with no function known, if |
| this safety check ever triggers, then we'll have to try harder. */ |
| gdb_assert (function || !readbuf); |
| if (mips_debug) |
| switch (fval_reg) |
| { |
| case mips_fval_fpr: |
| fprintf_unfiltered (gdb_stderr, "Return float in $fp0\n"); |
| break; |
| case mips_fval_gpr: |
| fprintf_unfiltered (gdb_stderr, "Return float in $2\n"); |
| break; |
| case mips_fval_both: |
| fprintf_unfiltered (gdb_stderr, "Return float in $fp0 and $2\n"); |
| break; |
| } |
| if (fval_reg != mips_fval_gpr) |
| mips_xfer_register (gdbarch, regcache, |
| (gdbarch_num_regs (gdbarch) |
| + mips_regnum (gdbarch)->fp0), |
| TYPE_LENGTH (type), |
| gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, 0); |
| if (fval_reg != mips_fval_fpr) |
| mips_xfer_register (gdbarch, regcache, |
| gdbarch_num_regs (gdbarch) + 2, |
| TYPE_LENGTH (type), |
| gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, 0); |
| return RETURN_VALUE_REGISTER_CONVENTION; |
| } |
| else if (TYPE_CODE (type) == TYPE_CODE_FLT |
| && TYPE_LENGTH (type) == 8 && tdep->mips_fpu_type != MIPS_FPU_NONE) |
| { |
| /* A double-precision floating-point value. If reading in or copying, |
| then we get it from/put it to FP1 and FP0 for standard MIPS code or |
| GPR2 and GPR3 for MIPS16 code. If writing out only, then we put it |
| to both FP1/FP0 and GPR2/GPR3. We do not support reading in with |
| no function known, if this safety check ever triggers, then we'll |
| have to try harder. */ |
| gdb_assert (function || !readbuf); |
| if (mips_debug) |
| switch (fval_reg) |
| { |
| case mips_fval_fpr: |
| fprintf_unfiltered (gdb_stderr, "Return float in $fp1/$fp0\n"); |
| break; |
| case mips_fval_gpr: |
| fprintf_unfiltered (gdb_stderr, "Return float in $2/$3\n"); |
| break; |
| case mips_fval_both: |
| fprintf_unfiltered (gdb_stderr, |
| "Return float in $fp1/$fp0 and $2/$3\n"); |
| break; |
| } |
| if (fval_reg != mips_fval_gpr) |
| { |
| /* The most significant part goes in FP1, and the least significant |
| in FP0. */ |
| switch (gdbarch_byte_order (gdbarch)) |
| { |
| case BFD_ENDIAN_LITTLE: |
| mips_xfer_register (gdbarch, regcache, |
| (gdbarch_num_regs (gdbarch) |
| + mips_regnum (gdbarch)->fp0 + 0), |
| 4, gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, 0); |
| mips_xfer_register (gdbarch, regcache, |
| (gdbarch_num_regs (gdbarch) |
| + mips_regnum (gdbarch)->fp0 + 1), |
| 4, gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, 4); |
| break; |
| case BFD_ENDIAN_BIG: |
| mips_xfer_register (gdbarch, regcache, |
| (gdbarch_num_regs (gdbarch) |
| + mips_regnum (gdbarch)->fp0 + 1), |
| 4, gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, 0); |
| mips_xfer_register (gdbarch, regcache, |
| (gdbarch_num_regs (gdbarch) |
| + mips_regnum (gdbarch)->fp0 + 0), |
| 4, gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, 4); |
| break; |
| default: |
| internal_error (__FILE__, __LINE__, _("bad switch")); |
| } |
| } |
| if (fval_reg != mips_fval_fpr) |
| { |
| /* The two 32-bit parts are always placed in GPR2 and GPR3 |
| following these registers' memory order. */ |
| mips_xfer_register (gdbarch, regcache, |
| gdbarch_num_regs (gdbarch) + 2, |
| 4, gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, 0); |
| mips_xfer_register (gdbarch, regcache, |
| gdbarch_num_regs (gdbarch) + 3, |
| 4, gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, 4); |
| } |
| return RETURN_VALUE_REGISTER_CONVENTION; |
| } |
| #if 0 |
| else if (TYPE_CODE (type) == TYPE_CODE_STRUCT |
| && TYPE_NFIELDS (type) <= 2 |
| && TYPE_NFIELDS (type) >= 1 |
| && ((TYPE_NFIELDS (type) == 1 |
| && (TYPE_CODE (TYPE_FIELD_TYPE (type, 0)) |
| == TYPE_CODE_FLT)) |
| || (TYPE_NFIELDS (type) == 2 |
| && (TYPE_CODE (TYPE_FIELD_TYPE (type, 0)) |
| == TYPE_CODE_FLT) |
| && (TYPE_CODE (TYPE_FIELD_TYPE (type, 1)) |
| == TYPE_CODE_FLT))) |
| && tdep->mips_fpu_type != MIPS_FPU_NONE) |
| { |
| /* A struct that contains one or two floats. Each value is part |
| in the least significant part of their floating point |
| register.. */ |
| gdb_byte reg[MAX_REGISTER_SIZE]; |
| int regnum; |
| int field; |
| for (field = 0, regnum = mips_regnum (gdbarch)->fp0; |
| field < TYPE_NFIELDS (type); field++, regnum += 2) |
| { |
| int offset = (FIELD_BITPOS (TYPE_FIELDS (type)[field]) |
| / TARGET_CHAR_BIT); |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stderr, "Return float struct+%d\n", |
| offset); |
| mips_xfer_register (gdbarch, regcache, |
| gdbarch_num_regs (gdbarch) + regnum, |
| TYPE_LENGTH (TYPE_FIELD_TYPE (type, field)), |
| gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, offset); |
| } |
| return RETURN_VALUE_REGISTER_CONVENTION; |
| } |
| #endif |
| #if 0 |
| else if (TYPE_CODE (type) == TYPE_CODE_STRUCT |
| || TYPE_CODE (type) == TYPE_CODE_UNION) |
| { |
| /* A structure or union. Extract the left justified value, |
| regardless of the byte order. I.e. DO NOT USE |
| mips_xfer_lower. */ |
| int offset; |
| int regnum; |
| for (offset = 0, regnum = MIPS_V0_REGNUM; |
| offset < TYPE_LENGTH (type); |
| offset += register_size (gdbarch, regnum), regnum++) |
| { |
| int xfer = register_size (gdbarch, regnum); |
| if (offset + xfer > TYPE_LENGTH (type)) |
| xfer = TYPE_LENGTH (type) - offset; |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stderr, "Return struct+%d:%d in $%d\n", |
| offset, xfer, regnum); |
| mips_xfer_register (gdbarch, regcache, |
| gdbarch_num_regs (gdbarch) + regnum, xfer, |
| BFD_ENDIAN_UNKNOWN, readbuf, writebuf, offset); |
| } |
| return RETURN_VALUE_REGISTER_CONVENTION; |
| } |
| #endif |
| else |
| { |
| /* A scalar extract each part but least-significant-byte |
| justified. o32 thinks registers are 4 byte, regardless of |
| the ISA. */ |
| int offset; |
| int regnum; |
| for (offset = 0, regnum = MIPS_V0_REGNUM; |
| offset < TYPE_LENGTH (type); |
| offset += MIPS32_REGSIZE, regnum++) |
| { |
| int xfer = MIPS32_REGSIZE; |
| if (offset + xfer > TYPE_LENGTH (type)) |
| xfer = TYPE_LENGTH (type) - offset; |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stderr, "Return scalar+%d:%d in $%d\n", |
| offset, xfer, regnum); |
| mips_xfer_register (gdbarch, regcache, |
| gdbarch_num_regs (gdbarch) + regnum, xfer, |
| gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, offset); |
| } |
| return RETURN_VALUE_REGISTER_CONVENTION; |
| } |
| } |
| |
| /* O64 ABI. This is a hacked up kind of 64-bit version of the o32 |
| ABI. */ |
| |
| static CORE_ADDR |
| mips_o64_push_dummy_call (struct gdbarch *gdbarch, struct value *function, |
| struct regcache *regcache, CORE_ADDR bp_addr, |
| int nargs, |
| struct value **args, CORE_ADDR sp, |
| int struct_return, CORE_ADDR struct_addr) |
| { |
| int argreg; |
| int float_argreg; |
| int argnum; |
| int len = 0; |
| int stack_offset = 0; |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| CORE_ADDR func_addr = find_function_addr (function, NULL); |
| |
| /* For shared libraries, "t9" needs to point at the function |
| address. */ |
| regcache_cooked_write_signed (regcache, MIPS_T9_REGNUM, func_addr); |
| |
| /* Set the return address register to point to the entry point of |
| the program, where a breakpoint lies in wait. */ |
| regcache_cooked_write_signed (regcache, MIPS_RA_REGNUM, bp_addr); |
| |
| /* First ensure that the stack and structure return address (if any) |
| are properly aligned. The stack has to be at least 64-bit |
| aligned even on 32-bit machines, because doubles must be 64-bit |
| aligned. For n32 and n64, stack frames need to be 128-bit |
| aligned, so we round to this widest known alignment. */ |
| |
| sp = align_down (sp, 16); |
| struct_addr = align_down (struct_addr, 16); |
| |
| /* Now make space on the stack for the args. */ |
| for (argnum = 0; argnum < nargs; argnum++) |
| { |
| struct type *arg_type = check_typedef (value_type (args[argnum])); |
| |
| /* Allocate space on the stack. */ |
| len += align_up (TYPE_LENGTH (arg_type), MIPS64_REGSIZE); |
| } |
| sp -= align_up (len, 16); |
| |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "mips_o64_push_dummy_call: sp=%s allocated %ld\n", |
| paddress (gdbarch, sp), (long) align_up (len, 16)); |
| |
| /* Initialize the integer and float register pointers. */ |
| argreg = MIPS_A0_REGNUM; |
| float_argreg = mips_fpa0_regnum (gdbarch); |
| |
| /* The struct_return pointer occupies the first parameter-passing reg. */ |
| if (struct_return) |
| { |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "mips_o64_push_dummy_call: " |
| "struct_return reg=%d %s\n", |
| argreg, paddress (gdbarch, struct_addr)); |
| regcache_cooked_write_unsigned (regcache, argreg++, struct_addr); |
| stack_offset += MIPS64_REGSIZE; |
| } |
| |
| /* Now load as many as possible of the first arguments into |
| registers, and push the rest onto the stack. Loop thru args |
| from first to last. */ |
| for (argnum = 0; argnum < nargs; argnum++) |
| { |
| const gdb_byte *val; |
| struct value *arg = args[argnum]; |
| struct type *arg_type = check_typedef (value_type (arg)); |
| int len = TYPE_LENGTH (arg_type); |
| enum type_code typecode = TYPE_CODE (arg_type); |
| |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "mips_o64_push_dummy_call: %d len=%d type=%d", |
| argnum + 1, len, (int) typecode); |
| |
| val = value_contents (arg); |
| |
| /* Floating point arguments passed in registers have to be |
| treated specially. On 32-bit architectures, doubles are |
| passed in register pairs; the even FP register gets the |
| low word, and the odd FP register gets the high word. |
| On O64, the first two floating point arguments are also |
| copied to general registers, because MIPS16 functions |
| don't use float registers for arguments. This duplication |
| of arguments in general registers can't hurt non-MIPS16 |
| functions because those registers are normally skipped. */ |
| |
| if (fp_register_arg_p (gdbarch, typecode, arg_type) |
| && float_argreg <= MIPS_LAST_FP_ARG_REGNUM (gdbarch)) |
| { |
| LONGEST regval = extract_unsigned_integer (val, len, byte_order); |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s", |
| float_argreg, phex (regval, len)); |
| regcache_cooked_write_unsigned (regcache, float_argreg++, regval); |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s", |
| argreg, phex (regval, len)); |
| regcache_cooked_write_unsigned (regcache, argreg, regval); |
| argreg++; |
| /* Reserve space for the FP register. */ |
| stack_offset += align_up (len, MIPS64_REGSIZE); |
| } |
| else |
| { |
| /* Copy the argument to general registers or the stack in |
| register-sized pieces. Large arguments are split between |
| registers and stack. */ |
| /* Note: structs whose size is not a multiple of MIPS64_REGSIZE |
| are treated specially: Irix cc passes them in registers |
| where gcc sometimes puts them on the stack. For maximum |
| compatibility, we will put them in both places. */ |
| int odd_sized_struct = (len > MIPS64_REGSIZE |
| && len % MIPS64_REGSIZE != 0); |
| while (len > 0) |
| { |
| /* Remember if the argument was written to the stack. */ |
| int stack_used_p = 0; |
| int partial_len = (len < MIPS64_REGSIZE ? len : MIPS64_REGSIZE); |
| |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, " -- partial=%d", |
| partial_len); |
| |
| /* Write this portion of the argument to the stack. */ |
| if (argreg > MIPS_LAST_ARG_REGNUM (gdbarch) |
| || odd_sized_struct) |
| { |
| /* Should shorter than int integer values be |
| promoted to int before being stored? */ |
| int longword_offset = 0; |
| CORE_ADDR addr; |
| stack_used_p = 1; |
| if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) |
| { |
| if ((typecode == TYPE_CODE_INT |
| || typecode == TYPE_CODE_PTR |
| || typecode == TYPE_CODE_FLT) |
| && len <= 4) |
| longword_offset = MIPS64_REGSIZE - len; |
| } |
| |
| if (mips_debug) |
| { |
| fprintf_unfiltered (gdb_stdlog, " - stack_offset=%s", |
| paddress (gdbarch, stack_offset)); |
| fprintf_unfiltered (gdb_stdlog, " longword_offset=%s", |
| paddress (gdbarch, longword_offset)); |
| } |
| |
| addr = sp + stack_offset + longword_offset; |
| |
| if (mips_debug) |
| { |
| int i; |
| fprintf_unfiltered (gdb_stdlog, " @%s ", |
| paddress (gdbarch, addr)); |
| for (i = 0; i < partial_len; i++) |
| { |
| fprintf_unfiltered (gdb_stdlog, "%02x", |
| val[i] & 0xff); |
| } |
| } |
| write_memory (addr, val, partial_len); |
| } |
| |
| /* Note!!! This is NOT an else clause. Odd sized |
| structs may go thru BOTH paths. */ |
| /* Write this portion of the argument to a general |
| purpose register. */ |
| if (argreg <= MIPS_LAST_ARG_REGNUM (gdbarch)) |
| { |
| LONGEST regval = extract_signed_integer (val, partial_len, |
| byte_order); |
| /* Value may need to be sign extended, because |
| mips_isa_regsize() != mips_abi_regsize(). */ |
| |
| /* A non-floating-point argument being passed in a |
| general register. If a struct or union, and if |
| the remaining length is smaller than the register |
| size, we have to adjust the register value on |
| big endian targets. |
| |
| It does not seem to be necessary to do the |
| same for integral types. */ |
| |
| if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG |
| && partial_len < MIPS64_REGSIZE |
| && (typecode == TYPE_CODE_STRUCT |
| || typecode == TYPE_CODE_UNION)) |
| regval <<= ((MIPS64_REGSIZE - partial_len) |
| * TARGET_CHAR_BIT); |
| |
| if (mips_debug) |
| fprintf_filtered (gdb_stdlog, " - reg=%d val=%s", |
| argreg, |
| phex (regval, MIPS64_REGSIZE)); |
| regcache_cooked_write_unsigned (regcache, argreg, regval); |
| argreg++; |
| |
| /* Prevent subsequent floating point arguments from |
| being passed in floating point registers. */ |
| float_argreg = MIPS_LAST_FP_ARG_REGNUM (gdbarch) + 1; |
| } |
| |
| len -= partial_len; |
| val += partial_len; |
| |
| /* Compute the offset into the stack at which we will |
| copy the next parameter. |
| |
| In older ABIs, the caller reserved space for |
| registers that contained arguments. This was loosely |
| refered to as their "home". Consequently, space is |
| always allocated. */ |
| |
| stack_offset += align_up (partial_len, MIPS64_REGSIZE); |
| } |
| } |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stdlog, "\n"); |
| } |
| |
| regcache_cooked_write_signed (regcache, MIPS_SP_REGNUM, sp); |
| |
| /* Return adjusted stack pointer. */ |
| return sp; |
| } |
| |
| static enum return_value_convention |
| mips_o64_return_value (struct gdbarch *gdbarch, struct value *function, |
| struct type *type, struct regcache *regcache, |
| gdb_byte *readbuf, const gdb_byte *writebuf) |
| { |
| CORE_ADDR func_addr = function ? find_function_addr (function, NULL) : 0; |
| int mips16 = mips_pc_is_mips16 (gdbarch, func_addr); |
| struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
| enum mips_fval_reg fval_reg; |
| |
| fval_reg = readbuf ? mips16 ? mips_fval_gpr : mips_fval_fpr : mips_fval_both; |
| if (TYPE_CODE (type) == TYPE_CODE_STRUCT |
| || TYPE_CODE (type) == TYPE_CODE_UNION |
| || TYPE_CODE (type) == TYPE_CODE_ARRAY) |
| return RETURN_VALUE_STRUCT_CONVENTION; |
| else if (fp_register_arg_p (gdbarch, TYPE_CODE (type), type)) |
| { |
| /* A floating-point value. If reading in or copying, then we get it |
| from/put it to FP0 for standard MIPS code or GPR2 for MIPS16 code. |
| If writing out only, then we put it to both FP0 and GPR2. We do |
| not support reading in with no function known, if this safety |
| check ever triggers, then we'll have to try harder. */ |
| gdb_assert (function || !readbuf); |
| if (mips_debug) |
| switch (fval_reg) |
| { |
| case mips_fval_fpr: |
| fprintf_unfiltered (gdb_stderr, "Return float in $fp0\n"); |
| break; |
| case mips_fval_gpr: |
| fprintf_unfiltered (gdb_stderr, "Return float in $2\n"); |
| break; |
| case mips_fval_both: |
| fprintf_unfiltered (gdb_stderr, "Return float in $fp0 and $2\n"); |
| break; |
| } |
| if (fval_reg != mips_fval_gpr) |
| mips_xfer_register (gdbarch, regcache, |
| (gdbarch_num_regs (gdbarch) |
| + mips_regnum (gdbarch)->fp0), |
| TYPE_LENGTH (type), |
| gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, 0); |
| if (fval_reg != mips_fval_fpr) |
| mips_xfer_register (gdbarch, regcache, |
| gdbarch_num_regs (gdbarch) + 2, |
| TYPE_LENGTH (type), |
| gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, 0); |
| return RETURN_VALUE_REGISTER_CONVENTION; |
| } |
| else |
| { |
| /* A scalar extract each part but least-significant-byte |
| justified. */ |
| int offset; |
| int regnum; |
| for (offset = 0, regnum = MIPS_V0_REGNUM; |
| offset < TYPE_LENGTH (type); |
| offset += MIPS64_REGSIZE, regnum++) |
| { |
| int xfer = MIPS64_REGSIZE; |
| if (offset + xfer > TYPE_LENGTH (type)) |
| xfer = TYPE_LENGTH (type) - offset; |
| if (mips_debug) |
| fprintf_unfiltered (gdb_stderr, "Return scalar+%d:%d in $%d\n", |
| offset, xfer, regnum); |
| mips_xfer_register (gdbarch, regcache, |
| gdbarch_num_regs (gdbarch) + regnum, |
| xfer, gdbarch_byte_order (gdbarch), |
| readbuf, writebuf, offset); |
| } |
| return RETURN_VALUE_REGISTER_CONVENTION; |
| } |
| } |
| |
| /* Floating point register management. |
| |
| Background: MIPS1 & 2 fp registers are 32 bits wide. To support |
| 64bit operations, these early MIPS cpus treat fp register pairs |
| (f0,f1) as a single register (d0). Later MIPS cpu's have 64 bit fp |
| registers and offer a compatibility mode that emulates the MIPS2 fp |
| model. When operating in MIPS2 fp compat mode, later cpu's split |
| double precision floats into two 32-bit chunks and store them in |
| consecutive fp regs. To display 64-bit floats stored in this |
| fashion, we have to combine 32 bits from f0 and 32 bits from f1. |
| Throw in user-configurable endianness and you have a real mess. |
| |
| The way this works is: |
| - If we are in 32-bit mode or on a 32-bit processor, then a 64-bit |
| double-precision value will be split across two logical registers. |
| The lower-numbered logical register will hold the low-order bits, |
| regardless of the processor's endianness. |
| - If we are on a 64-bit processor, and we are looking for a |
| single-precision value, it will be in the low ordered bits |
| of a 64-bit GPR (after mfc1, for example) or a 64-bit register |
| save slot in memory. |
| - If we are in 64-bit mode, everything is straightforward. |
| |
| Note that this code only deals with "live" registers at the top of the |
| stack. We will attempt to deal with saved registers later, when |
| the raw/cooked register interface is in place. (We need a general |
| interface that can deal with dynamic saved register sizes -- fp |
| regs could be 32 bits wide in one frame and 64 on the frame above |
| and below). */ |
| |
| /* Copy a 32-bit single-precision value from the current frame |
| into rare_buffer. */ |
| |
| static void |
| mips_read_fp_register_single (struct frame_info *frame, int regno, |
| gdb_byte *rare_buffer) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| int raw_size = register_size (gdbarch, regno); |
| gdb_byte *raw_buffer = (gdb_byte *) alloca (raw_size); |
| |
| if (!deprecated_frame_register_read (frame, regno, raw_buffer)) |
| error (_("can't read register %d (%s)"), |
| regno, gdbarch_register_name (gdbarch, regno)); |
| if (raw_size == 8) |
| { |
| /* We have a 64-bit value for this register. Find the low-order |
| 32 bits. */ |
| int offset; |
| |
| if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) |
| offset = 4; |
| else |
| offset = 0; |
| |
| memcpy (rare_buffer, raw_buffer + offset, 4); |
| } |
| else |
| { |
| memcpy (rare_buffer, raw_buffer, 4); |
| } |
| } |
| |
| /* Copy a 64-bit double-precision value from the current frame into |
| rare_buffer. This may include getting half of it from the next |
| register. */ |
| |
| static void |
| mips_read_fp_register_double (struct frame_info *frame, int regno, |
| gdb_byte *rare_buffer) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| int raw_size = register_size (gdbarch, regno); |
| |
| if (raw_size == 8 && !mips2_fp_compat (frame)) |
| { |
| /* We have a 64-bit value for this register, and we should use |
| all 64 bits. */ |
| if (!deprecated_frame_register_read (frame, regno, rare_buffer)) |
| error (_("can't read register %d (%s)"), |
| regno, gdbarch_register_name (gdbarch, regno)); |
| } |
| else |
| { |
| int rawnum = regno % gdbarch_num_regs (gdbarch); |
| |
| if ((rawnum - mips_regnum (gdbarch)->fp0) & 1) |
| internal_error (__FILE__, __LINE__, |
| _("mips_read_fp_register_double: bad access to " |
| "odd-numbered FP register")); |
| |
| /* mips_read_fp_register_single will find the correct 32 bits from |
| each register. */ |
| if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) |
| { |
| mips_read_fp_register_single (frame, regno, rare_buffer + 4); |
| mips_read_fp_register_single (frame, regno + 1, rare_buffer); |
| } |
| else |
| { |
| mips_read_fp_register_single (frame, regno, rare_buffer); |
| mips_read_fp_register_single (frame, regno + 1, rare_buffer + 4); |
| } |
| } |
| } |
| |
| static void |
| mips_print_fp_register (struct ui_file *file, struct frame_info *frame, |
| int regnum) |
| { /* Do values for FP (float) regs. */ |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| gdb_byte *raw_buffer; |
| double doub, flt1; /* Doubles extracted from raw hex data. */ |
| int inv1, inv2; |
| |
| raw_buffer |
| = ((gdb_byte *) |
| alloca (2 * register_size (gdbarch, mips_regnum (gdbarch)->fp0))); |
| |
| fprintf_filtered (file, "%s:", gdbarch_register_name (gdbarch, regnum)); |
| fprintf_filtered (file, "%*s", |
| 4 - (int) strlen (gdbarch_register_name (gdbarch, regnum)), |
| ""); |
| |
| if (register_size (gdbarch, regnum) == 4 || mips2_fp_compat (frame)) |
| { |
| struct value_print_options opts; |
| |
| /* 4-byte registers: Print hex and floating. Also print even |
| numbered registers as doubles. */ |
| mips_read_fp_register_single (frame, regnum, raw_buffer); |
| flt1 = unpack_double (builtin_type (gdbarch)->builtin_float, |
| raw_buffer, &inv1); |
| |
| get_formatted_print_options (&opts, 'x'); |
| print_scalar_formatted (raw_buffer, |
| builtin_type (gdbarch)->builtin_uint32, |
| &opts, 'w', file); |
| |
| fprintf_filtered (file, " flt: "); |
| if (inv1) |
| fprintf_filtered (file, " <invalid float> "); |
| else |
| fprintf_filtered (file, "%-17.9g", flt1); |
| |
| if ((regnum - gdbarch_num_regs (gdbarch)) % 2 == 0) |
| { |
| mips_read_fp_register_double (frame, regnum, raw_buffer); |
| doub = unpack_double (builtin_type (gdbarch)->builtin_double, |
| raw_buffer, &inv2); |
| |
| fprintf_filtered (file, " dbl: "); |
| if (inv2) |
| fprintf_filtered (file, "<invalid double>"); |
| else |
| fprintf_filtered (file, "%-24.17g", doub); |
| } |
| } |
| else |
| { |
| struct value_print_options opts; |
| |
| /* Eight byte registers: print each one as hex, float and double. */ |
| mips_read_fp_register_single (frame, regnum, raw_buffer); |
| flt1 = unpack_double (builtin_type (gdbarch)->builtin_float, |
| raw_buffer, &inv1); |
| |
| mips_read_fp_register_double (frame, regnum, raw_buffer); |
| doub = unpack_double (builtin_type (gdbarch)->builtin_double, |
| raw_buffer, &inv2); |
| |
| get_formatted_print_options (&opts, 'x'); |
| print_scalar_formatted (raw_buffer, |
| builtin_type (gdbarch)->builtin_uint64, |
| &opts, 'g', file); |
| |
| fprintf_filtered (file, " flt: "); |
| if (inv1) |
| fprintf_filtered (file, "<invalid float>"); |
| else |
| fprintf_filtered (file, "%-17.9g", flt1); |
| |
| fprintf_filtered (file, " dbl: "); |
| if (inv2) |
| fprintf_filtered (file, "<invalid double>"); |
| else |
| fprintf_filtered (file, "%-24.17g", doub); |
| } |
| } |
| |
| static void |
| mips_print_register (struct ui_file *file, struct frame_info *frame, |
| int regnum) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| struct value_print_options opts; |
| struct value *val; |
| |
| if (mips_float_register_p (gdbarch, regnum)) |
| { |
| mips_print_fp_register (file, frame, regnum); |
| return; |
| } |
| |
| val = get_frame_register_value (frame, regnum); |
| |
| fputs_filtered (gdbarch_register_name (gdbarch, regnum), file); |
| |
| /* The problem with printing numeric register names (r26, etc.) is that |
| the user can't use them on input. Probably the best solution is to |
| fix it so that either the numeric or the funky (a2, etc.) names |
| are accepted on input. */ |
| if (regnum < MIPS_NUMREGS) |
| fprintf_filtered (file, "(r%d): ", regnum); |
| else |
| fprintf_filtered (file, ": "); |
| |
| get_formatted_print_options (&opts, 'x'); |
| val_print_scalar_formatted (value_type (val), |
| value_contents_for_printing (val), |
| value_embedded_offset (val), |
| val, |
| &opts, 0, file); |
| } |
| |
| /* Print IEEE exception condition bits in FLAGS. */ |
| |
| static void |
| print_fpu_flags (struct ui_file *file, int flags) |
| { |
| if (flags & (1 << 0)) |
| fputs_filtered (" inexact", file); |
| if (flags & (1 << 1)) |
| fputs_filtered (" uflow", file); |
| if (flags & (1 << 2)) |
| fputs_filtered (" oflow", file); |
| if (flags & (1 << 3)) |
| fputs_filtered (" div0", file); |
| if (flags & (1 << 4)) |
| fputs_filtered (" inval", file); |
| if (flags & (1 << 5)) |
| fputs_filtered (" unimp", file); |
| fputc_filtered ('\n', file); |
| } |
| |
| /* Print interesting information about the floating point processor |
| (if present) or emulator. */ |
| |
| static void |
| mips_print_float_info (struct gdbarch *gdbarch, struct ui_file *file, |
| struct frame_info *frame, const char *args) |
| { |
| int fcsr = mips_regnum (gdbarch)->fp_control_status; |
| enum mips_fpu_type type = MIPS_FPU_TYPE (gdbarch); |
| ULONGEST fcs = 0; |
| int i; |
| |
| if (fcsr == -1 || !read_frame_register_unsigned (frame, fcsr, &fcs)) |
| type = MIPS_FPU_NONE; |
| |
| fprintf_filtered (file, "fpu type: %s\n", |
| type == MIPS_FPU_DOUBLE ? "double-precision" |
| : type == MIPS_FPU_SINGLE ? "single-precision" |
| : "none / unused"); |
| |
| if (type == MIPS_FPU_NONE) |
| return; |
| |
| fprintf_filtered (file, "reg size: %d bits\n", |
| register_size (gdbarch, mips_regnum (gdbarch)->fp0) * 8); |
| |
| fputs_filtered ("cond :", file); |
| if (fcs & (1 << 23)) |
| fputs_filtered (" 0", file); |
| for (i = 1; i <= 7; i++) |
| if (fcs & (1 << (24 + i))) |
| fprintf_filtered (file, " %d", i); |
| fputc_filtered ('\n', file); |
| |
| fputs_filtered ("cause :", file); |
| print_fpu_flags (file, (fcs >> 12) & 0x3f); |
| fputs ("mask :", stdout); |
| print_fpu_flags (file, (fcs >> 7) & 0x1f); |
| fputs ("flags :", stdout); |
| print_fpu_flags (file, (fcs >> 2) & 0x1f); |
| |
| fputs_filtered ("rounding: ", file); |
| switch (fcs & 3) |
| { |
| case 0: fputs_filtered ("nearest\n", file); break; |
| case 1: fputs_filtered ("zero\n", file); break; |
| case 2: fputs_filtered ("+inf\n", file); break; |
| case 3: fputs_filtered ("-inf\n", file); break; |
| } |
| |
| fputs_filtered ("flush :", file); |
| if (fcs & (1 << 21)) |
| fputs_filtered (" nearest", file); |
| if (fcs & (1 << 22)) |
| fputs_filtered (" override", file); |
| if (fcs & (1 << 24)) |
| fputs_filtered (" zero", file); |
| if ((fcs & (0xb << 21)) == 0) |
| fputs_filtered (" no", file); |
| fputc_filtered ('\n', file); |
| |
| fprintf_filtered (file, "nan2008 : %s\n", fcs & (1 << 18) ? "yes" : "no"); |
| fprintf_filtered (file, "abs2008 : %s\n", fcs & (1 << 19) ? "yes" : "no"); |
| fputc_filtered ('\n', file); |
| |
| default_print_float_info (gdbarch, file, frame, args); |
| } |
| |
| /* Replacement for generic do_registers_info. |
| Print regs in pretty columns. */ |
| |
| static int |
| print_fp_register_row (struct ui_file *file, struct frame_info *frame, |
| int regnum) |
| { |
| fprintf_filtered (file, " "); |
| mips_print_fp_register (file, frame, regnum); |
| fprintf_filtered (file, "\n"); |
| return regnum + 1; |
| } |
| |
| |
| /* Print a row's worth of GP (int) registers, with name labels above. */ |
| |
| static int |
| print_gp_register_row (struct ui_file *file, struct frame_info *frame, |
| int start_regnum) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| /* Do values for GP (int) regs. */ |
| gdb_byte raw_buffer[MAX_REGISTER_SIZE]; |
| int ncols = (mips_abi_regsize (gdbarch) == 8 ? 4 : 8); /* display cols |
| per row. */ |
| int col, byte; |
| int regnum; |
| |
| /* For GP registers, we print a separate row of names above the vals. */ |
| for (col = 0, regnum = start_regnum; |
| col < ncols && regnum < gdbarch_num_regs (gdbarch) |
| + gdbarch_num_pseudo_regs (gdbarch); |
| regnum++) |
| { |
| if (*gdbarch_register_name (gdbarch, regnum) == '\0') |
| continue; /* unused register */ |
| if (mips_float_register_p (gdbarch, regnum)) |
| break; /* End the row: reached FP register. */ |
| /* Large registers are handled separately. */ |
| if (register_size (gdbarch, regnum) > mips_abi_regsize (gdbarch)) |
| { |
| if (col > 0) |
| break; /* End the row before this register. */ |
| |
| /* Print this register on a row by itself. */ |
| mips_print_register (file, frame, regnum); |
| fprintf_filtered (file, "\n"); |
| return regnum + 1; |
| } |
| if (col == 0) |
| fprintf_filtered (file, " "); |
| fprintf_filtered (file, |
| mips_abi_regsize (gdbarch) == 8 ? "%17s" : "%9s", |
| gdbarch_register_name (gdbarch, regnum)); |
| col++; |
| } |
| |
| if (col == 0) |
| return regnum; |
| |
| /* Print the R0 to R31 names. */ |
| if ((start_regnum % gdbarch_num_regs (gdbarch)) < MIPS_NUMREGS) |
| fprintf_filtered (file, "\n R%-4d", |
| start_regnum % gdbarch_num_regs (gdbarch)); |
| else |
| fprintf_filtered (file, "\n "); |
| |
| /* Now print the values in hex, 4 or 8 to the row. */ |
| for (col = 0, regnum = start_regnum; |
| col < ncols && regnum < gdbarch_num_regs (gdbarch) |
| + gdbarch_num_pseudo_regs (gdbarch); |
| regnum++) |
| { |
| if (*gdbarch_register_name (gdbarch, regnum) == '\0') |
| continue; /* unused register */ |
| if (mips_float_register_p (gdbarch, regnum)) |
| break; /* End row: reached FP register. */ |
| if (register_size (gdbarch, regnum) > mips_abi_regsize (gdbarch)) |
| break; /* End row: large register. */ |
| |
| /* OK: get the data in raw format. */ |
| if (!deprecated_frame_register_read (frame, regnum, raw_buffer)) |
| error (_("can't read register %d (%s)"), |
| regnum, gdbarch_register_name (gdbarch, regnum)); |
| /* pad small registers */ |
| for (byte = 0; |
| byte < (mips_abi_regsize (gdbarch) |
| - register_size (gdbarch, regnum)); byte++) |
| printf_filtered (" "); |
| /* Now print the register value in hex, endian order. */ |
| if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) |
| for (byte = |
| register_size (gdbarch, regnum) - register_size (gdbarch, regnum); |
| byte < register_size (gdbarch, regnum); byte++) |
| fprintf_filtered (file, "%02x", raw_buffer[byte]); |
| else |
| for (byte = register_size (gdbarch, regnum) - 1; |
| byte >= 0; byte--) |
| fprintf_filtered (file, "%02x", raw_buffer[byte]); |
| fprintf_filtered (file, " "); |
| col++; |
| } |
| if (col > 0) /* ie. if we actually printed anything... */ |
| fprintf_filtered (file, "\n"); |
| |
| return regnum; |
| } |
| |
| /* MIPS_DO_REGISTERS_INFO(): called by "info register" command. */ |
| |
| static void |
| mips_print_registers_info (struct gdbarch *gdbarch, struct ui_file *file, |
| struct frame_info *frame, int regnum, int all) |
| { |
| if (regnum != -1) /* Do one specified register. */ |
| { |
| gdb_assert (regnum >= gdbarch_num_regs (gdbarch)); |
| if (*(gdbarch_register_name (gdbarch, regnum)) == '\0') |
| error (_("Not a valid register for the current processor type")); |
| |
| mips_print_register (file, frame, regnum); |
| fprintf_filtered (file, "\n"); |
| } |
| else |
| /* Do all (or most) registers. */ |
| { |
| regnum = gdbarch_num_regs (gdbarch); |
| while (regnum < gdbarch_num_regs (gdbarch) |
| + gdbarch_num_pseudo_regs (gdbarch)) |
| { |
| if (mips_float_register_p (gdbarch, regnum)) |
| { |
| if (all) /* True for "INFO ALL-REGISTERS" command. */ |
| regnum = print_fp_register_row (file, frame, regnum); |
| else |
| regnum += MIPS_NUMREGS; /* Skip floating point regs. */ |
| } |
| else |
| regnum = print_gp_register_row (file, frame, regnum); |
| } |
| } |
| } |
| |
| static int |
| mips_single_step_through_delay (struct gdbarch *gdbarch, |
| struct frame_info *frame) |
| { |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| CORE_ADDR pc = get_frame_pc (frame); |
| struct address_space *aspace; |
| enum mips_isa isa; |
| ULONGEST insn; |
| int status; |
| int size; |
| |
| if ((mips_pc_is_mips (pc) |
| && !mips32_insn_at_pc_has_delay_slot (gdbarch, pc)) |
| || (mips_pc_is_micromips (gdbarch, pc) |
| && !micromips_insn_at_pc_has_delay_slot (gdbarch, pc, 0)) |
| || (mips_pc_is_mips16 (gdbarch, pc) |
| && !mips16_insn_at_pc_has_delay_slot (gdbarch, pc, 0))) |
| return 0; |
| |
| isa = mips_pc_isa (gdbarch, pc); |
| /* _has_delay_slot above will have validated the read. */ |
| insn = mips_fetch_instruction (gdbarch, isa, pc, NULL); |
| size = mips_insn_size (isa, insn); |
| aspace = get_frame_address_space (frame); |
| return breakpoint_here_p (aspace, pc + size) != no_breakpoint_here; |
| } |
| |
| /* To skip prologues, I use this predicate. Returns either PC itself |
| if the code at PC does not look like a function prologue; otherwise |
| returns an address that (if we're lucky) follows the prologue. If |
| LENIENT, then we must skip everything which is involved in setting |
| up the frame (it's OK to skip more, just so long as we don't skip |
| anything which might clobber the registers which are being saved. |
| We must skip more in the case where part of the prologue is in the |
| delay slot of a non-prologue instruction). */ |
| |
| static CORE_ADDR |
| mips_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) |
| { |
| CORE_ADDR limit_pc; |
| CORE_ADDR func_addr; |
| |
| /* 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, NULL, &func_addr, NULL)) |
| { |
| CORE_ADDR post_prologue_pc |
| = skip_prologue_using_sal (gdbarch, func_addr); |
| if (post_prologue_pc != 0) |
| return max (pc, post_prologue_pc); |
| } |
| |
| /* Can't determine prologue from the symbol table, need to examine |
| instructions. */ |
| |
| /* Find an upper limit on the function prologue using the debug |
| information. If the debug information could not be used to provide |
| that bound, then use an arbitrary large number as the upper bound. */ |
| limit_pc = skip_prologue_using_sal (gdbarch, pc); |
| if (limit_pc == 0) |
| limit_pc = pc + 100; /* Magic. */ |
| |
| if (mips_pc_is_mips16 (gdbarch, pc)) |
| return mips16_scan_prologue (gdbarch, pc, limit_pc, NULL, NULL); |
| else if (mips_pc_is_micromips (gdbarch, pc)) |
| return micromips_scan_prologue (gdbarch, pc, limit_pc, NULL, NULL); |
| else |
| return mips32_scan_prologue (gdbarch, pc, limit_pc, NULL, NULL); |
| } |
| |
| /* Implement the stack_frame_destroyed_p gdbarch method (32-bit version). |
| This is a helper function for mips_stack_frame_destroyed_p. */ |
| |
| static int |
| mips32_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc) |
| { |
| CORE_ADDR func_addr = 0, func_end = 0; |
| |
| if (find_pc_partial_function (pc, NULL, &func_addr, &func_end)) |
| { |
| /* The MIPS epilogue is max. 12 bytes long. */ |
| CORE_ADDR addr = func_end - 12; |
| |
| if (addr < func_addr + 4) |
| addr = func_addr + 4; |
| if (pc < addr) |
| return 0; |
| |
| for (; pc < func_end; pc += MIPS_INSN32_SIZE) |
| { |
| unsigned long high_word; |
| unsigned long inst; |
| |
| inst = mips_fetch_instruction (gdbarch, ISA_MIPS, pc, NULL); |
| high_word = (inst >> 16) & 0xffff; |
| |
| if (high_word != 0x27bd /* addiu $sp,$sp,offset */ |
| && high_word != 0x67bd /* daddiu $sp,$sp,offset */ |
| && inst != 0x03e00008 /* jr $ra */ |
| && inst != 0x00000000) /* nop */ |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* Implement the stack_frame_destroyed_p gdbarch method (microMIPS version). |
| This is a helper function for mips_stack_frame_destroyed_p. */ |
| |
| static int |
| micromips_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc) |
| { |
| CORE_ADDR func_addr = 0; |
| CORE_ADDR func_end = 0; |
| CORE_ADDR addr; |
| ULONGEST insn; |
| long offset; |
| int dreg; |
| int sreg; |
| int loc; |
| |
| if (!find_pc_partial_function (pc, NULL, &func_addr, &func_end)) |
| return 0; |
| |
| /* The microMIPS epilogue is max. 12 bytes long. */ |
| addr = func_end - 12; |
| |
| if (addr < func_addr + 2) |
| addr = func_addr + 2; |
| if (pc < addr) |
| return 0; |
| |
| for (; pc < func_end; pc += loc) |
| { |
| loc = 0; |
| insn = mips_fetch_instruction (gdbarch, ISA_MICROMIPS, pc, NULL); |
| loc += MIPS_INSN16_SIZE; |
| switch (mips_insn_size (ISA_MICROMIPS, insn)) |
| { |
| /* 32-bit instructions. */ |
| case 2 * MIPS_INSN16_SIZE: |
| insn <<= 16; |
| insn |= mips_fetch_instruction (gdbarch, |
| ISA_MICROMIPS, pc + loc, NULL); |
| loc += MIPS_INSN16_SIZE; |
| switch (micromips_op (insn >> 16)) |
| { |
| case 0xc: /* ADDIU: bits 001100 */ |
| case 0x17: /* DADDIU: bits 010111 */ |
| sreg = b0s5_reg (insn >> 16); |
| dreg = b5s5_reg (insn >> 16); |
| offset = (b0s16_imm (insn) ^ 0x8000) - 0x8000; |
| if (sreg == MIPS_SP_REGNUM && dreg == MIPS_SP_REGNUM |
| /* (D)ADDIU $sp, imm */ |
| && offset >= 0) |
| break; |
| return 0; |
| |
| default: |
| return 0; |
| } |
| break; |
| |
| /* 16-bit instructions. */ |
| case MIPS_INSN16_SIZE: |
| switch (micromips_op (insn)) |
| { |
| case 0x3: /* MOVE: bits 000011 */ |
| sreg = b0s5_reg (insn); |
| dreg = b5s5_reg (insn); |
| if (sreg == 0 && dreg == 0) |
| /* MOVE $zero, $zero aka NOP */ |
| break; |
| return 0; |
| |
| case 0x11: /* POOL16C: bits 010001 */ |
| if (b5s5_op (insn) == 0x18 |
| /* JRADDIUSP: bits 010011 11000 */ |
| || (b5s5_op (insn) == 0xd |
| /* JRC: bits 010011 01101 */ |
| && b0s5_reg (insn) == MIPS_RA_REGNUM)) |
| /* JRC $ra */ |
| break; |
| return 0; |
| |
| case 0x13: /* POOL16D: bits 010011 */ |
| offset = micromips_decode_imm9 (b1s9_imm (insn)); |
| if ((insn & 0x1) == 0x1 |
| /* ADDIUSP: bits 010011 1 */ |
| && offset > 0) |
| break; |
| return 0; |
| |
| default: |
| return 0; |
| } |
| } |
| } |
| |
| return 1; |
| } |
| |
| /* Implement the stack_frame_destroyed_p gdbarch method (16-bit version). |
| This is a helper function for mips_stack_frame_destroyed_p. */ |
| |
| static int |
| mips16_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc) |
| { |
| CORE_ADDR func_addr = 0, func_end = 0; |
| |
| if (find_pc_partial_function (pc, NULL, &func_addr, &func_end)) |
| { |
| /* The MIPS epilogue is max. 12 bytes long. */ |
| CORE_ADDR addr = func_end - 12; |
| |
| if (addr < func_addr + 4) |
| addr = func_addr + 4; |
| if (pc < addr) |
| return 0; |
| |
| for (; pc < func_end; pc += MIPS_INSN16_SIZE) |
| { |
| unsigned short inst; |
| |
| inst = mips_fetch_instruction (gdbarch, ISA_MIPS16, pc, NULL); |
| |
| if ((inst & 0xf800) == 0xf000) /* extend */ |
| continue; |
| |
| if (inst != 0x6300 /* addiu $sp,offset */ |
| && inst != 0xfb00 /* daddiu $sp,$sp,offset */ |
| && inst != 0xe820 /* jr $ra */ |
| && inst != 0xe8a0 /* jrc $ra */ |
| && inst != 0x6500) /* nop */ |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* Implement the stack_frame_destroyed_p gdbarch method. |
| |
| The epilogue is defined here as the area at the end of a function, |
| after an instruction which destroys the function's stack frame. */ |
| |
| static int |
| mips_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc) |
| { |
| if (mips_pc_is_mips16 (gdbarch, pc)) |
| return mips16_stack_frame_destroyed_p (gdbarch, pc); |
| else if (mips_pc_is_micromips (gdbarch, pc)) |
| return micromips_stack_frame_destroyed_p (gdbarch, pc); |
| else |
| return mips32_stack_frame_destroyed_p (gdbarch, pc); |
| } |
| |
| /* Root of all "set mips "/"show mips " commands. This will eventually be |
| used for all MIPS-specific commands. */ |
| |
| static void |
| show_mips_command (char *args, int from_tty) |
| { |
| help_list (showmipscmdlist, "show mips ", all_commands, gdb_stdout); |
| } |
| |
| static void |
| set_mips_command (char *args, int from_tty) |
| { |
| printf_unfiltered |
| ("\"set mips\" must be followed by an appropriate subcommand.\n"); |
| help_list (setmipscmdlist, "set mips ", all_commands, gdb_stdout); |
| } |
| |
| /* Commands to show/set the MIPS FPU type. */ |
| |
| static void |
| show_mipsfpu_command (char *args, int from_tty) |
| { |
| char *fpu; |
| |
| if (gdbarch_bfd_arch_info (target_gdbarch ())->arch != bfd_arch_mips) |
| { |
| printf_unfiltered |
| ("The MIPS floating-point coprocessor is unknown " |
| "because the current architecture is not MIPS.\n"); |
| return; |
| } |
| |
| switch (MIPS_FPU_TYPE (target_gdbarch ())) |
| { |
| case MIPS_FPU_SINGLE: |
| fpu = "single-precision"; |
| break; |
| case MIPS_FPU_DOUBLE: |
| fpu = "double-precision"; |
| break; |
| case MIPS_FPU_NONE: |
| fpu = "absent (none)"; |
| break; |
| default: |
| internal_error (__FILE__, __LINE__, _("bad switch")); |
| } |
| if (mips_fpu_type_auto) |
| printf_unfiltered ("The MIPS floating-point coprocessor " |
| "is set automatically (currently %s)\n", |
| fpu); |
| else |
| printf_unfiltered |
| ("The MIPS floating-point coprocessor is assumed to be %s\n", fpu); |
| } |
| |
| |
| static void |
| set_mipsfpu_command (char *args, int from_tty) |
| { |
| printf_unfiltered ("\"set mipsfpu\" must be followed by \"double\", " |
| "\"single\",\"none\" or \"auto\".\n"); |
| show_mipsfpu_command (args, from_tty); |
| } |
| |
| static void |
| set_mipsfpu_single_command (char *args, int from_tty) |
| { |
| struct gdbarch_info info; |
| gdbarch_info_init (&info); |
| mips_fpu_type = MIPS_FPU_SINGLE; |
| mips_fpu_type_auto = 0; |
| /* FIXME: cagney/2003-11-15: Should be setting a field in "info" |
| instead of relying on globals. Doing that would let generic code |
| handle the search for this specific architecture. */ |
| if (!gdbarch_update_p (info)) |
| internal_error (__FILE__, __LINE__, _("set mipsfpu failed")); |
| } |
| |
| static void |
| set_mipsfpu_double_command (char *args, int from_tty) |
| { |
| struct gdbarch_info info; |
| gdbarch_info_init (&info); |
| mips_fpu_type = MIPS_FPU_DOUBLE; |
| mips_fpu_type_auto = 0; |
| /* FIXME: cagney/2003-11-15: Should be setting a field in "info" |
| instead of relying on globals. Doing that would let generic code |
| handle the search for this specific architecture. */ |
| if (!gdbarch_update_p (info)) |
| internal_error (__FILE__, __LINE__, _("set mipsfpu failed")); |
| } |
| |
| static void |
| set_mipsfpu_none_command (char *args, int from_tty) |
| { |
| struct gdbarch_info info; |
| gdbarch_info_init (&info); |
| mips_fpu_type = MIPS_FPU_NONE; |
| mips_fpu_type_auto = 0; |
| /* FIXME: cagney/2003-11-15: Should be setting a field in "info" |
| instead of relying on globals. Doing that would let generic code |
| handle the search for this specific architecture. */ |
| if (!gdbarch_update_p (info)) |
| internal_error (__FILE__, __LINE__, _("set mipsfpu failed")); |
| } |
| |
| static void |
| set_mipsfpu_auto_command (char *args, int from_tty) |
| { |
| mips_fpu_type_auto = 1; |
| } |
| |
| /* Just like reinit_frame_cache, but with the right arguments to be |
| callable as an sfunc. */ |
| |
| static void |
| reinit_frame_cache_sfunc (char *args, int from_tty, |
| struct cmd_list_element *c) |
| { |
| reinit_frame_cache (); |
| } |
| |
| static int |
| gdb_print_insn_mips (bfd_vma memaddr, struct disassemble_info *info) |
| { |
| struct gdbarch *gdbarch = (struct gdbarch *) info->application_data; |
| |
| /* FIXME: cagney/2003-06-26: Is this even necessary? The |
| disassembler needs to be able to locally determine the ISA, and |
| not rely on GDB. Otherwize the stand-alone 'objdump -d' will not |
| work. */ |
| if (mips_pc_is_mips16 (gdbarch, memaddr)) |
| info->mach = bfd_mach_mips16; |
| else if (mips_pc_is_micromips (gdbarch, memaddr)) |
| info->mach = bfd_mach_mips_micromips; |
| |
| /* Round down the instruction address to the appropriate boundary. */ |
| memaddr &= (info->mach == bfd_mach_mips16 |
| || info->mach == bfd_mach_mips_micromips) ? ~1 : ~3; |
| |
| /* Set the disassembler options. */ |
| if (!info->disassembler_options) |
| /* This string is not recognized explicitly by the disassembler, |
| but it tells the disassembler to not try to guess the ABI from |
| the bfd elf headers, such that, if the user overrides the ABI |
| of a program linked as NewABI, the disassembly will follow the |
| register naming conventions specified by the user. */ |
| info->disassembler_options = "gpr-names=32"; |
| |
| /* Call the appropriate disassembler based on the target endian-ness. */ |
| if (info->endian == BFD_ENDIAN_BIG) |
| return print_insn_big_mips (memaddr, info); |
| else |
| return print_insn_little_mips (memaddr, info); |
| } |
| |
| static int |
| gdb_print_insn_mips_n32 (bfd_vma memaddr, struct disassemble_info *info) |
| { |
| /* Set up the disassembler info, so that we get the right |
| register names from libopcodes. */ |
| info->disassembler_options = "gpr-names=n32"; |
| info->flavour = bfd_target_elf_flavour; |
| |
| return gdb_print_insn_mips (memaddr, info); |
| } |
| |
| static int |
| gdb_print_insn_mips_n64 (bfd_vma memaddr, struct disassemble_info *info) |
| { |
| /* Set up the disassembler info, so that we get the right |
| register names from libopcodes. */ |
| info->disassembler_options = "gpr-names=64"; |
| info->flavour = bfd_target_elf_flavour; |
| |
| return gdb_print_insn_mips (memaddr, info); |
| } |
| |
| /* This function implements gdbarch_breakpoint_from_pc. It uses the |
| program counter value to determine whether a 16- or 32-bit breakpoint |
| should be used. It returns a pointer to a string of bytes that encode a |
| breakpoint instruction, stores the length of the string to *lenptr, and |
| adjusts pc (if necessary) to point to the actual memory location where |
| the breakpoint should be inserted. */ |
| |
| static const gdb_byte * |
| mips_breakpoint_from_pc (struct gdbarch *gdbarch, |
| CORE_ADDR *pcptr, int *lenptr) |
| { |
| CORE_ADDR pc = *pcptr; |
| |
| if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) |
| { |
| if (mips_pc_is_mips16 (gdbarch, pc)) |
| { |
| static gdb_byte mips16_big_breakpoint[] = { 0xe8, 0xa5 }; |
| *pcptr = unmake_compact_addr (pc); |
| *lenptr = sizeof (mips16_big_breakpoint); |
| return mips16_big_breakpoint; |
| } |
| else if (mips_pc_is_micromips (gdbarch, pc)) |
| { |
| static gdb_byte micromips16_big_breakpoint[] = { 0x46, 0x85 }; |
| static gdb_byte micromips32_big_breakpoint[] = { 0, 0x5, 0, 0x7 }; |
| ULONGEST insn; |
| int err; |
| int size; |
| |
| insn = mips_fetch_instruction (gdbarch, ISA_MICROMIPS, pc, &err); |
| size = err ? 2 : mips_insn_size (ISA_MICROMIPS, insn); |
| *pcptr = unmake_compact_addr (pc); |
| *lenptr = size; |
| return (size == 2) ? micromips16_big_breakpoint |
| : micromips32_big_breakpoint; |
| } |
| else |
| { |
| /* The IDT board uses an unusual breakpoint value, and |
| sometimes gets confused when it sees the usual MIPS |
| breakpoint instruction. */ |
| static gdb_byte big_breakpoint[] = { 0, 0x5, 0, 0xd }; |
| static gdb_byte pmon_big_breakpoint[] = { 0, 0, 0, 0xd }; |
| static gdb_byte idt_big_breakpoint[] = { 0, 0, 0x0a, 0xd }; |
| /* Likewise, IRIX appears to expect a different breakpoint, |
| although this is not apparent until you try to use pthreads. */ |
| static gdb_byte irix_big_breakpoint[] = { 0, 0, 0, 0xd }; |
| |
| *lenptr = sizeof (big_breakpoint); |
| |
| if (strcmp (target_shortname, "mips") == 0) |
| return idt_big_breakpoint; |
| else if (strcmp (target_shortname, "ddb") == 0 |
| || strcmp (target_shortname, "pmon") == 0 |
| || strcmp (target_shortname, "lsi") == 0) |
| return pmon_big_breakpoint; |
| else if (gdbarch_osabi (gdbarch) == GDB_OSABI_IRIX) |
| return irix_big_breakpoint; |
| else |
| return big_breakpoint; |
| } |
| } |
| else |
| { |
| if (mips_pc_is_mips16 (gdbarch, pc)) |
| { |
| static gdb_byte mips16_little_breakpoint[] = { 0xa5, 0xe8 }; |
| *pcptr = unmake_compact_addr (pc); |
| *lenptr = sizeof (mips16_little_breakpoint); |
| return mips16_little_breakpoint; |
| } |
| else if (mips_pc_is_micromips (gdbarch, pc)) |
| { |
| static gdb_byte micromips16_little_breakpoint[] = { 0x85, 0x46 }; |
| static gdb_byte micromips32_little_breakpoint[] = { 0x5, 0, 0x7, 0 }; |
| ULONGEST insn; |
| int err; |
| int size; |
| |
| insn = mips_fetch_instruction (gdbarch, ISA_MICROMIPS, pc, &err); |
| size = err ? 2 : mips_insn_size (ISA_MICROMIPS, insn); |
| *pcptr = unmake_compact_addr (pc); |
| *lenptr = size; |
| return (size == 2) ? micromips16_little_breakpoint |
| : micromips32_little_breakpoint; |
| } |
| else |
| { |
| static gdb_byte little_breakpoint[] = { 0xd, 0, 0x5, 0 }; |
| static gdb_byte pmon_little_breakpoint[] = { 0xd, 0, 0, 0 }; |
| static gdb_byte idt_little_breakpoint[] = { 0xd, 0x0a, 0, 0 }; |
| |
| *lenptr = sizeof (little_breakpoint); |
| |
| if (strcmp (target_shortname, "mips") == 0) |
| return idt_little_breakpoint; |
| else if (strcmp (target_shortname, "ddb") == 0 |
| || strcmp (target_shortname, "pmon") == 0 |
| || strcmp (target_shortname, "lsi") == 0) |
| return pmon_little_breakpoint; |
| else |
| return little_breakpoint; |
| } |
| } |
| } |
| |
| /* Determine the remote breakpoint kind suitable for the PC. The following |
| kinds are used: |
| |
| * 2 -- 16-bit MIPS16 mode breakpoint, |
| |
| * 3 -- 16-bit microMIPS mode breakpoint, |
| |
| * 4 -- 32-bit standard MIPS mode breakpoint, |
| |
| * 5 -- 32-bit microMIPS mode breakpoint. */ |
| |
| static void |
| mips_remote_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, |
| int *kindptr) |
| { |
| CORE_ADDR pc = *pcptr; |
| |
| if (mips_pc_is_mips16 (gdbarch, pc)) |
| { |
| *pcptr = unmake_compact_addr (pc); |
| *kindptr = 2; |
| } |
| else if (mips_pc_is_micromips (gdbarch, pc)) |
| { |
| ULONGEST insn; |
| int status; |
| int size; |
| |
| insn = mips_fetch_instruction (gdbarch, ISA_MICROMIPS, pc, &status); |
| size = status ? 2 : mips_insn_size (ISA_MICROMIPS, insn) == 2 ? 2 : 4; |
| *pcptr = unmake_compact_addr (pc); |
| *kindptr = size | 1; |
| } |
| else |
| *kindptr = 4; |
| } |
| |
| /* Return non-zero if the standard MIPS instruction INST has a branch |
| delay slot (i.e. it is a jump or branch instruction). This function |
| is based on mips32_next_pc. */ |
| |
| static int |
| mips32_instruction_has_delay_slot (struct gdbarch *gdbarch, ULONGEST inst) |
| { |
| int op; |
| int rs; |
| int rt; |
| |
| op = itype_op (inst); |
| if ((inst & 0xe0000000) != 0) |
| { |
| rs = itype_rs (inst); |
| rt = itype_rt (inst); |
| return (is_octeon_bbit_op (op, gdbarch) |
| || op >> 2 == 5 /* BEQL, BNEL, BLEZL, BGTZL: bits 0101xx */ |
| || op == 29 /* JALX: bits 011101 */ |
| || (op == 17 |
| && (rs == 8 |
| /* BC1F, BC1FL, BC1T, BC1TL: 010001 01000 */ |
| || (rs == 9 && (rt & 0x2) == 0) |
| /* BC1ANY2F, BC1ANY2T: bits 010001 01001 */ |
| || (rs == 10 && (rt & 0x2) == 0)))); |
| /* BC1ANY4F, BC1ANY4T: bits 010001 01010 */ |
| } |
| else |
| switch (op & 0x07) /* extract bits 28,27,26 */ |
| { |
| case 0: /* SPECIAL */ |
| op = rtype_funct (inst); |
| return (op == 8 /* JR */ |
| || op == 9); /* JALR */ |
| break; /* end SPECIAL */ |
| case 1: /* REGIMM */ |
| rs = itype_rs (inst); |
| rt = itype_rt (inst); /* branch condition */ |
| return ((rt & 0xc) == 0 |
| /* BLTZ, BLTZL, BGEZ, BGEZL: bits 000xx */ |
| /* BLTZAL, BLTZALL, BGEZAL, BGEZALL: 100xx */ |
| || ((rt & 0x1e) == 0x1c && rs == 0)); |
| /* BPOSGE32, BPOSGE64: bits 1110x */ |
| break; /* end REGIMM */ |
| default: /* J, JAL, BEQ, BNE, BLEZ, BGTZ */ |
| return 1; |
| break; |
| } |
| } |
| |
| /* Return non-zero if a standard MIPS instruction at ADDR has a branch |
| delay slot (i.e. it is a jump or branch instruction). */ |
| |
| static int |
| mips32_insn_at_pc_has_delay_slot (struct gdbarch *gdbarch, CORE_ADDR addr) |
| { |
| ULONGEST insn; |
| int status; |
| |
| insn = mips_fetch_instruction (gdbarch, ISA_MIPS, addr, &status); |
| if (status) |
| return 0; |
| |
| return mips32_instruction_has_delay_slot (gdbarch, insn); |
| } |
| |
| /* Return non-zero if the microMIPS instruction INSN, comprising the |
| 16-bit major opcode word in the high 16 bits and any second word |
| in the low 16 bits, has a branch delay slot (i.e. it is a non-compact |
| jump or branch instruction). The instruction must be 32-bit if |
| MUSTBE32 is set or can be any instruction otherwise. */ |
| |
| static int |
| micromips_instruction_has_delay_slot (ULONGEST insn, int mustbe32) |
| { |
| ULONGEST major = insn >> 16; |
| |
| switch (micromips_op (major)) |
| { |
| /* 16-bit instructions. */ |
| case 0x33: /* B16: bits 110011 */ |
| case 0x2b: /* BNEZ16: bits 101011 */ |
| case 0x23: /* BEQZ16: bits 100011 */ |
| return !mustbe32; |
| case 0x11: /* POOL16C: bits 010001 */ |
| return (!mustbe32 |
| && ((b5s5_op (major) == 0xc |
| /* JR16: bits 010001 01100 */ |
| || (b5s5_op (major) & 0x1e) == 0xe))); |
| /* JALR16, JALRS16: bits 010001 0111x */ |
| /* 32-bit instructions. */ |
| case 0x3d: /* JAL: bits 111101 */ |
| case 0x3c: /* JALX: bits 111100 */ |
| case 0x35: /* J: bits 110101 */ |
| case 0x2d: /* BNE: bits 101101 */ |
| case 0x25: /* BEQ: bits 100101 */ |
| case 0x1d: /* JALS: bits 011101 */ |
| return 1; |
| case 0x10: /* POOL32I: bits 010000 */ |
| return ((b5s5_op (major) & 0x1c) == 0x0 |
| /* BLTZ, BLTZAL, BGEZ, BGEZAL: 010000 000xx */ |
| || (b5s5_op (major) & 0x1d) == 0x4 |
| /* BLEZ, BGTZ: bits 010000 001x0 */ |
| || (b5s5_op (major) & 0x1d) == 0x11 |
| /* BLTZALS, BGEZALS: bits 010000 100x1 */ |
| || ((b5s5_op (major) & 0x1e) == 0x14 |
| && (major & 0x3) == 0x0) |
| /* BC2F, BC2T: bits 010000 1010x xxx00 */ |
| || (b5s5_op (major) & 0x1e) == 0x1a |
| /* BPOSGE64, BPOSGE32: bits 010000 1101x */ |
| || ((b5s5_op (major) & 0x1e) == 0x1c |
| && (major & 0x3) == 0x0) |
| /* BC1F, BC1T: bits 010000 1110x xxx00 */ |
| || ((b5s5_op (major) & 0x1c) == 0x1c |
| && (major & 0x3) == 0x1)); |
| /* BC1ANY*: bits 010000 111xx xxx01 */ |
| case 0x0: /* POOL32A: bits 000000 */ |
| return (b0s6_op (insn) == 0x3c |
| /* POOL32Axf: bits 000000 ... 111100 */ |
| && (b6s10_ext (insn) & 0x2bf) == 0x3c); |
| /* JALR, JALR.HB: 000000 000x111100 111100 */ |
| /* JALRS, JALRS.HB: 000000 010x111100 111100 */ |
| default: |
| return 0; |
| } |
| } |
| |
| /* Return non-zero if a microMIPS instruction at ADDR has a branch delay |
| slot (i.e. it is a non-compact jump instruction). The instruction |
| must be 32-bit if MUSTBE32 is set or can be any instruction otherwise. */ |
| |
| static int |
| micromips_insn_at_pc_has_delay_slot (struct gdbarch *gdbarch, |
| CORE_ADDR addr, int mustbe32) |
| { |
| ULONGEST insn; |
| int status; |
| int size; |
| |
| insn = mips_fetch_instruction (gdbarch, ISA_MICROMIPS, addr, &status); |
| if (status) |
| return 0; |
| size = mips_insn_size (ISA_MICROMIPS, insn); |
| insn <<= 16; |
| if (size == 2 * MIPS_INSN16_SIZE) |
| { |
| insn |= mips_fetch_instruction (gdbarch, ISA_MICROMIPS, addr, &status); |
| if (status) |
| return 0; |
| } |
| |
| return micromips_instruction_has_delay_slot (insn, mustbe32); |
| } |
| |
| /* Return non-zero if the MIPS16 instruction INST, which must be |
| a 32-bit instruction if MUSTBE32 is set or can be any instruction |
| otherwise, has a branch delay slot (i.e. it is a non-compact jump |
| instruction). This function is based on mips16_next_pc. */ |
| |
| static int |
| mips16_instruction_has_delay_slot (unsigned short inst, int mustbe32) |
| { |
| if ((inst & 0xf89f) == 0xe800) /* JR/JALR (16-bit instruction) */ |
| return !mustbe32; |
| return (inst & 0xf800) == 0x1800; /* JAL/JALX (32-bit instruction) */ |
| } |
| |
| /* Return non-zero if a MIPS16 instruction at ADDR has a branch delay |
| slot (i.e. it is a non-compact jump instruction). The instruction |
| must be 32-bit if MUSTBE32 is set or can be any instruction otherwise. */ |
| |
| static int |
| mips16_insn_at_pc_has_delay_slot (struct gdbarch *gdbarch, |
| CORE_ADDR addr, int mustbe32) |
| { |
| unsigned short insn; |
| int status; |
| |
| insn = mips_fetch_instruction (gdbarch, ISA_MIPS16, addr, &status); |
| if (status) |
| return 0; |
| |
| return mips16_instruction_has_delay_slot (insn, mustbe32); |
| } |
| |
| /* Calculate the starting address of the MIPS memory segment BPADDR is in. |
| This assumes KSSEG exists. */ |
| |
| static CORE_ADDR |
| mips_segment_boundary (CORE_ADDR bpaddr) |
| { |
| CORE_ADDR mask = CORE_ADDR_MAX; |
| int segsize; |
| |
| if (sizeof (CORE_ADDR) == 8) |
| /* Get the topmost two bits of bpaddr in a 32-bit safe manner (avoid |
| a compiler warning produced where CORE_ADDR is a 32-bit type even |
| though in that case this is dead code). */ |
| switch (bpaddr >> ((sizeof (CORE_ADDR) << 3) - 2) & 3) |
| { |
| case 3: |
| if (bpaddr == (bfd_signed_vma) (int32_t) bpaddr) |
| segsize = 29; /* 32-bit compatibility segment */ |
| else |
| segsize = 62; /* xkseg */ |
| break; |
| case 2: /* xkphys */ |
| segsize = 59; |
| break; |
| default: /* xksseg (1), xkuseg/kuseg (0) */ |
| segsize = 62; |
| break; |
| } |
| else if (bpaddr & 0x80000000) /* kernel segment */ |
| segsize = 29; |
| else |
| segsize = 31; /* user segment */ |
| mask <<= segsize; |
| return bpaddr & mask; |
| } |
| |
| /* Move the breakpoint at BPADDR out of any branch delay slot by shifting |
| it backwards if necessary. Return the address of the new location. */ |
| |
| static CORE_ADDR |
| mips_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr) |
| { |
| CORE_ADDR prev_addr; |
| CORE_ADDR boundary; |
| CORE_ADDR func_addr; |
| |
| /* If a breakpoint is set on the instruction in a branch delay slot, |
| GDB gets confused. When the breakpoint is hit, the PC isn't on |
| the instruction in the branch delay slot, the PC will point to |
| the branch instruction. Since the PC doesn't match any known |
| breakpoints, GDB reports a trap exception. |
| |
| There are two possible fixes for this problem. |
| |
| 1) When the breakpoint gets hit, see if the BD bit is set in the |
| Cause register (which indicates the last exception occurred in a |
| branch delay slot). If the BD bit is set, fix the PC to point to |
| the instruction in the branch delay slot. |
| |
| 2) When the user sets the breakpoint, don't allow him to set the |
| breakpoint on the instruction in the branch delay slot. Instead |
| move the breakpoint to the branch instruction (which will have |
| the same result). |
| |
| The problem with the first solution is that if the user then |
| single-steps the processor, the branch instruction will get |
| skipped (since GDB thinks the PC is on the instruction in the |
| branch delay slot). |
| |
| So, we'll use the second solution. To do this we need to know if |
| the instruction we're trying to set the breakpoint on is in the |
| branch delay slot. */ |
| |
| boundary = mips_segment_boundary (bpaddr); |
| |
| /* Make sure we don't scan back before the beginning of the current |
| function, since we may fetch constant data or insns that look like |
| a jump. Of course we might do that anyway if the compiler has |
| moved constants inline. :-( */ |
| if (find_pc_partial_function (bpaddr, NULL, &func_addr, NULL) |
| && func_addr > boundary && func_addr <= bpaddr) |
| boundary = func_addr; |
| |
| if (mips_pc_is_mips (bpaddr)) |
| { |
| if (bpaddr == boundary) |
| return bpaddr; |
| |
| /* If the previous instruction has a branch delay slot, we have |
| to move the breakpoint to the branch instruction. */ |
| prev_addr = bpaddr - 4; |
| if (mips32_insn_at_pc_has_delay_slot (gdbarch, prev_addr)) |
| bpaddr = prev_addr; |
| } |
| else |
| { |
| int (*insn_at_pc_has_delay_slot) (struct gdbarch *, CORE_ADDR, int); |
| CORE_ADDR addr, jmpaddr; |
| int i; |
| |
| boundary = unmake_compact_addr (boundary); |
| |
| /* The only MIPS16 instructions with delay slots are JAL, JALX, |
| JALR and JR. An absolute JAL/JALX is always 4 bytes long, |
| so try for that first, then try the 2 byte JALR/JR. |
| The microMIPS ASE has a whole range of jumps and branches |
| with delay slots, some of which take 4 bytes and some take |
| 2 bytes, so the idea is the same. |
| FIXME: We have to assume that bpaddr is not the second half |
| of an extended instruction. */ |
| insn_at_pc_has_delay_slot = (mips_pc_is_micromips (gdbarch, bpaddr) |
| ? micromips_insn_at_pc_has_delay_slot |
| : mips16_insn_at_pc_has_delay_slot); |
| |
| jmpaddr = 0; |
| addr = bpaddr; |
| for (i = 1; i < 4; i++) |
| { |
| if (unmake_compact_addr (addr) == boundary) |
| break; |
| addr -= MIPS_INSN16_SIZE; |
| if (i == 1 && insn_at_pc_has_delay_slot (gdbarch, addr, 0)) |
| /* Looks like a JR/JALR at [target-1], but it could be |
| the second word of a previous JAL/JALX, so record it |
| and check back one more. */ |
| jmpaddr = addr; |
| else if (i > 1 && insn_at_pc_has_delay_slot (gdbarch, addr, 1)) |
| { |
| if (i == 2) |
| /* Looks like a JAL/JALX at [target-2], but it could also |
| be the second word of a previous JAL/JALX, record it, |
| and check back one more. */ |
| jmpaddr = addr; |
| else |
| /* Looks like a JAL/JALX at [target-3], so any previously |
| recorded JAL/JALX or JR/JALR must be wrong, because: |
| |
| >-3: JAL |
| -2: JAL-ext (can't be JAL/JALX) |
| -1: bdslot (can't be JR/JALR) |
| 0: target insn |
| |
| Of course it could be another JAL-ext which looks |
| like a JAL, but in that case we'd have broken out |
| of this loop at [target-2]: |
| |
| -4: JAL |
| >-3: JAL-ext |
| -2: bdslot (can't be jmp) |
| -1: JR/JALR |
| 0: target insn */ |
| jmpaddr = 0; |
| } |
| else |
| { |
| /* Not a jump instruction: if we're at [target-1] this |
| could be the second word of a JAL/JALX, so continue; |
| otherwise we're done. */ |
| if (i > 1) |
| break; |
| } |
| } |
| |
| if (jmpaddr) |
| bpaddr = jmpaddr; |
| } |
| |
| return bpaddr; |
| } |
| |
| /* Return non-zero if SUFFIX is one of the numeric suffixes used for MIPS16 |
| call stubs, one of 1, 2, 5, 6, 9, 10, or, if ZERO is non-zero, also 0. */ |
| |
| static int |
| mips_is_stub_suffix (const char *suffix, int zero) |
| { |
| switch (suffix[0]) |
| { |
| case '0': |
| return zero && suffix[1] == '\0'; |
| case '1': |
| return suffix[1] == '\0' || (suffix[1] == '0' && suffix[2] == '\0'); |
| case '2': |
| case '5': |
| case '6': |
| case '9': |
| return suffix[1] == '\0'; |
| default: |
| return 0; |
| } |
| } |
| |
| /* Return non-zero if MODE is one of the mode infixes used for MIPS16 |
| call stubs, one of sf, df, sc, or dc. */ |
| |
| static int |
| mips_is_stub_mode (const char *mode) |
| { |
| return ((mode[0] == 's' || mode[0] == 'd') |
| && (mode[1] == 'f' || mode[1] == 'c')); |
| } |
| |
| /* Code at PC is a compiler-generated stub. Such a stub for a function |
| bar might have a name like __fn_stub_bar, and might look like this: |
| |
| mfc1 $4, $f13 |
| mfc1 $5, $f12 |
| mfc1 $6, $f15 |
| mfc1 $7, $f14 |
| |
| followed by (or interspersed with): |
| |
| j bar |
| |
| or: |
| |
| lui $25, %hi(bar) |
| addiu $25, $25, %lo(bar) |
| jr $25 |
| |
| ($1 may be used in old code; for robustness we accept any register) |
| or, in PIC code: |
| |
| lui $28, %hi(_gp_disp) |
| addiu $28, $28, %lo(_gp_disp) |
| addu $28, $28, $25 |
| lw $25, %got(bar) |
| addiu $25, $25, %lo(bar) |
| jr $25 |
| |
| In the case of a __call_stub_bar stub, the sequence to set up |
| arguments might look like this: |
| |
| mtc1 $4, $f13 |
| mtc1 $5, $f12 |
| mtc1 $6, $f15 |
| mtc1 $7, $f14 |
| |
| followed by (or interspersed with) one of the jump sequences above. |
| |
| In the case of a __call_stub_fp_bar stub, JAL or JALR is used instead |
| of J or JR, respectively, followed by: |
| |
| mfc1 $2, $f0 |
| mfc1 $3, $f1 |
| jr $18 |
| |
| We are at the beginning of the stub here, and scan down and extract |
| the target address from the jump immediate instruction or, if a jump |
| register instruction is used, from the register referred. Return |
| the value of PC calculated or 0 if inconclusive. |
| |
| The limit on the search is arbitrarily set to 20 instructions. FIXME. */ |
| |
| static CORE_ADDR |
| mips_get_mips16_fn_stub_pc (struct frame_info *frame, CORE_ADDR pc) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| int addrreg = MIPS_ZERO_REGNUM; |
| CORE_ADDR start_pc = pc; |
| CORE_ADDR target_pc = 0; |
| CORE_ADDR addr = 0; |
| CORE_ADDR gp = 0; |
| int status = 0; |
| int i; |
| |
| for (i = 0; |
| status == 0 && target_pc == 0 && i < 20; |
| i++, pc += MIPS_INSN32_SIZE) |
| { |
| ULONGEST inst = mips_fetch_instruction (gdbarch, ISA_MIPS, pc, NULL); |
| CORE_ADDR imm; |
| int rt; |
| int rs; |
| int rd; |
| |
| switch (itype_op (inst)) |
| { |
| case 0: /* SPECIAL */ |
| switch (rtype_funct (inst)) |
| { |
| case 8: /* JR */ |
| case 9: /* JALR */ |
| rs = rtype_rs (inst); |
| if (rs == MIPS_GP_REGNUM) |
| target_pc = gp; /* Hmm... */ |
| else if (rs == addrreg) |
| target_pc = addr; |
| break; |
| |
| case 0x21: /* ADDU */ |
| rt = rtype_rt (inst); |
| rs = rtype_rs (inst); |
| rd = rtype_rd (inst); |
| if (rd == MIPS_GP_REGNUM |
| && ((rs == MIPS_GP_REGNUM && rt == MIPS_T9_REGNUM) |
| || (rs == MIPS_T9_REGNUM && rt == MIPS_GP_REGNUM))) |
| gp += start_pc; |
| break; |
| } |
| break; |
| |
| case 2: /* J */ |
| case 3: /* JAL */ |
| target_pc = jtype_target (inst) << 2; |
| target_pc += ((pc + 4) & ~(CORE_ADDR) 0x0fffffff); |
| break; |
| |
| case 9: /* ADDIU */ |
| rt = itype_rt (inst); |
| rs = itype_rs (inst); |
| if (rt == rs) |
| { |
| imm = (itype_immediate (inst) ^ 0x8000) - 0x8000; |
| if (rt == MIPS_GP_REGNUM) |
| gp += imm; |
| else if (rt == addrreg) |
| addr += imm; |
| } |
| break; |
| |
| case 0xf: /* LUI */ |
| rt = itype_rt (inst); |
| imm = ((itype_immediate (inst) ^ 0x8000) - 0x8000) << 16; |
| if (rt == MIPS_GP_REGNUM) |
| gp = imm; |
| else if (rt != MIPS_ZERO_REGNUM) |
| { |
| addrreg = rt; |
| addr = imm; |
| } |
| break; |
| |
| case 0x23: /* LW */ |
| rt = itype_rt (inst); |
| rs = itype_rs (inst); |
| imm = (itype_immediate (inst) ^ 0x8000) - 0x8000; |
| if (gp != 0 && rs == MIPS_GP_REGNUM) |
| { |
| gdb_byte buf[4]; |
| |
| memset (buf, 0, sizeof (buf)); |
| status = target_read_memory (gp + imm, buf, sizeof (buf)); |
| addrreg = rt; |
| addr = extract_signed_integer (buf, sizeof (buf), byte_order); |
| } |
| break; |
| } |
| } |
| |
| return target_pc; |
| } |
| |
| /* If PC is in a MIPS16 call or return stub, return the address of the |
| target PC, which is either the callee or the caller. There are several |
| cases which must be handled: |
| |
| * If the PC is in __mips16_ret_{d,s}{f,c}, this is a return stub |
| and the target PC is in $31 ($ra). |
| * If the PC is in __mips16_call_stub_{1..10}, this is a call stub |
| and the target PC is in $2. |
| * If the PC at the start of __mips16_call_stub_{s,d}{f,c}_{0..10}, |
| i.e. before the JALR instruction, this is effectively a call stub |
| and the target PC is in $2. Otherwise this is effectively |
| a return stub and the target PC is in $18. |
| * If the PC is at the start of __call_stub_fp_*, i.e. before the |
| JAL or JALR instruction, this is effectively a call stub and the |
| target PC is buried in the instruction stream. Otherwise this |
| is effectively a return stub and the target PC is in $18. |
| * If the PC is in __call_stub_* or in __fn_stub_*, this is a call |
| stub and the target PC is buried in the instruction stream. |
| |
| See the source code for the stubs in gcc/config/mips/mips16.S, or the |
| stub builder in gcc/config/mips/mips.c (mips16_build_call_stub) for the |
| gory details. */ |
| |
| static CORE_ADDR |
| mips_skip_mips16_trampoline_code (struct frame_info *frame, CORE_ADDR pc) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| CORE_ADDR start_addr; |
| const char *name; |
| size_t prefixlen; |
| |
| /* Find the starting address and name of the function containing the PC. */ |
| if (find_pc_partial_function (pc, &name, &start_addr, NULL) == 0) |
| return 0; |
| |
| /* If the PC is in __mips16_ret_{d,s}{f,c}, this is a return stub |
| and the target PC is in $31 ($ra). */ |
| prefixlen = strlen (mips_str_mips16_ret_stub); |
| if (strncmp (name, mips_str_mips16_ret_stub, prefixlen) == 0 |
| && mips_is_stub_mode (name + prefixlen) |
| && name[prefixlen + 2] == '\0') |
| return get_frame_register_signed |
| (frame, gdbarch_num_regs (gdbarch) + MIPS_RA_REGNUM); |
| |
| /* If the PC is in __mips16_call_stub_*, this is one of the call |
| call/return stubs. */ |
| prefixlen = strlen (mips_str_mips16_call_stub); |
| if (strncmp (name, mips_str_mips16_call_stub, prefixlen) == 0) |
| { |
| /* If the PC is in __mips16_call_stub_{1..10}, this is a call stub |
| and the target PC is in $2. */ |
| if (mips_is_stub_suffix (name + prefixlen, 0)) |
| return get_frame_register_signed |
| (frame, gdbarch_num_regs (gdbarch) + MIPS_V0_REGNUM); |
| |
| /* If the PC at the start of __mips16_call_stub_{s,d}{f,c}_{0..10}, |
| i.e. before the JALR instruction, this is effectively a call stub |
| and the target PC is in $2. Otherwise this is effectively |
| a return stub and the target PC is in $18. */ |
| else if (mips_is_stub_mode (name + prefixlen) |
| && name[prefixlen + 2] == '_' |
| && mips_is_stub_suffix (name + prefixlen + 3, 0)) |
| { |
| if (pc == start_addr) |
| /* This is the 'call' part of a call stub. The return |
| address is in $2. */ |
| return get_frame_register_signed |
| (frame, gdbarch_num_regs (gdbarch) + MIPS_V0_REGNUM); |
| else |
| /* This is the 'return' part of a call stub. The return |
| address is in $18. */ |
| return get_frame_register_signed |
| (frame, gdbarch_num_regs (gdbarch) + MIPS_S2_REGNUM); |
| } |
| else |
| return 0; /* Not a stub. */ |
| } |
| |
| /* If the PC is in __call_stub_* or __fn_stub*, this is one of the |
| compiler-generated call or call/return stubs. */ |
| if (startswith (name, mips_str_fn_stub) |
| || startswith (name, mips_str_call_stub)) |
| { |
| if (pc == start_addr) |
| /* This is the 'call' part of a call stub. Call this helper |
| to scan through this code for interesting instructions |
| and determine the final PC. */ |
| return mips_get_mips16_fn_stub_pc (frame, pc); |
| else |
| /* This is the 'return' part of a call stub. The return address |
| is in $18. */ |
| return get_frame_register_signed |
| (frame, gdbarch_num_regs (gdbarch) + MIPS_S2_REGNUM); |
| } |
| |
| return 0; /* Not a stub. */ |
| } |
| |
| /* Return non-zero if the PC is inside a return thunk (aka stub or trampoline). |
| This implements the IN_SOLIB_RETURN_TRAMPOLINE macro. */ |
| |
| static int |
| mips_in_return_stub (struct gdbarch *gdbarch, CORE_ADDR pc, const char *name) |
| { |
| CORE_ADDR start_addr; |
| size_t prefixlen; |
| |
| /* Find the starting address of the function containing the PC. */ |
| if (find_pc_partial_function (pc, NULL, &start_addr, NULL) == 0) |
| return 0; |
| |
| /* If the PC is in __mips16_call_stub_{s,d}{f,c}_{0..10} but not at |
| the start, i.e. after the JALR instruction, this is effectively |
| a return stub. */ |
| prefixlen = strlen (mips_str_mips16_call_stub); |
| if (pc != start_addr |
| && strncmp (name, mips_str_mips16_call_stub, prefixlen) == 0 |
| && mips_is_stub_mode (name + prefixlen) |
| && name[prefixlen + 2] == '_' |
| && mips_is_stub_suffix (name + prefixlen + 3, 1)) |
| return 1; |
| |
| /* If the PC is in __call_stub_fp_* but not at the start, i.e. after |
| the JAL or JALR instruction, this is effectively a return stub. */ |
| prefixlen = strlen (mips_str_call_fp_stub); |
| if (pc != start_addr |
| && strncmp (name, mips_str_call_fp_stub, prefixlen) == 0) |
| return 1; |
| |
| /* Consume the .pic. prefix of any PIC stub, this function must return |
| true when the PC is in a PIC stub of a __mips16_ret_{d,s}{f,c} stub |
| or the call stub path will trigger in handle_inferior_event causing |
| it to go astray. */ |
| prefixlen = strlen (mips_str_pic); |
| if (strncmp (name, mips_str_pic, prefixlen) == 0) |
| name += prefixlen; |
| |
| /* If the PC is in __mips16_ret_{d,s}{f,c}, this is a return stub. */ |
| prefixlen = strlen (mips_str_mips16_ret_stub); |
| if (strncmp (name, mips_str_mips16_ret_stub, prefixlen) == 0 |
| && mips_is_stub_mode (name + prefixlen) |
| && name[prefixlen + 2] == '\0') |
| return 1; |
| |
| return 0; /* Not a stub. */ |
| } |
| |
| /* If the current PC is the start of a non-PIC-to-PIC stub, return the |
| PC of the stub target. The stub just loads $t9 and jumps to it, |
| so that $t9 has the correct value at function entry. */ |
| |
| static CORE_ADDR |
| mips_skip_pic_trampoline_code (struct frame_info *frame, CORE_ADDR pc) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| struct bound_minimal_symbol msym; |
| int i; |
| gdb_byte stub_code[16]; |
| int32_t stub_words[4]; |
| |
| /* The stub for foo is named ".pic.foo", and is either two |
| instructions inserted before foo or a three instruction sequence |
| which jumps to foo. */ |
| msym = lookup_minimal_symbol_by_pc (pc); |
| if (msym.minsym == NULL |
| || BMSYMBOL_VALUE_ADDRESS (msym) != pc |
| || MSYMBOL_LINKAGE_NAME (msym.minsym) == NULL |
| || !startswith (MSYMBOL_LINKAGE_NAME (msym.minsym), ".pic.")) |
| return 0; |
| |
| /* A two-instruction header. */ |
| if (MSYMBOL_SIZE (msym.minsym) == 8) |
| return pc + 8; |
| |
| /* A three-instruction (plus delay slot) trampoline. */ |
| if (MSYMBOL_SIZE (msym.minsym) == 16) |
| { |
| if (target_read_memory (pc, stub_code, 16) != 0) |
| return 0; |
| for (i = 0; i < 4; i++) |
| stub_words[i] = extract_unsigned_integer (stub_code + i * 4, |
| 4, byte_order); |
| |
| /* A stub contains these instructions: |
| lui t9, %hi(target) |
| j target |
| addiu t9, t9, %lo(target) |
| nop |
| |
| This works even for N64, since stubs are only generated with |
| -msym32. */ |
| if ((stub_words[0] & 0xffff0000U) == 0x3c190000 |
| && (stub_words[1] & 0xfc000000U) == 0x08000000 |
| && (stub_words[2] & 0xffff0000U) == 0x27390000 |
| && stub_words[3] == 0x00000000) |
| return ((((stub_words[0] & 0x0000ffff) << 16) |
| + (stub_words[2] & 0x0000ffff)) ^ 0x8000) - 0x8000; |
| } |
| |
| /* Not a recognized stub. */ |
| return 0; |
| } |
| |
| static CORE_ADDR |
| mips_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc) |
| { |
| CORE_ADDR requested_pc = pc; |
| CORE_ADDR target_pc; |
| CORE_ADDR new_pc; |
| |
| do |
| { |
| target_pc = pc; |
| |
| new_pc = mips_skip_mips16_trampoline_code (frame, pc); |
| if (new_pc) |
| pc = new_pc; |
| |
| new_pc = find_solib_trampoline_target (frame, pc); |
| if (new_pc) |
| pc = new_pc; |
| |
| new_pc = mips_skip_pic_trampoline_code (frame, pc); |
| if (new_pc) |
| pc = new_pc; |
| } |
| while (pc != target_pc); |
| |
| return pc != requested_pc ? pc : 0; |
| } |
| |
| /* Convert a dbx stab register number (from `r' declaration) to a GDB |
| [1 * gdbarch_num_regs .. 2 * gdbarch_num_regs) REGNUM. */ |
| |
| static int |
| mips_stab_reg_to_regnum (struct gdbarch *gdbarch, int num) |
| { |
| int regnum; |
| if (num >= 0 && num < 32) |
| regnum = num; |
| else if (num >= 38 && num < 70) |
| regnum = num + mips_regnum (gdbarch)->fp0 - 38; |
| else if (num == 70) |
| regnum = mips_regnum (gdbarch)->hi; |
| else if (num == 71) |
| regnum = mips_regnum (gdbarch)->lo; |
| else if (mips_regnum (gdbarch)->dspacc != -1 && num >= 72 && num < 78) |
| regnum = num + mips_regnum (gdbarch)->dspacc - 72; |
| else |
| return -1; |
| return gdbarch_num_regs (gdbarch) + regnum; |
| } |
| |
| |
| /* Convert a dwarf, dwarf2, or ecoff register number to a GDB [1 * |
| gdbarch_num_regs .. 2 * gdbarch_num_regs) REGNUM. */ |
| |
| static int |
| mips_dwarf_dwarf2_ecoff_reg_to_regnum (struct gdbarch *gdbarch, int num) |
| { |
| int regnum; |
| if (num >= 0 && num < 32) |
| regnum = num; |
| else if (num >= 32 && num < 64) |
| regnum = num + mips_regnum (gdbarch)->fp0 - 32; |
| else if (num == 64) |
| regnum = mips_regnum (gdbarch)->hi; |
| else if (num == 65) |
| regnum = mips_regnum (gdbarch)->lo; |
| else if (mips_regnum (gdbarch)->dspacc != -1 && num >= 66 && num < 72) |
| regnum = num + mips_regnum (gdbarch)->dspacc - 66; |
| else |
| return -1; |
| return gdbarch_num_regs (gdbarch) + regnum; |
| } |
| |
| static int |
| mips_register_sim_regno (struct gdbarch *gdbarch, int regnum) |
| { |
| /* Only makes sense to supply raw registers. */ |
| gdb_assert (regnum >= 0 && regnum < gdbarch_num_regs (gdbarch)); |
| /* FIXME: cagney/2002-05-13: Need to look at the pseudo register to |
| decide if it is valid. Should instead define a standard sim/gdb |
| register numbering scheme. */ |
| if (gdbarch_register_name (gdbarch, |
| gdbarch_num_regs (gdbarch) + regnum) != NULL |
| && gdbarch_register_name (gdbarch, |
| gdbarch_num_regs (gdbarch) |
| + regnum)[0] != '\0') |
| return regnum; |
| else |
| return LEGACY_SIM_REGNO_IGNORE; |
| } |
| |
| |
| /* Convert an integer into an address. Extracting the value signed |
| guarantees a correctly sign extended address. */ |
| |
| static CORE_ADDR |
| mips_integer_to_address (struct gdbarch *gdbarch, |
| struct type *type, const gdb_byte *buf) |
| { |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| return extract_signed_integer (buf, TYPE_LENGTH (type), byte_order); |
| } |
| |
| /* Dummy virtual frame pointer method. This is no more or less accurate |
| than most other architectures; we just need to be explicit about it, |
| because the pseudo-register gdbarch_sp_regnum will otherwise lead to |
| an assertion failure. */ |
| |
| static void |
| mips_virtual_frame_pointer (struct gdbarch *gdbarch, |
| CORE_ADDR pc, int *reg, LONGEST *offset) |
| { |
| *reg = MIPS_SP_REGNUM; |
| *offset = 0; |
| } |
| |
| static void |
| mips_find_abi_section (bfd *abfd, asection *sect, void *obj) |
| { |
| enum mips_abi *abip = (enum mips_abi *) obj; |
| const char *name = bfd_get_section_name (abfd, sect); |
| |
| if (*abip != MIPS_ABI_UNKNOWN) |
| return; |
| |
| if (!startswith (name, ".mdebug.")) |
| return; |
| |
| if (strcmp (name, ".mdebug.abi32") == 0) |
| *abip = MIPS_ABI_O32; |
| else if (strcmp (name, ".mdebug.abiN32") == 0) |
| *abip = MIPS_ABI_N32; |
| else if (strcmp (name, ".mdebug.abi64") == 0) |
| *abip = MIPS_ABI_N64; |
| else if (strcmp (name, ".mdebug.abiO64") == 0) |
| *abip = MIPS_ABI_O64; |
| else if (strcmp (name, ".mdebug.eabi32") == 0) |
| *abip = MIPS_ABI_EABI32; |
| else if (strcmp (name, ".mdebug.eabi64") == 0) |
| *abip = MIPS_ABI_EABI64; |
| else |
| warning (_("unsupported ABI %s."), name + 8); |
| } |
| |
| static void |
| mips_find_long_section (bfd *abfd, asection *sect, void *obj) |
| { |
| int *lbp = (int *) obj; |
| const char *name = bfd_get_section_name (abfd, sect); |
| |
| if (startswith (name, ".gcc_compiled_long32")) |
| *lbp = 32; |
| else if (startswith (name, ".gcc_compiled_long64")) |
| *lbp = 64; |
| else if (startswith (name, ".gcc_compiled_long")) |
| warning (_("unrecognized .gcc_compiled_longXX")); |
| } |
| |
| static enum mips_abi |
| global_mips_abi (void) |
| { |
| int i; |
| |
| for (i = 0; mips_abi_strings[i] != NULL; i++) |
| if (mips_abi_strings[i] == mips_abi_string) |
| return (enum mips_abi) i; |
| |
| internal_error (__FILE__, __LINE__, _("unknown ABI string")); |
| } |
| |
| /* Return the default compressed instruction set, either of MIPS16 |
| or microMIPS, selected when none could have been determined from |
| the ELF header of the binary being executed (or no binary has been |
| selected. */ |
| |
| static enum mips_isa |
| global_mips_compression (void) |
| { |
| int i; |
| |
| for (i = 0; mips_compression_strings[i] != NULL; i++) |
| if (mips_compression_strings[i] == mips_compression_string) |
| return (enum mips_isa) i; |
| |
| internal_error (__FILE__, __LINE__, _("unknown compressed ISA string")); |
| } |
| |
| static void |
| mips_register_g_packet_guesses (struct gdbarch *gdbarch) |
| { |
| /* If the size matches the set of 32-bit or 64-bit integer registers, |
| assume that's what we've got. */ |
| register_remote_g_packet_guess (gdbarch, 38 * 4, mips_tdesc_gp32); |
| register_remote_g_packet_guess (gdbarch, 38 * 8, mips_tdesc_gp64); |
| |
| /* If the size matches the full set of registers GDB traditionally |
| knows about, including floating point, for either 32-bit or |
| 64-bit, assume that's what we've got. */ |
| register_remote_g_packet_guess (gdbarch, 90 * 4, mips_tdesc_gp32); |
| register_remote_g_packet_guess (gdbarch, 90 * 8, mips_tdesc_gp64); |
| |
| /* Otherwise we don't have a useful guess. */ |
| } |
| |
| static struct value * |
| value_of_mips_user_reg (struct frame_info *frame, const void *baton) |
| { |
| const int *reg_p = (const int *) baton; |
| return value_of_register (*reg_p, frame); |
| } |
| |
| static struct gdbarch * |
| mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) |
| { |
| struct gdbarch *gdbarch; |
| struct gdbarch_tdep *tdep; |
| int elf_flags; |
| enum mips_abi mips_abi, found_abi, wanted_abi; |
| int i, num_regs; |
| enum mips_fpu_type fpu_type; |
| struct tdesc_arch_data *tdesc_data = NULL; |
| int elf_fpu_type = Val_GNU_MIPS_ABI_FP_ANY; |
| const char **reg_names; |
| struct mips_regnum mips_regnum, *regnum; |
| enum mips_isa mips_isa; |
| int dspacc; |
| int dspctl; |
| |
| /* Fill in the OS dependent register numbers and names. */ |
| if (info.osabi == GDB_OSABI_IRIX) |
| { |
| mips_regnum.fp0 = 32; |
| mips_regnum.pc = 64; |
| mips_regnum.cause = 65; |
| mips_regnum.badvaddr = 66; |
| mips_regnum.hi = 67; |
| mips_regnum.lo = 68; |
| mips_regnum.fp_control_status = 69; |
| mips_regnum.fp_implementation_revision = 70; |
| mips_regnum.dspacc = dspacc = -1; |
| mips_regnum.dspctl = dspctl = -1; |
| num_regs = 71; |
| reg_names = mips_irix_reg_names; |
| } |
| else if (info.osabi == GDB_OSABI_LINUX) |
| { |
| mips_regnum.fp0 = 38; |
| mips_regnum.pc = 37; |
| mips_regnum.cause = 36; |
| mips_regnum.badvaddr = 35; |
| mips_regnum.hi = 34; |
| mips_regnum.lo = 33; |
| mips_regnum.fp_control_status = 70; |
| mips_regnum.fp_implementation_revision = 71; |
| mips_regnum.dspacc = -1; |
| mips_regnum.dspctl = -1; |
| dspacc = 72; |
| dspctl = 78; |
| num_regs = 90; |
| reg_names = mips_linux_reg_names; |
| } |
| else |
| { |
| mips_regnum.lo = MIPS_EMBED_LO_REGNUM; |
| mips_regnum.hi = MIPS_EMBED_HI_REGNUM; |
| mips_regnum.badvaddr = MIPS_EMBED_BADVADDR_REGNUM; |
| mips_regnum.cause = MIPS_EMBED_CAUSE_REGNUM; |
| mips_regnum.pc = MIPS_EMBED_PC_REGNUM; |
| mips_regnum.fp0 = MIPS_EMBED_FP0_REGNUM; |
| mips_regnum.fp_control_status = 70; |
| mips_regnum.fp_implementation_revision = 71; |
| mips_regnum.dspacc = dspacc = -1; |
| mips_regnum.dspctl = dspctl = -1; |
| num_regs = MIPS_LAST_EMBED_REGNUM + 1; |
| if (info.bfd_arch_info != NULL |
| && info.bfd_arch_info->mach == bfd_mach_mips3900) |
| reg_names = mips_tx39_reg_names; |
| else |
| reg_names = mips_generic_reg_names; |
| } |
| |
| /* Check any target description for validity. */ |
| if (tdesc_has_registers (info.target_desc)) |
| { |
| static const char *const mips_gprs[] = { |
| "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", "r29", "r30", "r31" |
| }; |
| static const char *const mips_fprs[] = { |
| "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", |
| "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", |
| "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", |
| "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", |
| }; |
| |
| const struct tdesc_feature *feature; |
| int valid_p; |
| |
| feature = tdesc_find_feature (info.target_desc, |
| "org.gnu.gdb.mips.cpu"); |
| if (feature == NULL) |
| return NULL; |
| |
| tdesc_data = tdesc_data_alloc (); |
| |
| valid_p = 1; |
| for (i = MIPS_ZERO_REGNUM; i <= MIPS_RA_REGNUM; i++) |
| valid_p &= tdesc_numbered_register (feature, tdesc_data, i, |
| mips_gprs[i]); |
| |
| |
| valid_p &= tdesc_numbered_register (feature, tdesc_data, |
| mips_regnum.lo, "lo"); |
| valid_p &= tdesc_numbered_register (feature, tdesc_data, |
| mips_regnum.hi, "hi"); |
| valid_p &= tdesc_numbered_register (feature, tdesc_data, |
| mips_regnum.pc, "pc"); |
| |
| if (!valid_p) |
| { |
| tdesc_data_cleanup (tdesc_data); |
| return NULL; |
| } |
| |
| feature = tdesc_find_feature (info.target_desc, |
| "org.gnu.gdb.mips.cp0"); |
| if (feature == NULL) |
| { |
| tdesc_data_cleanup (tdesc_data); |
| return NULL; |
| } |
| |
| valid_p = 1; |
| valid_p &= tdesc_numbered_register (feature, tdesc_data, |
| mips_regnum.badvaddr, "badvaddr"); |
| valid_p &= tdesc_numbered_register (feature, tdesc_data, |
| MIPS_PS_REGNUM, "status"); |
| valid_p &= tdesc_numbered_register (feature, tdesc_data, |
| mips_regnum.cause, "cause"); |
| |
| if (!valid_p) |
| { |
| tdesc_data_cleanup (tdesc_data); |
| return NULL; |
| } |
| |
| /* FIXME drow/2007-05-17: The FPU should be optional. The MIPS |
| backend is not prepared for that, though. */ |
| feature = tdesc_find_feature (info.target_desc, |
| "org.gnu.gdb.mips.fpu"); |
| if (feature == NULL) |
| { |
| tdesc_data_cleanup (tdesc_data); |
| return NULL; |
| } |
| |
| valid_p = 1; |
| for (i = 0; i < 32; i++) |
| valid_p &= tdesc_numbered_register (feature, tdesc_data, |
| i + mips_regnum.fp0, mips_fprs[i]); |
| |
| valid_p &= tdesc_numbered_register (feature, tdesc_data, |
| mips_regnum.fp_control_status, |
| "fcsr"); |
| valid_p |
| &= tdesc_numbered_register (feature, tdesc_data, |
| mips_regnum.fp_implementation_revision, |
| "fir"); |
| |
| if (!valid_p) |
| { |
| tdesc_data_cleanup (tdesc_data); |
| return NULL; |
| } |
| |
| num_regs = mips_regnum.fp_implementation_revision + 1; |
| |
| if (dspacc >= 0) |
| { |
| feature = tdesc_find_feature (info.target_desc, |
| "org.gnu.gdb.mips.dsp"); |
| /* The DSP registers are optional; it's OK if they are absent. */ |
| if (feature != NULL) |
| { |
| i = 0; |
| valid_p = 1; |
| valid_p &= tdesc_numbered_register (feature, tdesc_data, |
| dspacc + i++, "hi1"); |
| valid_p &= tdesc_numbered_register (feature, tdesc_data, |
| dspacc + i++, "lo1"); |
| valid_p &= tdesc_numbered_register (feature, tdesc_data, |
| dspacc + i++, "hi2"); |
| valid_p &= tdesc_numbered_register (feature, tdesc_data, |
| dspacc + i++, "lo2"); |
| valid_p &= tdesc_numbered_register (feature, tdesc_data, |
| dspacc + i++, "hi3"); |
| valid_p &= tdesc_numbered_register (feature, tdesc_data, |
| dspacc + i++, "lo3"); |
| |
| valid_p &= tdesc_numbered_register (feature, tdesc_data, |
| dspctl, "dspctl"); |
| |
| if (!valid_p) |
| { |
| tdesc_data_cleanup (tdesc_data); |
| return NULL; |
| } |
| |
| mips_regnum.dspacc = dspacc; |
| mips_regnum.dspctl = dspctl; |
| |
| num_regs = mips_regnum.dspctl + 1; |
| } |
| } |
| |
| /* It would be nice to detect an attempt to use a 64-bit ABI |
| when only 32-bit registers are provided. */ |
| reg_names = NULL; |
| } |
| |
| /* First of all, extract the elf_flags, if available. */ |
| if (info.abfd && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour) |
| elf_flags = elf_elfheader (info.abfd)->e_flags; |
| else if (arches != NULL) |
| elf_flags = gdbarch_tdep (arches->gdbarch)->elf_flags; |
| else |
| elf_flags = 0; |
| if (gdbarch_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "mips_gdbarch_init: elf_flags = 0x%08x\n", elf_flags); |
| |
| /* Check ELF_FLAGS to see if it specifies the ABI being used. */ |
| switch ((elf_flags & EF_MIPS_ABI)) |
| { |
| case E_MIPS_ABI_O32: |
| found_abi = MIPS_ABI_O32; |
| break; |
| case E_MIPS_ABI_O64: |
| found_abi = MIPS_ABI_O64; |
| break; |
| case E_MIPS_ABI_EABI32: |
| found_abi = MIPS_ABI_EABI32; |
| break; |
| case E_MIPS_ABI_EABI64: |
| found_abi = MIPS_ABI_EABI64; |
| break; |
| default: |
| if ((elf_flags & EF_MIPS_ABI2)) |
| found_abi = MIPS_ABI_N32; |
| else |
| found_abi = MIPS_ABI_UNKNOWN; |
| break; |
| } |
| |
| /* GCC creates a pseudo-section whose name describes the ABI. */ |
| if (found_abi == MIPS_ABI_UNKNOWN && info.abfd != NULL) |
| bfd_map_over_sections (info.abfd, mips_find_abi_section, &found_abi); |
| |
| /* If we have no useful BFD information, use the ABI from the last |
| MIPS architecture (if there is one). */ |
| if (found_abi == MIPS_ABI_UNKNOWN && info.abfd == NULL && arches != NULL) |
| found_abi = gdbarch_tdep (arches->gdbarch)->found_abi; |
| |
| /* Try the architecture for any hint of the correct ABI. */ |
| if (found_abi == MIPS_ABI_UNKNOWN |
| && info.bfd_arch_info != NULL |
| && info.bfd_arch_info->arch == bfd_arch_mips) |
| { |
| switch (info.bfd_arch_info->mach) |
| { |
| case bfd_mach_mips3900: |
| found_abi = MIPS_ABI_EABI32; |
| break; |
| case bfd_mach_mips4100: |
| case bfd_mach_mips5000: |
| found_abi = MIPS_ABI_EABI64; |
| break; |
| case bfd_mach_mips8000: |
| case bfd_mach_mips10000: |
| /* On Irix, ELF64 executables use the N64 ABI. The |
| pseudo-sections which describe the ABI aren't present |
| on IRIX. (Even for executables created by gcc.) */ |
| if (info.abfd != NULL |
| && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour |
| && elf_elfheader (info.abfd)->e_ident[EI_CLASS] == ELFCLASS64) |
| found_abi = MIPS_ABI_N64; |
| else |
| found_abi = MIPS_ABI_N32; |
| break; |
| } |
| } |
| |
| /* Default 64-bit objects to N64 instead of O32. */ |
| if (found_abi == MIPS_ABI_UNKNOWN |
| && info.abfd != NULL |
| && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour |
| && elf_elfheader (info.abfd)->e_ident[EI_CLASS] == ELFCLASS64) |
| found_abi = MIPS_ABI_N64; |
| |
| if (gdbarch_debug) |
| fprintf_unfiltered (gdb_stdlog, "mips_gdbarch_init: found_abi = %d\n", |
| found_abi); |
| |
| /* What has the user specified from the command line? */ |
| wanted_abi = global_mips_abi (); |
| if (gdbarch_debug) |
| fprintf_unfiltered (gdb_stdlog, "mips_gdbarch_init: wanted_abi = %d\n", |
| wanted_abi); |
| |
| /* Now that we have found what the ABI for this binary would be, |
| check whether the user is overriding it. */ |
| if (wanted_abi != MIPS_ABI_UNKNOWN) |
| mips_abi = wanted_abi; |
| else if (found_abi != MIPS_ABI_UNKNOWN) |
| mips_abi = found_abi; |
| else |
| mips_abi = MIPS_ABI_O32; |
| if (gdbarch_debug) |
| fprintf_unfiltered (gdb_stdlog, "mips_gdbarch_init: mips_abi = %d\n", |
| mips_abi); |
| |
| /* Determine the default compressed ISA. */ |
| if ((elf_flags & EF_MIPS_ARCH_ASE_MICROMIPS) != 0 |
| && (elf_flags & EF_MIPS_ARCH_ASE_M16) == 0) |
| mips_isa = ISA_MICROMIPS; |
| else if ((elf_flags & EF_MIPS_ARCH_ASE_M16) != 0 |
| && (elf_flags & EF_MIPS_ARCH_ASE_MICROMIPS) == 0) |
| mips_isa = ISA_MIPS16; |
| else |
| mips_isa = global_mips_compression (); |
| mips_compression_string = mips_compression_strings[mips_isa]; |
| |
| /* Also used when doing an architecture lookup. */ |
| if (gdbarch_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "mips_gdbarch_init: " |
| "mips64_transfers_32bit_regs_p = %d\n", |
| mips64_transfers_32bit_regs_p); |
| |
| /* Determine the MIPS FPU type. */ |
| #ifdef HAVE_ELF |
| if (info.abfd |
| && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour) |
| elf_fpu_type = bfd_elf_get_obj_attr_int (info.abfd, OBJ_ATTR_GNU, |
| Tag_GNU_MIPS_ABI_FP); |
| #endif /* HAVE_ELF */ |
| |
| if (!mips_fpu_type_auto) |
| fpu_type = mips_fpu_type; |
| else if (elf_fpu_type != Val_GNU_MIPS_ABI_FP_ANY) |
| { |
| switch (elf_fpu_type) |
| { |
| case Val_GNU_MIPS_ABI_FP_DOUBLE: |
| fpu_type = MIPS_FPU_DOUBLE; |
| break; |
| case Val_GNU_MIPS_ABI_FP_SINGLE: |
| fpu_type = MIPS_FPU_SINGLE; |
| break; |
| case Val_GNU_MIPS_ABI_FP_SOFT: |
| default: |
| /* Soft float or unknown. */ |
| fpu_type = MIPS_FPU_NONE; |
| break; |
| } |
| } |
| else if (info.bfd_arch_info != NULL |
| && info.bfd_arch_info->arch == bfd_arch_mips) |
| switch (info.bfd_arch_info->mach) |
| { |
| case bfd_mach_mips3900: |
| case bfd_mach_mips4100: |
| case bfd_mach_mips4111: |
| case bfd_mach_mips4120: |
| fpu_type = MIPS_FPU_NONE; |
| break; |
| case bfd_mach_mips4650: |
| fpu_type = MIPS_FPU_SINGLE; |
| break; |
| default: |
| fpu_type = MIPS_FPU_DOUBLE; |
| break; |
| } |
| else if (arches != NULL) |
| fpu_type = gdbarch_tdep (arches->gdbarch)->mips_fpu_type; |
| else |
| fpu_type = MIPS_FPU_DOUBLE; |
| if (gdbarch_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "mips_gdbarch_init: fpu_type = %d\n", fpu_type); |
| |
| /* Check for blatant incompatibilities. */ |
| |
| /* If we have only 32-bit registers, then we can't debug a 64-bit |
| ABI. */ |
| if (info.target_desc |
| && tdesc_property (info.target_desc, PROPERTY_GP32) != NULL |
| && mips_abi != MIPS_ABI_EABI32 |
| && mips_abi != MIPS_ABI_O32) |
| { |
| if (tdesc_data != NULL) |
| tdesc_data_cleanup (tdesc_data); |
| return NULL; |
| } |
| |
| /* Try to find a pre-existing architecture. */ |
| for (arches = gdbarch_list_lookup_by_info (arches, &info); |
| arches != NULL; |
| arches = gdbarch_list_lookup_by_info (arches->next, &info)) |
| { |
| /* MIPS needs to be pedantic about which ABI and the compressed |
| ISA variation the object is using. */ |
| if (gdbarch_tdep (arches->gdbarch)->elf_flags != elf_flags) |
| continue; |
| if (gdbarch_tdep (arches->gdbarch)->mips_abi != mips_abi) |
| continue; |
| if (gdbarch_tdep (arches->gdbarch)->mips_isa != mips_isa) |
| continue; |
| /* Need to be pedantic about which register virtual size is |
| used. */ |
| if (gdbarch_tdep (arches->gdbarch)->mips64_transfers_32bit_regs_p |
| != mips64_transfers_32bit_regs_p) |
| continue; |
| /* Be pedantic about which FPU is selected. */ |
| if (gdbarch_tdep (arches->gdbarch)->mips_fpu_type != fpu_type) |
| continue; |
| |
| if (tdesc_data != NULL) |
| tdesc_data_cleanup (tdesc_data); |
| return arches->gdbarch; |
| } |
| |
| /* Need a new architecture. Fill in a target specific vector. */ |
| tdep = XNEW (struct gdbarch_tdep); |
| gdbarch = gdbarch_alloc (&info, tdep); |
| tdep->elf_flags = elf_flags; |
| tdep->mips64_transfers_32bit_regs_p = mips64_transfers_32bit_regs_p; |
| tdep->found_abi = found_abi; |
| tdep->mips_abi = mips_abi; |
| tdep->mips_isa = mips_isa; |
| tdep->mips_fpu_type = fpu_type; |
| tdep->register_size_valid_p = 0; |
| tdep->register_size = 0; |
| |
| if (info.target_desc) |
| { |
| /* Some useful properties can be inferred from the target. */ |
| if (tdesc_property (info.target_desc, PROPERTY_GP32) != NULL) |
| { |
| tdep->register_size_valid_p = 1; |
| tdep->register_size = 4; |
| } |
| else if (tdesc_property (info.target_desc, PROPERTY_GP64) != NULL) |
| { |
| tdep->register_size_valid_p = 1; |
| tdep->register_size = 8; |
| } |
| } |
| |
| /* Initially set everything according to the default ABI/ISA. */ |
| set_gdbarch_short_bit (gdbarch, 16); |
| set_gdbarch_int_bit (gdbarch, 32); |
| set_gdbarch_float_bit (gdbarch, 32); |
| set_gdbarch_double_bit (gdbarch, 64); |
| set_gdbarch_long_double_bit (gdbarch, 64); |
| set_gdbarch_register_reggroup_p (gdbarch, mips_register_reggroup_p); |
| set_gdbarch_pseudo_register_read (gdbarch, mips_pseudo_register_read); |
| set_gdbarch_pseudo_register_write (gdbarch, mips_pseudo_register_write); |
| |
| set_gdbarch_ax_pseudo_register_collect (gdbarch, |
| mips_ax_pseudo_register_collect); |
| set_gdbarch_ax_pseudo_register_push_stack |
| (gdbarch, mips_ax_pseudo_register_push_stack); |
| |
| set_gdbarch_elf_make_msymbol_special (gdbarch, |
| mips_elf_make_msymbol_special); |
| set_gdbarch_make_symbol_special (gdbarch, mips_make_symbol_special); |
| set_gdbarch_adjust_dwarf2_addr (gdbarch, mips_adjust_dwarf2_addr); |
| set_gdbarch_adjust_dwarf2_line (gdbarch, mips_adjust_dwarf2_line); |
| |
| regnum = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct mips_regnum); |
| *regnum = mips_regnum; |
| set_gdbarch_fp0_regnum (gdbarch, regnum->fp0); |
| set_gdbarch_num_regs (gdbarch, num_regs); |
| set_gdbarch_num_pseudo_regs (gdbarch, num_regs); |
| set_gdbarch_register_name (gdbarch, mips_register_name); |
| set_gdbarch_virtual_frame_pointer (gdbarch, mips_virtual_frame_pointer); |
| tdep->mips_processor_reg_names = reg_names; |
| tdep->regnum = regnum; |
| |
| switch (mips_abi) |
| { |
| case MIPS_ABI_O32: |
| set_gdbarch_push_dummy_call (gdbarch, mips_o32_push_dummy_call); |
| set_gdbarch_return_value (gdbarch, mips_o32_return_value); |
| tdep->mips_last_arg_regnum = MIPS_A0_REGNUM + 4 - 1; |
| tdep->mips_last_fp_arg_regnum = tdep->regnum->fp0 + 12 + 4 - 1; |
| tdep->default_mask_address_p = 0; |
| set_gdbarch_long_bit (gdbarch, 32); |
| set_gdbarch_ptr_bit (gdbarch, 32); |
| set_gdbarch_long_long_bit (gdbarch, 64); |
| break; |
| case MIPS_ABI_O64: |
| set_gdbarch_push_dummy_call (gdbarch, mips_o64_push_dummy_call); |
| set_gdbarch_return_value (gdbarch, mips_o64_return_value); |
| tdep->mips_last_arg_regnum = MIPS_A0_REGNUM + 4 - 1; |
| tdep->mips_last_fp_arg_regnum = tdep->regnum->fp0 + 12 + 4 - 1; |
| tdep->default_mask_address_p = 0; |
| set_gdbarch_long_bit (gdbarch, 32); |
| set_gdbarch_ptr_bit (gdbarch, 32); |
| set_gdbarch_long_long_bit (gdbarch, 64); |
| break; |
| case MIPS_ABI_EABI32: |
| set_gdbarch_push_dummy_call (gdbarch, mips_eabi_push_dummy_call); |
| set_gdbarch_return_value (gdbarch, mips_eabi_return_value); |
| tdep->mips_last_arg_regnum = MIPS_A0_REGNUM + 8 - 1; |
| tdep->mips_last_fp_arg_regnum = tdep->regnum->fp0 + 12 + 8 - 1; |
| tdep->default_mask_address_p = 0; |
| set_gdbarch_long_bit (gdbarch, 32); |
| set_gdbarch_ptr_bit (gdbarch, 32); |
| set_gdbarch_long_long_bit (gdbarch, 64); |
| break; |
| case MIPS_ABI_EABI64: |
| set_gdbarch_push_dummy_call (gdbarch, mips_eabi_push_dummy_call); |
| set_gdbarch_return_value (gdbarch, mips_eabi_return_value); |
| tdep->mips_last_arg_regnum = MIPS_A0_REGNUM + 8 - 1; |
| tdep->mips_last_fp_arg_regnum = tdep->regnum->fp0 + 12 + 8 - 1; |
| tdep->default_mask_address_p = 0; |
| set_gdbarch_long_bit (gdbarch, 64); |
| set_gdbarch_ptr_bit (gdbarch, 64); |
| set_gdbarch_long_long_bit (gdbarch, 64); |
| break; |
| case MIPS_ABI_N32: |
| set_gdbarch_push_dummy_call (gdbarch, mips_n32n64_push_dummy_call); |
| set_gdbarch_return_value (gdbarch, mips_n32n64_return_value); |
| tdep->mips_last_arg_regnum = MIPS_A0_REGNUM + 8 - 1; |
| tdep->mips_last_fp_arg_regnum = tdep->regnum->fp0 + 12 + 8 - 1; |
| tdep->default_mask_address_p = 0; |
| set_gdbarch_long_bit (gdbarch, 32); |
| set_gdbarch_ptr_bit (gdbarch, 32); |
| set_gdbarch_long_long_bit (gdbarch, 64); |
| set_gdbarch_long_double_bit (gdbarch, 128); |
| set_gdbarch_long_double_format (gdbarch, floatformats_ibm_long_double); |
| break; |
| case MIPS_ABI_N64: |
| set_gdbarch_push_dummy_call (gdbarch, mips_n32n64_push_dummy_call); |
| set_gdbarch_return_value (gdbarch, mips_n32n64_return_value); |
| tdep->mips_last_arg_regnum = MIPS_A0_REGNUM + 8 - 1; |
| tdep->mips_last_fp_arg_regnum = tdep->regnum->fp0 + 12 + 8 - 1; |
| tdep->default_mask_address_p = 0; |
| set_gdbarch_long_bit (gdbarch, 64); |
| set_gdbarch_ptr_bit (gdbarch, 64); |
| set_gdbarch_long_long_bit (gdbarch, 64); |
| set_gdbarch_long_double_bit (gdbarch, 128); |
| set_gdbarch_long_double_format (gdbarch, floatformats_ibm_long_double); |
| break; |
| default: |
| internal_error (__FILE__, __LINE__, _("unknown ABI in switch")); |
| } |
| |
| /* GCC creates a pseudo-section whose name specifies the size of |
| longs, since -mlong32 or -mlong64 may be used independent of |
| other options. How those options affect pointer sizes is ABI and |
| architecture dependent, so use them to override the default sizes |
| set by the ABI. This table shows the relationship between ABI, |
| -mlongXX, and size of pointers: |
| |
| ABI -mlongXX ptr bits |
| --- -------- -------- |
| o32 32 32 |
| o32 64 32 |
| n32 32 32 |
| n32 64 64 |
| o64 32 32 |
| o64 64 64 |
| n64 32 32 |
| n64 64 64 |
| eabi32 32 32 |
| eabi32 64 32 |
| eabi64 32 32 |
| eabi64 64 64 |
| |
| Note that for o32 and eabi32, pointers are always 32 bits |
| regardless of any -mlongXX option. For all others, pointers and |
| longs are the same, as set by -mlongXX or set by defaults. */ |
| |
| if (info.abfd != NULL) |
| { |
| int long_bit = 0; |
| |
| bfd_map_over_sections (info.abfd, mips_find_long_section, &long_bit); |
| if (long_bit) |
| { |
| set_gdbarch_long_bit (gdbarch, long_bit); |
| switch (mips_abi) |
| { |
| case MIPS_ABI_O32: |
| case MIPS_ABI_EABI32: |
| break; |
| case MIPS_ABI_N32: |
| case MIPS_ABI_O64: |
| case MIPS_ABI_N64: |
| case MIPS_ABI_EABI64: |
| set_gdbarch_ptr_bit (gdbarch, long_bit); |
| break; |
| default: |
| internal_error (__FILE__, __LINE__, _("unknown ABI in switch")); |
| } |
| } |
| } |
| |
| /* FIXME: jlarmour/2000-04-07: There *is* a flag EF_MIPS_32BIT_MODE |
| that could indicate -gp32 BUT gas/config/tc-mips.c contains the |
| comment: |
| |
| ``We deliberately don't allow "-gp32" to set the MIPS_32BITMODE |
| flag in object files because to do so would make it impossible to |
| link with libraries compiled without "-gp32". This is |
| unnecessarily restrictive. |
| |
| We could solve this problem by adding "-gp32" multilibs to gcc, |
| but to set this flag before gcc is built with such multilibs will |
| break too many systems.'' |
| |
| But even more unhelpfully, the default linker output target for |
| mips64-elf is elf32-bigmips, and has EF_MIPS_32BIT_MODE set, even |
| for 64-bit programs - you need to change the ABI to change this, |
| and not all gcc targets support that currently. Therefore using |
| this flag to detect 32-bit mode would do the wrong thing given |
| the current gcc - it would make GDB treat these 64-bit programs |
| as 32-bit programs by default. */ |
| |
| set_gdbarch_read_pc (gdbarch, mips_read_pc); |
| set_gdbarch_write_pc (gdbarch, mips_write_pc); |
| |
| /* Add/remove bits from an address. The MIPS needs be careful to |
| ensure that all 32 bit addresses are sign extended to 64 bits. */ |
| set_gdbarch_addr_bits_remove (gdbarch, mips_addr_bits_remove); |
| |
| /* Unwind the frame. */ |
| set_gdbarch_unwind_pc (gdbarch, mips_unwind_pc); |
| set_gdbarch_unwind_sp (gdbarch, mips_unwind_sp); |
| set_gdbarch_dummy_id (gdbarch, mips_dummy_id); |
| |
| /* Map debug register numbers onto internal register numbers. */ |
| set_gdbarch_stab_reg_to_regnum (gdbarch, mips_stab_reg_to_regnum); |
| set_gdbarch_ecoff_reg_to_regnum (gdbarch, |
| mips_dwarf_dwarf2_ecoff_reg_to_regnum); |
| set_gdbarch_dwarf2_reg_to_regnum (gdbarch, |
| mips_dwarf_dwarf2_ecoff_reg_to_regnum); |
| set_gdbarch_register_sim_regno (gdbarch, mips_register_sim_regno); |
| |
| /* MIPS version of CALL_DUMMY. */ |
| |
| set_gdbarch_call_dummy_location (gdbarch, ON_STACK); |
| set_gdbarch_push_dummy_code (gdbarch, mips_push_dummy_code); |
| set_gdbarch_frame_align (gdbarch, mips_frame_align); |
| |
| set_gdbarch_print_float_info (gdbarch, mips_print_float_info); |
| |
| set_gdbarch_convert_register_p (gdbarch, mips_convert_register_p); |
| set_gdbarch_register_to_value (gdbarch, mips_register_to_value); |
| set_gdbarch_value_to_register (gdbarch, mips_value_to_register); |
| |
| set_gdbarch_inner_than (gdbarch, core_addr_lessthan); |
| set_gdbarch_breakpoint_from_pc (gdbarch, mips_breakpoint_from_pc); |
| set_gdbarch_remote_breakpoint_from_pc (gdbarch, |
| mips_remote_breakpoint_from_pc); |
| set_gdbarch_adjust_breakpoint_address (gdbarch, |
| mips_adjust_breakpoint_address); |
| |
| set_gdbarch_skip_prologue (gdbarch, mips_skip_prologue); |
| |
| set_gdbarch_stack_frame_destroyed_p (gdbarch, mips_stack_frame_destroyed_p); |
| |
| set_gdbarch_pointer_to_address (gdbarch, signed_pointer_to_address); |
| set_gdbarch_address_to_pointer (gdbarch, address_to_signed_pointer); |
| set_gdbarch_integer_to_address (gdbarch, mips_integer_to_address); |
| |
| set_gdbarch_register_type (gdbarch, mips_register_type); |
| |
| set_gdbarch_print_registers_info (gdbarch, mips_print_registers_info); |
| |
| if (mips_abi == MIPS_ABI_N32) |
| set_gdbarch_print_insn (gdbarch, gdb_print_insn_mips_n32); |
| else if (mips_abi == MIPS_ABI_N64) |
| set_gdbarch_print_insn (gdbarch, gdb_print_insn_mips_n64); |
| else |
| set_gdbarch_print_insn (gdbarch, gdb_print_insn_mips); |
| |
| /* FIXME: cagney/2003-08-29: The macros target_have_steppable_watchpoint, |
| HAVE_NONSTEPPABLE_WATCHPOINT, and target_have_continuable_watchpoint |
| need to all be folded into the target vector. Since they are |
| being used as guards for target_stopped_by_watchpoint, why not have |
| target_stopped_by_watchpoint return the type of watchpoint that the code |
| is sitting on? */ |
| set_gdbarch_have_nonsteppable_watchpoint (gdbarch, 1); |
| |
| set_gdbarch_skip_trampoline_code (gdbarch, mips_skip_trampoline_code); |
| |
| /* NOTE drow/2012-04-25: We overload the core solib trampoline code |
| to support MIPS16. This is a bad thing. Make sure not to do it |
| if we have an OS ABI that actually supports shared libraries, since |
| shared library support is more important. If we have an OS someday |
| that supports both shared libraries and MIPS16, we'll have to find |
| a better place for these. |
| macro/2012-04-25: But that applies to return trampolines only and |
| currently no MIPS OS ABI uses shared libraries that have them. */ |
| set_gdbarch_in_solib_return_trampoline (gdbarch, mips_in_return_stub); |
| |
| set_gdbarch_single_step_through_delay (gdbarch, |
| mips_single_step_through_delay); |
| |
| /* Virtual tables. */ |
| set_gdbarch_vbit_in_delta (gdbarch, 1); |
| |
| mips_register_g_packet_guesses (gdbarch); |
| |
| /* Hook in OS ABI-specific overrides, if they have been registered. */ |
| info.tdep_info = tdesc_data; |
| gdbarch_init_osabi (info, gdbarch); |
| |
| /* The hook may have adjusted num_regs, fetch the final value and |
| set pc_regnum and sp_regnum now that it has been fixed. */ |
| num_regs = gdbarch_num_regs (gdbarch); |
| set_gdbarch_pc_regnum (gdbarch, regnum->pc + num_regs); |
| set_gdbarch_sp_regnum (gdbarch, MIPS_SP_REGNUM + num_regs); |
| |
| /* Unwind the frame. */ |
| dwarf2_append_unwinders (gdbarch); |
| frame_unwind_append_unwinder (gdbarch, &mips_stub_frame_unwind); |
| frame_unwind_append_unwinder (gdbarch, &mips_insn16_frame_unwind); |
| frame_unwind_append_unwinder (gdbarch, &mips_micro_frame_unwind); |
| frame_unwind_append_unwinder (gdbarch, &mips_insn32_frame_unwind); |
| frame_base_append_sniffer (gdbarch, dwarf2_frame_base_sniffer); |
| frame_base_append_sniffer (gdbarch, mips_stub_frame_base_sniffer); |
| frame_base_append_sniffer (gdbarch, mips_insn16_frame_base_sniffer); |
| frame_base_append_sniffer (gdbarch, mips_micro_frame_base_sniffer); |
| frame_base_append_sniffer (gdbarch, mips_insn32_frame_base_sniffer); |
| |
| if (tdesc_data) |
| { |
| set_tdesc_pseudo_register_type (gdbarch, mips_pseudo_register_type); |
| tdesc_use_registers (gdbarch, info.target_desc, tdesc_data); |
| |
| /* Override the normal target description methods to handle our |
| dual real and pseudo registers. */ |
| set_gdbarch_register_name (gdbarch, mips_register_name); |
| set_gdbarch_register_reggroup_p (gdbarch, |
| mips_tdesc_register_reggroup_p); |
| |
| num_regs = gdbarch_num_regs (gdbarch); |
| set_gdbarch_num_pseudo_regs (gdbarch, num_regs); |
| set_gdbarch_pc_regnum (gdbarch, tdep->regnum->pc + num_regs); |
| set_gdbarch_sp_regnum (gdbarch, MIPS_SP_REGNUM + num_regs); |
| } |
| |
| /* Add ABI-specific aliases for the registers. */ |
| if (mips_abi == MIPS_ABI_N32 || mips_abi == MIPS_ABI_N64) |
| for (i = 0; i < ARRAY_SIZE (mips_n32_n64_aliases); i++) |
| user_reg_add (gdbarch, mips_n32_n64_aliases[i].name, |
| value_of_mips_user_reg, &mips_n32_n64_aliases[i].regnum); |
| else |
| for (i = 0; i < ARRAY_SIZE (mips_o32_aliases); i++) |
| user_reg_add (gdbarch, mips_o32_aliases[i].name, |
| value_of_mips_user_reg, &mips_o32_aliases[i].regnum); |
| |
| /* Add some other standard aliases. */ |
| for (i = 0; i < ARRAY_SIZE (mips_register_aliases); i++) |
| user_reg_add (gdbarch, mips_register_aliases[i].name, |
| value_of_mips_user_reg, &mips_register_aliases[i].regnum); |
| |
| for (i = 0; i < ARRAY_SIZE (mips_numeric_register_aliases); i++) |
| user_reg_add (gdbarch, mips_numeric_register_aliases[i].name, |
| value_of_mips_user_reg, |
| &mips_numeric_register_aliases[i].regnum); |
| |
| return gdbarch; |
| } |
| |
| static void |
| mips_abi_update (char *ignore_args, int from_tty, struct cmd_list_element *c) |
| { |
| struct gdbarch_info info; |
| |
| /* Force the architecture to update, and (if it's a MIPS architecture) |
| mips_gdbarch_init will take care of the rest. */ |
| gdbarch_info_init (&info); |
| gdbarch_update_p (info); |
| } |
| |
| /* Print out which MIPS ABI is in use. */ |
| |
| static void |
| show_mips_abi (struct ui_file *file, |
| int from_tty, |
| struct cmd_list_element *ignored_cmd, |
| const char *ignored_value) |
| { |
| if (gdbarch_bfd_arch_info (target_gdbarch ())->arch != bfd_arch_mips) |
| fprintf_filtered |
| (file, |
| "The MIPS ABI is unknown because the current architecture " |
| "is not MIPS.\n"); |
| else |
| { |
| enum mips_abi global_abi = global_mips_abi (); |
| enum mips_abi actual_abi = mips_abi (target_gdbarch ()); |
| const char *actual_abi_str = mips_abi_strings[actual_abi]; |
| |
| if (global_abi == MIPS_ABI_UNKNOWN) |
| fprintf_filtered |
| (file, |
| "The MIPS ABI is set automatically (currently \"%s\").\n", |
| actual_abi_str); |
| else if (global_abi == actual_abi) |
| fprintf_filtered |
| (file, |
| "The MIPS ABI is assumed to be \"%s\" (due to user setting).\n", |
| actual_abi_str); |
| else |
| { |
| /* Probably shouldn't happen... */ |
| fprintf_filtered (file, |
| "The (auto detected) MIPS ABI \"%s\" is in use " |
| "even though the user setting was \"%s\".\n", |
| actual_abi_str, mips_abi_strings[global_abi]); |
| } |
| } |
| } |
| |
| /* Print out which MIPS compressed ISA encoding is used. */ |
| |
| static void |
| show_mips_compression (struct ui_file *file, int from_tty, |
| struct cmd_list_element *c, const char *value) |
| { |
| fprintf_filtered (file, _("The compressed ISA encoding used is %s.\n"), |
| value); |
| } |
| |
| static void |
| mips_dump_tdep (struct gdbarch *gdbarch, struct ui_file *file) |
| { |
| struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
| if (tdep != NULL) |
| { |
| int ef_mips_arch; |
| int ef_mips_32bitmode; |
| /* Determine the ISA. */ |
| switch (tdep->elf_flags & EF_MIPS_ARCH) |
| { |
| case E_MIPS_ARCH_1: |
| ef_mips_arch = 1; |
| break; |
| case E_MIPS_ARCH_2: |
| ef_mips_arch = 2; |
| break; |
| case E_MIPS_ARCH_3: |
| ef_mips_arch = 3; |
| break; |
| case E_MIPS_ARCH_4: |
| ef_mips_arch = 4; |
| break; |
| default: |
| ef_mips_arch = 0; |
| break; |
| } |
| /* Determine the size of a pointer. */ |
| ef_mips_32bitmode = (tdep->elf_flags & EF_MIPS_32BITMODE); |
| fprintf_unfiltered (file, |
| "mips_dump_tdep: tdep->elf_flags = 0x%x\n", |
| tdep->elf_flags); |
| fprintf_unfiltered (file, |
| "mips_dump_tdep: ef_mips_32bitmode = %d\n", |
| ef_mips_32bitmode); |
| fprintf_unfiltered (file, |
| "mips_dump_tdep: ef_mips_arch = %d\n", |
| ef_mips_arch); |
| fprintf_unfiltered (file, |
| "mips_dump_tdep: tdep->mips_abi = %d (%s)\n", |
| tdep->mips_abi, mips_abi_strings[tdep->mips_abi]); |
| fprintf_unfiltered (file, |
| "mips_dump_tdep: " |
| "mips_mask_address_p() %d (default %d)\n", |
| mips_mask_address_p (tdep), |
| tdep->default_mask_address_p); |
| } |
| fprintf_unfiltered (file, |
| "mips_dump_tdep: MIPS_DEFAULT_FPU_TYPE = %d (%s)\n", |
| MIPS_DEFAULT_FPU_TYPE, |
| (MIPS_DEFAULT_FPU_TYPE == MIPS_FPU_NONE ? "none" |
| : MIPS_DEFAULT_FPU_TYPE == MIPS_FPU_SINGLE ? "single" |
| : MIPS_DEFAULT_FPU_TYPE == MIPS_FPU_DOUBLE ? "double" |
| : "???")); |
| fprintf_unfiltered (file, "mips_dump_tdep: MIPS_EABI = %d\n", |
| MIPS_EABI (gdbarch)); |
| fprintf_unfiltered (file, |
| "mips_dump_tdep: MIPS_FPU_TYPE = %d (%s)\n", |
| MIPS_FPU_TYPE (gdbarch), |
| (MIPS_FPU_TYPE (gdbarch) == MIPS_FPU_NONE ? "none" |
| : MIPS_FPU_TYPE (gdbarch) == MIPS_FPU_SINGLE ? "single" |
| : MIPS_FPU_TYPE (gdbarch) == MIPS_FPU_DOUBLE ? "double" |
| : "???")); |
| } |
| |
| extern initialize_file_ftype _initialize_mips_tdep; /* -Wmissing-prototypes */ |
| |
| void |
| _initialize_mips_tdep (void) |
| { |
| static struct cmd_list_element *mipsfpulist = NULL; |
| struct cmd_list_element *c; |
| |
| mips_abi_string = mips_abi_strings[MIPS_ABI_UNKNOWN]; |
| if (MIPS_ABI_LAST + 1 |
| != sizeof (mips_abi_strings) / sizeof (mips_abi_strings[0])) |
| internal_error (__FILE__, __LINE__, _("mips_abi_strings out of sync")); |
| |
| gdbarch_register (bfd_arch_mips, mips_gdbarch_init, mips_dump_tdep); |
| |
| mips_pdr_data = register_objfile_data (); |
| |
| /* Create feature sets with the appropriate properties. The values |
| are not important. */ |
| mips_tdesc_gp32 = allocate_target_description (); |
| set_tdesc_property (mips_tdesc_gp32, PROPERTY_GP32, ""); |
| |
| mips_tdesc_gp64 = allocate_target_description (); |
| set_tdesc_property (mips_tdesc_gp64, PROPERTY_GP64, ""); |
| |
| /* Add root prefix command for all "set mips"/"show mips" commands. */ |
| add_prefix_cmd ("mips", no_class, set_mips_command, |
| _("Various MIPS specific commands."), |
| &setmipscmdlist, "set mips ", 0, &setlist); |
| |
| add_prefix_cmd ("mips", no_class, show_mips_command, |
| _("Various MIPS specific commands."), |
| &showmipscmdlist, "show mips ", 0, &showlist); |
| |
| /* Allow the user to override the ABI. */ |
| add_setshow_enum_cmd ("abi", class_obscure, mips_abi_strings, |
| &mips_abi_string, _("\ |
| Set the MIPS ABI used by this program."), _("\ |
| Show the MIPS ABI used by this program."), _("\ |
| This option can be set to one of:\n\ |
| auto - the default ABI associated with the current binary\n\ |
| o32\n\ |
| o64\n\ |
| n32\n\ |
| n64\n\ |
| eabi32\n\ |
| eabi64"), |
| mips_abi_update, |
| show_mips_abi, |
| &setmipscmdlist, &showmipscmdlist); |
| |
| /* Allow the user to set the ISA to assume for compressed code if ELF |
| file flags don't tell or there is no program file selected. This |
| setting is updated whenever unambiguous ELF file flags are interpreted, |
| and carried over to subsequent sessions. */ |
| add_setshow_enum_cmd ("compression", class_obscure, mips_compression_strings, |
| &mips_compression_string, _("\ |
| Set the compressed ISA encoding used by MIPS code."), _("\ |
| Show the compressed ISA encoding used by MIPS code."), _("\ |
| Select the compressed ISA encoding used in functions that have no symbol\n\ |
| information available. The encoding can be set to either of:\n\ |
| mips16\n\ |
| micromips\n\ |
| and is updated automatically from ELF file flags if available."), |
| mips_abi_update, |
| show_mips_compression, |
| &setmipscmdlist, &showmipscmdlist); |
| |
| /* Let the user turn off floating point and set the fence post for |
| heuristic_proc_start. */ |
| |
| add_prefix_cmd ("mipsfpu", class_support, set_mipsfpu_command, |
| _("Set use of MIPS floating-point coprocessor."), |
| &mipsfpulist, "set mipsfpu ", 0, &setlist); |
| add_cmd ("single", class_support, set_mipsfpu_single_command, |
| _("Select single-precision MIPS floating-point coprocessor."), |
| &mipsfpulist); |
| add_cmd ("double", class_support, set_mipsfpu_double_command, |
| _("Select double-precision MIPS floating-point coprocessor."), |
| &mipsfpulist); |
| add_alias_cmd ("on", "double", class_support, 1, &mipsfpulist); |
| add_alias_cmd ("yes", "double", class_support, 1, &mipsfpulist); |
| add_alias_cmd ("1", "double", class_support, 1, &mipsfpulist); |
| add_cmd ("none", class_support, set_mipsfpu_none_command, |
| _("Select no MIPS floating-point coprocessor."), &mipsfpulist); |
| add_alias_cmd ("off", "none", class_support, 1, &mipsfpulist); |
| add_alias_cmd ("no", "none", class_support, 1, &mipsfpulist); |
| add_alias_cmd ("0", "none", class_support, 1, &mipsfpulist); |
| add_cmd ("auto", class_support, set_mipsfpu_auto_command, |
| _("Select MIPS floating-point coprocessor automatically."), |
| &mipsfpulist); |
| add_cmd ("mipsfpu", class_support, show_mipsfpu_command, |
| _("Show current use of MIPS floating-point coprocessor target."), |
| &showlist); |
| |
| /* We really would like to have both "0" and "unlimited" work, but |
| command.c doesn't deal with that. So make it a var_zinteger |
| because the user can always use "999999" or some such for unlimited. */ |
| add_setshow_zinteger_cmd ("heuristic-fence-post", class_support, |
| &heuristic_fence_post, _("\ |
| Set the distance searched for the start of a function."), _("\ |
| Show the distance searched for the start of a function."), _("\ |
| If you are debugging a stripped executable, GDB needs to search through the\n\ |
| program for the start of a function. This command sets the distance of the\n\ |
| search. The only need to set it is when debugging a stripped executable."), |
| reinit_frame_cache_sfunc, |
| NULL, /* FIXME: i18n: The distance searched for |
| the start of a function is %s. */ |
| &setlist, &showlist); |
| |
| /* Allow the user to control whether the upper bits of 64-bit |
| addresses should be zeroed. */ |
| add_setshow_auto_boolean_cmd ("mask-address", no_class, |
| &mask_address_var, _("\ |
| Set zeroing of upper 32 bits of 64-bit addresses."), _("\ |
| Show zeroing of upper 32 bits of 64-bit addresses."), _("\ |
| Use \"on\" to enable the masking, \"off\" to disable it and \"auto\" to\n\ |
| allow GDB to determine the correct value."), |
| NULL, show_mask_address, |
| &setmipscmdlist, &showmipscmdlist); |
| |
| /* Allow the user to control the size of 32 bit registers within the |
| raw remote packet. */ |
| add_setshow_boolean_cmd ("remote-mips64-transfers-32bit-regs", class_obscure, |
| &mips64_transfers_32bit_regs_p, _("\ |
| Set compatibility with 64-bit MIPS target that transfers 32-bit quantities."), |
| _("\ |
| Show compatibility with 64-bit MIPS target that transfers 32-bit quantities."), |
| _("\ |
| Use \"on\" to enable backward compatibility with older MIPS 64 GDB+target\n\ |
| that would transfer 32 bits for some registers (e.g. SR, FSR) and\n\ |
| 64 bits for others. Use \"off\" to disable compatibility mode"), |
| set_mips64_transfers_32bit_regs, |
| NULL, /* FIXME: i18n: Compatibility with 64-bit |
| MIPS target that transfers 32-bit |
| quantities is %s. */ |
| &setlist, &showlist); |
| |
| /* Debug this files internals. */ |
| add_setshow_zuinteger_cmd ("mips", class_maintenance, |
| &mips_debug, _("\ |
| Set mips debugging."), _("\ |
| Show mips debugging."), _("\ |
| When non-zero, mips specific debugging is enabled."), |
| NULL, |
| NULL, /* FIXME: i18n: Mips debugging is |
| currently %s. */ |
| &setdebuglist, &showdebuglist); |
| } |