mirror of
https://github.com/mikpe/pdp10-tools.git
synced 2026-04-14 23:58:09 +00:00
ld_output: cleanups
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
%%% -*- erlang-indent-level: 2 -*-
|
||||
%%%
|
||||
%%% ELF output for pdp10-elf-ld
|
||||
%%% Copyright (C) 2020 Mikael Pettersson
|
||||
%%% ELF output for pdp10-elf ld
|
||||
%%% Copyright (C) 2020-2023 Mikael Pettersson
|
||||
%%%
|
||||
%%% This file is part of pdp10-tools.
|
||||
%%%
|
||||
@@ -17,6 +17,17 @@
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
|
||||
%%%
|
||||
%%%=============================================================================
|
||||
%%%
|
||||
%%% Generate ELF output for the linked input:
|
||||
%%%
|
||||
%%% - ELF header
|
||||
%%% - Program Header table, one Phdr per segment
|
||||
%%% - Segments
|
||||
%%%
|
||||
%%% Relocations are applied while the image data is copied from input sections
|
||||
%%% to output segments.
|
||||
|
||||
-module(ld_output).
|
||||
|
||||
@@ -56,13 +67,6 @@ output([Fun | Funs], Entry, Segments, GlobalMap, FileMap, FP, Offset) ->
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
resolve(Value, _GlobalMap) when is_integer(Value) -> {ok, Value};
|
||||
resolve(Symbol, GlobalMap) ->
|
||||
case maps:get(Symbol, GlobalMap, false) of
|
||||
Value when is_integer(Value) -> {ok, Value};
|
||||
false -> {error, {?MODULE, {undefined_symbol, Symbol}}}
|
||||
end.
|
||||
|
||||
output_padding(0, _FP) -> ok;
|
||||
output_padding(N, FP) when N > 0 ->
|
||||
case pdp10_stdio:fputc(0, FP) of
|
||||
@@ -114,43 +118,43 @@ output_segments([Segment | Segments], GlobalMap, FileMap, FP, Offset) ->
|
||||
|
||||
output_segment(Segment, GlobalMap, FileMap, FP, Offset) ->
|
||||
#segment{phdr = Phdr, sections = Sections} = Segment,
|
||||
#elf36_Phdr{p_offset = SegOffset, p_vaddr = SegVAddr} = Phdr,
|
||||
#elf36_Phdr{p_offset = SegOffset} = Phdr,
|
||||
case output_padding(SegOffset - Offset, FP) of
|
||||
ok -> output_sections(Sections, GlobalMap, FileMap, FP, SegOffset, SegVAddr);
|
||||
ok -> output_sections(Sections, GlobalMap, FileMap, FP, SegOffset);
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
%% Sections Output -------------------------------------------------------------
|
||||
|
||||
output_sections(Sections, GlobalMap, FileMap, FP, SegOffset, SegVAddr) ->
|
||||
output_sections(Sections, GlobalMap, FileMap, FP, SegOffset, SegVAddr, _Offset = 0).
|
||||
output_sections(Sections, GlobalMap, FileMap, FP, SegOffset) ->
|
||||
output_sections(Sections, GlobalMap, FileMap, FP, SegOffset, _Offset = 0).
|
||||
|
||||
output_sections(_Sections = [], _GlobalMap, _FileMap, _FP, SegOffset, _SegVAddr, Offset) ->
|
||||
output_sections(_Sections = [], _GlobalMap, _FileMap, _FP, SegOffset, Offset) ->
|
||||
{ok, SegOffset + Offset};
|
||||
output_sections([Section | Sections], GlobalMap, FileMap, FP, SegOffset, SegVAddr, Offset) ->
|
||||
case output_section(Section, GlobalMap, FileMap, FP, SegOffset, SegVAddr, Offset) of
|
||||
{ok, NewOffset} -> output_sections(Sections, GlobalMap, FileMap, FP, SegOffset, SegVAddr, NewOffset);
|
||||
output_sections([Section | Sections], GlobalMap, FileMap, FP, SegOffset, Offset) ->
|
||||
case output_section(Section, GlobalMap, FileMap, FP, SegOffset, Offset) of
|
||||
{ok, NewOffset} -> output_sections(Sections, GlobalMap, FileMap, FP, SegOffset, NewOffset);
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
output_section(Section, GlobalMap, FileMap, FP, SegOffset, SegVAddr, Offset) ->
|
||||
output_section(Section, GlobalMap, FileMap, FP, SegOffset, Offset) ->
|
||||
#section{shdr = Shdr, frags = Frags} = Section,
|
||||
#elf36_Shdr{sh_offset = ShOffset} = Shdr,
|
||||
case output_padding(ShOffset - Offset, FP) of
|
||||
ok -> output_frags(Frags, GlobalMap, FileMap, FP, SegOffset + ShOffset, SegVAddr + ShOffset);
|
||||
ok -> output_frags(Frags, GlobalMap, FileMap, FP, SegOffset + ShOffset);
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
%% Fragments Output ------------------------------------------------------------
|
||||
|
||||
output_frags(Frags, GlobalMap, FileMap, FP, SectOffset, SectVAddr) ->
|
||||
output_frags(Frags, GlobalMap, FileMap, FP, SectOffset, SectVAddr, _Offset = 0).
|
||||
output_frags(Frags, GlobalMap, FileMap, FP, SectOffset) ->
|
||||
output_frags(Frags, GlobalMap, FileMap, FP, SectOffset, _Offset = 0).
|
||||
|
||||
output_frags(_Frags = [], _GlobalMap, _FileMap, _FP, SectOffset, _SectVAddr, Offset) ->
|
||||
output_frags(_Frags = [], _GlobalMap, _FileMap, _FP, SectOffset, Offset) ->
|
||||
{ok, SectOffset + Offset};
|
||||
output_frags([Frag | Frags], GlobalMap, FileMap, FP, SectOffset, SectVAddr, Offset) ->
|
||||
case output_frag(Frag, GlobalMap, FileMap, FP, SectOffset, SectVAddr, Offset) of
|
||||
{ok, NewOffset} -> output_frags(Frags, GlobalMap, FileMap, FP, SectOffset, SectVAddr, NewOffset);
|
||||
output_frags([Frag | Frags], GlobalMap, FileMap, FP, SectOffset, Offset) ->
|
||||
case output_frag(Frag, GlobalMap, FileMap, FP, Offset) of
|
||||
{ok, NewOffset} -> output_frags(Frags, GlobalMap, FileMap, FP, SectOffset, NewOffset);
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
@@ -158,156 +162,88 @@ output_frags([Frag | Frags], GlobalMap, FileMap, FP, SectOffset, SectVAddr, Offs
|
||||
%%
|
||||
%% Copy a section from an input file and append it to the output file.
|
||||
%% Simultaneously apply associated relocations.
|
||||
%%
|
||||
%% The implementation is a state machine with two states:
|
||||
%%
|
||||
%% - COPY: FragOffset < RelOffset of next reloc, or there is not next reloc
|
||||
%% input bytes are written to the output FP, FragOffset is incremented
|
||||
%%
|
||||
%% - ACCUM: FragOffset >= RelOffset of next reloc
|
||||
%% input bytes are appended to temporary Buffer, FragOffset is incremented
|
||||
%% if Buffer length equals operand size of next reloc:
|
||||
%% convert Buffer to input word, apply reloc, giving output word
|
||||
%% convert output word to Pushback buffer and push that back to input
|
||||
%% decrement FragOffset by Buffer length, empty Buffer
|
||||
|
||||
output_frag(Frag, GlobalMap, FileMap, FP, _SectOffset, _SectVAddr, Offset) ->
|
||||
output_frag(Frag, GlobalMap, FileMap, FP, Offset) ->
|
||||
case input_init(Frag) of
|
||||
{ok, {Input, Relocs}} ->
|
||||
Output = output_init(Frag, GlobalMap, FileMap, FP, Relocs),
|
||||
case output_frag(Input, Output) of
|
||||
SortedRelocs = lists:keysort(#elf36_Rela.r_offset, Relocs),
|
||||
#sectfrag{file = File, shdr = #elf36_Shdr{sh_name = ShName}} = Frag,
|
||||
case output_frag(Input, File, ShName, FP, SortedRelocs, GlobalMap, FileMap) of
|
||||
{ok, FragOffset} -> {ok, Offset + FragOffset};
|
||||
{error, _Reason} = Error -> Error
|
||||
end;
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
output_frag(Input, Output) ->
|
||||
case input(Input) of
|
||||
{ok, {Byte, NewInput}} ->
|
||||
case output(Output, Byte) of
|
||||
{ok, {Pushback, NewOutput}} ->
|
||||
output_frag(input_pushback(NewInput, Pushback), NewOutput);
|
||||
{ok, NewOutput} -> output_frag(NewInput, NewOutput);
|
||||
{error, _Reason} = Error -> Error
|
||||
end;
|
||||
eof ->
|
||||
output_frag(Input, File, ShName, FP, Relocs, GlobalMap, FileMap) ->
|
||||
case output_frag(Input, Relocs, File, GlobalMap, FileMap, FP, _FragOffset = 0, _Buffer = []) of
|
||||
{ok, {_Relocs = [], FragOffset}} ->
|
||||
input_fini(Input),
|
||||
output_fini(Output);
|
||||
{ok, FragOffset};
|
||||
{ok, {_Relocs, _FragOffset2}} ->
|
||||
{error, {?MODULE, {unresolved_relocs, File, ShName}}};
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
-record(frag_input, {fp, file, sh_name, size, pushback}).
|
||||
|
||||
input_init(Frag) ->
|
||||
#sectfrag{file = File, shdr = Shdr, relocs = RelocShdr} = Frag,
|
||||
case pdp10_stdio:fopen(File, [raw, read]) of
|
||||
{ok, FP} ->
|
||||
case input_relocs(FP, RelocShdr) of
|
||||
{ok, Relocs} ->
|
||||
#elf36_Shdr{sh_offset = ShOffset, sh_size = ShSize, sh_name = ShName} = Shdr,
|
||||
case pdp10_stdio:fseek(FP, {bof, ShOffset}) of
|
||||
ok ->
|
||||
Input = #frag_input{fp = FP, file = File, sh_name = ShName, size = ShSize, pushback = []},
|
||||
{ok, {Input, Relocs}};
|
||||
{error, _Reason} = Error -> Error
|
||||
end;
|
||||
{error, _Reason} = Error -> Error
|
||||
end;
|
||||
{error, Reason} -> {error, {?MODULE, {cannot_open, File, Reason}}}
|
||||
end.
|
||||
|
||||
input_relocs(_FP, false) -> {ok, []};
|
||||
input_relocs(FP, Shdr) -> pdp10_elf36:read_RelaTab(FP, Shdr).
|
||||
|
||||
input_fini(#frag_input{fp = FP}) ->
|
||||
pdp10_stdio:fclose(FP).
|
||||
|
||||
input_pushback(#frag_input{pushback = Pushback} = Input, Buffer) ->
|
||||
Input#frag_input{pushback = Buffer ++ Pushback}.
|
||||
|
||||
input(#frag_input{pushback = [Byte | Pushback]} = Input) ->
|
||||
{ok, {Byte, Input#frag_input{pushback = Pushback}}};
|
||||
input(#frag_input{size = 0}) -> eof;
|
||||
input(#frag_input{fp = FP, size = Size} = Input) when Size > 0 ->
|
||||
case pdp10_stdio:fgetc(FP) of
|
||||
{ok, Byte} -> {ok, {Byte, Input#frag_input{size = Size - 1}}};
|
||||
eof ->
|
||||
#frag_input{file = File, sh_name = ShName} = Input,
|
||||
{error, {?MODULE, {premature_eof, File, ShName}}};
|
||||
output_frag(Input, Relocs, File, GlobalMap, FileMap, FP, FragOffset, Buffer) ->
|
||||
case input(Input) of
|
||||
{ok, {Byte, NewInput}} -> output_frag(Byte, NewInput, Relocs, File, GlobalMap, FileMap, FP, FragOffset, Buffer);
|
||||
eof -> {ok, {Relocs, FragOffset}};
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
-record(frag_output,
|
||||
{ file
|
||||
, sh_name
|
||||
, globalmap
|
||||
, filemap
|
||||
, fp
|
||||
, relocs
|
||||
, frag_offset
|
||||
, buffer
|
||||
}).
|
||||
|
||||
output_init(Frag, GlobalMap, FileMap, FP, Relocs) ->
|
||||
#sectfrag{file = File, shdr = #elf36_Shdr{sh_name = ShName}} = Frag,
|
||||
SortedRelocs = lists:keysort(#elf36_Rela.r_offset, Relocs),
|
||||
#frag_output{file = File, sh_name = ShName, globalmap = GlobalMap, filemap = FileMap,
|
||||
fp = FP, relocs = SortedRelocs, frag_offset = 0, buffer = []}.
|
||||
|
||||
output_fini(#frag_output{relocs = [], frag_offset = FragOffset}) -> {ok, FragOffset};
|
||||
output_fini(#frag_output{file = File, sh_name = ShName}) ->
|
||||
{error, {?MODULE, {unresolved_relocs, File, ShName}}}.
|
||||
|
||||
output(Output, Byte) ->
|
||||
case Output#frag_output.relocs of
|
||||
[] -> output_directly(Output, Byte);
|
||||
[#elf36_Rela{r_offset = RelOffset} = Rela | _] ->
|
||||
FragOffset = Output#frag_output.frag_offset,
|
||||
output_frag(Byte, Input, Relocs, File, GlobalMap, FileMap, FP, FragOffset, Buffer) ->
|
||||
case Relocs of
|
||||
[] -> output_byte(Byte, Input, Relocs, File, GlobalMap, FileMap, FP, FragOffset);
|
||||
[#elf36_Rela{r_offset = RelOffset} = Reloc | NewRelocs] ->
|
||||
case FragOffset < RelOffset of
|
||||
true -> output_directly(Output, Byte);
|
||||
true -> output_byte(Byte, Input, Relocs, File, GlobalMap, FileMap, FP, FragOffset);
|
||||
false ->
|
||||
NewBuffer = [Byte | Output#frag_output.buffer],
|
||||
NewBuffer = [Byte | Buffer],
|
||||
NewFragOffset = FragOffset + 1,
|
||||
NewOutput = Output#frag_output{frag_offset = NewFragOffset, buffer = NewBuffer},
|
||||
case length(NewBuffer) =:= sizeof_reloc(Rela) of
|
||||
true -> output_reloc(NewOutput);
|
||||
false -> {ok, NewOutput}
|
||||
Length = length(NewBuffer),
|
||||
case Length =:= sizeof_reloc(Reloc) of
|
||||
true ->
|
||||
Word = buffer_to_word(Length, lists:reverse(NewBuffer)),
|
||||
case apply_reloc(Reloc, File, GlobalMap, FileMap, Word) of
|
||||
{ok, NewWord} ->
|
||||
Pushback = word_to_buffer(Length, NewWord),
|
||||
NewInput = input_pushback(Input, Pushback),
|
||||
output_frag(NewInput, NewRelocs, File, GlobalMap, FileMap, FP, NewFragOffset - Length, _Buffer = []);
|
||||
{error, _Reason} = Error -> Error
|
||||
end;
|
||||
false -> output_frag(Input, Relocs, File, GlobalMap, FileMap, FP, NewFragOffset, NewBuffer)
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
output_reloc(Output) ->
|
||||
#frag_output{ file = File
|
||||
, filemap = FileMap
|
||||
, globalmap = GlobalMap
|
||||
, relocs = [Reloc | Relocs]
|
||||
, buffer = Buffer0
|
||||
, frag_offset = FragOffset
|
||||
} = Output,
|
||||
#elf36_Rela{ r_info = Info
|
||||
, r_addend = Addend
|
||||
} = Reloc,
|
||||
Type = ?ELF36_R_TYPE(Info),
|
||||
SymNdx = ?ELF36_R_SYM(Info),
|
||||
case symbol_value(SymNdx, File, FileMap, GlobalMap) of
|
||||
{ok, SymbolValue} ->
|
||||
Buffer1 = lists:reverse(Buffer0),
|
||||
Length = length(Buffer1),
|
||||
Word = buffer_word(Length, Buffer1),
|
||||
%% TODO: handle {error,_} returns
|
||||
{ok, NewWord} = apply_reloc(Type, SymbolValue + Addend, Word),
|
||||
Pushback = word_buffer(Length, NewWord),
|
||||
{ok, {Pushback, Output#frag_output{relocs = Relocs, buffer = [], frag_offset = FragOffset - Length}}};
|
||||
output_byte(Byte, Input, Relocs, File, GlobalMap, FileMap, FP, FragOffset) ->
|
||||
case pdp10_stdio:fputc(Byte, FP) of
|
||||
ok -> output_frag(Input, Relocs, File, GlobalMap, FileMap, FP, FragOffset + 1, _Buffer = []);
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
symbol_value(SymNdx, File, FileMap, GlobalMap) ->
|
||||
LocalMap = maps:get(File, FileMap),
|
||||
case maps:get(SymNdx, LocalMap) of
|
||||
Value when is_integer(Value) -> {ok, Value};
|
||||
Symbol when is_list(Symbol) ->
|
||||
case maps:get(Symbol, GlobalMap, false) of
|
||||
Value when is_integer(Value) -> {ok, Value};
|
||||
false -> {error, {?MODULE, {undefined_symbol, Symbol}}}
|
||||
end
|
||||
end.
|
||||
buffer_to_word(4, Buffer) -> pdp10_extint:uint36_from_ext(Buffer);
|
||||
buffer_to_word(2, Buffer) -> pdp10_extint:uint18_from_ext(Buffer);
|
||||
buffer_to_word(1, [Byte]) -> Byte.
|
||||
|
||||
buffer_word(4, Buffer) -> pdp10_extint:uint36_from_ext(Buffer);
|
||||
buffer_word(2, Buffer) -> pdp10_extint:uint18_from_ext(Buffer);
|
||||
buffer_word(1, [Byte]) -> Byte.
|
||||
word_to_buffer(4, Word) -> pdp10_extint:uint36_to_ext(Word);
|
||||
word_to_buffer(2, Word) -> pdp10_extint:uint18_to_ext(Word);
|
||||
word_to_buffer(1, Byte) -> [Byte].
|
||||
|
||||
word_buffer(4, Word) -> pdp10_extint:uint36_to_ext(Word);
|
||||
word_buffer(2, Word) -> pdp10_extint:uint18_to_ext(Word);
|
||||
word_buffer(1, Byte) -> [Byte].
|
||||
%% Relocations -----------------------------------------------------------------
|
||||
|
||||
sizeof_reloc(#elf36_Rela{r_info = Info}) ->
|
||||
case ?ELF36_R_TYPE(Info) of
|
||||
@@ -323,6 +259,15 @@ sizeof_reloc(#elf36_Rela{r_info = Info}) ->
|
||||
?R_PDP10_LITERAL_B -> 1
|
||||
end.
|
||||
|
||||
apply_reloc(Reloc, File, GlobalMap, FileMap, Word) ->
|
||||
#elf36_Rela{r_info = Info, r_addend = Addend} = Reloc,
|
||||
Type = ?ELF36_R_TYPE(Info),
|
||||
SymNdx = ?ELF36_R_SYM(Info),
|
||||
case symbol_value(SymNdx, File, FileMap, GlobalMap) of
|
||||
{ok, SymbolValue} -> apply_reloc(Type, SymbolValue + Addend, Word);
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
apply_reloc(Type, Value, Word) ->
|
||||
case Type of
|
||||
?R_PDP10_IFIW ->
|
||||
@@ -379,10 +324,68 @@ apply_reloc(Type, Value, Word) ->
|
||||
{ok, Value}
|
||||
end.
|
||||
|
||||
output_directly(Output, Byte) ->
|
||||
#frag_output{fp = FP, frag_offset = FragOffset} = Output,
|
||||
case pdp10_stdio:fputc(Byte, FP) of
|
||||
ok -> {ok, Output#frag_output{frag_offset = FragOffset + 1}};
|
||||
%% Symbol values ---------------------------------------------------------------
|
||||
|
||||
symbol_value(SymNdx, File, FileMap, GlobalMap) ->
|
||||
LocalMap = maps:get(File, FileMap),
|
||||
resolve(maps:get(SymNdx, LocalMap), GlobalMap).
|
||||
|
||||
resolve(Value, _GlobalMap) when is_integer(Value) -> {ok, Value};
|
||||
resolve(Symbol, GlobalMap) ->
|
||||
case maps:get(Symbol, GlobalMap, false) of
|
||||
Value when is_integer(Value) -> {ok, Value};
|
||||
false -> {error, {?MODULE, {undefined_symbol, Symbol}}}
|
||||
end.
|
||||
|
||||
%% Fragment input buffer -------------------------------------------------------
|
||||
%%
|
||||
%% An input section consists of its image data and associated relocations.
|
||||
%% An input object is a streaming reader of that image data with a pushback buffer.
|
||||
|
||||
-record(frag_input,
|
||||
{ fp :: pdp10_stdio:file()
|
||||
, file :: string()
|
||||
, sh_name :: string()
|
||||
, size :: non_neg_integer()
|
||||
, pushback :: [0..511]
|
||||
}).
|
||||
|
||||
input_init(Frag) ->
|
||||
#sectfrag{file = File, shdr = Shdr, relocs = RelocShdr} = Frag,
|
||||
case pdp10_stdio:fopen(File, [raw, read]) of
|
||||
{ok, FP} ->
|
||||
case input_relocs(FP, RelocShdr) of
|
||||
{ok, Relocs} ->
|
||||
#elf36_Shdr{sh_offset = ShOffset, sh_size = ShSize, sh_name = ShName} = Shdr,
|
||||
case pdp10_stdio:fseek(FP, {bof, ShOffset}) of
|
||||
ok ->
|
||||
Input = #frag_input{fp = FP, file = File, sh_name = ShName, size = ShSize, pushback = []},
|
||||
{ok, {Input, Relocs}};
|
||||
{error, _Reason} = Error -> Error
|
||||
end;
|
||||
{error, _Reason} = Error -> Error
|
||||
end;
|
||||
{error, Reason} -> {error, {?MODULE, {cannot_open, File, Reason}}}
|
||||
end.
|
||||
|
||||
input_relocs(_FP, false) -> {ok, []};
|
||||
input_relocs(FP, Shdr) -> pdp10_elf36:read_RelaTab(FP, Shdr).
|
||||
|
||||
input_fini(#frag_input{fp = FP}) ->
|
||||
pdp10_stdio:fclose(FP).
|
||||
|
||||
input_pushback(#frag_input{pushback = Pushback} = Input, Buffer) ->
|
||||
Input#frag_input{pushback = Buffer ++ Pushback}.
|
||||
|
||||
input(#frag_input{pushback = [Byte | Pushback]} = Input) ->
|
||||
{ok, {Byte, Input#frag_input{pushback = Pushback}}};
|
||||
input(#frag_input{size = 0}) -> eof;
|
||||
input(#frag_input{fp = FP, size = Size} = Input) when Size > 0 ->
|
||||
case pdp10_stdio:fgetc(FP) of
|
||||
{ok, Byte} -> {ok, {Byte, Input#frag_input{size = Size - 1}}};
|
||||
eof ->
|
||||
#frag_input{file = File, sh_name = ShName} = Input,
|
||||
{error, {?MODULE, {premature_eof, File, ShName}}};
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user