From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from EUR05-AM6-obe.outbound.protection.outlook.com (mail-am6eur05on2079.outbound.protection.outlook.com [40.107.22.79]) by sourceware.org (Postfix) with ESMTPS id 108F43858D3C for ; Tue, 3 May 2022 21:56:47 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 108F43858D3C ARC-Seal: i=2; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=pass; b=lHESXeijthkC04cEigTe3eUbeKvq/cCX113h14OVI3cbvMoHfxiUQe+jX7P3dBSmTZwk1HgyAGDK0fIoAThLipLl4BjN2gjkT793FIAvVZVQXE7+KSoD/hQG+7YWffKxjs8UExjNOblKrRyfBC7ooMk9PR1fn0CtaApedGvwhNHs6iV4TcaY3IftmF8IN6kC9DooiaodmupPmXuoD2qSzsdx2gmNEe/APHnDaaBZrunfCtZ0l1CCiAPdMKkHmkce9VOfkS5HZ/svCY0dHXiQlLc5oIw0Du9SDjs+7pg+2AX/5b/8eJmewBgSoJwy6h1p4XzkTju8Goqzd87QKXq0vg== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=1nExHiik/GEeK+OMRNGgAWIpDfWXME+/I7+NxH9jyEc=; b=Pas7n8iCSHr9zxYrerIkoFDQpb/tQBue5D2QGM5PeGNCTG5FOHZu09zgtXIrRu/6pXYupMKZBLZLNP8jlD/Z3r1ClARg2K1bcFLVGt6CyesfDWnO896cj4q3JNJjCCt0+a+gh+wXVpCu0x40Q+CNjlnh1aKylE8oRhC9pF8s2izQCdwASyV6+NH2e6BU1HtMXP64dC5u2nyeaWiwd3Woj14Ycyq/rnjSz/fVt+4pP66KWuSv33dkem50tX0KzDnckXFRr000EyMVbbmvMrD53/WAKTkBYPd/FRFuZGDpU9YN8rioN+ob6U1RQS5mZNZw1xA4PVLw7ojHWepGP2ttFg== ARC-Authentication-Results: i=2; mx.microsoft.com 1; spf=pass (sender ip is 63.35.35.123) smtp.rcpttodomain=sourceware.org smtp.mailfrom=arm.com; dmarc=pass (p=none sp=none pct=100) action=none header.from=arm.com; dkim=pass (signature was verified) header.d=armh.onmicrosoft.com; arc=pass (0 oda=1 ltdi=1 spf=[1, 1, smtp.mailfrom=arm.com] dmarc=[1, 1, header.from=arm.com]) Received: from AS9PR06CA0347.eurprd06.prod.outlook.com (2603:10a6:20b:466::33) by AM0PR08MB3220.eurprd08.prod.outlook.com (2603:10a6:208:57::25) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5206.24; Tue, 3 May 2022 21:56:44 +0000 Received: from AM5EUR03FT059.eop-EUR03.prod.protection.outlook.com (2603:10a6:20b:466:cafe::e2) by AS9PR06CA0347.outlook.office365.com (2603:10a6:20b:466::33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5206.13 via Frontend Transport; Tue, 3 May 2022 21:56:43 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 63.35.35.123) smtp.mailfrom=arm.com; dkim=pass (signature was verified) header.d=armh.onmicrosoft.com;dmarc=pass action=none header.from=arm.com; Received-SPF: Pass (protection.outlook.com: domain of arm.com designates 63.35.35.123 as permitted sender) receiver=protection.outlook.com; client-ip=63.35.35.123; helo=64aa7808-outbound-1.mta.getcheckrecipient.com; Received: from 64aa7808-outbound-1.mta.getcheckrecipient.com (63.35.35.123) by AM5EUR03FT059.mail.protection.outlook.com (10.152.17.193) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5227.15 via Frontend Transport; Tue, 3 May 2022 21:56:43 +0000 Received: ("Tessian outbound ac9bb5dd84f6:v118"); Tue, 03 May 2022 21:56:43 +0000 X-CheckRecipientChecked: true X-CR-MTA-CID: 7f39c859081ddce1 X-CR-MTA-TID: 64aa7808 Received: from fbc06d12bf79.1 by 64aa7808-outbound-1.mta.getcheckrecipient.com id 9A93272E-9A71-4DC3-89C7-0F3DCAE9AB7B.1; Tue, 03 May 2022 21:56:37 +0000 Received: from EUR04-DB3-obe.outbound.protection.outlook.com by 64aa7808-outbound-1.mta.getcheckrecipient.com with ESMTPS id fbc06d12bf79.1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384); Tue, 03 May 2022 21:56:37 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=USRSy7+mD81XOYWM6xVJShBMj7u/mijVWvU4HRsiWLTJozoaOax8MaRkIN++MkFhxuwI/wZqb+OTV18B/Fjrdq14XRon7MTKuilz37h5IZG6otnhu1ysCLGKfz2raj05mkDGOEhA4a0qII98x1Gg82zSz328/7WXeoaot9z+ZcNQqOhRq9tndoAblniuuNI5Ovb0mqzJMQqyuJUEQ2EZV0WMC27B03QBAz0gYW08wuty2v8BWzkL62xFaTkfkw1mvJy34TT1l/EZEsFipt8zAbLlkBMCR+jkN6Dv6Zg3aFgTCHYu9zRfyGUDYz5MjHpk7enex6ZOh47kh6I+gWYsag== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=1nExHiik/GEeK+OMRNGgAWIpDfWXME+/I7+NxH9jyEc=; b=JyOCyt9tRTLdVdCS20sv3sM+hintMoCWZx7+9iRIomvaV1GD2gvEy1ZwGU/JzqnxqttiuI4XVesVJ2OFLfNm3Q49V2vEE8X92aSRvXv2ItmZCj9r2+QPB9obgKSoLPqt0JspcWiY67QMwL/Czy4iKNLMGBjwC9/U4StrQ6xSmvsltqQ9ZXoc6dzvd9CdX9c57rv7kY9+/KgWKEN18w2f1P9nW3Q8th47oGsflmYmbOLkUMbCawc9ZvNLmtQoABM2TiWINmMEoq+WBDMI62DOGETQyZ/DIZ7aZB+e4wIz4d0hPwIFxQeQcRnfn24rWatxO5dGFYL3HvvY9zETPCblWg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 40.67.248.234) smtp.rcpttodomain=sourceware.org smtp.mailfrom=arm.com; dmarc=pass (p=none sp=none pct=100) action=none header.from=arm.com; dkim=none (message not signed); arc=none Received: from DU2PR04CA0051.eurprd04.prod.outlook.com (2603:10a6:10:234::26) by DB6PR0802MB2550.eurprd08.prod.outlook.com (2603:10a6:4:a1::20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5206.12; Tue, 3 May 2022 21:56:35 +0000 Received: from DBAEUR03FT050.eop-EUR03.prod.protection.outlook.com (2603:10a6:10:234:cafe::5) by DU2PR04CA0051.outlook.office365.com (2603:10a6:10:234::26) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5206.25 via Frontend Transport; Tue, 3 May 2022 21:56:35 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 40.67.248.234) smtp.mailfrom=arm.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=arm.com; Received-SPF: Pass (protection.outlook.com: domain of arm.com designates 40.67.248.234 as permitted sender) receiver=protection.outlook.com; client-ip=40.67.248.234; helo=nebula.arm.com; Received: from nebula.arm.com (40.67.248.234) by DBAEUR03FT050.mail.protection.outlook.com (100.127.142.250) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.20.5206.12 via Frontend Transport; Tue, 3 May 2022 21:56:35 +0000 Received: from AZ-NEU-EX04.Arm.com (10.251.24.32) by AZ-NEU-EX03.Arm.com (10.251.24.31) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2308.27; Tue, 3 May 2022 21:56:38 +0000 Received: from e129171.arm.com (10.57.0.164) by mail.arm.com (10.251.24.32) with Microsoft SMTP Server id 15.1.2308.27 via Frontend Transport; Tue, 3 May 2022 21:56:38 +0000 From: Luis Machado To: Subject: [PATCH, v4] [AArch64] MTE corefile support Date: Tue, 3 May 2022 22:56:32 +0100 Message-ID: <20220503215632.914608-1-luis.machado@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220331140343.9047-1-luis.machado@arm.com> References: <20220331140343.9047-1-luis.machado@arm.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-EOPAttributedMessage: 1 X-MS-Office365-Filtering-Correlation-Id: 76782184-86d6-47b4-8a0c-08da2d4fcfe6 X-MS-TrafficTypeDiagnostic: DB6PR0802MB2550:EE_|AM5EUR03FT059:EE_|AM0PR08MB3220:EE_ X-Microsoft-Antispam-PRVS: x-checkrecipientrouted: true NoDisclaimer: true X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam-Untrusted: BCL:0; X-Microsoft-Antispam-Message-Info-Original: FM34b1Vy8W37PYyfGvyMXNZaBUeoUQv1kC4hhCbWqcqDeWhKt0DyxClCZvKhxpduPGAU08FSikcP8R6M0RG3eZ3i+0BPclfHiNokIp8GKK3lGJwWI9L1Hnu4fgb+Phnp5auVCGs0McLJNHnuhZ5SGlHlvYqeWYValDP4/WwNUmttzgogWbktKAOVC1M9nRDW52epIqNmmqLqFeW/2GUt1S3Oc5lhdefUhAJYwHxBKkp3+IzYl285dNXl98VMry9kSK7sh6wwjmaPR5CvSTG/v+OQGgNgS1VgTRFTJKHKgXRXEaRaa54ScEMxB5wvmVI5oz6pvayra/FX71R4irmkCOFrHB0ZIHwiD5qOTuwD3TMGAY88aPamWTAY8j5NbjtUIJnZ4nqJ+f7Dvhs6z7B+FpgXbcG7tnnkbOe+ngPtKP7W2QSY0ZC1nPnS1FdRF4t7tOuyZKNXkvS84wMu74griONO5mzTyd70CXbBIpcfLxnuVH2IFtUzIdIkYiXOWxrlUQbjCNK4DRPHuy/D2iTLVM0a9KuD94rxsSMOQP+uheIxHnhFhl9ab/3PCTNCuWjTk7iSZ/dJpdxQ92Y5L/H/WcJQFwwutcVCFs0WVS66VnSoy11HnY8tQSgwL25Zj3Xw6HoE3LEPb89rDtukXo4w35hEWS5ktmRHF42Jd6R0TUsxvw7gSYk6uxfxcdCoYmOjFvTBqv0Rxw0CaKVFWgp2UU+VOXbsM2zFqnlGBs1ahK/37sMQCCmLv6+qoZBPf2L31q5gP5e0LIQnQ3estjA6UiAOeE9J6ZI9EHM1UAmRR/PJEl0RJ9SAZ5vUA0T8ZjCE X-Forefront-Antispam-Report-Untrusted: CIP:40.67.248.234; CTRY:IE; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:nebula.arm.com; PTR:InfoDomainNonexistent; CAT:NONE; SFS:(13230001)(4636009)(36840700001)(46966006)(40470700004)(81166007)(30864003)(83380400001)(47076005)(44832011)(426003)(36860700001)(336012)(356005)(26005)(36756003)(82310400005)(1076003)(8936002)(186003)(6666004)(7696005)(2616005)(5660300002)(8676002)(2906002)(316002)(508600001)(70206006)(70586007)(86362001)(6916009)(40460700003)(2004002)(36900700001)(579004); DIR:OUT; SFP:1101; X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB6PR0802MB2550 X-MS-Exchange-Transport-CrossTenantHeadersStripped: AM5EUR03FT059.eop-EUR03.prod.protection.outlook.com X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id-Prvs: f8d3eb4e-7157-4443-b98f-08da2d4fcabc X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: c4O7IC416W+OOz1yRktpVWbo7HNOQMoKbPkG92fz3Qf4rylV9ie9TRTbymPSIOS/qI3aV7G0STswnO547l9T319+Y0sa44mkjrZMaSLKJnE8lVYHHoTdG+aHkRTGKP1Ujdyo3xIqXlPC4m/d37pZtHlB8xwpC1dIs8ujA7OgUTWBcLAxN6wD7LxeOutt7Ryg56Wy2DTzUxkReHaqubCDMfp7XKouZ442rvVOekVAKaoF6fPzGa2pZQueoazX6+VZ+ZG3JSxjARppo87z6xzpuI2XyO2PT/ZVjDghi+Uhg3hibTO8u7Rfeip4hJRvf/Z13Tsv0HQ5OFvOty/LV1Qv4MB+glBjztDX4oVixN2FTFPmfXVnG5MbVC2HG2GvOrq+JI5S6VNzAj4bWdjiLn+AVLu9shbAc5NRxEYqrgi25sWewW5ZT4GdDFqzeqv5OKx548H3e/tLExbh8Ansi8ZeJvaTsr+gD9fH2wuROMVn/MJZzRhYjMuvG8vOF1IMq0njqVfEcN2ZWyywIYMPUgjVy6hpNzciZXLnmTH7txdcmBdjeBBbYa2T/B/W9Av1/kwxcu/UY8742zoxhnKke3fVff1cSzFkWuJQESymirxPDXihbROnHmmJT+edrGv2FAgbhKBr1d1nyXsp9gkcT+H0fUpvtv9niDljQT0/He0sHU6el7AGvJgiDSm1McF9ECjXHaVHryNApfSHqL4j0fyNBrdpyKvSm7gWWAub5Y6Nwrex7CW3njcnhXBqWXlhV+EY9q0uEX2a0+Zd1qGl/0Z1xQ== X-Forefront-Antispam-Report: CIP:63.35.35.123; CTRY:IE; LANG:en; SCL:1; SRV:; IPV:CAL; SFV:NSPM; H:64aa7808-outbound-1.mta.getcheckrecipient.com; PTR:ec2-63-35-35-123.eu-west-1.compute.amazonaws.com; CAT:NONE; SFS:(13230001)(4636009)(46966006)(40470700004)(36840700001)(7696005)(336012)(81166007)(426003)(83380400001)(26005)(47076005)(40460700003)(316002)(86362001)(82310400005)(186003)(30864003)(44832011)(36756003)(5660300002)(8936002)(1076003)(36860700001)(70586007)(6666004)(8676002)(6916009)(70206006)(2906002)(508600001)(2616005)(2004002)(579004); DIR:OUT; SFP:1101; X-OriginatorOrg: arm.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 03 May 2022 21:56:43.9251 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 76782184-86d6-47b4-8a0c-08da2d4fcfe6 X-MS-Exchange-CrossTenant-Id: f34e5979-57d9-4aaa-ad4d-b122a662184d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=f34e5979-57d9-4aaa-ad4d-b122a662184d; Ip=[63.35.35.123]; Helo=[64aa7808-outbound-1.mta.getcheckrecipient.com] X-MS-Exchange-CrossTenant-AuthSource: AM5EUR03FT059.eop-EUR03.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0PR08MB3220 X-Spam-Status: No, score=-13.1 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_PASS, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE, UNPARSEABLE_RELAY autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) 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: Tue, 03 May 2022 21:56:52 -0000 v4: - Updated documentation (added cross-references). - Updated the segment name from PT_ARM_MEMTAG_MTE to PT_AARCH64_MEMTAG_MTE. v3: - Updated NEWS and documentation to be more thorough. v2: - Rework memory tag section handling to use generic section fields. -- Teach GDB how to dump memory tags for AArch64 when using the gcore command and how to read memory tag data back from a core file generated by GDB (via gcore) or by the Linux kernel. The format is documented in the Linux Kernel documentation [1]. Each tagged memory range (listed in /proc//smaps) gets dumped to its own PT_AARCH64_MEMTAG_MTE segment. A section named ".memtag" is created for each of those segments when reading the core file back. To save a little bit of space, given MTE tags only take 4 bits, the memory tags are stored packed as 2 tags per byte. When reading the data back, the tags are unpacked. I've added a new testcase to exercise the feature. Build-tested with --enable-targets=all and regression tested on aarch64-linux Ubuntu 20.04. [1] Documentation/arm64/memory-tagging-extension.rst (Core Dump Support) --- gdb/Makefile.in | 1 + gdb/NEWS | 10 ++ gdb/aarch64-linux-tdep.c | 167 +++++++++++++++++++ gdb/arch/aarch64-mte-linux.c | 56 +++++++ gdb/arch/aarch64-mte-linux.h | 10 ++ gdb/corelow.c | 62 +++++++ gdb/defs.h | 3 +- gdb/doc/gdb.texinfo | 19 +++ gdb/gcore.c | 83 ++++++++- gdb/gdbarch-components.py | 35 ++++ gdb/gdbarch-gen.h | 26 +++ gdb/gdbarch.c | 96 +++++++++++ gdb/linux-tdep.c | 39 ++++- gdb/memtag.c | 61 +++++++ gdb/memtag.h | 50 ++++++ gdb/testsuite/gdb.arch/aarch64-mte-gcore.c | 93 +++++++++++ gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp | 107 ++++++++++++ 17 files changed, 910 insertions(+), 8 deletions(-) create mode 100644 gdb/memtag.c create mode 100644 gdb/memtag.h create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.c create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 418094775a5..fac9364bea4 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1120,6 +1120,7 @@ COMMON_SFILES = \ memattr.c \ memory-map.c \ memrange.c \ + memtag.c \ minidebug.c \ minsyms.c \ mipsread.c \ diff --git a/gdb/NEWS b/gdb/NEWS index 982f4a1a18c..3d925dc3663 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -3,6 +3,16 @@ *** Changes since GDB 12 +* GDB now supports dumping memory tag data for AArch64 MTE. It also supports + reading memory tag data for AArch64 MTE from core files generated by + the gcore command or the Linux kernel. + + When a process uses memory-mapped pages protected by memory tags (for + example, AArch64 MTE), this additional information will be recorded in + the core file in the event of a crash or if GDB generates a core file + from the current process state. GDB will show this additional information + automatically, or through one of the memory-tag subcommands. + * GDB now supports hardware watchpoints on FreeBSD/Aarch64. * Remove support for building against Python 2, it is now only possible to diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index 55094b3d88b..12d98e71796 100644 --- a/gdb/aarch64-linux-tdep.c +++ b/gdb/aarch64-linux-tdep.c @@ -53,6 +53,9 @@ #include "gdbsupport/selftest.h" +#include "elf/common.h" +#include "elf/aarch64.h" + /* Signal frame handling. +------------+ ^ @@ -1781,6 +1784,155 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch, } } +/* AArch64 Linux implementation of the gdbarch_create_memtag_section hook. */ + +static asection * +aarch64_linux_create_memtag_section (struct gdbarch *gdbarch, bfd *obfd, + CORE_ADDR address, size_t size) +{ + gdb_assert (obfd != nullptr); + gdb_assert (size > 0); + + /* Create the section and associated program header. */ + asection *mte_section = bfd_make_section_anyway (obfd, "memtag"); + + if (mte_section == nullptr) + return nullptr; + + bfd_set_section_vma (mte_section, address); + /* The size of the memory range covered by the memory tags. We reuse the + section's rawsize field for this purpose. */ + mte_section->rawsize = size; + /* Tags are stored packed as 2 tags per byte. */ + bfd_set_section_size (mte_section, (size / AARCH64_MTE_GRANULE_SIZE) / 2); + /* Make sure the section's flags has SEC_HAS_CONTENTS, otherwise BFD will + refuse to write data to this section. */ + bfd_set_section_flags (mte_section, SEC_HAS_CONTENTS); + + /* Store program header information. */ + bfd_record_phdr (obfd, PT_AARCH64_MEMTAG_MTE, 1, 0, 0, 0, 0, 0, 1, + &mte_section); + + return mte_section; +} + +/* Maximum number of tags to request. */ +#define MAX_TAGS_TO_TRANSFER 1024 + +/* AArch64 Linux implementation of the gdbarch_fill_memtag_section hook. */ + +static bool +aarch64_linux_fill_memtag_section (struct gdbarch *gdbarch, asection *osec) +{ + /* We only handle MTE tags for now. */ + + size_t segment_size = osec->rawsize; + CORE_ADDR start_address = bfd_section_vma (osec); + CORE_ADDR end_address = start_address + segment_size; + + /* Figure out how many tags we need to store in this memory range. */ + size_t granules = aarch64_mte_get_tag_granules (start_address, segment_size, + AARCH64_MTE_GRANULE_SIZE); + + /* If there are no tag granules to fetch, just return. */ + if (granules == 0) + return true; + + CORE_ADDR address = start_address; + + /* Vector of tags. */ + gdb::byte_vector tags; + + while (granules > 0) + { + /* Transfer tags in chunks. */ + gdb::byte_vector tags_read; + size_t xfer_len + = (granules >= MAX_TAGS_TO_TRANSFER)? + MAX_TAGS_TO_TRANSFER * AARCH64_MTE_GRANULE_SIZE : + granules * AARCH64_MTE_GRANULE_SIZE; + + if (!target_fetch_memtags (address, xfer_len, tags_read, + static_cast (memtag_type::allocation))) + { + warning (_("Failed to read MTE tags from memory range [%s,%s)."), + phex_nz (start_address, sizeof (start_address)), + phex_nz (end_address, sizeof (end_address))); + return false; + } + + /* Transfer over the tags that have been read. */ + tags.insert (tags.end (), tags_read.begin (), tags_read.end ()); + + /* Adjust the remaining granules and starting address. */ + granules -= tags_read.size (); + address += tags_read.size () * AARCH64_MTE_GRANULE_SIZE; + } + + /* Pack the MTE tag bits. */ + aarch64_mte_pack_tags (tags); + + if (!bfd_set_section_contents (osec->owner, osec, tags.data (), + 0, tags.size ())) + { + warning (_("Failed to write %s bytes of corefile memory " + "tag content (%s)."), + pulongest (tags.size ()), + bfd_errmsg (bfd_get_error ())); + } + return true; +} + +/* AArch64 Linux implementation of the gdbarch_decode_memtag_section + hook. Decode a memory tag section and return the requested tags. + + The section is guaranteed to cover the [ADDRESS, ADDRESS + length) + range. */ + +static gdb::byte_vector +aarch64_linux_decode_memtag_section (struct gdbarch *gdbarch, + bfd_section *section, + int type, + CORE_ADDR address, size_t length) +{ + gdb_assert (section != nullptr); + + /* The requested address must not be less than section->vma. */ + gdb_assert (section->vma <= address); + + /* Figure out how many tags we need to fetch in this memory range. */ + size_t granules = aarch64_mte_get_tag_granules (address, length, + AARCH64_MTE_GRANULE_SIZE); + /* Sanity check. */ + gdb_assert (granules > 0); + + /* Fetch the total number of tags in the range [VMA, address + length). */ + size_t granules_from_vma + = aarch64_mte_get_tag_granules (section->vma, + address - section->vma + length, + AARCH64_MTE_GRANULE_SIZE); + + /* Adjust the tags vector to contain the exact number of packed bytes. */ + gdb::byte_vector tags (((granules - 1) >> 1) + 1); + + /* Figure out the starting offset into the packed tags data. */ + file_ptr offset = ((granules_from_vma - granules) >> 1); + + if (!bfd_get_section_contents (section->owner, section, tags.data (), + offset, tags.size ())) + error (_("Couldn't read contents from memtag section.")); + + /* At this point, the tags are packed 2 per byte. Unpack them before + returning. */ + bool skip_first = ((granules_from_vma - granules) % 2) != 0; + aarch64_mte_unpack_tags (tags, skip_first); + + /* Resize to the exact number of tags that was requested. */ + tags.resize (granules); + + return tags; +} + static void aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { @@ -1864,6 +2016,21 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_report_signal_info (gdbarch, aarch64_linux_report_signal_info); + + /* Core file helpers. */ + + /* Core file helper to create a memory tag section for a particular + PT_LOAD segment. */ + set_gdbarch_create_memtag_section + (gdbarch, aarch64_linux_create_memtag_section); + + /* Core file helper to fill a memory tag section with tag data. */ + set_gdbarch_fill_memtag_section + (gdbarch, aarch64_linux_fill_memtag_section); + + /* Core file helper to decode a memory tag section. */ + set_gdbarch_decode_memtag_section (gdbarch, + aarch64_linux_decode_memtag_section); } /* Initialize the aarch64_linux_record_tdep. */ diff --git a/gdb/arch/aarch64-mte-linux.c b/gdb/arch/aarch64-mte-linux.c index fc7a8cc00f7..3af6f364e91 100644 --- a/gdb/arch/aarch64-mte-linux.c +++ b/gdb/arch/aarch64-mte-linux.c @@ -21,6 +21,62 @@ /* See arch/aarch64-mte-linux.h */ +void +aarch64_mte_pack_tags (gdb::byte_vector &tags) +{ + /* Nothing to pack? */ + if (tags.empty ()) + return; + + /* If the tags vector has an odd number of elements, add another + zeroed-out element to make it even. This facilitates packing. */ + if ((tags.size () % 2) != 0) + tags.emplace_back (0); + + for (int unpacked = 0, packed = 0; unpacked < tags.size (); + unpacked += 2, packed++) + tags[packed] = (tags[unpacked + 1] << 4) | tags[unpacked]; + + /* Now we have half the size. */ + tags.resize (tags.size () / 2); +} + +/* See arch/aarch64-mte-linux.h */ + +void +aarch64_mte_unpack_tags (gdb::byte_vector &tags, bool skip_first) +{ + /* Nothing to unpack? */ + if (tags.empty ()) + return; + + /* An unpacked MTE tags vector will have twice the number of elements + compared to an unpacked one. */ + gdb::byte_vector unpacked_tags (tags.size () * 2); + + int unpacked = 0, packed = 0; + + if (skip_first) + { + /* We are not interested in the first unpacked element, just discard + it. */ + unpacked_tags[unpacked] = (tags[packed] >> 4) & 0xf; + unpacked++; + packed++; + } + + for (; packed < tags.size (); unpacked += 2, packed++) + { + unpacked_tags[unpacked] = tags[packed] & 0xf; + unpacked_tags[unpacked + 1] = (tags[packed] >> 4) & 0xf; + } + + /* Update the original tags vector. */ + tags = std::move (unpacked_tags); +} + +/* See arch/aarch64-mte-linux.h */ + size_t aarch64_mte_get_tag_granules (CORE_ADDR addr, size_t len, size_t granule_size) { diff --git a/gdb/arch/aarch64-mte-linux.h b/gdb/arch/aarch64-mte-linux.h index d158926feff..8a145b447aa 100644 --- a/gdb/arch/aarch64-mte-linux.h +++ b/gdb/arch/aarch64-mte-linux.h @@ -32,6 +32,7 @@ /* We have one tag per 16 bytes of memory. */ #define AARCH64_MTE_GRANULE_SIZE 16 +#define AARCH64_MTE_TAG_BIT_SIZE 4 #define AARCH64_MTE_LOGICAL_TAG_START_BIT 56 #define AARCH64_MTE_LOGICAL_MAX_VALUE 0xf @@ -71,4 +72,13 @@ extern CORE_ADDR aarch64_mte_set_ltag (CORE_ADDR address, CORE_ADDR tag); It is always possible to get the logical tag. */ extern CORE_ADDR aarch64_mte_get_ltag (CORE_ADDR address); +/* Given a TAGS vector containing 1 MTE tag per byte, pack the data as + 2 tags per byte and resize the vector. */ +void aarch64_mte_pack_tags (gdb::byte_vector &tags); + +/* Given a TAGS vector containing 2 MTE tags per byte, unpack the data as + 1 tag per byte and resize the vector. If SKIP_FIRST is TRUE, skip the + first unpacked element. Otherwise leave it in the unpacked vector. */ +void aarch64_mte_unpack_tags (gdb::byte_vector &tags, bool skip_first); + #endif /* ARCH_AARCH64_LINUX_H */ diff --git a/gdb/corelow.c b/gdb/corelow.c index 8c33fb7ebb2..8b8994f80db 100644 --- a/gdb/corelow.c +++ b/gdb/corelow.c @@ -52,6 +52,7 @@ #include #include "gdbcmd.h" #include "xml-tdesc.h" +#include "memtag.h" #ifndef O_LARGEFILE #define O_LARGEFILE 0 @@ -101,6 +102,13 @@ class core_target final : public process_stratum_target bool info_proc (const char *, enum info_proc_what) override; + bool supports_memory_tagging () override; + + /* Core file implementation of fetch_memtags. Fetch the memory tags from + core file notes. */ + bool fetch_memtags (CORE_ADDR address, size_t len, + gdb::byte_vector &tags, int type) override; + /* A few helpers. */ /* Getter, see variable definition. */ @@ -1162,6 +1170,60 @@ core_target::info_proc (const char *args, enum info_proc_what request) return true; } +/* Implementation of the "supports_memory_tagging" target_ops method. */ + +bool +core_target::supports_memory_tagging () +{ + /* Look for memory tag sections. If they exist, that means this core file + supports memory tagging. */ + + return (bfd_get_section_by_name (core_bfd, "memtag") != nullptr); +} + +/* Implementation of the "fetch_memtags" target_ops method. */ + +bool +core_target::fetch_memtags (CORE_ADDR address, size_t len, + gdb::byte_vector &tags, int type) +{ + struct gdbarch *gdbarch = target_gdbarch (); + + /* Make sure we have a way to decode the memory tag notes. */ + if (!gdbarch_decode_memtag_section_p (gdbarch)) + error (_("gdbarch_decode_memtag_section not implemented for this " + "architecture.")); + + memtag_section_info info; + info.memtag_section = nullptr; + + while (get_next_core_memtag_section (core_bfd, info.memtag_section, + address, info)) + { + size_t adjusted_length + = (address + len < info.end_address)? len : (info.end_address - address); + + /* Decode the memory tag note and return the tags. */ + gdb::byte_vector tags_read + = gdbarch_decode_memtag_section (gdbarch, info.memtag_section, type, + address, adjusted_length); + + /* Transfer over the tags that have been read. */ + tags.insert (tags.end (), tags_read.begin (), tags_read.end ()); + + /* ADDRESS + LEN may cross the boundaries of a particular memory tag + segment. Check if we need to fetch tags from a different section. */ + if (!tags_read.empty () && (address + len) < info.end_address) + return true; + + /* There are more tags to fetch. Update ADDRESS and LEN. */ + len -= (info.end_address - address); + address = info.end_address; + } + + return false; +} + /* Get a pointer to the current core target. If not connected to a core target, return NULL. */ diff --git a/gdb/defs.h b/gdb/defs.h index 99bfdd526ff..51a7576a56a 100644 --- a/gdb/defs.h +++ b/gdb/defs.h @@ -344,7 +344,8 @@ extern const char *pc_prefix (CORE_ADDR); typedef int (*find_memory_region_ftype) (CORE_ADDR addr, unsigned long size, int read, int write, int exec, - int modified, void *data); + int modified, bool memory_tagged, + void *data); /* * Possible lvalue types. Like enum language, this should be in value.h, but needs to be here for the same reason. */ diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 38ad2ac32b0..36f10f20cfb 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -25555,6 +25555,25 @@ options that can be controlled at runtime and emulates the @code{prctl} option @code{PR_SET_TAGGED_ADDR_CTRL}. For further information, see the documentation in the Linux kernel. +@value{GDBN} supports dumping memory tag data to core files through the +@command{gcore} command and reading memory tag data from core files generated +by the @command{gcore} command or the Linux kernel. + +When a process uses memory-mapped pages protected by memory tags (for +example, AArch64 MTE), this additional information will be recorded in +the core file in the event of a crash or if @value{GDBN} generates a core file +from the current process state. + +The memory tag data will be used so developers can display the memory +tags from a particular memory region (using the @samp{m} modifier to the +@command{x} command, using the @command{print} command or using the various +@command{memory-tag} subcommands. + +In the case of a crash, @value{GDBN} will attempt to retrieve the memory tag +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}. + @node i386 @subsection x86 Architecture-specific Issues diff --git a/gdb/gcore.c b/gdb/gcore.c index fdb22b72a07..b81ef81ab84 100644 --- a/gdb/gcore.c +++ b/gdb/gcore.c @@ -349,6 +349,12 @@ make_output_phdrs (bfd *obfd, asection *osec) int p_flags = 0; int p_type = 0; + /* Memory tag segments have already been handled by the architecture, as + those contain arch-specific information. If we have one of those, just + return. */ + if (startswith (bfd_section_name (osec), "memtag")) + return; + /* FIXME: these constants may only be applicable for ELF. */ if (startswith (bfd_section_name (osec), "load")) p_type = PT_LOAD; @@ -371,7 +377,8 @@ make_output_phdrs (bfd *obfd, asection *osec) static int gcore_create_callback (CORE_ADDR vaddr, unsigned long size, int read, - int write, int exec, int modified, void *data) + int write, int exec, int modified, bool memory_tagged, + void *data) { bfd *obfd = (bfd *) data; asection *osec; @@ -454,6 +461,45 @@ gcore_create_callback (CORE_ADDR vaddr, unsigned long size, int read, return 0; } +/* gdbarch_find_memory_region callback for creating a memory tag section. + DATA is 'bfd *' for the core file GDB is creating. */ + +static int +gcore_create_memtag_section_callback (CORE_ADDR vaddr, unsigned long size, + int read, int write, int exec, + int modified, bool memory_tagged, + void *data) +{ + /* Are there memory tags in this particular memory map entry? */ + if (!memory_tagged) + return 0; + + bfd *obfd = (bfd *) data; + + /* Ask the architecture to create a memory tag section for this particular + memory map entry. It will be populated with contents later, as we can't + start writing the contents before we have all the sections sorted out. */ + asection *memtag_section + = gdbarch_create_memtag_section (target_gdbarch (), obfd, vaddr, size); + + if (memtag_section == nullptr) + { + warning (_("Couldn't make gcore memory tag segment: %s"), + bfd_errmsg (bfd_get_error ())); + return 1; + } + + if (info_verbose) + { + gdb_printf (gdb_stdout, "Saved memory tag segment, %s bytes " + "at %s\n", + plongest (bfd_section_size (memtag_section)), + paddress (target_gdbarch (), vaddr)); + } + + return 0; +} + int objfile_find_memory_regions (struct target_ops *self, find_memory_region_ftype func, void *obfd) @@ -483,6 +529,7 @@ objfile_find_memory_regions (struct target_ops *self, (flags & SEC_READONLY) == 0, /* Writable. */ (flags & SEC_CODE) != 0, /* Executable. */ 1, /* MODIFIED is unknown, pass it as true. */ + false, /* No memory tags in the object file. */ obfd); if (ret != 0) return ret; @@ -496,6 +543,7 @@ objfile_find_memory_regions (struct target_ops *self, 1, /* Stack section will be writable. */ 0, /* Stack section will not be executable. */ 1, /* Stack section will be modified. */ + false, /* No memory tags in the object file. */ obfd); /* Make a heap segment. */ @@ -506,6 +554,7 @@ objfile_find_memory_regions (struct target_ops *self, 1, /* Heap section will be writable. */ 0, /* Heap section will not be executable. */ 1, /* Heap section will be modified. */ + false, /* No memory tags in the object file. */ obfd); return 0; @@ -555,6 +604,20 @@ gcore_copy_callback (bfd *obfd, asection *osec) } } +/* Callback to copy contents to a particular memory tag section. */ + +static void +gcore_copy_memtag_section_callback (bfd *obfd, asection *osec) +{ + /* We are only interested in "memtag" sections. */ + if (!startswith (bfd_section_name (osec), "memtag")) + return; + + /* Fill the section with memory tag contents. */ + if (!gdbarch_fill_memtag_section (target_gdbarch (), osec)) + error (_("Failed to fill memory tag section for core file.")); +} + static int gcore_memory_sections (bfd *obfd) { @@ -567,13 +630,27 @@ gcore_memory_sections (bfd *obfd) return 0; /* FIXME: error return/msg? */ } + /* Take care of dumping memory tags, if there are any. */ + if (!gdbarch_find_memory_regions_p (target_gdbarch ()) + || gdbarch_find_memory_regions (target_gdbarch (), + gcore_create_memtag_section_callback, + obfd) != 0) + { + if (target_find_memory_regions (gcore_create_memtag_section_callback, + obfd) != 0) + return 0; + } + /* Record phdrs for section-to-segment mapping. */ for (asection *sect : gdb_bfd_sections (obfd)) make_output_phdrs (obfd, sect); - /* Copy memory region contents. */ + /* Copy memory region and memory tag contents. */ for (asection *sect : gdb_bfd_sections (obfd)) - gcore_copy_callback (obfd, sect); + { + gcore_copy_callback (obfd, sect); + gcore_copy_memtag_section_callback (obfd, sect); + } return 1; } diff --git a/gdb/gdbarch-components.py b/gdb/gdbarch-components.py index e8f20c83ff0..6fa1b7591db 100644 --- a/gdb/gdbarch-components.py +++ b/gdb/gdbarch-components.py @@ -1522,6 +1522,41 @@ Find core file memory regions invalid=True, ) +Method( + comment=""" +Given a bfd OBFD, segment ADDRESS and SIZE, create a memory tag section to be dumped to a core file +""", + type="asection *", + name="create_memtag_section", + params=[("bfd *", "obfd"), ("CORE_ADDR", "address"), ("size_t", "size")], + predicate=True, + invalid=True, +) + +Method( + comment=""" +Given a memory tag section OSEC, fill OSEC's contents with the appropriate tag data +""", + type="bool", + name="fill_memtag_section", + params=[("asection *", "osec")], + predicate=True, + invalid=True, +) + +Method( + comment=""" +Decode a memory tag SECTION and return the tags of type TYPE contained in +the memory range [ADDRESS, ADDRESS + LENGTH). +If no tags were found, return an empty vector. +""", + type="gdb::byte_vector", + name="decode_memtag_section", + params=[("bfd_section *", "section"), ("int", "type"), ("CORE_ADDR", "address"), ("size_t", "length")], + predicate=True, + invalid=True, +) + Method( comment=""" Read offset OFFSET of TARGET_OBJECT_LIBRARIES formatted shared libraries list from diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h index 882b9057b1a..1d19f51f21d 100644 --- a/gdb/gdbarch-gen.h +++ b/gdb/gdbarch-gen.h @@ -874,6 +874,32 @@ typedef int (gdbarch_find_memory_regions_ftype) (struct gdbarch *gdbarch, find_m extern int gdbarch_find_memory_regions (struct gdbarch *gdbarch, find_memory_region_ftype func, void *data); extern void set_gdbarch_find_memory_regions (struct gdbarch *gdbarch, gdbarch_find_memory_regions_ftype *find_memory_regions); +/* Given a bfd OBFD, segment ADDRESS and SIZE, create a memory tag section to be dumped to a core file */ + +extern bool gdbarch_create_memtag_section_p (struct gdbarch *gdbarch); + +typedef asection * (gdbarch_create_memtag_section_ftype) (struct gdbarch *gdbarch, bfd *obfd, CORE_ADDR address, size_t size); +extern asection * gdbarch_create_memtag_section (struct gdbarch *gdbarch, bfd *obfd, CORE_ADDR address, size_t size); +extern void set_gdbarch_create_memtag_section (struct gdbarch *gdbarch, gdbarch_create_memtag_section_ftype *create_memtag_section); + +/* Given a memory tag section OSEC, fill OSEC's contents with the appropriate tag data */ + +extern bool gdbarch_fill_memtag_section_p (struct gdbarch *gdbarch); + +typedef bool (gdbarch_fill_memtag_section_ftype) (struct gdbarch *gdbarch, asection *osec); +extern bool gdbarch_fill_memtag_section (struct gdbarch *gdbarch, asection *osec); +extern void set_gdbarch_fill_memtag_section (struct gdbarch *gdbarch, gdbarch_fill_memtag_section_ftype *fill_memtag_section); + +/* Decode a memory tag SECTION and return the tags of type TYPE contained in + the memory range [ADDRESS, ADDRESS + LENGTH). + If no tags were found, return an empty vector. */ + +extern bool gdbarch_decode_memtag_section_p (struct gdbarch *gdbarch); + +typedef gdb::byte_vector (gdbarch_decode_memtag_section_ftype) (struct gdbarch *gdbarch, bfd_section *section, int type, CORE_ADDR address, size_t length); +extern gdb::byte_vector gdbarch_decode_memtag_section (struct gdbarch *gdbarch, bfd_section *section, int type, CORE_ADDR address, size_t length); +extern void set_gdbarch_decode_memtag_section (struct gdbarch *gdbarch, gdbarch_decode_memtag_section_ftype *decode_memtag_section); + /* Read offset OFFSET of TARGET_OBJECT_LIBRARIES formatted shared libraries list from core file into buffer READBUF with length LEN. Return the number of bytes read (zero indicates failure). diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index a588bdef61a..f5dbacb14e7 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -171,6 +171,9 @@ struct gdbarch gdbarch_iterate_over_regset_sections_ftype *iterate_over_regset_sections; gdbarch_make_corefile_notes_ftype *make_corefile_notes; gdbarch_find_memory_regions_ftype *find_memory_regions; + gdbarch_create_memtag_section_ftype *create_memtag_section; + gdbarch_fill_memtag_section_ftype *fill_memtag_section; + gdbarch_decode_memtag_section_ftype *decode_memtag_section; gdbarch_core_xfer_shared_libraries_ftype *core_xfer_shared_libraries; gdbarch_core_xfer_shared_libraries_aix_ftype *core_xfer_shared_libraries_aix; gdbarch_core_pid_to_str_ftype *core_pid_to_str; @@ -527,6 +530,9 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of iterate_over_regset_sections, has predicate. */ /* Skip verify of make_corefile_notes, has predicate. */ /* Skip verify of find_memory_regions, has predicate. */ + /* Skip verify of create_memtag_section, has predicate. */ + /* Skip verify of fill_memtag_section, has predicate. */ + /* Skip verify of decode_memtag_section, has predicate. */ /* Skip verify of core_xfer_shared_libraries, has predicate. */ /* Skip verify of core_xfer_shared_libraries_aix, has predicate. */ /* Skip verify of core_pid_to_str, has predicate. */ @@ -1096,6 +1102,24 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) gdb_printf (file, "gdbarch_dump: find_memory_regions = <%s>\n", host_address_to_string (gdbarch->find_memory_regions)); + gdb_printf (file, + "gdbarch_dump: gdbarch_create_memtag_section_p() = %d\n", + gdbarch_create_memtag_section_p (gdbarch)); + gdb_printf (file, + "gdbarch_dump: create_memtag_section = <%s>\n", + host_address_to_string (gdbarch->create_memtag_section)); + gdb_printf (file, + "gdbarch_dump: gdbarch_fill_memtag_section_p() = %d\n", + gdbarch_fill_memtag_section_p (gdbarch)); + gdb_printf (file, + "gdbarch_dump: fill_memtag_section = <%s>\n", + host_address_to_string (gdbarch->fill_memtag_section)); + gdb_printf (file, + "gdbarch_dump: gdbarch_decode_memtag_section_p() = %d\n", + gdbarch_decode_memtag_section_p (gdbarch)); + gdb_printf (file, + "gdbarch_dump: decode_memtag_section = <%s>\n", + host_address_to_string (gdbarch->decode_memtag_section)); gdb_printf (file, "gdbarch_dump: gdbarch_core_xfer_shared_libraries_p() = %d\n", gdbarch_core_xfer_shared_libraries_p (gdbarch)); @@ -3744,6 +3768,78 @@ set_gdbarch_find_memory_regions (struct gdbarch *gdbarch, gdbarch->find_memory_regions = find_memory_regions; } +bool +gdbarch_create_memtag_section_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->create_memtag_section != NULL; +} + +asection * +gdbarch_create_memtag_section (struct gdbarch *gdbarch, bfd *obfd, CORE_ADDR address, size_t size) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->create_memtag_section != NULL); + if (gdbarch_debug >= 2) + gdb_printf (gdb_stdlog, "gdbarch_create_memtag_section called\n"); + return gdbarch->create_memtag_section (gdbarch, obfd, address, size); +} + +void +set_gdbarch_create_memtag_section (struct gdbarch *gdbarch, + gdbarch_create_memtag_section_ftype create_memtag_section) +{ + gdbarch->create_memtag_section = create_memtag_section; +} + +bool +gdbarch_fill_memtag_section_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->fill_memtag_section != NULL; +} + +bool +gdbarch_fill_memtag_section (struct gdbarch *gdbarch, asection *osec) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->fill_memtag_section != NULL); + if (gdbarch_debug >= 2) + gdb_printf (gdb_stdlog, "gdbarch_fill_memtag_section called\n"); + return gdbarch->fill_memtag_section (gdbarch, osec); +} + +void +set_gdbarch_fill_memtag_section (struct gdbarch *gdbarch, + gdbarch_fill_memtag_section_ftype fill_memtag_section) +{ + gdbarch->fill_memtag_section = fill_memtag_section; +} + +bool +gdbarch_decode_memtag_section_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->decode_memtag_section != NULL; +} + +gdb::byte_vector +gdbarch_decode_memtag_section (struct gdbarch *gdbarch, bfd_section *section, int type, CORE_ADDR address, size_t length) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->decode_memtag_section != NULL); + if (gdbarch_debug >= 2) + gdb_printf (gdb_stdlog, "gdbarch_decode_memtag_section called\n"); + return gdbarch->decode_memtag_section (gdbarch, section, type, address, length); +} + +void +set_gdbarch_decode_memtag_section (struct gdbarch *gdbarch, + gdbarch_decode_memtag_section_ftype decode_memtag_section) +{ + gdbarch->decode_memtag_section = decode_memtag_section; +} + bool gdbarch_core_xfer_shared_libraries_p (struct gdbarch *gdbarch) { diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index 4e728a06e7e..8a83ed320cf 100644 --- a/gdb/linux-tdep.c +++ b/gdb/linux-tdep.c @@ -42,6 +42,7 @@ #include "gcore.h" #include "gcore-elf.h" #include "solib-svr4.h" +#include "memtag.h" #include #include @@ -1320,6 +1321,7 @@ typedef int linux_find_memory_region_ftype (ULONGEST vaddr, ULONGEST size, ULONGEST offset, ULONGEST inode, int read, int write, int exec, int modified, + bool memory_tagged, const char *filename, void *data); @@ -1470,10 +1472,11 @@ parse_smaps_data (const char *data, return smaps; } -/* See linux-tdep.h. */ +/* Helper that checks if an address is in a memory tag page for a live + process. */ -bool -linux_address_in_memtag_page (CORE_ADDR address) +static bool +linux_process_address_in_memtag_page (CORE_ADDR address) { if (current_inferior ()->fake_pid_p) return false; @@ -1505,6 +1508,30 @@ linux_address_in_memtag_page (CORE_ADDR address) return false; } +/* Helper that checks if an address is in a memory tag page for a core file + process. */ + +static bool +linux_core_file_address_in_memtag_page (CORE_ADDR address) +{ + if (core_bfd == nullptr) + return false; + + memtag_section_info info; + return get_next_core_memtag_section (core_bfd, nullptr, address, info); +} + +/* See linux-tdep.h. */ + +bool +linux_address_in_memtag_page (CORE_ADDR address) +{ + if (!target_has_execution ()) + return linux_core_file_address_in_memtag_page (address); + + return linux_process_address_in_memtag_page (address); +} + /* List memory regions in the inferior for a corefile. */ static int @@ -1593,6 +1620,7 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch, map.offset, map.inode, map.read, map.write, map.exec, 1, /* MODIFIED is true because we want to dump the mapping. */ + map.vmflags.memory_tagging != 0, map.filename.c_str (), obfd); } } @@ -1621,12 +1649,14 @@ static int linux_find_memory_regions_thunk (ULONGEST vaddr, ULONGEST size, ULONGEST offset, ULONGEST inode, int read, int write, int exec, int modified, + bool memory_tagged, const char *filename, void *arg) { struct linux_find_memory_regions_data *data = (struct linux_find_memory_regions_data *) arg; - return data->func (vaddr, size, read, write, exec, modified, data->obfd); + return data->func (vaddr, size, read, write, exec, modified, memory_tagged, + data->obfd); } /* A variant of linux_find_memory_regions_full that is suitable as the @@ -1675,6 +1705,7 @@ static int linux_make_mappings_callback (ULONGEST vaddr, ULONGEST size, ULONGEST offset, ULONGEST inode, int read, int write, int exec, int modified, + bool memory_tagged, const char *filename, void *data) { struct linux_make_mappings_data *map_data diff --git a/gdb/memtag.c b/gdb/memtag.c new file mode 100644 index 00000000000..af86137c49d --- /dev/null +++ b/gdb/memtag.c @@ -0,0 +1,61 @@ +/* GDB generic memory tagging functions. + + Copyright (C) 2022 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 . */ + +#include "defs.h" +#include "memtag.h" +#include "bfd.h" + +/* See memtag.h */ + +bool +get_next_core_memtag_section (bfd *abfd, asection *section, + CORE_ADDR address, memtag_section_info &info) +{ + /* If the caller provided no SECTION to start from, search from the + beginning. */ + if (section == nullptr) + section = bfd_get_section_by_name (abfd, "memtag"); + + /* Go through all the memtag sections and figure out if ADDRESS + falls within one of the memory ranges that contain tags. */ + while (section != nullptr) + { + size_t memtag_range_size = section->rawsize; + size_t tags_size = bfd_section_size (section); + + /* Empty memory range and empty tag dump should not happen. */ + gdb_assert (memtag_range_size != 0); + gdb_assert (tags_size != 0); + + CORE_ADDR start_address = bfd_section_vma (section); + CORE_ADDR end_address = start_address + memtag_range_size; + + /* Is the address within [start_address, end_address)? */ + if (address >= start_address + && address < end_address) + { + info.start_address = start_address; + info.end_address = end_address; + info.memtag_section = section; + return true; + } + section = bfd_get_next_section_by_name (abfd, section); + } + return false; +} diff --git a/gdb/memtag.h b/gdb/memtag.h new file mode 100644 index 00000000000..fe908c1e5e3 --- /dev/null +++ b/gdb/memtag.h @@ -0,0 +1,50 @@ +/* GDB generic memory tagging definitions. + Copyright (C) 2022 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 MEMTAG_H +#define MEMTAG_H + +#include "bfd.h" + +struct memtag_section_info +{ + /* The start address of the tagged memory range. */ + CORE_ADDR start_address; + /* The final address of the tagged memory range. */ + CORE_ADDR end_address; + /* The section containing tags for the memory range + [start_address, end_address). */ + asection *memtag_section; +}; + +/* Helper function to walk through memory tag sections in a core file. + + Return TRUE if there is a "memtag" section containing ADDRESS. Return FALSE + otherwise. + + If SECTION is provided, search from that section onwards. If SECTION is + nullptr, then start a new search. + + If a "memtag" section containing ADDRESS is found, fill INFO with data + about such section. Otherwise leave it unchanged. */ + +bool get_next_core_memtag_section (bfd *abfd, asection *section, + CORE_ADDR address, + memtag_section_info &info); + +#endif /* MEMTAG_H */ diff --git a/gdb/testsuite/gdb.arch/aarch64-mte-gcore.c b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.c new file mode 100644 index 00000000000..b20ebcff424 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.c @@ -0,0 +1,93 @@ +/* This test program is part of GDB, the GNU debugger. + + Copyright 2021 Free Software Foundation, Inc. + + 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 . */ + +/* Exercise AArch64's Memory Tagging Extension with tagged pointers. */ + +/* This test was based on the documentation for the AArch64 Memory Tagging + Extension from the Linux Kernel, found in the sources in + Documentation/arm64/memory-tagging-extension.rst. */ + +#include +#include +#include +#include +#include +#include +#include + +/* From arch/arm64/include/uapi/asm/hwcap.h */ +#define HWCAP2_MTE (1 << 18) + +/* From arch/arm64/include/uapi/asm/mman.h */ +#define PROT_MTE 0x20 + +/* From include/uapi/linux/prctl.h */ +#define PR_SET_TAGGED_ADDR_CTRL 55 +#define PR_GET_TAGGED_ADDR_CTRL 56 +#define PR_TAGGED_ADDR_ENABLE (1UL << 0) +#define PR_MTE_TCF_SHIFT 1 +#define PR_MTE_TCF_SYNC (1UL << PR_MTE_TCF_SHIFT) +#define PR_MTE_TAG_SHIFT 3 + +void +access_memory (unsigned char *tagged_ptr) +{ + tagged_ptr[0] = 'a'; +} + +int +main (int argc, char **argv) +{ + unsigned char *tagged_ptr; + unsigned long page_sz = sysconf (_SC_PAGESIZE); + unsigned long hwcap2 = getauxval(AT_HWCAP2); + + /* Bail out if MTE is not supported. */ + if (!(hwcap2 & HWCAP2_MTE)) + return 1; + + /* Enable the tagged address ABI, synchronous MTE tag check faults and + allow all non-zero tags in the randomly generated set. */ + if (prctl (PR_SET_TAGGED_ADDR_CTRL, + PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC + | (0xfffe << PR_MTE_TAG_SHIFT), + 0, 0, 0)) + { + perror ("prctl () failed"); + return 1; + } + + /* Create a mapping that will have PROT_MTE set. */ + tagged_ptr = mmap (0, page_sz, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (tagged_ptr == MAP_FAILED) + { + perror ("mmap () failed"); + return 1; + } + + /* Enable MTE on the above anonymous mmap. */ + if (mprotect (tagged_ptr, page_sz, PROT_READ | PROT_WRITE | PROT_MTE)) + { + perror ("mprotect () failed"); + return 1; + } + + access_memory (tagged_ptr); + + return 0; +} diff --git a/gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp new file mode 100644 index 00000000000..8a19c4b449e --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp @@ -0,0 +1,107 @@ +# Copyright (C) 2018-2021 Free Software Foundation, Inc. +# +# 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 is part of the gdb testsuite. + +# Test generating and reading a core file with MTE memory tags. + +if {![is_aarch64_target]} { + verbose "Skipping ${gdb_test_file_name}." + return +} + +standard_testfile +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return -1 +} + +if ![runto_main] { + untested "could not run to main" + return -1 +} + +# Targets that don't support memory tagging should not execute the +# runtime memory tagging tests. +if {![supports_memtag]} { + unsupported "memory tagging unsupported" + return -1 +} + +gdb_breakpoint "access_memory" + +if [gdb_continue "access_memory"] { + return -1 +} + +# Set each tag granule to a different tag value, from 0x0 to 0xf. +set atag_msg "Allocation tag\\(s\\) updated successfully\." +for {set i 15} {$i >= 0} {incr i -1} { + set index [expr [expr 15 - $i] * 16] + set tag [format "%02x" $i] + gdb_test "memory-tag set-allocation-tag &tagged_ptr\[$index\] 1 $tag" \ + $atag_msg \ + "set memory tag of &tagged_ptr\[$index\] to $tag" +} + +# Run until a crash and confirm GDB displays memory tag violation +# information. +gdb_test "continue" \ + [multi_line \ + "Program received signal SIGSEGV, Segmentation fault" \ + "Memory tag violation while accessing address $hex" \ + "Allocation tag $hex" \ + "Logical tag $hex\." \ + "$hex in access_memory \\(.*\\) at .*" \ + ".*tagged_ptr\\\[0\\\] = 'a';"] \ + "display tag violation information for live process" + +# Generate the core file. +set core_filename [standard_output_file "$testfile.core"] +set core_generated [gdb_gcore_cmd "$core_filename" "generate core file"] + +if { !$core_generated } { + return -1 +} + +clean_restart $binfile + +# Load the core file and make sure we see the tag violation fault +# information. +gdb_test "core $core_filename" \ + [multi_line \ + "Core was generated by.*\." \ + "Program terminated with signal SIGSEGV, Segmentation fault" \ + "Memory tag violation while accessing address $hex" \ + "Allocation tag 0xf" \ + "Logical tag 0x0\." \ + "#0.*$hex in access_memory \\(.*\\) at .*" \ + ".*tagged_ptr\\\[0\\\] = 'a';"] \ + "core file shows tag violation information" + +# Make sure we have the tag_ctl register. +gdb_test "info register tag_ctl" \ + "tag_ctl.*$hex.*${::decimal}" \ + "tag_ctl is available" + +# Check if the tag granules have the expected values. If they do, that +# means the core file saved the tags properly and GDB has read them +# correctly. +for {set i 15} {$i >= 0} {incr i -1} { + set index [expr [expr 15 - $i] * 16] + set tag [format "%x" $i] + gdb_test "memory-tag print-allocation-tag &tagged_ptr\[$index\]" \ + "= 0x$tag" \ + "memory tag of &tagged_ptr\[$index\] is correct" +} -- 2.25.1