/* Common code for targets with the none ABI (bare-metal), but where the
   BFD library is build with ELF support.

   Copyright (C) 2020-2024 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 "elf-none-tdep.h"
#include "exceptions.h"
#include "regset.h"
#include "elf-bfd.h"
#include "inferior.h"
#include "regcache.h"
#include "gdbarch.h"
#include "gcore.h"
#include "gcore-elf.h"

/* 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>
elf_none_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd,
			      int *note_size)
{
  gdb::unique_xmalloc_ptr<char> note_data;

  /* Add note information about the executable and its arguments.  */
  std::string fname;
  std::string psargs;
  static const size_t fname_len = 16;
  static const size_t psargs_len = 80;
  if (current_program_space->exec_filename () != nullptr)
    {
      const char *exe = current_program_space->exec_filename ();
      fname = lbasename (exe);
      psargs = std::string (exe);

      const std::string &infargs = current_inferior ()->args ();
      if (!infargs.empty ())
	psargs += ' ' + infargs;

      /* All existing targets that handle writing out prpsinfo expect the
	 fname and psargs strings to be at least 16 and 80 characters long
	 respectively, including a null terminator at the end.  Resize to
	 the expected length minus one to ensure there is a null within the
	 required length.  */
      fname.resize (fname_len - 1);
      psargs.resize (psargs_len - 1);
    }

  /* Resize the buffers up to their required lengths.  This will fill any
     remaining space with the null character.  */
  fname.resize (fname_len);
  psargs.resize (psargs_len);

  /* Now write out the prpsinfo structure.  */
  note_data.reset (elfcore_write_prpsinfo (obfd, note_data.release (),
					   note_size, fname.c_str (),
					   psargs.c_str ()));
  if (note_data == nullptr)
    return nullptr;

  /* Thread register information.  */
  try
    {
      update_thread_list ();
    }
  catch (const gdb_exception_error &e)
    {
      exception_print (gdb_stderr, e);
    }

  /* Like the Linux kernel, prefer dumping the signalled thread first.
     "First thread" is what tools use to infer the signalled thread.  */
  thread_info *signalled_thr = gcore_find_signalled_thread ();

  /* All threads are reported as having been stopped by the same signal
     that stopped SIGNALLED_THR.  */
  gdb_signal stop_signal;
  if (signalled_thr != nullptr)
    stop_signal = signalled_thr->stop_signal ();
  else
    stop_signal = GDB_SIGNAL_0;

  if (signalled_thr != nullptr)
    gcore_elf_build_thread_register_notes (gdbarch, signalled_thr,
					   stop_signal, obfd, &note_data,
					   note_size);
  for (thread_info *thr : current_inferior ()->non_exited_threads ())
    {
      if (thr == signalled_thr)
	continue;

      gcore_elf_build_thread_register_notes (gdbarch, thr, stop_signal, obfd,
					     &note_data, note_size);
    }


  /* Include the target description when possible.  Some architectures
     allow for per-thread gdbarch so we should really be emitting a tdesc
     per-thread, however, we don't currently support reading in a
     per-thread tdesc, so just emit the tdesc for the signalled thread.  */
  gdbarch = target_thread_architecture (signalled_thr->ptid);
  gcore_elf_make_tdesc_note (gdbarch, obfd, &note_data, note_size);

  return note_data;
}

/* See none-tdep.h.  */

void
elf_none_init_abi (struct gdbarch *gdbarch)
{
  /* Default core file support.  */
  set_gdbarch_make_corefile_notes (gdbarch, elf_none_make_corefile_notes);
}