[RFA 3/5] New port: CR16: gdb port

Kaushik Phatak
2012-10-04T10:22:15Z

Hi,
This patch adds the tdep related files to gdb.
The port uses basic structure of the Renesas RX port and some concepts borrowed
from the ARM port. The linux port uses a different breakpoint instruction eliminating
external hardware requirement. The register set is slightly different for ELF
and Linux which gets set by set_gdbarch_register_name.
Kindly review the same and let me know if further modifications are required.

Additional Information on CR16 target:
The CR16 target saves the return address (right shifted by 1) onto the RA
register, which is later pushed onto the stack for leaf calls. The unwinder
takes care of this in the frame_prev_register call.
The CR16 target has a combination of 16 and 32 bit registers. R0 to R11 are
16 bits wide and can be accessed as 16 bit or 32 bit (register pair).
R12, R13, RA and SP are 32 bit registers.

Regards,
Kaushik

2012-10-04 Kaushik Phatak <kaushik.phatak@kpitcummins.com>

gdb/Changelog
* cr16-linux-tdep.c: New file.
* cr16-tdep.c: New file.
* cr16-tdep.h: New file.

--- ./gdb_src.orig/gdb/cr16-linux-tdep.c 1970-01-01 05:30:00.000000000 +0530
+++ ./gdb_src/gdb/cr16-linux-tdep.c 2012-10-01 11:47:44.000000000 +0530
@@ -0,0 +1,64 @@
+/* Target-dependent code for GNU/Linux on the Sitel CR16 processors.
+
+ Copyright 2007-2012 Free Software Foundation, Inc.
+
+ Contributed by Kaushik Phatak (kaushik.pahatk@kpitcummins.com)
+ KPIT Cummins Infosystems Limited, Pune India.
+
+ 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 "osabi.h"
+#include "linux-tdep.h"
+#include "solib-svr4.h"
+#include "symtab.h"
+#include "cr16-tdep.h"
+
+const gdb_byte breakpoint_uclinux[] = { 0xC7, 0x00 };
+
+
+/* OS specific initialization of gdbarch. */
+
+static void
+cr16_uclinux_init_abi (struct gdbarch_info info,
+ struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ linux_init_abi (info, gdbarch);
+
+ /* The opcode of excp bpt is 0x00C8, however for uclinux we will use the
+ excp flg (0x00C7) to insert a breakpoint. The excp bpt requires external
+ hardware support for breakpoints to work on CR16 target. Software based
+ breakpoints are implemented in the kernel using excp flg and tested on
+ the SC14452 target. Use 0x00C7 with gdbserver/kernel and 0x00C8 for sim/ELF
+ We represent the breakpoint in little endian format since CR16 supports
+ only little endian.
+ */
+ tdep->breakpoint = breakpoint_uclinux;
+
+}
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_cr16_linux_tdep;
+
+void
+_initialize_cr16_linux_tdep (void)
+{
+ gdbarch_register_osabi (bfd_arch_cr16, 0, GDB_OSABI_LINUX,
+ cr16_uclinux_init_abi);
+}
+
+
--- ./gdb_src.orig/gdb/cr16-tdep.c 1970-01-01 05:30:00.000000000 +0530
+++ ./gdb_src/gdb/cr16-tdep.c 2012-10-04 15:23:27.000000000 +0530
@@ -0,0 +1,930 @@
+/* Target-dependent code for the Sitel CR16 for GDB, the GNU debugger.
+
+ Copyright (C) 2012 Free Software Foundation, Inc.
+
+ Contributed by Kaushik Phatak (kaushik.pahatk@kpitcummins.com)
+ KPIT Cummins Infosystems Limited, Pune India.
+ 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 "arch-utils.h"
+#include "prologue-value.h"
+#include "target.h"
+#include "regcache.h"
+#include "opcode/cr16.h"
+#include "dis-asm.h"
+#include "gdbtypes.h"
+#include "frame.h"
+#include "frame-unwind.h"
+#include "frame-base.h"
+#include "value.h"
+#include "gdbcore.h"
+#include "dwarf2-frame.h"
+#include "gdb/sim-cr16.h"
+#include "elf/cr16.h"
+#include "elf-bfd.h"
+#include "linux-tdep.h"
+#include "osabi.h"
+#include "cr16-tdep.h"
+
+const gdb_byte breakpoint_elf[] = { 0xC8, 0x00 };
+
+typedef unsigned short wordU;
+extern wordU words[3];
+extern ULONGLONG allWords;
+extern ins currInsn;
+
+/* Certain important register numbers. */
+enum
+{
+ CR16_R0_REGNUM = 0,
+ CR16_R7_REGNUM = 7,
+ CR16_R12_REGNUM = 12,
+ CR16_FP_REGNUM = 13,
+ CR16_RA_REGNUM = 14,
+ CR16_SP_REGNUM = 15,
+ CR16_PC_REGNUM = 16,
+};
+
+/* This structure holds the results of a prologue analysis. */
+struct cr16_prologue
+{
+ /* The offset from the frame base to the stack pointer --- always
+ zero or negative.
+
+ Calling this a "size" is a bit misleading, but given that the
+ stack grows downwards, using offsets for everything keeps one
+ from going completely sign-crazy: you never change anything's
+ sign for an ADD instruction; always change the second operand's
+ sign for a SUB instruction; and everything takes care of
+ itself. */
+ int frame_size;
+
+ /* Non-zero if this function has initialized the frame pointer from
+ the stack pointer, zero otherwise. */
+ int has_frame_ptr;
+
+ /* If has_frame_ptr is non-zero, this is the offset from the frame
+ base to where the frame pointer points. This is always zero or
+ negative. */
+ int frame_ptr_offset;
+
+ /* The address of the first instruction at which the frame has been
+ set up and the arguments are where the debug info says they are
+ --- as best as we can tell. */
+ CORE_ADDR prologue_end;
+
+ /* reg_offset[R] is the offset from the CFA at which register R is
+ saved, or 1 if register R has not been saved. (Real values are
+ always zero or negative.) */
+ int reg_offset[CR16_NUM_REGS];
+};
+
+/* Implement the "register_name" gdbarch method. */
+static const char *
+cr16_register_name (struct gdbarch *gdbarch, int regnr)
+{
+ static const char *const reg_names[] = {
+ "r0",
+ "r1",
+ "r2",
+ "r3",
+ "r4",
+ "r5",
+ "r6",
+ "r7",
+ "r8",
+ "r9",
+ "r10",
+ "r11",
+ "r12",
+ "r13",
+ "ra",
+ "sp",
+ "pc",
+ "isp",
+ "usp",
+ "intbase",
+ "psr",
+ "cfg"
+ };
+
+ return reg_names[regnr];
+}
+
+static const char *
+cr16_linux_register_name (struct gdbarch *gdbarch, int regnr)
+{
+ static const char *const reg_names[] = {
+ "r0",
+ "r1",
+ "r2",
+ "r3",
+ "r4",
+ "r5",
+ "r6",
+ "r7",
+ "r8",
+ "r9",
+ "r10",
+ "r11",
+ "r12",
+ "r13",
+ "ra",
+ "psr",
+ "pc",
+ "r0r1_orig",
+ "intbase",
+ "usp",
+ "cfg"
+ };
+
+ return reg_names[regnr];
+}
+
+/* Implement the "register_type" gdbarch method. */
+static struct type *
+cr16_register_type (struct gdbarch *gdbarch, int reg_nr)
+{
+ switch (reg_nr)
+ {
+ case CR16_PC_REGNUM: /* Note:PC in CR16 is of 24 bits */
+ return builtin_type (gdbarch)->builtin_func_ptr;
+
+ case CR16_RA_REGNUM: /* Return address reg */
+ return builtin_type (gdbarch)->builtin_data_ptr;
+ break;
+
+ case CR16_FP_REGNUM: /*Frame Pointer reg */
+ case CR16_SP_REGNUM: /*Stack Pointer reg */
+ return builtin_type (gdbarch)->builtin_data_ptr;
+ break;
+
+ case SIM_CR16_ISP_REGNUM:
+ case SIM_CR16_USP_REGNUM:
+ case SIM_CR16_INTBASE_REGNUM:
+ return builtin_type (gdbarch)->builtin_int32;
+ break;
+
+ case SIM_CR16_PSR_REGNUM:
+ case SIM_CR16_CFG_REGNUM:
+ return builtin_type (gdbarch)->builtin_uint32;
+ break;
+
+ case SIM_CR16_R0_REGNUM:
+ case SIM_CR16_R1_REGNUM:
+ case SIM_CR16_R2_REGNUM:
+ case SIM_CR16_R3_REGNUM:
+ case SIM_CR16_R4_REGNUM:
+ case SIM_CR16_R5_REGNUM:
+ case SIM_CR16_R6_REGNUM:
+ case SIM_CR16_R7_REGNUM:
+ case SIM_CR16_R8_REGNUM:
+ case SIM_CR16_R9_REGNUM:
+ case SIM_CR16_R10_REGNUM:
+ case SIM_CR16_R11_REGNUM:
+ return builtin_type (gdbarch)->builtin_int16;
+ break;
+
+ case SIM_CR16_R12_REGNUM:
+ return builtin_type (gdbarch)->builtin_int32;
+ break;
+
+ default:
+ printf
+ ("\nRegister Type not supported\nFunction : cr16_register_type\n");
+ return 0;
+ break;
+ }
+}
+
+/* Function for finding saved registers in a 'struct pv_area'; this
+ function is passed to pv_area_scan.
+
+ If VALUE is a saved register, ADDR says it was saved at a constant
+ offset from the frame base, and SIZE indicates that the whole
+ register was saved, record its offset. */
+static void
+check_for_saved (void *result_untyped, pv_t addr, CORE_ADDR size, pv_t value)
+{
+ struct cr16_prologue *result = (struct cr16_prologue *) result_untyped;
+
+ if (value.kind == pvk_register
+ && value.k == 0
+ && pv_is_register (addr, CR16_SP_REGNUM)
+ && size == register_size (target_gdbarch, value.reg))
+ result->reg_offset[value.reg] = addr.k;
+}
+
+/* Define a "handle" struct for fetching the next opcode. */
+struct cr16_get_opcode_byte_handle
+{
+ CORE_ADDR pc;
+};
+
+/* Use functions from opcodes/cr16-dis.c by making them non-static */
+extern void make_instruction (void);
+extern int match_opcode (void);
+extern void get_words_at_PC (bfd_vma memaddr, struct disassemble_info *info);
+
+/* Analyze a prologue starting at START_PC, going no further than
+ LIMIT_PC. Fill in RESULT as appropriate. */
+static void
+cr16_analyze_prologue (CORE_ADDR start_pc,
+ CORE_ADDR limit_pc, struct cr16_prologue *result)
+{
+ CORE_ADDR pc, next_pc;
+ gdb_byte buf[6];
+ char insn_byte1, insn_byte2;
+ int rn;
+ int length;
+ pv_t reg[CR16_NUM_REGS];
+ struct pv_area *stack;
+ struct cleanup *back_to;
+ CORE_ADDR after_last_frame_setup_insn = start_pc;
+ int is_decoded; /* Nonzero means instruction has a match. */
+
+ memset (result, 0, sizeof (*result));
+
+ for (rn = 0; rn < CR16_NUM_REGS; rn++)
+ {
+ reg[rn] = pv_register (rn, 0);
+ result->reg_offset[rn] = 1;
+ }
+
+ stack = make_pv_area (CR16_SP_REGNUM, gdbarch_addr_bit (target_gdbarch));
+ back_to = make_cleanup_free_pv_area (stack);
+
+ pc = start_pc;
+ while (pc < limit_pc)
+ {
+ target_read_memory (pc, buf, 6); /* Read 6 bytes, max 48 bit opcode */
+ words[0] = buf[1] << 8 | buf[0];
+ words[1] = buf[3] << 8 | buf[2];
+ words[2] = buf[5] << 8 | buf[4];
+ allWords =
+ ((ULONGLONG) words[0] << 32) + ((unsigned long) words[1] << 16) +
+ words[2];
+
+ /* Find a matching opcode in table. */
+ is_decoded = match_opcode ();
+ make_instruction ();
+ length = currInsn.size;
+ next_pc = pc + length;
+ insn_byte1 = (words[0] >> 8) & 0xFF;
+ /* If PUSH, then save RA and other regs. */
+ if (insn_byte1 == 0x01)
+ {
+ int r1, r2;
+ int r;
+ insn_byte2 = words[0];
+
+ if (insn_byte2 & 0x80)
+ {
+ reg[CR16_SP_REGNUM] = pv_add_constant (reg[CR16_SP_REGNUM], -4);
+ pv_area_store (stack, reg[CR16_SP_REGNUM], 4,
+ reg[CR16_RA_REGNUM]);
+ }
+ /* Start Register=r1, 3 bit imm count=r2 */
+ r1 = insn_byte2 & 0x0F;
+ r2 = ((insn_byte2 & 0x70) >> 4);
+ r2 = r2 + r1 + 1;
+
+ for (r = r1; r < r2; r++)
+ {
+ if (r >= CR16_R12_REGNUM)
+ {
+ reg[CR16_SP_REGNUM] =
+ pv_add_constant (reg[CR16_SP_REGNUM], -4);
+ pv_area_store (stack, reg[CR16_SP_REGNUM], 4, reg[r]);
+ r++;
+ }
+ else
+ {
+ reg[CR16_SP_REGNUM] =
+ pv_add_constant (reg[CR16_SP_REGNUM], -2);
+ pv_area_store (stack, reg[CR16_SP_REGNUM], 2, reg[r]);
+ }
+ }
+ after_last_frame_setup_insn = next_pc;
+ }
+ /* Add constant to SP */
+ else if (insn_byte1 == 0x60)
+ {
+ int rdst;
+ signed short addend;
+ insn_byte2 = words[0];
+ rdst = insn_byte2 & 0x0F;
+ if (rdst == CR16_SP_REGNUM)
+ {
+ if (length == 2)
+ {
+ addend = (insn_byte2 & 0xF0) >> 4;
+ reg[rdst] = pv_add_constant (reg[rdst], addend);
+ }
+ if (length == 4)
+ {
+ addend = words[1];
+ reg[rdst] = pv_add_constant (reg[rdst], addend);
+ }
+ after_last_frame_setup_insn = next_pc;
+ }
+ }
+ /* Check for MOVD insn */
+ else if (insn_byte1 == 0x55)
+ {
+ int rdst, rsrc;
+ insn_byte2 = words[0];
+ rsrc = (insn_byte2 & 0xF0) >> 4;
+ rdst = (insn_byte2 & 0x0F);
+ reg[rdst] = reg[rsrc];
+ if (rsrc == CR16_SP_REGNUM && rdst == CR16_FP_REGNUM)
+ after_last_frame_setup_insn = next_pc;
+ }
+ else if (((insn_byte1 >> 4) & 0x0F) == 0xd)
+ {
+ /* This moves an argument register to the stack. Don't
+ record it, but allow it to be a part of the prologue. */
+ after_last_frame_setup_insn = next_pc;
+ }
+ else
+ {
+ break; /* Terminate the prologue scan. */
+ }
+
+ pc = next_pc;
+ }
+
+ /* Is the frame size (offset, really) a known constant? */
+ if (pv_is_register (reg[CR16_SP_REGNUM], CR16_SP_REGNUM))
+ result->frame_size = reg[CR16_SP_REGNUM].k;
+
+ /* Was the frame pointer initialized? */
+ if (pv_is_register (reg[CR16_FP_REGNUM], CR16_SP_REGNUM))
+ {
+ result->has_frame_ptr = 1;
+ result->frame_ptr_offset = reg[CR16_FP_REGNUM].k;
+ }
+
+ /* Record where all the registers were saved. */
+ pv_area_scan (stack, check_for_saved, (void *) result);
+
+ result->prologue_end = after_last_frame_setup_insn;
+ do_cleanups (back_to);
+}
+
+
+/* Implement the "skip_prologue" gdbarch method. */
+static CORE_ADDR
+cr16_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+ const char *name;
+ CORE_ADDR func_addr, func_end;
+ struct cr16_prologue p;
+
+ /* Try to find the extent of the function that contains PC. */
+ if (!find_pc_partial_function (pc, &name, &func_addr, &func_end))
+ return pc;
+
+ cr16_analyze_prologue (pc, func_end, &p);
+ return p.prologue_end;
+}
+
+/* Given a frame described by THIS_FRAME, decode the prologue of its
+ associated function if there is not cache entry as specified by
+ THIS_PROLOGUE_CACHE. Save the decoded prologue in the cache and
+ return that struct as the value of this function. */
+static struct cr16_prologue *
+cr16_analyze_frame_prologue (struct frame_info *this_frame,
+ void **this_prologue_cache)
+{
+ if (!*this_prologue_cache)
+ {
+ CORE_ADDR func_start, stop_addr;
+
+ *this_prologue_cache = FRAME_OBSTACK_ZALLOC (struct cr16_prologue);
+
+ func_start = get_frame_func (this_frame);
+ stop_addr = get_frame_pc (this_frame);
+
+ /* If we couldn't find any function containing the PC, then
+ just initialize the prologue cache, but don't do anything. */
+ if (!func_start)
+ stop_addr = func_start;
+
+ cr16_analyze_prologue (func_start, stop_addr, *this_prologue_cache);
+ }
+
+ return *this_prologue_cache;
+}
+
+/* Given the next frame and a prologue cache, return this frame's
+ base. */
+static CORE_ADDR
+cr16_frame_base (struct frame_info *this_frame, void **this_prologue_cache)
+{
+ struct cr16_prologue *p
+ = cr16_analyze_frame_prologue (this_frame, this_prologue_cache);
+
+ /* In functions that use alloca, the distance between the stack
+ pointer and the frame base varies dynamically, so we can't use
+ the SP plus static information like prologue analysis to find the
+ frame base. However, such functions must have a frame pointer,
+ to be able to restore the SP on exit. So whenever we do have a
+ frame pointer, use that to find the base. */
+ if (p->has_frame_ptr)
+ {
+ CORE_ADDR fp = get_frame_register_unsigned (this_frame, CR16_FP_REGNUM);
+ return fp - p->frame_ptr_offset;
+ }
+ else
+ {
+ CORE_ADDR sp = get_frame_register_unsigned (this_frame, CR16_SP_REGNUM);
+ return sp - p->frame_size;
+ }
+}
+
+/* Implement the "frame_this_id" method for unwinding frames. */
+static void
+cr16_frame_this_id (struct frame_info *this_frame,
+ void **this_prologue_cache, struct frame_id *this_id)
+{
+ *this_id =
+ frame_id_build (cr16_frame_base (this_frame, this_prologue_cache),
+ get_frame_func (this_frame));
+}
+
+static struct value *
+cr16_frame_prev_register (struct frame_info *this_frame,
+ void **this_prologue_cache, int regnum)
+{
+ struct cr16_prologue *p =
+ cr16_analyze_frame_prologue (this_frame, this_prologue_cache);
+ CORE_ADDR frame_base = cr16_frame_base (this_frame, this_prologue_cache);
+ int reg_size = register_size (get_frame_arch (this_frame), regnum);
+ ULONGEST ra_prev;
+
+ if (regnum == CR16_SP_REGNUM)
+ return frame_unwind_got_constant (this_frame, regnum, frame_base);
+
+ /* The call instruction has saved the return address on the RA register,
+ CR16_R13_REGNUM. So, we need not adjust anything directly. We will
+ analyze prologue as this RA register is pushed onto stack for further
+ leaf function calls to work */
+ else if (regnum == CR16_PC_REGNUM)
+ {
+ ra_prev = frame_unwind_register_unsigned (this_frame, CR16_RA_REGNUM);
+ ra_prev = ra_prev << 1;
+ return frame_unwind_got_constant (this_frame, CR16_PC_REGNUM, ra_prev);
+ }
+
+ /* If prologue analysis says we saved this register somewhere,
+ return a description of the stack slot holding it. */
+ else if (p->reg_offset[regnum] != 1)
+ {
+ return frame_unwind_got_memory (this_frame, regnum,
+ frame_base + p->reg_offset[regnum]);
+ }
+
+ /* Otherwise, presume we haven't changed the value of this
+ register, and get it from the next frame. */
+ else
+ {
+ return frame_unwind_got_register (this_frame, regnum, regnum);
+ }
+}
+
+static const struct frame_unwind cr16_frame_unwind = {
+ NORMAL_FRAME,
+ default_frame_unwind_stop_reason,
+ cr16_frame_this_id,
+ cr16_frame_prev_register,
+ NULL,
+ default_frame_sniffer
+};
+
+/* Implement the "unwind_pc" gdbarch method. */
+static CORE_ADDR
+cr16_unwind_pc (struct gdbarch *gdbarch, struct frame_info *this_frame)
+{
+ ULONGEST pc;
+
+ pc = frame_unwind_register_unsigned (this_frame, CR16_PC_REGNUM);
+ return pc;
+}
+
+/* Implement the "unwind_sp" gdbarch method. */
+static CORE_ADDR
+cr16_unwind_sp (struct gdbarch *gdbarch, struct frame_info *this_frame)
+{
+ ULONGEST sp;
+
+ sp = frame_unwind_register_unsigned (this_frame, CR16_SP_REGNUM);
+ return sp;
+}
+
+/* Implement the "dummy_id" gdbarch method. */
+static struct frame_id
+cr16_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
+{
+ return
+ frame_id_build (get_frame_register_unsigned (this_frame, CR16_SP_REGNUM),
+ get_frame_pc (this_frame));
+}
+
+/* Implement the "push_dummy_call" gdbarch method. */
+static CORE_ADDR
+cr16_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)
+{
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ int write_pass;
+ int sp_off = 0;
+ CORE_ADDR cfa;
+ int num_register_candidate_args;
+
+ struct type *func_type = value_type (function);
+
+ /* Dereference function pointer types. */
+ while (TYPE_CODE (func_type) == TYPE_CODE_PTR)
+ func_type = TYPE_TARGET_TYPE (func_type);
+
+ /* The end result had better be a function or a method. */
+ gdb_assert (TYPE_CODE (func_type) == TYPE_CODE_FUNC
+ || TYPE_CODE (func_type) == TYPE_CODE_METHOD);
+
+ /* Functions with a variable number of arguments have all of their
+ variable arguments and the last non-variable argument passed
+ on the stack.
+
+ Otherwise, we can pass up to four arguments on the stack.
+
+ Once computed, we leave this value alone. I.e. we don't update
+ it in case of a struct return going in a register or an argument
+ requiring multiple registers, etc. We rely instead on the value
+ of the ``arg_reg'' variable to get these other details correct. */
+
+ if (TYPE_VARARGS (func_type))
+ num_register_candidate_args = TYPE_NFIELDS (func_type) - 1;
+ else
+ num_register_candidate_args = 4;
+
+ /* We make two passes; the first does the stack allocation,
+ the second actually stores the arguments. */
+ for (write_pass = 0; write_pass <= 1; write_pass++)
+ {
+ int i;
+ int arg_reg = CR16_R0_REGNUM;
+
+ if (write_pass)
+ sp = align_down (sp - sp_off, 4);
+ sp_off = 0;
+
+ if (struct_return)
+ {
+ struct type *return_type = TYPE_TARGET_TYPE (func_type);
+
+ gdb_assert (TYPE_CODE (return_type) == TYPE_CODE_STRUCT
+ || TYPE_CODE (func_type) == TYPE_CODE_UNION);
+
+ if (TYPE_LENGTH (return_type) > 16
+ || TYPE_LENGTH (return_type) % 4 != 0)
+ {
+ if (write_pass)
+ regcache_cooked_write_unsigned (regcache, CR16_R12_REGNUM,
+ struct_addr);
+ }
+ }
+
+ /* Push the arguments. */
+ for (i = 0; i < nargs; i++)
+ {
+ struct value *arg = args[i];
+ const gdb_byte *arg_bits = value_contents_all (arg);
+ struct type *arg_type = check_typedef (value_type (arg));
+ ULONGEST arg_size = TYPE_LENGTH (arg_type);
+
+ if (i == 0 && struct_addr != 0 && !struct_return
+ && TYPE_CODE (arg_type) == TYPE_CODE_PTR
+ && extract_unsigned_integer (arg_bits, 4,
+ byte_order) == struct_addr)
+ {
+ /* This argument represents the address at which C++ (and
+ possibly other languages) store their return value.
+ Put this value in R15. */
+ if (write_pass)
+ regcache_cooked_write_unsigned (regcache, CR16_R12_REGNUM,
+ struct_addr);
+ }
+ else if (TYPE_CODE (arg_type) != TYPE_CODE_STRUCT
+ && TYPE_CODE (arg_type) != TYPE_CODE_UNION)
+ {
+ /* Argument is a scalar. */
+ if (arg_size == 8)
+ {
+ if (i < num_register_candidate_args
+ && arg_reg <= CR16_R7_REGNUM - 1)
+ {
+ /* If argument registers are going to be used to pass
+ an 8 byte scalar, the ABI specifies that two registers
+ must be available. */
+ if (write_pass)
+ {
+ regcache_cooked_write_unsigned (regcache, arg_reg,
+ extract_unsigned_integer
+ (arg_bits, 4,
+ byte_order));
+ regcache_cooked_write_unsigned (regcache,
+ arg_reg + 1,
+ extract_unsigned_integer
+ (arg_bits + 4, 4,
+ byte_order));
+ }
+ arg_reg += 2;
+ }
+ else
+ {
+ sp_off = align_up (sp_off, 4);
+ /* Otherwise, pass the 8 byte scalar on the stack. */
+ if (write_pass)
+ write_memory (sp + sp_off, arg_bits, 8);
+ sp_off += 8;
+ }
+ }
+ else
+ {
+ ULONGEST u;
+
+ gdb_assert (arg_size <= 4);
+
+ u =
+ extract_unsigned_integer (arg_bits, arg_size, byte_order);
+
+ if (i < num_register_candidate_args
+ && arg_reg <= CR16_R7_REGNUM)
+ {
+ if (write_pass)
+ regcache_cooked_write_unsigned (regcache, arg_reg, u);
+ arg_reg += 1;
+ }
+ else
+ {
+ int p_arg_size = 4;
+
+ if (TYPE_PROTOTYPED (func_type)
+ && i < TYPE_NFIELDS (func_type))
+ {
+ struct type *p_arg_type =
+ TYPE_FIELD_TYPE (func_type, i);
+ p_arg_size = TYPE_LENGTH (p_arg_type);
+ }
+
+ sp_off = align_up (sp_off, p_arg_size);
+
+ if (write_pass)
+ write_memory_unsigned_integer (sp + sp_off,
+ p_arg_size, byte_order,
+ u);
+ sp_off += p_arg_size;
+ }
+ }
+ }
+ else
+ {
+ /* Argument is a struct or union. Pass as much of the struct
+ in registers, if possible. Pass the rest on the stack. */
+ while (arg_size > 0)
+ {
+ if (i < num_register_candidate_args
+ && arg_reg <= CR16_R7_REGNUM
+ && arg_size <= 4 * (CR16_R7_REGNUM - arg_reg + 1)
+ && arg_size % 4 == 0)
+ {
+ int len = min (arg_size, 4);
+
+ if (write_pass)
+ regcache_cooked_write_unsigned (regcache, arg_reg,
+ extract_unsigned_integer
+ (arg_bits, len,
+ byte_order));
+ arg_bits += len;
+ arg_size -= len;
+ arg_reg++;
+ }
+ else
+ {
+ sp_off = align_up (sp_off, 4);
+ if (write_pass)
+ write_memory (sp + sp_off, arg_bits, arg_size);
+ sp_off += align_up (arg_size, 4);
+ arg_size = 0;
+ }
+ }
+ }
+ }
+ }
+
+ /* Keep track of the stack address prior to pushing the return address.
+ This is the value that we'll return. */
+ cfa = sp;
+
+ /* Push the return address. */
+ sp = sp - 4;
+ write_memory_unsigned_integer (sp, 4, byte_order, bp_addr);
+
+ /* Update the stack pointer. */
+ regcache_cooked_write_unsigned (regcache, CR16_SP_REGNUM, sp);
+
+ return cfa;
+}
+
+/* Implement the "return_value" gdbarch method. */
+static enum return_value_convention
+cr16_return_value (struct gdbarch *gdbarch,
+ struct type *func_type,
+ struct type *valtype,
+ struct regcache *regcache,
+ gdb_byte * readbuf, const gdb_byte * writebuf)
+{
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ ULONGEST valtype_len = TYPE_LENGTH (valtype);
+
+ if (TYPE_LENGTH (valtype) > 16
+ || ((TYPE_CODE (valtype) == TYPE_CODE_STRUCT
+ || TYPE_CODE (valtype) == TYPE_CODE_UNION)
+ && TYPE_LENGTH (valtype) % 4 != 0))
+ return RETURN_VALUE_STRUCT_CONVENTION;
+
+ if (readbuf)
+ {
+ ULONGEST u;
+ int argreg = CR16_R0_REGNUM;
+ int offset = 0;
+
+ while (valtype_len > 0)
+ {
+ int len = min (valtype_len, 4);
+
+ regcache_cooked_read_unsigned (regcache, argreg, &u);
+ store_unsigned_integer (readbuf + offset, len, byte_order, u);
+ valtype_len -= len;
+ offset += len;
+ argreg++;
+ }
+ }
+
+ if (writebuf)
+ {
+ ULONGEST u;
+ int argreg = CR16_R0_REGNUM;
+ int offset = 0;
+
+ while (valtype_len > 0)
+ {
+ int len = min (valtype_len, 4);
+
+ u = extract_unsigned_integer (writebuf + offset, len, byte_order);
+ regcache_cooked_write_unsigned (regcache, argreg, u);
+ valtype_len -= len;
+ offset += len;
+ argreg++;
+ }
+ }
+
+ return RETURN_VALUE_REGISTER_CONVENTION;
+}
+
+/* Implement the "breakpoint_from_pc" gdbarch method. */
+static const gdb_byte *
+cr16_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR * pcptr,
+ int *lenptr)
+{
+ /* We use different breakpoint instructions for ELF and uClinux.
+ See cr16-linux-tdep.c for more details.
+ */
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ *lenptr = 2;
+ if (tdep == NULL || tdep->breakpoint == NULL)
+ {
+ return breakpoint_elf;
+ }
+ return tdep->breakpoint;
+}
+
+
+
+/* Allocate and initialize a gdbarch object. */
+static struct gdbarch *
+cr16_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+{
+ struct gdbarch *gdbarch;
+ struct gdbarch_tdep *tdep;
+ int elf_flags;
+
+ /* Extract the elf_flags if available. */
+ if (info.abfd != NULL
+ && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour)
+ elf_flags = elf_elfheader (info.abfd)->e_flags;
+ else
+ elf_flags = 0;
+
+ /* Try to find the architecture in the list of already defined
+ architectures. */
+ for (arches = gdbarch_list_lookup_by_info (arches, &info);
+ arches != NULL;
+ arches = gdbarch_list_lookup_by_info (arches->next, &info))
+ {
+ if (gdbarch_tdep (arches->gdbarch)->elf_flags != elf_flags)
+ continue;
+
+ return arches->gdbarch;
+ }
+ /* None found, create a new architecture from the information
+ provided. */
+ tdep = (struct gdbarch_tdep *) xcalloc (1,sizeof (struct gdbarch_tdep));
+ tdep->elf_flags = elf_flags;
+ gdbarch = gdbarch_alloc (&info, tdep);
+
+ set_gdbarch_num_pseudo_regs (gdbarch, 0);
+ if(info.osabi == GDB_OSABI_LINUX)
+ {
+ set_gdbarch_num_regs (gdbarch, CR16_LINUX_NUM_REGS);
+ set_gdbarch_register_name (gdbarch, cr16_linux_register_name);
+ }
+ else
+ {
+ set_gdbarch_num_regs (gdbarch, CR16_NUM_REGS);
+ set_gdbarch_register_name (gdbarch, cr16_register_name);
+ }
+ set_gdbarch_register_type (gdbarch, cr16_register_type);
+ set_gdbarch_pc_regnum (gdbarch, CR16_PC_REGNUM);
+ set_gdbarch_sp_regnum (gdbarch, CR16_SP_REGNUM);
+ set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+ set_gdbarch_decr_pc_after_break (gdbarch, 2);
+ set_gdbarch_breakpoint_from_pc (gdbarch, cr16_breakpoint_from_pc);
+ set_gdbarch_skip_prologue (gdbarch, cr16_skip_prologue);
+
+ /* Passing NULL values in the following two functions
+ for the time being, to fix later */
+ set_gdbarch_print_insn (gdbarch, print_insn_cr16);
+ set_gdbarch_unwind_pc (gdbarch, cr16_unwind_pc);
+ set_gdbarch_unwind_sp (gdbarch, cr16_unwind_sp);
+
+ /* Methods for saving / extracting a dummy frame's ID.
+ The ID's stack address must match the SP value returned by
+ PUSH_DUMMY_CALL, and saved by generic_save_dummy_frame_tos. */
+ set_gdbarch_dummy_id (gdbarch, cr16_dummy_id);
+ set_gdbarch_push_dummy_call (gdbarch, cr16_push_dummy_call);
+ /* Target builtin data types. */
+ set_gdbarch_char_signed (gdbarch, 8);
+ set_gdbarch_short_bit (gdbarch, 16);
+
+ /* if we don't pass the option -mint32
+ To fix : add if else case depending on the option passed,
+ sp that we can have int size as 16 or 32 bits both. */
+ set_gdbarch_int_bit (gdbarch, 16);
+ set_gdbarch_long_bit (gdbarch, 32);
+ set_gdbarch_long_long_bit (gdbarch, 64);
+ set_gdbarch_float_bit (gdbarch, 32);
+
+ set_gdbarch_ptr_bit (gdbarch, 32);
+ set_gdbarch_float_format (gdbarch, floatformats_ieee_single);
+ set_gdbarch_double_bit (gdbarch, 64);
+ set_gdbarch_long_double_bit (gdbarch, 64);
+ set_gdbarch_double_format (gdbarch, floatformats_ieee_double);
+ set_gdbarch_long_double_format (gdbarch, floatformats_ieee_double);
+
+ frame_unwind_append_unwinder (gdbarch, &cr16_frame_unwind);
+ set_gdbarch_return_value (gdbarch, cr16_return_value);
+
+ /* Hook in ABI-specific overrides, if they have been registered. */
+ gdbarch_init_osabi (info, gdbarch);
+
+ return gdbarch;
+
+}
+
+/* -Wmissing-prototypes */
+extern initialize_file_ftype _initialize_cr16_tdep;
+
+/* Register the above initialization routine. */
+void
+_initialize_cr16_tdep (void)
+{
+ register_gdbarch_init (bfd_arch_cr16, cr16_gdbarch_init);
+}
--- ./gdb_src.orig/gdb/cr16-tdep.h 1970-01-01 05:30:00.000000000 +0530
+++ ./gdb_src/gdb/cr16-tdep.h 2012-09-04 13:05:39.000000000 +0530
@@ -0,0 +1,36 @@
+/* GNU/Linux on CR16 target support.
+ Copyright (C) 2011-2012 Free Software Foundation, Inc.
+
+ Contributed by Kaushik Phatak (kaushik.pahatk@kpitcummins.com)
+ KPIT Cummins Infosystems Limited, Pune India.
+
+ 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/>. */
+
+#define CR16_NUM_REGS 22
+#define CR16_LINUX_NUM_REGS 21
+
+extern const gdb_byte breakpoint_elf[];
+extern const gdb_byte breakpoint_linux[];
+
+/* Target-dependent structure in gdbarch. */
+/* Architecture specific data. */
+struct gdbarch_tdep
+{
+ /* The ELF header flags specify the multilib used. */
+ int elf_flags;
+
+ const char *breakpoint; /* Breakpoint instruction. */
+};