-/* yyscript.y -- linker script grammer for gold. */
+/* yyscript.y -- linker script grammar for gold. */
/* Copyright 2006, 2007, 2008 Free Software Foundation, Inc.
Written by Ian Lance Taylor <iant@google.com>.
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include "script-c.h"
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_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. */
%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 HLL
%token INCLUDE
%token INHIBIT_COMMON_ALLOCATION
+%token INFO
%token INPUT
%token KEEP
%token LENGTH /* LENGTH, l, len */
%token NEXT
%token NOCROSSREFS
%token NOFLOAT
+%token NOLOAD
%token ONLY_IF_RO
%token ONLY_IF_RW
%token ORIGIN /* ORIGIN, o, org */
%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 opt_address_and_section_type
+%type <expr> parse_exp exp
%type <expr> opt_at opt_align opt_subalign opt_fill
-%type <output_section_header> section_header
+%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
+%type <phdr_info> phdr_info
%type <versyms> vers_defns
%type <versnode> vers_tag
%type <deplist> verdep
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. */
/* A command which may appear at top level of a linker script. */
file_cmd:
- GROUP
+ 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 ')'
| 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
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_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_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 ')'
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 = $2.address;
+ $$.section_type = $2.section_type;
$$.load_address = $3;
$$.align = $4;
$$.subalign = $5;
- script_pop_lex_mode(closure);
+ $$.constraint = $7;
}
;
'(' in section_header. */
opt_address_and_section_type:
- ':'
- { $$ = NULL; }
+ ':'
+ {
+ $$.address = NULL;
+ $$.section_type = SCRIPT_SECTION_TYPE_NONE;
+ }
| '(' ')' ':'
- { $$ = NULL; }
+ {
+ $$.address = NULL;
+ $$.section_type = SCRIPT_SECTION_TYPE_NONE;
+ }
| exp ':'
- { $$ = $1; }
+ {
+ $$.address = $1;
+ $$.section_type = SCRIPT_SECTION_TYPE_NONE;
+ }
| exp '(' ')' ':'
- { $$ = $1; }
- | exp '(' string ')' ':'
{
- yyerror(closure, "section types are not supported");
- $$ = $1;
+ $$.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;
}
;
{ $$ = $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:
- { script_push_lex_into_expression_mode(closure); }
opt_memspec opt_at_memspec opt_phdr opt_fill opt_comma
{
- $$.fill = $5;
- script_pop_lex_mode(closure);
+ $$.fill = $4;
+ $$.phdrs = $3;
}
;
/* The program segment an output section should go into. */
opt_phdr:
opt_phdr ':' string
- { yyerror(closure, "program headers are not supported"); }
+ { $$ = script_string_list_push_back($1, $3.value, $3.length); }
| /* empty */
+ { $$ = NULL; }
;
-/* The value to use to fill an output section. */
+/* The value to use to fill an output section. FIXME: This does not
+ handle a string of arbitrary length. */
opt_fill:
- '=' exp
+ '=' parse_exp
{ $$ = $2; }
| /* empty */
{ $$ = NULL; }
some ELF linker scripts use it although it does
nothing, we accept it and ignore it. */
}
+ | SORT_BY_NAME '(' CONSTRUCTORS ')'
| ';'
;
{ script_add_assertion(closure, $3, $5.value, $5.length); }
;
+/* 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
| BLOCK '(' exp ')'
{ $$ = script_exp_function_align(script_exp_string(".", 1), $3); }
| DATA_SEGMENT_ALIGN '(' exp ',' exp ')'
- { $$ = script_exp_function_data_segment_align($3, $5); }
+ {
+ script_data_segment_align(closure);
+ $$ = script_exp_function_data_segment_align($3, $5);
+ }
| DATA_SEGMENT_RELRO_END '(' exp ',' exp ')'
- { $$ = script_exp_function_data_segment_relro_end($3, $5); }
+ {
+ 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); }
{ 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