diff options
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/testsuite/ChangeLog | 5 | ||||
-rw-r--r-- | gdb/testsuite/gdb.arch/s390-multiarch.c | 314 | ||||
-rw-r--r-- | gdb/testsuite/gdb.arch/s390-multiarch.exp | 159 |
3 files changed, 478 insertions, 0 deletions
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 45da6fd..6cd8c23 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -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 index 0000000..f464fac --- /dev/null +++ b/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", ¬e_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 index 0000000..e31ad17 --- /dev/null +++ b/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 |