123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478 |
- { This file is part of wasmbin - a collection of WebAssembly binary utils.
- Copyright (C) 2019, 2020 Dmitry Boyarintsev <[email protected]>
- Copyright (C) 2020 by the Free Pascal development team
- This source 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 2 of the License, or (at your option)
- any later version.
- This code 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.
- A copy of the GNU General Public License is available on the World Wide Web
- at <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing
- to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
- Boston, MA 02110-1335, USA.
- }
- unit wasmbin;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes, SysUtils, lebutils, wasmbincode;
- const
- valtype_i32 = $7f;
- valtype_i64 = $7e;
- valtype_f32 = $7d;
- valtype_f64 = $7C;
- block_type = $40;
- func_type = $60;
- elem_type = $70;
- global_const = $00;
- global_mut = $01;
- limit_min_inf = $00; // minimum - to infinity
- limit_min_max = $01; // minimum - maximum
- const
- WasmId = #0'asm';
- WasmId_Int = $6D736100;
- Wasm_Version1 = 1;
- var
- WasmId_Buf : array [0..3] of char = (#0, 'a','s','m');
- type
- TLimit = record
- limitType : byte;
- min : UInt32;
- max : UInt32;
- end;
- TMemoryType = TLimit;
- TTableType = record
- elemType : Byte; // see "elem_type"
- limits : TLimit;
- end;
- TGlobalType = record
- valtype : Byte; // see "valtype_" consts
- mut : Byte; // see "global_" consts
- end;
- const
- SECT_CUSTOM = 0; // custom section
- SECT_TYPE = 1; // type section
- SECT_IMPORT = 2; // import section
- SECT_FUNCTION = 3; // function section
- SECT_TABLE = 4; // table section
- SECT_MEMORY = 5; // memory section
- SECT_GLOBAL = 6; // global section
- SECT_EXPORT = 7; // export section
- SECT_START = 8; // start section
- SECT_ELEMENT = 9; // element section
- SECT_CODE = 10; // code section
- SECT_DATA = 11; // data section
- type
- TSection = packed record
- id : byte;
- size : LongWord; // it is Leb128 encoded in the file, thus cannot be read directly
- end;
- TFuncType = record
- param : array of byte;
- result : array of byte;
- end;
- TFuncTypeArray = record
- funTypes : array of TFuncType;
- end;
- TCodeLocalEntry = record
- count : LongWord;
- valtyp : Byte;
- end;
- TCodeInstr = record
- inst : byte;
- idxArr: array of LongWord;
- case byte of
- 0: (align, offset : LongWord);
- 1: (index: LongWord);
- 2: (i32: LongWord);
- 3: (i64: UInt64);
- 4: (f32: single);
- 5: (f64: double);
- // for labels
- 6: (idxCount: integer;
- idxDef :LongWord);
- 7: (returnType: byte);
- end;
- TCodeEntry = record
- locals : array of TCodeLocalEntry;
- instBuf : array of byte;
- end;
- TCodeSection = record
- entries : array of TCodeEntry;
- end;
- const
- IMPDESC_FUNC = $00;
- IMPDESC_TABLE = $01;
- IMPDESC_MEM = $02;
- IMPDESC_GLOBAL = $03;
- type
- TImportEntry = record
- module : string;
- name : string;
- case desc: byte of
- IMPDESC_FUNC : (
- fnType : UInt32;
- );
- IMPDESC_TABLE: (
- tblType : TTableType;
- );
- IMPDESC_TABLE: (
- memType : TMemoryType;
- );
- IMPDESC_GLOBAL: (
- glbType : TGlobalType;
- );
- end;
- TImportSection = record
- entries : array of TImportEntry;
- end;
- const
- EXPDESC_FUNC = $00;
- EXPDESC_TABLE = $01;
- EXPDESC_MEM = $02;
- EXPDESC_GLOBAL = $03;
- type
- TExportEntry = record
- name : string;
- desc : byte;
- index : UInt32;
- end;
- TExportSection = record
- entries : array of TExportEntry;
- end;
- const
- ELEMTYPE_FUNC = $70;
- type
- TElementEntry = record
- table : Uint32;
- expr : array of byte; // instructions
- funcs : array of UInt32;
- end;
- TElementSection = record
- entries : array of TElementEntry;
- end;
- 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 ReadName(st: TStream): string;
- procedure WriteName(st: TStream; const str: string);
- 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);
- function isUnreachable(const cd: TCodeEntry): Boolean;
- procedure ReadExportEntry(src: TStream; var ex: TExportEntry);
- // reads the export entry
- procedure ReadExport(src: TStream; var ex: TExportSection);
- procedure WriteExport(const ex: TExportSection; dst: TStream);
- function isWasmStream(st: TStream): Boolean;
- function isWasmFile(const fn: string): Boolean;
- procedure ReadElementEntry(st: TStream; var en: TElementEntry);
- procedure ReadElementSection(st: TStream; var sc: TelementSection);
- procedure ReadLimit(st: TStream; var lm: TLimit);
- procedure ReadTableType(st: TStream; var tb: TTableType);
- procedure ReadGlobalType(st: TStream; var gb: TGlobalType);
- function ReadImportEntry(st: TStream; var imp: TImportEntry): Boolean;
- function ReadImportSection(st: TStream; var imp: TImportSection): Boolean;
- implementation
- procedure ReadLimit(st: TStream; var lm: TLimit);
- begin
- lm.limitType := st.ReadByte;
- lm.min := ReadU(st);
- if lm.limitType <> limit_min_inf then
- lm.max := ReadU(st)
- else
- lm.max := 0;
- end;
- procedure ReadTableType(st: TStream; var tb: TTableType);
- begin
- tb.elemType := st.ReadByte;
- ReadLimit(st, tb.limits);
- end;
- procedure ReadGlobalType(st: TStream; var gb: TGlobalType);
- begin
- gb.valtype := st.ReadByte;
- gb.mut := st.ReadByte;
- end;
- function ValTypeToStr(id: integer): string;
- begin
- case id of
- valtype_i32 : Result := 'i32';
- valtype_i64 : Result := 'i64';
- valtype_f32 : Result := 'f32';
- valtype_f64 : Result := 'f64';
- else
- Str(id, Result);
- Result := 'iUnk'+Result;
- end
- end;
- function SectionIdToStr(id: integer): string;
- begin
- case id of
- sect_custom : Result := 'custom'; // custom section
- sect_type : Result := 'type'; // type section
- sect_import : Result := 'import'; // import section
- sect_function : Result := 'function'; // function section
- sect_table : Result := 'table'; // table section
- sect_memory : Result := 'memory'; // memory section
- sect_global : Result := 'global'; // global section
- sect_export : Result := 'export'; // export section
- sect_start : Result := 'start'; // start section
- sect_element : Result := 'element'; // element section
- sect_code : Result := 'code'; // code section
- sect_data : Result := 'data'; // data section
- else
- Str(id, Result);
- Result := 'sect_unknown'+Result;
- end;
- end;
- function ReadName(st: TStream): string;
- var
- ln : LongWord;
- begin
- ln := ReadU(st);
- SetLength(result, ln);
- if ln>0 then st.Read(result[1], ln);
- end;
- procedure WriteName(st: TStream; const str: string);
- begin
- WriteU32(st, length(str));
- if length(str)>0 then
- st.Write(str[1], length(str));
- end;
- function GetName(sr: TStream): string;
- begin
- Result := ReadName(sr);
- 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;
- eofs : Int64;
- begin
- sz := ReadU(src);
- eofs := src.Position+sz;
- 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;
- SetLength(en.instBuf, eofs-src.Position);
- if (length(en.instBuf)>0) then
- src.Read(en.instBuf[0], length(en.instBuf));
- 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;
- function isUnreachable(const cd: TCodeEntry): Boolean;
- begin
- Result:=(length(cd.instBuf)>0) and (cd.instBuf[0]=INST_TRAP);
- end;
- procedure ReadExportEntry(src: TStream; var ex: TExportEntry);
- begin
- ex.name := ReadName(src);
- ex.desc := src.ReadByte;
- ex.index := ReadU(src);
- end;
- procedure ReadExport(src: TStream; var ex: TExportSection);
- var
- cnt : integer;
- i : integer;
- begin
- cnt := ReadU(src);
- SetLength(ex.entries, cnt);
- for i:=0 to cnt-1 do
- ReadExportEntry(src, ex.entries[i]);
- end;
- procedure WriteExport(const ex: TExportSection; dst: TStream);
- var
- i : integer;
- begin
- WriteU32(dst, length(ex.entries));
- for i:=0 to length(ex.entries)-1 do begin
- WriteName(dst, ex.entries[i].name);
- dst.WriteByte(ex.entries[i].desc);
- WriteU32(dst, ex.entries[i].index);
- end;
- end;
- function isWasmStream(st: TStream): Boolean;
- var
- pos : Int64;
- begin
- try
- pos:=st.Position;
- try
- Result := st.ReadDWord = WasmId_Int;
- finally
- st.Position:=pos;
- end;
- except
- Result:=false;
- end;
- end;
- function isWasmFile(const fn: string): Boolean;
- var
- fs: TFileStream;
- begin
- try
- fs:=TFileStream.Create(fn, fmOpenRead or fmShareDenyNone);
- try
- Result:=isWasmStream(fs);
- finally
- fs.Free;
- end;
- except
- Result := false;
- end;
- end;
- procedure ReadElementEntry(st: TStream; var en: TElementEntry);
- var
- ln : integer;
- i : integer;
- begin
- en.table := ReadU(st);
- ln:=InstLen(st);
- if ln<0 then Exit;
- SetLength(en.expr, ln);
- if ln>0 then st.Read(en.expr[0], ln);
- ln:=ReadU(st);
- SetLength(en.funcs, ln);
- for i:=0 to ln-1 do
- en.funcs[i]:=ReadU(st);
- end;
- procedure ReadElementSection(st: TStream; var sc: TelementSection);
- var
- cnt : integer;
- i : integer;
- begin
- cnt := ReadU(st);
- SetLength(sc.entries, cnt);
- for i:=0 to cnt-1 do
- ReadElementEntry(st, sc.entries[i]);
- end;
- function ReadImportEntry(st: TStream; var imp: TImportEntry): Boolean;
- begin
- Result := true;
- imp.module := ReadName(st);
- imp.name := ReadName(st);
- imp.desc := st.ReadByte;
- case imp.desc of
- IMPDESC_FUNC : imp.fnType := ReadU(st);
- IMPDESC_TABLE: ReadTableType(st, imp.tblType);
- IMPDESC_MEM: ReadLimit(st, imp.memType);
- IMPDESC_GLOBAL: ReadGlobalType(st, imp.glbType);
- else
- Result := false;
- end;
- end;
- function ReadImportSection(st: TStream; var imp: TImportSection): Boolean;
- var
- cnt : integer;
- i : integer;
- begin
- cnt := ReadU(st);
- SetLength(imp.entries, cnt);
- Result := true;
- if cnt>0 then
- for i:=0 to cnt-1 do
- if not ReadImportEntry(st, imp.entries[i]) then begin
- Result := false;
- break;
- end;
- end;
- end.
|