/* * NT basis DLL * * This file contains the Nt* API functions of NTDLL.DLL. * In the original ntdll.dll they all seem to just call int 0x2e (down to the NTOSKRNL) * * Copyright 1996-1998 Marcus Meissner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "config.h" #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include "ntstatus.h" #define WIN32_NO_STATUS #include "wine/debug.h" #include "windef.h" #include "winternl.h" #include "ntdll_misc.h" #include "wine/server.h" #ifdef HAVE_MACH_MACH_H #include #endif WINE_DEFAULT_DEBUG_CHANNEL(ntdll); static ULONG execute_flags = MEM_EXECUTE_OPTION_DISABLE; /* * Process object */ /****************************************************************************** * NtTerminateProcess [NTDLL.@] * * Native applications must kill themselves when done */ NTSTATUS WINAPI NtTerminateProcess( HANDLE handle, LONG exit_code ) { NTSTATUS ret; BOOL self; SERVER_START_REQ( terminate_process ) { req->handle = wine_server_obj_handle( handle ); req->exit_code = exit_code; ret = wine_server_call( req ); self = !ret && reply->self; } SERVER_END_REQ; if (self && handle) _exit( exit_code ); return ret; } /****************************************************************************** * RtlGetCurrentPeb [NTDLL.@] * */ PEB * WINAPI RtlGetCurrentPeb(void) { return NtCurrentTeb()->Peb; } /*********************************************************************** * __wine_make_process_system (NTDLL.@) * * Mark the current process as a system process. * Returns the event that is signaled when all non-system processes have exited. */ HANDLE CDECL __wine_make_process_system(void) { HANDLE ret = 0; SERVER_START_REQ( make_process_system ) { if (!wine_server_call( req )) ret = wine_server_ptr_handle( reply->event ); } SERVER_END_REQ; return ret; } static UINT process_error_mode; #define UNIMPLEMENTED_INFO_CLASS(c) \ case c: \ FIXME("(process=%p) Unimplemented information class: " #c "\n", ProcessHandle); \ ret = STATUS_INVALID_INFO_CLASS; \ break ULONG_PTR get_system_affinity_mask(void) { ULONG num_cpus = NtCurrentTeb()->Peb->NumberOfProcessors; if (num_cpus >= sizeof(ULONG_PTR) * 8) return ~(ULONG_PTR)0; return ((ULONG_PTR)1 << num_cpus) - 1; } #if defined(HAVE_MACH_MACH_H) static void fill_VM_COUNTERS(VM_COUNTERS* pvmi) { #if defined(MACH_TASK_BASIC_INFO) struct mach_task_basic_info info; mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT; if(task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &infoCount) == KERN_SUCCESS) { pvmi->VirtualSize = info.resident_size + info.virtual_size; pvmi->PagefileUsage = info.virtual_size; pvmi->WorkingSetSize = info.resident_size; pvmi->PeakWorkingSetSize = info.resident_size_max; } #endif } #elif defined(linux) static void fill_VM_COUNTERS(VM_COUNTERS* pvmi) { FILE *f; char line[256]; unsigned long value; f = fopen("/proc/self/status", "r"); if (!f) return; while (fgets(line, sizeof(line), f)) { if (sscanf(line, "VmPeak: %lu", &value)) pvmi->PeakVirtualSize = (ULONG64)value * 1024; else if (sscanf(line, "VmSize: %lu", &value)) pvmi->VirtualSize = (ULONG64)value * 1024; else if (sscanf(line, "VmHWM: %lu", &value)) pvmi->PeakWorkingSetSize = (ULONG64)value * 1024; else if (sscanf(line, "VmRSS: %lu", &value)) pvmi->WorkingSetSize = (ULONG64)value * 1024; else if (sscanf(line, "RssAnon: %lu", &value)) pvmi->PagefileUsage += (ULONG64)value * 1024; else if (sscanf(line, "VmSwap: %lu", &value)) pvmi->PagefileUsage += (ULONG64)value * 1024; } pvmi->PeakPagefileUsage = pvmi->PagefileUsage; fclose(f); } #else static void fill_VM_COUNTERS(VM_COUNTERS* pvmi) { /* FIXME : real data */ } #endif /****************************************************************************** * NtQueryInformationProcess [NTDLL.@] * ZwQueryInformationProcess [NTDLL.@] * */ NTSTATUS WINAPI NtQueryInformationProcess( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength) { NTSTATUS ret = STATUS_SUCCESS; ULONG len = 0; TRACE("(%p,0x%08x,%p,0x%08x,%p)\n", ProcessHandle,ProcessInformationClass, ProcessInformation,ProcessInformationLength, ReturnLength); switch (ProcessInformationClass) { UNIMPLEMENTED_INFO_CLASS(ProcessQuotaLimits); UNIMPLEMENTED_INFO_CLASS(ProcessBasePriority); UNIMPLEMENTED_INFO_CLASS(ProcessRaisePriority); UNIMPLEMENTED_INFO_CLASS(ProcessExceptionPort); UNIMPLEMENTED_INFO_CLASS(ProcessAccessToken); UNIMPLEMENTED_INFO_CLASS(ProcessLdtInformation); UNIMPLEMENTED_INFO_CLASS(ProcessLdtSize); UNIMPLEMENTED_INFO_CLASS(ProcessIoPortHandlers); UNIMPLEMENTED_INFO_CLASS(ProcessPooledUsageAndLimits); UNIMPLEMENTED_INFO_CLASS(ProcessWorkingSetWatch); UNIMPLEMENTED_INFO_CLASS(ProcessUserModeIOPL); UNIMPLEMENTED_INFO_CLASS(ProcessEnableAlignmentFaultFixup); UNIMPLEMENTED_INFO_CLASS(ProcessPriorityClass); UNIMPLEMENTED_INFO_CLASS(ProcessWx86Information); UNIMPLEMENTED_INFO_CLASS(ProcessPriorityBoost); UNIMPLEMENTED_INFO_CLASS(ProcessDeviceMap); UNIMPLEMENTED_INFO_CLASS(ProcessSessionInformation); UNIMPLEMENTED_INFO_CLASS(ProcessForegroundInformation); UNIMPLEMENTED_INFO_CLASS(ProcessLUIDDeviceMapsEnabled); UNIMPLEMENTED_INFO_CLASS(ProcessBreakOnTermination); UNIMPLEMENTED_INFO_CLASS(ProcessHandleTracing); case ProcessBasicInformation: { PROCESS_BASIC_INFORMATION pbi; const ULONG_PTR affinity_mask = get_system_affinity_mask(); if (ProcessInformationLength >= sizeof(PROCESS_BASIC_INFORMATION)) { if (!ProcessInformation) ret = STATUS_ACCESS_VIOLATION; else if (!ProcessHandle) ret = STATUS_INVALID_HANDLE; else { SERVER_START_REQ(get_process_info) { req->handle = wine_server_obj_handle( ProcessHandle ); if ((ret = wine_server_call( req )) == STATUS_SUCCESS) { pbi.ExitStatus = reply->exit_code; pbi.PebBaseAddress = wine_server_get_ptr( reply->peb ); pbi.AffinityMask = reply->affinity & affinity_mask; pbi.BasePriority = reply->priority; pbi.UniqueProcessId = reply->pid; pbi.InheritedFromUniqueProcessId = reply->ppid; } } SERVER_END_REQ; memcpy(ProcessInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION)); len = sizeof(PROCESS_BASIC_INFORMATION); } if (ProcessInformationLength > sizeof(PROCESS_BASIC_INFORMATION)) ret = STATUS_INFO_LENGTH_MISMATCH; } else { len = sizeof(PROCESS_BASIC_INFORMATION); ret = STATUS_INFO_LENGTH_MISMATCH; } } break; case ProcessIoCounters: { IO_COUNTERS pii; if (ProcessInformationLength >= sizeof(IO_COUNTERS)) { if (!ProcessInformation) ret = STATUS_ACCESS_VIOLATION; else if (!ProcessHandle) ret = STATUS_INVALID_HANDLE; else { /* FIXME : real data */ memset(&pii, 0 , sizeof(IO_COUNTERS)); memcpy(ProcessInformation, &pii, sizeof(IO_COUNTERS)); len = sizeof(IO_COUNTERS); } if (ProcessInformationLength > sizeof(IO_COUNTERS)) ret = STATUS_INFO_LENGTH_MISMATCH; } else { len = sizeof(IO_COUNTERS); ret = STATUS_INFO_LENGTH_MISMATCH; } } break; case ProcessVmCounters: { VM_COUNTERS pvmi; /* older Windows versions don't have the PrivatePageCount field */ if (ProcessInformationLength >= FIELD_OFFSET(VM_COUNTERS,PrivatePageCount)) { if (!ProcessInformation) ret = STATUS_ACCESS_VIOLATION; else { memset(&pvmi, 0 , sizeof(VM_COUNTERS)); if (ProcessHandle == GetCurrentProcess()) fill_VM_COUNTERS(&pvmi); else { SERVER_START_REQ(get_process_vm_counters) { req->handle = wine_server_obj_handle( ProcessHandle ); if (!(ret = wine_server_call( req ))) { pvmi.PeakVirtualSize = reply->peak_virtual_size; pvmi.VirtualSize = reply->virtual_size; pvmi.PeakWorkingSetSize = reply->peak_working_set_size; pvmi.WorkingSetSize = reply->working_set_size; pvmi.PagefileUsage = reply->pagefile_usage; pvmi.PeakPagefileUsage = reply->peak_pagefile_usage; } } SERVER_END_REQ; if (ret) break; } len = ProcessInformationLength; if (len != FIELD_OFFSET(VM_COUNTERS,PrivatePageCount)) len = sizeof(VM_COUNTERS); memcpy(ProcessInformation, &pvmi, min(ProcessInformationLength,sizeof(VM_COUNTERS))); } if (ProcessInformationLength != FIELD_OFFSET(VM_COUNTERS,PrivatePageCount) && ProcessInformationLength != sizeof(VM_COUNTERS)) ret = STATUS_INFO_LENGTH_MISMATCH; } else { len = sizeof(pvmi); ret = STATUS_INFO_LENGTH_MISMATCH; } } break; case ProcessTimes: { KERNEL_USER_TIMES pti; if (ProcessInformationLength >= sizeof(KERNEL_USER_TIMES)) { if (!ProcessInformation) ret = STATUS_ACCESS_VIOLATION; else if (!ProcessHandle) ret = STATUS_INVALID_HANDLE; else { /* FIXME : User- and KernelTime have to be implemented */ memset(&pti, 0, sizeof(KERNEL_USER_TIMES)); SERVER_START_REQ(get_process_info) { req->handle = wine_server_obj_handle( ProcessHandle ); if ((ret = wine_server_call( req )) == STATUS_SUCCESS) { pti.CreateTime.QuadPart = reply->start_time; pti.ExitTime.QuadPart = reply->end_time; } } SERVER_END_REQ; memcpy(ProcessInformation, &pti, sizeof(KERNEL_USER_TIMES)); len = sizeof(KERNEL_USER_TIMES); } if (ProcessInformationLength > sizeof(KERNEL_USER_TIMES)) ret = STATUS_INFO_LENGTH_MISMATCH; } else { len = sizeof(KERNEL_USER_TIMES); ret = STATUS_INFO_LENGTH_MISMATCH; } } break; case ProcessDebugPort: len = sizeof(DWORD_PTR); if (ProcessInformationLength == len) { if (!ProcessInformation) ret = STATUS_ACCESS_VIOLATION; else if (!ProcessHandle) ret = STATUS_INVALID_HANDLE; else { SERVER_START_REQ(get_process_info) { req->handle = wine_server_obj_handle( ProcessHandle ); if ((ret = wine_server_call( req )) == STATUS_SUCCESS) { *(DWORD_PTR *)ProcessInformation = reply->debugger_present ? ~(DWORD_PTR)0 : 0; } } SERVER_END_REQ; } } else ret = STATUS_INFO_LENGTH_MISMATCH; break; case ProcessDebugFlags: len = sizeof(DWORD); if (ProcessInformationLength == len) { if (!ProcessInformation) ret = STATUS_ACCESS_VIOLATION; else if (!ProcessHandle) ret = STATUS_INVALID_HANDLE; else { SERVER_START_REQ(get_process_info) { req->handle = wine_server_obj_handle( ProcessHandle ); if ((ret = wine_server_call( req )) == STATUS_SUCCESS) { *(DWORD *)ProcessInformation = reply->debug_children; } } SERVER_END_REQ; } } else ret = STATUS_INFO_LENGTH_MISMATCH; break; case ProcessDefaultHardErrorMode: len = sizeof(process_error_mode); if (ProcessInformationLength == len) memcpy(ProcessInformation, &process_error_mode, len); else ret = STATUS_INFO_LENGTH_MISMATCH; break; case ProcessDebugObjectHandle: /* "These are not the debuggers you are looking for." * * set it to 0 aka "no debugger" to satisfy copy protections */ len = sizeof(HANDLE); if (ProcessInformationLength == len) { if (!ProcessInformation) ret = STATUS_ACCESS_VIOLATION; else if (!ProcessHandle) ret = STATUS_INVALID_HANDLE; else { memset(ProcessInformation, 0, ProcessInformationLength); ret = STATUS_PORT_NOT_SET; } } else ret = STATUS_INFO_LENGTH_MISMATCH; break; case ProcessHandleCount: if (ProcessInformationLength >= 4) { if (!ProcessInformation) ret = STATUS_ACCESS_VIOLATION; else if (!ProcessHandle) ret = STATUS_INVALID_HANDLE; else { memset(ProcessInformation, 0, 4); len = 4; } if (ProcessInformationLength > 4) ret = STATUS_INFO_LENGTH_MISMATCH; } else { len = 4; ret = STATUS_INFO_LENGTH_MISMATCH; } break; case ProcessAffinityMask: len = sizeof(ULONG_PTR); if (ProcessInformationLength == len) { const ULONG_PTR system_mask = get_system_affinity_mask(); SERVER_START_REQ(get_process_info) { req->handle = wine_server_obj_handle( ProcessHandle ); if (!(ret = wine_server_call( req ))) *(ULONG_PTR *)ProcessInformation = reply->affinity & system_mask; } SERVER_END_REQ; } else ret = STATUS_INFO_LENGTH_MISMATCH; break; case ProcessWow64Information: len = sizeof(ULONG_PTR); if (ProcessInformationLength != len) ret = STATUS_INFO_LENGTH_MISMATCH; else if (!ProcessInformation) ret = STATUS_ACCESS_VIOLATION; else if(!ProcessHandle) ret = STATUS_INVALID_HANDLE; else { ULONG_PTR val = 0; if (ProcessHandle == GetCurrentProcess()) val = is_wow64; else if (server_cpus & ((1 << CPU_x86_64) | (1 << CPU_ARM64))) { SERVER_START_REQ( get_process_info ) { req->handle = wine_server_obj_handle( ProcessHandle ); if (!(ret = wine_server_call( req ))) val = (reply->cpu != CPU_x86_64 && reply->cpu != CPU_ARM64); } SERVER_END_REQ; } *(ULONG_PTR *)ProcessInformation = val; } break; case ProcessImageFileName: /* FIXME: this will return a DOS path. Windows returns an NT path. Changing this would require also changing kernel32.QueryFullProcessImageName. * The latter may be harder because of the lack of RtlNtPathNameToDosPathName. */ SERVER_START_REQ(get_dll_info) { UNICODE_STRING *image_file_name_str = ProcessInformation; req->handle = wine_server_obj_handle( ProcessHandle ); req->base_address = 0; /* main module */ wine_server_set_reply( req, image_file_name_str ? image_file_name_str + 1 : NULL, ProcessInformationLength > sizeof(UNICODE_STRING) ? ProcessInformationLength - sizeof(UNICODE_STRING) : 0 ); ret = wine_server_call( req ); if (ret == STATUS_BUFFER_TOO_SMALL) ret = STATUS_INFO_LENGTH_MISMATCH; len = sizeof(UNICODE_STRING) + reply->filename_len; if (ret == STATUS_SUCCESS) { image_file_name_str->MaximumLength = image_file_name_str->Length = reply->filename_len; image_file_name_str->Buffer = (PWSTR)(image_file_name_str + 1); } } SERVER_END_REQ; break; case ProcessExecuteFlags: len = sizeof(ULONG); if (ProcessInformationLength == len) *(ULONG *)ProcessInformation = execute_flags; else ret = STATUS_INFO_LENGTH_MISMATCH; break; default: FIXME("(%p,info_class=%d,%p,0x%08x,%p) Unknown information class\n", ProcessHandle,ProcessInformationClass, ProcessInformation,ProcessInformationLength, ReturnLength); ret = STATUS_INVALID_INFO_CLASS; break; } if (ReturnLength) *ReturnLength = len; return ret; } /****************************************************************************** * NtSetInformationProcess [NTDLL.@] * ZwSetInformationProcess [NTDLL.@] */ NTSTATUS WINAPI NtSetInformationProcess( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, IN PVOID ProcessInformation, IN ULONG ProcessInformationLength) { NTSTATUS ret = STATUS_SUCCESS; switch (ProcessInformationClass) { case ProcessDefaultHardErrorMode: if (ProcessInformationLength != sizeof(UINT)) return STATUS_INVALID_PARAMETER; process_error_mode = *(UINT *)ProcessInformation; break; case ProcessAffinityMask: { const ULONG_PTR system_mask = get_system_affinity_mask(); if (ProcessInformationLength != sizeof(DWORD_PTR)) return STATUS_INVALID_PARAMETER; if (*(PDWORD_PTR)ProcessInformation & ~system_mask) return STATUS_INVALID_PARAMETER; if (!*(PDWORD_PTR)ProcessInformation) return STATUS_INVALID_PARAMETER; SERVER_START_REQ( set_process_info ) { req->handle = wine_server_obj_handle( ProcessHandle ); req->affinity = *(PDWORD_PTR)ProcessInformation; req->mask = SET_PROCESS_INFO_AFFINITY; ret = wine_server_call( req ); } SERVER_END_REQ; break; } case ProcessPriorityClass: if (ProcessInformationLength != sizeof(PROCESS_PRIORITY_CLASS)) return STATUS_INVALID_PARAMETER; else { PROCESS_PRIORITY_CLASS* ppc = ProcessInformation; SERVER_START_REQ( set_process_info ) { req->handle = wine_server_obj_handle( ProcessHandle ); /* FIXME Foreground isn't used */ req->priority = ppc->PriorityClass; req->mask = SET_PROCESS_INFO_PRIORITY; ret = wine_server_call( req ); } SERVER_END_REQ; } break; case ProcessExecuteFlags: if (ProcessInformationLength != sizeof(ULONG)) return STATUS_INVALID_PARAMETER; else if (execute_flags & MEM_EXECUTE_OPTION_PERMANENT) return STATUS_ACCESS_DENIED; else { BOOL enable; switch (*(ULONG *)ProcessInformation & (MEM_EXECUTE_OPTION_ENABLE|MEM_EXECUTE_OPTION_DISABLE)) { case MEM_EXECUTE_OPTION_ENABLE: enable = TRUE; break; case MEM_EXECUTE_OPTION_DISABLE: enable = FALSE; break; default: return STATUS_INVALID_PARAMETER; } execute_flags = *(ULONG *)ProcessInformation; VIRTUAL_SetForceExec( enable ); } break; default: FIXME("(%p,0x%08x,%p,0x%08x) stub\n", ProcessHandle,ProcessInformationClass,ProcessInformation, ProcessInformationLength); ret = STATUS_NOT_IMPLEMENTED; break; } return ret; } /****************************************************************************** * NtFlushInstructionCache [NTDLL.@] * ZwFlushInstructionCache [NTDLL.@] */ NTSTATUS WINAPI NtFlushInstructionCache( IN HANDLE ProcessHandle, IN LPCVOID BaseAddress, IN SIZE_T Size) { static int once; if (!once++) { #if defined(__x86_64__) || defined(__i386__) TRACE("%p %p %ld - no-op on x86 and x86_64\n", ProcessHandle, BaseAddress, Size ); #else FIXME("%p %p %ld\n", ProcessHandle, BaseAddress, Size ); #endif } return STATUS_SUCCESS; } /****************************************************************** * NtOpenProcess [NTDLL.@] * ZwOpenProcess [NTDLL.@] */ NTSTATUS WINAPI NtOpenProcess(PHANDLE handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES* attr, const CLIENT_ID* cid) { NTSTATUS status; SERVER_START_REQ( open_process ) { req->pid = HandleToULong(cid->UniqueProcess); req->access = access; req->attributes = attr ? attr->Attributes : 0; status = wine_server_call( req ); if (!status) *handle = wine_server_ptr_handle( reply->handle ); } SERVER_END_REQ; return status; } /****************************************************************************** * NtResumeProcess * ZwResumeProcess */ NTSTATUS WINAPI NtResumeProcess( HANDLE handle ) { FIXME("stub: %p\n", handle); return STATUS_NOT_IMPLEMENTED; } /****************************************************************************** * NtSuspendProcess * ZwSuspendProcess */ NTSTATUS WINAPI NtSuspendProcess( HANDLE handle ) { FIXME("stub: %p\n", handle); return STATUS_NOT_IMPLEMENTED; }