1 /* Support for 32-bit i386 NLM (NetWare Loadable Module) 2 Copyright (C) 1993-2014 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 3 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., 51 Franklin Street - Fifth Floor, Boston, 19 MA 02110-1301, USA. */ 20 21 #include "sysdep.h" 22 #include "bfd.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 /* Adjust the reloc location by an absolute value. */ 33 34 static reloc_howto_type nlm_i386_abs_howto = 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. */ 48 49 /* Adjust the reloc location by a PC relative displacement. */ 50 51 static reloc_howto_type nlm_i386_pcrel_howto = 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. */ 65 66 /* Read a NetWare i386 reloc. */ 67 68 static bfd_boolean 69 nlm_i386_read_reloc (bfd *abfd, 70 nlmNAME (symbol_type) *sym, 71 asection **secp, 72 arelent *rel) 73 { 74 bfd_byte temp[4]; 75 bfd_vma val; 76 const char *name; 77 78 if (bfd_bread (temp, (bfd_size_type) sizeof (temp), abfd) != sizeof (temp)) 79 return FALSE; 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 137 return TRUE; 138 } 139 140 /* Write a NetWare i386 reloc. */ 141 142 static bfd_boolean 143 nlm_i386_write_import (bfd * abfd, asection * sec, arelent * rel) 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); 163 return FALSE; 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); 191 return FALSE; 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); 211 return FALSE; 212 } 213 } 214 } 215 216 bfd_put_32 (abfd, val, temp); 217 if (bfd_bwrite (temp, (bfd_size_type) sizeof (temp), abfd) != sizeof (temp)) 218 return FALSE; 219 220 return TRUE; 221 } 222 223 /* I want to be able to use objcopy to turn an i386 a.out or COFF file 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 232 static bfd_boolean 233 nlm_i386_mangle_relocs (bfd *abfd, 234 asection *sec, 235 const void * data, 236 bfd_vma offset, 237 bfd_size_type count) 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 323 return TRUE; 324 } 325 326 /* Read a NetWare i386 import record. */ 327 328 static bfd_boolean 329 nlm_i386_read_import (bfd * abfd, nlmNAME (symbol_type) * sym) 330 { 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. */ 335 char *name; 336 337 if (bfd_bread (& symlength, (bfd_size_type) sizeof (symlength), abfd) 338 != sizeof (symlength)) 339 return FALSE; 340 sym -> symbol.the_bfd = abfd; 341 name = bfd_alloc (abfd, (bfd_size_type) symlength + 1); 342 if (name == NULL) 343 return FALSE; 344 if (bfd_bread (name, (bfd_size_type) symlength, abfd) != symlength) 345 return FALSE; 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; 351 if (bfd_bread (temp, (bfd_size_type) sizeof (temp), abfd) != sizeof (temp)) 352 return FALSE; 353 rcount = H_GET_32 (abfd, temp); 354 nlm_relocs = bfd_alloc (abfd, rcount * sizeof (struct nlm_relent)); 355 if (!nlm_relocs) 356 return FALSE; 357 sym -> relocs = nlm_relocs; 358 sym -> rcnt = 0; 359 while (sym -> rcnt < rcount) 360 { 361 asection *section; 362 363 if (! nlm_i386_read_reloc (abfd, sym, §ion, &nlm_relocs -> reloc)) 364 return FALSE; 365 nlm_relocs -> section = section; 366 nlm_relocs++; 367 sym -> rcnt++; 368 } 369 return TRUE; 370 } 371 372 /* Write out an external reference. */ 373 374 static bfd_boolean 375 nlm_i386_write_external (bfd *abfd, 376 bfd_size_type count, 377 asymbol *sym, 378 struct reloc_and_sec *relocs) 379 { 380 unsigned int i; 381 bfd_byte len; 382 unsigned char temp[NLM_TARGET_LONG_SIZE]; 383 384 len = strlen (sym->name); 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) 388 return FALSE; 389 390 bfd_put_32 (abfd, count, temp); 391 if (bfd_bwrite (temp, (bfd_size_type) sizeof (temp), abfd) != sizeof (temp)) 392 return FALSE; 393 394 for (i = 0; i < count; i++) 395 if (! nlm_i386_write_import (abfd, relocs[i].sec, relocs[i].rel)) 396 return FALSE; 397 398 return TRUE; 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), 407 0, /* Optional_prefix_size. */ 408 bfd_arch_i386, 409 0, 410 FALSE, 411 0, /* Backend_object_p. */ 412 0, /* Write_prefix_func. */ 413 nlm_i386_read_reloc, 414 nlm_i386_mangle_relocs, 415 nlm_i386_read_import, 416 nlm_i386_write_import, 417 0, /* Set_public_section. */ 418 0, /* Set_public_offset. */ 419 nlm_swap_fixed_header_in, 420 nlm_swap_fixed_header_out, 421 nlm_i386_write_external, 422 0, /* Write_export. */ 423 }; 424 425 #define TARGET_LITTLE_NAME "nlm32-i386" 426 #define TARGET_LITTLE_SYM i386_nlm32_vec 427 #define TARGET_BACKEND_DATA & nlm32_i386_backend 428 429 #include "nlm-target.h" 430