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