From c7fb7dfd794700bf6977a907a8612e9b644e4fe4 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Mon, 4 Jul 2016 21:53:55 +0200 Subject: o Improved parser. --- cmake/elfinfo/.gitignore | 1 + cmake/elfinfo/GnuLd.g4 | 1500 -------------------- cmake/elfinfo/GnuLdLexer.g4 | 209 +++ cmake/elfinfo/GnuLdParser.g4 | 1345 ++++++++++++++++++ cmake/elfinfo/g4.sh | 11 + cmake/elfinfo/pom.xml | 57 +- cmake/elfinfo/src/main/antlr4/Test1Lexer.g4 | 22 + cmake/elfinfo/src/main/antlr4/Test1Parser.g4 | 23 + .../io/trygvis/ld/CollectingErrorListener.java | 17 + .../src/main/java/io/trygvis/ld/LdScript.java | 73 +- .../java/io/trygvis/ld/StringGnuLdVisitor.java | 59 + .../main/test/io/trygvis/ld/FullScriptsTest.java | 34 + .../test/io/trygvis/ld/InputSectionSpecTest.java | 38 + .../src/main/test/io/trygvis/ld/NameTest.java | 50 + .../ld/SystemOutReportingErrorListener.java | 32 + .../main/test/io/trygvis/ld/test1/Test1Test.java | 102 ++ 16 files changed, 2042 insertions(+), 1531 deletions(-) delete mode 100644 cmake/elfinfo/GnuLd.g4 create mode 100644 cmake/elfinfo/GnuLdLexer.g4 create mode 100644 cmake/elfinfo/GnuLdParser.g4 create mode 100755 cmake/elfinfo/g4.sh create mode 100644 cmake/elfinfo/src/main/antlr4/Test1Lexer.g4 create mode 100644 cmake/elfinfo/src/main/antlr4/Test1Parser.g4 create mode 100644 cmake/elfinfo/src/main/java/io/trygvis/ld/CollectingErrorListener.java create mode 100644 cmake/elfinfo/src/main/java/io/trygvis/ld/StringGnuLdVisitor.java create mode 100644 cmake/elfinfo/src/main/test/io/trygvis/ld/FullScriptsTest.java create mode 100644 cmake/elfinfo/src/main/test/io/trygvis/ld/InputSectionSpecTest.java create mode 100644 cmake/elfinfo/src/main/test/io/trygvis/ld/NameTest.java create mode 100644 cmake/elfinfo/src/main/test/io/trygvis/ld/SystemOutReportingErrorListener.java create mode 100644 cmake/elfinfo/src/main/test/io/trygvis/ld/test1/Test1Test.java diff --git a/cmake/elfinfo/.gitignore b/cmake/elfinfo/.gitignore index a2d5086..db5702a 100644 --- a/cmake/elfinfo/.gitignore +++ b/cmake/elfinfo/.gitignore @@ -3,3 +3,4 @@ *.iws *.jar /antlr +*.tokens diff --git a/cmake/elfinfo/GnuLd.g4 b/cmake/elfinfo/GnuLd.g4 deleted file mode 100644 index da7b01f..0000000 --- a/cmake/elfinfo/GnuLd.g4 +++ /dev/null @@ -1,1500 +0,0 @@ -grammar GnuLd; - -/* -TODO: check right associative annotations -*/ - -/* -%token INT -%token NAME LNAME -*/ - -/* -%right UNARY -%token END -%left '(' -*/ - - -file: - /*INPUT_SCRIPT*/ script_file - | /*INPUT_MRI_SCRIPT*/ mri_script_file - | /*INPUT_VERSION_SCRIPT*/ version_script_file - | /*INPUT_DYNAMIC_LIST*/ dynamic_list_file - | /*INPUT_DEFSYM*/ defsym_expr - ; - -filename: NAME; - - -defsym_expr: -// { ldlex_defsym(); } - NAME '=' exp -// { -// ldlex_popstate(); -// lang_add_assignment (exp_defsym ($2, $4)); -// } - ; - -/* SYNTAX WITHIN AN MRI SCRIPT FILE */ -mri_script_file: -// { -// ldlex_mri_script (); -// PUSH_ERROR (_("MRI style script")); -// } - mri_script_lines -// { -// ldlex_popstate (); -// mri_draw_tree (); -// 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 /*{ - 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); } - | 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_name($2); } - | ALIAS NAME ',' NAME - // { mri_alias($2,$4,0);} - | ALIAS NAME ',' INT - // { mri_alias ($2, 0, (int) $4.integer); } - | BASE exp - // { mri_base($2); } - | TRUNCATE INT - // { mri_truncate ((unsigned int) $2.integer); } - | CASE casesymlist - | EXTERN extern_name_list - | INCLUDE filename - // { ldlex_script (); ldfile_open_command_file($2); } - mri_script_lines END - // { ldlex_popstate (); } - | START NAME - // { lang_add_entry ($2, FALSE); } - | - ; - -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); } - ; - -casesymlist: - /* empty */ // { $$ = NULL; } - | NAME - | casesymlist ',' NAME - ; - -/* Parsed as expressions so that commas separate entries */ -extern_name_list: - // { ldlex_expression (); } - extern_name_list_body - // { ldlex_popstate (); } - ; - -extern_name_list_body: - NAME - // { ldlang_add_undef ($1, FALSE); } - | extern_name_list_body NAME - // { ldlang_add_undef ($2, FALSE); } - | extern_name_list_body ',' NAME - // { ldlang_add_undef ($3, FALSE); } - ; - -script_file: - // { ldlex_both(); } - ifile_list - // { ldlex_popstate(); } - ; - -ifile_list: - ifile_list ifile_p1 - | - ; - - -ifile_p1: - memory - | sections - | phdrs - | startup - | high_level_library - | low_level_library - | floating_point_support - | statement_anywhere - | version - | ';' - | TARGET_K '(' NAME ')' - // { lang_add_target($3); } - | SEARCH_DIR '(' filename ')' - // { ldfile_add_library_path ($3, FALSE); } - | OUTPUT '(' filename ')' - // { lang_add_output($3, 1); } - | OUTPUT_FORMAT '(' NAME ')' - // { 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, bfd_arch_unknown); } - | FORCE_COMMON_ALLOCATION - // { command_line.force_common_definition = TRUE ; } - | INHIBIT_COMMON_ALLOCATION - // { command_line.inhibit_common_definition = TRUE ; } - | INPUT '(' input_list ')' - | GROUP - // { lang_enter_group (); } - '(' input_list ')' - // { lang_leave_group (); } - | MAP '(' filename ')' - // { lang_add_map($3); } - | INCLUDE filename - // { ldlex_script (); ldfile_open_command_file($2); } - ifile_list END - // { ldlex_popstate (); } - | NOCROSSREFS '(' nocrossref_list ')' - // { - // lang_add_nocrossref ($3); - // } - | NOCROSSREFS_TO '(' nocrossref_list ')' - // { - // lang_add_nocrossref_to ($3); - // } - | EXTERN '(' extern_name_list ')' - | INSERT_K AFTER NAME - // { lang_add_insert ($3, 0); } - | INSERT_K BEFORE NAME - // { lang_add_insert ($3, 1); } - | REGION_ALIAS '(' NAME ',' NAME ')' - // { lang_memory_region_alias ($3, $5); } - | LD_FEATURE '(' NAME ')' - // { lang_ld_feature ($3); } - ; - -input_list: - // { ldlex_inputlist(); } - input_list1 - // { ldlex_popstate(); } - ; - -input_list1: - NAME - // { lang_add_input_file($1,lang_input_file_is_search_file_enum, - // (char *)NULL); } - | input_list1 ',' NAME - // { lang_add_input_file($3,lang_input_file_is_search_file_enum, - // (char *)NULL); } - | input_list1 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_list1 ',' LNAME - // { lang_add_input_file($3,lang_input_file_is_l_enum, - // (char *)NULL); } - | input_list1 LNAME - // { lang_add_input_file($2,lang_input_file_is_l_enum, - // (char *)NULL); } - | AS_NEEDED '(' - // { $$ = input_flags.add_DT_NEEDED_for_regular; - // input_flags.add_DT_NEEDED_for_regular = TRUE; } - // input_list1 ')' - // { input_flags.add_DT_NEEDED_for_regular = $3; } - | input_list1 ',' AS_NEEDED '(' - // { $$ = input_flags.add_DT_NEEDED_for_regular; - // input_flags.add_DT_NEEDED_for_regular = TRUE; } - // input_list1 ')' - // { input_flags.add_DT_NEEDED_for_regular = $5; } - | input_list1 AS_NEEDED '(' - // { $$ = input_flags.add_DT_NEEDED_for_regular; - // input_flags.add_DT_NEEDED_for_regular = TRUE; } - // input_list1 ')' - // { input_flags.add_DT_NEEDED_for_regular = $4; } - ; - -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, FALSE); } - | assignment end - | ASSERT_K /*{ldlex_expression ();}*/ '(' exp ',' NAME ')' - // { ldlex_popstate (); - // lang_add_assignment (exp_assert ($4, $6)); } - ; - -/* The '*' and '?' cases are there because the lexer returns them as - separate tokens rather than as NAME. */ -wildcard_name: - NAME - // { - // $$ = $1; - // } - | '*' - // { - // $$ = "*"; - // } - | '?' - // { - // $$ = "?"; - // } - ; - -wildcard_spec: - wildcard_name -// { -// $$.name = $1; -// $$.sorted = none; -// $$.exclude_name_list = NULL; -// $$.section_flag_list = NULL; -// } - | EXCLUDE_FILE '(' exclude_name_list ')' wildcard_name -// { -// $$.name = $5; -// $$.sorted = none; -// $$.exclude_name_list = $3; -// $$.section_flag_list = NULL; -// } - | SORT_BY_NAME '(' wildcard_name ')' -// { -// $$.name = $3; -// $$.sorted = by_name; -// $$.exclude_name_list = NULL; -// $$.section_flag_list = NULL; -// } - | SORT_BY_ALIGNMENT '(' wildcard_name ')' -// { -// $$.name = $3; -// $$.sorted = by_alignment; -// $$.exclude_name_list = NULL; -// $$.section_flag_list = NULL; -// } - | SORT_NONE '(' wildcard_name ')' -// { -// $$.name = $3; -// $$.sorted = by_none; -// $$.exclude_name_list = NULL; -// $$.section_flag_list = NULL; -// } - | SORT_BY_NAME '(' SORT_BY_ALIGNMENT '(' wildcard_name ')' ')' -// { -// $$.name = $5; -// $$.sorted = by_name_alignment; -// $$.exclude_name_list = NULL; -// $$.section_flag_list = NULL; -// } - | SORT_BY_NAME '(' SORT_BY_NAME '(' wildcard_name ')' ')' -// { -// $$.name = $5; -// $$.sorted = by_name; -// $$.exclude_name_list = NULL; -// $$.section_flag_list = NULL; -// } - | SORT_BY_ALIGNMENT '(' SORT_BY_NAME '(' wildcard_name ')' ')' -// { -// $$.name = $5; -// $$.sorted = by_alignment_name; -// $$.exclude_name_list = NULL; -// $$.section_flag_list = NULL; -// } - | SORT_BY_ALIGNMENT '(' SORT_BY_ALIGNMENT '(' wildcard_name ')' ')' -// { -// $$.name = $5; -// $$.sorted = by_alignment; -// $$.exclude_name_list = NULL; -// $$.section_flag_list = NULL; -// } - | SORT_BY_NAME '(' EXCLUDE_FILE '(' exclude_name_list ')' wildcard_name ')' -// { -// $$.name = $7; -// $$.sorted = by_name; -// $$.exclude_name_list = $5; -// $$.section_flag_list = NULL; -// } - | SORT_BY_INIT_PRIORITY '(' wildcard_name ')' -// { -// $$.name = $3; -// $$.sorted = by_init_priority; -// $$.exclude_name_list = NULL; -// $$.section_flag_list = NULL; -// } - ; - -sect_flag_list: NAME -// { -// struct flag_info_list *n; -// n = ((struct flag_info_list *) xmalloc (sizeof *n)); -// if ($1[0] == '!') -// { -// n->with = without_flags; -// n->name = &$1[1]; -// } -// else -// { -// n->with = with_flags; -// n->name = $1; -// } -// n->valid = FALSE; -// n->next = NULL; -// $$ = n; -// } - | sect_flag_list '&' NAME -// { -// struct flag_info_list *n; -// n = ((struct flag_info_list *) xmalloc (sizeof *n)); -// if ($3[0] == '!') -// { -// n->with = without_flags; -// n->name = &$3[1]; -// } -// else -// { -// n->with = with_flags; -// n->name = $3; -// } -// n->valid = FALSE; -// n->next = $1; -// $$ = n; -// } - ; - -sect_flags: - /*not used by antlr: INPUT_SECTION_FLAGS*/ '(' sect_flag_list ')' -// { -// struct flag_info *n; -// n = ((struct flag_info *) xmalloc (sizeof *n)); -// n->flag_list = $3; -// n->flags_initialized = FALSE; -// n->not_with_flags = 0; -// n->only_with_flags = 0; -// $$ = n; -// } - ; - -exclude_name_list: - exclude_name_list wildcard_name -// { -// struct name_list *tmp; -// tmp = (struct name_list *) xmalloc (sizeof *tmp); -// tmp->name = $2; -// tmp->next = $1; -// $$ = tmp; -// } - | - wildcard_name -// { -// struct name_list *tmp; -// tmp = (struct name_list *) xmalloc (sizeof *tmp); -// tmp->name = $1; -// tmp->next = NULL; -// $$ = tmp; -// } - ; - -file_NAME_list: - file_NAME_list opt_comma wildcard_spec -// { -// struct wildcard_list *tmp; -// tmp = (struct wildcard_list *) xmalloc (sizeof *tmp); -// tmp->next = $1; -// tmp->spec = $3; -// $$ = tmp; -// } - | - wildcard_spec -// { -// struct wildcard_list *tmp; -// tmp = (struct wildcard_list *) xmalloc (sizeof *tmp); -// tmp->next = NULL; -// tmp->spec = $1; -// $$ = tmp; -// } - ; - -input_section_spec_no_keep: - NAME -// { -// struct wildcard_spec tmp; -// tmp.name = $1; -// tmp.exclude_name_list = NULL; -// tmp.sorted = none; -// tmp.section_flag_list = NULL; -// lang_add_wild (&tmp, NULL, ldgram_had_keep); -// } - | sect_flags NAME -// { -// struct wildcard_spec tmp; -// tmp.name = $2; -// tmp.exclude_name_list = NULL; -// tmp.sorted = none; -// tmp.section_flag_list = $1; -// lang_add_wild (&tmp, NULL, ldgram_had_keep); -// } - | '[' file_NAME_list ']' -// { -// lang_add_wild (NULL, $2, ldgram_had_keep); -// } - | sect_flags '[' file_NAME_list ']' -// { -// struct wildcard_spec tmp; -// tmp.name = NULL; -// tmp.exclude_name_list = NULL; -// tmp.sorted = none; -// tmp.section_flag_list = $1; -// lang_add_wild (&tmp, $3, ldgram_had_keep); -// } - | wildcard_spec '(' file_NAME_list ')' -// { -// lang_add_wild (&$1, $3, ldgram_had_keep); -// } - | sect_flags wildcard_spec '(' file_NAME_list ')' -// { -// $2.section_flag_list = $1; -// lang_add_wild (&$2, $4, ldgram_had_keep); -// } - ; - -input_section_spec: - input_section_spec_no_keep - | KEEP '(' -// { ldgram_had_keep = TRUE; } - input_section_spec_no_keep ')' -// { ldgram_had_keep = FALSE; } - ; - -statement: - assignment end - | CREATE_OBJECT_SYMBOLS -// { -// lang_add_attribute(lang_object_symbols_statement_enum); -// } - | ';' - | CONSTRUCTORS -// { -// -// lang_add_attribute(lang_constructors_statement_enum); -// } - | SORT_BY_NAME '(' CONSTRUCTORS ')' -// { -// constructors_sorted = TRUE; -// lang_add_attribute (lang_constructors_statement_enum); -// } - | input_section_spec - | length '(' mustbe_exp ')' -// { -// lang_add_data ((int) $1, $3); -// } - - | FILL '(' fill_exp ')' -// { -// lang_add_fill ($3); -// } - | ASSERT_K /*{ldlex_expression ();}*/ '(' exp ',' NAME ')' end -// { ldlex_popstate (); -// lang_add_assignment (exp_assert ($4, $6)); } - | INCLUDE filename -// { ldlex_script (); ldfile_open_command_file($2); } - statement_list_opt END -// { ldlex_popstate (); } - ; - -statement_list: - statement_list statement - | statement - ; - -statement_list_opt: - /* empty */ - | statement_list - ; - -length: - QUAD -// { $$ = $1; } - | SQUAD -// { $$ = $1; } - | LONG -// { $$ = $1; } - | SHORT -// { $$ = $1; } - | BYTE -// { $$ = $1; } - ; - -fill_exp: - mustbe_exp -// { -// $$ = exp_get_fill ($1, 0, "fill value"); -// } - ; - -fill_opt: - '=' fill_exp - // { $$ = $2; } - | // { $$ = (fill_type *) 0; } - ; - -assign_op: - PLUSEQ -// { $$ = '+'; } - | MINUSEQ -// { $$ = '-'; } - | MULTEQ -// { $$ = '*'; } - | DIVEQ -// { $$ = '/'; } - | LSHIFTEQ -// { $$ = LSHIFT; } - | RSHIFTEQ -// { $$ = RSHIFT; } - | ANDEQ -// { $$ = '&'; } - | OREQ -// { $$ = '|'; } - - ; - -end: ';' | ',' - ; - - -assignment: - NAME '=' mustbe_exp -// { -// lang_add_assignment (exp_assign ($1, $3, FALSE)); -// } - | NAME assign_op mustbe_exp -// { -// lang_add_assignment (exp_assign ($1, -// exp_binop ($2, -// exp_nameop (NAME, -// $1), -// $3), FALSE)); -// } - | HIDDEN_ '(' NAME '=' mustbe_exp ')' -// { -// lang_add_assignment (exp_assign ($3, $5, TRUE)); -// } - | PROVIDE '(' NAME '=' mustbe_exp ')' -// { -// lang_add_assignment (exp_provide ($3, $5, FALSE)); -// } - | PROVIDE_HIDDEN '(' NAME '=' mustbe_exp ')' -// { -// lang_add_assignment (exp_provide ($3, $5, TRUE)); -// } - ; - - -opt_comma: - ',' | ; - - -memory: - MEMORY '{' memory_spec_list_opt '}' - ; - -memory_spec_list_opt: memory_spec_list | ; - -memory_spec_list: - memory_spec_list opt_comma memory_spec - | memory_spec - ; - - -memory_spec: NAME -// { region = lang_memory_region_lookup ($1, TRUE); } - attributes_opt ':' - origin_spec opt_comma length_spec -// {} - | INCLUDE filename -// { ldlex_script (); ldfile_open_command_file($2); } - memory_spec_list_opt END -// { ldlex_popstate (); } - ; - -origin_spec: - ORIGIN '=' mustbe_exp -// { -// region->origin_exp = $3; -// region->current = region->origin; -// } - ; - -length_spec: - LENGTH '=' mustbe_exp -// { -// region->length_exp = $3; -// } - ; - -attributes_opt: - /* empty */ - /* { *//* dummy action to avoid bison 1.25 error message *//* } - |*/ '(' attributes_list ')' - ; - -attributes_list: - attributes_string - | attributes_list attributes_string - ; - -attributes_string: - NAME # attributeNormal -// { lang_set_flags (region, $1, 0); } - | '!' NAME # attributeInverted -// { lang_set_flags (region, $2, 1); } - ; - -/* -This would be best but the tokenizer would have to be made context sensitive which is too much work given how -easy it is to check the flags after parsing. - -attributes_string : - attribute - | '!' attribute # attributeInverted - ; - -attribute: 'r' | 'w' | 'x' | 'a' | 'i' | 'l' ; -*/ -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); } - ; - -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 - // { ldlex_popstate (); $$=$2;} - ; - -exp : - '-' exp # expNegate // TODO: %prec UNARY -// { $$ = exp_unop ('-', $2); } - | '(' exp ')' # expParen -// { $$ = $2; } - | NEXT '(' exp ')' # expNextParen // TODO: %prec UNARY -// { $$ = exp_unop ((int) $1,$3); } - | '!' exp # expInvert // TODO: %prec UNARY -// { $$ = exp_unop ('!', $2); } - | '+' exp # expPlus // TODO: %prec UNARY -// { $$ = $2; } - | '~' exp # expMinus // TODO: %prec UNARY -// { $$ = exp_unop ('~', $2);} - | exp '*' exp # expMul -// { $$ = exp_binop ('*', $1, $3); } - | exp '/' exp # expDiv -// { $$ = exp_binop ('/', $1, $3); } - | exp '%' exp # expMod -// { $$ = exp_binop ('%', $1, $3); } - | exp '+' exp # expAdd -// { $$ = exp_binop ('+', $1, $3); } - | exp '-' exp # expSub -// { $$ = exp_binop ('-' , $1, $3); } - | exp LSHIFT exp # expLshift -// { $$ = exp_binop (LSHIFT , $1, $3); } - | exp RSHIFT exp # expRshift -// { $$ = exp_binop (RSHIFT , $1, $3); } - | exp EQ exp # expEq -// { $$ = exp_binop (EQ , $1, $3); } - | exp NE exp # expNe -// { $$ = exp_binop (NE , $1, $3); } - | exp LE exp # expLe -// { $$ = exp_binop (LE , $1, $3); } - | exp GE exp # expGe -// { $$ = exp_binop (GE , $1, $3); } - | exp '<' exp # expGt -// { $$ = exp_binop ('<' , $1, $3); } - | exp '>' exp # expLt -// { $$ = exp_binop ('>' , $1, $3); } - | exp '&' exp # expAnd -// { $$ = exp_binop ('&' , $1, $3); } - | exp '^' exp # expXor -// { $$ = exp_binop ('^' , $1, $3); } - | exp '|' exp # expOr -// { $$ = exp_binop ('|' , $1, $3); } - | exp '?' exp ':' exp # expTrinary -// { $$ = exp_trinop ('?' , $1, $3, $5); } - | exp ANDAND exp # expAndand -// { $$ = exp_binop (ANDAND , $1, $3); } - | exp OROR exp # expOror -// { $$ = exp_binop (OROR , $1, $3); } - | DEFINED '(' NAME ')' # expDefined -// { $$ = exp_nameop (DEFINED, $3); } - | INT # expInt -// { $$ = exp_bigintop ($1.integer, $1.str); } - | SIZEOF_HEADERS # expSizeofHeaders -// { $$ = exp_nameop (SIZEOF_HEADERS,0); } - | ALIGNOF '(' NAME ')' # expAlignof -// { $$ = exp_nameop (ALIGNOF,$3); } - | SIZEOF '(' NAME ')' # expSizeof -// { $$ = exp_nameop (SIZEOF,$3); } - | ADDR '(' NAME ')' # expAddr -// { $$ = exp_nameop (ADDR,$3); } - | LOADADDR '(' NAME ')' # expLoadaddr -// { $$ = exp_nameop (LOADADDR,$3); } - | CONSTANT '(' NAME ')' # expConstant -// { $$ = exp_nameop (CONSTANT,$3); } - | ABSOLUTE '(' exp ')' # expAbsolute -// { $$ = exp_unop (ABSOLUTE, $3); } - | ALIGN_K '(' exp ')' # expAlign -// { $$ = exp_unop (ALIGN_K,$3); } - | ALIGN_K '(' exp ',' exp ')' # expAlign2 -// { $$ = exp_binop (ALIGN_K,$3,$5); } - | DATA_SEGMENT_ALIGN '(' exp ',' exp ')' # expDataSegmentAlign -// { $$ = exp_binop (DATA_SEGMENT_ALIGN, $3, $5); } - | DATA_SEGMENT_RELRO_END '(' exp ',' exp ')' # expDataSegmentRelRoEnd -// { $$ = exp_binop (DATA_SEGMENT_RELRO_END, $5, $3); - | DATA_SEGMENT_END '(' exp ')' # expDataSegmentEnd -// { $$ = exp_unop (DATA_SEGMENT_END, $3); } - | SEGMENT_START '(' NAME ',' exp ')' # expSegmentStart -// { /* The operands to the expression node are -// placed in the opposite order from the way -// in which they appear in the script as -// that allows us to reuse more code in -// fold_binary. */ -// $$ = exp_binop (SEGMENT_START, -// $5, -// exp_nameop (NAME, $3)); } - | BLOCK '(' exp ')' # expBlock -// { $$ = exp_unop (ALIGN_K,$3); } - | NAME # expName -// { $$ = exp_nameop (NAME,$1); } - | MAX_K '(' exp ',' exp ')' # expMax -// { $$ = exp_binop (MAX_K, $3, $5 ); } - | MIN_K '(' exp ',' exp ')' # expMin -// { $$ = exp_binop (MIN_K, $3, $5 ); } - | ASSERT_K '(' exp ',' NAME ')' # expAssert -// { $$ = exp_assert ($3, $5); } - | ORIGIN '(' NAME ')' # expOrigin -// { $$ = exp_nameop (ORIGIN, $3); } - | LENGTH '(' NAME ')' # expLengthExp -// { $$ = exp_nameop (LENGTH, $3); } - | LOG2CEIL '(' exp ')' # expLog2ceil -// { $$ = exp_unop (LOG2CEIL, $3); } - ; - - -memspec_at_opt: - AT '>' NAME // { $$ = $3; } - | // { $$ = 0; } - ; - -opt_at: - AT '(' exp ')' // { $$ = $3; } - | // { $$ = 0; } - ; - -opt_align: - ALIGN_K '(' exp ')' // { $$ = $3; } - | // { $$ = 0; } - ; - -opt_align_with_input: - ALIGN_WITH_INPUT // { $$ = ALIGN_WITH_INPUT; } - | // { $$ = 0; } - ; - -opt_subalign: - SUBALIGN '(' exp ')' // { $$ = $3; } - | // { $$ = 0; } - ; - -sect_constraint: - ONLY_IF_RO // { $$ = ONLY_IF_RO; } - | ONLY_IF_RW // { $$ = ONLY_IF_RW; } - | SPECIAL // { $$ = SPECIAL; } - | // { $$ = 0; } - ; - -section: NAME // { ldlex_expression(); } - opt_exp_with_type - opt_at - opt_align - opt_align_with_input - opt_subalign // { ldlex_popstate (); ldlex_script (); } - sect_constraint - '{' -// { -// lang_enter_output_section_statement($1, $3, -// sectype, -// $5, $7, $4, $9, $6); -// } - statement_list_opt - '}' // { ldlex_popstate (); ldlex_expression (); } - memspec_opt memspec_at_opt phdr_opt fill_opt -// { -// ldlex_popstate (); -// lang_leave_output_section_statement ($18, $15, $17, $16); -// } - opt_comma - {} - | OVERLAY -// { ldlex_expression (); } - opt_exp_without_type opt_nocrossrefs opt_at opt_subalign -// { ldlex_popstate (); ldlex_script (); } - '{' -// { -// lang_enter_overlay ($3, $6); -// } - overlay_section - '}' -// { ldlex_popstate (); ldlex_expression (); } - memspec_opt memspec_at_opt phdr_opt fill_opt -// { -// ldlex_popstate (); -// lang_leave_overlay ($5, (int) $4, -// $16, $13, $15, $14); -// } - 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_assign (".", $3, FALSE)); -// } - '{' sec_or_group_p1 '}' - | INCLUDE filename - // { ldlex_script (); ldfile_open_command_file($2); } - sec_or_group_p1 END - // { ldlex_popstate (); } - ; - -type: - NOLOAD // { sectype = noload_section; } - | DSECT // { sectype = noalloc_section; } - | COPY // { sectype = noalloc_section; } - | INFO // { sectype = noalloc_section; } - | OVERLAY // { sectype = noalloc_section; } - ; - -atype: - '(' type ')' - | /* EMPTY */ // { sectype = normal_section; } - | '(' ')' // { sectype = normal_section; } - ; - -opt_exp_with_type: - 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; } - ; - -opt_exp_without_type: - exp ':' // { $$ = $1; } - | ':' // { $$ = (etree_type *) NULL; } - ; - -opt_nocrossrefs: - /* empty */ -// { $$ = 0; } - | NOCROSSREFS -// { $$ = 1; } - ; - -memspec_opt: - '>' NAME - // { $$ = $2; } - | // { $$ = DEFAULT_MEMORY_REGION; } - ; - -phdr_opt: - /* empty */ -// { -// $$ = NULL; -// } - | phdr_opt ':' NAME -// { -// struct lang_output_section_phdr_list *n; -// -// n = ((struct lang_output_section_phdr_list *) -// xmalloc (sizeof *n)); -// n->name = $3; -// n->used = FALSE; -// n->next = $1; -// $$ = n; -// } - ; - -overlay_section: - /* empty */ - | overlay_section - NAME -// { -// ldlex_script (); -// lang_enter_overlay_section ($2); -// } - '{' statement_list_opt '}' -// { ldlex_popstate (); ldlex_expression (); } - phdr_opt fill_opt -// { -// ldlex_popstate (); -// lang_leave_overlay_section ($9, $8); -// } - opt_comma - ; - -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", "PT_TLS" -// }; -// -// 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; -// } -// if (i == sizeof phdr_types / sizeof phdr_types[0]) -// { -// if (strcmp (s, "PT_GNU_EH_FRAME") == 0) -// $$ = exp_intop (0x6474e550); -// else if (strcmp (s, "PT_GNU_STACK") == 0) -// $$ = exp_intop (0x6474e551); -// else -// { -// einfo (_("\ -//%X%P:%S: unknown phdr type `%s' (try integer literal)\n"), -// NULL, s); -// $$ = exp_intop (0); -// } -// } -// } -// } - ; - -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"), -// NULL, $1); -// } - | AT '(' exp ')' phdr_qualifiers -// { -// $$ = $5; -// $$.at = $3; -// } - ; - -phdr_val: - /* empty */ -// { -// $$ = NULL; -// } - | '(' exp ')' -// { -// $$ = $2; -// } - ; - -dynamic_list_file: -// { -// ldlex_version_file (); -// PUSH_ERROR (_("dynamic list")); -// } - dynamic_list_nodes -// { -// ldlex_popstate (); -// POP_ERROR (); -// } - ; - -dynamic_list_nodes: - dynamic_list_node - | dynamic_list_nodes dynamic_list_node - ; - -dynamic_list_node: - '{' dynamic_list_tag '}' ';' - ; - -dynamic_list_tag: - vers_defns ';' -// { -// lang_append_dynamic_list ($1); -// } - ; - -/* This syntax is used within an external version script file. */ - -version_script_file: -// { -// ldlex_version_file (); -// PUSH_ERROR (_("VERSION script")); -// } - vers_nodes -// { -// ldlex_popstate (); -// POP_ERROR (); -// } - ; - -/* This is used within a normal linker script file. */ - -version: -// { -// ldlex_version_script (); -// } - VERSIONK '{' vers_nodes '}' -// { -// ldlex_popstate (); -// } - ; - -vers_nodes: - vers_node - | vers_nodes vers_node - ; - -vers_node: - '{' vers_tag '}' ';' -// { -// lang_register_vers_node (NULL, $2, NULL); -// } - | VERS_TAG '{' vers_tag '}' ';' -// { -// lang_register_vers_node ($1, $3, NULL); -// } - | VERS_TAG '{' vers_tag '}' verdep ';' -// { -// lang_register_vers_node ($1, $3, $5); -// } - ; - -verdep: - VERS_TAG -// { -// $$ = lang_add_vers_depend (NULL, $1); -// } - | verdep VERS_TAG -// { -// $$ = lang_add_vers_depend ($1, $2); -// } - ; - -vers_tag: - /* empty */ -// { -// $$ = lang_new_vers_node (NULL, NULL); -// } - | vers_defns ';' -// { -// $$ = lang_new_vers_node ($1, NULL); -// } - | GLOBAL ':' vers_defns ';' -// { -// $$ = lang_new_vers_node ($3, NULL); -// } - | LOCAL ':' vers_defns ';' -// { -// $$ = lang_new_vers_node (NULL, $3); -// } - | GLOBAL ':' vers_defns ';' LOCAL ':' vers_defns ';' -// { -// $$ = lang_new_vers_node ($3, $7); -// } - ; - -vers_defns: - VERS_IDENTIFIER -// { -// $$ = lang_new_vers_pattern (NULL, $1, ldgram_vers_current_lang, FALSE); -// } - | NAME -// { -// $$ = lang_new_vers_pattern (NULL, $1, ldgram_vers_current_lang, TRUE); -// } - | vers_defns ';' VERS_IDENTIFIER -// { -// $$ = lang_new_vers_pattern ($1, $3, ldgram_vers_current_lang, FALSE); -// } - | vers_defns ';' NAME -// { -// $$ = lang_new_vers_pattern ($1, $3, ldgram_vers_current_lang, TRUE); -// } - | vers_defns ';' EXTERN NAME '{' -// { -// $$ = ldgram_vers_current_lang; -// ldgram_vers_current_lang = $4; -// } - vers_defns opt_semicolon '}' -// { -// struct bfd_elf_version_expr *pat; -// for (pat = $7; pat->next != NULL; pat = pat->next); -// pat->next = $1; -// $$ = $7; -// ldgram_vers_current_lang = $6; -// } - | EXTERN NAME '{' -// { -// $$ = ldgram_vers_current_lang; -// ldgram_vers_current_lang = $2; -// } - vers_defns opt_semicolon '}' -// { -// $$ = $5; -// ldgram_vers_current_lang = $4; -// } - | GLOBAL -// { -// $$ = lang_new_vers_pattern (NULL, "global", ldgram_vers_current_lang, FALSE); -// } - | vers_defns ';' GLOBAL -// { -// $$ = lang_new_vers_pattern ($1, "global", ldgram_vers_current_lang, FALSE); -// } - | LOCAL -// { -// $$ = lang_new_vers_pattern (NULL, "local", ldgram_vers_current_lang, FALSE); -// } - | vers_defns ';' LOCAL -// { -// $$ = lang_new_vers_pattern ($1, "local", ldgram_vers_current_lang, FALSE); -// } - | EXTERN -// { -// $$ = lang_new_vers_pattern (NULL, "extern", ldgram_vers_current_lang, FALSE); -// } - | vers_defns ';' EXTERN -// { -// $$ = lang_new_vers_pattern ($1, "extern", ldgram_vers_current_lang, FALSE); -// } - ; - -opt_semicolon: - /* empty */ - | ';' - ; - -//%token -LBRACE : '{'; -RBRACE : '}'; - -// -// assign_op atype attributes_opt sect_constraint opt_align_with_input -// filename -//%type vers_defns -//%type vers_tag -//%type verdep - -ABSOLUTE : 'ABSOLUTE'; -ADDR : 'ADDR'; -AFTER : 'AFTER'; -ALIAS : 'ALIAS'; -ALIGN : 'ALIGN'; -ALIGN_K : 'ALIGN'; -ALIGNMOD : 'ALIGNMOD'; -ALIGNOF : 'ALIGNOF'; -ALIGN_WITH_INPUT : 'ALIGN_WITH_INPUT'; -AS_NEEDED : 'AS_NEEDED'; -ASSERT_K : 'ASSERT'; -AT : 'AT'; -BASE : 'BASE'; -BEFORE : 'BEFORE'; -BIND : 'BIND'; -BLOCK : 'BLOCK'; -BYTE : 'BYTE'; -CASE : 'CASE'; -CHIP : 'CHIP'; -CONSTANT : 'CONSTANT'; -CONSTRUCTORS : 'CONSTRUCTORS'; -COPY : 'COPY'; -CREATE_OBJECT_SYMBOLS : 'CREATE_OBJECT_SYMBOLS'; -DATA_SEGMENT_ALIGN : 'DATA_SEGMENT_ALIGN'; -DATA_SEGMENT : 'DATA_SEGMENT'; -DATA_SEGMENT_END : 'DATA_SEGMENT_END'; -DATA_SEGMENT_RELRO_END : 'DATA_SEGMENT_RELRO_END'; -DEFINED : 'DEFINED'; -DEFSYMEND : 'DEFSYMEND'; -DSECT : 'DSECT'; -ENDWORD : 'ENDWORD'; -ENTRY : 'ENTRY'; -EXCLUDE_FILE : 'EXCLUDE_FILE'; -EXTERN : 'EXTERN'; -FILL : 'FILL'; -FLOAT : 'FLOAT'; -FORCE_COMMON_ALLOCATION : 'FORCE_COMMON_ALLOCATION'; -FORMAT : 'FORMAT'; -GLOBAL : 'GLOBAL'; -GROUP : 'GROUP'; -HIDDEN_ : 'HIDDEN'; -HLL : 'HLL'; -INCLUDE : 'INCLUDE'; -INFO : 'INFO'; -INHIBIT_COMMON_ALLOCATION : 'INHIBIT_COMMON_ALLOCATION'; -//INPUT_DEFSYM : 'INPUT_DEFSYM'; -//INPUT_DYNAMIC_LIST : 'INPUT_DYNAMIC_LIST'; -INPUT : 'INPUT'; -//INPUT_MRI_SCRIPT : 'INPUT_MRI_SCRIPT'; -//INPUT_SCRIPT : 'INPUT_SCRIPT'; -//INPUT_SECTION_FLAGS : 'INPUT_SECTION_FLAGS'; -//INPUT_VERSION_SCRIPT : 'INPUT_VERSION_SCRIPT'; -INSERT_K : 'INSERT'; -KEEP : 'KEEP'; -LD_FEATURE : 'LD_FEATURE'; -LENGTH : 'LENGTH'; -LIST : 'LIST'; -LOADADDR : 'LOADADDR'; -LOAD : 'LOAD'; -LOCAL : 'LOCAL'; -LOG2CEIL : 'LOG2CEIL'; -LONG : 'LONG'; -MAP : 'MAP'; -MAX_K : 'MAX'; -MEMORY : 'MEMORY'; -MIN_K : 'MIN'; -NAMEWORD : 'NAMEWORD'; -NEWLINE : 'NEWLINE'; -NEXT : 'NEXT'; -NOCROSSREFS : 'NOCROSSREFS'; -NOCROSSREFS_TO : 'NOCROSSREFS_TO'; -NOFLOAT : 'NOFLOAT'; -NOLOAD : 'NOLOAD'; -ONLY_IF_RO : 'ONLY_IF_RO'; -ONLY_IF_RW : 'ONLY_IF_RW'; -ORDER : 'ORDER'; -ORIGIN : 'ORIGIN'; // TODO: or 'org' or 'o'. -OUTPUT_ARCH : 'OUTPUT_ARCH'; -OUTPUT_FORMAT : 'OUTPUT_FORMAT'; -OUTPUT : 'OUTPUT'; -OVERLAY : 'OVERLAY'; -PHDRS : 'PHDRS'; -PROVIDE_HIDDEN : 'PROVIDE_HIDDEN'; -PROVIDE : 'PROVIDE'; -PUBLIC : 'PUBLIC'; -QUAD : 'QUAD'; -REGION_ALIAS : 'REGION_ALIAS'; -REL : 'REL'; -SEARCH_DIR : 'SEARCH_DIR'; -SECTIONS : 'SECTIONS'; -SECT : 'SECT'; -SEGMENT_START : 'SEGMENT_START'; -SHORT : 'SHORT'; -SIZEOF_HEADERS : 'SIZEOF_HEADERS'; -SIZEOF : 'SIZEOF'; -SORT_BY_ALIGNMENT : 'SORT_BY_ALIGNMENT'; -SORT_BY_INIT_PRIORITY : 'SORT_BY_INIT_PRIORITY'; -SORT_BY_NAME : 'SORT_BY_NAME'; -SORT_NONE : 'SORT_NONE'; -SPECIAL : 'SPECIAL'; -SQUAD : 'SQUAD'; -START : 'START'; -STARTUP : 'STARTUP'; -SUBALIGN : 'SUBALIGN'; -SYSLIB : 'SYSLIB'; -TARGET_K : 'TARGET'; -TRUNCATE : 'TRUNCATE'; -VERS_IDENTIFIER : 'VERS_IDENTIFIER'; -VERSIONK : 'VERSIONK'; -VERS_TAG : 'VERS_TAG'; - -/* -Names are very liberal, they can be full strings and start with a dot. -*/ - -NAME : [\._a-zA-Z][_a-zA-Z0-9]*; - -// TODO: ld supports some really fancy expressions here, like "0101010b", "ffH", "ffx", "$Aa" etc -//INT : '0x' [0-9a-fA-F]+ -// | [0-9]+; -INT : INT_NUMBER INT_SIZE?; -INT_NUMBER : INT_HEX - | INT_DECIMAL; -INT_HEX : '0x' [0-9a-fA-F]+; -INT_DECIMAL : [0-9]+; -INT_SIZE : 'M' | 'm' | 'K' | 'k'; - -END : 'END'; - -LNAME : '-l' NAME; -PLUSEQ : '+='; -MINUSEQ : '-='; -MULTEQ : '*='; -DIVEQ : '/='; -LSHIFTEQ : '<<='; -RSHIFTEQ : '>>='; -ANDEQ : '&='; -OREQ : '|='; -LSHIFT : '<<'; -RSHIFT : '>>'; -EQ : '='; -NE : '!='; -LE : '<='; -GE : '>='; -ANDAND : '&&'; -OROR : '||'; - -BlockComment - : '/*' .*? '*/' -> skip - ; - -WS : [ \t\r\n]+ -> skip ; diff --git a/cmake/elfinfo/GnuLdLexer.g4 b/cmake/elfinfo/GnuLdLexer.g4 new file mode 100644 index 0000000..fba488e --- /dev/null +++ b/cmake/elfinfo/GnuLdLexer.g4 @@ -0,0 +1,209 @@ +lexer grammar GnuLdLexer; + + +//%token +LBRACE : '{'; +RBRACE : '}'; + +// +// assign_op atype attributes_opt sect_constraint opt_align_with_input +// filename +//%type vers_defns +//%type vers_tag +//%type verdep + +ABSOLUTE : 'ABSOLUTE'; +ADDR : 'ADDR'; +AFTER : 'AFTER'; +ALIAS : 'ALIAS'; +//ALIGN : 'ALIGN'; +ALIGN_K : 'ALIGN'; +ALIGNMOD : 'ALIGNMOD'; +ALIGNOF : 'ALIGNOF'; +ALIGN_WITH_INPUT : 'ALIGN_WITH_INPUT'; +AS_NEEDED : 'AS_NEEDED'; +ASSERT_K : 'ASSERT'; +AT : 'AT'; +BASE : 'BASE'; +BEFORE : 'BEFORE'; +BIND : 'BIND'; +BLOCK : 'BLOCK'; +BYTE : 'BYTE'; +CASE : 'CASE'; +CHIP : 'CHIP'; +CONSTANT : 'CONSTANT'; +CONSTRUCTORS : 'CONSTRUCTORS'; +COPY : 'COPY'; +CREATE_OBJECT_SYMBOLS : 'CREATE_OBJECT_SYMBOLS'; +DATA_SEGMENT_ALIGN : 'DATA_SEGMENT_ALIGN'; +DATA_SEGMENT : 'DATA_SEGMENT'; +DATA_SEGMENT_END : 'DATA_SEGMENT_END'; +DATA_SEGMENT_RELRO_END : 'DATA_SEGMENT_RELRO_END'; +DEFINED : 'DEFINED'; +DEFSYMEND : 'DEFSYMEND'; +DSECT : 'DSECT'; +ENDWORD : 'ENDWORD'; +ENTRY : 'ENTRY'; +EXCLUDE_FILE : 'EXCLUDE_FILE'; +EXTERN : 'EXTERN'; +FILL : 'FILL'; +FLOAT : 'FLOAT'; +FORCE_COMMON_ALLOCATION : 'FORCE_COMMON_ALLOCATION'; +FORMAT : 'FORMAT'; +GLOBAL : 'GLOBAL'; +GROUP : 'GROUP'; +HIDDEN_ : 'HIDDEN'; +HLL : 'HLL'; +INCLUDE : 'INCLUDE'; +INFO : 'INFO'; +INHIBIT_COMMON_ALLOCATION : 'INHIBIT_COMMON_ALLOCATION'; +//INPUT_DEFSYM : 'INPUT_DEFSYM'; +//INPUT_DYNAMIC_LIST : 'INPUT_DYNAMIC_LIST'; +INPUT : 'INPUT'; +//INPUT_MRI_SCRIPT : 'INPUT_MRI_SCRIPT'; +//INPUT_SCRIPT : 'INPUT_SCRIPT'; +//INPUT_SECTION_FLAGS : 'INPUT_SECTION_FLAGS'; +//INPUT_VERSION_SCRIPT : 'INPUT_VERSION_SCRIPT'; +INSERT_K : 'INSERT'; +KEEP : 'KEEP'; +LD_FEATURE : 'LD_FEATURE'; +LENGTH : 'LENGTH'; +LIST : 'LIST'; +LOADADDR : 'LOADADDR'; +LOAD : 'LOAD'; +LOCAL : 'LOCAL'; +LOG2CEIL : 'LOG2CEIL'; +LONG : 'LONG'; +MAP : 'MAP'; +MAX_K : 'MAX'; +MEMORY : 'MEMORY'; +MIN_K : 'MIN'; +NAMEWORD : 'NAMEWORD'; +NEWLINE : 'NEWLINE'; +NEXT : 'NEXT'; +NOCROSSREFS : 'NOCROSSREFS'; +NOCROSSREFS_TO : 'NOCROSSREFS_TO'; +NOFLOAT : 'NOFLOAT'; +NOLOAD : 'NOLOAD'; +ONLY_IF_RO : 'ONLY_IF_RO'; +ONLY_IF_RW : 'ONLY_IF_RW'; +ORDER : 'ORDER'; +ORIGIN : 'ORIGIN'; // TODO: or 'org' or 'o'. +OUTPUT_ARCH : 'OUTPUT_ARCH'; +OUTPUT_FORMAT : 'OUTPUT_FORMAT'; +OUTPUT : 'OUTPUT'; +OVERLAY : 'OVERLAY'; +PHDRS : 'PHDRS'; +PROVIDE_HIDDEN : 'PROVIDE_HIDDEN'; +PROVIDE : 'PROVIDE'; +PUBLIC : 'PUBLIC'; +QUAD : 'QUAD'; +REGION_ALIAS : 'REGION_ALIAS'; +REL : 'REL'; +SEARCH_DIR : 'SEARCH_DIR'; +SECTIONS : 'SECTIONS'; +SECT : 'SECT'; +SEGMENT_START : 'SEGMENT_START'; +SHORT : 'SHORT'; +SIZEOF_HEADERS : 'SIZEOF_HEADERS'; +SIZEOF : 'SIZEOF'; +SORT_BY_ALIGNMENT : 'SORT_BY_ALIGNMENT'; +SORT_BY_INIT_PRIORITY : 'SORT_BY_INIT_PRIORITY'; +SORT_BY_NAME : 'SORT_BY_NAME'; +SORT_NONE : 'SORT_NONE'; +SPECIAL : 'SPECIAL'; +SQUAD : 'SQUAD'; +START : 'START'; +STARTUP : 'STARTUP'; +SUBALIGN : 'SUBALIGN'; +SYSLIB : 'SYSLIB'; +TARGET_K : 'TARGET'; +TRUNCATE : 'TRUNCATE'; +VERS_IDENTIFIER : 'VERS_IDENTIFIER'; +VERSIONK : 'VERSIONK'; +VERS_TAG : 'VERS_TAG'; + +/* +Names are very liberal, they can be full strings and start with a dot. +*/ + +QUOTE : '"' -> skip, pushMode(STRING); + +//SPACE : ' '; +//SPACES : ' '+; + +//name : +// '"' (NAME | SPACE | SPACES)+ '"' # nameQuoted +// | NAME # namePlain; + +//NAME : [\._a-zA-Z][\._a-zA-Z0-9]*; +NAME : [*\._a-zA-Z][*\.\/_a-zA-Z0-9]*; + +// TODO: ld supports some really fancy expressions here, like "0101010b", "ffH", "ffx", "$Aa" etc +//INT : '0x' [0-9a-fA-F]+ +// | [0-9]+; +INT : INT_NUMBER INT_SIZE?; +fragment +INT_NUMBER : INT_HEX + | INT_DECIMAL; +fragment +INT_HEX : '0x' [0-9a-fA-F]+; +fragment +INT_DECIMAL : [0-9]+; +fragment +INT_SIZE : 'M' | 'm' | 'K' | 'k'; + +END : 'END'; + +LNAME : '-l' NAME; +PLUSEQ : '+='; +MINUSEQ : '-='; +MULTEQ : '*='; +DIVEQ : '/='; +LSHIFTEQ : '<<='; +RSHIFTEQ : '>>='; +ANDEQ : '&='; +OREQ : '|='; +LSHIFT : '<<'; +RSHIFT : '>>'; +EQEQ : '=='; +EQ : '='; +NE : '!='; +LE : '<='; +GE : '>='; +ANDAND : '&&'; +OROR : '||'; + +// Extra tokens +COLON : ':'; +EXLAMATION : '!'; +DASH : '-'; +PLUS : '+'; +TILDE : '~'; +SLASH : '/'; +MOD : '%'; +LT : '<'; +GT : '>'; +HAT : '^'; +BAR : '|'; +COMMA : ','; +SEMICOLON : ';'; +LPAREN : '('; +RPAREN : ')'; +STAR : '*'; +QUESTION : '?'; +AMPERSAND : '&'; +LBRACKET : '['; +RBRACKET : ']'; + +BlockComment + : '/*' .*? '*/' -> skip + ; + +WS + : [ \t\r\n]+ -> skip + ; + +mode STRING; +STRING_ANY : ~'"'; +STRING_END_QUOTE : '"' -> skip, popMode; diff --git a/cmake/elfinfo/GnuLdParser.g4 b/cmake/elfinfo/GnuLdParser.g4 new file mode 100644 index 0000000..1ed1119 --- /dev/null +++ b/cmake/elfinfo/GnuLdParser.g4 @@ -0,0 +1,1345 @@ +parser grammar GnuLdParser; + +options { + tokenVocab = GnuLdLexer; +} + +/* +TODO: check right associative annotations +*/ + +/* +%token INT +%token NAME LNAME +*/ + +/* +%right UNARY +%token END +%left LPAREN +*/ + + +file: + /*INPUT_SCRIPT*/ script_file + | /*INPUT_MRI_SCRIPT*/ mri_script_file + | /*INPUT_VERSION_SCRIPT*/ version_script_file + | /*INPUT_DYNAMIC_LIST*/ dynamic_list_file + | /*INPUT_DEFSYM*/ defsym_expr + ; + +filename: NAME; + + +defsym_expr: +// { ldlex_defsym(); } + NAME EQ exp +// { +// ldlex_popstate(); +// lang_add_assignment (exp_defsym ($2, $4)); +// } + ; + +/* SYNTAX WITHIN AN MRI SCRIPT FILE */ +mri_script_file: +// { +// ldlex_mri_script (); +// PUSH_ERROR (_("MRI style script")); +// } + mri_script_lines +// { +// ldlex_popstate (); +// mri_draw_tree (); +// POP_ERROR (); +// } + ; + +mri_script_lines: + mri_script_lines mri_script_command NEWLINE + | + ; + +mri_script_command: + CHIP exp + | CHIP exp COMMA exp + | NAME /*{ + einfo(_("%P%F: unrecognised keyword in MRI style script '%s'\n"),$1); + }*/ + | LIST /*{ + config.map_filename = "-"; + }*/ + | ORDER ordernamelist + | ENDWORD + | PUBLIC NAME EQ exp + // { mri_public($2, $4); } + | PUBLIC NAME COMMA exp + // { mri_public($2, $4); } + | PUBLIC NAME exp + // { mri_public($2, $3); } + | FORMAT NAME + // { mri_format($2); } + | SECT NAME COMMA exp + // { mri_output_section($2, $4);} + | SECT NAME exp + // { mri_output_section($2, $3);} + | SECT NAME EQ exp + // { mri_output_section($2, $4);} + | ALIGN_K NAME EQ exp + // { mri_align($2,$4); } + | ALIGN_K NAME COMMA exp + // { mri_align($2,$4); } + | ALIGNMOD NAME EQ exp + // { mri_alignmod($2,$4); } + | ALIGNMOD NAME COMMA exp + // { mri_alignmod($2,$4); } + | ABSOLUTE mri_abs_name_list + | LOAD mri_load_name_list + | NAMEWORD NAME + // { mri_name($2); } + | ALIAS NAME COMMA NAME + // { mri_alias($2,$4,0);} + | ALIAS NAME COMMA INT + // { mri_alias ($2, 0, (int) $4.integer); } + | BASE exp + // { mri_base($2); } + | TRUNCATE INT + // { mri_truncate ((unsigned int) $2.integer); } + | CASE casesymlist + | EXTERN extern_name_list + | INCLUDE filename + // { ldlex_script (); ldfile_open_command_file($2); } + mri_script_lines END + // { ldlex_popstate (); } + | START NAME + // { lang_add_entry ($2, FALSE); } + | + ; + +ordernamelist: + ordernamelist COMMA NAME // { mri_order($3); } + | ordernamelist NAME // { mri_order($2); } + | + ; + +mri_load_name_list: + NAME + // { mri_load($1); } + | mri_load_name_list COMMA NAME // { mri_load($3); } + ; + +mri_abs_name_list: + NAME + // { mri_only_load($1); } + | mri_abs_name_list COMMA NAME + // { mri_only_load($3); } + ; + +casesymlist: + /* empty */ // { $$ = NULL; } + | NAME + | casesymlist COMMA NAME + ; + +/* Parsed as expressions so that commas separate entries */ +extern_name_list: + // { ldlex_expression (); } + extern_name_list_body + // { ldlex_popstate (); } + ; + +extern_name_list_body: + NAME + // { ldlang_add_undef ($1, FALSE); } + | extern_name_list_body NAME + // { ldlang_add_undef ($2, FALSE); } + | extern_name_list_body COMMA NAME + // { ldlang_add_undef ($3, FALSE); } + ; + +script_file: + // { ldlex_both(); } + ifile_list + // { ldlex_popstate(); } + ; + +ifile_list: + ifile_list ifile_p1 + | + ; + + +ifile_p1: + memory + | sections + | phdrs + | startup + | high_level_library + | low_level_library + | floating_point_support + | statement_anywhere + | version + | SEMICOLON + | TARGET_K LPAREN NAME RPAREN + // { lang_add_target($3); } + | SEARCH_DIR LPAREN filename RPAREN + // { ldfile_add_library_path ($3, FALSE); } + | OUTPUT LPAREN filename RPAREN + // { lang_add_output($3, 1); } + | OUTPUT_FORMAT LPAREN NAME RPAREN + // { lang_add_output_format ($3, (char *) NULL, + // (char *) NULL, 1); } + | OUTPUT_FORMAT LPAREN NAME COMMA NAME COMMA NAME RPAREN + // { lang_add_output_format ($3, $5, $7, 1); } + | OUTPUT_ARCH LPAREN NAME RPAREN + // { ldfile_set_output_arch ($3, bfd_arch_unknown); } + | FORCE_COMMON_ALLOCATION + // { command_line.force_common_definition = TRUE ; } + | INHIBIT_COMMON_ALLOCATION + // { command_line.inhibit_common_definition = TRUE ; } + | INPUT LPAREN input_list RPAREN + | GROUP + // { lang_enter_group (); } + LPAREN input_list RPAREN + // { lang_leave_group (); } + | MAP LPAREN filename RPAREN + // { lang_add_map($3); } + | INCLUDE filename + // { ldlex_script (); ldfile_open_command_file($2); } + ifile_list END + // { ldlex_popstate (); } + | NOCROSSREFS LPAREN nocrossref_list RPAREN + // { + // lang_add_nocrossref ($3); + // } + | NOCROSSREFS_TO LPAREN nocrossref_list RPAREN + // { + // lang_add_nocrossref_to ($3); + // } + | EXTERN LPAREN extern_name_list RPAREN + | INSERT_K AFTER NAME + // { lang_add_insert ($3, 0); } + | INSERT_K BEFORE NAME + // { lang_add_insert ($3, 1); } + | REGION_ALIAS LPAREN NAME COMMA NAME RPAREN + // { lang_memory_region_alias ($3, $5); } + | LD_FEATURE LPAREN NAME RPAREN + // { lang_ld_feature ($3); } + ; + +input_list: + // { ldlex_inputlist(); } + input_list1 + // { ldlex_popstate(); } + ; + +input_list1: + NAME + // { lang_add_input_file($1,lang_input_file_is_search_file_enum, + // (char *)NULL); } + | input_list1 COMMA NAME + // { lang_add_input_file($3,lang_input_file_is_search_file_enum, + // (char *)NULL); } + | input_list1 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_list1 COMMA LNAME + // { lang_add_input_file($3,lang_input_file_is_l_enum, + // (char *)NULL); } + | input_list1 LNAME + // { lang_add_input_file($2,lang_input_file_is_l_enum, + // (char *)NULL); } + | AS_NEEDED LPAREN + // { $$ = input_flags.add_DT_NEEDED_for_regular; + // input_flags.add_DT_NEEDED_for_regular = TRUE; } + // input_list1 RPAREN + // { input_flags.add_DT_NEEDED_for_regular = $3; } + | input_list1 COMMA AS_NEEDED LPAREN + // { $$ = input_flags.add_DT_NEEDED_for_regular; + // input_flags.add_DT_NEEDED_for_regular = TRUE; } + // input_list1 RPAREN + // { input_flags.add_DT_NEEDED_for_regular = $5; } + | input_list1 AS_NEEDED LPAREN + // { $$ = input_flags.add_DT_NEEDED_for_regular; + // input_flags.add_DT_NEEDED_for_regular = TRUE; } + // input_list1 RPAREN + // { input_flags.add_DT_NEEDED_for_regular = $4; } + ; + +sections: + SECTIONS LBRACE sec_or_group_p1 RBRACE + ; + +sec_or_group_p1: + sec_or_group_p1 section + | sec_or_group_p1 statement_anywhere + | + ; + +statement_anywhere: + ENTRY LPAREN NAME RPAREN + // { lang_add_entry ($3, FALSE); } + | assignment end + | ASSERT_K /*{ldlex_expression ();}*/ LPAREN exp COMMA string RPAREN + // { ldlex_popstate (); + // lang_add_assignment (exp_assert ($4, $6)); } + ; + +/* The '*' and '?' cases are there because the lexer returns them as + separate tokens rather than as name. */ +wildcard_name: + NAME + // { + // $$ = $1; + // } + | STAR + // { + // $$ = "*"; + // } + | QUESTION + // { + // $$ = "?"; + // } + ; + +wildcard_spec: + wildcard_name +// { +// $$.name = $1; +// $$.sorted = none; +// $$.exclude_name_list = NULL; +// $$.section_flag_list = NULL; +// } + | EXCLUDE_FILE LPAREN exclude_name_list RPAREN wildcard_name +// { +// $$.name = $5; +// $$.sorted = none; +// $$.exclude_name_list = $3; +// $$.section_flag_list = NULL; +// } + | SORT_BY_NAME LPAREN wildcard_name RPAREN +// { +// $$.name = $3; +// $$.sorted = by_name; +// $$.exclude_name_list = NULL; +// $$.section_flag_list = NULL; +// } + | SORT_BY_ALIGNMENT LPAREN wildcard_name RPAREN +// { +// $$.name = $3; +// $$.sorted = by_alignment; +// $$.exclude_name_list = NULL; +// $$.section_flag_list = NULL; +// } + | SORT_NONE LPAREN wildcard_name RPAREN +// { +// $$.name = $3; +// $$.sorted = by_none; +// $$.exclude_name_list = NULL; +// $$.section_flag_list = NULL; +// } + | SORT_BY_NAME LPAREN SORT_BY_ALIGNMENT LPAREN wildcard_name RPAREN RPAREN +// { +// $$.name = $5; +// $$.sorted = by_name_alignment; +// $$.exclude_name_list = NULL; +// $$.section_flag_list = NULL; +// } + | SORT_BY_NAME LPAREN SORT_BY_NAME LPAREN wildcard_name RPAREN RPAREN +// { +// $$.name = $5; +// $$.sorted = by_name; +// $$.exclude_name_list = NULL; +// $$.section_flag_list = NULL; +// } + | SORT_BY_ALIGNMENT LPAREN SORT_BY_NAME LPAREN wildcard_name RPAREN RPAREN +// { +// $$.name = $5; +// $$.sorted = by_alignment_name; +// $$.exclude_name_list = NULL; +// $$.section_flag_list = NULL; +// } + | SORT_BY_ALIGNMENT LPAREN SORT_BY_ALIGNMENT LPAREN wildcard_name RPAREN RPAREN +// { +// $$.name = $5; +// $$.sorted = by_alignment; +// $$.exclude_name_list = NULL; +// $$.section_flag_list = NULL; +// } + | SORT_BY_NAME LPAREN EXCLUDE_FILE LPAREN exclude_name_list RPAREN wildcard_name RPAREN +// { +// $$.name = $7; +// $$.sorted = by_name; +// $$.exclude_name_list = $5; +// $$.section_flag_list = NULL; +// } + | SORT_BY_INIT_PRIORITY LPAREN wildcard_name RPAREN +// { +// $$.name = $3; +// $$.sorted = by_init_priority; +// $$.exclude_name_list = NULL; +// $$.section_flag_list = NULL; +// } + ; + +sect_flag_list: NAME +// { +// struct flag_info_list *n; +// n = ((struct flag_info_list *) xmalloc (sizeof *n)); +// if ($1[0] == '!') +// { +// n->with = without_flags; +// n->name = &$1[1]; +// } +// else +// { +// n->with = with_flags; +// n->name = $1; +// } +// n->valid = FALSE; +// n->next = NULL; +// $$ = n; +// } + | sect_flag_list AMPERSAND NAME +// { +// struct flag_info_list *n; +// n = ((struct flag_info_list *) xmalloc (sizeof *n)); +// if ($3[0] == '!') +// { +// n->with = without_flags; +// n->name = &$3[1]; +// } +// else +// { +// n->with = with_flags; +// n->name = $3; +// } +// n->valid = FALSE; +// n->next = $1; +// $$ = n; +// } + ; + +sect_flags: + /*not used by antlr: INPUT_SECTION_FLAGS*/ LPAREN sect_flag_list RPAREN +// { +// struct flag_info *n; +// n = ((struct flag_info *) xmalloc (sizeof *n)); +// n->flag_list = $3; +// n->flags_initialized = FALSE; +// n->not_with_flags = 0; +// n->only_with_flags = 0; +// $$ = n; +// } + ; + +exclude_name_list: + exclude_name_list wildcard_name +// { +// struct name_list *tmp; +// tmp = (struct name_list *) xmalloc (sizeof *tmp); +// tmp->name = $2; +// tmp->next = $1; +// $$ = tmp; +// } + | + wildcard_name +// { +// struct name_list *tmp; +// tmp = (struct name_list *) xmalloc (sizeof *tmp); +// tmp->name = $1; +// tmp->next = NULL; +// $$ = tmp; +// } + ; + +file_name_list: + file_name_list opt_comma wildcard_spec +// { +// struct wildcard_list *tmp; +// tmp = (struct wildcard_list *) xmalloc (sizeof *tmp); +// tmp->next = $1; +// tmp->spec = $3; +// $$ = tmp; +// } + | + wildcard_spec +// { +// struct wildcard_list *tmp; +// tmp = (struct wildcard_list *) xmalloc (sizeof *tmp); +// tmp->next = NULL; +// tmp->spec = $1; +// $$ = tmp; +// } + ; + +input_section_spec_no_keep: + NAME +// { +// struct wildcard_spec tmp; +// tmp.name = $1; +// tmp.exclude_name_list = NULL; +// tmp.sorted = none; +// tmp.section_flag_list = NULL; +// lang_add_wild (&tmp, NULL, ldgram_had_keep); +// } + | sect_flags NAME +// { +// struct wildcard_spec tmp; +// tmp.name = $2; +// tmp.exclude_name_list = NULL; +// tmp.sorted = none; +// tmp.section_flag_list = $1; +// lang_add_wild (&tmp, NULL, ldgram_had_keep); +// } + | LBRACKET file_name_list RBRACKET +// { +// lang_add_wild (NULL, $2, ldgram_had_keep); +// } + | sect_flags LBRACKET file_name_list RBRACKET +// { +// struct wildcard_spec tmp; +// tmp.name = NULL; +// tmp.exclude_name_list = NULL; +// tmp.sorted = none; +// tmp.section_flag_list = $1; +// lang_add_wild (&tmp, $3, ldgram_had_keep); +// } + | wildcard_spec LPAREN file_name_list RPAREN +// { +// lang_add_wild (&$1, $3, ldgram_had_keep); +// } + | sect_flags wildcard_spec LPAREN file_name_list RPAREN +// { +// $2.section_flag_list = $1; +// lang_add_wild (&$2, $4, ldgram_had_keep); +// } + ; + +input_section_spec: + input_section_spec_no_keep + | KEEP LPAREN +// { ldgram_had_keep = TRUE; } + input_section_spec_no_keep RPAREN +// { ldgram_had_keep = FALSE; } + ; + +statement: + assignment end + | CREATE_OBJECT_SYMBOLS +// { +// lang_add_attribute(lang_object_symbols_statement_enum); +// } + | SEMICOLON + | CONSTRUCTORS +// { +// +// lang_add_attribute(lang_constructors_statement_enum); +// } + | SORT_BY_NAME LPAREN CONSTRUCTORS RPAREN +// { +// constructors_sorted = TRUE; +// lang_add_attribute (lang_constructors_statement_enum); +// } + | input_section_spec + | length LPAREN mustbe_exp RPAREN +// { +// lang_add_data ((int) $1, $3); +// } + + | FILL LPAREN fill_exp RPAREN +// { +// lang_add_fill ($3); +// } + | ASSERT_K /*{ldlex_expression ();}*/ LPAREN exp COMMA NAME RPAREN end +// { ldlex_popstate (); +// lang_add_assignment (exp_assert ($4, $6)); } + | INCLUDE filename +// { ldlex_script (); ldfile_open_command_file($2); } + statement_list_opt END +// { ldlex_popstate (); } + ; + +statement_list: + statement_list statement + | statement + ; + +statement_list_opt: + /* empty */ + | statement_list + ; + +length: + QUAD +// { $$ = $1; } + | SQUAD +// { $$ = $1; } + | LONG +// { $$ = $1; } + | SHORT +// { $$ = $1; } + | BYTE +// { $$ = $1; } + ; + +fill_exp: + mustbe_exp +// { +// $$ = exp_get_fill ($1, 0, "fill value"); +// } + ; + +fill_opt: + EQ fill_exp + // { $$ = $2; } + | // { $$ = (fill_type *) 0; } + ; + +assign_op: + PLUSEQ +// { $$ = '+'; } + | MINUSEQ +// { $$ = '-'; } + | MULTEQ +// { $$ = '*'; } + | DIVEQ +// { $$ = '/'; } + | LSHIFTEQ +// { $$ = LSHIFT; } + | RSHIFTEQ +// { $$ = RSHIFT; } + | ANDEQ +// { $$ = '&'; } + | OREQ +// { $$ = '|'; } + + ; + +end: SEMICOLON | COMMA + ; + + +assignment: + NAME EQ mustbe_exp +// { +// lang_add_assignment (exp_assign ($1, $3, FALSE)); +// } + | NAME assign_op mustbe_exp +// { +// lang_add_assignment (exp_assign ($1, +// exp_binop ($2, +// exp_nameop (NAME, +// $1), +// $3), FALSE)); +// } + | HIDDEN_ LPAREN NAME EQ mustbe_exp RPAREN +// { +// lang_add_assignment (exp_assign ($3, $5, TRUE)); +// } + | PROVIDE LPAREN NAME EQ mustbe_exp RPAREN +// { +// lang_add_assignment (exp_provide ($3, $5, FALSE)); +// } + | PROVIDE_HIDDEN LPAREN NAME EQ mustbe_exp RPAREN +// { +// lang_add_assignment (exp_provide ($3, $5, TRUE)); +// } + ; + + +opt_comma: + COMMA | ; + + +memory: + MEMORY LBRACE memory_spec_list_opt RBRACE + ; + +memory_spec_list_opt: memory_spec_list | ; + +memory_spec_list: + memory_spec_list opt_comma memory_spec + | memory_spec + ; + + +memory_spec: NAME +// { region = lang_memory_region_lookup ($1, TRUE); } + attributes_opt COLON + origin_spec opt_comma length_spec +// {} + | INCLUDE filename +// { ldlex_script (); ldfile_open_command_file($2); } + memory_spec_list_opt END +// { ldlex_popstate (); } + ; + +origin_spec: + ORIGIN EQ mustbe_exp +// { +// region->origin_exp = $3; +// region->current = region->origin; +// } + ; + +length_spec: + LENGTH EQ mustbe_exp +// { +// region->length_exp = $3; +// } + ; + +attributes_opt: + /* empty */ + /* { *//* dummy action to avoid bison 1.25 error message *//* } + |*/ LPAREN attributes_list RPAREN + ; + +attributes_list: + attributes_string + | attributes_list attributes_string + ; + +attributes_string: + NAME # attributeNormal +// { lang_set_flags (region, $1, 0); } + | EXLAMATION NAME # attributeInverted +// { lang_set_flags (region, $2, 1); } + ; + +/* +This would be best but the tokenizer would have to be made context sensitive which is too much work given how +easy it is to check the flags after parsing. + +attributes_string : + attribute + | '!' attribute # attributeInverted + ; + +attribute: 'r' | 'w' | 'x' | 'a' | 'i' | 'l' ; +*/ +startup: + STARTUP LPAREN filename RPAREN +// { lang_startup($3); } + ; + +high_level_library: + HLL LPAREN high_level_library_NAME_list RPAREN + | HLL LPAREN RPAREN +// { 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 LPAREN low_level_library_NAME_list RPAREN + ; 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); } + ; + +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 COMMA 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 + // { ldlex_popstate (); $$=$2;} + ; + +exp : + DASH exp # expNegate // TODO: %prec UNARY +// { $$ = exp_unop ('-', $2); } + | LPAREN exp RPAREN # expParen +// { $$ = $2; } + | NEXT LPAREN exp RPAREN # expNextParen // TODO: %prec UNARY +// { $$ = exp_unop ((int) $1,$3); } + | EXLAMATION exp # expInvert // TODO: %prec UNARY +// { $$ = exp_unop ('!', $2); } + | PLUS exp # expPlus // TODO: %prec UNARY +// { $$ = $2; } + | TILDE exp # expMinus // TODO: %prec UNARY +// { $$ = exp_unop ('~', $2);} + | exp STAR exp # expMul +// { $$ = exp_binop ('*', $1, $3); } + | exp SLASH exp # expDiv +// { $$ = exp_binop ('/', $1, $3); } + | exp MOD exp # expMod +// { $$ = exp_binop ('%', $1, $3); } + | exp PLUS exp # expAdd +// { $$ = exp_binop ('+', $1, $3); } + | exp DASH exp # expSub +// { $$ = exp_binop ('-' , $1, $3); } + | exp LSHIFT exp # expLshift +// { $$ = exp_binop (LSHIFT , $1, $3); } + | exp RSHIFT exp # expRshift +// { $$ = exp_binop (RSHIFT , $1, $3); } + | exp EQEQ exp # expEq +// { $$ = exp_binop (EQ , $1, $3); } + | exp NE exp # expNe +// { $$ = exp_binop (NE , $1, $3); } + | exp LE exp # expLe +// { $$ = exp_binop (LE , $1, $3); } + | exp GE exp # expGe +// { $$ = exp_binop (GE , $1, $3); } + | exp LT exp # expLt +// { $$ = exp_binop ('<' , $1, $3); } + | exp GT exp # expGt +// { $$ = exp_binop ('>' , $1, $3); } + | exp AMPERSAND exp # expAnd +// { $$ = exp_binop ('&' , $1, $3); } + | exp HAT exp # expXor +// { $$ = exp_binop ('^' , $1, $3); } + | exp BAR exp # expOr +// { $$ = exp_binop ('|' , $1, $3); } + | exp QUESTION exp COLON exp # expTrinary +// { $$ = exp_trinop ('?' , $1, $3, $5); } + | exp ANDAND exp # expAndand +// { $$ = exp_binop (ANDAND , $1, $3); } + | exp OROR exp # expOror +// { $$ = exp_binop (OROR , $1, $3); } + | DEFINED LPAREN NAME RPAREN # expDefined +// { $$ = exp_nameop (DEFINED, $3); } + | INT # expInt +// { $$ = exp_bigintop ($1.integer, $1.str); } + | SIZEOF_HEADERS # expSizeofHeaders +// { $$ = exp_nameop (SIZEOF_HEADERS,0); } + | ALIGNOF LPAREN NAME RPAREN # expAlignof +// { $$ = exp_nameop (ALIGNOF,$3); } + | SIZEOF LPAREN NAME RPAREN # expSizeof +// { $$ = exp_nameop (SIZEOF,$3); } + | ADDR LPAREN NAME RPAREN # expAddr +// { $$ = exp_nameop (ADDR,$3); } + | LOADADDR LPAREN NAME RPAREN # expLoadaddr +// { $$ = exp_nameop (LOADADDR,$3); } + | CONSTANT LPAREN NAME RPAREN # expConstant +// { $$ = exp_nameop (CONSTANT,$3); } + | ABSOLUTE LPAREN exp RPAREN # expAbsolute +// { $$ = exp_unop (ABSOLUTE, $3); } + | ALIGN_K LPAREN exp RPAREN # expAlign +// { $$ = exp_unop (ALIGN_K,$3); } + | ALIGN_K LPAREN exp COMMA exp RPAREN # expAlignK +// { $$ = exp_binop (ALIGN_K,$3,$5); } + | DATA_SEGMENT_ALIGN LPAREN exp COMMA exp RPAREN # expDataSegmentAlign +// { $$ = exp_binop (DATA_SEGMENT_ALIGN, $3, $5); } + | DATA_SEGMENT_RELRO_END LPAREN exp COMMA exp RPAREN # expDataSegmentRelRoEnd +// { $$ = exp_binop (DATA_SEGMENT_RELRO_END, $5, $3); + | DATA_SEGMENT_END LPAREN exp RPAREN # expDataSegmentEnd +// { $$ = exp_unop (DATA_SEGMENT_END, $3); } + | SEGMENT_START LPAREN NAME COMMA exp RPAREN # expSegmentStart +// { /* The operands to the expression node are +// placed in the opposite order from the way +// in which they appear in the script as +// that allows us to reuse more code in +// fold_binary. */ +// $$ = exp_binop (SEGMENT_START, +// $5, +// exp_nameop (NAME, $3)); } + | BLOCK LPAREN exp RPAREN # expBlock +// { $$ = exp_unop (ALIGN_K,$3); } + | NAME # expName +// { $$ = exp_nameop (NAME,$1); } + | MAX_K LPAREN exp COMMA exp RPAREN # expMax +// { $$ = exp_binop (MAX_K, $3, $5 ); } + | MIN_K LPAREN exp COMMA exp RPAREN # expMin +// { $$ = exp_binop (MIN_K, $3, $5 ); } + | ASSERT_K LPAREN exp COMMA NAME RPAREN # expAssert +// { $$ = exp_assert ($3, $5); } + | ORIGIN LPAREN NAME RPAREN # expOrigin +// { $$ = exp_nameop (ORIGIN, $3); } + | LENGTH LPAREN NAME RPAREN # expLengthExp +// { $$ = exp_nameop (LENGTH, $3); } + | LOG2CEIL LPAREN exp RPAREN # expLog2ceil +// { $$ = exp_unop (LOG2CEIL, $3); } + ; + + +memspec_at_opt: + AT GT NAME // { $$ = $3; } + | // { $$ = 0; } + ; + +opt_at: + AT LPAREN exp RPAREN // { $$ = $3; } + | // { $$ = 0; } + ; + +opt_align: + ALIGN_K LPAREN exp RPAREN // { $$ = $3; } + | // { $$ = 0; } + ; + +opt_align_with_input: + ALIGN_WITH_INPUT // { $$ = ALIGN_WITH_INPUT; } + | // { $$ = 0; } + ; + +opt_subalign: + SUBALIGN LPAREN exp RPAREN // { $$ = $3; } + | // { $$ = 0; } + ; + +sect_constraint: + ONLY_IF_RO // { $$ = ONLY_IF_RO; } + | ONLY_IF_RW // { $$ = ONLY_IF_RW; } + | SPECIAL // { $$ = SPECIAL; } + | // { $$ = 0; } + ; + +section: NAME // { ldlex_expression(); } + opt_exp_with_type + opt_at + opt_align + opt_align_with_input + opt_subalign // { ldlex_popstate (); ldlex_script (); } + sect_constraint + LBRACE +// { +// lang_enter_output_section_statement($1, $3, +// sectype, +// $5, $7, $4, $9, $6); +// } + statement_list_opt + RBRACE // { ldlex_popstate (); ldlex_expression (); } + memspec_opt memspec_at_opt phdr_opt fill_opt +// { +// ldlex_popstate (); +// lang_leave_output_section_statement ($18, $15, $17, $16); +// } + opt_comma + {} + | OVERLAY +// { ldlex_expression (); } + opt_exp_without_type opt_nocrossrefs opt_at opt_subalign +// { ldlex_popstate (); ldlex_script (); } + LBRACE +// { +// lang_enter_overlay ($3, $6); +// } + overlay_section + RBRACE +// { ldlex_popstate (); ldlex_expression (); } + memspec_opt memspec_at_opt phdr_opt fill_opt +// { +// ldlex_popstate (); +// lang_leave_overlay ($5, (int) $4, +// $16, $13, $15, $14); +// } + 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_assign (".", $3, FALSE)); +// } + LBRACE sec_or_group_p1 RBRACE + | INCLUDE filename + // { ldlex_script (); ldfile_open_command_file($2); } + sec_or_group_p1 END + // { ldlex_popstate (); } + ; + +type: + NOLOAD // { sectype = noload_section; } + | DSECT // { sectype = noalloc_section; } + | COPY // { sectype = noalloc_section; } + | INFO // { sectype = noalloc_section; } + | OVERLAY // { sectype = noalloc_section; } + ; + +atype: + LPAREN type RPAREN + | /* EMPTY */ // { sectype = normal_section; } + | LPAREN RPAREN // { sectype = normal_section; } + ; + +opt_exp_with_type: + exp atype COLON // { $$ = $1; } + | atype COLON // { $$ = (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 LPAREN exp RPAREN atype COLON // { $$ = $3; } + | BIND LPAREN exp RPAREN BLOCK LPAREN exp RPAREN atype COLON + // { $$ = $3; } + ; + +opt_exp_without_type: + exp COLON // { $$ = $1; } + | COLON // { $$ = (etree_type *) NULL; } + ; + +opt_nocrossrefs: + /* empty */ +// { $$ = 0; } + | NOCROSSREFS +// { $$ = 1; } + ; + +memspec_opt: + GT NAME + // { $$ = $2; } + | // { $$ = DEFAULT_MEMORY_REGION; } + ; + +phdr_opt: + /* empty */ +// { +// $$ = NULL; +// } + | phdr_opt COLON NAME +// { +// struct lang_output_section_phdr_list *n; +// +// n = ((struct lang_output_section_phdr_list *) +// xmalloc (sizeof *n)); +// n->name = $3; +// n->used = FALSE; +// n->next = $1; +// $$ = n; +// } + ; + +overlay_section: + /* empty */ + | overlay_section + NAME +// { +// ldlex_script (); +// lang_enter_overlay_section ($2); +// } + LBRACE statement_list_opt RBRACE +// { ldlex_popstate (); ldlex_expression (); } + phdr_opt fill_opt +// { +// ldlex_popstate (); +// lang_leave_overlay_section ($9, $8); +// } + opt_comma + ; + +phdrs: + PHDRS LBRACE phdr_list RBRACE + ; + +phdr_list: + /* empty */ + | phdr_list phdr + ; + +phdr: + NAME // { ldlex_expression (); } + phdr_type phdr_qualifiers // { ldlex_popstate (); } + SEMICOLON +// { +// 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", "PT_TLS" +// }; +// +// 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; +// } +// if (i == sizeof phdr_types / sizeof phdr_types[0]) +// { +// if (strcmp (s, "PT_GNU_EH_FRAME") == 0) +// $$ = exp_intop (0x6474e550); +// else if (strcmp (s, "PT_GNU_STACK") == 0) +// $$ = exp_intop (0x6474e551); +// else +// { +// einfo (_("\ +//%X%P:%S: unknown phdr type `%s' (try integer literal)\n"), +// NULL, s); +// $$ = exp_intop (0); +// } +// } +// } +// } + ; + +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"), +// NULL, $1); +// } + | AT LPAREN exp RPAREN phdr_qualifiers +// { +// $$ = $5; +// $$.at = $3; +// } + ; + +phdr_val: + /* empty */ +// { +// $$ = NULL; +// } + | LPAREN exp RPAREN +// { +// $$ = $2; +// } + ; + +dynamic_list_file: +// { +// ldlex_version_file (); +// PUSH_ERROR (_("dynamic list")); +// } + dynamic_list_nodes +// { +// ldlex_popstate (); +// POP_ERROR (); +// } + ; + +dynamic_list_nodes: + dynamic_list_node + | dynamic_list_nodes dynamic_list_node + ; + +dynamic_list_node: + LBRACE dynamic_list_tag RBRACE SEMICOLON + ; + +dynamic_list_tag: + vers_defns SEMICOLON +// { +// lang_append_dynamic_list ($1); +// } + ; + +/* This syntax is used within an external version script file. */ + +version_script_file: +// { +// ldlex_version_file (); +// PUSH_ERROR (_("VERSION script")); +// } + vers_nodes +// { +// ldlex_popstate (); +// POP_ERROR (); +// } + ; + +/* This is used within a normal linker script file. */ + +version: +// { +// ldlex_version_script (); +// } + VERSIONK LBRACE vers_nodes RBRACE +// { +// ldlex_popstate (); +// } + ; + +vers_nodes: + vers_node + | vers_nodes vers_node + ; + +vers_node: + LBRACE vers_tag RBRACE SEMICOLON +// { +// lang_register_vers_node (NULL, $2, NULL); +// } + | VERS_TAG LBRACE vers_tag RBRACE SEMICOLON +// { +// lang_register_vers_node ($1, $3, NULL); +// } + | VERS_TAG LBRACE vers_tag RBRACE verdep SEMICOLON +// { +// lang_register_vers_node ($1, $3, $5); +// } + ; + +verdep: + VERS_TAG +// { +// $$ = lang_add_vers_depend (NULL, $1); +// } + | verdep VERS_TAG +// { +// $$ = lang_add_vers_depend ($1, $2); +// } + ; + +vers_tag: + /* empty */ +// { +// $$ = lang_new_vers_node (NULL, NULL); +// } + | vers_defns SEMICOLON +// { +// $$ = lang_new_vers_node ($1, NULL); +// } + | GLOBAL COLON vers_defns SEMICOLON +// { +// $$ = lang_new_vers_node ($3, NULL); +// } + | LOCAL COLON vers_defns SEMICOLON +// { +// $$ = lang_new_vers_node (NULL, $3); +// } + | GLOBAL COLON vers_defns SEMICOLON LOCAL COLON vers_defns SEMICOLON +// { +// $$ = lang_new_vers_node ($3, $7); +// } + ; + +vers_defns: + VERS_IDENTIFIER +// { +// $$ = lang_new_vers_pattern (NULL, $1, ldgram_vers_current_lang, FALSE); +// } + | NAME +// { +// $$ = lang_new_vers_pattern (NULL, $1, ldgram_vers_current_lang, TRUE); +// } + | vers_defns SEMICOLON VERS_IDENTIFIER +// { +// $$ = lang_new_vers_pattern ($1, $3, ldgram_vers_current_lang, FALSE); +// } + | vers_defns SEMICOLON NAME +// { +// $$ = lang_new_vers_pattern ($1, $3, ldgram_vers_current_lang, TRUE); +// } + | vers_defns SEMICOLON EXTERN NAME LBRACE +// { +// $$ = ldgram_vers_current_lang; +// ldgram_vers_current_lang = $4; +// } + vers_defns opt_semicolon RBRACE +// { +// struct bfd_elf_version_expr *pat; +// for (pat = $7; pat->next != NULL; pat = pat->next); +// pat->next = $1; +// $$ = $7; +// ldgram_vers_current_lang = $6; +// } + | EXTERN NAME LBRACE +// { +// $$ = ldgram_vers_current_lang; +// ldgram_vers_current_lang = $2; +// } + vers_defns opt_semicolon RBRACE +// { +// $$ = $5; +// ldgram_vers_current_lang = $4; +// } + | GLOBAL +// { +// $$ = lang_new_vers_pattern (NULL, "global", ldgram_vers_current_lang, FALSE); +// } + | vers_defns SEMICOLON GLOBAL +// { +// $$ = lang_new_vers_pattern ($1, "global", ldgram_vers_current_lang, FALSE); +// } + | LOCAL +// { +// $$ = lang_new_vers_pattern (NULL, "local", ldgram_vers_current_lang, FALSE); +// } + | vers_defns SEMICOLON LOCAL +// { +// $$ = lang_new_vers_pattern ($1, "local", ldgram_vers_current_lang, FALSE); +// } + | EXTERN +// { +// $$ = lang_new_vers_pattern (NULL, "extern", ldgram_vers_current_lang, FALSE); +// } + | vers_defns SEMICOLON EXTERN +// { +// $$ = lang_new_vers_pattern ($1, "extern", ldgram_vers_current_lang, FALSE); +// } + ; + +opt_semicolon: + /* empty */ + | SEMICOLON + ; + +//name +// : NAME; + +string: STRING_ANY*; diff --git a/cmake/elfinfo/g4.sh b/cmake/elfinfo/g4.sh new file mode 100755 index 0000000..2f33070 --- /dev/null +++ b/cmake/elfinfo/g4.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +url="http://repo1.maven.org/maven2/org/antlr/antlr4/4.5.3/antlr4-4.5.3.jar" +jar="$(basename $url)" + +if [[ ! -r "$jar" ]] +then + wget "$url" +fi + +exec java -cp "$jar" org.antlr.v4.Tool "$@" diff --git a/cmake/elfinfo/pom.xml b/cmake/elfinfo/pom.xml index bb46e6f..60511b0 100644 --- a/cmake/elfinfo/pom.xml +++ b/cmake/elfinfo/pom.xml @@ -7,19 +7,28 @@ org.antlr antlr4 - 4.5.3 + ${antlr4-runtime.version} org.antlr antlr4-runtime - 4.5.3 + ${antlr4-runtime.version} com.google.code.findbugs jsr305 3.0.1 + + junit + junit + 4.12 + test + + + 4.5.3 + @@ -29,6 +38,50 @@ 1.8 + + org.antlr + antlr4-maven-plugin + ${antlr4-runtime.version} + + + gnu + + antlr4 + + + + -package + io.trygvis.ld.antlr + + + GnuLd*.g4 + + . + target/generated-sources/antlr4/io/trygvis/ld/antlr + + + + test1 + + antlr4 + + + + -package + io.trygvis.ld.test1 + + + **/Test1*.g4 + + target/generated-sources/antlr4/io/trygvis/ld/test1 + + + + + true + target/generated-sources/antlr4 + + diff --git a/cmake/elfinfo/src/main/antlr4/Test1Lexer.g4 b/cmake/elfinfo/src/main/antlr4/Test1Lexer.g4 new file mode 100644 index 0000000..142229d --- /dev/null +++ b/cmake/elfinfo/src/main/antlr4/Test1Lexer.g4 @@ -0,0 +1,22 @@ +lexer grammar Test1Lexer; + +channels { + WHITESPACE_CHANNEL +} + +NAME : [a-z]+; + +// Characters +C_PLUS : '+'; +C_MINUS : '-'; +C_QUOTE : '"' -> skip, pushMode(STRING); + +//SPACE : [ ]; + +WS + : [ \t\r\n]+ -> channel(WHITESPACE_CHANNEL) + ; + +mode STRING; +STRING_ANY : ~'"'; +STRING_END_QUOTE : '"' -> skip, popMode; diff --git a/cmake/elfinfo/src/main/antlr4/Test1Parser.g4 b/cmake/elfinfo/src/main/antlr4/Test1Parser.g4 new file mode 100644 index 0000000..affdfe5 --- /dev/null +++ b/cmake/elfinfo/src/main/antlr4/Test1Parser.g4 @@ -0,0 +1,23 @@ +parser grammar Test1Parser; + +options { + tokenVocab = Test1Lexer; +} + +name + : NAME + | STRING_ANY* + ; + +file + : stmt* + ; + +stmt + : expr + ; + +expr + : name C_PLUS name + | name C_MINUS name + ; diff --git a/cmake/elfinfo/src/main/java/io/trygvis/ld/CollectingErrorListener.java b/cmake/elfinfo/src/main/java/io/trygvis/ld/CollectingErrorListener.java new file mode 100644 index 0000000..968a02b --- /dev/null +++ b/cmake/elfinfo/src/main/java/io/trygvis/ld/CollectingErrorListener.java @@ -0,0 +1,17 @@ +package io.trygvis.ld; + +import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; + +import java.util.ArrayList; +import java.util.List; + +public class CollectingErrorListener extends BaseErrorListener { + public final List errors = new ArrayList<>(); + + public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { + String s = line + ":" + charPositionInLine + " " + msg; + errors.add(s); + } +} diff --git a/cmake/elfinfo/src/main/java/io/trygvis/ld/LdScript.java b/cmake/elfinfo/src/main/java/io/trygvis/ld/LdScript.java index f827a8b..f3c93ac 100644 --- a/cmake/elfinfo/src/main/java/io/trygvis/ld/LdScript.java +++ b/cmake/elfinfo/src/main/java/io/trygvis/ld/LdScript.java @@ -1,12 +1,14 @@ package io.trygvis.ld; -import io.trygvis.ld.antlr.GnuLdBaseVisitor; import io.trygvis.ld.antlr.GnuLdLexer; import io.trygvis.ld.antlr.GnuLdParser; +import io.trygvis.ld.antlr.GnuLdParserBaseVisitor; import org.antlr.v4.runtime.ANTLRFileStream; import org.antlr.v4.runtime.BufferedTokenStream; +import org.antlr.v4.runtime.ConsoleErrorListener; import org.antlr.v4.runtime.tree.ParseTreeProperty; +import java.io.File; import java.math.BigInteger; import java.util.ArrayList; import java.util.HashSet; @@ -15,6 +17,12 @@ import java.util.Set; public class LdScript { + public final List memoryAreas; + + public LdScript(List memoryAreas) { + this.memoryAreas = memoryAreas; + } + public enum MemoryAttribute { R, W, X, A, I, } @@ -76,39 +84,31 @@ public class LdScript { String name; } - public static void main(String[] args) throws Exception { - try { - work(args); - } catch (Exception e) { - e.printStackTrace(System.out); - } - } - - private static void work(String[] args) throws Exception { - args = new String[]{"lds/d2000.ld"}; + public static LdScript parse(File path) throws Exception { + GnuLdLexer lexer = new GnuLdLexer(new ANTLRFileStream(path.getAbsolutePath())); + BufferedTokenStream tokens = new BufferedTokenStream(lexer); + GnuLdParser parser = new GnuLdParser(tokens); + parser.setBuildParseTree(true); + parser.removeErrorListeners(); + ConsoleErrorListener errorListener = new ConsoleErrorListener(); + parser.addErrorListener(errorListener); + CollectingErrorListener collectingErrorListener = new CollectingErrorListener(); + parser.addErrorListener(collectingErrorListener); - GnuLdLexer lexer = new GnuLdLexer(new ANTLRFileStream(args[0])); - GnuLdParser parser = new GnuLdParser(new BufferedTokenStream(lexer)); + GnuLdParser.FileContext file = parser.file(); -// ElfInfoGnuLdBaseListener listener = new ElfInfoGnuLdBaseListener(); -// parser.addParseListener(listener); -// -// parser.file(); -// -// for (Section section : listener.sections) { -// System.out.println("section.name = " + section.name); -// } + if (parser.getNumberOfSyntaxErrors() > 0) { + throw new ParseErrorException(lexer, tokens, parser, collectingErrorListener.errors + ); + } ElfinfoGnuLdBaseVisitor visitor = new ElfinfoGnuLdBaseVisitor(); - visitor.visit(parser.file()); + visitor.visit(file); - System.out.println("--------------------------------------------------------"); - for (MemoryArea area : visitor.memoryAreas) { - System.out.println(" " + area.name + "(" + area.prettyAttributes() + ") : ORIGIN = " + area.prettyOrigin() + ", LENGTH=" + area.prettyLength()); - } + return new LdScript(visitor.memoryAreas); } - private static class ElfinfoExprGnuLdBaseVisitor extends GnuLdBaseVisitor { + private static class ElfinfoExprGnuLdBaseVisitor extends GnuLdParserBaseVisitor { private BigInteger value; private ParseTreeProperty es = new ParseTreeProperty<>(); @@ -227,7 +227,7 @@ public class LdScript { } @Override - public ElfinfoExprGnuLdBaseVisitor visitExpAlign2(GnuLdParser.ExpAlign2Context ctx) { + public ElfinfoExprGnuLdBaseVisitor visitExpAlignK(GnuLdParser.ExpAlignKContext ctx) { return visitChildren(ctx); } @@ -416,7 +416,7 @@ public class LdScript { } } - private static class ElfinfoGnuLdBaseVisitor extends GnuLdBaseVisitor { + public static class ElfinfoGnuLdBaseVisitor extends GnuLdParserBaseVisitor { public List memoryAreas = new ArrayList<>(); public List
sections = new ArrayList<>(); @@ -473,6 +473,7 @@ public class LdScript { if (!attributes.isEmpty()) { throw new RuntimeException("Attributes for memory areas can only be attributesInverted (with '!') as the first character in a specification; foo(!rw), not foo(x!rw)."); } +// String name = ctx.name().getText(); String name = ctx.NAME().getText(); System.out.println("ctx.ATTRIBUTE().getText() = " + name); @@ -480,4 +481,18 @@ public class LdScript { return visitChildren(ctx); } } + + public static class ParseErrorException extends Exception { + public final GnuLdLexer lexer; + public final BufferedTokenStream tokens; + public final GnuLdParser parser; + public final List errors; + + public ParseErrorException(GnuLdLexer lexer, BufferedTokenStream tokens, GnuLdParser parser, List errors) { + this.lexer = lexer; + this.tokens = tokens; + this.parser = parser; + this.errors = errors; + } + } } diff --git a/cmake/elfinfo/src/main/java/io/trygvis/ld/StringGnuLdVisitor.java b/cmake/elfinfo/src/main/java/io/trygvis/ld/StringGnuLdVisitor.java new file mode 100644 index 0000000..a975497 --- /dev/null +++ b/cmake/elfinfo/src/main/java/io/trygvis/ld/StringGnuLdVisitor.java @@ -0,0 +1,59 @@ +package io.trygvis.ld; + +import io.trygvis.ld.antlr.GnuLdParserBaseVisitor; +import io.trygvis.ld.antlr.GnuLdParser; +import org.antlr.v4.runtime.tree.ParseTree; + +import java.util.List; + +class StringGnuLdVisitor extends GnuLdParserBaseVisitor { + +// public static String parseName(GnuLdParser.NameContext ctx) { +// return ""; +// } + + /* + public static String parseName(GnuLdParser.NameContext ctx) { + StringGnuLdVisitor v = new StringGnuLdVisitor(); + return v.visit(ctx); + } + + private StringBuilder string = new StringBuilder(); + + @Override + public String visitNamePlain(GnuLdParser.NamePlainContext ctx) { + System.out.println("NameTest.visitNamePlain"); + String s = ctx.NAME().getText(); + string.append(s); + return s; + } + +// @Override +// public String visitName_or_space(GnuLdParser.Name_or_spaceContext ctx) { +// System.out.println("StringGnuLdBaseVisitor.visitName_or_space"); +// +// TerminalNode name = ctx.NAME(); +// String s = name != null ? name.getText() : " "; +// string.append(s); +// return s; +// } + + @Override + public String visitNameQuoted(GnuLdParser.NameQuotedContext ctx) { + System.out.println("StringGnuLdBaseVisitor.visitNameQuoted"); + List children = ctx.children; + System.out.println("children.size()) = " + children.size()); + + String s = ""; + for (int i = 1; i < children.size() - 1; i++) { + ParseTree part = children.get(i); + System.out.println("part.getText() = " + part.getText()); + s += part.getText(); + } + + return s; +// +// return visitChildren(ctx); + } + */ +} diff --git a/cmake/elfinfo/src/main/test/io/trygvis/ld/FullScriptsTest.java b/cmake/elfinfo/src/main/test/io/trygvis/ld/FullScriptsTest.java new file mode 100644 index 0000000..1452f1f --- /dev/null +++ b/cmake/elfinfo/src/main/test/io/trygvis/ld/FullScriptsTest.java @@ -0,0 +1,34 @@ +package io.trygvis.ld; + +import io.trygvis.ld.test1.Test1Test; +import org.junit.Test; + +import java.io.File; + +public class FullScriptsTest { + + @Test + public void testD2000() throws Exception { + fullScript2("lds/d2000.ld"); + } + + @Test + public void testStm32() throws Exception { + fullScript2("lds/stm32.ld"); + } + + private void fullScript2(String fileName) throws Exception { + try { + LdScript script = LdScript.parse(new File(fileName)); + + System.out.println("--------------------------------------------------------"); + for (LdScript.MemoryArea area : script.memoryAreas) { + System.out.println(" " + area.name + "(" + area.prettyAttributes() + ") : ORIGIN = " + area.prettyOrigin() + ", LENGTH=" + area.prettyLength()); + } + } catch (LdScript.ParseErrorException e) { + Test1Test.showTokens(e.tokens, e.parser); + System.out.println("Got " + e.errors.size() + " errors:"); + e.errors.forEach(System.out::println); + } + } +} diff --git a/cmake/elfinfo/src/main/test/io/trygvis/ld/InputSectionSpecTest.java b/cmake/elfinfo/src/main/test/io/trygvis/ld/InputSectionSpecTest.java new file mode 100644 index 0000000..2950061 --- /dev/null +++ b/cmake/elfinfo/src/main/test/io/trygvis/ld/InputSectionSpecTest.java @@ -0,0 +1,38 @@ +package io.trygvis.ld; + +import io.trygvis.ld.antlr.GnuLdLexer; +import io.trygvis.ld.antlr.GnuLdParser; +import org.antlr.v4.runtime.ANTLRInputStream; +import org.antlr.v4.runtime.CommonTokenStream; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class InputSectionSpecTest { + @Test + public void testName1() throws Exception { + a("*(.text)", "(input_section_spec (input_section_spec_no_keep (wildcard_spec (wildcard_name *)) ( (file_name_list (wildcard_spec (wildcard_name .text))) )))"); + } + + // This doesn't feel right + @Test + public void testName2() throws Exception { + a("*(.text.*)", "(input_section_spec (input_section_spec_no_keep (wildcard_spec (wildcard_name *)) ( (file_name_list (file_name_list (wildcard_spec (wildcard_name .text.))) opt_comma (wildcard_spec (wildcard_name *))) )))"); + } + + @Test + public void testName3() throws Exception { + a("KEEP(*/init_high.cpp.obj(.isr_vectors))", "(input_section_spec KEEP ( (input_section_spec_no_keep (wildcard_spec (wildcard_name *)) / init_high.cpp.obj ( .isr_vectors) ))"); + } + + private void a(String input, String expected) { + GnuLdParser parser = new GnuLdParser(new CommonTokenStream(new GnuLdLexer(new ANTLRInputStream(input)))); + parser.setBuildParseTree(true); + parser.removeErrorListeners(); + parser.addErrorListener(new SystemOutReportingErrorListener(input)); + GnuLdParser.Input_section_specContext input_section_spec = parser.input_section_spec(); + + assertEquals(input, expected, input_section_spec.toStringTree(parser)); + assertEquals(0, parser.getNumberOfSyntaxErrors()); + } +} diff --git a/cmake/elfinfo/src/main/test/io/trygvis/ld/NameTest.java b/cmake/elfinfo/src/main/test/io/trygvis/ld/NameTest.java new file mode 100644 index 0000000..8d7502f --- /dev/null +++ b/cmake/elfinfo/src/main/test/io/trygvis/ld/NameTest.java @@ -0,0 +1,50 @@ +package io.trygvis.ld; + +import io.trygvis.ld.antlr.GnuLdLexer; +import io.trygvis.ld.antlr.GnuLdParser; +import org.antlr.v4.runtime.ANTLRInputStream; +import org.antlr.v4.runtime.CommonTokenStream; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class NameTest { + @Test + public void testName1() throws Exception { + a("foo", "foo"); + } + + @Test + public void testName2() throws Exception { + a("\"foo bar\"", "foo bar"); + } + + // TODO: not really expected + @Test + public void testName3() throws Exception { + a("\"foo bar\"", "foo bar"); + } + + private void a(String input, String expected) { + /* + CommonTokenStream tokens = new CommonTokenStream(new GnuLdLexer(new ANTLRInputStream(input))); + tokens.fill(); + for (Object tok : tokens.getTokens()) { + System.out.println(tok); + } + GnuLdParser parser = new GnuLdParser(tokens); + +// StringGnuLdVisitor visitor = new StringGnuLdVisitor(); + GnuLdParser.NameContext name = parser.name(); + + System.out.println("Input : |" + input + "|"); + System.out.println("String tree: " + name.toStringTree(parser)); + +// visitor.visit(name); + String actual = StringGnuLdVisitor.parseName(name); + + parser.setTrace(true); + assertEquals(input + "\n" + name.toStringTree(), expected, actual); + */ + } +} diff --git a/cmake/elfinfo/src/main/test/io/trygvis/ld/SystemOutReportingErrorListener.java b/cmake/elfinfo/src/main/test/io/trygvis/ld/SystemOutReportingErrorListener.java new file mode 100644 index 0000000..c21a2e2 --- /dev/null +++ b/cmake/elfinfo/src/main/test/io/trygvis/ld/SystemOutReportingErrorListener.java @@ -0,0 +1,32 @@ +package io.trygvis.ld; + +import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class SystemOutReportingErrorListener extends BaseErrorListener { + @Nullable + private final String input; + + public SystemOutReportingErrorListener(@Nonnull String input) { + this.input = input; + } + + public SystemOutReportingErrorListener() { + input = null; + } + + public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { + System.out.println("line " + line + ":" + charPositionInLine + " " + msg); + if (input != null) { + System.out.println(input); + for (int i = 1; i < charPositionInLine; i++) { + System.out.print(' '); + } + } + System.out.println('^'); + } +} diff --git a/cmake/elfinfo/src/main/test/io/trygvis/ld/test1/Test1Test.java b/cmake/elfinfo/src/main/test/io/trygvis/ld/test1/Test1Test.java new file mode 100644 index 0000000..ca3f49a --- /dev/null +++ b/cmake/elfinfo/src/main/test/io/trygvis/ld/test1/Test1Test.java @@ -0,0 +1,102 @@ +package io.trygvis.ld.test1; + +import org.antlr.v4.runtime.ANTLRInputStream; +import org.antlr.v4.runtime.BufferedTokenStream; +import org.antlr.v4.runtime.CommonToken; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.Token; +import org.junit.Test; + +import javax.annotation.Nonnull; + +import static org.junit.Assert.assertEquals; + +public class Test1Test { + + @Test + public void test1() { + name("foo", "foo"); + } + + @Test + public void test2() { + name("\"foo bar\"", "foo bar"); + } + + private void name(String input, String expected) { + Test1Parser parser = load(input); + + Test1Parser.NameContext name = parser.name(); + System.out.println("String tree: " + name.toStringTree(parser)); + + String actual = name.getText(); + assertEquals(input + "\n" + name.toStringTree(parser), expected, actual); + } + + @Test + public void expr1() { + expr("a + b", "(expr (name a) + (name b))"); + } + + @Test + public void expr2() { + expr("\"foo bar\" + b", "(expr (name f o o b a r) + (name b))"); + } + + @Test + public void expr3() { + expr("\n\n\r\n\t\"foo bar\" + b\r\r\t", "(expr (name f o o b a r) + (name b))"); + } + + private void expr(String input, String expected) { + Test1Parser parser = load(input); + + Test1Parser.ExprContext expr = parser.expr(); + String stringTree = expr.toStringTree(parser); + + assertEquals(expected, stringTree); + } + + @Nonnull + private Test1Parser load(String input) { + System.out.println("Input : |" + input + "|"); + CommonTokenStream tokens = new CommonTokenStream(new Test1Lexer(new ANTLRInputStream(input))); + Test1Parser parser = new Test1Parser(tokens); + parser.setBuildParseTree(true); + parser.setTrace(true); + + tokens.fill(); + showTokens(tokens, parser); + return parser; + } + + public static void showTokens(BufferedTokenStream tokens, Parser parser) { + for (Token tok : tokens.getTokens()) { + String s; + if (tok instanceof CommonToken) { + CommonToken t = (CommonToken) tok; + + String channelStr = ""; + if (t.getChannel() > 0) { + channelStr = ",channel=" + t.getChannel(); + } + + String txt = t.getText(); + if (txt != null) { + txt = txt.replace("\n", "\\n"); + txt = txt.replace("\r", "\\r"); + txt = txt.replace("\t", "\\t"); + } else { + txt = ""; + } + + String type = parser.getVocabulary().getDisplayName(t.getType()); + s = "[@" + t.getTokenIndex() + "," + t.getStartIndex() + ":" + t.getStopIndex() + "=\'" + txt + "\',<" + type + ">" + channelStr + "," + t.getLine() + ":" + t.getCharPositionInLine() + "]"; + } else { + s = tok.toString(); + } + System.out.println(s); + } + } +} -- cgit v1.2.3