/* A YACC grammer to parse a superset of the AT&T linker scripting languaue. Copyright (C) 1991, 1993 Free Software Foundation, Inc. Written by Steve Chamberlain of Cygnus Support (steve@cygnus.com). This file is part of GNU ld. 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ %{ /* */ #define DONTDECLARE_MALLOC #include "bfd.h" #include "sysdep.h" #include "bfdlink.h" #include "ld.h" #include "ldexp.h" #include "ldver.h" #include "ldlang.h" #include "ldemul.h" #include "ldfile.h" #include "ldmisc.h" #include "ldmain.h" #include "mri.h" #include "ldlex.h" #define YYDEBUG 1 static int typebits; static char *dirlist_ptr; lang_memory_region_type *region; char *current_file; boolean ldgram_want_filename = true; boolean had_script = false; boolean force_make_executable = false; boolean ldgram_in_script = false; boolean ldgram_had_equals = false; #define ERROR_NAME_MAX 20 static char *error_names[ERROR_NAME_MAX]; static int error_index; #define PUSH_ERROR(x) if (error_index < ERROR_NAME_MAX) error_names[error_index] = x; error_index++; #define POP_ERROR() error_index--; %} %union { bfd_vma integer; int voidval; char *name; int token; union etree_union *etree; struct sec *section; struct lang_output_section_statement_struct *output_section_statement; union lang_statement_union **statement_ptr; int lineno; struct { FILE *file; char *name; unsigned int lineno; } state; } %type exp opt_exp_with_type mustbe_exp opt_at %type fill_opt %type memspec_opt %token INT %token NAME %type length %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 '*' '/' '%' /*%token '+' '-' '*' '/' '%'*/ %right UNARY %token END %left '(' %token ALIGN_K BLOCK QUAD LONG SHORT BYTE %token SECTIONS %token '{' '}' %token SIZEOF_HEADERS OUTPUT_FORMAT FORCE_COMMON_ALLOCATION OUTPUT_ARCH %token SIZEOF_HEADERS %token INCLUDE %token MEMORY DEFSYMEND %token NOLOAD DSECT COPY INFO OVERLAY %token NAME DEFINED TARGET_K SEARCH_DIR MAP ENTRY %token OPTION_e OPTION_c OPTION_noinhibit_exec OPTION_s OPTION_S OPTION_sort_common OPTION_warn_common %token OPTION_EB OPTION_EL OPTION_G OPTION_Gval OPTION_help %token OPTION_format OPTION_oformat OPTION_F OPTION_u OPTION_y OPTION_Bstatic OPTION_N %token SIZEOF NEXT ADDR %token OPTION_d OPTION_dc OPTION_dp OPTION_x OPTION_X OPTION_defsym %token OPTION_v OPTION_V OPTION_m OPTION_memul OPTION_M OPTION_t STARTUP HLL SYSLIB FLOAT NOFLOAT %token OPTION_L OPTION_Map %token OPTION_n OPTION_r OPTION_o OPTION_b OPTION_R OPTION_relax OPTION_version %token OPTION_l OPTION_Lfile OPTION_T OPTION_Aarch OPTION_Tfile %token OPTION_Texp OPTION_esymbol OPTION_usymbol OPTION_ysymbol %token OPTION_Ur %token ORIGIN FILL OPTION_g %token LENGTH CREATE_OBJECT_SYMBOLS INPUT OUTPUT CONSTRUCTORS %token OPTION_RETAIN_SYMBOLS_FILE ALIGNMOD AT %token OPTION_Qy OPTION_Y OPTION_dn OPTION_call_shared OPTION_non_shared %token OPTION_Oval OPTION_stats OPTION_no_keep_memory %token OPTION_YP %type assign_op %type filename %token CHIP LIST SECT ABSOLUTE LOAD NEWLINE ENDWORD ORDER NAMEWORD %token FORMAT PUBLIC DEFSYMEND BASE ALIAS TRUNCATE REL %% file: command_line filename: NAME; command_line: command_line command_line_option | ; command_line_option: OPTION_Bstatic { } | OPTION_help { help (); exit (0); } | OPTION_v { ldversion(0); version_printed = true; } | OPTION_V { ldversion(1); version_printed = true; trace_file_tries = true; } | OPTION_version { ldversion(0); version_printed = true; } | OPTION_t { trace_files = true; } | OPTION_Map NAME { write_map = true; config.map_filename = $2; } | OPTION_m NAME { /* Ignore. */ } | OPTION_memul { /* Ignore. */ } | OPTION_M { config.map_filename = "-"; } | OPTION_n { config.magic_demand_paged = false; } | OPTION_N { config.text_read_only = false; config.magic_demand_paged = false; } | OPTION_s { link_info.strip = strip_all; } | OPTION_S { link_info.strip = strip_debugger; } | OPTION_stats { config.stats = true; } | OPTION_no_keep_memory { link_info.keep_memory = false; } | OPTION_u NAME { ldlang_add_undef($2); } | OPTION_usymbol { ldlang_add_undef($1); } | OPTION_r { link_info.relocateable = true; config.build_constructors = false; config.magic_demand_paged = false; config.text_read_only = false; } | OPTION_Ur { link_info.relocateable = true; config.build_constructors = true; config.magic_demand_paged = false; config.text_read_only = false; } | OPTION_o filename { lang_add_output($2, 0); } | OPTION_e NAME { lang_add_entry($2); } | OPTION_esymbol { lang_add_entry($1); } | OPTION_X { link_info.discard = discard_l; } | OPTION_x { link_info.discard = discard_all; } | OPTION_noinhibit_exec { force_make_executable = true; } | OPTION_sort_common { config.sort_common = true; } | OPTION_warn_common { config.warn_common = true; } | OPTION_d { command_line.force_common_definition = true; } | OPTION_relax { command_line.relax = true; } | OPTION_dc { command_line.force_common_definition = true; } | OPTION_g { /* Ignored */ } | OPTION_dp { command_line.force_common_definition = true; } | OPTION_format NAME { lang_add_target($2); } | OPTION_oformat NAME { lang_add_output_format($2, 0); } | OPTION_Texp { ldlex_expression(); hex_mode = 16; } INT { ldlex_popstate(); lang_section_start($1,exp_intop($3)); hex_mode = 0; } | OPTION_y NAME { add_ysym($2); } | OPTION_ysymbol { add_ysym($1); } | OPTION_Aarch { ldfile_add_arch($1); } | OPTION_b NAME { lang_add_target($2); } | OPTION_L NAME { ldfile_add_library_path($2); } | OPTION_Lfile { ldfile_add_library_path($1); } | OPTION_F { /* Ignore */ } | OPTION_c filename {ldfile_open_command_file($2); } mri_script_file END { ldlex_command();} | OPTION_Tfile { ldfile_open_command_file($1); } script_file END { ldlex_command();} | OPTION_T filename { ldfile_open_command_file($2); } script_file END { ldlex_command();} | OPTION_l { lang_add_input_file($1, lang_input_file_is_l_enum, (char *)NULL); } | OPTION_R filename { lang_add_input_file($2, lang_input_file_is_symbols_only_enum, (char *)NULL); } | OPTION_defsym { ldlex_defsym(); } NAME '=' exp DEFSYMEND { ldlex_popstate(); lang_add_assignment(exp_assop($4,$3,$5)); } | OPTION_RETAIN_SYMBOLS_FILE filename { add_keepsyms_file ($2); } | OPTION_EB { /* FIXME: This is currently ignored. It means ``produce a big-endian object file''. It could be used to select an output format. */ } | OPTION_EL { /* FIXME: This is currently ignored. It means ``produce a little-endian object file''. It could be used to select an output format. */ } | OPTION_G NAME { g_switch_value = atoi ($2); } | OPTION_Gval { g_switch_value = yylval.integer; } | OPTION_Qy | OPTION_dn | OPTION_non_shared | OPTION_call_shared | OPTION_Oval | OPTION_YP { dirlist_ptr = $1; goto set_default_dirlist; } | OPTION_Y NAME { if (strncmp ($2, "P,", 2)) einfo ("%P%F: unknown -Y option -- %s\n", $2); else { char *p; dirlist_ptr = $2; set_default_dirlist: while (1) { p = strchr (dirlist_ptr, ':'); if (p != NULL) *p = 0; if (*dirlist_ptr) ldfile_add_library_path (dirlist_ptr); if (p == NULL) break; *p = ':'; dirlist_ptr = p + 1; } } } | '{' script_file '}' { /* This parses compiled-in scripts. */ } | NAME { if (*$1 == '-') einfo("%P%F: illegal option -- %s\n", $1); else lang_add_input_file($1,lang_input_file_is_file_enum, (char *)NULL); } ; /* SYNTAX WITHIN AN MRI SCRIPT FILE */ mri_script_file: { ldlex_mri_script(); PUSH_ERROR("MRI style script"); } mri_script_lines { ldlex_popstate(); POP_ERROR(); } ; mri_script_lines: mri_script_lines mri_script_command NEWLINE | ; mri_script_command: CHIP exp | CHIP exp ',' exp | NAME { einfo("%P%F: unrecognised keyword in MRI style script '%s'\n",$1); } | LIST { write_map = true; config.map_filename = "-"; } | ORDER ordernamelist | ENDWORD | PUBLIC NAME '=' exp { mri_public($2, $4); } | PUBLIC NAME ',' exp { mri_public($2, $4); } | PUBLIC NAME exp { mri_public($2, $3); } | FORMAT NAME { mri_format($2); } | SECT NAME ',' exp { mri_output_section($2, $4);} | SECT NAME exp { mri_output_section($2, $3);} | SECT NAME '=' exp { mri_output_section($2, $4);} | ALIGN_K NAME '=' exp { mri_align($2,$4); } | ALIGNMOD NAME '=' exp { mri_alignmod($2,$4); } | ABSOLUTE mri_abs_name_list | LOAD mri_load_name_list | NAMEWORD NAME { mri_name($2); } | ALIAS NAME ',' NAME { mri_alias($2,$4,0);} | ALIAS NAME ',' INT { mri_alias($2,0,(int) $4);} | BASE exp { mri_base($2); } | TRUNCATE INT { mri_truncate((unsigned int) $2); } | ; ordernamelist: ordernamelist ',' NAME { mri_order($3); } | ordernamelist NAME { mri_order($2); } | ; mri_load_name_list: NAME { mri_load($1); } | mri_load_name_list ',' NAME { mri_load($3); } ; mri_abs_name_list: NAME { mri_only_load($1); } | mri_abs_name_list ',' NAME { mri_only_load($3); } ; script_file: { ldlex_both(); } ifile_list { ldlex_popstate(); } ; ifile_list: ifile_list ifile_p1 | ; ifile_p1: memory | sections | startup | high_level_library | low_level_library | floating_point_support | statement_anywhere | ';' | TARGET_K '(' NAME ')' { lang_add_target($3); } | SEARCH_DIR '(' filename ')' { ldfile_add_library_path($3); } | OUTPUT '(' filename ')' { lang_add_output($3, 1); } | OUTPUT_FORMAT '(' NAME ')' { lang_add_output_format($3, 1); } | OUTPUT_ARCH '(' NAME ')' { ldfile_set_output_arch($3); } | FORCE_COMMON_ALLOCATION { command_line.force_common_definition = true ; } | INPUT '(' input_list ')' | MAP '(' filename ')' { lang_add_map($3); } | INCLUDE filename { ldfile_open_command_file($2); } ifile_list END ; input_list: NAME { lang_add_input_file($1,lang_input_file_is_search_file_enum, (char *)NULL); } | input_list ',' NAME { lang_add_input_file($3,lang_input_file_is_search_file_enum, (char *)NULL); } | input_list NAME { lang_add_input_file($2,lang_input_file_is_search_file_enum, (char *)NULL); } ; sections: SECTIONS '{' sec_or_group_p1 '}' ; sec_or_group_p1: sec_or_group_p1 section | sec_or_group_p1 statement_anywhere | ; statement_anywhere: ENTRY '(' NAME ')' { lang_add_entry($3); } | assignment end ; file_NAME_list: NAME { lang_add_wild($1, current_file); } | file_NAME_list opt_comma NAME { lang_add_wild($3, current_file); } ; input_section_spec: NAME { lang_add_wild((char *)NULL, $1); } | '[' { current_file = (char *)NULL; } file_NAME_list ']' | NAME { current_file =$1; } '(' file_NAME_list ')' | '*' { current_file = (char *)NULL; } '(' file_NAME_list ')' ; statement: assignment end | CREATE_OBJECT_SYMBOLS { lang_add_attribute(lang_object_symbols_statement_enum); } | ';' | CONSTRUCTORS { lang_add_attribute(lang_constructors_statement_enum); } | input_section_spec | length '(' exp ')' { lang_add_data((int) $1,$3); } | FILL '(' exp ')' { lang_add_fill (exp_get_value_int($3, 0, "fill value", lang_first_phase_enum)); } ; statement_list: statement_list statement | statement ; statement_list_opt: /* empty */ | statement_list ; length: QUAD { $$ = $1; } | LONG { $$ = $1; } | SHORT { $$ = $1; } | BYTE { $$ = $1; } ; fill_opt: '=' mustbe_exp { $$ = exp_get_value_int($2, 0, "fill value", lang_first_phase_enum); } | { $$ = 0; } ; assign_op: PLUSEQ { $$ = '+'; } | MINUSEQ { $$ = '-'; } | MULTEQ { $$ = '*'; } | DIVEQ { $$ = '/'; } | LSHIFTEQ { $$ = LSHIFT; } | RSHIFTEQ { $$ = RSHIFT; } | ANDEQ { $$ = '&'; } | OREQ { $$ = '|'; } ; end: ';' | ',' ; assignment: NAME '=' mustbe_exp { lang_add_assignment(exp_assop($2,$1,$3)); } | NAME assign_op mustbe_exp { lang_add_assignment(exp_assop('=',$1,exp_binop($2,exp_nameop(NAME,$1),$3))); } ; opt_comma: ',' | ; memory: MEMORY '{' memory_spec memory_spec_list '}' ; memory_spec_list: memory_spec_list memory_spec | memory_spec_list ',' memory_spec | ; memory_spec: NAME { region = lang_memory_region_lookup($1); } attributes_opt ':' origin_spec opt_comma length_spec ; origin_spec: ORIGIN '=' mustbe_exp { region->current = region->origin = exp_get_vma($3, 0L,"origin", lang_first_phase_enum); } ; length_spec: LENGTH '=' mustbe_exp { region->length = exp_get_vma($3, ~((bfd_vma)0), "length", lang_first_phase_enum); } attributes_opt: '(' NAME ')' { lang_set_flags(®ion->flags, $2); } | ; startup: STARTUP '(' filename ')' { lang_startup($3); } ; high_level_library: HLL '(' high_level_library_NAME_list ')' | HLL '(' ')' { ldemul_hll((char *)NULL); } ; high_level_library_NAME_list: high_level_library_NAME_list opt_comma filename { ldemul_hll($3); } | filename { ldemul_hll($1); } ; low_level_library: SYSLIB '(' low_level_library_NAME_list ')' ; low_level_library_NAME_list: low_level_library_NAME_list opt_comma filename { ldemul_syslib($3); } | ; floating_point_support: FLOAT { lang_float(true); } | NOFLOAT { lang_float(false); } ; mustbe_exp: { ldlex_expression(); } exp { ldlex_popstate(); $$=$2;} ; exp : '-' exp %prec UNARY { $$ = exp_unop('-', $2); } | '(' exp ')' { $$ = $2; } | NEXT '(' exp ')' %prec UNARY { $$ = exp_unop((int) $1,$3); } | '!' exp %prec UNARY { $$ = exp_unop('!', $2); } | '+' exp %prec UNARY { $$ = $2; } | '~' exp %prec UNARY { $$ = exp_unop('~', $2);} | exp '*' exp { $$ = exp_binop('*', $1, $3); } | exp '/' exp { $$ = exp_binop('/', $1, $3); } | exp '%' exp { $$ = exp_binop('%', $1, $3); } | exp '+' exp { $$ = exp_binop('+', $1, $3); } | exp '-' exp { $$ = exp_binop('-' , $1, $3); } | exp LSHIFT exp { $$ = exp_binop(LSHIFT , $1, $3); } | exp RSHIFT exp { $$ = exp_binop(RSHIFT , $1, $3); } | exp EQ exp { $$ = exp_binop(EQ , $1, $3); } | exp NE exp { $$ = exp_binop(NE , $1, $3); } | exp LE exp { $$ = exp_binop(LE , $1, $3); } | exp GE exp { $$ = exp_binop(GE , $1, $3); } | exp '<' exp { $$ = exp_binop('<' , $1, $3); } | exp '>' exp { $$ = exp_binop('>' , $1, $3); } | exp '&' exp { $$ = exp_binop('&' , $1, $3); } | exp '^' exp { $$ = exp_binop('^' , $1, $3); } | exp '|' exp { $$ = exp_binop('|' , $1, $3); } | exp '?' exp ':' exp { $$ = exp_trinop('?' , $1, $3, $5); } | exp ANDAND exp { $$ = exp_binop(ANDAND , $1, $3); } | exp OROR exp { $$ = exp_binop(OROR , $1, $3); } | DEFINED '(' NAME ')' { $$ = exp_nameop(DEFINED, $3); } | INT { $$ = exp_intop($1); } | SIZEOF_HEADERS { $$ = exp_nameop(SIZEOF_HEADERS,0); } | SIZEOF '(' NAME ')' { $$ = exp_nameop(SIZEOF,$3); } | ADDR '(' NAME ')' { $$ = exp_nameop(ADDR,$3); } | ABSOLUTE '(' exp ')' { $$ = exp_unop(ABSOLUTE, $3); } | ALIGN_K '(' exp ')' { $$ = exp_unop(ALIGN_K,$3); } | NAME { $$ = exp_nameop(NAME,$1); } ; opt_at: AT '(' exp ')' { $$ = $3; } | { $$ = 0; } ; section: NAME { ldlex_expression(); } opt_exp_with_type opt_at { ldlex_popstate(); } '{' { lang_enter_output_section_statement($1,$3,typebits,0,0,0,$4); } statement_list_opt '}' {ldlex_expression();} fill_opt memspec_opt { ldlex_popstate(); lang_leave_output_section_statement($11, $12); } opt_comma ; type: NOLOAD { typebits = SEC_NEVER_LOAD; } | DSECT { typebits = 0; } | COPY { typebits = 0; } | INFO { typebits = 0; } | OVERLAY { typebits = 0; } | { typebits = SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS; } ; opt_exp_with_type: exp ':' { $$ = $1; typebits =0;} | exp '(' type ')' ':' { $$ = $1; } | ':' { $$= (etree_type *)NULL; typebits = 0; } | '(' type ')' ':' { $$= (etree_type *)NULL; } ; memspec_opt: '>' NAME { $$ = $2; } | { $$ = "*default*"; } ; %% void yyerror(arg) const char *arg; { if (error_index > 0 && error_index < ERROR_NAME_MAX) einfo("%P%F: %S %s in %s\n", arg, error_names[error_index-1]); else einfo("%P%F: %S %s\n", arg); }