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