| /* Target dependent code for GDB on TI C6x systems. |
| |
| Copyright (C) 2010-2024 Free Software Foundation, Inc. |
| Contributed by Andrew Jenner <andrew@codesourcery.com> |
| Contributed by Yao Qi <yao@codesourcery.com> |
| |
| This file is part of GDB. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| #include "extract-store-integer.h" |
| #include "frame.h" |
| #include "frame-unwind.h" |
| #include "frame-base.h" |
| #include "trad-frame.h" |
| #include "dwarf2/frame.h" |
| #include "symtab.h" |
| #include "inferior.h" |
| #include "gdbtypes.h" |
| #include "gdbcore.h" |
| #include "cli/cli-cmds.h" |
| #include "target.h" |
| #include "dis-asm.h" |
| #include "regcache.h" |
| #include "value.h" |
| #include "symfile.h" |
| #include "arch-utils.h" |
| #include "glibc-tdep.h" |
| #include "infcall.h" |
| #include "regset.h" |
| #include "tramp-frame.h" |
| #include "linux-tdep.h" |
| #include "solib.h" |
| #include "objfiles.h" |
| #include "osabi.h" |
| #include "tic6x-tdep.h" |
| #include "language.h" |
| #include "target-descriptions.h" |
| #include <algorithm> |
| |
| #define TIC6X_OPCODE_SIZE 4 |
| #define TIC6X_FETCH_PACKET_SIZE 32 |
| |
| #define INST_S_BIT(INST) ((INST >> 1) & 1) |
| #define INST_X_BIT(INST) ((INST >> 12) & 1) |
| |
| const gdb_byte tic6x_bkpt_illegal_opcode_be[] = { 0x56, 0x45, 0x43, 0x14 }; |
| const gdb_byte tic6x_bkpt_illegal_opcode_le[] = { 0x14, 0x43, 0x45, 0x56 }; |
| |
| struct tic6x_unwind_cache |
| { |
| /* The frame's base, optionally used by the high-level debug info. */ |
| CORE_ADDR base; |
| |
| /* The previous frame's inner most stack address. Used as this |
| frame ID's stack_addr. */ |
| CORE_ADDR cfa; |
| |
| /* The address of the first instruction in this function */ |
| CORE_ADDR pc; |
| |
| /* Which register holds the return address for the frame. */ |
| int return_regnum; |
| |
| /* The offset of register saved on stack. If register is not saved, the |
| corresponding element is -1. */ |
| CORE_ADDR reg_saved[TIC6X_NUM_CORE_REGS]; |
| }; |
| |
| |
| /* Name of TI C6x core registers. */ |
| static const char *const tic6x_register_names[] = |
| { |
| "A0", "A1", "A2", "A3", /* 0 1 2 3 */ |
| "A4", "A5", "A6", "A7", /* 4 5 6 7 */ |
| "A8", "A9", "A10", "A11", /* 8 9 10 11 */ |
| "A12", "A13", "A14", "A15", /* 12 13 14 15 */ |
| "B0", "B1", "B2", "B3", /* 16 17 18 19 */ |
| "B4", "B5", "B6", "B7", /* 20 21 22 23 */ |
| "B8", "B9", "B10", "B11", /* 24 25 26 27 */ |
| "B12", "B13", "B14", "B15", /* 28 29 30 31 */ |
| "CSR", "PC", /* 32 33 */ |
| }; |
| |
| /* This array maps the arguments to the register number which passes argument |
| in function call according to C6000 ELF ABI. */ |
| static const int arg_regs[] = { 4, 20, 6, 22, 8, 24, 10, 26, 12, 28 }; |
| |
| /* This is the implementation of gdbarch method register_name. */ |
| |
| static const char * |
| tic6x_register_name (struct gdbarch *gdbarch, int regno) |
| { |
| if (tdesc_has_registers (gdbarch_target_desc (gdbarch))) |
| return tdesc_register_name (gdbarch, regno); |
| else if (regno >= ARRAY_SIZE (tic6x_register_names)) |
| return ""; |
| else |
| return tic6x_register_names[regno]; |
| } |
| |
| /* This is the implementation of gdbarch method register_type. */ |
| |
| static struct type * |
| tic6x_register_type (struct gdbarch *gdbarch, int regno) |
| { |
| |
| if (regno == TIC6X_PC_REGNUM) |
| return builtin_type (gdbarch)->builtin_func_ptr; |
| else |
| return builtin_type (gdbarch)->builtin_uint32; |
| } |
| |
| static void |
| tic6x_setup_default (struct tic6x_unwind_cache *cache) |
| { |
| int i; |
| |
| for (i = 0; i < TIC6X_NUM_CORE_REGS; i++) |
| cache->reg_saved[i] = -1; |
| } |
| |
| static unsigned long tic6x_fetch_instruction (struct gdbarch *, CORE_ADDR); |
| static int tic6x_register_number (int reg, int side, int crosspath); |
| |
| /* Do a full analysis of the prologue at START_PC and update CACHE accordingly. |
| Bail out early if CURRENT_PC is reached. Returns the address of the first |
| instruction after the prologue. */ |
| |
| static CORE_ADDR |
| tic6x_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc, |
| const CORE_ADDR current_pc, |
| struct tic6x_unwind_cache *cache, |
| const frame_info_ptr &this_frame) |
| { |
| unsigned int src_reg, base_reg, dst_reg; |
| int i; |
| CORE_ADDR pc = start_pc; |
| CORE_ADDR return_pc = start_pc; |
| int frame_base_offset_to_sp = 0; |
| /* Counter of non-stw instructions after first insn ` sub sp, xxx, sp'. */ |
| int non_stw_insn_counter = 0; |
| |
| if (start_pc >= current_pc) |
| return_pc = current_pc; |
| |
| cache->base = 0; |
| |
| /* The landmarks in prologue is one or two SUB instructions to SP. |
| Instructions on setting up dsbt are in the last part of prologue, if |
| needed. In maxim, prologue can be divided to three parts by two |
| `sub sp, xx, sp' insns. */ |
| |
| /* Step 1: Look for the 1st and 2nd insn `sub sp, xx, sp', in which, the |
| 2nd one is optional. */ |
| while (pc < current_pc) |
| { |
| unsigned long inst = tic6x_fetch_instruction (gdbarch, pc); |
| |
| if ((inst & 0x1ffc) == 0x1dc0 || (inst & 0x1ffc) == 0x1bc0 |
| || (inst & 0x0ffc) == 0x9c0) |
| { |
| /* SUBAW/SUBAH/SUB, and src1 is ucst 5. */ |
| unsigned int src2 = tic6x_register_number ((inst >> 18) & 0x1f, |
| INST_S_BIT (inst), 0); |
| unsigned int dst = tic6x_register_number ((inst >> 23) & 0x1f, |
| INST_S_BIT (inst), 0); |
| |
| if (src2 == TIC6X_SP_REGNUM && dst == TIC6X_SP_REGNUM) |
| { |
| /* Extract const from insn SUBAW/SUBAH/SUB, and translate it to |
| offset. The constant offset is decoded in bit 13-17 in all |
| these three kinds of instructions. */ |
| unsigned int ucst5 = (inst >> 13) & 0x1f; |
| |
| if ((inst & 0x1ffc) == 0x1dc0) /* SUBAW */ |
| frame_base_offset_to_sp += ucst5 << 2; |
| else if ((inst & 0x1ffc) == 0x1bc0) /* SUBAH */ |
| frame_base_offset_to_sp += ucst5 << 1; |
| else if ((inst & 0x0ffc) == 0x9c0) /* SUB */ |
| frame_base_offset_to_sp += ucst5; |
| else |
| gdb_assert_not_reached ("unexpected instruction"); |
| |
| return_pc = pc + 4; |
| } |
| } |
| else if ((inst & 0x174) == 0x74) /* stw SRC, *+b15(uconst) */ |
| { |
| /* The y bit determines which file base is read from. */ |
| base_reg = tic6x_register_number ((inst >> 18) & 0x1f, |
| (inst >> 7) & 1, 0); |
| |
| if (base_reg == TIC6X_SP_REGNUM) |
| { |
| src_reg = tic6x_register_number ((inst >> 23) & 0x1f, |
| INST_S_BIT (inst), 0); |
| |
| cache->reg_saved[src_reg] = ((inst >> 13) & 0x1f) << 2; |
| |
| return_pc = pc + 4; |
| } |
| non_stw_insn_counter = 0; |
| } |
| else |
| { |
| non_stw_insn_counter++; |
| /* Following instruction sequence may be emitted in prologue: |
| |
| <+0>: subah .D2 b15,28,b15 |
| <+4>: or .L2X 0,a4,b0 |
| <+8>: || stw .D2T2 b14,*+b15(56) |
| <+12>:[!b0] b .S1 0xe50e4c1c <sleep+220> |
| <+16>:|| stw .D2T1 a10,*+b15(48) |
| <+20>:stw .D2T2 b3,*+b15(52) |
| <+24>:stw .D2T1 a4,*+b15(40) |
| |
| we should look forward for next instruction instead of breaking loop |
| here. So far, we allow almost two sequential non-stw instructions |
| in prologue. */ |
| if (non_stw_insn_counter >= 2) |
| break; |
| } |
| |
| |
| pc += 4; |
| } |
| /* Step 2: Skip insn on setting up dsbt if it is. Usually, it looks like, |
| ldw .D2T2 *+b14(0),b14 */ |
| unsigned long inst = tic6x_fetch_instruction (gdbarch, pc); |
| /* The s bit determines which file dst will be loaded into, same effect as |
| other places. */ |
| dst_reg = tic6x_register_number ((inst >> 23) & 0x1f, (inst >> 1) & 1, 0); |
| /* The y bit (bit 7), instead of s bit, determines which file base be |
| used. */ |
| base_reg = tic6x_register_number ((inst >> 18) & 0x1f, (inst >> 7) & 1, 0); |
| |
| if ((inst & 0x164) == 0x64 /* ldw */ |
| && dst_reg == TIC6X_DP_REGNUM /* dst is B14 */ |
| && base_reg == TIC6X_DP_REGNUM) /* baseR is B14 */ |
| { |
| return_pc = pc + 4; |
| } |
| |
| if (this_frame) |
| { |
| cache->base = get_frame_register_unsigned (this_frame, TIC6X_SP_REGNUM); |
| |
| if (cache->reg_saved[TIC6X_FP_REGNUM] != -1) |
| { |
| /* If the FP now holds an offset from the CFA then this is a frame |
| which uses the frame pointer. */ |
| |
| cache->cfa = get_frame_register_unsigned (this_frame, |
| TIC6X_FP_REGNUM); |
| } |
| else |
| { |
| /* FP doesn't hold an offset from the CFA. If SP still holds an |
| offset from the CFA then we might be in a function which omits |
| the frame pointer. */ |
| |
| cache->cfa = cache->base + frame_base_offset_to_sp; |
| } |
| } |
| |
| /* Adjust all the saved registers such that they contain addresses |
| instead of offsets. */ |
| for (i = 0; i < TIC6X_NUM_CORE_REGS; i++) |
| if (cache->reg_saved[i] != -1) |
| cache->reg_saved[i] = cache->base + cache->reg_saved[i]; |
| |
| return return_pc; |
| } |
| |
| /* This is the implementation of gdbarch method skip_prologue. */ |
| |
| static CORE_ADDR |
| tic6x_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc) |
| { |
| CORE_ADDR func_addr; |
| struct tic6x_unwind_cache cache; |
| |
| /* See if we can determine the end of the prologue via the symbol table. |
| If so, then return either PC, or the PC after the prologue, whichever is |
| greater. */ |
| if (find_pc_partial_function (start_pc, NULL, &func_addr, NULL)) |
| { |
| CORE_ADDR post_prologue_pc |
| = skip_prologue_using_sal (gdbarch, func_addr); |
| if (post_prologue_pc != 0) |
| return std::max (start_pc, post_prologue_pc); |
| } |
| |
| /* Can't determine prologue from the symbol table, need to examine |
| instructions. */ |
| return tic6x_analyze_prologue (gdbarch, start_pc, (CORE_ADDR) -1, &cache, |
| NULL); |
| } |
| |
| /* Implement the breakpoint_kind_from_pc gdbarch method. */ |
| |
| static int |
| tic6x_breakpoint_kind_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr) |
| { |
| return 4; |
| } |
| |
| /* Implement the sw_breakpoint_from_kind gdbarch method. */ |
| |
| static const gdb_byte * |
| tic6x_sw_breakpoint_from_kind (struct gdbarch *gdbarch, int kind, int *size) |
| { |
| tic6x_gdbarch_tdep *tdep = gdbarch_tdep<tic6x_gdbarch_tdep> (gdbarch); |
| |
| *size = kind; |
| |
| if (tdep == NULL || tdep->breakpoint == NULL) |
| { |
| if (BFD_ENDIAN_BIG == gdbarch_byte_order_for_code (gdbarch)) |
| return tic6x_bkpt_illegal_opcode_be; |
| else |
| return tic6x_bkpt_illegal_opcode_le; |
| } |
| else |
| return tdep->breakpoint; |
| } |
| |
| static void |
| tic6x_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum, |
| struct dwarf2_frame_state_reg *reg, |
| const frame_info_ptr &this_frame) |
| { |
| /* Mark the PC as the destination for the return address. */ |
| if (regnum == gdbarch_pc_regnum (gdbarch)) |
| reg->how = DWARF2_FRAME_REG_RA; |
| |
| /* Mark the stack pointer as the call frame address. */ |
| else if (regnum == gdbarch_sp_regnum (gdbarch)) |
| reg->how = DWARF2_FRAME_REG_CFA; |
| |
| /* The above was taken from the default init_reg in dwarf2-frame.c |
| while the below is c6x specific. */ |
| |
| /* Callee save registers. The ABI designates A10-A15 and B10-B15 as |
| callee-save. */ |
| else if ((regnum >= 10 && regnum <= 15) || (regnum >= 26 && regnum <= 31)) |
| reg->how = DWARF2_FRAME_REG_SAME_VALUE; |
| else |
| /* All other registers are caller-save. */ |
| reg->how = DWARF2_FRAME_REG_UNDEFINED; |
| } |
| |
| /* This is the implementation of gdbarch method unwind_pc. */ |
| |
| static CORE_ADDR |
| tic6x_unwind_pc (struct gdbarch *gdbarch, const frame_info_ptr &next_frame) |
| { |
| gdb_byte buf[8]; |
| |
| frame_unwind_register (next_frame, TIC6X_PC_REGNUM, buf); |
| return extract_typed_address (buf, builtin_type (gdbarch)->builtin_func_ptr); |
| } |
| |
| /* Frame base handling. */ |
| |
| static struct tic6x_unwind_cache* |
| tic6x_frame_unwind_cache (const frame_info_ptr &this_frame, |
| void **this_prologue_cache) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (this_frame); |
| CORE_ADDR current_pc; |
| struct tic6x_unwind_cache *cache; |
| |
| if (*this_prologue_cache) |
| return (struct tic6x_unwind_cache *) *this_prologue_cache; |
| |
| cache = FRAME_OBSTACK_ZALLOC (struct tic6x_unwind_cache); |
| (*this_prologue_cache) = cache; |
| |
| cache->return_regnum = TIC6X_RA_REGNUM; |
| |
| tic6x_setup_default (cache); |
| |
| cache->pc = get_frame_func (this_frame); |
| current_pc = get_frame_pc (this_frame); |
| |
| /* Prologue analysis does the rest... */ |
| if (cache->pc != 0) |
| tic6x_analyze_prologue (gdbarch, cache->pc, current_pc, cache, this_frame); |
| |
| return cache; |
| } |
| |
| static void |
| tic6x_frame_this_id (const frame_info_ptr &this_frame, void **this_cache, |
| struct frame_id *this_id) |
| { |
| struct tic6x_unwind_cache *cache = |
| tic6x_frame_unwind_cache (this_frame, this_cache); |
| |
| /* This marks the outermost frame. */ |
| if (cache->base == 0) |
| return; |
| |
| (*this_id) = frame_id_build (cache->cfa, cache->pc); |
| } |
| |
| static struct value * |
| tic6x_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache, |
| int regnum) |
| { |
| struct tic6x_unwind_cache *cache = |
| tic6x_frame_unwind_cache (this_frame, this_cache); |
| |
| gdb_assert (regnum >= 0); |
| |
| /* The PC of the previous frame is stored in the RA register of |
| the current frame. Frob regnum so that we pull the value from |
| the correct place. */ |
| if (regnum == TIC6X_PC_REGNUM) |
| regnum = cache->return_regnum; |
| |
| if (regnum == TIC6X_SP_REGNUM && cache->cfa) |
| return frame_unwind_got_constant (this_frame, regnum, cache->cfa); |
| |
| /* If we've worked out where a register is stored then load it from |
| there. */ |
| if (regnum < TIC6X_NUM_CORE_REGS && cache->reg_saved[regnum] != -1) |
| return frame_unwind_got_memory (this_frame, regnum, |
| cache->reg_saved[regnum]); |
| |
| return frame_unwind_got_register (this_frame, regnum, regnum); |
| } |
| |
| static CORE_ADDR |
| tic6x_frame_base_address (const frame_info_ptr &this_frame, void **this_cache) |
| { |
| struct tic6x_unwind_cache *info |
| = tic6x_frame_unwind_cache (this_frame, this_cache); |
| return info->base; |
| } |
| |
| static const struct frame_unwind tic6x_frame_unwind = |
| { |
| "tic6x prologue", |
| NORMAL_FRAME, |
| default_frame_unwind_stop_reason, |
| tic6x_frame_this_id, |
| tic6x_frame_prev_register, |
| NULL, |
| default_frame_sniffer |
| }; |
| |
| static const struct frame_base tic6x_frame_base = |
| { |
| &tic6x_frame_unwind, |
| tic6x_frame_base_address, |
| tic6x_frame_base_address, |
| tic6x_frame_base_address |
| }; |
| |
| |
| static struct tic6x_unwind_cache * |
| tic6x_make_stub_cache (const frame_info_ptr &this_frame) |
| { |
| struct tic6x_unwind_cache *cache; |
| |
| cache = FRAME_OBSTACK_ZALLOC (struct tic6x_unwind_cache); |
| |
| cache->return_regnum = TIC6X_RA_REGNUM; |
| |
| tic6x_setup_default (cache); |
| |
| cache->cfa = get_frame_register_unsigned (this_frame, TIC6X_SP_REGNUM); |
| |
| return cache; |
| } |
| |
| static void |
| tic6x_stub_this_id (const frame_info_ptr &this_frame, void **this_cache, |
| struct frame_id *this_id) |
| { |
| struct tic6x_unwind_cache *cache; |
| |
| if (*this_cache == NULL) |
| *this_cache = tic6x_make_stub_cache (this_frame); |
| cache = (struct tic6x_unwind_cache *) *this_cache; |
| |
| *this_id = frame_id_build (cache->cfa, get_frame_pc (this_frame)); |
| } |
| |
| static int |
| tic6x_stub_unwind_sniffer (const struct frame_unwind *self, |
| const frame_info_ptr &this_frame, |
| void **this_prologue_cache) |
| { |
| CORE_ADDR addr_in_block; |
| |
| addr_in_block = get_frame_address_in_block (this_frame); |
| if (in_plt_section (addr_in_block)) |
| return 1; |
| |
| return 0; |
| } |
| |
| static const struct frame_unwind tic6x_stub_unwind = |
| { |
| "tic6x stub", |
| NORMAL_FRAME, |
| default_frame_unwind_stop_reason, |
| tic6x_stub_this_id, |
| tic6x_frame_prev_register, |
| NULL, |
| tic6x_stub_unwind_sniffer |
| }; |
| |
| /* Return the instruction on address PC. */ |
| |
| static unsigned long |
| tic6x_fetch_instruction (struct gdbarch *gdbarch, CORE_ADDR pc) |
| { |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| return read_memory_unsigned_integer (pc, TIC6X_OPCODE_SIZE, byte_order); |
| } |
| |
| /* Compute the condition of INST if it is a conditional instruction. Always |
| return 1 if INST is not a conditional instruction. */ |
| |
| static int |
| tic6x_condition_true (struct regcache *regcache, unsigned long inst) |
| { |
| int register_number; |
| int register_value; |
| static const int register_numbers[8] = { -1, 16, 17, 18, 1, 2, 0, -1 }; |
| |
| register_number = register_numbers[(inst >> 29) & 7]; |
| if (register_number == -1) |
| return 1; |
| |
| register_value = regcache_raw_get_signed (regcache, register_number); |
| if ((inst & 0x10000000) != 0) |
| return register_value == 0; |
| return register_value != 0; |
| } |
| |
| /* Get the register number by decoding raw bits REG, SIDE, and CROSSPATH in |
| instruction. */ |
| |
| static int |
| tic6x_register_number (int reg, int side, int crosspath) |
| { |
| int r = (reg & 15) | ((crosspath ^ side) << 4); |
| if ((reg & 16) != 0) /* A16 - A31, B16 - B31 */ |
| r += 37; |
| return r; |
| } |
| |
| static int |
| tic6x_extract_signed_field (int value, int low_bit, int bits) |
| { |
| int mask = (1 << bits) - 1; |
| int r = (value >> low_bit) & mask; |
| if ((r & (1 << (bits - 1))) != 0) |
| r -= mask + 1; |
| return r; |
| } |
| |
| /* Determine where to set a single step breakpoint. */ |
| |
| static CORE_ADDR |
| tic6x_get_next_pc (struct regcache *regcache, CORE_ADDR pc) |
| { |
| struct gdbarch *gdbarch = regcache->arch (); |
| unsigned long inst; |
| int register_number; |
| int last = 0; |
| |
| do |
| { |
| inst = tic6x_fetch_instruction (gdbarch, pc); |
| |
| last = !(inst & 1); |
| |
| if (inst == TIC6X_INST_SWE) |
| { |
| tic6x_gdbarch_tdep *tdep |
| = gdbarch_tdep<tic6x_gdbarch_tdep> (gdbarch); |
| |
| if (tdep->syscall_next_pc != NULL) |
| return tdep->syscall_next_pc (get_current_frame ()); |
| } |
| |
| if (tic6x_condition_true (regcache, inst)) |
| { |
| if ((inst & 0x0000007c) == 0x00000010) |
| { |
| /* B with displacement */ |
| pc &= ~(TIC6X_FETCH_PACKET_SIZE - 1); |
| pc += tic6x_extract_signed_field (inst, 7, 21) << 2; |
| break; |
| } |
| if ((inst & 0x0f83effc) == 0x00000360) |
| { |
| /* B with register */ |
| |
| register_number = tic6x_register_number ((inst >> 18) & 0x1f, |
| INST_S_BIT (inst), |
| INST_X_BIT (inst)); |
| pc = regcache_raw_get_unsigned (regcache, register_number); |
| break; |
| } |
| if ((inst & 0x00001ffc) == 0x00001020) |
| { |
| /* BDEC */ |
| register_number = tic6x_register_number ((inst >> 23) & 0x1f, |
| INST_S_BIT (inst), 0); |
| if (regcache_raw_get_signed (regcache, register_number) >= 0) |
| { |
| pc &= ~(TIC6X_FETCH_PACKET_SIZE - 1); |
| pc += tic6x_extract_signed_field (inst, 7, 10) << 2; |
| } |
| break; |
| } |
| if ((inst & 0x00001ffc) == 0x00000120) |
| { |
| /* BNOP with displacement */ |
| pc &= ~(TIC6X_FETCH_PACKET_SIZE - 1); |
| pc += tic6x_extract_signed_field (inst, 16, 12) << 2; |
| break; |
| } |
| if ((inst & 0x0f830ffe) == 0x00800362) |
| { |
| /* BNOP with register */ |
| register_number = tic6x_register_number ((inst >> 18) & 0x1f, |
| 1, INST_X_BIT (inst)); |
| pc = regcache_raw_get_unsigned (regcache, register_number); |
| break; |
| } |
| if ((inst & 0x00001ffc) == 0x00000020) |
| { |
| /* BPOS */ |
| register_number = tic6x_register_number ((inst >> 23) & 0x1f, |
| INST_S_BIT (inst), 0); |
| if (regcache_raw_get_signed (regcache, register_number) >= 0) |
| { |
| pc &= ~(TIC6X_FETCH_PACKET_SIZE - 1); |
| pc += tic6x_extract_signed_field (inst, 13, 10) << 2; |
| } |
| break; |
| } |
| if ((inst & 0xf000007c) == 0x10000010) |
| { |
| /* CALLP */ |
| pc &= ~(TIC6X_FETCH_PACKET_SIZE - 1); |
| pc += tic6x_extract_signed_field (inst, 7, 21) << 2; |
| break; |
| } |
| } |
| pc += TIC6X_OPCODE_SIZE; |
| } |
| while (!last); |
| return pc; |
| } |
| |
| /* This is the implementation of gdbarch method software_single_step. */ |
| |
| static std::vector<CORE_ADDR> |
| tic6x_software_single_step (struct regcache *regcache) |
| { |
| CORE_ADDR next_pc = tic6x_get_next_pc (regcache, regcache_read_pc (regcache)); |
| |
| return {next_pc}; |
| } |
| |
| /* This is the implementation of gdbarch method frame_align. */ |
| |
| static CORE_ADDR |
| tic6x_frame_align (struct gdbarch *gdbarch, CORE_ADDR addr) |
| { |
| return align_down (addr, 8); |
| } |
| |
| /* Given a return value in REGCACHE with a type VALTYPE, extract and copy its |
| value into VALBUF. */ |
| |
| static void |
| tic6x_extract_return_value (struct type *valtype, struct regcache *regcache, |
| enum bfd_endian byte_order, gdb_byte *valbuf) |
| { |
| int len = valtype->length (); |
| |
| /* pointer types are returned in register A4, |
| up to 32-bit types in A4 |
| up to 64-bit types in A5:A4 */ |
| if (len <= 4) |
| { |
| /* In big-endian, |
| - one-byte structure or union occupies the LSB of single even register. |
| - for two-byte structure or union, the first byte occupies byte 1 of |
| register and the second byte occupies byte 0. |
| so, we read the contents in VAL from the LSBs of register. */ |
| if (len < 3 && byte_order == BFD_ENDIAN_BIG) |
| regcache->cooked_read_part (TIC6X_A4_REGNUM, 4 - len, len, valbuf); |
| else |
| regcache->cooked_read (TIC6X_A4_REGNUM, valbuf); |
| } |
| else if (len <= 8) |
| { |
| /* For a 5-8 byte structure or union in big-endian, the first byte |
| occupies byte 3 (the MSB) of the upper (odd) register and the |
| remaining bytes fill the decreasingly significant bytes. 5-7 |
| byte structures or unions have padding in the LSBs of the |
| lower (even) register. */ |
| if (byte_order == BFD_ENDIAN_BIG) |
| { |
| regcache->cooked_read (TIC6X_A4_REGNUM, valbuf + 4); |
| regcache->cooked_read (TIC6X_A5_REGNUM, valbuf); |
| } |
| else |
| { |
| regcache->cooked_read (TIC6X_A4_REGNUM, valbuf); |
| regcache->cooked_read (TIC6X_A5_REGNUM, valbuf + 4); |
| } |
| } |
| } |
| |
| /* Write into appropriate registers a function return value |
| of type TYPE, given in virtual format. */ |
| |
| static void |
| tic6x_store_return_value (struct type *valtype, struct regcache *regcache, |
| enum bfd_endian byte_order, const gdb_byte *valbuf) |
| { |
| int len = valtype->length (); |
| |
| /* return values of up to 8 bytes are returned in A5:A4 */ |
| |
| if (len <= 4) |
| { |
| if (len < 3 && byte_order == BFD_ENDIAN_BIG) |
| regcache->cooked_write_part (TIC6X_A4_REGNUM, 4 - len, len, valbuf); |
| else |
| regcache->cooked_write (TIC6X_A4_REGNUM, valbuf); |
| } |
| else if (len <= 8) |
| { |
| if (byte_order == BFD_ENDIAN_BIG) |
| { |
| regcache->cooked_write (TIC6X_A4_REGNUM, valbuf + 4); |
| regcache->cooked_write (TIC6X_A5_REGNUM, valbuf); |
| } |
| else |
| { |
| regcache->cooked_write (TIC6X_A4_REGNUM, valbuf); |
| regcache->cooked_write (TIC6X_A5_REGNUM, valbuf + 4); |
| } |
| } |
| } |
| |
| /* This is the implementation of gdbarch method return_value. */ |
| |
| static enum return_value_convention |
| tic6x_return_value (struct gdbarch *gdbarch, struct value *function, |
| struct type *type, struct regcache *regcache, |
| gdb_byte *readbuf, const gdb_byte *writebuf) |
| { |
| /* In C++, when function returns an object, even its size is small |
| enough, it stii has to be passed via reference, pointed by register |
| A3. */ |
| if (current_language->la_language == language_cplus) |
| { |
| if (type != NULL) |
| { |
| type = check_typedef (type); |
| if (!(language_pass_by_reference (type).trivially_copyable)) |
| return RETURN_VALUE_STRUCT_CONVENTION; |
| } |
| } |
| |
| if (type->length () > 8) |
| return RETURN_VALUE_STRUCT_CONVENTION; |
| |
| if (readbuf) |
| tic6x_extract_return_value (type, regcache, |
| gdbarch_byte_order (gdbarch), readbuf); |
| if (writebuf) |
| tic6x_store_return_value (type, regcache, |
| gdbarch_byte_order (gdbarch), writebuf); |
| |
| return RETURN_VALUE_REGISTER_CONVENTION; |
| } |
| |
| /* Get the alignment requirement of TYPE. */ |
| |
| static int |
| tic6x_arg_type_alignment (struct type *type) |
| { |
| int len = check_typedef (type)->length (); |
| enum type_code typecode = check_typedef (type)->code (); |
| |
| if (typecode == TYPE_CODE_STRUCT || typecode == TYPE_CODE_UNION) |
| { |
| /* The stack alignment of a structure (and union) passed by value is the |
| smallest power of two greater than or equal to its size. |
| This cannot exceed 8 bytes, which is the largest allowable size for |
| a structure passed by value. */ |
| |
| if (len <= 2) |
| return len; |
| else if (len <= 4) |
| return 4; |
| else if (len <= 8) |
| return 8; |
| else |
| gdb_assert_not_reached ("unexpected length of data"); |
| } |
| else |
| { |
| if (len <= 4) |
| return 4; |
| else if (len == 8) |
| { |
| if (typecode == TYPE_CODE_COMPLEX) |
| return 4; |
| else |
| return 8; |
| } |
| else if (len == 16) |
| { |
| if (typecode == TYPE_CODE_COMPLEX) |
| return 8; |
| else |
| return 16; |
| } |
| else |
| internal_error (_("unexpected length %d of type"), |
| len); |
| } |
| } |
| |
| /* This is the implementation of gdbarch method push_dummy_call. */ |
| |
| static CORE_ADDR |
| tic6x_push_dummy_call (struct gdbarch *gdbarch, struct value *function, |
| struct regcache *regcache, CORE_ADDR bp_addr, |
| int nargs, struct value **args, CORE_ADDR sp, |
| function_call_return_method return_method, |
| CORE_ADDR struct_addr) |
| { |
| int argreg = 0; |
| int argnum; |
| int stack_offset = 4; |
| int references_offset = 4; |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| struct type *func_type = function->type (); |
| /* The first arg passed on stack. Mostly the first 10 args are passed by |
| registers. */ |
| int first_arg_on_stack = 10; |
| |
| /* Set the return address register to point to the entry point of |
| the program, where a breakpoint lies in wait. */ |
| regcache_cooked_write_unsigned (regcache, TIC6X_RA_REGNUM, bp_addr); |
| |
| /* The caller must pass an argument in A3 containing a destination address |
| for the returned value. The callee returns the object by copying it to |
| the address in A3. */ |
| if (return_method == return_method_struct) |
| regcache_cooked_write_unsigned (regcache, 3, struct_addr); |
| |
| /* Determine the type of this function. */ |
| func_type = check_typedef (func_type); |
| if (func_type->code () == TYPE_CODE_PTR) |
| func_type = check_typedef (func_type->target_type ()); |
| |
| gdb_assert (func_type->code () == TYPE_CODE_FUNC |
| || func_type->code () == TYPE_CODE_METHOD); |
| |
| /* For a variadic C function, the last explicitly declared argument and all |
| remaining arguments are passed on the stack. */ |
| if (func_type->has_varargs ()) |
| first_arg_on_stack = func_type->num_fields () - 1; |
| |
| /* Now make space on the stack for the args. */ |
| for (argnum = 0; argnum < nargs; argnum++) |
| { |
| int len = align_up (args[argnum]->type ()->length (), 4); |
| if (argnum >= 10 - argreg) |
| references_offset += len; |
| stack_offset += len; |
| } |
| sp -= stack_offset; |
| /* SP should be 8-byte aligned, see C6000 ABI section 4.4.1 |
| Stack Alignment. */ |
| sp = align_down (sp, 8); |
| stack_offset = 4; |
| |
| /* Now load as many as possible of the first arguments into |
| registers, and push the rest onto the stack. Loop through args |
| from first to last. */ |
| for (argnum = 0; argnum < nargs; argnum++) |
| { |
| const gdb_byte *val; |
| struct value *arg = args[argnum]; |
| struct type *arg_type = check_typedef (arg->type ()); |
| int len = arg_type->length (); |
| enum type_code typecode = arg_type->code (); |
| |
| val = arg->contents ().data (); |
| |
| /* Copy the argument to general registers or the stack in |
| register-sized pieces. */ |
| if (argreg < first_arg_on_stack) |
| { |
| if (len <= 4) |
| { |
| if (typecode == TYPE_CODE_STRUCT || typecode == TYPE_CODE_UNION) |
| { |
| /* In big-endian, |
| - one-byte structure or union occupies the LSB of single |
| even register. |
| - for two-byte structure or union, the first byte |
| occupies byte 1 of register and the second byte occupies |
| byte 0. |
| so, we write the contents in VAL to the lsp of |
| register. */ |
| if (len < 3 && byte_order == BFD_ENDIAN_BIG) |
| regcache->cooked_write_part (arg_regs[argreg], 4 - len, len, |
| val); |
| else |
| regcache->cooked_write (arg_regs[argreg], val); |
| } |
| else |
| { |
| /* The argument is being passed by value in a single |
| register. */ |
| CORE_ADDR regval = extract_unsigned_integer (val, len, |
| byte_order); |
| |
| regcache_cooked_write_unsigned (regcache, arg_regs[argreg], |
| regval); |
| } |
| } |
| else |
| { |
| if (len <= 8) |
| { |
| if (typecode == TYPE_CODE_STRUCT |
| || typecode == TYPE_CODE_UNION) |
| { |
| /* For a 5-8 byte structure or union in big-endian, the |
| first byte occupies byte 3 (the MSB) of the upper (odd) |
| register and the remaining bytes fill the decreasingly |
| significant bytes. 5-7 byte structures or unions have |
| padding in the LSBs of the lower (even) register. */ |
| if (byte_order == BFD_ENDIAN_BIG) |
| { |
| regcache->cooked_write (arg_regs[argreg] + 1, val); |
| regcache->cooked_write_part (arg_regs[argreg], 0, |
| len - 4, val + 4); |
| } |
| else |
| { |
| regcache->cooked_write (arg_regs[argreg], val); |
| regcache->cooked_write_part (arg_regs[argreg] + 1, 0, |
| len - 4, val + 4); |
| } |
| } |
| else |
| { |
| /* The argument is being passed by value in a pair of |
| registers. */ |
| ULONGEST regval = extract_unsigned_integer (val, len, |
| byte_order); |
| |
| regcache_cooked_write_unsigned (regcache, |
| arg_regs[argreg], |
| regval); |
| regcache_cooked_write_unsigned (regcache, |
| arg_regs[argreg] + 1, |
| regval >> 32); |
| } |
| } |
| else |
| { |
| /* The argument is being passed by reference in a single |
| register. */ |
| CORE_ADDR addr; |
| |
| /* It is not necessary to adjust REFERENCES_OFFSET to |
| 8-byte aligned in some cases, in which 4-byte alignment |
| is sufficient. For simplicity, we adjust |
| REFERENCES_OFFSET to 8-byte aligned. */ |
| references_offset = align_up (references_offset, 8); |
| |
| addr = sp + references_offset; |
| write_memory (addr, val, len); |
| references_offset += align_up (len, 4); |
| regcache_cooked_write_unsigned (regcache, arg_regs[argreg], |
| addr); |
| } |
| } |
| argreg++; |
| } |
| else |
| { |
| /* The argument is being passed on the stack. */ |
| CORE_ADDR addr; |
| |
| /* There are six different cases of alignment, and these rules can |
| be found in tic6x_arg_type_alignment: |
| |
| 1) 4-byte aligned if size is less than or equal to 4 byte, such |
| as short, int, struct, union etc. |
| 2) 8-byte aligned if size is less than or equal to 8-byte, such |
| as double, long long, |
| 3) 4-byte aligned if it is of type _Complex float, even its size |
| is 8-byte. |
| 4) 8-byte aligned if it is of type _Complex double or _Complex |
| long double, even its size is 16-byte. Because, the address of |
| variable is passed as reference. |
| 5) struct and union larger than 8-byte are passed by reference, so |
| it is 4-byte aligned. |
| 6) struct and union of size between 4 byte and 8 byte varies. |
| alignment of struct variable is the alignment of its first field, |
| while alignment of union variable is the max of all its fields' |
| alignment. */ |
| |
| if (len <= 4) |
| ; /* Default is 4-byte aligned. Nothing to be done. */ |
| else if (len <= 8) |
| stack_offset = align_up (stack_offset, |
| tic6x_arg_type_alignment (arg_type)); |
| else if (len == 16) |
| { |
| /* _Complex double or _Complex long double */ |
| if (typecode == TYPE_CODE_COMPLEX) |
| { |
| /* The argument is being passed by reference on stack. */ |
| references_offset = align_up (references_offset, 8); |
| |
| addr = sp + references_offset; |
| /* Store variable on stack. */ |
| write_memory (addr, val, len); |
| |
| references_offset += align_up (len, 4); |
| |
| /* Pass the address of variable on stack as reference. */ |
| store_unsigned_integer ((gdb_byte *) val, 4, byte_order, |
| addr); |
| len = 4; |
| |
| } |
| else |
| internal_error (_("unexpected type %d of arg %d"), |
| typecode, argnum); |
| } |
| else |
| internal_error (_("unexpected length %d of arg %d"), len, argnum); |
| |
| addr = sp + stack_offset; |
| write_memory (addr, val, len); |
| stack_offset += align_up (len, 4); |
| } |
| } |
| |
| regcache_cooked_write_signed (regcache, TIC6X_SP_REGNUM, sp); |
| |
| /* Return adjusted stack pointer. */ |
| return sp; |
| } |
| |
| /* This is the implementation of gdbarch method stack_frame_destroyed_p. */ |
| |
| static int |
| tic6x_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc) |
| { |
| unsigned long inst = tic6x_fetch_instruction (gdbarch, pc); |
| /* Normally, the epilogue is composed by instruction `b .S2 b3'. */ |
| if ((inst & 0x0f83effc) == 0x360) |
| { |
| unsigned int src2 = tic6x_register_number ((inst >> 18) & 0x1f, |
| INST_S_BIT (inst), |
| INST_X_BIT (inst)); |
| if (src2 == TIC6X_RA_REGNUM) |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* This is the implementation of gdbarch method get_longjmp_target. */ |
| |
| static int |
| tic6x_get_longjmp_target (const frame_info_ptr &frame, CORE_ADDR *pc) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| CORE_ADDR jb_addr; |
| gdb_byte buf[4]; |
| |
| /* JMP_BUF is passed by reference in A4. */ |
| jb_addr = get_frame_register_unsigned (frame, 4); |
| |
| /* JMP_BUF contains 13 elements of type int, and return address is stored |
| in the last slot. */ |
| if (target_read_memory (jb_addr + 12 * 4, buf, 4)) |
| return 0; |
| |
| *pc = extract_unsigned_integer (buf, 4, byte_order); |
| |
| return 1; |
| } |
| |
| /* This is the implementation of gdbarch method |
| return_in_first_hidden_param_p. */ |
| |
| static int |
| tic6x_return_in_first_hidden_param_p (struct gdbarch *gdbarch, |
| struct type *type) |
| { |
| return 0; |
| } |
| |
| static struct gdbarch * |
| tic6x_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) |
| { |
| tdesc_arch_data_up tdesc_data; |
| const struct target_desc *tdesc = info.target_desc; |
| int has_gp = 0; |
| |
| /* Check any target description for validity. */ |
| if (tdesc_has_registers (tdesc)) |
| { |
| const struct tdesc_feature *feature; |
| int valid_p, i; |
| |
| feature = tdesc_find_feature (tdesc, "org.gnu.gdb.tic6x.core"); |
| |
| if (feature == NULL) |
| return NULL; |
| |
| tdesc_data = tdesc_data_alloc (); |
| |
| valid_p = 1; |
| for (i = 0; i < 32; i++) /* A0 - A15, B0 - B15 */ |
| valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), i, |
| tic6x_register_names[i]); |
| |
| /* CSR */ |
| valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), i++, |
| tic6x_register_names[TIC6X_CSR_REGNUM]); |
| valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), i++, |
| tic6x_register_names[TIC6X_PC_REGNUM]); |
| |
| if (!valid_p) |
| return NULL; |
| |
| feature = tdesc_find_feature (tdesc, "org.gnu.gdb.tic6x.gp"); |
| if (feature) |
| { |
| int j = 0; |
| static const char *const gp[] = |
| { |
| "A16", "A17", "A18", "A19", "A20", "A21", "A22", "A23", |
| "A24", "A25", "A26", "A27", "A28", "A29", "A30", "A31", |
| "B16", "B17", "B18", "B19", "B20", "B21", "B22", "B23", |
| "B24", "B25", "B26", "B27", "B28", "B29", "B30", "B31", |
| }; |
| |
| has_gp = 1; |
| valid_p = 1; |
| for (j = 0; j < 32; j++) /* A16 - A31, B16 - B31 */ |
| valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), |
| i++, gp[j]); |
| |
| if (!valid_p) |
| return NULL; |
| } |
| |
| feature = tdesc_find_feature (tdesc, "org.gnu.gdb.tic6x.c6xp"); |
| if (feature) |
| { |
| valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), |
| i++, "TSR"); |
| valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), |
| i++, "ILC"); |
| valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), |
| i++, "RILC"); |
| |
| if (!valid_p) |
| return NULL; |
| } |
| |
| } |
| |
| /* Find a candidate among extant architectures. */ |
| for (arches = gdbarch_list_lookup_by_info (arches, &info); |
| arches != NULL; |
| arches = gdbarch_list_lookup_by_info (arches->next, &info)) |
| { |
| tic6x_gdbarch_tdep *tdep |
| = gdbarch_tdep<tic6x_gdbarch_tdep> (arches->gdbarch); |
| |
| if (has_gp != tdep->has_gp) |
| continue; |
| |
| if (tdep && tdep->breakpoint) |
| return arches->gdbarch; |
| } |
| |
| gdbarch *gdbarch |
| = gdbarch_alloc (&info, gdbarch_tdep_up (new tic6x_gdbarch_tdep)); |
| tic6x_gdbarch_tdep *tdep = gdbarch_tdep<tic6x_gdbarch_tdep> (gdbarch); |
| |
| tdep->has_gp = has_gp; |
| |
| /* Data type sizes. */ |
| set_gdbarch_ptr_bit (gdbarch, 32); |
| set_gdbarch_addr_bit (gdbarch, 32); |
| set_gdbarch_short_bit (gdbarch, 16); |
| set_gdbarch_int_bit (gdbarch, 32); |
| set_gdbarch_long_bit (gdbarch, 32); |
| set_gdbarch_long_long_bit (gdbarch, 64); |
| set_gdbarch_float_bit (gdbarch, 32); |
| set_gdbarch_double_bit (gdbarch, 64); |
| |
| set_gdbarch_float_format (gdbarch, floatformats_ieee_single); |
| set_gdbarch_double_format (gdbarch, floatformats_ieee_double); |
| |
| /* The register set. */ |
| set_gdbarch_num_regs (gdbarch, TIC6X_NUM_REGS); |
| set_gdbarch_sp_regnum (gdbarch, TIC6X_SP_REGNUM); |
| set_gdbarch_pc_regnum (gdbarch, TIC6X_PC_REGNUM); |
| |
| set_gdbarch_register_name (gdbarch, tic6x_register_name); |
| set_gdbarch_register_type (gdbarch, tic6x_register_type); |
| |
| set_gdbarch_inner_than (gdbarch, core_addr_lessthan); |
| |
| set_gdbarch_skip_prologue (gdbarch, tic6x_skip_prologue); |
| set_gdbarch_breakpoint_kind_from_pc (gdbarch, |
| tic6x_breakpoint_kind_from_pc); |
| set_gdbarch_sw_breakpoint_from_kind (gdbarch, |
| tic6x_sw_breakpoint_from_kind); |
| |
| set_gdbarch_unwind_pc (gdbarch, tic6x_unwind_pc); |
| |
| /* Unwinding. */ |
| dwarf2_append_unwinders (gdbarch); |
| |
| frame_unwind_append_unwinder (gdbarch, &tic6x_stub_unwind); |
| frame_unwind_append_unwinder (gdbarch, &tic6x_frame_unwind); |
| frame_base_set_default (gdbarch, &tic6x_frame_base); |
| |
| dwarf2_frame_set_init_reg (gdbarch, tic6x_dwarf2_frame_init_reg); |
| |
| /* Single stepping. */ |
| set_gdbarch_software_single_step (gdbarch, tic6x_software_single_step); |
| |
| /* Call dummy code. */ |
| set_gdbarch_frame_align (gdbarch, tic6x_frame_align); |
| |
| set_gdbarch_return_value (gdbarch, tic6x_return_value); |
| |
| /* Enable inferior call support. */ |
| set_gdbarch_push_dummy_call (gdbarch, tic6x_push_dummy_call); |
| |
| set_gdbarch_get_longjmp_target (gdbarch, tic6x_get_longjmp_target); |
| |
| set_gdbarch_stack_frame_destroyed_p (gdbarch, tic6x_stack_frame_destroyed_p); |
| |
| set_gdbarch_return_in_first_hidden_param_p (gdbarch, |
| tic6x_return_in_first_hidden_param_p); |
| |
| /* Hook in ABI-specific overrides, if they have been registered. */ |
| gdbarch_init_osabi (info, gdbarch); |
| |
| if (tdesc_data != nullptr) |
| tdesc_use_registers (gdbarch, tdesc, std::move (tdesc_data)); |
| |
| return gdbarch; |
| } |
| |
| void _initialize_tic6x_tdep (); |
| void |
| _initialize_tic6x_tdep () |
| { |
| gdbarch_register (bfd_arch_tic6x, tic6x_gdbarch_init); |
| } |