xref: /openbsd-src/gnu/usr.bin/binutils/bfd/nlm32-i386.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
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"
452