1/* PEF support for BFD.
2   Copyright (C) 1999-2017 Free Software Foundation, Inc.
3
4   This file is part of BFD, the Binary File Descriptor library.
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 3 of the License, or
9   (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
19   MA 02110-1301, USA.  */
20
21/* PEF (Preferred Executable Format) is the binary file format for late
22   classic Mac OS versions (before Darwin).  It is supported by both m68k
23   and PowerPc.  It is also called CFM (Code Fragment Manager).  */
24
25#include "sysdep.h"
26#include "safe-ctype.h"
27#include "pef.h"
28#include "pef-traceback.h"
29#include "bfd.h"
30#include "libbfd.h"
31#include "libiberty.h"
32
33#ifndef BFD_IO_FUNCS
34#define BFD_IO_FUNCS 0
35#endif
36
37#define bfd_pef_close_and_cleanup                   _bfd_generic_close_and_cleanup
38#define bfd_pef_bfd_free_cached_info                _bfd_generic_bfd_free_cached_info
39#define bfd_pef_new_section_hook                    _bfd_generic_new_section_hook
40#define bfd_pef_bfd_is_local_label_name             bfd_generic_is_local_label_name
41#define bfd_pef_bfd_is_target_special_symbol ((bfd_boolean (*) (bfd *, asymbol *)) bfd_false)
42#define bfd_pef_get_lineno                          _bfd_nosymbols_get_lineno
43#define bfd_pef_find_nearest_line                   _bfd_nosymbols_find_nearest_line
44#define bfd_pef_find_line                           _bfd_nosymbols_find_line
45#define bfd_pef_find_inliner_info                   _bfd_nosymbols_find_inliner_info
46#define bfd_pef_get_symbol_version_string	    _bfd_nosymbols_get_symbol_version_string
47#define bfd_pef_bfd_make_debug_symbol               _bfd_nosymbols_bfd_make_debug_symbol
48#define bfd_pef_read_minisymbols                    _bfd_generic_read_minisymbols
49#define bfd_pef_minisymbol_to_symbol                _bfd_generic_minisymbol_to_symbol
50#define bfd_pef_set_arch_mach                       _bfd_generic_set_arch_mach
51#define bfd_pef_get_section_contents                _bfd_generic_get_section_contents
52#define bfd_pef_set_section_contents                _bfd_generic_set_section_contents
53#define bfd_pef_bfd_get_relocated_section_contents  bfd_generic_get_relocated_section_contents
54#define bfd_pef_bfd_relax_section                   bfd_generic_relax_section
55#define bfd_pef_bfd_gc_sections                     bfd_generic_gc_sections
56#define bfd_pef_bfd_lookup_section_flags            bfd_generic_lookup_section_flags
57#define bfd_pef_bfd_merge_sections                  bfd_generic_merge_sections
58#define bfd_pef_bfd_is_group_section		    bfd_generic_is_group_section
59#define bfd_pef_bfd_discard_group                   bfd_generic_discard_group
60#define bfd_pef_section_already_linked	            _bfd_generic_section_already_linked
61#define bfd_pef_bfd_define_common_symbol            bfd_generic_define_common_symbol
62#define bfd_pef_bfd_link_hash_table_create          _bfd_generic_link_hash_table_create
63#define bfd_pef_bfd_link_add_symbols                _bfd_generic_link_add_symbols
64#define bfd_pef_bfd_link_just_syms                  _bfd_generic_link_just_syms
65#define bfd_pef_bfd_copy_link_hash_symbol_type \
66  _bfd_generic_copy_link_hash_symbol_type
67#define bfd_pef_bfd_final_link                      _bfd_generic_final_link
68#define bfd_pef_bfd_link_split_section              _bfd_generic_link_split_section
69#define bfd_pef_get_section_contents_in_window      _bfd_generic_get_section_contents_in_window
70#define bfd_pef_bfd_link_check_relocs               _bfd_generic_link_check_relocs
71
72static int
73bfd_pef_parse_traceback_table (bfd *abfd,
74			       asection *section,
75			       unsigned char *buf,
76			       size_t len,
77			       size_t pos,
78			       asymbol *sym,
79			       FILE *file)
80{
81  struct traceback_table table;
82  size_t offset;
83  const char *s;
84  asymbol tmpsymbol;
85
86  if (sym == NULL)
87    sym = & tmpsymbol;
88
89  sym->name = NULL;
90  sym->value = 0;
91  sym->the_bfd = abfd;
92  sym->section = section;
93  sym->flags = 0;
94  sym->udata.i = 0;
95
96  /* memcpy is fine since all fields are unsigned char.  */
97  if ((pos + 8) > len)
98    return -1;
99  memcpy (&table, buf + pos, 8);
100
101  /* Calling code relies on returned symbols having a name and
102     correct offset.  */
103  if ((table.lang != TB_C) && (table.lang != TB_CPLUSPLUS))
104    return -1;
105
106  if (! (table.flags2 & TB_NAME_PRESENT))
107    return -1;
108
109  if (! (table.flags1 & TB_HAS_TBOFF))
110    return -1;
111
112  offset = 8;
113
114  if ((table.flags5 & TB_FLOATPARAMS) || (table.fixedparams))
115    offset += 4;
116
117  if (table.flags1 & TB_HAS_TBOFF)
118    {
119      struct traceback_table_tboff off;
120
121      if ((pos + offset + 4) > len)
122	return -1;
123      off.tb_offset = bfd_getb32 (buf + pos + offset);
124      offset += 4;
125
126      /* Need to subtract 4 because the offset includes the 0x0L
127	 preceding the table.  */
128      if (file != NULL)
129	fprintf (file, " [offset = 0x%lx]", off.tb_offset);
130
131      if ((file == NULL) && ((off.tb_offset + 4) > (pos + offset)))
132	return -1;
133
134      sym->value = pos - off.tb_offset - 4;
135    }
136
137  if (table.flags2 & TB_INT_HNDL)
138    offset += 4;
139
140  if (table.flags1 & TB_HAS_CTL)
141    {
142      struct traceback_table_anchors anchors;
143
144      if ((pos + offset + 4) > len)
145	return -1;
146      anchors.ctl_info = bfd_getb32 (buf + pos + offset);
147      offset += 4;
148
149      if (anchors.ctl_info > 1024)
150	return -1;
151
152      offset += anchors.ctl_info * 4;
153    }
154
155  if (table.flags2 & TB_NAME_PRESENT)
156    {
157      struct traceback_table_routine name;
158      char *namebuf;
159
160      if ((pos + offset + 2) > len)
161	return -1;
162      name.name_len = bfd_getb16 (buf + pos + offset);
163      offset += 2;
164
165      if (name.name_len > 4096)
166	return -1;
167
168      if ((pos + offset + name.name_len) > len)
169	return -1;
170
171      namebuf = bfd_alloc (abfd, name.name_len + 1);
172      if (namebuf == NULL)
173	return -1;
174
175      memcpy (namebuf, buf + pos + offset, name.name_len);
176      namebuf[name.name_len] = '\0';
177
178      /* Strip leading period inserted by compiler.  */
179      if (namebuf[0] == '.')
180	memmove (namebuf, namebuf + 1, name.name_len + 1);
181
182      sym->name = namebuf;
183
184      for (s = sym->name; (*s != '\0'); s++)
185	if (! ISPRINT (*s))
186	  return -1;
187
188      offset += name.name_len;
189    }
190
191  if (table.flags2 & TB_USES_ALLOCA)
192    offset += 4;
193
194  if (table.flags4 & TB_HAS_VEC_INFO)
195    offset += 4;
196
197  if (file != NULL)
198    fprintf (file, " [length = 0x%lx]", (unsigned long) offset);
199
200  return offset;
201}
202
203static void
204bfd_pef_print_symbol (bfd *abfd,
205		      void * afile,
206		      asymbol *symbol,
207		      bfd_print_symbol_type how)
208{
209  FILE *file = (FILE *) afile;
210
211  switch (how)
212    {
213    case bfd_print_symbol_name:
214      fprintf (file, "%s", symbol->name);
215      break;
216    default:
217      bfd_print_symbol_vandf (abfd, (void *) file, symbol);
218      fprintf (file, " %-5s %s", symbol->section->name, symbol->name);
219      if (CONST_STRNEQ (symbol->name, "__traceback_"))
220	{
221	  unsigned char *buf = xmalloc (symbol->udata.i);
222	  size_t offset = symbol->value + 4;
223	  size_t len = symbol->udata.i;
224	  int ret;
225
226	  bfd_get_section_contents (abfd, symbol->section, buf, offset, len);
227	  ret = bfd_pef_parse_traceback_table (abfd, symbol->section, buf,
228					       len, 0, NULL, file);
229	  if (ret < 0)
230	    fprintf (file, " [ERROR]");
231	  free (buf);
232	}
233    }
234}
235
236static void
237bfd_pef_convert_architecture (unsigned long architecture,
238			      enum bfd_architecture *type,
239			      unsigned long *subtype)
240{
241  const unsigned long ARCH_POWERPC = 0x70777063; /* 'pwpc'.  */
242  const unsigned long ARCH_M68K = 0x6d36386b; /* 'm68k'.  */
243
244  *subtype = bfd_arch_unknown;
245  *type = bfd_arch_unknown;
246
247  if (architecture == ARCH_POWERPC)
248    *type = bfd_arch_powerpc;
249  else if (architecture == ARCH_M68K)
250    *type = bfd_arch_m68k;
251}
252
253static bfd_boolean
254bfd_pef_mkobject (bfd *abfd ATTRIBUTE_UNUSED)
255{
256  return TRUE;
257}
258
259static const char *bfd_pef_section_name (bfd_pef_section *section)
260{
261  switch (section->section_kind)
262    {
263    case BFD_PEF_SECTION_CODE: return "code";
264    case BFD_PEF_SECTION_UNPACKED_DATA: return "unpacked-data";
265    case BFD_PEF_SECTION_PACKED_DATA: return "packed-data";
266    case BFD_PEF_SECTION_CONSTANT: return "constant";
267    case BFD_PEF_SECTION_LOADER: return "loader";
268    case BFD_PEF_SECTION_DEBUG: return "debug";
269    case BFD_PEF_SECTION_EXEC_DATA: return "exec-data";
270    case BFD_PEF_SECTION_EXCEPTION: return "exception";
271    case BFD_PEF_SECTION_TRACEBACK: return "traceback";
272    default: return "unknown";
273    }
274}
275
276static unsigned long bfd_pef_section_flags (bfd_pef_section *section)
277{
278  switch (section->section_kind)
279    {
280    case BFD_PEF_SECTION_CODE:
281      return SEC_HAS_CONTENTS | SEC_LOAD | SEC_ALLOC | SEC_CODE;
282    case BFD_PEF_SECTION_UNPACKED_DATA:
283    case BFD_PEF_SECTION_PACKED_DATA:
284    case BFD_PEF_SECTION_CONSTANT:
285    case BFD_PEF_SECTION_LOADER:
286    case BFD_PEF_SECTION_DEBUG:
287    case BFD_PEF_SECTION_EXEC_DATA:
288    case BFD_PEF_SECTION_EXCEPTION:
289    case BFD_PEF_SECTION_TRACEBACK:
290    default:
291      return SEC_HAS_CONTENTS | SEC_LOAD | SEC_ALLOC;
292    }
293}
294
295static asection *
296bfd_pef_make_bfd_section (bfd *abfd, bfd_pef_section *section)
297{
298  asection *bfdsec;
299  const char *name = bfd_pef_section_name (section);
300
301  bfdsec = bfd_make_section_anyway (abfd, name);
302  if (bfdsec == NULL)
303    return NULL;
304
305  bfdsec->vma = section->default_address + section->container_offset;
306  bfdsec->lma = section->default_address + section->container_offset;
307  bfdsec->size = section->container_length;
308  bfdsec->filepos = section->container_offset;
309  bfdsec->alignment_power = section->alignment;
310
311  bfdsec->flags = bfd_pef_section_flags (section);
312
313  return bfdsec;
314}
315
316int
317bfd_pef_parse_loader_header (bfd *abfd ATTRIBUTE_UNUSED,
318			     unsigned char *buf,
319			     size_t len,
320			     bfd_pef_loader_header *header)
321{
322  BFD_ASSERT (len == 56);
323
324  header->main_section = bfd_getb32 (buf);
325  header->main_offset = bfd_getb32 (buf + 4);
326  header->init_section = bfd_getb32 (buf + 8);
327  header->init_offset = bfd_getb32 (buf + 12);
328  header->term_section = bfd_getb32 (buf + 16);
329  header->term_offset = bfd_getb32 (buf + 20);
330  header->imported_library_count = bfd_getb32 (buf + 24);
331  header->total_imported_symbol_count = bfd_getb32 (buf + 28);
332  header->reloc_section_count = bfd_getb32 (buf + 32);
333  header->reloc_instr_offset = bfd_getb32 (buf + 36);
334  header->loader_strings_offset = bfd_getb32 (buf + 40);
335  header->export_hash_offset = bfd_getb32 (buf + 44);
336  header->export_hash_table_power = bfd_getb32 (buf + 48);
337  header->exported_symbol_count = bfd_getb32 (buf + 52);
338
339  return 0;
340}
341
342int
343bfd_pef_parse_imported_library (bfd *abfd ATTRIBUTE_UNUSED,
344				unsigned char *buf,
345				size_t len,
346				bfd_pef_imported_library *header)
347{
348  BFD_ASSERT (len == 24);
349
350  header->name_offset = bfd_getb32 (buf);
351  header->old_implementation_version = bfd_getb32 (buf + 4);
352  header->current_version = bfd_getb32 (buf + 8);
353  header->imported_symbol_count = bfd_getb32 (buf + 12);
354  header->first_imported_symbol = bfd_getb32 (buf + 16);
355  header->options = buf[20];
356  header->reserved_a = buf[21];
357  header->reserved_b = bfd_getb16 (buf + 22);
358
359  return 0;
360}
361
362int
363bfd_pef_parse_imported_symbol (bfd *abfd ATTRIBUTE_UNUSED,
364			       unsigned char *buf,
365			       size_t len,
366			       bfd_pef_imported_symbol *symbol)
367{
368  unsigned long value;
369
370  BFD_ASSERT (len == 4);
371
372  value = bfd_getb32 (buf);
373  symbol->symbol_class = value >> 24;
374  symbol->name = value & 0x00ffffff;
375
376  return 0;
377}
378
379int
380bfd_pef_scan_section (bfd *abfd, bfd_pef_section *section)
381{
382  unsigned char buf[28];
383
384  bfd_seek (abfd, section->header_offset, SEEK_SET);
385  if (bfd_bread ((void *) buf, 28, abfd) != 28)
386    return -1;
387
388  section->name_offset = bfd_h_get_32 (abfd, buf);
389  section->default_address = bfd_h_get_32 (abfd, buf + 4);
390  section->total_length = bfd_h_get_32 (abfd, buf + 8);
391  section->unpacked_length = bfd_h_get_32 (abfd, buf + 12);
392  section->container_length = bfd_h_get_32 (abfd, buf + 16);
393  section->container_offset = bfd_h_get_32 (abfd, buf + 20);
394  section->section_kind = buf[24];
395  section->share_kind = buf[25];
396  section->alignment = buf[26];
397  section->reserved = buf[27];
398
399  section->bfd_section = bfd_pef_make_bfd_section (abfd, section);
400  if (section->bfd_section == NULL)
401    return -1;
402
403  return 0;
404}
405
406void
407bfd_pef_print_loader_header (bfd *abfd ATTRIBUTE_UNUSED,
408			     bfd_pef_loader_header *header,
409			     FILE *file)
410{
411  fprintf (file, "main_section: %ld\n", header->main_section);
412  fprintf (file, "main_offset: %lu\n", header->main_offset);
413  fprintf (file, "init_section: %ld\n", header->init_section);
414  fprintf (file, "init_offset: %lu\n", header->init_offset);
415  fprintf (file, "term_section: %ld\n", header->term_section);
416  fprintf (file, "term_offset: %lu\n", header->term_offset);
417  fprintf (file, "imported_library_count: %lu\n",
418	   header->imported_library_count);
419  fprintf (file, "total_imported_symbol_count: %lu\n",
420	   header->total_imported_symbol_count);
421  fprintf (file, "reloc_section_count: %lu\n", header->reloc_section_count);
422  fprintf (file, "reloc_instr_offset: %lu\n", header->reloc_instr_offset);
423  fprintf (file, "loader_strings_offset: %lu\n",
424	   header->loader_strings_offset);
425  fprintf (file, "export_hash_offset: %lu\n", header->export_hash_offset);
426  fprintf (file, "export_hash_table_power: %lu\n",
427	   header->export_hash_table_power);
428  fprintf (file, "exported_symbol_count: %lu\n",
429	   header->exported_symbol_count);
430}
431
432int
433bfd_pef_print_loader_section (bfd *abfd, FILE *file)
434{
435  bfd_pef_loader_header header;
436  asection *loadersec = NULL;
437  unsigned char *loaderbuf = NULL;
438  size_t loaderlen = 0;
439
440  loadersec = bfd_get_section_by_name (abfd, "loader");
441  if (loadersec == NULL)
442    return -1;
443
444  loaderlen = loadersec->size;
445  loaderbuf = bfd_malloc (loaderlen);
446
447  if (bfd_seek (abfd, loadersec->filepos, SEEK_SET) < 0
448      || bfd_bread ((void *) loaderbuf, loaderlen, abfd) != loaderlen
449      || loaderlen < 56
450      || bfd_pef_parse_loader_header (abfd, loaderbuf, 56, &header) < 0)
451    {
452      free (loaderbuf);
453      return -1;
454    }
455
456  bfd_pef_print_loader_header (abfd, &header, file);
457  return 0;
458}
459
460int
461bfd_pef_scan_start_address (bfd *abfd)
462{
463  bfd_pef_loader_header header;
464  asection *section;
465
466  asection *loadersec = NULL;
467  unsigned char *loaderbuf = NULL;
468  size_t loaderlen = 0;
469  int ret;
470
471  loadersec = bfd_get_section_by_name (abfd, "loader");
472  if (loadersec == NULL)
473    goto end;
474
475  loaderlen = loadersec->size;
476  loaderbuf = bfd_malloc (loaderlen);
477  if (bfd_seek (abfd, loadersec->filepos, SEEK_SET) < 0)
478    goto error;
479  if (bfd_bread ((void *) loaderbuf, loaderlen, abfd) != loaderlen)
480    goto error;
481
482  if (loaderlen < 56)
483    goto error;
484  ret = bfd_pef_parse_loader_header (abfd, loaderbuf, 56, &header);
485  if (ret < 0)
486    goto error;
487
488  if (header.main_section < 0)
489    goto end;
490
491  for (section = abfd->sections; section != NULL; section = section->next)
492    if ((long) (section->index + 1) == header.main_section)
493      break;
494
495  if (section == NULL)
496    goto error;
497
498  abfd->start_address = section->vma + header.main_offset;
499
500 end:
501  if (loaderbuf != NULL)
502    free (loaderbuf);
503  return 0;
504
505 error:
506  if (loaderbuf != NULL)
507    free (loaderbuf);
508  return -1;
509}
510
511int
512bfd_pef_scan (bfd *abfd,
513	      bfd_pef_header *header,
514	      bfd_pef_data_struct *mdata)
515{
516  unsigned int i;
517  enum bfd_architecture cputype;
518  unsigned long cpusubtype;
519
520  mdata->header = *header;
521
522  bfd_pef_convert_architecture (header->architecture, &cputype, &cpusubtype);
523  if (cputype == bfd_arch_unknown)
524    {
525      _bfd_error_handler (_("bfd_pef_scan: unknown architecture 0x%lx"),
526			  header->architecture);
527      return -1;
528    }
529  bfd_set_arch_mach (abfd, cputype, cpusubtype);
530
531  mdata->header = *header;
532
533  abfd->flags = (abfd->xvec->object_flags
534		 | (abfd->flags & (BFD_IN_MEMORY | BFD_IO_FUNCS)));
535
536  if (header->section_count != 0)
537    {
538      mdata->sections = bfd_alloc (abfd, header->section_count * sizeof (bfd_pef_section));
539
540      if (mdata->sections == NULL)
541	return -1;
542
543      for (i = 0; i < header->section_count; i++)
544	{
545	  bfd_pef_section *cur = &mdata->sections[i];
546	  cur->header_offset = 40 + (i * 28);
547	  if (bfd_pef_scan_section (abfd, cur) < 0)
548	    return -1;
549	}
550    }
551
552  if (bfd_pef_scan_start_address (abfd) < 0)
553    return -1;
554
555  abfd->tdata.pef_data = mdata;
556
557  return 0;
558}
559
560static int
561bfd_pef_read_header (bfd *abfd, bfd_pef_header *header)
562{
563  unsigned char buf[40];
564
565  bfd_seek (abfd, 0, SEEK_SET);
566
567  if (bfd_bread ((void *) buf, 40, abfd) != 40)
568    return -1;
569
570  header->tag1 = bfd_getb32 (buf);
571  header->tag2 = bfd_getb32 (buf + 4);
572  header->architecture = bfd_getb32 (buf + 8);
573  header->format_version = bfd_getb32 (buf + 12);
574  header->timestamp = bfd_getb32 (buf + 16);
575  header->old_definition_version = bfd_getb32 (buf + 20);
576  header->old_implementation_version = bfd_getb32 (buf + 24);
577  header->current_version = bfd_getb32 (buf + 28);
578  header->section_count = bfd_getb32 (buf + 32) + 1;
579  header->instantiated_section_count = bfd_getb32 (buf + 34);
580  header->reserved = bfd_getb32 (buf + 36);
581
582  return 0;
583}
584
585static const bfd_target *
586bfd_pef_object_p (bfd *abfd)
587{
588  bfd_pef_header header;
589  bfd_pef_data_struct *mdata;
590
591  if (bfd_pef_read_header (abfd, &header) != 0)
592    goto wrong;
593
594  if (header.tag1 != BFD_PEF_TAG1 || header.tag2 != BFD_PEF_TAG2)
595    goto wrong;
596
597  mdata = (bfd_pef_data_struct *) bfd_zalloc (abfd, sizeof (*mdata));
598  if (mdata == NULL)
599    goto fail;
600
601  if (bfd_pef_scan (abfd, &header, mdata))
602    goto wrong;
603
604  return abfd->xvec;
605
606 wrong:
607  bfd_set_error (bfd_error_wrong_format);
608
609 fail:
610  return NULL;
611}
612
613static int
614bfd_pef_parse_traceback_tables (bfd *abfd,
615				asection *sec,
616				unsigned char *buf,
617				size_t len,
618				long *nsym,
619				asymbol **csym)
620{
621  char *name;
622
623  asymbol function;
624  asymbol traceback;
625
626  const char *const tbprefix = "__traceback_";
627  size_t tbnamelen;
628
629  size_t pos = 0;
630  unsigned long count = 0;
631  int ret;
632
633  for (;;)
634    {
635      /* We're reading symbols two at a time.  */
636      if (csym && ((csym[count] == NULL) || (csym[count + 1] == NULL)))
637	break;
638
639      pos += 3;
640      pos -= (pos % 4);
641
642      while ((pos + 4) <= len)
643	{
644	  if (bfd_getb32 (buf + pos) == 0)
645	    break;
646	  pos += 4;
647	}
648
649      if ((pos + 4) > len)
650	break;
651
652      ret = bfd_pef_parse_traceback_table (abfd, sec, buf, len, pos + 4,
653					   &function, 0);
654      if (ret < 0)
655	{
656	  /* Skip over 0x0L to advance to next possible traceback table.  */
657	  pos += 4;
658	  continue;
659	}
660
661      BFD_ASSERT (function.name != NULL);
662
663      /* Don't bother to compute the name if we are just
664	 counting symbols.  */
665      if (csym)
666	{
667	  tbnamelen = strlen (tbprefix) + strlen (function.name);
668	  name = bfd_alloc (abfd, tbnamelen + 1);
669	  if (name == NULL)
670	    {
671	      bfd_release (abfd, (void *) function.name);
672	      function.name = NULL;
673	      break;
674	    }
675	  snprintf (name, tbnamelen + 1, "%s%s", tbprefix, function.name);
676	  traceback.name = name;
677	  traceback.value = pos;
678	  traceback.the_bfd = abfd;
679	  traceback.section = sec;
680	  traceback.flags = 0;
681	  traceback.udata.i = ret;
682
683	  *(csym[count]) = function;
684	  *(csym[count + 1]) = traceback;
685	}
686
687      pos += ret;
688      count += 2;
689    }
690
691  *nsym = count;
692  return 0;
693}
694
695static int
696bfd_pef_parse_function_stub (bfd *abfd ATTRIBUTE_UNUSED,
697			     unsigned char *buf,
698			     size_t len,
699			     unsigned long *offset)
700{
701  BFD_ASSERT (len == 24);
702
703  if ((bfd_getb32 (buf) & 0xffff0000) != 0x81820000)
704    return -1;
705  if (bfd_getb32 (buf + 4) != 0x90410014)
706    return -1;
707  if (bfd_getb32 (buf + 8) != 0x800c0000)
708    return -1;
709  if (bfd_getb32 (buf + 12) != 0x804c0004)
710    return -1;
711  if (bfd_getb32 (buf + 16) != 0x7c0903a6)
712    return -1;
713  if (bfd_getb32 (buf + 20) != 0x4e800420)
714    return -1;
715
716  if (offset != NULL)
717    *offset = (bfd_getb32 (buf) & 0x0000ffff) / 4;
718
719  return 0;
720}
721
722static int
723bfd_pef_parse_function_stubs (bfd *abfd,
724			      asection *codesec,
725			      unsigned char *codebuf,
726			      size_t codelen,
727			      unsigned char *loaderbuf,
728			      size_t loaderlen,
729			      unsigned long *nsym,
730			      asymbol **csym)
731{
732  const char *const sprefix = "__stub_";
733  size_t codepos = 0;
734  unsigned long count = 0;
735  bfd_pef_loader_header header;
736  bfd_pef_imported_library *libraries = NULL;
737  bfd_pef_imported_symbol *imports = NULL;
738  unsigned long i;
739  int ret;
740
741  if (loaderlen < 56)
742    goto error;
743
744  ret = bfd_pef_parse_loader_header (abfd, loaderbuf, 56, &header);
745  if (ret < 0)
746    goto error;
747
748  libraries = bfd_malloc
749    (header.imported_library_count * sizeof (bfd_pef_imported_library));
750  imports = bfd_malloc
751    (header.total_imported_symbol_count * sizeof (bfd_pef_imported_symbol));
752
753  if (loaderlen < (56 + (header.imported_library_count * 24)))
754    goto error;
755  for (i = 0; i < header.imported_library_count; i++)
756    {
757      ret = bfd_pef_parse_imported_library
758	(abfd, loaderbuf + 56 + (i * 24), 24, &libraries[i]);
759      if (ret < 0)
760	goto error;
761    }
762
763  if (loaderlen < (56 + (header.imported_library_count * 24)
764		   + (header.total_imported_symbol_count * 4)))
765    goto error;
766  for (i = 0; i < header.total_imported_symbol_count; i++)
767    {
768      ret = (bfd_pef_parse_imported_symbol
769	     (abfd,
770	      loaderbuf + 56 + (header.imported_library_count * 24) + (i * 4),
771	      4, &imports[i]));
772      if (ret < 0)
773	goto error;
774    }
775
776  codepos = 0;
777
778  for (;;)
779    {
780      asymbol sym;
781      const char *symname;
782      char *name;
783      unsigned long sym_index;
784
785      if (csym && (csym[count] == NULL))
786	break;
787
788      codepos += 3;
789      codepos -= (codepos % 4);
790
791      while ((codepos + 4) <= codelen)
792	{
793	  if ((bfd_getb32 (codebuf + codepos) & 0xffff0000) == 0x81820000)
794	    break;
795	  codepos += 4;
796	}
797
798      if ((codepos + 4) > codelen)
799	break;
800
801      ret = bfd_pef_parse_function_stub (abfd, codebuf + codepos, 24, &sym_index);
802      if (ret < 0)
803	{
804	  codepos += 24;
805	  continue;
806	}
807
808      if (sym_index >= header.total_imported_symbol_count)
809	{
810	  codepos += 24;
811	  continue;
812	}
813
814      {
815	size_t max, namelen;
816	const char *s;
817
818	if (loaderlen < (header.loader_strings_offset + imports[sym_index].name))
819	  goto error;
820
821	max = loaderlen - (header.loader_strings_offset + imports[sym_index].name);
822	symname = (char *) loaderbuf;
823	symname += header.loader_strings_offset + imports[sym_index].name;
824	namelen = 0;
825	for (s = symname; s < (symname + max); s++)
826	  {
827	    if (*s == '\0')
828	      break;
829	    if (! ISPRINT (*s))
830	      goto error;
831	    namelen++;
832	  }
833	if (*s != '\0')
834	  goto error;
835
836	name = bfd_alloc (abfd, strlen (sprefix) + namelen + 1);
837	if (name == NULL)
838	  break;
839
840	snprintf (name, strlen (sprefix) + namelen + 1, "%s%s",
841		  sprefix, symname);
842	sym.name = name;
843      }
844
845      sym.value = codepos;
846      sym.the_bfd = abfd;
847      sym.section = codesec;
848      sym.flags = 0;
849      sym.udata.i = 0;
850
851      codepos += 24;
852
853      if (csym != NULL)
854	*(csym[count]) = sym;
855
856      count++;
857    }
858
859  goto end;
860
861 end:
862  if (libraries != NULL)
863    free (libraries);
864  if (imports != NULL)
865    free (imports);
866  *nsym = count;
867  return 0;
868
869 error:
870  if (libraries != NULL)
871    free (libraries);
872  if (imports != NULL)
873    free (imports);
874  *nsym = count;
875  return -1;
876}
877
878static long
879bfd_pef_parse_symbols (bfd *abfd, asymbol **csym)
880{
881  unsigned long count = 0;
882
883  asection *codesec = NULL;
884  unsigned char *codebuf = NULL;
885  size_t codelen = 0;
886
887  asection *loadersec = NULL;
888  unsigned char *loaderbuf = NULL;
889  size_t loaderlen = 0;
890
891  codesec = bfd_get_section_by_name (abfd, "code");
892  if (codesec != NULL)
893    {
894      codelen = codesec->size;
895      codebuf = bfd_malloc (codelen);
896      if (bfd_seek (abfd, codesec->filepos, SEEK_SET) < 0)
897	goto end;
898      if (bfd_bread ((void *) codebuf, codelen, abfd) != codelen)
899	goto end;
900    }
901
902  loadersec = bfd_get_section_by_name (abfd, "loader");
903  if (loadersec != NULL)
904    {
905      loaderlen = loadersec->size;
906      loaderbuf = bfd_malloc (loaderlen);
907      if (bfd_seek (abfd, loadersec->filepos, SEEK_SET) < 0)
908	goto end;
909      if (bfd_bread ((void *) loaderbuf, loaderlen, abfd) != loaderlen)
910	goto end;
911    }
912
913  count = 0;
914  if (codesec != NULL)
915    {
916      long ncount = 0;
917      bfd_pef_parse_traceback_tables (abfd, codesec, codebuf, codelen,
918				      &ncount, csym);
919      count += ncount;
920    }
921
922  if ((codesec != NULL) && (loadersec != NULL))
923    {
924      unsigned long ncount = 0;
925      bfd_pef_parse_function_stubs
926	(abfd, codesec, codebuf, codelen, loaderbuf, loaderlen, &ncount,
927	 (csym != NULL) ? (csym + count) : NULL);
928      count += ncount;
929    }
930
931  if (csym != NULL)
932    csym[count] = NULL;
933
934 end:
935  if (codebuf != NULL)
936    free (codebuf);
937
938  if (loaderbuf != NULL)
939    free (loaderbuf);
940
941  return count;
942}
943
944static long
945bfd_pef_count_symbols (bfd *abfd)
946{
947  return bfd_pef_parse_symbols (abfd, NULL);
948}
949
950static long
951bfd_pef_get_symtab_upper_bound (bfd *abfd)
952{
953  long nsyms = bfd_pef_count_symbols (abfd);
954
955  if (nsyms < 0)
956    return nsyms;
957  return ((nsyms + 1) * sizeof (asymbol *));
958}
959
960static long
961bfd_pef_canonicalize_symtab (bfd *abfd, asymbol **alocation)
962{
963  long i;
964  asymbol *syms;
965  long ret;
966  long nsyms = bfd_pef_count_symbols (abfd);
967
968  if (nsyms < 0)
969    return nsyms;
970
971  syms = bfd_alloc (abfd, nsyms * sizeof (asymbol));
972  if (syms == NULL)
973    return -1;
974
975  for (i = 0; i < nsyms; i++)
976    alocation[i] = &syms[i];
977
978  alocation[nsyms] = NULL;
979
980  ret = bfd_pef_parse_symbols (abfd, alocation);
981  if (ret != nsyms)
982    return 0;
983
984  return ret;
985}
986
987#define bfd_pef_make_empty_symbol _bfd_generic_make_empty_symbol
988
989static void
990bfd_pef_get_symbol_info (bfd *abfd ATTRIBUTE_UNUSED,
991			 asymbol *symbol,
992			 symbol_info *ret)
993{
994  bfd_symbol_info (symbol, ret);
995}
996
997static int
998bfd_pef_sizeof_headers (bfd *abfd ATTRIBUTE_UNUSED,
999			struct bfd_link_info *info ATTRIBUTE_UNUSED)
1000{
1001  return 0;
1002}
1003
1004const bfd_target pef_vec =
1005{
1006  "pef",			/* Name.  */
1007  bfd_target_pef_flavour,	/* Flavour.  */
1008  BFD_ENDIAN_BIG,		/* Byteorder.  */
1009  BFD_ENDIAN_BIG,		/* Header_byteorder.  */
1010  (HAS_RELOC | EXEC_P |		/* Object flags.  */
1011   HAS_LINENO | HAS_DEBUG |
1012   HAS_SYMS | HAS_LOCALS | DYNAMIC | WP_TEXT | D_PAGED),
1013  (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE | SEC_DATA
1014   | SEC_ROM | SEC_HAS_CONTENTS), /* Section_flags.  */
1015  0,				/* Symbol_leading_char.  */
1016  ' ',				/* AR_pad_char.  */
1017  16,				/* AR_max_namelen.  */
1018  0,				/* match priority.  */
1019  bfd_getb64, bfd_getb_signed_64, bfd_putb64,
1020  bfd_getb32, bfd_getb_signed_32, bfd_putb32,
1021  bfd_getb16, bfd_getb_signed_16, bfd_putb16,	/* Data.  */
1022  bfd_getb64, bfd_getb_signed_64, bfd_putb64,
1023  bfd_getb32, bfd_getb_signed_32, bfd_putb32,
1024  bfd_getb16, bfd_getb_signed_16, bfd_putb16,	/* Headers.  */
1025  {				/* bfd_check_format.  */
1026    _bfd_dummy_target,
1027    bfd_pef_object_p,		/* bfd_check_format.  */
1028    _bfd_dummy_target,
1029    _bfd_dummy_target,
1030  },
1031  {				/* bfd_set_format.  */
1032    bfd_false,
1033    bfd_pef_mkobject,
1034    bfd_false,
1035    bfd_false,
1036  },
1037  {				/* bfd_write_contents.  */
1038    bfd_false,
1039    bfd_true,
1040    bfd_false,
1041    bfd_false,
1042  },
1043
1044  BFD_JUMP_TABLE_GENERIC (bfd_pef),
1045  BFD_JUMP_TABLE_COPY (_bfd_generic),
1046  BFD_JUMP_TABLE_CORE (_bfd_nocore),
1047  BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive),
1048  BFD_JUMP_TABLE_SYMBOLS (bfd_pef),
1049  BFD_JUMP_TABLE_RELOCS (_bfd_norelocs),
1050  BFD_JUMP_TABLE_WRITE (bfd_pef),
1051  BFD_JUMP_TABLE_LINK (bfd_pef),
1052  BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
1053
1054  NULL,
1055
1056  NULL
1057};
1058
1059#define bfd_pef_xlib_close_and_cleanup              _bfd_generic_close_and_cleanup
1060#define bfd_pef_xlib_bfd_free_cached_info           _bfd_generic_bfd_free_cached_info
1061#define bfd_pef_xlib_new_section_hook               _bfd_generic_new_section_hook
1062#define bfd_pef_xlib_get_section_contents           _bfd_generic_get_section_contents
1063#define bfd_pef_xlib_set_section_contents           _bfd_generic_set_section_contents
1064#define bfd_pef_xlib_get_section_contents_in_window _bfd_generic_get_section_contents_in_window
1065#define bfd_pef_xlib_set_section_contents_in_window _bfd_generic_set_section_contents_in_window
1066
1067static int
1068bfd_pef_xlib_read_header (bfd *abfd, bfd_pef_xlib_header *header)
1069{
1070  unsigned char buf[80];
1071
1072  bfd_seek (abfd, 0, SEEK_SET);
1073
1074  if (bfd_bread ((void *) buf, sizeof buf, abfd) != sizeof buf)
1075    return -1;
1076
1077  header->tag1 = bfd_getb32 (buf);
1078  header->tag2 = bfd_getb32 (buf + 4);
1079  header->current_format = bfd_getb32 (buf + 8);
1080  header->container_strings_offset = bfd_getb32 (buf + 12);
1081  header->export_hash_offset = bfd_getb32 (buf + 16);
1082  header->export_key_offset = bfd_getb32 (buf + 20);
1083  header->export_symbol_offset = bfd_getb32 (buf + 24);
1084  header->export_names_offset = bfd_getb32 (buf + 28);
1085  header->export_hash_table_power = bfd_getb32 (buf + 32);
1086  header->exported_symbol_count = bfd_getb32 (buf + 36);
1087  header->frag_name_offset = bfd_getb32 (buf + 40);
1088  header->frag_name_length = bfd_getb32 (buf + 44);
1089  header->dylib_path_offset = bfd_getb32 (buf + 48);
1090  header->dylib_path_length = bfd_getb32 (buf + 52);
1091  header->cpu_family = bfd_getb32 (buf + 56);
1092  header->cpu_model = bfd_getb32 (buf + 60);
1093  header->date_time_stamp = bfd_getb32 (buf + 64);
1094  header->current_version = bfd_getb32 (buf + 68);
1095  header->old_definition_version = bfd_getb32 (buf + 72);
1096  header->old_implementation_version = bfd_getb32 (buf + 76);
1097
1098  return 0;
1099}
1100
1101static int
1102bfd_pef_xlib_scan (bfd *abfd, bfd_pef_xlib_header *header)
1103{
1104  bfd_pef_xlib_data_struct *mdata = NULL;
1105
1106  mdata = bfd_alloc (abfd, sizeof (* mdata));
1107  if (mdata == NULL)
1108    return -1;
1109
1110  mdata->header = *header;
1111
1112  abfd->flags = (abfd->xvec->object_flags
1113		 | (abfd->flags & (BFD_IN_MEMORY | BFD_IO_FUNCS)));
1114
1115  abfd->tdata.pef_xlib_data = mdata;
1116
1117  return 0;
1118}
1119
1120static const bfd_target *
1121bfd_pef_xlib_object_p (bfd *abfd)
1122{
1123  bfd_pef_xlib_header header;
1124
1125  if (bfd_pef_xlib_read_header (abfd, &header) != 0)
1126    {
1127      bfd_set_error (bfd_error_wrong_format);
1128      return NULL;
1129    }
1130
1131  if ((header.tag1 != BFD_PEF_XLIB_TAG1)
1132      || ((header.tag2 != BFD_PEF_VLIB_TAG2)
1133	  && (header.tag2 != BFD_PEF_BLIB_TAG2)))
1134    {
1135      bfd_set_error (bfd_error_wrong_format);
1136      return NULL;
1137    }
1138
1139  if (bfd_pef_xlib_scan (abfd, &header) != 0)
1140    {
1141      bfd_set_error (bfd_error_wrong_format);
1142      return NULL;
1143    }
1144
1145  return abfd->xvec;
1146}
1147
1148const bfd_target pef_xlib_vec =
1149{
1150  "pef-xlib",			/* Name.  */
1151  bfd_target_pef_xlib_flavour,	/* Flavour.  */
1152  BFD_ENDIAN_BIG,		/* Byteorder */
1153  BFD_ENDIAN_BIG,		/* Header_byteorder.  */
1154  (HAS_RELOC | EXEC_P |		/* Object flags.  */
1155   HAS_LINENO | HAS_DEBUG |
1156   HAS_SYMS | HAS_LOCALS | DYNAMIC | WP_TEXT | D_PAGED),
1157  (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE | SEC_DATA
1158   | SEC_ROM | SEC_HAS_CONTENTS),/* Section_flags.  */
1159  0,				/* Symbol_leading_char.  */
1160  ' ',				/* AR_pad_char.  */
1161  16,				/* AR_max_namelen.  */
1162  0,				/* match priority.  */
1163  bfd_getb64, bfd_getb_signed_64, bfd_putb64,
1164  bfd_getb32, bfd_getb_signed_32, bfd_putb32,
1165  bfd_getb16, bfd_getb_signed_16, bfd_putb16,	/* Data.  */
1166  bfd_getb64, bfd_getb_signed_64, bfd_putb64,
1167  bfd_getb32, bfd_getb_signed_32, bfd_putb32,
1168  bfd_getb16, bfd_getb_signed_16, bfd_putb16,	/* Headers.  */
1169  {				/* bfd_check_format.  */
1170    _bfd_dummy_target,
1171    bfd_pef_xlib_object_p,	/* bfd_check_format.  */
1172    _bfd_dummy_target,
1173    _bfd_dummy_target,
1174  },
1175  {				/* bfd_set_format.  */
1176    bfd_false,
1177    bfd_pef_mkobject,
1178    bfd_false,
1179    bfd_false,
1180  },
1181  {				/* bfd_write_contents.  */
1182    bfd_false,
1183    bfd_true,
1184    bfd_false,
1185    bfd_false,
1186  },
1187
1188  BFD_JUMP_TABLE_GENERIC (bfd_pef_xlib),
1189  BFD_JUMP_TABLE_COPY (_bfd_generic),
1190  BFD_JUMP_TABLE_CORE (_bfd_nocore),
1191  BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive),
1192  BFD_JUMP_TABLE_SYMBOLS (_bfd_nosymbols),
1193  BFD_JUMP_TABLE_RELOCS (_bfd_norelocs),
1194  BFD_JUMP_TABLE_WRITE (_bfd_nowrite),
1195  BFD_JUMP_TABLE_LINK (_bfd_nolink),
1196  BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
1197
1198  NULL,
1199
1200  NULL
1201};
1202