[v4] gdb: ADI support

Message ID 1500424890-112103-1-git-send-email-weimin.pan@oracle.com
State New, archived
Headers

Commit Message

Weimin Pan July 19, 2017, 12:41 a.m. UTC
  The M7 processor supports an Application Data Integrity (ADI) feature
   that detects invalid data accesses.  When software allocates data, it
   chooses a 4-bit version number, sets the version in the upper 4 bits
   of the 64-bit pointer to that data, and stores the 4-bit version in
   every cacheline of the object.  Hardware saves the latter in spare
   bits in the cache and memory hierarchy. On each load and store, the
   processor compares the upper 4 VA (virtual address) bits to the
   cacheline's version. If there is a mismatch, the processor generates
   a version mismatch trap which can be either precise or disrupting.
   The trap is an error condition which the kernel delivers to the process
   as a SIGSEGV signal.

   The upper 4 bits of the VA represent a version and are not part of the
   true address.  The processor clears these bits and sign extends bit 59
   to generate the true address.

   Note that 32-bit applications cannot use ADI.

   This patch adds ADI support in gdb which allows the user to examine
   current version tags and assign new version tags in the program.
   It also catches and reports precise or disrupting memory corruption
   traps.

Tested in sparc64-linux-gnu. No regressions.

gdb/ChangeLog:
2017-07-18  Weimin Pan  <weimin.pan@oracle.com>

	* sparc64-tdep.h: (adi_normalize_address): New export.
	* sparc-nat.h: (open_adi_tag_fd): New export.
	* sparc64-linux-nat.c: (open_adi_tag_fd): New function.
	* sparc64-linux-tdep.c:
	(SEGV_ACCADI, SEGV_ADIDERR, SEGV_ADIPERR) New defines.
	(sparc64_linux_handle_segmentation_fault): New function.
	(sparc64_linux_init_abi): Register 
	sparc64_linux_handle_segmentation_fault
	* sparc64-tdep.c: Include cli-utils.h,gdbcmd.h,auxv.h.
	(sparc64_addr_bits_remove): New function.
	(sparc64_init_abi): Register sparc64_addr_bits_remove.
	(MAX_PROC_NAME_SIZE): New macro.
	(AT_ADI_BLKSZ, AT_ADI_NBITS, AT_ADI_UEONADI) New defines.
	(sparc64adilist): New variable.
	(adi_proc_list): New variable.
	(find_adi_info): New function.
	(add_adi_info): New function.
	(get_adi_info_proc): New function.
	(get_adi_info): New function.
	(info_adi_command): New function.
	(read_maps_entry): New function.
	(adi_available): New function.
	(adi_normalize_address): New function.
	(adi_align_address): New function.
	(adi_convert_byte_count): New function.
	(adi_tag_fd): New function.
	(adi_is_addr_mapped): New function.
	(adi_read_versions): New function.
	(adi_write_versions): New function.
	(adi_print_versions): New function.
	(do_examine): New function.
	(do_assign): New function.
	(adi_examine_command): New function.
	(adi_assign_command): New function.
	(_initialize_sparc64_adi_tdep): New function.

gdb/doc/ChangeLog:
2017-07-18  Weimin Pan  <weimin.pan@oracle.com>

	* gdb.texinfo (Architectures): Add new Sparc64 section to document 
	ADI support.
	* NEWS: Add "adi examine" and "adi assign" commands.

gdb/testsuite/ChangeLog:
2017-07-18  Weimin Pan  <weimin.pan@oracle.com>

	* gdb.arch/sparc64-adi.exp: New file.

---
 gdb/ChangeLog                           |   39 +++
 gdb/doc/ChangeLog                       |    4 +
 gdb/doc/gdb.texinfo                     |   73 +++++
 gdb/sparc64-linux-nat.c                 |    3 +
 gdb/sparc64-linux-tdep.c                |   69 ++++
 gdb/sparc64-tdep.c                      |  526 +++++++++++++++++++++++++++++++
 gdb/sparc64-tdep.h                      |    2 +
 gdb/testsuite/ChangeLog                 |    4 +
 gdb/testsuite/gdb.arch/sparc64-adi.c    |  135 ++++++++
 gdb/testsuite/gdb.arch/sparc64-adi.exp  |   55 ++++
 gdb/testsuite/gdb.arch/sparc64-adilib.h |   24 ++
 11 files changed, 934 insertions(+), 0 deletions(-)
 create mode 100755 gdb/testsuite/gdb.arch/sparc64-adi.c
 create mode 100644 gdb/testsuite/gdb.arch/sparc64-adi.exp
 create mode 100755 gdb/testsuite/gdb.arch/sparc64-adilib.h
  

Comments

Yao Qi July 25, 2017, 3:15 p.m. UTC | #1
Weimin Pan <weimin.pan@oracle.com> writes:

> +/* Per-process ADI stat info.  */
> +
> +struct sparc64_adi_info
> +{
> +  /* The process identifier.  */
> +  pid_t pid;
> +
> +  /* The ADI stat.  */
> +  struct adi_stat_t stat;
> +
> +  /* Linked list.  */
> +  struct sparc64_adi_info *next;
> +};

Use C++ STL list.  I suggested it on the v3 review.

> +
> +/* Normalize a versioned address - a VA with ADI bits (63-60) set.  */
> +
> +static CORE_ADDR
> +adi_normalize_address (CORE_ADDR addr)
> +{
> +  struct adi_stat_t adi_stat = get_adi_info (ptid_get_pid (inferior_ptid));
> +
> +  if (adi_stat.nbits)
> +    return ((CORE_ADDR)(((long)addr << adi_stat.nbits) >> adi_stat.nbits));
> +  return addr;
> +}
> +
> +/* Align a normalized address - a VA with bit 59 sign extended into ADI bits.  */

This line is too long.

> +
> +static CORE_ADDR
> +adi_align_address (CORE_ADDR naddr)
> +{
> +  struct adi_stat_t adi_stat = get_adi_info (ptid_get_pid (inferior_ptid));
> +
> +  return (naddr - (naddr % adi_stat.blksize)) / adi_stat.blksize;
> +}
> +

> +
> +/* Read ADI version tag value for memory locations starting at "vaddr"
> +   for "size" number of bytes.  */
> +
> +static int
> +adi_read_versions (CORE_ADDR vaddr, size_t size, unsigned char *tags)
> +{
> +  int fd = adi_tag_fd ();
> +  if (fd == -1)
> +    return -1;
> +
> +  if (!adi_is_addr_mapped (vaddr, size))
> +    {
> +      struct adi_stat_t adi_stat = get_adi_info (ptid_get_pid (inferior_ptid));
> +      error(_("Address at 0x%lx is not in ADI maps"), vaddr*adi_stat.blksize);

These two lines are too long.

> +    }
> +
> +  int target_errno;
> +  return target_fileio_pread (fd, tags, size, vaddr, &target_errno);
> +}
> +
> +/* Write ADI version tag for memory locations starting at "vaddr" for
> + "size" number of bytes to "tags".  */
> +
> +static int
> +adi_write_versions (CORE_ADDR vaddr, size_t size, unsigned char *tags)
> +{
> +  int fd = adi_tag_fd ();
> +  if (fd == -1)
> +    return -1;
> +
> +  if (!adi_is_addr_mapped (vaddr, size))
> +    {
> +      struct adi_stat_t adi_stat = get_adi_info (ptid_get_pid (inferior_ptid));
> +      error(_("Address at 0x%lx is not in ADI maps"), vaddr*adi_stat.blksize);
> +    }
> +
> +  int target_errno;
> +  return target_fileio_pwrite (fd, tags, size, vaddr, &target_errno);
> +}
> +
> +/* Print ADI version tag value in "tags" for memory locations starting
> +   at "vaddr" with number of "cnt".  */
> +

Replace "tags" with TAGS, "cnt' with CNT, "vaddr" with VADDR.

> +static void
> +adi_print_versions (CORE_ADDR vaddr, size_t cnt, unsigned char *tags)
> +{
> +  int v_idx = 0;
> +  const int maxelts = 8;  /* # of elements per line */
> +
> +  struct adi_stat_t adi_stat = get_adi_info (ptid_get_pid (inferior_ptid));
> +
> +  while (cnt > 0)
> +    {
> +      QUIT;
> +      printf_filtered ("0x%016lx:\t", vaddr * adi_stat.blksize);
> +      for (int i = maxelts; i > 0 && cnt > 0; i--, cnt--)
> +        {
> +          if (tags[v_idx] == 0xff)    /* no version tag */
> +            printf_filtered ("- ");
> +          else
> +            printf_filtered ("%1X ", tags[v_idx]);
> +          ++v_idx;
> +        }
> +      printf_filtered ("\n");
> +      gdb_flush (gdb_stdout);
> +      vaddr += maxelts;
> +    }
> +}
> +

