gdb: add target_ops::supports_displaced_step
[deliverable/binutils-gdb.git] / gold / symtab.cc
index c6b47b05ca050399d1f6ab92f0618c8f726c3fa8..205444b5ea0e8404ff3771725b723a05ac6d6eed 100644 (file)
@@ -1,6 +1,6 @@
 // symtab.cc -- the gold symbol table
 
-// Copyright (C) 2006-2015 Free Software Foundation, Inc.
+// Copyright (C) 2006-2020 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -47,8 +47,8 @@ namespace gold
 
 // Class Symbol.
 
-// Initialize fields in Symbol.  This initializes everything except u_
-// and source_.
+// Initialize fields in Symbol.  This initializes everything except
+// u1_, u2_ and source_.
 
 void
 Symbol::init_fields(const char* name, const char* version,
@@ -80,6 +80,8 @@ Symbol::init_fields(const char* name, const char* version,
   this->undef_binding_set_ = false;
   this->undef_binding_weak_ = false;
   this->is_predefined_ = false;
+  this->is_protected_ = false;
+  this->non_zero_localentry_ = false;
 }
 
 // Return the demangled version of the symbol's name, but only
@@ -118,8 +120,8 @@ Symbol::init_base_object(const char* name, const char* version, Object* object,
 {
   this->init_fields(name, version, sym.get_st_type(), sym.get_st_bind(),
                    sym.get_st_visibility(), sym.get_st_nonvis());
-  this->u_.from_object.object = object;
-  this->u_.from_object.shndx = st_shndx;
+  this->u1_.object = object;
+  this->u2_.shndx = st_shndx;
   this->is_ordinary_shndx_ = is_ordinary;
   this->source_ = FROM_OBJECT;
   this->in_reg_ = !object->is_dynamic();
@@ -138,8 +140,8 @@ Symbol::init_base_output_data(const char* name, const char* version,
                              bool is_predefined)
 {
   this->init_fields(name, version, type, binding, visibility, nonvis);
-  this->u_.in_output_data.output_data = od;
-  this->u_.in_output_data.offset_is_from_end = offset_is_from_end;
+  this->u1_.output_data = od;
+  this->u2_.offset_is_from_end = offset_is_from_end;
   this->source_ = IN_OUTPUT_DATA;
   this->in_reg_ = true;
   this->in_real_elf_ = true;
@@ -158,8 +160,8 @@ Symbol::init_base_output_segment(const char* name, const char* version,
                                 bool is_predefined)
 {
   this->init_fields(name, version, type, binding, visibility, nonvis);
-  this->u_.in_output_segment.output_segment = os;
-  this->u_.in_output_segment.offset_base = offset_base;
+  this->u1_.output_segment = os;
+  this->u2_.offset_base = offset_base;
   this->source_ = IN_OUTPUT_SEGMENT;
   this->in_reg_ = true;
   this->in_real_elf_ = true;
@@ -204,8 +206,8 @@ Symbol::allocate_base_common(Output_data* od)
 {
   gold_assert(this->is_common());
   this->source_ = IN_OUTPUT_DATA;
-  this->u_.in_output_data.output_data = od;
-  this->u_.in_output_data.offset_is_from_end = false;
+  this->u1_.output_data = od;
+  this->u2_.offset_is_from_end = false;
 }
 
 // Initialize the fields in Sized_symbol for SYM in OBJECT.
@@ -285,11 +287,12 @@ Sized_symbol<size>::init_constant(const char* name, const char* version,
 template<int size>
 void
 Sized_symbol<size>::init_undefined(const char* name, const char* version,
-                                  elfcpp::STT type, elfcpp::STB binding,
-                                  elfcpp::STV visibility, unsigned char nonvis)
+                                  Value_type value, elfcpp::STT type,
+                                  elfcpp::STB binding, elfcpp::STV visibility,
+                                  unsigned char nonvis)
 {
   this->init_base_undefined(name, version, type, binding, visibility, nonvis);
-  this->value_ = 0;
+  this->value_ = value;
   this->symsize_ = 0;
 }
 
@@ -485,19 +488,19 @@ Symbol::output_section() const
     {
     case FROM_OBJECT:
       {
-       unsigned int shndx = this->u_.from_object.shndx;
+       unsigned int shndx = this->u2_.shndx;
        if (shndx != elfcpp::SHN_UNDEF && this->is_ordinary_shndx_)
          {
-           gold_assert(!this->u_.from_object.object->is_dynamic());
-           gold_assert(this->u_.from_object.object->pluginobj() == NULL);
-           Relobj* relobj = static_cast<Relobj*>(this->u_.from_object.object);
+           gold_assert(!this->u1_.object->is_dynamic());
+           gold_assert(this->u1_.object->pluginobj() == NULL);
+           Relobj* relobj = static_cast<Relobj*>(this->u1_.object);
            return relobj->output_section(shndx);
          }
        return NULL;
       }
 
     case IN_OUTPUT_DATA:
-      return this->u_.in_output_data.output_data->output_section();
+      return this->u1_.output_data->output_section();
 
     case IN_OUTPUT_SEGMENT:
     case IS_CONSTANT:
@@ -524,8 +527,8 @@ Symbol::set_output_section(Output_section* os)
       break;
     case IS_CONSTANT:
       this->source_ = IN_OUTPUT_DATA;
-      this->u_.in_output_data.output_data = os;
-      this->u_.in_output_data.offset_is_from_end = false;
+      this->u1_.output_data = os;
+      this->u2_.offset_is_from_end = false;
       break;
     case IN_OUTPUT_SEGMENT:
     case IS_UNDEFINED:
@@ -543,8 +546,8 @@ Symbol::set_output_segment(Output_segment* os, Segment_offset_base base)
 {
   gold_assert(this->is_predefined_);
   this->source_ = IN_OUTPUT_SEGMENT;
-  this->u_.in_output_segment.output_segment = os;
-  this->u_.in_output_segment.offset_base = base;
+  this->u1_.output_segment = os;
+  this->u2_.offset_base = base;
 }
 
 // Set the symbol to undefined.  This is used for pre-defined
@@ -562,10 +565,11 @@ Symbol::set_undefined()
 
 Symbol_table::Symbol_table(unsigned int count,
                            const Version_script_info& version_script)
-  : saw_undefined_(0), offset_(0), table_(count), namepool_(),
-    forwarders_(), commons_(), tls_commons_(), small_commons_(),
+  : saw_undefined_(0), offset_(0), has_gnu_output_(false), table_(count),
+    namepool_(), forwarders_(), commons_(), tls_commons_(), small_commons_(),
     large_commons_(), forced_locals_(), warnings_(),
-    version_script_(version_script), gc_(NULL), icf_(NULL)
+    version_script_(version_script), gc_(NULL), icf_(NULL),
+    target_symbols_()
 {
   namepool_.reserve(count);
 }
@@ -863,6 +867,9 @@ Symbol_table::define_default_version(Sized_symbol<size>* sym,
       // other is defined in a shared object, then they are different
       // symbols.
 
+      // If the two symbols are from different shared objects,
+      // they are different symbols.
+
       // Otherwise, we just resolve the symbols as though they were
       // the same.
 
@@ -874,6 +881,11 @@ Symbol_table::define_default_version(Sized_symbol<size>* sym,
       else if (pdef->second->visibility() != elfcpp::STV_DEFAULT
               && sym->is_from_dynobj())
        ;
+      else if (pdef->second->is_from_dynobj()
+              && sym->is_from_dynobj()
+              && pdef->second->is_defined()
+              && pdef->second->object() != sym->object())
+        ;
       else
        {
          const Sized_symbol<size>* symdef;
@@ -977,8 +989,8 @@ Symbol_table::add_from_object(Object* object,
   // ins.first->second: the value (Symbol*).
   // ins.second: true if new entry was inserted, false if not.
 
-  Sized_symbol<size>* ret;
-  bool was_undefined;
+  Sized_symbol<size>* ret = NULL;
+  bool was_undefined_in_reg;
   bool was_common;
   if (!ins.second)
     {
@@ -986,7 +998,7 @@ Symbol_table::add_from_object(Object* object,
       ret = this->get_sized_symbol<size>(ins.first->second);
       gold_assert(ret != NULL);
 
-      was_undefined = ret->is_undefined();
+      was_undefined_in_reg = ret->is_undefined() && ret->in_reg();
       // Commons from plugins are just placeholders.
       was_common = ret->is_common() && ret->object()->pluginobj() == NULL;
 
@@ -1037,19 +1049,44 @@ Symbol_table::add_from_object(Object* object,
          // it, then change it to NAME/VERSION.
          ret = this->get_sized_symbol<size>(insdefault.first->second);
 
-         was_undefined = ret->is_undefined();
-         // Commons from plugins are just placeholders.
-         was_common = ret->is_common() && ret->object()->pluginobj() == NULL;
-
-         this->resolve(ret, sym, st_shndx, is_ordinary, orig_st_shndx, object,
-                       version, is_default_version);
-          if (parameters->options().gc_sections())
-            this->gc_mark_dyn_syms(ret);
-         ins.first->second = ret;
+         // If the existing symbol already has a version,
+         // don't override it with the new symbol.
+         // This should only happen when the new symbol
+         // is from a shared library.
+         if (ret->version() != NULL)
+           {
+             if (!object->is_dynamic())
+               {
+                 gold_warning(_("%s: conflicting default version definition"
+                                " for %s@@%s"),
+                              object->name().c_str(), name, version);
+                 if (ret->source() == Symbol::FROM_OBJECT)
+                   gold_info(_("%s: %s: previous definition of %s@@%s here"),
+                             program_name,
+                             ret->object()->name().c_str(),
+                             name, ret->version());
+               }
+             ret = NULL;
+             is_default_version = false;
+           }
+         else
+           {
+             was_undefined_in_reg = ret->is_undefined() && ret->in_reg();
+             // Commons from plugins are just placeholders.
+             was_common = (ret->is_common()
+                           && ret->object()->pluginobj() == NULL);
+
+             this->resolve(ret, sym, st_shndx, is_ordinary, orig_st_shndx,
+                           object, version, is_default_version);
+             if (parameters->options().gc_sections())
+               this->gc_mark_dyn_syms(ret);
+             ins.first->second = ret;
+           }
        }
-      else
+
+      if (ret == NULL)
        {
-         was_undefined = false;
+         was_undefined_in_reg = false;
          was_common = false;
 
          Sized_target<size, big_endian>* target =
@@ -1058,7 +1095,8 @@ Symbol_table::add_from_object(Object* object,
            ret = new Sized_symbol<size>();
          else
            {
-             ret = target->make_symbol();
+             ret = target->make_symbol(name, sym.get_st_type(), object,
+                                       st_shndx, sym.get_st_value());
              if (ret == NULL)
                {
                  // This means that we don't want a symbol table
@@ -1092,9 +1130,10 @@ Symbol_table::add_from_object(Object* object,
        ret->set_is_default();
     }
 
-  // Record every time we see a new undefined symbol, to speed up
-  // archive groups.
-  if (!was_undefined && ret->is_undefined())
+  // Record every time we see a new undefined symbol, to speed up archive
+  // groups. We only care about symbols undefined in regular objects here
+  // because undefined symbols only in dynamic objects should't trigger rescans.
+  if (!was_undefined_in_reg && ret->is_undefined() && ret->in_reg())
     {
       ++this->saw_undefined_;
       if (parameters->options().has_plugins())
@@ -1171,7 +1210,10 @@ Symbol_table::add_from_relobj(
 
       const char* name = sym_names + st_name;
 
-      if (strcmp (name, "__gnu_lto_slim") == 0)
+      if (!parameters->options().relocatable()
+         && name[0] == '_'
+         && name[1] == '_'
+         && strcmp (name + (name[2] == '_'), "__gnu_lto_slim") == 0)
         gold_info(_("%s: plugin needed to handle lto object"),
                  relobj->name().c_str());
 
@@ -1313,6 +1355,9 @@ Symbol_table::add_from_relobj(
       res = this->add_from_object(relobj, name, name_key, ver, ver_key,
                                  is_default_version, *psym, st_shndx,
                                  is_ordinary, orig_st_shndx);
+
+      if (res == NULL)
+       continue;
       
       if (is_forced_local)
        this->force_local(res);
@@ -1394,6 +1439,9 @@ Symbol_table::add_from_pluginobj(
                              is_default_version, *sym, st_shndx,
                              is_ordinary, st_shndx);
 
+  if (res == NULL)
+    return NULL;
+
   if (is_forced_local)
     this->force_local(res);
 
@@ -1468,14 +1516,20 @@ Symbol_table::add_from_dynobj(
       // A protected symbol in a shared library must be treated as a
       // normal symbol when viewed from outside the shared library.
       // Implement this by overriding the visibility here.
+      // Likewise, an IFUNC symbol in a shared library must be treated
+      // as a normal FUNC symbol.
       elfcpp::Sym<size, big_endian>* psym = &sym;
       unsigned char symbuf[sym_size];
       elfcpp::Sym<size, big_endian> sym2(symbuf);
-      if (sym.get_st_visibility() == elfcpp::STV_PROTECTED)
+      if (sym.get_st_visibility() == elfcpp::STV_PROTECTED
+         || sym.get_st_type() == elfcpp::STT_GNU_IFUNC)
        {
          memcpy(symbuf, p, sym_size);
          elfcpp::Sym_write<size, big_endian> sw(symbuf);
-         sw.put_st_other(elfcpp::STV_DEFAULT, sym.get_st_nonvis());
+         if (sym.get_st_visibility() == elfcpp::STV_PROTECTED)
+           sw.put_st_other(elfcpp::STV_DEFAULT, sym.get_st_nonvis());
+         if (sym.get_st_type() == elfcpp::STT_GNU_IFUNC)
+           sw.put_st_info(sym.get_st_bind(), elfcpp::STT_FUNC);
          psym = &sym2;
        }
 
@@ -1584,6 +1638,9 @@ Symbol_table::add_from_dynobj(
            }
        }
 
+      if (res == NULL)
+       continue;
+
       // Note that it is possible that RES was overridden by an
       // earlier object, in which case it can't be aliased here.
       if (st_shndx != elfcpp::SHN_UNDEF
@@ -1593,6 +1650,13 @@ Symbol_table::add_from_dynobj(
          && res->object() == dynobj)
        object_symbols.push_back(res);
 
+      // If the symbol has protected visibility in the dynobj,
+      // mark it as such if it was not overridden.
+      if (res->source() == Symbol::FROM_OBJECT
+          && res->object() == dynobj
+          && sym.get_st_visibility() == elfcpp::STV_PROTECTED)
+        res->set_is_protected();
+
       if (sympointers != NULL)
        (*sympointers)[i] = res;
     }
@@ -1615,7 +1679,6 @@ Symbol_table::add_from_incrobj(
 
   Stringpool::Key ver_key = 0;
   bool is_default_version = false;
-  bool is_forced_local = false;
 
   Stringpool::Key name_key;
   name = this->namepool_.add(name, true, &name_key);
@@ -1625,9 +1688,6 @@ Symbol_table::add_from_incrobj(
                              is_default_version, *sym, st_shndx,
                              is_ordinary, st_shndx);
 
-  if (is_forced_local)
-    this->force_local(res);
-
   return res;
 }
 
@@ -1727,8 +1787,9 @@ template<int size, bool big_endian>
 Sized_symbol<size>*
 Symbol_table::define_special_symbol(const char** pname, const char** pversion,
                                    bool only_if_ref,
+                                   elfcpp::STV visibility,
                                     Sized_symbol<size>** poldsym,
-                                   bool* resolve_oldsym)
+                                   bool* resolve_oldsym, bool is_forced_local)
 {
   *resolve_oldsym = false;
   *poldsym = NULL;
@@ -1737,7 +1798,7 @@ Symbol_table::define_special_symbol(const char** pname, const char** pversion,
   // the version script.
   std::string v;
   bool is_default_version = false;
-  if (*pversion == NULL)
+  if (!is_forced_local && *pversion == NULL)
     {
       bool is_global;
       if (this->version_script_.get_symbol_version(*pname, &v, &is_global))
@@ -1765,8 +1826,21 @@ Symbol_table::define_special_symbol(const char** pname, const char** pversion,
       oldsym = this->lookup(*pname, *pversion);
       if (oldsym == NULL && is_default_version)
        oldsym = this->lookup(*pname, NULL);
-      if (oldsym == NULL || !oldsym->is_undefined())
+      if (oldsym == NULL)
        return NULL;
+      if (!oldsym->is_undefined())
+       {
+         // Skip if the old definition is from a regular object.
+         if (!oldsym->is_from_dynobj())
+           return NULL;
+
+         // If the symbol has hidden or internal visibility, ignore
+         // definition and reference from a dynamic object.
+         if ((visibility == elfcpp::STV_HIDDEN
+              || visibility == elfcpp::STV_INTERNAL)
+             && !oldsym->in_reg())
+           return NULL;
+       }
 
       *pname = oldsym->name();
       if (is_default_version)
@@ -1824,10 +1898,13 @@ Symbol_table::define_special_symbol(const char** pname, const char** pversion,
          add_to_table = true;
          add_loc = ins.first;
 
-         if (is_default_version && !insdefault.second)
+         if (is_default_version
+             && !insdefault.second
+             && insdefault.first->second->version() == NULL)
            {
              // We are adding NAME/VERSION, and it is the default
-             // version.  We already have an entry for NAME/NULL.
+             // version.  We already have an entry for NAME/NULL
+             // that does not already have a version.
              oldsym = insdefault.first->second;
              *resolve_oldsym = true;
            }
@@ -1851,7 +1928,8 @@ Symbol_table::define_special_symbol(const char** pname, const char** pversion,
     {
       Sized_target<size, big_endian>* sized_target =
        parameters->sized_target<size, big_endian>();
-      sym = sized_target->make_symbol();
+      sym = sized_target->make_symbol(*pname, elfcpp::STT_NOTYPE,
+                                     NULL, elfcpp::SHN_UNDEF, 0);
       if (sym == NULL)
         return NULL;
     }
@@ -1934,13 +2012,17 @@ Symbol_table::do_define_in_output_data(
   Sized_symbol<size>* sym;
   Sized_symbol<size>* oldsym;
   bool resolve_oldsym;
+  const bool is_forced_local = binding == elfcpp::STB_LOCAL;
 
   if (parameters->target().is_big_endian())
     {
 #if defined(HAVE_TARGET_32_BIG) || defined(HAVE_TARGET_64_BIG)
       sym = this->define_special_symbol<size, true>(&name, &version,
-                                                   only_if_ref, &oldsym,
-                                                   &resolve_oldsym);
+                                                   only_if_ref,
+                                                   visibility,
+                                                   &oldsym,
+                                                   &resolve_oldsym,
+                                                   is_forced_local);
 #else
       gold_unreachable();
 #endif
@@ -1949,8 +2031,11 @@ Symbol_table::do_define_in_output_data(
     {
 #if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_64_LITTLE)
       sym = this->define_special_symbol<size, false>(&name, &version,
-                                                    only_if_ref, &oldsym,
-                                                    &resolve_oldsym);
+                                                    only_if_ref,
+                                                    visibility,
+                                                    &oldsym,
+                                                    &resolve_oldsym,
+                                                    is_forced_local);
 #else
       gold_unreachable();
 #endif
@@ -1965,8 +2050,7 @@ Symbol_table::do_define_in_output_data(
 
   if (oldsym == NULL)
     {
-      if (binding == elfcpp::STB_LOCAL
-         || this->version_script_.symbol_is_local(name))
+      if (is_forced_local || this->version_script_.symbol_is_local(name))
        this->force_local(sym);
       else if (version != NULL)
        sym->set_is_default();
@@ -1980,8 +2064,8 @@ Symbol_table::do_define_in_output_data(
     return sym;
   else
     {
-      if (binding == elfcpp::STB_LOCAL
-         || this->version_script_.symbol_is_local(name))
+      if (defined == PREDEFINED
+         && (is_forced_local || this->version_script_.symbol_is_local(name)))
        this->force_local(oldsym);
       delete sym;
       return oldsym;
@@ -2051,13 +2135,17 @@ Symbol_table::do_define_in_output_segment(
   Sized_symbol<size>* sym;
   Sized_symbol<size>* oldsym;
   bool resolve_oldsym;
+  const bool is_forced_local = binding == elfcpp::STB_LOCAL;
 
   if (parameters->target().is_big_endian())
     {
 #if defined(HAVE_TARGET_32_BIG) || defined(HAVE_TARGET_64_BIG)
       sym = this->define_special_symbol<size, true>(&name, &version,
-                                                   only_if_ref, &oldsym,
-                                                   &resolve_oldsym);
+                                                   only_if_ref,
+                                                   visibility,
+                                                   &oldsym,
+                                                   &resolve_oldsym,
+                                                   is_forced_local);
 #else
       gold_unreachable();
 #endif
@@ -2066,8 +2154,11 @@ Symbol_table::do_define_in_output_segment(
     {
 #if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_64_LITTLE)
       sym = this->define_special_symbol<size, false>(&name, &version,
-                                                    only_if_ref, &oldsym,
-                                                    &resolve_oldsym);
+                                                    only_if_ref,
+                                                    visibility,
+                                                    &oldsym,
+                                                    &resolve_oldsym,
+                                                    is_forced_local);
 #else
       gold_unreachable();
 #endif
@@ -2082,8 +2173,7 @@ Symbol_table::do_define_in_output_segment(
 
   if (oldsym == NULL)
     {
-      if (binding == elfcpp::STB_LOCAL
-         || this->version_script_.symbol_is_local(name))
+      if (is_forced_local || this->version_script_.symbol_is_local(name))
        this->force_local(sym);
       else if (version != NULL)
        sym->set_is_default();
@@ -2097,8 +2187,7 @@ Symbol_table::do_define_in_output_segment(
     return sym;
   else
     {
-      if (binding == elfcpp::STB_LOCAL
-         || this->version_script_.symbol_is_local(name))
+      if (is_forced_local || this->version_script_.symbol_is_local(name))
        this->force_local(oldsym);
       delete sym;
       return oldsym;
@@ -2167,13 +2256,17 @@ Symbol_table::do_define_as_constant(
   Sized_symbol<size>* sym;
   Sized_symbol<size>* oldsym;
   bool resolve_oldsym;
+  const bool is_forced_local = binding == elfcpp::STB_LOCAL;
 
   if (parameters->target().is_big_endian())
     {
 #if defined(HAVE_TARGET_32_BIG) || defined(HAVE_TARGET_64_BIG)
       sym = this->define_special_symbol<size, true>(&name, &version,
-                                                   only_if_ref, &oldsym,
-                                                   &resolve_oldsym);
+                                                   only_if_ref,
+                                                   visibility,
+                                                   &oldsym,
+                                                   &resolve_oldsym,
+                                                   is_forced_local);
 #else
       gold_unreachable();
 #endif
@@ -2182,8 +2275,11 @@ Symbol_table::do_define_as_constant(
     {
 #if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_64_LITTLE)
       sym = this->define_special_symbol<size, false>(&name, &version,
-                                                    only_if_ref, &oldsym,
-                                                    &resolve_oldsym);
+                                                    only_if_ref,
+                                                    visibility,
+                                                    &oldsym,
+                                                    &resolve_oldsym,
+                                                    is_forced_local);
 #else
       gold_unreachable();
 #endif
@@ -2202,8 +2298,7 @@ Symbol_table::do_define_as_constant(
       if ((version == NULL
           || name != version
           || value != 0)
-         && (binding == elfcpp::STB_LOCAL
-             || this->version_script_.symbol_is_local(name)))
+         && (is_forced_local || this->version_script_.symbol_is_local(name)))
        this->force_local(sym);
       else if (version != NULL
               && (name != version || value != 0))
@@ -2219,8 +2314,7 @@ Symbol_table::do_define_as_constant(
     return sym;
   else
     {
-      if (binding == elfcpp::STB_LOCAL
-         || this->version_script_.symbol_is_local(name))
+      if (is_forced_local || this->version_script_.symbol_is_local(name))
        this->force_local(oldsym);
       delete sym;
       return oldsym;
@@ -2410,8 +2504,11 @@ Symbol_table::add_undefined_symbol_from_command_line(const char* name)
     {
 #if defined(HAVE_TARGET_32_BIG) || defined(HAVE_TARGET_64_BIG)
       sym = this->define_special_symbol<size, true>(&name, &version,
-                                                   false, &oldsym,
-                                                   &resolve_oldsym);
+                                                   false,
+                                                   elfcpp::STV_DEFAULT,
+                                                   &oldsym,
+                                                   &resolve_oldsym,
+                                                   false);
 #else
       gold_unreachable();
 #endif
@@ -2420,8 +2517,11 @@ Symbol_table::add_undefined_symbol_from_command_line(const char* name)
     {
 #if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_64_LITTLE)
       sym = this->define_special_symbol<size, false>(&name, &version,
-                                                    false, &oldsym,
-                                                    &resolve_oldsym);
+                                                    false,
+                                                    elfcpp::STV_DEFAULT,
+                                                    &oldsym,
+                                                    &resolve_oldsym,
+                                                    false);
 #else
       gold_unreachable();
 #endif
@@ -2429,23 +2529,47 @@ Symbol_table::add_undefined_symbol_from_command_line(const char* name)
 
   gold_assert(oldsym == NULL);
 
-  sym->init_undefined(name, version, elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
+  sym->init_undefined(name, version, 0, elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
                      elfcpp::STV_DEFAULT, 0);
   ++this->saw_undefined_;
 }
 
 // Set the dynamic symbol indexes.  INDEX is the index of the first
-// global dynamic symbol.  Pointers to the symbols are stored into the
-// vector SYMS.  The names are added to DYNPOOL.  This returns an
-// updated dynamic symbol index.
+// global dynamic symbol.  Pointers to the global symbols are stored
+// into the vector SYMS.  The names are added to DYNPOOL.
+// This returns an updated dynamic symbol index.
 
 unsigned int
 Symbol_table::set_dynsym_indexes(unsigned int index,
+                                unsigned int* pforced_local_count,
                                 std::vector<Symbol*>* syms,
                                 Stringpool* dynpool,
                                 Versions* versions)
 {
-  std::vector<Symbol*> as_needed_sym;
+  // First process all the symbols which have been forced to be local,
+  // as they must appear before all global symbols.
+  unsigned int forced_local_count = 0;
+  for (Forced_locals::iterator p = this->forced_locals_.begin();
+       p != this->forced_locals_.end();
+       ++p)
+    {
+      Symbol* sym = *p;
+      gold_assert(sym->is_forced_local());
+      if (sym->has_dynsym_index())
+        continue;
+      if (!sym->should_add_dynsym_entry(this))
+       sym->set_dynsym_index(-1U);
+      else
+        {
+          sym->set_dynsym_index(index);
+          ++index;
+          ++forced_local_count;
+         dynpool->add(sym->name(), false, NULL);
+         if (sym->type() == elfcpp::STT_GNU_IFUNC)
+           this->set_has_gnu_output();
+        }
+    }
+  *pforced_local_count = forced_local_count;
 
   // Allow a target to set dynsym indexes.
   if (parameters->target().has_custom_set_dynsym_indexes())
@@ -2456,10 +2580,18 @@ Symbol_table::set_dynsym_indexes(unsigned int index,
            ++p)
         {
           Symbol* sym = p->second;
+          if (sym->is_forced_local())
+           continue;
           if (!sym->should_add_dynsym_entry(this))
             sym->set_dynsym_index(-1U);
           else
-            dyn_symbols.push_back(sym);
+           {
+             dyn_symbols.push_back(sym);
+             if (sym->type() == elfcpp::STT_GNU_IFUNC
+                 || (sym->binding() == elfcpp::STB_GNU_UNIQUE
+                     && parameters->options().gnu_unique()))
+               this->set_has_gnu_output();
+           }
         }
 
       return parameters->target().set_dynsym_indexes(&dyn_symbols, index, syms,
@@ -2472,6 +2604,9 @@ Symbol_table::set_dynsym_indexes(unsigned int index,
     {
       Symbol* sym = p->second;
 
+      if (sym->is_forced_local())
+        continue;
+
       // Note that SYM may already have a dynamic symbol index, since
       // some symbols appear more than once in the symbol table, with
       // and without a version.
@@ -2484,15 +2619,10 @@ Symbol_table::set_dynsym_indexes(unsigned int index,
          ++index;
          syms->push_back(sym);
          dynpool->add(sym->name(), false, NULL);
-
-         // If the symbol is defined in a dynamic object and is
-         // referenced strongly in a regular object, then mark the
-         // dynamic object as needed.  This is used to implement
-         // --as-needed.
-         if (sym->is_from_dynobj()
-             && sym->in_reg()
-             && !sym->is_undef_binding_weak())
-           sym->object()->set_is_needed();
+         if (sym->type() == elfcpp::STT_GNU_IFUNC
+             || (sym->binding() == elfcpp::STB_GNU_UNIQUE
+                 && parameters->options().gnu_unique()))
+           this->set_has_gnu_output();
 
          // Record any version information, except those from
          // as-needed libraries not seen to be needed.  Note that the
@@ -2504,35 +2634,46 @@ Symbol_table::set_dynsym_indexes(unsigned int index,
                  || sym->object()->is_needed())
                versions->record_version(this, dynpool, sym);
              else
-               as_needed_sym.push_back(sym);
+               {
+                 if (parameters->options().warn_drop_version())
+                   gold_warning(_("discarding version information for "
+                                  "%s@%s, defined in unused shared library %s "
+                                  "(linked with --as-needed)"),
+                                sym->name(), sym->version(),
+                                sym->object()->name().c_str());
+                 sym->clear_version();
+               }
            }
        }
     }
 
-  // Process version information for symbols from as-needed libraries.
-  for (std::vector<Symbol*>::iterator p = as_needed_sym.begin();
-       p != as_needed_sym.end();
-       ++p)
-    {
-      Symbol* sym = *p;
-
-      if (sym->object()->is_needed())
-       versions->record_version(this, dynpool, sym);
-      else
-       sym->clear_version();
-    }
-
   // Finish up the versions.  In some cases this may add new dynamic
   // symbols.
   index = versions->finalize(this, index, syms);
 
+  // Process target-specific symbols.
+  for (std::vector<Symbol*>::iterator p = this->target_symbols_.begin();
+       p != this->target_symbols_.end();
+       ++p)
+    {
+      (*p)->set_dynsym_index(index);
+      ++index;
+      syms->push_back(*p);
+      dynpool->add((*p)->name(), false, NULL);
+    }
+
   return index;
 }
 
 // Set the final values for all the symbols.  The index of the first
 // global symbol in the output file is *PLOCAL_SYMCOUNT.  Record the
 // file offset OFF.  Add their names to POOL.  Return the new file
-// offset.  Update *PLOCAL_SYMCOUNT if necessary.
+// offset.  Update *PLOCAL_SYMCOUNT if necessary.  DYNOFF and
+// DYN_GLOBAL_INDEX refer to the start of the symbols that will be
+// written from the global symbol table in Symtab::write_globals(),
+// which will include forced-local symbols.  DYN_GLOBAL_INDEX is
+// not necessarily the same as the sh_info field for the .dynsym
+// section, which will point to the first real global symbol.
 
 off_t
 Symbol_table::finalize(off_t off, off_t dynoff, size_t dyn_global_index,
@@ -2567,6 +2708,13 @@ Symbol_table::finalize(off_t off, off_t dynoff, size_t dyn_global_index,
   else
     gold_unreachable();
 
+  if (this->has_gnu_output_)
+    {
+      Target* target = const_cast<Target*>(&parameters->target());
+      if (target->osabi() == elfcpp::ELFOSABI_NONE)
+       target->set_osabi(elfcpp::ELFOSABI_GNU);
+    }
+
   // Now that we have the final symbol table, we can reliably note
   // which symbols should get warnings.
   this->warnings_.note_warnings(this);
@@ -2618,6 +2766,8 @@ Symbol_table::sized_finalize(off_t off, Stringpool* pool,
        {
          this->add_to_final_symtab<size>(sym, pool, &index, &off);
          ++*plocal_symcount;
+         if (sym->type() == elfcpp::STT_GNU_IFUNC)
+           this->set_has_gnu_output();
        }
     }
 
@@ -2628,7 +2778,21 @@ Symbol_table::sized_finalize(off_t off, Stringpool* pool,
     {
       Symbol* sym = p->second;
       if (this->sized_finalize_symbol<size>(sym))
-       this->add_to_final_symtab<size>(sym, pool, &index, &off);
+       {
+         this->add_to_final_symtab<size>(sym, pool, &index, &off);
+         if (sym->type() == elfcpp::STT_GNU_IFUNC
+             || (sym->binding() == elfcpp::STB_GNU_UNIQUE
+                 && parameters->options().gnu_unique()))
+           this->set_has_gnu_output();
+       }
+    }
+
+  // Now do target-specific symbols.
+  for (std::vector<Symbol*>::iterator p = this->target_symbols_.begin();
+       p != this->target_symbols_.end();
+       ++p)
+    {
+      this->add_to_final_symtab<size>(*p, pool, &index, &off);
     }
 
   this->output_count_ = index - orig_index;
@@ -3059,12 +3223,23 @@ Symbol_table::sized_write_globals(const Stringpool* sympool,
            // In object files symbol values are section
            // relative.
            if (parameters->options().relocatable())
-             sym_value -= od->address();
+             {
+               Output_section* os = od->output_section();
+               gold_assert(os != NULL);
+               sym_value -= os->address();
+             }
          }
          break;
 
        case Symbol::IN_OUTPUT_SEGMENT:
-         shndx = elfcpp::SHN_ABS;
+         {
+           Output_segment* oseg = sym->output_segment();
+           Output_section* osect = oseg->first_section();
+           if (osect == NULL)
+             shndx = elfcpp::SHN_ABS;
+           else
+             shndx = osect->out_shndx();
+         }
          break;
 
        case Symbol::IS_CONSTANT:
@@ -3100,6 +3275,54 @@ Symbol_table::sized_write_globals(const Stringpool* sympool,
        }
     }
 
+  // Write the target-specific symbols.
+  for (std::vector<Symbol*>::const_iterator p = this->target_symbols_.begin();
+       p != this->target_symbols_.end();
+       ++p)
+    {
+      Sized_symbol<size>* sym = static_cast<Sized_symbol<size>*>(*p);
+
+      unsigned int sym_index = sym->symtab_index();
+      unsigned int dynsym_index;
+      if (dynamic_view == NULL)
+       dynsym_index = -1U;
+      else
+       dynsym_index = sym->dynsym_index();
+
+      unsigned int shndx;
+      switch (sym->source())
+       {
+       case Symbol::IS_CONSTANT:
+         shndx = elfcpp::SHN_ABS;
+         break;
+       case Symbol::IS_UNDEFINED:
+         shndx = elfcpp::SHN_UNDEF;
+         break;
+       default:
+         gold_unreachable();
+       }
+
+      if (sym_index != -1U)
+       {
+         sym_index -= first_global_index;
+         gold_assert(sym_index < output_count);
+         unsigned char* ps = psyms + (sym_index * sym_size);
+         this->sized_write_symbol<size, big_endian>(sym, sym->value(), shndx,
+                                                    sym->binding(), sympool,
+                                                    ps);
+       }
+
+      if (dynsym_index != -1U)
+       {
+         dynsym_index -= first_dynamic_global_index;
+         gold_assert(dynsym_index < dynamic_count);
+         unsigned char* pd = dynamic_view + (dynsym_index * sym_size);
+         this->sized_write_symbol<size, big_endian>(sym, sym->value(), shndx,
+                                                    sym->binding(), dynpool,
+                                                    pd);
+       }
+    }
+
   of->write_output_view(this->offset_, oview_size, psyms);
   if (dynamic_view != NULL)
     of->write_output_view(this->dynamic_offset_, dynamic_size, dynamic_view);
@@ -3130,10 +3353,7 @@ Symbol_table::sized_write_symbol(
   else
     osym.put_st_size(sym->symsize());
   elfcpp::STT type = sym->type();
-  // Turn IFUNC symbols from shared libraries into normal FUNC symbols.
-  if (type == elfcpp::STT_GNU_IFUNC
-      && sym->is_from_dynobj())
-    type = elfcpp::STT_FUNC;
+  gold_assert(type != elfcpp::STT_GNU_IFUNC || !sym->is_from_dynobj());
   // A version script may have overridden the default binding.
   if (sym->is_forced_local())
     osym.put_st_info(elfcpp::elf_st_info(elfcpp::STB_LOCAL, type));
@@ -3741,6 +3961,21 @@ Sized_symbol<32>::init_output_data(const char* name, const char* version,
                                   unsigned char nonvis,
                                   bool offset_is_from_end,
                                   bool is_predefined);
+
+template
+void
+Sized_symbol<32>::init_constant(const char* name, const char* version,
+                               Value_type value, Size_type symsize,
+                               elfcpp::STT type, elfcpp::STB binding,
+                               elfcpp::STV visibility, unsigned char nonvis,
+                               bool is_predefined);
+
+template
+void
+Sized_symbol<32>::init_undefined(const char* name, const char* version,
+                                Value_type value, elfcpp::STT type,
+                                elfcpp::STB binding, elfcpp::STV visibility,
+                                unsigned char nonvis);
 #endif
 
 #if defined(HAVE_TARGET_64_LITTLE) || defined(HAVE_TARGET_64_BIG)
@@ -3754,6 +3989,21 @@ Sized_symbol<64>::init_output_data(const char* name, const char* version,
                                   unsigned char nonvis,
                                   bool offset_is_from_end,
                                   bool is_predefined);
+
+template
+void
+Sized_symbol<64>::init_constant(const char* name, const char* version,
+                               Value_type value, Size_type symsize,
+                               elfcpp::STT type, elfcpp::STB binding,
+                               elfcpp::STV visibility, unsigned char nonvis,
+                               bool is_predefined);
+
+template
+void
+Sized_symbol<64>::init_undefined(const char* name, const char* version,
+                                Value_type value, elfcpp::STT type,
+                                elfcpp::STB binding, elfcpp::STV visibility,
+                                unsigned char nonvis);
 #endif
 
 #ifdef HAVE_TARGET_32_LITTLE
This page took 0.035437 seconds and 4 git commands to generate.