These changes cut the size of libbfd.a on a Sun4 by about 11%.
[external/binutils.git] / bfd / reloc16.c
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                         sac@cygnus.com 
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