From Craig Silverstein: Handle quoted strings differently in version
authorIan Lance Taylor <iant@google.com>
Fri, 18 Jan 2008 23:35:09 +0000 (23:35 +0000)
committerIan Lance Taylor <iant@google.com>
Fri, 18 Jan 2008 23:35:09 +0000 (23:35 +0000)
scripts, and handle extern "C++" in the middle of a block.

gold/dwarf_reader.cc
gold/script-c.h
gold/script.cc
gold/testsuite/ver_matching_def.cc
gold/testsuite/ver_matching_test.sh
gold/testsuite/version_script.map
gold/yyscript.y

index bf03ca3..19c1a03 100644 (file)
@@ -125,6 +125,7 @@ Sized_dwarf_line_info<size, big_endian>::Sized_dwarf_line_info(Object* object,
 {
   unsigned int debug_shndx;
   for (debug_shndx = 0; debug_shndx < object->shnum(); ++debug_shndx)
+    // FIXME: do this more efficiently: section_name() isn't super-fast
     if (object->section_name(debug_shndx) == ".debug_line")
       {
         section_size_type buffer_size;
index 275f4a2..da8b558 100644 (file)
@@ -233,7 +233,11 @@ script_add_vers_depend(void* closure,
 extern struct Version_expression_list *
 script_new_vers_pattern(void* closure,
                        struct Version_expression_list *,
-                       const char *, int);
+                       const char *, int, int);
+
+extern struct Version_expression_list *
+script_merge_expressions(struct Version_expression_list *a,
+                         struct Version_expression_list *b);
 
 extern struct Version_tree *
 script_new_vers_node(void* closure,
index 16c0cc0..3822c24 100644 (file)
@@ -1517,7 +1517,7 @@ yylex(YYSTYPE* lvalp, void* closurev)
 
     case Token::TOKEN_QUOTED_STRING:
       lvalp->string.value = token->string_value(&lvalp->string.length);
-      return STRING;
+      return QUOTED_STRING;
 
     case Token::TOKEN_OPERATOR:
       return token->operator_value();
@@ -1713,11 +1713,14 @@ script_pop_lex_mode(void* closurev)
 // 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) {}
+                     const std::string& language,
+                     bool exact_match)
+      : pattern(pattern), language(language), exact_match(exact_match) {}
 
   std::string pattern;
   std::string language;
+  // If false, we use glob() to match pattern.  If true, we use strcmp().
+  bool exact_match;
 };
 
 
@@ -1789,14 +1792,15 @@ Version_script_info::get_symbol_version_helper(const char* symbol_name,
   for (size_t j = 0; j < version_trees_.size(); ++j)
     {
       // Is it a global symbol for this version?
-      const Version_expression_list* exp =
+      const Version_expression_list* explist =
           check_global ? version_trees_[j]->global : version_trees_[j]->local;
-      if (exp != NULL)
-        for (size_t k = 0; k < exp->expressions.size(); ++k)
+      if (explist != NULL)
+        for (size_t k = 0; k < explist->expressions.size(); ++k)
           {
             const char* name_to_match = symbol_name;
+            const struct Version_expression& exp = explist->expressions[k];
             char* demangled_name = NULL;
-            if (exp->expressions[k].language == "C++")
+            if (exp.language == "C++")
               {
                 demangled_name = cplus_demangle(symbol_name,
                                                 DMGL_ANSI | DMGL_PARAMS);
@@ -1805,7 +1809,7 @@ Version_script_info::get_symbol_version_helper(const char* symbol_name,
                   continue;
                 name_to_match = demangled_name;
               }
-            else if (exp->expressions[k].language == "Java")
+            else if (exp.language == "Java")
               {
                 demangled_name = cplus_demangle(symbol_name,
                                                 (DMGL_ANSI | DMGL_PARAMS
@@ -1815,8 +1819,12 @@ Version_script_info::get_symbol_version_helper(const char* symbol_name,
                   continue;
                 name_to_match = demangled_name;
               }
-            bool matched = fnmatch(exp->expressions[k].pattern.c_str(),
-                                   name_to_match, FNM_NOESCAPE) == 0;
+            bool matched;
+            if (exp.exact_match)
+              matched = strcmp(exp.pattern.c_str(), name_to_match) == 0;
+            else
+              matched = fnmatch(exp.pattern.c_str(), name_to_match,
+                                FNM_NOESCAPE) == 0;
             if (demangled_name != NULL)
               free(demangled_name);
             if (matched)
@@ -1893,17 +1901,32 @@ script_add_vers_depend(void* closurev,
 extern "C" struct Version_expression_list *
 script_new_vers_pattern(void* closurev,
                        struct Version_expression_list *expressions,
-                       const char *pattern, int patlen)
+                       const char *pattern, int patlen, int exact_match)
 {
   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()));
+                         closure->get_current_language(),
+                         static_cast<bool>(exact_match)));
   return expressions;
 }
 
+// Attaches b to the end of a, and clears b.  So a = a + b and b = {}.
+
+extern "C" struct Version_expression_list*
+script_merge_expressions(struct Version_expression_list *a,
+                         struct Version_expression_list *b)
+{
+  a->expressions.insert(a->expressions.end(),
+                        b->expressions.begin(), b->expressions.end());
+  // We could delete b and remove it from expressions_lists_, but
+  // that's a lot of work.  This works just as well.
+  b->expressions.clear();
+  return a;
+}
+
 // Combine the global and local expressions into a a Version_tree.
 
 extern "C" struct Version_tree *
@@ -1918,7 +1941,7 @@ script_new_vers_node(void* closurev,
   return tree;
 }
 
-// Handle a transition in language, such as at the 
+// Handle a transition in language, such as at the
 // start or end of 'extern "C++"'
 
 extern "C" void
index 71d8d32..2eba163 100644 (file)
@@ -28,6 +28,10 @@ void foo1() {} // local
 void bar() {} // V1
 void bar1() {} // global
 
+void baz(int*) {}          // V1
+void baz(int*, char) {}    // global
+void baz(char*, int) {}    // global
+
 extern "C" {
 void bar2() {}  // V1
 };
index 48dd9a5..2ecdd98 100755 (executable)
@@ -73,6 +73,12 @@ check ver_matching_test.stdout "Base  *bla$"
 check ver_matching_test.stdout "V2  *blaz$"
 check ver_matching_test.stdout "V2  *blazb$"
 
+# Stuff inside quotes is matched literally, so "baz(int*, char)" should
+# not match the "baz(int *)" entry in the version table.
+check ver_matching_test.stdout "V1   *baz(int\\*)$"
+check_missing ver_matching_test.stdout "V1   *baz(int\\*, char)$"
+check_missing ver_matching_test.stdout "V1   *baz(char\\*, int)$"
+
 # 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"
index 2a17523..c7662af 100644 (file)
@@ -3,11 +3,16 @@ V1 {
         extern "C++"
         {
            "bar()";
-           myns::*;
+           "baz(int*)";
         };
        foo;
        blaza*;
         bar*;
+       # Make sure we parse "extern" when it's not first thing in the section.
+        extern "C++"
+        {
+           myns::*;
+        };
        # Would be a keyword in a linker script.
        SECTIONS;
        sizeof_headers;
index 40acb00..a1d58d3 100644 (file)
@@ -82,6 +82,7 @@
 /* Constants.  */
 
 %token <string> STRING
+%token <string> QUOTED_STRING
 %token <integer> INTEGER
 
 /* Keywords.  This list is taken from ldgram.y and ldlex.l in the old
 %type <versyms> vers_defns
 %type <versnode> vers_tag
 %type <deplist> verdep
+%type <string> string
 
 %%
 
@@ -207,7 +209,7 @@ file_cmd:
            { script_start_group(closure); }
          '(' input_list ')'
            { script_end_group(closure); }
-        | OPTION '(' STRING ')'
+        | OPTION '(' string ')'
            { script_parse_option(closure, $3.value, $3.length); }
         | VERSIONK '{'
             { script_push_lex_into_version_mode(closure); }
@@ -222,9 +224,9 @@ file_cmd:
    these is more-or-less OK since most scripts simply explicitly
    choose the default.  */
 ignore_cmd:
-         OUTPUT_FORMAT '(' STRING ')'
-       | OUTPUT_FORMAT '(' STRING ',' STRING ',' STRING ')'
-       | OUTPUT_ARCH '(' STRING ')'
+         OUTPUT_FORMAT '(' string ')'
+       | OUTPUT_FORMAT '(' string ',' string ',' string ')'
+       | OUTPUT_ARCH '(' string ')'
        ;
 
 /* A list of input file names.  */
@@ -235,7 +237,7 @@ input_list:
 
 /* An input file name.  */
 input_list_element:
-         STRING
+         string
            { script_add_file(closure, $1.value, $1.length); }
        | AS_NEEDED
            { script_start_as_needed(closure); }
@@ -246,66 +248,66 @@ input_list_element:
 /* A command which may appear at the top level of a linker script, or
    within a SECTIONS block.  */
 file_or_sections_cmd:
-         ENTRY '(' STRING ')'
+         ENTRY '(' string ')'
            { script_set_entry(closure, $3.value, $3.length); }
        | assignment end
        ;
 
 /* Set a symbol to a value.  */
 assignment:
-         STRING '=' parse_exp
+         string '=' parse_exp
            { script_set_symbol(closure, $1.value, $1.length, $3, 0, 0); }
-       | STRING PLUSEQ parse_exp
+       | string PLUSEQ parse_exp
            {
              Expression_ptr s = script_exp_string($1.value, $1.length);
              Expression_ptr e = script_exp_binary_add(s, $3);
              script_set_symbol(closure, $1.value, $1.length, e, 0, 0);
            }
-       | STRING MINUSEQ parse_exp
+       | string MINUSEQ parse_exp
            {
              Expression_ptr s = script_exp_string($1.value, $1.length);
              Expression_ptr e = script_exp_binary_sub(s, $3);
              script_set_symbol(closure, $1.value, $1.length, e, 0, 0);
            }
-       | STRING MULTEQ parse_exp
+       | string MULTEQ parse_exp
            {
              Expression_ptr s = script_exp_string($1.value, $1.length);
              Expression_ptr e = script_exp_binary_mult(s, $3);
              script_set_symbol(closure, $1.value, $1.length, e, 0, 0);
            }
-       | STRING DIVEQ parse_exp
+       | string DIVEQ parse_exp
            {
              Expression_ptr s = script_exp_string($1.value, $1.length);
              Expression_ptr e = script_exp_binary_div(s, $3);
              script_set_symbol(closure, $1.value, $1.length, e, 0, 0);
            }
-       | STRING LSHIFTEQ parse_exp
+       | string LSHIFTEQ parse_exp
            {
              Expression_ptr s = script_exp_string($1.value, $1.length);
              Expression_ptr e = script_exp_binary_lshift(s, $3);
              script_set_symbol(closure, $1.value, $1.length, e, 0, 0);
            }
-       | STRING RSHIFTEQ parse_exp
+       | string RSHIFTEQ parse_exp
            {
              Expression_ptr s = script_exp_string($1.value, $1.length);
              Expression_ptr e = script_exp_binary_rshift(s, $3);
              script_set_symbol(closure, $1.value, $1.length, e, 0, 0);
            }
-       | STRING ANDEQ parse_exp
+       | string ANDEQ parse_exp
            {
              Expression_ptr s = script_exp_string($1.value, $1.length);
              Expression_ptr e = script_exp_binary_bitwise_and(s, $3);
              script_set_symbol(closure, $1.value, $1.length, e, 0, 0);
            }
-       | STRING OREQ parse_exp
+       | string OREQ parse_exp
            {
              Expression_ptr s = script_exp_string($1.value, $1.length);
              Expression_ptr e = script_exp_binary_bitwise_or(s, $3);
              script_set_symbol(closure, $1.value, $1.length, e, 0, 0);
            }
-       | PROVIDE '(' STRING '=' parse_exp ')'
+       | PROVIDE '(' string '=' parse_exp ')'
            { script_set_symbol(closure, $3.value, $3.length, $5, 1, 0); }
-       | PROVIDE_HIDDEN '(' STRING '=' parse_exp ')'
+       | PROVIDE_HIDDEN '(' string '=' parse_exp ')'
            { script_set_symbol(closure, $3.value, $3.length, $5, 1, 1); }
        ;
 
@@ -373,27 +375,29 @@ exp:
            { $$ = script_exp_integer($1); }
        | STRING
            { $$ = script_exp_string($1.value, $1.length); }
+       | QUOTED_STRING
+           { $$ = script_exp_string($1.value, $1.length); }
        | MAX_K '(' exp ',' exp ')'
            { $$ = script_exp_function_max($3, $5); }
        | MIN_K '(' exp ',' exp ')'
            { $$ = script_exp_function_min($3, $5); }
-       | DEFINED '(' STRING ')'
+       | DEFINED '(' string ')'
            { $$ = script_exp_function_defined($3.value, $3.length); }
        | SIZEOF_HEADERS
            { $$ = script_exp_function_sizeof_headers(); }
-       | ALIGNOF '(' STRING ')'
+       | ALIGNOF '(' string ')'
            { $$ = script_exp_function_alignof($3.value, $3.length); }
-       | SIZEOF '(' STRING ')'
+       | SIZEOF '(' string ')'
            { $$ = script_exp_function_sizeof($3.value, $3.length); }
-       | ADDR '(' STRING ')'
+       | ADDR '(' string ')'
            { $$ = script_exp_function_addr($3.value, $3.length); }
-       | LOADADDR '(' STRING ')'
+       | LOADADDR '(' string ')'
            { $$ = script_exp_function_loadaddr($3.value, $3.length); }
-       | ORIGIN '(' STRING ')'
+       | ORIGIN '(' string ')'
            { $$ = script_exp_function_origin($3.value, $3.length); }
-       | LENGTH '(' STRING ')'
+       | LENGTH '(' string ')'
            { $$ = script_exp_function_length($3.value, $3.length); }
-       | CONSTANT '(' STRING ')'
+       | CONSTANT '(' string ')'
            { $$ = script_exp_function_constant($3.value, $3.length); }
        | ABSOLUTE '(' exp ')'
            { $$ = script_exp_function_absolute($3); }
@@ -409,17 +413,17 @@ exp:
            { $$ = script_exp_function_data_segment_relro_end($3, $5); }
        | DATA_SEGMENT_END '(' exp ')'
            { $$ = script_exp_function_data_segment_end($3); }
-       | SEGMENT_START '(' STRING ',' exp ')'
+       | SEGMENT_START '(' string ',' exp ')'
            {
              $$ = script_exp_function_segment_start($3.value, $3.length, $5);
            }
-       | ASSERT_K '(' exp ',' STRING ')'
+       | ASSERT_K '(' exp ',' string ')'
            { $$ = script_exp_function_assert($3, $5.value, $5.length); }
        ;
 
 /* Handle the --defsym option.  */
 defsym_expr:
-         STRING '=' parse_exp
+         string '=' parse_exp
            { script_set_symbol(closure, $1.value, $1.length, $3, 0, 0); }
        ;
 
@@ -438,23 +442,23 @@ vers_node:
            {
              script_register_vers_node (closure, NULL, 0, $2, NULL);
            }
-       | STRING '{' vers_tag '}' ';'
+       | string '{' vers_tag '}' ';'
            {
              script_register_vers_node (closure, $1.value, $1.length, $3,
                                         NULL);
            }
-       | STRING '{' vers_tag '}' verdep ';'
+       | string '{' vers_tag '}' verdep ';'
            {
              script_register_vers_node (closure, $1.value, $1.length, $3, $5);
            }
        ;
 
 verdep:
-         STRING
+         string
            {
              $$ = script_add_vers_depend (closure, NULL, $1.value, $1.length);
            }
-       | verdep STRING
+       | verdep string
            {
              $$ = script_add_vers_depend (closure, $1, $2.value, $2.length);
            }
@@ -473,36 +477,70 @@ vers_tag:
            { $$ = script_new_vers_node (closure, $3, $7); }
        ;
 
+/* Here is one of the rare places we care about the distinction
+   between STRING and QUOTED_STRING.  For QUOTED_STRING, we do exact
+   matching on the pattern, so we pass in true for the exact_match
+   parameter.  For STRING, we do glob matching and pass in false.  */
 vers_defns:
          STRING
            {
              $$ = script_new_vers_pattern (closure, NULL, $1.value,
-                                           $1.length);
+                                           $1.length, 0);
+           }
+       | QUOTED_STRING
+           {
+             $$ = script_new_vers_pattern (closure, NULL, $1.value,
+                                           $1.length, 1);
            }
        | vers_defns ';' STRING
            {
-             $$ = script_new_vers_pattern (closure, $1, $3.value, $3.length);
+             $$ = script_new_vers_pattern (closure, $1, $3.value,
+                                            $3.length, 0);
+           }
+       | vers_defns ';' QUOTED_STRING
+           {
+             $$ = script_new_vers_pattern (closure, $1, $3.value,
+                                            $3.length, 1);
            }
-        | /* Push STRING on the language stack. */
-          EXTERN STRING '{'
-           { version_script_push_lang(closure, $2.value, $2.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);
            }
+        | /* Push string on the language stack.  This is more complicated
+             than the other cases because we need to merge the linked-list
+             state from the pre-EXTERN defns and the post-EXTERN defns.  */
+          vers_defns ';' EXTERN string '{'
+           { version_script_push_lang (closure, $4.value, $4.length); }
+         vers_defns opt_semicolon '}'
+           {
+             $$ = script_merge_expressions ($1, $7);
+             version_script_pop_lang(closure);
+           }
         | EXTERN  // "extern" as a symbol name
            {
              $$ = script_new_vers_pattern (closure, NULL, "extern",
-                                           sizeof("extern") - 1);
+                                           sizeof("extern") - 1, 1);
            }
        | vers_defns ';' EXTERN
            {
              $$ = script_new_vers_pattern (closure, $1, "extern",
-                                           sizeof("extern") - 1);
+                                           sizeof("extern") - 1, 1);
            }
        ;
 
+/* A string can be either a STRING or a QUOTED_STRING.  Almost all the
+   time we don't care, and we use this rule.  */
+string:
+          STRING
+           { $$ = $1; }
+       | QUOTED_STRING
+           { $$ = $1; }
+       ;
+
 /* Some statements require a terminator, which may be a semicolon or a
    comma.  */
 end: