aboutsummaryrefslogtreecommitdiff
path: root/bfd/coff-maxq.c
blob: 94d5237d4a028eec071eb422b8134e481b00144d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
/* BFD back-end for MAXQ COFF binaries.
   Copyright 2004, 2005, 2006, 2007, 2008  Free Software Foundation, Inc.

   Contributed by Vineet Sharma (vineets@noida.hcltech.com) Inderpreet S.
   (inderpreetb@noida.hcltech.com)

   HCL Technologies Ltd.

   This file is part of BFD, the Binary File Descriptor library.

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the Free 
   Software Foundation; either version 3 of the License, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation, Inc., 
   51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */

#include "sysdep.h"
#include "bfd.h"
#include "libbfd.h"
#include "coff/maxq.h"
#include "coff/internal.h"
#include "libcoff.h"
#include "libiberty.h"

#ifndef MAXQ20
#define MAXQ20 1
#endif

#define RTYPE2HOWTO(cache_ptr, dst)                                     \
  ((cache_ptr)->howto =                                                 \
   ((dst)->r_type < 48 							\
    ? howto_table + (((dst)->r_type==47) ? 6: ((dst)->r_type))		\
    : NULL))

#define COFF_DEFAULT_SECTION_ALIGNMENT_POWER (2)

/* Code to swap in the reloc offset.  */
#define SWAP_IN_RELOC_OFFSET    H_GET_16
#define SWAP_OUT_RELOC_OFFSET   H_PUT_16

#define SHORT_JUMP 	        BFD_RELOC_16_PCREL_S2
#define LONG_JUMP 	        BFD_RELOC_14
#define ABSOLUTE_ADDR_FOR_DATA  BFD_RELOC_24

/* checks the range of short jump -127 to 128 */
#define IS_SJUMP_RANGE(x) ((x > -128) && (x < 129))
#define HIGH_WORD_MASK    0xff00
#define LOW_WORD_MASK     0x00ff

static long
get_symbol_value (asymbol *symbol)
{
  long relocation = 0;

  if (bfd_is_com_section (symbol->section))
    relocation = 0;
  else
    relocation = symbol->value +
      symbol->section->output_section->vma + symbol->section->output_offset;

  return relocation;
}

