wasmnormalize.pas 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. { This file is part of wasmbin - a collection of WebAssembly binary utils.
  2. Copyright (C) 2019, 2020 Dmitry Boyarintsev <[email protected]>
  3. Copyright (C) 2020 by the Free Pascal development team
  4. This source is free software; you can redistribute it and/or modify it under
  5. the terms of the GNU General Public License as published by the Free
  6. Software Foundation; either version 2 of the License, or (at your option)
  7. any later version.
  8. This code is distributed in the hope that it will be useful, but WITHOUT ANY
  9. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  10. FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  11. details.
  12. A copy of the GNU General Public License is available on the World Wide Web
  13. at <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing
  14. to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
  15. Boston, MA 02110-1335, USA.
  16. }
  17. unit wasmnormalize;
  18. {$mode objfpc}{$H+}
  19. interface
  20. uses
  21. SysUtils, Classes,
  22. wasmmodule, wasmbin, wasmbincode, wasmlink;
  23. procedure Normalize(m: TWasmModule);
  24. implementation
  25. procedure PopulateRelocData(module: TWasmModule; ci: TWasmInstr);
  26. var
  27. idx : integer;
  28. obj : TObject;
  29. begin
  30. case INST_FLAGS[ci.code].Param of
  31. ipi32OrFunc:
  32. if (ci.operand1.textVal<>'') and (ci.operand1.textVal[1]='$') then begin
  33. //if not ci.hasRelocIdx then
  34. idx := RegisterFuncInElem(module, ci.operand1.textVal);
  35. obj := GetFuncByNum(module, idx);
  36. //AddReloc(rt, dst.Position+ofsAddition, idx);
  37. ci.operandNum := idx;
  38. ci.SetReloc(INST_RELOC_FLAGS[ci.code].relocType, obj);
  39. end;
  40. ipLeb:
  41. if (INST_RELOC_FLAGS[ci.code].doReloc) then begin
  42. case INST_RELOC_FLAGS[ci.code].relocType of
  43. R_WASM_FUNCTION_INDEX_LEB:
  44. ci.SetReloc(INST_RELOC_FLAGS[ci.code].relocType, GetFuncByNum(module, ci.operandNum));
  45. R_WASM_GLOBAL_INDEX_LEB:
  46. ci.SetReloc(INST_RELOC_FLAGS[ci.code].relocType, GetGlobalByNum(module, ci.operandNum));
  47. R_WASM_MEMORY_ADDR_LEB :
  48. ci.SetReloc(INST_RELOC_FLAGS[ci.code].relocType, GetMemByNum(module, ci.operandNum));
  49. end;
  50. end;
  51. ipCallType:
  52. if Assigned(ci.insttype) then
  53. begin
  54. ci.SetReloc(INST_RELOC_FLAGS[ci.code].relocType, ci.insttype);
  55. end;
  56. end;
  57. end;
  58. // searching back in the labels stack.
  59. // returning the "number" of steps to jump back to the label
  60. function GetJumpLabelIndex(const JumpToLbl: string; LblStack: TStrings): Integer;
  61. var
  62. i : integer;
  63. begin
  64. i:=LblStack.Count-1;
  65. while (i>=0) and (LblStack[i]<>JumpToLbl) do
  66. dec(i);
  67. Result := LblStack.Count-i-1;
  68. end;
  69. // Normalizing instruction list, popuplating index reference ($index)
  70. // with the actual numbers. (params, locals, globals, memory, functions index)
  71. //
  72. // pass "f" as nil, if instruction list doesn't belong to a function
  73. function NormalizeInst(m: TWasmModule; f: TWasmFunc; l: TWasmInstrList; checkEnd: boolean = true): Boolean;
  74. var
  75. i : integer;
  76. j : integer;
  77. ci : TWasmInstr;
  78. endNeed : Integer;
  79. lbl : TStringList;
  80. const
  81. ValidResTypes = [VALTYPE_NONE,VALTYPE_I32,VALTYPE_I64,VALTYPE_F32,VALTYPE_F64];
  82. begin
  83. Result := true;
  84. endNeed := 1;
  85. lbl := TStringList.Create;
  86. try
  87. for i:=0 to l.Count-1 do begin
  88. ci:=l[i];
  89. case INST_FLAGS[ci.code].Param of
  90. ipResType:
  91. begin
  92. inc(endNeed);
  93. if not (byte(ci.operandNum) in ValidResTypes) then
  94. ci.operandNum := VALTYPE_NONE;
  95. lbl.Add(ci.jumplabel);
  96. end;
  97. end;
  98. case ci.code of
  99. INST_local_get, INST_local_set, INST_local_tee:
  100. begin
  101. if not Assigned(f) then begin
  102. Result:=false;
  103. Exit;
  104. end;
  105. if (ci.operandIdx<>'') and (ci.operandNum<0) then begin
  106. j:=FindParam(f.functype.params, ci.operandIdx);
  107. if j<0 then begin
  108. j:=FindParam(f.locals, ci.operandIdx);
  109. if j>=0 then inc(j, f.functype.ParamCount);
  110. end;
  111. ci.operandNum:=j;
  112. end;
  113. end;
  114. INST_global_get, INST_global_set:
  115. begin
  116. if not Assigned(f) then begin
  117. Result:=false;
  118. Exit;
  119. end;
  120. if (ci.operandIdx<>'') and (ci.operandNum<0) then begin
  121. j:=FindGlobal(m, ci.operandIdx);
  122. ci.operandNum:=j;
  123. end;
  124. end;
  125. INST_call:
  126. begin
  127. if (ci.operandIdx<>'') and (ci.operandNum<0) then
  128. ci.operandNum:=FindFunc(m,ci.operandIdx);
  129. end;
  130. INST_call_indirect:
  131. begin
  132. if Assigned(ci.insttype) and (ci.insttype.typeNum<0) then begin
  133. if ci.insttype.typeIdx <> '' then
  134. ci.insttype.typeNum:=FindFuncType(m, ci.insttype.typeIdx)
  135. else
  136. ci.insttype.typeNum:=RegisterFuncType(m, ci.insttype);
  137. end;
  138. end;
  139. INST_br, INST_br_if, INST_br_table: begin
  140. if ci.code = INST_br_table then
  141. for j:=0 to ci.vecTableCount-1 do
  142. if ci.vecTable[j].id <> '' then
  143. ci.vecTable[j].idNum:=GetJumpLabelIndex(ci.vecTable[j].id, lbl);
  144. if ci.operandIdx<>'' then
  145. ci.operandNum:=GetJumpLabelIndex(ci.operandIdx, lbl);
  146. end;
  147. INST_END: begin
  148. dec(endNeed);
  149. if lbl.Count>0 then lbl.Delete(lbl.Count-1);
  150. end;
  151. end;
  152. PopulateRelocData(m, ci);
  153. end;
  154. // adding end instruction
  155. if checkEnd and (endNeed>0) then
  156. l.AddInstr(INST_END);
  157. finally
  158. lbl.Free;
  159. end;
  160. end;
  161. procedure NormalizeFuncType(m: TWasmModule; fn : TWasmFuncType);
  162. begin
  163. if fn.isNumOrIdx then begin
  164. if fn.typeIdx<>'' then
  165. fn.typeNum:=FindFuncType(m, fn.typeIdx);
  166. end else
  167. fn.typeNum:=RegisterFuncType(m, fn);
  168. end;
  169. procedure NormalizeImport(m: TWasmModule; out fnIdx: Integer;
  170. out memIdx: Integer; out globIdx: Integer; out tblIdx : Integer);
  171. var
  172. i : integer;
  173. im : TWasmImport;
  174. begin
  175. fnIdx := 0;
  176. memIdx := 0;
  177. globIdx := 0;
  178. tblIdx := 0;
  179. for i:=0 to m.ImportCount-1 do begin
  180. im := m.GetImport(i);
  181. if Assigned(im.fn) then begin
  182. im.fn.idNum:=fnIdx;
  183. NormalizeFuncType(m, im.fn.functype);
  184. inc(fnIdx);
  185. end else if Assigned(im.mem) then begin
  186. im.mem.id.idNum := memIdx;
  187. inc(memIdx);
  188. end else if Assigned(im.glob) then begin
  189. im.glob.id.idNum := globIdx;
  190. inc(globIdx);
  191. end else if Assigned(im.table) then begin
  192. im.table.id.idNum := tblIdx;
  193. inc(tblIdx);
  194. end;
  195. end;
  196. end;
  197. procedure NormalizeGlobals(m: TWasmModule);
  198. var
  199. i : integer;
  200. begin
  201. for i:=0 to m.GlobalCount-1 do
  202. m.GetGlobal(i).id.idNum:=i;
  203. end;
  204. procedure NormalizeTable(m: TWasmModule);
  205. var
  206. i : integer;
  207. j : integer;
  208. t : TWasmTable;
  209. se : TWasmElement;
  210. de : TWasmElement;
  211. begin
  212. for i:=0 to m.TableCount-1 do begin
  213. t := m.GetTable(i);
  214. t.id.idNum:=i; // todo: is it safe?
  215. end;
  216. for i:=0 to m.TableCount-1 do begin
  217. t := m.GetTable(i);
  218. if not Assigned(t.elem) then continue;
  219. se:=t.elem;
  220. de := m.AddElement;
  221. de.tableId.idNum := t.id.idNum;
  222. if t.elemsType = 0 then t.elemsType := ELEMTYPE_FUNC;
  223. de.funcCount:=se.funcCount;
  224. if se.funcCount>0 then begin
  225. SetLength(de.funcs, de.funcCount);
  226. for j:=0 to de.funcCount-1 do begin
  227. de.funcs[j].id := se.funcs[j].id;
  228. de.funcs[j].idNum := se.funcs[j].idNum;
  229. end;
  230. end;
  231. end;
  232. end;
  233. procedure NormalizeTableLimit(m: TWasmModule);
  234. var
  235. i : integer;
  236. elCount : integer;
  237. t : TWasmTable;
  238. begin
  239. elCount:=0;
  240. for i:=0 to m.ElementCount-1 do
  241. inc(elCount, m.GetElement(i).funcCount );
  242. for i:=0 to m.TableCount-1 do begin
  243. t := m.GetTable(i);
  244. if (t.min=0) and (t.max=0) then begin
  245. t.min := elCount;
  246. t.max := elCount;
  247. Break;
  248. end;
  249. end;
  250. end;
  251. // For each element on the module, funcId is replaced with the proper IdNum
  252. // it should only be called after all functions have their IDs resolved
  253. procedure NormalizeElems(m: TWasmModule);
  254. var
  255. i : integer;
  256. e : TWasmElement;
  257. j : integer;
  258. l : TWasmInstrList;
  259. begin
  260. //todo: resolve offsets
  261. for i:=0 to m.ElementCount-1 do begin
  262. e := m.GetElement(i);
  263. l := e.AddOffset;
  264. if (l.Count=0) then l.AddInstr(INST_i32_const).operand1.s32:=0;
  265. NormalizeInst( m, nil, l);
  266. for j := 0 to e.funcCount-1 do
  267. if e.funcs[j].idNum<0 then
  268. e.funcs[j].idNum := FindFunc(m, e.funcs[j].id);
  269. end;
  270. end;
  271. // normalizing reference
  272. procedure Normalize(m: TWasmModule);
  273. var
  274. i : integer;
  275. f : TWasmFunc;
  276. x : TWasmExport;
  277. fnIdx : Integer;
  278. memIdx : Integer;
  279. globIdx : Integer;
  280. tblIdx : Integer;
  281. g : TWasmGlobal;
  282. begin
  283. fnIdx := 0;
  284. NormalizeGlobals(m);
  285. NormalizeTable(m);
  286. NormalizeImport(m, fnIdx, memIdx, globIdx, tblIdx);
  287. NormalizeTableLimit(m);
  288. for i:=0 to m.FuncCount-1 do begin
  289. f:=m.GetFunc(i);
  290. f.idNum := fnIdx;
  291. NormalizeFuncType(m, f.functype);
  292. inc(fnIdx);
  293. end;
  294. NormalizeElems(m);
  295. for i:=0 to m.GlobalCount-1 do
  296. begin
  297. g := m.GetGlobal(i);
  298. g.id.idNum := globIdx;
  299. inc(globIdx);
  300. NormalizeInst(m, nil, g.StartValue);
  301. end;
  302. // normalizing function body
  303. for i:=0 to m.FuncCount-1 do begin
  304. f:=m.GetFunc(i);
  305. // finding the reference in functions
  306. // populating "nums" where string "index" is used
  307. NormalizeInst(m, f, f.instr);
  308. end;
  309. // normalizing exports
  310. for i:=0 to m.ExportCount-1 do begin
  311. x:=m.GetExport(i);
  312. if x.exportNum<0 then
  313. case x.exportType of
  314. EXPDESC_FUNC:
  315. if x.exportIdx<>'' then
  316. x.exportNum := FindFunc(m, x.exportIdx);
  317. end;
  318. end;
  319. end;
  320. end.