/* A YACC grammer to parse a superset of the AT&T linker scripting languaue.
- Copyright (C) 1991, 1993 Free Software Foundation, Inc.
+ Copyright (C) 1991, 92, 93, 94, 95, 1996 Free Software Foundation, Inc.
Written by Steve Chamberlain of Cygnus Support (steve@cygnus.com).
This file is part of GNU ld.
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. */
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
%{
/*
#include "ldmisc.h"
#include "ldmain.h"
#include "mri.h"
+#include "ldlex.h"
+#ifndef YYDEBUG
#define YYDEBUG 1
+#endif
-static int typebits;
-
-static char *dirlist_ptr;
+static enum section_type sectype;
lang_memory_region_type *region;
char *name;
int token;
union etree_union *etree;
+ struct phdr_info
+ {
+ boolean filehdr;
+ boolean phdrs;
+ union etree_union *at;
+ union etree_union *flags;
+ } phdr;
+ struct lang_nocrossref *nocrossref;
}
-%type <etree> exp opt_exp_with_type mustbe_exp opt_at
+%type <etree> exp opt_exp_with_type mustbe_exp opt_at phdr_type phdr_val
%type <integer> fill_opt
-%type <name> memspec_opt
+%type <name> memspec_opt casesymlist
%token <integer> INT
-%token <name> NAME
-%type <integer> length
+%token <name> NAME LNAME
+%type <integer> length
+%type <phdr> phdr_qualifiers
+%type <nocrossref> nocrossref_list
%right <token> PLUSEQ MINUSEQ MULTEQ DIVEQ '=' LSHIFTEQ RSHIFTEQ ANDEQ OREQ
%right <token> '?' ':'
%right UNARY
%token END
%left <token> '('
-%token <token> ALIGN_K BLOCK QUAD LONG SHORT BYTE
-%token SECTIONS
+%token <token> ALIGN_K BLOCK BIND QUAD LONG SHORT BYTE
+%token SECTIONS PHDRS
%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 <integer> SIZEOF NEXT ADDR
-%token STARTUP HLL SYSLIB FLOAT NOFLOAT
+%token NAME LNAME DEFINED TARGET_K SEARCH_DIR MAP ENTRY
+%token <integer> SIZEOF NEXT ADDR LOADADDR
+%token STARTUP HLL SYSLIB FLOAT NOFLOAT NOCROSSREFS
%token ORIGIN FILL
-%token LENGTH CREATE_OBJECT_SYMBOLS INPUT OUTPUT CONSTRUCTORS
-%token ALIGNMOD AT
-%type <token> assign_op
+%token LENGTH CREATE_OBJECT_SYMBOLS INPUT GROUP OUTPUT CONSTRUCTORS
+%token ALIGNMOD AT PROVIDE
+%type <token> assign_op atype
%type <name> filename
%token CHIP LIST SECT ABSOLUTE LOAD NEWLINE ENDWORD ORDER NAMEWORD
%token FORMAT PUBLIC DEFSYMEND BASE ALIAS TRUNCATE REL
-%token INPUT_SCRIPT INPUT_MRI_SCRIPT INPUT_DEFSYM
+%token INPUT_SCRIPT INPUT_MRI_SCRIPT INPUT_DEFSYM CASE EXTERN START
-%{
-/* For byacc, this has to come after INPUT_SCRIPT et al. are defined. */
-#include "ldlex.h"
-%}
%%
file:
/* SYNTAX WITHIN AN MRI SCRIPT FILE */
mri_script_file:
- { ldlex_mri_script();
- PUSH_ERROR("MRI style script");
+ {
+ ldlex_mri_script ();
+ PUSH_ERROR ("MRI style script");
}
mri_script_lines
- { ldlex_popstate();
- POP_ERROR();
+ {
+ ldlex_popstate ();
+ mri_draw_tree ();
+ POP_ERROR ();
}
;
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
+ | PUBLIC NAME '=' exp
{ mri_public($2, $4); }
- | PUBLIC NAME ',' exp
+ | PUBLIC NAME ',' exp
{ mri_public($2, $4); }
| PUBLIC NAME exp
{ mri_public($2, $3); }
| FORMAT NAME
{ mri_format($2); }
- | SECT NAME ',' exp
+ | SECT NAME ',' exp
{ mri_output_section($2, $4);}
| SECT NAME exp
{ mri_output_section($2, $3);}
{ mri_output_section($2, $4);}
| ALIGN_K NAME '=' exp
{ mri_align($2,$4); }
+ | ALIGN_K NAME ',' exp
+ { mri_align($2,$4); }
| ALIGNMOD NAME '=' exp
{ mri_alignmod($2,$4); }
+ | ALIGNMOD NAME ',' exp
+ { mri_alignmod($2,$4); }
| ABSOLUTE mri_abs_name_list
| LOAD mri_load_name_list
| NAMEWORD NAME
{ mri_base($2); }
| TRUNCATE INT
{ mri_truncate((unsigned int) $2); }
+ | CASE casesymlist
+ | EXTERN extern_name_list
+ | INCLUDE filename
+ { ldfile_open_command_file ($2); } mri_script_lines END
+ | START NAME
+ { lang_add_entry ($2, false); }
|
;
{ mri_only_load($3); }
;
+casesymlist:
+ /* empty */ { $$ = NULL; }
+ | NAME
+ | casesymlist ',' NAME
+ ;
+
+extern_name_list:
+ NAME
+ { ldlang_add_undef ($1); }
+ | extern_name_list ',' NAME
+ { ldlang_add_undef ($3); }
+ ;
+
script_file:
{
ldlex_both();
ifile_p1:
memory
| sections
+ | phdrs
| startup
| high_level_library
| low_level_library
| TARGET_K '(' NAME ')'
{ lang_add_target($3); }
| SEARCH_DIR '(' filename ')'
- { ldfile_add_library_path($3); }
+ { ldfile_add_library_path ($3, false); }
| OUTPUT '(' filename ')'
{ lang_add_output($3, 1); }
| OUTPUT_FORMAT '(' NAME ')'
- { lang_add_output_format($3, 1); }
+ { lang_add_output_format ($3, (char *) NULL,
+ (char *) NULL, 1); }
+ | OUTPUT_FORMAT '(' NAME ',' NAME ',' NAME ')'
+ { lang_add_output_format ($3, $5, $7, 1); }
| OUTPUT_ARCH '(' NAME ')'
{ ldfile_set_output_arch($3); }
| FORCE_COMMON_ALLOCATION
{ command_line.force_common_definition = true ; }
| INPUT '(' input_list ')'
+ | GROUP
+ { lang_enter_group (); }
+ '(' input_list ')'
+ { lang_leave_group (); }
| MAP '(' filename ')'
{ lang_add_map($3); }
| INCLUDE filename
{ ldfile_open_command_file($2); } ifile_list END
+ | NOCROSSREFS '(' nocrossref_list ')'
+ {
+ lang_add_nocrossref ($3);
+ }
;
input_list:
| input_list NAME
{ lang_add_input_file($2,lang_input_file_is_search_file_enum,
(char *)NULL); }
+ | LNAME
+ { lang_add_input_file($1,lang_input_file_is_l_enum,
+ (char *)NULL); }
+ | input_list ',' LNAME
+ { lang_add_input_file($3,lang_input_file_is_l_enum,
+ (char *)NULL); }
+ | input_list LNAME
+ { lang_add_input_file($2,lang_input_file_is_l_enum,
+ (char *)NULL); }
;
sections:
statement_anywhere:
ENTRY '(' NAME ')'
- { lang_add_entry($3); }
+ { lang_add_entry ($3, false); }
| assignment end
;
+/* The '*' and '?' cases are there because the lexer returns them as
+ separate tokens rather than as NAME. */
file_NAME_list:
NAME
- { lang_add_wild($1, current_file); }
+ { lang_add_wild ($1, current_file); }
+ | '*'
+ { lang_add_wild ("*", current_file); }
+ | '?'
+ { lang_add_wild ("?", current_file); }
| file_NAME_list opt_comma NAME
- { lang_add_wild($3, current_file); }
+ { lang_add_wild ($3, current_file); }
+ | file_NAME_list opt_comma '*'
+ { lang_add_wild ("*", current_file); }
+ | file_NAME_list opt_comma '?'
+ { lang_add_wild ("?", current_file); }
;
input_section_spec:
']'
| NAME
{
- current_file =$1;
+ current_file = $1;
+ }
+ '(' file_NAME_list ')'
+ | '?'
+ /* This case is needed because the lexer returns a
+ single question mark as '?' rather than NAME. */
+ {
+ current_file = "?";
}
'(' file_NAME_list ')'
| '*'
lang_add_attribute(lang_constructors_statement_enum);
}
| input_section_spec
- | length '(' exp ')'
+ | length '(' mustbe_exp ')'
{
lang_add_data((int) $1,$3);
}
- | FILL '(' exp ')'
+ | FILL '(' mustbe_exp ')'
{
lang_add_fill
(exp_get_value_int($3,
assignment:
NAME '=' mustbe_exp
{
- lang_add_assignment(exp_assop($2,$1,$3));
+ 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)));
+ lang_add_assignment (exp_assop ('=', $1,
+ exp_binop ($2,
+ exp_nameop (NAME,
+ $1),
+ $3)));
+ }
+ | PROVIDE '(' NAME '=' mustbe_exp ')'
+ {
+ lang_add_assignment (exp_provide ($3, $5));
}
-
;
{ lang_float(false); }
;
+nocrossref_list:
+ /* empty */
+ {
+ $$ = NULL;
+ }
+ | NAME nocrossref_list
+ {
+ struct lang_nocrossref *n;
+
+ n = (struct lang_nocrossref *) xmalloc (sizeof *n);
+ n->name = $1;
+ n->next = $2;
+ $$ = n;
+ }
+ | NAME ',' nocrossref_list
+ {
+ struct lang_nocrossref *n;
+
+ n = (struct lang_nocrossref *) xmalloc (sizeof *n);
+ n->name = $1;
+ n->next = $3;
+ $$ = n;
+ }
+ ;
mustbe_exp: { ldlex_expression(); }
exp
{ $$ = exp_nameop(SIZEOF,$3); }
| ADDR '(' NAME ')'
{ $$ = exp_nameop(ADDR,$3); }
+ | LOADADDR '(' NAME ')'
+ { $$ = exp_nameop(LOADADDR,$3); }
| ABSOLUTE '(' exp ')'
{ $$ = exp_unop(ABSOLUTE, $3); }
| ALIGN_K '(' exp ')'
{ $$ = exp_unop(ALIGN_K,$3); }
+ | BLOCK '(' exp ')'
+ { $$ = exp_unop(ALIGN_K,$3); }
| NAME
{ $$ = exp_nameop(NAME,$1); }
;
section: NAME { ldlex_expression(); }
opt_exp_with_type
- opt_at { ldlex_popstate(); }
+ opt_at { ldlex_popstate (); ldlex_script (); }
'{'
{
- lang_enter_output_section_statement($1,$3,typebits,0,0,0,$4);
+ lang_enter_output_section_statement($1, $3,
+ sectype,
+ 0, 0, 0, $4);
}
statement_list_opt
- '}' {ldlex_expression();} fill_opt memspec_opt
+ '}' { ldlex_popstate (); ldlex_expression (); }
+ memspec_opt phdr_opt fill_opt
{
ldlex_popstate();
- lang_leave_output_section_statement($11, $12);
+ lang_leave_output_section_statement($13, $11);
}
-opt_comma
-
+ opt_comma
+ | /* The GROUP case is just enough to support the gcc
+ svr3.ifile script. It is not intended to be full
+ support. I'm not even sure what GROUP is supposed
+ to mean. */
+ GROUP { ldlex_expression (); }
+ opt_exp_with_type
+ {
+ ldlex_popstate ();
+ lang_add_assignment (exp_assop ('=', ".", $3));
+ }
+ '{' sec_or_group_p1 '}'
;
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; }
+ NOLOAD { sectype = noload_section; }
+ | DSECT { sectype = dsect_section; }
+ | COPY { sectype = copy_section; }
+ | INFO { sectype = info_section; }
+ | OVERLAY { sectype = overlay_section; }
;
+atype:
+ '(' type ')'
+ | /* EMPTY */ { sectype = normal_section; }
+ ;
opt_exp_with_type:
- exp ':' { $$ = $1; typebits =0;}
- | exp '(' type ')' ':' { $$ = $1; }
- | ':' { $$= (etree_type *)NULL; typebits = 0; }
- | '(' type ')' ':' { $$= (etree_type *)NULL; }
+ exp atype ':' { $$ = $1; }
+ | atype ':' { $$ = (etree_type *)NULL; }
+ | /* The BIND cases are to support the gcc svr3.ifile
+ script. They aren't intended to implement full
+ support for the BIND keyword. I'm not even sure
+ what BIND is supposed to mean. */
+ BIND '(' exp ')' atype ':' { $$ = $3; }
+ | BIND '(' exp ')' BLOCK '(' exp ')' atype ':'
+ { $$ = $3; }
;
memspec_opt:
{ $$ = $2; }
| { $$ = "*default*"; }
;
+
+phdr_opt:
+ /* empty */
+ | phdr_opt ':' NAME
+ {
+ lang_section_in_phdr ($3);
+ }
+ ;
+
+phdrs:
+ PHDRS '{' phdr_list '}'
+ ;
+
+phdr_list:
+ /* empty */
+ | phdr_list phdr
+ ;
+
+phdr:
+ NAME { ldlex_expression (); }
+ phdr_type phdr_qualifiers { ldlex_popstate (); }
+ ';'
+ {
+ lang_new_phdr ($1, $3, $4.filehdr, $4.phdrs, $4.at,
+ $4.flags);
+ }
+ ;
+
+phdr_type:
+ exp
+ {
+ $$ = $1;
+
+ if ($1->type.node_class == etree_name
+ && $1->type.node_code == NAME)
+ {
+ const char *s;
+ unsigned int i;
+ static const char * const phdr_types[] =
+ {
+ "PT_NULL", "PT_LOAD", "PT_DYNAMIC",
+ "PT_INTERP", "PT_NOTE", "PT_SHLIB",
+ "PT_PHDR"
+ };
+
+ s = $1->name.name;
+ for (i = 0;
+ i < sizeof phdr_types / sizeof phdr_types[0];
+ i++)
+ if (strcmp (s, phdr_types[i]) == 0)
+ {
+ $$ = exp_intop (i);
+ break;
+ }
+ }
+ }
+ ;
+
+phdr_qualifiers:
+ /* empty */
+ {
+ memset (&$$, 0, sizeof (struct phdr_info));
+ }
+ | NAME phdr_val phdr_qualifiers
+ {
+ $$ = $3;
+ if (strcmp ($1, "FILEHDR") == 0 && $2 == NULL)
+ $$.filehdr = true;
+ else if (strcmp ($1, "PHDRS") == 0 && $2 == NULL)
+ $$.phdrs = true;
+ else if (strcmp ($1, "FLAGS") == 0 && $2 != NULL)
+ $$.flags = $2;
+ else
+ einfo ("%X%P:%S: PHDRS syntax error at `%s'\n", $1);
+ }
+ | AT '(' exp ')' phdr_qualifiers
+ {
+ $$ = $5;
+ $$.at = $3;
+ }
+ ;
+
+phdr_val:
+ /* empty */
+ {
+ $$ = NULL;
+ }
+ | '(' exp ')'
+ {
+ $$ = $2;
+ }
+ ;
+
%%
void
yyerror(arg)
const char *arg;
{
+ if (ldfile_assumed_script)
+ einfo ("%P:%s: file format not recognized; treating as linker script\n",
+ ldfile_input_filename);
if (error_index > 0 && error_index < ERROR_NAME_MAX)
- einfo("%P%F: %S %s in %s\n", arg, error_names[error_index-1]);
+ einfo ("%P%F:%S: %s in %s\n", arg, error_names[error_index-1]);
else
- einfo("%P%F: %S %s\n", arg);
+ einfo ("%P%F:%S: %s\n", arg);
}