mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-27 04:52:05 +08:00
68cffbbd44
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/<pid>/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)
176 lines
5.5 KiB
Plaintext
176 lines
5.5 KiB
Plaintext
# Copyright (C) 2018-2022 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 generating and reading a core file with MTE memory tags.
|
|
|
|
proc test_mte_core_file { core_filename mode } {
|
|
# Load the core file and make sure we see the tag violation fault
|
|
# information.
|
|
if {$mode == "sync"} {
|
|
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 ${::hex}" \
|
|
"Logical tag ${::hex}\." \
|
|
"#0.*${::hex} in main \\(.*\\) at .*" \
|
|
".*mmap_pointers\\\[0\\\] = 0x4;"] \
|
|
"core file shows $mode memory tag violation"
|
|
} else {
|
|
gdb_test "core $core_filename" \
|
|
[multi_line \
|
|
"Core was generated by.*\." \
|
|
"Program terminated with signal SIGSEGV, Segmentation fault" \
|
|
"Memory tag violation" \
|
|
"Fault address unavailable\." \
|
|
"#0 ${::hex} in .* from .*"] \
|
|
"core file shows $mode memory tag violation"
|
|
}
|
|
|
|
# Make sure we have the tag_ctl register.
|
|
gdb_test "info register tag_ctl" \
|
|
"tag_ctl.*${::hex}.*${::decimal}" \
|
|
"tag_ctl is available"
|
|
|
|
# In ASYNC mode, there is nothing left to test, as the program stops at
|
|
# a place where further source code inspection is not possible.
|
|
if {$mode == "async"} {
|
|
return
|
|
}
|
|
|
|
# First, figure out the page size.
|
|
set page_size [get_valueof "" "page_sz" "0" \
|
|
"fetch value of page size"]
|
|
|
|
# Get the number of maps for the test
|
|
set nmaps [get_valueof "" "NMAPS" "0" \
|
|
"fetch number of maps"]
|
|
set tag 1
|
|
|
|
# Iterate over all of the MTE-protected memory mappings and make sure
|
|
# GDB retrieves the correct allocation tags for each one. If the tag
|
|
# has the expected value, that means the core file was generated correctly
|
|
# and that GDB read the contents correctly.
|
|
for {set i 0} {$i < $nmaps} {incr i} {
|
|
for {set offset 0} {$offset < $page_size} {set offset [expr $offset + 16]} {
|
|
set hex_tag [format "%x" $tag]
|
|
gdb_test "memory-tag print-allocation-tag mmap_pointers\[$i\] + $offset" \
|
|
"= 0x$hex_tag" \
|
|
"mmap_ponters\[$i\] + $offset contains expected tag"
|
|
# Update the expected tag. The test writes tags in sequential
|
|
# order.
|
|
set tag [expr ($tag + 1) % 16]
|
|
}
|
|
}
|
|
}
|
|
|
|
# Exercise MTE corefile support using mode MODE (Async or Sync)
|
|
|
|
proc test_mode { mode } {
|
|
|
|
set compile_flags {"debug" "macros" "additional_flags=-march=armv8.5-a+memtag"}
|
|
|
|
# If we are testing async mode, we need to force the testcase to use
|
|
# such mode.
|
|
if {$mode == "async"} {
|
|
lappend compile_flags "additional_flags=-DASYNC"
|
|
}
|
|
|
|
standard_testfile
|
|
set executable "${::testfile}-${mode}"
|
|
if {[prepare_for_testing "failed to prepare" ${executable} ${::srcfile} ${compile_flags}]} {
|
|
return -1
|
|
}
|
|
set binfile [standard_output_file ${executable}]
|
|
|
|
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
|
|
}
|
|
|
|
# Run until a crash and confirm GDB displays memory tag violation
|
|
# information.
|
|
if {$mode == "sync"} {
|
|
gdb_test "continue" \
|
|
[multi_line \
|
|
"Program received signal SIGSEGV, Segmentation fault" \
|
|
"Memory tag violation while accessing address ${::hex}" \
|
|
"Allocation tag 0x1" \
|
|
"Logical tag 0x0\." \
|
|
"${::hex} in main \\(.*\\) at .*" \
|
|
".*mmap_pointers\\\[0\\\] = 0x4;"] \
|
|
"run to memory $mode tag violation"
|
|
} else {
|
|
gdb_test "continue" \
|
|
[multi_line \
|
|
"Program received signal SIGSEGV, Segmentation fault" \
|
|
"Memory tag violation" \
|
|
"Fault address unavailable\." \
|
|
"${::hex} in .* from .*"] \
|
|
"run to memory $mode tag violation"
|
|
}
|
|
|
|
# Generate the gcore core file.
|
|
set gcore_filename [standard_output_file "${executable}.gcore"]
|
|
set gcore_generated [gdb_gcore_cmd "$gcore_filename" "generate gcore file"]
|
|
|
|
# Generate a native core file.
|
|
set core_filename [core_find ${binfile}]
|
|
set core_generated [expr {$core_filename != ""}]
|
|
|
|
# At this point we have a couple core files, the gcore one generated by GDB
|
|
# and the native one generated by the Linux Kernel. Make sure GDB can read
|
|
# both correctly.
|
|
|
|
if {$gcore_generated} {
|
|
clean_restart ${binfile}
|
|
with_test_prefix "gcore corefile" {
|
|
test_mte_core_file $gcore_filename $mode
|
|
}
|
|
} else {
|
|
fail "gcore corefile not generated"
|
|
}
|
|
|
|
if {$core_generated} {
|
|
clean_restart ${binfile}
|
|
with_test_prefix "native corefile" {
|
|
test_mte_core_file $core_filename $mode
|
|
}
|
|
} else {
|
|
untested "native corefile not generated"
|
|
}
|
|
|
|
}
|
|
|
|
if {![is_aarch64_target]} {
|
|
verbose "Skipping ${gdb_test_file_name}."
|
|
return
|
|
}
|
|
|
|
# Run tests
|
|
foreach_with_prefix mode {"sync" "async"} {
|
|
test_mode $mode
|
|
}
|