Read input scripts which look like input objects with proper

serialization.
This commit is contained in:
Ian Lance Taylor 2008-02-28 19:46:06 +00:00
parent a7dfd0102b
commit da769d5629
8 changed files with 238 additions and 118 deletions

View File

@ -120,8 +120,8 @@ Read_symbols::run(Workqueue* workqueue)
// If we didn't queue a new task, then we need to explicitly unblock // If we didn't queue a new task, then we need to explicitly unblock
// the token. // the token.
if (!this->do_read_symbols(workqueue)) if (!this->do_read_symbols(workqueue))
workqueue->queue_front(new Unblock_token(this->this_blocker_, workqueue->queue_soon(new Unblock_token(this->this_blocker_,
this->next_blocker_)); this->next_blocker_));
} }
// Open the file and read the symbols. Return true if a new task was // Open the file and read the symbols. Return true if a new task was
@ -189,11 +189,14 @@ Read_symbols::do_read_symbols(Workqueue* workqueue)
input_file->file().unlock(this); input_file->file().unlock(this);
workqueue->queue_front(new Add_symbols(this->input_objects_, // We use queue_next because everything is cached for this
this->symtab_, this->layout_, // task to run right away if possible.
obj, sd,
this->this_blocker_, workqueue->queue_next(new Add_symbols(this->input_objects_,
this->next_blocker_)); this->symtab_, this->layout_,
obj, sd,
this->this_blocker_,
this->next_blocker_));
return true; return true;
} }
@ -208,30 +211,34 @@ Read_symbols::do_read_symbols(Workqueue* workqueue)
input_file); input_file);
arch->setup(this); arch->setup(this);
workqueue->queue_front(new Add_archive_symbols(this->symtab_, workqueue->queue_next(new Add_archive_symbols(this->symtab_,
this->layout_, this->layout_,
this->input_objects_, this->input_objects_,
arch, arch,
this->input_group_, this->input_group_,
this->this_blocker_, this->this_blocker_,
this->next_blocker_)); this->next_blocker_));
return true; return true;
} }
} }
// Try to parse this file as a script. // Queue up a task to try to parse this file as a script. We use a
if (read_input_script(workqueue, this->options_, this->symtab_, // separate task so that the script will be read in order with other
this->layout_, this->dirpath_, this->input_objects_, // objects named on the command line. Also so that we don't try to
this->input_group_, this->input_argument_, input_file, // read multiple scripts simultaneously, which could lead to
ehdr_buf, read_size, this->this_blocker_, // unpredictable changes to the General_options structure.
this->next_blocker_))
return true;
// Here we have to handle any other input file types we need. workqueue->queue_soon(new Read_script(this->options_,
gold_error(_("%s: not an object or archive"), this->symtab_,
input_file->file().filename().c_str()); this->layout_,
this->dirpath_,
return false; this->input_objects_,
this->input_group_,
this->input_argument_,
input_file,
this->this_blocker_,
this->next_blocker_));
return true;
} }
// Handle a group. We need to walk through the arguments over and // Handle a group. We need to walk through the arguments over and
@ -258,21 +265,22 @@ Read_symbols::do_group(Workqueue* workqueue)
Task_token* next_blocker = new Task_token(true); Task_token* next_blocker = new Task_token(true);
next_blocker->add_blocker(); next_blocker->add_blocker();
workqueue->queue(new Read_symbols(this->options_, this->input_objects_, workqueue->queue_soon(new Read_symbols(this->options_,
this->symtab_, this->layout_, this->input_objects_,
this->dirpath_, arg, input_group, this->symtab_, this->layout_,
this_blocker, next_blocker)); this->dirpath_, arg, input_group,
this_blocker, next_blocker));
this_blocker = next_blocker; this_blocker = next_blocker;
} }
const int saw_undefined = this->symtab_->saw_undefined(); const int saw_undefined = this->symtab_->saw_undefined();
workqueue->queue(new Finish_group(this->input_objects_, workqueue->queue_soon(new Finish_group(this->input_objects_,
this->symtab_, this->symtab_,
this->layout_, this->layout_,
input_group, input_group,
saw_undefined, saw_undefined,
this_blocker, this_blocker,
this->next_blocker_)); this->next_blocker_));
} }
// Return a debugging name for a Read_symbols task. // Return a debugging name for a Read_symbols task.
@ -409,4 +417,69 @@ Finish_group::run(Workqueue*)
delete this->input_group_; delete this->input_group_;
} }
// Class Read_script
Read_script::~Read_script()
{
if (this->this_blocker_ != NULL)
delete this->this_blocker_;
// next_blocker_ is deleted by the task associated with the next
// input file.
}
// We are blocked by this_blocker_.
Task_token*
Read_script::is_runnable()
{
if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked())
return this->this_blocker_;
return NULL;
}
// We don't unlock next_blocker_ here. If the script names any input
// files, then the last file will be responsible for unlocking it.
void
Read_script::locks(Task_locker*)
{
}
// Read the script, if it is a script.
void
Read_script::run(Workqueue* workqueue)
{
bool used_next_blocker;
if (!read_input_script(workqueue, this->options_, this->symtab_,
this->layout_, this->dirpath_, this->input_objects_,
this->input_group_, this->input_argument_,
this->input_file_, this->next_blocker_,
&used_next_blocker))
{
// Here we have to handle any other input file types we need.
gold_error(_("%s: not an object or archive"),
this->input_file_->file().filename().c_str());
}
if (!used_next_blocker)
{
// Queue up a task to unlock next_blocker. We can't just unlock
// it here, as we don't hold the workqueue lock.
workqueue->queue_soon(new Unblock_token(NULL, this->next_blocker_));
}
}
// Return a debugging name for a Read_script task.
std::string
Read_script::get_name() const
{
std::string ret("Read_script ");
if (this->input_argument_->file().is_lib())
ret += "-l";
ret += this->input_argument_->file().name();
return ret;
}
} // End namespace gold. } // End namespace gold.

