Back to index

cell-binutils  2.17cvs20070401
nlm32-i386.c
Go to the documentation of this file.
00001 /* Support for 32-bit i386 NLM (NetWare Loadable Module)
00002    Copyright 1993, 1994, 2000, 2001, 2002, 2003, 2005
00003    Free Software Foundation, Inc.
00004 
00005    This file is part of BFD, the Binary File Descriptor library.
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2 of the License, or
00010    (at your option) any later version.
00011 
00012    This program is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015    GNU General Public License for more details.
00016 
00017    You should have received a copy of the GNU General Public License
00018    along with this program; if not, write to the Free Software
00019    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
00020 
00021 #include "bfd.h"
00022 #include "sysdep.h"
00023 #include "libbfd.h"
00024 
00025 #define ARCH_SIZE 32
00026 
00027 #include "nlm/i386-ext.h"
00028 #define Nlm_External_Fixed_Header  Nlm32_i386_External_Fixed_Header
00029 
00030 #include "libnlm.h"
00031 
00032 /* Adjust the reloc location by an absolute value.  */
00033 
00034 static reloc_howto_type nlm_i386_abs_howto =
00035   HOWTO (0,                 /* Type.  */
00036         0,                  /* Rightshift.  */
00037         2,                  /* Size (0 = byte, 1 = short, 2 = long).  */
00038         32,                 /* Bitsize.  */
00039         FALSE,                     /* PC relative.  */
00040         0,                  /* Bitpos.  */
00041         complain_overflow_bitfield, /* Complain_on_overflow.  */
00042         0,                  /* Special_function.  */
00043         "32",               /* Name.  */
00044         TRUE,               /* Partial_inplace.  */
00045         0xffffffff,         /* Source mask.  */
00046         0xffffffff,         /* Dest mask.  */
00047         FALSE);             /* PR rel_offset.  */
00048 
00049 /* Adjust the reloc location by a PC relative displacement.  */
00050 
00051 static reloc_howto_type nlm_i386_pcrel_howto =
00052   HOWTO (1,                 /* Type.  */
00053         0,                  /* Rightshift.  */
00054         2,                  /* Size (0 = byte, 1 = short, 2 = long).  */
00055         32,                 /* Bitsize.  */
00056         TRUE,               /* PC relative.  */
00057         0,                  /* Bitpos.  */
00058         complain_overflow_signed, /* Complain_on_overflow.  */
00059         0,                  /* Special_function.  */
00060         "DISP32",           /* Name.  */
00061         TRUE,               /* Partial_inplace.  */
00062         0xffffffff,         /* Source mask.  */
00063         0xffffffff,         /* Dest mask.  */
00064         TRUE);                     /* PR rel_offset.  */
00065 
00066 /* Read a NetWare i386 reloc.  */
00067 
00068 static bfd_boolean
00069 nlm_i386_read_reloc (bfd *abfd,
00070                    nlmNAME (symbol_type) *sym,
00071                    asection **secp,
00072                    arelent *rel)
00073 {
00074   bfd_byte temp[4];
00075   bfd_vma val;
00076   const char *name;
00077 
00078   if (bfd_bread (temp, (bfd_size_type) sizeof (temp), abfd) != sizeof (temp))
00079     return FALSE;
00080 
00081   val = bfd_get_32 (abfd, temp);
00082 
00083   /* The value is an offset into either the code or data segment.
00084      This is the location which needs to be adjusted.
00085 
00086      If this is a relocation fixup rather than an imported symbol (the
00087      sym argument is NULL) then the high bit is 0 if the location
00088      needs to be adjusted by the address of the data segment, or 1 if
00089      the location needs to be adjusted by the address of the code
00090      segment.  If this is an imported symbol, then the high bit is 0
00091      if the location is 0 if the location should be adjusted by the
00092      offset to the symbol, or 1 if the location should adjusted by the
00093      absolute value of the symbol.
00094 
00095      The second most significant bit is 0 if the value is an offset
00096      into the data segment, or 1 if the value is an offset into the
00097      code segment.
00098 
00099      All this translates fairly easily into a BFD reloc.  */
00100 
00101   if (sym == NULL)
00102     {
00103       if ((val & NLM_HIBIT) == 0)
00104        name = NLM_INITIALIZED_DATA_NAME;
00105       else
00106        {
00107          name = NLM_CODE_NAME;
00108          val &=~ NLM_HIBIT;
00109        }
00110       rel->sym_ptr_ptr = bfd_get_section_by_name (abfd, name)->symbol_ptr_ptr;
00111       rel->howto = &nlm_i386_abs_howto;
00112     }
00113   else
00114     {
00115       /* In this case we do not need to set the sym_ptr_ptr field.  */
00116       rel->sym_ptr_ptr = NULL;
00117       if ((val & NLM_HIBIT) == 0)
00118        rel->howto = &nlm_i386_pcrel_howto;
00119       else
00120        {
00121          rel->howto = &nlm_i386_abs_howto;
00122          val &=~ NLM_HIBIT;
00123        }
00124     }
00125 
00126   if ((val & (NLM_HIBIT >> 1)) == 0)
00127     *secp = bfd_get_section_by_name (abfd, NLM_INITIALIZED_DATA_NAME);
00128   else
00129     {
00130       *secp = bfd_get_section_by_name (abfd, NLM_CODE_NAME);
00131       val &=~ (NLM_HIBIT >> 1);
00132     }
00133 
00134   rel->address = val;
00135   rel->addend = 0;
00136 
00137   return TRUE;
00138 }
00139 
00140 /* Write a NetWare i386 reloc.  */
00141 
00142 static bfd_boolean
00143 nlm_i386_write_import (bfd * abfd, asection * sec, arelent * rel)
00144 {
00145   asymbol *sym;
00146   bfd_vma val;
00147   bfd_byte temp[4];
00148 
00149   /* NetWare only supports two kinds of relocs.  We should check
00150      special_function here, as well, but at the moment coff-i386
00151      relocs uses a special_function which does not affect what we do
00152      here.  */
00153   if (rel->addend != 0
00154       || rel->howto == NULL
00155       || rel->howto->rightshift != 0
00156       || rel->howto->size != 2
00157       || rel->howto->bitsize != 32
00158       || rel->howto->bitpos != 0
00159       || rel->howto->src_mask != 0xffffffff
00160       || rel->howto->dst_mask != 0xffffffff)
00161     {
00162       bfd_set_error (bfd_error_invalid_operation);
00163       return FALSE;
00164     }
00165 
00166   sym = *rel->sym_ptr_ptr;
00167 
00168   /* The value we write out is the offset into the appropriate
00169      segment.  This offset is the section vma, adjusted by the vma of
00170      the lowest section in that segment, plus the address of the
00171      relocation.  */
00172   val = bfd_get_section_vma (abfd, sec) + rel->address;
00173 
00174   /* The second most significant bit is 0 if the value is an offset
00175      into the data segment, or 1 if the value is an offset into the
00176      code segment.  */
00177   if (bfd_get_section_flags (abfd, sec) & SEC_CODE)
00178     {
00179       val -= nlm_get_text_low (abfd);
00180       val |= NLM_HIBIT >> 1;
00181     }
00182   else
00183     val -= nlm_get_data_low (abfd);
00184 
00185   if (! bfd_is_und_section (bfd_get_section (sym)))
00186     {
00187       /* NetWare only supports absolute internal relocs.  */
00188       if (rel->howto->pc_relative)
00189        {
00190          bfd_set_error (bfd_error_invalid_operation);
00191          return FALSE;
00192        }
00193 
00194       /* The high bit is 1 if the reloc is against the code section, 0
00195         if against the data section.  */
00196       if (bfd_get_section_flags (abfd, bfd_get_section (sym)) & SEC_CODE)
00197        val |= NLM_HIBIT;
00198     }
00199   else
00200     {
00201       /* The high bit is 1 if this is an absolute reloc, 0 if it is PC
00202         relative.  */
00203       if (! rel->howto->pc_relative)
00204        val |= NLM_HIBIT;
00205       else
00206        {
00207          /* PC relative relocs on NetWare must be pcrel_offset.  */
00208          if (! rel->howto->pcrel_offset)
00209            {
00210              bfd_set_error (bfd_error_invalid_operation);
00211              return FALSE;
00212            }
00213        }
00214     }
00215 
00216   bfd_put_32 (abfd, val, temp);
00217   if (bfd_bwrite (temp, (bfd_size_type) sizeof (temp), abfd) != sizeof (temp))
00218     return FALSE;
00219 
00220   return TRUE;
00221 }
00222 
00223 /* I want to be able to use objcopy to turn an i386 a.out or COFF file
00224    into a NetWare i386 module.  That means that the relocs from the
00225    source file have to be mapped into relocs that apply to the target
00226    file.  This function is called by nlm_set_section_contents to give
00227    it a chance to rework the relocs.
00228 
00229    This is actually a fairly general concept.  However, this is not a
00230    general implementation.  */
00231 
00232 static bfd_boolean
00233 nlm_i386_mangle_relocs (bfd *abfd,
00234                      asection *sec,
00235                      const PTR data,
00236                      bfd_vma offset,
00237                      bfd_size_type count)
00238 {
00239   arelent **rel_ptr_ptr, **rel_end;
00240 
00241   rel_ptr_ptr = sec->orelocation;
00242   rel_end = rel_ptr_ptr + sec->reloc_count;
00243   for (; rel_ptr_ptr < rel_end; rel_ptr_ptr++)
00244     {
00245       arelent *rel;
00246       asymbol *sym;
00247       bfd_vma addend;
00248 
00249       rel = *rel_ptr_ptr;
00250       sym = *rel->sym_ptr_ptr;
00251 
00252       /* Note that no serious harm will ensue if we fail to change a
00253         reloc.  We will wind up failing in nlm_i386_write_import.  */
00254 
00255       /* Make sure this reloc is within the data we have.  We only 4
00256         byte relocs here, so we insist on having 4 bytes.  */
00257       if (rel->address < offset
00258          || rel->address + 4 > offset + count)
00259        continue;
00260 
00261       /* NetWare doesn't support reloc addends, so we get rid of them
00262         here by simply adding them into the object data.  We handle
00263         the symbol value, if any, the same way.  */
00264       addend = rel->addend + sym->value;
00265 
00266       /* The value of a symbol is the offset into the section.  If the
00267         symbol is in the .bss segment, we need to include the size of
00268         the data segment in the offset as well.  Fortunately, we know
00269         that at this point the size of the data section is in the NLM
00270         header.  */
00271       if (((bfd_get_section_flags (abfd, bfd_get_section (sym))
00272            & SEC_LOAD) == 0)
00273          && ((bfd_get_section_flags (abfd, bfd_get_section (sym))
00274               & SEC_ALLOC) != 0))
00275        addend += nlm_fixed_header (abfd)->dataImageSize;
00276 
00277       if (addend != 0
00278          && rel->howto != NULL
00279          && rel->howto->rightshift == 0
00280          && rel->howto->size == 2
00281          && rel->howto->bitsize == 32
00282          && rel->howto->bitpos == 0
00283          && rel->howto->src_mask == 0xffffffff
00284          && rel->howto->dst_mask == 0xffffffff)
00285        {
00286          bfd_vma val;
00287 
00288          val = bfd_get_32 (abfd, (bfd_byte *) data + rel->address - offset);
00289          val += addend;
00290          bfd_put_32 (abfd, val, (bfd_byte *) data + rel->address - offset);
00291          rel->addend = 0;
00292        }
00293 
00294       /* NetWare uses a reloc with pcrel_offset set.  We adjust
00295         pc_relative relocs accordingly.  We are going to change the
00296         howto field, so we can only do this if the current one is
00297         compatible.  We should check special_function here, but at
00298         the moment coff-i386 uses a special_function which does not
00299         affect what we are doing here.  */
00300       if (rel->howto != NULL
00301          && rel->howto->pc_relative
00302          && ! rel->howto->pcrel_offset
00303          && rel->howto->rightshift == 0
00304          && rel->howto->size == 2
00305          && rel->howto->bitsize == 32
00306          && rel->howto->bitpos == 0
00307          && rel->howto->src_mask == 0xffffffff
00308          && rel->howto->dst_mask == 0xffffffff)
00309        {
00310          bfd_vma val;
00311 
00312          /* When pcrel_offset is not set, it means that the negative
00313             of the address of the memory location is stored in the
00314             memory location.  We must add it back in.  */
00315          val = bfd_get_32 (abfd, (bfd_byte *) data + rel->address - offset);
00316          val += rel->address;
00317          bfd_put_32 (abfd, val, (bfd_byte *) data + rel->address - offset);
00318 
00319          rel->howto = &nlm_i386_pcrel_howto;
00320        }
00321     }
00322 
00323   return TRUE;
00324 }
00325 
00326 /* Read a NetWare i386 import record.  */
00327 
00328 static bfd_boolean
00329 nlm_i386_read_import (bfd * abfd, nlmNAME (symbol_type) * sym)
00330 {
00331   struct nlm_relent *nlm_relocs;   /* Relocation records for symbol.  */
00332   bfd_size_type rcount;                   /* Number of relocs.  */
00333   bfd_byte temp[NLM_TARGET_LONG_SIZE];    /* Temporary 32-bit value.  */
00334   unsigned char symlength;         /* Length of symbol name.  */
00335   char *name;
00336 
00337   if (bfd_bread (& symlength, (bfd_size_type) sizeof (symlength), abfd)
00338       != sizeof (symlength))
00339     return FALSE;
00340   sym -> symbol.the_bfd = abfd;
00341   name = bfd_alloc (abfd, (bfd_size_type) symlength + 1);
00342   if (name == NULL)
00343     return FALSE;
00344   if (bfd_bread (name, (bfd_size_type) symlength, abfd) != symlength)
00345     return FALSE;
00346   name[symlength] = '\0';
00347   sym -> symbol.name = name;
00348   sym -> symbol.flags = 0;
00349   sym -> symbol.value = 0;
00350   sym -> symbol.section = bfd_und_section_ptr;
00351   if (bfd_bread (temp, (bfd_size_type) sizeof (temp), abfd) != sizeof (temp))
00352     return FALSE;
00353   rcount = H_GET_32 (abfd, temp);
00354   nlm_relocs = bfd_alloc (abfd, rcount * sizeof (struct nlm_relent));
00355   if (!nlm_relocs)
00356     return FALSE;
00357   sym -> relocs = nlm_relocs;
00358   sym -> rcnt = 0;
00359   while (sym -> rcnt < rcount)
00360     {
00361       asection *section;
00362 
00363       if (! nlm_i386_read_reloc (abfd, sym, &section, &nlm_relocs -> reloc))
00364        return FALSE;
00365       nlm_relocs -> section = section;
00366       nlm_relocs++;
00367       sym -> rcnt++;
00368     }
00369   return TRUE;
00370 }
00371 
00372 /* Write out an external reference.  */
00373 
00374 static bfd_boolean
00375 nlm_i386_write_external (bfd *abfd,
00376                       bfd_size_type count,
00377                       asymbol *sym,
00378                       struct reloc_and_sec *relocs)
00379 {
00380   unsigned int i;
00381   bfd_byte len;
00382   unsigned char temp[NLM_TARGET_LONG_SIZE];
00383 
00384   len = strlen (sym->name);
00385   if ((bfd_bwrite (&len, (bfd_size_type) sizeof (bfd_byte), abfd)
00386        != sizeof (bfd_byte))
00387       || bfd_bwrite (sym->name, (bfd_size_type) len, abfd) != len)
00388     return FALSE;
00389 
00390   bfd_put_32 (abfd, count, temp);
00391   if (bfd_bwrite (temp, (bfd_size_type) sizeof (temp), abfd) != sizeof (temp))
00392     return FALSE;
00393 
00394   for (i = 0; i < count; i++)
00395     if (! nlm_i386_write_import (abfd, relocs[i].sec, relocs[i].rel))
00396       return FALSE;
00397 
00398   return TRUE;
00399 }
00400 
00401 #include "nlmswap.h"
00402 
00403 static const struct nlm_backend_data nlm32_i386_backend =
00404 {
00405   "NetWare Loadable Module\032",
00406   sizeof (Nlm32_i386_External_Fixed_Header),
00407   0,   /* Optional_prefix_size.  */
00408   bfd_arch_i386,
00409   0,
00410   FALSE,
00411   0,   /* Backend_object_p.  */
00412   0,   /* Write_prefix_func.  */
00413   nlm_i386_read_reloc,
00414   nlm_i386_mangle_relocs,
00415   nlm_i386_read_import,
00416   nlm_i386_write_import,
00417   0,   /* Set_public_section.  */
00418   0,   /* Set_public_offset.  */
00419   nlm_swap_fixed_header_in,
00420   nlm_swap_fixed_header_out,
00421   nlm_i386_write_external,
00422   0,   /* Write_export.  */
00423 };
00424 
00425 #define TARGET_LITTLE_NAME         "nlm32-i386"
00426 #define TARGET_LITTLE_SYM          nlmNAME (i386_vec)
00427 #define TARGET_BACKEND_DATA        & nlm32_i386_backend
00428 
00429 #include "nlm-target.h"