From da769d5629564b82d4eec1b256ffc562d5c01624 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 28 Feb 2008 19:46:06 +0000 Subject: [PATCH] Read input scripts which look like input objects with proper serialization. --- gold/readsyms.cc | 149 ++++++++++++++++++++++++++++++++++++++++-------------- gold/readsyms.h | 47 +++++++++++++++++ gold/reloc.cc | 6 +-- gold/script.cc | 72 +++++--------------------- gold/script.h | 13 +++-- gold/token.h | 10 ++++ gold/workqueue.cc | 47 ++++++++++++++--- gold/workqueue.h | 14 +++-- 8 files changed, 239 insertions(+), 119 deletions(-) diff --git a/gold/readsyms.cc b/gold/readsyms.cc index 2ba8dbb..1658a58 100644 --- a/gold/readsyms.cc +++ b/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; - - // 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; + // 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. + + 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. diff --git a/gold/readsyms.h b/gold/readsyms.h index 7a4db41..fa870b9 100644 --- a/gold/readsyms.h +++ b/gold/readsyms.h @@ -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) diff --git a/gold/reloc.cc b/gold/reloc.cc index 405d1da..fde7e9e 100644 --- a/gold/reloc.cc +++ b/gold/reloc.cc @@ -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. diff --git a/gold/script.cc b/gold/script.cc index 42fb71c..307d1cf 100644 --- a/gold/script.cc +++ b/gold/script.cc @@ -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; } diff --git a/gold/script.h b/gold/script.h index 78d9ff0..ea19cfa 100644 --- a/gold/script.h +++ b/gold/script.h @@ -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. diff --git a/gold/token.h b/gold/token.h index 49a7d51..6c99441 100644 --- a/gold/token.h +++ b/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* diff --git a/gold/workqueue.cc b/gold/workqueue.cc index 647daf2..68d716e 100644 --- a/gold/workqueue.cc +++ b/gold/workqueue.cc @@ -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); +} + +// Queue a task which should run soon. + +void +Workqueue::queue_soon(Task* t) +{ + t->set_should_run_soon(); + this->add_to_queue(&this->first_tasks_, t, false); } -// Add a task to the front of the queue. +// Queue a task which should run next. void -Workqueue::queue_front(Task* t) +Workqueue::queue_next(Task* t) { t->set_should_run_soon(); - this->add_to_queue(&this->first_tasks_, t); + this->add_to_queue(&this->first_tasks_, t, true); } // Return whether to cancel the current thread. diff --git a/gold/workqueue.h b/gold/workqueue.h index 5f2137e..7c87d15 100644 --- a/gold/workqueue.h +++ b/gold/workqueue.h @@ -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* -- 2.7.4