Split kernel32 into separate files (part 2)

This commit is contained in:
Luke Street 2025-10-02 00:20:28 -06:00
parent 9ea5b24b67
commit fa3ed4893a
53 changed files with 7352 additions and 6134 deletions

View File

@ -92,7 +92,6 @@ modernize-replace-random-shuffle,
modernize-return-braced-init-list,
modernize-shrink-to-fit,
modernize-unary-static-assert,
modernize-use-auto,
modernize-use-bool-literals,
modernize-use-emplace,
modernize-use-equals-default,
@ -143,4 +142,4 @@ readability-static-accessed-through-instance,
readability-static-definition-in-anonymous-namespace,
readability-string-compare,
readability-uniqueptr-delete-release,
readability-use-anyofallof'
readability-use-anyofallof'

View File

@ -4,20 +4,19 @@
- Core launcher logic lives in `main.cpp`, `loader.cpp`, `files.cpp`, `handles.cpp` and `module_registry.cpp`; shared interfaces in headers near them.
- Windows API shims reside in `dll/`, grouped by emulated DLL name; keep new APIs in the matching file instead of creating ad-hoc helpers.
- Reusable utilities sit in `strutil.*`, `processes.*` and `resources.*`; prefer extending these before introducing new singleton modules.
- Sample fixtures for exercising the loader live in `test/`; keep new repros small and self-contained.
- Sample fixtures for exercising the loader live in `test/`.
## Build, Test, and Development Commands
- `cmake -B build -GNinja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON` configures a 32-bit toolchain; ensure multilib packages are present.
- `cmake --build build --target wibo` compiles the shim; switch to `-DCMAKE_BUILD_TYPE=Release` for optimised binaries.
- `cmake -B build -GNinja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DBUILD_TESTING=ON` configures a 32-bit toolchain; ensure multilib packages are present.
- `cmake --build build --target wibo` compiles the program and tests.
- `./build/wibo /path/to/program.exe` runs a Windows binary. Use `WIBO_DEBUG=1` (or `--debug`/`-D`) for verbose logging. Use `--chdir`/`-C` to set the working directory.
- `cmake -B build -DBUILD_TESTING=ON` + `ctest --test-dir build --output-on-failure` runs the self-checking WinAPI fixtures (requires `i686-w64-mingw32-gcc` and `i686-w64-mingw32-windres`).
- `clang-format -i path/to/file.cpp` and `clang-tidy path/to/file.cpp -p build` keep contributions aligned with the repo's tooling.
- DON'T use `clang-format` on existing files, only new or heavily modified ones; the repo hasn't been fully formatted yet.
## Coding Style & Naming Conventions
- Formatting follows `.clang-format` (LLVM base, tabbed indentation width 4, 120 column limit); never hand-wrap differently.
- Prefer PascalCase for emulated Win32 entry points, camelCase for internal helpers, and SCREAMING_SNAKE_CASE for constants or macros.
- Document non-obvious control flow with short comments and keep platform-specific code paths behind descriptive helper functions.
- Use PascalCase for Win32 entry points, camelCase for internal helpers, SCREAMING_SNAKE_CASE for Win32 constants, kCamelCase for internal constants, and g_camelCase for globals.
- Put static functions and variables in anonymous namespaces at the top of the file.
## Shim Implementation Guidelines
- Target pre-XP behavior; our binaries are old and don't expect modern WinAPI behavior.
@ -26,14 +25,14 @@
- Add `DEBUG_LOG` calls to trace execution and parameter values; these are invaluable when diagnosing issues with real-world binaries.
## Testing Guidelines
- Fixture binaries live in `test/` and are compiled automatically when `BUILD_TESTING` is enabled; keep new repros small and self-contained (`test_<feature>.c`).
- Fixture tests live in `test/` and are compiled automatically with `i686-w64-mingw32-gcc` when `BUILD_TESTING` is enabled.
- Keep new repros small and self-contained (`test_<feature>.c`).
- All fixtures must self-assert; use `test_assert.h` helpers so `ctest` fails on mismatched WinAPI behaviour.
- Cross-compile new repros with `i686-w64-mingw32-gcc` (and `i686-w64-mingw32-windres` for resources); CMake handles this during the build, but direct invocation is useful while iterating.
- Run `ctest --test-dir build --output-on-failure` after rebuilding to verify changes; ensure failures print actionable diagnostics.
- Update `CMakeLists.txt` to add new fixture sources.
- Rebuild, then run tests with `ctest --test-dir build --output-on-failure`.
- Always run tests against `wine` manually to confirm expected behaviour. If `wine` fails, the expected behaviour is likely wrong. (`wine` is not perfect, but we can assume it's closer to Windows than we are.)
## Debugging Workflow
- Reproduce crashes under `gdb` (or `lldb`) with `-q -batch` to capture backtraces, register state, and the faulting instruction without interactive prompts.
- Enable `WIBO_DEBUG=1` and tee output to a log when running the guest binary; loader traces often pinpoint missing imports, resource lookups, or API shims that misbehave.
- Inspect relevant source right away—most issues stem from stubbed shims in `dll/`; compare the guest stack from `gdb` with those implementations.
- When host-side behaviour is suspect (filesystem, execve, etc.), rerun under `strace -f -o <log>`; this highlights missing files or permissions before the shim faults.
- If the `ghidra` MCP tool is available, request that the user import and analyze the guest binary; you can then use it to disassemble/decompile code around the crash site.
- Enable `WIBO_DEBUG=1` and output to a log (i.e. `&>/tmp/wibo.log`) when running the guest binary; loader traces often pinpoint missing imports, resource lookups, or API shims that misbehave. The answer is usually in the last few dozen lines before the crash.
- Inspect relevant source right away—most issues stem from stubbed shims in `dll/`.

View File

@ -38,9 +38,23 @@ add_executable(wibo
dll/kernel32.cpp
dll/kernel32/errhandlingapi.cpp
dll/kernel32/fibersapi.cpp
dll/kernel32/heapapi.cpp
dll/kernel32/memoryapi.cpp
dll/kernel32/handleapi.cpp
dll/kernel32/fileapi.cpp
dll/kernel32/debugapi.cpp
dll/kernel32/interlockedapi.cpp
dll/kernel32/libloaderapi.cpp
dll/kernel32/winbase.cpp
dll/kernel32/wincon.cpp
dll/kernel32/processenv.cpp
dll/kernel32/profileapi.cpp
dll/kernel32/processthreadsapi.cpp
dll/kernel32/stringapiset.cpp
dll/kernel32/sysinfoapi.cpp
dll/kernel32/timezoneapi.cpp
dll/kernel32/synchapi.cpp
dll/kernel32/winnls.cpp
dll/kernel32/wow64apiset.cpp
dll/lmgr.cpp
dll/mscoree.cpp

423
common.h
View File

@ -9,244 +9,239 @@
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <utility>
#include <sys/stat.h>
#include <unistd.h>
#include <unordered_map>
#include <utility>
#include <vector>
// On Windows, the incoming stack is aligned to a 4 byte boundary.
// force_align_arg_pointer will realign the stack to match GCC's 16 byte alignment.
#ifdef __clang__
#define WIN_ENTRY __attribute__((force_align_arg_pointer))
#else
#define WIN_ENTRY __attribute__((force_align_arg_pointer, callee_pop_aggregate_return(0)))
#endif
#define WIN_FUNC WIN_ENTRY __attribute__((stdcall))
#define DEBUG_LOG(...) \
do { \
if (wibo::debugEnabled) { \
wibo::debug_log(__VA_ARGS__); \
} \
} while (0)
#define DEBUG_LOG(...) \
do { \
if (wibo::debugEnabled) { \
wibo::debug_log(__VA_ARGS__); \
} \
} while (0)
#ifndef NDEBUG
#define VERBOSE_LOG(...) DEBUG_LOG(__VA_ARGS__)
#else
#define VERBOSE_LOG(...) ((void)0)
#endif
typedef void *HANDLE;
typedef void *HMODULE;
typedef void *PVOID;
typedef void *LPVOID;
typedef void *FARPROC;
typedef uint16_t WORD;
typedef uint32_t DWORD;
typedef DWORD *PDWORD;
typedef DWORD *LPDWORD;
typedef int32_t LONG;
typedef LONG *PLONG;
typedef uint32_t ULONG;
typedef ULONG *PULONG;
typedef int64_t LARGE_INTEGER;
typedef LARGE_INTEGER *PLARGE_INTEGER;
typedef uintptr_t ULONG_PTR;
typedef ULONG_PTR DWORD_PTR;
typedef DWORD_PTR *PDWORD_PTR;
typedef char *LPSTR;
typedef const char *LPCSTR;
typedef uint16_t *LPWSTR;
typedef const uint16_t *LPCWSTR;
typedef int BOOL;
typedef BOOL *PBOOL;
typedef unsigned char UCHAR;
typedef UCHAR *PUCHAR;
typedef size_t SIZE_T;
typedef SIZE_T *PSIZE_T;
typedef unsigned char BYTE;
typedef unsigned int UINT;
typedef void *HKEY;
typedef HKEY *PHKEY;
typedef DWORD REGSAM;
typedef LONG LSTATUS;
using HANDLE = void *;
using HMODULE = void *;
using HGLOBAL = HANDLE;
using HLOCAL = HANDLE;
using HRSRC = HANDLE;
using LPHANDLE = HANDLE *;
using PVOID = void *;
using LPVOID = void *;
using LPCVOID = const void *;
using FARPROC = void *;
using WORD = uint16_t;
using LPWORD = WORD *;
using LANGID = WORD;
using DWORD = uint32_t;
using PDWORD = DWORD *;
using LPDWORD = DWORD *;
using LONG = int32_t;
using PLONG = LONG *;
using ULONG = uint32_t;
using PULONG = ULONG *;
using LARGE_INTEGER = int64_t;
using PLARGE_INTEGER = LARGE_INTEGER *;
using ULONG_PTR = uintptr_t;
using UINT_PTR = uintptr_t;
using DWORD_PTR = ULONG_PTR;
using PDWORD_PTR = DWORD_PTR *;
using SHORT = int16_t;
using LPSTR = char *;
using LPCSTR = const char *;
using LPCCH = const char *;
using LPWSTR = uint16_t *;
using LPCWSTR = const uint16_t *;
using LPCWCH = const uint16_t *;
using WCHAR = uint16_t;
using LPCH = char *;
using LPWCH = uint16_t *;
using BOOL = int;
using PBOOL = BOOL *;
using LPBOOL = BOOL *;
using UCHAR = unsigned char;
using PUCHAR = UCHAR *;
using SIZE_T = size_t;
using PSIZE_T = SIZE_T *;
using BYTE = unsigned char;
using BOOLEAN = unsigned char;
using UINT = unsigned int;
using HKEY = void *;
using PHKEY = HKEY *;
using REGSAM = DWORD;
using LSTATUS = LONG;
using LCID = DWORD;
using LCTYPE = DWORD;
typedef struct _OVERLAPPED {
ULONG_PTR Internal;
ULONG_PTR InternalHigh;
union {
struct {
DWORD Offset;
DWORD OffsetHigh;
};
PVOID Pointer;
};
HANDLE hEvent;
} OVERLAPPED, *LPOVERLAPPED;
constexpr BOOL TRUE = 1;
constexpr BOOL FALSE = 0;
#define TRUE 1
#define FALSE 0
constexpr DWORD STILL_ACTIVE = 259;
#define STILL_ACTIVE 259
constexpr DWORD FILE_FLAG_OVERLAPPED = 0x40000000;
constexpr DWORD FILE_FLAG_NO_BUFFERING = 0x20000000;
#define TIME_ZONE_ID_UNKNOWN 0
#define TIME_ZONE_ID_STANDARD 1
#define TIME_ZONE_ID_DAYLIGHT 2
#define FILE_FLAG_OVERLAPPED 0x40000000
#define FILE_FLAG_NO_BUFFERING 0x20000000
#define MAX_PATH (260)
#define STD_INPUT_HANDLE ((DWORD) - 10)
#define STD_OUTPUT_HANDLE ((DWORD) - 11)
#define STD_ERROR_HANDLE ((DWORD) - 12)
constexpr DWORD STD_INPUT_HANDLE = ((DWORD)-10);
constexpr DWORD STD_OUTPUT_HANDLE = ((DWORD)-11);
constexpr DWORD STD_ERROR_HANDLE = ((DWORD)-12);
namespace wibo {
extern uint32_t lastError;
extern char **argv;
extern int argc;
extern std::filesystem::path guestExecutablePath;
extern std::string executableName;
extern std::string commandLine;
extern std::vector<uint16_t> commandLineW;
extern bool debugEnabled;
extern unsigned int debugIndent;
extern uint16_t tibSelector;
extern thread_local uint32_t lastError;
extern char **argv;
extern int argc;
extern std::filesystem::path guestExecutablePath;
extern std::string executableName;
extern std::string commandLine;
extern std::vector<uint16_t> commandLineW;
extern bool debugEnabled;
extern unsigned int debugIndent;
extern uint16_t tibSelector;
void debug_log(const char *fmt, ...);
void debug_log(const char *fmt, ...);
using ResolveByName = void *(*)(const char *);
using ResolveByOrdinal = void *(*)(uint16_t);
struct Module {
const char** names;
ResolveByName byName;
ResolveByOrdinal byOrdinal;
};
struct ModuleInfo;
void initializeModuleRegistry();
void shutdownModuleRegistry();
ModuleInfo *moduleInfoFromHandle(HMODULE module);
void setDllDirectoryOverride(const std::filesystem::path &path);
void clearDllDirectoryOverride();
std::optional<std::filesystem::path> dllDirectoryOverride();
ModuleInfo *findLoadedModule(const char *name);
void registerOnExitTable(void *table);
void addOnExitFunction(void *table, void (*func)());
void executeOnExitTable(void *table);
void runPendingOnExit(ModuleInfo &info);
using ResolveByName = void *(*)(const char *);
using ResolveByOrdinal = void *(*)(uint16_t);
struct Module {
const char **names;
ResolveByName byName;
ResolveByOrdinal byOrdinal;
};
struct ModuleInfo;
void initializeModuleRegistry();
void shutdownModuleRegistry();
ModuleInfo *moduleInfoFromHandle(HMODULE module);
void setDllDirectoryOverride(const std::filesystem::path &path);
void clearDllDirectoryOverride();
std::optional<std::filesystem::path> dllDirectoryOverride();
ModuleInfo *findLoadedModule(const char *name);
void registerOnExitTable(void *table);
void addOnExitFunction(void *table, void (*func)());
void executeOnExitTable(void *table);
void runPendingOnExit(ModuleInfo &info);
ModuleInfo *loadModule(const char *name);
void freeModule(ModuleInfo *info);
void *resolveFuncByName(ModuleInfo *info, const char *funcName);
void *resolveFuncByOrdinal(ModuleInfo *info, uint16_t ordinal);
void *resolveMissingImportByName(const char *dllName, const char *funcName);
void *resolveMissingImportByOrdinal(const char *dllName, uint16_t ordinal);
ModuleInfo *loadModule(const char *name);
void freeModule(ModuleInfo *info);
void *resolveFuncByName(ModuleInfo *info, const char *funcName);
void *resolveFuncByOrdinal(ModuleInfo *info, uint16_t ordinal);
void *resolveMissingImportByName(const char *dllName, const char *funcName);
void *resolveMissingImportByOrdinal(const char *dllName, uint16_t ordinal);
struct ResourceIdentifier {
ResourceIdentifier() : isString(false), id(0) {}
static ResourceIdentifier fromID(uint32_t value) {
ResourceIdentifier ident;
ident.isString = false;
ident.id = value;
return ident;
}
static ResourceIdentifier fromString(std::u16string value) {
ResourceIdentifier ident;
ident.isString = true;
ident.name = std::move(value);
return ident;
}
bool isString;
uint32_t id;
std::u16string name;
};
struct ResourceLocation {
const void *dataEntry = nullptr;
const void *data = nullptr;
uint32_t size = 0;
uint16_t language = 0;
};
struct ImageResourceDataEntry {
uint32_t offsetToData;
uint32_t size;
uint32_t codePage;
uint32_t reserved;
};
struct Executable {
Executable() = default;
~Executable();
bool loadPE(FILE *file, bool exec);
bool resolveImports();
void *imageBase = nullptr;
size_t imageSize = 0;
void *entryPoint = nullptr;
void *rsrcBase = nullptr;
uint32_t rsrcSize = 0;
uintptr_t preferredImageBase = 0;
intptr_t relocationDelta = 0;
uint32_t exportDirectoryRVA = 0;
uint32_t exportDirectorySize = 0;
uint32_t relocationDirectoryRVA = 0;
uint32_t relocationDirectorySize = 0;
uint32_t importDirectoryRVA = 0;
uint32_t importDirectorySize = 0;
uint32_t delayImportDirectoryRVA = 0;
uint32_t delayImportDirectorySize = 0;
bool execMapped = false;
bool importsResolved = false;
bool importsResolving = false;
bool findResource(const ResourceIdentifier &type, const ResourceIdentifier &name,
std::optional<uint16_t> language, ResourceLocation &out) const;
template <typename T>
T *fromRVA(uintptr_t rva) const {
return (T *) (rva + (uint8_t *) imageBase);
}
template <typename T>
T *fromRVA(T *rva) const {
return fromRVA<T>((uintptr_t) rva);
}
};
extern ModuleInfo *mainModule;
struct ModuleInfo {
// Windows-style handle to the module. For the main module, this is the image base.
// For other modules, this is a pointer to the ModuleInfo structure.
HMODULE handle;
// Original name used to load the module
std::string originalName;
// Normalized module name
std::string normalizedName;
// Full path to the loaded module
std::filesystem::path resolvedPath;
// Pointer to the built-in module, nullptr if loaded from file
const wibo::Module *module = nullptr;
// Loaded PE executable
std::unique_ptr<wibo::Executable> executable;
// Reference count, or UINT_MAX for built-in modules
unsigned int refCount = 0;
bool processAttachCalled = false;
bool processAttachSucceeded = false;
uint32_t exportOrdinalBase = 0;
std::vector<void *> exportsByOrdinal;
std::unordered_map<std::string, uint16_t> exportNameToOrdinal;
bool exportsInitialized = false;
std::vector<void *> onExitFunctions;
};
ModuleInfo *registerProcessModule(std::unique_ptr<Executable> executable, std::filesystem::path resolvedPath,
std::string originalName);
Executable *executableFromModule(HMODULE module);
/**
* HMODULE will be `nullptr` or `mainModule->imageBase` if it's the main module,
* otherwise it will be a pointer to a `wibo::ModuleInfo`.
*/
inline bool isMainModule(HMODULE hModule) {
return hModule == nullptr || hModule == reinterpret_cast<HMODULE>(mainModule) ||
(mainModule && mainModule->executable && hModule == mainModule->executable->imageBase);
struct ResourceIdentifier {
ResourceIdentifier() : isString(false), id(0) {}
static ResourceIdentifier fromID(uint32_t value) {
ResourceIdentifier ident;
ident.isString = false;
ident.id = value;
return ident;
}
static ResourceIdentifier fromString(std::u16string value) {
ResourceIdentifier ident;
ident.isString = true;
ident.name = std::move(value);
return ident;
}
bool isString;
uint32_t id;
std::u16string name;
};
struct ResourceLocation {
const void *dataEntry = nullptr;
const void *data = nullptr;
uint32_t size = 0;
uint16_t language = 0;
};
struct ImageResourceDataEntry {
uint32_t offsetToData;
uint32_t size;
uint32_t codePage;
uint32_t reserved;
};
struct Executable {
Executable() = default;
~Executable();
bool loadPE(FILE *file, bool exec);
bool resolveImports();
void *imageBase = nullptr;
size_t imageSize = 0;
void *entryPoint = nullptr;
void *rsrcBase = nullptr;
uint32_t rsrcSize = 0;
uintptr_t preferredImageBase = 0;
intptr_t relocationDelta = 0;
uint32_t exportDirectoryRVA = 0;
uint32_t exportDirectorySize = 0;
uint32_t relocationDirectoryRVA = 0;
uint32_t relocationDirectorySize = 0;
uint32_t importDirectoryRVA = 0;
uint32_t importDirectorySize = 0;
uint32_t delayImportDirectoryRVA = 0;
uint32_t delayImportDirectorySize = 0;
bool execMapped = false;
bool importsResolved = false;
bool importsResolving = false;
bool findResource(const ResourceIdentifier &type, const ResourceIdentifier &name, std::optional<uint16_t> language,
ResourceLocation &out) const;
template <typename T> T *fromRVA(uintptr_t rva) const { return (T *)(rva + (uint8_t *)imageBase); }
template <typename T> T *fromRVA(T *rva) const { return fromRVA<T>((uintptr_t)rva); }
};
extern ModuleInfo *mainModule;
struct ModuleInfo {
// Windows-style handle to the module. For the main module, this is the image base.
// For other modules, this is a pointer to the ModuleInfo structure.
HMODULE handle;
// Original name used to load the module
std::string originalName;
// Normalized module name
std::string normalizedName;
// Full path to the loaded module
std::filesystem::path resolvedPath;
// Pointer to the built-in module, nullptr if loaded from file
const wibo::Module *module = nullptr;
// Loaded PE executable
std::unique_ptr<wibo::Executable> executable;
// Reference count, or UINT_MAX for built-in modules
unsigned int refCount = 0;
bool processAttachCalled = false;
bool processAttachSucceeded = false;
uint32_t exportOrdinalBase = 0;
std::vector<void *> exportsByOrdinal;
std::unordered_map<std::string, uint16_t> exportNameToOrdinal;
bool exportsInitialized = false;
std::vector<void *> onExitFunctions;
};
ModuleInfo *registerProcessModule(std::unique_ptr<Executable> executable, std::filesystem::path resolvedPath,
std::string originalName);
Executable *executableFromModule(HMODULE module);
/**
* HMODULE will be `nullptr` or `mainModule->imageBase` if it's the main module,
* otherwise it will be a pointer to a `wibo::ModuleInfo`.
*/
inline bool isMainModule(HMODULE hModule) {
return hModule == nullptr || hModule == reinterpret_cast<HMODULE>(mainModule) ||
(mainModule && mainModule->executable && hModule == mainModule->executable->imageBase);
}
} // namespace wibo

File diff suppressed because it is too large Load Diff

13
dll/kernel32/debugapi.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "debugapi.h"
#include "common.h"
#include "errors.h"
namespace kernel32 {
BOOL WIN_FUNC IsDebuggerPresent() {
DEBUG_LOG("STUB: IsDebuggerPresent()\n");
wibo::lastError = ERROR_SUCCESS;
return FALSE;
}
} // namespace kernel32

9
dll/kernel32/debugapi.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include "common.h"
namespace kernel32 {
BOOL WIN_FUNC IsDebuggerPresent();
} // namespace kernel32

View File

@ -1,13 +1,18 @@
#include "errhandlingapi.h"
#include "common.h"
#include "kernel32.h"
#include "errors.h"
namespace {
LPTOP_LEVEL_EXCEPTION_FILTER g_topLevelExceptionFilter = nullptr;
UINT g_processErrorMode = 0;
} // namespace
namespace kernel32 {
void setLastErrorFromErrno() { wibo::lastError = wibo::winErrorFromErrno(errno); }
DWORD WIN_FUNC GetLastError() {
DEBUG_LOG("GetLastError() -> %u\n", wibo::lastError);
return wibo::lastError;

View File

@ -0,0 +1,44 @@
#pragma once
#include "common.h"
constexpr DWORD EXCEPTION_MAXIMUM_PARAMETERS = 15;
struct EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
};
using PEXCEPTION_RECORD = EXCEPTION_RECORD *;
using PCONTEXT = void *;
struct EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ContextRecord;
};
using PEXCEPTION_POINTERS = EXCEPTION_POINTERS *;
using PVECTORED_EXCEPTION_HANDLER = LONG(WIN_FUNC *)(PEXCEPTION_POINTERS ExceptionInfo);
using LPTOP_LEVEL_EXCEPTION_FILTER = LONG(WIN_FUNC *)(PEXCEPTION_POINTERS ExceptionInfo);
constexpr LONG EXCEPTION_CONTINUE_EXECUTION = static_cast<LONG>(-1);
constexpr LONG EXCEPTION_CONTINUE_SEARCH = 0;
constexpr LONG EXCEPTION_EXECUTE_HANDLER = 1;
namespace kernel32 {
DWORD WIN_FUNC GetLastError();
void WIN_FUNC SetLastError(DWORD dwErrCode);
void WIN_FUNC RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, DWORD nNumberOfArguments,
const ULONG_PTR *lpArguments);
PVOID WIN_FUNC AddVectoredExceptionHandler(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler);
LPTOP_LEVEL_EXCEPTION_FILTER WIN_FUNC
SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
LONG WIN_FUNC UnhandledExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo);
UINT WIN_FUNC SetErrorMode(UINT uMode);
} // namespace kernel32

View File

@ -1,20 +1,24 @@
#include "fibersapi.h"
#include "common.h"
#include "errors.h"
#include "kernel32.h"
namespace {
constexpr DWORD kMaxFlsValues = 0x100;
bool g_flsValuesUsed[kMaxFlsValues] = {false};
LPVOID g_flsValues[kMaxFlsValues] = {nullptr};
} // namespace
namespace kernel32 {
constexpr size_t MAX_FLS_VALUES = 0x100;
static bool flsValuesUsed[MAX_FLS_VALUES] = {false};
static void *flsValues[MAX_FLS_VALUES];
DWORD WIN_FUNC FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback) {
DEBUG_LOG("FlsAlloc(%p)", lpCallback);
// If the function succeeds, the return value is an FLS index initialized to zero.
for (size_t i = 0; i < MAX_FLS_VALUES; i++) {
if (flsValuesUsed[i] == false) {
flsValuesUsed[i] = true;
flsValues[i] = nullptr;
for (DWORD i = 0; i < kMaxFlsValues; i++) {
if (g_flsValuesUsed[i] == false) {
g_flsValuesUsed[i] = true;
g_flsValues[i] = nullptr;
DEBUG_LOG(" -> %d\n", i);
return i;
}
@ -26,8 +30,8 @@ DWORD WIN_FUNC FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback) {
BOOL WIN_FUNC FlsFree(DWORD dwFlsIndex) {
DEBUG_LOG("FlsFree(%u)\n", dwFlsIndex);
if (dwFlsIndex >= 0 && dwFlsIndex < MAX_FLS_VALUES && flsValuesUsed[dwFlsIndex]) {
flsValuesUsed[dwFlsIndex] = false;
if (dwFlsIndex >= 0 && dwFlsIndex < kMaxFlsValues && g_flsValuesUsed[dwFlsIndex]) {
g_flsValuesUsed[dwFlsIndex] = false;
return TRUE;
} else {
wibo::lastError = 1;
@ -38,8 +42,8 @@ BOOL WIN_FUNC FlsFree(DWORD dwFlsIndex) {
PVOID WIN_FUNC FlsGetValue(DWORD dwFlsIndex) {
VERBOSE_LOG("FlsGetValue(%u)\n", dwFlsIndex);
PVOID result = nullptr;
if (dwFlsIndex >= 0 && dwFlsIndex < MAX_FLS_VALUES && flsValuesUsed[dwFlsIndex]) {
result = flsValues[dwFlsIndex];
if (dwFlsIndex >= 0 && dwFlsIndex < kMaxFlsValues && g_flsValuesUsed[dwFlsIndex]) {
result = g_flsValues[dwFlsIndex];
// See https://learn.microsoft.com/en-us/windows/win32/api/fibersapi/nf-fibersapi-flsgetvalue
wibo::lastError = ERROR_SUCCESS;
} else {
@ -51,8 +55,8 @@ PVOID WIN_FUNC FlsGetValue(DWORD dwFlsIndex) {
BOOL WIN_FUNC FlsSetValue(DWORD dwFlsIndex, PVOID lpFlsData) {
VERBOSE_LOG("FlsSetValue(%u, %p)\n", dwFlsIndex, lpFlsData);
if (dwFlsIndex >= 0 && dwFlsIndex < MAX_FLS_VALUES && flsValuesUsed[dwFlsIndex]) {
flsValues[dwFlsIndex] = lpFlsData;
if (dwFlsIndex >= 0 && dwFlsIndex < kMaxFlsValues && g_flsValuesUsed[dwFlsIndex]) {
g_flsValues[dwFlsIndex] = lpFlsData;
return TRUE;
} else {
wibo::lastError = 1;

16
dll/kernel32/fibersapi.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include "common.h"
using PFLS_CALLBACK_FUNCTION = void (*)(void *);
constexpr DWORD FLS_OUT_OF_INDEXES = 0xFFFFFFFF;
namespace kernel32 {
DWORD WIN_FUNC FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback);
BOOL WIN_FUNC FlsFree(DWORD dwFlsIndex);
PVOID WIN_FUNC FlsGetValue(DWORD dwFlsIndex);
BOOL WIN_FUNC FlsSetValue(DWORD dwFlsIndex, PVOID lpFlsData);
} // namespace kernel32

564
dll/kernel32/fileapi.cpp Normal file
View File

@ -0,0 +1,564 @@
#include "fileapi.h"
#include "errors.h"
#include "files.h"
#include "internal.h"
#include "strutil.h"
#include <algorithm>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <fcntl.h>
#include <filesystem>
#include <fnmatch.h>
#include <random>
#include <string>
#include <system_error>
#include <unistd.h>
namespace {
using random_shorts_engine =
std::independent_bits_engine<std::default_random_engine, sizeof(unsigned short) * 8, unsigned short>;
constexpr uintptr_t kPseudoFindHandleValue = 1;
const HANDLE kPseudoFindHandle = reinterpret_cast<HANDLE>(kPseudoFindHandleValue);
constexpr uint64_t kWindowsTicksPerSecond = 10000000ULL;
constexpr uint64_t kSecondsBetween1601And1970 = 11644473600ULL;
const FILETIME kDefaultFindFileTime = {
static_cast<DWORD>((kSecondsBetween1601And1970 * kWindowsTicksPerSecond) & 0xFFFFFFFFULL),
static_cast<DWORD>((kSecondsBetween1601And1970 * kWindowsTicksPerSecond) >> 32)};
struct FindFirstFileHandle {
std::filesystem::directory_iterator it;
std::filesystem::directory_iterator end;
std::string pattern;
};
struct FullPathInfo {
std::string path;
size_t filePartOffset = std::string::npos;
};
bool computeFullPath(const std::string &input, FullPathInfo &outInfo) {
bool endsWithSeparator = false;
if (!input.empty()) {
char last = input.back();
endsWithSeparator = (last == '\\' || last == '/');
}
std::filesystem::path hostPath = files::pathFromWindows(input.c_str());
std::error_code ec;
std::filesystem::path absPath = std::filesystem::absolute(hostPath, ec);
if (ec) {
errno = ec.value();
kernel32::setLastErrorFromErrno();
return false;
}
std::string windowsPath = files::pathToWindows(absPath);
if (endsWithSeparator && !windowsPath.empty() && windowsPath.back() != '\\') {
windowsPath.push_back('\\');
}
if (!windowsPath.empty() && windowsPath.back() != '\\') {
size_t lastSlash = windowsPath.find_last_of('\\');
if (lastSlash == std::string::npos) {
outInfo.filePartOffset = 0;
} else if (lastSlash + 1 < windowsPath.size()) {
outInfo.filePartOffset = lastSlash + 1;
}
} else {
outInfo.filePartOffset = std::string::npos;
}
outInfo.path = std::move(windowsPath);
return true;
}
inline bool isPseudoHandle(HANDLE handle) { return reinterpret_cast<uintptr_t>(handle) == kPseudoFindHandleValue; }
inline void setCommonFindDataFields(WIN32_FIND_DATAA &data) {
data.ftCreationTime = kDefaultFindFileTime;
data.ftLastAccessTime = kDefaultFindFileTime;
data.ftLastWriteTime = kDefaultFindFileTime;
data.dwFileAttributes = 0;
data.nFileSizeHigh = 0;
data.nFileSizeLow = 0;
data.dwReserved0 = 0;
data.dwReserved1 = 0;
data.cFileName[0] = '\0';
data.cAlternateFileName[0] = '\0';
}
inline void setCommonFindDataFields(WIN32_FIND_DATAW &data) {
data.ftCreationTime = kDefaultFindFileTime;
data.ftLastAccessTime = kDefaultFindFileTime;
data.ftLastWriteTime = kDefaultFindFileTime;
data.dwFileAttributes = 0;
data.nFileSizeHigh = 0;
data.nFileSizeLow = 0;
data.dwReserved0 = 0;
data.dwReserved1 = 0;
data.cFileName[0] = 0;
data.cAlternateFileName[0] = 0;
}
DWORD computeAttributesAndSize(const std::filesystem::path &path, DWORD &sizeHigh, DWORD &sizeLow) {
std::error_code ec;
auto status = std::filesystem::status(path, ec);
uint64_t fileSize = 0;
DWORD attributes = 0;
if (status.type() == std::filesystem::file_type::directory) {
attributes |= FILE_ATTRIBUTE_DIRECTORY;
}
if (status.type() == std::filesystem::file_type::regular) {
attributes |= FILE_ATTRIBUTE_NORMAL;
fileSize = std::filesystem::file_size(path, ec);
}
sizeHigh = static_cast<DWORD>(fileSize >> 32);
sizeLow = static_cast<DWORD>(fileSize);
return attributes;
}
void setFindFileDataFromPath(const std::filesystem::path &path, WIN32_FIND_DATAA &data) {
setCommonFindDataFields(data);
data.dwFileAttributes = computeAttributesAndSize(path, data.nFileSizeHigh, data.nFileSizeLow);
std::string fileName = path.filename().string();
if (fileName.size() >= MAX_PATH) {
fileName.resize(MAX_PATH - 1);
}
std::strncpy(data.cFileName, fileName.c_str(), MAX_PATH);
data.cFileName[MAX_PATH - 1] = '\0';
std::strncpy(data.cAlternateFileName, "8P3FMTFN.BAD", sizeof(data.cAlternateFileName));
data.cAlternateFileName[sizeof(data.cAlternateFileName) - 1] = '\0';
}
void setFindFileDataFromPath(const std::filesystem::path &path, WIN32_FIND_DATAW &data) {
setCommonFindDataFields(data);
data.dwFileAttributes = computeAttributesAndSize(path, data.nFileSizeHigh, data.nFileSizeLow);
std::string fileName = path.filename().string();
auto wideName = stringToWideString(fileName.c_str());
size_t copyLen = std::min<size_t>(MAX_PATH - 1, wstrlen(wideName.data()));
wstrncpy(data.cFileName, wideName.data(), copyLen);
data.cFileName[copyLen] = 0;
auto wideAlt = stringToWideString("8P3FMTFN.BAD");
copyLen = std::min<size_t>(sizeof(data.cAlternateFileName) / sizeof(data.cAlternateFileName[0]) - 1,
wstrlen(wideAlt.data()));
wstrncpy(data.cAlternateFileName, wideAlt.data(), copyLen);
data.cAlternateFileName[copyLen] = 0;
}
bool nextMatch(FindFirstFileHandle &handle, std::filesystem::path &outPath) {
for (; handle.it != handle.end; ++handle.it) {
const auto current = *handle.it;
if (fnmatch(handle.pattern.c_str(), current.path().filename().c_str(), 0) == 0) {
outPath = current.path();
++handle.it;
return true;
}
}
return false;
}
bool initializeEnumeration(const std::filesystem::path &parent, const std::string &pattern, FindFirstFileHandle &handle,
std::filesystem::path &firstMatch) {
if (pattern.empty()) {
return false;
}
handle = FindFirstFileHandle{std::filesystem::directory_iterator(parent), std::filesystem::directory_iterator(),
pattern};
return nextMatch(handle, firstMatch);
}
} // namespace
namespace kernel32 {
DWORD WIN_FUNC GetFullPathNameA(LPCSTR lpFileName, DWORD nBufferLength, LPSTR lpBuffer, LPSTR *lpFilePart) {
DEBUG_LOG("GetFullPathNameA(%s, %u)\n", lpFileName ? lpFileName : "(null)", nBufferLength);
if (lpFilePart) {
*lpFilePart = nullptr;
}
if (!lpFileName) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
FullPathInfo info;
if (!computeFullPath(lpFileName, info)) {
return 0;
}
DEBUG_LOG(" -> %s\n", info.path.c_str());
const size_t pathLen = info.path.size();
const auto required = static_cast<DWORD>(pathLen + 1);
if (nBufferLength == 0) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return required;
}
if (!lpBuffer) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
if (nBufferLength < required) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return required;
}
memcpy(lpBuffer, info.path.c_str(), pathLen);
lpBuffer[pathLen] = '\0';
if (lpFilePart) {
if (info.filePartOffset != std::string::npos && info.filePartOffset < pathLen) {
*lpFilePart = lpBuffer + info.filePartOffset;
} else {
*lpFilePart = nullptr;
}
}
wibo::lastError = ERROR_SUCCESS;
return static_cast<DWORD>(pathLen);
}
DWORD WIN_FUNC GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength, LPWSTR lpBuffer, LPWSTR *lpFilePart) {
DEBUG_LOG("GetFullPathNameW(%p, %u)\n", lpFileName, nBufferLength);
if (lpFilePart) {
*lpFilePart = nullptr;
}
if (!lpFileName) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
std::string narrow = wideStringToString(lpFileName);
FullPathInfo info;
if (!computeFullPath(narrow, info)) {
return 0;
}
DEBUG_LOG(" -> %s\n", info.path.c_str());
auto widePath = stringToWideString(info.path.c_str());
const size_t wideLen = widePath.size();
const auto required = static_cast<DWORD>(wideLen);
if (nBufferLength == 0) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return required;
}
if (!lpBuffer) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
if (nBufferLength < required) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return required;
}
std::copy(widePath.begin(), widePath.end(), lpBuffer);
if (lpFilePart) {
if (info.filePartOffset != std::string::npos && info.filePartOffset < info.path.size()) {
*lpFilePart = lpBuffer + info.filePartOffset;
} else {
*lpFilePart = nullptr;
}
}
wibo::lastError = ERROR_SUCCESS;
return static_cast<DWORD>(wideLen - 1);
}
DWORD WIN_FUNC GetShortPathNameA(LPCSTR lpszLongPath, LPSTR lpszShortPath, DWORD cchBuffer) {
DEBUG_LOG("GetShortPathNameA(%s)\n", lpszLongPath ? lpszLongPath : "(null)");
if (!lpszLongPath || !lpszShortPath) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
std::filesystem::path absPath = std::filesystem::absolute(files::pathFromWindows(lpszLongPath));
std::string absStr = files::pathToWindows(absPath);
DWORD required = static_cast<DWORD>(absStr.length() + 1);
if (cchBuffer < required) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return required;
}
strcpy(lpszShortPath, absStr.c_str());
wibo::lastError = ERROR_SUCCESS;
return required - 1;
}
DWORD WIN_FUNC GetShortPathNameW(LPCWSTR lpszLongPath, LPWSTR lpszShortPath, DWORD cchBuffer) {
if (!lpszLongPath || !lpszShortPath) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
std::string longPath = wideStringToString(lpszLongPath);
DEBUG_LOG("GetShortPathNameW(%s)\n", longPath.c_str());
std::filesystem::path absPath = std::filesystem::absolute(files::pathFromWindows(longPath.c_str()));
std::string absStr = files::pathToWindows(absPath);
auto absStrW = stringToWideString(absStr.c_str());
size_t len = wstrlen(absStrW.data());
DWORD required = static_cast<DWORD>(len + 1);
if (cchBuffer < required) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return required;
}
wstrncpy(lpszShortPath, absStrW.data(), len + 1);
wibo::lastError = ERROR_SUCCESS;
return static_cast<DWORD>(len);
}
UINT WIN_FUNC GetTempFileNameA(LPCSTR lpPathName, LPCSTR lpPrefixString, UINT uUnique, LPSTR lpTempFileName) {
DEBUG_LOG("GetTempFileNameA(%s, %s, %u)\n", lpPathName ? lpPathName : "(null)",
lpPrefixString ? lpPrefixString : "(null)", uUnique);
if (!lpPathName || !lpPrefixString || !lpTempFileName) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
if (strlen(lpPathName) > MAX_PATH - 14) {
wibo::lastError = ERROR_BUFFER_OVERFLOW;
return 0;
}
char uniqueStr[20];
std::filesystem::path path;
if (uUnique == 0) {
std::random_device rd;
random_shorts_engine rse(rd());
while (true) {
uUnique = rse();
if (uUnique == 0) {
continue;
}
snprintf(uniqueStr, sizeof(uniqueStr), "%.3s%X.TMP", lpPrefixString, uUnique);
path = files::pathFromWindows(lpPathName) / uniqueStr;
int fd = open(path.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0644);
if (fd >= 0) {
close(fd);
break;
}
}
} else {
snprintf(uniqueStr, sizeof(uniqueStr), "%.3s%X.TMP", lpPrefixString, uUnique & 0xFFFF);
path = files::pathFromWindows(lpPathName) / uniqueStr;
}
std::string str = files::pathToWindows(path);
DEBUG_LOG(" -> %s\n", str.c_str());
strncpy(lpTempFileName, str.c_str(), MAX_PATH);
lpTempFileName[MAX_PATH - 1] = '\0';
wibo::lastError = ERROR_SUCCESS;
return uUnique;
}
DWORD WIN_FUNC GetTempPathA(DWORD nBufferLength, LPSTR lpBuffer) {
DEBUG_LOG("GetTempPathA(%u, %p)\n", nBufferLength, lpBuffer);
if (nBufferLength == 0 || lpBuffer == nullptr) {
wibo::lastError = ERROR_INVALID_PARAMETER;
DEBUG_LOG(" -> ERROR_INVALID_PARAMETER\n");
return 0;
}
const char *path = getenv("WIBO_TMP_DIR");
if (!path) {
path = "Z:\\tmp\\";
}
size_t len = strlen(path);
if (len + 1 > nBufferLength) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
DEBUG_LOG(" -> ERROR_INSUFFICIENT_BUFFER\n");
return static_cast<DWORD>(len + 1);
}
DEBUG_LOG(" -> %s\n", path);
strncpy(lpBuffer, path, nBufferLength);
lpBuffer[nBufferLength - 1] = '\0';
wibo::lastError = ERROR_SUCCESS;
return static_cast<DWORD>(len);
}
HANDLE WIN_FUNC FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData) {
DEBUG_LOG("FindFirstFileA(%s, %p)", lpFileName ? lpFileName : "(null)", lpFindFileData);
if (!lpFileName || !lpFindFileData) {
wibo::lastError = ERROR_INVALID_PARAMETER;
DEBUG_LOG(" -> ERROR_INVALID_PARAMETER\n");
return INVALID_HANDLE_VALUE;
}
std::filesystem::path hostPath = files::pathFromWindows(lpFileName);
DEBUG_LOG(" -> %s\n", hostPath.c_str());
std::error_code ec;
auto status = std::filesystem::status(hostPath, ec);
setCommonFindDataFields(*lpFindFileData);
if (status.type() == std::filesystem::file_type::regular) {
setFindFileDataFromPath(hostPath, *lpFindFileData);
wibo::lastError = ERROR_SUCCESS;
return kPseudoFindHandle;
}
std::filesystem::path parent = hostPath.parent_path();
if (parent.empty()) {
parent = ".";
}
if (!std::filesystem::exists(parent)) {
wibo::lastError = ERROR_PATH_NOT_FOUND;
return INVALID_HANDLE_VALUE;
}
std::filesystem::path match;
auto *handle = new FindFirstFileHandle();
if (!initializeEnumeration(parent, hostPath.filename().string(), *handle, match)) {
delete handle;
wibo::lastError = ERROR_FILE_NOT_FOUND;
return INVALID_HANDLE_VALUE;
}
setFindFileDataFromPath(match, *lpFindFileData);
wibo::lastError = ERROR_SUCCESS;
return reinterpret_cast<HANDLE>(handle);
}
HANDLE WIN_FUNC FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData) {
DEBUG_LOG("FindFirstFileW(%p, %p)", lpFileName, lpFindFileData);
if (!lpFileName || !lpFindFileData) {
wibo::lastError = ERROR_INVALID_PARAMETER;
DEBUG_LOG(" -> ERROR_INVALID_PARAMETER\n");
return INVALID_HANDLE_VALUE;
}
std::string narrowName = wideStringToString(lpFileName);
std::filesystem::path hostPath = files::pathFromWindows(narrowName.c_str());
DEBUG_LOG(", %s -> %s\n", narrowName.c_str(), hostPath.c_str());
std::error_code ec;
auto status = std::filesystem::status(hostPath, ec);
setCommonFindDataFields(*lpFindFileData);
if (status.type() == std::filesystem::file_type::regular) {
setFindFileDataFromPath(hostPath, *lpFindFileData);
wibo::lastError = ERROR_SUCCESS;
return kPseudoFindHandle;
}
std::filesystem::path parent = hostPath.parent_path();
if (parent.empty()) {
parent = ".";
}
if (!std::filesystem::exists(parent)) {
wibo::lastError = ERROR_PATH_NOT_FOUND;
return INVALID_HANDLE_VALUE;
}
std::filesystem::path match;
auto *handle = new FindFirstFileHandle();
if (!initializeEnumeration(parent, hostPath.filename().string(), *handle, match)) {
delete handle;
wibo::lastError = ERROR_FILE_NOT_FOUND;
return INVALID_HANDLE_VALUE;
}
setFindFileDataFromPath(match, *lpFindFileData);
wibo::lastError = ERROR_SUCCESS;
return reinterpret_cast<HANDLE>(handle);
}
HANDLE WIN_FUNC FindFirstFileExA(LPCSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags) {
DEBUG_LOG("FindFirstFileExA(%s, %d, %p, %d, %p, 0x%x) -> ", lpFileName ? lpFileName : "(null)", fInfoLevelId,
lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags);
(void)fInfoLevelId;
(void)fSearchOp;
(void)lpSearchFilter;
(void)dwAdditionalFlags;
return FindFirstFileA(lpFileName, static_cast<LPWIN32_FIND_DATAA>(lpFindFileData));
}
BOOL WIN_FUNC FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData) {
DEBUG_LOG("FindNextFileA(%p, %p)\n", hFindFile, lpFindFileData);
if (!lpFindFileData) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
if (isPseudoHandle(hFindFile)) {
wibo::lastError = ERROR_NO_MORE_FILES;
return FALSE;
}
auto *handle = reinterpret_cast<FindFirstFileHandle *>(hFindFile);
if (!handle) {
wibo::lastError = ERROR_INVALID_HANDLE;
return FALSE;
}
std::filesystem::path match;
if (!nextMatch(*handle, match)) {
wibo::lastError = ERROR_NO_MORE_FILES;
return FALSE;
}
setFindFileDataFromPath(match, *lpFindFileData);
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC FindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData) {
DEBUG_LOG("FindNextFileW(%p, %p)\n", hFindFile, lpFindFileData);
if (!lpFindFileData) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
if (isPseudoHandle(hFindFile)) {
wibo::lastError = ERROR_NO_MORE_FILES;
return FALSE;
}
auto *handle = reinterpret_cast<FindFirstFileHandle *>(hFindFile);
if (!handle) {
wibo::lastError = ERROR_INVALID_HANDLE;
return FALSE;
}
std::filesystem::path match;
if (!nextMatch(*handle, match)) {
wibo::lastError = ERROR_NO_MORE_FILES;
return FALSE;
}
setFindFileDataFromPath(match, *lpFindFileData);
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC FindClose(HANDLE hFindFile) {
DEBUG_LOG("FindClose(%p)\n", hFindFile);
if (isPseudoHandle(hFindFile) || hFindFile == nullptr) {
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
auto *handle = reinterpret_cast<FindFirstFileHandle *>(hFindFile);
if (!handle) {
wibo::lastError = ERROR_INVALID_HANDLE;
return FALSE;
}
delete handle;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
} // namespace kernel32

22
dll/kernel32/fileapi.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include "common.h"
#include "minwinbase.h"
namespace kernel32 {
DWORD WIN_FUNC GetFullPathNameA(LPCSTR lpFileName, DWORD nBufferLength, LPSTR lpBuffer, LPSTR *lpFilePart);
DWORD WIN_FUNC GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength, LPWSTR lpBuffer, LPWSTR *lpFilePart);
DWORD WIN_FUNC GetShortPathNameA(LPCSTR lpszLongPath, LPSTR lpszShortPath, DWORD cchBuffer);
DWORD WIN_FUNC GetShortPathNameW(LPCWSTR lpszLongPath, LPWSTR lpszShortPath, DWORD cchBuffer);
UINT WIN_FUNC GetTempFileNameA(LPCSTR lpPathName, LPCSTR lpPrefixString, UINT uUnique, LPSTR lpTempFileName);
DWORD WIN_FUNC GetTempPathA(DWORD nBufferLength, LPSTR lpBuffer);
HANDLE WIN_FUNC FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData);
HANDLE WIN_FUNC FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData);
HANDLE WIN_FUNC FindFirstFileExA(LPCSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags);
BOOL WIN_FUNC FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData);
BOOL WIN_FUNC FindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData);
BOOL WIN_FUNC FindClose(HANDLE hFindFile);
} // namespace kernel32

181
dll/kernel32/handleapi.cpp Normal file
View File

@ -0,0 +1,181 @@
#include "handleapi.h"
#include "errors.h"
#include "files.h"
#include "handles.h"
#include "internal.h"
#include "processes.h"
#include <pthread.h>
#include <unistd.h>
namespace advapi32 {
void releaseToken(void *tokenPtr);
}
namespace kernel32 {
BOOL WIN_FUNC DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle,
LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions) {
DEBUG_LOG("DuplicateHandle(%p, %p, %p, %p, %x, %d, %x)\n", hSourceProcessHandle, hSourceHandle,
hTargetProcessHandle, lpTargetHandle, dwDesiredAccess, bInheritHandle, dwOptions);
(void)dwDesiredAccess;
(void)bInheritHandle;
(void)dwOptions;
if (!lpTargetHandle) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
auto validateProcessHandle = [&](HANDLE handle) -> bool {
uintptr_t raw = reinterpret_cast<uintptr_t>(handle);
if (raw == static_cast<uintptr_t>(-1)) {
return true;
}
auto data = handles::dataFromHandle(handle, false);
if (data.type != handles::TYPE_PROCESS || data.ptr == nullptr) {
return false;
}
auto *proc = reinterpret_cast<processes::Process *>(data.ptr);
return proc && proc->pid == getpid();
};
if (!validateProcessHandle(hSourceProcessHandle) || !validateProcessHandle(hTargetProcessHandle)) {
DEBUG_LOG("DuplicateHandle: unsupported process handle combination (source=%p target=%p)\n",
hSourceProcessHandle, hTargetProcessHandle);
wibo::lastError = ERROR_INVALID_HANDLE;
return FALSE;
}
auto file = files::fileHandleFromHandle(hSourceHandle);
if (file && (file->fp == stdin || file->fp == stdout || file->fp == stderr)) {
HANDLE handle = files::duplicateFileHandle(file, false);
DEBUG_LOG("DuplicateHandle: duplicated std handle -> %p\n", handle);
*lpTargetHandle = handle;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
uintptr_t sourceHandleRaw = reinterpret_cast<uintptr_t>(hSourceHandle);
if (sourceHandleRaw == static_cast<uintptr_t>(-1)) {
HANDLE handle = processes::allocProcessHandle(getpid());
processes::Process *proc = processes::processFromHandle(handle, false);
if (proc) {
proc->exitCode = STILL_ACTIVE;
proc->forcedExitCode = STILL_ACTIVE;
proc->terminationRequested = false;
}
DEBUG_LOG("DuplicateHandle: created process handle for current process -> %p\n", handle);
*lpTargetHandle = handle;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
if (sourceHandleRaw == kPseudoCurrentThreadHandleValue) {
ThreadObject *obj = ensureCurrentThreadObject();
if (obj) {
retainThreadObject(obj);
HANDLE handle = handles::allocDataHandle({handles::TYPE_THREAD, obj, 0});
DEBUG_LOG("DuplicateHandle: duplicated pseudo current thread -> %p\n", handle);
*lpTargetHandle = handle;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
ThreadObject *syntheticObj = new ThreadObject();
syntheticObj->thread = pthread_self();
syntheticObj->finished = false;
syntheticObj->joined = false;
syntheticObj->detached = true;
syntheticObj->synthetic = true;
syntheticObj->exitCode = 0;
syntheticObj->refCount = 1;
syntheticObj->suspendCount = 0;
pthread_mutex_init(&syntheticObj->mutex, nullptr);
pthread_cond_init(&syntheticObj->cond, nullptr);
HANDLE handle = handles::allocDataHandle({handles::TYPE_THREAD, syntheticObj, 0});
DEBUG_LOG("DuplicateHandle: created synthetic thread handle -> %p\n", handle);
*lpTargetHandle = handle;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
handles::Data data = handles::dataFromHandle(hSourceHandle, false);
if (data.type == handles::TYPE_PROCESS && data.ptr) {
auto *original = reinterpret_cast<processes::Process *>(data.ptr);
HANDLE handle = processes::allocProcessHandle(original->pid);
auto *copy = processes::processFromHandle(handle, false);
if (copy) {
*copy = *original;
}
DEBUG_LOG("DuplicateHandle: duplicated process handle -> %p\n", handle);
*lpTargetHandle = handle;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
if (data.type == handles::TYPE_THREAD && data.ptr) {
auto *threadObj = reinterpret_cast<ThreadObject *>(data.ptr);
if (!retainThreadObject(threadObj)) {
wibo::lastError = ERROR_INVALID_HANDLE;
return FALSE;
}
HANDLE handle = handles::allocDataHandle({handles::TYPE_THREAD, threadObj, 0});
DEBUG_LOG("DuplicateHandle: duplicated thread handle -> %p\n", handle);
*lpTargetHandle = handle;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
DEBUG_LOG("DuplicateHandle: unsupported handle type for %p\n", hSourceHandle);
wibo::lastError = ERROR_INVALID_HANDLE;
return FALSE;
}
BOOL WIN_FUNC CloseHandle(HANDLE hObject) {
DEBUG_LOG("CloseHandle(%p)\n", hObject);
auto data = handles::dataFromHandle(hObject, true);
if (data.type == handles::TYPE_UNUSED || data.ptr == nullptr) {
wibo::lastError = ERROR_INVALID_HANDLE;
return FALSE;
}
bool success = true;
if (data.type == handles::TYPE_FILE) {
auto file = reinterpret_cast<files::FileHandle *>(data.ptr);
if (file) {
if (file->closeOnDestroy && file->fp && !(file->fp == stdin || file->fp == stdout || file->fp == stderr)) {
fclose(file->fp);
}
delete file;
} else {
success = false;
}
} else if (data.type == handles::TYPE_MAPPED) {
if (!closeFileMappingHandle(data.ptr)) {
success = false;
}
} else if (data.type == handles::TYPE_PROCESS) {
auto *proc = reinterpret_cast<processes::Process *>(data.ptr);
if (proc) {
delete proc;
} else {
success = false;
}
} else if (data.type == handles::TYPE_TOKEN) {
advapi32::releaseToken(data.ptr);
} else if (data.type == handles::TYPE_MUTEX) {
releaseMutexObject(reinterpret_cast<MutexObject *>(data.ptr));
} else if (data.type == handles::TYPE_EVENT) {
releaseEventObject(reinterpret_cast<EventObject *>(data.ptr));
} else if (data.type == handles::TYPE_THREAD) {
releaseThreadObject(reinterpret_cast<ThreadObject *>(data.ptr));
} else if (data.type == handles::TYPE_SEMAPHORE) {
releaseSemaphoreObject(reinterpret_cast<SemaphoreObject *>(data.ptr));
} else {
success = false;
}
if (!success) {
wibo::lastError = ERROR_INVALID_HANDLE;
return FALSE;
}
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
} // namespace kernel32

11
dll/kernel32/handleapi.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include "common.h"
namespace kernel32 {
BOOL WIN_FUNC CloseHandle(HANDLE hObject);
BOOL WIN_FUNC DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle,
LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions);
} // namespace kernel32

310
dll/kernel32/heapapi.cpp Normal file
View File

@ -0,0 +1,310 @@
#include "heapapi.h"
#include "common.h"
#include "errors.h"
#include "handles.h"
#include "internal.h"
#include <algorithm>
#include <mimalloc.h>
#include <mutex>
#include <new>
#include <sys/mman.h>
namespace {
struct HeapRecord {
mi_heap_t *heap = nullptr;
DWORD createFlags = 0;
SIZE_T initialSize = 0;
SIZE_T maximumSize = 0;
DWORD compatibility = 0;
bool isProcessHeap = false;
};
std::once_flag g_processHeapInitFlag;
HANDLE g_processHeapHandle = nullptr;
HeapRecord *g_processHeapRecord = nullptr;
void ensureProcessHeapInitialized() {
std::call_once(g_processHeapInitFlag, []() {
mi_heap_t *heap = mi_heap_get_default();
auto *record = new (std::nothrow) HeapRecord{};
if (!record) {
return;
}
record->heap = heap;
record->isProcessHeap = true;
g_processHeapRecord = record;
g_processHeapHandle = handles::allocDataHandle({handles::TYPE_HEAP, record, 0});
});
}
HeapRecord *activeHeapRecord(HANDLE hHeap) {
if (!hHeap) {
wibo::lastError = ERROR_INVALID_HANDLE;
return nullptr;
}
ensureProcessHeapInitialized();
auto data = handles::dataFromHandle(hHeap, false);
if (data.type != handles::TYPE_HEAP || data.ptr == nullptr) {
wibo::lastError = ERROR_INVALID_HANDLE;
return nullptr;
}
wibo::lastError = ERROR_SUCCESS;
return static_cast<HeapRecord *>(data.ptr);
}
HeapRecord *popHeapRecord(HANDLE hHeap) {
ensureProcessHeapInitialized();
auto preview = handles::dataFromHandle(hHeap, false);
if (preview.type != handles::TYPE_HEAP || preview.ptr == nullptr) {
wibo::lastError = ERROR_INVALID_HANDLE;
return nullptr;
}
auto data = handles::dataFromHandle(hHeap, true);
wibo::lastError = ERROR_SUCCESS;
return static_cast<HeapRecord *>(data.ptr);
}
bool isExecutableHeap(const HeapRecord *record) {
return record && ((record->createFlags & HEAP_CREATE_ENABLE_EXECUTE) != 0);
}
LPVOID heapAllocFromRecord(HeapRecord *record, DWORD dwFlags, SIZE_T dwBytes) {
if (!record) {
return nullptr;
}
if ((record->createFlags | dwFlags) & HEAP_GENERATE_EXCEPTIONS) {
DEBUG_LOG("HeapAlloc: HEAP_GENERATE_EXCEPTIONS not supported\n");
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
const bool zeroMemory = (dwFlags & HEAP_ZERO_MEMORY) != 0;
const SIZE_T requestSize = std::max<SIZE_T>(1, dwBytes);
void *mem = zeroMemory ? mi_heap_zalloc(record->heap, requestSize) : mi_heap_malloc(record->heap, requestSize);
if (!mem) {
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
return nullptr;
}
if (isExecutableHeap(record)) {
kernel32::tryMarkExecutable(mem);
}
wibo::lastError = ERROR_SUCCESS;
return mem;
}
} // namespace
namespace kernel32 {
HANDLE WIN_FUNC HeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize) {
DEBUG_LOG("HeapCreate(%u, %zu, %zu)\n", flOptions, dwInitialSize, dwMaximumSize);
if (dwMaximumSize != 0 && dwInitialSize > dwMaximumSize) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
mi_heap_t *heap = mi_heap_new();
if (!heap) {
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
return nullptr;
}
auto *record = new (std::nothrow) HeapRecord{};
if (!record) {
mi_heap_delete(heap);
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
return nullptr;
}
record->heap = heap;
record->createFlags = flOptions;
record->initialSize = dwInitialSize;
record->maximumSize = dwMaximumSize;
record->isProcessHeap = false;
HANDLE handle = handles::allocDataHandle({handles::TYPE_HEAP, record, 0});
wibo::lastError = ERROR_SUCCESS;
return handle;
}
BOOL WIN_FUNC HeapDestroy(HANDLE hHeap) {
DEBUG_LOG("HeapDestroy(%p)\n", hHeap);
HeapRecord *record = activeHeapRecord(hHeap);
if (!record) {
return FALSE;
}
if (record->isProcessHeap) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
record = popHeapRecord(hHeap);
if (!record) {
return FALSE;
}
mi_heap_destroy(record->heap);
delete record;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
HANDLE WIN_FUNC GetProcessHeap() {
ensureProcessHeapInitialized();
wibo::lastError = ERROR_SUCCESS;
DEBUG_LOG("GetProcessHeap() -> %p\n", g_processHeapHandle);
return g_processHeapHandle;
}
BOOL WIN_FUNC HeapSetInformation(HANDLE HeapHandle, HEAP_INFORMATION_CLASS HeapInformationClass, PVOID HeapInformation,
SIZE_T HeapInformationLength) {
DEBUG_LOG("HeapSetInformation(%p, %d, %p, %zu)\n", HeapHandle, static_cast<int>(HeapInformationClass),
HeapInformation, HeapInformationLength);
ensureProcessHeapInitialized();
switch (HeapInformationClass) {
case HeapCompatibilityInformation: {
if (!HeapInformation || HeapInformationLength < sizeof(ULONG)) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
HeapRecord *target = HeapHandle ? activeHeapRecord(HeapHandle) : g_processHeapRecord;
if (!target) {
return FALSE;
}
target->compatibility = *static_cast<ULONG *>(HeapInformation);
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
case HeapEnableTerminationOnCorruption:
wibo::lastError = ERROR_SUCCESS;
return TRUE;
case HeapOptimizeResources:
wibo::lastError = ERROR_CALL_NOT_IMPLEMENTED;
return FALSE;
default:
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
}
LPVOID WIN_FUNC HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes) {
DEBUG_LOG("HeapAlloc(%p, 0x%x, %zu) ", hHeap, dwFlags, dwBytes);
HeapRecord *record = activeHeapRecord(hHeap);
if (!record) {
DEBUG_LOG("-> NULL\n");
return nullptr;
}
void *mem = heapAllocFromRecord(record, dwFlags, dwBytes);
DEBUG_LOG("-> %p\n", mem);
return mem;
}
LPVOID WIN_FUNC HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, SIZE_T dwBytes) {
DEBUG_LOG("HeapReAlloc(%p, 0x%x, %p, %zu) ", hHeap, dwFlags, lpMem, dwBytes);
HeapRecord *record = activeHeapRecord(hHeap);
if (!record) {
DEBUG_LOG("-> NULL\n");
return nullptr;
}
if (lpMem == nullptr) {
void *alloc = heapAllocFromRecord(record, dwFlags, dwBytes);
DEBUG_LOG("-> %p (alloc)\n", alloc);
return alloc;
}
if (!mi_heap_check_owned(record->heap, lpMem)) {
wibo::lastError = ERROR_INVALID_PARAMETER;
DEBUG_LOG("-> NULL (not owned)\n");
return nullptr;
}
if ((record->createFlags | dwFlags) & HEAP_GENERATE_EXCEPTIONS) {
DEBUG_LOG("-> NULL (exceptions unsupported)\n");
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
const bool inplaceOnly = (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY) != 0;
const bool zeroMemory = (dwFlags & HEAP_ZERO_MEMORY) != 0;
if (dwBytes == 0) {
if (!inplaceOnly) {
mi_free(lpMem);
wibo::lastError = ERROR_SUCCESS;
DEBUG_LOG("-> NULL (freed)\n");
return nullptr;
}
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
DEBUG_LOG("-> NULL (zero size with in-place flag)\n");
return nullptr;
}
const SIZE_T requestSize = std::max<SIZE_T>(1, dwBytes);
const SIZE_T oldSize = mi_usable_size(lpMem);
if (inplaceOnly || requestSize <= oldSize) {
if (requestSize > oldSize) {
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
DEBUG_LOG("-> NULL (cannot grow in place)\n");
return nullptr;
}
wibo::lastError = ERROR_SUCCESS;
DEBUG_LOG("-> %p (in-place)\n", lpMem);
return lpMem;
}
void *ret = mi_heap_realloc(record->heap, lpMem, requestSize);
if (!ret) {
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
return nullptr;
}
if (zeroMemory && requestSize > oldSize) {
size_t newUsable = mi_usable_size(ret);
if (newUsable > oldSize) {
size_t zeroLen = std::min<SIZE_T>(newUsable, requestSize) - oldSize;
memset(static_cast<char *>(ret) + oldSize, 0, zeroLen);
}
}
if (isExecutableHeap(record)) {
tryMarkExecutable(ret);
}
wibo::lastError = ERROR_SUCCESS;
DEBUG_LOG("-> %p\n", ret);
return ret;
}
SIZE_T WIN_FUNC HeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem) {
DEBUG_LOG("HeapSize(%p, 0x%x, %p)\n", hHeap, dwFlags, lpMem);
(void)dwFlags;
HeapRecord *record = activeHeapRecord(hHeap);
if (!record) {
return static_cast<SIZE_T>(-1);
}
if (!lpMem) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return static_cast<SIZE_T>(-1);
}
if (!mi_heap_check_owned(record->heap, const_cast<LPVOID>(lpMem))) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return static_cast<SIZE_T>(-1);
}
size_t size = mi_usable_size(lpMem);
wibo::lastError = ERROR_SUCCESS;
return static_cast<SIZE_T>(size);
}
BOOL WIN_FUNC HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) {
DEBUG_LOG("HeapFree(%p, 0x%x, %p)\n", hHeap, dwFlags, lpMem);
(void)dwFlags;
if (lpMem == nullptr) {
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
HeapRecord *record = activeHeapRecord(hHeap);
if (!record) {
return FALSE;
}
if (!mi_heap_check_owned(record->heap, lpMem)) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
mi_free(lpMem);
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
} // namespace kernel32

29
dll/kernel32/heapapi.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include "common.h"
constexpr DWORD HEAP_NO_SERIALIZE = 0x00000001;
constexpr DWORD HEAP_GENERATE_EXCEPTIONS = 0x00000004;
constexpr DWORD HEAP_ZERO_MEMORY = 0x00000008;
constexpr DWORD HEAP_REALLOC_IN_PLACE_ONLY = 0x00000010;
constexpr DWORD HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;
enum HEAP_INFORMATION_CLASS {
HeapCompatibilityInformation = 0,
HeapEnableTerminationOnCorruption = 1,
HeapOptimizeResources = 3,
};
namespace kernel32 {
HANDLE WIN_FUNC HeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize);
BOOL WIN_FUNC HeapDestroy(HANDLE hHeap);
HANDLE WIN_FUNC GetProcessHeap();
BOOL WIN_FUNC HeapSetInformation(HANDLE HeapHandle, HEAP_INFORMATION_CLASS HeapInformationClass, PVOID HeapInformation,
SIZE_T HeapInformationLength);
LPVOID WIN_FUNC HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);
LPVOID WIN_FUNC HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, SIZE_T dwBytes);
SIZE_T WIN_FUNC HeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem);
BOOL WIN_FUNC HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);
} // namespace kernel32

View File

@ -0,0 +1,43 @@
#include "interlockedapi.h"
#include "common.h"
#include <cstring>
namespace kernel32 {
LONG WIN_FUNC InterlockedIncrement(LONG volatile *Addend) {
VERBOSE_LOG("InterlockedIncrement(%p)\n", Addend);
return ++(*Addend);
}
LONG WIN_FUNC InterlockedDecrement(LONG volatile *Addend) {
VERBOSE_LOG("InterlockedDecrement(%p)\n", Addend);
return --(*Addend);
}
LONG WIN_FUNC InterlockedExchange(LONG volatile *Target, LONG Value) {
VERBOSE_LOG("InterlockedExchange(%p, %ld)\n", Target, static_cast<long>(Value));
LONG initial = *Target;
*Target = Value;
return initial;
}
LONG WIN_FUNC InterlockedCompareExchange(LONG volatile *Destination, LONG Exchange, LONG Comperand) {
VERBOSE_LOG("InterlockedCompareExchange(%p, %ld, %ld)\n", Destination, static_cast<long>(Exchange),
static_cast<long>(Comperand));
LONG original = *Destination;
if (original == Comperand) {
*Destination = Exchange;
}
return original;
}
void WIN_FUNC InitializeSListHead(PSLIST_HEADER ListHead) {
DEBUG_LOG("InitializeSListHead(%p)\n", ListHead);
if (!ListHead) {
return;
}
std::memset(ListHead, 0, sizeof(*ListHead));
}
} // namespace kernel32

View File

@ -0,0 +1,27 @@
#pragma once
#include "common.h"
namespace kernel32 {
struct SLIST_ENTRY {
SLIST_ENTRY *Next;
};
using PSLIST_ENTRY = SLIST_ENTRY *;
struct SLIST_HEADER {
SLIST_ENTRY *Head;
unsigned short Depth;
unsigned short Sequence;
};
using PSLIST_HEADER = SLIST_HEADER *;
LONG WIN_FUNC InterlockedIncrement(LONG volatile *Addend);
LONG WIN_FUNC InterlockedDecrement(LONG volatile *Addend);
LONG WIN_FUNC InterlockedExchange(LONG volatile *Target, LONG Value);
LONG WIN_FUNC InterlockedCompareExchange(LONG volatile *Destination, LONG Exchange, LONG Comperand);
void WIN_FUNC InitializeSListHead(PSLIST_HEADER ListHead);
} // namespace kernel32

View File

@ -1,7 +1,8 @@
#pragma once
#include "kernel32.h"
#include "common.h"
#include <pthread.h>
#include <string>
namespace kernel32 {
@ -45,10 +46,19 @@ struct SemaphoreObject {
int refCount = 1;
};
inline constexpr uintptr_t kPseudoCurrentThreadHandleValue = static_cast<uintptr_t>(-2);
void releaseMutexObject(MutexObject *obj);
void releaseEventObject(EventObject *obj);
void releaseSemaphoreObject(SemaphoreObject *obj);
void resetOverlappedEvent(OVERLAPPED *ov);
void signalOverlappedEvent(OVERLAPPED *ov);
void tryMarkExecutable(void *mem);
void setLastErrorFromErrno();
bool closeFileMappingHandle(void *mappingPtr);
ThreadObject *ensureCurrentThreadObject();
ThreadObject *threadObjectFromHandle(HANDLE hThread);
ThreadObject *retainThreadObject(ThreadObject *obj);
void releaseThreadObject(ThreadObject *obj);
} // namespace kernel32

View File

@ -1,193 +0,0 @@
#pragma once
#include "common.h"
// errhandlingapi.h
constexpr DWORD EXCEPTION_MAXIMUM_PARAMETERS = 15;
struct EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
};
using PEXCEPTION_RECORD = EXCEPTION_RECORD *;
using PCONTEXT = void *;
struct EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ContextRecord;
};
using PEXCEPTION_POINTERS = EXCEPTION_POINTERS *;
using PVECTORED_EXCEPTION_HANDLER = LONG(WIN_FUNC *)(PEXCEPTION_POINTERS ExceptionInfo);
using LPTOP_LEVEL_EXCEPTION_FILTER = LONG(WIN_FUNC *)(PEXCEPTION_POINTERS ExceptionInfo);
constexpr LONG EXCEPTION_CONTINUE_EXECUTION = static_cast<LONG>(-1);
constexpr LONG EXCEPTION_CONTINUE_SEARCH = 0;
constexpr LONG EXCEPTION_EXECUTE_HANDLER = 1;
// synchapi.h constants
constexpr DWORD WAIT_OBJECT_0 = 0x00000000;
constexpr DWORD WAIT_ABANDONED = 0x00000080;
constexpr DWORD WAIT_TIMEOUT = 0x00000102;
constexpr DWORD WAIT_FAILED = 0xFFFFFFFF;
constexpr DWORD INFINITE = 0xFFFFFFFF;
// minwinbase.h
struct SECURITY_ATTRIBUTES {
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
};
using PSECURITY_ATTRIBUTES = SECURITY_ATTRIBUTES *;
using LPSECURITY_ATTRIBUTES = SECURITY_ATTRIBUTES *;
// sysinfoapi.h
struct SYSTEM_INFO {
union {
DWORD dwOemId;
struct {
WORD wProcessorArchitecture;
WORD wReserved;
};
};
DWORD dwPageSize;
LPVOID lpMinimumApplicationAddress;
LPVOID lpMaximumApplicationAddress;
DWORD_PTR dwActiveProcessorMask;
DWORD dwNumberOfProcessors;
DWORD dwProcessorType;
DWORD dwAllocationGranularity;
WORD wProcessorLevel;
WORD wProcessorRevision;
};
using LPSYSTEM_INFO = SYSTEM_INFO *;
// processthreadsapi.h
struct PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
};
using PPROCESS_INFORMATION = PROCESS_INFORMATION *;
using LPPROCESS_INFORMATION = PROCESS_INFORMATION *;
struct STARTUPINFOA {
DWORD cb;
LPSTR lpReserved;
LPSTR lpDesktop;
LPSTR lpTitle;
DWORD dwX;
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars;
DWORD dwYCountChars;
DWORD dwFillAttribute;
DWORD dwFlags;
WORD wShowWindow;
WORD cbReserved2;
unsigned char *lpReserved2;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
};
using LPSTARTUPINFOA = STARTUPINFOA *;
struct STARTUPINFOW {
DWORD cb;
LPWSTR lpReserved;
LPWSTR lpDesktop;
LPWSTR lpTitle;
DWORD dwX;
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars;
DWORD dwYCountChars;
DWORD dwFillAttribute;
DWORD dwFlags;
WORD wShowWindow;
WORD cbReserved2;
unsigned char *lpReserved2;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
};
using LPSTARTUPINFOW = STARTUPINFOW *;
// fibersapi.h
using PFLS_CALLBACK_FUNCTION = void (*)(void *);
constexpr DWORD FLS_OUT_OF_INDEXES = 0xFFFFFFFF;
namespace kernel32 {
// fibersapi.h
DWORD WIN_FUNC FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback);
BOOL WIN_FUNC FlsFree(DWORD dwFlsIndex);
PVOID WIN_FUNC FlsGetValue(DWORD dwFlsIndex);
BOOL WIN_FUNC FlsSetValue(DWORD dwFlsIndex, PVOID lpFlsData);
// errhandlingapi.h
DWORD WIN_FUNC GetLastError();
void WIN_FUNC SetLastError(DWORD dwErrCode);
void WIN_FUNC RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, DWORD nNumberOfArguments,
const ULONG_PTR *lpArguments);
PVOID WIN_FUNC AddVectoredExceptionHandler(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler);
LPTOP_LEVEL_EXCEPTION_FILTER WIN_FUNC
SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
LONG WIN_FUNC UnhandledExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo);
UINT WIN_FUNC SetErrorMode(UINT uMode);
// wow64apiset.h
BOOL WIN_FUNC Wow64DisableWow64FsRedirection(PVOID *OldValue);
BOOL WIN_FUNC Wow64RevertWow64FsRedirection(PVOID OldValue);
BOOL WIN_FUNC IsWow64Process(HANDLE hProcess, PBOOL Wow64Process);
// sysinfoapi.h
void WIN_FUNC GetSystemInfo(LPSYSTEM_INFO lpSystemInfo);
// processthreadsapi.h
HANDLE WIN_FUNC GetCurrentProcess();
DWORD WIN_FUNC GetCurrentProcessId();
DWORD WIN_FUNC GetCurrentThreadId();
BOOL WIN_FUNC GetProcessAffinityMask(HANDLE hProcess, PDWORD_PTR lpProcessAffinityMask, PDWORD_PTR lpSystemAffinityMask);
BOOL WIN_FUNC SetProcessAffinityMask(HANDLE hProcess, DWORD_PTR dwProcessAffinityMask);
void WIN_FUNC ExitProcess(UINT uExitCode);
BOOL WIN_FUNC TerminateProcess(HANDLE hProcess, UINT uExitCode);
BOOL WIN_FUNC GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode);
BOOL WIN_FUNC CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags,
LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation);
BOOL WIN_FUNC CreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags,
LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation);
// synchapi.h
HANDLE WIN_FUNC CreateMutexA(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCSTR lpName);
HANDLE WIN_FUNC CreateMutexW(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCWSTR lpName);
BOOL WIN_FUNC ReleaseMutex(HANDLE hMutex);
HANDLE WIN_FUNC CreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCSTR lpName);
HANDLE WIN_FUNC CreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCWSTR lpName);
BOOL WIN_FUNC SetEvent(HANDLE hEvent);
BOOL WIN_FUNC ResetEvent(HANDLE hEvent);
HANDLE WIN_FUNC CreateSemaphoreA(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount,
LPCSTR lpName);
HANDLE WIN_FUNC CreateSemaphoreW(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount,
LPCWSTR lpName);
BOOL WIN_FUNC ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, PLONG lpPreviousCount);
DWORD WIN_FUNC WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
} // namespace kernel32

