diff --git a/erlang/Makefile b/erlang/Makefile index 9262778..d827816 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 readelf +PROGRAMS=8to9 ar as ld nm od readelf default: compile link diff --git a/erlang/apps/ld/src/ld.app.src b/erlang/apps/ld/src/ld.app.src new file mode 100644 index 0000000..817acbd --- /dev/null +++ b/erlang/apps/ld/src/ld.app.src @@ -0,0 +1,25 @@ +%%% Copyright (C) 2020 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, ld, + [{description, "'ld' clone for pdp10-elf"}, + {vsn, "0.1.0"}, + {registered, []}, + {applications, [kernel, stdlib, lib]}, + {env, []}, + {modules, []} + ]}. diff --git a/erlang/apps/ld/src/ld.erl b/erlang/apps/ld/src/ld.erl new file mode 100644 index 0000000..fc23505 --- /dev/null +++ b/erlang/apps/ld/src/ld.erl @@ -0,0 +1,384 @@ +%%% -*- erlang-indent-level: 2 -*- +%%% +%%% 'ld' clone for pdp10-elf +%%% Copyright (C) 2020 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(ld). +-export([ main/1 + , format_error/1 + ]). + +-define(EMULATION, "elf36_pdp10"). +-define(OBJFORMAT, "elf36-pdp10"). + +-record(options, + { entry = "_start" :: string() + , files = [] :: [{file, string()} | {library, string()}] + , fini = "_fini" :: string() + , init = "_init" :: string() + , library_path = [] :: [string()] + , output = "a.out" :: string() + , page_align_sections = false :: boolean() + , rwtext = false :: boolean() + , section_starts = [] :: [{string(), integer()}] + , trace = 0 :: non_neg_integer() + }). + +%% Command-line interface ====================================================== + +main(Argv) -> + escript_runtime:start(fun main_/1, Argv). + +-spec main_([string()]) -> no_return(). +main_(Argv) -> + case ld(Argv) of + ok -> halt(0); + {error, Reason} -> + escript_runtime:fatal("~s\n", [error:format(Reason)]) + end. + +ld(Argv) -> + %% NYI options: + %% @file + %% -a + %% --audit + %% -c / --mri-script + %% -d / -dc / -dp + %% -P / --depaudit + %% --exclude-libs + %% --exclude-modules-for-imlib + %% -E / --[no-]export-dynamic + %% -EL + %% -f / --auxiliary + %% -F / --filter + %% -g + %% -G / --gpsize + %% -h / -soname + %% -M / --print-map + %% --[no-]print-map-discarded + %% -O + %% -plugin + %% --push-state / --pop-state + %% -q / --emit-relocs + %% --force-dynamic + %% -r / --relocatable / -i + %% -R / --just-symbols + %% -s / --strip-all + %% -S / --strip-debug + %% --[no-]strip-discarded + %% -T / --script + %% -dT / --default-script + %% -u / --undefined + %% --require-defined + %% -Ur + %% --orphan-handling + %% --unique + %% -x / --discard-all + %% -X / --discard-locals + %% -y / --trace-symbol + %% -Y + %% -z + %% -( / -) / --start-group / --end-group + %% --[no-]accept-unknown-input-arch + %% --[no-]as-needed + %% -assert + %% -Bdynamic / -dy / -call_shared + %% -Bgroup + %% -Bstatic / -dn / -non_shared / -static + %% -Bsymbolic + %% -Bsymbolic-functions + %% --dynamic-list + %% --dynamic-list-data + %% --dynamic-list-cpp-new + %% --dynamic-list-cpp-typeinfo + %% --[no-]check-sections + %% --[no-]copy-dt-needed-entries / --[no-]add-needed + %% --cref + %% --no-define-common + %% --force-group-allocation + %% --defsym + %% --[no-]demangle + %% -I / --dynamic-linker + %% --no-dynamic-linker + %% --embedded-relocs + %% --disable-multiple-abs-defs + %% --[no-]fatal-warnings + %% --force-exe-suffix + %% --[no-]gc-sections + %% --[no-]print-gc-sections + %% --gc-keep-exported + %% --print-memory-usage + %% -Map + %% --no-keep-memory + %% --no-undefined / -z defs + %% --allow-multiple-definitions / -z muldefs + %% --[no-]allow-shlib-undefined + %% --no-undefined-version + %% --default-symver + %% --default-imported-symver + %% --no-warn-mismatch + %% --no-warn-search-mismatch + %% --noinhibit-exec + %% -nostdlib + %% --out-implib + %% -pie / --pic-executable + %% -qmagic + %% -Qy + %% --[no-]relax + %% --retain-symbols-file + %% -rpath + %% -rpath-link + %% -shared / -Bshareable + %% --sort-common + %% --sort-section + %% --spare-dynamic-tags + %% --split-by-file + %% --split-by-reloc + %% --stats + %% --sysroot + %% --task-link + %% --traditional-format + %% -Tldata-segment + %% --unresolved-symbols + %% --dll-verbose / --verbose + %% --version-script + %% --warn-common + %% --warn-constructors + %% --warn-multiple-gp + %% --warn-once + %% --warn-section-align + %% --warn-shared-textrel + %% --warn-alternate-em + %% --warn-unresolved-symbols / --error-unresolved-symbols + %% --[no-]whole-archive + %% --wrap + %% --[no-]eh-frame-hdr + %% --no-ld-generated-unwind-info + %% --enable-new-dtags / --disable-new-dtags + %% --hash-size + %% --hash-style + %% --compress-debug-sections + %% --reduce-memory-overheads + %% --build-id + %% All other target-specific options. + %% + %% LD has quite archaic option syntax: + %% - it takes some single-dash long options, e.g. -nostdlib + %% - options and non-options must be processed in the order given, + %% e.g. due to --start-group ... --end-group + case getopt:parse(Argv, "-b:e:l:L:m:nNo:tvV", + [ %% single-dash long options + { "-EB", no, 'EB' } + , { "-fini", required, fini } + , { "-init", required, init } + , { "-Tbss", required, 'Tbss' } + , { "-Tdata", required, 'Tdata' } + , { "-Trodata-segment", required, 'Trodata' } + , { "-Ttext", required, 'Ttext' } + , { "-Ttext-segment", required, 'Ttext' } + %% long-only options + , { "help", no, help } + , { "no-omagic", no, no_omagic } + , { "oformat", required, oformat } + , { "print-output-format", no, print_output_format } + , { "section-start", required, section_start } + , { "target-help", no, target_help } + %% long aliases for short options + , { "entry", required, $e } + , { "format", required, $b } + , { "library", required, $l } + , { "library-path", required, $L } + , { "nmagic", no, $n } + , { "omagic", no, $N } + , { "output", required, $o } + , { "trace", no, $t } + , { "version", no, $v } + ]) of + {ok, {Opts, _NonOpts = []}} -> + case process_options(Opts) of + {ok, Options} -> + %% FIXME: input(Options) -> {ok, _} | {error, _} + {ok, TUnit} = input(Options), + output(Options, TUnit); + {error, _Reason} = Error -> Error + end; + {error, _Reason} = Error -> Error + end. + +process_options(Opts) -> process_options(Opts, #options{}). + +process_options([Opt | Opts], Options) -> + case process_option(Opt, Options) of + {ok, NewOptions} -> process_options(Opts, NewOptions); + {error, _Reason} = Error -> Error + end; +process_options([], Options) -> + #options{ files = Files, library_path = LibraryPath } = Options, + {ok, Options#options{ files = lists:reverse(Files) + , library_path = lists:reverse(LibraryPath) + }}. + +process_option(Opt, Options) -> + case Opt of + 'EB' -> handle_EB(Opt, Options); + {$m, _} -> handle_emulation(Opt, Options); + {$e, _} -> handle_entry(Opt, Options); + {fini, _} -> handle_fini(Opt, Options); + {$b, _} -> handle_format(Opt, Options); + help -> handle_help(Opt, Options); + {init, _} -> handle_init(Opt, Options); + {$l, _} -> handle_library(Opt, Options); + {$L, _} -> handle_library_path(Opt, Options); + $n -> handle_nmagic(Opt, Options); + no_omagic -> handle_no_omagic(Opt, Options); + {oformat, _} -> handle_oformat(Opt, Options); + $N -> handle_omagic(Opt, Options); + {$o, _} -> handle_output(Opt, Options); + print_output_format -> handle_print_output_format(Opt, Options); + {section_start, _} -> handle_section_start(Opt, Options); + target_help -> handle_target_help(Opt, Options); + {'Tbss', _} -> handle_Tbss(Opt, Options); + {'Tdata', _} -> handle_Tdata(Opt, Options); + $t -> handle_trace(Opt, Options); + {'Trodata', _} -> handle_Trodata(Opt, Options); + {'Ttext', _} -> handle_Ttext(Opt, Options); + $V -> handle_V(Opt, Options); + $v -> handle_version(Opt, Options) + end. + +%% Option Handlers ============================================================= + +handle_EB('EB', Options) -> + {ok, Options}. + +handle_emulation({$m, Emulation}, Options) -> + case Emulation of + ?EMULATION -> {ok, Options}; + _ -> {error, {?MODULE, {invalid, emulation, Emulation}}} + end. + +handle_entry({$e, Entry}, Options) -> + {ok, Options#options{entry = Entry}}. + +handle_fini({fini, Fini}, Options) -> + {ok, Options#options{fini = Fini}}. + +handle_format({$b, InputFormat}, Options) -> + case InputFormat of + ?OBJFORMAT -> {ok, Options}; + _ -> {error, {?MODULE, {invalid, "input format", InputFormat}}} + end. + +handle_help(help, _Options) -> + io:format("--help: NYI\n"), + exit(0). + +handle_init({init, Init}, Options) -> + {ok, Options#options{init = Init}}. + +handle_library({$l, Library}, Options) -> + {ok, Options#options{files = [{library, Library} | Options#options.files]}}. + +handle_library_path({$L, Path}, Options) -> + {ok, Options#options{library_path = [Path | Options#options.library_path]}}. + +handle_nmagic($n, Options) -> + {ok, Options#options{page_align_sections = false}}. + +handle_no_omagic(no_omagic, Options) -> + {ok, Options#options{rwtext = false, page_align_sections = true}}. + +handle_oformat({oformat, OutputFormat}, Options) -> + case OutputFormat of + ?OBJFORMAT -> {ok, Options}; + _ -> {error, {?MODULE, {invalid, "output format", OutputFormat}}} + end. + +handle_omagic($N, Options) -> + {ok, Options#options{rwtext = true, page_align_sections = false}}. + +handle_output({$o, Output}, Options) -> + {ok, Options#options{output = Output}}. + +handle_print_output_format(print_output_format, Options) -> + io:format(?OBJFORMAT "\n"), + {ok, Options}. + +handle_section_start({section_start, Arg}, Options) -> + case string:split(Arg, "=") of + [Name, Org] -> section_start(Name, Org, Options); + _ -> {error, {?MODULE, {invalid, "section-start", Arg}}} + end. + +handle_target_help(target_help, _Options) -> + io:format("No target-specific options apply.\n"), + exit(0). + +handle_Tbss({'Tbss', Org}, Options) -> + section_start(".bss", Org, Options). + +handle_Tdata({'Tdata', Org}, Options) -> + section_start(".data", Org, Options). + +handle_trace($t, Options) -> + {ok, Options#options{trace = Options#options.trace + 1}}. + +handle_Trodata({'Trodata', Org}, Options) -> + section_start(".rodata", Org, Options). + +handle_Ttext({'Ttext', Org}, Options) -> + section_start(".text", Org, Options). + +handle_V($V, Options) -> + version(), + io:format(" Supported emulations:\n"), + io:format(" " ?EMULATION "\n"), + {ok, Options}. + +handle_version($v, Options) -> + version(), + {ok, Options}. + +section_start(Name, Org, Options) -> + case strtol:parse(Org, _Base = 16) of + {ok, {Value, _Rest = []}} -> + SectionStarts = Options#options.section_starts, + NewSectionStarts = lists:keystore(Name, 1, SectionStarts, {Name, Value}), + {ok, Options#options{section_starts = NewSectionStarts}}; + _ -> {error, {?MODULE, {invalid, "org for section " ++ Name, Org}}} + end. + +version() -> + io:format("pdp10-tools ld version 0.1\n"). + +%% Input Processing ============================================================ + +input(_Options) -> {ok, []}. % FIXME + +%% Output Generation =========================================================== + +output(_Options, []) -> ok. % FIXME + +%% Error Formatting ============================================================ + +-spec format_error(term()) -> io_lib:chars(). +format_error(Reason) -> + case Reason of + {invalid, What, Parameter} -> + io_lib:format("invalid ~s: ~s", [What, Parameter]) + end. diff --git a/erlang/rebar.config b/erlang/rebar.config index 977449b..eb4c84d 100644 --- a/erlang/rebar.config +++ b/erlang/rebar.config @@ -44,6 +44,7 @@ {'8to9', main, 1} , {ar, main, 1} , {as, main, 1} + , {ld, main, 1} , {nm, main, 1} , {od, main, 1} , {readelf, main, 1} @@ -51,6 +52,7 @@ , {assemble, format_error, 1} , {getopt, format_error, 1} , {input, format_error, 1} + , {ld, format_error, 1} , {output, format_error, 1} , {parse, format_error, 1} , {pdp10_elf36, format_error, 1} @@ -69,6 +71,7 @@ [ {'8to9', [{escript_main_app, '8to9'}]} , {ar, [{escript_main_app, ar}]} , {as, [{escript_main_app, as}]} + , {ld, [{escript_main_app, ld}]} , {nm, [{escript_main_app, nm}]} , {od, [{escript_main_app, od}]} , {readelf, [{escript_main_app, readelf}]}