1/* BFD back-end for PPCbug boot records.
2   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2006
3   Free Software Foundation, Inc.
4   Written by Michael Meissner, Cygnus Support, <meissner@cygnus.com>
5
6This file is part of BFD, the Binary File Descriptor library.
7
8This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2 of the License, or
11(at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program; if not, write to the Free Software
20Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
21
22/* This is a BFD backend which may be used to write PowerPCBug boot objects.
23   It may only be used for output, not input.  The intention is that this may
24   be used as an output format for objcopy in order to generate raw binary
25   data.
26
27   This is very simple.  The only complication is that the real data
28   will start at some address X, and in some cases we will not want to
29   include X zeroes just to get to that point.  Since the start
30   address is not meaningful for this object file format, we use it
31   instead to indicate the number of zeroes to skip at the start of
32   the file.  objcopy cooperates by specially setting the start
33   address to zero by default.  */
34
35#include "safe-ctype.h"
36#include "bfd.h"
37#include "sysdep.h"
38#include "libbfd.h"
39
40/* PPCbug location structure */
41typedef struct ppcboot_location {
42  bfd_byte	ind;
43  bfd_byte	head;
44  bfd_byte	sector;
45  bfd_byte	cylinder;
46} ppcboot_location_t;
47
48/* PPCbug partition table layout */
49typedef struct ppcboot_partition {
50  ppcboot_location_t	partition_begin;	/* partition begin */
51  ppcboot_location_t	partition_end;		/* partition end */
52  bfd_byte		sector_begin[4];	/* 32-bit start RBA (zero-based), little endian */
53  bfd_byte		sector_length[4];	/* 32-bit RBA count (one-based), little endian */
54} ppcboot_partition_t;
55
56/* PPCbug boot layout.  */
57typedef struct ppcboot_hdr {
58  bfd_byte		pc_compatibility[446];	/* x86 instruction field */
59  ppcboot_partition_t	partition[4];		/* partition information */
60  bfd_byte		signature[2];		/* 0x55 and 0xaa */
61  bfd_byte		entry_offset[4];	/* entry point offset, little endian */
62  bfd_byte		length[4];		/* load image length, little endian */
63  bfd_byte		flags;			/* flag field */
64  bfd_byte		os_id;			/* OS_ID */
65  char			partition_name[32];	/* partition name */
66  bfd_byte		reserved1[470];		/* reserved */
67}
68#ifdef __GNUC__
69  __attribute__ ((packed))
70#endif
71ppcboot_hdr_t;
72
73/* Signature bytes for last 2 bytes of the 512 byte record */
74#define SIGNATURE0 0x55
75#define SIGNATURE1 0xaa
76
77/* PowerPC boot type */
78#define PPC_IND 0x41
79
80/* Information needed for ppcboot header */
81typedef struct ppcboot_data {
82  ppcboot_hdr_t	header;				/* raw header */
83  asection *sec;				/* single section */
84} ppcboot_data_t;
85
86/* Any bfd we create by reading a ppcboot file has three symbols:
87   a start symbol, an end symbol, and an absolute length symbol.  */
88#define PPCBOOT_SYMS 3
89
90static bfd_boolean ppcboot_mkobject PARAMS ((bfd *));
91static const bfd_target *ppcboot_object_p PARAMS ((bfd *));
92static bfd_boolean ppcboot_set_arch_mach
93  PARAMS ((bfd *, enum bfd_architecture, unsigned long));
94static bfd_boolean ppcboot_get_section_contents
95  PARAMS ((bfd *, asection *, PTR, file_ptr, bfd_size_type));
96static long ppcboot_get_symtab_upper_bound PARAMS ((bfd *));
97static char *mangle_name PARAMS ((bfd *, char *));
98static long ppcboot_canonicalize_symtab PARAMS ((bfd *, asymbol **));
99static void ppcboot_get_symbol_info PARAMS ((bfd *, asymbol *, symbol_info *));
100static bfd_boolean ppcboot_set_section_contents
101  PARAMS ((bfd *, asection *, const PTR, file_ptr, bfd_size_type));
102static bfd_boolean ppcboot_bfd_print_private_bfd_data PARAMS ((bfd *, PTR));
103
104#define ppcboot_set_tdata(abfd, ptr) ((abfd)->tdata.any = (PTR) (ptr))
105#define ppcboot_get_tdata(abfd) ((ppcboot_data_t *) ((abfd)->tdata.any))
106
107/* Create a ppcboot object.  Invoked via bfd_set_format.  */
108
109static bfd_boolean
110ppcboot_mkobject (abfd)
111     bfd *abfd;
112{
113  if (!ppcboot_get_tdata (abfd))
114    {
115      bfd_size_type amt = sizeof (ppcboot_data_t);
116      ppcboot_set_tdata (abfd, bfd_zalloc (abfd, amt));
117    }
118
119  return TRUE;
120}
121
122
123/* Set the architecture to PowerPC */
124static bfd_boolean
125ppcboot_set_arch_mach (abfd, arch, machine)
126     bfd *abfd;
127     enum bfd_architecture arch;
128     unsigned long machine;
129{
130  if (arch == bfd_arch_unknown)
131    arch = bfd_arch_powerpc;
132
133  else if (arch != bfd_arch_powerpc)
134    return FALSE;
135
136  return bfd_default_set_arch_mach (abfd, arch, machine);
137}
138
139
140/* Any file may be considered to be a ppcboot file, provided the target
141   was not defaulted.  That is, it must be explicitly specified as
142   being ppcboot.  */
143
144static const bfd_target *
145ppcboot_object_p (abfd)
146     bfd *abfd;
147{
148  struct stat statbuf;
149  asection *sec;
150  ppcboot_hdr_t hdr;
151  size_t i;
152  ppcboot_data_t *tdata;
153  flagword flags;
154
155  BFD_ASSERT (sizeof (ppcboot_hdr_t) == 1024);
156
157  if (abfd->target_defaulted)
158    {
159      bfd_set_error (bfd_error_wrong_format);
160      return NULL;
161    }
162
163  /* Find the file size.  */
164  if (bfd_stat (abfd, &statbuf) < 0)
165    {
166      bfd_set_error (bfd_error_system_call);
167      return NULL;
168    }
169
170  if ((size_t) statbuf.st_size < sizeof (ppcboot_hdr_t))
171    {
172      bfd_set_error (bfd_error_wrong_format);
173      return NULL;
174    }
175
176  if (bfd_bread ((PTR) &hdr, (bfd_size_type) sizeof (hdr), abfd)
177      != sizeof (hdr))
178    {
179      if (bfd_get_error () != bfd_error_system_call)
180	bfd_set_error (bfd_error_wrong_format);
181
182      return NULL;
183    }
184
185  /* Now do some basic checks.  */
186  for (i = 0; i < sizeof (hdr.pc_compatibility); i++)
187    if (hdr.pc_compatibility[i])
188      {
189	bfd_set_error (bfd_error_wrong_format);
190	return NULL;
191      }
192
193  if (hdr.signature[0] != SIGNATURE0 || hdr.signature[1] != SIGNATURE1)
194    {
195      bfd_set_error (bfd_error_wrong_format);
196      return NULL;
197    }
198
199  if (hdr.partition[0].partition_end.ind != PPC_IND)
200    {
201      bfd_set_error (bfd_error_wrong_format);
202      return NULL;
203    }
204
205  abfd->symcount = PPCBOOT_SYMS;
206
207  /* One data section.  */
208  flags = SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_CODE | SEC_HAS_CONTENTS;
209  sec = bfd_make_section_with_flags (abfd, ".data", flags);
210  if (sec == NULL)
211    return NULL;
212  sec->vma = 0;
213  sec->size = statbuf.st_size - sizeof (ppcboot_hdr_t);
214  sec->filepos = sizeof (ppcboot_hdr_t);
215
216  ppcboot_mkobject (abfd);
217  tdata = ppcboot_get_tdata (abfd);
218  tdata->sec = sec;
219  memcpy ((PTR) &tdata->header, (PTR) &hdr, sizeof (ppcboot_hdr_t));
220
221  ppcboot_set_arch_mach (abfd, bfd_arch_powerpc, 0L);
222  return abfd->xvec;
223}
224
225#define ppcboot_close_and_cleanup _bfd_generic_close_and_cleanup
226#define ppcboot_bfd_free_cached_info _bfd_generic_bfd_free_cached_info
227#define ppcboot_new_section_hook _bfd_generic_new_section_hook
228
229
230/* Get contents of the only section.  */
231
232static bfd_boolean
233ppcboot_get_section_contents (abfd, section, location, offset, count)
234     bfd *abfd;
235     asection *section ATTRIBUTE_UNUSED;
236     PTR location;
237     file_ptr offset;
238     bfd_size_type count;
239{
240  if (bfd_seek (abfd, offset + (file_ptr) sizeof (ppcboot_hdr_t), SEEK_SET) != 0
241      || bfd_bread (location, count, abfd) != count)
242    return FALSE;
243  return TRUE;
244}
245
246
247/* Return the amount of memory needed to read the symbol table.  */
248
249static long
250ppcboot_get_symtab_upper_bound (abfd)
251     bfd *abfd ATTRIBUTE_UNUSED;
252{
253  return (PPCBOOT_SYMS + 1) * sizeof (asymbol *);
254}
255
256
257/* Create a symbol name based on the bfd's filename.  */
258
259static char *
260mangle_name (abfd, suffix)
261     bfd *abfd;
262     char *suffix;
263{
264  bfd_size_type size;
265  char *buf;
266  char *p;
267
268  size = (strlen (bfd_get_filename (abfd))
269	  + strlen (suffix)
270	  + sizeof "_ppcboot__");
271
272  buf = (char *) bfd_alloc (abfd, size);
273  if (buf == NULL)
274    return "";
275
276  sprintf (buf, "_ppcboot_%s_%s", bfd_get_filename (abfd), suffix);
277
278  /* Change any non-alphanumeric characters to underscores.  */
279  for (p = buf; *p; p++)
280    if (! ISALNUM (*p))
281      *p = '_';
282
283  return buf;
284}
285
286
287/* Return the symbol table.  */
288
289static long
290ppcboot_canonicalize_symtab (abfd, alocation)
291     bfd *abfd;
292     asymbol **alocation;
293{
294  asection *sec = ppcboot_get_tdata (abfd)->sec;
295  asymbol *syms;
296  unsigned int i;
297  bfd_size_type amt = PPCBOOT_SYMS * sizeof (asymbol);
298
299  syms = (asymbol *) bfd_alloc (abfd, amt);
300  if (syms == NULL)
301    return FALSE;
302
303  /* Start symbol.  */
304  syms[0].the_bfd = abfd;
305  syms[0].name = mangle_name (abfd, "start");
306  syms[0].value = 0;
307  syms[0].flags = BSF_GLOBAL;
308  syms[0].section = sec;
309  syms[0].udata.p = NULL;
310
311  /* End symbol.  */
312  syms[1].the_bfd = abfd;
313  syms[1].name = mangle_name (abfd, "end");
314  syms[1].value = sec->size;
315  syms[1].flags = BSF_GLOBAL;
316  syms[1].section = sec;
317  syms[1].udata.p = NULL;
318
319  /* Size symbol.  */
320  syms[2].the_bfd = abfd;
321  syms[2].name = mangle_name (abfd, "size");
322  syms[2].value = sec->size;
323  syms[2].flags = BSF_GLOBAL;
324  syms[2].section = bfd_abs_section_ptr;
325  syms[2].udata.p = NULL;
326
327  for (i = 0; i < PPCBOOT_SYMS; i++)
328    *alocation++ = syms++;
329  *alocation = NULL;
330
331  return PPCBOOT_SYMS;
332}
333
334#define ppcboot_make_empty_symbol _bfd_generic_make_empty_symbol
335#define ppcboot_print_symbol _bfd_nosymbols_print_symbol
336
337/* Get information about a symbol.  */
338
339static void
340ppcboot_get_symbol_info (ignore_abfd, symbol, ret)
341     bfd *ignore_abfd ATTRIBUTE_UNUSED;
342     asymbol *symbol;
343     symbol_info *ret;
344{
345  bfd_symbol_info (symbol, ret);
346}
347
348#define ppcboot_bfd_is_target_special_symbol \
349  ((bfd_boolean (*) (bfd *, asymbol *)) bfd_false)
350#define ppcboot_bfd_is_local_label_name bfd_generic_is_local_label_name
351#define ppcboot_get_lineno _bfd_nosymbols_get_lineno
352#define ppcboot_find_nearest_line _bfd_nosymbols_find_nearest_line
353#define ppcboot_find_inliner_info _bfd_nosymbols_find_inliner_info
354#define ppcboot_bfd_make_debug_symbol _bfd_nosymbols_bfd_make_debug_symbol
355#define ppcboot_read_minisymbols _bfd_generic_read_minisymbols
356#define ppcboot_minisymbol_to_symbol _bfd_generic_minisymbol_to_symbol
357
358#define ppcboot_get_reloc_upper_bound \
359  ((long (*) PARAMS ((bfd *, asection *))) bfd_0l)
360#define ppcboot_canonicalize_reloc \
361  ((long (*) PARAMS ((bfd *, asection *, arelent **, asymbol **))) bfd_0l)
362#define ppcboot_bfd_reloc_type_lookup _bfd_norelocs_bfd_reloc_type_lookup
363
364/* Write section contents of a ppcboot file.  */
365
366static bfd_boolean
367ppcboot_set_section_contents (abfd, sec, data, offset, size)
368     bfd *abfd;
369     asection *sec;
370     const PTR data;
371     file_ptr offset;
372     bfd_size_type size;
373{
374  if (! abfd->output_has_begun)
375    {
376      bfd_vma low;
377      asection *s;
378
379      /* The lowest section VMA sets the virtual address of the start
380         of the file.  We use the set the file position of all the
381         sections.  */
382      low = abfd->sections->vma;
383      for (s = abfd->sections->next; s != NULL; s = s->next)
384	if (s->vma < low)
385	  low = s->vma;
386
387      for (s = abfd->sections; s != NULL; s = s->next)
388	s->filepos = s->vma - low;
389
390      abfd->output_has_begun = TRUE;
391    }
392
393  return _bfd_generic_set_section_contents (abfd, sec, data, offset, size);
394}
395
396
397static int
398ppcboot_sizeof_headers (bfd *abfd ATTRIBUTE_UNUSED,
399			struct bfd_link_info *info ATTRIBUTE_UNUSED)
400{
401  return sizeof (ppcboot_hdr_t);
402}
403
404
405/* Print out the program headers.  */
406
407static bfd_boolean
408ppcboot_bfd_print_private_bfd_data (abfd, farg)
409     bfd *abfd;
410     PTR farg;
411{
412  FILE *f = (FILE *)farg;
413  ppcboot_data_t *tdata = ppcboot_get_tdata (abfd);
414  long entry_offset = bfd_getl_signed_32 ((PTR) tdata->header.entry_offset);
415  long length = bfd_getl_signed_32 ((PTR) tdata->header.length);
416  int i;
417
418  fprintf (f, _("\nppcboot header:\n"));
419  fprintf (f, _("Entry offset        = 0x%.8lx (%ld)\n"), entry_offset, entry_offset);
420  fprintf (f, _("Length              = 0x%.8lx (%ld)\n"), length, length);
421
422  if (tdata->header.flags)
423    fprintf (f, _("Flag field          = 0x%.2x\n"), tdata->header.flags);
424
425  if (tdata->header.os_id)
426    fprintf (f, "OS_ID               = 0x%.2x\n", tdata->header.os_id);
427
428  if (tdata->header.partition_name)
429    fprintf (f, _("Partition name      = \"%s\"\n"), tdata->header.partition_name);
430
431  for (i = 0; i < 4; i++)
432    {
433      long sector_begin  = bfd_getl_signed_32 ((PTR) tdata->header.partition[i].sector_begin);
434      long sector_length = bfd_getl_signed_32 ((PTR) tdata->header.partition[i].sector_length);
435
436      /* Skip all 0 entries */
437      if (!tdata->header.partition[i].partition_begin.ind
438	  && !tdata->header.partition[i].partition_begin.head
439	  && !tdata->header.partition[i].partition_begin.sector
440	  && !tdata->header.partition[i].partition_begin.cylinder
441	  && !tdata->header.partition[i].partition_end.ind
442	  && !tdata->header.partition[i].partition_end.head
443	  && !tdata->header.partition[i].partition_end.sector
444	  && !tdata->header.partition[i].partition_end.cylinder
445	  && !sector_begin && !sector_length)
446	continue;
447
448      fprintf (f, _("\nPartition[%d] start  = { 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x }\n"), i,
449	       tdata->header.partition[i].partition_begin.ind,
450	       tdata->header.partition[i].partition_begin.head,
451	       tdata->header.partition[i].partition_begin.sector,
452	       tdata->header.partition[i].partition_begin.cylinder);
453
454      fprintf (f, _("Partition[%d] end    = { 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x }\n"), i,
455	       tdata->header.partition[i].partition_end.ind,
456	       tdata->header.partition[i].partition_end.head,
457	       tdata->header.partition[i].partition_end.sector,
458	       tdata->header.partition[i].partition_end.cylinder);
459
460      fprintf (f, _("Partition[%d] sector = 0x%.8lx (%ld)\n"), i, sector_begin, sector_begin);
461      fprintf (f, _("Partition[%d] length = 0x%.8lx (%ld)\n"), i, sector_length, sector_length);
462    }
463
464  fprintf (f, "\n");
465  return TRUE;
466}
467
468
469#define ppcboot_bfd_get_relocated_section_contents \
470  bfd_generic_get_relocated_section_contents
471#define ppcboot_bfd_relax_section bfd_generic_relax_section
472#define ppcboot_bfd_gc_sections bfd_generic_gc_sections
473#define ppcboot_bfd_merge_sections bfd_generic_merge_sections
474#define ppcboot_bfd_is_group_section bfd_generic_is_group_section
475#define ppcboot_bfd_discard_group bfd_generic_discard_group
476#define ppcboot_section_already_linked \
477  _bfd_generic_section_already_linked
478#define ppcboot_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
479#define ppcboot_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
480#define ppcboot_bfd_link_add_symbols _bfd_generic_link_add_symbols
481#define ppcboot_bfd_link_just_syms _bfd_generic_link_just_syms
482#define ppcboot_bfd_final_link _bfd_generic_final_link
483#define ppcboot_bfd_link_split_section _bfd_generic_link_split_section
484#define ppcboot_get_section_contents_in_window \
485  _bfd_generic_get_section_contents_in_window
486
487#define ppcboot_bfd_copy_private_bfd_data _bfd_generic_bfd_copy_private_bfd_data
488#define ppcboot_bfd_merge_private_bfd_data _bfd_generic_bfd_merge_private_bfd_data
489#define ppcboot_bfd_copy_private_section_data _bfd_generic_bfd_copy_private_section_data
490#define ppcboot_bfd_copy_private_symbol_data _bfd_generic_bfd_copy_private_symbol_data
491#define ppcboot_bfd_copy_private_header_data _bfd_generic_bfd_copy_private_header_data
492#define ppcboot_bfd_set_private_flags _bfd_generic_bfd_set_private_flags
493#define ppcboot_bfd_print_private_bfd_dat ppcboot_bfd_print_private_bfd_data
494
495const bfd_target ppcboot_vec =
496{
497  "ppcboot",			/* name */
498  bfd_target_unknown_flavour,	/* flavour */
499  BFD_ENDIAN_BIG,		/* byteorder is big endian for code */
500  BFD_ENDIAN_LITTLE,		/* header_byteorder */
501  EXEC_P,			/* object_flags */
502  (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE | SEC_DATA
503   | SEC_ROM | SEC_HAS_CONTENTS), /* section_flags */
504  0,				/* symbol_leading_char */
505  ' ',				/* ar_pad_char */
506  16,				/* ar_max_namelen */
507  bfd_getb64, bfd_getb_signed_64, bfd_putb64,
508  bfd_getb32, bfd_getb_signed_32, bfd_putb32,
509  bfd_getb16, bfd_getb_signed_16, bfd_putb16,	/* data */
510  bfd_getl64, bfd_getl_signed_64, bfd_putl64,
511  bfd_getl32, bfd_getl_signed_32, bfd_putl32,
512  bfd_getl16, bfd_getl_signed_16, bfd_putl16,	/* hdrs */
513  {				/* bfd_check_format */
514    _bfd_dummy_target,
515    ppcboot_object_p,		/* bfd_check_format */
516    _bfd_dummy_target,
517    _bfd_dummy_target,
518  },
519  {				/* bfd_set_format */
520    bfd_false,
521    ppcboot_mkobject,
522    bfd_false,
523    bfd_false,
524  },
525  {				/* bfd_write_contents */
526    bfd_false,
527    bfd_true,
528    bfd_false,
529    bfd_false,
530  },
531
532  BFD_JUMP_TABLE_GENERIC (ppcboot),
533  BFD_JUMP_TABLE_COPY (ppcboot),
534  BFD_JUMP_TABLE_CORE (_bfd_nocore),
535  BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive),
536  BFD_JUMP_TABLE_SYMBOLS (ppcboot),
537  BFD_JUMP_TABLE_RELOCS (ppcboot),
538  BFD_JUMP_TABLE_WRITE (ppcboot),
539  BFD_JUMP_TABLE_LINK (ppcboot),
540  BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
541
542  NULL,
543
544  NULL
545};
546