system.pp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2006 by Florian Klaempfl and Pavel Ozerski
  4. member of the Free Pascal development team.
  5. FPC Pascal system unit for the Win64 API.
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. unit System;
  13. interface
  14. { $define SYSTEMEXCEPTIONDEBUG}
  15. {$ifdef SYSTEMDEBUG}
  16. {$define SYSTEMEXCEPTIONDEBUG}
  17. {$endif SYSTEMDEBUG}
  18. {$define DISABLE_NO_THREAD_MANAGER}
  19. {$define HAS_WIDESTRINGMANAGER}
  20. { include system-independent routine headers }
  21. {$I systemh.inc}
  22. const
  23. LineEnding = #13#10;
  24. LFNSupport = true;
  25. DirectorySeparator = '\';
  26. DriveSeparator = ':';
  27. ExtensionSeparator = '.';
  28. PathSeparator = ';';
  29. AllowDirectorySeparators : set of char = ['\','/'];
  30. AllowDriveSeparators : set of char = [':'];
  31. { FileNameCaseSensitive is defined separately below!!! }
  32. maxExitCode = 65535;
  33. MaxPathLen = 260;
  34. AllFilesMask = '*';
  35. type
  36. PEXCEPTION_FRAME = ^TEXCEPTION_FRAME;
  37. TEXCEPTION_FRAME = record
  38. next : PEXCEPTION_FRAME;
  39. handler : pointer;
  40. end;
  41. const
  42. { Default filehandles }
  43. UnusedHandle : THandle = THandle(-1);
  44. StdInputHandle : THandle = 0;
  45. StdOutputHandle : THandle = 0;
  46. StdErrorHandle : THandle = 0;
  47. System_exception_frame : PEXCEPTION_FRAME =nil;
  48. FileNameCaseSensitive : boolean = true;
  49. CtrlZMarksEOF: boolean = true; (* #26 is considered as end of file *)
  50. sLineBreak = LineEnding;
  51. DefaultTextLineBreakStyle : TTextLineBreakStyle = tlbsCRLF;
  52. type
  53. TStartupInfo = record
  54. cb : longint;
  55. lpReserved : Pointer;
  56. lpDesktop : Pointer;
  57. lpTitle : Pointer;
  58. dwX : longint;
  59. dwY : longint;
  60. dwXSize : longint;
  61. dwYSize : longint;
  62. dwXCountChars : longint;
  63. dwYCountChars : longint;
  64. dwFillAttribute : longint;
  65. dwFlags : longint;
  66. wShowWindow : Word;
  67. cbReserved2 : Word;
  68. lpReserved2 : Pointer;
  69. hStdInput : THandle;
  70. hStdOutput : THandle;
  71. hStdError : THandle;
  72. end;
  73. var
  74. { C compatible arguments }
  75. argc : longint;
  76. argv : ppchar;
  77. { Win32 Info }
  78. startupinfo : tstartupinfo;
  79. StartupConsoleMode : dword;
  80. hprevinst,
  81. MainInstance : qword;
  82. cmdshow : longint;
  83. DLLreason,DLLparam:longint;
  84. type
  85. TDLL_Entry_Hook = procedure (dllparam : longint);
  86. const
  87. Dll_Process_Detach_Hook : TDLL_Entry_Hook = nil;
  88. Dll_Thread_Attach_Hook : TDLL_Entry_Hook = nil;
  89. Dll_Thread_Detach_Hook : TDLL_Entry_Hook = nil;
  90. Const
  91. { it can be discussed whether fmShareDenyNone means read and write or read, write and delete, see
  92. also http://bugs.freepascal.org/view.php?id=8898, this allows users to configure the used
  93. value
  94. }
  95. fmShareDenyNoneFlags : DWord = 3;
  96. implementation
  97. var
  98. SysInstance : qword;public;
  99. { used by wstrings.inc because wstrings.inc is included before sysos.inc
  100. this is put here (FK) }
  101. function SysAllocStringLen(psz:pointer;len:dword):pointer;stdcall;
  102. external 'oleaut32.dll' name 'SysAllocStringLen';
  103. procedure SysFreeString(bstr:pointer);stdcall;
  104. external 'oleaut32.dll' name 'SysFreeString';
  105. function SysReAllocStringLen(var bstr:pointer;psz: pointer;
  106. len:dword): Integer; stdcall;external 'oleaut32.dll' name 'SysReAllocStringLen';
  107. { include system independent routines }
  108. {$I system.inc}
  109. {*****************************************************************************
  110. System Dependent Exit code
  111. *****************************************************************************}
  112. procedure install_exception_handlers;forward;
  113. procedure remove_exception_handlers;forward;
  114. procedure PascalMain;stdcall;external name 'PASCALMAIN';
  115. procedure fpc_do_exit;stdcall;external name 'FPC_DO_EXIT';
  116. Procedure ExitDLL(Exitcode : longint); forward;
  117. Procedure system_exit;
  118. begin
  119. { don't call ExitProcess inside
  120. the DLL exit code !!
  121. This crashes Win95 at least PM }
  122. if IsLibrary then
  123. ExitDLL(ExitCode);
  124. if not IsConsole then
  125. begin
  126. Close(stderr);
  127. Close(stdout);
  128. Close(erroutput);
  129. Close(Input);
  130. Close(Output);
  131. { what about Input and Output ?? PM }
  132. { now handled, FPK }
  133. end;
  134. remove_exception_handlers;
  135. { call exitprocess, with cleanup as required }
  136. ExitProcess(exitcode);
  137. end;
  138. var
  139. { old compilers emitted a reference to _fltused if a module contains
  140. floating type code so the linker could leave away floating point
  141. libraries or not. VC does this as well so we need to define this
  142. symbol as well (FK)
  143. }
  144. _fltused : int64;cvar;public;
  145. { value of the stack segment
  146. to check if the call stack can be written on exceptions }
  147. _SS : Cardinal;
  148. procedure Exe_entry;[public,alias:'_FPC_EXE_Entry'];
  149. var
  150. ST : pointer;
  151. begin
  152. IsLibrary:=false;
  153. { install the handlers for exe only ?
  154. or should we install them for DLL also ? (PM) }
  155. install_exception_handlers;
  156. ExitCode:=0;
  157. asm
  158. { allocate space for an exception frame }
  159. pushq $0
  160. pushq %gs:(0)
  161. { movl %rsp,%gs:(0)
  162. but don't insert it as it doesn't
  163. point to anything yet
  164. this will be used in signals unit }
  165. movq %rsp,%rax
  166. {$ifdef FPC_HAS_RIP_RELATIVE}
  167. movq %rax,System_exception_frame(%rip)
  168. {$else}
  169. movq %rax,System_exception_frame
  170. {$endif}
  171. { keep stack aligned }
  172. pushq $0
  173. pushq %rbp
  174. movq %rsp,%rax
  175. movq %rax,st
  176. end;
  177. StackTop:=st;
  178. asm
  179. xorq %rax,%rax
  180. movw %ss,%ax
  181. {$ifdef FPC_HAS_RIP_RELATIVE}
  182. movl %eax,_SS(%rip)
  183. {$else}
  184. movl %eax,_SS
  185. {$endif}
  186. xorq %rbp,%rbp
  187. call PASCALMAIN
  188. popq %rbp
  189. popq %rax
  190. end;
  191. { if we pass here there was no error ! }
  192. system_exit;
  193. end;
  194. function GetConsoleMode(hConsoleHandle: THandle; var lpMode: DWORD): Boolean; stdcall; external 'kernel32' name 'GetConsoleMode';
  195. function Dll_entry{$ifdef FPC_HAS_INDIRECT_MAIN_INFORMATION}(const info : TEntryInformation){$endif FPC_HAS_INDIRECT_MAIN_INFORMATION} : longbool;forward;
  196. procedure _FPC_DLLMainCRTStartup(_hinstance : qword;_dllreason,_dllparam:longint);stdcall;public name '_DLLMainCRTStartup';
  197. begin
  198. IsConsole:=true;
  199. sysinstance:=_hinstance;
  200. dllreason:=_dllreason;
  201. dllparam:=_dllparam;
  202. DLL_Entry;
  203. end;
  204. procedure _FPC_DLLWinMainCRTStartup(_hinstance : qword;_dllreason,_dllparam:longint);stdcall;public name '_DLLWinMainCRTStartup';
  205. begin
  206. IsConsole:=false;
  207. sysinstance:=_hinstance;
  208. dllreason:=_dllreason;
  209. dllparam:=_dllparam;
  210. DLL_Entry;
  211. end;
  212. function GetCurrentProcess : dword;
  213. stdcall;external 'kernel32' name 'GetCurrentProcess';
  214. function ReadProcessMemory(process : dword;address : pointer;dest : pointer;size : dword;bytesread : pdword) : longbool;
  215. stdcall;external 'kernel32' name 'ReadProcessMemory';
  216. function is_prefetch(p : pointer) : boolean;
  217. var
  218. a : array[0..15] of byte;
  219. doagain : boolean;
  220. instrlo,instrhi,opcode : byte;
  221. i : longint;
  222. begin
  223. result:=false;
  224. { read memory savely without causing another exeception }
  225. if not(ReadProcessMemory(GetCurrentProcess,p,@a,sizeof(a),nil)) then
  226. exit;
  227. i:=0;
  228. doagain:=true;
  229. while doagain and (i<15) do
  230. begin
  231. opcode:=a[i];
  232. instrlo:=opcode and $f;
  233. instrhi:=opcode and $f0;
  234. case instrhi of
  235. { prefix? }
  236. $20,$30:
  237. doagain:=(instrlo and 7)=6;
  238. $60:
  239. doagain:=(instrlo and $c)=4;
  240. $f0:
  241. doagain:=instrlo in [0,2,3];
  242. $0:
  243. begin
  244. result:=(instrlo=$f) and (a[i+1] in [$d,$18]);
  245. exit;
  246. end;
  247. else
  248. doagain:=false;
  249. end;
  250. inc(i);
  251. end;
  252. end;
  253. {******************************************************************************}
  254. { include code common with win64 }
  255. {$I syswin.inc}
  256. {******************************************************************************}
  257. //
  258. // Hardware exception handling
  259. //
  260. type
  261. M128A = record
  262. Low : QWord;
  263. High : Int64;
  264. end;
  265. PContext = ^TContext;
  266. TContext = record
  267. P1Home : QWord;
  268. P2Home : QWord;
  269. P3Home : QWord;
  270. P4Home : QWord;
  271. P5Home : QWord;
  272. P6Home : QWord;
  273. ContextFlags : DWord;
  274. MxCsr : DWord;
  275. SegCs : word;
  276. SegDs : word;
  277. SegEs : word;
  278. SegFs : word;
  279. SegGs : word;
  280. SegSs : word;
  281. EFlags : DWord;
  282. Dr0 : QWord;
  283. Dr1 : QWord;
  284. Dr2 : QWord;
  285. Dr3 : QWord;
  286. Dr6 : QWord;
  287. Dr7 : QWord;
  288. Rax : QWord;
  289. Rcx : QWord;
  290. Rdx : QWord;
  291. Rbx : QWord;
  292. Rsp : QWord;
  293. Rbp : QWord;
  294. Rsi : QWord;
  295. Rdi : QWord;
  296. R8 : QWord;
  297. R9 : QWord;
  298. R10 : QWord;
  299. R11 : QWord;
  300. R12 : QWord;
  301. R13 : QWord;
  302. R14 : QWord;
  303. R15 : QWord;
  304. Rip : QWord;
  305. Header : array[0..1] of M128A;
  306. Legacy : array[0..7] of M128A;
  307. Xmm0 : M128A;
  308. Xmm1 : M128A;
  309. Xmm2 : M128A;
  310. Xmm3 : M128A;
  311. Xmm4 : M128A;
  312. Xmm5 : M128A;
  313. Xmm6 : M128A;
  314. Xmm7 : M128A;
  315. Xmm8 : M128A;
  316. Xmm9 : M128A;
  317. Xmm10 : M128A;
  318. Xmm11 : M128A;
  319. Xmm12 : M128A;
  320. Xmm13 : M128A;
  321. Xmm14 : M128A;
  322. Xmm15 : M128A;
  323. VectorRegister : array[0..25] of M128A;
  324. VectorControl : QWord;
  325. DebugControl : QWord;
  326. LastBranchToRip : QWord;
  327. LastBranchFromRip : QWord;
  328. LastExceptionToRip : QWord;
  329. LastExceptionFromRip : QWord;
  330. end;
  331. type
  332. PExceptionRecord = ^TExceptionRecord;
  333. TExceptionRecord = record
  334. ExceptionCode : DWord;
  335. ExceptionFlags : DWord;
  336. ExceptionRecord : PExceptionRecord;
  337. ExceptionAddress : Pointer;
  338. NumberParameters : DWord;
  339. ExceptionInformation : array[0..EXCEPTION_MAXIMUM_PARAMETERS-1] of Pointer;
  340. end;
  341. PExceptionPointers = ^TExceptionPointers;
  342. TExceptionPointers = packed record
  343. ExceptionRecord : PExceptionRecord;
  344. ContextRecord : PContext;
  345. end;
  346. TVectoredExceptionHandler = function (excep : PExceptionPointers) : Longint;
  347. function AddVectoredExceptionHandler(FirstHandler : DWORD;VectoredHandler : TVectoredExceptionHandler) : longint;
  348. external 'kernel32' name 'AddVectoredExceptionHandler';
  349. const
  350. MaxExceptionLevel = 16;
  351. exceptLevel : Byte = 0;
  352. var
  353. exceptRip : array[0..MaxExceptionLevel-1] of Int64;
  354. exceptError : array[0..MaxExceptionLevel-1] of Byte;
  355. resetFPU : array[0..MaxExceptionLevel-1] of Boolean;
  356. {$ifdef SYSTEMEXCEPTIONDEBUG}
  357. procedure DebugHandleErrorAddrFrame(error : longint; addr, frame : pointer);
  358. begin
  359. if IsConsole then
  360. begin
  361. write(stderr,'HandleErrorAddrFrame(error=',error);
  362. write(stderr,',addr=',hexstr(int64(addr),16));
  363. writeln(stderr,',frame=',hexstr(int64(frame),16),')');
  364. end;
  365. HandleErrorAddrFrame(error,addr,frame);
  366. end;
  367. {$endif SYSTEMEXCEPTIONDEBUG}
  368. procedure JumpToHandleErrorFrame;
  369. var
  370. rip, rbp : int64;
  371. error : longint;
  372. begin
  373. // save ebp
  374. asm
  375. movq (%rbp),%rax
  376. movq %rax,rbp
  377. end;
  378. if exceptLevel>0 then
  379. dec(exceptLevel);
  380. rip:=exceptRip[exceptLevel];
  381. error:=exceptError[exceptLevel];
  382. {$ifdef SYSTEMEXCEPTIONDEBUG}
  383. if IsConsole then
  384. writeln(stderr,'In JumpToHandleErrorFrame error=',error);
  385. {$endif SYSTEMEXCEPTIONDEBUG}
  386. if resetFPU[exceptLevel] then
  387. SysResetFPU;
  388. { build a fake stack }
  389. asm
  390. movq rbp,%r8
  391. movq rip,%rdx
  392. movl error,%ecx
  393. pushq rip
  394. movq rbp,%rbp // Change frame pointer
  395. {$ifdef SYSTEMEXCEPTIONDEBUG}
  396. jmpl DebugHandleErrorAddrFrame
  397. {$else not SYSTEMEXCEPTIONDEBUG}
  398. jmpl HandleErrorAddrFrame
  399. {$endif SYSTEMEXCEPTIONDEBUG}
  400. end;
  401. end;
  402. function syswin64_x86_64_exception_handler(excep : PExceptionPointers) : Longint;public;
  403. var
  404. res: longint;
  405. err: byte;
  406. must_reset_fpu: boolean;
  407. begin
  408. res:=EXCEPTION_CONTINUE_SEARCH;
  409. {$ifdef SYSTEMEXCEPTIONDEBUG}
  410. if IsConsole then
  411. Writeln(stderr,'syswin64_x86_64_exception_handler called');
  412. {$endif SYSTEMEXCEPTIONDEBUG}
  413. if excep^.ContextRecord^.SegSs=_SS then
  414. begin
  415. err := 0;
  416. must_reset_fpu := true;
  417. {$ifdef SYSTEMEXCEPTIONDEBUG}
  418. if IsConsole then Writeln(stderr,'Exception ',
  419. hexstr(excep^.ExceptionRecord^.ExceptionCode,8));
  420. {$endif SYSTEMEXCEPTIONDEBUG}
  421. case cardinal(excep^.ExceptionRecord^.ExceptionCode) of
  422. STATUS_INTEGER_DIVIDE_BY_ZERO,
  423. STATUS_FLOAT_DIVIDE_BY_ZERO :
  424. err := 200;
  425. STATUS_ARRAY_BOUNDS_EXCEEDED :
  426. begin
  427. err := 201;
  428. must_reset_fpu := false;
  429. end;
  430. STATUS_STACK_OVERFLOW :
  431. begin
  432. err := 202;
  433. must_reset_fpu := false;
  434. end;
  435. STATUS_FLOAT_OVERFLOW :
  436. err := 205;
  437. STATUS_FLOAT_DENORMAL_OPERAND,
  438. STATUS_FLOAT_UNDERFLOW :
  439. err := 206;
  440. { excep^.ContextRecord^.FloatSave.StatusWord := excep^.ContextRecord^.FloatSave.StatusWord and $ffffff00;}
  441. STATUS_FLOAT_INEXACT_RESULT,
  442. STATUS_FLOAT_INVALID_OPERATION,
  443. STATUS_FLOAT_STACK_CHECK :
  444. err := 207;
  445. STATUS_INTEGER_OVERFLOW :
  446. begin
  447. err := 215;
  448. must_reset_fpu := false;
  449. end;
  450. STATUS_ILLEGAL_INSTRUCTION:
  451. err := 216;
  452. STATUS_ACCESS_VIOLATION:
  453. { Athlon prefetch bug? }
  454. if is_prefetch(pointer(excep^.ContextRecord^.rip)) then
  455. begin
  456. { if yes, then retry }
  457. excep^.ExceptionRecord^.ExceptionCode := 0;
  458. res:=EXCEPTION_CONTINUE_EXECUTION;
  459. end
  460. else
  461. err := 216;
  462. STATUS_CONTROL_C_EXIT:
  463. err := 217;
  464. STATUS_PRIVILEGED_INSTRUCTION:
  465. begin
  466. err := 218;
  467. must_reset_fpu := false;
  468. end;
  469. else
  470. begin
  471. if ((excep^.ExceptionRecord^.ExceptionCode and SEVERITY_ERROR) = SEVERITY_ERROR) then
  472. err := 217
  473. else
  474. { pass through exceptions which aren't an error. The problem is that vectored handlers
  475. always are called before structured ones so we see also internal exceptions of libraries.
  476. I wonder if there is a better solution (FK)
  477. }
  478. res:=EXCEPTION_CONTINUE_SEARCH;
  479. end;
  480. end;
  481. if (err <> 0) and (exceptLevel < MaxExceptionLevel) then
  482. begin
  483. exceptRip[exceptLevel] := excep^.ContextRecord^.Rip;
  484. exceptError[exceptLevel] := err;
  485. resetFPU[exceptLevel] := must_reset_fpu;
  486. inc(exceptLevel);
  487. excep^.ContextRecord^.Rip := Int64(@JumpToHandleErrorFrame);
  488. excep^.ExceptionRecord^.ExceptionCode := 0;
  489. res := EXCEPTION_CONTINUE_EXECUTION;
  490. {$ifdef SYSTEMEXCEPTIONDEBUG}
  491. if IsConsole then begin
  492. writeln(stderr,'Exception Continue Exception set at ',
  493. hexstr(exceptRip[exceptLevel-1],16));
  494. writeln(stderr,'Rip changed to ',
  495. hexstr(int64(@JumpToHandleErrorFrame),16), ' error=', err);
  496. end;
  497. {$endif SYSTEMEXCEPTIONDEBUG}
  498. end;
  499. end;
  500. syswin64_x86_64_exception_handler := res;
  501. end;
  502. procedure install_exception_handlers;
  503. begin
  504. AddVectoredExceptionHandler(1,@syswin64_x86_64_exception_handler);
  505. end;
  506. procedure remove_exception_handlers;
  507. begin
  508. end;
  509. procedure fpc_cpucodeinit;
  510. begin
  511. end;
  512. procedure LinkIn(p1,p2,p3: Pointer); inline;
  513. begin
  514. end;
  515. procedure _FPC_mainCRTStartup;stdcall;public name '_mainCRTStartup';
  516. begin
  517. IsConsole:=true;
  518. GetConsoleMode(GetStdHandle((Std_Input_Handle)),StartupConsoleMode);
  519. {$ifdef FPC_USE_TLS_DIRECTORY}
  520. LinkIn(@_tls_used,@FreePascal_TLS_callback,@FreePascal_end_of_TLS_callback);
  521. {$endif FPC_USE_TLS_DIRECTORY}
  522. Exe_entry;
  523. end;
  524. procedure _FPC_WinMainCRTStartup;stdcall;public name '_WinMainCRTStartup';
  525. begin
  526. IsConsole:=false;
  527. {$ifdef FPC_USE_TLS_DIRECTORY}
  528. LinkIn(@_tls_used,@FreePascal_TLS_callback,@FreePascal_end_of_TLS_callback);
  529. {$endif FPC_USE_TLS_DIRECTORY}
  530. Exe_entry;
  531. end;
  532. function CheckInitialStkLen(stklen : SizeUInt) : SizeUInt;assembler;
  533. asm
  534. movq %gs:(8),%rax
  535. subq %gs:(16),%rax
  536. end;
  537. begin
  538. SysResetFPU;
  539. if not(IsLibrary) then
  540. SysInitFPU;
  541. { pass dummy value }
  542. StackLength := CheckInitialStkLen($1000000);
  543. StackBottom := StackTop - StackLength;
  544. { get some helpful informations }
  545. GetStartupInfo(@startupinfo);
  546. { some misc Win32 stuff }
  547. hprevinst:=0;
  548. if not IsLibrary then
  549. SysInstance:=getmodulehandle(nil);
  550. MainInstance:=SysInstance;
  551. cmdshow:=startupinfo.wshowwindow;
  552. { Setup heap }
  553. InitHeap;
  554. SysInitExceptions;
  555. { setup fastmove stuff }
  556. fpc_cpucodeinit;
  557. SysInitStdIO;
  558. { Arguments }
  559. setup_arguments;
  560. { Reset IO Error }
  561. InOutRes:=0;
  562. ProcessID := GetCurrentProcessID;
  563. { threading }
  564. InitSystemThreads;
  565. { Reset internal error variable }
  566. errno:=0;
  567. initvariantmanager;
  568. initwidestringmanager;
  569. {$ifndef VER2_2}
  570. initunicodestringmanager;
  571. {$endif VER2_2}
  572. InitWin32Widestrings;
  573. DispCallByIDProc:=@DoDispCallByIDError;
  574. end.