From patchwork Thu May 23 03:51:22 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: 90710 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 73EF23849AE9 for ; Thu, 23 May 2024 03:52:09 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from mail-pj1-x1031.google.com (mail-pj1-x1031.google.com [IPv6:2607:f8b0:4864:20::1031]) by sourceware.org (Postfix) with ESMTPS id 84A653865488 for ; Thu, 23 May 2024 03:51:32 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 84A653865488 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 84A653865488 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::1031 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1716436297; cv=none; b=BKSemsD4F3HVec8qpxEUP/3qeIDJ7I9yRB6g2UgaEKj71BGZabhlpuqvPDGSxEY2ASGn16X8nQteANORNKlmcHFcgRtUIFmUAqEjuN1uxdUmSCuH57ulFxe2AmuEVJju6N4aMTZrliXeIh4n1AHIQPCQpjApOAKZ2ztFwcgqOgQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1716436297; c=relaxed/simple; bh=cVTTJfOU9D1WeYgi6uAF8Q3dSASG8wBIk15+CexUe78=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=wgqzI956PI1lmh66cUx+lyoHhPGU8ckxmP4v3rL4MrEGevpMwvTAmBT8adXp/fj9RrRUj4xrmrdfvSKy43r++sOBYV+QUUNXaSl7G0mLgjKXsYmg+1ydlb15QMaUmTheHVrFyV639zepqmhzl61K6HiuhO0Lyb1yvTxmnT2LuIk= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pj1-x1031.google.com with SMTP id 98e67ed59e1d1-2bd92c36cebso1580941a91.0 for ; Wed, 22 May 2024 20:51:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1716436291; x=1717041091; darn=sourceware.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=RBoHwSgF8xYLjJcEc+G4G7bkLW7C3uZr8zNFMvEEXnU=; b=PG3kfT3awlWbK7nNSdsz7/hpgssnROIzcKOV9bJg0FBgDDc8Ec8KUqI0h3olngt5q6 TglVVRwxnVj3qvI70ocq+0X8XOAcv9BAQrlBCySgMKQ1HG5fOW+n3NavAQPE0FdQbt9D /QTTm/30p4lMhm3rxemrEdwnMv9J5g6Jj4cNzbWrzF9ULHSzkhaMvmHJ2E1j88UTT8Oc rtgcKTiZM3hjSPFspnL1LlW4N2CTOYiLJVEDRs8WHcINtJ7XMuu1AN8PVR278cp8wZEM BB+t15f/7qIz6rdXSKEpFYGM9GTunBlRxoJOnxBF5I+bvUZDqPCNgYXR6YAIWM8QsEII r0ig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1716436291; x=1717041091; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=RBoHwSgF8xYLjJcEc+G4G7bkLW7C3uZr8zNFMvEEXnU=; b=iVlqET7mHna69z84hXyFdM7Gu42Zaweoj81I/pAVXk/YAs718pyTr6RsS+E+c/kkqQ 2OpkxhkMBwdbWEucpG5MKVFbmANeILdZ7RU0xowHbd2ECQ2CrXPTyRJQ8W2ZnTqTUXi6 2aTzq6o+o61AXUU/AEot+EtZnMnuPa2m5jq8LHGab1UopYGHTJ5cTMTGo+WiCVAUGfc3 qlpNcoyEghRbkNsodNAoNiaNnyreJsa0WQfuBzf4b/PIBWaz5UenDK8l1gcuCrs5RJfS 8Mv/TBGXt+HS4czvScble/brTgJ6VYNdR1iEXNfxKk92rp78l6NCDpmdGjwflny9xylg mcKw== X-Gm-Message-State: AOJu0Yx2A1K8wY/T8P8CwRbB/fL/NZYcOhZ+xBj+901x66U4XVhrPBx2 M4GpRxWcgid4A/IFJ2verTiKFL0E431VslcVwqGLqtYpC87UWziFbkTrSo5nHVSeV2Cd90yOnHO d X-Google-Smtp-Source: AGHT+IGZHmJomf+q4Bl5eOp1PeVNwxP4BH4Z6aHW9i/JNNCqtMNVkGXO2XB1N1z8grMS2zanlRnW6Q== X-Received: by 2002:a17:90a:a617:b0:2b9:d6cd:ff63 with SMTP id 98e67ed59e1d1-2bd9f454a03mr4052459a91.8.1716436291312; Wed, 22 May 2024 20:51:31 -0700 (PDT) Received: from localhost ([2804:14d:7e39:8470:f149:d562:aa25:4733]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2bdd9f4dfbfsm562537a91.37.2024.05.22.20.51.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 22 May 2024 20:51:30 -0700 (PDT) From: Thiago Jung Bauermann To: gdb-patches@sourceware.org Cc: Christophe Lyon , Luis Machado , Guinevere Larsen , Pedro Alves Subject: [PATCH v4 1/3] gdb/aarch64: Disable displaced single-step for MOPS instructions Date: Thu, 23 May 2024 00:51:22 -0300 Message-ID: <20240523035124.2639220-2-thiago.bauermann@linaro.org> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20240523035124.2639220-1-thiago.bauermann@linaro.org> References: <20240523035124.2639220-1-thiago.bauermann@linaro.org> MIME-Version: 1.0 X-Spam-Status: No, score=-10.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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+patchwork=sourceware.org@sourceware.org The AArch64 MOPS (Memory Operation) instructions provide a standardised instruction sequence to perform a memset, memcpy or memmove. A sequence is always composed of three instructions: a prologue instruction, a main instruction and an epilogue instruction. As an illustration, here are the implementations of these memory operations in glibc 2.39: (gdb) disassemble/r Dump of assembler code for function __memset_mops: => 0x0000fffff7e8d780 <+0>: d503201f nop 0x0000fffff7e8d784 <+4>: aa0003e3 mov x3, x0 0x0000fffff7e8d788 <+8>: 19c10443 setp [x3]!, x2!, x1 0x0000fffff7e8d78c <+12>: 19c14443 setm [x3]!, x2!, x1 0x0000fffff7e8d790 <+16>: 19c18443 sete [x3]!, x2!, x1 0x0000fffff7e8d794 <+20>: d65f03c0 ret End of assembler dump. (gdb) disassemble/r Dump of assembler code for function __memcpy_mops: => 0x0000fffff7e8c580 <+0>: d503201f nop 0x0000fffff7e8c584 <+4>: aa0003e3 mov x3, x0 0x0000fffff7e8c588 <+8>: 19010443 cpyfp [x3]!, [x1]!, x2! 0x0000fffff7e8c58c <+12>: 19410443 cpyfm [x3]!, [x1]!, x2! 0x0000fffff7e8c590 <+16>: 19810443 cpyfe [x3]!, [x1]!, x2! 0x0000fffff7e8c594 <+20>: d65f03c0 ret End of assembler dump. (gdb) disassemble/r Dump of assembler code for function __memmove_mops: => 0x0000fffff7e8d180 <+0>: d503201f nop 0x0000fffff7e8d184 <+4>: aa0003e3 mov x3, x0 0x0000fffff7e8d188 <+8>: 1d010443 cpyp [x3]!, [x1]!, x2! 0x0000fffff7e8d18c <+12>: 1d410443 cpym [x3]!, [x1]!, x2! 0x0000fffff7e8d190 <+16>: 1d810443 cpye [x3]!, [x1]!, x2! 0x0000fffff7e8d194 <+20>: d65f03c0 ret End of assembler dump. The Arm Architecture Reference Manual says that "the prologue, main, and epilogue instructions are expected to be run in succession and to appear consecutively in memory". Therefore this patch disables displaced stepping on them. The testcase verifies that MOPS sequences are correctly single-stepped. PR tdep/31666 Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31666 --- Changes in v4: - Fix typo in comment (Spotted by Luis). - Moved procedures is_at_instruction and arrive_at_instruction to lib/gdb.exp so that they can be used by gdb.reverse/aarch64-mops.exp. - Moved procedure allow_aarch64_mops_tests to this patch. Change in v3: - Remove aarch64_software_single_step_mops function and the change to call it from aarch64_software_single_step, since Luis clarified that it is in fact possible to single step through MOPS sequences. No change in v2. gdb/aarch64-tdep.c | 8 +- .../gdb.arch/aarch64-mops-single-step.c | 73 ++++++++++++++ .../gdb.arch/aarch64-mops-single-step.exp | 98 ++++++++++++++++++ gdb/testsuite/lib/gdb.exp | 99 +++++++++++++++++++ 4 files changed, 275 insertions(+), 3 deletions(-) create mode 100644 gdb/testsuite/gdb.arch/aarch64-mops-single-step.c create mode 100644 gdb/testsuite/gdb.arch/aarch64-mops-single-step.exp diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index 8d0553f3d7cd..05ecd421cd0e 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -3808,10 +3808,12 @@ aarch64_displaced_step_copy_insn (struct gdbarch *gdbarch, if (aarch64_decode_insn (insn, &inst, 1, NULL) != 0) return NULL; - /* Look for a Load Exclusive instruction which begins the sequence. */ - if (inst.opcode->iclass == ldstexcl && bit (insn, 22)) + /* Look for a Load Exclusive instruction which begins the sequence, + or for a MOPS instruction. */ + if ((inst.opcode->iclass == ldstexcl && bit (insn, 22)) + || AARCH64_CPU_HAS_FEATURE (*inst.opcode->avariant, MOPS)) { - /* We can't displaced step atomic sequences. */ + /* We can't displaced step atomic sequences nor MOPS instructions. */ return NULL; } diff --git a/gdb/testsuite/gdb.arch/aarch64-mops-single-step.c b/gdb/testsuite/gdb.arch/aarch64-mops-single-step.c new file mode 100644 index 000000000000..4a27867d4b57 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-mops-single-step.c @@ -0,0 +1,73 @@ +/* This file is part of GDB, the GNU debugger. + + Copyright 2024 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 . */ + +#define TEST_STRING "Just a test string." +#define BUF_SIZE sizeof(TEST_STRING) + +int +main (void) +{ + char source[BUF_SIZE] = TEST_STRING; + char dest[BUF_SIZE]; + char *p, *q; + long size, zero; + + /* Note: The prfm instruction in the asm statements below is there just + to allow the testcase to recognize when the PC is at the instruction + right after the MOPS sequence. */ + + p = dest; + size = sizeof (dest); + zero = 0; + /* Break memset. */ + /* memset implemented in MOPS instructions. */ + __asm__ volatile ("setp [%0]!, %1!, %2\n\t" + "setm [%0]!, %1!, %2\n\t" + "sete [%0]!, %1!, %2\n\t" + "prfm pldl3keep, [%0, #0]\n\t" + : "+&r"(p), "+&r"(size) + : "r"(zero) + : "memory"); + + p = dest; + q = source; + size = sizeof (dest); + /* Break memcpy. */ + /* memcpy implemented in MOPS instructions. */ + __asm__ volatile ("cpyfp [%0]!, [%1]!, %2!\n\t" + "cpyfm [%0]!, [%1]!, %2!\n\t" + "cpyfe [%0]!, [%1]!, %2!\n\t" + "prfm pldl3keep, [%0, #0]\n\t" + : "+&r" (p), "+&r" (q), "+&r" (size) + : + : "memory"); + + p = dest; + q = source; + size = sizeof (dest); + /* Break memmove. */ + /* memmove implemented in MOPS instructions. */ + __asm__ volatile ("cpyp [%0]!, [%1]!, %2!\n\t" + "cpym [%0]!, [%1]!, %2!\n\t" + "cpye [%0]!, [%1]!, %2!\n\t" + "prfm pldl3keep, [%0, #0]\n\t" + : "+&r" (p), "+&r" (q), "+&r" (size) + : + : "memory"); + + return 0; +} diff --git a/gdb/testsuite/gdb.arch/aarch64-mops-single-step.exp b/gdb/testsuite/gdb.arch/aarch64-mops-single-step.exp new file mode 100644 index 000000000000..2cb5fdcca36b --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-mops-single-step.exp @@ -0,0 +1,98 @@ +# Copyright 2024 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 single stepping through MOPS (memory operations) instruction sequences. + +require allow_aarch64_mops_tests + +standard_testfile +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ + [list debug additional_flags=-march=armv9.3-a]] } { + return -1 +} + +# If the inferior is rescheduled to another CPU while a main or epilogue +# instruction is executed, the OS resets the inferior back to the prologue +# instruction, so we need to allow for that possibility. +proc step_through_sequence { prefix } { + set count 0 + + while { [is_at_instruction ${prefix}p] == 1 && $count < 50 } { + incr count + + # The stepi output isn't useful to detect whether we stepped over + # the instruction. + gdb_test "stepi" "\[^\r\n\]+" "step over ${prefix}p" + if { [is_at_instruction ${prefix}m] == 1 } { + pass "stepped over ${prefix}p" + } else { + fail "stepped over ${prefix}e" + return 0 + } + + gdb_test "stepi" "\[^\r\n\]+" "step over ${prefix}m" + if { [is_at_instruction ${prefix}e] == 1 } { + pass "stepped over ${prefix}m" + } elseif { [is_at_instruction ${prefix}p] == 1 } { + # The inferior was rescheduled to another CPU. + pass "${prefix}m: reset back to prologue" + continue + } else { + fail "stepped over ${prefix}m" + return 0 + } + + gdb_test "stepi" "\[^\r\n\]+" "step over ${prefix}e" + if { [is_at_instruction prfm] == 1 } { + pass "stepped over ${prefix}e" + return 1 + } elseif { [is_at_instruction ${prefix}p] == 1 } { + # The inferior was rescheduled to another CPU. + pass "${prefix}e: reset back to prologue" + continue + } + } + + fail "step through $prefix sequence" + return 0 +} + +if ![runto_main] { + return -1 +} + +gdb_breakpoint ${srcfile}:[gdb_get_line_number "Break memset"] +gdb_breakpoint ${srcfile}:[gdb_get_line_number "Break memcpy"] +gdb_breakpoint ${srcfile}:[gdb_get_line_number "Break memmove"] + +gdb_continue_to_breakpoint "memset breakpoint" + +if { [arrive_at_instruction setp] } { + step_through_sequence set +} + +gdb_continue_to_breakpoint "memcpy breakpoint" + +if { [arrive_at_instruction cpyfp] } { + step_through_sequence cpyf +} + +gdb_continue_to_breakpoint "memmove breakpoint" + +if { [arrive_at_instruction cpyp] } { + step_through_sequence cpy +} diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index 55f6ab1fd8b9..762cf1cd5cf0 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -848,6 +848,44 @@ proc gdb_continue_to_breakpoint {name {location_pattern .*}} { }] } +# Check whether GDB is stopped at the given instruction. +# INSTRUCTION should be just its mnemonic, without any arguments. + +proc is_at_instruction { instruction } { + global gdb_prompt hex + + set test "pc points to $instruction" + gdb_test_multiple {x/i $pc} $test { + -re -wrap "=> $hex \[^\r\n\]+:\t$instruction\t\[^\r\n\]+" { + return 1 + } + -re "\r\n$gdb_prompt $" { + return 0 + } + } + + return 0 +} + +# Single-steps GDB until it arrives at the given instruction. +# INSTRUCTION should be just its mnemonic, without any arguments. + +proc arrive_at_instruction { instruction } { + set count 0 + + while { [is_at_instruction $instruction] != 1 } { + gdb_test -nopass "stepi" "\[^\r\n\]+" \ + "stepi #$count to reach $instruction" + incr count + + if { $count > 50 } { + fail "didn't reach $instruction" + return 0 + } + } + + return 1 +} # gdb_internal_error_resync: # @@ -4497,6 +4535,67 @@ proc aarch64_supports_sme_svl { length } { return 1 } +# Run a test on the target to see if it supports AArch64 MOPS (Memory +# Operations) extensions. Return 1 if so, 0 if it does not. Note this +# causes a restart of GDB. + +gdb_caching_proc allow_aarch64_mops_tests {} { + global srcdir subdir gdb_prompt inferior_exited_re + + set me "allow_aarch64_mops_tests" + + if { ![is_aarch64_target]} { + return 0 + } + + # ARMv9.3-A contains the MOPS extension. The test program doesn't use it, + # but take the opportunity to check whether the toolchain knows about MOPS. + set compile_flags "{additional_flags=-march=armv9.3-a}" + + # Compile a program that tests the MOPS feature. + set src { + #include + #include + + #ifndef HWCAP2_MOPS + #define HWCAP2_MOPS (1UL << 43) + #endif + + int main() { + bool mops_supported = getauxval (AT_HWCAP2) & HWCAP2_MOPS; + + return !mops_supported; + } + } + + if {![gdb_simple_compile $me $src executable $compile_flags]} { + return 0 + } + + # Compilation succeeded so now run it via gdb. + clean_restart $obj + gdb_run_cmd + gdb_expect { + -re ".*$inferior_exited_re with code 01.*${gdb_prompt} $" { + verbose -log "\n$me mops support not detected" + set allow_mops_tests 0 + } + -re ".*$inferior_exited_re normally.*${gdb_prompt} $" { + verbose -log "\n$me: mops support detected" + set allow_mops_tests 1 + } + default { + warning "\n$me: default case taken" + set allow_mops_tests 0 + } + } + gdb_exit + remote_file build delete $obj + + verbose "$me: returning $allow_mops_tests" 2 + return $allow_mops_tests +} + # A helper that compiles a test case to see if __int128 is supported. proc gdb_int128_helper {lang} { return [gdb_can_simple_compile "i128-for-$lang" {