as: rewrite in Erlang, initial version

This commit is contained in:
Mikael Pettersson 2019-08-11 21:50:14 +02:00
parent c184c9b4a8
commit ce0595ef44
15 changed files with 2057 additions and 1 deletions

View File

@ -21,7 +21,7 @@ REBAR3=$(shell type -p rebar3 || echo ./rebar3)
REBAR3_GIT=https://github.com/erlang/rebar3.git
REBAR3_VSN=3.7.5
PROGRAMS=8to9 ar nm od
PROGRAMS=8to9 ar as nm od
default: compile link

View File

@ -0,0 +1,31 @@
/*
* test1.s
* Copyright (C) 2013-2019 Mikael Pettersson
*
* This file is part of pdp10-tools.
*
* pdp10-tools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* pdp10-tools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
*/
/* int foo(void) { return 27; }
*/
.file "test1.c"
.text
.globl foo
.type foo,@function
foo:
movei 1,033
popj 017,
.size foo,.-foo
.ident "GCC: (GNU) 4.3.0.- for XKL-2 (XKL LLC, Kirkland, WA, USA) Built 2013-08-15 23:03 +0200 on porter by mikpe"

View File

@ -0,0 +1,25 @@
%%% Copyright (C) 2019 Mikael Pettersson
%%%
%%% This file is part of pdp10-tools.
%%%
%%% pdp10-tools is free software: you can redistribute it and/or modify
%%% it under the terms of the GNU General Public License as published by
%%% the Free Software Foundation, either version 3 of the License, or
%%% (at your option) any later version.
%%%
%%% pdp10-tools is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%%% GNU General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
{application, as,
[{description, "'as' clone for pdp10-elf"},
{vsn, "0.1.0"},
{registered, []},
{applications, [kernel, stdlib, lib]},
{env, []},
{modules, []}
]}.

75
erlang/apps/as/src/as.erl Normal file
View File

@ -0,0 +1,75 @@
%%% -*- erlang-indent-level: 2 -*-
%%%
%%% 'as' clone for pdp10-elf
%%% Copyright (C) 2013-2019 Mikael Pettersson
%%%
%%% This file is part of pdp10-tools.
%%%
%%% pdp10-tools is free software: you can redistribute it and/or modify
%%% it under the terms of the GNU General Public License as published by
%%% the Free Software Foundation, either version 3 of the License, or
%%% (at your option) any later version.
%%%
%%% pdp10-tools is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%%% GNU General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
-module(as).
-export([main/1]).
%% Command-line interface ======================================================
main(Argv) ->
escript_runtime:start(fun main_/1, Argv).
main_(Argv) ->
case getopt:parse(Argv, "vo:",
[ {"version", no, version}
]) of
{ok, {Options, Files}} ->
OutFile = scan_options(Options),
case as(Files, OutFile) of
ok -> halt(0);
{error, _Reason} = Error -> escript_runtime:fatal("~p\n", [Error])
end;
{error, ErrMsg} ->
escript_runtime:errmsg("~s\n", [ErrMsg]),
usage()
end.
scan_options(Options) ->
lists:foldl(fun scan_option/2, "a.out", Options).
scan_option($v, OutFile) ->
version(),
OutFile;
scan_option(version, _OutFile) ->
version(),
halt(0);
scan_option({$o, OutFile}, _OutFile) ->
OutFile.
usage() ->
escript_runtime:fmterr(
"Usage: ~s [-v] [-o objfile] [files..]\n",
[escript_runtime:progname()]),
halt(1).
version() ->
io:format(standard_io, "pdp10-tools as version 0.2\n", []).
%% As ==========================================================================
as(Files, OutFile) ->
case input:files(Files) of
{ok, Tunit0} ->
case assemble:tunit(Tunit0) of
{ok, Tunit} -> output:tunit(Tunit, OutFile);
{error, _Reason} = Error -> Error
end;
{error, _Reason} = Error -> Error
end.

View File

