From Andrew Chatham and Craig Silverstein: Add support for version
authorIan Lance Taylor <iant@google.com>
Tue, 15 Jan 2008 23:41:28 +0000 (23:41 +0000)
committerIan Lance Taylor <iant@google.com>
Tue, 15 Jan 2008 23:41:28 +0000 (23:41 +0000)
scripts.

20 files changed:
gold/Makefile.in
gold/configure
gold/configure.ac
gold/dynobj.cc
gold/dynobj.h
gold/layout.cc
gold/main.cc
gold/options.cc
gold/options.h
gold/script-c.h
gold/script.cc
gold/script.h
gold/symtab.cc
gold/symtab.h
gold/testsuite/Makefile.am
gold/testsuite/Makefile.in
gold/testsuite/ver_matching_def.cc [new file with mode: 0644]
gold/testsuite/ver_matching_test.sh [new file with mode: 0755]
gold/testsuite/version_script.map [new file with mode: 0644]
gold/yyscript.y

index 8e02a9c..aa399ba 100644 (file)
@@ -198,6 +198,8 @@ MSGMERGE = @MSGMERGE@
 NATIVE_LINKER_FALSE = @NATIVE_LINKER_FALSE@
 NATIVE_LINKER_TRUE = @NATIVE_LINKER_TRUE@
 NO_WERROR = @NO_WERROR@
+OBJDUMP_AND_CPPFILT_FALSE = @OBJDUMP_AND_CPPFILT_FALSE@
+OBJDUMP_AND_CPPFILT_TRUE = @OBJDUMP_AND_CPPFILT_TRUE@
 OBJEXT = @OBJEXT@
 PACKAGE = @PACKAGE@
 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
index 66e6167..deccea5 100755 (executable)
@@ -309,7 +309,7 @@ ac_includes_default="\
 # include <unistd.h>
 #endif"
 
-ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar THREADS_TRUE THREADS_FALSE TARGETOBJS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE YACC RANLIB ac_ct_RANLIB LN_S USE_NLS LIBINTL LIBINTL_DEP INCINTL XGETTEXT GMSGFMT POSUB CATALOGS DATADIRNAME INSTOBJEXT GENCAT CATOBJEXT MKINSTALLDIRS MSGFMT MSGMERGE NATIVE_LINKER_TRUE NATIVE_LINKER_FALSE GCC_TRUE GCC_FALSE FN_PTRS_IN_SO_WITHOUT_PIC_TRUE FN_PTRS_IN_SO_WITHOUT_PIC_FALSE TLS_TRUE TLS_FALSE STATIC_TLS_TRUE STATIC_TLS_FALSE WARN_CFLAGS NO_WERROR WARN_CXXFLAGS LFS_CXXFLAGS LIBOBJS CPP EGREP CXXCPP MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT LTLIBOBJS'
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar THREADS_TRUE THREADS_FALSE TARGETOBJS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE YACC RANLIB ac_ct_RANLIB LN_S USE_NLS LIBINTL LIBINTL_DEP INCINTL XGETTEXT GMSGFMT POSUB CATALOGS DATADIRNAME INSTOBJEXT GENCAT CATOBJEXT MKINSTALLDIRS MSGFMT MSGMERGE NATIVE_LINKER_TRUE NATIVE_LINKER_FALSE GCC_TRUE GCC_FALSE OBJDUMP_AND_CPPFILT_TRUE OBJDUMP_AND_CPPFILT_FALSE FN_PTRS_IN_SO_WITHOUT_PIC_TRUE FN_PTRS_IN_SO_WITHOUT_PIC_FALSE TLS_TRUE TLS_FALSE STATIC_TLS_TRUE STATIC_TLS_FALSE WARN_CFLAGS NO_WERROR WARN_CXXFLAGS LFS_CXXFLAGS LIBOBJS CPP EGREP CXXCPP MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT LTLIBOBJS'
 ac_subst_files=''
 
 # Initialize some variables set by options.
@@ -4459,6 +4459,16 @@ fi
 
 
 
+if objdump --help >/dev/null && c++filt --help >/dev/null; then
+  OBJDUMP_AND_CPPFILT_TRUE=
+  OBJDUMP_AND_CPPFILT_FALSE='#'
+else
+  OBJDUMP_AND_CPPFILT_TRUE='#'
+  OBJDUMP_AND_CPPFILT_FALSE=
+fi
+
+
+
 
 if
   case $target_cpu in
@@ -6388,6 +6398,13 @@ echo "$as_me: error: conditional \"GCC\" was never defined.
 Usually this means the macro was only invoked conditionally." >&2;}
    { (exit 1); exit 1; }; }
 fi