View File

@ -0,0 +1,269 @@
#include "libloaderapi.h"
#include "errors.h"
#include "files.h"
#include "resources.h"
#include "strutil.h"
#include <algorithm>
#include <cassert>
#include <optional>
#include <string>
namespace {
HRSRC findResourceInternal(HMODULE hModule, const wibo::ResourceIdentifier &type, const wibo::ResourceIdentifier &name,
std::optional<uint16_t> language) {
auto *exe = wibo::executableFromModule(hModule);
if (!exe) {
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
return nullptr;
}
wibo::ResourceLocation loc;
if (!exe->findResource(type, name, language, loc)) {
return nullptr;
}
return reinterpret_cast<HRSRC>(const_cast<void *>(loc.dataEntry));
}
} // namespace
namespace kernel32 {
BOOL WIN_FUNC DisableThreadLibraryCalls(HMODULE hLibModule) {
DEBUG_LOG("DisableThreadLibraryCalls(%p)\n", hLibModule);
(void)hLibModule;
return TRUE;
}
HMODULE WIN_FUNC GetModuleHandleA(LPCSTR lpModuleName) {
DEBUG_LOG("GetModuleHandleA(%s)\n", lpModuleName);
const auto *module = wibo::findLoadedModule(lpModuleName);
if (!module) {
wibo::lastError = ERROR_MOD_NOT_FOUND;
return nullptr;
}
wibo::lastError = ERROR_SUCCESS;
return module->handle;
}
HMODULE WIN_FUNC GetModuleHandleW(LPCWSTR lpModuleName) {
DEBUG_LOG("GetModuleHandleW -> ");
if (lpModuleName) {
const auto lpModuleNameA = wideStringToString(lpModuleName);
return GetModuleHandleA(lpModuleNameA.c_str());
}
return GetModuleHandleA(nullptr);
}
DWORD WIN_FUNC GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize) {
DEBUG_LOG("GetModuleFileNameA(%p, %p, %u)\n", hModule, lpFilename, nSize);
if (!lpFilename) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
auto *info = wibo::moduleInfoFromHandle(hModule);
if (!info) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
std::string path;
if (!info->resolvedPath.empty()) {
path = files::pathToWindows(info->resolvedPath);
} else {
path = info->originalName;
}
DEBUG_LOG("-> %s\n", path.c_str());
if (nSize == 0) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return 0;
}
const size_t len = path.size();
const size_t copyLen = std::min(len, static_cast<size_t>(nSize - 1));
std::memcpy(lpFilename, path.c_str(), copyLen);
if (copyLen < nSize) {
lpFilename[copyLen] = '\0';
}
if (copyLen < len) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return nSize;
}
wibo::lastError = ERROR_SUCCESS;
return static_cast<DWORD>(copyLen);
}
DWORD WIN_FUNC GetModuleFileNameW(HMODULE hModule, LPWSTR lpFilename, DWORD nSize) {
DEBUG_LOG("GetModuleFileNameW(%p, %s, %u)\n", hModule, wideStringToString(lpFilename).c_str(), nSize);
if (!lpFilename) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
auto *info = wibo::moduleInfoFromHandle(hModule);
if (!info) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
std::string path;
if (!info->resolvedPath.empty()) {
path = files::pathToWindows(info->resolvedPath);
} else {
path = info->originalName;
}
if (nSize == 0) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return 0;
}
auto wide = stringToWideString(path.c_str());
if (wide.empty()) {
wide.push_back(0);
}
const size_t len = wide.size() - 1;
const size_t copyLen = std::min(len, static_cast<size_t>(nSize - 1));
for (size_t i = 0; i < copyLen; ++i) {
lpFilename[i] = wide[i];
}
if (copyLen < static_cast<size_t>(nSize)) {
lpFilename[copyLen] = 0;
}
if (copyLen < len) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return nSize;
}
wibo::lastError = ERROR_SUCCESS;
return static_cast<DWORD>(copyLen);
}
HRSRC WIN_FUNC FindResourceA(HMODULE hModule, LPCSTR lpName, LPCSTR lpType) {
DEBUG_LOG("FindResourceA %p %p %p\n", hModule, lpName, lpType);
auto type = wibo::resourceIdentifierFromAnsi(lpType);
auto name = wibo::resourceIdentifierFromAnsi(lpName);
return findResourceInternal(hModule, type, name, std::nullopt);
}
HRSRC WIN_FUNC FindResourceExA(HMODULE hModule, LPCSTR lpType, LPCSTR lpName, WORD wLanguage) {
DEBUG_LOG("FindResourceExA %p %p %p %u\n", hModule, lpName, lpType, wLanguage);
auto type = wibo::resourceIdentifierFromAnsi(lpType);
auto name = wibo::resourceIdentifierFromAnsi(lpName);
return findResourceInternal(hModule, type, name, wLanguage);
}
HRSRC WIN_FUNC FindResourceW(HMODULE hModule, LPCWSTR lpName, LPCWSTR lpType) {
DEBUG_LOG("FindResourceW %p\n", hModule);
auto type = wibo::resourceIdentifierFromWide(lpType);
auto name = wibo::resourceIdentifierFromWide(lpName);
return findResourceInternal(hModule, type, name, std::nullopt);
}
HRSRC WIN_FUNC FindResourceExW(HMODULE hModule, LPCWSTR lpType, LPCWSTR lpName, WORD wLanguage) {
DEBUG_LOG("FindResourceExW %p %u\n", hModule, wLanguage);
auto type = wibo::resourceIdentifierFromWide(lpType);
auto name = wibo::resourceIdentifierFromWide(lpName);
return findResourceInternal(hModule, type, name, wLanguage);
}
HGLOBAL WIN_FUNC LoadResource(HMODULE hModule, HRSRC hResInfo) {
DEBUG_LOG("LoadResource %p %p\n", hModule, hResInfo);
if (!hResInfo) {
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
return nullptr;
}
auto *exe = wibo::executableFromModule(hModule);
if (!exe || !exe->rsrcBase) {
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
return nullptr;
}
const auto *entry = reinterpret_cast<const wibo::ImageResourceDataEntry *>(hResInfo);
if (!wibo::resourceEntryBelongsToExecutable(*exe, entry)) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
return const_cast<void *>(exe->fromRVA<const void>(entry->offsetToData));
}
LPVOID WIN_FUNC LockResource(HGLOBAL hResData) {
DEBUG_LOG("LockResource(%p)\n", hResData);
return hResData;
}
DWORD WIN_FUNC SizeofResource(HMODULE hModule, HRSRC hResInfo) {
DEBUG_LOG("SizeofResource(%p, %p)\n", hModule, hResInfo);
if (!hResInfo) {
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
return 0;
}
auto *exe = wibo::executableFromModule(hModule);
if (!exe || !exe->rsrcBase) {
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
return 0;
}
const auto *entry = reinterpret_cast<const wibo::ImageResourceDataEntry *>(hResInfo);
if (!wibo::resourceEntryBelongsToExecutable(*exe, entry)) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
return entry->size;
}
HMODULE WIN_FUNC LoadLibraryA(LPCSTR lpLibFileName) {
DEBUG_LOG("LoadLibraryA(%s)\n", lpLibFileName);
const auto *info = wibo::loadModule(lpLibFileName);
if (!info) {
return nullptr;
}
wibo::lastError = ERROR_SUCCESS;
return info->handle;
}
HMODULE WIN_FUNC LoadLibraryW(LPCWSTR lpLibFileName) {
if (!lpLibFileName) {
return nullptr;
}
auto filename = wideStringToString(lpLibFileName);
DEBUG_LOG("LoadLibraryW(%s)\n", filename.c_str());
return LoadLibraryA(filename.c_str());
}
HMODULE WIN_FUNC LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) {
assert(!hFile);
DEBUG_LOG("LoadLibraryExW(%x) -> ", dwFlags);
auto filename = wideStringToString(lpLibFileName);
return LoadLibraryA(filename.c_str());
}
BOOL WIN_FUNC FreeLibrary(HMODULE hLibModule) {
DEBUG_LOG("FreeLibrary(%p)\n", hLibModule);
auto *info = wibo::moduleInfoFromHandle(hLibModule);
if (!info) {
wibo::lastError = ERROR_INVALID_HANDLE;
return FALSE;
}
wibo::freeModule(info);
return TRUE;
}
FARPROC WIN_FUNC GetProcAddress(HMODULE hModule, LPCSTR lpProcName) {
FARPROC result;
const auto info = wibo::moduleInfoFromHandle(hModule);
if (!info) {
DEBUG_LOG("GetProcAddress(%p) -> ERROR_INVALID_HANDLE\n", hModule);
wibo::lastError = ERROR_INVALID_HANDLE;
return nullptr;
}
const auto proc = reinterpret_cast<uintptr_t>(lpProcName);
if (proc & ~0xFFFFu) {
DEBUG_LOG("GetProcAddress(%s, %s) ", info->normalizedName.c_str(), lpProcName);
result = wibo::resolveFuncByName(info, lpProcName);
} else {
DEBUG_LOG("GetProcAddress(%s, %u) ", info->normalizedName.c_str(), proc);
result = wibo::resolveFuncByOrdinal(info, static_cast<uint16_t>(proc));
}
DEBUG_LOG("-> %p\n", result);
if (!result) {
wibo::lastError = ERROR_PROC_NOT_FOUND;
} else {
wibo::lastError = ERROR_SUCCESS;
}
return result;
}
} // namespace kernel32

