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