> +
> +static void
> +adi_assign_command (char *args, int from_tty)
> +{
> +  /* make sure program is active and adi is available */
> +  if (!target_has_execution)
> +    error (_("ADI command requires a live process/thread"));
> +
> +  if (!adi_available ())
> +    error (_("No ADI information"));
> +
> +  char *exp = args;
> +  if (exp == 0)
> +    error_no_arg (_("Usage: adi assign|a[/count] <addr> = <version>"));
> +
> +  char *q = (char *) strchr (exp, '=');
> +  if (q)
> +    *q++ = 0;
> +  else
> +    error (_("Usage: adi assign|a[/count] <addr> = <version>"));
> +
> +  size_t cnt = 1;
> +  char *p = args;
> +  if (exp && *exp == '/')
> +    {
> +      p = exp + 1;
> +      cnt = get_number (&p);
> +    }
> +
> +  CORE_ADDR next_address = 0;
> +  if (p != 0 && *p != 0)
> +    next_address = parse_and_eval_address (p);
> +  else
> +    error (_("Usage: adi assign|a[/count] <addr> = <version>"));
> +
> +  int version = 0;
> +  if (q)           /* parse version tag */

if (q != NULL)

> +    {
> +      struct adi_stat_t adi_stat = get_adi_info (ptid_get_pid (inferior_ptid));

The ling is too long.

> +      version = parse_and_eval_long (q);
> +      if (version < 0 || version > adi_stat.max_version)
> +        error (_("Invalid ADI version tag %d"), version);
> +    }
> +
> +  do_assign(next_address, cnt, version);
> +}
> +
> +void
> +_initialize_sparc64_adi_tdep (void)
> +{
> +
> +  add_prefix_cmd ("adi", class_support, info_adi_command,
> +                  _("ADI version related commands."),
> +                  &sparc64adilist, "adi ", 0, &cmdlist);
> +  add_cmd ("examine", class_support, adi_examine_command,
> +           _("Examine ADI versions."), &sparc64adilist);
> +  add_alias_cmd ("x", "examine", no_class, 1, &sparc64adilist);
> +  add_cmd ("assign", class_support, adi_assign_command,
> +           _("Assign ADI versions."), &sparc64adilist);
> +
> +}
> +
> +
>  /* The functions on this page are intended to be used to classify
>     function arguments.  */
>  
> @@ -1290,6 +1805,14 @@ sparc64_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum,
>      }
>  }
>  
> +/* sparc64_addr_bits_remove - remove useless address bits  */
> +
> +static CORE_ADDR
> +sparc64_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR addr)
> +{
> +  return adi_normalize_address (addr);
> +}
> +
>  void
>  sparc64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>  {
> @@ -1342,6 +1865,8 @@ sparc64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>  
>    frame_unwind_append_unwinder (gdbarch, &sparc64_frame_unwind);
>    frame_base_set_default (gdbarch, &sparc64_frame_base);
> +
> +  set_gdbarch_addr_bits_remove (gdbarch, sparc64_addr_bits_remove);
>  }
>  
>  
> @@ -1666,3 +2191,4 @@ const struct sparc_fpregmap sparc64_bsd_fpregmap =
>    0 * 8,			/* %f0 */
>    32 * 8,			/* %fsr */
>  };
> +
> diff --git a/gdb/sparc64-tdep.h b/gdb/sparc64-tdep.h
> index 324778e..da61a82 100644
> --- a/gdb/sparc64-tdep.h
> +++ b/gdb/sparc64-tdep.h
> @@ -138,4 +138,6 @@ extern struct trad_frame_saved_reg *
>  
>  extern const struct sparc_fpregmap sparc64_bsd_fpregmap;
>  
> +extern void sparc64_forget_process (pid_t pid);
> +
>  #endif /* sparc64-tdep.h */
> diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
> index 6b22498..5f8837f 100644
> --- a/gdb/testsuite/ChangeLog
> +++ b/gdb/testsuite/ChangeLog
> @@ -1,3 +1,7 @@
> +2017-07-18  Weimin Pan  <weimin.pan@oracle.com>
> +
> +	* gdb.arch/sparc64-adi.exp: New file.

and gdb.arch/sparc64-adi.c.

> +
>  2017-07-09  Tom Tromey  <tom@tromey.com>
>  
>  	* gdb.dwarf2/shortpiece.exp: New file.
> diff --git a/gdb/testsuite/gdb.arch/sparc64-adi.c b/gdb/testsuite/gdb.arch/sparc64-adi.c
> new file mode 100755
> index 0000000..a5557ef
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/sparc64-adi.c
> @@ -0,0 +1,135 @@
> +/* Application Data Integrity (ADI) test in sparc64.
> +
> +   Copyright 2017 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 <http://www.gnu.org/licenses/>.  */
> +
> +#include "sparc64-adilib.h"
> +
> +#define MAPSIZE 8192
> +#define SHMSIZE 102400
> +#ifndef PROT_ADI
> +#define PROT_ADI 0x10
> +#endif
> +
> +static int
> +memory_fill(char *addr, size_t size, int pattern)
> +{

In general, the test case should follow the GNU coding standard too.

> +	long	*aligned_addr = (long *) addr;
> +	long	i;
> +	for (i = 0; i < size / sizeof (long); i += ONEKB) {
> +		*aligned_addr = pattern;
> +		aligned_addr = aligned_addr + ONEKB;
> +	}
> +	return (0);
> +}
> +
> +int main()
> +{
> +	char	*haddr;
> +	caddr_t	vaddr;
> +	int	version;
> +
> +	// test ISM
> +	int shmid = shmget(IPC_PRIVATE, SHMSIZE, IPC_CREAT | 0666);
> +	if (shmid == -1) {
> +		printf ("ERROR: shmget failed with an error:%d\n", errno);
> +		exit(1);

The test case is run by GDB testsuite, rather than by human, so these
printing isn't useful.

> +	}
> +	char *shmaddr = (char *)shmat(shmid, NULL, 0x666 | SHM_RND);
> +	if (shmaddr == (char *)-1) {
> +		printf("ERROR: shmat failed with an error:%d\n", errno);
> +		shmctl(shmid, IPC_RMID, NULL);
> +		exit(1);
> +	}
> +	// enable ADI on ISM segment
> +	if (mprotect(shmaddr, SHMSIZE, PROT_READ|PROT_WRITE|PROT_ADI)) {
> +		perror("mprotect failed");
> +		goto err_out;
> +	}
> +	if (memory_fill(shmaddr, SHMSIZE, 0xdeadbeaf) != 0) { /* line breakpoint here */
> +		printf("ERROR: ISM cannot fill memory\n");
> +		exit(1);
> +	}
> +	adi_clr_version(shmaddr, SHMSIZE);
> +	caddr_t vshmaddr = adi_set_version(shmaddr, SHMSIZE, 0x8);
> +	if (vshmaddr == 0) {
> +		printf("ERROR: ISM cannot set version\n");
> +		exit(1);
> +	}
> +	// test mmap
> +	int fd = open("/dev/zero", O_RDWR);
> +	if (fd < 0){
> +		printf("ERROR:MMAP cannot open \n");
> +		exit(1);
> +	}
> +	char *maddr = (char *)mmap(NULL,MAPSIZE,PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
> +	if (maddr == (char *)-1) {
> +			printf("ERROR: mmap failed with an error:%d\n", errno);
> +			exit(1);
> +	}
> +	// enable ADI
> +	if (mprotect(shmaddr, MAPSIZE, PROT_READ|PROT_WRITE|PROT_ADI)) {
> +		perror("mprotect failed");
> +		goto err_out;
> +	}
> +	if (memory_fill(maddr, MAPSIZE, 0xdeadbeaf) != 0) {
> +		printf("ERROR:MMAP cannot fill memory\n");
> +		exit(1);
> +	}
> +	caddr_t vmaddr = adi_set_version(maddr, MAPSIZE, 0x8);
> +	if (vmaddr == 0)
> +		printf ("ERROR: MMAP cannot set version\n");
> +
> +	// test heap
> +	haddr = (char*) memalign(MAPSIZE, MAPSIZE);
> +	if (!haddr) {
> +		printf ("ERROR: HEAP cannot memalign\n");
> +	}
> +	// enable ADI
> +	if (mprotect(shmaddr, MAPSIZE, PROT_READ|PROT_WRITE|PROT_ADI)) {
> +		perror("mprotect failed");
> +		goto err_out;
> +	}
> +
> +	if (memory_fill(haddr, MAPSIZE, 0xdeadbeaf) != 0) {
> +		printf("ERROR:HEAP cannot fill memory\n");
> +		exit(1);
> +	}
> +	adi_clr_version(haddr, MAPSIZE);
> +	/* Set some ADP version number */
> +	caddr_t vaddr1, vaddr2, vaddr3, vaddr4;
> +	vaddr = adi_set_version(haddr, 64*2, 0x8);
> +	vaddr1 = adi_set_version(haddr+64*2, 64*2, 0x9);
> +	vaddr2 = adi_clr_version(haddr+64*4, 64*2);
> +	vaddr3 = adi_set_version(haddr+64*6, 64*2, 0xa);
> +	vaddr4 = adi_set_version(haddr+64*8, 64*10, 0x3);
> +	if (vaddr == 0) {
> +		printf("ERROR: adi_set_version() failed to set version num\n");
> +		exit(1);
> +	}
> +	char *versioned_p = vaddr;
> +	*versioned_p = 'a';
> +	char *uvp = haddr;	// unversioned pointer
> +	*uvp = 'b';		// version mismatch trap
> +
> +	return (0);
> +err_out:
> +        if (shmdt((const void *)shmaddr) != 0)
> +                perror("Detach failure");
> +        shmctl(shmid, IPC_RMID, NULL);
> +        exit(1);
> +}
> diff --git a/gdb/testsuite/gdb.arch/sparc64-adi.exp b/gdb/testsuite/gdb.arch/sparc64-adi.exp
> new file mode 100644
> index 0000000..25ab183
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/sparc64-adi.exp
> @@ -0,0 +1,55 @@
> +# Copyright 2017 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.
> +
> +# Basic tests of examining/assigning ADI version tags, and reporting precise mismatch.

This line is too long.

> +
> +if ![istarget "sparc64*-*-linux*"] then {
> +    verbose "Skipping sparc64 ADI test."
> +    return 0
> +}
> +
> +set testfile sparc64-adi
> +set srcfile ${testfile}.c
> +set binfile ${objdir}/${subdir}/${testfile}

Replace them with standard_testfile.

> +
> +set cflags "-g"
> +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \
> +          [list additional_flags=$cflags libs=-ladi]] } {

[list debug libs=-ladi]

> +    return -1
> +}
> +
> +if ![runto_main] then {
> +    untested "could not run to main"
> +    return -1
> +}
> +
> +gdb_test "break [gdb_get_line_number "line breakpoint here"]" \
> +         "Breakpoint .* at .*${srcfile}.*" \
> +         "set line breakpoint in main"
> +gdb_continue_to_breakpoint "continue to line breakpoint in main"
> +
> +##########################################
> +set newadi "7"
> +gdb_test "adi x shmaddr" "" "examine ADI"