View File

@ -220,6 +220,53 @@ class Finish_group : public Task
Task_token* next_blocker_; Task_token* next_blocker_;
}; };
// This class is used to read a file which was not recognized as an
// object or archive. It tries to read it as a linker script, using
// the tokens to serialize with the calls to Add_symbols.
class Read_script : public Task
{
public:
Read_script(const General_options& options, Symbol_table* symtab,
Layout* layout, Dirsearch* dirpath, Input_objects* input_objects,
Input_group* input_group, const Input_argument* input_argument,
Input_file* input_file, Task_token* this_blocker,
Task_token* next_blocker)
: options_(options), symtab_(symtab), layout_(layout), dirpath_(dirpath),
input_objects_(input_objects), input_group_(input_group),
input_argument_(input_argument), input_file_(input_file),
this_blocker_(this_blocker), next_blocker_(next_blocker)
{ }
~Read_script();
// The standard Task methods.
Task_token*
is_runnable();
void
locks(Task_locker*);
void
run(Workqueue*);
std::string
get_name() const;
private:
const General_options& options_;
Symbol_table* symtab_;
Layout* layout_;
Dirsearch* dirpath_;
Input_objects* input_objects_;
Input_group* input_group_;
const Input_argument* input_argument_;
Input_file* input_file_;
Task_token* this_blocker_;
Task_token* next_blocker_;
};
} // end namespace gold } // end namespace gold
#endif // !defined(GOLD_READSYMS_H) #endif // !defined(GOLD_READSYMS_H)

View File

@ -64,9 +64,9 @@ Read_relocs::run(Workqueue* workqueue)
this->object_->read_relocs(rd); this->object_->read_relocs(rd);
this->object_->release(); this->object_->release();
workqueue->queue_front(new Scan_relocs(this->options_, this->symtab_, workqueue->queue_next(new Scan_relocs(this->options_, this->symtab_,
this->layout_, this->object_, rd, this->layout_, this->object_, rd,
this->symtab_lock_, this->blocker_)); this->symtab_lock_, this->blocker_));
} }
// Return a debugging name for the task. // Return a debugging name for the task.

View File

