From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 1774 invoked by alias); 6 Aug 2011 02:26:57 -0000 Received: (qmail 1762 invoked by uid 22791); 6 Aug 2011 02:26:50 -0000 X-SWARE-Spam-Status: No, hits=-2.2 required=5.0 tests=AWL,BAYES_00,RP_MATCHES_RCVD,TW_EG,TW_MV,TW_SB X-Spam-Check-By: sourceware.org Received: from mail.codesourcery.com (HELO mail.codesourcery.com) (38.113.113.100) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Sat, 06 Aug 2011 02:26:27 +0000 Received: (qmail 4335 invoked from network); 6 Aug 2011 02:26:24 -0000 Received: from unknown (HELO ?192.168.0.101?) (yao@127.0.0.2) by mail.codesourcery.com with ESMTPA; 6 Aug 2011 02:26:24 -0000 Message-ID: <4E3CA64C.1070901@codesourcery.com> Date: Sat, 06 Aug 2011 02:26:00 -0000 From: Yao Qi User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.18) Gecko/20110617 Lightning/1.0b2 Thunderbird/3.1.11 MIME-Version: 1.0 To: Pedro Alves CC: gdb-patches@sourceware.org Subject: Re: [RFA 5/8] New port: TI C6x: gdb port References: <4E2638A6.1070406@codesourcery.com> <201108041333.42945.pedro@codesourcery.com> In-Reply-To: <201108041333.42945.pedro@codesourcery.com> Content-Type: multipart/mixed; boundary="------------040702070400040702090602" X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2011-08/txt/msg00126.txt.bz2 This is a multi-part message in MIME format. --------------040702070400040702090602 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Content-length: 17449 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 (齐尧) --------------040702070400040702090602 Content-Type: text/x-patch; name="0006-gdb-tic6x-port.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0006-gdb-tic6x-port.patch" Content-length: 53428 2011-08-06 Andrew Jenner Yao Qi 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 + + 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 . */ + +#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 + Contributed by Yao Qi + + 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 . */ + +#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 + <+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 + + 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 . */ + +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 --------------040702070400040702090602--