mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-23 18:14:13 +08:00
Read input scripts which look like input objects with proper
serialization.
This commit is contained in:
parent
a7dfd0102b
commit
da769d5629
147
gold/readsyms.cc
147
gold/readsyms.cc
@ -120,8 +120,8 @@ Read_symbols::run(Workqueue* workqueue)
|
||||
// If we didn't queue a new task, then we need to explicitly unblock
|
||||
// the token.
|
||||
if (!this->do_read_symbols(workqueue))
|
||||
workqueue->queue_front(new Unblock_token(this->this_blocker_,
|
||||
this->next_blocker_));
|
||||
workqueue->queue_soon(new Unblock_token(this->this_blocker_,
|
||||
this->next_blocker_));
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
workqueue->queue_front(new Add_symbols(this->input_objects_,
|
||||
this->symtab_, this->layout_,
|
||||
obj, sd,
|
||||
this->this_blocker_,
|
||||
this->next_blocker_));
|
||||
// We use queue_next because everything is cached for this
|
||||
// task to run right away if possible.
|
||||
|
||||
workqueue->queue_next(new Add_symbols(this->input_objects_,
|
||||
this->symtab_, this->layout_,
|
||||
obj, sd,
|
||||
this->this_blocker_,
|
||||
this->next_blocker_));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -208,30 +211,34 @@ Read_symbols::do_read_symbols(Workqueue* workqueue)
|
||||
input_file);
|
||||
arch->setup(this);
|
||||
|
||||
workqueue->queue_front(new Add_archive_symbols(this->symtab_,
|
||||
this->layout_,
|
||||
this->input_objects_,
|
||||
arch,
|
||||
this->input_group_,
|
||||
this->this_blocker_,
|
||||
this->next_blocker_));
|
||||
workqueue->queue_next(new Add_archive_symbols(this->symtab_,
|
||||
this->layout_,
|
||||
this->input_objects_,
|
||||
arch,
|
||||
this->input_group_,
|
||||
this->this_blocker_,
|
||||
this->next_blocker_));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to parse this file as a script.
|
||||
if (read_input_script(workqueue, this->options_, this->symtab_,
|
||||
this->layout_, this->dirpath_, this->input_objects_,
|
||||
this->input_group_, this->input_argument_, input_file,
|
||||
ehdr_buf, read_size, this->this_blocker_,
|
||||
this->next_blocker_))
|
||||
return true;
|
||||
// Queue up a task to try to parse this file as a script. We use a
|
||||
// separate task so that the script will be read in order with other
|
||||
// objects named on the command line. Also so that we don't try to
|
||||
// read multiple scripts simultaneously, which could lead to
|
||||
// unpredictable changes to the General_options structure.
|
||||
|
||||
// Here we have to handle any other input file types we need.
|
||||
gold_error(_("%s: not an object or archive"),
|
||||
input_file->file().filename().c_str());
|
||||
|
||||
return false;
|
||||
workqueue->queue_soon(new Read_script(this->options_,
|
||||
this->symtab_,
|
||||
this->layout_,
|
||||
this->dirpath_,
|
||||
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
|
||||
@ -258,21 +265,22 @@ Read_symbols::do_group(Workqueue* workqueue)
|
||||
|
||||
Task_token* next_blocker = new Task_token(true);
|
||||
next_blocker->add_blocker();
|
||||
workqueue->queue(new Read_symbols(this->options_, this->input_objects_,
|
||||
this->symtab_, this->layout_,
|
||||
this->dirpath_, arg, input_group,
|
||||
this_blocker, next_blocker));
|
||||
workqueue->queue_soon(new Read_symbols(this->options_,
|
||||
this->input_objects_,
|
||||
this->symtab_, this->layout_,
|
||||
this->dirpath_, arg, input_group,
|
||||
this_blocker, next_blocker));
|
||||
this_blocker = next_blocker;
|
||||
}
|
||||
|
||||
const int saw_undefined = this->symtab_->saw_undefined();
|
||||
workqueue->queue(new Finish_group(this->input_objects_,
|
||||
this->symtab_,
|
||||
this->layout_,
|
||||
input_group,
|
||||
saw_undefined,
|
||||
this_blocker,
|
||||
this->next_blocker_));
|
||||
workqueue->queue_soon(new Finish_group(this->input_objects_,
|
||||
this->symtab_,
|
||||
this->layout_,
|
||||
input_group,
|
||||
saw_undefined,
|
||||
this_blocker,
|
||||
this->next_blocker_));
|
||||
}
|
||||
|
||||
// Return a debugging name for a Read_symbols task.
|
||||
@ -409,4 +417,69 @@ Finish_group::run(Workqueue*)
|
||||
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.
|
||||
|
@ -220,6 +220,53 @@ class Finish_group : public Task
|
||||
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
|
||||
|
||||
#endif // !defined(GOLD_READSYMS_H)
|
||||
|
@ -64,9 +64,9 @@ Read_relocs::run(Workqueue* workqueue)
|
||||
this->object_->read_relocs(rd);
|
||||
this->object_->release();
|
||||
|
||||
workqueue->queue_front(new Scan_relocs(this->options_, this->symtab_,
|
||||
this->layout_, this->object_, rd,
|
||||
this->symtab_lock_, this->blocker_));
|
||||
workqueue->queue_next(new Scan_relocs(this->options_, this->symtab_,
|
||||
this->layout_, this->object_, rd,
|
||||
this->symtab_lock_, this->blocker_));
|
||||
}
|
||||
|
||||
// Return a debugging name for the task.
|
||||
|
@ -846,47 +846,6 @@ Lex::next_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.
|
||||
|
||||
// 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
|
||||
// as a script. We've already read BYTES of data into P, but we
|
||||
// ignore that. Return true if the file was handled.
|
||||
// as a script. Return true if the file was handled.
|
||||
|
||||
bool
|
||||
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,
|
||||
Input_group* input_group,
|
||||
const Input_argument* input_argument,
|
||||
Input_file* input_file, const unsigned char*, off_t,
|
||||
Task_token* this_blocker, Task_token* next_blocker)
|
||||
Input_file* input_file, Task_token* next_blocker,
|
||||
bool* used_next_blocker)
|
||||
{
|
||||
*used_next_blocker = false;
|
||||
|
||||
std::string 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)
|
||||
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())
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
return true;
|
||||
|
||||
Task_token* this_blocker = NULL;
|
||||
for (Input_arguments::const_iterator p = closure.inputs()->begin();
|
||||
p != closure.inputs()->end();
|
||||
++p)
|
||||
@ -1401,12 +1351,14 @@ read_input_script(Workqueue* workqueue, const General_options& options,
|
||||
nb = new Task_token(true);
|
||||
nb->add_blocker();
|
||||
}
|
||||
workqueue->queue(new Read_symbols(options, input_objects, symtab,
|
||||
layout, dirsearch, &*p,
|
||||
input_group, this_blocker, nb));
|
||||
workqueue->queue_soon(new Read_symbols(options, input_objects, symtab,
|
||||
layout, dirsearch, &*p,
|
||||
input_group, this_blocker, nb));
|
||||
this_blocker = nb;
|
||||
}
|
||||
|
||||
*used_next_blocker = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -381,17 +381,16 @@ class Script_options
|
||||
};
|
||||
|
||||
// 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
|
||||
// already read BYTES of data into P. Return true if the file was
|
||||
// handled. This has to handle /usr/lib/libc.so on a GNU/Linux
|
||||
// system.
|
||||
// recognized as an ELF file. Try to read it as a script. Return
|
||||
// true if the file was handled. This has to handle /usr/lib/libc.so
|
||||
// on a GNU/Linux system. *USED_NEXT_BLOCKER is set to indicate
|
||||
// whether the function took over NEXT_BLOCKER.
|
||||
|
||||
bool
|
||||
read_input_script(Workqueue*, const General_options&, Symbol_table*, Layout*,
|
||||
Dirsearch*, Input_objects*, Input_group*,
|
||||
const Input_argument*, Input_file*, const unsigned char* p,
|
||||
off_t bytes, Task_token* this_blocker,
|
||||
Task_token* next_blocker);
|
||||
const Input_argument*, Input_file*,
|
||||
Task_token* next_blocker, bool* used_next_blocker);
|
||||
|
||||
// FILE was found as an argument to --script (-T).
|
||||
// Read it as a script, and execute its contents immediately.
|
||||
|
10
gold/token.h
10
gold/token.h
@ -48,6 +48,10 @@ class Task_list
|
||||
empty() const
|
||||
{ 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.
|
||||
void
|
||||
push_back(Task* t);
|
||||
@ -166,6 +170,12 @@ class Task_token
|
||||
add_waiting(Task* 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
|
||||
// return it. Return NULL if no Tasks are waiting.
|
||||
Task*
|
||||
|
@ -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
|
||||
// released.
|
||||
|
||||
@ -130,19 +148,25 @@ Workqueue::~Workqueue()
|
||||
// waiting for a Token.
|
||||
|
||||
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_);
|
||||
|
||||
Task_token* token = t->is_runnable();
|
||||
if (token != NULL)
|
||||
{
|
||||
token->add_waiting(t);
|
||||
if (front)
|
||||
token->add_waiting_front(t);
|
||||
else
|
||||
token->add_waiting(t);
|
||||
++this->waiting_;
|
||||
}
|
||||
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.
|
||||
this->condvar_.signal();
|
||||
}
|
||||
@ -153,16 +177,25 @@ Workqueue::add_to_queue(Task_list* queue, Task* t)
|
||||
void
|
||||
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
|
||||
Workqueue::queue_front(Task* t)
|
||||
Workqueue::queue_soon(Task* t)
|
||||
{
|
||||
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.
|
||||
|
@ -205,10 +205,16 @@ class Workqueue
|
||||
void
|
||||
queue(Task*);
|
||||
|
||||
// Add a new task to the front of the work queue. It will be the
|
||||
// next task to run if it is ready.
|
||||
// Add a new task to the work queue which should run soon. If the
|
||||
// task is ready, it will be run before any tasks added using
|
||||
// queue().
|
||||
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
|
||||
// until all tasks have completed. The argument is the thread
|
||||
@ -228,7 +234,7 @@ class Workqueue
|
||||
|
||||
// Add a task to a queue.
|
||||
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.
|
||||
Task*
|
||||
|
Loading…
Reference in New Issue
Block a user