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