diff --git a/erlang/apps/as/src/input.erl b/erlang/apps/as/src/input.erl index 5c1c8c3..57d4a8d 100644 --- a/erlang/apps/as/src/input.erl +++ b/erlang/apps/as/src/input.erl @@ -1,7 +1,7 @@ %%% -*- erlang-indent-level: 2 -*- %%% %%% input processing phase for pdp10-elf as -%%% Copyright (C) 2013-2019 Mikael Pettersson +%%% Copyright (C) 2013-2020 Mikael Pettersson %%% %%% This file is part of pdp10-tools. %%% @@ -93,6 +93,7 @@ pass1_stmt(Location, Ctx, Stmt) -> #s_dot_popsection{} -> dot_popsection(Location, Ctx, Stmt); #s_dot_previous{} -> dot_previous(Location, Ctx, Stmt); #s_dot_pushsection{} -> dot_pushsection(Location, Ctx, Stmt); + #s_dot_section{} -> dot_section(Location, Ctx, Stmt); #s_dot_subsection{} -> dot_subsection(Location, Ctx, Stmt); #s_dot_text{} -> dot_text(Location, Ctx, Stmt); _ -> {ok, ctx_append(Ctx, Location, Stmt)} @@ -117,6 +118,16 @@ dot_pushsection(Location, Ctx, #s_dot_pushsection{name = SectionName, nr = SubsectionNr}) -> ctx_pushsection(Ctx, Location, SectionName, SubsectionNr). +dot_section(Location, Ctx, Stmt) -> + #s_dot_section{ name = SectionName + , nr = SubsectionNrOpt + , sh_flags = ShFlags + , sh_type = ShType + , sh_entsize = ShEntSize + } = Stmt, + ctx_section(Ctx, Location, SectionName, SubsectionNrOpt, + ShFlags, ShType, ShEntSize). + dot_subsection(_Location, Ctx, #s_dot_subsection{nr = SubsectionNr}) -> {ok, ctx_subsection(Ctx, SubsectionNr)}. @@ -201,6 +212,36 @@ ctx_pushsection(Ctx0, Location, SectionName, SubsectionNr) -> % implements .push {error, _Reason} = Error -> Error end. +ctx_section(Ctx0, Location, SectionName, SubsectionNrOpt, + ShFlags, ShType, ShEntSize) -> + {IsPushsection, SubsectionNr} = + case SubsectionNrOpt of + false -> {false, 0}; + _ -> {true, SubsectionNrOpt} + end, + Ctx = ctx_flush(Ctx0), + #ctx{ sections_map = SectionsMap0 + , stack = Stack + , current = Current + , previous = Previous + } = Ctx, + case enter_section(Location, SectionName, SubsectionNr, SectionsMap0, + ShFlags, ShType, ShEntSize) of + {ok, {Stmts, SectionsMap}} -> + NewStack = + case IsPushsection of + true -> [{Current, Previous} | Stack]; + false -> Stack + end, + {ok, Ctx#ctx{ sections_map = SectionsMap + , stack = NewStack + , current = {SectionName, SubsectionNr} + , previous = Current + , stmts = Stmts + }}; + {error, _Reason} = Error -> Error + end. + ctx_subsection(Ctx0, SubsectionNr) -> % implements .subsection Ctx = ctx_flush(Ctx0), #ctx{ sections_map = SectionsMap @@ -236,21 +277,118 @@ ctx_append(Ctx, Location, Stmt) -> #ctx{stmts = Stmts} = Ctx, Ctx#ctx{stmts = [{Location, Stmt} | Stmts]}. -enter_section(Location, SectionName, SubsectionNr, SectionsMap0) -> - case maps:get(SectionName, SectionsMap0, false) of +enter_section(Location, SectionName, SubsectionNr, SectionsMap) -> + enter_section(Location, SectionName, SubsectionNr, SectionsMap, + _ShFlags = 0, _ShType = 0, _ShEntSize = 0). + +enter_section(Location, SectionName, SubsectionNr, SectionsMap0, + ShFlags, ShType, ShEntSize) -> + {Section0, SubsectionsMap} = get_section(SectionName, SectionsMap0), + case update_section(Location, Section0, ShFlags, ShType, ShEntSize) of + {ok, Section} -> + SectionsMap = maps:put(SectionName, {Section, SubsectionsMap}, SectionsMap0), + Stmts = maps:get(SubsectionNr, SubsectionsMap, []), + {ok, {Stmts, SectionsMap}}; + {error, _Reason} = Error -> Error + end. + +get_section(SectionName, SectionsMap) -> + case maps:get(SectionName, SectionsMap, false) of + {_Section, _SubsectionsMap} = Result -> Result; false -> case section_from_name(SectionName) of - false -> - fmterr(Location, "unknown section ~s", [SectionName]); - Section = #section{} -> + #section{} = Section -> SubsectionsMap = #{}, - SectionsMap = maps:put(SectionName, {Section, SubsectionsMap}, SectionsMap0), - Stmts = [], - {ok, {Stmts, SectionsMap}} + {Section, SubsectionsMap}; + false -> + Section = make_section(SectionName), + SubsectionsMap = #{}, + {Section, SubsectionsMap} + end + end. + +make_section(SectionName) -> + %% update_section/5 will set sh_flags, sh_type, and sh_entsize + #section{ name = SectionName + , data = {stmts, []} + , dot = 0 + , shndx = 0 + , sh_name = 0 + , sh_type = 0 + , sh_offset = 0 + , sh_flags = 0 + , sh_link = ?SHN_UNDEF + , sh_addralign = 0 + , sh_entsize = 0 + }. + +update_section(Location, Section0, ShFlags, ShType, ShEntSize) -> + case update_sh_flags(Location, Section0, ShFlags) of + {ok, Section1} -> + case update_sh_type(Location, Section1, ShType) of + {ok, Section2} -> + update_sh_entsize(Location, Section2, ShEntSize); + {error, _Reason} = Error -> Error end; - {_Section, SubsectionsMap} -> - Stmts = maps:get(SubsectionNr, SubsectionsMap, []), - {ok, {Stmts, SectionsMap0}} + {error, _Reason} = Error -> Error + end. + +update_sh_flags(Location, Section, ShFlags) -> + case ShFlags of + 0 -> {ok, Section}; + _ -> + case Section#section.sh_flags of + 0 -> {ok, Section#section{sh_flags = ShFlags}}; + ShFlags -> {ok, Section}; + ShFlags0 -> + case may_update_sh_flags(Section, ShFlags) of + true -> {ok, Section#section{sh_flags = ShFlags0 bor ShFlags}}; + false -> fmterr(Location, "cannot change section flags", []) + end + end + end. + +may_update_sh_flags(Section, ShFlags) -> + %% Processor and application-specific flags may be added to an existing + %% section. The range of application-specific flags isn't defined in the + %% ELF spec: we interpret that as any flag outside of the reserved ranges. + ReservedMask = (?SHF_COMPRESSED * 2 - 1) bor ?SHF_MASKOS, + case (ShFlags band ReservedMask) =:= 0 of + true -> true; + false -> + case ShFlags of + ?SHF_ALLOC -> + case Section#section.name of + ".interp" -> true; + ".strtab" -> true; + ".symtab" -> true; + _ -> false + end; + ?SHF_EXECINSTR -> Section#section.name =:= ".note.GNU-stack"; + _ -> false + end + end. + +update_sh_type(Location, Section, ShType) -> + case ShType of + 0 -> {ok, Section}; + _ -> + case Section#section.sh_type of + 0 -> {ok, Section#section{sh_type = ShType}}; + ShType -> {ok, Section}; + _ -> fmterr(Location, "cannot change section type", []) + end + end. + +update_sh_entsize(Location, Section, ShEntSize) -> + case ShEntSize of + 0 -> {ok, Section}; + _ -> + case Section#section.sh_entsize of + 0 -> {ok, Section#section{sh_entsize = ShEntSize}}; + ShEntSize -> {ok, Section}; + _ -> fmterr(Location, "cannot change section element size", []) + end end. enter_subsection(SectionName, SubsectionNr, SectionsMap) -> diff --git a/erlang/apps/as/src/parse.erl b/erlang/apps/as/src/parse.erl index 4fc217e..aac3f5e 100644 --- a/erlang/apps/as/src/parse.erl +++ b/erlang/apps/as/src/parse.erl @@ -1,7 +1,7 @@ %%% -*- erlang-indent-level: 2 -*- %%% %%% parser for pdp10-elf as -%%% Copyright (C) 2013-2019 Mikael Pettersson +%%% Copyright (C) 2013-2020 Mikael Pettersson %%% %%% This file is part of pdp10-tools. %%% @@ -27,6 +27,7 @@ -include("token.hrl"). -include("tunit.hrl"). -include_lib("lib/include/pdp10_opcodes.hrl"). +-include_lib("lib/include/pdp10_elf36.hrl"). -type location() :: scan:location(). @@ -46,6 +47,7 @@ stmt(ScanState) -> {ok, {Location, ?T_DOT_POPSECTION}} -> dot_popsection(ScanState, Location); {ok, {Location, ?T_DOT_PREVIOUS}} -> dot_previous(ScanState, Location); {ok, {Location, ?T_DOT_PUSHSECTION}} -> dot_pushsection(ScanState, Location); + {ok, {Location, ?T_DOT_SECTION}} -> dot_section(ScanState, Location); {ok, {Location, ?T_DOT_SHORT}} -> dot_short(ScanState, Location); {ok, {Location, ?T_DOT_SIZE}} -> dot_size(ScanState, Location); {ok, {Location, ?T_DOT_SUBSECTION}} -> dot_subsection(ScanState, Location); @@ -478,6 +480,177 @@ dot_type(ScanState, Location) -> dot_word(ScanState, Location) -> dot_long(ScanState, Location, ".word"). +%% .section/.pushsection directives -------------------------------------------- +%% +%% .section +%% .pushsection [, ] +%% +%% ::= [, [, ]] +%% | , , , (if contain M) +%% +%% ::= '"' * '"' +%% ::= [adexwMST0-9] +%% +%% ::= "@progbits" | "@nobits" | "@note" | "@init_array" | "@fini_array" +%% | "@preinit_array" +%% +%% TODO: add support for "G" and "?" flags, and and specs: +%% +%% ::= , , , [, ] (if contain G) +%% | , , , , [, ] (if contain both M and G) +%% +%% ::= "comdat" | ".gnu.linkonce" + +dot_section(ScanState, Location) -> + dot_section_name(ScanState, Location, _IsPushsection = false). + +dot_section_name(ScanState, Location, IsPushsection) -> + case section_name(ScanState) of + {ok, Name} -> dot_section_subsection(ScanState, Location, Name, IsPushsection); + {error, _Reason} = Error -> Error + end. + +dot_section_subsection(ScanState, Location, Name, IsPushsection) -> + case IsPushsection of + true -> + case scan:token(ScanState) of + {ok, {_Location1, ?T_NEWLINE}} -> + dot_section_finish(Location, Name, _Nr = 0, _ShFlags = 0, _ShType = 0, _ShEntSize = 0); + {ok, {_Location1, ?T_COMMA}} -> + case scan:token(ScanState) of + {ok, {_Location2, {?T_UINTEGER, Nr}}} -> + dot_section_flags(ScanState, Location, Name, Nr); + ScanRes -> + dot_section_flags(ScanState, Location, Name, _Nr = 0, ScanRes) + end; + ScanRes -> badtok("expected comma or newline", ScanRes) + end; + false -> dot_section_flags(ScanState, Location, Name, _Nr = false) + end. + +dot_section_flags(ScanState, Location, Name, Nr) -> + case scan:token(ScanState) of + {ok, {_Location, ?T_NEWLINE}} -> + dot_section_finish(Location, Name, Nr, _ShFlags = 0, _ShType = 0, _ShEntSize = 0); + {ok, {_Location, ?T_COMMA}} -> + dot_section_flags(ScanState, Location, Name, Nr, scan:token(ScanState)); + ScanRes -> badtok("expected comma or newline", ScanRes) + end. + +dot_section_flags(ScanState, Location, Name, Nr, ScanRes) -> + case ScanRes of + {ok, {_Location, {?T_STRING, String}}} -> + case sh_flags(String) of + {ok, ShFlags} -> dot_section_type(ScanState, Location, Name, Nr, ShFlags); + false -> badtok("invalid ", ScanRes) + end; + _ -> badtok("expected ", ScanRes) + end. + +sh_flags(String) -> sh_flags(String, 0). + +sh_flags([], ShFlags) -> {ok, ShFlags}; +sh_flags([C | Cs] = String, ShFlags) -> + case sh_flag(C) of + false -> + case strtol:parse(String, _Base = 10) of + {ok, {Mask, Rest}} -> sh_flags(Rest, ShFlags bor Mask); + {error, _Reason} -> false + end; + Flag -> sh_flags(Cs, ShFlags bor Flag) + end. + +sh_flag(C) -> + case C of + $a -> ?SHF_ALLOC; + $d -> ?SHF_GNU_MBIND; + $e -> ?SHF_EXCLUDE; + $w -> ?SHF_WRITE; + $x -> ?SHF_EXECINSTR; + $M -> ?SHF_MERGE; + $S -> ?SHF_STRINGS; + $T -> ?SHF_TLS; + %% TODO: permit $G and $? + _ -> false + end. + +dot_section_type(ScanState, Location, Name, Nr, ShFlags) -> + case scan:token(ScanState) of + {ok, {_Location1, ?T_NEWLINE}} = ScanRes -> + case (ShFlags band (?SHF_MERGE bor ?SHF_GROUP)) =/= 0 of + true -> badtok("expected ,@type", ScanRes); + false -> + dot_section_finish(Location, Name, Nr, ShFlags, _ShType = 0, _ShEntSize = 0) + end; + {ok, {_Location1, ?T_COMMA}} -> + case sh_type(ScanState) of + {ok, ShType} -> dot_section_entsize(ScanState, Location, Name, Nr, ShFlags, ShType); + {error, _Reason} = Error -> Error + end; + ScanRes -> badtok("expected comma or newline", ScanRes) + end. + +sh_type(ScanState) -> + case scan:token(ScanState) of + {ok, {_Location1, ?T_AT}} -> + case scan:token(ScanState) of + {ok, {_Location2, {?T_SYMBOL, Name}}} = ScanRes -> + case Name of + "progbits" -> {ok, ?SHT_PROGBITS}; + "nobits" -> {ok, ?SHT_NOBITS}; + "note" -> {ok, ?SHT_NOTE}; + "init_array" -> {ok, ?SHT_INIT_ARRAY}; + "fini_array" -> {ok, ?SHT_FINI_ARRAY}; + "preinit_array" -> {ok, ?SHT_PREINIT_ARRAY}; + _ -> badtok("invalid @type", ScanRes) + end; + {ok, {_Location2, {?T_UINTEGER, ShType}}} -> {ok, ShType}; + ScanRes -> badtok("expected or ", ScanRes) + end; + ScanRes -> badtok("expected @type", ScanRes) + end. + +dot_section_entsize(ScanState, Location, Name, Nr, ShFlags, ShType) -> + case (ShFlags band ?SHF_MERGE) =/= 0 of + true -> + case scan:token(ScanState) of + {ok, {_Location1, ?T_COMMA}} -> + case scan:token(ScanState) of + {ok, {_Location2, {?T_UINTEGER, ShEntSize}}} -> + dot_section_newline(ScanState, Location, Name, Nr, ShFlags, ShType, ShEntSize); + ScanRes -> badtok("expected ", ScanRes) + end; + ScanRes -> badtok("expected ,", ScanRes) + end; + false -> + dot_section_newline(ScanState, Location, Name, Nr, ShFlags, ShType, _ShEntSize = 0) + end. + +dot_section_newline(ScanState, Location, Name, Nr, ShFlags, ShType, ShEntSize) -> + case scan:token(ScanState) of + {ok, {_Location, ?T_NEWLINE}} -> + dot_section_finish(Location, Name, Nr, ShFlags, ShType, ShEntSize); + ScanRes -> badtok("expected ", ScanRes) + end. + +dot_section_finish(Location, Name, Nr, ShFlags, ShType, ShEntSize) -> + {ok, {Location, #s_dot_section{ name = Name + , nr = Nr + , sh_type = ShType + , sh_flags = ShFlags + , sh_entsize = ShEntSize + }}}. + +section_name(ScanState) -> + case scan:token(ScanState) of + {ok, {_Location, {?T_STRING, Name}}} -> {ok, Name}; + {ok, {_Location, {?T_SYMBOL, Name}}} -> {ok, Name}; + %% TODO: do we need a general mapping from reserved to plain symbols? + {ok, {_Location, ?T_DOT_DATA}} -> {ok, ".data"}; + {ok, {_Location, ?T_DOT_TEXT}} -> {ok, ".text"}; + ScanRes -> badtok("invalid section name", ScanRes) + end. + %% Expressions ----------------------------------------------------------------- %% ::= ( ("," )*)? diff --git a/erlang/apps/as/src/token.erl b/erlang/apps/as/src/token.erl index 8506cd5..f595d55 100644 --- a/erlang/apps/as/src/token.erl +++ b/erlang/apps/as/src/token.erl @@ -1,7 +1,7 @@ %%% -*- erlang-indent-level: 2 -*- %%% %%% token handling for pdp10-elf as -%%% Copyright (C) 2013-2019 Mikael Pettersson +%%% Copyright (C) 2013-2020 Mikael Pettersson %%% %%% This file is part of pdp10-tools. %%% @@ -41,6 +41,7 @@ from_symbol(Name) -> ".popsection" -> ?T_DOT_POPSECTION; ".previous" -> ?T_DOT_PREVIOUS; ".pushsection" -> ?T_DOT_PUSHSECTION; + ".section" -> ?T_DOT_SECTION; ".short" -> ?T_DOT_SHORT; ".size" -> ?T_DOT_SIZE; ".subsection" -> ?T_DOT_SUBSECTION; @@ -65,6 +66,7 @@ format(Token) -> ?T_DOT_POPSECTION -> ".popsection"; ?T_DOT_PREVIOUS -> ".previous"; ?T_DOT_PUSHSECTION -> ".pushsection"; + ?T_DOT_SECTION -> ".section"; ?T_DOT_SHORT -> ".short"; ?T_DOT_SIZE -> ".size"; ?T_DOT_SUBSECTION -> ".subsection"; diff --git a/erlang/apps/as/src/token.hrl b/erlang/apps/as/src/token.hrl index 1a3da82..51f48cb 100644 --- a/erlang/apps/as/src/token.hrl +++ b/erlang/apps/as/src/token.hrl @@ -1,7 +1,7 @@ %%% -*- erlang-indent-level: 2 -*- %%% %%% token definitions for pdp10-elf as -%%% Copyright (C) 2013-2019 Mikael Pettersson +%%% Copyright (C) 2013-2020 Mikael Pettersson %%% %%% This file is part of pdp10-tools. %%% @@ -38,6 +38,7 @@ -define(T_DOT_POPSECTION, 'T_DOT_POPSECTION'). % .popsection -define(T_DOT_PREVIOUS, 'T_DOT_PREVIOUS'). % .previous -define(T_DOT_PUSHSECTION, 'T_DOT_PUSHSECTION'). % .pushsection +-define(T_DOT_SECTION, 'T_DOT_SECTION'). % .section -define(T_DOT_SHORT, 'T_DOT_SHORT'). % .short -define(T_DOT_SIZE, 'T_DOT_SIZE'). % .size -define(T_DOT_SUBSECTION, 'T_DOT_SUBSECTION'). % .subsection @@ -78,6 +79,7 @@ | ?T_DOT_POPSECTION | ?T_DOT_PREVIOUS | ?T_DOT_PUSHSECTION + | ?T_DOT_SECTION | ?T_DOT_SHORT | ?T_DOT_SIZE | ?T_DOT_SUBSECTION diff --git a/erlang/apps/as/src/tunit.hrl b/erlang/apps/as/src/tunit.hrl index 64fc667..c9f6cdd 100644 --- a/erlang/apps/as/src/tunit.hrl +++ b/erlang/apps/as/src/tunit.hrl @@ -1,7 +1,7 @@ %%% -*- erlang-indent-level: 2 -*- %%% %%% translation unit declarations for pdp10-elf as. -%%% Copyright (C) 2013-2019 Mikael Pettersson +%%% Copyright (C) 2013-2020 Mikael Pettersson %%% %%% This file is part of pdp10-tools. %%% @@ -66,6 +66,19 @@ %% .pushsection name [, nr] (TODO: extend) -record(s_dot_pushsection, {name :: string(), nr :: non_neg_integer()}). +%% .section name, "flags", @type, ... +%% .pushsection name, [, nr], "flags", @type, ... +%% TODO: add support for G and ? flags and ,, +-record(s_dot_section, + { name :: string() + %% nr is false for .section and a subsection number for .pushsection + , nr :: false | non_neg_integer() + %% the following are as per the Elf36 Shdr spec + , sh_type :: non_neg_integer() + , sh_flags :: non_neg_integer() + , sh_entsize :: non_neg_integer() + }). + %% .short [expr (, expr)*] -record(s_dot_short, {exprs :: [expr()]}). @@ -102,6 +115,7 @@ | #s_dot_popsection{} | #s_dot_previous{} | #s_dot_pushsection{} + | #s_dot_section{} | #s_dot_size{} | #s_dot_subsection{} | #s_dot_text{}