2004-04-02 Andrew Cagney <cagney@redhat.com>
[binutils.git] / bfd / nlm32-i386.c
blobd084d18dc7c3b253abed9494ced1e34e90db08b1
1 /* Support for 32-bit i386 NLM (NetWare Loadable Module)
2 Copyright 1993, 1994, 2000, 2001, 2002, 2003
3 Free Software Foundation, Inc.
5 This file is part of BFD, the Binary File Descriptor library.
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.
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.
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. */
21 #include "bfd.h"
22 #include "sysdep.h"
23 #include "libbfd.h"
25 #define ARCH_SIZE 32
27 #include "nlm/i386-ext.h"
28 #define Nlm_External_Fixed_Header Nlm32_i386_External_Fixed_Header
30 #include "libnlm.h"
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 *));
43 /* Adjust the reloc location by an absolute value. */
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 */
60 /* Adjust the reloc location by a PC relative displacement. */
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 */
77 /* Read a NetWare i386 reloc. */
79 static bfd_boolean
80 nlm_i386_read_reloc (abfd, sym, secp, rel)
81 bfd *abfd;
82 nlmNAME(symbol_type) *sym;
83 asection **secp;
84 arelent *rel;
86 bfd_byte temp[4];
87 bfd_vma val;
88 const char *name;
90 if (bfd_bread (temp, (bfd_size_type) sizeof (temp), abfd) != sizeof (temp))
91 return FALSE;
93 val = bfd_get_32 (abfd, temp);
95 /* The value is an offset into either the code or data segment.
96 This is the location which needs to be adjusted.
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.
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.
111 All this translates fairly easily into a BFD reloc. */
113 if (sym == NULL)
115 if ((val & NLM_HIBIT) == 0)
116 name = NLM_INITIALIZED_DATA_NAME;
117 else
119 name = NLM_CODE_NAME;
120 val &=~ NLM_HIBIT;
122 rel->sym_ptr_ptr = bfd_get_section_by_name (abfd, name)->symbol_ptr_ptr;
123 rel->howto = &nlm_i386_abs_howto;
125 else
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
133 rel->howto = &nlm_i386_abs_howto;
134 val &=~ NLM_HIBIT;
138 if ((val & (NLM_HIBIT >> 1)) == 0)
139 *secp = bfd_get_section_by_name (abfd, NLM_INITIALIZED_DATA_NAME);
140 else
142 *secp = bfd_get_section_by_name (abfd, NLM_CODE_NAME);
143 val &=~ (NLM_HIBIT >> 1);
146 rel->address = val;
147 rel->addend = 0;
149 return TRUE;
152 /* Write a NetWare i386 reloc. */
154 static bfd_boolean
155 nlm_i386_write_import (abfd, sec, rel)
156 bfd *abfd;
157 asection *sec;
158 arelent *rel;
160 asymbol *sym;
161 bfd_vma val;
162 bfd_byte temp[4];
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)
177 bfd_set_error (bfd_error_invalid_operation);
178 return FALSE;
181 sym = *rel->sym_ptr_ptr;
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;
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)
194 val -= nlm_get_text_low (abfd);
195 val |= NLM_HIBIT >> 1;
197 else
198 val -= nlm_get_data_low (abfd);
200 if (! bfd_is_und_section (bfd_get_section (sym)))
202 /* NetWare only supports absolute internal relocs. */
203 if (rel->howto->pc_relative)
205 bfd_set_error (bfd_error_invalid_operation);
206 return FALSE;
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;
214 else
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
222 /* PC relative relocs on NetWare must be pcrel_offset. */
223 if (! rel->howto->pcrel_offset)
225 bfd_set_error (bfd_error_invalid_operation);
226 return FALSE;
231 bfd_put_32 (abfd, val, temp);
232 if (bfd_bwrite (temp, (bfd_size_type) sizeof (temp), abfd) != sizeof (temp))
233 return FALSE;
235 return TRUE;
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.
244 This is actually a fairly general concept. However, this is not a
245 general implementation. */
247 static bfd_boolean
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;
255 arelent **rel_ptr_ptr, **rel_end;
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++)
261 arelent *rel;
262 asymbol *sym;
263 bfd_vma addend;
265 rel = *rel_ptr_ptr;
266 sym = *rel->sym_ptr_ptr;
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. */
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;
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;
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;
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)
302 bfd_vma val;
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;
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)
326 bfd_vma val;
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);
335 rel->howto = &nlm_i386_pcrel_howto;
339 return TRUE;
342 /* Read a NetWare i386 import record */
343 static bfd_boolean
344 nlm_i386_read_import (abfd, sym)
345 bfd *abfd;
346 nlmNAME(symbol_type) *sym;
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;
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)
380 asection *section;
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++;
388 return TRUE;
391 /* Write out an external reference. */
393 static bfd_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;
400 unsigned int i;
401 bfd_byte len;
402 unsigned char temp[NLM_TARGET_LONG_SIZE];
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;
410 bfd_put_32 (abfd, count, temp);
411 if (bfd_bwrite (temp, (bfd_size_type) sizeof (temp), abfd) != sizeof (temp))
412 return FALSE;
414 for (i = 0; i < count; i++)
416 if (! nlm_i386_write_import (abfd, relocs[i].sec, relocs[i].rel))
417 return FALSE;
420 return TRUE;
423 #include "nlmswap.h"
425 static const struct nlm_backend_data nlm32_i386_backend =
427 "NetWare Loadable Module\032",
428 sizeof (Nlm32_i386_External_Fixed_Header),
429 0, /* optional_prefix_size */
430 bfd_arch_i386,
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 */
447 #define TARGET_LITTLE_NAME "nlm32-i386"
448 #define TARGET_LITTLE_SYM nlmNAME(i386_vec)
449 #define TARGET_BACKEND_DATA &nlm32_i386_backend
451 #include "nlm-target.h"