+if test -z "${OBJDUMP_AND_CPPFILT_TRUE}" && test -z "${OBJDUMP_AND_CPPFILT_FALSE}"; then
+  { { echo "$as_me:$LINENO: error: conditional \"OBJDUMP_AND_CPPFILT\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+echo "$as_me: error: conditional \"OBJDUMP_AND_CPPFILT\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
 if test -z "${FN_PTRS_IN_SO_WITHOUT_PIC_TRUE}" && test -z "${FN_PTRS_IN_SO_WITHOUT_PIC_FALSE}"; then
   { { echo "$as_me:$LINENO: error: conditional \"FN_PTRS_IN_SO_WITHOUT_PIC\" was never defined.
 Usually this means the macro was only invoked conditionally." >&5
@@ -7040,6 +7057,8 @@ s,@NATIVE_LINKER_TRUE@,$NATIVE_LINKER_TRUE,;t t
 s,@NATIVE_LINKER_FALSE@,$NATIVE_LINKER_FALSE,;t t
 s,@GCC_TRUE@,$GCC_TRUE,;t t
 s,@GCC_FALSE@,$GCC_FALSE,;t t
+s,@OBJDUMP_AND_CPPFILT_TRUE@,$OBJDUMP_AND_CPPFILT_TRUE,;t t
+s,@OBJDUMP_AND_CPPFILT_FALSE@,$OBJDUMP_AND_CPPFILT_FALSE,;t t
 s,@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@,$FN_PTRS_IN_SO_WITHOUT_PIC_TRUE,;t t
 s,@FN_PTRS_IN_SO_WITHOUT_PIC_FALSE@,$FN_PTRS_IN_SO_WITHOUT_PIC_FALSE,;t t
 s,@TLS_TRUE@,$TLS_TRUE,;t t
index ea4fd67..cad5e02 100644 (file)
@@ -157,6 +157,8 @@ AC_EXEEXT
 AM_CONDITIONAL(NATIVE_LINKER,
   test "x$target_alias" = "x" -o "x$host_alias" = "x$target_alias")
 AM_CONDITIONAL(GCC, test "$GCC" = yes)
+AM_CONDITIONAL(OBJDUMP_AND_CPPFILT,
+       [objdump --help >/dev/null && c++filt --help >/dev/null])
 
 dnl Some architectures do not support taking pointers of functions
 dnl defined in shared libraries except in -fPIC mode.  We need to
index 3c3549d..90abbe7 100644 (file)
@@ -1,6 +1,6 @@
 // dynobj.cc -- dynamic object support for gold
 
-// Copyright 2006, 2007 Free Software Foundation, Inc.
+// Copyright 2006, 2007, 2008 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -1227,6 +1227,46 @@ Verneed::write(const Stringpool* dynpool, bool is_last,
 
 // Versions methods.
 
+Versions::Versions(const General_options& options, Stringpool* dynpool)
+  : defs_(), needs_(), version_table_(),
+    is_finalized_(false), version_script_(options.version_script())
+{
+  // We always need a base version, so define that first. Nothing
+  // explicitly declares itself as part of base, so it doesn't need to
+  // be in version_table_. 
+  // FIXME: Should use soname here when creating a shared object. Is
+  // this fixme still valid? It looks like it's doing the right thing
+  // to me.
+  if (parameters->output_is_shared())
+    {
+      const char* name = dynpool->add(parameters->output_file_name(),
+                                      false, NULL);
+      Verdef* vdbase = new Verdef(name, std::vector<std::string>(),
+                                  true, false, true);
+      this->defs_.push_back(vdbase);
+    }
+
+  if (!this->version_script_.empty())
+    {
+      // Parse the version script, and insert each declared version into
+      // defs_ and version_table_.
+      std::vector<std::string> versions = this->version_script_.get_versions();
+      for (size_t k = 0; k < versions.size(); ++k)
+        {
+          Stringpool::Key version_key;
+          const char* version = dynpool->add(versions[k].c_str(),
+                                             true, &version_key);
+          Verdef* const vd = new Verdef(
+              version,
+              options.version_script().get_dependencies(version),
+              false, false, false);
+          this->defs_.push_back(vd);
+          Key key(version_key, 0);
+          this->version_table_.insert(std::make_pair(key, vd));
+        }
+    }
+}
+
 Versions::~Versions()
 {
   for (Defs::iterator p = this->defs_.begin();
@@ -1265,7 +1305,7 @@ Versions::record_version(const Symbol_table* symtab,
 {
   gold_assert(!this->is_finalized_);
   gold_assert(sym->version() != NULL);
-
+  
   Stringpool::Key version_key;
   const char* version = dynpool->add(sym->version(), false, &version_key);
 
@@ -1292,7 +1332,7 @@ Versions::add_def(const Symbol* sym, const char* version,
   Version_base* const vbnull = NULL;
   std::pair<Version_table::iterator, bool> ins =
     this->version_table_.insert(std::make_pair(k, vbnull));
-
+  
   if (!ins.second)
     {
       // We already have an entry for this version.
@@ -1318,16 +1358,10 @@ Versions::add_def(const Symbol* sym, const char* version,
          return;
        }
 
-      // If this is the first version we are defining, first define
-      // the base version.  FIXME: Should use soname here when
-      // creating a shared object.
-      Verdef* vdbase = new Verdef(parameters->output_file_name(), true, false,
-                                 true);
-      this->defs_.push_back(vdbase);
-
       // When creating a regular executable, automatically define
       // a new version.
-      Verdef* vd = new Verdef(version, false, false, false);
+      Verdef* vd = new Verdef(version, std::vector<std::string>(),
+                              false, false, false);
       this->defs_.push_back(vd);
       ins.first->second = vd;
     }
@@ -1499,10 +1533,14 @@ Versions::symbol_section_contents(const Symbol_table* symtab,
       const char* version = (*p)->version();
       if (version == NULL)
        version_index = elfcpp::VER_NDX_GLOBAL;
-      else
+      else        
        version_index = this->version_index(symtab, dynpool, *p);
+      // If the symbol was defined as foo@V1 instead of foo@@V1, add
+      // the hidden bit.
+      if ((*p)->version() != NULL && !(*p)->is_default())
+        version_index |= elfcpp::VERSYM_HIDDEN;
       elfcpp::Swap<16, big_endian>::writeval(pbuf + (*p)->dynsym_index() * 2,
-                                            version_index);
+                                             version_index);
     }
 
   *pp = pbuf;
index cc6ec54..a8c418d 100644 (file)
@@ -32,6 +32,7 @@ namespace gold
 {
 
 class General_options;
+class Version_script_info;
 
 // A dynamic object (ET_DYN).  This is an abstract base class itself.
 // The implementations is the template class Sized_dynobj.
@@ -309,8 +310,9 @@ class Version_base
 class Verdef : public Version_base
 {
  public:
-  Verdef(const char* name, bool is_base, bool is_weak, bool is_symbol_created)
-    : name_(name), deps_(), is_base_(is_base), is_weak_(is_weak),
+  Verdef(const char* name, const std::vector<std::string>& deps,
+         bool is_base, bool is_weak, bool is_symbol_created)
+    : name_(name), deps_(deps), is_base_(is_base), is_weak_(is_weak),
       is_symbol_created_(is_symbol_created)
   { }
 
@@ -358,7 +360,7 @@ class Verdef : public Version_base
 
   // The type of the list of version dependencies.  Each dependency
   // should be canonicalized in the dynamic Stringpool.
-  typedef std::vector<const char*> Deps;
+  typedef std::vector<std::string> Deps;
 
   // The name of this version.  This should be canonicalized in the
   // dynamic Stringpool.
@@ -459,9 +461,7 @@ class Verneed
 class Versions
 {
  public:
-  Versions()
-    : defs_(), needs_(), version_table_(), is_finalized_(false)
-  { }
+  Versions(const General_options&, Stringpool*);
 
   ~Versions();
 
@@ -513,7 +513,14 @@ class Versions
                        unsigned int* psize, unsigned int* pentries
                         ACCEPT_SIZE_ENDIAN) const;
 
+  const Version_script_info&
+  version_script() const
+  { return this->version_script_; }
+      
  private:
+  Versions(const Versions&);
+  Versions& operator=(const Versions&);
+
   // The type of the list of version definitions.
   typedef std::vector<Verdef*> Defs;
 
@@ -568,6 +575,8 @@ class Versions
   Version_table version_table_;
   // Whether the version indexes have been set.
   bool is_finalized_;
+  // Contents of --version-script, if passed, or NULL.
+  const Version_script_info& version_script_;
 };
 
 } // End namespace gold.
index 7db2e81..63fd2b8 100644 (file)
@@ -683,7 +683,7 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab,
       Output_section* dynstr;
       std::vector<Symbol*> dynamic_symbols;
       unsigned int local_dynamic_count;
-      Versions versions;
+      Versions versions(this->options_, &this->dynpool_);
       this->create_dynamic_symtab(input_objects, target, symtab, &dynstr,
                                  &local_dynamic_count, &dynamic_symbols,
                                  &versions);
index fb201d7..507e5dd 100644 (file)
@@ -84,7 +84,8 @@ main(int argc, char** argv)
   // we're going to see based on the number of input files.  Even when
   // this is off, it means at worse we don't quite optimize hashtable
   // resizing as well as we could have (perhap using more memory).
-  Symbol_table symtab(command_line.number_of_input_files() * 1024);
+  Symbol_table symtab(command_line.number_of_input_files() * 1024,
+                      command_line.options().version_script());
 
   // The layout object.
   Layout layout(command_line.options(), &script_options);
index 9b5103c..9198136 100644 (file)
@@ -155,7 +155,23 @@ invoke_script(int argc, char** argv, char* arg, bool long_option,
                                                          arg, long_option,
                                                          &ret);
   if (!read_commandline_script(script_name, cmdline))
-    gold::gold_error(_("unable to parse script file %s"), script_name);
+    gold::gold_fatal(_("unable to parse script file %s"), script_name);
+  return ret;
+}
+
+// Handle the special --version-script option, which reads a version script.
+
+int
+invoke_version_script(int argc, char** argv, char* arg, bool long_option,
+                      gold::Command_line* cmdline)
+{
+  int ret;
+  const char* script_name = cmdline->get_special_argument("version-script",
+                                                          argc, argv,
+                                                         arg, long_option,
+                                                         &ret);
+  if (!read_version_script(script_name, cmdline))
+    gold::gold_fatal(_("unable to parse version script file %s"), script_name);
   return ret;
 }
 
@@ -458,6 +474,9 @@ options::Command_line_options::options[] =
   SPECIAL('T', "script", N_("Read linker script"),
          N_("-T FILE, --script FILE"), TWO_DASHES,
          &invoke_script),
+  SPECIAL('\0', "version-script", N_("Read version script"),
+         N_("--version-script FILE"), TWO_DASHES,
+         &invoke_version_script),
   GENERAL_NOARG('\0', "threads", N_("Run the linker multi-threaded"),
                NULL, TWO_DASHES, &General_options::set_threads),
   GENERAL_NOARG('\0', "no-threads", N_("Do not run the linker multi-threaded"),
index e81856c..47623dc 100644 (file)
@@ -233,6 +233,11 @@ class General_options
   sysroot() const
   { return this->sysroot_; }
 
+  // --version-script: The version script to apply if --shared is true.
+  const Version_script_info&
+  version_script() const
+  { return *this->script_options_->version_script_info(); }
+
   // -Ttext: The address of the .text section
   uint64_t
   text_segment_address() const
index 95816b7..275f4a2 100644 (file)
@@ -107,6 +107,12 @@ script_parse_option(void* closure, const char*, size_t);
 extern void
 script_push_lex_into_expression_mode(void* closure);
 
+/* Called by the bison parser to push the lexer into version
+   mode.  */
+
+extern void
+script_push_lex_into_version_mode(void* closure);
+
 /* Called by the bison parser to pop the lexer mode.  */
 
 extern void
@@ -208,6 +214,38 @@ script_exp_function_segment_start(const char*, size_t, Expression_ptr);
 extern Expression_ptr
 script_exp_function_assert(Expression_ptr, const char*, size_t);
 
+struct Version_dependency_list;
+struct Version_expression_list;
+struct Version_tree;
+
+extern void
+script_register_vers_node(void* closure,
+                         const char* tag,
+                         int taglen,
+                         struct Version_tree *,
+                         struct Version_dependency_list *);
+
+extern struct Version_dependency_list *
+script_add_vers_depend(void* closure,
+                      struct Version_dependency_list *existing_dependencies,
+                      const char *depend_to_add, int deplen);
+
+extern struct Version_expression_list *
+script_new_vers_pattern(void* closure,
+                       struct Version_expression_list *,
+                       const char *, int);
+
+extern struct Version_tree *
+script_new_vers_node(void* closure,
+                    struct Version_expression_list *global,
+                    struct Version_expression_list *local);
+
+extern void
+version_script_push_lang(void* closure, const char* lang, int langlen);
+
+extern void
+version_script_pop_lang(void* closure);
+
 #ifdef __cplusplus
 }
 #endif
index ae9cb86..16c0cc0 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "gold.h"
 
+#include <fnmatch.h>
 #include <string>
 #include <vector>
 #include <cstdio>
@@ -29,6 +30,7 @@
 #include "filenames.h"
 
 #include "elfcpp.h"
+#include "demangle.h"
 #include "dirsearch.h"
 #include "options.h"
 #include "fileread.h"
@@ -245,26 +247,32 @@ class Lex
   inline bool
   can_start_name(char c, char c2);
 
-  // Return whether C can appear in a name which has already started.
-  inline bool
-  can_continue_name(char c);
+  // If C can appear in a name which has already started, return a
+  // pointer to a character later in the token or just past
+  // it. Otherwise, return NULL.
+  inline const char*
+  can_continue_name(const char* c);
 
   // Return whether C, C2, C3 can start a hex number.
   inline bool
   can_start_hex(char c, char c2, char c3);
 
-  // Return whether C can appear in a hex number.
-  inline bool
-  can_continue_hex(char c);
+  // If C can appear in a hex number which has already started, return
+  // a pointer to a character later in the token or just past
+  // it. Otherwise, return NULL.
+  inline const char*
+  can_continue_hex(const char* c);
 
   // Return whether C can start a non-hex number.
   static inline bool
   can_start_number(char c);
 
-  // Return whether C can appear in a non-hex number.
-  inline bool
-  can_continue_number(char c)
-  { return Lex::can_start_number(c); }
+  // If C can appear in a decimal number which has already started,
+  // return a pointer to a character later in the token or just past
+  // it. Otherwise, return NULL.
+  inline const char*
+  can_continue_number(const char* c)
+  { return Lex::can_start_number(*c) ? c + 1 : NULL; }
 
   // If C1 C2 C3 form a valid three character operator, return the
   // opcode.  Otherwise return 0.
@@ -299,7 +307,7 @@ class Lex
   // MATCH.  Set *PP to the character following the token.
   inline Token
   gather_token(Token::Classification,
-              bool (Lex::*can_continue_fn)(char),
+              const char* (Lex::*can_continue_fn)(const char*),
               const char* start, const char* match, const char** pp);
 
   // Build a token from a quoted string.
@@ -382,7 +390,10 @@ Lex::can_start_name(char c, char c2)
       return this->mode_ == LINKER_SCRIPT;
 
     case '~':
-      return this->mode_ == LINKER_SCRIPT && can_continue_name(c2);
+      return this->mode_ == LINKER_SCRIPT && can_continue_name(&c2);
+
+    case '*': case '[': 
+      return this->mode_ == VERSION_SCRIPT;
 
     default:
       return false;
@@ -395,10 +406,10 @@ Lex::can_start_name(char c, char c2)
 // script language requires spaces around operators, unless we know
 // that we are parsing an expression.
 
-inline bool
-Lex::can_continue_name(char c)
+inline const char*
+Lex::can_continue_name(const char* c)
 {
-  switch (c)
+  switch (*c)
     {
     case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
     case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
@@ -413,16 +424,38 @@ Lex::can_continue_name(char c)
     case '_': case '.': case '$':
     case '0': case '1': case '2': case '3': case '4':
     case '5': case '6': case '7': case '8': case '9':
-      return true;
+      return c + 1;
 
     case '/': case '\\': case '~':
-    case '=': case '+': case '-':
-    case ':': case '[': case ']':
-    case ',': case '?': case '*':
-      return this->mode_ == LINKER_SCRIPT;
+    case '=': case '+':
+    case ',': case '?': 
+      if (this->mode_ == LINKER_SCRIPT)
+        return c + 1;
+      return NULL;
+
+    case '[': case ']': case '*': case '-':
+      if (this->mode_ == LINKER_SCRIPT || this->mode_ == VERSION_SCRIPT)
+        return c + 1;
+      return NULL;
+
+    case '^':
+      if (this->mode_ == VERSION_SCRIPT)
+        return c + 1;
+      return NULL;
+
+    case ':':
+      if (this->mode_ == LINKER_SCRIPT)
+        return c + 1;
+      else if (this->mode_ == VERSION_SCRIPT && (c[1] == ':'))
+        {
+          // A name can have '::' in it, as that's a c++ namespace
+          // separator. But a single colon is not part of a name.
+          return c + 2;
+        }
+      return NULL;
 
     default:
-      return false;
+      return NULL;
     }
 }
 
@@ -439,25 +472,25 @@ inline bool
 Lex::can_start_hex(char c1, char c2, char c3)
 {
   if (c1 == '0' && (c2 == 'x' || c2 == 'X'))
-    return this->can_continue_hex(c3);
+    return this->can_continue_hex(&c3);
   return false;
 }
 
 // Return whether C can appear in a hex number.
 
-inline bool
-Lex::can_continue_hex(char c)
+inline const char*
+Lex::can_continue_hex(const char* c)
 {
-  switch (c)
+  switch (*c)
     {
     case '0': case '1': case '2': case '3': case '4':
     case '5': case '6': case '7': case '8': case '9':
     case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
-      return true;
+      return c + 1;
 
     default:
-      return false;
+      return NULL;
     }
 }
 
@@ -652,13 +685,14 @@ Lex::skip_line_comment(const char** pp)
 
 inline Token
 Lex::gather_token(Token::Classification classification,
-                 bool (Lex::*can_continue_fn)(char),
+                 const char* (Lex::*can_continue_fn)(const char*),
                  const char* start,
                  const char* match,
                  const char **pp)
 {
-  while ((this->*can_continue_fn)(*match))
-    ++match;
+  const char* new_match = NULL;
+  while ((new_match = (this->*can_continue_fn)(match)))
+    match = new_match;
   *pp = match;
   return this->make_token(classification, start, match - start, start);
 }
@@ -941,8 +975,13 @@ class Parser_closure
     : filename_(filename), posdep_options_(posdep_options),
       in_group_(in_group), is_in_sysroot_(is_in_sysroot),
       command_line_(command_line), script_options_(script_options),
+      version_script_info_(script_options->version_script_info()),
       lex_(lex), lineno_(0), charpos_(0), lex_mode_stack_(), inputs_(NULL)
-  { }
+  { 
+    // We start out processing C symbols in the default lex mode.
+    language_stack_.push_back("");
+    lex_mode_stack_.push_back(lex->mode());
+  }
 
   // Return the file name.
   const char*
@@ -978,6 +1017,11 @@ class Parser_closure
   script_options()
   { return this->script_options_; }
 
+  // Return the object in which version script information should be stored.
+  Version_script_info*
+  version_script()
+  { return this->version_script_info_; }
+
   // Return the next token, and advance.
   const Token*
   next_token()
@@ -1005,6 +1049,11 @@ class Parser_closure
     this->lex_mode_stack_.pop_back();
   }
 
+  // Return the current lexer mode.
+  Lex::Mode
+  lex_mode() const
+  { return this->lex_mode_stack_.back(); }
+
   // Return the line number of the last token.
   int
   lineno() const
@@ -1030,6 +1079,23 @@ class Parser_closure
   saw_inputs() const
   { return this->inputs_ != NULL && !this->inputs_->empty(); }
 
+  // Return the current language being processed in a version script
+  // (eg, "C++").  The empty string represents unmangled C names.
+  const std::string&
+  get_current_language() const
+  { return this->language_stack_.back(); }
+
+  // Push a language onto the stack when entering an extern block.
+  void push_language(const std::string& lang)
+  { this->language_stack_.push_back(lang); }
+
+  // Pop a language off of the stack when exiting an extern block.
+  void pop_language()
+  {
+    gold_assert(!this->language_stack_.empty());
+    this->language_stack_.pop_back();
+  }
+
  private:
   // The name of the file we are reading.
   const char* filename_;
@@ -1043,6 +1109,8 @@ class Parser_closure
   Command_line* command_line_;
   // Options which may be set from any linker script.
   Script_options* script_options_;
+  // Information parsed from a version script.
+  Version_script_info* version_script_info_;
   // The lexer.
   Lex* lex_;
   // The line number of the last token returned by next_token.
@@ -1051,6 +1119,9 @@ class Parser_closure
   int charpos_;
   // A stack of lexer modes.
   std::vector<Lex::Mode> lex_mode_stack_;
+  // A stack of which extern/language block we're inside. Can be C++,
+  // java, or empty for C.
+  std::vector<std::string> language_stack_;
   // New input files found to add to the link.
   Input_arguments* inputs_;
 };
@@ -1119,11 +1190,13 @@ read_input_script(Workqueue* workqueue, const General_options& options,
   return true;
 }
 
-// FILENAME was found as an argument to --script (-T).
-// Read it as a script, and execute its contents immediately.
+// Helper function for read_version_script() and
+// read_commandline_script().  Processes the given file in the mode
+// indicated by first_token and lex_mode.
 
-bool
-read_commandline_script(const char* filename, Command_line* cmdline)
+static bool
+read_script_file(const char* filename, Command_line* cmdline,
+                 int first_token, Lex::Mode lex_mode)
 {
   // TODO: if filename is a relative filename, search for it manually
   // using "." + cmdline->options()->search_path() -- not dirsearch.
@@ -1143,7 +1216,8 @@ read_commandline_script(const char* filename, Command_line* cmdline)
   std::string input_string;
   Lex::read_file(&input_file, &input_string);
 
-  Lex lex(input_string.c_str(), input_string.length(), PARSING_LINKER_SCRIPT);
+  Lex lex(input_string.c_str(), input_string.length(), first_token);
+  lex.set_mode(lex_mode);
 
   Parser_closure closure(filename,
                         cmdline->position_dependent_options(),
@@ -1165,6 +1239,27 @@ read_commandline_script(const char* filename, Command_line* cmdline)
   return true;
 }
 
+// FILENAME was found as an argument to --script (-T).
+// Read it as a script, and execute its contents immediately.
+
+bool
+read_commandline_script(const char* filename, Command_line* cmdline)
+{
+  return read_script_file(filename, cmdline,
+                          PARSING_LINKER_SCRIPT, Lex::LINKER_SCRIPT);
+}
+
+// FILE was found as an argument to --version-script.  Read it as a
+// version script, and store its contents in
+// cmdline->script_options()->version_script_info().
+
+bool
+read_version_script(const char* filename, Command_line* cmdline)
+{
+  return read_script_file(filename, cmdline,
+                          PARSING_VERSION_SCRIPT, Lex::VERSION_SCRIPT);
+}
+
 // Implement the --defsym option on the command line.  Return true if
 // all is well.
 
@@ -1189,7 +1284,8 @@ Script_options::define_symbol(const char* definition)
 }
 
 // Manage mapping from keywords to the codes expected by the bison
-// parser.
+// parser.  We construct one global object for each lex mode with
+// keywords.
 
 class Keyword_to_parsecode
 {
@@ -1203,25 +1299,27 @@ class Keyword_to_parsecode
     int parsecode;
   };
 
+  Keyword_to_parsecode(const Keyword_parsecode* keywords,
+                       int keyword_count)
+      : keyword_parsecodes_(keywords), keyword_count_(keyword_count)
+  { }
+
   // Return the parsecode corresponding KEYWORD, or 0 if it is not a
   // keyword.
-  static int
-  keyword_to_parsecode(const char* keyword, size_t len);
+  int
+  keyword_to_parsecode(const char* keyword, size_t len) const;
 
  private:
-  // The array of all keywords.
-  static const Keyword_parsecode keyword_parsecodes_[];
-
-  // The number of keywords.
-  static const int keyword_count;
+  const Keyword_parsecode* keyword_parsecodes_;
+  const int keyword_count_;
 };
 
 // Mapping from keyword string to keyword parsecode.  This array must
 // be kept in sorted order.  Parsecodes are looked up using bsearch.
 // This array must correspond to the list of parsecodes in yyscript.y.
 
-const Keyword_to_parsecode::Keyword_parsecode
-Keyword_to_parsecode::keyword_parsecodes_[] =
+static const Keyword_to_parsecode::Keyword_parsecode
+script_keyword_parsecodes[] =
 {
   { "ABSOLUTE", ABSOLUTE },
   { "ADDR", ADDR },
@@ -1303,9 +1401,23 @@ Keyword_to_parsecode::keyword_parsecodes_[] =
   { "sizeof_headers", SIZEOF_HEADERS },
 };
 
-const int Keyword_to_parsecode::keyword_count =
-  (sizeof(Keyword_to_parsecode::keyword_parsecodes_)
-   / sizeof(Keyword_to_parsecode::keyword_parsecodes_[0]));
+static const Keyword_to_parsecode
+script_keywords(&script_keyword_parsecodes[0],
+                (sizeof(script_keyword_parsecodes)
+                 / sizeof(script_keyword_parsecodes[0])));
+
+static const Keyword_to_parsecode::Keyword_parsecode
+version_script_keyword_parsecodes[] =
+{
+  { "extern", EXTERN },
+  { "global", GLOBAL },
+  { "local", LOCAL },
+};
+
+static const Keyword_to_parsecode
+version_script_keywords(&version_script_keyword_parsecodes[0],
+                        (sizeof(version_script_keyword_parsecodes)
+                         / sizeof(version_script_keyword_parsecodes[0])));
 
 // Comparison function passed to bsearch.
 
@@ -1335,16 +1447,17 @@ ktt_compare(const void* keyv, const void* kttv)
 } // End extern "C".
 
 int
-Keyword_to_parsecode::keyword_to_parsecode(const char* keyword, size_t len)
+Keyword_to_parsecode::keyword_to_parsecode(const char* keyword,
+                                           size_t len) const
 {
   Ktt_key key;
   key.str = keyword;
   key.len = len;
   void* kttv = bsearch(&key,
-                      Keyword_to_parsecode::keyword_parsecodes_,
-                      Keyword_to_parsecode::keyword_count,
-                      sizeof(Keyword_to_parsecode::keyword_parsecodes_[0]),
-                      ktt_compare);
+                       this->keyword_parsecodes_,
+                       this->keyword_count_,
+                       sizeof(this->keyword_parsecodes_[0]),
+                       ktt_compare);
   if (kttv == NULL)
     return 0;
   Keyword_parsecode* ktt = static_cast<Keyword_parsecode*>(kttv);
@@ -1383,7 +1496,18 @@ yylex(YYSTYPE* lvalp, void* closurev)
        // This is either a keyword or a STRING.
        size_t len;
        const char* str = token->string_value(&len);
-       int parsecode = Keyword_to_parsecode::keyword_to_parsecode(str, len);
+       int parsecode = 0;
+        switch (closure->lex_mode())
+          {
+          case Lex::LINKER_SCRIPT:
+            parsecode = script_keywords.keyword_to_parsecode(str, len);
+            break;
+          case Lex::VERSION_SCRIPT:
+            parsecode = version_script_keywords.keyword_to_parsecode(str, len);
+            break;
+          default:
+            break;
+          }
        if (parsecode != 0)
          return parsecode;
        lvalp->string.value = str;
@@ -1561,6 +1685,16 @@ script_push_lex_into_expression_mode(void* closurev)
   closure->push_lex_mode(Lex::EXPRESSION);
 }
 
+/* Called by the bison parser to push the lexer into version
+   mode.  */
+
+extern void
+script_push_lex_into_version_mode(void* closurev)
+{
+  Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+  closure->push_lex_mode(Lex::VERSION_SCRIPT);
+}
+
 /* Called by the bison parser to pop the lexer mode.  */
 
 extern void
@@ -1569,3 +1703,234 @@ script_pop_lex_mode(void* closurev)
   Parser_closure* closure = static_cast<Parser_closure*>(closurev);
   closure->pop_lex_mode();
 }
+
+// The following structs are used within the VersionInfo class as well
+// as in the bison helper functions.  They store the information
+// parsed from the version script.
+
+// A single version expression.
+// For example, pattern="std::map*" and language="C++".
+// pattern and language should be from the stringpool
+struct Version_expression {
+  Version_expression(const std::string& pattern,
+                     const std::string& language)
+      : pattern(pattern), language(language) {}
+
+  std::string pattern;
+  std::string language;
+};
+
+
+// A list of expressions.
+struct Version_expression_list {
+  std::vector<struct Version_expression> expressions;
+};
+
+
+// A list of which versions upon which another version depends.
+// Strings should be from the Stringpool.
+struct Version_dependency_list {
+  std::vector<std::string> dependencies;
+};
+
+
+// The total definition of a version.  It includes the tag for the
+// version, its global and local expressions, and any dependencies.
+struct Version_tree {
+  Version_tree()
+      : tag(), global(NULL), local(NULL), dependencies(NULL) {}
+
+  std::string tag;
+  const struct Version_expression_list* global;
+  const struct Version_expression_list* local;
+  const struct Version_dependency_list* dependencies;
+};
+
+Version_script_info::~Version_script_info()
+{
+  for (size_t k = 0; k < dependency_lists_.size(); ++k)
+    delete dependency_lists_[k];
+  for (size_t k = 0; k < version_trees_.size(); ++k)
+    delete version_trees_[k];
+  for (size_t k = 0; k < expression_lists_.size(); ++k)
+    delete expression_lists_[k];
+}
+
+std::vector<std::string>
+Version_script_info::get_versions() const
+{
+  std::vector<std::string> ret;
+  for (size_t j = 0; j < version_trees_.size(); ++j)
+    ret.push_back(version_trees_[j]->tag);
+  return ret;
+}
+
+std::vector<std::string>
+Version_script_info::get_dependencies(const char* version) const
+{
+  std::vector<std::string> ret;
+  for (size_t j = 0; j < version_trees_.size(); ++j)
+    if (version_trees_[j]->tag == version)
+      {
+        const struct Version_dependency_list* deps =
+          version_trees_[j]->dependencies;
+        if (deps != NULL)
+          for (size_t k = 0; k < deps->dependencies.size(); ++k)
+            ret.push_back(deps->dependencies[k]);
+        return ret;
+      }
+  return ret;
+}
+
+const std::string&
+Version_script_info::get_symbol_version_helper(const char* symbol_name,
+                                               bool check_global) const
+{
+  for (size_t j = 0; j < version_trees_.size(); ++j)
+    {
+      // Is it a global symbol for this version?
+      const Version_expression_list* exp =
+          check_global ? version_trees_[j]->global : version_trees_[j]->local;
+      if (exp != NULL)
+        for (size_t k = 0; k < exp->expressions.size(); ++k)
+          {
+            const char* name_to_match = symbol_name;
+            char* demangled_name = NULL;
+            if (exp->expressions[k].language == "C++")
+              {
+                demangled_name = cplus_demangle(symbol_name,
+                                                DMGL_ANSI | DMGL_PARAMS);
+                // This isn't a C++ symbol.
+                if (demangled_name == NULL)
+                  continue;
+                name_to_match = demangled_name;
+              }
+            else if (exp->expressions[k].language == "Java")
+              {
+                demangled_name = cplus_demangle(symbol_name,
+                                                (DMGL_ANSI | DMGL_PARAMS
+                                                | DMGL_JAVA));
+                // This isn't a Java symbol.
+                if (demangled_name == NULL)
+                  continue;
+                name_to_match = demangled_name;
+              }
+            bool matched = fnmatch(exp->expressions[k].pattern.c_str(),
+                                   name_to_match, FNM_NOESCAPE) == 0;
+            if (demangled_name != NULL)
+              free(demangled_name);
+            if (matched)
+              return version_trees_[j]->tag;
+          }
+    }
+  static const std::string empty = "";
+  return empty;
+}
+
+struct Version_dependency_list*
+Version_script_info::allocate_dependency_list()
+{
+  dependency_lists_.push_back(new Version_dependency_list);
+  return dependency_lists_.back();
+}
+
+struct Version_expression_list*
+Version_script_info::allocate_expression_list()
+{
+  expression_lists_.push_back(new Version_expression_list);
+  return expression_lists_.back();
+}
+
+struct Version_tree*
+Version_script_info::allocate_version_tree()
+{
+  version_trees_.push_back(new Version_tree);
+  return version_trees_.back();
+}
+
+// Register an entire version node. For example:
+//
+// GLIBC_2.1 {
+//   global: foo;
+// } GLIBC_2.0;
+//
+// - tag is "GLIBC_2.1"
+// - tree contains the information "global: foo"
+// - deps contains "GLIBC_2.0"
+
+extern "C" void
+script_register_vers_node(void*,
+                         const char* tag,
+                         int taglen,
+                         struct Version_tree *tree,
+                         struct Version_dependency_list *deps)
+{
+  gold_assert(tree != NULL);
+  gold_assert(tag != NULL);
+  tree->dependencies = deps;
+  tree->tag = std::string(tag, taglen);
+}
+
+// Add a dependencies to the list of existing dependencies, if any,
+// and return the expanded list.
+
+extern "C" struct Version_dependency_list *
+script_add_vers_depend(void* closurev,
+                      struct Version_dependency_list *all_deps,
+                      const char *depend_to_add, int deplen)
+{
+  Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+  if (all_deps == NULL)
+    all_deps = closure->version_script()->allocate_dependency_list();
+  all_deps->dependencies.push_back(std::string(depend_to_add, deplen));
+  return all_deps;
+}
+
+// Add a pattern expression to an existing list of expressions, if any.
+// TODO: In the old linker, the last argument used to be a bool, but I
+// don't know what it meant.
+
+extern "C" struct Version_expression_list *
+script_new_vers_pattern(void* closurev,
+                       struct Version_expression_list *expressions,
+                       const char *pattern, int patlen)
+{
+  Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+  if (expressions == NULL)
+    expressions = closure->version_script()->allocate_expression_list();
+  expressions->expressions.push_back(
+      Version_expression(std::string(pattern, patlen),
+                         closure->get_current_language()));
+  return expressions;
+}
+
+// Combine the global and local expressions into a a Version_tree.
+
+extern "C" struct Version_tree *
+script_new_vers_node(void* closurev,
+                    struct Version_expression_list *global,
+                    struct Version_expression_list *local)
+{
+  Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+  Version_tree* tree = closure->version_script()->allocate_version_tree();
+  tree->global = global;
+  tree->local = local;
+  return tree;
+}
+
+// Handle a transition in language, such as at the 
+// start or end of 'extern "C++"'
+
+extern "C" void
+version_script_push_lang(void* closurev, const char* lang, int langlen)
+{
+  Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+  closure->push_language(std::string(lang, langlen));
+}
+
+extern "C" void
+version_script_pop_lang(void* closurev)
+{
+  Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+  closure->pop_language();
+}
index 0dfa4bb..69906cb 100644 (file)
 
 #include <vector>
 
+struct Version_dependency_list;
+struct Version_expression_list;
+struct Version_tree;
+
 namespace gold
 {
 
@@ -80,6 +84,69 @@ class Expression
   Expression& operator=(const Expression&);
 };
 
+
+// Version_script_info stores information parsed from the version
+// script, either provided by --version-script or as part of a linker
+// script.  A single Version_script_info object per target is owned by
+// Script_options.
+
+class Version_script_info {
+ public:
+  ~Version_script_info();
+
+  // Return whether any version were defined in the version script.
+  bool
+  empty() const
+  { return this->version_trees_.empty(); }
+
+  // Return the version associated with the given symbol name.
+  // Strings are allocated out of the stringpool given in the
+  // constructor.  Strings are allocated out of the stringpool given
+  // in the constructor.
+  const std::string&
+  get_symbol_version(const char* symbol) const
+  { return get_symbol_version_helper(symbol, true); }
+
+  // Return whether this symbol matches the local: section of a
+  // version script (it doesn't matter which).  This test is only
+  // valid if get_symbol_version() returns the empty string, as we
+  // don't test that here.
+  bool
+  symbol_is_local(const char* symbol) const
+  { return !get_symbol_version_helper(symbol, false).empty(); }
+
+  // Return the names of versions defined in the version script.
+  // Strings are allocated out of the stringpool given in the
+  // constructor.
+  std::vector<std::string>
+  get_versions() const;
+
+  // Return the list of dependencies for this version.
+  std::vector<std::string>
+  get_dependencies(const char* version) const;
+
+  // The following functions should only be used by the bison helper
+  // functions.  They allocate new structs whose memory belongs to
+  // Version_script_info.  The bison functions copy the information
+  // from the version script into these structs.
+  struct Version_dependency_list*
+  allocate_dependency_list();
+
+  struct Version_expression_list*
+  allocate_expression_list();
+
+  struct Version_tree*
+  allocate_version_tree();
+
+ private:
+  const std::string& get_symbol_version_helper(const char* symbol,
+                                               bool check_global) const;
+
+  std::vector<struct Version_dependency_list*> dependency_lists_;
+  std::vector<struct Version_expression_list*> expression_lists_;
+  std::vector<struct Version_tree*> version_trees_;
+};
+
 // We can read a linker script in two different contexts: when
 // initially parsing the command line, and when we find an input file
 // which is actually a linker script.  Also some of the data which can
@@ -127,6 +194,12 @@ class Script_options
   void
   finalize_symbols(Symbol_table*, const Layout*);
 
+  // Version information parsed from a version script.  Everything
+  // else has a pointer to this object.
+  Version_script_info*
+  version_script_info()
+  { return &version_script_info_; }
+
  private:
   // We keep a list of symbol assignments.
   struct Symbol_assignment
@@ -160,6 +233,8 @@ class Script_options
   std::string entry_;
   // Symbols to set.
   Symbol_assignments symbol_assignments_;
+  // Version information parsed from a version script.
+  Version_script_info version_script_info_;
 };
 
 // FILE was found as an argument on the command line, but was not
