(Read_symbols::requeue): New function.
(Read_symbols::do_read_symbols): If make_elf_object fails because
the target type is not configured, and the file was searched for,
issue a warning and retry with the next directory.
(Add_symbols::run): If the file has an incompatible format, and
it was searched for, requeue the Read_symbols task. On error,
release the object.
* readsyms.h (class Read_symbols): Add dirindex_ field. Add
dirindex parameter to constructor. Change all callers. Declare
incompatible_warning and requeue.
(class Add_symbols): Add dirpath_, dirindex_, mapfile_,
input_argument_ and input_group_ fields. Add them to
constructor. Change all callers.
(class Read_script): Add dirindex_ field. Add it to constructor.
Change all callers.
* archive.cc (Archive::setup): Remove input_objects parameter.
Change all callers.
(Archive::get_file_and_offset): Likewise.
(Archive::read_all_symbols): Likewise.
(Archive::read_symbols): Likewise.
(Archive::get_elf_object_for_member): Remove input_objects
parameter. Add punconfigured parameter. Change all callers.
(Archive::add_symbols): Change return type to bool. Check return
value of include_member.
(Archive::include_all_members): Likewise.
(Archive::include_member): Change return type to bool. Return
false if first included object has incompatible target. Set
included_member_ field.
(Add_archive_symbols::run): If add_symbols returns false, requeue
Read_symbols task.
* archive.h (class Archive): Add included_member_ field.
Initialize it in constructor. Add input_file and searched_for
methods. Update declarations.
(class Add_archive_symbols): Add dirpath_, dirindex_, and
input_argument_ fields. Add them to constructor. Change all
callers.
* script.cc: Include "target-select.h".
(class Parser_closure): Add skip_on_incompatible_target_ and
found_incompatible_target_ fields. Add
skip_on_incompatible_target parameter to constructor. Change all
callers. Add methods skip_on_incompatible_target,
clear_skip_on_incompatible_target, found_incompatible_target, and
set_found_incompatible_target.
(read_input_script): Add dirindex parameter. Change all callers.
If parser finds an incompatible target, requeue Read_symbols
task.
(script_set_symbol): Clear skip_on_incompatible_target in
closure.
(script_add_assertion, script_parse_option): Likewise.
(script_start_sections, script_add_phdr): Likewise.
(script_check_output_format): New function.
* script.h (read_input_script): Update declaration.
* script-c.h (script_check_output_format): Declare.
* yyscript.y (file_cmd): Handle OUTPUT_FORMAT.
(ignore_cmd): Remove OUTPUT_FORMAT.
* fileread.cc (Input_file::Input_file): Add explicit this.
(Input_file::will_search_for): New function.
(Input_file::open): Add pindex parameter. Change all callers.
* fileread.h (class Input_file): Add input_file_argument method.
Declare will_search_for. Update declarations.
* object.cc (make_elf_object): Add punconfigured parameter.
Change all callers.
* object.h (class Object): Make input_file public. Add
searched_for method.
(make_elf_object): Update declaration.
* dirsearch.cc (Dirsearch::find): Add pindex parameter. Use it to
restart search.
* dirsearch.h (class Dirsearch): Update declaration.
* options.h (class General_options): Add --warn-search-mismatch.
* parameters.cc (Parameters::is_compatible_target): New function.
* parameters.h (class Parameters): Declare is_compatible_target.
* workqueue.cc (Workqueue::add_blocker): New function.
* workqueue.h (class Workqueue): Declare add_blocker.
2009-03-13 Ian Lance Taylor <iant@google.com>
+ * readsyms.cc (Read_symbols::incompatible_warning): New function.
+ (Read_symbols::requeue): New function.
+ (Read_symbols::do_read_symbols): If make_elf_object fails because
+ the target type is not configured, and the file was searched for,
+ issue a warning and retry with the next directory.
+ (Add_symbols::run): If the file has an incompatible format, and
+ it was searched for, requeue the Read_symbols task. On error,
+ release the object.
+ * readsyms.h (class Read_symbols): Add dirindex_ field. Add
+ dirindex parameter to constructor. Change all callers. Declare
+ incompatible_warning and requeue.
+ (class Add_symbols): Add dirpath_, dirindex_, mapfile_,
+ input_argument_ and input_group_ fields. Add them to
+ constructor. Change all callers.
+ (class Read_script): Add dirindex_ field. Add it to constructor.
+ Change all callers.
+ * archive.cc (Archive::setup): Remove input_objects parameter.
+ Change all callers.
+ (Archive::get_file_and_offset): Likewise.
+ (Archive::read_all_symbols): Likewise.
+ (Archive::read_symbols): Likewise.
+ (Archive::get_elf_object_for_member): Remove input_objects
+ parameter. Add punconfigured parameter. Change all callers.
+ (Archive::add_symbols): Change return type to bool. Check return
+ value of include_member.
+ (Archive::include_all_members): Likewise.
+ (Archive::include_member): Change return type to bool. Return
+ false if first included object has incompatible target. Set
+ included_member_ field.
+ (Add_archive_symbols::run): If add_symbols returns false, requeue
+ Read_symbols task.
+ * archive.h (class Archive): Add included_member_ field.
+ Initialize it in constructor. Add input_file and searched_for
+ methods. Update declarations.
+ (class Add_archive_symbols): Add dirpath_, dirindex_, and
+ input_argument_ fields. Add them to constructor. Change all
+ callers.
+ * script.cc: Include "target-select.h".
+ (class Parser_closure): Add skip_on_incompatible_target_ and
+ found_incompatible_target_ fields. Add
+ skip_on_incompatible_target parameter to constructor. Change all
+ callers. Add methods skip_on_incompatible_target,
+ clear_skip_on_incompatible_target, found_incompatible_target, and
+ set_found_incompatible_target.
+ (read_input_script): Add dirindex parameter. Change all callers.
+ If parser finds an incompatible target, requeue Read_symbols
+ task.
+ (script_set_symbol): Clear skip_on_incompatible_target in
+ closure.
+ (script_add_assertion, script_parse_option): Likewise.
+ (script_start_sections, script_add_phdr): Likewise.
+ (script_check_output_format): New function.
+ * script.h (read_input_script): Update declaration.
+ * script-c.h (script_check_output_format): Declare.
+ * yyscript.y (file_cmd): Handle OUTPUT_FORMAT.
+ (ignore_cmd): Remove OUTPUT_FORMAT.
+ * fileread.cc (Input_file::Input_file): Add explicit this.
+ (Input_file::will_search_for): New function.
+ (Input_file::open): Add pindex parameter. Change all callers.
+ * fileread.h (class Input_file): Add input_file_argument method.
+ Declare will_search_for. Update declarations.
+ * object.cc (make_elf_object): Add punconfigured parameter.
+ Change all callers.
+ * object.h (class Object): Make input_file public. Add
+ searched_for method.
+ (make_elf_object): Update declaration.
+ * dirsearch.cc (Dirsearch::find): Add pindex parameter. Use it to
+ restart search.
+ * dirsearch.h (class Dirsearch): Update declaration.
+ * options.h (class General_options): Add --warn-search-mismatch.
+ * parameters.cc (Parameters::is_compatible_target): New function.
+ * parameters.h (class Parameters): Declare is_compatible_target.
+ * workqueue.cc (Workqueue::add_blocker): New function.
+ * workqueue.h (class Workqueue): Declare add_blocker.
+
* fileread.cc (Input_file::open): Remove options parameter.
Change all callers.
(Input_file::open_binary): Likewise.
// table.
void
-Archive::setup(Input_objects* input_objects)
+Archive::setup()
{
// We need to ignore empty archives.
if (this->input_file_->file().filesize() == sarmag)
preread_syms = false;
#endif
if (preread_syms)
- this->read_all_symbols(input_objects);
+ this->read_all_symbols();
}
// Unlock any nested archives.
// to the name of the archive member. Return TRUE on success.
bool
-Archive::get_file_and_offset(off_t off, Input_objects* input_objects,
- Input_file** input_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;
new Input_file_argument(member_name->c_str(), false, "", false,
parameters->options());
*input_file = new Input_file(input_file_arg);
- if (!(*input_file)->open(*this->dirpath_, this->task_))
+ int dummy = 0;
+ if (!(*input_file)->open(*this->dirpath_, this->task_, &dummy))
return false;
arch = new Archive(*member_name, *input_file, false, this->dirpath_,
this->task_);
- arch->setup(input_objects);
+ 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, input_objects, input_file,
- memoff, memsize, member_name);
+ return arch->get_file_and_offset(nested_off, input_file, memoff,
+ memsize, member_name);
}
// This is an external member of a thin archive. Open the
new Input_file_argument(member_name->c_str(), false, "", false,
this->input_file_->options());
*input_file = new Input_file(input_file_arg);
- if (!(*input_file)->open(*this->dirpath_, this->task_))
+ int dummy = 0;
+ if (!(*input_file)->open(*this->dirpath_, this->task_, &dummy))
return false;
*memoff = 0;
return true;
}
-// Return an ELF object for the member at offset OFF. Set *MEMBER_NAME to
-// the name of the member.
+// 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.
Object*
-Archive::get_elf_object_for_member(off_t off, Input_objects* input_objects)
+Archive::get_elf_object_for_member(off_t off, bool* punconfigured)
{
- std::string member_name;
+ *punconfigured = false;
+
Input_file* input_file;
off_t memoff;
off_t memsize;
-
- if (!this->get_file_and_offset(off, input_objects, &input_file, &memoff,
- &memsize, &member_name))
+ std::string member_name;
+ if (!this->get_file_and_offset(off, &input_file, &memoff, &memsize,
+ &member_name))
return NULL;
if (parameters->options().has_plugins())
}
return make_elf_object((std::string(this->input_file_->filename())
- + "(" + member_name + ")"),
- input_file, memoff, ehdr, read_size);
+ + "(" + member_name + ")"),
+ input_file, memoff, ehdr, read_size,
+ punconfigured);
}
// Read the symbols from all the archive members in the link.
void
-Archive::read_all_symbols(Input_objects* input_objects)
+Archive::read_all_symbols()
{
for (Archive::const_iterator p = this->begin();
p != this->end();
++p)
- this->read_symbols(input_objects, p->off);
+ this->read_symbols(p->off);
}
// Read the symbols from an archive member in the link. OFF is the file
// offset of the member header.
void
-Archive::read_symbols(Input_objects* input_objects, off_t off)
+Archive::read_symbols(off_t off)
{
- Object* obj = this->get_elf_object_for_member(off, input_objects);
+ bool dummy;
+ Object* obj = this->get_elf_object_for_member(off, &dummy);
if (obj == NULL)
return;
// the symbol table. If it exists as a strong undefined symbol, we
// pull in the corresponding element. We have to do this in a loop,
// since pulling in one element may create new undefined symbols which
-// may be satisfied by other objects in the archive.
+// may be satisfied by other objects in the archive. Return true in
+// the normal case, false if the first member we tried to add from
+// this archive had an incompatible target.
-void
+bool
Archive::add_symbols(Symbol_table* symtab, Layout* layout,
Input_objects* input_objects, Mapfile* mapfile)
{
why = "-u ";
why += sym_name;
}
- this->include_member(symtab, layout, input_objects,
- last_seen_offset, mapfile, sym, why.c_str());
+ if (!this->include_member(symtab, layout, input_objects,
+ last_seen_offset, mapfile, sym,
+ why.c_str()))
+ return false;
added_new_object = true;
}
while (added_new_object);
input_objects->archive_stop(this);
+
+ return true;
}
// Include all the archive members in the link. This is for --whole-archive.
-void
+bool
Archive::include_all_members(Symbol_table* symtab, Layout* layout,
Input_objects* input_objects, Mapfile* mapfile)
{
p != this->members_.end();
++p)
{
- this->include_member(symtab, layout, input_objects, p->first,
- mapfile, NULL, "--whole-archive");
+ if (!this->include_member(symtab, layout, input_objects, p->first,
+ mapfile, NULL, "--whole-archive"))
+ return false;
++Archive::total_members;
}
}
p != this->end();
++p)
{
- this->include_member(symtab, layout, input_objects, p->off,
- mapfile, NULL, "--whole-archive");
+ if (!this->include_member(symtab, layout, input_objects, p->off,
+ mapfile, NULL, "--whole-archive"))
+ return false;
++Archive::total_members;
}
}
input_objects->archive_stop(this);
+
+ return true;
}
// Return the number of members in the archive. This is only used for
// Include an archive member in the link. OFF is the file offset of
// the member header. WHY is the reason we are including this member.
+// Return true if we added the member or if we had an error, return
+// false if this was the first member we tried to add from this
+// archive and it had an incompatible format.
-void
+bool
Archive::include_member(Symbol_table* symtab, Layout* layout,
Input_objects* input_objects, off_t off,
Mapfile* mapfile, Symbol* sym, const char* why)
if (p != this->members_.end())
{
Object *obj = p->second.obj_;
+
+ if (!this->included_member_
+ && this->searched_for()
+ && !parameters->is_compatible_target(obj->target()))
+ return false;
+
Read_symbols_data *sd = p->second.sd_;
if (mapfile != NULL)
mapfile->report_include_archive_member(obj->name(), sym, why);
{
obj->layout(symtab, layout, sd);
obj->add_symbols(symtab, sd, layout);
+ this->included_member_ = true;
}
delete sd;
- return;
+ return true;
+ }
+
+ bool unconfigured;
+ Object* obj = this->get_elf_object_for_member(off, &unconfigured);
+
+ if (!this->included_member_
+ && this->searched_for()
+ && (obj == NULL
+ ? unconfigured
+ : !parameters->is_compatible_target(obj->target())))
+ {
+ if (obj != NULL)
+ delete obj;
+ return false;
}
- Object* obj = this->get_elf_object_for_member(off, input_objects);
if (obj == NULL)
- return;
+ return true;
if (mapfile != NULL)
mapfile->report_include_archive_member(obj->name(), sym, why);
if (pluginobj != NULL)
{
pluginobj->add_symbols(symtab, NULL, layout);
- return;
+ this->included_member_ = true;
+ return true;
}
- if (input_objects->add_object(obj))
+ if (!input_objects->add_object(obj))
+ delete obj;
+ else
{
Read_symbols_data sd;
obj->read_symbols(&sd);
// for the next task.
if (obj->offset() == 0)
obj->unlock(this->task_);
+
+ this->included_member_ = true;
}
- else
- {
- // FIXME: We need to close the descriptor here.
- delete obj;
- }
+
+ return true;
}
// Print statistical information to stderr. This is used for --stats.
}
void
-Add_archive_symbols::run(Workqueue*)
+Add_archive_symbols::run(Workqueue* workqueue)
{
- this->archive_->add_symbols(this->symtab_, this->layout_,
- this->input_objects_, this->mapfile_);
-
+ bool added = this->archive_->add_symbols(this->symtab_, this->layout_,
+ this->input_objects_,
+ this->mapfile_);
this->archive_->unlock_nested_archives();
this->archive_->release();
this->archive_->clear_uncached_views();
+ if (!added)
+ {
+ // This archive holds object files which are incompatible with
+ // our output file.
+ Read_symbols::incompatible_warning(this->input_argument_,
+ this->archive_->input_file());
+ Read_symbols::requeue(workqueue, this->input_objects_, this->symtab_,
+ this->layout_, this->dirpath_, this->dirindex_,
+ this->mapfile_, this->input_argument_,
+ this->input_group_, this->next_blocker_);
+ delete this->archive_;
+ return;
+ }
+
if (this->input_group_ != NULL)
this->input_group_->add_archive(this->archive_);
else
{
class Task;
+class Input_argument;
class Input_file;
class Input_objects;
class Input_group;
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), nested_archives_(),
- dirpath_(dirpath), task_(task), num_members_(0)
+ is_thin_archive_(is_thin_archive), included_member_(false),
+ nested_archives_(), dirpath_(dirpath), task_(task), num_members_(0)
{ }
// The length of the magic string at the start of an archive.
name() const
{ return this->name_; }
+ // The input file.
+ const Input_file*
+ input_file() const
+ { return this->input_file_; }
+
// The file name.
const std::string&
filename() const
// Set up the archive: read the symbol map.
void
- setup(Input_objects*);
+ setup();
// Get a reference to the underlying file.
File_read&
// Select members from the archive as needed and add them to the
// link.
- void
+ bool
add_symbols(Symbol_table*, Layout*, Input_objects*, Mapfile*);
// Dump statistical information to stderr.
// within that file (0 if not a nested archive), and *MEMBER_NAME
// to the name of the archive member. Return TRUE on success.
bool
- get_file_and_offset(off_t off, Input_objects* input_objects,
- Input_file** input_file, off_t* memoff,
+ get_file_and_offset(off_t off, Input_file** input_file, off_t* memoff,
off_t* memsize, std::string* member_name);
- // Return an ELF object for the member at offset OFF. Set *MEMBER_NAME to
- // the name of the member.
+ // Return an ELF object for the member at offset OFF.
Object*
- get_elf_object_for_member(off_t off, Input_objects* input_objects);
+ get_elf_object_for_member(off_t off, bool*);
// Read the symbols from all the archive members in the link.
void
- read_all_symbols(Input_objects* input_objects);
+ read_all_symbols();
// Read the symbols from an archive member in the link. OFF is the file
// offset of the member header.
void
- read_symbols(Input_objects* input_objects, off_t off);
+ read_symbols(off_t off);
// Include all the archive members in the link.
- void
+ bool
include_all_members(Symbol_table*, Layout*, Input_objects*, Mapfile*);
// Include an archive member in the link.
- void
+ bool
include_member(Symbol_table*, Layout*, Input_objects*, off_t off,
Mapfile*, Symbol*, const char* why);
+ // Return whether we found this archive by searching a directory.
+ bool
+ searched_for() const
+ { return this->input_file_->will_search_for(); }
+
// Iterate over archive members.
class const_iterator;
std::map<off_t, Archive_member> members_;
// True if this is a thin archive.
const bool is_thin_archive_;
+ // True if we have included at least one object from this archive.
+ bool included_member_;
// Table of nested archives, indexed by filename.
Nested_archive_table nested_archives_;
// The directory search path.
{
public:
Add_archive_symbols(Symbol_table* symtab, Layout* layout,
- Input_objects* input_objects, Mapfile* mapfile,
+ Input_objects* input_objects, Dirsearch* dirpath,
+ int dirindex, Mapfile* mapfile,
+ const Input_argument* input_argument,
Archive* archive, Input_group* input_group,
Task_token* this_blocker,
Task_token* next_blocker)
: symtab_(symtab), layout_(layout), input_objects_(input_objects),
- mapfile_(mapfile), archive_(archive), input_group_(input_group),
- this_blocker_(this_blocker), next_blocker_(next_blocker)
+ dirpath_(dirpath), dirindex_(dirindex), mapfile_(mapfile),
+ input_argument_(input_argument), archive_(archive),
+ input_group_(input_group), this_blocker_(this_blocker),
+ next_blocker_(next_blocker)
{ }
~Add_archive_symbols();
Symbol_table* symtab_;
Layout* layout_;
Input_objects* input_objects_;
+ Dirsearch* dirpath_;
+ int dirindex_;
Mapfile* mapfile_;
+ const Input_argument* input_argument_;
Archive* archive_;
Input_group* input_group_;
Task_token* this_blocker_;
namespace gold
{
+// Initialize.
+
void
Dirsearch::initialize(Workqueue* workqueue,
const General_options::Dir_list* directories)
}
}
-// NOTE: we only log failed file-lookup attempts here. Successfully
-// lookups will eventually get logged in File_read::open.
+// Search for a file. NOTE: we only log failed file-lookup attempts
+// here. Successfully lookups will eventually get logged in
+// File_read::open.
std::string
Dirsearch::find(const std::string& n1, const std::string& n2,
- bool *is_in_sysroot) const
+ bool* is_in_sysroot, int* pindex) const
{
gold_assert(!this->token_.is_blocked());
+ gold_assert(*pindex >= 0);
- for (General_options::Dir_list::const_iterator p =
- this->directories_->begin();
- p != this->directories_->end();
- ++p)
+ for (unsigned int i = static_cast<unsigned int>(*pindex);
+ i < this->directories_->size();
+ ++i)
{
+ const Search_directory* p = &this->directories_->at(i);
Dir_cache* pdc = caches->lookup(p->name().c_str());
gold_assert(pdc != NULL);
if (pdc->find(n1))
{
*is_in_sysroot = p->is_in_sysroot();
+ *pindex = i;
return p->name() + '/' + n1;
}
else
if (pdc->find(n2))
{
*is_in_sysroot = p->is_in_sysroot();
+ *pindex = i;
return p->name() + '/' + n2;
}
else
}
}
+ *pindex = -2;
return std::string();
}
// second one may be empty). Return a full path name for the file,
// or the empty string if it could not be found. This may only be
// called if the token is not blocked. Set *IS_IN_SYSROOT if the
- // file was found in a directory which is in the sysroot.
+ // file was found in a directory which is in the sysroot. *PINDEX
+ // should be set to zero the first time this is called; it will be
+ // updated with the index of the directory where the file is found,
+ // and that value plus one may be used to find the next file with
+ // the same name(s).
std::string
- find(const std::string&, const std::string& n2, bool *is_in_sysroot) const;
+ find(const std::string&, const std::string& n2, bool *is_in_sysroot,
+ int* pindex) const;
// Return the blocker token which controls access.
Task_token*
this->input_argument_ =
new Input_file_argument(name, false, "", false,
Position_dependent_options());
- bool ok = file_.open(task, name, contents, size);
+ bool ok = this->file_.open(task, name, contents, size);
gold_assert(ok);
}
return this->input_argument_->just_symbols();
}
+// Return whether this is a file that we will search for in the list
+// of directories.
+
+bool
+Input_file::will_search_for() const
+{
+ return (!IS_ABSOLUTE_PATH(this->input_argument_->name())
+ && (this->input_argument_->is_lib()
+ || this->input_argument_->extra_search_path() != NULL));
+}
+
// Open the file.
// If the filename is not absolute, we assume it is in the current
// the file location, rather than the current directory.
bool
-Input_file::open(const Dirsearch& dirpath, const Task* task)
+Input_file::open(const Dirsearch& dirpath, const Task* task, int *pindex)
{
std::string name;
// Case 1: name is an absolute file, just try to open it
// Case 2: name is relative but is_lib is false and extra_search_path
// is empty
- if (IS_ABSOLUTE_PATH (this->input_argument_->name())
+ if (IS_ABSOLUTE_PATH(this->input_argument_->name())
|| (!this->input_argument_->is_lib()
&& this->input_argument_->extra_search_path() == NULL))
{
n2 = n1 + ".a";
n1 += ".so";
}
- name = dirpath.find(n1, n2, &this->is_in_sysroot_);
+ name = dirpath.find(n1, n2, &this->is_in_sysroot_, pindex);
if (name.empty())
{
gold_error(_("cannot find -l%s"),
name += '/';
name += this->input_argument_->name();
struct stat dummy_stat;
- if (::stat(name.c_str(), &dummy_stat) < 0)
+ if (*pindex > 0 || ::stat(name.c_str(), &dummy_stat) < 0)
{
// extra_search_path failed, so check the normal search-path.
+ int index = *pindex;
+ if (index > 0)
+ --index;
name = dirpath.find(this->input_argument_->name(), "",
- &this->is_in_sysroot_);
+ &this->is_in_sysroot_, &index);
if (name.empty())
{
gold_error(_("cannot find %s"),
this->input_argument_->name());
return false;
}
+ *pindex = index + 1;
}
this->found_name_ = this->input_argument_->name();
}
Input_file(const Task*, const char* name, const unsigned char* contents,
off_t size);
+ // Return the command line argument.
+ const Input_file_argument*
+ input_file_argument() const
+ { return this->input_argument_; }
+
+ // Return whether this is a file that we will search for in the list
+ // of directories.
+ bool
+ will_search_for() const;
+
// Open the file. If the open fails, this will report an error and
- // return false.
+ // return false. If there is a search, it starts at directory
+ // *PINDEX. *PINDEX should be initialized to zero. It may be
+ // restarted to find the next file with a matching name by
+ // incrementing the result and calling this again.
bool
- open(const Dirsearch&, const Task*);
+ open(const Dirsearch&, const Task*, int *pindex);
// Return the name given by the user. For -lc this will return "c".
const char*
Task_token* next_blocker = new Task_token(true);
next_blocker->add_blocker();
workqueue->queue(new Read_symbols(input_objects, symtab, layout,
- &search_path, mapfile, &*p, NULL,
+ &search_path, 0, mapfile, &*p, NULL,
this_blocker, next_blocker));
this_blocker = next_blocker;
}
Object*
make_elf_object(const std::string& name, Input_file* input_file, off_t offset,
- const unsigned char* p, section_offset_type bytes)
+ const unsigned char* p, section_offset_type bytes,
+ bool* punconfigured)
{
+ if (punconfigured != NULL)
+ *punconfigured = false;
+
if (bytes < elfcpp::EI_NIDENT)
{
gold_error(_("%s: ELF file too short"), name.c_str());
return make_elf_sized_object<32, true>(name, input_file,
offset, ehdr);
#else
- gold_error(_("%s: not configured to support "
- "32-bit big-endian object"),
- name.c_str());
+ if (punconfigured != NULL)
+ *punconfigured = true;
+ else
+ gold_error(_("%s: not configured to support "
+ "32-bit big-endian object"),
+ name.c_str());
return NULL;
#endif
}
return make_elf_sized_object<32, false>(name, input_file,
offset, ehdr);
#else
- gold_error(_("%s: not configured to support "
- "32-bit little-endian object"),
- name.c_str());
+ if (punconfigured != NULL)
+ *punconfigured = true;
+ else
+ gold_error(_("%s: not configured to support "
+ "32-bit little-endian object"),
+ name.c_str());
return NULL;
#endif
}
return make_elf_sized_object<64, true>(name, input_file,
offset, ehdr);
#else
- gold_error(_("%s: not configured to support "
- "64-bit big-endian object"),
- name.c_str());
+ if (punconfigured != NULL)
+ *punconfigured = true;
+ else
+ gold_error(_("%s: not configured to support "
+ "64-bit big-endian object"),
+ name.c_str());
return NULL;
#endif
}
return make_elf_sized_object<64, false>(name, input_file,
offset, ehdr);
#else
- gold_error(_("%s: not configured to support "
- "64-bit little-endian object"),
- name.c_str());
+ if (punconfigured != NULL)
+ *punconfigured = true;
+ else
+ gold_error(_("%s: not configured to support "
+ "64-bit little-endian object"),
+ name.c_str());
return NULL;
#endif
}
target() const
{ return this->target_; }
+ // Get the file. We pass on const-ness.
+ Input_file*
+ input_file()
+ { return this->input_file_; }
+
+ const Input_file*
+ input_file() const
+ { return this->input_file_; }
+
// Lock the underlying file.
void
lock(const Task* t)
is_in_system_directory() const
{ return this->input_file()->is_in_system_directory(); }
+ // Return whether we found this object by searching a directory.
+ bool
+ searched_for() const
+ { return this->input_file()->will_search_for(); }
+
protected:
// Returns NULL for Objects that are not plugin objects. This method
// is overridden in the Pluginobj class.
virtual void
do_get_global_symbol_counts(const Symbol_table*, size_t*, size_t*) const = 0;
- // Get the file. We pass on const-ness.
- Input_file*
- input_file()
- { return this->input_file_; }
-
- const Input_file*
- input_file() const
- { return this->input_file_; }
-
// Set the target.
void
set_target(int machine, int size, bool big_endian, int osabi,
};
// Return an Object appropriate for the input file. P is BYTES long,
-// and holds the ELF header.
+// and holds the ELF header. If PUNCONFIGURED is not NULL, then if
+// this sees an object the linker is not configured to support, it
+// sets *PUNCONFIGURED to true and returns NULL without giving an
+// error message.
extern Object*
make_elf_object(const std::string& name, Input_file*,
off_t offset, const unsigned char* p,
- section_offset_type bytes);
+ section_offset_type bytes, bool* punconfigured);
} // end namespace gold
DEFINE_special(version_script, options::TWO_DASHES, '\0',
N_("Read version script"), N_("FILE"));
+ DEFINE_bool(warn_search_mismatch, options::TWO_DASHES, '\0', true,
+ N_("Warn when skipping an incompatible library"),
+ N_("Don't warn when skipping an incompatible library"));
+
DEFINE_bool(whole_archive, options::TWO_DASHES, '\0', false,
N_("Include all archive contents"),
N_("Include only needed archive contents"));
return *target;
}
+// Return whether TARGET is compatible with the target we are using.
+
+bool
+Parameters::is_compatible_target(const Target* target) const
+{
+ if (this->target_ == NULL)
+ return true;
+ return target == this->target_;
+}
+
Parameters::Target_size_endianness
Parameters::size_and_endianness() const
{
const Target&
default_target() const;
+ // Return true if TARGET is compatible with the current target.
+ bool
+ is_compatible_target(const Target*) const;
+
bool
doing_static_link() const
{
this->symtab_,
this->layout_,
this->dirpath_,
+ 0,
this->mapfile_,
input_argument,
NULL,
// Add_symbols task.
}
+// If appropriate, issue a warning about skipping an incompatible
+// file.
+
+void
+Read_symbols::incompatible_warning(const Input_argument* input_argument,
+ const Input_file* input_file)
+{
+ if (parameters->options().warn_search_mismatch())
+ gold_warning("skipping incompatible %s while searching for %s",
+ input_file->filename().c_str(),
+ input_argument->file().name());
+}
+
+// Requeue a Read_symbols task to search for the next object with the
+// same name.
+
+void
+Read_symbols::requeue(Workqueue* workqueue, Input_objects* input_objects,
+ Symbol_table* symtab, Layout* layout, Dirsearch* dirpath,
+ int dirindex, Mapfile* mapfile,
+ const Input_argument* input_argument,
+ Input_group* input_group, Task_token* next_blocker)
+{
+ // Bump the directory search index.
+ ++dirindex;
+
+ // We don't need to worry about this_blocker, since we already
+ // reached it. However, we are removing the blocker on next_blocker
+ // because the calling task is completing. So we need to add a new
+ // blocker. Since next_blocker may be shared by several tasks, we
+ // need to increment the count with the workqueue lock held.
+ workqueue->add_blocker(next_blocker);
+
+ workqueue->queue(new Read_symbols(input_objects, symtab, layout, dirpath,
+ dirindex, mapfile, input_argument,
+ input_group, NULL, next_blocker));
+}
+
// Return whether a Read_symbols task is runnable. We can read an
// ordinary input file immediately. For an archive specified using
// -l, we have to wait until the search path is complete.
}
Input_file* input_file = new Input_file(&this->input_argument_->file());
- if (!input_file->open(*this->dirpath_, this))
+ if (!input_file->open(*this->dirpath_, this, &this->dirindex_))
return false;
// Read enough of the file to pick up the entire ELF header.
Archive* arch = new Archive(this->input_argument_->file().name(),
input_file, is_thin_archive,
this->dirpath_, this);
- arch->setup(this->input_objects_);
+ arch->setup();
// Unlock the archive so it can be used in the next task.
arch->unlock(this);
workqueue->queue_next(new Add_archive_symbols(this->symtab_,
this->layout_,
this->input_objects_,
+ this->dirpath_,
+ this->dirindex_,
this->mapfile_,
+ this->input_argument_,
arch,
this->input_group_,
this->this_blocker_,
workqueue->queue_next(new Add_symbols(this->input_objects_,
this->symtab_,
this->layout_,
- obj, NULL,
+ this->dirpath_,
+ this->dirindex_,
+ this->mapfile_,
+ this->input_argument_,
+ this->input_group_,
+ obj,
+ NULL,
this->this_blocker_,
this->next_blocker_));
return true;
{
// This is an ELF object.
+ bool unconfigured;
Object* obj = make_elf_object(input_file->filename(),
- input_file, 0, ehdr, read_size);
+ input_file, 0, ehdr, read_size,
+ &unconfigured);
if (obj == NULL)
- return false;
+ {
+ if (unconfigured && input_file->will_search_for())
+ {
+ Read_symbols::incompatible_warning(this->input_argument_,
+ input_file);
+ input_file->file().release();
+ input_file->file().unlock(this);
+ delete input_file;
+ ++this->dirindex_;
+ return this->do_read_symbols(workqueue);
+ }
+ return false;
+ }
Read_symbols_data* sd = new Read_symbols_data;
obj->read_symbols(sd);
workqueue->queue_next(new Add_symbols(this->input_objects_,
this->symtab_, this->layout_,
- obj, sd,
+ this->dirpath_,
+ this->dirindex_,
+ this->mapfile_,
+ this->input_argument_,
+ this->input_group_,
+ obj,
+ sd,
this->this_blocker_,
this->next_blocker_));
workqueue->queue_soon(new Read_script(this->symtab_,
this->layout_,
this->dirpath_,
+ this->dirindex_,
this->input_objects_,
this->mapfile_,
this->input_group_,
next_blocker->add_blocker();
workqueue->queue_soon(new Read_symbols(this->input_objects_,
this->symtab_, this->layout_,
- this->dirpath_, this->mapfile_,
- arg, input_group,
+ this->dirpath_, this->dirindex_,
+ this->mapfile_, arg, input_group,
this_blocker, next_blocker));
this_blocker = next_blocker;
}
// Add the symbols in the object to the symbol table.
void
-Add_symbols::run(Workqueue*)
+Add_symbols::run(Workqueue* workqueue)
{
Pluginobj* pluginobj = this->object_->pluginobj();
if (pluginobj != NULL)
return;
}
- if (!this->input_objects_->add_object(this->object_))
+ // If this file has an incompatible format, try for another file
+ // with the same name.
+ if (this->object_->searched_for()
+ && !parameters->is_compatible_target(this->object_->target()))
{
- // FIXME: We need to close the descriptor here.
+ Read_symbols::incompatible_warning(this->input_argument_,
+ this->object_->input_file());
+ Read_symbols::requeue(workqueue, this->input_objects_, this->symtab_,
+ this->layout_, this->dirpath_, this->dirindex_,
+ this->mapfile_, this->input_argument_,
+ this->input_group_, this->next_blocker_);
+ this->object_->release();
+ delete this->object_;
+ }
+ else if (!this->input_objects_->add_object(this->object_))
+ {
+ this->object_->release();
delete this->object_;
}
else
{
bool used_next_blocker;
if (!read_input_script(workqueue, this->symtab_, this->layout_,
- this->dirpath_, this->input_objects_,
+ this->dirpath_, this->dirindex_, this->input_objects_,
this->mapfile_, this->input_group_,
this->input_argument_, this->input_file_,
this->next_blocker_, &used_next_blocker))
// NEXT_BLOCKER is used to block the next input file from adding
// symbols.
Read_symbols(Input_objects* input_objects, Symbol_table* symtab,
- Layout* layout, Dirsearch* dirpath, Mapfile* mapfile,
- const Input_argument* input_argument,
+ Layout* layout, Dirsearch* dirpath, int dirindex,
+ Mapfile* mapfile, const Input_argument* input_argument,
Input_group* input_group, Task_token* this_blocker,
Task_token* next_blocker)
: input_objects_(input_objects), symtab_(symtab), layout_(layout),
- dirpath_(dirpath), mapfile_(mapfile), input_argument_(input_argument),
- input_group_(input_group), this_blocker_(this_blocker),
- next_blocker_(next_blocker)
+ dirpath_(dirpath), dirindex_(dirindex), mapfile_(mapfile),
+ input_argument_(input_argument), input_group_(input_group),
+ this_blocker_(this_blocker), next_blocker_(next_blocker)
{ }
~Read_symbols();
+ // If appropriate, issue a warning about skipping an incompatible
+ // object.
+ static void
+ incompatible_warning(const Input_argument*, const Input_file*);
+
+ // Requeue a Read_symbols task to search for the next object with
+ // the same name.
+ static void
+ requeue(Workqueue*, Input_objects*, Symbol_table*, Layout*, Dirsearch*,
+ int dirindex, Mapfile*, const Input_argument*, Input_group*,
+ Task_token* next_blocker);
+
// The standard Task methods.
Task_token*
Symbol_table* symtab_;
Layout* layout_;
Dirsearch* dirpath_;
+ int dirindex_;
Mapfile* mapfile_;
const Input_argument* input_argument_;
Input_group* input_group_;
// one for the previous input file. NEXT_BLOCKER is used to prevent
// the next task from running.
Add_symbols(Input_objects* input_objects, Symbol_table* symtab,
- Layout* layout, Object* object,
+ Layout* layout, Dirsearch* dirpath, int dirindex,
+ Mapfile* mapfile, const Input_argument* input_argument,
+ Input_group* input_group, Object* object,
Read_symbols_data* sd, Task_token* this_blocker,
Task_token* next_blocker)
: input_objects_(input_objects), symtab_(symtab), layout_(layout),
+ dirpath_(dirpath), dirindex_(dirindex), mapfile_(mapfile),
+ input_argument_(input_argument), input_group_(input_group),
object_(object), sd_(sd), this_blocker_(this_blocker),
next_blocker_(next_blocker)
{ }
Input_objects* input_objects_;
Symbol_table* symtab_;
Layout* layout_;
+ Dirsearch* dirpath_;
+ int dirindex_;
+ Mapfile* mapfile_;
+ const Input_argument* input_argument_;
+ Input_group* input_group_;
Object* object_;
Read_symbols_data* sd_;
Task_token* this_blocker_;
{
public:
Read_script(Symbol_table* symtab, Layout* layout, Dirsearch* dirpath,
- Input_objects* input_objects, Mapfile* mapfile,
+ int dirindex, Input_objects* input_objects, Mapfile* mapfile,
Input_group* input_group, const Input_argument* input_argument,
Input_file* input_file, Task_token* this_blocker,
Task_token* next_blocker)
- : symtab_(symtab), layout_(layout), dirpath_(dirpath),
+ : symtab_(symtab), layout_(layout), dirpath_(dirpath), dirindex_(dirindex),
input_objects_(input_objects), mapfile_(mapfile),
input_group_(input_group), input_argument_(input_argument),
input_file_(input_file), this_blocker_(this_blocker),
Symbol_table* symtab_;
Layout* layout_;
Dirsearch* dirpath_;
+ int dirindex_;
Input_objects* input_objects_;
Mapfile* mapfile_;
Input_group* input_group_;
extern void
script_parse_option(void* closure, const char*, size_t);
+/* Called by the bison parser to handle OUTPUT_FORMAT. This return 0
+ if the parse should be aborted. */
+
+extern int
+script_check_output_format(void* closure, const char*, size_t,
+ const char*, size_t, const char*, size_t);
+
/* Called by the bison parser to handle SEARCH_DIR. */
extern void
#include "parameters.h"
#include "layout.h"
#include "symtab.h"
+#include "target-select.h"
#include "script.h"
#include "script-c.h"
bool in_group, bool is_in_sysroot,
Command_line* command_line,
Script_options* script_options,
- Lex* lex)
+ Lex* lex,
+ bool skip_on_incompatible_target)
: filename_(filename), posdep_options_(posdep_options),
in_group_(in_group), is_in_sysroot_(is_in_sysroot),
+ skip_on_incompatible_target_(skip_on_incompatible_target),
+ found_incompatible_target_(false),
command_line_(command_line), script_options_(script_options),
version_script_info_(script_options->version_script_info()),
lex_(lex), lineno_(0), charpos_(0), lex_mode_stack_(), inputs_(NULL)
is_in_sysroot() const
{ return this->is_in_sysroot_; }
+ // Whether to skip to the next file with the same name if we find an
+ // incompatible target in an OUTPUT_FORMAT statement.
+ bool
+ skip_on_incompatible_target() const
+ { return this->skip_on_incompatible_target_; }
+
+ // Stop skipping to the next flie on an incompatible target. This
+ // is called when we make some unrevocable change to the data
+ // structures.
+ void
+ clear_skip_on_incompatible_target()
+ { this->skip_on_incompatible_target_ = false; }
+
+ // Whether we found an incompatible target in an OUTPUT_FORMAT
+ // statement.
+ bool
+ found_incompatible_target() const
+ { return this->found_incompatible_target_; }
+
+ // Note that we found an incompatible target.
+ void
+ set_found_incompatible_target()
+ { this->found_incompatible_target_ = true; }
+
// Returns the Command_line structure passed in at constructor time.
// This value may be NULL. The caller may modify this, which modifies
// the passed-in Command_line object (not a copy).
bool in_group_;
// Whether the script was found in a sysrooted directory.
bool is_in_sysroot_;
+ // If this is true, then if we find an OUTPUT_FORMAT with an
+ // incompatible target, then we tell the parser to abort so that we
+ // can search for the next file with the same name.
+ bool skip_on_incompatible_target_;
+ // True if we found an OUTPUT_FORMAT with an incompatible target.
+ bool found_incompatible_target_;
// May be NULL if the user chooses not to pass one in.
Command_line* command_line_;
// Options which may be set from any linker script.
bool
read_input_script(Workqueue* workqueue, Symbol_table* symtab, Layout* layout,
- Dirsearch* dirsearch, Input_objects* input_objects,
- Mapfile* mapfile, Input_group* input_group,
+ Dirsearch* dirsearch, int dirindex,
+ Input_objects* input_objects, Mapfile* mapfile,
+ Input_group* input_group,
const Input_argument* input_argument,
Input_file* input_file, Task_token* next_blocker,
bool* used_next_blocker)
input_file->is_in_sysroot(),
NULL,
layout->script_options(),
- &lex);
+ &lex,
+ input_file->will_search_for());
if (yyparse(&closure) != 0)
- return false;
+ {
+ if (closure.found_incompatible_target())
+ {
+ Read_symbols::incompatible_warning(input_argument, input_file);
+ Read_symbols::requeue(workqueue, input_objects, symtab, layout,
+ dirsearch, dirindex, mapfile, input_argument,
+ input_group, next_blocker);
+ return true;
+ }
+ return false;
+ }
if (!closure.saw_inputs())
return true;
nb->add_blocker();
}
workqueue->queue_soon(new Read_symbols(input_objects, symtab,
- layout, dirsearch, mapfile, &*p,
+ layout, dirsearch, 0, mapfile, &*p,
input_group, this_blocker, nb));
this_blocker = nb;
}
posdep.set_format_enum(General_options::OBJECT_FORMAT_ELF);
Input_file_argument input_argument(filename, false, "", false, posdep);
Input_file input_file(&input_argument);
- if (!input_file.open(dirsearch, task))
+ int dummy = 0;
+ if (!input_file.open(dirsearch, task, &dummy))
return false;
std::string input_string;
input_file.is_in_sysroot(),
cmdline,
script_options,
- &lex);
+ &lex,
+ false);
if (yyparse(&closure) != 0)
{
input_file.file().unlock(task);
Position_dependent_options posdep_options;
Parser_closure closure("command line", posdep_options, false, false, NULL,
- this, &lex);
+ this, &lex, false);
if (yyparse(&closure) != 0)
return false;
const bool hidden = hiddeni != 0;
closure->script_options()->add_symbol_assignment(name, length, value,
provide, hidden);
+ closure->clear_skip_on_incompatible_target();
}
// Called by the bison parser to add an assertion.
{
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
closure->script_options()->add_assertion(check, message, messagelen);
+ closure->clear_skip_on_incompatible_target();
}
// Called by the bison parser to parse an OPTION.
// into mutable_option, so we can't free it. In cases the class
// does not store such a pointer, this is a memory leak. Alas. :(
}
+ closure->clear_skip_on_incompatible_target();
+}
+
+// Called by the bison parser to handle OUTPUT_FORMAT. OUTPUT_FORMAT
+// takes either one or three arguments. In the three argument case,
+// the format depends on the endianness option, which we don't
+// currently support (FIXME). If we see an OUTPUT_FORMAT for the
+// wrong format, then we want to search for a new file. Returning 0
+// here will cause the parser to immediately abort.
+
+extern "C" int
+script_check_output_format(void* closurev,
+ const char* default_name, size_t default_length,
+ const char*, size_t, const char*, size_t)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ std::string name(default_name, default_length);
+ Target* target = select_target_by_name(name.c_str());
+ if (target == NULL || !parameters->is_compatible_target(target))
+ {
+ if (closure->skip_on_incompatible_target())
+ {
+ closure->set_found_incompatible_target();
+ return 0;
+ }
+ // FIXME: Should we warn about the unknown target?
+ }
+ return 1;
}
// Called by the bison parser to handle SEARCH_DIR. This is handled
{
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
closure->script_options()->script_sections()->start_sections();
+ closure->clear_skip_on_incompatible_target();
}
// Called by the bison parser to finish a SECTIONS clause.
Script_sections* ss = closure->script_options()->script_sections();
ss->add_phdr(name, namelen, type, includes_filehdr, includes_phdrs,
is_flags_valid, info->flags, info->load_address);
+ closure->clear_skip_on_incompatible_target();
}
// Convert a program header string to a type.
// whether the function took over NEXT_BLOCKER.
bool
-read_input_script(Workqueue*, Symbol_table*, Layout*, Dirsearch*,
+read_input_script(Workqueue*, Symbol_table*, Layout*, Dirsearch*, int,
Input_objects*, Mapfile*, Input_group*,
const Input_argument*, Input_file*,
Task_token* next_blocker, bool* used_next_blocker);
binary.converted_size());
Object* object = make_elf_object("test.o", &input_file, 0,
binary.converted_data(),
- binary.converted_size());
+ binary.converted_size(), NULL);
CHECK(object != NULL);
if (object == NULL)
return false;
const Task* task = reinterpret_cast<const Task*>(-1);
Input_file input_file(task, "test.o", test_file, test_file_size);
Object* object = make_elf_object("test.o", &input_file, 0,
- test_file, test_file_size);
+ test_file, test_file_size, NULL);
CHECK(object->name() == "test.o");
CHECK(!object->is_dynamic());
CHECK(object->target() == target_test_pointer);
this->condvar_.broadcast();
}
+// Add a new blocker to an existing Task_token.
+
+void
+Workqueue::add_blocker(Task_token* token)
+{
+ Hold_lock hl(this->lock_);
+ token->add_blocker();
+}
+
} // End namespace gold.
void
set_thread_count(int);
+ // Add a new blocker to an existing Task_token. This must be done
+ // with the workqueue lock held. This should not be done routinely,
+ // only in special circumstances.
+ void
+ add_blocker(Task_token*);
+
private:
// This class can not be copied.
Workqueue(const Workqueue&);
| INPUT '(' input_list ')'
| OPTION '(' string ')'
{ script_parse_option(closure, $3.value, $3.length); }
+ | OUTPUT_FORMAT '(' string ')'
+ {
+ if (!script_check_output_format(closure, $3.value, $3.length,
+ NULL, 0, NULL, 0))
+ YYABORT;
+ }
+ | OUTPUT_FORMAT '(' string ',' string ',' string ')'
+ {
+ if (!script_check_output_format(closure, $3.value, $3.length,
+ $5.value, $5.length,
+ $7.value, $7.length))
+ YYABORT;
+ }
| PHDRS '{' phdrs_defs '}'
| SEARCH_DIR '(' string ')'
{ script_add_search_dir(closure, $3.value, $3.length); }
these is more-or-less OK since most scripts simply explicitly
choose the default. */
ignore_cmd:
- OUTPUT_FORMAT '(' string ')'
- | OUTPUT_FORMAT '(' string ',' string ',' string ')'
- | OUTPUT_ARCH '(' string ')'
+ OUTPUT_ARCH '(' string ')'
;
/* A list of input file names. */