View File

@ -0,0 +1,25 @@
#pragma once
#include "common.h"
namespace kernel32 {
BOOL WIN_FUNC DisableThreadLibraryCalls(HMODULE hLibModule);
HMODULE WIN_FUNC GetModuleHandleA(LPCSTR lpModuleName);
HMODULE WIN_FUNC GetModuleHandleW(LPCWSTR lpModuleName);
DWORD WIN_FUNC GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize);
DWORD WIN_FUNC GetModuleFileNameW(HMODULE hModule, LPWSTR lpFilename, DWORD nSize);
HRSRC WIN_FUNC FindResourceA(HMODULE hModule, LPCSTR lpName, LPCSTR lpType);
HRSRC WIN_FUNC FindResourceExA(HMODULE hModule, LPCSTR lpType, LPCSTR lpName, WORD wLanguage);
HRSRC WIN_FUNC FindResourceW(HMODULE hModule, LPCWSTR lpName, LPCWSTR lpType);
HRSRC WIN_FUNC FindResourceExW(HMODULE hModule, LPCWSTR lpType, LPCWSTR lpName, WORD wLanguage);
HGLOBAL WIN_FUNC LoadResource(HMODULE hModule, HRSRC hResInfo);
LPVOID WIN_FUNC LockResource(HGLOBAL hResData);
DWORD WIN_FUNC SizeofResource(HMODULE hModule, HRSRC hResInfo);
HMODULE WIN_FUNC LoadLibraryA(LPCSTR lpLibFileName);
HMODULE WIN_FUNC LoadLibraryW(LPCWSTR lpLibFileName);
HMODULE WIN_FUNC LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags);
BOOL WIN_FUNC FreeLibrary(HMODULE hLibModule);
FARPROC WIN_FUNC GetProcAddress(HMODULE hModule, LPCSTR lpProcName);
} // namespace kernel32

858
dll/kernel32/memoryapi.cpp Normal file
View File

