[PATCH 003/188] starting on linking utils

From 533bcbb688ca6868da515bff3d266377e1b88eff Mon Sep 17 00:00:00 2001
From: Dmitry Boyarintsev <skalogryz.lists@gmail.com>
Date: Tue, 24 Sep 2019 12:34:37 -0400

git-svn-id: branches/wasm@45999 -
This commit is contained in:
nickysn 2020-08-03 12:58:44 +00:00
parent 93d6993296
commit 64027a4527
6 changed files with 258 additions and 69 deletions

1
.gitattributes vendored
View File

@ -18981,3 +18981,4 @@ utils/wasmbin/wasmbin.pas svneol=native#text/plain
utils/wasmbin/wasmbindebug.pas svneol=native#text/plain
utils/wasmbin/wasmld.lpi svneol=native#text/plain
utils/wasmbin/wasmld.lpr svneol=native#text/plain
utils/wasmbin/wasmlink.pas svneol=native#text/plain

View File

@ -3,47 +3,13 @@ unit lebutils;
interface
uses
SysUtils, Classes, wasmbin;
SysUtils, Classes;
function ReadU(src: TStream): UInt64;
function ReadS(src: TStream; bits: Integer): Int64;
procedure ReadFuncTypesArray(src: TStream; var arr: TFuncTypeArray);
procedure ReadFuncType(src: TStream; var ft: TFuncType);
procedure ReadCodeEntry(src: TStream; var en: TCodeEntry);
procedure ReadCodeSection(src: TStream; var sc: TCodeSection);
implementation
procedure ReadFuncType(src: TStream; var ft: TFuncType);
var
c: integer;
begin
// vector of t1
c:=ReadU(src);
SetLength(ft.param, c);
src.Read(ft.param[0], c);
// vector of t2
c:=ReadU(src);
SetLength(ft.result, c);
src.Read(ft.result[0], c);
end;
procedure ReadFuncTypesArray(src: TStream; var arr: TFuncTypeArray);
var
cnt : integer;
i : Integer;
begin
cnt := ReadU(src);
SetLength(arr.funTypes, cnt);
for i:=0 to cnt-1 do begin
if src.ReadByte = func_type then
ReadFuncType(src, arr.funTypes[i]);
end;
end;
function ReadU(src: TStream): UInt64;
var
b : byte;
@ -78,34 +44,4 @@ begin
result := result or ( (not 0) shl sh);
end;
procedure ReadCodeEntry(src: TStream; var en: TCodeEntry);
var
sz : integer; // size in bytes
//pos : int64;
cnt : Integer;
i : integer;
begin
sz := ReadU(src);
cnt := ReadU(src);
SetLength(en.locals, cnt);
for i:=0 to cnt-1 do begin
en.locals[i].count := ReadU(src);
en.locals[i].valtyp := src.ReadByte;
end;
end;
procedure ReadCodeSection(src: TStream; var sc: TCodeSection);
var
cnt : integer;
i : integer;
begin
cnt := ReadU(src);
SetLength(sc.entries, cnt);
for i:= 0 to cnt-1 do
ReadCodeEntry(src, sc.entries[i]);
end;
end.

View File

@ -5,7 +5,7 @@ unit wasmbin;
interface
uses
Classes, SysUtils;
Classes, SysUtils, lebutils;
const
valtype_i32 = $7f;
@ -90,6 +90,20 @@ type
function SectionIdToStr(id: integer): string;
function ValTypeToStr(id: integer): string;
// reads the name from the input stream
// the name consists of
// size - in butes Leb128
// bytes - in utf8 format
function GetName(sr: TStream): string;
// reads
function GetU32(sr: TStream): UInt32;
// reads the code entry into TCodeEntry structure
procedure ReadCodeEntry(src: TStream; var en: TCodeEntry);
// reads the code entry into TCodeEntry structure
procedure ReadCodeSection(src: TStream; var sc: TCodeSection);
implementation
function ValTypeToStr(id: integer): string;
@ -127,5 +141,49 @@ begin
end;
function GetName(sr: TStream): string;
var
ln : LongWord;
begin
ln := ReadU(sr);
SetLength(result, ln);
if ln>0 then sr.Read(result[1], ln);
end;
function GetU32(sr: TStream): UInt32;
begin
Result := UInt32(ReadU(sr));
end;
procedure ReadCodeEntry(src: TStream; var en: TCodeEntry);
var
sz : integer; // size in bytes
//pos : int64;
cnt : Integer;
i : integer;
begin
sz := ReadU(src);
cnt := ReadU(src);
SetLength(en.locals, cnt);
for i:=0 to cnt-1 do begin
en.locals[i].count := ReadU(src);
en.locals[i].valtyp := src.ReadByte;
end;
end;
procedure ReadCodeSection(src: TStream; var sc: TCodeSection);
var
cnt : integer;
i : integer;
begin
cnt := ReadU(src);
SetLength(sc.entries, cnt);
for i:= 0 to cnt-1 do
ReadCodeEntry(src, sc.entries[i]);
end;
end.

