123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- { 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 wasmnormalize;
- {$mode objfpc}{$H+}
- interface
- uses
- SysUtils, Classes,
- wasmmodule, wasmbin, wasmbincode, wasmlink;
- procedure Normalize(m: TWasmModule);
- implementation
- procedure PopulateRelocData(module: TWasmModule; ci: TWasmInstr);
- var
- idx : integer;
- obj : TObject;
- begin
- case INST_FLAGS[ci.code].Param of
- ipi32OrFunc:
- if (ci.operand1.textVal<>'') and (ci.operand1.textVal[1]='$') then begin
- //if not ci.hasRelocIdx then
- idx := RegisterFuncInElem(module, ci.operand1.textVal);
- obj := GetFuncByNum(module, idx);
- //AddReloc(rt, dst.Position+ofsAddition, idx);
- ci.operandNum := idx;
- ci.SetReloc(INST_RELOC_FLAGS[ci.code].relocType, obj);
- end;
- ipLeb:
- if (INST_RELOC_FLAGS[ci.code].doReloc) then begin
- case INST_RELOC_FLAGS[ci.code].relocType of
- R_WASM_FUNCTION_INDEX_LEB:
- ci.SetReloc(INST_RELOC_FLAGS[ci.code].relocType, GetFuncByNum(module, ci.operandNum));
- R_WASM_GLOBAL_INDEX_LEB:
- ci.SetReloc(INST_RELOC_FLAGS[ci.code].relocType, GetGlobalByNum(module, ci.operandNum));
- R_WASM_MEMORY_ADDR_LEB :
- ci.SetReloc(INST_RELOC_FLAGS[ci.code].relocType, GetMemByNum(module, ci.operandNum));
- end;
- end;
- ipCallType:
- if Assigned(ci.insttype) then
- begin
- ci.SetReloc(INST_RELOC_FLAGS[ci.code].relocType, ci.insttype);
- end;
- end;
- end;
- // searching back in the labels stack.
- // returning the "number" of steps to jump back to the label
- function GetJumpLabelIndex(const JumpToLbl: string; LblStack: TStrings): Integer;
- var
- i : integer;
- begin
- i:=LblStack.Count-1;
- while (i>=0) and (LblStack[i]<>JumpToLbl) do
- dec(i);
- Result := LblStack.Count-i-1;
- end;
- // Normalizing instruction list, popuplating index reference ($index)
- // with the actual numbers. (params, locals, globals, memory, functions index)
- //
- // pass "f" as nil, if instruction list doesn't belong to a function
- function NormalizeInst(m: TWasmModule; f: TWasmFunc; l: TWasmInstrList; checkEnd: boolean = true): Boolean;
- var
- i : integer;
- j : integer;
- ci : TWasmInstr;
- endNeed : Integer;
- lbl : TStringList;
- const
- ValidResTypes = [VALTYPE_NONE,VALTYPE_I32,VALTYPE_I64,VALTYPE_F32,VALTYPE_F64];
- begin
- Result := true;
- endNeed := 1;
- lbl := TStringList.Create;
- try
- for i:=0 to l.Count-1 do begin
- ci:=l[i];
- case INST_FLAGS[ci.code].Param of
- ipResType:
- begin
- inc(endNeed);
- if not (byte(ci.operandNum) in ValidResTypes) then
- ci.operandNum := VALTYPE_NONE;
- lbl.Add(ci.jumplabel);
- end;
- end;
- case ci.code of
- INST_local_get, INST_local_set, INST_local_tee:
- begin
- if not Assigned(f) then begin
- Result:=false;
- Exit;
- end;
- if (ci.operandIdx<>'') and (ci.operandNum<0) then begin
- j:=FindParam(f.functype.params, ci.operandIdx);
- if j<0 then begin
- j:=FindParam(f.locals, ci.operandIdx);
- if j>=0 then inc(j, f.functype.ParamCount);
- end;
- ci.operandNum:=j;
- end;
- end;
- INST_global_get, INST_global_set:
- begin
- if not Assigned(f) then begin
- Result:=false;
- Exit;
- end;
- if (ci.operandIdx<>'') and (ci.operandNum<0) then begin
- j:=FindGlobal(m, ci.operandIdx);
- ci.operandNum:=j;
- end;
- end;
- INST_call:
- begin
- if (ci.operandIdx<>'') and (ci.operandNum<0) then
- ci.operandNum:=FindFunc(m,ci.operandIdx);
- end;
- INST_call_indirect:
- begin
- if Assigned(ci.insttype) and (ci.insttype.typeNum<0) then begin
- if ci.insttype.typeIdx <> '' then
- ci.insttype.typeNum:=FindFuncType(m, ci.insttype.typeIdx)
- else
- ci.insttype.typeNum:=RegisterFuncType(m, ci.insttype);
- end;
- end;
- INST_br, INST_br_if, INST_br_table: begin
- if ci.code = INST_br_table then
- for j:=0 to ci.vecTableCount-1 do
- if ci.vecTable[j].id <> '' then
- ci.vecTable[j].idNum:=GetJumpLabelIndex(ci.vecTable[j].id, lbl);
- if ci.operandIdx<>'' then
- ci.operandNum:=GetJumpLabelIndex(ci.operandIdx, lbl);
- end;
- INST_END: begin
- dec(endNeed);
- if lbl.Count>0 then lbl.Delete(lbl.Count-1);
- end;
- end;
- PopulateRelocData(m, ci);
- end;
- // adding end instruction
- if checkEnd and (endNeed>0) then
- l.AddInstr(INST_END);
- finally
- lbl.Free;
- end;
- end;
- procedure NormalizeFuncType(m: TWasmModule; fn : TWasmFuncType);
- begin
- if fn.isNumOrIdx then begin
- if fn.typeIdx<>'' then
- fn.typeNum:=FindFuncType(m, fn.typeIdx);
- end else
- fn.typeNum:=RegisterFuncType(m, fn);
- end;
- procedure NormalizeImport(m: TWasmModule; out fnIdx: Integer;
- out memIdx: Integer; out globIdx: Integer; out tblIdx : Integer);
- var
- i : integer;
- im : TWasmImport;
- begin
- fnIdx := 0;
- memIdx := 0;
- globIdx := 0;
- tblIdx := 0;
- for i:=0 to m.ImportCount-1 do begin
- im := m.GetImport(i);
- if Assigned(im.fn) then begin
- im.fn.idNum:=fnIdx;
- NormalizeFuncType(m, im.fn.functype);
- inc(fnIdx);
- end else if Assigned(im.mem) then begin
- im.mem.id.idNum := memIdx;
- inc(memIdx);
- end else if Assigned(im.glob) then begin
- im.glob.id.idNum := globIdx;
- inc(globIdx);
- end else if Assigned(im.table) then begin
- im.table.id.idNum := tblIdx;
- inc(tblIdx);
- end;
- end;
- end;
- procedure NormalizeGlobals(m: TWasmModule);
- var
- i : integer;
- begin
- for i:=0 to m.GlobalCount-1 do
- m.GetGlobal(i).id.idNum:=i;
- end;
- procedure NormalizeTable(m: TWasmModule);
- var
- i : integer;
- j : integer;
- t : TWasmTable;
- se : TWasmElement;
- de : TWasmElement;
- begin
- for i:=0 to m.TableCount-1 do begin
- t := m.GetTable(i);
- t.id.idNum:=i; // todo: is it safe?
- end;
- for i:=0 to m.TableCount-1 do begin
- t := m.GetTable(i);
- if not Assigned(t.elem) then continue;
- se:=t.elem;
- de := m.AddElement;
- de.tableId.idNum := t.id.idNum;
- if t.elemsType = 0 then t.elemsType := ELEMTYPE_FUNC;
- de.funcCount:=se.funcCount;
- if se.funcCount>0 then begin
- SetLength(de.funcs, de.funcCount);
- for j:=0 to de.funcCount-1 do begin
- de.funcs[j].id := se.funcs[j].id;
- de.funcs[j].idNum := se.funcs[j].idNum;
- end;
- end;
- end;
- end;
- procedure NormalizeTableLimit(m: TWasmModule);
- var
- i : integer;
- elCount : integer;
- t : TWasmTable;
- begin
- elCount:=0;
- for i:=0 to m.ElementCount-1 do
- inc(elCount, m.GetElement(i).funcCount );
- for i:=0 to m.TableCount-1 do begin
- t := m.GetTable(i);
- if (t.min=0) and (t.max=0) then begin
- t.min := elCount;
- t.max := elCount;
- Break;
- end;
- end;
- end;
- // For each element on the module, funcId is replaced with the proper IdNum
- // it should only be called after all functions have their IDs resolved
- procedure NormalizeElems(m: TWasmModule);
- var
- i : integer;
- e : TWasmElement;
- j : integer;
- l : TWasmInstrList;
- begin
- //todo: resolve offsets
- for i:=0 to m.ElementCount-1 do begin
- e := m.GetElement(i);
- l := e.AddOffset;
- if (l.Count=0) then l.AddInstr(INST_i32_const).operand1.s32:=0;
- NormalizeInst( m, nil, l);
- for j := 0 to e.funcCount-1 do
- if e.funcs[j].idNum<0 then
- e.funcs[j].idNum := FindFunc(m, e.funcs[j].id);
- end;
- end;
- // normalizing reference
- procedure Normalize(m: TWasmModule);
- var
- i : integer;
- f : TWasmFunc;
- x : TWasmExport;
- fnIdx : Integer;
- memIdx : Integer;
- globIdx : Integer;
- tblIdx : Integer;
- g : TWasmGlobal;
- begin
- fnIdx := 0;
- NormalizeGlobals(m);
- NormalizeTable(m);
- NormalizeImport(m, fnIdx, memIdx, globIdx, tblIdx);
- NormalizeTableLimit(m);
- for i:=0 to m.FuncCount-1 do begin
- f:=m.GetFunc(i);
- f.idNum := fnIdx;
- NormalizeFuncType(m, f.functype);
- inc(fnIdx);
- end;
- NormalizeElems(m);
- for i:=0 to m.GlobalCount-1 do
- begin
- g := m.GetGlobal(i);
- g.id.idNum := globIdx;
- inc(globIdx);
- NormalizeInst(m, nil, g.StartValue);
- end;
- // normalizing function body
- for i:=0 to m.FuncCount-1 do begin
- f:=m.GetFunc(i);
- // finding the reference in functions
- // populating "nums" where string "index" is used
- NormalizeInst(m, f, f.instr);
- end;
- // normalizing exports
- for i:=0 to m.ExportCount-1 do begin
- x:=m.GetExport(i);
- if x.exportNum<0 then
- case x.exportType of
- EXPDESC_FUNC:
- if x.exportIdx<>'' then
- x.exportNum := FindFunc(m, x.exportIdx);
- end;
- end;
- end;
- end.
|