system.pp 12 KB

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