View File

@ -9,6 +9,9 @@ uses
procedure DumpTypes(sr: TStream);
procedure ReadFuncTypesArray(src: TStream; var arr: TFuncTypeArray);
procedure ReadFuncType(src: TStream; var ft: TFuncType);
implementation
procedure DumpTypes(sr: TStream);
@ -32,5 +35,34 @@ begin
end;
end;
procedure ReadFuncType(src: TStream; var ft: TFuncType);
var
c: integer;
begin
// vector of t1
c:=ReadU(src);
SetLength(ft.param, c);
src.Read(ft.param[0], c);
// vector of t2
c:=ReadU(src);
SetLength(ft.result, c);
src.Read(ft.result[0], c);
end;
procedure ReadFuncTypesArray(src: TStream; var arr: TFuncTypeArray);
var
cnt : integer;
i : Integer;
begin
cnt := ReadU(src);
SetLength(arr.funTypes, cnt);
for i:=0 to cnt-1 do begin
if src.ReadByte = func_type then
ReadFuncType(src, arr.funTypes[i]);
end;
end;
end.

View File

@ -7,7 +7,7 @@ uses
cthreads,
{$ENDIF}{$ENDIF}
{ you can add units after this }
Classes, SysUtils, wasmbin, lebutils, wasmbindebug;
Classes, SysUtils, wasmbin, lebutils, wasmbindebug, wasmlink;
function ReadStream(st: TStream): Boolean;
var
@ -15,6 +15,7 @@ var
ofs : int64;
sc : TSection;
ps : int64;
nm : string;
begin
dw := st.ReadDWord;
Result := dw = WasmId_Int;
@ -31,11 +32,17 @@ begin
writeln(ofs,': id=', sc.id,'(', SectionIdToStr(sc.id),') sz=', sc.size);
ps := st.Position+sc.size;
if sc.id= 1 then DumpTypes(st);
if sc.id=0 then begin
nm := GetName(st);
writeln(nm);
if nm = SectionName_Linking then
DumpLinking(st, sc.size - (st.Position - ofs));
end;
//if sc.id= 1 then DumpTypes(st);
if st.Position <> ps then
begin
writeln('adjust stream targ=',ps,' actual: ', st.position);
//writeln('adjust stream targ=',ps,' actual: ', st.position);
st.Position := ps;
end;
end;

155
utils/wasmbin/wasmlink.pas Normal file
View File

