Read input scripts which look like input objects with proper
authorIan Lance Taylor <iant@google.com>
Thu, 28 Feb 2008 19:46:06 +0000 (19:46 +0000)
committerIan Lance Taylor <iant@google.com>
Thu, 28 Feb 2008 19:46:06 +0000 (19:46 +0000)
serialization.

gold/readsyms.cc
gold/readsyms.h
gold/reloc.cc
gold/script.cc
gold/script.h
gold/token.h
gold/workqueue.cc
gold/workqueue.h

index 2ba8dbb..1658a58 100644 (file)
@@ -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.
index 7a4db41..fa870b9 100644 (file)
@@ -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)
index 405d1da..fde7e9e 100644 (file)
@@ -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.
index 42fb71c..307d1cf 100644 (file)
@@ -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;
 }
 
index 78d9ff0..ea19cfa 100644 (file)
@@ -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.
index 49a7d51..6c99441 100644 (file)
@@ -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*
index 647daf2..68d716e 100644 (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
 // 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.
index 5f2137e..7c87d15 100644 (file)
@@ -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*