sl@0: /* sl@0: * tclWin32Dll.c -- sl@0: * sl@0: * This file contains the DLL entry point. sl@0: * sl@0: * Copyright (c) 1995-1996 Sun Microsystems, Inc. sl@0: * Copyright (c) 1998-2000 Scriptics Corporation. sl@0: * sl@0: * See the file "license.terms" for information on usage and redistribution sl@0: * of this file, and for a DISCLAIMER OF ALL WARRANTIES. sl@0: * sl@0: * RCS: @(#) $Id: tclWin32Dll.c,v 1.24.2.10 2006/10/17 04:36:45 dgp Exp $ sl@0: */ sl@0: sl@0: #include "tclWinInt.h" sl@0: sl@0: /* sl@0: * The following data structures are used when loading the thunking sl@0: * library for execing child processes under Win32s. sl@0: */ sl@0: sl@0: typedef DWORD (WINAPI UT32PROC)(LPVOID lpBuff, DWORD dwUserDefined, sl@0: LPVOID *lpTranslationList); sl@0: sl@0: typedef BOOL (WINAPI UTREGISTER)(HANDLE hModule, LPCSTR SixteenBitDLL, sl@0: LPCSTR InitName, LPCSTR ProcName, UT32PROC **ThirtyTwoBitThunk, sl@0: FARPROC UT32Callback, LPVOID Buff); sl@0: sl@0: typedef VOID (WINAPI UTUNREGISTER)(HANDLE hModule); sl@0: sl@0: /* sl@0: * The following variables keep track of information about this DLL sl@0: * on a per-instance basis. Each time this DLL is loaded, it gets its own sl@0: * new data segment with its own copy of all static and global information. sl@0: */ sl@0: sl@0: static HINSTANCE hInstance; /* HINSTANCE of this DLL. */ sl@0: static int platformId; /* Running under NT, or 95/98? */ sl@0: sl@0: #ifdef HAVE_NO_SEH sl@0: sl@0: /* sl@0: * Unlike Borland and Microsoft, we don't register exception handlers sl@0: * by pushing registration records onto the runtime stack. Instead, we sl@0: * register them by creating an EXCEPTION_REGISTRATION within the activation sl@0: * record. sl@0: */ sl@0: sl@0: typedef struct EXCEPTION_REGISTRATION { sl@0: struct EXCEPTION_REGISTRATION* link; sl@0: EXCEPTION_DISPOSITION (*handler)( struct _EXCEPTION_RECORD*, void*, sl@0: struct _CONTEXT*, void* ); sl@0: void* ebp; sl@0: void* esp; sl@0: int status; sl@0: } EXCEPTION_REGISTRATION; sl@0: sl@0: #endif sl@0: sl@0: /* sl@0: * VC++ 5.x has no 'cpuid' assembler instruction, so we sl@0: * must emulate it sl@0: */ sl@0: #if defined(_MSC_VER) && ( _MSC_VER <= 1100 ) sl@0: #define cpuid __asm __emit 0fh __asm __emit 0a2h sl@0: #endif sl@0: sl@0: /* sl@0: * The following function tables are used to dispatch to either the sl@0: * wide-character or multi-byte versions of the operating system calls, sl@0: * depending on whether the Unicode calls are available. sl@0: */ sl@0: sl@0: static TclWinProcs asciiProcs = { sl@0: 0, sl@0: sl@0: (BOOL (WINAPI *)(CONST TCHAR *, LPDCB)) BuildCommDCBA, sl@0: (TCHAR *(WINAPI *)(TCHAR *)) CharLowerA, sl@0: (BOOL (WINAPI *)(CONST TCHAR *, CONST TCHAR *, BOOL)) CopyFileA, sl@0: (BOOL (WINAPI *)(CONST TCHAR *, LPSECURITY_ATTRIBUTES)) CreateDirectoryA, sl@0: (HANDLE (WINAPI *)(CONST TCHAR *, DWORD, DWORD, SECURITY_ATTRIBUTES *, sl@0: DWORD, DWORD, HANDLE)) CreateFileA, sl@0: (BOOL (WINAPI *)(CONST TCHAR *, TCHAR *, LPSECURITY_ATTRIBUTES, sl@0: LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, CONST TCHAR *, sl@0: LPSTARTUPINFOA, LPPROCESS_INFORMATION)) CreateProcessA, sl@0: (BOOL (WINAPI *)(CONST TCHAR *)) DeleteFileA, sl@0: (HANDLE (WINAPI *)(CONST TCHAR *, WIN32_FIND_DATAT *)) FindFirstFileA, sl@0: (BOOL (WINAPI *)(HANDLE, WIN32_FIND_DATAT *)) FindNextFileA, sl@0: (BOOL (WINAPI *)(WCHAR *, LPDWORD)) GetComputerNameA, sl@0: (DWORD (WINAPI *)(DWORD, WCHAR *)) GetCurrentDirectoryA, sl@0: (DWORD (WINAPI *)(CONST TCHAR *)) GetFileAttributesA, sl@0: (DWORD (WINAPI *)(CONST TCHAR *, DWORD nBufferLength, WCHAR *, sl@0: TCHAR **)) GetFullPathNameA, sl@0: (DWORD (WINAPI *)(HMODULE, WCHAR *, int)) GetModuleFileNameA, sl@0: (DWORD (WINAPI *)(CONST TCHAR *, WCHAR *, DWORD)) GetShortPathNameA, sl@0: (UINT (WINAPI *)(CONST TCHAR *, CONST TCHAR *, UINT uUnique, sl@0: WCHAR *)) GetTempFileNameA, sl@0: (DWORD (WINAPI *)(DWORD, WCHAR *)) GetTempPathA, sl@0: (BOOL (WINAPI *)(CONST TCHAR *, WCHAR *, DWORD, LPDWORD, LPDWORD, LPDWORD, sl@0: WCHAR *, DWORD)) GetVolumeInformationA, sl@0: (HINSTANCE (WINAPI *)(CONST TCHAR *)) LoadLibraryA, sl@0: (TCHAR (WINAPI *)(WCHAR *, CONST TCHAR *)) lstrcpyA, sl@0: (BOOL (WINAPI *)(CONST TCHAR *, CONST TCHAR *)) MoveFileA, sl@0: (BOOL (WINAPI *)(CONST TCHAR *)) RemoveDirectoryA, sl@0: (DWORD (WINAPI *)(CONST TCHAR *, CONST TCHAR *, CONST TCHAR *, DWORD, sl@0: WCHAR *, TCHAR **)) SearchPathA, sl@0: (BOOL (WINAPI *)(CONST TCHAR *)) SetCurrentDirectoryA, sl@0: (BOOL (WINAPI *)(CONST TCHAR *, DWORD)) SetFileAttributesA, sl@0: /* sl@0: * The three NULL function pointers will only be set when sl@0: * Tcl_FindExecutable is called. If you don't ever call that sl@0: * function, the application will crash whenever WinTcl tries to call sl@0: * functions through these null pointers. That is not a bug in Tcl sl@0: * -- Tcl_FindExecutable is obligatory in recent Tcl releases. sl@0: */ sl@0: NULL, sl@0: NULL, sl@0: (int (__cdecl*)(CONST TCHAR *, struct _utimbuf *)) _utime, sl@0: NULL, sl@0: NULL, sl@0: /* getLongPathNameProc */ sl@0: NULL, sl@0: /* Security SDK - not available on 95,98,ME */ sl@0: NULL, NULL, NULL, NULL, NULL, NULL, sl@0: /* ReadConsole and WriteConsole */ sl@0: (BOOL (WINAPI *)(HANDLE, LPVOID, DWORD, LPDWORD, LPVOID)) ReadConsoleA, sl@0: (BOOL (WINAPI *)(HANDLE, const VOID*, DWORD, LPDWORD, LPVOID)) WriteConsoleA sl@0: }; sl@0: sl@0: static TclWinProcs unicodeProcs = { sl@0: 1, sl@0: sl@0: (BOOL (WINAPI *)(CONST TCHAR *, LPDCB)) BuildCommDCBW, sl@0: (TCHAR *(WINAPI *)(TCHAR *)) CharLowerW, sl@0: (BOOL (WINAPI *)(CONST TCHAR *, CONST TCHAR *, BOOL)) CopyFileW, sl@0: (BOOL (WINAPI *)(CONST TCHAR *, LPSECURITY_ATTRIBUTES)) CreateDirectoryW, sl@0: (HANDLE (WINAPI *)(CONST TCHAR *, DWORD, DWORD, SECURITY_ATTRIBUTES *, sl@0: DWORD, DWORD, HANDLE)) CreateFileW, sl@0: (BOOL (WINAPI *)(CONST TCHAR *, TCHAR *, LPSECURITY_ATTRIBUTES, sl@0: LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, CONST TCHAR *, sl@0: LPSTARTUPINFOA, LPPROCESS_INFORMATION)) CreateProcessW, sl@0: (BOOL (WINAPI *)(CONST TCHAR *)) DeleteFileW, sl@0: (HANDLE (WINAPI *)(CONST TCHAR *, WIN32_FIND_DATAT *)) FindFirstFileW, sl@0: (BOOL (WINAPI *)(HANDLE, WIN32_FIND_DATAT *)) FindNextFileW, sl@0: (BOOL (WINAPI *)(WCHAR *, LPDWORD)) GetComputerNameW, sl@0: (DWORD (WINAPI *)(DWORD, WCHAR *)) GetCurrentDirectoryW, sl@0: (DWORD (WINAPI *)(CONST TCHAR *)) GetFileAttributesW, sl@0: (DWORD (WINAPI *)(CONST TCHAR *, DWORD nBufferLength, WCHAR *, sl@0: TCHAR **)) GetFullPathNameW, sl@0: (DWORD (WINAPI *)(HMODULE, WCHAR *, int)) GetModuleFileNameW, sl@0: (DWORD (WINAPI *)(CONST TCHAR *, WCHAR *, DWORD)) GetShortPathNameW, sl@0: (UINT (WINAPI *)(CONST TCHAR *, CONST TCHAR *, UINT uUnique, sl@0: WCHAR *)) GetTempFileNameW, sl@0: (DWORD (WINAPI *)(DWORD, WCHAR *)) GetTempPathW, sl@0: (BOOL (WINAPI *)(CONST TCHAR *, WCHAR *, DWORD, LPDWORD, LPDWORD, LPDWORD, sl@0: WCHAR *, DWORD)) GetVolumeInformationW, sl@0: (HINSTANCE (WINAPI *)(CONST TCHAR *)) LoadLibraryW, sl@0: (TCHAR (WINAPI *)(WCHAR *, CONST TCHAR *)) lstrcpyW, sl@0: (BOOL (WINAPI *)(CONST TCHAR *, CONST TCHAR *)) MoveFileW, sl@0: (BOOL (WINAPI *)(CONST TCHAR *)) RemoveDirectoryW, sl@0: (DWORD (WINAPI *)(CONST TCHAR *, CONST TCHAR *, CONST TCHAR *, DWORD, sl@0: WCHAR *, TCHAR **)) SearchPathW, sl@0: (BOOL (WINAPI *)(CONST TCHAR *)) SetCurrentDirectoryW, sl@0: (BOOL (WINAPI *)(CONST TCHAR *, DWORD)) SetFileAttributesW, sl@0: /* sl@0: * The three NULL function pointers will only be set when sl@0: * Tcl_FindExecutable is called. If you don't ever call that sl@0: * function, the application will crash whenever WinTcl tries to call sl@0: * functions through these null pointers. That is not a bug in Tcl sl@0: * -- Tcl_FindExecutable is obligatory in recent Tcl releases. sl@0: */ sl@0: NULL, sl@0: NULL, sl@0: (int (__cdecl*)(CONST TCHAR *, struct _utimbuf *)) _wutime, sl@0: NULL, sl@0: NULL, sl@0: /* getLongPathNameProc */ sl@0: NULL, sl@0: /* Security SDK - will be filled in on NT,XP,2000,2003 */ sl@0: NULL, NULL, NULL, NULL, NULL, NULL, sl@0: /* ReadConsole and WriteConsole */ sl@0: (BOOL (WINAPI *)(HANDLE, LPVOID, DWORD, LPDWORD, LPVOID)) ReadConsoleW, sl@0: (BOOL (WINAPI *)(HANDLE, const VOID*, DWORD, LPDWORD, LPVOID)) WriteConsoleW sl@0: }; sl@0: sl@0: TclWinProcs *tclWinProcs; sl@0: static Tcl_Encoding tclWinTCharEncoding; sl@0: sl@0: sl@0: #ifdef HAVE_NO_SEH sl@0: sl@0: /* Need to add noinline flag to DllMain declaration so that gcc -O3 sl@0: * does not inline asm code into DllEntryPoint and cause a sl@0: * compile time error because of redefined local labels. sl@0: */ sl@0: sl@0: BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, sl@0: LPVOID reserved) sl@0: __attribute__ ((noinline)); sl@0: sl@0: #else sl@0: sl@0: /* sl@0: * The following declaration is for the VC++ DLL entry point. sl@0: */ sl@0: sl@0: BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, sl@0: LPVOID reserved); sl@0: #endif /* HAVE_NO_SEH */ sl@0: sl@0: sl@0: /* sl@0: * The following structure and linked list is to allow us to map between sl@0: * volume mount points and drive letters on the fly (no Win API exists sl@0: * for this). sl@0: */ sl@0: typedef struct MountPointMap { sl@0: CONST WCHAR* volumeName; /* Native wide string volume name */ sl@0: char driveLetter; /* Drive letter corresponding to sl@0: * the volume name. */ sl@0: struct MountPointMap* nextPtr; /* Pointer to next structure in list, sl@0: * or NULL */ sl@0: } MountPointMap; sl@0: sl@0: /* sl@0: * This is the head of the linked list, which is protected by the sl@0: * mutex which follows, for thread-enabled builds. sl@0: */ sl@0: MountPointMap *driveLetterLookup = NULL; sl@0: TCL_DECLARE_MUTEX(mountPointMap) sl@0: sl@0: /* We will need this below */ sl@0: extern Tcl_FSDupInternalRepProc TclNativeDupInternalRep; sl@0: sl@0: #ifdef __WIN32__ sl@0: #ifndef STATIC_BUILD sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * DllEntryPoint -- sl@0: * sl@0: * This wrapper function is used by Borland to invoke the sl@0: * initialization code for Tcl. It simply calls the DllMain sl@0: * routine. sl@0: * sl@0: * Results: sl@0: * See DllMain. sl@0: * sl@0: * Side effects: sl@0: * See DllMain. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: BOOL APIENTRY sl@0: DllEntryPoint(hInst, reason, reserved) sl@0: HINSTANCE hInst; /* Library instance handle. */ sl@0: DWORD reason; /* Reason this function is being called. */ sl@0: LPVOID reserved; /* Not used. */ sl@0: { sl@0: return DllMain(hInst, reason, reserved); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * DllMain -- sl@0: * sl@0: * This routine is called by the VC++ C run time library init sl@0: * code, or the DllEntryPoint routine. It is responsible for sl@0: * initializing various dynamically loaded libraries. sl@0: * sl@0: * Results: sl@0: * TRUE on sucess, FALSE on failure. sl@0: * sl@0: * Side effects: sl@0: * Establishes 32-to-16 bit thunk and initializes sockets library. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: BOOL APIENTRY sl@0: DllMain(hInst, reason, reserved) sl@0: HINSTANCE hInst; /* Library instance handle. */ sl@0: DWORD reason; /* Reason this function is being called. */ sl@0: LPVOID reserved; /* Not used. */ sl@0: { sl@0: #ifdef HAVE_NO_SEH sl@0: EXCEPTION_REGISTRATION registration; sl@0: #endif sl@0: sl@0: switch (reason) { sl@0: case DLL_PROCESS_ATTACH: sl@0: DisableThreadLibraryCalls(hInst); sl@0: TclWinInit(hInst); sl@0: return TRUE; sl@0: sl@0: case DLL_PROCESS_DETACH: sl@0: /* sl@0: * Protect the call to Tcl_Finalize. The OS could be unloading sl@0: * us from an exception handler and the state of the stack might sl@0: * be unstable. sl@0: */ sl@0: #ifdef HAVE_NO_SEH sl@0: __asm__ __volatile__ ( sl@0: sl@0: /* sl@0: * Construct an EXCEPTION_REGISTRATION to protect the sl@0: * call to Tcl_Finalize sl@0: */ sl@0: "leal %[registration], %%edx" "\n\t" sl@0: "movl %%fs:0, %%eax" "\n\t" sl@0: "movl %%eax, 0x0(%%edx)" "\n\t" /* link */ sl@0: "leal 1f, %%eax" "\n\t" sl@0: "movl %%eax, 0x4(%%edx)" "\n\t" /* handler */ sl@0: "movl %%ebp, 0x8(%%edx)" "\n\t" /* ebp */ sl@0: "movl %%esp, 0xc(%%edx)" "\n\t" /* esp */ sl@0: "movl %[error], 0x10(%%edx)" "\n\t" /* status */ sl@0: sl@0: /* sl@0: * Link the EXCEPTION_REGISTRATION on the chain sl@0: */ sl@0: "movl %%edx, %%fs:0" "\n\t" sl@0: sl@0: /* sl@0: * Call Tcl_Finalize sl@0: */ sl@0: "call _Tcl_Finalize" "\n\t" sl@0: sl@0: /* sl@0: * Come here on a normal exit. Recover the EXCEPTION_REGISTRATION sl@0: * and store a TCL_OK status sl@0: */ sl@0: sl@0: "movl %%fs:0, %%edx" "\n\t" sl@0: "movl %[ok], %%eax" "\n\t" sl@0: "movl %%eax, 0x10(%%edx)" "\n\t" sl@0: "jmp 2f" "\n" sl@0: sl@0: /* sl@0: * Come here on an exception. Get the EXCEPTION_REGISTRATION sl@0: * that we previously put on the chain. sl@0: */ sl@0: sl@0: "1:" "\t" sl@0: "movl %%fs:0, %%edx" "\n\t" sl@0: "movl 0x8(%%edx), %%edx" "\n" sl@0: sl@0: sl@0: /* sl@0: * Come here however we exited. Restore context from the sl@0: * EXCEPTION_REGISTRATION in case the stack is unbalanced. sl@0: */ sl@0: sl@0: "2:" "\t" sl@0: "movl 0xc(%%edx), %%esp" "\n\t" sl@0: "movl 0x8(%%edx), %%ebp" "\n\t" sl@0: "movl 0x0(%%edx), %%eax" "\n\t" sl@0: "movl %%eax, %%fs:0" "\n\t" sl@0: sl@0: : sl@0: /* No outputs */ sl@0: : sl@0: [registration] "m" (registration), sl@0: [ok] "i" (TCL_OK), sl@0: [error] "i" (TCL_ERROR) sl@0: : sl@0: "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory" sl@0: ); sl@0: sl@0: #else /* HAVE_NO_SEH */ sl@0: __try { sl@0: Tcl_Finalize(); sl@0: } __except (EXCEPTION_EXECUTE_HANDLER) { sl@0: /* empty handler body. */ sl@0: } sl@0: #endif sl@0: sl@0: break; sl@0: } sl@0: sl@0: return TRUE; sl@0: } sl@0: sl@0: #endif /* !STATIC_BUILD */ sl@0: #endif /* __WIN32__ */ sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclWinGetTclInstance -- sl@0: * sl@0: * Retrieves the global library instance handle. sl@0: * sl@0: * Results: sl@0: * Returns the global library instance handle. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: HINSTANCE sl@0: TclWinGetTclInstance() sl@0: { sl@0: return hInstance; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclWinInit -- sl@0: * sl@0: * This function initializes the internal state of the tcl library. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Initializes the tclPlatformId variable. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclWinInit(hInst) sl@0: HINSTANCE hInst; /* Library instance handle. */ sl@0: { sl@0: OSVERSIONINFO os; sl@0: sl@0: hInstance = hInst; sl@0: os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); sl@0: GetVersionEx(&os); sl@0: platformId = os.dwPlatformId; sl@0: sl@0: /* sl@0: * We no longer support Win32s, so just in case someone manages to sl@0: * get a runtime there, make sure they know that. sl@0: */ sl@0: sl@0: if (platformId == VER_PLATFORM_WIN32s) { sl@0: panic("Win32s is not a supported platform"); sl@0: } sl@0: sl@0: tclWinProcs = &asciiProcs; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclWinGetPlatformId -- sl@0: * sl@0: * Determines whether running under NT, 95, or Win32s, to allow sl@0: * runtime conditional code. sl@0: * sl@0: * Results: sl@0: * The return value is one of: sl@0: * VER_PLATFORM_WIN32s Win32s on Windows 3.1. (not supported) sl@0: * VER_PLATFORM_WIN32_WINDOWS Win32 on Windows 95. sl@0: * VER_PLATFORM_WIN32_NT Win32 on Windows NT sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: TclWinGetPlatformId() sl@0: { sl@0: return platformId; sl@0: } sl@0: sl@0: /* sl@0: *------------------------------------------------------------------------- sl@0: * sl@0: * TclWinNoBackslash -- sl@0: * sl@0: * We're always iterating through a string in Windows, changing the sl@0: * backslashes to slashes for use in Tcl. sl@0: * sl@0: * Results: sl@0: * All backslashes in given string are changed to slashes. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *------------------------------------------------------------------------- sl@0: */ sl@0: sl@0: char * sl@0: TclWinNoBackslash( sl@0: char *path) /* String to change. */ sl@0: { sl@0: char *p; sl@0: sl@0: for (p = path; *p != '\0'; p++) { sl@0: if (*p == '\\') { sl@0: *p = '/'; sl@0: } sl@0: } sl@0: return path; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpCheckStackSpace -- sl@0: * sl@0: * Detect if we are about to blow the stack. Called before an sl@0: * evaluation can happen when nesting depth is checked. sl@0: * sl@0: * Results: sl@0: * 1 if there is enough stack space to continue; 0 if not. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: TclpCheckStackSpace() sl@0: { sl@0: sl@0: #ifdef HAVE_NO_SEH sl@0: EXCEPTION_REGISTRATION registration; sl@0: #endif sl@0: int retval = 0; sl@0: sl@0: /* sl@0: * We can recurse only if there is at least TCL_WIN_STACK_THRESHOLD sl@0: * bytes of stack space left. alloca() is cheap on windows; basically sl@0: * it just subtracts from the stack pointer causing the OS to throw an sl@0: * exception if the stack pointer is set below the bottom of the stack. sl@0: */ sl@0: sl@0: #ifdef HAVE_NO_SEH sl@0: __asm__ __volatile__ ( sl@0: sl@0: /* sl@0: * Construct an EXCEPTION_REGISTRATION to protect the sl@0: * call to __alloca sl@0: */ sl@0: "leal %[registration], %%edx" "\n\t" sl@0: "movl %%fs:0, %%eax" "\n\t" sl@0: "movl %%eax, 0x0(%%edx)" "\n\t" /* link */ sl@0: "leal 1f, %%eax" "\n\t" sl@0: "movl %%eax, 0x4(%%edx)" "\n\t" /* handler */ sl@0: "movl %%ebp, 0x8(%%edx)" "\n\t" /* ebp */ sl@0: "movl %%esp, 0xc(%%edx)" "\n\t" /* esp */ sl@0: "movl %[error], 0x10(%%edx)" "\n\t" /* status */ sl@0: sl@0: /* sl@0: * Link the EXCEPTION_REGISTRATION on the chain sl@0: */ sl@0: "movl %%edx, %%fs:0" "\n\t" sl@0: sl@0: /* sl@0: * Attempt a call to __alloca, to determine whether there's sl@0: * sufficient memory to be had. sl@0: */ sl@0: sl@0: "movl %[size], %%eax" "\n\t" sl@0: "pushl %%eax" "\n\t" sl@0: "call __alloca" "\n\t" sl@0: sl@0: /* sl@0: * Come here on a normal exit. Recover the EXCEPTION_REGISTRATION sl@0: * and store a TCL_OK status sl@0: */ sl@0: "movl %%fs:0, %%edx" "\n\t" sl@0: "movl %[ok], %%eax" "\n\t" sl@0: "movl %%eax, 0x10(%%edx)" "\n\t" sl@0: "jmp 2f" "\n" sl@0: sl@0: /* sl@0: * Come here on an exception. Get the EXCEPTION_REGISTRATION sl@0: * that we previously put on the chain. sl@0: */ sl@0: "1:" "\t" sl@0: "movl %%fs:0, %%edx" "\n\t" sl@0: "movl 0x8(%%edx), %%edx" "\n\t" sl@0: sl@0: /* sl@0: * Come here however we exited. Restore context from the sl@0: * EXCEPTION_REGISTRATION in case the stack is unbalanced. sl@0: */ sl@0: sl@0: "2:" "\t" sl@0: "movl 0xc(%%edx), %%esp" "\n\t" sl@0: "movl 0x8(%%edx), %%ebp" "\n\t" sl@0: "movl 0x0(%%edx), %%eax" "\n\t" sl@0: "movl %%eax, %%fs:0" "\n\t" sl@0: sl@0: : sl@0: /* No outputs */ sl@0: : sl@0: [registration] "m" (registration), sl@0: [ok] "i" (TCL_OK), sl@0: [error] "i" (TCL_ERROR), sl@0: [size] "i" (TCL_WIN_STACK_THRESHOLD) sl@0: : sl@0: "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory" sl@0: ); sl@0: retval = (registration.status == TCL_OK); sl@0: sl@0: #else /* !HAVE_NO_SEH */ sl@0: __try { sl@0: #ifdef HAVE_ALLOCA_GCC_INLINE sl@0: __asm__ __volatile__ ( sl@0: "movl %0, %%eax" "\n\t" sl@0: "call __alloca" "\n\t" sl@0: : sl@0: : "i"(TCL_WIN_STACK_THRESHOLD) sl@0: : "%eax"); sl@0: #else sl@0: alloca(TCL_WIN_STACK_THRESHOLD); sl@0: #endif /* HAVE_ALLOCA_GCC_INLINE */ sl@0: retval = 1; sl@0: } __except (EXCEPTION_EXECUTE_HANDLER) {} sl@0: #endif /* HAVE_NO_SEH */ sl@0: sl@0: return retval; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclWinGetPlatform -- sl@0: * sl@0: * This is a kludge that allows the test library to get access sl@0: * the internal tclPlatform variable. sl@0: * sl@0: * Results: sl@0: * Returns a pointer to the tclPlatform variable. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: TclPlatformType * sl@0: TclWinGetPlatform() sl@0: { sl@0: return &tclPlatform; sl@0: } sl@0: sl@0: /* sl@0: *--------------------------------------------------------------------------- sl@0: * sl@0: * TclWinSetInterfaces -- sl@0: * sl@0: * A helper proc that allows the test library to change the sl@0: * tclWinProcs structure to dispatch to either the wide-character sl@0: * or multi-byte versions of the operating system calls, depending sl@0: * on whether Unicode is the system encoding. sl@0: * sl@0: * As well as this, we can also try to load in some additional sl@0: * procs which may/may not be present depending on the current sl@0: * Windows version (e.g. Win95 will not have the procs below). sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *--------------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclWinSetInterfaces( sl@0: int wide) /* Non-zero to use wide interfaces, 0 sl@0: * otherwise. */ sl@0: { sl@0: Tcl_FreeEncoding(tclWinTCharEncoding); sl@0: sl@0: if (wide) { sl@0: tclWinProcs = &unicodeProcs; sl@0: tclWinTCharEncoding = Tcl_GetEncoding(NULL, "unicode"); sl@0: if (tclWinProcs->getFileAttributesExProc == NULL) { sl@0: HINSTANCE hInstance = LoadLibraryA("kernel32"); sl@0: if (hInstance != NULL) { sl@0: tclWinProcs->getFileAttributesExProc = sl@0: (BOOL (WINAPI *)(CONST TCHAR *, GET_FILEEX_INFO_LEVELS, sl@0: LPVOID)) GetProcAddress(hInstance, "GetFileAttributesExW"); sl@0: tclWinProcs->createHardLinkProc = sl@0: (BOOL (WINAPI *)(CONST TCHAR *, CONST TCHAR*, sl@0: LPSECURITY_ATTRIBUTES)) GetProcAddress(hInstance, sl@0: "CreateHardLinkW"); sl@0: tclWinProcs->findFirstFileExProc = sl@0: (HANDLE (WINAPI *)(CONST TCHAR*, UINT, sl@0: LPVOID, UINT, LPVOID, DWORD)) GetProcAddress(hInstance, sl@0: "FindFirstFileExW"); sl@0: tclWinProcs->getVolumeNameForVMPProc = sl@0: (BOOL (WINAPI *)(CONST TCHAR*, TCHAR*, sl@0: DWORD)) GetProcAddress(hInstance, sl@0: "GetVolumeNameForVolumeMountPointW"); sl@0: FreeLibrary(hInstance); sl@0: } sl@0: hInstance = LoadLibraryA("advapi32"); sl@0: if (hInstance != NULL) { sl@0: tclWinProcs->getFileSecurityProc = (BOOL (WINAPI *)( sl@0: LPCTSTR lpFileName, sl@0: SECURITY_INFORMATION RequestedInformation, sl@0: PSECURITY_DESCRIPTOR pSecurityDescriptor, sl@0: DWORD nLength, LPDWORD lpnLengthNeeded)) sl@0: GetProcAddress(hInstance, "GetFileSecurityW"); sl@0: tclWinProcs->impersonateSelfProc = (BOOL (WINAPI *) ( sl@0: SECURITY_IMPERSONATION_LEVEL ImpersonationLevel)) sl@0: GetProcAddress(hInstance, "ImpersonateSelf"); sl@0: tclWinProcs->openThreadTokenProc = (BOOL (WINAPI *) ( sl@0: HANDLE ThreadHandle, DWORD DesiredAccess, sl@0: BOOL OpenAsSelf, PHANDLE TokenHandle)) sl@0: GetProcAddress(hInstance, "OpenThreadToken"); sl@0: tclWinProcs->revertToSelfProc = (BOOL (WINAPI *) (void)) sl@0: GetProcAddress(hInstance, "RevertToSelf"); sl@0: tclWinProcs->mapGenericMaskProc = (VOID (WINAPI *) ( sl@0: PDWORD AccessMask, PGENERIC_MAPPING GenericMapping)) sl@0: GetProcAddress(hInstance, "MapGenericMask"); sl@0: tclWinProcs->accessCheckProc = (BOOL (WINAPI *)( sl@0: PSECURITY_DESCRIPTOR pSecurityDescriptor, sl@0: HANDLE ClientToken, DWORD DesiredAccess, sl@0: PGENERIC_MAPPING GenericMapping, sl@0: PPRIVILEGE_SET PrivilegeSet, sl@0: LPDWORD PrivilegeSetLength, LPDWORD GrantedAccess, sl@0: LPBOOL AccessStatus)) GetProcAddress(hInstance, sl@0: "AccessCheck"); sl@0: FreeLibrary(hInstance); sl@0: } sl@0: } sl@0: } else { sl@0: tclWinProcs = &asciiProcs; sl@0: tclWinTCharEncoding = NULL; sl@0: if (tclWinProcs->getFileAttributesExProc == NULL) { sl@0: HINSTANCE hInstance = LoadLibraryA("kernel32"); sl@0: if (hInstance != NULL) { sl@0: tclWinProcs->getFileAttributesExProc = sl@0: (BOOL (WINAPI *)(CONST TCHAR *, GET_FILEEX_INFO_LEVELS, sl@0: LPVOID)) GetProcAddress(hInstance, "GetFileAttributesExA"); sl@0: tclWinProcs->createHardLinkProc = sl@0: (BOOL (WINAPI *)(CONST TCHAR *, CONST TCHAR*, sl@0: LPSECURITY_ATTRIBUTES)) GetProcAddress(hInstance, sl@0: "CreateHardLinkA"); sl@0: tclWinProcs->findFirstFileExProc = sl@0: (HANDLE (WINAPI *)(CONST TCHAR*, UINT, sl@0: LPVOID, UINT, LPVOID, DWORD)) GetProcAddress(hInstance, sl@0: "FindFirstFileExA"); sl@0: tclWinProcs->getLongPathNameProc = NULL; sl@0: tclWinProcs->getVolumeNameForVMPProc = sl@0: (BOOL (WINAPI *)(CONST TCHAR*, TCHAR*, sl@0: DWORD)) GetProcAddress(hInstance, sl@0: "GetVolumeNameForVolumeMountPointA"); sl@0: FreeLibrary(hInstance); sl@0: } sl@0: } sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *--------------------------------------------------------------------------- sl@0: * sl@0: * TclWinResetInterfaceEncodings -- sl@0: * sl@0: * Called during finalization to free up any encodings we use. sl@0: * The tclWinProcs-> look up table is still ok to use after sl@0: * this call, provided no encoding conversion is required. sl@0: * sl@0: * We also clean up any memory allocated in our mount point sl@0: * map which is used to follow certain kinds of symlinks. sl@0: * That code should never be used once encodings are taken sl@0: * down. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *--------------------------------------------------------------------------- sl@0: */ sl@0: void sl@0: TclWinResetInterfaceEncodings() sl@0: { sl@0: MountPointMap *dlIter, *dlIter2; sl@0: if (tclWinTCharEncoding != NULL) { sl@0: Tcl_FreeEncoding(tclWinTCharEncoding); sl@0: tclWinTCharEncoding = NULL; sl@0: } sl@0: /* Clean up the mount point map */ sl@0: Tcl_MutexLock(&mountPointMap); sl@0: dlIter = driveLetterLookup; sl@0: while (dlIter != NULL) { sl@0: dlIter2 = dlIter->nextPtr; sl@0: ckfree((char*)dlIter->volumeName); sl@0: ckfree((char*)dlIter); sl@0: dlIter = dlIter2; sl@0: } sl@0: Tcl_MutexUnlock(&mountPointMap); sl@0: } sl@0: sl@0: /* sl@0: *--------------------------------------------------------------------------- sl@0: * sl@0: * TclWinResetInterfaces -- sl@0: * sl@0: * Called during finalization to reset us to a safe state for reuse. sl@0: * After this call, it is best not to use the tclWinProcs-> look sl@0: * up table since it is likely to be different to what is expected. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *--------------------------------------------------------------------------- sl@0: */ sl@0: void sl@0: TclWinResetInterfaces() sl@0: { sl@0: tclWinProcs = &asciiProcs; sl@0: } sl@0: sl@0: /* sl@0: *-------------------------------------------------------------------- sl@0: * sl@0: * TclWinDriveLetterForVolMountPoint sl@0: * sl@0: * Unfortunately, Windows provides no easy way at all to get hold sl@0: * of the drive letter for a volume mount point, but we need that sl@0: * information to understand paths correctly. So, we have to sl@0: * build an associated array to find these correctly, and allow sl@0: * quick and easy lookup from volume mount points to drive letters. sl@0: * sl@0: * We assume here that we are running on a system for which the wide sl@0: * character interfaces are used, which is valid for Win 2000 and WinXP sl@0: * which are the only systems on which this function will ever be called. sl@0: * sl@0: * Result: the drive letter, or -1 if no drive letter corresponds to sl@0: * the given mount point. sl@0: * sl@0: *-------------------------------------------------------------------- sl@0: */ sl@0: char sl@0: TclWinDriveLetterForVolMountPoint(CONST WCHAR *mountPoint) sl@0: { sl@0: MountPointMap *dlIter, *dlPtr2; sl@0: WCHAR Target[55]; /* Target of mount at mount point */ sl@0: WCHAR drive[4] = { L'A', L':', L'\\', L'\0' }; sl@0: sl@0: /* sl@0: * Detect the volume mounted there. Unfortunately, there is no sl@0: * simple way to map a unique volume name to a DOS drive letter. sl@0: * So, we have to build an associative array. sl@0: */ sl@0: sl@0: Tcl_MutexLock(&mountPointMap); sl@0: dlIter = driveLetterLookup; sl@0: while (dlIter != NULL) { sl@0: if (wcscmp(dlIter->volumeName, mountPoint) == 0) { sl@0: /* sl@0: * We need to check whether this information is sl@0: * still valid, since either the user or various sl@0: * programs could have adjusted the mount points on sl@0: * the fly. sl@0: */ sl@0: drive[0] = L'A' + (dlIter->driveLetter - 'A'); sl@0: /* Try to read the volume mount point and see where it points */ sl@0: if ((*tclWinProcs->getVolumeNameForVMPProc)((TCHAR*)drive, sl@0: (TCHAR*)Target, 55) != 0) { sl@0: if (wcscmp((WCHAR*)dlIter->volumeName, Target) == 0) { sl@0: /* Nothing has changed */ sl@0: Tcl_MutexUnlock(&mountPointMap); sl@0: return dlIter->driveLetter; sl@0: } sl@0: } sl@0: /* sl@0: * If we reach here, unfortunately, this mount point is sl@0: * no longer valid at all sl@0: */ sl@0: if (driveLetterLookup == dlIter) { sl@0: dlPtr2 = dlIter; sl@0: driveLetterLookup = dlIter->nextPtr; sl@0: } else { sl@0: for (dlPtr2 = driveLetterLookup; sl@0: dlPtr2 != NULL; dlPtr2 = dlPtr2->nextPtr) { sl@0: if (dlPtr2->nextPtr == dlIter) { sl@0: dlPtr2->nextPtr = dlIter->nextPtr; sl@0: dlPtr2 = dlIter; sl@0: break; sl@0: } sl@0: } sl@0: } sl@0: /* Now dlPtr2 points to the structure to free */ sl@0: ckfree((char*)dlPtr2->volumeName); sl@0: ckfree((char*)dlPtr2); sl@0: /* sl@0: * Restart the loop --- we could try to be clever sl@0: * and continue half way through, but the logic is a sl@0: * bit messy, so it's cleanest just to restart sl@0: */ sl@0: dlIter = driveLetterLookup; sl@0: continue; sl@0: } sl@0: dlIter = dlIter->nextPtr; sl@0: } sl@0: sl@0: /* We couldn't find it, so we must iterate over the letters */ sl@0: sl@0: for (drive[0] = L'A'; drive[0] <= L'Z'; drive[0]++) { sl@0: /* Try to read the volume mount point and see where it points */ sl@0: if ((*tclWinProcs->getVolumeNameForVMPProc)((TCHAR*)drive, sl@0: (TCHAR*)Target, 55) != 0) { sl@0: int alreadyStored = 0; sl@0: for (dlIter = driveLetterLookup; dlIter != NULL; sl@0: dlIter = dlIter->nextPtr) { sl@0: if (wcscmp((WCHAR*)dlIter->volumeName, Target) == 0) { sl@0: alreadyStored = 1; sl@0: break; sl@0: } sl@0: } sl@0: if (!alreadyStored) { sl@0: dlPtr2 = (MountPointMap*) ckalloc(sizeof(MountPointMap)); sl@0: dlPtr2->volumeName = TclNativeDupInternalRep(Target); sl@0: dlPtr2->driveLetter = 'A' + (drive[0] - L'A'); sl@0: dlPtr2->nextPtr = driveLetterLookup; sl@0: driveLetterLookup = dlPtr2; sl@0: } sl@0: } sl@0: } sl@0: /* Try again */ sl@0: for (dlIter = driveLetterLookup; dlIter != NULL; sl@0: dlIter = dlIter->nextPtr) { sl@0: if (wcscmp(dlIter->volumeName, mountPoint) == 0) { sl@0: Tcl_MutexUnlock(&mountPointMap); sl@0: return dlIter->driveLetter; sl@0: } sl@0: } sl@0: /* sl@0: * The volume doesn't appear to correspond to a drive letter -- we sl@0: * remember that fact and store '-1' so we don't have to look it sl@0: * up each time. sl@0: */ sl@0: dlPtr2 = (MountPointMap*) ckalloc(sizeof(MountPointMap)); sl@0: dlPtr2->volumeName = TclNativeDupInternalRep((ClientData)mountPoint); sl@0: dlPtr2->driveLetter = -1; sl@0: dlPtr2->nextPtr = driveLetterLookup; sl@0: driveLetterLookup = dlPtr2; sl@0: Tcl_MutexUnlock(&mountPointMap); sl@0: return -1; sl@0: } sl@0: sl@0: /* sl@0: *--------------------------------------------------------------------------- sl@0: * sl@0: * Tcl_WinUtfToTChar, Tcl_WinTCharToUtf -- sl@0: * sl@0: * Convert between UTF-8 and Unicode when running Windows NT or sl@0: * the current ANSI code page when running Windows 95. sl@0: * sl@0: * On Mac, Unix, and Windows 95, all strings exchanged between Tcl sl@0: * and the OS are "char" oriented. We need only one Tcl_Encoding to sl@0: * convert between UTF-8 and the system's native encoding. We use sl@0: * NULL to represent that encoding. sl@0: * sl@0: * On NT, some strings exchanged between Tcl and the OS are "char" sl@0: * oriented, while others are in Unicode. We need two Tcl_Encoding sl@0: * APIs depending on whether we are targeting a "char" or Unicode sl@0: * interface. sl@0: * sl@0: * Calling Tcl_UtfToExternal() or Tcl_ExternalToUtf() with an sl@0: * encoding of NULL should always used to convert between UTF-8 sl@0: * and the system's "char" oriented encoding. The following two sl@0: * functions are used in Windows-specific code to convert between sl@0: * UTF-8 and Unicode strings (NT) or "char" strings(95). This saves sl@0: * you the trouble of writing the following type of fragment over and sl@0: * over: sl@0: * sl@0: * if (running NT) { sl@0: * encoding <- Tcl_GetEncoding("unicode"); sl@0: * nativeBuffer <- UtfToExternal(encoding, utfBuffer); sl@0: * Tcl_FreeEncoding(encoding); sl@0: * } else { sl@0: * nativeBuffer <- UtfToExternal(NULL, utfBuffer); sl@0: * } sl@0: * sl@0: * By convention, in Windows a TCHAR is a character in the ANSI code sl@0: * page on Windows 95, a Unicode character on Windows NT. If you sl@0: * plan on targeting a Unicode interfaces when running on NT and a sl@0: * "char" oriented interface while running on 95, these functions sl@0: * should be used. If you plan on targetting the same "char" sl@0: * oriented function on both 95 and NT, use Tcl_UtfToExternal() sl@0: * with an encoding of NULL. sl@0: * sl@0: * Results: sl@0: * The result is a pointer to the string in the desired target sl@0: * encoding. Storage for the result string is allocated in sl@0: * dsPtr; the caller must call Tcl_DStringFree() when the result sl@0: * is no longer needed. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *--------------------------------------------------------------------------- sl@0: */ sl@0: sl@0: TCHAR * sl@0: Tcl_WinUtfToTChar(string, len, dsPtr) sl@0: CONST char *string; /* Source string in UTF-8. */ sl@0: int len; /* Source string length in bytes, or < 0 for sl@0: * strlen(). */ sl@0: Tcl_DString *dsPtr; /* Uninitialized or free DString in which sl@0: * the converted string is stored. */ sl@0: { sl@0: return (TCHAR *) Tcl_UtfToExternalDString(tclWinTCharEncoding, sl@0: string, len, dsPtr); sl@0: } sl@0: sl@0: char * sl@0: Tcl_WinTCharToUtf(string, len, dsPtr) sl@0: CONST TCHAR *string; /* Source string in Unicode when running sl@0: * NT, ANSI when running 95. */ sl@0: int len; /* Source string length in bytes, or < 0 for sl@0: * platform-specific string length. */ sl@0: Tcl_DString *dsPtr; /* Uninitialized or free DString in which sl@0: * the converted string is stored. */ sl@0: { sl@0: return Tcl_ExternalToUtfDString(tclWinTCharEncoding, sl@0: (CONST char *) string, len, dsPtr); sl@0: } sl@0: sl@0: /* sl@0: *------------------------------------------------------------------------ sl@0: * sl@0: * TclWinCPUID -- sl@0: * sl@0: * Get CPU ID information on an Intel box under Windows sl@0: * sl@0: * Results: sl@0: * Returns TCL_OK if successful, TCL_ERROR if CPUID is not sl@0: * supported or fails. sl@0: * sl@0: * Side effects: sl@0: * If successful, stores EAX, EBX, ECX and EDX registers after sl@0: * the CPUID instruction in the four integers designated by 'regsPtr' sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: TclWinCPUID( unsigned int index, /* Which CPUID value to retrieve */ sl@0: unsigned int * regsPtr ) /* Registers after the CPUID */ sl@0: { sl@0: sl@0: #ifdef HAVE_NO_SEH sl@0: EXCEPTION_REGISTRATION registration; sl@0: #endif sl@0: int status = TCL_ERROR; sl@0: sl@0: #if defined(__GNUC__) && !defined(_WIN64) sl@0: sl@0: /* sl@0: * Execute the CPUID instruction with the given index, and sl@0: * store results off 'regPtr'. sl@0: */ sl@0: sl@0: __asm__ __volatile__ ( sl@0: sl@0: /* sl@0: * Construct an EXCEPTION_REGISTRATION to protect the sl@0: * CPUID instruction (early 486's don't have CPUID) sl@0: */ sl@0: "leal %[registration], %%edx" "\n\t" sl@0: "movl %%fs:0, %%eax" "\n\t" sl@0: "movl %%eax, 0x0(%%edx)" "\n\t" /* link */ sl@0: "leal 1f, %%eax" "\n\t" sl@0: "movl %%eax, 0x4(%%edx)" "\n\t" /* handler */ sl@0: "movl %%ebp, 0x8(%%edx)" "\n\t" /* ebp */ sl@0: "movl %%esp, 0xc(%%edx)" "\n\t" /* esp */ sl@0: "movl %[error], 0x10(%%edx)" "\n\t" /* status */ sl@0: sl@0: /* sl@0: * Link the EXCEPTION_REGISTRATION on the chain sl@0: */ sl@0: "movl %%edx, %%fs:0" "\n\t" sl@0: sl@0: /* sl@0: * Do the CPUID instruction, and save the results in sl@0: * the 'regsPtr' area sl@0: */ sl@0: sl@0: "movl %[rptr], %%edi" "\n\t" sl@0: "movl %[index], %%eax" "\n\t" sl@0: "cpuid" "\n\t" sl@0: "movl %%eax, 0x0(%%edi)" "\n\t" sl@0: "movl %%ebx, 0x4(%%edi)" "\n\t" sl@0: "movl %%ecx, 0x8(%%edi)" "\n\t" sl@0: "movl %%edx, 0xc(%%edi)" "\n\t" sl@0: sl@0: /* sl@0: * Come here on a normal exit. Recover the EXCEPTION_REGISTRATION sl@0: * and store a TCL_OK status sl@0: */ sl@0: "movl %%fs:0, %%edx" "\n\t" sl@0: "movl %[ok], %%eax" "\n\t" sl@0: "movl %%eax, 0x10(%%edx)" "\n\t" sl@0: "jmp 2f" "\n" sl@0: sl@0: /* sl@0: * Come here on an exception. Get the EXCEPTION_REGISTRATION sl@0: * that we previously put on the chain. sl@0: */ sl@0: "1:" "\t" sl@0: "movl %%fs:0, %%edx" "\n\t" sl@0: "movl 0x8(%%edx), %%edx" "\n\t" sl@0: sl@0: /* sl@0: * Come here however we exited. Restore context from the sl@0: * EXCEPTION_REGISTRATION in case the stack is unbalanced. sl@0: */ sl@0: sl@0: "2:" "\t" sl@0: "movl 0xc(%%edx), %%esp" "\n\t" sl@0: "movl 0x8(%%edx), %%ebp" "\n\t" sl@0: "movl 0x0(%%edx), %%eax" "\n\t" sl@0: "movl %%eax, %%fs:0" "\n\t" sl@0: sl@0: : sl@0: /* No outputs */ sl@0: : sl@0: [index] "m" (index), sl@0: [rptr] "m" (regsPtr), sl@0: [registration] "m" (registration), sl@0: [ok] "i" (TCL_OK), sl@0: [error] "i" (TCL_ERROR) sl@0: : sl@0: "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory" ); sl@0: status = registration.status; sl@0: sl@0: #elif defined(_MSC_VER) && !defined(_WIN64) sl@0: sl@0: /* Define a structure in the stack frame to hold the registers */ sl@0: sl@0: struct { sl@0: DWORD dw0; sl@0: DWORD dw1; sl@0: DWORD dw2; sl@0: DWORD dw3; sl@0: } regs; sl@0: regs.dw0 = index; sl@0: sl@0: /* Execute the CPUID instruction and save regs in the stack frame */ sl@0: sl@0: _try { sl@0: _asm { sl@0: push ebx sl@0: push ecx sl@0: push edx sl@0: mov eax, regs.dw0 sl@0: cpuid sl@0: mov regs.dw0, eax sl@0: mov regs.dw1, ebx sl@0: mov regs.dw2, ecx sl@0: mov regs.dw3, edx sl@0: pop edx sl@0: pop ecx sl@0: pop ebx sl@0: } sl@0: sl@0: /* Copy regs back out to the caller */ sl@0: sl@0: regsPtr[0]=regs.dw0; sl@0: regsPtr[1]=regs.dw1; sl@0: regsPtr[2]=regs.dw2; sl@0: regsPtr[3]=regs.dw3; sl@0: sl@0: status = TCL_OK; sl@0: } __except( EXCEPTION_EXECUTE_HANDLER ) { sl@0: } sl@0: sl@0: #else sl@0: /* Don't know how to do assembly code for sl@0: * this compiler and/or architecture */ sl@0: #endif sl@0: return status; sl@0: }