Can you match the output?

> +gdb_test "adi a/100 shmaddr=${newadi}" "" "assign ADI"

Likewise.

> +gdb_test "adi x/100 shmaddr" \
> +"0x\[0-9a-f\]+00:.*\t${newadi} ${newadi}.*" "examine new ADI"
> +gdb_test "adi a/100 shmaddr=0x0" "" "reset ADI"
> +gdb_test "continue" \
> +         ".*Program received signal SIGSEGV.*
> +.*ADI precise mismatch while accessing address.*" \
> +	"continue to sigsegv"
> diff --git a/gdb/testsuite/gdb.arch/sparc64-adilib.h b/gdb/testsuite/gdb.arch/sparc64-adilib.h
> new file mode 100755
> index 0000000..add026c
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/sparc64-adilib.h
> @@ -0,0 +1,24 @@

Why do you need a header?
  
Jose E. Marchesi July 25, 2017, 3:31 p.m. UTC | #2
> +/* Per-process ADI stat info.  */
    > +
    > +struct sparc64_adi_info
    > +{
    > +  /* The process identifier.  */
    > +  pid_t pid;
    > +
    > +  /* The ADI stat.  */
    > +  struct adi_stat_t stat;
    > +
    > +  /* Linked list.  */
    > +  struct sparc64_adi_info *next;
    > +};
    
    Use C++ STL list.  I suggested it on the v3 review.

