bfd/
[deliverable/binutils-gdb.git] / bfd / elflink.c
index 6af091b1015ef7d117522602022f98b903441f2b..360ac69cdcb1e9666df0dbafaf039dc8936bfbcf 100644 (file)
@@ -1,6 +1,6 @@
 /* ELF linking support for BFD.
    Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-   2005, 2006 Free Software Foundation, Inc.
+   2005, 2006, 2007 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
@@ -438,14 +438,22 @@ bfd_elf_link_record_dynamic_symbol (struct bfd_link_info *info,
 
 void
 bfd_elf_link_mark_dynamic_symbol (struct bfd_link_info *info,
-                                 struct elf_link_hash_entry *h)
+                                 struct elf_link_hash_entry *h,
+                                 Elf_Internal_Sym *sym)
 {
-  struct bfd_elf_dynamic_list *d = info->dynamic;
+  struct bfd_elf_dynamic_list *d = info->dynamic_list;
 
-  if (d == NULL || info->relocatable)
+  /* It may be called more than once on the same H.  */
+  if(h->dynamic || info->relocatable)
     return;
 
-  if ((*d->match) (&d->head, NULL, h->root.root.string))
+  if ((info->dynamic_data
+       && (h->type == STT_OBJECT
+          || (sym != NULL
+              && ELF_ST_TYPE (sym->st_info) == STT_OBJECT)))
+      || (d != NULL 
+         && h->root.type == bfd_link_hash_new
+         && (*d->match) (&d->head, NULL, h->root.root.string)))
     h->dynamic = 1;
 }
 
@@ -483,7 +491,7 @@ bfd_elf_record_link_assignment (bfd *output_bfd,
 
   if (h->root.type == bfd_link_hash_new)
     {
-      bfd_elf_link_mark_dynamic_symbol (info, h);
+      bfd_elf_link_mark_dynamic_symbol (info, h, NULL);
       h->non_elf = 0;
     }
 
@@ -861,13 +869,17 @@ _bfd_elf_merge_symbol (bfd *abfd,
         || h->root.type == bfd_link_hash_warning)
     h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
+  /* We have to check it for every instance since the first few may be
+     refereences and not all compilers emit symbol type for undefined
+     symbols.  */
+  bfd_elf_link_mark_dynamic_symbol (info, h, sym);
+
   /* If we just created the symbol, mark it as being an ELF symbol.
      Other than that, there is nothing to do--there is no merge issue
      with a newly defined symbol--so we just return.  */
 
   if (h->root.type == bfd_link_hash_new)
     {
-      bfd_elf_link_mark_dynamic_symbol (info, h);
       h->non_elf = 0;
       return TRUE;
     }
@@ -1544,6 +1556,10 @@ _bfd_elf_add_default_symbol (bfd *abfd,
       hi = h;
     }
 
+  /* Check if HI is a warning symbol.  */
+  if (hi->root.type == bfd_link_hash_warning)
+    hi = (struct elf_link_hash_entry *) hi->root.u.i.link;
+
   /* If there is a duplicate definition somewhere, then HI may not
      point to an indirect symbol.  We will have reported an error to
      the user in that case.  */
@@ -4035,11 +4051,13 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
                }
            }
 
-         /* Remember the symbol size and type.  */
-         if (isym->st_size != 0
+         /* Remember the symbol size if it isn't undefined.  */
+         if ((isym->st_size != 0 && isym->st_shndx != SHN_UNDEF)
              && (definition || h->size == 0))
            {
-             if (h->size != 0 && h->size != isym->st_size && ! size_change_ok)
+             if (h->size != 0
+                 && h->size != isym->st_size
+                 && ! size_change_ok)
                (*_bfd_error_handler)
                  (_("Warning: size of symbol `%s' changed"
                     " from %lu in %B to %lu in %B"),
@@ -5219,6 +5237,7 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
   if (!is_elf_hash_table (info->hash))
     return TRUE;
 
+  bed = get_elf_backend_data (output_bfd);
   elf_tdata (output_bfd)->relro = info->relro;
   if (info->execstack)
     elf_tdata (output_bfd)->stack_flags = PF_R | PF_W | PF_X;
@@ -5245,7 +5264,7 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
                exec = PF_X;
              notesec = s;
            }
-         else
+         else if (bed->default_execstack)
            exec = PF_X;
        }
       if (notesec)
@@ -5266,7 +5285,6 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
 
   /* The backend may have to create some sections regardless of whether
      we're dynamic or not.  */
-  bed = get_elf_backend_data (output_bfd);
   if (bed->elf_backend_always_size_sections
       && ! (*bed->elf_backend_always_size_sections) (output_bfd, info))
     return FALSE;
@@ -6334,6 +6352,719 @@ struct elf_outext_info
   struct elf_final_link_info *finfo;
 };
 
