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