/* Target-dependent code for the RISC-V architecture, for GDB.
Copyright (C) 2018 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#include "defs.h"
#include "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-bfd.h"
#include "symcat.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 "gdbsupport/common-defs.h"
#include "cli/cli-decode.h"
#include "observable.h"
#include "target-float.h"
#include "loongarch-tdep.h"
#include "arch/loongarch.h"
#include "record.h"
#include "record-full.h"
#include "arch/loongarch-insn.h"
/* Description The location of saved registers in this buffer
(in particular the PC to use after longjmp is called) varies
depending on the ABI or the C Library. */
#define LOONGARCH_JB_PC 0
#define TYPE_CODE(t) (t->code ())
#define TYPE_TAG_NAME(t) (TYPE_MAIN_TYPE(t)->name)
#define TYPE_NFIELDS(t) (t->num_fields ())
#define TYPE_NAME(t) (t->name ())
static int
loongarch_rlen (struct gdbarch *gdbarch)
{
switch (gdbarch_tdep (gdbarch)->ef_abi)
{
case EF_LARCH_ABI_LP64:
case EF_LARCH_ABI_XLP32:
return 64;
case EF_LARCH_ABI_LP32:
return 32;
default:
gdb_assert_not_reached ("unknown ABI");
}
return 0;
}
static insn_t
loongarch_fetch_instruction (CORE_ADDR addr, int *errp)
{
/* 本意是传入足够的信息得到指令长度,目前不是变长指令,无所谓了 */
size_t insnlen = loongarch_insn_length (0);
gdb_byte buf[insnlen];
int err;
ULONGEST ret;
err = target_read_memory (addr, buf, insnlen);
if (errp != NULL)
*errp = err;
if (err != 0)
{
if (errp == NULL)
memory_error (TARGET_XFER_E_IO, addr);
return 0;
}
ret = extract_unsigned_integer (buf, insnlen, BFD_ENDIAN_LITTLE);
return ret;
}
static int
loongarch_insn_is_branch_and_must_branch (insn_t insn)
{
if ((insn & 0xfc000000) == 0x4c000000 /* jirl r0:5,r5:5,s10:16<<2 */
|| (insn & 0xfc000000) == 0x50000000 /* b sb0:10|10:16<<2 */
|| (insn & 0xfc000000) == 0x54000000 /* bl sb0:10|10:16<<2 */
|| (insn & 0xfc0003e0) == 0x48000200 /* jiscr0 s0:5|10:16<<2 */
|| (insn & 0xfc0003e0) == 0x48000300) /* jiscr1 s0:5|10:16<<2 */
return 1;
return 0;
}
static int
loongarch_insn_is_branch (insn_t insn)
{
if (loongarch_insn_is_branch_and_must_branch (insn)
|| (insn & 0xfc000000) == 0x40000000 /* beqz r5:5,sb0:5|10:16<<2 */
|| (insn & 0xfc000000) == 0x44000000 /* bnez r5:5,sb0:5|10:16<<2 */
|| (insn & 0xfc000300) == 0x48000000 /* bceqz c5:3,sb0:5|10:16<<2 */
|| (insn & 0xfc000300) == 0x48000100 /* bcnez c5:3,sb0:5|10:16<<2 */
|| (insn & 0xfc000000) == 0x58000000 /* beq r5:5,r0:5,sb10:16<<2 */
|| (insn & 0xfc000000) == 0x5c000000 /* bne r5:5,r0:5,sb10:16<<2 */
|| (insn & 0xfc000000) == 0x60000000 /* blt r5:5,r0:5,sb10:16<<2 */
|| (insn & 0xfc000000) == 0x64000000 /* bge r5:5,r0:5,sb10:16<<2 */
|| (insn & 0xfc000000) == 0x68000000 /* bltu r5:5,r0:5,sb10:16<<2 */
|| (insn & 0xfc000000) == 0x6c000000) /* bgeu r5:5,r0:5,sb10:16<<2 */
return 1;
return 0;
}
static CORE_ADDR
loongarch_next_pc_if_branch (struct regcache *regcache, CORE_ADDR cur_pc, insn_t insn)
{
struct gdbarch *gdbarch = regcache->arch ();
auto regs = &gdbarch_tdep (gdbarch)->regs;
CORE_ADDR next_pc;
if ((insn & 0xfc000000) == 0x40000000 /* beqz r5:5,sb0:5|10:16<<2 */
|| (insn & 0xfc000000) == 0x44000000 /* bnez r5:5,sb0:5|10:16<<2 */
|| (insn & 0xfc000300) == 0x48000000 /* bceqz c5:3,sb0:5|10:16<<2 */
|| (insn & 0xfc000300) == 0x48000100) /* bcnez c5:3,sb0:5|10:16<<2 */
next_pc = cur_pc + loongarch_decode_imm ("0:5|10:16<<2", insn, 1);
else if ((insn & 0xfc0003e0) == 0x48000200) /* jiscr0 s0:5|10:16<<2 */
{
gdb_assert (0 <= regs->scr);
next_pc = regcache_raw_get_signed (regcache, regs->scr)
+ loongarch_decode_imm ("0:5|10:16<<2", insn, 1);
}
else if ((insn & 0xfc0003e0) == 0x48000300) /* jiscr1 s0:5|10:16<<2 */
{
gdb_assert (0 <= regs->scr);
next_pc = regcache_raw_get_signed (regcache, regs->scr + 1)
+ loongarch_decode_imm ("0:5|10:16<<2", insn, 1);
}
else if ((insn & 0xfc000000) == 0x4c000000) /* jirl r0:5,r5:5,s10:16<<2 */
next_pc = regcache_raw_get_signed
(regcache, regs->r + loongarch_decode_imm ("5:5", insn, 0))
+ loongarch_decode_imm ("10:16<<2", insn, 1);
else if ((insn & 0xfc000000) == 0x50000000 /* b sb0:10|10:16<<2 */
|| (insn & 0xfc000000) == 0x54000000)/* bl sb0:10|10:16<<2 */
next_pc = cur_pc + loongarch_decode_imm ("0:10|10:16<<2", insn, 1);
else if ((insn & 0xfc000000) == 0x58000000 /* beq r5:5,r0:5,sb10:16<<2 */
|| (insn & 0xfc000000) == 0x5c000000 /* bne r5:5,r0:5,sb10:16<<2 */
|| (insn & 0xfc000000) == 0x60000000 /* blt r5:5,r0:5,sb10:16<<2 */
|| (insn & 0xfc000000) == 0x64000000 /* bge r5:5,r0:5,sb10:16<<2 */
|| (insn & 0xfc000000) == 0x68000000 /* bltu r5:5,r0:5,sb10:16<<2 */
|| (insn & 0xfc000000) == 0x6c000000)/* bgeu r5:5,r0:5,sb10:16<<2 */
next_pc = cur_pc + loongarch_decode_imm ("10:16<<2", insn, 1);
else
gdb_assert_not_reached ("I don't know what branch is this");
return next_pc;
}
/* Checks for an atomic sequence of instructions beginning with a LL/LLD
instruction and ending with a SC/SCD instruction. If such a sequence
is found, attempt to step through it. A breakpoint is placed at the end of
the sequence. */
static std::vector
loongarch_deal_with_atomic_sequence (struct regcache *regcache, CORE_ADDR pc)
{
struct gdbarch *gdbarch = regcache->arch ();
CORE_ADDR next_pc;
std::vector next_pcs;
insn_t insn = loongarch_fetch_instruction (pc, NULL);
size_t insnlen = loongarch_insn_length (insn);
int i, atomic_sequence_length, found_atomic_sequence_endpoint;
/* 这个函数由loongarch_software_single_step调用,在single step时尝试找到原子
指令序列的终点。返回的CORE_ADDR向量似乎意味着控制流的所有可能去处。
如果 return {} 则意味着认为接下来的指令不属于原子操作。 */
if ((insn & 0xff000000) != 0x20000000 /* ll.w */
&& (insn & 0xff000000) != 0x22000000) /* ll.d */
return {};
if (loongarch_debug)
fprintf_unfiltered (gdb_stdlog,
"Single step: PC: %s OK, I found ll\\.[wd] here. It's atomic sequence?\n",
paddress (gdbarch, pc));
atomic_sequence_length = 30; /* Magic. */
found_atomic_sequence_endpoint = 0;
for (pc += insnlen, i = 0; i < atomic_sequence_length; pc += insnlen, i++)
{
insn = loongarch_fetch_instruction (pc, NULL);
insnlen = loongarch_insn_length (insn);
if (loongarch_insn_is_branch_and_must_branch (insn))
{
if (loongarch_debug)
fprintf_unfiltered (gdb_stdlog,
"Single step: PC: %s Must branch here. Treat it normally.\n",
paddress (gdbarch, pc));
break;
}
else if (loongarch_insn_is_branch (insn))
{
next_pc = loongarch_next_pc_if_branch (regcache, pc, insn);
if (loongarch_debug)
fprintf_unfiltered (gdb_stdlog,
"Single step: PC: %s May branch inside and target is %s. Breakpoint there.\n",
paddress (gdbarch, pc), paddress (gdbarch, next_pc));
next_pcs.push_back (next_pc);
}
else if ((insn & 0xff000000) == 0x21000000 /* sc.w */
|| (insn & 0xff000000) == 0x23000000) /* sc.d */
{
found_atomic_sequence_endpoint = 1;
next_pc = pc + insnlen;
if (loongarch_debug)
fprintf_unfiltered (gdb_stdlog,
"Single step: PC: %s I found sc\\.[wd] and atomic sequence ends at here.\n"
"Breakpoint next pc: %s.\n",
paddress (gdbarch, pc), paddress (gdbarch, next_pc));
next_pcs.push_back (next_pc);
break;
}
}
if (!found_atomic_sequence_endpoint)
{
if (loongarch_debug)
fprintf_unfiltered (gdb_stdlog,
"Single step: PC: %s Not ends with sc\\.[wd] in %d insns?\n"
"Treat it as not atomic sequence.\n",
paddress (gdbarch, pc), atomic_sequence_length);
return {};
}
return next_pcs;
}
/* mips_software_single_step() is called just before we want to resume
the inferior, if we want to single-step it but there is no hardware
or kernel single-step support (MIPS on GNU/Linux for example). We find
the target of the coming instruction and breakpoint it. */
std::vector
loongarch_software_single_step (struct regcache *regcache)
{
struct gdbarch *gdbarch = regcache->arch ();
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
CORE_ADDR pc = regcache_read_pc (regcache);
std::vector next_pcs =
loongarch_deal_with_atomic_sequence (regcache, pc);
if (!next_pcs.empty ())
return next_pcs;
insn_t insn = loongarch_fetch_instruction (pc, NULL);
size_t insnlen = loongarch_insn_length (insn);
CORE_ADDR next = pc + insnlen;
if ((insn & 0xffff8000) == 0x002b0000 && tdep->syscall_next_pc)
{
CORE_ADDR syscall_next = tdep->syscall_next_pc (get_current_frame ());
if (syscall_next != -1)
{
if (loongarch_debug)
fprintf_unfiltered (gdb_stdlog,
"PC: %s Syscall found. Next pc is %s.\n",
paddress (gdbarch, pc), paddress (gdbarch, syscall_next));
return {syscall_next};
}
}
if (loongarch_insn_is_branch (insn))
{
CORE_ADDR branch_tgt = loongarch_next_pc_if_branch (regcache, pc, insn);
if (loongarch_debug)
fprintf_unfiltered (gdb_stdlog,
"PC: %s Next pc is %s if branch, %s for non-branch.\n",
paddress (gdbarch, pc), paddress (gdbarch, branch_tgt), paddress (gdbarch, next));
return {next, branch_tgt};
}
else
{
if (loongarch_debug)
fprintf_unfiltered (gdb_stdlog,
"PC: %s Next pc is %s.\n",
paddress (gdbarch, pc), paddress (gdbarch, next));
return {next};
}
}
/* Callback function for user_reg_add. */
static struct value *
value_of_loongarch_user_reg (struct frame_info *frame, const void *baton)
{
return value_of_register ((long long) baton, frame);
}
/* Implement the register_name gdbarch method. */
static const char *
loongarch_register_name (struct gdbarch *gdbarch, int regnum)
{
auto regs = &gdbarch_tdep (gdbarch)->regs;
if (0 <= regs->r
&& regs->r <= regnum
&& regnum < regs->r + 32)
switch (gdbarch_tdep (gdbarch)->ef_abi)
{
case EF_LARCH_ABI_LP64:
return loongarch_r_lp64_name[regnum - regs->r] + 1;
}
else if (0 <= regs->f
&& regs->f <= regnum
&& regnum < regs->f + 32)
switch (gdbarch_tdep (gdbarch)->ef_abi)
{
case EF_LARCH_ABI_LP64:
return loongarch_f_lp64_name[regnum - regs->f] + 1;
}
return tdesc_register_name (gdbarch, regnum);
}
/* Analyze the function prologue from START_PC to LIMIT_PC. Builds
the associated FRAME_CACHE if not null.
Return the address of the first instruction past the prologue. */
static CORE_ADDR
loongarch_scan_prologue (struct gdbarch *gdbarch,
CORE_ADDR start_pc, CORE_ADDR limit_pc,
struct frame_info *this_frame,
struct trad_frame_cache *this_cache)
{
/* 关键问题:我们分析callee的prologue而推断caller的保存寄存器被store在相对于
callee的CFA的偏移量,但在函数执行中途我们不好拿到CFA的值,这是回溯的障碍。
另外,黄沛给出了Power的ABI作参考,callee在维护调用栈时保持栈顶的两个地址
恒为caller的返回地址和栈顶。在这里就不用analyze prologue了。但我们的指令集
看起来还做不到这一点。
在这里,我们约定
a. CFA(canonical frame address from dwarf2)的值为caller调用callee那一瞬间
$sp的值,即See MIPS run中的“$sp on entry”。这是因为分析prologue是按照控制
流走向的,这样使得分析更加自然。
b. 定义frame pointer为函数正文执行期间保持不变的,且存储有和CFA有常数
偏移量的值的寄存器。lp64中的$fp便是这个用途,但考虑到二进制翻译会有寄存器
映射,frame pointer还真不一定是$fp。对于栈帧不变的函数,$sp就是
frame pointer。
那么,
a. 如果有一个$fp($r22)是$sp($r3)附加偏移量得到的。一旦$r22确定,在整个
函数执行期间不会变化。对于栈帧可变的函数这很重要。由此,根据callee的$fp
可以反推进入callee时$sp的值,由此就得到了CFA。
b. 如果没有$fp被确定,那么我们认为在prologue中,$sp被调整后,其在函数执行
期间不会变化,由此我们就知道了CFA。这是栈帧不变的函数。
c. 上述约定尽量在-O0下生效,不过说到底也只是个启发式做法罢了。指令调度会
使prologue没那么工整;shrink-wrapping会弱化prologue的概念;手写汇编更是可
以自由发挥,这使得分析极其困难,我们也点到为止。
那么prologue分析分为两部分,
1. 求出callee的frame pointer,及frame pointer和CFA的偏移量以求出CFA。
2. 求出来the offset based on CFA storing the register of caller */
auto regs = &gdbarch_tdep (gdbarch)->regs;
int rlen_is_64b = (loongarch_rlen (gdbarch) == 64);
CORE_ADDR cur_pc, prologue_end = 0;
insn_t insn;
size_t insnlen;
int sp = regs->sp - regs->r;
int fp = sp;/* frame pointer */
long frame_offset = 0;
int non_prologue_insns = 0;
int cfa_unknown = 0;
/* try to trace li */
int64_t r_value[32] = {0};
int r_value_known[32] = {1, 0};
long r_cfa_offset[32] = {0};
int r_cfa_offset_p[32] = {0};
long f_cfa_offset[32] = {0};
int f_cfa_offset_p[32] = {0};
if (start_pc + 80 < limit_pc)
limit_pc = start_pc + 80;
for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += insnlen)
{
int rd, rj, rk;
int64_t si12, si20, si14;
insn = loongarch_fetch_instruction (cur_pc, NULL);
insnlen = loongarch_insn_length (insn);
rd = loongarch_decode_imm ("0:5", insn, 0);
rj = loongarch_decode_imm ("5:5", insn, 0);
rk = loongarch_decode_imm ("10:5", insn, 0);
si12 = loongarch_decode_imm ("10:12", insn, 1);
si20 = loongarch_decode_imm ("5:20", insn, 1);
si14 = loongarch_decode_imm ("10:14<<2", insn, 1);
if ((((insn & 0xffc00000) == 0x02800000 /* addi.w fp,fp,si12 */
&& !rlen_is_64b)
|| ((insn & 0xffc00000) == 0x02c00000 /* addi.d fp,fp,si12 */
&& rlen_is_64b))
&& rd == fp && rj == fp)
{
if (si12 < 0)
frame_offset -= si12;
else
/* Exit loop if a positive stack adjustment is found, which
usually means that the stack cleanup code in the function
epilogue is reached. */
break;
prologue_end = cur_pc + insnlen;
}
else if ((((insn & 0xffc00000) == 0x29800000 /* st.w rd,fp,si12 */
&& !rlen_is_64b)
|| ((insn & 0xffc00000) == 0x29c00000 /* st.d rd,fp,si12 */
&& rlen_is_64b))
&& rj == fp)
{
if (!r_cfa_offset_p[rd] && !r_value_known[rd])
r_cfa_offset[rd] = si12 - frame_offset, r_cfa_offset_p[rd] = 1;
prologue_end = cur_pc + insnlen;
}
else if ((((insn & 0xff000000) == 0x25000000 /* stptr.w rd,fp,si14 */
&& !rlen_is_64b)
|| ((insn & 0xff000000) == 0x27000000 /* stptr.d rd,fp,si14 */
&& rlen_is_64b))
&& rj == fp)
{
if (!r_cfa_offset_p[rd] && !r_value_known[rd])
r_cfa_offset[rd] = si14 - frame_offset, r_cfa_offset_p[rd] = 1;
prologue_end = cur_pc + insnlen;
}
else if (((insn & 0xffc00000) == 0x2b400000 /* fst.s fd,fp,si12 */
|| (insn & 0xffc00000) == 0x2bc00000) /* fst.d fd,fp,si12 */
&& rj == fp)
{
if (!f_cfa_offset_p[rd])
f_cfa_offset[rd] = si12 - frame_offset, f_cfa_offset_p[rd] = 1;
}
else if ((((insn & 0xffff8000) == 0x00110000 /* sub.w fp,fp,rk */
&& !rlen_is_64b)
|| ((insn & 0xffff8000) == 0x00118000 /* sub.d fp,fp,rk */
&& rlen_is_64b))
&& rd == fp && rj == fp)
{
if (r_value_known[rk])
{
frame_offset += r_value[rk];
prologue_end = cur_pc + insnlen;
}
else
cfa_unknown = 1;
}
else if ((insn & 0xffff8000) == 0x00150000 /* or rd,fp,$r0 */
&& rj == fp && rk == 0)
{
fp = rd;
prologue_end = cur_pc + insnlen;
}
else if ((insn & 0xffc00000) == 0x02800000) /* addi.w rd,rj,si12 */
{
if (r_value_known[rj] && rd != 0)
r_value[rd] = (int32_t) (r_value[rj] + si12), r_value_known[rd] = 1;
}
else if ((insn & 0xffc00000) == 0x03800000) /* ori rd,rj,si12 */
{
if (r_value_known[rj] && rd != 0)
r_value[rd] = r_value[rj] | (si12 & 0xfff) , r_value_known[rd] = 1;
}
else if ((insn & 0xfe000000) == 0x14000000) /* lu12i.w rd,si20 */
{
if (rd != 0)
r_value[rd] = si20 << 12, r_value_known[rd] = 1;
}
else if ((insn & 0xfe000000) == 0x16000000) /* lu32i.d rd,si20 */
{
if (r_value_known[rd] && rd != 0)
r_value[rd] = (r_value[rd] & 0xffffffff) | (si20 << 32)
, r_value_known[rd] = 1;
}
else if ((insn & 0xffc00000) == 0x03000000) /* lu52i.d rd,rj,si12 */
{
if (r_value_known[rj] && rd != 0)
r_value[rd] = (r_value[rj] & 0xfffffffffffff) | (si12 << 52)
, r_value_known[rd] = 1;
}
else if (loongarch_insn_is_branch (insn))
break;/* shrink-wrap or end of prologue in a basic block */
else
non_prologue_insns++;
if (5 < non_prologue_insns) /* 4 INSNs for 'la' and one for some other */
break;
}
if (loongarch_debug)
{
const char *fun_name;
find_pc_partial_function (start_pc, &fun_name, NULL, NULL);
fprintf_unfiltered (gdb_stdlog,
"Prologue Analyze: -- Start -- Callee [%s] %s\n",
fun_name ? fun_name : "", paddress (gdbarch, start_pc));
}
do
{
int i;
CORE_ADDR cfa = -1, ret = -1;
if (!(this_frame && this_cache))
break;
if (!cfa_unknown)
{
try
{
cfa = get_frame_register_signed
(this_frame, regs->r + fp) + frame_offset;
}
catch (const gdb_exception_error &ex)
{
cfa_unknown = 1;
}
if (loongarch_debug)
fprintf_unfiltered (gdb_stdlog,
"Prologue Analyze: CFA is (frame pointer $%s + 0x%lx) = %s\n",
gdbarch_register_name (gdbarch, regs->r + fp),
(long)frame_offset, cfa_unknown ? "" : paddress (gdbarch, cfa));
}
else
if (loongarch_debug)
fprintf_unfiltered (gdb_stdlog,
"Prologue Analyze: Unknown stack frame size, so can't get known CFA\n");
if (r_cfa_offset_p[1] && !cfa_unknown)
{
CORE_ADDR ret_saved = cfa + r_cfa_offset[1];
trad_frame_set_reg_addr
(this_cache, gdbarch_pc_regnum (gdbarch), ret_saved);
if (loongarch_debug)
fprintf_unfiltered (gdb_stdlog,
"Prologue Analyze: Return addr saved in (CFA - 0x%lx) = %s\n",
-r_cfa_offset[1], paddress (gdbarch, ret_saved));
}
else if (r_cfa_offset_p[1] /* && cfa_unknown */)
{
if (loongarch_debug)
fprintf_unfiltered (gdb_stdlog,
"Prologue Analyze: Return addr saved in (CFA - 0x%lx), but CFA is unknown\n",
-r_cfa_offset[1]);
}
else
{
trad_frame_set_reg_realreg
(this_cache, gdbarch_pc_regnum (gdbarch), regs->r + 1);
if (loongarch_debug)
fprintf_unfiltered (gdb_stdlog,
"Prologue Analyze: No found $r1 pushed in stack. Return addr saved in $r1\n");
}
if (cfa_unknown)
{
trad_frame_set_this_base (this_cache, -1);
break;
}
/* 在这里认为callee的CFA是caller的$sp的值。真实情况不一定如此。但在分析
caller的prologue时,
如果发现caller的frame pointer不为$sp,会unwind备份在callee frame中的
frame pointer,此时$sp的值也不重要了;
如果发现caller的frame pointer为$sp,那么$sp的值就是这个CFA */
trad_frame_set_reg_value
(this_cache, gdbarch_sp_regnum (gdbarch), (LONGEST) cfa);
trad_frame_set_this_base (this_cache, cfa);
if (loongarch_debug)
fprintf_unfiltered (gdb_stdlog,
"Prologue Analyze: Where caller's registers saved as follow:\n");
for (i = 0; i < 32; i++)
if (r_cfa_offset_p[i] && i != 1)
{
trad_frame_set_reg_addr
(this_cache, regs->r + i, cfa + r_cfa_offset[i]);
if (loongarch_debug)
fprintf_unfiltered (gdb_stdlog,
"Prologue Analyze: $%s: saved in (CFA - 0x%lx) = %s\n",
gdbarch_register_name (gdbarch, regs->r + i), -r_cfa_offset[i],
paddress (gdbarch, cfa + r_cfa_offset[i]));
}
if (regs->f <= 0)
for (i = 0; i < 32; i++)
{
if (f_cfa_offset_p[i])
trad_frame_set_reg_addr
(this_cache, regs->f + i, cfa + f_cfa_offset[i]);
if (loongarch_debug)
fprintf_unfiltered (gdb_stdlog,
"Prologue Analyze: $%s: saved in (CFA - 0x%lx) = %s\n",
gdbarch_register_name (gdbarch, regs->f + i), -f_cfa_offset[i],
paddress (gdbarch, cfa + f_cfa_offset[i]));
}
}
while (0);
if (loongarch_debug)
fprintf_unfiltered (gdb_stdlog,
"Prologue Analyze: -- End -- %s\n", paddress (gdbarch, cur_pc));
return prologue_end ? prologue_end : cur_pc;
}
/* Implement the loongarch_skip_prologue gdbarch method. */
/* To skip prologues, I use this predicate. Returns either PC itself
if the code at PC does not look like a function prologue; otherwise
returns an address that (if we're lucky) follows the prologue. If
LENIENT, then we must skip everything which is involved in setting
up the frame (it's OK to skip more, just so long as we don't skip
anything which might clobber the registers which are being saved.
We must skip more in the case where part of the prologue is in the
delay slot of a non-prologue instruction). */
static CORE_ADDR
loongarch_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
{
CORE_ADDR limit_pc;
CORE_ADDR func_addr;
/* See if we can determine the end of the prologue via the symbol table.
If so, then return either PC, or the PC after the prologue, whichever
is greater. */
if (find_pc_partial_function (pc, NULL, &func_addr, NULL))
{
CORE_ADDR post_prologue_pc
= skip_prologue_using_sal (gdbarch, func_addr);
if (post_prologue_pc != 0)
return std::max (pc, post_prologue_pc);
}
/* Can't determine prologue from the symbol table, need to examine
instructions. */
/* Find an upper limit on the function prologue using the debug
information. If the debug information could not be used to provide
that bound, then use an arbitrary large number as the upper bound. */
limit_pc = skip_prologue_using_sal (gdbarch, pc);
if (limit_pc == 0)
limit_pc = pc + 100; /* Magic. */
return loongarch_scan_prologue (gdbarch, pc, limit_pc, NULL, NULL);
}
/* Adjust the address downward (direction of stack growth) so that it
is correctly aligned for a new stack frame. */
static CORE_ADDR
loongarch_frame_align (struct gdbarch *gdbarch, CORE_ADDR addr)
{
return align_down (addr, 16);
}
/* Implement the unwind_pc gdbarch method. */
static CORE_ADDR
loongarch_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
{
return frame_unwind_register_signed
(next_frame, gdbarch_pc_regnum (gdbarch));
}
/* Implement the unwind_sp gdbarch method. */
static CORE_ADDR
loongarch_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
{
return frame_unwind_register_signed
(next_frame, gdbarch_sp_regnum (gdbarch));
}
/* Implement the dummy_id gdbarch method. */
static struct frame_id
loongarch_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
{
return frame_id_build
(get_frame_register_signed (this_frame,
gdbarch_sp_regnum (gdbarch)),
get_frame_pc (this_frame));
}
/* Generate, or return the cached frame cache for the RiscV frame
unwinder. */
static struct trad_frame_cache *
loongarch_frame_cache (struct frame_info *this_frame, void **this_cache)
{
struct gdbarch *gdbarch = get_frame_arch (this_frame);
struct trad_frame_cache *cache;
CORE_ADDR pc, start_addr, stack_addr;
if (*this_cache != NULL)
return (struct trad_frame_cache *) *this_cache;
cache = trad_frame_cache_zalloc (this_frame);
*this_cache = cache;
/* 这里的情况是,已知callee中各寄存器的值(包括PC),分析callee的prologue
来推测caller中各寄存器备份在callee栈帧中的位置。 */
/* 我们先拿到callee的PC */
pc = get_frame_address_in_block (this_frame);
if (find_pc_partial_function (pc, NULL, &start_addr, NULL))
{
/* find_pc_partial_function会从debug信息拿到函数开头,如果成功了,那啥也
不说了,开始scan_prologue */
loongarch_scan_prologue (gdbarch, start_addr, pc, this_frame, cache);
stack_addr = trad_frame_get_this_base (cache);
trad_frame_set_id (cache, stack_addr == -1 ?
frame_id_build_unavailable_stack (start_addr) :
frame_id_build (stack_addr, start_addr));
}
else
{
/* 如果失败了,可能有三种情况
1. PC在某个trampoline上。那应该有某个struct tramp_frame的sniffer的匹配
优先级比这个通用的struct frame_unwind高。
2. PC在某个函数中,可能在elf文件中但没debug信息;也可能是自修改代码。不考虑
3. PC非法,反正要么已经发生段错误了;要么迟早发生段错误。不过这种的也要
看情况,比如基本块没设计好,PC飞了,我们根本没任何线索搞backtrace。
我们在这里考虑的情况唯一情况是进行了失败的绝对跳转的函数调用,这样的话
caller的寄存器都在,而caller的地址就在ra中。 */
auto regs = &gdbarch_tdep (gdbarch)->regs;
/* 注意:我们认为caller的$ra是unknown的。原因是,下次unwind时的
find_pc_partial_function失效后(即callee的$ra不在某个带debug信息的
函数内),认为caller的caller的PC为caller的$ra。如果仍然认为caller的$ra
是callee的$ra,这个失效的PC值会让unwind无法停下来了。 */
trad_frame_set_reg_realreg (cache, regs->ra, -2/* TF_REG_UNKNOWN */);
trad_frame_set_reg_realreg
(cache, gdbarch_pc_regnum (gdbarch), regs->ra);
trad_frame_set_id (cache, frame_id_build_unavailable_stack (pc));
}
return cache;
}
/* Implement the this_id callback for RiscV frame unwinder. */
static void
loongarch_frame_this_id (struct frame_info *this_frame,
void **prologue_cache,
struct frame_id *this_id)
{
struct trad_frame_cache *info;
info = loongarch_frame_cache (this_frame, prologue_cache);
trad_frame_get_id (info, this_id);
}
/* Implement the prev_register callback for RiscV frame unwinder. */
static struct value *
loongarch_frame_prev_register (struct frame_info *this_frame,
void **prologue_cache,
int regnum)
{
struct trad_frame_cache *info;
info = loongarch_frame_cache (this_frame, prologue_cache);
return trad_frame_get_register (info, this_frame, regnum);
}
/* Structure defining the RiscV normal frame unwind functions. Since we
are the fallback unwinder (DWARF unwinder is used first), we use the
default frame sniffer, which always accepts the frame. */
static const struct frame_unwind loongarch_frame_unwind =
{
/*.type =*/ NORMAL_FRAME,
/*.stop_reason =*/ default_frame_unwind_stop_reason,
/*.this_id =*/ loongarch_frame_this_id,
/*.prev_register =*/ loongarch_frame_prev_register,
/*.unwind_data =*/ NULL,
/*.sniffer =*/ default_frame_sniffer,
/*.dealloc_cache =*/ NULL,
/*.prev_arch =*/ NULL,
};
struct small_struct_data_t
{
char fixed_member;
char float_member;
bool first_member_is_float;
};
static void
pass_on_reg (struct regcache *regcache, int regno, const gdb_byte *val,
int len)
{
gdb_byte reg[32];
memset (reg, 0, sizeof (reg));
memcpy (reg, val, len);
regcache->cooked_write (regno, reg);
}
static void
get_struct_member (struct type *tp, small_struct_data_t *small_struct)
{
for (int i = 0; i < TYPE_NFIELDS(tp); i++)
{
field fd = tp->field (i);
struct type *t = fd.type;
/* Call check_typedef(TYPE_TARGET_TYPE (TYPE)) on our type to make
sure that, if TYPE is a TYPE_CODE_TYPEDEF, its TYPE is set to
the target type instead of TYPE_CODE_TYPEDEF. */
if (TYPE_CODE(t) == TYPE_CODE_TYPEDEF)
t = check_typedef (TYPE_TARGET_TYPE (t));
switch (TYPE_CODE(t))
{
case TYPE_CODE_STRUCT:
get_struct_member (t, small_struct);
break;
case TYPE_CODE_COMPLEX:
small_struct->float_member += 2;
break;
case TYPE_CODE_FLT:
small_struct->float_member++;
break;
default:
if (small_struct->float_member > 0 && small_struct->fixed_member == 0)
small_struct->first_member_is_float = true;
small_struct->fixed_member++;
break;
}
}
}
/* 为了做mips的汇编级兼容,LP64和n64的传参方法是相同的 */
/* N32/N64 ABI stuff. */
/* Implement the push dummy call gdbarch callback. */
static CORE_ADDR
loongarch_xlp32lp64_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 struct_return, CORE_ADDR struct_addr)
{
const size_t rlen = loongarch_rlen (gdbarch) / 8;
size_t i;
auto regs = &gdbarch_tdep (gdbarch)->regs;
/* 1. We find out the size of space to settle actual args */
size_t Narg_slots = 0;
if (struct_return)
Narg_slots++;
for (i = 0; i < nargs; i++)
{
struct value *arg = args[i];
struct type *arg_type = check_typedef (value_type (arg));
size_t len = TYPE_LENGTH (arg_type);
size_t align = type_align (arg_type);
enum type_code typecode = TYPE_CODE (arg_type);
gdb_assert (0 < align);
if (typecode == TYPE_CODE_COMPLEX && len == 8
&& TYPE_CODE (check_typedef (TYPE_TARGET_TYPE (arg_type)))
== TYPE_CODE_FLT)
/* For _Complex float, sometimes we need two float argument registers
to fit its real and img. */
Narg_slots += 2;
else
{
Narg_slots = align_up (Narg_slots, (align + rlen - 1) / rlen);
Narg_slots += (len + rlen - 1) / rlen;
}
}
/* 2. Set all actual arguments here */
struct type *func_type = check_typedef (value_type (function));
gdb_byte raw_args[Narg_slots * rlen];
int flt_num = 0;
Narg_slots = 0;
if (struct_return)
store_signed_integer (raw_args + Narg_slots * rlen, rlen, BFD_ENDIAN_LITTLE, struct_addr)
, Narg_slots++;
for (i = 0; i < nargs; i++)
{
struct value *arg = args[i];
struct type *arg_type = check_typedef (value_type (arg));
size_t len = TYPE_LENGTH (arg_type);
size_t align = type_align (arg_type);
enum type_code typecode = TYPE_CODE (arg_type);
const gdb_byte *val = value_contents (arg);
int is_var = TYPE_VARARGS (func_type) && TYPE_NFIELDS (func_type) <= i;
small_struct_data_t small_struct = {0, 0, false};
if (((typecode == TYPE_CODE_INT && TYPE_UNSIGNED (arg_type))
|| typecode == TYPE_CODE_ENUM) && len <= rlen)
{
/* For unsigned scalar type in a register */
ULONGEST i = extract_unsigned_integer (val, len, BFD_ENDIAN_LITTLE);
store_unsigned_integer (raw_args + Narg_slots * rlen, rlen, BFD_ENDIAN_LITTLE, i);
Narg_slots++;
}
else if ((typecode == TYPE_CODE_INT || typecode == TYPE_CODE_PTR) && len <= rlen)
{
/* For signed scalar type in a register */
LONGEST i = extract_signed_integer (val, len, BFD_ENDIAN_LITTLE);
store_signed_integer (raw_args + Narg_slots * rlen, rlen, BFD_ENDIAN_LITTLE, i);
Narg_slots++;
}
else if (typecode == TYPE_CODE_FLT)
{
/* long double */
if (len > rlen )
{
if ( Narg_slots >= 8)
Narg_slots = align_up (Narg_slots, (align + rlen - 1) / rlen);
memcpy (raw_args + Narg_slots * rlen, val, rlen);
memcpy (raw_args + (Narg_slots + 1) * rlen, val + rlen, len - rlen);
Narg_slots += 2;
}
/* float、 double */
else
{
if (flt_num < 8)
{
pass_on_reg (regcache, regs->f/* $fa0 */ + flt_num, val, rlen);
flt_num++;
}
else
{
memcpy (raw_args + Narg_slots * rlen, val, len);
Narg_slots += (len + rlen - 1) / rlen;
}
}
}
else if (typecode == TYPE_CODE_STRUCT)
{
get_struct_member (arg_type, &small_struct);
/* It’s passed by reference and are replaced in the argument list with the address.
If there is an available GAR, the reference is passed in the GAR, and passed on
the stack if no GAR is available */
if (len > rlen * 2)
{
/* Address on register, data on stack. */
sp = align_down (sp - len, 16);
write_memory (sp, val, len);
memcpy (raw_args + Narg_slots * rlen, (const gdb_byte *) &sp, rlen);
Narg_slots += 1;
}
else if (rlen < len && len <= 2 * rlen)
{
/* Only fixed-point members, the argument is passed in a pair of available GAR,
with the low-order bits in the lower-numbered GAR and the high-order bits in
the higher-numbered GAR. If only one GAR is available, the low-order bits are
in the GAR and the high-order bits are on the stack, and passed on the stack
if no GAR is available. */
if (small_struct.fixed_member > 0 && small_struct.float_member == 0)
{
memcpy (raw_args + Narg_slots * rlen, val, rlen);
memcpy (raw_args + (Narg_slots + 1) * rlen, val + rlen, len - rlen);
Narg_slots += 2;
}
/* Only floating-point members. */
else if (small_struct.fixed_member == 0 && small_struct.float_member > 0 )
{
/* The structure has one long double member or one double member and two
adjacent float members or 3-4 float members. The argument is passed
in a pair of available GAR, with the low-order bits in the lower-numbered
GAR and the high-order bits in the higher-numbered GAR. If only one
GAR is available, the low-order bits are in the GAR and the high-order
bits are on the stack, and passed on the stack if no GAR is available. */
if (small_struct.float_member == 1 || small_struct.float_member > 2 )
{
memcpy (raw_args + Narg_slots * rlen, val, rlen);
memcpy (raw_args + (Narg_slots + 1) * rlen, val + rlen, len - rlen);
Narg_slots += 2;
}
/* The structure with two double members is passed in a pair of available
FARs. If no a pair of available FARs, it’s passed in GARs. If only one
GAR is available, the low-order bits are in the GAR and the high-order
bits are on the stack, and passed on the stack if no GAR is available,
A structure with one double member and one float member is same. */
else if (small_struct.float_member == 2)
{
if (flt_num < 7)
{
pass_on_reg (regcache, regs->f + flt_num++, val, rlen);
pass_on_reg (regcache, regs->f + flt_num++, val + rlen, len - rlen);
}
else
{
memcpy (raw_args + Narg_slots * rlen, val, rlen);
memcpy (raw_args + (Narg_slots + 1) * rlen, val + rlen, len - rlen);
Narg_slots += 2;
}
}
}
/* Both fixed-point and floating-point members. */
else if (small_struct.fixed_member > 0 && small_struct.float_member > 0 )
{
/* The structure has one floating-point member and one fixed-point member.
If one FAR and one GAR are available, the floating-point member of the
structure is passed in the FAR, and the integer member of the structure
is passed in the GAR; If no floating-point registers but two GARs are
available, it’s passed in the two GARs; If only one GAR is available,
the low-order bits are in the GAR and the high-order bits are on the
stack; And it’s passed on the stack if no GAR is available. */
if (small_struct.fixed_member == 1 && small_struct.float_member == 1)
{
if (flt_num < 8 && Narg_slots < 8)
{
if (small_struct.first_member_is_float)
{
pass_on_reg (regcache, regs->f + flt_num++, val, rlen);
memcpy (raw_args + Narg_slots * rlen, val + rlen, len - rlen);
Narg_slots ++;
}
else
{
memcpy (raw_args + Narg_slots * rlen, val, rlen);
Narg_slots ++;
pass_on_reg (regcache, regs->f + flt_num++, val + rlen, len - rlen);
}
}
else
{
memcpy (raw_args + Narg_slots * rlen, val, rlen);
memcpy (raw_args + (Narg_slots + 1) * rlen, val + rlen, len - rlen);
Narg_slots += 2;
}
}
/* Others, the argument is passed in a pair of available GAR, with the
low-order bits in the lower-numbered GAR and the high-order bits in
the higher-numbered GAR. If only one GAR is available, the low-order
bits are in the GAR and the high-order bits are on the stack, and
passed on the stack if no GAR is available. */
else
{
memcpy (raw_args + Narg_slots * rlen, val, rlen);
memcpy (raw_args + (Narg_slots + 1) * rlen, val + rlen, len - rlen);
Narg_slots += 2;
}
}
}
/* len < = rlen */
else
{
/* The structure has only fixed-point members. If there is an available GAR,
the structure is passed through the GAR by value passing; If no GAR is
available, it’s passed on the stack. */
if (small_struct.fixed_member > 0 && small_struct.float_member == 0)
{
memcpy (raw_args + Narg_slots * rlen, val, len);
Narg_slots ++;
}
/* The structure has only floating-point members. */
else if (small_struct.fixed_member == 0 && small_struct.float_member > 0)
{
/* One floating-point member. The argument is passed in a FAR; If no FAR
is available, the value is passed in a GAR; if no GAR is available, the
value is passed on the stack. */
if (small_struct.float_member == 1)
{
if (flt_num < 8)
{
pass_on_reg (regcache, regs->f + flt_num++, val, rlen);
}
else
{
memcpy (raw_args + Narg_slots * rlen, val, len);
Narg_slots ++;
}
}
/* Two floating-point members. The argument is passed in a pair of available
FAR, with the low-order float member bits in the lower-numbered FAR and
the high-order float member bits in the higher-numbered FAR. If the number
of available FAR is less than 2, it’s passed in a GAR, and passed on the
stack if no GAR is available. */
else if (small_struct.float_member == 2)
{
if (flt_num < 7)
{
pass_on_reg (regcache, regs->f + flt_num++, val, len/2);
pass_on_reg (regcache, regs->f + flt_num++, val + len/2, len/2);
}
else
{
memcpy (raw_args + Narg_slots * rlen, val, len);
Narg_slots ++;
}
}
}
/* The structure has both fixed-point and floating-point members,
i.e. the structure has one float member and… */
else if (small_struct.fixed_member > 0 && small_struct.float_member == 1)
{
/* Multiple fixed-point members. If there are available GAR, the structure
is passed in a GAR, and passed on the stack if no GAR is available. */
if (small_struct.fixed_member > 1)
{
memcpy (raw_args + Narg_slots * rlen, val, len);
Narg_slots ++;
}
/* Only one fixed-point member. If one FAR and one GAR are available, the
floating-point member of the structure is passed in the FAR, and the integer
member of the structure is passed in the GAR; If no floating-point register
but one GAR is available, it’s passed in GAR; If no GAR is available, it’s
passed on the stack. */
else if (small_struct.fixed_member == 1)
{
if (flt_num < 8 && Narg_slots < 8)
{
if (small_struct.first_member_is_float)
{
pass_on_reg (regcache, regs->f + flt_num++, val, len/2);
memcpy (raw_args + Narg_slots * rlen, val + len/2, len/2);
Narg_slots ++;
}
else
{
memcpy (raw_args + Narg_slots * rlen, val, len/2);
Narg_slots ++;
pass_on_reg (regcache, regs->f + flt_num++, val + len/2, len/2);
}
}
else
{
memcpy (raw_args + Narg_slots * rlen, val, len);
Narg_slots ++;
}
}
}
}
}
else if (typecode == TYPE_CODE_UNION)
{
/* The argument is passed in a GAR, or on the stack by value if no GAR is available. */
if (len <= rlen)
{
memcpy (raw_args + Narg_slots * rlen, val, len);
Narg_slots += 1;
}
/* The argument is passed in a pair of available GAR, with the low-order bits in the
lower-numbered GAR and the high-order bits in the higher-numbered GAR. If only one
GAR is available, the low-order bits are in the GAR and the high-order bits are on
the stack. The arguments are passed on the stack when no GAR is available. */
else if (len > rlen && len <= 2 * rlen)
{
memcpy (raw_args + Narg_slots * rlen, val, rlen);
memcpy (raw_args + (Narg_slots + 1) * rlen, val + rlen, len - rlen);
Narg_slots += 2;
}
/* It’s passed by reference and are replaced in the argument list with the address.
If there is an available GAR, the reference is passed in the GAR, and passed on
the stack if no GAR is available. */
else
{
sp = align_down (sp - len, 16);
write_memory (sp, val, len);
memcpy (raw_args + Narg_slots * rlen, (const gdb_byte *) &sp, rlen);
Narg_slots += 1;
}
}
else if (typecode == TYPE_CODE_COMPLEX)
{
struct type *t_type = check_typedef (TYPE_TARGET_TYPE (arg_type));
int tlen = TYPE_LENGTH (t_type);
/* float _Complex */
if (tlen < rlen)
{
if (flt_num < 7)
{
pass_on_reg (regcache, regs->f/* $fa0 */ + flt_num++, val, tlen);
pass_on_reg (regcache, regs->f/* $fa0 */ + flt_num++, val + tlen, tlen);
}
else
{
memcpy (raw_args + Narg_slots * rlen, val, rlen);
Narg_slots += 1;
}
}
/* double _Complex */
else if (tlen == rlen)
{
if (flt_num < 7)
{
pass_on_reg (regcache, regs->f/* $fa0 */ + flt_num++, val, tlen);
pass_on_reg (regcache, regs->f/* $fa0 */ + flt_num++, val + tlen, tlen);
}
else
{
memcpy (raw_args + Narg_slots * rlen, val, tlen);
memcpy (raw_args + (Narg_slots + 1) * rlen, val + tlen, tlen);
Narg_slots += 2;
}
}
/* long double _Complex */
else
{
/* Address on register, data on stack. */
sp = align_down (sp - len, 16);
write_memory (sp, val, len);
memcpy (raw_args + Narg_slots * rlen, (const gdb_byte *) &sp, rlen);
Narg_slots += 1;
}
}
else
{
/* Otherwise for bigger actual args, such as
'_Complex double long' etc, we memcpy. */
memcpy (raw_args + Narg_slots * rlen, val, len);
Narg_slots += (len + rlen - 1) / rlen;
}
}
/* 3. Write in stack and argument registers */
if (8 < Narg_slots)
sp -= (Narg_slots - 8) * rlen;
sp = align_down (sp, 16);
if (8 < Narg_slots)
write_memory (sp, raw_args + 8 * rlen, (Narg_slots - 8) * rlen);
regcache_cooked_write_signed (regcache, regs->ra, bp_addr);
regcache_cooked_write_signed (regcache, regs->sp, sp);
for (i = 0; i < (8 < Narg_slots ? 8 : Narg_slots); i++)
{
pass_on_reg (regcache, regs->r+4/* $a0 */ + i, raw_args + i * rlen, rlen);
}
return sp;
}
static void
loongarch_xfer_reg_part (struct regcache *regcache, int reg_num,
int len, gdb_byte *readbuf, size_t readbuf_off,
const gdb_byte *writebuf, size_t writebuf_off)
{
if (readbuf)
regcache->cooked_read_part (reg_num, 0, len, readbuf + readbuf_off);
if (writebuf)
regcache->cooked_write_part (reg_num, 0, len, writebuf + writebuf_off);
}
static enum return_value_convention
loongarch_xlp32lp64_return_value (struct gdbarch *gdbarch,
struct value *function,
struct type *type, struct regcache *regcache,
gdb_byte *readbuf, const gdb_byte *writebuf)
{
const size_t rlen = loongarch_rlen (gdbarch) / 8;
auto regs = &gdbarch_tdep (gdbarch)->regs;
size_t len = TYPE_LENGTH (type);
enum type_code typecode = TYPE_CODE (type);
int fpu_exist = 0 <= regs->f;
int fv = fpu_exist ? regs->f : regs->r + 4;
gdb_assert (8 <= sizeof (LONGEST));
gdb_assert (!fpu_exist || register_size (gdbarch, regs->f) == rlen);
if (2 * rlen < len)
return RETURN_VALUE_STRUCT_CONVENTION;
if ((typecode == TYPE_CODE_FLT
|| (typecode == TYPE_CODE_STRUCT && TYPE_NFIELDS (type) == 1
&& TYPE_CODE (check_typedef (type->field (0).type ()))
== TYPE_CODE_FLT))
&& len <= rlen/* FIXME: May fpu32 on loongarch32 */)
/* If $fv0 could fit in. */
loongarch_xfer_reg_part (regcache, fv, len, readbuf, 0, writebuf, 0);
else if ((typecode == TYPE_CODE_FLT
|| (typecode == TYPE_CODE_STRUCT && TYPE_NFIELDS (type) == 1
&& TYPE_CODE (check_typedef (type->field (0).type ()))
== TYPE_CODE_FLT)) && rlen < len && len <= 2 * rlen)
/* For 'long double' on fpu64 or 'double' on fpu32,
'$fv0 | $fv1' is that.*/
loongarch_xfer_reg_part (regcache, fv, rlen, readbuf, 0, writebuf, 0)
, loongarch_xfer_reg_part
(regcache, fv + 1, len - rlen, readbuf, rlen, writebuf, rlen);
else if (typecode == TYPE_CODE_STRUCT && TYPE_NFIELDS (type) == 2
&& TYPE_CODE (check_typedef (type->field (0).type ()))
== TYPE_CODE_FLT
&& TYPE_CODE (check_typedef (type->field (1).type ()))
== TYPE_CODE_FLT)
{
/* For structure with two float member,
$fv0 is the 1st member and $fv1 is the 2nd member */
int off = FIELD_BITPOS (type->field (1)) / TARGET_CHAR_BIT;
int len1 = TYPE_LENGTH (check_typedef (type->field (0).type ()));
int len2 = TYPE_LENGTH (check_typedef (type->field (1).type ()));
loongarch_xfer_reg_part (regcache, fv, len1, readbuf, 0, writebuf, 0);
loongarch_xfer_reg_part
(regcache, fv + 1, len2, readbuf, off, writebuf, off);
}
else if (typecode == TYPE_CODE_COMPLEX
&& TYPE_CODE (check_typedef (TYPE_TARGET_TYPE (type)))
== TYPE_CODE_FLT)
/* For '_Complex', $fv0 is real and $fv1 is img. */
loongarch_xfer_reg_part (regcache, fv, len / 2, readbuf, 0, writebuf, 0)
, loongarch_xfer_reg_part
(regcache, fv + 1, len / 2, readbuf, len / 2, writebuf, len / 2);
else if (((typecode == TYPE_CODE_INT && TYPE_UNSIGNED (type))
|| typecode == TYPE_CODE_ENUM)
&& len <= rlen)
/* For unsigned scalar type, we have zero-extended one in $v0. */
if (writebuf)
{
gdb_byte buf[rlen];
store_signed_integer (buf, rlen, BFD_ENDIAN_LITTLE,
extract_unsigned_integer (writebuf, len, BFD_ENDIAN_LITTLE));
loongarch_xfer_reg_part
(regcache, regs->r + 4, rlen, NULL, 0, writebuf, 0);
}
else
loongarch_xfer_reg_part
(regcache, regs->r + 4, len, readbuf, 0, NULL, 0);
else if (((typecode == TYPE_CODE_INT && !TYPE_UNSIGNED (type))
|| typecode == TYPE_CODE_PTR)
&& len <= rlen)
/* For signed scalar type, we have sign-extended one in $v0. */
if (writebuf)
{
gdb_byte buf[rlen];
store_signed_integer (buf, rlen, BFD_ENDIAN_LITTLE,
extract_signed_integer (writebuf, len, BFD_ENDIAN_LITTLE));
loongarch_xfer_reg_part
(regcache, regs->r + 4, rlen, NULL, 0, writebuf, 0);
}
else
loongarch_xfer_reg_part
(regcache, regs->r + 4, len, readbuf, 0, NULL, 0);
else
{
/* For small structure or int64_t on loongarch32 */
if (len <= rlen)
loongarch_xfer_reg_part
(regcache, regs->r + 4, len, readbuf, 0, writebuf, 0);
else
loongarch_xfer_reg_part
(regcache, regs->r + 4, rlen, readbuf, 0, writebuf, 0)
, loongarch_xfer_reg_part (regcache, regs->r + 5, len - rlen,
readbuf, rlen, writebuf, rlen);
}
return RETURN_VALUE_REGISTER_CONVENTION;
}
static int
loongarch_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, int num)
{
auto regs = &gdbarch_tdep (gdbarch)->regs;
if (0 <= num && num < 32)
return regs->r + num;
else if (32 <= num && num < 64 && 0 <= regs->f)
return regs->f + num - 32;
else if (64 <= num && num < 72 && 0 <= regs->fcc)
return regs->fcc + num - 64;
else
return -1;
}
static std::string
loongarch_gcc_target_options (struct gdbarch *gdbarch)
{
return NULL;
}
static int
loongarch_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
struct reggroup *group)
{
auto regs = &gdbarch_tdep (gdbarch)->regs;
if (gdbarch_register_name (gdbarch, regnum) == NULL
|| *gdbarch_register_name (gdbarch, regnum) == '\0')
return 0;
int raw_p = regnum < gdbarch_num_regs (gdbarch);
if (group == save_reggroup || group == restore_reggroup)
return raw_p;
if (group == all_reggroup)
return 1;
/* 重载默认的reggroup_p函数主要是为了自定义info register的寄存器显示。
默认的reggroup_p对float_reggroup的判断是“register_type是不是浮点”,
我在feature中对浮点寄存器设定的类型是union {float f; double d;},
因此reggroup_p默认情况下不认为浮点寄存器属于float_reggroup,info float
就不打印浮点寄存器;info vector也是类似。info register默认会打印LBT
寄存器,因为它们属于general_reggroup,但实际上这些寄存器相当特殊。 */
if (group == general_reggroup
&& (regs->pc == regnum
|| regs->badvaddr == regnum
|| (regs->r <= regnum && regnum < regs->r + 32)))
return 1;
/* Only $rx and $pc in general_reggroup */
if (group == general_reggroup)
return 0;
if (0 <= regs->f
&& (regs->fcsr == regnum
|| (regs->f <= regnum && regnum < regs->f + 32)
|| (regs->fcc <= regnum && regnum < regs->fcc +8)))
return group == float_reggroup;
/* Only $fx / $fccx / $fcsr in float_reggroup */
if (group == float_reggroup)
return 0;
if (0 <= regs->vr && regs->vr <= regnum && regnum < regs->vr + 32)
if (group == vector_reggroup)
return 1;
if (0 <= regs->xr && regs->xr <= regnum && regnum < regs->xr + 32)
if (group == vector_reggroup)
return 1;
int ret = tdesc_register_in_reggroup_p (gdbarch, regnum, group);
if (ret != -1)
return ret;
return default_register_reggroup_p (gdbarch, regnum, group);
}
static void
loongarch_print_all_r_registers (struct gdbarch *gdbarch, struct ui_file *file,
struct frame_info *frame)
{
int i, col;
int rlen = loongarch_rlen (gdbarch) / 8;
int ncols = rlen == 4 ? 8 : 4;
for (i = 0; i < 32; i += ncols)
{
fprintf_filtered (file, " ");
for (col = 0; col < ncols; col++)
fprintf_filtered (file, rlen == 8 ? "%17s" : "%9s",
gdbarch_register_name (gdbarch, i + col));
fprintf_filtered (file, "\nR%-4d", i);
for (col = 0; col < ncols; col++)
{
const gdb_byte *raw_buffer;
struct value *value = get_frame_register_value (frame, i + col);
int byte;
if (value_optimized_out (value) || !value_entirely_available (value))
fprintf_filtered
(file, "%*s", 2 * rlen, rlen == 4 ? "" : "");
else
{
int byte;
const gdb_byte *raw_buffer = value_contents_all (value);
for (byte = rlen - 1; 0 <= byte; byte--)
fprintf_filtered (file, "%02x", raw_buffer[byte]);
}
fprintf_filtered (file, " ");
}
fprintf_filtered (file, "\n");
}
}
static void
loongarch_print_fp_register (struct ui_file *file, struct frame_info *frame,
int regnum)
{
struct gdbarch *gdbarch = get_frame_arch (frame);
struct value *value = get_frame_register_value (frame, regnum);
const gdb_byte *raw_buffer = value_contents_all (value);
struct value_print_options opts;
fprintf_filtered (file, "%-5s ", gdbarch_register_name (gdbarch, regnum));
get_formatted_print_options (&opts, 'x');
fprintf_filtered (file, "%s","{f = ");
print_scalar_formatted (raw_buffer,
builtin_type (gdbarch)->builtin_uint32,
&opts, 'w', file);
fprintf_filtered (file, ", %s","d = ");
print_scalar_formatted (raw_buffer,
builtin_type (gdbarch)->builtin_uint32,
&opts, 'g', file);
fprintf_filtered (file, "%s","} ");
fprintf_filtered (file, "{f = %s, ",
target_float_to_string (raw_buffer,
builtin_type (gdbarch)->builtin_float, "%-17.9g").c_str());
fprintf_filtered (file, "d = %s}\n",
target_float_to_string (raw_buffer,
builtin_type (gdbarch)->builtin_double, "%-24.17g").c_str());
}
static void
loongarch_print_registers_info (struct gdbarch *gdbarch, struct ui_file *file,
struct frame_info *frame, int regnum, int all)
{
int i;
auto regs = &gdbarch_tdep (gdbarch)->regs;
const int numregs = gdbarch_num_regs (gdbarch)
+ gdbarch_num_pseudo_regs (gdbarch);
for (i = 0; i < numregs; i++)
{
if (regnum == -1)
{
if (regs->r == i)
loongarch_print_all_r_registers (gdbarch, file, frame), i += 32;
if (all)
{
if (!gdbarch_register_reggroup_p (gdbarch, i, all_reggroup))
continue;
}
else
{
if (!gdbarch_register_reggroup_p (gdbarch, i, general_reggroup))
continue;
}
}
else if (i != regnum)
continue;
if (gdbarch_register_name (gdbarch, i) == NULL
|| *(gdbarch_register_name (gdbarch, i)) == '\0')
continue;
if (gdbarch_register_reggroup_p (gdbarch, i, float_reggroup))
loongarch_print_fp_register (file, frame, i);
else
default_print_registers_info (gdbarch, file, frame, i, 0);
}
}
constexpr gdb_byte loongarch_default_breakpoint[] = {0x05, 0x00, 0x2a, 0x00};
typedef BP_MANIPULATION (loongarch_default_breakpoint) loongarch_breakpoint;
/* 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. */
/* This predicate tests whether we need to read lsx/lasx registers
(instead of fp registers with the same DWARF2 code
(thus the same internal code, though lasx/lsx/fp reg internal codes are different))
according to the byte-size of requested type. */
static int
loongarch_fp_regnum_refers_to_lsx_lasx_p (struct gdbarch *gdbarch, int regnum, struct type *type)
{
/* Conditions:
1) regnum is in "disputed" zone (fp/lsx/lasx, translated from dwarf regnum)
2) type is larger than 8 bytes
(if specified type is larger than 8 bytes,
then regnum refers to lsx / lasx register instead of fp register)
*/
return
regnum >= gdbarch_tdep(gdbarch)->regs.f
&& regnum < gdbarch_tdep(gdbarch)->regs.f + 32
&& TYPE_LENGTH (type) > 8;
}
static int
loongarch_convert_register_p (struct gdbarch *gdbarch,
int regnum, struct type *type)
{
return loongarch_fp_regnum_refers_to_lsx_lasx_p (gdbarch, regnum, type);
}
static int
loongarch_register_to_value (struct frame_info *frame, int regnum,
struct type *type, gdb_byte *to,
int *optimizedp, int *unavailablep)
{
struct gdbarch *gdbarch = get_frame_arch (frame);
if (loongarch_fp_regnum_refers_to_lsx_lasx_p (gdbarch, regnum, type))
{
/* Add a displacement to regnum */
switch (TYPE_LENGTH (type))
{
case 16: /* 16-byte types, access vr */
if (!get_frame_register_bytes (frame, regnum + gdbarch_tdep(gdbarch)->regs.vr - gdbarch_tdep(gdbarch)->regs.f,
0, 16, to, optimizedp, unavailablep))
return 0;
break;
case 32: /* 32-byte types, access xr */
if (!get_frame_register_bytes (frame, regnum + gdbarch_tdep(gdbarch)->regs.xr - gdbarch_tdep(gdbarch)->regs.f,
0, 32, to, optimizedp, unavailablep))
return 0;
break;
default:
goto fail;
}
*optimizedp = *unavailablep = 0;
return 1; // 1 for success, 0 for fail
}
fail:
internal_error (__FILE__, __LINE__,
_("loongarch_register_to_value: unrecognized case"));
}
static void
loongarch_value_to_register (struct frame_info *frame, int regnum,
struct type *type, const gdb_byte *from)
{
struct gdbarch *gdbarch = get_frame_arch (frame);
if (loongarch_fp_regnum_refers_to_lsx_lasx_p (gdbarch, regnum, type))
{
switch (TYPE_LENGTH (type))
{
case 16: /* 16-byte types, access vr */
put_frame_register (frame,
regnum + gdbarch_tdep(gdbarch)->regs.vr - gdbarch_tdep(gdbarch)->regs.f, from);
return;
case 32: /* 32-byte types, access xr */
put_frame_register (frame,
regnum + gdbarch_tdep(gdbarch)->regs.xr - gdbarch_tdep(gdbarch)->regs.f, from);
return;
}
}
internal_error (__FILE__, __LINE__,
_("loongarch_value_to_register: unrecognized case"));
}
/* Figure out where the longjmp will land.
We expect the first arg to be a pointer to the jmp_buf structure
from which we extract the pc (LOONGARCH_JB_PC) that we will land
at. The pc is copied into PC. This routine returns 1 on
success. */
static int
loongarch_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc)
{
CORE_ADDR jb_addr;
struct gdbarch *gdbarch = get_frame_arch (frame);
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
gdb_byte buf[tdep->jb_elt_size];
jb_addr = get_frame_register_unsigned (frame, LOONGARCH_A0_REGNUM);
if (target_read_memory (jb_addr + tdep->jb_pc * tdep->jb_elt_size, buf,
tdep->jb_elt_size))
return 0;
*pc = extract_unsigned_integer (buf, tdep->jb_elt_size, BFD_ENDIAN_LITTLE);
return 1;
}
static struct gdbarch *
loongarch_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
{
struct gdbarch *gdbarch;
struct gdbarch_tdep tdep_instant, *tdep;
struct tdesc_arch_data *tdesc_data = NULL;
const struct target_desc *tdesc = info.target_desc;
int i;
size_t regnum;
tdep = &tdep_instant;
memset (tdep, 0, sizeof (tdep));
memset (&tdep->regs, -1, sizeof (tdep->regs));
if (info.abfd != NULL
&& bfd_get_flavour (info.abfd) == bfd_target_elf_flavour)
{
unsigned char eclass = elf_elfheader (info.abfd)->e_ident[EI_CLASS];
int e_flags = elf_elfheader (info.abfd)->e_flags;
auto e_abi = e_flags & EF_LARCH_ABI;
switch (e_abi)
{
case EF_LARCH_ABI_XLP32:
case EF_LARCH_ABI_LP32:
case EF_LARCH_ABI_LP64:
tdep->ef_abi = e_abi;
break;
default:
tdep->ef_abi = EF_LARCH_ABI_LP64;
}
}
else
tdep->ef_abi = EF_LARCH_ABI_LP64;
/* Check any target description for validity. */
if (!tdesc_has_registers (tdesc))
tdesc = loongarch_get_base_target_description
(tdep->ef_abi == EF_LARCH_ABI_LP32 ? 32 : 64);
int valid_p = 1;
const struct tdesc_feature *feature;
feature = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.base");
if (feature == NULL)
return NULL;
regnum = 0;
tdesc_data = tdesc_data_alloc ();
tdep->regs.r = regnum;
for (i = 0; i < 32; i++)
valid_p &= tdesc_numbered_register (feature, tdesc_data, regnum++,
loongarch_r_normal_name[i] + 1);
valid_p &= tdesc_numbered_register
(feature, tdesc_data, tdep->regs.pc = regnum++, "pc");
valid_p &= tdesc_numbered_register
(feature, tdesc_data, tdep->regs.badvaddr = regnum++, "badvaddr");
if ((feature = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.fpu")))
{
tdep->regs.f = regnum;
for (i = 0; i < 32; i++)
valid_p &= tdesc_numbered_register (feature, tdesc_data, regnum++,
loongarch_f_normal_name[i] + 1);
tdep->regs.fcc = regnum;
valid_p &= tdesc_numbered_register (feature, tdesc_data, regnum++, "fcc0");
valid_p &= tdesc_numbered_register (feature, tdesc_data, regnum++, "fcc1");
valid_p &= tdesc_numbered_register (feature, tdesc_data, regnum++, "fcc2");
valid_p &= tdesc_numbered_register (feature, tdesc_data, regnum++, "fcc3");
valid_p &= tdesc_numbered_register (feature, tdesc_data, regnum++, "fcc4");
valid_p &= tdesc_numbered_register (feature, tdesc_data, regnum++, "fcc5");
valid_p &= tdesc_numbered_register (feature, tdesc_data, regnum++, "fcc6");
valid_p &= tdesc_numbered_register (feature, tdesc_data, regnum++, "fcc7");
valid_p &= tdesc_numbered_register
(feature, tdesc_data, tdep->regs.fcsr = regnum++, "fcsr");
}
if ((feature = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.lbt")))
{
tdep->regs.scr = regnum;
for (i = 0; i < 4; i++)
valid_p &= tdesc_numbered_register (feature, tdesc_data, regnum++,
loongarch_cr_normal_name[i] + 1);
valid_p &= tdesc_numbered_register
(feature, tdesc_data, tdep->regs.EFLAG = regnum++, "EFLAG");
valid_p &= tdesc_numbered_register
(feature, tdesc_data, tdep->regs.lbt_top = regnum++, "lbt_top");
}
if ((feature = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.lsx")))
{
tdep->regs.vr = regnum;
for (i = 0; i < 32; i++)
valid_p &= tdesc_numbered_register (feature, tdesc_data, regnum++,
loongarch_v_normal_name[i] + 1);
}
if ((feature = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.lasx")))
{
tdep->regs.xr = regnum;
for (i = 0; i < 32; i++)
valid_p &= tdesc_numbered_register (feature, tdesc_data, regnum++,
loongarch_x_normal_name[i] + 1);
}
if (!valid_p)
{
tdesc_data_cleanup (tdesc_data);
return NULL;
}
info.byte_order_for_code = BFD_ENDIAN_LITTLE;
/* 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))
{
if (gdbarch_tdep (arches->gdbarch)->ef_abi != tdep->ef_abi)
continue;
if (tdesc_data != NULL)
tdesc_data_cleanup (tdesc_data);
return arches->gdbarch;
}
/* None found, so create a new architecture from the information provided. */
tdep = (struct gdbarch_tdep *) xmalloc (sizeof (tdep_instant));
memcpy (tdep, &tdep_instant, sizeof (tdep_instant));
gdbarch = gdbarch_alloc (&info, tdep);
/* Target data types. */
switch (tdep->ef_abi)
{
case EF_LARCH_ABI_XLP32:
set_gdbarch_short_bit (gdbarch, 16);
set_gdbarch_int_bit (gdbarch, 32);
set_gdbarch_long_bit (gdbarch, 64);
set_gdbarch_long_long_bit (gdbarch, 64);
set_gdbarch_float_bit (gdbarch, 32);
set_gdbarch_double_bit (gdbarch, 64);
set_gdbarch_long_double_bit (gdbarch, 128);
set_gdbarch_long_double_format (gdbarch, floatformats_ia64_quad);
set_gdbarch_ptr_bit (gdbarch, 32);
set_gdbarch_char_signed (gdbarch, 0);
break;
case EF_LARCH_ABI_LP32:
set_gdbarch_short_bit (gdbarch, 16);
set_gdbarch_int_bit (gdbarch, 32);
set_gdbarch_long_bit (gdbarch, 32);
set_gdbarch_long_long_bit (gdbarch, 32);
set_gdbarch_float_bit (gdbarch, 32);
set_gdbarch_double_bit (gdbarch, 64);
set_gdbarch_long_double_bit (gdbarch, 128);
set_gdbarch_long_double_format (gdbarch, floatformats_ia64_quad);
set_gdbarch_ptr_bit (gdbarch, 32);
set_gdbarch_char_signed (gdbarch, 0);
break;
case EF_LARCH_ABI_LP64:
set_gdbarch_short_bit (gdbarch, 16);
set_gdbarch_int_bit (gdbarch, 32);
set_gdbarch_long_bit (gdbarch, 64);
set_gdbarch_long_long_bit (gdbarch, 64);
set_gdbarch_float_bit (gdbarch, 32);
set_gdbarch_double_bit (gdbarch, 64);
set_gdbarch_long_double_bit (gdbarch, 128);
set_gdbarch_long_double_format (gdbarch, floatformats_ia64_quad);
set_gdbarch_ptr_bit (gdbarch, 64);
set_gdbarch_char_signed (gdbarch, 0);
tdep->regs.ra = tdep->regs.r + 1;
tdep->regs.sp = tdep->regs.r + 3;
tdep->jb_pc = LOONGARCH_JB_PC;
tdep->jb_elt_size = gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT;
for (i = 0; i < ARRAY_SIZE (loongarch_r_normal_name); ++i)
if (loongarch_r_normal_name[i][0] != '\0')
user_reg_add (gdbarch, loongarch_r_normal_name[i] + 1,
value_of_loongarch_user_reg, (void *) (size_t) (tdep->regs.r + i));
for (i = 0; i < ARRAY_SIZE (loongarch_r_lp64_name); ++i)
if (loongarch_r_lp64_name[i][0] != '\0')
user_reg_add (gdbarch, loongarch_r_lp64_name[i] + 1,
value_of_loongarch_user_reg, (void *) (size_t) (tdep->regs.r + i));
for (i = 0; i < ARRAY_SIZE (loongarch_r_lp64_name1); ++i)
if (loongarch_r_lp64_name[i][0] != '\0')
user_reg_add (gdbarch, loongarch_r_lp64_name1[i] + 1,
value_of_loongarch_user_reg, (void *) (size_t) (tdep->regs.r + i));
/* Add float register names. */
for (i = 0; i < ARRAY_SIZE (loongarch_f_normal_name); ++i)
{
if (loongarch_f_normal_name[i][0] != '\0')
user_reg_add (gdbarch, loongarch_f_normal_name[i] + 1,
value_of_loongarch_user_reg,
(void *) (size_t) (tdep->regs.f + i));
}
for (i = 0; i < ARRAY_SIZE (loongarch_f_lp64_name); ++i)
{
if (loongarch_f_lp64_name[i][0] != '\0')
user_reg_add (gdbarch, loongarch_f_lp64_name[i] + 1,
value_of_loongarch_user_reg,
(void *) (size_t) (tdep->regs.f + i));
}
for (i = 0; i < ARRAY_SIZE (loongarch_f_lp64_name1); ++i)
{
if (loongarch_f_lp64_name1[i][0] != '\0')
user_reg_add (gdbarch, loongarch_f_lp64_name1[i] + 1,
value_of_loongarch_user_reg,
(void *) (size_t) (tdep->regs.f + i));
}
/* Functions handling dummy frames. */
set_gdbarch_push_dummy_call
(gdbarch, loongarch_xlp32lp64_push_dummy_call);
set_gdbarch_return_value (gdbarch, loongarch_xlp32lp64_return_value);
break;
default:
gdb_assert_not_reached ("unknown ABI");
}
/* Register architecture. */
set_gdbarch_num_regs (gdbarch, regnum);
set_gdbarch_sp_regnum (gdbarch, tdep->regs.sp);
set_gdbarch_pc_regnum (gdbarch, tdep->regs.pc);
// set_gdbarch_ps_regnum (gdbarch, loongarch_FP_REGNUM);
// set_gdbarch_deprecated_fp_regnum (gdbarch, loongarch_FP_REGNUM);
tdesc_use_registers (gdbarch, tdesc, tdesc_data);
/* Functions to supply register information. */
set_gdbarch_register_name (gdbarch, loongarch_register_name);
/* Handle overlapping dwarf2 register code for fp/lsx/lasx */
set_gdbarch_convert_register_p (gdbarch, loongarch_convert_register_p);
set_gdbarch_register_to_value (gdbarch, loongarch_register_to_value);
set_gdbarch_value_to_register (gdbarch, loongarch_value_to_register);
/* Functions to analyze frames. */
set_gdbarch_skip_prologue (gdbarch, loongarch_skip_prologue);
set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
set_gdbarch_frame_align (gdbarch, loongarch_frame_align);
/* Functions to access frame data. */
set_gdbarch_unwind_pc (gdbarch, loongarch_unwind_pc);
set_gdbarch_unwind_sp (gdbarch, loongarch_unwind_sp);
set_gdbarch_dummy_id (gdbarch, loongarch_dummy_id);
set_gdbarch_software_single_step (gdbarch, loongarch_software_single_step);
set_gdbarch_breakpoint_kind_from_pc (gdbarch, loongarch_breakpoint::kind_from_pc);
set_gdbarch_sw_breakpoint_from_kind (gdbarch, loongarch_breakpoint::bp_from_kind);
set_gdbarch_have_nonsteppable_watchpoint (gdbarch, 1);
/* Virtual tables. */
set_gdbarch_vbit_in_delta (gdbarch, 1);
set_gdbarch_gcc_target_options (gdbarch, loongarch_gcc_target_options);
/* Hook in OS ABI-specific overrides, if they have been registered. */
info.target_desc = tdesc;
info.tdesc_data = tdesc_data;
gdbarch_init_osabi (info, gdbarch);
set_gdbarch_register_reggroup_p (gdbarch, loongarch_register_reggroup_p);
set_gdbarch_register_name (gdbarch, loongarch_register_name);
set_gdbarch_print_registers_info (gdbarch, loongarch_print_registers_info);
set_gdbarch_get_longjmp_target (gdbarch, loongarch_get_longjmp_target);
/* Frame unwinders. Use DWARF debug info if available, otherwise use our own
unwinder. */
set_gdbarch_dwarf2_reg_to_regnum (gdbarch, loongarch_dwarf2_reg_to_regnum);
dwarf2_append_unwinders (gdbarch);
frame_unwind_append_unwinder (gdbarch, &loongarch_frame_unwind);
return gdbarch;
}
/* Allocate new loongarch_inferior_data object. */
// static struct loongarch_inferior_data *
// loongarch_new_inferior_data (void)
// {
// struct loongarch_inferior_data *inf_data
// = new (struct loongarch_inferior_data);
// inf_data->misa_read = false;
// return inf_data;
// }
/* Free inferior data. */
// static void
// loongarch_inferior_data_cleanup (struct inferior *inf, void *data)
// {
// struct loongarch_inferior_data *inf_data =
// static_cast (data);
// delete (inf_data);
// }
/* Return loongarch_inferior_data for the given INFERIOR. If not yet created,
construct it. */
// struct loongarch_inferior_data *
// loongarch_inferior_data (struct inferior *const inf)
// {
// struct loongarch_inferior_data *inf_data;
// gdb_assert (inf != NULL);
// inf_data
// = (struct loongarch_inferior_data *) inferior_data (inf, loongarch_inferior_data_reg);
// if (inf_data == NULL)
// {
// inf_data = loongarch_new_inferior_data ();
// set_inferior_data (inf, loongarch_inferior_data_reg, inf_data);
// }
// return inf_data;
// }
/* Free the inferior data when an inferior exits. */
// static void
// loongarch_invalidate_inferior_data (struct inferior *inf)
// {
// struct loongarch_inferior_data *inf_data;
// gdb_assert (inf != NULL);
// /* Don't call loongarch_INFERIOR_DATA as we don't want to create the data if
// we've not already created it by this point. */
// inf_data
// = (struct loongarch_inferior_data *) inferior_data (inf, loongarch_inferior_data_reg);
// if (inf_data != NULL)
// {
// delete (inf_data);
// set_inferior_data (inf, loongarch_inferior_data_reg, NULL);
// }
// }
static void
info_loongarch (const char *addr_exp, int from_tty)
{
char *buf, *t;
int set;
char *item;
unsigned long addr;
unsigned long long value;
if (addr_exp)
{
addr_exp = skip_spaces (addr_exp);
buf = (char *) alloca (strlen (addr_exp) + 1);
strcpy (buf, addr_exp);
loongarch_eliminate_adjacent_repeat_char (buf, ' ');
}
else
goto Empty;
if (!(t = strtok (buf, " ")))
goto Empty;
if (strcmp (t, "set") == 0)
{
t = strtok (NULL, " ");
set = 1;
}
else
{
if (strcmp (t, "get") == 0)
t = strtok (NULL, " ");
set = 0;
}
if (!(item = t))
goto Empty;
if (!(t = strtok (NULL, " ")))
goto Empty;
addr = strtoul (t, NULL, 0);
if (set && (t = strtok (NULL, " ")) == NULL)
goto Empty;
value = strtoll (t, NULL, 0);
if (set)
if (strcmp (item, "cpucfg") == 0)
{
uint32_t t = value;
ULONGEST xfered_len;
target_xfer_partial (current_top_target (), TARGET_OBJECT_LARCH,
"cpucfg", NULL, (const gdb_byte *) &t, addr * 4, sizeof (t),
&xfered_len);
if (0 < xfered_len)
fprintf_unfiltered (gdb_stdout, "ok\n");
else
error ("Set failed");
}
else
{
uint64_t t = value;
ULONGEST xfered_len;
target_xfer_partial (current_top_target (), TARGET_OBJECT_LARCH,
item, NULL, (const gdb_byte *) &t, addr * 8, sizeof (t),
&xfered_len);
if (0 < xfered_len)
fprintf_unfiltered (gdb_stdout, "ok\n");
else
error ("Set failed");
}
else
if (strcmp (item, "cpucfg") == 0)
{
uint32_t t;
ULONGEST xfered_len;
target_xfer_partial (current_top_target (), TARGET_OBJECT_LARCH,
"cpucfg", (gdb_byte *) &t, NULL, addr * 4, sizeof (t),
&xfered_len);
if (0 < xfered_len)
fprintf_unfiltered (gdb_stdout, "return is %x\n", t);
else
error ("Get failed");
}
else
{
uint64_t t;
ULONGEST xfered_len;
target_xfer_partial (current_top_target (), TARGET_OBJECT_LARCH,
item, (gdb_byte *) &t, NULL, addr * 8, sizeof (t),
&xfered_len);
if (0 < xfered_len)
fprintf_unfiltered (gdb_stdout, "return is %llx\n", (long long) t);
else
error ("Get failed");
}
return;
Empty:
error ("Empty. Should be 'info loongarch ([get]|set) item addr [value]'");
}
#define REG_ALLOC(REGS, LENGTH, RECORD_BUF) \
do \
{ \
unsigned int reg_len = LENGTH; \
if (reg_len) \
{ \
REGS = XNEWVEC (uint32_t, reg_len); \
memcpy(®S[0], &RECORD_BUF[0], sizeof(uint32_t)*LENGTH); \
} \
} \
while (0)
#define MEM_ALLOC(MEMS, LENGTH, RECORD_BUF) \
do \
{ \
unsigned int mem_len = LENGTH; \
if (mem_len) \
{ \
MEMS = XNEWVEC (struct loongarch_mem_r, mem_len); \
memcpy(&MEMS->len, &RECORD_BUF[0], \
sizeof(struct loongarch_mem_r) * LENGTH); \
} \
} \
while (0)
struct loongarch_mem_r
{
uint64_t len; /* Record length. */
uint64_t addr; /* Memory address. */
};
enum loongarch_record_result
{
LOONGARCH_RECORD_SUCCESS,
LOONGARCH_RECORD_UNSUPPORTED,
LOONGARCH_RECORD_UNKNOWN
};
typedef struct insn_decode_record_t
{
struct gdbarch *gdbarch;
struct regcache *regcache;
CORE_ADDR this_addr; /* Address of insn to be recorded. */
uint32_t loongarch_insn; /* Insn to be recorded. */
uint32_t mem_rec_count; /* Count of memory records. */
uint32_t reg_rec_count; /* Count of register records. */
uint32_t *loongarch_regs; /* Registers to be recorded. */
struct loongarch_mem_r *loongarch_mems; /* Memory locations to be recorded. */
} insn_decode_record;
static unsigned int loongarch_record_data_process_insn_record(insn_decode_record *loongarch_insn_r){
int rd;
uint32_t record_buf[1];
auto regs = &gdbarch_tdep (loongarch_insn_r->gdbarch)->regs;
rd = loongarch_decode_imm ("0:5", loongarch_insn_r->loongarch_insn, 0) + regs->r;
record_buf[0] = rd;
loongarch_insn_r -> reg_rec_count = 1;
REG_ALLOC (loongarch_insn_r->loongarch_regs, loongarch_insn_r->reg_rec_count,record_buf);
return LOONGARCH_RECORD_SUCCESS;
}
static unsigned int loongarch_record_read_time_insn_record(insn_decode_record *loongarch_insn_r){
int rd,rj;
uint32_t record_buf[2];
auto regs = &gdbarch_tdep (loongarch_insn_r->gdbarch)->regs;
rd = loongarch_decode_imm ("0:5", loongarch_insn_r->loongarch_insn, 0) + regs->r;
rj = loongarch_decode_imm ("5:5", loongarch_insn_r->loongarch_insn, 0) + regs->r;
record_buf[0] = rd;
record_buf[1] = rj;
loongarch_insn_r -> reg_rec_count = 2;
REG_ALLOC (loongarch_insn_r->loongarch_regs, loongarch_insn_r->reg_rec_count,record_buf);
return LOONGARCH_RECORD_SUCCESS;
}
static unsigned int loongarch_record_jump_insn_record(insn_decode_record *loongarch_insn_r){
if(is_jirl_ins(loongarch_insn_r->loongarch_insn)){
int rd;
uint32_t record_buf[1];
auto regs = &gdbarch_tdep (loongarch_insn_r->gdbarch)->regs;
rd = loongarch_decode_imm ("0:5", loongarch_insn_r->loongarch_insn, 0) + regs->r;
record_buf[0] = rd;
loongarch_insn_r -> reg_rec_count = 1;
REG_ALLOC (loongarch_insn_r->loongarch_regs, loongarch_insn_r->reg_rec_count,record_buf);
return LOONGARCH_RECORD_SUCCESS;
}
else if(is_bl_ins(loongarch_insn_r->loongarch_insn)){
uint32_t record_buf[1];
auto regs = &gdbarch_tdep (loongarch_insn_r->gdbarch)->regs;
record_buf[0] = regs->r + 1;
loongarch_insn_r -> reg_rec_count = 1;
REG_ALLOC (loongarch_insn_r->loongarch_regs, loongarch_insn_r->reg_rec_count,record_buf);
return LOONGARCH_RECORD_SUCCESS;
}
return LOONGARCH_RECORD_SUCCESS;
}
//FADD.S FADD.D FSUB.S .... MOVGR2FR.D MOVGR2FRH.W
//MOVCF2FR(only 1 bit) //FIXME:?
//FCVT.S.D FCVT.D.S .... FRINT.S FRINT.D
static unsigned int loongarch_record_float_data_process_insn_record(insn_decode_record *loongarch_insn_r){
int fd;
uint32_t record_buf[1];
auto regs = &gdbarch_tdep (loongarch_insn_r->gdbarch)->regs;
fd = loongarch_decode_imm ("0:5", loongarch_insn_r->loongarch_insn, 0) + regs->f;
record_buf[0] = fd;
loongarch_insn_r -> reg_rec_count = 1;
REG_ALLOC (loongarch_insn_r->loongarch_regs, loongarch_insn_r->reg_rec_count,record_buf);
return LOONGARCH_RECORD_SUCCESS;
}
static unsigned int loongarch_record_fcsr_process_insn_record(insn_decode_record *loongarch_insn_r){
int fcsr;
uint32_t record_buf[1];
auto regs = &gdbarch_tdep (loongarch_insn_r->gdbarch)->regs;
fcsr = loongarch_decode_imm ("0:5", loongarch_insn_r->loongarch_insn, 0) + regs->fcsr;
record_buf[0] = fcsr;
loongarch_insn_r -> reg_rec_count = 1;
REG_ALLOC (loongarch_insn_r->loongarch_regs, loongarch_insn_r->reg_rec_count,record_buf);
return LOONGARCH_RECORD_SUCCESS;
}
//FIXME:no cf regs
static unsigned int loongarch_record_cf_process_insn_record(insn_decode_record *loongarch_insn_r){
int cf,imm3_4;
uint32_t record_buf[1];
cf = loongarch_decode_imm ("0:3", loongarch_insn_r->loongarch_insn, 0);
imm3_4 = loongarch_decode_imm ("3:2", loongarch_insn_r->loongarch_insn, 0);
if(imm3_4 != 0){
return LOONGARCH_RECORD_UNSUPPORTED;
}
record_buf[0] = cf;
loongarch_insn_r -> reg_rec_count = 1;
REG_ALLOC (loongarch_insn_r->loongarch_regs, loongarch_insn_r->reg_rec_count,record_buf);
return LOONGARCH_RECORD_SUCCESS;
}
static unsigned int loongarch_record_float_insn_record(insn_decode_record *loongarch_insn_r){
if(is_movgr2fcsr_ins(loongarch_insn_r->loongarch_insn)){
return loongarch_record_fcsr_process_insn_record(loongarch_insn_r);
} else if(is_cf_ins(loongarch_insn_r->loongarch_insn)){
return loongarch_record_cf_process_insn_record(loongarch_insn_r);
} else {
return loongarch_record_float_data_process_insn_record(loongarch_insn_r);
}
}
static unsigned int loongarch_record_store_insn_record(insn_decode_record *loongarch_insn_r){
enum store_types
{
STB,STH,STW,STD,STXB,STXH,STXW,STXD,STPTRW,STPTRD,SCW,SCD,FSTS,FSTD,FSTXS,FSTXD,NOT_STORE
};
int store_type;
uint64_t record_buf_mem[2];
store_type = is_stb_ins(loongarch_insn_r->loongarch_insn) ? STB :
is_sth_ins(loongarch_insn_r->loongarch_insn) ? STH :
is_stw_ins(loongarch_insn_r->loongarch_insn) ? STW :
is_std_ins(loongarch_insn_r->loongarch_insn) ? STD :
is_stxb_ins(loongarch_insn_r->loongarch_insn) ? STXB :
is_stxh_ins(loongarch_insn_r->loongarch_insn) ? STXH :
is_stxw_ins(loongarch_insn_r->loongarch_insn) ? STXW :
is_stxd_ins(loongarch_insn_r->loongarch_insn) ? STXD :
is_stptrw_ins(loongarch_insn_r->loongarch_insn) ? STPTRW :
is_stptrd_ins(loongarch_insn_r->loongarch_insn) ? STPTRD :
is_scw_ins(loongarch_insn_r->loongarch_insn) ? SCW :
is_scd_ins(loongarch_insn_r->loongarch_insn) ? SCD :
is_fsts_ins(loongarch_insn_r->loongarch_insn) ? FSTS :
is_fstd_ins(loongarch_insn_r->loongarch_insn) ? FSTD :
is_fstxs_ins(loongarch_insn_r->loongarch_insn) ? FSTXS :
is_fstxd_ins(loongarch_insn_r->loongarch_insn) ? FSTXD :
NOT_STORE;
if(store_type == STB || store_type == STH || store_type == STW || store_type == STD || store_type == FSTS || store_type == FSTD ){
int rj_no;
int data_size;
int imm;
uint64_t rj_val;
uint64_t address;
auto regs = &gdbarch_tdep (loongarch_insn_r->gdbarch)->regs;
rj_no = loongarch_decode_imm ("5:5", loongarch_insn_r->loongarch_insn, 0) + regs->r ;
imm = loongarch_decode_imm ("10:12", loongarch_insn_r->loongarch_insn, 0);
regcache_raw_read_unsigned (loongarch_insn_r->regcache, rj_no, &rj_val);
address = rj_val + imm;
data_size = store_type == STB ? 1 :
store_type == STH ? 2 :
store_type == STW || store_type == FSTS ? 4 :
store_type == STD || store_type == FSTD ? 8 : 0;
record_buf_mem[0] = data_size;
record_buf_mem[1] = address;
loongarch_insn_r->mem_rec_count = 1;
MEM_ALLOC (loongarch_insn_r->loongarch_mems, loongarch_insn_r->mem_rec_count,record_buf_mem);
return LOONGARCH_RECORD_SUCCESS;
}
else if(store_type == STXB || store_type == STXH || store_type == STXW || store_type == STXD || store_type == FSTXS || store_type == FSTXD ){
int data_size;
int rj_no,rk_no;
uint64_t rj_val,rk_val;
uint64_t address;
auto regs = &gdbarch_tdep (loongarch_insn_r->gdbarch)->regs;
rj_no = loongarch_decode_imm ("5:5", loongarch_insn_r->loongarch_insn, 0) + regs->r ;
rk_no = loongarch_decode_imm ("10:5", loongarch_insn_r->loongarch_insn, 0) + regs->r ;
regcache_raw_read_unsigned (loongarch_insn_r->regcache, rj_no, &rj_val);
regcache_raw_read_unsigned (loongarch_insn_r->regcache, rk_no, &rk_val);
address = rj_val + rk_val;
data_size = store_type == STXB ? 1 :
store_type == STXH ? 2 :
store_type == STXW || store_type == FSTXS ? 4 :
store_type == STXD || store_type == FSTXD ? 8 : 0;
record_buf_mem[0] = data_size;
record_buf_mem[1] = address;
loongarch_insn_r->mem_rec_count = 1;
MEM_ALLOC (loongarch_insn_r->loongarch_mems, loongarch_insn_r->mem_rec_count,record_buf_mem);
return LOONGARCH_RECORD_SUCCESS;
}else if(store_type == STPTRW || store_type == STPTRD || store_type == SCW || store_type == SCD){ //FIXME:sc.w and sc.d correct the LLbit? Is LLit a register?
int data_size;
int rj_no;
int imm;
uint64_t rj_val;
uint64_t address;
auto regs = &gdbarch_tdep (loongarch_insn_r->gdbarch)->regs;
rj_no = loongarch_decode_imm ("5:5", loongarch_insn_r->loongarch_insn, 0) + regs->r ;
imm = loongarch_decode_imm ("10:14", loongarch_insn_r->loongarch_insn, 0);
regcache_raw_read_unsigned (loongarch_insn_r->regcache, rj_no, &rj_val);
address = rj_val + imm;
data_size = store_type == STPTRW || store_type == SCW ? 4 :
store_type == STPTRD || store_type == SCD ? 8 : 0;
record_buf_mem[0] = data_size;
record_buf_mem[1] = address;
loongarch_insn_r->mem_rec_count = 1;
MEM_ALLOC (loongarch_insn_r->loongarch_mems, loongarch_insn_r->mem_rec_count,record_buf_mem);
return LOONGARCH_RECORD_SUCCESS;
}
return LOONGARCH_RECORD_UNSUPPORTED;
}
static unsigned int loongarch_record_am_insn_record(insn_decode_record *loongarch_insn_r){
int rj_no,rd_no;
int word_or_double;
int data_size;
uint64_t address;
uint32_t record_buf[1];
uint64_t record_buf_mem[2];
auto regs = &gdbarch_tdep (loongarch_insn_r->gdbarch)->regs;
rd_no = loongarch_decode_imm ("0:5", loongarch_insn_r->loongarch_insn, 0) + regs->r;
rj_no = loongarch_decode_imm ("5:5", loongarch_insn_r->loongarch_insn, 0) + regs->r;
word_or_double = loongarch_decode_imm ("15:1", loongarch_insn_r->loongarch_insn, 0);
regcache_raw_read_unsigned (loongarch_insn_r->regcache, rj_no, &address);
data_size = word_or_double == 1 ? 8 : 4;
record_buf[0] = rd_no;
loongarch_insn_r -> reg_rec_count = 1;
record_buf_mem[0] = data_size;
record_buf_mem[1] = address;
loongarch_insn_r->mem_rec_count = 1;
REG_ALLOC (loongarch_insn_r->loongarch_regs, loongarch_insn_r->reg_rec_count,record_buf);
MEM_ALLOC (loongarch_insn_r->loongarch_mems, loongarch_insn_r->mem_rec_count,record_buf_mem);
return LOONGARCH_RECORD_SUCCESS;
}
static unsigned int loongarch_record_cond_load_insn_record(insn_decode_record *loongarch_insn_r){
int rd_no,rj_no,rk_no,fd_no;
uint64_t rj_val,rk_val;
auto regs = &gdbarch_tdep (loongarch_insn_r->gdbarch)->regs;
rd_no = loongarch_decode_imm ("0:5", loongarch_insn_r->loongarch_insn, 0) + regs->r;
fd_no = loongarch_decode_imm ("0:5", loongarch_insn_r->loongarch_insn, 0) + regs->f;
rj_no = loongarch_decode_imm ("5:5", loongarch_insn_r->loongarch_insn, 0) + regs->r;
rk_no = loongarch_decode_imm ("10:5", loongarch_insn_r->loongarch_insn, 0) + regs->r;
regcache_raw_read_unsigned (loongarch_insn_r->regcache, rj_no, &rj_val);
regcache_raw_read_unsigned (loongarch_insn_r->regcache, rk_no, &rk_val);
if((is_ldgt_ins(loongarch_insn_r->loongarch_insn) && (rj_val > rk_val)) ||
(is_ldle_ins(loongarch_insn_r->loongarch_insn) && (rj_val <= rk_val)) ){
uint32_t record_buf[1];
record_buf[0] = rd_no;
loongarch_insn_r -> reg_rec_count = 1;
REG_ALLOC (loongarch_insn_r->loongarch_regs, loongarch_insn_r->reg_rec_count,record_buf);
}
else if((is_fldgt_ins(loongarch_insn_r->loongarch_insn) && (rj_val > rk_val)) ||
(is_fldle_ins(loongarch_insn_r->loongarch_insn) && (rj_val <= rk_val)) ){
uint32_t record_buf[1];
record_buf[0] = fd_no;
loongarch_insn_r -> reg_rec_count = 1;
REG_ALLOC (loongarch_insn_r->loongarch_regs, loongarch_insn_r->reg_rec_count,record_buf);
}
return LOONGARCH_RECORD_SUCCESS;
}
static unsigned int loongarch_record_cond_store_insn_record(insn_decode_record *loongarch_insn_r){
int rj_no,rk_no;
int data_size;
uint64_t rj_val,rk_val;
auto regs = &gdbarch_tdep (loongarch_insn_r->gdbarch)->regs;
rj_no = loongarch_decode_imm ("5:5", loongarch_insn_r->loongarch_insn, 0) + regs->r;
rk_no = loongarch_decode_imm ("10:5", loongarch_insn_r->loongarch_insn, 0) + regs->r;
regcache_raw_read_unsigned (loongarch_insn_r->regcache, rj_no, &rj_val);
regcache_raw_read_unsigned (loongarch_insn_r->regcache, rk_no, &rk_val);
if((is_stgt_ins(loongarch_insn_r->loongarch_insn) && (rj_val > rk_val)) ||
(is_stle_ins(loongarch_insn_r->loongarch_insn) && (rj_val <= rk_val)) ){
uint64_t record_buf_mem[2];
data_size = (loongarch_insn_r->loongarch_insn & 0x00018000) == 0x00000000 ? 1 :
(loongarch_insn_r->loongarch_insn & 0x00018000) == 0x00008000 ? 2 :
(loongarch_insn_r->loongarch_insn & 0x00018000) == 0x00010000 ? 4 :
(loongarch_insn_r->loongarch_insn & 0x00018000) == 0x00018000 ? 8 : 0;
record_buf_mem[0] = data_size;
record_buf_mem[1] = rj_val;
loongarch_insn_r->mem_rec_count = 1;
MEM_ALLOC (loongarch_insn_r->loongarch_mems, loongarch_insn_r->mem_rec_count,record_buf_mem);
}
else if((is_fstgt_ins(loongarch_insn_r->loongarch_insn) && (rj_val > rk_val)) ||
(is_fstle_ins(loongarch_insn_r->loongarch_insn) && (rj_val <= rk_val)) ){
uint64_t record_buf_mem[2];
data_size = (loongarch_insn_r->loongarch_insn & 0x00008000) == 0x00000000 ? 4 :
(loongarch_insn_r->loongarch_insn & 0x00008000) == 0x00008000 ? 8 : 0;
record_buf_mem[0] = data_size;
record_buf_mem[1] = rj_val;
loongarch_insn_r->mem_rec_count = 1;
MEM_ALLOC (loongarch_insn_r->loongarch_mems, loongarch_insn_r->mem_rec_count,record_buf_mem);
}
return LOONGARCH_RECORD_SUCCESS;
}
static unsigned int loongarch_record_nop_insn_record(insn_decode_record *loongarch_insn_r){
return LOONGARCH_RECORD_SUCCESS;
}
/* Decodes insns type and invokes its record handler. */
static unsigned int loongarch_record_decode_insn_handler(insn_decode_record *loongarch_insn_r){
if(is_data_process_ins(loongarch_insn_r->loongarch_insn)){
return loongarch_record_data_process_insn_record(loongarch_insn_r);
} else if(is_jump_ins(loongarch_insn_r->loongarch_insn)){
return loongarch_record_jump_insn_record(loongarch_insn_r);
} else if(is_store_ins(loongarch_insn_r->loongarch_insn)){
return loongarch_record_store_insn_record(loongarch_insn_r);
} else if(is_read_time_ins(loongarch_insn_r->loongarch_insn)){
return loongarch_record_read_time_insn_record(loongarch_insn_r);
} else if(is_float_ins(loongarch_insn_r->loongarch_insn)){
return loongarch_record_float_insn_record(loongarch_insn_r);
} else if(is_nop_ins(loongarch_insn_r->loongarch_insn)){
return loongarch_record_nop_insn_record(loongarch_insn_r);
} else if(is_am_ins(loongarch_insn_r->loongarch_insn)){
return loongarch_record_am_insn_record(loongarch_insn_r);
} else if(is_cond_load_ins(loongarch_insn_r->loongarch_insn)){
return loongarch_record_cond_load_insn_record(loongarch_insn_r);
} else if(is_cond_store_ins(loongarch_insn_r->loongarch_insn)){
return loongarch_record_cond_store_insn_record(loongarch_insn_r);
}
printf("DEBUG INFO:instruction handler cannot decode the instruction:%08x.\n",loongarch_insn_r->loongarch_insn);
// printf("DEBUG INFO:the instruction %08x is nop? : %d is syscall? %d\n",loongarch_insn_r->loongarch_insn,is_nop_ins(loongarch_insn_r->loongarch_insn));
return LOONGARCH_RECORD_UNSUPPORTED;
}
/*
static void print_all_registers(insn_decode_record *loongarch_insn_r){
uint64_t reg_val;
int reg_no = 0;
for(reg_no = 0; reg_no < 33; reg_no++){
regcache_raw_read_unsigned (loongarch_insn_r->regcache, reg_no, ®_val);
printf("REGISTER INFO:The value of REG %d is %lu\n",reg_no,reg_val);
}
printf("SIZE INFO:size of CORE_ADDR is %lu,size of unsigned long int is %lu, size of uint32_t is %lu",sizeof(CORE_ADDR),sizeof(unsigned long int),sizeof(uint32_t));
return;
}
*/
int loongarch_process_record (struct gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR insn_addr)
{
int no_of_rec = 0;
uint32_t ret = 0;
insn_decode_record loongarch_record;
auto regs = &gdbarch_tdep (gdbarch)->regs;
/* reset the content of loongarch_record */
memset (&loongarch_record, 0, sizeof (insn_decode_record));
/* write the loongarch_record */
loongarch_record.gdbarch = gdbarch;
loongarch_record.regcache = regcache;
loongarch_record.this_addr = insn_addr;
/* Get the current instruction */
loongarch_record.loongarch_insn = (uint32_t) loongarch_fetch_instruction (insn_addr, NULL);
ret = loongarch_record_decode_insn_handler (&loongarch_record);
if (ret == LOONGARCH_RECORD_UNSUPPORTED) {
// printf_unfiltered (_("DEBUG INFO:Process record does not support instruction 0x%08x at address %s.\n"),loongarch_record.loongarch_insn, paddress (gdbarch, insn_addr));
ret = -1;
}
if (ret == LOONGARCH_RECORD_SUCCESS) {
/* Record registers. */
// printf("DEBUG INFO:decode end, fill the changed registers\n");
record_full_arch_list_add_reg (loongarch_record.regcache,regs->pc);
if (loongarch_record.loongarch_regs) {
for (no_of_rec = 0; no_of_rec < loongarch_record.reg_rec_count; no_of_rec++) {
if (record_full_arch_list_add_reg(loongarch_record.regcache , loongarch_record.loongarch_regs[no_of_rec]))
ret = -1;
}
}
/* Record memories. */
// printf("DEBUG INFO:decode end, fill the changed memories\n");
if (loongarch_record.loongarch_mems) {
for (no_of_rec = 0; no_of_rec < loongarch_record.mem_rec_count; no_of_rec++) {
if (record_full_arch_list_add_mem((CORE_ADDR)loongarch_record.loongarch_mems[no_of_rec].addr,loongarch_record.loongarch_mems[no_of_rec].len))
ret = -1;
}
}
// printf("DEBUG INFO:fill end\n");
if (record_full_arch_list_add_end ())
ret = -1;
}
return ret;
}
void
_initialize_loongarch_tdep (void)
{
gdbarch_register (bfd_arch_loongarch, loongarch_gdbarch_init, NULL);
add_info ("loongarch", info_loongarch, _("Loongarch extra"));
/* Debug this files internals. */
add_setshow_zuinteger_cmd ("loongarch", class_maintenance,
&loongarch_debug, _("\
Set loongarch debugging."), _("\
Show loongarch debugging."), _("\
When non-zero, loongarch specific debugging is enabled."),
NULL,
NULL,
&setdebuglist, &showdebuglist);
}