1/* BFD backend for MIPS BSD (a.out) binaries.
2   Copyright 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
3   2007 Free Software Foundation, Inc.
4   Written by Ralph Campbell.
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/* #define ENTRY_CAN_BE_ZERO */
23#define N_HEADER_IN_TEXT(x) 1
24#define N_SHARED_LIB(x) 0
25#define N_TXTADDR(x) \
26    (N_MAGIC(x) != ZMAGIC ? (x).a_entry :	/* object file or NMAGIC */\
27	    TEXT_START_ADDR + EXEC_BYTES_SIZE	/* no padding */\
28    )
29#define N_DATADDR(x) (N_TXTADDR(x)+N_TXTSIZE(x))
30#define TEXT_START_ADDR 4096
31#define TARGET_PAGE_SIZE 4096
32#define SEGMENT_SIZE TARGET_PAGE_SIZE
33#define DEFAULT_ARCH bfd_arch_mips
34#define MACHTYPE_OK(mtype) ((mtype) == M_UNKNOWN \
35			    || (mtype) == M_MIPS1 || (mtype) == M_MIPS2)
36#define MY_symbol_leading_char '\0'
37
38/* Do not "beautify" the CONCAT* macro args.  Traditional C will not
39   remove whitespace added here, and thus will fail to concatenate
40   the tokens.  */
41#define MY(OP) CONCAT2 (mipsbsd_,OP)
42
43#include "sysdep.h"
44#include "bfd.h"
45#include "libbfd.h"
46#include "libaout.h"
47
48#define SET_ARCH_MACH(ABFD, EXEC) \
49  MY(set_arch_mach) (ABFD, N_MACHTYPE (EXEC)); \
50  MY(choose_reloc_size) (ABFD);
51static void MY(set_arch_mach) PARAMS ((bfd *abfd, unsigned long machtype));
52static void MY(choose_reloc_size) PARAMS ((bfd *abfd));
53
54#define MY_write_object_contents MY(write_object_contents)
55static bfd_boolean MY(write_object_contents) PARAMS ((bfd *abfd));
56
57/* We can't use MY(x) here because it leads to a recursive call to CONCAT2
58   when expanded inside JUMP_TABLE.  */
59#define MY_bfd_reloc_type_lookup mipsbsd_reloc_type_lookup
60#define MY_bfd_reloc_name_lookup mipsbsd_reloc_name_lookup
61#define MY_canonicalize_reloc mipsbsd_canonicalize_reloc
62
63#define MY_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
64#define MY_bfd_link_add_symbols _bfd_generic_link_add_symbols
65#define MY_final_link_callback unused
66#define MY_bfd_final_link _bfd_generic_final_link
67
68#define MY_backend_data &MY(backend_data)
69#define MY_BFD_TARGET
70
71#include "aout-target.h"
72
73static bfd_reloc_status_type mips_fix_jmp_addr
74  PARAMS ((bfd *, arelent *, struct bfd_symbol *, PTR, asection *,
75	   bfd *, char **));
76
77long MY(canonicalize_reloc) PARAMS ((bfd *, sec_ptr, arelent **, asymbol **));
78
79static void
80MY(set_arch_mach) (abfd, machtype)
81     bfd *abfd;
82     unsigned long machtype;
83{
84  enum bfd_architecture arch;
85  unsigned int machine;
86
87  /* Determine the architecture and machine type of the object file.  */
88  switch (machtype)
89    {
90    case M_MIPS1:
91      arch = bfd_arch_mips;
92      machine = bfd_mach_mips3000;
93      break;
94
95    case M_MIPS2:
96      arch = bfd_arch_mips;
97      machine = bfd_mach_mips4000;
98      break;
99
100    default:
101      arch = bfd_arch_obscure;
102      machine = 0;
103      break;
104    }
105
106  bfd_set_arch_mach (abfd, arch, machine);
107}
108
109/* Determine the size of a relocation entry, based on the architecture */
110static void
111MY (choose_reloc_size) (abfd)
112     bfd *abfd;
113{
114  switch (bfd_get_arch (abfd))
115    {
116    case bfd_arch_sparc:
117    case bfd_arch_mips:
118      obj_reloc_entry_size (abfd) = RELOC_EXT_SIZE;
119      break;
120    default:
121      obj_reloc_entry_size (abfd) = RELOC_STD_SIZE;
122      break;
123    }
124}
125
126/* Write an object file in BSD a.out format.
127  Section contents have already been written.  We write the
128  file header, symbols, and relocation.  */
129
130static bfd_boolean
131MY (write_object_contents) (abfd)
132     bfd *abfd;
133{
134  struct external_exec exec_bytes;
135  struct internal_exec *execp = exec_hdr (abfd);
136
137  /* Magic number, maestro, please!  */
138  switch (bfd_get_arch (abfd))
139    {
140    case bfd_arch_m68k:
141      switch (bfd_get_mach (abfd))
142	{
143	case bfd_mach_m68010:
144	  N_SET_MACHTYPE (*execp, M_68010);
145	  break;
146	default:
147	case bfd_mach_m68020:
148	  N_SET_MACHTYPE (*execp, M_68020);
149	  break;
150	}
151      break;
152    case bfd_arch_sparc:
153      N_SET_MACHTYPE (*execp, M_SPARC);
154      break;
155    case bfd_arch_i386:
156      N_SET_MACHTYPE (*execp, M_386);
157      break;
158    case bfd_arch_mips:
159      switch (bfd_get_mach (abfd))
160	{
161	case bfd_mach_mips4000:
162	case bfd_mach_mips6000:
163	  N_SET_MACHTYPE (*execp, M_MIPS2);
164	  break;
165	default:
166	  N_SET_MACHTYPE (*execp, M_MIPS1);
167	  break;
168	}
169      break;
170    default:
171      N_SET_MACHTYPE (*execp, M_UNKNOWN);
172    }
173
174  MY (choose_reloc_size) (abfd);
175
176  WRITE_HEADERS (abfd, execp);
177
178  return TRUE;
179}
180
181/* MIPS relocation types.  */
182#define MIPS_RELOC_32		0
183#define MIPS_RELOC_JMP		1
184#define MIPS_RELOC_WDISP16	2
185#define MIPS_RELOC_HI16		3
186#define MIPS_RELOC_HI16_S	4
187#define MIPS_RELOC_LO16		5
188
189/* This is only called when performing a BFD_RELOC_MIPS_JMP relocation.
190   The jump destination address is formed from the upper 4 bits of the
191   "current" program counter concatenated with the jump instruction's
192   26 bit field and two trailing zeros.
193   If the destination address is not in the same segment as the "current"
194   program counter, then we need to signal an error.  */
195
196static bfd_reloc_status_type
197mips_fix_jmp_addr (abfd, reloc_entry, symbol, data, input_section, output_bfd,
198		   error_message)
199     bfd *abfd ATTRIBUTE_UNUSED;
200     arelent *reloc_entry;
201     struct bfd_symbol *symbol;
202     PTR data ATTRIBUTE_UNUSED;
203     asection *input_section;
204     bfd *output_bfd;
205     char **error_message ATTRIBUTE_UNUSED;
206{
207  bfd_vma relocation, pc;
208
209  /* If this is a partial relocation, just continue.  */
210  if (output_bfd != (bfd *)NULL)
211    return bfd_reloc_continue;
212
213  /* If this is an undefined symbol, return error */
214  if (bfd_is_und_section (symbol->section)
215      && (symbol->flags & BSF_WEAK) == 0)
216    return bfd_reloc_undefined;
217
218  /* Work out which section the relocation is targeted at and the
219     initial relocation command value.  */
220  if (bfd_is_com_section (symbol->section))
221    relocation = 0;
222  else
223    relocation = symbol->value;
224
225  relocation += symbol->section->output_section->vma;
226  relocation += symbol->section->output_offset;
227  relocation += reloc_entry->addend;
228
229  pc = input_section->output_section->vma + input_section->output_offset +
230    reloc_entry->address + 4;
231
232  if ((relocation & 0xF0000000) != (pc & 0xF0000000))
233    return bfd_reloc_overflow;
234
235  return bfd_reloc_continue;
236}
237
238/* This is only called when performing a BFD_RELOC_HI16_S relocation.
239   We need to see if bit 15 is set in the result. If it is, we add
240   0x10000 and continue normally. This will compensate for the sign extension
241   when the low bits are added at run time.  */
242
243static bfd_reloc_status_type
244mips_fix_hi16_s PARAMS ((bfd *, arelent *, asymbol *, PTR,
245			 asection *, bfd *, char **));
246
247static bfd_reloc_status_type
248mips_fix_hi16_s (abfd, reloc_entry, symbol, data, input_section,
249		 output_bfd, error_message)
250     bfd *abfd ATTRIBUTE_UNUSED;
251     arelent *reloc_entry;
252     asymbol *symbol;
253     PTR data ATTRIBUTE_UNUSED;
254     asection *input_section ATTRIBUTE_UNUSED;
255     bfd *output_bfd;
256     char **error_message ATTRIBUTE_UNUSED;
257{
258  bfd_vma relocation;
259
260  /* If this is a partial relocation, just continue.  */
261  if (output_bfd != (bfd *)NULL)
262    return bfd_reloc_continue;
263
264  /* If this is an undefined symbol, return error.  */
265  if (bfd_is_und_section (symbol->section)
266      && (symbol->flags & BSF_WEAK) == 0)
267    return bfd_reloc_undefined;
268
269  /* Work out which section the relocation is targeted at and the
270     initial relocation command value.  */
271  if (bfd_is_com_section (symbol->section))
272    relocation = 0;
273  else
274    relocation = symbol->value;
275
276  relocation += symbol->section->output_section->vma;
277  relocation += symbol->section->output_offset;
278  relocation += reloc_entry->addend;
279
280  if (relocation & 0x8000)
281    reloc_entry->addend += 0x10000;
282
283  return bfd_reloc_continue;
284}
285
286static reloc_howto_type mips_howto_table_ext[] = {
287  {MIPS_RELOC_32,      0, 2, 32, FALSE, 0,  complain_overflow_bitfield, 0,
288	"32",       FALSE, 0, 0xffffffff, FALSE},
289  {MIPS_RELOC_JMP,     2, 2, 26, FALSE, 0, complain_overflow_dont,
290	mips_fix_jmp_addr,
291	"MIPS_JMP", FALSE, 0, 0x03ffffff, FALSE},
292  {MIPS_RELOC_WDISP16, 2, 2, 16, TRUE,  0, complain_overflow_signed, 0,
293	"WDISP16",  FALSE, 0, 0x0000ffff, FALSE},
294  {MIPS_RELOC_HI16,   16, 2, 16, FALSE, 0, complain_overflow_bitfield, 0,
295	"HI16",     FALSE, 0, 0x0000ffff, FALSE},
296  {MIPS_RELOC_HI16_S, 16, 2, 16, FALSE, 0, complain_overflow_bitfield,
297        mips_fix_hi16_s,
298        "HI16_S",   FALSE, 0, 0x0000ffff, FALSE},
299  {MIPS_RELOC_LO16,    0, 2, 16, FALSE, 0, complain_overflow_dont, 0,
300	"LO16",     FALSE, 0, 0x0000ffff, FALSE},
301};
302
303static reloc_howto_type *
304MY(reloc_type_lookup) (bfd *abfd, bfd_reloc_code_real_type code)
305{
306
307  if (bfd_get_arch (abfd) != bfd_arch_mips)
308    return 0;
309
310  switch (code)
311    {
312    case BFD_RELOC_CTOR:
313    case BFD_RELOC_32:
314      return (&mips_howto_table_ext[MIPS_RELOC_32]);
315    case BFD_RELOC_MIPS_JMP:
316      return (&mips_howto_table_ext[MIPS_RELOC_JMP]);
317    case BFD_RELOC_16_PCREL_S2:
318      return (&mips_howto_table_ext[MIPS_RELOC_WDISP16]);
319    case BFD_RELOC_HI16:
320      return (&mips_howto_table_ext[MIPS_RELOC_HI16]);
321    case BFD_RELOC_HI16_S:
322      return (&mips_howto_table_ext[MIPS_RELOC_HI16_S]);
323    case BFD_RELOC_LO16:
324      return (&mips_howto_table_ext[MIPS_RELOC_LO16]);
325    default:
326      return 0;
327    }
328}
329
330static reloc_howto_type *
331MY(reloc_name_lookup) (bfd *abfd ATTRIBUTE_UNUSED,
332			     const char *r_name)
333{
334  unsigned int i;
335
336  for (i = 0;
337       i < sizeof (mips_howto_table_ext) / sizeof (mips_howto_table_ext[0]);
338       i++)
339    if (mips_howto_table_ext[i].name != NULL
340	&& strcasecmp (mips_howto_table_ext[i].name, r_name) == 0)
341      return &mips_howto_table_ext[i];
342
343  return NULL;
344}
345
346/* This is just like the standard aoutx.h version but we need to do our
347   own mapping of external reloc type values to howto entries.  */
348long
349MY(canonicalize_reloc) (abfd, section, relptr, symbols)
350      bfd *abfd;
351      sec_ptr section;
352      arelent **relptr;
353      asymbol **symbols;
354{
355  arelent *tblptr = section->relocation;
356  unsigned int count, c;
357  extern reloc_howto_type NAME(aout,ext_howto_table)[];
358
359  /* If we have already read in the relocation table, return the values.  */
360  if (section->flags & SEC_CONSTRUCTOR)
361    {
362      arelent_chain *chain = section->constructor_chain;
363
364      for (count = 0; count < section->reloc_count; count++)
365	{
366	  *relptr++ = &chain->relent;
367	  chain = chain->next;
368	}
369      *relptr = 0;
370      return section->reloc_count;
371    }
372
373  if (tblptr && section->reloc_count)
374    {
375      for (count = 0; count++ < section->reloc_count;)
376	*relptr++ = tblptr++;
377      *relptr = 0;
378      return section->reloc_count;
379    }
380
381  if (!NAME(aout,slurp_reloc_table) (abfd, section, symbols))
382    return -1;
383  tblptr = section->relocation;
384
385  /* fix up howto entries.  */
386  for (count = 0; count++ < section->reloc_count;)
387    {
388      c = tblptr->howto - NAME(aout,ext_howto_table);
389      tblptr->howto = &mips_howto_table_ext[c];
390
391      *relptr++ = tblptr++;
392    }
393  *relptr = 0;
394  return section->reloc_count;
395}
396
397static const struct aout_backend_data MY(backend_data) = {
398  0,				/* zmagic contiguous */
399  1,				/* text incl header */
400  0,				/* entry is text address */
401  0,				/* exec_hdr_flags */
402  TARGET_PAGE_SIZE,			/* text vma */
403  MY_set_sizes,
404  0,				/* text size includes exec header */
405  0,				/* add_dynamic_symbols */
406  0,				/* add_one_symbol */
407  0,				/* link_dynamic_object */
408  0,				/* write_dynamic_symbol */
409  0,				/* check_dynamic_reloc */
410  0				/* finish_dynamic_link */
411};
412
413extern const bfd_target aout_mips_big_vec;
414
415const bfd_target aout_mips_little_vec =
416  {
417    "a.out-mips-little",		/* name */
418    bfd_target_aout_flavour,
419    BFD_ENDIAN_LITTLE,		/* target byte order (little) */
420    BFD_ENDIAN_LITTLE,		/* target headers byte order (little) */
421    (HAS_RELOC | EXEC_P |		/* object flags */
422     HAS_LINENO | HAS_DEBUG |
423     HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED),
424    (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_CODE | SEC_DATA),
425    MY_symbol_leading_char,
426    ' ',				/* ar_pad_char */
427    15,				/* ar_max_namelen */
428    bfd_getl64, bfd_getl_signed_64, bfd_putl64,
429    bfd_getl32, bfd_getl_signed_32, bfd_putl32,
430    bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* data */
431    bfd_getl64, bfd_getl_signed_64, bfd_putl64,
432    bfd_getl32, bfd_getl_signed_32, bfd_putl32,
433    bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* hdrs */
434    {_bfd_dummy_target, MY_object_p, /* bfd_check_format */
435     bfd_generic_archive_p, MY_core_file_p},
436    {bfd_false, MY_mkobject,	/* bfd_set_format */
437     _bfd_generic_mkarchive, bfd_false},
438    {bfd_false, MY_write_object_contents, /* bfd_write_contents */
439     _bfd_write_archive_contents, bfd_false},
440
441    BFD_JUMP_TABLE_GENERIC (MY),
442    BFD_JUMP_TABLE_COPY (MY),
443    BFD_JUMP_TABLE_CORE (MY),
444    BFD_JUMP_TABLE_ARCHIVE (MY),
445    BFD_JUMP_TABLE_SYMBOLS (MY),
446    BFD_JUMP_TABLE_RELOCS (MY),
447    BFD_JUMP_TABLE_WRITE (MY),
448    BFD_JUMP_TABLE_LINK (MY),
449    BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
450
451    & aout_mips_big_vec,
452
453    (PTR) MY_backend_data
454  };
455
456const bfd_target aout_mips_big_vec =
457  {
458    "a.out-mips-big",		/* name */
459    bfd_target_aout_flavour,
460    BFD_ENDIAN_BIG,		/* target byte order (big) */
461    BFD_ENDIAN_BIG,		/* target headers byte order (big) */
462    (HAS_RELOC | EXEC_P |		/* object flags */
463     HAS_LINENO | HAS_DEBUG |
464     HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED),
465    (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_CODE | SEC_DATA),
466    MY_symbol_leading_char,
467    ' ',				/* ar_pad_char */
468    15,				/* ar_max_namelen */
469    bfd_getb64, bfd_getb_signed_64, bfd_putb64,
470    bfd_getb32, bfd_getb_signed_32, bfd_putb32,
471    bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* data */
472    bfd_getb64, bfd_getb_signed_64, bfd_putb64,
473    bfd_getb32, bfd_getb_signed_32, bfd_putb32,
474    bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* hdrs */
475    {_bfd_dummy_target, MY_object_p, /* bfd_check_format */
476     bfd_generic_archive_p, MY_core_file_p},
477    {bfd_false, MY_mkobject,	/* bfd_set_format */
478     _bfd_generic_mkarchive, bfd_false},
479    {bfd_false, MY_write_object_contents, /* bfd_write_contents */
480     _bfd_write_archive_contents, bfd_false},
481
482    BFD_JUMP_TABLE_GENERIC (MY),
483    BFD_JUMP_TABLE_COPY (MY),
484    BFD_JUMP_TABLE_CORE (MY),
485    BFD_JUMP_TABLE_ARCHIVE (MY),
486    BFD_JUMP_TABLE_SYMBOLS (MY),
487    BFD_JUMP_TABLE_RELOCS (MY),
488    BFD_JUMP_TABLE_WRITE (MY),
489    BFD_JUMP_TABLE_LINK (MY),
490    BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
491
492    & aout_mips_little_vec,
493
494    (PTR) MY_backend_data
495  };
496