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