@ -0,0 +1,858 @@
#include "memoryapi.h"
#include "common.h"
#include "errors.h"
#include "files.h"
#include "handles.h"
#include "internal.h"
#include "strutil.h"
#include <algorithm>
#include <cerrno>
#include <fcntl.h>
#include <iterator>
#include <limits>
#include <map>
#include <mutex>
#include <sys/mman.h>
#include <unistd.h>
#include <unordered_map>
#include <utility>
#include <vector>
namespace kernel32 {
int64_t getFileSize(HANDLE hFile);
}
namespace {
constexpr size_t kVirtualAllocationGranularity = 64 * 1024;
struct MappingObject {
int fd = -1;
size_t maxSize = 0;
DWORD protect = 0;
bool anonymous = false;
bool closed = false;
size_t refCount = 0;
};
struct ViewInfo {
void *mapBase = nullptr;
size_t mapLength = 0;
MappingObject *owner = nullptr;
};
std::unordered_map<void *, ViewInfo> g_viewInfo;
void closeMappingIfPossible(MappingObject *mapping) {
if (!mapping) {
return;
}
if (mapping->fd != -1) {
close(mapping->fd);
mapping->fd = -1;
}
delete mapping;
}
void tryReleaseMapping(MappingObject *mapping) {
if (!mapping) {
return;
}
if (mapping->closed && mapping->refCount == 0) {
closeMappingIfPossible(mapping);
}
}
struct VirtualAllocation {
uintptr_t base = 0;
size_t size = 0;
DWORD allocationProtect = 0;
std::vector<DWORD> pageProtect;
};
std::map<uintptr_t, VirtualAllocation> g_virtualAllocations;
std::mutex g_virtualAllocMutex;
size_t systemPageSize() {
static size_t cached = []() {
long detected = sysconf(_SC_PAGESIZE);
if (detected <= 0) {
return static_cast<size_t>(4096);
}
return static_cast<size_t>(detected);
}();
return cached;
}
uintptr_t alignDown(uintptr_t value, size_t alignment) {
const uintptr_t mask = static_cast<uintptr_t>(alignment) - 1;
return value & ~mask;
}
uintptr_t alignUp(uintptr_t value, size_t alignment) {
const uintptr_t mask = static_cast<uintptr_t>(alignment) - 1;
if (mask == std::numeric_limits<uintptr_t>::max()) {
return value;
}
if (value > std::numeric_limits<uintptr_t>::max() - mask) {
return std::numeric_limits<uintptr_t>::max();
}
return (value + mask) & ~mask;
}
bool addOverflows(uintptr_t base, size_t amount) {
return base > std::numeric_limits<uintptr_t>::max() - static_cast<uintptr_t>(amount);
}
uintptr_t regionEnd(const VirtualAllocation &region) { return region.base + region.size; }
bool rangeOverlapsLocked(uintptr_t base, size_t length) {
if (length == 0) {
return false;
}
if (addOverflows(base, length - 1)) {
return true;
}
uintptr_t end = base + length;
auto next = g_virtualAllocations.lower_bound(base);
if (next != g_virtualAllocations.begin()) {
auto prev = std::prev(next);
if (regionEnd(prev->second) > base) {
return true;
}
}
if (next != g_virtualAllocations.end() && next->second.base < end) {
return true;
}
return false;
}
std::map<uintptr_t, VirtualAllocation>::iterator findRegionIterator(uintptr_t address) {
auto it = g_virtualAllocations.upper_bound(address);
if (it == g_virtualAllocations.begin()) {
return g_virtualAllocations.end();
}
--it;
if (address >= regionEnd(it->second)) {
return g_virtualAllocations.end();
}
return it;
}
VirtualAllocation *lookupRegion(uintptr_t address) {
auto it = findRegionIterator(address);
if (it == g_virtualAllocations.end()) {
return nullptr;
}
return &it->second;
}
bool rangeWithinRegion(const VirtualAllocation &region, uintptr_t start, size_t length) {
if (length == 0) {
return start >= region.base && start <= regionEnd(region);
}
if (start < region.base) {
return false;
}
if (addOverflows(start, length)) {
return false;
}
return (start + length) <= regionEnd(region);
}
void markCommitted(VirtualAllocation &region, uintptr_t start, size_t length, DWORD protect) {
if (length == 0) {
return;
}
const size_t pageSize = systemPageSize();
const size_t firstPage = (start - region.base) / pageSize;
const size_t pageCount = length / pageSize;
for (size_t i = 0; i < pageCount; ++i) {
region.pageProtect[firstPage + i] = protect;
}
}
void markDecommitted(VirtualAllocation &region, uintptr_t start, size_t length) {
if (length == 0) {
return;
}
const size_t pageSize = systemPageSize();
const size_t firstPage = (start - region.base) / pageSize;
const size_t pageCount = length / pageSize;
for (size_t i = 0; i < pageCount; ++i) {
region.pageProtect[firstPage + i] = 0;
}
}
void *alignedReserve(size_t length, int prot, int flags) {
const size_t granularity = kVirtualAllocationGranularity;
const size_t request = length + granularity;
void *raw = mmap(nullptr, request, prot, flags, -1, 0);
if (raw == MAP_FAILED) {
return MAP_FAILED;
}
uintptr_t rawAddr = reinterpret_cast<uintptr_t>(raw);
uintptr_t aligned = alignUp(rawAddr, granularity);
size_t front = aligned - rawAddr;
size_t back = (rawAddr + request) - (aligned + length);
if (front != 0) {
if (munmap(raw, front) != 0) {
munmap(raw, request);
return MAP_FAILED;
}
}
if (back != 0) {
if (munmap(reinterpret_cast<void *>(aligned + length), back) != 0) {
munmap(reinterpret_cast<void *>(aligned), length);
return MAP_FAILED;
}
}
return reinterpret_cast<void *>(aligned);
}
int translateProtect(DWORD flProtect) {
switch (flProtect) {
case PAGE_NOACCESS:
return PROT_NONE;
case PAGE_READONLY:
return PROT_READ;
case PAGE_READWRITE:
case PAGE_WRITECOPY:
return PROT_READ | PROT_WRITE;
case PAGE_EXECUTE:
return PROT_EXEC;
case PAGE_EXECUTE_READ:
return PROT_READ | PROT_EXEC;
case PAGE_EXECUTE_READWRITE:
case PAGE_EXECUTE_WRITECOPY:
return PROT_READ | PROT_WRITE | PROT_EXEC;
default:
DEBUG_LOG("Unhandled flProtect: %u, defaulting to RW\n", flProtect);
return PROT_READ | PROT_WRITE;
}
}
} // namespace
namespace kernel32 {
HANDLE WIN_FUNC CreateFileMappingA(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect,
DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCSTR lpName) {
DEBUG_LOG("CreateFileMappingA(%p, %p, %u, %u, %u, %s)\n", hFile, lpFileMappingAttributes, flProtect,
dwMaximumSizeHigh, dwMaximumSizeLow, lpName ? lpName : "(null)");
(void)lpFileMappingAttributes;
(void)lpName;
auto *mapping = new MappingObject();
mapping->protect = flProtect;
uint64_t size = (static_cast<uint64_t>(dwMaximumSizeHigh) << 32) | dwMaximumSizeLow;
if (flProtect != PAGE_READONLY && flProtect != PAGE_READWRITE && flProtect != PAGE_WRITECOPY) {
DEBUG_LOG("CreateFileMappingA: unsupported protection 0x%x\n", flProtect);
wibo::lastError = ERROR_INVALID_PARAMETER;
closeMappingIfPossible(mapping);
return nullptr;
}
if (hFile == INVALID_HANDLE_VALUE) {
mapping->anonymous = true;
mapping->fd = -1;
if (size == 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
closeMappingIfPossible(mapping);
return nullptr;
}
mapping->maxSize = size;
} else {
FILE *fp = files::fpFromHandle(hFile);
if (!fp) {
wibo::lastError = ERROR_INVALID_HANDLE;
closeMappingIfPossible(mapping);
return nullptr;
}
int originalFd = fileno(fp);
if (originalFd == -1) {
setLastErrorFromErrno();
closeMappingIfPossible(mapping);
return nullptr;
}
int dupFd = fcntl(originalFd, F_DUPFD_CLOEXEC, 0);
if (dupFd == -1) {
setLastErrorFromErrno();
closeMappingIfPossible(mapping);
return nullptr;
}
mapping->fd = dupFd;
if (size == 0) {
int64_t fileSize = getFileSize(hFile);
if (fileSize < 0) {
closeMappingIfPossible(mapping);
return nullptr;
}
size = static_cast<uint64_t>(fileSize);
}
mapping->maxSize = size;
}
wibo::lastError = ERROR_SUCCESS;
return handles::allocDataHandle({handles::TYPE_MAPPED, mapping, static_cast<size_t>(mapping->maxSize)});
}
HANDLE WIN_FUNC CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect,
DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCWSTR lpName) {
DEBUG_LOG("CreateFileMappingW -> ");
std::string name = wideStringToString(lpName);
return CreateFileMappingA(hFile, lpFileMappingAttributes, flProtect, dwMaximumSizeHigh, dwMaximumSizeLow,
lpName ? name.c_str() : nullptr);
}
LPVOID WIN_FUNC MapViewOfFile(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap) {
DEBUG_LOG("MapViewOfFile(%p, 0x%x, %u, %u, %zu)\n", hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
dwFileOffsetLow, dwNumberOfBytesToMap);
handles::Data data = handles::dataFromHandle(hFileMappingObject, false);
if (data.type != handles::TYPE_MAPPED) {
wibo::lastError = ERROR_INVALID_HANDLE;
return nullptr;
}
auto *mapping = reinterpret_cast<MappingObject *>(data.ptr);
if (!mapping) {
wibo::lastError = ERROR_INVALID_HANDLE;
return nullptr;
}
if (mapping->closed) {
wibo::lastError = ERROR_INVALID_HANDLE;
return nullptr;
}
uint64_t offset = (static_cast<uint64_t>(dwFileOffsetHigh) << 32) | dwFileOffsetLow;
if (mapping->anonymous && offset != 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
size_t maxSize = mapping->maxSize;
uint64_t length = static_cast<uint64_t>(dwNumberOfBytesToMap);
if (length == 0) {
if (maxSize == 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
if (offset > maxSize) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
length = maxSize - offset;
}
if (length == 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
if (maxSize != 0 && offset + length > maxSize) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
int prot = PROT_READ;
bool wantWrite = (dwDesiredAccess & FILE_MAP_WRITE) != 0;
bool wantExecute = (dwDesiredAccess & FILE_MAP_EXECUTE) != 0;
if (mapping->protect == PAGE_READWRITE) {
if (wantWrite) {
prot |= PROT_WRITE;
}
} else {
if (wantWrite && (dwDesiredAccess & FILE_MAP_COPY) == 0) {
wibo::lastError = ERROR_ACCESS_DENIED;
return nullptr;
}
}
if (wantExecute) {
prot |= PROT_EXEC;
}
int flags = 0;
if (mapping->anonymous) {
flags |= MAP_ANONYMOUS;
}
flags |= (dwDesiredAccess & FILE_MAP_COPY) ? MAP_PRIVATE : MAP_SHARED;
size_t pageSize = static_cast<size_t>(sysconf(_SC_PAGESIZE));
off_t alignedOffset = mapping->anonymous ? 0 : static_cast<off_t>(offset & ~static_cast<uint64_t>(pageSize - 1));
size_t offsetDelta = static_cast<size_t>(offset - static_cast<uint64_t>(alignedOffset));
uint64_t requestedLength = length + offsetDelta;
if (requestedLength < length) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
size_t mapLength = static_cast<size_t>(requestedLength);
if (static_cast<uint64_t>(mapLength) != requestedLength) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
int mmapFd = mapping->anonymous ? -1 : mapping->fd;
void *mapBase = mmap(nullptr, mapLength, prot, flags, mmapFd, alignedOffset);
if (mapBase == MAP_FAILED) {
setLastErrorFromErrno();
return nullptr;
}
void *viewPtr = static_cast<uint8_t *>(mapBase) + offsetDelta;
g_viewInfo[viewPtr] = ViewInfo{mapBase, mapLength, mapping};
mapping->refCount++;
wibo::lastError = ERROR_SUCCESS;
return viewPtr;
}
BOOL WIN_FUNC UnmapViewOfFile(LPCVOID lpBaseAddress) {
DEBUG_LOG("UnmapViewOfFile(%p)\n", lpBaseAddress);
auto it = g_viewInfo.find(const_cast<void *>(lpBaseAddress));
if (it == g_viewInfo.end()) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
ViewInfo info = it->second;
g_viewInfo.erase(it);
if (info.mapBase && info.mapLength) {
munmap(info.mapBase, info.mapLength);
}
if (info.owner && info.owner->refCount > 0) {
info.owner->refCount--;
tryReleaseMapping(info.owner);
}
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
bool closeFileMappingHandle(void *mappingPtr) {
auto *mapping = reinterpret_cast<MappingObject *>(mappingPtr);
if (!mapping) {
return false;
}
mapping->closed = true;
tryReleaseMapping(mapping);
return true;
}
LPVOID WIN_FUNC VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) {
DEBUG_LOG("VirtualAlloc(%p, %zu, %u, %u)\n", lpAddress, dwSize, flAllocationType, flProtect);
if (dwSize == 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
DWORD unsupportedFlags = flAllocationType & (MEM_WRITE_WATCH | MEM_PHYSICAL | MEM_LARGE_PAGES | MEM_RESET_UNDO);
if (unsupportedFlags != 0) {
DEBUG_LOG("VirtualAlloc unsupported flags: 0x%x\n", unsupportedFlags);
wibo::lastError = ERROR_NOT_SUPPORTED;
return nullptr;
}
bool reserve = (flAllocationType & MEM_RESERVE) != 0;
bool commit = (flAllocationType & MEM_COMMIT) != 0;
bool reset = (flAllocationType & MEM_RESET) != 0;
if (!reserve && commit && lpAddress == nullptr) {
reserve = true;
}
if (reset) {
if (reserve || commit) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
if (!lpAddress) {
wibo::lastError = ERROR_INVALID_ADDRESS;
return nullptr;
}
const size_t pageSize = systemPageSize();
uintptr_t request = reinterpret_cast<uintptr_t>(lpAddress);
if (addOverflows(request, static_cast<size_t>(dwSize))) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
uintptr_t start = alignDown(request, pageSize);
uintptr_t end = alignUp(request + static_cast<uintptr_t>(dwSize), pageSize);
size_t length = static_cast<size_t>(end - start);
std::unique_lock<std::mutex> lock(g_virtualAllocMutex);
VirtualAllocation *region = lookupRegion(start);
if (!region || !rangeWithinRegion(*region, start, length)) {
wibo::lastError = ERROR_INVALID_ADDRESS;
return nullptr;
}
#ifdef MADV_FREE
int advice = MADV_FREE;
#else
int advice = MADV_DONTNEED;
#endif
if (madvise(reinterpret_cast<void *>(start), length, advice) != 0) {
wibo::lastError = wibo::winErrorFromErrno(errno);
return nullptr;
}
wibo::lastError = ERROR_SUCCESS;
return reinterpret_cast<LPVOID>(start);
}
if (!reserve && !commit) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
const size_t pageSize = systemPageSize();
std::unique_lock<std::mutex> lock(g_virtualAllocMutex);
if (reserve) {
uintptr_t base = 0;
size_t length = 0;
if (lpAddress) {
uintptr_t request = reinterpret_cast<uintptr_t>(lpAddress);
base = alignDown(request, kVirtualAllocationGranularity);
size_t offset = static_cast<size_t>(request - base);
if (addOverflows(offset, static_cast<size_t>(dwSize))) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
size_t span = static_cast<size_t>(dwSize) + offset;
uintptr_t alignedSpan = alignUp(span, pageSize);
if (alignedSpan == std::numeric_limits<uintptr_t>::max()) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
length = static_cast<size_t>(alignedSpan);
if (length == 0 || rangeOverlapsLocked(base, length)) {
wibo::lastError = ERROR_INVALID_ADDRESS;
return nullptr;
}
} else {
uintptr_t aligned = alignUp(static_cast<uintptr_t>(dwSize), pageSize);
if (aligned == std::numeric_limits<uintptr_t>::max() || aligned == 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
length = static_cast<size_t>(aligned);
}
const int prot = commit ? translateProtect(flProtect) : PROT_NONE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
if (!commit) {
flags |= MAP_NORESERVE;
}
void *result = MAP_FAILED;
if (lpAddress) {
#ifdef MAP_FIXED_NOREPLACE
flags |= MAP_FIXED_NOREPLACE;
#else
flags |= MAP_FIXED;
#endif
result = mmap(reinterpret_cast<void *>(base), length, prot, flags, -1, 0);
} else {
result = alignedReserve(length, prot, flags);
}
if (result == MAP_FAILED) {
wibo::lastError = wibo::winErrorFromErrno(errno);
return nullptr;
}
if (reinterpret_cast<uintptr_t>(result) >= 0x80000000) {
munmap(result, length);
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
return nullptr;
}
uintptr_t actualBase = reinterpret_cast<uintptr_t>(result);
VirtualAllocation allocation{};
allocation.base = actualBase;
allocation.size = length;
allocation.allocationProtect = flProtect;
allocation.pageProtect.assign(length / pageSize, commit ? flProtect : 0);
g_virtualAllocations[actualBase] = std::move(allocation);
wibo::lastError = ERROR_SUCCESS;
return result;
}
uintptr_t request = reinterpret_cast<uintptr_t>(lpAddress);
if (addOverflows(request, static_cast<size_t>(dwSize))) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
uintptr_t start = alignDown(request, pageSize);
uintptr_t end = alignUp(request + static_cast<uintptr_t>(dwSize), pageSize);
size_t length = static_cast<size_t>(end - start);
if (length == 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
VirtualAllocation *region = lookupRegion(start);
if (!region || !rangeWithinRegion(*region, start, length)) {
wibo::lastError = ERROR_INVALID_ADDRESS;
return nullptr;
}
const size_t pageCount = length / pageSize;
std::vector<std::pair<uintptr_t, size_t>> committedRuns;
committedRuns.reserve(pageCount);
for (size_t i = 0; i < pageCount; ++i) {
size_t pageIndex = ((start - region->base) / pageSize) + i;
if (pageIndex >= region->pageProtect.size()) {
wibo::lastError = ERROR_INVALID_ADDRESS;
return nullptr;
}
if (region->pageProtect[pageIndex] != 0) {
continue;
}
uintptr_t runBase = start + i * pageSize;
size_t runLength = pageSize;
while (i + 1 < pageCount) {
size_t nextIndex = ((start - region->base) / pageSize) + i + 1;
if (region->pageProtect[nextIndex] != 0) {
break;
}
++i;
runLength += pageSize;
}
committedRuns.emplace_back(runBase, runLength);
}
for (const auto &run : committedRuns) {
void *result = mmap(reinterpret_cast<void *>(run.first), run.second, translateProtect(flProtect),
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
if (result == MAP_FAILED) {
wibo::lastError = wibo::winErrorFromErrno(errno);
return nullptr;
}
markCommitted(*region, run.first, run.second, flProtect);
}
wibo::lastError = ERROR_SUCCESS;
DEBUG_LOG("VirtualAlloc commit success -> %p\n", reinterpret_cast<void *>(start));
return reinterpret_cast<LPVOID>(start);
}
BOOL WIN_FUNC VirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType) {
DEBUG_LOG("VirtualFree(%p, %zu, %u)\n", lpAddress, dwSize, dwFreeType);
if (!lpAddress) {
wibo::lastError = ERROR_INVALID_ADDRESS;
return FALSE;
}
if ((dwFreeType & (MEM_COALESCE_PLACEHOLDERS | MEM_PRESERVE_PLACEHOLDER)) != 0) {
wibo::lastError = ERROR_NOT_SUPPORTED;
return FALSE;
}
const bool release = (dwFreeType & MEM_RELEASE) != 0;
const bool decommit = (dwFreeType & MEM_DECOMMIT) != 0;
if (release == decommit) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
const size_t pageSize = systemPageSize();
std::unique_lock<std::mutex> lock(g_virtualAllocMutex);
if (release) {
uintptr_t base = reinterpret_cast<uintptr_t>(lpAddress);
auto exact = g_virtualAllocations.find(base);
if (exact == g_virtualAllocations.end()) {
auto containing = findRegionIterator(base);
if (dwSize != 0 && containing != g_virtualAllocations.end()) {
wibo::lastError = ERROR_INVALID_PARAMETER;
} else {
wibo::lastError = ERROR_INVALID_ADDRESS;
}
return FALSE;
}
if (dwSize != 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
size_t length = exact->second.size;
g_virtualAllocations.erase(exact);
lock.unlock();
if (munmap(lpAddress, length) != 0) {
wibo::lastError = wibo::winErrorFromErrno(errno);
return FALSE;
}
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
uintptr_t request = reinterpret_cast<uintptr_t>(lpAddress);
auto regionIt = findRegionIterator(request);
if (regionIt == g_virtualAllocations.end()) {
wibo::lastError = ERROR_INVALID_ADDRESS;
return FALSE;
}
VirtualAllocation &region = regionIt->second;
uintptr_t start = alignDown(request, pageSize);
uintptr_t end = 0;
if (dwSize == 0) {
if (request != region.base) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
start = region.base;
end = region.base + region.size;
} else {
if (addOverflows(request, static_cast<size_t>(dwSize))) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
end = alignUp(request + static_cast<uintptr_t>(dwSize), pageSize);
}
if (end <= start) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
size_t length = static_cast<size_t>(end - start);
if (!rangeWithinRegion(region, start, length)) {
wibo::lastError = ERROR_INVALID_ADDRESS;
return FALSE;
}
void *result = mmap(reinterpret_cast<void *>(start), length, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_NORESERVE, -1, 0);
if (result == MAP_FAILED) {
wibo::lastError = wibo::winErrorFromErrno(errno);
return FALSE;
}
markDecommitted(region, start, length);
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC VirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect) {
DEBUG_LOG("VirtualProtect(%p, %zu, %u)\n", lpAddress, dwSize, flNewProtect);
if (!lpAddress || dwSize == 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
const size_t pageSize = systemPageSize();
uintptr_t request = reinterpret_cast<uintptr_t>(lpAddress);
uintptr_t start = alignDown(request, pageSize);
uintptr_t end = alignUp(request + static_cast<uintptr_t>(dwSize), pageSize);
if (end <= start) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
std::unique_lock<std::mutex> lock(g_virtualAllocMutex);
VirtualAllocation *region = lookupRegion(start);
if (!region || !rangeWithinRegion(*region, start, static_cast<size_t>(end - start))) {
wibo::lastError = ERROR_INVALID_ADDRESS;
return FALSE;
}
const size_t firstPage = (start - region->base) / pageSize;
const size_t pageCount = (end - start) / pageSize;
if (pageCount == 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
DWORD previousProtect = region->pageProtect[firstPage];
if (previousProtect == 0) {
wibo::lastError = ERROR_NOACCESS;
return FALSE;
}
for (size_t i = 0; i < pageCount; ++i) {
if (region->pageProtect[firstPage + i] == 0) {
wibo::lastError = ERROR_NOACCESS;
return FALSE;
}
}
int prot = translateProtect(flNewProtect);
if (mprotect(reinterpret_cast<void *>(start), end - start, prot) != 0) {
wibo::lastError = wibo::winErrorFromErrno(errno);
return FALSE;
}
for (size_t i = 0; i < pageCount; ++i) {
region->pageProtect[firstPage + i] = flNewProtect;
}
lock.unlock();
if (lpflOldProtect) {
*lpflOldProtect = previousProtect;
}
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
SIZE_T WIN_FUNC VirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength) {
DEBUG_LOG("VirtualQuery(%p, %p, %zu)\n", lpAddress, lpBuffer, dwLength);
if (!lpBuffer || dwLength < sizeof(MEMORY_BASIC_INFORMATION) || !lpAddress) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
std::memset(lpBuffer, 0, sizeof(MEMORY_BASIC_INFORMATION));
const size_t pageSize = systemPageSize();
uintptr_t request = reinterpret_cast<uintptr_t>(lpAddress);
uintptr_t pageBase = alignDown(request, pageSize);
std::unique_lock<std::mutex> lock(g_virtualAllocMutex);
VirtualAllocation *region = lookupRegion(pageBase);
if (!region) {
wibo::lastError = ERROR_INVALID_ADDRESS;
return 0;
}
const size_t pageIndex = (pageBase - region->base) / pageSize;
if (pageIndex >= region->pageProtect.size()) {
wibo::lastError = ERROR_INVALID_ADDRESS;
return 0;
}
const bool committed = region->pageProtect[pageIndex] != 0;
uintptr_t blockStart = pageBase;
uintptr_t blockEnd = pageBase + pageSize;
while (blockStart > region->base) {
size_t idx = (blockStart - region->base) / pageSize - 1;
bool pageCommitted = region->pageProtect[idx] != 0;
if (pageCommitted != committed) {
break;
}
blockStart -= pageSize;
}
while (blockEnd < region->base + region->size) {
size_t idx = (blockEnd - region->base) / pageSize;
bool pageCommitted = region->pageProtect[idx] != 0;
if (pageCommitted != committed) {
break;
}
blockEnd += pageSize;
}
lpBuffer->BaseAddress = reinterpret_cast<PVOID>(blockStart);
lpBuffer->AllocationBase = reinterpret_cast<PVOID>(region->base);
lpBuffer->AllocationProtect = region->allocationProtect != 0 ? region->allocationProtect : PAGE_NOACCESS;
lpBuffer->RegionSize = blockEnd - blockStart;
lpBuffer->State = committed ? MEM_COMMIT : MEM_RESERVE;
lpBuffer->Protect = committed ? region->pageProtect[pageIndex] : 0;
lpBuffer->Type = MEM_PRIVATE;
lock.unlock();
wibo::lastError = ERROR_SUCCESS;
return sizeof(MEMORY_BASIC_INFORMATION);
}
BOOL WIN_FUNC GetProcessWorkingSetSize(HANDLE hProcess, PSIZE_T lpMinimumWorkingSetSize,
PSIZE_T lpMaximumWorkingSetSize) {
DEBUG_LOG("GetProcessWorkingSetSize(%p, %p, %p)\n", hProcess, lpMinimumWorkingSetSize, lpMaximumWorkingSetSize);
(void)hProcess;
if (!lpMinimumWorkingSetSize || !lpMaximumWorkingSetSize) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
*lpMinimumWorkingSetSize = 32 * 1024 * 1024; // 32 MiB stub
*lpMaximumWorkingSetSize = 128 * 1024 * 1024; // 128 MiB stub
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC SetProcessWorkingSetSize(HANDLE hProcess, SIZE_T dwMinimumWorkingSetSize,
SIZE_T dwMaximumWorkingSetSize) {
DEBUG_LOG("SetProcessWorkingSetSize(%p, %zu, %zu)\n", hProcess, dwMinimumWorkingSetSize, dwMaximumWorkingSetSize);
(void)hProcess;
(void)dwMinimumWorkingSetSize;
(void)dwMaximumWorkingSetSize;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
} // namespace kernel32

66
dll/kernel32/memoryapi.h Normal file
View File

@ -0,0 +1,66 @@
#pragma once
#include "common.h"
#include "minwinbase.h"
// Allocation type flags
constexpr DWORD MEM_COMMIT = 0x00001000;
constexpr DWORD MEM_RESERVE = 0x00002000;
constexpr DWORD MEM_DECOMMIT = 0x00004000;
constexpr DWORD MEM_RELEASE = 0x00008000;
constexpr DWORD MEM_RESET = 0x00080000;
constexpr DWORD MEM_RESET_UNDO = 0x01000000;
constexpr DWORD MEM_TOP_DOWN = 0x00100000;
constexpr DWORD MEM_WRITE_WATCH = 0x00200000;
constexpr DWORD MEM_PHYSICAL = 0x00400000;
constexpr DWORD MEM_PRIVATE = 0x00020000;
constexpr DWORD MEM_LARGE_PAGES = 0x20000000;
constexpr DWORD MEM_COALESCE_PLACEHOLDERS = 0x00000001;
constexpr DWORD MEM_PRESERVE_PLACEHOLDER = 0x00000002;
// Page protection constants
constexpr DWORD PAGE_NOACCESS = 0x01;
constexpr DWORD PAGE_READONLY = 0x02;
constexpr DWORD PAGE_READWRITE = 0x04;
constexpr DWORD PAGE_WRITECOPY = 0x08;
constexpr DWORD PAGE_EXECUTE = 0x10;
constexpr DWORD PAGE_EXECUTE_READ = 0x20;
constexpr DWORD PAGE_EXECUTE_READWRITE = 0x40;
constexpr DWORD PAGE_EXECUTE_WRITECOPY = 0x80;
constexpr DWORD FILE_MAP_COPY = 0x00000001;
constexpr DWORD FILE_MAP_WRITE = 0x00000002;
constexpr DWORD FILE_MAP_READ = 0x00000004;
constexpr DWORD FILE_MAP_EXECUTE = 0x00000020;
struct MEMORY_BASIC_INFORMATION {
PVOID BaseAddress;
PVOID AllocationBase;
DWORD AllocationProtect;
SIZE_T RegionSize;
DWORD State;
DWORD Protect;
DWORD Type;
};
using PMEMORY_BASIC_INFORMATION = MEMORY_BASIC_INFORMATION *;
namespace kernel32 {
HANDLE WIN_FUNC CreateFileMappingA(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect,
DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCSTR lpName);
HANDLE WIN_FUNC CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect,
DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCWSTR lpName);
LPVOID WIN_FUNC MapViewOfFile(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap);
BOOL WIN_FUNC UnmapViewOfFile(LPCVOID lpBaseAddress);
LPVOID WIN_FUNC VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
BOOL WIN_FUNC VirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);
BOOL WIN_FUNC VirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
SIZE_T WIN_FUNC VirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength);
BOOL WIN_FUNC GetProcessWorkingSetSize(HANDLE hProcess, PSIZE_T lpMinimumWorkingSetSize,
PSIZE_T lpMaximumWorkingSetSize);
BOOL WIN_FUNC SetProcessWorkingSetSize(HANDLE hProcess, SIZE_T dwMinimumWorkingSetSize, SIZE_T dwMaximumWorkingSetSize);
} // namespace kernel32

92
dll/kernel32/minwinbase.h Normal file
View File

@ -0,0 +1,92 @@
#pragma once
#include "common.h"
struct SECURITY_ATTRIBUTES {
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
};
using PSECURITY_ATTRIBUTES = SECURITY_ATTRIBUTES *;
using LPSECURITY_ATTRIBUTES = SECURITY_ATTRIBUTES *;
struct FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
};
using PFILETIME = FILETIME *;
using LPFILETIME = FILETIME *;
struct SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
};
using PSYSTEMTIME = SYSTEMTIME *;
using LPSYSTEMTIME = SYSTEMTIME *;
enum FINDEX_INFO_LEVELS { FindExInfoStandard, FindExInfoBasic, FindExInfoMaxInfoLevel };
enum FINDEX_SEARCH_OPS {
FindExSearchNameMatch,
FindExSearchLimitToDirectories,
FindExSearchLimitToDevices,
FindExSearchMaxSearchOp
};
constexpr DWORD FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
constexpr DWORD FILE_ATTRIBUTE_NORMAL = 0x00000080;
constexpr DWORD MAX_PATH = 260;
struct WIN32_FIND_DATAA {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD dwReserved0;
DWORD dwReserved1;
char cFileName[MAX_PATH];
char cAlternateFileName[14];
};
struct WIN32_FIND_DATAW {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD dwReserved0;
DWORD dwReserved1;
WCHAR cFileName[MAX_PATH];
WCHAR cAlternateFileName[14];
};
using PWIN32_FIND_DATAA = WIN32_FIND_DATAA *;
using LPWIN32_FIND_DATAA = WIN32_FIND_DATAA *;
using PWIN32_FIND_DATAW = WIN32_FIND_DATAW *;
using LPWIN32_FIND_DATAW = WIN32_FIND_DATAW *;
typedef struct _OVERLAPPED {
ULONG_PTR Internal;
ULONG_PTR InternalHigh;
union {
struct {
DWORD Offset;
DWORD OffsetHigh;
};
PVOID Pointer;
};
HANDLE hEvent;
} OVERLAPPED, *LPOVERLAPPED;

258
dll/kernel32/processenv.cpp Normal file
View File

@ -0,0 +1,258 @@
#include "processenv.h"
#include "errors.h"
#include "files.h"
#include "internal.h"
#include "strutil.h"
#include <cstdlib>
#include <cstring>
#include <mimalloc.h>
#include <string>
#include <strings.h>
#include <unistd.h>
namespace {
std::string convertEnvValueForWindows(const std::string &name, const char *rawValue) {
if (!rawValue) {
return {};
}
if (strcasecmp(name.c_str(), "PATH") != 0) {
return rawValue;
}
std::string converted = files::hostPathListToWindows(rawValue);
return converted.empty() ? std::string(rawValue) : converted;
}
std::string convertEnvValueToHost(const std::string &name, const char *rawValue) {
if (!rawValue) {
return {};
}
if (strcasecmp(name.c_str(), "PATH") != 0) {
return rawValue;
}
std::string converted = files::windowsPathListToHost(rawValue);
return converted.empty() ? std::string(rawValue) : converted;
}
} // namespace
namespace kernel32 {
LPSTR WIN_FUNC GetCommandLineA() {
DEBUG_LOG("GetCommandLineA() -> %s\n", wibo::commandLine.c_str());
wibo::lastError = ERROR_SUCCESS;
return const_cast<LPSTR>(wibo::commandLine.c_str());
}
LPWSTR WIN_FUNC GetCommandLineW() {
DEBUG_LOG("GetCommandLineW() -> %s\n", wideStringToString(wibo::commandLineW.data()).c_str());
wibo::lastError = ERROR_SUCCESS;
return wibo::commandLineW.data();
}
HANDLE WIN_FUNC GetStdHandle(DWORD nStdHandle) {
DEBUG_LOG("GetStdHandle(%u)\n", nStdHandle);
return files::getStdHandle(nStdHandle);
}
BOOL WIN_FUNC SetStdHandle(DWORD nStdHandle, HANDLE hHandle) {
DEBUG_LOG("SetStdHandle(%u, %p)\n", nStdHandle, hHandle);
return files::setStdHandle(nStdHandle, hHandle);
}
LPCH WIN_FUNC GetEnvironmentStrings() {
DEBUG_LOG("GetEnvironmentStrings()\n");
size_t bufSize = 0;
char **work = environ;
while (*work) {
bufSize += strlen(*work) + 1;
work++;
}
bufSize++;
char *buffer = static_cast<char *>(mi_malloc(bufSize));
if (!buffer) {
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
return nullptr;
}
char *ptr = buffer;
work = environ;
while (*work) {
size_t strSize = strlen(*work);
memcpy(ptr, *work, strSize);
ptr[strSize] = 0;
ptr += strSize + 1;
work++;
}
*ptr = 0;
wibo::lastError = ERROR_SUCCESS;
return buffer;
}
LPWCH WIN_FUNC GetEnvironmentStringsW() {
DEBUG_LOG("GetEnvironmentStringsW()\n");
size_t bufSizeW = 0;
char **work = environ;
while (*work) {
bufSizeW += strlen(*work) + 1;
work++;
}
bufSizeW++;
uint16_t *buffer = static_cast<uint16_t *>(mi_malloc(bufSizeW * sizeof(uint16_t)));
if (!buffer) {
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
return nullptr;
}
uint16_t *ptr = buffer;
work = environ;
while (*work) {
VERBOSE_LOG("-> %s\n", *work);
size_t strSize = strlen(*work);
for (size_t i = 0; i < strSize; i++) {
*ptr++ = static_cast<uint8_t>((*work)[i]);
}
*ptr++ = 0;
work++;
}
*ptr = 0;
wibo::lastError = ERROR_SUCCESS;
return buffer;
}
BOOL WIN_FUNC FreeEnvironmentStringsA(LPCH penv) {
DEBUG_LOG("FreeEnvironmentStringsA(%p)\n", penv);
if (!penv) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
free(penv);
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC FreeEnvironmentStringsW(LPWCH penv) {
DEBUG_LOG("FreeEnvironmentStringsW(%p)\n", penv);
if (!penv) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
free(penv);
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
DWORD WIN_FUNC GetEnvironmentVariableA(LPCSTR lpName, LPSTR lpBuffer, DWORD nSize) {
DEBUG_LOG("GetEnvironmentVariableA(%s, %p, %u)\n", lpName ? lpName : "(null)", lpBuffer, nSize);
if (!lpName) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
const char *rawValue = getenv(lpName);
if (!rawValue) {
wibo::lastError = ERROR_ENVVAR_NOT_FOUND;
return 0;
}
std::string converted = convertEnvValueForWindows(lpName, rawValue);
const std::string &finalValue = converted.empty() ? std::string(rawValue) : converted;
DWORD len = static_cast<DWORD>(finalValue.size());
if (nSize == 0) {
wibo::lastError = ERROR_SUCCESS;
return len + 1;
}
if (!lpBuffer) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
if (nSize <= len) {
wibo::lastError = ERROR_SUCCESS;
return len + 1;
}
memcpy(lpBuffer, finalValue.c_str(), len + 1);
wibo::lastError = ERROR_SUCCESS;
return len;
}
DWORD WIN_FUNC GetEnvironmentVariableW(LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize) {
std::string name = lpName ? wideStringToString(lpName) : std::string();
DEBUG_LOG("GetEnvironmentVariableW(%s, %p, %u)\n", name.c_str(), lpBuffer, nSize);
if (name.empty()) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
const char *rawValue = getenv(name.c_str());
if (!rawValue) {
wibo::lastError = ERROR_ENVVAR_NOT_FOUND;
return 0;
}
std::string converted = convertEnvValueForWindows(name, rawValue);
const std::string &finalValue = converted.empty() ? std::string(rawValue) : converted;
auto wideValue = stringToWideString(finalValue.c_str());
DWORD required = static_cast<DWORD>(wideValue.size());
if (nSize == 0) {
wibo::lastError = ERROR_SUCCESS;
return required;
}
if (!lpBuffer) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
if (nSize < required) {
wibo::lastError = ERROR_SUCCESS;
return required;
}
std::copy(wideValue.begin(), wideValue.end(), lpBuffer);
wibo::lastError = ERROR_SUCCESS;
return required - 1;
}
BOOL WIN_FUNC SetEnvironmentVariableA(LPCSTR lpName, LPCSTR lpValue) {
DEBUG_LOG("SetEnvironmentVariableA(%s, %s)\n", lpName ? lpName : "(null)", lpValue ? lpValue : "(null)");
if (!lpName || std::strchr(lpName, '=')) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
int rc = 0;
if (!lpValue) {
rc = unsetenv(lpName);
if (rc != 0) {
setLastErrorFromErrno();
return FALSE;
}
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
std::string hostValue = convertEnvValueToHost(lpName, lpValue);
const char *valuePtr = hostValue.empty() ? lpValue : hostValue.c_str();
rc = setenv(lpName, valuePtr, 1);
if (rc != 0) {
setLastErrorFromErrno();
return FALSE;
}
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC SetEnvironmentVariableW(LPCWSTR lpName, LPCWSTR lpValue) {
DEBUG_LOG("SetEnvironmentVariableW -> ");
if (!lpName) {
wibo::lastError = ERROR_INVALID_PARAMETER;
DEBUG_LOG("ERROR_INVALID_PARAMETER\n");
return FALSE;
}
std::string name = wideStringToString(lpName);
std::string value = lpValue ? wideStringToString(lpValue) : std::string();
return SetEnvironmentVariableA(name.c_str(), lpValue ? value.c_str() : nullptr);
}
} // namespace kernel32

20
dll/kernel32/processenv.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include "common.h"
namespace kernel32 {
LPSTR WIN_FUNC GetCommandLineA();
LPWSTR WIN_FUNC GetCommandLineW();
HANDLE WIN_FUNC GetStdHandle(DWORD nStdHandle);
BOOL WIN_FUNC SetStdHandle(DWORD nStdHandle, HANDLE hHandle);
LPCH WIN_FUNC GetEnvironmentStrings();
LPWCH WIN_FUNC GetEnvironmentStringsW();
BOOL WIN_FUNC FreeEnvironmentStringsA(LPCH penv);
BOOL WIN_FUNC FreeEnvironmentStringsW(LPWCH penv);
DWORD WIN_FUNC GetEnvironmentVariableA(LPCSTR lpName, LPSTR lpBuffer, DWORD nSize);
DWORD WIN_FUNC GetEnvironmentVariableW(LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize);
BOOL WIN_FUNC SetEnvironmentVariableA(LPCSTR lpName, LPCSTR lpValue);
BOOL WIN_FUNC SetEnvironmentVariableW(LPCWSTR lpName, LPCWSTR lpValue);
} // namespace kernel32

View File

@ -1,21 +1,76 @@
#include "processthreadsapi.h"
#include "common.h"
#include "errors.h"
#include "files.h"
#include "handles.h"
#include "kernel32.h"
#include "internal.h"
#include "processes.h"
#include "strutil.h"
#include "timeutil.h"
#include <cerrno>
#include <csignal>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <functional>
#include <limits>
#include <pthread.h>
#include <string>
#include <sys/resource.h>
#include <sys/time.h>
#include <unistd.h>
#include <vector>
namespace {
using kernel32::ThreadObject;
constexpr DWORD kMaxTlsValues = 100;
bool g_tlsSlotsUsed[kMaxTlsValues] = {false};
LPVOID g_tlsSlots[kMaxTlsValues] = {nullptr};
DWORD_PTR g_processAffinityMask = 0;
bool g_processAffinityMaskInitialized = false;
const FILETIME kDefaultThreadFileTime = {static_cast<DWORD>(UNIX_TIME_ZERO & 0xFFFFFFFFULL),
static_cast<DWORD>(UNIX_TIME_ZERO >> 32)};
constexpr DWORD STARTF_USESHOWWINDOW = 0x00000001;
constexpr DWORD STARTF_USESTDHANDLES = 0x00000100;
constexpr WORD SW_SHOWNORMAL = 1;
struct ThreadStartData {
LPTHREAD_START_ROUTINE startRoutine;
void *parameter;
ThreadObject *threadObject;
};
FILETIME fileTimeFromTimeval(const struct timeval &value) {
uint64_t total = 0;
if (value.tv_sec > 0 || value.tv_usec > 0) {
total = static_cast<uint64_t>(value.tv_sec) * 10000000ULL + static_cast<uint64_t>(value.tv_usec) * 10ULL;
}
return fileTimeFromDuration(total);
}
FILETIME fileTimeFromTimespec(const struct timespec &value) {
uint64_t total = 0;
if (value.tv_sec > 0 || value.tv_nsec > 0) {
total = static_cast<uint64_t>(value.tv_sec) * 10000000ULL + static_cast<uint64_t>(value.tv_nsec) / 100ULL;
}
return fileTimeFromDuration(total);
}
void destroyThreadObject(ThreadObject *obj) {
if (!obj) {
return;
}
pthread_cond_destroy(&obj->cond);
pthread_mutex_destroy(&obj->mutex);
delete obj;
}
DWORD_PTR computeSystemAffinityMask() {
long reported = sysconf(_SC_NPROCESSORS_ONLN);
if (reported <= 0) {
@ -29,12 +84,153 @@ DWORD_PTR computeSystemAffinityMask() {
return (static_cast<DWORD_PTR>(1) << usable) - 1;
}
DWORD_PTR g_processAffinityMask = 0;
bool g_processAffinityMaskInitialized = false;
template <typename StartupInfo> void populateStartupInfo(StartupInfo *info) {
if (!info) {
return;
}
std::memset(info, 0, sizeof(StartupInfo));
info->cb = sizeof(StartupInfo);
info->dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
info->wShowWindow = SW_SHOWNORMAL;
info->cbReserved2 = 0;
info->lpReserved2 = nullptr;
info->hStdInput = files::getStdHandle(STD_INPUT_HANDLE);
info->hStdOutput = files::getStdHandle(STD_OUTPUT_HANDLE);
info->hStdError = files::getStdHandle(STD_ERROR_HANDLE);
}
} // namespace
namespace kernel32 {
thread_local ThreadObject *g_currentThreadObject = nullptr;
ThreadObject *ensureCurrentThreadObject() {
ThreadObject *obj = g_currentThreadObject;
if (obj) {
return obj;
}
obj = new ThreadObject();
obj->thread = pthread_self();
obj->finished = false;
obj->joined = false;
obj->detached = true;
obj->synthetic = false;
obj->exitCode = STILL_ACTIVE;
obj->refCount = 0;
obj->suspendCount = 0;
pthread_mutex_init(&obj->mutex, nullptr);
pthread_cond_init(&obj->cond, nullptr);
g_currentThreadObject = obj;
return obj;
}
ThreadObject *threadObjectFromHandle(HANDLE hThread) {
auto raw = reinterpret_cast<uintptr_t>(hThread);
if (raw == kPseudoCurrentThreadHandleValue) {
return ensureCurrentThreadObject();
}
if (raw == static_cast<uintptr_t>(-1) || raw == 0) {
return nullptr;
}
auto data = handles::dataFromHandle(hThread, false);
if (data.type != handles::TYPE_THREAD || data.ptr == nullptr) {
return nullptr;
}
return reinterpret_cast<ThreadObject *>(data.ptr);
}
ThreadObject *retainThreadObject(ThreadObject *obj) {
if (!obj) {
return nullptr;
}
pthread_mutex_lock(&obj->mutex);
obj->refCount++;
pthread_mutex_unlock(&obj->mutex);
return obj;
}
void releaseThreadObject(ThreadObject *obj) {
if (!obj) {
return;
}
pthread_t thread = 0;
pthread_mutex_lock(&obj->mutex);
obj->refCount--;
bool shouldDelete = false;
bool shouldDetach = false;
bool finished = obj->finished;
bool joined = obj->joined;
bool detached = obj->detached;
bool synthetic = obj->synthetic;
thread = obj->thread;
if (obj->refCount == 0) {
if (finished || synthetic) {
shouldDelete = true;
} else if (!detached) {
obj->detached = true;
shouldDetach = true;
detached = true;
}
}
pthread_mutex_unlock(&obj->mutex);
if (shouldDetach && !synthetic) {
pthread_detach(thread);
}
if (shouldDelete) {
if (!synthetic) {
if (!joined && !detached) {
pthread_join(thread, nullptr);
}
}
destroyThreadObject(obj);
}
}
static void *threadTrampoline(void *param) {
ThreadStartData *data = static_cast<ThreadStartData *>(param);
ThreadObject *obj = data->threadObject;
LPTHREAD_START_ROUTINE startRoutine = data->startRoutine;
void *userParam = data->parameter;
delete data;
uint16_t previousSegment = 0;
bool tibInstalled = false;
if (wibo::tibSelector) {
asm volatile("mov %%fs, %0" : "=r"(previousSegment));
asm volatile("movw %0, %%fs" : : "r"(wibo::tibSelector) : "memory");
tibInstalled = true;
}
g_currentThreadObject = obj;
pthread_mutex_lock(&obj->mutex);
while (obj->suspendCount > 0) {
pthread_cond_wait(&obj->cond, &obj->mutex);
}
pthread_mutex_unlock(&obj->mutex);
DWORD result = startRoutine ? startRoutine(userParam) : 0;
pthread_mutex_lock(&obj->mutex);
obj->finished = true;
obj->exitCode = result;
pthread_cond_broadcast(&obj->cond);
bool shouldDelete = (obj->refCount == 0);
bool detached = obj->detached;
pthread_mutex_unlock(&obj->mutex);
g_currentThreadObject = nullptr;
if (shouldDelete) {
assert(detached && "ThreadObject must be detached when refCount reaches zero before completion");
destroyThreadObject(obj);
}
if (tibInstalled) {
asm volatile("movw %0, %%fs" : : "r"(previousSegment) : "memory");
}
return nullptr;
}
HANDLE WIN_FUNC GetCurrentProcess() {
DEBUG_LOG("GetCurrentProcess() -> %p\n", reinterpret_cast<void *>(static_cast<uintptr_t>(-1)));
return reinterpret_cast<HANDLE>(static_cast<uintptr_t>(-1));
@ -53,6 +249,14 @@ DWORD WIN_FUNC GetCurrentThreadId() {
return threadId;
}
HANDLE WIN_FUNC GetCurrentThread() {
ThreadObject *obj = ensureCurrentThreadObject();
(void)obj;
HANDLE pseudoHandle = reinterpret_cast<HANDLE>(kPseudoCurrentThreadHandleValue);
DEBUG_LOG("GetCurrentThread() -> %p\n", pseudoHandle);
return pseudoHandle;
}
BOOL WIN_FUNC GetProcessAffinityMask(HANDLE hProcess, PDWORD_PTR lpProcessAffinityMask,
PDWORD_PTR lpSystemAffinityMask) {
DEBUG_LOG("GetProcessAffinityMask(%p, %p, %p)\n", hProcess, lpProcessAffinityMask, lpSystemAffinityMask);
@ -116,6 +320,38 @@ BOOL WIN_FUNC SetProcessAffinityMask(HANDLE hProcess, DWORD_PTR dwProcessAffinit
return TRUE;
}
DWORD_PTR WIN_FUNC SetThreadAffinityMask(HANDLE hThread, DWORD_PTR dwThreadAffinityMask) {
DEBUG_LOG("SetThreadAffinityMask(%p, 0x%lx)\n", hThread, static_cast<unsigned long>(dwThreadAffinityMask));
if (dwThreadAffinityMask == 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
uintptr_t rawThreadHandle = reinterpret_cast<uintptr_t>(hThread);
bool isPseudoHandle = rawThreadHandle == kPseudoCurrentThreadHandleValue || rawThreadHandle == 0 ||
rawThreadHandle == static_cast<uintptr_t>(-1);
if (!isPseudoHandle) {
auto data = handles::dataFromHandle(hThread, false);
if (data.type != handles::TYPE_THREAD) {
wibo::lastError = ERROR_INVALID_HANDLE;
return 0;
}
}
DWORD_PTR processMask = 0;
DWORD_PTR systemMask = 0;
if (!GetProcessAffinityMask(GetCurrentProcess(), &processMask, &systemMask)) {
return 0;
}
if ((dwThreadAffinityMask & ~systemMask) != 0 || (dwThreadAffinityMask & processMask) == 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
wibo::lastError = ERROR_SUCCESS;
return processMask;
}
void WIN_FUNC ExitProcess(UINT uExitCode) {
DEBUG_LOG("ExitProcess(%u)\n", uExitCode);
std::exit(static_cast<int>(uExitCode));
@ -169,6 +405,258 @@ BOOL WIN_FUNC GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode) {
return TRUE;
}
DWORD WIN_FUNC TlsAlloc() {
VERBOSE_LOG("TlsAlloc()\n");
for (DWORD i = 0; i < kMaxTlsValues; ++i) {
if (!g_tlsSlotsUsed[i]) {
g_tlsSlotsUsed[i] = true;
g_tlsSlots[i] = nullptr;
wibo::lastError = ERROR_SUCCESS;
return i;
}
}
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
return TLS_OUT_OF_INDEXES;
}
BOOL WIN_FUNC TlsFree(DWORD dwTlsIndex) {
VERBOSE_LOG("TlsFree(%u)\n", dwTlsIndex);
if (dwTlsIndex >= kMaxTlsValues || !g_tlsSlotsUsed[dwTlsIndex]) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
g_tlsSlotsUsed[dwTlsIndex] = false;
g_tlsSlots[dwTlsIndex] = nullptr;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
LPVOID WIN_FUNC TlsGetValue(DWORD dwTlsIndex) {
VERBOSE_LOG("TlsGetValue(%u)\n", dwTlsIndex);
if (dwTlsIndex >= kMaxTlsValues || !g_tlsSlotsUsed[dwTlsIndex]) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return nullptr;
}
wibo::lastError = ERROR_SUCCESS;
return g_tlsSlots[dwTlsIndex];
}
BOOL WIN_FUNC TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue) {
VERBOSE_LOG("TlsSetValue(%u, %p)\n", dwTlsIndex, lpTlsValue);
if (dwTlsIndex >= kMaxTlsValues || !g_tlsSlotsUsed[dwTlsIndex]) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
g_tlsSlots[dwTlsIndex] = lpTlsValue;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
DWORD WIN_FUNC ResumeThread(HANDLE hThread) {
DEBUG_LOG("ResumeThread(%p)\n", hThread);
ThreadObject *obj = threadObjectFromHandle(hThread);
if (!obj) {
wibo::lastError = ERROR_INVALID_HANDLE;
return static_cast<DWORD>(-1);
}
pthread_mutex_lock(&obj->mutex);
DWORD previous = obj->suspendCount;
if (obj->suspendCount > 0) {
obj->suspendCount--;
if (obj->suspendCount == 0) {
pthread_cond_broadcast(&obj->cond);
}
}
pthread_mutex_unlock(&obj->mutex);
wibo::lastError = ERROR_SUCCESS;
return previous;
}
HRESULT WIN_FUNC SetThreadDescription(HANDLE hThread, LPCWSTR lpThreadDescription) {
DEBUG_LOG("STUB: SetThreadDescription(%p, %p)\n", hThread, lpThreadDescription);
(void)hThread;
(void)lpThreadDescription;
return S_OK;
}
HANDLE WIN_FUNC CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags,
LPDWORD lpThreadId) {
DEBUG_LOG("CreateThread(%p, %zu, %p, %p, %u, %p)\n", lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter,
dwCreationFlags, lpThreadId);
(void)lpThreadAttributes;
constexpr DWORD CREATE_SUSPENDED = 0x00000004;
constexpr DWORD STACK_SIZE_PARAM_IS_A_RESERVATION = 0x00010000;
constexpr DWORD SUPPORTED_FLAGS = CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION;
if ((dwCreationFlags & ~SUPPORTED_FLAGS) != 0) {
DEBUG_LOG("CreateThread: unsupported creation flags 0x%x\n", dwCreationFlags);
wibo::lastError = ERROR_NOT_SUPPORTED;
return nullptr;
}
bool startSuspended = (dwCreationFlags & CREATE_SUSPENDED) != 0;
ThreadObject *obj = new ThreadObject();
pthread_mutex_init(&obj->mutex, nullptr);
pthread_cond_init(&obj->cond, nullptr);
obj->finished = false;
obj->joined = false;
obj->detached = false;
obj->exitCode = 0;
obj->refCount = 1;
obj->suspendCount = startSuspended ? 1u : 0u;
obj->synthetic = false;
ThreadStartData *startData = new ThreadStartData{lpStartAddress, lpParameter, obj};
pthread_attr_t attr;
pthread_attr_t *attrPtr = nullptr;
if (dwStackSize != 0) {
pthread_attr_init(&attr);
size_t stackSize = dwStackSize;
#ifdef PTHREAD_STACK_MIN
if (stackSize < static_cast<size_t>(PTHREAD_STACK_MIN)) {
stackSize = PTHREAD_STACK_MIN;
}
#endif
if (pthread_attr_setstacksize(&attr, stackSize) == 0) {
attrPtr = &attr;
} else {
pthread_attr_destroy(&attr);
}
}
int rc = pthread_create(&obj->thread, attrPtr, threadTrampoline, startData);
if (attrPtr) {
pthread_attr_destroy(attrPtr);
}
if (rc != 0) {
delete startData;
destroyThreadObject(obj);
errno = rc;
setLastErrorFromErrno();
return nullptr;
}
if (lpThreadId) {
std::size_t hashed = std::hash<pthread_t>{}(obj->thread);
*lpThreadId = static_cast<DWORD>(hashed & 0xffffffffu);
}
wibo::lastError = ERROR_SUCCESS;
return handles::allocDataHandle({handles::TYPE_THREAD, obj, 0});
}
void WIN_FUNC ExitThread(DWORD dwExitCode) {
DEBUG_LOG("ExitThread(%u)\n", dwExitCode);
ThreadObject *obj = g_currentThreadObject;
uint16_t previousSegment = 0;
bool tibInstalled = false;
if (wibo::tibSelector) {
asm volatile("mov %%fs, %0" : "=r"(previousSegment));
asm volatile("movw %0, %%fs" : : "r"(wibo::tibSelector) : "memory");
tibInstalled = true;
}
if (obj) {
pthread_mutex_lock(&obj->mutex);
obj->finished = true;
obj->exitCode = dwExitCode;
pthread_cond_broadcast(&obj->cond);
bool shouldDelete = (obj->refCount == 0);
bool detached = obj->detached;
pthread_mutex_unlock(&obj->mutex);
g_currentThreadObject = nullptr;
if (shouldDelete) {
assert(detached && "ThreadObject must be detached when refCount reaches zero before completion");
destroyThreadObject(obj);
}
}
if (tibInstalled) {
asm volatile("movw %0, %%fs" : : "r"(previousSegment) : "memory");
}
pthread_exit(nullptr);
}
BOOL WIN_FUNC GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode) {
DEBUG_LOG("GetExitCodeThread(%p, %p)\n", hThread, lpExitCode);
if (!lpExitCode) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
ThreadObject *obj = threadObjectFromHandle(hThread);
if (!obj) {
wibo::lastError = ERROR_INVALID_HANDLE;
return FALSE;
}
pthread_mutex_lock(&obj->mutex);
DWORD code = obj->finished ? obj->exitCode : STILL_ACTIVE;
pthread_mutex_unlock(&obj->mutex);
*lpExitCode = code;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC SetThreadPriority(HANDLE hThread, int nPriority) {
DEBUG_LOG("STUB: SetThreadPriority(%p, %d)\n", hThread, nPriority);
(void)hThread;
(void)nPriority;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
int WIN_FUNC GetThreadPriority(HANDLE hThread) {
DEBUG_LOG("STUB: GetThreadPriority(%p)\n", hThread);
(void)hThread;
wibo::lastError = ERROR_SUCCESS;
return 0;
}
BOOL WIN_FUNC GetThreadTimes(HANDLE hThread, FILETIME *lpCreationTime, FILETIME *lpExitTime, FILETIME *lpKernelTime,
FILETIME *lpUserTime) {
DEBUG_LOG("GetThreadTimes(%p, %p, %p, %p, %p)\n", hThread, lpCreationTime, lpExitTime, lpKernelTime, lpUserTime);
if (!lpKernelTime || !lpUserTime) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
bool isPseudoCurrentThread = reinterpret_cast<uintptr_t>(hThread) == kPseudoCurrentThreadHandleValue ||
hThread == nullptr || hThread == reinterpret_cast<HANDLE>(static_cast<uintptr_t>(-1));
if (!isPseudoCurrentThread) {
DEBUG_LOG("GetThreadTimes: unsupported handle %p\n", hThread);
wibo::lastError = ERROR_INVALID_HANDLE;
return FALSE;
}
if (lpCreationTime) {
*lpCreationTime = kDefaultThreadFileTime;
}
if (lpExitTime) {
lpExitTime->dwLowDateTime = 0;
lpExitTime->dwHighDateTime = 0;
}
struct rusage usage;
if (getrusage(RUSAGE_THREAD, &usage) == 0) {
*lpKernelTime = fileTimeFromTimeval(usage.ru_stime);
*lpUserTime = fileTimeFromTimeval(usage.ru_utime);
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
struct timespec cpuTime;
if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &cpuTime) == 0) {
*lpKernelTime = fileTimeFromDuration(0);
*lpUserTime = fileTimeFromTimespec(cpuTime);
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
setLastErrorFromErrno();
*lpKernelTime = fileTimeFromDuration(0);
*lpUserTime = fileTimeFromDuration(0);
return FALSE;
}
BOOL WIN_FUNC CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags,
LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo,
@ -256,4 +744,20 @@ BOOL WIN_FUNC CreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LP
lpProcessInformation);
}
void WIN_FUNC GetStartupInfoA(LPSTARTUPINFOA lpStartupInfo) {
DEBUG_LOG("GetStartupInfoA(%p)\n", lpStartupInfo);
populateStartupInfo(lpStartupInfo);
}
void WIN_FUNC GetStartupInfoW(LPSTARTUPINFOW lpStartupInfo) {
DEBUG_LOG("GetStartupInfoW(%p)\n", lpStartupInfo);
populateStartupInfo(lpStartupInfo);
}
BOOL WIN_FUNC SetThreadStackGuarantee(PULONG StackSizeInBytes) {
DEBUG_LOG("STUB: SetThreadStackGuarantee(%p)\n", StackSizeInBytes);
(void)StackSizeInBytes;
return TRUE;
}
} // namespace kernel32

View File

@ -0,0 +1,107 @@
#pragma once
#include "common.h"
#include "errors.h"
#include "minwinbase.h"
struct PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
};
using PPROCESS_INFORMATION = PROCESS_INFORMATION *;
using LPPROCESS_INFORMATION = PROCESS_INFORMATION *;
struct STARTUPINFOA {
DWORD cb;
LPSTR lpReserved;
LPSTR lpDesktop;
LPSTR lpTitle;
DWORD dwX;
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars;
DWORD dwYCountChars;
DWORD dwFillAttribute;
DWORD dwFlags;
WORD wShowWindow;
WORD cbReserved2;
unsigned char *lpReserved2;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
};
using LPSTARTUPINFOA = STARTUPINFOA *;
struct STARTUPINFOW {
DWORD cb;
LPWSTR lpReserved;
LPWSTR lpDesktop;
LPWSTR lpTitle;
DWORD dwX;
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars;
DWORD dwYCountChars;
DWORD dwFillAttribute;
DWORD dwFlags;
WORD wShowWindow;
WORD cbReserved2;
unsigned char *lpReserved2;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
};
using LPSTARTUPINFOW = STARTUPINFOW *;
constexpr DWORD TLS_OUT_OF_INDEXES = 0xFFFFFFFFu;
typedef DWORD(WIN_FUNC *LPTHREAD_START_ROUTINE)(LPVOID);
namespace kernel32 {
HANDLE WIN_FUNC GetCurrentProcess();
DWORD WIN_FUNC GetCurrentProcessId();
DWORD WIN_FUNC GetCurrentThreadId();
HANDLE WIN_FUNC GetCurrentThread();
BOOL WIN_FUNC GetProcessAffinityMask(HANDLE hProcess, PDWORD_PTR lpProcessAffinityMask,
PDWORD_PTR lpSystemAffinityMask);
BOOL WIN_FUNC SetProcessAffinityMask(HANDLE hProcess, DWORD_PTR dwProcessAffinityMask);
DWORD_PTR WIN_FUNC SetThreadAffinityMask(HANDLE hThread, DWORD_PTR dwThreadAffinityMask);
DWORD WIN_FUNC ResumeThread(HANDLE hThread);
HRESULT WIN_FUNC SetThreadDescription(HANDLE hThread, LPCWSTR lpThreadDescription);
void WIN_FUNC ExitProcess(UINT uExitCode);
BOOL WIN_FUNC TerminateProcess(HANDLE hProcess, UINT uExitCode);
BOOL WIN_FUNC GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode);
DWORD WIN_FUNC TlsAlloc();
BOOL WIN_FUNC TlsFree(DWORD dwTlsIndex);
LPVOID WIN_FUNC TlsGetValue(DWORD dwTlsIndex);
BOOL WIN_FUNC TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue);
HANDLE WIN_FUNC CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags,
LPDWORD lpThreadId);
void WIN_FUNC ExitThread(DWORD dwExitCode);
BOOL WIN_FUNC GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode);
BOOL WIN_FUNC SetThreadPriority(HANDLE hThread, int nPriority);
int WIN_FUNC GetThreadPriority(HANDLE hThread);
BOOL WIN_FUNC GetThreadTimes(HANDLE hThread, FILETIME *lpCreationTime, FILETIME *lpExitTime, FILETIME *lpKernelTime,
FILETIME *lpUserTime);
BOOL WIN_FUNC CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags,
LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation);
BOOL WIN_FUNC CreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags,
LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation);
void WIN_FUNC GetStartupInfoA(LPSTARTUPINFOA lpStartupInfo);
void WIN_FUNC GetStartupInfoW(LPSTARTUPINFOW lpStartupInfo);
BOOL WIN_FUNC SetThreadStackGuarantee(PULONG StackSizeInBytes);
} // namespace kernel32

View File

@ -0,0 +1,29 @@
#include "profileapi.h"
#include "common.h"
#include "errors.h"
namespace kernel32 {
BOOL WIN_FUNC QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount) {
VERBOSE_LOG("STUB: QueryPerformanceCounter(%p)\n", lpPerformanceCount);
if (!lpPerformanceCount) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
*lpPerformanceCount = 0;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency) {
VERBOSE_LOG("STUB: QueryPerformanceFrequency(%p)\n", lpFrequency);
if (!lpFrequency) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
*lpFrequency = 1;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
} // namespace kernel32

10
dll/kernel32/profileapi.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include "common.h"
namespace kernel32 {
BOOL WIN_FUNC QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount);
BOOL WIN_FUNC QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
} // namespace kernel32

View File

@ -0,0 +1,141 @@
#include "stringapiset.h"
#include "errors.h"
#include "strutil.h"
#include <cstring>
#include <cwctype>
#include <string>
#include <vector>
namespace kernel32 {
int WIN_FUNC WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar,
LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar) {
DEBUG_LOG("WideCharToMultiByte(%u, %u, %p, %d, %p, %d, %p, %p)\n", CodePage, dwFlags, lpWideCharStr, cchWideChar,
lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar);
(void)CodePage;
(void)dwFlags;
(void)lpDefaultChar;
if (lpUsedDefaultChar) {
*lpUsedDefaultChar = FALSE;
}
if (cchWideChar == -1) {
cchWideChar = static_cast<int>(wstrlen(lpWideCharStr)) + 1;
}
if (cbMultiByte == 0) {
return cchWideChar;
}
for (int i = 0; i < cchWideChar; i++) {
lpMultiByteStr[i] = static_cast<char>(lpWideCharStr[i] & 0xFF);
}
if (wibo::debugEnabled) {
std::string s(lpMultiByteStr, lpMultiByteStr + cchWideChar);
DEBUG_LOG("Converted string: [%s] (len %d)\n", s.c_str(), cchWideChar);
}
return cchWideChar;
}
int WIN_FUNC MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCCH lpMultiByteStr, int cbMultiByte,
LPWSTR lpWideCharStr, int cchWideChar) {
DEBUG_LOG("MultiByteToWideChar(%u, %u, %d, %d)\n", CodePage, dwFlags, cbMultiByte, cchWideChar);
(void)CodePage;
(void)dwFlags;
if (cbMultiByte == -1) {
cbMultiByte = static_cast<int>(strlen(lpMultiByteStr)) + 1;
}
if (cchWideChar == 0) {
return cbMultiByte;
}
if (wibo::debugEnabled) {
std::string s(lpMultiByteStr, lpMultiByteStr + cbMultiByte);
DEBUG_LOG("Converting string: [%s] (len %d)\n", s.c_str(), cbMultiByte);
}
assert(cbMultiByte <= cchWideChar);
for (int i = 0; i < cbMultiByte; i++) {
lpWideCharStr[i] = static_cast<uint16_t>(lpMultiByteStr[i] & 0xFF);
}
return cbMultiByte;
}
BOOL WIN_FUNC GetStringTypeW(DWORD dwInfoType, LPCWCH lpSrcStr, int cchSrc, LPWORD lpCharType) {
DEBUG_LOG("GetStringTypeW(%u, %p, %i, %p)\n", dwInfoType, lpSrcStr, cchSrc, lpCharType);
assert(dwInfoType == 1); // CT_CTYPE1
if (!lpSrcStr || !lpCharType) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
if (cchSrc < 0) {
cchSrc = static_cast<int>(wstrlen(lpSrcStr));
}
for (int i = 0; i < cchSrc; i++) {
wint_t c = lpSrcStr[i];
bool upper = std::iswupper(c);
bool lower = std::iswlower(c);
bool alpha = std::iswalpha(c);
bool digit = std::iswdigit(c);
bool space = std::iswspace(c);
bool blank = (c == L' ' || c == L'\t');
bool hex = std::iswxdigit(c);
bool cntrl = std::iswcntrl(c);
bool punct = std::iswpunct(c);
lpCharType[i] = (upper ? 1 : 0) | (lower ? 2 : 0) | (digit ? 4 : 0) | (space ? 8 : 0) | (punct ? 0x10 : 0) |
(cntrl ? 0x20 : 0) | (blank ? 0x40 : 0) | (hex ? 0x80 : 0) | (alpha ? 0x100 : 0);
}
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC GetStringTypeA(LCID Locale, DWORD dwInfoType, LPCSTR lpSrcStr, int cchSrc, LPWORD lpCharType) {
DEBUG_LOG("GetStringTypeA(%u, %u, %p, %d, %p)\n", Locale, dwInfoType, lpSrcStr, cchSrc, lpCharType);
(void)Locale;
if (!lpSrcStr || !lpCharType) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
if (dwInfoType != 1) {
wibo::lastError = ERROR_NOT_SUPPORTED;
return FALSE;
}
int length = cchSrc;
if (length < 0) {
length = static_cast<int>(strlen(lpSrcStr));
}
if (length < 0) {
length = 0;
}
std::vector<uint16_t> wide;
wide.reserve(static_cast<size_t>(length));
for (int i = 0; i < length; ++i) {
wide.push_back(static_cast<unsigned char>(lpSrcStr[i]));
}
BOOL result = TRUE;
if (length > 0) {
result = GetStringTypeW(dwInfoType, wide.data(), length, lpCharType);
} else {
result = TRUE;
}
wibo::lastError = result ? ERROR_SUCCESS : wibo::lastError;
return result;
}
} // namespace kernel32

View File

@ -0,0 +1,14 @@
#pragma once
#include "common.h"
namespace kernel32 {
int WIN_FUNC WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar,
LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar);
int WIN_FUNC MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCCH lpMultiByteStr, int cbMultiByte,
LPWSTR lpWideCharStr, int cchWideChar);
BOOL WIN_FUNC GetStringTypeW(DWORD dwInfoType, LPCWCH lpSrcStr, int cchSrc, LPWORD lpCharType);
BOOL WIN_FUNC GetStringTypeA(LCID Locale, DWORD dwInfoType, LPCSTR lpSrcStr, int cchSrc, LPWORD lpCharType);
} // namespace kernel32

View File

@ -1,12 +1,13 @@
#include "synchapi.h"
#include "common.h"
#include "errors.h"
#include "handles.h"
#include "internal.h"
#include "kernel32.h"
#include "processes.h"
#include "strutil.h"
#include <cerrno>
#include <cstring>
#include <mutex>
#include <pthread.h>
#include <string>
@ -489,6 +490,105 @@ DWORD WIN_FUNC WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
}
}
void WIN_FUNC InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection) {
VERBOSE_LOG("STUB: InitializeCriticalSection(%p)\n", lpCriticalSection);
if (!lpCriticalSection) {
return;
}
std::memset(lpCriticalSection, 0, sizeof(*lpCriticalSection));
}
BOOL WIN_FUNC InitializeCriticalSectionEx(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount, DWORD Flags) {
DEBUG_LOG("STUB: InitializeCriticalSectionEx(%p, %u, 0x%x)\n", lpCriticalSection, dwSpinCount, Flags);
if (!lpCriticalSection) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
if (Flags & ~CRITICAL_SECTION_NO_DEBUG_INFO) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
std::memset(lpCriticalSection, 0, sizeof(*lpCriticalSection));
lpCriticalSection->SpinCount = dwSpinCount;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount) {
DEBUG_LOG("STUB: InitializeCriticalSectionAndSpinCount(%p, %u)\n", lpCriticalSection, dwSpinCount);
if (!lpCriticalSection) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
std::memset(lpCriticalSection, 0, sizeof(*lpCriticalSection));
lpCriticalSection->SpinCount = dwSpinCount;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
void WIN_FUNC DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection) {
VERBOSE_LOG("STUB: DeleteCriticalSection(%p)\n", lpCriticalSection);
(void)lpCriticalSection;
}
void WIN_FUNC EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection) {
VERBOSE_LOG("STUB: EnterCriticalSection(%p)\n", lpCriticalSection);
(void)lpCriticalSection;
}
void WIN_FUNC LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection) {
VERBOSE_LOG("STUB: LeaveCriticalSection(%p)\n", lpCriticalSection);
(void)lpCriticalSection;
}
BOOL WIN_FUNC InitOnceBeginInitialize(LPINIT_ONCE lpInitOnce, DWORD dwFlags, PBOOL fPending, LPVOID *lpContext) {
DEBUG_LOG("STUB: InitOnceBeginInitialize(%p, %u, %p, %p)\n", lpInitOnce, dwFlags, fPending, lpContext);
if (!lpInitOnce) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
if (dwFlags & ~(INIT_ONCE_CHECK_ONLY | INIT_ONCE_ASYNC)) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
if (fPending) {
*fPending = TRUE;
}
if (lpContext) {
*lpContext = nullptr;
}
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC InitOnceComplete(LPINIT_ONCE lpInitOnce, DWORD dwFlags, LPVOID lpContext) {
DEBUG_LOG("STUB: InitOnceComplete(%p, %u, %p)\n", lpInitOnce, dwFlags, lpContext);
if (!lpInitOnce) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
if ((dwFlags & INIT_ONCE_INIT_FAILED) && (dwFlags & INIT_ONCE_ASYNC)) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
wibo::lastError = ERROR_SUCCESS;
(void)lpContext;
return TRUE;
}
void WIN_FUNC AcquireSRWLockShared(PSRWLOCK SRWLock) { VERBOSE_LOG("STUB: AcquireSRWLockShared(%p)\n", SRWLock); }
void WIN_FUNC ReleaseSRWLockShared(PSRWLOCK SRWLock) { VERBOSE_LOG("STUB: ReleaseSRWLockShared(%p)\n", SRWLock); }
void WIN_FUNC AcquireSRWLockExclusive(PSRWLOCK SRWLock) { VERBOSE_LOG("STUB: AcquireSRWLockExclusive(%p)\n", SRWLock); }
void WIN_FUNC ReleaseSRWLockExclusive(PSRWLOCK SRWLock) { VERBOSE_LOG("STUB: ReleaseSRWLockExclusive(%p)\n", SRWLock); }
BOOLEAN WIN_FUNC TryAcquireSRWLockExclusive(PSRWLOCK SRWLock) {
VERBOSE_LOG("STUB: TryAcquireSRWLockExclusive(%p)\n", SRWLock);
return TRUE;
}
void resetOverlappedEvent(OVERLAPPED *ov) {
if (!ov || !ov->hEvent) {
return;

105
dll/kernel32/synchapi.h Normal file
View File

@ -0,0 +1,105 @@
#pragma once
#include "common.h"
#include "minwinbase.h"
constexpr DWORD WAIT_OBJECT_0 = 0x00000000;
constexpr DWORD WAIT_ABANDONED = 0x00000080;
constexpr DWORD WAIT_TIMEOUT = 0x00000102;
constexpr DWORD WAIT_FAILED = 0xFFFFFFFF;
constexpr DWORD INFINITE = 0xFFFFFFFF;
constexpr DWORD INIT_ONCE_CHECK_ONLY = 0x00000001UL;
constexpr DWORD INIT_ONCE_ASYNC = 0x00000002UL;
constexpr DWORD INIT_ONCE_INIT_FAILED = 0x00000004UL;
constexpr DWORD INIT_ONCE_CTX_RESERVED_BITS = 2;
constexpr DWORD CRITICAL_SECTION_NO_DEBUG_INFO = 0x01000000UL;
struct LIST_ENTRY {
LIST_ENTRY *Flink;
LIST_ENTRY *Blink;
};
struct RTL_CRITICAL_SECTION;
struct RTL_CRITICAL_SECTION_DEBUG {
WORD Type;
WORD CreatorBackTraceIndex;
RTL_CRITICAL_SECTION *CriticalSection;
LIST_ENTRY ProcessLocksList;
DWORD EntryCount;
DWORD ContentionCount;
DWORD Flags;
WORD CreatorBackTraceIndexHigh;
WORD SpareWORD;
DWORD Spare[2];
};
struct RTL_CRITICAL_SECTION {
RTL_CRITICAL_SECTION_DEBUG *DebugInfo;
LONG LockCount;
LONG RecursionCount;
HANDLE OwningThread;
HANDLE LockSemaphore;
ULONG_PTR SpinCount;
};
using PRTL_CRITICAL_SECTION = RTL_CRITICAL_SECTION *;
using LPCRITICAL_SECTION = RTL_CRITICAL_SECTION *;
using PCRITICAL_SECTION = RTL_CRITICAL_SECTION *;
using PRTL_CRITICAL_SECTION_DEBUG = RTL_CRITICAL_SECTION_DEBUG *;
union RTL_RUN_ONCE {
PVOID Ptr;
};
using PRTL_RUN_ONCE = RTL_RUN_ONCE *;
using INIT_ONCE = RTL_RUN_ONCE;
using PINIT_ONCE = INIT_ONCE *;
using LPINIT_ONCE = INIT_ONCE *;
constexpr INIT_ONCE INIT_ONCE_STATIC_INIT{nullptr};
union RTL_SRWLOCK {
PVOID Ptr;
};
using SRWLOCK = RTL_SRWLOCK;
using PSRWLOCK = SRWLOCK *;
using PRTL_SRWLOCK = SRWLOCK *;
constexpr SRWLOCK SRWLOCK_INIT{nullptr};
namespace kernel32 {
HANDLE WIN_FUNC CreateMutexA(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCSTR lpName);
HANDLE WIN_FUNC CreateMutexW(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCWSTR lpName);
BOOL WIN_FUNC ReleaseMutex(HANDLE hMutex);
HANDLE WIN_FUNC CreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState,
LPCSTR lpName);
HANDLE WIN_FUNC CreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState,
LPCWSTR lpName);
BOOL WIN_FUNC SetEvent(HANDLE hEvent);
BOOL WIN_FUNC ResetEvent(HANDLE hEvent);
HANDLE WIN_FUNC CreateSemaphoreA(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount,
LPCSTR lpName);
HANDLE WIN_FUNC CreateSemaphoreW(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount,
LPCWSTR lpName);
BOOL WIN_FUNC ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, PLONG lpPreviousCount);
DWORD WIN_FUNC WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
void WIN_FUNC InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
BOOL WIN_FUNC InitializeCriticalSectionEx(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount, DWORD Flags);
BOOL WIN_FUNC InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount);
void WIN_FUNC DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
void WIN_FUNC EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
void WIN_FUNC LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
BOOL WIN_FUNC InitOnceBeginInitialize(LPINIT_ONCE lpInitOnce, DWORD dwFlags, PBOOL fPending, LPVOID *lpContext);
BOOL WIN_FUNC InitOnceComplete(LPINIT_ONCE lpInitOnce, DWORD dwFlags, LPVOID lpContext);
void WIN_FUNC AcquireSRWLockShared(PSRWLOCK SRWLock);
void WIN_FUNC ReleaseSRWLockShared(PSRWLOCK SRWLock);
void WIN_FUNC AcquireSRWLockExclusive(PSRWLOCK SRWLock);
void WIN_FUNC ReleaseSRWLockExclusive(PSRWLOCK SRWLock);
BOOLEAN WIN_FUNC TryAcquireSRWLockExclusive(PSRWLOCK SRWLock);
} // namespace kernel32

