1 /* BFD back-end for AMD 29000 COFF binaries.
2 Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1997, 1999, 2000, 2001, 2002
3 Free Software Foundation, Inc.
4 Contributed by David Wood at New York University 7/8/91.
6 This file is part of BFD, the Binary File Descriptor library.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
27 #include "coff/a29k.h"
28 #include "coff/internal.h"
31 static long get_symbol_value
PARAMS ((asymbol
*));
32 static bfd_reloc_status_type a29k_reloc
33 PARAMS ((bfd
*, arelent
*, asymbol
*, PTR
, asection
*, bfd
*, char **));
34 static bfd_boolean coff_a29k_relocate_section
35 PARAMS ((bfd
*, struct bfd_link_info
*, bfd
*, asection
*, bfd_byte
*,
36 struct internal_reloc
*, struct internal_syment
*, asection
**));
37 static bfd_boolean coff_a29k_adjust_symndx
38 PARAMS ((bfd
*, struct bfd_link_info
*, bfd
*, asection
*,
39 struct internal_reloc
*, bfd_boolean
*));
40 static void reloc_processing
41 PARAMS ((arelent
*, struct internal_reloc
*, asymbol
**, bfd
*, asection
*));
43 #define COFF_DEFAULT_SECTION_ALIGNMENT_POWER (2)
45 #define INSERT_HWORD(WORD,HWORD) \
46 (((WORD) & 0xff00ff00) | (((HWORD) & 0xff00) << 8) | ((HWORD)& 0xff))
47 #define EXTRACT_HWORD(WORD) \
48 ((((WORD) & 0x00ff0000) >> 8) | ((WORD) & 0xff))
49 #define SIGN_EXTEND_HWORD(HWORD) \
50 (((HWORD) ^ 0x8000) - 0x8000)
52 /* Provided the symbol, returns the value reffed. */
55 get_symbol_value (symbol
)
60 if (bfd_is_com_section (symbol
->section
))
63 relocation
= symbol
->value
+
64 symbol
->section
->output_section
->vma
+
65 symbol
->section
->output_offset
;
70 /* This function is in charge of performing all the 29k relocations. */
72 static bfd_reloc_status_type
73 a29k_reloc (abfd
, reloc_entry
, symbol_in
, data
, input_section
, output_bfd
,
79 asection
*input_section
;
83 /* The consth relocation comes in two parts, we have to remember
84 the state between calls, in these variables. */
85 static bfd_boolean part1_consth_active
= FALSE
;
86 static unsigned long part1_consth_value
;
88 unsigned long sym_value
;
89 unsigned long unsigned_value
;
90 unsigned short r_type
;
92 unsigned long addr
= reloc_entry
->address
; /*+ input_section->vma*/
93 bfd_byte
*hit_data
=addr
+ (bfd_byte
*) (data
);
95 r_type
= reloc_entry
->howto
->type
;
99 /* Partial linking - do nothing. */
100 reloc_entry
->address
+= input_section
->output_offset
;
104 if (symbol_in
!= NULL
105 && bfd_is_und_section (symbol_in
->section
))
107 /* Keep the state machine happy in case we're called again. */
108 if (r_type
== R_IHIHALF
)
110 part1_consth_active
= TRUE
;
111 part1_consth_value
= 0;
113 return bfd_reloc_undefined
;
116 if ((part1_consth_active
) && (r_type
!= R_IHCONST
))
118 part1_consth_active
= FALSE
;
119 *error_message
= (char *) _("Missing IHCONST");
121 return bfd_reloc_dangerous
;
124 sym_value
= get_symbol_value(symbol_in
);
129 insn
= bfd_get_32 (abfd
, hit_data
);
130 /* Take the value in the field and sign extend it. */
131 signed_value
= EXTRACT_HWORD(insn
);
132 signed_value
= SIGN_EXTEND_HWORD(signed_value
);
135 /* See the note on the R_IREL reloc in coff_a29k_relocate_section. */
136 if (signed_value
== - (long) reloc_entry
->address
)
139 signed_value
+= sym_value
+ reloc_entry
->addend
;
140 if ((signed_value
& ~0x3ffff) == 0)
141 { /* Absolute jmp/call */
142 insn
|= (1 << 24); /* Make it absolute */
143 /* FIXME: Should we change r_type to R_IABS. */
147 /* Relative jmp/call, so subtract from the value the
148 address of the place we're coming from. */
149 signed_value
-= (reloc_entry
->address
150 + input_section
->output_section
->vma
151 + input_section
->output_offset
);
152 if (signed_value
> 0x1ffff || signed_value
< -0x20000)
153 return bfd_reloc_overflow
;
156 insn
= INSERT_HWORD (insn
, signed_value
);
157 bfd_put_32 (abfd
, (bfd_vma
) insn
,hit_data
);
160 insn
= bfd_get_32 (abfd
, hit_data
);
161 unsigned_value
= EXTRACT_HWORD(insn
);
162 unsigned_value
+= sym_value
+ reloc_entry
->addend
;
163 insn
= INSERT_HWORD(insn
, unsigned_value
);
164 bfd_put_32 (abfd
, (bfd_vma
) insn
, hit_data
);
167 insn
= bfd_get_32 (abfd
, hit_data
);
169 Just get the symbol value that is referenced. */
170 part1_consth_active
= TRUE
;
171 part1_consth_value
= sym_value
+ reloc_entry
->addend
;
172 /* Don't modify insn until R_IHCONST. */
175 insn
= bfd_get_32 (abfd
, hit_data
);
177 Now relocate the reference. */
178 if (! part1_consth_active
)
180 *error_message
= (char *) _("Missing IHIHALF");
181 return bfd_reloc_dangerous
;
183 /* sym_ptr_ptr = r_symndx, in coff_slurp_reloc_table() */
184 unsigned_value
= 0; /*EXTRACT_HWORD(insn) << 16;*/
185 unsigned_value
+= reloc_entry
->addend
; /* r_symndx */
186 unsigned_value
+= part1_consth_value
;
187 unsigned_value
= unsigned_value
>> 16;
188 insn
= INSERT_HWORD(insn
, unsigned_value
);
189 part1_consth_active
= FALSE
;
190 bfd_put_32 (abfd
, (bfd_vma
) insn
, hit_data
);
193 insn
= bfd_get_8 (abfd
, hit_data
);
194 unsigned_value
= insn
+ sym_value
+ reloc_entry
->addend
;
195 if (unsigned_value
& 0xffffff00)
196 return bfd_reloc_overflow
;
197 bfd_put_8 (abfd
, unsigned_value
, hit_data
);
200 insn
= bfd_get_16 (abfd
, hit_data
);
201 unsigned_value
= insn
+ sym_value
+ reloc_entry
->addend
;
202 if (unsigned_value
& 0xffff0000)
203 return bfd_reloc_overflow
;
204 bfd_put_16 (abfd
, (bfd_vma
) insn
, hit_data
);
207 insn
= bfd_get_32 (abfd
, hit_data
);
208 insn
+= sym_value
+ reloc_entry
->addend
;
209 bfd_put_32 (abfd
, (bfd_vma
) insn
, hit_data
);
212 *error_message
= _("Unrecognized reloc");
213 return bfd_reloc_dangerous
;
216 return(bfd_reloc_ok
);
219 /*FIXME: I'm not real sure about this table. */
220 static reloc_howto_type howto_table
[] =
222 {R_ABS
, 0, 3, 32, FALSE
, 0, complain_overflow_bitfield
,a29k_reloc
,"ABS", TRUE
, 0xffffffff,0xffffffff, FALSE
},
246 {R_IREL
, 0, 3, 32, TRUE
, 0, complain_overflow_signed
,a29k_reloc
,"IREL", TRUE
, 0xffffffff,0xffffffff, FALSE
},
247 {R_IABS
, 0, 3, 32, FALSE
, 0, complain_overflow_bitfield
, a29k_reloc
,"IABS", TRUE
, 0xffffffff,0xffffffff, FALSE
},
248 {R_ILOHALF
, 0, 3, 16, TRUE
, 0, complain_overflow_signed
, a29k_reloc
,"ILOHALF", TRUE
, 0x0000ffff,0x0000ffff, FALSE
},
249 {R_IHIHALF
, 0, 3, 16, TRUE
, 16, complain_overflow_signed
, a29k_reloc
,"IHIHALF", TRUE
, 0xffff0000,0xffff0000, FALSE
},
250 {R_IHCONST
, 0, 3, 16, TRUE
, 0, complain_overflow_signed
, a29k_reloc
,"IHCONST", TRUE
, 0xffff0000,0xffff0000, FALSE
},
251 {R_BYTE
, 0, 0, 8, FALSE
, 0, complain_overflow_bitfield
, a29k_reloc
,"BYTE", TRUE
, 0x000000ff,0x000000ff, FALSE
},
252 {R_HWORD
, 0, 1, 16, FALSE
, 0, complain_overflow_bitfield
, a29k_reloc
,"HWORD", TRUE
, 0x0000ffff,0x0000ffff, FALSE
},
253 {R_WORD
, 0, 2, 32, FALSE
, 0, complain_overflow_bitfield
, a29k_reloc
,"WORD", TRUE
, 0xffffffff,0xffffffff, FALSE
},
256 #define BADMAG(x) A29KBADMAG(x)
258 #define RELOC_PROCESSING(relent, reloc, symbols, abfd, section) \
259 reloc_processing(relent, reloc, symbols, abfd, section)
262 reloc_processing (relent
,reloc
, symbols
, abfd
, section
)
264 struct internal_reloc
*reloc
;
269 static bfd_vma ihihalf_vaddr
= (bfd_vma
) -1;
271 relent
->address
= reloc
->r_vaddr
;
272 relent
->howto
= howto_table
+ reloc
->r_type
;
273 if (reloc
->r_type
== R_IHCONST
)
275 /* The address of an R_IHCONST should always be the address of
276 the immediately preceding R_IHIHALF. relocs generated by gas
277 are correct, but relocs generated by High C are different (I
278 can't figure out what the address means for High C). We can
279 handle both gas and High C by ignoring the address here, and
280 simply reusing the address saved for R_IHIHALF. */
281 if (ihihalf_vaddr
== (bfd_vma
) -1)
283 relent
->address
= ihihalf_vaddr
;
284 ihihalf_vaddr
= (bfd_vma
) -1;
285 relent
->addend
= reloc
->r_symndx
;
286 relent
->sym_ptr_ptr
= bfd_abs_section_ptr
->symbol_ptr_ptr
;
292 relent
->sym_ptr_ptr
= symbols
+ obj_convert (abfd
)[reloc
->r_symndx
];
294 ptr
= *(relent
->sym_ptr_ptr
);
297 && bfd_asymbol_bfd(ptr
) == abfd
298 && ((ptr
->flags
& BSF_OLD_COMMON
) == 0))
303 relent
->address
-= section
->vma
;
304 if (reloc
->r_type
== R_IHIHALF
)
305 ihihalf_vaddr
= relent
->address
;
306 else if (ihihalf_vaddr
!= (bfd_vma
) -1)
311 /* The reloc processing routine for the optimized COFF linker. */
314 coff_a29k_relocate_section (output_bfd
, info
, input_bfd
, input_section
,
315 contents
, relocs
, syms
, sections
)
316 bfd
*output_bfd ATTRIBUTE_UNUSED
;
317 struct bfd_link_info
*info
;
319 asection
*input_section
;
321 struct internal_reloc
*relocs
;
322 struct internal_syment
*syms
;
325 struct internal_reloc
*rel
;
326 struct internal_reloc
*relend
;
330 /* If we are performing a relocateable link, we don't need to do a
331 thing. The caller will take care of adjusting the reloc
332 addresses and symbol indices. */
333 if (info
->relocateable
)
340 relend
= rel
+ input_section
->reloc_count
;
341 for (; rel
< relend
; rel
++)
345 struct coff_link_hash_entry
*h
;
346 struct internal_syment
*sym
;
349 bfd_boolean overflow
;
352 unsigned long unsigned_value
;
353 bfd_reloc_status_type rstat
;
355 symndx
= rel
->r_symndx
;
356 loc
= contents
+ rel
->r_vaddr
- input_section
->vma
;
358 if (symndx
== -1 || rel
->r_type
== R_IHCONST
)
361 h
= obj_coff_sym_hashes (input_bfd
)[symndx
];
367 /* An R_IHCONST reloc does not have a symbol. Instead, the
368 symbol index is an addend. R_IHCONST is always used in
369 conjunction with R_IHHALF. */
370 if (rel
->r_type
!= R_IHCONST
)
375 sec
= bfd_abs_section_ptr
;
379 sec
= sections
[symndx
];
380 val
= (sec
->output_section
->vma
388 if ( h
->root
.type
== bfd_link_hash_defined
389 || h
->root
.type
== bfd_link_hash_defweak
)
391 sec
= h
->root
.u
.def
.section
;
392 val
= (h
->root
.u
.def
.value
393 + sec
->output_section
->vma
394 + sec
->output_offset
);
398 if (! ((*info
->callbacks
->undefined_symbol
)
399 (info
, h
->root
.root
.string
, input_bfd
, input_section
,
400 rel
->r_vaddr
- input_section
->vma
, TRUE
)))
407 if (! ((*info
->callbacks
->reloc_dangerous
)
408 (info
, _("missing IHCONST reloc"), input_bfd
,
409 input_section
, rel
->r_vaddr
- input_section
->vma
)))
420 bfd_set_error (bfd_error_bad_value
);
424 insn
= bfd_get_32 (input_bfd
, loc
);
426 /* Extract the addend. */
427 signed_value
= EXTRACT_HWORD (insn
);
428 signed_value
= SIGN_EXTEND_HWORD (signed_value
);
431 /* Unfortunately, there are two different versions of COFF
432 a29k. In the original AMD version, the value stored in
433 the field for the R_IREL reloc is a simple addend. In
434 the GNU version, the value is the negative of the address
435 of the reloc within section. We try to cope here by
436 assuming the AMD version, unless the addend is exactly
437 the negative of the address; in the latter case we assume
438 the GNU version. This means that something like
442 will fail, because the addend of -4 will happen to equal
443 the negative of the address within the section. The
444 compiler will never generate code like this.
446 At some point in the future we may want to take out this
449 if (signed_value
== - (long) (rel
->r_vaddr
- input_section
->vma
))
452 /* Determine the destination of the jump. */
455 if ((signed_value
& ~0x3ffff) == 0)
457 /* We can use an absolute jump. */
462 /* Make the destination PC relative. */
463 signed_value
-= (input_section
->output_section
->vma
464 + input_section
->output_offset
465 + (rel
->r_vaddr
- input_section
->vma
));
466 if (signed_value
> 0x1ffff || signed_value
< - 0x20000)
473 /* Put the adjusted value back into the instruction. */
475 insn
= INSERT_HWORD (insn
, signed_value
);
477 bfd_put_32 (input_bfd
, (bfd_vma
) insn
, loc
);
481 insn
= bfd_get_32 (input_bfd
, loc
);
482 unsigned_value
= EXTRACT_HWORD (insn
);
483 unsigned_value
+= val
;
484 insn
= INSERT_HWORD (insn
, unsigned_value
);
485 bfd_put_32 (input_bfd
, (bfd_vma
) insn
, loc
);
489 /* Save the value for the R_IHCONST reloc. */
497 if (! ((*info
->callbacks
->reloc_dangerous
)
498 (info
, _("missing IHIHALF reloc"), input_bfd
,
499 input_section
, rel
->r_vaddr
- input_section
->vma
)))
504 insn
= bfd_get_32 (input_bfd
, loc
);
505 unsigned_value
= rel
->r_symndx
+ hihalf_val
;
506 unsigned_value
>>= 16;
507 insn
= INSERT_HWORD (insn
, unsigned_value
);
508 bfd_put_32 (input_bfd
, (bfd_vma
) insn
, loc
);
517 rstat
= _bfd_relocate_contents (howto_table
+ rel
->r_type
,
518 input_bfd
, val
, loc
);
519 if (rstat
== bfd_reloc_overflow
)
521 else if (rstat
!= bfd_reloc_ok
)
529 char buf
[SYMNMLEN
+ 1];
534 name
= h
->root
.root
.string
;
535 else if (sym
== NULL
)
537 else if (sym
->_n
._n_n
._n_zeroes
== 0
538 && sym
->_n
._n_n
._n_offset
!= 0)
539 name
= obj_coff_strings (input_bfd
) + sym
->_n
._n_n
._n_offset
;
542 strncpy (buf
, sym
->_n
._n_name
, SYMNMLEN
);
543 buf
[SYMNMLEN
] = '\0';
547 if (! ((*info
->callbacks
->reloc_overflow
)
548 (info
, name
, howto_table
[rel
->r_type
].name
, (bfd_vma
) 0,
549 input_bfd
, input_section
,
550 rel
->r_vaddr
- input_section
->vma
)))
558 #define coff_relocate_section coff_a29k_relocate_section
560 /* We don't want to change the symndx of a R_IHCONST reloc, since it
561 is actually an addend, not a symbol index at all. */
564 coff_a29k_adjust_symndx (obfd
, info
, ibfd
, sec
, irel
, adjustedp
)
565 bfd
*obfd ATTRIBUTE_UNUSED
;
566 struct bfd_link_info
*info ATTRIBUTE_UNUSED
;
567 bfd
*ibfd ATTRIBUTE_UNUSED
;
568 asection
*sec ATTRIBUTE_UNUSED
;
569 struct internal_reloc
*irel
;
570 bfd_boolean
*adjustedp
;
572 if (irel
->r_type
== R_IHCONST
)
579 #define coff_adjust_symndx coff_a29k_adjust_symndx
581 #include "coffcode.h"
583 CREATE_BIG_COFF_TARGET_VEC (a29kcoff_big_vec
, "coff-a29k-big", 0, SEC_READONLY
, '_', NULL
)