+ ENTRY '(' string ')'
+ { script_set_entry(closure, $3.value, $3.length); }
+ | assignment end
+ | ASSERT_K '(' parse_exp ',' string ')'
+ { script_add_assertion(closure, $3, $5.value, $5.length); }
+ ;
+
+/* A list of MEMORY definitions. */
+memory_defs:
+ memory_defs opt_comma memory_def
+ | /* empty */
+ ;
+
+/* A single MEMORY definition. */
+memory_def:
+ string memory_attr ':' memory_origin '=' parse_exp opt_comma memory_length '=' parse_exp
+ { script_add_memory(closure, $1.value, $1.length, $2, $6, $10); }
+ |
+ /* LD supports an INCLUDE directive here, currently GOLD does not. */
+ INCLUDE string
+ { script_include_directive(closure, $2.value, $2.length); }
+ |
+ ;
+
+/* The (optional) attributes of a MEMORY region. */
+memory_attr:
+ '(' string ')'
+ { $$ = script_parse_memory_attr(closure, $2.value, $2.length, 0); }
+ | /* Inverted attributes. */
+ '(' '!' string ')'
+ { $$ = script_parse_memory_attr(closure, $3.value, $3.length, 1); }
+ | /* empty */
+ { $$ = 0; }
+ ;
+
+memory_origin:
+ ORIGIN
+ |
+ ORG
+ |
+ 'o'
+ ;
+
+memory_length:
+ LENGTH
+ |
+ LEN
+ |
+ 'l'
+ ;
+
+/* A list of program header definitions. */
+phdrs_defs:
+ phdrs_defs phdr_def
+ | /* empty */
+ ;
+
+/* A program header definition. */
+phdr_def:
+ string phdr_type phdr_info ';'
+ { script_add_phdr(closure, $1.value, $1.length, $2, &$3); }
+ ;
+
+/* A program header type. The GNU linker accepts a general expression
+ here, but that would be a pain because we would have to dig into
+ the expression structure. It's unlikely that anybody uses anything
+ other than a string or a number here, so that is all we expect. */
+phdr_type:
+ string
+ { $$ = script_phdr_string_to_type(closure, $1.value, $1.length); }
+ | INTEGER
+ { $$ = $1; }
+ ;
+
+/* Additional information for a program header. */
+phdr_info:
+ /* empty */
+ { memset(&$$, 0, sizeof(struct Phdr_info)); }
+ | string phdr_info
+ {
+ $$ = $2;
+ if ($1.length == 7 && strncmp($1.value, "FILEHDR", 7) == 0)
+ $$.includes_filehdr = 1;
+ else
+ yyerror(closure, "PHDRS syntax error");
+ }
+ | PHDRS phdr_info
+ {
+ $$ = $2;
+ $$.includes_phdrs = 1;
+ }
+ | string '(' INTEGER ')' phdr_info
+ {
+ $$ = $5;
+ if ($1.length == 5 && strncmp($1.value, "FLAGS", 5) == 0)
+ {
+ $$.is_flags_valid = 1;
+ $$.flags = $3;
+ }
+ else
+ yyerror(closure, "PHDRS syntax error");
+ }
+ | AT '(' parse_exp ')' phdr_info
+ {
+ $$ = $5;
+ $$.load_address = $3;
+ }
+ ;
+
+/* Set a symbol to a value. */
+assignment:
+ string '=' parse_exp
+ { script_set_symbol(closure, $1.value, $1.length, $3, 0, 0); }
+ | 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
+ {
+ 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
+ {
+ 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
+ {
+ 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
+ {
+ 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
+ {
+ 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
+ {
+ 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
+ {
+ 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 ')'
+ { script_set_symbol(closure, $3.value, $3.length, $5, 1, 0); }
+ | PROVIDE_HIDDEN '(' string '=' parse_exp ')'
+ { script_set_symbol(closure, $3.value, $3.length, $5, 1, 1); }
+ ;
+
+/* Parse an expression, putting the lexer into the right mode. */
+parse_exp:
+ { script_push_lex_into_expression_mode(closure); }
+ exp
+ {
+ script_pop_lex_mode(closure);
+ $$ = $2;
+ }
+ ;
+
+/* An expression. */
+exp:
+ '(' exp ')'
+ { $$ = $2; }
+ | '-' exp %prec UNARY
+ { $$ = script_exp_unary_minus($2); }
+ | '!' exp %prec UNARY
+ { $$ = script_exp_unary_logical_not($2); }
+ | '~' exp %prec UNARY
+ { $$ = script_exp_unary_bitwise_not($2); }
+ | '+' exp %prec UNARY
+ { $$ = $2; }
+ | exp '*' exp
+ { $$ = script_exp_binary_mult($1, $3); }
+ | exp '/' exp
+ { $$ = script_exp_binary_div($1, $3); }
+ | exp '%' exp
+ { $$ = script_exp_binary_mod($1, $3); }
+ | exp '+' exp
+ { $$ = script_exp_binary_add($1, $3); }
+ | exp '-' exp
+ { $$ = script_exp_binary_sub($1, $3); }
+ | exp LSHIFT exp
+ { $$ = script_exp_binary_lshift($1, $3); }
+ | exp RSHIFT exp
+ { $$ = script_exp_binary_rshift($1, $3); }
+ | exp EQ exp
+ { $$ = script_exp_binary_eq($1, $3); }
+ | exp NE exp
+ { $$ = script_exp_binary_ne($1, $3); }
+ | exp LE exp
+ { $$ = script_exp_binary_le($1, $3); }
+ | exp GE exp
+ { $$ = script_exp_binary_ge($1, $3); }
+ | exp '<' exp
+ { $$ = script_exp_binary_lt($1, $3); }
+ | exp '>' exp
+ { $$ = script_exp_binary_gt($1, $3); }
+ | exp '&' exp
+ { $$ = script_exp_binary_bitwise_and($1, $3); }
+ | exp '^' exp
+ { $$ = script_exp_binary_bitwise_xor($1, $3); }
+ | exp '|' exp
+ { $$ = script_exp_binary_bitwise_or($1, $3); }
+ | exp ANDAND exp
+ { $$ = script_exp_binary_logical_and($1, $3); }
+ | exp OROR exp
+ { $$ = script_exp_binary_logical_or($1, $3); }
+ | exp '?' exp ':' exp
+ { $$ = script_exp_trinary_cond($1, $3, $5); }
+ | INTEGER
+ { $$ = script_exp_integer($1); }
+ | string
+ { $$ = script_symbol(closure, $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 ')'
+ { $$ = script_exp_function_defined($3.value, $3.length); }
+ | SIZEOF_HEADERS
+ { $$ = script_exp_function_sizeof_headers(); }
+ | ALIGNOF '(' string ')'
+ { $$ = script_exp_function_alignof($3.value, $3.length); }
+ | SIZEOF '(' string ')'
+ { $$ = script_exp_function_sizeof($3.value, $3.length); }
+ | ADDR '(' string ')'
+ { $$ = script_exp_function_addr($3.value, $3.length); }
+ | LOADADDR '(' string ')'
+ { $$ = script_exp_function_loadaddr($3.value, $3.length); }
+ | ORIGIN '(' string ')'
+ { $$ = script_exp_function_origin(closure, $3.value, $3.length); }
+ | LENGTH '(' string ')'
+ { $$ = script_exp_function_length(closure, $3.value, $3.length); }
+ | CONSTANT '(' string ')'
+ { $$ = script_exp_function_constant($3.value, $3.length); }
+ | ABSOLUTE '(' exp ')'
+ { $$ = script_exp_function_absolute($3); }
+ | ALIGN_K '(' exp ')'
+ { $$ = script_exp_function_align(script_exp_string(".", 1), $3); }
+ | ALIGN_K '(' exp ',' exp ')'
+ { $$ = script_exp_function_align($3, $5); }
+ | BLOCK '(' exp ')'
+ { $$ = script_exp_function_align(script_exp_string(".", 1), $3); }
+ | DATA_SEGMENT_ALIGN '(' exp ',' exp ')'
+ {
+ script_data_segment_align(closure);
+ $$ = script_exp_function_data_segment_align($3, $5);
+ }
+ | DATA_SEGMENT_RELRO_END '(' exp ',' exp ')'
+ {
+ script_data_segment_relro_end(closure);
+ $$ = script_exp_function_data_segment_relro_end($3, $5);
+ }
+ | DATA_SEGMENT_END '(' exp ')'
+ { $$ = script_exp_function_data_segment_end($3); }
+ | SEGMENT_START '(' string ',' exp ')'
+ {
+ $$ = script_exp_function_segment_start($3.value, $3.length, $5);
+ /* We need to take note of any SEGMENT_START expressions
+ because they change the behaviour of -Ttext, -Tdata and
+ -Tbss options. */
+ script_saw_segment_start_expression(closure);
+ }
+ | ASSERT_K '(' exp ',' string ')'
+ { $$ = script_exp_function_assert($3, $5.value, $5.length); }
+ ;
+
+/* Handle the --defsym option. */
+defsym_expr:
+ string '=' parse_exp
+ { script_set_symbol(closure, $1.value, $1.length, $3, 0, 0); }
+ ;
+
+/* Handle the --dynamic-list option. A dynamic list has the format
+ { sym1; sym2; extern "C++" { namespace::sym3 }; };
+ We store the symbol we see in the "local" list; that is where
+ Command_line::in_dynamic_list() will look to do its check.
+ TODO(csilvers): More than one of these brace-lists can appear, and
+ should just be merged and treated as a single list. */
+dynamic_list_expr: dynamic_list_nodes ;
+
+dynamic_list_nodes:
+ dynamic_list_node
+ | dynamic_list_nodes dynamic_list_node
+ ;
+
+dynamic_list_node:
+ '{' vers_defns ';' '}' ';'
+ { script_new_vers_node (closure, NULL, $2); }
+ ;
+
+/* 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); }
+ ;
+
+/* 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, 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, 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); }
+ 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, 1);
+ }
+ | vers_defns ';' EXTERN
+ {
+ $$ = script_new_vers_pattern (closure, $1, "extern",
+ 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:
+ ';'
+ | ','
+ ;
+
+/* An optional semicolon. */
+opt_semicolon:
+ ';'
+ | /* empty */