@ -846,47 +846,6 @@ Lex::next_token()
return &this->token_; return &this->token_;
} }
// A trivial task which waits for THIS_BLOCKER to be clear and then
// clears NEXT_BLOCKER. THIS_BLOCKER may be NULL.
class Script_unblock : public Task
{
public:
Script_unblock(Task_token* this_blocker, Task_token* next_blocker)
: this_blocker_(this_blocker), next_blocker_(next_blocker)
{ }
~Script_unblock()
{
if (this->this_blocker_ != NULL)
delete this->this_blocker_;
}
Task_token*
is_runnable()
{
if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked())
return this->this_blocker_;
return NULL;
}
void
locks(Task_locker* tl)
{ tl->add(this, this->next_blocker_); }
void
run(Workqueue*)
{ }
std::string
get_name() const
{ return "Script_unblock"; }
private:
Task_token* this_blocker_;
Task_token* next_blocker_;
};
// class Symbol_assignment. // class Symbol_assignment.
// Add the symbol to the symbol table. This makes sure the symbol is // Add the symbol to the symbol table. This makes sure the symbol is
@ -1347,8 +1306,7 @@ class Parser_closure
}; };
// FILE was found as an argument on the command line. Try to read it // FILE was found as an argument on the command line. Try to read it
// as a script. We've already read BYTES of data into P, but we // as a script. Return true if the file was handled.
// ignore that. Return true if the file was handled.
bool bool
read_input_script(Workqueue* workqueue, const General_options& options, read_input_script(Workqueue* workqueue, const General_options& options,
@ -1356,9 +1314,11 @@ read_input_script(Workqueue* workqueue, const General_options& options,
Dirsearch* dirsearch, Input_objects* input_objects, Dirsearch* dirsearch, Input_objects* input_objects,
Input_group* input_group, Input_group* input_group,
const Input_argument* input_argument, const Input_argument* input_argument,
Input_file* input_file, const unsigned char*, off_t, Input_file* input_file, Task_token* next_blocker,
Task_token* this_blocker, Task_token* next_blocker) bool* used_next_blocker)
{ {
*used_next_blocker = false;
std::string input_string; std::string input_string;
Lex::read_file(input_file, &input_string); Lex::read_file(input_file, &input_string);
@ -1375,20 +1335,10 @@ read_input_script(Workqueue* workqueue, const General_options& options,
if (yyparse(&closure) != 0) if (yyparse(&closure) != 0)
return false; return false;
// THIS_BLOCKER must be clear before we may add anything to the
// symbol table. We are responsible for unblocking NEXT_BLOCKER
// when we are done. We are responsible for deleting THIS_BLOCKER
// when it is unblocked.
if (!closure.saw_inputs()) if (!closure.saw_inputs())
{ return true;
// The script did not add any files to read. Note that we are
// not permitted to call NEXT_BLOCKER->unblock() here even if
// THIS_BLOCKER is NULL, as we do not hold the workqueue lock.
workqueue->queue(new Script_unblock(this_blocker, next_blocker));
return true;
}
Task_token* this_blocker = NULL;
for (Input_arguments::const_iterator p = closure.inputs()->begin(); for (Input_arguments::const_iterator p = closure.inputs()->begin();
p != closure.inputs()->end(); p != closure.inputs()->end();
++p) ++p)
@ -1401,12 +1351,14 @@ read_input_script(Workqueue* workqueue, const General_options& options,
nb = new Task_token(true); nb = new Task_token(true);
nb->add_blocker(); nb->add_blocker();
} }
workqueue->queue(new Read_symbols(options, input_objects, symtab, workqueue->queue_soon(new Read_symbols(options, input_objects, symtab,
layout, dirsearch, &*p, layout, dirsearch, &*p,
input_group, this_blocker, nb)); input_group, this_blocker, nb));
this_blocker = nb; this_blocker = nb;
} }
*used_next_blocker = true;
return true; return true;
} }

View File

@ -381,17 +381,16 @@ class Script_options
}; };
// FILE was found as an argument on the command line, but was not // FILE was found as an argument on the command line, but was not
// recognized as an ELF file. Try to read it as a script. We've // recognized as an ELF file. Try to read it as a script. Return
// already read BYTES of data into P. Return true if the file was // true if the file was handled. This has to handle /usr/lib/libc.so
// handled. This has to handle /usr/lib/libc.so on a GNU/Linux // on a GNU/Linux system. *USED_NEXT_BLOCKER is set to indicate
// system. // whether the function took over NEXT_BLOCKER.
bool bool
read_input_script(Workqueue*, const General_options&, Symbol_table*, Layout*, read_input_script(Workqueue*, const General_options&, Symbol_table*, Layout*,
Dirsearch*, Input_objects*, Input_group*, Dirsearch*, Input_objects*, Input_group*,
const Input_argument*, Input_file*, const unsigned char* p, const Input_argument*, Input_file*,
off_t bytes, Task_token* this_blocker, Task_token* next_blocker, bool* used_next_blocker);
Task_token* next_blocker);
// FILE was found as an argument to --script (-T). // FILE was found as an argument to --script (-T).
// Read it as a script, and execute its contents immediately. // Read it as a script, and execute its contents immediately.

View File

@ -48,6 +48,10 @@ class Task_list
empty() const empty() const
{ return this->head_ == NULL; } { return this->head_ == NULL; }
// Add T to the head of the list.
void
push_front(Task* t);
// Add T to the end of the list. // Add T to the end of the list.
void void
push_back(Task* t); push_back(Task* t);
@ -166,6 +170,12 @@ class Task_token
add_waiting(Task* t) add_waiting(Task* t)
{ this->waiting_.push_back(t); } { this->waiting_.push_back(t); }
// Add T to the front of the list of tasks waiting for this token to
// be released.
void
add_waiting_front(Task* t)
{ this->waiting_.push_front(t); }
// Remove the first Task waiting for this token to be released, and // Remove the first Task waiting for this token to be released, and
// return it. Return NULL if no Tasks are waiting. // return it. Return NULL if no Tasks are waiting.
Task* Task*

View File

@ -50,6 +50,24 @@ Task_list::push_back(Task* t)
} }
} }
// Add T to the front of the list.
inline void
Task_list::push_front(Task* t)
{
gold_assert(t->list_next() == NULL);
if (this->head_ == NULL)
{
this->head_ = t;
this->tail_ = t;
}
else
{
t->set_list_next(this->head_);
this->head_ = t;
}
}
// Remove and return the first Task waiting for this lock to be // Remove and return the first Task waiting for this lock to be
// released. // released.
@ -130,19 +148,25 @@ Workqueue::~Workqueue()
// waiting for a Token. // waiting for a Token.
void void
Workqueue::add_to_queue(Task_list* queue, Task* t) Workqueue::add_to_queue(Task_list* queue, Task* t, bool front)
{ {
Hold_lock hl(this->lock_); Hold_lock hl(this->lock_);
Task_token* token = t->is_runnable(); Task_token* token = t->is_runnable();
if (token != NULL) if (token != NULL)
{ {
token->add_waiting(t); if (front)
token->add_waiting_front(t);
else
token->add_waiting(t);
++this->waiting_; ++this->waiting_;
} }
else else
{ {
queue->push_back(t); if (front)
queue->push_front(t);
else
queue->push_back(t);
// Tell any waiting thread that there is work to do. // Tell any waiting thread that there is work to do.
this->condvar_.signal(); this->condvar_.signal();
} }
@ -153,16 +177,25 @@ Workqueue::add_to_queue(Task_list* queue, Task* t)
void void
Workqueue::queue(Task* t) Workqueue::queue(Task* t)
{ {
this->add_to_queue(&this->tasks_, t); this->add_to_queue(&this->tasks_, t, false);
} }
// Add a task to the front of the queue. // Queue a task which should run soon.
void void
Workqueue::queue_front(Task* t) Workqueue::queue_soon(Task* t)
{ {
t->set_should_run_soon(); t->set_should_run_soon();
this->add_to_queue(&this->first_tasks_, t); this->add_to_queue(&this->first_tasks_, t, false);
}
// Queue a task which should run next.
void
Workqueue::queue_next(Task* t)
{
t->set_should_run_soon();
this->add_to_queue(&this->first_tasks_, t, true);
} }
// Return whether to cancel the current thread. // Return whether to cancel the current thread.

View File

@ -205,10 +205,16 @@ class Workqueue
void void
queue(Task*); queue(Task*);
// Add a new task to the front of the work queue. It will be the // Add a new task to the work queue which should run soon. If the
// next task to run if it is ready. // task is ready, it will be run before any tasks added using
// queue().
void void
queue_front(Task*); queue_soon(Task*);
// Add a new task to the work queue which should run next if it is
// ready.
void
queue_next(Task*);
// Process all the tasks on the work queue. This function runs // Process all the tasks on the work queue. This function runs
// until all tasks have completed. The argument is the thread // until all tasks have completed. The argument is the thread
@ -228,7 +234,7 @@ class Workqueue
// Add a task to a queue. // Add a task to a queue.
void void
add_to_queue(Task_list* queue, Task* t); add_to_queue(Task_list* queue, Task* t, bool front);
// Find a runnable task, or wait for one. // Find a runnable task, or wait for one.
Task* Task*