Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Andrew Burgess <andrew.burgess@embecosm.com>
To: Luis Machado <luis.machado@linaro.org>
Cc: Fredrik Hederstierna <fredrik.hederstierna@verisure.com>,
	binutils@sourceware.org, gdb-patches@sourceware.org,
	Paul Mathieu <paulmathieu@google.com>
Subject: Re: [PATCH 5/8] gdb/riscv: introduce bare metal core dump support
Date: Mon, 7 Dec 2020 15:17:02 +0000	[thread overview]
Message-ID: <20201207151702.GK2729@embecosm.com> (raw)
In-Reply-To: <ae2790a1-437d-ac93-b683-d8d0ff3d4bfc@linaro.org>

* Luis Machado <luis.machado@linaro.org> [2020-12-02 15:12:26 -0300]:

> CC-ing paulmathieu@google.com and fredrik.hederstierna@verisure.com, who
> were also looking into supporting bare metal ARM core files.
> 
> Just a quick note...
> 
> Back in October we pondered about this and it looked reasonable, at the
> time, to put some effort into documenting the format (unlike the current
> Linux core file format, which lacks good documentation).
> 
> My take on it is that we need to settle on a format that works for multiple
> architectures.

Hi Luis,

Thanks for your feedback, I'd love to better understand what you are
proposing here.

Could you expand a little on why we need a format that supports
multiple architectures (i.e. what benefits will this bring)?  And how
this would be different to the approach taken here?

Thanks,
Andrew

