system.pp 14 KB

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