* cofflink.c (process_embedded_commands): New function
authorSteve Chamberlain <sac@cygnus>
Wed, 14 Jun 1995 22:29:47 +0000 (22:29 +0000)
committerSteve Chamberlain <sac@cygnus>
Wed, 14 Jun 1995 22:29:47 +0000 (22:29 +0000)
reads and handles .drectve sections for PE.
(coff_link_input_bfd): Call new function if PE.

bfd/cofflink.c

index 87769648c27b08e39066a2eb2000cbf3a1a8e9d9..2e6fe26bec026aa1b1a89e3550e2df3b85866f67 100644 (file)
@@ -1,5 +1,5 @@
 /* COFF specific linker code.
-   Copyright 1994 Free Software Foundation, Inc.
+   Copyright 1994, 1995 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Cygnus Support.
 
 This file is part of BFD, the Binary File Descriptor library.
@@ -78,6 +78,9 @@ struct coff_final_link_info
   bfd_byte *external_relocs;
   /* Buffer large enough to hold swapped relocs of any input section.  */
   struct internal_reloc *internal_relocs;
+
+enum   bfd_link_subsystem  subsystem;
+bfd_link_stack_heap stack_heap_parameters;
 };
 
 static struct bfd_hash_entry *coff_link_hash_newfunc
@@ -100,6 +103,45 @@ static boolean coff_reloc_link_order
   PARAMS ((bfd *, struct coff_final_link_info *, asection *,
           struct bfd_link_order *));
 
+
+/* These new data and data types are used to keep track of the .idata$4 and
+   .idata$5 relocations which are put into the .idata section for all of the
+   *.dll input libraries linked in.  This is not a great solution, and may
+   break in the future if MS changes the format of its libraries, but it
+   does work for the collection of mstools libraries we are currently working
+   with.  The main problem is that there are some new majic symbols defined
+   in the libraries which are non-standard coff and simply aren't handled 
+   completely by ld.  What has been included here will help finish up the job.
+     Basically, during the link, .idata$4 and .idata$5 pointers are correctly
+   relocated to the image.  At the very end of the link, the .idata$2
+   information is written.  This data appears at the beginning of the .idata
+   section and a 'set' of information appears for each *.dll passed in.
+   Each set of information consists of 3 addresses, a pointer to the .idata$4
+   start, a pointer to .idata$6 (which has the name of the dll), and a pointer
+   to .idata$5 start.  The idata$4 and 5 information is a list of pointers
+   which appear to point to the name of various functions found within the dll.
+   When invoked, the loader will write over these names with the correct
+   addresses to use for these functions.  
+     Without this 'fix', all information appears correctly except for the
+   addresses of the .idata$4 and 5 starts within the .idata$2 portion of the
+   .idata section.  What we will do is to keep track of the dll's processed
+   and the number of functions needed from each dll.  From this information
+   we can correctly compute the start of the idata$4 and 5 lists for each
+   dll in the idata section */
+static int num_DLLs_done = 0;
+static int num_DLLs      = 0;
+static int all_entries   = 0;
+struct DLL_struct {
+  const char * DLL_name;
+  int          num_entries;
+};
+struct DLL_struct MS_DLL[10];
+static bfd_vma idata_4_prev = 0;
+static bfd_vma idata_5_prev = 0;
+static bfd_vma add_to_val   = 0;
+
+
+
 /* Create an entry in a COFF linker hash table.  */
 
 static struct bfd_hash_entry *
