This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] S390: New "multi-arch" test case


New S/390-specific test case for verifying the correct switching
between various S/390 architecture variants.  The test case creates
two core files and derives "patched" versions from those that look as
if created on different architecture variants.  Then each core file is
loaded into GDB and it is checked whether the expected set of
registers is available.

OK to apply?


testsuite/ChangeLog:

2013-10-09  Andreas Arnez  <arnez@linux.vnet.ibm.com>

	* gdb.arch/s390-multiarch.exp: New file.

	* gdb.arch/s390-multiarch.c: New file.


Index: gdb/gdb/testsuite/gdb.arch/s390-multiarch.c
===================================================================
--- /dev/null
+++ gdb/gdb/testsuite/gdb.arch/s390-multiarch.c
@@ -0,0 +1,314 @@
+/* Copyright 2013 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 <errno.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef struct
+{
+  unsigned char	e_ident[16];
+  uint16_t	e_type;
+  uint16_t	e_machine;
+  uint32_t	e_version;
+  uint32_t	e_entry;
+  uint32_t	e_phoff;
+  uint32_t	e_shoff;
+  uint32_t	e_flags;
+  uint16_t	e_ehsize;
+  uint16_t	e_phentsize;
+  uint16_t	e_phnum;
+  uint16_t	e_shentsize;
+  uint16_t	e_shnum;
+  uint16_t	e_shstrndx;
+} Elf32_Ehdr;
+
+typedef struct
+{
+  unsigned char	e_ident[16];
+  uint16_t	e_type;
+  uint16_t	e_machine;
+  uint32_t	e_version;
+  uint64_t	e_entry;
+  uint64_t	e_phoff;
+  uint64_t	e_shoff;
+  uint32_t	e_flags;
+  uint16_t	e_ehsize;
+  uint16_t	e_phentsize;
+  uint16_t	e_phnum;
+  uint16_t	e_shentsize;
+  uint16_t	e_shnum;
+  uint16_t	e_shstrndx;
+} Elf64_Ehdr;
+
+typedef struct
+{
+  uint32_t	p_type;
+  uint32_t	p_offset;
+  uint32_t	p_vaddr;
+  uint32_t	p_paddr;
+  uint32_t	p_filesz;
+  uint32_t	p_memsz;
+  uint32_t	p_flags;
+  uint32_t	p_align;
+} Elf32_Phdr;
+
+typedef struct
+{
+  uint32_t	p_type;
+  uint32_t	p_flags;
+  uint64_t	p_offset;
+  uint64_t	p_vaddr;
+  uint64_t	p_paddr;
+  uint64_t	p_filesz;
+  uint64_t	p_memsz;
+  uint64_t	p_align;
+} Elf64_Phdr;
+
+struct elfbuf
+{
+  const char *path;
+  unsigned char *buf;
+  size_t len;
+  enum { ELFCLASS32 = 1,
+	 ELFCLASS64 = 2 } ei_class;
+};
+
+#define ELFBUF_EHDR_LEN(elf)					\
+  ((elf)->ei_class == ELFCLASS32 ? sizeof (Elf32_Ehdr) :	\
+   sizeof (Elf64_Ehdr))
+
+#define ELFBUF_EHDR(elf, memb)			\
+  ((elf)->ei_class == ELFCLASS32 ?		\
+   ((Elf32_Ehdr *) (elf)->buf)->memb		\
+   : ((Elf64_Ehdr *) (elf)->buf)->memb)
+
+#define ELFBUF_PHDR_LEN(elf)					\
+  ((elf)->ei_class == ELFCLASS32 ? sizeof (Elf32_Phdr) :	\
+   sizeof (Elf64_Phdr))
+
+#define ELFBUF_PHDR(elf, idx, memb)				\
+  ((elf)->ei_class == ELFCLASS32 ?				\
+   ((Elf32_Phdr *) &(elf)->buf[((Elf32_Ehdr *)(elf)->buf)	\
+			       ->e_phoff])[idx].memb		\
+   : ((Elf64_Phdr *) &(elf)->buf[((Elf64_Ehdr *)(elf)->buf)	\
+				 ->e_phoff])[idx].memb)
+
+static void
+exit_with_msg(const char *fmt, ...)
+{
+  va_list ap;
+
+  fflush (stdout);
+  va_start (ap, fmt);
+  vfprintf (stderr, fmt, ap);
+  va_end (ap);
+
+  if (errno)
+    {
+      fputs (": ", stderr);
+      perror (NULL);
+    }
+  else
+    fputc ('\n', stderr);
+  exit (1);
+}
+
+static void
+read_file (unsigned char **buf_ptr, size_t *len_ptr, FILE *fp)
+{
+  size_t len = 0;
+  size_t size = 1024;
+  size_t chunk;
+  unsigned char *buf = malloc (size);
+
+  while ((chunk = fread (buf + len, 1, size - len, fp)) == size - len)
+    {
+      len = size;
+      size *= 2;
+      buf = realloc (buf, size);
+    }
+  len += chunk;
+  *buf_ptr = buf;
+  *len_ptr = len;
+}
+
+static void
+write_file (unsigned char *buf, size_t len, FILE *fp)
+{
+  fwrite (buf, 1, len, fp);
+}
+
+static void
+elfbuf_init_from_file (struct elfbuf *elf, const char *path)
+{
+  FILE *fp = fopen (path, "rb");
+  unsigned char *buf;
+  size_t len;
+
+  if (fp == NULL)
+    exit_with_msg ("%s", path);
+
+  read_file (&buf, &len, fp);
+  fclose (fp);
+
+  /* Validate ELF identification. */
+  if (len < 16
+      || buf[0] != 0x7f || buf[1] != 0x45 || buf[2] != 0x4c || buf[3] != 0x46
+      || buf[4] < 1 || buf[4] > 2 || buf[5] < 1 || buf[5] > 2)
+    exit_with_msg ("%s: unsupported or invalid ELF file", path);
+
+  elf->path = path;
+  elf->buf = buf;
+  elf->len = len;
+  elf->ei_class = buf[4];
+
+  if (ELFBUF_EHDR_LEN (elf) > len
+      || ELFBUF_EHDR (elf, e_phoff) > len
+      || ELFBUF_EHDR (elf, e_phnum) > ((len - ELFBUF_EHDR (elf, e_phoff))
+				       / ELFBUF_PHDR_LEN (elf)) )
+    exit_with_msg ("%s: unexpected end of data", path);
+
+  if (ELFBUF_EHDR (elf, e_phentsize) != ELFBUF_PHDR_LEN (elf))
+    exit_with_msg ("%s: inconsistent ELF header", path);
+}
+
+static void
+elfbuf_write_to_file (struct elfbuf *elf, const char *path)
+{
+  FILE *fp = fopen (path, "wb");
+
+  if (fp == NULL)
+    exit_with_msg ("%s", path);
+
+  write_file (elf->buf, elf->len, fp);
+  fclose (fp);
+}
+
+/* In the auxv note starting at OFFSET with size LEN, mask the hwcap
+   field using the HWCAP_MASK. */
+
+static void
+elfbuf_handle_auxv (struct elfbuf *elf, size_t offset, size_t len,
+		    unsigned long hwcap_mask)
+{
+  size_t i;
+  uint32_t *auxv32 = (uint32_t *) (elf->buf + offset);
+  uint64_t *auxv64 = (uint64_t *) auxv32;
+  size_t entry_size = elf->ei_class == ELFCLASS32 ?
+    sizeof (auxv32[0]) : sizeof (auxv64[0]);
+
+  for (i = 0; i < len / entry_size; i++)
+    {
+      uint64_t auxv_type = elf->ei_class == ELFCLASS32 ?
+	auxv32[2 * i] : auxv64[2 * i];
+
+      if (auxv_type == 0)
+	break;
+      if (auxv_type != 16)
+	continue;
+
+      if (elf->ei_class == ELFCLASS32)
+	auxv32[2 * i + 1] &= (uint32_t) hwcap_mask;
+      else
+	auxv64[2 * i + 1] &= (uint64_t) hwcap_mask;
+    }
+}
+
+/* In the note segment starting at OFFSET with size LEN, make notes
+   with type NOTE_TYPE unrecognizable by GDB.  Also, mask the hwcap
+   field of any auxv notes using the HWCAP_MASK. */
+
+static void
+elfbuf_handle_note_segment (struct elfbuf *elf, size_t offset, size_t len,
+			    unsigned note_type, unsigned long hwcap_mask)
+{
+  size_t pos = 0;
+
+  while (pos + 12 < len)
+    {
+      uint32_t *note = (uint32_t *) (elf->buf + offset + pos);
+      size_t desc_pos = pos + 12 + ((note[0] + 3) & ~3);
+      size_t next_pos = desc_pos + ((note[1] + 3) & ~3);
+
+      if (desc_pos > len || next_pos > len)
+	exit_with_msg ("%s: corrupt notes data", elf->path);
+
+      if (note[2] == note_type)
+	note[2] |= 0xff000000;
+      else if (note[2] == 6 && hwcap_mask != 0)
+	elfbuf_handle_auxv (elf, offset + desc_pos, note[1],
+			    hwcap_mask);
+      pos = next_pos;
+    }
+}
+
+static void
+elfbuf_handle_core_notes (struct elfbuf *elf, unsigned note_type,
+			  unsigned long hwcap_mask)
+{
+  unsigned ph_idx;
+
+  if (ELFBUF_EHDR (elf, e_type) != 4)
+    exit_with_msg ("%s: not a core file", elf->path);
+
+  /* Iterate over program headers. */
+  for (ph_idx = 0; ph_idx != ELFBUF_EHDR (elf, e_phnum); ph_idx++)
+    {
+      size_t offset = ELFBUF_PHDR (elf, ph_idx, p_offset);
+      size_t filesz = ELFBUF_PHDR (elf, ph_idx, p_filesz);
+
+      if (offset > elf->len || filesz > elf->len - offset)
+	exit_with_msg ("%s: unexpected end of data", elf->path);
+
+      /* Deal with NOTE segments only. */
+      if (ELFBUF_PHDR (elf, ph_idx, p_type) != 4)
+	continue;
+      elfbuf_handle_note_segment (elf, offset, filesz, note_type,
+				  hwcap_mask);
+    }
+}
+
+int
+main (int argc, char *argv[])
+{
+  unsigned note_type;
+  unsigned long hwcap_mask = 0;
+  struct elfbuf elf;
+
+  if (argc < 4)
+    {
+      abort ();
+    }
+
+  if (sscanf (argv[3], "%u", &note_type) != 1)
+    exit_with_msg ("%s: bad command line arguments\n", argv[0]);
+
+  if (argc >= 5)
+    {
+      if (sscanf (argv[4], "%lu", &hwcap_mask) != 1)
+	exit_with_msg ("%s: bad command line arguments\n", argv[0]);
+    }
+
+  elfbuf_init_from_file (&elf, argv[1]);
+  elfbuf_handle_core_notes (&elf, note_type, hwcap_mask);
+  elfbuf_write_to_file (&elf, argv[2]);
+
+  return 0;
+}
Index: gdb/gdb/testsuite/gdb.arch/s390-multiarch.exp
===================================================================
--- /dev/null
+++ gdb/gdb/testsuite/gdb.arch/s390-multiarch.exp
@@ -0,0 +1,159 @@
+# Copyright 2013 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/>.
+
+# Please email any bugs, comments, and/or additions to this file to:
+# bug-gdb@gnu.org
+
+# This file is part of the gdb testsuite.
+
+
+# This test is supported on 64-bit s390 targets only, and only when
+# running native.  It should be executed on a sufficiently new Linux
+# kernel that provides the 'system_call' regset.
+
+if { ![isnative] || ![istarget s390x-*-* ] } {
+    verbose "Skipping s390 multi-arch tests."
+    return
+}
+
+set testfile "s390-multiarch"
+set srcfile "${srcdir}/${subdir}/${testfile}.c"
+set binprefix "${objdir}/${subdir}/${testfile}"
+
+gdb_exit
+
+if { [get_compiler_info] } {
+    return -1
+}
+
+proc compile_and_dump {variant ccopts binfile} {
+    global srcfile
+    set compile_flags {debug}
+    foreach opt $ccopts {
+	lappend compile_flags "additional_flags=$opt"
+    }
+    set test "compile ($variant)"
+    if { [gdb_compile $srcfile $binfile executable $compile_flags] != "" } {
+	fail $test
+	return {}
+    }
+    pass $test
+
+    set test "create core file ($variant)"
+    set corefile [core_find $binfile]
+    if {$corefile == ""} {
+	fail $test
+	return {}
+    }
+    pass $test
+    return $corefile
+}
+
+proc test_linux_v2 {} {
+    set test "Linux v2"
+    gdb_test_multiple "info reg system_call" "$test" {
+	-re "system_call\[ \t\]+0x\[0-9a-z\]+\t.*" {
+	    pass "$test"
+	    return 1
+	}
+	-re "Invalid register `system_call'.*" {
+	    unsupported "$test (no system_call reg)"
+	    return 0
+	}
+    }
+    return 0
+}
+
+proc test_register_valid {reg variant} {
+    gdb_test "info reg $reg" \
+	"$reg\[ \t\]+0x\[0-9a-z\]+\t.*" \
+	"'$reg' exists ($variant)"
+}
+
+proc test_register_invalid {reg variant} {
+    gdb_test "info reg $reg" \
+	"Invalid register `$reg'.*" \
+	"'$reg' must not exist ($variant)"
+}
+
+proc test_all_core64 {core type} {
+    set variant "64-bit $type"
+    gdb_core_cmd $core "core-file ($variant)"
+    if { ! [test_linux_v2] } {
+	return
+    }
+    test_register_valid "last_break" $variant
+    gdb_core_cmd "${core}.2" "core-file #2 ($variant)"
+    test_register_invalid "system_call" $variant
+    gdb_core_cmd "${core}.3" "core-file #3 ($variant)"
+    test_register_invalid "last_break" $variant
+}
+
+proc test_all_core31 {core type} {
+    set variant "31-bit $type"
+    gdb_core_cmd $core "core-file ($variant)"
+    if { ! [test_linux_v2] } {
+	return
+    }
+    test_register_valid "r0h" $variant
+    test_register_valid "last_break" $variant
+    gdb_core_cmd "${core}.1" "core-file #1 ($variant)"
+    test_register_invalid "r0h" $variant
+    gdb_core_cmd "${core}.2" "core-file #2 ($variant)"
+    test_register_invalid "system_call" $variant
+    gdb_core_cmd "${core}.3" "core-file #3 ($variant)"
+    test_register_invalid "last_break" $variant
+}
+
+set binfile "${binprefix}-64"
+set core64 [compile_and_dump 64 {-m64} $binfile]
+if { $core64 != "" } {
+    # Remove 'system_call' and mask hwcap
+    remote_exec host "$binfile $core64 ${core64}.2 775 1023"
+    # Remove 'last_break'
+    remote_exec host "$binfile ${core64}.2 ${core64}.3 774"
+}
+
+set binfile "${binprefix}-31"
+set core31 [compile_and_dump 31 {-m31 -mesa} $binfile]
+if { $core31 != "" } {
+    # Create "patched" core file by removing 'high_gprs' notes
+    remote_exec host "$binfile $core31 ${core31}.1 768"
+    # Remove 'system_call' and mask off TE and any newer capabilities
+    # from hwcap
+    remote_exec host "$binfile $core31 ${core31}.2 775 1023"
+    # Remove 'last_break'
+    remote_exec host "$binfile ${core31}.2 ${core31}.3 774"
+}
+
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+
+if { $core64 != "" } {
+    test_all_core64 $core64 "no exec"
+    gdb_load "${binprefix}-64"
+    test_all_core64 $core64 "with exec"
+}
+
+gdb_test "file" ".*" "discard symbol table" \
+    {Discard symbol table from `.*'\? \(y or n\) } "y"
+
+if { $core31 != "" } {
+    test_all_core31 $core31 "no exec"
+    gdb_load "${binprefix}-31"
+    test_all_core31 $core31 "with exec"
+}
+
+gdb_exit


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]