@ -0,0 +1,102 @@
%%% -*- erlang-indent-level: 2 -*-
%%%
%%% sections assembler for pdp10-elf as
%%% Copyright (C) 2013-2019 Mikael Pettersson
%%%
%%% This file is part of pdp10-tools.
%%%
%%% pdp10-tools is free software: you can redistribute it and/or modify
%%% it under the terms of the GNU General Public License as published by
%%% the Free Software Foundation, either version 3 of the License, or
%%% (at your option) any later version.
%%%
%%% pdp10-tools is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%%% GNU General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
-module(assemble).
-export([ tunit/1
]).
-include("tunit.hrl").
-include_lib("lib/include/pdp10_elf36.hrl").
tunit(Tunit) ->
sections(maps:values(Tunit#tunit.sections), Tunit).
sections([], Tunit) -> {ok, Tunit};
sections([Section | Sections], Tunit) ->
case section(Section, Tunit) of
{ok, NewTunit} -> sections(Sections, NewTunit);
{error, _Reason} = Error -> Error
end.
section(Section, Tunit) ->
case Section of
#section{ name = ".text" ++ _
, sh_type = ?SHT_PROGBITS
, sh_flags = ?SHF_ALLOC bor ?SHF_EXECINSTR
} -> text(Section, Tunit);
#section{ name = ".comment"
, sh_type = ?SHT_PROGBITS
, sh_flags = ?SHF_MERGE bor ?SHF_STRINGS
} -> comment(Section, Tunit);
#section{ name = Name } ->
{error, io_lib:format("don't know how to assemble section ~s", [Name])}
end.
%% Assemble .comment -----------------------------------------------------------
%%
%% The image starts with a NUL, followed by the strings, all NUL-terminated.
comment(Section = #section{data = {stmts, Stmts}}, Tunit) ->
Image = comment_image(Stmts),
NewSection = Section#section{data = {image, Image}, dot = image_size(Image)},
{ok, tunit:put_section(Tunit, NewSection)}.
comment_image(Stmts) -> comment_image(Stmts, []).
comment_image([], []) -> [];
comment_image([], Acc) -> lists:reverse([0 | Acc]);
comment_image([#s_dot_ident{string = String} | Stmts], Acc) ->
comment_image(Stmts, [[0 | String] | Acc]).
%% FIXME: duplicated
image_size(Image) -> image_size(Image, 0).
image_size([H | T], Acc) -> image_size(T, image_size(H, Acc));
image_size([], Acc) -> Acc;
image_size(TByte, Acc) when is_integer(TByte), 0 =< TByte, TByte =< 511 -> Acc + 1.
%% Assemble .text --------------------------------------------------------------
text(Section = #section{data = {stmts, Stmts}}, Tunit) ->
Image = text_image(Stmts),
{ok, tunit:put_section(Tunit, Section#section{data = {image, Image}})}.
text_image(Stmts) -> text_image(Stmts, []).
text_image([], Acc) -> Acc; % the input Stmts were in reverse order
text_image([Stmt | Stmts], Acc) ->
text_image(Stmts, [insn_image(Stmt) | Acc]).
insn_image(Insn) ->
#s_insn{ high13 = High13
, at = At
, address = Address
, index = Index
} = Insn,
Word = (((High13 band ((1 bsl 13) - 1)) bsl (36 - 13)) bor
((case At of true -> 1; false -> 0 end) bsl (36 - 14)) bor
((Index band ((1 bsl 4) - 1)) bsl (36 - 18)) bor
(Address band ((1 bsl 18) - 1))),
%% big-endian conversion
[(Word bsr 27) band 511,
(Word bsr 18) band 511,
(Word bsr 9) band 511,
Word band 511].

View File

@ -0,0 +1,243 @@
%%% -*- erlang-indent-level: 2 -*-
%%%
%%% input processing phase for pdp10-elf as
%%% Copyright (C) 2013-2019 Mikael Pettersson
%%%
%%% This file is part of pdp10-tools.
%%%
%%% pdp10-tools is free software: you can redistribute it and/or modify
%%% it under the terms of the GNU General Public License as published by
%%% the Free Software Foundation, either version 3 of the License, or
%%% (at your option) any later version.
%%%
%%% pdp10-tools is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%%% GNU General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
-module(input).
-export([ files/1
]).
-include("tunit.hrl").
-include_lib("lib/include/pdp10_elf36.hrl").
files(Files) ->
NewFiles =
case Files of
[] -> ["--"];
_ -> Files
end,
files(NewFiles, tunit_init()).
files([], Tunit) -> {ok, Tunit};
files([File | Files], Tunit) ->
case file(File, Tunit) of
{ok, NewTunit} -> files(Files, NewTunit);
{error, _Reason} = Error -> Error
end.
file(File, Tunit) ->
case scan_state_open(File) of
{ok, ScanState} ->
try process(ScanState, Tunit)
after scan_state:fclose(ScanState)
end;
{error, _Reason} = Error -> Error
end.
%% Open next input file, support "--" and "-" as aliases for stdin.
scan_state_open(File) ->
case File of
"--" -> scan_state:stdin();
"-" -> scan_state:stdin();
_ -> scan_state:fopen(File)
end.
process(ScanState, Tunit) ->
case parse:stmt(ScanState) of
eof -> {ok, Tunit};
{ok, Stmt} ->
case interpret(ScanState, Tunit, Stmt) of
{ok, NewTunit} -> process(ScanState, NewTunit);
{error, _Reason} = Error -> Error
end;
{error, _Reason} = Error -> Error
end.
interpret(ScanState, Tunit, Stmt) ->
case Stmt of
#s_dot_file{} -> dot_file(ScanState, Tunit, Stmt);
#s_dot_globl{} -> dot_globl(ScanState, Tunit, Stmt);
#s_dot_ident{} -> dot_ident(ScanState, Tunit, Stmt);
#s_dot_size{} -> dot_size(ScanState, Tunit, Stmt);
#s_dot_text{} -> dot_text(ScanState, Tunit, Stmt);
#s_dot_type{} -> dot_type(ScanState, Tunit, Stmt);
#s_label{} -> label(ScanState, Tunit, Stmt);
#s_insn{} -> insn(ScanState, Tunit, Stmt)
end.
dot_file(_ScanState, Tunit, #s_dot_file{string = String}) ->
Symbol = #symbol{ name = String
, section = false
, st_value = 0
, st_size = 0
, st_info = ?ELF_ST_INFO(?STB_LOCAL, ?STT_FILE)
, st_name = 0
, st_shndx = 0
},
{ok, tunit:put_symbol(Tunit, Symbol)}.
dot_globl(ScanState, Tunit, #s_dot_globl{name = Name}) ->
case tunit:get_symbol(Tunit, Name) of
false ->
Symbol =
#symbol{ name = Name
, section = false
, st_value = false
, st_size = false
, st_info = ?ELF_ST_INFO(?STB_GLOBAL, ?STT_NOTYPE)
, st_name = 0
, st_shndx = 0
},
{ok, tunit:put_symbol(Tunit, Symbol)};
#symbol{st_info = StInfo} = OldSymbol ->
case ?ELF_ST_BIND(StInfo) of
?STB_GLOBAL -> {ok, Tunit};
?STB_LOCAL -> % FIXME: assumed local-by-default, are there hard-local symbols?
Symbol = OldSymbol#symbol{st_info = ?ELF_ST_INFO(?STB_GLOBAL, ?ELF_ST_TYPE(StInfo))},
{ok, tunit:put_symbol(Tunit, Symbol)};
Bind ->
fmterr(ScanState, "symbol ~s has previous incompatible binding type ~p", [Name, Bind])
end
end.
dot_ident(_ScanState, Tunit, #s_dot_ident{} = Stmt) ->
#section{data = {stmts, Stmts}} = OldSection =
case tunit:get_section(Tunit, ".comment") of
false -> section_dot_comment();
Section -> Section
end,
NewSection = OldSection#section{data = {stmts, [Stmt | Stmts]}},
{ok, tunit:put_section(Tunit, NewSection)}.
dot_size(ScanState, Tunit, #s_dot_size{name = Name}) ->
#tunit{cursect = Cursect} = Tunit,
#section{dot = Dot} = tunit:get_section(Tunit, Cursect),
case tunit:get_symbol(Tunit, Name) of
#symbol{st_size = StSize} when StSize =/= false ->
fmterr(ScanState, "size of symbol ~s already defined", [Name]);
#symbol{section = Section} when Section =/= Cursect ->
fmterr(ScanState, "symbol ~s not defined in same section as dot", [Name]);
#symbol{st_value = StValue} = OldSymbol when StValue =< Dot -> % note: false > integer()
Symbol = OldSymbol#symbol{st_size = Dot - StValue},
{ok, tunit:put_symbol(Tunit, Symbol)};
#symbol{st_value = StValue} when StValue =/= false, StValue > Dot ->
fmterr(ScanState, "cannot make symbol ~s negative size", [Name]);
_ ->
fmterr(ScanState, "symbol ~s not defined", [Name])
end.
dot_text(_ScanState, Tunit, #s_dot_text{}) ->
%% just check that .text has been pre-created
#section{} = tunit:get_section(Tunit, ".text"),
{ok, Tunit#tunit{cursect = ".text"}}.
dot_type(ScanState, Tunit, #s_dot_type{name = Name}) ->
case tunit:get_symbol(Tunit, Name) of
false ->
Symbol =
#symbol{ name = Name
, section = false
, st_value = false
, st_size = false
, st_info = ?ELF_ST_INFO(?STB_LOCAL, ?STT_FUNC)
, st_name = 0
, st_shndx = 0
},
{ok, tunit:put_symbol(Tunit, Symbol)};
#symbol{st_info = StInfo} = OldSymbol ->
case ?ELF_ST_TYPE(StInfo) of
?STT_FUNC -> {ok, Tunit};
?STT_NOTYPE ->
Symbol = OldSymbol#symbol{st_info = ?ELF_ST_INFO(?ELF_ST_BIND(StInfo), ?STT_FUNC)},
{ok, tunit:put_symbol(Tunit, Symbol)};
Type ->
fmterr(ScanState, "symbol ~s has previous incompatible type ~p", [Name, Type])
end
end.
label(ScanState, Tunit, #s_label{name = Name}) ->
case tunit:get_symbol(Tunit, Name) of
#symbol{section = false, st_value = false} = Symbol -> label2(Tunit, Symbol);
#symbol{} -> fmterr(ScanState, "label ~s already defined", [Name]);
false -> label2(Tunit, #symbol{name = Name, st_size = false, st_info = 0})
end.
label2(Tunit, Symbol) ->
#tunit{cursect = Cursect} = Tunit,
#section{dot = Dot} = tunit:get_section(Tunit, Cursect),
{ok, tunit:put_symbol(Tunit, Symbol#symbol{section = Cursect, st_value = Dot})}.
insn(ScanState, Tunit, #s_insn{} = Stmt) ->
#tunit{cursect = Cursect} = Tunit,
#section{data = {stmts, Stmts}, dot = Dot} = Section = tunit:get_section(Tunit, Cursect),
case Dot rem 4 of % FIXME: target-specific
0 ->
NewSection =
Section#section{ data = {stmts, [Stmt | Stmts]}
, dot = Dot + 4 % FIXME: target-specific
},
{ok, tunit:put_section(Tunit, NewSection)};
_ -> fmterr(ScanState, "misaligned address for instruction", [])
end.
%% Initialization --------------------------------------------------------------
tunit_init() ->
SectionText = section_dot_text(),
Tunit = tunit:put_section(tunit:new(), SectionText),
Tunit#tunit{cursect = SectionText#section.name}.
%% Predefined Sections ---------------------------------------------------------
section_dot_comment() -> % ".comment"
#section{ name = ".comment"
, data = {stmts, []}
, dot = false % do not allow dot or labels here
, shndx = 0
, sh_name = 0
, sh_type = ?SHT_PROGBITS
, sh_offset = 0
, sh_flags = ?SHF_MERGE bor ?SHF_STRINGS
, sh_link = 0
, sh_addralign = 1
, sh_entsize = 1
}.
section_dot_text() -> % ".text"
#section{ name = ".text"
, data = {stmts, []}
, dot = 0
, shndx = 0
, sh_name = 0
, sh_type = ?SHT_PROGBITS
, sh_offset = 0
, sh_flags = ?SHF_ALLOC bor ?SHF_EXECINSTR
, sh_link = 0
, sh_addralign = 4 % FIXME: target-specific
, sh_entsize = 0
}.
%% Error reporting -------------------------------------------------------------
%% FIXME: this is duplicated a few times, move it to scan_state.erl
fmterr(ScanState, Fmt, Args) ->
{ok, FileName} = scan_state:filename(ScanState),
{ok, LineNr} = scan_state:linenr(ScanState),
{error, lists:flatten(io_lib:format("file ~s line ~p: " ++ Fmt,
[FileName, LineNr | Args]))}.

View File

@ -0,0 +1,560 @@
%%% -*- erlang-indent-level: 2 -*-
%%%
%%% ELF output for pdp10-elf as
%%% Copyright (C) 2013-2019 Mikael Pettersson
%%%
%%% This file is part of pdp10-tools.
%%%
%%% pdp10-tools is free software: you can redistribute it and/or modify
%%% it under the terms of the GNU General Public License as published by
%%% the Free Software Foundation, either version 3 of the License, or
%%% (at your option) any later version.
%%%
%%% pdp10-tools is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%%% GNU General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
%%%
%%%-----------------------------------------------------------------------------
%%%
%%% Output file layout:
%%%
%%% ELF header
%%% <sections directly defined by the input>
%%% <.strtab, if any symbols>
%%% <.symtab, if any symbols>
%%% <.shstrtab, if any sections>
%%% <section header table, if any sections>
%%%
%%% Processing steps:
%%%
%%% initialize context
%%% for each section:
%%% - add name to .shstrtab, assign sh_name
%%% - assign sh_offset and st_shndx
%%% - update context
%%% for each symbol:
%%% - add name to .strtab, assign st_name
%%% - assign st_shndx
%%% append .strtab to list of sections
%%% append .symtab to list of sections
%%% append .shstrtab to list of sections
-module(output).
-export([ tunit/2
]).
-include("tunit.hrl").
-include_lib("lib/include/pdp10_elf36.hrl").
-record(strtab,
{ map :: #{string() => non_neg_integer()}
, dot :: pos_integer()
}).
-record(context,
{ tunit :: #tunit{}
, shnum :: pos_integer()
, offset :: pos_integer()
, shstrtab :: #strtab{}
, strtab :: #strtab{}
}).
tunit(Tunit, File) ->
emit(layout(Tunit), File).
%% LAYOUT ======================================================================
layout(Tunit) ->
lists:foldl(
fun(Fun, Context) -> Fun(Context) end,
context_new(Tunit),
[ fun process_sections/1
, fun process_symbols/1
, fun create_strtab/1
, fun create_symtab/1
, fun create_shstrtab/1
, fun align_shtab/1
]).
context_new(Tunit) ->
#context{ tunit = Tunit
, shnum = 1
, offset = ?ELF36_EHDR_SIZEOF
, shstrtab = strtab_new()
, strtab = strtab_new()
}.
%% Sections --------------------------------------------------------------------
process_sections(Context) ->
#context{tunit = #tunit{sections = Sections}} = Context,
lists:foldl(fun process_section/2, Context, maps:values(Sections)).
process_section(Section, Context) ->
append_section(Context, Section).
append_section(Context, Section) ->
#section{ dot = Dot
, name = Name
, sh_addralign = ShAddrAlign
} = Section,
case Dot of
0 -> Context;
_ ->
#context{ tunit = Tunit
, shnum = ShNum
, offset = Offset
, shstrtab = ShStrTab
} = Context,
{ShName, NewShStrTab} = strtab_enter(ShStrTab, Name),
ShOffset = (Offset + ShAddrAlign - 1) band bnot (ShAddrAlign - 1),
NewSection =
Section#section{sh_name = ShName, sh_offset = ShOffset, shndx = ShNum},
NewTunit = tunit:put_section(Tunit, NewSection),
Context#context{ tunit = NewTunit
, shnum = ShNum + 1
, offset = ShOffset + Dot
, shstrtab = NewShStrTab
}
end.
%% Symbols ---------------------------------------------------------------------
process_symbols(Context) ->
#context{tunit = #tunit{symbols = Symbols}} = Context,
lists:foldl(fun process_symbol/2, Context, maps:values(Symbols)).
process_symbol(Symbol, Context) ->
#symbol{name = Name, section = Section} = Symbol,
#context{tunit = Tunit, strtab = StrTab} = Context,
{StName, NewStrTab} = strtab_enter(StrTab, Name),
StShndx =
case tunit:get_section(Tunit, Section) of
false -> ?SHN_ABS;
#section{shndx = Shndx} -> Shndx % assigned in append_section/2 above
end,
NewSymbol = Symbol#symbol{st_name = StName, st_shndx = StShndx},
NewTunit = tunit:put_symbol(Tunit, NewSymbol),
Context#context{tunit = NewTunit, strtab = NewStrTab}.
%% Symbol string table (.strtab) -----------------------------------------------
create_strtab(Context) ->
case maps:size(Context#context.tunit#tunit.symbols) of
0 -> Context;
_ ->
StrTab = Context#context.strtab,
Image = strtab_image(StrTab),
Section =
#section{ name = ".strtab"
, data = {image, Image}
, dot = image_size(Image)
, sh_type = ?SHT_STRTAB
, sh_flags = ?SHF_MERGE bor ?SHF_STRINGS % FIXME: check
, sh_link = 0
, sh_addralign = 1 % FIXME: check
, sh_entsize = 1 % FIXME: check
},
append_section(Context, Section)
end.
%% Symbol table (.symtab) ------------------------------------------------------
create_symtab(Context) ->
#context{tunit = Tunit} = Context,
#tunit{symbols = Symbols} = Tunit,
case maps:size(Symbols) of
0 -> Context;
NrSyms ->
#section{shndx = StrTabShndx} = tunit:get_section(Tunit, ".strtab"),
Image = symbols_image(Symbols),
Size = (NrSyms + 1) * ?ELF36_SYM_SIZEOF,
Size = image_size(Image), % consistency check
Section =
#section{ name = ".symtab"
, data = {image, Image}
, dot = Size
, sh_type = ?SHT_SYMTAB
, sh_flags = 0
, sh_link = StrTabShndx
, sh_addralign = 4 % FIXME: check
, sh_entsize = ?ELF36_SYM_SIZEOF
},
append_section(Context, Section)
end.
symbols_image(Symbols) ->
ElfSym0 =
#elf36_Sym{ st_name = 0
, st_value = 0
, st_size = 0
, st_info = ?ELF36_ST_INFO(?STB_LOCAL, ?STT_NOTYPE)
, st_other = 0
, st_shndx = ?SHN_UNDEF
},
[elf36_Sym_image(ElfSym0) |
lists:map(fun symbol_image/1, maps:values(Symbols))].
symbol_image(Symbol) ->
#symbol{ st_value = StValue
, st_size = StSize
, st_info = StInfo
, st_name = StName
, st_shndx = StShndx
} = Symbol,
ElfSym =
#elf36_Sym{ st_name = StName
, st_value = StValue
, st_size = StSize
, st_info = StInfo
, st_other = ?STV_DEFAULT % FIXME: should be set earlier
, st_shndx = StShndx
},
elf36_Sym_image(ElfSym).
%% FIXME: the code below belongs in a library
elf36_Sym_image(ElfSym) ->
#elf36_Sym{ st_name = StName
, st_value = StValue
, st_size = StSize
, st_info = StInfo
, st_other = StOther
, st_shndx = StShndx
} = ElfSym,
[ elf36_Word_image(StName)
, elf36_Addr_image(StValue)
, elf36_Word_image(if StSize =:= false -> 0; true -> StSize end)
, elf36_Uchar_image(StInfo)
, elf36_Uchar_image(StOther)
, elf36_Half_image(StShndx)
].
elf36_Addr_image(Addr) -> uint36_image(Addr).
elf36_Half_image(Half) -> uint18_image(Half).
elf36_Off_image(Off) -> uint36_image(Off).
elf36_Word_image(Word) -> uint36_image(Word).
elf36_Uchar_image(Uchar) -> uint9_image(Uchar).
uint9_image(Uint9) ->
Uint9 band 511.
uint18_image(Uint18) ->
[(Uint18 bsr 9) band 511, Uint18 band 511].
uint36_image(Uint36) ->
[(Uint36 bsr 27) band 511,
(Uint36 bsr 18) band 511,
(Uint36 bsr 9) band 511,
Uint36 band 511].
image_size(Image) -> image_size(Image, 0).
image_size([H | T], Acc) -> image_size(T, image_size(H, Acc));
image_size([], Acc) -> Acc;
image_size(TByte, Acc) when is_integer(TByte), 0 =< TByte, TByte =< 511 -> Acc + 1.
%% Section Header String Table (.shstrtab) -------------------------------------
create_shstrtab(Context) ->
case Context#context.shnum of
1 -> Context;
_ ->
%% Note that append_section/1 enters the section's name to shstrtab,
%% updating its contents if the name wasn't already there, which would
%% invalidate the image recorded in the section. To avoid that, enter
%% the name first.
OldShStrTab = Context#context.shstrtab,
{_ShName, NewShStrTab} = strtab_enter(OldShStrTab, ".shstrtab"),
Image = strtab_image(NewShStrTab),
Section =
#section{ name = ".shstrtab"
, data = {image, Image}
, dot = image_size(Image)
, sh_type = ?SHT_STRTAB
, sh_flags = ?SHF_MERGE bor ?SHF_STRINGS % FIXME: check
, sh_link = 0
, sh_addralign = 1 % FIXME: check
, sh_entsize = 1 % FIXME: check
},
append_section(Context#context{shstrtab = NewShStrTab}, Section)
end.
%% Align Section Header Table --------------------------------------------------
align_shtab(Context) ->
case Context#context.shnum of
1 ->
Context#context{shnum = 0, offset = 0};
_ ->
Offset = Context#context.offset,
ShTabOffset = (Offset + (4 - 1)) band bnot (4 - 1),
Context#context{offset = ShTabOffset}
end.
%% String Tables ---------------------------------------------------------------
%% FIXME: duplicates code for .ident directive / .comment section
strtab_new() ->
#strtab{map = maps:new(), dot = 1}. % 1 due to NUL before 1st string
strtab_enter(StrTab = #strtab{map = Map, dot = Dot}, String) ->
case maps:get(String, Map, false) of
false -> {Dot, StrTab#strtab{map = maps:put(String, Dot, Map),
dot = Dot + length(String) + 1}}; % +1 for terminating NUL
Offset -> {Offset, StrTab}
end.
strtab_image(#strtab{map = Map}) ->
KVs = maps:to_list(Map),
VKs = lists:map(fun({K, V}) -> {V, K} end, KVs),
SortedVKs = lists:sort(VKs),
[0 | lists:map(fun({_V, K}) -> K ++ [0] end, SortedVKs)].
%% EMIT ========================================================================
emit(Context, File) ->
case pdp10_stdio:fopen(File, [raw, write, delayed_write]) of
{ok, FP} ->
try
Funs =
[ fun emit_elf_header/3
, fun emit_sections/3
, fun emit_shtab/3
],
emit(Funs, Context, FP, 0)
after pdp10_stdio:fclose(FP)
end;
{error, Reason} -> {error, io_lib:format("opening ~s: ~p", [File, Reason])}
end.
emit([], _Context, _FP, _Offset) -> ok;
emit([Fun | Funs], Context, FP, Offset) ->
case Fun(Context, FP, Offset) of
{ok, NewOffset} -> emit(Funs, Context, FP, NewOffset);
{error, _Reason} = Error -> Error
end.
emit_elf_header(Context, FP, Offset = 0) ->
ShStrTabShndx =
case tunit:get_section(Context#context.tunit, ".shstrtab") of
#section{shndx = Shndx} -> Shndx;
false -> 0
end,
ElfHdr =
#elf36_Ehdr{ e_ident = e_ident()
, e_type = ?ET_REL
, e_machine = ?EM_PDP10 % FIXME: target-specific
, e_version = ?EV_CURRENT
, e_entry = 0
, e_phoff = 0
, e_shoff = Context#context.offset
, e_flags = 0
, e_ehsize = ?ELF36_EHDR_SIZEOF
, e_phentsize = 0
, e_phnum = 0
, e_shentsize = ?ELF36_SHDR_SIZEOF
, e_shnum = Context#context.shnum
, e_shstrndx = ShStrTabShndx
},
emit_image(elf36_Ehdr_image(ElfHdr), ?ELF36_EHDR_SIZEOF, FP, Offset).
emit_sections(Context, FP, Offset = ?ELF36_EHDR_SIZEOF) ->
#context{tunit = #tunit{sections = SectionsMap}} = Context,
Sections = lists:sort(fun order_by_sh_offset/2, maps:values(SectionsMap)),
emit_sections2(Sections, FP, Offset).
emit_sections2([], _FP, Offset) -> {ok, Offset};
emit_sections2([Section | Sections], FP, Offset) ->
case emit_section(Section, FP, Offset) of
{ok, NewOffset} -> emit_sections2(Sections, FP, NewOffset);
{error, _Reason} = Error -> Error
end.
emit_section(Section, FP, Offset) ->
case Section#section.dot of
0 -> {ok, Offset};
Dot ->
ShOffset = Section#section.sh_offset,
NrPadBytes = ShOffset - Offset,
case emit_padding(NrPadBytes, FP) of
ok ->
{image, Image} = Section#section.data,
emit_image(Image, Dot, FP, ShOffset);
{error, _Reason} = Error -> Error
end
end.
emit_shtab(Context, FP, Offset) ->
case Context#context.offset of
0 -> {ok, Offset};
ShTabOffset ->
case emit_padding(ShTabOffset - Offset, FP) of
ok ->
case emit_shdr0(FP, ShTabOffset) of
{ok, NewOffset} ->
#context{tunit = #tunit{sections = SectionsMap}} = Context,
Sections = lists:sort(fun order_by_shndx/2, maps:values(SectionsMap)),
emit_shdrs(Sections, FP, NewOffset);
{error, _Reason} = Error -> Error
end;
{error, _Reason} = Error -> Error
end
end.
emit_shdrs([], _FP, Offset) -> {ok, Offset};
emit_shdrs([Section | Sections], FP, Offset) ->
case emit_shdr(Section, FP, Offset) of
{ok, NewOffset} -> emit_shdrs(Sections, FP, NewOffset);
{error, _Reason} = Error -> Error
end.
emit_shdr(Section, FP, Offset) ->
case Section#section.dot of
0 -> {ok, Offset};
Dot ->
#section{ sh_name = ShName
, sh_type = ShType
, sh_offset = ShOffset
, sh_flags = ShFlags
, sh_link = ShLink
, sh_addralign = ShAddrAlign
, sh_entsize = ShEntSize
} = Section,
ElfShdr =
#elf36_Shdr{ sh_name = ShName
, sh_type = ShType
, sh_flags = ShFlags
, sh_addr = 0
, sh_offset = ShOffset
, sh_size = Dot
, sh_link = ShLink
, sh_info = 0 % FIXME: for symtab, LAST_LOCAL + 1
, sh_addralign = ShAddrAlign
, sh_entsize = ShEntSize
},
emit_elf36_Shdr(ElfShdr, FP, Offset)
end.
emit_shdr0(FP, Offset) ->
ElfShdr0 =
#elf36_Shdr{ sh_name = 0
, sh_type = ?SHT_NULL
, sh_flags = 0
, sh_addr = 0
, sh_offset = 0
, sh_size = 0
, sh_link = 0
, sh_info = 0
, sh_addralign = 0
, sh_entsize = 0
},
emit_elf36_Shdr(ElfShdr0, FP, Offset).
emit_elf36_Shdr(Shdr, FP, Offset) ->
emit_image(elf36_Shdr_image(Shdr), ?ELF36_SHDR_SIZEOF, FP, Offset).
elf36_Shdr_image(ElfShdr) ->
#elf36_Shdr{ sh_name = ShName
, sh_type = ShType
, sh_flags = ShFlags
, sh_addr = ShAddr
, sh_offset = ShOffset
, sh_size = ShSize
, sh_link = ShLink
, sh_info = ShInfo
, sh_addralign = ShAddrAlign
, sh_entsize = ShEntSize
} = ElfShdr,
[ elf36_Word_image(ShName)
, elf36_Word_image(ShType)
, elf36_Word_image(ShFlags)
, elf36_Addr_image(ShAddr)
, elf36_Off_image(ShOffset)
, elf36_Word_image(ShSize)
, elf36_Word_image(ShLink)
, elf36_Word_image(ShInfo)
, elf36_Word_image(ShAddrAlign)
, elf36_Word_image(ShEntSize)
].
emit_padding(0, _FP) -> ok;
emit_padding(N, FP) when N > 0 ->
case pdp10_stdio:fputc(0, FP) of
ok -> emit_padding(N - 1, FP);
{error, _Reason} = Error -> Error
end.
order_by_sh_offset(Section1, Section2) ->
Section1#section.sh_offset =< Section2#section.sh_offset.
order_by_shndx(Section1, Section2) ->
Section1#section.shndx =< Section2#section.shndx.
elf36_Ehdr_image(ElfHdr) ->
#elf36_Ehdr{ e_ident = EIdent
, e_type = EType
, e_machine = EMachine
, e_version = EVersion
, e_entry = EEntry
, e_phoff = EPhOff
, e_shoff = EShOff
, e_flags = EFlags
, e_ehsize = EEhSize
, e_phentsize = EPhEntSize
, e_phnum = EPhNum
, e_shentsize = EShEntSize
, e_shnum = EShNum
, e_shstrndx = EShStrNdx
} = ElfHdr,
[ EIdent % already a list of bytes
, elf36_Half_image(EType)
, elf36_Half_image(EMachine)
, elf36_Word_image(EVersion)
, elf36_Addr_image(EEntry)
, elf36_Off_image(EPhOff)
, elf36_Off_image(EShOff)
, elf36_Word_image(EFlags)
, elf36_Half_image(EEhSize)
, elf36_Half_image(EPhEntSize)
, elf36_Half_image(EPhNum)
, elf36_Half_image(EShEntSize)
, elf36_Half_image(EShNum)
, elf36_Half_image(EShStrNdx)
].
e_ident() ->
tuple_to_list(
erlang:make_tuple(
?EI_NIDENT, 0,
[ {1 + ?EI_MAG0, ?ELFMAG0}
, {1 + ?EI_MAG1, ?ELFMAG1}
, {1 + ?EI_MAG2, ?ELFMAG2}
, {1 + ?EI_MAG3, ?ELFMAG3}
, {1 + ?EI_CLASS, ?ELFCLASS36}
, {1 + ?EI_DATA, ?ELFDATA2MSB}
, {1 + ?EI_VERSION, ?EV_CURRENT}
, {1 + ?EI_OSABI, ?ELFOSABI_NONE} % TODO: ELFOSABI_LINUX instead?
, {1 + ?EI_ABIVERSION, 0}
])).
emit_image(Image, NrBytes, FP, Offset) ->
NrBytes = image_size(Image), % assert
case image_write(Image, FP) of
ok -> {ok, Offset + NrBytes};
{error, _Reason} = Error -> Error
end.
image_write([H | T], FP) ->
case image_write(H, FP) of
ok -> image_write(T, FP);
{error, _Reason} = Error -> Error
end;
image_write([], _FP) -> ok;
image_write(TByte, FP) when is_integer(TByte), 0 =< TByte, TByte =< 511 ->
pdp10_stdio:fputc(TByte, FP).

View File

@ -0,0 +1,312 @@
%%% -*- erlang-indent-level: 2 -*-
%%%
%%% parser for pdp10-elf as
%%% Copyright (C) 2013-2019 Mikael Pettersson
%%%
%%% This file is part of pdp10-tools.
%%%
%%% pdp10-tools is free software: you can redistribute it and/or modify
%%% it under the terms of the GNU General Public License as published by
%%% the Free Software Foundation, either version 3 of the License, or
%%% (at your option) any later version.
%%%
%%% pdp10-tools is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%%% GNU General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
-module(parse).
-export([ stmt/1
]).
-include("token.hrl").
-include("tunit.hrl").
-include_lib("lib/include/pdp10_opcodes.hrl").
stmt(ScanState) ->
case scan:token(ScanState) of
{ok, ?T_DOT_FILE} -> dot_file(ScanState);
{ok, ?T_DOT_GLOBL} -> dot_globl(ScanState);
{ok, ?T_DOT_IDENT} -> dot_ident(ScanState);
{ok, ?T_DOT_SIZE} -> dot_size(ScanState);
{ok, ?T_DOT_TEXT} -> dot_text(ScanState);
{ok, ?T_DOT_TYPE} -> dot_type(ScanState);
{ok, {?T_SYMBOL, Name}} -> stmt_after_symbol(ScanState, Name);
{ok, ?T_NEWLINE} -> stmt(ScanState);
{ok, ?T_EOF} -> eof;
ScanRes -> badtok(ScanState, "expected directive, label, or instruction", ScanRes)
end.
%% Instructions and labels -----------------------------------------------------
%%
%% Recognize:
%%
%% <label> ::= <symbol> ":"
%%
%% <insn> ::= <symbol> (<accumulator> ",")? <address> <newline>
%%
%% <accumulator> ::= <uinteger> [uint <= 0xF]
%%
%% <address> ::= "@"? <displacement>? <index>?
%%
%% <displacement> ::= <uinteger>
%%
%% <index> ::= "(" <accumulator> ")"
%%
%% Examples:
%%
%% foo:
%% popj 17,
%% pushj 17,bar
%% movei 1,@fum(2)
%%
%% TODO: <displacement> should be <expr> and permit parentheses and
%% various operators.
%%
%% Ambiguous example:
%%
%% <symbol> (<uinteger>) <newline>
%%
%% This is ambiguous since we have no special notation for <register>, and the
%% same kind of parentheses are used for expression grouping in the displacement
%% as for the index register.
%%
%% This might denote an insn with a parenthesized displacement and no index,
%% or it might denote an insn with an index but no displacement.
%%
%% However, an index always uses the displacement, even if zero, so we require
%% an explicit displacement if an index is used.
%%
%% This means that "opcode (...)" is interpreted as having a displacement but no
%% index. Use "opcode 0(index)" if an index with zero displacement is needed.
stmt_after_symbol(ScanState, Name) ->
case scan:token(ScanState) of
{ok, ?T_COLON} -> {ok, #s_label{name = Name}};
{ok, ?T_NEWLINE} -> make_insn(ScanState, Name, false, false, false, false);
{ok, {?T_UINTEGER, UInt}} -> insn_uint(ScanState, Name, UInt);
ScanRes -> badtok(ScanState, "junk after symbol", ScanRes)
end.
%% Seen "<symbol> <uinteger>". The <uinteger> is the <accumulator> if followed
%% by ",", otherwise (the start of) the <displacement>.
insn_uint(ScanState, Name, UInt) ->
case scan:token(ScanState) of
{ok, ?T_COMMA} -> % the Uint is the Accumulator, parse EA next
insn_ea(ScanState, Name, _AccOrDev = UInt);
{ok, ?T_LPAREN} -> % the Uint is the Displacement, parse Index next
insn_ea_index(ScanState, Name, _AccOrDev = false, _At = false, _Displacement = UInt);
{ok, ?T_NEWLINE} -> % the Uint is the Displacement
make_insn(ScanState, Name, _AccOrDev = false, _At = false, _Displacement = UInt, _Index = false);
ScanRes -> badtok(ScanState, "junk after <symbol> <uinteger>", ScanRes)
end.
%% <symbol> <accordev> "," . [ ["@"] <displacement> ["(" <index> ")"] ] <newline>
insn_ea(ScanState, Name, AccOrDev) ->
case scan:token(ScanState) of
{ok, ?T_NEWLINE} ->
make_insn(ScanState, Name, AccOrDev, _At = false, _Displacement = false, _Index = false);
{ok, ?T_AT} -> insn_ea_at(ScanState, Name, AccOrDev);
{ok, {?T_UINTEGER, Displacement}} ->
insn_ea_disp(ScanState, Name, AccOrDev, _At = false, Displacement);
ScanRes -> badtok(ScanState, "junk after comma", ScanRes)
end.
%% <symbol> [<accordev> ","] "@" . <displacement> . ["(" <index> ")"] <newline>
insn_ea_at(ScanState, Name, AccOrDev) ->
case scan:token(ScanState) of
{ok, {?T_UINTEGER, Displacement}} ->
insn_ea_disp(ScanState, Name, AccOrDev, _At = true, Displacement);
ScanRes -> badtok(ScanState, "junk after @", ScanRes)
end.
%% <symbol> [<accordev> ","] ["@"] <displacement> . ["(" <index> ")"] <newline>
insn_ea_disp(ScanState, Name, AccOrDev, At, Displacement) ->
case scan:token(ScanState) of
{ok, ?T_LPAREN} -> insn_ea_index(ScanState, Name, AccOrDev, At, Displacement);
{ok, ?T_NEWLINE} -> make_insn(ScanState, Name, AccOrDev, At, Displacement, _Index = false);
ScanRes -> badtok(ScanState, "junk after <displacement>", ScanRes)
end.
%% <symbol> [<accordev> ","] ["@"] <displacement> "(" . <index> ")" <newline>
insn_ea_index(ScanState, Name, AccOrDev, At, Displacement) ->
case scan:token(ScanState) of
{ok, {?T_UINTEGER, Index}} when Index =< 8#17 ->
case scan:token(ScanState) of
{ok, ?T_RPAREN} ->
case scan:token(ScanState) of
{ok, ?T_NEWLINE} ->
make_insn(ScanState, Name, AccOrDev, At, Displacement, Index);
ScanRes -> badtok(ScanState, "junk after <index>", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk in <index>", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk in <index>", ScanRes)
end.
make_insn(ScanState, Name, AccOrDev, At, Displacement, Index) ->
Models = ?PDP10_KL10_271, % FIXME: make dynamic
case pdp10_opcodes:insn_from_name(Models, Name, AccOrDev =/= false) of
false -> badinsn(ScanState, "invalid mnemonic ~s", Name);
#pdp10_insn_desc{ high13 = High13
, format = Format
, e_unused = EUnused
, extended = false % TODO: handle extended opcodes
} ->
case make_high13(ScanState, Name, AccOrDev, High13, Format) of
{error, _Reason} = Error -> Error;
{ok, FinalHigh13} ->
case check_e(ScanState, Name, At, Displacement, Index, EUnused) of
{error, _Reason} = Error -> Error;
ok -> {ok, #s_insn{ high13 = FinalHigh13
, at = At
, address = if Displacement =:= false -> 0; true -> Displacement end
, index = if Index =:= false -> 0; true -> Index end
}}
end
end
end.
make_high13(ScanState, Name, AccOrDev, High13, Format) ->
case {Format, AccOrDev} of
{?PDP10_INSN_A_OPCODE, false} ->
{ok, High13};
{?PDP10_INSN_A_OPCODE, _} ->
badinsn(ScanState, "~s: extraneous accumulator operand", Name);
{?PDP10_INSN_IO, false} ->
badinsn(ScanState, "~s: missing device operand", Name);
{?PDP10_INSN_IO, _} ->
make_high13_io(High13, AccOrDev);
{?PDP10_INSN_BASIC, false} ->
badinsn(ScanState, "~s: missing accumulator operand", Name);
{?PDP10_INSN_BASIC, _} ->
make_high13_basic(High13, AccOrDev);
{?PDP10_INSN_A_NONZERO, false} ->
badinsn(ScanState, "~s: missing accumulator operand", Name);
{?PDP10_INSN_A_NONZERO, 0} ->
badinsn(ScanState, "~s: accumulator must not be zero", Name);
{?PDP10_INSN_A_NONZERO, _} ->
make_high13_basic(High13, AccOrDev)
end.
make_high13_basic(High13, Accumulator) ->
Mask = ((1 bsl 4) - 1),
{ok, (High13 band bnot Mask) bor (Accumulator band Mask)}.
make_high13_io(High13, Device) ->
Mask = ((1 bsl 7) - 1),
{ok, (High13 band bnot (Mask bsl 3)) bor ((Device band Mask) bsl 3)}.
check_e(ScanState, Name, At, Displacement, Index, EUnused) ->
HaveE = At orelse Displacement =/= false orelse Index =/= false,
case {EUnused, HaveE} of
{true, false} -> ok;
{true, true} -> badinsn(ScanState, "~s: extraneous address operand", Name);
{false, false} -> badinsn(ScanState, "~s: missing address operand", Name);
{false, true} -> ok
end.
badinsn(ScanState, Fmt, Mnemonic) ->
fmterr(ScanState, Fmt, [Mnemonic]).
%% Directives ------------------------------------------------------------------
dot_file(ScanState) ->
dot_file_or_ident(ScanState, fun(String) -> #s_dot_file{string = String} end,
"junk after .file").
dot_ident(ScanState) ->
dot_file_or_ident(ScanState, fun(String) -> #s_dot_ident{string = String} end,
"junk after .ident").
dot_file_or_ident(ScanState, MkStmt, ErrMsg) ->
case scan:token(ScanState) of
{ok, {?T_STRING, String}} ->
case scan:token(ScanState) of
{ok, ?T_NEWLINE} -> {ok, MkStmt(String)};
ScanRes -> badtok(ScanState, ErrMsg, ScanRes)
end;
ScanRes -> badtok(ScanState, ErrMsg, ScanRes)
end.
dot_globl(ScanState) ->
case scan:token(ScanState) of
{ok, {?T_SYMBOL, Name}} ->
case scan:token(ScanState) of
{ok, ?T_NEWLINE} -> {ok, #s_dot_globl{name = Name}};
ScanRes -> badtok(ScanState, "junk after .globl", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .globl", ScanRes)
end.
%% For now only accepts ".size <sym>,.-<sym>". TODO: extend
dot_size(ScanState) ->
case scan:token(ScanState) of
{ok, {?T_SYMBOL, Name}} ->
case scan:token(ScanState) of
{ok, ?T_COMMA} ->
case scan:token(ScanState) of
{ok, ?T_DOT} ->
case scan:token(ScanState) of
{ok, ?T_MINUS} ->
case scan:token(ScanState) of
{ok, {?T_SYMBOL, Name}} -> % same Name as above
case scan:token(ScanState) of
{ok, ?T_NEWLINE} -> {ok, #s_dot_size{name = Name}};
ScanRes -> badtok(ScanState, "junk after .size", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .size", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .size", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .size", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .size", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .size", ScanRes)
end.
dot_text(ScanState) ->
case scan:token(ScanState) of
{ok, ?T_NEWLINE} -> {ok, #s_dot_text{}};
ScanRes -> badtok(ScanState, "junk after .text", ScanRes)
end.
%% For now only accepts ".type <sym>,@function". TODO: extend
dot_type(ScanState) ->
case scan:token(ScanState) of
{ok, {?T_SYMBOL, Name}} ->
case scan:token(ScanState) of
{ok, ?T_COMMA} ->
case scan:token(ScanState) of
{ok, ?T_AT} ->
case scan:token(ScanState) of
{ok, {?T_SYMBOL, "function"}} ->
case scan:token(ScanState) of
{ok, ?T_NEWLINE} -> {ok, #s_dot_type{name = Name}};
ScanRes -> badtok(ScanState, "junk after .type", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .type", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .type", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .type", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .type", ScanRes)
end.
%% Error reporting -------------------------------------------------------------
badtok(_ScanState, _ErrMsg, {error, _Reason} = Error) -> Error;
badtok(ScanState, ErrMsg, {ok, Token}) ->
fmterr(ScanState, ErrMsg ++ "; current token is ~s", [token:to_string(Token)]).
fmterr(ScanState, Fmt, Args) ->
{ok, FileName} = scan_state:filename(ScanState),
{ok, LineNr} = scan_state:linenr(ScanState),
{error, lists:flatten(io_lib:format("file ~s line ~p: " ++ Fmt,
[FileName, LineNr | Args]))}.

232
erlang/apps/as/src/scan.erl Normal file
View File

@ -0,0 +1,232 @@
%%% -*- erlang-indent-level: 2 -*-
%%%
%%% scanner for pdp10-elf as
%%% Copyright (C) 2013-2019 Mikael Pettersson
%%%
%%% This file is part of pdp10-tools.
%%%
%%% pdp10-tools is free software: you can redistribute it and/or modify
%%% it under the terms of the GNU General Public License as published by
%%% the Free Software Foundation, either version 3 of the License, or
%%% (at your option) any later version.
%%%
%%% pdp10-tools is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%%% GNU General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
-module(scan).
-export([ token/1
]).
-include("token.hrl").
token(ScanState) ->
case scan_state:fgetc(ScanState) of
{error, _Reason} = Error -> Error;
eof -> {ok, ?T_EOF};
{ok, Ch} ->
case Ch of
$\s -> token(ScanState);
$\t -> token(ScanState);
$\r -> token(ScanState);
$\f -> token(ScanState);
$\n -> {ok, ?T_NEWLINE};
$# -> do_line_comment(ScanState);
$@ -> {ok, ?T_AT};
$: -> {ok, ?T_COLON};
$, -> {ok, ?T_COMMA};
$( -> {ok, ?T_LPAREN};
$) -> {ok, ?T_RPAREN};
$/ -> do_slash(ScanState);
$\" -> do_string(ScanState, []);
$- -> {ok, ?T_MINUS};
_ ->
if $0 =< Ch, Ch =< $9 -> do_number(ScanState, Ch);
($A =< Ch andalso Ch =< $Z) orelse
($a =< Ch andalso Ch =< $z) orelse
Ch =:= $. orelse
Ch =:= $$ orelse
Ch =:= $_ -> do_symbol(ScanState, [Ch]);
true -> badchar(ScanState, Ch, "")
end
end
end.
%% Scan after seeing '#'.
do_line_comment(ScanState) ->
case scan_state:fgetc(ScanState) of
{error, _Reason} = Error -> Error;
eof -> badchar(ScanState, eof, "in line comment");
{ok, $\n} -> {ok, ?T_NEWLINE};
{ok, _Ch} -> do_line_comment(ScanState)
end.
%% Scan after seeing '/'.
do_slash(ScanState) ->
case scan_state:fgetc(ScanState) of
{error, _Reason} = Error -> Error;
{ok, $*} -> do_c_comment(ScanState, false);
{ok, Ch} ->
scan_state:ungetc(Ch, ScanState),
badchar(ScanState, Ch, "after /"); % TODO: NYI: T_DIV
eof ->
badchar(ScanState, eof, "after /")
end.
%% Scan after seeing '/* ...'.
do_c_comment(ScanState, PrevWasStar) ->
case scan_state:fgetc(ScanState) of
{error, _Reason} = Error -> Error;
eof -> badchar(ScanState, eof, "in /*...*/ comment");
{ok, $*} -> do_c_comment(ScanState, true);
{ok, $/} when PrevWasStar -> token(ScanState);
{ok, _Ch} -> do_c_comment(ScanState, false)
end.
%% Scan after seeing '"'.
do_string(ScanState, Chars) ->
case scan_state:fgetc(ScanState) of
{error, _Reason} = Error -> Error;
eof -> badchar(ScanState, eof, "in string literal");
{ok, $\n} -> badchar(ScanState, $\n, "in string literal");
{ok, $\"} -> {ok, {?T_STRING, lists:reverse(Chars)}};
{ok, $\\} ->
case do_escape(ScanState) of
{error, _Reason} = Error -> Error;
{ok, Ch} -> do_string(ScanState, [Ch | Chars])
end;
{ok, Ch} -> do_string(ScanState, [Ch | Chars])
end.
%% Scan after seeing '\' in a string literal.
do_escape(ScanState) ->
case scan_state:fgetc(ScanState) of
{error, _Reason} = Error -> Error;
eof -> badchar(ScanState, eof, "in \\ character escape");
{ok, Ch} ->
case Ch of
$n -> {ok, $\n};
$t -> {ok, $\t};
$f -> {ok, $\f};
$r -> {ok, $\r};
$b -> {ok, $\b};
$\\ -> {ok, $\\};
$\' -> {ok, $\'};
$\" -> {ok, $\"};
_ ->
if $0 =< Ch, Ch =< $7 -> do_octal_escape(ScanState, Ch - $0, 2);
true -> badchar(ScanState, Ch, "in \\ character escape")
end
end
end.
do_octal_escape(_ScanState, Val, 0) -> {ok, Val};
do_octal_escape(ScanState, Val, N) ->
case scan_state:fgetc(ScanState) of
{error, _Reason} = Error -> Error;
eof -> badchar(ScanState, eof, "in \\ character escape");
{ok, Ch} ->
if $0 =< Ch, Ch =< $t -> do_octal_escape(ScanState, Val * 8 + (Ch - $0), N - 1);
true ->
case scan_state:ungetc(Ch, ScanState) of
{error, _Reason} = Error -> Error;
ok -> {ok, Val}
end
end
end.
do_symbol(ScanState, Chars) ->
case scan_state:fgetc(ScanState) of
{error, _Reason} = Error -> Error;
eof -> do_symbol(lists:reverse(Chars));
{ok, Ch} ->
if ($A =< Ch andalso Ch =< $Z) orelse
($a =< Ch andalso Ch =< $z) orelse
($0 =< Ch andalso Ch =< $9) orelse
Ch =:= $. orelse
Ch =:= $$ orelse
Ch =:= $_ -> do_symbol(ScanState, [Ch | Chars]);
true ->
case scan_state:ungetc(Ch, ScanState) of
{error, _Reason} = Error -> Error;
ok -> do_symbol(lists:reverse(Chars))
end
end
end.
do_symbol(Chars) ->
case Chars of
[$.] -> {ok, ?T_DOT};
[$. | _] -> {ok, token:from_symbol(Chars)};
_ -> {ok, {?T_SYMBOL, Chars}}
end.
do_number(ScanState, Dig0) ->
case Dig0 of
$0 ->
case scan_state:fgetc(ScanState) of
{error, _Reason} = Error -> Error;
eof -> {ok, {?T_UINTEGER, Dig0 - $0}};
{ok, Ch} ->
if Ch =:= $x; Ch =:= $X ->
%% must have hex digit after 0x
case scan_state:fgetc(ScanState) of
{error, _Reason} = Error -> Error;
eof -> badchar(ScanState, eof, "after 0x in number");
{ok, Ch} ->
case chval(Ch) of
ChVal when ChVal < 16 ->
do_number(ScanState, _Base = 16, ChVal);
_Val -> badchar(ScanState, Ch, "after 0x in number")
end
end;
true ->
case scan_state:ungetc(Ch, ScanState) of
{error, _Reason} = Error -> Error;
ok -> do_number(ScanState, _Base = 8, _Val = 0)
end
end
end;
_ -> do_number(ScanState, _Base = 10, _Val = Dig0 - $0)
end.
do_number(ScanState, Base, Val) ->
case scan_state:fgetc(ScanState) of
{error, _Reason} = Error -> Error;
eof -> {ok, {?T_UINTEGER, Val}};
{ok, Ch} ->
case chval(Ch) of
ChVal when ChVal < Base ->
do_number(ScanState, Base, Val * Base + ChVal);
_ChVal ->
%% TODO: check for <decimal>[bf] which is a local label reference
case scan_state:ungetc(Ch, ScanState) of
{error, _Reason} = Error -> Error;
ok -> {ok, {?T_UINTEGER, Val}}
end
end
end.
chval(Ch) ->
if $0 =< Ch, Ch =< $9 -> Ch - $0;
$A =< Ch, Ch =< $F -> Ch - ($A - 10);
$a =< Ch, Ch =< $f -> Ch - ($a - 10);
true -> 255 % signals invalid by being >= any valid base
end.
badchar(ScanState, Ch, Context) ->
ChStr = char_to_string(Ch),
{ok, FileName} = scan_state:filename(ScanState),
{ok, LineNr} = scan_state:linenr(ScanState),
{error, lists:flatten(io_lib:format("~s line ~p: invalid character '~s' ~s",
[FileName, LineNr, ChStr, Context]))}.
char_to_string(eof) -> "<EOF>";
char_to_string(Ch) when $\s =< Ch, Ch =< $~ -> [$', Ch, $'];
char_to_string(Ch) ->
[$', $\\, $0 + ((Ch bsr 6) band 3), $0 + ((Ch bsr 3) band 7), $0 + (Ch band 7), $'].

View File

@ -0,0 +1,171 @@
%%% -*- erlang-indent-level: 2 -*-
%%%
%%% scanner state manager for pdp10-elf as
%%% Copyright (C) 2019 Mikael Pettersson
%%%
%%% This file is part of pdp10-tools.
%%%
%%% pdp10-tools is free software: you can redistribute it and/or modify
%%% it under the terms of the GNU General Public License as published by
%%% the Free Software Foundation, either version 3 of the License, or
%%% (at your option) any later version.
%%%
%%% pdp10-tools is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%%% GNU General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
-module(scan_state).
-behaviour(gen_server).
%% API
-export([ % file I/O wrappers
fclose/1
, fgetc/1
, fopen/1
, stdin/0
, ungetc/2
% meta-data accessors
, filename/1
, linenr/1
]).
%% gen_server callbacks
-export([ init/1
, handle_call/3
, handle_cast/2
, handle_info/2
, terminate/2
, code_change/3
]).
%% The scanner state records the I/O handle, implements a one-character
%% pushback buffer, and maintains the current line number.
%% TODO: maintain column number too?
-record(state,
{ filename :: string()
, iodev :: file:fd() | standard_io
, ungetc :: [] | byte()
, linenr :: pos_integer()
}).
%% API -------------------------------------------------------------------------
-spec fclose(pid()) -> ok | {error, any()}.
fclose(Pid) ->
gen_server:call(Pid, fclose, infinity).
-spec fgetc(pid()) -> {ok, byte()} | eof | {error, any()}.
fgetc(Pid) ->
gen_server:call(Pid, fgetc, infinity).
-spec fopen(string()) -> {ok, pid()} | {error, any()}.
fopen(File) ->
do_fopen(File).
-spec stdin() -> {ok, pid()}.
stdin() ->
do_fopen(stdin).
do_fopen(File) ->
gen_server:start(?MODULE, File, []).
-spec ungetc(byte(), pid()) -> ok | {error, any()}.
ungetc(Ch, Pid) ->
gen_server:call(Pid, {ungetc, Ch}, infinity).
-spec filename(pid()) -> {ok, string()}.
filename(Pid) ->
gen_server:call(Pid, filename, infinity).
-spec linenr(pid()) -> {ok, pos_integer()}.
linenr(Pid) ->
gen_server:call(Pid, linenr, infinity).
%% gen_server callbacks --------------------------------------------------------
init(stdin) ->
do_init("<stdin>", standard_io);
init(File) ->
case file:open(File, [raw, read, read_ahead]) of
{ok, IoDev} -> do_init(File, IoDev);
{error, Reason} -> {stop, Reason}
end.
do_init(FileName, IoDev) ->
{ok, #state{ filename = FileName
, iodev = IoDev
, ungetc = []
, linenr = 1
}}.
handle_call(Req, _From, State) ->
case Req of
fclose ->
handle_fclose(State);
fgetc ->
handle_fgetc(State);
{ungetc, Ch} ->
handle_ungetc(State, Ch);
filename ->
{reply, {ok, State#state.filename}, State};
linenr ->
{reply, {ok, State#state.linenr}, State};
_ ->
{reply, {error, {bad_request, Req}}, State}
end.
handle_cast(_Req, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, State) ->
do_fclose(State).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% fclose ----------------------------------------------------------------------
handle_fclose(State) ->
{stop, normal, ok, State}.
do_fclose(State) ->
case State#state.iodev of
standard_io -> ok;
IoDev -> file:close(IoDev)
end.
%% fgetc -----------------------------------------------------------------------
handle_fgetc(State) ->
case State#state.ungetc of
[] ->
{Result, NewState} =
case file:read(State#state.iodev, 1) of
{ok, [Byte]} ->
{{ok, Byte},
case Byte of
$\n -> State#state{linenr = State#state.linenr + 1};
_ -> State
end};
eof -> {eof, State};
{error, _Reason} = Error -> {Error, State}
end,
{reply, Result, NewState};
Ch ->
{reply, {ok, Ch}, State#state{ungetc = []}}
end.
%% ungetc ----------------------------------------------------------------------
handle_ungetc(State, Ch) ->
case State#state.ungetc of
[] -> {reply, ok, State#state{ungetc = Ch}};
_ -> {reply, {error, ungetc}, State}
end.

View File

@ -0,0 +1,62 @@
%%% -*- erlang-indent-level: 2 -*-
%%%
%%% token handling for pdp10-elf as
%%% Copyright (C) 2013-2019 Mikael Pettersson
%%%
%%% This file is part of pdp10-tools.
%%%
%%% pdp10-tools is free software: you can redistribute it and/or modify
%%% it under the terms of the GNU General Public License as published by
%%% the Free Software Foundation, either version 3 of the License, or
%%% (at your option) any later version.
%%%
%%% pdp10-tools is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%%% GNU General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
-module(token).
-export([ from_symbol/1
, to_string/1
]).
-include("token.hrl").
-spec from_symbol(string()) -> token().
from_symbol(Name) ->
case Name of
".file" -> ?T_DOT_FILE;
".globl" -> ?T_DOT_GLOBL;
".ident" -> ?T_DOT_IDENT;
".size" -> ?T_DOT_SIZE;
".text" -> ?T_DOT_TEXT;
".type" -> ?T_DOT_TYPE;
_ -> {?T_SYMBOL, Name}
end.
-spec to_string(token()) -> string().
to_string(Token) ->
case Token of
?T_DOT_FILE -> ".file";
?T_DOT_GLOBL -> ".globl";
?T_DOT_IDENT -> ".ident";
?T_DOT_SIZE -> ".size";
?T_DOT_TEXT -> ".text";
?T_DOT_TYPE -> ".type";
{?T_SYMBOL, Name} -> io_lib:format("symbol:~s", [Name]);
{?T_UINTEGER, Int} -> io_lib:format("uinteger:~p", [Int]);
{?T_STRING, Str} -> io_lib:format("string:~p", [Str]); % FIXME: quoting
?T_AT -> "@";
?T_COLON -> ":";
?T_COMMA -> ",";
?T_DOT -> ".";
?T_LPAREN -> "(";
?T_MINUS -> "-";
?T_RPAREN -> ")";
?T_NEWLINE -> "<newline>";
?T_EOF -> "<eof>"
end.

View File

@ -0,0 +1,76 @@
%%% -*- erlang-indent-level: 2 -*-
%%%
%%% token definitions for pdp10-elf as
%%% Copyright (C) 2013-2019 Mikael Pettersson
%%%
%%% This file is part of pdp10-tools.
%%%
%%% pdp10-tools is free software: you can redistribute it and/or modify
%%% it under the terms of the GNU General Public License as published by
%%% the Free Software Foundation, either version 3 of the License, or
%%% (at your option) any later version.
%%%
%%% pdp10-tools is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%%% GNU General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
-ifndef(TOKEN_HRL).
-define(TOKEN_HRL, 1).
%% Manifest constants for tokens. These are passed around as-is for
%% attribute-less tokens, or as {Token, Attribute} tuples for tokens
%% with attributes (ordinary symbols, literals, errors).
%% reserved symbols including directives
-define(T_DOT_FILE, 'T_DOT_FILE'). % .file
-define(T_DOT_GLOBL, 'T_DOT_GLOBL'). % .globl
-define(T_DOT_IDENT, 'T_DOT_IDENT'). % .ident
-define(T_DOT_SIZE, 'T_DOT_SIZE'). % .size
-define(T_DOT_TEXT, 'T_DOT_TEXT'). % .text
-define(T_DOT_TYPE, 'T_DOT_TYPE'). % .type
%% ordinary (non-reserved, non-special non-synthetic) symbols
-define(T_SYMBOL, 'T_SYMBOL'). % pushj, foo, .Lbar
%% literals
-define(T_UINTEGER, 'T_UINTEGER'). % 017
-define(T_STRING, 'T_STRING'). % "foo"
%% special symbols including operators and separators
-define(T_AT, 'T_AT'). % @
-define(T_COLON, 'T_COLON'). % :
-define(T_COMMA, 'T_COMMA'). % ,
-define(T_DOT, 'T_DOT'). % .
-define(T_LPAREN, 'T_LPAREN'). % (
-define(T_MINUS, 'T_MINUS'). % -
-define(T_RPAREN, 'T_RPAREN'). % )
%% synthetic symbols
-define(T_NEWLINE, 'T_NEWLINE'). % <end-of-line>
-define(T_EOF, 'T_EOF'). % <end-of-file>
-type token() :: ?T_DOT_FILE
| ?T_DOT_GLOBL
| ?T_DOT_IDENT
| ?T_DOT_SIZE
| ?T_DOT_TEXT
| ?T_DOT_TYPE
| {?T_SYMBOL, string()}
| {?T_UINTEGER, non_neg_integer()}
| {?T_STRING, string()}
| ?T_AT
| ?T_COLON
| ?T_COMMA
| ?T_DOT
| ?T_LPAREN
| ?T_MINUS
| ?T_RPAREN
| ?T_NEWLINE
| ?T_EOF
.
-endif. % TOKEN_HRL

View File

@ -0,0 +1,50 @@
%%% -*- erlang-indent-level: 2 -*-
%%%
%%% translation unit handling for pdp10-elf as.
%%% Copyright (C) 2013-2019 Mikael Pettersson
%%%
%%% This file is part of pdp10-tools.
%%%
%%% pdp10-tools is free software: you can redistribute it and/or modify
%%% it under the terms of the GNU General Public License as published by
%%% the Free Software Foundation, either version 3 of the License, or
%%% (at your option) any later version.
%%%
%%% pdp10-tools is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%%% GNU General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
-module(tunit).
-export([ new/0
, get_section/2
, put_section/2
, get_symbol/2
, put_symbol/2
]).
-include("tunit.hrl").
%% API -------------------------------------------------------------------------
new() ->
#tunit{ sections = #{}
, cursect = false
, symbols = #{}
}.
get_section(#tunit{sections = Sections}, Name) ->
maps:get(Name, Sections, false).
put_section(Tunit = #tunit{sections = Sections}, Section) ->
Tunit#tunit{sections = maps:put(Section#section.name, Section, Sections)}.
get_symbol(#tunit{symbols = Symbols}, Name) ->
maps:get(Name, Symbols, false).
put_symbol(Tunit = #tunit{symbols = Symbols}, Symbol) ->
Tunit#tunit{symbols = maps:put(Symbol#symbol.name, Symbol, Symbols)}.

View File

@ -0,0 +1,116 @@
%%% -*- erlang-indent-level: 2 -*-
%%%
%%% translation unit declarations for pdp10-elf as.
%%% Copyright (C) 2013-2019 Mikael Pettersson
%%%
%%% This file is part of pdp10-tools.
%%%
%%% pdp10-tools is free software: you can redistribute it and/or modify
%%% it under the terms of the GNU General Public License as published by
%%% the Free Software Foundation, either version 3 of the License, or
%%% (at your option) any later version.
%%%
%%% pdp10-tools is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%%% GNU General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
-ifndef(TUNIT_HRL).
-define(TUNIT_HRL, 1).
%% A directive, label, or instruction is parsed to a statement, which is
%% either interpreted immediately or appended to the representation of the
%% current section.
%% .file "foo.c"
-record(s_dot_file, {string :: string()}).
%% .globl foo
-record(s_dot_globl, {name :: string()}).
%% .ident "..."
-record(s_dot_ident, {string :: string()}).
%% .size foo,.-foo (TODO: extend)
-record(s_dot_size, {name :: string()}).
%% .text
-record(s_dot_text, {}).
%% .type foo,@function (TODO: extend)
-record(s_dot_type, {name :: string()}).
%% foo:
-record(s_label, {name :: string()}).
%% opcode accumulator,@address(index)
-record(s_insn,
{ high13 :: 0..((1 bsl 13) - 1)
, at :: boolean()
, address :: non_neg_integer() % TODO: relocatable expr
, index :: 0..((1 bsl 4) - 1)
}).
-type stmt() :: #s_dot_file{}
| #s_dot_globl{}
| #s_dot_ident{}
| #s_dot_size{}
| #s_dot_text{}
| #s_dot_type{}
| #s_label{}
| #s_insn{}
.
%% After the assembly phase sections contain raw image data.
%% Image data is reduced to sequences of "target bytes".
%% We do not use Erlang binaries here to avoid complicating
%% mapping from octet-based hosts to nonet-based targets.
-type tbyte() :: 0..511. % may contain octets or nonets
-type image() :: tbyte() | [image()].
%% Sections accumulate code or data, and define symbols.
-record(section,
{ name :: string()
, data :: {stmts, [stmt()]} % before assembly, in reverse
| {image, image()} % after assembly
, dot :: non_neg_integer()
, shndx :: non_neg_integer() % assigned during output
%% FIXME: should contain an #elf36_Shdr{} here instead
, sh_name :: non_neg_integer() % assigned during output
, sh_type :: non_neg_integer()
, sh_offset :: non_neg_integer() % assigned during output
, sh_flags :: non_neg_integer()
, sh_link :: non_neg_integer() % assigned during output
, sh_addralign :: non_neg_integer()
, sh_entsize :: non_neg_integer()
}).
%% Symbol values.
-record(symbol,
{ name :: string()
, section :: false | string() % false if UNDEF or ABS
%% FIXME: should contain an #elf36_Sym{} here instead
, st_value :: false | non_neg_integer()
, st_size :: false | non_neg_integer()
, st_info :: byte()
, st_name :: non_neg_integer() % assigned during output
, st_shndx :: non_neg_integer() % assigned during output
}).
%% The translation unit object is the top-level container for the
%% representation of the sections, other information collected from
%% the input, and information synthesized during assembly.
-record(tunit,
{ sections :: #{string() => #section{}}
, cursect :: string()
, symbols :: #{string() => #symbol{}}
}).
-endif. % TUNIT_HRL

View File

@ -35,6 +35,7 @@
[
{'8to9', [{escript_main_app, '8to9'}]}
, {ar, [{escript_main_app, ar}]}
, {as, [{escript_main_app, as}]}
, {nm, [{escript_main_app, nm}]}
, {od, [{escript_main_app, od}]}
]}.