wasmbinwriter.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  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 wasmbinwriter;
  18. {$mode objfpc}{$H+}
  19. interface
  20. uses
  21. Classes, SysUtils, AVL_Tree,
  22. wasmmodule, wasmbin, lebutils, wasmbincode
  23. ,wasmlink;
  24. type
  25. TSectionRec = record
  26. secpos : int64;
  27. szpos : int64;
  28. datapos : int64;
  29. endofdata : int64;
  30. end;
  31. TSymbolObject = class(TObject)
  32. idx : Integer;
  33. syminfo : TSymInfo;
  34. next : TSymbolObject;
  35. wasmObj : TObject;
  36. end;
  37. { TBinWriter }
  38. TBinWriter = class
  39. protected
  40. dst : TStream;
  41. org : TStream;
  42. strm : TList;
  43. module : TWasmModule;
  44. // the list of relocations per module
  45. writeSec : Byte;
  46. reloc : array of TRelocationEntry;
  47. relocCount : integer;
  48. symHead : TSymbolObject;
  49. symTail : TSymbolObject;
  50. syms : TAVLTree;
  51. symCount : Integer;
  52. function AddSymbolObject(obj: TObject): TSymbolObject;
  53. procedure AddRelocWithIndex(relocType: byte; secOfs: int64; index: UInt32);
  54. procedure AddRelocToObj(relocType: byte; secOfs: int64; wasmObj: TObject);
  55. procedure WriteRelocU32(u: longword);
  56. procedure WriteString(const s: string);
  57. procedure SectionBegin(secId: byte; out secRec: TSectionRec; secsize: longWord=0);
  58. function SectionEnd(var secRec: TSectionRec): Boolean;
  59. procedure WriteInstList(list: TWasmInstrList; ofsAddition: LongWord);
  60. procedure WriteImportSect;
  61. procedure WriteFuncTypeSect;
  62. procedure WriteTableSect;
  63. procedure WriteMemorySect;
  64. procedure WriteGlobalSect;
  65. procedure WriteFuncSect;
  66. procedure WriteExportSect;
  67. procedure WriteCodeSect;
  68. procedure WriteElemSect;
  69. procedure PrepareLinkSym(m: TWasmModule);
  70. procedure WriteLinkingSect;
  71. procedure WriteRelocSect;
  72. procedure pushStream(st: TStream);
  73. function popStream: TStream;
  74. public
  75. keepLeb128 : Boolean; // keep leb128 at 4 offset relocatable
  76. writeReloc : Boolean; // writting relocation (linking) information
  77. constructor Create;
  78. destructor Destroy; override;
  79. function Write(m: TWasmModule; adst: TStream): Boolean;
  80. end;
  81. function WriteModule(m: TWasmModule; dst: TStream;
  82. awriteLEB128: Boolean = false;
  83. awriteReloc: Boolean = false): Boolean;
  84. type
  85. TLocalsInfo = record
  86. count : Integer;
  87. tp : byte;
  88. end;
  89. TLocalInfoArray = array of TLocalsInfo;
  90. // returns the list of local arrays
  91. procedure GetLocalInfo(func: TWasmFunc; out loc: TLocalInfoArray);
  92. procedure WriteLimit(dst: TStream; amin, amax: LongWord);
  93. implementation
  94. function ComparePtrUInt(p1,p2: PtrUInt): Integer; inline;
  95. begin
  96. if p1<p2 then Result:=-1
  97. else if p1=p2 then Result:=0
  98. else Result:=1;
  99. end;
  100. function CompareSymObjs(Item1, Item2: Pointer): Integer;
  101. var
  102. s1, s2: TSymbolObject;
  103. begin
  104. s1:=TSymbolObject(Item1);
  105. s2:=TSymbolObject(Item2);
  106. Result:=ComparePtrUInt(PtrUInt(s1.wasmObj), PtrUInt(s2.wasmObj));
  107. end;
  108. function CompareWasmToSymObj(Item1, Item2: Pointer): Integer;
  109. var
  110. s2: TSymbolObject;
  111. begin
  112. s2:=TSymbolObject(Item2);
  113. Result:=ComparePtrUInt(PtrUInt(Item1), PtrUInt(s2.wasmObj));
  114. end;
  115. procedure WriteLimit(dst: TStream; amin, amax: LongWord);
  116. begin
  117. if not Assigned(dst) then Exit;
  118. if amax>0 then begin
  119. dst.WriteByte(1);
  120. WriteU32(dst, amin);
  121. WriteU32(dst, amax);
  122. end else begin
  123. dst.WriteByte(0);
  124. WriteU32(dst, amin);
  125. end;
  126. end;
  127. procedure GetLocalInfo(func: TWasmFunc; out loc: TLocalInfoArray);
  128. var
  129. i : integer;
  130. cnt : integer;
  131. tp : byte;
  132. nt : byte;
  133. j : integer;
  134. procedure Push;
  135. begin
  136. if j=length(loc) then begin
  137. if j=0 then SetLength(loc, 1)
  138. else SetLength(loc, j*2);
  139. end;
  140. loc[j].tp:=tp;
  141. loc[j].count:=cnt;
  142. inc(j);
  143. end;
  144. begin
  145. SetLength(Loc, 0);
  146. if func.LocalsCount = 0 then Exit;
  147. cnt:=1;
  148. tp:=func.GetLocal(0).tp;
  149. j:=0;
  150. for i:=1 to func.LocalsCount-1 do begin
  151. nt := func.GetLocal(i).tp;
  152. if nt<>tp then begin
  153. Push;
  154. tp:=nt;
  155. cnt:=1;
  156. end else
  157. inc(cnt);
  158. end;
  159. Push;
  160. SetLength(loc, j);
  161. end;
  162. function WriteModule(m: TWasmModule; dst: TStream;
  163. awriteLEB128, awriteReloc: Boolean): Boolean;
  164. var
  165. bw : TBinWriter;
  166. begin
  167. bw := TBinWriter.Create;
  168. try
  169. bw.keepLeb128:=awriteLEB128;
  170. bw.writeReloc:=awriteReloc;
  171. Result := bw.Write(m, dst);
  172. finally
  173. bw.Free;
  174. end;
  175. end;
  176. { TBinWriter }
  177. function GetLinkName(const linkInfo: TLinkInfo; const id: string): string;
  178. begin
  179. if linkInfo.Name<>'' then Result:=linkInfo.Name
  180. else Result:=id;
  181. end;
  182. function TBinWriter.AddSymbolObject(obj: TObject): TSymbolObject;
  183. var
  184. so : TSymbolObject;
  185. t : TAVLTreeNode;
  186. begin
  187. t := syms.FindKey(obj, @CompareWasmToSymObj);
  188. if Assigned(t) then begin
  189. Result:=TSymbolObject(t.Data);
  190. Exit;
  191. end;
  192. so := TSymbolObject.Create;
  193. if not Assigned(symHead) then symHead:=so;
  194. if Assigned(symTail) then symTail.Next:=so;
  195. so.idx:=symCount;
  196. so.wasmObj:=obj;
  197. symTail:=so;
  198. inc(symCount);
  199. Result:=so;
  200. if (obj is TWasmFunc) then begin
  201. so.syminfo.kind:=SYMTAB_FUNCTION;
  202. so.syminfo.symindex:=TWasmFunc(obj).idNum;
  203. end else if (obj is TWasmGlobal) then begin
  204. so.syminfo.kind:=SYMTAB_GLOBAL;
  205. so.syminfo.symindex:=TWasmGlobal(obj).id.idNum;
  206. so.syminfo.symname:=GetLinkName(TWasmGlobal(obj).LinkInfo, TWasmGlobal(obj).id.id); //todo: use symbolic name
  207. end else if (obj is TWasmTable) then begin
  208. so.syminfo.kind:=SYMTAB_TABLE;
  209. so.syminfo.symindex:=TWasmTable(obj).id.idNum;
  210. end;
  211. syms.Add(so);
  212. end;
  213. procedure TBinWriter.AddRelocWithIndex(relocType: byte; secOfs: int64; index: UInt32);
  214. var
  215. i : integer;
  216. f : TWasmFunc;
  217. //so : TSymbolObject;
  218. begin
  219. if relocCount=length(reloc) then begin
  220. if relocCount=0 then SetLength(reloc, 16)
  221. else SetLength(reloc, relocCount*2);
  222. end;
  223. i:=relocCount;
  224. reloc[i].sec:=writeSec;
  225. reloc[i].reltype:=relocType;
  226. reloc[i].offset:=secOfs;
  227. reloc[i].index:=index;
  228. inc(relocCount);
  229. end;
  230. procedure TBinWriter.AddRelocToObj(relocType: byte; secOfs: int64; wasmObj: TObject);
  231. var
  232. idx : integer;
  233. begin
  234. if not Assigned(wasmObj) then Exit;
  235. idx:=AddSymbolObject(wasmObj).idx;
  236. AddRelocWithIndex(relocType, secOfs, idx);
  237. end;
  238. procedure TBinWriter.WriteRelocU32(u: longword);
  239. begin
  240. WriteU(dst, u, sizeof(u)*8, keepLeb128);
  241. end;
  242. procedure TBinWriter.WriteString(const s: string);
  243. begin
  244. WriteU32(dst, length(s));
  245. if length(s)>0 then
  246. dst.Write(s[1], length(s));
  247. end;
  248. function TBinWriter.Write(m: TWasmModule; adst: TStream): Boolean;
  249. var
  250. l : Longword;
  251. begin
  252. if not Assigned(m) or not Assigned(adst) then begin
  253. Result:=false;
  254. Exit;
  255. end;
  256. keepLeb128:=keepLeb128 or writeReloc; // use 128, if relocation has been requested
  257. module:=m;
  258. dst:=adst;
  259. org:=adst;
  260. dst.Write(WasmId_Buf, length(WasmId_Buf));
  261. l:=NtoLE(Wasm_Version1);
  262. dst.Write(l, sizeof(l));
  263. writeSec:=0;
  264. // 01 function type section
  265. if m.TypesCount>0 then begin
  266. WriteFuncTypeSect;
  267. inc(writeSec);
  268. end;
  269. // 02 import section
  270. if m.ImportCount>0 then begin
  271. WriteImportSect;
  272. inc(writeSec);
  273. end;
  274. // 03 function section
  275. if m.FuncCount>0 then begin
  276. WriteFuncSect;
  277. inc(writeSec);
  278. end;
  279. // 04 tables section
  280. if m.TableCount>0 then begin
  281. WriteTableSect;
  282. inc(writeSec);
  283. end;
  284. // 05 memory section
  285. if m.MemoryCount>0 then begin
  286. WriteMemorySect;
  287. inc(writeSec);
  288. end;
  289. // 06 globals section
  290. if m.GlobalCount>0 then begin
  291. WriteGlobalSect;
  292. inc(writeSec);
  293. end;
  294. // 07 export section
  295. if m.ExportCount>0 then begin
  296. WriteExportSect;
  297. inc(writeSec);
  298. end;
  299. // 09 - element sections
  300. if m.ElementCount>0 then begin
  301. WriteElemSect;
  302. inc(writeSec);
  303. end;
  304. // 10 code section
  305. if m.FuncCount>0 then begin
  306. WriteCodeSect;
  307. inc(writeSec);
  308. end;
  309. if writeReloc then begin
  310. PrepareLinkSym(m);
  311. WriteLinkingSect;
  312. WriteRelocSect;
  313. end;
  314. Result:=true;
  315. end;
  316. procedure TBinWriter.SectionBegin(secId: byte; out secRec: TSectionRec; secsize: longWord=0);
  317. begin
  318. secRec.secpos:=dst.Position;
  319. dst.WriteByte(secId);
  320. secRec.szpos:=dst.Position;
  321. WriteRelocU32(secsize);
  322. secRec.datapos:=dst.Position;
  323. secRec.endofdata:=dst.Position+secsize;
  324. end;
  325. function TBinWriter.SectionEnd(var secRec: TSectionRec): Boolean;
  326. var
  327. sz: LongWord;
  328. begin
  329. secRec.endofdata:=dst.Position;
  330. dst.Position:=secRec.szpos;
  331. sz := secRec.endofdata - secRec.datapos;
  332. WriteRelocU32(sz);
  333. dst.Position:=secRec.endofdata;
  334. Result := true;
  335. end;
  336. procedure TBinWriter.WriteFuncTypeSect;
  337. var
  338. sc : TSectionRec;
  339. i : integer;
  340. j : integer;
  341. tp : TWasmFuncType;
  342. begin
  343. SectionBegin(SECT_TYPE, sc);
  344. WriteU32(dst, module.TypesCount);
  345. for i:=0 to module.TypesCount-1 do begin
  346. tp:=module.GetType(i);
  347. dst.WriteByte(func_type);
  348. WriteU32(dst, tp.ParamCount);
  349. for j:=0 to tp.ParamCount-1 do
  350. dst.WriteByte(tp.GetParam(j).tp);
  351. WriteU32(dst, tp.ResultCount);
  352. for j:=0 to tp.ResultCount-1 do
  353. dst.WriteByte(tp.GetResult(j).tp);
  354. end;
  355. SectionEnd(sc);
  356. end;
  357. procedure TBinWriter.WriteTableSect;
  358. var
  359. sc : TSectionRec;
  360. i : integer;
  361. t : TWasmTable;
  362. begin
  363. SectionBegin(SECT_TABLE, sc);
  364. WriteU32(dst, module.TableCount);
  365. for i:=0 to module.TableCount-1 do begin
  366. t:=module.GetTable(i);
  367. dst.WriteByte(t.elemsType);
  368. WriteLimit(dst, t.min, t.max);
  369. end;
  370. SectionEnd(sc);
  371. end;
  372. procedure TBinWriter.WriteMemorySect;
  373. var
  374. sc : TSectionRec;
  375. i : integer;
  376. m : TWasmMemory;
  377. begin
  378. SectionBegin(SECT_MEMORY, sc);
  379. WriteU32(dst, module.MemoryCount);
  380. for i:=0 to module.MemoryCount-1 do begin
  381. m := module.GetMemory(i);
  382. WriteLimit(dst, m.min, m.max);
  383. end;
  384. SectionEnd(sc);
  385. end;
  386. procedure TBinWriter.WriteGlobalSect;
  387. var
  388. sc : TSectionRec;
  389. i : integer;
  390. g : TWasmGlobal;
  391. begin
  392. SectionBegin(SECT_GLOBAL, sc);
  393. WriteU32(dst, module.GlobalCount);
  394. for i:=0 to module.GlobalCount-1 do begin
  395. g := module.GetGlobal(i);
  396. dst.WriteByte(g.tp);
  397. if g.isMutable then dst.WriteByte(1)
  398. else dst.WriteByte(0);
  399. WriteInstList(g.StartValue, sc.datapos);
  400. end;
  401. SectionEnd(sc);
  402. end;
  403. procedure TBinWriter.WriteFuncSect;
  404. var
  405. sc : TSectionRec;
  406. i : integer;
  407. begin
  408. SectionBegin(SECT_FUNCTION, sc);
  409. WriteU32(dst, module.FuncCount);
  410. for i:=0 to module.FuncCount-1 do
  411. // wat2test doesn't write the function section as relocatable
  412. // WriteRelocU32(m.GetFunc(i).functype.typeNum);
  413. WriteU32(dst, module.GetFunc(i).functype.typeNum);
  414. SectionEnd(sc);
  415. end;
  416. procedure TBinWriter.WriteExportSect;
  417. var
  418. sc : TSectionRec;
  419. i : integer;
  420. x : TWasmExport;
  421. begin
  422. SectionBegin(SECT_EXPORT, sc);
  423. WriteU32(dst, module.ExportCount);
  424. for i:=0 to module.ExportCount-1 do begin
  425. x:=module.GetExport(i);
  426. WriteU32(dst, length(x.name));
  427. if length(x.name)>0 then
  428. dst.Write(x.name[1], length(x.name));
  429. dst.WriteByte(x.exportType);
  430. //wat2wasm doesn't write relocate the information
  431. //WriteRelocU32(x.exportNum);
  432. WriteU32(dst, x.exportNum);
  433. end;
  434. SectionEnd(sc);
  435. end;
  436. procedure TBinWriter.WriteCodeSect;
  437. var
  438. sc : TSectionRec;
  439. i, j : integer;
  440. sz : int64;
  441. mem : TMemoryStream;
  442. la : TLocalInfoArray;
  443. f : TWasmFunc;
  444. dofs : Int64;
  445. fnofs : Int64; // function offset in the data of "code section"
  446. main : TMemoryStream;
  447. begin
  448. // for the use of leb128, the header can be written ahead of the body
  449. // as the size of the section would always take 5 bytes.
  450. // for not forcing leb128, the size of the body must be known ahead of time
  451. if keepLeb128 then begin
  452. SectionBegin(SECT_CODE, sc);
  453. dofs := dst.Position;
  454. end else
  455. dofs := 0; // we don't really care. dofs only matters for relocation+keepLeb128
  456. main:=TMemoryStream.Create;
  457. mem:=TMemoryStream.Create;
  458. try
  459. pushStream(main);
  460. WriteU32(dst, module.FuncCount);
  461. for i :=0 to module.FuncCount-1 do begin
  462. f:=module.GetFunc(i);
  463. GetLocalInfo(f, la);
  464. mem.Position:=0;
  465. fnofs := dofs + main.Position + 5; // "la" will be written after, 5 is for the writeSize. +5 is for WriteRelocU32(sz)
  466. pushStream(mem);
  467. WriteU32(dst, length(la));
  468. for j:=0 to length(la)-1 do begin
  469. WriteU32(dst, la[j].count);
  470. dst.WriteByte(la[j].tp);
  471. end;
  472. WriteInstList(f.instr, LongWord(fnofs-sc.datapos));
  473. popStream;
  474. sz:=mem.Position;
  475. mem.Position:=0;
  476. WriteRelocU32(sz);
  477. dst.CopyFrom(mem, sz);
  478. end;
  479. popStream;
  480. if not keepLeb128 then
  481. SectionBegin(SECT_CODE, sc, main.Size);
  482. main.Position:=0;
  483. dst.CopyFrom(main, main.Size);
  484. finally
  485. mem.Free;
  486. main.Free;
  487. end;
  488. SectionEnd(sc);
  489. end;
  490. procedure TBinWriter.WriteElemSect;
  491. var
  492. sc : TSectionRec;
  493. el : TWasmElement;
  494. i : Integer;
  495. j : Integer;
  496. begin
  497. SectionBegin(SECT_ELEMENT, sc);
  498. WriteU32(dst, module.ElementCount);
  499. for i:=0 to module.ElementCount-1 do begin
  500. el := module.GetElement(i);
  501. WriteU32(dst, el.tableId.idNum);
  502. WriteInstList(el.offset, sc.datapos);
  503. WriteU32(dst, el.funcCount);
  504. if writeReloc then begin
  505. for j:=0 to el.funcCount-1 do begin
  506. AddRelocToObj(R_WASM_FUNCTION_INDEX_LEB, dst.Position - sc.datapos,
  507. GetFuncByNum(module, el.funcs[j].idNum));
  508. WriteRelocU32(el.funcs[j].idNum);
  509. end;
  510. end else
  511. for j:=0 to el.funcCount-1 do
  512. WriteU32(dst, el.funcs[j].idNum);
  513. end;
  514. SectionEnd(sc);
  515. end;
  516. procedure TBinWriter.WriteLinkingSect;
  517. var
  518. sc : TSectionRec;
  519. mem : TMemoryStream;
  520. so : TSymbolObject;
  521. begin
  522. SectionBegin(SECT_CUSTOM, sc);
  523. WriteString(SectionName_Linking);
  524. WriteU32(dst, LINKING_VERSION);
  525. if symCount>0 then begin
  526. dst.WriteByte(WASM_SYMBOL_TABLE);
  527. mem := TMemoryStream.Create;
  528. try
  529. pushStream(mem);
  530. //WriteU32(dst, symCount);
  531. WriteRelocU32(symCount);
  532. so:=symHead;
  533. while Assigned(so) do begin
  534. dst.WriteByte(so.syminfo.kind);
  535. WriteU32(dst, so.syminfo.flags);
  536. WriteU32(dst, so.syminfo.symindex);
  537. //if ((so.syminfo.flags and WASM_SYM_EXPLICIT_NAME)>0) then begin
  538. WriteU32(dst, length(so.syminfo.symname));
  539. dst.Write(so.syminfo.symname[1], length(so.syminfo.symname));
  540. //end;
  541. so:=so.next;
  542. end;
  543. popStream;
  544. mem.Position:=0;
  545. WriteU32(dst, mem.Size);
  546. dst.CopyFrom(mem, mem.size);
  547. finally
  548. mem.Free;
  549. end;
  550. end;
  551. // todo: fill out subsections
  552. SectionEnd(sc);
  553. end;
  554. procedure TBinWriter.WriteRelocSect;
  555. var
  556. i : integer;
  557. j : integer;
  558. si : Byte;
  559. cnt: integer;
  560. sc : TSectionRec;
  561. begin
  562. si:=0;
  563. i:=0;
  564. while (si<writeSec) and (i<relocCount) do begin
  565. if reloc[i].sec=si then begin
  566. SectionBegin(SECT_CUSTOM, sc);
  567. WriteString(SectionNamePfx_Reloc+'Code');
  568. j:=i;
  569. cnt:=0;
  570. while (i<relocCount) and (reloc[i].sec=si) do begin
  571. inc(cnt);
  572. inc(i);
  573. end;
  574. WriteU32(dst, reloc[j].sec);
  575. WriteU32(dst, cnt);
  576. for j:=j to i-1 do begin
  577. dst.WriteByte(reloc[j].reltype);
  578. WriteU32(dst, reloc[j].offset);
  579. WriteU32(dst, reloc[j].index);
  580. end;
  581. SectionEnd(sc);
  582. end;
  583. inc(si);
  584. end;
  585. end;
  586. procedure TBinWriter.WriteInstList(list: TWasmInstrList; ofsAddition: LongWord);
  587. var
  588. i : integer;
  589. j : integer;
  590. ci : TWasmInstr;
  591. rt : Byte;
  592. begin
  593. for i:=0 to list.Count-1 do begin
  594. ci :=list[i];
  595. dst.WriteByte(ci.code);
  596. if ci.hasRelocIdx then begin
  597. rt := ci.relocType;
  598. AddRelocToObj(rt, dst.Position+ofsAddition, ci.relocObj);
  599. end;
  600. case INST_FLAGS[ci.code].Param of
  601. ipi32: WriteS(dst, ci.operand1.s32, sizeof(ci.operand1.s32));
  602. ipi64: WriteS64(dst, ci.operand1.s64);
  603. ipu32: WriteU32(dst, ci.operand1.u32);
  604. ipu64: WriteS64(dst, Int64(ci.operand1.u64));
  605. ipf32: dst.Write(ci.operand1.f32, sizeof(ci.operand1.f32));
  606. ipf64: dst.Write(ci.operand1.f64, sizeof(ci.operand1.f64));
  607. ipi32OrFunc: begin
  608. if ci.hasRelocIdx then
  609. // should have been populated with Normalize
  610. WriteRelocU32(LongWord(ci.operand1.u32)) // todo!
  611. else
  612. WriteS(dst, ci.operand1.s32, sizeof(ci.operand1.s32));
  613. end;
  614. //ipf32, // float point single
  615. //ipf64, // float point double
  616. ipLeb:
  617. begin
  618. if ci.hasRelocIdx then
  619. WriteRelocU32(ci.operandNum)
  620. else
  621. WriteU32(dst, ci.operandNum);
  622. end;
  623. ipCallType:
  624. begin
  625. if Assigned(ci.insttype) then begin
  626. if ci.hasRelocIdx
  627. then WriteRelocU32(ci.insttype.typeNum)
  628. else WriteU32(dst, ci.insttype.typeNum);
  629. end else
  630. WriteU32(dst, LongWord(-1)); // this is an error.
  631. // table index reference
  632. WriteU32(dst, ci.operandNum);
  633. end;
  634. ipJumpVec: begin
  635. writeU32(dst, ci.vecTableCount);
  636. for j:=0 to ci.vecTableCount-1 do
  637. WriteU32(dst, ci.vecTable[j].idNum);
  638. WriteU32(dst, ci.operandNum);
  639. end;
  640. ipResType:
  641. dst.WriteByte(byte(ci.operandNum));
  642. ipZero:
  643. dst.WriteByte(0);
  644. ipOfsAlign: begin
  645. // align
  646. WriteU32(dst, ci.operand2.u32);
  647. // offset
  648. WriteU32(dst, ci.operand1.u32);
  649. end;
  650. end;
  651. end;
  652. end;
  653. procedure TBinWriter.WriteImportSect;
  654. var
  655. sc : TSectionRec;
  656. i : integer;
  657. im : TWasmImport;
  658. const
  659. isMutableFlag : array [boolean] of byte = (global_const, global_mut);
  660. begin
  661. SectionBegin(SECT_IMPORT, sc);
  662. WriteU32(dst, module.ImportCount);
  663. for i:=0 to module.ImportCount-1 do begin
  664. im:=module.GetImport(i);
  665. WriteString(im.module);
  666. WriteString(im.name);
  667. if Assigned(im.fn) then begin
  668. dst.WriteByte(IMPDESC_FUNC);
  669. WriteU32(dst, im.fn.functype.typeNum);
  670. end else if Assigned(im.mem) then begin
  671. dst.WriteByte(IMPDESC_MEM);
  672. WriteLimit(dst, im.mem.min, im.mem.max)
  673. end else if Assigned(im.table) then begin
  674. dst.WriteByte(IMPDESC_TABLE);
  675. dst.WriteByte(im.table.elemsType);
  676. WriteLimit(dst, im.table.min, im.table.max);
  677. end else if Assigned(im.glob) then begin
  678. dst.WriteByte(IMPDESC_GLOBAL);
  679. dst.WriteByte(im.glob.tp);
  680. dst.WriteByte(isMutableFlag[im.glob.isMutable]);
  681. end;
  682. end;
  683. SectionEnd(sc);
  684. end;
  685. procedure TBinWriter.pushStream(st: TStream);
  686. begin
  687. if st=nil then Exit;
  688. strm.Add(st);
  689. dst:=st;
  690. end;
  691. function TBinWriter.popStream: TStream;
  692. begin
  693. if strm.Count=0 then
  694. Result:=nil
  695. else begin
  696. Result:=TStream(strm[strm.Count-1]);
  697. strm.Delete(strm.Count-1);
  698. end;
  699. if strm.Count=0 then dst:=org
  700. else dst:=TStream(strm[strm.Count-1]);
  701. end;
  702. constructor TBinWriter.Create;
  703. begin
  704. inherited Create;
  705. strm:=TList.Create;
  706. syms:=TAVLTree.Create(@CompareSymObjs);
  707. end;
  708. destructor TBinWriter.Destroy;
  709. begin
  710. syms.Free;
  711. strm.Free;
  712. inherited Destroy;
  713. end;
  714. function isFuncLinkSym(const l: TLinkInfo): boolean;
  715. begin
  716. Result:=(l.Binding<>lbUndefined)
  717. or l.isHidden
  718. or l.isUndefined
  719. or l.NoStrip;
  720. end;
  721. procedure LinkInfoToBin(const src: TLinkInfo; var dst: TSymInfo; ASymTab: byte; objFnIdx: longword);
  722. begin
  723. dst.kind := ASymTab;
  724. dst.flags := 0;
  725. case src.Binding of
  726. lbWeak: dst.flags := dst.flags or WASM_SYM_BINDING_WEAK;
  727. lbLocal: dst.flags := dst.flags or WASM_SYM_BINDING_LOCAL;
  728. lbForHost: dst.flags := dst.flags or WASM_SYM_EXPORTED;
  729. end;
  730. if src.isHidden then dst.flags := dst.flags or WASM_SYM_VISIBILITY_HIDDEN;
  731. if src.isUndefined then dst.flags := dst.flags or WASM_SYM_UNDEFINED;
  732. if src.NoStrip then dst.flags := dst.flags or WASM_SYM_NO_STRIP;
  733. dst.symindex := objFnIdx;
  734. dst.hasSymIndex := ASymTab<>SYMTAB_DATA;
  735. dst.hasSymName := src.Name<>'';
  736. if (dst.hasSymName) then begin
  737. dst.flags := dst.flags or WASM_SYM_EXPLICIT_NAME;
  738. dst.symname := src.Name;
  739. end;
  740. end;
  741. procedure TBinWriter.PrepareLinkSym(m: TWasmModule);
  742. var
  743. i : integer;
  744. f : TWasmFunc;
  745. so : TSymbolObject;
  746. begin
  747. for i:=0 to m.FuncCount-1 do begin
  748. f := m.GetFunc(i);
  749. if isFuncLinkSym(f.LinkInfo) then begin
  750. if f.LinkInfo.Name ='' then f.LinkInfo.Name := f.id;
  751. so:=AddSymbolObject(f);
  752. LinkInfoToBin(f.linkInfo, so.syminfo, SYMTAB_FUNCTION, f.idNum);
  753. end;
  754. end;
  755. end;
  756. end.