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 
31 reloc_howto_type * MYNS (bfd_reloc_type_lookup) (bfd * abfd, bfd_reloc_code_real_type);
32 bfd_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 
38 void 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 
77 reloc_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 
141 static reloc_howto_type *
142 MY (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 
166 static void
167 MY (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 
201 static void MY_swap_std_reloc_in (bfd *, struct reloc_std_external *, arelent *, asymbol **, bfd_size_type);
202 static void MY_swap_std_reloc_out (bfd *, arelent *, struct reloc_std_external *);
203 
204 #include "aoutx.h"
205 
206 reloc_howto_type *
207 MY (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 
249 static void
250 MY_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 
269 static void
270 MY_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 
321 bfd_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