From e5ab6af52d38ee068c1860ef113b7db4cc985cfb Mon Sep 17 00:00:00 2001 From: Youling Tang Date: Mon, 30 May 2022 20:08:30 +0800 Subject: [PATCH] gdbserver: Add LoongArch/Linux support Implement LoongArch/Linux support, including XML target description handling based on features determined, GPR regset support, and software breakpoint handling. In the Linux kernel code of LoongArch, ptrace implements PTRACE_POKEUSR and PTRACE_PEEKUSR in the arch_ptrace function, so srv_linux_usrregs is set to yes. With this patch on LoongArch: $ make check-gdb TESTS="gdb.server/server-connect.exp" [...] # of expected passes 18 [...] Signed-off-by: Youling Tang Signed-off-by: Tiezhu Yang --- gdb/NEWS | 3 + gdb/arch/loongarch.c | 12 +- gdb/arch/loongarch.h | 13 ++ gdbserver/Makefile.in | 2 + gdbserver/configure.srv | 6 + gdbserver/linux-loongarch-low.cc | 245 +++++++++++++++++++++++++++++++ 6 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 gdbserver/linux-loongarch-low.cc diff --git a/gdb/NEWS b/gdb/NEWS index 7164e18bfa5..0efcce7d3ef 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -694,6 +694,8 @@ QMemTags ** GDBserver is now supported on RISC-V GNU/Linux. + ** GDBserver is now supported on LoongArch GNU/Linux. + ** GDBserver no longer supports these host triplets: i[34567]86-*-lynxos* @@ -785,6 +787,7 @@ alias [-a] [--] ALIAS = COMMAND [DEFAULT-ARGS...] * New targets GNU/Linux/RISC-V (gdbserver) riscv*-*-linux* +GNU/Linux/LoongArch (gdbserver) loongarch*-*-linux* BPF bpf-unknown-none Z80 z80-unknown-* diff --git a/gdb/arch/loongarch.c b/gdb/arch/loongarch.c index 934f6e489c5..11310c1dd93 100644 --- a/gdb/arch/loongarch.c +++ b/gdb/arch/loongarch.c @@ -25,7 +25,13 @@ #include "../features/loongarch/base32.c" #include "../features/loongarch/base64.c" -static target_desc_up +#ifndef GDBSERVER +#define STATIC_IN_GDB static +#else +#define STATIC_IN_GDB +#endif + +STATIC_IN_GDB target_desc_up loongarch_create_target_description (const struct loongarch_gdbarch_features features) { /* Now we should create a new target description. */ @@ -51,6 +57,8 @@ loongarch_create_target_description (const struct loongarch_gdbarch_features fea return tdesc; } +#ifndef GDBSERVER + /* Wrapper used by std::unordered_map to generate hash for feature set. */ struct loongarch_gdbarch_features_hasher { @@ -86,3 +94,5 @@ loongarch_lookup_target_description (const struct loongarch_gdbarch_features fea loongarch_tdesc_cache.emplace (features, std::move (tdesc)); return ptr; } + +#endif /* !GDBSERVER */ diff --git a/gdb/arch/loongarch.h b/gdb/arch/loongarch.h index 9e10df967d1..8194ea66c0a 100644 --- a/gdb/arch/loongarch.h +++ b/gdb/arch/loongarch.h @@ -60,6 +60,17 @@ struct loongarch_gdbarch_features } }; +#ifdef GDBSERVER + +/* Create and return a target description that is compatible with FEATURES. + This is only used directly from the gdbserver where the created target + description is modified after it is return. */ + +target_desc_up loongarch_create_target_description + (const struct loongarch_gdbarch_features features); + +#else + /* Lookup an already existing target description matching FEATURES, or create a new target description if this is the first time we have seen FEATURES. For the same FEATURES the same target_desc is always @@ -70,4 +81,6 @@ struct loongarch_gdbarch_features const target_desc *loongarch_lookup_target_description (const struct loongarch_gdbarch_features features); +#endif /* GDBSERVER */ + #endif /* ARCH_LOONGARCH_H */ diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in index 6e14278cd4b..75e497753e8 100644 --- a/gdbserver/Makefile.in +++ b/gdbserver/Makefile.in @@ -176,6 +176,7 @@ SFILES = \ $(srcdir)/linux-arc-low.cc \ $(srcdir)/linux-arm-low.cc \ $(srcdir)/linux-ia64-low.cc \ + $(srcdir)/linux-loongarch-low.cc \ $(srcdir)/linux-low.cc \ $(srcdir)/linux-m68k-low.cc \ $(srcdir)/linux-mips-low.cc \ @@ -211,6 +212,7 @@ SFILES = \ $(srcdir)/../gdb/arch/arm.c \ $(srcdir)/../gdb/arch/arm-get-next-pcs.c \ $(srcdir)/../gdb/arch/arm-linux.c \ + $(srcdir)/../gdb/arch/loongarch.c \ $(srcdir)/../gdb/arch/ppc-linux-common.c \ $(srcdir)/../gdb/arch/riscv.c \ $(srcdir)/../gdb/nat/aarch64-mte-linux-ptrace.c \ diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv index d37053628fc..ebb6b32a6c4 100644 --- a/gdbserver/configure.srv +++ b/gdbserver/configure.srv @@ -126,6 +126,12 @@ case "${gdbserver_host}" in srv_tgtobj="$srv_linux_obj linux-ia64-low.o" srv_linux_usrregs=yes ;; + loongarch*-*-linux*) srv_tgtobj="arch/loongarch.o linux-loongarch-low.o" + srv_tgtobj="${srv_tgtobj} ${srv_linux_obj}" + srv_linux_regsets=yes + srv_linux_usrregs=yes + srv_linux_thread_db=yes + ;; m68*-*-linux*) if test "$gdb_cv_m68k_is_coldfire" = yes; then srv_regobj=reg-cf.o else diff --git a/gdbserver/linux-loongarch-low.cc b/gdbserver/linux-loongarch-low.cc new file mode 100644 index 00000000000..5d3739354e6 --- /dev/null +++ b/gdbserver/linux-loongarch-low.cc @@ -0,0 +1,245 @@ +/* GNU/Linux/LoongArch specific low level interface, for the remote server + for GDB. + Copyright (C) 2022 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "server.h" +#include "linux-low.h" +#include "tdesc.h" +#include "elf/common.h" +#include "arch/loongarch.h" + +/* Linux target ops definitions for the LoongArch architecture. */ + +class loongarch_target : public linux_process_target +{ +public: + + const regs_info *get_regs_info () override; + + int breakpoint_kind_from_pc (CORE_ADDR *pcptr) override; + + const gdb_byte *sw_breakpoint_from_kind (int kind, int *size) override; + +protected: + + void low_arch_setup () override; + + bool low_cannot_fetch_register (int regno) override; + + bool low_cannot_store_register (int regno) override; + + bool low_fetch_register (regcache *regcache, int regno) override; + + bool low_supports_breakpoints () override; + + CORE_ADDR low_get_pc (regcache *regcache) override; + + void low_set_pc (regcache *regcache, CORE_ADDR newpc) override; + + bool low_breakpoint_at (CORE_ADDR pc) override; +}; + +/* The singleton target ops object. */ + +static loongarch_target the_loongarch_target; + +bool +loongarch_target::low_cannot_fetch_register (int regno) +{ + gdb_assert_not_reached ("linux target op low_cannot_fetch_register " + "is not implemented by the target"); +} + +bool +loongarch_target::low_cannot_store_register (int regno) +{ + gdb_assert_not_reached ("linux target op low_cannot_store_register " + "is not implemented by the target"); +} + +/* Implementation of linux target ops method "low_arch_setup". */ + +void +loongarch_target::low_arch_setup () +{ + static const char *expedite_regs[] = { "r3", "pc", NULL }; + loongarch_gdbarch_features features; + target_desc_up tdesc; + + features.xlen = sizeof (elf_greg_t); + tdesc = loongarch_create_target_description (features); + + if (!tdesc->expedite_regs) + init_target_desc (tdesc.get (), expedite_regs); + current_process ()->tdesc = tdesc.release (); +} + +/* Collect GPRs from REGCACHE into BUF. */ + +static void +loongarch_fill_gregset (struct regcache *regcache, void *buf) +{ + const struct target_desc *tdesc = regcache->tdesc; + elf_gregset_t *regset = (elf_gregset_t *) buf; + int regno = find_regno (tdesc, "r0"); + int i; + + for (i = 1; i < 32; i++) + collect_register (regcache, regno + i, *regset + i); + collect_register_by_name (regcache, "pc", *regset + 32); + collect_register_by_name (regcache, "badv", *regset + 33); +} + +/* Supply GPRs from BUF into REGCACHE. */ + +static void +loongarch_store_gregset (struct regcache *regcache, const void *buf) +{ + const struct target_desc *tdesc = regcache->tdesc; + const elf_gregset_t *regset = (const elf_gregset_t *) buf; + int regno = find_regno (tdesc, "r0"); + int i; + + supply_register_zeroed (regcache, regno); + for (i = 1; i < 32; i++) + supply_register (regcache, regno + i, *regset + i); + supply_register_by_name (regcache, "pc", *regset + 32); + supply_register_by_name (regcache, "badv", *regset + 33); +} + +/* LoongArch/Linux regsets. */ +static struct regset_info loongarch_regsets[] = { + { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS, sizeof (elf_gregset_t), + GENERAL_REGS, loongarch_fill_gregset, loongarch_store_gregset }, + NULL_REGSET +}; + +/* LoongArch/Linux regset information. */ +static struct regsets_info loongarch_regsets_info = + { + loongarch_regsets, /* regsets */ + 0, /* num_regsets */ + NULL, /* disabled_regsets */ + }; + +/* Definition of linux_target_ops data member "regs_info". */ +static struct regs_info loongarch_regs = + { + NULL, /* regset_bitmap */ + NULL, /* usrregs */ + &loongarch_regsets_info, + }; + +/* Implementation of linux target ops method "get_regs_info". */ + +const regs_info * +loongarch_target::get_regs_info () +{ + return &loongarch_regs; +} + +/* Implementation of linux target ops method "low_fetch_register". */ + +bool +loongarch_target::low_fetch_register (regcache *regcache, int regno) +{ + const struct target_desc *tdesc = regcache->tdesc; + + if (regno != find_regno (tdesc, "r0")) + return false; + supply_register_zeroed (regcache, regno); + return true; +} + +bool +loongarch_target::low_supports_breakpoints () +{ + return true; +} + +/* Implementation of linux target ops method "low_get_pc". */ + +CORE_ADDR +loongarch_target::low_get_pc (regcache *regcache) +{ + if (register_size (regcache->tdesc, 0) == 8) + return linux_get_pc_64bit (regcache); + else + return linux_get_pc_32bit (regcache); +} + +/* Implementation of linux target ops method "low_set_pc". */ + +void +loongarch_target::low_set_pc (regcache *regcache, CORE_ADDR newpc) +{ + if (register_size (regcache->tdesc, 0) == 8) + linux_set_pc_64bit (regcache, newpc); + else + linux_set_pc_32bit (regcache, newpc); +} + +#define loongarch_breakpoint_len 4 + +/* LoongArch BRK software debug mode instruction. + This instruction needs to match gdb/loongarch-tdep.c + (loongarch_default_breakpoint). */ +static const gdb_byte loongarch_breakpoint[] = {0x05, 0x00, 0x2a, 0x00}; + +/* Implementation of target ops method "breakpoint_kind_from_pc". */ + +int +loongarch_target::breakpoint_kind_from_pc (CORE_ADDR *pcptr) +{ + return loongarch_breakpoint_len; +} + +/* Implementation of target ops method "sw_breakpoint_from_kind". */ + +const gdb_byte * +loongarch_target::sw_breakpoint_from_kind (int kind, int *size) +{ + *size = loongarch_breakpoint_len; + return (const gdb_byte *) &loongarch_breakpoint; +} + +/* Implementation of linux target ops method "low_breakpoint_at". */ + +bool +loongarch_target::low_breakpoint_at (CORE_ADDR pc) +{ + gdb_byte insn[loongarch_breakpoint_len]; + + read_memory (pc, (unsigned char *) &insn, loongarch_breakpoint_len); + if (memcmp (insn, loongarch_breakpoint, loongarch_breakpoint_len) == 0) + return true; + + return false; +} + +/* The linux target ops object. */ + +linux_process_target *the_linux_target = &the_loongarch_target; + +/* Initialize the LoongArch/Linux target. */ + +void +initialize_low_arch () +{ + initialize_regsets_info (&loongarch_regsets_info); +} -- 2.30.2