system.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2000 by the Free Pascal development team.
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. **********************************************************************}
  10. { no stack check in system }
  11. {$S-}
  12. unit system;
  13. interface
  14. {$define StdErrToConsole}
  15. {$define useLongNamespaceByDefault}
  16. {$define autoHeapRelease}
  17. {$ifdef SYSTEMDEBUG}
  18. {$define SYSTEMEXCEPTIONDEBUG}
  19. {$endif SYSTEMDEBUG}
  20. {$ifdef cpui386}
  21. {$define Set_i386_Exception_handler}
  22. {$endif cpui386}
  23. { include system-independent routine headers }
  24. {$I systemh.inc}
  25. {Platform specific information}
  26. const
  27. LineEnding = #13#10;
  28. LFNSupport : boolean = false;
  29. DirectorySeparator = '/';
  30. DriveSeparator = ':';
  31. PathSeparator = ';';
  32. { FileNameCaseSensitive is defined separately below!!! }
  33. maxExitCode = 255;
  34. MaxPathLen = 256;
  35. CONST
  36. { Default filehandles }
  37. UnusedHandle : THandle = -1;
  38. StdInputHandle : THandle = 0;
  39. StdOutputHandle : THandle = 0;
  40. StdErrorHandle : THandle = 0;
  41. FileNameCaseSensitive : boolean = false;
  42. CtrlZMarksEOF: boolean = false; (* #26 not considered as end of file *)
  43. sLineBreak = LineEnding;
  44. DefaultTextLineBreakStyle : TTextLineBreakStyle = tlbsCRLF;
  45. TYPE
  46. TNWCheckFunction = procedure (var code : longint);
  47. VAR
  48. ArgC : INTEGER;
  49. ArgV : ppchar;
  50. NetwareCheckFunction : TNWCheckFunction;
  51. NetwareMainThreadGroupID: longint;
  52. NetwareCodeStartAddress : dword;
  53. NetwareUnloadProc : pointer = nil; {like exitProc but for nlm unload only}
  54. CONST
  55. envp : ppchar = nil; {dummy to make heaptrc happy}
  56. procedure ConsolePrintf (FormatStr : PCHAR; Param : LONGINT); CDecl; external 'clib' name 'printf';
  57. procedure ConsolePrintf (FormatStr : PCHAR; Param : pchar); CDecl; external 'clib' name 'printf';
  58. procedure ConsolePrintf (FormatStr : PCHAR; P1,P2 : LONGINT); CDecl; external 'clib' name 'printf';
  59. procedure ConsolePrintf (FormatStr : PCHAR; P1,P2,P3 : LONGINT); CDecl; external 'clib' name 'printf';
  60. procedure ConsolePrintf (FormatStr : PCHAR); CDecl; external 'clib' name 'printf';
  61. // this gives internal compiler error 200404181
  62. // procedure ConsolePrintf (FormatStr : PCHAR; Param : array of const); CDecl; EXTERNAL 'clib' name 'ConsolePrintf';
  63. procedure __EnterDebugger; cdecl; external 'clib' name 'EnterDebugger';
  64. type
  65. TSysCloseAllRemainingSemaphores = procedure;
  66. TSysReleaseThreadVars = procedure;
  67. TSysSetThreadDataAreaPtr = function (newPtr:pointer):pointer;
  68. procedure NWSysSetThreadFunctions (crs:TSysCloseAllRemainingSemaphores;
  69. rtv:TSysReleaseThreadVars;
  70. stdata:TSysSetThreadDataAreaPtr);
  71. function NWGetCodeStart : pointer; // needed for lineinfo
  72. implementation
  73. { Indicate that stack checking is taken care by OS}
  74. {$DEFINE NO_GENERIC_STACK_CHECK}
  75. { include system independent routines }
  76. {$I system.inc}
  77. //procedure __EnterDebugger; cdecl; external 'clib' name 'EnterDebugger';
  78. procedure PASCALMAIN;external name 'PASCALMAIN';
  79. procedure fpc_do_exit;external name 'FPC_DO_EXIT';
  80. {*****************************************************************************
  81. Startup
  82. *****************************************************************************}
  83. function __GetBssStart : pointer; external name '__getBssStart';
  84. function __getUninitializedDataSize : longint; external name '__getUninitializedDataSize';
  85. //function __getDataStart : longint; external name '__getDataStart';
  86. function __GetTextStart : longint; external name '__getTextStart';
  87. PROCEDURE nlm_main (_ArgC : LONGINT; _ArgV : ppchar); CDECL; [public,alias: '_nlm_main'];
  88. BEGIN
  89. // Initialize BSS
  90. if __getUninitializedDataSize > 0 then
  91. fillchar (__getBssStart^,__getUninitializedDataSize,0);
  92. NetwareCodeStartAddress := __GetTextStart;
  93. ArgC := _ArgC;
  94. ArgV := _ArgV;
  95. fpc_threadvar_relocate_proc := nil;
  96. PASCALMAIN;
  97. END;
  98. function NWGetCodeStart : pointer; // needed for lineinfo
  99. begin
  100. NWGetCodeStart := pointer(NetwareCodeStartAddress);
  101. end;
  102. {*****************************************************************************
  103. System Dependent Exit code
  104. *****************************************************************************}
  105. var SigTermHandlerActive : boolean;
  106. Procedure system_exit;
  107. begin
  108. if TerminatingThreadID <> 0 then
  109. if TerminatingThreadID <> ThreadId then
  110. if TerminatingThreadID <> _GetThreadID then
  111. begin
  112. {$ifdef DEBUG_MT}
  113. ConsolePrintf ('Terminating Thread %x because halt was called while Thread %x terminates nlm'#13#10,_GetThreadId,TerminatingThreadId);
  114. {$endif}
  115. ExitThread (EXIT_THREAD,0);
  116. // only for the case ExitThread fails
  117. while true do
  118. _ThreadSwitchWithDelay;
  119. end;
  120. if assigned (CloseAllRemainingSemaphores) then CloseAllRemainingSemaphores;
  121. if assigned (ReleaseThreadVars) then ReleaseThreadVars;
  122. {$ifdef autoHeapRelease}
  123. FreeSbrkMem; { free memory allocated by heapmanager }
  124. {$endif}
  125. if not SigTermHandlerActive then
  126. begin
  127. if ExitCode <> 0 Then { otherwise we dont see runtime-errors }
  128. _SetAutoScreenDestructionMode (false);
  129. _exit (ExitCode);
  130. end;
  131. end;
  132. {*****************************************************************************
  133. Stack check code
  134. *****************************************************************************}
  135. const StackErr : boolean = false;
  136. procedure int_stackcheck(stack_size:Cardinal);[public,alias:'FPC_STACKCHECK'];
  137. {
  138. called when trying to get local stack if the compiler directive $S
  139. is set this function must preserve all registers
  140. With a 2048 byte safe area used to write to StdIo without crossing
  141. the stack boundary
  142. }
  143. begin
  144. if StackErr then exit; // avoid recursive calls
  145. asm
  146. pusha
  147. end;
  148. stackerr := ( _stackavail < stack_size + 2048);
  149. asm
  150. popa
  151. end;
  152. if not StackErr then exit;
  153. StackErr := true;
  154. HandleError (202);
  155. end;
  156. {*****************************************************************************
  157. ParamStr/Randomize
  158. *****************************************************************************}
  159. { number of args }
  160. function paramcount : longint;
  161. begin
  162. paramcount := argc - 1;
  163. end;
  164. { argument number l }
  165. function paramstr(l : longint) : string;
  166. begin
  167. if (l>=0) and (l+1<=argc) then
  168. begin
  169. paramstr:=strpas(argv[l]);
  170. if l = 0 then // fix nlm path
  171. begin
  172. for l := 1 to length (paramstr) do
  173. if paramstr[l] = '\' then paramstr[l] := '/';
  174. end;
  175. end else
  176. paramstr:='';
  177. end;
  178. { set randseed to a new pseudo random value }
  179. procedure randomize;
  180. begin
  181. randseed := _time (NIL);
  182. end;
  183. {*****************************************************************************
  184. Thread Handling
  185. *****************************************************************************}
  186. procedure InitFPU;assembler;
  187. asm
  188. fninit
  189. fldcw fpucw
  190. end;
  191. { if return-value is <> 0, netware shows the message
  192. Unload Anyway ?
  193. To Disable unload at all, SetNLMDontUnloadFlag can be used on
  194. Netware >= 4.0 }
  195. function CheckFunction : longint; CDECL; [public,alias: 'FPC_NW_CHECKFUNCTION'];
  196. var oldTG:longint;
  197. oldPtr: pointer;
  198. begin
  199. if assigned (NetwareCheckFunction) then
  200. begin
  201. { this function is called without clib context, to allow clib
  202. calls, we set the thread group id before calling the
  203. user-function }
  204. oldTG := _SetThreadGroupID (NetwareMainThreadGroupID);
  205. { to allow use of threadvars, we simply set the threadvar-memory
  206. from the main thread }
  207. if assigned (SetThreadDataAreaPtr) then
  208. oldPtr := SetThreadDataAreaPtr (NIL); { nil means main threadvars }
  209. result := 0;
  210. NetwareCheckFunction (result);
  211. if assigned (SetThreadDataAreaPtr) then
  212. SetThreadDataAreaPtr (oldPtr);
  213. _SetThreadGroupID (oldTG);
  214. end else
  215. result := 0;
  216. end;
  217. {$ifdef StdErrToConsole}
  218. var ConsoleBuff : array [0..512] of char;
  219. Function ConsoleWrite(Var F: TextRec): Integer;
  220. var
  221. i : longint;
  222. Begin
  223. if F.BufPos>0 then
  224. begin
  225. if F.BufPos>sizeof(ConsoleBuff)-1 then
  226. i:=sizeof(ConsoleBuff)-1
  227. else
  228. i:=F.BufPos;
  229. Move(F.BufPtr^,ConsoleBuff,i);
  230. ConsoleBuff[i] := #0;
  231. ConsolePrintf(@ConsoleBuff[0]);
  232. end;
  233. F.BufPos:=0;
  234. ConsoleWrite := 0;
  235. End;
  236. Function ConsoleClose(Var F: TextRec): Integer;
  237. begin
  238. ConsoleClose:=0;
  239. end;
  240. Function ConsoleOpen(Var F: TextRec): Integer;
  241. Begin
  242. TextRec(F).InOutFunc:=@ConsoleWrite;
  243. TextRec(F).FlushFunc:=@ConsoleWrite;
  244. TextRec(F).CloseFunc:=@ConsoleClose;
  245. ConsoleOpen:=0;
  246. End;
  247. procedure AssignStdErrConsole(Var T: Text);
  248. begin
  249. Assign(T,'');
  250. TextRec(T).OpenFunc:=@ConsoleOpen;
  251. Rewrite(T);
  252. end;
  253. {$endif}
  254. { this will be called if the nlm is unloaded. It will NOT be
  255. called if the program exits i.e. with halt.
  256. Halt (or _exit) can not be called from this callback procedure }
  257. procedure TermSigHandler (Sig:longint); CDecl;
  258. var oldTG : longint;
  259. oldPtr: pointer;
  260. err : longint;
  261. current_exit : procedure;
  262. ThreadName : array [0..20] of char;
  263. HadExitProc : boolean;
  264. Count : longint;
  265. begin
  266. oldTG := _SetThreadGroupID (NetwareMainThreadGroupID); { this is only needed for nw 3.11 }
  267. { _GetThreadDataAreaPtr will not be valid because the signal
  268. handler is called by netware with a differnt thread. To avoid
  269. problems in the exit routines, we set the data of the main thread
  270. here }
  271. if assigned (SetThreadDataAreaPtr) then
  272. oldPtr := SetThreadDataAreaPtr (NIL); { nil means main thread }
  273. {this signal handler is called within the console command
  274. thread, the main thread is still running. Via NetwareUnloadProc
  275. running threads may terminate itself}
  276. TerminatingThreadID := _GetThreadID;
  277. {$ifdef DEBUG_MT}
  278. ConsolePrintf (#13'TermSigHandler Called, MainThread:%x, OurThread: %x'#13#10,ThreadId,TerminatingThreadId);
  279. if NetwareUnloadProc <> nil then
  280. ConsolePrintf (#13'Calling NetwareUnloadProcs'#13#10);
  281. {$endif}
  282. HadExitProc := false;
  283. {we need to finalize winock to release threads
  284. waiting on a blocking socket call. If that thread
  285. calls halt, we have to avoid that unit finalization
  286. is called by that thread because we are doing it
  287. here
  288. like the old exitProc, mainly to allow winsock to release threads
  289. blocking in a winsock calls }
  290. while NetwareUnloadProc<>nil Do
  291. Begin
  292. InOutRes:=0;
  293. current_exit:=tProcedure(NetwareUnloadProc);
  294. NetwareUnloadProc:=nil;
  295. current_exit();
  296. _ThreadSwitchWithDelay;
  297. hadExitProc := true;
  298. End;
  299. err := 0;
  300. if hadExitProc then
  301. begin {give the main thread a little bit of time to terminate}
  302. count := 0;
  303. repeat
  304. err := _GetThreadName(ThreadID,ThreadName);
  305. if err = 0 then _Delay (200);
  306. inc(count);
  307. until (err <> 0) or (count > 100); {about 20 seconds}
  308. {$ifdef DEBUG_MT}
  309. if err = 0 then
  310. ConsolePrintf (#13,'Main Thread not terminated'#13#10)
  311. else
  312. ConsolePrintf (#13'Main Thread has ended'#13#10);
  313. {$endif}
  314. end;
  315. if err = 0 then
  316. {$ifdef DEBUG_MT}
  317. begin
  318. err := _SuspendThread(ThreadId);
  319. ConsolePrintf (#13'SuspendThread(%x) returned %d'#13#10,ThreadId,err);
  320. end;
  321. {$else}
  322. _SuspendThread(ThreadId);
  323. {$endif}
  324. _ThreadSwitchWithDelay;
  325. {$ifdef DEBUG_MT}
  326. ConsolePrintf (#13'Calling do_exit'#13#10);
  327. {$endif}
  328. SigTermHandlerActive := true; { to avoid that system_exit calls _exit }
  329. do_exit; { calls finalize units }
  330. if assigned (SetThreadDataAreaPtr) then
  331. SetThreadDataAreaPtr (oldPtr);
  332. _SetThreadGroupID (oldTG);
  333. {$ifdef DEBUG_MT}
  334. ConsolePrintf (#13'TermSigHandler: all done'#13#10);
  335. {$endif}
  336. end;
  337. procedure SysInitStdIO;
  338. begin
  339. { Setup stdin, stdout and stderr }
  340. StdInputHandle := _fileno (LONGINT (_GetStdIn^)); // GetStd** returns **FILE
  341. StdOutputHandle:= _fileno (LONGINT (_GetStdOut^));
  342. StdErrorHandle := _fileno (LONGINT (_GetStdErr^));
  343. OpenStdIO(Input,fmInput,StdInputHandle);
  344. OpenStdIO(Output,fmOutput,StdOutputHandle);
  345. OpenStdIO(StdOut,fmOutput,StdOutputHandle);
  346. {$ifdef StdErrToConsole}
  347. AssignStdErrConsole(StdErr);
  348. AssignStdErrConsole(ErrOutput);
  349. {$else}
  350. OpenStdIO(StdErr,fmOutput,StdErrorHandle);
  351. OpenStdIO(ErrOutput,fmOutput,StdErrorHandle);
  352. {$endif}
  353. end;
  354. function GetProcessID: SizeUInt;
  355. begin
  356. GetProcessID := SizeUInt (GetNlmHandle);
  357. end;
  358. function CheckInitialStkLen(stklen : SizeUInt) : SizeUInt;
  359. begin
  360. result := stklen;
  361. end;
  362. {*****************************************************************************
  363. SystemUnit Initialization
  364. *****************************************************************************}
  365. Begin
  366. StackLength := CheckInitialStkLen(initialstklen);
  367. StackBottom := SPtr - StackLength;
  368. SigTermHandlerActive := false;
  369. NetwareCheckFunction := nil;
  370. NetwareMainThreadGroupID := _GetThreadGroupID;
  371. _Signal (_SIGTERM, @TermSigHandler);
  372. {$ifdef useLongNamespaceByDefault}
  373. if _getenv ('FPC_DISABLE_LONG_NAMESPACE') = nil then
  374. begin
  375. if _SetCurrentNameSpace (NW_NS_LONG) <> 255 then
  376. begin
  377. if _SetTargetNamespace (NW_NS_LONG) <> 255 then
  378. LFNSupport := true
  379. else
  380. _SetCurrentNameSpace (NW_NS_DOS);
  381. end;
  382. end;
  383. {$endif useLongNamespaceByDefault}
  384. { Setup heap }
  385. InitHeap;
  386. SysInitExceptions;
  387. { Reset IO Error }
  388. InOutRes:=0;
  389. ThreadID := _GetThreadID;
  390. {$ifdef DEBUG_MT}
  391. ConsolePrintf (#13'Start system, ThreadID: %x'#13#10,ThreadID);
  392. {$endif}
  393. SysInitStdIO;
  394. {Delphi Compatible}
  395. IsLibrary := FALSE;
  396. IsConsole := TRUE;
  397. ExitCode := 0;
  398. InitSystemThreads;
  399. initvariantmanager;
  400. initwidestringmanager;
  401. End.