View File

@ -1,12 +1,22 @@
#include "sysinfoapi.h"
#include "common.h"
#include "kernel32.h"
#include "errors.h"
#include "timeutil.h"
#include <cstring>
#include <ctime>
#include <sys/time.h>
namespace {
constexpr WORD PROCESSOR_ARCHITECTURE_INTEL = 0;
constexpr DWORD PROCESSOR_INTEL_PENTIUM = 586;
constexpr uint64_t kUnixTimeZero = 11644473600ULL * 10000000ULL;
constexpr DWORD kMajorVersion = 6;
constexpr DWORD kMinorVersion = 2;
constexpr DWORD kBuildNumber = 0;
DWORD_PTR computeSystemProcessorMask(unsigned int cpuCount) {
const auto maskWidth = static_cast<unsigned int>(sizeof(DWORD_PTR) * 8);
if (cpuCount >= maskWidth) {
@ -15,6 +25,7 @@ DWORD_PTR computeSystemProcessorMask(unsigned int cpuCount) {
DWORD_PTR mask = (static_cast<DWORD_PTR>(1) << cpuCount) - 1;
return mask == 0 ? 1 : mask;
}
} // namespace
namespace kernel32 {
@ -55,4 +66,135 @@ void WIN_FUNC GetSystemInfo(LPSYSTEM_INFO lpSystemInfo) {
lpSystemInfo->dwAllocationGranularity = 0x10000;
}
void WIN_FUNC GetSystemTime(LPSYSTEMTIME lpSystemTime) {
DEBUG_LOG("GetSystemTime(%p)\n", lpSystemTime);
if (!lpSystemTime) {
return;
}
time_t now = time(nullptr);
struct tm tmUtc{};
#if defined(_GNU_SOURCE) || defined(__APPLE__)
gmtime_r(&now, &tmUtc);
#else
struct tm *tmp = gmtime(&now);
if (!tmp) {
return;
}
tmUtc = *tmp;
#endif
lpSystemTime->wYear = static_cast<WORD>(tmUtc.tm_year + 1900);
lpSystemTime->wMonth = static_cast<WORD>(tmUtc.tm_mon + 1);
lpSystemTime->wDayOfWeek = static_cast<WORD>(tmUtc.tm_wday);
lpSystemTime->wDay = static_cast<WORD>(tmUtc.tm_mday);
lpSystemTime->wHour = static_cast<WORD>(tmUtc.tm_hour);
lpSystemTime->wMinute = static_cast<WORD>(tmUtc.tm_min);
lpSystemTime->wSecond = static_cast<WORD>(tmUtc.tm_sec);
lpSystemTime->wMilliseconds = 0;
}
void WIN_FUNC GetLocalTime(LPSYSTEMTIME lpSystemTime) {
DEBUG_LOG("GetLocalTime(%p)\n", lpSystemTime);
if (!lpSystemTime) {
return;
}
time_t now = time(nullptr);
struct tm tmLocal{};
#if defined(_GNU_SOURCE) || defined(__APPLE__)
localtime_r(&now, &tmLocal);
#else
struct tm *tmp = localtime(&now);
if (!tmp) {
return;
}
tmLocal = *tmp;
#endif
lpSystemTime->wYear = static_cast<WORD>(tmLocal.tm_year + 1900);
lpSystemTime->wMonth = static_cast<WORD>(tmLocal.tm_mon + 1);
lpSystemTime->wDayOfWeek = static_cast<WORD>(tmLocal.tm_wday);
lpSystemTime->wDay = static_cast<WORD>(tmLocal.tm_mday);
lpSystemTime->wHour = static_cast<WORD>(tmLocal.tm_hour);
lpSystemTime->wMinute = static_cast<WORD>(tmLocal.tm_min);
lpSystemTime->wSecond = static_cast<WORD>(tmLocal.tm_sec);
lpSystemTime->wMilliseconds = 0;
}
void WIN_FUNC GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime) {
DEBUG_LOG("GetSystemTimeAsFileTime(%p)\n", lpSystemTimeAsFileTime);
if (!lpSystemTimeAsFileTime) {
return;
}
#if defined(CLOCK_REALTIME)
struct timespec ts{};
if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
uint64_t ticks = kUnixTimeZero;
ticks += static_cast<uint64_t>(ts.tv_sec) * 10000000ULL;
ticks += static_cast<uint64_t>(ts.tv_nsec) / 100ULL;
*lpSystemTimeAsFileTime = fileTimeFromDuration(ticks);
return;
}
#endif
struct timeval tv{};
if (gettimeofday(&tv, nullptr) == 0) {
uint64_t ticks = kUnixTimeZero;
ticks += static_cast<uint64_t>(tv.tv_sec) * 10000000ULL;
ticks += static_cast<uint64_t>(tv.tv_usec) * 10ULL;
*lpSystemTimeAsFileTime = fileTimeFromDuration(ticks);
return;
}
const FILETIME fallback = {static_cast<DWORD>(kUnixTimeZero & 0xFFFFFFFFULL),
static_cast<DWORD>(kUnixTimeZero >> 32)};
*lpSystemTimeAsFileTime = fallback;
}
DWORD WIN_FUNC GetTickCount() {
DEBUG_LOG("GetTickCount()\n");
#if defined(CLOCK_MONOTONIC)
struct timespec ts{};
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
uint64_t milliseconds =
static_cast<uint64_t>(ts.tv_sec) * 1000ULL + static_cast<uint64_t>(ts.tv_nsec) / 1000000ULL;
DWORD result = static_cast<DWORD>(milliseconds & 0xFFFFFFFFULL);
DEBUG_LOG(" -> %u\n", result);
return result;
}
#endif
struct timeval tv{};
if (gettimeofday(&tv, nullptr) == 0) {
uint64_t milliseconds =
static_cast<uint64_t>(tv.tv_sec) * 1000ULL + static_cast<uint64_t>(tv.tv_usec) / 1000ULL;
DWORD result = static_cast<DWORD>(milliseconds & 0xFFFFFFFFULL);
DEBUG_LOG(" -> %u\n", result);
return result;
}
DEBUG_LOG(" -> 0\n");
return 0;
}
DWORD WIN_FUNC GetVersion() {
DEBUG_LOG("GetVersion()\n");
return kMajorVersion | (kMinorVersion << 8) | (5 << 16) | (kBuildNumber << 24);
}
BOOL WIN_FUNC GetVersionExA(LPOSVERSIONINFOA lpVersionInformation) {
DEBUG_LOG("GetVersionExA(%p)\n", lpVersionInformation);
if (!lpVersionInformation) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
std::memset(lpVersionInformation, 0, lpVersionInformation->dwOSVersionInfoSize);
lpVersionInformation->dwMajorVersion = kMajorVersion;
lpVersionInformation->dwMinorVersion = kMinorVersion;
lpVersionInformation->dwBuildNumber = kBuildNumber;
lpVersionInformation->dwPlatformId = 2;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
} // namespace kernel32

