Commit | Line | Data |
---|---|---|
252b5132 | 1 | /* Support for 32-bit i386 NLM (NetWare Loadable Module) |
0f867abe AM |
2 | Copyright 1993, 1994, 2000, 2001, 2002, 2003 |
3 | Free Software Foundation, Inc. | |
252b5132 RH |
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 | ||
b34976b6 | 32 | static bfd_boolean nlm_i386_read_reloc |
252b5132 | 33 | PARAMS ((bfd *, nlmNAME(symbol_type) *, asection **, arelent *)); |
b34976b6 | 34 | static bfd_boolean nlm_i386_write_import |
252b5132 | 35 | PARAMS ((bfd *, asection *, arelent *)); |
b34976b6 | 36 | static bfd_boolean nlm_i386_mangle_relocs |
0f867abe | 37 | PARAMS ((bfd *, asection *, const PTR, bfd_vma, bfd_size_type)); |
b34976b6 | 38 | static bfd_boolean nlm_i386_read_import |
252b5132 | 39 | PARAMS ((bfd *, nlmNAME(symbol_type) *)); |
b34976b6 | 40 | static bfd_boolean nlm_i386_write_external |
252b5132 RH |
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 */ | |
b34976b6 | 50 | FALSE, /* pc_relative */ |
252b5132 RH |
51 | 0, /* bitpos */ |
52 | complain_overflow_bitfield, /* complain_on_overflow */ | |
53 | 0, /* special_function */ | |
54 | "32", /* name */ | |
b34976b6 | 55 | TRUE, /* partial_inplace */ |
252b5132 RH |
56 | 0xffffffff, /* src_mask */ |
57 | 0xffffffff, /* dst_mask */ | |
b34976b6 | 58 | FALSE); /* pcrel_offset */ |
252b5132 RH |
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 */ | |
b34976b6 | 67 | TRUE, /* pc_relative */ |
252b5132 RH |
68 | 0, /* bitpos */ |
69 | complain_overflow_signed, /* complain_on_overflow */ | |
70 | 0, /* special_function */ | |
71 | "DISP32", /* name */ | |
b34976b6 | 72 | TRUE, /* partial_inplace */ |
252b5132 RH |
73 | 0xffffffff, /* src_mask */ |
74 | 0xffffffff, /* dst_mask */ | |
b34976b6 | 75 | TRUE); /* pcrel_offset */ |
252b5132 RH |
76 | |
77 | /* Read a NetWare i386 reloc. */ | |
78 | ||
b34976b6 | 79 | static bfd_boolean |
252b5132 RH |
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 | ||
dc810e39 | 90 | if (bfd_bread (temp, (bfd_size_type) sizeof (temp), abfd) != sizeof (temp)) |
b34976b6 | 91 | return FALSE; |
252b5132 RH |
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 | ||
b34976b6 | 149 | return TRUE; |
252b5132 RH |
150 | } |
151 | ||
152 | /* Write a NetWare i386 reloc. */ | |
153 | ||
b34976b6 | 154 | static bfd_boolean |
252b5132 RH |
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); | |
b34976b6 | 178 | return FALSE; |
252b5132 RH |
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); | |
b34976b6 | 206 | return FALSE; |
252b5132 RH |
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); | |
b34976b6 | 226 | return FALSE; |
252b5132 RH |
227 | } |
228 | } | |
229 | } | |
1518639e | 230 | |
252b5132 | 231 | bfd_put_32 (abfd, val, temp); |
dc810e39 | 232 | if (bfd_bwrite (temp, (bfd_size_type) sizeof (temp), abfd) != sizeof (temp)) |
b34976b6 | 233 | return FALSE; |
252b5132 | 234 | |
b34976b6 | 235 | return TRUE; |
252b5132 RH |
236 | } |
237 | ||
08da05b0 | 238 | /* I want to be able to use objcopy to turn an i386 a.out or COFF file |
252b5132 RH |
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 | ||
b34976b6 | 247 | static bfd_boolean |
252b5132 RH |
248 | nlm_i386_mangle_relocs (abfd, sec, data, offset, count) |
249 | bfd *abfd; | |
250 | asection *sec; | |
0f867abe | 251 | const PTR data; |
252b5132 RH |
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 | ||
b34976b6 | 339 | return TRUE; |
252b5132 RH |
340 | } |
341 | ||
342 | /* Read a NetWare i386 import record */ | |
b34976b6 | 343 | static bfd_boolean |
252b5132 RH |
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 | ||
dc810e39 | 354 | if (bfd_bread ((PTR) &symlength, (bfd_size_type) sizeof (symlength), abfd) |
252b5132 | 355 | != sizeof (symlength)) |
b34976b6 | 356 | return FALSE; |
252b5132 | 357 | sym -> symbol.the_bfd = abfd; |
dc810e39 | 358 | name = bfd_alloc (abfd, (bfd_size_type) symlength + 1); |
252b5132 | 359 | if (name == NULL) |
b34976b6 | 360 | return FALSE; |
dc810e39 | 361 | if (bfd_bread (name, (bfd_size_type) symlength, abfd) != symlength) |
b34976b6 | 362 | return FALSE; |
252b5132 RH |
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; | |
dc810e39 AM |
368 | if (bfd_bread ((PTR) temp, (bfd_size_type) sizeof (temp), abfd) |
369 | != sizeof (temp)) | |
b34976b6 | 370 | return FALSE; |
dc810e39 | 371 | rcount = H_GET_32 (abfd, temp); |
252b5132 RH |
372 | nlm_relocs = ((struct nlm_relent *) |
373 | bfd_alloc (abfd, rcount * sizeof (struct nlm_relent))); | |
374 | if (!nlm_relocs) | |
b34976b6 | 375 | return FALSE; |
252b5132 RH |
376 | sym -> relocs = nlm_relocs; |
377 | sym -> rcnt = 0; | |
378 | while (sym -> rcnt < rcount) | |
379 | { | |
380 | asection *section; | |
1518639e | 381 | |
82e51918 | 382 | if (! nlm_i386_read_reloc (abfd, sym, §ion, &nlm_relocs -> reloc)) |
b34976b6 | 383 | return FALSE; |
252b5132 RH |
384 | nlm_relocs -> section = section; |
385 | nlm_relocs++; | |
386 | sym -> rcnt++; | |
387 | } | |
b34976b6 | 388 | return TRUE; |
252b5132 RH |
389 | } |
390 | ||
391 | /* Write out an external reference. */ | |
392 | ||
b34976b6 | 393 | static bfd_boolean |
252b5132 RH |
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); | |
dc810e39 AM |
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) | |
b34976b6 | 408 | return FALSE; |
252b5132 RH |
409 | |
410 | bfd_put_32 (abfd, count, temp); | |
dc810e39 | 411 | if (bfd_bwrite (temp, (bfd_size_type) sizeof (temp), abfd) != sizeof (temp)) |
b34976b6 | 412 | return FALSE; |
252b5132 RH |
413 | |
414 | for (i = 0; i < count; i++) | |
415 | { | |
82e51918 | 416 | if (! nlm_i386_write_import (abfd, relocs[i].sec, relocs[i].rel)) |
b34976b6 | 417 | return FALSE; |
252b5132 RH |
418 | } |
419 | ||
b34976b6 | 420 | return TRUE; |
252b5132 RH |
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, | |
b34976b6 | 432 | FALSE, |
252b5132 RH |
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" |