Add icf_virtual_function_folding_test to check_PROGRAMS.
[binutils-gdb.git] / gold / archive.cc
index 7c82ca0aa3e9120b2d9b3af0ec78cae2d3df6da8..36e4862871e28e105b27b4eca97d1ddb0e03490a 100644 (file)
@@ -1,6 +1,6 @@
 // archive.cc -- archive support for gold
 
-// Copyright 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+// Copyright 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -83,15 +83,15 @@ const char Archive::armagt[sarmag] =
 
 const char Archive::arfmag[2] = { '`', '\n' };
 
-Archive::Archive(const std::string& aname, Input_file* ainput_file,
-                 bool is_a_thin_archive, Dirsearch* dirpath, Task* task)
-  : name_(aname), input_file_(ainput_file), armap_(), armap_names_(),
+Archive::Archive(const std::string& name, Input_file* input_file,
+                 bool is_thin_archive, Dirsearch* dirpath, Task* task)
+  : name_(name), input_file_(input_file), armap_(), armap_names_(),
     extended_names_(), armap_checked_(), seen_offsets_(), members_(),
-    is_thin_archive_(is_a_thin_archive), included_member_(false),
+    is_thin_archive_(is_thin_archive), included_member_(false),
     nested_archives_(), dirpath_(dirpath), task_(task), num_members_(0)
 {
   this->no_export_ =
-    parameters->options().check_excluded_libs(ainput_file->found_name());
+    parameters->options().check_excluded_libs(input_file->found_name());
 }
 
 // Set up the archive: read the symbol map and the extended name
