mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-24 10:35:12 +08:00
08106042d9
I built GDB for all targets on a x86-64/GNU-Linux system, and then (accidentally) passed GDB a RISC-V binary, and asked GDB to "run" the binary on the native target. I got this error: (gdb) show architecture The target architecture is set to "auto" (currently "i386"). (gdb) file /tmp/hello.rv32.exe Reading symbols from /tmp/hello.rv32.exe... (gdb) show architecture The target architecture is set to "auto" (currently "riscv:rv32"). (gdb) run Starting program: /tmp/hello.rv32.exe ../../src/gdb/i387-tdep.c:596: internal-error: i387_supply_fxsave: Assertion `tdep->st0_regnum >= I386_ST0_REGNUM' failed. What's going on here is this; initially the architecture is i386, this is based on the default architecture, which is set based on the native target. After loading the RISC-V executable the architecture of the current inferior is updated based on the architecture of the executable. When we "run", GDB does a fork & exec, with the inferior being controlled through ptrace. GDB sees an initial stop from the inferior as soon as the inferior comes to life. In response to this stop GDB ends up calling save_stop_reason (linux-nat.c), which ends up trying to read register from the inferior, to do this we end up calling target_ops::fetch_registers, which, for the x86-64 native target, calls amd64_linux_nat_target::fetch_registers. After this I eventually end up in i387_supply_fxsave, different x86 based targets will end in different functions to fetch registers, but it doesn't really matter which function we end up in, the problem is this line, which is repeated in many places: i386_gdbarch_tdep *tdep = (i386_gdbarch_tdep *) gdbarch_tdep (arch); The problem here is that the ARCH in this line comes from the current inferior, which, as we discussed above, will be a RISC-V gdbarch, the tdep field will actually be of type riscv_gdbarch_tdep, not i386_gdbarch_tdep. After this cast we are relying on undefined behaviour, in my case I happen to trigger an assert, but this might not always be the case. The thing I tried that exposed this problem was of course, trying to start an executable of the wrong architecture on a native target. I don't think that the correct solution for this problem is to detect, at the point of cast, that the gdbarch_tdep object is of the wrong type, but, I did wonder, is there a way that we could protect ourselves from incorrectly casting the gdbarch_tdep object? I think that there is something we can do here, and this commit is the first step in that direction, though no actual check is added by this commit. This commit can be split into two parts: (1) In gdbarch.h and arch-utils.c. In these files I have modified gdbarch_tdep (the function) so that it now takes a template argument, like this: template<typename TDepType> static inline TDepType * gdbarch_tdep (struct gdbarch *gdbarch) { struct gdbarch_tdep *tdep = gdbarch_tdep_1 (gdbarch); return static_cast<TDepType *> (tdep); } After this change we are no better protected, but the cast is now done within the gdbarch_tdep function rather than at the call sites, this leads to the second, much larger change in this commit, (2) Everywhere gdbarch_tdep is called, we make changes like this: - i386_gdbarch_tdep *tdep = (i386_gdbarch_tdep *) gdbarch_tdep (arch); + i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (arch); There should be no functional change after this commit. In the next commit I will build on this change to add an assertion in gdbarch_tdep that checks we are casting to the correct type.
3057 lines
84 KiB
C
3057 lines
84 KiB
C
/* Target-dependent code for the CSKY architecture, for GDB.
|
|
|
|
Copyright (C) 2010-2022 Free Software Foundation, Inc.
|
|
|
|
Contributed by C-SKY Microsystems and Mentor Graphics.
|
|
|
|
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 "gdbsupport/gdb_assert.h"
|
|
#include "frame.h"
|
|
#include "inferior.h"
|
|
#include "symtab.h"
|
|
#include "value.h"
|
|
#include "gdbcmd.h"
|
|
#include "language.h"
|
|
#include "gdbcore.h"
|
|
#include "symfile.h"
|
|
#include "objfiles.h"
|
|
#include "gdbtypes.h"
|
|
#include "target.h"
|
|
#include "arch-utils.h"
|
|
#include "regcache.h"
|
|
#include "osabi.h"
|
|
#include "block.h"
|
|
#include "reggroups.h"
|
|
#include "elf/csky.h"
|
|
#include "elf-bfd.h"
|
|
#include "symcat.h"
|
|
#include "sim-regno.h"
|
|
#include "dis-asm.h"
|
|
#include "frame-unwind.h"
|
|
#include "frame-base.h"
|
|
#include "trad-frame.h"
|
|
#include "infcall.h"
|
|
#include "floatformat.h"
|
|
#include "remote.h"
|
|
#include "target-descriptions.h"
|
|
#include "dwarf2/frame.h"
|
|
#include "user-regs.h"
|
|
#include "valprint.h"
|
|
#include "csky-tdep.h"
|
|
#include "regset.h"
|
|
#include "opcode/csky.h"
|
|
#include <algorithm>
|
|
#include <vector>
|
|
|
|
/* Control debugging information emitted in this file. */
|
|
|
|
static bool csky_debug = false;
|
|
|
|
static const reggroup *cr_reggroup;
|
|
static const reggroup *fr_reggroup;
|
|
static const reggroup *vr_reggroup;
|
|
static const reggroup *mmu_reggroup;
|
|
static const reggroup *prof_reggroup;
|
|
|
|
static const char *csky_supported_tdesc_feature_names[] = {
|
|
(const char *)"org.gnu.csky.abiv2.gpr",
|
|
(const char *)"org.gnu.csky.abiv2.fpu",
|
|
(const char *)"org.gnu.csky.abiv2.cr",
|
|
(const char *)"org.gnu.csky.abiv2.fvcr",
|
|
(const char *)"org.gnu.csky.abiv2.mmu",
|
|
(const char *)"org.gnu.csky.abiv2.tee",
|
|
(const char *)"org.gnu.csky.abiv2.fpu2",
|
|
(const char *)"org.gnu.csky.abiv2.bank0",
|
|
(const char *)"org.gnu.csky.abiv2.bank1",
|
|
(const char *)"org.gnu.csky.abiv2.bank2",
|
|
(const char *)"org.gnu.csky.abiv2.bank3",
|
|
(const char *)"org.gnu.csky.abiv2.bank4",
|
|
(const char *)"org.gnu.csky.abiv2.bank5",
|
|
(const char *)"org.gnu.csky.abiv2.bank6",
|
|
(const char *)"org.gnu.csky.abiv2.bank7",
|
|
(const char *)"org.gnu.csky.abiv2.bank8",
|
|
(const char *)"org.gnu.csky.abiv2.bank9",
|
|
(const char *)"org.gnu.csky.abiv2.bank10",
|
|
(const char *)"org.gnu.csky.abiv2.bank11",
|
|
(const char *)"org.gnu.csky.abiv2.bank12",
|
|
(const char *)"org.gnu.csky.abiv2.bank13",
|
|
(const char *)"org.gnu.csky.abiv2.bank14",
|
|
(const char *)"org.gnu.csky.abiv2.bank15",
|
|
(const char *)"org.gnu.csky.abiv2.bank16",
|
|
(const char *)"org.gnu.csky.abiv2.bank17",
|
|
(const char *)"org.gnu.csky.abiv2.bank18",
|
|
(const char *)"org.gnu.csky.abiv2.bank19",
|
|
(const char *)"org.gnu.csky.abiv2.bank20",
|
|
(const char *)"org.gnu.csky.abiv2.bank21",
|
|
(const char *)"org.gnu.csky.abiv2.bank22",
|
|
(const char *)"org.gnu.csky.abiv2.bank23",
|
|
(const char *)"org.gnu.csky.abiv2.bank24",
|
|
(const char *)"org.gnu.csky.abiv2.bank25",
|
|
(const char *)"org.gnu.csky.abiv2.bank26",
|
|
(const char *)"org.gnu.csky.abiv2.bank27",
|
|
(const char *)"org.gnu.csky.abiv2.bank28",
|
|
(const char *)"org.gnu.csky.abiv2.bank29",
|
|
(const char *)"org.gnu.csky.abiv2.bank30",
|
|
(const char *)"org.gnu.csky.abiv2.bank31"
|
|
};
|
|
|
|
struct csky_supported_tdesc_register
|
|
{
|
|
char name[16];
|
|
int num;
|
|
};
|
|
|
|
static const struct csky_supported_tdesc_register csky_supported_gpr_regs[] = {
|
|
{"r0", 0},
|
|
{"r1", 1},
|
|
{"r2", 2},
|
|
{"r3", 3},
|
|
{"r4", 4},
|
|
{"r5", 5},
|
|
{"r6", 6},
|
|
{"r7", 7},
|
|
{"r8", 8},
|
|
{"r9", 9},
|
|
{"r10", 10},
|
|
{"r11", 11},
|
|
{"r12", 12},
|
|
{"r13", 13},
|
|
{"r14", 14},
|
|
{"r15", 15},
|
|
{"r16", 16},
|
|
{"r17", 17},
|
|
{"r18", 18},
|
|
{"r19", 19},
|
|
{"r20", 20},
|
|
{"r21", 21},
|
|
{"r22", 22},
|
|
{"r23", 23},
|
|
{"r24", 24},
|
|
{"r25", 25},
|
|
{"r26", 26},
|
|
{"r27", 27},
|
|
{"r28", 28},
|
|
{"r28", 28},
|
|
{"r29", 29},
|
|
{"r30", 30},
|
|
{"r31", 31},
|
|
{"hi", CSKY_HI_REGNUM},
|
|
{"lo", CSKY_LO_REGNUM},
|
|
{"pc", CSKY_PC_REGNUM}
|
|
};
|
|
|
|
static const struct csky_supported_tdesc_register csky_supported_fpu_regs[] = {
|
|
/* fr0~fr15. */
|
|
{"fr0", CSKY_FR0_REGNUM + 0},
|
|
{"fr1", CSKY_FR0_REGNUM + 1},
|
|
{"fr2", CSKY_FR0_REGNUM + 2},
|
|
{"fr3", CSKY_FR0_REGNUM + 3},
|
|
{"fr4", CSKY_FR0_REGNUM + 4},
|
|
{"fr5", CSKY_FR0_REGNUM + 5},
|
|
{"fr6", CSKY_FR0_REGNUM + 6},
|
|
{"fr7", CSKY_FR0_REGNUM + 7},
|
|
{"fr8", CSKY_FR0_REGNUM + 8},
|
|
{"fr9", CSKY_FR0_REGNUM + 9},
|
|
{"fr10", CSKY_FR0_REGNUM + 10},
|
|
{"fr11", CSKY_FR0_REGNUM + 11},
|
|
{"fr12", CSKY_FR0_REGNUM + 12},
|
|
{"fr13", CSKY_FR0_REGNUM + 13},
|
|
{"fr14", CSKY_FR0_REGNUM + 14},
|
|
{"fr15", CSKY_FR0_REGNUM + 15},
|
|
/* fr16~fr31. */
|
|
{"fr16", CSKY_FR16_REGNUM + 0},
|
|
{"fr17", CSKY_FR16_REGNUM + 1},
|
|
{"fr18", CSKY_FR16_REGNUM + 2},
|
|
{"fr19", CSKY_FR16_REGNUM + 3},
|
|
{"fr20", CSKY_FR16_REGNUM + 4},
|
|
{"fr21", CSKY_FR16_REGNUM + 5},
|
|
{"fr22", CSKY_FR16_REGNUM + 6},
|
|
{"fr23", CSKY_FR16_REGNUM + 7},
|
|
{"fr24", CSKY_FR16_REGNUM + 8},
|
|
{"fr25", CSKY_FR16_REGNUM + 9},
|
|
{"fr26", CSKY_FR16_REGNUM + 10},
|
|
{"fr27", CSKY_FR16_REGNUM + 11},
|
|
{"fr28", CSKY_FR16_REGNUM + 12},
|
|
{"fr29", CSKY_FR16_REGNUM + 13},
|
|
{"fr30", CSKY_FR16_REGNUM + 14},
|
|
{"fr31", CSKY_FR16_REGNUM + 15},
|
|
/* vr0~vr15. */
|
|
{"vr0", CSKY_VR0_REGNUM + 0},
|
|
{"vr1", CSKY_VR0_REGNUM + 1},
|
|
{"vr2", CSKY_VR0_REGNUM + 2},
|
|
{"vr3", CSKY_VR0_REGNUM + 3},
|
|
{"vr4", CSKY_VR0_REGNUM + 4},
|
|
{"vr5", CSKY_VR0_REGNUM + 5},
|
|
{"vr6", CSKY_VR0_REGNUM + 6},
|
|
{"vr7", CSKY_VR0_REGNUM + 7},
|
|
{"vr8", CSKY_VR0_REGNUM + 8},
|
|
{"vr9", CSKY_VR0_REGNUM + 9},
|
|
{"vr10", CSKY_VR0_REGNUM + 10},
|
|
{"vr11", CSKY_VR0_REGNUM + 11},
|
|
{"vr12", CSKY_VR0_REGNUM + 12},
|
|
{"vr13", CSKY_VR0_REGNUM + 13},
|
|
{"vr14", CSKY_VR0_REGNUM + 14},
|
|
{"vr15", CSKY_VR0_REGNUM + 15},
|
|
/* fpu control registers. */
|
|
{"fcr", CSKY_FCR_REGNUM + 0},
|
|
{"fid", CSKY_FCR_REGNUM + 1},
|
|
{"fesr", CSKY_FCR_REGNUM + 2},
|
|
};
|
|
|
|
static const struct csky_supported_tdesc_register csky_supported_ar_regs[] = {
|
|
{"ar0", CSKY_AR0_REGNUM + 0},
|
|
{"ar1", CSKY_AR0_REGNUM + 1},
|
|
{"ar2", CSKY_AR0_REGNUM + 2},
|
|
{"ar3", CSKY_AR0_REGNUM + 3},
|
|
{"ar4", CSKY_AR0_REGNUM + 4},
|
|
{"ar5", CSKY_AR0_REGNUM + 5},
|
|
{"ar6", CSKY_AR0_REGNUM + 6},
|
|
{"ar7", CSKY_AR0_REGNUM + 7},
|
|
{"ar8", CSKY_AR0_REGNUM + 8},
|
|
{"ar9", CSKY_AR0_REGNUM + 9},
|
|
{"ar10", CSKY_AR0_REGNUM + 10},
|
|
{"ar11", CSKY_AR0_REGNUM + 11},
|
|
{"ar12", CSKY_AR0_REGNUM + 12},
|
|
{"ar13", CSKY_AR0_REGNUM + 13},
|
|
{"ar14", CSKY_AR0_REGNUM + 14},
|
|
{"ar15", CSKY_AR0_REGNUM + 15},
|
|
};
|
|
|
|
static const struct csky_supported_tdesc_register csky_supported_bank0_regs[] = {
|
|
{"cr0", CSKY_CR0_REGNUM + 0},
|
|
{"cr1", CSKY_CR0_REGNUM + 1},
|
|
{"cr2", CSKY_CR0_REGNUM + 2},
|
|
{"cr3", CSKY_CR0_REGNUM + 3},
|
|
{"cr4", CSKY_CR0_REGNUM + 4},
|
|
{"cr5", CSKY_CR0_REGNUM + 5},
|
|
{"cr6", CSKY_CR0_REGNUM + 6},
|
|
{"cr7", CSKY_CR0_REGNUM + 7},
|
|
{"cr8", CSKY_CR0_REGNUM + 8},
|
|
{"cr9", CSKY_CR0_REGNUM + 9},
|
|
{"cr10", CSKY_CR0_REGNUM + 10},
|
|
{"cr11", CSKY_CR0_REGNUM + 11},
|
|
{"cr12", CSKY_CR0_REGNUM + 12},
|
|
{"cr13", CSKY_CR0_REGNUM + 13},
|
|
{"cr14", CSKY_CR0_REGNUM + 14},
|
|
{"cr15", CSKY_CR0_REGNUM + 15},
|
|
{"cr16", CSKY_CR0_REGNUM + 16},
|
|
{"cr17", CSKY_CR0_REGNUM + 17},
|
|
{"cr18", CSKY_CR0_REGNUM + 18},
|
|
{"cr19", CSKY_CR0_REGNUM + 19},
|
|
{"cr20", CSKY_CR0_REGNUM + 20},
|
|
{"cr21", CSKY_CR0_REGNUM + 21},
|
|
{"cr22", CSKY_CR0_REGNUM + 22},
|
|
{"cr23", CSKY_CR0_REGNUM + 23},
|
|
{"cr24", CSKY_CR0_REGNUM + 24},
|
|
{"cr25", CSKY_CR0_REGNUM + 25},
|
|
{"cr26", CSKY_CR0_REGNUM + 26},
|
|
{"cr27", CSKY_CR0_REGNUM + 27},
|
|
{"cr28", CSKY_CR0_REGNUM + 28},
|
|
{"cr29", CSKY_CR0_REGNUM + 29},
|
|
{"cr30", CSKY_CR0_REGNUM + 30},
|
|
{"cr31", CSKY_CR0_REGNUM + 31}
|
|
};
|
|
|
|
static const struct csky_supported_tdesc_register csky_supported_mmu_regs[] = {
|
|
{"mcr0", 128},
|
|
{"mcr2", 129},
|
|
{"mcr3", 130},
|
|
{"mcr4", 131},
|
|
{"mcr6", 132},
|
|
{"mcr8", 133},
|
|
{"mcr29", 134},
|
|
{"mcr30", 135},
|
|
{"mcr31", 136}
|
|
};
|
|
|
|
static const struct csky_supported_tdesc_register csky_supported_bank15_regs[] = {
|
|
{"cp15cp1", 253},
|
|
{"cp15cp5", 254},
|
|
{"cp15cp7", 255},
|
|
{"cp15cp9", 256},
|
|
{"cp15cp10", 257},
|
|
{"cp15cp11", 258},
|
|
{"cp15cp12", 259},
|
|
{"cp15cp13", 260},
|
|
{"cp15cp14", 261},
|
|
{"cp15cp15", 262},
|
|
{"cp15cp16", 263},
|
|
{"cp15cp17", 264},
|
|
{"cp15cp18", 265},
|
|
{"cp15cp19", 266},
|
|
{"cp15cp20", 267},
|
|
{"cp15cp21", 268},
|
|
{"cp15cp22", 269},
|
|
{"cp15cp23", 270},
|
|
{"cp15cp24", 271},
|
|
{"cp15cp25", 272},
|
|
{"cp15cp26", 273},
|
|
{"cp15cp27", 274},
|
|
{"cp15cp28", 275},
|
|
};
|
|
|
|
static const struct csky_supported_tdesc_register csky_supported_alias_regs[] = {
|
|
/* Alias register names for Bank0. */
|
|
{"psr", CSKY_CR0_REGNUM + 0},
|
|
{"vbr", CSKY_CR0_REGNUM + 1},
|
|
{"epsr", CSKY_CR0_REGNUM + 2},
|
|
{"fpsr", CSKY_CR0_REGNUM + 3},
|
|
{"epc", CSKY_CR0_REGNUM + 4},
|
|
{"fpc", CSKY_CR0_REGNUM + 5},
|
|
{"ss0", CSKY_CR0_REGNUM + 6},
|
|
{"ss1", CSKY_CR0_REGNUM + 7},
|
|
{"ss2", CSKY_CR0_REGNUM + 8},
|
|
{"ss3", CSKY_CR0_REGNUM + 9},
|
|
{"ss4", CSKY_CR0_REGNUM + 10},
|
|
{"gcr", CSKY_CR0_REGNUM + 11},
|
|
{"gsr", CSKY_CR0_REGNUM + 12},
|
|
{"cpuid", CSKY_CR0_REGNUM + 13},
|
|
{"ccr", CSKY_CR0_REGNUM + 18},
|
|
{"capr", CSKY_CR0_REGNUM + 19},
|
|
{"pacr", CSKY_CR0_REGNUM + 20},
|
|
{"prsr", CSKY_CR0_REGNUM + 21},
|
|
{"chr", CSKY_CR0_REGNUM + 31},
|
|
/* Alias register names for MMU. */
|
|
{"mir", 128},
|
|
{"mel0", 129},
|
|
{"mel1", 130},
|
|
{"meh", 131},
|
|
{"mpr", 132},
|
|
{"mcir", 133},
|
|
{"mpgd", 134},
|
|
{"msa0", 135},
|
|
{"msa1", 136},
|
|
/* Alias register names for Bank1. */
|
|
{"ebr", 190},
|
|
{"errlc", 195},
|
|
{"erraddr", 196},
|
|
{"errsts", 197},
|
|
{"errinj", 198},
|
|
{"usp", 203},
|
|
{"int_sp", 204},
|
|
{"itcmcr", 211},
|
|
{"dtcmcr", 212},
|
|
{"cindex", 215},
|
|
{"cdata0", 216},
|
|
{"cdata1", 217},
|
|
{"cdata2", 218},
|
|
{"cins", 220},
|
|
/* Alias register names for Bank3. */
|
|
{"sepsr", 221},
|
|
{"t_wssr", 221},
|
|
{"sevbr", 222},
|
|
{"t_wrcr", 222},
|
|
{"seepsr", 223},
|
|
{"seepc", 225},
|
|
{"nsssp", 227},
|
|
{"t_usp", 228},
|
|
{"dcr", 229},
|
|
{"t_pcr", 230},
|
|
};
|
|
|
|
/* Functions declaration. */
|
|
|
|
static const char *
|
|
csky_pseudo_register_name (struct gdbarch *gdbarch, int regno);
|
|
|
|
/* Get csky supported registers's count for tdesc xml. */
|
|
|
|
static int
|
|
csky_get_supported_tdesc_registers_count()
|
|
{
|
|
int count = 0;
|
|
count += ARRAY_SIZE (csky_supported_gpr_regs);
|
|
count += ARRAY_SIZE (csky_supported_fpu_regs);
|
|
count += ARRAY_SIZE (csky_supported_ar_regs);
|
|
count += ARRAY_SIZE (csky_supported_bank0_regs);
|
|
count += ARRAY_SIZE (csky_supported_mmu_regs);
|
|
count += ARRAY_SIZE (csky_supported_bank15_regs);
|
|
count += ARRAY_SIZE (csky_supported_alias_regs);
|
|
/* Bank1~Bank14, Bank16~Bank31. */
|
|
count += 32 * (14 + 16);
|
|
return count;
|
|
}
|
|
|
|
/* Return a supported register according to index. */
|
|
|
|
static const struct csky_supported_tdesc_register *
|
|
csky_get_supported_register_by_index (int index)
|
|
{
|
|
static struct csky_supported_tdesc_register tdesc_reg;
|
|
int count = 0;
|
|
int multi, remain;
|
|
int count_gpr = ARRAY_SIZE (csky_supported_gpr_regs);
|
|
int count_fpu = ARRAY_SIZE (csky_supported_fpu_regs);
|
|
int count_ar = ARRAY_SIZE (csky_supported_ar_regs);
|
|
int count_bank0 = ARRAY_SIZE (csky_supported_bank0_regs);
|
|
int count_mmu = ARRAY_SIZE (csky_supported_mmu_regs);
|
|
int count_bank15 = ARRAY_SIZE (csky_supported_bank15_regs);
|
|
int count_alias = ARRAY_SIZE (csky_supported_alias_regs);
|
|
|
|
count = count_gpr;
|
|
if (index < count)
|
|
return &csky_supported_gpr_regs[index];
|
|
if (index < (count + count_fpu))
|
|
return &csky_supported_fpu_regs[index - count];
|
|
count += count_fpu;
|
|
if (index < (count + count_ar))
|
|
return &csky_supported_ar_regs[index - count];
|
|
count += count_ar;
|
|
if (index < (count + count_bank0))
|
|
return &csky_supported_bank0_regs[index - count];
|
|
count += count_bank0;
|
|
if (index < (count + count_mmu))
|
|
return &csky_supported_mmu_regs[index - count];
|
|
count += count_mmu;
|
|
if (index < (count + count_bank15))
|
|
return &csky_supported_bank15_regs[index - count];
|
|
count += count_bank15;
|
|
if (index < (count + count_alias))
|
|
return &csky_supported_alias_regs[index - count];
|
|
count += count_alias;
|
|
index -= count;
|
|
multi = index / 32;
|
|
remain = index % 32;
|
|
switch (multi)
|
|
{
|
|
case 0: /* Bank1. */
|
|
{
|
|
sprintf (tdesc_reg.name, "cp1cr%d", remain);
|
|
tdesc_reg.num = 189 + remain;
|
|
}
|
|
break;
|
|
case 1: /* Bank2. */
|
|
{
|
|
sprintf (tdesc_reg.name, "cp2cr%d", remain);
|
|
tdesc_reg.num = 276 + remain;
|
|
}
|
|
break;
|
|
case 2: /* Bank3. */
|
|
{
|
|
sprintf (tdesc_reg.name, "cp3cr%d", remain);
|
|
tdesc_reg.num = 221 + remain;
|
|
}
|
|
break;
|
|
case 3: /* Bank4. */
|
|
case 4: /* Bank5. */
|
|
case 5: /* Bank6. */
|
|
case 6: /* Bank7. */
|
|
case 7: /* Bank8. */
|
|
case 8: /* Bank9. */
|
|
case 9: /* Bank10. */
|
|
case 10: /* Bank11. */
|
|
case 11: /* Bank12. */
|
|
case 12: /* Bank13. */
|
|
case 13: /* Bank14. */
|
|
{
|
|
/* Regitsers in Bank4~14 have continuous regno with start 308. */
|
|
sprintf (tdesc_reg.name, "cp%dcr%d", (multi + 1), remain);
|
|
tdesc_reg.num = 308 + ((multi - 3) * 32) + remain;
|
|
}
|
|
break;
|
|
case 14: /* Bank16. */
|
|
case 15: /* Bank17. */
|
|
case 16: /* Bank18. */
|
|
case 17: /* Bank19. */
|
|
case 18: /* Bank20. */
|
|
case 19: /* Bank21. */
|
|
case 20: /* Bank22. */
|
|
case 21: /* Bank23. */
|
|
case 22: /* Bank24. */
|
|
case 23: /* Bank25. */
|
|
case 24: /* Bank26. */
|
|
case 25: /* Bank27. */
|
|
case 26: /* Bank28. */
|
|
case 27: /* Bank29. */
|
|
case 28: /* Bank30. */
|
|
case 29: /* Bank31. */
|
|
{
|
|
/* Regitsers in Bank16~31 have continuous regno with start 660. */
|
|
sprintf (tdesc_reg.name, "cp%dcr%d", (multi + 2), remain);
|
|
tdesc_reg.num = 660 + ((multi - 14) * 32) + remain;
|
|
}
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
return &tdesc_reg;
|
|
}
|
|
|
|
/* Convenience function to print debug messages in prologue analysis. */
|
|
|
|
static void
|
|
print_savedreg_msg (int regno, int offsets[], bool print_continuing)
|
|
{
|
|
gdb_printf (gdb_stdlog, "csky: r%d saved at offset 0x%x\n",
|
|
regno, offsets[regno]);
|
|
if (print_continuing)
|
|
gdb_printf (gdb_stdlog, "csky: continuing\n");
|
|
}
|
|
|
|
/* Check whether the instruction at ADDR is 16-bit or not. */
|
|
|
|
static int
|
|
csky_pc_is_csky16 (struct gdbarch *gdbarch, CORE_ADDR addr)
|
|
{
|
|
gdb_byte target_mem[2];
|
|
int status;
|
|
unsigned int insn;
|
|
int ret = 1;
|
|
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
|
|
|
status = target_read_memory (addr, target_mem, 2);
|
|
/* Assume a 16-bit instruction if we can't read memory. */
|
|
if (status)
|
|
return 1;
|
|
|
|
/* Get instruction from memory. */
|
|
insn = extract_unsigned_integer (target_mem, 2, byte_order);
|
|
if ((insn & CSKY_32_INSN_MASK) == CSKY_32_INSN_MASK)
|
|
ret = 0;
|
|
else if (insn == CSKY_BKPT_INSN)
|
|
{
|
|
/* Check for 32-bit bkpt instruction which is all 0. */
|
|
status = target_read_memory (addr + 2, target_mem, 2);
|
|
if (status)
|
|
return 1;
|
|
|
|
insn = extract_unsigned_integer (target_mem, 2, byte_order);
|
|
if (insn == CSKY_BKPT_INSN)
|
|
ret = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* Get one instruction at ADDR and store it in INSN. Return 2 for
|
|
a 16-bit instruction or 4 for a 32-bit instruction. */
|
|
|
|
static int
|
|
csky_get_insn (struct gdbarch *gdbarch, CORE_ADDR addr, unsigned int *insn)
|
|
{
|
|
gdb_byte target_mem[2];
|
|
unsigned int insn_type;
|
|
int status;
|
|
int insn_len = 2;
|
|
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
|
|
|
status = target_read_memory (addr, target_mem, 2);
|
|
if (status)
|
|
memory_error (TARGET_XFER_E_IO, addr);
|
|
|
|
insn_type = extract_unsigned_integer (target_mem, 2, byte_order);
|
|
if (CSKY_32_INSN_MASK == (insn_type & CSKY_32_INSN_MASK))
|
|
{
|
|
status = target_read_memory (addr + 2, target_mem, 2);
|
|
if (status)
|
|
memory_error (TARGET_XFER_E_IO, addr);
|
|
insn_type = ((insn_type << 16)
|
|
| extract_unsigned_integer (target_mem, 2, byte_order));
|
|
insn_len = 4;
|
|
}
|
|
*insn = insn_type;
|
|
return insn_len;
|
|
}
|
|
|
|
/* Implement the read_pc gdbarch method. */
|
|
|
|
static CORE_ADDR
|
|
csky_read_pc (readable_regcache *regcache)
|
|
{
|
|
ULONGEST pc;
|
|
regcache->cooked_read (CSKY_PC_REGNUM, &pc);
|
|
return pc;
|
|
}
|
|
|
|
/* Implement the write_pc gdbarch method. */
|
|
|
|
static void
|
|
csky_write_pc (regcache *regcache, CORE_ADDR val)
|
|
{
|
|
regcache_cooked_write_unsigned (regcache, CSKY_PC_REGNUM, val);
|
|
}
|
|
|
|
/* C-Sky ABI register names. */
|
|
|
|
static const char * const csky_register_names[] =
|
|
{
|
|
/* General registers 0 - 31. */
|
|
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
|
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
|
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
|
|
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
|
|
|
|
/* DSP hilo registers 36 and 37. */
|
|
"", "", "", "", "hi", "lo", "", "",
|
|
|
|
/* FPU/VPU general registers 40 - 71. */
|
|
"fr0", "fr1", "fr2", "fr3", "fr4", "fr5", "fr6", "fr7",
|
|
"fr8", "fr9", "fr10", "fr11", "fr12", "fr13", "fr14", "fr15",
|
|
"vr0", "vr1", "vr2", "vr3", "vr4", "vr5", "vr6", "vr7",
|
|
"vr8", "vr9", "vr10", "vr11", "vr12", "vr13", "vr14", "vr15",
|
|
|
|
/* Program counter 72. */
|
|
"pc",
|
|
|
|
/* Optional registers (ar) 73 - 88. */
|
|
"ar0", "ar1", "ar2", "ar3", "ar4", "ar5", "ar6", "ar7",
|
|
"ar8", "ar9", "ar10", "ar11", "ar12", "ar13", "ar14", "ar15",
|
|
|
|
/* Control registers (cr) 89 - 119. */
|
|
"psr", "vbr", "epsr", "fpsr", "epc", "fpc", "ss0", "ss1",
|
|
"ss2", "ss3", "ss4", "gcr", "gsr", "cr13", "cr14", "cr15",
|
|
"cr16", "cr17", "cr18", "cr19", "cr20", "cr21", "cr22", "cr23",
|
|
"cr24", "cr25", "cr26", "cr27", "cr28", "cr29", "cr30", "cr31",
|
|
|
|
/* FPU/VPU control registers 121 ~ 123. */
|
|
/* User sp 127. */
|
|
"fid", "fcr", "fesr", "", "", "", "usp",
|
|
|
|
/* MMU control registers: 128 - 136. */
|
|
"mcr0", "mcr2", "mcr3", "mcr4", "mcr6", "mcr8", "mcr29", "mcr30",
|
|
"mcr31", "", "", "",
|
|
|
|
/* Profiling control registers 140 - 143. */
|
|
/* Profiling software general registers 144 - 157. */
|
|
"profcr0", "profcr1", "profcr2", "profcr3", "profsgr0", "profsgr1",
|
|
"profsgr2", "profsgr3", "profsgr4", "profsgr5", "profsgr6", "profsgr7",
|
|
"profsgr8", "profsgr9", "profsgr10","profsgr11","profsgr12", "profsgr13",
|
|
"", "",
|
|
|
|
/* Profiling architecture general registers 160 - 174. */
|
|
"profagr0", "profagr1", "profagr2", "profagr3", "profagr4", "profagr5",
|
|
"profagr6", "profagr7", "profagr8", "profagr9", "profagr10","profagr11",
|
|
"profagr12","profagr13","profagr14", "",
|
|
|
|
/* Profiling extension general registers 176 - 188. */
|
|
"profxgr0", "profxgr1", "profxgr2", "profxgr3", "profxgr4", "profxgr5",
|
|
"profxgr6", "profxgr7", "profxgr8", "profxgr9", "profxgr10","profxgr11",
|
|
"profxgr12",
|
|
|
|
/* Control registers in bank1. */
|
|
"", "", "", "", "", "", "", "",
|
|
"", "", "", "", "", "", "", "",
|
|
"cp1cr16", "cp1cr17", "cp1cr18", "cp1cr19", "cp1cr20", "", "", "",
|
|
"", "", "", "", "", "", "", "",
|
|
|
|
/* Control registers in bank3 (ICE). */
|
|
"sepsr", "sevbr", "seepsr", "", "seepc", "", "nsssp", "seusp",
|
|
"sedcr", "", "", "", "", "", "", "",
|
|
"", "", "", "", "", "", "", "",
|
|
"", "", "", "", "", "", "", ""
|
|
};
|
|
|
|
/* Implement the register_name gdbarch method. */
|
|
|
|
static const char *
|
|
csky_register_name (struct gdbarch *gdbarch, int reg_nr)
|
|
{
|
|
int num_regs = gdbarch_num_regs (gdbarch);
|
|
int num_pseudo_regs = gdbarch_num_pseudo_regs (gdbarch);
|
|
|
|
if ((reg_nr >= num_regs) && (reg_nr < (num_regs + num_pseudo_regs)))
|
|
return csky_pseudo_register_name (gdbarch, reg_nr);
|
|
|
|
if (tdesc_has_registers (gdbarch_target_desc (gdbarch)))
|
|
return tdesc_register_name (gdbarch, reg_nr);
|
|
|
|
if (reg_nr < 0)
|
|
return NULL;
|
|
|
|
if (reg_nr >= gdbarch_num_regs (gdbarch))
|
|
return NULL;
|
|
|
|
return csky_register_names[reg_nr];
|
|
}
|
|
|
|
/* Construct vector type for vrx registers. */
|
|
|
|
static struct type *
|
|
csky_vector_type (struct gdbarch *gdbarch)
|
|
{
|
|
const struct builtin_type *bt = builtin_type (gdbarch);
|
|
|
|
struct type *t;
|
|
|
|
t = arch_composite_type (gdbarch, "__gdb_builtin_type_vec128i",
|
|
TYPE_CODE_UNION);
|
|
|
|
append_composite_type_field (t, "u32",
|
|
init_vector_type (bt->builtin_int32, 4));
|
|
append_composite_type_field (t, "u16",
|
|
init_vector_type (bt->builtin_int16, 8));
|
|
append_composite_type_field (t, "u8",
|
|
init_vector_type (bt->builtin_int8, 16));
|
|
|
|
t->set_is_vector (true);
|
|
t->set_name ("builtin_type_vec128i");
|
|
|
|
return t;
|
|
}
|
|
|
|
/* Return the GDB type object for the "standard" data type
|
|
of data in register N. */
|
|
|
|
static struct type *
|
|
csky_register_type (struct gdbarch *gdbarch, int reg_nr)
|
|
{
|
|
/* If type has been described in tdesc-xml, use it. */
|
|
if (tdesc_has_registers (gdbarch_target_desc (gdbarch)))
|
|
{
|
|
struct type *tdesc_t = tdesc_register_type (gdbarch, reg_nr);
|
|
if (tdesc_t)
|
|
return tdesc_t;
|
|
}
|
|
|
|
/* PC, EPC, FPC is a text pointer. */
|
|
if ((reg_nr == CSKY_PC_REGNUM) || (reg_nr == CSKY_EPC_REGNUM)
|
|
|| (reg_nr == CSKY_FPC_REGNUM))
|
|
return builtin_type (gdbarch)->builtin_func_ptr;
|
|
|
|
/* VBR is a data pointer. */
|
|
if (reg_nr == CSKY_VBR_REGNUM)
|
|
return builtin_type (gdbarch)->builtin_data_ptr;
|
|
|
|
/* Float register has 64 bits, and only in ck810. */
|
|
if ((reg_nr >=CSKY_FR0_REGNUM) && (reg_nr <= CSKY_FR0_REGNUM + 15))
|
|
return arch_float_type (gdbarch, 64, "builtin_type_csky_ext",
|
|
floatformats_ieee_double);
|
|
|
|
/* Vector register has 128 bits, and only in ck810. */
|
|
if ((reg_nr >= CSKY_VR0_REGNUM) && (reg_nr <= CSKY_VR0_REGNUM + 15))
|
|
return csky_vector_type (gdbarch);
|
|
|
|
/* Profiling general register has 48 bits, we use 64bit. */
|
|
if ((reg_nr >= CSKY_PROFGR_REGNUM) && (reg_nr <= CSKY_PROFGR_REGNUM + 44))
|
|
return builtin_type (gdbarch)->builtin_uint64;
|
|
|
|
if (reg_nr == CSKY_SP_REGNUM)
|
|
return builtin_type (gdbarch)->builtin_data_ptr;
|
|
|
|
/* Others are 32 bits. */
|
|
return builtin_type (gdbarch)->builtin_int32;
|
|
}
|
|
|
|
/* Data structure to marshall items in a dummy stack frame when
|
|
calling a function in the inferior. */
|
|
|
|
struct csky_stack_item
|
|
{
|
|
csky_stack_item (int len_, const gdb_byte *data_)
|
|
: len (len_), data (data_)
|
|
{}
|
|
|
|
int len;
|
|
const gdb_byte *data;
|
|
};
|
|
|
|
/* Implement the push_dummy_call gdbarch method. */
|
|
|
|
static CORE_ADDR
|
|
csky_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
|
|
struct regcache *regcache, CORE_ADDR bp_addr,
|
|
int nargs, struct value **args, CORE_ADDR sp,
|
|
function_call_return_method return_method,
|
|
CORE_ADDR struct_addr)
|
|
{
|
|
int argnum;
|
|
int argreg = CSKY_ABI_A0_REGNUM;
|
|
int last_arg_regnum = CSKY_ABI_LAST_ARG_REGNUM;
|
|
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
|
std::vector<csky_stack_item> stack_items;
|
|
|
|
/* Set the return address. For CSKY, the return breakpoint is
|
|
always at BP_ADDR. */
|
|
regcache_cooked_write_unsigned (regcache, CSKY_LR_REGNUM, bp_addr);
|
|
|
|
/* The struct_return pointer occupies the first parameter
|
|
passing register. */
|
|
if (return_method == return_method_struct)
|
|
{
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: struct return in %s = %s\n",
|
|
gdbarch_register_name (gdbarch, argreg),
|
|
paddress (gdbarch, struct_addr));
|
|
}
|
|
regcache_cooked_write_unsigned (regcache, argreg, struct_addr);
|
|
argreg++;
|
|
}
|
|
|
|
/* Put parameters into argument registers in REGCACHE.
|
|
In ABI argument registers are r0 through r3. */
|
|
for (argnum = 0; argnum < nargs; argnum++)
|
|
{
|
|
int len;
|
|
struct type *arg_type;
|
|
const gdb_byte *val;
|
|
|
|
arg_type = check_typedef (value_type (args[argnum]));
|
|
len = TYPE_LENGTH (arg_type);
|
|
val = value_contents (args[argnum]).data ();
|
|
|
|
/* Copy the argument to argument registers or the dummy stack.
|
|
Large arguments are split between registers and stack.
|
|
|
|
If len < 4, there is no need to worry about endianness since
|
|
the arguments will always be stored in the low address. */
|
|
if (len < 4)
|
|
{
|
|
CORE_ADDR regval
|
|
= extract_unsigned_integer (val, len, byte_order);
|
|
regcache_cooked_write_unsigned (regcache, argreg, regval);
|
|
argreg++;
|
|
}
|
|
else
|
|
{
|
|
while (len > 0)
|
|
{
|
|
int partial_len = len < 4 ? len : 4;
|
|
if (argreg <= last_arg_regnum)
|
|
{
|
|
/* The argument is passed in an argument register. */
|
|
CORE_ADDR regval
|
|
= extract_unsigned_integer (val, partial_len,
|
|
byte_order);
|
|
if (byte_order == BFD_ENDIAN_BIG)
|
|
regval <<= (4 - partial_len) * 8;
|
|
|
|
/* Put regval into register in REGCACHE. */
|
|
regcache_cooked_write_unsigned (regcache, argreg,
|
|
regval);
|
|
argreg++;
|
|
}
|
|
else
|
|
{
|
|
/* The argument should be pushed onto the dummy stack. */
|
|
stack_items.emplace_back (4, val);
|
|
}
|
|
len -= partial_len;
|
|
val += partial_len;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Transfer the dummy stack frame to the target. */
|
|
std::vector<csky_stack_item>::reverse_iterator iter;
|
|
for (iter = stack_items.rbegin (); iter != stack_items.rend (); ++iter)
|
|
{
|
|
sp -= iter->len;
|
|
write_memory (sp, iter->data, iter->len);
|
|
}
|
|
|
|
/* Finally, update the SP register. */
|
|
regcache_cooked_write_unsigned (regcache, CSKY_SP_REGNUM, sp);
|
|
return sp;
|
|
}
|
|
|
|
/* Implement the return_value gdbarch method. */
|
|
|
|
static enum return_value_convention
|
|
csky_return_value (struct gdbarch *gdbarch, struct value *function,
|
|
struct type *valtype, struct regcache *regcache,
|
|
gdb_byte *readbuf, const gdb_byte *writebuf)
|
|
{
|
|
CORE_ADDR regval;
|
|
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
|
int len = TYPE_LENGTH (valtype);
|
|
unsigned int ret_regnum = CSKY_RET_REGNUM;
|
|
|
|
/* Csky abi specifies that return values larger than 8 bytes
|
|
are put on the stack. */
|
|
if (len > 8)
|
|
return RETURN_VALUE_STRUCT_CONVENTION;
|
|
else
|
|
{
|
|
if (readbuf != NULL)
|
|
{
|
|
ULONGEST tmp;
|
|
/* By using store_unsigned_integer we avoid having to do
|
|
anything special for small big-endian values. */
|
|
regcache->cooked_read (ret_regnum, &tmp);
|
|
store_unsigned_integer (readbuf, (len > 4 ? 4 : len),
|
|
byte_order, tmp);
|
|
if (len > 4)
|
|
{
|
|
regcache->cooked_read (ret_regnum + 1, &tmp);
|
|
store_unsigned_integer (readbuf + 4, 4, byte_order, tmp);
|
|
}
|
|
}
|
|
if (writebuf != NULL)
|
|
{
|
|
regval = extract_unsigned_integer (writebuf, len > 4 ? 4 : len,
|
|
byte_order);
|
|
regcache_cooked_write_unsigned (regcache, ret_regnum, regval);
|
|
if (len > 4)
|
|
{
|
|
regval = extract_unsigned_integer ((gdb_byte *) writebuf + 4,
|
|
4, byte_order);
|
|
regcache_cooked_write_unsigned (regcache, ret_regnum + 1,
|
|
regval);
|
|
}
|
|
|
|
}
|
|
return RETURN_VALUE_REGISTER_CONVENTION;
|
|
}
|
|
}
|
|
|
|
/* Implement the frame_align gdbarch method.
|
|
|
|
Adjust the address downward (direction of stack growth) so that it
|
|
is correctly aligned for a new stack frame. */
|
|
|
|
static CORE_ADDR
|
|
csky_frame_align (struct gdbarch *gdbarch, CORE_ADDR addr)
|
|
{
|
|
return align_down (addr, 4);
|
|
}
|
|
|
|
/* Unwind cache used for gdbarch fallback unwinder. */
|
|
|
|
struct csky_unwind_cache
|
|
{
|
|
/* The stack pointer at the time this frame was created; i.e. the
|
|
caller's stack pointer when this function was called. It is used
|
|
to identify this frame. */
|
|
CORE_ADDR prev_sp;
|
|
|
|
/* The frame base for this frame is just prev_sp - frame size.
|
|
FRAMESIZE is the distance from the frame pointer to the
|
|
initial stack pointer. */
|
|
int framesize;
|
|
|
|
/* The register used to hold the frame pointer for this frame. */
|
|
int framereg;
|
|
|
|
/* Saved register offsets. */
|
|
trad_frame_saved_reg *saved_regs;
|
|
};
|
|
|
|
/* Do prologue analysis, returning the PC of the first instruction
|
|
after the function prologue. */
|
|
|
|
static CORE_ADDR
|
|
csky_analyze_prologue (struct gdbarch *gdbarch,
|
|
CORE_ADDR start_pc,
|
|
CORE_ADDR limit_pc,
|
|
CORE_ADDR end_pc,
|
|
struct frame_info *this_frame,
|
|
struct csky_unwind_cache *this_cache,
|
|
lr_type_t lr_type)
|
|
{
|
|
CORE_ADDR addr;
|
|
unsigned int insn, rn;
|
|
int framesize = 0;
|
|
int stacksize = 0;
|
|
int register_offsets[CSKY_NUM_GREGS_SAVED_GREGS];
|
|
int insn_len;
|
|
/* For adjusting fp. */
|
|
int is_fp_saved = 0;
|
|
int adjust_fp = 0;
|
|
|
|
/* REGISTER_OFFSETS will contain offsets from the top of the frame
|
|
(NOT the frame pointer) for the various saved registers, or -1
|
|
if the register is not saved. */
|
|
for (rn = 0; rn < CSKY_NUM_GREGS_SAVED_GREGS; rn++)
|
|
register_offsets[rn] = -1;
|
|
|
|
/* Analyze the prologue. Things we determine from analyzing the
|
|
prologue include the size of the frame and which registers are
|
|
saved (and where). */
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: Scanning prologue: start_pc = 0x%x,"
|
|
"limit_pc = 0x%x\n", (unsigned int) start_pc,
|
|
(unsigned int) limit_pc);
|
|
}
|
|
|
|
/* Default to 16 bit instruction. */
|
|
insn_len = 2;
|
|
stacksize = 0;
|
|
for (addr = start_pc; addr < limit_pc; addr += insn_len)
|
|
{
|
|
/* Get next insn. */
|
|
insn_len = csky_get_insn (gdbarch, addr, &insn);
|
|
|
|
/* Check if 32 bit. */
|
|
if (insn_len == 4)
|
|
{
|
|
/* subi32 sp,sp oimm12. */
|
|
if (CSKY_32_IS_SUBI0 (insn))
|
|
{
|
|
/* Got oimm12. */
|
|
int offset = CSKY_32_SUBI_IMM (insn);
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: got subi sp,%d; continuing\n",
|
|
offset);
|
|
}
|
|
stacksize += offset;
|
|
continue;
|
|
}
|
|
/* stm32 ry-rz,(sp). */
|
|
else if (CSKY_32_IS_STMx0 (insn))
|
|
{
|
|
/* Spill register(s). */
|
|
int start_register;
|
|
int reg_count;
|
|
int offset;
|
|
|
|
/* BIG WARNING! The CKCore ABI does not restrict functions
|
|
to taking only one stack allocation. Therefore, when
|
|
we save a register, we record the offset of where it was
|
|
saved relative to the current stacksize. This will
|
|
then give an offset from the SP upon entry to our
|
|
function. Remember, stacksize is NOT constant until
|
|
we're done scanning the prologue. */
|
|
start_register = CSKY_32_STM_VAL_REGNUM (insn);
|
|
reg_count = CSKY_32_STM_SIZE (insn);
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: got stm r%d-r%d,(sp)\n",
|
|
start_register,
|
|
start_register + reg_count);
|
|
}
|
|
|
|
for (rn = start_register, offset = 0;
|
|
rn <= start_register + reg_count;
|
|
rn++, offset += 4)
|
|
{
|
|
register_offsets[rn] = stacksize - offset;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: r%d saved at 0x%x"
|
|
" (offset %d)\n",
|
|
rn, register_offsets[rn],
|
|
offset);
|
|
}
|
|
}
|
|
if (csky_debug)
|
|
gdb_printf (gdb_stdlog, "csky: continuing\n");
|
|
continue;
|
|
}
|
|
/* stw ry,(sp,disp). */
|
|
else if (CSKY_32_IS_STWx0 (insn))
|
|
{
|
|
/* Spill register: see note for IS_STM above. */
|
|
int disp;
|
|
|
|
rn = CSKY_32_ST_VAL_REGNUM (insn);
|
|
disp = CSKY_32_ST_OFFSET (insn);
|
|
register_offsets[rn] = stacksize - disp;
|
|
if (csky_debug)
|
|
print_savedreg_msg (rn, register_offsets, true);
|
|
continue;
|
|
}
|
|
else if (CSKY_32_IS_MOV_FP_SP (insn))
|
|
{
|
|
/* SP is saved to FP reg, means code afer prologue may
|
|
modify SP. */
|
|
is_fp_saved = 1;
|
|
adjust_fp = stacksize;
|
|
continue;
|
|
}
|
|
else if (CSKY_32_IS_MFCR_EPSR (insn))
|
|
{
|
|
unsigned int insn2;
|
|
addr += 4;
|
|
int mfcr_regnum = insn & 0x1f;
|
|
insn_len = csky_get_insn (gdbarch, addr, &insn2);
|
|
if (insn_len == 2)
|
|
{
|
|
int stw_regnum = (insn2 >> 5) & 0x7;
|
|
if (CSKY_16_IS_STWx0 (insn2) && (mfcr_regnum == stw_regnum))
|
|
{
|
|
int offset;
|
|
|
|
/* CSKY_EPSR_REGNUM. */
|
|
rn = CSKY_NUM_GREGS;
|
|
offset = CSKY_16_STWx0_OFFSET (insn2);
|
|
register_offsets[rn] = stacksize - offset;
|
|
if (csky_debug)
|
|
print_savedreg_msg (rn, register_offsets, true);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/* INSN_LEN == 4. */
|
|
int stw_regnum = (insn2 >> 21) & 0x1f;
|
|
if (CSKY_32_IS_STWx0 (insn2) && (mfcr_regnum == stw_regnum))
|
|
{
|
|
int offset;
|
|
|
|
/* CSKY_EPSR_REGNUM. */
|
|
rn = CSKY_NUM_GREGS;
|
|
offset = CSKY_32_ST_OFFSET (insn2);
|
|
register_offsets[rn] = framesize - offset;
|
|
if (csky_debug)
|
|
print_savedreg_msg (rn, register_offsets, true);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_MFCR_FPSR (insn))
|
|
{
|
|
unsigned int insn2;
|
|
addr += 4;
|
|
int mfcr_regnum = insn & 0x1f;
|
|
insn_len = csky_get_insn (gdbarch, addr, &insn2);
|
|
if (insn_len == 2)
|
|
{
|
|
int stw_regnum = (insn2 >> 5) & 0x7;
|
|
if (CSKY_16_IS_STWx0 (insn2) && (mfcr_regnum
|
|
== stw_regnum))
|
|
{
|
|
int offset;
|
|
|
|
/* CSKY_FPSR_REGNUM. */
|
|
rn = CSKY_NUM_GREGS + 1;
|
|
offset = CSKY_16_STWx0_OFFSET (insn2);
|
|
register_offsets[rn] = stacksize - offset;
|
|
if (csky_debug)
|
|
print_savedreg_msg (rn, register_offsets, true);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/* INSN_LEN == 4. */
|
|
int stw_regnum = (insn2 >> 21) & 0x1f;
|
|
if (CSKY_32_IS_STWx0 (insn2) && (mfcr_regnum == stw_regnum))
|
|
{
|
|
int offset;
|
|
|
|
/* CSKY_FPSR_REGNUM. */
|
|
rn = CSKY_NUM_GREGS + 1;
|
|
offset = CSKY_32_ST_OFFSET (insn2);
|
|
register_offsets[rn] = framesize - offset;
|
|
if (csky_debug)
|
|
print_savedreg_msg (rn, register_offsets, true);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_MFCR_EPC (insn))
|
|
{
|
|
unsigned int insn2;
|
|
addr += 4;
|
|
int mfcr_regnum = insn & 0x1f;
|
|
insn_len = csky_get_insn (gdbarch, addr, &insn2);
|
|
if (insn_len == 2)
|
|
{
|
|
int stw_regnum = (insn2 >> 5) & 0x7;
|
|
if (CSKY_16_IS_STWx0 (insn2) && (mfcr_regnum == stw_regnum))
|
|
{
|
|
int offset;
|
|
|
|
/* CSKY_EPC_REGNUM. */
|
|
rn = CSKY_NUM_GREGS + 2;
|
|
offset = CSKY_16_STWx0_OFFSET (insn2);
|
|
register_offsets[rn] = stacksize - offset;
|
|
if (csky_debug)
|
|
print_savedreg_msg (rn, register_offsets, true);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/* INSN_LEN == 4. */
|
|
int stw_regnum = (insn2 >> 21) & 0x1f;
|
|
if (CSKY_32_IS_STWx0 (insn2) && (mfcr_regnum == stw_regnum))
|
|
{
|
|
int offset;
|
|
|
|
/* CSKY_EPC_REGNUM. */
|
|
rn = CSKY_NUM_GREGS + 2;
|
|
offset = CSKY_32_ST_OFFSET (insn2);
|
|
register_offsets[rn] = framesize - offset;
|
|
if (csky_debug)
|
|
print_savedreg_msg (rn, register_offsets, true);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_MFCR_FPC (insn))
|
|
{
|
|
unsigned int insn2;
|
|
addr += 4;
|
|
int mfcr_regnum = insn & 0x1f;
|
|
insn_len = csky_get_insn (gdbarch, addr, &insn2);
|
|
if (insn_len == 2)
|
|
{
|
|
int stw_regnum = (insn2 >> 5) & 0x7;
|
|
if (CSKY_16_IS_STWx0 (insn2) && (mfcr_regnum == stw_regnum))
|
|
{
|
|
int offset;
|
|
|
|
/* CSKY_FPC_REGNUM. */
|
|
rn = CSKY_NUM_GREGS + 3;
|
|
offset = CSKY_16_STWx0_OFFSET (insn2);
|
|
register_offsets[rn] = stacksize - offset;
|
|
if (csky_debug)
|
|
print_savedreg_msg (rn, register_offsets, true);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/* INSN_LEN == 4. */
|
|
int stw_regnum = (insn2 >> 21) & 0x1f;
|
|
if (CSKY_32_IS_STWx0 (insn2) && (mfcr_regnum == stw_regnum))
|
|
{
|
|
int offset;
|
|
|
|
/* CSKY_FPC_REGNUM. */
|
|
rn = CSKY_NUM_GREGS + 3;
|
|
offset = CSKY_32_ST_OFFSET (insn2);
|
|
register_offsets[rn] = framesize - offset;
|
|
if (csky_debug)
|
|
print_savedreg_msg (rn, register_offsets, true);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_PUSH (insn))
|
|
{
|
|
/* Push for 32_bit. */
|
|
if (CSKY_32_IS_PUSH_R29 (insn))
|
|
{
|
|
stacksize += 4;
|
|
register_offsets[29] = stacksize;
|
|
if (csky_debug)
|
|
print_savedreg_msg (29, register_offsets, false);
|
|
}
|
|
if (CSKY_32_PUSH_LIST2 (insn))
|
|
{
|
|
int num = CSKY_32_PUSH_LIST2 (insn);
|
|
int tmp = 0;
|
|
stacksize += num * 4;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: push regs_array: r16-r%d\n",
|
|
16 + num - 1);
|
|
}
|
|
for (rn = 16; rn <= 16 + num - 1; rn++)
|
|
{
|
|
register_offsets[rn] = stacksize - tmp;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: r%d saved at 0x%x"
|
|
" (offset %d)\n", rn,
|
|
register_offsets[rn], tmp);
|
|
}
|
|
tmp += 4;
|
|
}
|
|
}
|
|
if (CSKY_32_IS_PUSH_R15 (insn))
|
|
{
|
|
stacksize += 4;
|
|
register_offsets[15] = stacksize;
|
|
if (csky_debug)
|
|
print_savedreg_msg (15, register_offsets, false);
|
|
}
|
|
if (CSKY_32_PUSH_LIST1 (insn))
|
|
{
|
|
int num = CSKY_32_PUSH_LIST1 (insn);
|
|
int tmp = 0;
|
|
stacksize += num * 4;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: push regs_array: r4-r%d\n",
|
|
4 + num - 1);
|
|
}
|
|
for (rn = 4; rn <= 4 + num - 1; rn++)
|
|
{
|
|
register_offsets[rn] = stacksize - tmp;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: r%d saved at 0x%x"
|
|
" (offset %d)\n", rn,
|
|
register_offsets[rn], tmp);
|
|
}
|
|
tmp += 4;
|
|
}
|
|
}
|
|
|
|
framesize = stacksize;
|
|
if (csky_debug)
|
|
gdb_printf (gdb_stdlog, "csky: continuing\n");
|
|
continue;
|
|
}
|
|
else if (CSKY_32_IS_LRW4 (insn) || CSKY_32_IS_MOVI4 (insn)
|
|
|| CSKY_32_IS_MOVIH4 (insn) || CSKY_32_IS_BMASKI4 (insn))
|
|
{
|
|
int adjust = 0;
|
|
int offset = 0;
|
|
unsigned int insn2;
|
|
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: looking at large frame\n");
|
|
}
|
|
if (CSKY_32_IS_LRW4 (insn))
|
|
{
|
|
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
|
int literal_addr = (addr + ((insn & 0xffff) << 2))
|
|
& 0xfffffffc;
|
|
adjust = read_memory_unsigned_integer (literal_addr, 4,
|
|
byte_order);
|
|
}
|
|
else if (CSKY_32_IS_MOVI4 (insn))
|
|
adjust = (insn & 0xffff);
|
|
else if (CSKY_32_IS_MOVIH4 (insn))
|
|
adjust = (insn & 0xffff) << 16;
|
|
else
|
|
{
|
|
/* CSKY_32_IS_BMASKI4 (insn). */
|
|
adjust = (1 << (((insn & 0x3e00000) >> 21) + 1)) - 1;
|
|
}
|
|
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: base stacksize=0x%x\n", adjust);
|
|
|
|
/* May have zero or more insns which modify r4. */
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: looking for r4 adjusters...\n");
|
|
}
|
|
|
|
offset = 4;
|
|
insn_len = csky_get_insn (gdbarch, addr + offset, &insn2);
|
|
while (CSKY_IS_R4_ADJUSTER (insn2))
|
|
{
|
|
if (CSKY_32_IS_ADDI4 (insn2))
|
|
{
|
|
int imm = (insn2 & 0xfff) + 1;
|
|
adjust += imm;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: addi r4,%d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_SUBI4 (insn2))
|
|
{
|
|
int imm = (insn2 & 0xfff) + 1;
|
|
adjust -= imm;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: subi r4,%d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_NOR4 (insn2))
|
|
{
|
|
adjust = ~adjust;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: nor r4,r4,r4\n");
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_ROTLI4 (insn2))
|
|
{
|
|
int imm = ((insn2 >> 21) & 0x1f);
|
|
int temp = adjust >> (32 - imm);
|
|
adjust <<= imm;
|
|
adjust |= temp;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: rotli r4,r4,%d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_LISI4 (insn2))
|
|
{
|
|
int imm = ((insn2 >> 21) & 0x1f);
|
|
adjust <<= imm;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: lsli r4,r4,%d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_BSETI4 (insn2))
|
|
{
|
|
int imm = ((insn2 >> 21) & 0x1f);
|
|
adjust |= (1 << imm);
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: bseti r4,r4 %d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_BCLRI4 (insn2))
|
|
{
|
|
int imm = ((insn2 >> 21) & 0x1f);
|
|
adjust &= ~(1 << imm);
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: bclri r4,r4 %d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_IXH4 (insn2))
|
|
{
|
|
adjust *= 3;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: ixh r4,r4,r4\n");
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_IXW4 (insn2))
|
|
{
|
|
adjust *= 5;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: ixw r4,r4,r4\n");
|
|
}
|
|
}
|
|
else if (CSKY_16_IS_ADDI4 (insn2))
|
|
{
|
|
int imm = (insn2 & 0xff) + 1;
|
|
adjust += imm;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: addi r4,%d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_16_IS_SUBI4 (insn2))
|
|
{
|
|
int imm = (insn2 & 0xff) + 1;
|
|
adjust -= imm;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: subi r4,%d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_16_IS_NOR4 (insn2))
|
|
{
|
|
adjust = ~adjust;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: nor r4,r4\n");
|
|
}
|
|
}
|
|
else if (CSKY_16_IS_BSETI4 (insn2))
|
|
{
|
|
int imm = (insn2 & 0x1f);
|
|
adjust |= (1 << imm);
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: bseti r4, %d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_16_IS_BCLRI4 (insn2))
|
|
{
|
|
int imm = (insn2 & 0x1f);
|
|
adjust &= ~(1 << imm);
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: bclri r4, %d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_16_IS_LSLI4 (insn2))
|
|
{
|
|
int imm = (insn2 & 0x1f);
|
|
adjust <<= imm;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: lsli r4,r4, %d\n", imm);
|
|
}
|
|
}
|
|
|
|
offset += insn_len;
|
|
insn_len = csky_get_insn (gdbarch, addr + offset, &insn2);
|
|
};
|
|
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog, "csky: done looking for"
|
|
" r4 adjusters\n");
|
|
}
|
|
|
|
/* If the next insn adjusts the stack pointer, we keep
|
|
everything; if not, we scrap it and we've found the
|
|
end of the prologue. */
|
|
if (CSKY_IS_SUBU4 (insn2))
|
|
{
|
|
addr += offset;
|
|
stacksize += adjust;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: found stack adjustment of"
|
|
" 0x%x bytes.\n", adjust);
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: skipping to new address %s\n",
|
|
core_addr_to_string_nz (addr));
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: continuing\n");
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* None of these instructions are prologue, so don't touch
|
|
anything. */
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: no subu sp,sp,r4; NOT altering"
|
|
" stacksize.\n");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* insn_len != 4. */
|
|
|
|
/* subi.sp sp,disp. */
|
|
if (CSKY_16_IS_SUBI0 (insn))
|
|
{
|
|
int offset = CSKY_16_SUBI_IMM (insn);
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: got subi r0,%d; continuing\n",
|
|
offset);
|
|
}
|
|
stacksize += offset;
|
|
continue;
|
|
}
|
|
/* stw.16 rz,(sp,disp). */
|
|
else if (CSKY_16_IS_STWx0 (insn))
|
|
{
|
|
/* Spill register: see note for IS_STM above. */
|
|
int disp;
|
|
|
|
rn = CSKY_16_ST_VAL_REGNUM (insn);
|
|
disp = CSKY_16_ST_OFFSET (insn);
|
|
register_offsets[rn] = stacksize - disp;
|
|
if (csky_debug)
|
|
print_savedreg_msg (rn, register_offsets, true);
|
|
continue;
|
|
}
|
|
else if (CSKY_16_IS_MOV_FP_SP (insn))
|
|
{
|
|
/* SP is saved to FP reg, means prologue may modify SP. */
|
|
is_fp_saved = 1;
|
|
adjust_fp = stacksize;
|
|
continue;
|
|
}
|
|
else if (CSKY_16_IS_PUSH (insn))
|
|
{
|
|
/* Push for 16_bit. */
|
|
int offset = 0;
|
|
if (CSKY_16_IS_PUSH_R15 (insn))
|
|
{
|
|
stacksize += 4;
|
|
register_offsets[15] = stacksize;
|
|
if (csky_debug)
|
|
print_savedreg_msg (15, register_offsets, false);
|
|
offset += 4;
|
|
}
|
|
if (CSKY_16_PUSH_LIST1 (insn))
|
|
{
|
|
int num = CSKY_16_PUSH_LIST1 (insn);
|
|
int tmp = 0;
|
|
stacksize += num * 4;
|
|
offset += num * 4;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: push regs_array: r4-r%d\n",
|
|
4 + num - 1);
|
|
}
|
|
for (rn = 4; rn <= 4 + num - 1; rn++)
|
|
{
|
|
register_offsets[rn] = stacksize - tmp;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: r%d saved at 0x%x"
|
|
" (offset %d)\n", rn,
|
|
register_offsets[rn], offset);
|
|
}
|
|
tmp += 4;
|
|
}
|
|
}
|
|
|
|
framesize = stacksize;
|
|
if (csky_debug)
|
|
gdb_printf (gdb_stdlog, "csky: continuing\n");
|
|
continue;
|
|
}
|
|
else if (CSKY_16_IS_LRW4 (insn) || CSKY_16_IS_MOVI4 (insn))
|
|
{
|
|
int adjust = 0;
|
|
unsigned int insn2;
|
|
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: looking at large frame\n");
|
|
}
|
|
if (CSKY_16_IS_LRW4 (insn))
|
|
{
|
|
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
|
int offset = ((insn & 0x300) >> 3) | (insn & 0x1f);
|
|
int literal_addr = (addr + ( offset << 2)) & 0xfffffffc;
|
|
adjust = read_memory_unsigned_integer (literal_addr, 4,
|
|
byte_order);
|
|
}
|
|
else
|
|
{
|
|
/* CSKY_16_IS_MOVI4 (insn). */
|
|
adjust = (insn & 0xff);
|
|
}
|
|
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: base stacksize=0x%x\n", adjust);
|
|
}
|
|
|
|
/* May have zero or more instructions which modify r4. */
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: looking for r4 adjusters...\n");
|
|
}
|
|
int offset = 2;
|
|
insn_len = csky_get_insn (gdbarch, addr + offset, &insn2);
|
|
while (CSKY_IS_R4_ADJUSTER (insn2))
|
|
{
|
|
if (CSKY_32_IS_ADDI4 (insn2))
|
|
{
|
|
int imm = (insn2 & 0xfff) + 1;
|
|
adjust += imm;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: addi r4,%d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_SUBI4 (insn2))
|
|
{
|
|
int imm = (insn2 & 0xfff) + 1;
|
|
adjust -= imm;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: subi r4,%d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_NOR4 (insn2))
|
|
{
|
|
adjust = ~adjust;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: nor r4,r4,r4\n");
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_ROTLI4 (insn2))
|
|
{
|
|
int imm = ((insn2 >> 21) & 0x1f);
|
|
int temp = adjust >> (32 - imm);
|
|
adjust <<= imm;
|
|
adjust |= temp;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: rotli r4,r4,%d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_LISI4 (insn2))
|
|
{
|
|
int imm = ((insn2 >> 21) & 0x1f);
|
|
adjust <<= imm;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: lsli r4,r4,%d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_BSETI4 (insn2))
|
|
{
|
|
int imm = ((insn2 >> 21) & 0x1f);
|
|
adjust |= (1 << imm);
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: bseti r4,r4 %d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_BCLRI4 (insn2))
|
|
{
|
|
int imm = ((insn2 >> 21) & 0x1f);
|
|
adjust &= ~(1 << imm);
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: bclri r4,r4 %d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_IXH4 (insn2))
|
|
{
|
|
adjust *= 3;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: ixh r4,r4,r4\n");
|
|
}
|
|
}
|
|
else if (CSKY_32_IS_IXW4 (insn2))
|
|
{
|
|
adjust *= 5;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: ixw r4,r4,r4\n");
|
|
}
|
|
}
|
|
else if (CSKY_16_IS_ADDI4 (insn2))
|
|
{
|
|
int imm = (insn2 & 0xff) + 1;
|
|
adjust += imm;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: addi r4,%d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_16_IS_SUBI4 (insn2))
|
|
{
|
|
int imm = (insn2 & 0xff) + 1;
|
|
adjust -= imm;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: subi r4,%d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_16_IS_NOR4 (insn2))
|
|
{
|
|
adjust = ~adjust;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: nor r4,r4\n");
|
|
}
|
|
}
|
|
else if (CSKY_16_IS_BSETI4 (insn2))
|
|
{
|
|
int imm = (insn2 & 0x1f);
|
|
adjust |= (1 << imm);
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: bseti r4, %d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_16_IS_BCLRI4 (insn2))
|
|
{
|
|
int imm = (insn2 & 0x1f);
|
|
adjust &= ~(1 << imm);
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: bclri r4, %d\n", imm);
|
|
}
|
|
}
|
|
else if (CSKY_16_IS_LSLI4 (insn2))
|
|
{
|
|
int imm = (insn2 & 0x1f);
|
|
adjust <<= imm;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog,
|
|
"csky: lsli r4,r4, %d\n", imm);
|
|
}
|
|
}
|
|
|
|
offset += insn_len;
|
|
insn_len = csky_get_insn (gdbarch, addr + offset, &insn2);
|
|
};
|
|
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog, "csky: "
|
|
"done looking for r4 adjusters\n");
|
|
}
|
|
|
|
/* If the next instruction adjusts the stack pointer, we keep
|
|
everything; if not, we scrap it and we've found the end
|
|
of the prologue. */
|
|
if (CSKY_IS_SUBU4 (insn2))
|
|
{
|
|
addr += offset;
|
|
stacksize += adjust;
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog, "csky: "
|
|
"found stack adjustment of 0x%x"
|
|
" bytes.\n", adjust);
|
|
gdb_printf (gdb_stdlog, "csky: "
|
|
"skipping to new address %s\n",
|
|
core_addr_to_string_nz (addr));
|
|
gdb_printf (gdb_stdlog, "csky: continuing\n");
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* None of these instructions are prologue, so don't touch
|
|
anything. */
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog, "csky: no subu sp,r4; "
|
|
"NOT altering stacksize.\n");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* This is not a prologue instruction, so stop here. */
|
|
if (csky_debug)
|
|
{
|
|
gdb_printf (gdb_stdlog, "csky: insn is not a prologue"
|
|
" insn -- ending scan\n");
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (this_cache)
|
|
{
|
|
CORE_ADDR unwound_fp;
|
|
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
|
this_cache->framesize = framesize;
|
|
|
|
if (is_fp_saved)
|
|
{
|
|
this_cache->framereg = CSKY_FP_REGNUM;
|
|
unwound_fp = get_frame_register_unsigned (this_frame,
|
|
this_cache->framereg);
|
|
this_cache->prev_sp = unwound_fp + adjust_fp;
|
|
}
|
|
else
|
|
{
|
|
this_cache->framereg = CSKY_SP_REGNUM;
|
|
unwound_fp = get_frame_register_unsigned (this_frame,
|
|
this_cache->framereg);
|
|
this_cache->prev_sp = unwound_fp + stacksize;
|
|
}
|
|
|
|
/* Note where saved registers are stored. The offsets in
|
|
REGISTER_OFFSETS are computed relative to the top of the frame. */
|
|
for (rn = 0; rn < CSKY_NUM_GREGS; rn++)
|
|
{
|
|
if (register_offsets[rn] >= 0)
|
|
{
|
|
this_cache->saved_regs[rn].set_addr (this_cache->prev_sp
|
|
- register_offsets[rn]);
|
|
if (csky_debug)
|
|
{
|
|
CORE_ADDR rn_value = read_memory_unsigned_integer (
|
|
this_cache->saved_regs[rn].addr (), 4, byte_order);
|
|
gdb_printf (gdb_stdlog, "Saved register %s "
|
|
"stored at 0x%08lx, value=0x%08lx\n",
|
|
csky_register_names[rn],
|
|
(unsigned long)
|
|
this_cache->saved_regs[rn].addr (),
|
|
(unsigned long) rn_value);
|
|
}
|
|
}
|
|
}
|
|
if (lr_type == LR_TYPE_EPC)
|
|
{
|
|
/* rte || epc . */
|
|
this_cache->saved_regs[CSKY_PC_REGNUM]
|
|
= this_cache->saved_regs[CSKY_EPC_REGNUM];
|
|
}
|
|
else if (lr_type == LR_TYPE_FPC)
|
|
{
|
|
/* rfi || fpc . */
|
|
this_cache->saved_regs[CSKY_PC_REGNUM]
|
|
= this_cache->saved_regs[CSKY_FPC_REGNUM];
|
|
}
|
|
else
|
|
{
|
|
this_cache->saved_regs[CSKY_PC_REGNUM]
|
|
= this_cache->saved_regs[CSKY_LR_REGNUM];
|
|
}
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
|
|
/* Detect whether PC is at a point where the stack frame has been
|
|
destroyed. */
|
|
|
|
static int
|
|
csky_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc)
|
|
{
|
|
unsigned int insn;
|
|
CORE_ADDR addr;
|
|
CORE_ADDR func_start, func_end;
|
|
|
|
if (!find_pc_partial_function (pc, NULL, &func_start, &func_end))
|
|
return 0;
|
|
|
|
bool fp_saved = false;
|
|
int insn_len;
|
|
for (addr = func_start; addr < func_end; addr += insn_len)
|
|
{
|
|
/* Get next insn. */
|
|
insn_len = csky_get_insn (gdbarch, addr, &insn);
|
|
|
|
if (insn_len == 2)
|
|
{
|
|
/* Is sp is saved to fp. */
|
|
if (CSKY_16_IS_MOV_FP_SP (insn))
|
|
fp_saved = true;
|
|
/* If sp was saved to fp and now being restored from
|
|
fp then it indicates the start of epilog. */
|
|
else if (fp_saved && CSKY_16_IS_MOV_SP_FP (insn))
|
|
return pc >= addr;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Implement the skip_prologue gdbarch hook. */
|
|
|
|
static CORE_ADDR
|
|
csky_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
|
|
{
|
|
CORE_ADDR func_addr, func_end;
|
|
const int default_search_limit = 128;
|
|
|
|
/* See if we can find the end of the prologue using the symbol table. */
|
|
if (find_pc_partial_function (pc, NULL, &func_addr, &func_end))
|
|
{
|
|
CORE_ADDR post_prologue_pc
|
|
= skip_prologue_using_sal (gdbarch, func_addr);
|
|
|
|
if (post_prologue_pc != 0)
|
|
return std::max (pc, post_prologue_pc);
|
|
}
|
|
else
|
|
func_end = pc + default_search_limit;
|
|
|
|
/* Find the end of prologue. Default lr_type. */
|
|
return csky_analyze_prologue (gdbarch, pc, func_end, func_end,
|
|
NULL, NULL, LR_TYPE_R15);
|
|
}
|
|
|
|
/* Implement the breakpoint_kind_from_pc gdbarch method. */
|
|
|
|
static int
|
|
csky_breakpoint_kind_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr)
|
|
{
|
|
if (csky_pc_is_csky16 (gdbarch, *pcptr))
|
|
return CSKY_INSN_SIZE16;
|
|
else
|
|
return CSKY_INSN_SIZE32;
|
|
}
|
|
|
|
/* Implement the sw_breakpoint_from_kind gdbarch method. */
|
|
|
|
static const gdb_byte *
|
|
csky_sw_breakpoint_from_kind (struct gdbarch *gdbarch, int kind, int *size)
|
|
{
|
|
*size = kind;
|
|
if (kind == CSKY_INSN_SIZE16)
|
|
{
|
|
static gdb_byte csky_16_breakpoint[] = { 0, 0 };
|
|
return csky_16_breakpoint;
|
|
}
|
|
else
|
|
{
|
|
static gdb_byte csky_32_breakpoint[] = { 0, 0, 0, 0 };
|
|
return csky_32_breakpoint;
|
|
}
|
|
}
|
|
|
|
/* Implement the memory_insert_breakpoint gdbarch method. */
|
|
|
|
static int
|
|
csky_memory_insert_breakpoint (struct gdbarch *gdbarch,
|
|
struct bp_target_info *bp_tgt)
|
|
{
|
|
int val;
|
|
const unsigned char *bp;
|
|
gdb_byte bp_write_record1[] = { 0, 0, 0, 0 };
|
|
gdb_byte bp_write_record2[] = { 0, 0, 0, 0 };
|
|
gdb_byte bp_record[] = { 0, 0, 0, 0 };
|
|
|
|
/* Sanity-check bp_address. */
|
|
if (bp_tgt->reqstd_address % 2)
|
|
warning (_("Invalid breakpoint address 0x%x is an odd number."),
|
|
(unsigned int) bp_tgt->reqstd_address);
|
|
scoped_restore restore_memory
|
|
= make_scoped_restore_show_memory_breakpoints (1);
|
|
|
|
/* Determine appropriate breakpoint_kind for this address. */
|
|
bp_tgt->kind = csky_breakpoint_kind_from_pc (gdbarch,
|
|
&bp_tgt->reqstd_address);
|
|
|
|
/* Save the memory contents. */
|
|
bp_tgt->shadow_len = bp_tgt->kind;
|
|
|
|
/* Fill bp_tgt->placed_address. */
|
|
bp_tgt->placed_address = bp_tgt->reqstd_address;
|
|
|
|
if (bp_tgt->kind == CSKY_INSN_SIZE16)
|
|
{
|
|
if ((bp_tgt->reqstd_address % 4) == 0)
|
|
{
|
|
/* Read two bytes. */
|
|
val = target_read_memory (bp_tgt->reqstd_address,
|
|
bp_tgt->shadow_contents, 2);
|
|
if (val)
|
|
return val;
|
|
|
|
/* Read two bytes. */
|
|
val = target_read_memory (bp_tgt->reqstd_address + 2,
|
|
bp_record, 2);
|
|
if (val)
|
|
return val;
|
|
|
|
/* Write the breakpoint. */
|
|
bp_write_record1[2] = bp_record[0];
|
|
bp_write_record1[3] = bp_record[1];
|
|
bp = bp_write_record1;
|
|
val = target_write_raw_memory (bp_tgt->reqstd_address, bp,
|
|
CSKY_WR_BKPT_MODE);
|
|
}
|
|
else
|
|
{
|
|
val = target_read_memory (bp_tgt->reqstd_address,
|
|
bp_tgt->shadow_contents, 2);
|
|
if (val)
|
|
return val;
|
|
|
|
val = target_read_memory (bp_tgt->reqstd_address - 2,
|
|
bp_record, 2);
|
|
if (val)
|
|
return val;
|
|
|
|
/* Write the breakpoint. */
|
|
bp_write_record1[0] = bp_record[0];
|
|
bp_write_record1[1] = bp_record[1];
|
|
bp = bp_write_record1;
|
|
val = target_write_raw_memory (bp_tgt->reqstd_address - 2,
|
|
bp, CSKY_WR_BKPT_MODE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bp_tgt->placed_address % 4 == 0)
|
|
{
|
|
val = target_read_memory (bp_tgt->reqstd_address,
|
|
bp_tgt->shadow_contents,
|
|
CSKY_WR_BKPT_MODE);
|
|
if (val)
|
|
return val;
|
|
|
|
/* Write the breakpoint. */
|
|
bp = bp_write_record1;
|
|
val = target_write_raw_memory (bp_tgt->reqstd_address,
|
|
bp, CSKY_WR_BKPT_MODE);
|
|
}
|
|
else
|
|
{
|
|
val = target_read_memory (bp_tgt->reqstd_address,
|
|
bp_tgt->shadow_contents,
|
|
CSKY_WR_BKPT_MODE);
|
|
if (val)
|
|
return val;
|
|
|
|
val = target_read_memory (bp_tgt->reqstd_address - 2,
|
|
bp_record, 2);
|
|
if (val)
|
|
return val;
|
|
|
|
val = target_read_memory (bp_tgt->reqstd_address + 4,
|
|
bp_record + 2, 2);
|
|
if (val)
|
|
return val;
|
|
|
|
bp_write_record1[0] = bp_record[0];
|
|
bp_write_record1[1] = bp_record[1];
|
|
bp_write_record2[2] = bp_record[2];
|
|
bp_write_record2[3] = bp_record[3];
|
|
|
|
/* Write the breakpoint. */
|
|
bp = bp_write_record1;
|
|
val = target_write_raw_memory (bp_tgt->reqstd_address - 2, bp,
|
|
CSKY_WR_BKPT_MODE);
|
|
if (val)
|
|
return val;
|
|
|
|
/* Write the breakpoint. */
|
|
bp = bp_write_record2;
|
|
val = target_write_raw_memory (bp_tgt->reqstd_address + 2, bp,
|
|
CSKY_WR_BKPT_MODE);
|
|
}
|
|
}
|
|
return val;
|
|
}
|
|
|
|
/* Restore the breakpoint shadow_contents to the target. */
|
|
|
|
static int
|
|
csky_memory_remove_breakpoint (struct gdbarch *gdbarch,
|
|
struct bp_target_info *bp_tgt)
|
|
{
|
|
int val;
|
|
gdb_byte bp_record[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
/* Different for shadow_len 2 or 4. */
|
|
if (bp_tgt->shadow_len == 2)
|
|
{
|
|
/* Do word-sized writes on word-aligned boundaries and read
|
|
padding bytes as necessary. */
|
|
if (bp_tgt->reqstd_address % 4 == 0)
|
|
{
|
|
val = target_read_memory (bp_tgt->reqstd_address + 2,
|
|
bp_record + 2, 2);
|
|
if (val)
|
|
return val;
|
|
bp_record[0] = bp_tgt->shadow_contents[0];
|
|
bp_record[1] = bp_tgt->shadow_contents[1];
|
|
return target_write_raw_memory (bp_tgt->reqstd_address,
|
|
bp_record, CSKY_WR_BKPT_MODE);
|
|
}
|
|
else
|
|
{
|
|
val = target_read_memory (bp_tgt->reqstd_address - 2,
|
|
bp_record, 2);
|
|
if (val)
|
|
return val;
|
|
bp_record[2] = bp_tgt->shadow_contents[0];
|
|
bp_record[3] = bp_tgt->shadow_contents[1];
|
|
return target_write_raw_memory (bp_tgt->reqstd_address - 2,
|
|
bp_record, CSKY_WR_BKPT_MODE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Do word-sized writes on word-aligned boundaries and read
|
|
padding bytes as necessary. */
|
|
if (bp_tgt->placed_address % 4 == 0)
|
|
{
|
|
return target_write_raw_memory (bp_tgt->reqstd_address,
|
|
bp_tgt->shadow_contents,
|
|
CSKY_WR_BKPT_MODE);
|
|
}
|
|
else
|
|
{
|
|
val = target_read_memory (bp_tgt->reqstd_address - 2,
|
|
bp_record, 2);
|
|
if (val)
|
|
return val;
|
|
val = target_read_memory (bp_tgt->reqstd_address + 4,
|
|
bp_record+6, 2);
|
|
if (val)
|
|
return val;
|
|
|
|
bp_record[2] = bp_tgt->shadow_contents[0];
|
|
bp_record[3] = bp_tgt->shadow_contents[1];
|
|
bp_record[4] = bp_tgt->shadow_contents[2];
|
|
bp_record[5] = bp_tgt->shadow_contents[3];
|
|
|
|
return target_write_raw_memory (bp_tgt->reqstd_address - 2,
|
|
bp_record,
|
|
CSKY_WR_BKPT_MODE * 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Determine link register type. */
|
|
|
|
static lr_type_t
|
|
csky_analyze_lr_type (struct gdbarch *gdbarch,
|
|
CORE_ADDR start_pc, CORE_ADDR end_pc)
|
|
{
|
|
CORE_ADDR addr;
|
|
unsigned int insn, insn_len;
|
|
insn_len = 2;
|
|
|
|
for (addr = start_pc; addr < end_pc; addr += insn_len)
|
|
{
|
|
insn_len = csky_get_insn (gdbarch, addr, &insn);
|
|
if (insn_len == 4)
|
|
{
|
|
if (CSKY_32_IS_MFCR_EPSR (insn) || CSKY_32_IS_MFCR_EPC (insn)
|
|
|| CSKY_32_IS_RTE (insn))
|
|
return LR_TYPE_EPC;
|
|
}
|
|
else if (CSKY_32_IS_MFCR_FPSR (insn) || CSKY_32_IS_MFCR_FPC (insn)
|
|
|| CSKY_32_IS_RFI (insn))
|
|
return LR_TYPE_FPC;
|
|
else if (CSKY_32_IS_JMP (insn) || CSKY_32_IS_BR (insn)
|
|
|| CSKY_32_IS_JMPIX (insn) || CSKY_32_IS_JMPI (insn))
|
|
return LR_TYPE_R15;
|
|
else
|
|
{
|
|
/* 16 bit instruction. */
|
|
if (CSKY_16_IS_JMP (insn) || CSKY_16_IS_BR (insn)
|
|
|| CSKY_16_IS_JMPIX (insn))
|
|
return LR_TYPE_R15;
|
|
}
|
|
}
|
|
return LR_TYPE_R15;
|
|
}
|
|
|
|
/* Heuristic unwinder. */
|
|
|
|
static struct csky_unwind_cache *
|
|
csky_frame_unwind_cache (struct frame_info *this_frame)
|
|
{
|
|
CORE_ADDR prologue_start, prologue_end, func_end, prev_pc, block_addr;
|
|
struct csky_unwind_cache *cache;
|
|
const struct block *bl;
|
|
unsigned long func_size = 0;
|
|
struct gdbarch *gdbarch = get_frame_arch (this_frame);
|
|
unsigned int sp_regnum = CSKY_SP_REGNUM;
|
|
|
|
/* Default lr type is r15. */
|
|
lr_type_t lr_type = LR_TYPE_R15;
|
|
|
|
cache = FRAME_OBSTACK_ZALLOC (struct csky_unwind_cache);
|
|
cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);
|
|
|
|
/* Assume there is no frame until proven otherwise. */
|
|
cache->framereg = sp_regnum;
|
|
|
|
cache->framesize = 0;
|
|
|
|
prev_pc = get_frame_pc (this_frame);
|
|
block_addr = get_frame_address_in_block (this_frame);
|
|
if (find_pc_partial_function (block_addr, NULL, &prologue_start,
|
|
&func_end) == 0)
|
|
/* We couldn't find a function containing block_addr, so bail out
|
|
and hope for the best. */
|
|
return cache;
|
|
|
|
/* Get the (function) symbol matching prologue_start. */
|
|
bl = block_for_pc (prologue_start);
|
|
if (bl != NULL)
|
|
func_size = bl->end () - bl->start ();
|
|
else
|
|
{
|
|
struct bound_minimal_symbol msymbol
|
|
= lookup_minimal_symbol_by_pc (prologue_start);
|
|
if (msymbol.minsym != NULL)
|
|
func_size = msymbol.minsym->size ();
|
|
}
|
|
|
|
/* If FUNC_SIZE is 0 we may have a special-case use of lr
|
|
e.g. exception or interrupt. */
|
|
if (func_size == 0)
|
|
lr_type = csky_analyze_lr_type (gdbarch, prologue_start, func_end);
|
|
|
|
prologue_end = std::min (func_end, prev_pc);
|
|
|
|
/* Analyze the function prologue. */
|
|
csky_analyze_prologue (gdbarch, prologue_start, prologue_end,
|
|
func_end, this_frame, cache, lr_type);
|
|
|
|
/* gdbarch_sp_regnum contains the value and not the address. */
|
|
cache->saved_regs[sp_regnum].set_value (cache->prev_sp);
|
|
return cache;
|
|
}
|
|
|
|
/* Implement the this_id function for the normal unwinder. */
|
|
|
|
static void
|
|
csky_frame_this_id (struct frame_info *this_frame,
|
|
void **this_prologue_cache, struct frame_id *this_id)
|
|
{
|
|
struct csky_unwind_cache *cache;
|
|
struct frame_id id;
|
|
|
|
if (*this_prologue_cache == NULL)
|
|
*this_prologue_cache = csky_frame_unwind_cache (this_frame);
|
|
cache = (struct csky_unwind_cache *) *this_prologue_cache;
|
|
|
|
/* This marks the outermost frame. */
|
|
if (cache->prev_sp == 0)
|
|
return;
|
|
|
|
id = frame_id_build (cache->prev_sp, get_frame_func (this_frame));
|
|
*this_id = id;
|
|
}
|
|
|
|
/* Implement the prev_register function for the normal unwinder. */
|
|
|
|
static struct value *
|
|
csky_frame_prev_register (struct frame_info *this_frame,
|
|
void **this_prologue_cache, int regnum)
|
|
{
|
|
struct csky_unwind_cache *cache;
|
|
|
|
if (*this_prologue_cache == NULL)
|
|
*this_prologue_cache = csky_frame_unwind_cache (this_frame);
|
|
cache = (struct csky_unwind_cache *) *this_prologue_cache;
|
|
|
|
return trad_frame_get_prev_register (this_frame, cache->saved_regs,
|
|
regnum);
|
|
}
|
|
|
|
/* Data structures for the normal prologue-analysis-based
|
|
unwinder. */
|
|
|
|
static const struct frame_unwind csky_unwind_cache = {
|
|
"cski prologue",
|
|
NORMAL_FRAME,
|
|
default_frame_unwind_stop_reason,
|
|
csky_frame_this_id,
|
|
csky_frame_prev_register,
|
|
NULL,
|
|
default_frame_sniffer,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
csky_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 (find_pc_partial_function (addr_in_block, NULL, NULL, NULL) == 0
|
|
|| in_plt_section (addr_in_block))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct csky_unwind_cache *
|
|
csky_make_stub_cache (struct frame_info *this_frame)
|
|
{
|
|
struct csky_unwind_cache *cache;
|
|
|
|
cache = FRAME_OBSTACK_ZALLOC (struct csky_unwind_cache);
|
|
cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);
|
|
cache->prev_sp = get_frame_register_unsigned (this_frame, CSKY_SP_REGNUM);
|
|
|
|
return cache;
|
|
}
|
|
|
|
static void
|
|
csky_stub_this_id (struct frame_info *this_frame,
|
|
void **this_cache,
|
|
struct frame_id *this_id)
|
|
{
|
|
struct csky_unwind_cache *cache;
|
|
|
|
if (*this_cache == NULL)
|
|
*this_cache = csky_make_stub_cache (this_frame);
|
|
cache = (struct csky_unwind_cache *) *this_cache;
|
|
|
|
/* Our frame ID for a stub frame is the current SP and LR. */
|
|
*this_id = frame_id_build (cache->prev_sp, get_frame_pc (this_frame));
|
|
}
|
|
|
|
static struct value *
|
|
csky_stub_prev_register (struct frame_info *this_frame,
|
|
void **this_cache,
|
|
int prev_regnum)
|
|
{
|
|
struct csky_unwind_cache *cache;
|
|
|
|
if (*this_cache == NULL)
|
|
*this_cache = csky_make_stub_cache (this_frame);
|
|
cache = (struct csky_unwind_cache *) *this_cache;
|
|
|
|
/* If we are asked to unwind the PC, then return the LR. */
|
|
if (prev_regnum == CSKY_PC_REGNUM)
|
|
{
|
|
CORE_ADDR lr;
|
|
|
|
lr = frame_unwind_register_unsigned (this_frame, CSKY_LR_REGNUM);
|
|
return frame_unwind_got_constant (this_frame, prev_regnum, lr);
|
|
}
|
|
|
|
if (prev_regnum == CSKY_SP_REGNUM)
|
|
return frame_unwind_got_constant (this_frame, prev_regnum, cache->prev_sp);
|
|
|
|
return trad_frame_get_prev_register (this_frame, cache->saved_regs,
|
|
prev_regnum);
|
|
}
|
|
|
|
static frame_unwind csky_stub_unwind = {
|
|
"csky stub",
|
|
NORMAL_FRAME,
|
|
default_frame_unwind_stop_reason,
|
|
csky_stub_this_id,
|
|
csky_stub_prev_register,
|
|
NULL,
|
|
csky_stub_unwind_sniffer
|
|
};
|
|
|
|
/* Implement the this_base, this_locals, and this_args hooks
|
|
for the normal unwinder. */
|
|
|
|
static CORE_ADDR
|
|
csky_frame_base_address (struct frame_info *this_frame, void **this_cache)
|
|
{
|
|
struct csky_unwind_cache *cache;
|
|
|
|
if (*this_cache == NULL)
|
|
*this_cache = csky_frame_unwind_cache (this_frame);
|
|
cache = (struct csky_unwind_cache *) *this_cache;
|
|
|
|
return cache->prev_sp - cache->framesize;
|
|
}
|
|
|
|
static const struct frame_base csky_frame_base = {
|
|
&csky_unwind_cache,
|
|
csky_frame_base_address,
|
|
csky_frame_base_address,
|
|
csky_frame_base_address
|
|
};
|
|
|
|
/* Initialize register access method. */
|
|
|
|
static void
|
|
csky_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum,
|
|
struct dwarf2_frame_state_reg *reg,
|
|
struct frame_info *this_frame)
|
|
{
|
|
if (regnum == gdbarch_pc_regnum (gdbarch))
|
|
reg->how = DWARF2_FRAME_REG_RA;
|
|
else if (regnum == gdbarch_sp_regnum (gdbarch))
|
|
reg->how = DWARF2_FRAME_REG_CFA;
|
|
}
|
|
|
|
/* Create csky register groups. */
|
|
|
|
static void
|
|
csky_init_reggroup ()
|
|
{
|
|
cr_reggroup = reggroup_new ("cr", USER_REGGROUP);
|
|
fr_reggroup = reggroup_new ("fr", USER_REGGROUP);
|
|
vr_reggroup = reggroup_new ("vr", USER_REGGROUP);
|
|
mmu_reggroup = reggroup_new ("mmu", USER_REGGROUP);
|
|
prof_reggroup = reggroup_new ("profiling", USER_REGGROUP);
|
|
}
|
|
|
|
/* Add register groups into reggroup list. */
|
|
|
|
static void
|
|
csky_add_reggroups (struct gdbarch *gdbarch)
|
|
{
|
|
reggroup_add (gdbarch, cr_reggroup);
|
|
reggroup_add (gdbarch, fr_reggroup);
|
|
reggroup_add (gdbarch, vr_reggroup);
|
|
reggroup_add (gdbarch, mmu_reggroup);
|
|
reggroup_add (gdbarch, prof_reggroup);
|
|
}
|
|
|
|
/* Return the groups that a CSKY register can be categorised into. */
|
|
|
|
static int
|
|
csky_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
|
|
const struct reggroup *reggroup)
|
|
{
|
|
int raw_p;
|
|
|
|
if (gdbarch_register_name (gdbarch, regnum) == NULL
|
|
|| gdbarch_register_name (gdbarch, regnum)[0] == '\0')
|
|
return 0;
|
|
|
|
if (reggroup == all_reggroup)
|
|
return 1;
|
|
|
|
raw_p = regnum < gdbarch_num_regs (gdbarch);
|
|
if (reggroup == save_reggroup || reggroup == restore_reggroup)
|
|
return raw_p;
|
|
|
|
if (((regnum >= CSKY_R0_REGNUM) && (regnum <= CSKY_R0_REGNUM + 31))
|
|
&& (reggroup == general_reggroup))
|
|
return 1;
|
|
|
|
if (((regnum == CSKY_PC_REGNUM)
|
|
|| ((regnum >= CSKY_CR0_REGNUM)
|
|
&& (regnum <= CSKY_CR0_REGNUM + 30)))
|
|
&& (reggroup == cr_reggroup))
|
|
return 2;
|
|
|
|
if ((((regnum >= CSKY_VR0_REGNUM) && (regnum <= CSKY_VR0_REGNUM + 15))
|
|
|| ((regnum >= CSKY_FCR_REGNUM)
|
|
&& (regnum <= CSKY_FCR_REGNUM + 2)))
|
|
&& (reggroup == vr_reggroup))
|
|
return 3;
|
|
|
|
if (((regnum >= CSKY_MMU_REGNUM) && (regnum <= CSKY_MMU_REGNUM + 8))
|
|
&& (reggroup == mmu_reggroup))
|
|
return 4;
|
|
|
|
if (((regnum >= CSKY_PROFCR_REGNUM)
|
|
&& (regnum <= CSKY_PROFCR_REGNUM + 48))
|
|
&& (reggroup == prof_reggroup))
|
|
return 5;
|
|
|
|
if ((((regnum >= CSKY_FR0_REGNUM) && (regnum <= CSKY_FR0_REGNUM + 15))
|
|
|| ((regnum >= CSKY_FCR_REGNUM) && (regnum <= CSKY_FCR_REGNUM + 2)))
|
|
&& (reggroup == fr_reggroup))
|
|
return 6;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Implement the dwarf2_reg_to_regnum gdbarch method. */
|
|
|
|
static int
|
|
csky_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int dw_reg)
|
|
{
|
|
/* For GPRs. */
|
|
if (dw_reg >= CSKY_R0_REGNUM && dw_reg <= (CSKY_R0_REGNUM + 31))
|
|
return dw_reg;
|
|
|
|
/* For Hi, Lo, PC. */
|
|
if ((dw_reg == CSKY_HI_REGNUM) || (dw_reg == CSKY_LO_REGNUM)
|
|
|| (dw_reg == CSKY_PC_REGNUM))
|
|
return dw_reg;
|
|
|
|
/* For Float and Vector pseudo registers. */
|
|
if ((dw_reg >= FV_PSEUDO_REGNO_FIRST) && (dw_reg <= FV_PSEUDO_REGNO_LAST))
|
|
{
|
|
char name_buf[4];
|
|
|
|
xsnprintf (name_buf, sizeof (name_buf), "s%d",
|
|
dw_reg - FV_PSEUDO_REGNO_FIRST);
|
|
return user_reg_map_name_to_regnum (gdbarch, name_buf,
|
|
strlen (name_buf));
|
|
}
|
|
|
|
/* Others, unknown. */
|
|
return -1;
|
|
}
|
|
|
|
/* Override interface for command: info register. */
|
|
|
|
static void
|
|
csky_print_registers_info (struct gdbarch *gdbarch, struct ui_file *file,
|
|
struct frame_info *frame, int regnum, int all)
|
|
{
|
|
/* Call default print_registers_info function. */
|
|
default_print_registers_info (gdbarch, file, frame, regnum, all);
|
|
|
|
/* For command: info register. */
|
|
if (regnum == -1 && all == 0)
|
|
{
|
|
default_print_registers_info (gdbarch, file, frame,
|
|
CSKY_PC_REGNUM, 0);
|
|
default_print_registers_info (gdbarch, file, frame,
|
|
CSKY_EPC_REGNUM, 0);
|
|
default_print_registers_info (gdbarch, file, frame,
|
|
CSKY_CR0_REGNUM, 0);
|
|
default_print_registers_info (gdbarch, file, frame,
|
|
CSKY_EPSR_REGNUM, 0);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Check whether xml has discribled the essential regs. */
|
|
|
|
static int
|
|
csky_essential_reg_check (const struct csky_supported_tdesc_register *reg)
|
|
{
|
|
if ((strcmp (reg->name , "pc") == 0)
|
|
&& (reg->num == CSKY_PC_REGNUM))
|
|
return CSKY_TDESC_REGS_PC_NUMBERED;
|
|
else if ((strcmp (reg->name , "r14") == 0)
|
|
&& (reg->num == CSKY_SP_REGNUM))
|
|
return CSKY_TDESC_REGS_SP_NUMBERED;
|
|
else if ((strcmp (reg->name , "r15") == 0)
|
|
&& (reg->num == CSKY_LR_REGNUM))
|
|
return CSKY_TDESC_REGS_LR_NUMBERED;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* Check whether xml has discribled the fr0~fr15 regs. */
|
|
|
|
static int
|
|
csky_fr0_fr15_reg_check (const struct csky_supported_tdesc_register *reg) {
|
|
int i = 0;
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
if ((strcmp (reg->name, csky_supported_fpu_regs[i].name) == 0)
|
|
&& (csky_supported_fpu_regs[i].num == reg->num))
|
|
return (1 << i);
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
/* Check whether xml has discribled the fr16~fr31 regs. */
|
|
|
|
static int
|
|
csky_fr16_fr31_reg_check (const struct csky_supported_tdesc_register *reg) {
|
|
int i = 0;
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
if ((strcmp (reg->name, csky_supported_fpu_regs[i + 16].name) == 0)
|
|
&& (csky_supported_fpu_regs[i + 16].num == reg->num))
|
|
return (1 << i);
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
/* Check whether xml has discribled the vr0~vr15 regs. */
|
|
|
|
static int
|
|
csky_vr0_vr15_reg_check (const struct csky_supported_tdesc_register *reg) {
|
|
int i = 0;
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
if ((strcmp (reg->name, csky_supported_fpu_regs[i + 32].name) == 0)
|
|
&& (csky_supported_fpu_regs[i + 32].num == reg->num))
|
|
return (1 << i);
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
/* Return pseudo reg's name. */
|
|
|
|
static const char *
|
|
csky_pseudo_register_name (struct gdbarch *gdbarch, int regno)
|
|
{
|
|
int num_regs = gdbarch_num_regs (gdbarch);
|
|
csky_gdbarch_tdep *tdep
|
|
= gdbarch_tdep<csky_gdbarch_tdep> (gdbarch);
|
|
|
|
regno -= num_regs;
|
|
|
|
if (tdep->fv_pseudo_registers_count)
|
|
{
|
|
static const char *const fv_pseudo_names[] = {
|
|
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
|
|
"s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15",
|
|
"s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23",
|
|
"s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31",
|
|
"s32", "s33", "s34", "s35", "s36", "s37", "s38", "s39",
|
|
"s40", "s41", "s42", "s43", "s44", "s45", "s46", "s47",
|
|
"s48", "s49", "s50", "s51", "s52", "s53", "s54", "s55",
|
|
"s56", "s57", "s58", "s59", "s60", "s61", "s62", "s63",
|
|
"s64", "s65", "s66", "s67", "s68", "s69", "s70", "s71",
|
|
"s72", "s73", "s74", "s75", "s76", "s77", "s78", "s79",
|
|
"s80", "s81", "s82", "s83", "s84", "s85", "s86", "s87",
|
|
"s88", "s89", "s90", "s91", "s92", "s93", "s94", "s95",
|
|
"s96", "s97", "s98", "s99", "s100", "s101", "s102", "s103",
|
|
"s104", "s105", "s106", "s107", "s108", "s109", "s110", "s111",
|
|
"s112", "s113", "s114", "s115", "s116", "s117", "s118", "s119",
|
|
"s120", "s121", "s122", "s123", "s124", "s125", "s126", "s127",
|
|
};
|
|
|
|
if (regno < tdep->fv_pseudo_registers_count)
|
|
{
|
|
if ((regno < 64) && ((regno % 4) >= 2) && !tdep->has_vr0)
|
|
return NULL;
|
|
else if ((regno >= 64) && ((regno % 4) >= 2))
|
|
return NULL;
|
|
else
|
|
return fv_pseudo_names[regno];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Read for csky pseudo regs. */
|
|
|
|
static enum register_status
|
|
csky_pseudo_register_read (struct gdbarch *gdbarch,
|
|
struct readable_regcache *regcache,
|
|
int regnum, gdb_byte *buf)
|
|
{
|
|
int num_regs = gdbarch_num_regs (gdbarch);
|
|
csky_gdbarch_tdep *tdep
|
|
= gdbarch_tdep<csky_gdbarch_tdep> (gdbarch);
|
|
|
|
regnum -= num_regs;
|
|
|
|
if (regnum < tdep->fv_pseudo_registers_count)
|
|
{
|
|
enum register_status status;
|
|
int gdb_regnum = 0;
|
|
int offset = 0;
|
|
gdb_byte reg_buf[16];
|
|
|
|
/* Ensure getting s0~s63 from vrx if tdep->has_vr0 is ture. */
|
|
if (tdep->has_vr0)
|
|
{
|
|
if (regnum < 64)
|
|
{
|
|
gdb_regnum = CSKY_VR0_REGNUM + (regnum / 4);
|
|
offset = (regnum % 4) * 4;
|
|
}
|
|
else
|
|
{
|
|
gdb_regnum = CSKY_FR16_REGNUM + ((regnum - 64) / 4);
|
|
if ((regnum % 4) >= 2)
|
|
return REG_UNAVAILABLE;
|
|
offset = (regnum % 2) * 4;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gdb_regnum = CSKY_FR0_REGNUM + (regnum / 4);
|
|
if ((regnum % 4) >= 2)
|
|
return REG_UNAVAILABLE;
|
|
offset = (regnum % 2) * 4;
|
|
}
|
|
|
|
status = regcache->raw_read (gdb_regnum, reg_buf);
|
|
if (status == REG_VALID)
|
|
memcpy (buf, reg_buf + offset, 4);
|
|
return status;
|
|
}
|
|
|
|
return REG_UNKNOWN;
|
|
}
|
|
|
|
/* Write for csky pseudo regs. */
|
|
|
|
static void
|
|
csky_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
|
|
int regnum, const gdb_byte *buf)
|
|
{
|
|
int num_regs = gdbarch_num_regs (gdbarch);
|
|
csky_gdbarch_tdep *tdep
|
|
= gdbarch_tdep<csky_gdbarch_tdep> (gdbarch);
|
|
|
|
regnum -= num_regs;
|
|
|
|
if (regnum < tdep->fv_pseudo_registers_count)
|
|
{
|
|
gdb_byte reg_buf[16];
|
|
int gdb_regnum = 0;
|
|
int offset = 0;
|
|
|
|
if (tdep->has_vr0)
|
|
{
|
|
if (regnum < 64)
|
|
{
|
|
gdb_regnum = CSKY_VR0_REGNUM + (regnum / 4);
|
|
offset = (regnum % 4) * 4;
|
|
}
|
|
else
|
|
{
|
|
gdb_regnum = CSKY_FR16_REGNUM + ((regnum - 64) / 4);
|
|
if ((regnum % 4) >= 2)
|
|
return;
|
|
offset = (regnum % 2) * 4;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gdb_regnum = CSKY_FR0_REGNUM + (regnum / 4);
|
|
if ((regnum % 4) >= 2)
|
|
return;
|
|
offset = (regnum % 2) * 4;
|
|
}
|
|
|
|
regcache->raw_read (gdb_regnum, reg_buf);
|
|
memcpy (reg_buf + offset, buf, 4);
|
|
regcache->raw_write (gdb_regnum, reg_buf);
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* 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 at program startup, when reading a core file, and when
|
|
reading a binary file. */
|
|
|
|
static struct gdbarch *
|
|
csky_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
|
|
{
|
|
struct gdbarch *gdbarch;
|
|
/* Analyze info.abfd. */
|
|
unsigned int fpu_abi = 0;
|
|
unsigned int vdsp_version = 0;
|
|
unsigned int fpu_hardfp = 0;
|
|
/* Analyze info.target_desc */
|
|
int num_regs = 0;
|
|
int has_fr0 = 0;
|
|
int has_fr16 = 0;
|
|
int has_vr0 = 0;
|
|
tdesc_arch_data_up tdesc_data;
|
|
|
|
if (tdesc_has_registers (info.target_desc))
|
|
{
|
|
int valid_p = 0;
|
|
int numbered = 0;
|
|
int index = 0;
|
|
int i = 0;
|
|
int feature_names_count = ARRAY_SIZE (csky_supported_tdesc_feature_names);
|
|
int support_tdesc_regs_count
|
|
= csky_get_supported_tdesc_registers_count();
|
|
const struct csky_supported_tdesc_register *tdesc_reg;
|
|
const struct tdesc_feature *feature;
|
|
|
|
tdesc_data = tdesc_data_alloc ();
|
|
for (index = 0; index < feature_names_count; index ++)
|
|
{
|
|
feature = tdesc_find_feature (info.target_desc,
|
|
csky_supported_tdesc_feature_names[index]);
|
|
if (feature != NULL)
|
|
{
|
|
for (i = 0; i < support_tdesc_regs_count; i++)
|
|
{
|
|
tdesc_reg = csky_get_supported_register_by_index (i);
|
|
if (!tdesc_reg)
|
|
break;
|
|
numbered = tdesc_numbered_register (feature, tdesc_data.get(),
|
|
tdesc_reg->num,
|
|
tdesc_reg->name);
|
|
if (numbered) {
|
|
valid_p |= csky_essential_reg_check (tdesc_reg);
|
|
has_fr0 |= csky_fr0_fr15_reg_check (tdesc_reg);
|
|
has_fr16 |= csky_fr16_fr31_reg_check (tdesc_reg);
|
|
has_vr0 |= csky_vr0_vr15_reg_check (tdesc_reg);
|
|
if (num_regs < tdesc_reg->num)
|
|
num_regs = tdesc_reg->num;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (valid_p != CSKY_TDESC_REGS_ESSENTIAL_VALUE)
|
|
return NULL;
|
|
}
|
|
|
|
/* When the type of bfd file is srec(or any files are not elf),
|
|
the E_FLAGS will be not credible. */
|
|
if (info.abfd != NULL && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour)
|
|
{
|
|
/* Get FPU, VDSP build options. */
|
|
fpu_abi = bfd_elf_get_obj_attr_int (info.abfd,
|
|
OBJ_ATTR_PROC,
|
|
Tag_CSKY_FPU_ABI);
|
|
vdsp_version = bfd_elf_get_obj_attr_int (info.abfd,
|
|
OBJ_ATTR_PROC,
|
|
Tag_CSKY_VDSP_VERSION);
|
|
fpu_hardfp = bfd_elf_get_obj_attr_int (info.abfd,
|
|
OBJ_ATTR_PROC,
|
|
Tag_CSKY_FPU_HARDFP);
|
|
}
|
|
|
|
/* Find a candidate among the list of pre-declared architectures. */
|
|
for (arches = gdbarch_list_lookup_by_info (arches, &info);
|
|
arches != NULL;
|
|
arches = gdbarch_list_lookup_by_info (arches->next, &info))
|
|
{
|
|
csky_gdbarch_tdep *tdep
|
|
= gdbarch_tdep<csky_gdbarch_tdep> (arches->gdbarch);
|
|
if (fpu_abi != tdep->fpu_abi)
|
|
continue;
|
|
if (vdsp_version != tdep->vdsp_version)
|
|
continue;
|
|
if (fpu_hardfp != tdep->fpu_hardfp)
|
|
continue;
|
|
|
|
/* Found a match. */
|
|
return arches->gdbarch;
|
|
}
|
|
|
|
/* None found, create a new architecture from the information
|
|
provided. */
|
|
csky_gdbarch_tdep *tdep = new csky_gdbarch_tdep;
|
|
gdbarch = gdbarch_alloc (&info, tdep);
|
|
tdep->fpu_abi = fpu_abi;
|
|
tdep->vdsp_version = vdsp_version;
|
|
tdep->fpu_hardfp = fpu_hardfp;
|
|
|
|
if (tdesc_data != NULL)
|
|
{
|
|
if ((has_vr0 == CSKY_FULL16_ONEHOT_VALUE)
|
|
&& (has_fr16 == CSKY_FULL16_ONEHOT_VALUE))
|
|
{
|
|
tdep->has_vr0 = 1;
|
|
tdep->fv_pseudo_registers_count = 128;
|
|
}
|
|
else if ((has_vr0 == CSKY_FULL16_ONEHOT_VALUE)
|
|
&& (has_fr16 != CSKY_FULL16_ONEHOT_VALUE))
|
|
{
|
|
tdep->has_vr0 = 1;
|
|
tdep->fv_pseudo_registers_count = 64;
|
|
}
|
|
else if ((has_fr0 == CSKY_FULL16_ONEHOT_VALUE)
|
|
&& (has_vr0 != CSKY_FULL16_ONEHOT_VALUE))
|
|
{
|
|
tdep->has_vr0 = 0;
|
|
tdep->fv_pseudo_registers_count = 64;
|
|
}
|
|
else
|
|
{
|
|
tdep->has_vr0 = 0;
|
|
tdep->fv_pseudo_registers_count = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tdep->has_vr0 = 1;
|
|
tdep->fv_pseudo_registers_count = 64;
|
|
}
|
|
|
|
/* Target data types. */
|
|
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);
|
|
|
|
/* Information about the target architecture. */
|
|
set_gdbarch_return_value (gdbarch, csky_return_value);
|
|
set_gdbarch_breakpoint_kind_from_pc (gdbarch, csky_breakpoint_kind_from_pc);
|
|
set_gdbarch_sw_breakpoint_from_kind (gdbarch, csky_sw_breakpoint_from_kind);
|
|
|
|
/* Register architecture. */
|
|
set_gdbarch_num_regs (gdbarch, CSKY_NUM_REGS);
|
|
set_gdbarch_pc_regnum (gdbarch, CSKY_PC_REGNUM);
|
|
set_gdbarch_sp_regnum (gdbarch, CSKY_SP_REGNUM);
|
|
set_gdbarch_register_name (gdbarch, csky_register_name);
|
|
set_gdbarch_register_type (gdbarch, csky_register_type);
|
|
set_gdbarch_read_pc (gdbarch, csky_read_pc);
|
|
set_gdbarch_write_pc (gdbarch, csky_write_pc);
|
|
set_gdbarch_print_registers_info (gdbarch, csky_print_registers_info);
|
|
csky_add_reggroups (gdbarch);
|
|
set_gdbarch_register_reggroup_p (gdbarch, csky_register_reggroup_p);
|
|
set_gdbarch_stab_reg_to_regnum (gdbarch, csky_dwarf_reg_to_regnum);
|
|
set_gdbarch_dwarf2_reg_to_regnum (gdbarch, csky_dwarf_reg_to_regnum);
|
|
dwarf2_frame_set_init_reg (gdbarch, csky_dwarf2_frame_init_reg);
|
|
|
|
/* Functions to analyze frames. */
|
|
frame_base_set_default (gdbarch, &csky_frame_base);
|
|
set_gdbarch_skip_prologue (gdbarch, csky_skip_prologue);
|
|
set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
|
|
set_gdbarch_frame_align (gdbarch, csky_frame_align);
|
|
set_gdbarch_stack_frame_destroyed_p (gdbarch, csky_stack_frame_destroyed_p);
|
|
|
|
/* Functions handling dummy frames. */
|
|
set_gdbarch_push_dummy_call (gdbarch, csky_push_dummy_call);
|
|
|
|
/* Frame unwinders. Use DWARF debug info if available,
|
|
otherwise use our own unwinder. */
|
|
dwarf2_append_unwinders (gdbarch);
|
|
frame_unwind_append_unwinder (gdbarch, &csky_stub_unwind);
|
|
frame_unwind_append_unwinder (gdbarch, &csky_unwind_cache);
|
|
|
|
/* Breakpoints. */
|
|
set_gdbarch_memory_insert_breakpoint (gdbarch,
|
|
csky_memory_insert_breakpoint);
|
|
set_gdbarch_memory_remove_breakpoint (gdbarch,
|
|
csky_memory_remove_breakpoint);
|
|
|
|
/* Hook in ABI-specific overrides, if they have been registered. */
|
|
gdbarch_init_osabi (info, gdbarch);
|
|
|
|
/* Support simple overlay manager. */
|
|
set_gdbarch_overlay_update (gdbarch, simple_overlay_update);
|
|
set_gdbarch_char_signed (gdbarch, 0);
|
|
|
|
if (tdesc_data != nullptr)
|
|
{
|
|
set_gdbarch_num_regs (gdbarch, (num_regs + 1));
|
|
tdesc_use_registers (gdbarch, info.target_desc, std::move (tdesc_data));
|
|
set_gdbarch_register_type (gdbarch, csky_register_type);
|
|
}
|
|
|
|
if (tdep->fv_pseudo_registers_count)
|
|
{
|
|
set_gdbarch_num_pseudo_regs (gdbarch,
|
|
tdep->fv_pseudo_registers_count);
|
|
set_gdbarch_pseudo_register_read (gdbarch,
|
|
csky_pseudo_register_read);
|
|
set_gdbarch_pseudo_register_write (gdbarch,
|
|
csky_pseudo_register_write);
|
|
set_tdesc_pseudo_register_name (gdbarch, csky_pseudo_register_name);
|
|
}
|
|
|
|
return gdbarch;
|
|
}
|
|
|
|
void _initialize_csky_tdep ();
|
|
void
|
|
_initialize_csky_tdep ()
|
|
{
|
|
|
|
register_gdbarch_init (bfd_arch_csky, csky_gdbarch_init);
|
|
|
|
csky_init_reggroup ();
|
|
|
|
/* Allow debugging this file's internals. */
|
|
add_setshow_boolean_cmd ("csky", class_maintenance, &csky_debug,
|
|
_("Set C-Sky debugging."),
|
|
_("Show C-Sky debugging."),
|
|
_("When on, C-Sky specific debugging is enabled."),
|
|
NULL,
|
|
NULL,
|
|
&setdebuglist, &showdebuglist);
|
|
}
|