system.pp 12 KB

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