48
dll/kernel32/sysinfoapi.h Normal file
View File

@ -0,0 +1,48 @@
#pragma once
#include "common.h"
#include "minwinbase.h"
struct SYSTEM_INFO {
union {
DWORD dwOemId;
struct {
WORD wProcessorArchitecture;
WORD wReserved;
};
};
DWORD dwPageSize;
LPVOID lpMinimumApplicationAddress;
LPVOID lpMaximumApplicationAddress;
DWORD_PTR dwActiveProcessorMask;
DWORD dwNumberOfProcessors;
DWORD dwProcessorType;
DWORD dwAllocationGranularity;
WORD wProcessorLevel;
WORD wProcessorRevision;
};
using LPSYSTEM_INFO = SYSTEM_INFO *;
struct OSVERSIONINFOA {
DWORD dwOSVersionInfoSize;
DWORD dwMajorVersion;
DWORD dwMinorVersion;
DWORD dwBuildNumber;
DWORD dwPlatformId;
char szCSDVersion[128];
};
using LPOSVERSIONINFOA = OSVERSIONINFOA *;
namespace kernel32 {
void WIN_FUNC GetSystemInfo(LPSYSTEM_INFO lpSystemInfo);
void WIN_FUNC GetSystemTime(LPSYSTEMTIME lpSystemTime);
void WIN_FUNC GetLocalTime(LPSYSTEMTIME lpSystemTime);
void WIN_FUNC GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime);
DWORD WIN_FUNC GetTickCount();
DWORD WIN_FUNC GetVersion();
BOOL WIN_FUNC GetVersionExA(LPOSVERSIONINFOA lpVersionInformation);
} // namespace kernel32

212
dll/kernel32/timeutil.h Normal file
View File

@ -0,0 +1,212 @@
#pragma once
#include "minwinbase.h"
#include <cstdint>
#include <ctime>
#include <limits>
inline constexpr int64_t HUNDRED_NS_PER_SECOND = 10000000LL;
inline constexpr int64_t HUNDRED_NS_PER_MILLISECOND = 10000LL;
inline constexpr int64_t SECONDS_PER_DAY = 86400LL;
inline constexpr uint64_t TICKS_PER_DAY = static_cast<uint64_t>(SECONDS_PER_DAY) * HUNDRED_NS_PER_SECOND;
inline constexpr uint64_t UNIX_TIME_ZERO = 11644473600ULL * 10000000ULL;
inline constexpr uint64_t MAX_VALID_FILETIME = 0x8000000000000000ULL;
inline constexpr int64_t DAYS_TO_UNIX_EPOCH = 134774LL;
struct CivilDate {
int year;
unsigned month;
unsigned day;
};
inline int64_t daysFromCivil(int year, unsigned month, unsigned day) {
year -= month <= 2 ? 1 : 0;
const int era = (year >= 0 ? year : year - 399) / 400;
const unsigned yoe = static_cast<unsigned>(year - era * 400);
const unsigned doy = (153 * (month + (month > 2 ? -3 : 9)) + 2) / 5 + day - 1;
const unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + yoe / 400 + doy;
return era * 146097 + static_cast<int64_t>(doe) - 719468;
}
inline CivilDate civilFromDays(int64_t z) {
z += 719468;
const int64_t era = (z >= 0 ? z : z - 146096) / 146097;
const unsigned doe = static_cast<unsigned>(z - era * 146097);
const unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
const int64_t y = static_cast<int64_t>(yoe) + era * 400;
const unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
const unsigned mp = (5 * doy + 2) / 153;
const unsigned d = doy - (153 * mp + 2) / 5 + 1;
const unsigned m = mp + (mp < 10 ? 3 : -9);
return {static_cast<int>(y + (m <= 2)), m, d};
}
inline bool isLeapYear(int year) {
if ((year % 4) != 0) {
return false;
}
if ((year % 100) != 0) {
return true;
}
return (year % 400) == 0;
}
inline unsigned daysInMonth(int year, unsigned month) {
static const unsigned baseDays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
unsigned idx = month - 1;
unsigned value = baseDays[idx];
if (month == 2 && isLeapYear(year)) {
value += 1;
}
return value;
}
inline bool validateSystemTime(const SYSTEMTIME &st) {
if (st.wYear < 1601) {
return false;
}
if (st.wMonth < 1 || st.wMonth > 12) {
return false;
}
if (st.wDay < 1 || st.wDay > static_cast<short>(daysInMonth(st.wYear, static_cast<unsigned>(st.wMonth)))) {
return false;
}
if (st.wHour < 0 || st.wHour > 23) {
return false;
}
if (st.wMinute < 0 || st.wMinute > 59) {
return false;
}
if (st.wSecond < 0 || st.wSecond > 59) {
return false;
}
if (st.wMilliseconds < 0 || st.wMilliseconds > 999) {
return false;
}
return true;
}
inline bool systemTimeToUnixParts(const SYSTEMTIME &st, int64_t &secondsOut, uint32_t &hundredsOut) {
if (!validateSystemTime(st)) {
return false;
}
int64_t days = daysFromCivil(st.wYear, static_cast<unsigned>(st.wMonth), static_cast<unsigned>(st.wDay));
int64_t secondsOfDay =
static_cast<int64_t>(st.wHour) * 3600LL + static_cast<int64_t>(st.wMinute) * 60LL + st.wSecond;
secondsOut = days * SECONDS_PER_DAY + secondsOfDay;
hundredsOut = static_cast<uint32_t>(st.wMilliseconds) * static_cast<uint32_t>(HUNDRED_NS_PER_MILLISECOND);
return true;
}
inline uint64_t fileTimeToDuration(const FILETIME &value) {
return (static_cast<uint64_t>(value.dwHighDateTime) << 32) | value.dwLowDateTime;
}
inline bool fileTimeToUnixParts(const FILETIME &ft, int64_t &secondsOut, uint32_t &hundredsOut) {
uint64_t ticks = fileTimeToDuration(ft);
if (ticks >= UNIX_TIME_ZERO) {
uint64_t diff = ticks - UNIX_TIME_ZERO;
secondsOut = static_cast<int64_t>(diff / HUNDRED_NS_PER_SECOND);
hundredsOut = static_cast<uint32_t>(diff % HUNDRED_NS_PER_SECOND);
} else {
uint64_t diff = UNIX_TIME_ZERO - ticks;
secondsOut = -static_cast<int64_t>(diff / HUNDRED_NS_PER_SECOND);
uint64_t rem = diff % HUNDRED_NS_PER_SECOND;
if (rem != 0) {
secondsOut -= 1;
rem = HUNDRED_NS_PER_SECOND - rem;
}
hundredsOut = static_cast<uint32_t>(rem);
}
return true;
}
inline FILETIME fileTimeFromDuration(uint64_t ticks100ns) {
FILETIME result;
result.dwLowDateTime = static_cast<DWORD>(ticks100ns & 0xFFFFFFFFULL);
result.dwHighDateTime = static_cast<DWORD>(ticks100ns >> 32);
return result;
}
inline bool unixPartsToFileTime(int64_t seconds, uint32_t hundreds, FILETIME &out) {
if (hundreds >= HUNDRED_NS_PER_SECOND) {
return false;
}
#if defined(__SIZEOF_INT128__)
__int128 total = static_cast<__int128>(seconds) * HUNDRED_NS_PER_SECOND;
total += static_cast<__int128>(hundreds);
total += static_cast<__int128>(UNIX_TIME_ZERO);
if (total < 0 || total > static_cast<__int128>(std::numeric_limits<uint64_t>::max())) {
return false;
}
uint64_t ticks = static_cast<uint64_t>(total);
#else
long double total = static_cast<long double>(seconds) * static_cast<long double>(HUNDRED_NS_PER_SECOND);
total += static_cast<long double>(hundreds);
total += static_cast<long double>(UNIX_TIME_ZERO);
if (total < 0.0L || total > static_cast<long double>(std::numeric_limits<uint64_t>::max())) {
return false;
}
uint64_t ticks = static_cast<uint64_t>(total);
#endif
out = fileTimeFromDuration(ticks);
return true;
}
inline bool unixPartsToTimespec(int64_t seconds, uint32_t hundreds, struct timespec &out) {
if (hundreds >= HUNDRED_NS_PER_SECOND) {
return false;
}
if (seconds > static_cast<int64_t>(std::numeric_limits<time_t>::max()) ||
seconds < static_cast<int64_t>(std::numeric_limits<time_t>::min())) {
return false;
}
out.tv_sec = static_cast<time_t>(seconds);
out.tv_nsec = static_cast<long>(hundreds) * 100L;
return true;
}
inline bool tmToUnixSeconds(const struct tm &tmValue, int64_t &secondsOut) {
int year = tmValue.tm_year + 1900;
int month = tmValue.tm_mon + 1;
int day = tmValue.tm_mday;
int hour = tmValue.tm_hour;
int minute = tmValue.tm_min;
int second = tmValue.tm_sec;
if (month < 1 || month > 12) {
return false;
}
if (day < 1 || day > static_cast<int>(daysInMonth(year, static_cast<unsigned>(month)))) {
return false;
}
if (hour < 0 || hour > 23) {
return false;
}
if (minute < 0 || minute > 59) {
return false;
}
if (second < 0 || second > 60) {
return false;
}
if (second == 60) {
second = 59;
}
int64_t days = daysFromCivil(year, static_cast<unsigned>(month), static_cast<unsigned>(day));
secondsOut =
days * SECONDS_PER_DAY + static_cast<int64_t>(hour) * 3600LL + static_cast<int64_t>(minute) * 60LL + second;
return true;
}
inline bool shouldIgnoreFileTimeParam(const FILETIME *ft) {
if (!ft) {
return true;
}
if (ft->dwLowDateTime == 0 && ft->dwHighDateTime == 0) {
return true;
}
if (ft->dwLowDateTime == 0xFFFFFFFF && ft->dwHighDateTime == 0xFFFFFFFF) {
return true;
}
return false;
}

