testsuite/ChangeLog:
authorUlrich Weigand <uweigand@de.ibm.com>
Fri, 11 Oct 2013 18:50:56 +0000 (18:50 +0000)
committerUlrich Weigand <uweigand@de.ibm.com>
Fri, 11 Oct 2013 18:50:56 +0000 (18:50 +0000)
2013-10-11  Andreas Arnez  <arnez@linux.vnet.ibm.com>

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

gdb/testsuite/ChangeLog
gdb/testsuite/gdb.arch/s390-multiarch.c [new file with mode: 0644]
gdb/testsuite/gdb.arch/s390-multiarch.exp [new file with mode: 0644]

index 45da6fd..6cd8c23 100644 (file)
@@ -1,3 +1,8 @@
+2013-10-11  Andreas Arnez  <arnez@linux.vnet.ibm.com>
+
+       * gdb.arch/s390-multiarch.exp: New file.
+       * gdb.arch/s390-multiarch.c: New file.
+
 2013-10-11  Joel Brobecker  <brobecker@adacore.com>
 
        * gdb.ada/mi_catch_ex.exp: Adjusts all "catch ..." tests to
diff --git a/gdb/testsuite/gdb.arch/s390-multiarch.c b/gdb/testsuite/gdb.arch/s390-multiarch.c
new file mode 100644 (file)
index 0000000..f464fac
--- /dev/null
@@ -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;
+}
diff --git a/gdb/testsuite/gdb.arch/s390-multiarch.exp b/gdb/testsuite/gdb.arch/s390-multiarch.exp
new file mode 100644 (file)
index 0000000..e31ad17
--- /dev/null
@@ -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