This commit was generated by cvs2svn to track changes on a CVS vendor
[platform/upstream/binutils.git] / bfd / nlm32-i386.c
1 /* Support for 32-bit i386 NLM (NetWare Loadable Module)
2    Copyright (C) 1993 Free Software Foundation, Inc.
3
4 This file is part of BFD, the Binary File Descriptor library.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19
20 #include "bfd.h"
21 #include "sysdep.h"
22 #include "libbfd.h"
23
24 #define ARCH_SIZE 32
25
26 #include "nlm/i386-ext.h"
27 #define Nlm_External_Fixed_Header       Nlm32_i386_External_Fixed_Header
28
29 #include "libnlm.h"
30
31 static boolean nlm_i386_read_reloc
32   PARAMS ((bfd *, nlmNAME(symbol_type) *, asection **, arelent *));
33 static boolean nlm_i386_write_import
34   PARAMS ((bfd *, asection *, arelent *));
35 static boolean nlm_i386_mangle_relocs
36   PARAMS ((bfd *, asection *, PTR, bfd_vma, bfd_size_type));
37 static boolean nlm_i386_read_import
38   PARAMS ((bfd *, nlmNAME(symbol_type) *));
39 static boolean nlm_i386_write_external
40   PARAMS ((bfd *, bfd_size_type, asymbol *, struct reloc_and_sec *));
41
42 /* Adjust the reloc location by an absolute value.  */
43
44 static reloc_howto_type nlm_i386_abs_howto =
45   HOWTO (0,                     /* type */
46          0,                     /* rightshift */
47          2,                     /* size (0 = byte, 1 = short, 2 = long) */
48          32,                    /* bitsize */
49          false,                 /* pc_relative */
50          0,                     /* bitpos */
51          complain_overflow_bitfield, /* complain_on_overflow */
52          0,                     /* special_function */
53          "32",                  /* name */
54          true,                  /* partial_inplace */
55          0xffffffff,            /* src_mask */
56          0xffffffff,            /* dst_mask */
57          false);                /* pcrel_offset */
58
59 /* Adjust the reloc location by a PC relative displacement.  */
60
61 static reloc_howto_type nlm_i386_pcrel_howto =
62   HOWTO (1,                     /* type */
63          0,                     /* rightshift */
64          2,                     /* size (0 = byte, 1 = short, 2 = long) */
65          32,                    /* bitsize */
66          true,                  /* pc_relative */
67          0,                     /* bitpos */
68          complain_overflow_signed, /* complain_on_overflow */
69          0,                     /* special_function */
70          "DISP32",              /* name */
71          true,                  /* partial_inplace */
72          0xffffffff,            /* src_mask */
73          0xffffffff,            /* dst_mask */
74          true);                 /* pcrel_offset */
75
76 /* Read a NetWare i386 reloc.  */
77
78 static boolean
79 nlm_i386_read_reloc (abfd, sym, secp, rel)
80      bfd *abfd;
81      nlmNAME(symbol_type) *sym;
82      asection **secp;
83      arelent *rel;
84 {
85   bfd_byte temp[4];
86   bfd_vma val;
87   const char *name;
88
89   if (bfd_read (temp, sizeof (temp), 1, abfd) != sizeof (temp))
90     return false;
91
92   val = bfd_get_32 (abfd, temp);
93
94   /* The value is an offset into either the code or data segment.
95      This is the location which needs to be adjusted.
96
97      If this is a relocation fixup rather than an imported symbol (the
98      sym argument is NULL) then the high bit is 0 if the location
99      needs to be adjusted by the address of the data segment, or 1 if
100      the location needs to be adjusted by the address of the code
101      segment.  If this is an imported symbol, then the high bit is 0
102      if the location is 0 if the location should be adjusted by the
103      offset to the symbol, or 1 if the location should adjusted by the
104      absolute value of the symbol.
105
106      The second most significant bit is 0 if the value is an offset
107      into the data segment, or 1 if the value is an offset into the
108      code segment.
109
110      All this translates fairly easily into a BFD reloc.  */
111
112   if (sym == NULL)
113     {
114       if ((val & NLM_HIBIT) == 0)
115         name = NLM_INITIALIZED_DATA_NAME;
116       else
117         {
118           name = NLM_CODE_NAME;
119           val &=~ NLM_HIBIT;
120         }
121       rel->sym_ptr_ptr = bfd_get_section_by_name (abfd, name)->symbol_ptr_ptr;
122       rel->howto = &nlm_i386_abs_howto;
123     }
124   else
125     {
126       /* In this case we do not need to set the sym_ptr_ptr field.  */
127       rel->sym_ptr_ptr = NULL;
128       if ((val & NLM_HIBIT) == 0)
129         rel->howto = &nlm_i386_pcrel_howto;
130       else
131         {
132           rel->howto = &nlm_i386_abs_howto;
133           val &=~ NLM_HIBIT;
134         }
135     }
136
137   if ((val & (NLM_HIBIT >> 1)) == 0)
138     *secp = bfd_get_section_by_name (abfd, NLM_INITIALIZED_DATA_NAME);
139   else
140     {
141       *secp = bfd_get_section_by_name (abfd, NLM_CODE_NAME);
142       val &=~ (NLM_HIBIT >> 1);
143     }
144
145   rel->address = val;
146   rel->addend = 0;
147
148   return true;
149 }
150
151 /* Write a NetWare i386 reloc.  */
152
153 static boolean
154 nlm_i386_write_import (abfd, sec, rel)
155      bfd *abfd;
156      asection *sec;
157      arelent *rel;
158 {
159   asymbol *sym;
160   bfd_vma val;
161   bfd_byte temp[4];
162
163   /* NetWare only supports two kinds of relocs.  We should check
164      special_function here, as well, but at the moment coff-i386
165      relocs uses a special_function which does not affect what we do
166      here.  */
167   if (rel->addend != 0
168       || rel->howto == NULL
169       || rel->howto->rightshift != 0
170       || rel->howto->size != 2
171       || rel->howto->bitsize != 32
172       || rel->howto->bitpos != 0
173       || rel->howto->src_mask != 0xffffffff
174       || rel->howto->dst_mask != 0xffffffff)
175     {
176       bfd_set_error (bfd_error_invalid_operation);
177       return false;
178     }
179
180   sym = *rel->sym_ptr_ptr;
181
182   /* The value we write out is the offset into the appropriate
183      segment.  This offset is the section vma, adjusted by the vma of
184      the lowest section in that segment, plus the address of the
185      relocation.  */
186   val = bfd_get_section_vma (abfd, sec) + rel->address;
187
188   /* The second most significant bit is 0 if the value is an offset
189      into the data segment, or 1 if the value is an offset into the
190      code segment.  */
191   if (bfd_get_section_flags (abfd, sec) & SEC_CODE)
192     {
193       val -= nlm_get_text_low (abfd);
194       val |= NLM_HIBIT >> 1;
195     }
196   else
197     val -= nlm_get_data_low (abfd);
198
199   if (! bfd_is_und_section (bfd_get_section (sym)))
200     {
201       /* NetWare only supports absolute internal relocs.  */
202       if (rel->howto->pc_relative)
203         {
204           bfd_set_error (bfd_error_invalid_operation);
205           return false;
206         }
207
208       /* The high bit is 1 if the reloc is against the code section, 0
209          if against the data section.  */
210       if (bfd_get_section_flags (abfd, bfd_get_section (sym)) & SEC_CODE)
211         val |= NLM_HIBIT;
212     }
213   else
214     {
215       /* The high bit is 1 if this is an absolute reloc, 0 if it is PC
216          relative.  */
217       if (! rel->howto->pc_relative)
218         val |= NLM_HIBIT;
219       else
220         {
221           /* PC relative relocs on NetWare must be pcrel_offset.  */
222           if (! rel->howto->pcrel_offset)
223             {
224               bfd_set_error (bfd_error_invalid_operation);
225               return false;
226             }
227         }
228     }
229   
230   bfd_put_32 (abfd, val, temp);
231   if (bfd_write (temp, sizeof (temp), 1, abfd) != sizeof (temp))
232     return false;
233
234   return true;
235 }
236
237 /* I want to be able to use objcopy to turn a i386 a.out or COFF file
238    into a NetWare i386 module.  That means that the relocs from the
239    source file have to be mapped into relocs that apply to the target
240    file.  This function is called by nlm_set_section_contents to give
241    it a chance to rework the relocs.
242
243    This is actually a fairly general concept.  However, this is not a
244    general implementation.  */
245
246 static boolean
247 nlm_i386_mangle_relocs (abfd, sec, data, offset, count)
248      bfd *abfd;
249      asection *sec;
250      PTR data;
251      bfd_vma offset;
252      bfd_size_type count;
253 {
254   arelent **rel_ptr_ptr, **rel_end;
255
256   rel_ptr_ptr = sec->orelocation;
257   rel_end = rel_ptr_ptr + sec->reloc_count;
258   for (; rel_ptr_ptr < rel_end; rel_ptr_ptr++)
259     {
260       arelent *rel;
261       asymbol *sym;
262       bfd_vma addend;
263
264       rel = *rel_ptr_ptr;
265       sym = *rel->sym_ptr_ptr;
266
267       /* Note that no serious harm will ensue if we fail to change a
268          reloc.  We will wind up failing in nlm_i386_write_import.  */
269
270       /* Make sure this reloc is within the data we have.  We only 4
271          byte relocs here, so we insist on having 4 bytes.  */
272       if (rel->address < offset
273           || rel->address + 4 > offset + count)
274         continue;
275
276       /* NetWare doesn't support reloc addends, so we get rid of them
277          here by simply adding them into the object data.  We handle
278          the symbol value, if any, the same way.  */
279       addend = rel->addend + sym->value;
280
281       /* The value of a symbol is the offset into the section.  If the
282          symbol is in the .bss segment, we need to include the size of
283          the data segment in the offset as well.  Fortunately, we know
284          that at this point the size of the data section is in the NLM
285          header.  */
286       if (((bfd_get_section_flags (abfd, bfd_get_section (sym))
287             & SEC_LOAD) == 0)
288           && ((bfd_get_section_flags (abfd, bfd_get_section (sym))
289                & SEC_ALLOC) != 0))
290         addend += nlm_fixed_header (abfd)->dataImageSize;
291
292       if (addend != 0
293           && rel->howto != NULL
294           && rel->howto->rightshift == 0
295           && rel->howto->size == 2
296           && rel->howto->bitsize == 32
297           && rel->howto->bitpos == 0
298           && rel->howto->src_mask == 0xffffffff
299           && rel->howto->dst_mask == 0xffffffff)
300         {
301           bfd_vma val;
302
303           val = bfd_get_32 (abfd, (bfd_byte *) data + rel->address - offset);
304           val += addend;
305           bfd_put_32 (abfd, val, (bfd_byte *) data + rel->address - offset);
306           rel->addend = 0;
307         }
308
309       /* NetWare uses a reloc with pcrel_offset set.  We adjust
310          pc_relative relocs accordingly.  We are going to change the
311          howto field, so we can only do this if the current one is
312          compatible.  We should check special_function here, but at
313          the moment coff-i386 uses a special_function which does not
314          affect what we are doing here.  */
315       if (rel->howto != NULL
316           && rel->howto->pc_relative
317           && ! rel->howto->pcrel_offset
318           && rel->howto->rightshift == 0
319           && rel->howto->size == 2
320           && rel->howto->bitsize == 32
321           && rel->howto->bitpos == 0
322           && rel->howto->src_mask == 0xffffffff
323           && rel->howto->dst_mask == 0xffffffff)
324         {
325           bfd_vma val;
326
327           /* When pcrel_offset is not set, it means that the negative
328              of the address of the memory location is stored in the
329              memory location.  We must add it back in.  */
330           val = bfd_get_32 (abfd, (bfd_byte *) data + rel->address - offset);
331           val += rel->address;
332           bfd_put_32 (abfd, val, (bfd_byte *) data + rel->address - offset);
333
334           rel->howto = &nlm_i386_pcrel_howto;
335         }
336     }
337
338   return true;
339 }
340
341 /* Read a NetWare i386 import record */
342 static boolean
343 nlm_i386_read_import (abfd, sym)
344      bfd *abfd;
345      nlmNAME(symbol_type) *sym;
346 {
347   struct nlm_relent *nlm_relocs;        /* relocation records for symbol */
348   bfd_size_type rcount;                 /* number of relocs */
349   bfd_byte temp[NLM_TARGET_LONG_SIZE];  /* temporary 32-bit value */
350   unsigned char symlength;              /* length of symbol name */
351   char *name;
352
353   if (bfd_read ((PTR) &symlength, sizeof (symlength), 1, abfd)
354       != sizeof (symlength))
355     return false;
356   sym -> symbol.the_bfd = abfd;
357   name = bfd_alloc (abfd, symlength + 1);
358   if (name == NULL)
359     return false;
360   if (bfd_read (name, symlength, 1, abfd) != symlength)
361     return false;
362   name[symlength] = '\0';
363   sym -> symbol.name = name;
364   sym -> symbol.flags = 0;
365   sym -> symbol.value = 0;
366   sym -> symbol.section = bfd_und_section_ptr;
367   if (bfd_read ((PTR) temp, sizeof (temp), 1, abfd) != sizeof (temp))
368     return false;
369   rcount = bfd_h_get_32 (abfd, temp);
370   nlm_relocs = ((struct nlm_relent *)
371                 bfd_alloc (abfd, rcount * sizeof (struct nlm_relent)));
372   if (!nlm_relocs)
373     return false;
374   sym -> relocs = nlm_relocs;
375   sym -> rcnt = 0;
376   while (sym -> rcnt < rcount)
377     {
378       asection *section;
379       
380       if (nlm_i386_read_reloc (abfd, sym, &section,
381                                &nlm_relocs -> reloc)
382           == false)
383         return false;
384       nlm_relocs -> section = section;
385       nlm_relocs++;
386       sym -> rcnt++;
387     }
388   return true;
389 }
390
391 /* Write out an external reference.  */
392
393 static boolean
394 nlm_i386_write_external (abfd, count, sym, relocs)
395      bfd *abfd;
396      bfd_size_type count;
397      asymbol *sym;
398      struct reloc_and_sec *relocs;
399 {
400   unsigned int i;
401   bfd_byte len;
402   unsigned char temp[NLM_TARGET_LONG_SIZE];
403
404   len = strlen (sym->name);
405   if ((bfd_write (&len, sizeof (bfd_byte), 1, abfd) != sizeof(bfd_byte))
406       || bfd_write (sym->name, len, 1, abfd) != len)
407     return false;
408
409   bfd_put_32 (abfd, count, temp);
410   if (bfd_write (temp, sizeof(temp), 1, abfd) != sizeof (temp))
411     return false;
412
413   for (i = 0; i < count; i++)
414     {
415       if (nlm_i386_write_import (abfd, relocs[i].sec,
416                                  relocs[i].rel) == false)
417         return false;
418     }
419
420   return true;
421 }
422
423 #include "nlmswap.h"
424
425 static const struct nlm_backend_data nlm32_i386_backend =
426 {
427   "NetWare Loadable Module\032",
428   sizeof (Nlm32_i386_External_Fixed_Header),
429   0,    /* optional_prefix_size */
430   bfd_arch_i386,
431   0,
432   false,
433   0,    /* backend_object_p */
434   0,    /* write_prefix_func */
435   nlm_i386_read_reloc,
436   nlm_i386_mangle_relocs,
437   nlm_i386_read_import,
438   nlm_i386_write_import,
439   0,    /* set_public_section */
440   0,    /* get_public_offset */
441   nlm_swap_fixed_header_in,
442   nlm_swap_fixed_header_out,
443   nlm_i386_write_external,
444   0,    /* write_export */
445 };
446
447 #define TARGET_LITTLE_NAME              "nlm32-i386"
448 #define TARGET_LITTLE_SYM               nlmNAME(i386_vec)
449 #define TARGET_BACKEND_DATA             &nlm32_i386_backend
450
451 #include "nlm-target.h"