1 |
/* BFD back-end for MIPS Extended-Coff files. |
/* BFD back-end for MIPS Extended-Coff files. |
2 |
Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, |
Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, |
3 |
2000, 2001, 2002 |
2000, 2001, 2002, 2003, 2004 |
4 |
Free Software Foundation, Inc. |
Free Software Foundation, Inc. |
5 |
Original version by Per Bothner. |
Original version by Per Bothner. |
6 |
Full support added by Ian Lance Taylor, ian@cygnus.com. |
Full support added by Ian Lance Taylor, ian@cygnus.com. |
35 |
|
|
36 |
/* Prototypes for static functions. */ |
/* Prototypes for static functions. */ |
37 |
|
|
38 |
static boolean mips_ecoff_bad_format_hook PARAMS ((bfd *abfd, PTR filehdr)); |
static bfd_boolean mips_ecoff_bad_format_hook |
39 |
static void mips_ecoff_swap_reloc_in PARAMS ((bfd *, PTR, |
PARAMS ((bfd *abfd, PTR filehdr)); |
40 |
struct internal_reloc *)); |
static void mips_ecoff_swap_reloc_in |
41 |
static void mips_ecoff_swap_reloc_out PARAMS ((bfd *, |
PARAMS ((bfd *, PTR, struct internal_reloc *)); |
42 |
const struct internal_reloc *, |
static void mips_ecoff_swap_reloc_out |
43 |
PTR)); |
PARAMS ((bfd *, const struct internal_reloc *, PTR)); |
44 |
static void mips_adjust_reloc_in PARAMS ((bfd *, |
static void mips_adjust_reloc_in |
45 |
const struct internal_reloc *, |
PARAMS ((bfd *, const struct internal_reloc *, arelent *)); |
46 |
arelent *)); |
static void mips_adjust_reloc_out |
47 |
static void mips_adjust_reloc_out PARAMS ((bfd *, const arelent *, |
PARAMS ((bfd *, const arelent *, struct internal_reloc *)); |
48 |
struct internal_reloc *)); |
static bfd_reloc_status_type mips_generic_reloc |
49 |
static bfd_reloc_status_type mips_generic_reloc PARAMS ((bfd *abfd, |
PARAMS ((bfd *abfd, arelent *reloc, asymbol *symbol, PTR data, |
50 |
arelent *reloc, |
asection *section, bfd *output_bfd, char **error)); |
51 |
asymbol *symbol, |
static bfd_reloc_status_type mips_refhi_reloc |
52 |
PTR data, |
PARAMS ((bfd *abfd, arelent *reloc, asymbol *symbol, PTR data, |
53 |
asection *section, |
asection *section, bfd *output_bfd, char **error)); |
54 |
bfd *output_bfd, |
static bfd_reloc_status_type mips_reflo_reloc |
55 |
char **error)); |
PARAMS ((bfd *abfd, arelent *reloc, asymbol *symbol, PTR data, |
56 |
static bfd_reloc_status_type mips_refhi_reloc PARAMS ((bfd *abfd, |
asection *section, bfd *output_bfd, char **error)); |
57 |
arelent *reloc, |
static bfd_reloc_status_type mips_gprel_reloc |
58 |
asymbol *symbol, |
PARAMS ((bfd *abfd, arelent *reloc, asymbol *symbol, PTR data, |
59 |
PTR data, |
asection *section, bfd *output_bfd, char **error)); |
60 |
asection *section, |
static void mips_relocate_hi |
61 |
bfd *output_bfd, |
PARAMS ((struct internal_reloc *refhi, struct internal_reloc *reflo, |
62 |
char **error)); |
bfd *input_bfd, asection *input_section, bfd_byte *contents, |
63 |
static bfd_reloc_status_type mips_reflo_reloc PARAMS ((bfd *abfd, |
bfd_vma relocation)); |
64 |
arelent *reloc, |
static bfd_boolean mips_relocate_section |
65 |
asymbol *symbol, |
PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, PTR)); |
|
PTR data, |
|
|
asection *section, |
|
|
bfd *output_bfd, |
|
|
char **error)); |
|
|
static bfd_reloc_status_type mips_gprel_reloc PARAMS ((bfd *abfd, |
|
|
arelent *reloc, |
|
|
asymbol *symbol, |
|
|
PTR data, |
|
|
asection *section, |
|
|
bfd *output_bfd, |
|
|
char **error)); |
|
|
static bfd_reloc_status_type mips_relhi_reloc PARAMS ((bfd *abfd, |
|
|
arelent *reloc, |
|
|
asymbol *symbol, |
|
|
PTR data, |
|
|
asection *section, |
|
|
bfd *output_bfd, |
|
|
char **error)); |
|
|
static bfd_reloc_status_type mips_rello_reloc PARAMS ((bfd *abfd, |
|
|
arelent *reloc, |
|
|
asymbol *symbol, |
|
|
PTR data, |
|
|
asection *section, |
|
|
bfd *output_bfd, |
|
|
char **error)); |
|
|
static bfd_reloc_status_type mips_switch_reloc PARAMS ((bfd *abfd, |
|
|
arelent *reloc, |
|
|
asymbol *symbol, |
|
|
PTR data, |
|
|
asection *section, |
|
|
bfd *output_bfd, |
|
|
char **error)); |
|
|
static void mips_relocate_hi PARAMS ((struct internal_reloc *refhi, |
|
|
struct internal_reloc *reflo, |
|
|
bfd *input_bfd, |
|
|
asection *input_section, |
|
|
bfd_byte *contents, |
|
|
size_t adjust, |
|
|
bfd_vma relocation, |
|
|
boolean pcrel)); |
|
|
static boolean mips_relocate_section PARAMS ((bfd *, struct bfd_link_info *, |
|
|
bfd *, asection *, |
|
|
bfd_byte *, PTR)); |
|
|
static boolean mips_read_relocs PARAMS ((bfd *, asection *)); |
|
|
static boolean mips_relax_section PARAMS ((bfd *, asection *, |
|
|
struct bfd_link_info *, |
|
|
boolean *)); |
|
|
static boolean mips_relax_pcrel16 PARAMS ((struct bfd_link_info *, bfd *, |
|
|
asection *, |
|
|
struct ecoff_link_hash_entry *, |
|
|
bfd_byte *, bfd_vma)); |
|
66 |
static reloc_howto_type *mips_bfd_reloc_type_lookup |
static reloc_howto_type *mips_bfd_reloc_type_lookup |
67 |
PARAMS ((bfd *, bfd_reloc_code_real_type)); |
PARAMS ((bfd *, bfd_reloc_code_real_type)); |
68 |
|
|
101 |
0, /* rightshift */ |
0, /* rightshift */ |
102 |
0, /* size (0 = byte, 1 = short, 2 = long) */ |
0, /* size (0 = byte, 1 = short, 2 = long) */ |
103 |
8, /* bitsize */ |
8, /* bitsize */ |
104 |
false, /* pc_relative */ |
FALSE, /* pc_relative */ |
105 |
0, /* bitpos */ |
0, /* bitpos */ |
106 |
complain_overflow_dont, /* complain_on_overflow */ |
complain_overflow_dont, /* complain_on_overflow */ |
107 |
0, /* special_function */ |
0, /* special_function */ |
108 |
"IGNORE", /* name */ |
"IGNORE", /* name */ |
109 |
false, /* partial_inplace */ |
FALSE, /* partial_inplace */ |
110 |
0, /* src_mask */ |
0, /* src_mask */ |
111 |
0, /* dst_mask */ |
0, /* dst_mask */ |
112 |
false), /* pcrel_offset */ |
FALSE), /* pcrel_offset */ |
113 |
|
|
114 |
/* A 16 bit reference to a symbol, normally from a data section. */ |
/* A 16 bit reference to a symbol, normally from a data section. */ |
115 |
HOWTO (MIPS_R_REFHALF, /* type */ |
HOWTO (MIPS_R_REFHALF, /* type */ |
116 |
0, /* rightshift */ |
0, /* rightshift */ |
117 |
1, /* size (0 = byte, 1 = short, 2 = long) */ |
1, /* size (0 = byte, 1 = short, 2 = long) */ |
118 |
16, /* bitsize */ |
16, /* bitsize */ |
119 |
false, /* pc_relative */ |
FALSE, /* pc_relative */ |
120 |
0, /* bitpos */ |
0, /* bitpos */ |
121 |
complain_overflow_bitfield, /* complain_on_overflow */ |
complain_overflow_bitfield, /* complain_on_overflow */ |
122 |
mips_generic_reloc, /* special_function */ |
mips_generic_reloc, /* special_function */ |
123 |
"REFHALF", /* name */ |
"REFHALF", /* name */ |
124 |
true, /* partial_inplace */ |
TRUE, /* partial_inplace */ |
125 |
0xffff, /* src_mask */ |
0xffff, /* src_mask */ |
126 |
0xffff, /* dst_mask */ |
0xffff, /* dst_mask */ |
127 |
false), /* pcrel_offset */ |
FALSE), /* pcrel_offset */ |
128 |
|
|
129 |
/* A 32 bit reference to a symbol, normally from a data section. */ |
/* A 32 bit reference to a symbol, normally from a data section. */ |
130 |
HOWTO (MIPS_R_REFWORD, /* type */ |
HOWTO (MIPS_R_REFWORD, /* type */ |
131 |
0, /* rightshift */ |
0, /* rightshift */ |
132 |
2, /* size (0 = byte, 1 = short, 2 = long) */ |
2, /* size (0 = byte, 1 = short, 2 = long) */ |
133 |
32, /* bitsize */ |
32, /* bitsize */ |
134 |
false, /* pc_relative */ |
FALSE, /* pc_relative */ |
135 |
0, /* bitpos */ |
0, /* bitpos */ |
136 |
complain_overflow_bitfield, /* complain_on_overflow */ |
complain_overflow_bitfield, /* complain_on_overflow */ |
137 |
mips_generic_reloc, /* special_function */ |
mips_generic_reloc, /* special_function */ |
138 |
"REFWORD", /* name */ |
"REFWORD", /* name */ |
139 |
true, /* partial_inplace */ |
TRUE, /* partial_inplace */ |
140 |
0xffffffff, /* src_mask */ |
0xffffffff, /* src_mask */ |
141 |
0xffffffff, /* dst_mask */ |
0xffffffff, /* dst_mask */ |
142 |
false), /* pcrel_offset */ |
FALSE), /* pcrel_offset */ |
143 |
|
|
144 |
/* A 26 bit absolute jump address. */ |
/* A 26 bit absolute jump address. */ |
145 |
HOWTO (MIPS_R_JMPADDR, /* type */ |
HOWTO (MIPS_R_JMPADDR, /* type */ |
146 |
2, /* rightshift */ |
2, /* rightshift */ |
147 |
2, /* size (0 = byte, 1 = short, 2 = long) */ |
2, /* size (0 = byte, 1 = short, 2 = long) */ |
148 |
26, /* bitsize */ |
26, /* bitsize */ |
149 |
false, /* pc_relative */ |
FALSE, /* pc_relative */ |
150 |
0, /* bitpos */ |
0, /* bitpos */ |
151 |
complain_overflow_dont, /* complain_on_overflow */ |
complain_overflow_dont, /* complain_on_overflow */ |
152 |
/* This needs complex overflow |
/* This needs complex overflow |
154 |
bits must match the PC. */ |
bits must match the PC. */ |
155 |
mips_generic_reloc, /* special_function */ |
mips_generic_reloc, /* special_function */ |
156 |
"JMPADDR", /* name */ |
"JMPADDR", /* name */ |
157 |
true, /* partial_inplace */ |
TRUE, /* partial_inplace */ |
158 |
0x3ffffff, /* src_mask */ |
0x3ffffff, /* src_mask */ |
159 |
0x3ffffff, /* dst_mask */ |
0x3ffffff, /* dst_mask */ |
160 |
false), /* pcrel_offset */ |
FALSE), /* pcrel_offset */ |
161 |
|
|
162 |
/* The high 16 bits of a symbol value. Handled by the function |
/* The high 16 bits of a symbol value. Handled by the function |
163 |
mips_refhi_reloc. */ |
mips_refhi_reloc. */ |
165 |
16, /* rightshift */ |
16, /* rightshift */ |
166 |
2, /* size (0 = byte, 1 = short, 2 = long) */ |
2, /* size (0 = byte, 1 = short, 2 = long) */ |
167 |
16, /* bitsize */ |
16, /* bitsize */ |
168 |
false, /* pc_relative */ |
FALSE, /* pc_relative */ |
169 |
0, /* bitpos */ |
0, /* bitpos */ |
170 |
complain_overflow_bitfield, /* complain_on_overflow */ |
complain_overflow_bitfield, /* complain_on_overflow */ |
171 |
mips_refhi_reloc, /* special_function */ |
mips_refhi_reloc, /* special_function */ |
172 |
"REFHI", /* name */ |
"REFHI", /* name */ |
173 |
true, /* partial_inplace */ |
TRUE, /* partial_inplace */ |
174 |
0xffff, /* src_mask */ |
0xffff, /* src_mask */ |
175 |
0xffff, /* dst_mask */ |
0xffff, /* dst_mask */ |
176 |
false), /* pcrel_offset */ |
FALSE), /* pcrel_offset */ |
177 |
|
|
178 |
/* The low 16 bits of a symbol value. */ |
/* The low 16 bits of a symbol value. */ |
179 |
HOWTO (MIPS_R_REFLO, /* type */ |
HOWTO (MIPS_R_REFLO, /* type */ |
180 |
0, /* rightshift */ |
0, /* rightshift */ |
181 |
2, /* size (0 = byte, 1 = short, 2 = long) */ |
2, /* size (0 = byte, 1 = short, 2 = long) */ |
182 |
16, /* bitsize */ |
16, /* bitsize */ |
183 |
false, /* pc_relative */ |
FALSE, /* pc_relative */ |
184 |
0, /* bitpos */ |
0, /* bitpos */ |
185 |
complain_overflow_dont, /* complain_on_overflow */ |
complain_overflow_dont, /* complain_on_overflow */ |
186 |
mips_reflo_reloc, /* special_function */ |
mips_reflo_reloc, /* special_function */ |
187 |
"REFLO", /* name */ |
"REFLO", /* name */ |
188 |
true, /* partial_inplace */ |
TRUE, /* partial_inplace */ |
189 |
0xffff, /* src_mask */ |
0xffff, /* src_mask */ |
190 |
0xffff, /* dst_mask */ |
0xffff, /* dst_mask */ |
191 |
false), /* pcrel_offset */ |
FALSE), /* pcrel_offset */ |
192 |
|
|
193 |
/* A reference to an offset from the gp register. Handled by the |
/* A reference to an offset from the gp register. Handled by the |
194 |
function mips_gprel_reloc. */ |
function mips_gprel_reloc. */ |
196 |
0, /* rightshift */ |
0, /* rightshift */ |
197 |
2, /* size (0 = byte, 1 = short, 2 = long) */ |
2, /* size (0 = byte, 1 = short, 2 = long) */ |
198 |
16, /* bitsize */ |
16, /* bitsize */ |
199 |
false, /* pc_relative */ |
FALSE, /* pc_relative */ |
200 |
0, /* bitpos */ |
0, /* bitpos */ |
201 |
complain_overflow_signed, /* complain_on_overflow */ |
complain_overflow_signed, /* complain_on_overflow */ |
202 |
mips_gprel_reloc, /* special_function */ |
mips_gprel_reloc, /* special_function */ |
203 |
"GPREL", /* name */ |
"GPREL", /* name */ |
204 |
true, /* partial_inplace */ |
TRUE, /* partial_inplace */ |
205 |
0xffff, /* src_mask */ |
0xffff, /* src_mask */ |
206 |
0xffff, /* dst_mask */ |
0xffff, /* dst_mask */ |
207 |
false), /* pcrel_offset */ |
FALSE), /* pcrel_offset */ |
208 |
|
|
209 |
/* A reference to a literal using an offset from the gp register. |
/* A reference to a literal using an offset from the gp register. |
210 |
Handled by the function mips_gprel_reloc. */ |
Handled by the function mips_gprel_reloc. */ |
212 |
0, /* rightshift */ |
0, /* rightshift */ |
213 |
2, /* size (0 = byte, 1 = short, 2 = long) */ |
2, /* size (0 = byte, 1 = short, 2 = long) */ |
214 |
16, /* bitsize */ |
16, /* bitsize */ |
215 |
false, /* pc_relative */ |
FALSE, /* pc_relative */ |
216 |
0, /* bitpos */ |
0, /* bitpos */ |
217 |
complain_overflow_signed, /* complain_on_overflow */ |
complain_overflow_signed, /* complain_on_overflow */ |
218 |
mips_gprel_reloc, /* special_function */ |
mips_gprel_reloc, /* special_function */ |
219 |
"LITERAL", /* name */ |
"LITERAL", /* name */ |
220 |
true, /* partial_inplace */ |
TRUE, /* partial_inplace */ |
221 |
0xffff, /* src_mask */ |
0xffff, /* src_mask */ |
222 |
0xffff, /* dst_mask */ |
0xffff, /* dst_mask */ |
223 |
false), /* pcrel_offset */ |
FALSE), /* pcrel_offset */ |
224 |
|
|
225 |
EMPTY_HOWTO (8), |
EMPTY_HOWTO (8), |
226 |
EMPTY_HOWTO (9), |
EMPTY_HOWTO (9), |
227 |
EMPTY_HOWTO (10), |
EMPTY_HOWTO (10), |
228 |
EMPTY_HOWTO (11), |
EMPTY_HOWTO (11), |
229 |
|
|
230 |
/* This reloc is a Cygnus extension used when generating position |
/* FIXME: This relocation is used (internally only) to represent branches |
231 |
independent code for embedded systems. It represents a 16 bit PC |
when assembling. It should never appear in output files, and |
232 |
relative reloc rightshifted twice as used in the MIPS branch |
be removed. (It used to be used for embedded-PIC support.) */ |
|
instructions. */ |
|
233 |
HOWTO (MIPS_R_PCREL16, /* type */ |
HOWTO (MIPS_R_PCREL16, /* type */ |
234 |
2, /* rightshift */ |
2, /* rightshift */ |
235 |
2, /* size (0 = byte, 1 = short, 2 = long) */ |
2, /* size (0 = byte, 1 = short, 2 = long) */ |
236 |
16, /* bitsize */ |
16, /* bitsize */ |
237 |
true, /* pc_relative */ |
TRUE, /* pc_relative */ |
238 |
0, /* bitpos */ |
0, /* bitpos */ |
239 |
complain_overflow_signed, /* complain_on_overflow */ |
complain_overflow_signed, /* complain_on_overflow */ |
240 |
mips_generic_reloc, /* special_function */ |
mips_generic_reloc, /* special_function */ |
241 |
"PCREL16", /* name */ |
"PCREL16", /* name */ |
242 |
true, /* partial_inplace */ |
TRUE, /* partial_inplace */ |
243 |
0xffff, /* src_mask */ |
0xffff, /* src_mask */ |
244 |
0xffff, /* dst_mask */ |
0xffff, /* dst_mask */ |
245 |
true), /* pcrel_offset */ |
TRUE), /* pcrel_offset */ |
|
|
|
|
/* This reloc is a Cygnus extension used when generating position |
|
|
independent code for embedded systems. It represents the high 16 |
|
|
bits of a PC relative reloc. The next reloc must be |
|
|
MIPS_R_RELLO, and the addend is formed from the addends of the |
|
|
two instructions, just as in MIPS_R_REFHI and MIPS_R_REFLO. The |
|
|
final value is actually PC relative to the location of the |
|
|
MIPS_R_RELLO reloc, not the MIPS_R_RELHI reloc. */ |
|
|
HOWTO (MIPS_R_RELHI, /* type */ |
|
|
16, /* rightshift */ |
|
|
2, /* size (0 = byte, 1 = short, 2 = long) */ |
|
|
16, /* bitsize */ |
|
|
true, /* pc_relative */ |
|
|
0, /* bitpos */ |
|
|
complain_overflow_bitfield, /* complain_on_overflow */ |
|
|
mips_relhi_reloc, /* special_function */ |
|
|
"RELHI", /* name */ |
|
|
true, /* partial_inplace */ |
|
|
0xffff, /* src_mask */ |
|
|
0xffff, /* dst_mask */ |
|
|
true), /* pcrel_offset */ |
|
|
|
|
|
/* This reloc is a Cygnus extension used when generating position |
|
|
independent code for embedded systems. It represents the low 16 |
|
|
bits of a PC relative reloc. */ |
|
|
HOWTO (MIPS_R_RELLO, /* type */ |
|
|
0, /* rightshift */ |
|
|
2, /* size (0 = byte, 1 = short, 2 = long) */ |
|
|
16, /* bitsize */ |
|
|
true, /* pc_relative */ |
|
|
0, /* bitpos */ |
|
|
complain_overflow_dont, /* complain_on_overflow */ |
|
|
mips_rello_reloc, /* special_function */ |
|
|
"RELLO", /* name */ |
|
|
true, /* partial_inplace */ |
|
|
0xffff, /* src_mask */ |
|
|
0xffff, /* dst_mask */ |
|
|
true), /* pcrel_offset */ |
|
|
|
|
|
EMPTY_HOWTO (15), |
|
|
EMPTY_HOWTO (16), |
|
|
EMPTY_HOWTO (17), |
|
|
EMPTY_HOWTO (18), |
|
|
EMPTY_HOWTO (19), |
|
|
EMPTY_HOWTO (20), |
|
|
EMPTY_HOWTO (21), |
|
|
|
|
|
/* This reloc is a Cygnus extension used when generating position |
|
|
independent code for embedded systems. It represents an entry in |
|
|
a switch table, which is the difference between two symbols in |
|
|
the .text section. The symndx is actually the offset from the |
|
|
reloc address to the subtrahend. See include/coff/mips.h for |
|
|
more details. */ |
|
|
HOWTO (MIPS_R_SWITCH, /* type */ |
|
|
0, /* rightshift */ |
|
|
2, /* size (0 = byte, 1 = short, 2 = long) */ |
|
|
32, /* bitsize */ |
|
|
true, /* pc_relative */ |
|
|
0, /* bitpos */ |
|
|
complain_overflow_dont, /* complain_on_overflow */ |
|
|
mips_switch_reloc, /* special_function */ |
|
|
"SWITCH", /* name */ |
|
|
true, /* partial_inplace */ |
|
|
0xffffffff, /* src_mask */ |
|
|
0xffffffff, /* dst_mask */ |
|
|
true) /* pcrel_offset */ |
|
246 |
}; |
}; |
247 |
|
|
248 |
#define MIPS_HOWTO_COUNT \ |
#define MIPS_HOWTO_COUNT \ |
249 |
(sizeof mips_howto_table / sizeof mips_howto_table[0]) |
(sizeof mips_howto_table / sizeof mips_howto_table[0]) |
|
|
|
|
/* When the linker is doing relaxing, it may change an external PCREL16 |
|
|
reloc. This typically represents an instruction like |
|
|
bal foo |
|
|
We change it to |
|
|
.set noreorder |
|
|
bal $L1 |
|
|
lui $at,%hi(foo - $L1) |
|
|
$L1: |
|
|
addiu $at,%lo(foo - $L1) |
|
|
addu $at,$at,$31 |
|
|
jalr $at |
|
|
PCREL16_EXPANSION_ADJUSTMENT is the number of bytes this changes the |
|
|
instruction by. */ |
|
|
|
|
|
#define PCREL16_EXPANSION_ADJUSTMENT (4 * 4) |
|
250 |
|
|
251 |
/* See whether the magic number matches. */ |
/* See whether the magic number matches. */ |
252 |
|
|
253 |
static boolean |
static bfd_boolean |
254 |
mips_ecoff_bad_format_hook (abfd, filehdr) |
mips_ecoff_bad_format_hook (abfd, filehdr) |
255 |
bfd *abfd; |
bfd *abfd; |
256 |
PTR filehdr; |
PTR filehdr; |
261 |
{ |
{ |
262 |
case MIPS_MAGIC_1: |
case MIPS_MAGIC_1: |
263 |
/* I don't know what endianness this implies. */ |
/* I don't know what endianness this implies. */ |
264 |
return true; |
return TRUE; |
265 |
|
|
266 |
case MIPS_MAGIC_BIG: |
case MIPS_MAGIC_BIG: |
267 |
case MIPS_MAGIC_BIG2: |
case MIPS_MAGIC_BIG2: |
274 |
return bfd_little_endian (abfd); |
return bfd_little_endian (abfd); |
275 |
|
|
276 |
default: |
default: |
277 |
return false; |
return FALSE; |
278 |
} |
} |
279 |
} |
} |
280 |
|
|
319 |
<< RELOC_BITS3_TYPEHI_SH_LITTLE)); |
<< RELOC_BITS3_TYPEHI_SH_LITTLE)); |
320 |
intern->r_extern = (ext->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) != 0; |
intern->r_extern = (ext->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) != 0; |
321 |
} |
} |
|
|
|
|
/* If this is a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELHI or |
|
|
MIPS_R_RELLO reloc, r_symndx is actually the offset from the |
|
|
reloc address to the base of the difference (see |
|
|
include/coff/mips.h for more details). We copy symndx into the |
|
|
r_offset field so as not to confuse ecoff_slurp_reloc_table in |
|
|
ecoff.c. In adjust_reloc_in we then copy r_offset into the reloc |
|
|
addend. */ |
|
|
if (intern->r_type == MIPS_R_SWITCH |
|
|
|| (! intern->r_extern |
|
|
&& (intern->r_type == MIPS_R_RELLO |
|
|
|| intern->r_type == MIPS_R_RELHI))) |
|
|
{ |
|
|
BFD_ASSERT (! intern->r_extern); |
|
|
intern->r_offset = intern->r_symndx; |
|
|
if (intern->r_offset & 0x800000) |
|
|
intern->r_offset -= 0x1000000; |
|
|
intern->r_symndx = RELOC_SECTION_TEXT; |
|
|
} |
|
322 |
} |
} |
323 |
|
|
324 |
/* Swap a reloc out. */ |
/* Swap a reloc out. */ |
335 |
BFD_ASSERT (intern->r_extern |
BFD_ASSERT (intern->r_extern |
336 |
|| (intern->r_symndx >= 0 && intern->r_symndx <= 12)); |
|| (intern->r_symndx >= 0 && intern->r_symndx <= 12)); |
337 |
|
|
338 |
/* If this is a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELLO or |
r_symndx = intern->r_symndx; |
|
MIPS_R_RELHI reloc, we actually want to write the contents of |
|
|
r_offset out as the symbol index. This undoes the change made by |
|
|
mips_ecoff_swap_reloc_in. */ |
|
|
if (intern->r_type != MIPS_R_SWITCH |
|
|
&& (intern->r_extern |
|
|
|| (intern->r_type != MIPS_R_RELHI |
|
|
&& intern->r_type != MIPS_R_RELLO))) |
|
|
r_symndx = intern->r_symndx; |
|
|
else |
|
|
{ |
|
|
BFD_ASSERT (intern->r_symndx == RELOC_SECTION_TEXT); |
|
|
r_symndx = intern->r_offset & 0xffffff; |
|
|
} |
|
339 |
|
|
340 |
H_PUT_32 (abfd, intern->r_vaddr, ext->r_vaddr); |
H_PUT_32 (abfd, intern->r_vaddr, ext->r_vaddr); |
341 |
if (bfd_header_big_endian (abfd)) |
if (bfd_header_big_endian (abfd)) |
370 |
const struct internal_reloc *intern; |
const struct internal_reloc *intern; |
371 |
arelent *rptr; |
arelent *rptr; |
372 |
{ |
{ |
373 |
if (intern->r_type > MIPS_R_SWITCH) |
if (intern->r_type > MIPS_R_PCREL16) |
374 |
abort (); |
abort (); |
375 |
|
|
376 |
if (! intern->r_extern |
if (! intern->r_extern |
383 |
if (intern->r_type == MIPS_R_IGNORE) |
if (intern->r_type == MIPS_R_IGNORE) |
384 |
rptr->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr; |
rptr->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr; |
385 |
|
|
|
/* If this is a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELHI or |
|
|
MIPS_R_RELLO reloc, we want the addend field of the BFD relocto |
|
|
hold the value which was originally in the symndx field of the |
|
|
internal MIPS ECOFF reloc. This value was copied into |
|
|
intern->r_offset by mips_swap_reloc_in, and here we copy it into |
|
|
the addend field. */ |
|
|
if (intern->r_type == MIPS_R_SWITCH |
|
|
|| (! intern->r_extern |
|
|
&& (intern->r_type == MIPS_R_RELHI |
|
|
|| intern->r_type == MIPS_R_RELLO))) |
|
|
rptr->addend = intern->r_offset; |
|
|
|
|
386 |
rptr->howto = &mips_howto_table[intern->r_type]; |
rptr->howto = &mips_howto_table[intern->r_type]; |
387 |
} |
} |
388 |
|
|
392 |
static void |
static void |
393 |
mips_adjust_reloc_out (abfd, rel, intern) |
mips_adjust_reloc_out (abfd, rel, intern) |
394 |
bfd *abfd ATTRIBUTE_UNUSED; |
bfd *abfd ATTRIBUTE_UNUSED; |
395 |
const arelent *rel; |
const arelent *rel ATTRIBUTE_UNUSED; |
396 |
struct internal_reloc *intern; |
struct internal_reloc *intern ATTRIBUTE_UNUSED; |
397 |
{ |
{ |
|
/* For a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELHI or |
|
|
MIPS_R_RELLO reloc, we must copy rel->addend into |
|
|
intern->r_offset. This will then be written out as the symbol |
|
|
index by mips_ecoff_swap_reloc_out. This operation parallels the |
|
|
action of mips_adjust_reloc_in. */ |
|
|
if (intern->r_type == MIPS_R_SWITCH |
|
|
|| (! intern->r_extern |
|
|
&& (intern->r_type == MIPS_R_RELHI |
|
|
|| intern->r_type == MIPS_R_RELLO))) |
|
|
intern->r_offset = rel->addend; |
|
398 |
} |
} |
399 |
|
|
400 |
/* ECOFF relocs are either against external symbols, or against |
/* ECOFF relocs are either against external symbols, or against |
401 |
sections. If we are producing relocateable output, and the reloc |
sections. If we are producing relocatable output, and the reloc |
402 |
is against an external symbol, and nothing has given us any |
is against an external symbol, and nothing has given us any |
403 |
additional addend, the resulting reloc will also be against the |
additional addend, the resulting reloc will also be against the |
404 |
same symbol. In such a case, we don't want to change anything |
same symbol. In such a case, we don't want to change anything |
406 |
final link time. Rather than put special case code into |
final link time. Rather than put special case code into |
407 |
bfd_perform_relocation, all the reloc types use this howto |
bfd_perform_relocation, all the reloc types use this howto |
408 |
function. It just short circuits the reloc if producing |
function. It just short circuits the reloc if producing |
409 |
relocateable output against an external symbol. */ |
relocatable output against an external symbol. */ |
410 |
|
|
411 |
static bfd_reloc_status_type |
static bfd_reloc_status_type |
412 |
mips_generic_reloc (abfd, |
mips_generic_reloc (abfd, |
499 |
relocation += symbol->section->output_offset; |
relocation += symbol->section->output_offset; |
500 |
relocation += reloc_entry->addend; |
relocation += reloc_entry->addend; |
501 |
|
|
502 |
if (reloc_entry->address > input_section->_cooked_size) |
if (reloc_entry->address > bfd_get_section_limit (abfd, input_section)) |
503 |
return bfd_reloc_outofrange; |
return bfd_reloc_outofrange; |
504 |
|
|
505 |
/* Save the information, and let REFLO do the actual relocation. */ |
/* Save the information, and let REFLO do the actual relocation. */ |
605 |
bfd *output_bfd; |
bfd *output_bfd; |
606 |
char **error_message; |
char **error_message; |
607 |
{ |
{ |
608 |
boolean relocateable; |
bfd_boolean relocatable; |
609 |
bfd_vma gp; |
bfd_vma gp; |
610 |
bfd_vma relocation; |
bfd_vma relocation; |
611 |
unsigned long val; |
unsigned long val; |
624 |
} |
} |
625 |
|
|
626 |
if (output_bfd != (bfd *) NULL) |
if (output_bfd != (bfd *) NULL) |
627 |
relocateable = true; |
relocatable = TRUE; |
628 |
else |
else |
629 |
{ |
{ |
630 |
relocateable = false; |
relocatable = FALSE; |
631 |
output_bfd = symbol->section->output_section->owner; |
output_bfd = symbol->section->output_section->owner; |
632 |
} |
} |
633 |
|
|
634 |
if (bfd_is_und_section (symbol->section) && ! relocateable) |
if (bfd_is_und_section (symbol->section) && ! relocatable) |
635 |
return bfd_reloc_undefined; |
return bfd_reloc_undefined; |
636 |
|
|
637 |
/* We have to figure out the gp value, so that we can adjust the |
/* We have to figure out the gp value, so that we can adjust the |
638 |
symbol value correctly. We look up the symbol _gp in the output |
symbol value correctly. We look up the symbol _gp in the output |
639 |
BFD. If we can't find it, we're stuck. We cache it in the ECOFF |
BFD. If we can't find it, we're stuck. We cache it in the ECOFF |
640 |
target data. We don't need to adjust the symbol value for an |
target data. We don't need to adjust the symbol value for an |
641 |
external symbol if we are producing relocateable output. */ |
external symbol if we are producing relocatable output. */ |
642 |
gp = _bfd_get_gp_value (output_bfd); |
gp = _bfd_get_gp_value (output_bfd); |
643 |
if (gp == 0 |
if (gp == 0 |
644 |
&& (! relocateable |
&& (! relocatable |
645 |
|| (symbol->flags & BSF_SECTION_SYM) != 0)) |
|| (symbol->flags & BSF_SECTION_SYM) != 0)) |
646 |
{ |
{ |
647 |
if (relocateable) |
if (relocatable) |
648 |
{ |
{ |
649 |
/* Make up a value. */ |
/* Make up a value. */ |
650 |
gp = symbol->section->output_section->vma + 0x4000; |
gp = symbol->section->output_section->vma + 0x4000; |
697 |
relocation += symbol->section->output_section->vma; |
relocation += symbol->section->output_section->vma; |
698 |
relocation += symbol->section->output_offset; |
relocation += symbol->section->output_offset; |
699 |
|
|
700 |
if (reloc_entry->address > input_section->_cooked_size) |
if (reloc_entry->address > bfd_get_section_limit (abfd, input_section)) |
701 |
return bfd_reloc_outofrange; |
return bfd_reloc_outofrange; |
702 |
|
|
703 |
insn = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address); |
insn = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address); |
708 |
val -= 0x10000; |
val -= 0x10000; |
709 |
|
|
710 |
/* Adjust val for the final section location and GP value. If we |
/* Adjust val for the final section location and GP value. If we |
711 |
are producing relocateable output, we don't want to do this for |
are producing relocatable output, we don't want to do this for |
712 |
an external symbol. */ |
an external symbol. */ |
713 |
if (! relocateable |
if (! relocatable |
714 |
|| (symbol->flags & BSF_SECTION_SYM) != 0) |
|| (symbol->flags & BSF_SECTION_SYM) != 0) |
715 |
val += relocation - gp; |
val += relocation - gp; |
716 |
|
|
717 |
insn = (insn &~ (unsigned) 0xffff) | (val & 0xffff); |
insn = (insn &~ (unsigned) 0xffff) | (val & 0xffff); |
718 |
bfd_put_32 (abfd, (bfd_vma) insn, (bfd_byte *) data + reloc_entry->address); |
bfd_put_32 (abfd, (bfd_vma) insn, (bfd_byte *) data + reloc_entry->address); |
719 |
|
|
720 |
if (relocateable) |
if (relocatable) |
721 |
reloc_entry->address += input_section->output_offset; |
reloc_entry->address += input_section->output_offset; |
722 |
|
|
723 |
/* Make sure it fit in 16 bits. */ |
/* Make sure it fit in 16 bits. */ |
727 |
return bfd_reloc_ok; |
return bfd_reloc_ok; |
728 |
} |
} |
729 |
|
|
|
/* Do a RELHI relocation. We do this in conjunction with a RELLO |
|
|
reloc, just as REFHI and REFLO are done together. RELHI and RELLO |
|
|
are Cygnus extensions used when generating position independent |
|
|
code for embedded systems. */ |
|
|
|
|
|
/* FIXME: This should not be a static variable. */ |
|
|
|
|
|
static struct mips_hi *mips_relhi_list; |
|
|
|
|
|
static bfd_reloc_status_type |
|
|
mips_relhi_reloc (abfd, |
|
|
reloc_entry, |
|
|
symbol, |
|
|
data, |
|
|
input_section, |
|
|
output_bfd, |
|
|
error_message) |
|
|
bfd *abfd ATTRIBUTE_UNUSED; |
|
|
arelent *reloc_entry; |
|
|
asymbol *symbol; |
|
|
PTR data; |
|
|
asection *input_section; |
|
|
bfd *output_bfd; |
|
|
char **error_message ATTRIBUTE_UNUSED; |
|
|
{ |
|
|
bfd_reloc_status_type ret; |
|
|
bfd_vma relocation; |
|
|
struct mips_hi *n; |
|
|
|
|
|
/* If this is a reloc against a section symbol, then it is correct |
|
|
in the object file. The only time we want to change this case is |
|
|
when we are relaxing, and that is handled entirely by |
|
|
mips_relocate_section and never calls this function. */ |
|
|
if ((symbol->flags & BSF_SECTION_SYM) != 0) |
|
|
{ |
|
|
if (output_bfd != (bfd *) NULL) |
|
|
reloc_entry->address += input_section->output_offset; |
|
|
return bfd_reloc_ok; |
|
|
} |
|
|
|
|
|
/* This is an external symbol. If we're relocating, we don't want |
|
|
to change anything. */ |
|
|
if (output_bfd != (bfd *) NULL) |
|
|
{ |
|
|
reloc_entry->address += input_section->output_offset; |
|
|
return bfd_reloc_ok; |
|
|
} |
|
|
|
|
|
ret = bfd_reloc_ok; |
|
|
if (bfd_is_und_section (symbol->section) |
|
|
&& output_bfd == (bfd *) NULL) |
|
|
ret = bfd_reloc_undefined; |
|
|
|
|
|
if (bfd_is_com_section (symbol->section)) |
|
|
relocation = 0; |
|
|
else |
|
|
relocation = symbol->value; |
|
|
|
|
|
relocation += symbol->section->output_section->vma; |
|
|
relocation += symbol->section->output_offset; |
|
|
relocation += reloc_entry->addend; |
|
|
|
|
|
if (reloc_entry->address > input_section->_cooked_size) |
|
|
return bfd_reloc_outofrange; |
|
|
|
|
|
/* Save the information, and let RELLO do the actual relocation. */ |
|
|
n = (struct mips_hi *) bfd_malloc ((bfd_size_type) sizeof *n); |
|
|
if (n == NULL) |
|
|
return bfd_reloc_outofrange; |
|
|
n->addr = (bfd_byte *) data + reloc_entry->address; |
|
|
n->addend = relocation; |
|
|
n->next = mips_relhi_list; |
|
|
mips_relhi_list = n; |
|
|
|
|
|
if (output_bfd != (bfd *) NULL) |
|
|
reloc_entry->address += input_section->output_offset; |
|
|
|
|
|
return ret; |
|
|
} |
|
|
|
|
|
/* Do a RELLO relocation. This is a straightforward 16 bit PC |
|
|
relative relocation; this function exists in order to do the RELHI |
|
|
relocation described above. */ |
|
|
|
|
|
static bfd_reloc_status_type |
|
|
mips_rello_reloc (abfd, |
|
|
reloc_entry, |
|
|
symbol, |
|
|
data, |
|
|
input_section, |
|
|
output_bfd, |
|
|
error_message) |
|
|
bfd *abfd; |
|
|
arelent *reloc_entry; |
|
|
asymbol *symbol; |
|
|
PTR data; |
|
|
asection *input_section; |
|
|
bfd *output_bfd; |
|
|
char **error_message; |
|
|
{ |
|
|
if (mips_relhi_list != NULL) |
|
|
{ |
|
|
struct mips_hi *l; |
|
|
|
|
|
l = mips_relhi_list; |
|
|
while (l != NULL) |
|
|
{ |
|
|
unsigned long insn; |
|
|
unsigned long val; |
|
|
unsigned long vallo; |
|
|
struct mips_hi *next; |
|
|
|
|
|
/* Do the RELHI relocation. Note that we actually don't |
|
|
need to know anything about the RELLO itself, except |
|
|
where to find the low 16 bits of the addend needed by the |
|
|
RELHI. */ |
|
|
insn = bfd_get_32 (abfd, l->addr); |
|
|
vallo = (bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address) |
|
|
& 0xffff); |
|
|
val = ((insn & 0xffff) << 16) + vallo; |
|
|
val += l->addend; |
|
|
|
|
|
/* If the symbol is defined, make val PC relative. If the |
|
|
symbol is not defined we don't want to do this, because |
|
|
we don't want the value in the object file to incorporate |
|
|
the address of the reloc. */ |
|
|
if (! bfd_is_und_section (bfd_get_section (symbol)) |
|
|
&& ! bfd_is_com_section (bfd_get_section (symbol))) |
|
|
val -= (input_section->output_section->vma |
|
|
+ input_section->output_offset |
|
|
+ reloc_entry->address); |
|
|
|
|
|
/* The low order 16 bits are always treated as a signed |
|
|
value. Therefore, a negative value in the low order bits |
|
|
requires an adjustment in the high order bits. We need |
|
|
to make this adjustment in two ways: once for the bits we |
|
|
took from the data, and once for the bits we are putting |
|
|
back in to the data. */ |
|
|
if ((vallo & 0x8000) != 0) |
|
|
val -= 0x10000; |
|
|
if ((val & 0x8000) != 0) |
|
|
val += 0x10000; |
|
|
|
|
|
insn = (insn &~ (unsigned) 0xffff) | ((val >> 16) & 0xffff); |
|
|
bfd_put_32 (abfd, (bfd_vma) insn, l->addr); |
|
|
|
|
|
next = l->next; |
|
|
free (l); |
|
|
l = next; |
|
|
} |
|
|
|
|
|
mips_relhi_list = NULL; |
|
|
} |
|
|
|
|
|
/* If this is a reloc against a section symbol, then it is correct |
|
|
in the object file. The only time we want to change this case is |
|
|
when we are relaxing, and that is handled entirely by |
|
|
mips_relocate_section and never calls this function. */ |
|
|
if ((symbol->flags & BSF_SECTION_SYM) != 0) |
|
|
{ |
|
|
if (output_bfd != (bfd *) NULL) |
|
|
reloc_entry->address += input_section->output_offset; |
|
|
return bfd_reloc_ok; |
|
|
} |
|
|
|
|
|
/* bfd_perform_relocation does not handle pcrel_offset relocations |
|
|
correctly when generating a relocateable file, so handle them |
|
|
directly here. */ |
|
|
if (output_bfd != (bfd *) NULL) |
|
|
{ |
|
|
reloc_entry->address += input_section->output_offset; |
|
|
return bfd_reloc_ok; |
|
|
} |
|
|
|
|
|
/* Now do the RELLO reloc in the usual way. */ |
|
|
return mips_generic_reloc (abfd, reloc_entry, symbol, data, |
|
|
input_section, output_bfd, error_message); |
|
|
} |
|
|
|
|
|
/* This is the special function for the MIPS_R_SWITCH reloc. This |
|
|
special reloc is normally correct in the object file, and only |
|
|
requires special handling when relaxing. We don't want |
|
|
bfd_perform_relocation to tamper with it at all. */ |
|
|
|
|
|
static bfd_reloc_status_type |
|
|
mips_switch_reloc (abfd, |
|
|
reloc_entry, |
|
|
symbol, |
|
|
data, |
|
|
input_section, |
|
|
output_bfd, |
|
|
error_message) |
|
|
bfd *abfd ATTRIBUTE_UNUSED; |
|
|
arelent *reloc_entry ATTRIBUTE_UNUSED; |
|
|
asymbol *symbol ATTRIBUTE_UNUSED; |
|
|
PTR data ATTRIBUTE_UNUSED; |
|
|
asection *input_section ATTRIBUTE_UNUSED; |
|
|
bfd *output_bfd ATTRIBUTE_UNUSED; |
|
|
char **error_message ATTRIBUTE_UNUSED; |
|
|
{ |
|
|
return bfd_reloc_ok; |
|
|
} |
|
|
|
|
730 |
/* Get the howto structure for a generic reloc type. */ |
/* Get the howto structure for a generic reloc type. */ |
731 |
|
|
732 |
static reloc_howto_type * |
static reloc_howto_type * |
763 |
case BFD_RELOC_16_PCREL_S2: |
case BFD_RELOC_16_PCREL_S2: |
764 |
mips_type = MIPS_R_PCREL16; |
mips_type = MIPS_R_PCREL16; |
765 |
break; |
break; |
|
case BFD_RELOC_PCREL_HI16_S: |
|
|
mips_type = MIPS_R_RELHI; |
|
|
break; |
|
|
case BFD_RELOC_PCREL_LO16: |
|
|
mips_type = MIPS_R_RELLO; |
|
|
break; |
|
|
case BFD_RELOC_GPREL32: |
|
|
mips_type = MIPS_R_SWITCH; |
|
|
break; |
|
766 |
default: |
default: |
767 |
return (reloc_howto_type *) NULL; |
return (reloc_howto_type *) NULL; |
768 |
} |
} |
771 |
} |
} |
772 |
|
|
773 |
/* A helper routine for mips_relocate_section which handles the REFHI |
/* A helper routine for mips_relocate_section which handles the REFHI |
774 |
and RELHI relocations. The REFHI relocation must be followed by a |
relocations. The REFHI relocation must be followed by a REFLO |
775 |
REFLO relocation (and RELHI by a RELLO), and the addend used is |
relocation, and the addend used is formed from the addends of both |
776 |
formed from the addends of both instructions. */ |
instructions. */ |
777 |
|
|
778 |
static void |
static void |
779 |
mips_relocate_hi (refhi, reflo, input_bfd, input_section, contents, adjust, |
mips_relocate_hi (refhi, reflo, input_bfd, input_section, contents, |
780 |
relocation, pcrel) |
relocation) |
781 |
struct internal_reloc *refhi; |
struct internal_reloc *refhi; |
782 |
struct internal_reloc *reflo; |
struct internal_reloc *reflo; |
783 |
bfd *input_bfd; |
bfd *input_bfd; |
784 |
asection *input_section; |
asection *input_section; |
785 |
bfd_byte *contents; |
bfd_byte *contents; |
|
size_t adjust; |
|
786 |
bfd_vma relocation; |
bfd_vma relocation; |
|
boolean pcrel; |
|
787 |
{ |
{ |
788 |
unsigned long insn; |
unsigned long insn; |
789 |
unsigned long val; |
unsigned long val; |
793 |
return; |
return; |
794 |
|
|
795 |
insn = bfd_get_32 (input_bfd, |
insn = bfd_get_32 (input_bfd, |
796 |
contents + adjust + refhi->r_vaddr - input_section->vma); |
contents + refhi->r_vaddr - input_section->vma); |
797 |
if (reflo == NULL) |
if (reflo == NULL) |
798 |
vallo = 0; |
vallo = 0; |
799 |
else |
else |
800 |
vallo = (bfd_get_32 (input_bfd, |
vallo = (bfd_get_32 (input_bfd, |
801 |
contents + adjust + reflo->r_vaddr - input_section->vma) |
contents + reflo->r_vaddr - input_section->vma) |
802 |
& 0xffff); |
& 0xffff); |
803 |
|
|
804 |
val = ((insn & 0xffff) << 16) + vallo; |
val = ((insn & 0xffff) << 16) + vallo; |
812 |
if ((vallo & 0x8000) != 0) |
if ((vallo & 0x8000) != 0) |
813 |
val -= 0x10000; |
val -= 0x10000; |
814 |
|
|
|
if (pcrel) |
|
|
val -= (input_section->output_section->vma |
|
|
+ input_section->output_offset |
|
|
+ (reflo->r_vaddr - input_section->vma + adjust)); |
|
|
|
|
815 |
if ((val & 0x8000) != 0) |
if ((val & 0x8000) != 0) |
816 |
val += 0x10000; |
val += 0x10000; |
817 |
|
|
818 |
insn = (insn &~ (unsigned) 0xffff) | ((val >> 16) & 0xffff); |
insn = (insn &~ (unsigned) 0xffff) | ((val >> 16) & 0xffff); |
819 |
bfd_put_32 (input_bfd, (bfd_vma) insn, |
bfd_put_32 (input_bfd, (bfd_vma) insn, |
820 |
contents + adjust + refhi->r_vaddr - input_section->vma); |
contents + refhi->r_vaddr - input_section->vma); |
821 |
} |
} |
822 |
|
|
823 |
/* Relocate a section while linking a MIPS ECOFF file. */ |
/* Relocate a section while linking a MIPS ECOFF file. */ |
824 |
|
|
825 |
static boolean |
static bfd_boolean |
826 |
mips_relocate_section (output_bfd, info, input_bfd, input_section, |
mips_relocate_section (output_bfd, info, input_bfd, input_section, |
827 |
contents, external_relocs) |
contents, external_relocs) |
828 |
bfd *output_bfd; |
bfd *output_bfd; |
835 |
asection **symndx_to_section; |
asection **symndx_to_section; |
836 |
struct ecoff_link_hash_entry **sym_hashes; |
struct ecoff_link_hash_entry **sym_hashes; |
837 |
bfd_vma gp; |
bfd_vma gp; |
838 |
boolean gp_undefined; |
bfd_boolean gp_undefined; |
|
size_t adjust; |
|
|
long *offsets; |
|
839 |
struct external_reloc *ext_rel; |
struct external_reloc *ext_rel; |
840 |
struct external_reloc *ext_rel_end; |
struct external_reloc *ext_rel_end; |
841 |
unsigned int i; |
unsigned int i; |
842 |
boolean got_lo; |
bfd_boolean got_lo; |
843 |
struct internal_reloc lo_int_rel; |
struct internal_reloc lo_int_rel; |
844 |
bfd_size_type amt; |
bfd_size_type amt; |
845 |
|
|
855 |
amt = NUM_RELOC_SECTIONS * sizeof (asection *); |
amt = NUM_RELOC_SECTIONS * sizeof (asection *); |
856 |
symndx_to_section = (asection **) bfd_alloc (input_bfd, amt); |
symndx_to_section = (asection **) bfd_alloc (input_bfd, amt); |
857 |
if (!symndx_to_section) |
if (!symndx_to_section) |
858 |
return false; |
return FALSE; |
859 |
|
|
860 |
symndx_to_section[RELOC_SECTION_NONE] = NULL; |
symndx_to_section[RELOC_SECTION_NONE] = NULL; |
861 |
symndx_to_section[RELOC_SECTION_TEXT] = |
symndx_to_section[RELOC_SECTION_TEXT] = |
890 |
|
|
891 |
gp = _bfd_get_gp_value (output_bfd); |
gp = _bfd_get_gp_value (output_bfd); |
892 |
if (gp == 0) |
if (gp == 0) |
893 |
gp_undefined = true; |
gp_undefined = TRUE; |
894 |
else |
else |
895 |
gp_undefined = false; |
gp_undefined = FALSE; |
|
|
|
|
got_lo = false; |
|
896 |
|
|
897 |
adjust = 0; |
got_lo = FALSE; |
|
|
|
|
if (ecoff_section_data (input_bfd, input_section) == NULL) |
|
|
offsets = NULL; |
|
|
else |
|
|
offsets = ecoff_section_data (input_bfd, input_section)->offsets; |
|
898 |
|
|
899 |
ext_rel = (struct external_reloc *) external_relocs; |
ext_rel = (struct external_reloc *) external_relocs; |
900 |
ext_rel_end = ext_rel + input_section->reloc_count; |
ext_rel_end = ext_rel + input_section->reloc_count; |
901 |
for (i = 0; ext_rel < ext_rel_end; ext_rel++, i++) |
for (i = 0; ext_rel < ext_rel_end; ext_rel++, i++) |
902 |
{ |
{ |
903 |
struct internal_reloc int_rel; |
struct internal_reloc int_rel; |
904 |
boolean use_lo = false; |
bfd_boolean use_lo = FALSE; |
905 |
bfd_vma addend; |
bfd_vma addend; |
906 |
reloc_howto_type *howto; |
reloc_howto_type *howto; |
907 |
struct ecoff_link_hash_entry *h = NULL; |
struct ecoff_link_hash_entry *h = NULL; |
914 |
else |
else |
915 |
{ |
{ |
916 |
int_rel = lo_int_rel; |
int_rel = lo_int_rel; |
917 |
got_lo = false; |
got_lo = FALSE; |
918 |
} |
} |
919 |
|
|
920 |
BFD_ASSERT (int_rel.r_type |
BFD_ASSERT (int_rel.r_type |
921 |
< sizeof mips_howto_table / sizeof mips_howto_table[0]); |
< sizeof mips_howto_table / sizeof mips_howto_table[0]); |
922 |
|
|
923 |
/* The REFHI and RELHI relocs requires special handling. they |
/* The REFHI reloc requires special handling. It must be followed |
924 |
must be followed by a REFLO or RELLO reloc, respectively, and |
by a REFLO reloc, and the addend is formed from both relocs. */ |
925 |
the addend is formed from both relocs. */ |
if (int_rel.r_type == MIPS_R_REFHI) |
|
if (int_rel.r_type == MIPS_R_REFHI |
|
|
|| int_rel.r_type == MIPS_R_RELHI) |
|
926 |
{ |
{ |
927 |
struct external_reloc *lo_ext_rel; |
struct external_reloc *lo_ext_rel; |
928 |
|
|
929 |
/* As a GNU extension, permit an arbitrary number of REFHI |
/* As a GNU extension, permit an arbitrary number of REFHI |
930 |
or RELHI relocs before the REFLO or RELLO reloc. This |
relocs before the REFLO reloc. This permits gcc to emit |
931 |
permits gcc to emit the HI and LO relocs itself. */ |
the HI and LO relocs itself. */ |
932 |
for (lo_ext_rel = ext_rel + 1; |
for (lo_ext_rel = ext_rel + 1; |
933 |
lo_ext_rel < ext_rel_end; |
lo_ext_rel < ext_rel_end; |
934 |
lo_ext_rel++) |
lo_ext_rel++) |
940 |
} |
} |
941 |
|
|
942 |
if (lo_ext_rel < ext_rel_end |
if (lo_ext_rel < ext_rel_end |
943 |
&& (lo_int_rel.r_type |
&& lo_int_rel.r_type == MIPS_R_REFLO |
|
== (int_rel.r_type == MIPS_R_REFHI |
|
|
? MIPS_R_REFLO |
|
|
: MIPS_R_RELLO)) |
|
944 |
&& int_rel.r_extern == lo_int_rel.r_extern |
&& int_rel.r_extern == lo_int_rel.r_extern |
945 |
&& int_rel.r_symndx == lo_int_rel.r_symndx) |
&& int_rel.r_symndx == lo_int_rel.r_symndx) |
946 |
{ |
{ |
947 |
use_lo = true; |
use_lo = TRUE; |
948 |
if (lo_ext_rel == ext_rel + 1) |
if (lo_ext_rel == ext_rel + 1) |
949 |
got_lo = true; |
got_lo = TRUE; |
950 |
} |
} |
951 |
} |
} |
952 |
|
|
953 |
howto = &mips_howto_table[int_rel.r_type]; |
howto = &mips_howto_table[int_rel.r_type]; |
954 |
|
|
|
/* The SWITCH reloc must be handled specially. This reloc is |
|
|
marks the location of a difference between two portions of an |
|
|
object file. The symbol index does not reference a symbol, |
|
|
but is actually the offset from the reloc to the subtrahend |
|
|
of the difference. This reloc is correct in the object file, |
|
|
and needs no further adjustment, unless we are relaxing. If |
|
|
we are relaxing, we may have to add in an offset. Since no |
|
|
symbols are involved in this reloc, we handle it completely |
|
|
here. */ |
|
|
if (int_rel.r_type == MIPS_R_SWITCH) |
|
|
{ |
|
|
if (offsets != NULL |
|
|
&& offsets[i] != 0) |
|
|
{ |
|
|
r = _bfd_relocate_contents (howto, input_bfd, |
|
|
(bfd_vma) offsets[i], |
|
|
(contents |
|
|
+ adjust |
|
|
+ int_rel.r_vaddr |
|
|
- input_section->vma)); |
|
|
BFD_ASSERT (r == bfd_reloc_ok); |
|
|
} |
|
|
|
|
|
continue; |
|
|
} |
|
|
|
|
955 |
if (int_rel.r_extern) |
if (int_rel.r_extern) |
956 |
{ |
{ |
957 |
h = sym_hashes[int_rel.r_symndx]; |
h = sym_hashes[int_rel.r_symndx]; |
985 |
(info, _("GP relative relocation used when GP not defined"), |
(info, _("GP relative relocation used when GP not defined"), |
986 |
input_bfd, input_section, |
input_bfd, input_section, |
987 |
int_rel.r_vaddr - input_section->vma))) |
int_rel.r_vaddr - input_section->vma))) |
988 |
return false; |
return FALSE; |
989 |
/* Only give the error once per link. */ |
/* Only give the error once per link. */ |
990 |
gp = 4; |
gp = 4; |
991 |
_bfd_set_gp_value (output_bfd, gp); |
_bfd_set_gp_value (output_bfd, gp); |
992 |
gp_undefined = false; |
gp_undefined = FALSE; |
993 |
} |
} |
994 |
if (! int_rel.r_extern) |
if (! int_rel.r_extern) |
995 |
{ |
{ |
1001 |
and the GP value of OUTPUT_BFD (which is in GP). */ |
and the GP value of OUTPUT_BFD (which is in GP). */ |
1002 |
addend = ecoff_data (input_bfd)->gp - gp; |
addend = ecoff_data (input_bfd)->gp - gp; |
1003 |
} |
} |
1004 |
else if (! info->relocateable |
else if (! info->relocatable |
1005 |
|| h->root.type == bfd_link_hash_defined |
|| h->root.type == bfd_link_hash_defined |
1006 |
|| h->root.type == bfd_link_hash_defweak) |
|| h->root.type == bfd_link_hash_defweak) |
1007 |
{ |
{ |
1020 |
/* This is a relocation against an undefined or common |
/* This is a relocation against an undefined or common |
1021 |
symbol. The current addend in the instruction is |
symbol. The current addend in the instruction is |
1022 |
simply the desired offset into the symbol (normally |
simply the desired offset into the symbol (normally |
1023 |
zero). We are generating relocateable output, and we |
zero). We are generating relocatable output, and we |
1024 |
aren't going to define this symbol, so we just leave |
aren't going to define this symbol, so we just leave |
1025 |
the instruction alone. */ |
the instruction alone. */ |
1026 |
addend = 0; |
addend = 0; |
1027 |
} |
} |
1028 |
} |
} |
1029 |
|
|
1030 |
/* If we are relaxing, mips_relax_section may have set |
if (info->relocatable) |
|
offsets[i] to some value. A value of 1 means we must expand |
|
|
a PC relative branch into a multi-instruction of sequence, |
|
|
and any other value is an addend. */ |
|
|
if (offsets != NULL |
|
|
&& offsets[i] != 0) |
|
1031 |
{ |
{ |
1032 |
BFD_ASSERT (! info->relocateable); |
/* We are generating relocatable output, and must convert |
|
BFD_ASSERT (int_rel.r_type == MIPS_R_PCREL16 |
|
|
|| int_rel.r_type == MIPS_R_RELHI |
|
|
|| int_rel.r_type == MIPS_R_RELLO); |
|
|
if (offsets[i] != 1) |
|
|
addend += offsets[i]; |
|
|
else |
|
|
{ |
|
|
bfd_byte *here; |
|
|
|
|
|
BFD_ASSERT (int_rel.r_extern |
|
|
&& int_rel.r_type == MIPS_R_PCREL16); |
|
|
|
|
|
/* Move the rest of the instructions up. */ |
|
|
here = (contents |
|
|
+ adjust |
|
|
+ int_rel.r_vaddr |
|
|
- input_section->vma); |
|
|
memmove (here + PCREL16_EXPANSION_ADJUSTMENT, here, |
|
|
(size_t) (input_section->_raw_size |
|
|
- (int_rel.r_vaddr - input_section->vma))); |
|
|
|
|
|
/* Generate the new instructions. */ |
|
|
if (! mips_relax_pcrel16 (info, input_bfd, input_section, |
|
|
h, here, |
|
|
(input_section->output_section->vma |
|
|
+ input_section->output_offset |
|
|
+ (int_rel.r_vaddr |
|
|
- input_section->vma) |
|
|
+ adjust))) |
|
|
return false; |
|
|
|
|
|
/* We must adjust everything else up a notch. */ |
|
|
adjust += PCREL16_EXPANSION_ADJUSTMENT; |
|
|
|
|
|
/* mips_relax_pcrel16 handles all the details of this |
|
|
relocation. */ |
|
|
continue; |
|
|
} |
|
|
} |
|
|
|
|
|
/* If we are relaxing, and this is a reloc against the .text |
|
|
segment, we may need to adjust it if some branches have been |
|
|
expanded. The reloc types which are likely to occur in the |
|
|
.text section are handled efficiently by mips_relax_section, |
|
|
and thus do not need to be handled here. */ |
|
|
if (ecoff_data (input_bfd)->debug_info.adjust != NULL |
|
|
&& ! int_rel.r_extern |
|
|
&& int_rel.r_symndx == RELOC_SECTION_TEXT |
|
|
&& (strcmp (bfd_get_section_name (input_bfd, input_section), |
|
|
".text") != 0 |
|
|
|| (int_rel.r_type != MIPS_R_PCREL16 |
|
|
&& int_rel.r_type != MIPS_R_SWITCH |
|
|
&& int_rel.r_type != MIPS_R_RELHI |
|
|
&& int_rel.r_type != MIPS_R_RELLO))) |
|
|
{ |
|
|
bfd_vma adr; |
|
|
struct ecoff_value_adjust *a; |
|
|
|
|
|
/* We need to get the addend so that we know whether we need |
|
|
to adjust the address. */ |
|
|
BFD_ASSERT (int_rel.r_type == MIPS_R_REFWORD); |
|
|
|
|
|
adr = bfd_get_32 (input_bfd, |
|
|
(contents |
|
|
+ adjust |
|
|
+ int_rel.r_vaddr |
|
|
- input_section->vma)); |
|
|
|
|
|
for (a = ecoff_data (input_bfd)->debug_info.adjust; |
|
|
a != (struct ecoff_value_adjust *) NULL; |
|
|
a = a->next) |
|
|
{ |
|
|
if (adr >= a->start && adr < a->end) |
|
|
addend += a->adjust; |
|
|
} |
|
|
} |
|
|
|
|
|
if (info->relocateable) |
|
|
{ |
|
|
/* We are generating relocateable output, and must convert |
|
1033 |
the existing reloc. */ |
the existing reloc. */ |
1034 |
if (int_rel.r_extern) |
if (int_rel.r_extern) |
1035 |
{ |
{ |
1104 |
currently holds just the addend. We must adjust |
currently holds just the addend. We must adjust |
1105 |
by the address to get the right value. */ |
by the address to get the right value. */ |
1106 |
if (howto->pc_relative) |
if (howto->pc_relative) |
1107 |
{ |
relocation -= int_rel.r_vaddr - input_section->vma; |
|
relocation -= int_rel.r_vaddr - input_section->vma; |
|
|
|
|
|
/* If we are converting a RELHI or RELLO reloc |
|
|
from being against an external symbol to |
|
|
being against a section, we must put a |
|
|
special value into the r_offset field. This |
|
|
value is the old addend. The r_offset for |
|
|
both the RELHI and RELLO relocs are the same, |
|
|
and we set both when we see RELHI. */ |
|
|
if (int_rel.r_type == MIPS_R_RELHI) |
|
|
{ |
|
|
long addhi, addlo; |
|
|
|
|
|
addhi = bfd_get_32 (input_bfd, |
|
|
(contents |
|
|
+ adjust |
|
|
+ int_rel.r_vaddr |
|
|
- input_section->vma)); |
|
|
addhi &= 0xffff; |
|
|
if (addhi & 0x8000) |
|
|
addhi -= 0x10000; |
|
|
addhi <<= 16; |
|
|
|
|
|
if (! use_lo) |
|
|
addlo = 0; |
|
|
else |
|
|
{ |
|
|
addlo = bfd_get_32 (input_bfd, |
|
|
(contents |
|
|
+ adjust |
|
|
+ lo_int_rel.r_vaddr |
|
|
- input_section->vma)); |
|
|
addlo &= 0xffff; |
|
|
if (addlo & 0x8000) |
|
|
addlo -= 0x10000; |
|
|
|
|
|
lo_int_rel.r_offset = addhi + addlo; |
|
|
} |
|
|
|
|
|
int_rel.r_offset = addhi + addlo; |
|
|
} |
|
|
} |
|
1108 |
|
|
1109 |
h = NULL; |
h = NULL; |
1110 |
} |
} |
1120 |
(info, h->root.root.string, input_bfd, |
(info, h->root.root.string, input_bfd, |
1121 |
input_section, |
input_section, |
1122 |
int_rel.r_vaddr - input_section->vma))) |
int_rel.r_vaddr - input_section->vma))) |
1123 |
return false; |
return FALSE; |
1124 |
int_rel.r_symndx = 0; |
int_rel.r_symndx = 0; |
1125 |
} |
} |
1126 |
relocation = 0; |
relocation = 0; |
1140 |
|
|
1141 |
/* Adjust a PC relative relocation by removing the reference |
/* Adjust a PC relative relocation by removing the reference |
1142 |
to the original address in the section and including the |
to the original address in the section and including the |
1143 |
reference to the new address. However, external RELHI |
reference to the new address. */ |
1144 |
and RELLO relocs are PC relative, but don't include any |
if (howto->pc_relative) |
|
reference to the address. The addend is merely an |
|
|
addend. */ |
|
|
if (howto->pc_relative |
|
|
&& (! int_rel.r_extern |
|
|
|| (int_rel.r_type != MIPS_R_RELHI |
|
|
&& int_rel.r_type != MIPS_R_RELLO))) |
|
1145 |
relocation -= (input_section->output_section->vma |
relocation -= (input_section->output_section->vma |
1146 |
+ input_section->output_offset |
+ input_section->output_offset |
1147 |
- input_section->vma); |
- input_section->vma); |
1151 |
r = bfd_reloc_ok; |
r = bfd_reloc_ok; |
1152 |
else |
else |
1153 |
{ |
{ |
1154 |
if (int_rel.r_type != MIPS_R_REFHI |
if (int_rel.r_type != MIPS_R_REFHI) |
|
&& int_rel.r_type != MIPS_R_RELHI) |
|
1155 |
r = _bfd_relocate_contents (howto, input_bfd, relocation, |
r = _bfd_relocate_contents (howto, input_bfd, relocation, |
1156 |
(contents |
(contents |
|
+ adjust |
|
1157 |
+ int_rel.r_vaddr |
+ int_rel.r_vaddr |
1158 |
- input_section->vma)); |
- input_section->vma)); |
1159 |
else |
else |
1161 |
mips_relocate_hi (&int_rel, |
mips_relocate_hi (&int_rel, |
1162 |
use_lo ? &lo_int_rel : NULL, |
use_lo ? &lo_int_rel : NULL, |
1163 |
input_bfd, input_section, contents, |
input_bfd, input_section, contents, |
1164 |
adjust, relocation, |
relocation); |
|
int_rel.r_type == MIPS_R_RELHI); |
|
1165 |
r = bfd_reloc_ok; |
r = bfd_reloc_ok; |
1166 |
} |
} |
1167 |
} |
} |
1195 |
if (! ((*info->callbacks->undefined_symbol) |
if (! ((*info->callbacks->undefined_symbol) |
1196 |
(info, h->root.root.string, input_bfd, |
(info, h->root.root.string, input_bfd, |
1197 |
input_section, |
input_section, |
1198 |
int_rel.r_vaddr - input_section->vma, true))) |
int_rel.r_vaddr - input_section->vma, TRUE))) |
1199 |
return false; |
return FALSE; |
1200 |
relocation = 0; |
relocation = 0; |
1201 |
} |
} |
1202 |
} |
} |
1211 |
file. Make it look like a pcrel_offset relocation by |
file. Make it look like a pcrel_offset relocation by |
1212 |
adding in the start address. */ |
adding in the start address. */ |
1213 |
if (howto->pc_relative) |
if (howto->pc_relative) |
1214 |
{ |
relocation += int_rel.r_vaddr; |
|
if (int_rel.r_type != MIPS_R_RELHI || ! use_lo) |
|
|
relocation += int_rel.r_vaddr + adjust; |
|
|
else |
|
|
relocation += lo_int_rel.r_vaddr + adjust; |
|
|
} |
|
1215 |
} |
} |
1216 |
|
|
1217 |
if (int_rel.r_type != MIPS_R_REFHI |
if (int_rel.r_type != MIPS_R_REFHI) |
|
&& int_rel.r_type != MIPS_R_RELHI) |
|
1218 |
r = _bfd_final_link_relocate (howto, |
r = _bfd_final_link_relocate (howto, |
1219 |
input_bfd, |
input_bfd, |
1220 |
input_section, |
input_section, |
1221 |
contents, |
contents, |
1222 |
(int_rel.r_vaddr |
(int_rel.r_vaddr |
1223 |
- input_section->vma |
- input_section->vma), |
|
+ adjust), |
|
1224 |
relocation, |
relocation, |
1225 |
addend); |
addend); |
1226 |
else |
else |
1227 |
{ |
{ |
1228 |
mips_relocate_hi (&int_rel, |
mips_relocate_hi (&int_rel, |
1229 |
use_lo ? &lo_int_rel : NULL, |
use_lo ? &lo_int_rel : NULL, |
1230 |
input_bfd, input_section, contents, adjust, |
input_bfd, input_section, contents, |
1231 |
relocation, |
relocation); |
|
int_rel.r_type == MIPS_R_RELHI); |
|
1232 |
r = bfd_reloc_ok; |
r = bfd_reloc_ok; |
1233 |
} |
} |
1234 |
} |
} |
1245 |
& 0xf0000000) |
& 0xf0000000) |
1246 |
!= ((input_section->output_section->vma |
!= ((input_section->output_section->vma |
1247 |
+ input_section->output_offset |
+ input_section->output_offset |
1248 |
+ (int_rel.r_vaddr - input_section->vma) |
+ (int_rel.r_vaddr - input_section->vma)) |
|
+ adjust) |
|
1249 |
& 0xf0000000))) |
& 0xf0000000))) |
1250 |
r = bfd_reloc_overflow; |
r = bfd_reloc_overflow; |
1251 |
|
|
1261 |
const char *name; |
const char *name; |
1262 |
|
|
1263 |
if (int_rel.r_extern) |
if (int_rel.r_extern) |
1264 |
name = h->root.root.string; |
name = NULL; |
1265 |
else |
else |
1266 |
name = bfd_section_name (input_bfd, s); |
name = bfd_section_name (input_bfd, s); |
1267 |
if (! ((*info->callbacks->reloc_overflow) |
if (! ((*info->callbacks->reloc_overflow) |
1268 |
(info, name, howto->name, (bfd_vma) 0, |
(info, (h ? &h->root : NULL), name, howto->name, |
1269 |
input_bfd, input_section, |
(bfd_vma) 0, input_bfd, input_section, |
1270 |
int_rel.r_vaddr - input_section->vma))) |
int_rel.r_vaddr - input_section->vma))) |
1271 |
return false; |
return FALSE; |
1272 |
} |
} |
1273 |
break; |
break; |
1274 |
} |
} |
1275 |
} |
} |
1276 |
} |
} |
1277 |
|
|
1278 |
return true; |
return TRUE; |
|
} |
|
|
|
|
|
/* Read in the relocs for a section. */ |
|
|
|
|
|
static boolean |
|
|
mips_read_relocs (abfd, sec) |
|
|
bfd *abfd; |
|
|
asection *sec; |
|
|
{ |
|
|
struct ecoff_section_tdata *section_tdata; |
|
|
bfd_size_type amt; |
|
|
|
|
|
section_tdata = ecoff_section_data (abfd, sec); |
|
|
if (section_tdata == (struct ecoff_section_tdata *) NULL) |
|
|
{ |
|
|
amt = sizeof (struct ecoff_section_tdata); |
|
|
sec->used_by_bfd = (PTR) bfd_alloc (abfd, amt); |
|
|
if (sec->used_by_bfd == NULL) |
|
|
return false; |
|
|
|
|
|
section_tdata = ecoff_section_data (abfd, sec); |
|
|
section_tdata->external_relocs = NULL; |
|
|
section_tdata->contents = NULL; |
|
|
section_tdata->offsets = NULL; |
|
|
} |
|
|
|
|
|
if (section_tdata->external_relocs == NULL) |
|
|
{ |
|
|
amt = ecoff_backend (abfd)->external_reloc_size; |
|
|
amt *= sec->reloc_count; |
|
|
section_tdata->external_relocs = (PTR) bfd_alloc (abfd, amt); |
|
|
if (section_tdata->external_relocs == NULL && amt != 0) |
|
|
return false; |
|
|
|
|
|
if (bfd_seek (abfd, sec->rel_filepos, SEEK_SET) != 0 |
|
|
|| bfd_bread (section_tdata->external_relocs, amt, abfd) != amt) |
|
|
return false; |
|
|
} |
|
|
|
|
|
return true; |
|
|
} |
|
|
|
|
|
/* Relax a section when linking a MIPS ECOFF file. This is used for |
|
|
embedded PIC code, which always uses PC relative branches which |
|
|
only have an 18 bit range on MIPS. If a branch is not in range, we |
|
|
generate a long instruction sequence to compensate. Each time we |
|
|
find a branch to expand, we have to check all the others again to |
|
|
make sure they are still in range. This is slow, but it only has |
|
|
to be done when -relax is passed to the linker. |
|
|
|
|
|
This routine figures out which branches need to expand; the actual |
|
|
expansion is done in mips_relocate_section when the section |
|
|
contents are relocated. The information is stored in the offsets |
|
|
field of the ecoff_section_tdata structure. An offset of 1 means |
|
|
that the branch must be expanded into a multi-instruction PC |
|
|
relative branch (such an offset will only occur for a PC relative |
|
|
branch to an external symbol). Any other offset must be a multiple |
|
|
of four, and is the amount to change the branch by (such an offset |
|
|
will only occur for a PC relative branch within the same section). |
|
|
|
|
|
We do not modify the section relocs or contents themselves so that |
|
|
if memory usage becomes an issue we can discard them and read them |
|
|
again. The only information we must save in memory between this |
|
|
routine and the mips_relocate_section routine is the table of |
|
|
offsets. */ |
|
|
|
|
|
static boolean |
|
|
mips_relax_section (abfd, sec, info, again) |
|
|
bfd *abfd; |
|
|
asection *sec; |
|
|
struct bfd_link_info *info; |
|
|
boolean *again; |
|
|
{ |
|
|
struct ecoff_section_tdata *section_tdata; |
|
|
bfd_byte *contents = NULL; |
|
|
long *offsets; |
|
|
struct external_reloc *ext_rel; |
|
|
struct external_reloc *ext_rel_end; |
|
|
unsigned int i; |
|
|
|
|
|
/* Assume we are not going to need another pass. */ |
|
|
*again = false; |
|
|
|
|
|
/* If we are not generating an ECOFF file, this is much too |
|
|
confusing to deal with. */ |
|
|
if (info->hash->creator->flavour != bfd_get_flavour (abfd)) |
|
|
return true; |
|
|
|
|
|
/* If there are no relocs, there is nothing to do. */ |
|
|
if (sec->reloc_count == 0) |
|
|
return true; |
|
|
|
|
|
/* We are only interested in PC relative relocs, and why would there |
|
|
ever be one from anything but the .text section? */ |
|
|
if (strcmp (bfd_get_section_name (abfd, sec), ".text") != 0) |
|
|
return true; |
|
|
|
|
|
/* Read in the relocs, if we haven't already got them. */ |
|
|
section_tdata = ecoff_section_data (abfd, sec); |
|
|
if (section_tdata == (struct ecoff_section_tdata *) NULL |
|
|
|| section_tdata->external_relocs == NULL) |
|
|
{ |
|
|
if (! mips_read_relocs (abfd, sec)) |
|
|
goto error_return; |
|
|
section_tdata = ecoff_section_data (abfd, sec); |
|
|
} |
|
|
|
|
|
if (sec->_cooked_size == 0) |
|
|
{ |
|
|
/* We must initialize _cooked_size only the first time we are |
|
|
called. */ |
|
|
sec->_cooked_size = sec->_raw_size; |
|
|
} |
|
|
|
|
|
contents = section_tdata->contents; |
|
|
offsets = section_tdata->offsets; |
|
|
|
|
|
/* Look for any external PC relative relocs. Internal PC relative |
|
|
relocs are already correct in the object file, so they certainly |
|
|
can not overflow. */ |
|
|
ext_rel = (struct external_reloc *) section_tdata->external_relocs; |
|
|
ext_rel_end = ext_rel + sec->reloc_count; |
|
|
for (i = 0; ext_rel < ext_rel_end; ext_rel++, i++) |
|
|
{ |
|
|
struct internal_reloc int_rel; |
|
|
struct ecoff_link_hash_entry *h; |
|
|
asection *hsec; |
|
|
bfd_signed_vma relocation; |
|
|
struct external_reloc *adj_ext_rel; |
|
|
unsigned int adj_i; |
|
|
unsigned long ext_count; |
|
|
struct ecoff_link_hash_entry **adj_h_ptr; |
|
|
struct ecoff_link_hash_entry **adj_h_ptr_end; |
|
|
struct ecoff_value_adjust *adjust; |
|
|
bfd_size_type amt; |
|
|
|
|
|
/* If we have already expanded this reloc, we certainly don't |
|
|
need to do it again. */ |
|
|
if (offsets != (long *) NULL && offsets[i] == 1) |
|
|
continue; |
|
|
|
|
|
/* Quickly check that this reloc is external PCREL16. */ |
|
|
if (bfd_header_big_endian (abfd)) |
|
|
{ |
|
|
if ((ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_BIG) == 0 |
|
|
|| (((ext_rel->r_bits[3] & RELOC_BITS3_TYPE_BIG) |
|
|
>> RELOC_BITS3_TYPE_SH_BIG) |
|
|
!= MIPS_R_PCREL16)) |
|
|
continue; |
|
|
} |
|
|
else |
|
|
{ |
|
|
if ((ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) == 0 |
|
|
|| (((ext_rel->r_bits[3] & RELOC_BITS3_TYPE_LITTLE) |
|
|
>> RELOC_BITS3_TYPE_SH_LITTLE) |
|
|
!= MIPS_R_PCREL16)) |
|
|
continue; |
|
|
} |
|
|
|
|
|
mips_ecoff_swap_reloc_in (abfd, (PTR) ext_rel, &int_rel); |
|
|
|
|
|
h = ecoff_data (abfd)->sym_hashes[int_rel.r_symndx]; |
|
|
if (h == (struct ecoff_link_hash_entry *) NULL) |
|
|
abort (); |
|
|
|
|
|
if (h->root.type != bfd_link_hash_defined |
|
|
&& h->root.type != bfd_link_hash_defweak) |
|
|
{ |
|
|
/* Just ignore undefined symbols. These will presumably |
|
|
generate an error later in the link. */ |
|
|
continue; |
|
|
} |
|
|
|
|
|
/* Get the value of the symbol. */ |
|
|
hsec = h->root.u.def.section; |
|
|
relocation = (h->root.u.def.value |
|
|
+ hsec->output_section->vma |
|
|
+ hsec->output_offset); |
|
|
|
|
|
/* Subtract out the current address. */ |
|
|
relocation -= (sec->output_section->vma |
|
|
+ sec->output_offset |
|
|
+ (int_rel.r_vaddr - sec->vma)); |
|
|
|
|
|
/* The addend is stored in the object file. In the normal case |
|
|
of ``bal symbol'', the addend will be -4. It will only be |
|
|
different in the case of ``bal symbol+constant''. To avoid |
|
|
always reading in the section contents, we don't check the |
|
|
addend in the object file (we could easily check the contents |
|
|
if we happen to have already read them in, but I fear that |
|
|
this could be confusing). This means we will screw up if |
|
|
there is a branch to a symbol that is in range, but added to |
|
|
a constant which puts it out of range; in such a case the |
|
|
link will fail with a reloc overflow error. Since the |
|
|
compiler will never generate such code, it should be easy |
|
|
enough to work around it by changing the assembly code in the |
|
|
source file. */ |
|
|
relocation -= 4; |
|
|
|
|
|
/* Now RELOCATION is the number we want to put in the object |
|
|
file. See whether it fits. */ |
|
|
if (relocation >= -0x20000 && relocation < 0x20000) |
|
|
continue; |
|
|
|
|
|
/* Now that we know this reloc needs work, which will rarely |
|
|
happen, go ahead and grab the section contents. */ |
|
|
if (contents == (bfd_byte *) NULL) |
|
|
{ |
|
|
if (info->keep_memory) |
|
|
contents = (bfd_byte *) bfd_alloc (abfd, sec->_raw_size); |
|
|
else |
|
|
contents = (bfd_byte *) bfd_malloc (sec->_raw_size); |
|
|
if (contents == (bfd_byte *) NULL) |
|
|
goto error_return; |
|
|
if (! bfd_get_section_contents (abfd, sec, (PTR) contents, |
|
|
(file_ptr) 0, sec->_raw_size)) |
|
|
goto error_return; |
|
|
if (info->keep_memory) |
|
|
section_tdata->contents = contents; |
|
|
} |
|
|
|
|
|
/* We only support changing the bal instruction. It would be |
|
|
possible to handle other PC relative branches, but some of |
|
|
them (the conditional branches) would require a different |
|
|
length instruction sequence which would complicate both this |
|
|
routine and mips_relax_pcrel16. It could be written if |
|
|
somebody felt it were important. Ignoring this reloc will |
|
|
presumably cause a reloc overflow error later on. */ |
|
|
if (bfd_get_32 (abfd, contents + int_rel.r_vaddr - sec->vma) |
|
|
!= 0x0411ffff) /* bgezal $0,. == bal . */ |
|
|
continue; |
|
|
|
|
|
/* Bother. We need to expand this reloc, and we will need to |
|
|
make another relaxation pass since this change may put other |
|
|
relocs out of range. We need to examine the local branches |
|
|
and we need to allocate memory to hold the offsets we must |
|
|
add to them. We also need to adjust the values of all |
|
|
symbols in the object file following this location. */ |
|
|
|
|
|
sec->_cooked_size += PCREL16_EXPANSION_ADJUSTMENT; |
|
|
*again = true; |
|
|
|
|
|
if (offsets == (long *) NULL) |
|
|
{ |
|
|
bfd_size_type size; |
|
|
|
|
|
size = (bfd_size_type) sec->reloc_count * sizeof (long); |
|
|
offsets = (long *) bfd_zalloc (abfd, size); |
|
|
if (offsets == (long *) NULL) |
|
|
goto error_return; |
|
|
section_tdata->offsets = offsets; |
|
|
} |
|
|
|
|
|
offsets[i] = 1; |
|
|
|
|
|
/* Now look for all PC relative references that cross this reloc |
|
|
and adjust their offsets. */ |
|
|
adj_ext_rel = (struct external_reloc *) section_tdata->external_relocs; |
|
|
for (adj_i = 0; adj_ext_rel < ext_rel_end; adj_ext_rel++, adj_i++) |
|
|
{ |
|
|
struct internal_reloc adj_int_rel; |
|
|
bfd_vma start, stop; |
|
|
int change; |
|
|
|
|
|
mips_ecoff_swap_reloc_in (abfd, (PTR) adj_ext_rel, &adj_int_rel); |
|
|
|
|
|
if (adj_int_rel.r_type == MIPS_R_PCREL16) |
|
|
{ |
|
|
unsigned long insn; |
|
|
|
|
|
/* We only care about local references. External ones |
|
|
will be relocated correctly anyhow. */ |
|
|
if (adj_int_rel.r_extern) |
|
|
continue; |
|
|
|
|
|
/* We are only interested in a PC relative reloc within |
|
|
this section. FIXME: Cross section PC relative |
|
|
relocs may not be handled correctly; does anybody |
|
|
care? */ |
|
|
if (adj_int_rel.r_symndx != RELOC_SECTION_TEXT) |
|
|
continue; |
|
|
|
|
|
start = adj_int_rel.r_vaddr; |
|
|
|
|
|
insn = bfd_get_32 (abfd, |
|
|
contents + adj_int_rel.r_vaddr - sec->vma); |
|
|
|
|
|
stop = (insn & 0xffff) << 2; |
|
|
if ((stop & 0x20000) != 0) |
|
|
stop -= 0x40000; |
|
|
stop += adj_int_rel.r_vaddr + 4; |
|
|
} |
|
|
else if (adj_int_rel.r_type == MIPS_R_RELHI) |
|
|
{ |
|
|
struct internal_reloc rello; |
|
|
long addhi, addlo; |
|
|
|
|
|
/* The next reloc must be MIPS_R_RELLO, and we handle |
|
|
them together. */ |
|
|
BFD_ASSERT (adj_ext_rel + 1 < ext_rel_end); |
|
|
|
|
|
mips_ecoff_swap_reloc_in (abfd, (PTR) (adj_ext_rel + 1), &rello); |
|
|
|
|
|
BFD_ASSERT (rello.r_type == MIPS_R_RELLO); |
|
|
|
|
|
addhi = bfd_get_32 (abfd, |
|
|
contents + adj_int_rel.r_vaddr - sec->vma); |
|
|
addhi &= 0xffff; |
|
|
if (addhi & 0x8000) |
|
|
addhi -= 0x10000; |
|
|
addhi <<= 16; |
|
|
|
|
|
addlo = bfd_get_32 (abfd, contents + rello.r_vaddr - sec->vma); |
|
|
addlo &= 0xffff; |
|
|
if (addlo & 0x8000) |
|
|
addlo -= 0x10000; |
|
|
|
|
|
if (adj_int_rel.r_extern) |
|
|
{ |
|
|
/* The value we want here is |
|
|
sym - RELLOaddr + addend |
|
|
which we can express as |
|
|
sym - (RELLOaddr - addend) |
|
|
Therefore if we are expanding the area between |
|
|
RELLOaddr and RELLOaddr - addend we must adjust |
|
|
the addend. This is admittedly ambiguous, since |
|
|
we might mean (sym + addend) - RELLOaddr, but in |
|
|
practice we don't, and there is no way to handle |
|
|
that case correctly since at this point we have |
|
|
no idea whether any reloc is being expanded |
|
|
between sym and sym + addend. */ |
|
|
start = rello.r_vaddr - (addhi + addlo); |
|
|
stop = rello.r_vaddr; |
|
|
} |
|
|
else |
|
|
{ |
|
|
/* An internal RELHI/RELLO pair represents the |
|
|
difference between two addresses, $LC0 - foo. |
|
|
The symndx value is actually the difference |
|
|
between the reloc address and $LC0. This lets us |
|
|
compute $LC0, and, by considering the addend, |
|
|
foo. If the reloc we are expanding falls between |
|
|
those two relocs, we must adjust the addend. At |
|
|
this point, the symndx value is actually in the |
|
|
r_offset field, where it was put by |
|
|
mips_ecoff_swap_reloc_in. */ |
|
|
start = rello.r_vaddr - adj_int_rel.r_offset; |
|
|
stop = start + addhi + addlo; |
|
|
} |
|
|
} |
|
|
else if (adj_int_rel.r_type == MIPS_R_SWITCH) |
|
|
{ |
|
|
/* A MIPS_R_SWITCH reloc represents a word of the form |
|
|
.word $L3-$LS12 |
|
|
The value in the object file is correct, assuming the |
|
|
original value of $L3. The symndx value is actually |
|
|
the difference between the reloc address and $LS12. |
|
|
This lets us compute the original value of $LS12 as |
|
|
vaddr - symndx |
|
|
and the original value of $L3 as |
|
|
vaddr - symndx + addend |
|
|
where addend is the value from the object file. At |
|
|
this point, the symndx value is actually found in the |
|
|
r_offset field, since it was moved by |
|
|
mips_ecoff_swap_reloc_in. */ |
|
|
start = adj_int_rel.r_vaddr - adj_int_rel.r_offset; |
|
|
stop = start + bfd_get_32 (abfd, |
|
|
(contents |
|
|
+ adj_int_rel.r_vaddr |
|
|
- sec->vma)); |
|
|
} |
|
|
else |
|
|
continue; |
|
|
|
|
|
/* If the range expressed by this reloc, which is the |
|
|
distance between START and STOP crosses the reloc we are |
|
|
expanding, we must adjust the offset. The sign of the |
|
|
adjustment depends upon the direction in which the range |
|
|
crosses the reloc being expanded. */ |
|
|
if (start <= int_rel.r_vaddr && stop > int_rel.r_vaddr) |
|
|
change = PCREL16_EXPANSION_ADJUSTMENT; |
|
|
else if (start > int_rel.r_vaddr && stop <= int_rel.r_vaddr) |
|
|
change = - PCREL16_EXPANSION_ADJUSTMENT; |
|
|
else |
|
|
change = 0; |
|
|
|
|
|
offsets[adj_i] += change; |
|
|
|
|
|
if (adj_int_rel.r_type == MIPS_R_RELHI) |
|
|
{ |
|
|
adj_ext_rel++; |
|
|
adj_i++; |
|
|
offsets[adj_i] += change; |
|
|
} |
|
|
} |
|
|
|
|
|
/* Find all symbols in this section defined by this object file |
|
|
and adjust their values. Note that we decide whether to |
|
|
adjust the value based on the value stored in the ECOFF EXTR |
|
|
structure, because the value stored in the hash table may |
|
|
have been changed by an earlier expanded reloc and thus may |
|
|
no longer correctly indicate whether the symbol is before or |
|
|
after the expanded reloc. */ |
|
|
ext_count = ecoff_data (abfd)->debug_info.symbolic_header.iextMax; |
|
|
adj_h_ptr = ecoff_data (abfd)->sym_hashes; |
|
|
adj_h_ptr_end = adj_h_ptr + ext_count; |
|
|
for (; adj_h_ptr < adj_h_ptr_end; adj_h_ptr++) |
|
|
{ |
|
|
struct ecoff_link_hash_entry *adj_h; |
|
|
|
|
|
adj_h = *adj_h_ptr; |
|
|
if (adj_h != (struct ecoff_link_hash_entry *) NULL |
|
|
&& (adj_h->root.type == bfd_link_hash_defined |
|
|
|| adj_h->root.type == bfd_link_hash_defweak) |
|
|
&& adj_h->root.u.def.section == sec |
|
|
&& adj_h->esym.asym.value > int_rel.r_vaddr) |
|
|
adj_h->root.u.def.value += PCREL16_EXPANSION_ADJUSTMENT; |
|
|
} |
|
|
|
|
|
/* Add an entry to the symbol value adjust list. This is used |
|
|
by bfd_ecoff_debug_accumulate to adjust the values of |
|
|
internal symbols and FDR's. */ |
|
|
amt = sizeof (struct ecoff_value_adjust); |
|
|
adjust = (struct ecoff_value_adjust *) bfd_alloc (abfd, amt); |
|
|
if (adjust == (struct ecoff_value_adjust *) NULL) |
|
|
goto error_return; |
|
|
|
|
|
adjust->start = int_rel.r_vaddr; |
|
|
adjust->end = sec->vma + sec->_raw_size; |
|
|
adjust->adjust = PCREL16_EXPANSION_ADJUSTMENT; |
|
|
|
|
|
adjust->next = ecoff_data (abfd)->debug_info.adjust; |
|
|
ecoff_data (abfd)->debug_info.adjust = adjust; |
|
|
} |
|
|
|
|
|
if (contents != (bfd_byte *) NULL && ! info->keep_memory) |
|
|
free (contents); |
|
|
|
|
|
return true; |
|
|
|
|
|
error_return: |
|
|
if (contents != (bfd_byte *) NULL && ! info->keep_memory) |
|
|
free (contents); |
|
|
return false; |
|
|
} |
|
|
|
|
|
/* This routine is called from mips_relocate_section when a PC |
|
|
relative reloc must be expanded into the five instruction sequence. |
|
|
It handles all the details of the expansion, including resolving |
|
|
the reloc. */ |
|
|
|
|
|
static boolean |
|
|
mips_relax_pcrel16 (info, input_bfd, input_section, h, location, address) |
|
|
struct bfd_link_info *info ATTRIBUTE_UNUSED; |
|
|
bfd *input_bfd; |
|
|
asection *input_section ATTRIBUTE_UNUSED; |
|
|
struct ecoff_link_hash_entry *h; |
|
|
bfd_byte *location; |
|
|
bfd_vma address; |
|
|
{ |
|
|
bfd_vma relocation; |
|
|
|
|
|
/* 0x0411ffff is bgezal $0,. == bal . */ |
|
|
BFD_ASSERT (bfd_get_32 (input_bfd, location) == 0x0411ffff); |
|
|
|
|
|
/* We need to compute the distance between the symbol and the |
|
|
current address plus eight. */ |
|
|
relocation = (h->root.u.def.value |
|
|
+ h->root.u.def.section->output_section->vma |
|
|
+ h->root.u.def.section->output_offset); |
|
|
relocation -= address + 8; |
|
|
|
|
|
/* If the lower half is negative, increment the upper 16 half. */ |
|
|
if ((relocation & 0x8000) != 0) |
|
|
relocation += 0x10000; |
|
|
|
|
|
bfd_put_32 (input_bfd, (bfd_vma) 0x04110001, location); /* bal .+8 */ |
|
|
bfd_put_32 (input_bfd, |
|
|
0x3c010000 | ((relocation >> 16) & 0xffff), /* lui $at,XX */ |
|
|
location + 4); |
|
|
bfd_put_32 (input_bfd, |
|
|
0x24210000 | (relocation & 0xffff), /* addiu $at,$at,XX */ |
|
|
location + 8); |
|
|
bfd_put_32 (input_bfd, |
|
|
(bfd_vma) 0x003f0821, location + 12); /* addu $at,$at,$ra */ |
|
|
bfd_put_32 (input_bfd, |
|
|
(bfd_vma) 0x0020f809, location + 16); /* jalr $at */ |
|
|
|
|
|
return true; |
|
|
} |
|
|
|
|
|
/* Given a .sdata section and a .rel.sdata in-memory section, store |
|
|
relocation information into the .rel.sdata section which can be |
|
|
used at runtime to relocate the section. This is called by the |
|
|
linker when the --embedded-relocs switch is used. This is called |
|
|
after the add_symbols entry point has been called for all the |
|
|
objects, and before the final_link entry point is called. This |
|
|
function presumes that the object was compiled using |
|
|
-membedded-pic. */ |
|
|
|
|
|
boolean |
|
|
bfd_mips_ecoff_create_embedded_relocs (abfd, info, datasec, relsec, errmsg) |
|
|
bfd *abfd; |
|
|
struct bfd_link_info *info; |
|
|
asection *datasec; |
|
|
asection *relsec; |
|
|
char **errmsg; |
|
|
{ |
|
|
struct ecoff_link_hash_entry **sym_hashes; |
|
|
struct ecoff_section_tdata *section_tdata; |
|
|
struct external_reloc *ext_rel; |
|
|
struct external_reloc *ext_rel_end; |
|
|
bfd_byte *p; |
|
|
bfd_size_type amt; |
|
|
|
|
|
BFD_ASSERT (! info->relocateable); |
|
|
|
|
|
*errmsg = NULL; |
|
|
|
|
|
if (datasec->reloc_count == 0) |
|
|
return true; |
|
|
|
|
|
sym_hashes = ecoff_data (abfd)->sym_hashes; |
|
|
|
|
|
if (! mips_read_relocs (abfd, datasec)) |
|
|
return false; |
|
|
|
|
|
amt = (bfd_size_type) datasec->reloc_count * 4; |
|
|
relsec->contents = (bfd_byte *) bfd_alloc (abfd, amt); |
|
|
if (relsec->contents == NULL) |
|
|
return false; |
|
|
|
|
|
p = relsec->contents; |
|
|
|
|
|
section_tdata = ecoff_section_data (abfd, datasec); |
|
|
ext_rel = (struct external_reloc *) section_tdata->external_relocs; |
|
|
ext_rel_end = ext_rel + datasec->reloc_count; |
|
|
for (; ext_rel < ext_rel_end; ext_rel++, p += 4) |
|
|
{ |
|
|
struct internal_reloc int_rel; |
|
|
boolean text_relative; |
|
|
|
|
|
mips_ecoff_swap_reloc_in (abfd, (PTR) ext_rel, &int_rel); |
|
|
|
|
|
/* We are going to write a four byte word into the runtime reloc |
|
|
section. The word will be the address in the data section |
|
|
which must be relocated. This must be on a word boundary, |
|
|
which means the lower two bits must be zero. We use the |
|
|
least significant bit to indicate how the value in the data |
|
|
section must be relocated. A 0 means that the value is |
|
|
relative to the text section, while a 1 indicates that the |
|
|
value is relative to the data section. Given that we are |
|
|
assuming the code was compiled using -membedded-pic, there |
|
|
should not be any other possibilities. */ |
|
|
|
|
|
/* We can only relocate REFWORD relocs at run time. */ |
|
|
if (int_rel.r_type != MIPS_R_REFWORD) |
|
|
{ |
|
|
*errmsg = _("unsupported reloc type"); |
|
|
bfd_set_error (bfd_error_bad_value); |
|
|
return false; |
|
|
} |
|
|
|
|
|
if (int_rel.r_extern) |
|
|
{ |
|
|
struct ecoff_link_hash_entry *h; |
|
|
|
|
|
h = sym_hashes[int_rel.r_symndx]; |
|
|
/* If h is NULL, that means that there is a reloc against an |
|
|
external symbol which we thought was just a debugging |
|
|
symbol. This should not happen. */ |
|
|
if (h == (struct ecoff_link_hash_entry *) NULL) |
|
|
abort (); |
|
|
if ((h->root.type == bfd_link_hash_defined |
|
|
|| h->root.type == bfd_link_hash_defweak) |
|
|
&& (h->root.u.def.section->flags & SEC_CODE) != 0) |
|
|
text_relative = true; |
|
|
else |
|
|
text_relative = false; |
|
|
} |
|
|
else |
|
|
{ |
|
|
switch (int_rel.r_symndx) |
|
|
{ |
|
|
case RELOC_SECTION_TEXT: |
|
|
text_relative = true; |
|
|
break; |
|
|
case RELOC_SECTION_SDATA: |
|
|
case RELOC_SECTION_SBSS: |
|
|
case RELOC_SECTION_LIT8: |
|
|
text_relative = false; |
|
|
break; |
|
|
default: |
|
|
/* No other sections should appear in -membedded-pic |
|
|
code. */ |
|
|
*errmsg = _("reloc against unsupported section"); |
|
|
bfd_set_error (bfd_error_bad_value); |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
if ((int_rel.r_offset & 3) != 0) |
|
|
{ |
|
|
*errmsg = _("reloc not properly aligned"); |
|
|
bfd_set_error (bfd_error_bad_value); |
|
|
return false; |
|
|
} |
|
|
|
|
|
bfd_put_32 (abfd, |
|
|
(int_rel.r_vaddr - datasec->vma + datasec->output_offset |
|
|
+ (text_relative ? 0 : 1)), |
|
|
p); |
|
|
} |
|
|
|
|
|
return true; |
|
1279 |
} |
} |
1280 |
|
|
1281 |
/* This is the ECOFF backend structure. The backend field of the |
/* This is the ECOFF backend structure. The backend field of the |
1294 |
(unsigned (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* reloc_out */ |
(unsigned (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* reloc_out */ |
1295 |
mips_ecoff_swap_filehdr_out, mips_ecoff_swap_aouthdr_out, |
mips_ecoff_swap_filehdr_out, mips_ecoff_swap_aouthdr_out, |
1296 |
mips_ecoff_swap_scnhdr_out, |
mips_ecoff_swap_scnhdr_out, |
1297 |
FILHSZ, AOUTSZ, SCNHSZ, 0, 0, 0, 0, FILNMLEN, true, false, 4, false, 2, |
FILHSZ, AOUTSZ, SCNHSZ, 0, 0, 0, 0, FILNMLEN, TRUE, FALSE, 4, FALSE, 2, |
1298 |
mips_ecoff_swap_filehdr_in, mips_ecoff_swap_aouthdr_in, |
mips_ecoff_swap_filehdr_in, mips_ecoff_swap_aouthdr_in, |
1299 |
mips_ecoff_swap_scnhdr_in, NULL, |
mips_ecoff_swap_scnhdr_in, NULL, |
1300 |
mips_ecoff_bad_format_hook, _bfd_ecoff_set_arch_mach_hook, |
mips_ecoff_bad_format_hook, _bfd_ecoff_set_arch_mach_hook, |
1310 |
/* The page boundary used to align sections in a demand-paged |
/* The page boundary used to align sections in a demand-paged |
1311 |
executable file. E.g., 0x1000. */ |
executable file. E.g., 0x1000. */ |
1312 |
0x1000, |
0x1000, |
1313 |
/* True if the .rdata section is part of the text segment, as on the |
/* TRUE if the .rdata section is part of the text segment, as on the |
1314 |
Alpha. False if .rdata is part of the data segment, as on the |
Alpha. FALSE if .rdata is part of the data segment, as on the |
1315 |
MIPS. */ |
MIPS. */ |
1316 |
false, |
FALSE, |
1317 |
/* Bitsize of constructor entries. */ |
/* Bitsize of constructor entries. */ |
1318 |
32, |
32, |
1319 |
/* Reloc to use for constructor entries. */ |
/* Reloc to use for constructor entries. */ |
1385 |
_bfd_generic_get_section_contents_in_window |
_bfd_generic_get_section_contents_in_window |
1386 |
|
|
1387 |
/* Relaxing sections is MIPS specific. */ |
/* Relaxing sections is MIPS specific. */ |
1388 |
#define _bfd_ecoff_bfd_relax_section mips_relax_section |
#define _bfd_ecoff_bfd_relax_section bfd_generic_relax_section |
1389 |
|
|
1390 |
/* GC of sections is not done. */ |
/* GC of sections is not done. */ |
1391 |
#define _bfd_ecoff_bfd_gc_sections bfd_generic_gc_sections |
#define _bfd_ecoff_bfd_gc_sections bfd_generic_gc_sections |
1393 |
/* Merging of sections is not done. */ |
/* Merging of sections is not done. */ |
1394 |
#define _bfd_ecoff_bfd_merge_sections bfd_generic_merge_sections |
#define _bfd_ecoff_bfd_merge_sections bfd_generic_merge_sections |
1395 |
|
|
1396 |
|
#define _bfd_ecoff_bfd_is_group_section bfd_generic_is_group_section |
1397 |
#define _bfd_ecoff_bfd_discard_group bfd_generic_discard_group |
#define _bfd_ecoff_bfd_discard_group bfd_generic_discard_group |
1398 |
|
#define _bfd_ecoff_section_already_linked \ |
1399 |
|
_bfd_generic_section_already_linked |
1400 |
|
|
1401 |
extern const bfd_target ecoff_big_vec; |
extern const bfd_target ecoff_big_vec; |
1402 |
|
|