{ This file is part of the Free Pascal run time library. Copyright (c) 1999-2006 by Florian Klaempfl and Pavel Ozerski member of the Free Pascal development team. FPC Pascal system unit for the Win64 API. See the file COPYING.FPC, included in this distribution, for details about the copyright. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. **********************************************************************} unit System; interface { $define SYSTEMEXCEPTIONDEBUG} {$ifdef SYSTEMDEBUG} {$define SYSTEMEXCEPTIONDEBUG} {$endif SYSTEMDEBUG} {$define DISABLE_NO_THREAD_MANAGER} { include system-independent routine headers } {$I systemh.inc} const LineEnding = #13#10; LFNSupport = true; DirectorySeparator = '\'; DriveSeparator = ':'; ExtensionSeparator = '.'; PathSeparator = ';'; AllowDirectorySeparators : set of char = ['\','/']; AllowDriveSeparators : set of char = [':']; { FileNameCaseSensitive is defined separately below!!! } maxExitCode = 65535; MaxPathLen = 260; AllFilesMask = '*'; type PEXCEPTION_FRAME = ^TEXCEPTION_FRAME; TEXCEPTION_FRAME = record next : PEXCEPTION_FRAME; handler : pointer; end; const { Default filehandles } UnusedHandle : THandle = THandle(-1); StdInputHandle : THandle = 0; StdOutputHandle : THandle = 0; StdErrorHandle : THandle = 0; FileNameCaseSensitive : boolean = true; CtrlZMarksEOF: boolean = true; (* #26 is considered as end of file *) sLineBreak = LineEnding; DefaultTextLineBreakStyle : TTextLineBreakStyle = tlbsCRLF; { Thread count for DLL } Thread_count : longint = 0; type TStartupInfo = record cb : longint; lpReserved : Pointer; lpDesktop : Pointer; lpTitle : Pointer; dwX : longint; dwY : longint; dwXSize : longint; dwYSize : longint; dwXCountChars : longint; dwYCountChars : longint; dwFillAttribute : longint; dwFlags : longint; wShowWindow : Word; cbReserved2 : Word; lpReserved2 : Pointer; hStdInput : THandle; hStdOutput : THandle; hStdError : THandle; end; var { C compatible arguments } argc : longint; argv : ppchar; { Win32 Info } startupinfo : tstartupinfo; hprevinst, MainInstance : qword; cmdshow : longint; DLLreason,DLLparam:longint; type TDLL_Entry_Hook = procedure (dllparam : longint); const Dll_Process_Detach_Hook : TDLL_Entry_Hook = nil; Dll_Thread_Attach_Hook : TDLL_Entry_Hook = nil; Dll_Thread_Detach_Hook : TDLL_Entry_Hook = nil; Const { it can be discussed whether fmShareDenyNone means read and write or read, write and delete, see also http://bugs.freepascal.org/view.php?id=8898, this allows users to configure the used value } fmShareDenyNoneFlags : DWord = 3; implementation var SysInstance : qword;public; { used by wstrings.inc because wstrings.inc is included before sysos.inc this is put here (FK) } function SysAllocStringLen(psz:pointer;len:dword):pointer;stdcall; external 'oleaut32.dll' name 'SysAllocStringLen'; procedure SysFreeString(bstr:pointer);stdcall; external 'oleaut32.dll' name 'SysFreeString'; function SysReAllocStringLen(var bstr:pointer;psz: pointer; len:dword): Integer; stdcall;external 'oleaut32.dll' name 'SysReAllocStringLen'; { include system independent routines } {$I system.inc} {***************************************************************************** Parameter Handling *****************************************************************************} procedure setup_arguments; var arglen, count : longint; argstart, pc,arg : pchar; quote : char; argvlen : longint; buf: array[0..259] of char; // need MAX_PATH bytes, not 256! procedure allocarg(idx,len:longint); var oldargvlen : longint; begin if idx>=argvlen then begin oldargvlen:=argvlen; argvlen:=(idx+8) and (not 7); sysreallocmem(argv,argvlen*sizeof(pointer)); fillchar(argv[oldargvlen],(argvlen-oldargvlen)*sizeof(pointer),0); end; { use realloc to reuse already existing memory } { always allocate, even if length is zero, since } { the arg. is still present! } sysreallocmem(argv[idx],len+1); end; begin { create commandline, it starts with the executed filename which is argv[0] } { Win32 passes the command NOT via the args, but via getmodulefilename} count:=0; argv:=nil; argvlen:=0; ArgLen := GetModuleFileName(0, @buf[0], sizeof(buf)); buf[ArgLen] := #0; // be safe allocarg(0,arglen); move(buf,argv[0]^,arglen+1); { Setup cmdline variable } cmdline:=GetCommandLine; { process arguments } pc:=cmdline; {$IfDef SYSTEM_DEBUG_STARTUP} Writeln(stderr,'Win32 GetCommandLine is #',pc,'#'); {$EndIf } while pc^<>#0 do begin { skip leading spaces } while pc^ in [#1..#32] do inc(pc); if pc^=#0 then break; { calc argument length } quote:=' '; argstart:=pc; arglen:=0; while (pc^<>#0) do begin case pc^ of #1..#32 : begin if quote<>' ' then inc(arglen) else break; end; '"' : begin if quote<>'''' then begin if pchar(pc+1)^<>'"' then begin if quote='"' then quote:=' ' else quote:='"'; end else inc(pc); end else inc(arglen); end; '''' : begin if quote<>'"' then begin if pchar(pc+1)^<>'''' then begin if quote='''' then quote:=' ' else quote:=''''; end else inc(pc); end else inc(arglen); end; else inc(arglen); end; inc(pc); end; { copy argument } { Don't copy the first one, it is already there.} If Count<>0 then begin allocarg(count,arglen); quote:=' '; pc:=argstart; arg:=argv[count]; while (pc^<>#0) do begin case pc^ of #1..#32 : begin if quote<>' ' then begin arg^:=pc^; inc(arg); end else break; end; '"' : begin if quote<>'''' then begin if pchar(pc+1)^<>'"' then begin if quote='"' then quote:=' ' else quote:='"'; end else inc(pc); end else begin arg^:=pc^; inc(arg); end; end; '''' : begin if quote<>'"' then begin if pchar(pc+1)^<>'''' then begin if quote='''' then quote:=' ' else quote:=''''; end else inc(pc); end else begin arg^:=pc^; inc(arg); end; end; else begin arg^:=pc^; inc(arg); end; end; inc(pc); end; arg^:=#0; end; {$IfDef SYSTEM_DEBUG_STARTUP} Writeln(stderr,'dos arg ',count,' #',arglen,'#',argv[count],'#'); {$EndIf SYSTEM_DEBUG_STARTUP} inc(count); end; { get argc } argc:=count; { free unused memory, leaving a nil entry at the end } sysreallocmem(argv,(count+1)*sizeof(pointer)); argv[count] := nil; end; function paramcount : longint; begin paramcount := argc - 1; end; function paramstr(l : longint) : string; begin if (l>=0) and (l0 then dec(exceptLevel); rip:=exceptRip[exceptLevel]; error:=exceptError[exceptLevel]; {$ifdef SYSTEMEXCEPTIONDEBUG} if IsConsole then writeln(stderr,'In JumpToHandleErrorFrame error=',error); {$endif SYSTEMEXCEPTIONDEBUG} if resetFPU[exceptLevel] then SysResetFPU; { build a fake stack } asm movq rbp,%r8 movq rip,%rdx movl error,%ecx pushq rip movq rbp,%rbp // Change frame pointer {$ifdef SYSTEMEXCEPTIONDEBUG} jmpl DebugHandleErrorAddrFrame {$else not SYSTEMEXCEPTIONDEBUG} jmpl HandleErrorAddrFrame {$endif SYSTEMEXCEPTIONDEBUG} end; end; function syswin64_x86_64_exception_handler(excep : PExceptionPointers) : Longint;public; var res: longint; err: byte; must_reset_fpu: boolean; begin res:=EXCEPTION_CONTINUE_SEARCH; {$ifdef SYSTEMEXCEPTIONDEBUG} if IsConsole then Writeln(stderr,'syswin64_x86_64_exception_handler called'); {$endif SYSTEMEXCEPTIONDEBUG} if excep^.ContextRecord^.SegSs=_SS then begin err := 0; must_reset_fpu := true; {$ifdef SYSTEMEXCEPTIONDEBUG} if IsConsole then Writeln(stderr,'Exception ', hexstr(excep^.ExceptionRecord^.ExceptionCode,8)); {$endif SYSTEMEXCEPTIONDEBUG} case cardinal(excep^.ExceptionRecord^.ExceptionCode) of STATUS_INTEGER_DIVIDE_BY_ZERO, STATUS_FLOAT_DIVIDE_BY_ZERO : err := 200; STATUS_ARRAY_BOUNDS_EXCEEDED : begin err := 201; must_reset_fpu := false; end; STATUS_STACK_OVERFLOW : begin err := 202; must_reset_fpu := false; end; STATUS_FLOAT_OVERFLOW : err := 205; STATUS_FLOAT_DENORMAL_OPERAND, STATUS_FLOAT_UNDERFLOW : err := 206; { excep^.ContextRecord^.FloatSave.StatusWord := excep^.ContextRecord^.FloatSave.StatusWord and $ffffff00;} STATUS_FLOAT_INEXACT_RESULT, STATUS_FLOAT_INVALID_OPERATION, STATUS_FLOAT_STACK_CHECK : err := 207; STATUS_INTEGER_OVERFLOW : begin err := 215; must_reset_fpu := false; end; STATUS_ILLEGAL_INSTRUCTION: err := 216; STATUS_ACCESS_VIOLATION: { Athlon prefetch bug? } if is_prefetch(pointer(excep^.ContextRecord^.rip)) then begin { if yes, then retry } excep^.ExceptionRecord^.ExceptionCode := 0; res:=EXCEPTION_CONTINUE_EXECUTION; end else err := 216; STATUS_CONTROL_C_EXIT: err := 217; STATUS_PRIVILEGED_INSTRUCTION: begin err := 218; must_reset_fpu := false; end; else begin if ((excep^.ExceptionRecord^.ExceptionCode and SEVERITY_ERROR) = SEVERITY_ERROR) then err := 217 else { pass through exceptions which aren't an error. The problem is that vectored handlers always are called before structured ones so we see also internal exceptions of libraries. I wonder if there is a better solution (FK) } res:=EXCEPTION_CONTINUE_SEARCH; end; end; if (err <> 0) and (exceptLevel < MaxExceptionLevel) then begin exceptRip[exceptLevel] := excep^.ContextRecord^.Rip; exceptError[exceptLevel] := err; resetFPU[exceptLevel] := must_reset_fpu; inc(exceptLevel); excep^.ContextRecord^.Rip := Int64(@JumpToHandleErrorFrame); excep^.ExceptionRecord^.ExceptionCode := 0; res := EXCEPTION_CONTINUE_EXECUTION; {$ifdef SYSTEMEXCEPTIONDEBUG} if IsConsole then begin writeln(stderr,'Exception Continue Exception set at ', hexstr(exceptRip[exceptLevel-1],16)); writeln(stderr,'Rip changed to ', hexstr(int64(@JumpToHandleErrorFrame),16), ' error=', err); end; {$endif SYSTEMEXCEPTIONDEBUG} end; end; syswin64_x86_64_exception_handler := res; end; procedure install_exception_handlers; begin AddVectoredExceptionHandler(1,@syswin64_x86_64_exception_handler); end; procedure remove_exception_handlers; begin end; procedure fpc_cpucodeinit; begin end; {**************************************************************************** OS dependend widestrings ****************************************************************************} const { MultiByteToWideChar } MB_PRECOMPOSED = 1; CP_ACP = 0; WC_NO_BEST_FIT_CHARS = $400; function MultiByteToWideChar(CodePage:UINT; dwFlags:DWORD; lpMultiByteStr:PChar; cchMultiByte:longint; lpWideCharStr:PWideChar;cchWideChar:longint):longint; stdcall; external 'kernel32' name 'MultiByteToWideChar'; function WideCharToMultiByte(CodePage:UINT; dwFlags:DWORD; lpWideCharStr:PWideChar; cchWideChar:longint; lpMultiByteStr:PChar;cchMultiByte:longint; lpDefaultChar:PChar; lpUsedDefaultChar:pointer):longint; stdcall; external 'kernel32' name 'WideCharToMultiByte'; function CharUpperBuff(lpsz:LPWSTR; cchLength:DWORD):DWORD; stdcall; external 'user32' name 'CharUpperBuffW'; function CharLowerBuff(lpsz:LPWSTR; cchLength:DWORD):DWORD; stdcall; external 'user32' name 'CharLowerBuffW'; procedure Win32Wide2AnsiMove(source:pwidechar;var dest:ansistring;len:SizeInt); var destlen: SizeInt; begin // retrieve length including trailing #0 // not anymore, because this must also be usable for single characters destlen:=WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, source, len, nil, 0, nil, nil); // this will null-terminate setlength(dest, destlen); WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, source, len, @dest[1], destlen, nil, nil); end; procedure Win32Ansi2WideMove(source:pchar;var dest:widestring;len:SizeInt); var destlen: SizeInt; begin // retrieve length including trailing #0 // not anymore, because this must also be usable for single characters destlen:=MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, source, len, nil, 0); // this will null-terminate setlength(dest, destlen); MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, source, len, @dest[1], destlen); end; function Win32WideUpper(const s : WideString) : WideString; begin result:=s; UniqueString(result); if length(result)>0 then CharUpperBuff(LPWSTR(result),length(result)); end; function Win32WideLower(const s : WideString) : WideString; begin result:=s; UniqueString(result); if length(result)>0 then CharLowerBuff(LPWSTR(result),length(result)); end; {******************************************************************************} { include code common with win64 } {$I syswin.inc} {******************************************************************************} function CheckInitialStkLen(stklen : SizeUInt) : SizeUInt;assembler; asm movq %gs:(8),%rax subq %gs:(16),%rax end; begin SysResetFPU; if not(IsLibrary) then SysInitFPU; { pass dummy value } StackLength := CheckInitialStkLen($1000000); StackBottom := StackTop - StackLength; { get some helpful informations } GetStartupInfo(@startupinfo); { some misc Win32 stuff } hprevinst:=0; if not IsLibrary then SysInstance:=getmodulehandle(nil); MainInstance:=SysInstance; cmdshow:=startupinfo.wshowwindow; { Setup heap } InitHeap; SysInitExceptions; { setup fastmove stuff } fpc_cpucodeinit; SysInitStdIO; { Arguments } setup_arguments; { Reset IO Error } InOutRes:=0; ProcessID := GetCurrentProcessID; { threading } InitSystemThreads; { Reset internal error variable } errno:=0; initvariantmanager; initwidestringmanager; {$ifndef VER2_2} initunicodestringmanager; {$endif VER2_2} InitWin32Widestrings; DispCallByIDProc:=@DoDispCallByIDError; end.