
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 Approved-By: Luis Machado <luis.machado@arm.com> Tested-By: Luis Machado <luis.machado@arm.com>
98 lines
2.9 KiB
Text
98 lines
2.9 KiB
Text
# 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 <http://www.gnu.org/licenses/>.
|
|
#
|
|
# 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
|
|
}
|