summaryrefslogtreecommitdiffstats
path: root/binutils-2.25/gold/yyscript.y
diff options
context:
space:
mode:
Diffstat (limited to 'binutils-2.25/gold/yyscript.y')
-rw-r--r--binutils-2.25/gold/yyscript.y1133
1 files changed, 1133 insertions, 0 deletions
diff --git a/binutils-2.25/gold/yyscript.y b/binutils-2.25/gold/yyscript.y
new file mode 100644
index 00000000..51c755ba
--- /dev/null
+++ b/binutils-2.25/gold/yyscript.y
@@ -0,0 +1,1133 @@
+/* yyscript.y -- linker script grammar for gold. */
+
+/* Copyright 2006, 2007, 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 is a bison grammar to parse a subset of the original GNU ld
+ linker script language. */
+
+%{
+
+#include "config.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "script-c.h"
+
+%}
+
+/* We need to use a pure parser because we might be multi-threaded.
+ We pass some arguments through the parser to the lexer. */
+
+%pure-parser
+
+%parse-param {void* closure}
+%lex-param {void* closure}
+
+/* Since we require bison anyhow, we take advantage of it. */
+
+%error-verbose
+
+/* The values associated with tokens. */
+
+%union {
+ /* A string. */
+ struct Parser_string string;
+ /* A number. */
+ uint64_t integer;
+ /* An expression. */
+ Expression_ptr expr;
+ /* An output section header. */
+ struct Parser_output_section_header output_section_header;
+ /* An output section trailer. */
+ struct Parser_output_section_trailer output_section_trailer;
+ /* A section constraint. */
+ enum Section_constraint constraint;
+ /* A complete input section specification. */
+ struct Input_section_spec input_section_spec;
+ /* A list of wildcard specifications, with exclusions. */
+ struct Wildcard_sections wildcard_sections;
+ /* A single wildcard specification. */
+ struct Wildcard_section wildcard_section;
+ /* A list of strings. */
+ String_list_ptr string_list;
+ /* Information for a program header. */
+ struct Phdr_info phdr_info;
+ /* Used for version scripts and within VERSION {}. */
+ struct Version_dependency_list* deplist;
+ struct Version_expression_list* versyms;
+ struct Version_tree* versnode;
+ enum Script_section_type section_type;
+}
+
+/* Operators, including a precedence table for expressions. */
+
+%right PLUSEQ MINUSEQ MULTEQ DIVEQ '=' LSHIFTEQ RSHIFTEQ ANDEQ OREQ
+%right '?' ':'
+%left OROR
+%left ANDAND
+%left '|'
+%left '^'
+%left '&'
+%left EQ NE
+%left '<' '>' LE GE
+%left LSHIFT RSHIFT
+%left '+' '-'
+%left '*' '/' '%'
+
+/* A fake operator used to indicate unary operator precedence. */
+%right UNARY
+
+/* 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
+ GNU linker, with the keywords which only appear in MRI mode
+ removed. Not all these keywords are actually used in this grammar.
+ In most cases the keyword is recognized as the token name in upper
+ case. The comments indicate where this is not the case. */
+
+%token ABSOLUTE
+%token ADDR
+%token ALIGN_K /* ALIGN */
+%token ALIGNOF
+%token ASSERT_K /* ASSERT */
+%token AS_NEEDED
+%token AT
+%token BIND
+%token BLOCK
+%token BYTE
+%token CONSTANT
+%token CONSTRUCTORS
+%token COPY
+%token CREATE_OBJECT_SYMBOLS
+%token DATA_SEGMENT_ALIGN
+%token DATA_SEGMENT_END
+%token DATA_SEGMENT_RELRO_END
+%token DEFINED
+%token DSECT
+%token ENTRY
+%token EXCLUDE_FILE
+%token EXTERN
+%token FILL
+%token FLOAT
+%token FORCE_COMMON_ALLOCATION
+%token GLOBAL /* global */
+%token GROUP
+%token HLL
+%token INCLUDE
+%token INHIBIT_COMMON_ALLOCATION
+%token INFO
+%token INPUT
+%token KEEP
+%token LEN
+%token LENGTH /* LENGTH, l, len */
+%token LOADADDR
+%token LOCAL /* local */
+%token LONG
+%token MAP
+%token MAX_K /* MAX */
+%token MEMORY
+%token MIN_K /* MIN */
+%token NEXT
+%token NOCROSSREFS
+%token NOFLOAT
+%token NOLOAD
+%token ONLY_IF_RO
+%token ONLY_IF_RW
+%token ORG
+%token ORIGIN /* ORIGIN, o, org */
+%token OUTPUT
+%token OUTPUT_ARCH
+%token OUTPUT_FORMAT
+%token OVERLAY
+%token PHDRS
+%token PROVIDE
+%token PROVIDE_HIDDEN
+%token QUAD
+%token SEARCH_DIR
+%token SECTIONS
+%token SEGMENT_START
+%token SHORT
+%token SIZEOF
+%token SIZEOF_HEADERS /* SIZEOF_HEADERS, sizeof_headers */
+%token SORT_BY_ALIGNMENT
+%token SORT_BY_NAME
+%token SPECIAL
+%token SQUAD
+%token STARTUP
+%token SUBALIGN
+%token SYSLIB
+%token TARGET_K /* TARGET */
+%token TRUNCATE
+%token VERSIONK /* VERSION */
+
+/* Keywords, part 2. These are keywords that are unique to gold,
+ and not present in the old GNU linker. As before, unless the
+ comments say otherwise, the keyword is recognized as the token
+ name in upper case. */
+
+%token OPTION
+
+/* Special tokens used to tell the grammar what type of tokens we are
+ parsing. The token stream always begins with one of these tokens.
+ We do this because version scripts can appear embedded within
+ linker scripts, and because --defsym uses the expression
+ parser. */
+%token PARSING_LINKER_SCRIPT
+%token PARSING_VERSION_SCRIPT
+%token PARSING_DEFSYM
+%token PARSING_DYNAMIC_LIST
+
+/* Non-terminal types, where needed. */
+
+%type <expr> parse_exp exp
+%type <expr> opt_at opt_align opt_subalign opt_fill
+%type <output_section_header> section_header opt_address_and_section_type
+%type <section_type> section_type
+%type <output_section_trailer> section_trailer
+%type <constraint> opt_constraint
+%type <string_list> opt_phdr
+%type <integer> data_length
+%type <input_section_spec> input_section_no_keep
+%type <wildcard_sections> wildcard_sections
+%type <wildcard_section> wildcard_file wildcard_section
+%type <string_list> exclude_names
+%type <string> wildcard_name
+%type <integer> phdr_type memory_attr
+%type <phdr_info> phdr_info
+%type <versyms> vers_defns
+%type <versnode> vers_tag
+%type <deplist> verdep
+%type <string> string
+
+%%
+
+/* Read the special token to see what to read next. */
+top:
+ PARSING_LINKER_SCRIPT linker_script
+ | PARSING_VERSION_SCRIPT version_script
+ | PARSING_DEFSYM defsym_expr
+ | PARSING_DYNAMIC_LIST dynamic_list_expr
+ ;
+
+/* A file contains a list of commands. */
+linker_script:
+ linker_script file_cmd
+ | /* empty */
+ ;
+
+/* A command which may appear at top level of a linker script. */
+file_cmd:
+ EXTERN '(' extern_name_list ')'
+ | FORCE_COMMON_ALLOCATION
+ { script_set_common_allocation(closure, 1); }
+ | GROUP
+ { script_start_group(closure); }
+ '(' input_list ')'
+ { script_end_group(closure); }
+ | INHIBIT_COMMON_ALLOCATION
+ { script_set_common_allocation(closure, 0); }
+ | INPUT '(' input_list ')'
+ | MEMORY '{' memory_defs '}'
+ | OPTION '(' string ')'
+ { script_parse_option(closure, $3.value, $3.length); }
+ | OUTPUT_FORMAT '(' string ')'
+ {
+ if (!script_check_output_format(closure, $3.value, $3.length,
+ NULL, 0, NULL, 0))
+ YYABORT;
+ }
+ | OUTPUT_FORMAT '(' string ',' string ',' string ')'
+ {
+ if (!script_check_output_format(closure, $3.value, $3.length,
+ $5.value, $5.length,
+ $7.value, $7.length))
+ YYABORT;
+ }
+ | PHDRS '{' phdrs_defs '}'
+ | SEARCH_DIR '(' string ')'
+ { script_add_search_dir(closure, $3.value, $3.length); }
+ | SECTIONS '{'
+ { script_start_sections(closure); }
+ sections_block '}'
+ { script_finish_sections(closure); }
+ | TARGET_K '(' string ')'
+ { script_set_target(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
+ | ';'
+ ;
+
+/* Top level commands which we ignore. The GNU linker uses these to
+ select the output format, but we don't offer a choice. Ignoring
+ these is more-or-less OK since most scripts simply explicitly
+ choose the default. */
+ignore_cmd:
+ OUTPUT_ARCH '(' string ')'
+ ;
+
+/* A list of external undefined symbols. We put the lexer into
+ expression mode so that commas separate names; this is what the GNU
+ linker does. */
+
+extern_name_list:
+ { script_push_lex_into_expression_mode(closure); }
+ extern_name_list_body
+ { script_pop_lex_mode(closure); }
+ ;
+
+extern_name_list_body:
+ string
+ { script_add_extern(closure, $1.value, $1.length); }
+ | extern_name_list_body string
+ { script_add_extern(closure, $2.value, $2.length); }
+ | extern_name_list_body ',' string
+ { script_add_extern(closure, $3.value, $3.length); }
+ ;
+
+/* A list of input file names. */
+input_list:
+ input_list_element
+ | input_list opt_comma input_list_element
+ ;
+
+/* An input file name. */
+input_list_element:
+ string
+ { script_add_file(closure, $1.value, $1.length); }
+ | '-' STRING
+ { script_add_library(closure, $2.value, $2.length); }
+ | AS_NEEDED
+ { script_start_as_needed(closure); }
+ '(' input_list ')'
+ { script_end_as_needed(closure); }
+ ;
+
+/* Commands in a SECTIONS block. */
+sections_block:
+ sections_block section_block_cmd
+ | /* empty */
+ ;
+
+/* A command which may appear within a SECTIONS block. */
+section_block_cmd:
+ file_or_sections_cmd
+ | string section_header
+ { script_start_output_section(closure, $1.value, $1.length, &$2); }
+ '{' section_cmds '}' section_trailer
+ { script_finish_output_section(closure, &$7); }
+ ;
+
+/* The header of an output section in a SECTIONS block--everything
+ after the name. */
+section_header:
+ { script_push_lex_into_expression_mode(closure); }
+ opt_address_and_section_type opt_at opt_align opt_subalign
+ { script_pop_lex_mode(closure); }
+ opt_constraint
+ {
+ $$.address = $2.address;
+ $$.section_type = $2.section_type;
+ $$.load_address = $3;
+ $$.align = $4;
+ $$.subalign = $5;
+ $$.constraint = $7;
+ }
+ ;
+
+/* The optional address followed by the optional section type. This
+ is a separate nonterminal to avoid a shift/reduce conflict on
+ '(' in section_header. */
+
+opt_address_and_section_type:
+ ':'
+ {
+ $$.address = NULL;
+ $$.section_type = SCRIPT_SECTION_TYPE_NONE;
+ }
+ | '(' ')' ':'
+ {
+ $$.address = NULL;
+ $$.section_type = SCRIPT_SECTION_TYPE_NONE;
+ }
+ | exp ':'
+ {
+ $$.address = $1;
+ $$.section_type = SCRIPT_SECTION_TYPE_NONE;
+ }
+ | exp '(' ')' ':'
+ {
+ $$.address = $1;
+ $$.section_type = SCRIPT_SECTION_TYPE_NONE;
+ }
+ | '(' section_type ')' ':'
+ {
+ $$.address = NULL;
+ $$.section_type = $2;
+ }
+ | exp '(' section_type ')' ':'
+ {
+ $$.address = $1;
+ $$.section_type = $3;
+ }
+ ;
+
+/* We only support NOLOAD. */
+section_type:
+ NOLOAD
+ { $$ = SCRIPT_SECTION_TYPE_NOLOAD; }
+ | DSECT
+ {
+ yyerror(closure, "DSECT section type is unsupported");
+ $$ = SCRIPT_SECTION_TYPE_DSECT;
+ }
+ | COPY
+ {
+ yyerror(closure, "COPY section type is unsupported");
+ $$ = SCRIPT_SECTION_TYPE_COPY;
+ }
+ | INFO
+ {
+ yyerror(closure, "INFO section type is unsupported");
+ $$ = SCRIPT_SECTION_TYPE_INFO;
+ }
+ | OVERLAY
+ {
+ yyerror(closure, "OVERLAY section type is unsupported");
+ $$ = SCRIPT_SECTION_TYPE_OVERLAY;
+ }
+ ;
+
+/* The address at which an output section should be loaded. */
+opt_at:
+ /* empty */
+ { $$ = NULL; }
+ | AT '(' exp ')'
+ { $$ = $3; }
+ ;
+
+/* The alignment of an output section. */
+opt_align:
+ /* empty */
+ { $$ = NULL; }
+ | ALIGN_K '(' exp ')'
+ { $$ = $3; }
+ ;
+
+/* The input section alignment within an output section. */
+opt_subalign:
+ /* empty */
+ { $$ = NULL; }
+ | SUBALIGN '(' exp ')'
+ { $$ = $3; }
+ ;
+
+/* A section constraint. */
+opt_constraint:
+ /* empty */
+ { $$ = CONSTRAINT_NONE; }
+ | ONLY_IF_RO
+ { $$ = CONSTRAINT_ONLY_IF_RO; }
+ | ONLY_IF_RW
+ { $$ = CONSTRAINT_ONLY_IF_RW; }
+ | SPECIAL
+ { $$ = CONSTRAINT_SPECIAL; }
+ ;
+
+/* The trailer of an output section in a SECTIONS block. */
+section_trailer:
+ opt_memspec opt_at_memspec opt_phdr opt_fill opt_comma
+ {
+ $$.fill = $4;
+ $$.phdrs = $3;
+ }
+ ;
+
+/* A memory specification for an output section. */
+opt_memspec:
+ '>' string
+ { script_set_section_region(closure, $2.value, $2.length, 1); }
+ | /* empty */
+ ;
+
+/* A memory specification for where to load an output section. */
+opt_at_memspec:
+ AT '>' string
+ { script_set_section_region(closure, $3.value, $3.length, 0); }
+ | /* empty */
+ ;
+
+/* The program segment an output section should go into. */
+opt_phdr:
+ opt_phdr ':' string
+ { $$ = script_string_list_push_back($1, $3.value, $3.length); }
+ | /* empty */
+ { $$ = NULL; }
+ ;
+
+/* The value to use to fill an output section. FIXME: This does not
+ handle a string of arbitrary length. */
+opt_fill:
+ '=' parse_exp
+ { $$ = $2; }
+ | /* empty */
+ { $$ = NULL; }
+ ;
+
+/* Commands which may appear within the description of an output
+ section in a SECTIONS block. */
+section_cmds:
+ /* empty */
+ | section_cmds section_cmd
+ ;
+
+/* A command which may appear within the description of an output
+ section in a SECTIONS block. */
+section_cmd:
+ assignment end
+ | input_section_spec
+ | data_length '(' parse_exp ')'
+ { script_add_data(closure, $1, $3); }
+ | ASSERT_K '(' parse_exp ',' string ')'
+ { script_add_assertion(closure, $3, $5.value, $5.length); }
+ | FILL '(' parse_exp ')'
+ { script_add_fill(closure, $3); }
+ | CONSTRUCTORS
+ {
+ /* The GNU linker uses CONSTRUCTORS for the a.out object
+ file format. It does nothing when using ELF. Since
+ some ELF linker scripts use it although it does
+ nothing, we accept it and ignore it. */
+ }
+ | SORT_BY_NAME '(' CONSTRUCTORS ')'
+ | INCLUDE string
+ { script_include_directive(closure, $2.value, $2.length); }
+ | ';'
+ ;
+
+/* The length of data which may appear within the description of an
+ output section in a SECTIONS block. */
+data_length:
+ QUAD
+ { $$ = QUAD; }
+ | SQUAD
+ { $$ = SQUAD; }
+ | LONG
+ { $$ = LONG; }
+ | SHORT
+ { $$ = SHORT; }
+ | BYTE
+ { $$ = BYTE; }
+ ;
+
+/* An input section specification. This may appear within the
+ description of an output section in a SECTIONS block. */
+input_section_spec:
+ input_section_no_keep
+ { script_add_input_section(closure, &$1, 0); }
+ | KEEP '(' input_section_no_keep ')'
+ { script_add_input_section(closure, &$3, 1); }
+ ;
+
+/* An input section specification within a KEEP clause. */
+input_section_no_keep:
+ string
+ {
+ $$.file.name = $1;
+ $$.file.sort = SORT_WILDCARD_NONE;
+ $$.input_sections.sections = NULL;
+ $$.input_sections.exclude = NULL;
+ }
+ | wildcard_file '(' wildcard_sections ')'
+ {
+ $$.file = $1;
+ $$.input_sections = $3;
+ }
+ ;
+
+/* A wildcard file specification. */
+wildcard_file:
+ wildcard_name
+ {
+ $$.name = $1;
+ $$.sort = SORT_WILDCARD_NONE;
+ }
+ | SORT_BY_NAME '(' wildcard_name ')'
+ {
+ $$.name = $3;
+ $$.sort = SORT_WILDCARD_BY_NAME;
+ }
+ ;
+
+/* A list of wild card section specifications. */
+wildcard_sections:
+ wildcard_sections opt_comma wildcard_section
+ {
+ $$.sections = script_string_sort_list_add($1.sections, &$3);
+ $$.exclude = $1.exclude;
+ }
+ | wildcard_section
+ {
+ $$.sections = script_new_string_sort_list(&$1);
+ $$.exclude = NULL;
+ }
+ | wildcard_sections opt_comma EXCLUDE_FILE '(' exclude_names ')'
+ {
+ $$.sections = $1.sections;
+ $$.exclude = script_string_list_append($1.exclude, $5);
+ }
+ | EXCLUDE_FILE '(' exclude_names ')'
+ {
+ $$.sections = NULL;
+ $$.exclude = $3;
+ }
+ ;
+
+/* A single wild card specification. */
+wildcard_section:
+ wildcard_name
+ {
+ $$.name = $1;
+ $$.sort = SORT_WILDCARD_NONE;
+ }
+ | SORT_BY_NAME '(' wildcard_section ')'
+ {
+ $$.name = $3.name;
+ switch ($3.sort)
+ {
+ case SORT_WILDCARD_NONE:
+ $$.sort = SORT_WILDCARD_BY_NAME;
+ break;
+ case SORT_WILDCARD_BY_NAME:
+ case SORT_WILDCARD_BY_NAME_BY_ALIGNMENT:
+ break;
+ case SORT_WILDCARD_BY_ALIGNMENT:
+ case SORT_WILDCARD_BY_ALIGNMENT_BY_NAME:
+ $$.sort = SORT_WILDCARD_BY_NAME_BY_ALIGNMENT;
+ break;
+ default:
+ abort();
+ }
+ }
+ | SORT_BY_ALIGNMENT '(' wildcard_section ')'
+ {
+ $$.name = $3.name;
+ switch ($3.sort)
+ {
+ case SORT_WILDCARD_NONE:
+ $$.sort = SORT_WILDCARD_BY_ALIGNMENT;
+ break;
+ case SORT_WILDCARD_BY_ALIGNMENT:
+ case SORT_WILDCARD_BY_ALIGNMENT_BY_NAME:
+ break;
+ case SORT_WILDCARD_BY_NAME:
+ case SORT_WILDCARD_BY_NAME_BY_ALIGNMENT:
+ $$.sort = SORT_WILDCARD_BY_ALIGNMENT_BY_NAME;
+ break;
+ default:
+ abort();
+ }
+ }
+ ;
+
+/* A list of file names to exclude. */
+exclude_names:
+ exclude_names opt_comma wildcard_name
+ { $$ = script_string_list_push_back($1, $3.value, $3.length); }
+ | wildcard_name
+ { $$ = script_new_string_list($1.value, $1.length); }
+ ;
+
+/* A single wildcard name. We recognize '*' and '?' specially since
+ they are expression tokens. */
+wildcard_name:
+ string
+ { $$ = $1; }
+ | '*'
+ {
+ $$.value = "*";
+ $$.length = 1;
+ }
+ | '?'
+ {
+ $$.value = "?";
+ $$.length = 1;
+ }
+ ;
+
+/* A command which may appear at the top level of a linker script, or
+ within a SECTIONS block. */
+file_or_sections_cmd:
+ 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); }
+ | INCLUDE string
+ { script_include_directive(closure, $2.value, $2.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 */
+ ;
+
+/* An optional comma. */
+opt_comma:
+ ','
+ | /* empty */
+ ;
+
+%%