t_wasi.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. {
  2. Copyright (c) 2019 by Dmitry Boyarintsev
  3. This unit implements support import,export,link routines
  4. for the WASI target
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16. ****************************************************************************
  17. }
  18. unit t_wasi;
  19. {$i fpcdefs.inc}
  20. interface
  21. uses
  22. systems,
  23. globtype, globals,
  24. aasmbase,
  25. cfileutl, cutils, cclasses,
  26. import, export, aasmdata, aasmcpu,
  27. fmodule, ogbase, ogwasm,
  28. symconst, symsym, symdef, symcpu,
  29. link,
  30. i_wasi, tgcpu;
  31. type
  32. { texportlibwasi }
  33. texportlibwasi=class(texportlib)
  34. procedure preparelib(const s : string);override;
  35. procedure exportprocedure(hp : texported_item);override;
  36. procedure exportvar(hp : texported_item);override;
  37. procedure generatelib;override;
  38. end;
  39. { timportlibwasi }
  40. timportlibwasi = class(timportlib)
  41. procedure generatelib;override;
  42. end;
  43. { tlinkerwasi }
  44. tlinkerwasi=class(texternallinker)
  45. public
  46. constructor Create;override;
  47. procedure SetDefaultInfo;override;
  48. procedure InitSysInitUnitName;override;
  49. function MakeExecutable:boolean;override;
  50. function MakeSharedLibrary:boolean;override;
  51. end;
  52. { TInternalLinkerWasi }
  53. TInternalLinkerWasi=class(tinternallinker)
  54. private
  55. function GetExeSectionSize(aExeOutput: TExeOutput; const aname:string): QWord;
  56. protected
  57. procedure DefaultLinkScript;override;
  58. function GetDataSize(aExeOutput: TExeOutput): QWord;override;
  59. function GetBssSize(aExeOutput: TExeOutput): QWord;override;
  60. public
  61. constructor create;override;
  62. procedure InitSysInitUnitName;override;
  63. end;
  64. implementation
  65. uses
  66. SysUtils,
  67. verbose,
  68. comprsrc,rescmn;
  69. { timportlibwasi }
  70. procedure timportlibwasi.generatelib;
  71. begin
  72. end;
  73. { tlinkerwasi }
  74. constructor tlinkerwasi.Create;
  75. begin
  76. inherited Create;
  77. end;
  78. procedure tlinkerwasi.SetDefaultInfo;
  79. begin
  80. with Info do
  81. begin
  82. ExeCmd[1] := 'wasm-ld -m wasm32 $SONAME $GCSECTIONS $MAP -z stack-size=$STACKSIZE $OPT -o $EXE';
  83. DllCmd[1] := 'wasm-ld -m wasm32 $SONAME $GCSECTIONS $MAP -z stack-size=$STACKSIZE $OPT -o $EXE';
  84. end;
  85. end;
  86. procedure tlinkerwasi.InitSysInitUnitName;
  87. begin
  88. if current_module.islibrary then
  89. sysinitunit:='si_dll'
  90. else
  91. sysinitunit:='si_prc';
  92. end;
  93. function tlinkerwasi.MakeExecutable:boolean;
  94. const
  95. PageSize = 65536;
  96. var
  97. GCSectionsStr : ansistring;
  98. binstr, cmdstr : Tcmdstr;
  99. InitStr,
  100. FiniStr,
  101. SoNameStr : string[80];
  102. mapstr,ltostr : TCmdStr;
  103. success : Boolean;
  104. tmp : TCmdStrListItem;
  105. tempFileName : ansistring;
  106. initialmem,
  107. maxmem : longint;
  108. begin
  109. if not(cs_link_nolink in current_settings.globalswitches) then
  110. Message1(exec_i_linking,current_module.exefilename);
  111. { Create some replacements }
  112. mapstr:='';
  113. if (cs_link_map in current_settings.globalswitches) then
  114. mapstr:='-Map '+maybequoted(ChangeFileExt(current_module.exefilename,'.map'));
  115. if (cs_link_smart in current_settings.globalswitches) and
  116. create_smartlink_sections then
  117. GCSectionsStr:='--gc-sections'
  118. else
  119. GCSectionsStr:='';
  120. SoNameStr:='';
  121. SplitBinCmd(Info.ExeCmd[1],binstr,cmdstr);
  122. Replace(cmdstr,'$EXE',maybequoted(current_module.exefilename));
  123. tmp := TCmdStrListItem(ObjectFiles.First);
  124. while Assigned(tmp) do begin
  125. cmdstr := tmp.Str+ ' ' + cmdstr;
  126. tmp := TCmdStrListItem(tmp.Next);
  127. end;
  128. // if HasExports then
  129. // cmdstr := cmdstr + ' --export-dynamic'; //' --export-dynamic';
  130. cmdstr := cmdstr + ' --no-entry';
  131. initialmem:=align(heapsize,PageSize);
  132. maxmem:=align(maxheapsize,PageSize);
  133. if ts_wasm_threads in current_settings.targetswitches then
  134. begin
  135. cmdstr := cmdstr + ' --import-memory --shared-memory --global-base=1024';
  136. end;
  137. if initialmem>0 then
  138. cmdstr := cmdstr + ' --initial-memory=' + tostr(initialmem);
  139. if maxmem>0 then
  140. cmdstr := cmdstr + ' --max-memory=' + tostr(maxmem);
  141. if (cs_link_strip in current_settings.globalswitches) then
  142. begin
  143. { only remove non global symbols and debugging info for a library }
  144. cmdstr := cmdstr + ' --strip-all';
  145. end;
  146. Replace(cmdstr,'$OPT',Info.ExtraOptions);
  147. //Replace(cmdstr,'$RES',maybequoted(outputexedir+Info.ResName));
  148. //Replace(cmdstr,'$INIT',InitStr);
  149. //Replace(cmdstr,'$FINI',FiniStr);
  150. Replace(cmdstr,'$STACKSIZE',tostr(stacksize));
  151. Replace(cmdstr,'$SONAME',SoNameStr);
  152. Replace(cmdstr,'$MAP',mapstr);
  153. //Replace(cmdstr,'$LTO',ltostr);
  154. Replace(cmdstr,'$GCSECTIONS',GCSectionsStr);
  155. success:=DoExec(FindUtil(utilsprefix+binstr),cmdstr,true,false);
  156. MakeExecutable:=success;
  157. end;
  158. function tlinkerwasi.MakeSharedLibrary: boolean;
  159. var
  160. GCSectionsStr : ansistring;
  161. binstr, cmdstr : Tcmdstr;
  162. InitStr,
  163. FiniStr,
  164. SoNameStr : string[80];
  165. mapstr,ltostr : TCmdStr;
  166. success : Boolean;
  167. tmp : TCmdStrListItem;
  168. tempFileName : ansistring;
  169. begin
  170. Result:=false;
  171. if not(cs_link_nolink in current_settings.globalswitches) then
  172. Message1(exec_i_linking,current_module.sharedlibfilename);
  173. { Create some replacements }
  174. mapstr:='';
  175. if (cs_link_map in current_settings.globalswitches) then
  176. mapstr:='-Map '+maybequoted(ChangeFileExt(current_module.sharedlibfilename,'.map'));
  177. if (cs_link_smart in current_settings.globalswitches) and
  178. create_smartlink_sections then
  179. GCSectionsStr:='--gc-sections'
  180. else
  181. GCSectionsStr:='';
  182. SoNameStr:='';
  183. SplitBinCmd(Info.DllCmd[1],binstr,cmdstr);
  184. Replace(cmdstr,'$EXE',maybequoted(current_module.sharedlibfilename));
  185. tmp := TCmdStrListItem(ObjectFiles.First);
  186. while Assigned(tmp) do begin
  187. cmdstr := tmp.Str+ ' ' + cmdstr;
  188. tmp := TCmdStrListItem(tmp.Next);
  189. end;
  190. // if HasExports then
  191. // cmdstr := cmdstr + ' --export-dynamic'; //' --export-dynamic';
  192. cmdstr := cmdstr + ' --no-entry';
  193. if (cs_link_strip in current_settings.globalswitches) then
  194. begin
  195. { only remove non global symbols and debugging info for a library }
  196. cmdstr := cmdstr + ' --strip-all';
  197. end;
  198. Replace(cmdstr,'$OPT',Info.ExtraOptions);
  199. //Replace(cmdstr,'$RES',maybequoted(outputexedir+Info.ResName));
  200. //Replace(cmdstr,'$INIT',InitStr);
  201. //Replace(cmdstr,'$FINI',FiniStr);
  202. Replace(cmdstr,'$STACKSIZE',tostr(stacksize));
  203. Replace(cmdstr,'$SONAME',SoNameStr);
  204. Replace(cmdstr,'$MAP',mapstr);
  205. //Replace(cmdstr,'$LTO',ltostr);
  206. Replace(cmdstr,'$GCSECTIONS',GCSectionsStr);
  207. success:=DoExec(FindUtil(utilsprefix+binstr),cmdstr,true,false);
  208. MakeSharedLibrary:=success;
  209. end;
  210. { texportlibwasi }
  211. procedure texportlibwasi.preparelib(const s: string);
  212. begin
  213. //nothing to happen. wasm files are modules
  214. end;
  215. procedure texportlibwasi.exportprocedure(hp: texported_item);
  216. var
  217. nm : TSymStr;
  218. pd: tcpuprocdef;
  219. begin
  220. pd:=tcpuprocdef(tprocsym(hp.sym).ProcdefList[0]);
  221. if eo_promising_first in hp.options then
  222. pd.add_promising_export(hp.name^,false)
  223. else if eo_promising_last in hp.options then
  224. pd.add_promising_export(hp.name^,true)
  225. else
  226. begin
  227. nm := pd.mangledname;
  228. current_asmdata.asmlists[al_exports].Concat(tai_export_name.create(hp.name^, nm, ie_Func));
  229. end;
  230. end;
  231. procedure texportlibwasi.exportvar(hp: texported_item);
  232. begin
  233. //inherited exportvar(hp);
  234. end;
  235. procedure texportlibwasi.generatelib;
  236. begin
  237. //inherited generatelib;
  238. end;
  239. { TInternalLinkerWasi }
  240. function TInternalLinkerWasi.GetExeSectionSize(aExeOutput: TExeOutput;
  241. const aname: string): QWord;
  242. var
  243. sec: TExeSection;
  244. begin
  245. sec:=aExeOutput.findexesection(aname);
  246. if assigned(sec) then
  247. Result:=sec.size
  248. else
  249. Result:=0;
  250. end;
  251. procedure TInternalLinkerWasi.DefaultLinkScript;
  252. var
  253. s: TCmdStr;
  254. begin
  255. while not ObjectFiles.Empty do
  256. begin
  257. s:=ObjectFiles.GetFirst;
  258. if s<>'' then
  259. LinkScript.Concat('READOBJECT ' + maybequoted(s));
  260. end;
  261. LinkScript.Concat('EXESECTION .wasm_globals');
  262. LinkScript.Concat(' SYMBOL __stack_pointer');
  263. if ts_wasm_threads in current_settings.targetswitches then
  264. begin
  265. LinkScript.Concat(' SYMBOL __tls_base');
  266. LinkScript.Concat(' SYMBOL __tls_size');
  267. LinkScript.Concat(' SYMBOL __tls_align');
  268. end;
  269. LinkScript.Concat(' OBJSECTION .wasm_globals.*');
  270. LinkScript.Concat('ENDEXESECTION');
  271. { WebAssembly is a Harvard architecture, with multiple address spaces, so it
  272. is important to keep the sections grouped, and keep the first section in
  273. each group intact (otherwise, TWasmExeOutput.MemPos_ExeSection in ogwasm.pas
  274. needs to be updated) }
  275. { tags (used by WebAssembly native exceptions) }
  276. ScriptAddGenericSections('.wasm_tags');
  277. { code }
  278. if ts_wasm_threads in current_settings.targetswitches then
  279. begin
  280. linkscript.Concat('EXESECTION .text');
  281. linkscript.Concat(' OBJSECTION .text*');
  282. { functions, generated by the linker: }
  283. linkscript.Concat(' SYMBOL __wasm_init_tls');
  284. linkscript.Concat(' SYMBOL __fpc_wasm_init_shared_memory');
  285. linkscript.Concat('ENDEXESECTION');
  286. end
  287. else
  288. ScriptAddGenericSections('.text');
  289. if ts_wasm_threads in current_settings.targetswitches then
  290. ScriptAddGenericSections('.tbss');
  291. ScriptAddGenericSections(
  292. { data (initialized data first, uninitialized data later) }
  293. '.rodata,.data,fpc.resources,fpc.reshandles,.bss,'+
  294. { debug info }
  295. '.debug_frame,.debug_info,.debug_line,.debug_abbrev,.debug_aranges,.debug_ranges,.debug_str');
  296. end;
  297. function TInternalLinkerWasi.GetDataSize(aExeOutput: TExeOutput): QWord;
  298. begin
  299. Result:=GetExeSectionSize(aExeOutput,'.rodata') +
  300. GetExeSectionSize(aExeOutput,'.data') +
  301. GetExeSectionSize(aExeOutput,'fpc.resources');
  302. end;
  303. function TInternalLinkerWasi.GetBssSize(aExeOutput: TExeOutput): QWord;
  304. begin
  305. Result:=GetExeSectionSize(aExeOutput,'.bss') +
  306. GetExeSectionSize(aExeOutput,'.tbss') +
  307. GetExeSectionSize(aExeOutput,'fpc.reshandles');
  308. end;
  309. constructor TInternalLinkerWasi.create;
  310. begin
  311. inherited create;
  312. CExeOutput:=TWasmExeOutput;
  313. CObjInput:=TWasmObjInput;
  314. end;
  315. procedure TInternalLinkerWasi.InitSysInitUnitName;
  316. begin
  317. if current_module.islibrary then
  318. sysinitunit:='si_dll'
  319. else
  320. sysinitunit:='si_prc';
  321. end;
  322. initialization
  323. RegisterTarget(system_wasm32_wasip1_info);
  324. RegisterImport(system_wasm32_wasip1, timportlibwasi);
  325. RegisterExport(system_wasm32_wasip1, texportlibwasi);
  326. RegisterTarget(system_wasm32_wasip1threads_info);
  327. RegisterImport(system_wasm32_wasip1threads, timportlibwasi);
  328. RegisterExport(system_wasm32_wasip1threads, texportlibwasi);
  329. RegisterTarget(system_wasm32_wasip2_info);
  330. RegisterImport(system_wasm32_wasip2, timportlibwasi);
  331. RegisterExport(system_wasm32_wasip2, texportlibwasi);
  332. RegisterLinker(ld_int_wasi,TInternalLinkerWasi);
  333. RegisterLinker(ld_wasi, tlinkerwasi);
  334. RegisterRes(res_wasm_info,TWinLikeResourceFile);
  335. end.