General question: what's wrong with using a simple linked list of
structs if that is the developer's preference, and/or it follows the
current style of the surrounding code?  (Not saying it is in this case,
that's up to Weimin to say.)

As far as I can see the usage of STL is not mandated by the GDB coding
standards (as documented in the wiki) and developers may want to avoid
them in many circumstances.
  
Yao Qi July 25, 2017, 4:44 p.m. UTC | #3
jose.marchesi@oracle.com (Jose E. Marchesi) writes:

> General question: what's wrong with using a simple linked list of
> structs if that is the developer's preference, and/or it follows the
> current style of the surrounding code?  (Not saying it is in this case,
> that's up to Weimin to say.)

It makes the code a little bit unnecessarily complicated, IOW, std::list
or std::forward_list can make it simpler.  My preference is to use
standard c++ data structure rather than re-inventing it again.

>
> As far as I can see the usage of STL is not mandated by the GDB coding
> standards (as documented in the wiki) and developers may want to avoid
> them in many circumstances.

It is out of the scope of coding standard.  GDB is a C++ project,
when we organize something as a list, it is quite natural to use
standard c++ data structure.
  
Weimin Pan July 26, 2017, 9:12 p.m. UTC | #4
On 7/25/2017 8:15 AM, Yao Qi wrote:
> Weimin Pan <weimin.pan@oracle.com> writes:
>
>> +/* Per-process ADI stat info.  */
>> +
>> +struct sparc64_adi_info
>> +{
>> +  /* The process identifier.  */
>> +  pid_t pid;
>> +
>> +  /* The ADI stat.  */
>> +  struct adi_stat_t stat;
>> +
>> +  /* Linked list.  */
>> +  struct sparc64_adi_info *next;
>> +};
> Use C++ STL list.  I suggested it on the v3 review.

I'm using forward_list to replace the linked list.

> ...
>> +++ b/gdb/testsuite/gdb.arch/sparc64-adi.c
>> @@ -0,0 +1,135 @@
>> +/* Application Data Integrity (ADI) test in sparc64.
>> +
>> +   Copyright 2017 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 <http://www.gnu.org/licenses/>.  */
>> +
>> +#include "sparc64-adilib.h"
>> +
>> +#define MAPSIZE 8192
>> +#define SHMSIZE 102400
>> +#ifndef PROT_ADI
>> +#define PROT_ADI 0x10
>> +#endif
>> +
>> +static int
>> +memory_fill(char *addr, size_t size, int pattern)
>> +{
> In general, the test case should follow the GNU coding standard too.

OK, done.

> .....
>
>> +    return -1
>> +}
>> +
>> +if ![runto_main] then {
>> +    untested "could not run to main"
>> +    return -1
>> +}
>> +
>> +gdb_test "break [gdb_get_line_number "line breakpoint here"]" \
>> +         "Breakpoint .* at .*${srcfile}.*" \
>> +         "set line breakpoint in main"
>> +gdb_continue_to_breakpoint "continue to line breakpoint in main"
>> +
>> +##########################################
>> +set newadi "7"
>> +gdb_test "adi x shmaddr" "" "examine ADI"
> Can you match the output?

Here are the results of running this new test:

% make check TESTS="gdb.arch/sparc64-adi.exp"
...
...

                 === gdb tests ===

Schedule of variations:
     unix

Running target unix
Using /usr/share/dejagnu/baseboards/unix.exp as board description file 
for target.
Using /usr/share/dejagnu/config/unix.exp as generic interface file for 
target.
Using ~/binutils-gdb/gdb/testsuite/config/unix.exp as 
tool-and-target-specific interface file.
Running ~/binutils-gdb/gdb/testsuite/gdb.arch/sparc64-adi.exp ...

                 === gdb Summary ===

# of expected passes            7
...

>> +gdb_test "adi a/100 shmaddr=${newadi}" "" "assign ADI"
> Likewise.
>
>> +gdb_test "adi x/100 shmaddr" \
>> +"0x\[0-9a-f\]+00:.*\t${newadi} ${newadi}.*" "examine new ADI"
>> +gdb_test "adi a/100 shmaddr=0x0" "" "reset ADI"
>> +gdb_test "continue" \
>> +         ".*Program received signal SIGSEGV.*
>> +.*ADI precise mismatch while accessing address.*" \
>> +	"continue to sigsegv"
>> diff --git a/gdb/testsuite/gdb.arch/sparc64-adilib.h b/gdb/testsuite/gdb.arch/sparc64-adilib.h
>> new file mode 100755
>> index 0000000..add026c
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.arch/sparc64-adilib.h
>> @@ -0,0 +1,24 @@
> Why do you need a header?
>

Merged the header file into sparc64-adi.c.
  
Yao Qi July 27, 2017, 11:16 a.m. UTC | #5
Wei-min Pan <weimin.pan@oracle.com> writes:

>>> +##########################################
>>> +set newadi "7"
>>> +gdb_test "adi x shmaddr" "" "examine ADI"
>> Can you match the output?
>
> Here are the results of running this new test:
>

That is not what I mean.  Your test leaves the PATTERN
(the 2nd argument) empty,

gdb_test "adi x shmaddr" "" "examine ADI"

so it doesn't test much.  You need to add PATTERN to match the expected
output from command "adi x shmaddr".

> % make check TESTS="gdb.arch/sparc64-adi.exp"
> ...
> ...
>
>                 === gdb tests ===
>
> Schedule of variations:
>     unix
>
> Running target unix
> Using /usr/share/dejagnu/baseboards/unix.exp as board description file
> for target.
> Using /usr/share/dejagnu/config/unix.exp as generic interface file for
> target.
> Using ~/binutils-gdb/gdb/testsuite/config/unix.exp as
> tool-and-target-specific interface file.
> Running ~/binutils-gdb/gdb/testsuite/gdb.arch/sparc64-adi.exp ...
>
>                 === gdb Summary ===
>
> # of expected passes            7
> ...
  
Weimin Pan July 27, 2017, 4:34 p.m. UTC | #6
On 7/27/2017 4:16 AM, Yao Qi wrote:
> Wei-min Pan <weimin.pan@oracle.com> writes:
>
>>>> +##########################################
>>>> +set newadi "7"
>>>> +gdb_test "adi x shmaddr" "" "examine ADI"
>>> Can you match the output?
>> Here are the results of running this new test:
>>
> That is not what I mean.  Your test leaves the PATTERN
> (the 2nd argument) empty,
>
> gdb_test "adi x shmaddr" "" "examine ADI"
>
> so it doesn't test much.  You need to add PATTERN to match the expected
> output from command "adi x shmaddr".

The first "adi x shmaddr" command is mainly used to test that the ADI 
information is available
at the address. Its value which can be anything is not tested. The 
second "examine" command,
on the other hand, makes sure the new ADI version tag is being set 
correctly at the address.

>
>> % make check TESTS="gdb.arch/sparc64-adi.exp"
>> ...
>> ...
>>
>>                  === gdb tests ===
>>
>> Schedule of variations:
>>      unix
>>
>> Running target unix
>> Using /usr/share/dejagnu/baseboards/unix.exp as board description file
>> for target.
>> Using /usr/share/dejagnu/config/unix.exp as generic interface file for
>> target.
>> Using ~/binutils-gdb/gdb/testsuite/config/unix.exp as
>> tool-and-target-specific interface file.
>> Running ~/binutils-gdb/gdb/testsuite/gdb.arch/sparc64-adi.exp ...
>>
>>                  === gdb Summary ===
>>
>> # of expected passes            7
>> ...
  
Yao Qi July 28, 2017, 10:30 a.m. UTC | #7
On Thu, Jul 27, 2017 at 5:34 PM, Wei-min Pan <weimin.pan@oracle.com> wrote:

>
> The first "adi x shmaddr" command is mainly used to test that the ADI
> information is available
> at the address. Its value which can be anything is not tested. The second
> "examine" command,
> on the other hand, makes sure the new ADI version tag is being set correctly
> at the address.
>

Can you show me the output of these adi commands?
(the output in gdb.log for example)Then, I can give some
concrete suggestions on the patterns to match output
in these tests.
  
Weimin Pan July 28, 2017, 4:34 p.m. UTC | #8
On 7/28/2017 3:30 AM, Yao Qi wrote:
> On Thu, Jul 27, 2017 at 5:34 PM, Wei-min Pan <weimin.pan@oracle.com> wrote:
>
>> The first "adi x shmaddr" command is mainly used to test that the ADI
>> information is available
>> at the address. Its value which can be anything is not tested. The second
>> "examine" command,
>> on the other hand, makes sure the new ADI version tag is being set correctly
>> at the address.
>>
> Can you show me the output of these adi commands?
> (the output in gdb.log for example)Then, I can give some
> concrete suggestions on the patterns to match output
> in these tests.
>

Here are the outputs of these commands. Thanks.

....
Breakpoint 2, main () at 
/home/wepan/work/binutils-gdb/gdb/testsuite/gdb.arch/sparc64-adi.c:86^M
86        if (memory_fill (shmaddr, SHMSIZE, PAT) != 0) /* line 
breakpoint here */^M
(gdb) PASS: gdb.arch/sparc64-adi.exp: continue to breakpoint: continue 
to line breakpoint in main
adi x shmaddr^M
0xfff800010002c000:     0 ^M
(gdb) PASS: gdb.arch/sparc64-adi.exp: examine ADI
adi a/100 shmaddr=7^M
(gdb) PASS: gdb.arch/sparc64-adi.exp: assign ADI
adi x/100 shmaddr^M
0xfff800010002c000:     7 7 ^M
(gdb) PASS: gdb.arch/sparc64-adi.exp: examine new ADI
adi a/100 shmaddr=0x0^M
(gdb) PASS: gdb.arch/sparc64-adi.exp: reset ADI
continue^M
Continuing.^M
^M
Program received signal SIGSEGV, Segmentation fault^M
ADI precise mismatch while accessing address 0xfff8000100048000.^M
0xfff8000100129f38 in adi_set_version () from /usr/lib64/libadi.so^M
(gdb) PASS: gdb.arch/sparc64-adi.exp: continue to sigsegv
  
Yao Qi Aug. 1, 2017, 2:10 p.m. UTC | #9
Wei-min Pan <weimin.pan@oracle.com> writes:

> (gdb) PASS: gdb.arch/sparc64-adi.exp: continue to breakpoint: continue
> to line breakpoint in main
> adi x shmaddr^M
> 0xfff800010002c000:     0 ^M

gdb_test "adi x shmaddr" "${hex}00:\t0" "examine ADI"

> (gdb) PASS: gdb.arch/sparc64-adi.exp: examine ADI
> adi a/100 shmaddr=7^M

gdb_test_no_output "adi a/100 shmaddr=${newadi}" "assign ADI"

> (gdb) PASS: gdb.arch/sparc64-adi.exp: assign ADI
> adi x/100 shmaddr^M
> 0xfff800010002c000:     7 7 ^M

gdb_test "adi x/100 shmaddr" "${hex}00:\t${newadi} ${newadi}" \
         "examine new ADI"

> (gdb) PASS: gdb.arch/sparc64-adi.exp: examine new ADI
> adi a/100 shmaddr=0x0^M

gdb_test_no_output "adi a/100 shmaddr=0x0" "reset ADI"

> (gdb) PASS: gdb.arch/sparc64-adi.exp: reset ADI
> continue^M
> Continuing.^M
> ^M
> Program received signal SIGSEGV, Segmentation fault^M
> ADI precise mismatch while accessing address 0xfff8000100048000.^M
> 0xfff8000100129f38 in adi_set_version () from /usr/lib64/libadi.so^M

gdb_test "continue" \
         [multi_line "Program received signal SIGSEGV, Segmentation fault" \
                     "ADI precise mismatch while accessing address $hex" ] \
	"continue to sigsegv"

> (gdb) PASS: gdb.arch/sparc64-adi.exp: continue to sigsegv
  
Weimin Pan Aug. 1, 2017, 5:41 p.m. UTC | #10
Thanks for your suggestions. There are 3 failures however.


On 8/1/2017 7:10 AM, Yao Qi wrote:
> Wei-min Pan <weimin.pan@oracle.com> writes:
>
>> (gdb) PASS: gdb.arch/sparc64-adi.exp: continue to breakpoint: continue
>> to line breakpoint in main
>> adi x shmaddr^M
>> 0xfff800010002c000:     0 ^M
> gdb_test "adi x shmaddr" "${hex}00:\t0" "examine ADI"

It will pass if changed to

gdb_test "adi x shmaddr" "${hex}00:.*\t0.*" "examine ADI"


> gdb_test "adi x/100 shmaddr" "${hex}00:\t${newadi} ${newadi}" \
>           "examine new ADI"

It will pass if changed to:

gdb_test "adi x/100 shmaddr" "${hex}00:.*\t${newadi} ${newadi}.*" \
          "examine new ADI"

>> (gdb) PASS: gdb.arch/sparc64-adi.exp: reset ADI
>> continue^M
>> Continuing.^M
>> ^M
>> Program received signal SIGSEGV, Segmentation fault^M
>> ADI precise mismatch while accessing address 0xfff8000100048000.^M
>> 0xfff8000100129f38 in adi_set_version () from /usr/lib64/libadi.so^M
> gdb_test "continue" \
>           [multi_line "Program received signal SIGSEGV, Segmentation fault" \
>                       "ADI precise mismatch while accessing address $hex" ] \
> 	"continue to sigsegv"

And it will pass if changed to

gdb_test "continue" \
          [multi_line ".*Program received signal SIGSEGV, Segmentation 
fault.*" \
                      ".*ADI precise mismatch while accessing address 
$hex.*" ] \
         "continue to sigsegv"

>> (gdb) PASS: gdb.arch/sparc64-adi.exp: continue to sigsegv
  

Patch

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 23f2d1b..406a350 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,42 @@ 
+2017-07-18  Weimin Pan  <weimin.pan@oracle.com>
+
+	* sparc64-tdep.h: (sparc64_forget_process): New export.
+	* sparc64-linux-tdep.c:
+	(SEGV_ACCADI, SEGV_ADIDERR, SEGV_ADIPERR) New defines.
+	(sparc64_linux_handle_segmentation_fault): New function.
+	(sparc64_linux_init_abi): Register
+	sparc64_linux_handle_segmentation_fault
+	* sparc64-linux-nat.c:
+	(_initialize_sparc64_linux_nat): Register sparc64_forget_process
+	* sparc64-tdep.c: Include cli-utils.h,gdbcmd.h,auxv.h.
+	(sparc64_addr_bits_remove): New function.
+	(sparc64_init_abi): Register sparc64_addr_bits_remove.
+	(MAX_PROC_NAME_SIZE): New macro.
+	(AT_ADI_BLKSZ, AT_ADI_NBITS, AT_ADI_UEONADI) New defines.
+	(sparc64adilist): New variable.
+	(adi_proc_list): New variable.
+	(find_adi_info): New function.
+	(add_adi_info): New function.
+	(get_adi_info_proc): New function.
+	(get_adi_info): New function.
+	(info_adi_command): New function.
+	(read_maps_entry): New function.
+	(adi_available): New function.
+	(adi_normalize_address): New function.
+	(adi_align_address): New function.
+	(adi_convert_byte_count): New function.
+	(adi_tag_fd): New function.
+	(adi_is_addr_mapped): New function.
+	(adi_read_versions): New function.
+	(adi_write_versions): New function.
+	(adi_print_versions): New function.
+	(do_examine): New function.
+	(do_assign): New function.
+	(adi_examine_command): New function.
+	(adi_assign_command): New function.
+	(_initialize_sparc64_tdep): New function.
+	* NEWS: Add "adi examine" and "adi assign" commands.
+
 2017-07-10  Yao Qi  <yao.qi@linaro.org>
 
 	* features/i386/amd64-avx-avx512-linux.c: Re-generated.
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index 8c7c3da..6ccc3d1 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,3 +1,7 @@ 
+2017-07-18  Weimin Pan  <weimin.pan@oracle.com>
+	* gdb.texinfo (Architectures): Add new Sparc64 section to document 
+	ADI support.
+
 2017-06-20  Sergio Durigan Junior  <sergiodj@redhat.com>
 
 	PR gdb/21606
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index c167a86..1ed5bc3 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -22465,6 +22465,7 @@  all uses of @value{GDBN} with the architecture, both native and cross.
 * SPU::                Cell Broadband Engine SPU architecture
 * PowerPC::
 * Nios II::
+* Sparc64::
 @end menu
 
 @node AArch64
@@ -22849,6 +22850,78 @@  target code in @value{GDBN}.
 Show the current setting of Nios II debugging messages.
 @end table
 
+@node Sparc64
+@subsection Sparc64
+@cindex Sparc64 support
+@cindex Application Data Integrity
+@subsubsection ADI Support
+
+The M7 processor supports an Application Data Integrity (ADI) feature that 
+detects invalid data accesses.  When software allocates memory and enables 
+ADI on the allocated memory, it chooses a 4-bit version number, sets the 
+version in the upper 4 bits of the 64-bit pointer to that data, and stores 
+the 4-bit version in every cacheline of that data.  Hardware saves the latter 
+in spare bits in the cache and memory hierarchy.  On each load and store, 
+the processor compares the upper 4 VA (virtual address) bits to the 
+cacheline's version.  If there is a mismatch, the processor generates a 
+version mismatch trap which can be either precise or disrupting.  The trap 
+is an error condition which the kernel delivers to the process as a SIGSEGV 
+signal.
+
+Note that only 64-bit applications can use ADI and need to be built with
+ADI-enabled.
+
+Values of the ADI version tags, which are in granularity of a 
+cacheline (64 bytes), can be viewed or modified. 
+
+
+@table @code
+@kindex adi examine
+@item adi (examine | x) [ / @var{n} ] @var{addr}
+
+The @code{adi examine} command displays the value of one ADI version tag per 
+cacheline.  It has the following command syntax: 
+
+@var{n} is a decimal integer specifying the number in bytes; the default 
+is 1.  It specifies how much ADI version information, at the ratio of 1:ADI 
+block size, to display. 
+
+@var{addr} is the address in user address space where you want @value{GDBN} 
+to begin displaying the ADI version tags. 
+
+Below is an example of displaying ADI versions of variable "shmaddr".
+
+@smallexample
+(@value{GDBP}) adi x/100 shmaddr
+   0xfff800010002c000:     0 0
+@end smallexample
+
+@kindex adi assign
+@item adi (assign | a) [ / @var{n} ] @var{addr} = @var{tag}
+
+The @code{adi assign} command is used to assign new ADI version tag 
+to an address.  It has the following command syntax:
+
+@var{n} is a decimal integer specifying the number in bytes; 
+the default is 1.  It specifies how much ADI version information, at the 
+ratio of 1:ADI block size, to modify. 
+
+@var{addr} is the address in user address space where you want @value{GDBN} 
+to begin modifying the ADI version tags. 
+
+@var{tag} is the new ADI version tag.
+
+For example, do the following to modify then verify ADI versions of 
+variable "shmaddr":
+
+@smallexample
+(@value{GDBP}) adi a/100 shmaddr = 7
+(@value{GDBP}) adi x/100 shmaddr
+   0xfff800010002c000:     7 7
+@end smallexample
+
+@end table
+
 @node Controlling GDB
 @chapter Controlling @value{GDBN}
 
diff --git a/gdb/sparc64-linux-nat.c b/gdb/sparc64-linux-nat.c
index 638aa29..282980a 100644
--- a/gdb/sparc64-linux-nat.c
+++ b/gdb/sparc64-linux-nat.c
@@ -89,5 +89,8 @@  _initialize_sparc64_linux_nat (void)
   /* Register the target.  */
   linux_nat_add_target (t);
 
+  /* ADI support */
+  linux_nat_set_forget_process (t, sparc64_forget_process);
+
   sparc_gregmap = &sparc64_linux_ptrace_gregmap;
 }
diff --git a/gdb/sparc64-linux-tdep.c b/gdb/sparc64-linux-tdep.c
index 814bc29..2da978f 100644
--- a/gdb/sparc64-linux-tdep.c
+++ b/gdb/sparc64-linux-tdep.c
@@ -33,6 +33,17 @@ 
 #include "xml-syscall.h"
 #include "linux-tdep.h"
 
+/* ADI specific si_code */
+#ifndef SEGV_ACCADI
+#define SEGV_ACCADI	3
+#endif
+#ifndef SEGV_ADIDERR
+#define SEGV_ADIDERR	4
+#endif
+#ifndef SEGV_ADIPERR
+#define SEGV_ADIPERR	5
+#endif
+
 /* The syscall's XML filename for sparc 64-bit.  */
 #define XML_SYSCALL_FILENAME_SPARC64 "syscalls/sparc64-linux.xml"
 
@@ -104,6 +115,62 @@  sparc64_linux_sigframe_init (const struct tramp_frame *self,
     }
   trad_frame_set_id (this_cache, frame_id_build (base, func));
 }
+
+/* sparc64 GNU/Linux implementation of the handle_segmentation_fault
+   gdbarch hook.
+   Displays information related to ADI memory corruptions.  */
+
+void
+sparc64_linux_handle_segmentation_fault (struct gdbarch *gdbarch,
+				      struct ui_out *uiout)
+{
+  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word != 64)
+    return;
+
+  CORE_ADDR addr = 0;
+  long si_code = 0;
+
+  TRY
+    {
+      /* Evaluate si_code to see if the segfault is ADI related.  */
+      si_code = parse_and_eval_long ("$_siginfo.si_code\n");
+
+      if (si_code >= SEGV_ACCADI && si_code <= SEGV_ADIPERR)
+        addr = parse_and_eval_long ("$_siginfo._sifields._sigfault.si_addr");
+    }
+  CATCH (exception, RETURN_MASK_ALL)
+    {
+      return;
+    }
+  END_CATCH
+
+  /* Print out ADI event based on sig_code value */
+  switch (si_code)
+    {
+    case SEGV_ACCADI:	/* adi not enabled */
+      uiout->text ("\n");
+      uiout->field_string ("sigcode-meaning", _("ADI disabled"));
+      uiout->text (_(" while accessing address "));
+      uiout->field_fmt ("bound-access", "%s", paddress (gdbarch, addr));
+      break;
+    case SEGV_ADIDERR:	/* disrupting mismatch */
+      uiout->text ("\n");
+      uiout->field_string ("sigcode-meaning", _("ADI deferred mismatch"));
+      uiout->text (_(" while accessing address "));
+      uiout->field_fmt ("bound-access", "%s", paddress (gdbarch, addr));
+      break;
+    case SEGV_ADIPERR:	/* precise mismatch */
+      uiout->text ("\n");
+      uiout->field_string ("sigcode-meaning", _("ADI precise mismatch"));
+      uiout->text (_(" while accessing address "));
+      uiout->field_fmt ("bound-access", "%s", paddress (gdbarch, addr));
+      break;
+    default:
+      break;
+    }
+
+}
+
 
 /* Return the address of a system call's alternative return
    address.  */