@ -0,0 +1,155 @@
unit wasmlink;
// The unit covers the WebAssembly static linking convention
// as described at https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md
interface
uses
Classes, SysUtils, lebutils;
const
SectionName_Linking = 'linking';
SectionNamePfx_Reloc = 'reloc.';
type
TRelocationSection = record
section : UInt32; // the index of the target section
count : Uint32; // count of entries to follow
end;
TRelocationEntry = record
reltype : UInt8; // the relocation type (see R_WASM constants)
offset : UInt32; // offset of the value to rewrite
index : Uint32; // the index of the symbol used (or, for R_WASM_TYPE_INDEX_LEB relocations, the index of the type)
end;
TRelocationEntryEx = record
entry : TRelocationEntry;
addend : UInt32;
end;
const
// A relocation type can be one of the following:
R_WASM_FUNCTION_INDEX_LEB = 0; // a function index encoded as a 5-byte varuint32. Used for the immediate argument of a call instruction.
R_WASM_TABLE_INDEX_SLEB = 1; // a function table index encoded as a 5-byte varint32. Used to refer to the immediate argument of a i32.const instruction, e.g. taking the address of a function.
R_WASM_TABLE_INDEX_I32 = 2; // a function table index encoded as a uint32, e.g. taking the address of a function in a static data initializer.
R_WASM_MEMORY_ADDR_LEB = 3; // a linear memory index encoded as a 5-byte varuint32. Used for the immediate argument of a load or store instruction, e.g. directly loading from or storing to a C++ global.
R_WASM_MEMORY_ADDR_SLEB = 4; // a linear memory index encoded as a 5-byte varint32. Used for the immediate argument of a i32.const instruction, e.g. taking the address of a C++ global.
R_WASM_MEMORY_ADDR_I32 = 5; // a linear memory index encoded as a uint32, e.g. taking the address of a C++ global in a static data initializer.
R_WASM_TYPE_INDEX_LEB = 6; // a type table index encoded as a 5-byte varuint32, e.g. the type immediate in a call_indirect.
R_WASM_GLOBAL_INDEX_LEB = 7; // a global index encoded as a 5-byte varuint32, e.g. the index immediate in a get_global.
R_WASM_FUNCTION_OFFSET_I32 = 8; // a byte offset within code section for the specic function encoded as a uint32. The offsets start at the actual function code excluding its size field.
R_WASM_SECTION_OFFSET_I32 = 9; // an byte offset from start of the specified section encoded as a uint32.
R_WASM_EVENT_INDEX_LEB = 10; // an event index encoded as a 5-byte varuint32. Used for the immediate argument of a throw and if_except instruction.
R_WASM_TABLE_NUMBER_LEB = 13; // a table number encoded as a 5-byte varuint32. Used for the table immediate argument in the table.* instructions.
type
TLinkingMetadata = record
version : UInt32; // the version of linking metadata contained in this section. Currently: 2
end;
TLinkinSubSection = record
sectype : UInt8; // code identifying type of subsection
length : UInt32; // size of this subsection in bytes
end;
const
LINKING_VERSION = 2;
// The current list of valid TLinkinSubSection.sectype codes are:
WASM_SEGMENT_INFO = 5; // Extra metadata about the data segments.
WASM_INIT_FUNCS = 6; // Specifies a list of constructor functions to be called at startup.
// These constructors will be called in priority order after memory
// has been initialized.
WASM_COMDAT_INFO = 7; // Specifies the COMDAT groups of associated linking objects,
// which are linked only once and all together.
WASM_SYMBOL_TABLE = 8; // Specifies extra information about the symbols present in the module
type
TSymInfo = record
symkind : UInt8;
flags : UInt32;
end;
// The current set of valid flags for symbols are:
const
// Indicating that this is a weak symbol. When linking multiple modules
// defining the same symbol, all weak definitions are discarded if
// any strong definitions exist; then if multiple weak definitions
// exist all but one (unspecified) are discarded; and finally it is an error
// if more than one definition remains.
WASM_SYM_BINDING_WEAK = $01;
// Indicating that this is a local symbol (this is exclusive
// with WASM_SYM_BINDING_WEAK). Local symbols are not to be exported,
// or linked to other modules/sections. The names of all non-local
// symbols must be unique, but the names of local symbols are
// not considered for uniqueness. A local function or global
// symbol cannot reference an import.
WASM_SYM_BINDING_LOCAL = $02;
// Indicating that this is a hidden symbol. Hidden symbols are not to be
// exported when performing the final link, but may be linked to other modules.
WASM_SYM_VISIBILITY_HIDDEN = $04;
// Indicating that this symbol is not defined. For non-data symbols,
// this must match whether the symbol is an import or is defined;
// for data symbols, determines whether a segment is specified.
WASM_SYM_UNDEFINED = $10;
// The symbol is intended to be exported from the wasm module to the host
// environment. This differs from the visibility flags in that it effects
// the static linker.
WASM_SYM_EXPORTED = $20;
// The symbol uses an explicit symbol name, rather than reusing the name
// from a wasm import. This allows it to remap imports from foreign WebAssembly
// modules into local symbols with different names.
WASM_SYM_EXPLICIT_NAME = $40;
// The symbol is intended to be included in the linker output,
// regardless of whether it is used by the program.
WASM_SYM_NO_STRIP = $80;
function ReadMetaData(st: TStream; out m:TLinkingMetadata): Boolean;
function ReadLinkSubSect(st: TStream; out m: TLinkinSubSection): Boolean;
// dumps linking information. Note: that the name of the "Linking" section
// must have already been read
procedure DumpLinking(st: TStream; secsize: integer);
implementation
function ReadMetaData(st: TStream; out m:TLinkingMetadata): Boolean;
begin
FillChar(m, sizeof(m), 0);
m.version := ReadU(st);
Result:=true;
end;
function ReadLinkSubSect(st: TStream; out m: TLinkinSubSection): Boolean;
begin
FillChar(m, sizeof(m), 0);
m.sectype := ReadU(st);
m.length := ReadU(st);
Result:=true;
end;
procedure DumpLinking(st: TStream; secsize: integer);
var
mt : TLinkingMetadata;
en : Int64;
sub : TLinkinSubSection;
begin
en := st.Position+secsize;
ReadMetadata(st, mt);
writeln('version: ', mt.version);
while st.Position<en do begin
ReadLinkSubSect(st, sub);
writeln(sub.sectype);
st.Position:=st.Position+sub.length;
end;
end;
end.