ppcboot.c revision 60484
1/* BFD back-end for PPCbug boot records.
2   Copyright 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
3   Written by Michael Meissner, Cygnus Support, <meissner@cygnus.com>
4
5This file is part of BFD, the Binary File Descriptor library.
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2 of the License, or
10(at your option) any later version.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with this program; if not, write to the Free Software
19Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20
21/* This is a BFD backend which may be used to write PowerPCBug boot objects.
22   It may only be used for output, not input.  The intention is that this may
23   be used as an output format for objcopy in order to generate raw binary
24   data.
25
26   This is very simple.  The only complication is that the real data
27   will start at some address X, and in some cases we will not want to
28   include X zeroes just to get to that point.  Since the start
29   address is not meaningful for this object file format, we use it
30   instead to indicate the number of zeroes to skip at the start of
31   the file.  objcopy cooperates by specially setting the start
32   address to zero by default.  */
33
34#include <ctype.h>
35
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} ppcboot_hdr_t;
68
69/* Signature bytes for last 2 bytes of the 512 byte record */
70#define SIGNATURE0 0x55
71#define SIGNATURE1 0xaa
72
73/* PowerPC boot type */
74#define PPC_IND 0x41
75
76/* Information needed for ppcboot header */
77typedef struct ppcboot_data {
78  ppcboot_hdr_t	header;				/* raw header */
79  asection *sec;				/* single section */
80} ppcboot_data_t;
81
82/* Any bfd we create by reading a ppcboot file has three symbols:
83   a start symbol, an end symbol, and an absolute length symbol.  */
84#define PPCBOOT_SYMS 3
85
86static boolean ppcboot_mkobject PARAMS ((bfd *));
87static const bfd_target *ppcboot_object_p PARAMS ((bfd *));
88static boolean ppcboot_set_arch_mach
89  PARAMS ((bfd *, enum bfd_architecture, unsigned long));
90static boolean ppcboot_get_section_contents
91  PARAMS ((bfd *, asection *, PTR, file_ptr, bfd_size_type));
92static long ppcboot_get_symtab_upper_bound PARAMS ((bfd *));
93static char *mangle_name PARAMS ((bfd *, char *));
94static long ppcboot_get_symtab PARAMS ((bfd *, asymbol **));
95static asymbol *ppcboot_make_empty_symbol PARAMS ((bfd *));
96static void ppcboot_get_symbol_info PARAMS ((bfd *, asymbol *, symbol_info *));
97static boolean ppcboot_set_section_contents
98  PARAMS ((bfd *, asection *, PTR, file_ptr, bfd_size_type));
99static int ppcboot_sizeof_headers PARAMS ((bfd *, boolean));
100static boolean ppcboot_bfd_print_private_bfd_data PARAMS ((bfd *, PTR));
101
102#define ppcboot_set_tdata(abfd, ptr) ((abfd)->tdata.any = (PTR) (ptr))
103#define ppcboot_get_tdata(abfd) ((ppcboot_data_t *) ((abfd)->tdata.any))
104
105/* Create a ppcboot object.  Invoked via bfd_set_format.  */
106
107static boolean
108ppcboot_mkobject (abfd)
109     bfd *abfd;
110{
111  if (!ppcboot_get_tdata (abfd))
112    ppcboot_set_tdata (abfd, bfd_zalloc (abfd, sizeof (ppcboot_data_t)));
113
114  return true;
115}
116
117
118/* Set the architecture to PowerPC */
119static boolean
120ppcboot_set_arch_mach (abfd, arch, machine)
121     bfd *abfd;
122     enum bfd_architecture arch;
123     unsigned long machine;
124{
125  if (arch == bfd_arch_unknown)
126    arch = bfd_arch_powerpc;
127
128  else if (arch != bfd_arch_powerpc)
129    return false;
130
131  return bfd_default_set_arch_mach (abfd, arch, machine);
132}
133
134
135/* Any file may be considered to be a ppcboot file, provided the target
136   was not defaulted.  That is, it must be explicitly specified as
137   being ppcboot.  */
138
139static const bfd_target *
140ppcboot_object_p (abfd)
141     bfd *abfd;
142{
143  struct stat statbuf;
144  asection *sec;
145  ppcboot_hdr_t hdr;
146  size_t i;
147  ppcboot_data_t *tdata;
148
149  BFD_ASSERT (sizeof (ppcboot_hdr_t) == 1024);
150
151  if (abfd->target_defaulted)
152    {
153      bfd_set_error (bfd_error_wrong_format);
154      return NULL;
155    }
156
157  /* Find the file size.  */
158  if (bfd_stat (abfd, &statbuf) < 0)
159    {
160      bfd_set_error (bfd_error_system_call);
161      return NULL;
162    }
163
164  if ((size_t) statbuf.st_size < sizeof (ppcboot_hdr_t))
165    {
166      bfd_set_error (bfd_error_wrong_format);
167      return NULL;
168    }
169
170  if (bfd_read ((PTR) &hdr, sizeof (hdr), 1, abfd) != sizeof (hdr))
171    {
172      if (bfd_get_error () != bfd_error_system_call)
173	bfd_set_error (bfd_error_wrong_format);
174
175      return NULL;
176    }
177
178  /* Now do some basic checks.  */
179  for (i = 0; i < sizeof (hdr.pc_compatibility); i++)
180    if (hdr.pc_compatibility[i])
181      {
182	bfd_set_error (bfd_error_wrong_format);
183	return NULL;
184      }
185
186  if (hdr.signature[0] != SIGNATURE0 || hdr.signature[1] != SIGNATURE1)
187    {
188      bfd_set_error (bfd_error_wrong_format);
189      return NULL;
190    }
191
192  if (hdr.partition[0].partition_end.ind != PPC_IND)
193    {
194      bfd_set_error (bfd_error_wrong_format);
195      return NULL;
196    }
197
198  abfd->symcount = PPCBOOT_SYMS;
199
200  /* One data section.  */
201  sec = bfd_make_section (abfd, ".data");
202  if (sec == NULL)
203    return NULL;
204  sec->flags = SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_CODE | SEC_HAS_CONTENTS;
205  sec->vma = 0;
206  sec->_raw_size = statbuf.st_size - sizeof (ppcboot_hdr_t);
207  sec->filepos = sizeof (ppcboot_hdr_t);
208
209  ppcboot_mkobject (abfd);
210  tdata = ppcboot_get_tdata (abfd);
211  tdata->sec = sec;
212  memcpy ((PTR) &tdata->header, (PTR) &hdr, sizeof (ppcboot_hdr_t));
213
214  ppcboot_set_arch_mach (abfd, bfd_arch_powerpc, 0);
215  return abfd->xvec;
216}
217
218#define ppcboot_close_and_cleanup _bfd_generic_close_and_cleanup
219#define ppcboot_bfd_free_cached_info _bfd_generic_bfd_free_cached_info
220#define ppcboot_new_section_hook _bfd_generic_new_section_hook
221
222
223/* Get contents of the only section.  */
224
225static boolean
226ppcboot_get_section_contents (abfd, section, location, offset, count)
227     bfd *abfd;
228     asection *section ATTRIBUTE_UNUSED;
229     PTR location;
230     file_ptr offset;
231     bfd_size_type count;
232{
233  if (bfd_seek (abfd, offset + sizeof(ppcboot_hdr_t), SEEK_SET) != 0
234      || bfd_read (location, 1, count, abfd) != count)
235    return false;
236  return true;
237}
238
239
240/* Return the amount of memory needed to read the symbol table.  */
241
242static long
243ppcboot_get_symtab_upper_bound (abfd)
244     bfd *abfd ATTRIBUTE_UNUSED;
245{
246  return (PPCBOOT_SYMS + 1) * sizeof (asymbol *);
247}
248
249
250/* Create a symbol name based on the bfd's filename.  */
251
252static char *
253mangle_name (abfd, suffix)
254     bfd *abfd;
255     char *suffix;
256{
257  int size;
258  char *buf;
259  char *p;
260
261  size = (strlen (bfd_get_filename (abfd))
262	  + strlen (suffix)
263	  + sizeof "_ppcboot__");
264
265  buf = (char *) bfd_alloc (abfd, size);
266  if (buf == NULL)
267    return "";
268
269  sprintf (buf, "_ppcboot_%s_%s", bfd_get_filename (abfd), suffix);
270
271  /* Change any non-alphanumeric characters to underscores.  */
272  for (p = buf; *p; p++)
273    if (! isalnum ((unsigned char) *p))
274      *p = '_';
275
276  return buf;
277}
278
279
280/* Return the symbol table.  */
281
282static long
283ppcboot_get_symtab (abfd, alocation)
284     bfd *abfd;
285     asymbol **alocation;
286{
287  asection *sec = ppcboot_get_tdata (abfd)->sec;
288  asymbol *syms;
289  unsigned int i;
290
291  syms = (asymbol *) bfd_alloc (abfd, PPCBOOT_SYMS * sizeof (asymbol));
292  if (syms == NULL)
293    return false;
294
295  /* Start symbol.  */
296  syms[0].the_bfd = abfd;
297  syms[0].name = mangle_name (abfd, "start");
298  syms[0].value = 0;
299  syms[0].flags = BSF_GLOBAL;
300  syms[0].section = sec;
301  syms[0].udata.p = NULL;
302
303  /* End symbol.  */
304  syms[1].the_bfd = abfd;
305  syms[1].name = mangle_name (abfd, "end");
306  syms[1].value = sec->_raw_size;
307  syms[1].flags = BSF_GLOBAL;
308  syms[1].section = sec;
309  syms[1].udata.p = NULL;
310
311  /* Size symbol.  */
312  syms[2].the_bfd = abfd;
313  syms[2].name = mangle_name (abfd, "size");
314  syms[2].value = sec->_raw_size;
315  syms[2].flags = BSF_GLOBAL;
316  syms[2].section = bfd_abs_section_ptr;
317  syms[2].udata.p = NULL;
318
319  for (i = 0; i < PPCBOOT_SYMS; i++)
320    *alocation++ = syms++;
321  *alocation = NULL;
322
323  return PPCBOOT_SYMS;
324}
325
326
327/* Make an empty symbol.  */
328
329static asymbol *
330ppcboot_make_empty_symbol (abfd)
331     bfd *abfd;
332{
333  return (asymbol *) bfd_alloc (abfd, sizeof (asymbol));
334}
335
336
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_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_bfd_make_debug_symbol _bfd_nosymbols_bfd_make_debug_symbol
354#define ppcboot_read_minisymbols _bfd_generic_read_minisymbols
355#define ppcboot_minisymbol_to_symbol _bfd_generic_minisymbol_to_symbol
356
357#define ppcboot_get_reloc_upper_bound \
358  ((long (*) PARAMS ((bfd *, asection *))) bfd_0l)
359#define ppcboot_canonicalize_reloc \
360  ((long (*) PARAMS ((bfd *, asection *, arelent **, asymbol **))) bfd_0l)
361#define ppcboot_bfd_reloc_type_lookup _bfd_norelocs_bfd_reloc_type_lookup
362
363/* Write section contents of a ppcboot file.  */
364
365static boolean
366ppcboot_set_section_contents (abfd, sec, data, offset, size)
367     bfd *abfd;
368     asection *sec;
369     PTR data;
370     file_ptr offset;
371     bfd_size_type size;
372{
373  if (! abfd->output_has_begun)
374    {
375      bfd_vma low;
376      asection *s;
377
378      /* The lowest section VMA sets the virtual address of the start
379         of the file.  We use the set the file position of all the
380         sections.  */
381      low = abfd->sections->vma;
382      for (s = abfd->sections->next; s != NULL; s = s->next)
383	if (s->vma < low)
384	  low = s->vma;
385
386      for (s = abfd->sections; s != NULL; s = s->next)
387	s->filepos = s->vma - low;
388
389      abfd->output_has_begun = true;
390    }
391
392  return _bfd_generic_set_section_contents (abfd, sec, data, offset, size);
393}
394
395
396static int
397ppcboot_sizeof_headers (abfd, exec)
398     bfd *abfd ATTRIBUTE_UNUSED;
399     boolean exec ATTRIBUTE_UNUSED;
400{
401  return sizeof (ppcboot_hdr_t);
402}
403
404
405/* Print out the program headers.  */
406
407static 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_link_hash_table_create _bfd_generic_link_hash_table_create
474#define ppcboot_bfd_link_add_symbols _bfd_generic_link_add_symbols
475#define ppcboot_bfd_final_link _bfd_generic_final_link
476#define ppcboot_bfd_link_split_section _bfd_generic_link_split_section
477#define ppcboot_get_section_contents_in_window \
478  _bfd_generic_get_section_contents_in_window
479
480#define ppcboot_bfd_copy_private_bfd_data _bfd_generic_bfd_copy_private_bfd_data
481#define ppcboot_bfd_merge_private_bfd_data _bfd_generic_bfd_merge_private_bfd_data
482#define ppcboot_bfd_copy_private_section_data _bfd_generic_bfd_copy_private_section_data
483#define ppcboot_bfd_copy_private_symbol_data _bfd_generic_bfd_copy_private_symbol_data
484#define ppcboot_bfd_set_private_flags _bfd_generic_bfd_set_private_flags
485#define ppcboot_bfd_print_private_bfd_dat ppcboot_bfd_print_private_bfd_data
486
487const bfd_target ppcboot_vec =
488{
489  "ppcboot",			/* name */
490  bfd_target_unknown_flavour,	/* flavour */
491  BFD_ENDIAN_BIG,		/* byteorder is big endian for code */
492  BFD_ENDIAN_LITTLE,		/* header_byteorder */
493  EXEC_P,			/* object_flags */
494  (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE | SEC_DATA
495   | SEC_ROM | SEC_HAS_CONTENTS), /* section_flags */
496  0,				/* symbol_leading_char */
497  ' ',				/* ar_pad_char */
498  16,				/* ar_max_namelen */
499  bfd_getb64, bfd_getb_signed_64, bfd_putb64,
500  bfd_getb32, bfd_getb_signed_32, bfd_putb32,
501  bfd_getb16, bfd_getb_signed_16, bfd_putb16,	/* data */
502  bfd_getl64, bfd_getl_signed_64, bfd_putl64,
503  bfd_getl32, bfd_getl_signed_32, bfd_putl32,
504  bfd_getl16, bfd_getl_signed_16, bfd_putl16,	/* hdrs */
505  {				/* bfd_check_format */
506    _bfd_dummy_target,
507    ppcboot_object_p,		/* bfd_check_format */
508    _bfd_dummy_target,
509    _bfd_dummy_target,
510  },
511  {				/* bfd_set_format */
512    bfd_false,
513    ppcboot_mkobject,
514    bfd_false,
515    bfd_false,
516  },
517  {				/* bfd_write_contents */
518    bfd_false,
519    bfd_true,
520    bfd_false,
521    bfd_false,
522  },
523
524  BFD_JUMP_TABLE_GENERIC (ppcboot),
525  BFD_JUMP_TABLE_COPY (ppcboot),
526  BFD_JUMP_TABLE_CORE (_bfd_nocore),
527  BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive),
528  BFD_JUMP_TABLE_SYMBOLS (ppcboot),
529  BFD_JUMP_TABLE_RELOCS (ppcboot),
530  BFD_JUMP_TABLE_WRITE (ppcboot),
531  BFD_JUMP_TABLE_LINK (ppcboot),
532  BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
533
534  NULL,
535
536  NULL
537};
538