From 39adfc7cd9f340503f8ee64777e716b9b7a65ec7 Mon Sep 17 00:00:00 2001 From: Qing Zhang Date: Tue, 29 Mar 2022 16:11:47 +0800 Subject: [PATCH 3/5] gdb-Add LoongArch gdb support Signed-off-by: Qing Zhang --- gdb/Makefile.in | 13 + gdb/arch/loongarch-linux-nat.c | 94 ++ gdb/arch/loongarch-linux-nat.h | 35 + gdb/arch/loongarch.c | 84 + gdb/arch/loongarch.h | 37 + gdb/configure.host | 3 + gdb/configure.nat | 4 + gdb/configure.tgt | 8 + gdb/doc/gdb.texinfo | 10 + gdb/features/Makefile | 10 + gdb/features/loongarch/base32.c | 47 + gdb/features/loongarch/base32.xml | 45 + gdb/features/loongarch/base64.c | 47 + gdb/features/loongarch/base64.xml | 45 + gdb/features/loongarch/fpu32.c | 54 + gdb/features/loongarch/fpu32.xml | 53 + gdb/features/loongarch/fpu64.c | 62 + gdb/features/loongarch/fpu64.xml | 58 + gdb/features/loongarch/lasx.c | 80 + gdb/features/loongarch/lasx.xml | 59 + gdb/features/loongarch/lbt32.c | 19 + gdb/features/loongarch/lbt32.xml | 17 + gdb/features/loongarch/lbt64.c | 19 + gdb/features/loongarch/lbt64.xml | 17 + gdb/features/loongarch/lsx.c | 80 + gdb/features/loongarch/lsx.xml | 59 + gdb/loongarch-linux-nat.c | 876 ++++++++++ gdb/loongarch-linux-tdep.c | 699 ++++++++ gdb/loongarch-linux-tdep.h | 48 + gdb/loongarch-tdep.c | 1925 ++++++++++++++++++++++ gdb/loongarch-tdep.h | 60 + gdb/nat/loongarch-linux-watch.c | 330 ++++ gdb/nat/loongarch-linux-watch.h | 132 ++ gdb/remote.c | 25 + gdb/target.h | 3 + gdb/testsuite/gdb.base/dump.exp | 4 + gdb/testsuite/gdb.base/float.exp | 2 + gdb/testsuite/gdb.trace/entry-values.exp | 2 + gdb/testsuite/gdb.xml/tdesc-regs.exp | 5 + 39 files changed, 5170 insertions(+) create mode 100644 gdb/arch/loongarch-linux-nat.c create mode 100644 gdb/arch/loongarch-linux-nat.h create mode 100644 gdb/arch/loongarch.c create mode 100644 gdb/arch/loongarch.h create mode 100644 gdb/features/loongarch/base32.c create mode 100644 gdb/features/loongarch/base32.xml create mode 100644 gdb/features/loongarch/base64.c create mode 100644 gdb/features/loongarch/base64.xml create mode 100644 gdb/features/loongarch/fpu32.c create mode 100644 gdb/features/loongarch/fpu32.xml create mode 100644 gdb/features/loongarch/fpu64.c create mode 100644 gdb/features/loongarch/fpu64.xml create mode 100644 gdb/features/loongarch/lasx.c create mode 100644 gdb/features/loongarch/lasx.xml create mode 100644 gdb/features/loongarch/lbt32.c create mode 100644 gdb/features/loongarch/lbt32.xml create mode 100644 gdb/features/loongarch/lbt64.c create mode 100644 gdb/features/loongarch/lbt64.xml create mode 100644 gdb/features/loongarch/lsx.c create mode 100644 gdb/features/loongarch/lsx.xml create mode 100644 gdb/loongarch-linux-nat.c create mode 100644 gdb/loongarch-linux-tdep.c create mode 100644 gdb/loongarch-linux-tdep.h create mode 100644 gdb/loongarch-tdep.c create mode 100644 gdb/loongarch-tdep.h create mode 100644 gdb/nat/loongarch-linux-watch.c create mode 100644 gdb/nat/loongarch-linux-watch.h diff --git a/gdb/Makefile.in b/gdb/Makefile.in index b8729ed..c9cbc5d 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -726,6 +726,8 @@ ALL_TARGET_OBS = \ arch/i386.o \ arch/ppc-linux-common.o \ arch/riscv.o \ + arch/loongarch.o \ + arch/loongarch-linux-nat.o \ arm-bsd-tdep.o \ arm-fbsd-tdep.o \ arm-linux-tdep.o \ @@ -774,6 +776,9 @@ ALL_TARGET_OBS = \ linux-record.o \ linux-tdep.o \ lm32-tdep.o \ + loongarch-tdep.o \ + loongarch-linux-tdep.o \ + loongarch-linux-nat.o \ m32c-tdep.o \ m32r-linux-tdep.o \ m32r-tdep.o \ @@ -1348,6 +1353,8 @@ HFILES_NO_SRCDIR = \ linux-record.h \ linux-tdep.h \ location.h \ + loongarch-tdep.h \ + loongarch-linux-tdep.h \ m2-lang.h \ m32r-tdep.h \ m68k-tdep.h \ @@ -1482,6 +1489,8 @@ HFILES_NO_SRCDIR = \ arch/arc.h \ arch/arm.h \ arch/i386.h \ + arch/loongarch.h \ + arch/loongarch-linux-nat.h \ arch/ppc-linux-common.h \ arch/ppc-linux-tdesc.h \ arch/riscv.h \ @@ -1527,6 +1536,7 @@ HFILES_NO_SRCDIR = \ nat/linux-personality.h \ nat/linux-ptrace.h \ nat/linux-waitpid.h \ + nat/loongarch-linux-watch.h \ nat/mips-linux-watch.h \ nat/ppc-linux.h \ nat/x86-cpuid.h \ @@ -2215,6 +2225,9 @@ ALLDEPFILES = \ linux-record.c \ linux-tdep.c \ lm32-tdep.c \ + loongarch-tdep.c \ + loongarch-linux-tdep.c \ + loongarch-linux-nat.c \ m32r-linux-nat.c \ m32r-linux-tdep.c \ m32r-tdep.c \ diff --git a/gdb/arch/loongarch-linux-nat.c b/gdb/arch/loongarch-linux-nat.c new file mode 100644 index 0000000..70bf742 --- /dev/null +++ b/gdb/arch/loongarch-linux-nat.c @@ -0,0 +1,94 @@ +/* Copyright (C) 2021 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 . */ + +/* For external ptrace. */ +#ifdef GDBSERVER +#include "server.h" +#include "nat/gdb_ptrace.h" +#else +#include "defs.h" +#include "nat/gdb_ptrace.h" +#endif + +#include "arch/loongarch.h" +#include "arch/loongarch-linux-nat.h" +#include "loongarch-linux-tdep.h" +#include "elf/common.h" +#include + +static uint32_t +loongarch_cpucfg_may_ptrace (uint64_t rj, int tid) +{ + char t_buf[rj * 4 + 4]; + struct iovec iovec = { .iov_base = &t_buf, .iov_len = sizeof (t_buf) }; + if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_CPUCFG, &iovec) < 0) + ((uint32_t *) t_buf)[rj] = loongarch_cpucfg (rj); + return ((uint32_t *) t_buf)[rj]; +} + +struct target_desc * +loongarch_linux_read_description_runtime (int tid) +{ + int rlen, fpu32, fpu64, lbt, lsx, lasx; + + uint32_t cpucfg1 = loongarch_cpucfg_may_ptrace (1, tid); + rlen = cpucfg1 & 0x2 /* LA64 */ ? 64 : 32; + + uint32_t cpucfg2 = loongarch_cpucfg_may_ptrace (2, tid); + fpu32 = 0, fpu64 = 0; + if (cpucfg2 & 0x4 /* FP_DP */) + fpu64 = 1; + else if (cpucfg2 & 0x2 /* FP_SP */) + fpu32 = 1; + if (fpu32 || fpu64) + { + loongarch_elf_fpregset_t regset; + struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) }; + if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec) < 0) + fpu32 = 0, fpu64 = 0; + } + + lbt = 0; + if (cpucfg2 & 0x1c0000 /* LBT_X86 || LBT_ARM || LBT_MIPS */) + { + loongarch_elf_lbtregset_t regset; + struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) }; + if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LBT, &iovec) == 0) + lbt = 1; + } + + lsx = 0; + if (cpucfg2 & 0x40 /* LSX */) + { + loongarch_elf_lsxregset_t regset; + struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) }; + if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LSX, &iovec) == 0) + lsx = 1; + } + + lasx = 0; + if (cpucfg2 & 0x80 /* LASX */) + { + loongarch_elf_lasxregset_t regset; + struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) }; + if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LASX, &iovec) == 0) + lasx = 1; + } + + return loongarch_create_target_description (rlen, fpu32, fpu64, lbt, lsx, + lasx); +} diff --git a/gdb/arch/loongarch-linux-nat.h b/gdb/arch/loongarch-linux-nat.h new file mode 100644 index 0000000..a9cd453 --- /dev/null +++ b/gdb/arch/loongarch-linux-nat.h @@ -0,0 +1,35 @@ +/* + Copyright (C) 2021 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 . */ + +#ifndef LOONGARCH_LINUX_NAT_H +#define LOONGARCH_LINUX_NAT_H +#include + +static inline uint32_t +loongarch_cpucfg (uint64_t rj) +{ + uint32_t ret; + asm ("cpucfg %0,%1" : "=r"(ret) : "r"(rj)); + return ret; +} + +struct target_desc; + +extern struct target_desc *loongarch_linux_read_description_runtime (int tid); + +#endif diff --git a/gdb/arch/loongarch.c b/gdb/arch/loongarch.c new file mode 100644 index 0000000..007c638 --- /dev/null +++ b/gdb/arch/loongarch.c @@ -0,0 +1,84 @@ +/* Copyright (C) 2021 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 "gdbsupport/common-defs.h" +#include "gdbsupport/common-regcache.h" +#include "arch/loongarch.h" + +const char *loongarch_expedite_regs[] = { "r3", "pc", NULL }; + +unsigned int loongarch_debug = 0; + +#include <../features/loongarch/base32.c> +#include <../features/loongarch/base64.c> +#include <../features/loongarch/fpu32.c> +#include <../features/loongarch/fpu64.c> +#include <../features/loongarch/lbt32.c> +#include <../features/loongarch/lbt64.c> +#include <../features/loongarch/lsx.c> +#include <../features/loongarch/lasx.c> + +target_desc * +loongarch_create_target_description (int rlen, int fpu32, int fpu64, int lbt, + int lsx, int lasx) +{ + gdb_assert (rlen == 32 || rlen == 64); + + target_desc_up tdesc = allocate_target_description (); + + set_tdesc_architecture (tdesc.get (), + rlen == 64 ? "loongarch64" : "loongarch32"); + + int regnum = 0; + + if (rlen == 64) + regnum = create_feature_loongarch_base64 (tdesc.get (), regnum); + else if (rlen == 32) + regnum = create_feature_loongarch_base32 (tdesc.get (), regnum); + else + gdb_assert_not_reached ("rlen unknown"); + + if (fpu32) + regnum = create_feature_loongarch_fpu32 (tdesc.get (), regnum); + else if (fpu64) + regnum = create_feature_loongarch_fpu64 (tdesc.get (), regnum); + + if (lbt && rlen == 32) + regnum = create_feature_loongarch_lbt32 (tdesc.get (), regnum); + else if (lbt && rlen == 64) + regnum = create_feature_loongarch_lbt64 (tdesc.get (), regnum); + + if (lsx) + regnum = create_feature_loongarch_lsx (tdesc.get (), regnum); + + if (lasx) + regnum = create_feature_loongarch_lasx (tdesc.get (), regnum); + + return tdesc.release (); +} + +target_desc * +loongarch_get_base_target_description (int rlen) +{ + if (rlen == 64) + return loongarch_create_target_description (64, 0, 0, 0, 0, 0); + else if (rlen == 32) + return loongarch_create_target_description (32, 0, 0, 0, 0, 0); + else + gdb_assert_not_reached ("rlen unknown"); + return NULL; +} diff --git a/gdb/arch/loongarch.h b/gdb/arch/loongarch.h new file mode 100644 index 0000000..9d9f65b --- /dev/null +++ b/gdb/arch/loongarch.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2021 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 . */ + +#ifndef ARCH_LOONGARCH_H +#define ARCH_LOONGARCH_H + +#include "elf/loongarch.h" +#include "opcode/loongarch.h" + +extern unsigned int loongarch_debug; + +struct target_desc; + +extern const char *loongarch_expedite_regs[]; + +extern struct target_desc *loongarch_get_base_target_description (int rlen); + +extern struct target_desc * +loongarch_create_target_description (int rlen, int fpu32, int fpu64, int lbt, + int lsx, int lasx); + +#endif diff --git a/gdb/configure.host b/gdb/configure.host index e94a19b..21a4310 100644 --- a/gdb/configure.host +++ b/gdb/configure.host @@ -64,6 +64,7 @@ arc*) gdb_host_cpu=arc ;; arm*) gdb_host_cpu=arm ;; hppa*) gdb_host_cpu=pa ;; i[34567]86*) gdb_host_cpu=i386 ;; +loongarch*) gdb_host_cpu=loongarch ;; m68*) gdb_host_cpu=m68k ;; mips*) gdb_host_cpu=mips ;; powerpc* | rs6000) gdb_host_cpu=powerpc ;; @@ -123,6 +124,8 @@ i[34567]86-*-cygwin*) gdb_host=cygwin ;; ia64-*-linux*) gdb_host=linux ;; +loongarch*-linux*) gdb_host=linux ;; + m68*-*-linux*) gdb_host=linux ;; m68*-*-netbsdelf* | m68*-*-knetbsd*-gnu) gdb_host=nbsdelf ;; diff --git a/gdb/configure.nat b/gdb/configure.nat index e34cccf..ce5cbc7 100644 --- a/gdb/configure.nat +++ b/gdb/configure.nat @@ -258,6 +258,10 @@ case ${gdb_host} in # Host: Intel IA-64 running GNU/Linux NATDEPFILES="${NATDEPFILES} ia64-linux-nat.o" ;; + loongarch) + # Host: LoongArch, running GNU/Linux. + NATDEPFILES="${NATDEPFILES} loongarch-linux-nat.o arch/loongarch-linux-nat.o linux-nat-trad.o nat/loongarch-linux-watch.o" + ;; m32r) # Host: M32R based machine running GNU/Linux NATDEPFILES="${NATDEPFILES} m32r-linux-nat.o" diff --git a/gdb/configure.tgt b/gdb/configure.tgt index 97a5a57..0b24ee2 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -97,6 +97,9 @@ xtensa*) cpu_obs="xtensa-tdep.o xtensa-config.o solib-svr4.o" ;; +loongarch*) + cpu_obs="arch/loongarch.o";; + esac # 2. Get the objects per os in $TARG. @@ -345,6 +348,11 @@ lm32-*-*) gdb_sim=../sim/lm32/libsim.a ;; +loongarch*-linux*) + gdb_target_obs="loongarch-tdep.o loongarch-linux-tdep.o glibc-tdep.o linux-tdep.o solib-svr4.o symfile-mem.o" + build_gdbserver=yes + ;; + m32c-*-*) # Target: Renesas M32C family gdb_target_obs="m32c-tdep.o" diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 8608c86..d563d59 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -46047,6 +46047,7 @@ registers using the capitalization used in the description. * ARC Features:: * ARM Features:: * i386 Features:: +* LoongArch Features:: * MicroBlaze Features:: * MIPS Features:: * M68K Features:: @@ -46264,6 +46265,15 @@ The @samp{org.gnu.gdb.i386.pkeys} feature is optional. It should describe a single register, @samp{pkru}. It is a 32-bit register valid for i386 and amd64. +@node LoongArch Features +@subsection LoongArch Features +@cindex target descriptions, LoongArch Features + +The @samp{org.gnu.gdb.loongarch.base} feature is required for LoongArch +targets. It should contain the registers @samp{r0} through @samp{r31}, +@samp{pc}, and @samp{badvaddr}. Either the architectural names (@samp{r0}, +@samp{r1}, etc) can be used, or the ABI names (@samp{zero}, @samp{ra}, etc). + @node MicroBlaze Features @subsection MicroBlaze Features @cindex target descriptions, MicroBlaze features diff --git a/gdb/features/Makefile b/gdb/features/Makefile index ded8c3b..504e689 100644 --- a/gdb/features/Makefile +++ b/gdb/features/Makefile @@ -74,6 +74,7 @@ arm-expedite = r11,sp,pc i386-expedite = ebp,esp,eip amd64-expedite = rbp,rsp,rip x32-expedite = rbp,rsp,rip +loongarch-expedite = r3,pc mips-expedite = r29,pc mips-dsp-expedite = r29,pc mips64-expedite = r29,pc @@ -180,6 +181,7 @@ GDB = false aarch64-feature = 1 arm-feature = 1 i386-feature = 1 +loongarch-feature = 1 riscv-feature = 1 tic6x-feature = 1 @@ -231,6 +233,14 @@ FEATURE_XMLFILES = aarch64-core.xml \ i386/64bit-pkeys.xml \ i386/64bit-sse.xml \ i386/x32-core.xml \ + loongarch/base32.xml \ + loongarch/base64.xml \ + loongarch/fpu32.xml \ + loongarch/fpu64.xml \ + loongarch/lbt32.xml \ + loongarch/lbt64.xml \ + loongarch/lsx.xml \ + loongarch/lasx.xml \ riscv/rv32e-xregs.xml \ riscv/32bit-cpu.xml \ riscv/32bit-fpu.xml \ diff --git a/gdb/features/loongarch/base32.c b/gdb/features/loongarch/base32.c new file mode 100644 index 0000000..b6f2d06 --- /dev/null +++ b/gdb/features/loongarch/base32.c @@ -0,0 +1,47 @@ +/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro: + Original: base32.xml */ + +#include "gdbsupport/tdesc.h" + +static int +create_feature_loongarch_base32 (struct target_desc *result, long regnum) +{ + struct tdesc_feature *feature; + + feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.base"); + tdesc_create_reg (feature, "r0", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r1", regnum++, 1, "general", 32, "code_ptr"); + tdesc_create_reg (feature, "r2", regnum++, 1, "general", 32, "data_ptr"); + tdesc_create_reg (feature, "r3", regnum++, 1, "general", 32, "data_ptr"); + tdesc_create_reg (feature, "r4", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r5", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r6", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r7", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r8", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r9", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r10", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r11", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r12", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r13", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r14", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r15", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r16", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r17", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r18", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r19", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r20", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r21", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r22", regnum++, 1, "general", 32, "data_ptr"); + tdesc_create_reg (feature, "r23", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r24", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r25", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r26", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r27", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r28", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r29", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r30", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "r31", regnum++, 1, "general", 32, "uint32"); + tdesc_create_reg (feature, "pc", regnum++, 1, "general", 32, "code_ptr"); + tdesc_create_reg (feature, "badvaddr", regnum++, 1, "general", 32, "code_ptr"); + return regnum; +} diff --git a/gdb/features/loongarch/base32.xml b/gdb/features/loongarch/base32.xml new file mode 100644 index 0000000..0afe81b --- /dev/null +++ b/gdb/features/loongarch/base32.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb/features/loongarch/base64.c b/gdb/features/loongarch/base64.c new file mode 100644 index 0000000..3ee2d9a --- /dev/null +++ b/gdb/features/loongarch/base64.c @@ -0,0 +1,47 @@ +/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro: + Original: base64.xml */ + +#include "gdbsupport/tdesc.h" + +static int +create_feature_loongarch_base64 (struct target_desc *result, long regnum) +{ + struct tdesc_feature *feature; + + feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.base"); + tdesc_create_reg (feature, "r0", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r1", regnum++, 1, "general", 64, "code_ptr"); + tdesc_create_reg (feature, "r2", regnum++, 1, "general", 64, "data_ptr"); + tdesc_create_reg (feature, "r3", regnum++, 1, "general", 64, "data_ptr"); + tdesc_create_reg (feature, "r4", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r5", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r6", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r7", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r8", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r9", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r10", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r11", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r12", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r13", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r14", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r15", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r16", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r17", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r18", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r19", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r20", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r21", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r22", regnum++, 1, "general", 64, "data_ptr"); + tdesc_create_reg (feature, "r23", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r24", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r25", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r26", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r27", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r28", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r29", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r30", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "r31", regnum++, 1, "general", 64, "uint64"); + tdesc_create_reg (feature, "pc", regnum++, 1, "general", 64, "code_ptr"); + tdesc_create_reg (feature, "badvaddr", regnum++, 1, "general", 64, "code_ptr"); + return regnum; +} diff --git a/gdb/features/loongarch/base64.xml b/gdb/features/loongarch/base64.xml new file mode 100644 index 0000000..b53479f --- /dev/null +++ b/gdb/features/loongarch/base64.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb/features/loongarch/fpu32.c b/gdb/features/loongarch/fpu32.c new file mode 100644 index 0000000..bf8964a --- /dev/null +++ b/gdb/features/loongarch/fpu32.c @@ -0,0 +1,54 @@ +/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro: + Original: fpu32.xml */ + +#include "gdbsupport/tdesc.h" + +static int +create_feature_loongarch_fpu32 (struct target_desc *result, long regnum) +{ + struct tdesc_feature *feature; + + feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.fpu"); + tdesc_create_reg (feature, "f0", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f1", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f2", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f3", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f4", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f5", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f6", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f7", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f8", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f9", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f10", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f11", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f12", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f13", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f14", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f15", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f16", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f17", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f18", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f19", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f20", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f21", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f22", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f23", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f24", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f25", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f26", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f27", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f28", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f29", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f30", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "f31", regnum++, 1, "float", 32, "ieee_single"); + tdesc_create_reg (feature, "fcc0", regnum++, 1, "float", 8, "uint8"); + tdesc_create_reg (feature, "fcc1", regnum++, 1, "float", 8, "uint8"); + tdesc_create_reg (feature, "fcc2", regnum++, 1, "float", 8, "uint8"); + tdesc_create_reg (feature, "fcc3", regnum++, 1, "float", 8, "uint8"); + tdesc_create_reg (feature, "fcc4", regnum++, 1, "float", 8, "uint8"); + tdesc_create_reg (feature, "fcc5", regnum++, 1, "float", 8, "uint8"); + tdesc_create_reg (feature, "fcc6", regnum++, 1, "float", 8, "uint8"); + tdesc_create_reg (feature, "fcc7", regnum++, 1, "float", 8, "uint8"); + tdesc_create_reg (feature, "fcsr", regnum++, 1, "float", 32, "uint32"); + return regnum; +} diff --git a/gdb/features/loongarch/fpu32.xml b/gdb/features/loongarch/fpu32.xml new file mode 100644 index 0000000..8421730 --- /dev/null +++ b/gdb/features/loongarch/fpu32.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb/features/loongarch/fpu64.c b/gdb/features/loongarch/fpu64.c new file mode 100644 index 0000000..f9e24c3 --- /dev/null +++ b/gdb/features/loongarch/fpu64.c @@ -0,0 +1,62 @@ +/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro: + Original: fpu64.xml */ + +#include "gdbsupport/tdesc.h" + +static int +create_feature_loongarch_fpu64 (struct target_desc *result, long regnum) +{ + struct tdesc_feature *feature; + + feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.fpu"); + tdesc_type_with_fields *type_with_fields; + type_with_fields = tdesc_create_union (feature, "fpu64type"); + tdesc_type *field_type; + field_type = tdesc_named_type (feature, "ieee_single"); + tdesc_add_field (type_with_fields, "f", field_type); + field_type = tdesc_named_type (feature, "ieee_double"); + tdesc_add_field (type_with_fields, "d", field_type); + + tdesc_create_reg (feature, "f0", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f1", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f2", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f3", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f4", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f5", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f6", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f7", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f8", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f9", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f10", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f11", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f12", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f13", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f14", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f15", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f16", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f17", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f18", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f19", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f20", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f21", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f22", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f23", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f24", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f25", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f26", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f27", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f28", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f29", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f30", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "f31", regnum++, 1, "float", 64, "fpu64type"); + tdesc_create_reg (feature, "fcc0", regnum++, 1, "float", 8, "uint8"); + tdesc_create_reg (feature, "fcc1", regnum++, 1, "float", 8, "uint8"); + tdesc_create_reg (feature, "fcc2", regnum++, 1, "float", 8, "uint8"); + tdesc_create_reg (feature, "fcc3", regnum++, 1, "float", 8, "uint8"); + tdesc_create_reg (feature, "fcc4", regnum++, 1, "float", 8, "uint8"); + tdesc_create_reg (feature, "fcc5", regnum++, 1, "float", 8, "uint8"); + tdesc_create_reg (feature, "fcc6", regnum++, 1, "float", 8, "uint8"); + tdesc_create_reg (feature, "fcc7", regnum++, 1, "float", 8, "uint8"); + tdesc_create_reg (feature, "fcsr", regnum++, 1, "float", 32, "uint32"); + return regnum; +} diff --git a/gdb/features/loongarch/fpu64.xml b/gdb/features/loongarch/fpu64.xml new file mode 100644 index 0000000..420be8b --- /dev/null +++ b/gdb/features/loongarch/fpu64.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb/features/loongarch/lasx.c b/gdb/features/loongarch/lasx.c new file mode 100644 index 0000000..96e3ea9 --- /dev/null +++ b/gdb/features/loongarch/lasx.c @@ -0,0 +1,80 @@ +/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro: + Original: lasx.xml */ + +#include "gdbsupport/tdesc.h" + +static int +create_feature_loongarch_lasx (struct target_desc *result, long regnum) +{ + struct tdesc_feature *feature; + + feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.lasx"); + tdesc_type *element_type; + element_type = tdesc_named_type (feature, "int8"); + tdesc_create_vector (feature, "v32i8", element_type, 32); + + element_type = tdesc_named_type (feature, "int16"); + tdesc_create_vector (feature, "v16i16", element_type, 16); + + element_type = tdesc_named_type (feature, "int32"); + tdesc_create_vector (feature, "v8i32", element_type, 8); + + element_type = tdesc_named_type (feature, "int64"); + tdesc_create_vector (feature, "v4i64", element_type, 4); + + element_type = tdesc_named_type (feature, "ieee_single"); + tdesc_create_vector (feature, "v8f32", element_type, 8); + + element_type = tdesc_named_type (feature, "ieee_double"); + tdesc_create_vector (feature, "v4f64", element_type, 4); + + tdesc_type_with_fields *type_with_fields; + type_with_fields = tdesc_create_union (feature, "lasxv"); + tdesc_type *field_type; + field_type = tdesc_named_type (feature, "v32i8"); + tdesc_add_field (type_with_fields, "v32i8", field_type); + field_type = tdesc_named_type (feature, "v16i16"); + tdesc_add_field (type_with_fields, "v16i16", field_type); + field_type = tdesc_named_type (feature, "v8i32"); + tdesc_add_field (type_with_fields, "v8i32", field_type); + field_type = tdesc_named_type (feature, "v4i64"); + tdesc_add_field (type_with_fields, "v4i64", field_type); + field_type = tdesc_named_type (feature, "v8f32"); + tdesc_add_field (type_with_fields, "v8f32", field_type); + field_type = tdesc_named_type (feature, "v4f64"); + tdesc_add_field (type_with_fields, "v4f64", field_type); + + tdesc_create_reg (feature, "xr0", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr1", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr2", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr3", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr4", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr5", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr6", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr7", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr8", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr9", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr10", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr11", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr12", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr13", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr14", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr15", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr16", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr17", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr18", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr19", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr20", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr21", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr22", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr23", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr24", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr25", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr26", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr27", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr28", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr29", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr30", regnum++, 1, "lasx", 256, "lasxv"); + tdesc_create_reg (feature, "xr31", regnum++, 1, "lasx", 256, "lasxv"); + return regnum; +} diff --git a/gdb/features/loongarch/lasx.xml b/gdb/features/loongarch/lasx.xml new file mode 100644 index 0000000..6f73df0 --- /dev/null +++ b/gdb/features/loongarch/lasx.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb/features/loongarch/lbt32.c b/gdb/features/loongarch/lbt32.c new file mode 100644 index 0000000..d245c75 --- /dev/null +++ b/gdb/features/loongarch/lbt32.c @@ -0,0 +1,19 @@ +/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro: + Original: lbt32.xml */ + +#include "gdbsupport/tdesc.h" + +static int +create_feature_loongarch_lbt32 (struct target_desc *result, long regnum) +{ + struct tdesc_feature *feature; + + feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.lbt"); + tdesc_create_reg (feature, "scr0", regnum++, 1, "lbt", 32, "uint32"); + tdesc_create_reg (feature, "scr1", regnum++, 1, "lbt", 32, "uint32"); + tdesc_create_reg (feature, "scr2", regnum++, 1, "lbt", 32, "uint32"); + tdesc_create_reg (feature, "scr3", regnum++, 1, "lbt", 32, "uint32"); + tdesc_create_reg (feature, "EFLAG", regnum++, 1, "lbt", 32, "uint32"); + tdesc_create_reg (feature, "x86_top", regnum++, 1, "lbt", 8, "uint8"); + return regnum; +} diff --git a/gdb/features/loongarch/lbt32.xml b/gdb/features/loongarch/lbt32.xml new file mode 100644 index 0000000..1c0133e --- /dev/null +++ b/gdb/features/loongarch/lbt32.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/gdb/features/loongarch/lbt64.c b/gdb/features/loongarch/lbt64.c new file mode 100644 index 0000000..ecef330 --- /dev/null +++ b/gdb/features/loongarch/lbt64.c @@ -0,0 +1,19 @@ +/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro: + Original: lbt64.xml */ + +#include "gdbsupport/tdesc.h" + +static int +create_feature_loongarch_lbt64 (struct target_desc *result, long regnum) +{ + struct tdesc_feature *feature; + + feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.lbt"); + tdesc_create_reg (feature, "scr0", regnum++, 1, "lbt", 64, "uint64"); + tdesc_create_reg (feature, "scr1", regnum++, 1, "lbt", 64, "uint64"); + tdesc_create_reg (feature, "scr2", regnum++, 1, "lbt", 64, "uint64"); + tdesc_create_reg (feature, "scr3", regnum++, 1, "lbt", 64, "uint64"); + tdesc_create_reg (feature, "EFLAG", regnum++, 1, "lbt", 32, "uint32"); + tdesc_create_reg (feature, "x86_top", regnum++, 1, "lbt", 8, "uint8"); + return regnum; +} diff --git a/gdb/features/loongarch/lbt64.xml b/gdb/features/loongarch/lbt64.xml new file mode 100644 index 0000000..1df26f5 --- /dev/null +++ b/gdb/features/loongarch/lbt64.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/gdb/features/loongarch/lsx.c b/gdb/features/loongarch/lsx.c new file mode 100644 index 0000000..dd253f3 --- /dev/null +++ b/gdb/features/loongarch/lsx.c @@ -0,0 +1,80 @@ +/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro: + Original: lsx.xml */ + +#include "gdbsupport/tdesc.h" + +static int +create_feature_loongarch_lsx (struct target_desc *result, long regnum) +{ + struct tdesc_feature *feature; + + feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.lsx"); + tdesc_type *element_type; + element_type = tdesc_named_type (feature, "int8"); + tdesc_create_vector (feature, "v16i8", element_type, 16); + + element_type = tdesc_named_type (feature, "int16"); + tdesc_create_vector (feature, "v8i16", element_type, 8); + + element_type = tdesc_named_type (feature, "int32"); + tdesc_create_vector (feature, "v4i32", element_type, 4); + + element_type = tdesc_named_type (feature, "int64"); + tdesc_create_vector (feature, "v2i64", element_type, 2); + + element_type = tdesc_named_type (feature, "ieee_single"); + tdesc_create_vector (feature, "v4f32", element_type, 4); + + element_type = tdesc_named_type (feature, "ieee_double"); + tdesc_create_vector (feature, "v2f64", element_type, 2); + + tdesc_type_with_fields *type_with_fields; + type_with_fields = tdesc_create_union (feature, "lsxv"); + tdesc_type *field_type; + field_type = tdesc_named_type (feature, "v16i8"); + tdesc_add_field (type_with_fields, "v16i8", field_type); + field_type = tdesc_named_type (feature, "v8i16"); + tdesc_add_field (type_with_fields, "v8i16", field_type); + field_type = tdesc_named_type (feature, "v4i32"); + tdesc_add_field (type_with_fields, "v4i32", field_type); + field_type = tdesc_named_type (feature, "v2i64"); + tdesc_add_field (type_with_fields, "v2i64", field_type); + field_type = tdesc_named_type (feature, "v4f32"); + tdesc_add_field (type_with_fields, "v4f32", field_type); + field_type = tdesc_named_type (feature, "v2f64"); + tdesc_add_field (type_with_fields, "v2f64", field_type); + + tdesc_create_reg (feature, "vr0", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr1", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr2", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr3", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr4", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr5", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr6", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr7", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr8", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr9", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr10", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr11", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr12", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr13", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr14", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr15", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr16", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr17", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr18", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr19", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr20", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr21", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr22", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr23", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr24", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr25", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr26", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr27", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr28", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr29", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr30", regnum++, 1, "lsx", 128, "lsxv"); + tdesc_create_reg (feature, "vr31", regnum++, 1, "lsx", 128, "lsxv"); + return regnum; +} diff --git a/gdb/features/loongarch/lsx.xml b/gdb/features/loongarch/lsx.xml new file mode 100644 index 0000000..a0a6ba4 --- /dev/null +++ b/gdb/features/loongarch/lsx.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb/loongarch-linux-nat.c b/gdb/loongarch-linux-nat.c new file mode 100644 index 0000000..f31eff4 --- /dev/null +++ b/gdb/loongarch-linux-nat.c @@ -0,0 +1,876 @@ +/* Target-dependent code for GNU/Linux LoongArch. + + Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Loongson Ltd. + + 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 "command.h" +#include "gdbcmd.h" +#include "inferior.h" +#include "loongarch-tdep.h" +#include "target.h" +#include "regcache.h" +#include "linux-nat-trad.h" +#include "loongarch-linux-tdep.h" +#include "target-descriptions.h" + +#include "gdb_proc_service.h" +#include "gregset.h" + +#include "nat/gdb_ptrace.h" +#include +#include "inf-ptrace.h" +#include "arch/loongarch-linux-nat.h" +#include "elf/common.h" + +#include "nat/loongarch-linux-watch.h" + +class loongarch_linux_nat_target final : public linux_nat_trad_target +{ +public: + void fetch_registers (struct regcache *, int) override; + void store_registers (struct regcache *, int) override; + + void close () override; + + int can_use_hw_breakpoint (enum bptype, int, int) override; + + int insert_hw_breakpoint (struct gdbarch *, + struct bp_target_info *) override; + + int remove_hw_breakpoint (struct gdbarch *, + struct bp_target_info *) override; + + int remove_watchpoint (CORE_ADDR, int, enum target_hw_bp_type, + struct expression *) override; + + int insert_watchpoint (CORE_ADDR, int, enum target_hw_bp_type, + struct expression *) override; + + bool stopped_by_watchpoint () override; + + bool stopped_data_address (CORE_ADDR *) override; + + int region_ok_for_hw_watchpoint (CORE_ADDR, int) override; + + const struct target_desc *read_description () override; + + enum target_xfer_status xfer_partial (enum target_object object, + const char *annex, gdb_byte *readbuf, + const gdb_byte *writebuf, + ULONGEST offset, ULONGEST len, + ULONGEST *xfered_len) override; + +protected: + CORE_ADDR register_u_offset (struct gdbarch *gdbarch, int regno, + int store_p) override; + + void low_new_thread (struct lwp_info *lp) override; +}; + +const struct target_desc * +loongarch_linux_nat_target::read_description () +{ + return loongarch_linux_read_description_runtime (inferior_ptid.pid ()); +} + +/* Fill GDB's register array with the general-purpose, pc and badvaddr + register values from the current thread. */ + +static void +fetch_gregs_from_thread (struct regcache *regcache, int regno, pid_t tid) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + loongarch_elf_gregset_t regset; + + if ((regno == -1) || (regs->r <= regno && regno < regs->r + 32) || + (regs->pc == regno) || (regs->badvaddr == regno)) + { + struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) }; + + if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, (long) &iovec) < 0) + perror_with_name (_("Couldn't get NT_PRSTATUS registers")); + else + loongarch_elf_gregset.supply_regset (NULL, regcache, regno, ®set, + sizeof (regset)); + } +} + +/* Store to the current thread the valid general-purpose, pc and badvaddr + register values in the GDB's register array. */ + +static void +store_gregs_to_thread (struct regcache *regcache, int regno, pid_t tid) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + loongarch_elf_gregset_t regset; + + if ((regno == -1) || (regs->r <= regno && regno < regs->r + 32) || + (regs->pc == regno) || (regs->badvaddr == regno)) + { + struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) }; + + if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, (long) &iovec) < 0) + perror_with_name (_("Couldn't get NT_PRSTATUS registers")); + else + { + loongarch_elf_gregset.collect_regset (NULL, regcache, regno, ®set, + sizeof (regset)); + if (ptrace (PTRACE_SETREGSET, tid, NT_PRSTATUS, (long) &iovec) < 0) + perror_with_name (_("Couldn't set NT_PRSTATUS registers")); + } + } +} + +/* Fill GDB's register array with the fp, fcc and fcsr + register values from the current thread. */ + +static void +fetch_fpregs_from_thread (struct regcache *regcache, int regno, pid_t tid) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + loongarch_elf_fpregset_t regset; + + if ((regno == -1) || (regs->f <= regno && regno < regs->f + 32) || + (regs->fcc <= regno && regno < regs->fcc + 8) || (regs->fcsr == regno)) + { + struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) }; + + if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0) + perror_with_name (_("Couldn't get NT_FPREGSET registers")); + else + loongarch_elf_fpregset.supply_regset (NULL, regcache, regno, ®set, + sizeof (regset)); + } +} + +/* Store to the current thread the valid fp, fcc and fcsr + register values in the GDB's register array. */ + +static void +store_fpregs_to_thread (struct regcache *regcache, int regno, pid_t tid) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + loongarch_elf_fpregset_t regset; + + if ((regno == -1) || (regs->f <= regno && regno < regs->f + 32) || + (regs->fcc <= regno && regno < regs->fcc + 8) || (regs->fcsr == regno)) + { + struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) }; + + if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0) + perror_with_name (_("Couldn't get NT_FPREGSET registers")); + else + { + loongarch_elf_fpregset.collect_regset (NULL, regcache, regno, ®set, + sizeof (regset)); + if (ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0) + perror_with_name (_("Couldn't set NT_FPREGSET registers")); + } + } +} + +/* Fill GDB's register array with the Binary Translation + register values from the current thread. */ + +static void +fetch_lbtregs_from_thread (struct regcache *regcache, int regno, pid_t tid) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + loongarch_elf_lbtregset_t regset; + + if ((regno == -1) || (regs->scr <= regno && regno < regs->scr + 4) || + (regs->EFLAG == regno) || (regs->x86_top == regno)) + { + struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) }; + + if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LBT, (long) &iovec) < 0) + perror_with_name (_("Couldn't get LBT registers")); + else + loongarch_elf_lbtregset.supply_regset (NULL, regcache, regno, ®set, + sizeof (regset)); + } +} + +/* Store to the current thread the valid Binary Translation + register values in the GDB's register array. */ + +static void +store_lbtregs_to_thread (struct regcache *regcache, int regno, pid_t tid) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + loongarch_elf_lbtregset_t regset; + + if ((regno == -1) || (regs->scr <= regno && regno < regs->scr + 4) || + (regs->EFLAG == regno) || (regs->x86_top == regno)) + { + struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) }; + + if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LBT, (long) &iovec) < 0) + perror_with_name (_("Couldn't get LBT registers")); + else + { + loongarch_elf_lbtregset.collect_regset (NULL, regcache, regno, ®set, + sizeof (regset)); + if (ptrace (PTRACE_SETREGSET, tid, NT_LARCH_LBT, (long) &iovec) < 0) + perror_with_name (_("Couldn't set LBT registers")); + } + } +} + +/* Fill GDB's register array with the SIMD eXtension + register values from the current thread. */ + +static void +fetch_lsxregs_from_thread (struct regcache *regcache, int regno, pid_t tid) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + loongarch_elf_lsxregset_t regset; + + if ((regno == -1) || (regs->vr <= regno && regno < regs->vr + 32)) + { + struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) }; + + if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LSX, (long) &iovec) < 0) + perror_with_name (_("Couldn't get LSX registers")); + else + loongarch_elf_lsxregset.supply_regset (NULL, regcache, regno, ®set, + sizeof (regset)); + } +} + +/* Store to the current thread the valid SIMD eXtension + register values in the GDB's register array. */ + +static void +store_lsxregs_to_thread (struct regcache *regcache, int regno, pid_t tid) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + loongarch_elf_lsxregset_t regset; + + if ((regno == -1) || (regs->vr <= regno && regno < regs->vr + 32)) + { + struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) }; + + if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LSX, (long) &iovec) < 0) + perror_with_name (_("Couldn't get LSX registers")); + else + { + loongarch_elf_lsxregset.collect_regset (NULL, regcache, regno, ®set, + sizeof (regset)); + if (ptrace (PTRACE_SETREGSET, tid, NT_LARCH_LSX, (long) &iovec) < 0) + perror_with_name (_("Couldn't set LSX registers")); + } + } +} + +/* Fill GDB's register array with the Advanced SIMD eXtension + register values from the current thread. */ + +static void +fetch_lasxregs_from_thread (struct regcache *regcache, int regno, pid_t tid) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + loongarch_elf_lasxregset_t regset; + + if ((regno == -1) || (regs->xr <= regno && regno < regs->xr + 32)) + { + struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) }; + + if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LASX, (long) &iovec) < 0) + perror_with_name (_("Couldn't get LASX registers")); + else + loongarch_elf_lasxregset.supply_regset (NULL, regcache, regno, ®set, + sizeof (regset)); + } +} + +/* Store to the current thread the valid Advanced SIMD eXtension + register values in the GDB's register array. */ + +static void +store_lasxregs_to_thread (struct regcache *regcache, int regno, pid_t tid) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + loongarch_elf_lasxregset_t regset; + + if ((regno == -1) || (regs->xr <= regno && regno < regs->xr + 32)) + { + struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) }; + + if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LSX, (long) &iovec) < 0) + perror_with_name (_("Couldn't get LSX registers")); + else + { + loongarch_elf_lsxregset.collect_regset (NULL, regcache, regno, ®set, + sizeof (regset)); + if (ptrace (PTRACE_SETREGSET, tid, NT_LARCH_LSX, (long) &iovec) < 0) + perror_with_name (_("Couldn't set LSX registers")); + } + } +} + +/* Implement the "fetch_registers" target_ops method. */ + +void +loongarch_linux_nat_target::fetch_registers (struct regcache *regcache, + int regno) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + pid_t tid = get_ptrace_pid (regcache->ptid ()); + + fetch_gregs_from_thread(regcache, regno, tid); + + if (regs->f > 0) + fetch_fpregs_from_thread(regcache, regno, tid); + + if (regs->scr > 0) + fetch_lbtregs_from_thread(regcache, regno, tid); + + if (regs->vr > 0) + fetch_lsxregs_from_thread(regcache, regno, tid); + + if (regs->xr > 0) + fetch_lasxregs_from_thread(regcache, regno, tid); +} + +/* Implement the "store_registers" target_ops method. */ + +void +loongarch_linux_nat_target::store_registers (struct regcache *regcache, + int regno) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + pid_t tid = get_ptrace_pid (regcache->ptid ()); + + store_gregs_to_thread(regcache, regno, tid); + + if (regs->f > 0) + store_fpregs_to_thread(regcache, regno, tid); + + if (regs->scr > 0) + store_lbtregs_to_thread(regcache, regno, tid); + + if (regs->vr > 0) + store_lsxregs_to_thread(regcache, regno, tid); + + if (regs->xr > 0) + store_lasxregs_to_thread(regcache, regno, tid); +} + +/* Return the address in the core dump or inferior of register REGNO. */ + +CORE_ADDR +loongarch_linux_nat_target::register_u_offset (struct gdbarch *gdbarch, + int regno, int store_p) +{ + auto regs = &gdbarch_tdep (gdbarch)->regs; + /* according to */ + if (0 <= regs->r && regs->r <= regno && regno < regs->r + GPR_NUM) + return GPR_BASE + regno - regs->r; + else if (regs->pc == regno) + return PC; + return -1; +} + +/* Implement the to_xfer_partial target_ops method. */ + +enum target_xfer_status +loongarch_linux_nat_target::xfer_partial (enum target_object object, + const char *annex, gdb_byte *readbuf, + const gdb_byte *writebuf, + ULONGEST offset, ULONGEST len, + ULONGEST *xfered_len) +{ + pid_t pid = inferior_ptid.pid (); + + if (object != TARGET_OBJECT_LARCH) + return linux_nat_trad_target::xfer_partial ( + object, annex, readbuf, writebuf, offset, len, xfered_len); + + if (strcmp (annex, "cpucfg") == 0) + { + if (writebuf) + return TARGET_XFER_E_IO; + if (offset % 4 != 0 || offset % 4 != 0) + return TARGET_XFER_E_IO; + char t_buf[offset + len]; + struct iovec iovec = { .iov_base = &t_buf, .iov_len = sizeof (t_buf) }; + if (ptrace (PTRACE_GETREGSET, pid, NT_LARCH_CPUCFG, &iovec) < 0) + { + size_t i; + for (i = offset / 4; i < (offset + len) / 4 + 1; i++) + ((uint32_t *) t_buf)[i] = loongarch_cpucfg (i); + } + memcpy (readbuf, t_buf + offset, len); + *xfered_len = len; + return TARGET_XFER_OK; + } + + return TARGET_XFER_E_IO; +} + +static loongarch_linux_nat_target the_loongarch_linux_nat_target; + +/* Wrapper functions. These are only used by libthread_db. */ + +void +supply_gregset (struct regcache *regcache, + const gdb_gregset_t /* elf_gregset_t */ *gregset) +{ + loongarch_elf_gregset.supply_regset (NULL, regcache, -1, gregset, + sizeof (gdb_gregset_t)); +} + +void +fill_gregset (const struct regcache *regcache, + gdb_gregset_t /* elf_gregset_t */ *gregset, int regno) +{ + loongarch_elf_gregset.collect_regset (NULL, regcache, regno, gregset, + sizeof (gdb_gregset_t)); +} + +void +supply_fpregset (struct regcache *regcache, + const gdb_fpregset_t /* elf_fpregset_t */ *fpregset) +{ + loongarch_elf_fpregset.supply_regset (NULL, regcache, -1, fpregset, + sizeof (gdb_fpregset_t)); +} + +void +fill_fpregset (const struct regcache *regcache, + gdb_fpregset_t /* elf_fpregset_t */ *fpregset, int regno) +{ + loongarch_elf_fpregset.collect_regset (NULL, regcache, regno, fpregset, + sizeof (gdb_fpregset_t)); +} + +/* -1 if the kernel and/or CPU do not support watch registers. + 1 if watch_readback is valid and we can read style, num_valid + and the masks. + 0 if we need to read the watch_readback. */ + +static int watch_readback_valid; + +/* Cached watch register read values. */ + +static struct pt_watch_regs watch_readback = +{ + .max_valid = MAX_DEBUG_REGISTER, +}; + +static struct loongarch_watchpoint *current_watches; + +/* The current set of watch register values for writing the + registers. */ + +static struct pt_watch_regs watch_mirror = +{ + .max_valid = MAX_DEBUG_REGISTER, +}; + +static void +loongarch_show_dr (const char *func, CORE_ADDR addr, int len, + enum target_hw_bp_type type) +{ + int i; + + puts_unfiltered (func); + if (addr || len) + printf_unfiltered ( + " (addr=%s, len=%d, type=%s)", paddress (target_gdbarch (), addr), len, + type == hw_write + ? "data-write" + : (type == hw_read + ? "data-read" + : (type == hw_access ? "data-read/write" + : (type == hw_execute ? "instruction-execute" + : "??unknown??")))); + puts_unfiltered (":\n"); + + for (i = 0; i < MAX_DEBUG_REGISTER; i++) + printf_unfiltered ( + "\tDR%d: addr=%s, mask=%s\n", i, + paddress (target_gdbarch (), + loongarch_linux_watch_get_addr (&watch_mirror, i)), + paddress (target_gdbarch (), + loongarch_linux_watch_get_mask (&watch_mirror, i))); +} + +/* Target to_can_use_hw_breakpoint implementation. Return 1 if we can + handle the specified watch type. */ + +int +loongarch_linux_nat_target::can_use_hw_breakpoint (enum bptype type, int cnt, + int ot) +{ + int i; + uint32_t wanted_mask, irw_mask; + long lwp = inferior_ptid.lwp (); + + if (!loongarch_linux_read_watch_registers (lwp, &watch_readback, + &watch_readback_valid, 0)) + return 0; + + switch (type) + { + case bp_hardware_watchpoint: + wanted_mask = W_MASK; + break; + case bp_read_watchpoint: + wanted_mask = R_MASK; + break; + case bp_access_watchpoint: + wanted_mask = R_MASK | W_MASK; + break; + case bp_hardware_breakpoint: + wanted_mask = I_MASK; + break; + default: + return 0; + } + + for (i = 0; i < loongarch_linux_watch_get_num_valid (&watch_readback) && cnt; + i++) + { + irw_mask = loongarch_linux_watch_get_irwmask (&watch_readback, i); + if ((irw_mask & wanted_mask) == wanted_mask) + cnt--; + } + return (cnt == 0) ? 1 : 0; +} + +/* Target to_stopped_by_watchpoint implementation. Return 1 if + stopped by watchpoint. The watchhi R and W bits indicate the watch + register triggered. */ + +bool +loongarch_linux_nat_target::stopped_by_watchpoint () +{ + int n; + int num_valid; + + if (!loongarch_linux_read_watch_registers ( + inferior_ptid.lwp (), &watch_readback, &watch_readback_valid, 1)) + return false; + + num_valid = loongarch_linux_watch_get_num_valid (&watch_readback); + + for (n = 0; n < MAX_DEBUG_REGISTER && n < num_valid; n++) + if (loongarch_linux_watch_get_irwstat (&watch_readback, n) + & (R_MASK | W_MASK)) + return true; + + return false; +} + +/* Target to_stopped_data_address implementation. Set the address + where the watch triggered (if known). Return 1 if the address was + known. */ + +bool +loongarch_linux_nat_target::stopped_data_address (CORE_ADDR *paddr) +{ + int num_valid, n; + if (!loongarch_linux_read_watch_registers ( + inferior_ptid.lwp (), &watch_readback, &watch_readback_valid, 1)) + return false; + + num_valid = loongarch_linux_watch_get_num_valid (&watch_readback); + + for (n = 0; n < MAX_DEBUG_REGISTER && n < num_valid; n++) + if (loongarch_linux_watch_get_irwstat (&watch_readback, n) + & (R_MASK | W_MASK)) + { + CORE_ADDR t_addr, t_mask; + int t_irw; + struct loongarch_watchpoint *watch; + + t_addr = loongarch_linux_watch_get_addr (&watch_readback, n); + t_irw = loongarch_linux_watch_get_irw (&watch_readback, n) & IRW_MASK; + t_mask = loongarch_linux_watch_get_mask (&watch_readback, n); + + for (watch = current_watches; watch != NULL; watch = watch->next) + { + CORE_ADDR addr = watch->addr; + CORE_ADDR last_byte = addr + watch->len - 1; + + if ((t_irw & loongarch_linux_watch_type_to_irw (watch->type)) == 0) + { + /* Different type. */ + continue; + } + /* Check for overlap of even a single byte. */ + if (last_byte >= t_addr && addr <= t_addr + t_mask) + { + *paddr = addr; + return true; + } + } + } + return false; +} + +/* Target to_region_ok_for_hw_watchpoint implementation. Return 1 if + the specified region can be covered by the watch registers. */ + +int +loongarch_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, + int len) +{ + struct pt_watch_regs dummy_regs; + int i; + + if (!loongarch_linux_read_watch_registers ( + inferior_ptid.lwp (), &watch_readback, &watch_readback_valid, 0)) + return 0; + + dummy_regs = watch_readback; + /* Clear them out. */ + for (i = 0; i < loongarch_linux_watch_get_num_valid (&dummy_regs); i++) + { + loongarch_linux_watch_set_addr (&dummy_regs, i, 0); + loongarch_linux_watch_set_mask (&dummy_regs, i, 0); + loongarch_linux_watch_set_irw (&dummy_regs, i, 0); + } + return loongarch_linux_watch_try_one_watch (&dummy_regs, addr, len, 0); +} + +/* Write the mirrored watch register values for each thread. */ + +static int +write_watchpoint_regs (void) +{ + struct lwp_info *lp; + int tid; + + ALL_LWPS (lp) + { + tid = lp->ptid.lwp (); + if (ptrace (PTRACE_SET_WATCH_REGS, tid, &watch_mirror, NULL) == -1) + perror_with_name (_ ("Couldn't write debug register")); + } + return 0; +} + +/* linux_nat_target::low_new_thread implementation. */ + +void +loongarch_linux_nat_target::low_new_thread (struct lwp_info *lp) +{ + long tid = lp->ptid.lwp (); + + if (!loongarch_linux_read_watch_registers (tid, &watch_readback, + &watch_readback_valid, 0)) + return; + + if (ptrace (PTRACE_SET_WATCH_REGS, tid, &watch_mirror, NULL) == -1) + perror_with_name (_ ("Couldn't write debug register")); +} + +/* Target to_insert_watchpoint implementation. Try to insert a new + watch. Return zero on success. */ + +int +loongarch_linux_nat_target::insert_watchpoint (CORE_ADDR addr, int len, + enum target_hw_bp_type type, + struct expression *cond) +{ + struct pt_watch_regs regs = + { + .max_valid = MAX_DEBUG_REGISTER, + }; + struct loongarch_watchpoint *new_watch; + struct loongarch_watchpoint **pw; + + int retval; + + if (!loongarch_linux_read_watch_registers ( + inferior_ptid.lwp (), &watch_readback, &watch_readback_valid, 0)) + return -1; + + if (len <= 0) + return -1; + + regs = watch_readback; + /* Add the current watches. */ + loongarch_linux_watch_populate_regs (current_watches, ®s); + + /* Now try to add the new watch. */ + if (!loongarch_linux_watch_try_one_watch ( + ®s, addr, len, loongarch_linux_watch_type_to_irw (type))) + return -1; + + /* It fit. Stick it on the end of the list. */ + new_watch = XNEW (struct loongarch_watchpoint); + new_watch->addr = addr; + new_watch->len = len; + new_watch->type = type; + new_watch->next = NULL; + + pw = ¤t_watches; + while (*pw != NULL) + pw = &(*pw)->next; + *pw = new_watch; + + watch_mirror = regs; + retval = write_watchpoint_regs (); + + if (show_debug_regs) + loongarch_show_dr ("insert_watchpoint", addr, len, type); + + return retval; +} + +/* Target to_remove_watchpoint implementation. Try to remove a watch. + Return zero on success. */ + +int +loongarch_linux_nat_target::remove_watchpoint (CORE_ADDR addr, int len, + enum target_hw_bp_type type, + struct expression *cond) +{ + int retval; + int deleted_one; + + struct loongarch_watchpoint **pw; + struct loongarch_watchpoint *w; + + /* Search for a known watch that matches. Then unlink and free + it. */ + deleted_one = 0; + pw = ¤t_watches; + while ((w = *pw)) + { + if (w->addr == addr && w->len == len && w->type == type) + { + *pw = w->next; + xfree (w); + deleted_one = 1; + break; + } + pw = &(w->next); + } + + if (!deleted_one) + return -1; /* We don't know about it, fail doing nothing. */ + + /* At this point watch_readback is known to be valid because we + could not have added the watch without reading it. */ + gdb_assert (watch_readback_valid == 1); + + watch_mirror = watch_readback; + loongarch_linux_watch_populate_regs (current_watches, &watch_mirror); + + retval = write_watchpoint_regs (); + + if (show_debug_regs) + loongarch_show_dr ("remove_watchpoint", addr, len, type); + + return retval; +} + +/* Insert a hardware-assisted breakpoint at BP_TGT->reqstd_address. + Return 0 on success, -1 on failure. */ + +int +loongarch_linux_nat_target::insert_hw_breakpoint ( + struct gdbarch *gdbarch, struct bp_target_info *bp_tgt) +{ + int ret; + CORE_ADDR addr = bp_tgt->placed_address = bp_tgt->reqstd_address; + int len = 4; + const enum target_hw_bp_type type = hw_execute; + + gdbarch_breakpoint_from_pc (gdbarch, &addr, &len); + + if (show_debug_regs) + fprintf_unfiltered ( + gdb_stdlog, "insert_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n", + (unsigned long) addr, len); + + ret = insert_watchpoint (addr, len, type, NULL); + return ret; +} + +/* Remove a hardware-assisted breakpoint at BP_TGT->placed_address. + Return 0 on success, -1 on failure. */ + +int +loongarch_linux_nat_target::remove_hw_breakpoint ( + struct gdbarch *gdbarch, struct bp_target_info *bp_tgt) +{ + int ret; + CORE_ADDR addr = bp_tgt->placed_address; + int len = 4; + const enum target_hw_bp_type type = hw_execute; + + gdbarch_breakpoint_from_pc (gdbarch, &addr, &len); + + if (show_debug_regs) + fprintf_unfiltered ( + gdb_stdlog, "remove_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n", + (unsigned long) addr, len); + + ret = remove_watchpoint (addr, len, type, NULL); + + return ret; +} + +/* Target to_close implementation. Free any watches and call the + super implementation. */ + +void +loongarch_linux_nat_target::close () +{ + struct loongarch_watchpoint *w; + struct loongarch_watchpoint *nw; + + /* Clean out the current_watches list. */ + w = current_watches; + while (w) + { + nw = w->next; + xfree (w); + w = nw; + } + current_watches = NULL; + + linux_nat_trad_target::close (); +} + +void _initialize_loongarch_linux_nat (); +void +_initialize_loongarch_linux_nat () +{ + add_setshow_boolean_cmd ( + "show-debug-regs", class_maintenance, &show_debug_regs, _ ("\ +Set whether to show variables that mirror the LoongArch debug registers."), + _ ("\ +Show whether to show variables that mirror the LoongArch debug registers."), + _ ("\ +Use \"on\" to enable, \"off\" to disable.\n\ +If enabled, the debug registers values are shown when GDB inserts\n\ +or removes a hardware breakpoint or watchpoint, and when the inferior\n\ +triggers a breakpoint or watchpoint."), + NULL, NULL, &maintenance_set_cmdlist, &maintenance_show_cmdlist); + + linux_target = &the_loongarch_linux_nat_target; + add_inf_child_target (&the_loongarch_linux_nat_target); +} diff --git a/gdb/loongarch-linux-tdep.c b/gdb/loongarch-linux-tdep.c new file mode 100644 index 0000000..1145d9e --- /dev/null +++ b/gdb/loongarch-linux-tdep.c @@ -0,0 +1,699 @@ +/* Target-dependent code for GNU/Linux LoongArch. + + Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Loongson Ltd. + + 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 "inferior.h" +#include "gdbcore.h" +#include "target.h" +#include "solib-svr4.h" +#include "osabi.h" +#include "loongarch-tdep.h" +#include "frame.h" +#include "regcache.h" +#include "trad-frame.h" +#include "tramp-frame.h" +#include "gdbtypes.h" +#include "objfiles.h" +#include "solib.h" +#include "solist.h" +#include "symtab.h" +#include "target-descriptions.h" +#include "loongarch-linux-tdep.h" +#include "glibc-tdep.h" +#include "linux-tdep.h" +#include "xml-syscall.h" +#include "gdbsupport/gdb_signals.h" + +static void +loongarch_supply_elf_gregset (const struct regset *r, + struct regcache *regcache, int regno, + const void *gprs, size_t len) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + gdb_assert (0 <= regs->r && sizeof (loongarch_elf_gregset_t) <= len); + + int regsize = register_size (regcache->arch (), regs->r); + const gdb_byte *buf = NULL; + + if (regno == -1) + { + /* Set $r0 = 0. */ + regcache->raw_supply_zeroed (regs->r); + + for (int i = 1; i < 32; i++) + { + buf = (const gdb_byte*)gprs + regsize * i; + regcache->raw_supply (regs->r + i, (const void *)buf); + } + + /* Size base (pc) = regsize * regs->pc. */ + buf = (const gdb_byte*)gprs + regsize * regs->pc; + regcache->raw_supply (regs->pc, (const void *)buf); + + /* Size base (badvaddr) = regsize * regs->badvaddr. */ + buf = (const gdb_byte*)gprs + regsize * regs->badvaddr; + regcache->raw_supply (regs->badvaddr, (const void *)buf); + } + else if (regs->r == regno) + regcache->raw_supply_zeroed (regno); + else if ((regs->r < regno && regno < regs->r + 32) + || (regs->pc == regno) + || (regs->badvaddr == regno)) + { + /* Offset offset (regno) = regsize * (regno - regs->r). */ + buf = (const gdb_byte*)gprs + regsize * (regno - regs->r); + regcache->raw_supply (regno, (const void *)buf); + } +} + +static void +loongarch_fill_elf_gregset (const struct regset *r, + const struct regcache *regcache, int regno, + void *gprs, size_t len) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + gdb_assert (0 <= regs->r && sizeof (loongarch_elf_gregset_t) <= len); + int regsize = register_size (regcache->arch (), regs->r); + gdb_byte *buf = NULL; + + if (regno == -1) + { + for (int i = 0; i < 32; i++) + { + buf = (gdb_byte *)gprs + regsize * i; + regcache->raw_collect (regs->r + i, (void *)buf); + } + + /* Size base (pc) = regsize * regs->pc. */ + buf = (gdb_byte *)gprs + regsize * regs->pc; + regcache->raw_collect (regs->pc, (void *)buf); + + /* Size base (badvaddr) = regsize * regs->badvaddr. */ + buf = (gdb_byte *)gprs + regsize * regs->badvaddr; + regcache->raw_collect (regs->badvaddr, (void *)buf); + } + else if ((regs->r <= regno && regno < regs->r + 32) + ||(regs->pc == regno) + ||(regs->badvaddr == regno)) + { + /* Offset offset (regno) = regsize * (regno - regs->r). */ + buf = (gdb_byte *)gprs + regsize * (regno - regs->r); + regcache->raw_collect (regno, (void *)buf); + } +} + +const struct regset loongarch_elf_gregset = +{ + NULL, + loongarch_supply_elf_gregset, + loongarch_fill_elf_gregset, +}; + +static void +loongarch_supply_elf_fpregset (const struct regset *r, + struct regcache *regcache, int regno, + const void *fprs, size_t len) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + gdb_assert (0 <= regs->f && sizeof (loongarch_elf_fpregset_t) <= len); + + const gdb_byte *buf = NULL; + int fprsize = register_size (regcache->arch (), regs->f); + int fccsize = register_size (regcache->arch (), regs->fcc); + + if (regno == -1) + { + /* 32 fprs. */ + for (int i = 0; i < 32; i++) + { + buf = (const gdb_byte *)fprs + fprsize * i; + regcache->raw_supply (regs->f + i, (const void *)buf); + } + + /* 8 fccs , base (fcc) = 32 * sizeof (fpr). */ + buf = (const gdb_byte *)fprs + fprsize * 32; + for (int i = 0; i < 8; i++) + { + regcache->raw_supply (regs->fcc + i, (const void *)buf); + buf += fccsize; + } + + /* Size base (fcsr) = 32 * sizeof (fpr) + 8 * sizeof (fcc). */ + buf = (const gdb_byte *)fprs + 32 * fprsize + 8 * fccsize; + regno = regs->fcsr; + } + else if (regs->f <= regno && regno < regs->f + 32) + { + /* Offset offset (regno - f) = (regno - regs->f) * sizeof (fpr). */ + buf = (const gdb_byte *)fprs + fprsize * (regno - regs->f); + } + else if (regs->fcc <= regno && regno < regs->fcc + 8) + { + /* Size base (fcc) + offset (regno - fcc) + = 32 * sizeof (fpr) + (regno - regs->fcc) * sizeof (fcc). */ + buf = (const gdb_byte *)fprs + 32 * fprsize + + (regno - regs->fcc) * fccsize; + } + else if (regs->fcsr == regno) + { + /* Size base (fcsr) = 32 * sizeof (fpr) + 8 * sizeof (fcc). */ + buf = (const gdb_byte *)fprs + 32 * fprsize + 8 * fccsize; + } + else + { + return; + } + + /* Supply register. */ + regcache->raw_supply (regno, (const void *)buf); +} + +static void +loongarch_fill_elf_fpregset (const struct regset *r, + const struct regcache *regcache, int regno, + void *fprs, size_t len) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + gdb_assert (0 <= regs->f && sizeof (loongarch_elf_fpregset_t) <= len); + gdb_byte *buf = NULL; + int fprsize = register_size (regcache->arch (), regs->f); + int fccsize = register_size (regcache->arch (), regs->fcc); + + if (regno == -1) + { + /* 32 fprs. */ + for (int i = 0; i < 32; i++) + { + buf = (gdb_byte *)fprs + fprsize * i; + regcache->raw_collect (regs->f + i, (void *)buf); + } + + /* 8 fccs , base (fcc) = 32 * sizeof (fpr). */ + buf = (gdb_byte *)fprs + fprsize * 32; + for (int i = 0; i < 8; i++) + { + regcache->raw_collect (regs->fcc + i, (void *)buf); + buf += fccsize; + } + + /* Size base (fcsr) = 32 * sizeof (fpr) + 8 * sizeof (fcc). */ + buf = (gdb_byte *)fprs + fprsize * 32 + fccsize * 8; + regno = regs->fcsr; + } + else if (regs->f <= regno && regno < regs->f + 32) + { + /* Offset offset (regno - f) = (regno - regs->f) * sizeof (fpr). */ + buf = (gdb_byte *)fprs + fprsize * (regno - regs->f); + } + else if (regs->fcc <= regno && regno < regs->fcc + 8) + { + /* Size base (fcc) + offset (regno - fcc) + = 32 * sizeof (fpr) + (regno - regs->fcc) * sizeof (fcc). */ + buf = (gdb_byte *)fprs + 32 * fprsize + (regno - regs->fcc) * fccsize; + } + else if (regs->fcsr == regno) + { + /* Size base (fcsr) = 32 * sizeof (fpr) + 8 * sizeof (fcc). */ + buf = (gdb_byte *)fprs + 32 * fprsize + 8 * fccsize; + } + else + { + return; + } + + /* Supply register. */ + regcache->raw_collect (regno, (void *)buf); +} + +const struct regset loongarch_elf_fpregset = +{ + NULL, + loongarch_supply_elf_fpregset, + loongarch_fill_elf_fpregset, +}; + +static void +loongarch_supply_elf_cpucfgregset (const struct regset *r, + struct regcache *regcache, int regno, + const void *cpucfgs, size_t len) +{ +} + +static void +loongarch_fill_elf_cpucfgregset (const struct regset *r, + const struct regcache *regcache, int regno, + void *cpucfgs, size_t len) +{ + ULONGEST xfered_len; + target_xfer_partial (current_inferior ()->top_target (), + /* current_top_target (),*/ TARGET_OBJECT_LARCH, + "cpucfg", (gdb_byte *) cpucfgs, NULL, 0, len, + &xfered_len); + memset ((gdb_byte *) cpucfgs + xfered_len, 0, len - xfered_len); +} + +const struct regset loongarch_elf_cpucfgregset = +{ + NULL, + loongarch_supply_elf_cpucfgregset, + loongarch_fill_elf_cpucfgregset, +}; + +static void +loongarch_supply_elf_lbtregset (const struct regset *r, + struct regcache *regcache, int regno, + const void *lbtrs, size_t len) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + gdb_assert (0 <= regs->scr && sizeof (loongarch_elf_lbtregset_t) <= len); + + const gdb_byte *buf = NULL; + int scrsize = register_size (regcache->arch (), regs->scr); + int efsize = register_size (regcache->arch (), regs->EFLAG); + + if (regno == -1) + { + /* 4 scrs. */ + for (int i = 0; i < 4; i++) + { + buf = (const gdb_byte *)lbtrs + scrsize * i; + regcache->raw_supply (regs->scr + i, (const void *)buf); + } + + /* Size base (EFLAG) = 4 * sizeof (scr). */ + buf = (const gdb_byte *)lbtrs + scrsize * 4; + regcache->raw_supply (regs->EFLAG, (const void *)buf); + + /* Size base (x86_top) = 4 * sizeof (scr) + sizeof (EFLAG). */ + buf = (const gdb_byte *)lbtrs + scrsize * 4 + efsize; + regno = regs->x86_top; + } + else if (regs->scr <= regno && regno < regs->scr + 4) + /* Offset offset (EFLAG) = sizeof (scr) * (regno - regs->scr). */ + buf = (const gdb_byte *)lbtrs + scrsize * (regno - regs->scr); + else if (regs->EFLAG == regno) + /* Size base (EFLAG) = 4 * sizeof (scr). */ + buf = (const gdb_byte *)lbtrs + scrsize * 4; + else if (regs->x86_top == regno) + { + /* Size base (x86_top) = 4 * sizeof (scr) + sizeof (EFLAG). */ + buf = (const gdb_byte *)lbtrs + scrsize * 4 + efsize; + } + else + { + return; + } + + regcache->raw_supply (regno, (const void *)buf); +} + +static void +loongarch_fill_elf_lbtregset (const struct regset *r, + const struct regcache *regcache, int regno, + void *lbtrs, size_t len) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + gdb_assert (0 <= regs->scr && sizeof (loongarch_elf_lbtregset_t) <= len); + + gdb_byte *buf = NULL; + int scrsize = register_size (regcache->arch (), regs->scr); + int efsize = register_size (regcache->arch (), regs->EFLAG); + + if (regno == -1) + { + + /* 4 scrs. */ + for (int i = 0; i < 4; i++) + { + buf = (gdb_byte *)lbtrs + scrsize * i; + regcache->raw_collect (regs->scr + i, (void *)buf); + } + + /* Size base (EFLAG) = 4 * sizeof (scr). */ + buf = (gdb_byte *)lbtrs + scrsize * 4; + regcache->raw_collect (regs->EFLAG, (void *)buf); + + /* Size base (x86_top) = 4 * sizeof (scr) + sizeof (EFLAG). */ + buf = (gdb_byte *)lbtrs + scrsize * 4 + efsize; + regno = regs->x86_top; + } + else if (regs->scr <= regno && regno < regs->scr + 4) + /* Offset offset (EFLAG) = sizeof (scr) * (regno - regs->scr). */ + buf = (gdb_byte *)lbtrs + scrsize * (regno - regs->scr); + else if (regs->EFLAG == regno) + /* Size base (EFLAG) = 4 * sizeof (scr). */ + buf = (gdb_byte *)lbtrs + scrsize * 4; + else if (regs->x86_top == regno) + /* Size base (x86_top) = 4 * sizeof (scr) + sizeof (EFLAG). */ + buf = (gdb_byte *)lbtrs + scrsize * 4 + efsize; + else + { + return; + } + + regcache->raw_collect (regno, (void *)buf); +} + +const struct regset loongarch_elf_lbtregset = +{ + NULL, + loongarch_supply_elf_lbtregset, + loongarch_fill_elf_lbtregset, +}; + +static void +loongarch_supply_elf_lsxregset (const struct regset *r, + struct regcache *regcache, int regno, + const void *lsxrs, size_t len) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + gdb_assert (0 <= regs->vr && sizeof (loongarch_elf_lsxregset_t) <= len); + + const gdb_byte *buf = NULL; + int regsize = register_size (regcache->arch (), regs->vr); + + if (regno == -1) + { + for (int i = 0; i < 32; i++) + { + buf = (const gdb_byte *)lsxrs + regsize * i; + regcache->raw_supply (regs->vr + i, (const void *)buf); + } + } + else if (regs->vr <= regno && regno < regs->vr + 32) + { + buf = (const gdb_byte *)lsxrs + regsize * (regno - regs->vr); + regcache->raw_supply (regno, (const void *)buf); + } +} + +static void +loongarch_fill_elf_lsxregset (const struct regset *r, + const struct regcache *regcache, int regno, + void *lsxrs, size_t len) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + gdb_assert (0 <= regs->vr && sizeof (loongarch_elf_lsxregset_t) <= len); + + gdb_byte *buf = NULL; + int regsize = register_size (regcache->arch (), regs->vr); + + if (regno == -1) + { + for (int i = 0; i < 32; i++) + { + buf = (gdb_byte *)lsxrs + regsize * i; + regcache->raw_collect (regs->vr + i, (void *)buf); + } + } + else if (regs->vr <= regno && regno < regs->vr + 32) + { + buf = (gdb_byte *)lsxrs + regsize * (regno - regs->vr); + regcache->raw_collect (regno, (void *)buf); + } +} + +const struct regset loongarch_elf_lsxregset = +{ + NULL, + loongarch_supply_elf_lsxregset, + loongarch_fill_elf_lsxregset, +}; + +static void +loongarch_supply_elf_lasxregset (const struct regset *r, + struct regcache *regcache, int regno, + const void *lasxrs, size_t len) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + gdb_assert (0 <= regs->xr && sizeof (loongarch_elf_lasxregset_t) <= len); + + const gdb_byte *buf = NULL; + int regsize = register_size (regcache->arch (), regs->xr); + + if (regno == -1) + { + for (int i = 0; i < 32; i++) + { + buf = (const gdb_byte *)lasxrs + regsize * i; + regcache->raw_supply (regs->xr + i, (const void *)buf); + } + } + else if (regs->xr <= regno && regno < regs->xr + 32) + { + buf = (const gdb_byte *)lasxrs + regsize * (regno - regs->xr); + regcache->raw_supply (regno, (const void *)buf); + } +} + +static void +loongarch_fill_elf_lasxregset (const struct regset *r, + const struct regcache *regcache, int regno, + void *lasxrs, size_t len) +{ + auto regs = &gdbarch_tdep (regcache->arch ())->regs; + gdb_assert (0 <= regs->xr && sizeof (loongarch_elf_lasxregset_t) <= len); + + gdb_byte *buf = NULL; + int regsize = register_size (regcache->arch (), regs->xr); + + if (regno == -1) + { + for (int i = 0; i < 32; i++) + { + buf = (gdb_byte *)lasxrs + regsize * i; + regcache->raw_collect (regs->xr + i, (void *)buf); + } + } + else if (regs->xr <= regno && regno < regs->xr + 32) + { + buf = (gdb_byte *)lasxrs + regsize * (regno - regs->xr); + regcache->raw_collect (regno, (void *)buf); + } +} + +const struct regset loongarch_elf_lasxregset = +{ + NULL, + loongarch_supply_elf_lasxregset, + loongarch_fill_elf_lasxregset, +}; + +static void +loongarch_linux_iterate_over_regset_sections ( + struct gdbarch *gdbarch, iterate_over_regset_sections_cb *cb, void *cb_data, + const struct regcache *regcache) +{ + auto regs = &gdbarch_tdep (gdbarch)->regs; + if (0 <= regs->r) + cb (".reg", sizeof (loongarch_elf_gregset_t), + sizeof (loongarch_elf_gregset_t), &loongarch_elf_gregset, NULL, + cb_data); + if (0 <= regs->f) + cb (".reg2", sizeof (loongarch_elf_fpregset_t), + sizeof (loongarch_elf_fpregset_t), &loongarch_elf_fpregset, NULL, + cb_data); + do + { + uint32_t t; + ULONGEST xfered_len; + if (target_xfer_partial (current_inferior ()->top_target (), + /* current_top_target (),*/ TARGET_OBJECT_LARCH, + "cpucfg", (gdb_byte *) &t, NULL, 0, sizeof (t), + &xfered_len) != TARGET_XFER_OK) + break; + cb (".reg-loongarch-cpucfg", 64 * 4, 64 * 4, &loongarch_elf_cpucfgregset, + "LoongArch CPU config", cb_data); + } + while (0); + if (0 <= regs->scr) + cb (".reg-loongarch-lbt", sizeof (loongarch_elf_lbtregset_t), + sizeof (loongarch_elf_lbtregset_t), &loongarch_elf_lbtregset, + "LoongArch Binary Translation", cb_data); + if (0 <= regs->vr) + cb (".reg-loongarch-lsx", sizeof (loongarch_elf_lsxregset_t), + sizeof (loongarch_elf_lsxregset_t), &loongarch_elf_lsxregset, + "LoongArch SIMD Extension", cb_data); + if (0 <= regs->xr) + cb (".reg-loongarch-lasx", sizeof (loongarch_elf_lasxregset_t), + sizeof (loongarch_elf_lasxregset_t), &loongarch_elf_lasxregset, + "LoongArch Advanced SIMD Extension", cb_data); +} + +static const struct target_desc * +loongarch_linux_core_read_description (struct gdbarch *gdbarch, + struct target_ops *target, bfd *abfd) +{ + int rlen, fpu32, fpu64, lbt, lsx, lasx; + + rlen = 64; + fpu32 = 0; + + fpu64 = !!bfd_get_section_by_name (abfd, ".reg2"); + lbt = !!bfd_get_section_by_name (abfd, ".reg-loongarch-lbt"); + lsx = !!bfd_get_section_by_name (abfd, ".reg-loongarch-lsx"); + lasx = !!bfd_get_section_by_name (abfd, ".reg-loongarch-lasx"); + + return loongarch_create_target_description (rlen, fpu32, fpu64, lbt, lsx, + lasx); +} + +/* The RT signal frames look like this: + struct rt_sigframe { + u32 rs_ass[4]; + u32 rs_pad[2]; + struct siginfo rs_info; + struct ucontext rs_uc; + }; + + struct ucontext { + unsigned long uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; + struct sigcontext uc_mcontext; + sigset_t uc_sigmask; + unsigned long long uc_extcontext[0]; + }; */ + +#define RTSIGFRAME_SIGINFO_SIZE 136 +#define RTSIGFRAME_SIGINFO_OFFSET (6 * 4) +#define UCONTEXT_SIGCONTEXT_OFFSET 64 +#define RTSIGFRAME_SIGCONTEXT_OFFSET (RTSIGFRAME_SIGINFO_OFFSET \ + + RTSIGFRAME_SIGINFO_SIZE \ + + UCONTEXT_SIGCONTEXT_OFFSET) + +static void +loongarch_linux_lp64_sigframe_init (const struct tramp_frame *self, + struct frame_info *this_frame, + struct trad_frame_cache *this_cache, + CORE_ADDR func) +{ + struct gdbarch *gdbarch = get_frame_arch (this_frame); + auto regs = &gdbarch_tdep (gdbarch)->regs; + CORE_ADDR frame_sp = get_frame_sp (this_frame); + + CORE_ADDR sigcontext_base = frame_sp + RTSIGFRAME_SIGCONTEXT_OFFSET; + int i; + + trad_frame_set_reg_addr (this_cache, regs->pc, sigcontext_base); + for (i = 0; i < 32; i++) + trad_frame_set_reg_addr (this_cache, regs->r + i, + sigcontext_base + 8 + i * 8); + + trad_frame_set_id (this_cache, frame_id_build (frame_sp, func)); +} + +static const struct tramp_frame loongarch_linux_lp64_rt_sigframe = +{ + SIGTRAMP_FRAME, + 4, + { /* From $kernel/arch/loongarch/vdso/sigreturn.S. */ + /* ori $r11, $r0, 0x8b(__NR_rt_sigreturn) */ + { 0x03822c0b, ULONGEST_MAX }, + { 0x002b0000, ULONGEST_MAX }, /* syscall 0 */ + { TRAMP_SENTINEL_INSN, ULONGEST_MAX } }, + loongarch_linux_lp64_sigframe_init, + NULL +}; + +/* Return the current system call's number present in the + a7 register. When the function fails, it returns -1. */ + +static LONGEST +loongarch_linux_get_syscall_number (struct gdbarch *gdbarch, + thread_info *thread) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + auto regs = &tdep->regs; + struct regcache *regcache = get_thread_regcache (thread); + LONGEST ret; + + if ((EF_LOONGARCH_IS_LP64 (tdep->ef_abi)) + && (REG_VALID == regcache_cooked_read_signed (regcache, regs->r + 11, &ret))) + return ret; + + return -1; +} + +static CORE_ADDR +loongarch_linux_syscall_next_pc (struct frame_info *frame) +{ + struct gdbarch *gdbarch = get_frame_arch (frame); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + auto regs = &tdep->regs; + ULONGEST a7 = get_frame_register_unsigned (frame, regs->r + 11); + + /* If we are about to make a sigreturn syscall, use the unwinder to + decode the signal frame. */ + if ((EF_LOONGARCH_IS_LP64 (tdep->ef_abi)) + && (a7 == 0x8b /* LP64: __NR_rt_sigreturn. */)) + return frame_unwind_caller_pc (get_current_frame ()); + + return -1; +} + +static void +loongarch_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + linux_init_abi (info, gdbarch, 0); /* FIXME displaced step support. */ + + if (EF_LOONGARCH_IS_ILP32 (tdep->ef_abi)) + set_solib_svr4_fetch_link_map_offsets ( + gdbarch, svr4_ilp32_fetch_link_map_offsets); + + else if (EF_LOONGARCH_IS_LP64 (tdep->ef_abi)) + { + set_solib_svr4_fetch_link_map_offsets (gdbarch, + svr4_lp64_fetch_link_map_offsets); + tramp_frame_prepend_unwinder (gdbarch, + &loongarch_linux_lp64_rt_sigframe); + tdep->syscall_next_pc = loongarch_linux_syscall_next_pc; + + set_gdbarch_get_syscall_number (gdbarch, + loongarch_linux_get_syscall_number); + } + + /* GNU/Linux uses the dynamic linker included in the GNU C Library. */ + set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver); + + /* Enable TLS support. */ + set_gdbarch_fetch_tls_load_module_address (gdbarch, + svr4_fetch_objfile_link_map); + + set_gdbarch_call_dummy_location (gdbarch, AT_ENTRY_POINT); + + /* Core file support. */ + set_gdbarch_iterate_over_regset_sections ( + gdbarch, loongarch_linux_iterate_over_regset_sections); + set_gdbarch_core_read_description (gdbarch, + loongarch_linux_core_read_description); +} + +void _initialize_loongarch_linux_tdep (); +void +_initialize_loongarch_linux_tdep () +{ + gdbarch_register_osabi (bfd_arch_loongarch, bfd_mach_loongarch32, + GDB_OSABI_LINUX, loongarch_linux_init_abi); + gdbarch_register_osabi (bfd_arch_loongarch, bfd_mach_loongarch64, + GDB_OSABI_LINUX, loongarch_linux_init_abi); +} diff --git a/gdb/loongarch-linux-tdep.h b/gdb/loongarch-linux-tdep.h new file mode 100644 index 0000000..e3456a8 --- /dev/null +++ b/gdb/loongarch-linux-tdep.h @@ -0,0 +1,48 @@ +/* GNU/Linux on LoongArch target support, prototypes. + + Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Loongson Ltd. + + 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 . */ + +#ifndef LOONGARCH_LINUX_TDEP_H +#define LOONGARCH_LINUX_TDEP_H + +#include + +#define ELF_NGREG 45 +#define ELF_NFPREG 34 + +typedef uint64_t loongarch_elf_gregset_t[ELF_NGREG]; +extern const struct regset loongarch_elf_gregset; + +typedef uint64_t loongarch_elf_fpregset_t[ELF_NFPREG]; +extern const struct regset loongarch_elf_fpregset; + +/* Regset variable size. */ +extern const struct regset loongarch_elf_cpucfg; + +/* 4 SCRs + 4-byte EFLAG + 1-byte x86_top. */ +typedef uint64_t loongarch_elf_lbtregset_t[5]; +extern const struct regset loongarch_elf_lbtregset; + +typedef uint64_t loongarch_elf_lsxregset_t[32 * 2]; +extern const struct regset loongarch_elf_lsxregset; + +typedef uint64_t loongarch_elf_lasxregset_t[32 * 4]; +extern const struct regset loongarch_elf_lasxregset; + +#endif diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c new file mode 100644 index 0000000..87331bd --- /dev/null +++ b/gdb/loongarch-tdep.c @@ -0,0 +1,1925 @@ +/* Target-dependent code for GNU/Linux LoongArch. + + Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Loongson Ltd. + + 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 "loongarch-tdep.h" +#include "arch/loongarch.h" + +#include + +/* 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. */ +#define LOONGARCH_JB_PC 0 + +static int +loongarch_rlen (struct gdbarch *gdbarch) +{ + if (EF_LOONGARCH_IS_LP64 (gdbarch_tdep (gdbarch)->ef_abi)) + return 64; + else if (EF_LOONGARCH_IS_ILP32 (gdbarch_tdep (gdbarch)->ef_abi)) + return 32; + else + 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 */ + 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 & 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; + + 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; +} + +/* Implement LoongArch software single step. */ + +std::vector +loongarch_software_single_step (struct regcache *regcache); +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) + && (EF_LOONGARCH_IS_LP64 (gdbarch_tdep (gdbarch)->ef_abi))) + return loongarch_r_lp64_name[regnum - regs->r] + 1; + + else if ((0 <= regs->f && regs->f <= regnum && regnum < regs->f + 32) + && (EF_LOONGARCH_IS_LP64 (gdbarch_tdep (gdbarch)->ef_abi))) + 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) +{ + 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; + + 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 sp,sp,si12 */ + && !rlen_is_64b) + || ((insn & 0xffc00000) == 0x02c00000 /* addi.d sp,sp,si12 */ + && rlen_is_64b)) + && rd == sp && rj == sp) + { + 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,sp,si12 */ + && !rlen_is_64b) + || ((insn & 0xffc00000) == 0x29c00000 /* st.d rd,sp,si12 */ + && rlen_is_64b)) + && rj == sp) + { + 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,sp,si14 */ + && !rlen_is_64b) + || ((insn & 0xff000000) == 0x27000000 /* stptr.d rd,sp,si14 */ + && rlen_is_64b)) + && rj == sp) + { + 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,sp,si12 */ + || (insn & 0xffc00000) == 0x2bc00000) /* fst.d fd,sp,si12 */ + && rj == sp) + { + 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 sp,sp,rk */ + && !rlen_is_64b) + || ((insn & 0xffff8000) == 0x00118000 /* sub.d sp,sp,rk */ + && rlen_is_64b)) + && rd == sp && rj == sp) + { + if (r_value_known[rk]) + { + frame_offset += r_value[rk]; + prologue_end = cur_pc + insnlen; + } + else + cfa_unknown = 1; + } + else if ((((insn & 0xffff8000) == 0x00100000 /* add.w sp,sp,rk */ + && !rlen_is_64b) + || ((insn & 0xffff8000) == 0x00108000 /* add.d sp,sp,rk */ + && rlen_is_64b)) + && rd == sp && rj == sp) + { + if (r_value_known[rk] && r_value[rk] < 0) + { + frame_offset -= r_value[rk]; + prologue_end = cur_pc + insnlen; + } + else + cfa_unknown = 1; + } + else if ((insn & 0xffff8000) == 0x00150000 /* or rd,sp,$r0 */ + && rj == sp && rk == 0) + { + sp = 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++; + + /* 4 INSNs for 'la' and one for some other. */ + if (5 < non_prologue_insns) + 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; + + if (!(this_frame && this_cache)) + break; + + if (!cfa_unknown) + { + try + { + cfa = get_frame_register_signed (this_frame, regs->r + sp) + + frame_offset; + } + catch (const gdb_exception_error &ex) + { + cfa_unknown = 1; + if (ex.error != NOT_AVAILABLE_ERROR) + throw; + } + + if (loongarch_debug) + fprintf_unfiltered ( + gdb_stdlog, + "Prologue Analyze: CFA is (frame pointer $%s + 0x%lx) = %s\n", + gdbarch_register_name (gdbarch, regs->r + sp), + (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; + } + + 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 loongarch 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; + + pc = get_frame_address_in_block (this_frame); + if (find_pc_partial_function (pc, NULL, &start_addr, NULL)) + { + 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 + { + auto regs = &gdbarch_tdep (gdbarch)->regs; + 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 loongarch 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 loongarch 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); +} + +static const struct frame_unwind loongarch_frame_unwind = { + "loongarch prologue", + /*.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, +}; + +typedef struct stack_data_t +{ + const gdb_byte *addr = NULL; + int len = 0; + bool ref = false; +} stack_data_t; + +static void +pass_on_stack (std::vector &stack, const gdb_byte *val, int len, + int align, bool ref = false) +{ + stack_data_t buf; + buf.addr = val; + buf.len = align_up (len, align); + buf.ref = ref; + + stack.push_back (buf); +} + +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 +compute_type_num(struct type *tp, int &complex_num, int &float_num, + int &other_num, int &counter, int &float_seq, + int &other_seq) +{ + if (tp->code () == TYPE_CODE_COMPLEX) + complex_num++; + else if (tp->code () == TYPE_CODE_FLT) + float_num++; + else if (tp->code () != TYPE_CODE_STRUCT) + other_num++; + + /* When the function parameter or return value type is a structure, + traverse each member in the structure and make relevant marks. */ + for (int i = 0; i < tp->num_fields (); 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 (t->code () == TYPE_CODE_TYPEDEF) + t = check_typedef (TYPE_TARGET_TYPE (t)); + + switch (t->code ()) + { + case TYPE_CODE_STRUCT: + compute_type_num(t, complex_num, float_num, other_num, + counter, float_seq, other_seq); + break; + case TYPE_CODE_COMPLEX: + complex_num++; + break; + case TYPE_CODE_FLT: + float_num++; + float_seq = ++counter; + break; + default: + other_num++; + other_seq = ++counter; + break; + } + } +} + +static void +pass_small_struct_on_reg (struct gdbarch *gdbarch, struct type *tp, + const gdb_byte *data, std::vector &gp, + std::vector &fp) +{ + const int rlen = loongarch_rlen (gdbarch) / 8; + int len = TYPE_LENGTH (tp); + int complex_num = 0, float_num = 0, other_num = 0; + int counter = 0, float_seq = 0, other_seq = 0; + stack_data_t elm; + + gdb_assert (len <= 2 * rlen); + + /* Compute the relevant members and types in the function parameters + and mark them. */ + compute_type_num(tp, complex_num, float_num, other_num, + counter, float_seq, other_seq); + + if (other_num > 0 && float_num == 0 && len <= rlen) + { + /* For the small structure has only other types (like char/short/int/long + etc.), and the size does not exceed rlen, pass on one gp or stack. */ + elm.addr = data; + elm.len = rlen; + gp.push_back (elm); + } + else if (float_num == 1 && complex_num == 0 && other_num == 0 && len <= rlen) + { + /* For the small structure has only floating-point (like float/double), + and the size does not exceed rlen, pass on one fp or stack. */ + elm.addr = data; + elm.len = rlen; + fp.push_back (elm); + } + else if (float_num == 1 && other_num == 1) + { + /* For the small structure has only one floating-point type and + one other type(like float and int, char and double etc.), the + floating-point type passes through one fp or stack, and the + other types pass on one gp or stack. */ + if (float_seq < other_seq) + { + /* Floating point first, first pass on fp, then gp. */ + elm.addr = data; + if (len == rlen) + elm.len = rlen / 2; + else + elm.len = rlen; + fp.push_back (elm); + elm.addr += elm.len; + gp.push_back (elm); + } + else + { + /* Floating point after, first pass on gp, then fp. */ + elm.addr = data; + if (len == rlen) + elm.len = rlen / 2; + else + elm.len = rlen; + gp.push_back (elm); + elm.addr += elm.len; + fp.push_back (elm); + } + } + else if ((complex_num == 1 && float_num == 0 && other_num == 0) || + (float_num ==2 && other_num == 0)) + { + /* For the small structure has only two floating-point types or + * one complex number type, pass on two fp or stack. */ + elm.addr = data; + /* 2 float or 1 'float _Complex'. */ + if (len == rlen) + elm.len = rlen / 2; + /* 2 double or 1 'double _Complex'. */ + else + elm.len = rlen; + fp.push_back (elm); + elm.addr += elm.len; + fp.push_back (elm); + } + else + { + /* For other cases, pass on two gp or stack. */ + /* For example, the small structure is of the following type, + 1. with more than 2 other types and the size is greater than rlen + (like struct{int; int; int;}; struct{long; int; short; char;}; etc.). + 2. with 'long double' on fpu64 or 'double' on fpu32 + (like struct{long double;}; or struct{double;}; etc.). + 3. with more than 2 floating-point types + (like struct{float; float; float;}; struct{float; float; double;}; + struct{float; float; float; float;}; etc.) + 4. with 2 'float _Complex' + (like struct{float _Complex; float _Complex;} etc.). */ + elm.addr = data; + elm.len = rlen; + gp.push_back (elm); + elm.addr += elm.len; + gp.push_back (elm); + } +} + +static bool +try_pass_small_struct_on_reg (struct gdbarch *gdbarch, + struct regcache *regcache, struct value *arg, + int &gp, int &fp, int gp_max, int fp_max) +{ + const int rlen = loongarch_rlen (gdbarch) / 8; + struct type *a_type = check_typedef (value_type (arg)); + int len = TYPE_LENGTH (a_type); + const gdb_byte *val = value_contents (arg); + + std::vector gpreg; + std::vector fpreg; + + gdb_assert (len <= 2 * rlen); + // gdb_assert (a_type->code () == TYPE_CODE_STRUCT); + + pass_small_struct_on_reg (gdbarch, a_type, val, gpreg, fpreg); + + if (gp + gpreg.size () - 1 < gp_max && fp + fpreg.size () - 1 < fp_max) + { + for (auto it : gpreg) + { + pass_on_reg (regcache, gp, it.addr, it.len); + gp++; + } + for (auto it : fpreg) + { + pass_on_reg (regcache, fp, it.addr, it.len); + fp++; + } + return true; + } + return false; +} + +/* Implement the push dummy call gdbarch callback. */ + +static CORE_ADDR +loongarch_lp32lp64_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) +{ + const int rlen = loongarch_rlen (gdbarch) / 8; + auto regs = &gdbarch_tdep (gdbarch)->regs; + int gp = regs->r + 4; /* $a0 = $r4 = regs->r + 4 */ + int fp = regs->f; /* $fa0 */ + const int gp_max = gp + 8; /* gpr $a0 ~ $a7 ($r4 ~ $r11) */ + const int fp_max = fp + 8; /* fpr $fa0 ~ $fa7 */ + std::vector stack; + int vec_insn = 0; + + { + if (return_method != return_method_normal) + { + regcache_cooked_write_unsigned (regcache, gp++, struct_addr); + } + + if (return_method == return_method_hidden_param) + { + args++; + nargs--; + } + } + regcache_cooked_write_signed (regcache, regs->ra, bp_addr); + + struct type *f_type = check_typedef (value_type (function)); + + for (int i = 0; i < nargs; i++) + { + struct value *arg = args[i]; + struct type *a_type = check_typedef (value_type (arg)); + int len = TYPE_LENGTH (a_type); + const gdb_byte *val = value_contents (arg); + + switch (a_type->code ()) + { + case TYPE_CODE_INT: + case TYPE_CODE_BOOL: + case TYPE_CODE_CHAR: + case TYPE_CODE_RANGE: + case TYPE_CODE_ENUM: + case TYPE_CODE_PTR: + if (gp < gp_max) + { + if (a_type->is_unsigned ()) + { + ULONGEST data + = extract_unsigned_integer (val, len, BFD_ENDIAN_LITTLE); + regcache_cooked_write_unsigned (regcache, gp++, data); + } + else + { + LONGEST data + = extract_signed_integer (val, len, BFD_ENDIAN_LITTLE); + regcache_cooked_write_signed (regcache, gp++, data); + } + } + else + { + pass_on_stack (stack, val, len, rlen); + } + break; + case TYPE_CODE_FLT: + if (len <= rlen) + { + if (!f_type->has_varargs () && (fp < fp_max)) + pass_on_reg (regcache, fp++, val, len); + else if (gp < gp_max) + pass_on_reg (regcache, gp++, val, len); + else + pass_on_stack (stack, val, len, rlen); + } + /* Long double like struct. */ + else + { + if (gp < gp_max - 1) + { + pass_on_reg (regcache, gp++, val, rlen); + pass_on_reg (regcache, gp++, val + rlen, len - rlen); + } + else + pass_on_stack (stack, val, len, rlen); + } + break; + case TYPE_CODE_ARRAY: + /* lsx */ + if (a_type->is_vector () && len == vec_insn && vec_insn == 16 + && fp < fp_max) + { + pass_on_reg (regcache, regs->vr + (fp++ - regs->f), val, len); + } + /* lasx */ + else if (a_type->is_vector () && len == vec_insn && vec_insn == 32 + && fp < fp_max) + { + pass_on_reg (regcache, regs->xr + (fp++ - regs->f), val, len); + } + /* scalar */ + else + { + if (len > rlen * 2) + { + /* Address on register, data on stack. */ + sp = align_down (sp - len, rlen); + write_memory (sp, val, len); + if (gp < gp_max) + pass_on_reg (regcache, gp++, (const gdb_byte *) &sp, rlen); + else + pass_on_stack (stack, (const gdb_byte *) sp, rlen, rlen, + true); + } + else + { + if (len <= rlen && gp < gp_max) + { + pass_on_reg (regcache, gp++, val, len); + } + else if (gp + 1 < gp_max) + { + pass_on_reg (regcache, gp++, val, rlen); + pass_on_reg (regcache, gp++, val + rlen, rlen); + } + else + { + pass_on_stack (stack, val, len, rlen); + } + } + } + break; + case TYPE_CODE_STRUCT: + case TYPE_CODE_UNION: + if (len > rlen * 2) + { + /* Address on register, data on stack. */ + sp = align_down (sp - len, rlen); + write_memory (sp, val, len); + if (gp < gp_max) + pass_on_reg (regcache, gp++, (const gdb_byte *) &sp, rlen); + else + pass_on_stack (stack, (const gdb_byte *) sp, rlen, rlen, true); + } + else + { + if (!try_pass_small_struct_on_reg (gdbarch, regcache, arg, gp, + fp, gp_max, fp_max)) + { + pass_on_stack (stack, val, len, rlen); + } + } + break; + case TYPE_CODE_COMPLEX: + { + /* Two fpr or mem. */ + struct type *t_type = check_typedef (TYPE_TARGET_TYPE (a_type)); + int tlen = TYPE_LENGTH (t_type); + + if (tlen < rlen) + { + if (!f_type->has_varargs () && fp + 1 < fp_max) + { + pass_on_reg (regcache, fp++, (const gdb_byte *) val, tlen); + pass_on_reg (regcache, fp++, (const gdb_byte *) val + tlen, + tlen); + } + else if (gp < gp_max) + { + pass_on_reg (regcache, gp++, (const gdb_byte *) val, rlen); + } + else + { + pass_on_stack (stack, val, len, rlen); + } + } + else if (tlen == rlen) + { + if (!f_type->has_varargs () && fp + 1 < fp_max) + { + pass_on_reg (regcache, fp++, (const gdb_byte *) val, tlen); + pass_on_reg (regcache, fp++, (const gdb_byte *) val + tlen, + tlen); + } + else if (gp + 1 < gp_max) + { + pass_on_reg (regcache, gp++, (const gdb_byte *) val, rlen); + pass_on_reg (regcache, gp++, (const gdb_byte *) val + rlen, + rlen); + } + else if (gp + 1 == gp_max) + { + pass_on_reg (regcache, gp++, (const gdb_byte *) val, rlen); + pass_on_stack (stack, val, tlen, rlen); + } + else + { + pass_on_stack (stack, val, len, rlen); + } + } + else + { + sp = align_down (sp - len, rlen); + write_memory (sp, val, len); + if (gp < gp_max) + pass_on_reg (regcache, gp++, (const gdb_byte *) &sp, rlen); + else + { + pass_on_stack (stack, (const gdb_byte *) sp, rlen, rlen, + true); + } + } + } + break; + default: + break; + } + } + + for (auto it : stack) + sp = align_down (sp - it.len, rlen); + + sp = align_down (sp, 16); + CORE_ADDR tsp = sp; + for (auto it : stack) + { + if (it.ref) + write_memory (tsp, (const gdb_byte *) &it.addr, it.len); + else + write_memory (tsp, it.addr, it.len); + tsp += it.len; + stack.pop_back (); + } + regcache_cooked_write_unsigned (regcache, regs->sp, sp); + 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_lp64_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 (); + 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_INT && type->is_unsigned ()) + || 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->is_unsigned ()) + || 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 + { + int complex_num = 0, float_num = 0, other_num = 0; + int counter = 0, float_seq = 0, other_seq = 0, tlen; + /* Calculate the relevant members and types in the return value + and mark them. */ + compute_type_num(type, complex_num, float_num, other_num, + counter, float_seq, other_seq); + + if (len == rlen) + tlen = rlen / 2; + else + tlen = rlen; + + /* For the small structure has only other types members (like char/short/int/long + etc.), and the size does not exceed rlen, pass on $v0. */ + /* For 'char/short/int/long' etc. pass on $v0. */ + if (other_num > 0 && float_num == 0 && len <= rlen) + loongarch_xfer_reg_part (regcache, regs->r + 4, len, readbuf, 0, + writebuf, 0); + /* For small structure with only one floating-point member, (like float/double) pass on $fv0. */ + /* For float/double pass on $fv0. */ + else if (float_num == 1 && complex_num == 0 && other_num == 0 && len <= rlen) + loongarch_xfer_reg_part (regcache, fv, len, readbuf, 0, writebuf, 0); + /* For small structure with one float/double member and one other member + (char/short/int/long etc.). If the float/dobule member is in the front + position, the float/dobule member pass on $fv0, the other member pass + on $v0, otherwise the opposite . */ + else if (float_num == 1 && other_num == 1) + if (float_seq < other_seq) + loongarch_xfer_reg_part (regcache, fv, rlen, readbuf, 0, + writebuf, 0), + loongarch_xfer_reg_part (regcache, regs->r + 4, tlen, readbuf, + tlen, writebuf, rlen); + else + loongarch_xfer_reg_part (regcache, regs->r + 4, rlen, readbuf, 0, + writebuf, 0), + loongarch_xfer_reg_part (regcache, fv, tlen, readbuf, + tlen, writebuf, rlen); + /* For small structure with one 'float/double _Complex' member, + $fv0 is real and $fv1 is img. */ + /* For small structure with only one float and double member or + or two float member , or two dobule member, $fv0 is the 1st + member and $fv1 is the 2nd member. */ + /* For 'float/double _Complex', $fv0 is real and $fv1 is img. */ + else if ((complex_num == 1 && float_num == 0 && other_num == 0) || + (float_num ==2 && other_num == 0)) + loongarch_xfer_reg_part (regcache, fv, rlen, readbuf, 0, + writebuf, 0), + loongarch_xfer_reg_part (regcache, fv + 1, tlen, readbuf, + tlen, writebuf, rlen); + /* For small structure with 'long double' member, + or when the small structure has more than two vaild members + and the size is greater than rlen, pass on $v0 and $v1. */ + /* For small structure with two 'float _Complex' member, + $v0 is the 1st member and $v1 is the 2nd member. */ + /* For 'long double' on fpu64 or 'double' on fpu32 pass on $v0 and $v1. */ + 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 ""; +} + +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; + + 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); +} + +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, {to + 0, 16}, 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, {to + 0, 32}, 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")); +} + +static int +loongarch_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc) +{ + CORE_ADDR jb_addr; + struct gdbarch *gdbarch = get_frame_arch (frame); + uint32_t ptr_size = gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT; + gdb_byte buf[ptr_size]; + + jb_addr = get_frame_register_unsigned (frame, LOONGARCH_A0_REGNUM); + + if (target_read_memory ((jb_addr + LOONGARCH_JB_PC * ptr_size), + buf, ptr_size)) + return 0; + + *pc = extract_unsigned_integer (buf, ptr_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; + tdesc_arch_data_up tdesc_data; + 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 abfd is nullptr then a EF_LOONGARCH_ABI_LP64 is returned in + its default state. */ + if (info.abfd != NULL + && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour) + { + int eflags = elf_elfheader (info.abfd)->e_flags; + unsigned char eclass = elf_elfheader (info.abfd)->e_ident[EI_CLASS]; + + if (eflags) /* Executable file */ + { + tdep->ef_abi = (EF_LOONGARCH_ABI(eflags) & EF_LOONGARCH_ABI_MASK); + } + else /* Core file */ + { + if (eclass == ELFCLASS64) + tdep->ef_abi = EF_LOONGARCH_ABI_LP64_DOUBLE_FLOAT; + else + tdep->ef_abi = EF_LOONGARCH_ABI_ILP32_DOUBLE_FLOAT; + } + } + else + tdep->ef_abi = EF_LOONGARCH_ABI_LP64_DOUBLE_FLOAT; + + /* Check any target description for validity. */ + if (!tdesc_has_registers (tdesc)) + tdesc = loongarch_get_base_target_description ( + EF_LOONGARCH_IS_ILP32 (tdep->ef_abi) ? 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.get (), regnum++, + loongarch_r_normal_name[i] + 1); + valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), + tdep->regs.pc = regnum++, "pc"); + valid_p + &= tdesc_numbered_register (feature, tdesc_data.get (), + 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.get (), regnum++, + loongarch_f_normal_name[i] + 1); + tdep->regs.fcc = regnum; + valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++, + "fcc0"); + valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++, + "fcc1"); + valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++, + "fcc2"); + valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++, + "fcc3"); + valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++, + "fcc4"); + valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++, + "fcc5"); + valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++, + "fcc6"); + valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++, + "fcc7"); + valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), + 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.get (), regnum++, + loongarch_cr_normal_name[i] + 1); + valid_p + &= tdesc_numbered_register (feature, tdesc_data.get (), + tdep->regs.EFLAG = regnum++, "EFLAG"); + valid_p + &= tdesc_numbered_register (feature, tdesc_data.get (), + tdep->regs.x86_top = regnum++, "x86_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.get (), 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.get (), regnum++, + loongarch_x_normal_name[i] + 1); + } + + if (!valid_p) + { + 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; + + 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. */ + if (EF_LOONGARCH_IS_ILP32 (tdep->ef_abi)) + { + 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); + } + else if (EF_LOONGARCH_IS_LP64 (tdep->ef_abi)) + { + 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; + + 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)); + + /* Functions handling dummy frames. */ + set_gdbarch_push_dummy_call (gdbarch, + loongarch_lp32lp64_push_dummy_call); + set_gdbarch_return_value (gdbarch, loongarch_lp64_return_value); + + } + else + gdb_assert_not_reached ("unknown ABI"); + + /* Hook in OS ABI-specific overrides, if they have been registered. */ + info.target_desc = tdesc; + info.tdesc_data = tdesc_data.get (); + + /* Register architecture. */ + set_gdbarch_num_regs (gdbarch, regnum); + set_gdbarch_sp_regnum (gdbarch, tdep->regs.sp); + set_gdbarch_pc_regnum (gdbarch, tdep->regs.pc); + + tdesc_use_registers (gdbarch, tdesc, std::move (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); + + 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_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; +} + +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 val32 = value; + ULONGEST xfered_len; + target_xfer_partial (current_inferior ()->top_target (), + TARGET_OBJECT_LARCH, "cpucfg", NULL, + (const gdb_byte *) &val32, addr * 4, + sizeof (val32), &xfered_len); + if (0 < xfered_len) + fprintf_unfiltered (gdb_stdout, "ok\n"); + else + error ("Set failed"); + } + else + { + uint64_t val64 = value; + ULONGEST xfered_len; + target_xfer_partial (current_inferior ()->top_target (), + TARGET_OBJECT_LARCH, item, NULL, + (const gdb_byte *) &val64, addr * 8, + sizeof (val64), &xfered_len); + if (0 < xfered_len) + fprintf_unfiltered (gdb_stdout, "ok\n"); + else + error ("Set failed"); + } + else if (strcmp (item, "cpucfg") == 0) + { + uint32_t val32; + ULONGEST xfered_len; + target_xfer_partial (current_inferior ()->top_target (), + TARGET_OBJECT_LARCH, "cpucfg", (gdb_byte *) &val32, + NULL, addr * 4, sizeof (val32), &xfered_len); + if (0 < xfered_len) + fprintf_unfiltered (gdb_stdout, "return is %x\n", val32); + else + error ("Get failed"); + } + else + { + uint64_t val64; + ULONGEST xfered_len; + target_xfer_partial (current_inferior ()->top_target (), + TARGET_OBJECT_LARCH, item, (gdb_byte *) &val64, + NULL, addr * 8, sizeof (val64), &xfered_len); + if (0 < xfered_len) + fprintf_unfiltered (gdb_stdout, "return is %llx\n", (long long) val64); + else + error ("Get failed"); + } + + return; +Empty: + error ("Empty. Should be 'info loongarch ([get]|set) item addr [value]'"); +} + +void _initialize_loongarch_tdep (); +void +_initialize_loongarch_tdep () +{ + 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); +} diff --git a/gdb/loongarch-tdep.h b/gdb/loongarch-tdep.h new file mode 100644 index 0000000..c673c9e --- /dev/null +++ b/gdb/loongarch-tdep.h @@ -0,0 +1,60 @@ +/* Target-dependent code for GNU/Linux LoongArch. + + Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Loongson Ltd. + + 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 . */ + +#ifndef LOONGARCH_TDEP_H +#define LOONGARCH_TDEP_H + +#include "arch/loongarch.h" + +/* Register numbers of important registers. Note that most of + these values are "real" register numbers, and correspond to the + general registers of the machine. */ +#define LOONGARCH_A0_REGNUM 4 /* Loc of first arg */ + +struct gdbarch_tdep +{ + int ef_abi; /* EF_LOONGARCH_ABI */ + + struct + { + int r; + int ra; + int sp; + int pc; + int badvaddr; + + int f; + int fcc; + int fcsr; + int vr; + int xr; + + int scr; + int EFLAG; + int x86_top; + + } regs; + + /* Return the expected next PC if FRAME is stopped at a syscall + instruction. */ + CORE_ADDR (*syscall_next_pc) (struct frame_info *frame); +}; + +#endif /* LOONGARCH_TDEP_H */ diff --git a/gdb/nat/loongarch-linux-watch.c b/gdb/nat/loongarch-linux-watch.c new file mode 100644 index 0000000..f7c0dbf --- /dev/null +++ b/gdb/nat/loongarch-linux-watch.c @@ -0,0 +1,330 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Loongson Ltd. + + This file is part of GDB. + + Based on MIPS target. + + 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 "gdbsupport/common-defs.h" +#include "nat/gdb_ptrace.h" +#include "loongarch-linux-watch.h" + +/* Assuming usable watch registers REGS, return the irwmask of + register N. */ + +uint8_t +loongarch_linux_watch_get_irwmask (struct pt_watch_regs *regs, int n) +{ + switch (regs->style) + { + case pt_watch_style_la32: + return regs->la32[n].irwmask & IRW_MASK; + case pt_watch_style_la64: + return regs->la64[n].irwmask & IRW_MASK; + default: + internal_error (__FILE__, __LINE__, + _ ("Unrecognized watch register style")); + } +} + +/* Assuming usable watch registers REGS, return the irwstat of + register N. */ + +uint8_t +loongarch_linux_watch_get_irwstat (struct pt_watch_regs *regs, int n) +{ + switch (regs->style) + { + case pt_watch_style_la32: + return regs->la32[n].irwstat & IRW_MASK; + case pt_watch_style_la64: + return regs->la64[n].irwstat & IRW_MASK; + default: + internal_error (__FILE__, __LINE__, + _ ("Unrecognized watch register style")); + } +} + +/* Assuming usable watch registers REGS, return the num_valid. */ + +uint32_t +loongarch_linux_watch_get_num_valid (struct pt_watch_regs *regs) +{ + return regs->num_valid; +} + +/* Assuming usable watch registers REGS, return the addr of + register N. */ + +CORE_ADDR +loongarch_linux_watch_get_addr (struct pt_watch_regs *regs, int n) +{ + switch (regs->style) + { + case pt_watch_style_la32: + return regs->la32[n].addr; + case pt_watch_style_la64: + return regs->la64[n].addr; + default: + internal_error (__FILE__, __LINE__, + _ ("Unrecognized watch register style")); + } +} + +/* Assuming usable watch registers REGS, set addr of register N to + VALUE. */ + +void +loongarch_linux_watch_set_addr (struct pt_watch_regs *regs, int n, + CORE_ADDR value) +{ + switch (regs->style) + { + case pt_watch_style_la32: + /* The cast will never throw away bits as 64 bit addresses can + never be used on a 32 bit kernel. */ + regs->la32[n].addr = (uint32_t) value; + break; + case pt_watch_style_la64: + regs->la64[n].addr = value; + break; + default: + internal_error (__FILE__, __LINE__, + _ ("Unrecognized watch register style")); + } +} + +/* Assuming usable watch registers REGS, return the mask of + register N. */ + +CORE_ADDR +loongarch_linux_watch_get_mask (struct pt_watch_regs *regs, int n) +{ + switch (regs->style) + { + case pt_watch_style_la32: + return regs->la32[n].mask; + case pt_watch_style_la64: + return regs->la64[n].mask; + default: + internal_error (__FILE__, __LINE__, + _ ("Unrecognized watch register style")); + } +} + +/* Assuming usable watch registers REGS, set mask of register N to + VALUE. */ + +void +loongarch_linux_watch_set_mask (struct pt_watch_regs *regs, int n, + CORE_ADDR value) +{ + switch (regs->style) + { + case pt_watch_style_la32: + regs->la32[n].mask = value; + break; + case pt_watch_style_la64: + regs->la64[n].mask = value; + break; + default: + internal_error (__FILE__, __LINE__, + _ ("Unrecognized watch register style")); + } +} + +/* Assuming usable watch registers REGS, return the irw of + register N. */ + +uint8_t +loongarch_linux_watch_get_irw (struct pt_watch_regs *regs, int n) +{ + switch (regs->style) + { + case pt_watch_style_la32: + return regs->la32[n].irw; + case pt_watch_style_la64: + return regs->la64[n].irw; + default: + internal_error (__FILE__, __LINE__, + _ ("Unrecognized watch register style")); + } +} + +/* Assuming usable watch registers REGS, set irw of register N to + VALUE. */ + +void +loongarch_linux_watch_set_irw (struct pt_watch_regs *regs, int n, + uint8_t value) +{ + switch (regs->style) + { + case pt_watch_style_la32: + regs->la32[n].irw = value; + break; + case pt_watch_style_la64: + regs->la64[n].irw = value; + break; + default: + internal_error (__FILE__, __LINE__, + _ ("Unrecognized watch register style")); + } +} + +/* Read the watch registers of process LWPID and store it in + WATCH_READBACK. Save true to *WATCH_READBACK_VALID if watch + registers are valid. Return 1 if watch registers are usable. + Cached information is used unless FORCE is true. */ + +int +loongarch_linux_read_watch_registers (long lwpid, + struct pt_watch_regs *watch_readback, + int *watch_readback_valid, int force) +{ + if (force || *watch_readback_valid == 0) + { + if (ptrace (PTRACE_GET_WATCH_REGS, lwpid, watch_readback, NULL) == -1) + { + *watch_readback_valid = -1; + return 0; + } + if (watch_readback->num_valid == 0) + { + *watch_readback_valid = -1; + return 0; + } + /* Watch registers appear to be usable. */ + *watch_readback_valid = 1; + } + return (*watch_readback_valid == 1) ? 1 : 0; +} + +/* Convert GDB's TYPE to an IRW mask. */ + +uint32_t +loongarch_linux_watch_type_to_irw (enum target_hw_bp_type type) +{ + switch (type) + { + case hw_write: + return W_MASK; + case hw_read: + return R_MASK; + case hw_access: + return (W_MASK | R_MASK); + case hw_execute: + return I_MASK; + default: + return 0; + } +} + +/* Set any low order bits in MASK that are not set. */ + +static CORE_ADDR +fill_mask (CORE_ADDR mask) +{ + CORE_ADDR f = 1; + + while (f && f < mask) + { + mask |= f; + f <<= 1; + } + return mask; +} + +/* Try to add a single watch to the specified registers REGS. The + address of added watch is ADDR, the length is LEN, and the mask + is IRW. Return 1 on success, 0 on failure. */ + +int +loongarch_linux_watch_try_one_watch (struct pt_watch_regs *regs, + CORE_ADDR addr, int len, uint32_t irw) +{ + CORE_ADDR base_addr, last_byte; + CORE_ADDR mask_bits, t_addr, t_mask; + uint8_t t_irw; + int i; + + if (len <= 0) + return 0; + + last_byte = addr + len - 1; + mask_bits = fill_mask (addr ^ last_byte); + base_addr = addr & ~mask_bits; + + /* Check to see if it is covered by current registers. */ + for (i = 0; i < loongarch_linux_watch_get_num_valid (regs); i++) + { + t_addr = loongarch_linux_watch_get_addr (regs, i); + t_irw = loongarch_linux_watch_get_irw (regs, i); + if (t_addr != 0 && irw == ((uint32_t) t_irw & irw)) + { + t_mask = loongarch_linux_watch_get_mask (regs, i); + if (addr >= t_addr && last_byte <= (t_addr + t_mask)) + return 1; + } + } + /* Try to find an empty register. */ + for (i = 0; i < loongarch_linux_watch_get_num_valid (regs); i++) + { + t_addr = loongarch_linux_watch_get_addr (regs, i); + if (t_addr == 0 + && irw == (loongarch_linux_watch_get_irwmask (regs, i) & irw)) + { + /* It fits, we'll take it. */ + loongarch_linux_watch_set_addr (regs, i, base_addr); + loongarch_linux_watch_set_mask (regs, i, mask_bits); + loongarch_linux_watch_set_irw (regs, i, irw); + return 1; + } + } + /* It didn't fit anywhere, we failed. */ + return 0; +} + +/* Fill in the watch registers REGS with the currently cached + watches CURRENT_WATCHES. */ + +void +loongarch_linux_watch_populate_regs ( + struct loongarch_watchpoint *current_watches, struct pt_watch_regs *regs) +{ + struct loongarch_watchpoint *w; + int i; + + /* Clear them out. */ + for (i = 0; i < loongarch_linux_watch_get_num_valid (regs); i++) + { + loongarch_linux_watch_set_addr (regs, i, 0); + loongarch_linux_watch_set_mask (regs, i, 0); + loongarch_linux_watch_set_irw (regs, i, 0); + } + + w = current_watches; + while (w) + { + uint32_t irw = loongarch_linux_watch_type_to_irw (w->type); + + i = loongarch_linux_watch_try_one_watch (regs, w->addr, w->len, irw); + /* They must all fit, because we previously calculated that they + would. */ + gdb_assert (i); + w = w->next; + } +} diff --git a/gdb/nat/loongarch-linux-watch.h b/gdb/nat/loongarch-linux-watch.h new file mode 100644 index 0000000..ab80b44 --- /dev/null +++ b/gdb/nat/loongarch-linux-watch.h @@ -0,0 +1,132 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Loongson Ltd. + + This file is part of GDB. + + Based on MIPS target. + + 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 . */ + +#ifndef LOONGARCH_LINUX_WATCH_H +#define LOONGARCH_LINUX_WATCH_H 1 + +#include +#include "gdbsupport/break-common.h" + +#define MAX_DEBUG_REGISTER 16 + +/* If macro PTRACE_GET_WATCH_REGS is not defined, kernel header doesn't + have hardware watchpoint-related structures. Define them below. */ + +#ifndef PTRACE_GET_WATCH_REGS +#define PTRACE_GET_WATCH_REGS 0xd0 +#define PTRACE_SET_WATCH_REGS 0xd1 + +enum pt_watch_style +{ + pt_watch_style_la32, + pt_watch_style_la64 +}; + +/* A value of zero in a watchlo indicates that it is available. */ + +struct la32_watch_regs +{ + uint32_t addr; + /* Lower 16 bits of watchhi. */ + uint32_t mask; + /* Valid mask and I R W bits. + * bit 0 -- 1 if W bit is usable. + * bit 1 -- 1 if R bit is usable. + * bit 2 -- 1 if I bit is usable. + * bits 3 - 11 -- Valid watchhi mask bits. + */ + uint8_t irw; + uint8_t irwstat; + uint8_t irwmask; + /* There is confusion across gcc versions about structure alignment, + so we force 8 byte alignment for these structures so they match + the kernel even if it was build with a different gcc version. */ +} __attribute__ ((aligned (8))); + +struct la64_watch_regs +{ + uint64_t addr; + uint64_t mask; + uint8_t irw; + uint8_t irwstat; + uint8_t irwmask; +} __attribute__ ((aligned (8))); + +struct pt_watch_regs +{ + uint16_t max_valid; + uint16_t num_valid; + enum pt_watch_style style; + union + { + struct la32_watch_regs la32[MAX_DEBUG_REGISTER]; + struct la64_watch_regs la64[MAX_DEBUG_REGISTER]; + }; +}; + +#endif /* !PTRACE_GET_WATCH_REGS */ + +#define W_BIT 0 +#define R_BIT 1 +#define I_BIT 2 + +#define W_MASK (1 << W_BIT) +#define R_MASK (1 << R_BIT) +#define I_MASK (1 << I_BIT) + +#define IRW_MASK (I_MASK | R_MASK | W_MASK) + +/* We keep list of all watchpoints we should install and calculate the + watch register values each time the list changes. This allows for + easy sharing of watch registers for more than one watchpoint. */ + +struct loongarch_watchpoint +{ + CORE_ADDR addr; + int len; + enum target_hw_bp_type type; + struct loongarch_watchpoint *next; +}; + +uint32_t loongarch_linux_watch_get_num_valid (struct pt_watch_regs *regs); +uint8_t loongarch_linux_watch_get_irwmask (struct pt_watch_regs *regs, int n); +uint8_t loongarch_linux_watch_get_irwstat (struct pt_watch_regs *regs, int n); +CORE_ADDR loongarch_linux_watch_get_addr (struct pt_watch_regs *regs, int n); +void loongarch_linux_watch_set_addr (struct pt_watch_regs *regs, int n, + CORE_ADDR value); +CORE_ADDR loongarch_linux_watch_get_mask (struct pt_watch_regs *regs, int n); +void loongarch_linux_watch_set_mask (struct pt_watch_regs *regs, int n, + CORE_ADDR value); +uint8_t loongarch_linux_watch_get_irw (struct pt_watch_regs *regs, int n); +void loongarch_linux_watch_set_irw (struct pt_watch_regs *regs, int n, + uint8_t value); +int loongarch_linux_watch_try_one_watch (struct pt_watch_regs *regs, + CORE_ADDR addr, int len, + uint32_t irw); +void loongarch_linux_watch_populate_regs ( + struct loongarch_watchpoint *current_watches, struct pt_watch_regs *regs); +uint32_t loongarch_linux_watch_type_to_irw (enum target_hw_bp_type type); + +int loongarch_linux_read_watch_registers (long lwpid, + struct pt_watch_regs *watch_readback, + int *watch_readback_valid, + int force); + +#endif /* #define LOONGARCH_LINUX_WATCH_H */ diff --git a/gdb/remote.c b/gdb/remote.c index f2271ad..94abdca 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -2070,6 +2070,8 @@ enum { PACKET_qXfer_statictrace_read, PACKET_qXfer_traceframe_info, PACKET_qXfer_uib, + PACKET_qXfer_loongarch_read, + PACKET_qXfer_loongarch_write, PACKET_qGetTIBAddr, PACKET_qGetTLSAddr, PACKET_qSupported, @@ -5250,6 +5252,10 @@ static const struct protocol_feature remote_protocol_features[] = { PACKET_qXfer_threads }, { "qXfer:traceframe-info:read", PACKET_DISABLE, remote_supported_packet, PACKET_qXfer_traceframe_info }, + { "qXfer:loongarch:read", PACKET_DISABLE, remote_supported_packet, + PACKET_qXfer_loongarch_read }, + { "qXfer:loongarch:write", PACKET_DISABLE, remote_supported_packet, + PACKET_qXfer_loongarch_write }, { "QPassSignals", PACKET_DISABLE, remote_supported_packet, PACKET_QPassSignals }, { "QCatchSyscalls", PACKET_DISABLE, remote_supported_packet, @@ -11264,6 +11270,18 @@ remote_target::xfer_partial (enum target_object object, return TARGET_XFER_E_IO; } + if (object == TARGET_OBJECT_LARCH) + { + if (readbuf) + return remote_read_qxfer ("loongarch", annex, readbuf, offset, len, + xfered_len, &remote_protocol_packets + [PACKET_qXfer_loongarch_read]); + else + return remote_write_qxfer ("loongarch", annex, writebuf, offset, len, + xfered_len, &remote_protocol_packets + [PACKET_qXfer_loongarch_write]); + } + /* Only handle flash writes. */ if (writebuf != NULL) { @@ -15099,6 +15117,13 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL, add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_uib], "qXfer:uib:read", "unwind-info-block", 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_loongarch_read], + "qXfer:loongarch:read", "read-loongarch-object", 0); + + add_packet_config_cmd + (&remote_protocol_packets[PACKET_qXfer_loongarch_write], + "qXfer:loongarch:write", "write-loongarch-object", 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_qGetTLSAddr], "qGetTLSAddr", "get-thread-local-storage-address", 0); diff --git a/gdb/target.h b/gdb/target.h index ddd4f38..f3f6045 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -135,6 +135,9 @@ enum inferior_event_type enum target_object { + /* LARCH target specific transfer. See "loongarch-nat.c" "corelow.c" + and "remote.c". */ + TARGET_OBJECT_LARCH, /* AVR target specific transfer. See "avr-tdep.c" and "remote.c". */ TARGET_OBJECT_AVR, /* Transfer up-to LEN bytes of memory starting at OFFSET. */ diff --git a/gdb/testsuite/gdb.base/dump.exp b/gdb/testsuite/gdb.base/dump.exp index 52c6983..53e6873 100644 --- a/gdb/testsuite/gdb.base/dump.exp +++ b/gdb/testsuite/gdb.base/dump.exp @@ -145,11 +145,13 @@ make_dump_file "dump srec val [set intarr1.srec] intarray" \ make_dump_file "dump srec val [set intstr1.srec] intstruct" \ "dump struct as value, srec" +if { ![istarget loongarch*-*-*] } { make_dump_file "dump ihex val [set intarr1.ihex] intarray" \ "dump array as value, intel hex" make_dump_file "dump ihex val [set intstr1.ihex] intstruct" \ "dump struct as value, intel hex" +} make_dump_file "dump tekhex val [set intarr1.tekhex] intarray" \ "dump array as value, tekhex" @@ -246,11 +248,13 @@ make_dump_file "dump srec mem [set intarr2.srec] $array_start $array_end" \ make_dump_file "dump srec mem [set intstr2.srec] $struct_start $struct_end" \ "dump struct as memory, srec" +if { ![istarget loongarch*-*-*] } { make_dump_file "dump ihex mem [set intarr2.ihex] $array_start $array_end" \ "dump array as memory, ihex" make_dump_file "dump ihex mem [set intstr2.ihex] $struct_start $struct_end" \ "dump struct as memory, ihex" +} make_dump_file "dump tekhex mem [set intarr2.tekhex] $array_start $array_end" \ "dump array as memory, tekhex" diff --git a/gdb/testsuite/gdb.base/float.exp b/gdb/testsuite/gdb.base/float.exp index dc5e2fa..d179a8f 100644 --- a/gdb/testsuite/gdb.base/float.exp +++ b/gdb/testsuite/gdb.base/float.exp @@ -120,6 +120,8 @@ if { [is_aarch64_target] } then { pass "info float (without FPU)" } } +} elseif [istarget "loongarch*-*-*"] then { + gdb_test "info float" "f.*fcc0.*fcsr.*" "info float" } else { gdb_test "info float" "No floating.point info available for this processor." "info float (unknown target)" } diff --git a/gdb/testsuite/gdb.trace/entry-values.exp b/gdb/testsuite/gdb.trace/entry-values.exp index 3695a1e..f19f5b9 100644 --- a/gdb/testsuite/gdb.trace/entry-values.exp +++ b/gdb/testsuite/gdb.trace/entry-values.exp @@ -62,6 +62,8 @@ if { [istarget "arm*-*-*"] || [istarget "aarch64*-*-*"] } { # returns. The only exception is JALRC, in which case execution # resumes from `insn1' instead. set call_insn {jalrc|[jb]al[sxr]*[ \t][^\r\n]+\r\n} +} elseif { [istarget "loongarch*-*-*"] } { + set call_insn "bl" } else { set call_insn "call" } diff --git a/gdb/testsuite/gdb.xml/tdesc-regs.exp b/gdb/testsuite/gdb.xml/tdesc-regs.exp index 7402ba8..4b94747 100644 --- a/gdb/testsuite/gdb.xml/tdesc-regs.exp +++ b/gdb/testsuite/gdb.xml/tdesc-regs.exp @@ -83,6 +83,11 @@ switch -glob -- [istarget] { set regdir "i386/" set core-regs {64bit-core.xml 64bit-sse.xml} } + "loongarch64-*-*" { + set architecture "loongarch64" + set regdir "loongarch/" + set core-regs {base64.xml fpu64.xml lbt64.xml lsx.xml lasx.xml} + } } # If no core registers were specified, assume this target does not -- 2.36.0