+
+/* Support for evaluating a complex relocation.
+
+   Complex relocations are generalized, self-describing relocations.  The
+   implementation of them consists of two parts: complex symbols, and the
+   relocations themselves. 
+
+   The relocations are use a reserved elf-wide relocation type code (R_RELC
+   external / BFD_RELOC_RELC internal) and an encoding of relocation field
+   information (start bit, end bit, word width, etc) into the addend.  This
+   information is extracted from CGEN-generated operand tables within gas.
+
+   Complex symbols are mangled symbols (BSF_RELC external / STT_RELC
+   internal) representing prefix-notation expressions, including but not
+   limited to those sorts of expressions normally encoded as addends in the
+   addend field.  The symbol mangling format is:
+
+   <node> := <literal>
+          |  <unary-operator> ':' <node>
+          |  <binary-operator> ':' <node> ':' <node>
+         ;
+
+   <literal> := 's' <digits=N> ':' <N character symbol name>
+             |  'S' <digits=N> ':' <N character section name>
+            |  '#' <hexdigits>
+            ;
+
+   <binary-operator> := as in C
+   <unary-operator> := as in C, plus "0-" for unambiguous negation.  */
+
+static void
+set_symbol_value (bfd *                         bfd_with_globals,
+                 struct elf_final_link_info *  finfo,    
+                 int                           symidx,
+                 bfd_vma                       val)
+{
+  bfd_boolean                    is_local;
+  Elf_Internal_Sym *             sym;
+  struct elf_link_hash_entry **  sym_hashes;
+  struct elf_link_hash_entry *   h;
+
+  sym_hashes = elf_sym_hashes (bfd_with_globals);
+  sym = finfo->internal_syms + symidx;  
+  is_local = ELF_ST_BIND(sym->st_info) == STB_LOCAL;
+  
+  if (is_local)
+    {
+      /* It is a local symbol: move it to the
+        "absolute" section and give it a value.  */
+      sym->st_shndx = SHN_ABS;
+      sym->st_value = val;
+    }
+  else 
+    {
+      /* It is a global symbol: set its link type
+        to "defined" and give it a value.  */
+      h = sym_hashes [symidx];   
+      while (h->root.type == bfd_link_hash_indirect
+            || h->root.type == bfd_link_hash_warning)
+       h = (struct elf_link_hash_entry *) h->root.u.i.link;
+      h->root.type = bfd_link_hash_defined;
+      h->root.u.def.value = val;
+      h->root.u.def.section = bfd_abs_section_ptr;
+    }
+}
+
+static bfd_boolean 
+resolve_symbol (const char *                  name,
+               bfd *                         input_bfd,
+               struct elf_final_link_info *  finfo,
+               bfd_vma *                     result,
+               size_t                        locsymcount)
+{
+  Elf_Internal_Sym *            sym;
+  struct bfd_link_hash_entry *  global_entry;
+  const char *                  candidate = NULL;
+  Elf_Internal_Shdr *           symtab_hdr;
+  asection *                    sec = NULL;
+  size_t                        i;
+  
+  symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
+
+  for (i = 0; i < locsymcount; ++ i)
+    {
+      sym = finfo->internal_syms + i;
+      sec = finfo->sections [i];
+
+      if (ELF_ST_BIND (sym->st_info) != STB_LOCAL)
+       continue;
+
+      candidate = bfd_elf_string_from_elf_section (input_bfd,
+                                                  symtab_hdr->sh_link,
+                                                  sym->st_name);
+#ifdef DEBUG
+      printf ("Comparing string: '%s' vs. '%s' = 0x%x\n", 
+             name, candidate, (unsigned int)sym->st_value);
+#endif
+      if (candidate && strcmp (candidate, name) == 0)
+       {
+         * result = sym->st_value;
+
+         if (sym->st_shndx > SHN_UNDEF && 
+             sym->st_shndx < SHN_LORESERVE)
+           {
+#ifdef DEBUG
+             printf ("adjusting for sec '%s' @ 0x%x + 0x%x\n",
+                     sec->output_section->name, 
+                     (unsigned int)sec->output_section->vma, 
+                     (unsigned int)sec->output_offset);
+#endif
+             * result += sec->output_offset + sec->output_section->vma;
+           }
+#ifdef DEBUG
+         printf ("Found symbol with effective value %8.8x\n", (unsigned int)* result);
+#endif
+         return TRUE;
+       }
+    }
+
+  /* Hmm, haven't found it yet. perhaps it is a global.  */
+  global_entry = bfd_link_hash_lookup (finfo->info->hash, name, FALSE, FALSE, TRUE);
+  if (!global_entry)
+    return FALSE;
+  
+  if (global_entry->type == bfd_link_hash_defined
+      || global_entry->type == bfd_link_hash_defweak)
+    {
+      * result = global_entry->u.def.value 
+       + global_entry->u.def.section->output_section->vma 
+       + global_entry->u.def.section->output_offset;
+#ifdef DEBUG
+      printf ("Found GLOBAL symbol '%s' with value %8.8x\n",
+             global_entry->root.string, (unsigned int)*result);
+#endif
+      return TRUE;
+    } 
+
+  if (global_entry->type == bfd_link_hash_common)
+    {
+      *result = global_entry->u.def.value +
+       bfd_com_section_ptr->output_section->vma +
+       bfd_com_section_ptr->output_offset;
+#ifdef DEBUG
+      printf ("Found COMMON symbol '%s' with value %8.8x\n",
+             global_entry->root.string, (unsigned int)*result);
+#endif
+      return TRUE;
+    }
+  
+  return FALSE;
+}
+
+static bfd_boolean
+resolve_section (const char *  name,
+                asection *    sections,
+                bfd_vma *     result)
+{
+  asection *    curr;
+  unsigned int  len;
+
+  for (curr = sections; curr; curr = curr->next)    
+    if (strcmp (curr->name, name) == 0)
+      {
+       *result = curr->vma;
+       return TRUE;
+      }
+
+  /* Hmm. still haven't found it. try pseudo-section names.  */
+  for (curr = sections; curr; curr = curr->next)    
+    {
+      len = strlen (curr->name);
+      if (len > strlen (name)) 
+       continue;
+
+      if (strncmp (curr->name, name, len) == 0)
+       {
+         if (strncmp (".end", name + len, 4) == 0)
+           {
+             *result = curr->vma + curr->size;
+             return TRUE;
+           }
+
+         /* Insert more pseudo-section names here, if you like.  */
+       }
+    }
+  
+  return FALSE;
+}
+
+static void
+undefined_reference (const char *  reftype,
+                    const char *  name)
+{
+  _bfd_error_handler (_("undefined %s reference in complex symbol: %s"), reftype, name);
+}
+
+static bfd_boolean
+eval_symbol (bfd_vma *                     result,
+            char *                        sym,
+            char **                       advanced,
+            bfd *                         input_bfd,
+            struct elf_final_link_info *  finfo,
+            bfd_vma                       addr,
+            bfd_vma                       section_offset,
+            size_t                        locsymcount,
+            int                           signed_p)
+{
+  int           len;
+  int           symlen;
+  bfd_vma       a;
+  bfd_vma       b;
+  const int     bufsz = 4096;
+  char          symbuf [bufsz];
+  const char *  symend;
+  bfd_boolean   symbol_is_section = FALSE;
+
+  len = strlen (sym);
+  symend = sym + len;
+
+  if (len < 1 || len > bufsz)
+    {
+      bfd_set_error (bfd_error_invalid_operation);
+      return FALSE;
+    }
+  
+  switch (* sym)
+    {
+    case '.':
+      * result = addr + section_offset;
+      * advanced = sym + 1;
+      return TRUE;
+
+    case '#':
+      ++ sym;
+      * result = strtoul (sym, advanced, 16);
+      return TRUE;
+
+    case 'S':
+      symbol_is_section = TRUE;
+    case 's':      
+      ++ sym;
+      symlen = strtol (sym, &sym, 10);
+      ++ sym; /* Skip the trailing ':'.  */
+
+      if ((symend < sym) || ((symlen + 1) > bufsz))
+       {
+         bfd_set_error (bfd_error_invalid_operation);
+         return FALSE;
+       }
+
+      memcpy (symbuf, sym, symlen);
+      symbuf [symlen] = '\0';
+      * advanced = sym + symlen;
+      
+      /* Is it always possible, with complex symbols, that gas "mis-guessed" 
+        the symbol as a section, or vice-versa. so we're pretty liberal in our
+        interpretation here; section means "try section first", not "must be a
+        section", and likewise with symbol.  */
+
+      if (symbol_is_section) 
+       {
+         if ((resolve_section (symbuf, finfo->output_bfd->sections, result) != TRUE)
+             && (resolve_symbol (symbuf, input_bfd, finfo, result, locsymcount) != TRUE))
+           {
+             undefined_reference ("section", symbuf);
+             return FALSE;
+           }
+       } 
+      else 
+       {
+         if ((resolve_symbol (symbuf, input_bfd, finfo, result, locsymcount) != TRUE)
+             && (resolve_section (symbuf, finfo->output_bfd->sections,
+                                  result) != TRUE))
+           {
+             undefined_reference ("symbol", symbuf);
+             return FALSE;
+           }
+       }
+
+      return TRUE;
+      
+      /* All that remains are operators.  */
+
+#define UNARY_OP(op)                                           \
+  if (strncmp (sym, #op, strlen (#op)) == 0)                   \
+    {                                                          \
+      sym += strlen (#op);                                     \
+      if (* sym == ':')                                                \
+        ++ sym;                                                        \
+      if (eval_symbol (& a, sym, & sym, input_bfd, finfo, addr, \
+                       section_offset, locsymcount, signed_p)   \
+                                                    != TRUE)   \
+        return FALSE;                                          \
+      if (signed_p)                                             \
+        * result = op ((signed)a);                             \
+      else                                                      \
+        * result = op a;                                        \
+      * advanced = sym;                                        \
+      return TRUE;                                             \
+    }
+
+#define BINARY_OP(op)                                          \
+  if (strncmp (sym, #op, strlen (#op)) == 0)                   \
+    {                                                          \
+      sym += strlen (#op);                                     \
+      if (* sym == ':')                                                \
+        ++ sym;                                                        \
+      if (eval_symbol (& a, sym, & sym, input_bfd, finfo, addr, \
+                       section_offset, locsymcount, signed_p)   \
+                                                     != TRUE)  \
+        return FALSE;                                          \
+      ++ sym;                                                  \
+      if (eval_symbol (& b, sym, & sym, input_bfd, finfo, addr, \
+                       section_offset, locsymcount, signed_p)   \
+                                                     != TRUE)  \
+        return FALSE;                                          \
+      if (signed_p)                                             \
+        * result = ((signed) a) op ((signed) b);               \
+      else                                                      \
+        * result = a op b;                                      \
+      * advanced = sym;                                                \
+      return TRUE;                                             \
+    }
+
+    default:
+      UNARY_OP  (0-);
+      BINARY_OP (<<);
+      BINARY_OP (>>);
+      BINARY_OP (==);
+      BINARY_OP (!=);
+      BINARY_OP (<=);
+      BINARY_OP (>=);
+      BINARY_OP (&&);
+      BINARY_OP (||);
+      UNARY_OP  (~);
+      UNARY_OP  (!);
+      BINARY_OP (*);
+      BINARY_OP (/);
+      BINARY_OP (%);
+      BINARY_OP (^);
+      BINARY_OP (|);
+      BINARY_OP (&);
+      BINARY_OP (+);
+      BINARY_OP (-);
+      BINARY_OP (<);
+      BINARY_OP (>);
+#undef UNARY_OP
+#undef BINARY_OP
+      _bfd_error_handler (_("unknown operator '%c' in complex symbol"), * sym);
+      bfd_set_error (bfd_error_invalid_operation);
+      return FALSE;
+    }
+}
+
+/* Entry point to evaluator, called from elf_link_input_bfd.  */
+
+static bfd_boolean
+evaluate_complex_relocation_symbols (bfd * input_bfd,
+                                    struct elf_final_link_info * finfo,
+                                    size_t locsymcount)
+{
+  const struct elf_backend_data * bed;
+  Elf_Internal_Shdr *             symtab_hdr;
+  struct elf_link_hash_entry **   sym_hashes;
+  asection *                      reloc_sec;
+  bfd_boolean                     result = TRUE;
+
+  /* For each section, we're going to check and see if it has any
+     complex relocations, and we're going to evaluate any of them
+     we can.  */
+
+  if (finfo->info->relocatable)
+    return TRUE;
+
+  symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
+  sym_hashes = elf_sym_hashes (input_bfd);
+  bed = get_elf_backend_data (input_bfd);
+
+  for (reloc_sec = input_bfd->sections; reloc_sec; reloc_sec = reloc_sec->next)
+    {
+      Elf_Internal_Rela * internal_relocs;
+      unsigned long i;
+
+      /* This section was omitted from the link.  */
+      if (! reloc_sec->linker_mark)
+       continue;
+
+      /* Only process sections containing relocs.  */
+      if ((reloc_sec->flags & SEC_RELOC) == 0)
+       continue;
+
+      if (reloc_sec->reloc_count == 0)
+       continue;
+
+      /* Read in the relocs for this section.  */
+      internal_relocs
+       = _bfd_elf_link_read_relocs (input_bfd, reloc_sec, NULL,
+                                    (Elf_Internal_Rela *) NULL,
+                                    FALSE);
+      if (internal_relocs == NULL)
+       continue;
+
+      for (i = reloc_sec->reloc_count; i--;)
+       {
+         Elf_Internal_Rela * rel;
+         char * sym_name;
+         bfd_vma index;
+         Elf_Internal_Sym * sym;
+         bfd_vma result;
+         bfd_vma section_offset;
+         bfd_vma addr;
+         int signed_p = 0;
+
+         rel = internal_relocs + i;
+         section_offset = reloc_sec->output_section->vma
+           + reloc_sec->output_offset;
+         addr = rel->r_offset;
+
+         index = ELF32_R_SYM (rel->r_info);
+         if (bed->s->arch_size == 64)
+           index >>= 24;
+
+         if (index == STN_UNDEF)
+           continue;
+
+         if (index < locsymcount)
+           {
+             /* The symbol is local.  */
+             sym = finfo->internal_syms + index;
+
+             /* We're only processing STT_RELC or STT_SRELC type symbols.  */
+             if ((ELF_ST_TYPE (sym->st_info) != STT_RELC) &&
+                 (ELF_ST_TYPE (sym->st_info) != STT_SRELC))
+               continue;
+
+             sym_name = bfd_elf_string_from_elf_section
+               (input_bfd, symtab_hdr->sh_link, sym->st_name);
+
+             signed_p = (ELF_ST_TYPE (sym->st_info) == STT_SRELC);
+           }
+         else
+           {
+             /* The symbol is global.  */
+             struct elf_link_hash_entry * h;
+
+             if (elf_bad_symtab (input_bfd))
+               continue;
+
+             h = sym_hashes [index - locsymcount];
+             while (   h->root.type == bfd_link_hash_indirect
+                    || h->root.type == bfd_link_hash_warning)
+               h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+             if (h->type != STT_RELC && h->type != STT_SRELC)
+               continue;
+
+             signed_p = (h->type == STT_SRELC);
+             sym_name = (char *) h->root.root.string;
+           }
+#ifdef DEBUG
+         printf ("Encountered a complex symbol!");
+         printf (" (input_bfd %s, section %s, reloc %ld\n",
+                 input_bfd->filename, reloc_sec->name, i);
+         printf (" symbol: idx  %8.8lx, name %s\n",
+                 index, sym_name);
+         printf (" reloc : info %8.8lx, addr %8.8lx\n",
+                 rel->r_info, addr);
+         printf (" Evaluating '%s' ...\n ", sym_name);
+#endif
+         if (eval_symbol (& result, sym_name, & sym_name, input_bfd, 
+                          finfo, addr, section_offset, locsymcount,
+                          signed_p))
+           /* Symbol evaluated OK.  Update to absolute value.  */
+           set_symbol_value (input_bfd, finfo, index, result);
+
+         else
+           result = FALSE;
+       }
+
+      if (internal_relocs != elf_section_data (reloc_sec)->relocs)
+       free (internal_relocs);
+    }
+
+  /* If nothing went wrong, then we adjusted 
+     everything we wanted to adjust.  */
+  return result;
+}
+
+static void
+put_value (bfd_vma        size,
+          unsigned long  chunksz,
+          bfd *          input_bfd,
+          bfd_vma        x,
+          bfd_byte *     location)
+{
+  location += (size - chunksz);
+
+  for (; size; size -= chunksz, location -= chunksz, x >>= (chunksz * 8)) 
+    {
+      switch (chunksz)
+       {
+       default:
+       case 0:
+         abort ();
+       case 1:
+         bfd_put_8 (input_bfd, x, location);
+         break;
+       case 2:
+         bfd_put_16 (input_bfd, x, location);
+         break;
+       case 4:
+         bfd_put_32 (input_bfd, x, location);
+         break;
+       case 8:
+#ifdef BFD64
+         bfd_put_64 (input_bfd, x, location);
+#else
+         abort ();
+#endif
+         break;
+       }
+    }
+}
+
+static bfd_vma 
+get_value (bfd_vma        size,
+          unsigned long  chunksz,
+          bfd *          input_bfd,
+          bfd_byte *     location)
+{
+  bfd_vma x = 0;
+
+  for (; size; size -= chunksz, location += chunksz) 
+    {
+      switch (chunksz)
+       {
+       default:
+       case 0:
+         abort ();
+       case 1:
+         x = (x << (8 * chunksz)) | bfd_get_8 (input_bfd, location);
+         break;
+       case 2:
+         x = (x << (8 * chunksz)) | bfd_get_16 (input_bfd, location);
+         break;
+       case 4:
+         x = (x << (8 * chunksz)) | bfd_get_32 (input_bfd, location);
+         break;
+       case 8:
+#ifdef BFD64
+         x = (x << (8 * chunksz)) | bfd_get_64 (input_bfd, location);
+#else
+         abort ();
+#endif
+         break;
+       }
+    }
+  return x;
+}
+
+static void 
+decode_complex_addend
+    (unsigned long * start,   /* in bits */
+     unsigned long * oplen,   /* in bits */
+     unsigned long * len,     /* in bits */
+     unsigned long * wordsz,  /* in bytes */
+     unsigned long * chunksz,  /* in bytes */
+     unsigned long * lsb0_p,
+     unsigned long * signed_p,
+     unsigned long * trunc_p,
+     unsigned long encoded)
+{
+  * start     =  encoded        & 0x3F;
+  * len       = (encoded >>  6) & 0x3F;
+  * oplen     = (encoded >> 12) & 0x3F;
+  * wordsz    = (encoded >> 18) & 0xF;
+  * chunksz   = (encoded >> 22) & 0xF;
+  * lsb0_p    = (encoded >> 27) & 1;
+  * signed_p  = (encoded >> 28) & 1;
+  * trunc_p   = (encoded >> 29) & 1;
+}
+
+void
+bfd_elf_perform_complex_relocation
+    (bfd *                   output_bfd ATTRIBUTE_UNUSED,
+     struct bfd_link_info *  info,
+     bfd *                   input_bfd,
+     asection *              input_section,
+     bfd_byte *              contents,
+     Elf_Internal_Rela *     rel,
+     Elf_Internal_Sym *      local_syms,
+     asection **             local_sections)
+{
+  const struct elf_backend_data * bed;
+  Elf_Internal_Shdr * symtab_hdr;
+  asection * sec;
+  bfd_vma relocation = 0, shift, x;
+  bfd_vma r_symndx;
+  bfd_vma mask;
+  unsigned long start, oplen, len, wordsz, 
+    chunksz, lsb0_p, signed_p, trunc_p;
+
+  /*  Perform this reloc, since it is complex.
+      (this is not to say that it necessarily refers to a complex
+      symbol; merely that it is a self-describing CGEN based reloc.
+      i.e. the addend has the complete reloc information (bit start, end,
+      word size, etc) encoded within it.).  */ 
+  r_symndx = ELF32_R_SYM (rel->r_info);
+  bed = get_elf_backend_data (input_bfd);
+  if (bed->s->arch_size == 64)
+    r_symndx >>= 24;
+
+#ifdef DEBUG
+  printf ("Performing complex relocation %ld...\n", r_symndx);
+#endif
+
+  symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
+  if (r_symndx < symtab_hdr->sh_info)
+    {
+      /* The symbol is local.  */
+      Elf_Internal_Sym * sym;
+
+      sym = local_syms + r_symndx;
+      sec = local_sections [r_symndx];
+      relocation = sym->st_value;
+      if (sym->st_shndx > SHN_UNDEF && 
+         sym->st_shndx < SHN_LORESERVE)
+       relocation += (sec->output_offset +
+                      sec->output_section->vma);
+    }
+  else
+    {
+      /* The symbol is global.  */
+      struct elf_link_hash_entry **sym_hashes;
+      struct elf_link_hash_entry * h;
+
+      sym_hashes = elf_sym_hashes (input_bfd);
+      h = sym_hashes [r_symndx];
+
+      while (h->root.type == bfd_link_hash_indirect
+            || h->root.type == bfd_link_hash_warning)
+       h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+      if (h->root.type == bfd_link_hash_defined
+         || h->root.type == bfd_link_hash_defweak)
+       {
+         sec = h->root.u.def.section;
+         relocation = h->root.u.def.value;
+
+         if (! bfd_is_abs_section (sec))
+           relocation += (sec->output_section->vma 
+                          + sec->output_offset); 
+       }
+      if (h->root.type == bfd_link_hash_undefined
+         && !((*info->callbacks->undefined_symbol)
+              (info, h->root.root.string, input_bfd,
+               input_section, rel->r_offset,
+               info->unresolved_syms_in_objects == RM_GENERATE_ERROR
+               || ELF_ST_VISIBILITY (h->other))))
+       return;
+    }
+
+  decode_complex_addend (& start, & oplen, & len, & wordsz, 
+                        & chunksz, & lsb0_p, & signed_p, 
+                        & trunc_p, rel->r_addend);
+
+  mask = (((1L << (len - 1)) - 1) << 1) | 1;
+
+  if (lsb0_p)
+    shift = (start + 1) - len;
+  else
+    shift = (8 * wordsz) - (start + len);
+
+  x = get_value (wordsz, chunksz, input_bfd, contents + rel->r_offset);          
+
+#ifdef DEBUG
+  printf ("Doing complex reloc: "
+         "lsb0? %ld, signed? %ld, trunc? %ld, wordsz %ld, "
+         "chunksz %ld, start %ld, len %ld, oplen %ld\n"
+         "    dest: %8.8lx, mask: %8.8lx, reloc: %8.8lx\n",
+         lsb0_p, signed_p, trunc_p, wordsz, chunksz, start, len,
+         oplen, x, mask,  relocation);
+#endif
+
+  if (! trunc_p)
+    {
+      /* Now do an overflow check.  */
+      if (bfd_check_overflow ((signed_p ? 
+                              complain_overflow_signed : 
+                              complain_overflow_unsigned),
+                             len, 0, (8 * wordsz), 
+                             relocation) == bfd_reloc_overflow)
+       (*_bfd_error_handler) 
+         ("%s (%s + 0x%lx): relocation overflow: 0x%lx %sdoes not fit "
+          "within 0x%lx", 
+          input_bfd->filename, input_section->name, rel->r_offset,
+          relocation, (signed_p ? "(signed) " : ""), mask);
+    }
+         
+  /* Do the deed.  */
+  x = (x & ~(mask << shift)) | ((relocation & mask) << shift);
+
+#ifdef DEBUG
+  printf ("           relocation: %8.8lx\n"
+         "         shifted mask: %8.8lx\n"
+         " shifted/masked reloc: %8.8lx\n"
+         "               result: %8.8lx\n",
+         relocation, (mask << shift), 
+         ((relocation & mask) << shift), x);
+#endif
+  put_value (wordsz, chunksz, input_bfd, x, contents + rel->r_offset);
+}
+
 /* When performing a relocatable link, the input relocations are
    preserved.  But, if they reference global symbols, the indices
    referenced must be updated.  Update all the relocations in
@@ -7358,6 +8089,15 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
       if (isymbuf == NULL)
        return FALSE;
     }
+  /* evaluate_complex_relocation_symbols looks for symbols in
+     finfo->internal_syms.  */
+  else if (isymbuf != NULL && locsymcount != 0)
+    {
+      bfd_elf_get_elf_syms (input_bfd, symtab_hdr, locsymcount, 0,
+                           finfo->internal_syms,
+                           finfo->external_syms,
+                           finfo->locsym_shndx);
+    }
 
   /* Find local symbol sections and adjust values of symbols in
      SEC_MERGE sections.  Write out those local symbols we know are
@@ -7495,6 +8235,9 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
        return FALSE;
     }
 
+  if (! evaluate_complex_relocation_symbols (input_bfd, finfo, locsymcount))
+    return FALSE;
+
   /* Relocate the contents of each section.  */
   sym_hashes = elf_sym_hashes (input_bfd);
   for (o = input_bfd->sections; o != NULL; o = o->next)
@@ -7651,16 +8394,6 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
                              continue;
                            }
                        }
-
-                     /* Remove the symbol reference from the reloc, but
-                        don't kill the reloc completely.  This is so that
-                        a zero value will be written into the section,
-                        which may have non-zero contents put there by the
-                        assembler.  Zero in things like an eh_frame fde
-                        pc_begin allows stack unwinders to recognize the
-                        fde as bogus.  */
-                     rel->r_info &= r_type_mask;
-                     rel->r_addend = 0;
                    }
                }
            }
@@ -7814,8 +8547,10 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
 
                          /* If we have discarded a section, the output
                             section will be the absolute section.  In
-                            case of discarded link-once and discarded
-                            SEC_MERGE sections, use the kept section.  */
+                            case of discarded SEC_MERGE sections, use
+                            the kept section.  relocate_section should
+                            have already handled discarded linkonce
+                            sections.  */
                          if (bfd_is_abs_section (osec)
                              && sec->kept_section != NULL
                              && sec->kept_section->output_section != NULL)
@@ -7940,7 +8675,8 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
 
       /* Write out the modified section contents.  */
       if (bed->elf_backend_write_section
-         && (*bed->elf_backend_write_section) (output_bfd, o, contents))
+         && (*bed->elf_backend_write_section) (output_bfd, finfo->info, o,
+                                               contents))
        {
          /* Section written out.  */
        }
@@ -9344,10 +10080,6 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 \f
 /* Garbage collect unused sections.  */
 
-typedef asection * (*gc_mark_hook_fn)
-  (asection *, struct bfd_link_info *, Elf_Internal_Rela *,
-   struct elf_link_hash_entry *, Elf_Internal_Sym *);
-
 /* Default gc_mark_hook.  */
 
 asection *
@@ -9385,7 +10117,7 @@ _bfd_elf_gc_mark_hook (asection *sec,
 bfd_boolean
 _bfd_elf_gc_mark (struct bfd_link_info *info,
                  asection *sec,
-                 gc_mark_hook_fn gc_mark_hook)
+                 elf_gc_mark_hook_fn gc_mark_hook)
 {
   bfd_boolean ret;
   bfd_boolean is_eh;
@@ -9754,9 +10486,7 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
 {
   bfd_boolean ok = TRUE;
   bfd *sub;
-  asection * (*gc_mark_hook)
-    (asection *, struct bfd_link_info *, Elf_Internal_Rela *,
-     struct elf_link_hash_entry *h, Elf_Internal_Sym *);
+  elf_gc_mark_hook_fn gc_mark_hook;
   const struct elf_backend_data *bed = get_elf_backend_data (abfd);
 
   if (!bed->can_gc_sections
@@ -9803,6 +10533,10 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
            return FALSE;
     }
 
+  /* Allow the backend to mark additional target specific sections.  */
+  if (bed->gc_mark_extra_sections)
+    bed->gc_mark_extra_sections(info, gc_mark_hook);
+
   /* ... again for sections marked from eh_frame.  */
   for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
     {
@@ -10201,12 +10935,15 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
       if ((abfd->flags & DYNAMIC) != 0)
        continue;
 
-      eh = bfd_get_section_by_name (abfd, ".eh_frame");
-      if (info->relocatable
-         || (eh != NULL
+      eh = NULL;
+      if (!info->relocatable)
+       {
+         eh = bfd_get_section_by_name (abfd, ".eh_frame");
+         if (eh != NULL
              && (eh->size == 0
-                 || bfd_is_abs_section (eh->output_section))))
-       eh = NULL;
+                 || bfd_is_abs_section (eh->output_section)))
+           eh = NULL;
+       }
 
       stab = bfd_get_section_by_name (abfd, ".stab");
       if (stab != NULL
This page took 0.036183 seconds and 4 git commands to generate.