@@ -338,6 +405,8 @@  sparc64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   set_xml_syscall_file_name (gdbarch, XML_SYSCALL_FILENAME_SPARC64);
   set_gdbarch_get_syscall_number (gdbarch,
                                   sparc64_linux_get_syscall_number);
+  set_gdbarch_handle_segmentation_fault (gdbarch,
+					 sparc64_linux_handle_segmentation_fault);
 }
 
 
diff --git a/gdb/sparc64-tdep.c b/gdb/sparc64-tdep.c
index 9e0e6b5..75e8776 100644
--- a/gdb/sparc64-tdep.c
+++ b/gdb/sparc64-tdep.c
@@ -46,6 +46,521 @@ 
    sparc64_-prefix for 64-bit specific code and the sparc_-prefix for
    code can handle both.  */
 
+/* The M7 processor supports an Application Data Integrity (ADI) feature
+   that detects invalid data accesses.  When software allocates memory and 
+   enables ADI on the allocated memory, it chooses a 4-bit version number, 
+   sets the version in the upper 4 bits of the 64-bit pointer to that data, 
+   and stores the 4-bit version in every cacheline of the object.  Hardware 
+   saves the latter in spare bits in the cache and memory hierarchy. On each 
+   load and store, the processor compares the upper 4 VA (virtual address) bits 
+   to the cacheline's version. If there is a mismatch, the processor generates
+   a version mismatch trap which can be either precise or disrupting.
+   The trap is an error condition which the kernel delivers to the process
+   as a SIGSEGV signal.
+
+   The upper 4 bits of the VA represent a version and are not part of the
+   true address.  The processor clears these bits and sign extends bit 59
+   to generate the true address.
+
+   Note that 32-bit applications cannot use ADI. */
+
+
+#include "cli/cli-utils.h"
+#include "gdbcmd.h"
+#include "auxv.h"
+
+#define MAX_PROC_NAME_SIZE sizeof("/proc/99999/lwp/9999/adi/lstatus")
+
+/* ELF Auxiliary vectors */
+#ifndef AT_ADI_BLKSZ
+#define AT_ADI_BLKSZ    34
+#endif
+#ifndef AT_ADI_NBITS
+#define AT_ADI_NBITS    35
+#endif
+#ifndef AT_ADI_UEONADI
+#define AT_ADI_UEONADI  36
+#endif
+
+/* ADI command list.  */
+static struct cmd_list_element *sparc64adilist = NULL;
+
+/* ADI stat settings.  */
+struct adi_stat_t
+{
+  /* The ADI block size.  */
+  unsigned long blksize;
+
+  /* Number of bits used for an ADI version tag which can be
+   * used together with the shift value for an ADI version tag
+   * to encode or extract the ADI version value in a pointer.  */
+  unsigned long nbits;
+
+  /* The maximum ADI version tag value supported.  */
+  int max_version;
+
+  /* ADI version tag file.  */
+  int tag_fd;
+
+  /* ADI availability check has been done.  */
+  bool checked_avail;
+
+  /* ADI is available.  */
+  bool is_avail;
+
+};
+
+/* Per-process ADI stat info.  */
+
+struct sparc64_adi_info
+{
+  /* The process identifier.  */
+  pid_t pid;
+
+  /* The ADI stat.  */
+  struct adi_stat_t stat;
+
+  /* Linked list.  */
+  struct sparc64_adi_info *next;
+};
+
+static struct sparc64_adi_info *adi_proc_list = NULL;
+
+/* Find ADI info for process PID.  */
+
+static struct sparc64_adi_info *
+find_adi_info (pid_t pid)
+{
+  struct sparc64_adi_info *proc;
+
+  for (proc = adi_proc_list; proc; proc = proc->next)
+    if (proc->pid == pid)
+      return proc;
+
+  return NULL;
+}
+
+/* Add ADI info for process PID.  Returns newly allocated info
+   object.  */
+
+static struct sparc64_adi_info *
+add_adi_info (pid_t pid)
+{
+  struct sparc64_adi_info *proc = XCNEW (struct sparc64_adi_info);
+
+  proc->pid = pid;
+  proc->next = adi_proc_list;
+  adi_proc_list = proc;
+  proc->stat.is_avail = false;
+  proc->stat.checked_avail = false;
+  proc->stat.tag_fd = 0;
+
+  return proc;
+}
+
+/* Get ADI info for process PID, creating one if it doesn't exist.  */
+
+static struct sparc64_adi_info * 
+get_adi_info_proc (pid_t pid)
+{
+  struct sparc64_adi_info *proc;
+
+  proc = find_adi_info (pid);
+  if (proc == NULL)
+    proc = add_adi_info (pid);
+
+  return proc;
+}
+
+static struct adi_stat_t 
+get_adi_info (pid_t pid)
+{
+  struct sparc64_adi_info *proc;
+
+  proc = get_adi_info_proc (pid);
+  return proc->stat;
+}
+
+void
+sparc64_forget_process (pid_t pid)
+{
+  struct sparc64_adi_info *proc, **proc_link;
+  int target_errno;
+
+  proc = adi_proc_list;
+  proc_link = &adi_proc_list;
+
+  while (proc != NULL)
+    {
+      if (proc->pid == pid)
+	{
+	  *proc_link = proc->next;
+    	  if (proc->stat.tag_fd > 0)
+    	    target_fileio_close (proc->stat.tag_fd, &target_errno);
+	  xfree (proc);
+	  return;
+	}
+
+      proc_link = &proc->next;
+      proc = *proc_link;
+    }
+}
+
+static void
+info_adi_command (char *args, int from_tty)
+{
+  printf_unfiltered ("\"adi\" must be followed by \"examine\" "
+                     "or \"assign\".\n");
+  help_list (sparc64adilist, "adi ", all_commands, gdb_stdout);
+}
+
+/* Read attributes of a maps entry in /proc/[pid]/adi/maps.  */
+
+static void
+read_maps_entry (const char *line,
+              ULONGEST *addr, ULONGEST *endaddr)
+{
+  const char *p = line;
+
+  *addr = strtoulst (p, &p, 16);
+  if (*p == '-')
+    p++;
+
+  *endaddr = strtoulst (p, &p, 16);
+}
+
+/* Check if ADI is available.  */
+
+static bool
+adi_available (void)
+{
+  pid_t pid = ptid_get_pid (inferior_ptid);
+  struct sparc64_adi_info *proc = get_adi_info_proc (pid);
+
+  if (proc->stat.checked_avail)
+    return proc->stat.is_avail;
+
+  proc->stat.checked_avail = true;
+  if (target_auxv_search (&current_target, AT_ADI_BLKSZ, 
+                          &proc->stat.blksize) <= 0)
+    return false;
+  target_auxv_search (&current_target, AT_ADI_NBITS, &proc->stat.nbits);
+  proc->stat.max_version = (1 << proc->stat.nbits) - 2;
+  proc->stat.is_avail = true;
+
+  return proc->stat.is_avail;
+}
+
+/* Normalize a versioned address - a VA with ADI bits (63-60) set.  */
+
+static CORE_ADDR
+adi_normalize_address (CORE_ADDR addr)
+{
+  struct adi_stat_t adi_stat = get_adi_info (ptid_get_pid (inferior_ptid));
+
+  if (adi_stat.nbits)
+    return ((CORE_ADDR)(((long)addr << adi_stat.nbits) >> adi_stat.nbits));
+  return addr;
+}
+
+/* Align a normalized address - a VA with bit 59 sign extended into ADI bits.  */
+
+static CORE_ADDR
+adi_align_address (CORE_ADDR naddr)
+{
+  struct adi_stat_t adi_stat = get_adi_info (ptid_get_pid (inferior_ptid));
+
+  return (naddr - (naddr % adi_stat.blksize)) / adi_stat.blksize;
+}
+
+/* Convert a byte count to count at a ratio of 1:adi_blksz.  */
+
+static int
+adi_convert_byte_count (CORE_ADDR naddr, int nbytes, CORE_ADDR locl)
+{
+  struct adi_stat_t adi_stat = get_adi_info (ptid_get_pid (inferior_ptid));
+
+  return ((naddr + nbytes + adi_stat.blksize - 1) / adi_stat.blksize) - locl;
+}
+
+/* The /proc/[pid]/adi/tags file, which allows gdb to get/set ADI
+   version in a target process, maps linearly to the address space
+   of the target process at a ratio of 1:adi_blksz.
+
+   A read (or write) at offset K in the file returns (or modifies)
+   the ADI version tag stored in the cacheline containing address
+   K * adi_blksz, encoded as 1 version tag per byte.  The allowed
+   version tag values are between 0 and adi_stat.max_version.  */
+
+static int
+adi_tag_fd (void)
+{
+  pid_t pid = ptid_get_pid (inferior_ptid);
+  struct sparc64_adi_info *proc = get_adi_info_proc (pid);
+
+  if (proc->stat.tag_fd != 0)
+    return proc->stat.tag_fd;
+
+  char cl_name[MAX_PROC_NAME_SIZE];
+  snprintf (cl_name, sizeof(cl_name), "/proc/%d/adi/tags", pid);
+  int target_errno;
+  proc->stat.tag_fd = target_fileio_open (NULL, cl_name, O_RDWR|O_EXCL, 0,
+			                  &target_errno);
+  return proc->stat.tag_fd;
+}
+
+/* Check if an address set is ADI enabled, using /proc/[pid]/adi/maps
+   which was exported by the kernel and contains the currently ADI
+   mapped memory regions and their access permissions.  */
+
+static bool
+adi_is_addr_mapped (CORE_ADDR vaddr, size_t cnt)
+{
+  char filename[MAX_PROC_NAME_SIZE];
+  size_t i = 0;
+
+  pid_t pid = ptid_get_pid (inferior_ptid);
+  snprintf (filename, sizeof filename, "/proc/%d/adi/maps", pid);
+  char *data = target_fileio_read_stralloc (NULL, filename);
+  if (data)
+    {
+      struct cleanup *cleanup = make_cleanup (xfree, data);
+      struct adi_stat_t adi_stat = get_adi_info (pid);
+      char *line;
+      for (line = strtok (data, "\n"); line; line = strtok (NULL, "\n"))
+        {
+          ULONGEST addr, endaddr;
+
+          read_maps_entry (line, &addr, &endaddr);
+
+          while (((vaddr + i) * adi_stat.blksize) >= addr
+                 && ((vaddr + i) * adi_stat.blksize) < endaddr)
+            {
+              if (++i == cnt)
+                {
+                  do_cleanups (cleanup);
+                  return true;
+                }
+            }
+        }
+        do_cleanups (cleanup);
+      }
+    else
+      warning (_("unable to open /proc file '%s'"), filename);
+
+  return false;
+}
+
+/* Read ADI version tag value for memory locations starting at "vaddr"
+   for "size" number of bytes.  */
+
+static int
+adi_read_versions (CORE_ADDR vaddr, size_t size, unsigned char *tags)
+{
+  int fd = adi_tag_fd ();
+  if (fd == -1)
+    return -1;
+
+  if (!adi_is_addr_mapped (vaddr, size))
+    {
+      struct adi_stat_t adi_stat = get_adi_info (ptid_get_pid (inferior_ptid));
+      error(_("Address at 0x%lx is not in ADI maps"), vaddr*adi_stat.blksize);
+    }
+
+  int target_errno;
+  return target_fileio_pread (fd, tags, size, vaddr, &target_errno);
+}
+
+/* Write ADI version tag for memory locations starting at "vaddr" for
+ "size" number of bytes to "tags".  */
+
+static int
+adi_write_versions (CORE_ADDR vaddr, size_t size, unsigned char *tags)
+{
+  int fd = adi_tag_fd ();
+  if (fd == -1)
+    return -1;
+
+  if (!adi_is_addr_mapped (vaddr, size))
+    {
+      struct adi_stat_t adi_stat = get_adi_info (ptid_get_pid (inferior_ptid));
+      error(_("Address at 0x%lx is not in ADI maps"), vaddr*adi_stat.blksize);
+    }
+
+  int target_errno;
+  return target_fileio_pwrite (fd, tags, size, vaddr, &target_errno);
+}
+
+/* Print ADI version tag value in "tags" for memory locations starting
+   at "vaddr" with number of "cnt".  */
+
+static void
+adi_print_versions (CORE_ADDR vaddr, size_t cnt, unsigned char *tags)
+{
+  int v_idx = 0;
+  const int maxelts = 8;  /* # of elements per line */
+
+  struct adi_stat_t adi_stat = get_adi_info (ptid_get_pid (inferior_ptid));
+
+  while (cnt > 0)
+    {
+      QUIT;
+      printf_filtered ("0x%016lx:\t", vaddr * adi_stat.blksize);
+      for (int i = maxelts; i > 0 && cnt > 0; i--, cnt--)
+        {
+          if (tags[v_idx] == 0xff)    /* no version tag */
+            printf_filtered ("- ");
+          else
+            printf_filtered ("%1X ", tags[v_idx]);
+          ++v_idx;
+        }
+      printf_filtered ("\n");
+      gdb_flush (gdb_stdout);
+      vaddr += maxelts;
+    }
+}
+
+static void
+do_examine (CORE_ADDR start, int bcnt)
+{
+  CORE_ADDR vaddr = adi_normalize_address (start);
+  struct cleanup *cleanup;
+
+  CORE_ADDR vstart = adi_align_address (vaddr);
+  int cnt = adi_convert_byte_count (vaddr, bcnt, vstart);
+  unsigned char *buf = (unsigned char *) xmalloc (cnt);
+  cleanup = make_cleanup (xfree, buf);
+  int read_cnt = adi_read_versions (vstart, cnt, buf);
+  if (read_cnt == -1)
+    error (_("No ADI information"));
+  else if (read_cnt < cnt)
+    error(_("No ADI information at 0x%lx"), vaddr);
+
+  adi_print_versions (vstart, cnt, buf);
+
+  do_cleanups (cleanup);
+}
+
+static void
+do_assign (CORE_ADDR start, size_t bcnt, int version)
+{
+  CORE_ADDR vaddr = adi_normalize_address (start);
+
+  CORE_ADDR vstart = adi_align_address (vaddr);
+  int cnt = adi_convert_byte_count (vaddr, bcnt, vstart);
+  std::vector<unsigned char> buf (cnt, version);
+  int set_cnt = adi_write_versions (vstart, cnt, buf.data ());
+
+  if (set_cnt == -1)
+    error (_("No ADI information"));
+  else if (set_cnt < cnt)
+    error(_("No ADI information at 0x%lx"), vaddr);
+
+}
+
+/* ADI examine version tag command.
+
+   Command syntax:
+
+     adi (examine|x)/count <addr> */
+
+static void
+adi_examine_command (char *args, int from_tty)
+{
+  /* make sure program is active and adi is available */
+  if (!target_has_execution)
+    error (_("ADI command requires a live process/thread"));
+
+  if (!adi_available ())
+    error (_("No ADI information"));
+
+  pid_t pid = ptid_get_pid (inferior_ptid);
+  struct sparc64_adi_info *proc = get_adi_info_proc (pid);
+  int cnt = 1;
+  char *p = args;
+  if (p && *p == '/')
+    {
+      p++;
+      cnt = get_number (&p);
+    }
+
+  CORE_ADDR next_address = 0;
+  if (p != 0 && *p != 0)
+    next_address = parse_and_eval_address (p);
+  if (!cnt || !next_address)
+    error (_("Usage: adi examine|x[/count] <addr>"));
+
+  do_examine(next_address, cnt);
+}
+
+/* ADI assign version tag command.
+
+   Command syntax:
+
+     adi (assign|a)/count <addr> = <version>  */
+
+static void
+adi_assign_command (char *args, int from_tty)
+{
+  /* make sure program is active and adi is available */
+  if (!target_has_execution)
+    error (_("ADI command requires a live process/thread"));
+
+  if (!adi_available ())
+    error (_("No ADI information"));
+
+  char *exp = args;
+  if (exp == 0)
+    error_no_arg (_("Usage: adi assign|a[/count] <addr> = <version>"));
+
+  char *q = (char *) strchr (exp, '=');
+  if (q)
+    *q++ = 0;
+  else
+    error (_("Usage: adi assign|a[/count] <addr> = <version>"));
+
+  size_t cnt = 1;
+  char *p = args;
+  if (exp && *exp == '/')
+    {
+      p = exp + 1;
+      cnt = get_number (&p);
+    }
+
+  CORE_ADDR next_address = 0;
+  if (p != 0 && *p != 0)
+    next_address = parse_and_eval_address (p);
+  else
+    error (_("Usage: adi assign|a[/count] <addr> = <version>"));
+
+  int version = 0;
+  if (q)           /* parse version tag */
+    {
+      struct adi_stat_t adi_stat = get_adi_info (ptid_get_pid (inferior_ptid));
+      version = parse_and_eval_long (q);
+      if (version < 0 || version > adi_stat.max_version)
+        error (_("Invalid ADI version tag %d"), version);
+    }
+
+  do_assign(next_address, cnt, version);
+}
+
+void
+_initialize_sparc64_adi_tdep (void)
+{
+
+  add_prefix_cmd ("adi", class_support, info_adi_command,
+                  _("ADI version related commands."),
+                  &sparc64adilist, "adi ", 0, &cmdlist);
+  add_cmd ("examine", class_support, adi_examine_command,
+           _("Examine ADI versions."), &sparc64adilist);
+  add_alias_cmd ("x", "examine", no_class, 1, &sparc64adilist);
+  add_cmd ("assign", class_support, adi_assign_command,
+           _("Assign ADI versions."), &sparc64adilist);
+
+}
+
+
 /* The functions on this page are intended to be used to classify
    function arguments.  */
 
