diff options
author | Mike Frysinger <vapier@gentoo.org> | 2010-12-29 16:11:49 +0000 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2010-12-29 16:11:49 +0000 |
commit | 9102122396b6b977eb2e283a6c092d1ab510e788 (patch) | |
tree | 8363a4c175ef9433c81f1e61afd4f0635deace85 /gdb/bfin-tdep.c | |
parent | abc1f4cd130f48697c535e0d64580721d181d3d1 (diff) | |
download | gdb-9102122396b6b977eb2e283a6c092d1ab510e788.zip gdb-9102122396b6b977eb2e283a6c092d1ab510e788.tar.gz gdb-9102122396b6b977eb2e283a6c092d1ab510e788.tar.bz2 |
gdb: bfin: new port
Initial support for Blackfin processors. This supports the standard ABI.
Signed-off-by: Jie Zhang <jie.zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Diffstat (limited to 'gdb/bfin-tdep.c')
-rw-r--r-- | gdb/bfin-tdep.c | 862 |
1 files changed, 862 insertions, 0 deletions
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 <http://www.gnu.org/licenses/>. */ + +#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] = <reg>;" 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); +} |