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