as: handle .section directive

This commit is contained in:
Mikael Pettersson 2020-01-03 21:39:43 +01:00
parent 72df4dc2b3
commit 14184eb4cd
5 changed files with 345 additions and 16 deletions

View File

@ -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 <nr>
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) ->

View File

@ -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 <name> <sectionspec>
%% .pushsection <name> [, <nr>] <sectionspec>
%%
%% <sectionspec> ::= [, <flags> [, <type>]]
%% | , <flags>, <type>, <entsize> (if <flags> contain M)
%%
%% <flags> ::= '"' <flagschar>* '"'
%% <flagschar> ::= [adexwMST0-9]
%%
%% <type> ::= "@progbits" | "@nobits" | "@note" | "@init_array" | "@fini_array"
%% | "@preinit_array"
%%
%% TODO: add support for "G" and "?" flags, and <groupname> and <linkage> specs:
%%
%% <sectionspec> ::= , <flags>, <type>, <groupname> [, <linkage>] (if <flags> contain G)
%% | , <flags>, <type>, <entsize>, <groupname> [, <linkage>] (if <flags> contain both M and G)
%%
%% <linkage> ::= "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 <flags>", ScanRes)
end;
_ -> badtok("expected <string>", 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 <symbol> or <uinteger>", 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 <uinteger>", ScanRes)
end;
ScanRes -> badtok("expected ,<entsize>", 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 <newline>", 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 -----------------------------------------------------------------
%% <expr_list> ::= (<expr> ("," <expr>)*)?

View File

@ -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";

View File

@ -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

View File

@ -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 ,<group>,<linkage>
-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{}