View File

@ -0,0 +1,278 @@
#include "timezoneapi.h"
#include "errors.h"
#include "timeutil.h"
#include <cerrno>
#include <cstring>
#include <ctime>
namespace kernel32 {
BOOL WIN_FUNC SystemTimeToFileTime(const SYSTEMTIME *lpSystemTime, LPFILETIME lpFileTime) {
DEBUG_LOG("SystemTimeToFileTime(%p, %p)\n", lpSystemTime, lpFileTime);
if (!lpSystemTime || !lpFileTime) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
int64_t seconds = 0;
uint32_t hundreds = 0;
if (!systemTimeToUnixParts(*lpSystemTime, seconds, hundreds)) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
FILETIME result;
if (!unixPartsToFileTime(seconds, hundreds, result)) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
if (fileTimeToDuration(result) >= MAX_VALID_FILETIME) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
*lpFileTime = result;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC FileTimeToSystemTime(const FILETIME *lpFileTime, LPSYSTEMTIME lpSystemTime) {
DEBUG_LOG("FileTimeToSystemTime(%p, %p)\n", lpFileTime, lpSystemTime);
if (!lpFileTime || !lpSystemTime) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
uint64_t ticks = fileTimeToDuration(*lpFileTime);
if (ticks >= MAX_VALID_FILETIME) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
uint64_t daysSince1601 = ticks / TICKS_PER_DAY;
uint64_t ticksOfDay = ticks % TICKS_PER_DAY;
uint32_t secondsOfDay = static_cast<uint32_t>(ticksOfDay / HUNDRED_NS_PER_SECOND);
uint32_t hundredNs = static_cast<uint32_t>(ticksOfDay % HUNDRED_NS_PER_SECOND);
int64_t daysSince1970 = static_cast<int64_t>(daysSince1601) - DAYS_TO_UNIX_EPOCH;
CivilDate date = civilFromDays(daysSince1970);
lpSystemTime->wYear = static_cast<WORD>(date.year);
lpSystemTime->wMonth = static_cast<WORD>(date.month);
lpSystemTime->wDay = static_cast<WORD>(date.day);
lpSystemTime->wDayOfWeek = static_cast<WORD>((daysSince1601 + 1ULL) % 7ULL);
lpSystemTime->wHour = static_cast<WORD>(secondsOfDay / 3600U);
lpSystemTime->wMinute = static_cast<WORD>((secondsOfDay % 3600U) / 60U);
lpSystemTime->wSecond = static_cast<WORD>(secondsOfDay % 60U);
lpSystemTime->wMilliseconds = static_cast<WORD>(hundredNs / HUNDRED_NS_PER_MILLISECOND);
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC FileTimeToLocalFileTime(const FILETIME *lpFileTime, LPFILETIME lpLocalFileTime) {
DEBUG_LOG("FileTimeToLocalFileTime(%p, %p)\n", lpFileTime, lpLocalFileTime);
if (!lpFileTime || !lpLocalFileTime) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
int64_t seconds = 0;
uint32_t hundreds = 0;
if (!fileTimeToUnixParts(*lpFileTime, seconds, hundreds)) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
if (seconds > static_cast<int64_t>(std::numeric_limits<time_t>::max()) ||
seconds < static_cast<int64_t>(std::numeric_limits<time_t>::min())) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
time_t unixTime = static_cast<time_t>(seconds);
struct tm localTm{};
#if defined(_POSIX_VERSION)
if (!localtime_r(&unixTime, &localTm)) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
#else
struct tm *tmp = localtime(&unixTime);
if (!tmp) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
localTm = *tmp;
#endif
int64_t localAsUtcSeconds = 0;
if (!tmToUnixSeconds(localTm, localAsUtcSeconds)) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
int64_t offsetSeconds = localAsUtcSeconds - seconds;
int64_t localSeconds = seconds + offsetSeconds;
FILETIME result;
if (!unixPartsToFileTime(localSeconds, hundreds, result)) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
*lpLocalFileTime = result;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC LocalFileTimeToFileTime(const FILETIME *lpLocalFileTime, LPFILETIME lpFileTime) {
DEBUG_LOG("LocalFileTimeToFileTime(%p, %p)\n", lpLocalFileTime, lpFileTime);
if (!lpLocalFileTime || !lpFileTime) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
uint64_t ticks = fileTimeToDuration(*lpLocalFileTime);
if (ticks >= MAX_VALID_FILETIME) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
uint32_t hundredNs = static_cast<uint32_t>(ticks % HUNDRED_NS_PER_SECOND);
uint64_t daysSince1601 = ticks / TICKS_PER_DAY;
uint64_t ticksOfDay = ticks % TICKS_PER_DAY;
uint32_t secondsOfDay = static_cast<uint32_t>(ticksOfDay / HUNDRED_NS_PER_SECOND);
int64_t daysSince1970 = static_cast<int64_t>(daysSince1601) - DAYS_TO_UNIX_EPOCH;
CivilDate date = civilFromDays(daysSince1970);
struct tm localTm{};
localTm.tm_year = date.year - 1900;
localTm.tm_mon = static_cast<int>(date.month) - 1;
localTm.tm_mday = static_cast<int>(date.day);
localTm.tm_hour = static_cast<int>(secondsOfDay / 3600U);
localTm.tm_min = static_cast<int>((secondsOfDay % 3600U) / 60U);
localTm.tm_sec = static_cast<int>(secondsOfDay % 60U);
localTm.tm_isdst = -1;
struct tm tmCopy = localTm;
errno = 0;
time_t utcTime = mktime(&tmCopy);
if (utcTime == static_cast<time_t>(-1) && errno != 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
FILETIME result;
if (!unixPartsToFileTime(static_cast<int64_t>(utcTime), hundredNs, result)) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
*lpFileTime = result;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC DosDateTimeToFileTime(WORD wFatDate, WORD wFatTime, LPFILETIME lpFileTime) {
DEBUG_LOG("DosDateTimeToFileTime(%04x, %04x, %p)\n", wFatDate, wFatTime, lpFileTime);
if (!lpFileTime) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
unsigned day = wFatDate & 0x1F;
unsigned month = (wFatDate >> 5) & 0x0F;
unsigned year = ((wFatDate >> 9) & 0x7F) + 1980;
unsigned second = (wFatTime & 0x1F) * 2;
unsigned minute = (wFatTime >> 5) & 0x3F;
unsigned hour = (wFatTime >> 11) & 0x1F;
if (day == 0 || month == 0 || month > 12 || day > 31 || hour > 23 || minute > 59 || second > 59) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
struct tm tmValue{};
tmValue.tm_year = static_cast<int>(year) - 1900;
tmValue.tm_mon = static_cast<int>(month) - 1;
tmValue.tm_mday = static_cast<int>(day);
tmValue.tm_hour = static_cast<int>(hour);
tmValue.tm_min = static_cast<int>(minute);
tmValue.tm_sec = static_cast<int>(second);
tmValue.tm_isdst = -1;
time_t localSeconds = mktime(&tmValue);
if (localSeconds == static_cast<time_t>(-1)) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
uint64_t ticks = (static_cast<uint64_t>(localSeconds) + UNIX_TIME_ZERO / HUNDRED_NS_PER_SECOND) * 10000000ULL;
*lpFileTime = fileTimeFromDuration(ticks);
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC FileTimeToDosDateTime(const FILETIME *lpFileTime, LPWORD lpFatDate, LPWORD lpFatTime) {
DEBUG_LOG("FileTimeToDosDateTime(%p, %p, %p)\n", lpFileTime, lpFatDate, lpFatTime);
if (!lpFileTime || !lpFatDate || !lpFatTime) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
uint64_t ticks = fileTimeToDuration(*lpFileTime);
if (ticks < UNIX_TIME_ZERO) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
time_t utcSeconds = static_cast<time_t>((ticks / 10000000ULL) - (UNIX_TIME_ZERO / HUNDRED_NS_PER_SECOND));
struct tm tmValue{};
if (!localtime_r(&utcSeconds, &tmValue)) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
int year = tmValue.tm_year + 1900;
if (year < 1980 || year > 2107) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
*lpFatDate = static_cast<WORD>(((year - 1980) << 9) | ((tmValue.tm_mon + 1) << 5) | tmValue.tm_mday);
*lpFatTime = static_cast<WORD>(((tmValue.tm_hour & 0x1F) << 11) | ((tmValue.tm_min & 0x3F) << 5) |
((tmValue.tm_sec / 2) & 0x1F));
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
DWORD WIN_FUNC GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation) {
DEBUG_LOG("GetTimeZoneInformation(%p)\n", lpTimeZoneInformation);
if (!lpTimeZoneInformation) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return TIME_ZONE_ID_INVALID;
}
std::memset(lpTimeZoneInformation, 0, sizeof(*lpTimeZoneInformation));
tzset();
auto copyName = [](WCHAR *dest, const char *src) {
if (!src) {
dest[0] = 0;
return;
}
for (size_t i = 0; i < 31 && src[i]; ++i) {
dest[i] = static_cast<unsigned char>(src[i]);
dest[i + 1] = 0;
}
};
time_t now = time(nullptr);
struct tm localTm{};
#if defined(_GNU_SOURCE) || defined(__APPLE__)
localtime_r(&now, &localTm);
#else
struct tm *tmp = localtime(&now);
if (tmp) {
localTm = *tmp;
}
#endif
long offsetSeconds = 0;
#if defined(__APPLE__) || defined(__linux__)
offsetSeconds = -localTm.tm_gmtoff;
#else
extern long timezone;
offsetSeconds = timezone;
if (localTm.tm_isdst > 0) {
extern int daylight;
if (daylight) {
offsetSeconds -= 3600;
}
}
#endif
lpTimeZoneInformation->Bias = static_cast<LONG>(offsetSeconds / 60);
copyName(lpTimeZoneInformation->StandardName, tzname[0]);
const char *daylightName = (daylight && tzname[1]) ? tzname[1] : tzname[0];
copyName(lpTimeZoneInformation->DaylightName, daylightName);
DWORD result = TIME_ZONE_ID_UNKNOWN;
if (daylight && localTm.tm_isdst > 0) {
lpTimeZoneInformation->DaylightBias = -60;
result = TIME_ZONE_ID_DAYLIGHT;
} else {
result = TIME_ZONE_ID_STANDARD;
}
wibo::lastError = ERROR_SUCCESS;
return result;
}
} // namespace kernel32

View File

@ -0,0 +1,33 @@
#pragma once
#include "common.h"
#include "minwinbase.h"
struct TIME_ZONE_INFORMATION {
LONG Bias;
WCHAR StandardName[32];
SYSTEMTIME StandardDate;
LONG StandardBias;
WCHAR DaylightName[32];
SYSTEMTIME DaylightDate;
LONG DaylightBias;
};
using LPTIME_ZONE_INFORMATION = TIME_ZONE_INFORMATION *;
constexpr DWORD TIME_ZONE_ID_UNKNOWN = 0;
constexpr DWORD TIME_ZONE_ID_STANDARD = 1;
constexpr DWORD TIME_ZONE_ID_DAYLIGHT = 2;
constexpr DWORD TIME_ZONE_ID_INVALID = 0xFFFFFFFFu;
namespace kernel32 {
BOOL WIN_FUNC SystemTimeToFileTime(const SYSTEMTIME *lpSystemTime, LPFILETIME lpFileTime);
BOOL WIN_FUNC FileTimeToSystemTime(const FILETIME *lpFileTime, LPSYSTEMTIME lpSystemTime);
BOOL WIN_FUNC FileTimeToLocalFileTime(const FILETIME *lpFileTime, LPFILETIME lpLocalFileTime);
BOOL WIN_FUNC LocalFileTimeToFileTime(const FILETIME *lpLocalFileTime, LPFILETIME lpFileTime);
BOOL WIN_FUNC DosDateTimeToFileTime(WORD wFatDate, WORD wFatTime, LPFILETIME lpFileTime);
BOOL WIN_FUNC FileTimeToDosDateTime(const FILETIME *lpFileTime, LPWORD lpFatDate, LPWORD lpFatTime);
DWORD WIN_FUNC GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation);
} // namespace kernel32

575
dll/kernel32/winbase.cpp Normal file
View File

@ -0,0 +1,575 @@
#include "winbase.h"
#include "errors.h"
#include "files.h"
#include "internal.h"
#include "strutil.h"
#include <algorithm>
#include <cassert>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <limits>
#include <mimalloc.h>
#include <string>
#include <sys/mman.h>
#include <sys/statvfs.h>
#include <system_error>
namespace {
constexpr UINT GMEM_MOVEABLE = 0x0002;
constexpr UINT GMEM_ZEROINIT = 0x0040;
constexpr UINT GMEM_MODIFY = 0x0080;
constexpr UINT LMEM_MOVEABLE = 0x0002;
constexpr UINT LMEM_ZEROINIT = 0x0040;
void *doAlloc(UINT dwBytes, bool zero) {
if (dwBytes == 0) {
dwBytes = 1;
}
void *ret = mi_malloc_aligned(dwBytes, 8);
if (ret && zero) {
std::memset(ret, 0, mi_usable_size(ret));
}
return ret;
}
void *doRealloc(void *mem, UINT dwBytes, bool zero) {
if (dwBytes == 0) {
dwBytes = 1;
}
size_t oldSize = mi_usable_size(mem);
void *ret = mi_realloc_aligned(mem, dwBytes, 8);
size_t newSize = mi_usable_size(ret);
if (ret && zero && newSize > oldSize) {
std::memset(static_cast<char *>(ret) + oldSize, 0, newSize - oldSize);
}
return ret;
}
bool tryGetCurrentDirectoryPath(std::string &outPath) {
std::error_code ec;
std::filesystem::path cwd = std::filesystem::current_path(ec);
if (ec) {
errno = ec.value();
kernel32::setLastErrorFromErrno();
return false;
}
outPath = files::pathToWindows(cwd);
return true;
}
bool computeLongWindowsPath(const std::string &inputPath, std::string &longPath) {
bool hasTrailingSlash = false;
if (!inputPath.empty()) {
char last = inputPath.back();
hasTrailingSlash = (last == '\\' || last == '/');
}
auto hostPath = files::pathFromWindows(inputPath.c_str());
if (hostPath.empty()) {
wibo::lastError = ERROR_PATH_NOT_FOUND;
return false;
}
std::error_code ec;
if (!std::filesystem::exists(hostPath, ec)) {
wibo::lastError = ERROR_FILE_NOT_FOUND;
return false;
}
longPath = files::pathToWindows(hostPath);
if (hasTrailingSlash && !longPath.empty() && longPath.back() != '\\') {
longPath.push_back('\\');
}
wibo::lastError = ERROR_SUCCESS;
return true;
}
bool resolveDiskFreeSpaceStat(const char *rootPathName, struct statvfs &outBuf, std::string &resolvedPath) {
std::filesystem::path hostPath;
if (rootPathName && *rootPathName) {
hostPath = files::pathFromWindows(rootPathName);
} else {
std::error_code ec;
hostPath = std::filesystem::current_path(ec);
if (ec) {
wibo::lastError = ERROR_PATH_NOT_FOUND;
return false;
}
}
if (hostPath.empty()) {
wibo::lastError = ERROR_PATH_NOT_FOUND;
return false;
}
hostPath = hostPath.lexically_normal();
if (hostPath.empty()) {
hostPath = std::filesystem::path("/");
}
std::error_code ec;
if (!hostPath.is_absolute()) {
auto abs = std::filesystem::absolute(hostPath, ec);
if (ec) {
wibo::lastError = ERROR_PATH_NOT_FOUND;
return false;
}
hostPath = abs;
}
std::filesystem::path queryPath = hostPath;
while (true) {
std::string query = queryPath.empty() ? std::string("/") : queryPath.string();
if (query.empty()) {
query = "/";
}
if (statvfs(query.c_str(), &outBuf) == 0) {
resolvedPath = query;
wibo::lastError = ERROR_SUCCESS;
return true;
}
int savedErrno = errno;
if (savedErrno != ENOENT && savedErrno != ENOTDIR) {
errno = savedErrno;
kernel32::setLastErrorFromErrno();
return false;
}
std::filesystem::path parent = queryPath.parent_path();
if (parent == queryPath) {
errno = savedErrno;
kernel32::setLastErrorFromErrno();
return false;
}
if (parent.empty()) {
parent = std::filesystem::path("/");
}
queryPath = parent;
}
}
} // namespace
namespace kernel32 {
void tryMarkExecutable(void *mem) {
if (!mem) {
return;
}
size_t usable = mi_usable_size(mem);
if (usable == 0) {
return;
}
long pageSize = sysconf(_SC_PAGESIZE);
if (pageSize <= 0) {
return;
}
uintptr_t start = reinterpret_cast<uintptr_t>(mem);
uintptr_t alignedStart = start & ~static_cast<uintptr_t>(pageSize - 1);
uintptr_t end = (start + usable + pageSize - 1) & ~static_cast<uintptr_t>(pageSize - 1);
size_t length = static_cast<size_t>(end - alignedStart);
if (length == 0) {
return;
}
mprotect(reinterpret_cast<void *>(alignedStart), length, PROT_READ | PROT_WRITE | PROT_EXEC);
}
BOOL WIN_FUNC IsBadReadPtr(LPCVOID lp, UINT_PTR ucb) {
DEBUG_LOG("STUB: IsBadReadPtr(ptr=%p, size=%zu)\n", lp, static_cast<size_t>(ucb));
if (!lp) {
return TRUE;
}
return FALSE;
}
BOOL WIN_FUNC IsBadWritePtr(LPVOID lp, UINT_PTR ucb) {
DEBUG_LOG("STUB: IsBadWritePtr(ptr=%p, size=%zu)\n", lp, static_cast<size_t>(ucb));
if (!lp && ucb != 0) {
return TRUE;
}
return FALSE;
}
HGLOBAL WIN_FUNC GlobalAlloc(UINT uFlags, SIZE_T dwBytes) {
VERBOSE_LOG("GlobalAlloc(%x, %zu)\n", uFlags, static_cast<size_t>(dwBytes));
if (uFlags & GMEM_MOVEABLE) {
// not implemented rn
assert(0);
return nullptr;
}
bool zero = (uFlags & GMEM_ZEROINIT) != 0;
return doAlloc(static_cast<UINT>(dwBytes), zero);
}
HGLOBAL WIN_FUNC GlobalFree(HGLOBAL hMem) {
VERBOSE_LOG("GlobalFree(%p)\n", hMem);
std::free(hMem);
return nullptr;
}
HGLOBAL WIN_FUNC GlobalReAlloc(HGLOBAL hMem, SIZE_T dwBytes, UINT uFlags) {
VERBOSE_LOG("GlobalReAlloc(%p, %zu, %x)\n", hMem, static_cast<size_t>(dwBytes), uFlags);
if (uFlags & GMEM_MODIFY) {
assert(0);
return nullptr;
}
bool zero = (uFlags & GMEM_ZEROINIT) != 0;
return doRealloc(hMem, static_cast<UINT>(dwBytes), zero);
}
UINT WIN_FUNC GlobalFlags(HGLOBAL hMem) {
VERBOSE_LOG("GlobalFlags(%p)\n", hMem);
(void)hMem;
return 0;
}
HLOCAL WIN_FUNC LocalAlloc(UINT uFlags, SIZE_T uBytes) {
VERBOSE_LOG("LocalAlloc(%x, %zu)\n", uFlags, static_cast<size_t>(uBytes));
bool zero = (uFlags & LMEM_ZEROINIT) != 0;
if ((uFlags & LMEM_MOVEABLE) != 0) {
DEBUG_LOG(" ignoring LMEM_MOVEABLE\n");
}
void *result = doAlloc(static_cast<UINT>(uBytes), zero);
if (!result) {
wibo::lastError = ERROR_NOT_SUPPORTED;
return nullptr;
}
// Legacy Windows applications (pre-NX and DEP) may expect executable memory from LocalAlloc.
tryMarkExecutable(result);
DEBUG_LOG(" -> %p\n", result);
wibo::lastError = ERROR_SUCCESS;
return result;
}
HLOCAL WIN_FUNC LocalFree(HLOCAL hMem) {
VERBOSE_LOG("LocalFree(%p)\n", hMem);
// Windows returns NULL on success.
std::free(hMem);
wibo::lastError = ERROR_SUCCESS;
return nullptr;
}
HLOCAL WIN_FUNC LocalReAlloc(HLOCAL hMem, SIZE_T uBytes, UINT uFlags) {
VERBOSE_LOG("LocalReAlloc(%p, %zu, %x)\n", hMem, static_cast<size_t>(uBytes), uFlags);
bool zero = (uFlags & LMEM_ZEROINIT) != 0;
if ((uFlags & LMEM_MOVEABLE) != 0) {
DEBUG_LOG(" ignoring LMEM_MOVEABLE\n");
}
void *result = doRealloc(hMem, static_cast<UINT>(uBytes), zero);
if (!result && uBytes != 0) {
wibo::lastError = ERROR_NOT_SUPPORTED;
return nullptr;
}
// Legacy Windows applications (pre-NX and DEP) may expect executable memory from LocalReAlloc.
tryMarkExecutable(result);
DEBUG_LOG(" -> %p\n", result);
wibo::lastError = ERROR_SUCCESS;
return result;
}
HLOCAL WIN_FUNC LocalHandle(LPCVOID pMem) {
VERBOSE_LOG("LocalHandle(%p)\n", pMem);
return const_cast<LPVOID>(pMem);
}
LPVOID WIN_FUNC LocalLock(HLOCAL hMem) {
VERBOSE_LOG("LocalLock(%p)\n", hMem);
wibo::lastError = ERROR_SUCCESS;
return hMem;
}
BOOL WIN_FUNC LocalUnlock(HLOCAL hMem) {
VERBOSE_LOG("LocalUnlock(%p)\n", hMem);
(void)hMem;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
SIZE_T WIN_FUNC LocalSize(HLOCAL hMem) {
VERBOSE_LOG("LocalSize(%p)\n", hMem);
return hMem ? mi_usable_size(hMem) : 0;
}
UINT WIN_FUNC LocalFlags(HLOCAL hMem) {
VERBOSE_LOG("LocalFlags(%p)\n", hMem);
(void)hMem;
return 0;
}
UINT WIN_FUNC GetSystemDirectoryA(LPSTR lpBuffer, UINT uSize) {
DEBUG_LOG("GetSystemDirectoryA(%p, %u)\n", lpBuffer, uSize);
if (!lpBuffer) {
return 0;
}
const char *systemDir = "C:\\Windows\\System32";
const auto len = std::strlen(systemDir);
if (uSize < len + 1) {
return static_cast<UINT>(len + 1);
}
std::strcpy(lpBuffer, systemDir);
return static_cast<UINT>(len);
}
UINT WIN_FUNC GetWindowsDirectoryA(LPSTR lpBuffer, UINT uSize) {
DEBUG_LOG("GetWindowsDirectoryA(%p, %u)\n", lpBuffer, uSize);
if (!lpBuffer) {
return 0;
}
const char *windowsDir = "C:\\Windows";
const auto len = std::strlen(windowsDir);
if (uSize < len + 1) {
return static_cast<UINT>(len + 1);
}
std::strcpy(lpBuffer, windowsDir);
return static_cast<UINT>(len);
}
DWORD WIN_FUNC GetCurrentDirectoryA(DWORD nBufferLength, LPSTR lpBuffer) {
DEBUG_LOG("GetCurrentDirectoryA(%u, %p)\n", nBufferLength, lpBuffer);
std::string path;
if (!tryGetCurrentDirectoryPath(path)) {
return 0;
}
const DWORD required = static_cast<DWORD>(path.size() + 1);
if (nBufferLength == 0) {
return required;
}
if (!lpBuffer) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
if (nBufferLength < required) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return required;
}
std::memcpy(lpBuffer, path.c_str(), required);
wibo::lastError = ERROR_SUCCESS;
return required - 1;
}
DWORD WIN_FUNC GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer) {
DEBUG_LOG("GetCurrentDirectoryW(%u, %p)\n", nBufferLength, lpBuffer);
std::string path;
if (!tryGetCurrentDirectoryPath(path)) {
return 0;
}
auto widePath = stringToWideString(path.c_str());
const DWORD required = static_cast<DWORD>(widePath.size());
if (nBufferLength == 0) {
return required;
}
if (!lpBuffer) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
if (nBufferLength < required) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return required;
}
std::copy(widePath.begin(), widePath.end(), lpBuffer);
wibo::lastError = ERROR_SUCCESS;
return required - 1;
}
int WIN_FUNC SetCurrentDirectoryA(LPCSTR lpPathName) {
DEBUG_LOG("SetCurrentDirectoryA(%s)\n", lpPathName ? lpPathName : "(null)");
if (!lpPathName) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
auto hostPath = files::pathFromWindows(lpPathName);
std::error_code ec;
std::filesystem::current_path(hostPath, ec);
if (ec) {
errno = ec.value();
kernel32::setLastErrorFromErrno();
return 0;
}
wibo::lastError = ERROR_SUCCESS;
return 1;
}
int WIN_FUNC SetCurrentDirectoryW(LPCWSTR lpPathName) {
DEBUG_LOG("SetCurrentDirectoryW\n");
if (!lpPathName) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
std::string path = wideStringToString(lpPathName);
return SetCurrentDirectoryA(path.c_str());
}
DWORD WIN_FUNC GetLongPathNameA(LPCSTR lpszShortPath, LPSTR lpszLongPath, DWORD cchBuffer) {
DEBUG_LOG("GetLongPathNameA(%s, %p, %u)\n", lpszShortPath ? lpszShortPath : "(null)", lpszLongPath, cchBuffer);
if (!lpszShortPath) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
std::string input(lpszShortPath);
std::string longPath;
if (!computeLongWindowsPath(input, longPath)) {
return 0;
}
DWORD required = static_cast<DWORD>(longPath.size() + 1);
if (cchBuffer == 0) {
return required;
}
if (!lpszLongPath) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
if (cchBuffer < required) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return required;
}
std::memcpy(lpszLongPath, longPath.c_str(), required);
wibo::lastError = ERROR_SUCCESS;
return required - 1;
}
DWORD WIN_FUNC GetLongPathNameW(LPCWSTR lpszShortPath, LPWSTR lpszLongPath, DWORD cchBuffer) {
DEBUG_LOG("GetLongPathNameW(%p, %p, %u)\n", lpszShortPath, lpszLongPath, cchBuffer);
if (!lpszShortPath) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
std::string input = wideStringToString(lpszShortPath);
std::string longPath;
if (!computeLongWindowsPath(input, longPath)) {
return 0;
}
auto wideLong = stringToWideString(longPath.c_str());
DWORD required = static_cast<DWORD>(wideLong.size());
if (cchBuffer == 0) {
return required;
}
if (!lpszLongPath) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
if (cchBuffer < required) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return required;
}
std::copy(wideLong.begin(), wideLong.end(), lpszLongPath);
wibo::lastError = ERROR_SUCCESS;
return required - 1;
}
BOOL WIN_FUNC GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector,
LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters) {
DEBUG_LOG("GetDiskFreeSpaceA(%s)\n", lpRootPathName ? lpRootPathName : "(null)");
struct statvfs buf{};
std::string resolvedPath;
if (!resolveDiskFreeSpaceStat(lpRootPathName, buf, resolvedPath)) {
return FALSE;
}
uint64_t blockSize = buf.f_frsize ? buf.f_frsize : buf.f_bsize;
if (blockSize == 0) {
blockSize = 4096;
}
unsigned int bytesPerSector = 512;
if (blockSize % bytesPerSector != 0) {
bytesPerSector =
static_cast<unsigned int>(std::min<uint64_t>(blockSize, std::numeric_limits<unsigned int>::max()));
}
unsigned int sectorsPerCluster = static_cast<unsigned int>(blockSize / bytesPerSector);
if (sectorsPerCluster == 0) {
sectorsPerCluster = 1;
bytesPerSector =
static_cast<unsigned int>(std::min<uint64_t>(blockSize, std::numeric_limits<unsigned int>::max()));
}
uint64_t totalClusters64 = buf.f_blocks;
uint64_t freeClusters64 = buf.f_bavail;
if (lpSectorsPerCluster) {
*lpSectorsPerCluster = sectorsPerCluster;
}
if (lpBytesPerSector) {
*lpBytesPerSector = bytesPerSector;
}
if (lpNumberOfFreeClusters) {
uint64_t clamped = std::min<uint64_t>(freeClusters64, std::numeric_limits<unsigned int>::max());
*lpNumberOfFreeClusters = static_cast<DWORD>(clamped);
}
if (lpTotalNumberOfClusters) {
uint64_t clamped = std::min<uint64_t>(totalClusters64, std::numeric_limits<unsigned int>::max());
*lpTotalNumberOfClusters = static_cast<DWORD>(clamped);
}
DEBUG_LOG("\t-> host %s, spc %u, bps %u, free clusters %u, total clusters %u\n", resolvedPath.c_str(),
lpSectorsPerCluster ? *lpSectorsPerCluster : 0, lpBytesPerSector ? *lpBytesPerSector : 0,
lpNumberOfFreeClusters ? *lpNumberOfFreeClusters : 0,
lpTotalNumberOfClusters ? *lpTotalNumberOfClusters : 0);
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC GetDiskFreeSpaceW(LPCWSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector,
LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters) {
std::string rootPath = wideStringToString(lpRootPathName);
return GetDiskFreeSpaceA(lpRootPathName ? rootPath.c_str() : nullptr, lpSectorsPerCluster, lpBytesPerSector,
lpNumberOfFreeClusters, lpTotalNumberOfClusters);
}
BOOL WIN_FUNC GetDiskFreeSpaceExA(LPCSTR lpDirectoryName, uint64_t *lpFreeBytesAvailableToCaller,
uint64_t *lpTotalNumberOfBytes, uint64_t *lpTotalNumberOfFreeBytes) {
DEBUG_LOG("GetDiskFreeSpaceExA(%s)\n", lpDirectoryName ? lpDirectoryName : "(null)");
struct statvfs buf{};
std::string resolvedPath;
if (!resolveDiskFreeSpaceStat(lpDirectoryName, buf, resolvedPath)) {
return FALSE;
}
uint64_t blockSize = buf.f_frsize ? buf.f_frsize : buf.f_bsize;
if (blockSize == 0) {
blockSize = 4096;
}
uint64_t freeToCaller = static_cast<uint64_t>(buf.f_bavail) * blockSize;
uint64_t totalBytes = static_cast<uint64_t>(buf.f_blocks) * blockSize;
uint64_t totalFree = static_cast<uint64_t>(buf.f_bfree) * blockSize;
if (lpFreeBytesAvailableToCaller) {
*lpFreeBytesAvailableToCaller = freeToCaller;
}
if (lpTotalNumberOfBytes) {
*lpTotalNumberOfBytes = totalBytes;
}
if (lpTotalNumberOfFreeBytes) {
*lpTotalNumberOfFreeBytes = totalFree;
}
DEBUG_LOG("\t-> host %s, free %llu, total %llu, total free %llu\n", resolvedPath.c_str(),
static_cast<unsigned long long>(freeToCaller), static_cast<unsigned long long>(totalBytes),
static_cast<unsigned long long>(totalFree));
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC GetDiskFreeSpaceExW(LPCWSTR lpDirectoryName, uint64_t *lpFreeBytesAvailableToCaller,
uint64_t *lpTotalNumberOfBytes, uint64_t *lpTotalNumberOfFreeBytes) {
DEBUG_LOG("GetDiskFreeSpaceExW -> ");
std::string directoryName = wideStringToString(lpDirectoryName);
return GetDiskFreeSpaceExA(lpDirectoryName ? directoryName.c_str() : nullptr, lpFreeBytesAvailableToCaller,
lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes);
}
} // namespace kernel32

41
dll/kernel32/winbase.h Normal file
View File

@ -0,0 +1,41 @@
#pragma once
#include "common.h"
namespace kernel32 {
BOOL WIN_FUNC IsBadReadPtr(LPCVOID lp, UINT_PTR ucb);
BOOL WIN_FUNC IsBadWritePtr(LPVOID lp, UINT_PTR ucb);
HGLOBAL WIN_FUNC GlobalAlloc(UINT uFlags, SIZE_T dwBytes);
HGLOBAL WIN_FUNC GlobalFree(HGLOBAL hMem);
HGLOBAL WIN_FUNC GlobalReAlloc(HGLOBAL hMem, SIZE_T dwBytes, UINT uFlags);
UINT WIN_FUNC GlobalFlags(HGLOBAL hMem);
HLOCAL WIN_FUNC LocalAlloc(UINT uFlags, SIZE_T uBytes);
HLOCAL WIN_FUNC LocalFree(HLOCAL hMem);
HLOCAL WIN_FUNC LocalReAlloc(HLOCAL hMem, SIZE_T uBytes, UINT uFlags);
HLOCAL WIN_FUNC LocalHandle(LPCVOID pMem);
LPVOID WIN_FUNC LocalLock(HLOCAL hMem);
BOOL WIN_FUNC LocalUnlock(HLOCAL hMem);
SIZE_T WIN_FUNC LocalSize(HLOCAL hMem);
UINT WIN_FUNC LocalFlags(HLOCAL hMem);
UINT WIN_FUNC GetSystemDirectoryA(LPSTR lpBuffer, UINT uSize);
UINT WIN_FUNC GetWindowsDirectoryA(LPSTR lpBuffer, UINT uSize);
DWORD WIN_FUNC GetCurrentDirectoryA(DWORD nBufferLength, LPSTR lpBuffer);
DWORD WIN_FUNC GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer);
int WIN_FUNC SetCurrentDirectoryA(LPCSTR lpPathName);
int WIN_FUNC SetCurrentDirectoryW(LPCWSTR lpPathName);
DWORD WIN_FUNC GetLongPathNameA(LPCSTR lpszShortPath, LPSTR lpszLongPath, DWORD cchBuffer);
DWORD WIN_FUNC GetLongPathNameW(LPCWSTR lpszShortPath, LPWSTR lpszLongPath, DWORD cchBuffer);
BOOL WIN_FUNC GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector,
LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters);
BOOL WIN_FUNC GetDiskFreeSpaceW(LPCWSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector,
LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters);
BOOL WIN_FUNC GetDiskFreeSpaceExA(LPCSTR lpDirectoryName, uint64_t *lpFreeBytesAvailableToCaller,
uint64_t *lpTotalNumberOfBytes, uint64_t *lpTotalNumberOfFreeBytes);
BOOL WIN_FUNC GetDiskFreeSpaceExW(LPCWSTR lpDirectoryName, uint64_t *lpFreeBytesAvailableToCaller,
uint64_t *lpTotalNumberOfBytes, uint64_t *lpTotalNumberOfFreeBytes);
} // namespace kernel32

