diff options
Diffstat (limited to 'gdb/tic6x-tdep.c')
-rw-r--r-- | gdb/tic6x-tdep.c | 1386 |
1 files changed, 1386 insertions, 0 deletions
diff --git a/gdb/tic6x-tdep.c b/gdb/tic6x-tdep.c new file mode 100644 index 0000000..eedb5e9 --- /dev/null +++ b/gdb/tic6x-tdep.c @@ -0,0 +1,1386 @@ +/* Target dependent code for GDB on TI C6x systems. + + Copyright (C) 2010, 2011. + 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 "defs.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 "gdbcmd.h" +#include "target.h" +#include "dis-asm.h" +#include "regcache.h" +#include "value.h" +#include "symfile.h" +#include "arch-utils.h" +#include "floatformat.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 "gdb_assert.h" +#include "osabi.h" +#include "tic6x-tdep.h" +#include "language.h" +#include "target-descriptions.h" + +#include "features/tic6x-c64xp.c" +#include "features/tic6x-c64x.c" +#include "features/tic6x-c62x.c" + +#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) + +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 (regno < 0) + return NULL; + + 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. */ + +CORE_ADDR +tic6x_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc, + const CORE_ADDR current_pc, + struct tic6x_unwind_cache *cache, + struct frame_info *this_frame) +{ + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + unsigned long inst; + 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) + { + int offset = 0; + + 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 */ + 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. */ + +CORE_ADDR +tic6x_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc) +{ + CORE_ADDR limit_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 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); +} + +/* This is the implementation of gdbarch method breakpiont_from_pc. */ + +const unsigned char* +tic6x_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *bp_addr, + int *bp_size) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + *bp_size = 4; + + 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; +} + +/* This is the implementation of gdbarch method print_insn. */ + +static int +tic6x_print_insn (bfd_vma memaddr, disassemble_info *info) +{ + return print_insn_tic6x (memaddr, info); +} + +static void +tic6x_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum, + struct dwarf2_frame_state_reg *reg, + struct frame_info *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, struct frame_info *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); +} + +/* This is the implementation of gdbarch method unwind_sp. */ + +static CORE_ADDR +tic6x_unwind_sp (struct gdbarch *gdbarch, struct frame_info *this_frame) +{ + return frame_unwind_register_unsigned (this_frame, TIC6X_SP_REGNUM); +} + + +/* Frame base handling. */ + +struct tic6x_unwind_cache* +tic6x_frame_unwind_cache (struct frame_info *this_frame, + void **this_prologue_cache) +{ + struct gdbarch *gdbarch = get_frame_arch (this_frame); + CORE_ADDR current_pc; + struct tic6x_unwind_cache *cache; + int i; + + if (*this_prologue_cache) + return *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 (struct frame_info *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 (struct frame_info *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 (struct frame_info *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 = +{ + 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 (struct frame_info *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 (struct frame_info *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 = *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, + struct frame_info *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, NULL)) + return 1; + + return 0; +} + +static const struct frame_unwind tic6x_stub_unwind = +{ + 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 frame_info *frame, 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 = get_frame_register_signed (frame, 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 frame_info *frame, CORE_ADDR pc) +{ + struct gdbarch *gdbarch = get_frame_arch (frame); + unsigned long inst; + int offset; + int register_number; + int last = 0; + + do + { + inst = tic6x_fetch_instruction (gdbarch, pc); + + last = !(inst & 1); + + if (inst == TIC6X_INST_SWE) + { + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + if (tdep->syscall_next_pc != NULL) + return tdep->syscall_next_pc (frame); + } + + if (tic6x_condition_true (frame, 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 = get_frame_register_unsigned (frame, register_number); + break; + } + if ((inst & 0x00001ffc) == 0x00001020) + { + /* BDEC */ + register_number = tic6x_register_number ((inst >> 23) & 0x1f, + INST_S_BIT (inst), 0); + if (get_frame_register_signed (frame, 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 = get_frame_register_unsigned (frame, register_number); + break; + } + if ((inst & 0x00001ffc) == 0x00000020) + { + /* BPOS */ + register_number = tic6x_register_number ((inst >> 23) & 0x1f, + INST_S_BIT (inst), 0); + if (get_frame_register_signed (frame, 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. */ + +int +tic6x_software_single_step (struct frame_info *frame) +{ + struct gdbarch *gdbarch = get_frame_arch (frame); + struct address_space *aspace = get_frame_address_space (frame); + CORE_ADDR next_pc = tic6x_get_next_pc (frame, get_frame_pc (frame)); + + insert_single_step_breakpoint (gdbarch, aspace, next_pc); + + return 1; +} + +/* 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); +} + +/* This is the implementation of gdbarch method register_to_value. */ + +static int +tic6x_register_to_value (struct frame_info *frame, int regnum, + struct type *type, gdb_byte * to, + int *optimizedp, int *unavailablep) +{ + get_frame_register (frame, regnum, (char *) to); + *optimizedp = *unavailablep = 0; + return 1; +} + +/* This is the implementation of gdbarch method value_to_register. */ + +static void +tic6x_value_to_register (struct frame_info *frame, int regnum, + struct type *type, const gdb_byte *from) +{ + put_frame_register (frame, regnum, from); +} + +/* 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 = TYPE_LENGTH (valtype); + + /* 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 (regcache, TIC6X_A4_REGNUM, 4 - len, len, + valbuf); + else + regcache_cooked_read (regcache, 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 (regcache, TIC6X_A4_REGNUM, valbuf + 4); + regcache_cooked_read (regcache, TIC6X_A5_REGNUM, valbuf); + } + else + { + regcache_cooked_read (regcache, TIC6X_A4_REGNUM, valbuf); + regcache_cooked_read (regcache, 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 = TYPE_LENGTH (valtype); + + /* 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 (regcache, TIC6X_A4_REGNUM, 4 - len, len, + valbuf); + else + regcache_cooked_write (regcache, TIC6X_A4_REGNUM, valbuf); + } + else if (len <= 8) + { + if (byte_order == BFD_ENDIAN_BIG) + { + regcache_cooked_write (regcache, TIC6X_A4_REGNUM, valbuf + 4); + regcache_cooked_write (regcache, TIC6X_A5_REGNUM, valbuf); + } + else + { + regcache_cooked_write (regcache, TIC6X_A4_REGNUM, valbuf); + regcache_cooked_write (regcache, 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 type *func_type, + struct type *type, struct regcache *regcache, + gdb_byte *readbuf, const gdb_byte *writebuf) +{ + if (TYPE_LENGTH (type) > 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; +} + +/* This is the implementation of gdbarch method dummy_id. */ + +static struct frame_id +tic6x_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame) +{ + return frame_id_build + (get_frame_register_unsigned (this_frame, TIC6X_SP_REGNUM), + get_frame_pc (this_frame)); +} + +/* Get the alignment requirement of TYPE. */ + +static int +tic6x_arg_type_alignment (struct type *type) +{ + int len = TYPE_LENGTH (check_typedef (type)); + enum type_code typecode = TYPE_CODE (check_typedef (type)); + + 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 (__FILE__, __LINE__, _("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, + int struct_return, CORE_ADDR struct_addr) +{ + int argreg = 0; + int argnum; + int len = 0; + int stack_offset = 4; + int references_offset = 4; + CORE_ADDR func_addr = find_function_addr (function, NULL); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + struct type *func_type = value_type (function); + /* The first arg passed on stack. Mostly the first 10 args are passed by + registers. */ + int first_arg_on_stack = 10; + /* If this inf-call is a cpp method call, and return value is passed by + reference, this flag is set to 1, otherwise set to 0. We need this flag + because computation of the return location in + infcall.c:call_function_by_hand is wrong for C6000 ELF ABI. In + call_function_by_hand, the language is considered first, and then + target ABI is considered. If language_pass_by_reference returns true, + the return location is passed as the first parameter to the function, + which is conflict with C6000 ELF ABI. If this flag is true, we should + adjust args and return locations accordingly to comply with C6000 ELF + ABI. */ + int cplus_return_struct_by_reference = 0; + + if (current_language->la_language == language_cplus) + { + struct type *values_type; + + find_function_addr (function, &values_type); + + if (values_type) + { + CHECK_TYPEDEF (values_type); + if (language_pass_by_reference (values_type)) + cplus_return_struct_by_reference = 1; + } + + } + /* 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 (struct_return) + regcache_cooked_write_unsigned (regcache, 3, struct_addr); + else if (cplus_return_struct_by_reference) + /* When cplus_return_struct_by_reference is 1, means local variable + lang_struct_return in call_function_by_hand is 1, so struct is + returned by reference, even STRUCT_RETURN is 0. Note that STRUCT_ADDR + is still valid in this case. */ + regcache_cooked_write_unsigned (regcache, 3, struct_addr); + + /* Determine the type of this function. */ + func_type = check_typedef (func_type); + if (TYPE_CODE (func_type) == TYPE_CODE_PTR) + func_type = check_typedef (TYPE_TARGET_TYPE (func_type)); + + gdb_assert (TYPE_CODE (func_type) == TYPE_CODE_FUNC + || TYPE_CODE (func_type) == TYPE_CODE_METHOD); + + /* For a variadic C function, the last explicitly declared argument and all + remaining arguments are passed on the stack. */ + if (TYPE_VARARGS (func_type)) + first_arg_on_stack = TYPE_NFIELDS (func_type) - 1; + + /* Now make space on the stack for the args. If + cplus_return_struct_by_reference is 1, means GDB pass an extra parameter + in ARGS, which is useless here, skip it. */ + for (argnum = cplus_return_struct_by_reference; argnum < nargs; argnum++) + { + int len = align_up (TYPE_LENGTH (value_type (args[argnum])), 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 = cplus_return_struct_by_reference; argnum < nargs; argnum++) + { + const gdb_byte *val; + struct value *arg = args[argnum]; + struct type *arg_type = check_typedef (value_type (arg)); + int len = TYPE_LENGTH (arg_type); + enum type_code typecode = TYPE_CODE (arg_type); + + val = value_contents (arg); + + /* 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 (regcache, arg_regs[argreg], + 4 - len, len, val); + else + regcache_cooked_write (regcache, 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 (regcache, + arg_regs[argreg] + 1, val); + regcache_cooked_write_part (regcache, + arg_regs[argreg], 0, + len - 4, val + 4); + } + else + { + regcache_cooked_write (regcache, arg_regs[argreg], + val); + regcache_cooked_write_part (regcache, + 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. */ + CORE_ADDR addr; + 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 (__FILE__, __LINE__, + _("unexpected type %d of arg %d"), + typecode, argnum); + } + else + internal_error (__FILE__, __LINE__, + _("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 in_function_epilogue_p. */ + +static int +tic6x_in_function_epilogue_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 (struct frame_info *frame, CORE_ADDR *pc) +{ + struct gdbarch *gdbarch = get_frame_arch (frame); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + CORE_ADDR jb_addr; + char 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; +} + +static struct gdbarch * +tic6x_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) +{ + struct gdbarch *gdbarch; + struct gdbarch_tdep *tdep; + struct tdesc_arch_data *tdesc_data = NULL; + 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, i, + tic6x_register_names[i]); + + /* CSR */ + valid_p &= tdesc_numbered_register (feature, tdesc_data, i++, + tic6x_register_names[TIC6X_CSR_REGNUM]); + valid_p &= tdesc_numbered_register (feature, tdesc_data, i++, + tic6x_register_names[TIC6X_PC_REGNUM]); + + if (!valid_p) + { + tdesc_data_cleanup (tdesc_data); + 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, i++, + gp[j]); + + if (!valid_p) + { + tdesc_data_cleanup (tdesc_data); + return NULL; + } + } + + feature = tdesc_find_feature (tdesc, "org.gnu.gdb.tic6x.c6xp"); + if (feature) + { + valid_p &= tdesc_numbered_register (feature, tdesc_data, i++, "TSR"); + valid_p &= tdesc_numbered_register (feature, tdesc_data, i++, "ILC"); + valid_p &= tdesc_numbered_register (feature, tdesc_data, i++, "RILC"); + + if (!valid_p) + { + tdesc_data_cleanup (tdesc_data); + 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)) + { + tdep = gdbarch_tdep (arches->gdbarch); + + if (has_gp != tdep->has_gp) + continue; + + if (tdep && tdep->breakpoint) + return arches->gdbarch; + } + + tdep = xcalloc (1, sizeof (struct gdbarch_tdep)); + + tdep->has_gp = has_gp; + gdbarch = gdbarch_alloc (&info, tdep); + + /* 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_from_pc (gdbarch, tic6x_breakpoint_from_pc); + + set_gdbarch_unwind_pc (gdbarch, tic6x_unwind_pc); + set_gdbarch_unwind_sp (gdbarch, tic6x_unwind_sp); + + /* Unwinding. */ + dwarf2_append_unwinders (gdbarch); + + frame_unwind_append_unwinder (gdbarch, &tic6x_stub_unwind); + frame_unwind_append_unwinder (gdbarch, &tic6x_frame_unwind); + + dwarf2_frame_set_init_reg (gdbarch, tic6x_dwarf2_frame_init_reg); + + /* Single stepping. */ + set_gdbarch_software_single_step (gdbarch, tic6x_software_single_step); + + set_gdbarch_print_insn (gdbarch, tic6x_print_insn); + + /* Call dummy code. */ + set_gdbarch_frame_align (gdbarch, tic6x_frame_align); + + set_gdbarch_register_to_value (gdbarch, tic6x_register_to_value); + set_gdbarch_value_to_register (gdbarch, tic6x_value_to_register); + + set_gdbarch_return_value (gdbarch, tic6x_return_value); + + set_gdbarch_dummy_id (gdbarch, tic6x_dummy_id); + + /* 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_in_function_epilogue_p (gdbarch, tic6x_in_function_epilogue_p); + + /* Hook in ABI-specific overrides, if they have been registered. */ + gdbarch_init_osabi (info, gdbarch); + + if (tdesc_data) + tdesc_use_registers (gdbarch, tdesc, tdesc_data); + + return gdbarch; +} + +void +_initialize_tic6x_tdep (void) +{ + register_gdbarch_init (bfd_arch_tic6x, tic6x_gdbarch_init); + + initialize_tdesc_tic6x_c64xp (); + initialize_tdesc_tic6x_c64x (); + initialize_tdesc_tic6x_c62x (); +} |