// archive.cc -- archive support for gold
-// Copyright 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+// Copyright 2006, 2007, 2008, 2009, 2010, 2011, 2013
+// Free Software Foundation, Inc.
// Written by Ian Lance Taylor <iant@google.com>.
// This file is part of gold.
namespace gold
{
+// Library_base methods.
+
+// Determine whether a definition of SYM_NAME should cause an archive
+// library member to be included in the link. Returns SHOULD_INCLUDE_YES
+// if the symbol is referenced but not defined, SHOULD_INCLUDE_NO if the
+// symbol is already defined, and SHOULD_INCLUDE_UNKNOWN if the symbol is
+// neither referenced nor defined.
+
+Library_base::Should_include
+Library_base::should_include_member(Symbol_table* symtab, Layout* layout,
+ 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))
+ {
+ *why = "-u ";
+ *why += sym_name;
+ }
+ else if (parameters->options().is_export_dynamic_symbol(sym_name))
+ {
+ *why = "--export-dynamic-symbol ";
+ *why += sym_name;
+ }
+ else if (layout->script_options()->is_referenced(sym_name))
+ {
+ size_t alc = 100 + strlen(sym_name);
+ char* buf = new char[alc];
+ snprintf(buf, alc, _("script or expression reference to %s"),
+ sym_name);
+ *why = buf;
+ delete[] buf;
+ }
+ else if (strcmp(sym_name, parameters->entry()) == 0)
+ {
+ *why = "entry symbol ";
+ *why += sym_name;
+ }
+ else
+ return Library_base::SHOULD_INCLUDE_UNKNOWN;
+ }
+ else if (!sym->is_undefined())
+ return Library_base::SHOULD_INCLUDE_NO;
+ // PR 12001: Do not include an archive when the undefined
+ // symbol has actually been defined on the command line.
+ else if (layout->script_options()->is_pending_assignment(sym_name))
+ return Library_base::SHOULD_INCLUDE_NO;
+ else if (sym->binding() == elfcpp::STB_WEAK)
+ return Library_base::SHOULD_INCLUDE_UNKNOWN;
+
+ return Library_base::SHOULD_INCLUDE_YES;
+}
+
// The header of an entry in the archive. This is all readable text,
-// padded with spaces where necesary. If the contents of an archive
+// padded with spaces where necessary. If the contents of an archive
// are all text file, the entire archive is readable.
struct Archive::Archive_header
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_thin_archive), included_member_(false),
- nested_archives_(), dirpath_(dirpath), task_(task), num_members_(0),
- incremental_info_(NULL)
+ : Library_base(task), name_(name), input_file_(input_file), armap_(),
+ armap_names_(), extended_names_(), armap_checked_(), seen_offsets_(),
+ members_(), is_thin_archive_(is_thin_archive), included_member_(false),
+ nested_archives_(), dirpath_(dirpath), num_members_(0),
+ included_all_members_(false)
{
this->no_export_ =
parameters->options().check_excluded_libs(input_file->found_name());
// The first member of the archive should be the symbol table.
std::string armap_name;
- section_size_type armap_size =
- convert_to_section_size_type(this->read_header(sarmag, false,
- &armap_name, NULL));
+ off_t header_size = this->read_header(sarmag, false, &armap_name, NULL);
+ if (header_size == -1)
+ return;
+
+ section_size_type armap_size = convert_to_section_size_type(header_size);
off_t off = sarmag;
if (armap_name.empty())
{
if ((off & 1) != 0)
++off;
std::string xname;
- section_size_type extended_size =
- convert_to_section_size_type(this->read_header(off, true, &xname, NULL));
+ header_size = this->read_header(off, true, &xname, NULL);
+ if (header_size == -1)
+ return;
+
+ section_size_type extended_size = convert_to_section_size_type(header_size);
if (xname == "/")
{
const unsigned char* p = this->get_view(off + sizeof(Archive_header),
}
// Interpret the header of HDR, the header of the archive member at
-// file offset OFF. Fail if something goes wrong. Return the size of
-// the member. Set *PNAME to the name of the member.
+// file offset OFF. Return the size of the member, or -1 if something
+// has gone wrong. Set *PNAME to the name of the member.
off_t
Archive::interpret_header(const Archive_header* hdr, off_t off,
{
gold_error(_("%s: malformed archive header at %zu"),
this->name().c_str(), static_cast<size_t>(off));
- return this->input_file_->file().filesize() - off;
+ return -1;
}
const int size_string_size = sizeof hdr->ar_size;
{
gold_error(_("%s: malformed archive header size at %zu"),
this->name().c_str(), static_cast<size_t>(off));
- return this->input_file_->file().filesize() - off;
+ return -1;
}
if (hdr->ar_name[0] != '/')
{
gold_error(_("%s: malformed archive header name at %zu"),
this->name().c_str(), static_cast<size_t>(off));
- return this->input_file_->file().filesize() - off;
+ return -1;
}
pname->assign(hdr->ar_name, name_end - hdr->ar_name);
if (nested_off != NULL)
{
gold_error(_("%s: bad extended name index at %zu"),
this->name().c_str(), static_cast<size_t>(off));
- return this->input_file_->file().filesize() - off;
+ return -1;
}
const char* name = this->extended_names_.data() + x;
{
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;
+ return -1;
}
pname->assign(name, name_end - 1 - name);
if (nested_off != NULL)
this->archive_->file().read(this->off_, sizeof(Archive_header), buf);
const Archive_header* hdr = reinterpret_cast<const Archive_header*>(buf);
- this->header_.size =
- this->archive_->interpret_header(hdr, this->off_, &this->header_.name,
- &this->header_.nested_off);
+ off_t size = this->archive_->interpret_header(hdr, this->off_,
+ &this->header_.name,
+ &this->header_.nested_off);
+ if (size == -1)
+ {
+ this->header_.off = filesize;
+ return;
+ }
+
+ this->header_.size = size;
this->header_.off = this->off_;
// Skip special members.
off_t nested_off;
*memsize = this->read_header(off, false, member_name, &nested_off);
+ if (*memsize == -1)
+ return false;
*input_file = this->input_file_;
*memoff = off + static_cast<off_t>(sizeof(Archive_header));
return true;
}
-// Return an ELF object for the member at offset OFF. If the ELF
-// object has an unsupported target type, set *PUNCONFIGURED to true
-// and return NULL.
+// Return an ELF object for the member at offset OFF. If
+// PUNCONFIGURED is not NULL, then if the ELF object has an
+// unsupported target type, set *PUNCONFIGURED to true and return
+// NULL.
Object*
Archive::get_elf_object_for_member(off_t off, bool* punconfigured)
{
- *punconfigured = false;
+ if (punconfigured != NULL)
+ *punconfigured = false;
Input_file* input_file;
off_t memoff;
&member_name))
return NULL;
+ const unsigned char* ehdr;
+ int read_size;
+ Object *obj = NULL;
+ bool is_elf_obj = false;
+
+ if (is_elf_object(input_file, memoff, &ehdr, &read_size))
+ {
+ obj = make_elf_object((std::string(this->input_file_->filename())
+ + "(" + member_name + ")"),
+ input_file, memoff, ehdr, read_size,
+ punconfigured);
+ is_elf_obj = true;
+ }
+
if (parameters->options().has_plugins())
{
- Object* obj = parameters->options().plugins()->claim_file(input_file,
- memoff,
- memsize);
- if (obj != NULL)
+ Object* plugin_obj
+ = parameters->options().plugins()->claim_file(input_file,
+ memoff,
+ memsize,
+ obj);
+ if (plugin_obj != NULL)
{
// The input file was claimed by a plugin, and its symbols
// have been provided by the plugin.
- return obj;
+ // Delete its elf object.
+ if (obj != NULL)
+ delete obj;
+ return plugin_obj;
}
}
- const unsigned char* ehdr;
- int read_size;
- if (!is_elf_object(input_file, memoff, &ehdr, &read_size))
+ if (!is_elf_obj)
{
gold_error(_("%s: member at %zu is not an ELF object"),
this->name().c_str(), static_cast<size_t>(off));
return NULL;
}
- Object* obj = make_elf_object((std::string(this->input_file_->filename())
- + "(" + member_name + ")"),
- input_file, memoff, ehdr, read_size,
- punconfigured);
if (obj == NULL)
return NULL;
obj->set_no_export(this->no_export());
void
Archive::read_symbols(off_t off)
{
- bool dummy;
- Object* obj = this->get_elf_object_for_member(off, &dummy);
-
+ Object* obj = this->get_elf_object_for_member(off, NULL);
if (obj == NULL)
return;
this->members_[off] = member;
}
-Archive::Should_include
-Archive::should_include_member(Symbol_table* symtab, Layout* layout,
- 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))
- {
- *why = "-u ";
- *why += sym_name;
- }
- else if (layout->script_options()->is_referenced(sym_name))
- {
- size_t alc = 100 + strlen(sym_name);
- char* buf = new char[alc];
- snprintf(buf, alc, _("script or expression reference to %s"),
- sym_name);
- *why = buf;
- delete[] buf;
- }
- else
- return Archive::SHOULD_INCLUDE_UNKNOWN;
- }
- 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
return true;
}
+// Return whether the archive includes a member which defines the
+// symbol SYM.
+
+bool
+Archive::defines_symbol(Symbol* sym) const
+{
+ const char* symname = sym->name();
+ size_t symname_len = strlen(symname);
+ size_t armap_size = this->armap_.size();
+ for (size_t i = 0; i < armap_size; ++i)
+ {
+ if (this->armap_checked_[i])
+ continue;
+ const char* archive_symname = (this->armap_names_.data()
+ + this->armap_[i].name_offset);
+ if (strncmp(archive_symname, symname, symname_len) != 0)
+ continue;
+ char c = archive_symname[symname_len];
+ if (c == '\0' && sym->version() == NULL)
+ return true;
+ if (c == '@')
+ {
+ const char* ver = archive_symname + symname_len + 1;
+ if (*ver == '@')
+ {
+ if (sym->version() == NULL)
+ return true;
+ ++ver;
+ }
+ if (sym->version() != NULL && strcmp(sym->version(), ver) == 0)
+ return true;
+ }
+ }
+ return false;
+}
+
// Include all the archive members in the link. This is for --whole-archive.
bool
Archive::include_all_members(Symbol_table* symtab, Layout* layout,
Input_objects* input_objects, Mapfile* mapfile)
{
+ // Don't include the same archive twice. This can happen if
+ // --whole-archive is nested inside --start-group (PR gold/12163).
+ if (this->included_all_members_)
+ return true;
+
+ this->included_all_members_ = true;
+
input_objects->archive_start(this);
if (this->members_.size() > 0)
return true;
}
- bool unconfigured;
- Object* obj = this->get_elf_object_for_member(off, &unconfigured);
-
- if (!this->included_member_
- && this->searched_for()
- && obj == NULL
- && unconfigured)
- return false;
+ // If this is the first object we are including from this archive,
+ // and we searched for this archive, most likely because it was
+ // found via a -l option, then if the target is incompatible we want
+ // to move on to the next archive found in the search path.
+ bool unconfigured = false;
+ bool* punconfigured = NULL;
+ if (!this->included_member_ && this->searched_for())
+ punconfigured = &unconfigured;
+ Object* obj = this->get_elf_object_for_member(off, punconfigured);
if (obj == NULL)
- return true;
+ {
+ // Return false to search for another archive, true if we found
+ // an error.
+ return unconfigured ? false : true;
+ }
if (mapfile != NULL)
mapfile->report_include_archive_member(obj->name(), sym, why);
{
{
if (layout->incremental_inputs() != NULL)
- layout->incremental_inputs()->report_object(obj, this);
+ layout->incremental_inputs()->report_object(obj, 0, this, NULL);
Read_symbols_data sd;
obj->read_symbols(&sd);
obj->layout(symtab, layout, &sd);
return true;
}
+// Iterate over all unused symbols, and call the visitor class V for each.
+
+void
+Archive::do_for_all_unused_symbols(Symbol_visitor_base* v) const
+{
+ for (std::vector<Armap_entry>::const_iterator p = this->armap_.begin();
+ p != this->armap_.end();
+ ++p)
+ {
+ if (this->seen_offsets_.find(p->file_offset)
+ == this->seen_offsets_.end())
+ v->visit(this->armap_names_.data() + p->name_offset);
+ }
+}
+
// Print statistical information to stderr. This is used for --stats.
void
// For an incremental link, begin recording layout information.
Incremental_inputs* incremental_inputs = this->layout_->incremental_inputs();
if (incremental_inputs != NULL)
- incremental_inputs->report_archive_begin(this->archive_);
+ {
+ unsigned int arg_serial = this->input_argument_->file().arg_serial();
+ Script_info* script_info = this->input_argument_->script_info();
+ incremental_inputs->report_archive_begin(this->archive_, arg_serial,
+ script_info);
+ }
bool added = this->archive_->add_symbols(this->symtab_, this->layout_,
this->input_objects_,
else
{
// For an incremental link, finish recording the layout information.
- Incremental_inputs* incremental_inputs = this->layout_->incremental_inputs();
if (incremental_inputs != NULL)
incremental_inputs->report_archive_end(this->archive_);
- // We no longer need to know about this archive.
- delete this->archive_;
+ if (!parameters->options().has_plugins()
+ || this->archive_->input_file()->options().whole_archive())
+ {
+ // We no longer need to know about this archive.
+ delete this->archive_;
+ }
+ else
+ {
+ // The plugin interface may want to rescan this archive.
+ parameters->options().plugins()->save_archive(this->archive_);
+ }
+
this->archive_ = NULL;
}
}
unsigned int Lib_group::total_members_loaded;
Lib_group::Lib_group(const Input_file_lib* lib, Task* task)
- : lib_(lib), task_(task), members_()
+ : Library_base(task), members_()
{
this->members_.resize(lib->size());
}
+const std::string&
+Lib_group::do_filename() const
+{
+ std::string *filename = new std::string("/group/");
+ return *filename;
+}
+
// 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.
+// through 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.
obj->lock(this->task_);
if (input_objects->add_object(obj))
{
- // FIXME: Record incremental link info for --start-lib/--end-lib.
if (layout->incremental_inputs() != NULL)
- layout->incremental_inputs()->report_object(obj, NULL);
+ layout->incremental_inputs()->report_object(obj, member.arg_serial_,
+ this, NULL);
obj->layout(symtab, layout, sd);
obj->add_symbols(symtab, sd, layout);
}
obj->unlock(this->task_);
}
+// Iterate over all unused symbols, and call the visitor class V for each.
+
+void
+Lib_group::do_for_all_unused_symbols(Symbol_visitor_base* v) const
+{
+ // Files are removed from the members list when used, so all the
+ // files remaining on the list are unused.
+ for (std::vector<Archive_member>::const_iterator p = this->members_.begin();
+ p != this->members_.end();
+ ++p)
+ {
+ Object* obj = p->obj_;
+ obj->for_all_global_symbols(p->sd_, v);
+ }
+}
+
// Print statistical information to stderr. This is used for --stats.
void
void
Add_lib_group_symbols::run(Workqueue*)
{
+ // For an incremental link, begin recording layout information.
+ Incremental_inputs* incremental_inputs = this->layout_->incremental_inputs();
+ if (incremental_inputs != NULL)
+ incremental_inputs->report_archive_begin(this->lib_, 0, NULL);
+
this->lib_->add_symbols(this->symtab_, this->layout_, this->input_objects_);
- // FIXME: Record incremental link info for --start_lib/--end_lib.
+ if (incremental_inputs != NULL)
+ incremental_inputs->report_archive_end(this->lib_);
}
Add_lib_group_symbols::~Add_lib_group_symbols()