]>
Commit | Line | Data |
---|---|---|
075caafd ILT |
1 | /* 8 and 16 bit COFF relocation functions, for BFD. |
2 | Copyright 1990, 1991, 1992 Free Software Foundation, Inc. | |
3 | Written by Cygnus Support. | |
4 | ||
5 | This file is part of BFD, the Binary File Descriptor library. | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 2 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program; if not, write to the Free Software | |
19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
20 | ||
21 | /* | |
22 | Most of this hacked by Steve Chamberlain, | |
23 | [email protected] | |
24 | */ | |
25 | ||
26 | /* These routines are used by coff-h8300 and coff-z8k to do | |
27 | relocation. */ | |
28 | ||
29 | #include "bfd.h" | |
30 | #include "sysdep.h" | |
31 | #include "libbfd.h" | |
32 | #include "seclet.h" | |
33 | #include "obstack.h" | |
34 | #include "coff/internal.h" | |
35 | #include "libcoff.h" | |
36 | ||
37 | extern bfd_error_vector_type bfd_error_vector; | |
38 | ||
39 | bfd_vma | |
40 | DEFUN(bfd_coff_reloc16_get_value,(reloc, seclet), | |
41 | arelent *reloc AND | |
42 | bfd_seclet_type *seclet) | |
43 | { | |
44 | bfd_vma value; | |
45 | asymbol *symbol = *(reloc->sym_ptr_ptr); | |
46 | /* A symbol holds a pointer to a section, and an offset from the | |
47 | base of the section. To relocate, we find where the section will | |
48 | live in the output and add that in */ | |
49 | ||
50 | if (symbol->section == &bfd_und_section) | |
51 | { | |
52 | /* Ouch, this is an undefined symbol.. */ | |
53 | bfd_error_vector.undefined_symbol(reloc, seclet); | |
54 | value = symbol->value; | |
55 | } | |
56 | else | |
57 | { | |
58 | value = symbol->value + | |
59 | symbol->section->output_offset + | |
60 | symbol->section->output_section->vma; | |
61 | } | |
62 | ||
63 | ||
64 | /* Add the value contained in the relocation */ | |
65 | value += reloc->addend; | |
66 | ||
67 | return value; | |
68 | } | |
69 | ||
70 | static void | |
71 | DEFUN(perform_slip,(s, slip, input_section, value), | |
72 | asymbol **s AND | |
73 | unsigned int slip AND | |
74 | asection *input_section AND | |
75 | bfd_vma value) | |
76 | { | |
77 | ||
78 | /* Find all symbols past this point, and make them know | |
79 | what's happened */ | |
80 | while (*s) | |
81 | { | |
82 | asymbol *p = *s; | |
83 | if (p->section == input_section) | |
84 | { | |
85 | /* This was pointing into this section, so mangle it */ | |
86 | if (p->value > value) | |
87 | { | |
88 | p->value -= slip; | |
89 | } | |
90 | } | |
91 | s++; | |
92 | ||
93 | } | |
94 | } | |
95 | static int | |
96 | DEFUN(movb1,(input_section, symbols, r, shrink), | |
97 | asection *input_section AND | |
98 | asymbol **symbols AND | |
99 | arelent *r AND | |
100 | unsigned int shrink) | |
101 | { | |
102 | bfd_vma value = bfd_coff_reloc16_get_value(r,0); | |
103 | ||
104 | if (value >= 0xff00) | |
105 | { | |
106 | ||
107 | /* Change the reloc type from 16bit, possible 8 to 8bit | |
108 | possible 16 */ | |
109 | r->howto = r->howto + 1; | |
110 | /* The place to relc moves back by one */ | |
111 | r->address -=1; | |
112 | ||
113 | /* This will be two bytes smaller in the long run */ | |
114 | shrink +=2 ; | |
115 | perform_slip(symbols, 2, input_section, r->address - shrink +1); | |
116 | ||
117 | ||
118 | } | |
119 | return shrink; | |
120 | } | |
121 | ||
122 | static int | |
123 | DEFUN(jmp1,(input_section, symbols, r, shrink), | |
124 | asection *input_section AND | |
125 | asymbol **symbols AND | |
126 | arelent *r AND | |
127 | unsigned int shrink) | |
128 | { | |
129 | ||
130 | ||
131 | bfd_vma value = bfd_coff_reloc16_get_value(r, 0); | |
132 | ||
133 | bfd_vma dot = input_section->output_section->vma + | |
134 | input_section->output_offset + r->address; | |
135 | bfd_vma gap; | |
136 | ||
137 | /* See if the address we're looking at within 127 bytes of where | |
138 | we are, if so then we can use a small branch rather than the | |
139 | jump we were going to */ | |
140 | ||
141 | gap = value - (dot - shrink); | |
142 | ||
143 | ||
144 | if (-120 < (long)gap && (long)gap < 120 ) | |
145 | { | |
146 | ||
147 | /* Change the reloc type from 16bit, possible 8 to 8bit | |
148 | possible 16 */ | |
149 | r->howto = r->howto + 1; | |
150 | /* The place to relc moves back by one */ | |
151 | r->address -=1; | |
152 | ||
153 | /* This will be two bytes smaller in the long run */ | |
154 | shrink +=2 ; | |
155 | perform_slip(symbols, 2, input_section, r->address-shrink +1); | |
156 | ||
157 | ||
158 | } | |
159 | return shrink; | |
160 | } | |
161 | ||
162 | boolean | |
163 | DEFUN(bfd_coff_reloc16_relax_section,(abfd, i, symbols), | |
164 | bfd *abfd AND | |
165 | asection *i AND | |
166 | asymbol **symbols) | |
167 | { | |
168 | ||
169 | /* Get enough memory to hold the stuff */ | |
170 | bfd *input_bfd = i->owner; | |
171 | asection *input_section = i; | |
172 | int shrink = 0 ; | |
173 | boolean new = false; | |
174 | ||
175 | bfd_size_type reloc_size = bfd_get_reloc_upper_bound(input_bfd, | |
176 | input_section); | |
177 | arelent **reloc_vector = (arelent **)bfd_xmalloc(reloc_size); | |
178 | ||
179 | /* Get the relocs and think about them */ | |
180 | if (bfd_canonicalize_reloc(input_bfd, | |
181 | input_section, | |
182 | reloc_vector, | |
183 | symbols)) | |
184 | { | |
185 | arelent **parent; | |
186 | for (parent = reloc_vector; *parent; parent++) | |
187 | { | |
188 | arelent *r = *parent; | |
189 | switch (r->howto->type) { | |
190 | case R_MOVB2: | |
191 | case R_JMP2: | |
192 | ||
193 | shrink+=2; | |
194 | break; | |
195 | ||
196 | case R_MOVB1: | |
197 | shrink = movb1(input_section, symbols, r, shrink); | |
198 | new = true; | |
199 | ||
200 | break; | |
201 | case R_JMP1: | |
202 | shrink = jmp1(input_section, symbols, r, shrink); | |
203 | new = true; | |
204 | ||
205 | break; | |
206 | } | |
207 | } | |
208 | ||
209 | } | |
210 | input_section->_cooked_size -= shrink; | |
211 | free((char *)reloc_vector); | |
212 | return new; | |
213 | } | |
214 | ||
215 | bfd_byte * | |
216 | DEFUN(bfd_coff_reloc16_get_relocated_section_contents,(in_abfd, seclet, data), | |
217 | bfd *in_abfd AND | |
218 | bfd_seclet_type *seclet AND | |
219 | bfd_byte *data) | |
220 | ||
221 | { | |
222 | /* Get enough memory to hold the stuff */ | |
223 | bfd *input_bfd = seclet->u.indirect.section->owner; | |
224 | asection *input_section = seclet->u.indirect.section; | |
225 | bfd_size_type reloc_size = bfd_get_reloc_upper_bound(input_bfd, | |
226 | input_section); | |
227 | arelent **reloc_vector = (arelent **)bfd_xmalloc(reloc_size); | |
228 | ||
229 | /* read in the section */ | |
230 | bfd_get_section_contents(input_bfd, | |
231 | input_section, | |
232 | data, | |
233 | 0, | |
234 | input_section->_raw_size); | |
235 | ||
236 | ||
237 | if (bfd_canonicalize_reloc(input_bfd, | |
238 | input_section, | |
239 | reloc_vector, | |
240 | seclet->u.indirect.symbols) ) | |
241 | { | |
242 | arelent **parent = reloc_vector; | |
243 | arelent *reloc ; | |
244 | ||
245 | ||
246 | ||
247 | unsigned int dst_address = 0; | |
248 | unsigned int src_address = 0; | |
249 | unsigned int run; | |
250 | unsigned int idx; | |
251 | ||
252 | /* Find how long a run we can do */ | |
253 | while (dst_address < seclet->size) | |
254 | { | |
255 | ||
256 | reloc = *parent; | |
257 | if (reloc) | |
258 | { | |
259 | /* Note that the relaxing didn't tie up the addresses in the | |
260 | relocation, so we use the original address to work out the | |
261 | run of non-relocated data */ | |
262 | run = reloc->address - src_address; | |
263 | parent++; | |
264 | ||
265 | } | |
266 | else | |
267 | { | |
268 | run = seclet->size - dst_address; | |
269 | } | |
270 | /* Copy the bytes */ | |
271 | for (idx = 0; idx < run; idx++) | |
272 | { | |
273 | data[dst_address++] = data[src_address++]; | |
274 | } | |
275 | ||
276 | /* Now do the relocation */ | |
277 | ||
278 | if (reloc) | |
279 | { | |
280 | switch (reloc->howto->type) | |
281 | { | |
282 | case R_JMP2: | |
283 | /* Speciial relaxed type */ | |
284 | { | |
285 | bfd_vma dot = seclet->offset + dst_address + seclet->u.indirect.section->output_section->vma; | |
286 | int gap = bfd_coff_reloc16_get_value(reloc,seclet)-dot-1; | |
287 | if ((gap & ~0xff ) != 0 &&((gap & 0xff00)!= 0xff00)) abort(); | |
288 | ||
289 | bfd_put_8(in_abfd,gap, data+dst_address); | |
290 | ||
291 | switch (data[dst_address-1]) | |
292 | { | |
293 | ||
294 | case 0x5e: | |
295 | /* jsr -> bsr */ | |
296 | bfd_put_8(in_abfd, 0x55, data+dst_address-1); | |
297 | break; | |
298 | case 0x5a: | |
299 | /* jmp ->bra */ | |
300 | bfd_put_8(in_abfd, 0x40, data+dst_address-1); | |
301 | break; | |
302 | ||
303 | default: | |
304 | abort(); | |
305 | ||
306 | } | |
307 | ||
308 | ||
309 | ||
310 | ||
311 | dst_address++; | |
312 | src_address+=3; | |
313 | ||
314 | break; | |
315 | } | |
316 | ||
317 | ||
318 | case R_MOVB2: | |
319 | /* Special relaxed type, there will be a gap between where we | |
320 | get stuff from and where we put stuff to now | |
321 | ||
322 | for a mov.b @aa:16 -> mov.b @aa:8 | |
323 | opcode 0x6a 0x0y offset | |
324 | -> 0x2y off | |
325 | */ | |
326 | if (data[dst_address-1] != 0x6a) | |
327 | abort(); | |
328 | switch (data[src_address] & 0xf0) | |
329 | { | |
330 | case 0x00: | |
331 | /* Src is memory */ | |
332 | data[dst_address-1] = (data[src_address] & 0xf) | 0x20; | |
333 | break; | |
334 | case 0x80: | |
335 | /* Src is reg */ | |
336 | data[dst_address-1] = (data[src_address] & 0xf) | 0x30; | |
337 | break; | |
338 | default: | |
339 | abort(); | |
340 | } | |
341 | ||
342 | /* the offset must fit ! after all, what was all the relaxing | |
343 | about ? */ | |
344 | ||
345 | bfd_put_8(in_abfd, bfd_coff_reloc16_get_value(reloc, seclet), | |
346 | data + dst_address); | |
347 | ||
348 | /* Note the magic - src goes up by two bytes, but dst by only | |
349 | one */ | |
350 | dst_address+=1; | |
351 | src_address+=3; | |
352 | ||
353 | break; | |
354 | /* PCrel 8 bits */ | |
355 | case R_PCRBYTE: | |
356 | { | |
357 | bfd_vma dot = seclet->offset + dst_address + seclet->u.indirect.section->output_section->vma; | |
358 | int gap = bfd_coff_reloc16_get_value(reloc,seclet)-dot; | |
359 | if (gap > 127 || gap < -128) | |
360 | { | |
361 | bfd_error_vector.reloc_value_truncated(reloc, seclet); | |
362 | } | |
363 | ||
364 | bfd_put_8(in_abfd,gap, data+dst_address); | |
365 | dst_address++; | |
366 | src_address++; | |
367 | ||
368 | break; | |
369 | } | |
370 | ||
371 | case R_RELBYTE: | |
372 | { | |
373 | unsigned int gap =bfd_coff_reloc16_get_value(reloc,seclet); | |
374 | if (gap > 0xff && gap < ~0xff) | |
375 | { | |
376 | bfd_error_vector.reloc_value_truncated(reloc, seclet); | |
377 | } | |
378 | ||
379 | bfd_put_8(in_abfd, gap, data+dst_address); | |
380 | dst_address+=1; | |
381 | src_address+=1; | |
382 | ||
383 | ||
384 | } | |
385 | break; | |
386 | case R_JMP1: | |
387 | /* A relword which would have like to have been a pcrel */ | |
388 | case R_MOVB1: | |
389 | /* A relword which would like to have been modified but | |
390 | didn't make it */ | |
391 | case R_RELWORD: | |
392 | bfd_put_16(in_abfd, bfd_coff_reloc16_get_value(reloc,seclet), | |
393 | data+dst_address); | |
394 | dst_address+=2; | |
395 | src_address+=2; | |
396 | break; | |
397 | default: | |
398 | bfd_coff_reloc16_extra_cases (in_abfd, seclet, reloc, data, | |
399 | &src_address, &dst_address); | |
400 | break; | |
401 | } | |
402 | } | |
403 | } | |
404 | } | |
405 | free((char *)reloc_vector); | |
406 | return data; | |
407 | ||
408 | } | |
409 |