Home | History | Annotate | Download | only in bfd
      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, &section, &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