From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm1-x341.google.com (mail-wm1-x341.google.com [IPv6:2a00:1450:4864:20::341]) by sourceware.org (Postfix) with ESMTPS id 4DC4A396D806 for ; Wed, 2 Dec 2020 17:39:43 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 4DC4A396D806 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=embecosm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=andrew.burgess@embecosm.com Received: by mail-wm1-x341.google.com with SMTP id h21so10578975wmb.2 for ; Wed, 02 Dec 2020 09:39:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=embecosm.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=AG6DAK/zgL7kMH3KynCeKtfj0yx1WwIUmb2DBtqVQbo=; b=Qiy4Oxb3+i9IyH+NZT5Zf/HK9L87wlt7ZoACrlTZA7aD89ZlETkkO+/AjZ3IUlqn1h VjGfdIJ2VRsxMSz/WjSUUF1aNAXq/Pm5xtA7uPzPOS5WI6E9VqCIXBvO1PpKCSL4RJ6z e7iPoOYIpXa8W51/+nJeVUDsHL9Q1yP+96b31eF1vAI92C8Co1aPZNjsGfQqwMKJjWBT M8Q+i+XF/gIAmmbqzT4wWa4LoTSb7rgNc8Oizk4w7FoQ29y2XJA0VWpf4S7gqC0T0/1C jtkxn6PZNpJj0z5ZXH7SaKCu7/CskPFfeaTFa26uUk/8aY1wz9DNTUuyTSQeC7BaEg+U 0veA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=AG6DAK/zgL7kMH3KynCeKtfj0yx1WwIUmb2DBtqVQbo=; b=e14gWv5YS0+gzsP+t0aOEhGWJEA7+IW+vQnStqzJrGoks82TI9jkDOiXXPY0jnvjjY mkSm7TxyVZQatpoSCXHvJK6bcO/RTBRkKeyBn6FvrWIpFhypn2aArG9mhyz62fqATeHI edzoHH8xqE2RE6tBY1LG4iXNOboWjbmhsAJCZCBTLIrkbYYnFc7KvgkRFPeX7iunasEB muPhJjbJbvuNqY9k29JUKQo3gUKx/Ansundhfw7aQ/75scE4IgyzvFPDlezwXiuz3A3h WqyKohfBKNdm+ehtQ8hcJyZvDqWumgr273XyFmMI8t+tU+FfqDXSAbRrWNiQiTRhjlfe 78Bg== X-Gm-Message-State: AOAM5309jgFkmKPabHQ1P8jBeqHwkBE14Ncv9+wJKfBk0sTrAoDd1CFK EJ5eRKgt3S5aD3ynJxdlPWXYTM44vgkEgg== X-Google-Smtp-Source: ABdhPJy7SS5gM1kwxPHzTdz05vspAWm3tRei4/faPrzPqQ8N3VJPHebhDAWWh0IAyVYtkK786lLHCg== X-Received: by 2002:a1c:4156:: with SMTP id o83mr255569wma.178.1606930782315; Wed, 02 Dec 2020 09:39:42 -0800 (PST) Received: from localhost (host109-154-20-215.range109-154.btcentralplus.com. [109.154.20.215]) by smtp.gmail.com with ESMTPSA id z189sm2782564wme.23.2020.12.02.09.39.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 02 Dec 2020 09:39:41 -0800 (PST) From: Andrew Burgess To: binutils@sourceware.org, gdb-patches@sourceware.org Subject: [PATCH 5/8] gdb/riscv: introduce bare metal core dump support Date: Wed, 2 Dec 2020 17:39:29 +0000 Message-Id: <5ba628c1042f3000947a1f2a6f9e24cf46573fa3.1606930261.git.andrew.burgess@embecosm.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-8.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPAM_BODY, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 02 Dec 2020 17:39:45 -0000 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 . */ + +/* 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 *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 *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 *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 +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 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, ¬e_data, note_size); + for (thread_info *thr : current_inferior ()->non_exited_threads ()) + { + if (thr == signalled_thr) + continue; + riscv_corefile_thread (gdbarch, obfd, signalled_thr, ¬e_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 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); +} + -- 2.25.4