From patchwork Sat Nov 2 02:56:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thiago Jung Bauermann X-Patchwork-Id: 840278 Delivered-To: patch@linaro.org Received: by 2002:adf:a38c:0:b0:37d:45d0:187 with SMTP id l12csp1158513wrb; Fri, 1 Nov 2024 19:59:04 -0700 (PDT) X-Forwarded-Encrypted: i=3; AJvYcCW+VLQGfhJlPGqbLbp3GlnQ4Q7PywearBnEE5rJHJeTFKu5QXnnDw4mhe/kJBAvMtPa2J9AvQ==@linaro.org X-Google-Smtp-Source: AGHT+IE4gu+ngl6v65NcQtDU87Mrk/DCcr4r48GZy0BZXD7T8MDUTBOc0GpdrFdV57EpIhlxqVcE X-Received: by 2002:a05:620a:4554:b0:7a1:c40d:7573 with SMTP id af79cd13be357-7b193f4d92bmr3566560985a.49.1730516344270; Fri, 01 Nov 2024 19:59:04 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1730516344; cv=pass; d=google.com; s=arc-20240605; b=AASnmO6+2HG1VGNz2r4dTL00zmu1evCuZi2NDKt/sfUdsK8AzTayOqWa4puag+c1tZ nFpsGh2ipOQ0m+o2dY0ufNDjD306Y78A2F9hAUfCXMo9flALTmf3KDK1iZCg2JBLFOhb hI0ML52cXv/aSKEYPfRn3nVByjU8xXgNhWsOrkEVd0042ArjfBCw1TY8DcdsVy9DqpV/ BqDJ0QelpwL1seRc4Vxx/ZRwJ/EkjLZsQ2MlWx9BxuHnIxbjjNfGyPgxGXuJvwQzJp2t P4aVAfS+t84048WXEs1wO00UUo8uXQ2Yhil+jnVrNACZ7qUp9Szhw9XEDYt78LqyxUrV Oe4Q== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:to:from :dkim-signature:arc-filter:dmarc-filter:delivered-to; bh=mjiW781HPoF9ScVavlKTVhjEajTu5UIvyl8t76EYeLY=; fh=72kqq0iQhigvR9Vv/oqX5ebs3Yyyw7XhzWxOOEPdupI=; b=a2y2McR2j+TFX9Cd3hM3PFfXPJLYeCRV45silh+ICXDFHOuEmiXLkfe3DDv026+P/n PQDDOv1w+DvGpiSZLVs54Y05rK+0kf+WM3+abG2yvZ2lAdHl8ZZSWJTvo+XFGq/6IuQQ Klgc5IXgQkh/Uzezhcbyhe1cZKSHCuGQXuo/PGw3Pqy2Zw0B6AI/XqZbygRzZo9bitrV dKPGQTALbN9XPHE1vVMtGe8hFGMKintGaIe4bqxQmxfv0B8YZHKVoa3PqMebolD/9H98 eOPPp4ybsTIjZiMPAKhgA3Y5eNBVqJ8/T9Cs6925HMsgkYqBiodT40xT7dkracnhMsvZ tOFQ==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=JC46uhGc; arc=pass (i=1); spf=pass (google.com: domain of gdb-patches-bounces~patch=linaro.org@sourceware.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gdb-patches-bounces~patch=linaro.org@sourceware.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from server2.sourceware.org (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id af79cd13be357-7b2f39f3ce1si570050485a.147.2024.11.01.19.59.04 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 01 Nov 2024 19:59:04 -0700 (PDT) Received-SPF: pass (google.com: domain of gdb-patches-bounces~patch=linaro.org@sourceware.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=JC46uhGc; arc=pass (i=1); spf=pass (google.com: domain of gdb-patches-bounces~patch=linaro.org@sourceware.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gdb-patches-bounces~patch=linaro.org@sourceware.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id D603C3857BA0 for ; Sat, 2 Nov 2024 02:59:03 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from mail-pl1-x633.google.com (mail-pl1-x633.google.com [IPv6:2607:f8b0:4864:20::633]) by sourceware.org (Postfix) with ESMTPS id 6FE303858415 for ; Sat, 2 Nov 2024 02:57:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 6FE303858415 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=linaro.org ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 6FE303858415 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::633 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1730516227; cv=none; b=wvGhEEPZY53vb73nPFds0JoW3HvE1W0VUFqtcKNI0tLMWfjRj80C/7rzR3mDoVvXmH6AyLZr3yzmhbRo1D+KzMYWvI0aAips91UW13626adrgFaEsvJbC8PV/h9yNe/F+duj7IqTI1QiP48nHFDfbrxtzbhtwmIwh8AN3GUtV98= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1730516227; c=relaxed/simple; bh=MnltwfHuLvb9OioBA+Cmy4wdevNgTOvu5yn9TM046iQ=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=LBZ2ZBwD6N4QQHgdB/6vL0wXU+mDg9dte0vwYdwe66cilPDSnehL1zr5eBha6ii3fyH2apmucx6pLosP187zU9V52Dwyo0lfqpCZcLqRe14Np96K3p87VHtaqs/tGNvd05l70ofdEv3W82cM6YH5OTeeWoGK4O2wiaRrM5ikk/w= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pl1-x633.google.com with SMTP id d9443c01a7336-20c7ee8fe6bso24676485ad.2 for ; Fri, 01 Nov 2024 19:57:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1730516221; x=1731121021; darn=sourceware.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=mjiW781HPoF9ScVavlKTVhjEajTu5UIvyl8t76EYeLY=; b=JC46uhGc9fny4q5vSq+e4VPASrypMLkwrc82FJmm0UAz/No0usT/7ELfrVO3AI8JHa v+I9VL6z7SccdlTV6KOxSABTTTEKev/43YMThTWfWtZMLXuGWN7CQwf7l5Lr3nUN4hEL zfyBeDD+ghmUxKjhZJ+9XXdMrJBIYuadNd0nAex17hD+xgoRq8tUGVbj9E5iRiSj7s0q sVeHQB7LTMN98oSc49ziWl544OPTbu+FJdQ1xO9G3c9DOzc55Yj6Ur7w8oTvA6J2TxIV HhWeBeBrWqwKiscQZT2uXl6UbAUmDT0JXkTcPHVtlDcg7QZOgCSaUJUEMFGPreSf+VCb rEFg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1730516221; x=1731121021; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=mjiW781HPoF9ScVavlKTVhjEajTu5UIvyl8t76EYeLY=; b=kt9slJRghsi6NY2eZdyzXETgp/BWDElm/IfpWEh6HO5UV0F0HJSkoxyDi5b2EFUOV3 E5R8ZdrAxT8nmi3ZpaWCB63TzdWsKLCK539DDIxG47lRX87T5tJ2zUqC34bzGvaFmWDK izG6ySg3rh5pBZvrJ0ljo68XDE3l3nUj2GRDVCneMMGNrjPRSjyTYsmvMtU/blEJSysC Bys8IpjqHbIPr2OZ8V0/85eAyqHpnmEAzjcTyaH4sizRGiaNdXAH96qauV60BRwzWbp/ tF4LcNkfquigfk/LexO3RI+LW2bKl4wZqivSGRO8+niRQO4wLyTAQbGDpKsGlePHAEuj M3Dg== X-Gm-Message-State: AOJu0Yw5iRqLtA1BxSXk+cEvmNWnRY0h2Gpwq7qeFSbT/dx9QmnUeh+O BPvxa4AxGwQa6klYhWY3nc9rxdQozb/iL+0W8bWA9nIA1BOMW11XcHNYdzk61oYptuwk2m9p4/Q x X-Received: by 2002:a17:902:e851:b0:20c:94f6:3e03 with SMTP id d9443c01a7336-210c6c6a272mr328375245ad.47.1730516221053; Fri, 01 Nov 2024 19:57:01 -0700 (PDT) Received: from localhost ([2804:14d:7e39:8470:f214:b4dc:314a:c1ee]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-211057d3bb8sm27610635ad.246.2024.11.01.19.57.00 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 01 Nov 2024 19:57:00 -0700 (PDT) From: Thiago Jung Bauermann To: gdb-patches@sourceware.org Subject: [RFC PATCH v4 07/15] GDB, gdbserver: Create concept of load-early registers Date: Fri, 1 Nov 2024 23:56:27 -0300 Message-ID: <20241102025635.586759-8-thiago.bauermann@linaro.org> X-Mailer: git-send-email 2.46.0 In-Reply-To: <20241102025635.586759-1-thiago.bauermann@linaro.org> References: <20241102025635.586759-1-thiago.bauermann@linaro.org> MIME-Version: 1.0 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~patch=linaro.org@sourceware.org These are registers that need to be saved first into the regcache. Consequently, also gdbserver needs to send these registers as expedited registers so that GDB can use them as early as possible. When debugging a remote target, load early registers using expedited registers and the p packet. For now the load-early registers concept is unused, as nothing in this patch adds registers to the load-early set. It is being sent separately to facilitate review. In a subsequent patch, registers which are used in location expressions that determine the size of other registers will need to be loaded early into the regcache so that they can be used to resolve the dynamic types of the variable-sized registers. This will be done by calling tdesc_create_reg () with the new load_early argument set to true. If I can move the VG register before the Z registers as Pedro suggested, this patch may be unnecessary. --- gdb/process-stratum-target.h | 3 ++ gdb/record-full.c | 18 +++++-- gdb/regcache.c | 99 +++++++++++++++++++++++++++++++++++- gdb/regcache.h | 17 +++++++ gdb/regformats/regdef.h | 16 ++++-- gdb/remote.c | 88 ++++++++++++++++++++++++++++---- gdb/target-descriptions.c | 24 ++++++++- gdb/target-descriptions.h | 5 ++ gdbserver/regcache.cc | 8 +++ gdbserver/regcache.h | 2 + gdbserver/tdesc.cc | 12 ++++- gdbsupport/common-regcache.h | 3 ++ gdbsupport/tdesc.cc | 9 ++-- gdbsupport/tdesc.h | 11 +++- 14 files changed, 285 insertions(+), 30 deletions(-) diff --git a/gdb/process-stratum-target.h b/gdb/process-stratum-target.h index 9aa9d874ecbb..4d4ad3431d16 100644 --- a/gdb/process-stratum-target.h +++ b/gdb/process-stratum-target.h @@ -55,6 +55,9 @@ class process_stratum_target : public target_ops gdbarch. */ struct gdbarch *thread_architecture (ptid_t ptid) override; + /* Supply the load-early registers to REGCACHE when it's first created. */ + virtual void supply_initial_registers (regcache *regcache) {}; + /* Default implementations for process_stratum targets. Return true if there's a selected inferior, false otherwise. */ bool has_all_memory () override; diff --git a/gdb/record-full.c b/gdb/record-full.c index 22a513ac79ab..8e4915abd083 100644 --- a/gdb/record-full.c +++ b/gdb/record-full.c @@ -926,15 +926,20 @@ record_full_core_open_1 () { regcache *regcache = get_thread_regcache (inferior_thread ()); int regnum = gdbarch_num_regs (regcache->arch ()); - int i; /* Get record_full_core_regbuf. */ target_fetch_registers (regcache, -1); record_full_core_regbuf = new detached_regcache (regcache->arch (), false); - for (i = 0; i < regnum; i ++) + /* Supply load-early registers first. */ + for (int i : regcache->load_early_registers ()) record_full_core_regbuf->raw_supply (i, *regcache); + for (int i = 0; i < regnum; i ++) + /* Load-early registers were already supplied. */ + if (!regcache->is_load_early_register (i)) + record_full_core_regbuf->raw_supply (i, *regcache); + record_full_core_sections = build_section_table (current_program_space->core_bfd ()); @@ -2107,10 +2112,15 @@ record_full_core_target::fetch_registers (struct regcache *regcache, if (regno < 0) { int num = gdbarch_num_regs (regcache->arch ()); - int i; - for (i = 0; i < num; i ++) + /* Supply load-early registers first. */ + for (int i : regcache->load_early_registers ()) regcache->raw_supply (i, *record_full_core_regbuf); + + for (int i = 0; i < num; i ++) + /* Load-early registers were already supplied. */ + if (!regcache->is_load_early_register (i)) + regcache->raw_supply (i, *record_full_core_regbuf); } else regcache->raw_supply (regno, *record_full_core_regbuf); diff --git a/gdb/regcache.c b/gdb/regcache.c index 6e0c730d0d59..61289d46f4b1 100644 --- a/gdb/regcache.c +++ b/gdb/regcache.c @@ -30,6 +30,7 @@ #include "regset.h" #include #include "cli/cli-cmds.h" +#include "target-descriptions.h" /* * DATA STRUCTURE @@ -68,6 +69,9 @@ struct regcache_descr long *register_offset = nullptr; long *sizeof_register = nullptr; + /* Registers that need to be loaded early in the register cache. */ + std::set load_early_regs; + /* Cached table containing the type of each register. */ struct type **register_type = nullptr; }; @@ -120,6 +124,9 @@ init_regcache_descr (struct gdbarch *gdbarch) descr->sizeof_register[i] = descr->register_type[i]->length (); descr->register_offset[i] = offset; offset += descr->sizeof_register[i]; + + if (tdesc_register_is_early_load (gdbarch, i)) + descr->load_early_regs.insert (i); } /* Set the real size of the raw register cache buffer. */ descr->sizeof_raw_registers = offset; @@ -230,6 +237,50 @@ reg_buffer::arch () const return m_descr->gdbarch; } +/* Utility functions returning useful register attributes stored in + the regcache descr. */ + +/* See regcache.h. */ + +bool +reg_buffer::fetch_load_early_registers () +{ + for (int regnum : this->load_early_registers ()) + if (this->get_register_status (regnum) != REG_VALID) + /* A reg_buffer can't fetch registers, so we can only report failure. */ + return false; + + return true; +} + +/* See regcache.h. */ + +bool +regcache::fetch_load_early_registers () +{ + for (int regnum : this->load_early_registers ()) + if (this->get_register_status (regnum) != REG_VALID) + target_fetch_registers (this, regnum); + + return true; +} + +/* See regcache.h. */ + +bool +reg_buffer::has_load_early_registers () +{ + return !m_descr->load_early_regs.empty (); +} + +/* See regcache.h. */ + +const std::set & +reg_buffer::load_early_registers () +{ + return m_descr->load_early_regs; +} + /* Helper for reg_buffer::register_buffer. */ template @@ -268,12 +319,31 @@ reg_buffer::save (register_read_ftype cooked_read) /* Clear the dest. */ memset (m_registers.get (), 0, m_descr->sizeof_cooked_registers); memset (m_register_status.get (), REG_UNKNOWN, m_descr->nr_cooked_registers); + + /* Save load early registers first. */ + for (int regnum : m_descr->load_early_regs) + { + gdb::array_view dst_buf = register_buffer (regnum); + register_status status = cooked_read (regnum, dst_buf); + + gdb_assert (status != REG_UNKNOWN); + + if (status != REG_VALID) + memset (dst_buf.data (), 0, dst_buf.size ()); + + m_register_status[regnum] = status; + } + /* Copy over any registers (identified by their membership in the save_reggroup) and mark them as valid. The full [0 .. gdbarch_num_regs + gdbarch_num_pseudo_regs) range is checked since some architectures need to save/restore `cooked' registers that live in memory. */ for (int regnum = 0; regnum < m_descr->nr_cooked_registers; regnum++) { + /* Load-early registers were already saved. */ + if (this->is_load_early_register (regnum)) + continue; + if (gdbarch_register_reggroup_p (gdbarch, regnum, save_reggroup)) { gdb::array_view dst_buf = register_buffer (regnum); @@ -293,19 +363,34 @@ void regcache::restore (readonly_detached_regcache *src) { struct gdbarch *gdbarch = m_descr->gdbarch; - int regnum; gdb_assert (src != NULL); gdb_assert (src->m_has_pseudo); gdb_assert (gdbarch == src->arch ()); + /* Restore load early registers first. */ + for (int regnum : m_descr->load_early_regs) + { + if (!gdbarch_register_reggroup_p (gdbarch, regnum, restore_reggroup)) + continue; + + if (src->m_register_status[regnum] != REG_VALID) + continue; + + cooked_write (regnum, src->register_buffer (regnum)); + } + /* Copy over any registers, being careful to only restore those that were both saved and need to be restored. The full [0 .. gdbarch_num_regs + gdbarch_num_pseudo_regs) range is checked since some architectures need to save/restore `cooked' registers that live in memory. */ - for (regnum = 0; regnum < m_descr->nr_cooked_registers; regnum++) + for (int regnum = 0; regnum < m_descr->nr_cooked_registers; regnum++) { + /* Load-early registers were already restored. */ + if (this->is_load_early_register (regnum)) + continue; + if (gdbarch_register_reggroup_p (gdbarch, regnum, restore_reggroup)) { if (src->m_register_status[regnum] == REG_VALID) @@ -324,6 +409,14 @@ reg_buffer::get_register_status (int regnum) const return m_register_status[regnum]; } +/* See gdbsupport/common-regcache.h. */ + +bool +reg_buffer::is_load_early_register (int regnum) const +{ + return m_descr->load_early_regs.count (regnum) > 0; +} + void reg_buffer::invalidate (int regnum) { @@ -394,6 +487,8 @@ get_thread_arch_regcache (inferior *inf_for_target_calls, ptid_t ptid, constructor explicitly instead of implicitly. */ ptid_regc_map.insert (std::make_pair (ptid, regcache_up (new_regcache))); + proc_target->supply_initial_registers (new_regcache); + return new_regcache; } diff --git a/gdb/regcache.h b/gdb/regcache.h index 739172a249b8..5c28fec4af11 100644 --- a/gdb/regcache.h +++ b/gdb/regcache.h @@ -20,6 +20,7 @@ #ifndef REGCACHE_H #define REGCACHE_H +#include #include "gdbsupport/array-view.h" #include "gdbsupport/common-regcache.h" #include "gdbsupport/function-view.h" @@ -198,6 +199,9 @@ class reg_buffer : public reg_buffer_common /* See gdbsupport/common-regcache.h. */ enum register_status get_register_status (int regnum) const override; +/* See gdbsupport/common-regcache.h. */ + bool is_load_early_register (int regnum) const override; + /* See gdbsupport/common-regcache.h. */ void raw_collect (int regnum, gdb::array_view dst) const override; @@ -256,6 +260,12 @@ class reg_buffer : public reg_buffer_common /* See gdbsupport/common-regcache.h. */ bool raw_compare (int regnum, const void *buf, int offset) const override; + /* Whether any register needs to be loaded before other registers. */ + bool has_load_early_registers (); + + /* Return set of regnums which need to be loaded before other registers. */ + const std::set &load_early_registers (); + /* See gdbsupport/common-regcache.h. */ int register_size (int regnum) const override; @@ -265,6 +275,10 @@ class reg_buffer : public reg_buffer_common int num_raw_registers () const; + /* Ensure all load early registers are fetched in this cache. + Return false if they aren't. */ + virtual bool fetch_load_early_registers (); + /* Return a view on register REGNUM's buffer cache. */ template gdb::array_view register_buffer (int regnum) const; @@ -485,6 +499,9 @@ class regcache : public detached_regcache gdb::array_view src, bool is_raw); + /* See class reg_buffer. */ + bool fetch_load_early_registers () override; + /* The inferior to switch to, to make target calls. This may not be the inferior of thread M_PTID. For instance, this diff --git a/gdb/regformats/regdef.h b/gdb/regformats/regdef.h index 0ba7a08bb0c5..09339d495209 100644 --- a/gdb/regformats/regdef.h +++ b/gdb/regformats/regdef.h @@ -26,13 +26,15 @@ struct reg reg (int _offset) : name (""), offset (_offset), - size (0) + size (0), + load_early (false) {} - reg (const char *_name, int _offset, int _size) + reg (const char *_name, int _offset, int _size, bool _load_early) : name (_name), offset (_offset), - size (_size) + size (_size), + load_early (_load_early) {} /* The name of this register - NULL for pad entries. */ @@ -49,11 +51,17 @@ struct reg /* The size (in bits) of the value of this register, as transmitted. */ int size; + /* Whether this register needs to be loaded early in the register cache, + because variable-size registers depend on it to calculate their + size. */ + bool load_early; + bool operator== (const reg &other) const { return (strcmp (name, other.name) == 0 && offset == other.offset - && size == other.size); + && size == other.size + && load_early == other.load_early); } bool operator!= (const reg &other) const diff --git a/gdb/remote.c b/gdb/remote.c index 2da2c5a4789a..0217c05bce52 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -683,6 +683,9 @@ class remote_state immediately, so queue is not needed for them. */ std::vector stop_reply_queue; + /* Contains the stop reply packet when first starting the inferior. */ + gdb::char_vector first_stop_reply; + /* FIXME: cagney/1999-09-23: Even though getpkt was called with ``forever'' still use the normal timeout mechanism. This is currently used by the ASYNC code to guarantee that target reads @@ -1218,6 +1221,8 @@ class remote_target : public process_stratum_target ptid_t select_thread_for_ambiguous_stop_reply (const struct target_waitstatus &status); + void supply_initial_registers (regcache *regcache) override; + void remote_notice_new_inferior (ptid_t currthread, bool executing); void print_one_stopped_thread (thread_info *thread); @@ -1317,6 +1322,8 @@ class remote_target : public process_stratum_target int fetch_register_using_p (struct regcache *regcache, packet_reg *reg); + + void fetch_early_registers (struct regcache *regcache); int send_g_packet (); void process_g_packet (struct regcache *regcache); void fetch_registers_using_g (struct regcache *regcache); @@ -1417,6 +1424,9 @@ class remote_target : public process_stratum_target bool start_remote_1 (int from_tty, int extended_p); + void supply_expedited_regs (struct regcache *regcache, + std::vector &expedited_regs); + /* The remote state. Don't reference this directly. Use the get_remote_state method instead. */ remote_state m_remote_state; @@ -8530,6 +8540,21 @@ remote_target::select_thread_for_ambiguous_stop_reply return first_resumed_thread->ptid; } +/* Supply the contents of EXPEDITED_REGS to REGCACHE. */ + +void +remote_target::supply_expedited_regs (struct regcache *regcache, + std::vector &expedited_regs) +{ + remote_state *rs = get_remote_state (); + + for (cached_reg_t ® : expedited_regs) + { + regcache->raw_supply (reg.num, reg.data); + rs->last_seen_expedited_registers.insert (reg.num); + } +} + /* Called when it is decided that STOP_REPLY holds the info of the event that is to be returned to the core. This function always destroys STOP_REPLY. */ @@ -8568,12 +8593,7 @@ remote_target::process_stop_reply (stop_reply_up stop_reply, regcache *regcache = get_thread_arch_regcache (find_inferior_ptid (this, ptid), ptid, stop_reply->arch); - - for (cached_reg_t ® : stop_reply->regcache) - { - regcache->raw_supply (reg.num, reg.data); - rs->last_seen_expedited_registers.insert (reg.num); - } + supply_expedited_regs (regcache, stop_reply->regcache); } remote_thread_info *remote_thr = get_remote_thread_info (this, ptid); @@ -8599,6 +8619,28 @@ remote_target::process_stop_reply (stop_reply_up stop_reply, return ptid; } +/* See gdb/process-stratum-target.h. */ + +void +remote_target::supply_initial_registers (regcache *regcache) +{ + remote_state *rs = get_remote_state (); + + if (rs->first_stop_reply.empty ()) + return; + + notif_event_up reply + = remote_notif_parse (this, ¬if_client_stop, + rs->first_stop_reply.data ()); + std::vector &expedited_regs + = ((struct stop_reply *) reply.get ())->regcache; + + if (!expedited_regs.empty ()) + supply_expedited_regs (regcache, expedited_regs); + + rs->first_stop_reply.clear (); +} + /* The non-stop mode version of target_wait. */ ptid_t @@ -8918,6 +8960,27 @@ remote_target::fetch_register_using_p (struct regcache *regcache, return 1; } +/* Fetch load-early registers individually with the 'p' packet. */ + +void +remote_target::fetch_early_registers (struct regcache *regcache) +{ + struct remote_state *rs = get_remote_state (); + remote_arch_state *rsa = rs->get_remote_arch_state (regcache->arch ()); + + for (int regnum : regcache->load_early_registers ()) + { + /* We may already have it from a stop reply packet. */ + if (regcache->get_register_status (regnum) == REG_VALID) + continue; + + packet_reg *reg = packet_reg_from_regnum (regcache->arch (), rsa, regnum); + int res = fetch_register_using_p (regcache, reg); + if (res == 0) + error (_("Could not load early register %d using p packet."), regnum); + } +} + /* Fetch the registers included in the target's 'g' packet. */ int @@ -9062,6 +9125,9 @@ remote_target::process_g_packet (struct regcache *regcache) void remote_target::fetch_registers_using_g (struct regcache *regcache) { + if (regcache->has_load_early_registers ()) + fetch_early_registers (regcache); + send_g_packet (); process_g_packet (regcache); } @@ -10920,7 +10986,6 @@ extended_remote_target::create_inferior (const char *exec_file, char **env, int from_tty) { int run_worked; - char *stop_reply; struct remote_state *rs = get_remote_state (); const char *remote_exec_file = get_remote_exec_file (); @@ -10953,7 +11018,10 @@ Remote replied unexpectedly while setting startup-with-shell: %s"), /* Now restart the remote server. */ run_worked = extended_remote_run (args) != -1; - if (!run_worked) + if (run_worked) + /* vRun's success return is a stop reply. */ + rs->first_stop_reply = rs->buf; + else { /* vRun was not supported. Fail if we need it to do what the user requested. */ @@ -10966,9 +11034,7 @@ Remote replied unexpectedly while setting startup-with-shell: %s"), extended_remote_restart (); } - /* vRun's success return is a stop reply. */ - stop_reply = run_worked ? rs->buf.data () : NULL; - add_current_inferior_and_thread (stop_reply); + add_current_inferior_and_thread (run_worked ? rs->buf.data () : nullptr); /* Get updated offsets, if the stub uses qOffsets. */ get_offsets (); diff --git a/gdb/target-descriptions.c b/gdb/target-descriptions.c index 1bd22c273a29..82e4d96276e3 100644 --- a/gdb/target-descriptions.c +++ b/gdb/target-descriptions.c @@ -850,6 +850,16 @@ tdesc_register_name (struct gdbarch *gdbarch, int regno) return ""; } +/* See target-descriptions.h. */ + +bool +tdesc_register_is_early_load (struct gdbarch *gdbarch, int regno) +{ + struct tdesc_reg *reg = tdesc_find_register (gdbarch, regno); + + return reg == nullptr ? false : reg->load_early; +} + struct type * tdesc_register_type (struct gdbarch *gdbarch, int regno) { @@ -1484,7 +1494,12 @@ class print_c_tdesc : public tdesc_element_visitor gdb_printf ("\"%s\", ", reg->group.c_str ()); else gdb_printf ("NULL, "); - gdb_printf ("%d, \"%s\");\n", reg->bitsize, reg->type.c_str ()); + gdb_printf ("%d, \"%s\"", reg->bitsize, reg->type.c_str ()); + + if (reg->load_early) + gdb_printf (", true"); + + gdb_printf (");\n"); } protected: @@ -1627,7 +1642,12 @@ class print_c_feature : public print_c_tdesc gdb_printf ("\"%s\", ", reg->group.c_str ()); else gdb_printf ("NULL, "); - gdb_printf ("%d, \"%s\");\n", reg->bitsize, reg->type.c_str ()); + gdb_printf ("%d, \"%s\"", reg->bitsize, reg->type.c_str ()); + + if (reg->load_early) + gdb_printf (", true"); + + gdb_printf (");\n"); m_next_regnum++; } diff --git a/gdb/target-descriptions.h b/gdb/target-descriptions.h index dc83db0acf28..e40c7db5f79f 100644 --- a/gdb/target-descriptions.h +++ b/gdb/target-descriptions.h @@ -199,6 +199,11 @@ const char *tdesc_feature_name (const struct tdesc_feature *feature); const char *tdesc_register_name (struct gdbarch *gdbarch, int regno); +/* Return whether register REGNO needs to be loaded early in the + register cache. */ + +bool tdesc_register_is_early_load (struct gdbarch *gdbarch, int regno); + /* Return the type of register REGNO, from the target description or from an architecture-provided pseudo_register_type method. */ diff --git a/gdbserver/regcache.cc b/gdbserver/regcache.cc index ec19864bd690..cd1ee2e5f145 100644 --- a/gdbserver/regcache.cc +++ b/gdbserver/regcache.cc @@ -568,6 +568,14 @@ regcache::get_register_status (int regnum) const /* See gdbsupport/common-regcache.h. */ +bool +regcache::is_load_early_register (int regnum) const +{ + return find_register_by_number (this->tdesc, regnum).load_early; +} + +/* See gdbsupport/common-regcache.h. */ + bool regcache::raw_compare (int regnum, const void *buf, int offset) const { diff --git a/gdbserver/regcache.h b/gdbserver/regcache.h index 5de8fd3d127e..07e48a7432b7 100644 --- a/gdbserver/regcache.h +++ b/gdbserver/regcache.h @@ -49,6 +49,8 @@ struct regcache : public reg_buffer_common /* See gdbsupport/common-regcache.h. */ enum register_status get_register_status (int regnum) const override; + bool is_load_early_register (int regnum) const override; + /* See gdbsupport/common-regcache.h. */ int register_size (int regnum) const override; diff --git a/gdbserver/tdesc.cc b/gdbserver/tdesc.cc index d052f43c76e6..6f7ebb7c5c76 100644 --- a/gdbserver/tdesc.cc +++ b/gdbserver/tdesc.cc @@ -56,6 +56,8 @@ init_target_desc (struct target_desc *tdesc, const char **expedite_regs) { int offset = 0; + /* Additional registers to expedite from the features. */ + std::vector expedite_from_features; /* Go through all the features and populate reg_defs. */ for (const tdesc_feature_up &feature : tdesc->features) @@ -70,8 +72,11 @@ init_target_desc (struct target_desc *tdesc, tdesc->reg_defs.resize (regnum, gdb::reg (offset)); tdesc->reg_defs.emplace_back (treg->name.c_str (), offset, - treg->bitsize); + treg->bitsize, treg->load_early); offset += treg->bitsize; + + if (treg->load_early) + expedite_from_features.push_back (treg->name.c_str ()); } tdesc->registers_size = offset / 8; @@ -88,6 +93,11 @@ init_target_desc (struct target_desc *tdesc, int expedite_count = 0; while (expedite_regs[expedite_count] != nullptr) tdesc->expedite_regs.push_back (expedite_regs[expedite_count++]); + + if (!expedite_from_features.empty ()) + tdesc->expedite_regs.insert (tdesc->expedite_regs.end (), + expedite_from_features.cbegin (), + expedite_from_features.cend ()); #endif } diff --git a/gdbsupport/common-regcache.h b/gdbsupport/common-regcache.h index 4594999346fd..93b1b5d522f2 100644 --- a/gdbsupport/common-regcache.h +++ b/gdbsupport/common-regcache.h @@ -73,6 +73,9 @@ struct reg_buffer_common buffer. */ virtual register_status get_register_status (int regnum) const = 0; + /* Does this register need to be loaded before others? */ + virtual bool is_load_early_register (int regnum) const = 0; + /* Return the size of register numbered REGNUM in this buffer. */ virtual int register_size (int regnum) const = 0; diff --git a/gdbsupport/tdesc.cc b/gdbsupport/tdesc.cc index 080d39c485dc..a99119274f44 100644 --- a/gdbsupport/tdesc.cc +++ b/gdbsupport/tdesc.cc @@ -21,12 +21,13 @@ tdesc_reg::tdesc_reg (struct tdesc_feature *feature, const std::string &name_, int regnum, int save_restore_, const char *group_, - int bitsize_, const char *type_) + int bitsize_, const char *type_, bool load_early_) : name (name_), target_regnum (regnum), save_restore (save_restore_), group (group_ != NULL ? group_ : ""), bitsize (bitsize_), - type (type_ != NULL ? type_ : "") + type (type_ != NULL ? type_ : ""), + load_early (load_early_) { /* If the register's type is target-defined, look it up now. We may not have easy access to the containing feature when we want it later. */ @@ -137,10 +138,10 @@ tdesc_named_type (const struct tdesc_feature *feature, const char *id) void tdesc_create_reg (struct tdesc_feature *feature, const char *name, int regnum, int save_restore, const char *group, - int bitsize, const char *type) + int bitsize, const char *type, bool load_early) { tdesc_reg *reg = new tdesc_reg (feature, name, regnum, save_restore, - group, bitsize, type); + group, bitsize, type, load_early); feature->registers.emplace_back (reg); } diff --git a/gdbsupport/tdesc.h b/gdbsupport/tdesc.h index c9e7603369cb..7e483486139b 100644 --- a/gdbsupport/tdesc.h +++ b/gdbsupport/tdesc.h @@ -70,7 +70,7 @@ struct tdesc_reg : tdesc_element { tdesc_reg (struct tdesc_feature *feature, const std::string &name_, int regnum, int save_restore_, const char *group_, - int bitsize_, const char *type_); + int bitsize_, const char *type_, bool load_early_ = false); virtual ~tdesc_reg () = default; @@ -110,6 +110,12 @@ struct tdesc_reg : tdesc_element /* The target-described type corresponding to TYPE, if found. */ struct tdesc_type *tdesc_type; + /* Whether this register needs to be loaded early in GDB's regcache. + + In addition, if true gdbserver will send it as an expedited register + in stop replies. */ + bool load_early; + void accept (tdesc_element_visitor &v) const override { v.visit (this); @@ -415,7 +421,8 @@ void tdesc_add_enum_value (tdesc_type_with_fields *type, int value, /* Create a register in feature FEATURE. */ void tdesc_create_reg (struct tdesc_feature *feature, const char *name, int regnum, int save_restore, const char *group, - int bitsize, const char *type); + int bitsize, const char *type, + bool load_early = false); /* Return the tdesc in string XML format. */