ppcboot.c revision 130561
1/* BFD back-end for PPCbug boot records.
2   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 int ppcboot_sizeof_headers PARAMS ((bfd *, bfd_boolean));
103static bfd_boolean ppcboot_bfd_print_private_bfd_data PARAMS ((bfd *, PTR));
104
105#define ppcboot_set_tdata(abfd, ptr) ((abfd)->tdata.any = (PTR) (ptr))
106#define ppcboot_get_tdata(abfd) ((ppcboot_data_t *) ((abfd)->tdata.any))
107
108/* Create a ppcboot object.  Invoked via bfd_set_format.  */
109
110static bfd_boolean
111ppcboot_mkobject (abfd)
112     bfd *abfd;
113{
114  if (!ppcboot_get_tdata (abfd))
115    {
116      bfd_size_type amt = sizeof (ppcboot_data_t);
117      ppcboot_set_tdata (abfd, bfd_zalloc (abfd, amt));
118    }
119
120  return TRUE;
121}
122
123
124/* Set the architecture to PowerPC */
125static bfd_boolean
126ppcboot_set_arch_mach (abfd, arch, machine)
127     bfd *abfd;
128     enum bfd_architecture arch;
129     unsigned long machine;
130{
131  if (arch == bfd_arch_unknown)
132    arch = bfd_arch_powerpc;
133
134  else if (arch != bfd_arch_powerpc)
135    return FALSE;
136
137  return bfd_default_set_arch_mach (abfd, arch, machine);
138}
139
140
141/* Any file may be considered to be a ppcboot file, provided the target
142   was not defaulted.  That is, it must be explicitly specified as
143   being ppcboot.  */
144
145static const bfd_target *
146ppcboot_object_p (abfd)
147     bfd *abfd;
148{
149  struct stat statbuf;
150  asection *sec;
151  ppcboot_hdr_t hdr;
152  size_t i;
153  ppcboot_data_t *tdata;
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  sec = bfd_make_section (abfd, ".data");
209  if (sec == NULL)
210    return NULL;
211  sec->flags = SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_CODE | SEC_HAS_CONTENTS;
212  sec->vma = 0;
213  sec->_raw_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->_raw_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->_raw_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_local_label_name bfd_generic_is_local_label_name
349#define ppcboot_get_lineno _bfd_nosymbols_get_lineno
350#define ppcboot_find_nearest_line _bfd_nosymbols_find_nearest_line
351#define ppcboot_bfd_make_debug_symbol _bfd_nosymbols_bfd_make_debug_symbol
352#define ppcboot_read_minisymbols _bfd_generic_read_minisymbols
353#define ppcboot_minisymbol_to_symbol _bfd_generic_minisymbol_to_symbol
354
355#define ppcboot_get_reloc_upper_bound \
356  ((long (*) PARAMS ((bfd *, asection *))) bfd_0l)
357#define ppcboot_canonicalize_reloc \
358  ((long (*) PARAMS ((bfd *, asection *, arelent **, asymbol **))) bfd_0l)
359#define ppcboot_bfd_reloc_type_lookup _bfd_norelocs_bfd_reloc_type_lookup
360
361/* Write section contents of a ppcboot file.  */
362
363static bfd_boolean
364ppcboot_set_section_contents (abfd, sec, data, offset, size)
365     bfd *abfd;
366     asection *sec;
367     const PTR data;
368     file_ptr offset;
369     bfd_size_type size;
370{
371  if (! abfd->output_has_begun)
372    {
373      bfd_vma low;
374      asection *s;
375
376      /* The lowest section VMA sets the virtual address of the start
377         of the file.  We use the set the file position of all the
378         sections.  */
379      low = abfd->sections->vma;
380      for (s = abfd->sections->next; s != NULL; s = s->next)
381	if (s->vma < low)
382	  low = s->vma;
383
384      for (s = abfd->sections; s != NULL; s = s->next)
385	s->filepos = s->vma - low;
386
387      abfd->output_has_begun = TRUE;
388    }
389
390  return _bfd_generic_set_section_contents (abfd, sec, data, offset, size);
391}
392
393
394static int
395ppcboot_sizeof_headers (abfd, exec)
396     bfd *abfd ATTRIBUTE_UNUSED;
397     bfd_boolean exec ATTRIBUTE_UNUSED;
398{
399  return sizeof (ppcboot_hdr_t);
400}
401
402
403/* Print out the program headers.  */
404
405static bfd_boolean
406ppcboot_bfd_print_private_bfd_data (abfd, farg)
407     bfd *abfd;
408     PTR farg;
409{
410  FILE *f = (FILE *)farg;
411  ppcboot_data_t *tdata = ppcboot_get_tdata (abfd);
412  long entry_offset = bfd_getl_signed_32 ((PTR) tdata->header.entry_offset);
413  long length = bfd_getl_signed_32 ((PTR) tdata->header.length);
414  int i;
415
416  fprintf (f, _("\nppcboot header:\n"));
417  fprintf (f, _("Entry offset        = 0x%.8lx (%ld)\n"), entry_offset, entry_offset);
418  fprintf (f, _("Length              = 0x%.8lx (%ld)\n"), 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"), i, sector_begin, sector_begin);
459      fprintf (f, _("Partition[%d] length = 0x%.8lx (%ld)\n"), i, sector_length, sector_length);
460    }
461
462  fprintf (f, "\n");
463  return TRUE;
464}
465
466
467#define ppcboot_bfd_get_relocated_section_contents \
468  bfd_generic_get_relocated_section_contents
469#define ppcboot_bfd_relax_section bfd_generic_relax_section
470#define ppcboot_bfd_gc_sections bfd_generic_gc_sections
471#define ppcboot_bfd_merge_sections bfd_generic_merge_sections
472#define ppcboot_bfd_discard_group bfd_generic_discard_group
473#define ppcboot_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
474#define ppcboot_bfd_link_hash_table_free _bfd_generic_link_hash_table_free
475#define ppcboot_bfd_link_add_symbols _bfd_generic_link_add_symbols
476#define ppcboot_bfd_link_just_syms _bfd_generic_link_just_syms
477#define ppcboot_bfd_final_link _bfd_generic_final_link
478#define ppcboot_bfd_link_split_section _bfd_generic_link_split_section
479#define ppcboot_get_section_contents_in_window \
480  _bfd_generic_get_section_contents_in_window
481
482#define ppcboot_bfd_copy_private_bfd_data _bfd_generic_bfd_copy_private_bfd_data
483#define ppcboot_bfd_merge_private_bfd_data _bfd_generic_bfd_merge_private_bfd_data
484#define ppcboot_bfd_copy_private_section_data _bfd_generic_bfd_copy_private_section_data
485#define ppcboot_bfd_copy_private_symbol_data _bfd_generic_bfd_copy_private_symbol_data
486#define ppcboot_bfd_set_private_flags _bfd_generic_bfd_set_private_flags
487#define ppcboot_bfd_print_private_bfd_dat ppcboot_bfd_print_private_bfd_data
488
489const bfd_target ppcboot_vec =
490{
491  "ppcboot",			/* name */
492  bfd_target_unknown_flavour,	/* flavour */
493  BFD_ENDIAN_BIG,		/* byteorder is big endian for code */
494  BFD_ENDIAN_LITTLE,		/* header_byteorder */
495  EXEC_P,			/* object_flags */
496  (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE | SEC_DATA
497   | SEC_ROM | SEC_HAS_CONTENTS), /* section_flags */
498  0,				/* symbol_leading_char */
499  ' ',				/* ar_pad_char */
500  16,				/* ar_max_namelen */
501  bfd_getb64, bfd_getb_signed_64, bfd_putb64,
502  bfd_getb32, bfd_getb_signed_32, bfd_putb32,
503  bfd_getb16, bfd_getb_signed_16, bfd_putb16,	/* data */
504  bfd_getl64, bfd_getl_signed_64, bfd_putl64,
505  bfd_getl32, bfd_getl_signed_32, bfd_putl32,
506  bfd_getl16, bfd_getl_signed_16, bfd_putl16,	/* hdrs */
507  {				/* bfd_check_format */
508    _bfd_dummy_target,
509    ppcboot_object_p,		/* bfd_check_format */
510    _bfd_dummy_target,
511    _bfd_dummy_target,
512  },
513  {				/* bfd_set_format */
514    bfd_false,
515    ppcboot_mkobject,
516    bfd_false,
517    bfd_false,
518  },
519  {				/* bfd_write_contents */
520    bfd_false,
521    bfd_true,
522    bfd_false,
523    bfd_false,
524  },
525
526  BFD_JUMP_TABLE_GENERIC (ppcboot),
527  BFD_JUMP_TABLE_COPY (ppcboot),
528  BFD_JUMP_TABLE_CORE (_bfd_nocore),
529  BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive),
530  BFD_JUMP_TABLE_SYMBOLS (ppcboot),
531  BFD_JUMP_TABLE_RELOCS (ppcboot),
532  BFD_JUMP_TABLE_WRITE (ppcboot),
533  BFD_JUMP_TABLE_LINK (ppcboot),
534  BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
535
536  NULL,
537
538  NULL
539};
540