From 64027a45277364c94fd9fbf6e87f39969b5e793a Mon Sep 17 00:00:00 2001 From: nickysn Date: Mon, 3 Aug 2020 12:58:44 +0000 Subject: [PATCH] [PATCH 003/188] starting on linking utils From 533bcbb688ca6868da515bff3d266377e1b88eff Mon Sep 17 00:00:00 2001 From: Dmitry Boyarintsev Date: Tue, 24 Sep 2019 12:34:37 -0400 git-svn-id: branches/wasm@45999 - --- .gitattributes | 1 + utils/wasmbin/lebutils.pas | 66 +------------- utils/wasmbin/wasmbin.pas | 60 ++++++++++++- utils/wasmbin/wasmbindebug.pas | 32 +++++++ utils/wasmbin/wasmld.lpr | 13 ++- utils/wasmbin/wasmlink.pas | 155 +++++++++++++++++++++++++++++++++ 6 files changed, 258 insertions(+), 69 deletions(-) create mode 100644 utils/wasmbin/wasmlink.pas diff --git a/.gitattributes b/.gitattributes index 7e7350f940..873aacb55a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -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 diff --git a/utils/wasmbin/lebutils.pas b/utils/wasmbin/lebutils.pas index 6fb8b63f59..b725bf9e95 100644 --- a/utils/wasmbin/lebutils.pas +++ b/utils/wasmbin/lebutils.pas @@ -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. diff --git a/utils/wasmbin/wasmbin.pas b/utils/wasmbin/wasmbin.pas index 897d843aa3..7b09381d6a 100644 --- a/utils/wasmbin/wasmbin.pas +++ b/utils/wasmbin/wasmbin.pas @@ -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. diff --git a/utils/wasmbin/wasmbindebug.pas b/utils/wasmbin/wasmbindebug.pas index fb57cc43f8..cc4c4e8ef0 100644 --- a/utils/wasmbin/wasmbindebug.pas +++ b/utils/wasmbin/wasmbindebug.pas @@ -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. diff --git a/utils/wasmbin/wasmld.lpr b/utils/wasmbin/wasmld.lpr index 76a2cd857b..cf704d9075 100644 --- a/utils/wasmbin/wasmld.lpr +++ b/utils/wasmbin/wasmld.lpr @@ -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; diff --git a/utils/wasmbin/wasmlink.pas b/utils/wasmbin/wasmlink.pas new file mode 100644 index 0000000000..06fb282e4d --- /dev/null +++ b/utils/wasmbin/wasmlink.pas @@ -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