Files
mikpe.pdp10-tools/erlang/apps/lib/src/libelf.erl
2025-08-13 14:34:19 +02:00

817 lines
28 KiB
Erlang

%%% -*- erlang-indent-level: 2 -*-
%%%
%%% I/O of ELF-36 entities
%%% Copyright (C) 2013-2025 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(libelf).
-export([ read_Ehdr/1
, read_Ehdr/3
, read_PhTab/2
, read_PhTab/4
, read_RelaTab/2
, read_RelaTab/4
, read_ShTab/2
, read_ShTab/4
, read_SymTab/2
, read_SymTab/4
, read_uint36/1
, make_Ehdr/0
, write_Ehdr/2
, write_Phdr/2
, format_error/1
]).
-include_lib("lib/include/libelf.hrl").
-include_lib("lib/include/stdint.hrl").
-type read_field() :: fun((pdp10_stdio:file())
-> {ok, integer()} | {error, {module(), term()}}).
-type write_field() :: fun((pdp10_stdio:file(), integer())
-> ok | {error, term()}).
-record(record_desc,
{ tag :: atom()
, fields :: [{read_field(), write_field()}]
}).
%% I/O of #elf_Ehdr{} ==========================================================
-spec read_Ehdr(pdp10_stdio:file())
-> {ok, #elf_Ehdr{}} | {error, {module(), term()}}.
read_Ehdr(FP) ->
read_Ehdr(FP, _Base = 0, _Limit = false).
%% FIXME: take EI_CLASS as parameter
-spec read_Ehdr(pdp10_stdio:file(), non_neg_integer(), false | non_neg_integer())
-> {ok, #elf_Ehdr{}} | {error, {module(), term()}}.
read_Ehdr(FP, Base, Limit) ->
case pdp10_stdio:fseek(FP, {bof, Base}) of
ok ->
case read_record(FP, elf_Ehdr_desc()) of
{ok, Ehdr} = Result ->
case (Limit =:= false) orelse (pdp10_stdio:ftell(FP) =< Limit) of
true ->
case check_Ehdr(Ehdr) of
ok -> Result;
{error, _Reason} = Error -> Error
end;
false -> {error, {?MODULE, {limit, "Ehdr"}}}
end;
{error, _Reason} = Error -> Error
end;
{error, _Reason} = Error -> Error
end.
%% FIXME: take EI_CLASS as parameter
-spec make_Ehdr() -> #elf_Ehdr{}.
make_Ehdr() ->
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} % TODO: target-specific
, {1 + ?EI_DATA, ?ELFDATA2MSB} % TODO: target-specific
, {1 + ?EI_VERSION, ?EV_CURRENT}
, {1 + ?EI_OSABI, ?ELFOSABI_NONE} % TODO: ELFOSABI_LINUX instead?
, {1 + ?EI_ABIVERSION, 0}
])),
#elf_Ehdr{ e_ident = Ident
, e_type = ?ET_NONE
, e_machine = ?EM_PDP10 % TODO: target-specific
, e_version = ?EV_CURRENT
, e_entry = 0
, e_phoff = 0
, e_shoff = 0
, e_flags = 0
, e_ehsize = ?ELF36_EHDR_SIZEOF
, e_phentsize = ?ELF36_PHDR_SIZEOF
, e_phnum = 0
, e_shentsize = ?ELF36_SHDR_SIZEOF
, e_shnum = 0
, e_shstrndx = 0
}.
%% FIXME: take EI_CLASS as parameter
-spec write_Ehdr(pdp10_stdio:file(), #elf_Ehdr{})
-> ok | {error, {module(), term()}}.
write_Ehdr(FP, Ehdr) ->
write_record(FP, Ehdr, elf_Ehdr_desc()).
elf_Ehdr_desc() ->
#record_desc{ tag = elf_Ehdr
, fields =
[ {fun read_e_ident/1, fun write_e_ident/2} % e_ident
, {fun read_Half/1, fun write_Half/2} % e_type
, {fun read_Half/1, fun write_Half/2} % e_machine
, {fun read_Word/1, fun write_Word/2} % e_version
, {fun read_Addr/1, fun write_Addr/2} % e_entry
, {fun read_Off/1, fun write_Off/2} % e_phoff
, {fun read_Off/1, fun write_Off/2} % e_shoff
, {fun read_Word/1, fun write_Word/2} % e_flags
, {fun read_Half/1, fun write_Half/2} % e_ehsize
, {fun read_Half/1, fun write_Half/2} % e_phentsize
, {fun read_Half/1, fun write_Half/2} % e_phnum
, {fun read_Half/1, fun write_Half/2} % e_shentsize
, {fun read_Half/1, fun write_Half/2} % e_shnum
, {fun read_Half/1, fun write_Half/2} % e_shstrndx
]
}.
read_e_ident(FP) ->
read(FP, ?EI_NIDENT, fun(Bytes) -> Bytes end).
write_e_ident(FP, Ident) ->
?EI_NIDENT = length(Ident), % assert
fputs(Ident, FP).
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_ei_pad/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_phentsize/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) ->
#elf_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) ->
#elf_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) ->
#elf_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) ->
#elf_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) ->
#elf_Ehdr{e_ident = Ident} = Ehdr,
Class = lists:nth(?EI_CLASS + 1, Ident),
case Class of
%% FIXME: extend
?ELFCLASS36 -> ok;
_ -> {error, {?MODULE, {wrong_ei_class, Class}}}
end.
check_Ehdr_ei_data(Ehdr) ->
#elf_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) ->
#elf_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) ->
#elf_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) ->
#elf_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_ei_pad(Ehdr) ->
#elf_Ehdr{e_ident = Ident} = Ehdr,
Pad = lists:nthtail(?EI_PAD, Ident),
Zeroes = lists:duplicate(?EI_NIDENT - ?EI_PAD, 0),
case Pad of
Zeroes -> ok;
_ -> {error, {?MODULE, {wrong_ei_pad, Pad}}}
end.
check_Ehdr_e_type(Ehdr) ->
#elf_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) ->
#elf_Ehdr{e_machine = Machine} = Ehdr,
case Machine of
?EM_PDP10 -> ok;
_ -> {error, {?MODULE, {wrong_e_machine, Machine}}}
end.
check_Ehdr_e_version(Ehdr) ->
#elf_Ehdr{e_version = Version} = Ehdr,
case Version of
?EV_CURRENT -> ok;
_ -> {error, {?MODULE, {wrong_e_version, Version}}}
end.
check_Ehdr_e_ehsize(Ehdr) ->
#elf_Ehdr{e_ehsize = EhSize} = Ehdr,
case EhSize of
%% FIXME: extend
?ELF36_EHDR_SIZEOF -> ok;
_ -> {error, {?MODULE, {wrong_e_ehsize, EhSize}}}
end.
check_Ehdr_e_phentsize(Ehdr) ->
#elf_Ehdr{e_phoff = PhOff, e_phentsize = PhEntSize} = Ehdr,
case {PhOff, PhEntSize} of
{0, _} -> ok;
%% FIXME: extend
{_, ?ELF36_PHDR_SIZEOF} -> ok;
_ -> {error, {?MODULE, {wrong_e_phentsize, PhEntSize}}}
end.
check_Ehdr_e_shentsize(Ehdr) ->
#elf_Ehdr{e_shoff = ShOff, e_shentsize = ShEntSize} = Ehdr,
case {ShOff, ShEntSize} of
{0, _} -> ok;
%% FIXME: extend
{_, ?ELF36_SHDR_SIZEOF} -> ok;
_ -> {error, {?MODULE, {wrong_e_shentsize, ShEntSize}}}
end.
%% I/O of PhTab ================================================================
-spec read_PhTab(pdp10_stdio:file(), #elf_Ehdr{})
-> {ok, [#elf_Phdr{}]} | {error, {module(), term()}}.
read_PhTab(FP, Ehdr) ->
read_PhTab(FP, _Base = 0, _Limit = false, Ehdr).
-spec read_PhTab(pdp10_stdio:file(), non_neg_integer(), false | non_neg_integer(), #elf_Ehdr{})
-> {ok, [#elf_Phdr{}]} | {error, {module(), term()}}.
read_PhTab(FP, Base, Limit, Ehdr) ->
#elf_Ehdr{ e_phoff = PhOff
, e_phentsize = PhEntSize
, e_phnum = PhNum } = Ehdr,
if PhOff =:= 0; PhNum =:= 0 ->
{ok, []};
true ->
true = PhEntSize =:= ?ELF36_PHDR_SIZEOF, % assert
%% FIXME: if PhNum = ?PN_XNUM the real PhNum is stored in Shdr0.sh_info
case pdp10_stdio:fseek(FP, {bof, Base + PhOff}) of
ok ->
case read_PhTab(FP, PhNum, []) of
{ok, _PhTab} = Result ->
case (Limit =:= false) orelse (pdp10_stdio:ftell(FP) =< Limit) of
true -> Result;
false -> {error, {?MODULE, {limit, "PhTab"}}}
end;
{error, _Reason} = Error -> Error
end;
{error, _Reason} = Error -> Error
end
end.
read_PhTab(_FP, 0, Phdrs) -> {ok, lists:reverse(Phdrs)};
read_PhTab(FP, PhNum, Phdrs) ->
case read_Phdr(FP) of
{ok, Phdr} -> read_PhTab(FP, PhNum - 1, [Phdr | Phdrs]);
{error, _Reason} = Error -> Error
end.
%% I/O of relocation tables ====================================================
-spec read_RelaTab(pdp10_stdio:file(), #elf_Shdr{})
-> {ok, [#elf_Rela{}]} | {error, {module(), term()}}.
read_RelaTab(FP, Shdr) ->
read_RelaTab(FP, _Base = 0, _Limit = false, Shdr).
-spec read_RelaTab(pdp10_stdio:file(), non_neg_integer(), false | non_neg_integer(), #elf_Shdr{})
-> {ok, [#elf_Rela{}]} | {error, {module(), term()}}.
read_RelaTab(FP, Base, Limit, Shdr) ->
#elf_Shdr{ sh_type = ShType
, sh_size = ShSize
, sh_offset = ShOffset
, sh_entsize = ShEntSize
} = Shdr,
case ShType of
?SHT_RELA ->
case ShEntSize of
?ELF36_RELA_SIZEOF ->
case ShSize rem ?ELF36_RELA_SIZEOF of
0 ->
RelaNum = ShSize div ?ELF36_RELA_SIZEOF,
case RelaNum of
0 -> {ok, []};
_ ->
case pdp10_stdio:fseek(FP, {bof, Base + ShOffset}) of
ok ->
case read_RelaTab(FP, RelaNum, []) of
{ok, _RelaTab} = Result ->
case (Limit =:= false) orelse (pdp10_stdio:ftell(FP) =< Limit) of
true -> Result;
false -> {error, {?MODULE, {limit, "RelaTab"}}}
end;
{error, _Reason} = Error -> Error
end;
{error, _Reason} = Error -> Error
end
end;
_ -> {error, {?MODULE, {wrong_relatab_sh_size, ShSize}}}
end;
_ -> {error, {?MODULE, {wrong_relatab_sh_entsize, ShEntSize}}}
end;
_ -> {error, {?MODULE, {wrong_relatab_sh_type, ShType}}}
end.
read_RelaTab(_FP, _RelaNum = 0, Relas) -> {ok, lists:reverse(Relas)};
read_RelaTab(FP, RelaNum, Relas) when RelaNum > 0 ->
case read_Rela(FP) of
{ok, Rela} -> read_RelaTab(FP, RelaNum - 1, [Rela | Relas]);
{error, _Reason} = Error -> Error
end.
%% I/O of ShTab ================================================================
-spec read_ShTab(pdp10_stdio:file(), #elf_Ehdr{})
-> {ok, [#elf_Shdr{}]} | {error, {module(), term()}}.
read_ShTab(FP, Ehdr) ->
read_ShTab(FP, _Base = 0, _Limit = false, Ehdr).
-spec read_ShTab(pdp10_stdio:file(), non_neg_integer(), false | non_neg_integer(), #elf_Ehdr{})
-> {ok, [#elf_Shdr{}]} | {error, {module(), term()}}.
read_ShTab(FP, Base, Limit, Ehdr) ->
#elf_Ehdr{ e_shoff = ShOff
, e_shnum = ShNum0
, e_shstrndx = ShStrNdx } = Ehdr,
case ShOff of
0 -> {ok, []};
_ ->
case pdp10_stdio:fseek(FP, {bof, Base + 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 (Limit =:= false) orelse (pdp10_stdio:ftell(FP) =< Limit) of
true ->
case read_ShStrTab(FP, Base, Limit, ShTab, ShStrNdx, Shdr0) of
{ok, ShStrTab} -> read_ShTab_names(ShTab, ShStrTab);
{error, _Reason} = Error -> Error
end;
false -> {error, {?MODULE, {limit, "ShTab"}}}
end;
{error, _Reason} = Error -> Error
end;
{error, _Reason} = Error -> Error
end;
{error, _Reason} = Error -> Error
end
end.
actual_ShNum(0, #elf_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.
read_ShTab_names(ShTab, ShStrTab) ->
read_ShTab_names(ShTab, ShStrTab, []).
read_ShTab_names([], _ShStrTab, Acc) -> {ok, lists:reverse(Acc)};
read_ShTab_names([Shdr | ShTab], ShStrTab, Acc) ->
case read_Shdr_name(Shdr, ShStrTab) of
{ok, NewShdr} -> read_ShTab_names(ShTab, ShStrTab, [NewShdr | Acc]);
{error, _Reason} = Error -> Error
end.
read_Shdr_name(Shdr = #elf_Shdr{sh_name = ShName}, ShStrTab) ->
case get_name(ShStrTab, ShName) of
{ok, Name} -> {ok, Shdr#elf_Shdr{sh_name = Name}};
{error, _Reason} = Error -> Error
end.
%% I/O of ShStrTab =============================================================
read_ShStrTab(FP, Base, Limit, ShTab, ShStrNdx, Shdr0) ->
case ShStrNdx of
?SHN_UNDEF -> {ok, []};
?SHN_XINDEX -> read_StrTab(FP, Base, Limit, ShTab, Shdr0#elf_Shdr.sh_link);
_ -> read_StrTab(FP, Base, Limit, ShTab, ShStrNdx)
end.
%% I/O of StrTab ===============================================================
read_StrTab(FP, Base, Limit, ShTab, Index) ->
ShNum = length(ShTab),
case Index > 0 andalso Index < ShNum of
true ->
#elf_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, Base + Offset}) of
ok ->
case pdp10_stdio:fread(1, Size, FP) of
{ok, _StrTab} = Result ->
case (Limit =:= false) orelse (pdp10_stdio:ftell(FP) =< Limit) of
true -> Result;
false -> {error, {?MODULE, {limit, "StrTab"}}}
end;
eof -> {error, {?MODULE, {eof_in_strtab, Index}}};
{error, _Reason} = Error -> Error
end;
{error, _Reason} = Error -> Error
end;
_ -> {error, {?MODULE, {wrong_strtab_sh_type, Type, Index}}}
end;
false -> {error, {?MODULE, {wrong_strtab_index, Index}}}
end.
%% reading a name from a StrTab ================================================
get_name(_StrTab, 0) -> {ok, ""}; % TODO: or some marker for "absent"
get_name(StrTab, Index) ->
try lists:nthtail(Index, StrTab) of
[C | Tail] -> get_name(C, Tail, [])
catch _C:_R ->
{error, {?MODULE, {wrong_index_in_strtab, Index}}}
end.
get_name(0, _Tail, Acc) -> {ok, lists:reverse(Acc)};
get_name(C1, [C2 | Tail], Acc) -> get_name(C2, Tail, [C1 | Acc]);
get_name(_C, [], _Acc) -> {error, {?MODULE, strtab_not_nul_terminated}}.
%% I/O of SymTab ===============================================================
-spec read_SymTab(pdp10_stdio:file(), [#elf_Shdr{}])
-> {ok, {[#elf_Sym{}],non_neg_integer()}} | {error, {module(), term()}}.
read_SymTab(FP, ShTab) ->
read_SymTab(FP, _Base = 0, _Limit = false, ShTab).
-spec read_SymTab(pdp10_stdio:file(), non_neg_integer(), false | non_neg_integer(), [#elf_Shdr{}])
-> {ok, {[#elf_Sym{}],non_neg_integer()}} | {error, {module(), term()}}.
read_SymTab(FP, Base, Limit, ShTab) ->
case find_SymTab(ShTab) of
false -> {ok, {[], ?SHN_UNDEF}};
{ok, {Shdr, ShNdx}} ->
#elf_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, Base, Limit, ShTab, ShLink) of
{ok, StrTab} ->
SymNum = ShSize div ?ELF36_SYM_SIZEOF,
case SymNum of
0 -> {ok, {[], ShNdx}};
_ ->
case pdp10_stdio:fseek(FP, {bof, Base + ShOffset}) of
ok ->
case read_SymTab(FP, SymNum, []) of
{ok, SymTab} ->
case (Limit =:= false) orelse (pdp10_stdio:ftell(FP) =< Limit) of
true ->
case read_SymTab_names(SymTab, StrTab) of
{ok, NewSymTab} -> {ok, {NewSymTab, ShNdx}};
{error, _Reason} = Error -> Error
end;
false -> {error, {?MODULE, {limit, "SymTab"}}}
end;
{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(ShTab) -> find_SymTab(ShTab, 0).
find_SymTab([], _I) -> false;
find_SymTab([#elf_Shdr{sh_type = ?SHT_SYMTAB} = Shdr | _], I) -> {ok, {Shdr, I}};
find_SymTab([_Shdr | ShTab], I) -> find_SymTab(ShTab, I + 1).
read_SymTab_names(SymTab, StrTab) ->
read_SymTab_names(SymTab, StrTab, []).
read_SymTab_names([], _StrTab, Acc) -> {ok, lists:reverse(Acc)};
read_SymTab_names([Sym | SymTab], StrTab, Acc) ->
case read_Sym_name(Sym, StrTab) of
{ok, NewSym} -> read_SymTab_names(SymTab, StrTab, [NewSym | Acc]);
{error, _Reason} = Error -> Error
end.
read_Sym_name(Sym = #elf_Sym{st_name = StName}, StrTab) ->
case get_name(StrTab, StName) of
{ok, Name} -> {ok, Sym#elf_Sym{st_name = Name}};
{error, _Reason} = Error -> Error
end.
%% I/O of #elf_Phdr{} ==========================================================
read_Phdr(FP) -> read_record(FP, elf_Phdr_desc()).
-spec write_Phdr(pdp10_stdio:file(), #elf_Phdr{})
-> ok | {error, {module(), term()}}.
write_Phdr(FP, Phdr) ->
write_record(FP, Phdr, elf_Phdr_desc()).
%% FIXME: take EI_CLASS as parameter
%% FIXME: ELF-64 stores the fields in a different order
elf_Phdr_desc() ->
#record_desc{ tag = elf_Phdr
, fields =
[ {fun read_Word/1, fun write_Word/2} % p_type
, {fun read_Off/1, fun write_Off/2} % p_offset
, {fun read_Addr/1, fun write_Addr/2} % p_vaddr
, {fun read_Addr/1, fun write_Addr/2} % p_paddr
, {fun read_Word/1, fun write_Word/2} % p_filesz
, {fun read_Word/1, fun write_Word/2} % p_memsz
, {fun read_Word/1, fun write_Word/2} % p_flags
, {fun read_Word/1, fun write_Word/2} % p_align
]
}.
%% I/O of #elf_Rela{} ==========================================================
read_Rela(FP) -> read_record(FP, elf_Rela_desc()).
%% FIXME: take EI_CLASS as parameter
elf_Rela_desc() ->
#record_desc{ tag = elf_Rela
, fields =
[ {fun read_Addr/1, fun write_Addr/2} % r_offset
, {fun read_Word/1, fun write_Word/2} % r_info
, {fun read_Sword/1, fun write_Sword/2} % r_addend
]
}.
%% I/O of #elf_Shdr{} ==========================================================
read_Shdr(FP) -> read_record(FP, elf_Shdr_desc()).
%% FIXME: take EI_CLASS as parameter
elf_Shdr_desc() ->
#record_desc{ tag = elf_Shdr
, fields =
[ {fun read_Word/1, fun write_Word/2} % sh_name
, {fun read_Word/1, fun write_Word/2} % sh_type
, {fun read_Word/1, fun write_Word/2} % sh_flags (FIXME: Xword)
, {fun read_Addr/1, fun write_Addr/2} % sh_addr
, {fun read_Off/1, fun write_Off/2} % sh_offset
, {fun read_Word/1, fun write_Word/2} % sh_size (FIXME: Xword)
, {fun read_Word/1, fun write_Word/2} % sh_link
, {fun read_Word/1, fun write_Word/2} % sh_info
, {fun read_Word/1, fun write_Word/2} % sh_addralign (FIXME: Xword)
, {fun read_Word/1, fun write_Word/2} % sh_entsize (FIXME: Xword)
]
}.
%% I/O of #elf_Sym{} ===========================================================
read_Sym(FP) -> read_record(FP, elf_Sym_desc()).
%% FIXME: take EI_CLASS as parameter
%% FIXME: ELF-64 orders the fields differently
elf_Sym_desc() ->
#record_desc{ tag = elf_Sym
, fields =
[ {fun read_Word/1, fun write_Word/2} % st_name
, {fun read_Addr/1, fun write_Addr/2} % st_value
, {fun read_Word/1, fun write_Word/2} % st_size
, {fun read_Uchar/1, fun write_Uchar/2} % st_info
, {fun read_Uchar/1, fun write_Uchar/2} % st_other
, {fun read_Half/1, fun write_Half/2} % st_shndx
]
}.
%% I/O of records ==============================================================
read_record(FP, #record_desc{tag = Tag, fields = Fields}) ->
read_record(FP, Fields, [Tag]).
read_record(FP, [{ReadField, _WriteField} | 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))}.
write_record(FP, Record, #record_desc{tag = Tag, fields = Fields}) ->
[Tag | Values] = tuple_to_list(Record),
do_write_record(FP, Fields, Values).
do_write_record(FP, [{_ReadField, WriteField} | Fields], [Value | Values]) ->
case WriteField(FP, Value) of
ok -> do_write_record(FP, Fields, Values);
{error, _Reason} = Error -> Error
end;
do_write_record(_FP, _Fields = [], _Values = []) ->
ok.
%% I/O of scalar items =========================================================
read_Addr(FP) -> read_uint36(FP).
read_Half(FP) -> read_uint18(FP).
read_Off(FP) -> read_uint36(FP).
read_Sword(FP) -> read_sint36(FP).
read_Uchar(FP) -> read_uint9(FP).
read_Word(FP) -> read_uint36(FP).
read_sint36(FP) ->
case read_uint36(FP) of
{ok, UInt36} -> {ok, sext:sext(UInt36, 36)};
{error, _Reason} = Error -> Error
end.
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 extint:uint18_from_ext/1).
read_uint36(FP) -> read(FP, 4, fun 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.
write_Addr(FP, UInt36) -> write_uint36(FP, UInt36).
write_Half(FP, UInt18) -> write_uint18(FP, UInt18).
write_Off(FP, UInt36) -> write_uint36(FP, UInt36).
write_Sword(FP, SInt36) -> write_uint36(FP, SInt36 band ?UINT36_MAX).
write_Uchar(FP, UInt9) -> write_uint9(FP, UInt9).
write_Word(FP, UInt36) -> write_uint36(FP, UInt36).
write_uint9(FP, UInt9) ->
pdp10_stdio:fputc(UInt9, FP).
write_uint18(FP, UInt18) ->
fputs(extint:uint18_to_ext(UInt18), FP).
write_uint36(FP, UInt36) ->
fputs(extint:uint36_to_ext(UInt36), FP).
fputs(Nonets, FP) ->
pdp10_stdio:fputs(Nonets, FP).
%% 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_ei_pad, Pad} ->
io_lib:format("wrong ei_pad ~p", [Pad]);
{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_phentsize, PhEntSize} ->
io_lib:format("wrong e_phentsize ~p", [PhEntSize]);
{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_relatab_sh_entsize, ShEntSize} ->
io_lib:format("wrong sh_entsize ~p in relocation table header", [ShEntSize]);
{wrong_relatab_sh_size, ShSize} ->
io_lib:format("wrong sh_size ~p in relocation table header", [ShSize]);
{wrong_relatab_sh_type, ShType} ->
io_lib:format("wrong sh_type ~p in relocation table header", [ShType]);
{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]);
eof ->
"premature EOF";
{wrong_index_in_strtab, Index} ->
io_lib:format("out of range index ~p in string table", [Index]);
strtab_not_nul_terminated ->
"string table not NUL-terminated";
{limit, What} ->
io_lib:format("~s extends beyond limit of embedded file", [What]);
_ ->
io_lib:format("~p", [Reason])
end.