pdp10_elf36: start rewrite in Erlang, move Elf36 I/O operations from nm to here

This commit is contained in:
Mikael Pettersson 2019-08-22 20:31:46 +02:00
parent 54cb1c0a94
commit c150845358

View File

@ -0,0 +1,459 @@
%%% -*- erlang-indent-level: 2 -*-
%%%
%%% I/O of PDP10 Elf36 entities
%%% 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(pdp10_elf36).
-export([ read_Ehdr/1
, read_ShTab/2
, read_SymTab/2
, format_error/1
]).
-include_lib("lib/include/pdp10_elf36.hrl").
-include_lib("lib/include/pdp10_stdint.hrl").
%% I/O of records ==============================================================
-type read_field() :: fun((pdp10_stdio:file())
-> {ok, integer()} | {error, {module(), term()}}).
-record(record_desc,
{ tag :: atom()
, fields :: [read_field()]
}).
read_record(FP, #record_desc{tag = Tag, fields = Fields}) ->
read_record(FP, Fields, [Tag]).
read_record(FP, [ReadField | Fields], Values) ->
case ReadField(FP) of
{ok, Value} -> read_record(FP, Fields, [Value | Values]);
{error, _Reason} = Error -> Error
end;
read_record(_FP, [], Values) ->
{ok, list_to_tuple(lists:reverse(Values))}.
%% I/O of #elf36_Ehdr{} ========================================================
-spec read_Ehdr(pdp10_stdio:file())
-> {ok, #elf36_Ehdr{}} | {error, {module(), term()}}.
read_Ehdr(FP) ->
case read_record(FP, elf36_Ehdr_desc()) of
{ok, Ehdr} = Result ->
case check_Ehdr(Ehdr) of
ok -> Result;
{error, _Reason} = Error -> Error
end;
{error, _Reason} = Error -> Error
end.
elf36_Ehdr_desc() ->
#record_desc{ tag = elf36_Ehdr
, fields =
[ fun read_e_ident/1 % e_ident
, fun read_Half/1 % e_type
, fun read_Half/1 % e_machine
, fun read_Word/1 % e_version
, fun read_Addr/1 % e_entry
, fun read_Off/1 % e_phoff
, fun read_Off/1 % e_shoff
, fun read_Word/1 % e_flags
, fun read_Half/1 % e_ehsize
, fun read_Half/1 % e_phentsize
, fun read_Half/1 % e_phnum
, fun read_Half/1 % e_shentsize
, fun read_Half/1 % e_shnum
, fun read_Half/1 % e_shstrndx
]
}.
read_e_ident(FP) ->
read(FP, ?EI_NIDENT, fun(Bytes) -> Bytes end).
check_Ehdr(Ehdr) ->
check(Ehdr,
[ fun check_Ehdr_ei_mag0/1
, fun check_Ehdr_ei_mag1/1
, fun check_Ehdr_ei_mag2/1
, fun check_Ehdr_ei_mag3/1
, fun check_Ehdr_ei_class/1
, fun check_Ehdr_ei_data/1
, fun check_Ehdr_ei_version/1
, fun check_Ehdr_ei_osabi/1
, fun check_Ehdr_ei_abiversion/1
, fun check_Ehdr_e_type/1
, fun check_Ehdr_e_machine/1
, fun check_Ehdr_e_version/1
, fun check_Ehdr_e_ehsize/1
, fun check_Ehdr_e_shentsize/1
]).
check(_X, []) -> ok;
check(X, [Check | Checks]) ->
case Check(X) of
ok -> check(X, Checks);
{error, _Reason} = Error -> Error
end.
check_Ehdr_ei_mag0(Ehdr) ->
#elf36_Ehdr{e_ident = Ident} = Ehdr,
Mag0 = lists:nth(?EI_MAG0 + 1, Ident),
case Mag0 of
?ELFMAG0 -> ok;
_ -> {error, {?MODULE, {wrong_ei_mag0, Mag0}}}
end.
check_Ehdr_ei_mag1(Ehdr) ->
#elf36_Ehdr{e_ident = Ident} = Ehdr,
Mag1 = lists:nth(?EI_MAG1 + 1, Ident),
case Mag1 of
?ELFMAG1 -> ok;
_ -> {error, {?MODULE, {wrong_ei_mag1, Mag1}}}
end.
check_Ehdr_ei_mag2(Ehdr) ->
#elf36_Ehdr{e_ident = Ident} = Ehdr,
Mag2 = lists:nth(?EI_MAG2 + 1, Ident),
case Mag2 of
?ELFMAG2 -> ok;
_ -> {error, {?MODULE, {wrong_ei_mag2, Mag2}}}
end.
check_Ehdr_ei_mag3(Ehdr) ->
#elf36_Ehdr{e_ident = Ident} = Ehdr,
Mag3 = lists:nth(?EI_MAG3 + 1, Ident),
case Mag3 of
?ELFMAG3 -> ok;
_ -> {error, {?MODULE, {wrong_ei_mag3, Mag3}}}
end.
check_Ehdr_ei_class(Ehdr) ->
#elf36_Ehdr{e_ident = Ident} = Ehdr,
Class = lists:nth(?EI_CLASS + 1, Ident),
case Class of
?ELFCLASS36 -> ok;
_ -> {error, {?MODULE, {wrong_ei_class, Class}}}
end.
check_Ehdr_ei_data(Ehdr) ->
#elf36_Ehdr{e_ident = Ident} = Ehdr,
Data = lists:nth(?EI_DATA + 1, Ident),
case Data of
?ELFDATA2MSB -> ok;
_ -> {error, {?MODULE, {wrong_ei_data, Data}}}
end.
check_Ehdr_ei_version(Ehdr) ->
#elf36_Ehdr{e_ident = Ident} = Ehdr,
Version = lists:nth(?EI_VERSION + 1, Ident),
case Version of
?EV_CURRENT -> ok;
_ -> {error, {?MODULE, {wrong_ei_version, Version}}}
end.
check_Ehdr_ei_osabi(Ehdr) ->
#elf36_Ehdr{e_ident = Ident} = Ehdr,
OSABI = lists:nth(?EI_OSABI + 1, Ident),
case OSABI of
?ELFOSABI_NONE -> ok;
?ELFOSABI_LINUX -> ok;
_ -> {error, {?MODULE, {wrong_ei_osabi, OSABI}}}
end.
check_Ehdr_ei_abiversion(Ehdr) ->
#elf36_Ehdr{e_ident = Ident} = Ehdr,
ABIVersion = lists:nth(?EI_ABIVERSION + 1, Ident),
case ABIVersion of
0 -> ok;
_ -> {error, {?MODULE, {wrong_ei_abiversion, ABIVersion}}}
end.
check_Ehdr_e_type(Ehdr) ->
#elf36_Ehdr{e_type = Type} = Ehdr,
case Type of
?ET_REL -> ok;
?ET_EXEC -> ok;
?ET_DYN -> ok;
?ET_CORE -> ok;
_ -> {error, {?MODULE, {wrong_e_type, Type}}}
end.
check_Ehdr_e_machine(Ehdr) ->
#elf36_Ehdr{e_machine = Machine} = Ehdr,
case Machine of
?EM_PDP10 -> ok;
_ -> {error, {?MODULE, {wrong_e_machine, Machine}}}
end.
check_Ehdr_e_version(Ehdr) ->
#elf36_Ehdr{e_version = Version} = Ehdr,
case Version of
?EV_CURRENT -> ok;
_ -> {error, {?MODULE, {wrong_e_version, Version}}}
end.
check_Ehdr_e_ehsize(Ehdr) ->
#elf36_Ehdr{e_ehsize = EhSize} = Ehdr,
case EhSize of
?ELF36_EHDR_SIZEOF -> ok;
_ -> {error, {?MODULE, {wrong_e_ehsize, EhSize}}}
end.
check_Ehdr_e_shentsize(Ehdr) ->
#elf36_Ehdr{e_shoff = ShOff, e_shentsize = ShEntSize} = Ehdr,
case {ShOff, ShEntSize} of
{0, _} -> ok;
{_, ?ELF36_SHDR_SIZEOF} -> ok;
_ -> {error, {?MODULE, {wrong_e_shentsize, ShEntSize}}}
end.
%% I/O of ShTab ================================================================
-spec read_ShTab(pdp10_stdio:file(), #elf36_Ehdr{})
-> {ok, {[#elf36_Shdr{}], term()}} | {error, {module(), term()}}.
read_ShTab(FP, Ehdr) ->
#elf36_Ehdr{ e_shoff = ShOff
, e_shnum = ShNum0
, e_shstrndx = ShStrNdx } = Ehdr,
case ShOff of
0 -> {ok, {[], []}};
_ ->
case pdp10_stdio:fseek(FP, {bof, ShOff}) of
ok ->
case read_Shdr(FP) of
{ok, Shdr0} ->
ShNum = actual_ShNum(ShNum0, Shdr0),
case read_ShTab(FP, ShNum - 1, [Shdr0]) of
{ok, ShTab} ->
case read_ShStrTab(FP, ShTab, ShStrNdx, Shdr0) of
{ok, ShStrTab} ->
%% TODO; now change the sh_names in ShTab to be strings
{ok, {ShTab, ShStrTab}};
{error, _Reason} = Error -> Error
end;
{error, _Reason} = Error -> Error
end;
{error, _Reason} = Error -> Error
end;
{error, _Reason} = Error -> Error
end
end.
actual_ShNum(0, #elf36_Shdr{sh_size = ShNum} = _Shdr0) -> ShNum;
actual_ShNum(ShNum, _Shdr0) -> ShNum.
read_ShTab(_FP, 0, Shdrs) -> {ok, lists:reverse(Shdrs)};
read_ShTab(FP, ShNum, Shdrs) ->
case read_Shdr(FP) of
{ok, Shdr} -> read_ShTab(FP, ShNum - 1, [Shdr | Shdrs]);
{error, _Reason} = Error -> Error
end.
%% I/O of ShStrTab =============================================================
read_ShStrTab(FP, ShTab, ShStrNdx, Shdr0) ->
case ShStrNdx of
?SHN_UNDEF -> {ok, []};
?SHN_XINDEX -> read_StrTab(FP, ShTab, Shdr0#elf36_Shdr.sh_link);
_ -> read_StrTab(FP, ShTab, ShStrNdx)
end.
%% I/O of StrTab ===============================================================
read_StrTab(FP, ShTab, Index) ->
ShNum = length(ShTab),
case Index > 0 andalso Index < ShNum of
true ->
#elf36_Shdr{ sh_type = Type
, sh_size = Size
, sh_offset = Offset
} = lists:nth(Index + 1, ShTab),
case Type of
?SHT_STRTAB ->
case pdp10_stdio:fseek(FP, {bof, Offset}) of
ok ->
case pdp10_stdio:fread(1, Size, FP) of
eof -> {error, {?MODULE, {eof_in_strtab, Index}}};
Other -> Other % {ok, _StrTab} or {error, _Reason}
end;
{error, _Reason} = Error -> Error
end;
_ -> {error, {?MODULE, {wrong_strtab_sh_type, Type, Index}}}
end;
false -> {error, {?MODULE, {wrong_strtab_index, Index}}}
end.
%% I/O of SymTab ===============================================================
read_SymTab(FP, ShTab) ->
case find_SymTab(ShTab) of
false -> {ok, {[], []}};
{ok, Shdr} ->
#elf36_Shdr{ sh_link = ShLink
, sh_entsize = ShEntSize
, sh_size = ShSize
, sh_offset = ShOffset
} = Shdr,
if ShEntSize =/= ?ELF36_SYM_SIZEOF ->
{error, {?MODULE, {wrong_symtab_sh_entsize, ShEntSize}}};
(ShSize rem ?ELF36_SYM_SIZEOF) =/= 0 ->
{error, {?MODULE, {wrong_symtab_sh_size, ShSize}}};
true ->
case read_StrTab(FP, ShTab, ShLink) of
{ok, StrTab} ->
SymNum = ShSize div ?ELF36_SYM_SIZEOF,
case SymNum of
0 -> {ok, {[], StrTab}};
_ ->
case pdp10_stdio:fseek(FP, {bof, ShOffset}) of
ok ->
case read_SymTab(FP, SymNum, []) of
{ok, SymTab} -> {ok, {SymTab, StrTab}};
{error, _Reason} = Error -> Error
end;
{error, _Reason} = Error -> Error
end
end;
{error, _Reason} = Error -> Error
end
end
end.
read_SymTab(_FP, _SymNum = 0, Syms) -> {ok, lists:reverse(Syms)};
read_SymTab(FP, SymNum, Syms) when SymNum > 0 ->
case read_Sym(FP) of
{ok, Sym} -> read_SymTab(FP, SymNum - 1, [Sym | Syms]);
{error, _Reason} = Error -> Error
end.
find_SymTab([]) -> false;
find_SymTab([#elf36_Shdr{sh_type = ?SHT_SYMTAB} = Shdr | _]) -> {ok, Shdr};
find_SymTab([_Shdr | ShTab]) -> find_SymTab(ShTab).
%% I/O of #elf36_Shdr{} ========================================================
read_Shdr(FP) -> read_record(FP, elf36_Shdr_desc()).
elf36_Shdr_desc() ->
#record_desc{ tag = elf36_Shdr
, fields =
[ fun read_Word/1 % sh_name
, fun read_Word/1 % sh_type
, fun read_Word/1 % sh_flags
, fun read_Addr/1 % sh_addr
, fun read_Off/1 % sh_offset
, fun read_Word/1 % sh_size
, fun read_Word/1 % sh_link
, fun read_Word/1 % sh_info
, fun read_Word/1 % sh_addralign
, fun read_Word/1 % sh_entsize
]
}.
%% I/O of #elf36_Sym{} =========================================================
read_Sym(FP) -> read_record(FP, elf36_Sym_desc()).
elf36_Sym_desc() ->
#record_desc{ tag = elf36_Sym
, fields =
[ fun read_Word/1 % st_name
, fun read_Addr/1 % st_value
, fun read_Word/1 % st_size
, fun read_Uchar/1 % st_info
, fun read_Uchar/1 % st_other
, fun read_Half/1 % st_shndx
]
}.
%% I/O of scalar items =========================================================
read_Addr(FP) -> read_uint36(FP).
read_Half(FP) -> read_uint18(FP).
read_Off(FP) -> read_uint36(FP).
read_Uchar(FP) -> read_uint9(FP).
read_Word(FP) -> read_uint36(FP).
read_uint9(FP) ->
case pdp10_stdio:fgetc(FP) of
eof -> {error, {?MODULE, eof}};
Other -> Other % {ok, _Nonet} or {error, _Reason}
end.
read_uint18(FP) -> read(FP, 2, fun pdp10_extint:uint18_from_ext/1).
read_uint36(FP) -> read(FP, 4, fun pdp10_extint:uint36_from_ext/1).
read(FP, N, ConvFun) when N >= 0 -> read(FP, N, ConvFun, []).
read(_FP, 0, ConvFun, Acc) -> {ok, ConvFun(lists:reverse(Acc))};
read(FP, N, ConvFun, Acc) ->
case read_uint9(FP) of
{ok, Nonet} -> read(FP, N - 1, ConvFun, [Nonet | Acc]);
{error, _Reason} = Error -> Error
end.
%% Error Formatting ============================================================
-spec format_error(term()) -> io_lib:chars().
format_error(Reason) ->
case Reason of
{wrong_ei_mag0, Mag0} ->
io_lib:format("wrong ei_mag0 ~p", [Mag0]);
{wrong_ei_mag1, Mag1} ->
io_lib:format("wrong ei_mag1 ~p", [Mag1]);
{wrong_ei_mag2, Mag2} ->
io_lib:format("wrong ei_mag2 ~p", [Mag2]);
{wrong_ei_mag3, Mag3} ->
io_lib:format("wrong ei_mag3 ~p", [Mag3]);
{wrong_ei_class, Class} ->
io_lib:format("wrong ei_class ~p", [Class]);
{wrong_ei_data, Data} ->
io_lib:format("wrong ei_data ~p", [Data]);
{wrong_ei_version, Version} ->
io_lib:format("wrong ei_version ~p", [Version]);
{wrong_ei_osabi, OSABI} ->
io_lib:format("wrong ei_osabi ~p", [OSABI]);
{wrong_ei_abiversion, ABIVersion} ->
io_lib:format("wrong ei_abiversion ~p", [ABIVersion]);
{wrong_e_type, Type} ->
io_lib:format("wrong e_type ~p", [Type]);
{wrong_e_machine, Machine} ->
io_lib:format("wrong e_machine ~p", [Machine]);
{wrong_e_version, Version} ->
io_lib:format("wrong e_version ~p", [Version]);
{wrong_e_ehsize, EhSize} ->
io_lib:format("wrong e_ehsize ~p", [EhSize]);
{wrong_e_shentsize, ShEntSize} ->
io_lib:format("wrong e_shentsize ~p", [ShEntSize]);
{eof_in_strtab, Index} ->
io_lib:format("premature EOF in string table at index ~p", [Index]);
{wrong_strtab_sh_type, ShType, Index} ->
io_lib:format("wrong sh_type ~p for string table at index ~p",
[ShType, Index]);
{wrong_strtab_index, Index} ->
io_lib:format("out of range index ~p for string table", [Index]);
{wrong_symtab_sh_entsize, ShEntSize} ->
io_lib:format("wrong sh_entsize ~p in symtab section header", [ShEntSize]);
{wrong_symtab_sh_size, ShSize} ->
io_lib:format("wrong sh_size ~p in symtab section header", [ShSize]);
_ ->
io_lib:format("~p", [Reason])
end.