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