/* This function performs all the maxq relocations.
   FIXME:  The handling of the addend in the 'BFD_*'
   relocations types.  */

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)
{
  unsigned char *addr = NULL;
  unsigned long x = 0;
  long call_addr = 0;
  short addend = 0;
  long diff = 0;

  /* If this is an undefined symbol, return error.  */
  if (symbol_in->section == &bfd_und_section
      && (symbol_in->flags & BSF_WEAK) == 0)
    return bfd_reloc_continue;

  if (data && reloc_entry)
    {
      addr = (unsigned char *) data + reloc_entry->address;
      call_addr = call_addr - call_addr;
      call_addr = get_symbol_value (symbol_in);

      /* Over here the value val stores the 8 bit/16 bit value. We will put a 
         check if we are moving a 16 bit immediate value into an 8 bit
         register. In that case we will generate a Upper bytes into PFX[0]
         and move the lower 8 bits as SRC.  */

      switch (reloc_entry->howto->type)
	{
	  /* BFD_RELOC_16_PCREL_S2 47 Handles all the relative jumps and
	     calls Note: Every relative jump or call is in words.  */
	case SHORT_JUMP:
	  /* Handle any addend.  */
	  addend = reloc_entry->addend;

	  if (addend > call_addr || addend > 0)
	    call_addr = symbol_in->section->output_section->vma + addend;
	  else if (addend < call_addr && addend > 0)
	    call_addr = call_addr + addend;
	  else if (addend < 0)
	    call_addr = call_addr + addend;

	  diff = ((call_addr << 1) - (reloc_entry->address << 1));

	  if (!IS_SJUMP_RANGE (diff))
	    {
	      bfd_perror (_("Can't Make it a Short Jump"));
	      return bfd_reloc_outofrange;
	    }

	  x = bfd_get_16 (abfd, addr);

	  x = x & LOW_WORD_MASK;
	  x = x | (diff << 8);
	  bfd_put_16 (abfd, (bfd_vma) x, addr);

	  return bfd_reloc_ok;

	case ABSOLUTE_ADDR_FOR_DATA:
	case LONG_JUMP:
	  /* BFD_RELOC_14 Handles intersegment or long jumps which might be
	     from code to code or code to data segment jumps. Note: When this 
	     fucntion is called by gas the section flags somehow do not
	     contain the info about the section type(CODE or DATA). Thus the
	     user needs to evoke the linker after assembling the files
	     because the Code-Code relocs are word aligned but code-data are
	     byte aligned.  */
	  addend = (reloc_entry->addend - reloc_entry->addend);

	  /* Handle any addend.  */
	  addend = reloc_entry->addend;

	  /* For relocation involving multiple file added becomes zero thus
	     this fails - check for zero added. In another case when we try
	     to add a stub to a file the addend shows the offset from the
	     start od this file.  */
	  addend = 0;

	  if (!bfd_is_com_section (symbol_in->section) &&
	      ((symbol_in->flags & BSF_OLD_COMMON) == 0))
	    {
	      if (reloc_entry->addend > symbol_in->value)
		addend = reloc_entry->addend - symbol_in->value;

	      if ((reloc_entry->addend < symbol_in->value)
		  && (reloc_entry->addend != 0))
		addend = reloc_entry->addend - symbol_in->value;

	      if (reloc_entry->addend == symbol_in->value)
		addend = 0;
	    }

	  if (bfd_is_com_section (symbol_in->section) ||
	      ((symbol_in->flags & BSF_OLD_COMMON) != 0))
	    addend = reloc_entry->addend;

	  if (addend < 0
	      &&  (call_addr < (long) (addend * (-1))))
	    addend = 0;

	  call_addr += addend;

	  /* FIXME: This check does not work well with the assembler,
	     linker needs to be run always.  */
	  if ((symbol_in->section->flags & SEC_CODE) == SEC_CODE)
	    {
	      /* Convert it into words.  */
	      call_addr = call_addr >> 1;

	      if (call_addr > 0xFFFF)	/* Intersegment Jump.  */
		{
		  bfd_perror (_("Exceeds Long Jump Range"));
		  return bfd_reloc_outofrange;
		}
	    }
	  else
	    {
	      /* case ABSOLUTE_ADDR_FOR_DATA : Resolves any code-data
		 segemnt relocs. These are NOT word aligned.  */

	      if (call_addr > 0xFFFF)	/* Intersegment Jump.  */
		{
		  bfd_perror (_("Absolute address Exceeds 16 bit Range"));
		  return bfd_reloc_outofrange;
		}
	    }

	  x = bfd_get_32 (abfd, addr);

	  x = (x & 0xFF00FF00);
	  x = (x | ((call_addr & HIGH_WORD_MASK) >> 8));
	  x = (x | (call_addr & LOW_WORD_MASK) << 16);

	  bfd_put_32 (abfd, (bfd_vma) x, addr);
	  return bfd_reloc_ok;

	case BFD_RELOC_8:
	  addend = (reloc_entry->addend - reloc_entry->addend);

	  if (!bfd_is_com_section (symbol_in->section) &&
	      ((symbol_in->flags & BSF_OLD_COMMON) == 0))
	    {
	      if (reloc_entry->addend > symbol_in->value)
		addend = reloc_entry->addend - symbol_in->value;
	      if (reloc_entry->addend < symbol_in->value)
		addend = reloc_entry->addend - symbol_in->value;
	      if (reloc_entry->addend == symbol_in->value)
		addend = 0;
	    }

	  if (bfd_is_com_section (symbol_in->section) ||
	      ((symbol_in->flags & BSF_OLD_COMMON) != 0))
	    addend = reloc_entry->addend;

	  if (addend < 0
	      && (call_addr < (long) (addend * (-1))))
	    addend = 0;

	  if (call_addr + addend > 0xFF)
	    {
	      bfd_perror (_("Absolute address Exceeds 8 bit Range"));
	      return bfd_reloc_outofrange;
	    }

	  x = bfd_get_8 (abfd, addr);
	  x = x & 0x00;
	  x = x | (call_addr + addend);

	  bfd_put_8 (abfd, (bfd_vma) x, addr);
	  return bfd_reloc_ok;

	case BFD_RELOC_16:
	  addend = (reloc_entry->addend - reloc_entry->addend);
	  if (!bfd_is_com_section (symbol_in->section) &&
	      ((symbol_in->flags & BSF_OLD_COMMON) == 0))
	    {
	      if (reloc_entry->addend > symbol_in->value)
		addend = reloc_entry->addend - symbol_in->value;

	      if (reloc_entry->addend < symbol_in->value)
		addend = reloc_entry->addend - symbol_in->value;

	      if (reloc_entry->addend == symbol_in->value)
		addend = 0;
	    }

	  if (bfd_is_com_section (symbol_in->section) ||
	      ((symbol_in->flags & BSF_OLD_COMMON) != 0))
	    addend = reloc_entry->addend;

	  if (addend < 0
	      && (call_addr < (long) (addend * (-1))))
	    addend = 0;

	  if ((call_addr + addend) > 0xFFFF)
	    {
	      bfd_perror (_("Absolute address Exceeds 16 bit Range"));
	      return bfd_reloc_outofrange;
	    }
	  else
	    {
	      unsigned short val = (call_addr + addend);

	      x = bfd_get_16 (abfd, addr);

	      /* LE */
	      x = (x & 0x0000);	/* Flush garbage value.  */
	      x = val;
	      if ((symbol_in->section->flags & SEC_CODE) == SEC_CODE)
		x = x >> 1;	/* Convert it into words.  */
	    }

	  bfd_put_16 (abfd, (bfd_vma) x, addr);
	  return bfd_reloc_ok;

	case BFD_RELOC_32:
	  addend = (reloc_entry->addend - reloc_entry->addend);

	  if (!bfd_is_com_section (symbol_in->section) &&
	      ((symbol_in->flags & BSF_OLD_COMMON) == 0))
	    {
	      if (reloc_entry->addend > symbol_in->value)
		addend = reloc_entry->addend - symbol_in->value;
	      if (reloc_entry->addend < symbol_in->value)
		addend = reloc_entry->addend - symbol_in->value;
	      if (reloc_entry->addend == symbol_in->value)
		addend = 0;
	    }

	  if (bfd_is_com_section (symbol_in->section) ||
	      ((symbol_in->flags & BSF_OLD_COMMON) != 0))
	    addend = reloc_entry->addend;

	  if (addend < 0
	      && (call_addr < (long) (addend * (-1))))
	    addend = 0;

	  if ((call_addr + addend) < 0)
	    {
	      bfd_perror ("Absolute address Exceeds 32 bit Range");
	      return bfd_reloc_outofrange;
	    }

	  x = bfd_get_32 (abfd, addr);
	  x = (x & 0x0000);	/* Flush garbage value.  */
	  x = call_addr + addend;
	  if ((symbol_in->section->flags & SEC_CODE) == SEC_CODE)
	    x = x >> 1;	/* Convert it into words.  */

	  bfd_put_32 (abfd, (bfd_vma) x, addr);
	  return bfd_reloc_ok;

	default:
	  bfd_perror (_("Unrecognized Reloc Type"));
	  return bfd_reloc_notsupported;
	}
    }

  return bfd_reloc_notsupported;
}

