From 9102122396b6b977eb2e283a6c092d1ab510e788 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 29 Dec 2010 16:11:49 +0000 Subject: gdb: bfin: new port Initial support for Blackfin processors. This supports the standard ABI. Signed-off-by: Jie Zhang Signed-off-by: Mike Frysinger --- gdb/ChangeLog | 11 + gdb/Makefile.in | 4 +- gdb/NEWS | 4 + gdb/bfin-linux-tdep.c | 175 +++++++++ gdb/bfin-tdep.c | 862 ++++++++++++++++++++++++++++++++++++++++++++ gdb/bfin-tdep.h | 102 ++++++ gdb/configure.tgt | 9 + gdb/syscalls/bfin-linux.xml | 326 +++++++++++++++++ 8 files changed, 1492 insertions(+), 1 deletion(-) create mode 100644 gdb/bfin-linux-tdep.c create mode 100644 gdb/bfin-tdep.c create mode 100644 gdb/bfin-tdep.h create mode 100644 gdb/syscalls/bfin-linux.xml diff --git a/gdb/ChangeLog b/gdb/ChangeLog index e57926c..88cdd75 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,14 @@ +2010-12-29 Jie Zhang + Mike Frysinger + + * Makefile.in (ALLDEPFILES): Add bfin-linux-tdep.c and bfin-tdep.c. + (HFILES_NO_SRCDIR): Add bfin-tdep.h. + (ALL_TARGET_OBS): Add bfin-linux-tdep.o and bfin-tdep.o. + * NEWS: Mention new Blackfin port. + * bfin-tdep.c, bfin-tdep.h, bfin-linux-tdep.c, + syscalls/bfin-linux.xml: New files. + * configure.tgt (bfin-*-*): Handle bfin targets. + 2010-12-29 Hui Zhu * ax-gdb.c (gen_expr): Change error message. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index ff10039..e0f6636 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -508,6 +508,7 @@ ALL_TARGET_OBS = \ armnbsd-tdep.o armobsd-tdep.o \ arm-tdep.o arm-wince-tdep.o \ avr-tdep.o \ + bfin-linux-tdep.o bfin-tdep.o \ cris-tdep.o \ dicos-tdep.o \ frv-linux-tdep.o frv-tdep.o \ @@ -782,7 +783,7 @@ annotate.h sim-regno.h dictionary.h dfp.h main.h frame-unwind.h \ remote-fileio.h i386-linux-tdep.h vax-tdep.h objc-lang.h \ sentinel-frame.h bcache.h symfile.h windows-tdep.h linux-tdep.h \ gdb_usleep.h jit.h xml-syscall.h ada-operator.inc microblaze-tdep.h \ -psymtab.h psympriv.h progspace.h +psymtab.h psympriv.h progspace.h bfin-tdep.h # Header files that already have srcdir in them, or which are in objdir. @@ -1420,6 +1421,7 @@ ALLDEPFILES = \ arm-linux-nat.c arm-linux-tdep.c arm-symbian-tdep.c arm-tdep.c \ armnbsd-nat.c armbsd-tdep.c armnbsd-tdep.c armobsd-tdep.c \ avr-tdep.c \ + bfin-linux-tdep.c bfin-tdep.c \ bsd-uthread.c bsd-kvm.c \ core-regset.c corelow.c \ dcache.c dicos-tdep.c darwin-nat.c \ diff --git a/gdb/NEWS b/gdb/NEWS index 91f1835..96b6f05 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -111,6 +111,10 @@ ** GDBserver is now supported on PowerPC LynxOS (versions 4.x and 5.x), and i686 LynxOS (version 5.x). +* New targets: + +Analog Devices, Inc. Blackfin Processor bfin-* + * Ada task switching is now supported on sparc-elf targets when debugging a program using the Ravenscar Profile. For more information, see the "Tasking Support when using the Ravenscar Profile" section diff --git a/gdb/bfin-linux-tdep.c b/gdb/bfin-linux-tdep.c new file mode 100644 index 0000000..545bdcf --- /dev/null +++ b/gdb/bfin-linux-tdep.c @@ -0,0 +1,175 @@ +/* Target-dependent code for Analog Devices Blackfin processor, for GDB. + + Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 + Free Software Foundation, Inc. + + Contributed by Analog Devices, Inc. + + 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 "arch-utils.h" +#include "regcache.h" +#include "tramp-frame.h" +#include "trad-frame.h" +#include "osabi.h" +#include "xml-syscall.h" +#include "linux-tdep.h" +#include "bfin-tdep.h" + +/* From . */ + +#define SIGCONTEXT_OFFSET 168 + +static const int bfin_linux_sigcontext_reg_offset[BFIN_NUM_REGS] = +{ + 0 * 4, /* %r0 */ + 1 * 4, /* %r1 */ + 2 * 4, /* %r2 */ + 3 * 4, /* %r3 */ + 4 * 4, /* %r4 */ + 5 * 4, /* %r5 */ + 6 * 4, /* %r6 */ + 7 * 4, /* %r7 */ + 8 * 4, /* %p0 */ + 9 * 4, /* %p1 */ + 10 * 4, /* %p2 */ + 11 * 4, /* %p3 */ + 12 * 4, /* %p4 */ + 13 * 4, /* %p5 */ + 14 * 4, /* %sp */ + 23 * 4, /* %fp */ + 24 * 4, /* %i0 */ + 25 * 4, /* %i1 */ + 26 * 4, /* %i2 */ + 27 * 4, /* %i3 */ + 28 * 4, /* %m0 */ + 29 * 4, /* %m1 */ + 30 * 4, /* %m2 */ + 31 * 4, /* %m3 */ + 36 * 4, /* %b0 */ + 37 * 4, /* %b1 */ + 38 * 4, /* %b2 */ + 39 * 4, /* %b3 */ + 32 * 4, /* %l0 */ + 33 * 4, /* %l1 */ + 34 * 4, /* %l2 */ + 35 * 4, /* %l3 */ + 17 * 4, /* %a0x */ + 15 * 4, /* %a0w */ + 18 * 4, /* %a1x */ + 16 * 4, /* %a1w */ + 19 * 4, /* %astat */ + 20 * 4, /* %rets */ + 40 * 4, /* %lc0 */ + 42 * 4, /* %lt0 */ + 44 * 4, /* %lb0 */ + 41 * 4, /* %lc1 */ + 43 * 4, /* %lt1 */ + 45 * 4, /* %lb1 */ + -1, /* %cycles */ + -1, /* %cycles2 */ + -1, /* %usp */ + 46 * 4, /* %seqstat */ + -1, /* syscfg */ + 21 * 4, /* %reti */ + 22 * 4, /* %retx */ + -1, /* %retn */ + -1, /* %rete */ + 21 * 4, /* %pc */ +}; + +/* Signal trampolines. */ + +static void +bfin_linux_sigframe_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_sp (this_frame); + CORE_ADDR pc = get_frame_pc (this_frame); + CORE_ADDR sigcontext = sp + SIGCONTEXT_OFFSET; + struct frame_id this_id; + const int *reg_offset = bfin_linux_sigcontext_reg_offset; + int i; + + for (i = 0; i < BFIN_NUM_REGS; i++) + if (reg_offset[i] != -1) + trad_frame_set_reg_addr (this_cache, i, sigcontext + reg_offset[i]); + + /* This would come after the LINK instruction in the ret_from_signal + function, hence the frame id would be SP + 8. */ + trad_frame_set_id (this_cache, frame_id_build (sp + 8, pc)); +} + +static const struct tramp_frame bfin_linux_sigframe = +{ + SIGTRAMP_FRAME, + 4, + { + { 0x00ADE128, 0xffffffff }, /* P0 = __NR_rt_sigreturn; */ + { 0x00A0, 0xffff }, /* EXCPT 0; */ + { TRAMP_SENTINEL_INSN, -1 }, + }, + bfin_linux_sigframe_init, +}; + +static LONGEST +bfin_linux_get_syscall_number (struct gdbarch *gdbarch, + ptid_t ptid) +{ + struct regcache *regcache = get_thread_regcache (ptid); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + /* The content of a register. */ + gdb_byte buf[4]; + /* The result. */ + LONGEST ret; + + /* Getting the system call number from the register. + When dealing with Blackfin architecture, this information + is stored at %p0 register. */ + regcache_cooked_read (regcache, BFIN_P0_REGNUM, buf); + + ret = extract_signed_integer (buf, 4, byte_order); + + return ret; +} + +static void +bfin_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) +{ + linux_init_abi (info, gdbarch); + + /* Set the sigtramp frame sniffer. */ + tramp_frame_prepend_unwinder (gdbarch, &bfin_linux_sigframe); + + /* Functions for 'catch syscall'. */ + set_xml_syscall_file_name ("syscalls/bfin-linux.xml"); + set_gdbarch_get_syscall_number (gdbarch, + bfin_linux_get_syscall_number); +} + +/* Provide a prototype to silence -Wmissing-prototypes. */ +extern initialize_file_ftype _initialize_bfin_linux_tdep; + +void +_initialize_bfin_linux_tdep (void) +{ + gdbarch_register_osabi (bfd_arch_bfin, 0, GDB_OSABI_LINUX, + bfin_linux_init_abi); +} diff --git a/gdb/bfin-tdep.c b/gdb/bfin-tdep.c new file mode 100644 index 0000000..ddebdf9 --- /dev/null +++ b/gdb/bfin-tdep.c @@ -0,0 +1,862 @@ +/* Target-dependent code for Analog Devices Blackfin processor, for GDB. + + Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 + Free Software Foundation, Inc. + + Contributed by Analog Devices, Inc. + + 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 "gdb_string.h" +#include "inferior.h" +#include "gdbcore.h" +#include "arch-utils.h" +#include "regcache.h" +#include "frame.h" +#include "frame-unwind.h" +#include "frame-base.h" +#include "trad-frame.h" +#include "dis-asm.h" +#include "gdb_assert.h" +#include "dwarf2-frame.h" +#include "symtab.h" +#include "elf-bfd.h" +#include "elf/bfin.h" +#include "osabi.h" +#include "infcall.h" +#include "xml-syscall.h" +#include "bfin-tdep.h" + +/* Macros used by prologue functions. */ +#define P_LINKAGE 0xE800 +#define P_MINUS_SP1 0x0140 +#define P_MINUS_SP2 0x05C0 +#define P_MINUS_SP3 0x0540 +#define P_MINUS_SP4 0x04C0 +#define P_SP_PLUS 0x6C06 +#define P_P2_LOW 0xE10A +#define P_P2_HIGH 0XE14A +#define P_SP_EQ_SP_PLUS_P2 0X5BB2 +#define P_SP_EQ_P2_PLUS_SP 0x5B96 +#define P_MINUS_MINUS_SP_EQ_RETS 0x0167 + +/* Macros used for program flow control. */ +/* 16 bit instruction, max */ +#define P_16_BIT_INSR_MAX 0xBFFF +/* 32 bit instruction, min */ +#define P_32_BIT_INSR_MIN 0xC000 +/* 32 bit instruction, max */ +#define P_32_BIT_INSR_MAX 0xE801 +/* jump (preg), 16-bit, min */ +#define P_JUMP_PREG_MIN 0x0050 +/* jump (preg), 16-bit, max */ +#define P_JUMP_PREG_MAX 0x0057 +/* jump (pc+preg), 16-bit, min */ +#define P_JUMP_PC_PLUS_PREG_MIN 0x0080 +/* jump (pc+preg), 16-bit, max */ +#define P_JUMP_PC_PLUS_PREG_MAX 0x0087 +/* jump.s pcrel13m2, 16-bit, min */ +#define P_JUMP_S_MIN 0x2000 +/* jump.s pcrel13m2, 16-bit, max */ +#define P_JUMP_S_MAX 0x2FFF +/* jump.l pcrel25m2, 32-bit, min */ +#define P_JUMP_L_MIN 0xE200 +/* jump.l pcrel25m2, 32-bit, max */ +#define P_JUMP_L_MAX 0xE2FF +/* conditional jump pcrel11m2, 16-bit, min */ +#define P_IF_CC_JUMP_MIN 0x1800 +/* conditional jump pcrel11m2, 16-bit, max */ +#define P_IF_CC_JUMP_MAX 0x1BFF +/* conditional jump(bp) pcrel11m2, 16-bit, min */ +#define P_IF_CC_JUMP_BP_MIN 0x1C00 +/* conditional jump(bp) pcrel11m2, 16-bit, max */ +#define P_IF_CC_JUMP_BP_MAX 0x1FFF +/* conditional !jump pcrel11m2, 16-bit, min */ +#define P_IF_NOT_CC_JUMP_MIN 0x1000 +/* conditional !jump pcrel11m2, 16-bit, max */ +#define P_IF_NOT_CC_JUMP_MAX 0x13FF +/* conditional jump(bp) pcrel11m2, 16-bit, min */ +#define P_IF_NOT_CC_JUMP_BP_MIN 0x1400 +/* conditional jump(bp) pcrel11m2, 16-bit, max */ +#define P_IF_NOT_CC_JUMP_BP_MAX 0x17FF +/* call (preg), 16-bit, min */ +#define P_CALL_PREG_MIN 0x0060 +/* call (preg), 16-bit, max */ +#define P_CALL_PREG_MAX 0x0067 +/* call (pc+preg), 16-bit, min */ +#define P_CALL_PC_PLUS_PREG_MIN 0x0070 +/* call (pc+preg), 16-bit, max */ +#define P_CALL_PC_PLUS_PREG_MAX 0x0077 +/* call pcrel25m2, 32-bit, min */ +#define P_CALL_MIN 0xE300 +/* call pcrel25m2, 32-bit, max */ +#define P_CALL_MAX 0xE3FF +/* RTS */ +#define P_RTS 0x0010 +/* MNOP */ +#define P_MNOP 0xC803 +/* EXCPT, 16-bit, min */ +#define P_EXCPT_MIN 0x00A0 +/* EXCPT, 16-bit, max */ +#define P_EXCPT_MAX 0x00AF +/* multi instruction mask 1, 16-bit */ +#define P_BIT_MULTI_INS_1 0xC000 +/* multi instruction mask 2, 16-bit */ +#define P_BIT_MULTI_INS_2 0x0800 + +/* The maximum bytes we search to skip the prologue. */ +#define UPPER_LIMIT 40 + +/* ASTAT bits */ +#define ASTAT_CC_POS 5 +#define ASTAT_CC (1 << ASTAT_CC_POS) + +/* Initial value: Register names used in BFIN's ISA documentation. */ + +static const char * const bfin_register_name_strings[] = +{ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "p0", "p1", "p2", "p3", "p4", "p5", "sp", "fp", + "i0", "i1", "i2", "i3", "m0", "m1", "m2", "m3", + "b0", "b1", "b2", "b3", "l0", "l1", "l2", "l3", + "a0x", "a0w", "a1x", "a1w", "astat", "rets", + "lc0", "lt0", "lb0", "lc1", "lt1", "lb1", "cycles", "cycles2", + "usp", "seqstat", "syscfg", "reti", "retx", "retn", "rete", + "pc", "cc", +}; + +#define NUM_BFIN_REGNAMES ARRAY_SIZE (bfin_register_name_strings) + + +/* In this diagram successive memory locations increase downwards or the + stack grows upwards with negative indices. (PUSH analogy for stack.) + + The top frame is the "frame" of the current function being executed. + + +--------------+ SP - + | local vars | ^ + +--------------+ | + | save regs | | + +--------------+ FP | + | old FP -|-- top + +--------------+ | frame + | RETS | | | + +--------------+ | | + | param 1 | | | + | param 2 | | | + | ... | | V + +--------------+ | - + | local vars | | ^ + +--------------+ | | + | save regs | | | + +--------------+<- | + | old FP -|-- next + +--------------+ | frame + | RETS | | | + +--------------+ | | + | param 1 | | | + | param 2 | | | + | ... | | V + +--------------+ | - + | local vars | | ^ + +--------------+ | | + | save regs | | | + +--------------+<- next frame + | old FP | | + +--------------+ | + | RETS | V + +--------------+ - + + The frame chain is formed as following: + + FP has the topmost frame. + FP + 4 has the previous FP and so on. */ + + +/* Map from DWARF2 register number to GDB register number. */ + +static const int map_gcc_gdb[] = +{ + BFIN_R0_REGNUM, + BFIN_R1_REGNUM, + BFIN_R2_REGNUM, + BFIN_R3_REGNUM, + BFIN_R4_REGNUM, + BFIN_R5_REGNUM, + BFIN_R6_REGNUM, + BFIN_R7_REGNUM, + BFIN_P0_REGNUM, + BFIN_P1_REGNUM, + BFIN_P2_REGNUM, + BFIN_P3_REGNUM, + BFIN_P4_REGNUM, + BFIN_P5_REGNUM, + BFIN_SP_REGNUM, + BFIN_FP_REGNUM, + BFIN_I0_REGNUM, + BFIN_I1_REGNUM, + BFIN_I2_REGNUM, + BFIN_I3_REGNUM, + BFIN_B0_REGNUM, + BFIN_B1_REGNUM, + BFIN_B2_REGNUM, + BFIN_B3_REGNUM, + BFIN_L0_REGNUM, + BFIN_L1_REGNUM, + BFIN_L2_REGNUM, + BFIN_L3_REGNUM, + BFIN_M0_REGNUM, + BFIN_M1_REGNUM, + BFIN_M2_REGNUM, + BFIN_M3_REGNUM, + BFIN_A0_DOT_X_REGNUM, + BFIN_A1_DOT_X_REGNUM, + BFIN_CC_REGNUM, + BFIN_RETS_REGNUM, + BFIN_RETI_REGNUM, + BFIN_RETX_REGNUM, + BFIN_RETN_REGNUM, + BFIN_RETE_REGNUM, + BFIN_ASTAT_REGNUM, + BFIN_SEQSTAT_REGNUM, + BFIN_USP_REGNUM, + BFIN_LT0_REGNUM, + BFIN_LT1_REGNUM, + BFIN_LC0_REGNUM, + BFIN_LC1_REGNUM, + BFIN_LB0_REGNUM, + BFIN_LB1_REGNUM +}; + + +struct bfin_frame_cache +{ + /* Base address. */ + CORE_ADDR base; + CORE_ADDR sp_offset; + CORE_ADDR pc; + int frameless_pc_value; + + /* Saved registers. */ + CORE_ADDR saved_regs[BFIN_NUM_REGS]; + CORE_ADDR saved_sp; + + /* Stack space reserved for local variables. */ + long locals; +}; + +/* Allocate and initialize a frame cache. */ + +static struct bfin_frame_cache * +bfin_alloc_frame_cache (void) +{ + struct bfin_frame_cache *cache; + int i; + + cache = FRAME_OBSTACK_ZALLOC (struct bfin_frame_cache); + + /* Base address. */ + cache->base = 0; + cache->sp_offset = -4; + cache->pc = 0; + cache->frameless_pc_value = 0; + + /* Saved registers. We initialize these to -1 since zero is a valid + offset (that's where fp is supposed to be stored). */ + for (i = 0; i < BFIN_NUM_REGS; i++) + cache->saved_regs[i] = -1; + + /* Frameless until proven otherwise. */ + cache->locals = -1; + + return cache; +} + +static struct bfin_frame_cache * +bfin_frame_cache (struct frame_info *this_frame, void **this_cache) +{ + struct bfin_frame_cache *cache; + int i; + + if (*this_cache) + return *this_cache; + + cache = bfin_alloc_frame_cache (); + *this_cache = cache; + + cache->base = get_frame_register_unsigned (this_frame, BFIN_FP_REGNUM); + if (cache->base == 0) + return cache; + + /* For normal frames, PC is stored at [FP + 4]. */ + cache->saved_regs[BFIN_PC_REGNUM] = 4; + cache->saved_regs[BFIN_FP_REGNUM] = 0; + + /* Adjust all the saved registers such that they contain addresses + instead of offsets. */ + for (i = 0; i < BFIN_NUM_REGS; i++) + if (cache->saved_regs[i] != -1) + cache->saved_regs[i] += cache->base; + + cache->pc = get_frame_func (this_frame) ; + if (cache->pc == 0 || cache->pc == get_frame_pc (this_frame)) + { + /* Either there is no prologue (frameless function) or we are at + the start of a function. In short we do not have a frame. + PC is stored in rets register. FP points to previous frame. */ + + cache->saved_regs[BFIN_PC_REGNUM] = + get_frame_register_unsigned (this_frame, BFIN_RETS_REGNUM); + cache->frameless_pc_value = 1; + cache->base = get_frame_register_unsigned (this_frame, BFIN_FP_REGNUM); + cache->saved_regs[BFIN_FP_REGNUM] = cache->base; + cache->saved_sp = cache->base; + } + else + { + cache->frameless_pc_value = 0; + + /* Now that we have the base address for the stack frame we can + calculate the value of SP in the calling frame. */ + cache->saved_sp = cache->base + 8; + } + + return cache; +} + +static void +bfin_frame_this_id (struct frame_info *this_frame, + void **this_cache, + struct frame_id *this_id) +{ + struct bfin_frame_cache *cache = bfin_frame_cache (this_frame, this_cache); + + /* This marks the outermost frame. */ + if (cache->base == 0) + return; + + /* See the end of bfin_push_dummy_call. */ + *this_id = frame_id_build (cache->base + 8, cache->pc); +} + +static struct value * +bfin_frame_prev_register (struct frame_info *this_frame, + void **this_cache, + int regnum) +{ + struct gdbarch *gdbarch = get_frame_arch (this_frame); + struct bfin_frame_cache *cache = bfin_frame_cache (this_frame, this_cache); + + if (regnum == gdbarch_sp_regnum (gdbarch) && cache->saved_sp) + return frame_unwind_got_constant (this_frame, regnum, cache->saved_sp); + + if (regnum < BFIN_NUM_REGS && cache->saved_regs[regnum] != -1) + return frame_unwind_got_memory (this_frame, regnum, + cache->saved_regs[regnum]); + + return frame_unwind_got_register (this_frame, regnum, regnum); +} + +static const struct frame_unwind bfin_frame_unwind = +{ + NORMAL_FRAME, + bfin_frame_this_id, + bfin_frame_prev_register, + NULL, + default_frame_sniffer +}; + +/* Check for "[--SP] = ;" insns. These are appear in function + prologues to save misc registers onto the stack. */ + +static int +is_minus_minus_sp (int op) +{ + op &= 0xFFC0; + + if ((op == P_MINUS_SP1) || (op == P_MINUS_SP2) + || (op == P_MINUS_SP3) || (op == P_MINUS_SP4)) + return 1; + + return 0; +} + +/* Skip all the insns that appear in generated function prologues. */ + +static CORE_ADDR +bfin_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) +{ + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + int op = read_memory_unsigned_integer (pc, 2, byte_order); + CORE_ADDR orig_pc = pc; + int done = 0; + + /* The new gcc prologue generates the register saves BEFORE the link + or RETS saving instruction. + So, our job is to stop either at those instructions or some upper + limit saying there is no frame! */ + + while (!done) + { + if (is_minus_minus_sp (op)) + { + while (is_minus_minus_sp (op)) + { + pc += 2; + op = read_memory_unsigned_integer (pc, 2, byte_order); + } + + if (op == P_LINKAGE) + pc += 4; + + done = 1; + } + else if (op == P_LINKAGE) + { + pc += 4; + done = 1; + } + else if (op == P_MINUS_MINUS_SP_EQ_RETS) + { + pc += 2; + done = 1; + } + else if (op == P_RTS) + { + done = 1; + } + else if ((op >= P_JUMP_PREG_MIN && op <= P_JUMP_PREG_MAX) + || (op >= P_JUMP_PC_PLUS_PREG_MIN + && op <= P_JUMP_PC_PLUS_PREG_MAX) + || (op == P_JUMP_S_MIN && op <= P_JUMP_S_MAX)) + { + done = 1; + } + else if (pc - orig_pc >= UPPER_LIMIT) + { + warning (_("Function Prologue not recognised; " + "pc will point to ENTRY_POINT of the function")); + pc = orig_pc + 2; + done = 1; + } + else + { + pc += 2; /* Not a terminating instruction go on. */ + op = read_memory_unsigned_integer (pc, 2, byte_order); + } + } + + /* TODO: + Dwarf2 uses entry point value AFTER some register initializations. + We should perhaps skip such asssignments as well (R6 = R1, ...). */ + + return pc; +} + +/* Return the GDB type object for the "standard" data type of data in + register N. This should be void pointer for P0-P5, SP, FP; + void pointer to function for PC; int otherwise. */ + +static struct type * +bfin_register_type (struct gdbarch *gdbarch, int regnum) +{ + if ((regnum >= BFIN_P0_REGNUM && regnum <= BFIN_FP_REGNUM) + || regnum == BFIN_USP_REGNUM) + return builtin_type (gdbarch)->builtin_data_ptr; + + if (regnum == BFIN_PC_REGNUM || regnum == BFIN_RETS_REGNUM || + regnum == BFIN_RETI_REGNUM || regnum == BFIN_RETX_REGNUM || + regnum == BFIN_RETN_REGNUM || regnum == BFIN_RETE_REGNUM || + regnum == BFIN_LT0_REGNUM || regnum == BFIN_LB0_REGNUM || + regnum == BFIN_LT1_REGNUM || regnum == BFIN_LB1_REGNUM) + return builtin_type (gdbarch)->builtin_func_ptr; + + return builtin_type (gdbarch)->builtin_int32; +} + +static CORE_ADDR +bfin_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); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + char buf[4]; + int i; + long reg_r0, reg_r1, reg_r2; + int total_len = 0; + enum bfin_abi abi = bfin_abi (gdbarch); + CORE_ADDR func_addr = find_function_addr (function, NULL); + + for (i = nargs - 1; i >= 0; i--) + { + struct type *value_type = value_enclosing_type (args[i]); + int len = TYPE_LENGTH (value_type); + + total_len += (len + 3) & ~3; + } + + /* At least twelve bytes of stack space must be allocated for the function's + arguments, even for functions that have less than 12 bytes of argument + data. */ + + if (total_len < 12) + sp -= 12 - total_len; + + /* Push arguments in reverse order. */ + + for (i = nargs - 1; i >= 0; i--) + { + struct type *value_type = value_enclosing_type (args[i]); + struct type *arg_type = check_typedef (value_type); + int len = TYPE_LENGTH (value_type); + int container_len = (len + 3) & ~3; + + sp -= container_len; + write_memory (sp, value_contents_writeable (args[i]), container_len); + } + + /* Initialize R0, R1, and R2 to the first 3 words of parameters. */ + + reg_r0 = read_memory_integer (sp, 4, byte_order); + regcache_cooked_write_unsigned (regcache, BFIN_R0_REGNUM, reg_r0); + reg_r1 = read_memory_integer (sp + 4, 4, byte_order); + regcache_cooked_write_unsigned (regcache, BFIN_R1_REGNUM, reg_r1); + reg_r2 = read_memory_integer (sp + 8, 4, byte_order); + regcache_cooked_write_unsigned (regcache, BFIN_R2_REGNUM, reg_r2); + + /* Store struct value address. */ + + if (struct_return) + regcache_cooked_write_unsigned (regcache, BFIN_P0_REGNUM, struct_addr); + + /* Set the dummy return value to bp_addr. + A dummy breakpoint will be setup to execute the call. */ + + regcache_cooked_write_unsigned (regcache, BFIN_RETS_REGNUM, bp_addr); + + /* Finally, update the stack pointer. */ + + regcache_cooked_write_unsigned (regcache, BFIN_SP_REGNUM, sp); + + return sp; +} + +/* Convert DWARF2 register number REG to the appropriate register number + used by GDB. */ + +static int +bfin_reg_to_regnum (struct gdbarch *gdbarch, int reg) +{ + if (reg > ARRAY_SIZE (map_gcc_gdb)) + return 0; + + return map_gcc_gdb[reg]; +} + +/* This function implements the BREAKPOINT_FROM_PC macro. It returns + a pointer to a string of bytes that encode a breakpoint instruction, + stores the length of the string to *lenptr, and adjusts the program + counter (if necessary) to point to the actual memory location where + the breakpoint should be inserted. */ + +static const unsigned char * +bfin_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, int *lenptr) +{ + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + unsigned short iw; + static unsigned char bfin_breakpoint[] = {0xa1, 0x00, 0x00, 0x00}; + static unsigned char bfin_sim_breakpoint[] = {0x25, 0x00, 0x00, 0x00}; + + iw = read_memory_unsigned_integer (*pcptr, 2, byte_order); + + if ((iw & 0xf000) >= 0xc000) + /* 32-bit instruction. */ + *lenptr = 4; + else + *lenptr = 2; + + if (strcmp (target_shortname, "sim") == 0) + return bfin_sim_breakpoint; + else + return bfin_breakpoint; +} + +static void +bfin_extract_return_value (struct type *type, + struct regcache *regs, + gdb_byte *dst) +{ + struct gdbarch *gdbarch = get_regcache_arch (regs); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + bfd_byte *valbuf = dst; + int len = TYPE_LENGTH (type); + ULONGEST tmp; + int regno = BFIN_R0_REGNUM; + + gdb_assert (len <= 8); + + while (len > 0) + { + regcache_cooked_read_unsigned (regs, regno++, &tmp); + store_unsigned_integer (valbuf, (len > 4 ? 4 : len), tmp, byte_order); + len -= 4; + valbuf += 4; + } +} + +/* Write into appropriate registers a function return value of type + TYPE, given in virtual format. */ + +static void +bfin_store_return_value (struct type *type, + struct regcache *regs, + const gdb_byte *src) +{ + const bfd_byte *valbuf = src; + + /* Integral values greater than one word are stored in consecutive + registers starting with R0. This will always be a multiple of + the register size. */ + + int len = TYPE_LENGTH (type); + int regno = BFIN_R0_REGNUM; + + gdb_assert (len <= 8); + + while (len > 0) + { + regcache_cooked_write (regs, regno++, valbuf); + len -= 4; + valbuf += 4; + } +} + +/* Determine, for architecture GDBARCH, how a return value of TYPE + should be returned. If it is supposed to be returned in registers, + and READBUF is nonzero, read the appropriate value from REGCACHE, + and copy it into READBUF. If WRITEBUF is nonzero, write the value + from WRITEBUF into REGCACHE. */ + +static enum return_value_convention +bfin_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) + bfin_extract_return_value (type, regcache, readbuf); + + if (writebuf) + bfin_store_return_value (type, regcache, writebuf); + + return RETURN_VALUE_REGISTER_CONVENTION; +} + +/* Return the BFIN register name corresponding to register I. */ + +static const char * +bfin_register_name (struct gdbarch *gdbarch, int i) +{ + return bfin_register_name_strings[i]; +} + +static void +bfin_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache, + int regnum, gdb_byte *buffer) +{ + gdb_byte *buf = (gdb_byte *) alloca (MAX_REGISTER_SIZE); + + if (regnum != BFIN_CC_REGNUM) + internal_error (__FILE__, __LINE__, + _("invalid register number %d"), regnum); + + /* Extract the CC bit from the ASTAT register. */ + regcache_raw_read (regcache, BFIN_ASTAT_REGNUM, buf); + buffer[1] = buffer[2] = buffer[3] = 0; + buffer[0] = !!(buf[0] & ASTAT_CC); +} + +static void +bfin_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache, + int regnum, const gdb_byte *buffer) +{ + gdb_byte *buf = (gdb_byte *) alloca (MAX_REGISTER_SIZE); + + if (regnum != BFIN_CC_REGNUM) + internal_error (__FILE__, __LINE__, + _("invalid register number %d"), regnum); + + /* Overlay the CC bit in the ASTAT register. */ + regcache_raw_read (regcache, BFIN_ASTAT_REGNUM, buf); + buf[0] = (buf[0] & ~ASTAT_CC) | ((buffer[0] & 1) << ASTAT_CC_POS); + regcache_raw_write (regcache, BFIN_ASTAT_REGNUM, buf); +} + +static CORE_ADDR +bfin_frame_base_address (struct frame_info *this_frame, void **this_cache) +{ + struct bfin_frame_cache *cache = bfin_frame_cache (this_frame, this_cache); + + return cache->base; +} + +static CORE_ADDR +bfin_frame_local_address (struct frame_info *this_frame, void **this_cache) +{ + struct bfin_frame_cache *cache = bfin_frame_cache (this_frame, this_cache); + + return cache->base - 4; +} + +static CORE_ADDR +bfin_frame_args_address (struct frame_info *this_frame, void **this_cache) +{ + struct bfin_frame_cache *cache = bfin_frame_cache (this_frame, this_cache); + + return cache->base + 8; +} + +static const struct frame_base bfin_frame_base = +{ + &bfin_frame_unwind, + bfin_frame_base_address, + bfin_frame_local_address, + bfin_frame_args_address +}; + +static struct frame_id +bfin_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame) +{ + CORE_ADDR sp; + + sp = get_frame_register_unsigned (this_frame, BFIN_SP_REGNUM); + + return frame_id_build (sp, get_frame_pc (this_frame)); +} + +static CORE_ADDR +bfin_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame) +{ + return frame_unwind_register_unsigned (next_frame, BFIN_PC_REGNUM); +} + +static CORE_ADDR +bfin_frame_align (struct gdbarch *gdbarch, CORE_ADDR address) +{ + return (address & ~0x3); +} + +enum bfin_abi +bfin_abi (struct gdbarch *gdbarch) +{ + return gdbarch_tdep (gdbarch)->bfin_abi; +} + +/* Initialize the current architecture based on INFO. If possible, + re-use an architecture from ARCHES, which is a list of + architectures already created during this debugging session. + + Called e.g. at program startup, when reading a core file, and when + reading a binary file. */ + +static struct gdbarch * +bfin_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) +{ + struct gdbarch_tdep *tdep; + struct gdbarch *gdbarch; + int elf_flags; + enum bfin_abi abi; + + /* Extract the ELF flags, if available. */ + if (info.abfd && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour) + elf_flags = elf_elfheader (info.abfd)->e_flags; + else + elf_flags = 0; + + abi = BFIN_ABI_FLAT; + + /* If there is already a candidate, use it. */ + + 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)->bfin_abi != abi) + continue; + return arches->gdbarch; + } + + tdep = XMALLOC (struct gdbarch_tdep); + gdbarch = gdbarch_alloc (&info, tdep); + + tdep->bfin_abi = abi; + + set_gdbarch_num_regs (gdbarch, BFIN_NUM_REGS); + set_gdbarch_pseudo_register_read (gdbarch, bfin_pseudo_register_read); + set_gdbarch_pseudo_register_write (gdbarch, bfin_pseudo_register_write); + set_gdbarch_num_pseudo_regs (gdbarch, BFIN_NUM_PSEUDO_REGS); + set_gdbarch_sp_regnum (gdbarch, BFIN_SP_REGNUM); + set_gdbarch_pc_regnum (gdbarch, BFIN_PC_REGNUM); + set_gdbarch_ps_regnum (gdbarch, BFIN_ASTAT_REGNUM); + set_gdbarch_dwarf2_reg_to_regnum (gdbarch, bfin_reg_to_regnum); + set_gdbarch_register_name (gdbarch, bfin_register_name); + set_gdbarch_register_type (gdbarch, bfin_register_type); + set_gdbarch_dummy_id (gdbarch, bfin_dummy_id); + set_gdbarch_push_dummy_call (gdbarch, bfin_push_dummy_call); + set_gdbarch_believe_pcc_promotion (gdbarch, 1); + set_gdbarch_return_value (gdbarch, bfin_return_value); + set_gdbarch_skip_prologue (gdbarch, bfin_skip_prologue); + set_gdbarch_inner_than (gdbarch, core_addr_lessthan); + set_gdbarch_breakpoint_from_pc (gdbarch, bfin_breakpoint_from_pc); + set_gdbarch_decr_pc_after_break (gdbarch, 2); + set_gdbarch_frame_args_skip (gdbarch, 8); + set_gdbarch_unwind_pc (gdbarch, bfin_unwind_pc); + set_gdbarch_frame_align (gdbarch, bfin_frame_align); + set_gdbarch_print_insn (gdbarch, print_insn_bfin); + + /* Hook in ABI-specific overrides, if they have been registered. */ + gdbarch_init_osabi (info, gdbarch); + + dwarf2_append_unwinders (gdbarch); + + frame_base_set_default (gdbarch, &bfin_frame_base); + + frame_unwind_append_unwinder (gdbarch, &bfin_frame_unwind); + + return gdbarch; +} + +/* Provide a prototype to silence -Wmissing-prototypes. */ +extern initialize_file_ftype _initialize_bfin_tdep; + +void +_initialize_bfin_tdep (void) +{ + register_gdbarch_init (bfd_arch_bfin, bfin_gdbarch_init); +} diff --git a/gdb/bfin-tdep.h b/gdb/bfin-tdep.h new file mode 100644 index 0000000..ecc46d2 --- /dev/null +++ b/gdb/bfin-tdep.h @@ -0,0 +1,102 @@ +/* Target-dependent code for Analog Devices Blackfin processor, for GDB. + + Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 + Free Software Foundation, Inc. + + Contributed by Analog Devices, Inc. + + 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 gdb_regnum { + /* Core Registers */ + BFIN_R0_REGNUM = 0, + BFIN_R1_REGNUM, + BFIN_R2_REGNUM, + BFIN_R3_REGNUM, + BFIN_R4_REGNUM, + BFIN_R5_REGNUM, + BFIN_R6_REGNUM, + BFIN_R7_REGNUM, + BFIN_P0_REGNUM, + BFIN_P1_REGNUM, + BFIN_P2_REGNUM, + BFIN_P3_REGNUM, + BFIN_P4_REGNUM, + BFIN_P5_REGNUM, + BFIN_SP_REGNUM, + BFIN_FP_REGNUM, + BFIN_I0_REGNUM, + BFIN_I1_REGNUM, + BFIN_I2_REGNUM, + BFIN_I3_REGNUM, + BFIN_M0_REGNUM, + BFIN_M1_REGNUM, + BFIN_M2_REGNUM, + BFIN_M3_REGNUM, + BFIN_B0_REGNUM, + BFIN_B1_REGNUM, + BFIN_B2_REGNUM, + BFIN_B3_REGNUM, + BFIN_L0_REGNUM, + BFIN_L1_REGNUM, + BFIN_L2_REGNUM, + BFIN_L3_REGNUM, + BFIN_A0_DOT_X_REGNUM, + BFIN_AO_DOT_W_REGNUM, + BFIN_A1_DOT_X_REGNUM, + BFIN_A1_DOT_W_REGNUM, + BFIN_ASTAT_REGNUM, + BFIN_RETS_REGNUM, + BFIN_LC0_REGNUM, + BFIN_LT0_REGNUM, + BFIN_LB0_REGNUM, + BFIN_LC1_REGNUM, + BFIN_LT1_REGNUM, + BFIN_LB1_REGNUM, + BFIN_CYCLES_REGNUM, + BFIN_CYCLES2_REGNUM, + BFIN_USP_REGNUM, + BFIN_SEQSTAT_REGNUM, + BFIN_SYSCFG_REGNUM, + BFIN_RETI_REGNUM, + BFIN_RETX_REGNUM, + BFIN_RETN_REGNUM, + BFIN_RETE_REGNUM, + + /* Pseudo Registers managed remotely. */ + BFIN_PC_REGNUM, + + /* Pseudo Registers managed locally. */ + BFIN_CC_REGNUM +}; +#define BFIN_NUM_REGS (BFIN_PC_REGNUM + 1) +#define BFIN_NUM_PSEUDO_REGS (1) + +/* The ABIs for Blackfin. */ +enum bfin_abi +{ + BFIN_ABI_FLAT +}; + +/* Target-dependent structure in gdbarch. */ +struct gdbarch_tdep +{ + /* Which ABI is in use? */ + enum bfin_abi bfin_abi; +}; + +/* Return the Blackfin ABI associated with GDBARCH. */ +extern enum bfin_abi bfin_abi (struct gdbarch *gdbarch); diff --git a/gdb/configure.tgt b/gdb/configure.tgt index 161cbdc..ef9b783 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -106,6 +106,15 @@ avr-*-*) gdb_sim=../sim/avr/libsim.a ;; +bfin-*-*linux*) + # Target: Blackfin Linux + gdb_target_obs="bfin-tdep.o bfin-linux-tdep.o linux-tdep.o" + ;; +bfin-*-*) + # Target: Blackfin processor + gdb_target_obs="bfin-tdep.o" + ;; + cris*) # Target: CRIS gdb_target_obs="cris-tdep.o corelow.o solib.o solib-svr4.o" diff --git a/gdb/syscalls/bfin-linux.xml b/gdb/syscalls/bfin-linux.xml new file mode 100644 index 0000000..0fd3a96 --- /dev/null +++ b/gdb/syscalls/bfin-linux.xml @@ -0,0 +1,326 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.1