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