@@ -181,6 +256,14 @@ read_input_script(Workqueue*, const General_options&, Symbol_table*, Layout*,
 bool
 read_commandline_script(const char* filename, Command_line*);
 
+// FILE was found as an argument to --version-script.  Read it as a
+// version script, and store its contents in
+// cmdline->script_options()->version_script_info().
+
+bool
+read_version_script(const char* filename, Command_line* cmdline);
+
+
 } // End namespace gold.
 
 #endif // !defined(GOLD_SCRIPT_H)
index ae8e751..e49178a 100644 (file)
@@ -295,9 +295,10 @@ Symbol::final_value_is_known() const
 
 // Class Symbol_table.
 
-Symbol_table::Symbol_table(unsigned int count)
+Symbol_table::Symbol_table(unsigned int count,
+                           const Version_script_info& version_script)
   : saw_undefined_(0), offset_(0), table_(count), namepool_(),
-    forwarders_(), commons_(), warnings_()
+    forwarders_(), commons_(), warnings_(), version_script_(version_script)
 {
   namepool_.reserve(count);
 }
@@ -571,6 +572,7 @@ Symbol_table::add_from_object(Object* object,
   if (!was_common && ret->is_common())
     this->commons_.push_back(ret);
 
+  ret->set_is_default(def);
   return ret;
 }
 
