From 964eed2f316be8a2e63e074c2d8d0b0be1c5defd Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sun, 3 Feb 2019 20:57:00 +0100 Subject: [PATCH] nm: rewrite in Erlang --- erlang/Makefile | 2 +- erlang/apps/nm/src/nm.app.src | 25 ++ erlang/apps/nm/src/nm.erl | 749 ++++++++++++++++++++++++++++++++++ erlang/rebar.config | 3 +- 4 files changed, 777 insertions(+), 2 deletions(-) create mode 100644 erlang/apps/nm/src/nm.app.src create mode 100644 erlang/apps/nm/src/nm.erl diff --git a/erlang/Makefile b/erlang/Makefile index bde3240..76e2d22 100644 --- a/erlang/Makefile +++ b/erlang/Makefile @@ -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 diff --git a/erlang/apps/nm/src/nm.app.src b/erlang/apps/nm/src/nm.app.src new file mode 100644 index 0000000..b4167fd --- /dev/null +++ b/erlang/apps/nm/src/nm.app.src @@ -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 . + +{application, nm, + [{description, "'nm' clone for PDP10 Elf36 files"}, + {vsn, "0.1.0"}, + {registered, []}, + {applications, [kernel, stdlib, lib]}, + {env, []}, + {modules, []} + ]}. diff --git a/erlang/apps/nm/src/nm.erl b/erlang/apps/nm/src/nm.erl new file mode 100644 index 0000000..661eb9b --- /dev/null +++ b/erlang/apps/nm/src/nm.erl @@ -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 . + +-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 + %% -l / --line-numbers + %% --size-sort + %% -s / --print-armap [TODO] + %% --target= + %% -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 \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= + 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= + 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). diff --git a/erlang/rebar.config b/erlang/rebar.config index 5f271ed..4edda75 100644 --- a/erlang/rebar.config +++ b/erlang/rebar.config @@ -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}]} ]}.