From 4cc0665f24bbd86deb1848cd67f90994a1b527b2 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Fri, 18 May 2012 23:46:40 +0000 Subject: [PATCH] gdb/ * NEWS: Add microMIPS support and "set mips compression", "show mips compression" commands. * mips-tdep.h (mips_isa): New enum. (gdbarch_tdep): Add mips_isa. (mips_pc_is_mips16): Update prototype. (mips_pc_is_mips, mips_pc_is_micromips): New prototypes. * mips-tdep.c (mips_compression_mips16): New variable. (mips_compression_micromips): Likewise. (mips_compression_strings): Likewise. (mips_compression_string): Likewise. (is_mips16_isa, is_micromips_isa): New functions. (is_mips16_addr): Rename to... (is_compact_addr): ... this. (unmake_mips16_addr): Likewise to... (unmake_compact_addr): ... this. (make_mips16_addr): Likewise to... (make_compact_addr): ... this. (is_mips_addr, is_mips16_addr, is_micromips_addr): New functions. (mips_elf_make_msymbol_special): Handle microMIPS code. (msymbol_is_special): Rename to... (msymbol_is_mips16): ... this. (mips_make_symbol_special, mips_pc_is_mips16): Update accordingly. (msymbol_is_mips, msymbol_is_micromips): New functions. (mips16_to_32_reg): Rename to... (mips_reg3_to_reg): ... this. (mips_pc_is_mips, mips_pc_is_micromips): New functions. (mips_pc_isa): Likewise. (mips_read_pc, mips_unwind_pc, mips_write_pc): Handle microMIPS code. (mips_fetch_instruction): Pass return status instead of printing an error message if requested. Handle microMIPS code. Bail out on an invalid ISA. (micromips_op): New macro. (b0s4_imm, b0s5_imm, b0s5_reg, b0s7_imm, b0s10_imm): Likewise. (b1s9_imm, b2s3_cc, b4s2_regl, b5s5_op, b5s5_reg): Likewise. (b6s4_op, b7s3_reg): Likewise. (b0s6_op, b0s11_op, b0s12_imm, b0s16_imm, b0s26_imm): Likewise. (b6s10_ext, b11s5_reg, b12s4_op): Likewise. (mips_insn_size): New function. (mips32_next_pc): Update mips_fetch_instruction call. (micromips_relative_offset7): New function. (micromips_relative_offset10): Likewise. (micromips_relative_offset16): Likewise. (micromips_pc_insn_size): Likewise. (micromips_bc1_pc): Likewise. (micromips_next_pc): Likewise. (unpack_mips16): Update mips_fetch_instruction call. (extended_mips16_next_pc): Update according to change to mips16_to_32_reg. (mips_next_pc): Update mips_pc_is_mips16 call. Handle microMIPS code. (mips16_scan_prologue): Update mips_fetch_instruction call. Update according to change to mips16_to_32_reg. (mips_insn16_frame_sniffer): Update mips_pc_is_mips16 call. (mips_insn16_frame_base_sniffer): Likewise. (micromips_decode_imm9): New function. (micromips_scan_prologue): Likewise. (mips_micro_frame_cache): Likewise. (mips_micro_frame_this_id): Likewise. (mips_micro_frame_prev_register): Likewise. (mips_micro_frame_sniffer): Likewise. (mips_micro_frame_unwind): New variable. (mips_micro_frame_base_address): New function. (mips_micro_frame_base): New variable. (mips_micro_frame_base_sniffer): New function. (mips32_scan_prologue): Update mips_fetch_instruction call. (mips_insn32_frame_sniffer): Check for the standard MIPS ISA rather than for MIPS16. (mips_insn32_frame_base_sniffer): Likewise. (mips_addr_bits_remove): Handle microMIPS code. (deal_with_atomic_sequence): Rename to... (mips_deal_with_atomic_sequence): ... this. Update the type of the variable used to hold an instruction. Remove the ISA bit check. Update mips_fetch_instruction call. (micromips_deal_with_atomic_sequence): New function. (deal_with_atomic_sequence): Likewise. (mips_about_to_return): Handle microMIPS code. Update mips_fetch_instruction call. (heuristic_proc_start): Check for the standard MIPS ISA rather than for MIPS16. Update mips_pc_is_mips16 and mips_fetch_instruction calls. Handle microMIPS code. (mips_push_dummy_code): Handle microMIPS code. (mips_eabi_push_dummy_call): Likewise. (mips_o32_return_value): Update mips_pc_is_mips16 call. (mips_o64_push_dummy_call): Handle microMIPS code. (mips_o64_return_value): Update mips_pc_is_mips16 call. (is_delayed): Remove function. (mips_single_step_through_delay): Replace the call to is_delayed with mips32_instruction_has_delay_slot. Correct MIPS16 handling. Handle microMIPS code. (mips_skip_prologue): Update mips_pc_is_mips16 call. Handle microMIPS code. (mips32_in_function_epilogue_p): Update mips_fetch_instruction call. (micromips_in_function_epilogue_p): New function. (mips16_in_function_epilogue_p): Update mips_fetch_instruction call. (mips_in_function_epilogue_p): Update mips_pc_is_mips16 call. Handle microMIPS. (gdb_print_insn_mips): Likewise. (mips_breakpoint_from_pc): Likewise. (mips_remote_breakpoint_from_pc): New function. (mips32_instruction_has_delay_slot): Simplify making use of the updated mips_fetch_instruction interface. (micromips_instruction_has_delay_slot): New function. (mips16_instruction_has_delay_slot): Simplify making use of the updated mips_fetch_instruction interface. (mips_adjust_breakpoint_address): Check for the standard MIPS ISA rather than for MIPS16 ISA. Update for unmake_compact_addr calls. Handle microMIPS code. (mips_get_mips16_fn_stub_pc): Update mips_fetch_instruction call. (mips_skip_trampoline_code): Handle microMIPS code. (global_mips_compression): New function. (mips_gdbarch_init): Handle the compressed ISA setting from ELF file flags. Register the microMIPS remote breakpoint handler and heuristic frame unwinder. (show_mips_compression): New function. (_initialize_mips_tdep): Add the "set mips compression" and "show mips compression" commands. gdb/doc/ * gdb.texinfo (MIPS): Document "set mips compression" and "show mips compression". (MIPS Breakpoint Kinds): New subsubsection. --- gdb/ChangeLog | 124 ++++ gdb/NEWS | 10 + gdb/doc/ChangeLog | 7 + gdb/doc/gdb.texinfo | 55 ++ gdb/mips-tdep.c | 1866 +++++++++++++++++++++++++++++++++++++++++++++------ gdb/mips-tdep.h | 19 +- 6 files changed, 1887 insertions(+), 194 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 1da3f40..d35a2e3 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,127 @@ +2012-05-18 Maciej W. Rozycki + + * NEWS: Add microMIPS support and "set mips compression", + "show mips compression" commands. + * mips-tdep.h (mips_isa): New enum. + (gdbarch_tdep): Add mips_isa. + (mips_pc_is_mips16): Update prototype. + (mips_pc_is_mips, mips_pc_is_micromips): New prototypes. + * mips-tdep.c (mips_compression_mips16): New variable. + (mips_compression_micromips): Likewise. + (mips_compression_strings): Likewise. + (mips_compression_string): Likewise. + (is_mips16_isa, is_micromips_isa): New functions. + (is_mips16_addr): Rename to... + (is_compact_addr): ... this. + (unmake_mips16_addr): Likewise to... + (unmake_compact_addr): ... this. + (make_mips16_addr): Likewise to... + (make_compact_addr): ... this. + (is_mips_addr, is_mips16_addr, is_micromips_addr): New + functions. + (mips_elf_make_msymbol_special): Handle microMIPS code. + (msymbol_is_special): Rename to... + (msymbol_is_mips16): ... this. + (mips_make_symbol_special, mips_pc_is_mips16): Update + accordingly. + (msymbol_is_mips, msymbol_is_micromips): New functions. + (mips16_to_32_reg): Rename to... + (mips_reg3_to_reg): ... this. + (mips_pc_is_mips, mips_pc_is_micromips): New functions. + (mips_pc_isa): Likewise. + (mips_read_pc, mips_unwind_pc, mips_write_pc): Handle microMIPS + code. + (mips_fetch_instruction): Pass return status instead of printing + an error message if requested. Handle microMIPS code. Bail out + on an invalid ISA. + (micromips_op): New macro. + (b0s4_imm, b0s5_imm, b0s5_reg, b0s7_imm, b0s10_imm): Likewise. + (b1s9_imm, b2s3_cc, b4s2_regl, b5s5_op, b5s5_reg): Likewise. + (b6s4_op, b7s3_reg): Likewise. + (b0s6_op, b0s11_op, b0s12_imm, b0s16_imm, b0s26_imm): Likewise. + (b6s10_ext, b11s5_reg, b12s4_op): Likewise. + (mips_insn_size): New function. + (mips32_next_pc): Update mips_fetch_instruction call. + (micromips_relative_offset7): New function. + (micromips_relative_offset10): Likewise. + (micromips_relative_offset16): Likewise. + (micromips_pc_insn_size): Likewise. + (micromips_bc1_pc): Likewise. + (micromips_next_pc): Likewise. + (unpack_mips16): Update mips_fetch_instruction call. + (extended_mips16_next_pc): Update according to change to + mips16_to_32_reg. + (mips_next_pc): Update mips_pc_is_mips16 call. Handle microMIPS + code. + (mips16_scan_prologue): Update mips_fetch_instruction call. + Update according to change to mips16_to_32_reg. + (mips_insn16_frame_sniffer): Update mips_pc_is_mips16 call. + (mips_insn16_frame_base_sniffer): Likewise. + (micromips_decode_imm9): New function. + (micromips_scan_prologue): Likewise. + (mips_micro_frame_cache): Likewise. + (mips_micro_frame_this_id): Likewise. + (mips_micro_frame_prev_register): Likewise. + (mips_micro_frame_sniffer): Likewise. + (mips_micro_frame_unwind): New variable. + (mips_micro_frame_base_address): New function. + (mips_micro_frame_base): New variable. + (mips_micro_frame_base_sniffer): New function. + (mips32_scan_prologue): Update mips_fetch_instruction call. + (mips_insn32_frame_sniffer): Check for the standard MIPS ISA + rather than for MIPS16. + (mips_insn32_frame_base_sniffer): Likewise. + (mips_addr_bits_remove): Handle microMIPS code. + (deal_with_atomic_sequence): Rename to... + (mips_deal_with_atomic_sequence): ... this. Update the type + of the variable used to hold an instruction. Remove the ISA bit + check. Update mips_fetch_instruction call. + (micromips_deal_with_atomic_sequence): New function. + (deal_with_atomic_sequence): Likewise. + (mips_about_to_return): Handle microMIPS code. Update + mips_fetch_instruction call. + (heuristic_proc_start): Check for the standard MIPS ISA rather + than for MIPS16. Update mips_pc_is_mips16 and + mips_fetch_instruction calls. Handle microMIPS code. + (mips_push_dummy_code): Handle microMIPS code. + (mips_eabi_push_dummy_call): Likewise. + (mips_o32_return_value): Update mips_pc_is_mips16 call. + (mips_o64_push_dummy_call): Handle microMIPS code. + (mips_o64_return_value): Update mips_pc_is_mips16 call. + (is_delayed): Remove function. + (mips_single_step_through_delay): Replace the call to is_delayed + with mips32_instruction_has_delay_slot. Correct MIPS16 handling. + Handle microMIPS code. + (mips_skip_prologue): Update mips_pc_is_mips16 call. Handle + microMIPS code. + (mips32_in_function_epilogue_p): Update mips_fetch_instruction + call. + (micromips_in_function_epilogue_p): New function. + (mips16_in_function_epilogue_p): Update mips_fetch_instruction + call. + (mips_in_function_epilogue_p): Update mips_pc_is_mips16 call. + Handle microMIPS. + (gdb_print_insn_mips): Likewise. + (mips_breakpoint_from_pc): Likewise. + (mips_remote_breakpoint_from_pc): New function. + (mips32_instruction_has_delay_slot): Simplify making use of the + updated mips_fetch_instruction interface. + (micromips_instruction_has_delay_slot): New function. + (mips16_instruction_has_delay_slot): Simplify making use of the + updated mips_fetch_instruction interface. + (mips_adjust_breakpoint_address): Check for the standard MIPS + ISA rather than for MIPS16 ISA. Update for unmake_compact_addr + calls. Handle microMIPS code. + (mips_get_mips16_fn_stub_pc): Update mips_fetch_instruction call. + (mips_skip_trampoline_code): Handle microMIPS code. + (global_mips_compression): New function. + (mips_gdbarch_init): Handle the compressed ISA setting from ELF + file flags. Register the microMIPS remote breakpoint handler + and heuristic frame unwinder. + (show_mips_compression): New function. + (_initialize_mips_tdep): Add the "set mips compression" and + "show mips compression" commands. + 2012-05-18 Sergio Durigan Junior * ada-lang.c: diff --git a/gdb/NEWS b/gdb/NEWS index e39ccd3..d85fe0d 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -3,6 +3,8 @@ *** Changes since GDB 7.4 +* GDB now supports debugging microMIPS binaries. + * The "info os" command on GNU/Linux can now display information on several new classes of objects managed by the operating system: "info os procgroups" lists process groups @@ -166,6 +168,14 @@ HP OpenVMS ia64 ia64-hp-openvms* * New options +set mips compression +show mips compression + Select the compressed ISA encoding used in functions that have no symbol + information available. The encoding can be set to either of: + mips16 + micromips + and is updated automatically from ELF file flags if available. + set breakpoint condition-evaluation show breakpoint condition-evaluation Control whether breakpoint conditions are evaluated by GDB ("host") or by diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index e1e5f60..5d97724 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,10 @@ +2012-05-18 Sandra Loosemore + Maciej W. Rozycki + + * gdb.texinfo (MIPS): Document "set mips compression" and "show + mips compression". + (MIPS Breakpoint Kinds): New subsubsection. + 2012-05-18 Eli Zaretskii * gdb.texinfo (Architecture-Specific Protocol Details): Define diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 3515da7..214fe5d 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -20565,6 +20565,38 @@ default). @kindex show mips abi Show the MIPS ABI used by @value{GDBN} to debug the inferior. +@item set mips compression @var{arg} +@kindex set mips compression +@cindex code compression, @acronym{MIPS} +Tell @value{GDBN} which @acronym{MIPS} compressed +@acronym{ISA, Instruction Set Architecture} encoding is used by the +inferior. @value{GDBN} uses this for code disassembly and other +internal interpretation purposes. This setting is only referred to +when no executable has been associated with the debugging session or +the executable does not provide information about the encoding it uses. +Otherwise this setting is automatically updated from information +provided by the executable. + +Possible values of @var{arg} are @samp{mips16} and @samp{micromips}. +The default compressed @acronym{ISA} encoding is @samp{mips16}, as +executables containing @acronym{MIPS16} code frequently are not +identified as such. + +This setting is ``sticky''; that is, it retains its value across +debugging sessions until reset either explicitly with this command or +implicitly from an executable. + +The compiler and/or assembler typically add symbol table annotations to +identify functions compiled for the @acronym{MIPS16} or +@acronym{microMIPS} @acronym{ISA}s. If these function-scope annotations +are present, @value{GDBN} uses them in preference to the global +compressed @acronym{ISA} encoding setting. + +@item show mips compression +@kindex show mips compression +Show the @acronym{MIPS} compressed @acronym{ISA} encoding used by +@value{GDBN} to debug the inferior. + @item set mipsfpu @itemx show mipsfpu @xref{MIPS Embedded, set mipsfpu}. @@ -36903,6 +36935,7 @@ These breakpoint kinds are defined for the @samp{Z0} and @samp{Z1} packets. @menu * MIPS Register packet Format:: +* MIPS Breakpoint Kinds:: @end menu @node MIPS Register packet Format @@ -36929,6 +36962,28 @@ as @code{MIPS32}. @end table +@node MIPS Breakpoint Kinds +@subsubsection @acronym{MIPS} Breakpoint Kinds +@cindex breakpoint kinds, @acronym{MIPS} + +These breakpoint kinds are defined for the @samp{Z0} and @samp{Z1} packets. + +@table @r + +@item 2 +16-bit @acronym{MIPS16} mode breakpoint. + +@item 3 +16-bit @acronym{microMIPS} mode breakpoint. + +@item 4 +32-bit standard @acronym{MIPS} mode breakpoint. + +@item 5 +32-bit @acronym{microMIPS} mode breakpoint. + +@end table + @node Tracepoint Packets @section Tracepoint Packets @cindex tracepoint packets diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c index cd6891a..b9eca2b 100644 --- a/gdb/mips-tdep.c +++ b/gdb/mips-tdep.c @@ -62,6 +62,12 @@ 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 *, CORE_ADDR); +static int micromips_instruction_has_delay_slot (struct gdbarch *, CORE_ADDR, + int); +static int mips16_instruction_has_delay_slot (struct gdbarch *, CORE_ADDR, + int); + /* 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) @@ -93,6 +99,20 @@ static const char *const mips_abi_strings[] = { 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 { @@ -246,53 +266,127 @@ mips_abi_regsize (struct gdbarch *gdbarch) } } -/* MIPS16 function addresses are odd (bit 0 is set). Here are some - functions to test, set, or clear bit 0 of addresses. */ +/* 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. */ -static CORE_ADDR -is_mips16_addr (CORE_ADDR addr) +/* 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_mips16_addr (CORE_ADDR addr) +unmake_compact_addr (CORE_ADDR addr) { return ((addr) & ~(CORE_ADDR) 1); } +/* Add the ISA (compression) bit to ADDR. */ + static CORE_ADDR -make_mips16_addr (CORE_ADDR addr) +make_compact_addr (CORE_ADDR addr) { return ((addr) | (CORE_ADDR) 1); } /* Functions for setting and testing a bit in a minimal symbol that - marks it as 16-bit function. The MSB of the minimal symbol's - "info" field is used for this purpose. + 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 16-bit function, and sets a "special" bit in a - minimal symbol to mark it as a 16-bit function + 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; in particular synthetic symbols such as for PLT stubs + have no ELF-private part at all. - MSYMBOL_IS_SPECIAL tests the "special" bit in a minimal symbol */ + 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) { - if (ELF_ST_IS_MIPS16 (((elf_symbol_type *) - (sym))->internal_elf_sym.st_other)) - { - MSYMBOL_TARGET_FLAG_1 (msym) = 1; - } + elf_symbol_type *elfsym = (elf_symbol_type *) sym; + + if ((sym->flags & BSF_SYNTHETIC) != 0) + return; + + if (ELF_ST_IS_MICROMIPS (elfsym->internal_elf_sym.st_other)) + MSYMBOL_TARGET_FLAG_2 (msym) = 1; + else if (ELF_ST_IS_MIPS16 (elfsym->internal_elf_sym.st_other)) + MSYMBOL_TARGET_FLAG_1 (msym) = 1; +} + +/* Return one iff MSYM refers to standard ISA code. */ + +static int +msymbol_is_mips (struct minimal_symbol *msym) +{ + return !(MSYMBOL_TARGET_FLAG_1 (msym) | MSYMBOL_TARGET_FLAG_2 (msym)); } +/* Return one iff MSYM refers to MIPS16 code. */ + static int -msymbol_is_special (struct minimal_symbol *msym) +msymbol_is_mips16 (struct minimal_symbol *msym) { return MSYMBOL_TARGET_FLAG_1 (msym); } +/* Return one iff MSYM refers to microMIPS code. */ + +static int +msymbol_is_micromips (struct minimal_symbol *msym) +{ + return MSYMBOL_TARGET_FLAG_2 (msym); +} + /* 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 @@ -686,8 +780,8 @@ mips_ax_pseudo_register_push_stack (struct gdbarch *gdbarch, return 0; } -/* Table to translate MIPS16 register field to actual register number. */ -static int mips16_to_32_reg[8] = { 16, 17, 2, 3, 4, 5, 6, 7 }; +/* Table to translate 3-bit register field to actual register number. */ +static int 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 @@ -1017,22 +1111,94 @@ show_mask_address (struct ui_file *file, int from_tty, } } +/* Tell if the program counter value in MEMADDR is in a standard ISA + function. */ + +int +mips_pc_is_mips (CORE_ADDR memaddr) +{ + struct 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 (memaddr); + if (sym) + return msymbol_is_mips (sym); + else + return is_mips_addr (memaddr); +} + /* Tell if the program counter value in MEMADDR is in a MIPS16 function. */ int -mips_pc_is_mips16 (CORE_ADDR memaddr) +mips_pc_is_mips16 (struct gdbarch *gdbarch, CORE_ADDR memaddr) { struct 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 or normal MIPS. Otherwise if bit 0 of - the address is set, assume this is a MIPS16 address. */ + 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 (memaddr); + if (sym) + return msymbol_is_mips16 (sym); + 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 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 (memaddr); + if (sym) + return msymbol_is_micromips (sym); + 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 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 (memaddr); if (sym) - return msymbol_is_special (sym); + { + if (msymbol_is_micromips (sym)) + return ISA_MICROMIPS; + else if (msymbol_is_mips16 (sym)) + return ISA_MIPS16; + else + return ISA_MIPS; + } else - return is_mips16_addr (memaddr); + { + if (is_mips_addr (memaddr)) + return ISA_MIPS; + else if (is_micromips_addr (gdbarch, memaddr)) + return ISA_MICROMIPS; + else + return ISA_MIPS16; + } } /* Various MIPS16 thunk (aka stub or trampoline) names. */ @@ -1084,8 +1250,8 @@ mips_read_pc (struct regcache *regcache) ULONGEST pc; regcache_cooked_read_signed (regcache, regnum, &pc); - if (is_mips16_addr (pc)) - pc = unmake_mips16_addr (pc); + if (is_compact_addr (pc)) + pc = unmake_compact_addr (pc); return pc; } @@ -1095,8 +1261,8 @@ 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)); - if (is_mips16_addr (pc)) - pc = unmake_mips16_addr (pc); + if (is_compact_addr (pc)) + pc = unmake_compact_addr (pc); /* 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 @@ -1106,14 +1272,14 @@ mips_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame) { pc = frame_unwind_register_signed (next_frame, gdbarch_num_regs (gdbarch) + MIPS_RA_REGNUM); - if (is_mips16_addr (pc)) - pc = unmake_mips16_addr (pc); + if (is_compact_addr (pc)) + pc = unmake_compact_addr (pc); if (mips_in_frame_stub (pc)) { pc = frame_unwind_register_signed (next_frame, gdbarch_num_regs (gdbarch) + MIPS_S2_REGNUM); - if (is_mips16_addr (pc)) - pc = unmake_mips16_addr (pc); + if (is_compact_addr (pc)) + pc = unmake_compact_addr (pc); } } return pc; @@ -1148,33 +1314,47 @@ mips_write_pc (struct regcache *regcache, CORE_ADDR pc) { int regnum = gdbarch_pc_regnum (get_regcache_arch (regcache)); - if (mips_pc_is_mips16 (pc)) - regcache_cooked_write_unsigned (regcache, regnum, make_mips16_addr (pc)); - else + if (mips_pc_is_mips (pc)) regcache_cooked_write_unsigned (regcache, regnum, pc); + else + regcache_cooked_write_unsigned (regcache, regnum, make_compact_addr (pc)); } -/* Fetch and return instruction from the specified location. If the PC - is odd, assume it's a MIPS16 instruction; otherwise MIPS32. */ +/* Fetch and return instruction from the specified location. Handle + MIPS16/microMIPS as appropriate. */ static ULONGEST -mips_fetch_instruction (struct gdbarch *gdbarch, CORE_ADDR addr) +mips_fetch_instruction (struct gdbarch *gdbarch, + enum mips_isa isa, CORE_ADDR addr, int *statusp) { enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); gdb_byte buf[MIPS_INSN32_SIZE]; int instlen; int status; - if (mips_pc_is_mips16 (addr)) + switch (isa) { + case ISA_MICROMIPS: + case ISA_MIPS16: instlen = MIPS_INSN16_SIZE; - addr = unmake_mips16_addr (addr); + addr = unmake_compact_addr (addr); + break; + case ISA_MIPS: + instlen = MIPS_INSN32_SIZE; + break; + default: + internal_error (__FILE__, __LINE__, _("invalid ISA")); + break; } - else - instlen = MIPS_INSN32_SIZE; status = target_read_memory (addr, buf, instlen); + if (statusp != NULL) + *statusp = status; if (status) - memory_error (status, addr); + { + if (statusp == NULL) + memory_error (status, addr); + return 0; + } return extract_unsigned_integer (buf, instlen, byte_order); } @@ -1195,6 +1375,63 @@ mips_fetch_instruction (struct gdbarch *gdbarch, CORE_ADDR addr) #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) == 0x1f) + return 3 * MIPS_INSN16_SIZE; + else 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) { @@ -1239,7 +1476,7 @@ 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, pc); + inst = mips_fetch_instruction (gdbarch, ISA_MIPS, pc, NULL); if ((inst & 0xe0000000) != 0) /* Not a special, jump or branch instruction. */ { @@ -1409,6 +1646,276 @@ mips32_next_pc (struct frame_info *frame, CORE_ADDR pc) 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)) + { + /* 48-bit instructions. */ + case 3 * MIPS_INSN16_SIZE: /* POOL48A: bits 011111 */ + /* No branch or jump instructions in this category. */ + pc += 2 * MIPS_INSN16_SIZE; + break; + + /* 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 @@ -1543,8 +2050,8 @@ unpack_mips16 (struct gdbarch *gdbarch, CORE_ADDR pc, unsigned int nexthalf; value = ((inst & 0x1f) << 5) | ((inst >> 5) & 0x1f); value = value << 16; - nexthalf = mips_fetch_instruction (gdbarch, pc + 2); /* low bit - still set. */ + nexthalf = mips_fetch_instruction (gdbarch, ISA_MIPS16, pc + 2, NULL); + /* Low bit still set. */ value |= nexthalf; offset = value; regx = -1; @@ -1598,7 +2105,7 @@ extended_mips16_next_pc (struct frame_info *frame, CORE_ADDR pc, struct upk_mips16 upk; int reg; unpack_mips16 (gdbarch, pc, extension, insn, ritype, &upk); - reg = get_frame_register_signed (frame, mips16_to_32_reg[upk.regx]); + reg = get_frame_register_signed (frame, mips_reg3_to_reg[upk.regx]); if (reg == 0) pc += (upk.offset << 1) + 2; else @@ -1610,7 +2117,7 @@ extended_mips16_next_pc (struct frame_info *frame, CORE_ADDR pc, struct upk_mips16 upk; int reg; unpack_mips16 (gdbarch, pc, extension, insn, ritype, &upk); - reg = get_frame_register_signed (frame, mips16_to_32_reg[upk.regx]); + reg = get_frame_register_signed (frame, mips_reg3_to_reg[upk.regx]); if (reg != 0) pc += (upk.offset << 1) + 2; else @@ -1643,7 +2150,7 @@ extended_mips16_next_pc (struct frame_info *frame, CORE_ADDR pc, upk.regx = (insn >> 8) & 0x07; upk.regy = (insn >> 5) & 0x07; if ((upk.regy & 1) == 0) - reg = mips16_to_32_reg[upk.regx]; + reg = mips_reg3_to_reg[upk.regx]; else reg = 31; /* Function return instruction. */ pc = get_frame_register_signed (frame, reg); @@ -1683,12 +2190,16 @@ mips16_next_pc (struct frame_info *frame, CORE_ADDR pc) 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 isnt hard because all the data is available. - The MIPS32 and MIPS16 variants are quite different. */ + The MIPS32, MIPS16 and microMIPS variants are quite different. */ static CORE_ADDR mips_next_pc (struct frame_info *frame, CORE_ADDR pc) { - if (mips_pc_is_mips16 (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); } @@ -1805,7 +2316,8 @@ mips16_scan_prologue (struct gdbarch *gdbarch, prev_inst = inst; /* Fetch and decode the instruction. */ - inst = (unsigned short) mips_fetch_instruction (gdbarch, cur_pc); + 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 @@ -1836,13 +2348,13 @@ mips16_scan_prologue (struct gdbarch *gdbarch, else if ((inst & 0xf800) == 0xd000) /* sw reg,n($sp) */ { offset = mips16_get_imm (prev_inst, inst, 8, 4, 0); - reg = mips16_to_32_reg[(inst & 0x700) >> 8]; + 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 = mips16_to_32_reg[(inst & 0xe0) >> 5]; + 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) */ @@ -1870,13 +2382,13 @@ mips16_scan_prologue (struct gdbarch *gdbarch, else if ((inst & 0xFF00) == 0xd900) /* sw reg,offset($s1) */ { offset = mips16_get_imm (prev_inst, inst, 5, 4, 0); - reg = mips16_to_32_reg[(inst & 0xe0) >> 5]; + 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 = mips16_to_32_reg[(inst & 0xe0) >> 5]; + reg = mips_reg3_to_reg[(inst & 0xe0) >> 5]; set_reg_offset (gdbarch, this_cache, reg, frame_addr + offset); } else if ((inst & 0xf81f) == 0xe809 @@ -2033,49 +2545,479 @@ mips16_scan_prologue (struct gdbarch *gdbarch, 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--) + /* 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]; + } + + /* If we didn't reach the end of the prologue when scanning the function + instructions, then set end_prologue_addr to the address of the + instruction immediately after the last one we scanned. */ + if (end_prologue_addr == 0) + end_prologue_addr = cur_pc; + + 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 (*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, *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 (*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 = 0; + 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. */ + 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; + 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)) + { + /* 48-bit instructions. */ + case 3 * MIPS_INSN16_SIZE: + /* No prologue instructions in this category. */ + this_non_prologue_insn = 1; + loc += 2 * MIPS_INSN16_SIZE; + break; + + /* 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 is 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: + this_non_prologue_insn = 1; + break; + } + 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: + this_non_prologue_insn = 1; + break; + } + break; + } + if (sp_adj < 0) + frame_offset -= sp_adj; + + non_prologue_insns += this_non_prologue_insn; + /* Enough non-prologue insns seen or positive stack adjustment? */ + if (end_prologue_addr == 0 && (non_prologue_insns > 1 || sp_adj > 0)) { - set_reg_offset (gdbarch, this_cache, reg, sp + offset); - offset -= mips_abi_regsize (gdbarch); + end_prologue_addr = prev_non_prologue_insn ? prev_pc : cur_pc; + break; } + prev_non_prologue_insn = this_non_prologue_insn; + prev_pc = cur_pc; } if (this_cache != NULL) { this_cache->base = - (get_frame_register_signed (this_frame, + (get_frame_register_signed (this_frame, gdbarch_num_regs (gdbarch) + frame_reg) - + frame_offset - frame_adjust); + + 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. */ + 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]; + = this_cache->saved_regs[gdbarch_num_regs (gdbarch) + MIPS_RA_REGNUM]; } /* If we didn't reach the end of the prologue when scanning the function instructions, then set end_prologue_addr to the address of the - instruction immediately after the last one we scanned. */ + 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. */ if (end_prologue_addr == 0) - end_prologue_addr = cur_pc; + end_prologue_addr = prev_non_prologue_insn ? prev_pc : cur_pc; return end_prologue_addr; } -/* Heuristic unwinder for 16-bit MIPS instruction set (aka MIPS16). +/* Heuristic unwinder for procedures using microMIPS instructions. Procedures that use the 32-bit instruction set are handled by the - mips_insn32 unwinder. */ + mips_insn32 unwinder. Likewise MIPS16 and the mips_insn16 unwinder. */ static struct mips_frame_cache * -mips_insn16_frame_cache (struct frame_info *this_frame, void **this_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 (*this_cache); + cache = FRAME_OBSTACK_ZALLOC (struct mips_frame_cache); (*this_cache) = cache; cache->saved_regs = trad_frame_alloc_saved_regs (this_frame); @@ -2087,15 +3029,15 @@ mips_insn16_frame_cache (struct frame_info *this_frame, void **this_cache) find_pc_partial_function (pc, NULL, &start_addr, NULL); if (start_addr == 0) - start_addr = heuristic_proc_start (gdbarch, pc); + 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; - mips16_scan_prologue (gdbarch, start_addr, pc, this_frame, *this_cache); + micromips_scan_prologue (gdbarch, start_addr, pc, this_frame, *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, @@ -2105,11 +3047,11 @@ mips_insn16_frame_cache (struct frame_info *this_frame, void **this_cache) } static void -mips_insn16_frame_this_id (struct frame_info *this_frame, void **this_cache, - struct frame_id *this_id) +mips_micro_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); + struct mips_frame_cache *info = mips_micro_frame_cache (this_frame, + this_cache); /* This marks the outermost frame. */ if (info->base == 0) return; @@ -2117,57 +3059,61 @@ mips_insn16_frame_this_id (struct frame_info *this_frame, void **this_cache, } static struct value * -mips_insn16_frame_prev_register (struct frame_info *this_frame, - void **this_cache, int regnum) +mips_micro_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); + 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_insn16_frame_sniffer (const struct frame_unwind *self, - struct frame_info *this_frame, void **this_cache) +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_mips16 (pc)) + + if (mips_pc_is_micromips (gdbarch, pc)) return 1; return 0; } -static const struct frame_unwind mips_insn16_frame_unwind = +static const struct frame_unwind mips_micro_frame_unwind = { NORMAL_FRAME, default_frame_unwind_stop_reason, - mips_insn16_frame_this_id, - mips_insn16_frame_prev_register, + mips_micro_frame_this_id, + mips_micro_frame_prev_register, NULL, - mips_insn16_frame_sniffer + mips_micro_frame_sniffer }; static CORE_ADDR -mips_insn16_frame_base_address (struct frame_info *this_frame, - void **this_cache) +mips_micro_frame_base_address (struct frame_info *this_frame, + void **this_cache) { - struct mips_frame_cache *info = mips_insn16_frame_cache (this_frame, - this_cache); + struct mips_frame_cache *info = mips_micro_frame_cache (this_frame, + this_cache); return info->base; } -static const struct frame_base mips_insn16_frame_base = +static const struct frame_base mips_micro_frame_base = { - &mips_insn16_frame_unwind, - mips_insn16_frame_base_address, - mips_insn16_frame_base_address, - mips_insn16_frame_base_address + &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_insn16_frame_base_sniffer (struct frame_info *this_frame) +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_mips16 (pc)) - return &mips_insn16_frame_base; + + if (mips_pc_is_micromips (gdbarch, pc)) + return &mips_micro_frame_base; else return NULL; } @@ -2236,7 +3182,8 @@ restart: int reg; /* Fetch the instruction. */ - inst = (unsigned long) mips_fetch_instruction (gdbarch, cur_pc); + 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; @@ -2420,7 +3367,7 @@ restart: /* 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. */ + 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) @@ -2485,7 +3432,7 @@ 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_mips16 (pc)) + if (mips_pc_is_mips (pc)) return 1; return 0; } @@ -2521,7 +3468,7 @@ 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_mips16 (pc)) + if (mips_pc_is_mips (pc)) return &mips_insn32_frame_base; else return NULL; @@ -2658,8 +3605,8 @@ mips_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR addr) { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); - if (is_mips16_addr (addr)) - addr = unmake_mips16_addr (addr); + if (is_compact_addr (addr)) + addr = unmake_compact_addr (addr); if (mips_mask_address_p (tdep) && (((ULONGEST) addr) >> 32 == 0xffffffffUL)) /* This hack is a work-around for existing boards using PMON, the @@ -2682,34 +3629,33 @@ mips_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR addr) return addr; } -/* Instructions used during single-stepping of atomic sequences. */ -#define LL_OPCODE 0x30 -#define LLD_OPCODE 0x34 -#define SC_OPCODE 0x38 -#define SCD_OPCODE 0x3c /* 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 -deal_with_atomic_sequence (struct gdbarch *gdbarch, - struct address_space *aspace, CORE_ADDR pc) +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. */ - unsigned long insn; + 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. */ - if (pc & 0x01) - return 0; - - insn = mips_fetch_instruction (gdbarch, loc); + 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; @@ -2720,7 +3666,7 @@ deal_with_atomic_sequence (struct gdbarch *gdbarch, { int is_branch = 0; loc += MIPS_INSN32_SIZE; - insn = mips_fetch_instruction (gdbarch, loc); + 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 @@ -2795,6 +3741,177 @@ deal_with_atomic_sequence (struct gdbarch *gdbarch, 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; /* 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)) + { + /* 48-bit instructions. */ + case 3 * MIPS_INSN16_SIZE: /* POOL48A: bits 011111 */ + loc += 2 * MIPS_INSN16_SIZE; + break; + + /* 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 @@ -2827,10 +3944,10 @@ mips_about_to_return (struct gdbarch *gdbarch, CORE_ADDR pc) ULONGEST hint; /* This used to check for MIPS16, but this piece of code is never - called for MIPS16 functions. */ - gdb_assert (!mips_pc_is_mips16 (pc)); + called for MIPS16 functions. And likewise microMIPS ones. */ + gdb_assert (mips_pc_is_mips (pc)); - insn = mips_fetch_instruction (gdbarch, pc); + insn = mips_fetch_instruction (gdbarch, ISA_MIPS, pc, NULL); hint = 0x7c0; return (insn & ~hint) == 0x3e00008; /* jr(.hb) $ra */ } @@ -2858,7 +3975,7 @@ heuristic_proc_start (struct gdbarch *gdbarch, CORE_ADDR pc) if (heuristic_fence_post == UINT_MAX || fence < VM_MIN_ADDRESS) fence = VM_MIN_ADDRESS; - instlen = mips_pc_is_mips16 (pc) ? MIPS_INSN16_SIZE : MIPS_INSN32_SIZE; + instlen = mips_pc_is_mips (pc) ? MIPS_INSN32_SIZE : MIPS_INSN16_SIZE; inf = current_inferior (); @@ -2903,7 +4020,7 @@ heuristic-fence-post' command.\n", return 0; } - else if (mips_pc_is_mips16 (start_pc)) + else if (mips_pc_is_mips16 (gdbarch, start_pc)) { unsigned short inst; @@ -2915,12 +4032,13 @@ heuristic-fence-post' command.\n", addiu sp,-n daddiu sp,-n extend -n followed by 'addiu sp,+n' or 'daddiu sp,+n'. */ - inst = mips_fetch_instruction (gdbarch, start_pc); + 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, start_pc - instlen); + inst = mips_fetch_instruction (gdbarch, ISA_MIPS16, + start_pc - instlen, NULL); if ((inst & 0xf800) == 0xf000) /* extend */ start_pc -= instlen; } @@ -2938,6 +4056,67 @@ heuristic-fence-post' command.\n", 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. */ @@ -3127,14 +4306,14 @@ mips_eabi_push_dummy_call (struct gdbarch *gdbarch, struct value *function, { CORE_ADDR addr = extract_signed_integer (value_contents (arg), len, byte_order); - if (mips_pc_is_mips16 (addr)) + if (mips_pc_is_mips (addr)) + val = value_contents (arg); + else { store_signed_integer (valbuf, len, byte_order, - make_mips16_addr (addr)); + make_compact_addr (addr)); val = valbuf; } - else - val = value_contents (arg); } /* The EABI passes structures that do not fit in a register by reference. */ @@ -4226,8 +5405,8 @@ mips_o32_return_value (struct gdbarch *gdbarch, struct value *function, 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); - int mips16 = mips_pc_is_mips16 (func_addr); enum mips_fval_reg fval_reg; fval_reg = readbuf ? mips16 ? mips_fval_gpr : mips_fval_fpr : mips_fval_both; @@ -4528,10 +5707,10 @@ mips_o64_push_dummy_call (struct gdbarch *gdbarch, struct value *function, { CORE_ADDR addr = extract_signed_integer (value_contents (arg), len, byte_order); - if (mips_pc_is_mips16 (addr)) + if (!mips_pc_is_mips (addr)) { store_signed_integer (valbuf, len, byte_order, - make_mips16_addr (addr)); + make_compact_addr (addr)); val = valbuf; } } @@ -4694,8 +5873,8 @@ mips_o64_return_value (struct gdbarch *gdbarch, struct value *function, 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); - int mips16 = mips_pc_is_mips16 (func_addr); enum mips_fval_reg fval_reg; fval_reg = readbuf ? mips16 ? mips_fval_gpr : mips_fval_fpr : mips_fval_both; @@ -5137,42 +6316,32 @@ mips_print_registers_info (struct gdbarch *gdbarch, struct ui_file *file, } } -/* Is this a branch with a delay slot? */ - -static int -is_delayed (unsigned long insn) -{ - int i; - for (i = 0; i < NUMOPCODES; ++i) - if (mips_opcodes[i].pinfo != INSN_MACRO - && (insn & mips_opcodes[i].mask) == mips_opcodes[i].match) - break; - return (i < NUMOPCODES - && (mips_opcodes[i].pinfo & (INSN_UNCOND_BRANCH_DELAY - | INSN_COND_BRANCH_DELAY - | INSN_COND_BRANCH_LIKELY))); -} - 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); - gdb_byte buf[MIPS_INSN32_SIZE]; - - /* There is no branch delay slot on MIPS16. */ - if (mips_pc_is_mips16 (pc)) - return 0; - - if (!breakpoint_here_p (get_frame_address_space (frame), pc + 4)) + struct address_space *aspace; + enum mips_isa isa; + ULONGEST insn; + int status; + int size; + + if ((mips_pc_is_mips (pc) + && !mips32_instruction_has_delay_slot (gdbarch, pc)) + || (mips_pc_is_micromips (gdbarch, pc) + && !micromips_instruction_has_delay_slot (gdbarch, pc, 0)) + || (mips_pc_is_mips16 (gdbarch, pc) + && !mips16_instruction_has_delay_slot (gdbarch, pc, 0))) return 0; - if (!safe_frame_unwind_memory (frame, pc, buf, sizeof buf)) - /* If error reading memory, guess that it is not a delayed - branch. */ - return 0; - return is_delayed (extract_unsigned_integer (buf, sizeof buf, byte_order)); + 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 @@ -5211,8 +6380,10 @@ mips_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) if (limit_pc == 0) limit_pc = pc + 100; /* Magic. */ - if (mips_pc_is_mips16 (pc)) + 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); } @@ -5239,7 +6410,7 @@ mips32_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc) unsigned long high_word; unsigned long inst; - inst = mips_fetch_instruction (gdbarch, pc); + inst = mips_fetch_instruction (gdbarch, ISA_MIPS, pc, NULL); high_word = (inst >> 16) & 0xffff; if (high_word != 0x27bd /* addiu $sp,$sp,offset */ @@ -5255,6 +6426,107 @@ mips32_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc) return 0; } +/* Check whether the PC is in a function epilogue (microMIPS version). + This is a helper function for mips_in_function_epilogue_p. */ + +static int +micromips_in_function_epilogue_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)) + { + /* 48-bit instructions. */ + case 3 * MIPS_INSN16_SIZE: + /* No epilogue instructions in this category. */ + return 0; + + /* 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; +} + /* Check whether the PC is in a function epilogue (16-bit version). This is a helper function for mips_in_function_epilogue_p. */ static int @@ -5276,7 +6548,7 @@ mips16_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc) { unsigned short inst; - inst = mips_fetch_instruction (gdbarch, pc); + inst = mips_fetch_instruction (gdbarch, ISA_MIPS16, pc, NULL); if ((inst & 0xf800) == 0xf000) /* extend */ continue; @@ -5300,8 +6572,10 @@ mips16_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc) static int mips_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc) { - if (mips_pc_is_mips16 (pc)) + if (mips_pc_is_mips16 (gdbarch, pc)) return mips16_in_function_epilogue_p (gdbarch, pc); + else if (mips_pc_is_micromips (gdbarch, pc)) + return micromips_in_function_epilogue_p (gdbarch, pc); else return mips32_in_function_epilogue_p (gdbarch, pc); } @@ -5450,15 +6724,20 @@ reinit_frame_cache_sfunc (char *args, int from_tty, static int gdb_print_insn_mips (bfd_vma memaddr, struct disassemble_info *info) { + struct gdbarch *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 (memaddr)) + 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 ? ~1 : ~3); + memaddr &= (info->mach == bfd_mach_mips16 + || info->mach == bfd_mach_mips_micromips) ? ~1 : ~3; /* Set the disassembler options. */ if (!info->disassembler_options) @@ -5509,15 +6788,33 @@ 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 (*pcptr)) + if (mips_pc_is_mips16 (gdbarch, pc)) { static gdb_byte mips16_big_breakpoint[] = { 0xe8, 0xa5 }; - *pcptr = unmake_mips16_addr (*pcptr); + *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 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); + *lenptr = size; + return (size == 2) ? micromips16_big_breakpoint + : micromips32_big_breakpoint; + } else { /* The IDT board uses an unusual breakpoint value, and @@ -5546,13 +6843,29 @@ mips_breakpoint_from_pc (struct gdbarch *gdbarch, } else { - if (mips_pc_is_mips16 (*pcptr)) + if (mips_pc_is_mips16 (gdbarch, pc)) { static gdb_byte mips16_little_breakpoint[] = { 0xa5, 0xe8 }; - *pcptr = unmake_mips16_addr (*pcptr); + *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 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); + *lenptr = size; + return (size == 2) ? micromips16_little_breakpoint + : micromips32_little_breakpoint; + } else { static gdb_byte little_breakpoint[] = { 0xd, 0, 0x5, 0 }; @@ -5573,6 +6886,43 @@ mips_breakpoint_from_pc (struct gdbarch *gdbarch, } } +/* 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 ADDR instruction has a branch delay slot (i.e. it is a jump or branch instruction). This function is based on mips32_next_pc. */ @@ -5580,18 +6930,16 @@ mips_breakpoint_from_pc (struct gdbarch *gdbarch, static int mips32_instruction_has_delay_slot (struct gdbarch *gdbarch, CORE_ADDR addr) { - gdb_byte buf[MIPS_INSN32_SIZE]; unsigned long inst; int status; int op; int rs; int rt; - status = target_read_memory (addr, buf, MIPS_INSN32_SIZE); + inst = mips_fetch_instruction (gdbarch, ISA_MIPS, addr, &status); if (status) return 0; - inst = mips_fetch_instruction (gdbarch, addr); op = itype_op (inst); if ((inst & 0xe0000000) != 0) { @@ -5635,18 +6983,83 @@ mips32_instruction_has_delay_slot (struct gdbarch *gdbarch, CORE_ADDR addr) has a branch delay slot (i.e. it is a non-compact jump instruction). */ static int +micromips_instruction_has_delay_slot (struct gdbarch *gdbarch, + CORE_ADDR addr, int mustbe32) +{ + ULONGEST insn; + int status; + + insn = mips_fetch_instruction (gdbarch, ISA_MICROMIPS, addr, &status); + if (status) + return 0; + + if (!mustbe32) /* 16-bit instructions. */ + return (micromips_op (insn) == 0x11 + /* POOL16C: bits 010001 */ + && (b5s5_op (insn) == 0xc + /* JR16: bits 010001 01100 */ + || (b5s5_op (insn) & 0x1e) == 0xe)) + /* JALR16, JALRS16: bits 010001 0111x */ + || (micromips_op (insn) & 0x37) == 0x23 + /* BEQZ16, BNEZ16: bits 10x011 */ + || micromips_op (insn) == 0x33; + /* B16: bits 110011 */ + + /* 32-bit instructions. */ + if (micromips_op (insn) == 0x0) + /* POOL32A: bits 000000 */ + { + insn <<= 16; + insn |= mips_fetch_instruction (gdbarch, ISA_MICROMIPS, addr, &status); + if (status) + return 0; + 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 */ + } + + return (micromips_op (insn) == 0x10 + /* POOL32I: bits 010000 */ + && ((b5s5_op (insn) & 0x1c) == 0x0 + /* BLTZ, BLTZAL, BGEZ, BGEZAL: 010000 000xx */ + || (b5s5_op (insn) & 0x1d) == 0x4 + /* BLEZ, BGTZ: bits 010000 001x0 */ + || (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 */ + || (micromips_op (insn) & 0x1f) == 0x1d + /* JALS, JAL: bits x11101 */ + || (micromips_op (insn) & 0x37) == 0x25 + /* BEQ, BNE: bits 10x101 */ + || micromips_op (insn) == 0x35 + /* J: bits 110101 */ + || micromips_op (insn) == 0x3c; + /* JALX: bits 111100 */ +} + +static int mips16_instruction_has_delay_slot (struct gdbarch *gdbarch, CORE_ADDR addr, int mustbe32) { - gdb_byte buf[MIPS_INSN16_SIZE]; unsigned short inst; int status; - status = target_read_memory (addr, buf, MIPS_INSN16_SIZE); + inst = mips_fetch_instruction (gdbarch, ISA_MIPS16, addr, &status); if (status) return 0; - inst = mips_fetch_instruction (gdbarch, addr); if (!mustbe32) return (inst & 0xf89f) == 0xe800; /* JR/JALR (16-bit instruction) */ return (inst & 0xf800) == 0x1800; /* JAL/JALX (32-bit instruction) */ @@ -5735,7 +7148,7 @@ mips_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr) && func_addr > boundary && func_addr <= bpaddr) boundary = func_addr; - if (!mips_pc_is_mips16 (bpaddr)) + if (mips_pc_is_mips (bpaddr)) { if (bpaddr == boundary) return bpaddr; @@ -5748,31 +7161,37 @@ mips_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr) } else { + int (*instruction_has_delay_slot) (struct gdbarch *, CORE_ADDR, int); CORE_ADDR addr, jmpaddr; int i; - boundary = unmake_mips16_addr (boundary); + 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. */ + instruction_has_delay_slot = (mips_pc_is_micromips (gdbarch, bpaddr) + ? micromips_instruction_has_delay_slot + : mips16_instruction_has_delay_slot); jmpaddr = 0; addr = bpaddr; for (i = 1; i < 4; i++) { - if (unmake_mips16_addr (addr) == boundary) + if (unmake_compact_addr (addr) == boundary) break; - addr -= 2; - if (i == 1 && mips16_instruction_has_delay_slot (gdbarch, addr, 0)) + addr -= MIPS_INSN16_SIZE; + if (i == 1 && instruction_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 - && mips16_instruction_has_delay_slot (gdbarch, addr, 1)) + else if (i > 1 && instruction_has_delay_slot (gdbarch, addr, 1)) { if (i == 2) /* Looks like a JAL/JALX at [target-2], but it could also @@ -5917,7 +7336,7 @@ mips_get_mips16_fn_stub_pc (struct frame_info *frame, CORE_ADDR pc) status == 0 && target_pc == 0 && i < 20; i++, pc += MIPS_INSN32_SIZE) { - ULONGEST inst = mips_fetch_instruction (gdbarch, pc); + ULONGEST inst = mips_fetch_instruction (gdbarch, ISA_MIPS, pc, NULL); CORE_ADDR imm; int rt; int rs; @@ -6219,24 +7638,24 @@ mips_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc) if (new_pc) { pc = new_pc; - if (is_mips16_addr (pc)) - pc = unmake_mips16_addr (pc); + if (is_compact_addr (pc)) + pc = unmake_compact_addr (pc); } new_pc = find_solib_trampoline_target (frame, pc); if (new_pc) { pc = new_pc; - if (is_mips16_addr (pc)) - pc = unmake_mips16_addr (pc); + if (is_compact_addr (pc)) + pc = unmake_compact_addr (pc); } new_pc = mips_skip_pic_trampoline_code (frame, pc); if (new_pc) { pc = new_pc; - if (is_mips16_addr (pc)) - pc = unmake_mips16_addr (pc); + if (is_compact_addr (pc)) + pc = unmake_compact_addr (pc); } } while (pc != target_pc); @@ -6390,6 +7809,23 @@ global_mips_abi (void) 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) { @@ -6427,6 +7863,7 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) int elf_fpu_type = 0; const char **reg_names; struct mips_regnum mips_regnum, *regnum; + enum mips_isa mips_isa; int dspacc; int dspctl; @@ -6720,6 +8157,17 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) 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, @@ -6825,6 +8273,7 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) 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; @@ -7050,6 +8499,8 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) 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); @@ -7114,10 +8565,12 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) 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) @@ -7211,6 +8664,16 @@ show_mips_abi (struct ui_file *file, } } +/* 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) { @@ -7327,6 +8790,23 @@ This option can be set to one of:\n\ 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. */ diff --git a/gdb/mips-tdep.h b/gdb/mips-tdep.h index 9dd4541..2e85895 100644 --- a/gdb/mips-tdep.h +++ b/gdb/mips-tdep.h @@ -38,6 +38,14 @@ enum mips_abi /* Return the MIPS ABI associated with GDBARCH. */ enum mips_abi mips_abi (struct gdbarch *gdbarch); +/* Base and compressed MIPS ISA variations. */ +enum mips_isa + { + ISA_MIPS = -1, /* mips_compression_string depends on it. */ + ISA_MIPS16, + ISA_MICROMIPS + }; + /* Return the MIPS ISA's register size. Just a short cut to the BFD architecture's word size. */ extern int mips_isa_regsize (struct gdbarch *gdbarch); @@ -77,6 +85,7 @@ struct gdbarch_tdep /* mips options */ enum mips_abi mips_abi; enum mips_abi found_abi; + enum mips_isa mips_isa; enum mips_fpu_type mips_fpu_type; int mips_last_arg_regnum; int mips_last_fp_arg_regnum; @@ -152,9 +161,17 @@ enum /* Single step based on where the current instruction will take us. */ extern int mips_software_single_step (struct frame_info *frame); +/* Tell if the program counter value in MEMADDR is in a standard + MIPS function. */ +extern int mips_pc_is_mips (bfd_vma memaddr); + /* Tell if the program counter value in MEMADDR is in a MIPS16 function. */ -extern int mips_pc_is_mips16 (bfd_vma memaddr); +extern int mips_pc_is_mips16 (struct gdbarch *gdbarch, bfd_vma memaddr); + +/* Tell if the program counter value in MEMADDR is in a microMIPS + function. */ +extern int mips_pc_is_micromips (struct gdbarch *gdbarch, bfd_vma memaddr); /* Return the currently configured (or set) saved register size. */ extern unsigned int mips_abi_regsize (struct gdbarch *gdbarch); -- 2.7.4