system.pp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2020,2021 by the Free Pascal development team.
  4. System unit for The WebAssembly System Interface (WASI).
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit system;
  12. interface
  13. {$DEFINE SYSTEM_HAS_FEATURE_MONITOR}
  14. {$define FPC_IS_SYSTEM}
  15. {$ifdef FPC_WASM_THREADS}
  16. {$define DISABLE_NO_THREAD_MANAGER}
  17. {$else FPC_WASM_THREADS}
  18. {$define USE_NOTHREADMANAGER}
  19. {$endif FPC_WASM_THREADS}
  20. {$I systemh.inc}
  21. const
  22. LineEnding = #10;
  23. LFNSupport = true;
  24. DirectorySeparator = '/';
  25. DriveSeparator = '';
  26. ExtensionSeparator = '.';
  27. PathSeparator = ':';
  28. AllowDirectorySeparators : set of AnsiChar = ['\','/'];
  29. AllowDriveSeparators : set of AnsiChar = [];
  30. { FileNameCaseSensitive and FileNameCasePreserving are defined below! }
  31. maxExitCode = 65535;
  32. MaxPathLen = 4096;
  33. AllFilesMask = '*';
  34. const
  35. UnusedHandle = -1;
  36. StdInputHandle = 0;
  37. StdOutputHandle = 1;
  38. StdErrorHandle = 2;
  39. FileNameCaseSensitive : boolean = true;
  40. FileNameCasePreserving: boolean = true;
  41. CtrlZMarksEOF: boolean = false; (* #26 not considered as end of file *)
  42. sLineBreak = LineEnding;
  43. DefaultTextLineBreakStyle : TTextLineBreakStyle = tlbsLF;
  44. var
  45. argc: longint;
  46. argv: PPAnsiChar;
  47. envp: PPAnsiChar;
  48. ___fpc_wasm_suspender: WasmExternRef; section 'WebAssembly.Global';
  49. function __fpc_get_wasm_suspender: WasmExternRef;
  50. procedure __fpc_set_wasm_suspender(v: WasmExternRef);
  51. property __fpc_wasm_suspender: WasmExternRef read __fpc_get_wasm_suspender write __fpc_set_wasm_suspender;
  52. Procedure DebugWriteln(aString : ShortString);
  53. implementation
  54. {$I wasitypes.inc}
  55. {$I wasiprocs.inc}
  56. function IntToStr(I : Longint) : ShortString;
  57. Var
  58. S : ShortString;
  59. begin
  60. Str(I,S);
  61. IntToStr:=S;
  62. end;
  63. Procedure DebugWriteln(aString : ShortString);
  64. var
  65. our_iov: __wasi_ciovec_t;
  66. our_nwritten: longint;
  67. res: __wasi_errno_t;
  68. begin
  69. our_iov.buf := @aString[1];
  70. our_iov.buf_len := Length(aString);
  71. repeat
  72. res:=__wasi_fd_write(1, @our_iov, 1, @our_nwritten);
  73. if res=__WASI_ERRNO_SUCCESS then
  74. begin
  75. our_iov.buf_len:=our_iov.buf_len-our_nwritten;
  76. our_iov.buf:=our_iov.buf+our_nwritten;
  77. end;
  78. Until (our_iov.buf_len=0) or (res=__WASI_ERRNO_SUCCESS) or ((res<>__WASI_ERRNO_INTR) and (res<>__WASI_ERRNO_AGAIN));
  79. end;
  80. function WasiAlloc (aLength : Longint) : Pointer; [public, alias: 'wasiAlloc'];
  81. begin
  82. Result:=GetMem(aLength);
  83. end;
  84. procedure WasiFree (aMem : pointer); [public, alias: 'wasiFree'];
  85. begin
  86. FreeMem(aMem);
  87. end;
  88. exports
  89. WasiAlloc,WasiFree;
  90. function __fpc_get_wasm_suspender: WasmExternRef;
  91. begin
  92. result:=___fpc_wasm_suspender;
  93. end;
  94. procedure __fpc_set_wasm_suspender(v: WasmExternRef);
  95. begin
  96. ___fpc_wasm_suspender:=v;
  97. end;
  98. function ConvertToFdRelativePath(path: RawByteString; out fd: LongInt; out relfd_path: RawByteString): Word; forward;
  99. function fpc_wasi_path_readlink_ansistring(
  100. fd: __wasi_fd_t;
  101. const path: PAnsiChar;
  102. path_len: size_t;
  103. out link: rawbytestring): __wasi_errno_t;[Public, Alias : 'FPC_WASI_PATH_READLINK_ANSISTRING'];
  104. const
  105. InitialBufLen=64;
  106. MaximumBufLen=16384;
  107. var
  108. CurrentBufLen: __wasi_size_t;
  109. BufUsed: __wasi_size_t;
  110. begin
  111. CurrentBufLen:=InitialBufLen div 2;
  112. repeat
  113. CurrentBufLen:=CurrentBufLen*2;
  114. SetLength(link,CurrentBufLen);
  115. result:=__wasi_path_readlink(fd,path,path_len,@(link[1]),CurrentBufLen,@BufUsed);
  116. until (result<>__WASI_ERRNO_SUCCESS) or ((result=__WASI_ERRNO_SUCCESS) and (BufUsed<CurrentBufLen)) or (CurrentBufLen>MaximumBufLen);
  117. if result=__WASI_ERRNO_SUCCESS then
  118. begin
  119. SetLength(link,BufUsed);
  120. setcodepage(link,DefaultRTLFileSystemCodePage,true);
  121. end
  122. else
  123. link:='';
  124. end;
  125. function HasDriveLetter(const path: rawbytestring): Boolean;
  126. begin
  127. HasDriveLetter:=(Length(path)>=2) and (UpCase(path[1]) in ['A'..'Z']) and (path[2] = ':');
  128. end;
  129. type
  130. PPreopenedDir = ^TPreopenedDir;
  131. TPreopenedDir = record
  132. dir_name: RawByteString;
  133. drive_str: RawByteString;
  134. fd: longint;
  135. end;
  136. PCurrentDir = ^TCurrentDir;
  137. TCurrentDir = record
  138. dir_name: RawByteString;
  139. drive_str: RawByteString;
  140. end;
  141. var
  142. preopened_dirs_count: longint;
  143. preopened_dirs: PPreopenedDir;
  144. drives_count: longint;
  145. current_dirs: PCurrentDir;
  146. current_drive: longint;
  147. {$I system.inc}
  148. var
  149. argv_size,
  150. argv_buf_size: __wasi_size_t;
  151. argv_buf: Pointer;
  152. environc,environ_buf_size,envp_size: __wasi_size_t;
  153. environ_buf: Pointer;
  154. function GetProcessID: SizeUInt;
  155. begin
  156. end;
  157. Procedure Randomize;
  158. Begin
  159. __wasi_random_get(@RandSeed,SizeOf(RandSeed));
  160. End;
  161. procedure System_exit;
  162. begin
  163. if ExitCode>=126 then
  164. begin
  165. Debugwriteln('##WASI-EXITCODE: '+IntToStr(ExitCode)+' -> 125##');
  166. ExitCode:=125;
  167. end;
  168. __wasi_proc_exit(ExitCode);
  169. End;
  170. function Do_ConvertToFdRelativePath(path: RawByteString; out fd: LongInt; out relfd_path: RawByteString): Word;
  171. var
  172. drive_nr,I,pdir_drive,longest_match,chridx: longint;
  173. IsAbsolutePath: Boolean;
  174. pdir: RawByteString;
  175. begin
  176. fd:=0;
  177. relfd_path:='';
  178. if HasDriveLetter(path) then
  179. begin
  180. drive_nr:=Ord(UpCase(path[1]))-(Ord('A')-1);
  181. delete(path,1,2);
  182. end
  183. else
  184. drive_nr:=current_drive;
  185. { path is relative to a current directory? }
  186. if (path='') or not (path[1] in AllowDirectorySeparators) then
  187. begin
  188. { if so, convert to absolute }
  189. if (drive_nr>=drives_count) or (current_dirs[drive_nr].dir_name='') then
  190. begin
  191. Do_ConvertToFdRelativePath:=15;
  192. exit;
  193. end;
  194. if current_dirs[drive_nr].dir_name[Length(current_dirs[drive_nr].dir_name)] in AllowDirectorySeparators then
  195. path:=current_dirs[drive_nr].dir_name+path
  196. else
  197. path:=current_dirs[drive_nr].dir_name+DirectorySeparator+path;
  198. end;
  199. { path is now absolute. Try to find it in the preopened dirs array }
  200. longest_match:=0;
  201. for I:=0 to preopened_dirs_count-1 do
  202. begin
  203. pdir:=preopened_dirs[I].dir_name;
  204. if preopened_dirs[I].drive_str<>'' then
  205. pdir_drive:=Ord(UpCase(preopened_dirs[I].drive_str[1]))-(Ord('A')-1)
  206. else
  207. pdir_drive:=0;
  208. if pdir_drive<>drive_nr then
  209. continue;
  210. if Length(pdir)>Length(path) then
  211. continue;
  212. if Copy(path,1,Length(pdir))<>pdir then
  213. continue;
  214. chridx:=Length(pdir)+1;
  215. if ((Length(pdir)<>1) or ((Length(pdir)=1) and not (pdir[1] in AllowDirectorySeparators))) and
  216. ((chridx>Length(path)) or not (path[chridx] in AllowDirectorySeparators)) then
  217. continue;
  218. if Length(pdir)>longest_match then
  219. begin
  220. longest_match:=Length(pdir);
  221. while (chridx<=Length(path)) and (path[chridx] in AllowDirectorySeparators) do
  222. Inc(chridx);
  223. fd:=preopened_dirs[I].fd;
  224. relfd_path:=Copy(path,chridx,Length(path)-chridx+1);
  225. end;
  226. end;
  227. if longest_match>0 then
  228. Do_ConvertToFdRelativePath:=0
  229. else
  230. Do_ConvertToFdRelativePath:=3;
  231. end;
  232. function ConvertToFdRelativePath(path: RawByteString; out fd: LongInt; out relfd_path: RawByteString): Word;[Public, Alias : 'FPC_WASI_CONVERTTOFDRELATIVEPATH'];
  233. begin
  234. ConvertToFdRelativePath:=Do_ConvertToFdRelativePath(ToSingleByteFileSystemEncodedFileName(path),fd,relfd_path);
  235. setcodepage(relfd_path,DefaultRTLFileSystemCodePage,true);
  236. end;
  237. procedure Setup_PreopenedDirs;
  238. var
  239. fd: __wasi_fd_t;
  240. prestat: __wasi_prestat_t;
  241. res: __wasi_errno_t;
  242. prestat_dir_name: RawByteString;
  243. drive_nr: longint;
  244. begin
  245. preopened_dirs_count:=0;
  246. preopened_dirs:=nil;
  247. drives_count:=0;
  248. current_dirs:=nil;
  249. current_drive:=0;
  250. fd:=3;
  251. repeat
  252. res:=__wasi_fd_prestat_get(fd, @prestat);
  253. if res=__WASI_ERRNO_SUCCESS then
  254. begin
  255. if (prestat.tag=__WASI_PREOPENTYPE_DIR) and (prestat.u.dir.pr_name_len>0) then
  256. begin
  257. SetLength(prestat_dir_name,prestat.u.dir.pr_name_len);
  258. if __wasi_fd_prestat_dir_name(fd,PByte(prestat_dir_name),prestat.u.dir.pr_name_len)=__WASI_ERRNO_SUCCESS then
  259. begin
  260. setcodepage(prestat_dir_name,DefaultRTLFileSystemCodePage,true);
  261. Inc(preopened_dirs_count);
  262. if preopened_dirs=nil then
  263. preopened_dirs:=AllocMem(preopened_dirs_count*SizeOf(TPreopenedDir))
  264. else
  265. ReAllocMem(preopened_dirs, preopened_dirs_count*SizeOf(TPreopenedDir));
  266. preopened_dirs[preopened_dirs_count-1].fd:=fd;
  267. if HasDriveLetter(prestat_dir_name) then
  268. begin
  269. drive_nr:=Ord(UpCase(prestat_dir_name[1]))-(Ord('A')-1);
  270. preopened_dirs[preopened_dirs_count-1].drive_str:=Copy(prestat_dir_name,1,2);
  271. preopened_dirs[preopened_dirs_count-1].dir_name:=Copy(prestat_dir_name,2,Length(prestat_dir_name)-2);
  272. end
  273. else
  274. begin
  275. drive_nr:=0;
  276. preopened_dirs[preopened_dirs_count-1].drive_str:='';
  277. preopened_dirs[preopened_dirs_count-1].dir_name:=prestat_dir_name;
  278. end;
  279. if (drive_nr+1)>drives_count then
  280. begin
  281. drives_count:=drive_nr+1;
  282. if current_dirs=nil then
  283. current_dirs:=AllocMem(drives_count*SizeOf(TCurrentDir))
  284. else
  285. ReAllocMem(current_dirs,drives_count*SizeOf(TCurrentDir));
  286. end;
  287. if current_dirs[drive_nr].dir_name='' then
  288. current_dirs[drive_nr].dir_name:=prestat_dir_name;
  289. end;
  290. end;
  291. end;
  292. Inc(fd);
  293. until res<>__WASI_ERRNO_SUCCESS;
  294. while (current_drive<(drives_count-1)) and (current_dirs[current_drive].dir_name='') do
  295. Inc(current_drive);
  296. for drive_nr:=0 to drives_count-1 do
  297. begin
  298. if drive_nr>0 then
  299. current_dirs[drive_nr].drive_str:=Chr(Ord('A')+drive_nr-1)+':';
  300. if current_dirs[drive_nr].dir_name='' then
  301. current_dirs[drive_nr].dir_name:=DirectorySeparator;
  302. end;
  303. end;
  304. procedure Setup_Environment;
  305. begin
  306. if envp<>nil then
  307. exit;
  308. if __wasi_environ_sizes_get(@environc, @environ_buf_size)<>__WASI_ERRNO_SUCCESS then
  309. begin
  310. envp:=nil;
  311. exit;
  312. end;
  313. envp_size:=(environc+1)*SizeOf(PAnsiChar);
  314. GetMem(envp, envp_size);
  315. GetMem(environ_buf, environ_buf_size);
  316. envp[environc]:=nil;
  317. if __wasi_environ_get(Pointer(envp), environ_buf)<>__WASI_ERRNO_SUCCESS then
  318. begin
  319. FreeMem(envp, envp_size);
  320. FreeMem(environ_buf, environ_buf_size);
  321. envp:=nil;
  322. end;
  323. end;
  324. procedure setup_arguments;
  325. begin
  326. if argv<>nil then
  327. exit;
  328. if __wasi_args_sizes_get(@argc, @argv_buf_size)<>__WASI_ERRNO_SUCCESS then
  329. begin
  330. argc:=0;
  331. argv:=nil;
  332. exit;
  333. end;
  334. argv_size:=(argc+1)*SizeOf(PAnsiChar);
  335. GetMem(argv, argv_size);
  336. GetMem(argv_buf, argv_buf_size);
  337. if __wasi_args_get(Pointer(argv), argv_buf)<>__WASI_ERRNO_SUCCESS then
  338. begin
  339. FreeMem(argv, argv_size);
  340. FreeMem(argv_buf, argv_buf_size);
  341. argc:=0;
  342. argv:=nil;
  343. end;
  344. end;
  345. Function ParamCount: Longint;
  346. Begin
  347. if argv=nil then
  348. setup_arguments;
  349. paramcount := argc - 1;
  350. End;
  351. function paramstr(l: longint) : shortstring;
  352. begin
  353. if argv=nil then
  354. setup_arguments;
  355. if (l>=0) and (l+1<=argc) then
  356. paramstr:=strpas(argv[l])
  357. else
  358. paramstr:='';
  359. end;
  360. procedure SysInitStdIO;
  361. begin
  362. OpenStdIO(Input,fmInput,StdInputHandle);
  363. OpenStdIO(Output,fmOutput,StdOutputHandle);
  364. OpenStdIO(ErrOutput,fmOutput,StdErrorHandle);
  365. OpenStdIO(StdOut,fmOutput,StdOutputHandle);
  366. OpenStdIO(StdErr,fmOutput,StdErrorHandle);
  367. end;
  368. function CheckInitialStkLen(stklen : SizeUInt) : SizeUInt;
  369. begin
  370. end;
  371. begin
  372. { To be set if this is a GUI or console application }
  373. IsConsole := TRUE;
  374. {$ifdef FPC_HAS_FEATURE_DYNLIBS}
  375. { If dynlibs feature is disabled,
  376. IsLibrary is a constant, which can thus not be set to a value }
  377. { To be set if this is a library and not a program }
  378. IsLibrary := FALSE;
  379. {$endif def FPC_HAS_FEATURE_DYNLIBS}
  380. { Setup heap }
  381. InitHeap;
  382. SysInitExceptions;
  383. initunicodestringmanager;
  384. { Reset IO Error }
  385. InOutRes:=0;
  386. {$ifdef FPC_WASM_THREADS}
  387. InitSystemThreads;
  388. InitThreadVars(@WasiRelocateThreadVar);
  389. {$endif}
  390. { Setup stdin, stdout and stderr }
  391. SysInitStdIO;
  392. Setup_Environment;
  393. Setup_PreopenedDirs;
  394. TLSInfoBlock:=Nil;
  395. end.