1/* BFD back-end for MAXQ COFF binaries.
2   Copyright 2004, 2007  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 3 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 "sysdep.h"
26#include "bfd.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  unsigned char *addr = NULL;
86  unsigned long x = 0;
87  long call_addr = 0;
88  short addend = 0;
89  long diff = 0;
90
91  /* If this is an undefined symbol, return error.  */
92  if (symbol_in->section == &bfd_und_section
93      && (symbol_in->flags & BSF_WEAK) == 0)
94    return bfd_reloc_continue;
95
96  if (data && reloc_entry)
97    {
98      addr = (unsigned char *) data + reloc_entry->address;
99      call_addr = call_addr - call_addr;
100      call_addr = get_symbol_value (symbol_in);
101
102      /* Over here the value val stores the 8 bit/16 bit value. We will put a
103         check if we are moving a 16 bit immediate value into an 8 bit
104         register. In that case we will generate a Upper bytes into PFX[0]
105         and move the lower 8 bits as SRC.  */
106
107      switch (reloc_entry->howto->type)
108	{
109	  /* BFD_RELOC_16_PCREL_S2 47 Handles all the relative jumps and
110	     calls Note: Every relative jump or call is in words.  */
111	case SHORT_JUMP:
112	  /* Handle any addend.  */
113	  addend = reloc_entry->addend;
114
115	  if (addend > call_addr || addend > 0)
116	    call_addr = symbol_in->section->output_section->vma + addend;
117	  else if (addend < call_addr && addend > 0)
118	    call_addr = call_addr + addend;
119	  else if (addend < 0)
120	    call_addr = call_addr + addend;
121
122	  diff = ((call_addr << 1) - (reloc_entry->address << 1));
123
124	  if (!IS_SJUMP_RANGE (diff))
125	    {
126	      bfd_perror (_("Can't Make it a Short Jump"));
127	      return bfd_reloc_outofrange;
128	    }
129
130	  x = bfd_get_16 (abfd, addr);
131
132	  x = x & LOW_WORD_MASK;
133	  x = x | (diff << 8);
134	  bfd_put_16 (abfd, (bfd_vma) x, addr);
135
136	  return bfd_reloc_ok;
137
138	case ABSOLUTE_ADDR_FOR_DATA:
139	case LONG_JUMP:
140	  /* BFD_RELOC_14 Handles intersegment or long jumps which might be
141	     from code to code or code to data segment jumps. Note: When this
142	     fucntion is called by gas the section flags somehow do not
143	     contain the info about the section type(CODE or DATA). Thus the
144	     user needs to evoke the linker after assembling the files
145	     because the Code-Code relocs are word aligned but code-data are
146	     byte aligned.  */
147	  addend = (reloc_entry->addend - reloc_entry->addend);
148
149	  /* Handle any addend.  */
150	  addend = reloc_entry->addend;
151
152	  /* For relocation involving multiple file added becomes zero thus
153	     this fails - check for zero added. In another case when we try
154	     to add a stub to a file the addend shows the offset from the
155	     start od this file.  */
156	  addend = 0;
157
158	  if (!bfd_is_com_section (symbol_in->section) &&
159	      ((symbol_in->flags & BSF_OLD_COMMON) == 0))
160	    {
161	      if (reloc_entry->addend > symbol_in->value)
162		addend = reloc_entry->addend - symbol_in->value;
163
164	      if ((reloc_entry->addend < symbol_in->value)
165		  && (reloc_entry->addend != 0))
166		addend = reloc_entry->addend - symbol_in->value;
167
168	      if (reloc_entry->addend == symbol_in->value)
169		addend = 0;
170	    }
171
172	  if (bfd_is_com_section (symbol_in->section) ||
173	      ((symbol_in->flags & BSF_OLD_COMMON) != 0))
174	    addend = reloc_entry->addend;
175
176	  if (addend < 0
177	      &&  (call_addr < (long) (addend * (-1))))
178	    addend = 0;
179
180	  call_addr += addend;
181
182	  /* FIXME: This check does not work well with the assembler,
183	     linker needs to be run always.  */
184	  if ((symbol_in->section->flags & SEC_CODE) == SEC_CODE)
185	    {
186	      /* Convert it into words.  */
187	      call_addr = call_addr >> 1;
188
189	      if (call_addr > 0xFFFF)	/* Intersegment Jump.  */
190		{
191		  bfd_perror (_("Exceeds Long Jump Range"));
192		  return bfd_reloc_outofrange;
193		}
194	    }
195	  else
196	    {
197	      /* case ABSOLUTE_ADDR_FOR_DATA : Resolves any code-data
198		 segemnt relocs. These are NOT word aligned.  */
199
200	      if (call_addr > 0xFFFF)	/* Intersegment Jump.  */
201		{
202		  bfd_perror (_("Absolute address Exceeds 16 bit Range"));
203		  return bfd_reloc_outofrange;
204		}
205	    }
206
207	  x = bfd_get_32 (abfd, addr);
208
209	  x = (x & 0xFF00FF00);
210	  x = (x | ((call_addr & HIGH_WORD_MASK) >> 8));
211	  x = (x | (call_addr & LOW_WORD_MASK) << 16);
212
213	  bfd_put_32 (abfd, (bfd_vma) x, addr);
214	  return bfd_reloc_ok;
215
216	case BFD_RELOC_8:
217	  addend = (reloc_entry->addend - reloc_entry->addend);
218
219	  if (!bfd_is_com_section (symbol_in->section) &&
220	      ((symbol_in->flags & BSF_OLD_COMMON) == 0))
221	    {
222	      if (reloc_entry->addend > symbol_in->value)
223		addend = reloc_entry->addend - symbol_in->value;
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 = 0;
228	    }
229
230	  if (bfd_is_com_section (symbol_in->section) ||
231	      ((symbol_in->flags & BSF_OLD_COMMON) != 0))
232	    addend = reloc_entry->addend;
233
234	  if (addend < 0
235	      && (call_addr < (long) (addend * (-1))))
236	    addend = 0;
237
238	  if (call_addr + addend > 0xFF)
239	    {
240	      bfd_perror (_("Absolute address Exceeds 8 bit Range"));
241	      return bfd_reloc_outofrange;
242	    }
243
244	  x = bfd_get_8 (abfd, addr);
245	  x = x & 0x00;
246	  x = x | (call_addr + addend);
247
248	  bfd_put_8 (abfd, (bfd_vma) x, addr);
249	  return bfd_reloc_ok;
250
251	case BFD_RELOC_16:
252	  addend = (reloc_entry->addend - reloc_entry->addend);
253	  if (!bfd_is_com_section (symbol_in->section) &&
254	      ((symbol_in->flags & BSF_OLD_COMMON) == 0))
255	    {
256	      if (reloc_entry->addend > symbol_in->value)
257		addend = reloc_entry->addend - symbol_in->value;
258
259	      if (reloc_entry->addend < symbol_in->value)
260		addend = reloc_entry->addend - symbol_in->value;
261
262	      if (reloc_entry->addend == symbol_in->value)
263		addend = 0;
264	    }
265
266	  if (bfd_is_com_section (symbol_in->section) ||
267	      ((symbol_in->flags & BSF_OLD_COMMON) != 0))
268	    addend = reloc_entry->addend;
269
270	  if (addend < 0
271	      && (call_addr < (long) (addend * (-1))))
272	    addend = 0;
273
274	  if ((call_addr + addend) > 0xFFFF)
275	    {
276	      bfd_perror (_("Absolute address Exceeds 16 bit Range"));
277	      return bfd_reloc_outofrange;
278	    }
279	  else
280	    {
281	      unsigned short val = (call_addr + addend);
282
283	      x = bfd_get_16 (abfd, addr);
284
285	      /* LE */
286	      x = (x & 0x0000);	/* Flush garbage value.  */
287	      x = val;
288	      if ((symbol_in->section->flags & SEC_CODE) == SEC_CODE)
289		x = x >> 1;	/* Convert it into words.  */
290	    }
291
292	  bfd_put_16 (abfd, (bfd_vma) x, addr);
293	  return bfd_reloc_ok;
294
295	case BFD_RELOC_32:
296	  addend = (reloc_entry->addend - reloc_entry->addend);
297
298	  if (!bfd_is_com_section (symbol_in->section) &&
299	      ((symbol_in->flags & BSF_OLD_COMMON) == 0))
300	    {
301	      if (reloc_entry->addend > symbol_in->value)
302		addend = reloc_entry->addend - symbol_in->value;
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 = 0;
307	    }
308
309	  if (bfd_is_com_section (symbol_in->section) ||
310	      ((symbol_in->flags & BSF_OLD_COMMON) != 0))
311	    addend = reloc_entry->addend;
312
313	  if (addend < 0
314	      && (call_addr < (long) (addend * (-1))))
315	    addend = 0;
316
317	  if ((call_addr + addend) < 0)
318	    {
319	      bfd_perror ("Absolute address Exceeds 32 bit Range");
320	      return bfd_reloc_outofrange;
321	    }
322
323	  x = bfd_get_32 (abfd, addr);
324	  x = (x & 0x0000);	/* Flush garbage value.  */
325	  x = call_addr + addend;
326	  if ((symbol_in->section->flags & SEC_CODE) == SEC_CODE)
327	    x = x >> 1;	/* Convert it into words.  */
328
329	  bfd_put_32 (abfd, (bfd_vma) x, addr);
330	  return bfd_reloc_ok;
331
332	default:
333	  bfd_perror (_("Unrecognized Reloc Type"));
334	  return bfd_reloc_notsupported;
335	}
336    }
337
338  return bfd_reloc_notsupported;
339}
340
341static reloc_howto_type howto_table[] =
342{
343  EMPTY_HOWTO (0),
344  EMPTY_HOWTO (1),
345  {
346   BFD_RELOC_32, 0, 1, 8, FALSE, 0, complain_overflow_bitfield,
347   coff_maxq20_reloc, "32Bit", TRUE, 0x000000ff, 0x000000ff, TRUE
348  },
349  {
350   SHORT_JUMP, 0, 1, 8, FALSE, 0, complain_overflow_bitfield,
351   coff_maxq20_reloc, "SHORT_JMP", TRUE, 0x000000ff, 0x000000ff, TRUE
352  },
353  {
354   ABSOLUTE_ADDR_FOR_DATA, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
355   coff_maxq20_reloc, "INTERSEGMENT_RELOC", TRUE, 0x00000000, 0x00000000,
356   FALSE
357  },
358  {
359   BFD_RELOC_16, 0, 1, 8, FALSE, 0, complain_overflow_bitfield,
360   coff_maxq20_reloc, "16Bit", TRUE, 0x000000ff, 0x000000ff, TRUE
361  },
362  {
363   LONG_JUMP, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
364   coff_maxq20_reloc, "LONG_JUMP", TRUE, 0x00000000, 0x00000000, FALSE
365  },
366  {
367   BFD_RELOC_8, 0, 1, 8, FALSE, 0, complain_overflow_bitfield,
368   coff_maxq20_reloc, "8bit", TRUE, 0x000000ff, 0x000000ff, TRUE
369  },
370  EMPTY_HOWTO (8),
371  EMPTY_HOWTO (9),
372  EMPTY_HOWTO (10),
373};
374
375static reloc_howto_type *
376maxq_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED,
377			bfd_reloc_code_real_type code)
378{
379  switch (code)
380    {
381      /* SHORT JUMP */
382    case BFD_RELOC_16_PCREL_S2:
383      return howto_table + 3;
384
385      /* INTERSEGMENT JUMP */
386    case BFD_RELOC_24:
387      return howto_table + 4;
388
389      /* BYTE RELOC */
390    case BFD_RELOC_8:
391      return howto_table + 7;
392
393      /* WORD RELOC */
394    case BFD_RELOC_16:
395      return howto_table + 5;
396
397      /* LONG RELOC */
398    case BFD_RELOC_32:
399      return howto_table + 2;
400
401      /* LONG JUMP */
402    case BFD_RELOC_14:
403      return howto_table + 6;
404
405    default:
406      return NULL;
407    }
408}
409
410static reloc_howto_type *
411maxq_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name)
412{
413  unsigned int i;
414
415  for (i = 0; i < sizeof (howto_table) / sizeof (howto_table[0]); i++)
416    if (howto_table[i].name != NULL
417	&& strcasecmp (howto_table[i].name, r_name) == 0)
418      return &howto_table[i];
419
420  return NULL;
421}
422
423#define coff_bfd_reloc_type_lookup maxq_reloc_type_lookup
424#define coff_bfd_reloc_name_lookup maxq_reloc_name_lookup
425
426/* Perform any necessary magic to the addend in a reloc entry.  */
427#define CALC_ADDEND(abfd, symbol, ext_reloc, cache_ptr) \
428 cache_ptr->addend =  ext_reloc.r_offset;
429
430#include "coffcode.h"
431
432#ifndef TARGET_UNDERSCORE
433#define TARGET_UNDERSCORE 1
434#endif
435
436#ifndef EXTRA_S_FLAGS
437#define EXTRA_S_FLAGS 0
438#endif
439
440/* Forward declaration for use initialising alternative_target field.  */
441CREATE_LITTLE_COFF_TARGET_VEC (maxqcoff_vec, "coff-maxq", 0, EXTRA_S_FLAGS,
442			       TARGET_UNDERSCORE, NULL, COFF_SWAP_TABLE);
443
444