diff --git a/erlang/Makefile b/erlang/Makefile index c4dfab2..5354d9e 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 ar as nm od +PROGRAMS=8to9 ar as nm od readelf default: compile link diff --git a/erlang/apps/readelf/src/readelf.app.src b/erlang/apps/readelf/src/readelf.app.src new file mode 100644 index 0000000..fa2284c --- /dev/null +++ b/erlang/apps/readelf/src/readelf.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, readelf, + [{description, "'readelf' clone for pdp10-elf"}, + {vsn, "0.1.0"}, + {registered, []}, + {applications, [kernel, stdlib, lib]}, + {env, []}, + {modules, []} + ]}. diff --git a/erlang/apps/readelf/src/readelf.erl b/erlang/apps/readelf/src/readelf.erl new file mode 100644 index 0000000..4fa11cb --- /dev/null +++ b/erlang/apps/readelf/src/readelf.erl @@ -0,0 +1,598 @@ +%%% -*- erlang-indent-level: 2 -*- +%%% +%%% 'readelf' 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 . + +-module(readelf). +-export([main/1]). + +-include_lib("lib/include/pdp10_elf36.hrl"). +-include_lib("lib/include/pdp10_opcodes.hrl"). + +-record(options, + { file_header = false + , segments = false + , sections = false + , section_groups = false + , section_details = false + , symbols = false + , dyn_syms = false + , notes = false + , relocs = false + , unwind = false + , dynamic = false + , version_info = false + , arch_specific = false + , use_dynamic = false + , archive_index = false + , histogram = false + , disassemble = false % extension + }). + +%% Command-line interface ====================================================== + +main(Argv) -> + escript_runtime:start(fun main_/1, Argv). + +main_(Argv) -> + case getopt:parse(Argv, "ahlSgtesnrudVADcIvW", + [ + %% long-only options + { "dyn-syms", no, dyn_syms } + , { "disassemble", no, disassemble } % extension + %% long aliases for short options + , { "all", no, $a } + , { "file-header", no, $h } + , { "program-headers", no, $l } + , { "segments", no, $l } + , { "section-groups", no, $g } + , { "section-details", no, $t } + , { "headers", no, $e } + , { "symbols", no, $s } + , { "syms", no, $s } + , { "notes", no, $n } + , { "relocs", no, $r } + , { "unwind", no, $u } + , { "version-info", no, $V } + , { "arch-specific", no, $A } + , { "use-dynamic", no, $D } + , { "archive-index", no, $c } + , { "histogram", no, $I } + , { "version", no, $v } + , { "wide", no, $W } + %% --{hex,string,relocated}-dump: NYI + %% --debug-dump: NYI + %% --dwarf-{depth,start}: NYI + %% --decompress: NYI + %% --help: NYI + %% @file: NYI + ]) of + {ok, {Options, Files}} -> + Opts = scan_options(Options), + case Files of + [] -> usage(); + _ -> + case readelf(Opts, Files) of + ok -> halt(0); + {error, Reason} -> + escript_runtime:fatal("~s\n", [error:format(Reason)]) + end + end; + {error, Reason} -> + escript_runtime:errmsg("~s\n", [error:format(Reason)]), + usage() + end. + +usage() -> + escript_runtime:fmterr( + "Usage: ~s -[ahlSgtesnrudVADcIvW] elffile..\n", + [escript_runtime:progname()]), + halt(1). + +scan_options(Options) -> + lists:foldl(fun scan_option/2, #options{}, Options). + +scan_option(Option, Opts) -> + case Option of + $a -> + Opts#options{ file_header = true + , segments = true + , sections = true + , symbols = true + , relocs = true + , dynamic = true + , notes = true + , version_info = true + , arch_specific = true + , unwind = true + , section_groups = true + , histogram = true }; + $h -> + Opts#options{ file_header = true }; + $l -> + Opts#options{ segments = true }; + $S -> + Opts#options{ sections = true }; + $g -> + Opts#options{ section_groups = true }; + $t -> + Opts#options{ section_details = true + , sections = true }; + $s -> + Opts#options{ symbols = true }; + dyn_syms -> + Opts#options{ dyn_syms = true }; + $e -> + Opts#options{ file_header = true + , segments = true + , sections = true }; + $n -> + Opts#options{ notes = true }; + $r -> + Opts#options{ relocs = true }; + $u -> + Opts#options{ unwind = true }; + $d -> + Opts#options{ dynamic = true }; + $V -> + Opts#options{ version_info = true }; + $A -> + Opts#options{ arch_specific = true }; + $D -> + Opts#options{ use_dynamic = true }; + $c -> + Opts#options{ archive_index = true }; + $I -> + Opts#options{ histogram = true }; + $v -> + version(); + $W -> + Opts; % --wide is default in this readelf + disassemble -> % extension + Opts#options{ disassemble = true } + end. + +version() -> + io:format(standard_io, "pdp10-tools readelf version 0.1\n", []), + halt(0). + +%% readelf ===================================================================== + +readelf(_Opts, []) -> ok; +readelf(Opts, [File | Files]) -> + case readelf_file(Opts, File) of + ok -> readelf(Opts, Files); + {error, _Reason} = Error -> Error + end. + +readelf_file(Opts, File) -> + case pdp10_stdio:fopen(File, [raw, read]) of + {ok, FP} -> + try readelf_ehdr(Opts, FP) + after pdp10_stdio:fclose(FP) end; + {error, _Reason} = Error -> Error + end. + +readelf_ehdr(Opts, FP) -> + case pdp10_elf36:read_Ehdr(FP) of + {ok, Ehdr} -> + case print_ehdr(Opts, Ehdr) of + ok -> readelf_shtab(Opts, FP, Ehdr); + {error, _Reason} = Error -> Error + end; + {error, _Reason} = Error -> Error + end. + +readelf_shtab(Opts, FP, Ehdr) -> + case pdp10_elf36:read_ShTab(FP, Ehdr) of + {ok, ShTab} -> + case print_shtab(Opts, ShTab) of + ok -> readelf_symtab(Opts, FP, ShTab); + {error, _Reason} = Error -> Error + end; + {error, _Reason} = Error -> Error + end. + +readelf_symtab(Opts, FP, ShTab) -> + case pdp10_elf36:read_SymTab(FP, ShTab) of + {ok, {SymTab, ShNdx}} -> + case print_symtab(Opts, SymTab, ShNdx, ShTab) of + ok -> disassemble(Opts, FP, ShTab, SymTab); + {error, _Reason} = Error -> Error + end; + {error, _Reason} = Error -> Error + end. + +%% print_ehdr ================================================================== + +print_ehdr(#options{file_header = false}, _Ehdr) -> ok; +print_ehdr(_Opts, Ehdr = #elf36_Ehdr{}) -> + EIdent = Ehdr#elf36_Ehdr.e_ident, + io:format("ELF Header:\n"), + print_e_ident(EIdent), + print_ei_class(lists:nth(1 + ?EI_CLASS, EIdent)), + print_ei_data(lists:nth(1 + ?EI_DATA, EIdent)), + print_ei_version(lists:nth(1 + ?EI_VERSION, EIdent)), + print_ei_osabi(lists:nth(1 + ?EI_OSABI, EIdent)), + print_ei_abiversion(lists:nth(1 + ?EI_ABIVERSION, EIdent)), + print_e_type(Ehdr#elf36_Ehdr.e_type), + print_e_machine(Ehdr#elf36_Ehdr.e_machine), + print_e_version(Ehdr#elf36_Ehdr.e_version), + print_e_entry(Ehdr#elf36_Ehdr.e_entry), + print_e_phoff(Ehdr#elf36_Ehdr.e_phoff), + print_e_shoff(Ehdr#elf36_Ehdr.e_shoff), + print_e_flags(Ehdr#elf36_Ehdr.e_flags), + print_e_ehsize(Ehdr#elf36_Ehdr.e_ehsize), + print_e_phentsize(Ehdr#elf36_Ehdr.e_phentsize), + print_e_phnum(Ehdr#elf36_Ehdr.e_phnum), + print_e_shentsize(Ehdr#elf36_Ehdr.e_shentsize), + print_e_shnum(Ehdr#elf36_Ehdr.e_shnum), + print_e_shstrndx(Ehdr#elf36_Ehdr.e_shstrndx), + io:format("\n"). + +print_e_ident(EIdent) -> + io:format(" Magic:\t\t\t\t"), + print_e_ident("", EIdent), + io:format("\n"). + +print_e_ident(_Pfx, []) -> ok; +print_e_ident(Pfx, [Byte | Bytes]) -> + io:format("~s", [Pfx]), + %% bytes in [256,512[ are unlikely, but handle them correctly anyway + if Byte > 255 -> io:format("~3.16.0b", [Byte]); + true -> io:format("~2.16.0b", [Byte]) + end, + print_e_ident(" ", Bytes). + +print_ei_class(EIClass) -> + io:format(" Class:\t\t\t\t~.10b (~s)\n", + [EIClass, + case EIClass of + ?ELFCLASS32 -> "ELF32"; + ?ELFCLASS64 -> "ELF64"; + ?ELFCLASS36 -> "ELF36"; + _ -> "?" + end]). + +print_ei_data(EIData) -> + io:format(" Data:\t\t\t\t\t~.10b (~s)\n", + [EIData, + case EIData of + ?ELFDATA2LSB -> "2's complement, little endian"; + ?ELFDATA2MSB -> "2's complement, big endian"; + _ -> "?" + end]). + +print_ei_version(EIVersion) -> + io:format(" Version:\t\t\t\t~.10b (~s)\n", + [EIVersion, version_string(EIVersion)]). + +print_ei_osabi(EIOSABI) -> + io:format(" OS/ABI:\t\t\t\t~.10b (~s)\n", + [EIOSABI, + case EIOSABI of + ?ELFOSABI_NONE -> "Generic"; + ?ELFOSABI_LINUX -> "Linux"; + _ -> "?" + end]). + +print_ei_abiversion(EIABIVersion) -> + io:format(" ABI Version:\t\t\t\t~.10b\n", [EIABIVersion]). + +print_e_type(EType) -> + io:format(" Type:\t\t\t\t\t~.10b (~s)\n", + [EType, + case EType of + ?ET_REL -> "Relocatable file"; + ?ET_EXEC -> "Executable file"; + ?ET_DYN -> "Shared object file"; + ?ET_CORE -> "Core file"; + _ -> "?" + end]). + +print_e_machine(EMachine) -> + io:format(" Machine:\t\t\t\t~.10b (~s)\n", + [EMachine, + case EMachine of + ?EM_PDP10 -> "Digital Equipment Corp. PDP-10"; + _ -> "?" + end]). + +print_e_version(EVersion) -> + io:format(" Version:\t\t\t\t~.10b (~s)\n", + [EVersion, version_string(EVersion)]). + +version_string(?EV_CURRENT) -> "current"; +version_string(_) -> "?". + +print_e_entry(EEntry) -> + io:format(" Entry point address:\t\t\t0x~.16b\n", [EEntry]). + +print_e_phoff(EPhOff) -> + io:format(" Start of program headers:\t\t~.10b\n", [EPhOff]). + +print_e_shoff(EShOff) -> + io:format(" Start of section headers:\t\t~.10b\n", [EShOff]). + +print_e_flags(EFLags) -> + io:format(" Flags:\t\t\t\t0x~.16b\n", [EFLags]). + +print_e_ehsize(EEhSize) -> + io:format(" Size of this header:\t\t\t~.10b\n", [EEhSize]). + +print_e_phentsize(EPhEntSize) -> + io:format(" Size of program headers:\t\t~.10b\n", [EPhEntSize]). + +print_e_phnum(EPhNum) -> + io:format(" Number of program headers:\t\t~.10b\n", [EPhNum]). + +print_e_shentsize(EShEntSize) -> + io:format(" Size of section headers:\t\t~.10b\n", [EShEntSize]). + +print_e_shnum(EShNum) -> + io:format(" Number of section headers:\t\t~.10b\n", [EShNum]). + +print_e_shstrndx(EShStrNdx) -> + io:format(" Section header string table index:\t~.10b\n", [EShStrNdx]). + +%% print_shtab ================================================================= + +print_shtab(#options{sections = false}, _ShTab) -> ok; +print_shtab(_Opts, ShTab) -> + io:format("Section Headers:\n"), + io:format(" [Nr] Name Type Addr Off Size ES Flg Lk Inf Al\n"), + print_shdrs(ShTab, 0). + +print_shdrs([Shdr | Shdrs], I) -> + io:format(" [~.10b] ~s ~s 0x~.16b ~.10b ~.10b ~.10b ~s ~.10b ~.10b ~.10b\n", + [ I + , sh_name(Shdr) + , sh_type(Shdr) + , Shdr#elf36_Shdr.sh_addr + , Shdr#elf36_Shdr.sh_offset + , Shdr#elf36_Shdr.sh_size + , Shdr#elf36_Shdr.sh_entsize + , sh_flags(Shdr) + , Shdr#elf36_Shdr.sh_link + , Shdr#elf36_Shdr.sh_info + , Shdr#elf36_Shdr.sh_addralign + ]), + print_shdrs(Shdrs, I + 1); +print_shdrs([], _I) -> + io:format("Key to Flags:\n"), + io:format(" W (write), A (alloc), X (execute), M (merge), S (strings), Z (compressed)\n"), + io:format(" I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)\n"), + io:format(" O (extra OS processing required), o (OS specific), p (processor specific)\n"), + io:format("\n"). + +sh_name(#elf36_Shdr{sh_name = ShName}) -> + case ShName of + "" -> "(empty)"; + _ -> ShName + end. + +sh_type(#elf36_Shdr{sh_type = ShType}) -> + case ShType of + ?SHT_NULL -> "NULL"; + ?SHT_PROGBITS -> "PROGBITS"; + ?SHT_SYMTAB -> "SYMTAB"; + ?SHT_STRTAB -> "STRTAB"; + ?SHT_RELA -> "RELA"; + ?SHT_HASH -> "HASH"; + ?SHT_DYNAMIC -> "DYNAMIC"; + ?SHT_NOTE -> "NOTE"; + ?SHT_NOBITS -> "NOBITS"; + ?SHT_REL -> "REL"; + ?SHT_SHLIB -> "SHLIB"; + ?SHT_DYNSYM -> "DYNSYM"; + ?SHT_INIT_ARRAY -> "INIT_ARRAY"; + ?SHT_FINI_ARRAY -> "FINI_ARRAY"; + ?SHT_PREINIT_ARRAY -> "PREINIT_ARRAY"; + ?SHT_GROUP -> "GROUP"; + ?SHT_SYMTAB_SHNDX -> "SYMTAB_SHNDX"; + ?SHT_GNU_INCREMENTAL_INPUTS -> "GNU_INCREMENTAL_INPUTS"; + ?SHT_GNU_ATTRIBUTES -> "GNU_ATTRIBUTES"; + ?SHT_GNU_HASH -> "GNU_HASH"; + ?SHT_GNU_LIBLIST -> "GNU_LIBLIST"; + ?SHT_GNU_verdef -> "GNU_verdef"; + ?SHT_GNU_verneed -> "GNU_verneed"; + ?SHT_GNU_versym -> "GNU_versym"; + _ -> io_lib:format("~.10b", [ShType]) + end. + +sh_flags(#elf36_Shdr{sh_flags = ShFlags}) -> + sh_flags("WAXxMSILOGTZ", 0, ShFlags, 0, []). + +sh_flags([C | Flags], I, ShFlags, Mask, Acc) -> + {NewMask, NewAcc} = + case I =/= 3 andalso (ShFlags band (1 bsl I)) =/= 0 of + true -> {Mask bor (1 bsl I), [C | Acc]}; + false -> {Mask, Acc} + end, + sh_flags(Flags, I + 1, ShFlags, NewMask, NewAcc); +sh_flags([], _I, ShFlags, Mask, Acc) -> + {NewMask, NewAcc} = + case ShFlags band ?SHF_EXCLUDE =/= 0 of + true -> {Mask bor ?SHF_EXCLUDE, [$E | Acc]}; + false -> {Mask, Acc} + end, + case ShFlags of + 0 -> "-"; + NewMask -> lists:reverse(NewAcc); + _ -> io_lib:format("0x~.16b", [ShFlags]) + end. + +%% print_symtab ================================================================ + +print_symtab(#options{symbols = false}, _SymTab, _ShNdx, _ShTab) -> ok; +print_symtab(_Opts, SymTab, ShNdx, ShTab) -> + Shdr = lists:nth(1 + ShNdx, ShTab), + io:format("Symbol table '~s' in section ~.10b contains ~.10b entries:\n", + [Shdr#elf36_Shdr.sh_name, ShNdx, length(SymTab)]), + io:format(" Num Value Size Type Bind Vis Ndx Name\n"), + print_syms(SymTab, 0). + +print_syms([Sym | Syms], I) -> + io:format(" ~.10b 0x~.16b ~.10b ~s ~s ~s ~.10b ~s\n", + [ I + , Sym#elf36_Sym.st_value + , Sym#elf36_Sym.st_size + , st_type(Sym) + , st_bind(Sym) + , st_vis(Sym) + , Sym#elf36_Sym.st_shndx + , st_name(Sym) + ]), + print_syms(Syms, I + 1); +print_syms([], _I) -> + io:format("\n"). + +st_type(#elf36_Sym{st_info = StInfo}) -> + case ?ELF36_ST_TYPE(StInfo) of + ?STT_NOTYPE -> "NOTYPE"; + ?STT_OBJECT -> "OBJECT"; + ?STT_FUNC -> "FUNC"; + ?STT_SECTION -> "SECTION"; + ?STT_FILE -> "FILE"; + ?STT_COMMON -> "COMMON"; + ?STT_TLS -> "TLS"; + ?STT_RELC -> "RELC"; + ?STT_SRELC -> "SRELC"; + ?STT_GNU_IFUNC -> "GNU_IFUNC"; + StType -> io_lib:format("~.10b", [StType]) + end. + +st_bind(#elf36_Sym{st_info = StInfo}) -> + case ?ELF36_ST_BIND(StInfo) of + ?STB_LOCAL -> "LOCAL"; + ?STB_GLOBAL -> "GLOBAL"; + ?STB_WEAK -> "WEAK"; + ?STB_GNU_UNIQUE -> "GNU_UNIQUE"; + StBind -> io_lib:format("~.10b", [StBind]) + end. + +st_vis(#elf36_Sym{st_other = StOther}) -> + case ?ELF36_ST_VISIBILITY(StOther) of + ?STV_DEFAULT -> "DEFAULT"; + ?STV_INTERNAL -> "INTERNAL"; + ?STV_HIDDEN -> "HIDDEN"; + ?STV_PROTECTED -> "PROTECTED"; + StVis -> io_lib:format("~.10b", [StVis]) + end. + +st_name(#elf36_Sym{st_name = StName}) -> + case StName of + "" -> "(empty)"; + _ -> StName + end. + +%% disassemble ================================================================= + +disassemble(#options{disassemble = false}, _FP, _ShTab, _SymTab) -> ok; +disassemble(_Opts, FP, ShTab, SymTab) -> + disassemble_sections(ShTab, 0, FP, SymTab). + +disassemble_sections([], _ShNdx, _FP, _SymTab) -> ok; +disassemble_sections([Shdr | ShTab], ShNdx, FP, SymTab) -> + case disassemble_section(Shdr, ShNdx, FP, SymTab) of + ok -> disassemble_sections(ShTab, ShNdx + 1, FP, SymTab); + {error, _Reason} = Error -> Error + end. + +disassemble_section(Shdr, ShNdx, FP, SymTab) -> + case Shdr of + #elf36_Shdr{ sh_type = ?SHT_PROGBITS + , sh_flags = ?SHF_ALLOC bor ?SHF_EXECINSTR + , sh_offset = ShOffset + , sh_size = ShSize + } -> + io:format("Disassembly of section nr ~.10b ~s:\n\n", + [ShNdx, sh_name(Shdr)]), + case pdp10_stdio:fseek(FP, {bof, ShOffset}) of + ok -> disassemble_insns(0, ShSize, FP, labels(SymTab, ShNdx)); + {error, _Reason} = Error -> Error + end; + #elf36_Shdr{} -> + ok + end. + +disassemble_insns(Offset, Size, FP, Labels) when Offset < Size -> + case pdp10_elf36:read_uint36(FP) of + {ok, InsnWord} -> + RestLabels = print_labels(Labels, Offset), + io:format(" 0x~.16b:\t0~12.8.0b\t", [Offset, InsnWord]), + disassemble_insn(InsnWord), + disassemble_insns(Offset + 4, Size, FP, RestLabels); + {error, _Reason} = Error -> Error + end; +disassemble_insns(_ShOffset, _ShSize, _FP, _Labels) -> ok. + +disassemble_insn(InsnWord) -> + Models = ?PDP10_KL10_271up, + High13 = InsnWord bsr (36 - 13), + Extended = false, + Section0 = false, + case pdp10_opcodes:insn_from_high13(Models, High13, Extended, Section0) of + #pdp10_insn_desc{name = Name, format = Format} -> + io:format("~s ", [Name]), + case Format of + ?PDP10_INSN_A_OPCODE -> ok; + _ -> io:format("~.10b,", [(InsnWord bsr 23) band 16#F]) + end, + case InsnWord band (1 bsl 22) of + 0 -> ok; + _ -> io:format("@") + end, + io:format("~.10b", [InsnWord band ((1 bsl 18) - 1)]), + case (InsnWord bsr 18) band 16#F of + 0 -> ok; + IxReg -> io:format("(~.10b)", [IxReg]) + end, + io:format("\n"); + false -> + io:format("(bad)\n") + end. + +print_labels([], _Offset) -> []; +print_labels(Labels = [Label | RestLabels], Offset) -> + #elf36_Sym{st_value = StValue} = Label, + if StValue < Offset -> print_labels(RestLabels, Offset); + StValue =:= Offset -> + io:format("0x~9.16.0b <~s>:\n", [Offset, st_name(Label)]), + print_labels(RestLabels, Offset); + true -> Labels + end. + +labels(SymTab, TextShNdx) -> + lists:sort(fun sym_cmp_by_value/2, + lists:filter(fun(Sym) -> sym_is_label(Sym, TextShNdx) end, + SymTab)). + +sym_is_label(Sym, TextShNdx) -> + #elf36_Sym{st_shndx = StShNdx, st_info = StInfo} = Sym, + StShNdx =:= TextShNdx andalso + ?ELF36_ST_TYPE(StInfo) =:= ?STT_FUNC andalso % TODO: type of a label? + (case ?ELF36_ST_BIND(StInfo) of + ?STB_GLOBAL -> true; + ?STB_LOCAL -> true; + ?STB_WEAK -> true; + _ -> false + end). + +sym_cmp_by_value(Sym1, Sym2) -> + Sym1#elf36_Sym.st_value =< Sym2#elf36_Sym.st_value. diff --git a/erlang/rebar.config b/erlang/rebar.config index e48bbdc..b5bdd1a 100644 --- a/erlang/rebar.config +++ b/erlang/rebar.config @@ -32,10 +32,10 @@ {escript_emu_args, "%%! +sbtu +A1 +Bd -noshell -smp auto\n"}. {profiles, - [ - {'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}]} + [ {'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}]} + , {readelf, [{escript_main_app, readelf}]} ]}.