summaryrefslogtreecommitdiff
path: root/gold
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2007-11-22 00:05:51 +0000
committerIan Lance Taylor <iant@google.com>2007-11-22 00:05:51 +0000
commitc79126688f8211ab17a893c5e80b09811d424fc1 (patch)
tree23a727f6718dc203a4e3b9ef1575e8a10b6f0d80 /gold
parent06d063c072d0e247751535bc5e394aa7b8be3b0f (diff)
downloadbinutils-gdb-c79126688f8211ab17a893c5e80b09811d424fc1.tar.gz
Add threading support.
Diffstat (limited to 'gold')
-rw-r--r--gold/Makefile.am6
-rw-r--r--gold/Makefile.in10
-rw-r--r--gold/archive.cc1
-rw-r--r--gold/archive.h8
-rw-r--r--gold/common.cc7
-rw-r--r--gold/common.h4
-rw-r--r--gold/debug.h51
-rw-r--r--gold/dirsearch.cc21
-rw-r--r--gold/errors.cc46
-rw-r--r--gold/errors.h12
-rw-r--r--gold/gold-threads.cc275
-rw-r--r--gold/gold-threads.h67
-rw-r--r--gold/gold.cc9
-rw-r--r--gold/layout.h16
-rw-r--r--gold/options.cc65
-rw-r--r--gold/options.h18
-rw-r--r--gold/parameters.cc6
-rw-r--r--gold/parameters.h28
-rw-r--r--gold/po/POTFILES.in2
-rw-r--r--gold/po/gold.pot432
-rw-r--r--gold/readsyms.cc33
-rw-r--r--gold/readsyms.h11
-rw-r--r--gold/reloc.cc27
-rw-r--r--gold/reloc.h9
-rw-r--r--gold/script.cc4
-rw-r--r--gold/symtab.cc33
-rw-r--r--gold/symtab.h10
-rw-r--r--gold/workqueue-internal.h129
-rw-r--r--gold/workqueue-threads.cc266
-rw-r--r--gold/workqueue.cc213
-rw-r--r--gold/workqueue.h61
31 files changed, 1443 insertions, 437 deletions
diff --git a/gold/Makefile.am b/gold/Makefile.am
index a91a1c80cc8..de9be2062bf 100644
--- a/gold/Makefile.am
+++ b/gold/Makefile.am
@@ -53,7 +53,8 @@ CCFILES = \
stringpool.cc \
target-select.cc \
version.cc \
- workqueue.cc
+ workqueue.cc \
+ workqueue-threads.cc
HFILES = \
archive.h \
@@ -84,7 +85,8 @@ HFILES = \
target-reloc.h \
target-select.h \
tls.h \
- workqueue.h
+ workqueue.h \
+ workqueue-internal.h
YFILES = \
yyscript.y
diff --git a/gold/Makefile.in b/gold/Makefile.in
index 9ca37010131..fd981d6b987 100644
--- a/gold/Makefile.in
+++ b/gold/Makefile.in
@@ -78,7 +78,8 @@ am__objects_1 = archive.$(OBJEXT) common.$(OBJEXT) defstd.$(OBJEXT) \
output.$(OBJEXT) parameters.$(OBJEXT) readsyms.$(OBJEXT) \
reloc.$(OBJEXT) resolve.$(OBJEXT) script.$(OBJEXT) \
symtab.$(OBJEXT) stringpool.$(OBJEXT) target-select.$(OBJEXT) \
- version.$(OBJEXT) workqueue.$(OBJEXT)
+ version.$(OBJEXT) workqueue.$(OBJEXT) \
+ workqueue-threads.$(OBJEXT)
am__objects_2 =
am__objects_3 = yyscript.$(OBJEXT)
am_libgold_a_OBJECTS = $(am__objects_1) $(am__objects_2) \
@@ -307,7 +308,8 @@ CCFILES = \
stringpool.cc \
target-select.cc \
version.cc \
- workqueue.cc
+ workqueue.cc \
+ workqueue-threads.cc
HFILES = \
archive.h \
@@ -338,7 +340,8 @@ HFILES = \
target-reloc.h \
target-select.h \
tls.h \
- workqueue.h
+ workqueue.h \
+ workqueue-internal.h
YFILES = \
yyscript.y
@@ -487,6 +490,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symtab.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/target-select.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/workqueue-threads.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/workqueue.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x86_64.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/yyscript.Po@am__quote@
diff --git a/gold/archive.cc b/gold/archive.cc
index 1442731f045..dc12ea95701 100644
--- a/gold/archive.cc
+++ b/gold/archive.cc
@@ -475,6 +475,7 @@ Add_archive_symbols::run(Workqueue*)
{
// We no longer need to know about this archive.
delete this->archive_;
+ this->archive_ = NULL;
}
}
diff --git a/gold/archive.h b/gold/archive.h
index 61d4d4b891f..57af1673563 100644
--- a/gold/archive.h
+++ b/gold/archive.h
@@ -187,6 +187,14 @@ class Add_archive_symbols : public Task
void
run(Workqueue*);
+ std::string
+ get_name() const
+ {
+ if (this->archive_ == NULL)
+ return "Add_archive_symbols";
+ return "Add_archive_symbols " + this->archive_->file().filename();
+ }
+
private:
class Add_archive_symbols_locker;
diff --git a/gold/common.cc b/gold/common.cc
index f723de30e19..3b616b154a1 100644
--- a/gold/common.cc
+++ b/gold/common.cc
@@ -226,12 +226,9 @@ Symbol_table::do_allocate_commons(const General_options&,
off = align_address(off, ssym->value());
- Size_type symsize = ssym->symsize();
- ssym->init(ssym->name(), poc, off, symsize, ssym->type(),
- ssym->binding(), ssym->visibility(), ssym->nonvis(),
- false);
+ ssym->allocate_common(poc, off);
- off += symsize;
+ off += ssym->symsize();
}
poc->set_space_size(off);
diff --git a/gold/common.h b/gold/common.h
index e7fe84e4c66..bd24985c9be 100644
--- a/gold/common.h
+++ b/gold/common.h
@@ -54,6 +54,10 @@ class Allocate_commons_task : public Task
void
run(Workqueue*);
+ std::string
+ get_name() const
+ { return "Allocate_commons_task"; }
+
private:
class Allocate_commons_locker;
diff --git a/gold/debug.h b/gold/debug.h
new file mode 100644
index 00000000000..c6bfb7ae678
--- /dev/null
+++ b/gold/debug.h
@@ -0,0 +1,51 @@
+// debug.h -- gold internal debugging support -*- C++ -*-
+
+// Copyright 2007 Free Software Foundation, Inc.
+// Written by Ian Lance Taylor <iant@google.com>.
+
+// This file is part of gold.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+// MA 02110-1301, USA.
+
+#ifndef GOLD_DEBUG_H
+#define GOLD_DEBUG_H
+
+#include "parameters.h"
+#include "errors.h"
+
+namespace gold
+{
+
+// The different types of debugging we support. These are bitflags.
+
+const int DEBUG_TASK = 1;
+
+const int DEBUG_ALL = DEBUG_TASK;
+
+// Print a debug message if TYPE is enabled. This is a macro so that
+// we only evaluate the arguments if necessary.
+
+#define gold_debug(TYPE, FORMAT, ...) \
+ do \
+ { \
+ if (is_debugging_enabled(TYPE)) \
+ parameters->errors()->debug(FORMAT, __VA_ARGS__); \
+ } \
+ while (0)
+
+} // End namespace gold.
+
+#endif // !defined(GOLD_DEBUG_H)
diff --git a/gold/dirsearch.cc b/gold/dirsearch.cc
index 45caf86daf2..dd1c7e6eb2c 100644
--- a/gold/dirsearch.cc
+++ b/gold/dirsearch.cc
@@ -158,7 +158,7 @@ Dir_caches::lookup(const char* dirname) const
// The caches.
-Dir_caches caches;
+Dir_caches* caches;
// A Task to read the directory.
@@ -169,11 +169,18 @@ class Dir_cache_task : public gold::Task
: dir_(dir), token_(token)
{ }
- Is_runnable_type is_runnable(gold::Workqueue*);
+ Is_runnable_type
+ is_runnable(gold::Workqueue*);
- gold::Task_locker* locks(gold::Workqueue*);
+ gold::Task_locker*
+ locks(gold::Workqueue*);
- void run(gold::Workqueue*);
+ void
+ run(gold::Workqueue*);
+
+ std::string
+ get_name() const
+ { return std::string("Dir_cache_task ") + this->dir_; }
private:
const char* dir_;
@@ -202,7 +209,7 @@ Dir_cache_task::locks(gold::Workqueue* workqueue)
void
Dir_cache_task::run(gold::Workqueue*)
{
- caches.add(this->dir_);
+ caches->add(this->dir_);
}
}
@@ -214,6 +221,8 @@ void
Dirsearch::initialize(Workqueue* workqueue,
const General_options::Dir_list* directories)
{
+ gold_assert(caches == NULL);
+ caches = new Dir_caches;
this->directories_ = directories;
for (General_options::Dir_list::const_iterator p = directories->begin();
p != directories->end();
@@ -235,7 +244,7 @@ Dirsearch::find(const std::string& n1, const std::string& n2,
p != this->directories_->end();
++p)
{
- Dir_cache* pdc = caches.lookup(p->name().c_str());
+ Dir_cache* pdc = caches->lookup(p->name().c_str());
gold_assert(pdc != NULL);
if (pdc->find(n1))
{
diff --git a/gold/errors.cc b/gold/errors.cc
index 2da1a2569ed..c979463dfc2 100644
--- a/gold/errors.cc
+++ b/gold/errors.cc
@@ -39,11 +39,20 @@ namespace gold
const int Errors::max_undefined_error_report;
Errors::Errors(const char* program_name)
- : program_name_(program_name), lock_(), error_count_(0), warning_count_(0),
- undefined_symbols_()
+ : program_name_(program_name), lock_(NULL), error_count_(0),
+ warning_count_(0), undefined_symbols_()
{
}
+// Initialize the lock_ field.
+
+void
+Errors::initialize_lock()
+{
+ if (this->lock_ == NULL)
+ this->lock_ = new Lock;
+}
+
// Report a fatal error.
void
@@ -63,8 +72,10 @@ Errors::error(const char* format, va_list args)
fprintf(stderr, "%s: ", this->program_name_);
vfprintf(stderr, format, args);
fputc('\n', stderr);
+
+ this->initialize_lock();
{
- Hold_lock h(this->lock_);
+ Hold_lock h(*this->lock_);
++this->error_count_;
}
}
@@ -77,8 +88,10 @@ Errors::warning(const char* format, va_list args)
fprintf(stderr, _("%s: warning: "), this->program_name_);
vfprintf(stderr, format, args);
fputc('\n', stderr);
+
+ this->initialize_lock();
{
- Hold_lock h(this->lock_);
+ Hold_lock h(*this->lock_);
++this->warning_count_;
}
}
@@ -95,8 +108,10 @@ Errors::error_at_location(const Relocate_info<size, big_endian>* relinfo,
relinfo->location(relnum, reloffset).c_str());
vfprintf(stderr, format, args);
fputc('\n', stderr);
+
+ this->initialize_lock();
{
- Hold_lock h(this->lock_);
+ Hold_lock h(*this->lock_);
++this->error_count_;
}
}
@@ -113,8 +128,10 @@ Errors::warning_at_location(const Relocate_info<size, big_endian>* relinfo,
relinfo->location(relnum, reloffset).c_str());
vfprintf(stderr, format, args);
fputc('\n', stderr);
+
+ this->initialize_lock();
{
- Hold_lock h(this->lock_);
+ Hold_lock h(*this->lock_);
++this->warning_count_;
}
}
@@ -127,8 +144,9 @@ Errors::undefined_symbol(const Symbol* sym,
const Relocate_info<size, big_endian>* relinfo,
size_t relnum, off_t reloffset)
{
+ this->initialize_lock();
{
- Hold_lock h(this->lock_);
+ Hold_lock h(*this->lock_);
if (++this->undefined_symbols_[sym] >= max_undefined_error_report)
return;
++this->error_count_;
@@ -138,6 +156,20 @@ Errors::undefined_symbol(const Symbol* sym,
sym->demangled_name().c_str());
}
+// Issue a debugging message.
+
+void
+Errors::debug(const char* format, ...)
+{
+ fprintf(stderr, _("%s: "), this->program_name_);
+
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+
+ fputc('\n', stderr);
+}
// The functions which the rest of the code actually calls.
diff --git a/gold/errors.h b/gold/errors.h
index 545c463bad3..17b72e10b36 100644
--- a/gold/errors.h
+++ b/gold/errors.h
@@ -80,6 +80,10 @@ class Errors
const Relocate_info<size, big_endian>* relinfo,
size_t relnum, off_t reloffset);
+ // Report a debugging message.
+ void
+ debug(const char* format, ...) ATTRIBUTE_PRINTF_2;
+
// Return the number of errors.
int
error_count() const
@@ -89,6 +93,12 @@ class Errors
Errors(const Errors&);
Errors& operator=(const Errors&);
+ // Initialize the lock. We don't do this in the constructor because
+ // lock initialization wants to know whether we are using threads or
+ // not.
+ void
+ initialize_lock();
+
// The number of times we report an undefined symbol.
static const int max_undefined_error_report = 5;
@@ -96,7 +106,7 @@ class Errors
const char* program_name_;
// This class can be accessed from multiple threads. This lock is
// used to control access to the data structures.
- Lock lock_;
+ Lock* lock_;
// Numbers of errors reported.
int error_count_;
// Number of warnings reported.
diff --git a/gold/gold-threads.cc b/gold/gold-threads.cc
index 396e6c10631..4ebbdd84960 100644
--- a/gold/gold-threads.cc
+++ b/gold/gold-threads.cc
@@ -22,25 +22,63 @@
#include "gold.h"
-#include <cerrno>
#include <cstring>
#ifdef ENABLE_THREADS
#include <pthread.h>
#endif
+#include "parameters.h"
#include "gold-threads.h"
namespace gold
{
-// Class Lock_impl.
+class Condvar_impl_nothreads;
-class Lock_impl
+// The non-threaded version of Lock_impl.
+
+class Lock_impl_nothreads : public Lock_impl
{
public:
- Lock_impl();
- ~Lock_impl();
+ Lock_impl_nothreads()
+ : acquired_(false)
+ { }
+
+ ~Lock_impl_nothreads()
+ { gold_assert(!this->acquired_); }
+
+ void
+ acquire()
+ {
+ gold_assert(!this->acquired_);
+ this->acquired_ = true;
+ }
+
+ void
+ release()
+ {
+ gold_assert(this->acquired_);
+ this->acquired_ = false;
+ }
+
+ private:
+ friend class Condvar_impl_nothreads;
+
+ bool acquired_;
+};
+
+#ifdef ENABLE_THREADS
+
+class Condvar_impl_threads;
+
+// The threaded version of Lock_impl.
+
+class Lock_impl_threads : public Lock_impl
+{
+ public:
+ Lock_impl_threads();
+ ~Lock_impl_threads();
void acquire();
@@ -48,187 +86,188 @@ class Lock_impl
private:
// This class can not be copied.
- Lock_impl(const Lock_impl&);
- Lock_impl& operator=(const Lock_impl&);
+ Lock_impl_threads(const Lock_impl_threads&);
+ Lock_impl_threads& operator=(const Lock_impl_threads&);
- friend class Condvar_impl;
+ friend class Condvar_impl_threads;
-#ifdef ENABLE_THREADS
pthread_mutex_t mutex_;
-#else
- bool acquired_;
-#endif
};
-#ifdef ENABLE_THREADS
-
-Lock_impl::Lock_impl()
+Lock_impl_threads::Lock_impl_threads()
{
pthread_mutexattr_t attr;
- if (pthread_mutexattr_init(&attr) != 0)
- gold_fatal(_("pthead_mutextattr_init failed: %s"), strerror(errno));
+ int err = pthread_mutexattr_init(&attr);
+ if (err != 0)
+ gold_fatal(_("pthead_mutextattr_init failed: %s"), strerror(err));
#ifdef PTHREAD_MUTEXT_ADAPTIVE_NP
- if (pthread_mutextattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP) != 0)
- gold_fatal(_("pthread_mutextattr_settype failed: %s"), strerror(errno));
+ err = pthread_mutextattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
+ if (err != 0)
+ gold_fatal(_("pthread_mutextattr_settype failed: %s"), strerror(err));
#endif
- if (pthread_mutex_init (&this->mutex_, &attr) != 0)
- gold_fatal(_("pthread_mutex_init failed: %s"), strerror(errno));
+ err = pthread_mutex_init (&this->mutex_, &attr);
+ if (err != 0)
+ gold_fatal(_("pthread_mutex_init failed: %s"), strerror(err));
- if (pthread_mutexattr_destroy(&attr) != 0)
- gold_fatal(_("pthread_mutexattr_destroy failed: %s"), strerror(errno));
+ err = pthread_mutexattr_destroy(&attr);
+ if (err != 0)
+ gold_fatal(_("pthread_mutexattr_destroy failed: %s"), strerror(err));
}
-Lock_impl::~Lock_impl()
+Lock_impl_threads::~Lock_impl_threads()
{
- if (pthread_mutex_destroy(&this->mutex_) != 0)
- gold_fatal(_("pthread_mutex_destroy failed: %s"), strerror(errno));
+ int err = pthread_mutex_destroy(&this->mutex_);
+ if (err != 0)
+ gold_fatal(_("pthread_mutex_destroy failed: %s"), strerror(err));
}
void
-Lock_impl::acquire()
+Lock_impl_threads::acquire()
{
- if (pthread_mutex_lock(&this->mutex_) != 0)
- gold_fatal(_("pthread_mutex_lock failed: %s"), strerror(errno));
+ int err = pthread_mutex_lock(&this->mutex_);
+ if (err != 0)
+ gold_fatal(_("pthread_mutex_lock failed: %s"), strerror(err));
}
void
-Lock_impl::release()
+Lock_impl_threads::release()
{
- if (pthread_mutex_unlock(&this->mutex_) != 0)
- gold_fatal(_("pthread_mutex_unlock failed: %s"), strerror(errno));
+ int err = pthread_mutex_unlock(&this->mutex_);
+ if (err != 0)
+ gold_fatal(_("pthread_mutex_unlock failed: %s"), strerror(err));
}
-#else // !defined(ENABLE_THREADS)
+#endif // defined(ENABLE_THREADS)
-Lock_impl::Lock_impl()
- : acquired_(false)
-{
-}
+// Class Lock.
-Lock_impl::~Lock_impl()
+Lock::Lock()
{
- gold_assert(!this->acquired_);
+ if (!parameters->threads())
+ this->lock_ = new Lock_impl_nothreads;
+ else
+ {
+#ifdef ENABLE_THREADS
+ this->lock_ = new Lock_impl_threads;
+#else
+ gold_unreachable();
+#endif
+ }
}
-void
-Lock_impl::acquire()
+Lock::~Lock()
{
- gold_assert(!this->acquired_);
- this->acquired_ = true;
+ delete this->lock_;
}
-void
-Lock_impl::release()
+// The non-threaded version of Condvar_impl.
+
+class Condvar_impl_nothreads : public Condvar_impl
{
- gold_assert(this->acquired_);
- this->acquired_ = false;
-}
+ public:
+ Condvar_impl_nothreads()
+ { }
-#endif // !defined(ENABLE_THREADS)
+ ~Condvar_impl_nothreads()
+ { }
-// Methods for Lock class.
+ void
+ wait(Lock_impl* li)
+ { gold_assert(static_cast<Lock_impl_nothreads*>(li)->acquired_); }
-Lock::Lock()
-{
- this->lock_ = new Lock_impl;
-}
-
-Lock::~Lock()
-{
- delete this->lock_;
-}
+ void
+ signal()
+ { }
-void
-Lock::acquire()
-{
- this->lock_->acquire();
-}
+ void
+ broadcast()
+ { }
+};
-void
-Lock::release()
-{
- this->lock_->release();
-}
+#ifdef ENABLE_THREADS
-// Class Condvar_impl.
+// The threaded version of Condvar_impl.
-class Condvar_impl
+class Condvar_impl_threads : public Condvar_impl
{
public:
- Condvar_impl();
- ~Condvar_impl();
+ Condvar_impl_threads();
+ ~Condvar_impl_threads();
- void wait(Lock_impl*);
- void signal();
+ void
+ wait(Lock_impl*);
+
+ void
+ signal();
+
+ void
+ broadcast();
private:
// This class can not be copied.
- Condvar_impl(const Condvar_impl&);
- Condvar_impl& operator=(const Condvar_impl&);
+ Condvar_impl_threads(const Condvar_impl_threads&);
+ Condvar_impl_threads& operator=(const Condvar_impl_threads&);
-#ifdef ENABLE_THREADS
pthread_cond_t cond_;
-#endif
};
-#ifdef ENABLE_THREADS
-
-Condvar_impl::Condvar_impl()
-{
- if (pthread_cond_init(&this->cond_, NULL) != 0)
- gold_fatal(_("pthread_cond_init failed: %s"), strerror(errno));
-}
-
-Condvar_impl::~Condvar_impl()
+Condvar_impl_threads::Condvar_impl_threads()
{
- if (pthread_cond_destroy(&this->cond_) != 0)
- gold_fatal(_("pthread_cond_destroy failed: %s"), strerror(errno));
+ int err = pthread_cond_init(&this->cond_, NULL);
+ if (err != 0)
+ gold_fatal(_("pthread_cond_init failed: %s"), strerror(err));
}
-void
-Condvar_impl::wait(Lock_impl* li)
+Condvar_impl_threads::~Condvar_impl_threads()
{
- if (pthread_cond_wait(&this->cond_, &li->mutex_) != 0)
- gold_fatal(_("pthread_cond_wait failed: %s"), strerror(errno));
+ int err = pthread_cond_destroy(&this->cond_);
+ if (err != 0)
+ gold_fatal(_("pthread_cond_destroy failed: %s"), strerror(err));
}
void
-Condvar_impl::signal()
-{
- if (pthread_cond_signal(&this->cond_) != 0)
- gold_fatal(_("pthread_cond_signal failed: %s"), strerror(errno));
-}
-
-#else // !defined(ENABLE_THREADS)
-
-Condvar_impl::Condvar_impl()
-{
-}
-
-Condvar_impl::~Condvar_impl()
+Condvar_impl_threads::wait(Lock_impl* li)
{
+ Lock_impl_threads* lit = static_cast<Lock_impl_threads*>(li);
+ int err = pthread_cond_wait(&this->cond_, &lit->mutex_);
+ if (err != 0)
+ gold_fatal(_("pthread_cond_wait failed: %s"), strerror(err));
}
void
-Condvar_impl::wait(Lock_impl* li)
+Condvar_impl_threads::signal()
{
- gold_assert(li->acquired_);
+ int err = pthread_cond_signal(&this->cond_);
+ if (err != 0)
+ gold_fatal(_("pthread_cond_signal failed: %s"), strerror(err));
}
void
-Condvar_impl::signal()
+Condvar_impl_threads::broadcast()
{
+ int err = pthread_cond_broadcast(&this->cond_);
+ if (err != 0)
+ gold_fatal(_("pthread_cond_broadcast failed: %s"), strerror(err));
}
-#endif // !defined(ENABLE_THREADS)
+#endif // defined(ENABLE_THREADS)
// Methods for Condvar class.
Condvar::Condvar(Lock& lock)
: lock_(lock)
{
- this->condvar_ = new Condvar_impl;
+ if (!parameters->threads())
+ this->condvar_ = new Condvar_impl_nothreads;
+ else
+ {
+#ifdef ENABLE_THREADS
+ this->condvar_ = new Condvar_impl_threads;
+#else
+ gold_unreachable();
+#endif
+ }
}
Condvar::~Condvar()
@@ -236,16 +275,4 @@ Condvar::~Condvar()
delete this->condvar_;
}
-void
-Condvar::wait()
-{
- this->condvar_->wait(this->lock_.get_impl());
-}
-
-void
-Condvar::signal()
-{
- this->condvar_->signal();
-}
-
} // End namespace gold.
diff --git a/gold/gold-threads.h b/gold/gold-threads.h
index a6f1752efe8..45f18ad2c5c 100644
--- a/gold/gold-threads.h
+++ b/gold/gold-threads.h
@@ -34,24 +34,45 @@
namespace gold
{
-class Lock_impl;
class Condvar;
+// The interface for the implementation of a Lock.
+
+class Lock_impl
+{
+ public:
+ Lock_impl()
+ { }
+
+ virtual
+ ~Lock_impl()
+ { }
+
+ virtual void
+ acquire() = 0;
+
+ virtual void
+ release() = 0;
+};
+
// A simple lock class.
class Lock
{
public:
Lock();
+
~Lock();
// Acquire the lock.
void
- acquire();
+ acquire()
+ { this->lock_->acquire(); }
// Release the lock.
void
- release();
+ release()
+ { this->lock_->release(); }
private:
// This class can not be copied.
@@ -86,7 +107,27 @@ class Hold_lock
Lock& lock_;
};
-class Condvar_impl;
+// The interface for the implementation of a condition variable.
+
+class Condvar_impl
+{
+ public:
+ Condvar_impl()
+ { }
+
+ virtual
+ ~Condvar_impl()
+ { }
+
+ virtual void
+ wait(Lock_impl*) = 0;
+
+ virtual void
+ signal() = 0;
+
+ virtual void
+ broadcast() = 0;
+};
// A simple condition variable class. It is always associated with a
// specific lock.
@@ -100,12 +141,22 @@ class Condvar
// Wait for the condition variable to be signalled. This should
// only be called when the lock is held.
void
- wait();
+ wait()
+ { this->condvar_->wait(this->lock_.get_impl()); }
+
+ // Signal the condition variable--wake up at least one thread
+ // waiting on the condition variable. This should only be called
+ // when the lock is held.
+ void
+ signal()
+ { this->condvar_->signal(); }
- // Signal the condition variable. This should only be called when
- // the lock is held.
+ // Broadcast the condition variable--wake up all threads waiting on
+ // the condition variable. This should only be called when the lock
+ // is held.
void
- signal();
+ broadcast()
+ { this->condvar_->broadcast(); }
private:
// This class can not be copied.
diff --git a/gold/gold.cc b/gold/gold.cc
index a4f145efba6..9baebaf6022 100644
--- a/gold/gold.cc
+++ b/gold/gold.cc
@@ -143,7 +143,8 @@ queue_initial_tasks(const General_options& options,
input_objects,
symtab,
layout),
- this_blocker));
+ this_blocker,
+ "Task_function Middle_runner"));
}
// Queue up the middle set of tasks. These are the tasks which run
@@ -239,7 +240,8 @@ queue_middle_tasks(const General_options& options,
input_objects,
symtab,
layout),
- blocker));
+ blocker,
+ "Task_function Layout_task_runner"));
}
// Queue up the final set of tasks. This is called at the end of
@@ -312,7 +314,8 @@ queue_final_tasks(const General_options& options,
// Queue a task to close the output file. This will be blocked by
// FINAL_BLOCKER.
workqueue->queue(new Task_function(new Close_task_runner(of),
- final_blocker));
+ final_blocker,
+ "Task_function Close_task_runner"));
}
} // End namespace gold.
diff --git a/gold/layout.h b/gold/layout.h
index 5b9f28defe6..9e0bcf72543 100644
--- a/gold/layout.h
+++ b/gold/layout.h
@@ -463,6 +463,10 @@ class Write_sections_task : public Task
void
run(Workqueue*);
+ std::string
+ get_name() const
+ { return "Write_sections_task"; }
+
private:
class Write_sections_locker;
@@ -494,6 +498,10 @@ class Write_data_task : public Task
void
run(Workqueue*);
+ std::string
+ get_name() const
+ { return "Write_data_task"; }
+
private:
const Layout* layout_;
const Symbol_table* symtab_;
@@ -525,6 +533,10 @@ class Write_symbols_task : public Task
void
run(Workqueue*);
+ std::string
+ get_name() const
+ { return "Write_symbols_task"; }
+
private:
const Symbol_table* symtab_;
const Input_objects* input_objects_;
@@ -561,6 +573,10 @@ class Write_after_input_sections_task : public Task
void
run(Workqueue*);
+ std::string
+ get_name() const
+ { return "Write_after_input_sections_task"; }
+
private:
class Write_sections_locker;
diff --git a/gold/options.cc b/gold/options.cc
index c962188f1c2..fb7990182c9 100644
--- a/gold/options.cc
+++ b/gold/options.cc
@@ -28,6 +28,7 @@
#include "filenames.h"
#include "libiberty.h"
+#include "debug.h"
#include "options.h"
namespace gold
@@ -106,6 +107,17 @@ struct options::One_z_option
void (General_options::*set)();
};
+// We have a separate table for --debug options.
+
+struct options::One_debug_option
+{
+ // The name of the option.
+ const char* name;
+
+ // The flags to turn on.
+ unsigned int debug_flags;
+};
+
class options::Command_line_options
{
public:
@@ -113,6 +125,8 @@ class options::Command_line_options
static const int options_size;
static const One_z_option z_options[];
static const int z_options_size;
+ static const One_debug_option debug_options[];
+ static const int debug_options_size;
};
} // End namespace gold.
@@ -247,7 +261,7 @@ help(int, char**, char*, bool, gold::Command_line*)
std::puts(options[i].doc);
}
- ::exit(0);
+ ::exit(EXIT_SUCCESS);
return 0;
}
@@ -258,7 +272,7 @@ int
version(int, char**, char* opt, bool, gold::Command_line*)
{
gold::print_version(opt[0] == 'v' && opt[1] == '\0');
- ::exit(0);
+ ::exit(EXIT_SUCCESS);
return 0;
}
@@ -466,7 +480,10 @@ options::Command_line_options::options[] =
SPECIAL('\0', "help", N_("Report usage information"), NULL,
TWO_DASHES, &help),
SPECIAL('v', "version", N_("Report version information"), NULL,
- TWO_DASHES, &version)
+ TWO_DASHES, &version),
+ GENERAL_ARG('\0', "debug", N_("Turn on debugging (all,task)"),
+ N_("--debug=TYPE"), TWO_DASHES,
+ &General_options::handle_debug_option)
};
const int options::Command_line_options::options_size =
@@ -484,6 +501,18 @@ options::Command_line_options::z_options[] =
const int options::Command_line_options::z_options_size =
sizeof(z_options) / sizeof(z_options[0]);
+// The --debug options.
+
+const options::One_debug_option
+options::Command_line_options::debug_options[] =
+{
+ { "all", DEBUG_ALL },
+ { "task", DEBUG_TASK },
+};
+
+const int options::Command_line_options::debug_options_size =
+ sizeof(debug_options) / sizeof(debug_options[0]);
+
// The default values for the general options.
General_options::General_options()
@@ -509,7 +538,8 @@ General_options::General_options()
thread_count_initial_(0),
thread_count_middle_(0),
thread_count_final_(0),
- execstack_(EXECSTACK_FROM_INPUT)
+ execstack_(EXECSTACK_FROM_INPUT),
+ debug_(0)
{
// We initialize demangle_ based on the environment variable
// COLLECT_NO_DEMANGLE. The gcc collect2 program will demangle the
@@ -547,7 +577,30 @@ General_options::handle_z_option(const char* arg)
fprintf(stderr, _("%s: unrecognized -z subcommand: %s\n"),
program_name, arg);
- ::exit(1);
+ ::exit(EXIT_FAILURE);
+}
+
+// Handle the --debug option.
+
+void
+General_options::handle_debug_option(const char* arg)
+{
+ const int debug_options_size =
+ options::Command_line_options::debug_options_size;
+ const gold::options::One_debug_option* debug_options =
+ options::Command_line_options::debug_options;
+ for (int i = 0; i < debug_options_size; ++i)
+ {
+ if (strcmp(arg, debug_options[i].name) == 0)
+ {
+ this->set_debug(debug_options[i].debug_flags);
+ return;
+ }
+ }
+
+ fprintf(stderr, _("%s: unrecognized --debug subcommand: %s\n"),
+ program_name, arg);
+ ::exit(EXIT_FAILURE);
}
// Add the sysroot, if any, to the search paths.
@@ -961,7 +1014,7 @@ Command_line::usage()
fprintf(stderr,
_("%s: use the --help option for usage information\n"),
program_name);
- ::exit(1);
+ ::exit(EXIT_FAILURE);
}
void
diff --git a/gold/options.h b/gold/options.h
index 48047c2aa3b..c7b08e8633f 100644
--- a/gold/options.h
+++ b/gold/options.h
@@ -46,11 +46,13 @@ class Command_line;
class Input_file_group;
class Position_dependent_options;
-namespace options {
+namespace options
+{
class Command_line_options;
struct One_option;
struct One_z_option;
+struct One_debug_option;
} // End namespace gold::options.
@@ -249,6 +251,11 @@ class General_options
is_stack_executable() const
{ return this->execstack_ == EXECSTACK_YES; }
+ // --debug
+ unsigned int
+ debug() const
+ { return this->debug_; }
+
private:
// Don't copy this structure.
General_options(const General_options&);
@@ -444,10 +451,18 @@ class General_options
set_noexecstack()
{ this->execstack_ = EXECSTACK_NO; }
+ void
+ set_debug(unsigned int flags)
+ { this->debug_ = flags; }
+
// Handle the -z option.
void
handle_z_option(const char*);
+ // Handle the --debug option.
+ void
+ handle_debug_option(const char*);
+
// Apply any sysroot to the directory lists.
void
add_sysroot();
@@ -476,6 +491,7 @@ class General_options
int thread_count_middle_;
int thread_count_final_;
Execstack execstack_;
+ unsigned int debug_;
};
// The current state of the position dependent options.
diff --git a/gold/parameters.cc b/gold/parameters.cc
index 7fbbf836778..cd05ffee8dd 100644
--- a/gold/parameters.cc
+++ b/gold/parameters.cc
@@ -31,11 +31,11 @@ namespace gold
// Initialize the parameters from the options.
Parameters::Parameters(Errors* errors)
- : errors_(errors), output_file_name_(NULL),
+ : errors_(errors), threads_(false), output_file_name_(NULL),
output_file_type_(OUTPUT_INVALID), sysroot_(),
strip_(STRIP_INVALID), allow_shlib_undefined_(false),
symbolic_(false), demangle_(false), detect_odr_violations_(false),
- optimization_level_(0), export_dynamic_(false),
+ optimization_level_(0), export_dynamic_(false), debug_(0),
is_doing_static_link_valid_(false), doing_static_link_(false),
is_size_and_endian_valid_(false), size_(0), is_big_endian_(false)
{
@@ -46,6 +46,7 @@ Parameters::Parameters(Errors* errors)
void
Parameters::set_from_options(const General_options* options)
{
+ this->threads_ = options->threads();
this->output_file_name_ = options->output_file_name();
this->sysroot_ = options->sysroot();
this->allow_shlib_undefined_ = options->allow_shlib_undefined();
@@ -54,6 +55,7 @@ Parameters::set_from_options(const General_options* options)
this->detect_odr_violations_ = options->detect_odr_violations();
this->optimization_level_ = options->optimization_level();
this->export_dynamic_ = options->export_dynamic();
+ this->debug_ = options->debug();
if (options->is_shared())
this->output_file_type_ = OUTPUT_SHARED;
diff --git a/gold/parameters.h b/gold/parameters.h
index 3186b6d454d..4e135c61e27 100644
--- a/gold/parameters.h
+++ b/gold/parameters.h
@@ -47,6 +47,14 @@ class Parameters
errors() const
{ return this->errors_; }
+ // Whether to use threads.
+ bool
+ threads() const
+ {
+ gold_assert(this->options_valid_);
+ return this->threads_;
+ }
+
// Return the output file name.
const char*
output_file_name() const
@@ -166,6 +174,15 @@ class Parameters
return this->export_dynamic_;
}
+ // Return the debug flags. These are the flags for which we should
+ // report internal debugging information.
+ unsigned int
+ debug() const
+ {
+ gold_assert(this->options_valid_);
+ return this->debug_;
+ }
+
// Whether we are doing a static link--a link in which none of the
// input files are shared libraries. This is only known after we
// have seen all the input files.
@@ -239,6 +256,8 @@ class Parameters
// Whether the fields set from the options are valid.
bool options_valid_;
+ // Whether to use threads.
+ bool threads_;
// The output file name.
const char* output_file_name_;
// The type of the output file.
@@ -259,6 +278,8 @@ class Parameters
int optimization_level_;
// Whether the -E/--export-dynamic flag is set.
bool export_dynamic_;
+ // The debug flags.
+ unsigned int debug_;
// Whether the doing_static_link_ field is valid.
bool is_doing_static_link_valid_;
@@ -287,6 +308,13 @@ extern void set_parameters_size_and_endianness(int size, bool is_big_endian);
// Set whether we are doing a static link.
extern void set_parameters_doing_static_link(bool doing_static_link);
+// Return whether we are doing a particular debugging type. The
+// argument is one of the flags from debug.h.
+
+inline bool
+is_debugging_enabled(unsigned int type)
+{ return (parameters->debug() & type) != 0; }
+
} // End namespace gold.
#endif // !defined(GOLD_PARAMETERS_H)
diff --git a/gold/po/POTFILES.in b/gold/po/POTFILES.in
index a78d65d0650..3797b1a3b22 100644
--- a/gold/po/POTFILES.in
+++ b/gold/po/POTFILES.in
@@ -54,4 +54,6 @@ tls.h
version.cc
workqueue.cc
workqueue.h
+workqueue-internal.h
+workqueue-threads.cc
x86_64.cc
diff --git a/gold/po/gold.pot b/gold/po/gold.pot
index c9e1e0d10de..73f1f015310 100644
--- a/gold/po/gold.pot
+++ b/gold/po/gold.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-11-09 15:55-0800\n"
+"POT-Creation-Date: 2007-11-20 16:37-0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,47 +16,47 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
-#: archive.cc:88
+#: archive.cc:95
#, c-format
msgid "%s: no archive symbol table (run ranlib)"
msgstr ""
-#: archive.cc:138
+#: archive.cc:145
#, c-format
msgid "%s: bad archive symbol table names"
msgstr ""
-#: archive.cc:168
+#: archive.cc:175
#, c-format
msgid "%s: malformed archive header at %zu"
msgstr ""
-#: archive.cc:188
+#: archive.cc:195
#, c-format
msgid "%s: malformed archive header size at %zu"
msgstr ""
-#: archive.cc:199
+#: archive.cc:206
#, c-format
msgid "%s: malformed archive header name at %zu"
msgstr ""
-#: archive.cc:224
+#: archive.cc:231
#, c-format
msgid "%s: bad extended name index at %zu"
msgstr ""
-#: archive.cc:234
+#: archive.cc:241
#, c-format
msgid "%s: bad extended name entry at header %zu"
msgstr ""
-#: archive.cc:327
+#: archive.cc:334
#, c-format
msgid "%s: short archive header at %zu"
msgstr ""
-#: archive.cc:378 archive.cc:392
+#: archive.cc:385 archive.cc:399
#, c-format
msgid "%s: member at %zu is not an ELF object"
msgstr ""
@@ -66,110 +66,115 @@ msgstr ""
msgid "%s: can not read directory: %s"
msgstr ""
-#: dynobj.cc:151
+#: dynobj.cc:145
#, c-format
msgid "unexpected duplicate type %u section: %u, %u"
msgstr ""
-#: dynobj.cc:187
+#: dynobj.cc:181
#, c-format
msgid "unexpected link in section %u header: %u != %u"
msgstr ""
-#: dynobj.cc:222
+#: dynobj.cc:217
#, c-format
msgid "DYNAMIC section %u link out of range: %u"
msgstr ""
-#: dynobj.cc:230
+#: dynobj.cc:225
#, c-format
msgid "DYNAMIC section %u link %u is not a strtab"
msgstr ""
-#: dynobj.cc:250
+#: dynobj.cc:253
#, c-format
msgid "DT_SONAME value out of range: %lld >= %lld"
msgstr ""
#: dynobj.cc:265
+#, c-format
+msgid "DT_NEEDED value out of range: %lld >= %lld"
+msgstr ""
+
+#: dynobj.cc:278
msgid "missing DT_NULL in dynamic segment"
msgstr ""
-#: dynobj.cc:309
+#: dynobj.cc:322
#, c-format
msgid "invalid dynamic symbol table name index: %u"
msgstr ""
-#: dynobj.cc:316
+#: dynobj.cc:329
#, c-format
msgid "dynamic symbol table name section has wrong type: %u"
msgstr ""
-#: dynobj.cc:389 object.cc:234 object.cc:569
+#: dynobj.cc:402 object.cc:236 object.cc:571
#, c-format
msgid "bad section name offset for section %u: %lu"
msgstr ""
-#: dynobj.cc:418
+#: dynobj.cc:431
#, c-format
msgid "duplicate definition for version %u"
msgstr ""
-#: dynobj.cc:447
+#: dynobj.cc:460
#, c-format
msgid "unexpected verdef version %u"
msgstr ""
-#: dynobj.cc:463
+#: dynobj.cc:476
#, c-format
msgid "verdef vd_cnt field too small: %u"
msgstr ""
-#: dynobj.cc:470
+#: dynobj.cc:483
#, c-format
msgid "verdef vd_aux field out of range: %u"
msgstr ""
-#: dynobj.cc:480
+#: dynobj.cc:493
#, c-format
msgid "verdaux vda_name field out of range: %u"
msgstr ""
-#: dynobj.cc:489
+#: dynobj.cc:502
#, c-format
msgid "verdef vd_next field out of range: %u"
msgstr ""
-#: dynobj.cc:522
+#: dynobj.cc:535
#, c-format
msgid "unexpected verneed version %u"
msgstr ""
-#: dynobj.cc:531
+#: dynobj.cc:544
#, c-format
msgid "verneed vn_aux field out of range: %u"
msgstr ""
-#: dynobj.cc:544
+#: dynobj.cc:557
#, c-format
msgid "vernaux vna_name field out of range: %u"
msgstr ""
-#: dynobj.cc:555
+#: dynobj.cc:568
#, c-format
msgid "verneed vna_next field out of range: %u"
msgstr ""
-#: dynobj.cc:566
+#: dynobj.cc:579
#, c-format
msgid "verneed vn_next field out of range: %u"
msgstr ""
-#: dynobj.cc:613
+#: dynobj.cc:626
msgid "size of dynamic symbols is not multiple of symbol size"
msgstr ""
-#: dynobj.cc:1290
+#: dynobj.cc:1303
#, c-format
msgid "symbol %s has undefined version %s"
msgstr ""
@@ -189,6 +194,11 @@ msgstr ""
msgid "%s: %s: undefined reference to '%s'\n"
msgstr ""
+#: errors.cc:146
+#, c-format
+msgid "%s: "
+msgstr ""
+
#: fileread.cc:49
#, c-format
msgid "munmap failed: %s"
@@ -251,12 +261,12 @@ msgstr ""
#. We had some input files, but we weren't able to open any of
#. them.
-#: gold.cc:118 gold.cc:164
+#: gold.cc:118 gold.cc:165
msgid "no input files"
msgstr ""
#. We print out just the first .so we see; there may be others.
-#: gold.cc:179
+#: gold.cc:180
#, c-format
msgid "cannot mix -static with dynamic object %s"
msgstr ""
@@ -266,97 +276,98 @@ msgstr ""
msgid "pthead_mutextattr_init failed: %s"
msgstr ""
-#: gold-threads.cc:72
+#: gold-threads.cc:73
#, c-format
msgid "pthread_mutextattr_settype failed: %s"
msgstr ""
-#: gold-threads.cc:76
+#: gold-threads.cc:78
#, c-format
msgid "pthread_mutex_init failed: %s"
msgstr ""
-#: gold-threads.cc:79
+#: gold-threads.cc:82
#, c-format
msgid "pthread_mutexattr_destroy failed: %s"
msgstr ""
-#: gold-threads.cc:85
+#: gold-threads.cc:89
#, c-format
msgid "pthread_mutex_destroy failed: %s"
msgstr ""
-#: gold-threads.cc:92
+#: gold-threads.cc:97
#, c-format
msgid "pthread_mutex_lock failed: %s"
msgstr ""
-#: gold-threads.cc:99
+#: gold-threads.cc:105
#, c-format
msgid "pthread_mutex_unlock failed: %s"
msgstr ""
-#: gold-threads.cc:180
+#: gold-threads.cc:193
#, c-format
msgid "pthread_cond_init failed: %s"
msgstr ""
-#: gold-threads.cc:186
+#: gold-threads.cc:200
#, c-format
msgid "pthread_cond_destroy failed: %s"
msgstr ""
-#: gold-threads.cc:193
+#: gold-threads.cc:208
#, c-format
msgid "pthread_cond_wait failed: %s"
msgstr ""
-#: gold-threads.cc:200
+#: gold-threads.cc:216
#, c-format
msgid "pthread_cond_signal failed: %s"
msgstr ""
+#: gold-threads.cc:224
+#, c-format
+msgid "pthread_cond_broadcast failed: %s"
+msgstr ""
+
#. FIXME: This needs to specify the location somehow.
-#: i386.cc:153 i386.cc:1310 x86_64.cc:165 x86_64.cc:1254
+#: i386.cc:160 i386.cc:1439 x86_64.cc:172 x86_64.cc:1269
msgid "missing expected TLS relocation"
msgstr ""
-#: i386.cc:761 x86_64.cc:723 x86_64.cc:898
+#: i386.cc:779 x86_64.cc:732 x86_64.cc:910
#, c-format
msgid "%s: unsupported reloc %u against local symbol"
msgstr ""
-#: i386.cc:862 i386.cc:1096 x86_64.cc:839 x86_64.cc:1079
+#: i386.cc:882 i386.cc:1169 x86_64.cc:851 x86_64.cc:1093
#, c-format
msgid "%s: unexpected reloc %u in object file"
msgstr ""
-#: i386.cc:948 x86_64.cc:912 x86_64.cc:1138
+#: i386.cc:1018 x86_64.cc:924 x86_64.cc:1152
#, c-format
msgid "%s: unsupported reloc %u against global symbol %s"
msgstr ""
-#: i386.cc:1193
+#: i386.cc:1322
#, c-format
msgid "%s: unsupported RELA reloc section"
msgstr ""
-#: i386.cc:1449 x86_64.cc:1452
+#: i386.cc:1579 x86_64.cc:1467
#, c-format
msgid "unexpected reloc %u in object file"
msgstr ""
-#: i386.cc:1481 i386.cc:1528 i386.cc:1535 i386.cc:1555 i386.cc:1584
-#: x86_64.cc:1473 x86_64.cc:1522 x86_64.cc:1533
+#: i386.cc:1611 i386.cc:1687 i386.cc:1694 i386.cc:1735 i386.cc:1791
+#: x86_64.cc:1488 x86_64.cc:1537 x86_64.cc:1548
#, c-format
msgid "unsupported reloc %u"
msgstr ""
-#: i386.cc:1506 x86_64.cc:1498
-msgid "TLS reloc but no TLS segment"
-msgstr ""
-
-#: i386.cc:1543
+#: i386.cc:1702
msgid "both SUN and GNU model TLS relocations"
msgstr ""
@@ -368,550 +379,602 @@ msgstr ""
msgid "entry in mergeable string section not null terminated"
msgstr ""
-#: object.cc:51
+#: object.cc:53
#, c-format
msgid "%s: unsupported ELF machine number %d"
msgstr ""
-#: object.cc:69 script.cc:1222
+#: object.cc:71 script.cc:1226
#, c-format
msgid "%s: %s"
msgstr ""
-#: object.cc:104
+#: object.cc:106
#, c-format
msgid "section name section has wrong type: %u"
msgstr ""
-#: object.cc:306
+#: object.cc:308
#, c-format
msgid "invalid symbol table name index: %u"
msgstr ""
-#: object.cc:312
+#: object.cc:314
#, c-format
msgid "symbol table name section has wrong type: %u"
msgstr ""
-#: object.cc:392
+#: object.cc:394
#, c-format
msgid "section group %u info %u out of range"
msgstr ""
-#: object.cc:410
+#: object.cc:412
#, c-format
msgid "symbol %u name offset %u out of range"
msgstr ""
-#: object.cc:442
+#: object.cc:444
#, c-format
msgid "section %u in section group %u out of range"
msgstr ""
-#: object.cc:532 reloc.cc:202 reloc.cc:469
+#: object.cc:534 reloc.cc:226 reloc.cc:493
#, c-format
msgid "relocation section %u has bad info %u"
msgstr ""
-#: object.cc:704
+#: object.cc:706
msgid "size of symbols is not multiple of symbol size"
msgstr ""
#. FIXME: Handle SHN_XINDEX.
-#: object.cc:796
+#: object.cc:798
#, c-format
msgid "unknown section index %u for local symbol %u"
msgstr ""
-#: object.cc:805
+#: object.cc:807
#, c-format
msgid "local symbol %u section index %u out of range"
msgstr ""
-#: object.cc:837
+#: object.cc:839
#, c-format
msgid "local symbol %u section name out of range: %u >= %u"
msgstr ""
-#: object.cc:1055
+#: object.cc:1070
#, c-format
msgid "%s: incompatible target"
msgstr ""
-#: object.cc:1213
+#: object.cc:1226
#, c-format
msgid "%s: unsupported ELF file type %d"
msgstr ""
-#: object.cc:1232 object.cc:1278 object.cc:1312
+#: object.cc:1245 object.cc:1291 object.cc:1325
#, c-format
msgid "%s: ELF file too short"
msgstr ""
-#: object.cc:1240
+#: object.cc:1253
#, c-format
msgid "%s: invalid ELF version 0"
msgstr ""
-#: object.cc:1242
+#: object.cc:1255
#, c-format
msgid "%s: unsupported ELF version %d"
msgstr ""
-#: object.cc:1249
+#: object.cc:1262
#, c-format
msgid "%s: invalid ELF class 0"
msgstr ""
-#: object.cc:1255
+#: object.cc:1268
#, c-format
msgid "%s: unsupported ELF class %d"
msgstr ""
-#: object.cc:1262
+#: object.cc:1275
#, c-format
msgid "%s: invalid ELF data encoding"
msgstr ""
-#: object.cc:1268
+#: object.cc:1281
#, c-format
msgid "%s: unsupported ELF data encoding %d"
msgstr ""
-#: object.cc:1288
+#: object.cc:1301
#, c-format
msgid "%s: not configured to support 32-bit big-endian object"
msgstr ""
-#: object.cc:1301
+#: object.cc:1314
#, c-format
msgid "%s: not configured to support 32-bit little-endian object"
msgstr ""
-#: object.cc:1322
+#: object.cc:1335
#, c-format
msgid "%s: not configured to support 64-bit big-endian object"
msgstr ""
-#: object.cc:1335
+#: object.cc:1348
#, c-format
msgid "%s: not configured to support 64-bit little-endian object"
msgstr ""
-#: options.cc:142
+#: options.cc:157
#, c-format
msgid "%s: unable to parse script file %s\n"
msgstr ""
-#: options.cc:170
+#: options.cc:185
#, c-format
msgid ""
"Usage: %s [options] file...\n"
"Options:\n"
msgstr ""
-#: options.cc:341
+#: options.cc:356
+msgid "Allow unresolved references in shared libraries"
+msgstr ""
+
+#: options.cc:360
+msgid "Do not allow unresolved references in shared libraries"
+msgstr ""
+
+#: options.cc:364
msgid "Only set DT_NEEDED for dynamic libs if used"
msgstr ""
-#: options.cc:344
+#: options.cc:367
msgid "Always DT_NEEDED for dynamic libs (default)"
msgstr ""
-#: options.cc:347
+#: options.cc:370
msgid "-l searches for shared libraries"
msgstr ""
-#: options.cc:351
+#: options.cc:374
msgid "-l does not search for shared libraries"
msgstr ""
-#: options.cc:354
+#: options.cc:377
msgid "Bind defined symbols locally"
msgstr ""
-#: options.cc:356
+#: options.cc:379
+msgid "Demangle C++ symbols in log messages"
+msgstr ""
+
+#: options.cc:382
+msgid "Do not demangle C++ symbols in log messages"
+msgstr ""
+
+#: options.cc:385
+msgid "Try to detect violations of the One Definition Rule"
+msgstr ""
+
+#: options.cc:387
msgid "Export all dynamic symbols"
msgstr ""
-#: options.cc:358
+#: options.cc:389
msgid "Create exception frame header"
msgstr ""
-#: options.cc:360
+#: options.cc:391
msgid "Set dynamic linker path"
msgstr ""
-#: options.cc:361
+#: options.cc:392
msgid "-I PROGRAM, --dynamic-linker PROGRAM"
msgstr ""
-#: options.cc:363
+#: options.cc:394
msgid "Search for library LIBNAME"
msgstr ""
-#: options.cc:364
+#: options.cc:395
msgid "-lLIBNAME, --library LIBNAME"
msgstr ""
-#: options.cc:366
+#: options.cc:397
msgid "Add directory to search path"
msgstr ""
-#: options.cc:367
+#: options.cc:398
msgid "-L DIR, --library-path DIR"
msgstr ""
-#: options.cc:369
+#: options.cc:400
msgid "Ignored for compatibility"
msgstr ""
-#: options.cc:371
+#: options.cc:402
msgid "Set output file name"
msgstr ""
-#: options.cc:372
+#: options.cc:403
msgid "-o FILE, --output FILE"
msgstr ""
-#: options.cc:374
+#: options.cc:405
msgid "Optimize output file size"
msgstr ""
-#: options.cc:375
+#: options.cc:406
msgid "-O level"
msgstr ""
-#: options.cc:377
+#: options.cc:408
msgid "Generate relocatable output"
msgstr ""
-#: options.cc:379
+#: options.cc:410
msgid "Add DIR to runtime search path"
msgstr ""
-#: options.cc:380
+#: options.cc:411
msgid "-R DIR, -rpath DIR"
msgstr ""
-#: options.cc:383
+#: options.cc:414
msgid "Add DIR to link time shared library search path"
msgstr ""
-#: options.cc:384
+#: options.cc:415
msgid "--rpath-link DIR"
msgstr ""
-#: options.cc:386
+#: options.cc:417
msgid "Strip all symbols"
msgstr ""
-#: options.cc:388
+#: options.cc:420
+msgid "Strip debug symbols that are unused by gdb (at least versions <= 6.7)"
+msgstr ""
+
+#. This must come after -Sdebug since it's a prefix of it.
+#: options.cc:424
msgid "Strip debugging information"
msgstr ""
-#: options.cc:390
+#: options.cc:426
msgid "Generate shared library"
msgstr ""
-#: options.cc:392
+#: options.cc:428
msgid "Do not link against shared libraries"
msgstr ""
-#: options.cc:394
+#: options.cc:430
msgid "Print resource usage statistics"
msgstr ""
-#: options.cc:396
+#: options.cc:432
msgid "Set target system root directory"
msgstr ""
-#: options.cc:397
+#: options.cc:433
msgid "--sysroot DIR"
msgstr ""
-#: options.cc:398
+#: options.cc:434
msgid "Set the address of the .text section"
msgstr ""
-#: options.cc:399
+#: options.cc:435
msgid "-Ttext ADDRESS"
msgstr ""
#. This must come after -Ttext since it's a prefix of it.
-#: options.cc:402
+#: options.cc:438
msgid "Read linker script"
msgstr ""
-#: options.cc:403
+#: options.cc:439
msgid "-T FILE, --script FILE"
msgstr ""
-#: options.cc:405
+#: options.cc:441
msgid "Run the linker multi-threaded"
msgstr ""
-#: options.cc:407
+#: options.cc:443
msgid "Do not run the linker multi-threaded"
msgstr ""
-#: options.cc:409
+#: options.cc:445
msgid "Number of threads to use"
msgstr ""
-#: options.cc:410
+#: options.cc:446
msgid "--thread-count COUNT"
msgstr ""
-#: options.cc:413
+#: options.cc:449
msgid "Number of threads to use in initial pass"
msgstr ""
-#: options.cc:414
+#: options.cc:450
msgid "--thread-count-initial COUNT"
msgstr ""
-#: options.cc:417
+#: options.cc:453
msgid "Number of threads to use in middle pass"
msgstr ""
-#: options.cc:418
+#: options.cc:454
msgid "--thread-count-middle COUNT"
msgstr ""
-#: options.cc:421
+#: options.cc:457
msgid "Number of threads to use in final pass"
msgstr ""
-#: options.cc:422
+#: options.cc:458
msgid "--thread-count-final COUNT"
msgstr ""
-#: options.cc:425
+#: options.cc:461
msgid "Include all archive contents"
msgstr ""
-#: options.cc:429
+#: options.cc:465
msgid "Include only needed archive contents"
msgstr ""
-#: options.cc:434
+#: options.cc:470
msgid ""
"Subcommands as follows:\n"
" -z execstack Mark output as requiring executable stack\n"
" -z noexecstack Mark output as not requiring executable stack"
msgstr ""
-#: options.cc:437
+#: options.cc:473
msgid "-z SUBCOMMAND"
msgstr ""
-#: options.cc:440
+#: options.cc:476
msgid "Start a library search group"
msgstr ""
-#: options.cc:442
+#: options.cc:478
msgid "End a library search group"
msgstr ""
-#: options.cc:444
+#: options.cc:480
msgid "Report usage information"
msgstr ""
-#: options.cc:446
+#: options.cc:482
msgid "Report version information"
msgstr ""
-#: options.cc:518
+#: options.cc:484
+msgid "Turn on debugging (all,task)"
+msgstr ""
+
+#: options.cc:485
+msgid "--debug=TYPE"
+msgstr ""
+
+#: options.cc:578
#, c-format
msgid "%s: unrecognized -z subcommand: %s\n"
msgstr ""
-#: options.cc:698
+#: options.cc:601
+#, c-format
+msgid "%s: unrecognized --debug subcommand: %s\n"
+msgstr ""
+
+#: options.cc:781
msgid "unexpected argument"
msgstr ""
-#: options.cc:705 options.cc:757 options.cc:838
+#: options.cc:788 options.cc:840 options.cc:921
msgid "missing argument"
msgstr ""
-#: options.cc:718 options.cc:766
+#: options.cc:801 options.cc:849
msgid "unknown option"
msgstr ""
-#: options.cc:784
+#: options.cc:867
#, c-format
msgid "%s: missing group end\n"
msgstr ""
-#: options.cc:912
+#: options.cc:995
msgid "may not nest groups"
msgstr ""
-#: options.cc:922
+#: options.cc:1005
msgid "group end without group start"
msgstr ""
-#: options.cc:932
+#: options.cc:1015
#, c-format
msgid "%s: use the --help option for usage information\n"
msgstr ""
-#: options.cc:941
+#: options.cc:1024
#, c-format
msgid "%s: %s: %s\n"
msgstr ""
-#: options.cc:950
+#: options.cc:1033
#, c-format
msgid "%s: -%c: %s\n"
msgstr ""
-#: options.h:338
+#: options.h:393
#, c-format
msgid "%s: invalid argument to -Ttext: %s\n"
msgstr ""
-#: options.h:351
+#: options.h:406
#, c-format
msgid "%s: invalid thread count: %s\n"
msgstr ""
-#: output.cc:1061
+#: output.cc:1108
#, c-format
msgid "invalid alignment %lu for section \"%s\""
msgstr ""
-#: output.cc:1787
+#: output.cc:1870
#, c-format
msgid "%s: open: %s"
msgstr ""
-#: output.cc:1792
+#: output.cc:1875
#, c-format
msgid "%s: lseek: %s"
msgstr ""
-#: output.cc:1795
+#: output.cc:1878
#, c-format
msgid "%s: write: %s"
msgstr ""
-#: output.cc:1801
+#: output.cc:1884
#, c-format
msgid "%s: mmap: %s"
msgstr ""
-#: output.cc:1811
+#: output.cc:1894
#, c-format
msgid "%s: munmap: %s"
msgstr ""
-#: output.cc:1815
+#: output.cc:1898
#, c-format
msgid "%s: close: %s"
msgstr ""
-#: readsyms.cc:147
+#: readsyms.cc:151
#, c-format
msgid "%s: file is empty"
msgstr ""
-#: readsyms.cc:182
+#: readsyms.cc:186
#, c-format
msgid "%s: ordinary object found in input group"
msgstr ""
#. Here we have to handle any other input file types we need.
-#: readsyms.cc:230
+#: readsyms.cc:234
#, c-format
msgid "%s: not an object or archive"
msgstr ""
-#: reloc.cc:221 reloc.cc:487
+#: reloc.cc:245 reloc.cc:511
#, c-format
msgid "relocation section %u uses unexpected symbol table %u"
msgstr ""
-#: reloc.cc:236 reloc.cc:505
+#: reloc.cc:260 reloc.cc:529
#, c-format
msgid "unexpected entsize for reloc section %u: %lu != %u"
msgstr ""
-#: reloc.cc:245 reloc.cc:514
+#: reloc.cc:269 reloc.cc:538
#, c-format
msgid "reloc section %u size %lu uneven"
msgstr ""
-#: reloc.cc:702
+#: reloc.cc:729
#, c-format
msgid "reloc section size %zu is not a multiple of reloc size %d\n"
msgstr ""
-#: resolve.cc:165
-#, c-format
-msgid "%s: invalid STB_LOCAL symbol %s in external symbols"
+#. We should only see externally visible symbols in the symbol
+#. table.
+#: resolve.cc:144
+msgid "invalid STB_LOCAL symbol in external symbols"
msgstr ""
-#: resolve.cc:171
-#, c-format
-msgid "%s: unsupported symbol binding %d for symbol %s"
+#. Any target which wants to handle STB_LOOS, etc., needs to
+#. define a resolve method.
+#: resolve.cc:150
+msgid "unsupported symbol binding"
msgstr ""
#. Two definitions of the same symbol.
#. FIXME: Do a better job of reporting locations.
-#: resolve.cc:310
+#: resolve.cc:318
#, c-format
msgid "%s: multiple definition of %s"
msgstr ""
-#: resolve.cc:311 resolve.cc:316
+#: resolve.cc:319 resolve.cc:324
msgid "command line"
msgstr ""
-#: resolve.cc:313
+#: resolve.cc:321
#, c-format
msgid "%s: previous definition here"
msgstr ""
#. There are some options that we could handle here--e.g.,
#. -lLIBRARY. Should we bother?
-#: script.cc:1326
+#: script.cc:1330
#, c-format
msgid ""
"%s: Ignoring command OPTION; OPTION is only valid for scripts specified via -"
"T"
msgstr ""
-#: symtab.cc:541
+#: symtab.cc:576
#, c-format
msgid "bad global symbol name offset %u at %zu"
msgstr ""
-#: symtab.cc:619
+#: symtab.cc:654
msgid "too few symbol versions"
msgstr ""
-#: symtab.cc:648
+#: symtab.cc:683
#, c-format
msgid "bad symbol name offset %u at %zu"
msgstr ""
-#: symtab.cc:702
+#: symtab.cc:737
#, c-format
msgid "versym for symbol %zu out of range: %u"
msgstr ""
-#: symtab.cc:710
+#: symtab.cc:745
#, c-format
msgid "versym for symbol %zu has no name: %u"
msgstr ""
-#: symtab.cc:1428 symtab.cc:1631
+#: symtab.cc:1463 symtab.cc:1676
#, c-format
msgid "%s: unsupported symbol section 0x%x"
msgstr ""
+#: symtab.cc:1800
+#, c-format
+msgid "%s: undefined reference to '%s'"
+msgstr ""
+
+#: symtab.cc:1941
+#, c-format
+msgid ""
+"while linking %s: symbol '%s' defined in multiple places (possible ODR "
+"violation):"
+msgstr ""
+
#: target-reloc.h:211
#, c-format
msgid "reloc has bad offset %zu"
@@ -940,12 +1003,21 @@ msgid ""
"This program has absolutely no warranty.\n"
msgstr ""
-#: x86_64.cc:1162
+#: workqueue-threads.cc:106
+#, c-format
+msgid "%s failed: %s"
+msgstr ""
+
+#: x86_64.cc:1177
#, c-format
msgid "%s: unsupported REL reloc section"
msgstr ""
-#: x86_64.cc:1561
+#: x86_64.cc:1513
+msgid "TLS reloc but no TLS segment"
+msgstr ""
+
+#: x86_64.cc:1576
#, c-format
msgid "unsupported reloc type %u"
msgstr ""
diff --git a/gold/readsyms.cc b/gold/readsyms.cc
index 39d33767033..5625f59f0e6 100644
--- a/gold/readsyms.cc
+++ b/gold/readsyms.cc
@@ -72,6 +72,10 @@ class Unblock_token : public Task
run(Workqueue*)
{ }
+ std::string
+ get_name() const
+ { return "Unblock_token"; }
+
private:
Task_token* this_blocker_;
Task_token* next_blocker_;
@@ -273,6 +277,35 @@ Read_symbols::do_group(Workqueue* workqueue)
this->next_blocker_));
}
+// Return a debugging name for a Read_symbols task.
+
+std::string
+Read_symbols::get_name() const
+{
+ if (!this->input_argument_->is_group())
+ {
+ std::string ret("Read_symbols ");
+ if (this->input_argument_->file().is_lib())
+ ret += "-l";
+ ret += this->input_argument_->file().name();
+ return ret;
+ }
+
+ std::string ret("Read_symbols group (");
+ bool add_space = false;
+ const Input_file_group* group = this->input_argument_->group();
+ for (Input_file_group::const_iterator p = group->begin();
+ p != group->end();
+ ++p)
+ {
+ if (add_space)
+ ret += ' ';
+ ret += p->file().name();
+ add_space = true;
+ }
+ return ret + ')';
+}
+
// Class Add_symbols.
Add_symbols::~Add_symbols()
diff --git a/gold/readsyms.h b/gold/readsyms.h
index d88446309dc..c02a0ee4672 100644
--- a/gold/readsyms.h
+++ b/gold/readsyms.h
@@ -77,6 +77,9 @@ class Read_symbols : public Task
void
run(Workqueue*);
+ std::string
+ get_name() const;
+
private:
// Handle an archive group.
void
@@ -129,6 +132,10 @@ class Add_symbols : public Task
void
run(Workqueue*);
+ std::string
+ get_name() const
+ { return "Add_symbols " + this->object_->name(); }
+
private:
class Add_symbols_locker;
@@ -201,6 +208,10 @@ class Finish_group : public Task
void
run(Workqueue*);
+ std::string
+ get_name() const
+ { return "Finish_group"; }
+
private:
Input_objects* input_objects_;
Symbol_table* symtab_;
diff --git a/gold/reloc.cc b/gold/reloc.cc
index ab74498d244..d50674f6d77 100644
--- a/gold/reloc.cc
+++ b/gold/reloc.cc
@@ -63,6 +63,14 @@ Read_relocs::run(Workqueue* workqueue)
this->symtab_lock_, this->blocker_));
}
+// Return a debugging name for the task.
+
+std::string
+Read_relocs::get_name() const
+{
+ return "Read_relocs " + this->object_->name();
+}
+
// Scan_relocs methods.
// These tasks scan the relocations read by Read_relocs and mark up
@@ -114,6 +122,14 @@ Scan_relocs::run(Workqueue*)
this->rd_ = NULL;
}
+// Return a debugging name for the task.
+
+std::string
+Scan_relocs::get_name() const
+{
+ return "Scan_relocs " + this->object_->name();
+}
+
// Relocate_task methods.
// We may have to wait for the output sections to be written.
@@ -125,6 +141,9 @@ Relocate_task::is_runnable(Workqueue*)
&& this->output_sections_blocker_->is_blocked())
return IS_BLOCKED;
+ if (this->object_->is_locked())
+ return IS_LOCKED;
+
return IS_RUNNABLE;
}
@@ -166,6 +185,14 @@ Relocate_task::run(Workqueue*)
this->of_);
}
+// Return a debugging name for the task.
+
+std::string
+Relocate_task::get_name() const
+{
+ return "Relocate_task " + this->object_->name();
+}
+
// Read the relocs and local symbols from the object file and store
// the information in RD.
diff --git a/gold/reloc.h b/gold/reloc.h
index 2ff49d84bf0..d84dc88828b 100644
--- a/gold/reloc.h
+++ b/gold/reloc.h
@@ -78,6 +78,9 @@ class Read_relocs : public Task
void
run(Workqueue*);
+ std::string
+ get_name() const;
+
private:
const General_options& options_;
Symbol_table* symtab_;
@@ -113,6 +116,9 @@ class Scan_relocs : public Task
void
run(Workqueue*);
+ std::string
+ get_name() const;
+
private:
class Scan_relocs_locker;
@@ -151,6 +157,9 @@ class Relocate_task : public Task
void
run(Workqueue*);
+ std::string
+ get_name() const;
+
private:
class Relocate_locker;
diff --git a/gold/script.cc b/gold/script.cc
index 83e490c2fcb..1ebd100b932 100644
--- a/gold/script.cc
+++ b/gold/script.cc
@@ -815,6 +815,10 @@ class Script_unblock : public Task
run(Workqueue*)
{ }
+ std::string
+ get_name() const
+ { return "Script_unblock"; }
+
private:
Task_token* this_blocker_;
Task_token* next_blocker_;
diff --git a/gold/symtab.cc b/gold/symtab.cc
index f0c09f9924d..b2901ab9faa 100644
--- a/gold/symtab.cc
+++ b/gold/symtab.cc
@@ -159,6 +159,17 @@ Symbol::init_base(const char* name, elfcpp::STT type,
this->in_reg_ = true;
}
+// Allocate a common symbol in the base.
+
+void
+Symbol::allocate_base_common(Output_data* od)
+{
+ gold_assert(this->is_common());
+ this->source_ = IN_OUTPUT_DATA;
+ this->u_.in_output_data.output_data = od;
+ this->u_.in_output_data.offset_is_from_end = false;
+}
+
// Initialize the fields in Sized_symbol for SYM in OBJECT.
template<int size>
@@ -219,6 +230,16 @@ Sized_symbol<size>::init(const char* name, Value_type value, Size_type symsize,
this->symsize_ = symsize;
}
+// Allocate a common symbol.
+
+template<int size>
+void
+Sized_symbol<size>::allocate_common(Output_data* od, Value_type value)
+{
+ this->allocate_base_common(od);
+ this->value_ = value;
+}
+
// Return true if this symbol should be added to the dynamic symbol
// table.
@@ -2017,6 +2038,18 @@ Warnings::issue_warning(const Symbol* sym,
// script to restrict this to only the ones needed for implemented
// targets.
+#if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_32_BIG)
+template
+void
+Sized_symbol<32>::allocate_common(Output_data*, Value_type);
+#endif
+
+#if defined(HAVE_TARGET_64_LITTLE) || defined(HAVE_TARGET_64_BIG)
+template
+void
+Sized_symbol<64>::allocate_common(Output_data*, Value_type);
+#endif
+
#ifdef HAVE_TARGET_32_LITTLE
template
void
diff --git a/gold/symtab.h b/gold/symtab.h
index b6e550463ee..9c7fb0935c7 100644
--- a/gold/symtab.h
+++ b/gold/symtab.h
@@ -598,6 +598,11 @@ class Symbol
void
override_base_with_special(const Symbol* from);
+ // Allocate a common symbol by giving it a location in the output
+ // file.
+ void
+ allocate_base_common(Output_data*);
+
private:
Symbol(const Symbol&);
Symbol& operator=(const Symbol&);
@@ -798,6 +803,11 @@ class Sized_symbol : public Symbol
set_value(Value_type value)
{ this->value_ = value; }
+ // Allocate a common symbol by giving it a location in the output
+ // file.
+ void
+ allocate_common(Output_data*, Value_type value);
+
private:
Sized_symbol(const Sized_symbol&);
Sized_symbol& operator=(const Sized_symbol&);
diff --git a/gold/workqueue-internal.h b/gold/workqueue-internal.h
new file mode 100644
index 00000000000..d9fd1608502
--- /dev/null
+++ b/gold/workqueue-internal.h
@@ -0,0 +1,129 @@
+// workqueue-internal.h -- internal work queue header for gold -*- C++ -*-
+
+// Copyright 2006, 2007 Free Software Foundation, Inc.
+// Written by Ian Lance Taylor <iant@google.com>.
+
+// This file is part of gold.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+// MA 02110-1301, USA.
+
+#ifndef GOLD_WORKQUEUE_INTERNAL_H
+#define GOLD_WORKQUEUE_INTERNAL_H
+
+#include <queue>
+
+#include "gold-threads.h"
+#include "workqueue.h"
+
+// This is an internal header file for different gold workqueue
+// implementations.
+
+namespace gold
+{
+
+class Workqueue_thread;
+
+// The Workqueue_runner abstract class. This is the interface used by
+// the general workqueue code to actually run a task.
+
+class Workqueue_runner
+{
+ public:
+ Workqueue_runner(Workqueue* workqueue)
+ : workqueue_(workqueue)
+ { }
+ virtual ~Workqueue_runner()
+ { }
+
+ // Run a task. This is always called in the main thread.
+ virtual void
+ run(Task*, Task_locker*) = 0;
+
+ // Set the number of threads to use. This is ignored when not using
+ // threads.
+ virtual void
+ set_thread_count(int) = 0;
+
+ protected:
+ // This is called by an implementation when a task is completed.
+ void completed(Task* t, Task_locker* tl)
+ { this->workqueue_->completed(t, tl); }
+
+ Workqueue* get_workqueue() const
+ { return this->workqueue_; }
+
+ private:
+ Workqueue* workqueue_;
+};
+
+// The threaded instantiation of Workqueue_runner.
+
+class Workqueue_runner_threadpool : public Workqueue_runner
+{
+ public:
+ Workqueue_runner_threadpool(Workqueue* workqueue);
+
+ ~Workqueue_runner_threadpool();
+
+ void
+ run(Task*, Task_locker*);
+
+ void
+ set_thread_count(int);
+
+ private:
+ // This class can not be copied.
+ Workqueue_runner_threadpool(const Workqueue_runner_threadpool&);
+ Workqueue_runner_threadpool& operator=(const Workqueue_runner_threadpool&);
+
+ // Return the next Task and Task_locker to run. This returns false
+ // if the calling thread should simply exit.
+ bool
+ get_next(Task**, Task_locker**);
+
+ // This is called when the thread completes a task.
+ void
+ thread_completed(Task*, Task_locker*);
+
+ // The Workqueue_thread class calls functions from this and from the
+ // parent Workqueue_runner.
+ friend class Workqueue_thread;
+
+ // An entry on the queue of tasks to run.
+ typedef std::pair<Task*, Task_locker*> Task_queue_entry;
+
+ // A queue of tasks to run.
+ typedef std::queue<Task_queue_entry> Task_queue;
+
+ // The number of threads we want to create. This is only changed in
+ // the main thread or when only one thread is running. This is set
+ // to zero when all threads should exit.
+ int desired_thread_count_;
+ // A lock controlling access to the remaining fields.
+ Lock lock_;
+ // The number of threads we have created.
+ int actual_thread_count_;
+ // The number of threads which are running a task.
+ int running_thread_count_;
+ // A queue of tasks to run.
+ Task_queue task_queue_;
+ // A condition variable which signals when the task_queue_ changed.
+ Condvar task_queue_condvar_;
+};
+
+} // End namespace gold.
+
+#endif // !defined(GOLD_WORKQUEUE_INTERNAL_H)
diff --git a/gold/workqueue-threads.cc b/gold/workqueue-threads.cc
new file mode 100644
index 00000000000..a4f347de5de
--- /dev/null
+++ b/gold/workqueue-threads.cc
@@ -0,0 +1,266 @@
+// workqueue-threads.cc -- the threaded workqueue for gold
+
+// Copyright 2007 Free Software Foundation, Inc.
+// Written by Ian Lance Taylor <iant@google.com>.
+
+// This file is part of gold.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+// MA 02110-1301, USA.
+
+// This file holds the workqueue implementation which may be used when
+// using threads.
+
+#include "gold.h"
+
+#ifdef ENABLE_THREADS
+
+#include <cstring>
+#include <pthread.h>
+
+#include "debug.h"
+#include "gold-threads.h"
+#include "workqueue.h"
+#include "workqueue-internal.h"
+
+namespace gold
+{
+
+// Class Workqueue_thread represents a single thread. Creating an
+// instance of this spawns a new thread.
+
+class Workqueue_thread
+{
+ public:
+ Workqueue_thread(Workqueue_runner_threadpool*);
+
+ ~Workqueue_thread();
+
+ private:
+ // This class can not be copied.
+ Workqueue_thread(const Workqueue_thread&);
+ Workqueue_thread& operator=(const Workqueue_thread&);
+
+ // Check for error from a pthread function.
+ void
+ check(const char* function, int err) const;
+
+ // A function to pass to pthread_create. This is called with a
+ // pointer to an instance of this object.
+ static void*
+ thread_body(void*);
+
+ // The main loop of the thread.
+ void
+ run();
+
+ // A pointer to the threadpool that this thread is part of.
+ Workqueue_runner_threadpool* threadpool_;
+ // The thread ID.
+ pthread_t tid_;
+};
+
+// Create the thread in the constructor.
+
+Workqueue_thread::Workqueue_thread(Workqueue_runner_threadpool* threadpool)
+ : threadpool_(threadpool)
+{
+ pthread_attr_t attr;
+ int err = pthread_attr_init(&attr);
+ this->check("pthread_attr_init", err);
+
+ err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ this->check("pthread_attr_setdetachstate", err);
+
+ err = pthread_create(&this->tid_, &attr, &Workqueue_thread::thread_body,
+ reinterpret_cast<void*>(this));
+ this->check("pthread_create", err);
+
+ err = pthread_attr_destroy(&attr);
+ this->check("pthread_attr_destroy", err);
+}
+
+// The destructor will be called when the thread is exiting.
+
+Workqueue_thread::~Workqueue_thread()
+{
+}
+
+// Check for an error.
+
+void
+Workqueue_thread::check(const char* function, int err) const
+{
+ if (err != 0)
+ gold_fatal(_("%s failed: %s"), function, strerror(err));
+}
+
+// Passed to pthread_create.
+
+extern "C"
+void*
+Workqueue_thread::thread_body(void* arg)
+{
+ Workqueue_thread* pwt = reinterpret_cast<Workqueue_thread*>(arg);
+ pwt->run();
+
+ // Delete the thread object as we exit.
+ delete pwt;
+
+ return NULL;
+}
+
+// This is the main loop of a worker thread. It picks up a new Task
+// and runs it.
+
+void
+Workqueue_thread::run()
+{
+ Workqueue_runner_threadpool* threadpool = this->threadpool_;
+ Workqueue* workqueue = threadpool->get_workqueue();
+
+ while (true)
+ {
+ Task* t;
+ Task_locker* tl;
+ if (!threadpool->get_next(&t, &tl))
+ return;
+
+ gold_debug(DEBUG_TASK, "running task %s", t->name().c_str());
+
+ t->run(workqueue);
+ threadpool->thread_completed(t, tl);
+ }
+}
+
+// Class Workqueue_runner_threadpool.
+
+// Constructor.
+
+Workqueue_runner_threadpool::Workqueue_runner_threadpool(Workqueue* workqueue)
+ : Workqueue_runner(workqueue),
+ desired_thread_count_(0),
+ lock_(),
+ actual_thread_count_(0),
+ running_thread_count_(0),
+ task_queue_(),
+ task_queue_condvar_(this->lock_)
+{
+}
+
+// Destructor.
+
+Workqueue_runner_threadpool::~Workqueue_runner_threadpool()
+{
+ // Tell the threads to exit.
+ Hold_lock hl(this->lock_);
+ this->desired_thread_count_ = 0;
+ this->task_queue_condvar_.broadcast();
+}
+
+// Run a task. This doesn't actually run the task: it pushes on the
+// queue of tasks to run. This is always called in the main thread.
+
+void
+Workqueue_runner_threadpool::run(Task* t, Task_locker* tl)
+{
+ Hold_lock hl(this->lock_);
+
+ // This is where we create threads as needed, subject to the limit
+ // of the desired thread count.
+ gold_assert(this->desired_thread_count_ > 0);
+ gold_assert(this->actual_thread_count_ >= this->running_thread_count_);
+ if (this->actual_thread_count_ == this->running_thread_count_
+ && this->actual_thread_count_ < this->desired_thread_count_)
+ {
+ // Note that threads delete themselves when they exit, so we
+ // don't keep pointers to them.
+ new Workqueue_thread(this);
+ ++this->actual_thread_count_;
+ }
+
+ this->task_queue_.push(std::make_pair(t, tl));
+ this->task_queue_condvar_.signal();
+}
+
+// Set the thread count. This is only called in the main thread, and
+// is only called when there are no threads running.
+
+void
+Workqueue_runner_threadpool::set_thread_count(int thread_count)
+{
+ gold_assert(this->running_thread_count_ <= 1);
+ gold_assert(thread_count > 0);
+ this->desired_thread_count_ = thread_count;
+}
+
+// Get the next task to run. This is always called by an instance of
+// Workqueue_thread, and is never called in the main thread. It
+// returns false if the calling thread should exit.
+
+bool
+Workqueue_runner_threadpool::get_next(Task** pt, Task_locker** ptl)
+{
+ Hold_lock hl(this->lock_);
+
+ // This is where we destroy threads, by telling them to exit.
+ gold_assert(this->actual_thread_count_ > this->running_thread_count_);
+ if (this->actual_thread_count_ > this->desired_thread_count_)
+ {
+ --this->actual_thread_count_;
+ return false;
+ }
+
+ while (this->task_queue_.empty() && this->desired_thread_count_ > 0)
+ {
+ // Wait for a new task to become available.
+ this->task_queue_condvar_.wait();
+ }
+
+ // Check whether we are exiting.
+ if (this->desired_thread_count_ == 0)
+ {
+ gold_assert(this->actual_thread_count_ > 0);
+ --this->actual_thread_count_;
+ return false;
+ }
+
+ *pt = this->task_queue_.front().first;
+ *ptl = this->task_queue_.front().second;
+ this->task_queue_.pop();
+
+ ++this->running_thread_count_;
+
+ return true;
+}
+
+// This is called when a thread completes its task.
+
+void
+Workqueue_runner_threadpool::thread_completed(Task* t, Task_locker* tl)
+{
+ {
+ Hold_lock hl(this->lock_);
+ gold_assert(this->actual_thread_count_ > 0);
+ gold_assert(this->running_thread_count_ > 0);
+ --this->running_thread_count_;
+ }
+
+ this->completed(t, tl);
+}
+
+} // End namespace gold.
+
+#endif // defined(ENABLE_THREADS)
diff --git a/gold/workqueue.cc b/gold/workqueue.cc
index 95c14ce5a85..018c96bdbd2 100644
--- a/gold/workqueue.cc
+++ b/gold/workqueue.cc
@@ -22,11 +22,9 @@
#include "gold.h"
-#ifdef ENABLE_THREADS
-#include <pthread.h>
-#endif
-
+#include "debug.h"
#include "workqueue.h"
+#include "workqueue-internal.h"
namespace gold
{
@@ -145,38 +143,6 @@ Task_block_token::~Task_block_token()
}
}
-// The Workqueue_runner abstract class.
-
-class Workqueue_runner
-{
- public:
- Workqueue_runner(Workqueue* workqueue)
- : workqueue_(workqueue)
- { }
- virtual ~Workqueue_runner()
- { }
-
- // Run a task. This is always called in the main thread.
- virtual void
- run(Task*, Task_locker*) = 0;
-
- // Set the number of threads to use. This is ignored when not using
- // threads.
- virtual void
- set_thread_count(int) = 0;
-
- protected:
- // This is called by an implementation when a task is completed.
- void completed(Task* t, Task_locker* tl)
- { this->workqueue_->completed(t, tl); }
-
- Workqueue* get_workqueue() const
- { return this->workqueue_; }
-
- private:
- Workqueue* workqueue_;
-};
-
// The simple single-threaded implementation of Workqueue_runner.
class Workqueue_runner_single : public Workqueue_runner
@@ -212,12 +178,15 @@ Workqueue_runner_single::set_thread_count(int thread_count)
Workqueue::Workqueue(const General_options& options)
: tasks_lock_(),
+ first_tasks_(),
tasks_(),
completed_lock_(),
completed_(),
running_(0),
+ queued_(0),
completed_condvar_(this->completed_lock_),
- cleared_blockers_(0)
+ cleared_blockers_(0),
+ desired_thread_count_(1)
{
bool threads = options.threads();
#ifndef ENABLE_THREADS
@@ -226,11 +195,18 @@ Workqueue::Workqueue(const General_options& options)
if (!threads)
this->runner_ = new Workqueue_runner_single(this);
else
- gold_unreachable();
+ {
+#ifdef ENABLE_THREADS
+ this->runner_ = new Workqueue_runner_threadpool(this);
+#else
+ gold_unreachable();
+#endif
+ }
}
Workqueue::~Workqueue()
{
+ gold_assert(this->first_tasks_.empty());
gold_assert(this->tasks_.empty());
gold_assert(this->completed_.empty());
gold_assert(this->running_ == 0);
@@ -241,8 +217,14 @@ Workqueue::~Workqueue()
void
Workqueue::queue(Task* t)
{
- Hold_lock hl(this->tasks_lock_);
- this->tasks_.push_back(t);
+ {
+ Hold_lock hl(this->tasks_lock_);
+ this->tasks_.push_back(t);
+ }
+ {
+ Hold_lock hl(this->completed_lock_);
+ ++this->queued_;
+ }
}
// Add a task to the front of the queue.
@@ -250,8 +232,14 @@ Workqueue::queue(Task* t)
void
Workqueue::queue_front(Task* t)
{
- Hold_lock hl(this->tasks_lock_);
- this->tasks_.push_front(t);
+ {
+ Hold_lock hl(this->tasks_lock_);
+ this->first_tasks_.push_front(t);
+ }
+ {
+ Hold_lock hl(this->completed_lock_);
+ ++this->queued_;
+ }
}
// Clear the list of completed tasks. Return whether we cleared
@@ -277,48 +265,36 @@ Workqueue::clear_completed()
// a blocker.
Task*
-Workqueue::find_runnable(Task_list& tasks, bool* all_blocked)
+Workqueue::find_runnable(Task_list* tasks, bool* all_blocked)
{
- Task* tlast = tasks.back();
+ Task* tlast = tasks->back();
*all_blocked = true;
- while (true)
+ Task* t;
+ do
{
- Task* t = tasks.front();
- tasks.pop_front();
+ t = tasks->front();
+ tasks->pop_front();
Task::Is_runnable_type is_runnable = t->is_runnable(this);
if (is_runnable == Task::IS_RUNNABLE)
- return t;
-
- if (is_runnable != Task::IS_BLOCKED)
- *all_blocked = false;
-
- tasks.push_back(t);
-
- if (t == tlast)
{
- // We couldn't find any runnable task. If there are any
- // completed tasks, free their locks and try again.
-
{
- Hold_lock hl2(this->completed_lock_);
-
- if (!this->clear_completed())
- {
- // There had better be some tasks running, or we will
- // never find a runnable task.
- gold_assert(this->running_ > 0);
-
- // We couldn't find any runnable tasks, and we
- // couldn't release any locks.
- return NULL;
- }
+ Hold_lock hl(this->completed_lock_);
+ --this->queued_;
}
- // We're going around again, so recompute ALL_BLOCKED.
- *all_blocked = true;
+ return t;
}
+
+ if (is_runnable != Task::IS_BLOCKED)
+ *all_blocked = false;
+
+ tasks->push_back(t);
}
+ while (t != tlast);
+
+ // We couldn't find any runnable task.
+ return NULL;
}
// Process all the tasks on the workqueue. This is the main loop in
@@ -334,28 +310,56 @@ Workqueue::process()
bool empty;
bool all_blocked;
+ // Don't start more tasks than desired.
+ {
+ Hold_lock hl(this->completed_lock_);
+
+ this->clear_completed();
+ while (this->running_ >= this->desired_thread_count_)
+ {
+ this->completed_condvar_.wait();
+ this->clear_completed();
+ }
+ }
+
{
Hold_lock hl(this->tasks_lock_);
- if (this->tasks_.empty())
+ bool first_empty;
+ bool all_blocked_first;
+ if (this->first_tasks_.empty())
{
t = NULL;
empty = true;
- all_blocked = false;
+ first_empty = true;
+ all_blocked_first = false;
}
else
{
- t = this->find_runnable(this->tasks_, &all_blocked);
+ t = this->find_runnable(&this->first_tasks_, &all_blocked_first);
empty = false;
+ first_empty = false;
+ }
+
+ if (t == NULL)
+ {
+ if (this->tasks_.empty())
+ all_blocked = false;
+ else
+ {
+ t = this->find_runnable(&this->tasks_, &all_blocked);
+ if (!first_empty && !all_blocked_first)
+ all_blocked = false;
+ empty = false;
+ }
}
}
// If T != NULL, it is a task we can run.
// If T == NULL && empty, then there are no tasks waiting to
- // be run at this level.
+ // be run.
// If T == NULL && !empty, then there tasks waiting to be
- // run at this level, but they are waiting for something to
- // unlock.
+ // run, but they are waiting for something to unlock.
if (t != NULL)
this->run(t);
@@ -371,10 +375,16 @@ Workqueue::process()
if (all_blocked)
{
this->cleared_blockers_ = 0;
+ int queued = this->queued_;
this->clear_completed();
- while (this->cleared_blockers_ == 0)
+ while (this->cleared_blockers_ == 0
+ && queued == this->queued_)
{
- gold_assert(this->running_ > 0);
+ if (this->running_ <= 0)
+ {
+ this->show_queued_tasks();
+ gold_unreachable();
+ }
this->completed_condvar_.wait();
this->clear_completed();
}
@@ -416,7 +426,12 @@ Workqueue::process()
void
Workqueue::run(Task* t)
{
- ++this->running_;
+ gold_debug(DEBUG_TASK, "starting task %s", t->name().c_str());
+
+ {
+ Hold_lock hl(this->completed_lock_);
+ ++this->running_;
+ }
this->runner_->run(t, t->locks(this));
}
@@ -427,6 +442,8 @@ Workqueue::run(Task* t)
void
Workqueue::completed(Task* t, Task_locker* tl)
{
+ gold_debug(DEBUG_TASK, "completed task %s", t->name().c_str());
+
{
Hold_lock hl(this->completed_lock_);
gold_assert(this->running_ > 0);
@@ -434,6 +451,7 @@ Workqueue::completed(Task* t, Task_locker* tl)
this->completed_.push_back(tl);
this->completed_condvar_.signal();
}
+
delete t;
}
@@ -452,7 +470,40 @@ Workqueue::cleared_blocker()
void
Workqueue::set_thread_count(int threads)
{
+ gold_assert(threads > 0);
+ this->desired_thread_count_ = threads;
this->runner_->set_thread_count(threads);
}
+// Dump the list of queued tasks and their current state, for
+// debugging purposes.
+
+void
+Workqueue::show_queued_tasks()
+{
+ fprintf(stderr, _("gold task queue:\n"));
+ Hold_lock hl(this->tasks_lock_);
+ for (Task_list::const_iterator p = this->tasks_.begin();
+ p != this->tasks_.end();
+ ++p)
+ {
+ fprintf(stderr, " %s ", (*p)->name().c_str());
+ switch ((*p)->is_runnable(this))
+ {
+ case Task::IS_RUNNABLE:
+ fprintf(stderr, "runnable");
+ break;
+ case Task::IS_BLOCKED:
+ fprintf(stderr, "blocked");
+ break;
+ case Task::IS_LOCKED:
+ fprintf(stderr, "locked");
+ break;
+ default:
+ gold_unreachable();
+ }
+ putc('\n', stderr);
+ }
+}
+
} // End namespace gold.
diff --git a/gold/workqueue.h b/gold/workqueue.h
index 777b3aa0918..e435739c4ad 100644
--- a/gold/workqueue.h
+++ b/gold/workqueue.h
@@ -275,6 +275,7 @@ class Task
{
public:
Task()
+ : name_()
{ }
virtual ~Task()
{ }
@@ -307,9 +308,29 @@ class Task
virtual void
run(Workqueue*) = 0;
+ // Return the name of the Task. This is only used for debugging
+ // purposes.
+ const std::string&
+ name()
+ {
+ if (this->name_.empty())
+ this->name_ = this->get_name();
+ return this->name_;
+ }
+
+ protected:
+ // Get the name of the task. This must be implemented by the child
+ // class.
+ virtual std::string
+ get_name() const = 0;
+
private:
+ // This task may not be copied.
Task(const Task&);
Task& operator=(const Task&);
+
+ // Task name, for debugging purposes.
+ std::string name_;
};
// A simple task which waits for a blocker and then runs a function.
@@ -329,8 +350,9 @@ class Task_function : public Task
public:
// Both points should be allocated using new, and will be deleted
// after the task runs.
- Task_function(Task_function_runner* runner, Task_token* blocker)
- : runner_(runner), blocker_(blocker)
+ Task_function(Task_function_runner* runner, Task_token* blocker,
+ const char* name)
+ : runner_(runner), blocker_(blocker), name_(name)
{ }
~Task_function()
@@ -356,12 +378,18 @@ class Task_function : public Task
run(Workqueue* workqueue)
{ this->runner_->run(workqueue); }
+ // The debugging name.
+ std::string
+ get_name() const
+ { return this->name_; }
+
private:
Task_function(const Task_function&);
Task_function& operator=(const Task_function&);
Task_function_runner* runner_;
Task_token* blocker_;
+ const char* name_;
};
// The workqueue
@@ -403,39 +431,56 @@ class Workqueue
typedef std::list<Task*> Task_list;
// Run a task.
- void run(Task*);
+ void
+ run(Task*);
friend class Workqueue_runner;
// Find a runnable task.
- Task* find_runnable(Task_list&, bool*);
+ Task*
+ find_runnable(Task_list*, bool*);
// Add a lock to the completed queue.
- void completed(Task*, Task_locker*);
+ void
+ completed(Task*, Task_locker*);
// Clear the completed queue.
- bool clear_completed();
+ bool
+ clear_completed();
+
+ // Print the list of queued tasks.
+ void
+ show_queued_tasks();
// How to run a task. Only accessed from main thread.
Workqueue_runner* runner_;
// Lock for access to tasks_ members.
Lock tasks_lock_;
- // List of tasks to execute at each link level.
+ // List of tasks to execute soon.
+ Task_list first_tasks_;
+ // List of tasks to execute after the ones in first_tasks_.
Task_list tasks_;
- // Lock for access to completed_ and running_ members.
+ // Lock for access to completed_, running_, and queued_.
Lock completed_lock_;
// List of Task_locker objects for main thread to free.
std::list<Task_locker*> completed_;
// Number of tasks currently running.
int running_;
+ // Number of tasks currently on queue (both first_tasks_ and
+ // tasks_).
+ int queued_;
// Condition variable signalled when a new entry is added to completed_.
Condvar completed_condvar_;
// Number of blocker tokens which were fully cleared. Only accessed
// from main thread.
int cleared_blockers_;
+
+ // The desired thread count. Only set by the main thread or by a
+ // singleton thread. Only accessed from the main thread.
+ int desired_thread_count_;
};
} // End namespace gold.