@@ -304,7 +346,7 @@ coff_read_string_table (abfd)
   else
     {
 #if STRING_SIZE_SIZE == 4
-      strsize = bfd_h_get_32 (abfd, extstrsize);
+      strsize = bfd_h_get_32 (abfd, (bfd_byte *) extstrsize);
 #else
  #error Change bfd_h_get_32
 #endif
@@ -566,9 +608,10 @@ coff_link_add_symbols (abfd, info)
                      bfd_byte *eaux;
                      union internal_auxent *iaux;
 
-                     alloc = bfd_hash_allocate (&info->hash->table,
-                                                (sym.n_numaux
-                                                 * sizeof (*alloc)));
+                     alloc = ((union internal_auxent *)
+                              bfd_hash_allocate (&info->hash->table,
+                                                 (sym.n_numaux
+                                                  * sizeof (*alloc))));
                      if (alloc == NULL)
                        {
                          bfd_set_error (bfd_error_no_memory);
@@ -593,6 +636,126 @@ coff_link_add_symbols (abfd, info)
   return true;
 }
 
+/* parse out a -heap <reserved>,<commit> line */
+
+static char *
+dores_com (ptr, def,res, com)
+     char *ptr;
+     int *def;
+     int *res;
+     int *com;
+{
+  *def = 1;
+  *res = strtoul (ptr, &ptr, 0);
+  if (ptr[0] == ',')
+    *com = strtoul (ptr+1, &ptr, 0);
+  return ptr;
+}
+
+static char *get_name(ptr, dst)
+char *ptr;
+char **dst;
+{
+  while (*ptr == ' ')
+    ptr++;
+  *dst = ptr;
+  while (*ptr && *ptr != ' ')
+    ptr++;
+  *ptr = 0;
+  return ptr+1;
+}
+/* Process any magic embedded commands in a section called .drectve */
+                       
+static int
+process_embedded_commands (abfd)
+     bfd *abfd;
+{
+  asection *sec = bfd_get_section_by_name (abfd, ".drectve");
+  char *s;
+  char *e;
+  char *copy;
+  if (!s) 
+    return 1;
+
+  copy = malloc (sec->_raw_size);
+  if (!copy) 
+    {
+      bfd_set_error (bfd_error_no_memory);
+      return 0;
+    }
+  if (! bfd_get_section_contents(abfd, sec, copy, 0, sec->_raw_size)) 
+    {
+      free (copy);
+      return 0;
+    }
+  e = copy + sec->_raw_size;
+  for (s = copy;  s < e ; ) 
+    {
+      if (s[0]!= '-') {
+       s++;
+       continue;
+      }
+      if (strncmp (s,"-attr", 5) == 0)
+       {
+         char *name;
+         char *attribs;
+         asection *asec;
+
+         int loop = 1;
+         int had_write = 0;
+         int had_read = 0;
+         int had_exec= 0;
+         int had_shared= 0;
+         s += 5;
+         s = get_name(s, &name);
+         s = get_name(s, &attribs);
+         while (loop) {
+           switch (*attribs++) 
+             {
+             case 'W':
+               had_write = 1;
+               break;
+             case 'R':
+               had_read = 1;
+               break;
+             case 'S':
+               had_shared = 1;
+               break;
+             case 'X':
+               had_exec = 1;
+               break;
+             default:
+               loop = 0;
+             }
+         }
+         asec = bfd_get_section_by_name (abfd, name);
+         if (asec) {
+           if (had_exec)
+             asec->flags |= SEC_CODE;
+           if (!had_write)
+             asec->flags |= SEC_READONLY;
+         }
+       }
+      else if (strncmp (s,"-heap", 5) == 0)
+       {
+         s = dores_com (s+5, 
+                        &NT_stack_heap.heap_defined,
+                        &NT_stack_heap.heap_reserve,
+                        &NT_stack_heap.heap_commit);
+       }
+      else if (strncmp (s,"-stack", 6) == 0)
+       {
+         s = dores_com (s+6,
+                        &NT_stack_heap.heap_defined,
+                        &NT_stack_heap.heap_reserve,
+                        &NT_stack_heap.heap_commit);
+       }
+      else 
+       s++;
+    }
+  free (copy);
+  return 1;
+}
 /* Do the final link step.  */
 
 boolean
@@ -633,6 +796,15 @@ _bfd_coff_final_link (abfd, info)
   finfo.external_relocs = NULL;
   finfo.internal_relocs = NULL;
 
+  if (obj_pe(abfd))
+    {
+      /* store the subsystem, stack and heap parameters in variables defined
+        in internal.h so that when they are needed to write the NT optional
+        file header (coffcode.h), they will be available */
+      NT_subsystem  = info->subsystem;
+      NT_stack_heap = info->stack_heap_parameters;
+    }
+
   finfo.strtab = _bfd_stringtab_init ();
   if (finfo.strtab == NULL)
     goto error_return;
@@ -989,7 +1161,7 @@ _bfd_coff_final_link (abfd, info)
 #if STRING_SIZE_SIZE == 4
   bfd_h_put_32 (abfd,
                _bfd_stringtab_size (finfo.strtab) + STRING_SIZE_SIZE,
-               strbuf);
+               (bfd_byte *) strbuf);
 #else
  #error Change bfd_h_put_32
 #endif
