wasmtoolutils.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. unit wasmtoolutils;
  2. interface
  3. uses
  4. Classes,SysUtils, wasmbin, lebutils,
  5. //wasmbindebug,
  6. wasmlink, wasmlinkchange;
  7. function ChangeSymbolFlagStream(st: TStream; syms: TStrings): Boolean;
  8. procedure ChangeSymbolFlag(const fn, symfn: string);
  9. function PredictSymbolsFromLink(const wasmfn: string; weakList: TStrings; doVerbose: Boolean = false): Boolean;
  10. procedure MatchExportNameToSymName(const x: TExportSection; const l: TLinkingSection; dst: TStrings);
  11. function ExportRenameSym(var x: TExportSection; syms: TStrings): Integer;
  12. function ExportRenameProcess(st, dst: TStream; syms: TStrings; doVerbose: Boolean): Boolean;
  13. function ExportNameGather(const wasmfile: string; syms: TStrings; doVerbose: Boolean = false): Boolean;
  14. procedure ExportRename(const fn, symfn: string; doVerbose: Boolean);
  15. implementation
  16. function ChangeSymbolFlagStream(st: TStream; syms: TStrings): Boolean;
  17. var
  18. dw : LongWord;
  19. ofs : int64;
  20. sc : TSection;
  21. ps : int64;
  22. nm : string;
  23. begin
  24. dw := st.ReadDWord;
  25. Result := dw = WasmId_Int;
  26. if not Result then Exit;
  27. dw := st.ReadDWord;
  28. while st.Position<st.Size do begin
  29. ofs := st.Position;
  30. sc.id := st.ReadByte;
  31. sc.Size := ReadU(st);
  32. ps := st.Position+sc.size;
  33. if sc.id=0 then begin
  34. nm := GetName(st);
  35. if nm = SectionName_Linking then begin
  36. ProcessLinkingSection(st, syms);
  37. break;
  38. end;
  39. //DumpLinking(st, sc.size - (st.Position - ofs));
  40. end;
  41. //if sc.id= 1 then DumpTypes(st);
  42. if st.Position <> ps then
  43. begin
  44. //writeln('adjust stream targ=',ps,' actual: ', st.position);
  45. st.Position := ps;
  46. end;
  47. end;
  48. end;
  49. // Assumption is made, there's only 1 table in the file!
  50. // if a function is a stub function (the only code is "unreachable"), the status given
  51. // "weak" (it's a reference function elsewhere)
  52. // if a function is located in the function table, then the status given is
  53. // "hidden" (do not add to the final linked executable)
  54. // if a function is not located in the function table, the status given is:
  55. // "hidden"+"local" (local means the function can be used only in this object file)
  56. procedure MatchExportNameToSymFlag(
  57. const imp: TImportSection;
  58. const c: TCodeSection;
  59. const e: TElementSection;
  60. const x: TExportSection;
  61. var l: TLinkingSection;
  62. weakList: TStrings; // do known set of globals weak
  63. doVerbose: Boolean);
  64. type
  65. TFuncType = (ftImpl = 0, ftIntf, ftStub, ftExport);
  66. TFuncInfo = record
  67. hasSymbol : Boolean;
  68. fnType : TFuncType;
  69. end;
  70. var
  71. i : integer;
  72. j : integer;
  73. idx : integer;
  74. fn : array of TFuncInfo;
  75. codeofs: integer;
  76. begin
  77. idx := -1;
  78. for i:=0 to length(l.symbols)-1 do
  79. if l.symbols[i].kind = SYMTAB_FUNCTION then begin
  80. if l.symbols[i].symindex>idx then begin
  81. idx:= l.symbols[i].symindex;
  82. end;
  83. end;
  84. SetLength(fn, idx+1);
  85. for i:=0 to length(l.symbols)-1 do
  86. if l.symbols[i].kind = SYMTAB_FUNCTION then begin
  87. idx := l.symbols[i].symindex;
  88. fn[idx].hasSymbol:=true;
  89. end;
  90. for i:=0 to length(e.entries)-1 do
  91. for j:=0 to length(e.entries[i].funcs)-1 do begin
  92. idx := e.entries[i].funcs[j];
  93. fn[idx].fnType:=ftIntf;
  94. end;
  95. codeofs:=0;
  96. for i:=0 to length(imp.entries)-1 do
  97. if imp.entries[i].desc = IMPDESC_FUNC then
  98. inc(codeofs);
  99. for i:=codeofs to length(fn)-1 do begin
  100. if not fn[i].hasSymbol then begin
  101. Continue;
  102. end;
  103. if (fn[i].fnType=ftImpl) and (isUnreachable(c.entries[i-codeofs])) then begin
  104. fn[i].fnType:=ftStub;
  105. end;
  106. end;
  107. for i:=0 to length(x.entries)-1 do begin
  108. if x.entries[i].desc = EXPDESC_FUNC then begin
  109. idx := x.entries[i].index;
  110. if fn[idx].fnType<>ftStub then
  111. fn[idx].fnType:=ftExport;
  112. end;
  113. end;
  114. for i:=0 to length(l.symbols)-1 do begin
  115. if l.symbols[i].kind = SYMTAB_FUNCTION then begin
  116. j := l.symbols[i].symindex;
  117. if j>=codeofs then // not imported
  118. case fn[j].fnType of
  119. ftImpl:
  120. l.symbols[i].flags := l.symbols[i].flags or WASM_SYM_VISIBILITY_HIDDEN or WASM_SYM_BINDING_LOCAL;
  121. ftIntf:
  122. l.symbols[i].flags := l.symbols[i].flags or WASM_SYM_VISIBILITY_HIDDEN;
  123. ftStub:
  124. l.symbols[i].flags := l.symbols[i].flags or WASM_SYM_BINDING_WEAK or WASM_SYM_VISIBILITY_HIDDEN;
  125. ftExport:
  126. //l.symbols[i].flags := l.symbols[i].flags or WASM_SYM_VISIBILITY_HIDDEN or WASM_SYM_BINDING_WEAK;
  127. ;
  128. end;
  129. if DoVerbose then begin
  130. write('func ');
  131. if l.symbols[i].hasSymName then
  132. write(l.symbols[i].symname)
  133. else
  134. write('#',j);
  135. write(' ', fn[j].fnType);
  136. writeln;
  137. end;
  138. //if l.symbols[i].symindex>mx then mx := ;
  139. end else if (l.symbols[i].kind = SYMTAB_GLOBAL) and Assigned(weakList) then begin
  140. if l.symbols[i].hasSymName and (weakList.IndexOf(l.symbols[i].symname)>=0) then begin
  141. if doVerbose then
  142. writeln('weakining: ',l.symbols[i].symname);
  143. l.symbols[i].flags := l.symbols[i].flags or WASM_SYM_BINDING_WEAK or WASM_SYM_VISIBILITY_HIDDEN;
  144. end;
  145. end;
  146. end;
  147. end;
  148. function PredictSymbolsFromLink(const wasmfn: string; weakList: TStrings; doVerbose: Boolean = false): Boolean;
  149. var
  150. st : TFileStream;
  151. dw : LongWord;
  152. foundCode : Boolean;
  153. foundElement : Boolean;
  154. foundLink : Boolean;
  155. foundExport : Boolean;
  156. foundImport : Boolean;
  157. ofs : Int64;
  158. ps : Int64;
  159. sc : TSection;
  160. c : TCodeSection;
  161. imp : TImportSection;
  162. l : TLinkingSection;
  163. e : TElementSection;
  164. x : TExportSection;
  165. nm : string;
  166. lofs : Int64;
  167. lsize : Int64;
  168. mem : TMemoryStream;
  169. mem2 : TMemoryStream;
  170. begin
  171. st := TFileStream.Create(wasmfn, fmOpenReadWrite or fmShareDenyNone);
  172. try
  173. dw := st.ReadDWord;
  174. Result := dw = WasmId_Int;
  175. if not Result then Exit;
  176. dw := st.ReadDWord;
  177. foundElement := false;
  178. foundCode := false;
  179. foundLink := false;
  180. foundExport := false;
  181. foundImport := false;
  182. Result := false;
  183. while st.Position<st.Size do begin
  184. ofs := st.Position;
  185. sc.id := st.ReadByte;
  186. sc.Size := ReadU(st);
  187. ps := st.Position+sc.size;
  188. case sc.id of
  189. SECT_IMPORT: begin
  190. ReadImportSection(st, imp);
  191. foundImport := true;
  192. end;
  193. SECT_EXPORT: begin
  194. ReadExport(st, x);
  195. foundExport := true;
  196. end;
  197. SECT_ELEMENT: begin
  198. ReadElementSection(st, e);
  199. foundElement := true;
  200. end;
  201. SECT_CODE: begin
  202. ReadCodeSection(st, c);
  203. foundCode := true;
  204. end;
  205. SECT_CUSTOM: begin
  206. nm := ReadName(st);
  207. if nm = SectionName_Linking then begin
  208. lofs:=ofs;
  209. ReadLinkingSection(st, sc.size, l);
  210. foundLink := true;
  211. lsize := ps-lofs;
  212. end;
  213. end;
  214. end;
  215. if st.Position <> ps then begin
  216. st.Position := ps;
  217. end;
  218. Result := foundLink and foundCode and foundElement;
  219. if Result then break;
  220. end;
  221. if not foundExport then SetLength(x.entries,0);
  222. if not foundImport then SetLength(imp.entries, 0);
  223. if Result then begin
  224. if doVerbose then writeln('detecting symbols');
  225. MatchExportNameToSymFlag(imp, c, e, x, l, weakList, doVerbose);
  226. mem:=TMemoryStream.Create;
  227. mem2:=TMemoryStream.Create;
  228. try
  229. st.Position:=lofs+lsize;
  230. mem2.CopyFrom(st, st.Size - st.Position);
  231. st.Position:=lofs;
  232. WriteName(mem, SectionName_Linking);
  233. WriteLinkingSection(mem, l);
  234. st.WriteByte(SECT_CUSTOM);
  235. if doVerbose then writeln('section size: ', mem.Size);
  236. WriteU32(st, mem.Size);
  237. mem.Position:=0;
  238. if doVerbose then writeln('copying from mem');
  239. st.CopyFrom(mem, mem.Size);
  240. mem2.Position:=0;
  241. if doVerbose then writeln('copying from mem2');
  242. st.CopyFrom(mem2, mem2.Size);
  243. st.Size:=st.Position;
  244. finally
  245. mem.Free;
  246. mem2.Free;
  247. end;
  248. if doVerbose then writeln('written: ', st.Position-lofs,' bytes');
  249. end else
  250. writeln('failed. section find status. Likning: ', foundLink,'; Code: ', foundCode,'; Element: ', foundElement);
  251. finally
  252. st.Free;
  253. end;
  254. end;
  255. procedure ChangeSymbolFlag(const fn, symfn: string);
  256. var
  257. fs :TFileStream;
  258. syms: TStringList;
  259. begin
  260. syms:=TStringList.Create;
  261. fs := TFileStream.Create(fn, fmOpenReadWrite or fmShareDenyNone);
  262. try
  263. if (symfn<>'') then begin
  264. ReadSymbolsConf(symfn, syms);
  265. ChangeSymbolFlagStream(fs, syms);
  266. end;
  267. finally
  268. fs.Free;
  269. syms.Free;
  270. end;
  271. end;
  272. function ExportRenameSym(var x: TExportSection; syms: TStrings): integer;
  273. var
  274. i : integer;
  275. v : string;
  276. begin
  277. Result := 0;
  278. for i:=0 to length(x.entries)-1 do begin
  279. v := syms.Values[x.entries[i].name];
  280. if v <> '' then begin
  281. x.entries[i].name := v;
  282. inc(Result);
  283. end;
  284. end;
  285. end;
  286. function ExportRenameProcess(st, dst: TStream; syms: TStrings; doVerbose: Boolean): Boolean;
  287. var
  288. dw : LongWord;
  289. ofs : int64;
  290. sc : TSection;
  291. ps : int64;
  292. x : TExportSection;
  293. mem : TMemoryStream;
  294. cnt : integer;
  295. begin
  296. dw := st.ReadDWord;
  297. Result := dw = WasmId_Int;
  298. if not Result then begin
  299. Exit;
  300. end;
  301. dw := st.ReadDWord;
  302. while st.Position<st.Size do begin
  303. ofs := st.Position;
  304. sc.id := st.ReadByte;
  305. sc.Size := ReadU(st);
  306. ps := st.Position+sc.size;
  307. if sc.id = SECT_EXPORT then begin
  308. if doVerbose then writeln(' export section found');
  309. ReadExport(st, x);
  310. cnt := ExportRenameSym(x, syms);
  311. st.Position:=0;
  312. dst.CopyFrom(st, ofs);
  313. st.Position:=ps;
  314. mem := TMemoryStream.Create;
  315. WriteExport(x, mem);
  316. mem.Position:=0;
  317. dst.WriteByte(SECT_EXPORT);
  318. WriteU32(dst, mem.Size);
  319. dst.CopyFrom(mem, mem.Size);
  320. dst.CopyFrom(st, st.Size-st.Position);
  321. break;
  322. end;
  323. if st.Position <> ps then
  324. st.Position := ps;
  325. end;
  326. end;
  327. // match between exported function names and symbol names
  328. procedure MatchExportNameToSymName(const x: TExportSection; const l: TLinkingSection; dst: TStrings);
  329. var
  330. expname : string;
  331. i,j : integer;
  332. begin
  333. for i:=0 to length(x.entries)-1 do begin
  334. // gathering only function names for now
  335. if x.entries[i].desc <> EXPDESC_FUNC then continue;
  336. expname := x.entries[i].name;
  337. for j:=0 to length(l.symbols)-1 do begin
  338. if (l.symbols[j].kind = SYMTAB_FUNCTION)
  339. and (l.symbols[j].symindex = x.entries[i].index)
  340. and (l.symbols[j].hasSymName)
  341. then
  342. dst.Values[ l.symbols[j].symname ] := expname;
  343. end;
  344. end;
  345. end;
  346. function ExportNameGather(const wasmfile: string; syms: TStrings; doVerbose: Boolean = false): Boolean;
  347. var
  348. dw : LongWord;
  349. ofs : int64;
  350. sc : TSection;
  351. ps : int64;
  352. mem : TMemoryStream;
  353. cnt : integer;
  354. st : TFileStream;
  355. nm : string;
  356. x : TExportSection;
  357. foundExport: Boolean;
  358. l : TLinkingSection;
  359. foundLink: Boolean;
  360. begin
  361. st := TFileStream.Create(wasmfile, fmOpenRead or fmShareDenyNone);
  362. try
  363. dw := st.ReadDWord;
  364. Result := dw = WasmId_Int;
  365. if not Result then begin
  366. Exit;
  367. end;
  368. dw := st.ReadDWord;
  369. foundExport:=false;
  370. foundLink:=false;
  371. while st.Position<st.Size do begin
  372. ofs := st.Position;
  373. sc.id := st.ReadByte;
  374. sc.Size := ReadU(st);
  375. ps := st.Position+sc.size;
  376. if sc.id = SECT_EXPORT then begin
  377. if doVerbose then writeln(' export section found');
  378. ReadExport(st, x);
  379. cnt := ExportRenameSym(x, syms);
  380. foundExport:=true;
  381. end else if sc.id = SECT_CUSTOM then begin
  382. nm := ReadName(st);
  383. if nm = SectionName_Linking then begin
  384. foundLink := true;
  385. ReadLinkingSection(st, sc.size, l);
  386. end;
  387. end;
  388. if st.Position <> ps then
  389. st.Position := ps;
  390. end;
  391. Result := foundLink and foundExport;
  392. if Result then
  393. MatchExportNameToSymName(x, l, syms);
  394. finally
  395. st.Free;
  396. end;
  397. end;
  398. procedure ExportRename(const fn, symfn: string; doVerbose: Boolean);
  399. var
  400. fs : TFileStream;
  401. syms : TStringList;
  402. dst : TMemoryStream;
  403. begin
  404. if doVerbose then writeln('Export symbols renaming');
  405. syms:=TStringList.Create;
  406. fs := TFileStream.Create(fn, fmOpenReadWrite or fmShareDenyNone);
  407. dst := TMemoryStream.Create;
  408. try
  409. if (symfn <> '') and fileExists(symfn) then
  410. begin
  411. if doVerbose then writeln('reading symbols: ', symfn);
  412. if isWasmFile(symfn) then
  413. ExportNameGather(symfn, syms, doVerbose)
  414. else
  415. syms.LoadFromFile(symfn);
  416. if doVerbose then write(syms.Text);
  417. end;
  418. ExportRenameProcess(fs, dst, syms, doVerbose);
  419. fs.Position:=0;
  420. dst.Position:=0;
  421. fs.CopyFrom(dst, dst.Size);
  422. fs.Size:=dst.Size;
  423. finally
  424. dst.Free;
  425. fs.Free;
  426. syms.Free;
  427. end;
  428. end;
  429. end.