@@ -247,9 +247,9 @@ Archive::interpret_header(const Archive_header* hdr, off_t off,
   *ps = '\0';
 
   errno = 0;
-  char* hend;
-  off_t member_size = strtol(size_string, &hend, 10);
-  if (*hend != '\0'
+  char* end;
+  off_t member_size = strtol(size_string, &end, 10);
+  if (*end != '\0'
       || member_size < 0
       || (member_size == LONG_MAX && errno == ERANGE))
     {
@@ -275,7 +275,8 @@ Archive::interpret_header(const Archive_header* hdr, off_t off,
   else if (hdr->ar_name[1] == ' ')
     {
       // This is the symbol table.
-      pname->clear();
+      if (!pname->empty())
+       pname->clear();
     }
   else if (hdr->ar_name[1] == '/')
     {
@@ -285,11 +286,11 @@ Archive::interpret_header(const Archive_header* hdr, off_t off,
   else
     {
       errno = 0;
-      long x = strtol(hdr->ar_name + 1, &hend, 10);
+      long x = strtol(hdr->ar_name + 1, &end, 10);
       long y = 0;
-      if (*hend == ':')
-        y = strtol(hend + 1, &hend, 10);
-      if (*hend != ' '
+      if (*end == ':')
+        y = strtol(end + 1, &end, 10);
+      if (*end != ' '
          || x < 0
          || (x == LONG_MAX && errno == ERANGE)
          || static_cast<size_t>(x) >= this->extended_names_.size())
@@ -299,16 +300,16 @@ Archive::interpret_header(const Archive_header* hdr, off_t off,
          return this->input_file_->file().filesize() - off;
        }
 
-      const char* name_start = this->extended_names_.data() + x;
-      const char* name_end = strchr(name_start, '\n');
-      if (static_cast<size_t>(name_end - name_start) > this->extended_names_.size()
+      const char* name = this->extended_names_.data() + x;
+      const char* name_end = strchr(name, '\n');
+      if (static_cast<size_t>(name_end - name) > this->extended_names_.size()
          || name_end[-1] != '/')
        {
          gold_error(_("%s: bad extended name entry at header %zu"),
                     this->name().c_str(), static_cast<size_t>(off));
          return this->input_file_->file().filesize() - off;
        }
-      pname->assign(name_start, name_end - 1 - name_start);
+      pname->assign(name, name_end - 1 - name);
       if (nested_off != NULL)
         *nested_off = y;
     }
@@ -452,14 +453,14 @@ Archive::end()
 // to the name of the archive member.  Return TRUE on success.
 
 bool
-Archive::get_file_and_offset(off_t off, Input_file** in_file, off_t* memoff,
+Archive::get_file_and_offset(off_t off, Input_file** input_file, off_t* memoff,
                              off_t* memsize, std::string* member_name)
 {
   off_t nested_off;
 
   *memsize = this->read_header(off, false, member_name, &nested_off);
 
-  *in_file = this->input_file_;
+  *input_file = this->input_file_;
   *memoff = off + static_cast<off_t>(sizeof(Archive_header));
 
   if (!this->is_thin_archive_)
@@ -492,18 +493,18 @@ Archive::get_file_and_offset(off_t off, Input_file** in_file, off_t* memoff,
             new Input_file_argument(member_name->c_str(),
                                     Input_file_argument::INPUT_FILE_TYPE_FILE,
                                     "", false, parameters->options());
-          *in_file = new Input_file(input_file_arg);
+          *input_file = new Input_file(input_file_arg);
          int dummy = 0;
-          if (!(*in_file)->open(*this->dirpath_, this->task_, &dummy))
+          if (!(*input_file)->open(*this->dirpath_, this->task_, &dummy))
             return false;
-          arch = new Archive(*member_name, *in_file, false, this->dirpath_,
+          arch = new Archive(*member_name, *input_file, false, this->dirpath_,
                              this->task_);
           arch->setup();
           std::pair<Nested_archive_table::iterator, bool> ins =
             this->nested_archives_.insert(std::make_pair(*member_name, arch));
           gold_assert(ins.second);
         }
-      return arch->get_file_and_offset(nested_off, in_file, memoff,
+      return arch->get_file_and_offset(nested_off, input_file, memoff,
                                       memsize, member_name);
     }
 
@@ -513,13 +514,13 @@ Archive::get_file_and_offset(off_t off, Input_file** in_file, off_t* memoff,
       new Input_file_argument(member_name->c_str(),
                               Input_file_argument::INPUT_FILE_TYPE_FILE,
                               "", false, this->input_file_->options());
-  *in_file = new Input_file(input_file_arg);
+  *input_file = new Input_file(input_file_arg);
   int dummy = 0;
-  if (!(*in_file)->open(*this->dirpath_, this->task_, &dummy))
+  if (!(*input_file)->open(*this->dirpath_, this->task_, &dummy))
     return false;
 
   *memoff = 0;
-  *memsize = (*in_file)->file().filesize();
+  *memsize = (*input_file)->file().filesize();
   return true;
 }
 
@@ -532,17 +533,17 @@ Archive::get_elf_object_for_member(off_t off, bool* punconfigured)
 {
   *punconfigured = false;
 
-  Input_file* in_file;
+  Input_file* input_file;
   off_t memoff;
   off_t memsize;
   std::string member_name;
-  if (!this->get_file_and_offset(off, &in_file, &memoff, &memsize,
+  if (!this->get_file_and_offset(off, &input_file, &memoff, &memsize,
                                 &member_name))
     return NULL;
 
   if (parameters->options().has_plugins())
     {
-      Object* obj = parameters->options().plugins()->claim_file(in_file,
+      Object* obj = parameters->options().plugins()->claim_file(input_file,
                                                                 memoff,
                                                                 memsize);
       if (obj != NULL)
@@ -555,7 +556,7 @@ Archive::get_elf_object_for_member(off_t off, bool* punconfigured)
 
   const unsigned char* ehdr;
   int read_size;
-  if (!is_elf_object(in_file, memoff, &ehdr, &read_size))
+  if (!is_elf_object(input_file, memoff, &ehdr, &read_size))
     {
       gold_error(_("%s: member at %zu is not an ELF object"),
                 this->name().c_str(), static_cast<size_t>(off));
@@ -564,7 +565,7 @@ Archive::get_elf_object_for_member(off_t off, bool* punconfigured)
 
   Object *obj = make_elf_object((std::string(this->input_file_->filename())
                                 + "(" + member_name + ")"),
-                               in_file, memoff, ehdr, read_size,
+                               input_file, memoff, ehdr, read_size,
                                punconfigured);
   if (obj == NULL)
     return NULL;
@@ -601,6 +602,68 @@ Archive::read_symbols(off_t off)
   this->members_[off] = member;
 }
 
+Archive::Should_include
+Archive::should_include_member(Symbol_table* symtab, const char* sym_name,
+                               Symbol** symp, std::string* why, char** tmpbufp,
+                               size_t* tmpbuflen)
+{
+  // In an object file, and therefore in an archive map, an
+  // '@' in the name separates the symbol name from the
+  // version name.  If there are two '@' characters, this is
+  // the default version.
+  char* tmpbuf = *tmpbufp;
+  const char* ver = strchr(sym_name, '@');
+  bool def = false;
+  if (ver != NULL)
+    {
+      size_t symlen = ver - sym_name;
+      if (symlen + 1 > *tmpbuflen)
+        {
+          tmpbuf = static_cast<char*>(xrealloc(tmpbuf, symlen + 1));
+          *tmpbufp = tmpbuf;
+          *tmpbuflen = symlen + 1;
+        }
+      memcpy(tmpbuf, sym_name, symlen);
+      tmpbuf[symlen] = '\0';
+      sym_name = tmpbuf;
+
+      ++ver;
+      if (*ver == '@')
+        {
+          ++ver;
+          def = true;
+        }
+    }
+
+  Symbol* sym = symtab->lookup(sym_name, ver);
+  if (def
+      && ver != NULL
+      && (sym == NULL
+          || !sym->is_undefined()
+          || sym->binding() == elfcpp::STB_WEAK))
+    sym = symtab->lookup(sym_name, NULL);
+
+  *symp = sym;
+
+  if (sym == NULL)
+    {
+      // Check whether the symbol was named in a -u option.
+      if (!parameters->options().is_undefined(sym_name))
+       return Archive::SHOULD_INCLUDE_UNKNOWN;
+      else
+        {
+          *why = "-u ";
+          *why += sym_name;
+        }
+    }
+  else if (!sym->is_undefined())
+    return Archive::SHOULD_INCLUDE_NO;
+  else if (sym->binding() == elfcpp::STB_WEAK)
+    return Archive::SHOULD_INCLUDE_UNKNOWN;
+
+  return Archive::SHOULD_INCLUDE_YES;
+}
+
 // Select members from the archive and add them to the link.  We walk
 // through the elements in the archive map, and look each one up in
 // the symbol table.  If it exists as a strong undefined symbol, we
@@ -660,64 +723,23 @@ Archive::add_symbols(Symbol_table* symtab, Layout* layout,
          const char* sym_name = (this->armap_names_.data()
                                  + this->armap_[i].name_offset);
 
-         // In an object file, and therefore in an archive map, an
-         // '@' in the name separates the symbol name from the
-         // version name.  If there are two '@' characters, this is
-         // the default version.
-         const char* ver = strchr(sym_name, '@');
-         bool def = false;
-         if (ver != NULL)
-           {
-             size_t symlen = ver - sym_name;
-             if (symlen + 1 > tmpbuflen)
-               {
-                 tmpbuf = static_cast<char*>(realloc(tmpbuf, symlen + 1));
-                 tmpbuflen = symlen + 1;
-               }
-             memcpy(tmpbuf, sym_name, symlen);
-             tmpbuf[symlen] = '\0';
-             sym_name = tmpbuf;
-
-             ++ver;
-             if (*ver == '@')
-               {
-                 ++ver;
-                 def = true;
-               }
-           }
+          Symbol* sym;
+          std::string why;
+          Archive::Should_include t =
+              Archive::should_include_member(symtab, sym_name, &sym, &why,
+                                             &tmpbuf, &tmpbuflen);
 
-         Symbol* sym = symtab->lookup(sym_name, ver);
-         if (def
-             && (sym == NULL
-                 || !sym->is_undefined()
-                 || sym->binding() == elfcpp::STB_WEAK))
-           sym = symtab->lookup(sym_name, NULL);
+         if (t == Archive::SHOULD_INCLUDE_NO
+              || t == Archive::SHOULD_INCLUDE_YES)
+           this->armap_checked_[i] = true;
 
-         if (sym == NULL)
-           {
-             // Check whether the symbol was named in a -u option.
-             if (!parameters->options().is_undefined(sym_name))
-               continue;
-           }
-         else if (!sym->is_undefined())
-           {
-              this->armap_checked_[i] = true;
-             continue;
-           }
-         else if (sym->binding() == elfcpp::STB_WEAK)
+         if (t != Archive::SHOULD_INCLUDE_YES)
            continue;
 
          // We want to include this object in the link.
          last_seen_offset = this->armap_[i].file_offset;
          this->seen_offsets_.insert(last_seen_offset);
-          this->armap_checked_[i] = true;
 
-         std::string why;
-         if (sym == NULL)
-           {
-             why = "-u ";
-             why += sym_name;
-           }
          if (!this->include_member(symtab, layout, input_objects,
                                    last_seen_offset, mapfile, sym,
                                    why.c_str()))
@@ -855,10 +877,12 @@ Archive::include_member(Symbol_table* symtab, Layout* layout,
     delete obj;
   else
     {
-      Read_symbols_data sd;
-      obj->read_symbols(&sd);
-      obj->layout(symtab, layout, &sd);
-      obj->add_symbols(symtab, &sd, layout);
+      {
+       Read_symbols_data sd;
+       obj->read_symbols(&sd);
+       obj->layout(symtab, layout, &sd);
+       obj->add_symbols(symtab, &sd, layout);
+      }
 
       // If this is an external member of a thin archive, unlock the file
       // for the next task.
@@ -947,4 +971,145 @@ Add_archive_symbols::run(Workqueue* workqueue)
     }
 }
 
+// Class Lib_group static variables.
+unsigned int Lib_group::total_lib_groups;
+unsigned int Lib_group::total_members;
+unsigned int Lib_group::total_members_loaded;
+
+Lib_group::Lib_group(const Input_file_lib* lib, Task* task)
+  : lib_(lib), task_(task), members_()
+{
+  this->members_.resize(lib->size());
+}
+
+// Select members from the lib group and add them to the link.  We walk
+// through the the members, and check if each one up should be included.
+// If the object says it should be included, we do so.  We have to do
+// this in a loop, since including one member may create new undefined
+// symbols which may be satisfied by other members.
+
+void
+Lib_group::add_symbols(Symbol_table* symtab, Layout* layout,
+                       Input_objects* input_objects)
+{
+  ++Lib_group::total_lib_groups;
+
+  Lib_group::total_members += this->members_.size();
+
+  bool added_new_object;
+  do
+    {
+      added_new_object = false;
+      unsigned int i = 0;
+      while (i < this->members_.size())
+       {
+         const Archive_member& member = this->members_[i];
+         Object *obj = member.obj_;
+         std::string why;
+
+          // Skip files with no symbols. Plugin objects have
+          // member.sd_ == NULL.
+          if (obj != NULL
+             && (member.sd_ == NULL || member.sd_->symbol_names != NULL))
+            {
+             Archive::Should_include t = obj->should_include_member(symtab,
+                                                                    member.sd_,
+                                                                    &why);
+
+             if (t != Archive::SHOULD_INCLUDE_YES)
+               {
+                 ++i;
+                 continue;
+               }
+
+             this->include_member(symtab, layout, input_objects, member);
+
+             added_new_object = true;
+           }
+          else
+            {
+              if (member.sd_ != NULL)
+                delete member.sd_;
+            }
+
+         this->members_[i] = this->members_.back();
+         this->members_.pop_back();
+       }
+    }
+  while (added_new_object);
+}
+
+// Include a lib group member in the link.
+
+void
+Lib_group::include_member(Symbol_table* symtab, Layout* layout,
+                         Input_objects* input_objects,
+                         const Archive_member& member)
+{
+  ++Lib_group::total_members_loaded;
+
+  Object* obj = member.obj_;
+  gold_assert(obj != NULL);
+
+  Pluginobj* pluginobj = obj->pluginobj();
+  if (pluginobj != NULL)
+    {
+      pluginobj->add_symbols(symtab, NULL, layout);
+      return;
+    }
+
+  Read_symbols_data* sd = member.sd_;
+  gold_assert(sd != NULL);
+  obj->lock(this->task_);
+  if (input_objects->add_object(obj))
+    {
+      obj->layout(symtab, layout, sd);
+      obj->add_symbols(symtab, sd, layout);
+      // Unlock the file for the next task.
+      obj->unlock(this->task_);
+    }
+  delete sd;
+}
+
+// Print statistical information to stderr.  This is used for --stats.
+
+void
+Lib_group::print_stats()
+{
+  fprintf(stderr, _("%s: lib groups: %u\n"),
+          program_name, Lib_group::total_lib_groups);
+  fprintf(stderr, _("%s: total lib groups members: %u\n"),
+          program_name, Lib_group::total_members);
+  fprintf(stderr, _("%s: loaded lib groups members: %u\n"),
+          program_name, Lib_group::total_members_loaded);
+}
+
+Task_token*
+Add_lib_group_symbols::is_runnable()
+{
+  if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked())
+    return this->this_blocker_;
+  return NULL;
+}
+
+void
+Add_lib_group_symbols::locks(Task_locker* tl)
+{
+  tl->add(this, this->next_blocker_);
+}
+
+void
+Add_lib_group_symbols::run(Workqueue*)
+{
+  this->lib_->add_symbols(this->symtab_, this->layout_, this->input_objects_);
+}
+
+Add_lib_group_symbols::~Add_lib_group_symbols()
+{
+  if (this->this_blocker_ != NULL)
+    delete this->this_blocker_;
+  // next_blocker_ is deleted by the task associated with the next
+  // input file.
+}
+
 } // End namespace gold.