Initial macOS support (x86_64 with Rosetta 2)

This commit is contained in:
2025-11-08 01:45:47 -07:00
parent f56bd8e2a7
commit cc0a887302
33 changed files with 1281 additions and 4186 deletions

View File

@@ -2,6 +2,13 @@ cmake_minimum_required(VERSION 3.13)
project(wibo LANGUAGES ASM C CXX) 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") set(WIBO_VERSION "" CACHE STRING "Version string for the wibo binary; if empty, attempts to use git describe")
if(NOT "${WIBO_VERSION}" STREQUAL "") 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` # 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_options(mimalloc-obj PRIVATE -Wno-psabi)
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_compile_definitions(mimalloc-obj PRIVATE MI_USE_BUILTIN_THREAD_POINTER=1) target_compile_definitions(mimalloc-obj PRIVATE MI_USE_BUILTIN_THREAD_POINTER=1)
endif()
if (WIBO_64) if (WIBO_64)
# Disable a noisy warning on startup # Disable a noisy warning on startup
target_compile_definitions(mimalloc-obj PRIVATE MI_DEBUG=0) target_compile_definitions(mimalloc-obj PRIVATE MI_DEBUG=0)
endif() endif()
if (WIBO_ENABLE_LIBURING) if (WIBO_ENABLE_LIBURING AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
FetchContent_Declare( FetchContent_Declare(
liburing liburing
GIT_REPOSITORY https://github.com/axboe/liburing.git GIT_REPOSITORY https://github.com/axboe/liburing.git
@@ -142,7 +151,6 @@ add_executable(wibo
dll/advapi32/wincrypt.cpp dll/advapi32/wincrypt.cpp
dll/advapi32/winreg.cpp dll/advapi32/winreg.cpp
dll/bcrypt.cpp dll/bcrypt.cpp
dll/crt.cpp
dll/kernel32.cpp dll/kernel32.cpp
dll/kernel32/debugapi.cpp dll/kernel32/debugapi.cpp
dll/kernel32/errhandlingapi.cpp dll/kernel32/errhandlingapi.cpp
@@ -169,7 +177,6 @@ add_executable(wibo
dll/kernel32/wow64apiset.cpp dll/kernel32/wow64apiset.cpp
dll/lmgr.cpp dll/lmgr.cpp
dll/mscoree.cpp dll/mscoree.cpp
dll/msvcrt.cpp
dll/ntdll.cpp dll/ntdll.cpp
dll/ole32.cpp dll/ole32.cpp
dll/rpcrt4.cpp dll/rpcrt4.cpp
@@ -178,7 +185,6 @@ add_executable(wibo
dll/version.cpp dll/version.cpp
src/access.cpp src/access.cpp
src/async_io.cpp src/async_io.cpp
src/async_io_epoll.cpp
src/errors.cpp src/errors.cpp
src/files.cpp src/files.cpp
src/handles.cpp src/handles.cpp
@@ -186,23 +192,52 @@ add_executable(wibo
src/loader.cpp src/loader.cpp
src/main.cpp src/main.cpp
src/modules.cpp src/modules.cpp
src/processes.cpp src/processes_common.cpp
src/resources.cpp src/resources.cpp
src/setup.S src/setup.S
src/strutil.cpp src/strutil.cpp
src/tls.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_definitions(wibo PRIVATE _GNU_SOURCE _FILE_OFFSET_BITS=64 _TIME_BITS=64)
target_compile_features(wibo PRIVATE cxx_std_20) target_compile_features(wibo PRIVATE cxx_std_20)
target_compile_options(wibo PRIVATE -Wall -Wextra) target_compile_options(wibo PRIVATE -Wall -Wextra)
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) target_link_options(wibo PRIVATE -no-pie -Wl,--image-base=0x70000000)
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(wibo PRIVATE -maccumulate-outgoing-args) target_compile_options(wibo PRIVATE -maccumulate-outgoing-args)
target_link_options(wibo PRIVATE -maccumulate-outgoing-args) target_link_options(wibo PRIVATE -maccumulate-outgoing-args)
endif() endif()
target_include_directories(wibo PRIVATE dll src ${WIBO_GENERATED_HEADER_DIR}) target_include_directories(wibo PRIVATE dll src ${WIBO_GENERATED_HEADER_DIR})
target_link_libraries(wibo PRIVATE mimalloc-obj atomic) target_link_libraries(wibo PRIVATE mimalloc-obj)
if (WIBO_ENABLE_LIBURING) 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_compile_definitions(wibo PRIVATE WIBO_ENABLE_LIBURING=1)
target_link_libraries(wibo PRIVATE liburing) target_link_libraries(wibo PRIVATE liburing)
target_sources(wibo PRIVATE src/async_io_uring.cpp) 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. # Track down libclang for trampoline generation.
# find_package(Clang) ends up requiring too many dependencies, # find_package(Clang) ends up requiring too many dependencies,
# so a quick-and-dirty manual search is done here instead. # so a quick-and-dirty manual search is done here instead.
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(CLANG_ROOT "/usr" CACHE PATH "Path to Clang installation") 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_MIN_VER 17)
set(_LLVM_MAX_VER 25) set(_LLVM_MAX_VER 25)
set(_CLANG_LIB_SUFFIXES "") 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 bcrypt HEADERS dll/bcrypt.h)
wibo_codegen_module(NAME entry HEADERS src/entry.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 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 version HEADERS dll/version.h)
wibo_codegen_module(NAME rpcrt4 HEADERS dll/rpcrt4.h) wibo_codegen_module(NAME rpcrt4 HEADERS dll/rpcrt4.h)
wibo_codegen_module(NAME vcruntime HEADERS dll/vcruntime.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}) add_test(NAME wibo.build_fixtures COMMAND ${_wibo_fixture_build_command})
endif() endif()
endif() endif()
if (NOT TARGET wibo_test_fixtures)
add_custom_target(wibo_test_fixtures)
endif()

View File

@@ -19,7 +19,7 @@ BOOL WINAPI OpenProcessToken(HANDLE ProcessHandle, DWORD DesiredAccess, PHANDLE
} }
Pin<ProcessObject> obj; Pin<ProcessObject> obj;
if (kernel32::isPseudoCurrentProcessHandle(ProcessHandle)) { if (kernel32::isPseudoCurrentProcessHandle(ProcessHandle)) {
obj = make_pin<ProcessObject>(getpid(), -1); obj = make_pin<ProcessObject>(getpid(), -1, false);
} else { } else {
obj = wibo::handles().getAs<ProcessObject>(ProcessHandle); obj = wibo::handles().getAs<ProcessObject>(ProcessHandle);
} }

View File

@@ -92,12 +92,15 @@ BOOL WINAPI CryptGenRandom(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer) {
return FALSE; return FALSE;
} }
#ifdef __APPLE__
arc4random_buf(pbBuffer, dwLen);
#else
ssize_t ret = getrandom(pbBuffer, dwLen, 0); ssize_t ret = getrandom(pbBuffer, dwLen, 0);
if (ret < 0 || static_cast<DWORD>(ret) != dwLen) { if (ret < 0 || static_cast<DWORD>(ret) != dwLen) {
kernel32::setLastError(ERROR_NOT_SUPPORTED); kernel32::setLastError(ERROR_NOT_SUPPORTED);
return FALSE; return FALSE;
} }
#endif
return TRUE; return TRUE;
} }

View File

@@ -15,6 +15,9 @@ constexpr ULONG BCRYPT_RNG_USE_ENTROPY_IN_BUFFER = 0x00000001;
constexpr ULONG BCRYPT_USE_SYSTEM_PREFERRED_RNG = 0x00000002; constexpr ULONG BCRYPT_USE_SYSTEM_PREFERRED_RNG = 0x00000002;
bool fillWithSystemRandom(PUCHAR buffer, size_t length) { bool fillWithSystemRandom(PUCHAR buffer, size_t length) {
#ifdef __APPLE__
arc4random_buf(buffer, length);
#else
while (length > 0) { while (length > 0) {
ssize_t written = getrandom(buffer, length, 0); ssize_t written = getrandom(buffer, length, 0);
if (written < 0) { if (written < 0) {
@@ -27,6 +30,7 @@ bool fillWithSystemRandom(PUCHAR buffer, size_t length) {
buffer += written; buffer += written;
length -= static_cast<size_t>(written); length -= static_cast<size_t>(written);
} }
#endif
return true; return true;
} }

View File

@@ -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 <csignal>
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <vector>
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<GUEST_PTR *>(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<char *>(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<GUEST_PTR *>(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<char *>(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<GUEST_PTR *>(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<char *>(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<GUEST_PTR *>(fromGuestPtr(table->first));
GUEST_PTR *last = reinterpret_cast<GUEST_PTR *>(fromGuestPtr(table->last));
GUEST_PTR *end = reinterpret_cast<GUEST_PTR *>(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<GUEST_PTR *>(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<void *>(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<GUEST_PTR *>(fromGuestPtr(table->first));
GUEST_PTR *last = reinterpret_cast<GUEST_PTR *>(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<SIZE_T>(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,
};

View File

@@ -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

View File

@@ -39,7 +39,7 @@ BOOL WINAPI DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, H
auto &handles = wibo::handles(); auto &handles = wibo::handles();
if (isPseudoCurrentProcessHandle(hSourceHandle)) { if (isPseudoCurrentProcessHandle(hSourceHandle)) {
auto po = make_pin<ProcessObject>(getpid(), -1); auto po = make_pin<ProcessObject>(getpid(), -1, false);
auto handle = handles.alloc(std::move(po), 0, 0); auto handle = handles.alloc(std::move(po), 0, 0);
DEBUG_LOG("DuplicateHandle: created process handle for current process -> %p\n", handle); DEBUG_LOG("DuplicateHandle: created process handle for current process -> %p\n", handle);
*lpTargetHandle = handle; *lpTargetHandle = handle;

View File

@@ -3,7 +3,6 @@
#include "common.h" #include "common.h"
#include "context.h" #include "context.h"
#include <atomic>
#include <cstring> #include <cstring>
namespace kernel32 { namespace kernel32 {
@@ -11,22 +10,22 @@ namespace kernel32 {
LONG WINAPI InterlockedIncrement(LONG volatile *Addend) { LONG WINAPI InterlockedIncrement(LONG volatile *Addend) {
HOST_CONTEXT_GUARD(); HOST_CONTEXT_GUARD();
VERBOSE_LOG("InterlockedIncrement(%p)\n", Addend); VERBOSE_LOG("InterlockedIncrement(%p)\n", Addend);
std::atomic_ref<LONG> a(*const_cast<LONG *>(Addend)); auto *ptr = const_cast<LONG *>(Addend);
return a.fetch_add(1, std::memory_order_seq_cst) + 1; return __atomic_add_fetch(ptr, 1, __ATOMIC_SEQ_CST);
} }
LONG WINAPI InterlockedDecrement(LONG volatile *Addend) { LONG WINAPI InterlockedDecrement(LONG volatile *Addend) {
HOST_CONTEXT_GUARD(); HOST_CONTEXT_GUARD();
VERBOSE_LOG("InterlockedDecrement(%p)\n", Addend); VERBOSE_LOG("InterlockedDecrement(%p)\n", Addend);
std::atomic_ref<LONG> a(*const_cast<LONG *>(Addend)); auto *ptr = const_cast<LONG *>(Addend);
return a.fetch_sub(1, std::memory_order_seq_cst) - 1; return __atomic_sub_fetch(ptr, 1, __ATOMIC_SEQ_CST);
} }
LONG WINAPI InterlockedExchange(LONG volatile *Target, LONG Value) { LONG WINAPI InterlockedExchange(LONG volatile *Target, LONG Value) {
HOST_CONTEXT_GUARD(); HOST_CONTEXT_GUARD();
VERBOSE_LOG("InterlockedExchange(%p, %ld)\n", Target, static_cast<long>(Value)); VERBOSE_LOG("InterlockedExchange(%p, %ld)\n", Target, static_cast<long>(Value));
std::atomic_ref<LONG> a(*const_cast<LONG *>(Target)); auto *ptr = const_cast<LONG *>(Target);
return a.exchange(Value, std::memory_order_seq_cst); return __atomic_exchange_n(ptr, Value, __ATOMIC_SEQ_CST);
} }
LONG WINAPI InterlockedCompareExchange(LONG volatile *Destination, LONG Exchange, LONG Comperand) { 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<long>(Exchange), VERBOSE_LOG("InterlockedCompareExchange(%p, %ld, %ld)\n", Destination, static_cast<long>(Exchange),
static_cast<long>(Comperand)); static_cast<long>(Comperand));
std::atomic_ref<LONG> a(*const_cast<LONG *>(Destination)); auto *ptr = const_cast<LONG *>(Destination);
LONG expected = Comperand; 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; return expected;
} }

View File

@@ -64,8 +64,10 @@ struct ProcessObject final : WaitableObject {
int pidfd; int pidfd;
DWORD exitCode = STILL_ACTIVE; DWORD exitCode = STILL_ACTIVE;
bool forcedExitCode = false; 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 { ~ProcessObject() override {
if (pidfd != -1) { 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 { struct ThreadObject final : WaitableObject {
static constexpr ObjectType kType = ObjectType::Thread; static constexpr ObjectType kType = ObjectType::Thread;
@@ -83,7 +91,7 @@ struct ThreadObject final : WaitableObject {
unsigned int suspendCount = 0; unsigned int suspendCount = 0;
TEB *tib = nullptr; 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 { ~ThreadObject() override {
// Threads are detached at creation; we can safely drop // Threads are detached at creation; we can safely drop

View File

@@ -368,11 +368,13 @@ BOOL WINAPI CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBU
configureInheritability(pipeFds[0], inheritHandles); configureInheritability(pipeFds[0], inheritHandles);
configureInheritability(pipeFds[1], inheritHandles); configureInheritability(pipeFds[1], inheritHandles);
#ifdef __linux__
if (nSize != 0) { if (nSize != 0) {
// Best-effort adjustment; ignore failures as recommended by docs. // Best-effort adjustment; ignore failures as recommended by docs.
fcntl(pipeFds[0], F_SETPIPE_SZ, static_cast<int>(nSize)); fcntl(pipeFds[0], F_SETPIPE_SZ, static_cast<int>(nSize));
fcntl(pipeFds[1], F_SETPIPE_SZ, static_cast<int>(nSize)); fcntl(pipeFds[1], F_SETPIPE_SZ, static_cast<int>(nSize));
} }
#endif
auto readObj = make_pin<FileObject>(pipeFds[0]); auto readObj = make_pin<FileObject>(pipeFds[0]);
readObj->shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE; readObj->shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE;
@@ -479,15 +481,19 @@ HANDLE WINAPI CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode
if (accessMode == PIPE_ACCESS_INBOUND) { if (accessMode == PIPE_ACCESS_INBOUND) {
serverFd = fds[0]; serverFd = fds[0];
companionFd = fds[1]; companionFd = fds[1];
#ifdef __linux__
if (nInBufferSize != 0) { if (nInBufferSize != 0) {
fcntl(serverFd, F_SETPIPE_SZ, static_cast<int>(nInBufferSize)); fcntl(serverFd, F_SETPIPE_SZ, static_cast<int>(nInBufferSize));
} }
#endif
} else { } else {
serverFd = fds[1]; serverFd = fds[1];
companionFd = fds[0]; companionFd = fds[0];
#ifdef __linux__
if (nOutBufferSize != 0) { if (nOutBufferSize != 0) {
fcntl(serverFd, F_SETPIPE_SZ, static_cast<int>(nOutBufferSize)); fcntl(serverFd, F_SETPIPE_SZ, static_cast<int>(nOutBufferSize));
} }
#endif
} }
} }

View File

@@ -15,6 +15,10 @@
#include <strings.h> #include <strings.h>
#include <unistd.h> #include <unistd.h>
#ifdef __APPLE__
extern char **environ;
#endif
namespace { namespace {
GUEST_PTR g_commandLineA = GUEST_NULL; GUEST_PTR g_commandLineA = GUEST_NULL;

View File

@@ -196,7 +196,11 @@ DWORD WINAPI GetCurrentProcessId() {
DWORD WINAPI GetCurrentThreadId() { DWORD WINAPI GetCurrentThreadId() {
HOST_CONTEXT_GUARD(); HOST_CONTEXT_GUARD();
pthread_t thread = pthread_self(); pthread_t thread = pthread_self();
#ifdef __linux__
const auto threadId = static_cast<DWORD>(thread); const auto threadId = static_cast<DWORD>(thread);
#else
const auto threadId = static_cast<DWORD>(reinterpret_cast<uintptr_t>(thread));
#endif
DEBUG_LOG("GetCurrentThreadId() -> %u\n", threadId); DEBUG_LOG("GetCurrentThreadId() -> %u\n", threadId);
return threadId; return threadId;
} }
@@ -322,10 +326,25 @@ BOOL WINAPI TerminateProcess(HANDLE hProcess, UINT uExitCode) {
if (process->signaled) { if (process->signaled) {
return TRUE; return TRUE;
} }
int killResult = 0;
#if defined(__linux__)
if (process->pidfd != -1) {
if (syscall(SYS_pidfd_send_signal, process->pidfd, SIGKILL, nullptr, 0) != 0) { if (syscall(SYS_pidfd_send_signal, process->pidfd, SIGKILL, nullptr, 0) != 0) {
int err = errno; killResult = errno;
DEBUG_LOG("TerminateProcess: pidfd_send_signal(%d) failed: %s\n", process->pidfd, strerror(err)); DEBUG_LOG("TerminateProcess: pidfd_send_signal(%d) failed: %s\n", process->pidfd, strerror(killResult));
switch (err) { }
} 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 ESRCH:
case EPERM: case EPERM:
setLastError(ERROR_ACCESS_DENIED); setLastError(ERROR_ACCESS_DENIED);
@@ -467,7 +486,7 @@ HANDLE WINAPI CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwSt
return NO_HANDLE; return NO_HANDLE;
} }
Pin<ThreadObject> obj = make_pin<ThreadObject>(0); // tid set during pthread_create Pin<ThreadObject> obj = make_pin<ThreadObject>(); // tid set during pthread_create
if ((dwCreationFlags & CREATE_SUSPENDED) != 0) { if ((dwCreationFlags & CREATE_SUSPENDED) != 0) {
obj->suspendCount = 1; obj->suspendCount = 1;
} }
@@ -584,12 +603,14 @@ BOOL WINAPI GetThreadTimes(HANDLE hThread, FILETIME *lpCreationTime, FILETIME *l
lpExitTime->dwHighDateTime = 0; lpExitTime->dwHighDateTime = 0;
} }
#ifdef __linux__
struct rusage usage {}; struct rusage usage {};
if (getrusage(RUSAGE_THREAD, &usage) == 0) { if (getrusage(RUSAGE_THREAD, &usage) == 0) {
*lpKernelTime = fileTimeFromTimeval(usage.ru_stime); *lpKernelTime = fileTimeFromTimeval(usage.ru_stime);
*lpUserTime = fileTimeFromTimeval(usage.ru_utime); *lpUserTime = fileTimeFromTimeval(usage.ru_utime);
return TRUE; return TRUE;
} }
#endif
struct timespec cpuTime {}; struct timespec cpuTime {};
if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &cpuTime) == 0) { if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &cpuTime) == 0) {

View File

@@ -437,10 +437,13 @@ DWORD WINAPI WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
case ObjectType::Process: { case ObjectType::Process: {
auto po = std::move(obj).downcast<ProcessObject>(); auto po = std::move(obj).downcast<ProcessObject>();
std::unique_lock lk(po->m); 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? // Windows actually allows you to wait on your own process, but why bother?
return WAIT_TIMEOUT; return WAIT_TIMEOUT;
} }
if (po->signaled) {
return WAIT_OBJECT_0;
}
bool ok = doWait(lk, po->cv, [&] { return po->signaled; }); bool ok = doWait(lk, po->cv, [&] { return po->signaled; });
return ok ? WAIT_OBJECT_0 : WAIT_TIMEOUT; return ok ? WAIT_OBJECT_0 : WAIT_TIMEOUT;
} }

View File

@@ -135,4 +135,11 @@ BOOL WINAPI ReadConsoleInputA(HANDLE hConsoleInput, INPUT_RECORD *lpBuffer, DWOR
return TRUE; return TRUE;
} }
BOOL WINAPI VerifyConsoleIoHandle(HANDLE handle) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("STUB: VerifyConsoleIoHandle(%p)\n", handle);
(void)handle;
return FALSE;
}
} // namespace kernel32 } // namespace kernel32

View File

@@ -42,5 +42,6 @@ BOOL WINAPI PeekConsoleInputA(HANDLE hConsoleInput, INPUT_RECORD *lpBuffer, DWOR
LPDWORD lpNumberOfEventsRead); LPDWORD lpNumberOfEventsRead);
BOOL WINAPI ReadConsoleInputA(HANDLE hConsoleInput, INPUT_RECORD *lpBuffer, DWORD nLength, BOOL WINAPI ReadConsoleInputA(HANDLE hConsoleInput, INPUT_RECORD *lpBuffer, DWORD nLength,
LPDWORD lpNumberOfEventsRead); LPDWORD lpNumberOfEventsRead);
BOOL WINAPI VerifyConsoleIoHandle(HANDLE handle);
} // namespace kernel32 } // namespace kernel32

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -49,7 +49,9 @@ static constexpr BackendEntry kBackends[] = {
#if WIBO_ENABLE_LIBURING #if WIBO_ENABLE_LIBURING
{"io_uring", detail::createIoUringBackend}, {"io_uring", detail::createIoUringBackend},
#endif #endif
#ifdef __linux__
{"epoll", detail::createEpollBackend}, {"epoll", detail::createEpollBackend},
#endif
}; };
AsyncIOBackend &asyncIO() { AsyncIOBackend &asyncIO() {

View File

@@ -24,7 +24,9 @@ namespace detail {
#if WIBO_ENABLE_LIBURING #if WIBO_ENABLE_LIBURING
std::unique_ptr<AsyncIOBackend> createIoUringBackend(); std::unique_ptr<AsyncIOBackend> createIoUringBackend();
#endif #endif
#ifdef __linux__
std::unique_ptr<AsyncIOBackend> createEpollBackend(); std::unique_ptr<AsyncIOBackend> createEpollBackend();
#endif
} // namespace detail } // namespace detail

View File

@@ -17,15 +17,17 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#ifdef __linux__
// Alpine hack: rename duplicate prctl_mm_map (sys/prctl.h also includes it) // Alpine hack: rename duplicate prctl_mm_map (sys/prctl.h also includes it)
#define prctl_mm_map _prctl_mm_map #define prctl_mm_map _prctl_mm_map
#include <linux/prctl.h> #include <linux/prctl.h>
#undef prctl_mm_map #undef prctl_mm_map
#include <sys/prctl.h>
#endif
#include <mimalloc.h> #include <mimalloc.h>
#include <mimalloc/internal.h> #include <mimalloc/internal.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/prctl.h>
#include <unistd.h> #include <unistd.h>
// Pre-initialization logging macros // Pre-initialization logging macros
@@ -36,8 +38,14 @@ namespace {
constexpr uintptr_t kLowMemoryStart = 0x00110000UL; // 1 MiB + 64 KiB constexpr uintptr_t kLowMemoryStart = 0x00110000UL; // 1 MiB + 64 KiB
constexpr uintptr_t kHeapMax = 0x60000000UL; // 1 GiB 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 kTopDownStart = 0x7F000000UL; // Just below 2GB
constexpr uintptr_t kTwoGB = 0x80000000UL; constexpr uintptr_t kTwoGB = 0x80000000UL;
#endif
constexpr std::size_t kGuestArenaSize = 512ULL * 1024ULL * 1024ULL; // 512 MiB constexpr std::size_t kGuestArenaSize = 512ULL * 1024ULL * 1024ULL; // 512 MiB
constexpr std::size_t kVirtualAllocationGranularity = 64ULL * 1024ULL; constexpr std::size_t kVirtualAllocationGranularity = 64ULL * 1024ULL;
@@ -69,6 +77,7 @@ struct VirtualAllocation {
std::map<uintptr_t, VirtualAllocation> g_virtualAllocations; std::map<uintptr_t, VirtualAllocation> g_virtualAllocations;
#ifndef __APPLE__
const uintptr_t kDefaultMmapMinAddr = 0x10000u; const uintptr_t kDefaultMmapMinAddr = 0x10000u;
uintptr_t readMmapMinAddr() { uintptr_t readMmapMinAddr() {
@@ -98,6 +107,7 @@ uintptr_t mmapMinAddr() {
static uintptr_t minAddr = readMmapMinAddr(); static uintptr_t minAddr = readMmapMinAddr();
return minAddr; return minAddr;
} }
#endif
uintptr_t alignDown(uintptr_t value, std::size_t alignment) { uintptr_t alignDown(uintptr_t value, std::size_t alignment) {
const uintptr_t mask = static_cast<uintptr_t>(alignment) - 1; const uintptr_t mask = static_cast<uintptr_t>(alignment) - 1;
@@ -311,9 +321,13 @@ bool mapAtAddr(uintptr_t addr, std::size_t size, const char *name, void **outPtr
if (p == MAP_FAILED) { if (p == MAP_FAILED) {
return false; return false;
} }
#ifdef __linux__
if (name) { if (name) {
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, addr, size, 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); recordGuestMapping(addr, size, PAGE_READWRITE, MEM_RESERVE, PAGE_READWRITE, MEM_PRIVATE);
if (outPtr) { if (outPtr) {
*outPtr = p; *outPtr = p;
@@ -668,11 +682,13 @@ VmStatus virtualAlloc(void **baseAddress, std::size_t *regionSize, DWORD allocat
if (mapped == MAP_FAILED) { if (mapped == MAP_FAILED) {
return vmStatusFromErrno(errno); return vmStatusFromErrno(errno);
} }
#ifdef __linux__
if (type == MEM_IMAGE) { if (type == MEM_IMAGE) {
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, base, length, "wibo guest image"); prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, base, length, "wibo guest image");
} else { } else {
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, base, length, "wibo guest allocated"); prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, base, length, "wibo guest allocated");
} }
#endif
uintptr_t actualBase = reinterpret_cast<uintptr_t>(mapped); uintptr_t actualBase = reinterpret_cast<uintptr_t>(mapped);
VirtualAllocation allocation{}; VirtualAllocation allocation{};
allocation.base = actualBase; allocation.base = actualBase;
@@ -739,7 +755,9 @@ VmStatus virtualAlloc(void **baseAddress, std::size_t *regionSize, DWORD allocat
if (res == MAP_FAILED) { if (res == MAP_FAILED) {
return vmStatusFromErrno(errno); return vmStatusFromErrno(errno);
} }
#ifdef __linux__
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, run.first, run.second, "wibo guest committed"); prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, run.first, run.second, "wibo guest committed");
#endif
markCommitted(*region, run.first, run.second, protect); 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) { if (res == MAP_FAILED) {
return vmStatusFromErrno(errno); return vmStatusFromErrno(errno);
} }
#ifdef __linux__
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, base, length, "wibo reserved"); prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, base, length, "wibo reserved");
#endif
eraseGuestMapping(base); eraseGuestMapping(base);
return VmStatus::Success; return VmStatus::Success;
} }
@@ -828,7 +848,9 @@ VmStatus virtualFree(void *baseAddress, std::size_t regionSize, DWORD freeType)
if (res == MAP_FAILED) { if (res == MAP_FAILED) {
return vmStatusFromErrno(errno); return vmStatusFromErrno(errno);
} }
#ifdef __linux__
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, res, length, "wibo reserved"); prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, res, length, "wibo reserved");
#endif
markDecommitted(region, start, length); markDecommitted(region, start, length);
refreshGuestMapping(region); refreshGuestMapping(region);
return VmStatus::Success; return VmStatus::Success;
@@ -1043,6 +1065,7 @@ bool reserveGuestStack(std::size_t stackSizeBytes, void **outStackLimit, void **
} // namespace wibo::heap } // namespace wibo::heap
#ifndef __APPLE__
static void debugPrintMaps() { static void debugPrintMaps() {
char buf[1024]; char buf[1024];
int fd = open("/proc/self/maps", O_RDONLY); 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"); perror("heap: failed reserve memory");
exit(1); exit(1);
} }
#ifdef __linux__
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, len, "wibo reserved"); prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, len, "wibo reserved");
#endif
} }
lastMapEnd = mapEnd; lastMapEnd = mapEnd;
@@ -1203,27 +1228,36 @@ static size_t blockLower2GB(MEMORY_BASIC_INFORMATION mappings[MAX_NUM_MAPPINGS])
return numMappings; return numMappings;
} }
#endif
#if defined(__clang__) #if defined(__clang__)
__attribute__((constructor(101))) __attribute__((constructor(101)))
#else #else
__attribute__((constructor)) __attribute__((constructor))
#endif #endif
__attribute__((used)) static void wibo_heap_constructor() { __attribute__((used)) static void
wibo_heap_constructor() {
#ifndef __APPLE__
MEMORY_BASIC_INFORMATION mappings[MAX_NUM_MAPPINGS]; MEMORY_BASIC_INFORMATION mappings[MAX_NUM_MAPPINGS];
memset(mappings, 0, sizeof(mappings)); memset(mappings, 0, sizeof(mappings));
#endif
bool debug = getenv("WIBO_DEBUG_HEAP") != nullptr; bool debug = getenv("WIBO_DEBUG_HEAP") != nullptr;
if (debug) { if (debug) {
LOG_OUT("heap: initializing...\n"); LOG_OUT("heap: initializing...\n");
#ifndef __APPLE__
debugPrintMaps(); debugPrintMaps();
#endif
} }
#ifndef __APPLE__
size_t numMappings = blockLower2GB(mappings); size_t numMappings = blockLower2GB(mappings);
#endif
// Now we can allocate memory // Now we can allocate memory
if (debug) { if (debug) {
mi_option_enable(mi_option_show_stats); mi_option_enable(mi_option_show_stats);
mi_option_enable(mi_option_verbose); mi_option_enable(mi_option_verbose);
} }
g_mappings = new std::map<uintptr_t, MEMORY_BASIC_INFORMATION>; g_mappings = new std::map<uintptr_t, MEMORY_BASIC_INFORMATION>;
#ifndef __APPLE__
for (size_t i = 0; i < numMappings; ++i) { for (size_t i = 0; i < numMappings; ++i) {
if (debug) { if (debug) {
fprintf(stderr, "Existing %zu: BaseAddress=%x, RegionSize=%u\n", i, mappings[i].BaseAddress, 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<uintptr_t>(fromGuestPtr(mappings[i].BaseAddress)), mappings[i]); g_mappings->emplace(reinterpret_cast<uintptr_t>(fromGuestPtr(mappings[i].BaseAddress)), mappings[i]);
} }
#endif
} }

View File

@@ -13,22 +13,42 @@
#ifdef __x86_64__ #ifdef __x86_64__
.equ TEB_SP, 0xfa0 # CurrentStackPointer .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_32, 0x23 # 32-bit code segment (Linux)
.equ CS_64, 0x33 # 64-bit code segment (Linux) .equ CS_64, 0x33 # 64-bit code segment (Linux)
.equ DS_32, 0x2b # 32-bit data 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 jmp fword ptr [rip] # far jump into 32-bit code
.long 1f # 32-bit code offset .long 1f # 32-bit code offset
.word CS_32 # 32-bit code segment (Linux) .word CS_32 # 32-bit code segment
#endif
.code32 .code32
1: 1:
endbr32 endbr32
.endm .endm
.macro LJMP64 .macro LJMP64 teb_reg
// Annoyingly, we can't assemble this in Intel syntax // Annoyingly, we can't assemble this in Intel syntax
.att_syntax prefix .att_syntax prefix
ljmp $CS_64, $1f ljmp $CS_64, $1f
@@ -39,3 +59,19 @@
.endm .endm
#endif // __x86_64__ #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

View File

@@ -10,12 +10,6 @@
#include "types.h" #include "types.h"
#include "version_info.h" #include "version_info.h"
#ifdef __x86_64__
#include "setup.h"
#endif
#include <asm/ldt.h>
#include <asm/prctl.h>
#include <cstdarg> #include <cstdarg>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
@@ -26,9 +20,17 @@
#include <pthread.h> #include <pthread.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <threads.h>
#include <unistd.h> #include <unistd.h>
#include <vector>
#ifdef __x86_64__
#include "setup.h"
#endif
#ifdef __linux__
#include <asm/ldt.h>
#include <asm/prctl.h>
#include <threads.h>
#endif
char **wibo::argv; char **wibo::argv;
int wibo::argc; int wibo::argc;
@@ -49,7 +51,11 @@ void wibo::debug_log(const char *fmt, ...) {
for (size_t i = 0; i < wibo::debugIndent; i++) for (size_t i = 0; i < wibo::debugIndent; i++)
fprintf(stderr, "\t"); fprintf(stderr, "\t");
pthread_t threadId = pthread_self(); pthread_t threadId = pthread_self();
#ifdef __APPLE__
fprintf(stderr, "[thread %p] ", threadId);
#else
fprintf(stderr, "[thread %lu] ", threadId); fprintf(stderr, "[thread %lu] ", threadId);
#endif
vfprintf(stderr, fmt, args); vfprintf(stderr, fmt, args);
fflush(stderr); fflush(stderr);
} }
@@ -101,8 +107,8 @@ bool wibo::installTibForCurrentThread(TEB *tibPtr) {
currentThreadTeb = tibPtr; currentThreadTeb = tibPtr;
#ifdef __x86_64__ #ifdef __x86_64__
tibEntryNumber = x86_64_thread_setup(tibEntryNumber, tibPtr); tibEntryNumber = tebThreadSetup(tibEntryNumber, tibPtr);
if (tibEntryNumber == -1 || tibPtr->CurrentFsSelector == 0) { if (tibEntryNumber < 0 || tibPtr->CurrentFsSelector == 0) {
perror("x86_64_thread_setup failed"); perror("x86_64_thread_setup failed");
return false; return false;
} }

View File

@@ -175,9 +175,8 @@ StubFuncType resolveMissingFuncName(const char *dllName, const char *funcName) {
} }
StubFuncType resolveMissingFuncOrdinal(const char *dllName, uint16_t ordinal) { StubFuncType resolveMissingFuncOrdinal(const char *dllName, uint16_t ordinal) {
char buf[16]; std::string funcName = std::to_string(ordinal);
sprintf(buf, "%d", ordinal); return resolveMissingFuncName(dllName, funcName.c_str());
return resolveMissingFuncName(dllName, buf);
} }
struct ModuleRegistry { struct ModuleRegistry {

View File

@@ -2,7 +2,6 @@
#include "common.h" #include "common.h"
#include "entry.h" #include "entry.h"
#include "msvcrt.h"
#include "tls.h" #include "tls.h"
#include "types.h" #include "types.h"

View File

@@ -3,34 +3,47 @@
#include "kernel32/internal.h" #include "kernel32/internal.h"
#include <filesystem> #include <filesystem>
#include <memory>
#include <optional> #include <optional>
#include <string> #include <string>
#include <thread>
#include <vector> #include <vector>
using kernel32::ProcessObject; using kernel32::ProcessObject;
namespace wibo { namespace wibo {
namespace detail {
class ProcessManagerImpl {
public:
virtual ~ProcessManagerImpl() = default;
virtual bool init() = 0;
virtual void shutdown() = 0;
virtual bool addProcess(Pin<ProcessObject> po) = 0;
[[nodiscard]] virtual bool running() const = 0;
};
struct SpawnProcessInfo {
pid_t pid = -1;
int pidfd = -1;
};
std::unique_ptr<ProcessManagerImpl> createProcessManagerImpl();
int spawnProcess(char *const argv[], char *const envp[], SpawnProcessInfo &info);
} // namespace detail
class ProcessManager { class ProcessManager {
public: public:
ProcessManager();
~ProcessManager(); ~ProcessManager();
bool init(); bool init();
void shutdown(); void shutdown();
bool addProcess(Pin<ProcessObject> po); bool addProcess(Pin<ProcessObject> po);
bool running() const { return mRunning.load(std::memory_order_acquire); } [[nodiscard]] bool running() const;
private: private:
void runLoop(); std::unique_ptr<detail::ProcessManagerImpl> mImpl;
void wake() const;
void checkPidfd(int pidfd);
mutable std::shared_mutex m;
std::atomic<bool> mRunning = false;
std::thread mThread;
int mEpollFd = -1;
int mWakeFd = -1;
std::unordered_map<int, Pin<ProcessObject>> mReg;
}; };
ProcessManager &processes(); ProcessManager &processes();

View File

@@ -1,223 +1,60 @@
#include "processes.h" #include "processes.h"
#include "common.h" #include "common.h"
#include "files.h" #include "files.h"
#include "handles.h" #include "handles.h"
#include "kernel32/internal.h" #include "kernel32/internal.h"
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cerrno> #include <cerrno>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <fcntl.h>
#include <filesystem> #include <filesystem>
#include <linux/sched.h>
#include <mutex>
#include <optional> #include <optional>
#include <shared_mutex>
#include <spawn.h>
#include <string> #include <string>
#include <strings.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector> #include <vector>
namespace { extern char **environ;
inline DWORD decodeExitCode(const siginfo_t &si) { using kernel32::ProcessObject;
switch (si.si_code) {
case CLD_EXITED:
return static_cast<DWORD>(si.si_status);
case CLD_KILLED:
case CLD_DUMPED:
return 0xC0000000u | static_cast<DWORD>(si.si_status);
default:
return 0;
}
}
} // namespace
namespace wibo { namespace wibo {
ProcessManager::~ProcessManager() { shutdown(); } ProcessManager::ProcessManager() : mImpl(detail::createProcessManagerImpl()) {}
ProcessManager::~ProcessManager() = default;
bool ProcessManager::init() { bool ProcessManager::init() {
if (mRunning.load(std::memory_order_acquire)) { if (!mImpl) {
return true;
}
mEpollFd = epoll_create1(EPOLL_CLOEXEC);
if (mEpollFd < 0) {
perror("epoll_create1");
return false; return false;
} }
return mImpl->init();
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;
} }
void ProcessManager::shutdown() { void ProcessManager::shutdown() {
if (!mRunning.exchange(false, std::memory_order_acq_rel)) { if (mImpl) {
return; mImpl->shutdown();
}
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 ProcessManager::addProcess(Pin<ProcessObject> po) { bool ProcessManager::addProcess(Pin<ProcessObject> po) {
if (!po) { if (!mImpl) {
return false; return false;
} }
pid_t pid; return mImpl->addProcess(std::move(po));
int pidfd;
{
std::lock_guard lk(po->m);
pid = po->pid;
pidfd = po->pidfd;
if (pidfd < 0) {
return false;
} }
epoll_event ev{}; bool ProcessManager::running() const {
ev.events = EPOLLIN; return mImpl && mImpl->running();
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;
}
void ProcessManager::runLoop() {
constexpr int kMaxEvents = 64;
std::array<epoll_event, kMaxEvents> 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<ProcessObject> 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);
} }
ProcessManager &processes() { ProcessManager &processes() {
static ProcessManager mgr; static ProcessManager mgr;
if (!mgr.init()) { if (!mgr.init()) {
fprintf(stderr, "Failed to initialize ProcessManager\n"); std::fprintf(stderr, "Failed to initialize ProcessManager\n");
abort(); std::abort();
} }
return mgr; return mgr;
} }
@@ -307,7 +144,7 @@ static std::vector<std::filesystem::path> buildSearchDirectories() {
} }
}; };
addFromEnv("WIBO_PATH"); addFromEnv("WIBO_PATH");
addFromEnv("WINEPATH"); // Wine compatibility addFromEnv("WINEPATH");
addFromEnv("PATH"); addFromEnv("PATH");
return dirs; return dirs;
} }
@@ -371,22 +208,6 @@ std::optional<std::filesystem::path> resolveExecutable(const std::string &comman
return std::nullopt; return std::nullopt;
} }
static int spawnClone(pid_t &pid, int &pidfd, char **argv, char **envp) {
pid = static_cast<pid_t>(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<std::string> &args, Pin<kernel32::ProcessObject> &pinOut) { static int spawnInternal(const std::vector<std::string> &args, Pin<kernel32::ProcessObject> &pinOut) {
std::vector<char *> argv; std::vector<char *> argv;
argv.reserve(args.size() + 2); argv.reserve(args.size() + 2);
@@ -423,20 +244,19 @@ static int spawnInternal(const std::vector<std::string> &args, Pin<kernel32::Pro
envp.push_back(const_cast<char *>(s.c_str())); envp.push_back(const_cast<char *>(s.c_str()));
envp.push_back(nullptr); envp.push_back(nullptr);
pid_t pid = -1; detail::SpawnProcessInfo info;
int pidfd = -1; int rc = detail::spawnProcess(argv.data(), envp.data(), info);
int rc = spawnClone(pid, pidfd, argv.data(), envp.data());
if (rc != 0) { if (rc != 0) {
return rc; 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<kernel32::ProcessObject>(pid, pidfd); auto obj = make_pin<kernel32::ProcessObject>(info.pid, info.pidfd, true);
pinOut = obj.clone(); pinOut = obj.clone();
if (!processes().addProcess(std::move(obj))) { if (!processes().addProcess(std::move(obj))) {
fprintf(stderr, "Failed to add process to process manager\n"); std::fprintf(stderr, "Failed to add process to process manager\n");
abort(); std::abort();
} }
return 0; return 0;
} }
@@ -531,3 +351,4 @@ std::vector<std::string> splitCommandLine(const char *commandLine) {
} }
} // namespace wibo } // namespace wibo

278
src/processes_darwin.cpp Normal file
View File

@@ -0,0 +1,278 @@
#include "processes.h"
#include "common.h"
#include "handles.h"
#include "kernel32/internal.h"
#include <array>
#include <atomic>
#include <cerrno>
#include <csignal>
#include <cstring>
#include <filesystem>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <spawn.h>
#include <system_error>
#include <string>
#include <sys/event.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <thread>
#include <unordered_map>
#include <unistd.h>
#define ENUM_DYLD_BOOL
#include <mach-o/dyld.h>
using kernel32::ProcessObject;
namespace {
DWORD decodeExitStatus(int status) {
if (WIFEXITED(status)) {
return static_cast<DWORD>(WEXITSTATUS(status));
}
if (WIFSIGNALED(status)) {
return 0xC0000000u | static_cast<DWORD>(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<ProcessObject> 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<bool> mRunning{false};
std::thread mThread;
int mKqueueFd = -1;
uintptr_t mWakeIdent = 1;
std::unordered_map<pid_t, Pin<ProcessObject>> mReg;
};
} // namespace
namespace wibo::detail {
std::unique_ptr<ProcessManagerImpl> createProcessManagerImpl() {
return std::make_unique<DarwinProcessManager>();
}
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<ProcessObject> 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<uintptr_t>(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<struct kevent, kMaxEvents> 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<pid_t>(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<ProcessObject> 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);
}

261
src/processes_linux.cpp Normal file
View File

@@ -0,0 +1,261 @@
#include "processes.h"
#include "common.h"
#include "handles.h"
#include "kernel32/internal.h"
#include <array>
#include <atomic>
#include <cerrno>
#include <csignal>
#include <cstring>
#include <memory>
#include <shared_mutex>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <thread>
#include <unordered_map>
#include <unistd.h>
#include <linux/sched.h>
using kernel32::ProcessObject;
namespace {
inline DWORD decodeExitCode(const siginfo_t &si) {
switch (si.si_code) {
case CLD_EXITED:
return static_cast<DWORD>(si.si_status);
case CLD_KILLED:
case CLD_DUMPED:
return 0xC0000000u | static_cast<DWORD>(si.si_status);
default:
return 0;
}
}
class LinuxProcessManager final : public wibo::detail::ProcessManagerImpl {
public:
bool init() override;
void shutdown() override;
bool addProcess(Pin<ProcessObject> 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<bool> mRunning{false};
std::thread mThread;
int mEpollFd = -1;
int mWakeFd = -1;
std::unordered_map<int, Pin<ProcessObject>> mReg;
};
} // namespace
namespace wibo::detail {
std::unique_ptr<ProcessManagerImpl> createProcessManagerImpl() {
return std::make_unique<LinuxProcessManager>();
}
int spawnProcess(char *const argv[], char *const envp[], SpawnProcessInfo &info) {
pid_t pid = static_cast<pid_t>(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<ProcessObject> 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<epoll_event, kMaxEvents> 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<ProcessObject> 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);
}

View File

@@ -1,18 +1,25 @@
#include "macros.S" #include "macros.S"
#ifndef __APPLE__
.section .note.GNU-stack, "", @progbits .section .note.GNU-stack, "", @progbits
#endif
.text .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) #if defined(__x86_64__) && !defined(__APPLE__)
.globl x86_64_thread_setup
.type x86_64_thread_setup, @function # int tebThreadSetup(int entryNumber, TEB *teb)
x86_64_thread_setup: .globl tebThreadSetup
.type tebThreadSetup, @function
tebThreadSetup:
push rbx # save rbx push rbx # save rbx
mov r8, rsp # save host stack mov r8, rsp # save host stack
rdfsbase r9 # read host FS base rdfsbase r9 # read host FS base
mov rdx, qword ptr [rsi+TEB_SP] # fetch guest stack 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 ax, 0x2b # user data segment (Linux)
mov ds, ax # setup data segment mov ds, ax # setup data segment
mov es, ax # setup extra segment mov es, ax # setup extra segment
@@ -38,21 +45,23 @@ x86_64_thread_setup:
mov eax, -1 # return -1 mov eax, -1 # return -1
2: 2:
add esp, 0x10 # cleanup stack 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 cdqe # sign-extend eax to rax
mov rsp, r8 # switch to host stack mov rsp, r8 # switch to host stack
wrfsbase r9 # restore host FS base wrfsbase r9 # restore host FS base
pop rbx # restore rbx pop rbx # restore rbx
ret ret
.size x86_64_thread_setup, .-x86_64_thread_setup .size tebThreadSetup, .-tebThreadSetup
#endif // __x86_64__ #endif // __x86_64__
.altmacro
.code32 .code32
.macro stubThunkX number .macro stubThunkX number
#ifdef __x86_64__ #if defined(__APPLE__)
.globl __Z9stubThunkILm\()\number\()EEvv
__Z9stubThunkILm\()\number\()EEvv:
#elif defined(__x86_64__)
.globl _Z9stubThunkILm\()\number\()EEvv .globl _Z9stubThunkILm\()\number\()EEvv
.type _Z9stubThunkILm\()\number\()EEvv, @function .type _Z9stubThunkILm\()\number\()EEvv, @function
_Z9stubThunkILm\()\number\()EEvv: _Z9stubThunkILm\()\number\()EEvv:
@@ -64,11 +73,267 @@ _Z9stubThunkILj\()\number\()EEvv:
pop eax pop eax
push \number push \number
push eax push eax
#ifdef __APPLE__
jmp _thunk_entry_stubBase
#else
jmp thunk_entry_stubBase jmp thunk_entry_stubBase
#endif
.endm .endm
.set i, 0 stubThunkX 0
.rept 256 stubThunkX 1
stubThunkX %i stubThunkX 2
.set i, i+1 stubThunkX 3
.endr 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

View File

@@ -1,11 +1,13 @@
#pragma once #pragma once
#include "types.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#ifdef __x86_64__ #ifdef __x86_64__
int x86_64_thread_setup(int entry_number, void *teb); int tebThreadSetup(int entryNumber, TEB *teb);
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus

105
src/setup_darwin.cpp Normal file
View File

@@ -0,0 +1,105 @@
#include "common.h"
#include "setup.h"
#include "types.h"
#include <cerrno>
#include <cstdint>
#include <architecture/i386/table.h>
#include <i386/user_ldt.h>
// 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<uint16_t>(limit);
entry.code.base00 = static_cast<uint16_t>(base);
entry.code.base16 = static_cast<uint8_t>(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<uint8_t>(limit >> 16);
entry.code.opsz = DESC_CODE_32B;
entry.code.granular = granular;
entry.code.base24 = static_cast<uint8_t>(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<uintptr_t>(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<uint32_t>(tebBase));
// Rosetta 2 requires size 0x1000 (limit 0xFFF) specifically
ldt_entry fsLdt = createLdtEntry(static_cast<uint32_t>(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;
}

View File

@@ -538,7 +538,8 @@ typedef struct _TEB {
WORD CurrentGsSelector; WORD CurrentGsSelector;
void *CurrentStackPointer; void *CurrentStackPointer;
#ifdef __x86_64__ #ifdef __x86_64__
void *CurrentFsBase; void *HostFsBase;
void *HostGsBase;
#endif #endif
} TEB; } TEB;
typedef GUEST_PTR PTEB; typedef GUEST_PTR PTEB;

View File

@@ -49,6 +49,8 @@ if "LIBCLANG_PATH" in os.environ:
f"Warning: LIBCLANG_PATH={libclang_path} is not a file or directory\n" f"Warning: LIBCLANG_PATH={libclang_path} is not a file or directory\n"
) )
SYMBOL_PREFIX = "_" if sys.platform == "darwin" else ""
class Arch(str, Enum): class Arch(str, Enum):
X86 = "x86" X86 = "x86"
@@ -524,7 +526,7 @@ def emit_cc_thunk32(f: FuncInfo | TypedefInfo, lines: List[str]):
# Get current TEB # Get current TEB
if host_to_guest: if host_to_guest:
lines.append("\tmov ecx, gs:[currentThreadTeb@ntpoff]") lines.append("\tGET_TEB_HOST ecx")
else: else:
lines.append("\tmov ecx, fs:[TEB_SELF]") 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: if host_to_guest:
lines.append("\tmov ecx, fs:[TEB_SELF]") lines.append("\tmov ecx, fs:[TEB_SELF]")
else: else:
lines.append("\tmov ecx, gs:[currentThreadTeb@ntpoff]") lines.append("\tGET_TEB_HOST ecx")
lines.append("\tmov ax, fs") lines.append("\tmov ax, fs")
lines.append("\tmov dx, word ptr [ecx+TEB_FS_SEL]") lines.append("\tmov dx, word ptr [ecx+TEB_FS_SEL]")
lines.append("\tmov word ptr [ecx+TEB_FS_SEL], ax") 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.args,
f.source_cc, f.source_cc,
Arch.X86_64 if host_to_guest else Arch.X86, 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, skip_args=1 if host_to_guest else 0,
) )
target_layout = compute_arg_layout( target_layout = compute_arg_layout(
@@ -710,22 +712,23 @@ def emit_cc_thunk64(f: FuncInfo | TypedefInfo, lines: List[str]):
lines.append(".code64") lines.append(".code64")
# Save rbx and rbp # Save rbx and rbp
lines.append("\tpush rbx")
lines.append("\tpush rbp") lines.append("\tpush rbp")
lines.append("\tpush rbx")
# Stash host stack in r10 # Stash host stack in r10
lines.append("\tmov r10, rsp") lines.append("\tmov r10, rsp")
# Get current TEB # Get current TEB
lines.append("\tmov rcx, fs:[currentThreadTeb@tpoff]") lines.append("\tGET_TEB_HOST rbx")
if sys.platform != "darwin":
# Save FS base # Save FS base
lines.append("\trdfsbase r9") lines.append("\trdfsbase r9")
lines.append("\tmov qword ptr [rcx+TEB_FSBASE], r9") lines.append("\tmov qword ptr [rbx+TEB_FSBASE], r9")
# Save RSP and load guest stack # Save RSP and load guest stack
lines.append("\tmov rbp, qword ptr [rcx+TEB_SP]") lines.append("\tmov rbp, qword ptr [rbx+TEB_SP]")
lines.append("\tmov qword ptr [rcx+TEB_SP], rsp") lines.append("\tmov qword ptr [rbx+TEB_SP], rsp")
lines.append("\tmov rsp, rbp") lines.append("\tmov rsp, rbp")
# Allocate stack space for arguments # 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}") lines.append(f"\tmov {ptr_type} [rsp+{target.stack_offset}], {register}")
# Jump to 32-bit mode # Jump to 32-bit mode
lines.append("\tLJMP32") lines.append("\tLJMP32 rbx")
# Setup FS selector # 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") lines.append("\tmov fs, ax")
# Call into target # Call into target
lines.append(f"\tcall {call_target}") lines.append(f"\tcall {call_target}")
# Get current TEB # Get current TEB (32-bit code may clobber ebx)
lines.append("\tmov ecx, fs:[TEB_SELF]") lines.append("\tmov ebx, fs:[TEB_SELF]")
# Jump back to 64-bit # Jump back to 64-bit
lines.append("\tLJMP64") lines.append("\tLJMP64 ebx")
# Sign extend return value if necessary # Sign extend return value if necessary
if f.return_type.sign_extended: if f.return_type.sign_extended:
lines.append("\tcdqe") lines.append("\tcdqe")
if sys.platform != "darwin":
# Restore FS base # Restore FS base
lines.append("\tmov r9, qword ptr [rcx+TEB_FSBASE]") lines.append("\tmov r9, qword ptr [rbx+TEB_FSBASE]")
lines.append("\twrfsbase r9") lines.append("\twrfsbase r9")
# Restore host stack # Restore host stack
lines.append("\tmov rsp, qword ptr [rcx+TEB_SP]") lines.append("\tmov rsp, qword ptr [rbx+TEB_SP]")
lines.append("\tmov qword ptr [rcx+TEB_SP], rbp") lines.append("\tmov qword ptr [rbx+TEB_SP], rbp")
# Restore rbp, rbx and return # Restore rbp, rbx and return
lines.append("\tpop rbp")
lines.append("\tpop rbx") lines.append("\tpop rbx")
lines.append("\tpop rbp")
lines.append("\tret") lines.append("\tret")
else: else:
lines.append(".code32") lines.append(".code32")
@@ -796,27 +800,30 @@ def emit_cc_thunk64(f: FuncInfo | TypedefInfo, lines: List[str]):
lines.append("\tpush ebp") lines.append("\tpush ebp")
lines.append("\tpush esi") lines.append("\tpush esi")
lines.append("\tpush edi") lines.append("\tpush edi")
lines.append("\tpush ebx")
# Get current TEB # Get current TEB
lines.append("\tmov ecx, fs:[TEB_SELF]") lines.append("\tmov ebx, fs:[TEB_SELF]")
if sys.platform != "darwin":
# Save fs segment # Save fs segment
lines.append("\tmov di, fs") lines.append("\tmov di, fs")
lines.append("\tmov word ptr [ecx+TEB_FS_SEL], di") lines.append("\tmov word ptr [ebx+TEB_FS_SEL], di")
# Jump back to 64-bit # Jump back to 64-bit
lines.append("\tLJMP64") lines.append("\tLJMP64 ebx")
if sys.platform != "darwin":
# Restore FS base # Restore FS base
lines.append("\tmov r9, qword ptr [rcx+TEB_FSBASE]") lines.append("\tmov r9, qword ptr [rbx+TEB_FSBASE]")
lines.append("\twrfsbase r9") lines.append("\twrfsbase r9")
# Stash guest stack in r10 # Stash guest stack in r10
lines.append("\tmov r10, rsp") lines.append("\tmov r10, rsp")
# Restore host stack # Restore host stack
lines.append("\tmov rbp, qword ptr [rcx+TEB_SP]") lines.append("\tmov rbp, qword ptr [rbx+TEB_SP]")
lines.append("\tmov qword ptr [rcx+TEB_SP], rsp") lines.append("\tmov qword ptr [rbx+TEB_SP], rsp")
lines.append("\tmov rsp, rbp") lines.append("\tmov rsp, rbp")
# Allocate stack space for arguments # Allocate stack space for arguments
@@ -881,21 +888,20 @@ def emit_cc_thunk64(f: FuncInfo | TypedefInfo, lines: List[str]):
# Call into target # Call into target
lines.append(f"\tcall {call_target}") lines.append(f"\tcall {call_target}")
# Get current TEB
lines.append("\tmov rcx, fs:[currentThreadTeb@tpoff]")
# Restore host stack # Restore host stack
lines.append("\tmov rsp, qword ptr [rcx+TEB_SP]") lines.append("\tmov rsp, qword ptr [rbx+TEB_SP]")
lines.append("\tmov qword ptr [rcx+TEB_SP], rbp") lines.append("\tmov qword ptr [rbx+TEB_SP], rbp")
# Jump to 32-bit mode # Jump to 32-bit mode
lines.append("\tLJMP32") lines.append("\tLJMP32 rbx")
if sys.platform != "darwin":
# Setup FS selector # Setup FS selector
lines.append("\tmov di, word ptr [ecx+TEB_FS_SEL]") lines.append("\tmov di, word ptr [ebx+TEB_FS_SEL]")
lines.append("\tmov fs, di") lines.append("\tmov fs, di")
# Restore registers # Restore registers
lines.append("\tpop ebx")
lines.append("\tpop edi") lines.append("\tpop edi")
lines.append("\tpop esi") lines.append("\tpop esi")
lines.append("\tpop ebp") lines.append("\tpop ebp")
@@ -918,7 +924,7 @@ def emit_guest_to_host_thunks(
lines: List[str], dll: str, funcs: Iterable[FuncInfo], arch: Arch lines: List[str], dll: str, funcs: Iterable[FuncInfo], arch: Arch
) -> None: ) -> None:
for f in funcs: for f in funcs:
thunk = f"thunk_{dll}_{f.name}" thunk = f"{SYMBOL_PREFIX}thunk_{dll}_{f.name}"
lines.append("") lines.append("")
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})" f"# {f.qualified_ns}::{f.name} (source_cc={f.source_cc.name}, target_cc={f.target_cc.name}, variadic={f.variadic})"
@@ -933,9 +939,11 @@ def emit_guest_to_host_thunks(
details.append(f"sign_extended={arg.sign_extended}") details.append(f"sign_extended={arg.sign_extended}")
lines.append(f"\t# Arg {i} ({', '.join(details)})") lines.append(f"\t# Arg {i} ({', '.join(details)})")
lines.append(f".globl {thunk}") lines.append(f".globl {thunk}")
if sys.platform != "darwin":
lines.append(f".type {thunk}, @function") lines.append(f".type {thunk}, @function")
lines.append(f"{thunk}:") lines.append(f"{thunk}:")
emit_cc_thunk(f, lines, arch) emit_cc_thunk(f, lines, arch)
if sys.platform != "darwin":
lines.append(f".size {thunk}, .-{thunk}") lines.append(f".size {thunk}, .-{thunk}")
@@ -943,7 +951,7 @@ def emit_host_to_guest_thunks(
lines: List[str], typedefs: Iterable[TypedefInfo], arch: Arch lines: List[str], typedefs: Iterable[TypedefInfo], arch: Arch
) -> None: ) -> None:
for f in typedefs: for f in typedefs:
thunk = f"call_{f.name}" thunk = f"{SYMBOL_PREFIX}call_{f.name}"
lines.append("") lines.append("")
lines.append( lines.append(
f"# {f.name} (target_cc={f.target_cc.name}, variadic={f.variadic})" f"# {f.name} (target_cc={f.target_cc.name}, variadic={f.variadic})"
@@ -961,10 +969,15 @@ def emit_host_to_guest_thunks(
# details.append(f"class={f.return_type.arg_class.value}") # details.append(f"class={f.return_type.arg_class.value}")
# details.append(f"sign_extended={f.return_type.sign_extended}") # details.append(f"sign_extended={f.return_type.sign_extended}")
# lines.append(f"\t# Ret ({', '.join(details)})") # lines.append(f"\t# Ret ({', '.join(details)})")
if sys.platform == "darwin":
lines.append(f".globl {thunk}")
lines.append(f".weak_definition {thunk}")
else:
lines.append(f".weak {thunk}") lines.append(f".weak {thunk}")
lines.append(f".type {thunk}, @function") lines.append(f".type {thunk}, @function")
lines.append(f"{thunk}:") lines.append(f"{thunk}:")
emit_cc_thunk(f, lines, arch) emit_cc_thunk(f, lines, arch)
if sys.platform != "darwin":
lines.append(f".size {thunk}, .-{thunk}") lines.append(f".size {thunk}, .-{thunk}")
@@ -1079,12 +1092,16 @@ def main() -> int:
if args.arch == "x86": if args.arch == "x86":
arch = Arch.X86 arch = Arch.X86
target = "i686-pc-linux-gnu"
elif args.arch == "x86_64": elif args.arch == "x86_64":
arch = Arch.X86_64 arch = Arch.X86_64
if sys.platform == "darwin":
target = "x86_64-apple-darwin"
else:
target = "x86_64-pc-linux-gnu"
else: else:
raise ValueError(f"Unsupported architecture: {args.arch}") 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) tu = parse_tu(args.headers, args.incs, target)
funcs = collect_functions(tu, args.ns, arch) funcs = collect_functions(tu, args.ns, arch)
typedefs = collect_typedefs(tu, arch) typedefs = collect_typedefs(tu, arch)
@@ -1097,6 +1114,7 @@ def main() -> int:
lines: List[str] = [] lines: List[str] = []
lines.append("# Auto-generated thunks; DO NOT EDIT.") lines.append("# Auto-generated thunks; DO NOT EDIT.")
lines.append('#include "macros.S"') lines.append('#include "macros.S"')
if sys.platform != "darwin":
lines.append('.section .note.GNU-stack, "", @progbits') lines.append('.section .note.GNU-stack, "", @progbits')
lines.append(".text") lines.append(".text")