diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ef363a..d762d03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(wibo LANGUAGES ASM C CXX) +set(CMAKE_CXX_STANDARD 20) + +if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15") + set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE) +endif() + set(WIBO_VERSION "" CACHE STRING "Version string for the wibo binary; if empty, attempts to use git describe") if(NOT "${WIBO_VERSION}" STREQUAL "") @@ -92,13 +99,15 @@ FetchContent_MakeAvailable(mimalloc) # Disable `note: the alignment of '_Atomic long long int' fields changed in GCC 11.1` target_compile_options(mimalloc-obj PRIVATE -Wno-psabi) -target_compile_definitions(mimalloc-obj PRIVATE MI_USE_BUILTIN_THREAD_POINTER=1) +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_compile_definitions(mimalloc-obj PRIVATE MI_USE_BUILTIN_THREAD_POINTER=1) +endif() if (WIBO_64) # Disable a noisy warning on startup target_compile_definitions(mimalloc-obj PRIVATE MI_DEBUG=0) endif() -if (WIBO_ENABLE_LIBURING) +if (WIBO_ENABLE_LIBURING AND CMAKE_SYSTEM_NAME STREQUAL "Linux") FetchContent_Declare( liburing GIT_REPOSITORY https://github.com/axboe/liburing.git @@ -142,7 +151,6 @@ add_executable(wibo dll/advapi32/wincrypt.cpp dll/advapi32/winreg.cpp dll/bcrypt.cpp - dll/crt.cpp dll/kernel32.cpp dll/kernel32/debugapi.cpp dll/kernel32/errhandlingapi.cpp @@ -169,7 +177,6 @@ add_executable(wibo dll/kernel32/wow64apiset.cpp dll/lmgr.cpp dll/mscoree.cpp - dll/msvcrt.cpp dll/ntdll.cpp dll/ole32.cpp dll/rpcrt4.cpp @@ -178,7 +185,6 @@ add_executable(wibo dll/version.cpp src/access.cpp src/async_io.cpp - src/async_io_epoll.cpp src/errors.cpp src/files.cpp src/handles.cpp @@ -186,23 +192,52 @@ add_executable(wibo src/loader.cpp src/main.cpp src/modules.cpp - src/processes.cpp + src/processes_common.cpp src/resources.cpp src/setup.S src/strutil.cpp src/tls.cpp ) + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_sources(wibo PRIVATE + src/async_io_epoll.cpp + src/processes_linux.cpp + ) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + target_sources(wibo PRIVATE + src/processes_darwin.cpp + src/setup_darwin.cpp + ) + set_source_files_properties(src/setup_darwin.cpp PROPERTIES COMPILE_FLAGS "-fms-extensions") +else() + message(FATAL_ERROR "Unsupported platform for ProcessManager") +endif() target_compile_definitions(wibo PRIVATE _GNU_SOURCE _FILE_OFFSET_BITS=64 _TIME_BITS=64) target_compile_features(wibo PRIVATE cxx_std_20) target_compile_options(wibo PRIVATE -Wall -Wextra) -target_link_options(wibo PRIVATE -no-pie -Wl,--image-base=0x70000000) +if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + target_link_options(wibo PRIVATE + -Wl,-no_pie,-no_fixup_chains + -Wl,-segalign,0x1000 + -Wl,-pagezero_size,0x1000 + -Wl,-image_base,0x7E001000 + -Wl,-segaddr,RESV32,0x1000 + -Wl,-segprot,RESV32,-,- + ) +else() + target_link_options(wibo PRIVATE -no-pie -Wl,--image-base=0x70000000) +endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_compile_options(wibo PRIVATE -maccumulate-outgoing-args) target_link_options(wibo PRIVATE -maccumulate-outgoing-args) endif() target_include_directories(wibo PRIVATE dll src ${WIBO_GENERATED_HEADER_DIR}) -target_link_libraries(wibo PRIVATE mimalloc-obj atomic) -if (WIBO_ENABLE_LIBURING) +target_link_libraries(wibo PRIVATE mimalloc-obj) +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_link_libraries(wibo PRIVATE atomic) +endif() +if (WIBO_ENABLE_LIBURING AND CMAKE_SYSTEM_NAME STREQUAL "Linux") target_compile_definitions(wibo PRIVATE WIBO_ENABLE_LIBURING=1) target_link_libraries(wibo PRIVATE liburing) target_sources(wibo PRIVATE src/async_io_uring.cpp) @@ -216,7 +251,11 @@ find_package(Python3 COMPONENTS Interpreter REQUIRED) # Track down libclang for trampoline generation. # find_package(Clang) ends up requiring too many dependencies, # so a quick-and-dirty manual search is done here instead. -set(CLANG_ROOT "/usr" CACHE PATH "Path to Clang installation") +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(CLANG_ROOT "/usr" CACHE PATH "Path to Clang installation") +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(CLANG_ROOT "/Library/Developer/CommandLineTools/usr/lib" CACHE PATH "Path to Clang installation") +endif() set(_LLVM_MIN_VER 17) set(_LLVM_MAX_VER 25) set(_CLANG_LIB_SUFFIXES "") @@ -274,9 +313,7 @@ wibo_codegen_module(NAME advapi32 HEADERS ) wibo_codegen_module(NAME bcrypt HEADERS dll/bcrypt.h) wibo_codegen_module(NAME entry HEADERS src/entry.h) -wibo_codegen_module(NAME crt HEADERS dll/crt.h) wibo_codegen_module(NAME mscoree HEADERS dll/mscoree.h) -wibo_codegen_module(NAME msvcrt HEADERS dll/msvcrt.h) wibo_codegen_module(NAME version HEADERS dll/version.h) wibo_codegen_module(NAME rpcrt4 HEADERS dll/rpcrt4.h) wibo_codegen_module(NAME vcruntime HEADERS dll/vcruntime.h) @@ -432,3 +469,7 @@ if (WIBO_ENABLE_FIXTURE_TESTS) add_test(NAME wibo.build_fixtures COMMAND ${_wibo_fixture_build_command}) endif() endif() + +if (NOT TARGET wibo_test_fixtures) + add_custom_target(wibo_test_fixtures) +endif() diff --git a/dll/advapi32/processthreadsapi.cpp b/dll/advapi32/processthreadsapi.cpp index dd50225..c288402 100644 --- a/dll/advapi32/processthreadsapi.cpp +++ b/dll/advapi32/processthreadsapi.cpp @@ -19,7 +19,7 @@ BOOL WINAPI OpenProcessToken(HANDLE ProcessHandle, DWORD DesiredAccess, PHANDLE } Pin obj; if (kernel32::isPseudoCurrentProcessHandle(ProcessHandle)) { - obj = make_pin(getpid(), -1); + obj = make_pin(getpid(), -1, false); } else { obj = wibo::handles().getAs(ProcessHandle); } diff --git a/dll/advapi32/wincrypt.cpp b/dll/advapi32/wincrypt.cpp index 93b10e6..92a7344 100644 --- a/dll/advapi32/wincrypt.cpp +++ b/dll/advapi32/wincrypt.cpp @@ -92,12 +92,15 @@ BOOL WINAPI CryptGenRandom(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer) { return FALSE; } +#ifdef __APPLE__ + arc4random_buf(pbBuffer, dwLen); +#else ssize_t ret = getrandom(pbBuffer, dwLen, 0); if (ret < 0 || static_cast(ret) != dwLen) { kernel32::setLastError(ERROR_NOT_SUPPORTED); return FALSE; } - +#endif return TRUE; } diff --git a/dll/bcrypt.cpp b/dll/bcrypt.cpp index e27c99c..b491d94 100644 --- a/dll/bcrypt.cpp +++ b/dll/bcrypt.cpp @@ -15,6 +15,9 @@ constexpr ULONG BCRYPT_RNG_USE_ENTROPY_IN_BUFFER = 0x00000001; constexpr ULONG BCRYPT_USE_SYSTEM_PREFERRED_RNG = 0x00000002; bool fillWithSystemRandom(PUCHAR buffer, size_t length) { +#ifdef __APPLE__ + arc4random_buf(buffer, length); +#else while (length > 0) { ssize_t written = getrandom(buffer, length, 0); if (written < 0) { @@ -27,6 +30,7 @@ bool fillWithSystemRandom(PUCHAR buffer, size_t length) { buffer += written; length -= static_cast(written); } +#endif return true; } diff --git a/dll/crt.cpp b/dll/crt.cpp deleted file mode 100644 index 0172c6f..0000000 --- a/dll/crt.cpp +++ /dev/null @@ -1,478 +0,0 @@ -#include "crt.h" - -#include "common.h" -#include "context.h" -#include "crt_trampolines.h" -#include "heap.h" -#include "kernel32/internal.h" -#include "modules.h" -#include "types.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace { - -FILE *mapToHostFile(_FILE *file) { - if (!file) - return nullptr; - switch (file->_file) { - case STDIN_FILENO: - return stdin; - case STDOUT_FILENO: - return stdout; - case STDERR_FILENO: - return stderr; - default: - return nullptr; - } -} - -} // namespace - -namespace crt { - -int _commode = 0; -int _fmode = 0; - -std::vector<_PVFV> atexitFuncs; -_invalid_parameter_handler invalidParameterHandler = nullptr; - -GUEST_PTR guestArgv = GUEST_NULL; -GUEST_PTR guestEnviron = GUEST_NULL; - -void CDECL _initterm(GUEST_PTR *ppfn, GUEST_PTR *end) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_initterm(%p, %p)\n", ppfn, end); - do { - if (GUEST_PTR pfn = *++ppfn) { - DEBUG_LOG("-> calling %p\n", pfn); - auto fn = reinterpret_cast<_PVFV>(fromGuestPtr(pfn)); - call__PVFV(fn); - } - } while (ppfn < end); -} - -int CDECL _initterm_e(GUEST_PTR *ppfn, GUEST_PTR *end) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_initterm_e(%p, %p)\n", ppfn, end); - do { - if (GUEST_PTR pfn = *++ppfn) { - DEBUG_LOG("-> calling %p\n", pfn); - auto fn = reinterpret_cast<_PIFV>(fromGuestPtr(pfn)); - int err = call__PIFV(fn); - if (err) - return err; - } - } while (ppfn < end); - - return 0; -} - -void CDECL _set_app_type(_crt_app_type type) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("STUB: _set_app_type(%i)\n", type); -} - -int CDECL _set_fmode(int mode) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_set_fmode(%i)\n", mode); - _fmode = mode; - return 0; -} - -GUEST_PTR CDECL __p__commode() { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("__p__commode()\n"); - return toGuestPtr(&_commode); -} - -GUEST_PTR CDECL __p__fmode() { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("__p__fmode()\n"); - return toGuestPtr(&_fmode); -} - -int CDECL _crt_atexit(void (*func)()) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_crt_atexit(%p)\n", func); - atexitFuncs.push_back(func); - return 0; -} - -int CDECL _configure_narrow_argv(_crt_argv_mode mode) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("STUB: _configure_narrow_argv(%i)\n", mode); - return 0; -} - -_invalid_parameter_handler CDECL _set_invalid_parameter_handler(_invalid_parameter_handler newHandler) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("STUB: _set_invalid_parameter_handler(%p)\n", newHandler); - _invalid_parameter_handler oldHandler = invalidParameterHandler; - invalidParameterHandler = newHandler; - return oldHandler; -} - -int CDECL _controlfp_s(unsigned int *currentControl, unsigned int newControl, unsigned int mask) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("STUB: _controlfp_s(%p, %u, %u)\n", currentControl, newControl, mask); - return 0; -} - -int CDECL _configthreadlocale(int per_thread_locale_type) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("STUB: _configthreadlocale(%i)\n", per_thread_locale_type); - return 0; -} - -int CDECL _initialize_narrow_environment() { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("STUB: _initialize_narrow_environment()\n"); - return 0; -} - -int CDECL _set_new_mode(int newhandlermode) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("STUB: _set_new_mode(%i)\n", newhandlermode); - return 0; -} - -GUEST_PTR CDECL _get_initial_narrow_environment() { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_get_initial_narrow_environment()\n"); - if (guestEnviron == GUEST_NULL) { - int count = 0; - while (environ[count]) { - count++; - } - GUEST_PTR *buf = reinterpret_cast(wibo::heap::guestMalloc(count * sizeof(GUEST_PTR))); - if (!buf) { - return GUEST_NULL; - } - for (int i = 0; i < count; i++) { - size_t len = ::strlen(environ[i]); - char *str = reinterpret_cast(wibo::heap::guestMalloc(len + 1)); - ::memcpy(str, environ[i], len + 1); - buf[i] = toGuestPtr(str); - } - guestEnviron = toGuestPtr(buf); - } - return guestEnviron; -} - -GUEST_PTR CDECL __p__environ() { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("__p__environ()\n"); - if (guestEnviron == GUEST_NULL) { - int count = 0; - while (environ[count]) { - count++; - } - GUEST_PTR *buf = reinterpret_cast(wibo::heap::guestMalloc(count * sizeof(GUEST_PTR))); - if (!buf) { - return GUEST_NULL; - } - for (int i = 0; i < count; i++) { - size_t len = ::strlen(environ[i]); - char *str = reinterpret_cast(wibo::heap::guestMalloc(len + 1)); - ::memcpy(str, environ[i], len + 1); - buf[i] = toGuestPtr(str); - } - guestEnviron = toGuestPtr(buf); - } - return toGuestPtr(&environ); -} - -GUEST_PTR CDECL __p___argv() { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("__p___argv()\n"); - if (guestArgv == GUEST_NULL) { - int count = 0; - while (wibo::argv[count]) { - count++; - } - GUEST_PTR *buf = reinterpret_cast(wibo::heap::guestMalloc(count * sizeof(GUEST_PTR))); - if (!buf) { - return GUEST_NULL; - } - for (int i = 0; i < count; i++) { - size_t len = ::strlen(wibo::argv[i]); - char *str = reinterpret_cast(wibo::heap::guestMalloc(len + 1)); - ::memcpy(str, wibo::argv[i], len + 1); - buf[i] = toGuestPtr(str); - } - guestArgv = toGuestPtr(buf); - } - return toGuestPtr(&guestArgv); -} - -GUEST_PTR CDECL __p___argc() { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("__p___argc()\n"); - return toGuestPtr(&wibo::argc); -} - -SIZE_T CDECL strlen(const char *str) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("strlen(%p)\n", str); - return ::strlen(str); -} - -int CDECL strcmp(const char *lhs, const char *rhs) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("strcmp(%p, %p)\n", lhs, rhs); - return ::strcmp(lhs, rhs); -} - -int CDECL strncmp(const char *lhs, const char *rhs, SIZE_T count) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("strncmp(%p, %p, %zu)\n", lhs, rhs, count); - return ::strncmp(lhs, rhs, count); -} - -char *CDECL strcpy(char *dest, const char *src) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("strcpy(%p, %p)\n", dest, src); - return ::strcpy(dest, src); -} - -char *CDECL strncpy(char *dest, const char *src, SIZE_T count) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("strncpy(%p, %p, %zu)\n", dest, src, count); - return ::strncpy(dest, src, count); -} - -const char *CDECL strrchr(const char *str, int ch) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("strrchr(%p, %i)\n", str, ch); - return ::strrchr(str, ch); -} - -void *CDECL malloc(SIZE_T size) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("malloc(%zu)\n", size); - return wibo::heap::guestMalloc(size); -} - -void *CDECL calloc(SIZE_T count, SIZE_T size) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("calloc(%zu, %zu)\n", count, size); - return wibo::heap::guestCalloc(count, size); -} - -void *CDECL realloc(void *ptr, SIZE_T newSize) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("realloc(%p, %zu)\n", ptr, newSize); - return wibo::heap::guestRealloc(ptr, newSize); -} - -void CDECL free(void *ptr) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("free(%p)\n", ptr); - wibo::heap::guestFree(ptr); -} - -void *CDECL memcpy(void *dest, const void *src, SIZE_T count) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("memcpy(%p, %p, %zu)\n", dest, src, count); - return std::memcpy(dest, src, count); -} - -void *CDECL memmove(void *dest, const void *src, SIZE_T count) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("memmove(%p, %p, %zu)\n", dest, src, count); - return std::memmove(dest, src, count); -} - -void *CDECL memset(void *dest, int ch, SIZE_T count) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("memset(%p, %i, %zu)\n", dest, ch, count); - return std::memset(dest, ch, count); -} - -int CDECL memcmp(const void *lhs, const void *rhs, SIZE_T count) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("memcmp(%p, %p, %zu)\n", lhs, rhs, count); - return std::memcmp(lhs, rhs, count); -} - -int CDECL __setusermatherr(void *handler) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("STUB: __setusermatherr(%p)\n", handler); - return 0; -} - -int CDECL _initialize_onexit_table(_onexit_table_t *table) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_initialize_onexit_table(%p)\n", table); - if (!table) - return -1; - if (table->first != table->last) - return 0; - table->first = GUEST_NULL; - table->last = GUEST_NULL; - table->end = GUEST_NULL; - return 0; -} - -int CDECL _register_onexit_function(_onexit_table_t *table, _onexit_t func) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_register_onexit_function(%p, %p)\n", table, func); - if (!table || !func) - return -1; - GUEST_PTR *first = reinterpret_cast(fromGuestPtr(table->first)); - GUEST_PTR *last = reinterpret_cast(fromGuestPtr(table->last)); - GUEST_PTR *end = reinterpret_cast(fromGuestPtr(table->end)); - if (last == end) { - size_t count = end - first; - size_t newCount = count + 1; - if (newCount <= 0) - return -1; - GUEST_PTR *newTable = static_cast(wibo::heap::guestRealloc(first, newCount * sizeof(GUEST_PTR))); - if (!newTable) - return -1; - table->first = toGuestPtr(newTable); - last = newTable + count; - table->end = toGuestPtr(newTable + newCount); - } - *last = toGuestPtr(reinterpret_cast(func)); - table->last = toGuestPtr(last + 1); - return 0; -} - -int CDECL _execute_onexit_table(_onexit_table_t *table) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_execute_onexit_table(%p)\n", table); - if (!table) - return -1; - GUEST_PTR *first = reinterpret_cast(fromGuestPtr(table->first)); - GUEST_PTR *last = reinterpret_cast(fromGuestPtr(table->last)); - for (auto it = first; it != last; ++it) { - _onexit_t fn = reinterpret_cast<_onexit_t>(fromGuestPtr(*it)); - DEBUG_LOG("Calling onexit_table function %p\n", fn); - call__onexit_t(fn); - } - return 0; -} - -void CDECL exit(int status) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("exit(%i)\n", status); - _cexit(); - kernel32::exitInternal(status); -} - -void CDECL _cexit() { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_cexit()\n"); - for (auto it = atexitFuncs.rbegin(); it != atexitFuncs.rend(); ++it) { - DEBUG_LOG("Calling atexit function %p\n", *it); - call__PVFV(*it); - } - std::fflush(nullptr); -} - -void CDECL _exit(int status) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_exit(%i)\n", status); - kernel32::exitInternal(status); -} - -void CDECL abort() { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("abort()\n"); - std::abort(); -} - -signal_handler CDECL signal(int signum, signal_handler handler) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("signal(%i, %p)\n", signum, handler); - return std::signal(signum, handler); -} - -void *CDECL __acrt_iob_func(unsigned int index) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("__acrt_iob_func(%u)\n", index); - if (index == 0) - return stdin; - if (index == 1) - return stdout; - if (index == 2) - return stderr; - return nullptr; -} - -int CDECL_NO_CONV __stdio_common_vfprintf(ULONGLONG options, _FILE *stream, const char *format, void *locale, - va_list args) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("__stdio_common_vfprintf(%llu, %p, %s, %p, %p)\n", options, stream, format, locale, args); - FILE *hostFile = mapToHostFile(stream); - if (!hostFile) - return -1; - return vfprintf(hostFile, format, args); -} - -int CDECL_NO_CONV __stdio_common_vsprintf(ULONGLONG options, char *buffer, SIZE_T len, const char *format, void *locale, - va_list args) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("__stdio_common_vsprintf(%llu, %p, %zu, %s, %p, ...)\n", options, buffer, len, format, locale); - if (!buffer || !format) - return -1; - int result = vsnprintf(buffer, len, format, args); - if (result < 0) - return -1; - if (len > 0 && static_cast(result) >= len) - return -1; - return result; -} - -static thread_local sort_compare currentCompare = nullptr; - -static int doCompare(const void *a, const void *b) { return call_sort_compare(currentCompare, a, b); } - -void CDECL qsort(void *base, SIZE_T num, SIZE_T size, sort_compare compare) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("qsort(%p, %zu, %zu, %p)\n", base, num, size, compare); - currentCompare = compare; - ::qsort(base, num, size, doCompare); -} - -int CDECL puts(const char *str) { - HOST_CONTEXT_GUARD(); - if (!str) { - str = "(null)"; - } - DEBUG_LOG("puts(%s)\n", str); - if (std::fputs(str, stdout) < 0) - return EOF; - if (std::fputc('\n', stdout) == EOF) - return EOF; - return 0; -} - -} // namespace crt - -#include "crt_trampolines.h" - -extern const wibo::ModuleStub lib_crt = { - (const char *[]){ - "api-ms-win-crt-heap-l1-1-0", - "api-ms-win-crt-locale-l1-1-0", - "api-ms-win-crt-runtime-l1-1-0", - "api-ms-win-crt-stdio-l1-1-0", - "api-ms-win-crt-string-l1-1-0", - "api-ms-win-crt-environment-l1-1-0", - "api-ms-win-crt-math-l1-1-0", - "api-ms-win-crt-private-l1-1-0", - "api-ms-win-crt-utility-l1-1-0", - nullptr, - }, - crtThunkByName, - nullptr, -}; diff --git a/dll/crt.h b/dll/crt.h deleted file mode 100644 index 816b606..0000000 --- a/dll/crt.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -#include "types.h" - -typedef void(_CC_CDECL *_PVFV)(); -typedef int(_CC_CDECL *_PIFV)(); -typedef void(_CC_CDECL *_invalid_parameter_handler)(const WCHAR *, const WCHAR *, const WCHAR *, UINT, UINT_PTR); - -typedef enum _crt_app_type { - _crt_unknown_app, - _crt_console_app, - _crt_gui_app, -} _crt_app_type; - -typedef enum _crt_argv_mode { - _crt_argv_no_arguments, - _crt_argv_unexpanded_arguments, - _crt_argv_expanded_arguments, -} _crt_argv_mode; - -typedef void(_CC_CDECL *signal_handler)(int); -typedef int(_CC_CDECL *sort_compare)(const void *, const void *); -typedef int(_CC_CDECL *_onexit_t)(); - -struct _onexit_table_t { - GUEST_PTR first; - GUEST_PTR last; - GUEST_PTR end; -}; - -namespace crt { - -extern int _commode; -extern int _fmode; - -void CDECL _initterm(GUEST_PTR *ppfn, GUEST_PTR *end); -int CDECL _initterm_e(GUEST_PTR *ppfn, GUEST_PTR *end); -void CDECL _set_app_type(_crt_app_type type); -int CDECL _set_fmode(int mode); -GUEST_PTR CDECL __p__commode(); -GUEST_PTR CDECL __p__fmode(); -int CDECL _crt_atexit(_PVFV func); -int CDECL _configure_narrow_argv(_crt_argv_mode mode); -_invalid_parameter_handler CDECL _set_invalid_parameter_handler(_invalid_parameter_handler newHandler); -int CDECL _controlfp_s(unsigned int *currentControl, unsigned int newControl, unsigned int mask); -int CDECL _configthreadlocale(int per_thread_locale_type); -int CDECL _initialize_narrow_environment(); -int CDECL _set_new_mode(int newhandlermode); -GUEST_PTR CDECL _get_initial_narrow_environment(); -GUEST_PTR CDECL __p__environ(); -GUEST_PTR CDECL __p___argv(); -GUEST_PTR CDECL __p___argc(); -SIZE_T CDECL strlen(const char *str); -int CDECL strcmp(const char *lhs, const char *rhs); -int CDECL strncmp(const char *lhs, const char *rhs, SIZE_T count); -char *CDECL strcpy(char *dest, const char *src); -char *CDECL strncpy(char *dest, const char *src, SIZE_T count); -const char *CDECL strrchr(const char *str, int ch); -void *CDECL malloc(SIZE_T size); -void *CDECL calloc(SIZE_T count, SIZE_T size); -void *CDECL realloc(void *ptr, SIZE_T newSize); -void CDECL free(void *ptr); -void *CDECL memcpy(void *dest, const void *src, SIZE_T count); -void *CDECL memmove(void *dest, const void *src, SIZE_T count); -void *CDECL memset(void *dest, int ch, SIZE_T count); -int CDECL memcmp(const void *lhs, const void *rhs, SIZE_T count); -int CDECL __setusermatherr(void *handler); -int CDECL _initialize_onexit_table(_onexit_table_t *table); -int CDECL _register_onexit_function(_onexit_table_t *table, _onexit_t func); -int CDECL _execute_onexit_table(_onexit_table_t *table); -void CDECL exit(int status); -void CDECL _cexit(); -void CDECL _exit(int status); -void CDECL abort(); -signal_handler CDECL signal(int signum, signal_handler handler); -void *CDECL __acrt_iob_func(unsigned int index); -#ifndef __x86_64__ // TODO -int CDECL_NO_CONV __stdio_common_vfprintf(ULONGLONG options, _FILE *stream, const char *format, void *locale, - va_list args); -int CDECL_NO_CONV __stdio_common_vsprintf(ULONGLONG options, char *buffer, SIZE_T len, const char *format, - void *locale, va_list args); -#endif -void CDECL qsort(void *base, SIZE_T num, SIZE_T size, sort_compare compare); -int CDECL puts(const char *str); - -} // namespace crt diff --git a/dll/kernel32/handleapi.cpp b/dll/kernel32/handleapi.cpp index 7ab07d2..75cf7de 100644 --- a/dll/kernel32/handleapi.cpp +++ b/dll/kernel32/handleapi.cpp @@ -39,7 +39,7 @@ BOOL WINAPI DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, H auto &handles = wibo::handles(); if (isPseudoCurrentProcessHandle(hSourceHandle)) { - auto po = make_pin(getpid(), -1); + auto po = make_pin(getpid(), -1, false); auto handle = handles.alloc(std::move(po), 0, 0); DEBUG_LOG("DuplicateHandle: created process handle for current process -> %p\n", handle); *lpTargetHandle = handle; diff --git a/dll/kernel32/interlockedapi.cpp b/dll/kernel32/interlockedapi.cpp index f54f66c..5d79729 100644 --- a/dll/kernel32/interlockedapi.cpp +++ b/dll/kernel32/interlockedapi.cpp @@ -3,7 +3,6 @@ #include "common.h" #include "context.h" -#include #include namespace kernel32 { @@ -11,22 +10,22 @@ namespace kernel32 { LONG WINAPI InterlockedIncrement(LONG volatile *Addend) { HOST_CONTEXT_GUARD(); VERBOSE_LOG("InterlockedIncrement(%p)\n", Addend); - std::atomic_ref a(*const_cast(Addend)); - return a.fetch_add(1, std::memory_order_seq_cst) + 1; + auto *ptr = const_cast(Addend); + return __atomic_add_fetch(ptr, 1, __ATOMIC_SEQ_CST); } LONG WINAPI InterlockedDecrement(LONG volatile *Addend) { HOST_CONTEXT_GUARD(); VERBOSE_LOG("InterlockedDecrement(%p)\n", Addend); - std::atomic_ref a(*const_cast(Addend)); - return a.fetch_sub(1, std::memory_order_seq_cst) - 1; + auto *ptr = const_cast(Addend); + return __atomic_sub_fetch(ptr, 1, __ATOMIC_SEQ_CST); } LONG WINAPI InterlockedExchange(LONG volatile *Target, LONG Value) { HOST_CONTEXT_GUARD(); VERBOSE_LOG("InterlockedExchange(%p, %ld)\n", Target, static_cast(Value)); - std::atomic_ref a(*const_cast(Target)); - return a.exchange(Value, std::memory_order_seq_cst); + auto *ptr = const_cast(Target); + return __atomic_exchange_n(ptr, Value, __ATOMIC_SEQ_CST); } LONG WINAPI InterlockedCompareExchange(LONG volatile *Destination, LONG Exchange, LONG Comperand) { @@ -34,9 +33,9 @@ LONG WINAPI InterlockedCompareExchange(LONG volatile *Destination, LONG Exchange VERBOSE_LOG("InterlockedCompareExchange(%p, %ld, %ld)\n", Destination, static_cast(Exchange), static_cast(Comperand)); - std::atomic_ref a(*const_cast(Destination)); + auto *ptr = const_cast(Destination); LONG expected = Comperand; - a.compare_exchange_strong(expected, Exchange, std::memory_order_seq_cst); + __atomic_compare_exchange_n(ptr, &expected, Exchange, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } diff --git a/dll/kernel32/internal.h b/dll/kernel32/internal.h index 877b8a8..eb6b412 100644 --- a/dll/kernel32/internal.h +++ b/dll/kernel32/internal.h @@ -64,8 +64,10 @@ struct ProcessObject final : WaitableObject { int pidfd; DWORD exitCode = STILL_ACTIVE; bool forcedExitCode = false; + bool waitable = true; - explicit ProcessObject(pid_t pid, int pidfd) : WaitableObject(kType), pid(pid), pidfd(pidfd) {} + explicit ProcessObject(pid_t pid, int pidfd, bool waitable = true) + : WaitableObject(kType), pid(pid), pidfd(pidfd), waitable(waitable) {} ~ProcessObject() override { if (pidfd != -1) { @@ -75,6 +77,12 @@ struct ProcessObject final : WaitableObject { } }; +#ifdef __linux__ +constexpr pthread_t pthread_null = 0; +#else +constexpr pthread_t pthread_null = nullptr; +#endif + struct ThreadObject final : WaitableObject { static constexpr ObjectType kType = ObjectType::Thread; @@ -83,7 +91,7 @@ struct ThreadObject final : WaitableObject { unsigned int suspendCount = 0; TEB *tib = nullptr; - explicit ThreadObject(pthread_t thread) : WaitableObject(kType), thread(thread) {} + explicit ThreadObject(pthread_t thread = pthread_null) : WaitableObject(kType), thread(thread) {} ~ThreadObject() override { // Threads are detached at creation; we can safely drop diff --git a/dll/kernel32/namedpipeapi.cpp b/dll/kernel32/namedpipeapi.cpp index c08e5eb..039c2ac 100644 --- a/dll/kernel32/namedpipeapi.cpp +++ b/dll/kernel32/namedpipeapi.cpp @@ -368,11 +368,13 @@ BOOL WINAPI CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBU configureInheritability(pipeFds[0], inheritHandles); configureInheritability(pipeFds[1], inheritHandles); +#ifdef __linux__ if (nSize != 0) { // Best-effort adjustment; ignore failures as recommended by docs. fcntl(pipeFds[0], F_SETPIPE_SZ, static_cast(nSize)); fcntl(pipeFds[1], F_SETPIPE_SZ, static_cast(nSize)); } +#endif auto readObj = make_pin(pipeFds[0]); readObj->shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE; @@ -384,8 +386,8 @@ BOOL WINAPI CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBU } HANDLE WINAPI CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances, - DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut, - LPSECURITY_ATTRIBUTES lpSecurityAttributes) { + DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut, + LPSECURITY_ATTRIBUTES lpSecurityAttributes) { HOST_CONTEXT_GUARD(); DEBUG_LOG("CreateNamedPipeA(%s, 0x%08x, 0x%08x, %u, %u, %u, %u, %p)\n", lpName ? lpName : "(null)", dwOpenMode, dwPipeMode, nMaxInstances, nOutBufferSize, nInBufferSize, nDefaultTimeOut, lpSecurityAttributes); @@ -479,15 +481,19 @@ HANDLE WINAPI CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode if (accessMode == PIPE_ACCESS_INBOUND) { serverFd = fds[0]; companionFd = fds[1]; +#ifdef __linux__ if (nInBufferSize != 0) { fcntl(serverFd, F_SETPIPE_SZ, static_cast(nInBufferSize)); } +#endif } else { serverFd = fds[1]; companionFd = fds[0]; +#ifdef __linux__ if (nOutBufferSize != 0) { fcntl(serverFd, F_SETPIPE_SZ, static_cast(nOutBufferSize)); } +#endif } } diff --git a/dll/kernel32/processenv.cpp b/dll/kernel32/processenv.cpp index 1c9d5d9..add02d9 100644 --- a/dll/kernel32/processenv.cpp +++ b/dll/kernel32/processenv.cpp @@ -15,6 +15,10 @@ #include #include +#ifdef __APPLE__ +extern char **environ; +#endif + namespace { GUEST_PTR g_commandLineA = GUEST_NULL; diff --git a/dll/kernel32/processthreadsapi.cpp b/dll/kernel32/processthreadsapi.cpp index 57de67f..9121e47 100644 --- a/dll/kernel32/processthreadsapi.cpp +++ b/dll/kernel32/processthreadsapi.cpp @@ -196,7 +196,11 @@ DWORD WINAPI GetCurrentProcessId() { DWORD WINAPI GetCurrentThreadId() { HOST_CONTEXT_GUARD(); pthread_t thread = pthread_self(); +#ifdef __linux__ const auto threadId = static_cast(thread); +#else + const auto threadId = static_cast(reinterpret_cast(thread)); +#endif DEBUG_LOG("GetCurrentThreadId() -> %u\n", threadId); return threadId; } @@ -322,10 +326,25 @@ BOOL WINAPI TerminateProcess(HANDLE hProcess, UINT uExitCode) { if (process->signaled) { return TRUE; } - if (syscall(SYS_pidfd_send_signal, process->pidfd, SIGKILL, nullptr, 0) != 0) { - int err = errno; - DEBUG_LOG("TerminateProcess: pidfd_send_signal(%d) failed: %s\n", process->pidfd, strerror(err)); - switch (err) { + int killResult = 0; +#if defined(__linux__) + if (process->pidfd != -1) { + if (syscall(SYS_pidfd_send_signal, process->pidfd, SIGKILL, nullptr, 0) != 0) { + killResult = errno; + DEBUG_LOG("TerminateProcess: pidfd_send_signal(%d) failed: %s\n", process->pidfd, strerror(killResult)); + } + } else if (kill(process->pid, SIGKILL) != 0) { + killResult = errno; + DEBUG_LOG("TerminateProcess: kill(%d) failed: %s\n", process->pid, strerror(killResult)); + } +#else + if (kill(process->pid, SIGKILL) != 0) { + killResult = errno; + DEBUG_LOG("TerminateProcess: kill(%d) failed: %s\n", process->pid, strerror(killResult)); + } +#endif + if (killResult != 0) { + switch (killResult) { case ESRCH: case EPERM: setLastError(ERROR_ACCESS_DENIED); @@ -467,7 +486,7 @@ HANDLE WINAPI CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwSt return NO_HANDLE; } - Pin obj = make_pin(0); // tid set during pthread_create + Pin obj = make_pin(); // tid set during pthread_create if ((dwCreationFlags & CREATE_SUSPENDED) != 0) { obj->suspendCount = 1; } @@ -584,14 +603,16 @@ BOOL WINAPI GetThreadTimes(HANDLE hThread, FILETIME *lpCreationTime, FILETIME *l lpExitTime->dwHighDateTime = 0; } - struct rusage usage{}; +#ifdef __linux__ + struct rusage usage {}; if (getrusage(RUSAGE_THREAD, &usage) == 0) { *lpKernelTime = fileTimeFromTimeval(usage.ru_stime); *lpUserTime = fileTimeFromTimeval(usage.ru_utime); return TRUE; } +#endif - struct timespec cpuTime{}; + struct timespec cpuTime {}; if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &cpuTime) == 0) { *lpKernelTime = fileTimeFromDuration(0); *lpUserTime = fileTimeFromTimespec(cpuTime); diff --git a/dll/kernel32/synchapi.cpp b/dll/kernel32/synchapi.cpp index ebe22f9..cd9246b 100644 --- a/dll/kernel32/synchapi.cpp +++ b/dll/kernel32/synchapi.cpp @@ -437,10 +437,13 @@ DWORD WINAPI WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) { case ObjectType::Process: { auto po = std::move(obj).downcast(); std::unique_lock lk(po->m); - if (po->pidfd == -1) { + if (!po->signaled && !po->waitable) { // Windows actually allows you to wait on your own process, but why bother? return WAIT_TIMEOUT; } + if (po->signaled) { + return WAIT_OBJECT_0; + } bool ok = doWait(lk, po->cv, [&] { return po->signaled; }); return ok ? WAIT_OBJECT_0 : WAIT_TIMEOUT; } diff --git a/dll/kernel32/wincon.cpp b/dll/kernel32/wincon.cpp index a580014..95baafb 100644 --- a/dll/kernel32/wincon.cpp +++ b/dll/kernel32/wincon.cpp @@ -62,7 +62,7 @@ BOOL WINAPI GetConsoleScreenBufferInfo(HANDLE hConsoleOutput, CONSOLE_SCREEN_BUF } BOOL WINAPI WriteConsoleW(HANDLE hConsoleOutput, LPCVOID lpBuffer, DWORD nNumberOfCharsToWrite, - LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved) { + LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved) { HOST_CONTEXT_GUARD(); DEBUG_LOG("WriteConsoleW(%p, %p, %u, %p, %p)\n", hConsoleOutput, lpBuffer, nNumberOfCharsToWrite, lpNumberOfCharsWritten, lpReserved); @@ -110,7 +110,7 @@ DWORD WINAPI GetConsoleTitleW(LPWSTR lpConsoleTitle, DWORD nSize) { } BOOL WINAPI PeekConsoleInputA(HANDLE hConsoleInput, INPUT_RECORD *lpBuffer, DWORD nLength, - LPDWORD lpNumberOfEventsRead) { + LPDWORD lpNumberOfEventsRead) { HOST_CONTEXT_GUARD(); DEBUG_LOG("STUB: PeekConsoleInputA(%p, %p, %u)\n", hConsoleInput, lpBuffer, nLength); (void)hConsoleInput; @@ -123,7 +123,7 @@ BOOL WINAPI PeekConsoleInputA(HANDLE hConsoleInput, INPUT_RECORD *lpBuffer, DWOR } BOOL WINAPI ReadConsoleInputA(HANDLE hConsoleInput, INPUT_RECORD *lpBuffer, DWORD nLength, - LPDWORD lpNumberOfEventsRead) { + LPDWORD lpNumberOfEventsRead) { HOST_CONTEXT_GUARD(); DEBUG_LOG("STUB: ReadConsoleInputA(%p, %p, %u)\n", hConsoleInput, lpBuffer, nLength); (void)hConsoleInput; @@ -135,4 +135,11 @@ BOOL WINAPI ReadConsoleInputA(HANDLE hConsoleInput, INPUT_RECORD *lpBuffer, DWOR return TRUE; } +BOOL WINAPI VerifyConsoleIoHandle(HANDLE handle) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("STUB: VerifyConsoleIoHandle(%p)\n", handle); + (void)handle; + return FALSE; +} + } // namespace kernel32 diff --git a/dll/kernel32/wincon.h b/dll/kernel32/wincon.h index 683030e..818c968 100644 --- a/dll/kernel32/wincon.h +++ b/dll/kernel32/wincon.h @@ -42,5 +42,6 @@ BOOL WINAPI PeekConsoleInputA(HANDLE hConsoleInput, INPUT_RECORD *lpBuffer, DWOR LPDWORD lpNumberOfEventsRead); BOOL WINAPI ReadConsoleInputA(HANDLE hConsoleInput, INPUT_RECORD *lpBuffer, DWORD nLength, LPDWORD lpNumberOfEventsRead); +BOOL WINAPI VerifyConsoleIoHandle(HANDLE handle); } // namespace kernel32 diff --git a/dll/msvcrt.cpp b/dll/msvcrt.cpp deleted file mode 100644 index 531fed7..0000000 --- a/dll/msvcrt.cpp +++ /dev/null @@ -1,3041 +0,0 @@ -#include "msvcrt.h" - -#include "common.h" -#include "context.h" -#include "files.h" -#include "heap.h" -#include "kernel32/internal.h" -#include "modules.h" -#include "msvcrt_trampolines.h" -#include "processes.h" -#include "strutil.h" -#include "types.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef O_BINARY -#define O_BINARY 0 -#endif - -namespace { - -std::mutex g_fileMutex; -std::unordered_map g_files; - -FILE *mapToHostFile(_FILE *file) { - if (!file) - return nullptr; - switch (file->_file) { - case -1: - return nullptr; - case STDIN_FILENO: - return stdin; - case STDOUT_FILENO: - return stdout; - case STDERR_FILENO: - return stderr; - default: - return g_files[file->_file]; - } -} - -_FILE *mapToGuestFile(FILE *file) { - if (!file) - return nullptr; - int fd = fileno(file); - _FILE *out = new (wibo::heap::guestMalloc(sizeof(_FILE))) _FILE(fd); - std::lock_guard lock(g_fileMutex); - g_files[fd] = file; - return out; -} - -int closeGuestFile(_FILE *file) { - if (!file) - return -1; - int fd = file->_file; - { - std::lock_guard lock(g_fileMutex); - g_files.erase(fd); - } - int ret = close(fd); - wibo::heap::guestFree(file); - return ret; -} - -} // namespace - -namespace msvcrt { - int _commode; - int _fmode; - GUEST_PTR __initenv = GUEST_NULL; - GUEST_PTR __winitenv = GUEST_NULL; - GUEST_PTR _wpgmptr = GUEST_NULL; - GUEST_PTR _pgmptr = GUEST_NULL; - int __mb_cur_max = 1; - _FILE _iob[_IOB_ENTRIES] = {_FILE{STDOUT_FILENO}, _FILE{STDERR_FILENO}, _FILE{STDIN_FILENO}}; - - constexpr int MB_CP_ANSI = -3; - constexpr int MB_CP_OEM = -2; - constexpr int MB_CP_LOCALE = -4; - constexpr int MB_CP_SBCS = 0; - constexpr int MB_CP_UTF8 = -5; - - static int mbCodePageSetting = MB_CP_ANSI; - static unsigned int floatingPointControlWord = 0x0009001F; // _CW_DEFAULT for x87 - static std::array mbctypeTable; - static std::vector<_onexit_t> onExitFuncs; - - constexpr unsigned short PCTYPE_UPPER = 0x0001; - constexpr unsigned short PCTYPE_LOWER = 0x0002; - constexpr unsigned short PCTYPE_DIGIT = 0x0004; - constexpr unsigned short PCTYPE_SPACE = 0x0008; - constexpr unsigned short PCTYPE_PUNCT = 0x0010; - constexpr unsigned short PCTYPE_CONTROL = 0x0020; - constexpr unsigned short PCTYPE_BLANK = 0x0040; - constexpr unsigned short PCTYPE_HEX = 0x0080; - constexpr unsigned short PCTYPE_LEADBYTE = 0x8000; - - constexpr unsigned char _MS = 0x01; - constexpr unsigned char _MP = 0x02; - constexpr unsigned char _M1 = 0x04; - constexpr unsigned char _M2 = 0x08; - constexpr unsigned char _SBUP = 0x10; - constexpr unsigned char _SBLOW = 0x20; - - using ByteRange = std::pair; - - template - void setMbctypeFlag(unsigned char flag, const std::array &ranges) { - for (const auto &range : ranges) { - for (int value = range.first; value <= range.second; ++value) { - mbctypeTable[static_cast(value)] |= flag; - } - } - } - - int mbCurMaxForCodePage(int codepage); - - void updateMbctypeForCodePage(int codepage) { - mbctypeTable.fill(0); - - switch (codepage) { - case 932: { - const std::array lead{{ByteRange{0x81, 0x9F}, ByteRange{0xE0, 0xFC}}}; - const std::array trail{{ByteRange{0x40, 0x7E}, ByteRange{0x80, 0xFC}}}; - setMbctypeFlag(_M1, lead); - setMbctypeFlag(_M2, trail); - break; - } - case 936: { - const std::array lead{{ByteRange{0x81, 0xFE}}}; - const std::array trail{{ByteRange{0x40, 0xFE}}}; - setMbctypeFlag(_M1, lead); - setMbctypeFlag(_M2, trail); - break; - } - case 949: { - const std::array lead{{ByteRange{0x81, 0xFE}}}; - const std::array trail{{ByteRange{0x41, 0x5A}, ByteRange{0x61, 0x7A}, ByteRange{0x81, 0xFE}}}; - setMbctypeFlag(_M1, lead); - setMbctypeFlag(_M2, trail); - break; - } - case 950: { - const std::array lead{{ByteRange{0x81, 0xFE}}}; - const std::array trail{{ByteRange{0x40, 0x7E}, ByteRange{0xA1, 0xFE}}}; - setMbctypeFlag(_M1, lead); - setMbctypeFlag(_M2, trail); - break; - } - case 1361: { - const std::array lead{{ByteRange{0x81, 0xFE}}}; - const std::array trail{{ByteRange{0x31, 0x7E}, ByteRange{0x81, 0xFE}}}; - setMbctypeFlag(_M1, lead); - setMbctypeFlag(_M2, trail); - break; - } - default: - break; - } - } - - std::once_flag &mbctypeInitFlag() { - static std::once_flag flag; - return flag; - } - - void ensureMbctypeInitialized() { - std::call_once(mbctypeInitFlag(), []() { - updateMbctypeForCodePage(mbCodePageSetting); - __mb_cur_max = mbCurMaxForCodePage(mbCodePageSetting); - }); - } - - bool isLeadByte(unsigned char byte) { - ensureMbctypeInitialized(); - return (mbctypeTable[byte] & _M1) != 0; - } - - bool isTrailByte(unsigned char byte) { - ensureMbctypeInitialized(); - return (mbctypeTable[byte] & _M2) != 0; - } - - std::once_flag &pctypeInitFlag() { - static std::once_flag flag; - return flag; - } - - std::array &pctypeTable() { - static std::array table = {}; - std::call_once(pctypeInitFlag(), []() { - table[0] = 0; - for (int i = 0; i < 256; ++i) { - unsigned short flags = 0; - unsigned char ch = static_cast(i); - if (std::isupper(ch)) flags |= PCTYPE_UPPER; - if (std::islower(ch)) flags |= PCTYPE_LOWER; - if (std::isdigit(ch)) flags |= PCTYPE_DIGIT; - if (std::isspace(ch)) flags |= PCTYPE_SPACE; - if (std::iscntrl(ch)) flags |= PCTYPE_CONTROL; - if (ch == ' ' || ch == '\t') flags |= PCTYPE_BLANK; - if (std::ispunct(ch)) flags |= PCTYPE_PUNCT; - if (std::isxdigit(ch)) flags |= PCTYPE_HEX; - if (isLeadByte(ch)) flags |= PCTYPE_LEADBYTE; - table[i + 1] = flags; - } - }); - return table; - } - - int CDECL _except_handler4_common(void *, void *, void *, void *); - std::vector &putenvStorage() { - static std::vector storage; - return storage; - } - - _FILE *standardIobEntries() { - return _iob; - } - - _FILE *CDECL __iob_func() { - HOST_CONTEXT_GUARD(); - return _iob; - } - - _FILE *CDECL __p__iob() { - HOST_CONTEXT_GUARD(); - return _iob; - } - - void CDECL setbuf(_FILE *stream, char *buffer) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("setbuf(%p, %p)\n", stream, buffer); - if (!stream) { - return; - } - FILE *host = mapToHostFile(stream); - if (buffer) { - setvbuf(host, buffer, _IOFBF, BUFSIZ); - } else { - setvbuf(host, nullptr, _IONBF, 0); - } - } - - void CDECL _splitpath(const char *path, char *drive, char *dir, char *fname, char *ext) { - HOST_CONTEXT_GUARD(); - if (drive) drive[0] = '\0'; - if (dir) dir[0] = '\0'; - if (fname) fname[0] = '\0'; - if (ext) ext[0] = '\0'; - - if (!path) { - errno = EINVAL; - return; - } - - const char *cursor = path; - if (cursor[0] && cursor[1] == ':') { - if (drive) { - drive[0] = cursor[0]; - drive[1] = ':'; - drive[2] = '\0'; - } - cursor += 2; - } - - const char *dirEnd = nullptr; - for (const char *scan = cursor; *scan; ++scan) { - if (*scan == '/' || *scan == '\\') { - dirEnd = scan + 1; - } - } - - const char *filename = cursor; - if (dirEnd) { - if (dir) { - SIZE_T dirLen = static_cast(dirEnd - cursor); - std::memcpy(dir, cursor, dirLen); - dir[dirLen] = '\0'; - } - filename = dirEnd; - } - - const char *extStart = nullptr; - for (const char *scan = filename; *scan; ++scan) { - if (*scan == '.') { - extStart = scan; - } - } - - const char *nameEnd = extStart ? extStart : filename + std::strlen(filename); - - if (fname) { - auto nameLen = static_cast(nameEnd - filename); - std::memcpy(fname, filename, nameLen); - fname[nameLen] = '\0'; - } - if (ext && extStart) { - std::strcpy(ext, extStart); - } - - DEBUG_LOG("_splitpath(%s) -> drive='%s' dir='%s' fname='%s' ext='%s'\n", - path, - drive ? drive : "", - dir ? dir : "", - fname ? fname : "", - ext ? ext : ""); - } - - int CDECL _fileno(_FILE *stream) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_fileno(%p)\n", stream); - if (!stream) { - errno = EINVAL; - return -1; - } - FILE *host = mapToHostFile(stream); - return ::fileno(host); - } - - int mbCurMaxForCodePage(int codepage) { - switch (codepage) { - case MB_CP_SBCS: - case MB_CP_ANSI: - case MB_CP_OEM: - case MB_CP_LOCALE: - return 1; - case MB_CP_UTF8: - case 65001: - return 4; - case 932: - case 936: - case 949: - case 950: - case 1361: - return 2; - default: - return 1; - } - } - - void refreshMbCurMax() { - ensureMbctypeInitialized(); - __mb_cur_max = mbCurMaxForCodePage(mbCodePageSetting); - } - - int CDECL _getmbcp() { - HOST_CONTEXT_GUARD(); - ensureMbctypeInitialized(); - DEBUG_LOG("_getmbcp() -> %d\n", mbCodePageSetting); - return mbCodePageSetting; - } - - GUEST_PTR CDECL __p___mb_cur_max() { - HOST_CONTEXT_GUARD(); - ensureMbctypeInitialized(); - DEBUG_LOG("__p___mb_cur_max() -> %u\n", __mb_cur_max); - return toGuestPtr(&__mb_cur_max); - } - - int CDECL _setmbcp(int codepage) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_setmbcp(%d)\n", codepage); - ensureMbctypeInitialized(); - - switch (codepage) { - case MB_CP_SBCS: - case MB_CP_UTF8: - case MB_CP_OEM: - case MB_CP_ANSI: - case MB_CP_LOCALE: - break; - default: - if (codepage < 0) { - errno = EINVAL; - return -1; - } - break; - } - - mbCodePageSetting = codepage; - updateMbctypeForCodePage(codepage); - __mb_cur_max = mbCurMaxForCodePage(codepage); - return 0; - } - - GUEST_PTR CDECL __p__mbctype() { - HOST_CONTEXT_GUARD(); - ensureMbctypeInitialized(); - DEBUG_LOG("__p__mbctype() -> %p\n", mbctypeTable.data()); - return toGuestPtr(mbctypeTable.data()); - } - - GUEST_PTR CDECL __p__pctype() { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("__p__pctype()\n"); - static unsigned short *pointer = nullptr; - pointer = pctypeTable().data() + 1; - return toGuestPtr(&pointer); - } - - int CDECL _isctype(int ch, int mask) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_isctype(%d, %d)\n", ch, mask); - if (ch == EOF) { - return 0; - } - if (ch < 0 || ch > 255) { - return 0; - } - return (pctypeTable()[static_cast(ch) + 1] & mask) != 0; - } - - namespace { - constexpr SIZE_T LOCK_TABLE_SIZE = 64; - std::array &lockTable() { - static std::array table; - return table; - } - - std::string normalizeEnvStringForWindows(const char *src) { - if (!src) { - return std::string(); - } - std::string entry(src); - auto pos = entry.find('='); - if (pos == std::string::npos) { - return entry; - } - std::string name = entry.substr(0, pos); - std::string value = entry.substr(pos + 1); - if (strcasecmp(name.c_str(), "PATH") == 0) { - std::string converted = files::hostPathListToWindows(value); - std::string result = converted.empty() ? value : converted; - std::string exeDir; - if (!wibo::guestExecutablePath.empty()) { - auto exePath = wibo::guestExecutablePath.parent_path(); - if (!exePath.empty()) { - exeDir = files::pathToWindows(exePath); - } - } - if (!exeDir.empty()) { - std::string loweredResult = stringToLower(result); - std::string loweredExe = stringToLower(exeDir); - bool present = false; - SIZE_T start = 0; - while (start <= loweredResult.size()) { - SIZE_T end = loweredResult.find(';', start); - if (end == std::string::npos) { - end = loweredResult.size(); - } - if (loweredResult.substr(start, end - start) == loweredExe) { - present = true; - break; - } - if (end == loweredResult.size()) { - break; - } - start = end + 1; - } - if (!present) { - if (!result.empty() && result.back() != ';') { - result.push_back(';'); - } - result += exeDir; - } - } - entry = name + "=" + result; - } - return entry; - } - - template - struct StringListStorage { - std::vector> strings; - wibo::heap::guest_ptr pointers; - - template - GUEST_PTR *assign(char **source, Converter convert) { - if (!source) { - strings.clear(); - pointers.reset(); - return nullptr; - } - - SIZE_T count = 0; - while (source[count]) { - ++count; - } - - strings.clear(); - strings.reserve(count); - pointers = wibo::heap::make_guest_unique(count + 1); - - for (SIZE_T i = 0; i < count; ++i) { - auto data = convert(source[i]); - auto buffer = wibo::heap::make_guest_unique(data.size()); - std::copy(data.begin(), data.end(), buffer.get()); - CharT *raw = buffer.get(); - strings.emplace_back(std::move(buffer)); - pointers[i] = toGuestPtr(raw); - } - - pointers[count] = GUEST_NULL; - return pointers.get(); - } - }; - - std::vector copyNarrowString(const char *src) { - std::string normalized = normalizeEnvStringForWindows(src); - SIZE_T len = normalized.size(); - std::vector result(len + 1); - if (len > 0) { - std::memcpy(result.data(), normalized.data(), len); - } - result[len] = '\0'; - return result; - } - - std::vector copyWideString(const char *src) { - std::string normalized = normalizeEnvStringForWindows(src); - return stringToWideString(normalized.c_str()); - } - - template - // NOLINTNEXTLINE(readability-non-const-parameter) - int getMainArgsCommon(int *argcOut, GUEST_PTR *argvOut, GUEST_PTR *envOut, Converter convert) { - if (argcOut) { - *argcOut = wibo::argc; - } - - static StringListStorage argvStorage; - static StringListStorage envStorage; - - if (argvOut) { - *argvOut = toGuestPtr(argvStorage.assign(wibo::argv, convert)); - } - - GUEST_PTR *envData = envStorage.assign(environ, convert); - if (envOut) { - *envOut = toGuestPtr(envData); - } - - if constexpr (std::is_same_v) { - __winitenv = toGuestPtr(envData); - } else if constexpr (std::is_same_v) { - __initenv = toGuestPtr(envData); - } - - return 0; - } - - template - SIZE_T envStringLength(const CharT *str) { - if (!str) { - return 0; - } - if constexpr (std::is_same_v) { - return std::strlen(str); - } else { - return wstrlen(str); - } - } - - template - int envStringCompare(const CharT *lhs, const CharT *rhs, SIZE_T count) { - if constexpr (std::is_same_v) { - return std::strncmp(lhs, rhs, count); - } else { - return wstrncmp(lhs, rhs, count); - } - } - - template - struct EnvLookupResult { - const CharT *value; - SIZE_T length; - }; - - template - std::optional> findEnvironmentValue(CharT **env, const CharT *varname) { - if (!env || !varname) { - return std::nullopt; - } - - SIZE_T nameLength = envStringLength(varname); - if (nameLength == 0) { - return std::nullopt; - } - - for (CharT **cursor = env; *cursor; ++cursor) { - CharT *entry = *cursor; - if (envStringCompare(entry, varname, nameLength) == 0 && entry[nameLength] == static_cast('=')) { - const CharT *value = entry + nameLength + 1; - return EnvLookupResult{value, envStringLength(value)}; - } - } - - return std::nullopt; - } - - uint16_t **ensureWideEnvironment() { - if (!__winitenv) { - getMainArgsCommon(nullptr, nullptr, nullptr, copyWideString); - } - return reinterpret_cast(__winitenv); - } - } // namespace - - // Stub because we're only ever a console application - void CDECL __set_app_type(int at) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("STUB: __set_app_type(%d)\n", at); - (void)at; - } - - GUEST_PTR CDECL __p__fmode() { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("__p__fmode() -> %p\n", &_fmode); - return toGuestPtr(&_fmode); - } - - GUEST_PTR CDECL __p__commode() { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("__p__commode() -> %p\n", &_commode); - return toGuestPtr(&_commode); - } - - - void CDECL _initterm(GUEST_PTR *ppfn, GUEST_PTR *end) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_initterm(%p, %p)\n", ppfn, end); - do { - if (GUEST_PTR pfn = *++ppfn) { - DEBUG_LOG("-> calling %p\n", pfn); - auto fn = reinterpret_cast<_PVFV>(fromGuestPtr(pfn)); - call__PVFV(fn); - } - } while (ppfn < end); - } - - int CDECL _initterm_e(GUEST_PTR *ppfn, GUEST_PTR *end) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_initterm_e(%p, %p)\n", ppfn, end); - do { - if (GUEST_PTR pfn = *++ppfn) { - DEBUG_LOG("-> calling %p\n", pfn); - auto fn = reinterpret_cast<_PIFV>(fromGuestPtr(pfn)); - int err = call__PIFV(fn); - if (err) - return err; - } - } while (ppfn < end); - - return 0; - } - - unsigned int CDECL _controlfp(unsigned int newControl, unsigned int mask) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_controlfp(newControl=%08x, mask=%08x)\n", newControl, mask); - unsigned int previous = floatingPointControlWord; - if (mask != 0) { - floatingPointControlWord = (floatingPointControlWord & ~mask) | (newControl & mask); - } - return previous; - } - - int CDECL _controlfp_s(unsigned int *currentControl, unsigned int newControl, unsigned int mask) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_controlfp_s(currentControl=%p, newControl=%08x, mask=%08x)\n", currentControl, newControl, mask); - if (mask != 0 && (mask & 0xFF000000) != 0) { - // Unsupported bits: match real CRT behaviour by ignoring but logging. - DEBUG_LOG("STUB: _controlfp_s unsupported mask bits %08x\n", mask); - } - if (mask != 0) { - floatingPointControlWord = (floatingPointControlWord & ~mask) | (newControl & mask); - } - if (currentControl) { - *currentControl = floatingPointControlWord; - } - return 0; - } - - _onexit_t CDECL _onexit(_onexit_t func) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_onexit(%p)\n", func); - if (!func) { - return nullptr; - } - onExitFuncs.push_back(func); - return func; - } - - // NOLINTNEXTLINE(readability-non-const-parameter) - int CDECL __wgetmainargs(int *wargc, GUEST_PTR *wargv, GUEST_PTR *wenv, int doWildcard, int *startInfo) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("__wgetmainargs(doWildcard=%d)\n", doWildcard); - (void)startInfo; - if (doWildcard) { - DEBUG_LOG("\tWildcard expansion is not implemented\n"); - } - - std::setlocale(LC_CTYPE, ""); - return getMainArgsCommon(wargc, wargv, wenv, copyWideString); - } - - // NOLINTNEXTLINE(readability-non-const-parameter) - int CDECL __getmainargs(int *argc, GUEST_PTR *argv, GUEST_PTR *env, int doWildcard, int *startInfo) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("__getmainargs(doWildcard=%d)\n", doWildcard); - (void)startInfo; - if (doWildcard) { - DEBUG_LOG("\tWildcard expansion is not implemented\n"); - } - return getMainArgsCommon(argc, argv, env, copyNarrowString); - } - - char* CDECL getenv(const char *varname){ - HOST_CONTEXT_GUARD(); - DEBUG_LOG("getenv(%s)\n", varname); - return std::getenv(varname); - } - - GUEST_PTR CDECL __p___initenv() { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("__p___initenv() -> %p\n", &__initenv); - return toGuestPtr(&__initenv); - } - - char* CDECL strcat(char *dest, const char *src) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("strcat(%p, %s)\n", dest, src); - return std::strcat(dest, src); - } - - char* CDECL strcpy(char *dest, const char *src) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("strcpy(%p, %s)\n", dest, src); - return std::strcpy(dest, src); - } - - int CDECL _access(const char *path, int mode) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_access(%s, %d)\n", path ? path : "(null)", mode); - if (!path) { - errno = EINVAL; - return -1; - } - - auto hostPath = files::pathFromWindows(path); - int flags = F_OK; - if (mode & 2) { - flags |= W_OK; - } - if (mode & 4) { - flags |= R_OK; - } - if (mode & 1) { - flags |= X_OK; - } - - if (::access(hostPath.c_str(), flags) == 0) { - return 0; - } - - return -1; - } - - int CDECL _ismbblead(unsigned int c) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_ismbblead(%d)\n", c); - if (c > 0xFF) { - return 0; - } - return isLeadByte(static_cast(c)) ? 1 : 0; - } - - int CDECL _ismbbtrail(unsigned int c) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_ismbbtrail(%d)\n", c); - if (c > 0xFF) { - return 0; - } - return isTrailByte(static_cast(c)) ? 1 : 0; - } - - int CDECL _ismbcspace(unsigned int c) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_ismbcspace(%d)\n", c); - if (c <= 0xFF) { - return std::isspace(static_cast(c)) ? 1 : 0; - } - - unsigned char lead = static_cast((c >> 8) & 0xFF); - unsigned char trail = static_cast(c & 0xFF); - if (isLeadByte(lead) && isTrailByte(trail)) { - // Treat known double-byte ideographic space as whitespace (CP932/936 etc.) - if ((lead == 0x81 && trail == 0x40) || (lead == 0xA1 && trail == 0xA1)) { - return 1; - } - } - return 0; - } - - void CDECL _mbccpy(unsigned char *dest, const unsigned char *src) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_mbccpy(%p, %s)\n", dest, src); - if (!dest || !src) { - return; - } - - dest[0] = src[0]; - if (isLeadByte(src[0]) && src[1]) { - dest[1] = src[1]; - } - } - - unsigned char* CDECL _mbsinc(const unsigned char *str) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_mbsinc(%p)\n", str); - if (!str) { - return nullptr; - } - if (*str == '\0') { - return const_cast(str); - } - if (isLeadByte(static_cast(*str)) && str[1] != '\0') { - return const_cast(str + 2); - } - return const_cast(str + 1); - } - - unsigned char* CDECL _mbsdec(const unsigned char *start, const unsigned char *current) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_mbsdec(%p, %p)\n", start, current); - if (!start || !current || current <= start) { - DEBUG_LOG("_mbsdec invalid args start=%p current=%p\n", start, current); - return nullptr; - } - - const unsigned char *iter = start; - const unsigned char *prev = nullptr; - while (iter < current) { - if (*iter == '\0') { - break; - } - prev = iter; - const unsigned char *next = _mbsinc(iter); - if (next >= current) { - break; - } - iter = next; - } - - if (!prev) { - LONG diff = static_cast(current - start); - DEBUG_LOG("_mbsdec fallback start=%p current=%p diff=%ld first-bytes=%02x %02x %02x %02x\n", - start, current, diff, - start ? start[0] : 0, start ? start[1] : 0, - start ? start[2] : 0, start ? start[3] : 0); - const unsigned char *fallback = current ? current - 1 : current; - if (diff >= 2 && start && start[0] == 0 && start[1] != 0) { - fallback = current - 2; - } - return const_cast(fallback); - } - return const_cast(prev); - } - - unsigned int CDECL _mbclen(const unsigned char *str) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_mbclen(%s)\n", str); - if (!str || *str == '\0') { - return 0; - } - return (isLeadByte(*str) && str[1] != '\0') ? 2U : 1U; - } - - int CDECL _mbscmp(const unsigned char *lhs, const unsigned char *rhs) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_mbscmp(%s, %s)\n", lhs, rhs); - if (!lhs || !rhs) { - return (lhs == rhs) ? 0 : (lhs ? 1 : -1); - } - return std::strcmp(reinterpret_cast(lhs), reinterpret_cast(rhs)); - } - - int CDECL _mbsicmp(const unsigned char *lhs, const unsigned char *rhs) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_mbsicmp(%s, %s)\n", lhs, rhs); - if (!lhs || !rhs) { - return (lhs == rhs) ? 0 : (lhs ? 1 : -1); - } - return strcasecmp(reinterpret_cast(lhs), reinterpret_cast(rhs)); - } - - unsigned char* CDECL _mbsstr(const unsigned char *haystack, const unsigned char *needle) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_mbsstr(%s, %s)\n", haystack, needle); - if (!haystack || !needle) { - return nullptr; - } - const char *result = std::strstr(reinterpret_cast(haystack), reinterpret_cast(needle)); - return result ? reinterpret_cast(const_cast(result)) : nullptr; - } - - unsigned char* CDECL _mbschr(const unsigned char *str, unsigned int ch) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_mbschr(%s, %d)\n", str, ch); - if (!str) { - return nullptr; - } - unsigned char target = static_cast(ch & 0xFF); - const char *result = std::strchr(reinterpret_cast(str), target); - return result ? reinterpret_cast(const_cast(result)) : nullptr; - } - - unsigned char* CDECL _mbsrchr(const unsigned char *str, unsigned int ch) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_mbsrchr(%s, %d)\n", str, ch); - if (!str) { - return nullptr; - } - unsigned char target = static_cast(ch & 0xFF); - const char *result = std::strrchr(reinterpret_cast(str), target); - return result ? reinterpret_cast(const_cast(result)) : nullptr; - } - - unsigned char* CDECL _mbslwr(unsigned char *str) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_mbslwr(%p)\n", str); - if (!str) { - return nullptr; - } - for (unsigned char *p = str; *p; ++p) { - *p = static_cast(std::tolower(*p)); - } - return str; - } - - unsigned char* CDECL _mbsupr(unsigned char *str) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_mbsupr(%p)\n", str); - if (!str) { - return nullptr; - } - for (unsigned char *p = str; *p; ++p) { - *p = static_cast(std::toupper(*p)); - } - return str; - } - - unsigned char *CDECL _mbsinc_l(const unsigned char *str, void *) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_mbsinc_l(%p)\n", str); - return _mbsinc(str); - } - - unsigned char *CDECL _mbsdec_l(const unsigned char *start, const unsigned char *current, void *locale) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_mbsdec_l(%p, %p, %p)\n", start, current, locale); - return _mbsdec(start, current); - } - - int CDECL _mbsncmp(const unsigned char *lhs, const unsigned char *rhs, SIZE_T count) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_mbsncmp(%s, %s, %zu)\n", lhs, rhs, count); - if (!lhs || !rhs) { - return (lhs == rhs) ? 0 : (lhs ? 1 : -1); - } - return std::strncmp(reinterpret_cast(lhs), reinterpret_cast(rhs), count); - } - - SIZE_T CDECL _mbsspn(const unsigned char *str, const unsigned char *set) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_mbsspn(%s, %s)\n", str, set); - if (!str || !set) { - return 0; - } - return std::strspn(reinterpret_cast(str), reinterpret_cast(set)); - } - - int CDECL _ismbcdigit(unsigned int ch) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_ismbcdigit(%d)\n", ch); - if (ch <= 0xFF) { - return std::isdigit(static_cast(ch)) ? 1 : 0; - } - return 0; - } - - int CDECL _stricmp(const char *lhs, const char *rhs) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_stricmp(%s, %s)\n", lhs, rhs); - if (!lhs || !rhs) { - return (lhs == rhs) ? 0 : (lhs ? 1 : -1); - } - return strcasecmp(lhs, rhs); - } - - int CDECL _strnicmp(const char *lhs, const char *rhs, SIZE_T count) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_strnicmp(%s, %s, %zu)\n", lhs, rhs, count); - if (!lhs || !rhs) { - return (lhs == rhs) ? 0 : (lhs ? 1 : -1); - } - return strncasecmp(lhs, rhs, count); - } - - int CDECL _memicmp(const void *lhs, const void *rhs, SIZE_T count) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_memicmp(%p, %p, %zu)\n", lhs, rhs, count); - if (!lhs || !rhs) { - return (lhs == rhs) ? 0 : (lhs ? 1 : -1); - } - const auto *a = static_cast(lhs); - const auto *b = static_cast(rhs); - for (SIZE_T i = 0; i < count; ++i) { - const auto ca = static_cast(std::tolower(a[i])); - const auto cb = static_cast(std::tolower(b[i])); - if (ca != cb) { - return (ca < cb) ? -1 : 1; - } - } - return 0; - } - - int CDECL_NO_CONV _vsnprintf(char *buffer, SIZE_T count, const char *format, va_list args) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_vsnprintf(%p, %zu, %s, %p)\n", buffer, count, format, args); - if (!buffer || !format) { - errno = EINVAL; - return -1; - } - int result = vsnprintf(buffer, count, format, args); - if (result < 0) { - return -1; - } - if (static_cast(result) >= count) { - buffer[count ? count - 1 : 0] = '\0'; - return -1; - } - return result; - } - - int CDECL_NO_CONV _snprintf(char *buffer, SIZE_T count, const char *format, ...) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_snprintf(%p, %zu, %s, ...)\n", buffer, count, format); - va_list args; - va_start(args, format); - int result = _vsnprintf(buffer, count, format, args); - va_end(args); - return result; - } - - int CDECL_NO_CONV sprintf(char *buffer, const char *format, ...) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("sprintf(%p, %s, ...)\n", buffer, format); - va_list args; - va_start(args, format); - int result = ::vsprintf(buffer, format, args); - va_end(args); - return result; - } - - int CDECL_NO_CONV printf(const char *format, ...) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("printf(%s, ...)\n", format); - va_list args; - va_start(args, format); - int result = ::vprintf(format, args); - va_end(args); - return result; - } - - int CDECL_NO_CONV sscanf(const char *buffer, const char *format, ...) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("sscanf(%p, %p, ...)\n", buffer, format); - va_list args; - va_start(args, format); - int result = ::vsscanf(buffer, format, args); - va_end(args); - return result; - } - - char *CDECL fgets(char *str, int count, _FILE *stream) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("fgets(%p, %d, %p)\n", str, count, stream); - if (!str || count <= 0) { - return nullptr; - } - FILE *host = mapToHostFile(stream); - return ::fgets(str, count, host); - } - - SIZE_T CDECL fread(void *buffer, SIZE_T size, SIZE_T count, _FILE *stream) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("fread(%p, %zu, %zu, %p)\n", buffer, size, count, stream); - FILE *host = mapToHostFile(stream); - return ::fread(buffer, size, count, host); - } - - _FILE *CDECL _fsopen(const char *filename, const char *mode, int shflag) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_fsopen(%s, %s, %d)\n", filename ? filename : "(null)", mode ? mode : "(null)", shflag); - (void)shflag; - if (!filename || !mode) { - errno = EINVAL; - return nullptr; - } - auto hostPath = files::pathFromWindows(filename); - return mapToGuestFile(::fopen(hostPath.c_str(), mode)); - } - - int CDECL _sopen(const char *path, int oflag, int shflag, int pmode) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_sopen(%s, %d, %d, %d)\n", path ? path : "(null)", oflag, shflag, pmode); - (void)shflag; - if (!path) { - errno = EINVAL; - return -1; - } - auto hostPath = files::pathFromWindows(path); - int flags = oflag; - flags &= ~O_BINARY; - return ::open(hostPath.c_str(), flags, pmode); - } - - int CDECL _read(int fd, void *buffer, unsigned int count) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_read(%d, %p, %u)\n", fd, buffer, count); - return static_cast(::read(fd, buffer, count)); - } - - int CDECL _close(int fd) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_close(%d)\n", fd); - return ::close(fd); - } - - LONG CDECL _lseek(int fd, LONG offset, int origin) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_lseek(%d, %ld, %d)\n", fd, offset, origin); - off_t result = ::lseek(fd, static_cast(offset), origin); - return static_cast(result); - } - - int CDECL _unlink(const char *path) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_unlink(%s)\n", path ? path : "(null)"); - if (!path) { - errno = EINVAL; - return -1; - } - auto hostPath = files::pathFromWindows(path); - return ::unlink(hostPath.c_str()); - } - - int CDECL _utime(const char *path, const _utimbuf *times) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_utime(%s, %p)\n", path ? path : "(null)", times); - if (!path) { - errno = EINVAL; - return -1; - } - auto hostPath = files::pathFromWindows(path); - if (!times) { - return ::utime(hostPath.c_str(), nullptr); - } - utimbuf native{static_cast(times->actime), static_cast(times->modtime)}; - return ::utime(hostPath.c_str(), &native); - } - - int CDECL _chsize(int fd, LONG size) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_chsize(%d, %ld)\n", fd, size); - return ::ftruncate(fd, static_cast(size)); - } - - char* CDECL strncpy(char *dest, const char *src, SIZE_T count) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("strncpy(%p, %s, %zu)\n", dest, src ? src : "(null)", count); - return std::strncpy(dest, src, count); - } - - char* CDECL strpbrk(const char *str, const char *accept) { - HOST_CONTEXT_GUARD(); - const char *result = std::strpbrk(str, accept); - DEBUG_LOG("strpbrk(%s, %s) -> %p\n", str ? str : "(null)", accept ? accept : "(null)", result); - return result ? const_cast(result) : nullptr; - } - - char* CDECL strstr(const char *haystack, const char *needle) { - HOST_CONTEXT_GUARD(); - const char *result = std::strstr(haystack, needle); - DEBUG_LOG("strstr(%s, %s) -> %p\n", haystack ? haystack : "(null)", needle ? needle : "(null)", result); - return result ? const_cast(result) : nullptr; - } - - char* CDECL strrchr(const char *str, int ch) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("strrchr(%s, %c)\n", str ? str : "(null)", ch); - const char *result = std::strrchr(str, ch); - return result ? const_cast(result) : nullptr; - } - - char* CDECL strtok(char *str, const char *delim) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("strtok(%p, %s)\n", str, delim ? delim : "(null)"); - return std::strtok(str, delim); - } - - LONG CDECL _adj_fdiv_r(LONG value) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("STUB: _adj_fdiv_r(%ld)\n", value); - return value; - } - - void CDECL _adjust_fdiv(LONG n) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("STUB: _adjust_fdiv(%ld)\n", n); - (void)n; - } - - int CDECL _ftime(struct _timeb *timeptr) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_ftime(%p)\n", timeptr); - if (!timeptr) { - errno = EINVAL; - return -1; - } - struct timeval tv; - if (gettimeofday(&tv, nullptr) != 0) { - return -1; - } - timeptr->time = static_cast(tv.tv_sec); - timeptr->millitm = static_cast(tv.tv_usec / 1000); - timeptr->timezone = 0; - timeptr->dstflag = 0; - return 0; - } - - ULONG CDECL _ultoa(ULONG value, char *str, int radix) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_ultoa(%lu, %p, %d)\n", value, str, radix); - if (!str || radix < 2 || radix > 36) { - errno = EINVAL; - return 0; - } - char buffer[65]; - char *cursor = buffer + sizeof(buffer); - *--cursor = '\0'; - if (value == 0) { - *--cursor = '0'; - } - while (value > 0) { - ULONG digit = value % static_cast(radix); - value /= static_cast(radix); - *--cursor = static_cast(digit < 10 ? '0' + digit : 'A' + (digit - 10)); - } - std::strcpy(str, cursor); - return static_cast(std::strlen(str)); - } - - char* CDECL _ltoa(LONG value, char *str, int radix) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_ltoa(%ld, %p, %d)\n", value, str, radix); - if (!str || radix < 2 || radix > 36) { - errno = EINVAL; - return nullptr; - } - bool negative = value < 0; - ULONG absValue = negative ? static_cast(-value) : static_cast(value); - char buffer[65]; - ULONG length = _ultoa(absValue, buffer, radix); - std::string result; - if (negative) { - result.push_back('-'); - } - result.append(buffer, buffer + length); - std::strcpy(str, result.c_str()); - return str; - } - - char* CDECL _makepath(char *path, const char *drive, const char *dir, const char *fname, const char *ext) { - HOST_CONTEXT_GUARD(); - if (!path) { - return nullptr; - } - std::string result; - if (drive && drive[0]) { - result.append(drive); - if (result.back() != ':') { - result.push_back(':'); - } - } - if (dir && dir[0]) { - result.append(dir); - char last = result.empty() ? '\0' : result.back(); - if (last != '/' && last != '\\') { - result.push_back('\\'); - } - } - if (fname && fname[0]) { - result.append(fname); - } - if (ext && ext[0]) { - if (ext[0] != '.') { - result.push_back('.'); - } - result.append(ext); - } - DEBUG_LOG("_makepath(%p, %s, %s, %s, %s) -> %s\n", path, drive ? drive : "(null)", dir ? dir : "(null)", - fname ? fname : "(null)", ext ? ext : "(null)", result.c_str()); - std::strcpy(path, result.c_str()); - return path; - } - - char* CDECL _fullpath(char *absPath, const char *relPath, SIZE_T maxLength) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_fullpath(%p, %s, %zu)\n", absPath, relPath ? relPath : "(null)", maxLength); - if (!relPath) { - errno = EINVAL; - return nullptr; - } - std::filesystem::path hostPath = files::pathFromWindows(relPath); - std::filesystem::path resolved = hostPath; - if (!hostPath.is_absolute()) { - resolved = std::filesystem::absolute(hostPath); - } - std::string winPath = files::pathToWindows(resolved); - if (absPath) { - if (winPath.size() + 1 > maxLength) { - errno = ERANGE; - return nullptr; - } - std::strcpy(absPath, winPath.c_str()); - return absPath; - } - char *result = static_cast(wibo::heap::guestMalloc(winPath.size() + 1)); - if (!result) { - errno = ENOMEM; - return nullptr; - } - DEBUG_LOG("-> %s\n", winPath.c_str()); - std::strcpy(result, winPath.c_str()); - return result; - } - - int CDECL _putenv(const char *envString) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_putenv(%s)\n", envString ? envString : "(null)"); - if (!envString) { - errno = EINVAL; - return -1; - } - std::string entry(envString); - if (entry.find('=') == std::string::npos) { - errno = EINVAL; - return -1; - } - auto &storage = putenvStorage(); - storage.push_back(entry); - char *stored = storage.back().data(); - return ::putenv(stored); - } - - char *CDECL _mktemp(char *templateName) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_mktemp(%s)\n", templateName); - if (!templateName) { - errno = EINVAL; - return nullptr; - } - SIZE_T originalLen = std::strlen(templateName); - std::string hostTemplate = files::pathFromWindows(templateName).string(); - if (hostTemplate.empty()) { - hostTemplate = templateName; - } - std::vector mutableTemplate(hostTemplate.begin(), hostTemplate.end()); - mutableTemplate.push_back('\0'); - int fd = mkstemp(mutableTemplate.data()); - if (fd == -1) { - templateName[0] = '\0'; - return templateName; - } - ::close(fd); - ::unlink(mutableTemplate.data()); - std::string hostResult(mutableTemplate.data()); - std::string winResult = files::pathToWindows(std::filesystem::path(hostResult)); - std::strncpy(templateName, winResult.c_str(), originalLen); - templateName[originalLen] = '\0'; - DEBUG_LOG("-> %s\n", templateName); - return templateName; - } - - int CDECL _except_handler3(void *record, void *frame, void *context, void *dispatch) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_except_handler3(%p, %p, %p, %p)\n", record, frame, context, dispatch); - return _except_handler4_common(record, frame, context, dispatch); - } - - int CDECL getchar() { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("getchar()\n"); - return std::getchar(); - } - - TIME_T CDECL time(TIME_T *t) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("time(%p)\n", t); - TIME_T result = static_cast(std::time(nullptr)); - if (t) { - *t = result; - } - return result; - } - - char *CDECL __unDName(char *outputString, const char *mangledName, int maxStringLength, - void *(*allocFunc)(SIZE_T), void (*freeFunc)(void *), unsigned short) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("STUB: __unDName(%p, %s, %d, %p, %p)\n", outputString, mangledName ? mangledName : "(null)", - maxStringLength, allocFunc, freeFunc); - (void)allocFunc; - (void)freeFunc; - if (outputString && maxStringLength > 0) { - outputString[0] = '\0'; - return outputString; - } - return nullptr; - } - - char* CDECL setlocale(int category, const char *locale){ - HOST_CONTEXT_GUARD(); - DEBUG_LOG("setlocale(%d, %s)\n", category, locale ? locale : "(null)"); - char *result = std::setlocale(category, locale); - if (result) { - refreshMbCurMax(); - } - return result; - } - - int CDECL _wdupenv_s(GUEST_PTR *buffer, SIZE_T *numberOfElements, const uint16_t *varname){ - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_wdupenv_s(%p, %p, %s)\n", buffer, numberOfElements, wideStringToString(varname).c_str()); - if (buffer) { - *buffer = GUEST_NULL; - } - if (numberOfElements) { - *numberOfElements = 0; - } - - if (!buffer || !varname) { - DEBUG_LOG("_wdupenv_s: invalid parameter\n"); - errno = EINVAL; - return EINVAL; - } - - std::string var_str = wideStringToString(varname); - DEBUG_LOG("_wdupenv_s: var name %s\n", var_str.c_str()); - - auto env = ensureWideEnvironment(); - auto match = findEnvironmentValue(env, varname); - if (!match) { - DEBUG_LOG("Could not find env var %s\n", var_str.c_str()); - return 0; - } - - SIZE_T value_len = match->length; - auto *copy = static_cast(wibo::heap::guestMalloc((value_len + 1) * sizeof(uint16_t))); - if (!copy) { - DEBUG_LOG("_wdupenv_s: allocation failed\n"); - errno = ENOMEM; - return ENOMEM; - } - - wstrncpy(copy, match->value, value_len); - copy[value_len] = 0; - *buffer = toGuestPtr(copy); - if (numberOfElements) { - *numberOfElements = value_len + 1; - } - return 0; - } - - int CDECL _wgetenv_s(SIZE_T* pReturnValue, uint16_t* buffer, SIZE_T numberOfElements, const uint16_t* varname){ - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_wgetenv_s(%p, %p, %zu, %s)\n", pReturnValue, buffer, numberOfElements, - wideStringToString(varname).c_str()); - if (pReturnValue) { - *pReturnValue = 0; - } - if (numberOfElements > 0 && buffer) { - buffer[0] = 0; - } - - bool bufferRequired = numberOfElements != 0; - if (!pReturnValue || !varname || (bufferRequired && !buffer)) { - DEBUG_LOG("_wgetenv_s: invalid parameter\n"); - errno = EINVAL; - return EINVAL; - } - - std::string var_str = wideStringToString(varname); - DEBUG_LOG("_wgetenv_s: var name %s\n", var_str.c_str()); - - auto env = ensureWideEnvironment(); - auto match = findEnvironmentValue(env, varname); - if (!match) { - return 0; - } - - SIZE_T required = match->length + 1; - *pReturnValue = required; - if (!bufferRequired || !buffer) { - return 0; - } - - if (required > numberOfElements) { - errno = ERANGE; - return ERANGE; - } - - wstrncpy(buffer, match->value, match->length); - buffer[match->length] = 0; - return 0; - } - - SIZE_T CDECL strlen(const char *str) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("strlen(%s)\n", str); - return ::strlen(str); - } - - int CDECL strcmp(const char *lhs, const char *rhs) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("strcmp(%s, %s)\n", lhs, rhs); - return ::strcmp(lhs, rhs); - } - - int CDECL strncmp(const char *lhs, const char *rhs, SIZE_T count) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("strncmp(%s, %s, %zu)\n", lhs, rhs, count); - return ::strncmp(lhs, rhs, count); - } - - void CDECL _exit(int status) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_exit(%d)\n", status); - kernel32::exitInternal(status); - } - - int CDECL strcpy_s(char *dest, SIZE_T dest_size, const char *src) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("strcpy_s(%p, %zu, %s)\n", dest, dest_size, src); - if (!dest || !src || dest_size == 0) { - return 22; - } - - SIZE_T src_len = ::strlen(src); - if (src_len + 1 > dest_size) { - dest[0] = 0; - return 34; - } - - std::memcpy(dest, src, src_len + 1); - return 0; - } - - int CDECL strcat_s(char *dest, SIZE_T numberOfElements, const char *src) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("strcat_s(%p, %zu, %s)\n", dest, numberOfElements, src); - if (!dest || !src || numberOfElements == 0) { - return 22; - } - - SIZE_T dest_len = ::strlen(dest); - SIZE_T src_len = ::strlen(src); - if (dest_len + src_len + 1 > numberOfElements) { - dest[0] = 0; - return 34; - } - - std::memcpy(dest + dest_len, src, src_len + 1); - return 0; - } - - int CDECL strncpy_s(char *dest, SIZE_T dest_size, const char *src, SIZE_T count) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("strncpy_s(%p, %zu, %s, %zu)\n", dest, dest_size, src, count); - constexpr SIZE_T TRUNCATE = static_cast(-1); - constexpr int STRUNCATE = 80; - - if (!dest || dest_size == 0) { - return 22; - } - - if (!src) { - dest[0] = 0; - return count == 0 ? 0 : 22; - } - - if (count == 0) { - dest[0] = 0; - return 0; - } - - if (count == TRUNCATE) { - SIZE_T src_len = ::strlen(src); - if (src_len + 1 > dest_size) { - SIZE_T copy_len = dest_size > 0 ? dest_size - 1 : 0; - if (copy_len > 0) { - std::memcpy(dest, src, copy_len); - } - dest[copy_len] = '\0'; - return STRUNCATE; - } - std::memcpy(dest, src, src_len + 1); - return 0; - } - - SIZE_T src_len = ::strlen(src); - SIZE_T copy_len = count < src_len ? count : src_len; - if (copy_len >= dest_size) { - dest[0] = 0; - return 34; - } - - if (copy_len > 0) { - std::memcpy(dest, src, copy_len); - } - dest[copy_len] = '\0'; - return 0; - } - - char *CDECL _strdup(const char *strSource) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_strdup(%s)\n", strSource); - if (!strSource) { - return nullptr; - } - - SIZE_T length = ::strlen(strSource); - auto *copy = static_cast(wibo::heap::guestMalloc(length + 1)); - if (!copy) { - return nullptr; - } - - std::memcpy(copy, strSource, length + 1); - return copy; - } - - ULONG CDECL strtoul(const char *str, GUEST_PTR *endptr, int base) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("strtoul(%s, %p, %d)\n", str, endptr, base); - int ret; - if (endptr != nullptr) { - char *endptr_host = reinterpret_cast(fromGuestPtr(*endptr)); - ret = ::strtoul(str, &endptr_host, base); - *endptr = toGuestPtr(endptr_host); - } else { - ret = ::strtoul(str, nullptr, base); - } - return ret; - } - - void* CDECL malloc(SIZE_T size){ - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("malloc(%zu)\n", size); - return wibo::heap::guestMalloc(size); - } - - void* CDECL calloc(SIZE_T count, SIZE_T size){ - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("calloc(%zu, %zu)\n", count, size); - return wibo::heap::guestCalloc(count, size); - } - - void* CDECL realloc(void *ptr, SIZE_T size) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("realloc(%p, %zu)\n", ptr, size); - return wibo::heap::guestRealloc(ptr, size); - } - - void* CDECL _malloc_crt(SIZE_T size) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("_malloc_crt(%zu)\n", size); - return wibo::heap::guestMalloc(size); - } - - void CDECL _lock(int locknum) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("_lock(%d)\n", locknum); - if (locknum < 0 || static_cast(locknum) >= LOCK_TABLE_SIZE) { - DEBUG_LOG("_lock: unsupported lock %d\n", locknum); - return; - } - lockTable()[static_cast(locknum)].lock(); - } - - void CDECL _unlock(int locknum) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("_unlock(%d)\n", locknum); - if (locknum < 0 || static_cast(locknum) >= LOCK_TABLE_SIZE) { - DEBUG_LOG("_unlock: unsupported lock %d\n", locknum); - return; - } - lockTable()[static_cast(locknum)].unlock(); - } - - _onexit_t CDECL __dllonexit(_onexit_t func, GUEST_PTR *pbegin, GUEST_PTR *pend) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("__dllonexit(%p, %p, %p)\n", func, pbegin, pend); - if (!pbegin || !pend) { - return nullptr; - } - - size_t len = pend - pbegin; - if (++len <= 0) { - return nullptr; - } - - GUEST_PTR *table = static_cast(wibo::heap::guestRealloc(pbegin, len * sizeof(GUEST_PTR))); - if (!table) { - return nullptr; - } - *pbegin = toGuestPtr(table); - *pend = toGuestPtr(table + len); - table[len - 1] = toGuestPtr(reinterpret_cast(func)); - return func; - } - - void CDECL free(void* ptr){ - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("free(%p)\n", ptr); - std::free(ptr); - } - - void* CDECL memcpy(void *dest, const void *src, SIZE_T count) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("memcpy(%p, %p, %zu)\n", dest, src, count); - return std::memcpy(dest, src, count); - } - - void* CDECL memmove(void *dest, const void *src, SIZE_T count) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("memmove(%p, %p, %zu)\n", dest, src, count); - return std::memmove(dest, src, count); - } - - int CDECL memcmp(const void *lhs, const void *rhs, SIZE_T count) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("memcmp(%p, %p, %zu)\n", lhs, rhs, count); - return std::memcmp(lhs, rhs, count); - } - - static thread_local sort_compare currentCompare = nullptr; - - static int doCompare(const void *a, const void *b) { return call_sort_compare(currentCompare, a, b); } - - void CDECL qsort(void *base, SIZE_T num, SIZE_T size, sort_compare compare) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("qsort(%p, %zu, %zu, %p)\n", base, num, size, compare); - currentCompare = compare; - ::qsort(base, num, size, doCompare); - } - - int CDECL fflush(_FILE *stream) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("fflush(%p)\n", stream); - if (!stream) { - return std::fflush(nullptr); - } - FILE *host = mapToHostFile(stream); - return std::fflush(host); - } - - int CDECL_NO_CONV vfwprintf(_FILE *stream, const uint16_t *format, va_list args) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("vfwprintf(%p, %s, ...)\n", stream, wideStringToString(format).c_str()); - FILE *host = mapToHostFile(stream ? stream : &_iob[0]); - std::wstring fmt; - if (format) { - for (const uint16_t *ptr = format; *ptr; ++ptr) { - fmt.push_back(static_cast(*ptr)); - } - } - fmt.push_back(L'\0'); - return std::vfwprintf(host, fmt.c_str(), args); - } - - _FILE *CDECL fopen(const char *filename, const char *mode) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("fopen(%s, %s)\n", filename ? filename : "(null)", mode ? mode : "(null)"); - return mapToGuestFile(std::fopen(filename, mode)); - } - - int CDECL _dup2(int fd1, int fd2) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("_dup2(%d, %d)\n", fd1, fd2); - return dup2(fd1, fd2); - } - - int CDECL _isatty(int fd) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("_isatty(%d)\n", fd); - return isatty(fd); - } - - int CDECL fseek(_FILE *stream, LONG offset, int origin) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("fseek(%p, %ld, %d)\n", stream, offset, origin); - FILE* host = mapToHostFile(stream); - return std::fseek(host, offset, origin); - } - - LONG CDECL ftell(_FILE *stream) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("ftell(%p)\n", stream); - FILE* host = mapToHostFile(stream); - return std::ftell(host); - } - - int CDECL feof(_FILE *stream) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("feof(%p)\n", stream); - FILE* host = mapToHostFile(stream); - return std::feof(host); - } - - int CDECL fputws(const uint16_t *str, _FILE *stream) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("fputws(%s, %p)\n", wideStringToString(str).c_str(), stream); - std::wstring temp; - if (str) { - for (const uint16_t *cursor = str; *cursor; ++cursor) { - temp.push_back(static_cast(*cursor)); - } - } - FILE* host = mapToHostFile(stream); - return std::fputws(temp.c_str(), host); - } - - int CDECL _cputws(const uint16_t *string) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_cputws(%s)\n", wideStringToString(string).c_str()); - return fputws(string, &_iob[0]); - } - - uint16_t* CDECL fgetws(uint16_t *buffer, int size, _FILE *stream) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("fgetws(%p, %d, %p)\n", buffer, size, stream); - if (!buffer || size <= 0) { - return nullptr; - } - std::vector temp(static_cast(size)); - FILE* host = mapToHostFile(stream); - wchar_t *res = std::fgetws(temp.data(), size, host); - if (!res) { - return nullptr; - } - for (int i = 0; i < size; ++i) { - buffer[i] = static_cast(temp[i]); - if (temp[i] == L'\0') { - break; - } - } - return buffer; - } - - WINT_T CDECL fgetwc(_FILE *stream) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("fgetwc(%p)\n", stream); - FILE* host = mapToHostFile(stream); - return std::fgetwc(host); - } - - int CDECL _wfopen_s(GUEST_PTR *stream, const uint16_t *filename, const uint16_t *mode) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_wfopen_s(%p, %s, %s)\n", stream, wideStringToString(filename).c_str(), - wideStringToString(mode).c_str()); - if (!stream || !filename || !mode) { - errno = EINVAL; - return EINVAL; - } - std::string narrowName = wideStringToString(filename); - std::string narrowMode = wideStringToString(mode); - FILE *handle = std::fopen(narrowName.c_str(), narrowMode.c_str()); - if (!handle) { - *stream = GUEST_NULL; - return errno ? errno : EINVAL; - } - *stream = toGuestPtr(mapToGuestFile(handle)); - return 0; - } - - int CDECL _wcsicmp(const uint16_t *lhs, const uint16_t *rhs) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("_wcsicmp(%s, %s)\n", wideStringToString(lhs).c_str(), wideStringToString(rhs).c_str()); - if (lhs == rhs) { - return 0; - } - if (!lhs) { - return -1; - } - if (!rhs) { - return 1; - } - - while (*lhs && *rhs) { - uint16_t a = wcharToLower(*lhs++); - uint16_t b = wcharToLower(*rhs++); - if (a != b) { - return static_cast(a) - static_cast(b); - } - } - - uint16_t a = wcharToLower(*lhs); - uint16_t b = wcharToLower(*rhs); - return static_cast(a) - static_cast(b); - } - - int CDECL _wmakepath_s(uint16_t *path, SIZE_T sizeInWords, const uint16_t *drive, const uint16_t *dir, - const uint16_t *fname, const uint16_t *ext) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_wmakepath_s(%p, %zu, %s, %s, %s, %s)\n", path, sizeInWords, wideStringToString(drive).c_str(), - wideStringToString(dir).c_str(), wideStringToString(fname).c_str(), wideStringToString(ext).c_str()); - if (!path || sizeInWords == 0) { - return EINVAL; - } - - path[0] = 0; - std::u16string result; - - auto append = [&](const uint16_t *src) { - if (!src || !*src) { - return; - } - for (const uint16_t *cursor = src; *cursor; ++cursor) { - result.push_back(static_cast(*cursor)); - } - }; - - if (drive && *drive) { - result.push_back(static_cast(drive[0])); - if (drive[1] == u':') { - result.push_back(u':'); - append(drive + 2); - } else { - result.push_back(u':'); - append(drive + 1); - } - } - - auto appendDir = [&](const uint16_t *directory) { - if (!directory || !*directory) { - return; - } - append(directory); - if (result.empty()) { - return; - } - char16_t last = result.back(); - if (last != u'/' && last != u'\\') { - result.push_back(u'\\'); - } - }; - - appendDir(dir); - append(fname); - - if (ext && *ext) { - if (*ext != u'.') { - result.push_back(u'.'); - append(ext); - } else { - append(ext); - } - } - - DEBUG_LOG("-> %s\n", wideStringToString(reinterpret_cast(result.c_str())).c_str()); - - SIZE_T required = result.size() + 1; - if (required > sizeInWords) { - path[0] = 0; - return ERANGE; - } - - for (SIZE_T i = 0; i < result.size(); ++i) { - path[i] = static_cast(result[i]); - } - path[result.size()] = 0; - return 0; - } - - int CDECL _wputenv_s(const uint16_t *varname, const uint16_t *value) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_wputenv_s(%p, %p)\n", varname, value); - if (!varname || !value) { - errno = EINVAL; - return EINVAL; - } - - if (!*varname) { - errno = EINVAL; - return EINVAL; - } - - for (const uint16_t *cursor = varname; *cursor; ++cursor) { - if (*cursor == static_cast('=')) { - errno = EINVAL; - return EINVAL; - } - } - - std::string name = wideStringToString(varname); - if (name.empty()) { - errno = EINVAL; - return EINVAL; - } - - int resultCode = 0; - if (!*value) { - if (unsetenv(name.c_str()) != 0) { - resultCode = errno != 0 ? errno : EINVAL; - } - } else { - std::string narrowValue = wideStringToString(value); - if (setenv(name.c_str(), narrowValue.c_str(), 1) != 0) { - resultCode = errno != 0 ? errno : EINVAL; - } - } - - if (resultCode != 0) { - errno = resultCode; - return resultCode; - } - - getMainArgsCommon(nullptr, nullptr, nullptr, copyNarrowString); - getMainArgsCommon(nullptr, nullptr, nullptr, copyWideString); - return 0; - } - - ULONG CDECL wcsspn(const uint16_t *str1, const uint16_t *str2) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("wcsspn(%p, %p)\n", str1, str2); - if (!str1 || !str2) { - return 0; - } - ULONG count = 0; - for (const uint16_t *p = str1; *p; ++p) { - bool match = false; - for (const uint16_t *q = str2; *q; ++q) { - if (*p == *q) { - match = true; - break; - } - } - if (!match) { - break; - } - ++count; - } - return count; - } - - LONG CDECL _wtol(const uint16_t *str) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("_wtol(%p)\n", str); - return wstrtol(str, nullptr, 10); - } - - int CDECL _wcsupr_s(uint16_t *str, SIZE_T size) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("_wcsupr_s(%p, %zu)\n", str, size); - if (!str || size == 0) { - return EINVAL; - } - SIZE_T len = wstrnlen(str, size); - if (len >= size) { - return ERANGE; - } - for (SIZE_T i = 0; i < len; ++i) { - str[i] = wcharToUpper(str[i]); - } - return 0; - } - - int CDECL _wcslwr_s(uint16_t *str, SIZE_T size) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("_wcslwr_s(%p, %zu)\n", str, size); - if (!str || size == 0) { - return EINVAL; - } - SIZE_T len = wstrnlen(str, size); - if (len >= size) { - return ERANGE; - } - for (SIZE_T i = 0; i < len; ++i) { - str[i] = wcharToLower(str[i]); - } - return 0; - } - - WINT_T CDECL towlower(WINT_T ch) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("towlower(%d)\n", ch); - return wcharToLower(ch); - } - - unsigned int CDECL _mbctolower(unsigned int ch) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("_mbctolower(%u)\n", ch); - if (ch <= 0xFF) { - unsigned char byte = static_cast(ch); - unsigned char lowered = static_cast(std::tolower(static_cast(byte))); - return static_cast(lowered); - } - return ch; - } - - int CDECL toupper(int ch) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("toupper(%d)\n", ch); - return std::toupper(ch); - } - - int CDECL tolower(int ch) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("tolower(%d)\n", ch); - return std::tolower(ch); - } - - int CDECL _ftime64_s(void *timeb) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("STUB: _ftime64_s(%p)\n", timeb); - (void)timeb; - return 0; - } - - int CDECL _crt_debugger_hook(int value) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("STUB: _crt_debugger_hook(%d)\n", value); - (void)value; - return 0; - } - - int CDECL _configthreadlocale(int mode) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_configthreadlocale(mode=%d)\n", mode); - static int currentMode = 0; - int previous = currentMode; - if (mode == -1) { - return previous; - } - if (mode == 0 || mode == 1 || mode == 2) { - currentMode = mode; - return previous; - } - errno = EINVAL; - return -1; - } - - void CDECL __setusermatherr(void* handler) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("STUB: __setusermatherr(handler=%p)\n", handler); - (void)handler; - } - - void CDECL _cexit() { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_cexit()\n"); - for (_onexit_t func : onExitFuncs) { - call__onexit_t(func); - } - std::fflush(nullptr); - } - - int CDECL_NO_CONV vfprintf(_FILE *stream, const char *format, va_list args) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("vfprintf(stream=%p, format=%s, args=%p)\n", stream, format, args); - if (!format || !stream) { - errno = EINVAL; - return -1; - } - FILE *native = mapToHostFile(stream); - if (!native) { - errno = EINVAL; - return -1; - } - va_list argsCopy; - va_copy(argsCopy, args); - int result = std::vfprintf(native, format, argsCopy); - va_end(argsCopy); - return result; - } - - int CDECL_NO_CONV fprintf(_FILE *stream, const char *format, ...) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("fprintf(%p, %s, ...)\n", stream, format); - va_list args; - va_start(args, format); - int result = msvcrt::vfprintf(stream, format, args); - va_end(args); - return result; - } - - int CDECL fputc(int ch, _FILE *stream) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("fputc(%d, %p)\n", ch, stream); - if (!stream) { - errno = EINVAL; - return EOF; - } - FILE *native = mapToHostFile(stream); - if (!native) { - errno = EINVAL; - return EOF; - } - return std::fputc(ch, native); - } - - SIZE_T CDECL fwrite(const void *buffer, SIZE_T size, SIZE_T count, _FILE *stream) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("fwrite(%p, %zu, %zu, %p)\n", buffer, size, count, stream); - if (!buffer || !stream) { - errno = EINVAL; - return 0; - } - FILE *native = mapToHostFile(stream); - if (!native) { - errno = EINVAL; - return 0; - } - return std::fwrite(buffer, size, count, native); - } - - char *CDECL strerror(int errnum) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("strerror(%d)\n", errnum); - return std::strerror(errnum); - } - - char *CDECL strchr(const char *str, int character) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("strchr(%s, %d)\n", str, character); - return const_cast(std::strchr(str, character)); - } - - struct lconv *CDECL localeconv() { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("localeconv()\n"); - return std::localeconv(); - } - - signal_handler CDECL signal(int sig, signal_handler handler) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("signal(%d, %p)\n", sig, handler); - if (sig != SIGABRT && sig != SIGFPE && sig != SIGILL && sig != SIGINT && - sig != SIGSEGV && sig != SIGTERM) { - DEBUG_LOG("signal: unsupported signal %d\n", sig); - errno = EINVAL; - return SIG_ERR; - } - return std::signal(sig, handler); - } - - SIZE_T CDECL wcslen(const uint16_t *str) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("wcslen(%p)\n", str); - return wstrlen(str); - } - - static void abort_and_log(const char *reason) { - DEBUG_LOG("Runtime abort: %s\n", reason ? reason : ""); - std::abort(); - } - - void CDECL abort() { - HOST_CONTEXT_GUARD(); - abort_and_log("abort"); - } - - int CDECL atoi(const char *str) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("atoi(%s)\n", str); - if (!str) { - errno = EINVAL; - return 0; - } - return std::atoi(str); - } - - int CDECL _amsg_exit(int reason) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_amsg_exit(%d)\n", reason); - abort_and_log("_amsg_exit"); - return reason; - } - - void CDECL _invoke_watson(const WCHAR *, const WCHAR *, const WCHAR *, UINT, UINT_PTR) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_invoke_watson(...)\n"); - abort_and_log("_invoke_watson"); - } - - void CDECL terminateShim() { - HOST_CONTEXT_GUARD(); - abort_and_log("terminate"); - } - - int CDECL _purecall() { - HOST_CONTEXT_GUARD(); - abort_and_log("_purecall"); - return 0; - } - - int CDECL _except_handler4_common(void *, void *, void *, void *) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("STUB: _except_handler4_common\n"); - return 0; - } - - LONG CDECL _XcptFilter(ULONG code, void *) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("STUB: _XcptFilter(%lu)\n", code); - return 0; - } - - int CDECL _get_wpgmptr(GUEST_PTR *pValue) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_get_wpgmptr(%p)\n", pValue); - if (!pValue) { - return 22; - } - if (_wpgmptr) { - *pValue = _wpgmptr; - return 0; - } - - // TODO - // const auto wStr = stringToWideString(wibo::guestExecutablePath.c_str()); - // delete[] _wpgmptr; - - // _wpgmptr = new uint16_t[wStr.size() + 1]; - // std::copy(wStr.begin(), wStr.end(), _wpgmptr); - // _wpgmptr[wStr.size()] = 0; - - // *pValue = _wpgmptr; - return 0; - } - - GUEST_PTR CDECL __p__pgmptr() { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("__p__pgmptr()\n"); - // TODO - // _pgmptr = const_cast(wibo::guestExecutablePath.c_str()); - // return &_pgmptr; - return GUEST_NULL; - } - - int CDECL _wsplitpath_s(const uint16_t * path, uint16_t * drive, SIZE_T driveNumberOfElements, uint16_t *dir, SIZE_T dirNumberOfElements, - uint16_t * fname, SIZE_T nameNumberOfElements, uint16_t * ext, SIZE_T extNumberOfElements){ - HOST_CONTEXT_GUARD(); - if(!path){ - return 22; - } - else { - std::string path_str = wideStringToString(path); - } - DEBUG_LOG("_wsplitpath_s(path=%p, drive=%p, driveNumberOfElements=%zu, dir=%p, dirNumberOfElements=%zu, " - "fname=%p, nameNumberOfElements=%zu, ext=%p, extNumberOfElements=%zu)\n", - path, drive, driveNumberOfElements, dir, dirNumberOfElements, fname, nameNumberOfElements, ext, extNumberOfElements); - - if(drive && driveNumberOfElements) drive[0] = L'\0'; - if(dir && dirNumberOfElements) dir[0] = L'\0'; - if(fname && nameNumberOfElements) fname[0] = L'\0'; - if(ext && extNumberOfElements) ext[0] = L'\0'; - - const uint16_t *slash = wstrrchr(path, L'/'); - const uint16_t *dot = wstrrchr(path, L'.'); - const uint16_t *filename_start = slash ? slash + 1 : path; - if (dot && dot < filename_start) dot = nullptr; - - if (dir && dirNumberOfElements && slash) { - SIZE_T dir_len = slash - path + 1; - if (dir_len >= dirNumberOfElements) return 34; - wstrncpy(dir, path, dir_len); - dir[dir_len] = L'\0'; - } - - if (fname && nameNumberOfElements) { - SIZE_T fname_len = dot ? (SIZE_T)(dot - filename_start) : wstrlen(filename_start); - if (fname_len >= nameNumberOfElements) return 34; - wstrncpy(fname, filename_start, fname_len); - fname[fname_len] = L'\0'; - } - - if (ext && extNumberOfElements && dot) { - SIZE_T ext_len = wstrlen(dot); - if (ext_len >= extNumberOfElements) return 34; - wstrncpy(ext, dot, ext_len); - ext[ext_len] = L'\0'; - } - - if (drive && driveNumberOfElements && path[1] == L':' && path[2] == L'/') { - if (driveNumberOfElements < 3) return 34; - drive[0] = path[0]; - drive[1] = L':'; - drive[2] = L'\0'; - } - - return 0; - } - - int CDECL wcscat_s(uint16_t *strDestination, SIZE_T numberOfElements, const uint16_t *strSource){ - HOST_CONTEXT_GUARD(); - std::string dst_str = wideStringToString(strDestination); - std::string src_str = wideStringToString(strSource); - DEBUG_LOG("wcscat_s %s %d %s", dst_str.c_str(), numberOfElements, src_str.c_str()); - if(!strDestination || !strSource || numberOfElements == 0) return 22; - - SIZE_T dest_len = wstrlen(strDestination); - SIZE_T src_len = wstrlen(strSource); - - if(dest_len + src_len + 1 > numberOfElements){ - if(strDestination && numberOfElements > 0) strDestination[0] = L'\0'; - return 34; - } - - wstrcat(strDestination, strSource); - dst_str = wideStringToString(strDestination); - DEBUG_LOG(" --> %s\n", dst_str.c_str()); - - return 0; - } - - uint16_t* CDECL _wcsdup(const uint16_t *strSource){ - HOST_CONTEXT_GUARD(); - // std::string src_str = wideStringToString(strSource); - // DEBUG_LOG("_wcsdup: %s", src_str.c_str()); - if(!strSource) return nullptr; - SIZE_T strLen = wstrlen(strSource); - - auto *dup = static_cast(wibo::heap::guestMalloc((strLen + 1) * sizeof(uint16_t))); - if(!dup) return nullptr; - - for(SIZE_T i = 0; i <= strLen; i++){ - dup[i] = strSource[i]; - } - - // std::string dst_str = wideStringToString(dup); - // DEBUG_LOG(" --> %s\n", dst_str.c_str()); - return dup; - } - - int CDECL _waccess_s(const uint16_t* path, int mode){ - HOST_CONTEXT_GUARD(); - std::string original = wideStringToString(path); - DEBUG_LOG("_waccess_s %s\n", original.c_str()); - std::filesystem::path host = files::pathFromWindows(original.c_str()); - std::string candidate; - if (!host.empty()) { - candidate = host.string(); - } else { - candidate = original; - std::replace(candidate.begin(), candidate.end(), '\\', '/'); - } - return access(candidate.c_str(), mode); - } - - void* CDECL memset(void *s, int c, SIZE_T n){ - HOST_CONTEXT_GUARD(); - return std::memset(s, c, n); - } - - int CDECL wcsncpy_s(uint16_t *strDest, SIZE_T numberOfElements, const uint16_t *strSource, SIZE_T count){ - HOST_CONTEXT_GUARD(); - std::string src_str = wideStringToString(strSource); - DEBUG_LOG("wcsncpy_s(%p, %zu, %p, %zu)\n", strDest, numberOfElements, strSource, count); - - if(!strDest || !strSource || numberOfElements == 0){ - if(strDest && numberOfElements > 0) strDest[0] = L'\0'; - return 1; - } - - if(count == (SIZE_T)-1) count = wstrlen(strSource); - - if(count >= numberOfElements){ - strDest[0] = L'\0'; - return 1; - } - - wstrncpy(strDest, strSource, count); - strDest[count] = L'\0'; - VERBOSE_LOG(" -> %s\n", wideStringToString(strDest).c_str()); - return 0; - } - - int CDECL wcsncat_s(uint16_t *strDest, SIZE_T numberOfElements, const uint16_t *strSource, SIZE_T count){ - HOST_CONTEXT_GUARD(); - std::string dst_str = wideStringToString(strDest); - std::string src_str = wideStringToString(strSource); - DEBUG_LOG("wscncat_s(%p, %zu, %p, %zu)\n", strDest, numberOfElements, strSource, count); - - if(!strDest || !strSource || numberOfElements == 0){ - if(strDest && numberOfElements > 0) strDest[0] = L'\0'; - return 1; - } - - SIZE_T dest_len = wstrlen(strDest); - SIZE_T src_len = (count == (SIZE_T)-1) ? wstrlen(strSource) : wstrnlen(strSource, count); - - if(dest_len + src_len + 1 > numberOfElements){ - strDest[0] = L'\0'; - return 1; - } - - wstrncat(strDest, strSource, src_len); - VERBOSE_LOG(" -> %s\n", wideStringToString(strDest).c_str()); - return 0; - } - - int CDECL _itow_s(int value, uint16_t *buffer, SIZE_T size, int radix){ - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("_itow_s(%d, %p, %zu, %d)\n", value, buffer, size, radix); - if (!buffer || size == 0) return 22; - (void)radix; - assert(radix == 10); // only base 10 supported for now - - std::string str = std::to_string(value); - std::vector wStr = stringToWideString(str.c_str()); - - if(wStr.size() + 1 > size){ - buffer[0] = 0; - return 34; - } - - std::copy(wStr.begin(), wStr.end(), buffer); - buffer[wStr.size()] = 0; - return 0; - } - - int CDECL _wtoi(const uint16_t* str) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("_wtoi(%p)\n", str); - return wstrtol(str, nullptr, 10); - } - - int CDECL _ltoa_s(LONG value, char *buffer, SIZE_T sizeInChars, int radix) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("_ltoa_s(%ld, %p, %zu, %d)\n", value, buffer, sizeInChars, radix); - if (!buffer || sizeInChars == 0) { - return 22; - } - if (radix < 2 || radix > 36) { - buffer[0] = 0; - return 22; - } - - bool isNegative = (value < 0) && (radix == 10); - uint64_t magnitude = isNegative ? static_cast(-(int64_t)value) : static_cast(static_cast(value)); - char temp[65]; - SIZE_T index = 0; - do { - uint64_t digit = magnitude % static_cast(radix); - temp[index++] = static_cast((digit < 10) ? ('0' + digit) : ('a' + (digit - 10))); - magnitude /= static_cast(radix); - } while (magnitude != 0 && index < sizeof(temp)); - - if (isNegative) { - temp[index++] = '-'; - } - - SIZE_T required = index + 1; // include null terminator - if (required > sizeInChars) { - buffer[0] = 0; - return 34; - } - - for (SIZE_T i = 0; i < index; ++i) { - buffer[i] = temp[index - i - 1]; - } - buffer[index] = '\0'; - return 0; - } - - int CDECL wcscpy_s(uint16_t *dest, SIZE_T dest_size, const uint16_t *src) { - HOST_CONTEXT_GUARD(); - std::string src_str = wideStringToString(src); - VERBOSE_LOG("wcscpy_s(%p, %zu, %p)\n", dest, dest_size, src); - if (!dest || !src || dest_size == 0) { - return 22; - } - - if (wstrlen(src) + 1 > dest_size) { - dest[0] = 0; - return 34; - } - - wstrcpy(dest, src); - return 0; - } - - int CDECL_NO_CONV swprintf_s(uint16_t *buffer, SIZE_T sizeOfBuffer, const uint16_t *format, ...) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("swprintf_s(%p, %zu, %p, ...)\n", buffer, sizeOfBuffer, format); - if (!buffer || sizeOfBuffer == 0 || !format) { - errno = EINVAL; - return EINVAL; - } - std::wstring fmt; - for (const uint16_t *ptr = format; *ptr; ++ptr) { - fmt.push_back(static_cast(*ptr)); - } - fmt.push_back(L'\0'); - std::vector temp(sizeOfBuffer); - va_list args; - va_start(args, format); - int written = std::vswprintf(temp.data(), temp.size(), fmt.c_str(), args); - va_end(args); - if (written < 0 || static_cast(written) >= sizeOfBuffer) { - buffer[0] = 0; - errno = ERANGE; - return ERANGE; - } - for (int i = 0; i <= written; ++i) { - buffer[i] = static_cast(temp[static_cast(i)]); - } - return written; - } - - int CDECL_NO_CONV swscanf_s(const uint16_t *buffer, const uint16_t *format, ...) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("swscanf_s(%p, %p, ...)\n", buffer, format); - if (!buffer || !format) { - errno = EINVAL; - return EOF; - } - std::wstring bufW; - for (const uint16_t *ptr = buffer; *ptr; ++ptr) { - bufW.push_back(static_cast(*ptr)); - } - bufW.push_back(L'\0'); - std::wstring fmt; - for (const uint16_t *ptr = format; *ptr; ++ptr) { - fmt.push_back(static_cast(*ptr)); - } - fmt.push_back(L'\0'); - va_list args; - va_start(args, format); - int result = std::vswscanf(bufW.c_str(), fmt.c_str(), args); - va_end(args); - return result; - } - - int* CDECL _get_osfhandle(int fd){ - HOST_CONTEXT_GUARD(); - DEBUG_LOG("STUB: _get_osfhandle(%d)\n", fd); - return (int*)fd; - } - - int CDECL _write(int fd, const void* buffer, unsigned int count) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("_write(fd=%d, buffer=%p, count=%u)\n", fd, buffer, count); - return (int)write(fd, buffer, count); - } - - void CDECL exit(int status) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("exit(%d)\n", status); - _cexit(); - kernel32::exitInternal(status); - } - - int CDECL wcsncmp(const uint16_t *string1, const uint16_t *string2, SIZE_T count) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("wcsncmp(%p, %p, %zu)\n", string1, string2, count); - return wstrncmp(string1, string2, count); - } - - int CDECL_NO_CONV _vswprintf_c_l(uint16_t *buffer, SIZE_T size, const uint16_t *format, ...) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_vswprintf_c_l(%p, %zu, %p, ...)\n", buffer, size, format); - if (!buffer || !format || size == 0) { - if (buffer && size > 0) { - buffer[0] = 0; - } - return -1; - } - - std::string narrow_fmt = wideStringToString(format); - DEBUG_LOG("\tFmt: %s\n", narrow_fmt.c_str()); - - va_list args; - va_start(args, format); - va_list argsCopy; - va_copy(argsCopy, args); - int required = vsnprintf(nullptr, 0, narrow_fmt.c_str(), argsCopy); - va_end(argsCopy); - - if (required < 0) { - va_end(args); - buffer[0] = 0; - return -1; - } - - std::vector narrowBuffer(static_cast(required) + 1); - int written = vsnprintf(narrowBuffer.data(), narrowBuffer.size(), narrow_fmt.c_str(), args); - va_end(args); - - if (written < 0) { - buffer[0] = 0; - return -1; - } - DEBUG_LOG("\tBuffer: %s\n", narrowBuffer.data()); - - std::vector wide = stringToWideString(narrowBuffer.data()); - SIZE_T wideLen = wide.size(); - - if (wideLen + 1 > size) { - buffer[0] = 0; - return -1; - } - if (wideLen > static_cast(std::numeric_limits::max())) { - buffer[0] = 0; - return -1; - } - - if (wideLen > 0) { - std::memcpy(buffer, wide.data(), wideLen * sizeof(uint16_t)); - } - buffer[wideLen] = 0; - - return static_cast(wideLen); - } - - const uint16_t* CDECL wcsstr( const uint16_t *dest, const uint16_t *src ){ - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("wcsstr(%p, %p)\n", dest, src); - return wstrstr(dest, src); - } - - int CDECL iswspace(WINT_T w){ - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("iswspace(%u)\n", w); - return std::iswspace(w); - } - - int CDECL iswdigit(WINT_T w){ - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("iswdigit(%u)\n", w); - return std::iswdigit(w); - } - - const uint16_t* CDECL wcschr(const uint16_t* str, uint16_t c){ - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("wcschr(%p, %u)\n", str, c); - return wstrchr(str, c); - } - - const uint16_t* CDECL wcsrchr(const uint16_t *str, uint16_t c){ - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("wcsrchr(%p, %u)\n", str, c); - return wstrrchr(str, c); - } - - ULONG CDECL wcstoul(const uint16_t *strSource, GUEST_PTR *endptr, int base){ - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("wcstoul(%p, %p, %d)\n", strSource, endptr, base); - int ret; - if (endptr != nullptr) { - uint16_t *endptr_host = reinterpret_cast(fromGuestPtr(*endptr)); - ret = ::wstrtoul(strSource, &endptr_host, base); - *endptr = toGuestPtr(endptr_host); - } else { - ret = ::wstrtoul(strSource, nullptr, base); - } - return ret; - } - - _FILE* CDECL _wfsopen(const uint16_t* filename, const uint16_t* mode, int shflag){ - HOST_CONTEXT_GUARD(); - if (!filename || !mode) return nullptr; - std::string fname_str = wideStringToString(filename); - std::string mode_str = wideStringToString(mode); - DEBUG_LOG("_wfsopen(%s, %s)\n", fname_str.c_str(), mode_str.c_str()); - - (void)shflag; - return fopen(fname_str.c_str(), mode_str.c_str()); - } - - int CDECL puts(const char *str) { - HOST_CONTEXT_GUARD(); - if (!str) { - str = "(null)"; - } - DEBUG_LOG("puts(%s)\n", str); - if (std::fputs(str, stdout) < 0) - return EOF; - if (std::fputc('\n', stdout) == EOF) - return EOF; - return 0; - } - - int CDECL fclose(_FILE* stream){ - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("fclose(%p)\n", stream); - return closeGuestFile(stream); - } - - int CDECL _flushall(){ - HOST_CONTEXT_GUARD(); - DEBUG_LOG("_flushall()\n"); - int count = 0; - - if (::fflush(stdin) == 0) count++; - if (::fflush(stdout) == 0) count++; - if (::fflush(stderr) == 0) count++; - - std::lock_guard lock(g_fileMutex); - for (auto &file : g_files) { - if (::fflush(file.second) == 0) - count++; - } - - return count; - } - - int* CDECL _errno() { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("_errno()\n"); - return &errno; - } - - LONG_PTR CDECL _wspawnvp(int mode, const uint16_t* cmdname, GUEST_PTR *argv) { - HOST_CONTEXT_GUARD(); - if (!cmdname || !argv) { - errno = EINVAL; - return -1; - } - - std::string command = wideStringToString(cmdname); - DEBUG_LOG("_wspawnvp(%d, %s)\n", mode, command.c_str()); - - std::vector argStorage; - for (GUEST_PTR *cursor = argv; *cursor; ++cursor) { - const WCHAR *arg = reinterpret_cast(fromGuestPtr(*cursor)); - argStorage.emplace_back(wideStringToString(arg)); - } - - auto resolved = wibo::resolveExecutable(command, false); - if (!resolved) { - errno = ENOENT; - DEBUG_LOG("-> failed to resolve executable for %s\n", command.c_str()); - return -1; - } - DEBUG_LOG("-> resolved to %s\n", resolved->c_str()); - - Pin po; - int spawnResult = wibo::spawnWithArgv(*resolved, argStorage, po); - if (spawnResult != 0) { - errno = spawnResult; - DEBUG_LOG("-> spawnWithArgv failed: %d\n", spawnResult); - return -1; - } - DEBUG_LOG("-> spawned pid %d\n", po->pid); - - constexpr int P_WAIT = 0; - constexpr int P_DETACH = 2; - - if (mode == P_WAIT) { - std::unique_lock lk(po->m); - po->cv.wait(lk, [&] { return po->signaled; }); - return static_cast(po->exitCode); - } - - if (mode == P_DETACH) { - return 0; - } - - // _P_NOWAIT and unknown flags: return process id - return static_cast(po->pid); - } - - LONG_PTR CDECL _spawnvp(int mode, const char *cmdname, GUEST_PTR *argv) { - HOST_CONTEXT_GUARD(); - if (!cmdname || !argv) { - errno = EINVAL; - return -1; - } - - std::string command(cmdname); - DEBUG_LOG("_spawnvp(%d, %s)\n", mode, command.c_str()); - - std::vector argStorage; - for (GUEST_PTR *cursor = argv; *cursor; ++cursor) { - const char *arg = reinterpret_cast(fromGuestPtr(*cursor)); - argStorage.emplace_back(arg); - } - - auto resolved = wibo::resolveExecutable(command, false); - if (!resolved) { - errno = ENOENT; - DEBUG_LOG("-> failed to resolve executable for %s\n", command.c_str()); - return -1; - } - DEBUG_LOG("-> resolved to %s\n", resolved->c_str()); - - Pin po; - int spawnResult = wibo::spawnWithArgv(*resolved, argStorage, po); - if (spawnResult != 0) { - errno = spawnResult; - DEBUG_LOG("-> spawnWithArgv failed: %d\n", spawnResult); - return -1; - } - DEBUG_LOG("-> spawned pid %d\n", po->pid); - - constexpr int P_WAIT = 0; - constexpr int P_DETACH = 2; - - if (mode == P_WAIT) { - std::unique_lock lk(po->m); - po->cv.wait(lk, [&] { return po->signaled; }); - return static_cast(po->exitCode); - } - - if (mode == P_DETACH) { - return 0; - } - - return static_cast(po->pid); - } - - int CDECL _wunlink(const uint16_t *filename){ - HOST_CONTEXT_GUARD(); - std::string str = wideStringToString(filename); - DEBUG_LOG("_wunlink(%s)\n", str.c_str()); - return unlink(str.c_str()); - } - - uint16_t* CDECL _wfullpath(uint16_t* absPath, const uint16_t* relPath, SIZE_T maxLength){ - HOST_CONTEXT_GUARD(); - std::string relPathStr = wideStringToString(relPath); - DEBUG_LOG("_wfullpath(%s, %zu)\n", relPathStr.c_str(), maxLength); - if(!relPath) return nullptr; - - char resolved[PATH_MAX]; - char* realpathResult = realpath(relPathStr.c_str(), resolved); - std::string finalPath; - - if(realpathResult){ - finalPath = resolved; - } - else if (!relPathStr.empty() && relPathStr[0] == '\\') { - // this is an absolute path - normalize it before assigning finalPath - for (char& c : relPathStr) if (c == '\\') c = '/'; - finalPath = relPathStr; - } - else { - DEBUG_LOG("\tcould not find realpath, trying cwd...\n"); - char cwd[PATH_MAX]; - if(!getcwd(cwd, sizeof(cwd))){ - return nullptr; - } - finalPath = std::string(cwd) + "/" + relPathStr; - } - - std::vector wResolved = stringToWideString(finalPath.c_str()); - // If caller provided a buffer, check size - if (absPath) { - if (wResolved.size() + 1 > maxLength) { - return nullptr; // too small - } - std::copy(wResolved.begin(), wResolved.end(), absPath); - absPath[wResolved.size()] = 0; - - std::string absPathStr = wideStringToString(absPath); - DEBUG_LOG("\t-> abspath %s\n", absPathStr.c_str()); - return absPath; - } else { - // Windows behavior: if absPath == NULL, allocate new - auto *newBuf = static_cast(wibo::heap::guestMalloc((wResolved.size() + 1) * sizeof(uint16_t))); - std::copy(wResolved.begin(), wResolved.end(), newBuf); - newBuf[wResolved.size()] = 0; - - std::string absPathStr = wideStringToString(newBuf); - DEBUG_LOG("\t-> abspath %s\n", absPathStr.c_str()); - return newBuf; - } - - } -} - -#include "msvcrt_trampolines.h" - -extern const wibo::ModuleStub lib_msvcrt = { - (const char *[]){ - "msvcrt", - "msvcrt40", - "msvcr70", - "msvcr71", - "msvcr80", - "msvcr90", - "msvcr100", - nullptr, - }, - msvcrtThunkByName, - nullptr, -}; diff --git a/dll/msvcrt.h b/dll/msvcrt.h deleted file mode 100644 index c78d37e..0000000 --- a/dll/msvcrt.h +++ /dev/null @@ -1,240 +0,0 @@ -#pragma once - -#include "types.h" - -using TIME_T = int; -using WINT_T = unsigned short; - -typedef void(_CC_CDECL *_PVFV)(); -typedef int(_CC_CDECL *_PIFV)(); -typedef int(_CC_CDECL *_onexit_t)(); -typedef void(_CC_CDECL *signal_handler)(int); -typedef int(_CC_CDECL *sort_compare)(const void *, const void *); - -struct _utimbuf { - LONG actime; - LONG modtime; -}; - -struct _timeb { - TIME_T time; - unsigned short millitm; - short timezone; - short dstflag; -}; - -struct lconv; - -namespace msvcrt { - -extern int _commode; -extern int _fmode; -extern GUEST_PTR __initenv; -extern GUEST_PTR __winitenv; -extern GUEST_PTR _wpgmptr; -extern GUEST_PTR _pgmptr; -extern int __mb_cur_max; -extern _FILE _iob[_IOB_ENTRIES]; - -_FILE *CDECL __iob_func(); -_FILE *CDECL __p__iob(); -void CDECL setbuf(_FILE *stream, char *buffer); -void CDECL _splitpath(const char *path, char *drive, char *dir, char *fname, char *ext); -int CDECL _fileno(_FILE *stream); -int CDECL _getmbcp(); -GUEST_PTR CDECL __p___mb_cur_max(); -int CDECL _setmbcp(int codepage); -GUEST_PTR CDECL __p__mbctype(); -GUEST_PTR CDECL __p__pctype(); -int CDECL _isctype(int ch, int mask); -void CDECL __set_app_type(int at); -GUEST_PTR CDECL __p__fmode(); -GUEST_PTR CDECL __p__commode(); -void CDECL _initterm(GUEST_PTR *ppfn, GUEST_PTR *end); -int CDECL _initterm_e(GUEST_PTR *ppfn, GUEST_PTR *end); -unsigned int CDECL _controlfp(unsigned int newControl, unsigned int mask); -int CDECL _controlfp_s(unsigned int *currentControl, unsigned int newControl, unsigned int mask); -_onexit_t CDECL _onexit(_onexit_t func); -int CDECL __wgetmainargs(int *wargc, GUEST_PTR *wargv, GUEST_PTR *wenv, int doWildcard, int *startInfo); -int CDECL __getmainargs(int *argc, GUEST_PTR *argv, GUEST_PTR *env, int doWildcard, int *startInfo); -char *CDECL getenv(const char *varname); -GUEST_PTR CDECL __p___initenv(); -char *CDECL strcat(char *dest, const char *src); -char *CDECL strcpy(char *dest, const char *src); -int CDECL _access(const char *path, int mode); -int CDECL _ismbblead(unsigned int c); -int CDECL _ismbbtrail(unsigned int c); -int CDECL _ismbcspace(unsigned int c); -void CDECL _mbccpy(unsigned char *dest, const unsigned char *src); -unsigned char *CDECL _mbsinc(const unsigned char *str); -unsigned char *CDECL _mbsdec(const unsigned char *start, const unsigned char *current); -unsigned int CDECL _mbclen(const unsigned char *str); -int CDECL _mbscmp(const unsigned char *lhs, const unsigned char *rhs); -int CDECL _mbsicmp(const unsigned char *lhs, const unsigned char *rhs); -unsigned char *CDECL _mbsstr(const unsigned char *haystack, const unsigned char *needle); -unsigned char *CDECL _mbschr(const unsigned char *str, unsigned int ch); -unsigned char *CDECL _mbsrchr(const unsigned char *str, unsigned int ch); -unsigned char *CDECL _mbslwr(unsigned char *str); -unsigned char *CDECL _mbsupr(unsigned char *str); -unsigned char *CDECL _mbsinc_l(const unsigned char *str, void *); -unsigned char *CDECL _mbsdec_l(const unsigned char *start, const unsigned char *current, void *locale); -int CDECL _mbsncmp(const unsigned char *lhs, const unsigned char *rhs, SIZE_T count); -SIZE_T CDECL _mbsspn(const unsigned char *str, const unsigned char *set); -int CDECL _ismbcdigit(unsigned int ch); -int CDECL _stricmp(const char *lhs, const char *rhs); -int CDECL _strnicmp(const char *lhs, const char *rhs, SIZE_T count); -int CDECL _memicmp(const void *lhs, const void *rhs, SIZE_T count); -#ifndef __x86_64__ // TODO -int CDECL_NO_CONV _vsnprintf(char *buffer, SIZE_T count, const char *format, va_list args); -int CDECL_NO_CONV _snprintf(char *buffer, SIZE_T count, const char *format, ...); -int CDECL_NO_CONV sprintf(char *buffer, const char *format, ...); -int CDECL_NO_CONV printf(const char *format, ...); -int CDECL_NO_CONV sscanf(const char *buffer, const char *format, ...); -#endif -char *CDECL fgets(char *str, int count, _FILE *stream); -SIZE_T CDECL fread(void *buffer, SIZE_T size, SIZE_T count, _FILE *stream); -_FILE *CDECL _fsopen(const char *filename, const char *mode, int shflag); -int CDECL _sopen(const char *path, int oflag, int shflag, int pmode); -int CDECL _read(int fd, void *buffer, unsigned int count); -int CDECL _close(int fd); -LONG CDECL _lseek(int fd, LONG offset, int origin); -int CDECL _unlink(const char *path); -int CDECL _utime(const char *path, const _utimbuf *times); -int CDECL _chsize(int fd, LONG size); -char *CDECL strncpy(char *dest, const char *src, SIZE_T count); -char *CDECL strpbrk(const char *str, const char *accept); -char *CDECL strstr(const char *haystack, const char *needle); -char *CDECL strrchr(const char *str, int ch); -char *CDECL strtok(char *str, const char *delim); -LONG CDECL _adj_fdiv_r(LONG value); -void CDECL _adjust_fdiv(LONG n); -int CDECL _ftime(struct _timeb *timeptr); -ULONG CDECL _ultoa(ULONG value, char *str, int radix); -char *CDECL _ltoa(LONG value, char *str, int radix); -char *CDECL _makepath(char *path, const char *drive, const char *dir, const char *fname, const char *ext); -char *CDECL _fullpath(char *absPath, const char *relPath, SIZE_T maxLength); -int CDECL _putenv(const char *envString); -char *CDECL _mktemp(char *templateName); -int CDECL _except_handler3(void *record, void *frame, void *context, void *dispatch); -int CDECL getchar(); -TIME_T CDECL time(TIME_T *t); -char *CDECL __unDName(char *outputString, const char *mangledName, int maxStringLength, void *(*allocFunc)(SIZE_T), - void (*freeFunc)(void *), unsigned short); -char *CDECL setlocale(int category, const char *locale); -int CDECL _wdupenv_s(GUEST_PTR *buffer, SIZE_T *numberOfElements, const WCHAR *varname); -int CDECL _wgetenv_s(SIZE_T *pReturnValue, WCHAR *buffer, SIZE_T numberOfElements, const WCHAR *varname); -SIZE_T CDECL strlen(const char *str); -int CDECL strcmp(const char *lhs, const char *rhs); -int CDECL strncmp(const char *lhs, const char *rhs, SIZE_T count); -void CDECL _exit(int status); -int CDECL strcpy_s(char *dest, SIZE_T dest_size, const char *src); -int CDECL strcat_s(char *dest, SIZE_T numberOfElements, const char *src); -int CDECL strncpy_s(char *dest, SIZE_T dest_size, const char *src, SIZE_T count); -char *CDECL _strdup(const char *strSource); -ULONG CDECL strtoul(const char *str, GUEST_PTR *endptr, int base); -void *CDECL malloc(SIZE_T size); -void *CDECL calloc(SIZE_T count, SIZE_T size); -void *CDECL realloc(void *ptr, SIZE_T size); -void *CDECL _malloc_crt(SIZE_T size); -void CDECL _lock(int locknum); -void CDECL _unlock(int locknum); -_onexit_t CDECL __dllonexit(_onexit_t func, GUEST_PTR *pbegin, GUEST_PTR *pend); -void CDECL free(void *ptr); -void *CDECL memcpy(void *dest, const void *src, SIZE_T count); -void *CDECL memmove(void *dest, const void *src, SIZE_T count); -int CDECL memcmp(const void *lhs, const void *rhs, SIZE_T count); -void CDECL qsort(void *base, SIZE_T num, SIZE_T size, sort_compare compare); -int CDECL fflush(_FILE *stream); -#ifndef __x86_64__ // TODO -int CDECL_NO_CONV vfwprintf(_FILE *stream, const WCHAR *format, va_list args); -#endif -_FILE *CDECL fopen(const char *filename, const char *mode); -int CDECL _dup2(int fd1, int fd2); -int CDECL _isatty(int fd); -int CDECL fseek(_FILE *stream, LONG offset, int origin); -LONG CDECL ftell(_FILE *stream); -int CDECL feof(_FILE *stream); -int CDECL fputws(const WCHAR *str, _FILE *stream); -int CDECL _cputws(const WCHAR *string); -WCHAR *CDECL fgetws(WCHAR *buffer, int size, _FILE *stream); -WINT_T CDECL fgetwc(_FILE *stream); -int CDECL _wfopen_s(GUEST_PTR *stream, const WCHAR *filename, const WCHAR *mode); -int CDECL _wcsicmp(const WCHAR *lhs, const WCHAR *rhs); -int CDECL _wmakepath_s(WCHAR *path, SIZE_T sizeInWords, const WCHAR *drive, const WCHAR *dir, const WCHAR *fname, - const WCHAR *ext); -int CDECL _wputenv_s(const WCHAR *varname, const WCHAR *value); -ULONG CDECL wcsspn(const WCHAR *str1, const WCHAR *str2); -LONG CDECL _wtol(const WCHAR *str); -int CDECL _wcsupr_s(WCHAR *str, SIZE_T size); -int CDECL _wcslwr_s(WCHAR *str, SIZE_T size); -WINT_T CDECL towlower(WINT_T ch); -unsigned int CDECL _mbctolower(unsigned int ch); -int CDECL toupper(int ch); -int CDECL tolower(int ch); -int CDECL _ftime64_s(void *timeb); -int CDECL _crt_debugger_hook(int value); -int CDECL _configthreadlocale(int mode); -void CDECL __setusermatherr(void *handler); -void CDECL _cexit(); -#ifndef __x86_64__ // TODO -int CDECL_NO_CONV vfprintf(_FILE *stream, const char *format, va_list args); -int CDECL_NO_CONV fprintf(_FILE *stream, const char *format, ...); -#endif -int CDECL fputc(int ch, _FILE *stream); -SIZE_T CDECL fwrite(const void *buffer, SIZE_T size, SIZE_T count, _FILE *stream); -char *CDECL strerror(int errnum); -char *CDECL strchr(const char *str, int character); -struct lconv *CDECL localeconv(); -signal_handler CDECL signal(int sig, signal_handler handler); -SIZE_T CDECL wcslen(const WCHAR *str); -void CDECL abort(); -int CDECL atoi(const char *str); -int CDECL _amsg_exit(int reason); -void CDECL _invoke_watson(const WCHAR *, const WCHAR *, const WCHAR *, UINT, UINT_PTR); -void CDECL terminateShim(); -int CDECL _purecall(); -int CDECL _except_handler4_common(void *, void *, void *, void *); -LONG CDECL _XcptFilter(ULONG code, void *); -int CDECL _get_wpgmptr(GUEST_PTR *pValue); -GUEST_PTR CDECL __p__pgmptr(); -int CDECL _wsplitpath_s(const WCHAR *path, WCHAR *drive, SIZE_T driveNumberOfElements, WCHAR *dir, - SIZE_T dirNumberOfElements, WCHAR *fname, SIZE_T nameNumberOfElements, WCHAR *ext, - SIZE_T extNumberOfElements); -int CDECL wcscat_s(WCHAR *strDestination, SIZE_T numberOfElements, const WCHAR *strSource); -WCHAR *CDECL _wcsdup(const WCHAR *strSource); -int CDECL _waccess_s(const WCHAR *path, int mode); -void *CDECL memset(void *s, int c, SIZE_T n); -int CDECL wcsncpy_s(WCHAR *strDest, SIZE_T numberOfElements, const WCHAR *strSource, SIZE_T count); -int CDECL wcsncat_s(WCHAR *strDest, SIZE_T numberOfElements, const WCHAR *strSource, SIZE_T count); -int CDECL _itow_s(int value, WCHAR *buffer, SIZE_T size, int radix); -int CDECL _wtoi(const WCHAR *str); -int CDECL _ltoa_s(LONG value, char *buffer, SIZE_T sizeInChars, int radix); -int CDECL wcscpy_s(WCHAR *dest, SIZE_T dest_size, const WCHAR *src); -#ifndef __x86_64__ // TODO -int CDECL_NO_CONV swprintf_s(WCHAR *buffer, SIZE_T sizeOfBuffer, const WCHAR *format, ...); -int CDECL_NO_CONV swscanf_s(const WCHAR *buffer, const WCHAR *format, ...); -#endif -int *CDECL _get_osfhandle(int fd); -int CDECL _write(int fd, const void *buffer, unsigned int count); -void CDECL exit(int status); -int CDECL wcsncmp(const WCHAR *string1, const WCHAR *string2, SIZE_T count); -#ifndef __x86_64__ // TODO -int CDECL_NO_CONV _vswprintf_c_l(WCHAR *buffer, SIZE_T size, const WCHAR *format, ...); -#endif -const WCHAR *CDECL wcsstr(const WCHAR *dest, const WCHAR *src); -int CDECL iswspace(WINT_T w); -int CDECL iswdigit(WINT_T w); -const WCHAR *CDECL wcschr(const WCHAR *str, WCHAR c); -const WCHAR *CDECL wcsrchr(const WCHAR *str, WCHAR c); -ULONG CDECL wcstoul(const WCHAR *strSource, GUEST_PTR *endptr, int base); -_FILE *CDECL _wfsopen(const WCHAR *filename, const WCHAR *mode, int shflag); -int CDECL puts(const char *str); -int CDECL fclose(_FILE *stream); -int CDECL _flushall(); -int *CDECL _errno(); -LONG_PTR CDECL _wspawnvp(int mode, const WCHAR *cmdname, GUEST_PTR *argv); -LONG_PTR CDECL _spawnvp(int mode, const char *cmdname, GUEST_PTR *argv); -int CDECL _wunlink(const WCHAR *filename); -WCHAR *CDECL _wfullpath(WCHAR *absPath, const WCHAR *relPath, SIZE_T maxLength); - -} // namespace msvcrt diff --git a/src/async_io.cpp b/src/async_io.cpp index 6730175..6b99333 100644 --- a/src/async_io.cpp +++ b/src/async_io.cpp @@ -49,7 +49,9 @@ static constexpr BackendEntry kBackends[] = { #if WIBO_ENABLE_LIBURING {"io_uring", detail::createIoUringBackend}, #endif +#ifdef __linux__ {"epoll", detail::createEpollBackend}, +#endif }; AsyncIOBackend &asyncIO() { diff --git a/src/async_io.h b/src/async_io.h index 715c8f2..d407186 100644 --- a/src/async_io.h +++ b/src/async_io.h @@ -24,7 +24,9 @@ namespace detail { #if WIBO_ENABLE_LIBURING std::unique_ptr createIoUringBackend(); #endif +#ifdef __linux__ std::unique_ptr createEpollBackend(); +#endif } // namespace detail diff --git a/src/heap.cpp b/src/heap.cpp index 4265ab2..a005837 100644 --- a/src/heap.cpp +++ b/src/heap.cpp @@ -17,15 +17,17 @@ #include #include +#ifdef __linux__ // Alpine hack: rename duplicate prctl_mm_map (sys/prctl.h also includes it) #define prctl_mm_map _prctl_mm_map #include #undef prctl_mm_map +#include +#endif #include #include #include -#include #include // Pre-initialization logging macros @@ -36,8 +38,14 @@ namespace { constexpr uintptr_t kLowMemoryStart = 0x00110000UL; // 1 MiB + 64 KiB constexpr uintptr_t kHeapMax = 0x60000000UL; // 1 GiB +#ifdef __APPLE__ +// On macOS, our program is mapped at 0x7E001000 +constexpr uintptr_t kTopDownStart = 0x7D000000UL; +constexpr uintptr_t kTwoGB = 0x7E000000UL; +#else constexpr uintptr_t kTopDownStart = 0x7F000000UL; // Just below 2GB constexpr uintptr_t kTwoGB = 0x80000000UL; +#endif constexpr std::size_t kGuestArenaSize = 512ULL * 1024ULL * 1024ULL; // 512 MiB constexpr std::size_t kVirtualAllocationGranularity = 64ULL * 1024ULL; @@ -69,6 +77,7 @@ struct VirtualAllocation { std::map g_virtualAllocations; +#ifndef __APPLE__ const uintptr_t kDefaultMmapMinAddr = 0x10000u; uintptr_t readMmapMinAddr() { @@ -98,6 +107,7 @@ uintptr_t mmapMinAddr() { static uintptr_t minAddr = readMmapMinAddr(); return minAddr; } +#endif uintptr_t alignDown(uintptr_t value, std::size_t alignment) { const uintptr_t mask = static_cast(alignment) - 1; @@ -311,9 +321,13 @@ bool mapAtAddr(uintptr_t addr, std::size_t size, const char *name, void **outPtr if (p == MAP_FAILED) { return false; } +#ifdef __linux__ if (name) { prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, addr, size, name); } +#else + (void)name; +#endif recordGuestMapping(addr, size, PAGE_READWRITE, MEM_RESERVE, PAGE_READWRITE, MEM_PRIVATE); if (outPtr) { *outPtr = p; @@ -668,11 +682,13 @@ VmStatus virtualAlloc(void **baseAddress, std::size_t *regionSize, DWORD allocat if (mapped == MAP_FAILED) { return vmStatusFromErrno(errno); } +#ifdef __linux__ if (type == MEM_IMAGE) { prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, base, length, "wibo guest image"); } else { prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, base, length, "wibo guest allocated"); } +#endif uintptr_t actualBase = reinterpret_cast(mapped); VirtualAllocation allocation{}; allocation.base = actualBase; @@ -739,7 +755,9 @@ VmStatus virtualAlloc(void **baseAddress, std::size_t *regionSize, DWORD allocat if (res == MAP_FAILED) { return vmStatusFromErrno(errno); } +#ifdef __linux__ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, run.first, run.second, "wibo guest committed"); +#endif markCommitted(*region, run.first, run.second, protect); } @@ -791,7 +809,9 @@ VmStatus virtualFree(void *baseAddress, std::size_t regionSize, DWORD freeType) if (res == MAP_FAILED) { return vmStatusFromErrno(errno); } +#ifdef __linux__ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, base, length, "wibo reserved"); +#endif eraseGuestMapping(base); return VmStatus::Success; } @@ -828,7 +848,9 @@ VmStatus virtualFree(void *baseAddress, std::size_t regionSize, DWORD freeType) if (res == MAP_FAILED) { return vmStatusFromErrno(errno); } +#ifdef __linux__ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, res, length, "wibo reserved"); +#endif markDecommitted(region, start, length); refreshGuestMapping(region); return VmStatus::Success; @@ -1043,6 +1065,7 @@ bool reserveGuestStack(std::size_t stackSizeBytes, void **outStackLimit, void ** } // namespace wibo::heap +#ifndef __APPLE__ static void debugPrintMaps() { char buf[1024]; int fd = open("/proc/self/maps", O_RDONLY); @@ -1194,7 +1217,9 @@ static size_t blockLower2GB(MEMORY_BASIC_INFORMATION mappings[MAX_NUM_MAPPINGS]) perror("heap: failed reserve memory"); exit(1); } +#ifdef __linux__ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, len, "wibo reserved"); +#endif } lastMapEnd = mapEnd; @@ -1203,27 +1228,36 @@ static size_t blockLower2GB(MEMORY_BASIC_INFORMATION mappings[MAX_NUM_MAPPINGS]) return numMappings; } +#endif #if defined(__clang__) __attribute__((constructor(101))) #else __attribute__((constructor)) #endif -__attribute__((used)) static void wibo_heap_constructor() { +__attribute__((used)) static void +wibo_heap_constructor() { +#ifndef __APPLE__ MEMORY_BASIC_INFORMATION mappings[MAX_NUM_MAPPINGS]; memset(mappings, 0, sizeof(mappings)); +#endif bool debug = getenv("WIBO_DEBUG_HEAP") != nullptr; if (debug) { LOG_OUT("heap: initializing...\n"); +#ifndef __APPLE__ debugPrintMaps(); +#endif } +#ifndef __APPLE__ size_t numMappings = blockLower2GB(mappings); +#endif // Now we can allocate memory if (debug) { mi_option_enable(mi_option_show_stats); mi_option_enable(mi_option_verbose); } g_mappings = new std::map; +#ifndef __APPLE__ for (size_t i = 0; i < numMappings; ++i) { if (debug) { fprintf(stderr, "Existing %zu: BaseAddress=%x, RegionSize=%u\n", i, mappings[i].BaseAddress, @@ -1231,4 +1265,5 @@ __attribute__((used)) static void wibo_heap_constructor() { } g_mappings->emplace(reinterpret_cast(fromGuestPtr(mappings[i].BaseAddress)), mappings[i]); } +#endif } diff --git a/src/macros.S b/src/macros.S index 17c491f..072e66e 100644 --- a/src/macros.S +++ b/src/macros.S @@ -13,22 +13,42 @@ #ifdef __x86_64__ .equ TEB_SP, 0xfa0 # CurrentStackPointer -.equ TEB_FSBASE, 0xfa8 # CurrentFsBase +.equ TEB_FSBASE, 0xfa8 # HostFsBase +.equ TEB_GSBASE, 0xfb0 # HostGsBase +#ifdef __linux__ .equ CS_32, 0x23 # 32-bit code segment (Linux) .equ CS_64, 0x33 # 64-bit code segment (Linux) .equ DS_32, 0x2b # 32-bit data segment (Linux) +#elif defined(__APPLE__) +.equ CS_64, 0x2b # 64-bit code segment (macOS) +#else +#error "Unsupported platform" +#endif -.macro LJMP32 +.macro LJMP32 teb_reg +#ifdef __APPLE__ +#define m1632 m1632_\@ +.data +m1632: + .long 1f # 32-bit code offset + .long 0 # 32-bit code segment (filled in at runtime) +.text + mov r10w, word ptr [\teb_reg+TEB_FS_SEL] + sub r10w, 16 + mov word ptr [rip+m1632+4], r10w + jmp fword ptr [rip+m1632] +#else jmp fword ptr [rip] # far jump into 32-bit code .long 1f # 32-bit code offset - .word CS_32 # 32-bit code segment (Linux) + .word CS_32 # 32-bit code segment +#endif .code32 1: endbr32 .endm -.macro LJMP64 +.macro LJMP64 teb_reg // Annoyingly, we can't assemble this in Intel syntax .att_syntax prefix ljmp $CS_64, $1f @@ -39,3 +59,19 @@ .endm #endif // __x86_64__ + +.macro GET_TEB_HOST reg + +#if defined(__APPLE__) && defined(__x86_64__) +// TLS slot 6 reserved for Win64 compatibility +// https://github.com/apple/darwin-libpthread/blob/03c4628c8940cca6fd6a82957f683af804f62e7f/private/tsd_private.h#L92-L97 +mov \reg, gs:[0x30] +#elif defined(__linux__) && defined(__x86_64__) +mov \reg, fs:[currentThreadTeb@tpoff] +#elif defined(__linux__) && defined(__i386__) +mov \reg, gs:[currentThreadTeb@ntpoff] +#else +#error "Unsupported platform" +#endif + +.endm diff --git a/src/main.cpp b/src/main.cpp index 9e2289c..7de5e9c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,12 +10,6 @@ #include "types.h" #include "version_info.h" -#ifdef __x86_64__ -#include "setup.h" -#endif - -#include -#include #include #include #include @@ -26,9 +20,17 @@ #include #include #include -#include #include -#include + +#ifdef __x86_64__ +#include "setup.h" +#endif + +#ifdef __linux__ +#include +#include +#include +#endif char **wibo::argv; int wibo::argc; @@ -49,7 +51,11 @@ void wibo::debug_log(const char *fmt, ...) { for (size_t i = 0; i < wibo::debugIndent; i++) fprintf(stderr, "\t"); pthread_t threadId = pthread_self(); +#ifdef __APPLE__ + fprintf(stderr, "[thread %p] ", threadId); +#else fprintf(stderr, "[thread %lu] ", threadId); +#endif vfprintf(stderr, fmt, args); fflush(stderr); } @@ -101,8 +107,8 @@ bool wibo::installTibForCurrentThread(TEB *tibPtr) { currentThreadTeb = tibPtr; #ifdef __x86_64__ - tibEntryNumber = x86_64_thread_setup(tibEntryNumber, tibPtr); - if (tibEntryNumber == -1 || tibPtr->CurrentFsSelector == 0) { + tibEntryNumber = tebThreadSetup(tibEntryNumber, tibPtr); + if (tibEntryNumber < 0 || tibPtr->CurrentFsSelector == 0) { perror("x86_64_thread_setup failed"); return false; } diff --git a/src/modules.cpp b/src/modules.cpp index 3f133cd..c8cbe91 100644 --- a/src/modules.cpp +++ b/src/modules.cpp @@ -175,9 +175,8 @@ StubFuncType resolveMissingFuncName(const char *dllName, const char *funcName) { } StubFuncType resolveMissingFuncOrdinal(const char *dllName, uint16_t ordinal) { - char buf[16]; - sprintf(buf, "%d", ordinal); - return resolveMissingFuncName(dllName, buf); + std::string funcName = std::to_string(ordinal); + return resolveMissingFuncName(dllName, funcName.c_str()); } struct ModuleRegistry { diff --git a/src/modules.h b/src/modules.h index fae0290..716be8b 100644 --- a/src/modules.h +++ b/src/modules.h @@ -2,7 +2,6 @@ #include "common.h" #include "entry.h" -#include "msvcrt.h" #include "tls.h" #include "types.h" diff --git a/src/processes.h b/src/processes.h index 8ced5dd..c1790c8 100644 --- a/src/processes.h +++ b/src/processes.h @@ -3,34 +3,47 @@ #include "kernel32/internal.h" #include +#include #include #include -#include #include using kernel32::ProcessObject; namespace wibo { +namespace detail { + +class ProcessManagerImpl { + public: + virtual ~ProcessManagerImpl() = default; + virtual bool init() = 0; + virtual void shutdown() = 0; + virtual bool addProcess(Pin po) = 0; + [[nodiscard]] virtual bool running() const = 0; +}; + +struct SpawnProcessInfo { + pid_t pid = -1; + int pidfd = -1; +}; + +std::unique_ptr createProcessManagerImpl(); +int spawnProcess(char *const argv[], char *const envp[], SpawnProcessInfo &info); + +} // namespace detail + class ProcessManager { public: + ProcessManager(); ~ProcessManager(); bool init(); void shutdown(); bool addProcess(Pin po); - bool running() const { return mRunning.load(std::memory_order_acquire); } + [[nodiscard]] bool running() const; private: - void runLoop(); - void wake() const; - void checkPidfd(int pidfd); - - mutable std::shared_mutex m; - std::atomic mRunning = false; - std::thread mThread; - int mEpollFd = -1; - int mWakeFd = -1; - std::unordered_map> mReg; + std::unique_ptr mImpl; }; ProcessManager &processes(); diff --git a/src/processes.cpp b/src/processes_common.cpp similarity index 63% rename from src/processes.cpp rename to src/processes_common.cpp index 29ea7ff..a5e99a8 100644 --- a/src/processes.cpp +++ b/src/processes_common.cpp @@ -1,223 +1,60 @@ #include "processes.h" + #include "common.h" #include "files.h" #include "handles.h" #include "kernel32/internal.h" + #include #include #include #include #include #include -#include #include -#include -#include #include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include #include -namespace { +extern char **environ; -inline DWORD decodeExitCode(const siginfo_t &si) { - switch (si.si_code) { - case CLD_EXITED: - return static_cast(si.si_status); - case CLD_KILLED: - case CLD_DUMPED: - return 0xC0000000u | static_cast(si.si_status); - default: - return 0; - } -} - -} // namespace +using kernel32::ProcessObject; namespace wibo { -ProcessManager::~ProcessManager() { shutdown(); } +ProcessManager::ProcessManager() : mImpl(detail::createProcessManagerImpl()) {} + +ProcessManager::~ProcessManager() = default; bool ProcessManager::init() { - if (mRunning.load(std::memory_order_acquire)) { - return true; - } - - mEpollFd = epoll_create1(EPOLL_CLOEXEC); - if (mEpollFd < 0) { - perror("epoll_create1"); + if (!mImpl) { return false; } - - mWakeFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); - if (mWakeFd < 0) { - perror("eventfd"); - close(mEpollFd); - mEpollFd = -1; - return false; - } - - epoll_event ev{}; - ev.events = EPOLLIN; - ev.data.fd = mWakeFd; - if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeFd, &ev) < 0) { - perror("epoll_ctl"); - close(mWakeFd); - mWakeFd = -1; - close(mEpollFd); - mEpollFd = -1; - return false; - } - - mRunning.store(true, std::memory_order_release); - mThread = std::thread(&ProcessManager::runLoop, this); - DEBUG_LOG("ProcessManager initialized\n"); - return true; + return mImpl->init(); } void ProcessManager::shutdown() { - if (!mRunning.exchange(false, std::memory_order_acq_rel)) { - return; - } - wake(); - if (mThread.joinable()) { - mThread.join(); - } - std::lock_guard lk(m); - mReg.clear(); - if (mWakeFd >= 0) { - close(mWakeFd); - mWakeFd = -1; - } - if (mEpollFd >= 0) { - close(mEpollFd); - mEpollFd = -1; + if (mImpl) { + mImpl->shutdown(); } } bool ProcessManager::addProcess(Pin po) { - if (!po) { + if (!mImpl) { return false; } - pid_t pid; - int pidfd; - { - std::lock_guard lk(po->m); - pid = po->pid; - pidfd = po->pidfd; - if (pidfd < 0) { - return false; - } - - epoll_event ev{}; - ev.events = EPOLLIN; - ev.data.fd = pidfd; - if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, pidfd, &ev) < 0) { - perror("epoll_ctl"); - close(pidfd); - po->pidfd = -1; - return false; - } - } - { - std::lock_guard lk(m); - mReg.emplace(pidfd, std::move(po)); - } - DEBUG_LOG("ProcessManager: registered pid %d with pidfd %d\n", pid, pidfd); - wake(); - return true; + return mImpl->addProcess(std::move(po)); } -void ProcessManager::runLoop() { - constexpr int kMaxEvents = 64; - std::array events{}; - while (mRunning.load(std::memory_order_acquire)) { - int n = epoll_wait(mEpollFd, events.data(), kMaxEvents, -1); - if (n < 0) { - if (errno == EINTR) { - continue; - } - perror("epoll_wait"); - break; - } - for (int i = 0; i < n; ++i) { - const auto &ev = events[i]; - if (ev.data.fd == mWakeFd) { - // Drain eventfd - uint64_t n; - while (read(mWakeFd, &n, sizeof(n)) == sizeof(n)) { - } - continue; - } - checkPidfd(ev.data.fd); - } - } -} - -void ProcessManager::wake() const { - if (mWakeFd < 0) { - return; - } - uint64_t n = 1; - ssize_t r [[maybe_unused]] = write(mWakeFd, &n, sizeof(n)); -} - -void ProcessManager::checkPidfd(int pidfd) { - DEBUG_LOG("ProcessManager: checking pidfd %d\n", pidfd); - - siginfo_t si{}; - si.si_code = CLD_DUMPED; - if (pidfd >= 0) { - int rc = waitid(P_PIDFD, pidfd, &si, WEXITED | WNOHANG); - if (rc < 0) { - // TODO: what to do here? - perror("waitid"); - } else if (rc == 0 && si.si_pid == 0) { - return; - } - epoll_ctl(mEpollFd, EPOLL_CTL_DEL, pidfd, nullptr); - } - - DEBUG_LOG("ProcessManager: pidfd %d exited: code=%d status=%d\n", pidfd, si.si_code, si.si_status); - - Pin po; - { - std::unique_lock lk(m); - auto it = mReg.find(pidfd); - if (it != mReg.end()) { - po = std::move(it->second); - mReg.erase(it); - } - } - close(pidfd); - if (!po) { - return; - } - { - std::lock_guard lk(po->m); - po->signaled = true; - po->pidfd = -1; - if (!po->forcedExitCode) { - po->exitCode = decodeExitCode(si); - } - } - po->cv.notify_all(); - po->notifyWaiters(false); +bool ProcessManager::running() const { + return mImpl && mImpl->running(); } ProcessManager &processes() { static ProcessManager mgr; if (!mgr.init()) { - fprintf(stderr, "Failed to initialize ProcessManager\n"); - abort(); + std::fprintf(stderr, "Failed to initialize ProcessManager\n"); + std::abort(); } return mgr; } @@ -307,7 +144,7 @@ static std::vector buildSearchDirectories() { } }; addFromEnv("WIBO_PATH"); - addFromEnv("WINEPATH"); // Wine compatibility + addFromEnv("WINEPATH"); addFromEnv("PATH"); return dirs; } @@ -371,22 +208,6 @@ std::optional resolveExecutable(const std::string &comman return std::nullopt; } -static int spawnClone(pid_t &pid, int &pidfd, char **argv, char **envp) { - pid = static_cast(syscall(SYS_clone, CLONE_PIDFD, nullptr, &pidfd)); - if (pid < 0) { - int err = errno; - perror("clone"); - return err; - } else if (pid == 0) { - prctl(PR_SET_PDEATHSIG, SIGKILL); - execve("/proc/self/exe", argv, envp); - // If we're still here, something went wrong - perror("execve"); - _exit(127); - } - return 0; -} - static int spawnInternal(const std::vector &args, Pin &pinOut) { std::vector argv; argv.reserve(args.size() + 2); @@ -423,20 +244,19 @@ static int spawnInternal(const std::vector &args, Pin(s.c_str())); envp.push_back(nullptr); - pid_t pid = -1; - int pidfd = -1; - int rc = spawnClone(pid, pidfd, argv.data(), envp.data()); + detail::SpawnProcessInfo info; + int rc = detail::spawnProcess(argv.data(), envp.data(), info); if (rc != 0) { return rc; } - DEBUG_LOG("Spawned process with PID %d (pidfd=%d)\n", pid, pidfd); + DEBUG_LOG("Spawned process with PID %d (pidfd=%d)\n", info.pid, info.pidfd); - auto obj = make_pin(pid, pidfd); + auto obj = make_pin(info.pid, info.pidfd, true); pinOut = obj.clone(); if (!processes().addProcess(std::move(obj))) { - fprintf(stderr, "Failed to add process to process manager\n"); - abort(); + std::fprintf(stderr, "Failed to add process to process manager\n"); + std::abort(); } return 0; } @@ -531,3 +351,4 @@ std::vector splitCommandLine(const char *commandLine) { } } // namespace wibo + diff --git a/src/processes_darwin.cpp b/src/processes_darwin.cpp new file mode 100644 index 0000000..2969833 --- /dev/null +++ b/src/processes_darwin.cpp @@ -0,0 +1,278 @@ +#include "processes.h" + +#include "common.h" +#include "handles.h" +#include "kernel32/internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ENUM_DYLD_BOOL +#include + +using kernel32::ProcessObject; + +namespace { + +DWORD decodeExitStatus(int status) { + if (WIFEXITED(status)) { + return static_cast(WEXITSTATUS(status)); + } + if (WIFSIGNALED(status)) { + return 0xC0000000u | static_cast(WTERMSIG(status)); + } + return 0; +} + +std::string &executablePath() { + static std::string path; + static std::once_flag once; + std::call_once(once, [] { + uint32_t size = 0; + if (_NSGetExecutablePath(nullptr, &size) != 0 && size > 0) { + std::string buffer(size, '\0'); + if (_NSGetExecutablePath(buffer.data(), &size) == 0) { + std::error_code ec; + auto canonical = std::filesystem::weakly_canonical(buffer.c_str(), ec); + if (!ec) { + path = canonical.string(); + } else { + path.assign(buffer.c_str()); + } + } + } + if (path.empty()) { + path = "wibo"; + } + }); + return path; +} + +class DarwinProcessManager final : public wibo::detail::ProcessManagerImpl { + public: + bool init() override; + void shutdown() override; + bool addProcess(Pin po) override; + [[nodiscard]] bool running() const override { return mRunning.load(std::memory_order_acquire); } + + private: + void runLoop(); + void wake() const; + void handleExit(pid_t pid); + + mutable std::shared_mutex m; + std::atomic mRunning{false}; + std::thread mThread; + int mKqueueFd = -1; + uintptr_t mWakeIdent = 1; + std::unordered_map> mReg; +}; + +} // namespace + +namespace wibo::detail { + +std::unique_ptr createProcessManagerImpl() { + return std::make_unique(); +} + +int spawnProcess(char *const argv[], char *const envp[], SpawnProcessInfo &info) { + auto &path = executablePath(); + posix_spawnattr_t attr; + int rc = posix_spawnattr_init(&attr); + if (rc != 0) { + return rc; + } + sigset_t mask; + sigemptyset(&mask); + rc = posix_spawnattr_setsigmask(&attr, &mask); + if (rc == 0) { + rc = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGMASK); + } + pid_t pid = -1; + if (rc == 0) { + rc = posix_spawn(&pid, path.c_str(), nullptr, &attr, argv, envp); + } + posix_spawnattr_destroy(&attr); + if (rc != 0) { + return rc; + } + info.pid = pid; + info.pidfd = -1; + return 0; +} + +} // namespace wibo::detail + +bool DarwinProcessManager::init() { + if (mRunning.load(std::memory_order_acquire)) { + return true; + } + + mKqueueFd = kqueue(); + if (mKqueueFd < 0) { + perror("kqueue"); + return false; + } + + struct kevent kev; + EV_SET(&kev, mWakeIdent, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, nullptr); + if (kevent(mKqueueFd, &kev, 1, nullptr, 0, nullptr) < 0) { + perror("kevent(EV_ADD user)"); + close(mKqueueFd); + mKqueueFd = -1; + return false; + } + + mRunning.store(true, std::memory_order_release); + mThread = std::thread(&DarwinProcessManager::runLoop, this); + DEBUG_LOG("ProcessManager (Darwin) initialized\n"); + return true; +} + +void DarwinProcessManager::shutdown() { + if (!mRunning.exchange(false, std::memory_order_acq_rel)) { + return; + } + wake(); + if (mThread.joinable()) { + mThread.join(); + } + std::lock_guard lk(m); + mReg.clear(); + if (mKqueueFd >= 0) { + close(mKqueueFd); + mKqueueFd = -1; + } +} + +bool DarwinProcessManager::addProcess(Pin po) { + if (!po) { + return false; + } + pid_t pid; + { + std::lock_guard lk(po->m); + pid = po->pid; + } + struct kevent kev; + EV_SET(&kev, static_cast(pid), EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT | NOTE_EXITSTATUS, 0, nullptr); + if (kevent(mKqueueFd, &kev, 1, nullptr, 0, nullptr) < 0) { + int err = errno; + DEBUG_LOG("ProcessManager: kevent add for pid %d failed: %s\n", pid, strerror(err)); + if (err == ESRCH) { + int status = 0; + pid_t waited = waitpid(pid, &status, WNOHANG); + if (waited <= 0) { + waitpid(pid, &status, 0); + } + { + std::lock_guard lk(po->m); + po->signaled = true; + po->pidfd = -1; + if (!po->forcedExitCode) { + po->exitCode = decodeExitStatus(status); + } + } + po->cv.notify_all(); + po->notifyWaiters(false); + return true; + } + return false; + } + { + std::lock_guard lk(m); + mReg.emplace(pid, std::move(po)); + } + DEBUG_LOG("ProcessManager: registered pid %d\n", pid); + wake(); + return true; +} + +void DarwinProcessManager::runLoop() { + constexpr int kMaxEvents = 64; + std::array events{}; + while (mRunning.load(std::memory_order_acquire)) { + int n = kevent(mKqueueFd, nullptr, 0, events.data(), kMaxEvents, nullptr); + if (n < 0) { + if (errno == EINTR) { + continue; + } + perror("kevent"); + break; + } + for (int i = 0; i < n; ++i) { + const auto &ev = events[i]; + if (ev.filter == EVFILT_USER) { + continue; + } + if (ev.filter == EVFILT_PROC && (ev.fflags & NOTE_EXIT)) { + handleExit(static_cast(ev.ident)); + } + } + } +} + +void DarwinProcessManager::wake() const { + if (mKqueueFd < 0) { + return; + } + struct kevent kev; + EV_SET(&kev, mWakeIdent, EVFILT_USER, 0, NOTE_TRIGGER, 0, nullptr); + kevent(mKqueueFd, &kev, 1, nullptr, 0, nullptr); +} + +void DarwinProcessManager::handleExit(pid_t pid) { + Pin po; + { + std::unique_lock lk(m); + auto it = mReg.find(pid); + if (it != mReg.end()) { + po = std::move(it->second); + mReg.erase(it); + } + } + if (!po) { + // Might be a race with registration; still ensure we reap the child. + int status = 0; + waitpid(pid, &status, WNOHANG); + return; + } + int status = 0; + pid_t waited = waitpid(pid, &status, WNOHANG); + if (waited == 0) { + // Child still around; block to reap. + waited = waitpid(pid, &status, 0); + } + if (waited < 0) { + int err = errno; + DEBUG_LOG("ProcessManager: waitpid(%d) failed: %s\n", pid, strerror(err)); + status = 0; + } + { + std::lock_guard lk(po->m); + po->signaled = true; + po->pidfd = -1; + if (!po->forcedExitCode) { + po->exitCode = decodeExitStatus(status); + } + } + po->cv.notify_all(); + po->notifyWaiters(false); +} diff --git a/src/processes_linux.cpp b/src/processes_linux.cpp new file mode 100644 index 0000000..0a27f53 --- /dev/null +++ b/src/processes_linux.cpp @@ -0,0 +1,261 @@ +#include "processes.h" + +#include "common.h" +#include "handles.h" +#include "kernel32/internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using kernel32::ProcessObject; + +namespace { + +inline DWORD decodeExitCode(const siginfo_t &si) { + switch (si.si_code) { + case CLD_EXITED: + return static_cast(si.si_status); + case CLD_KILLED: + case CLD_DUMPED: + return 0xC0000000u | static_cast(si.si_status); + default: + return 0; + } +} + +class LinuxProcessManager final : public wibo::detail::ProcessManagerImpl { + public: + bool init() override; + void shutdown() override; + bool addProcess(Pin po) override; + [[nodiscard]] bool running() const override { return mRunning.load(std::memory_order_acquire); } + + private: + void runLoop(); + void wake() const; + void checkPidfd(int pidfd); + + mutable std::shared_mutex m; + std::atomic mRunning{false}; + std::thread mThread; + int mEpollFd = -1; + int mWakeFd = -1; + std::unordered_map> mReg; +}; + +} // namespace + +namespace wibo::detail { + +std::unique_ptr createProcessManagerImpl() { + return std::make_unique(); +} + +int spawnProcess(char *const argv[], char *const envp[], SpawnProcessInfo &info) { + pid_t pid = static_cast(syscall(SYS_clone, CLONE_PIDFD, nullptr, &info.pidfd)); + if (pid < 0) { + info.pidfd = -1; + int err = errno; + perror("clone"); + return err; + } + if (pid == 0) { + if (prctl(PR_SET_PDEATHSIG, SIGKILL) != 0) { + perror("prctl(PR_SET_PDEATHSIG)"); + } + execve("/proc/self/exe", argv, envp); + perror("execve"); + _Exit(127); + } + info.pid = pid; + return 0; +} + +} // namespace wibo::detail + +namespace { + +bool epollAdd(int epollFd, int fd) { + epoll_event ev{}; + ev.events = EPOLLIN; + ev.data.fd = fd; + if (epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &ev) < 0) { + perror("epoll_ctl"); + return false; + } + return true; +} + +} // namespace + +bool LinuxProcessManager::init() { + if (mRunning.load(std::memory_order_acquire)) { + return true; + } + + mEpollFd = epoll_create1(EPOLL_CLOEXEC); + if (mEpollFd < 0) { + perror("epoll_create1"); + return false; + } + + mWakeFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (mWakeFd < 0) { + perror("eventfd"); + close(mEpollFd); + mEpollFd = -1; + return false; + } + + if (!epollAdd(mEpollFd, mWakeFd)) { + close(mWakeFd); + mWakeFd = -1; + close(mEpollFd); + mEpollFd = -1; + return false; + } + + mRunning.store(true, std::memory_order_release); + mThread = std::thread(&LinuxProcessManager::runLoop, this); + DEBUG_LOG("ProcessManager (Linux) initialized\n"); + return true; +} + +void LinuxProcessManager::shutdown() { + if (!mRunning.exchange(false, std::memory_order_acq_rel)) { + return; + } + wake(); + if (mThread.joinable()) { + mThread.join(); + } + std::lock_guard lk(m); + mReg.clear(); + if (mWakeFd >= 0) { + close(mWakeFd); + mWakeFd = -1; + } + if (mEpollFd >= 0) { + close(mEpollFd); + mEpollFd = -1; + } +} + +bool LinuxProcessManager::addProcess(Pin po) { + if (!po) { + return false; + } + pid_t pid; + int pidfd; + { + std::lock_guard lk(po->m); + pid = po->pid; + pidfd = po->pidfd; + if (pidfd < 0) { + return false; + } + if (!epollAdd(mEpollFd, pidfd)) { + close(pidfd); + po->pidfd = -1; + return false; + } + } + { + std::lock_guard lk(m); + mReg.emplace(pidfd, std::move(po)); + } + DEBUG_LOG("ProcessManager: registered pid %d with pidfd %d\n", pid, pidfd); + wake(); + return true; +} + +void LinuxProcessManager::runLoop() { + constexpr int kMaxEvents = 64; + std::array events{}; + while (mRunning.load(std::memory_order_acquire)) { + int n = epoll_wait(mEpollFd, events.data(), kMaxEvents, -1); + if (n < 0) { + if (errno == EINTR) { + continue; + } + perror("epoll_wait"); + break; + } + for (int i = 0; i < n; ++i) { + const auto &ev = events[i]; + if (ev.data.fd == mWakeFd) { + uint64_t value; + while (read(mWakeFd, &value, sizeof(value)) == sizeof(value)) { + } + continue; + } + checkPidfd(ev.data.fd); + } + } +} + +void LinuxProcessManager::wake() const { + if (mWakeFd < 0) { + return; + } + uint64_t n = 1; + ssize_t r [[maybe_unused]] = write(mWakeFd, &n, sizeof(n)); +} + +void LinuxProcessManager::checkPidfd(int pidfd) { + DEBUG_LOG("ProcessManager: checking pidfd %d\n", pidfd); + + siginfo_t si{}; + si.si_code = CLD_DUMPED; + if (pidfd >= 0) { + int rc = waitid(P_PIDFD, pidfd, &si, WEXITED | WNOHANG); + if (rc < 0) { + perror("waitid"); + } else if (rc == 0 && si.si_pid == 0) { + return; + } + epoll_ctl(mEpollFd, EPOLL_CTL_DEL, pidfd, nullptr); + } + + DEBUG_LOG("ProcessManager: pidfd %d exited: code=%d status=%d\n", pidfd, si.si_code, si.si_status); + + Pin po; + { + std::unique_lock lk(m); + auto it = mReg.find(pidfd); + if (it != mReg.end()) { + po = std::move(it->second); + mReg.erase(it); + } + } + close(pidfd); + if (!po) { + return; + } + { + std::lock_guard lk(po->m); + po->signaled = true; + po->pidfd = -1; + if (!po->forcedExitCode) { + po->exitCode = decodeExitCode(si); + } + } + po->cv.notify_all(); + po->notifyWaiters(false); +} + diff --git a/src/setup.S b/src/setup.S index c98ee13..cc6042d 100644 --- a/src/setup.S +++ b/src/setup.S @@ -1,18 +1,25 @@ #include "macros.S" +#ifndef __APPLE__ .section .note.GNU-stack, "", @progbits +#endif .text -#ifdef __x86_64__ +#ifdef __APPLE__ +.zerofill RESV32,RESV32,_wibo_reserve,0x7E000000-0x1000 +.no_dead_strip _wibo_reserve +#endif -# int x86_64_thread_setup(int entry_number, void *teb) -.globl x86_64_thread_setup -.type x86_64_thread_setup, @function -x86_64_thread_setup: +#if defined(__x86_64__) && !defined(__APPLE__) + +# int tebThreadSetup(int entryNumber, TEB *teb) +.globl tebThreadSetup +.type tebThreadSetup, @function +tebThreadSetup: push rbx # save rbx mov r8, rsp # save host stack rdfsbase r9 # read host FS base mov rdx, qword ptr [rsi+TEB_SP] # fetch guest stack - LJMP32 # far jump into 32-bit code + LJMP32 rsi # far jump into 32-bit code mov ax, 0x2b # user data segment (Linux) mov ds, ax # setup data segment mov es, ax # setup extra segment @@ -38,21 +45,23 @@ x86_64_thread_setup: mov eax, -1 # return -1 2: add esp, 0x10 # cleanup stack - LJMP64 # far jump into 64-bit code + LJMP64 esi # far jump into 64-bit code cdqe # sign-extend eax to rax mov rsp, r8 # switch to host stack wrfsbase r9 # restore host FS base pop rbx # restore rbx ret -.size x86_64_thread_setup, .-x86_64_thread_setup +.size tebThreadSetup, .-tebThreadSetup #endif // __x86_64__ -.altmacro .code32 .macro stubThunkX number -#ifdef __x86_64__ +#if defined(__APPLE__) +.globl __Z9stubThunkILm\()\number\()EEvv +__Z9stubThunkILm\()\number\()EEvv: +#elif defined(__x86_64__) .globl _Z9stubThunkILm\()\number\()EEvv .type _Z9stubThunkILm\()\number\()EEvv, @function _Z9stubThunkILm\()\number\()EEvv: @@ -64,11 +73,267 @@ _Z9stubThunkILj\()\number\()EEvv: pop eax push \number push eax +#ifdef __APPLE__ + jmp _thunk_entry_stubBase +#else jmp thunk_entry_stubBase +#endif .endm -.set i, 0 -.rept 256 - stubThunkX %i - .set i, i+1 -.endr +stubThunkX 0 +stubThunkX 1 +stubThunkX 2 +stubThunkX 3 +stubThunkX 4 +stubThunkX 5 +stubThunkX 6 +stubThunkX 7 +stubThunkX 8 +stubThunkX 9 +stubThunkX 10 +stubThunkX 11 +stubThunkX 12 +stubThunkX 13 +stubThunkX 14 +stubThunkX 15 +stubThunkX 16 +stubThunkX 17 +stubThunkX 18 +stubThunkX 19 +stubThunkX 20 +stubThunkX 21 +stubThunkX 22 +stubThunkX 23 +stubThunkX 24 +stubThunkX 25 +stubThunkX 26 +stubThunkX 27 +stubThunkX 28 +stubThunkX 29 +stubThunkX 30 +stubThunkX 31 +stubThunkX 32 +stubThunkX 33 +stubThunkX 34 +stubThunkX 35 +stubThunkX 36 +stubThunkX 37 +stubThunkX 38 +stubThunkX 39 +stubThunkX 40 +stubThunkX 41 +stubThunkX 42 +stubThunkX 43 +stubThunkX 44 +stubThunkX 45 +stubThunkX 46 +stubThunkX 47 +stubThunkX 48 +stubThunkX 49 +stubThunkX 50 +stubThunkX 51 +stubThunkX 52 +stubThunkX 53 +stubThunkX 54 +stubThunkX 55 +stubThunkX 56 +stubThunkX 57 +stubThunkX 58 +stubThunkX 59 +stubThunkX 60 +stubThunkX 61 +stubThunkX 62 +stubThunkX 63 +stubThunkX 64 +stubThunkX 65 +stubThunkX 66 +stubThunkX 67 +stubThunkX 68 +stubThunkX 69 +stubThunkX 70 +stubThunkX 71 +stubThunkX 72 +stubThunkX 73 +stubThunkX 74 +stubThunkX 75 +stubThunkX 76 +stubThunkX 77 +stubThunkX 78 +stubThunkX 79 +stubThunkX 80 +stubThunkX 81 +stubThunkX 82 +stubThunkX 83 +stubThunkX 84 +stubThunkX 85 +stubThunkX 86 +stubThunkX 87 +stubThunkX 88 +stubThunkX 89 +stubThunkX 90 +stubThunkX 91 +stubThunkX 92 +stubThunkX 93 +stubThunkX 94 +stubThunkX 95 +stubThunkX 96 +stubThunkX 97 +stubThunkX 98 +stubThunkX 99 +stubThunkX 100 +stubThunkX 101 +stubThunkX 102 +stubThunkX 103 +stubThunkX 104 +stubThunkX 105 +stubThunkX 106 +stubThunkX 107 +stubThunkX 108 +stubThunkX 109 +stubThunkX 110 +stubThunkX 111 +stubThunkX 112 +stubThunkX 113 +stubThunkX 114 +stubThunkX 115 +stubThunkX 116 +stubThunkX 117 +stubThunkX 118 +stubThunkX 119 +stubThunkX 120 +stubThunkX 121 +stubThunkX 122 +stubThunkX 123 +stubThunkX 124 +stubThunkX 125 +stubThunkX 126 +stubThunkX 127 +stubThunkX 128 +stubThunkX 129 +stubThunkX 130 +stubThunkX 131 +stubThunkX 132 +stubThunkX 133 +stubThunkX 134 +stubThunkX 135 +stubThunkX 136 +stubThunkX 137 +stubThunkX 138 +stubThunkX 139 +stubThunkX 140 +stubThunkX 141 +stubThunkX 142 +stubThunkX 143 +stubThunkX 144 +stubThunkX 145 +stubThunkX 146 +stubThunkX 147 +stubThunkX 148 +stubThunkX 149 +stubThunkX 150 +stubThunkX 151 +stubThunkX 152 +stubThunkX 153 +stubThunkX 154 +stubThunkX 155 +stubThunkX 156 +stubThunkX 157 +stubThunkX 158 +stubThunkX 159 +stubThunkX 160 +stubThunkX 161 +stubThunkX 162 +stubThunkX 163 +stubThunkX 164 +stubThunkX 165 +stubThunkX 166 +stubThunkX 167 +stubThunkX 168 +stubThunkX 169 +stubThunkX 170 +stubThunkX 171 +stubThunkX 172 +stubThunkX 173 +stubThunkX 174 +stubThunkX 175 +stubThunkX 176 +stubThunkX 177 +stubThunkX 178 +stubThunkX 179 +stubThunkX 180 +stubThunkX 181 +stubThunkX 182 +stubThunkX 183 +stubThunkX 184 +stubThunkX 185 +stubThunkX 186 +stubThunkX 187 +stubThunkX 188 +stubThunkX 189 +stubThunkX 190 +stubThunkX 191 +stubThunkX 192 +stubThunkX 193 +stubThunkX 194 +stubThunkX 195 +stubThunkX 196 +stubThunkX 197 +stubThunkX 198 +stubThunkX 199 +stubThunkX 200 +stubThunkX 201 +stubThunkX 202 +stubThunkX 203 +stubThunkX 204 +stubThunkX 205 +stubThunkX 206 +stubThunkX 207 +stubThunkX 208 +stubThunkX 209 +stubThunkX 210 +stubThunkX 211 +stubThunkX 212 +stubThunkX 213 +stubThunkX 214 +stubThunkX 215 +stubThunkX 216 +stubThunkX 217 +stubThunkX 218 +stubThunkX 219 +stubThunkX 220 +stubThunkX 221 +stubThunkX 222 +stubThunkX 223 +stubThunkX 224 +stubThunkX 225 +stubThunkX 226 +stubThunkX 227 +stubThunkX 228 +stubThunkX 229 +stubThunkX 230 +stubThunkX 231 +stubThunkX 232 +stubThunkX 233 +stubThunkX 234 +stubThunkX 235 +stubThunkX 236 +stubThunkX 237 +stubThunkX 238 +stubThunkX 239 +stubThunkX 240 +stubThunkX 241 +stubThunkX 242 +stubThunkX 243 +stubThunkX 244 +stubThunkX 245 +stubThunkX 246 +stubThunkX 247 +stubThunkX 248 +stubThunkX 249 +stubThunkX 250 +stubThunkX 251 +stubThunkX 252 +stubThunkX 253 +stubThunkX 254 +stubThunkX 255 +stubThunkX 256 diff --git a/src/setup.h b/src/setup.h index 1f926d1..5d8b48f 100644 --- a/src/setup.h +++ b/src/setup.h @@ -1,11 +1,13 @@ #pragma once +#include "types.h" + #ifdef __cplusplus extern "C" { #endif #ifdef __x86_64__ -int x86_64_thread_setup(int entry_number, void *teb); +int tebThreadSetup(int entryNumber, TEB *teb); #endif #ifdef __cplusplus diff --git a/src/setup_darwin.cpp b/src/setup_darwin.cpp new file mode 100644 index 0000000..53c8e7c --- /dev/null +++ b/src/setup_darwin.cpp @@ -0,0 +1,105 @@ +#include "common.h" +#include "setup.h" + +#include "types.h" + +#include +#include + +#include +#include + +// https://github.com/apple/darwin-libpthread/blob/03c4628c8940cca6fd6a82957f683af804f62e7f/private/tsd_private.h#L92-L97 +#define _PTHREAD_TSD_SLOT_RESERVED_WIN64 6 + +#define USER_PRIVILEGE 3 + +namespace { + +inline ldt_entry createLdtEntry(uint32_t base, uint32_t size, bool code) { + uint32_t limit; + uint8_t granular; + if (size > 0xFFFFF) { + limit = (size - 1) >> 12; + granular = DESC_GRAN_PAGE; + } else { + limit = size - 1; + granular = DESC_GRAN_BYTE; + } + ldt_entry entry; // NOLINT(cppcoreguidelines-pro-type-member-init) + // Must memset to zero to avoid uninitialized padding bytes + std::memset(&entry, 0, sizeof(ldt_entry)); + entry.code.limit00 = static_cast(limit); + entry.code.base00 = static_cast(base); + entry.code.base16 = static_cast(base >> 16); + entry.code.type = code ? DESC_CODE_READ : DESC_DATA_WRITE; + entry.code.dpl = USER_PRIVILEGE; + entry.code.present = 1; + entry.code.limit16 = static_cast(limit >> 16); + entry.code.opsz = DESC_CODE_32B; + entry.code.granular = granular; + entry.code.base24 = static_cast(base >> 24); + return entry; +} + +constexpr int createSelector(int entryNumber) { return (entryNumber << 3) | 0x4 /* TI=1 */ | USER_PRIVILEGE; } + +inline void writeTsdSlot(uint32_t slot, uint64_t val) { + // mov qword ptr gs:[slot*8], val + *(volatile uint64_t __seg_gs *)(slot * sizeof(void *)) = val; +} + +} // namespace + +int tebThreadSetup(int entryNumber, TEB *teb) { + bool alloc = entryNumber == -1; + if (alloc) { + ldt_entry unused{}; + entryNumber = i386_get_ldt(0, &unused, 1); + if (entryNumber < 0) { + return entryNumber; + } + DEBUG_LOG("Allocating LDT entry %d\n", entryNumber); + // Create code LDT entry at entry_number + 1 + ldt_entry codeLdt = createLdtEntry(0, 0xFFFFFFFF, true); + int codeLdtEntry = entryNumber++; + int ret = i386_set_ldt(codeLdtEntry, &codeLdt, 1); + if (ret < 0) { + return ret; + } else if (ret != codeLdtEntry) { + errno = EALREADY; + return -EALREADY; + } + DEBUG_LOG("Code selector %x\n", createSelector(ret)); + // Create data LDT entry at entry_number + 2 + ldt_entry dataLdt = createLdtEntry(0, 0xFFFFFFFF, false); + int dataLdtEntry = entryNumber++; + ret = i386_set_ldt(dataLdtEntry, &dataLdt, 1); + if (ret < 0) { + return ret; + } else if (ret != dataLdtEntry) { + errno = EALREADY; + return -EALREADY; + } + DEBUG_LOG("Data selector %x\n", createSelector(dataLdtEntry)); + } + uintptr_t tebBase = reinterpret_cast(teb); + if (tebBase > 0xFFFFFFFF) { + DEBUG_LOG("TEB base address exceeds 32-bit limit\n"); + errno = EINVAL; + return -EINVAL; + } + // Store the TEB base address in the reserved slot for Windows 64-bit (gs:[0x30]) + writeTsdSlot(_PTHREAD_TSD_SLOT_RESERVED_WIN64, static_cast(tebBase)); + // Rosetta 2 requires size 0x1000 (limit 0xFFF) specifically + ldt_entry fsLdt = createLdtEntry(static_cast(tebBase), 0x1000, false); + int ret = i386_set_ldt(entryNumber, &fsLdt, 1); + if (ret < 0) { + return ret; + } else if (ret != entryNumber) { + errno = EALREADY; + return -EALREADY; + } + teb->CurrentFsSelector = createSelector(entryNumber); + return entryNumber; +} diff --git a/src/types.h b/src/types.h index f188a6f..7b1c9f9 100644 --- a/src/types.h +++ b/src/types.h @@ -538,7 +538,8 @@ typedef struct _TEB { WORD CurrentGsSelector; void *CurrentStackPointer; #ifdef __x86_64__ - void *CurrentFsBase; + void *HostFsBase; + void *HostGsBase; #endif } TEB; typedef GUEST_PTR PTEB; diff --git a/tools/gen_trampolines.py b/tools/gen_trampolines.py index 6288027..1d4aaa0 100644 --- a/tools/gen_trampolines.py +++ b/tools/gen_trampolines.py @@ -49,6 +49,8 @@ if "LIBCLANG_PATH" in os.environ: f"Warning: LIBCLANG_PATH={libclang_path} is not a file or directory\n" ) +SYMBOL_PREFIX = "_" if sys.platform == "darwin" else "" + class Arch(str, Enum): X86 = "x86" @@ -524,7 +526,7 @@ def emit_cc_thunk32(f: FuncInfo | TypedefInfo, lines: List[str]): # Get current TEB if host_to_guest: - lines.append("\tmov ecx, gs:[currentThreadTeb@ntpoff]") + lines.append("\tGET_TEB_HOST ecx") else: lines.append("\tmov ecx, fs:[TEB_SELF]") @@ -613,7 +615,7 @@ def emit_cc_thunk32(f: FuncInfo | TypedefInfo, lines: List[str]): if host_to_guest: lines.append("\tmov ecx, fs:[TEB_SELF]") else: - lines.append("\tmov ecx, gs:[currentThreadTeb@ntpoff]") + lines.append("\tGET_TEB_HOST ecx") lines.append("\tmov ax, fs") lines.append("\tmov dx, word ptr [ecx+TEB_FS_SEL]") lines.append("\tmov word ptr [ecx+TEB_FS_SEL], ax") @@ -699,7 +701,7 @@ def emit_cc_thunk64(f: FuncInfo | TypedefInfo, lines: List[str]): f.args, f.source_cc, Arch.X86_64 if host_to_guest else Arch.X86, - stack_offset=24 if host_to_guest else 16, + stack_offset=24 if host_to_guest else 20, skip_args=1 if host_to_guest else 0, ) target_layout = compute_arg_layout( @@ -710,22 +712,23 @@ def emit_cc_thunk64(f: FuncInfo | TypedefInfo, lines: List[str]): lines.append(".code64") # Save rbx and rbp - lines.append("\tpush rbx") lines.append("\tpush rbp") + lines.append("\tpush rbx") # Stash host stack in r10 lines.append("\tmov r10, rsp") # Get current TEB - lines.append("\tmov rcx, fs:[currentThreadTeb@tpoff]") + lines.append("\tGET_TEB_HOST rbx") - # Save FS base - lines.append("\trdfsbase r9") - lines.append("\tmov qword ptr [rcx+TEB_FSBASE], r9") + if sys.platform != "darwin": + # Save FS base + lines.append("\trdfsbase r9") + lines.append("\tmov qword ptr [rbx+TEB_FSBASE], r9") # Save RSP and load guest stack - lines.append("\tmov rbp, qword ptr [rcx+TEB_SP]") - lines.append("\tmov qword ptr [rcx+TEB_SP], rsp") + lines.append("\tmov rbp, qword ptr [rbx+TEB_SP]") + lines.append("\tmov qword ptr [rbx+TEB_SP], rsp") lines.append("\tmov rsp, rbp") # Allocate stack space for arguments @@ -758,36 +761,37 @@ def emit_cc_thunk64(f: FuncInfo | TypedefInfo, lines: List[str]): lines.append(f"\tmov {ptr_type} [rsp+{target.stack_offset}], {register}") # Jump to 32-bit mode - lines.append("\tLJMP32") + lines.append("\tLJMP32 rbx") # Setup FS selector - lines.append("\tmov ax, word ptr [ecx+TEB_FS_SEL]") + lines.append("\tmov ax, word ptr [ebx+TEB_FS_SEL]") lines.append("\tmov fs, ax") # Call into target lines.append(f"\tcall {call_target}") - # Get current TEB - lines.append("\tmov ecx, fs:[TEB_SELF]") + # Get current TEB (32-bit code may clobber ebx) + lines.append("\tmov ebx, fs:[TEB_SELF]") # Jump back to 64-bit - lines.append("\tLJMP64") + lines.append("\tLJMP64 ebx") # Sign extend return value if necessary if f.return_type.sign_extended: lines.append("\tcdqe") - # Restore FS base - lines.append("\tmov r9, qword ptr [rcx+TEB_FSBASE]") - lines.append("\twrfsbase r9") + if sys.platform != "darwin": + # Restore FS base + lines.append("\tmov r9, qword ptr [rbx+TEB_FSBASE]") + lines.append("\twrfsbase r9") # Restore host stack - lines.append("\tmov rsp, qword ptr [rcx+TEB_SP]") - lines.append("\tmov qword ptr [rcx+TEB_SP], rbp") + lines.append("\tmov rsp, qword ptr [rbx+TEB_SP]") + lines.append("\tmov qword ptr [rbx+TEB_SP], rbp") # Restore rbp, rbx and return - lines.append("\tpop rbp") lines.append("\tpop rbx") + lines.append("\tpop rbp") lines.append("\tret") else: lines.append(".code32") @@ -796,27 +800,30 @@ def emit_cc_thunk64(f: FuncInfo | TypedefInfo, lines: List[str]): lines.append("\tpush ebp") lines.append("\tpush esi") lines.append("\tpush edi") + lines.append("\tpush ebx") # Get current TEB - lines.append("\tmov ecx, fs:[TEB_SELF]") + lines.append("\tmov ebx, fs:[TEB_SELF]") - # Save fs segment - lines.append("\tmov di, fs") - lines.append("\tmov word ptr [ecx+TEB_FS_SEL], di") + if sys.platform != "darwin": + # Save fs segment + lines.append("\tmov di, fs") + lines.append("\tmov word ptr [ebx+TEB_FS_SEL], di") # Jump back to 64-bit - lines.append("\tLJMP64") + lines.append("\tLJMP64 ebx") - # Restore FS base - lines.append("\tmov r9, qword ptr [rcx+TEB_FSBASE]") - lines.append("\twrfsbase r9") + if sys.platform != "darwin": + # Restore FS base + lines.append("\tmov r9, qword ptr [rbx+TEB_FSBASE]") + lines.append("\twrfsbase r9") # Stash guest stack in r10 lines.append("\tmov r10, rsp") # Restore host stack - lines.append("\tmov rbp, qword ptr [rcx+TEB_SP]") - lines.append("\tmov qword ptr [rcx+TEB_SP], rsp") + lines.append("\tmov rbp, qword ptr [rbx+TEB_SP]") + lines.append("\tmov qword ptr [rbx+TEB_SP], rsp") lines.append("\tmov rsp, rbp") # Allocate stack space for arguments @@ -881,21 +888,20 @@ def emit_cc_thunk64(f: FuncInfo | TypedefInfo, lines: List[str]): # Call into target lines.append(f"\tcall {call_target}") - # Get current TEB - lines.append("\tmov rcx, fs:[currentThreadTeb@tpoff]") - # Restore host stack - lines.append("\tmov rsp, qword ptr [rcx+TEB_SP]") - lines.append("\tmov qword ptr [rcx+TEB_SP], rbp") + lines.append("\tmov rsp, qword ptr [rbx+TEB_SP]") + lines.append("\tmov qword ptr [rbx+TEB_SP], rbp") # Jump to 32-bit mode - lines.append("\tLJMP32") + lines.append("\tLJMP32 rbx") - # Setup FS selector - lines.append("\tmov di, word ptr [ecx+TEB_FS_SEL]") - lines.append("\tmov fs, di") + if sys.platform != "darwin": + # Setup FS selector + lines.append("\tmov di, word ptr [ebx+TEB_FS_SEL]") + lines.append("\tmov fs, di") # Restore registers + lines.append("\tpop ebx") lines.append("\tpop edi") lines.append("\tpop esi") lines.append("\tpop ebp") @@ -918,7 +924,7 @@ def emit_guest_to_host_thunks( lines: List[str], dll: str, funcs: Iterable[FuncInfo], arch: Arch ) -> None: for f in funcs: - thunk = f"thunk_{dll}_{f.name}" + thunk = f"{SYMBOL_PREFIX}thunk_{dll}_{f.name}" lines.append("") lines.append( f"# {f.qualified_ns}::{f.name} (source_cc={f.source_cc.name}, target_cc={f.target_cc.name}, variadic={f.variadic})" @@ -933,17 +939,19 @@ def emit_guest_to_host_thunks( details.append(f"sign_extended={arg.sign_extended}") lines.append(f"\t# Arg {i} ({', '.join(details)})") lines.append(f".globl {thunk}") - lines.append(f".type {thunk}, @function") + if sys.platform != "darwin": + lines.append(f".type {thunk}, @function") lines.append(f"{thunk}:") emit_cc_thunk(f, lines, arch) - lines.append(f".size {thunk}, .-{thunk}") + if sys.platform != "darwin": + lines.append(f".size {thunk}, .-{thunk}") def emit_host_to_guest_thunks( lines: List[str], typedefs: Iterable[TypedefInfo], arch: Arch ) -> None: for f in typedefs: - thunk = f"call_{f.name}" + thunk = f"{SYMBOL_PREFIX}call_{f.name}" lines.append("") lines.append( f"# {f.name} (target_cc={f.target_cc.name}, variadic={f.variadic})" @@ -961,11 +969,16 @@ def emit_host_to_guest_thunks( # details.append(f"class={f.return_type.arg_class.value}") # details.append(f"sign_extended={f.return_type.sign_extended}") # lines.append(f"\t# Ret ({', '.join(details)})") - lines.append(f".weak {thunk}") - lines.append(f".type {thunk}, @function") + if sys.platform == "darwin": + lines.append(f".globl {thunk}") + lines.append(f".weak_definition {thunk}") + else: + lines.append(f".weak {thunk}") + lines.append(f".type {thunk}, @function") lines.append(f"{thunk}:") emit_cc_thunk(f, lines, arch) - lines.append(f".size {thunk}, .-{thunk}") + if sys.platform != "darwin": + lines.append(f".size {thunk}, .-{thunk}") def emit_header_mapping( @@ -1079,12 +1092,16 @@ def main() -> int: if args.arch == "x86": arch = Arch.X86 + target = "i686-pc-linux-gnu" elif args.arch == "x86_64": arch = Arch.X86_64 + if sys.platform == "darwin": + target = "x86_64-apple-darwin" + else: + target = "x86_64-pc-linux-gnu" else: raise ValueError(f"Unsupported architecture: {args.arch}") - target = "i686-pc-linux-gnu" if args.arch == "x86" else "x86_64-pc-linux-gnu" tu = parse_tu(args.headers, args.incs, target) funcs = collect_functions(tu, args.ns, arch) typedefs = collect_typedefs(tu, arch) @@ -1097,7 +1114,8 @@ def main() -> int: lines: List[str] = [] lines.append("# Auto-generated thunks; DO NOT EDIT.") lines.append('#include "macros.S"') - lines.append('.section .note.GNU-stack, "", @progbits') + if sys.platform != "darwin": + lines.append('.section .note.GNU-stack, "", @progbits') lines.append(".text") emit_guest_to_host_thunks(lines, args.dll, funcs, arch)