@@ -1054,6 +1226,9 @@ coff_link_input_bfd (finfo, input_bfd)
      bfd *input_bfd;
 {
   boolean (*sym_is_global) PARAMS ((bfd *, struct internal_syment *));
+  boolean (*adjust_symndx) PARAMS ((bfd *, struct bfd_link_info *, bfd *,
+                                   asection *, struct internal_reloc *,
+                                   boolean *));
   bfd *output_bfd;
   const char *strings;
   bfd_size_type syment_base;
@@ -1109,6 +1284,13 @@ coff_link_input_bfd (finfo, input_bfd)
   indexp = finfo->sym_indices;
   output_index = syment_base;
   outsym = finfo->outsyms;
+
+  if (obj_pe (output_bfd))
+      {
+       if (!process_embedded_commands (input_bfd))
+         return false;
+      }
+
   while (esym < esym_end)
     {
       struct internal_syment isym;
@@ -1387,7 +1569,8 @@ coff_link_input_bfd (finfo, input_bfd)
                {
                  /* If this is a long filename, we must put it in the
                     string table.  */
-                 if (auxp->x_file.x_n.x_zeroes == 0)
+                 if (auxp->x_file.x_n.x_zeroes == 0
+                     && auxp->x_file.x_n.x_offset != 0)
                    {
                      const char *filename;
                      bfd_size_type indx;
@@ -1570,13 +1753,14 @@ coff_link_input_bfd (finfo, input_bfd)
      symbol will be the first symbol in the next input file.  In the
      normal case, this will save us from writing out the C_FILE symbol
      again.  */
-  if (finfo->last_file_index >= syment_base)
+  if (finfo->last_file_index != -1
+      && finfo->last_file_index >= syment_base)
     {
       finfo->last_file.n_value = output_index;
       bfd_coff_swap_sym_out (output_bfd, (PTR) &finfo->last_file,
                             (PTR) (finfo->outsyms
-                                   + ((finfo->last_file_index - syment_base)
-                                      * osymesz)));
+                                   + ((finfo->last_file_index - syment_base)
+                                      * osymesz)));
     }
 
   /* Write the modified symbols to the output file.  */
@@ -1598,6 +1782,7 @@ coff_link_input_bfd (finfo, input_bfd)
 
   /* Relocate the contents of each section.  */
   relsz = bfd_coff_relsz (input_bfd);
+  adjust_symndx = coff_backend_info (input_bfd)->_bfd_coff_adjust_symndx;
   for (o = input_bfd->sections; o != NULL; o = o->next)
     {
       if ((o->flags & SEC_HAS_CONTENTS) == 0)
@@ -1605,7 +1790,7 @@ coff_link_input_bfd (finfo, input_bfd)
 
       if (! bfd_get_section_contents (input_bfd, o, finfo->contents,
                                      (file_ptr) 0, o->_raw_size))
-       return false;
+       return false;
 
       if ((o->flags & SEC_RELOC) != 0)
        {
@@ -1655,7 +1840,6 @@ coff_link_input_bfd (finfo, input_bfd)
              struct coff_link_hash_entry **rel_hash;
 
              offset = o->output_section->vma + o->output_offset - o->vma;
-
              irel = internal_relocs;
              irelend = irel + o->reloc_count;
              rel_hash = (finfo->section_info[target_index].rel_hashes
@@ -1663,6 +1847,7 @@ coff_link_input_bfd (finfo, input_bfd)
              for (; irel < irelend; irel++, rel_hash++)
                {
                  struct coff_link_hash_entry *h;
+                 boolean adjusted;
 
                  *rel_hash = NULL;
 
@@ -1673,6 +1858,16 @@ coff_link_input_bfd (finfo, input_bfd)
                  if (irel->r_symndx == -1)
                    continue;
 
+                 if (adjust_symndx)
+                   {
+                     if (! (*adjust_symndx) (output_bfd, finfo->info,
+                                             input_bfd, o, irel,
+                                             &adjusted))
+                       return false;
+                     if (adjusted)
+                       continue;
+                   }
+
                  h = obj_coff_sym_hashes (input_bfd)[irel->r_symndx];
                  if (h != NULL)
                    {
@@ -1792,12 +1987,13 @@ coff_write_global_sym (h, data)
       return false;
 
     case bfd_link_hash_undefined:
-    case bfd_link_hash_weak:
+    case bfd_link_hash_undefweak:
       isym.n_scnum = N_UNDEF;
       isym.n_value = 0;
       break;
 
     case bfd_link_hash_defined:
+    case bfd_link_hash_defweak:
       {
        asection *sec;
 
@@ -1897,7 +2093,7 @@ coff_reloc_link_order (output_bfd, finfo, output_section, link_order)
      asection *output_section;
      struct bfd_link_order *link_order;
 {
-  const reloc_howto_type *howto;
+  reloc_howto_type *howto;
   struct internal_reloc *irel;
   struct coff_link_hash_entry **rel_hash_ptr;
 
@@ -2039,6 +2235,7 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
   struct internal_reloc *rel;
   struct internal_reloc *relend;
 
+
   rel = relocs;
   relend = rel + input_section->reloc_count;
   for (; rel < relend; rel++)
@@ -2048,7 +2245,7 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
       struct internal_syment *sym;
       bfd_vma addend;
       bfd_vma val;
-      const reloc_howto_type *howto;
+      reloc_howto_type *howto;
       bfd_reloc_status_type rstat;
 
       symndx = rel->r_symndx;
@@ -2068,21 +2265,33 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
          size of the symbol is included in the section contents, or it
          is not.  We assume that the size is not included, and force
          the rtype_to_howto function to adjust the addend as needed.  */
+
       if (sym != NULL && sym->n_scnum != 0)
        addend = - sym->n_value;
       else
        addend = 0;
 
+
       howto = bfd_coff_rtype_to_howto (input_bfd, input_section, rel, h,
                                       sym, &addend);
       if (howto == NULL)
        return false;
 
+      /* WINDOWS_NT; in this next section, the value of 'val' will be computed.
+         With respect to the .idata and .rsrc sections, the NT_IMAGE_BASE
+         must be removed from the value that is to be relocated (NT_IMAGE_BASE
+         is currently defined in internal.h and has value 400000).  Now this
+         value should only be removed from addresses being relocated in the
+         .idata and .rsrc sections, not the .text section which should have
+         the 'real' address.  In addition, the .rsrc val's must also be
+         adjusted by the input_section->vma.  */
+
       val = 0;
 
       if (h == NULL)
        {
          asection *sec;
+          int i;
 
          if (symndx == -1)
            {
@@ -2092,15 +2301,30 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
          else
            {
              sec = sections[symndx];
-             val = (sec->output_section->vma
+              val = (sec->output_section->vma
                     + sec->output_offset
                     + sym->n_value
                     - sec->vma);
+             if (obj_pe(output_bfd)) {
+               /* Make a correction here to val if the sec is either .rsrc
+                  or .idata */
+               if (strcmp (input_section->name, ".text") != 0)
+                 {
+                   if (strncmp (sec->name, ".idata$", 7) == 0)
+                     val -= NT_IMAGE_BASE;
+                   else if (strncmp (sec->name, ".rsrc$", 6) == 0) 
+                     {
+                       val -= NT_IMAGE_BASE;
+                       val += sec->vma;
+                     }
+                 }
+             }
            }
        }
       else
        {
-         if (h->root.type == bfd_link_hash_defined)
+         if (h->root.type == bfd_link_hash_defined
+             || h->root.type == bfd_link_hash_defweak)
            {
              asection *sec;
 
@@ -2108,6 +2332,20 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
              val = (h->root.u.def.value
                     + sec->output_section->vma
                     + sec->output_offset);
+             if (obj_pe (output_bfd)) {
+               /* Make a correction here to val if the sec is either .rsrc
+                  or .idata */
+               if (strcmp (input_section->name, ".text") != 0)
+                 {
+                   if (strncmp (sec->name, ".idata$", 7) == 0)
+                     val -= NT_IMAGE_BASE;
+                   else if (strncmp (sec->name, ".rsrc$", 6) == 0) 
+                     {
+                       val -= NT_IMAGE_BASE;
+                       val += sec->vma;
+                     }
+                 }
+             }
            }
          else if (! info->relocateable)
            {
@@ -2118,6 +2356,102 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
            }
        }
 
+      if (obj_pe (output_bfd)) {
+
+       /* Here's where we will collect the information about the dll .idata$4 
+          and 5 entries and fix up the vals for .idata$2 information.  When
+          we encounter processing for .idata$5 (this could also be done for
+          .idata$4) we will keep track of the number of entries made for a
+          particular dll.  Now if we are processing .idata$2 input_section,
+          then we know how many entries have been made from each dll and we
+          have to fix up the .idata$2 start addresses for .idata$4 and 
+          .idata$5. */
+       add_to_val = 0;
+       if (strncmp (input_section->name, ".idata$5", 8) == 0)
+         {
+           if (num_DLLs == 0)  /* this is the first one */
+             {
+               num_DLLs += 1;
+               MS_DLL[num_DLLs].DLL_name = input_bfd->filename;
+               MS_DLL[num_DLLs].num_entries += 1;
+             }
+           else if (!strcmp (input_bfd->filename, MS_DLL[num_DLLs].DLL_name))
+             {
+               /* this is just another entry */
+               MS_DLL[num_DLLs].num_entries += 1;
+             }
+           else
+             {
+               /* This is a new DLL */
+               num_DLLs += 1;
+               MS_DLL[num_DLLs].DLL_name = input_bfd->filename;
+               MS_DLL[num_DLLs].num_entries += 1; 
+             }
+           all_entries += 1;
+         }
+
+       else if (strncmp (input_section->name, ".idata$2", 8) == 0)
+         {
+           /* All information about the number of entries needed from each
+              DLL has been collected at this point.  Now we actually want to
+              make and adjustment to the val's for .idata$4 and .idata$5
+              which are part of the .idata$2 section. */
+           /* first we have to get the symbol name from sym.  This will be
+              either .idata$4, .idata$5 or .idata$6.  A 'fixup' is computed for
+              .idata$4 and .idata$5 but not for .idata$6 (this value is handled
+              correctly already and doesn't have to be fixed) */
+           const char *name;
+           char buf[SYMNMLEN + 1];
+
+           if (sym->_n._n_n._n_zeroes == 0 && sym->_n._n_n._n_offset != 0)
+             name = obj_coff_strings (input_bfd) + sym->_n._n_n._n_offset;
+           else
+             {
+               strncpy (buf, sym->_n._n_name, SYMNMLEN);
+               buf[SYMNMLEN] = '\0';
+               name = buf;
+             }
+
+           if (num_DLLs_done)
+             {
+               /* we have done at least one.  The val fixups are based on the
+                  previous fixups */
+               if (strncmp (name, ".idata$4", 8) == 0)
+                 {
+                   add_to_val = idata_4_prev +
+                     ((MS_DLL[num_DLLs_done].num_entries + 1) * 4);
+                   idata_4_prev = add_to_val;
+                 }
+               else if (strncmp (name, ".idata$5", 8) == 0)
+                 {
+                   add_to_val = idata_5_prev +
+                     ((MS_DLL[num_DLLs_done].num_entries + 1) * 4);
+                   idata_5_prev = add_to_val;
+                   num_DLLs_done += 1; /* assuming that idata$5 is done after $4*/
+                 }
+             }
+           else
+             {
+               /* This is the first one.  The other idata$4 and 5 entries will be
+                  computed from these */
+               if (strncmp (name, ".idata$4", 8) == 0)
+                 {
+                   add_to_val = ((num_DLLs - 1) * 0x14) + 0x28;
+                   idata_4_prev = add_to_val;
+                 }
+               else if (strncmp (name, ".idata$5", 8) == 0)
+                 {
+                   add_to_val = idata_4_prev + (all_entries + num_DLLs) * 4;
+                   idata_5_prev = add_to_val;
+                   num_DLLs_done += 1; /* assuming that idata$5 is done after $4*/
+                 }
+            
+             }
+         }
+       val = val + add_to_val;
+       
+      }
+  
       rstat = _bfd_final_link_relocate (howto, input_bfd, input_section,
                                        contents,
                                        rel->r_vaddr - input_section->vma,