2004-10-21 H.J. Lu <hongjiu.lu@intel.com>
[deliverable/binutils-gdb.git] / bfd / nlm32-i386.c
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
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
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
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
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
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"
This page took 0.04142 seconds and 4 git commands to generate.