Signed-off-by: yangchenguang <yangchenguang@kylinsec.com.cn> (cherry picked from commit 6d10caf6da2fd566b2d4ed52ebc1b89aa22628f0)
5651 lines
192 KiB
Diff
5651 lines
192 KiB
Diff
From 39adfc7cd9f340503f8ee64777e716b9b7a65ec7 Mon Sep 17 00:00:00 2001
|
|
From: Qing Zhang <zhangqing@loongson.cn>
|
|
Date: Tue, 29 Mar 2022 16:11:47 +0800
|
|
Subject: [PATCH 3/5] gdb-Add LoongArch gdb support
|
|
|
|
Signed-off-by: Qing Zhang <zhangqing@loongson.cn>
|
|
---
|
|
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 <http://www.gnu.org/licenses/>. */
|
|
+
|
|
+/* 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 <sys/uio.h>
|
|
+
|
|
+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 <http://www.gnu.org/licenses/>. */
|
|
+
|
|
+#ifndef LOONGARCH_LINUX_NAT_H
|
|
+#define LOONGARCH_LINUX_NAT_H
|
|
+#include <stdint.h>
|
|
+
|
|
+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 <http://www.gnu.org/licenses/>. */
|
|
+
|
|
+#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 <http://www.gnu.org/licenses/>. */
|
|
+
|
|
+#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 @@
|
|
+<?xml version="1.0"?>
|
|
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
|
|
+ Contributed by Loongson Ltd.
|
|
+
|
|
+ Copying and distribution of this file, with or without modification,
|
|
+ are permitted in any medium without royalty provided the copyright
|
|
+ notice and this notice are preserved. -->
|
|
+
|
|
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
|
|
+<feature name="org.gnu.gdb.loongarch.base">
|
|
+ <reg name="r0" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r1" bitsize="32" type="code_ptr" group="general"/>
|
|
+ <reg name="r2" bitsize="32" type="data_ptr" group="general"/>
|
|
+ <reg name="r3" bitsize="32" type="data_ptr" group="general"/>
|
|
+ <reg name="r4" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r5" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r6" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r7" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r8" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r9" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r10" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r11" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r12" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r13" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r14" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r15" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r16" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r17" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r18" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r19" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r20" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r21" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r22" bitsize="32" type="data_ptr" group="general"/>
|
|
+ <reg name="r23" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r24" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r25" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r26" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r27" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r28" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r29" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r30" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="r31" bitsize="32" type="uint32" group="general"/>
|
|
+ <reg name="pc" bitsize="32" type="code_ptr" group="general"/>
|
|
+ <reg name="badvaddr" bitsize="32" type="code_ptr" group="general"/>
|
|
+</feature>
|
|
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 @@
|
|
+<?xml version="1.0"?>
|
|
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
|
|
+ Contributed by Loongson Ltd.
|
|
+
|
|
+ Copying and distribution of this file, with or without modification,
|
|
+ are permitted in any medium without royalty provided the copyright
|
|
+ notice and this notice are preserved. -->
|
|
+
|
|
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
|
|
+<feature name="org.gnu.gdb.loongarch.base">
|
|
+ <reg name="r0" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r1" bitsize="64" type="code_ptr" group="general"/>
|
|
+ <reg name="r2" bitsize="64" type="data_ptr" group="general"/>
|
|
+ <reg name="r3" bitsize="64" type="data_ptr" group="general"/>
|
|
+ <reg name="r4" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r5" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r6" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r7" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r8" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r9" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r10" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r11" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r12" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r13" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r14" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r15" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r16" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r17" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r18" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r19" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r20" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r21" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r22" bitsize="64" type="data_ptr" group="general"/>
|
|
+ <reg name="r23" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r24" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r25" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r26" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r27" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r28" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r29" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r30" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="r31" bitsize="64" type="uint64" group="general"/>
|
|
+ <reg name="pc" bitsize="64" type="code_ptr" group="general"/>
|
|
+ <reg name="badvaddr" bitsize="64" type="code_ptr" group="general"/>
|
|
+</feature>
|
|
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 @@
|
|
+<?xml version="1.0"?>
|
|
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
|
|
+ Contributed by Loongson Ltd.
|
|
+
|
|
+ Copying and distribution of this file, with or without modification,
|
|
+ are permitted in any medium without royalty provided the copyright
|
|
+ notice and this notice are preserved. -->
|
|
+
|
|
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
|
|
+<feature name="org.gnu.gdb.loongarch.fpu">
|
|
+
|
|
+ <reg name="f0" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f1" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f2" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f3" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f4" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f5" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f6" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f7" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f8" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f9" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f10" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f11" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f12" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f13" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f14" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f15" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f16" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f17" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f18" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f19" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f20" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f21" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f22" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f23" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f24" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f25" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f26" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f27" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f28" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f29" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f30" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="f31" bitsize="32" type="ieee_single" group="float"/>
|
|
+ <reg name="fcc0" bitsize="8" type="uint8" group="float"/>
|
|
+ <reg name="fcc1" bitsize="8" type="uint8" group="float"/>
|
|
+ <reg name="fcc2" bitsize="8" type="uint8" group="float"/>
|
|
+ <reg name="fcc3" bitsize="8" type="uint8" group="float"/>
|
|
+ <reg name="fcc4" bitsize="8" type="uint8" group="float"/>
|
|
+ <reg name="fcc5" bitsize="8" type="uint8" group="float"/>
|
|
+ <reg name="fcc6" bitsize="8" type="uint8" group="float"/>
|
|
+ <reg name="fcc7" bitsize="8" type="uint8" group="float"/>
|
|
+ <reg name="fcsr" bitsize="32" type="uint32" group="float"/>
|
|
+</feature>
|
|
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 @@
|
|
+<?xml version="1.0"?>
|
|
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
|
|
+ Contributed by Loongson Ltd.
|
|
+
|
|
+ Copying and distribution of this file, with or without modification,
|
|
+ are permitted in any medium without royalty provided the copyright
|
|
+ notice and this notice are preserved. -->
|
|
+
|
|
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
|
|
+<feature name="org.gnu.gdb.loongarch.fpu">
|
|
+
|
|
+ <union id="fpu64type">
|
|
+ <field name="f" type="ieee_single"/>
|
|
+ <field name="d" type="ieee_double"/>
|
|
+ </union>
|
|
+
|
|
+ <reg name="f0" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f1" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f2" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f3" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f4" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f5" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f6" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f7" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f8" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f9" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f10" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f11" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f12" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f13" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f14" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f15" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f16" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f17" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f18" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f19" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f20" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f21" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f22" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f23" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f24" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f25" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f26" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f27" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f28" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f29" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f30" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="f31" bitsize="64" type="fpu64type" group="float"/>
|
|
+ <reg name="fcc0" bitsize="8" type="uint8" group="float"/>
|
|
+ <reg name="fcc1" bitsize="8" type="uint8" group="float"/>
|
|
+ <reg name="fcc2" bitsize="8" type="uint8" group="float"/>
|
|
+ <reg name="fcc3" bitsize="8" type="uint8" group="float"/>
|
|
+ <reg name="fcc4" bitsize="8" type="uint8" group="float"/>
|
|
+ <reg name="fcc5" bitsize="8" type="uint8" group="float"/>
|
|
+ <reg name="fcc6" bitsize="8" type="uint8" group="float"/>
|
|
+ <reg name="fcc7" bitsize="8" type="uint8" group="float"/>
|
|
+ <reg name="fcsr" bitsize="32" type="uint32" group="float"/>
|
|
+</feature>
|
|
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 @@
|
|
+<?xml version="1.0"?>
|
|
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
|
|
+ Contributed by Loongson Ltd.
|
|
+
|
|
+ Copying and distribution of this file, with or without modification,
|
|
+ are permitted in any medium without royalty provided the copyright
|
|
+ notice and this notice are preserved. -->
|
|
+
|
|
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
|
|
+<feature name="org.gnu.gdb.loongarch.lasx">
|
|
+ <vector id="v32i8" type="int8" count="32"/>
|
|
+ <vector id="v16i16" type="int16" count="16"/>
|
|
+ <vector id="v8i32" type="int32" count="8"/>
|
|
+ <vector id="v4i64" type="int64" count="4"/>
|
|
+ <vector id="v8f32" type="ieee_single" count="8"/>
|
|
+ <vector id="v4f64" type="ieee_double" count="4"/>
|
|
+
|
|
+ <union id="lasxv">
|
|
+ <field name="v32i8" type="v32i8"/>
|
|
+ <field name="v16i16" type="v16i16"/>
|
|
+ <field name="v8i32" type="v8i32"/>
|
|
+ <field name="v4i64" type="v4i64"/>
|
|
+ <field name="v8f32" type="v8f32"/>
|
|
+ <field name="v4f64" type="v4f64"/>
|
|
+ </union>
|
|
+
|
|
+ <reg name="xr0" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr1" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr2" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr3" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr4" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr5" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr6" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr7" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr8" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr9" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr10" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr11" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr12" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr13" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr14" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr15" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr16" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr17" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr18" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr19" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr20" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr21" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr22" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr23" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr24" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr25" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr26" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr27" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr28" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr29" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr30" bitsize="256" type="lasxv" group="lasx"/>
|
|
+ <reg name="xr31" bitsize="256" type="lasxv" group="lasx"/>
|
|
+</feature>
|
|
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 @@
|
|
+<?xml version="1.0"?>
|
|
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
|
|
+ Contributed by Loongson Ltd.
|
|
+
|
|
+ Copying and distribution of this file, with or without modification,
|
|
+ are permitted in any medium without royalty provided the copyright
|
|
+ notice and this notice are preserved. -->
|
|
+
|
|
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
|
|
+<feature name="org.gnu.gdb.loongarch.lbt">
|
|
+ <reg name="scr0" bitsize="32" type="uint32" group="lbt"/>
|
|
+ <reg name="scr1" bitsize="32" type="uint32" group="lbt"/>
|
|
+ <reg name="scr2" bitsize="32" type="uint32" group="lbt"/>
|
|
+ <reg name="scr3" bitsize="32" type="uint32" group="lbt"/>
|
|
+ <reg name="EFLAG" bitsize="32" type="uint32" group="lbt"/>
|
|
+ <reg name="x86_top" bitsize="8" type="uint8" group="lbt"/>
|
|
+</feature>
|
|
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 @@
|
|
+<?xml version="1.0"?>
|
|
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
|
|
+ Contributed by Loongson Ltd.
|
|
+
|
|
+ Copying and distribution of this file, with or without modification,
|
|
+ are permitted in any medium without royalty provided the copyright
|
|
+ notice and this notice are preserved. -->
|
|
+
|
|
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
|
|
+<feature name="org.gnu.gdb.loongarch.lbt">
|
|
+ <reg name="scr0" bitsize="64" type="uint64" group="lbt"/>
|
|
+ <reg name="scr1" bitsize="64" type="uint64" group="lbt"/>
|
|
+ <reg name="scr2" bitsize="64" type="uint64" group="lbt"/>
|
|
+ <reg name="scr3" bitsize="64" type="uint64" group="lbt"/>
|
|
+ <reg name="EFLAG" bitsize="32" type="uint32" group="lbt"/>
|
|
+ <reg name="x86_top" bitsize="8" type="uint8" group="lbt"/>
|
|
+</feature>
|
|
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 @@
|
|
+<?xml version="1.0"?>
|
|
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
|
|
+ Contributed by Loongson Ltd.
|
|
+
|
|
+ Copying and distribution of this file, with or without modification,
|
|
+ are permitted in any medium without royalty provided the copyright
|
|
+ notice and this notice are preserved. -->
|
|
+
|
|
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
|
|
+<feature name="org.gnu.gdb.loongarch.lsx">
|
|
+ <vector id="v16i8" type="int8" count="16"/>
|
|
+ <vector id="v8i16" type="int16" count="8"/>
|
|
+ <vector id="v4i32" type="int32" count="4"/>
|
|
+ <vector id="v2i64" type="int64" count="2"/>
|
|
+ <vector id="v4f32" type="ieee_single" count="4"/>
|
|
+ <vector id="v2f64" type="ieee_double" count="2"/>
|
|
+
|
|
+ <union id="lsxv">
|
|
+ <field name="v16i8" type="v16i8"/>
|
|
+ <field name="v8i16" type="v8i16"/>
|
|
+ <field name="v4i32" type="v4i32"/>
|
|
+ <field name="v2i64" type="v2i64"/>
|
|
+ <field name="v4f32" type="v4f32"/>
|
|
+ <field name="v2f64" type="v2f64"/>
|
|
+ </union>
|
|
+
|
|
+ <reg name="vr0" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr1" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr2" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr3" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr4" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr5" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr6" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr7" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr8" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr9" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr10" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr11" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr12" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr13" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr14" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr15" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr16" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr17" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr18" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr19" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr20" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr21" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr22" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr23" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr24" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr25" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr26" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr27" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr28" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr29" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr30" bitsize="128" type="lsxv" group="lsx"/>
|
|
+ <reg name="vr31" bitsize="128" type="lsxv" group="lsx"/>
|
|
+</feature>
|
|
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 <http://www.gnu.org/licenses/>. */
|
|
+
|
|
+#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 <asm/ptrace.h>
|
|
+#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 <asm/ptrace.h> */
|
|
+ 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 <http://www.gnu.org/licenses/>. */
|
|
+
|
|
+#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 <http://www.gnu.org/licenses/>. */
|
|
+
|
|
+#ifndef LOONGARCH_LINUX_TDEP_H
|
|
+#define LOONGARCH_LINUX_TDEP_H
|
|
+
|
|
+#include <regset.h>
|
|
+
|
|
+#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 <http://www.gnu.org/licenses/>. */
|
|
+
|
|
+#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 <algorithm>
|
|
+
|
|
+/* 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<CORE_ADDR>
|
|
+loongarch_deal_with_atomic_sequence (struct regcache *regcache, CORE_ADDR pc)
|
|
+{
|
|
+ struct gdbarch *gdbarch = regcache->arch ();
|
|
+ CORE_ADDR next_pc;
|
|
+ std::vector<CORE_ADDR> 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<CORE_ADDR>
|
|
+loongarch_software_single_step (struct regcache *regcache);
|
|
+std::vector<CORE_ADDR>
|
|
+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<CORE_ADDR> 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 : "<unknown>",
|
|
+ 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 ? "<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_data_t> &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<stack_data_t> &gp,
|
|
+ std::vector<stack_data_t> &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<stack_data_t> gpreg;
|
|
+ std::vector<stack_data_t> 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_data_t> 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 <http://www.gnu.org/licenses/>. */
|
|
+
|
|
+#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 <http://www.gnu.org/licenses/>. */
|
|
+
|
|
+#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 <http://www.gnu.org/licenses/>. */
|
|
+
|
|
+#ifndef LOONGARCH_LINUX_WATCH_H
|
|
+#define LOONGARCH_LINUX_WATCH_H 1
|
|
+
|
|
+#include <asm/ptrace.h>
|
|
+#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
|
|
|