ld: ld_symtab: new phase to compute global and per-file symbol tables

This commit is contained in:
Mikael Pettersson
2020-04-18 17:08:03 +02:00
parent b1b7dc280c
commit d60f570258
3 changed files with 114 additions and 2 deletions

View File

@@ -218,7 +218,8 @@ ld(Argv) ->
%% TODO: receive ok | error
{ok, Segments0} = phase2(Options, Sections),
Segments = assign(Options, Segments0),
output(Options, Segments);
{GlobalMap, FileMap} = symtab_build(Options, Inputs, Segments),
output(Options, Segments, GlobalMap, FileMap);
{error, _Reason} = Error -> Error
end;
{error, _Reason} = Error -> Error
@@ -391,9 +392,13 @@ phase2(_Options, Sections) -> {ok, ld_phase2:phase2(Sections)}.
assign(_Options, Segments) -> ld_assign:assign(Segments).
%% Build symbol tables =========================================================
symtab_build(_Options, Inputs, Segments) -> ld_symtab:build(Inputs, Segments).
%% Output Generation ===========================================================
output(_Options, _Segments) -> ok. % FIXME
output(_Options, _Segments, _GlobalMap, _FileMap) -> ok. % FIXME
%% Error Formatting ============================================================

View File

@@ -47,4 +47,13 @@
, sections :: [#section{}]
}).
%% global symbol table
-type global() :: #{Symbol :: string() => Value :: non_neg_integer()}.
%% local symbol table within a given file (if Value is string(), consult global table)
-type local() :: #{SymNdx :: non_neg_integer() => Value :: non_neg_integer() | string()}.
%% map from file names to their local symbol tables
-type filemap() :: #{File :: string() => Local :: local()}.
-endif. % LD_INTERNAL_HRL

View File

@@ -0,0 +1,98 @@
%%% -*- erlang-indent-level: 2 -*-
%%%
%%% symbol table for pdp10-elf-ld
%%% 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 <http://www.gnu.org/licenses/>.
-module(ld_symtab).
-export([ build/2
]).
-include("ld_internal.hrl").
%% Build Symbol Tables =========================================================
-spec build([#input{}], [#segment{}]) -> {global(), filemap()}.
build(Inputs, Segments) ->
FragMap = scan_segments(Segments),
scan_inputs(Inputs, FragMap).
%% Scan all segments, which now have load addresses assigned, and record
%% the load address for every included fragment (file and section).
scan_segments(Segments) ->
lists:foldl(fun scan_segment/2, _FragMap = maps:new(), Segments).
scan_segment(#segment{phdr = Phdr, sections = Sections}, FragMap0) ->
#elf36_Phdr{p_vaddr = SegmentBase} = Phdr,
lists:foldl(fun(Section, FragMap) ->
scan_section(Section, FragMap, SegmentBase)
end, FragMap0, Sections).
scan_section(#section{shdr = Shdr, frags = Frags}, FragMap0, SegmentBase) ->
#elf36_Shdr{sh_offset = SectionOffset} = Shdr,
SectionBase = SegmentBase + SectionOffset,
lists:foldl(fun(Frag, FragMap) ->
scan_frag(Frag, FragMap, SectionBase)
end, FragMap0, Frags).
scan_frag(#sectfrag{file = File, shdr = Shdr, shndx = ShNdx}, FragMap, SectionBase) ->
#elf36_Shdr{sh_offset = SectionOffset} = Shdr,
maps:put({File, ShNdx}, SectionBase + SectionOffset, FragMap).
%% Scan all input files, whose segments now have load addresses in FragMap,
%% build a symbol table for each file, and update the global symbol table.
scan_inputs(Inputs, FragMap) ->
lists:foldl(fun(Input, {GlobalMap, FileMap}) ->
scan_input(Input, GlobalMap, FileMap, FragMap)
end, {_GlobalMap = maps:new(), _FileMap = maps:new()}, Inputs).
scan_input(Input, GlobalMap0, FileMap, FragMap) ->
#input{file = File, symtab = Syms} = Input,
{GlobalMap, SymTab} = scan_syms(Syms, _ShNdx = 0, File, FragMap, GlobalMap0, _SymTab = maps:new()),
{GlobalMap, maps:put(File, SymTab, FileMap)}.
scan_syms([], _SymNdx, _File, _FragMap, GlobalMap, SymTab) ->
{GlobalMap, SymTab};
scan_syms([Sym | Syms], SymNdx, File, FragMap, GlobalMap, SymTab) ->
{NewGlobalMap, NewSymTab} = scan_sym(Sym, SymNdx, File, FragMap, GlobalMap, SymTab),
scan_syms(Syms, SymNdx + 1, File, FragMap, NewGlobalMap, NewSymTab).
scan_sym(Sym, SymNdx, File, FragMap, GlobalMap0, SymTab0) ->
#elf36_Sym{st_name = Name, st_value = Value, st_info = Info, st_shndx = ShNdx} = Sym,
true = is_list(Name), % assert in-core name is string()
SymVal =
case ShNdx of
?SHN_ABS -> Value;
?SHN_UNDEF -> Name; % lookups redirect via GlobalMap
?SHN_COMMON -> error('FIXME');
?SHN_XINDEX -> error('FIXME');
_ ->
true = ShNdx < ?SHN_LORESERVE, % assert
case maps:get({File, ShNdx}, FragMap, false) of
false -> error; % not linked/loaded section
SectionBase -> SectionBase + Value
end
end,
SymTab = maps:put(SymNdx, SymVal, SymTab0),
GlobalMap =
case ?ELF36_ST_BIND(Info) of
?STB_GLOBAL when ShNdx =/= ?SHN_UNDEF -> maps:put(Name, SymVal, GlobalMap0);
?STB_GLOBAL -> GlobalMap0;
?STB_LOCAL -> GlobalMap0
end,
{GlobalMap, SymTab}.