static reloc_howto_type howto_table[] =
{
  EMPTY_HOWTO (0),
  EMPTY_HOWTO (1),
  {
   BFD_RELOC_32, 0, 1, 8, FALSE, 0, complain_overflow_bitfield,
   coff_maxq20_reloc, "32Bit", TRUE, 0x000000ff, 0x000000ff, TRUE
  },
  {
   SHORT_JUMP, 0, 1, 8, FALSE, 0, complain_overflow_bitfield,
   coff_maxq20_reloc, "SHORT_JMP", TRUE, 0x000000ff, 0x000000ff, TRUE
  },
  {
   ABSOLUTE_ADDR_FOR_DATA, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
   coff_maxq20_reloc, "INTERSEGMENT_RELOC", TRUE, 0x00000000, 0x00000000,
   FALSE
  },
  {
   BFD_RELOC_16, 0, 1, 8, FALSE, 0, complain_overflow_bitfield,
   coff_maxq20_reloc, "16Bit", TRUE, 0x000000ff, 0x000000ff, TRUE
  },
  {
   LONG_JUMP, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
   coff_maxq20_reloc, "LONG_JUMP", TRUE, 0x00000000, 0x00000000, FALSE
  },
  {
   BFD_RELOC_8, 0, 1, 8, FALSE, 0, complain_overflow_bitfield,
   coff_maxq20_reloc, "8bit", TRUE, 0x000000ff, 0x000000ff, TRUE
  },
  EMPTY_HOWTO (8),
  EMPTY_HOWTO (9),
  EMPTY_HOWTO (10),
};

static reloc_howto_type *
maxq_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED,
			bfd_reloc_code_real_type code)
{
  switch (code)
    {
      /* SHORT JUMP */
    case BFD_RELOC_16_PCREL_S2:
      return howto_table + 3;
      
      /* INTERSEGMENT JUMP */
    case BFD_RELOC_24:
      return howto_table + 4;
      
      /* BYTE RELOC */
    case BFD_RELOC_8:
      return howto_table + 7;
      
      /* WORD RELOC */
    case BFD_RELOC_16:
      return howto_table + 5;
      
      /* LONG RELOC */
    case BFD_RELOC_32:
      return howto_table + 2;
      
      /* LONG JUMP */
    case BFD_RELOC_14:
      return howto_table + 6;
      
    default:
      return NULL;
    }
}

static reloc_howto_type *
maxq_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name)
{
  unsigned int i;

  for (i = 0; i < sizeof (howto_table) / sizeof (howto_table[0]); i++)
    if (howto_table[i].name != NULL
	&& strcasecmp (howto_table[i].name, r_name) == 0)
      return &howto_table[i];

  return NULL;
}

#define coff_bfd_reloc_type_lookup maxq_reloc_type_lookup
#define coff_bfd_reloc_name_lookup maxq_reloc_name_lookup

/* Perform any necessary magic to the addend in a reloc entry.  */
#define CALC_ADDEND(abfd, symbol, ext_reloc, cache_ptr) \
 cache_ptr->addend =  ext_reloc.r_offset;

#ifndef bfd_pe_print_pdata
#define bfd_pe_print_pdata	NULL
#endif

#include "coffcode.h"

#ifndef TARGET_UNDERSCORE
#define TARGET_UNDERSCORE 1
#endif

#ifndef EXTRA_S_FLAGS
#define EXTRA_S_FLAGS 0
#endif

/* Forward declaration for use initialising alternative_target field.  */
CREATE_LITTLE_COFF_TARGET_VEC (maxqcoff_vec, "coff-maxq", 0, EXTRA_S_FLAGS,
			       TARGET_UNDERSCORE, NULL, COFF_SWAP_TABLE);