From patchwork Sun Jun 8 01:03:14 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thiago Jung Bauermann X-Patchwork-Id: 894805 Delivered-To: patch@linaro.org Received: by 2002:a05:6000:ecd:b0:3a4:ee3f:8f15 with SMTP id ea13csp1175956wrb; Sat, 7 Jun 2025 18:07:22 -0700 (PDT) X-Forwarded-Encrypted: i=3; AJvYcCVehmLBt3uDoPq62JuNZWdobfkVU6N1BdTo/VisfES1p9nHJPL79mHzhkfn7eILHvb9mzCmFw==@linaro.org X-Google-Smtp-Source: AGHT+IF6kN8G+WG6aSgGvpQzrvg3UhbWFIJXRXjijQdCPMF/A/yMR2rRSYCK45FTRkrh2WlJC8IQ X-Received: by 2002:a05:622a:2598:b0:4a3:d015:38c0 with SMTP id d75a77b69052e-4a5b9d931d7mr155313831cf.37.1749344842331; Sat, 07 Jun 2025 18:07:22 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1749344842; cv=pass; d=google.com; s=arc-20240605; b=d7+n9SUkGXGdNMRVBdTctUed7au8hBp5nFZrRrkpzhQrYyTx3BMj1IAXoV/e3SaTej Lgf0DpXmv+eyVaJz8sXX/6mKMxVUEp1q1uzSLqwnn9VU4HBRCNYhWV4Uog5Fv/HWGzfJ F6XunU2+uJEBeWDzi2M4N1y3Jl2/xkipxKYmWBX9gcXAPeRXuwTa5sNBJHbmJwu/BFvQ 57BeeoDdI34IMFfhlXSVR7P1mcjm8mzi8ADJVaQ/qwXB6CXVCPxyAdCnRBUCnKl0PTm6 oT3gmnfyQaVdN1WnjMK1GQXCxDjdbTsFh+8WUcU7sQ4HK/TZnkBlOvuQ/N9mmwn3O6hQ hLWg== 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:dkim-filter:arc-filter:dmarc-filter:delivered-to :dkim-filter; bh=s3wJiws+PXN3r+7Y/3E0ENlyddk9Hsb6egYpK6HMrJk=; fh=72kqq0iQhigvR9Vv/oqX5ebs3Yyyw7XhzWxOOEPdupI=; b=YKHiF1tp3XyrIDbKGY8sTCkMwbFVLZkFBnmYKQqkZtObgrhO0ASPJ+B5lo0/gGeIJV voxAjxJEh+9rrnMysEllLtSDJoopCrvA/dGysgtHzThNx/BNW3PYDIyhYKGOKNe4wK86 7PY9hiq9Q3Xd2FcAlrsoSfEShC8cHKxvndVh22RkTbXKC+QVv6IjdU3QnkaP06wGws2v txwVhtSs7+0M+DGLroSD4x05n2JNt0+CSDZ4uL2xCfysVqSseG68/3xWbi0t7i1WTW8R LwMaht4CdFiKYtr9iOvpUq6Eo0a8JBWKd7RFwNQvVt98l9DeB6ZDSBieneHvD1g85rEo yjzA==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=QVx8uAln; 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 d75a77b69052e-4a61988c639si49035731cf.324.2025.06.07.18.07.22 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 07 Jun 2025 18:07:22 -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=QVx8uAln; 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 C24993858406 for ; Sun, 8 Jun 2025 01:07:21 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C24993858406 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256 header.s=google header.b=QVx8uAln X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from mail-vs1-xe2c.google.com (mail-vs1-xe2c.google.com [IPv6:2607:f8b0:4864:20::e2c]) by sourceware.org (Postfix) with ESMTPS id D37E6385840C for ; Sun, 8 Jun 2025 01:03:52 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D37E6385840C 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 D37E6385840C Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::e2c ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1749344633; cv=none; b=c++HCKx2dmTcjP+7uWw6sSDcx5veGUbAPEvOnq8MnxLcCALjwlmt9oFlT/ABWFwnP8L7FtHystxsttaRR71Y7xpl0BkLIB+6bG1/pUVfLMNCSpwHYQRGFaLi0AFgJvAwAQhI82MRuI8zGq3dz/c8gsua2GJbi50WlAadsRNDsb4= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1749344633; c=relaxed/simple; bh=XlS5Y0HeA+7wna6mbOjkpx4e9+zGooEgiju8O/4gOsk=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=Rg7YG7EEwGznbeDze2il1iJ6G85w3uvq89ywSe0qzeV5jenW3uOEEzH8+pQU4l2/1DYYP/R7JwquaRai/exNcWxlrd9zjqP+9hg6uTuY9gC5qdUJQsTRAT3yomnDKocpmLk99zsmlQRRm0Ojrt0iODoBjzoW1UO4TQyiMS0g1rk= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D37E6385840C Received: by mail-vs1-xe2c.google.com with SMTP id ada2fe7eead31-4e5adc977ecso980036137.3 for ; Sat, 07 Jun 2025 18:03:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1749344632; x=1749949432; 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=s3wJiws+PXN3r+7Y/3E0ENlyddk9Hsb6egYpK6HMrJk=; b=QVx8uAln2Qaf6C8+MsGcO3OX48OkY5VOIkfLj9Ca/kPnbfAcE/KTtl+2L/aR3MwpLl U1hHwHz5vxH3JS7xvp2TAKaO3PR8FtPvwrQlhj6BHZraI0V70C9UyHUCQfVa7Azo4ykT Az3eTGyw43KssU/arlH/Oe+cbMQ+z/9wWdSij9AVGpAcqeuK3SKStTXKKvzWSEYeu5jx pJ1pGg4Zx6Bgi+svX2CbGvFITt4I2deHa0mScSHcDUVNrU868RAuhVNu9g231Nswo+Kn qZaGM/1svjJXmwbE4bNnQAQBEbXZPhCOdU6tRt35viY8o8Hlx4kcPYx4SQ22CI/NFcQi b24w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1749344632; x=1749949432; 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=s3wJiws+PXN3r+7Y/3E0ENlyddk9Hsb6egYpK6HMrJk=; b=OrK9CGiAYc0jlcIY7UB9p17bKH1v/DnPHtuVG1z7T6eJQbp+TIaBZ1cOGTnPVs4Rxa tcD4JJcODXGX2d1xpQKYtBEB0WIm/v6xOKb8pRWYAOyDKTUt3LvyeO268P3wLw3w22mR hrJ6wi0CpUvLHSSQ2ZczlhTrzweXyHznPFJxlKS1kPtw0mgKNx9KQpMbCjjPXp1GvA+O 5OmZbc/U8H4bql3S/+KcDGDA9f+0hKU0Wnn5To3uXpvoi/cOtGNQpWvY9s48lqaULHjR qSFtgMgyTq4KY98fyRlXsmMqRB7i2ogg+2qRvbLI0tacYrww9sOCctNF32CBAumF25Oi XeRA== X-Gm-Message-State: AOJu0YzU6fN0Lf3G7zh5RHWIYkEBQdugWTd1P8jaI99lhdQWHc4DPcAE Ml3QIgfYQdG1EvBzOlG/7Nfs3jy5jGur054bbO+RdIz7yAsSyF3pCaXbYNxxTBp6T+2e+hZwVFB NaNQK X-Gm-Gg: ASbGncsuTKzQ+zArJdyhv6XrEymq0x1VEi6bDyYxaCy3EHljK7T/elx/MyUdDOJf/KY MpyVdioU5FtkucJjJvZ7ix3Gpj3QOPAv2OFBWfQw+brfIuz8V//ufNInuHPbOpULhXwmR811hDS Th12QBN4F61ZCc54bcic4Js77GmhZb1J0yzFl1xnui5NSJtugmUArS1MHFSqT/kgP6reCYmgoMf BGZgjFbYsnV0VO27/bSkiaOVb6R+9YCwk+9ndIWq3wuZ8zAzUYQfwhEeWZ2U7qBzYWw+C3CLv1b VW140Z0COoaOE3rXEk6iJ4+SrNKVg+/ZB9jte75PuWDW7nKlw/gPViGlCCYSYFfkxi8SDFoY X-Received: by 2002:a05:6102:458a:b0:4e5:93f5:e834 with SMTP id ada2fe7eead31-4e772ade074mr8199612137.24.1749344631887; Sat, 07 Jun 2025 18:03:51 -0700 (PDT) Received: from localhost ([2804:14d:7e39:88d6:8cb1:1e8e:e951:f687]) by smtp.gmail.com with ESMTPSA id ada2fe7eead31-4e77391ee00sm3127237137.15.2025.06.07.18.03.50 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 07 Jun 2025 18:03:51 -0700 (PDT) From: Thiago Jung Bauermann To: gdb-patches@sourceware.org Subject: [PATCH 3/8] GDB, gdbserver: aarch64-linux: Initial Guarded Control Stack support Date: Sat, 7 Jun 2025 22:03:14 -0300 Message-ID: <20250608010338.2234530-4-thiago.bauermann@linaro.org> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250608010338.2234530-1-thiago.bauermann@linaro.org> References: <20250608010338.2234530-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 Add the org.gnu.gdb.aarch64.gcs feature with the GCSPR register, and the org.gnu.gdb.aarch64.gcs.linux feature with "registers" to represent the Linux kernel ptrace and prctl knobs that enable and lock specific GCS functionality. Reviewed-By: Luis Machado --- gdb/aarch64-linux-nat.c | 79 ++++++++++++++++++++++ gdb/aarch64-linux-tdep.c | 23 +++++++ gdb/aarch64-tdep.c | 101 +++++++++++++++++++++++++++++ gdb/aarch64-tdep.h | 14 ++++ gdb/arch/aarch64-gcs-linux.h | 44 +++++++++++++ gdb/arch/aarch64.c | 8 +++ gdb/arch/aarch64.h | 10 ++- gdb/doc/gdb.texinfo | 51 +++++++++++++++ gdb/features/Makefile | 2 + gdb/features/aarch64-gcs-linux.c | 21 ++++++ gdb/features/aarch64-gcs-linux.xml | 18 +++++ gdb/features/aarch64-gcs.c | 14 ++++ gdb/features/aarch64-gcs.xml | 11 ++++ gdbserver/linux-aarch64-low.cc | 46 +++++++++++++ 14 files changed, 441 insertions(+), 1 deletion(-) create mode 100644 gdb/arch/aarch64-gcs-linux.h create mode 100644 gdb/features/aarch64-gcs-linux.c create mode 100644 gdb/features/aarch64-gcs-linux.xml create mode 100644 gdb/features/aarch64-gcs.c create mode 100644 gdb/features/aarch64-gcs.xml diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c index d7869f42e825..0796b1e47955 100644 --- a/gdb/aarch64-linux-nat.c +++ b/gdb/aarch64-linux-nat.c @@ -51,6 +51,7 @@ #include "gdb_proc_service.h" #include "arch-utils.h" +#include "arch/aarch64-gcs-linux.h" #include "arch/aarch64-mte-linux.h" #include "nat/aarch64-mte-linux-ptrace.h" @@ -542,6 +543,67 @@ store_tlsregs_to_thread (struct regcache *regcache) perror_with_name (_("unable to store TLS register")); } +/* Fill GDB's register array with the GCS register values from + the current thread. */ + +static void +fetch_gcsregs_from_thread (struct regcache *regcache) +{ + aarch64_gdbarch_tdep *tdep + = gdbarch_tdep (regcache->arch ()); + + gdb_assert (tdep->gcs_reg_base != -1); + gdb_assert (tdep->gcs_linux_reg_base != -1); + + struct user_gcs user_gcs; + struct iovec iovec; + + iovec.iov_base = &user_gcs; + iovec.iov_len = sizeof (user_gcs); + + int tid = get_ptrace_pid (regcache->ptid ()); + if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_GCS, &iovec) != 0) + perror_with_name (_("unable to fetch GCS registers")); + + regcache->raw_supply (tdep->gcs_reg_base, &user_gcs.gcspr_el0); + regcache->raw_supply (tdep->gcs_linux_reg_base, &user_gcs.features_enabled); + regcache->raw_supply (tdep->gcs_linux_reg_base + 1, + &user_gcs.features_locked); +} + +/* Store to the current thread the valid GCS register set in the GDB's + register array. */ + +static void +store_gcsregs_to_thread (struct regcache *regcache) +{ + aarch64_gdbarch_tdep *tdep + = gdbarch_tdep (regcache->arch ()); + + gdb_assert (tdep->gcs_reg_base != -1); + gdb_assert (tdep->gcs_linux_reg_base != -1); + + if (REG_VALID != regcache->get_register_status (tdep->gcs_reg_base) + || REG_VALID != regcache->get_register_status (tdep->gcs_linux_reg_base) + || REG_VALID + != regcache->get_register_status (tdep->gcs_linux_reg_base + 1)) + return; + + struct user_gcs user_gcs; + regcache->raw_collect (tdep->gcs_reg_base, &user_gcs.gcspr_el0); + regcache->raw_collect (tdep->gcs_linux_reg_base, &user_gcs.features_enabled); + regcache->raw_collect (tdep->gcs_linux_reg_base + 1, + &user_gcs.features_locked); + + struct iovec iovec; + iovec.iov_base = &user_gcs; + iovec.iov_len = sizeof (user_gcs); + + int tid = get_ptrace_pid (regcache->ptid ()); + if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_GCS, &iovec) != 0) + perror_with_name (_("unable to store GCS registers")); +} + /* The AArch64 version of the "fetch_registers" target_ops method. Fetch REGNO from the target and place the result into REGCACHE. */ @@ -577,6 +639,9 @@ aarch64_fetch_registers (struct regcache *regcache, int regno) if (tdep->has_sme2 ()) fetch_zt_from_thread (regcache); + + if (tdep->has_gcs ()) + fetch_gcsregs_from_thread (regcache); } /* General purpose register? */ else if (regno < AARCH64_V0_REGNUM) @@ -609,6 +674,11 @@ aarch64_fetch_registers (struct regcache *regcache, int regno) && regno >= tdep->tls_regnum_base && regno < tdep->tls_regnum_base + tdep->tls_register_count) fetch_tlsregs_from_thread (regcache); + /* GCS register? */ + else if (tdep->has_gcs () + && (regno == tdep->gcs_reg_base || regno == tdep->gcs_linux_reg_base + || regno == tdep->gcs_linux_reg_base + 1)) + fetch_gcsregs_from_thread (regcache); } /* A version of the "fetch_registers" target_ops method used when running @@ -680,6 +750,9 @@ aarch64_store_registers (struct regcache *regcache, int regno) if (tdep->has_sme2 ()) store_zt_to_thread (regcache); + + if (tdep->has_gcs ()) + store_gcsregs_to_thread (regcache); } /* General purpose register? */ else if (regno < AARCH64_V0_REGNUM) @@ -706,6 +779,11 @@ aarch64_store_registers (struct regcache *regcache, int regno) && regno >= tdep->tls_regnum_base && regno < tdep->tls_regnum_base + tdep->tls_register_count) store_tlsregs_to_thread (regcache); + /* GCS register? */ + else if (tdep->has_gcs () + && (regno == tdep->gcs_reg_base || regno == tdep->gcs_linux_reg_base + || regno == tdep->gcs_linux_reg_base + 1)) + store_gcsregs_to_thread (regcache); /* PAuth registers are read-only. */ } @@ -881,6 +959,7 @@ aarch64_linux_nat_target::read_description () active or not. */ features.vq = aarch64_sve_get_vq (tid); features.pauth = hwcap & AARCH64_HWCAP_PACA; + features.gcs = features.gcs_linux = hwcap & HWCAP_GCS; features.mte = hwcap2 & HWCAP2_MTE; features.tls = aarch64_tls_register_count (tid); /* SME feature check. */ diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index a194ac809c23..ce213bb482b9 100644 --- a/gdb/aarch64-linux-tdep.c +++ b/gdb/aarch64-linux-tdep.c @@ -50,6 +50,7 @@ #include "record-full.h" #include "linux-record.h" +#include "arch/aarch64-gcs-linux.h" #include "arch/aarch64-mte.h" #include "arch/aarch64-mte-linux.h" #include "arch/aarch64-scalable-linux.h" @@ -1604,6 +1605,27 @@ aarch64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch, cb (".reg-aarch-tls", sizeof_tls_regset, sizeof_tls_regset, &aarch64_linux_tls_regset, "TLS register", cb_data); } + + /* Handle GCS registers. */ + if (tdep->has_gcs ()) + { + /* Create this on the fly in order to handle the variable regnums. */ + const struct regcache_map_entry gcs_regmap[] = + { + { 1, tdep->gcs_linux_reg_base, 8 }, /* features_enabled */ + { 1, tdep->gcs_linux_reg_base + 1, 8 }, /* features_locked */ + { 1, tdep->gcs_reg_base, 8 }, /* GCSPR */ + { 0 } + }; + + const struct regset aarch64_linux_gcs_regset = + { + gcs_regmap, regcache_supply_regset, regcache_collect_regset + }; + + cb (".reg-aarch-gcs", sizeof (struct user_gcs), sizeof (struct user_gcs), + &aarch64_linux_gcs_regset, "GCS registers", cb_data); + } } /* Implement the "core_read_description" gdbarch method. */ @@ -1628,6 +1650,7 @@ aarch64_linux_core_read_description (struct gdbarch *gdbarch, length. */ features.vq = aarch64_linux_core_read_vq_from_sections (gdbarch, abfd); features.pauth = hwcap & AARCH64_HWCAP_PACA; + features.gcs = features.gcs_linux = hwcap & HWCAP_GCS; features.mte = hwcap2 & HWCAP2_MTE; /* Handle the TLS section. */ diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index 8d54e59f332a..d728f60e9e15 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -159,6 +159,18 @@ static const char *const aarch64_mte_register_names[] = "tag_ctl" }; +static const char *const aarch64_gcs_register_names[] = { + /* Guarded Control Stack Pointer Register. */ + "gcspr" +}; + +static const char *const aarch64_gcs_linux_register_names[] = { + /* Field in struct user_gcs. */ + "gcs_features_enabled", + /* Field in struct user_gcs. */ + "gcs_features_locked", +}; + static int aarch64_stack_frame_destroyed_p (struct gdbarch *, CORE_ADDR); /* AArch64 prologue cache structure. */ @@ -1875,6 +1887,39 @@ pass_in_v_vfp_candidate (struct gdbarch *gdbarch, struct regcache *regcache, } } +/* Push LR_VALUE to the Guarded Control Stack. */ + +static void +aarch64_push_gcs_entry (regcache *regs, CORE_ADDR lr_value) +{ + gdbarch *arch = regs->arch (); + aarch64_gdbarch_tdep *tdep = gdbarch_tdep (arch); + CORE_ADDR gcs_addr; + + enum register_status status = regs->cooked_read (tdep->gcs_reg_base, + &gcs_addr); + if (status != REG_VALID) + error ("Can't read $gcspr."); + + gcs_addr -= 8; + gdb_byte buf[8]; + store_integer (buf, gdbarch_byte_order (arch), lr_value); + if (target_write_memory (gcs_addr, buf, sizeof (buf)) != 0) + error ("Can't write to Guarded Control Stack."); + + /* Update GCSPR. */ + regcache_cooked_write_unsigned (regs, tdep->gcs_reg_base, gcs_addr); +} + +/* Implement the "shadow_stack_push" gdbarch method. */ + +static void +aarch64_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR new_addr, + regcache *regcache) +{ + aarch64_push_gcs_entry (regcache, new_addr); +} + /* Implement the "push_dummy_call" gdbarch method. */ static CORE_ADDR @@ -4046,6 +4091,14 @@ aarch64_features_from_target_desc (const struct target_desc *tdesc) features.sme2 = (tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.sme2") != nullptr); + /* Check for the GCS feature. */ + features.gcs = (tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.gcs") + != nullptr); + + /* Check for the GCS Linux feature. */ + features.gcs_linux = (tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.gcs.linux") + != nullptr); + return features; } @@ -4590,6 +4643,46 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) int first_w_regnum = num_pseudo_regs; num_pseudo_regs += 31; + const struct tdesc_feature *feature_gcs + = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.gcs"); + int first_gcs_regnum = -1; + /* Add the GCS registers. */ + if (feature_gcs != nullptr) + { + first_gcs_regnum = num_regs; + /* Validate the descriptor provides the mandatory GCS registers and + allocate their numbers. */ + for (i = 0; i < ARRAY_SIZE (aarch64_gcs_register_names); i++) + valid_p &= tdesc_numbered_register (feature_gcs, tdesc_data.get (), + first_gcs_regnum + i, + aarch64_gcs_register_names[i]); + + num_regs += i; + } + + if (!valid_p) + return nullptr; + + const struct tdesc_feature *feature_gcs_linux + = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.gcs.linux"); + int first_gcs_linux_regnum = -1; + /* Add the GCS Linux registers. */ + if (feature_gcs_linux != nullptr) + { + first_gcs_linux_regnum = num_regs; + /* Validate the descriptor provides the mandatory GCS Linux registers + and allocate their numbers. */ + for (i = 0; i < ARRAY_SIZE (aarch64_gcs_linux_register_names); i++) + valid_p &= tdesc_numbered_register (feature_gcs_linux, tdesc_data.get (), + first_gcs_linux_regnum + i, + aarch64_gcs_linux_register_names[i]); + + /* This feature depends on the GCS feature. */ + valid_p &= feature_gcs != nullptr; + + num_regs += i; + } + if (!valid_p) return nullptr; @@ -4611,6 +4704,8 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) tdep->mte_reg_base = first_mte_regnum; tdep->tls_regnum_base = first_tls_regnum; tdep->tls_register_count = tls_register_count; + tdep->gcs_reg_base = first_gcs_regnum; + tdep->gcs_linux_reg_base = first_gcs_linux_regnum; /* Set the SME register set details. The pseudo-registers will be adjusted later. */ @@ -4733,6 +4828,9 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_get_pc_address_flags (gdbarch, aarch64_get_pc_address_flags); + if (tdep->has_gcs ()) + set_gdbarch_shadow_stack_push (gdbarch, aarch64_shadow_stack_push); + tdesc_use_registers (gdbarch, tdesc, std::move (tdesc_data)); /* Fetch the updated number of registers after we're done adding all @@ -4905,6 +5003,9 @@ aarch64_dump_tdep (struct gdbarch *gdbarch, struct ui_file *file) pulongest (tdep->sme_tile_pseudo_base)); gdb_printf (file, _("aarch64_dump_tdep: sme_svq = %s\n"), pulongest (tdep->sme_svq)); + + gdb_printf (file, _ ("aarch64_dump_tdep: gcs_reg_base = %d\n"), + tdep->gcs_reg_base); } #if GDB_SELF_TEST diff --git a/gdb/aarch64-tdep.h b/gdb/aarch64-tdep.h index 3b8dcc26545b..0332e80da7bc 100644 --- a/gdb/aarch64-tdep.h +++ b/gdb/aarch64-tdep.h @@ -182,6 +182,20 @@ struct aarch64_gdbarch_tdep : gdbarch_tdep_base { return sme2_zt0_regnum > 0; } + + /* First GCS register. This is -1 if no GCS registers are available. */ + int gcs_reg_base = -1; + + /* First GCS Linux-specific register. This is -1 if no GCS Linux feature is + available. */ + int gcs_linux_reg_base = -1; + + /* Returns true if the target supports GCS. */ + bool + has_gcs () const + { + return gcs_reg_base != -1; + } }; const target_desc *aarch64_read_description (const aarch64_features &features); diff --git a/gdb/arch/aarch64-gcs-linux.h b/gdb/arch/aarch64-gcs-linux.h new file mode 100644 index 000000000000..9366caa7289a --- /dev/null +++ b/gdb/arch/aarch64-gcs-linux.h @@ -0,0 +1,44 @@ +/* Common Linux target-dependent definitions for AArch64 GCS + + Copyright (C) 2025 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 . */ + +#ifndef ARCH_AARCH64_GCS_LINUX_H +#define ARCH_AARCH64_GCS_LINUX_H + +#include + +/* Feature check for Guarded Control Stack. */ +#ifndef HWCAP_GCS +#define HWCAP_GCS (1UL << 32) +#endif + +/* Make sure we only define these if the kernel header doesn't. */ +#ifndef GCS_MAGIC + +/* GCS state (NT_ARM_GCS). */ + +struct user_gcs +{ + uint64_t features_enabled; + uint64_t features_locked; + uint64_t gcspr_el0; +}; + +#endif /* GCS_MAGIC */ + +#endif /* ARCH_AARCH64_GCS_LINUX_H */ diff --git a/gdb/arch/aarch64.c b/gdb/arch/aarch64.c index 3e1ca0547340..dff2bc16003a 100644 --- a/gdb/arch/aarch64.c +++ b/gdb/arch/aarch64.c @@ -26,6 +26,8 @@ #include "../features/aarch64-sme.c" #include "../features/aarch64-sme2.c" #include "../features/aarch64-tls.c" +#include "../features/aarch64-gcs.c" +#include "../features/aarch64-gcs-linux.c" /* See arch/aarch64.h. */ @@ -65,6 +67,12 @@ aarch64_create_target_description (const aarch64_features &features) if (features.sme2) regnum = create_feature_aarch64_sme2 (tdesc.get (), regnum); + if (features.gcs) + regnum = create_feature_aarch64_gcs (tdesc.get (), regnum); + + if (features.gcs_linux) + regnum = create_feature_aarch64_gcs_linux (tdesc.get (), regnum); + return tdesc.release (); } diff --git a/gdb/arch/aarch64.h b/gdb/arch/aarch64.h index ee18b74b80f5..679d845df74e 100644 --- a/gdb/arch/aarch64.h +++ b/gdb/arch/aarch64.h @@ -51,6 +51,12 @@ struct aarch64_features /* Whether SME2 is supported. */ bool sme2 = false; + + /* Whether Guarded Control Stack is supported. */ + bool gcs = false; + + /* Whether Guarded Control Stack Linux features are supported. */ + bool gcs_linux = false; }; inline bool operator==(const aarch64_features &lhs, const aarch64_features &rhs) @@ -60,7 +66,9 @@ inline bool operator==(const aarch64_features &lhs, const aarch64_features &rhs) && lhs.mte == rhs.mte && lhs.tls == rhs.tls && lhs.svq == rhs.svq - && lhs.sme2 == rhs.sme2; + && lhs.sme2 == rhs.sme2 + && lhs.gcs == rhs.gcs + && lhs.gcs_linux == rhs.gcs_linux; } namespace std diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index c7b57cf22505..03f419e90436 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -26979,6 +26979,21 @@ information automatically from the core file, and will show one of the above messages depending on whether the synchronous or asynchronous mode is selected. @xref{Memory Tagging}. @xref{Memory}. +@subsubsection Guarded Control Stack +@cindex AArch64 GCS +@cindex AArch64 Guarded Control Stack + +When @value{GDBN} is debugging the AArch64 architecture, the program is +using the feature Guarded Control Stack (GCS) and there is support in the +kernel for GCS, @value{GDBN} will make a couple of special registers --- +@code{gcs_features_enabled} and @code{gcs_features_locked} --- available +through the @code{org.gnu.gdb.aarch64.gcs.linux} feature. These registers +expose some options that can be controlled at runtime and emulate the +@code{prctl} option @code{PR_SET_SHADOW_STACK_STATUS}. For further +information, see the +@uref{https://www.kernel.org/doc/html/latest/arch/arm64/gcs.html,ignored, +documentation} in the Linux kernel. + @node x86 @subsection x86 @@ -49655,6 +49670,42 @@ of bytes. Extra registers are allowed in this feature, but they will not affect @value{GDBN}. +@subsubsection GCS registers + +The @samp{org.gnu.gdb.aarch64.gcs} feature is optional. If present, it +means the target supports Guarded Control Stacks and must contain the +following register: + +@itemize @minus + +@item +@code{gcspr}, which points to the thread's Guarded Control Stack. It is 64 +bits in size and has a type of @samp{data_ptr}. + +@end itemize + +The @samp{org.gnu.gdb.aarch64.gcs.linux} feature is optional. If present, +then the @samp{org.gnu.gdb.aarch64.gcs} feature must also be present. The +@samp{org.gnu.gdb.aarch64.gcs.linux} feature represents facilities provided +by the Linux kernel for GCS support and should contain the following: + +@itemize @minus + +@item +@code{gcs_features_enabled} shows the features currently enabled via the +prctl or ptrace system calls. It is represented as if it were a 64-bit +register with a custom flags type. + +@item +@code{gcs_features_locked} shows the features currently locked via the +prctl or ptrace system calls. It is represented as if it were a 64-bit +register with a custom flags type. + +@end itemize + +Extra registers are allowed in these features, but they will not affect +@value{GDBN}. + @node ARC Features @subsection ARC Features @cindex target descriptions, ARC Features diff --git a/gdb/features/Makefile b/gdb/features/Makefile index 2afda1ccd00f..ed20a7537ffb 100644 --- a/gdb/features/Makefile +++ b/gdb/features/Makefile @@ -203,6 +203,8 @@ FEATURE_XMLFILES = aarch64-core.xml \ aarch64-fpu.xml \ aarch64-pauth.xml \ aarch64-mte.xml \ + aarch64-gcs.xml \ + aarch64-gcs-linux.xml \ arc/v1-core.xml \ arc/v1-aux.xml \ arc/v2-core.xml \ diff --git a/gdb/features/aarch64-gcs-linux.c b/gdb/features/aarch64-gcs-linux.c new file mode 100644 index 000000000000..6b0d25b4518c --- /dev/null +++ b/gdb/features/aarch64-gcs-linux.c @@ -0,0 +1,21 @@ +/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro: + Original: aarch64-gcs-linux.xml */ + +#include "gdbsupport/tdesc.h" + +static int +create_feature_aarch64_gcs_linux (struct target_desc *result, long regnum) +{ + struct tdesc_feature *feature; + + feature = tdesc_create_feature (result, "org.gnu.gdb.aarch64.gcs.linux"); + tdesc_type_with_fields *type_with_fields; + type_with_fields = tdesc_create_flags (feature, "features_flags", 8); + tdesc_add_flag (type_with_fields, 0, "PR_SHADOW_STACK_ENABLE"); + tdesc_add_flag (type_with_fields, 1, "PR_SHADOW_STACK_WRITE"); + tdesc_add_flag (type_with_fields, 2, "PR_SHADOW_STACK_PUSH"); + + tdesc_create_reg (feature, "gcs_features_enabled", regnum++, 1, "system", 64, "features_flags"); + tdesc_create_reg (feature, "gcs_features_locked", regnum++, 1, "system", 64, "features_flags"); + return regnum; +} diff --git a/gdb/features/aarch64-gcs-linux.xml b/gdb/features/aarch64-gcs-linux.xml new file mode 100644 index 000000000000..8d9d2ceb9260 --- /dev/null +++ b/gdb/features/aarch64-gcs-linux.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/gdb/features/aarch64-gcs.c b/gdb/features/aarch64-gcs.c new file mode 100644 index 000000000000..2b2caf29cc8c --- /dev/null +++ b/gdb/features/aarch64-gcs.c @@ -0,0 +1,14 @@ +/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro: + Original: aarch64-gcs.xml */ + +#include "gdbsupport/tdesc.h" + +static int +create_feature_aarch64_gcs (struct target_desc *result, long regnum) +{ + struct tdesc_feature *feature; + + feature = tdesc_create_feature (result, "org.gnu.gdb.aarch64.gcs"); + tdesc_create_reg (feature, "gcspr", regnum++, 1, "system", 64, "data_ptr"); + return regnum; +} diff --git a/gdb/features/aarch64-gcs.xml b/gdb/features/aarch64-gcs.xml new file mode 100644 index 000000000000..bbee5e001722 --- /dev/null +++ b/gdb/features/aarch64-gcs.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc index 2eb3af659ad1..9feb1914dbd6 100644 --- a/gdbserver/linux-aarch64-low.cc +++ b/gdbserver/linux-aarch64-low.cc @@ -39,6 +39,7 @@ #include "gdb_proc_service.h" #include "arch/aarch64.h" +#include "arch/aarch64-gcs-linux.h" #include "arch/aarch64-mte-linux.h" #include "arch/aarch64-scalable-linux.h" #include "linux-aarch32-tdesc.h" @@ -321,6 +322,42 @@ aarch64_store_tlsregset (struct regcache *regcache, const void *buf) supply_register (regcache, *regnum, tls_buf + sizeof (uint64_t)); } +/* Fill BUF with GCS register from the regcache. */ + +static void +aarch64_fill_gcsregset (struct regcache *regcache, void *buf) +{ + struct user_gcs *regset = (struct user_gcs *) buf; + int gcspr_regnum = find_regno (regcache->tdesc, "gcspr"); + int features_enabled_regnum = find_regno (regcache->tdesc, + "gcs_features_enabled"); + int features_locked_regnum = find_regno (regcache->tdesc, + "gcs_features_locked"); + + collect_register (regcache, gcspr_regnum, ®set->gcspr_el0); + collect_register (regcache, features_enabled_regnum, + ®set->features_enabled); + collect_register (regcache, features_locked_regnum, ®set->features_locked); +} + +/* Store GCS register to regcache. */ + +static void +aarch64_store_gcsregset (struct regcache *regcache, const void *buf) +{ + const struct user_gcs *regset = (const struct user_gcs *) buf; + int gcspr_regnum = find_regno (regcache->tdesc, "gcspr"); + int features_enabled_regnum = find_regno (regcache->tdesc, + "gcs_features_enabled"); + int features_locked_regnum = find_regno (regcache->tdesc, + "gcs_features_locked"); + + supply_register (regcache, gcspr_regnum, ®set->gcspr_el0); + supply_register (regcache, features_enabled_regnum, + ®set->features_enabled); + supply_register (regcache, features_locked_regnum, ®set->features_locked); +} + bool aarch64_target::low_supports_breakpoints () { @@ -846,6 +883,10 @@ static struct regset_info aarch64_regsets[] = { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_TLS, 0, OPTIONAL_REGS, aarch64_fill_tlsregset, aarch64_store_tlsregset }, + /* Guarded Control Stack registers. */ + { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_GCS, + 0, OPTIONAL_REGS, + aarch64_fill_gcsregset, aarch64_store_gcsregset }, NULL_REGSET }; @@ -909,6 +950,10 @@ aarch64_adjust_register_sets (const struct aarch64_features &features) if (features.sme2) regset->size = AARCH64_SME2_ZT0_SIZE; break; + case NT_ARM_GCS: + if (features.gcs_linux) + regset->size = sizeof (struct user_gcs); + break; default: gdb_assert_not_reached ("Unknown register set found."); } @@ -940,6 +985,7 @@ aarch64_target::low_arch_setup () /* A-profile MTE is 64-bit only. */ features.mte = linux_get_hwcap2 (pid, 8) & HWCAP2_MTE; features.tls = aarch64_tls_register_count (tid); + features.gcs = features.gcs_linux = linux_get_hwcap (pid, 8) & HWCAP_GCS; /* Scalable Matrix Extension feature and size check. */ if (linux_get_hwcap2 (pid, 8) & HWCAP2_SME)