> 
> On 12/2/20 2:39 PM, Andrew Burgess wrote:
> > This commit adds the ability for bare metal RISC-V target to generate
> > core files from within GDB.
> > 
> > The intended use case is that a user will connect to a remote bare
> > metal target, debug up to some error condition, then generate a core
> > file in the normal way using:
> > 
> >    (gdb) generate-core-file
> > 
> > This core file can then be used to revisit the state of the remote
> > target without having to reconnect to the remote target.
> > 
> > The core file creation is all placed into a new file
> > riscv-none-tdep.c, this follows the existing naming pattern as
> > riscv-linux-tdep.c, and riscv-fbsd-tdep.c, where 'none' is the ABI
> > name.
> > 
> > Currently only the x-regs and f-regs (if present) are written out, and
> > the formatting follows the Linux layout, rather than inventing yet
> > another layout.  Future commits will add support for writing out the
> > CSRs.
> > 
> > gdb/ChangeLog:
> > 
> > 	* Makefile.in (ALL_TARGET_OBS): Add riscv-none-tdep.o.
> > 	(ALLDEPFILES): Add riscv-none-tdep.c.
> > 	* configure.tgt (riscv*-*-*): Include riscv-none-tdep.c.
> > 	* riscv-none-tdep.c: New file.
> > ---
> >   gdb/ChangeLog         |   8 +
> >   gdb/Makefile.in       |   2 +
> >   gdb/configure.tgt     |   2 +-
> >   gdb/riscv-none-tdep.c | 345 ++++++++++++++++++++++++++++++++++++++++++
> >   4 files changed, 356 insertions(+), 1 deletion(-)
> >   create mode 100644 gdb/riscv-none-tdep.c
> > 
> > diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> > index a86e8d648e6..f515670d03d 100644
> > --- a/gdb/Makefile.in
> > +++ b/gdb/Makefile.in
> > @@ -807,6 +807,7 @@ ALL_TARGET_OBS = \
> >   	ravenscar-thread.o \
> >   	riscv-fbsd-tdep.o \
> >   	riscv-linux-tdep.o \
> > +	riscv-none-tdep.o \
> >   	riscv-ravenscar-thread.o \
> >   	riscv-tdep.o \
> >   	rl78-tdep.o \
> > @@ -2269,6 +2270,7 @@ ALLDEPFILES = \
> >   	riscv-fbsd-tdep.c \
> >   	riscv-linux-nat.c \
> >   	riscv-linux-tdep.c \
> > +	riscv-none-tdep.c \
> >   	riscv-ravenscar-thread.c \
> >   	riscv-tdep.c \
> >   	rl78-tdep.c \
> > diff --git a/gdb/configure.tgt b/gdb/configure.tgt
> > index 6e039838748..ad88ddd9302 100644
> > --- a/gdb/configure.tgt
> > +++ b/gdb/configure.tgt
> > @@ -85,7 +85,7 @@ ia64*-*-*)
> >   	;;
> >   riscv*-*-*)
> > -	cpu_obs="riscv-tdep.o arch/riscv.o \
> > +	cpu_obs="riscv-tdep.o riscv-none-tdep.o arch/riscv.o \
> >   	         ravenscar-thread.o riscv-ravenscar-thread.o";;
> >   x86_64-*-*)
> > diff --git a/gdb/riscv-none-tdep.c b/gdb/riscv-none-tdep.c
> > new file mode 100644
> > index 00000000000..0a4215a60e9
> > --- /dev/null
> > +++ b/gdb/riscv-none-tdep.c
> > @@ -0,0 +1,345 @@
> > +/* Copyright (C) 2020 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/>.  */
> > +
> > +/* This file contain code that is specific for bare-metal RISC-V targets.  */
> > +
> > +#include "defs.h"
> > +#include "inferior.h"
> > +#include "gdbcore.h"
> > +#include "target.h"
> > +#include "arch-utils.h"
> > +#include "regcache.h"
> > +#include "osabi.h"
> > +#include "riscv-tdep.h"
> > +#include "elf-bfd.h"
> > +#include "regset.h"
> > +
> > +/* Function declarations.  */
> > +
> > +static void riscv_collect_regset_section_cb
> > +	(const char *sect_name, int supply_size, int collect_size,
> > +	 const struct regset *regset, const char *human_name, void *cb_data);
> > +
> > +/* Function Definitions.  */
> > +
> > +/* Called to figure out a target description for the corefile being read.
> > +   If we get here then the corefile didn't have a target description
> > +   embedded inside it, so we need to figure out a default description
> > +   based just on the properties of the corefile itself.  */
> > +
> > +static const struct target_desc *
> > +riscv_core_read_description (struct gdbarch *gdbarch,
> > +			     struct target_ops *target,
> > +			     bfd *abfd)
> > +{
> > +  error (_("unable to figure out target description for RISC-V core files"));
> > +  return nullptr;
> > +}
> > +
> > +/* Determine which signal stopped execution.  */
> > +
> > +static int
> > +find_signalled_thread (struct thread_info *info, void *data)
> > +{
> > +  if (info->suspend.stop_signal != GDB_SIGNAL_0
> > +      && info->ptid.pid () == inferior_ptid.pid ())
> > +    return 1;
> > +
> > +  return 0;
> > +}
> > +
> > +/* Structure for passing information from riscv_corefile_thread via an
> > +   iterator to riscv_collect_regset_section_cb. */
> > +
> > +struct riscv_collect_regset_section_cb_data
> > +{
> > +  riscv_collect_regset_section_cb_data
> > +	(struct gdbarch *gdbarch, const struct regcache *regcache,
> > +	 ptid_t ptid, bfd *obfd, enum gdb_signal stop_signal,
> > +	 gdb::unique_xmalloc_ptr<char> *note_data, int *note_size)
> > +	  : gdbarch (gdbarch), regcache (regcache), obfd (obfd),
> > +	    note_data (note_data), note_size (note_size),
> > +	    stop_signal (stop_signal)
> > +  {
> > +    /* The LWP is often not available for bare metal target, in which case
> > +       use the tid instead.  */
> > +    if (ptid.lwp_p ())
> > +      lwp = ptid.lwp ();
> > +    else
> > +      lwp = ptid.tid ();
> > +  }
> > +
> > +  struct gdbarch *gdbarch;
> > +  const struct regcache *regcache;
> > +  bfd *obfd;
> > +  gdb::unique_xmalloc_ptr<char> *note_data;
> > +  int *note_size;
> > +  long lwp;
> > +  enum gdb_signal stop_signal;
> > +  bool abort_iteration = false;
> > +};
> > +
> > +/* Records information about the single thread INFO into *NOTE_DATA, and
> > +   updates *NOTE_SIZE.  OBFD is the core file being generated.  GDBARCH is
> > +   the architecture the core file is being created for.  */
> > +
> > +static void
> > +riscv_corefile_thread (struct gdbarch *gdbarch, bfd *obfd,
> > +		       struct thread_info *info,
> > +		       gdb::unique_xmalloc_ptr<char> *note_data,
> > +		       int *note_size)
> > +{
> > +  struct regcache *regcache
> > +    = get_thread_arch_regcache (info->inf->process_target (),
> > +				info->ptid, gdbarch);
> > +
> > +  /* Ideally we should be able to read all of the registers known to this
> > +     target.  Unfortunately, sometimes targets advertise CSRs that can't be
> > +     read.  We don't want these registers to prevent a core file being
> > +     dumped, so we fetch the registers one by one here, and ignore any
> > +     errors.  This does mean that the register will show up as zero in the
> > +     core dump, which might be confusing, but probably better than being
> > +     unable to dump a core file.  */
> > +  for (int regnum = 0; regnum < gdbarch_num_regs (gdbarch); ++regnum)
> > +    {
> > +      try
> > +        {
> > +          target_fetch_registers (regcache, regnum);
> > +        }
> > +      catch (const gdb_exception_error &e)
> > +        { /* Ignore errors.  */ }
> > +    }
> > +
> > +  /* Call riscv_collect_regset_section_cb for each regset, passing in the
> > +     DATA object.  Appends the core file notes to *(DATA->NOTE_DATA) to
> > +     describe all the registers in this thread.  */
> > +  riscv_collect_regset_section_cb_data data (gdbarch, regcache, info->ptid,
> > +					     obfd, info->suspend.stop_signal,
> > +					     note_data, note_size);
> > +  gdbarch_iterate_over_regset_sections (gdbarch,
> > +					riscv_collect_regset_section_cb,
> > +					&data, regcache);
> > +}
> > +
> > +/* Build the note section for a corefile, and return it in a malloc
> > +   buffer.  Currently this just  dumps all available registers for each
> > +   thread.  */
> > +
> > +static gdb::unique_xmalloc_ptr<char>
> > +riscv_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size)
> > +{
> > +  if (!gdbarch_iterate_over_regset_sections_p (gdbarch))
> > +    return NULL;
> > +
> > +  /* Data structures into which we accumulate the core file notes.  */
> > +  gdb::unique_xmalloc_ptr<char> note_data;
> > +
> > +  /* Add note information about the executable and its arguments.  */
> > +  char fname[16] = {'\0'};
> > +  char psargs[80] = {'\0'};
> > +  if (get_exec_file (0))
> > +    {
> > +      strncpy (fname, lbasename (get_exec_file (0)), sizeof (fname));
> > +      fname[sizeof (fname) - 1] = 0;
> > +      strncpy (psargs, get_exec_file (0), sizeof (psargs));
> > +      psargs[sizeof (psargs) - 1] = 0;
> > +
> > +      const char *inf_args = get_inferior_args ();
> > +      if (inf_args != nullptr && *inf_args != '\0'
> > +	  && (strlen (inf_args)
> > +	      < ((int) sizeof (psargs) - (int) strlen (psargs))))
> > +	{
> > +	  strncat (psargs, " ",
> > +		   sizeof (psargs) - strlen (psargs));
> > +	  strncat (psargs, inf_args,
> > +		   sizeof (psargs) - strlen (psargs));
> > +	}
> > +
> > +      psargs[sizeof (psargs) - 1] = 0;
> > +    }
> > +  note_data.reset (elfcore_write_prpsinfo (obfd, note_data.release (),
> > +					   note_size, fname,
> > +					   psargs));
> > +
> > +  /* Update our understanding of the available threads.  */
> > +  try
> > +    {
> > +      update_thread_list ();
> > +    }
> > +  catch (const gdb_exception_error &e)
> > +    {
> > +      exception_print (gdb_stderr, e);
> > +    }
> > +
> > +  /* As we do in linux-tdep.c, prefer dumping the signalled thread first.
> > +     The "first thread" is what tools use to infer the signalled thread.
> > +     In case there's more than one signalled thread, prefer the current
> > +     thread, if it is signalled.  */
> > +  thread_info *signalled_thr, *curr_thr = inferior_thread ();
> > +  if (curr_thr->suspend.stop_signal != GDB_SIGNAL_0)
> > +    signalled_thr = curr_thr;
> > +  else
> > +    {
> > +      signalled_thr = iterate_over_threads (find_signalled_thread, nullptr);
> > +      if (signalled_thr == nullptr)
> > +	signalled_thr = curr_thr;
> > +    }
> > +
> > +  /* First add information about the signalled thread, then add information
> > +     about all the other threads, see above for the reasoning.  */
> > +  riscv_corefile_thread (gdbarch, obfd, signalled_thr, &note_data, note_size);
> > +  for (thread_info *thr : current_inferior ()->non_exited_threads ())
> > +    {
> > +      if (thr == signalled_thr)
> > +	continue;
> > +      riscv_corefile_thread (gdbarch, obfd, signalled_thr, &note_data,
> > +			     note_size);
> > +    }
> > +
> > +  return note_data;
> > +}
> > +
> > +/* Define the general register mapping.  This follows the same format as
> > +   the RISC-V linux corefile.  The linux kernel puts the PC at offset 0,
> > +   gdb puts it at offset 32.  Register x0 is always 0 and can be ignored.
> > +   Registers x1 to x31 are in the same place.  */
> > +
> > +static const struct regcache_map_entry riscv_gregmap[] =
> > +{
> > +  { 1,  RISCV_PC_REGNUM, 0 },
> > +  { 31, RISCV_RA_REGNUM, 0 }, /* x1 to x31 */
> > +  { 0 }
> > +};
> > +
> > +/* Define the FP register mapping.  This follows the same format as the
> > +   RISC-V linux corefile.  The kernel puts the 32 FP regs first, and then
> > +   FCSR.  */
> > +
> > +static const struct regcache_map_entry riscv_fregmap[] =
> > +{
> > +  { 32, RISCV_FIRST_FP_REGNUM, 0 },
> > +  { 1, RISCV_CSR_FCSR_REGNUM, 0 },
> > +  { 0 }
> > +};
> > +
> > +/* Define the general register regset.  */
> > +
> > +static const struct regset riscv_gregset =
> > +{
> > +  riscv_gregmap, riscv_supply_regset, regcache_collect_regset
> > +};
> > +
> > +/* Define the FP register regset.  */
> > +
> > +static const struct regset riscv_fregset =
> > +{
> > +  riscv_fregmap, riscv_supply_regset, regcache_collect_regset
> > +};
> > +
> > +/* Callback for iterate_over_regset_sections that records a single regset
> > +   in the corefile note section.  */
> > +
> > +static void
> > +riscv_collect_regset_section_cb (const char *sect_name, int supply_size,
> > +				 int collect_size, const struct regset *regset,
> > +				 const char *human_name, void *cb_data)
> > +{
> > +  riscv_collect_regset_section_cb_data *data
> > +    = (riscv_collect_regset_section_cb_data *) cb_data;
> > +
> > +  /* The only flag is REGSET_VARIABLE_SIZE, and we don't use that.  */
> > +  gdb_assert (regset->flags == 0);
> > +  gdb_assert (supply_size == collect_size);
> > +
> > +  if (data->abort_iteration)
> > +    return;
> > +
> > +  gdb_assert (regset != nullptr && regset->collect_regset);
> > +
> > +  /* This is intentionally zero-initialized by using std::vector, so that
> > +     any padding bytes in the core file will show a zero.  */
> > +  std::vector<gdb_byte> buf (collect_size);
> > +
> > +  regset->collect_regset (regset, data->regcache, -1, buf.data (),
> > +			  collect_size);
> > +
> > +  /* PRSTATUS still needs to be treated specially.  */
> > +  if (strcmp (sect_name, ".reg") == 0)
> > +    data->note_data->reset (elfcore_write_prstatus
> > +			    (data->obfd, data->note_data->release (),
> > +			     data->note_size, data->lwp,
> > +			     gdb_signal_to_host (data->stop_signal),
> > +			     buf.data ()));
> > +  else
> > +    data->note_data->reset (elfcore_write_register_note
> > +			    (data->obfd, data->note_data->release (),
> > +			     data->note_size,
> > +			     sect_name, buf.data (), collect_size));
> > +
> > +  if (data->note_data->get () == NULL)
> > +    data->abort_iteration = true;
> > +}
> > +
> > +/* Implement the "iterate_over_regset_sections" gdbarch method.  */
> > +
> > +static void
> > +riscv_iterate_over_regset_sections (struct gdbarch *gdbarch,
> > +				    iterate_over_regset_sections_cb *cb,
> > +				    void *cb_data,
> > +				    const struct regcache *regcache)
> > +{
> > +  /* Write out the GPRs.  */
> > +  int sz = 32 * riscv_isa_xlen (gdbarch);
> > +  cb (".reg", sz, sz, &riscv_gregset, NULL, cb_data);
> > +
> > +  /* Write out the FPRs, but only if present.  */
> > +  if (riscv_isa_flen (gdbarch) > 0)
> > +    {
> > +      sz = (32 * riscv_isa_flen (gdbarch)
> > +	    + register_size (gdbarch, RISCV_CSR_FCSR_REGNUM));
> > +      cb (".reg2", sz, sz, &riscv_fregset, NULL, cb_data);
> > +    }
> > +}
> > +
> > +/* Initialize RISC-V bare-metal ABI info.  */
> > +
> > +static void
> > +riscv_none_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
> > +{
> > +  /* Find or create a target description from a core file.  */
> > +  set_gdbarch_core_read_description (gdbarch, riscv_core_read_description);
> > +
> > +  /* How to create a core file for bare metal RISC-V.  */
> > +  set_gdbarch_make_corefile_notes (gdbarch, riscv_make_corefile_notes);
> > +
> > +  /* Iterate over registers for reading and writing bare metal RISC-V core
> > +     files.  */
> > +  set_gdbarch_iterate_over_regset_sections
> > +    (gdbarch, riscv_iterate_over_regset_sections);
> > +
> > +}
> > +
> > +/* Initialize RISC-V bare-metal target support.  */
> > +
> > +void _initialize_riscv_none_tdep ();
> > +void
> > +_initialize_riscv_none_tdep ()
> > +{
> > +  gdbarch_register_osabi (bfd_arch_riscv, 0, GDB_OSABI_NONE,
> > +			  riscv_none_init_abi);
> > +}
> > +
> > 

  reply	other threads:[~2020-12-07 15:17 UTC|newest]