@@ -1290,6 +1805,14 @@  sparc64_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum,
     }
 }
 
+/* sparc64_addr_bits_remove - remove useless address bits  */
+
+static CORE_ADDR
+sparc64_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  return adi_normalize_address (addr);
+}
+
 void
 sparc64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 {
@@ -1342,6 +1865,8 @@  sparc64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 
   frame_unwind_append_unwinder (gdbarch, &sparc64_frame_unwind);
   frame_base_set_default (gdbarch, &sparc64_frame_base);
+
+  set_gdbarch_addr_bits_remove (gdbarch, sparc64_addr_bits_remove);
 }
 
 
@@ -1666,3 +2191,4 @@  const struct sparc_fpregmap sparc64_bsd_fpregmap =
   0 * 8,			/* %f0 */
   32 * 8,			/* %fsr */
 };
+
diff --git a/gdb/sparc64-tdep.h b/gdb/sparc64-tdep.h
index 324778e..da61a82 100644
--- a/gdb/sparc64-tdep.h
+++ b/gdb/sparc64-tdep.h
@@ -138,4 +138,6 @@  extern struct trad_frame_saved_reg *
 
 extern const struct sparc_fpregmap sparc64_bsd_fpregmap;
 
+extern void sparc64_forget_process (pid_t pid);
+
 #endif /* sparc64-tdep.h */
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 6b22498..5f8837f 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,7 @@ 
+2017-07-18  Weimin Pan  <weimin.pan@oracle.com>
+
+	* gdb.arch/sparc64-adi.exp: New file.
+
 2017-07-09  Tom Tromey  <tom@tromey.com>
 
 	* gdb.dwarf2/shortpiece.exp: New file.
