From Craig Silverstein: Handle quoted strings differently in version

scripts, and handle extern "C++" in the middle of a block.
This commit is contained in:
Ian Lance Taylor
2008-01-18 23:35:09 +00:00
parent 14144f39e4
commit 106002249a
7 changed files with 135 additions and 54 deletions

View File

@ -125,6 +125,7 @@ Sized_dwarf_line_info<size, big_endian>::Sized_dwarf_line_info(Object* object,
{ {
unsigned int debug_shndx; unsigned int debug_shndx;
for (debug_shndx = 0; debug_shndx < object->shnum(); ++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") if (object->section_name(debug_shndx) == ".debug_line")
{ {
section_size_type buffer_size; section_size_type buffer_size;

View File

@ -233,7 +233,11 @@ script_add_vers_depend(void* closure,
extern struct Version_expression_list * extern struct Version_expression_list *
script_new_vers_pattern(void* closure, script_new_vers_pattern(void* closure,
struct Version_expression_list *, 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 * extern struct Version_tree *
script_new_vers_node(void* closure, script_new_vers_node(void* closure,

View File

@ -1517,7 +1517,7 @@ yylex(YYSTYPE* lvalp, void* closurev)
case Token::TOKEN_QUOTED_STRING: case Token::TOKEN_QUOTED_STRING:
lvalp->string.value = token->string_value(&lvalp->string.length); lvalp->string.value = token->string_value(&lvalp->string.length);
return STRING; return QUOTED_STRING;
case Token::TOKEN_OPERATOR: case Token::TOKEN_OPERATOR:
return token->operator_value(); return token->operator_value();
@ -1713,11 +1713,14 @@ script_pop_lex_mode(void* closurev)
// pattern and language should be from the stringpool // pattern and language should be from the stringpool
struct Version_expression { struct Version_expression {
Version_expression(const std::string& pattern, Version_expression(const std::string& pattern,
const std::string& language) const std::string& language,
: pattern(pattern), language(language) {} bool exact_match)
: pattern(pattern), language(language), exact_match(exact_match) {}
std::string pattern; std::string pattern;
std::string language; 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) for (size_t j = 0; j < version_trees_.size(); ++j)
{ {
// Is it a global symbol for this version? // 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; check_global ? version_trees_[j]->global : version_trees_[j]->local;
if (exp != NULL) if (explist != NULL)
for (size_t k = 0; k < exp->expressions.size(); ++k) for (size_t k = 0; k < explist->expressions.size(); ++k)
{ {
const char* name_to_match = symbol_name; const char* name_to_match = symbol_name;
const struct Version_expression& exp = explist->expressions[k];
char* demangled_name = NULL; char* demangled_name = NULL;
if (exp->expressions[k].language == "C++") if (exp.language == "C++")
{ {
demangled_name = cplus_demangle(symbol_name, demangled_name = cplus_demangle(symbol_name,
DMGL_ANSI | DMGL_PARAMS); DMGL_ANSI | DMGL_PARAMS);
@ -1805,7 +1809,7 @@ Version_script_info::get_symbol_version_helper(const char* symbol_name,
continue; continue;
name_to_match = demangled_name; name_to_match = demangled_name;
} }
else if (exp->expressions[k].language == "Java") else if (exp.language == "Java")
{ {
demangled_name = cplus_demangle(symbol_name, demangled_name = cplus_demangle(symbol_name,
(DMGL_ANSI | DMGL_PARAMS (DMGL_ANSI | DMGL_PARAMS
@ -1815,8 +1819,12 @@ Version_script_info::get_symbol_version_helper(const char* symbol_name,
continue; continue;
name_to_match = demangled_name; name_to_match = demangled_name;
} }
bool matched = fnmatch(exp->expressions[k].pattern.c_str(), bool matched;
name_to_match, FNM_NOESCAPE) == 0; 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) if (demangled_name != NULL)
free(demangled_name); free(demangled_name);
if (matched) if (matched)
@ -1893,17 +1901,32 @@ script_add_vers_depend(void* closurev,
extern "C" struct Version_expression_list * extern "C" struct Version_expression_list *
script_new_vers_pattern(void* closurev, script_new_vers_pattern(void* closurev,
struct Version_expression_list *expressions, 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); Parser_closure* closure = static_cast<Parser_closure*>(closurev);
if (expressions == NULL) if (expressions == NULL)
expressions = closure->version_script()->allocate_expression_list(); expressions = closure->version_script()->allocate_expression_list();
expressions->expressions.push_back( expressions->expressions.push_back(
Version_expression(std::string(pattern, patlen), Version_expression(std::string(pattern, patlen),
closure->get_current_language())); closure->get_current_language(),
static_cast<bool>(exact_match)));
return expressions; 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. // Combine the global and local expressions into a a Version_tree.
extern "C" struct Version_tree * extern "C" struct Version_tree *

View File

@ -28,6 +28,10 @@ void foo1() {} // local
void bar() {} // V1 void bar() {} // V1
void bar1() {} // global void bar1() {} // global
void baz(int*) {} // V1
void baz(int*, char) {} // global
void baz(char*, int) {} // global
extern "C" { extern "C" {
void bar2() {} // V1 void bar2() {} // V1
}; };

View File

@ -73,6 +73,12 @@ check ver_matching_test.stdout "Base *bla$"
check ver_matching_test.stdout "V2 *blaz$" check ver_matching_test.stdout "V2 *blaz$"
check ver_matching_test.stdout "V2 *blazb$" 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 # 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. # dump, but we haven't figured out how to suppress it yet.
# check_missing ver_matching_test.stdout "foo1" # check_missing ver_matching_test.stdout "foo1"

View File

@ -3,11 +3,16 @@ V1 {
extern "C++" extern "C++"
{ {
"bar()"; "bar()";
myns::*; "baz(int*)";
}; };
foo; foo;
blaza*; blaza*;
bar*; 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. # Would be a keyword in a linker script.
SECTIONS; SECTIONS;
sizeof_headers; sizeof_headers;

View File

@ -82,6 +82,7 @@
/* Constants. */ /* Constants. */
%token <string> STRING %token <string> STRING
%token <string> QUOTED_STRING
%token <integer> INTEGER %token <integer> INTEGER
/* Keywords. This list is taken from ldgram.y and ldlex.l in the old /* Keywords. This list is taken from ldgram.y and ldlex.l in the old
@ -185,6 +186,7 @@
%type <versyms> vers_defns %type <versyms> vers_defns
%type <versnode> vers_tag %type <versnode> vers_tag
%type <deplist> verdep %type <deplist> verdep
%type <string> string
%% %%
@ -207,7 +209,7 @@ file_cmd:
{ script_start_group(closure); } { script_start_group(closure); }
'(' input_list ')' '(' input_list ')'
{ script_end_group(closure); } { script_end_group(closure); }
| OPTION '(' STRING ')' | OPTION '(' string ')'
{ script_parse_option(closure, $3.value, $3.length); } { script_parse_option(closure, $3.value, $3.length); }
| VERSIONK '{' | VERSIONK '{'
{ script_push_lex_into_version_mode(closure); } { script_push_lex_into_version_mode(closure); }
@ -222,9 +224,9 @@ file_cmd:
these is more-or-less OK since most scripts simply explicitly these is more-or-less OK since most scripts simply explicitly
choose the default. */ choose the default. */
ignore_cmd: ignore_cmd:
OUTPUT_FORMAT '(' STRING ')' OUTPUT_FORMAT '(' string ')'
| OUTPUT_FORMAT '(' STRING ',' STRING ',' STRING ')' | OUTPUT_FORMAT '(' string ',' string ',' string ')'
| OUTPUT_ARCH '(' STRING ')' | OUTPUT_ARCH '(' string ')'
; ;
/* A list of input file names. */ /* A list of input file names. */
@ -235,7 +237,7 @@ input_list:
/* An input file name. */ /* An input file name. */
input_list_element: input_list_element:
STRING string
{ script_add_file(closure, $1.value, $1.length); } { script_add_file(closure, $1.value, $1.length); }
| AS_NEEDED | AS_NEEDED
{ script_start_as_needed(closure); } { 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 /* A command which may appear at the top level of a linker script, or
within a SECTIONS block. */ within a SECTIONS block. */
file_or_sections_cmd: file_or_sections_cmd:
ENTRY '(' STRING ')' ENTRY '(' string ')'
{ script_set_entry(closure, $3.value, $3.length); } { script_set_entry(closure, $3.value, $3.length); }
| assignment end | assignment end
; ;
/* Set a symbol to a value. */ /* Set a symbol to a value. */
assignment: assignment:
STRING '=' parse_exp string '=' parse_exp
{ script_set_symbol(closure, $1.value, $1.length, $3, 0, 0); } { 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 s = script_exp_string($1.value, $1.length);
Expression_ptr e = script_exp_binary_add(s, $3); Expression_ptr e = script_exp_binary_add(s, $3);
script_set_symbol(closure, $1.value, $1.length, e, 0, 0); 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 s = script_exp_string($1.value, $1.length);
Expression_ptr e = script_exp_binary_sub(s, $3); Expression_ptr e = script_exp_binary_sub(s, $3);
script_set_symbol(closure, $1.value, $1.length, e, 0, 0); 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 s = script_exp_string($1.value, $1.length);
Expression_ptr e = script_exp_binary_mult(s, $3); Expression_ptr e = script_exp_binary_mult(s, $3);
script_set_symbol(closure, $1.value, $1.length, e, 0, 0); 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 s = script_exp_string($1.value, $1.length);
Expression_ptr e = script_exp_binary_div(s, $3); Expression_ptr e = script_exp_binary_div(s, $3);
script_set_symbol(closure, $1.value, $1.length, e, 0, 0); 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 s = script_exp_string($1.value, $1.length);
Expression_ptr e = script_exp_binary_lshift(s, $3); Expression_ptr e = script_exp_binary_lshift(s, $3);
script_set_symbol(closure, $1.value, $1.length, e, 0, 0); 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 s = script_exp_string($1.value, $1.length);
Expression_ptr e = script_exp_binary_rshift(s, $3); Expression_ptr e = script_exp_binary_rshift(s, $3);
script_set_symbol(closure, $1.value, $1.length, e, 0, 0); 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 s = script_exp_string($1.value, $1.length);
Expression_ptr e = script_exp_binary_bitwise_and(s, $3); Expression_ptr e = script_exp_binary_bitwise_and(s, $3);
script_set_symbol(closure, $1.value, $1.length, e, 0, 0); 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 s = script_exp_string($1.value, $1.length);
Expression_ptr e = script_exp_binary_bitwise_or(s, $3); Expression_ptr e = script_exp_binary_bitwise_or(s, $3);
script_set_symbol(closure, $1.value, $1.length, e, 0, 0); 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); } { 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); } { script_set_symbol(closure, $3.value, $3.length, $5, 1, 1); }
; ;
@ -373,27 +375,29 @@ exp:
{ $$ = script_exp_integer($1); } { $$ = script_exp_integer($1); }
| STRING | STRING
{ $$ = script_exp_string($1.value, $1.length); } { $$ = script_exp_string($1.value, $1.length); }
| QUOTED_STRING
{ $$ = script_exp_string($1.value, $1.length); }
| MAX_K '(' exp ',' exp ')' | MAX_K '(' exp ',' exp ')'
{ $$ = script_exp_function_max($3, $5); } { $$ = script_exp_function_max($3, $5); }
| MIN_K '(' exp ',' exp ')' | MIN_K '(' exp ',' exp ')'
{ $$ = script_exp_function_min($3, $5); } { $$ = script_exp_function_min($3, $5); }
| DEFINED '(' STRING ')' | DEFINED '(' string ')'
{ $$ = script_exp_function_defined($3.value, $3.length); } { $$ = script_exp_function_defined($3.value, $3.length); }
| SIZEOF_HEADERS | SIZEOF_HEADERS
{ $$ = script_exp_function_sizeof_headers(); } { $$ = script_exp_function_sizeof_headers(); }
| ALIGNOF '(' STRING ')' | ALIGNOF '(' string ')'
{ $$ = script_exp_function_alignof($3.value, $3.length); } { $$ = script_exp_function_alignof($3.value, $3.length); }
| SIZEOF '(' STRING ')' | SIZEOF '(' string ')'
{ $$ = script_exp_function_sizeof($3.value, $3.length); } { $$ = script_exp_function_sizeof($3.value, $3.length); }
| ADDR '(' STRING ')' | ADDR '(' string ')'
{ $$ = script_exp_function_addr($3.value, $3.length); } { $$ = script_exp_function_addr($3.value, $3.length); }
| LOADADDR '(' STRING ')' | LOADADDR '(' string ')'
{ $$ = script_exp_function_loadaddr($3.value, $3.length); } { $$ = script_exp_function_loadaddr($3.value, $3.length); }
| ORIGIN '(' STRING ')' | ORIGIN '(' string ')'
{ $$ = script_exp_function_origin($3.value, $3.length); } { $$ = script_exp_function_origin($3.value, $3.length); }
| LENGTH '(' STRING ')' | LENGTH '(' string ')'
{ $$ = script_exp_function_length($3.value, $3.length); } { $$ = script_exp_function_length($3.value, $3.length); }
| CONSTANT '(' STRING ')' | CONSTANT '(' string ')'
{ $$ = script_exp_function_constant($3.value, $3.length); } { $$ = script_exp_function_constant($3.value, $3.length); }
| ABSOLUTE '(' exp ')' | ABSOLUTE '(' exp ')'
{ $$ = script_exp_function_absolute($3); } { $$ = script_exp_function_absolute($3); }
@ -409,17 +413,17 @@ exp:
{ $$ = script_exp_function_data_segment_relro_end($3, $5); } { $$ = script_exp_function_data_segment_relro_end($3, $5); }
| DATA_SEGMENT_END '(' exp ')' | DATA_SEGMENT_END '(' exp ')'
{ $$ = script_exp_function_data_segment_end($3); } { $$ = 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); $$ = 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); } { $$ = script_exp_function_assert($3, $5.value, $5.length); }
; ;
/* Handle the --defsym option. */ /* Handle the --defsym option. */
defsym_expr: defsym_expr:
STRING '=' parse_exp string '=' parse_exp
{ script_set_symbol(closure, $1.value, $1.length, $3, 0, 0); } { 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); 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, script_register_vers_node (closure, $1.value, $1.length, $3,
NULL); NULL);
} }
| STRING '{' vers_tag '}' verdep ';' | string '{' vers_tag '}' verdep ';'
{ {
script_register_vers_node (closure, $1.value, $1.length, $3, $5); script_register_vers_node (closure, $1.value, $1.length, $3, $5);
} }
; ;
verdep: verdep:
STRING string
{ {
$$ = script_add_vers_depend (closure, NULL, $1.value, $1.length); $$ = script_add_vers_depend (closure, NULL, $1.value, $1.length);
} }
| verdep STRING | verdep string
{ {
$$ = script_add_vers_depend (closure, $1, $2.value, $2.length); $$ = script_add_vers_depend (closure, $1, $2.value, $2.length);
} }
@ -473,36 +477,70 @@ vers_tag:
{ $$ = script_new_vers_node (closure, $3, $7); } { $$ = 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: vers_defns:
STRING STRING
{ {
$$ = script_new_vers_pattern (closure, NULL, $1.value, $$ = 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 | 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. */ | vers_defns ';' QUOTED_STRING
EXTERN 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); } { version_script_push_lang (closure, $2.value, $2.length); }
vers_defns opt_semicolon '}' vers_defns opt_semicolon '}'
{ {
$$ = $5; $$ = $5;
version_script_pop_lang(closure); 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 | EXTERN // "extern" as a symbol name
{ {
$$ = script_new_vers_pattern (closure, NULL, "extern", $$ = script_new_vers_pattern (closure, NULL, "extern",
sizeof("extern") - 1); sizeof("extern") - 1, 1);
} }
| vers_defns ';' EXTERN | vers_defns ';' EXTERN
{ {
$$ = script_new_vers_pattern (closure, $1, "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 /* Some statements require a terminator, which may be a semicolon or a
comma. */ comma. */
end: end: