1/* BFD back-end for MAXQ COFF binaries.
2   Copyright 2004    Free Software Foundation, Inc.
3
4   Contributed by Vineet Sharma (vineets@noida.hcltech.com) Inderpreet S.
5   (inderpreetb@noida.hcltech.com)
6
7   HCL Technologies Ltd.
8
9   This file is part of BFD, the Binary File Descriptor library.
10
11   This program is free software; you can redistribute it and/or modify it
12   under the terms of the GNU General Public License as published by the Free
13   Software Foundation; either version 2 of the License, or (at your option)
14   any later version.
15
16   This program is distributed in the hope that it will be useful, but
17   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
19   for more details.
20
21   You should have received a copy of the GNU General Public License along
22   with this program; if not, write to the Free Software Foundation, Inc.,
23   51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
24
25#include "bfd.h"
26#include "sysdep.h"
27#include "libbfd.h"
28#include "coff/maxq.h"
29#include "coff/internal.h"
30#include "libcoff.h"
31#include "libiberty.h"
32
33#ifndef MAXQ20
34#define MAXQ20 1
35#endif
36
37#define RTYPE2HOWTO(cache_ptr, dst)                                     \
38  ((cache_ptr)->howto =                                                 \
39   ((dst)->r_type < 48 							\
40    ? howto_table + (((dst)->r_type==47) ? 6: ((dst)->r_type))		\
41    : NULL))
42
43#define COFF_DEFAULT_SECTION_ALIGNMENT_POWER (2)
44
45/* Code to swap in the reloc offset.  */
46#define SWAP_IN_RELOC_OFFSET    H_GET_16
47#define SWAP_OUT_RELOC_OFFSET   H_PUT_16
48
49#define SHORT_JUMP 	        BFD_RELOC_16_PCREL_S2
50#define LONG_JUMP 	        BFD_RELOC_14
51#define ABSOLUTE_ADDR_FOR_DATA  BFD_RELOC_24
52
53/* checks the range of short jump -127 to 128 */
54#define IS_SJUMP_RANGE(x) ((x > -128) && (x < 129))
55#define HIGH_WORD_MASK    0xff00
56#define LOW_WORD_MASK     0x00ff
57
58static long
59get_symbol_value (asymbol *symbol)
60{
61  long relocation = 0;
62
63  if (bfd_is_com_section (symbol->section))
64    relocation = 0;
65  else
66    relocation = symbol->value +
67      symbol->section->output_section->vma + symbol->section->output_offset;
68
69  return relocation;
70}
71
72/* This function performs all the maxq relocations.
73   FIXME:  The handling of the addend in the 'BFD_*'
74   relocations types.  */
75
76static bfd_reloc_status_type
77coff_maxq20_reloc (bfd *      abfd,
78		   arelent *  reloc_entry,
79		   asymbol *  symbol_in,
80		   void *     data,
81		   asection * input_section ATTRIBUTE_UNUSED,
82		   bfd *      output_bfd    ATTRIBUTE_UNUSED,
83		   char **    error_message ATTRIBUTE_UNUSED)
84{
85  reloc_howto_type *howto = NULL;
86  unsigned char *addr = NULL;
87  unsigned long x = 0;
88  long call_addr = 0;
89  short addend = 0;
90  long diff = 0;
91
92  /* If this is an undefined symbol, return error.  */
93  if (symbol_in->section == &bfd_und_section
94      && (symbol_in->flags & BSF_WEAK) == 0)
95    return bfd_reloc_continue;
96
97  if (data && reloc_entry)
98    {
99      howto = reloc_entry->howto;
100      addr = (unsigned char *) data + reloc_entry->address;
101      call_addr = call_addr - call_addr;
102      call_addr = get_symbol_value (symbol_in);
103
104      /* Over here the value val stores the 8 bit/16 bit value. We will put a
105         check if we are moving a 16 bit immediate value into an 8 bit
106         register. In that case we will generate a Upper bytes into PFX[0]
107         and move the lower 8 bits as SRC.  */
108
109      switch (reloc_entry->howto->type)
110	{
111	  /* BFD_RELOC_16_PCREL_S2 47 Handles all the relative jumps and
112	     calls Note: Every relative jump or call is in words.  */
113	case SHORT_JUMP:
114	  /* Handle any addend.  */
115	  addend = reloc_entry->addend;
116
117	  if (addend > call_addr || addend > 0)
118	    call_addr = symbol_in->section->output_section->vma + addend;
119	  else if (addend < call_addr && addend > 0)
120	    call_addr = call_addr + addend;
121	  else if (addend < 0)
122	    call_addr = call_addr + addend;
123
124	  diff = ((call_addr << 1) - (reloc_entry->address << 1));
125
126	  if (!IS_SJUMP_RANGE (diff))
127	    {
128	      bfd_perror (_("Can't Make it a Short Jump"));
129	      return bfd_reloc_outofrange;
130	    }
131
132	  x = bfd_get_16 (abfd, addr);
133
134	  x = x & LOW_WORD_MASK;
135	  x = x | (diff << 8);
136	  bfd_put_16 (abfd, (bfd_vma) x, addr);
137
138	  return bfd_reloc_ok;
139
140	case ABSOLUTE_ADDR_FOR_DATA:
141	case LONG_JUMP:
142	  /* BFD_RELOC_14 Handles intersegment or long jumps which might be
143	     from code to code or code to data segment jumps. Note: When this
144	     fucntion is called by gas the section flags somehow do not
145	     contain the info about the section type(CODE or DATA). Thus the
146	     user needs to evoke the linker after assembling the files
147	     because the Code-Code relocs are word aligned but code-data are
148	     byte aligned.  */
149	  addend = (reloc_entry->addend - reloc_entry->addend);
150
151	  /* Handle any addend.  */
152	  addend = reloc_entry->addend;
153
154	  /* For relocation involving multiple file added becomes zero thus
155	     this fails - check for zero added. In another case when we try
156	     to add a stub to a file the addend shows the offset from the
157	     start od this file.  */
158	  addend = 0;
159
160	  if (!bfd_is_com_section (symbol_in->section) &&
161	      ((symbol_in->flags & BSF_OLD_COMMON) == 0))
162	    {
163	      if (reloc_entry->addend > symbol_in->value)
164		addend = reloc_entry->addend - symbol_in->value;
165
166	      if ((reloc_entry->addend < symbol_in->value)
167		  && (reloc_entry->addend != 0))
168		addend = reloc_entry->addend - symbol_in->value;
169
170	      if (reloc_entry->addend == symbol_in->value)
171		addend = 0;
172	    }
173
174	  if (bfd_is_com_section (symbol_in->section) ||
175	      ((symbol_in->flags & BSF_OLD_COMMON) != 0))
176	    addend = reloc_entry->addend;
177
178	  if (addend < 0
179	      &&  (call_addr < (long) (addend * (-1))))
180	    addend = 0;
181
182	  call_addr += addend;
183
184	  /* FIXME: This check does not work well with the assembler,
185	     linker needs to be run always.  */
186	  if ((symbol_in->section->flags & SEC_CODE) == SEC_CODE)
187	    {
188	      /* Convert it into words.  */
189	      call_addr = call_addr >> 1;
190
191	      if (call_addr > 0xFFFF)	/* Intersegment Jump.  */
192		{
193		  bfd_perror (_("Exceeds Long Jump Range"));
194		  return bfd_reloc_outofrange;
195		}
196	    }
197	  else
198	    {
199	      /* case ABSOLUTE_ADDR_FOR_DATA : Resolves any code-data
200		 segemnt relocs. These are NOT word aligned.  */
201
202	      if (call_addr > 0xFFFF)	/* Intersegment Jump.  */
203		{
204		  bfd_perror (_("Absolute address Exceeds 16 bit Range"));
205		  return bfd_reloc_outofrange;
206		}
207	    }
208
209	  x = bfd_get_32 (abfd, addr);
210
211	  x = (x & 0xFF00FF00);
212	  x = (x | ((call_addr & HIGH_WORD_MASK) >> 8));
213	  x = (x | (call_addr & LOW_WORD_MASK) << 16);
214
215	  bfd_put_32 (abfd, (bfd_vma) x, addr);
216	  return bfd_reloc_ok;
217
218	case BFD_RELOC_8:
219	  addend = (reloc_entry->addend - reloc_entry->addend);
220
221	  if (!bfd_is_com_section (symbol_in->section) &&
222	      ((symbol_in->flags & BSF_OLD_COMMON) == 0))
223	    {
224	      if (reloc_entry->addend > symbol_in->value)
225		addend = reloc_entry->addend - symbol_in->value;
226	      if (reloc_entry->addend < symbol_in->value)
227		addend = reloc_entry->addend - symbol_in->value;
228	      if (reloc_entry->addend == symbol_in->value)
229		addend = 0;
230	    }
231
232	  if (bfd_is_com_section (symbol_in->section) ||
233	      ((symbol_in->flags & BSF_OLD_COMMON) != 0))
234	    addend = reloc_entry->addend;
235
236	  if (addend < 0
237	      && (call_addr < (long) (addend * (-1))))
238	    addend = 0;
239
240	  if (call_addr + addend > 0xFF)
241	    {
242	      bfd_perror (_("Absolute address Exceeds 8 bit Range"));
243	      return bfd_reloc_outofrange;
244	    }
245
246	  x = bfd_get_8 (abfd, addr);
247	  x = x & 0x00;
248	  x = x | (call_addr + addend);
249
250	  bfd_put_8 (abfd, (bfd_vma) x, addr);
251	  return bfd_reloc_ok;
252
253	case BFD_RELOC_16:
254	  addend = (reloc_entry->addend - reloc_entry->addend);
255	  if (!bfd_is_com_section (symbol_in->section) &&
256	      ((symbol_in->flags & BSF_OLD_COMMON) == 0))
257	    {
258	      if (reloc_entry->addend > symbol_in->value)
259		addend = reloc_entry->addend - symbol_in->value;
260
261	      if (reloc_entry->addend < symbol_in->value)
262		addend = reloc_entry->addend - symbol_in->value;
263
264	      if (reloc_entry->addend == symbol_in->value)
265		addend = 0;
266	    }
267
268	  if (bfd_is_com_section (symbol_in->section) ||
269	      ((symbol_in->flags & BSF_OLD_COMMON) != 0))
270	    addend = reloc_entry->addend;
271
272	  if (addend < 0
273	      && (call_addr < (long) (addend * (-1))))
274	    addend = 0;
275
276	  if ((call_addr + addend) > 0xFFFF)
277	    {
278	      bfd_perror (_("Absolute address Exceeds 16 bit Range"));
279	      return bfd_reloc_outofrange;
280	    }
281	  else
282	    {
283	      unsigned short val = (call_addr + addend);
284
285	      x = bfd_get_16 (abfd, addr);
286
287	      /* LE */
288	      x = (x & 0x0000);	/* Flush garbage value.  */
289	      x = val;
290	      if ((symbol_in->section->flags & SEC_CODE) == SEC_CODE)
291		x = x >> 1;	/* Convert it into words.  */
292	    }
293
294	  bfd_put_16 (abfd, (bfd_vma) x, addr);
295	  return bfd_reloc_ok;
296
297	case BFD_RELOC_32:
298	  addend = (reloc_entry->addend - reloc_entry->addend);
299
300	  if (!bfd_is_com_section (symbol_in->section) &&
301	      ((symbol_in->flags & BSF_OLD_COMMON) == 0))
302	    {
303	      if (reloc_entry->addend > symbol_in->value)
304		addend = reloc_entry->addend - symbol_in->value;
305	      if (reloc_entry->addend < symbol_in->value)
306		addend = reloc_entry->addend - symbol_in->value;
307	      if (reloc_entry->addend == symbol_in->value)
308		addend = 0;
309	    }
310
311	  if (bfd_is_com_section (symbol_in->section) ||
312	      ((symbol_in->flags & BSF_OLD_COMMON) != 0))
313	    addend = reloc_entry->addend;
314
315	  if (addend < 0
316	      && (call_addr < (long) (addend * (-1))))
317	    addend = 0;
318
319	  if ((call_addr + addend) < 0)
320	    {
321	      bfd_perror ("Absolute address Exceeds 32 bit Range");
322	      return bfd_reloc_outofrange;
323	    }
324
325	  x = bfd_get_32 (abfd, addr);
326	  x = (x & 0x0000);	/* Flush garbage value.  */
327	  x = call_addr + addend;
328	  if ((symbol_in->section->flags & SEC_CODE) == SEC_CODE)
329	    x = x >> 1;	/* Convert it into words.  */
330
331	  bfd_put_32 (abfd, (bfd_vma) x, addr);
332	  return bfd_reloc_ok;
333
334	default:
335	  bfd_perror (_("Unrecognized Reloc Type"));
336	  return bfd_reloc_notsupported;
337	}
338    }
339
340  return bfd_reloc_notsupported;
341}
342
343static reloc_howto_type howto_table[] =
344{
345  EMPTY_HOWTO (0),
346  EMPTY_HOWTO (1),
347  {
348   BFD_RELOC_32, 0, 1, 8, FALSE, 0, complain_overflow_bitfield,
349   coff_maxq20_reloc, "32Bit", TRUE, 0x000000ff, 0x000000ff, TRUE
350  },
351  {
352   SHORT_JUMP, 0, 1, 8, FALSE, 0, complain_overflow_bitfield,
353   coff_maxq20_reloc, "SHORT_JMP", TRUE, 0x000000ff, 0x000000ff, TRUE
354  },
355  {
356   ABSOLUTE_ADDR_FOR_DATA, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
357   coff_maxq20_reloc, "INTERSEGMENT_RELOC", TRUE, 0x00000000, 0x00000000,
358   FALSE
359  },
360  {
361   BFD_RELOC_16, 0, 1, 8, FALSE, 0, complain_overflow_bitfield,
362   coff_maxq20_reloc, "16Bit", TRUE, 0x000000ff, 0x000000ff, TRUE
363  },
364  {
365   LONG_JUMP, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
366   coff_maxq20_reloc, "LONG_JUMP", TRUE, 0x00000000, 0x00000000, FALSE
367  },
368  {
369   BFD_RELOC_8, 0, 1, 8, FALSE, 0, complain_overflow_bitfield,
370   coff_maxq20_reloc, "8bit", TRUE, 0x000000ff, 0x000000ff, TRUE
371  },
372  EMPTY_HOWTO (8),
373  EMPTY_HOWTO (9),
374  EMPTY_HOWTO (10),
375};
376
377/* Map BFD reloc types to MAXQ COFF reloc types.  */
378
379typedef struct maxq_reloc_map
380{
381  bfd_reloc_code_real_type  bfd_reloc_val;
382  unsigned int              maxq_reloc_val;
383  reloc_howto_type *        table;
384}
385reloc_map;
386
387static const reloc_map maxq_reloc_map[] =
388{
389  {BFD_RELOC_16_PCREL_S2, SHORT_JUMP, howto_table},
390  {BFD_RELOC_16,          LONG_JUMP,  howto_table},
391};
392
393static reloc_howto_type *
394maxq_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED,
395			bfd_reloc_code_real_type code)
396{
397  unsigned int i;
398
399  for (i = 0; i < ARRAY_SIZE (maxq_reloc_map); i++)
400    {
401      const reloc_map *entry;
402
403      entry = maxq_reloc_map + i;
404
405      switch (code)
406	{
407	  /* SHORT JUMP */
408	case BFD_RELOC_16_PCREL_S2:
409	  return howto_table + 3;
410
411	  /* INTERSEGMENT JUMP */
412	case BFD_RELOC_24:
413	  return howto_table + 4;
414
415	  /* BYTE RELOC */
416	case BFD_RELOC_8:
417	  return howto_table + 7;
418
419	  /* WORD RELOC */
420	case BFD_RELOC_16:
421	  return howto_table + 5;
422
423	  /* LONG RELOC */
424	case BFD_RELOC_32:
425	  return howto_table + 2;
426
427	  /* LONG JUMP */
428	case BFD_RELOC_14:
429	  return howto_table + 6;
430
431	default:
432	  return NULL;
433	}
434    }
435
436  return NULL;
437}
438
439#define coff_bfd_reloc_type_lookup maxq_reloc_type_lookup
440
441/* Perform any necessary magic to the addend in a reloc entry.  */
442#define CALC_ADDEND(abfd, symbol, ext_reloc, cache_ptr) \
443 cache_ptr->addend =  ext_reloc.r_offset;
444
445#include "coffcode.h"
446
447#ifndef TARGET_UNDERSCORE
448#define TARGET_UNDERSCORE 1
449#endif
450
451#ifndef EXTRA_S_FLAGS
452#define EXTRA_S_FLAGS 0
453#endif
454
455/* Forward declaration for use initialising alternative_target field.  */
456CREATE_LITTLE_COFF_TARGET_VEC (maxqcoff_vec, "coff-maxq", 0, EXTRA_S_FLAGS,
457			       TARGET_UNDERSCORE, NULL, COFF_SWAP_TABLE);
458
459