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