testsuite/ChangeLog:
[external/binutils.git] / gdb / testsuite / gdb.arch / s390-multiarch.c
1 /* Copyright 2013 Free Software Foundation, Inc.
2
3    This file is part of GDB.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 #include <errno.h>
19 #include <stdarg.h>
20 #include <stdint.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23
24 typedef struct
25 {
26   unsigned char e_ident[16];
27   uint16_t      e_type;
28   uint16_t      e_machine;
29   uint32_t      e_version;
30   uint32_t      e_entry;
31   uint32_t      e_phoff;
32   uint32_t      e_shoff;
33   uint32_t      e_flags;
34   uint16_t      e_ehsize;
35   uint16_t      e_phentsize;
36   uint16_t      e_phnum;
37   uint16_t      e_shentsize;
38   uint16_t      e_shnum;
39   uint16_t      e_shstrndx;
40 } Elf32_Ehdr;
41
42 typedef struct
43 {
44   unsigned char e_ident[16];
45   uint16_t      e_type;
46   uint16_t      e_machine;
47   uint32_t      e_version;
48   uint64_t      e_entry;
49   uint64_t      e_phoff;
50   uint64_t      e_shoff;
51   uint32_t      e_flags;
52   uint16_t      e_ehsize;
53   uint16_t      e_phentsize;
54   uint16_t      e_phnum;
55   uint16_t      e_shentsize;
56   uint16_t      e_shnum;
57   uint16_t      e_shstrndx;
58 } Elf64_Ehdr;
59
60 typedef struct
61 {
62   uint32_t      p_type;
63   uint32_t      p_offset;
64   uint32_t      p_vaddr;
65   uint32_t      p_paddr;
66   uint32_t      p_filesz;
67   uint32_t      p_memsz;
68   uint32_t      p_flags;
69   uint32_t      p_align;
70 } Elf32_Phdr;
71
72 typedef struct
73 {
74   uint32_t      p_type;
75   uint32_t      p_flags;
76   uint64_t      p_offset;
77   uint64_t      p_vaddr;
78   uint64_t      p_paddr;
79   uint64_t      p_filesz;
80   uint64_t      p_memsz;
81   uint64_t      p_align;
82 } Elf64_Phdr;
83
84 struct elfbuf
85 {
86   const char *path;
87   unsigned char *buf;
88   size_t len;
89   enum { ELFCLASS32 = 1,
90          ELFCLASS64 = 2 } ei_class;
91 };
92
93 #define ELFBUF_EHDR_LEN(elf)                                    \
94   ((elf)->ei_class == ELFCLASS32 ? sizeof (Elf32_Ehdr) :        \
95    sizeof (Elf64_Ehdr))
96
97 #define ELFBUF_EHDR(elf, memb)                  \
98   ((elf)->ei_class == ELFCLASS32 ?              \
99    ((Elf32_Ehdr *) (elf)->buf)->memb            \
100    : ((Elf64_Ehdr *) (elf)->buf)->memb)
101
102 #define ELFBUF_PHDR_LEN(elf)                                    \
103   ((elf)->ei_class == ELFCLASS32 ? sizeof (Elf32_Phdr) :        \
104    sizeof (Elf64_Phdr))
105
106 #define ELFBUF_PHDR(elf, idx, memb)                             \
107   ((elf)->ei_class == ELFCLASS32 ?                              \
108    ((Elf32_Phdr *) &(elf)->buf[((Elf32_Ehdr *)(elf)->buf)       \
109                                ->e_phoff])[idx].memb            \
110    : ((Elf64_Phdr *) &(elf)->buf[((Elf64_Ehdr *)(elf)->buf)     \
111                                  ->e_phoff])[idx].memb)
112
113 static void
114 exit_with_msg(const char *fmt, ...)
115 {
116   va_list ap;
117
118   fflush (stdout);
119   va_start (ap, fmt);
120   vfprintf (stderr, fmt, ap);
121   va_end (ap);
122
123   if (errno)
124     {
125       fputs (": ", stderr);
126       perror (NULL);
127     }
128   else
129     fputc ('\n', stderr);
130   exit (1);
131 }
132
133 static void
134 read_file (unsigned char **buf_ptr, size_t *len_ptr, FILE *fp)
135 {
136   size_t len = 0;
137   size_t size = 1024;
138   size_t chunk;
139   unsigned char *buf = malloc (size);
140
141   while ((chunk = fread (buf + len, 1, size - len, fp)) == size - len)
142     {
143       len = size;
144       size *= 2;
145       buf = realloc (buf, size);
146     }
147   len += chunk;
148   *buf_ptr = buf;
149   *len_ptr = len;
150 }
151
152 static void
153 write_file (unsigned char *buf, size_t len, FILE *fp)
154 {
155   fwrite (buf, 1, len, fp);
156 }
157
158 static void
159 elfbuf_init_from_file (struct elfbuf *elf, const char *path)
160 {
161   FILE *fp = fopen (path, "rb");
162   unsigned char *buf;
163   size_t len;
164
165   if (fp == NULL)
166     exit_with_msg ("%s", path);
167
168   read_file (&buf, &len, fp);
169   fclose (fp);
170
171   /* Validate ELF identification. */
172   if (len < 16
173       || buf[0] != 0x7f || buf[1] != 0x45 || buf[2] != 0x4c || buf[3] != 0x46
174       || buf[4] < 1 || buf[4] > 2 || buf[5] < 1 || buf[5] > 2)
175     exit_with_msg ("%s: unsupported or invalid ELF file", path);
176
177   elf->path = path;
178   elf->buf = buf;
179   elf->len = len;
180   elf->ei_class = buf[4];
181
182   if (ELFBUF_EHDR_LEN (elf) > len
183       || ELFBUF_EHDR (elf, e_phoff) > len
184       || ELFBUF_EHDR (elf, e_phnum) > ((len - ELFBUF_EHDR (elf, e_phoff))
185                                        / ELFBUF_PHDR_LEN (elf)) )
186     exit_with_msg ("%s: unexpected end of data", path);
187
188   if (ELFBUF_EHDR (elf, e_phentsize) != ELFBUF_PHDR_LEN (elf))
189     exit_with_msg ("%s: inconsistent ELF header", path);
190 }
191
192 static void
193 elfbuf_write_to_file (struct elfbuf *elf, const char *path)
194 {
195   FILE *fp = fopen (path, "wb");
196
197   if (fp == NULL)
198     exit_with_msg ("%s", path);
199
200   write_file (elf->buf, elf->len, fp);
201   fclose (fp);
202 }
203
204 /* In the auxv note starting at OFFSET with size LEN, mask the hwcap
205    field using the HWCAP_MASK. */
206
207 static void
208 elfbuf_handle_auxv (struct elfbuf *elf, size_t offset, size_t len,
209                     unsigned long hwcap_mask)
210 {
211   size_t i;
212   uint32_t *auxv32 = (uint32_t *) (elf->buf + offset);
213   uint64_t *auxv64 = (uint64_t *) auxv32;
214   size_t entry_size = elf->ei_class == ELFCLASS32 ?
215     sizeof (auxv32[0]) : sizeof (auxv64[0]);
216
217   for (i = 0; i < len / entry_size; i++)
218     {
219       uint64_t auxv_type = elf->ei_class == ELFCLASS32 ?
220         auxv32[2 * i] : auxv64[2 * i];
221
222       if (auxv_type == 0)
223         break;
224       if (auxv_type != 16)
225         continue;
226
227       if (elf->ei_class == ELFCLASS32)
228         auxv32[2 * i + 1] &= (uint32_t) hwcap_mask;
229       else
230         auxv64[2 * i + 1] &= (uint64_t) hwcap_mask;
231     }
232 }
233
234 /* In the note segment starting at OFFSET with size LEN, make notes
235    with type NOTE_TYPE unrecognizable by GDB.  Also, mask the hwcap
236    field of any auxv notes using the HWCAP_MASK. */
237
238 static void
239 elfbuf_handle_note_segment (struct elfbuf *elf, size_t offset, size_t len,
240                             unsigned note_type, unsigned long hwcap_mask)
241 {
242   size_t pos = 0;
243
244   while (pos + 12 < len)
245     {
246       uint32_t *note = (uint32_t *) (elf->buf + offset + pos);
247       size_t desc_pos = pos + 12 + ((note[0] + 3) & ~3);
248       size_t next_pos = desc_pos + ((note[1] + 3) & ~3);
249
250       if (desc_pos > len || next_pos > len)
251         exit_with_msg ("%s: corrupt notes data", elf->path);
252
253       if (note[2] == note_type)
254         note[2] |= 0xff000000;
255       else if (note[2] == 6 && hwcap_mask != 0)
256         elfbuf_handle_auxv (elf, offset + desc_pos, note[1],
257                             hwcap_mask);
258       pos = next_pos;
259     }
260 }
261
262 static void
263 elfbuf_handle_core_notes (struct elfbuf *elf, unsigned note_type,
264                           unsigned long hwcap_mask)
265 {
266   unsigned ph_idx;
267
268   if (ELFBUF_EHDR (elf, e_type) != 4)
269     exit_with_msg ("%s: not a core file", elf->path);
270
271   /* Iterate over program headers. */
272   for (ph_idx = 0; ph_idx != ELFBUF_EHDR (elf, e_phnum); ph_idx++)
273     {
274       size_t offset = ELFBUF_PHDR (elf, ph_idx, p_offset);
275       size_t filesz = ELFBUF_PHDR (elf, ph_idx, p_filesz);
276
277       if (offset > elf->len || filesz > elf->len - offset)
278         exit_with_msg ("%s: unexpected end of data", elf->path);
279
280       /* Deal with NOTE segments only. */
281       if (ELFBUF_PHDR (elf, ph_idx, p_type) != 4)
282         continue;
283       elfbuf_handle_note_segment (elf, offset, filesz, note_type,
284                                   hwcap_mask);
285     }
286 }
287
288 int
289 main (int argc, char *argv[])
290 {
291   unsigned note_type;
292   unsigned long hwcap_mask = 0;
293   struct elfbuf elf;
294
295   if (argc < 4)
296     {
297       abort ();
298     }
299
300   if (sscanf (argv[3], "%u", &note_type) != 1)
301     exit_with_msg ("%s: bad command line arguments\n", argv[0]);
302
303   if (argc >= 5)
304     {
305       if (sscanf (argv[4], "%lu", &hwcap_mask) != 1)
306         exit_with_msg ("%s: bad command line arguments\n", argv[0]);
307     }
308
309   elfbuf_init_from_file (&elf, argv[1]);
310   elfbuf_handle_core_notes (&elf, note_type, hwcap_mask);
311   elfbuf_write_to_file (&elf, argv[2]);
312
313   return 0;
314 }