t_wasi.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  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. DefaultMaxMemoryForThreads = 33554432;
  97. var
  98. GCSectionsStr : ansistring;
  99. binstr, cmdstr : Tcmdstr;
  100. InitStr,
  101. FiniStr,
  102. SoNameStr : string[80];
  103. mapstr,ltostr : TCmdStr;
  104. success : Boolean;
  105. tmp : TCmdStrListItem;
  106. tempFileName : ansistring;
  107. initialmem,
  108. maxmem : longint;
  109. begin
  110. if not(cs_link_nolink in current_settings.globalswitches) then
  111. Message1(exec_i_linking,current_module.exefilename);
  112. { Create some replacements }
  113. mapstr:='';
  114. if (cs_link_map in current_settings.globalswitches) then
  115. mapstr:='-Map '+maybequoted(ChangeFileExt(current_module.exefilename,'.map'));
  116. if (cs_link_smart in current_settings.globalswitches) and
  117. create_smartlink_sections then
  118. GCSectionsStr:='--gc-sections'
  119. else
  120. GCSectionsStr:='';
  121. SoNameStr:='';
  122. SplitBinCmd(Info.ExeCmd[1],binstr,cmdstr);
  123. Replace(cmdstr,'$EXE',maybequoted(current_module.exefilename));
  124. tmp := TCmdStrListItem(ObjectFiles.First);
  125. while Assigned(tmp) do begin
  126. cmdstr := tmp.Str+ ' ' + cmdstr;
  127. tmp := TCmdStrListItem(tmp.Next);
  128. end;
  129. // if HasExports then
  130. // cmdstr := cmdstr + ' --export-dynamic'; //' --export-dynamic';
  131. cmdstr := cmdstr + ' --no-entry';
  132. initialmem:=align(heapsize,PageSize);
  133. maxmem:=align(maxheapsize,PageSize);
  134. if ts_wasm_threads in current_settings.targetswitches then
  135. begin
  136. cmdstr := cmdstr + ' --import-memory --shared-memory --global-base=1024';
  137. if maxmem<=0 then
  138. maxmem:=align(DefaultMaxMemoryForThreads,PageSize);
  139. end;
  140. if initialmem>0 then
  141. cmdstr := cmdstr + ' --initial-memory=' + tostr(initialmem);
  142. if maxmem>0 then
  143. cmdstr := cmdstr + ' --max-memory=' + tostr(maxmem);
  144. if (cs_link_strip in current_settings.globalswitches) then
  145. begin
  146. { only remove non global symbols and debugging info for a library }
  147. cmdstr := cmdstr + ' --strip-all';
  148. end;
  149. Replace(cmdstr,'$OPT',Info.ExtraOptions);
  150. //Replace(cmdstr,'$RES',maybequoted(outputexedir+Info.ResName));
  151. //Replace(cmdstr,'$INIT',InitStr);
  152. //Replace(cmdstr,'$FINI',FiniStr);
  153. Replace(cmdstr,'$STACKSIZE',tostr(stacksize));
  154. Replace(cmdstr,'$SONAME',SoNameStr);
  155. Replace(cmdstr,'$MAP',mapstr);
  156. //Replace(cmdstr,'$LTO',ltostr);
  157. Replace(cmdstr,'$GCSECTIONS',GCSectionsStr);
  158. success:=DoExec(FindUtil(utilsprefix+binstr),cmdstr,true,false);
  159. MakeExecutable:=success;
  160. end;
  161. function tlinkerwasi.MakeSharedLibrary: boolean;
  162. var
  163. GCSectionsStr : ansistring;
  164. binstr, cmdstr : Tcmdstr;
  165. InitStr,
  166. FiniStr,
  167. SoNameStr : string[80];
  168. mapstr,ltostr : TCmdStr;
  169. success : Boolean;
  170. tmp : TCmdStrListItem;
  171. tempFileName : ansistring;
  172. begin
  173. Result:=false;
  174. if not(cs_link_nolink in current_settings.globalswitches) then
  175. Message1(exec_i_linking,current_module.sharedlibfilename);
  176. { Create some replacements }
  177. mapstr:='';
  178. if (cs_link_map in current_settings.globalswitches) then
  179. mapstr:='-Map '+maybequoted(ChangeFileExt(current_module.sharedlibfilename,'.map'));
  180. if (cs_link_smart in current_settings.globalswitches) and
  181. create_smartlink_sections then
  182. GCSectionsStr:='--gc-sections'
  183. else
  184. GCSectionsStr:='';
  185. SoNameStr:='';
  186. SplitBinCmd(Info.DllCmd[1],binstr,cmdstr);
  187. Replace(cmdstr,'$EXE',maybequoted(current_module.sharedlibfilename));
  188. tmp := TCmdStrListItem(ObjectFiles.First);
  189. while Assigned(tmp) do begin
  190. cmdstr := tmp.Str+ ' ' + cmdstr;
  191. tmp := TCmdStrListItem(tmp.Next);
  192. end;
  193. // if HasExports then
  194. // cmdstr := cmdstr + ' --export-dynamic'; //' --export-dynamic';
  195. cmdstr := cmdstr + ' --no-entry';
  196. if (cs_link_strip in current_settings.globalswitches) then
  197. begin
  198. { only remove non global symbols and debugging info for a library }
  199. cmdstr := cmdstr + ' --strip-all';
  200. end;
  201. Replace(cmdstr,'$OPT',Info.ExtraOptions);
  202. //Replace(cmdstr,'$RES',maybequoted(outputexedir+Info.ResName));
  203. //Replace(cmdstr,'$INIT',InitStr);
  204. //Replace(cmdstr,'$FINI',FiniStr);
  205. Replace(cmdstr,'$STACKSIZE',tostr(stacksize));
  206. Replace(cmdstr,'$SONAME',SoNameStr);
  207. Replace(cmdstr,'$MAP',mapstr);
  208. //Replace(cmdstr,'$LTO',ltostr);
  209. Replace(cmdstr,'$GCSECTIONS',GCSectionsStr);
  210. success:=DoExec(FindUtil(utilsprefix+binstr),cmdstr,true,false);
  211. MakeSharedLibrary:=success;
  212. end;
  213. { texportlibwasi }
  214. procedure texportlibwasi.preparelib(const s: string);
  215. begin
  216. //nothing to happen. wasm files are modules
  217. end;
  218. procedure texportlibwasi.exportprocedure(hp: texported_item);
  219. var
  220. nm : TSymStr;
  221. pd: tcpuprocdef;
  222. begin
  223. pd:=tcpuprocdef(tprocsym(hp.sym).ProcdefList[0]);
  224. if eo_promising_first in hp.options then
  225. pd.add_promising_export(hp.name^,false)
  226. else if eo_promising_last in hp.options then
  227. pd.add_promising_export(hp.name^,true)
  228. else
  229. begin
  230. nm := pd.mangledname;
  231. current_asmdata.asmlists[al_exports].Concat(tai_export_name.create(hp.name^, nm, ie_Func));
  232. end;
  233. end;
  234. procedure texportlibwasi.exportvar(hp: texported_item);
  235. begin
  236. //inherited exportvar(hp);
  237. end;
  238. procedure texportlibwasi.generatelib;
  239. begin
  240. //inherited generatelib;
  241. end;
  242. { TInternalLinkerWasi }
  243. function TInternalLinkerWasi.GetExeSectionSize(aExeOutput: TExeOutput;
  244. const aname: string): QWord;
  245. var
  246. sec: TExeSection;
  247. begin
  248. sec:=aExeOutput.findexesection(aname);
  249. if assigned(sec) then
  250. Result:=sec.size
  251. else
  252. Result:=0;
  253. end;
  254. procedure TInternalLinkerWasi.DefaultLinkScript;
  255. var
  256. s: TCmdStr;
  257. begin
  258. while not ObjectFiles.Empty do
  259. begin
  260. s:=ObjectFiles.GetFirst;
  261. if s<>'' then
  262. LinkScript.Concat('READOBJECT ' + maybequoted(s));
  263. end;
  264. LinkScript.Concat('EXESECTION .wasm_globals');
  265. LinkScript.Concat(' SYMBOL __stack_pointer');
  266. if ts_wasm_threads in current_settings.targetswitches then
  267. begin
  268. LinkScript.Concat(' SYMBOL __tls_base');
  269. LinkScript.Concat(' SYMBOL __tls_size');
  270. LinkScript.Concat(' SYMBOL __tls_align');
  271. end;
  272. LinkScript.Concat(' OBJSECTION .wasm_globals.*');
  273. LinkScript.Concat('ENDEXESECTION');
  274. { WebAssembly is a Harvard architecture, with multiple address spaces, so it
  275. is important to keep the sections grouped, and keep the first section in
  276. each group intact (otherwise, TWasmExeOutput.MemPos_ExeSection in ogwasm.pas
  277. needs to be updated) }
  278. { tags (used by WebAssembly native exceptions) }
  279. ScriptAddGenericSections('.wasm_tags');
  280. { code }
  281. if ts_wasm_threads in current_settings.targetswitches then
  282. begin
  283. linkscript.Concat('EXESECTION .text');
  284. linkscript.Concat(' OBJSECTION .text*');
  285. { functions, generated by the linker: }
  286. linkscript.Concat(' SYMBOL __wasm_init_tls');
  287. linkscript.Concat(' SYMBOL __fpc_wasm_init_shared_memory');
  288. linkscript.Concat('ENDEXESECTION');
  289. end
  290. else
  291. ScriptAddGenericSections('.text');
  292. if ts_wasm_threads in current_settings.targetswitches then
  293. ScriptAddGenericSections('.tbss');
  294. ScriptAddGenericSections(
  295. { data (initialized data first, uninitialized data later) }
  296. '.rodata,.data,fpc.resources,fpc.reshandles,.bss,'+
  297. { debug info }
  298. '.debug_frame,.debug_info,.debug_line,.debug_abbrev,.debug_aranges,.debug_ranges,.debug_str');
  299. end;
  300. function TInternalLinkerWasi.GetDataSize(aExeOutput: TExeOutput): QWord;
  301. begin
  302. Result:=GetExeSectionSize(aExeOutput,'.rodata') +
  303. GetExeSectionSize(aExeOutput,'.data') +
  304. GetExeSectionSize(aExeOutput,'fpc.resources');
  305. end;
  306. function TInternalLinkerWasi.GetBssSize(aExeOutput: TExeOutput): QWord;
  307. begin
  308. Result:=GetExeSectionSize(aExeOutput,'.bss') +
  309. GetExeSectionSize(aExeOutput,'.tbss') +
  310. GetExeSectionSize(aExeOutput,'fpc.reshandles');
  311. end;
  312. constructor TInternalLinkerWasi.create;
  313. begin
  314. inherited create;
  315. CExeOutput:=TWasmExeOutput;
  316. CObjInput:=TWasmObjInput;
  317. end;
  318. procedure TInternalLinkerWasi.InitSysInitUnitName;
  319. begin
  320. if current_module.islibrary then
  321. sysinitunit:='si_dll'
  322. else
  323. sysinitunit:='si_prc';
  324. end;
  325. initialization
  326. RegisterTarget(system_wasm32_wasi_info);
  327. RegisterImport(system_wasm32_wasi, timportlibwasi);
  328. RegisterExport(system_wasm32_wasi, texportlibwasi);
  329. RegisterLinker(ld_int_wasi,TInternalLinkerWasi);
  330. RegisterLinker(ld_wasi, tlinkerwasi);
  331. RegisterRes(res_wasm_info,TWinLikeResourceFile);
  332. end.