1/* BFD back-end for ns32k a.out-ish binaries.
2   Copyright 1990, 1991, 1992, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
3   2002, 2003, 2005 Free Software Foundation, Inc.
4   Contributed by Ian Dall (idall@eleceng.adelaide.edu.au).
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 2 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, MA 02110-1301, USA.  */
21
22#include "bfd.h"
23#include "aout/aout64.h"
24#include "ns32k.h"
25
26/* Do not "beautify" the CONCAT* macro args.  Traditional C will not
27   remove whitespace added here, and thus will fail to concatenate
28   the tokens.  */
29#define MYNS(OP) CONCAT2 (ns32kaout_,OP)
30
31reloc_howto_type * MYNS (bfd_reloc_type_lookup) (bfd * abfd, bfd_reloc_code_real_type);
32bfd_boolean        MYNS (write_object_contents) (bfd *abfd);
33
34/* Avoid multiple definitions from aoutx if supporting
35   standard a.out format(s) as well as this one.  */
36#define NAME(x,y) CONCAT3 (ns32kaout,_32_,y)
37
38void bfd_ns32k_arch (void);
39
40#include "libaout.h"
41
42#define MY(OP) MYNS (OP)
43
44#define MY_swap_std_reloc_in   MY (swap_std_reloc_in)
45#define MY_swap_std_reloc_out  MY (swap_std_reloc_out)
46
47/* The ns32k series is ah, unusual, when it comes to relocation.
48   There are three storage methods for relocatable objects.  There
49   are displacements, immediate operands and ordinary twos complement
50   data. Of these, only the last fits into the standard relocation
51   scheme.  Immediate operands are stored huffman encoded and
52   immediate operands are stored big endian (where as the natural byte
53   order is little endian for this architecture).
54
55   Note that the ns32k displacement storage method is orthogonal to
56   whether the relocation is pc relative or not. The "displacement"
57   storage scheme is used for essentially all address constants. The
58   displacement can be relative to zero (absolute displacement),
59   relative to the pc (pc relative), the stack pointer, the frame
60   pointer, the static base register and general purpose register etc.
61
62   For example:
63
64   sym1: .long .	 # pc relative 2's complement
65   sym1: .long foo	 # 2's complement not pc relative
66
67   self:  movd @self, r0 # pc relative displacement
68          movd foo, r0   # non pc relative displacement
69
70   self:  movd self, r0  # pc relative immediate
71          movd foo, r0   # non pc relative immediate
72
73   In addition, for historical reasons the encoding of the relocation types
74   in the a.out format relocation entries is such that even the relocation
75   methods which are standard are not encoded the standard way.  */
76
77reloc_howto_type MY (howto_table)[] =
78{
79  /* ns32k immediate operands.  */
80  HOWTO (BFD_RELOC_NS32K_IMM_8, 0, 0, 8, FALSE, 0, complain_overflow_signed,
81	 _bfd_ns32k_reloc_imm, "NS32K_IMM_8",
82	 TRUE, 0x000000ff,0x000000ff, FALSE),
83  HOWTO (BFD_RELOC_NS32K_IMM_16, 0, 1, 16, FALSE, 0, complain_overflow_signed,
84	 _bfd_ns32k_reloc_imm,  "NS32K_IMM_16",
85	 TRUE, 0x0000ffff,0x0000ffff, FALSE),
86  HOWTO (BFD_RELOC_NS32K_IMM_32, 0, 2, 32, FALSE, 0, complain_overflow_signed,
87	 _bfd_ns32k_reloc_imm, "NS32K_IMM_32",
88	 TRUE, 0xffffffff,0xffffffff, FALSE),
89  HOWTO (BFD_RELOC_NS32K_IMM_8_PCREL, 0, 0, 8, TRUE, 0, complain_overflow_signed,
90	 _bfd_ns32k_reloc_imm, "PCREL_NS32K_IMM_8",
91	 TRUE, 0x000000ff, 0x000000ff, FALSE),
92  HOWTO (BFD_RELOC_NS32K_IMM_16_PCREL, 0, 1, 16, TRUE, 0, complain_overflow_signed,
93	 _bfd_ns32k_reloc_imm, "PCREL_NS32K_IMM_16",
94	 TRUE, 0x0000ffff,0x0000ffff, FALSE),
95  HOWTO (BFD_RELOC_NS32K_IMM_32_PCREL, 0, 2, 32, TRUE, 0, complain_overflow_signed,
96	 _bfd_ns32k_reloc_imm, "PCREL_NS32K_IMM_32",
97	 TRUE, 0xffffffff,0xffffffff, FALSE),
98
99  /* ns32k displacements.  */
100  HOWTO (BFD_RELOC_NS32K_DISP_8, 0, 0, 7, FALSE, 0, complain_overflow_signed,
101	 _bfd_ns32k_reloc_disp, "NS32K_DISP_8",
102	 TRUE, 0x000000ff,0x000000ff, FALSE),
103  HOWTO (BFD_RELOC_NS32K_DISP_16, 0, 1, 14, FALSE, 0, complain_overflow_signed,
104	 _bfd_ns32k_reloc_disp, "NS32K_DISP_16",
105	 TRUE, 0x0000ffff, 0x0000ffff, FALSE),
106  HOWTO (BFD_RELOC_NS32K_DISP_32, 0, 2, 30, FALSE, 0, complain_overflow_signed,
107	 _bfd_ns32k_reloc_disp, "NS32K_DISP_32",
108	 TRUE, 0xffffffff, 0xffffffff, FALSE),
109  HOWTO (BFD_RELOC_NS32K_DISP_8_PCREL, 0, 0, 7, TRUE, 0, complain_overflow_signed,
110	   _bfd_ns32k_reloc_disp, "PCREL_NS32K_DISP_8",
111	 TRUE, 0x000000ff,0x000000ff, FALSE),
112  HOWTO (BFD_RELOC_NS32K_DISP_16_PCREL, 0, 1, 14, TRUE, 0, complain_overflow_signed,
113	 _bfd_ns32k_reloc_disp, "PCREL_NS32K_DISP_16",
114	 TRUE, 0x0000ffff,0x0000ffff, FALSE),
115  HOWTO (BFD_RELOC_NS32K_DISP_32_PCREL, 0, 2, 30, TRUE, 0, complain_overflow_signed,
116	 _bfd_ns32k_reloc_disp, "PCREL_NS32K_DISP_32",
117	 TRUE, 0xffffffff,0xffffffff, FALSE),
118
119  /* Normal 2's complement.  */
120  HOWTO (BFD_RELOC_8, 0, 0, 8, FALSE, 0, complain_overflow_bitfield,0,
121	 "8", TRUE, 0x000000ff,0x000000ff, FALSE),
122  HOWTO (BFD_RELOC_16, 0, 1, 16, FALSE, 0, complain_overflow_bitfield,0,
123	 "16", TRUE, 0x0000ffff,0x0000ffff, FALSE),
124  HOWTO (BFD_RELOC_32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,0,
125	 "32", TRUE, 0xffffffff,0xffffffff, FALSE),
126  HOWTO (BFD_RELOC_8_PCREL, 0, 0, 8, TRUE, 0, complain_overflow_signed, 0,
127	 "PCREL_8", TRUE, 0x000000ff,0x000000ff, FALSE),
128  HOWTO (BFD_RELOC_16_PCREL, 0, 1, 16, TRUE, 0, complain_overflow_signed, 0,
129	 "PCREL_16", TRUE, 0x0000ffff,0x0000ffff, FALSE),
130  HOWTO (BFD_RELOC_32_PCREL, 0, 2, 32, TRUE, 0, complain_overflow_signed, 0,
131	 "PCREL_32", TRUE, 0xffffffff,0xffffffff, FALSE),
132};
133
134#define CTOR_TABLE_RELOC_HOWTO(BFD) (MY (howto_table) + 14)
135
136#define RELOC_STD_BITS_NS32K_TYPE_BIG 		0x06
137#define RELOC_STD_BITS_NS32K_TYPE_LITTLE 	0x60
138#define RELOC_STD_BITS_NS32K_TYPE_SH_BIG 	1
139#define RELOC_STD_BITS_NS32K_TYPE_SH_LITTLE 	5
140
141static reloc_howto_type *
142MY (reloc_howto) (bfd *abfd ATTRIBUTE_UNUSED,
143		  struct reloc_std_external *rel,
144		  int *r_index,
145		  int *r_extern,
146		  int *r_pcrel)
147{
148  unsigned int r_length;
149  int r_ns32k_type;
150
151  *r_index =  ((rel->r_index[2] << 16)
152	       | (rel->r_index[1] << 8)
153	       |  rel->r_index[0] );
154  *r_extern  = (0 != (rel->r_type[0] & RELOC_STD_BITS_EXTERN_LITTLE));
155  *r_pcrel   = (0 != (rel->r_type[0] & RELOC_STD_BITS_PCREL_LITTLE));
156  r_length  =  ((rel->r_type[0] & RELOC_STD_BITS_LENGTH_LITTLE)
157		>> RELOC_STD_BITS_LENGTH_SH_LITTLE);
158  r_ns32k_type  =  ((rel->r_type[0] & RELOC_STD_BITS_NS32K_TYPE_LITTLE)
159		    >> RELOC_STD_BITS_NS32K_TYPE_SH_LITTLE);
160  return (MY (howto_table) + r_length + 3 * (*r_pcrel) + 6 * r_ns32k_type);
161}
162
163#define MY_reloc_howto(BFD, REL, IN, EX, PC) \
164  MY (reloc_howto) (BFD, REL, &IN, &EX, &PC)
165
166static void
167MY (put_reloc) (bfd *abfd,
168		int r_extern,
169		int r_index,
170		bfd_vma value,
171		reloc_howto_type *howto,
172		struct reloc_std_external *reloc)
173{
174  unsigned int r_length;
175  int r_pcrel;
176  int r_ns32k_type;
177
178  PUT_WORD (abfd, value, reloc->r_address);
179  r_length = howto->size ;	/* Size as a power of two.  */
180  r_pcrel  = (int) howto->pc_relative; /* Relative to PC?  */
181  r_ns32k_type = (howto - MY (howto_table) )/6;
182
183  reloc->r_index[2] = r_index >> 16;
184  reloc->r_index[1] = r_index >> 8;
185  reloc->r_index[0] = r_index;
186  reloc->r_type[0] =
187    (r_extern?    RELOC_STD_BITS_EXTERN_LITTLE: 0)
188      | (r_pcrel?     RELOC_STD_BITS_PCREL_LITTLE: 0)
189	| (r_length <<  RELOC_STD_BITS_LENGTH_SH_LITTLE)
190	  | (r_ns32k_type <<  RELOC_STD_BITS_NS32K_TYPE_SH_LITTLE);
191}
192
193#define MY_put_reloc(BFD, EXT, IDX, VAL, HOWTO, RELOC) \
194  MY (put_reloc) (BFD, EXT, IDX, VAL, HOWTO, RELOC)
195
196#define STAT_FOR_EXEC
197
198#define MY_final_link_relocate _bfd_ns32k_final_link_relocate
199#define MY_relocate_contents   _bfd_ns32k_relocate_contents
200
201static void MY_swap_std_reloc_in (bfd *, struct reloc_std_external *, arelent *, asymbol **, bfd_size_type);
202static void MY_swap_std_reloc_out (bfd *, arelent *, struct reloc_std_external *);
203
204#include "aoutx.h"
205
206reloc_howto_type *
207MY (bfd_reloc_type_lookup) (bfd *abfd, bfd_reloc_code_real_type code)
208{
209#define ENTRY(i,j)	case i: return &MY (howto_table)[j]
210
211  int ext = obj_reloc_entry_size (abfd) == RELOC_EXT_SIZE;
212
213  BFD_ASSERT (ext == 0);
214  if (code == BFD_RELOC_CTOR)
215    switch (bfd_get_arch_info (abfd)->bits_per_address)
216      {
217      case 32:
218	code = BFD_RELOC_32;
219	break;
220      default:
221	break;
222      }
223  switch (code)
224    {
225      ENTRY (BFD_RELOC_NS32K_IMM_8, 0);
226      ENTRY (BFD_RELOC_NS32K_IMM_16, 1);
227      ENTRY (BFD_RELOC_NS32K_IMM_32, 2);
228      ENTRY (BFD_RELOC_NS32K_IMM_8_PCREL, 3);
229      ENTRY (BFD_RELOC_NS32K_IMM_16_PCREL, 4);
230      ENTRY (BFD_RELOC_NS32K_IMM_32_PCREL, 5);
231      ENTRY (BFD_RELOC_NS32K_DISP_8, 6);
232      ENTRY (BFD_RELOC_NS32K_DISP_16, 7);
233      ENTRY (BFD_RELOC_NS32K_DISP_32, 8);
234      ENTRY (BFD_RELOC_NS32K_DISP_8_PCREL, 9);
235      ENTRY (BFD_RELOC_NS32K_DISP_16_PCREL, 10);
236      ENTRY (BFD_RELOC_NS32K_DISP_32_PCREL, 11);
237      ENTRY (BFD_RELOC_8, 12);
238      ENTRY (BFD_RELOC_16, 13);
239      ENTRY (BFD_RELOC_32, 14);
240      ENTRY (BFD_RELOC_8_PCREL, 15);
241      ENTRY (BFD_RELOC_16_PCREL, 16);
242      ENTRY (BFD_RELOC_32_PCREL, 17);
243    default:
244      return NULL;
245    }
246#undef ENTRY
247}
248
249static void
250MY_swap_std_reloc_in (bfd *abfd,
251		      struct reloc_std_external *bytes,
252		      arelent *cache_ptr,
253		      asymbol **symbols,
254		      bfd_size_type symcount ATTRIBUTE_UNUSED)
255{
256  int r_index;
257  int r_extern;
258  int r_pcrel;
259  struct aoutdata  *su = &(abfd->tdata.aout_data->a);
260
261  cache_ptr->address = H_GET_32 (abfd, bytes->r_address);
262
263  /* Now the fun stuff.  */
264  cache_ptr->howto = MY_reloc_howto (abfd, bytes, r_index, r_extern, r_pcrel);
265
266  MOVE_ADDRESS (0);
267}
268
269static void
270MY_swap_std_reloc_out (bfd *abfd,
271		       arelent *g,
272		       struct reloc_std_external *natptr)
273{
274  int r_index;
275  asymbol *sym = *(g->sym_ptr_ptr);
276  int r_extern;
277  unsigned int r_addend;
278  asection *output_section = sym->section->output_section;
279
280  r_addend = g->addend + (*(g->sym_ptr_ptr))->section->output_section->vma;
281
282  /* Name was clobbered by aout_write_syms to be symbol index.  */
283
284  /* If this relocation is relative to a symbol then set the
285     r_index to the symbols index, and the r_extern bit.
286
287     Absolute symbols can come in in two ways, either as an offset
288     from the abs section, or as a symbol which has an abs value.
289     Check for that here.  */
290  if (bfd_is_com_section (output_section)
291      || output_section == &bfd_abs_section
292      || output_section == &bfd_und_section)
293    {
294      if (bfd_abs_section.symbol == sym)
295	{
296	  /* Whoops, looked like an abs symbol, but is really an offset
297	     from the abs section.  */
298	  r_index = 0;
299	  r_extern = 0;
300	}
301      else
302	{
303	  /* Fill in symbol.  */
304	  r_extern = 1;
305#undef KEEPIT
306#define KEEPIT udata.i
307	  r_index =  (*(g->sym_ptr_ptr))->KEEPIT;
308#undef KEEPIT
309	}
310    }
311  else
312    {
313      /* Just an ordinary section.  */
314      r_extern = 0;
315      r_index  = output_section->target_index;
316    }
317
318  MY_put_reloc (abfd, r_extern, r_index, g->address, g->howto, natptr);
319}
320
321bfd_reloc_status_type
322_bfd_ns32k_relocate_contents (reloc_howto_type *howto,
323			      bfd *input_bfd,
324			      bfd_vma relocation,
325			      bfd_byte *location)
326{
327  int r_ns32k_type = (howto - MY (howto_table)) / 6;
328  bfd_vma (*get_data) (bfd_byte *, int);
329  void (*put_data) (bfd_vma, bfd_byte *, int);
330
331  switch (r_ns32k_type)
332    {
333    case 0:
334      get_data = _bfd_ns32k_get_immediate;
335      put_data = _bfd_ns32k_put_immediate;
336      break;
337    case 1:
338      get_data = _bfd_ns32k_get_displacement;
339      put_data = _bfd_ns32k_put_displacement;
340      break;
341    case 2:
342      return _bfd_relocate_contents (howto, input_bfd, relocation,
343				    location);
344      break;
345    default:
346      return bfd_reloc_notsupported;
347    }
348  return _bfd_do_ns32k_reloc_contents (howto, input_bfd, relocation,
349				       location, get_data, put_data);
350}
351