From 106002249a4341c91adc0c25f63c84cd1b544737 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 18 Jan 2008 23:35:09 +0000 Subject: [PATCH] From Craig Silverstein: Handle quoted strings differently in version scripts, and handle extern "C++" in the middle of a block. --- gold/dwarf_reader.cc | 1 + gold/script-c.h | 6 +- gold/script.cc | 49 ++++++++---- gold/testsuite/ver_matching_def.cc | 4 + gold/testsuite/ver_matching_test.sh | 6 ++ gold/testsuite/version_script.map | 7 +- gold/yyscript.y | 116 ++++++++++++++++++---------- 7 files changed, 135 insertions(+), 54 deletions(-) diff --git a/gold/dwarf_reader.cc b/gold/dwarf_reader.cc index bf03ca3228..19c1a035ac 100644 --- a/gold/dwarf_reader.cc +++ b/gold/dwarf_reader.cc @@ -125,6 +125,7 @@ Sized_dwarf_line_info::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; diff --git a/gold/script-c.h b/gold/script-c.h index 275f4a2281..da8b558c29 100644 --- a/gold/script-c.h +++ b/gold/script-c.h @@ -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, diff --git a/gold/script.cc b/gold/script.cc index 16c0cc0d86..3822c244a5 100644 --- a/gold/script.cc +++ b/gold/script.cc @@ -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(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(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 diff --git a/gold/testsuite/ver_matching_def.cc b/gold/testsuite/ver_matching_def.cc index 71d8d32fd5..2eba163ba7 100644 --- a/gold/testsuite/ver_matching_def.cc +++ b/gold/testsuite/ver_matching_def.cc @@ -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 }; diff --git a/gold/testsuite/ver_matching_test.sh b/gold/testsuite/ver_matching_test.sh index 48dd9a5b00..2ecdd98d28 100755 --- a/gold/testsuite/ver_matching_test.sh +++ b/gold/testsuite/ver_matching_test.sh @@ -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" diff --git a/gold/testsuite/version_script.map b/gold/testsuite/version_script.map index 2a175235b7..c7662af8d3 100644 --- a/gold/testsuite/version_script.map +++ b/gold/testsuite/version_script.map @@ -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; diff --git a/gold/yyscript.y b/gold/yyscript.y index 40acb00a2a..a1d58d395c 100644 --- a/gold/yyscript.y +++ b/gold/yyscript.y @@ -82,6 +82,7 @@ /* Constants. */ %token STRING +%token QUOTED_STRING %token INTEGER /* Keywords. This list is taken from ldgram.y and ldlex.l in the old @@ -185,6 +186,7 @@ %type vers_defns %type vers_tag %type verdep +%type 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); } - | /* Push STRING on the language stack. */ - EXTERN STRING '{' - { version_script_push_lang(closure, $2.value, $2.length); } + | 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); } 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: