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 
58 static long
get_symbol_value(asymbol * symbol)59 get_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 
76 static bfd_reloc_status_type
coff_maxq20_reloc(bfd * abfd,arelent * reloc_entry,asymbol * symbol_in,void * data,asection * input_section ATTRIBUTE_UNUSED,bfd * output_bfd ATTRIBUTE_UNUSED,char ** error_message ATTRIBUTE_UNUSED)77 coff_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 
343 static 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 
379 typedef 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 }
385 reloc_map;
386 
387 static 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 
393 static reloc_howto_type *
maxq_reloc_type_lookup(bfd * abfd ATTRIBUTE_UNUSED,bfd_reloc_code_real_type code)394 maxq_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.  */
456 CREATE_LITTLE_COFF_TARGET_VEC (maxqcoff_vec, "coff-maxq", 0, EXTRA_S_FLAGS,
457 			       TARGET_UNDERSCORE, NULL, COFF_SWAP_TABLE);
458 
459