Thread overview: 38+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-02 17:39 [PATCH 0/8] Bare-metal core dumps for RISC-V Andrew Burgess
2020-12-02 17:39 ` [PATCH 1/8] gdb/riscv: use a single regset supply function for riscv fbsd & linux Andrew Burgess
2021-01-18 14:15   ` Andrew Burgess
2020-12-02 17:39 ` [PATCH 2/8] bfd/binutils: support for gdb target descriptions in the core file Andrew Burgess
2020-12-02 18:21   ` Luis Machado via Gdb-patches
2020-12-02 22:58     ` Jim Wilson
2020-12-03 12:16       ` Luis Machado via Gdb-patches
     [not found]         ` <20201214115512.GI2945@embecosm.com>
2021-01-11 10:19           ` Andrew Burgess
2021-01-11 13:03             ` Luis Machado via Gdb-patches
2020-12-07 12:48     ` Andrew Burgess
2020-12-02 17:39 ` [PATCH 3/8] gdb: write target description into " Andrew Burgess
2020-12-03 20:36   ` Tom Tromey
2020-12-07 14:38     ` Andrew Burgess
2020-12-02 17:39 ` [PATCH 4/8] bfd/riscv: prepare to handle bare metal core dump creation Andrew Burgess
2020-12-02 23:24   ` Jim Wilson
2020-12-07 14:39     ` Andrew Burgess
2020-12-02 17:39 ` [PATCH 5/8] gdb/riscv: introduce bare metal core dump support Andrew Burgess
2020-12-02 18:12   ` Luis Machado via Gdb-patches
2020-12-07 15:17     ` Andrew Burgess [this message]
2020-12-07 15:58       ` Luis Machado via Gdb-patches
2020-12-07 16:58         ` Andrew Burgess
2020-12-07 17:24           ` Luis Machado via Gdb-patches
2020-12-07 18:11             ` Andrew Burgess
2020-12-07 19:00               ` Luis Machado via Gdb-patches
2020-12-07 19:23                 ` Andrew Burgess
2020-12-07 19:39                   ` Luis Machado via Gdb-patches
2020-12-07 19:51                     ` Paul Mathieu via Gdb-patches
2020-12-13 10:13                       ` Fredrik Hederstierna via Gdb-patches
2020-12-02 17:39 ` [PATCH 6/8] bfd/binutils: add support for RISC-V CSRs in core files Andrew Burgess
2020-12-02 23:50   ` Jim Wilson
2020-12-07 15:19     ` Andrew Burgess
2020-12-14 13:37     ` Andrew Burgess
2020-12-02 17:39 ` [PATCH 7/8] gdb/riscv: make riscv target description names global Andrew Burgess
2020-12-02 17:39 ` [PATCH 8/8] gdb/riscv: write CSRs into baremetal core dumps Andrew Burgess
2020-12-02 23:59 ` [PATCH 0/8] Bare-metal core dumps for RISC-V Jim Wilson
2020-12-07 12:10   ` Andrew Burgess
2020-12-07 19:57     ` Jim Wilson
2020-12-03 20:40 ` Tom Tromey

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201207151702.GK2729@embecosm.com \
    --to=andrew.burgess@embecosm.com \
    --cc=binutils@sourceware.org \
    --cc=fredrik.hederstierna@verisure.com \
    --cc=gdb-patches@sourceware.org \
    --cc=luis.machado@linaro.org \
    --cc=paulmathieu@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox