mirror of
https://github.com/mikpe/pdp10-tools.git
synced 2026-01-23 19:08:00 +00:00
nm: rewrite in Erlang
This commit is contained in:
parent
b37c24d009
commit
964eed2f31
@ -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 od
|
||||
PROGRAMS=8to9 nm od
|
||||
|
||||
default: compile link
|
||||
|
||||
|
||||
25
erlang/apps/nm/src/nm.app.src
Normal file
25
erlang/apps/nm/src/nm.app.src
Normal 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, nm,
|
||||
[{description, "'nm' clone for PDP10 Elf36 files"},
|
||||
{vsn, "0.1.0"},
|
||||
{registered, []},
|
||||
{applications, [kernel, stdlib, lib]},
|
||||
{env, []},
|
||||
{modules, []}
|
||||
]}.
|
||||
749
erlang/apps/nm/src/nm.erl
Normal file
749
erlang/apps/nm/src/nm.erl
Normal file
@ -0,0 +1,749 @@
|
||||
%%% -*- erlang-indent-level: 2 -*-
|
||||
%%%
|
||||
%%% 'nm' clone for PDP10 Elf36 files
|
||||
%%% 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(nm).
|
||||
-export([main/1]).
|
||||
|
||||
-include_lib("lib/include/pdp10_elf36.hrl").
|
||||
|
||||
-record(options, {
|
||||
print_file_name = false % -A, -o, --print-file-name
|
||||
, dynamic = false % -D, --dynamic
|
||||
, format = $b % -B, -P, --portability, -f [bsd|sysv|posix], --format=[bsd|sysv|posix]
|
||||
, extern_only = false % -g, --extern-only
|
||||
, numeric_sort = false % -n, -v, --numeric-sort
|
||||
, no_sort = false % -p, --no-sort
|
||||
, print_size = false % -S, --print-size
|
||||
, reverse_sort = false % -r, --reverse-sort
|
||||
, radix = $x % -t [dox], --radix=[dox]
|
||||
, undefined_only = false % -u, --undefined-only
|
||||
, defined_only = false % --defined-only
|
||||
}).
|
||||
|
||||
%% Command-line interface ======================================================
|
||||
|
||||
main(Argv) ->
|
||||
escript_runtime:start(fun main_/1, Argv).
|
||||
|
||||
main_(Argv) ->
|
||||
%% NYI options:
|
||||
%% -a / --debug-syms
|
||||
%% --demangle
|
||||
%% --plugin <name>
|
||||
%% -l / --line-numbers
|
||||
%% --size-sort
|
||||
%% -s / --print-armap [TODO]
|
||||
%% --target=<bfdname>
|
||||
%% -X 32_64
|
||||
%% --help [TODO?]
|
||||
%% @file [TODO?]
|
||||
case getopt:parse(Argv, "AoBDf:gnvpPSrt:uV",
|
||||
[ %% long-only options
|
||||
{"no-demangle", no, no_demangle}
|
||||
, {"special-syms", no, special_syms}
|
||||
, {"defined-only", no, defined_only}
|
||||
%% long aliases for short options
|
||||
, {"print-file-name", no, $A}
|
||||
, {"dynamic", no, $D}
|
||||
, {"format", required, $f}
|
||||
, {"extern-only", no, $g}
|
||||
, {"numeric-sort", no, $n}
|
||||
, {"no-sort", no, $p}
|
||||
, {"portability", no, $P}
|
||||
, {"print-size", no, $S}
|
||||
, {"reverse-sort", no, $r}
|
||||
, {"radix", required, $t}
|
||||
, {"undefined-only", no, $u}
|
||||
, {"version", no, $V}
|
||||
]) of
|
||||
{ok, {Options, Files}} ->
|
||||
nm(scan_options(Options), Files);
|
||||
{error, ErrMsg} ->
|
||||
escript_runtime:errmsg("~s\n", [ErrMsg]),
|
||||
usage()
|
||||
end.
|
||||
|
||||
usage() ->
|
||||
escript_runtime:fmterr(
|
||||
"Usage: ~s <options> <files..>\n",
|
||||
[escript_runtime:progname()]),
|
||||
halt(1).
|
||||
|
||||
scan_options(Options) ->
|
||||
Opts = #options{}, % defaults from the record declaration
|
||||
lists:foldl(fun scan_option/2, Opts, Options).
|
||||
|
||||
scan_option(no_demangle, Opts) -> % --no-demangle, NYI
|
||||
Opts;
|
||||
scan_option(special_syms, Opts) -> % --special-syms, NYI
|
||||
Opts;
|
||||
scan_option(defined_only, Opts) ->
|
||||
Opts#options{defined_only = true};
|
||||
scan_option($A, Opts) -> % -A, --print-file-name
|
||||
Opts#options{print_file_name = true};
|
||||
scan_option($o, Opts) -> % -o
|
||||
Opts#options{print_file_name = true};
|
||||
scan_option($B, Opts) -> % -B
|
||||
Opts#options{format = $b};
|
||||
scan_option($D, Opts) -> % -D, --dynamic
|
||||
Opts#options{dynamic = true};
|
||||
scan_option({$f, Arg}, Opts) -> % -f <format>, --format=<format>
|
||||
Opts#options{format = parse_format(Arg)};
|
||||
scan_option($g, Opts) -> % -g, --extern-only
|
||||
Opts#options{extern_only = true};
|
||||
scan_option($n, Opts) -> % -n, --numeric-sort
|
||||
Opts#options{numeric_sort = true};
|
||||
scan_option($v, Opts) -> % -v
|
||||
Opts#options{numeric_sort = true};
|
||||
scan_option($p, Opts) -> % -p, --no-sort
|
||||
Opts#options{no_sort = true};
|
||||
scan_option($P, Opts) -> % -P, --portability
|
||||
Opts#options{format = $p};
|
||||
scan_option($S, Opts) -> % -S, --print-size
|
||||
Opts#options{print_size = true};
|
||||
scan_option($r, Opts) -> % -r, --reverse-sort
|
||||
Opts#options{reverse_sort = true};
|
||||
scan_option({$t, Arg}, Opts) -> % -t <radix>, --radix=<radix>
|
||||
Opts#options{radix = parse_radix(Arg)};
|
||||
scan_option($u, Opts) -> % -u, --undefined-only
|
||||
Opts#options{undefined_only = true};
|
||||
scan_option($V, _Opts) -> % -V, --version
|
||||
io:format(standard_io, "pdp10-tools nm version 0.1\n", []),
|
||||
halt(0).
|
||||
|
||||
parse_format(String) ->
|
||||
case String of
|
||||
[C | _] when C =:= $b; C =:= $B -> $b;
|
||||
[C | _] when C =:= $s; C =:= $S -> $s;
|
||||
[C | _] when C =:= $p; C =:= $P -> $p;
|
||||
_ -> escript_runtime:fatal("invalid format '~s'\n", [String])
|
||||
end.
|
||||
|
||||
parse_radix(String) ->
|
||||
case String of
|
||||
[C] when C =:= $d; C =:= $o; C =:= $x -> C;
|
||||
_ -> escript_runtime:fatal("invalid radix '~s'\n", [String])
|
||||
end.
|
||||
|
||||
%% Nm ==========================================================================
|
||||
|
||||
nm(Opts, Files0) ->
|
||||
{Files, PrintFile} =
|
||||
case Files0 of
|
||||
[] -> {["a.out"], false};
|
||||
[_] -> {Files0, false};
|
||||
[_,_|_] -> {Files0, true}
|
||||
end,
|
||||
[nm1(Opts, PrintFile, File) || File <- Files],
|
||||
halt(0).
|
||||
|
||||
nm1(Opts, PrintFile, File) ->
|
||||
case pdp10_stdio:fopen(File, [read]) of
|
||||
{ok, FP} ->
|
||||
nm1(Opts, PrintFile, File, FP),
|
||||
pdp10_stdio:fclose(FP);
|
||||
{error, Reason} ->
|
||||
escript_runtime:fatal("failed to open ~s: ~p\n", [File, Reason])
|
||||
end.
|
||||
|
||||
nm1(Opts, PrintFile, File, FP) ->
|
||||
Ehdr = read_ehdr(File, FP),
|
||||
{ShTab, _ShStrTab} = read_shtab(File, FP, Ehdr),
|
||||
{SymTab, StrTab} = read_symtab(File, FP, ShTab),
|
||||
print_symtab(Opts, PrintFile, File, ShTab, SymTab, StrTab).
|
||||
|
||||
read_ehdr(File, FP) ->
|
||||
case pdp10_elf36_read_ehdr(FP) of
|
||||
{ok, Ehdr} ->
|
||||
Ehdr;
|
||||
{error, Reason} ->
|
||||
escript_runtime:fatal("invalid PDP10 ELF36 file ~s: ~p\n",
|
||||
[File, Reason])
|
||||
end.
|
||||
|
||||
read_shtab(File, FP, Ehdr) ->
|
||||
case read_shtab(FP, Ehdr) of
|
||||
{ok, ShTab, ShStrTab} -> {ShTab, ShStrTab};
|
||||
{error, Reason} ->
|
||||
escript_runtime:fatal("failed to read section header table in ~s: ~p\n",
|
||||
[File, Reason])
|
||||
end.
|
||||
|
||||
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 pdp10_elf36_read_shdr(FP) of
|
||||
{ok, Shdr0} ->
|
||||
ShNum = actual_shnum(ShNum0, Shdr0),
|
||||
case do_read_shtab(FP, ShNum - 1, [Shdr0]) of
|
||||
{ok, ShTab} ->
|
||||
case read_shstrtab(FP, ShTab, ShStrNdx, Shdr0) of
|
||||
{ok, ShStrTab} -> {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.
|
||||
|
||||
do_read_shtab(_FP, 0, Shdrs) -> {ok, lists:reverse(Shdrs)};
|
||||
do_read_shtab(FP, ShNum, Shdrs) when ShNum > 0 ->
|
||||
case pdp10_elf36_read_shdr(FP) of
|
||||
{ok, Shdr} -> do_read_shtab(FP, ShNum - 1, [Shdr | Shdrs]);
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
read_shstrtab(FP, ShTab, ShStrNdx0, Shdr0) ->
|
||||
case ShStrNdx0 of
|
||||
?SHN_UNDEF -> {ok, []};
|
||||
_ ->
|
||||
ShStrNdx =
|
||||
case ShStrNdx0 of
|
||||
?SHN_XINDEX -> Shdr0#elf36_Shdr.sh_link;
|
||||
_ -> ShStrNdx0
|
||||
end,
|
||||
read_strtab(FP, ShTab, ShStrNdx)
|
||||
end.
|
||||
|
||||
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
|
||||
{ok, _StrTab} = Result -> Result;
|
||||
{error, _Reason} = Error -> Error;
|
||||
eof -> {error, eof}
|
||||
end;
|
||||
{error, _Reason} = Error -> Error
|
||||
end;
|
||||
_ ->
|
||||
{error, io_lib:format("invalid type ~p for string table at index ~p",
|
||||
[Type, Index])}
|
||||
end;
|
||||
false ->
|
||||
{error, io_lib:format("invalid string table section index ~p",
|
||||
[Index])}
|
||||
end.
|
||||
|
||||
read_symtab(File, FP, ShTab) ->
|
||||
case read_symtab(FP, ShTab) of
|
||||
{ok, SymTab, StrTab} -> {SymTab, StrTab};
|
||||
{error, Reason} ->
|
||||
escript_runtime:fatal("failed to read symbol table table in ~s: ~p\n",
|
||||
[File, Reason])
|
||||
end.
|
||||
|
||||
read_symtab(FP, ShTab) ->
|
||||
case find_symtab(ShTab) of
|
||||
false -> {ok, [], []};
|
||||
{ok, Shdr} ->
|
||||
#elf36_Shdr{ sh_link = Link
|
||||
, sh_entsize = EntSize
|
||||
, sh_size = Size
|
||||
, sh_offset = Offset
|
||||
} = Shdr,
|
||||
case [] of
|
||||
_ when EntSize =/= ?ELF36_SYM_SIZEOF ->
|
||||
{error, io_lib:format("wrong sh_entsize ~p in symtab section header", [EntSize])};
|
||||
_ when (Size rem ?ELF36_SYM_SIZEOF) =/= 0 ->
|
||||
{error, io_lib:format("wrong sh_size ~p in symtab section header", [Size])};
|
||||
_ ->
|
||||
case read_strtab(FP, ShTab, Link) of
|
||||
{ok, StrTab} ->
|
||||
SymNum = Size div ?ELF36_SYM_SIZEOF,
|
||||
case SymNum of
|
||||
0 -> {ok, [], StrTab};
|
||||
_ ->
|
||||
case pdp10_stdio:fseek(FP, {bof, Offset}) of
|
||||
ok ->
|
||||
case do_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.
|
||||
|
||||
do_read_symtab(_FP, _SymNum = 0, Syms) -> {ok, lists:reverse(Syms)};
|
||||
do_read_symtab(FP, SymNum, Syms) when SymNum > 0 ->
|
||||
case pdp10_elf36_read_sym(FP) of
|
||||
{ok, Sym} -> do_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).
|
||||
|
||||
print_symtab(Opts, PrintFile, File, ShTab, SymTab, StrTab) ->
|
||||
case PrintFile of
|
||||
true -> io:format("\n~s:\n", [File]);
|
||||
false -> ok
|
||||
end,
|
||||
[sym_print(Opts, File, ShTab, Sym)
|
||||
|| Sym <- syms_sort(Opts, syms_assemble(ShTab, SymTab, StrTab))],
|
||||
ok.
|
||||
|
||||
sym_print(Opts, File, ShTab, {Sym, Value, Name}) ->
|
||||
case sym_type_letter(ShTab, Sym) of
|
||||
0 -> ok; % ignored
|
||||
Type ->
|
||||
%% TODO: handle --extern-only, --undefined-only, --defined-only
|
||||
%% TODO: handle --format={bsd,sysv,posix}
|
||||
if Opts#options.print_file_name -> io:format("~s:", [File]);
|
||||
true -> ok
|
||||
end,
|
||||
case Opts#options.radix of
|
||||
$x -> io:format("~*.*.*B", [9, 16, $0, Value]);
|
||||
$d -> io:format("~*.*.*B", [11, 10, $0, Value]);
|
||||
$o -> io:format("~*.*.*B", [12, 8, $0, Value])
|
||||
end,
|
||||
io:format(" ~c ", [Type]),
|
||||
io:format("~s\n", [case Name of [] -> "(empty)"; _ -> Name end])
|
||||
end.
|
||||
|
||||
sym_type_letter(ShTab, Sym) ->
|
||||
#elf36_Sym{st_info = Info, st_shndx = ShNdx} = Sym,
|
||||
Type = ?ELF36_ST_TYPE(Info),
|
||||
Bind = ?ELF36_ST_BIND(Info),
|
||||
case ShNdx of
|
||||
?SHN_ABS ->
|
||||
if Type =:= ?STT_NOTYPE; Type =:= ?STT_OBJECT; Type =:= ?STT_FUNC ->
|
||||
case Bind of
|
||||
?STB_GLOBAL -> $A;
|
||||
?STB_LOCAL -> $a;
|
||||
_ -> 0
|
||||
end;
|
||||
true -> 0
|
||||
end;
|
||||
?SHN_UNDEF ->
|
||||
case Bind of
|
||||
?STB_GLOBAL -> $U;
|
||||
_ -> 0
|
||||
end;
|
||||
?SHN_COMMON ->
|
||||
if Type =:= ?STT_NOTYPE; Type =:= ?STT_OBJECT ->
|
||||
case Bind of
|
||||
?STB_GLOBAL -> $C;
|
||||
?STB_LOCAL -> $c;
|
||||
_ -> 0
|
||||
end;
|
||||
true -> 0
|
||||
end;
|
||||
_ when Type =:= ?STT_GNU_IFUNC -> $i;
|
||||
_ when ShNdx >= length(ShTab) -> 0;
|
||||
_ ->
|
||||
#elf36_Shdr{sh_type = ShType, sh_flags = ShFlags} = lists:nth(ShNdx + 1, ShTab),
|
||||
case ShType of
|
||||
?SHT_NOBITS ->
|
||||
if (ShFlags band (?SHF_ALLOC bor ?SHF_WRITE)) =:= (?SHF_ALLOC bor ?SHF_WRITE) ->
|
||||
case Bind of
|
||||
?STB_GLOBAL -> $B;
|
||||
?STB_LOCAL -> $b;
|
||||
_ -> 0
|
||||
end;
|
||||
true -> 0
|
||||
end;
|
||||
?SHT_PROGBITS ->
|
||||
if (ShFlags band (?SHF_ALLOC bor ?SHF_EXECINSTR)) =:= (?SHF_ALLOC bor ?SHF_EXECINSTR) ->
|
||||
case Bind of
|
||||
?STB_GLOBAL -> $T;
|
||||
?STB_LOCAL -> $t;
|
||||
_ -> 0
|
||||
end;
|
||||
(ShFlags band (?SHF_ALLOC bor ?SHF_WRITE)) =:= (?SHF_ALLOC bor ?SHF_WRITE) ->
|
||||
case Bind of
|
||||
?STB_GLOBAL -> $D;
|
||||
?STB_LOCAL -> $d;
|
||||
_ -> 0
|
||||
end;
|
||||
(ShFlags band ?SHF_ALLOC) =/= 0 ->
|
||||
case Bind of
|
||||
?STB_GLOBAL -> $R;
|
||||
?STB_LOCAL -> $r;
|
||||
_ -> 0
|
||||
end;
|
||||
true -> 0
|
||||
end;
|
||||
_ -> 0
|
||||
end
|
||||
end.
|
||||
|
||||
syms_sort(Opts, Syms) ->
|
||||
case syms_cmpfn(Opts) of
|
||||
[] -> Syms;
|
||||
Compare -> lists:sort(Compare, Syms)
|
||||
end.
|
||||
|
||||
syms_cmpfn(Opts) ->
|
||||
case Opts#options.no_sort of
|
||||
true -> [];
|
||||
false ->
|
||||
Compare =
|
||||
case Opts#options.numeric_sort of
|
||||
true -> fun sym_cmp_value/2;
|
||||
false -> fun sym_cmp_name/2
|
||||
end,
|
||||
case Opts#options.reverse_sort of
|
||||
true -> fun(Sym1, Sym2) -> Compare(Sym2, Sym1) end;
|
||||
false -> Compare
|
||||
end
|
||||
end.
|
||||
|
||||
sym_cmp_value({_, Val1, _}, {_, Val2, _}) -> Val1 =< Val2.
|
||||
|
||||
sym_cmp_name({_, _, Name1}, {_, _, Name2}) -> Name1 =< Name2.
|
||||
|
||||
syms_assemble(ShTab, SymTab, StrTab) ->
|
||||
lists:map(fun(Sym) ->
|
||||
{Sym, sym_value(ShTab, Sym), sym_name(StrTab, Sym)}
|
||||
end, SymTab).
|
||||
|
||||
sym_name(StrTab, #elf36_Sym{st_name = Index}) ->
|
||||
{ok, Name} = get_name(StrTab, Index),
|
||||
Name.
|
||||
|
||||
get_name(StrTab, Index) ->
|
||||
try lists:nthtail(Index, StrTab) of
|
||||
[_|_] = Tail -> get_name2(Tail, [])
|
||||
catch _C:_R ->
|
||||
{error, {bad_strtab_index, Index}}
|
||||
end.
|
||||
|
||||
get_name2([], _Acc) -> {error, strtab_not_nul_terminated};
|
||||
get_name2([0 | _Tail], Acc) -> {ok, lists:reverse(Acc)};
|
||||
get_name2([Ch | Tail], Acc) -> get_name2(Tail, [Ch | Acc]).
|
||||
|
||||
sym_value(ShTab, #elf36_Sym{st_shndx = ShNdx, st_value = Value}) ->
|
||||
ShNum = length(ShTab),
|
||||
Base =
|
||||
case ShNdx > 0 andalso ShNdx < ShNum andalso ShNdx =/= ?SHN_ABS andalso ShNdx =/= ?SHN_COMMON of
|
||||
true ->
|
||||
#elf36_Shdr{ sh_type = Type
|
||||
, sh_flags = Flags
|
||||
, sh_addr = Addr
|
||||
} = lists:nth(ShNdx + 1, ShTab),
|
||||
case Type =:= ?SHT_PROGBITS andalso (Flags band ?SHF_ALLOC) =/= 0 of
|
||||
true -> Addr;
|
||||
false -> 0
|
||||
end;
|
||||
false -> 0
|
||||
end,
|
||||
Base + Value.
|
||||
|
||||
%% ELF36 =======================================================================
|
||||
%% (these bits should go into the pdp10_elf36 library module)
|
||||
|
||||
pdp10_elf36_read_ehdr(FP) ->
|
||||
case pdp10_elf36_read_record(FP, pdp10_elf36_ehdr_desc()) of
|
||||
{ok, Ehdr} ->
|
||||
case pdp10_elf36_check_ehdr(Ehdr) of
|
||||
ok -> {ok, Ehdr};
|
||||
{error, _Reason} = Error -> Error
|
||||
end;
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
pdp10_elf36_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
|
||||
]).
|
||||
|
||||
pdp10_elf36_read_shdr(FP) ->
|
||||
pdp10_elf36_read_record(FP, pdp10_elf36_shdr_desc()).
|
||||
|
||||
pdp10_elf36_read_sym(FP) ->
|
||||
pdp10_elf36_read_record(FP, pdp10_elf36_sym_desc()).
|
||||
|
||||
check(_X, []) -> ok;
|
||||
check(X, [Fun | Funs]) ->
|
||||
case Fun(X) of
|
||||
ok -> check(X, Funs);
|
||||
{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, io_lib:format("wrong ei_mag0 ~p", [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, io_lib:format("wrong ei_mag1 ~p", [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, io_lib:format("wrong ei_mag2 ~p", [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, io_lib:format("wrong ei_mag3 ~p", [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, io_lib:format("wrong ei_class ~p", [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, io_lib:format("wrong ei_data ~p", [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, io_lib:format("wrong ei_version ~p", [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, io_lib:format("wrong ei_osabi ~p", [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, io_lib:format("wrong ei_abiversion ~p", [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, io_lib:format("wrong e_type ~p", [Type])}
|
||||
end.
|
||||
|
||||
check_ehdr_e_machine(Ehdr) ->
|
||||
#elf36_Ehdr{ e_machine = Machine } = Ehdr,
|
||||
case Machine of
|
||||
?EM_PDP10 -> ok;
|
||||
_ -> {error, io_lib:format("wrong e_machine ~p", [Machine])}
|
||||
end.
|
||||
|
||||
check_ehdr_e_version(Ehdr) ->
|
||||
#elf36_Ehdr{ e_version = Version } = Ehdr,
|
||||
case Version of
|
||||
?EV_CURRENT -> ok;
|
||||
_ -> {error, io_lib:format("wrong e_version ~p", [Version])}
|
||||
end.
|
||||
|
||||
check_ehdr_e_ehsize(Ehdr) ->
|
||||
#elf36_Ehdr{ e_ehsize = EhSize } = Ehdr,
|
||||
case EhSize of
|
||||
?ELF36_EHDR_SIZEOF -> ok;
|
||||
_ -> {error, io_lib:format("wrong e_ehsize ~p", [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, io_lib:format("wrong e_shentsize ~p", [ShEntSize])}
|
||||
end.
|
||||
|
||||
%% Input of records ============================================================
|
||||
|
||||
-type reader() :: fun((pdp10_stdio:file()) -> {ok, integer()} | {error, any()}).
|
||||
|
||||
-record(record_desc, {
|
||||
tag :: atom()
|
||||
, fields :: [reader()]
|
||||
}).
|
||||
|
||||
pdp10_elf36_read_record(FP, #record_desc{tag = Tag, fields = FieldReaders}) ->
|
||||
pdp10_elf36_read_record(FP, FieldReaders, [Tag]).
|
||||
|
||||
pdp10_elf36_read_record(_FP, [], Values) ->
|
||||
{ok, list_to_tuple(lists:reverse(Values))};
|
||||
pdp10_elf36_read_record(FP, [FieldReader | FieldReaders], Values) ->
|
||||
case FieldReader(FP) of
|
||||
{ok, Value} ->
|
||||
pdp10_elf36_read_record(FP, FieldReaders, [Value | Values]);
|
||||
{error, _Reason} = Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
pdp10_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) ->
|
||||
pdp10_elf36_read(FP, ?EI_NIDENT, fun(L) -> L end).
|
||||
|
||||
pdp10_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
|
||||
]
|
||||
}.
|
||||
|
||||
pdp10_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
|
||||
]
|
||||
}.
|
||||
|
||||
%% Input of scalar items =======================================================
|
||||
|
||||
read_Uchar(FP) -> pdp10_elf36_read_uint9(FP).
|
||||
read_Half(FP) -> pdp10_elf36_read_uint18(FP).
|
||||
read_Word(FP) -> pdp10_elf36_read_uint36(FP).
|
||||
read_Addr(FP) -> pdp10_elf36_read_uint36(FP).
|
||||
read_Off(FP) -> pdp10_elf36_read_uint36(FP).
|
||||
|
||||
pdp10_elf36_read_uint9(FP) ->
|
||||
case pdp10_stdio:fgetc(FP) of
|
||||
{ok, _Nonet} = Res -> Res;
|
||||
{error, _Reason} = Res -> Res;
|
||||
eof -> {error, eof}
|
||||
end.
|
||||
|
||||
pdp10_elf36_read_uint18(FP) ->
|
||||
pdp10_elf36_read(FP, 2, fun make_uint18be/1).
|
||||
|
||||
pdp10_elf36_read_uint36(FP) ->
|
||||
pdp10_elf36_read(FP, 4, fun make_uint36be/1).
|
||||
|
||||
pdp10_elf36_read(FP, N, ConvFun) when N >= 0 ->
|
||||
pdp10_elf36_read(FP, N, ConvFun, []).
|
||||
|
||||
pdp10_elf36_read(_FP, 0, ConvFun, Acc) ->
|
||||
{ok, ConvFun(lists:reverse(Acc))};
|
||||
pdp10_elf36_read(FP, N, ConvFun, Acc) ->
|
||||
case pdp10_elf36_read_uint9(FP) of
|
||||
{ok, Nonet} ->
|
||||
pdp10_elf36_read(FP, N - 1, ConvFun, [Nonet | Acc]);
|
||||
{error, _Reason} = Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
make_uint18be([B1, B2]) -> % big-endian conversion
|
||||
((B1 band 16#1FF) bsl 9) bor (B2 band 16#1FF).
|
||||
|
||||
make_uint36be([B1, B2, B3, B4]) -> % big-endian conversion
|
||||
((B1 band 16#1FF) bsl 27) bor
|
||||
((B2 band 16#1FF) bsl 19) bor
|
||||
((B3 band 16#1FF) bsl 9) bor
|
||||
(B4 band 16#1FF).
|
||||
@ -1,6 +1,6 @@
|
||||
%% -*- erlang -*-
|
||||
%% rebar.config for pdp10-tools
|
||||
%% Copyright (C) 2018 Mikael Pettersson
|
||||
%% Copyright (C) 2018-2019 Mikael Pettersson
|
||||
%%
|
||||
%% This file is part of pdp10-tools.
|
||||
%%
|
||||
@ -34,5 +34,6 @@
|
||||
{profiles,
|
||||
[
|
||||
{'8to9', [{escript_main_app, '8to9'}]}
|
||||
, {nm, [{escript_main_app, nm}]}
|
||||
, {od, [{escript_main_app, od}]}
|
||||
]}.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user