112
dll/kernel32/wincon.cpp Normal file
View File

@ -0,0 +1,112 @@
#include "wincon.h"
#include "errors.h"
#include "files.h"
#include "strutil.h"
#include <cstdio>
namespace kernel32 {
BOOL WIN_FUNC GetConsoleMode(HANDLE hConsoleHandle, LPDWORD lpMode) {
DEBUG_LOG("STUB: GetConsoleMode(%p)\n", hConsoleHandle);
if (lpMode) {
*lpMode = 0;
}
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC SetConsoleMode(HANDLE hConsoleHandle, DWORD dwMode) {
DEBUG_LOG("STUB: SetConsoleMode(%p, 0x%x)\n", hConsoleHandle, dwMode);
(void)hConsoleHandle;
(void)dwMode;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
UINT WIN_FUNC GetConsoleOutputCP() {
DEBUG_LOG("STUB: GetConsoleOutputCP() -> 65001\n");
wibo::lastError = ERROR_SUCCESS;
return 65001; // UTF-8
}
BOOL WIN_FUNC SetConsoleCtrlHandler(PHANDLER_ROUTINE HandlerRoutine, BOOL Add) {
DEBUG_LOG("STUB: SetConsoleCtrlHandler(%p, %u)\n", reinterpret_cast<const void *>(HandlerRoutine), Add);
(void)HandlerRoutine;
(void)Add;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC GetConsoleScreenBufferInfo(HANDLE hConsoleOutput, CONSOLE_SCREEN_BUFFER_INFO *lpConsoleScreenBufferInfo) {
DEBUG_LOG("STUB: GetConsoleScreenBufferInfo(%p, %p)\n", hConsoleOutput, lpConsoleScreenBufferInfo);
(void)hConsoleOutput;
if (!lpConsoleScreenBufferInfo) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
lpConsoleScreenBufferInfo->dwSize = {80, 25};
lpConsoleScreenBufferInfo->dwCursorPosition = {0, 0};
lpConsoleScreenBufferInfo->wAttributes = 0;
lpConsoleScreenBufferInfo->srWindow = {0, 0, 79, 24};
lpConsoleScreenBufferInfo->dwMaximumWindowSize = {80, 25};
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC WriteConsoleW(HANDLE hConsoleOutput, LPCVOID lpBuffer, DWORD nNumberOfCharsToWrite,
LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved) {
DEBUG_LOG("WriteConsoleW(%p, %p, %u, %p, %p)\n", hConsoleOutput, lpBuffer, nNumberOfCharsToWrite,
lpNumberOfCharsWritten, lpReserved);
(void)lpReserved;
if (lpNumberOfCharsWritten) {
*lpNumberOfCharsWritten = 0;
}
if (!lpBuffer && nNumberOfCharsToWrite != 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
FILE *fp = files::fpFromHandle(hConsoleOutput);
if (fp == stdout || fp == stderr) {
auto str = wideStringToString(static_cast<const uint16_t *>(lpBuffer), nNumberOfCharsToWrite);
fprintf(fp, "%s", str.c_str());
if (lpNumberOfCharsWritten) {
*lpNumberOfCharsWritten = nNumberOfCharsToWrite;
}
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
wibo::lastError = ERROR_INVALID_HANDLE;
return FALSE;
}
BOOL WIN_FUNC PeekConsoleInputA(HANDLE hConsoleInput, INPUT_RECORD *lpBuffer, DWORD nLength,
LPDWORD lpNumberOfEventsRead) {
DEBUG_LOG("STUB: PeekConsoleInputA(%p, %p, %u)\n", hConsoleInput, lpBuffer, nLength);
(void)hConsoleInput;
(void)lpBuffer;
(void)nLength;
if (lpNumberOfEventsRead) {
*lpNumberOfEventsRead = 0;
}
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC ReadConsoleInputA(HANDLE hConsoleInput, INPUT_RECORD *lpBuffer, DWORD nLength,
LPDWORD lpNumberOfEventsRead) {
DEBUG_LOG("STUB: ReadConsoleInputA(%p, %p, %u)\n", hConsoleInput, lpBuffer, nLength);
(void)hConsoleInput;
(void)lpBuffer;
(void)nLength;
if (lpNumberOfEventsRead) {
*lpNumberOfEventsRead = 0;
}
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
} // namespace kernel32

43
dll/kernel32/wincon.h Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include "common.h"
struct COORD {
SHORT X;
SHORT Y;
};
struct SMALL_RECT {
SHORT Left;
SHORT Top;
SHORT Right;
SHORT Bottom;
};
struct CONSOLE_SCREEN_BUFFER_INFO {
COORD dwSize;
COORD dwCursorPosition;
WORD wAttributes;
SMALL_RECT srWindow;
COORD dwMaximumWindowSize;
};
struct INPUT_RECORD;
using PHANDLER_ROUTINE = BOOL(WIN_FUNC *)(DWORD CtrlType);
namespace kernel32 {
BOOL WIN_FUNC GetConsoleMode(HANDLE hConsoleHandle, LPDWORD lpMode);
BOOL WIN_FUNC SetConsoleMode(HANDLE hConsoleHandle, DWORD dwMode);
UINT WIN_FUNC GetConsoleOutputCP();
BOOL WIN_FUNC SetConsoleCtrlHandler(PHANDLER_ROUTINE HandlerRoutine, BOOL Add);
BOOL WIN_FUNC GetConsoleScreenBufferInfo(HANDLE hConsoleOutput, CONSOLE_SCREEN_BUFFER_INFO *lpConsoleScreenBufferInfo);
BOOL WIN_FUNC WriteConsoleW(HANDLE hConsoleOutput, LPCVOID lpBuffer, DWORD nNumberOfCharsToWrite,
LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved);
BOOL WIN_FUNC PeekConsoleInputA(HANDLE hConsoleInput, INPUT_RECORD *lpBuffer, DWORD nLength,
LPDWORD lpNumberOfEventsRead);
BOOL WIN_FUNC ReadConsoleInputA(HANDLE hConsoleInput, INPUT_RECORD *lpBuffer, DWORD nLength,
LPDWORD lpNumberOfEventsRead);
} // namespace kernel32

366
dll/kernel32/winnls.cpp Normal file
View File

@ -0,0 +1,366 @@
#include "winnls.h"
#include "errors.h"
#include "strutil.h"
#include <algorithm>
#include <cstring>
#include <cwctype>
#include <initializer_list>
#include <string>
#include <vector>
namespace {
constexpr DWORD kNormIgnoreCase = 0x00000001;
constexpr DWORD LCID_INSTALLED = 0x00000001;
constexpr DWORD LCID_SUPPORTED = 0x00000002;
constexpr DWORD LCID_ALTERNATE_SORTS = 0x00000004;
int compareStrings(const std::string &a, const std::string &b, DWORD dwCmpFlags) {
for (size_t i = 0;; ++i) {
if (i == a.size()) {
if (i == b.size()) {
return 2; // CSTR_EQUAL
}
return 1; // CSTR_LESS_THAN
}
if (i == b.size()) {
return 3; // CSTR_GREATER_THAN
}
unsigned char c = static_cast<unsigned char>(a[i]);
unsigned char d = static_cast<unsigned char>(b[i]);
if (dwCmpFlags & kNormIgnoreCase) {
if (c >= 'a' && c <= 'z') {
c = static_cast<unsigned char>(c - ('a' - 'A'));
}
if (d >= 'a' && d <= 'z') {
d = static_cast<unsigned char>(d - ('a' - 'A'));
}
}
if (c != d) {
return (c < d) ? 1 : 3;
}
}
}
std::string localeInfoString(int LCType) {
switch (LCType) {
case 4100: // LOCALE_IDEFAULTANSICODEPAGE
return "28591";
case 4097: // LOCALE_SENGLANGUAGE
return "Lang";
case 4098: // LOCALE_SENGCOUNTRY
return "Country";
case 0x1: // LOCALE_ILANGUAGE
return "0001";
case 0x15: // LOCALE_SINTLSYMBOL
return "Currency";
case 0x14: // LOCALE_SCURRENCY
return "sCurrency";
case 0x16: // LOCALE_SMONDECIMALSEP
return ".";
case 0x17: // LOCALE_SMONTHOUSANDSEP
return ",";
case 0x18: // LOCALE_SMONGROUPING
return ";";
case 0x50: // LOCALE_SPOSITIVESIGN
return "";
case 0x51: // LOCALE_SNEGATIVESIGN
return "-";
case 0x1A: // LOCALE_IINTLCURRDIGITS
case 0x19: // LOCALE_ICURRDIGITS
return "2";
default:
DEBUG_LOG("STUB: GetLocaleInfo LCType 0x%x not implemented\n", LCType);
return "";
}
}
} // namespace
namespace kernel32 {
UINT WIN_FUNC GetACP() {
DEBUG_LOG("GetACP() -> %u\n", 28591);
wibo::lastError = ERROR_SUCCESS;
return 28591; // Latin1 (ISO/IEC 8859-1)
}
LANGID WIN_FUNC GetSystemDefaultLangID() {
DEBUG_LOG("STUB: GetSystemDefaultLangID()\n");
return 0;
}
LANGID WIN_FUNC GetUserDefaultUILanguage() {
DEBUG_LOG("STUB: GetUserDefaultUILanguage()\n");
return 0;
}
BOOL WIN_FUNC GetCPInfo(UINT CodePage, LPCPINFO lpCPInfo) {
DEBUG_LOG("GetCPInfo(%u, %p)\n", CodePage, lpCPInfo);
(void)CodePage;
if (!lpCPInfo) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
lpCPInfo->MaxCharSize = 1;
std::fill(lpCPInfo->DefaultChar, lpCPInfo->DefaultChar + MAX_DEFAULTCHAR, 0);
lpCPInfo->DefaultChar[0] = '?';
std::fill(lpCPInfo->LeadByte, lpCPInfo->LeadByte + MAX_LEADBYTES, 0);
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
int WIN_FUNC CompareStringA(LCID Locale, DWORD dwCmpFlags, LPCSTR lpString1, int cchCount1, LPCSTR lpString2,
int cchCount2) {
DEBUG_LOG("CompareStringA(%u, %u, %s, %d, %s, %d)\n", Locale, dwCmpFlags, lpString1 ? lpString1 : "(null)",
cchCount1, lpString2 ? lpString2 : "(null)", cchCount2);
(void)Locale;
if (!lpString1 || !lpString2) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
if (cchCount1 < 0) {
cchCount1 = static_cast<int>(strlen(lpString1));
}
if (cchCount2 < 0) {
cchCount2 = static_cast<int>(strlen(lpString2));
}
std::string str1(lpString1, lpString1 + cchCount1);
std::string str2(lpString2, lpString2 + cchCount2);
wibo::lastError = ERROR_SUCCESS;
return compareStrings(str1, str2, dwCmpFlags);
}
int WIN_FUNC CompareStringW(LCID Locale, DWORD dwCmpFlags, LPCWCH lpString1, int cchCount1, LPCWCH lpString2,
int cchCount2) {
DEBUG_LOG("CompareStringW(%u, %u, %p, %d, %p, %d)\n", Locale, dwCmpFlags, lpString1, cchCount1, lpString2,
cchCount2);
(void)Locale;
if (!lpString1 || !lpString2) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
std::string str1 = wideStringToString(lpString1, cchCount1);
std::string str2 = wideStringToString(lpString2, cchCount2);
wibo::lastError = ERROR_SUCCESS;
return compareStrings(str1, str2, dwCmpFlags);
}
BOOL WIN_FUNC IsValidCodePage(UINT CodePage) {
DEBUG_LOG("IsValidCodePage(%u)\n", CodePage);
(void)CodePage;
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
BOOL WIN_FUNC IsValidLocale(LCID Locale, DWORD dwFlags) {
DEBUG_LOG("IsValidLocale(%u, 0x%x)\n", Locale, dwFlags);
(void)Locale;
if (dwFlags != 0 && (dwFlags & ~(LCID_INSTALLED | LCID_SUPPORTED | LCID_ALTERNATE_SORTS)) != 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
wibo::lastError = ERROR_SUCCESS;
return TRUE;
}
int WIN_FUNC GetLocaleInfoA(LCID Locale, LCTYPE LCType, LPSTR lpLCData, int cchData) {
DEBUG_LOG("GetLocaleInfoA(%u, %u, %p, %d)\n", Locale, LCType, lpLCData, cchData);
(void)Locale;
std::string value = localeInfoString(static_cast<int>(LCType));
size_t required = value.size() + 1;
if (cchData == 0) {
wibo::lastError = ERROR_SUCCESS;
return static_cast<int>(required);
}
if (!lpLCData || cchData < 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
if (static_cast<size_t>(cchData) < required) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return 0;
}
std::memcpy(lpLCData, value.c_str(), required);
wibo::lastError = ERROR_SUCCESS;
return static_cast<int>(required);
}
int WIN_FUNC GetLocaleInfoW(LCID Locale, LCTYPE LCType, LPWSTR lpLCData, int cchData) {
DEBUG_LOG("GetLocaleInfoW(%u, %u, %p, %d)\n", Locale, LCType, lpLCData, cchData);
(void)Locale;
std::string info = localeInfoString(static_cast<int>(LCType));
auto wide = stringToWideString(info.c_str());
size_t required = wide.size();
if (cchData == 0) {
wibo::lastError = ERROR_SUCCESS;
return static_cast<int>(required);
}
if (!lpLCData || cchData < 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
if (static_cast<size_t>(cchData) < required) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return 0;
}
std::memcpy(lpLCData, wide.data(), required * sizeof(uint16_t));
wibo::lastError = ERROR_SUCCESS;
return static_cast<int>(required);
}
BOOL WIN_FUNC EnumSystemLocalesA(LOCALE_ENUMPROCA lpLocaleEnumProc, DWORD dwFlags) {
DEBUG_LOG("EnumSystemLocalesA(%p, 0x%x)\n", lpLocaleEnumProc, dwFlags);
(void)dwFlags;
if (!lpLocaleEnumProc) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
char localeId[] = "00000409"; // en-US
BOOL callbackResult = lpLocaleEnumProc(localeId);
wibo::lastError = ERROR_SUCCESS;
return callbackResult;
}
LCID WIN_FUNC GetUserDefaultLCID() {
DEBUG_LOG("GetUserDefaultLCID()\n");
wibo::lastError = ERROR_SUCCESS;
return 0x0409; // en-US
}
BOOL WIN_FUNC IsDBCSLeadByte(BYTE TestChar) {
DEBUG_LOG("IsDBCSLeadByte(%u)\n", TestChar);
(void)TestChar;
wibo::lastError = ERROR_SUCCESS;
return FALSE;
}
BOOL WIN_FUNC IsDBCSLeadByteEx(UINT CodePage, BYTE TestChar) {
DEBUG_LOG("IsDBCSLeadByteEx(%u, %u)\n", CodePage, TestChar);
auto inRanges = [TestChar](std::initializer_list<std::pair<uint8_t, uint8_t>> ranges) -> BOOL {
for (const auto &range : ranges) {
if (TestChar >= range.first && TestChar <= range.second) {
return TRUE;
}
}
return FALSE;
};
switch (CodePage) {
case 932: // Shift-JIS
wibo::lastError = ERROR_SUCCESS;
return inRanges({{0x81, 0x9F}, {0xE0, 0xFC}});
case 936: // GBK
case 949: // Korean
case 950: // Big5
case 1361: // Johab
wibo::lastError = ERROR_SUCCESS;
return inRanges({{0x81, 0xFE}});
case 0: // CP_ACP
case 1: // CP_OEMCP
case 2: // CP_MACCP
case 3: // CP_THREAD_ACP
default:
wibo::lastError = ERROR_SUCCESS;
return FALSE;
}
}
int WIN_FUNC LCMapStringW(LCID Locale, DWORD dwMapFlags, LPCWCH lpSrcStr, int cchSrc, LPWSTR lpDestStr, int cchDest) {
DEBUG_LOG("LCMapStringW(%u, 0x%x, %p, %d, %p, %d)\n", Locale, dwMapFlags, lpSrcStr, cchSrc, lpDestStr, cchDest);
(void)Locale;
if (!lpSrcStr || cchSrc == 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
bool nullTerminated = cchSrc < 0;
size_t srcLen = nullTerminated ? (wstrlen(lpSrcStr) + 1) : static_cast<size_t>(cchSrc);
if (srcLen == 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
if (!lpDestStr || cchDest == 0) {
wibo::lastError = ERROR_SUCCESS;
return static_cast<int>(srcLen);
}
if (cchDest < static_cast<int>(srcLen)) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return 0;
}
if (dwMapFlags & (0x00000400u | 0x00000800u)) { // LCMAP_SORTKEY | LCMAP_BYTEREV
DEBUG_LOG("LCMapStringW: unsupported mapping flags 0x%x\n", dwMapFlags);
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
unsigned int casingFlags = dwMapFlags & (0x00000200u | 0x00000100u); // UPPERCASE | LOWERCASE
std::vector<uint16_t> buffer(srcLen, 0);
for (size_t i = 0; i < srcLen; ++i) {
uint16_t ch = lpSrcStr[i];
if (casingFlags == 0x00000200u) {
buffer[i] = static_cast<uint16_t>(std::towupper(static_cast<wint_t>(ch)));
} else if (casingFlags == 0x00000100u) {
buffer[i] = static_cast<uint16_t>(std::towlower(static_cast<wint_t>(ch)));
} else {
buffer[i] = ch;
}
}
std::memcpy(lpDestStr, buffer.data(), srcLen * sizeof(uint16_t));
wibo::lastError = ERROR_SUCCESS;
return static_cast<int>(srcLen);
}
int WIN_FUNC LCMapStringA(LCID Locale, DWORD dwMapFlags, LPCCH lpSrcStr, int cchSrc, LPSTR lpDestStr, int cchDest) {
DEBUG_LOG("LCMapStringA(%u, 0x%x, %p, %d, %p, %d)\n", Locale, dwMapFlags, lpSrcStr, cchSrc, lpDestStr, cchDest);
if (!lpSrcStr) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return 0;
}
int length = cchSrc;
if (length < 0) {
length = static_cast<int>(strlen(lpSrcStr)) + 1;
}
auto wideSrc = stringToWideString(lpSrcStr, static_cast<size_t>(length));
std::vector<uint16_t> wideDest(std::max(cchDest, 0));
int wideResult =
LCMapStringW(Locale, dwMapFlags, wideSrc.data(), length, wideDest.empty() ? nullptr : wideDest.data(), cchDest);
if (wideResult == 0) {
return 0;
}
if (!lpDestStr || cchDest == 0) {
return wideResult;
}
auto mapped = wideStringToString(wideDest.data(), wideResult);
size_t bytesToCopy = mapped.size() + 1;
if (static_cast<size_t>(cchDest) < bytesToCopy) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return 0;
}
std::memcpy(lpDestStr, mapped.c_str(), bytesToCopy);
wibo::lastError = ERROR_SUCCESS;
return wideResult;
}
} // namespace kernel32

38
dll/kernel32/winnls.h Normal file
View File

@ -0,0 +1,38 @@
#pragma once
#include "common.h"
constexpr UINT MAX_DEFAULTCHAR = 2;
constexpr UINT MAX_LEADBYTES = 12;
struct CPINFO {
UINT MaxCharSize;
BYTE DefaultChar[MAX_DEFAULTCHAR];
BYTE LeadByte[MAX_LEADBYTES];
};
using LPCPINFO = CPINFO *;
using LOCALE_ENUMPROCA = BOOL(WIN_FUNC *)(LPSTR);
namespace kernel32 {
UINT WIN_FUNC GetACP();
LANGID WIN_FUNC GetSystemDefaultLangID();
LANGID WIN_FUNC GetUserDefaultUILanguage();
BOOL WIN_FUNC GetCPInfo(UINT CodePage, LPCPINFO lpCPInfo);
int WIN_FUNC CompareStringA(LCID Locale, DWORD dwCmpFlags, LPCSTR lpString1, int cchCount1, LPCSTR lpString2,
int cchCount2);
int WIN_FUNC CompareStringW(LCID Locale, DWORD dwCmpFlags, LPCWCH lpString1, int cchCount1, LPCWCH lpString2,
int cchCount2);
BOOL WIN_FUNC IsValidCodePage(UINT CodePage);
BOOL WIN_FUNC IsValidLocale(LCID Locale, DWORD dwFlags);
int WIN_FUNC GetLocaleInfoA(LCID Locale, LCTYPE LCType, LPSTR lpLCData, int cchData);
int WIN_FUNC GetLocaleInfoW(LCID Locale, LCTYPE LCType, LPWSTR lpLCData, int cchData);
BOOL WIN_FUNC EnumSystemLocalesA(LOCALE_ENUMPROCA lpLocaleEnumProc, DWORD dwFlags);
LCID WIN_FUNC GetUserDefaultLCID();
BOOL WIN_FUNC IsDBCSLeadByte(BYTE TestChar);
BOOL WIN_FUNC IsDBCSLeadByteEx(UINT CodePage, BYTE TestChar);
int WIN_FUNC LCMapStringW(LCID Locale, DWORD dwMapFlags, LPCWCH lpSrcStr, int cchSrc, LPWSTR lpDestStr, int cchDest);
int WIN_FUNC LCMapStringA(LCID Locale, DWORD dwMapFlags, LPCCH lpSrcStr, int cchSrc, LPSTR lpDestStr, int cchDest);
} // namespace kernel32

View File

@ -1,7 +1,7 @@
#include "wow64apiset.h"
#include "common.h"
#include "errors.h"
#include "handles.h"
#include "kernel32.h"
namespace kernel32 {
@ -28,7 +28,7 @@ BOOL WIN_FUNC IsWow64Process(HANDLE hProcess, PBOOL Wow64Process) {
return FALSE;
}
const auto rawHandle = reinterpret_cast<uintptr_t>(hProcess);
uintptr_t rawHandle = reinterpret_cast<uintptr_t>(hProcess);
bool isPseudoHandle = rawHandle == static_cast<uintptr_t>(-1);
if (!isPseudoHandle) {
if (!hProcess) {

View File

@ -0,0 +1,11 @@
#pragma once
#include "common.h"
namespace kernel32 {
BOOL WIN_FUNC Wow64DisableWow64FsRedirection(PVOID *OldValue);
BOOL WIN_FUNC Wow64RevertWow64FsRedirection(PVOID OldValue);
BOOL WIN_FUNC IsWow64Process(HANDLE hProcess, PBOOL Wow64Process);
} // namespace kernel32

View File

@ -16,6 +16,7 @@
#define ERROR_BROKEN_PIPE 109
#define ERROR_NOT_SUPPORTED 50
#define ERROR_INVALID_PARAMETER 87
#define ERROR_ENVVAR_NOT_FOUND 203
#define ERROR_CALL_NOT_IMPLEMENTED 120
#define ERROR_BUFFER_OVERFLOW 111
#define ERROR_INSUFFICIENT_BUFFER 122

View File

@ -12,10 +12,11 @@
#include <sys/mman.h>
#include <sys/syscall.h>
#include <system_error>
#include <threads.h>
#include <unistd.h>
#include <vector>
uint32_t wibo::lastError = 0;
thread_local uint32_t wibo::lastError = 0;
char **wibo::argv;
int wibo::argc;
std::filesystem::path wibo::guestExecutablePath;
@ -369,7 +370,7 @@ int main(int argc, char **argv) {
// Build a command line
if (cmdLine.empty()) {
for (int i = 0; i < guestArgs.size(); ++i) {
for (size_t i = 0; i < guestArgs.size(); ++i) {
if (i != 0) {
cmdLine += ' ';
}

View File

@ -2,20 +2,20 @@
#include "common.h"
#include <algorithm>
#include <cctype>
#include <cwctype>
#include <cstdint>
#include <cwctype>
#include <sstream>
#include <string>
#include <vector>
void toLowerInPlace(std::string &str) {
std::transform(str.begin(), str.end(), str.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
}
void toUpperInPlace(std::string &str) {
std::transform(str.begin(), str.end(), str.begin(),
[](unsigned char c) { return static_cast<char>(std::toupper(c)); });
[](unsigned char c) { return static_cast<char>(std::toupper(c)); });
}
std::string stringToLower(std::string_view str) {
@ -205,15 +205,16 @@ std::string wideStringToString(const uint16_t *src, int len) {
return result;
}
std::vector<uint16_t> stringToWideString(const char *src) {
if (!src)
std::vector<uint16_t> stringToWideString(const char *src, size_t length) {
if (!src) {
return std::vector<uint16_t>{0};
size_t len = strlen(src);
std::vector<uint16_t> res(len + 1);
for (size_t i = 0; i < res.size(); i++) {
res[i] = static_cast<uint16_t>(src[i] & 0xFF);
}
res[len] = 0; // NUL terminate
size_t len = length == static_cast<size_t>(-1) ? strlen(src) : length;
std::vector<uint16_t> res(len + 1);
for (size_t i = 0; i < len; ++i) {
res[i] = static_cast<uint16_t>(static_cast<unsigned char>(src[i]));
}
res[len] = 0; // ensure NUL termination
return res;
}

View File

@ -16,7 +16,7 @@ uint16_t *wstrncat(uint16_t *dest, const uint16_t *src, size_t count);
uint16_t *wstrcpy(uint16_t *dest, const uint16_t *src);
size_t wstrncpy(uint16_t *dst, const uint16_t *src, size_t n);
std::string wideStringToString(const uint16_t *src, int len = -1);
std::vector<uint16_t> stringToWideString(const char *src);
std::vector<uint16_t> stringToWideString(const char *src, size_t length = static_cast<size_t>(-1));
long wstrtol(const uint16_t *string, uint16_t **end_ptr, int base);
unsigned long wstrtoul(const uint16_t *string, uint16_t **end_ptr, int base);
void toLowerInPlace(std::string &str);