gdb: LoongArch: Add initial baremetal support
authorTiezhu Yang <yangtiezhu@loongson.cn>
Fri, 11 Feb 2022 12:13:21 +0000 (20:13 +0800)
committerTiezhu Yang <yangtiezhu@loongson.cn>
Fri, 11 Feb 2022 12:13:21 +0000 (20:13 +0800)
This commit adds initial baremetal support for LoongArch.

Signed-off-by: Zhensong Liu <liuzhensong@loongson.cn>
Signed-off-by: Qing zhang <zhangqing@loongson.cn>
Signed-off-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
gdb/loongarch-tdep.c [new file with mode: 0644]
gdb/loongarch-tdep.h [new file with mode: 0644]

diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c
new file mode 100644 (file)
index 0000000..ffcff03
--- /dev/null
@@ -0,0 +1,316 @@
+/* Target-dependent code for the LoongArch architecture, 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 <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "arch-utils.h"
+#include "dwarf2/frame.h"
+#include "elf-bfd.h"
+#include "frame-unwind.h"
+#include "loongarch-tdep.h"
+#include "target-descriptions.h"
+#include "trad-frame.h"
+#include "user-regs.h"
+
+/* Implement the loongarch_skip_prologue gdbarch method.  */
+
+static CORE_ADDR
+loongarch_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR 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, nullptr, &func_addr, nullptr))
+    {
+      CORE_ADDR post_prologue_pc
+       = skip_prologue_using_sal (gdbarch, func_addr);
+      if (post_prologue_pc != 0)
+       return std::max (pc, post_prologue_pc);
+    }
+
+  return 0;
+}
+
+/* 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);
+}
+
+/* Generate, or return the cached frame cache for 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;
+
+  if (*this_cache != nullptr)
+    return (struct trad_frame_cache *) *this_cache;
+
+  cache = trad_frame_cache_zalloc (this_frame);
+  *this_cache = cache;
+
+  loongarch_gdbarch_tdep *tdep = (loongarch_gdbarch_tdep *) gdbarch_tdep (gdbarch);
+  trad_frame_set_reg_realreg (cache, gdbarch_pc_regnum (gdbarch), tdep->regs.ra);
+
+  pc = get_frame_address_in_block (this_frame);
+  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   =*/nullptr,
+  /*.sniffer      =*/default_frame_sniffer,
+  /*.dealloc_cache =*/nullptr,
+  /*.prev_arch    =*/nullptr,
+};
+
+/* Implement the "dwarf2_reg_to_regnum" gdbarch method.  */
+
+static int
+loongarch_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, int num)
+{
+  loongarch_gdbarch_tdep *tdep = (loongarch_gdbarch_tdep *) gdbarch_tdep (gdbarch);
+  auto regs = tdep->regs;
+
+  if (0 <= num && num < 32)
+    return regs.r + num;
+  else
+    return -1;
+}
+
+static constexpr gdb_byte loongarch_default_breakpoint[] = {0x05, 0x00, 0x2a, 0x00};
+typedef BP_MANIPULATION (loongarch_default_breakpoint) loongarch_breakpoint;
+
+/* Extract a set of required target features out of ABFD.  If ABFD is nullptr
+   then a LOONGARCH_GDBARCH_FEATURES is returned in its default state.  */
+
+static struct loongarch_gdbarch_features
+loongarch_features_from_bfd (const bfd *abfd)
+{
+  struct loongarch_gdbarch_features features;
+
+  /* Now try to improve on the defaults by looking at the binary we are
+     going to execute.  We assume the user knows what they are doing and
+     that the target will match the binary.  Remember, this code path is
+     only used at all if the target hasn't given us a description, so this
+     is really a last ditched effort to do something sane before giving
+     up.  */
+  if (abfd != nullptr && bfd_get_flavour (abfd) == bfd_target_elf_flavour)
+    {
+      unsigned char eclass = elf_elfheader (abfd)->e_ident[EI_CLASS];
+
+      if (eclass == ELFCLASS32)
+       features.xlen = 4;
+      else if (eclass == ELFCLASS64)
+       features.xlen = 8;
+      else
+       internal_error (__FILE__, __LINE__,
+                       _("unknown ELF header class %d"), eclass);
+    }
+
+  return features;
+}
+
+/* Find a suitable default target description.  Use the contents of INFO,
+   specifically the bfd object being executed, to guide the selection of a
+   suitable default target description.  */
+
+static const struct target_desc *
+loongarch_find_default_target_description (const struct gdbarch_info info)
+{
+  /* Extract desired feature set from INFO.  */
+  struct loongarch_gdbarch_features features
+    = loongarch_features_from_bfd (info.abfd);
+
+  /* If the XLEN field is still 0 then we got nothing useful from INFO.BFD,
+     maybe there was no bfd object.  In this case we fall back to a minimal
+     useful target with no floating point, the x-register size is selected
+     based on the architecture from INFO.  */
+  if (features.xlen == 0)
+    features.xlen = info.bfd_arch_info->bits_per_address == 32 ? 4 : 8;
+
+  /* Now build a target description based on the feature set.  */
+  return loongarch_lookup_target_description (features);
+}
+
+/* Initialize the current architecture based on INFO  */
+
+static struct gdbarch *
+loongarch_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+{
+  const struct target_desc *tdesc = info.target_desc;
+
+  /* Ensure we always have a target description.  */
+  if (!tdesc_has_registers (tdesc))
+    tdesc = loongarch_find_default_target_description (info);
+
+  const struct tdesc_feature *feature_cpu
+    = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.base");
+  if (feature_cpu == nullptr)
+    return nullptr;
+
+  int xlen_bitsize = tdesc_register_bitsize (feature_cpu, "pc");
+  struct loongarch_gdbarch_features features;
+  features.xlen = (xlen_bitsize / 8);
+
+  size_t regnum = 0;
+  tdesc_arch_data_up tdesc_data = tdesc_data_alloc ();
+  loongarch_gdbarch_tdep *tdep = new loongarch_gdbarch_tdep;
+  tdep->regs.r = regnum;
+
+  /* Validate the description provides the mandatory base registers
+     and allocate their numbers.  */
+  bool valid_p = true;
+  for (int i = 0; i < 32; i++)
+    valid_p &= tdesc_numbered_register (feature_cpu, tdesc_data.get (), regnum++,
+                                       loongarch_r_normal_name[i] + 1);
+  valid_p &= tdesc_numbered_register (feature_cpu, tdesc_data.get (),
+                                     tdep->regs.pc = regnum++, "pc");
+  valid_p &= tdesc_numbered_register (feature_cpu, tdesc_data.get (),
+                                     tdep->regs.badv = regnum++, "badv");
+  if (!valid_p)
+    return nullptr;
+
+  /* LoongArch code is always little-endian.  */
+  info.byte_order_for_code = BFD_ENDIAN_LITTLE;
+
+  /* Have a look at what the supplied (if any) bfd object requires of the
+     target, then check that this matches with what the target is
+     providing.  */
+  struct loongarch_gdbarch_features abi_features
+    = loongarch_features_from_bfd (info.abfd);
+
+  /* If the ABI_FEATURES xlen is 0 then this indicates we got no useful abi
+     features from the INFO object.  In this case we just treat the
+     hardware features as defining the abi.  */
+  if (abi_features.xlen == 0)
+    abi_features = features;
+
+  /* Find a candidate among the list of pre-declared architectures.  */
+  for (arches = gdbarch_list_lookup_by_info (arches, &info);
+       arches != nullptr;
+       arches = gdbarch_list_lookup_by_info (arches->next, &info))
+    {
+      /* Check that the feature set of the ARCHES matches the feature set
+        we are looking for.  If it doesn't then we can't reuse this
+        gdbarch.  */
+      loongarch_gdbarch_tdep *candidate_tdep
+       = (loongarch_gdbarch_tdep *) gdbarch_tdep (arches->gdbarch);
+
+      if (candidate_tdep->abi_features != abi_features)
+       continue;
+
+      break;
+    }
+
+  if (arches != nullptr)
+    return arches->gdbarch;
+
+  /* None found, so create a new architecture from the information provided.  */
+  struct gdbarch *gdbarch = gdbarch_alloc (&info, tdep);
+  tdep->abi_features = abi_features;
+
+  /* Target data types.  */
+  set_gdbarch_short_bit (gdbarch, 16);
+  set_gdbarch_int_bit (gdbarch, 32);
+  set_gdbarch_long_bit (gdbarch, info.bfd_arch_info->bits_per_address);
+  set_gdbarch_long_long_bit (gdbarch, 64);
+  set_gdbarch_ptr_bit (gdbarch, info.bfd_arch_info->bits_per_address);
+  set_gdbarch_char_signed (gdbarch, 0);
+
+  info.target_desc = tdesc;
+  info.tdesc_data = tdesc_data.get ();
+
+  /* Information about registers.  */
+  tdep->regs.ra = tdep->regs.r + 1;
+  tdep->regs.sp = tdep->regs.r + 3;
+  set_gdbarch_num_regs (gdbarch, regnum);
+  set_gdbarch_sp_regnum (gdbarch, tdep->regs.sp);
+  set_gdbarch_pc_regnum (gdbarch, tdep->regs.pc);
+
+  /* Finalise the target description registers.  */
+  tdesc_use_registers (gdbarch, tdesc, std::move (tdesc_data));
+
+  /* Advance PC across function entry code.  */
+  set_gdbarch_skip_prologue (gdbarch, loongarch_skip_prologue);
+
+  /* Stack grows downward.  */
+  set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+
+  /* Frame info.  */
+  set_gdbarch_frame_align (gdbarch, loongarch_frame_align);
+
+  /* Breakpoint manipulation.  */
+  set_gdbarch_breakpoint_kind_from_pc (gdbarch, loongarch_breakpoint::kind_from_pc);
+  set_gdbarch_sw_breakpoint_from_kind (gdbarch, loongarch_breakpoint::bp_from_kind);
+
+  /* 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);
+
+  /* Hook in OS ABI-specific overrides, if they have been registered.  */
+  gdbarch_init_osabi (info, gdbarch);
+
+  return gdbarch;
+}
+
+void _initialize_loongarch_tdep ();
+void
+_initialize_loongarch_tdep ()
+{
+  gdbarch_register (bfd_arch_loongarch, loongarch_gdbarch_init, nullptr);
+}
diff --git a/gdb/loongarch-tdep.h b/gdb/loongarch-tdep.h
new file mode 100644 (file)
index 0000000..b2fd16e
--- /dev/null
@@ -0,0 +1,49 @@
+/* Target-dependent header for the LoongArch architecture, 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 <http://www.gnu.org/licenses/>.  */
+
+#ifndef LOONGARCH_TDEP_H
+#define LOONGARCH_TDEP_H
+
+#include "gdbarch.h"
+#include "arch/loongarch.h"
+#include "regset.h"
+
+#include "elf/loongarch.h"
+#include "opcode/loongarch.h"
+
+/* Register set definitions.  */
+extern const struct regset loongarch_gregset;
+
+/* Target-dependent structure in gdbarch.  */
+struct loongarch_gdbarch_tdep : gdbarch_tdep
+{
+  /* Features about the abi that impact how the gdbarch is configured.  */
+  struct loongarch_gdbarch_features abi_features;
+
+  struct
+  {
+    int r;     /* General register. */
+    int ra;    /* Return Address.  */
+    int sp;    /* Stack Pointer.  */
+    int pc;    /* Program Counter.  */
+    int badv;  /* Bad vaddr for addressing exception.  */
+  } regs;      /* LoongArch registers  */
+};
+
+#endif /* LOONGARCH_TDEP_H  */