wasmbin.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  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 wasmbin;
  18. {$mode objfpc}{$H+}
  19. interface
  20. uses
  21. Classes, SysUtils, lebutils, wasmbincode;
  22. const
  23. valtype_i32 = $7f;
  24. valtype_i64 = $7e;
  25. valtype_f32 = $7d;
  26. valtype_f64 = $7C;
  27. block_type = $40;
  28. func_type = $60;
  29. elem_type = $70;
  30. global_const = $00;
  31. global_mut = $01;
  32. limit_min_inf = $00; // minimum - to infinity
  33. limit_min_max = $01; // minimum - maximum
  34. const
  35. WasmId = #0'asm';
  36. WasmId_Int = $6D736100;
  37. Wasm_Version1 = 1;
  38. var
  39. WasmId_Buf : array [0..3] of char = (#0, 'a','s','m');
  40. type
  41. TLimit = record
  42. limitType : byte;
  43. min : UInt32;
  44. max : UInt32;
  45. end;
  46. TMemoryType = TLimit;
  47. TTableType = record
  48. elemType : Byte; // see "elem_type"
  49. limits : TLimit;
  50. end;
  51. TGlobalType = record
  52. valtype : Byte; // see "valtype_" consts
  53. mut : Byte; // see "global_" consts
  54. end;
  55. const
  56. SECT_CUSTOM = 0; // custom section
  57. SECT_TYPE = 1; // type section
  58. SECT_IMPORT = 2; // import section
  59. SECT_FUNCTION = 3; // function section
  60. SECT_TABLE = 4; // table section
  61. SECT_MEMORY = 5; // memory section
  62. SECT_GLOBAL = 6; // global section
  63. SECT_EXPORT = 7; // export section
  64. SECT_START = 8; // start section
  65. SECT_ELEMENT = 9; // element section
  66. SECT_CODE = 10; // code section
  67. SECT_DATA = 11; // data section
  68. type
  69. TSection = packed record
  70. id : byte;
  71. size : LongWord; // it is Leb128 encoded in the file, thus cannot be read directly
  72. end;
  73. TFuncType = record
  74. param : array of byte;
  75. result : array of byte;
  76. end;
  77. TFuncTypeArray = record
  78. funTypes : array of TFuncType;
  79. end;
  80. TCodeLocalEntry = record
  81. count : LongWord;
  82. valtyp : Byte;
  83. end;
  84. TCodeInstr = record
  85. inst : byte;
  86. idxArr: array of LongWord;
  87. case byte of
  88. 0: (align, offset : LongWord);
  89. 1: (index: LongWord);
  90. 2: (i32: LongWord);
  91. 3: (i64: UInt64);
  92. 4: (f32: single);
  93. 5: (f64: double);
  94. // for labels
  95. 6: (idxCount: integer;
  96. idxDef :LongWord);
  97. 7: (returnType: byte);
  98. end;
  99. TCodeEntry = record
  100. locals : array of TCodeLocalEntry;
  101. instBuf : array of byte;
  102. end;
  103. TCodeSection = record
  104. entries : array of TCodeEntry;
  105. end;
  106. const
  107. IMPDESC_FUNC = $00;
  108. IMPDESC_TABLE = $01;
  109. IMPDESC_MEM = $02;
  110. IMPDESC_GLOBAL = $03;
  111. type
  112. TImportEntry = record
  113. module : string;
  114. name : string;
  115. case desc: byte of
  116. IMPDESC_FUNC : (
  117. fnType : UInt32;
  118. );
  119. IMPDESC_TABLE: (
  120. tblType : TTableType;
  121. );
  122. IMPDESC_TABLE: (
  123. memType : TMemoryType;
  124. );
  125. IMPDESC_GLOBAL: (
  126. glbType : TGlobalType;
  127. );
  128. end;
  129. TImportSection = record
  130. entries : array of TImportEntry;
  131. end;
  132. const
  133. EXPDESC_FUNC = $00;
  134. EXPDESC_TABLE = $01;
  135. EXPDESC_MEM = $02;
  136. EXPDESC_GLOBAL = $03;
  137. type
  138. TExportEntry = record
  139. name : string;
  140. desc : byte;
  141. index : UInt32;
  142. end;
  143. TExportSection = record
  144. entries : array of TExportEntry;
  145. end;
  146. const
  147. ELEMTYPE_FUNC = $70;
  148. type
  149. TElementEntry = record
  150. table : Uint32;
  151. expr : array of byte; // instructions
  152. funcs : array of UInt32;
  153. end;
  154. TElementSection = record
  155. entries : array of TElementEntry;
  156. end;
  157. function SectionIdToStr(id: integer): string;
  158. function ValTypeToStr(id: integer): string;
  159. // reads the name from the input stream
  160. // the name consists of
  161. // size - in butes Leb128
  162. // bytes - in utf8 format
  163. function ReadName(st: TStream): string;
  164. procedure WriteName(st: TStream; const str: string);
  165. function GetName(sr: TStream): string;
  166. // reads
  167. function GetU32(sr: TStream): UInt32;
  168. // reads the code entry into TCodeEntry structure
  169. procedure ReadCodeEntry(src: TStream; var en: TCodeEntry);
  170. // reads the code entry into TCodeEntry structure
  171. procedure ReadCodeSection(src: TStream; var sc: TCodeSection);
  172. function isUnreachable(const cd: TCodeEntry): Boolean;
  173. procedure ReadExportEntry(src: TStream; var ex: TExportEntry);
  174. // reads the export entry
  175. procedure ReadExport(src: TStream; var ex: TExportSection);
  176. procedure WriteExport(const ex: TExportSection; dst: TStream);
  177. function isWasmStream(st: TStream): Boolean;
  178. function isWasmFile(const fn: string): Boolean;
  179. procedure ReadElementEntry(st: TStream; var en: TElementEntry);
  180. procedure ReadElementSection(st: TStream; var sc: TelementSection);
  181. procedure ReadLimit(st: TStream; var lm: TLimit);
  182. procedure ReadTableType(st: TStream; var tb: TTableType);
  183. procedure ReadGlobalType(st: TStream; var gb: TGlobalType);
  184. function ReadImportEntry(st: TStream; var imp: TImportEntry): Boolean;
  185. function ReadImportSection(st: TStream; var imp: TImportSection): Boolean;
  186. implementation
  187. procedure ReadLimit(st: TStream; var lm: TLimit);
  188. begin
  189. lm.limitType := st.ReadByte;
  190. lm.min := ReadU(st);
  191. if lm.limitType <> limit_min_inf then
  192. lm.max := ReadU(st)
  193. else
  194. lm.max := 0;
  195. end;
  196. procedure ReadTableType(st: TStream; var tb: TTableType);
  197. begin
  198. tb.elemType := st.ReadByte;
  199. ReadLimit(st, tb.limits);
  200. end;
  201. procedure ReadGlobalType(st: TStream; var gb: TGlobalType);
  202. begin
  203. gb.valtype := st.ReadByte;
  204. gb.mut := st.ReadByte;
  205. end;
  206. function ValTypeToStr(id: integer): string;
  207. begin
  208. case id of
  209. valtype_i32 : Result := 'i32';
  210. valtype_i64 : Result := 'i64';
  211. valtype_f32 : Result := 'f32';
  212. valtype_f64 : Result := 'f64';
  213. else
  214. Str(id, Result);
  215. Result := 'iUnk'+Result;
  216. end
  217. end;
  218. function SectionIdToStr(id: integer): string;
  219. begin
  220. case id of
  221. sect_custom : Result := 'custom'; // custom section
  222. sect_type : Result := 'type'; // type section
  223. sect_import : Result := 'import'; // import section
  224. sect_function : Result := 'function'; // function section
  225. sect_table : Result := 'table'; // table section
  226. sect_memory : Result := 'memory'; // memory section
  227. sect_global : Result := 'global'; // global section
  228. sect_export : Result := 'export'; // export section
  229. sect_start : Result := 'start'; // start section
  230. sect_element : Result := 'element'; // element section
  231. sect_code : Result := 'code'; // code section
  232. sect_data : Result := 'data'; // data section
  233. else
  234. Str(id, Result);
  235. Result := 'sect_unknown'+Result;
  236. end;
  237. end;
  238. function ReadName(st: TStream): string;
  239. var
  240. ln : LongWord;
  241. begin
  242. ln := ReadU(st);
  243. SetLength(result, ln);
  244. if ln>0 then st.Read(result[1], ln);
  245. end;
  246. procedure WriteName(st: TStream; const str: string);
  247. begin
  248. WriteU32(st, length(str));
  249. if length(str)>0 then
  250. st.Write(str[1], length(str));
  251. end;
  252. function GetName(sr: TStream): string;
  253. begin
  254. Result := ReadName(sr);
  255. end;
  256. function GetU32(sr: TStream): UInt32;
  257. begin
  258. Result := UInt32(ReadU(sr));
  259. end;
  260. procedure ReadCodeEntry(src: TStream; var en: TCodeEntry);
  261. var
  262. sz : integer; // size in bytes
  263. //pos : int64;
  264. cnt : Integer;
  265. i : integer;
  266. eofs : Int64;
  267. begin
  268. sz := ReadU(src);
  269. eofs := src.Position+sz;
  270. cnt := ReadU(src);
  271. SetLength(en.locals, cnt);
  272. for i:=0 to cnt-1 do begin
  273. en.locals[i].count := ReadU(src);
  274. en.locals[i].valtyp := src.ReadByte;
  275. end;
  276. SetLength(en.instBuf, eofs-src.Position);
  277. if (length(en.instBuf)>0) then
  278. src.Read(en.instBuf[0], length(en.instBuf));
  279. end;
  280. procedure ReadCodeSection(src: TStream; var sc: TCodeSection);
  281. var
  282. cnt : integer;
  283. i : integer;
  284. begin
  285. cnt := ReadU(src);
  286. SetLength(sc.entries, cnt);
  287. for i:= 0 to cnt-1 do
  288. ReadCodeEntry(src, sc.entries[i]);
  289. end;
  290. function isUnreachable(const cd: TCodeEntry): Boolean;
  291. begin
  292. Result:=(length(cd.instBuf)>0) and (cd.instBuf[0]=INST_TRAP);
  293. end;
  294. procedure ReadExportEntry(src: TStream; var ex: TExportEntry);
  295. begin
  296. ex.name := ReadName(src);
  297. ex.desc := src.ReadByte;
  298. ex.index := ReadU(src);
  299. end;
  300. procedure ReadExport(src: TStream; var ex: TExportSection);
  301. var
  302. cnt : integer;
  303. i : integer;
  304. begin
  305. cnt := ReadU(src);
  306. SetLength(ex.entries, cnt);
  307. for i:=0 to cnt-1 do
  308. ReadExportEntry(src, ex.entries[i]);
  309. end;
  310. procedure WriteExport(const ex: TExportSection; dst: TStream);
  311. var
  312. i : integer;
  313. begin
  314. WriteU32(dst, length(ex.entries));
  315. for i:=0 to length(ex.entries)-1 do begin
  316. WriteName(dst, ex.entries[i].name);
  317. dst.WriteByte(ex.entries[i].desc);
  318. WriteU32(dst, ex.entries[i].index);
  319. end;
  320. end;
  321. function isWasmStream(st: TStream): Boolean;
  322. var
  323. pos : Int64;
  324. begin
  325. try
  326. pos:=st.Position;
  327. try
  328. Result := st.ReadDWord = WasmId_Int;
  329. finally
  330. st.Position:=pos;
  331. end;
  332. except
  333. Result:=false;
  334. end;
  335. end;
  336. function isWasmFile(const fn: string): Boolean;
  337. var
  338. fs: TFileStream;
  339. begin
  340. try
  341. fs:=TFileStream.Create(fn, fmOpenRead or fmShareDenyNone);
  342. try
  343. Result:=isWasmStream(fs);
  344. finally
  345. fs.Free;
  346. end;
  347. except
  348. Result := false;
  349. end;
  350. end;
  351. procedure ReadElementEntry(st: TStream; var en: TElementEntry);
  352. var
  353. ln : integer;
  354. i : integer;
  355. begin
  356. en.table := ReadU(st);
  357. ln:=InstLen(st);
  358. if ln<0 then Exit;
  359. SetLength(en.expr, ln);
  360. if ln>0 then st.Read(en.expr[0], ln);
  361. ln:=ReadU(st);
  362. SetLength(en.funcs, ln);
  363. for i:=0 to ln-1 do
  364. en.funcs[i]:=ReadU(st);
  365. end;
  366. procedure ReadElementSection(st: TStream; var sc: TelementSection);
  367. var
  368. cnt : integer;
  369. i : integer;
  370. begin
  371. cnt := ReadU(st);
  372. SetLength(sc.entries, cnt);
  373. for i:=0 to cnt-1 do
  374. ReadElementEntry(st, sc.entries[i]);
  375. end;
  376. function ReadImportEntry(st: TStream; var imp: TImportEntry): Boolean;
  377. begin
  378. Result := true;
  379. imp.module := ReadName(st);
  380. imp.name := ReadName(st);
  381. imp.desc := st.ReadByte;
  382. case imp.desc of
  383. IMPDESC_FUNC : imp.fnType := ReadU(st);
  384. IMPDESC_TABLE: ReadTableType(st, imp.tblType);
  385. IMPDESC_MEM: ReadLimit(st, imp.memType);
  386. IMPDESC_GLOBAL: ReadGlobalType(st, imp.glbType);
  387. else
  388. Result := false;
  389. end;
  390. end;
  391. function ReadImportSection(st: TStream; var imp: TImportSection): Boolean;
  392. var
  393. cnt : integer;
  394. i : integer;
  395. begin
  396. cnt := ReadU(st);
  397. SetLength(imp.entries, cnt);
  398. Result := true;
  399. if cnt>0 then
  400. for i:=0 to cnt-1 do
  401. if not ReadImportEntry(st, imp.entries[i]) then begin
  402. Result := false;
  403. break;
  404. end;
  405. end;
  406. end.