wasmbinwriter.pas 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. unit wasmbinwriter;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, wasmmodule, wasmbin, lebutils, wasmbincode
  6. ,wasmlink;
  7. type
  8. TSectionRec = record
  9. secpos : int64;
  10. szpos : int64;
  11. datapos : int64;
  12. endofdata : int64;
  13. end;
  14. { TBinWriter }
  15. TBinWriter = class
  16. protected
  17. dst : TStream;
  18. org : TStream;
  19. strm : TList;
  20. // the list of relocations per module
  21. reloc : array of TRelocationEntry;
  22. relocCount : integer;
  23. recOfs : int64;
  24. procedure AddReloc(relocType: byte; ofs: int64; index: UInt32);
  25. procedure WriteRelocU32(u: longword);
  26. procedure SectionBegin(secId: byte; out secRec: TSectionRec; secsize: longWord=0);
  27. function SectionEnd(var secRec: TSectionRec): Boolean;
  28. procedure WriteInstList(list: TWasmInstrList);
  29. procedure WriteFuncTypeSect(m: TWasmModule);
  30. procedure WriteFuncSect(m: TWasmModule);
  31. procedure WriteExportSect(m: TWasmModule);
  32. procedure WriteCodeSect(m: TWasmModule);
  33. procedure pushStream(st: TStream);
  34. function popStream: TStream;
  35. public
  36. keepLeb128 : Boolean; // keep leb128 at 4 offset relocatable
  37. writeReloc : Boolean; // writting relocation (linking) information
  38. constructor Create;
  39. destructor Destroy; override;
  40. function Write(m: TWasmModule; adst: TStream): Boolean;
  41. end;
  42. function WriteModule(m: TWasmModule; dst: TStream): Boolean;
  43. type
  44. TLocalsInfo = record
  45. count : Integer;
  46. tp : byte;
  47. end;
  48. TLocalInfoArray = array of TLocalsInfo;
  49. // returns the list of local arrays
  50. procedure GetLocalInfo(func: TWasmFunc; out loc: TLocalInfoArray);
  51. implementation
  52. procedure GetLocalInfo(func: TWasmFunc; out loc: TLocalInfoArray);
  53. var
  54. i : integer;
  55. cnt : integer;
  56. tp : byte;
  57. nt : byte;
  58. j : integer;
  59. procedure Push;
  60. begin
  61. if j=length(loc) then begin
  62. if j=0 then SetLength(loc, 1)
  63. else SetLength(loc, j*2);
  64. end;
  65. loc[j].tp:=tp;
  66. loc[j].count:=cnt;
  67. inc(j);
  68. end;
  69. begin
  70. SetLength(Loc, 0);
  71. if func.LocalsCount = 0 then Exit;
  72. cnt:=1;
  73. tp:=func.GetLocal(0).tp;
  74. j:=0;
  75. for i:=1 to func.LocalsCount-1 do begin
  76. nt := func.GetLocal(i).tp;
  77. if nt<>tp then begin
  78. Push;
  79. tp:=nt;
  80. cnt:=1;
  81. end else
  82. inc(cnt);
  83. end;
  84. Push;
  85. SetLength(loc, j);
  86. end;
  87. function WriteModule(m: TWasmModule; dst: TStream): Boolean;
  88. var
  89. bw : TBinWriter;
  90. begin
  91. bw := TBinWriter.Create;
  92. try
  93. bw.keepLeb128:=true;
  94. Normalize(m);
  95. Result := bw.Write(m, dst);
  96. finally
  97. bw.Free;
  98. end;
  99. end;
  100. { TBinWriter }
  101. procedure TBinWriter.AddReloc(relocType: byte; ofs: int64; index: UInt32);
  102. begin
  103. if relocCount=length(reloc) then begin
  104. if relocCount=0 then SetLength(reloc, 16)
  105. else SetLength(reloc, relocCount*2);
  106. end;
  107. reloc[relocCount].reltype:=relocType;
  108. reloc[relocCount].offset:=ofs+recOfs;
  109. reloc[relocCount].index:=index;
  110. inc(relocType);
  111. end;
  112. procedure TBinWriter.WriteRelocU32(u: longword);
  113. begin
  114. WriteU(dst, u, sizeof(u)*8, keepLeb128);
  115. end;
  116. function TBinWriter.Write(m: TWasmModule; adst: TStream): Boolean;
  117. var
  118. l : Longword;
  119. begin
  120. if not Assigned(m) or not Assigned(adst) then begin
  121. Result:=false;
  122. Exit;
  123. end;
  124. dst:=adst;
  125. org:=adst;
  126. dst.Write(WasmId_Buf, length(WasmId_Buf));
  127. l:=NtoLE(Wasm_Version1);
  128. dst.Write(l, sizeof(l));
  129. // 01 function type section
  130. WriteFuncTypeSect(m);
  131. // 03 function section
  132. WriteFuncSect(m);
  133. // 07 export section
  134. WriteExportSect(m);
  135. // 10 code section
  136. WriteCodeSect(m);
  137. Result:=true;
  138. end;
  139. procedure TBinWriter.SectionBegin(secId: byte; out secRec: TSectionRec; secsize: longWord=0);
  140. begin
  141. secRec.secpos:=dst.Position;
  142. dst.WriteByte(secId);
  143. secRec.szpos:=dst.Position;
  144. WriteRelocU32(secsize);
  145. secRec.datapos:=dst.Position;
  146. secRec.endofdata:=dst.Position+secsize;
  147. end;
  148. function TBinWriter.SectionEnd(var secRec: TSectionRec): Boolean;
  149. var
  150. sz: LongWord;
  151. begin
  152. secRec.endofdata:=dst.Position;
  153. dst.Position:=secRec.szpos;
  154. sz := secRec.endofdata - secRec.datapos;
  155. WriteRelocU32(sz);
  156. dst.Position:=secRec.endofdata;
  157. Result := true;
  158. end;
  159. procedure TBinWriter.WriteFuncTypeSect(m: TWasmModule);
  160. var
  161. sc : TSectionRec;
  162. i : integer;
  163. j : integer;
  164. tp : TWasmFuncType;
  165. begin
  166. SectionBegin(SECT_TYPE, sc);
  167. WriteU32(dst, m.TypesCount);
  168. for i:=0 to m.TypesCount-1 do begin
  169. tp:=m.GetType(i);
  170. dst.WriteByte(func_type);
  171. WriteU32(dst, tp.ParamCount);
  172. for j:=0 to tp.ParamCount-1 do
  173. dst.WriteByte(tp.GetParam(i).tp);
  174. WriteU32(dst, tp.ResultCount);
  175. for j:=0 to tp.ResultCount-1 do
  176. dst.WriteByte(tp.GetResult(i).tp);
  177. end;
  178. SectionEnd(sc);
  179. end;
  180. procedure TBinWriter.WriteFuncSect(m: TWasmModule);
  181. var
  182. sc : TSectionRec;
  183. i : integer;
  184. begin
  185. SectionBegin(SECT_FUNCTION, sc);
  186. WriteU32(dst, m.FuncCount);
  187. for i:=0 to m.FuncCount-1 do
  188. WriteRelocU32(m.GetFunc(i).functype.typeNum);
  189. SectionEnd(sc);
  190. end;
  191. procedure TBinWriter.WriteExportSect(m: TWasmModule);
  192. var
  193. sc : TSectionRec;
  194. i : integer;
  195. x : TWasmExport;
  196. begin
  197. SectionBegin(SECT_EXPORT, sc);
  198. WriteU32(dst, m.ExportCount);
  199. for i:=0 to m.ExportCount-1 do begin
  200. x:=m.GetExport(i);
  201. WriteU32(dst, length(x.name));
  202. if length(x.name)>0 then
  203. dst.Write(x.name[1], length(x.name));
  204. dst.WriteByte(x.exportType);
  205. WriteRelocU32(x.exportNum);
  206. end;
  207. SectionEnd(sc);
  208. end;
  209. procedure TBinWriter.WriteCodeSect(m: TWasmModule);
  210. var
  211. sc : TSectionRec;
  212. i, j : integer;
  213. sz : int64;
  214. mem : TMemoryStream;
  215. la : TLocalInfoArray;
  216. f : TWasmFunc;
  217. begin
  218. SectionBegin(SECT_CODE, sc);
  219. mem:=TMemoryStream.Create;
  220. try
  221. WriteU32(dst, m.FuncCount);
  222. for i :=0 to m.FuncCount-1 do begin
  223. f:=m.GetFunc(i);
  224. GetLocalInfo(f, la);
  225. mem.Position:=0;
  226. pushStream(mem);
  227. WriteU32(dst, length(la));
  228. for j:=0 to length(la)-1 do begin
  229. WriteU32(dst, la[i].count);
  230. dst.WriteByte(la[i].tp);
  231. end;
  232. WriteInstList(f.instr);
  233. popStream;
  234. sz:=mem.Position;
  235. mem.Position:=0;
  236. WriteRelocU32(sz);
  237. dst.CopyFrom(mem, sz);
  238. end;
  239. finally
  240. mem.Free;
  241. end;
  242. SectionEnd(sc);
  243. end;
  244. procedure TBinWriter.WriteInstList(list: TWasmInstrList);
  245. var
  246. i : integer;
  247. ci : TWasmInstr;
  248. begin
  249. for i:=0 to list.Count-1 do begin
  250. ci :=list[i];
  251. dst.WriteByte(ci.code);
  252. case INST_FLAGS[ci.code].Param of
  253. ipLeb:
  254. if INST_RELOC_FLAGS[ci.code].doReloc then
  255. WriteRelocU32(ci.operandNum)
  256. else
  257. WriteU32(dst, ci.operandNum);
  258. end;
  259. end;
  260. end;
  261. procedure TBinWriter.pushStream(st: TStream);
  262. begin
  263. if st=nil then Exit;
  264. strm.Add(st);
  265. dst:=st;
  266. end;
  267. function TBinWriter.popStream: TStream;
  268. begin
  269. if strm.Count=0 then
  270. Result:=nil
  271. else begin
  272. Result:=TStream(strm[strm.Count-1]);
  273. strm.Delete(strm.Count-1);
  274. end;
  275. if strm.Count=0 then dst:=org
  276. else dst:=TStream(strm[strm.Count-1]);
  277. end;
  278. constructor TBinWriter.Create;
  279. begin
  280. inherited Create;
  281. strm:=TList.Create;
  282. end;
  283. destructor TBinWriter.Destroy;
  284. begin
  285. strm.Free;
  286. inherited Destroy;
  287. end;
  288. end.