@@ -626,6 +628,31 @@ Symbol_table::add_from_relobj(
       // name from the version name.  If there are two '@' characters,
       // this is the default version.
       const char* ver = strchr(name, '@');
+      int namelen = 0;
+      bool def = false;
+
+      if (ver != NULL)
+        {
+          // The symbol name is of the form foo@VERSION or foo@@VERSION
+          namelen = ver - name;
+          ++ver;
+         if (*ver == '@')
+           {
+             def = true;
+             ++ver;
+           }
+        }
+      else if (!version_script_.empty())
+        {
+          // The symbol name did not have a version, but
+          // the version script may assign a version anyway.
+          namelen = strlen(name);
+          def = true;
+          const std::string& version =
+              version_script_.get_symbol_version(name);
+          if (!version.empty())
+            ver = version.c_str();
+        }
 
       Sized_symbol<size>* res;
       if (ver == NULL)
@@ -638,17 +665,8 @@ Symbol_table::add_from_relobj(
       else
        {
          Stringpool::Key name_key;
-         name = this->namepool_.add_with_length(name, ver - name, true,
+         name = this->namepool_.add_with_length(name, namelen, true,
                                                 &name_key);
-
-         bool def = false;
-         ++ver;
-         if (*ver == '@')
-           {
-             def = true;
-             ++ver;
-           }
-
          Stringpool::Key ver_key;
          ver = this->namepool_.add(ver, true, &ver_key);
 
@@ -1238,7 +1256,7 @@ Symbol_table::do_define_as_constant(
   if (sym == NULL)
     return NULL;
 
-  gold_assert(version == NULL || oldsym != NULL);
+  gold_assert(version == NULL || version == name || oldsym != NULL);
   sym->init(name, value, symsize, type, binding, visibility, nonvis);
 
   if (oldsym == NULL)
@@ -1392,8 +1410,8 @@ Symbol_table::set_dynsym_indexes(const Target* target,
          dynpool->add(sym->name(), false, NULL);
 
          // Record any version information.
-         if (sym->version() != NULL)
-           versions->record_version(this, dynpool, sym);
+          if (sym->version() != NULL)
+            versions->record_version(this, dynpool, sym);
        }
     }
 
index 59cda2e..43a228d 100644 (file)
@@ -46,6 +46,7 @@ class Dynobj;
 template<int size, bool big_endian>
 class Sized_dynobj;
 class Versions;
+class Version_script_info;
 class Input_objects;
 class Output_data;
 class Output_section;
@@ -111,6 +112,21 @@ class Symbol
   version() const
   { return this->version_; }
 
+  // Return whether this version is the default for this symbol name
+  // (eg, "foo@@V2" is a default version; "foo@V1" is not).  Only
+  // meaningful for versioned symbols.
+  bool
+  is_default() const
+  {
+    gold_assert(this->version_ != NULL);
+    return this->is_def_;
+  }
+
+  // Set whether this version is the default for this symbol name.
+  void
+  set_is_default(bool def)
+  { this->is_def_ = def; }
+
   // Return the symbol source.
   Source
   source() const
@@ -953,7 +969,7 @@ class Symbol_table
   // COUNT is an estimate of how many symbosl will be inserted in the
   // symbol table.  It's ok to put 0 if you don't know; a correct
   // guess will just save some CPU by reducing hashtable resizes.
-  Symbol_table(unsigned int count);
+  Symbol_table(unsigned int count, const Version_script_info& version_script);
 
   ~Symbol_table();
 
@@ -1115,6 +1131,11 @@ class Symbol_table
   void
   print_stats() const;
 
+  // Return the version script information.
+  const Version_script_info&
+  version_script() const
+  { return version_script_; }
+
  private:
   Symbol_table(const Symbol_table&);
   Symbol_table& operator=(const Symbol_table&);
@@ -1347,6 +1368,8 @@ class Symbol_table
   // definition.  This maps symbols with COPY relocs to the dynamic
   // object where they were defined.
   Copied_symbol_dynobjs copied_symbol_dynobjs_;
+  // Information parsed from the version script, if any.
+  const Version_script_info& version_script_;
 };
 
 // We inline get_sized_symbol for efficiency.
index 46e949c..5820038 100644 (file)
@@ -490,10 +490,10 @@ ver_test_LDFLAGS = -Bgcctestdir/ -Wl,-R,.
 ver_test_LDADD = ver_test_1.so ver_test_2.so ver_test_4.so
 ver_test_1.so: ver_test_1.o ver_test_2.so ver_test_3.o ver_test_4.so gcctestdir/ld
        $(CXXLINK) -Bgcctestdir/ -shared ver_test_1.o ver_test_2.so ver_test_3.o ver_test_4.so
-ver_test_2.so: ver_test_2.o $(srcdir)/ver_test_2.script ver_test_4.so
-       $(CXXLINK) -shared -Wl,--version-script,$(srcdir)/ver_test_2.script ver_test_2.o ver_test_4.so
-ver_test_4.so: ver_test_4.o $(srcdir)/ver_test_4.script
-       $(CXXLINK) -shared -Wl,--version-script,$(srcdir)/ver_test_4.script ver_test_4.o
+ver_test_2.so: ver_test_2.o $(srcdir)/ver_test_2.script ver_test_4.so gcctestdir/ld
+       $(CXXLINK) -Bgcctestdir/ -shared -Wl,--version-script,$(srcdir)/ver_test_2.script ver_test_2.o ver_test_4.so
+ver_test_4.so: ver_test_4.o $(srcdir)/ver_test_4.script gcctestdir/ld
+       $(CXXLINK) -Bgcctestdir/ -shared -Wl,--version-script,$(srcdir)/ver_test_4.script ver_test_4.o
 ver_test_1.o: ver_test_1.cc
        $(CXXCOMPILE) -c -fpic -o $@ $<
 ver_test_2.o: ver_test_2.cc
@@ -508,5 +508,15 @@ script_test_1_SOURCES = script_test_1.cc
 script_test_1_DEPENDENCIES = gcctestdir/ld script_test_1.t
 script_test_1_LDFLAGS = -Bgcctestdir/ -Wl,-R,. -T $(srcdir)/script_test_1.t
 
+if OBJDUMP_AND_CPPFILT
+check_SCRIPTS += ver_matching_test.sh
+check_DATA += ver_matching_test.stdout
+MOSTLYCLEANFILES += ver_matching_test.stdout
+ver_matching_def.so: ver_matching_def.cc gcctestdir/ld
+       $(CXXLINK) -O0 -Bgcctestdir/ -shared $(srcdir)/ver_matching_def.cc -Wl,--version-script=$(srcdir)/version_script.map
+ver_matching_test.stdout: ver_matching_def.so
+       objdump -T ver_matching_def.so | c++filt > ver_matching_test.stdout
+endif OBJDUMP_AND_CPPFILT
+
 endif GCC
 endif NATIVE_LINKER
index f61e4e5..8f51d28 100644 (file)
@@ -179,6 +179,9 @@ check_PROGRAMS = object_unittest$(EXEEXT) $(am__EXEEXT_1) \
 @NATIVE_LINKER_FALSE@  ../libgold.a ../../libiberty/libiberty.a \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1)
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_11 = ver_matching_test.sh
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_12 = ver_matching_test.stdout
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_13 = ver_matching_test.stdout
 subdir = testsuite
 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
@@ -633,6 +636,8 @@ MSGMERGE = @MSGMERGE@
 NATIVE_LINKER_FALSE = @NATIVE_LINKER_FALSE@
 NATIVE_LINKER_TRUE = @NATIVE_LINKER_TRUE@
 NO_WERROR = @NO_WERROR@
+OBJDUMP_AND_CPPFILT_FALSE = @OBJDUMP_AND_CPPFILT_FALSE@
+OBJDUMP_AND_CPPFILT_TRUE = @OBJDUMP_AND_CPPFILT_TRUE@
 OBJEXT = @OBJEXT@
 PACKAGE = @PACKAGE@
 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
@@ -722,13 +727,13 @@ INCLUDES = \
 # .o's), but not all of them (such as .so's and .err files).  We
 # improve on that here.  automake-1.9 info docs say "mostlyclean" is
 # the right choice for files 'make' builds that people rebuild.
-MOSTLYCLEANFILES = *.so $(am__append_9)
+MOSTLYCLEANFILES = *.so $(am__append_9) $(am__append_13)
 
 # We will add to these later, for each individual test.  Note
 # that we add each test under check_SCRIPTS or check_PROGRAMS;
 # the TESTS variable is automatically populated from these.
-check_SCRIPTS = $(am__append_7)
-check_DATA = $(am__append_8)
+check_SCRIPTS = $(am__append_7) $(am__append_11)
+check_DATA = $(am__append_8) $(am__append_12)
 TESTS = $(check_SCRIPTS) $(check_PROGRAMS)
 
 # ---------------------------------------------------------------------
@@ -1561,10 +1566,10 @@ uninstall-am: uninstall-info-am
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ test -s $@
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_1.so: ver_test_1.o ver_test_2.so ver_test_3.o ver_test_4.so gcctestdir/ld
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared ver_test_1.o ver_test_2.so ver_test_3.o ver_test_4.so
-@GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_2.so: ver_test_2.o $(srcdir)/ver_test_2.script ver_test_4.so
-@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -shared -Wl,--version-script,$(srcdir)/ver_test_2.script ver_test_2.o ver_test_4.so
-@GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_4.so: ver_test_4.o $(srcdir)/ver_test_4.script
-@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -shared -Wl,--version-script,$(srcdir)/ver_test_4.script ver_test_4.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_2.so: ver_test_2.o $(srcdir)/ver_test_2.script ver_test_4.so gcctestdir/ld
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared -Wl,--version-script,$(srcdir)/ver_test_2.script ver_test_2.o ver_test_4.so
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_4.so: ver_test_4.o $(srcdir)/ver_test_4.script gcctestdir/ld
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared -Wl,--version-script,$(srcdir)/ver_test_4.script ver_test_4.o
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_1.o: ver_test_1.cc
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -c -fpic -o $@ $<
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_2.o: ver_test_2.cc
@@ -1573,6 +1578,10 @@ uninstall-am: uninstall-info-am
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -c -fpic -o $@ $<
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_4.o: ver_test_4.cc
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -c -fpic -o $@ $<
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@ver_matching_def.so: ver_matching_def.cc gcctestdir/ld
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@       $(CXXLINK) -O0 -Bgcctestdir/ -shared $(srcdir)/ver_matching_def.cc -Wl,--version-script=$(srcdir)/version_script.map
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@ver_matching_test.stdout: ver_matching_def.so
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@       objdump -T ver_matching_def.so | c++filt > ver_matching_test.stdout
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
 .NOEXPORT:
diff --git a/gold/testsuite/ver_matching_def.cc b/gold/testsuite/ver_matching_def.cc
new file mode 100644 (file)
index 0000000..71d8d32
--- /dev/null
@@ -0,0 +1,69 @@
+// ver_matching_def.cc - test matching rules in version_script.map
+
+// Copyright 2007 Free Software Foundation, Inc.
+// Written by Cary Coutant <ccoutant@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.
+
+extern "C" {
+void foo() {} // V1
+void foo1() {} // local
+};
+
+void bar() {} // V1
+void bar1() {} // global
+
+extern "C" {
+void bar2() {}  // V1
+};
+
+namespace myns {
+void blah() {} // V1
+void bip() {} // V1
+
+class Stuff {
+ public:
+  Stuff() {} // V1
+};
+}
+
+class Biz {
+ public:
+  Biz() {} // global
+};
+
+namespace otherns {
+Biz biz; // global
+myns::Stuff stuff;  // V2
+};
+
+extern "C" {
+void blaza() {}  // V1
+void blaza1() {}  // V1
+
+void original_blaza2() {} // V2
+__asm__(".symver original_blaza2,blaza2@@V2");  // overrides script
+
+void bla() {} // global
+void blaz() {} // V2
+void blazb() {} // V2
+
+int globaoeufxstuff = 0;  // V1
+int globaoeufostuff = 0; // global
+float sizeof_headers = 50.0;  // V1
+};
diff --git a/gold/testsuite/ver_matching_test.sh b/gold/testsuite/ver_matching_test.sh
new file mode 100755 (executable)
index 0000000..48dd9a5
--- /dev/null
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+# ver_matching_test.sh -- a test case for version script matching
+
+# Copyright 2008 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 goes with ver_matching_def.cc, a C++ source file
+# constructed with several symbols mapped via version_script.map.  We
+# run readelf on the resulting shared object and check that each
+# symbol has the correct version.
+
+check()
+{
+    if ! grep -q "$2" "$1"
+    then
+       echo "Did not find expected symbol in $1:"
+       echo "   $2"
+       echo ""
+       echo "Actual output below:"
+       cat "$1"
+       exit 1
+    fi
+}
+
+check_missing()
+{
+    if grep -q "$2" "$1"
+    then
+       echo "Found unexpected symbol in $1:"
+       echo "   $2"
+       echo ""
+       echo "Actual output below:"
+       cat "$1"
+       exit 1
+    fi
+}
+
+check ver_matching_test.stdout "V1  *sizeof_headers$"
+check ver_matching_test.stdout "Base  *globaoeufostuff$"
+check ver_matching_test.stdout "V1  *globaoeufxstuff$"
+check ver_matching_test.stdout "V2  *otherns::stuff$"
+check ver_matching_test.stdout "Base  *otherns::biz$"
+check ver_matching_test.stdout "V1  *foo$"
+check ver_matching_test.stdout "V1  *bar()$"
+check ver_matching_test.stdout "Base  *bar1()$"
+check ver_matching_test.stdout "V1  *bar2$"
+check ver_matching_test.stdout "V1  *myns::blah()$"
+check ver_matching_test.stdout "V1  *myns::bip()$"
+check ver_matching_test.stdout "V1  *myns::Stuff::Stuff()$"
+check ver_matching_test.stdout "Base  *Biz::Biz()$"
+check ver_matching_test.stdout "V1  *blaza1$"
+check ver_matching_test.stdout "V2  *blaza2$"
+check ver_matching_test.stdout "V1  *blaza$"
+check ver_matching_test.stdout "Base  *bla$"
+check ver_matching_test.stdout "V2  *blaz$"
+check ver_matching_test.stdout "V2  *blazb$"
+
+# TODO: foo1 should be a local symbol and not show up in the .dynsym
+# dump, but we haven't figured out how to suppress it yet.
+# check_missing ver_matching_test.stdout "foo1"
+
+exit 0
diff --git a/gold/testsuite/version_script.map b/gold/testsuite/version_script.map
new file mode 100644 (file)
index 0000000..2a17523
--- /dev/null
@@ -0,0 +1,28 @@
+V1 {
+   global:
+        extern "C++"
+        {
+           "bar()";
+           myns::*;
+        };
+       foo;
+       blaza*;
+        bar*;
+       # Would be a keyword in a linker script.
+       SECTIONS;
+       sizeof_headers;
+       # Crazy globbiness
+       glob*f[^A-Zo]stuff;
+
+   local:
+        *foo*;
+};
+
+V2 {
+   global:
+       extern "C++" {
+           otherns::stuff;
+        };
+        blaz*;
+       foo;
+} V1;
index a1f954c..40acb00 100644 (file)
   uint64_t integer;
   /* An expression.  */
   Expression_ptr expr;
+  // Used for version scripts and within VERSION {}
+  struct Version_dependency_list* deplist;
+  struct Version_expression_list* versyms;
+  struct Version_tree* versnode;
 }
 
 /* Operators, including a precedence table for expressions.  */
 /* Non-terminal types, where needed.  */
 
 %type <expr> parse_exp exp
+%type <versyms> vers_defns
+%type <versnode> vers_tag
+%type <deplist> verdep
 
 %%
 
@@ -202,6 +209,10 @@ file_cmd:
            { script_end_group(closure); }
         | OPTION '(' STRING ')'
            { script_parse_option(closure, $3.value, $3.length); }
+        | VERSIONK '{'
+            { script_push_lex_into_version_mode(closure); }
+          version_script '}'
+            { script_pop_lex_mode(closure); }
        | file_or_sections_cmd
        | ignore_cmd
        ;
@@ -412,8 +423,84 @@ defsym_expr:
            { script_set_symbol(closure, $1.value, $1.length, $3, 0, 0); }
        ;
 
-/* A version script.  Not yet implemented.  */
+/* A version script.  */
 version_script:
+         vers_nodes
+       ;
+
+vers_nodes:
+         vers_node
+       | vers_nodes vers_node
+       ;
+
+vers_node:
+         '{' vers_tag '}' ';'
+           {
+             script_register_vers_node (closure, NULL, 0, $2, NULL);
+           }
+       | STRING '{' vers_tag '}' ';'
+           {
+             script_register_vers_node (closure, $1.value, $1.length, $3,
+                                        NULL);
+           }
+       | STRING '{' vers_tag '}' verdep ';'
+           {
+             script_register_vers_node (closure, $1.value, $1.length, $3, $5);
+           }
+       ;
+
+verdep:
+         STRING
+           {
+             $$ = script_add_vers_depend (closure, NULL, $1.value, $1.length);
+           }
+       | verdep STRING
+           {
+             $$ = script_add_vers_depend (closure, $1, $2.value, $2.length);
+           }
+       ;
+
+vers_tag:
+         /* empty */
+           { $$ = script_new_vers_node (closure, NULL, NULL); }
+       | vers_defns ';'
+           { $$ = script_new_vers_node (closure, $1, NULL); }
+       | GLOBAL ':' vers_defns ';'
+           { $$ = script_new_vers_node (closure, $3, NULL); }
+       | LOCAL ':' vers_defns ';'
+           { $$ = script_new_vers_node (closure, NULL, $3); }
+       | GLOBAL ':' vers_defns ';' LOCAL ':' vers_defns ';'
+           { $$ = script_new_vers_node (closure, $3, $7); }
+       ;
+
+vers_defns:
+         STRING
+           {
+             $$ = script_new_vers_pattern (closure, NULL, $1.value,
+                                           $1.length);
+           }
+       | vers_defns ';' STRING
+           {
+             $$ = script_new_vers_pattern (closure, $1, $3.value, $3.length);
+           }
+        | /* Push STRING on the language stack. */
+          EXTERN STRING '{'
+           { version_script_push_lang(closure, $2.value, $2.length); }
+         vers_defns opt_semicolon '}'
+           {
+             $$ = $5;
+             version_script_pop_lang(closure);
+           }
+        | EXTERN  // "extern" as a symbol name
+           {
+             $$ = script_new_vers_pattern (closure, NULL, "extern",
+                                           sizeof("extern") - 1);
+           }
+       | vers_defns ';' EXTERN
+           {
+             $$ = script_new_vers_pattern (closure, $1, "extern",
+                                           sizeof("extern") - 1);
+           }
        ;
 
 /* Some statements require a terminator, which may be a semicolon or a
@@ -423,6 +510,12 @@ end:
        | ','
        ;
 
+/* An optional semicolon.  */
+opt_semicolon:
+         ';'
+       |  /* empty */
+       ;
+
 /* An optional comma.  */
 opt_comma:
          ','