diff --git a/gdb/testsuite/gdb.arch/sparc64-adi.c b/gdb/testsuite/gdb.arch/sparc64-adi.c
new file mode 100755
index 0000000..a5557ef
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/sparc64-adi.c
@@ -0,0 +1,135 @@ 
+/* Application Data Integrity (ADI) test in sparc64.
+
+   Copyright 2017 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 <http://www.gnu.org/licenses/>.  */
+
+#include "sparc64-adilib.h"
+
+#define MAPSIZE 8192
+#define SHMSIZE 102400
+#ifndef PROT_ADI
+#define PROT_ADI 0x10
+#endif
+
+static int
+memory_fill(char *addr, size_t size, int pattern)
+{
+	long	*aligned_addr = (long *) addr;
+	long	i;
+	for (i = 0; i < size / sizeof (long); i += ONEKB) {
+		*aligned_addr = pattern;
+		aligned_addr = aligned_addr + ONEKB;
+	}
+	return (0);
+}
+
+int main()
+{
+	char	*haddr;
+	caddr_t	vaddr;
+	int	version;
+
+	// test ISM
+	int shmid = shmget(IPC_PRIVATE, SHMSIZE, IPC_CREAT | 0666);
+	if (shmid == -1) {
+		printf ("ERROR: shmget failed with an error:%d\n", errno);
+		exit(1);
+	}
+	char *shmaddr = (char *)shmat(shmid, NULL, 0x666 | SHM_RND);
+	if (shmaddr == (char *)-1) {
+		printf("ERROR: shmat failed with an error:%d\n", errno);
+		shmctl(shmid, IPC_RMID, NULL);
+		exit(1);
+	}
+	// enable ADI on ISM segment
+	if (mprotect(shmaddr, SHMSIZE, PROT_READ|PROT_WRITE|PROT_ADI)) {
+		perror("mprotect failed");
+		goto err_out;
+	}
+	if (memory_fill(shmaddr, SHMSIZE, 0xdeadbeaf) != 0) { /* line breakpoint here */
+		printf("ERROR: ISM cannot fill memory\n");
+		exit(1);
+	}
+	adi_clr_version(shmaddr, SHMSIZE);
+	caddr_t vshmaddr = adi_set_version(shmaddr, SHMSIZE, 0x8);
+	if (vshmaddr == 0) {
+		printf("ERROR: ISM cannot set version\n");
+		exit(1);
+	}
+	// test mmap
+	int fd = open("/dev/zero", O_RDWR);
+	if (fd < 0){
+		printf("ERROR:MMAP cannot open \n");
+		exit(1);
+	}
+	char *maddr = (char *)mmap(NULL,MAPSIZE,PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+	if (maddr == (char *)-1) {
+			printf("ERROR: mmap failed with an error:%d\n", errno);
+			exit(1);
+	}
+	// enable ADI
+	if (mprotect(shmaddr, MAPSIZE, PROT_READ|PROT_WRITE|PROT_ADI)) {
+		perror("mprotect failed");
+		goto err_out;
+	}
+	if (memory_fill(maddr, MAPSIZE, 0xdeadbeaf) != 0) {
+		printf("ERROR:MMAP cannot fill memory\n");
+		exit(1);
+	}
+	caddr_t vmaddr = adi_set_version(maddr, MAPSIZE, 0x8);
+	if (vmaddr == 0)
+		printf ("ERROR: MMAP cannot set version\n");
+
+	// test heap
+	haddr = (char*) memalign(MAPSIZE, MAPSIZE);
+	if (!haddr) {
+		printf ("ERROR: HEAP cannot memalign\n");
+	}
+	// enable ADI
+	if (mprotect(shmaddr, MAPSIZE, PROT_READ|PROT_WRITE|PROT_ADI)) {
+		perror("mprotect failed");
+		goto err_out;
+	}
+
+	if (memory_fill(haddr, MAPSIZE, 0xdeadbeaf) != 0) {
+		printf("ERROR:HEAP cannot fill memory\n");
+		exit(1);
+	}
+	adi_clr_version(haddr, MAPSIZE);
+	/* Set some ADP version number */
+	caddr_t vaddr1, vaddr2, vaddr3, vaddr4;
+	vaddr = adi_set_version(haddr, 64*2, 0x8);
+	vaddr1 = adi_set_version(haddr+64*2, 64*2, 0x9);
+	vaddr2 = adi_clr_version(haddr+64*4, 64*2);
+	vaddr3 = adi_set_version(haddr+64*6, 64*2, 0xa);
+	vaddr4 = adi_set_version(haddr+64*8, 64*10, 0x3);
+	if (vaddr == 0) {
+		printf("ERROR: adi_set_version() failed to set version num\n");
+		exit(1);
+	}
+	char *versioned_p = vaddr;
+	*versioned_p = 'a';
+	char *uvp = haddr;	// unversioned pointer
+	*uvp = 'b';		// version mismatch trap
+
+	return (0);
+err_out:
+        if (shmdt((const void *)shmaddr) != 0)
+                perror("Detach failure");
+        shmctl(shmid, IPC_RMID, NULL);
+        exit(1);
+}
diff --git a/gdb/testsuite/gdb.arch/sparc64-adi.exp b/gdb/testsuite/gdb.arch/sparc64-adi.exp
new file mode 100644
index 0000000..25ab183
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/sparc64-adi.exp
@@ -0,0 +1,55 @@ 
+# Copyright 2017 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.
+
+# Basic tests of examining/assigning ADI version tags, and reporting precise mismatch.
+
+if ![istarget "sparc64*-*-linux*"] then {
+    verbose "Skipping sparc64 ADI test."
+    return 0
+}
+
+set testfile sparc64-adi
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+set cflags "-g"
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile \
+          [list additional_flags=$cflags libs=-ladi]] } {
+    return -1
+}
+
+if ![runto_main] then {
+    untested "could not run to main"
+    return -1
+}
+
+gdb_test "break [gdb_get_line_number "line breakpoint here"]" \
+         "Breakpoint .* at .*${srcfile}.*" \
+         "set line breakpoint in main"
+gdb_continue_to_breakpoint "continue to line breakpoint in main"
+
+##########################################
+set newadi "7"
+gdb_test "adi x shmaddr" "" "examine ADI"
+gdb_test "adi a/100 shmaddr=${newadi}" "" "assign ADI"
+gdb_test "adi x/100 shmaddr" \
+"0x\[0-9a-f\]+00:.*\t${newadi} ${newadi}.*" "examine new ADI"
+gdb_test "adi a/100 shmaddr=0x0" "" "reset ADI"
+gdb_test "continue" \
+         ".*Program received signal SIGSEGV.*
+.*ADI precise mismatch while accessing address.*" \
+	"continue to sigsegv"
diff --git a/gdb/testsuite/gdb.arch/sparc64-adilib.h b/gdb/testsuite/gdb.arch/sparc64-adilib.h
new file mode 100755
index 0000000..add026c
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/sparc64-adilib.h
@@ -0,0 +1,24 @@ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/errno.h>
+#include <sys/utsname.h>
+#include <sys/param.h>
+#include <malloc.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/shm.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <setjmp.h>
+#include "adi.h"
+
+#define	ONEKB		1024
+