* [RFA 5/8] New port: TI C6x: gdb port
@ 2011-07-20 2:10 Yao Qi
2011-07-25 11:32 ` Yao Qi
2011-08-04 12:34 ` Pedro Alves
0 siblings, 2 replies; 16+ messages in thread
From: Yao Qi @ 2011-07-20 2:10 UTC (permalink / raw)
To: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 73 bytes --]
This patch adds the tdep stuff for tic6x in gdb.
--
Yao (é½å°§)
[-- Attachment #2: 0005-gdb-tic6x-port.patch --]
[-- Type: text/x-patch, Size: 52473 bytes --]
2011-07-19 Andrew Jenner <andrew@codesourcery.com>
Yao Qi <yao@codesourcery.com>
gdb/
* tic6x-linux-tdep.c: New file.
* tic6x-tdep.c: New file.
* tic6x-tdep.h: New file.
---
gdb/tic6x-linux-tdep.c | 208 +++++++
gdb/tic6x-tdep.c | 1420 ++++++++++++++++++++++++++++++++++++++++++++++++
gdb/tic6x-tdep.h | 51 ++
3 files changed, 1679 insertions(+), 0 deletions(-)
create mode 100644 gdb/tic6x-linux-tdep.c
create mode 100644 gdb/tic6x-tdep.c
create mode 100644 gdb/tic6x-tdep.h
diff --git a/gdb/tic6x-linux-tdep.c b/gdb/tic6x-linux-tdep.c
new file mode 100644
index 0000000..c310a52
--- /dev/null
+++ b/gdb/tic6x-linux-tdep.c
@@ -0,0 +1,208 @@
+/* GNU/Linux on TI C6x target support.
+ Copyright (C) 2011
+ Free Software Foundation, Inc.
+ 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 "solib.h"
+#include "osabi.h"
+#include "linux-tdep.h"
+#include "tic6x-tdep.h"
+#include "trad-frame.h"
+#include "tramp-frame.h"
+#include "gdb_assert.h"
+#include "elf-bfd.h"
+#include "elf/tic6x.h"
+
+/* The offset from rt_sigframe pointer to SP register. */
+#define TIC6X_SP_RT_SIGFRAME 8
+/* Size of struct siginfo info. */
+#define TIC6X_SIGINFO_SIZE 128
+/* Size of type stack_t, which contains three fields of type void*, int, and
+ size_t respectively. */
+#define TIC6X_STACK_T_SIZE 3 * 4
+
+static const char breakpoint_bnop_be[] = {0x00, 0x00, 0xa1, 0x22};
+static const char breakpoint_bnop_le[] = {0x22, 0xa1, 0x00, 0x00};
+
+/* Return the offset of register REGNUM in struct sigconext. Return 0 if no
+ such register in sigcontext. */
+
+static unsigned int
+tic6x_register_sigcontext_offset (unsigned int regnum)
+{
+ if (regnum == A4_REGNUM || regnum == A4_REGNUM + 2
+ || regnum == A4_REGNUM + 4)
+ return 4 * (regnum - A4_REGNUM + 2); /* A4, A6, A8 */
+ else if (regnum == A5_REGNUM || regnum == A5_REGNUM + 2
+ || regnum == A5_REGNUM + 4)
+ return 4 * (regnum - A5_REGNUM + 12); /* A5, A7, A9 */
+ else if (regnum == B4_REGNUM || regnum == B4_REGNUM + 2
+ || regnum == B4_REGNUM + 4)
+ return 4 * (regnum - B4_REGNUM + 3); /* B4, B6, B8 */
+ else if (regnum == B5_REGNUM || regnum == B5_REGNUM + 2
+ || regnum == B5_REGNUM + 4)
+ return 4 * (regnum - B5_REGNUM + 19); /* B5, B7, B9 */
+ else if (regnum >= 0 && regnum < A4_REGNUM)
+ return 4 * (regnum - 0 + 8); /* A0 - A3 */
+ else if (regnum >= B0_REGNUM && regnum < B4_REGNUM)
+ return 4 * (regnum - B0_REGNUM + 15); /* B0 - B3 */
+ else if (regnum >= A16_REGNUM && regnum < A16_REGNUM + 32)
+ return 4 * (regnum - A16_REGNUM + 23); /* A16 - A31, B16 -B31 */
+ else if (regnum == PC_REGNUM)
+ return 4 * 55;
+ else if (regnum == SP_REGNUM)
+ return 4;
+
+ return 0;
+}
+
+/* Support unwiding frame in signal trampoline. We don't check sigreturn,
+ since it is not used in kernel. */
+
+static void
+tic6x_linux_rt_sigreturn_init (const struct tramp_frame *self,
+ struct frame_info *this_frame,
+ struct trad_frame_cache *this_cache,
+ CORE_ADDR func)
+{
+ CORE_ADDR sp = get_frame_register_unsigned (this_frame, SP_REGNUM);
+ /* The base of struct sigcontext is computed by examining the definition of
+ struct rt_sigframe in linux kernel source arch/c6x/kernel/signal.c. */
+ CORE_ADDR base = sp + TIC6X_SP_RT_SIGFRAME
+ + 4 + 4 /* Pointer type *pinfo and *puc in struct rt_sigframe. */
+ + TIC6X_SIGINFO_SIZE
+ + 4 + 4 /* uc_flags and *uc_link in struct ucontext. */
+ + TIC6X_STACK_T_SIZE;
+ unsigned int reg_offset;
+ unsigned int i;
+
+ for (i = 0; i < 10; i++) /* A0 - A9 */
+ {
+ reg_offset = tic6x_register_sigcontext_offset (i);
+ gdb_assert (reg_offset != 0);
+
+ trad_frame_set_reg_addr (this_cache, i, base + reg_offset);
+ }
+
+ for (i = B0_REGNUM; i < B0_REGNUM + 10; i++) /* B0 - B9 */
+ {
+ reg_offset = tic6x_register_sigcontext_offset (i);
+ gdb_assert (reg_offset != 0);
+
+ trad_frame_set_reg_addr (this_cache, i, base + reg_offset);
+ }
+
+ for (i = A16_REGNUM; i < A16_REGNUM + 32; i++) /* A16 - A31, B16 - B31 */
+ {
+ reg_offset = tic6x_register_sigcontext_offset (i);
+ gdb_assert (reg_offset != 0);
+
+ trad_frame_set_reg_addr (this_cache, i, base + reg_offset);
+ }
+
+ trad_frame_set_reg_addr (this_cache, PC_REGNUM,
+ base + tic6x_register_sigcontext_offset (PC_REGNUM));
+ trad_frame_set_reg_addr (this_cache, SP_REGNUM,
+ base + tic6x_register_sigcontext_offset (SP_REGNUM));
+
+ /* Save a frame ID. */
+ trad_frame_set_id (this_cache, frame_id_build (sp, func));
+}
+
+static struct tramp_frame tic6x_linux_rt_sigreturn_tramp_frame = {
+ SIGTRAMP_FRAME,
+ 4,
+ {
+ {0x000045aa, 0x0fffffff}, /* mvk .S2 139,b0 */
+ {0x10000000, -1}, /* swe */
+ { TRAMP_SENTINEL_INSN }
+ },
+ tic6x_linux_rt_sigreturn_init
+};
+
+/* When FRAME is at a syscall instruction, return the PC of the next
+ instruction to be executed. */
+
+static CORE_ADDR
+tic6x_linux_syscall_next_pc (struct frame_info *frame)
+{
+ ULONGEST syscall_number = get_frame_register_unsigned (frame, B0_REGNUM);
+ CORE_ADDR pc = get_frame_pc (frame);
+
+ if (syscall_number == 139 /* rt_sigreturn */)
+ {
+ if (get_frame_type (frame) == SIGTRAMP_FRAME)
+ return frame_unwind_caller_pc (frame);
+ }
+
+ return pc + 4;
+}
+
+
+extern struct target_so_ops dsbt_so_ops;
+static void
+tic6x_uclinux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ linux_init_abi (info, gdbarch);
+
+ /* Shared library handling. */
+ set_solib_ops (gdbarch, &dsbt_so_ops);
+
+ tdep->syscall_next_pc = tic6x_linux_syscall_next_pc;
+
+#ifdef HAVE_ELF
+ /* In tic6x linux kernel, breakpoint instructions varies on different archs.
+ When either macro __TMS320C6XPLUS__ or _TMS320C6400_PLUS is defined,
+ breakpoint instruction is 0x56454314, which is an illegal opcode.
+ Otherwise, breakpoint instruction is 0x0000a122 (BNOP .S2 0,5). */
+ if (info.abfd)
+ switch (bfd_elf_get_obj_attr_int (info.abfd, OBJ_ATTR_PROC, Tag_ISA))
+ {
+ case C6XABI_Tag_ISA_C64XP:
+ case C6XABI_Tag_ISA_C67XP:
+ if (info.byte_order == BFD_ENDIAN_BIG)
+ tdep->breakpoint = breakpoint_illegal_opcode_be;
+ else
+ tdep->breakpoint = breakpoint_illegal_opcode_le;
+ break;
+ default:
+ {
+ if (info.byte_order == BFD_ENDIAN_BIG)
+ tdep->breakpoint = breakpoint_bnop_be;
+ else
+ tdep->breakpoint = breakpoint_bnop_le;
+ }
+ }
+#endif
+
+ /* Signal trampoline support. */
+ tramp_frame_prepend_unwinder (gdbarch,
+ &tic6x_linux_rt_sigreturn_tramp_frame);
+}
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_tic6x_linux_tdep;
+
+void
+_initialize_tic6x_linux_tdep (void)
+{
+ gdbarch_register_osabi (bfd_arch_tic6x, 0, GDB_OSABI_LINUX,
+ tic6x_uclinux_init_abi);
+}
diff --git a/gdb/tic6x-tdep.c b/gdb/tic6x-tdep.c
new file mode 100644
index 0000000..a4e7f58
--- /dev/null
+++ b/gdb/tic6x-tdep.c
@@ -0,0 +1,1420 @@
+/* Target dependent code for GDB on TI C6x systems.
+
+ Copyright (C) 2010
+ 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 "gdb_assert.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"
+
+/* Macros */
+
+#define TIC6X_OPCODE_SIZE 4
+#define TIC6X_FETCH_PACKET_SIZE 32
+
+static int arg_regs[] = {4, 20, 6, 22, 8, 24, 10, 26, 12, 28};
+#define INST_S_BIT(INST) ((INST >> 1) & 1)
+#define INST_X_BIT(INST) ((INST >> 12) & 1)
+
+/* Structures */
+struct register_info
+{
+ int size;
+ char *name;
+};
+
+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;
+
+ CORE_ADDR reg_saved[TIC6X_NUM_REGS];
+};
+
+
+/* tic6x_register_info_table[i] is the number of bytes of storage in
+ GDB's register array occupied by register i. */
+static struct register_info tic6x_register_info_table[] = {
+ /* 0 */ {4, "A0"},
+ /* 1 */ {4, "A1"},
+ /* 2 */ {4, "A2"},
+ /* 3 */ {4, "A3"},
+ /* 4 */ {4, "A4"},
+ /* 5 */ {4, "A5"},
+ /* 6 */ {4, "A6"},
+ /* 7 */ {4, "A7"},
+ /* 8 */ {4, "A8"},
+ /* 9 */ {4, "A9"},
+ /* 10 */ {4, "A10"},
+ /* 11 */ {4, "A11"},
+ /* 12 */ {4, "A12"},
+ /* 13 */ {4, "A13"},
+ /* 14 */ {4, "A14"},
+ /* 15 */ {4, "A15"},
+ /* 16 */ {4, "B0"},
+ /* 17 */ {4, "B1"},
+ /* 18 */ {4, "B2"},
+ /* 19 */ {4, "B3"},
+ /* 20 */ {4, "B4"},
+ /* 21 */ {4, "B5"},
+ /* 22 */ {4, "B6"},
+ /* 23 */ {4, "B7"},
+ /* 24 */ {4, "B8"},
+ /* 25 */ {4, "B9"},
+ /* 26 */ {4, "B10"},
+ /* 27 */ {4, "B11"},
+ /* 28 */ {4, "B12"},
+ /* 29 */ {4, "B13"},
+ /* 30 */ {4, "B14"},
+ /* 31 */ {4, "B15"},
+ /* 32 */ {4, "None"},
+ /* 33 */ {4, "PC"},
+ /* 34 */ {4, "IRP"},
+ /* 35 */ {4, "IFR"},
+ /* 36 */ {4, "NPR"},
+ /* 37 */ {4, "A16"},
+ /* 38 */ {4, "A17"},
+ /* 39 */ {4, "A18"},
+ /* 40 */ {4, "A19"},
+ /* 41 */ {4, "A20"},
+ /* 42 */ {4, "A21"},
+ /* 43 */ {4, "A22"},
+ /* 44 */ {4, "A23"},
+ /* 45 */ {4, "A24"},
+ /* 46 */ {4, "A25"},
+ /* 47 */ {4, "A26"},
+ /* 48 */ {4, "A27"},
+ /* 49 */ {4, "A28"},
+ /* 50 */ {4, "A29"},
+ /* 51 */ {4, "A30"},
+ /* 52 */ {4, "A31"},
+ /* 53 */ {4, "B16"},
+ /* 54 */ {4, "B17"},
+ /* 55 */ {4, "B18"},
+ /* 56 */ {4, "B19"},
+ /* 57 */ {4, "B20"},
+ /* 58 */ {4, "B21"},
+ /* 59 */ {4, "B22"},
+ /* 60 */ {4, "B23"},
+ /* 61 */ {4, "B24"},
+ /* 62 */ {4, "B25"},
+ /* 63 */ {4, "B26"},
+ /* 64 */ {4, "B27"},
+ /* 65 */ {4, "B28"},
+ /* 66 */ {4, "B29"},
+ /* 67 */ {4, "B30"},
+ /* 68 */ {4, "B31"},
+ /* 69 */ {4, "AMR"},
+ /* 70 */ {4, "CSR"},
+ /* 71 */ {4, "ISR"},
+ /* 72 */ {4, "ICR"},
+ /* 73 */ {4, "IER"},
+ /* 74 */ {4, "ISTP"},
+ /* 75 */ {4, "IN"},
+ /* 76 */ {4, "OUT"},
+ /* 77 */ {4, "ACR"},
+ /* 78 */ {4, "ADR"},
+ /* 79 */ {4, "FADCR"},
+ /* 80 */ {4, "FAUCR"},
+ /* 81 */ {4, "FMCR"},
+ /* 82 */ {4, "GFPGFR"},
+ /* 83 */ {4, "DIER"},
+ /* 84 */ {4, "REP"},
+ /* 85 */ {4, "TSCL"},
+ /* 86 */ {4, "TSCH"},
+ /* 87 */ {4, "ARP"},
+ /* 88 */ {4, "ILC"},
+ /* 89 */ {4, "RILC"},
+ /* 90 */ {4, "DNUM"},
+ /* 91 */ {4, "SSR"},
+ /* 92 */ {4, "GPLYA"},
+ /* 93 */ {4, "GPLYB"},
+ /* 94 */ {4, "TSR"},
+ /* 95 */ {4, "ITSR"},
+ /* 96 */ {4, "NTSR"},
+ /* 97 */ {4, "EFR"},
+ /* 98 */ {4, "IERR"},
+ /* 99 */ {4, "DMSG"},
+ /* 100 */ {4, "CMSG"},
+ /* 101 */ {4, "DT_DMA_ADDR"},
+ /* 102 */ {4, "DT_DMA_DATA"},
+ /* 103 */ {4, "DT_DMA_CNTL"},
+ /* 104 */ {4, "TCU_CNTL"},
+ /* 105 */ {4, "RTDX_REC_CNTL"},
+ /* 106 */ {4, "RTDX_XMT_CNTL"},
+ /* 107 */ {4, "RTDX_CFG"},
+ /* 108 */ {4, "RTDX_RDATA"},
+ /* 109 */ {4, "RTDX_WDATA"},
+ /* 110 */ {4, "RTDX_RADDR"},
+ /* 111 */ {4, "RTDX_WADDR"},
+ /* 112 */ {4, "MFREG0"},
+ /* 113 */ {4, "DBG_STAT"},
+ /* 114 */ {4, "BRK_EN"},
+ /* 115 */ {4, "HWBP0_CNT"},
+ /* 116 */ {4, "HWBP0"},
+ /* 117 */ {4, "HWBP1"},
+ /* 118 */ {4, "HWBP2"},
+ /* 119 */ {4, "HWBP3"},
+ /* 120 */ {4, "OVERLAY"},
+ /* 121 */ {4, "PC_PROF"},
+ /* 122 */ {4, "ATSR"},
+ /* 123 */ {4, "TRR"},
+ /* 124 */ {4, "TCRR"},
+ /* 125 */ {4, "DESR"},
+ /* 126 */ {4, "DETR"},
+ /* 127 */ {4, ""}
+};
+
+/* Find the name for the specified regno. */
+
+static const char *
+tic6x_register_name (struct gdbarch *gdbarch, int regno)
+{
+ if (regno < 0)
+ return NULL;
+ return tic6x_register_info_table[regno].name;
+}
+
+/* Return the GDB type object for the "standard" data type
+ of data in register 'regno'. */
+static struct type *
+tic6x_register_type (struct gdbarch *gdbarch, int regno)
+{
+ gdb_assert (tic6x_register_info_table[regno].size == 4);
+
+ if (regno == 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_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);
+
+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;
+
+ 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;
+ /* Counter of non-stw instructions after first insn ` sub sp, xxx, sp'. */
+ int non_stw_insn_counter = 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 == SP_REGNUM && dst == 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 (0);
+
+ return_pc = pc;
+ }
+ }
+ 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 == SP_REGNUM)
+ {
+ src_reg = tic6x_register_number ((inst >> 23) & 0x1f,
+ INST_S_BIT(inst), 0);
+
+ cache->reg_saved[src_reg] = ((inst >> 13) & 0x1f) << 2;
+ }
+ 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 == DP_REGNUM /* dst is B14 */
+ && base_reg == DP_REGNUM) /* baseR is B14 */
+ {
+ return_pc = pc + 4;
+ }
+
+
+ if (this_frame)
+ {
+ cache->base = get_frame_register_unsigned (this_frame,
+ SP_REGNUM);
+
+ if (cache->reg_saved[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,
+ 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_REGS; i++)
+ if (cache->reg_saved[i] != -1)
+ cache->reg_saved[i] = cache->base + cache->reg_saved[i];
+
+ return return_pc;
+}
+
+/* Given a PC value corresponding to the start of a function, return the PC
+ of the first instruction after the function prologue. */
+
+CORE_ADDR
+tic6x_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc)
+{
+ CORE_ADDR limit_pc;
+ CORE_ADDR func_addr;
+
+ /* See if we can determine the end of the prologue via the symbol table.
+ If so, then return either PC, or the PC after the prologue, whichever is
+ greater. */
+ if (find_pc_partial_function (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, return. */
+ return start_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 breakpoint_illegal_opcode_be;
+ else
+ return breakpoint_illegal_opcode_le;
+ }
+ else
+ return tdep->breakpoint;
+}
+
+int
+gdb_print_insn_tic6x (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;
+}
+
+static CORE_ADDR
+tic6x_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ char buf[8];
+
+ frame_unwind_register (next_frame, PC_REGNUM, buf);
+ return extract_typed_address (buf, builtin_type (gdbarch)->builtin_func_ptr);
+}
+
+static CORE_ADDR
+tic6x_unwind_sp (struct gdbarch *gdbarch, struct frame_info *this_frame)
+{
+ return frame_unwind_register_unsigned (this_frame, 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;
+
+ /* Zero all fields. */
+ cache->base = 0;
+ cache->cfa = 0;
+ cache->pc = 0;
+
+ cache->return_regnum = 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 == PC_REGNUM)
+ regnum = cache->return_regnum;
+
+ if (regnum == 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_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->base = 0;
+ cache->pc = 0;
+
+ cache->return_regnum = RA_REGNUM;
+
+ tic6x_setup_default(cache);
+
+ cache->cfa = get_frame_register_unsigned (this_frame, SP_REGNUM);
+
+ return cache;
+}
+
+/* Our frame ID for a stub frame is the current SP and LR. */
+
+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;
+}
+
+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
+};
+
+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);
+}
+
+static int
+tic6x_condition_true (struct frame_info *frame, unsigned long inst)
+{
+ int register_number;
+ int register_value;
+ static 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;
+}
+
+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 == 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;
+}
+
+/* tic6x_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. We find the target of the coming instruction
+ and breakpoint it. */
+
+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;
+}
+
+/* Adjust the address downward (direction of stack growth) so that it
+ is correctly aligned for a new stack frame. */
+
+static CORE_ADDR
+tic6x_frame_align (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+ return align_down (addr, 8);
+}
+
+/* We don't convert anything at the moment */
+static int
+tic6x_convert_register_p (struct gdbarch *gdbarch, int regnum,
+ struct type *type)
+{
+ return 0;
+}
+
+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 + 0, (char *) to + 0);
+ *optimizedp = *unavailablep = 0;
+ return 1;
+}
+
+static void
+tic6x_value_to_register (struct frame_info *frame, int regnum,
+ struct type *type, const gdb_byte *from)
+{
+ put_frame_register (frame, regnum + 0, (const char *) from + 0);
+}
+
+/* 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, A4_REGNUM, 4 - len, len, valbuf);
+ else
+ regcache_cooked_read (regcache, 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, A4_REGNUM, valbuf+4);
+ regcache_cooked_read (regcache, A5_REGNUM, valbuf);
+ }
+ else
+ {
+ regcache_cooked_read (regcache, A4_REGNUM, valbuf);
+ regcache_cooked_read (regcache, 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, A4_REGNUM, 4 - len, len, valbuf);
+ else
+ regcache_cooked_write (regcache, A4_REGNUM, valbuf);
+ }
+ else if (len <= 8)
+ {
+ if (byte_order == BFD_ENDIAN_BIG)
+ {
+ regcache_cooked_write (regcache, A4_REGNUM, valbuf+4);
+ regcache_cooked_write (regcache, A5_REGNUM, valbuf);
+ }
+ else
+ {
+ regcache_cooked_write (regcache, A4_REGNUM, valbuf);
+ regcache_cooked_write (regcache, A5_REGNUM, valbuf+4);
+ }
+ }
+}
+
+/* Determine, for architecture GDBARCH, how a return value of TYPE
+ should be returned. If it is supposed to be returned in registers,
+ and READBUF is non-zero, read the appropriate value from REGCACHE,
+ and copy it into READBUF. If WRITEBUF is non-zero, write the value
+ from WRITEBUF into REGCACHE. */
+
+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;
+}
+
+/* Assuming THIS_FRAME is a dummy, return the frame ID of that
+ dummy frame. The frame ID's base needs to match the TOS value
+ saved by save_dummy_frame_tos() and returned from
+ tic6x_push_dummy_call, and the PC needs to match the dummy frame's
+ breakpoint. */
+
+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, SP_REGNUM),
+ get_frame_pc (this_frame));
+}
+
+/* Get the alignment requirement of TYPE. */
+int
+tic6x_arg_type_alignment (struct type *type)
+{
+ int len = TYPE_LENGTH (type);
+ enum type_code typecode = TYPE_CODE (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 (0); /* Something wrong. */
+ }
+ 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
+ {
+ char *msg = xstrprintf ("unexpected length %d of type", len);
+ gdb_assert_not_reached (msg);
+ return 0;
+ }
+ }
+}
+
+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, 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
+ {
+ char *msg = xstrprintf ("unexpected type %d of arg %d",
+ typecode, argnum);
+ gdb_assert_not_reached (msg);
+ }
+ }
+ else
+ {
+ char *msg = xstrprintf ("unexpected length %d of arg %d", len,
+ argnum);
+ gdb_assert_not_reached (msg);
+ }
+
+
+ addr = sp + stack_offset;
+ write_memory (addr, val, len);
+ stack_offset += align_up (len, 4);
+ }
+ }
+
+ regcache_cooked_write_signed (regcache, SP_REGNUM, sp);
+
+ /* Return adjusted stack pointer. */
+ return sp;
+}
+
+/* Return 1 if the stack frame has already been destroyed by the
+ function epilogue. */
+
+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 == RA_REGNUM)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Longjmp support. */
+
+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;
+
+ /* 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 (tdep && tdep->breakpoint)
+ return arches->gdbarch;
+ }
+
+ tdep = xcalloc (1, sizeof (struct gdbarch_tdep));
+
+ 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, SP_REGNUM);
+ set_gdbarch_pc_regnum (gdbarch, 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, gdb_print_insn_tic6x);
+
+ /* Call dummy code. */
+ set_gdbarch_frame_align (gdbarch, tic6x_frame_align);
+
+ set_gdbarch_convert_register_p (gdbarch, tic6x_convert_register_p);
+ 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);
+
+ return gdbarch;
+}
+
+void
+_initialize_tic6x_tdep (void)
+{
+ int i, offset = 0;
+
+ register_gdbarch_init (bfd_arch_tic6x, tic6x_gdbarch_init);
+}
diff --git a/gdb/tic6x-tdep.h b/gdb/tic6x-tdep.h
new file mode 100644
index 0000000..600def3
--- /dev/null
+++ b/gdb/tic6x-tdep.h
@@ -0,0 +1,51 @@
+/* GNU/Linux on TI C6x target support.
+ Copyright (C) 2011
+ Free Software Foundation, Inc.
+ 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/>. */
+
+#undef FP_REGNUM
+#define FP_REGNUM 15 /* Frame Pointer: A15 */
+#undef SP_REGNUM
+#define SP_REGNUM 31 /* Stack Pointer: B15 */
+#define RA_REGNUM 19 /* Return address: B3 */
+#define DP_REGNUM 30 /* Data Page Pointer: B14 */
+#undef PC_REGNUM
+#define PC_REGNUM 33
+#define Z_REGNUM 127
+#define TIC6X_NUM_REGS 128
+#define A4_REGNUM 4
+#define A5_REGNUM 5
+#define B0_REGNUM 16
+#define B4_REGNUM 20
+#define B5_REGNUM 21
+#define A16_REGNUM 37
+
+#define INST_SWE 0x10000000
+
+static const char breakpoint_illegal_opcode_be[] = {0x56, 0x45, 0x43, 0x14};
+static const char breakpoint_illegal_opcode_le[] = {0x14, 0x43, 0x45, 0x56};
+
+/* Target-dependent structure in gdbarch. */
+struct gdbarch_tdep
+{
+ /* Return the expected next PC if FRAME is stopped at a syscall
+ instruction. */
+ CORE_ADDR (*syscall_next_pc) (struct frame_info *frame);
+
+ const char *breakpoint; /* Breakpoint instruction. */
+};
--
1.7.0.4
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFA 5/8] New port: TI C6x: gdb port
2011-07-20 2:10 [RFA 5/8] New port: TI C6x: gdb port Yao Qi
@ 2011-07-25 11:32 ` Yao Qi
2011-08-03 1:15 ` ping: " Yao Qi
2011-08-04 12:34 ` Pedro Alves
1 sibling, 1 reply; 16+ messages in thread
From: Yao Qi @ 2011-07-25 11:32 UTC (permalink / raw)
To: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 235 bytes --]
On 07/20/2011 10:08 AM, Yao Qi wrote:
> This patch adds the tdep stuff for tic6x in gdb.
This patch is updated for checking target description and looking for
features in tic6x_gdbarch_init.
OK for mainline?
--
Yao (é½å°§)
[-- Attachment #2: 0006-gdb-tic6x-port.patch --]
[-- Type: text/x-patch, Size: 55543 bytes --]
2011-07-19 Andrew Jenner <andrew@codesourcery.com>
Yao Qi <yao@codesourcery.com>
gdb/
* tic6x-linux-tdep.c: New file.
* tic6x-tdep.c: New file.
* tic6x-tdep.h: New file.
---
gdb/tic6x-linux-tdep.c | 223 +++++++
gdb/tic6x-tdep.c | 1515 ++++++++++++++++++++++++++++++++++++++++++++++++
gdb/tic6x-tdep.h | 52 ++
3 files changed, 1790 insertions(+), 0 deletions(-)
create mode 100644 gdb/tic6x-linux-tdep.c
create mode 100644 gdb/tic6x-tdep.c
create mode 100644 gdb/tic6x-tdep.h
diff --git a/gdb/tic6x-linux-tdep.c b/gdb/tic6x-linux-tdep.c
new file mode 100644
index 0000000..55c3d31
--- /dev/null
+++ b/gdb/tic6x-linux-tdep.c
@@ -0,0 +1,223 @@
+/* GNU/Linux on TI C6x target support.
+ Copyright (C) 2011
+ Free Software Foundation, Inc.
+ 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 "solib.h"
+#include "osabi.h"
+#include "linux-tdep.h"
+#include "tic6x-tdep.h"
+#include "trad-frame.h"
+#include "tramp-frame.h"
+#include "gdb_assert.h"
+#include "elf-bfd.h"
+#include "elf/tic6x.h"
+
+#include "features/tic6x-c64xp-linux.c"
+#include "features/tic6x-c64x-linux.c"
+#include "features/tic6x-c62x-linux.c"
+
+/* The offset from rt_sigframe pointer to SP register. */
+#define TIC6X_SP_RT_SIGFRAME 8
+/* Size of struct siginfo info. */
+#define TIC6X_SIGINFO_SIZE 128
+/* Size of type stack_t, which contains three fields of type void*, int, and
+ size_t respectively. */
+#define TIC6X_STACK_T_SIZE (3 * 4)
+
+static const char breakpoint_bnop_be[] = {0x00, 0x00, 0xa1, 0x22};
+static const char breakpoint_bnop_le[] = {0x22, 0xa1, 0x00, 0x00};
+
+/* Return the offset of register REGNUM in struct sigconext. Return 0 if no
+ such register in sigcontext. */
+
+static unsigned int
+tic6x_register_sigcontext_offset (unsigned int regnum, struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ if (regnum == A4_REGNUM || regnum == A4_REGNUM + 2
+ || regnum == A4_REGNUM + 4)
+ return 4 * (regnum - A4_REGNUM + 2); /* A4, A6, A8 */
+ else if (regnum == A5_REGNUM || regnum == A5_REGNUM + 2
+ || regnum == A5_REGNUM + 4)
+ return 4 * (regnum - A5_REGNUM + 12); /* A5, A7, A9 */
+ else if (regnum == B4_REGNUM || regnum == B4_REGNUM + 2
+ || regnum == B4_REGNUM + 4)
+ return 4 * (regnum - B4_REGNUM + 3); /* B4, B6, B8 */
+ else if (regnum == B5_REGNUM || regnum == B5_REGNUM + 2
+ || regnum == B5_REGNUM + 4)
+ return 4 * (regnum - B5_REGNUM + 19); /* B5, B7, B9 */
+ else if (regnum >= 0 && regnum < A4_REGNUM)
+ return 4 * (regnum - 0 + 8); /* A0 - A3 */
+ else if (regnum >= B0_REGNUM && regnum < B4_REGNUM)
+ return 4 * (regnum - B0_REGNUM + 15); /* B0 - B3 */
+ else if (regnum >= 34 && regnum < 34 + 32)
+ return 4 * (regnum - 34 + 23); /* A16 - A31, B16 -B31 */
+ else if (regnum == PC_REGNUM)
+ return 4 * (tdep->has_gp ? 55 : 23);
+ else if (regnum == SP_REGNUM)
+ return 4;
+
+ return 0;
+}
+
+/* Support unwiding frame in signal trampoline. We don't check sigreturn,
+ since it is not used in kernel. */
+
+static void
+tic6x_linux_rt_sigreturn_init (const struct tramp_frame *self,
+ struct frame_info *this_frame,
+ struct trad_frame_cache *this_cache,
+ CORE_ADDR func)
+{
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ CORE_ADDR sp = get_frame_register_unsigned (this_frame, SP_REGNUM);
+ /* The base of struct sigcontext is computed by examining the definition of
+ struct rt_sigframe in linux kernel source arch/c6x/kernel/signal.c. */
+ CORE_ADDR base = sp + TIC6X_SP_RT_SIGFRAME
+ + 4 + 4 /* Pointer type *pinfo and *puc in struct rt_sigframe. */
+ + TIC6X_SIGINFO_SIZE
+ + 4 + 4 /* uc_flags and *uc_link in struct ucontext. */
+ + TIC6X_STACK_T_SIZE;
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ unsigned int reg_offset;
+ unsigned int i;
+
+ for (i = 0; i < 10; i++) /* A0 - A9 */
+ {
+ reg_offset = tic6x_register_sigcontext_offset (i, gdbarch);
+ gdb_assert (reg_offset != 0);
+
+ trad_frame_set_reg_addr (this_cache, i, base + reg_offset);
+ }
+
+ for (i = B0_REGNUM; i < B0_REGNUM + 10; i++) /* B0 - B9 */
+ {
+ reg_offset = tic6x_register_sigcontext_offset (i, gdbarch);
+ gdb_assert (reg_offset != 0);
+
+ trad_frame_set_reg_addr (this_cache, i, base + reg_offset);
+ }
+
+ if (tdep->has_gp)
+ for (i = 34; i < 34 + 32; i++) /* A16 - A31, B16 - B31 */
+ {
+ reg_offset = tic6x_register_sigcontext_offset (i, gdbarch);
+ gdb_assert (reg_offset != 0);
+
+ trad_frame_set_reg_addr (this_cache, i, base + reg_offset);
+ }
+
+ trad_frame_set_reg_addr (this_cache, PC_REGNUM,
+ base + tic6x_register_sigcontext_offset (PC_REGNUM,
+ gdbarch));
+ trad_frame_set_reg_addr (this_cache, SP_REGNUM,
+ base + tic6x_register_sigcontext_offset (SP_REGNUM,
+ gdbarch));
+
+ /* Save a frame ID. */
+ trad_frame_set_id (this_cache, frame_id_build (sp, func));
+}
+
+static struct tramp_frame tic6x_linux_rt_sigreturn_tramp_frame = {
+ SIGTRAMP_FRAME,
+ 4,
+ {
+ {0x000045aa, 0x0fffffff}, /* mvk .S2 139,b0 */
+ {0x10000000, -1}, /* swe */
+ { TRAMP_SENTINEL_INSN }
+ },
+ tic6x_linux_rt_sigreturn_init
+};
+
+/* When FRAME is at a syscall instruction, return the PC of the next
+ instruction to be executed. */
+
+static CORE_ADDR
+tic6x_linux_syscall_next_pc (struct frame_info *frame)
+{
+ ULONGEST syscall_number = get_frame_register_unsigned (frame, B0_REGNUM);
+ CORE_ADDR pc = get_frame_pc (frame);
+
+ if (syscall_number == 139 /* rt_sigreturn */)
+ {
+ if (get_frame_type (frame) == SIGTRAMP_FRAME)
+ return frame_unwind_caller_pc (frame);
+ }
+
+ return pc + 4;
+}
+
+
+extern struct target_so_ops dsbt_so_ops;
+static void
+tic6x_uclinux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ linux_init_abi (info, gdbarch);
+
+ /* Shared library handling. */
+ set_solib_ops (gdbarch, &dsbt_so_ops);
+
+ tdep->syscall_next_pc = tic6x_linux_syscall_next_pc;
+
+#ifdef HAVE_ELF
+ /* In tic6x linux kernel, breakpoint instructions varies on different archs.
+ When either macro __TMS320C6XPLUS__ or _TMS320C6400_PLUS is defined,
+ breakpoint instruction is 0x56454314, which is an illegal opcode.
+ Otherwise, breakpoint instruction is 0x0000a122 (BNOP .S2 0,5). */
+ if (info.abfd)
+ switch (bfd_elf_get_obj_attr_int (info.abfd, OBJ_ATTR_PROC, Tag_ISA))
+ {
+ case C6XABI_Tag_ISA_C64XP:
+ case C6XABI_Tag_ISA_C67XP:
+ if (info.byte_order == BFD_ENDIAN_BIG)
+ tdep->breakpoint = breakpoint_illegal_opcode_be;
+ else
+ tdep->breakpoint = breakpoint_illegal_opcode_le;
+ break;
+ default:
+ {
+ if (info.byte_order == BFD_ENDIAN_BIG)
+ tdep->breakpoint = breakpoint_bnop_be;
+ else
+ tdep->breakpoint = breakpoint_bnop_le;
+ }
+ }
+#endif
+
+ /* Signal trampoline support. */
+ tramp_frame_prepend_unwinder (gdbarch,
+ &tic6x_linux_rt_sigreturn_tramp_frame);
+}
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_tic6x_linux_tdep;
+
+void
+_initialize_tic6x_linux_tdep (void)
+{
+ gdbarch_register_osabi (bfd_arch_tic6x, 0, GDB_OSABI_LINUX,
+ tic6x_uclinux_init_abi);
+
+ initialize_tdesc_tic6x_c64xp_linux ();
+ initialize_tdesc_tic6x_c64x_linux ();
+ initialize_tdesc_tic6x_c62x_linux ();
+}
diff --git a/gdb/tic6x-tdep.c b/gdb/tic6x-tdep.c
new file mode 100644
index 0000000..07e38dc
--- /dev/null
+++ b/gdb/tic6x-tdep.c
@@ -0,0 +1,1515 @@
+/* Target dependent code for GDB on TI C6x systems.
+
+ Copyright (C) 2010
+ 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 "gdb_assert.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"
+
+/* Macros */
+
+#define TIC6X_OPCODE_SIZE 4
+#define TIC6X_FETCH_PACKET_SIZE 32
+
+static int arg_regs[] = {4, 20, 6, 22, 8, 24, 10, 26, 12, 28};
+#define INST_S_BIT(INST) ((INST >> 1) & 1)
+#define INST_X_BIT(INST) ((INST >> 12) & 1)
+
+/* Structures */
+struct register_info
+{
+ int size;
+ char *name;
+};
+
+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;
+
+ CORE_ADDR reg_saved[TIC6X_NUM_REGS];
+};
+
+
+/* tic6x_register_info_table[i] is the number of bytes of storage in
+ GDB's register array occupied by register i. */
+static struct register_info tic6x_register_info_table[] = {
+ /* 0 */ {4, "A0"},
+ /* 1 */ {4, "A1"},
+ /* 2 */ {4, "A2"},
+ /* 3 */ {4, "A3"},
+ /* 4 */ {4, "A4"},
+ /* 5 */ {4, "A5"},
+ /* 6 */ {4, "A6"},
+ /* 7 */ {4, "A7"},
+ /* 8 */ {4, "A8"},
+ /* 9 */ {4, "A9"},
+ /* 10 */ {4, "A10"},
+ /* 11 */ {4, "A11"},
+ /* 12 */ {4, "A12"},
+ /* 13 */ {4, "A13"},
+ /* 14 */ {4, "A14"},
+ /* 15 */ {4, "A15"},
+ /* 16 */ {4, "B0"},
+ /* 17 */ {4, "B1"},
+ /* 18 */ {4, "B2"},
+ /* 19 */ {4, "B3"},
+ /* 20 */ {4, "B4"},
+ /* 21 */ {4, "B5"},
+ /* 22 */ {4, "B6"},
+ /* 23 */ {4, "B7"},
+ /* 24 */ {4, "B8"},
+ /* 25 */ {4, "B9"},
+ /* 26 */ {4, "B10"},
+ /* 27 */ {4, "B11"},
+ /* 28 */ {4, "B12"},
+ /* 29 */ {4, "B13"},
+ /* 30 */ {4, "B14"},
+ /* 31 */ {4, "B15"},
+ /* 32 */ {4, "None"},
+ /* 33 */ {4, "PC"},
+ /* 34 */ {4, "IRP"},
+ /* 35 */ {4, "IFR"},
+ /* 36 */ {4, "NRP"},
+ /* 37 */ {4, "A16"},
+ /* 38 */ {4, "A17"},
+ /* 39 */ {4, "A18"},
+ /* 40 */ {4, "A19"},
+ /* 41 */ {4, "A20"},
+ /* 42 */ {4, "A21"},
+ /* 43 */ {4, "A22"},
+ /* 44 */ {4, "A23"},
+ /* 45 */ {4, "A24"},
+ /* 46 */ {4, "A25"},
+ /* 47 */ {4, "A26"},
+ /* 48 */ {4, "A27"},
+ /* 49 */ {4, "A28"},
+ /* 50 */ {4, "A29"},
+ /* 51 */ {4, "A30"},
+ /* 52 */ {4, "A31"},
+ /* 53 */ {4, "B16"},
+ /* 54 */ {4, "B17"},
+ /* 55 */ {4, "B18"},
+ /* 56 */ {4, "B19"},
+ /* 57 */ {4, "B20"},
+ /* 58 */ {4, "B21"},
+ /* 59 */ {4, "B22"},
+ /* 60 */ {4, "B23"},
+ /* 61 */ {4, "B24"},
+ /* 62 */ {4, "B25"},
+ /* 63 */ {4, "B26"},
+ /* 64 */ {4, "B27"},
+ /* 65 */ {4, "B28"},
+ /* 66 */ {4, "B29"},
+ /* 67 */ {4, "B30"},
+ /* 68 */ {4, "B31"},
+ /* 69 */ {4, "AMR"},
+ /* 70 */ {4, "CSR"},
+ /* 71 */ {4, "ISR"},
+ /* 72 */ {4, "ICR"},
+ /* 73 */ {4, "IER"},
+ /* 74 */ {4, "ISTP"},
+ /* 75 */ {4, "IN"},
+ /* 76 */ {4, "OUT"},
+ /* 77 */ {4, "ACR"},
+ /* 78 */ {4, "ADR"},
+ /* 79 */ {4, "FADCR"},
+ /* 80 */ {4, "FAUCR"},
+ /* 81 */ {4, "FMCR"},
+ /* 82 */ {4, "GFPGFR"},
+ /* 83 */ {4, "DIER"},
+ /* 84 */ {4, "REP"},
+ /* 85 */ {4, "TSCL"},
+ /* 86 */ {4, "TSCH"},
+ /* 87 */ {4, "ARP"},
+ /* 88 */ {4, "ILC"},
+ /* 89 */ {4, "RILC"},
+ /* 90 */ {4, "DNUM"},
+ /* 91 */ {4, "SSR"},
+ /* 92 */ {4, "GPLYA"},
+ /* 93 */ {4, "GPLYB"},
+ /* 94 */ {4, "TSR"},
+ /* 95 */ {4, "ITSR"},
+ /* 96 */ {4, "NTSR"},
+ /* 97 */ {4, "EFR"},
+ /* 98 */ {4, "ECR"},
+ /* 99 */ {4, "IERR"},
+ /* 100 */ {4, "DMSG"},
+ /* 101 */ {4, "CMSG"},
+ /* 102 */ {4, "DT_DMA_ADDR"},
+ /* 103 */ {4, "DT_DMA_DATA"},
+ /* 104 */ {4, "DT_DMA_CNTL"},
+ /* 105 */ {4, "TCU_CNTL"},
+ /* 106 */ {4, "RTDX_REC_CNTL"},
+ /* 107 */ {4, "RTDX_XMT_CNTL"},
+ /* 108 */ {4, "RTDX_CFG"},
+ /* 109 */ {4, "RTDX_RDATA"},
+ /* 110 */ {4, "RTDX_WDATA"},
+ /* 111 */ {4, "RTDX_RADDR"},
+ /* 112 */ {4, "RTDX_WADDR"},
+ /* 113 */ {4, "MFREG0"},
+ /* 114 */ {4, "DBG_STAT"},
+ /* 115 */ {4, "BRK_EN"},
+ /* 116 */ {4, "HWBP0_CNT"},
+ /* 117 */ {4, "HWBP0"},
+ /* 118 */ {4, "HWBP1"},
+ /* 119 */ {4, "HWBP2"},
+ /* 120 */ {4, "HWBP3"},
+ /* 121 */ {4, "OVERLAY"},
+ /* 122 */ {4, "PC_PROF"},
+ /* 123 */ {4, "ATSR"},
+ /* 124 */ {4, "TRR"},
+ /* 125 */ {4, "TCRR"},
+ /* 126 */ {4, "DESR"},
+ /* 127 */ {4, "DETR"},
+ /* 128 */ {4, "STRM_HOLD"},
+ /* 129 */ {4, "PDATA_O"},
+ /* 130 */ {4, "TCR"}
+};
+
+/* Find the name for the specified regno. */
+
+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
+ return tic6x_register_info_table[regno].name;
+}
+
+/* Return the GDB type object for the "standard" data type
+ of data in register 'regno'. */
+static struct type *
+tic6x_register_type (struct gdbarch *gdbarch, int regno)
+{
+ gdb_assert (tic6x_register_info_table[regno].size == 4);
+
+ if (regno == 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_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);
+
+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;
+
+ 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;
+ /* Counter of non-stw instructions after first insn ` sub sp, xxx, sp'. */
+ int non_stw_insn_counter = 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 == SP_REGNUM && dst == 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 (0);
+
+ return_pc = pc;
+ }
+ }
+ 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 == SP_REGNUM)
+ {
+ src_reg = tic6x_register_number ((inst >> 23) & 0x1f,
+ INST_S_BIT(inst), 0);
+
+ cache->reg_saved[src_reg] = ((inst >> 13) & 0x1f) << 2;
+ }
+ 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 == DP_REGNUM /* dst is B14 */
+ && base_reg == DP_REGNUM) /* baseR is B14 */
+ {
+ return_pc = pc + 4;
+ }
+
+
+ if (this_frame)
+ {
+ cache->base = get_frame_register_unsigned (this_frame,
+ SP_REGNUM);
+
+ if (cache->reg_saved[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,
+ 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_REGS; i++)
+ if (cache->reg_saved[i] != -1)
+ cache->reg_saved[i] = cache->base + cache->reg_saved[i];
+
+ return return_pc;
+}
+
+/* Given a PC value corresponding to the start of a function, return the PC
+ of the first instruction after the function prologue. */
+
+CORE_ADDR
+tic6x_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc)
+{
+ CORE_ADDR limit_pc;
+ CORE_ADDR func_addr;
+
+ /* See if we can determine the end of the prologue via the symbol table.
+ If so, then return either PC, or the PC after the prologue, whichever is
+ greater. */
+ if (find_pc_partial_function (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, return. */
+ return start_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 breakpoint_illegal_opcode_be;
+ else
+ return breakpoint_illegal_opcode_le;
+ }
+ else
+ return tdep->breakpoint;
+}
+
+int
+gdb_print_insn_tic6x (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;
+}
+
+static CORE_ADDR
+tic6x_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ char buf[8];
+
+ frame_unwind_register (next_frame, PC_REGNUM, buf);
+ return extract_typed_address (buf, builtin_type (gdbarch)->builtin_func_ptr);
+}
+
+static CORE_ADDR
+tic6x_unwind_sp (struct gdbarch *gdbarch, struct frame_info *this_frame)
+{
+ return frame_unwind_register_unsigned (this_frame, 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;
+
+ /* Zero all fields. */
+ cache->base = 0;
+ cache->cfa = 0;
+ cache->pc = 0;
+
+ cache->return_regnum = 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 == PC_REGNUM)
+ regnum = cache->return_regnum;
+
+ if (regnum == 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_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->base = 0;
+ cache->pc = 0;
+
+ cache->return_regnum = RA_REGNUM;
+
+ tic6x_setup_default(cache);
+
+ cache->cfa = get_frame_register_unsigned (this_frame, SP_REGNUM);
+
+ return cache;
+}
+
+/* Our frame ID for a stub frame is the current SP and LR. */
+
+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;
+}
+
+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
+};
+
+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);
+}
+
+static int
+tic6x_condition_true (struct frame_info *frame, unsigned long inst)
+{
+ int register_number;
+ int register_value;
+ static 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;
+}
+
+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 == 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;
+}
+
+/* tic6x_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. We find the target of the coming instruction
+ and breakpoint it. */
+
+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;
+}
+
+/* Adjust the address downward (direction of stack growth) so that it
+ is correctly aligned for a new stack frame. */
+
+static CORE_ADDR
+tic6x_frame_align (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+ return align_down (addr, 8);
+}
+
+/* We don't convert anything at the moment */
+static int
+tic6x_convert_register_p (struct gdbarch *gdbarch, int regnum,
+ struct type *type)
+{
+ return 0;
+}
+
+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 + 0, (char *) to + 0);
+ *optimizedp = *unavailablep = 0;
+ return 1;
+}
+
+static void
+tic6x_value_to_register (struct frame_info *frame, int regnum,
+ struct type *type, const gdb_byte *from)
+{
+ put_frame_register (frame, regnum + 0, (const char *) from + 0);
+}
+
+/* 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, A4_REGNUM, 4 - len, len, valbuf);
+ else
+ regcache_cooked_read (regcache, 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, A4_REGNUM, valbuf+4);
+ regcache_cooked_read (regcache, A5_REGNUM, valbuf);
+ }
+ else
+ {
+ regcache_cooked_read (regcache, A4_REGNUM, valbuf);
+ regcache_cooked_read (regcache, 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, A4_REGNUM, 4 - len, len, valbuf);
+ else
+ regcache_cooked_write (regcache, A4_REGNUM, valbuf);
+ }
+ else if (len <= 8)
+ {
+ if (byte_order == BFD_ENDIAN_BIG)
+ {
+ regcache_cooked_write (regcache, A4_REGNUM, valbuf+4);
+ regcache_cooked_write (regcache, A5_REGNUM, valbuf);
+ }
+ else
+ {
+ regcache_cooked_write (regcache, A4_REGNUM, valbuf);
+ regcache_cooked_write (regcache, A5_REGNUM, valbuf+4);
+ }
+ }
+}
+
+/* Determine, for architecture GDBARCH, how a return value of TYPE
+ should be returned. If it is supposed to be returned in registers,
+ and READBUF is non-zero, read the appropriate value from REGCACHE,
+ and copy it into READBUF. If WRITEBUF is non-zero, write the value
+ from WRITEBUF into REGCACHE. */
+
+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;
+}
+
+/* Assuming THIS_FRAME is a dummy, return the frame ID of that
+ dummy frame. The frame ID's base needs to match the TOS value
+ saved by save_dummy_frame_tos() and returned from
+ tic6x_push_dummy_call, and the PC needs to match the dummy frame's
+ breakpoint. */
+
+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, SP_REGNUM),
+ get_frame_pc (this_frame));
+}
+
+/* Get the alignment requirement of TYPE. */
+int
+tic6x_arg_type_alignment (struct type *type)
+{
+ int len = TYPE_LENGTH (type);
+ enum type_code typecode = TYPE_CODE (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 (0); /* Something wrong. */
+ }
+ 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
+ {
+ char *msg = xstrprintf ("unexpected length %d of type", len);
+ gdb_assert_not_reached (msg);
+ return 0;
+ }
+ }
+}
+
+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, 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
+ {
+ char *msg = xstrprintf ("unexpected type %d of arg %d",
+ typecode, argnum);
+ gdb_assert_not_reached (msg);
+ }
+ }
+ else
+ {
+ char *msg = xstrprintf ("unexpected length %d of arg %d", len,
+ argnum);
+ gdb_assert_not_reached (msg);
+ }
+
+
+ addr = sp + stack_offset;
+ write_memory (addr, val, len);
+ stack_offset += align_up (len, 4);
+ }
+ }
+
+ regcache_cooked_write_signed (regcache, SP_REGNUM, sp);
+
+ /* Return adjusted stack pointer. */
+ return sp;
+}
+
+/* Return 1 if the stack frame has already been destroyed by the
+ function epilogue. */
+
+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 == RA_REGNUM)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Longjmp support. */
+
+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_info_table[i].name);
+
+ /* CSR */
+ valid_p &= tdesc_numbered_register (feature, tdesc_data, i++,
+ tic6x_register_info_table[70].name);
+ valid_p &= tdesc_numbered_register (feature, tdesc_data, i++,
+ tic6x_register_info_table[PC_REGNUM].name);
+
+ 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;
+
+ has_gp = 1;
+ valid_p = 1;
+ for (j = 0; j < 32; j++) /* A16 - A31, B16 - B31 */
+ {
+ char *name = tic6x_register_info_table[j + A16_REGNUM].name;
+
+ valid_p &= tdesc_numbered_register (feature, tdesc_data, i++,
+ name);
+ }
+
+ if (!valid_p)
+ {
+ tdesc_data_cleanup (tdesc_data);
+ return NULL;
+ }
+ }
+
+ feature = tdesc_find_feature (tdesc,
+ "org.gnu.gdb.tic6x.c6xp");
+ if (feature)
+ {
+ char *name = NULL;
+
+ name = tic6x_register_info_table[94].name; /* TSR */
+ valid_p &= tdesc_numbered_register (feature, tdesc_data, i++,
+ name);
+ name = tic6x_register_info_table[88].name; /* ILC */
+ valid_p &= tdesc_numbered_register (feature, tdesc_data, i++,
+ name);
+ name = tic6x_register_info_table[89].name; /* RILC */
+ valid_p &= tdesc_numbered_register (feature, tdesc_data, i++,
+ name);
+
+ 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, SP_REGNUM);
+ set_gdbarch_pc_regnum (gdbarch, 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, gdb_print_insn_tic6x);
+
+ /* Call dummy code. */
+ set_gdbarch_frame_align (gdbarch, tic6x_frame_align);
+
+ set_gdbarch_convert_register_p (gdbarch, tic6x_convert_register_p);
+ 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);
+}
diff --git a/gdb/tic6x-tdep.h b/gdb/tic6x-tdep.h
new file mode 100644
index 0000000..2d2742c
--- /dev/null
+++ b/gdb/tic6x-tdep.h
@@ -0,0 +1,52 @@
+/* GNU/Linux on TI C6x target support.
+ Copyright (C) 2011
+ Free Software Foundation, Inc.
+ 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/>. */
+
+#undef FP_REGNUM
+#define FP_REGNUM 15 /* Frame Pointer: A15 */
+#undef SP_REGNUM
+#define SP_REGNUM 31 /* Stack Pointer: B15 */
+#define RA_REGNUM 19 /* Return address: B3 */
+#define DP_REGNUM 30 /* Data Page Pointer: B14 */
+#undef PC_REGNUM
+#define PC_REGNUM 33
+#define TIC6X_NUM_REGS 131
+#define A4_REGNUM 4
+#define A5_REGNUM 5
+#define B0_REGNUM 16
+#define B4_REGNUM 20
+#define B5_REGNUM 21
+#define A16_REGNUM 37
+
+#define INST_SWE 0x10000000
+
+static const char breakpoint_illegal_opcode_be[] = {0x56, 0x45, 0x43, 0x14};
+static const char breakpoint_illegal_opcode_le[] = {0x14, 0x43, 0x45, 0x56};
+
+/* Target-dependent structure in gdbarch. */
+struct gdbarch_tdep
+{
+ /* Return the expected next PC if FRAME is stopped at a syscall
+ instruction. */
+ CORE_ADDR (*syscall_next_pc) (struct frame_info *frame);
+
+ const char *breakpoint; /* Breakpoint instruction. */
+
+ int has_gp; /* Has general purpose registers A16 - A31 and B16 - B31. */
+};
--
1.7.0.4
^ permalink raw reply [flat|nested] 16+ messages in thread
* ping: [RFA 5/8] New port: TI C6x: gdb port
2011-07-25 11:32 ` Yao Qi
@ 2011-08-03 1:15 ` Yao Qi
0 siblings, 0 replies; 16+ messages in thread
From: Yao Qi @ 2011-08-03 1:15 UTC (permalink / raw)
To: gdb-patches
On 07/25/2011 03:22 PM, Yao Qi wrote:
> On 07/20/2011 10:08 AM, Yao Qi wrote:
>> This patch adds the tdep stuff for tic6x in gdb.
>
> This patch is updated for checking target description and looking for
> features in tic6x_gdbarch_init.
>
> OK for mainline?
>
Ping. Can anyone review this patch?
http://sourceware.org/ml/gdb-patches/2011-07/msg00681.html
--
Yao (é½å°§)
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFA 5/8] New port: TI C6x: gdb port
2011-07-20 2:10 [RFA 5/8] New port: TI C6x: gdb port Yao Qi
2011-07-25 11:32 ` Yao Qi
@ 2011-08-04 12:34 ` Pedro Alves
2011-08-04 15:11 ` Joseph S. Myers
2011-08-06 2:26 ` Yao Qi
1 sibling, 2 replies; 16+ messages in thread
From: Pedro Alves @ 2011-08-04 12:34 UTC (permalink / raw)
To: gdb-patches; +Cc: Yao Qi
On Wednesday 20 July 2011 03:08:38, Yao Qi wrote:
> This patch adds the tdep stuff for tic6x in gdb.
Thanks.
On Wednesday 20 July 2011 03:08:38, Yao Qi wrote:
> +static const char breakpoint_bnop_be[] = {0x00, 0x00, 0xa1, 0x22};
> +static const char breakpoint_bnop_le[] = {0x22, 0xa1, 0x00, 0x00};
Please use gdb_byte instead of char for byte arrays.
> +/* Support unwiding frame in signal trampoline. We don't check sigreturn,
> + since it is not used in kernel. */
Typo unwiding.
> +/* Return the offset of register REGNUM in struct sigconext. Return 0 if no
> + such register in sigcontext. */
Typo sigconext.
> + CORE_ADDR base = sp + TIC6X_SP_RT_SIGFRAME
> + + 4 + 4 /* Pointer type *pinfo and *puc in struct rt_sigframe. */
> + + TIC6X_SIGINFO_SIZE
> + + 4 + 4 /* uc_flags and *uc_link in struct ucontext. */
> + + TIC6X_STACK_T_SIZE;
Wrap the rhs on ()'s and realign.
> +static CORE_ADDR
> +tic6x_linux_syscall_next_pc (struct frame_info *frame)
> +{
> + ULONGEST syscall_number = get_frame_register_unsigned (frame, B0_REGNUM);
> + CORE_ADDR pc = get_frame_pc (frame);
> +
> + if (syscall_number == 139 /* rt_sigreturn */)
> + {
> + if (get_frame_type (frame) == SIGTRAMP_FRAME)
> + return frame_unwind_caller_pc (frame);
> + }
> +
> + return pc + 4;
> +}
Is the frame type check really necessary?
> + /* In tic6x linux kernel, breakpoint instructions varies on different archs.
> + When either macro __TMS320C6XPLUS__ or _TMS320C6400_PLUS is defined,
"In the ... Linux kernel", capital L. Defined where? In the kernel's
sources? Sounds like a volatile implementation detail. Can we spell
out the arch names instead?
> + breakpoint instruction is 0x56454314, which is an illegal opcode.
> + Otherwise, breakpoint instruction is 0x0000a122 (BNOP .S2 0,5). */
On Wednesday 20 July 2011 03:08:38, Yao Qi wrote:
> + Copyright (C) 2010
Add 2011.
> +/* Macros */
> +
> +#define TIC6X_OPCODE_SIZE 4
> +#define TIC6X_FETCH_PACKET_SIZE 32
> +
> +static int arg_regs[] = {4, 20, 6, 22, 8, 24, 10, 26, 12, 28};
> +#define INST_S_BIT(INST) ((INST >> 1) & 1)
> +#define INST_X_BIT(INST) ((INST >> 12) & 1)
> +
> +/* Structures */
> +struct register_info
> +{
> + int size;
> + char *name;
> +};
> +
...
> + CORE_ADDR reg_saved[TIC6X_NUM_REGS];
Missing descriptions. Several functions and variables miss them, I'm
not going to point them all out individually.
> +/* tic6x_register_info_table[i] is the number of bytes of storage in
> + GDB's register array occupied by register i. */
> +static struct register_info tic6x_register_info_table[] = {
> + /* 0 */ {4, "A0"},
> + /* 1 */ {4, "A1"},
> + /* 2 */ {4, "A2"},
> + /* 3 */ {4, "A3"},
> + /* 4 */ {4, "A4"},
> + /* 5 */ {4, "A5"},
> + /* 6 */ {4, "A6"},
> + /* 7 */ {4, "A7"},
> + /* 8 */ {4, "A8"},
> + /* 9 */ {4, "A9"},
> + /* 10 */ {4, "A10"},
> + /* 11 */ {4, "A11"},
> + /* 12 */ {4, "A12"},
> + /* 13 */ {4, "A13"},
> + /* 14 */ {4, "A14"},
> + /* 15 */ {4, "A15"},
> + /* 16 */ {4, "B0"},
> + /* 17 */ {4, "B1"},
> + /* 18 */ {4, "B2"},
> + /* 19 */ {4, "B3"},
> + /* 20 */ {4, "B4"},
> + /* 21 */ {4, "B5"},
> + /* 22 */ {4, "B6"},
> + /* 23 */ {4, "B7"},
> + /* 24 */ {4, "B8"},
> + /* 25 */ {4, "B9"},
> + /* 26 */ {4, "B10"},
> + /* 27 */ {4, "B11"},
> + /* 28 */ {4, "B12"},
> + /* 29 */ {4, "B13"},
> + /* 30 */ {4, "B14"},
> + /* 31 */ {4, "B15"},
> + /* 32 */ {4, "None"},
> + /* 33 */ {4, "PC"},
> + /* 34 */ {4, "IRP"},
> + /* 35 */ {4, "IFR"},
> + /* 36 */ {4, "NPR"},
> + /* 37 */ {4, "A16"},
> + /* 38 */ {4, "A17"},
> + /* 39 */ {4, "A18"},
> + /* 40 */ {4, "A19"},
> + /* 41 */ {4, "A20"},
> + /* 42 */ {4, "A21"},
> + /* 43 */ {4, "A22"},
> + /* 44 */ {4, "A23"},
> + /* 45 */ {4, "A24"},
> + /* 46 */ {4, "A25"},
> + /* 47 */ {4, "A26"},
> + /* 48 */ {4, "A27"},
> + /* 49 */ {4, "A28"},
> + /* 50 */ {4, "A29"},
> + /* 51 */ {4, "A30"},
> + /* 52 */ {4, "A31"},
> + /* 53 */ {4, "B16"},
> + /* 54 */ {4, "B17"},
> + /* 55 */ {4, "B18"},
> + /* 56 */ {4, "B19"},
> + /* 57 */ {4, "B20"},
> + /* 58 */ {4, "B21"},
> + /* 59 */ {4, "B22"},
> + /* 60 */ {4, "B23"},
> + /* 61 */ {4, "B24"},
> + /* 62 */ {4, "B25"},
> + /* 63 */ {4, "B26"},
> + /* 64 */ {4, "B27"},
> + /* 65 */ {4, "B28"},
> + /* 66 */ {4, "B29"},
> + /* 67 */ {4, "B30"},
> + /* 68 */ {4, "B31"},
> + /* 69 */ {4, "AMR"},
> + /* 70 */ {4, "CSR"},
> + /* 71 */ {4, "ISR"},
> + /* 72 */ {4, "ICR"},
> + /* 73 */ {4, "IER"},
> + /* 74 */ {4, "ISTP"},
> + /* 75 */ {4, "IN"},
> + /* 76 */ {4, "OUT"},
> + /* 77 */ {4, "ACR"},
> + /* 78 */ {4, "ADR"},
> + /* 79 */ {4, "FADCR"},
> + /* 80 */ {4, "FAUCR"},
> + /* 81 */ {4, "FMCR"},
> + /* 82 */ {4, "GFPGFR"},
> + /* 83 */ {4, "DIER"},
> + /* 84 */ {4, "REP"},
> + /* 85 */ {4, "TSCL"},
> + /* 86 */ {4, "TSCH"},
> + /* 87 */ {4, "ARP"},
> + /* 88 */ {4, "ILC"},
> + /* 89 */ {4, "RILC"},
> + /* 90 */ {4, "DNUM"},
> + /* 91 */ {4, "SSR"},
> + /* 92 */ {4, "GPLYA"},
> + /* 93 */ {4, "GPLYB"},
> + /* 94 */ {4, "TSR"},
> + /* 95 */ {4, "ITSR"},
> + /* 96 */ {4, "NTSR"},
> + /* 97 */ {4, "EFR"},
> + /* 98 */ {4, "IERR"},
> + /* 99 */ {4, "DMSG"},
> + /* 100 */ {4, "CMSG"},
> + /* 101 */ {4, "DT_DMA_ADDR"},
> + /* 102 */ {4, "DT_DMA_DATA"},
> + /* 103 */ {4, "DT_DMA_CNTL"},
> + /* 104 */ {4, "TCU_CNTL"},
> + /* 105 */ {4, "RTDX_REC_CNTL"},
> + /* 106 */ {4, "RTDX_XMT_CNTL"},
> + /* 107 */ {4, "RTDX_CFG"},
> + /* 108 */ {4, "RTDX_RDATA"},
> + /* 109 */ {4, "RTDX_WDATA"},
> + /* 110 */ {4, "RTDX_RADDR"},
> + /* 111 */ {4, "RTDX_WADDR"},
> + /* 112 */ {4, "MFREG0"},
> + /* 113 */ {4, "DBG_STAT"},
> + /* 114 */ {4, "BRK_EN"},
> + /* 115 */ {4, "HWBP0_CNT"},
> + /* 116 */ {4, "HWBP0"},
> + /* 117 */ {4, "HWBP1"},
> + /* 118 */ {4, "HWBP2"},
> + /* 119 */ {4, "HWBP3"},
> + /* 120 */ {4, "OVERLAY"},
> + /* 121 */ {4, "PC_PROF"},
> + /* 122 */ {4, "ATSR"},
> + /* 123 */ {4, "TRR"},
> + /* 124 */ {4, "TCRR"},
> + /* 125 */ {4, "DESR"},
> + /* 126 */ {4, "DETR"},
> + /* 127 */ {4, ""}
> +};
> +
Seems like there are a lot of non-core registers here. Now that
the gdbserver patch has been updated to send in standard tic6x xml
register description features, all registers the target includes in the
description that are not part of the defined features should not need
to be hardcoded in gdb, since the xml description itself will describe
their sizes and names. See arm-tdep.c:arm_register_name for example.
More comments on this further down.
> +/* Return the GDB type object for the "standard" data type
> + of data in register 'regno'. */
> +static struct type *
> +tic6x_register_type (struct gdbarch *gdbarch, int regno)
Empty line between comment and function missing. Several instances of
this.
> +static void
> +tic6x_setup_default(struct tic6x_unwind_cache *cache)
> +{
Missing space before parens. Several instances of this as well.
> +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)
> +{
...
> +}
OOC, did you investigate using the prologue-value.h machinery for this?
> +CORE_ADDR
> +tic6x_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc)
> +{
> + CORE_ADDR limit_pc;
> + CORE_ADDR func_addr;
> +
> + /* See if we can determine the end of the prologue via the symbol table.
> + If so, then return either PC, or the PC after the prologue, whichever is
> + greater. */
> + if (find_pc_partial_function (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);
> + }
> +
Didn't you mean to call tic6x_analyze_prologue here too?
> + /* Can't determine prologue from the symbol table, return. */
> + return start_pc;
> +}
> +tic6x_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
> +{
> + char buf[8];
> +
> + frame_unwind_register (next_frame, PC_REGNUM, buf);
gdb_byte instead of char again.
> + cache = FRAME_OBSTACK_ZALLOC (struct tic6x_unwind_cache);
> + (*this_prologue_cache) = cache;
> +
> + /* Zero all fields. */
> + cache->base = 0;
> + cache->cfa = 0;
> + cache->pc = 0;
ZALLOC already does that for you.
> + /* If we've worked out where a register is stored then load it from there.
> + */
Odd placement for */. Write as:
/* If we've worked out where a register is stored then load it from
there. */
> +static const struct frame_unwind tic6x_frame_unwind =
> + {
`{' should go on column 0. Here and elsewhere.
> +struct frame_unwind tic6x_stub_unwind = {
> + NORMAL_FRAME,
`{' on next line, on column 0. Here and elsewhere.
> +static int
> +tic6x_condition_true (struct frame_info *frame, unsigned long inst)
> +{
> + int register_number;
> + int register_value;
> + static int register_numbers[8] = {-1, 16, 17, 18, 1, 2, 0, -1};
const and missing spaces:
static const int register_numbers[8] = { -1, 16, 17, 18, 1, 2, 0, -1 };
> + int r = (reg & 15) | ((crosspath ^ side) << 4);
> + if ((reg & 16) != 0) /* A16 - A31, B16-B31 */
> + r += 37;
Mind distracting inconsistencies. Either:
"A16 - A31, B16 - B31"
or:
"A16-A31, B16-B31"
> +/* tic6x_software_single_step() is called just before we want to resume the
Drop the ()'s. Or better just start with "Called just ..."
> + inferior, if we want to single-step it but there is no hardware or kernel
> + single-step support. We find the target of the coming instruction
> + and breakpoint it. */
> +
> +int
> +tic6x_software_single_step (struct frame_info *frame)
> +/* We don't convert anything at the moment */
> +static int
Missing period and double-space, and an empty line. When will
we start converting things? Tonight? :-)
> +tic6x_convert_register_p (struct gdbarch *gdbarch, int regnum,
> + struct type *type)
> +{
> + return 0;
> +}
> +
But more importantly, do you need to install this method at all?
generic_convert_register_p, the default hook, simply returns 0 too.
> +
> +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 + 0, (char *) to + 0);
> + *optimizedp = *unavailablep = 0;
> + return 1;
> +}
> +
> +static void
> +tic6x_value_to_register (struct frame_info *frame, int regnum,
> + struct type *type, const gdb_byte *from)
> +{
> + put_frame_register (frame, regnum + 0, (const char *) from + 0);
> +}
Unnecessary "+ 0"s and casts. Looks like a copy/past from some
implementation that reads a register in parts, like:
get_frame_register (frame, regnum + 0, (char *) to + 0);
get_frame_register (frame, regnum + 1, (char *) to + 4);
> +/* 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)
> +{
Empty line missing. Double space after period. Several other places
miss the double space. Please review all comments.
> +/* Determine, for architecture GDBARCH, how a return value of TYPE
> + should be returned. If it is supposed to be returned in registers,
> + and READBUF is non-zero, read the appropriate value from REGCACHE,
> + and copy it into READBUF. If WRITEBUF is non-zero, write the value
> + from WRITEBUF into REGCACHE. */
> +
> +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)
We'd decided a while ago that we shouldn't be copy&pasting such
comments on all hook implementations, because it leads to comment bit
rot when the parent comment changes. Instead write something like
"This is the implementation of gdbarch method foo". Here and
elsewhere.
> +/* Get the alignment requirement of TYPE. */
> +int
static?
> +tic6x_arg_type_alignment (struct type *type)
> +{
> + int len = TYPE_LENGTH (type);
> + enum type_code typecode = TYPE_CODE (type);
I think a check_typedef is in order.
> + else
> + gdb_assert (0); /* Something wrong. */
Either gdb_assert_not_reached, or internal error. Here and elsewhere
gdb_assert(0) appears.
> + char *msg = xstrprintf ("unexpected length %d of type", len);
> + gdb_assert_not_reached (msg);
> + return 0;
Well, that `return 0' is not reached. :-) Just use internal_error
instead of leaking MSG. There are other instances of this.
> +void
> +_initialize_tic6x_tdep (void)
> +{
> + int i, offset = 0;
Looks like a left over pasto.
> +
> + register_gdbarch_init (bfd_arch_tic6x, tic6x_gdbarch_init);
> +}
> +
> +#undef FP_REGNUM
> +#define FP_REGNUM 15 /* Frame Pointer: A15 */
> +#undef SP_REGNUM
Hmm? The #undef's raise alarm bells. Please prefix the
constants instead.. TIC6X_FP_REGNUM or some such.
> +#define SP_REGNUM 31 /* Stack Pointer: B15 */
> +#define RA_REGNUM 19 /* Return address: B3 */
> +#define DP_REGNUM 30 /* Data Page Pointer: B14 */
> +#undef PC_REGNUM
> +#define PC_REGNUM 33
> +#define Z_REGNUM 127
> +#define TIC6X_NUM_REGS 128
Not sure this numbering is right. Now that gdbserver is
sending standard features in the target description, you should make
gdb validate the target description (usually in gdbarch_init callback,
see arm-tdep.c for example) and assign register numbers with
tdesc_numbered_register and friends. That `128' looked suspiciously
high, as if including registers not present in the standard
tic6x xml features.
> +#define A4_REGNUM 4
> +#define A5_REGNUM 5
> +#define B0_REGNUM 16
> +#define B4_REGNUM 20
> +#define B5_REGNUM 21
> +#define A16_REGNUM 37
> +
> +#define INST_SWE 0x10000000
> +
> +static const char breakpoint_illegal_opcode_be[] = {0x56, 0x45, 0x43, 0x14};
> +static const char breakpoint_illegal_opcode_le[] = {0x14, 0x43, 0x45, 0x56};
Might as well make these extern and defined only in tic6x-tdep.c. A tic6x_
prefix would be good too.
--
Pedro Alves
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFA 5/8] New port: TI C6x: gdb port
2011-08-04 12:34 ` Pedro Alves
@ 2011-08-04 15:11 ` Joseph S. Myers
2011-08-04 17:45 ` Pedro Alves
2011-08-06 2:26 ` Yao Qi
1 sibling, 1 reply; 16+ messages in thread
From: Joseph S. Myers @ 2011-08-04 15:11 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches, Yao Qi
On Thu, 4 Aug 2011, Pedro Alves wrote:
> Seems like there are a lot of non-core registers here. Now that
> the gdbserver patch has been updated to send in standard tic6x xml
> register description features, all registers the target includes in the
> description that are not part of the defined features should not need
> to be hardcoded in gdb, since the xml description itself will describe
> their sizes and names. See arm-tdep.c:arm_register_name for example.
> More comments on this further down.
Won't GDB need a list of how the register names correspond to DWARF
register numbers (which are allocated for all these registers) somewhere?
--
Joseph S. Myers
joseph@codesourcery.com
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFA 5/8] New port: TI C6x: gdb port
2011-08-04 15:11 ` Joseph S. Myers
@ 2011-08-04 17:45 ` Pedro Alves
2011-08-04 19:48 ` Joseph S. Myers
2011-08-05 2:13 ` Yao Qi
0 siblings, 2 replies; 16+ messages in thread
From: Pedro Alves @ 2011-08-04 17:45 UTC (permalink / raw)
To: gdb-patches; +Cc: Joseph S. Myers, Yao Qi
On Thursday 04 August 2011 16:11:35, Joseph S. Myers wrote:
> On Thu, 4 Aug 2011, Pedro Alves wrote:
>
> > Seems like there are a lot of non-core registers here. Now that
> > the gdbserver patch has been updated to send in standard tic6x xml
> > register description features, all registers the target includes in the
> > description that are not part of the defined features should not need
> > to be hardcoded in gdb, since the xml description itself will describe
> > their sizes and names. See arm-tdep.c:arm_register_name for example.
> > More comments on this further down.
>
> Won't GDB need a list of how the register names correspond to DWARF
> register numbers (which are allocated for all these registers) somewhere?
AFAICS, gdbserver only exposes A0-A15, CSR, PC, A16-A31
B16-B31, TSR, ILC, RILC, so I assumed all other registers would be
system dependent non general purpose control registers, but it's
probably then that they are only not "user space" registers, and will
be acessible by bare metal probes/stubs? Linux programs won't access
those, right? I now browsed the ABI's dwarf section and I got that
impression.
The default is to assume 1-1 mapping between gdb numbers and dwarf
numbers, so it's easier to ensure the numbers are the same.
I guess that clears up my comment to the register numbers.
As for names, types and sizes, if the target doesn't include those
registers in the description, then it's as if they don't exist. A
description that reports those extra registers should include those
properties (name, bitsize, type, regnum) per register, so gdb would still
get them from the description. I can't tell if it would make sense
to add a new feature or features for those extra registers though.
--
Pedro Alves
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFA 5/8] New port: TI C6x: gdb port
2011-08-04 17:45 ` Pedro Alves
@ 2011-08-04 19:48 ` Joseph S. Myers
2011-08-05 2:13 ` Yao Qi
1 sibling, 0 replies; 16+ messages in thread
From: Joseph S. Myers @ 2011-08-04 19:48 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches, Yao Qi
On Thu, 4 Aug 2011, Pedro Alves wrote:
> > Won't GDB need a list of how the register names correspond to DWARF
> > register numbers (which are allocated for all these registers) somewhere?
>
> AFAICS, gdbserver only exposes A0-A15, CSR, PC, A16-A31
> B16-B31, TSR, ILC, RILC, so I assumed all other registers would be
> system dependent non general purpose control registers, but it's
> probably then that they are only not "user space" registers, and will
> be acessible by bare metal probes/stubs? Linux programs won't access
> those, right? I now browsed the ABI's dwarf section and I got that
> impression.
I expect most of them are bare metal only - and I don't suppose they are
particularly likely to appear in debug info either, but GDB may as well
know the numbers since they are assigned.
Some of the others *can* however be read/written from userspace (the
documentation of the MVC instruction gives details, for the documented
control registers). (The default in the ABI is for control registers to
be caller-save.)
Looking at the list of control registers, I'd think that at least AMR,
GFPGFR, GPLYA, GPLYB, FADCR, FAUCR, FMCR, SSR are plausibly relevant to
userspace - userspace code using inline assembly or intrinsics, anyway -
and ought to be available from gdbserver (which might of course require
kernel changes to expose them to ptrace).
--
Joseph S. Myers
joseph@codesourcery.com
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFA 5/8] New port: TI C6x: gdb port
2011-08-04 17:45 ` Pedro Alves
2011-08-04 19:48 ` Joseph S. Myers
@ 2011-08-05 2:13 ` Yao Qi
1 sibling, 0 replies; 16+ messages in thread
From: Yao Qi @ 2011-08-05 2:13 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches, Joseph S. Myers
On 08/05/2011 01:45 AM, Pedro Alves wrote:
> On Thursday 04 August 2011 16:11:35, Joseph S. Myers wrote:
>> On Thu, 4 Aug 2011, Pedro Alves wrote:
>>
>>> Seems like there are a lot of non-core registers here. Now that
>>> the gdbserver patch has been updated to send in standard tic6x xml
>>> register description features, all registers the target includes in the
>>> description that are not part of the defined features should not need
>>> to be hardcoded in gdb, since the xml description itself will describe
>>> their sizes and names. See arm-tdep.c:arm_register_name for example.
>>> More comments on this further down.
>>
>> Won't GDB need a list of how the register names correspond to DWARF
>> register numbers (which are allocated for all these registers) somewhere?
>
> AFAICS, gdbserver only exposes A0-A15, CSR, PC, A16-A31
> B16-B31, TSR, ILC, RILC, so I assumed all other registers would be
> system dependent non general purpose control registers, but it's
> probably then that they are only not "user space" registers, and will
> be acessible by bare metal probes/stubs? Linux programs won't access
> those, right? I now browsed the ABI's dwarf section and I got that
> impression.
These registers you mentioned are exposed because they are accessible
via ptrace. In bare mental probe/stub, there are more registers can be
accessed than ptrace.
>
> The default is to assume 1-1 mapping between gdb numbers and dwarf
> numbers, so it's easier to ensure the numbers are the same.
> I guess that clears up my comment to the register numbers.
> As for names, types and sizes, if the target doesn't include those
> registers in the description, then it's as if they don't exist. A
> description that reports those extra registers should include those
> properties (name, bitsize, type, regnum) per register, so gdb would still
> get them from the description. I can't tell if it would make sense
> to add a new feature or features for those extra registers though.
>
The current features of tic6x is sufficient, because they are all
registers that ptrace can access so far. It makes no sense to list some
registers but we can't access.
Bare mental probes can access more control registers, so we may need new
feature(s), but I don't think this patch series should cover this.
--
Yao (é½å°§)
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFA 5/8] New port: TI C6x: gdb port
2011-08-04 12:34 ` Pedro Alves
2011-08-04 15:11 ` Joseph S. Myers
@ 2011-08-06 2:26 ` Yao Qi
2011-08-08 13:04 ` Pedro Alves
1 sibling, 1 reply; 16+ messages in thread
From: Yao Qi @ 2011-08-06 2:26 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 17455 bytes --]
On 08/04/2011 08:33 PM, Pedro Alves wrote:
> On Wednesday 20 July 2011 03:08:38, Yao Qi wrote:
>> This patch adds the tdep stuff for tic6x in gdb.
>
> Thanks.
>
> On Wednesday 20 July 2011 03:08:38, Yao Qi wrote:
>> +static const char breakpoint_bnop_be[] = {0x00, 0x00, 0xa1, 0x22};
>> +static const char breakpoint_bnop_le[] = {0x22, 0xa1, 0x00, 0x00};
>
> Please use gdb_byte instead of char for byte arrays.
>
OK.
>> +/* Support unwiding frame in signal trampoline. We don't check sigreturn,
>> + since it is not used in kernel. */
>
> Typo unwiding.
>
Fixed.
>
>> +/* Return the offset of register REGNUM in struct sigconext. Return 0 if no
>> + such register in sigcontext. */
>
> Typo sigconext.
>
Fixed.
>> + CORE_ADDR base = sp + TIC6X_SP_RT_SIGFRAME
>> + + 4 + 4 /* Pointer type *pinfo and *puc in struct rt_sigframe. */
>> + + TIC6X_SIGINFO_SIZE
>> + + 4 + 4 /* uc_flags and *uc_link in struct ucontext. */
>> + + TIC6X_STACK_T_SIZE;
>
> Wrap the rhs on ()'s and realign.
>
OK.
>> +static CORE_ADDR
>> +tic6x_linux_syscall_next_pc (struct frame_info *frame)
>> +{
>> + ULONGEST syscall_number = get_frame_register_unsigned (frame, B0_REGNUM);
>> + CORE_ADDR pc = get_frame_pc (frame);
>> +
>> + if (syscall_number == 139 /* rt_sigreturn */)
>> + {
>> + if (get_frame_type (frame) == SIGTRAMP_FRAME)
>> + return frame_unwind_caller_pc (frame);
>> + }
>> +
>> + return pc + 4;
>> +}
>
> Is the frame type check really necessary?
>
It is not really necessary. When syscall nuber is 139, the frame should
be SIGTRAMP_FRAME. Remove this frame type check.
>> + /* In tic6x linux kernel, breakpoint instructions varies on different archs.
>> + When either macro __TMS320C6XPLUS__ or _TMS320C6400_PLUS is defined,
>
> "In the ... Linux kernel", capital L. Defined where? In the kernel's
> sources? Sounds like a volatile implementation detail. Can we spell
> out the arch names instead?
>
>
Yes, it is a little confusing to describe the kernel implementation
details here. The comment is changed to:
"In tic6x Linux kernel, breakpoint instructions varies on different archs.
On C64x+ and C67x+, breakpoint instruction is 0x56454314, which is an
illegal opcode. On other arch, breakpoint instruction is 0x0000a122
(BNOP .S2 0,5)."
>> + breakpoint instruction is 0x56454314, which is an illegal opcode.
>> + Otherwise, breakpoint instruction is 0x0000a122 (BNOP .S2 0,5). */
>
> On Wednesday 20 July 2011 03:08:38, Yao Qi wrote:
>> + Copyright (C) 2010
>
> Add 2011.
>
Added.
>
>> + CORE_ADDR reg_saved[TIC6X_NUM_REGS];
>
> Missing descriptions. Several functions and variables miss them, I'm
> not going to point them all out individually.
>
tic6x_analyze_prologue, tic6x_condition_true, and tic6x_register_number
are documented with comments.
There are still two struct frame_unwind variables and their functions
undocumented. It looks they are self-described, so I don't comment them.
>> +/* tic6x_register_info_table[i] is the number of bytes of storage in
>> + GDB's register array occupied by register i. */
>> +static struct register_info tic6x_register_info_table[] = {
>> + /* 0 */ {4, "A0"},
>> + /* 1 */ {4, "A1"},
>> + /* 2 */ {4, "A2"},
>> + /* 3 */ {4, "A3"},
>> + /* 4 */ {4, "A4"},
>> + /* 5 */ {4, "A5"},
>> + /* 6 */ {4, "A6"},
>> + /* 7 */ {4, "A7"},
>> + /* 8 */ {4, "A8"},
>> + /* 9 */ {4, "A9"},
>> + /* 10 */ {4, "A10"},
>> + /* 11 */ {4, "A11"},
>> + /* 12 */ {4, "A12"},
>> + /* 13 */ {4, "A13"},
>> + /* 14 */ {4, "A14"},
>> + /* 15 */ {4, "A15"},
>> + /* 16 */ {4, "B0"},
>> + /* 17 */ {4, "B1"},
>> + /* 18 */ {4, "B2"},
>> + /* 19 */ {4, "B3"},
>> + /* 20 */ {4, "B4"},
>> + /* 21 */ {4, "B5"},
>> + /* 22 */ {4, "B6"},
>> + /* 23 */ {4, "B7"},
>> + /* 24 */ {4, "B8"},
>> + /* 25 */ {4, "B9"},
>> + /* 26 */ {4, "B10"},
>> + /* 27 */ {4, "B11"},
>> + /* 28 */ {4, "B12"},
>> + /* 29 */ {4, "B13"},
>> + /* 30 */ {4, "B14"},
>> + /* 31 */ {4, "B15"},
>> + /* 32 */ {4, "None"},
>> + /* 33 */ {4, "PC"},
>> + /* 34 */ {4, "IRP"},
>> + /* 35 */ {4, "IFR"},
>> + /* 36 */ {4, "NPR"},
>> + /* 37 */ {4, "A16"},
>> + /* 38 */ {4, "A17"},
>> + /* 39 */ {4, "A18"},
>> + /* 40 */ {4, "A19"},
>> + /* 41 */ {4, "A20"},
>> + /* 42 */ {4, "A21"},
>> + /* 43 */ {4, "A22"},
>> + /* 44 */ {4, "A23"},
>> + /* 45 */ {4, "A24"},
>> + /* 46 */ {4, "A25"},
>> + /* 47 */ {4, "A26"},
>> + /* 48 */ {4, "A27"},
>> + /* 49 */ {4, "A28"},
>> + /* 50 */ {4, "A29"},
>> + /* 51 */ {4, "A30"},
>> + /* 52 */ {4, "A31"},
>> + /* 53 */ {4, "B16"},
>> + /* 54 */ {4, "B17"},
>> + /* 55 */ {4, "B18"},
>> + /* 56 */ {4, "B19"},
>> + /* 57 */ {4, "B20"},
>> + /* 58 */ {4, "B21"},
>> + /* 59 */ {4, "B22"},
>> + /* 60 */ {4, "B23"},
>> + /* 61 */ {4, "B24"},
>> + /* 62 */ {4, "B25"},
>> + /* 63 */ {4, "B26"},
>> + /* 64 */ {4, "B27"},
>> + /* 65 */ {4, "B28"},
>> + /* 66 */ {4, "B29"},
>> + /* 67 */ {4, "B30"},
>> + /* 68 */ {4, "B31"},
>> + /* 69 */ {4, "AMR"},
>> + /* 70 */ {4, "CSR"},
>> + /* 71 */ {4, "ISR"},
>> + /* 72 */ {4, "ICR"},
>> + /* 73 */ {4, "IER"},
>> + /* 74 */ {4, "ISTP"},
>> + /* 75 */ {4, "IN"},
>> + /* 76 */ {4, "OUT"},
>> + /* 77 */ {4, "ACR"},
>> + /* 78 */ {4, "ADR"},
>> + /* 79 */ {4, "FADCR"},
>> + /* 80 */ {4, "FAUCR"},
>> + /* 81 */ {4, "FMCR"},
>> + /* 82 */ {4, "GFPGFR"},
>> + /* 83 */ {4, "DIER"},
>> + /* 84 */ {4, "REP"},
>> + /* 85 */ {4, "TSCL"},
>> + /* 86 */ {4, "TSCH"},
>> + /* 87 */ {4, "ARP"},
>> + /* 88 */ {4, "ILC"},
>> + /* 89 */ {4, "RILC"},
>> + /* 90 */ {4, "DNUM"},
>> + /* 91 */ {4, "SSR"},
>> + /* 92 */ {4, "GPLYA"},
>> + /* 93 */ {4, "GPLYB"},
>> + /* 94 */ {4, "TSR"},
>> + /* 95 */ {4, "ITSR"},
>> + /* 96 */ {4, "NTSR"},
>> + /* 97 */ {4, "EFR"},
>> + /* 98 */ {4, "IERR"},
>> + /* 99 */ {4, "DMSG"},
>> + /* 100 */ {4, "CMSG"},
>> + /* 101 */ {4, "DT_DMA_ADDR"},
>> + /* 102 */ {4, "DT_DMA_DATA"},
>> + /* 103 */ {4, "DT_DMA_CNTL"},
>> + /* 104 */ {4, "TCU_CNTL"},
>> + /* 105 */ {4, "RTDX_REC_CNTL"},
>> + /* 106 */ {4, "RTDX_XMT_CNTL"},
>> + /* 107 */ {4, "RTDX_CFG"},
>> + /* 108 */ {4, "RTDX_RDATA"},
>> + /* 109 */ {4, "RTDX_WDATA"},
>> + /* 110 */ {4, "RTDX_RADDR"},
>> + /* 111 */ {4, "RTDX_WADDR"},
>> + /* 112 */ {4, "MFREG0"},
>> + /* 113 */ {4, "DBG_STAT"},
>> + /* 114 */ {4, "BRK_EN"},
>> + /* 115 */ {4, "HWBP0_CNT"},
>> + /* 116 */ {4, "HWBP0"},
>> + /* 117 */ {4, "HWBP1"},
>> + /* 118 */ {4, "HWBP2"},
>> + /* 119 */ {4, "HWBP3"},
>> + /* 120 */ {4, "OVERLAY"},
>> + /* 121 */ {4, "PC_PROF"},
>> + /* 122 */ {4, "ATSR"},
>> + /* 123 */ {4, "TRR"},
>> + /* 124 */ {4, "TCRR"},
>> + /* 125 */ {4, "DESR"},
>> + /* 126 */ {4, "DETR"},
>> + /* 127 */ {4, ""}
>> +};
>> +
>
> Seems like there are a lot of non-core registers here. Now that
> the gdbserver patch has been updated to send in standard tic6x xml
> register description features, all registers the target includes in the
> description that are not part of the defined features should not need
> to be hardcoded in gdb, since the xml description itself will describe
> their sizes and names. See arm-tdep.c:arm_register_name for example.
> More comments on this further down.
>
Yes, I kept these non-core registers here in order to handle the case
when gdbserver or other stubs don't have such xml description file. I
think it again, seems we don't have to worry that gdbserver or other
stubs doesn't have such xml description file.
In my new patch, only core-registers are kept here, and other registers
are removed.
>> +/* Return the GDB type object for the "standard" data type
>> + of data in register 'regno'. */
>> +static struct type *
>> +tic6x_register_type (struct gdbarch *gdbarch, int regno)
>
> Empty line between comment and function missing. Several instances of
> this.
>
Fixed it here and elsewhere (tic6x_get_next_pc and
tic6x_extract_return_value).
>> +static void
>> +tic6x_setup_default(struct tic6x_unwind_cache *cache)
>> +{
>
> Missing space before parens. Several instances of this as well.
>
Fixed.
>> +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)
>> +{
>
> ...
>
>> +}
>
> OOC, did you investigate using the prologue-value.h machinery for this?
>
No. c6x prologue is quite simple, so I parse the binary opcode directly.
>> +CORE_ADDR
>> +tic6x_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc)
>> +{
>> + CORE_ADDR limit_pc;
>> + CORE_ADDR func_addr;
>> +
>> + /* See if we can determine the end of the prologue via the symbol table.
>> + If so, then return either PC, or the PC after the prologue, whichever is
>> + greater. */
>> + if (find_pc_partial_function (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);
>> + }
>> +
>
> Didn't you mean to call tic6x_analyze_prologue here too?
>
>> + /* Can't determine prologue from the symbol table, return. */
>> + return start_pc;
>> +}
>
>
>
Yes, tic6x_analyze_prologue is needed here. The reason I didn't call
tic6x_analyze_prologue here is that the test results has no difference.
I thought we can determine prologue from symbol table quite well, so I
didn't chain the prologue analyzer.
It is no harm, at lease, to chain prologue analyzer here. So, I add a
call to tic6x_analyze_prologue here in new patch.
>> +tic6x_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
>> +{
>> + char buf[8];
>> +
>> + frame_unwind_register (next_frame, PC_REGNUM, buf);
>
> gdb_byte instead of char again.
>
>
OK, fixed.
>
>> + cache = FRAME_OBSTACK_ZALLOC (struct tic6x_unwind_cache);
>> + (*this_prologue_cache) = cache;
>> +
>> + /* Zero all fields. */
>> + cache->base = 0;
>> + cache->cfa = 0;
>> + cache->pc = 0;
>
> ZALLOC already does that for you.
>
>
Right, fixed.
>
>> + /* If we've worked out where a register is stored then load it from there.
>> + */
>
> Odd placement for */. Write as:
>
> /* If we've worked out where a register is stored then load it from
> there. */
>
>
Oh, yes. Fixed.
>> +static const struct frame_unwind tic6x_frame_unwind =
>> + {
>
> `{' should go on column 0. Here and elsewhere.
>
>
>> +struct frame_unwind tic6x_stub_unwind = {
>> + NORMAL_FRAME,
>
> `{' on next line, on column 0. Here and elsewhere.
>
Fixed.
>> +static int
>> +tic6x_condition_true (struct frame_info *frame, unsigned long inst)
>> +{
>> + int register_number;
>> + int register_value;
>> + static int register_numbers[8] = {-1, 16, 17, 18, 1, 2, 0, -1};
>
> const and missing spaces:
>
> static const int register_numbers[8] = { -1, 16, 17, 18, 1, 2, 0, -1 };
>
>
Fixed.
>
>> + int r = (reg & 15) | ((crosspath ^ side) << 4);
>> + if ((reg & 16) != 0) /* A16 - A31, B16-B31 */
>> + r += 37;
>
> Mind distracting inconsistencies. Either:
>
> "A16 - A31, B16 - B31"
>
> or:
>
> "A16-A31, B16-B31"
>
>
Fixed.
>
>> +/* tic6x_software_single_step() is called just before we want to resume the
>
> Drop the ()'s. Or better just start with "Called just ..."
>
() is removed.
>> +/* We don't convert anything at the moment */
>> +static int
>
> Missing period and double-space, and an empty line. When will
> we start converting things? Tonight? :-)
>
>> +tic6x_convert_register_p (struct gdbarch *gdbarch, int regnum,
>> + struct type *type)
>> +{
>> + return 0;
>> +}
>> +
>
> But more importantly, do you need to install this method at all?
> generic_convert_register_p, the default hook, simply returns 0 too.
>
>
Right, this function can be removed.
>> +
>> +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 + 0, (char *) to + 0);
>> + *optimizedp = *unavailablep = 0;
>> + return 1;
>> +}
>> +
>> +static void
>> +tic6x_value_to_register (struct frame_info *frame, int regnum,
>> + struct type *type, const gdb_byte *from)
>> +{
>> + put_frame_register (frame, regnum + 0, (const char *) from + 0);
>> +}
>
> Unnecessary "+ 0"s and casts. Looks like a copy/past from some
> implementation that reads a register in parts, like:
>
> get_frame_register (frame, regnum + 0, (char *) to + 0);
> get_frame_register (frame, regnum + 1, (char *) to + 4);
>
>
Remove "+ 0".
>> +/* 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)
>> +{
>
> Empty line missing. Double space after period. Several other places
> miss the double space. Please review all comments.
>
>
Fixed.
>> +/* Determine, for architecture GDBARCH, how a return value of TYPE
>> + should be returned. If it is supposed to be returned in registers,
>> + and READBUF is non-zero, read the appropriate value from REGCACHE,
>> + and copy it into READBUF. If WRITEBUF is non-zero, write the value
>> + from WRITEBUF into REGCACHE. */
>> +
>> +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)
>
> We'd decided a while ago that we shouldn't be copy&pasting such
> comments on all hook implementations, because it leads to comment bit
> rot when the parent comment changes. Instead write something like
> "This is the implementation of gdbarch method foo". Here and
> elsewhere.
>
Fixed.
>> +/* Get the alignment requirement of TYPE. */
>> +int
>
> static?
>
Oh, yes. Fixed.
>> +tic6x_arg_type_alignment (struct type *type)
>> +{
>> + int len = TYPE_LENGTH (type);
>> + enum type_code typecode = TYPE_CODE (type);
>
> I think a check_typedef is in order.
>
>
This line is changed to:
enum type_code typecode = TYPE_CODE (check_typedef (type));
>> + else
>> + gdb_assert (0); /* Something wrong. */
>
> Either gdb_assert_not_reached, or internal error. Here and elsewhere
> gdb_assert(0) appears.
>
Fixed.
>> + char *msg = xstrprintf ("unexpected length %d of type", len);
>> + gdb_assert_not_reached (msg);
>> + return 0;
>
> Well, that `return 0' is not reached. :-) Just use internal_error
> instead of leaking MSG. There are other instances of this.
>
Fixed.
>> +void
>> +_initialize_tic6x_tdep (void)
>> +{
>> + int i, offset = 0;
>
> Looks like a left over pasto.
>
Looks like the patch you reviewed is sent on July 20th, while I sent an
updated version on July 25th, in which these two local variables are
removed.
>> +
>> + register_gdbarch_init (bfd_arch_tic6x, tic6x_gdbarch_init);
>> +}
>
>
>
>> +
>> +#undef FP_REGNUM
>> +#define FP_REGNUM 15 /* Frame Pointer: A15 */
>> +#undef SP_REGNUM
>
> Hmm? The #undef's raise alarm bells. Please prefix the
> constants instead.. TIC6X_FP_REGNUM or some such.
>
Add prefix "TIC6X_" to these macros.
>> +#define SP_REGNUM 31 /* Stack Pointer: B15 */
>> +#define RA_REGNUM 19 /* Return address: B3 */
>> +#define DP_REGNUM 30 /* Data Page Pointer: B14 */
>> +#undef PC_REGNUM
>> +#define PC_REGNUM 33
>> +#define Z_REGNUM 127
>> +#define TIC6X_NUM_REGS 128
>
> Not sure this numbering is right. Now that gdbserver is
> sending standard features in the target description, you should make
> gdb validate the target description (usually in gdbarch_init callback,
> see arm-tdep.c for example) and assign register numbers with
> tdesc_numbered_register and friends. That `128' looked suspiciously
> high, as if including registers not present in the standard
> tic6x xml features.
>
This bits are in the patch I sent on July 20th, but they are changed in
a new version sent on July 25th. In the latest version (sent on July
25th), it did what you suggested here (assign register numbers with
tdesc_numbered_register).
In my new patch, TIC6X_NUM_REGS is set to 69, which is equivalent to the
max number of register that gdbserver can access via ptrace.
TIC6X_NUM_REGS is used for set_gdbarch_num_regs.
>> +#define A4_REGNUM 4
>> +#define A5_REGNUM 5
>> +#define B0_REGNUM 16
>> +#define B4_REGNUM 20
>> +#define B5_REGNUM 21
>> +#define A16_REGNUM 37
>> +
>> +#define INST_SWE 0x10000000
>> +
>> +static const char breakpoint_illegal_opcode_be[] = {0x56, 0x45, 0x43, 0x14};
>> +static const char breakpoint_illegal_opcode_le[] = {0x14, 0x43, 0x45, 0x56};
>
> Might as well make these extern and defined only in tic6x-tdep.c. A tic6x_
> prefix would be good too.
>
These macros are changed to enum, and prefixed with "TIC6X_". Moved
breakpoint definition to tic6x-tdep.c.
--
Yao (é½å°§)
[-- Attachment #2: 0006-gdb-tic6x-port.patch --]
[-- Type: text/x-patch, Size: 53428 bytes --]
2011-08-06 Andrew Jenner <andrew@codesourcery.com>
Yao Qi <yao@codesourcery.com>
gdb/
* tic6x-linux-tdep.c: New file.
* tic6x-tdep.c: New file.
* tic6x-tdep.h: New file.
---
gdb/tic6x-linux-tdep.c | 227 ++++++++
gdb/tic6x-tdep.c | 1414 ++++++++++++++++++++++++++++++++++++++++++++++++
gdb/tic6x-tdep.h | 54 ++
3 files changed, 1695 insertions(+), 0 deletions(-)
create mode 100644 gdb/tic6x-linux-tdep.c
create mode 100644 gdb/tic6x-tdep.c
create mode 100644 gdb/tic6x-tdep.h
diff --git a/gdb/tic6x-linux-tdep.c b/gdb/tic6x-linux-tdep.c
new file mode 100644
index 0000000..119b731
--- /dev/null
+++ b/gdb/tic6x-linux-tdep.c
@@ -0,0 +1,227 @@
+/* GNU/Linux on TI C6x target support.
+ Copyright (C) 2011
+ Free Software Foundation, Inc.
+ 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 "solib.h"
+#include "osabi.h"
+#include "linux-tdep.h"
+#include "tic6x-tdep.h"
+#include "trad-frame.h"
+#include "tramp-frame.h"
+#include "gdb_assert.h"
+#include "elf-bfd.h"
+#include "elf/tic6x.h"
+
+#include "features/tic6x-c64xp-linux.c"
+#include "features/tic6x-c64x-linux.c"
+#include "features/tic6x-c62x-linux.c"
+
+/* The offset from rt_sigframe pointer to SP register. */
+#define TIC6X_SP_RT_SIGFRAME 8
+/* Size of struct siginfo info. */
+#define TIC6X_SIGINFO_SIZE 128
+/* Size of type stack_t, which contains three fields of type void*, int, and
+ size_t respectively. */
+#define TIC6X_STACK_T_SIZE (3 * 4)
+
+const gdb_byte tic6x_bkpt_illegal_opcode_be[] = { 0x56, 0x45, 0x43, 0x14 };
+const gdb_byte tic6x_bkpt_illegal_opcode_le[] = { 0x14, 0x43, 0x45, 0x56 };
+
+static const gdb_byte tic6x_bkpt_bnop_be[] = { 0x00, 0x00, 0xa1, 0x22 };
+static const gdb_byte tic6x_bkpt_bnop_le[] = { 0x22, 0xa1, 0x00, 0x00 };
+
+/* Return the offset of register REGNUM in struct sigcontext. Return 0 if no
+ such register in sigcontext. */
+
+static unsigned int
+tic6x_register_sigcontext_offset (unsigned int regnum, struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ if (regnum == TIC6X_A4_REGNUM || regnum == TIC6X_A4_REGNUM + 2
+ || regnum == TIC6X_A4_REGNUM + 4)
+ return 4 * (regnum - TIC6X_A4_REGNUM + 2); /* A4, A6, A8 */
+ else if (regnum == TIC6X_A5_REGNUM || regnum == TIC6X_A5_REGNUM + 2
+ || regnum == TIC6X_A5_REGNUM + 4)
+ return 4 * (regnum - TIC6X_A5_REGNUM + 12); /* A5, A7, A9 */
+ else if (regnum == TIC6X_B4_REGNUM || regnum == TIC6X_B4_REGNUM + 2
+ || regnum == TIC6X_B4_REGNUM + 4)
+ return 4 * (regnum - TIC6X_B4_REGNUM + 3); /* B4, B6, B8 */
+ else if (regnum == TIC6X_B5_REGNUM || regnum == TIC6X_B5_REGNUM + 2
+ || regnum == TIC6X_B5_REGNUM + 4)
+ return 4 * (regnum - TIC6X_B5_REGNUM + 19); /* B5, B7, B9 */
+ else if (regnum >= 0 && regnum < TIC6X_A4_REGNUM)
+ return 4 * (regnum - 0 + 8); /* A0 - A3 */
+ else if (regnum >= TIC6X_B0_REGNUM && regnum < TIC6X_B4_REGNUM)
+ return 4 * (regnum - TIC6X_B0_REGNUM + 15); /* B0 - B3 */
+ else if (regnum >= 34 && regnum < 34 + 32)
+ return 4 * (regnum - 34 + 23); /* A16 - A31, B16 -B31 */
+ else if (regnum == TIC6X_PC_REGNUM)
+ return 4 * (tdep->has_gp ? 55 : 23);
+ else if (regnum == TIC6X_SP_REGNUM)
+ return 4;
+
+ return 0;
+}
+
+/* Support unwinding frame in signal trampoline. We don't check sigreturn,
+ since it is not used in kernel. */
+
+static void
+tic6x_linux_rt_sigreturn_init (const struct tramp_frame *self,
+ struct frame_info *this_frame,
+ struct trad_frame_cache *this_cache,
+ CORE_ADDR func)
+{
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ CORE_ADDR sp = get_frame_register_unsigned (this_frame, TIC6X_SP_REGNUM);
+ /* The base of struct sigcontext is computed by examining the definition of
+ struct rt_sigframe in linux kernel source arch/c6x/kernel/signal.c. */
+ CORE_ADDR base = (sp + TIC6X_SP_RT_SIGFRAME
+ /* Pointer type *pinfo and *puc in struct rt_sigframe. */
+ + 4 + 4
+ + TIC6X_SIGINFO_SIZE
+ + 4 + 4 /* uc_flags and *uc_link in struct ucontext. */
+ + TIC6X_STACK_T_SIZE);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ unsigned int reg_offset;
+ unsigned int i;
+
+ for (i = 0; i < 10; i++) /* A0 - A9 */
+ {
+ reg_offset = tic6x_register_sigcontext_offset (i, gdbarch);
+ gdb_assert (reg_offset != 0);
+
+ trad_frame_set_reg_addr (this_cache, i, base + reg_offset);
+ }
+
+ for (i = TIC6X_B0_REGNUM; i < TIC6X_B0_REGNUM + 10; i++) /* B0 - B9 */
+ {
+ reg_offset = tic6x_register_sigcontext_offset (i, gdbarch);
+ gdb_assert (reg_offset != 0);
+
+ trad_frame_set_reg_addr (this_cache, i, base + reg_offset);
+ }
+
+ if (tdep->has_gp)
+ for (i = 34; i < 34 + 32; i++) /* A16 - A31, B16 - B31 */
+ {
+ reg_offset = tic6x_register_sigcontext_offset (i, gdbarch);
+ gdb_assert (reg_offset != 0);
+
+ trad_frame_set_reg_addr (this_cache, i, base + reg_offset);
+ }
+
+ trad_frame_set_reg_addr (this_cache, TIC6X_PC_REGNUM,
+ base + tic6x_register_sigcontext_offset (TIC6X_PC_REGNUM,
+ gdbarch));
+ trad_frame_set_reg_addr (this_cache, TIC6X_SP_REGNUM,
+ base + tic6x_register_sigcontext_offset (TIC6X_SP_REGNUM,
+ gdbarch));
+
+ /* Save a frame ID. */
+ trad_frame_set_id (this_cache, frame_id_build (sp, func));
+}
+
+static struct tramp_frame tic6x_linux_rt_sigreturn_tramp_frame =
+{
+ SIGTRAMP_FRAME,
+ 4,
+ {
+ {0x000045aa, 0x0fffffff}, /* mvk .S2 139,b0 */
+ {0x10000000, -1}, /* swe */
+ {TRAMP_SENTINEL_INSN}
+ },
+ tic6x_linux_rt_sigreturn_init
+};
+
+/* When FRAME is at a syscall instruction, return the PC of the next
+ instruction to be executed. */
+
+static CORE_ADDR
+tic6x_linux_syscall_next_pc (struct frame_info *frame)
+{
+ ULONGEST syscall_number = get_frame_register_unsigned (frame,
+ TIC6X_B0_REGNUM);
+ CORE_ADDR pc = get_frame_pc (frame);
+
+ if (syscall_number == 139 /* rt_sigreturn */)
+ return frame_unwind_caller_pc (frame);
+
+ return pc + 4;
+}
+
+
+extern struct target_so_ops dsbt_so_ops;
+static void
+tic6x_uclinux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ linux_init_abi (info, gdbarch);
+
+ /* Shared library handling. */
+ set_solib_ops (gdbarch, &dsbt_so_ops);
+
+ tdep->syscall_next_pc = tic6x_linux_syscall_next_pc;
+
+#ifdef HAVE_ELF
+ /* In tic6x Linux kernel, breakpoint instructions varies on different archs.
+ On C64x+ and C67x+, breakpoint instruction is 0x56454314, which is an
+ illegal opcode. On other arch, breakpoint instruction is 0x0000a122
+ (BNOP .S2 0,5). */
+ if (info.abfd)
+ switch (bfd_elf_get_obj_attr_int (info.abfd, OBJ_ATTR_PROC, Tag_ISA))
+ {
+ case C6XABI_Tag_ISA_C64XP:
+ case C6XABI_Tag_ISA_C67XP:
+ if (info.byte_order == BFD_ENDIAN_BIG)
+ tdep->breakpoint = tic6x_bkpt_illegal_opcode_be;
+ else
+ tdep->breakpoint = tic6x_bkpt_illegal_opcode_le;
+ break;
+ default:
+ {
+ if (info.byte_order == BFD_ENDIAN_BIG)
+ tdep->breakpoint = tic6x_bkpt_bnop_be;
+ else
+ tdep->breakpoint = tic6x_bkpt_bnop_le;
+ }
+ }
+#endif
+
+ /* Signal trampoline support. */
+ tramp_frame_prepend_unwinder (gdbarch,
+ &tic6x_linux_rt_sigreturn_tramp_frame);
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_tic6x_linux_tdep;
+
+void
+_initialize_tic6x_linux_tdep (void)
+{
+ gdbarch_register_osabi (bfd_arch_tic6x, 0, GDB_OSABI_LINUX,
+ tic6x_uclinux_init_abi);
+
+ initialize_tdesc_tic6x_c64xp_linux ();
+ initialize_tdesc_tic6x_c64x_linux ();
+ initialize_tdesc_tic6x_c62x_linux ();
+}
diff --git a/gdb/tic6x-tdep.c b/gdb/tic6x-tdep.c
new file mode 100644
index 0000000..7362d69
--- /dev/null
+++ b/gdb/tic6x-tdep.c
@@ -0,0 +1,1414 @@
+/* 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"
+
+/* Macros */
+
+#define TIC6X_OPCODE_SIZE 4
+#define TIC6X_FETCH_PACKET_SIZE 32
+
+static int arg_regs[] = { 4, 20, 6, 22, 8, 24, 10, 26, 12, 28 };
+
+#define INST_S_BIT(INST) ((INST >> 1) & 1)
+#define INST_X_BIT(INST) ((INST >> 12) & 1)
+
+/* Structures */
+struct register_info
+{
+ int size;
+ char *name;
+};
+
+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];
+};
+
+
+/* tic6x_register_info_table[i] is the number of bytes of storage in
+ GDB's register array occupied by register i. */
+static struct register_info tic6x_register_info_table[] =
+{
+ /* 0 */ {4, "A0"},
+ /* 1 */ {4, "A1"},
+ /* 2 */ {4, "A2"},
+ /* 3 */ {4, "A3"},
+ /* 4 */ {4, "A4"},
+ /* 5 */ {4, "A5"},
+ /* 6 */ {4, "A6"},
+ /* 7 */ {4, "A7"},
+ /* 8 */ {4, "A8"},
+ /* 9 */ {4, "A9"},
+ /* 10 */ {4, "A10"},
+ /* 11 */ {4, "A11"},
+ /* 12 */ {4, "A12"},
+ /* 13 */ {4, "A13"},
+ /* 14 */ {4, "A14"},
+ /* 15 */ {4, "A15"},
+ /* 16 */ {4, "B0"},
+ /* 17 */ {4, "B1"},
+ /* 18 */ {4, "B2"},
+ /* 19 */ {4, "B3"},
+ /* 20 */ {4, "B4"},
+ /* 21 */ {4, "B5"},
+ /* 22 */ {4, "B6"},
+ /* 23 */ {4, "B7"},
+ /* 24 */ {4, "B8"},
+ /* 25 */ {4, "B9"},
+ /* 26 */ {4, "B10"},
+ /* 27 */ {4, "B11"},
+ /* 28 */ {4, "B12"},
+ /* 29 */ {4, "B13"},
+ /* 30 */ {4, "B14"},
+ /* 31 */ {4, "B15"},
+ /* 32 */ {4, "CSR"},
+ /* 33 */ {4, "PC"},
+};
+
+/* 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_info_table))
+ return "";
+ else
+ return tic6x_register_info_table[regno].name;
+}
+
+/* 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;
+}
+
+/* Our frame ID for a stub frame is the current SP and LR. */
+
+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 CORSSPATH 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, (const char *) 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 (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_info_table[i].name);
+
+ /* CSR */
+ valid_p &= tdesc_numbered_register (feature, tdesc_data, i++,
+ tic6x_register_info_table[TIC6X_CSR_REGNUM].name);
+ valid_p &= tdesc_numbered_register (feature, tdesc_data, i++,
+ tic6x_register_info_table[TIC6X_PC_REGNUM].name);
+
+ 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);
+}
diff --git a/gdb/tic6x-tdep.h b/gdb/tic6x-tdep.h
new file mode 100644
index 0000000..0227572
--- /dev/null
+++ b/gdb/tic6x-tdep.h
@@ -0,0 +1,54 @@
+/* GNU/Linux on TI C6x target support.
+ Copyright (C) 2011
+ Free Software Foundation, Inc.
+ 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/>. */
+
+enum
+{
+ TIC6X_A4_REGNUM = 4,
+ TIC6X_A5_REGNUM = 5,
+ TIC6X_FP_REGNUM = 15, /* Frame Pointer: A15 */
+ TIC6X_B0_REGNUM = 16,
+ TIC6X_RA_REGNUM = 19, /* Return address: B3 */
+ TIC6X_B4_REGNUM = 20,
+ TIC6X_B5_REGNUM = 21,
+ TIC6X_DP_REGNUM = 30, /* Data Page Pointer: B14 */
+ TIC6X_SP_REGNUM = 31, /* Stack Pointer: B15 */
+ TIC6X_CSR_REGNUM = 32,
+ TIC6X_PC_REGNUM = 33,
+ TIC6X_NUM_CORE_REGS = 33, /* The number of core registers */
+ TIC6X_RILC_REGNUM = 68,
+ TIC6X_NUM_REGS /* The number of registers */
+};
+
+#define TIC6X_INST_SWE 0x10000000
+
+extern const gdb_byte tic6x_bkpt_illegal_opcode_be[];
+extern const gdb_byte tic6x_bkpt_illegal_opcode_le[];
+
+/* Target-dependent structure in gdbarch. */
+struct gdbarch_tdep
+{
+ /* Return the expected next PC if FRAME is stopped at a syscall
+ instruction. */
+ CORE_ADDR (*syscall_next_pc) (struct frame_info *frame);
+
+ const char *breakpoint; /* Breakpoint instruction. */
+
+ int has_gp; /* Has general purpose registers A16 - A31 and B16 - B31. */
+};
--
1.7.0.4
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFA 5/8] New port: TI C6x: gdb port
2011-08-06 2:26 ` Yao Qi
@ 2011-08-08 13:04 ` Pedro Alves
2011-08-09 2:52 ` Yao Qi
0 siblings, 1 reply; 16+ messages in thread
From: Pedro Alves @ 2011-08-08 13:04 UTC (permalink / raw)
To: gdb-patches; +Cc: Yao Qi
On Saturday 06 August 2011 03:26:20, Yao Qi wrote:
> On 08/04/2011 08:33 PM, Pedro Alves wrote:
>
> >> +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)
> >> +{
> >
> > ...
> >
> >> +}
> >
> > OOC, did you investigate using the prologue-value.h machinery for this?
> >
>
> No. c6x prologue is quite simple, so I parse the binary opcode directly.
I won't make you go do that change, but using the prologue-value.h
machinery does not mean you don't parse the opcodes. It means you
parse the opcodes about the same, but re-use a nice mechanism of tracking
register moves and saves, instead of cooking your own per-arch, which
in turn should be more robust in face of sequences you don't handle
now, easier to debug, and easier to extend. I was actually thinking
you might have tried and found it lacking in some way.
> >> +tic6x_arg_type_alignment (struct type *type)
> >> +{
> >> + int len = TYPE_LENGTH (type);
> >> + enum type_code typecode = TYPE_CODE (type);
> >
> > I think a check_typedef is in order.
> >
> >
>
> This line is changed to:
>
> enum type_code typecode = TYPE_CODE (check_typedef (type));
You should call check_typedef earlier, before TYPE_LENGTH, not just
before TYPE_CODE.
> >> +_initialize_tic6x_tdep (void)
> >> +{
> >> + int i, offset = 0;
> >
> > Looks like a left over pasto.
> >
>
> Looks like the patch you reviewed is sent on July 20th, while I sent an
> updated version on July 25th, in which these two local variables are
> removed.
Sorry about that. Completely missed it.
> In my new patch, TIC6X_NUM_REGS is set to 69, which is equivalent to the
> max number of register that gdbserver can access via ptrace.
> TIC6X_NUM_REGS is used for set_gdbarch_num_regs.
Thanks.
> diff --git a/gdb/tic6x-tdep.c b/gdb/tic6x-tdep.c
> new file mode 100644
> index 0000000..7362d69
> --- /dev/null
> +++ b/gdb/tic6x-tdep.c
> @@ -0,0 +1,1414 @@
> +
> +/* Macros */
> +
> +#define TIC6X_OPCODE_SIZE 4
> +#define TIC6X_FETCH_PACKET_SIZE 32
> +
> +static int arg_regs[] = { 4, 20, 6, 22, 8, 24, 10, 26, 12, 28 };
const? Can you add a comment meantioning what this is?
It's not a "macro", so I'd say the "Macros" and "Structures" comments are
bound for bit rot.
> +/* Structures */
> +struct register_info
> +{
> + int size;
> + char *name;
> +};
Same.
> +
> +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];
> +};
> +
> +
> +/* tic6x_register_info_table[i] is the number of bytes of storage in
> + GDB's register array occupied by register i. */
> +static struct register_info tic6x_register_info_table[] =
> +{
> + /* 0 */ {4, "A0"},
> + /* 1 */ {4, "A1"},
> + /* 2 */ {4, "A2"},
> + /* 3 */ {4, "A3"},
> + /* 4 */ {4, "A4"},
> + /* 5 */ {4, "A5"},
> + /* 6 */ {4, "A6"},
> + /* 7 */ {4, "A7"},
> + /* 8 */ {4, "A8"},
> + /* 9 */ {4, "A9"},
> + /* 10 */ {4, "A10"},
> + /* 11 */ {4, "A11"},
> + /* 12 */ {4, "A12"},
> + /* 13 */ {4, "A13"},
> + /* 14 */ {4, "A14"},
> + /* 15 */ {4, "A15"},
> + /* 16 */ {4, "B0"},
> + /* 17 */ {4, "B1"},
> + /* 18 */ {4, "B2"},
> + /* 19 */ {4, "B3"},
> + /* 20 */ {4, "B4"},
> + /* 21 */ {4, "B5"},
> + /* 22 */ {4, "B6"},
> + /* 23 */ {4, "B7"},
> + /* 24 */ {4, "B8"},
> + /* 25 */ {4, "B9"},
> + /* 26 */ {4, "B10"},
> + /* 27 */ {4, "B11"},
> + /* 28 */ {4, "B12"},
> + /* 29 */ {4, "B13"},
> + /* 30 */ {4, "B14"},
> + /* 31 */ {4, "B15"},
> + /* 32 */ {4, "CSR"},
> + /* 33 */ {4, "PC"},
> +};
(I do wonder why do you need the register size field, if you're
always going to put '4' there anyway.)
> +
> +/* 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_info_table))
Spurious double space.
> + return "";
> + else
> + return tic6x_register_info_table[regno].name;
> +}
> +
> +/* 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)
Spurious double space.
> + return builtin_type (gdbarch)->builtin_func_ptr;
> + else
> + return builtin_type (gdbarch)->builtin_uint32;
> +}
> +/* Our frame ID for a stub frame is the current SP and LR. */
Is this comment right?
> +
> +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));
> +}
> +
> +/* Get the register number by decoding raw bits REG, SIDE, and CORSSPATH in
> + instruction. */
Typo CORSSPATH.
> +
> +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;
> +}
> +
> +/* 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. */
Spurious double space.
> +
> +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, (const char *) from);
> +}
Please remove the casts as well.
> + /* 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);
> + }
Unnecessary braces.
> +
> + return gdbarch;
> +}
> +
> +void
> +_initialize_tic6x_tdep (void)
> +{
> + register_gdbarch_init (bfd_arch_tic6x, tic6x_gdbarch_init);
> +}
No target descriptions initialized for tic6x-*-* ?
Otherwise looks okay to me, thanks.
--
Pedro Alves
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFA 5/8] New port: TI C6x: gdb port
2011-08-08 13:04 ` Pedro Alves
@ 2011-08-09 2:52 ` Yao Qi
2011-08-09 14:01 ` Pedro Alves
0 siblings, 1 reply; 16+ messages in thread
From: Yao Qi @ 2011-08-09 2:52 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 6194 bytes --]
On 08/08/2011 09:03 PM, Pedro Alves wrote:
> On Saturday 06 August 2011 03:26:20, Yao Qi wrote:
>> On 08/04/2011 08:33 PM, Pedro Alves wrote:
>>>> +tic6x_arg_type_alignment (struct type *type)
>>>> +{
>>>> + int len = TYPE_LENGTH (type);
>>>> + enum type_code typecode = TYPE_CODE (type);
>>>
>>> I think a check_typedef is in order.
>>>
>>>
>>
>> This line is changed to:
>>
>> enum type_code typecode = TYPE_CODE (check_typedef (type));
>
> You should call check_typedef earlier, before TYPE_LENGTH, not just
> before TYPE_CODE.
>
OK, call check_typedef before both TYPE_LENGTH and TYPE_CODE.
>> +
>> +/* Macros */
>> +
>> +#define TIC6X_OPCODE_SIZE 4
>> +#define TIC6X_FETCH_PACKET_SIZE 32
>> +
>> +static int arg_regs[] = { 4, 20, 6, 22, 8, 24, 10, 26, 12, 28 };
>
> const? Can you add a comment meantioning what this is?
Added, and moved it out of "macro" section.
> It's not a "macro", so I'd say the "Macros" and "Structures" comments are
> bound for bit rot.
>
>> +/* Structures */
>> +struct register_info
>> +{
>> + int size;
>> + char *name;
>> +};
>
> Same.
>
Remove "struct register_info" since size field is not used anymore.
>> +/* tic6x_register_info_table[i] is the number of bytes of storage in
>> + GDB's register array occupied by register i. */
>> +static struct register_info tic6x_register_info_table[] =
>> +{
>> + /* 0 */ {4, "A0"},
>> + /* 1 */ {4, "A1"},
>> + /* 2 */ {4, "A2"},
>> + /* 3 */ {4, "A3"},
>> + /* 4 */ {4, "A4"},
>> + /* 5 */ {4, "A5"},
>> + /* 6 */ {4, "A6"},
>> + /* 7 */ {4, "A7"},
>> + /* 8 */ {4, "A8"},
>> + /* 9 */ {4, "A9"},
>> + /* 10 */ {4, "A10"},
>> + /* 11 */ {4, "A11"},
>> + /* 12 */ {4, "A12"},
>> + /* 13 */ {4, "A13"},
>> + /* 14 */ {4, "A14"},
>> + /* 15 */ {4, "A15"},
>> + /* 16 */ {4, "B0"},
>> + /* 17 */ {4, "B1"},
>> + /* 18 */ {4, "B2"},
>> + /* 19 */ {4, "B3"},
>> + /* 20 */ {4, "B4"},
>> + /* 21 */ {4, "B5"},
>> + /* 22 */ {4, "B6"},
>> + /* 23 */ {4, "B7"},
>> + /* 24 */ {4, "B8"},
>> + /* 25 */ {4, "B9"},
>> + /* 26 */ {4, "B10"},
>> + /* 27 */ {4, "B11"},
>> + /* 28 */ {4, "B12"},
>> + /* 29 */ {4, "B13"},
>> + /* 30 */ {4, "B14"},
>> + /* 31 */ {4, "B15"},
>> + /* 32 */ {4, "CSR"},
>> + /* 33 */ {4, "PC"},
>> +};
>
> (I do wonder why do you need the register size field, if you're
> always going to put '4' there anyway.)
>
Yeah, agree. Change tic6x_register_info_table to tic6x_register_names,
an array of char*.
>> +
>> +/* 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_info_table))
>
> Spurious double space.
>
Fixed.
>> + return "";
>> + else
>> + return tic6x_register_info_table[regno].name;
>> +}
>> +
>> +/* 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)
>
> Spurious double space.
>
Fixed.
>> + return builtin_type (gdbarch)->builtin_func_ptr;
>> + else
>> + return builtin_type (gdbarch)->builtin_uint32;
>> +}
>
>> +/* Our frame ID for a stub frame is the current SP and LR. */
>
> Is this comment right?
>
Hmm, the comment is out of date. Get rid of it.
>> +
>> +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));
>> +}
>> +
>
>> +/* Get the register number by decoding raw bits REG, SIDE, and CORSSPATH in
>> + instruction. */
>
> Typo CORSSPATH.
>
Fixed.
>> +/* 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. */
>
> Spurious double space.
>
Fixed.
>> +
>> +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, (const char *) from);
>> +}
>
> Please remove the casts as well.
>
Removed.
>> + /* 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);
>> + }
>
> Unnecessary braces.
>
>
Removed.
>> +
>> + return gdbarch;
>> +}
>> +
>> +void
>> +_initialize_tic6x_tdep (void)
>> +{
>> + register_gdbarch_init (bfd_arch_tic6x, tic6x_gdbarch_init);
>> +}
>
> No target descriptions initialized for tic6x-*-* ?
>
Yes, we have target descriptions for tic6x-uclinux, but don't have for
tic6x-elf so far. So the target description is initialized in
tic6x-linux-tdep.c:_initialize_tic6x_linux_tdep.
I think the target description initialization should be moved to
tic6x-tdep.c:_initialize_tic6x_tdep, so tic6x-uclinux and tic6x-elf
share the target descriptions. In my new patch, target description
initialization is moved to tic6x-tdep.c:_initialize_tic6x_tdep, and
suffix "-linux" in feature names and files is removed (target
description is revised accordingly).
--
Yao (é½å°§)
[-- Attachment #2: 0006-gdb-tic6x-port.patch --]
[-- Type: text/x-patch, Size: 52845 bytes --]
2011-08-06 Andrew Jenner <andrew@codesourcery.com>
Yao Qi <yao@codesourcery.com>
gdb/
* tic6x-linux-tdep.c: New file.
* tic6x-tdep.c: New file.
* tic6x-tdep.h: New file.
---
gdb/tic6x-linux-tdep.c | 219 ++++++++
gdb/tic6x-tdep.c | 1386 ++++++++++++++++++++++++++++++++++++++++++++++++
gdb/tic6x-tdep.h | 54 ++
3 files changed, 1659 insertions(+), 0 deletions(-)
create mode 100644 gdb/tic6x-linux-tdep.c
create mode 100644 gdb/tic6x-tdep.c
create mode 100644 gdb/tic6x-tdep.h
diff --git a/gdb/tic6x-linux-tdep.c b/gdb/tic6x-linux-tdep.c
new file mode 100644
index 0000000..496e981
--- /dev/null
+++ b/gdb/tic6x-linux-tdep.c
@@ -0,0 +1,219 @@
+/* GNU/Linux on TI C6x target support.
+ Copyright (C) 2011
+ Free Software Foundation, Inc.
+ 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 "solib.h"
+#include "osabi.h"
+#include "linux-tdep.h"
+#include "tic6x-tdep.h"
+#include "trad-frame.h"
+#include "tramp-frame.h"
+#include "gdb_assert.h"
+#include "elf-bfd.h"
+#include "elf/tic6x.h"
+
+/* The offset from rt_sigframe pointer to SP register. */
+#define TIC6X_SP_RT_SIGFRAME 8
+/* Size of struct siginfo info. */
+#define TIC6X_SIGINFO_SIZE 128
+/* Size of type stack_t, which contains three fields of type void*, int, and
+ size_t respectively. */
+#define TIC6X_STACK_T_SIZE (3 * 4)
+
+const gdb_byte tic6x_bkpt_illegal_opcode_be[] = { 0x56, 0x45, 0x43, 0x14 };
+const gdb_byte tic6x_bkpt_illegal_opcode_le[] = { 0x14, 0x43, 0x45, 0x56 };
+
+static const gdb_byte tic6x_bkpt_bnop_be[] = { 0x00, 0x00, 0xa1, 0x22 };
+static const gdb_byte tic6x_bkpt_bnop_le[] = { 0x22, 0xa1, 0x00, 0x00 };
+
+/* Return the offset of register REGNUM in struct sigcontext. Return 0 if no
+ such register in sigcontext. */
+
+static unsigned int
+tic6x_register_sigcontext_offset (unsigned int regnum, struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ if (regnum == TIC6X_A4_REGNUM || regnum == TIC6X_A4_REGNUM + 2
+ || regnum == TIC6X_A4_REGNUM + 4)
+ return 4 * (regnum - TIC6X_A4_REGNUM + 2); /* A4, A6, A8 */
+ else if (regnum == TIC6X_A5_REGNUM || regnum == TIC6X_A5_REGNUM + 2
+ || regnum == TIC6X_A5_REGNUM + 4)
+ return 4 * (regnum - TIC6X_A5_REGNUM + 12); /* A5, A7, A9 */
+ else if (regnum == TIC6X_B4_REGNUM || regnum == TIC6X_B4_REGNUM + 2
+ || regnum == TIC6X_B4_REGNUM + 4)
+ return 4 * (regnum - TIC6X_B4_REGNUM + 3); /* B4, B6, B8 */
+ else if (regnum == TIC6X_B5_REGNUM || regnum == TIC6X_B5_REGNUM + 2
+ || regnum == TIC6X_B5_REGNUM + 4)
+ return 4 * (regnum - TIC6X_B5_REGNUM + 19); /* B5, B7, B9 */
+ else if (regnum >= 0 && regnum < TIC6X_A4_REGNUM)
+ return 4 * (regnum - 0 + 8); /* A0 - A3 */
+ else if (regnum >= TIC6X_B0_REGNUM && regnum < TIC6X_B4_REGNUM)
+ return 4 * (regnum - TIC6X_B0_REGNUM + 15); /* B0 - B3 */
+ else if (regnum >= 34 && regnum < 34 + 32)
+ return 4 * (regnum - 34 + 23); /* A16 - A31, B16 - B31 */
+ else if (regnum == TIC6X_PC_REGNUM)
+ return 4 * (tdep->has_gp ? 55 : 23);
+ else if (regnum == TIC6X_SP_REGNUM)
+ return 4;
+
+ return 0;
+}
+
+/* Support unwinding frame in signal trampoline. We don't check sigreturn,
+ since it is not used in kernel. */
+
+static void
+tic6x_linux_rt_sigreturn_init (const struct tramp_frame *self,
+ struct frame_info *this_frame,
+ struct trad_frame_cache *this_cache,
+ CORE_ADDR func)
+{
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ CORE_ADDR sp = get_frame_register_unsigned (this_frame, TIC6X_SP_REGNUM);
+ /* The base of struct sigcontext is computed by examining the definition of
+ struct rt_sigframe in linux kernel source arch/c6x/kernel/signal.c. */
+ CORE_ADDR base = (sp + TIC6X_SP_RT_SIGFRAME
+ /* Pointer type *pinfo and *puc in struct rt_sigframe. */
+ + 4 + 4
+ + TIC6X_SIGINFO_SIZE
+ + 4 + 4 /* uc_flags and *uc_link in struct ucontext. */
+ + TIC6X_STACK_T_SIZE);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ unsigned int reg_offset;
+ unsigned int i;
+
+ for (i = 0; i < 10; i++) /* A0 - A9 */
+ {
+ reg_offset = tic6x_register_sigcontext_offset (i, gdbarch);
+ gdb_assert (reg_offset != 0);
+
+ trad_frame_set_reg_addr (this_cache, i, base + reg_offset);
+ }
+
+ for (i = TIC6X_B0_REGNUM; i < TIC6X_B0_REGNUM + 10; i++) /* B0 - B9 */
+ {
+ reg_offset = tic6x_register_sigcontext_offset (i, gdbarch);
+ gdb_assert (reg_offset != 0);
+
+ trad_frame_set_reg_addr (this_cache, i, base + reg_offset);
+ }
+
+ if (tdep->has_gp)
+ for (i = 34; i < 34 + 32; i++) /* A16 - A31, B16 - B31 */
+ {
+ reg_offset = tic6x_register_sigcontext_offset (i, gdbarch);
+ gdb_assert (reg_offset != 0);
+
+ trad_frame_set_reg_addr (this_cache, i, base + reg_offset);
+ }
+
+ trad_frame_set_reg_addr (this_cache, TIC6X_PC_REGNUM,
+ base + tic6x_register_sigcontext_offset (TIC6X_PC_REGNUM,
+ gdbarch));
+ trad_frame_set_reg_addr (this_cache, TIC6X_SP_REGNUM,
+ base + tic6x_register_sigcontext_offset (TIC6X_SP_REGNUM,
+ gdbarch));
+
+ /* Save a frame ID. */
+ trad_frame_set_id (this_cache, frame_id_build (sp, func));
+}
+
+static struct tramp_frame tic6x_linux_rt_sigreturn_tramp_frame =
+{
+ SIGTRAMP_FRAME,
+ 4,
+ {
+ {0x000045aa, 0x0fffffff}, /* mvk .S2 139,b0 */
+ {0x10000000, -1}, /* swe */
+ {TRAMP_SENTINEL_INSN}
+ },
+ tic6x_linux_rt_sigreturn_init
+};
+
+/* When FRAME is at a syscall instruction, return the PC of the next
+ instruction to be executed. */
+
+static CORE_ADDR
+tic6x_linux_syscall_next_pc (struct frame_info *frame)
+{
+ ULONGEST syscall_number = get_frame_register_unsigned (frame,
+ TIC6X_B0_REGNUM);
+ CORE_ADDR pc = get_frame_pc (frame);
+
+ if (syscall_number == 139 /* rt_sigreturn */)
+ return frame_unwind_caller_pc (frame);
+
+ return pc + 4;
+}
+
+
+extern struct target_so_ops dsbt_so_ops;
+static void
+tic6x_uclinux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ linux_init_abi (info, gdbarch);
+
+ /* Shared library handling. */
+ set_solib_ops (gdbarch, &dsbt_so_ops);
+
+ tdep->syscall_next_pc = tic6x_linux_syscall_next_pc;
+
+#ifdef HAVE_ELF
+ /* In tic6x Linux kernel, breakpoint instructions varies on different archs.
+ On C64x+ and C67x+, breakpoint instruction is 0x56454314, which is an
+ illegal opcode. On other arch, breakpoint instruction is 0x0000a122
+ (BNOP .S2 0,5). */
+ if (info.abfd)
+ switch (bfd_elf_get_obj_attr_int (info.abfd, OBJ_ATTR_PROC, Tag_ISA))
+ {
+ case C6XABI_Tag_ISA_C64XP:
+ case C6XABI_Tag_ISA_C67XP:
+ if (info.byte_order == BFD_ENDIAN_BIG)
+ tdep->breakpoint = tic6x_bkpt_illegal_opcode_be;
+ else
+ tdep->breakpoint = tic6x_bkpt_illegal_opcode_le;
+ break;
+ default:
+ {
+ if (info.byte_order == BFD_ENDIAN_BIG)
+ tdep->breakpoint = tic6x_bkpt_bnop_be;
+ else
+ tdep->breakpoint = tic6x_bkpt_bnop_le;
+ }
+ }
+#endif
+
+ /* Signal trampoline support. */
+ tramp_frame_prepend_unwinder (gdbarch,
+ &tic6x_linux_rt_sigreturn_tramp_frame);
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_tic6x_linux_tdep;
+
+void
+_initialize_tic6x_linux_tdep (void)
+{
+ gdbarch_register_osabi (bfd_arch_tic6x, 0, GDB_OSABI_LINUX,
+ tic6x_uclinux_init_abi);
+}
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 ();
+}
diff --git a/gdb/tic6x-tdep.h b/gdb/tic6x-tdep.h
new file mode 100644
index 0000000..0227572
--- /dev/null
+++ b/gdb/tic6x-tdep.h
@@ -0,0 +1,54 @@
+/* GNU/Linux on TI C6x target support.
+ Copyright (C) 2011
+ Free Software Foundation, Inc.
+ 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/>. */
+
+enum
+{
+ TIC6X_A4_REGNUM = 4,
+ TIC6X_A5_REGNUM = 5,
+ TIC6X_FP_REGNUM = 15, /* Frame Pointer: A15 */
+ TIC6X_B0_REGNUM = 16,
+ TIC6X_RA_REGNUM = 19, /* Return address: B3 */
+ TIC6X_B4_REGNUM = 20,
+ TIC6X_B5_REGNUM = 21,
+ TIC6X_DP_REGNUM = 30, /* Data Page Pointer: B14 */
+ TIC6X_SP_REGNUM = 31, /* Stack Pointer: B15 */
+ TIC6X_CSR_REGNUM = 32,
+ TIC6X_PC_REGNUM = 33,
+ TIC6X_NUM_CORE_REGS = 33, /* The number of core registers */
+ TIC6X_RILC_REGNUM = 68,
+ TIC6X_NUM_REGS /* The number of registers */
+};
+
+#define TIC6X_INST_SWE 0x10000000
+
+extern const gdb_byte tic6x_bkpt_illegal_opcode_be[];
+extern const gdb_byte tic6x_bkpt_illegal_opcode_le[];
+
+/* Target-dependent structure in gdbarch. */
+struct gdbarch_tdep
+{
+ /* Return the expected next PC if FRAME is stopped at a syscall
+ instruction. */
+ CORE_ADDR (*syscall_next_pc) (struct frame_info *frame);
+
+ const char *breakpoint; /* Breakpoint instruction. */
+
+ int has_gp; /* Has general purpose registers A16 - A31 and B16 - B31. */
+};
--
1.7.0.4
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFA 5/8] New port: TI C6x: gdb port
2011-08-09 2:52 ` Yao Qi
@ 2011-08-09 14:01 ` Pedro Alves
2011-08-09 15:16 ` Yao Qi
0 siblings, 1 reply; 16+ messages in thread
From: Pedro Alves @ 2011-08-09 14:01 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On Tuesday 09 August 2011 03:52:13, Yao Qi wrote:
> Yes, we have target descriptions for tic6x-uclinux, but don't have for
> tic6x-elf so far. So the target description is initialized in
> tic6x-linux-tdep.c:_initialize_tic6x_linux_tdep.
>
> I think the target description initialization should be moved to
> tic6x-tdep.c:_initialize_tic6x_tdep, so tic6x-uclinux and tic6x-elf
> share the target descriptions. In my new patch, target description
> initialization is moved to tic6x-tdep.c:_initialize_tic6x_tdep, and
> suffix "-linux" in feature names and files is removed (target
> description is revised accordingly).
You can't do that given the descriptions specify GNU/Linux osabi.
There should be linux, and non-linux variants.
--
Pedro Alves
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFA 5/8] New port: TI C6x: gdb port
2011-08-09 14:01 ` Pedro Alves
@ 2011-08-09 15:16 ` Yao Qi
2011-08-09 15:42 ` Pedro Alves
0 siblings, 1 reply; 16+ messages in thread
From: Yao Qi @ 2011-08-09 15:16 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
On 08/09/2011 10:01 PM, Pedro Alves wrote:
> On Tuesday 09 August 2011 03:52:13, Yao Qi wrote:
>> Yes, we have target descriptions for tic6x-uclinux, but don't have for
>> tic6x-elf so far. So the target description is initialized in
>> tic6x-linux-tdep.c:_initialize_tic6x_linux_tdep.
>>
>> I think the target description initialization should be moved to
>> tic6x-tdep.c:_initialize_tic6x_tdep, so tic6x-uclinux and tic6x-elf
>> share the target descriptions. In my new patch, target description
>> initialization is moved to tic6x-tdep.c:_initialize_tic6x_tdep, and
>> suffix "-linux" in feature names and files is removed (target
>> description is revised accordingly).
>
> You can't do that given the descriptions specify GNU/Linux osabi.
> There should be linux, and non-linux variants.
>
Can't we get rid of osabi from xml description, and use them both for
linux and non-linux? Similar to arm's target descriptions.
--
Yao (é½å°§)
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFA 5/8] New port: TI C6x: gdb port
2011-08-09 15:16 ` Yao Qi
@ 2011-08-09 15:42 ` Pedro Alves
2011-08-10 2:37 ` Yao Qi
0 siblings, 1 reply; 16+ messages in thread
From: Pedro Alves @ 2011-08-09 15:42 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On Tuesday 09 August 2011 16:16:00, Yao Qi wrote:
> On 08/09/2011 10:01 PM, Pedro Alves wrote:
> > On Tuesday 09 August 2011 03:52:13, Yao Qi wrote:
> >> Yes, we have target descriptions for tic6x-uclinux, but don't have for
> >> tic6x-elf so far. So the target description is initialized in
> >> tic6x-linux-tdep.c:_initialize_tic6x_linux_tdep.
> >>
> >> I think the target description initialization should be moved to
> >> tic6x-tdep.c:_initialize_tic6x_tdep, so tic6x-uclinux and tic6x-elf
> >> share the target descriptions. In my new patch, target description
> >> initialization is moved to tic6x-tdep.c:_initialize_tic6x_tdep, and
> >> suffix "-linux" in feature names and files is removed (target
> >> description is revised accordingly).
> >
> > You can't do that given the descriptions specify GNU/Linux osabi.
> > There should be linux, and non-linux variants.
> >
>
> Can't we get rid of osabi from xml description, and use them both for
> linux and non-linux? Similar to arm's target descriptions.
We can, but that's a bit a step backwards. arm's descriptions don't
set the osabi because the support for the osabi field element was added
after the descriptions were. Having the target tell gdb the osabi makes
a multi-target gdb (*) figure out the correct arch even if you don't
specify an executable (for attach).
I believe that with an xi:include, the linux xml file would be minimal.
It's not a super important use case, so I won't insist. Do as you prefer.
(*) - otherwise gdb assumes the default osabi as set by gdb/configure.tgt)
--
Pedro Alves
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFA 5/8] New port: TI C6x: gdb port
2011-08-09 15:42 ` Pedro Alves
@ 2011-08-10 2:37 ` Yao Qi
2011-08-10 13:44 ` Pedro Alves
0 siblings, 1 reply; 16+ messages in thread
From: Yao Qi @ 2011-08-10 2:37 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 1239 bytes --]
On 08/09/2011 11:41 PM, Pedro Alves wrote:
>>> > >
>>> > > You can't do that given the descriptions specify GNU/Linux osabi.
>>> > > There should be linux, and non-linux variants.
>>> > >
>> >
>> > Can't we get rid of osabi from xml description, and use them both for
>> > linux and non-linux? Similar to arm's target descriptions.
> We can, but that's a bit a step backwards. arm's descriptions don't
> set the osabi because the support for the osabi field element was added
> after the descriptions were. Having the target tell gdb the osabi makes
> a multi-target gdb (*) figure out the correct arch even if you don't
> specify an executable (for attach).
I see, thanks for the explanation, Pedro.
> I believe that with an xi:include, the linux xml file would be minimal.
I reference the i386 xml files, and do the similar for tic6x. Copy
tic6x-{c62x,c64x,c64xp}.xml to tic6x-{c62x,c64x,c64xp}-linux.xml, and
add osabi in tic6x-*-linux.xml files. Generate corresponding *.c files
and *.data files.
In the new patch, linux target descriptions are initialized in
tic6x-linux-tdep.c:_initialize_tic6x_linux_tdep, and non-linux target
descriptions are initialized in tic6x-tdep.c:_initialize_tic6x_tdep.
--
Yao (é½å°§)
[-- Attachment #2: 0006-gdb-tic6x-port.patch --]
[-- Type: text/x-patch, Size: 53094 bytes --]
2011-08-06 Andrew Jenner <andrew@codesourcery.com>
Yao Qi <yao@codesourcery.com>
gdb/
* tic6x-linux-tdep.c: New file.
* tic6x-tdep.c: New file.
* tic6x-tdep.h: New file.
---
gdb/tic6x-linux-tdep.c | 227 ++++++++
gdb/tic6x-tdep.c | 1386 ++++++++++++++++++++++++++++++++++++++++++++++++
gdb/tic6x-tdep.h | 54 ++
3 files changed, 1667 insertions(+), 0 deletions(-)
create mode 100644 gdb/tic6x-linux-tdep.c
create mode 100644 gdb/tic6x-tdep.c
create mode 100644 gdb/tic6x-tdep.h
diff --git a/gdb/tic6x-linux-tdep.c b/gdb/tic6x-linux-tdep.c
new file mode 100644
index 0000000..0e4d327
--- /dev/null
+++ b/gdb/tic6x-linux-tdep.c
@@ -0,0 +1,227 @@
+/* GNU/Linux on TI C6x target support.
+ Copyright (C) 2011
+ Free Software Foundation, Inc.
+ 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 "solib.h"
+#include "osabi.h"
+#include "linux-tdep.h"
+#include "tic6x-tdep.h"
+#include "trad-frame.h"
+#include "tramp-frame.h"
+#include "gdb_assert.h"
+#include "elf-bfd.h"
+#include "elf/tic6x.h"
+
+#include "features/tic6x-c64xp-linux.c"
+#include "features/tic6x-c64x-linux.c"
+#include "features/tic6x-c62x-linux.c"
+
+/* The offset from rt_sigframe pointer to SP register. */
+#define TIC6X_SP_RT_SIGFRAME 8
+/* Size of struct siginfo info. */
+#define TIC6X_SIGINFO_SIZE 128
+/* Size of type stack_t, which contains three fields of type void*, int, and
+ size_t respectively. */
+#define TIC6X_STACK_T_SIZE (3 * 4)
+
+const gdb_byte tic6x_bkpt_illegal_opcode_be[] = { 0x56, 0x45, 0x43, 0x14 };
+const gdb_byte tic6x_bkpt_illegal_opcode_le[] = { 0x14, 0x43, 0x45, 0x56 };
+
+static const gdb_byte tic6x_bkpt_bnop_be[] = { 0x00, 0x00, 0xa1, 0x22 };
+static const gdb_byte tic6x_bkpt_bnop_le[] = { 0x22, 0xa1, 0x00, 0x00 };
+
+/* Return the offset of register REGNUM in struct sigcontext. Return 0 if no
+ such register in sigcontext. */
+
+static unsigned int
+tic6x_register_sigcontext_offset (unsigned int regnum, struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ if (regnum == TIC6X_A4_REGNUM || regnum == TIC6X_A4_REGNUM + 2
+ || regnum == TIC6X_A4_REGNUM + 4)
+ return 4 * (regnum - TIC6X_A4_REGNUM + 2); /* A4, A6, A8 */
+ else if (regnum == TIC6X_A5_REGNUM || regnum == TIC6X_A5_REGNUM + 2
+ || regnum == TIC6X_A5_REGNUM + 4)
+ return 4 * (regnum - TIC6X_A5_REGNUM + 12); /* A5, A7, A9 */
+ else if (regnum == TIC6X_B4_REGNUM || regnum == TIC6X_B4_REGNUM + 2
+ || regnum == TIC6X_B4_REGNUM + 4)
+ return 4 * (regnum - TIC6X_B4_REGNUM + 3); /* B4, B6, B8 */
+ else if (regnum == TIC6X_B5_REGNUM || regnum == TIC6X_B5_REGNUM + 2
+ || regnum == TIC6X_B5_REGNUM + 4)
+ return 4 * (regnum - TIC6X_B5_REGNUM + 19); /* B5, B7, B9 */
+ else if (regnum >= 0 && regnum < TIC6X_A4_REGNUM)
+ return 4 * (regnum - 0 + 8); /* A0 - A3 */
+ else if (regnum >= TIC6X_B0_REGNUM && regnum < TIC6X_B4_REGNUM)
+ return 4 * (regnum - TIC6X_B0_REGNUM + 15); /* B0 - B3 */
+ else if (regnum >= 34 && regnum < 34 + 32)
+ return 4 * (regnum - 34 + 23); /* A16 - A31, B16 - B31 */
+ else if (regnum == TIC6X_PC_REGNUM)
+ return 4 * (tdep->has_gp ? 55 : 23);
+ else if (regnum == TIC6X_SP_REGNUM)
+ return 4;
+
+ return 0;
+}
+
+/* Support unwinding frame in signal trampoline. We don't check sigreturn,
+ since it is not used in kernel. */
+
+static void
+tic6x_linux_rt_sigreturn_init (const struct tramp_frame *self,
+ struct frame_info *this_frame,
+ struct trad_frame_cache *this_cache,
+ CORE_ADDR func)
+{
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ CORE_ADDR sp = get_frame_register_unsigned (this_frame, TIC6X_SP_REGNUM);
+ /* The base of struct sigcontext is computed by examining the definition of
+ struct rt_sigframe in linux kernel source arch/c6x/kernel/signal.c. */
+ CORE_ADDR base = (sp + TIC6X_SP_RT_SIGFRAME
+ /* Pointer type *pinfo and *puc in struct rt_sigframe. */
+ + 4 + 4
+ + TIC6X_SIGINFO_SIZE
+ + 4 + 4 /* uc_flags and *uc_link in struct ucontext. */
+ + TIC6X_STACK_T_SIZE);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ unsigned int reg_offset;
+ unsigned int i;
+
+ for (i = 0; i < 10; i++) /* A0 - A9 */
+ {
+ reg_offset = tic6x_register_sigcontext_offset (i, gdbarch);
+ gdb_assert (reg_offset != 0);
+
+ trad_frame_set_reg_addr (this_cache, i, base + reg_offset);
+ }
+
+ for (i = TIC6X_B0_REGNUM; i < TIC6X_B0_REGNUM + 10; i++) /* B0 - B9 */
+ {
+ reg_offset = tic6x_register_sigcontext_offset (i, gdbarch);
+ gdb_assert (reg_offset != 0);
+
+ trad_frame_set_reg_addr (this_cache, i, base + reg_offset);
+ }
+
+ if (tdep->has_gp)
+ for (i = 34; i < 34 + 32; i++) /* A16 - A31, B16 - B31 */
+ {
+ reg_offset = tic6x_register_sigcontext_offset (i, gdbarch);
+ gdb_assert (reg_offset != 0);
+
+ trad_frame_set_reg_addr (this_cache, i, base + reg_offset);
+ }
+
+ trad_frame_set_reg_addr (this_cache, TIC6X_PC_REGNUM,
+ base + tic6x_register_sigcontext_offset (TIC6X_PC_REGNUM,
+ gdbarch));
+ trad_frame_set_reg_addr (this_cache, TIC6X_SP_REGNUM,
+ base + tic6x_register_sigcontext_offset (TIC6X_SP_REGNUM,
+ gdbarch));
+
+ /* Save a frame ID. */
+ trad_frame_set_id (this_cache, frame_id_build (sp, func));
+}
+
+static struct tramp_frame tic6x_linux_rt_sigreturn_tramp_frame =
+{
+ SIGTRAMP_FRAME,
+ 4,
+ {
+ {0x000045aa, 0x0fffffff}, /* mvk .S2 139,b0 */
+ {0x10000000, -1}, /* swe */
+ {TRAMP_SENTINEL_INSN}
+ },
+ tic6x_linux_rt_sigreturn_init
+};
+
+/* When FRAME is at a syscall instruction, return the PC of the next
+ instruction to be executed. */
+
+static CORE_ADDR
+tic6x_linux_syscall_next_pc (struct frame_info *frame)
+{
+ ULONGEST syscall_number = get_frame_register_unsigned (frame,
+ TIC6X_B0_REGNUM);
+ CORE_ADDR pc = get_frame_pc (frame);
+
+ if (syscall_number == 139 /* rt_sigreturn */)
+ return frame_unwind_caller_pc (frame);
+
+ return pc + 4;
+}
+
+
+extern struct target_so_ops dsbt_so_ops;
+static void
+tic6x_uclinux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ linux_init_abi (info, gdbarch);
+
+ /* Shared library handling. */
+ set_solib_ops (gdbarch, &dsbt_so_ops);
+
+ tdep->syscall_next_pc = tic6x_linux_syscall_next_pc;
+
+#ifdef HAVE_ELF
+ /* In tic6x Linux kernel, breakpoint instructions varies on different archs.
+ On C64x+ and C67x+, breakpoint instruction is 0x56454314, which is an
+ illegal opcode. On other arch, breakpoint instruction is 0x0000a122
+ (BNOP .S2 0,5). */
+ if (info.abfd)
+ switch (bfd_elf_get_obj_attr_int (info.abfd, OBJ_ATTR_PROC, Tag_ISA))
+ {
+ case C6XABI_Tag_ISA_C64XP:
+ case C6XABI_Tag_ISA_C67XP:
+ if (info.byte_order == BFD_ENDIAN_BIG)
+ tdep->breakpoint = tic6x_bkpt_illegal_opcode_be;
+ else
+ tdep->breakpoint = tic6x_bkpt_illegal_opcode_le;
+ break;
+ default:
+ {
+ if (info.byte_order == BFD_ENDIAN_BIG)
+ tdep->breakpoint = tic6x_bkpt_bnop_be;
+ else
+ tdep->breakpoint = tic6x_bkpt_bnop_le;
+ }
+ }
+#endif
+
+ /* Signal trampoline support. */
+ tramp_frame_prepend_unwinder (gdbarch,
+ &tic6x_linux_rt_sigreturn_tramp_frame);
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_tic6x_linux_tdep;
+
+void
+_initialize_tic6x_linux_tdep (void)
+{
+ gdbarch_register_osabi (bfd_arch_tic6x, 0, GDB_OSABI_LINUX,
+ tic6x_uclinux_init_abi);
+
+ initialize_tdesc_tic6x_c64xp_linux ();
+ initialize_tdesc_tic6x_c64x_linux ();
+ initialize_tdesc_tic6x_c62x_linux ();
+}
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 ();
+}
diff --git a/gdb/tic6x-tdep.h b/gdb/tic6x-tdep.h
new file mode 100644
index 0000000..0227572
--- /dev/null
+++ b/gdb/tic6x-tdep.h
@@ -0,0 +1,54 @@
+/* GNU/Linux on TI C6x target support.
+ Copyright (C) 2011
+ Free Software Foundation, Inc.
+ 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/>. */
+
+enum
+{
+ TIC6X_A4_REGNUM = 4,
+ TIC6X_A5_REGNUM = 5,
+ TIC6X_FP_REGNUM = 15, /* Frame Pointer: A15 */
+ TIC6X_B0_REGNUM = 16,
+ TIC6X_RA_REGNUM = 19, /* Return address: B3 */
+ TIC6X_B4_REGNUM = 20,
+ TIC6X_B5_REGNUM = 21,
+ TIC6X_DP_REGNUM = 30, /* Data Page Pointer: B14 */
+ TIC6X_SP_REGNUM = 31, /* Stack Pointer: B15 */
+ TIC6X_CSR_REGNUM = 32,
+ TIC6X_PC_REGNUM = 33,
+ TIC6X_NUM_CORE_REGS = 33, /* The number of core registers */
+ TIC6X_RILC_REGNUM = 68,
+ TIC6X_NUM_REGS /* The number of registers */
+};
+
+#define TIC6X_INST_SWE 0x10000000
+
+extern const gdb_byte tic6x_bkpt_illegal_opcode_be[];
+extern const gdb_byte tic6x_bkpt_illegal_opcode_le[];
+
+/* Target-dependent structure in gdbarch. */
+struct gdbarch_tdep
+{
+ /* Return the expected next PC if FRAME is stopped at a syscall
+ instruction. */
+ CORE_ADDR (*syscall_next_pc) (struct frame_info *frame);
+
+ const char *breakpoint; /* Breakpoint instruction. */
+
+ int has_gp; /* Has general purpose registers A16 - A31 and B16 - B31. */
+};
--
1.7.0.4
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFA 5/8] New port: TI C6x: gdb port
2011-08-10 2:37 ` Yao Qi
@ 2011-08-10 13:44 ` Pedro Alves
0 siblings, 0 replies; 16+ messages in thread
From: Pedro Alves @ 2011-08-10 13:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Yao Qi
On Wednesday 10 August 2011 03:36:35, Yao Qi wrote:
> On 08/09/2011 11:41 PM, Pedro Alves wrote:
> >>> > >
> >>> > > You can't do that given the descriptions specify GNU/Linux osabi.
> >>> > > There should be linux, and non-linux variants.
> >>> > >
> >> >
> >> > Can't we get rid of osabi from xml description, and use them both for
> >> > linux and non-linux? Similar to arm's target descriptions.
> > We can, but that's a bit a step backwards. arm's descriptions don't
> > set the osabi because the support for the osabi field element was added
> > after the descriptions were. Having the target tell gdb the osabi makes
> > a multi-target gdb (*) figure out the correct arch even if you don't
> > specify an executable (for attach).
>
> I see, thanks for the explanation, Pedro.
>
> > I believe that with an xi:include, the linux xml file would be minimal.
>
> I reference the i386 xml files, and do the similar for tic6x. Copy
> tic6x-{c62x,c64x,c64xp}.xml to tic6x-{c62x,c64x,c64xp}-linux.xml, and
> add osabi in tic6x-*-linux.xml files. Generate corresponding *.c files
> and *.data files.
Thanks, sounds good.
> In the new patch, linux target descriptions are initialized in
> tic6x-linux-tdep.c:_initialize_tic6x_linux_tdep, and non-linux target
> descriptions are initialized in tic6x-tdep.c:_initialize_tic6x_tdep.
Patch looks good to me.
--
Pedro Alves
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2011-08-10 13:44 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-07-20 2:10 [RFA 5/8] New port: TI C6x: gdb port Yao Qi
2011-07-25 11:32 ` Yao Qi
2011-08-03 1:15 ` ping: " Yao Qi
2011-08-04 12:34 ` Pedro Alves
2011-08-04 15:11 ` Joseph S. Myers
2011-08-04 17:45 ` Pedro Alves
2011-08-04 19:48 ` Joseph S. Myers
2011-08-05 2:13 ` Yao Qi
2011-08-06 2:26 ` Yao Qi
2011-08-08 13:04 ` Pedro Alves
2011-08-09 2:52 ` Yao Qi
2011-08-09 14:01 ` Pedro Alves
2011-08-09 15:16 ` Yao Qi
2011-08-09 15:42 ` Pedro Alves
2011-08-10 2:37 ` Yao Qi
2011-08-10 13:44 ` Pedro Alves
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox