mach-o cleanup: remove useless calls to bfd_seek.
[deliverable/binutils-gdb.git] / ld / ldexp.c
index f02f576698ebab50663f65af2a9f93a052f843c9..2ec8eb6d19892b5751afcacf9839181acd20cfd5 100644 (file)
@@ -49,13 +49,25 @@ segment_type *segments;
 struct ldexp_control expld;
 
 /* This structure records symbols for which we need to keep track of
-   definedness for use in the DEFINED () test.  */
+   definedness for use in the DEFINED () test.  It is also used in
+   making absolute symbols section relative late in the link.   */
 
 struct definedness_hash_entry
 {
   struct bfd_hash_entry root;
+
+  /* If this symbol was assigned from "dot" outside of an output
+     section statement, the section we'd like it relative to.  */
+  asection *final_sec;
+
+  /* Symbol was defined by an object file.  */
   unsigned int by_object : 1;
+
+  /* Symbols was defined by a script.  */
   unsigned int by_script : 1;
+
+  /* Low bit of iteration count.  Symbols with matching iteration have
+     been defined in this pass over the script.  */
   unsigned int iteration : 1;
 };
 
@@ -174,6 +186,7 @@ make_abs (void)
   if (expld.result.section != NULL)
     expld.result.value += expld.result.section->vma;
   expld.result.section = bfd_abs_section_ptr;
+  expld.rel_from_abs = FALSE;
 }
 
 static void
@@ -249,22 +262,13 @@ new_rel_from_abs (bfd_vma value)
 {
   asection *s = expld.section;
 
-  if (s == bfd_abs_section_ptr && expld.phase == lang_final_phase_enum)
-    s = section_for_dot ();
+  expld.rel_from_abs = TRUE;
   expld.result.valid_p = TRUE;
   expld.result.value = value - s->vma;
   expld.result.str = NULL;
   expld.result.section = s;
 }
 
-static void
-align_dot_val (bfd_vma align)
-{
-  bfd_vma base = expld.section->vma;
-
-  new_rel_from_abs (base + align_n (expld.dot - base, align));
-}
-
 /* New-function for the definedness hash table.  */
 
 static struct bfd_hash_entry *
@@ -330,6 +334,11 @@ update_definedness (const char *name, struct bfd_link_hash_entry *h)
 
   defentry->by_script = 1;
   defentry->iteration = lang_statement_iteration;
+  defentry->final_sec = bfd_abs_section_ptr;
+  if (expld.phase == lang_final_phase_enum
+      && expld.rel_from_abs
+      && expld.result.section == bfd_abs_section_ptr)
+    defentry->final_sec = section_for_dot ();
   return ret;
 }
 
@@ -343,7 +352,7 @@ fold_unary (etree_type *tree)
        {
        case ALIGN_K:
          if (expld.phase != lang_first_phase_enum)
-           align_dot_val (expld.result.value);
+           new_rel_from_abs (align_n (expld.dot, expld.result.value));
          else
            expld.result.valid_p = FALSE;
          break;
@@ -373,7 +382,7 @@ fold_unary (etree_type *tree)
          if (expld.phase != lang_first_phase_enum)
            {
              make_abs ();
-             align_dot_val (expld.result.value);
+             expld.result.value = align_n (expld.dot, expld.result.value);
            }
          else
            expld.result.valid_p = FALSE;
@@ -885,7 +894,7 @@ fold_name (etree_type *tree)
 }
 
 /* Return true if TREE is '.'.  */
+
 static bfd_boolean
 is_dot (const etree_type *tree)
 {
@@ -951,28 +960,37 @@ is_dot_plus_0 (const etree_type *tree)
              || is_sym_value (tree->binary.rhs, 0)));
 }
 
-/* Return true if TREE is "ALIGN (. != 0 ? some_expression : 1)",
-   or equivalent binary ALIGN expressions.  */
+/* Return true if TREE is "ALIGN (. != 0 ? some_expression : 1)".  */
 
 static bfd_boolean
 is_align_conditional (const etree_type *tree)
 {
-  if (tree->type.node_code != ALIGN_K)
-    return 0;
-  else if (tree->type.node_class == etree_unary)
-    tree = tree->unary.child;
-  else if (tree->type.node_class == etree_binary
-          && (is_dot (tree->binary.lhs)
-              || (tree->binary.lhs->type.node_class == etree_unary
-                  && tree->binary.lhs->type.node_code == ABSOLUTE
-                  && is_dot (tree->binary.lhs->unary.child))))
-    tree = tree->binary.rhs;
-  else
-    return 0;
+  if (tree->type.node_class == etree_unary
+      && tree->type.node_code == ALIGN_K)
+    {
+      tree = tree->unary.child;
+      return (tree->type.node_class == etree_trinary
+             && is_dot_ne_0 (tree->trinary.cond)
+             && is_value (tree->trinary.rhs, 1));
+    }
+  return FALSE;
+}
 
-  return (tree->type.node_class == etree_trinary
-         && is_dot_ne_0 (tree->trinary.cond)
-         && is_value (tree->trinary.rhs, 1));
+/* Subroutine of exp_fold_tree_1 for copying a symbol type.  */
+
+static void
+try_copy_symbol_type (struct bfd_link_hash_entry * h, etree_type *src)
+{
+  if (src->type.node_class == etree_name)
+    {
+      struct bfd_link_hash_entry *hsrc;
+
+      hsrc = bfd_link_hash_lookup (link_info.hash, src->name.name,
+                                   FALSE, FALSE, TRUE);
+      if (hsrc)
+       bfd_copy_link_hash_symbol_type (link_info.output_bfd, h,
+                                                   hsrc);
+    }
 }
 
 static void
@@ -1039,13 +1057,30 @@ exp_fold_tree_1 (etree_type *tree)
              exp_fold_tree_1 (tree->assign.src);
              expld.assigning_to_dot = FALSE;
 
+             /* If we are assigning to dot inside an output section
+                arrange to keep the section, except for certain
+                expressions that evaluate to zero.  We ignore . = 0,
+                . = . + 0, and . = ALIGN (. != 0 ? expr : 1).
+                We can't ignore all expressions that evaluate to zero
+                because an otherwise empty section might have padding
+                added by an alignment expression that changes with
+                relaxation.  Such a section might have zero size
+                before relaxation and so be stripped incorrectly.  */
+             if (expld.phase == lang_mark_phase_enum
+                 && expld.section != bfd_abs_section_ptr
+                 && !(expld.result.valid_p
+                      && expld.result.value == 0
+                      && (is_value (tree->assign.src, 0)
+                          || is_sym_value (tree->assign.src, 0)
+                          || is_dot_plus_0 (tree->assign.src)
+                          || is_align_conditional (tree->assign.src))))
+               expld.section->flags |= SEC_KEEP;
+
              if (!expld.result.valid_p)
                {
                  if (expld.phase != lang_mark_phase_enum)
                    einfo (_("%F%S invalid assignment to"
                             " location counter\n"), tree);
-                 else if (expld.section != bfd_abs_section_ptr)
-                   expld.section->flags |= SEC_KEEP;
                }
              else if (expld.dotp == NULL)
                einfo (_("%F%S assignment to location counter"
@@ -1065,25 +1100,6 @@ exp_fold_tree_1 (etree_type *tree)
                    nextdot += expld.result.section->vma;
                  else
                    nextdot += expld.section->vma;
-
-                 /* If we are assigning to dot inside an output
-                    section arrange to keep the section, except for
-                    certain expressions that evaluate to zero.  We
-                    can't ignore all expressions that evaluate to
-                    zero because an otherwise empty section might
-                    have padding added by an alignment expression
-                    that changes with relaxation.  Such a section
-                    might have zero size before relaxation and so be
-                    stripped incorrectly.  */
-                 if (expld.phase == lang_mark_phase_enum
-                     && expld.section != bfd_abs_section_ptr
-                     && !(nextdot == expld.section->vma
-                          && (is_value (tree->assign.src, 0)
-                              || is_sym_value (tree->assign.src, 0)
-                              || is_dot_plus_0 (tree->assign.src)
-                              || is_align_conditional (tree->assign.src))))
-                   expld.section->flags |= SEC_KEEP;
-
                  if (nextdot < expld.dot
                      && expld.section != bfd_abs_section_ptr)
                    einfo (_("%F%S cannot move location counter backwards"
@@ -1110,11 +1126,14 @@ exp_fold_tree_1 (etree_type *tree)
              if (h == NULL
                  || !(h->type == bfd_link_hash_new
                       || h->type == bfd_link_hash_undefined
+                      || h->type == bfd_link_hash_undefweak
                       || h->linker_def))
                {
                  /* Do nothing.  The symbol was never referenced, or
-                    was defined in some object file.  Undefined weak
-                    symbols stay undefined.  */
+                    was defined in some object file.  Note that
+                    undefweak symbols are defined by PROVIDE.  This
+                    is to support glibc use of __rela_iplt_start and
+                    similar weak references.  */
                  break;
                }
            }
@@ -1162,22 +1181,30 @@ exp_fold_tree_1 (etree_type *tree)
              h->type = bfd_link_hash_defined;
              h->u.def.value = expld.result.value;
              h->u.def.section = expld.result.section;
+             h->linker_def = 0;
              if (tree->type.node_class == etree_provide)
                tree->type.node_class = etree_provided;
 
              /* Copy the symbol type if this is a simple assignment of
-                one symbol to another.  This could be more general
-                (e.g. a ?: operator with NAMEs in each branch).  */
+                one symbol to another.  Also, handle the case of a foldable
+                ternary conditional with names on either side.  */
              if (tree->assign.src->type.node_class == etree_name)
+               try_copy_symbol_type (h, tree->assign.src);
+             else if (tree->assign.src->type.node_class == etree_trinary)
                {
-                 struct bfd_link_hash_entry *hsrc;
-
-                 hsrc = bfd_link_hash_lookup (link_info.hash,
-                                              tree->assign.src->name.name,
-                                              FALSE, FALSE, TRUE);
-                 if (hsrc)
-                   bfd_copy_link_hash_symbol_type (link_info.output_bfd, h,
-                                                   hsrc);
+                 exp_fold_tree_1 (tree->assign.src->trinary.cond);
+                 if (expld.result.valid_p)
+                   {
+                     if (expld.result.value
+                         && tree->assign.src->trinary.lhs->type.node_class
+                            == etree_name)
+                       try_copy_symbol_type (h, tree->assign.src->trinary.lhs);
+
+                     if (!expld.result.value
+                         && tree->assign.src->trinary.rhs->type.node_class
+                            == etree_name)
+                       try_copy_symbol_type (h, tree->assign.src->trinary.rhs);
+                   }
                }
            }
          else if (expld.phase == lang_final_phase_enum)
@@ -1206,6 +1233,7 @@ exp_fold_tree_1 (etree_type *tree)
 void
 exp_fold_tree (etree_type *tree, asection *current_section, bfd_vma *dotp)
 {
+  expld.rel_from_abs = FALSE;
   expld.dot = *dotp;
   expld.dotp = dotp;
   expld.section = current_section;
@@ -1215,6 +1243,7 @@ exp_fold_tree (etree_type *tree, asection *current_section, bfd_vma *dotp)
 void
 exp_fold_tree_no_dot (etree_type *tree)
 {
+  expld.rel_from_abs = FALSE;
   expld.dot = 0;
   expld.dotp = NULL;
   expld.section = bfd_abs_section_ptr;
@@ -1598,6 +1627,36 @@ ldexp_init (void)
     einfo (_("%P%F: can not create hash table: %E\n"));
 }
 
+/* Convert absolute symbols defined by a script from "dot" (also
+   SEGMENT_START or ORIGIN) outside of an output section statement,
+   to section relative.  */
+
+static bfd_boolean
+set_sym_sections (struct bfd_hash_entry *bh, void *inf ATTRIBUTE_UNUSED)
+{
+  struct definedness_hash_entry *def = (struct definedness_hash_entry *) bh;
+  if (def->final_sec != bfd_abs_section_ptr)
+    {
+      struct bfd_link_hash_entry *h;
+      h = bfd_link_hash_lookup (link_info.hash, bh->string,
+                               FALSE, FALSE, TRUE);
+      if (h != NULL
+         && h->type == bfd_link_hash_defined
+         && h->u.def.section == bfd_abs_section_ptr)
+       {
+         h->u.def.value -= def->final_sec->vma;
+         h->u.def.section = def->final_sec;
+       }
+    }
+  return TRUE;
+}
+
+void
+ldexp_finalize_syms (void)
+{
+  bfd_hash_traverse (&definedness_table, set_sym_sections, NULL);
+}
+
 void
 ldexp_finish (void)
 {
This page took 0.026726 seconds and 4 git commands to generate.