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, §ion, &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" 452