Browse Source

[PATCH 003/188] starting on linking utils

From 533bcbb688ca6868da515bff3d266377e1b88eff Mon Sep 17 00:00:00 2001
From: Dmitry Boyarintsev <[email protected]>
Date: Tue, 24 Sep 2019 12:34:37 -0400

git-svn-id: branches/wasm@45999 -
nickysn 5 years ago
parent
commit
64027a4527

+ 1 - 0
.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

+ 1 - 65
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.

+ 59 - 1
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.
 

+ 32 - 0
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.
 

+ 10 - 3
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;

+ 155 - 0
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<en do begin
+    ReadLinkSubSect(st, sub);
+    writeln(sub.sectype);
+    st.Position:=st.Position+sub.length;
+  end;
+end;
+
+
+end.