mirror of
https://github.com/decompals/wibo.git
synced 2025-10-15 14:45:12 +00:00
Split kernel32 into separate files (part 2)
This commit is contained in:
parent
9ea5b24b67
commit
fa3ed4893a
@ -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'
|
||||
|
25
AGENTS.md
25
AGENTS.md
@ -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/`.
|
||||
|
@ -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
423
common.h
@ -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
|
||||
|
6966
dll/kernel32.cpp
6966
dll/kernel32.cpp
File diff suppressed because it is too large
Load Diff
13
dll/kernel32/debugapi.cpp
Normal file
13
dll/kernel32/debugapi.cpp
Normal 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
9
dll/kernel32/debugapi.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
namespace kernel32 {
|
||||
|
||||
BOOL WIN_FUNC IsDebuggerPresent();
|
||||
|
||||
} // namespace kernel32
|
@ -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;
|
||||
|
44
dll/kernel32/errhandlingapi.h
Normal file
44
dll/kernel32/errhandlingapi.h
Normal 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
|
@ -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
16
dll/kernel32/fibersapi.h
Normal 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
564
dll/kernel32/fileapi.cpp
Normal 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
22
dll/kernel32/fileapi.h
Normal 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
181
dll/kernel32/handleapi.cpp
Normal 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
11
dll/kernel32/handleapi.h
Normal 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
310
dll/kernel32/heapapi.cpp
Normal 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
29
dll/kernel32/heapapi.h
Normal 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
|
43
dll/kernel32/interlockedapi.cpp
Normal file
43
dll/kernel32/interlockedapi.cpp
Normal 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
|
27
dll/kernel32/interlockedapi.h
Normal file
27
dll/kernel32/interlockedapi.h
Normal 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
|
@ -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
|
||||
|
@ -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
|
269
dll/kernel32/libloaderapi.cpp
Normal file
269
dll/kernel32/libloaderapi.cpp
Normal 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
|
25
dll/kernel32/libloaderapi.h
Normal file
25
dll/kernel32/libloaderapi.h
Normal 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
858
dll/kernel32/memoryapi.cpp
Normal 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 ®ion) { 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 ®ion, 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 ®ion, 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 ®ion, 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 ®ion = 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
66
dll/kernel32/memoryapi.h
Normal 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
92
dll/kernel32/minwinbase.h
Normal 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
258
dll/kernel32/processenv.cpp
Normal 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
20
dll/kernel32/processenv.h
Normal 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
|
@ -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
|
||||
|
107
dll/kernel32/processthreadsapi.h
Normal file
107
dll/kernel32/processthreadsapi.h
Normal 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
|
29
dll/kernel32/profileapi.cpp
Normal file
29
dll/kernel32/profileapi.cpp
Normal 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
10
dll/kernel32/profileapi.h
Normal 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
|
141
dll/kernel32/stringapiset.cpp
Normal file
141
dll/kernel32/stringapiset.cpp
Normal 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
|
14
dll/kernel32/stringapiset.h
Normal file
14
dll/kernel32/stringapiset.h
Normal 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
|
@ -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
105
dll/kernel32/synchapi.h
Normal 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
|
@ -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
48
dll/kernel32/sysinfoapi.h
Normal 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
212
dll/kernel32/timeutil.h
Normal 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;
|
||||
}
|
278
dll/kernel32/timezoneapi.cpp
Normal file
278
dll/kernel32/timezoneapi.cpp
Normal 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
|
33
dll/kernel32/timezoneapi.h
Normal file
33
dll/kernel32/timezoneapi.h
Normal 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
575
dll/kernel32/winbase.cpp
Normal 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
41
dll/kernel32/winbase.h
Normal 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
112
dll/kernel32/wincon.cpp
Normal 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
43
dll/kernel32/wincon.h
Normal 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
366
dll/kernel32/winnls.cpp
Normal 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
38
dll/kernel32/winnls.h
Normal 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
|
@ -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) {
|
||||
|
11
dll/kernel32/wow64apiset.h
Normal file
11
dll/kernel32/wow64apiset.h
Normal 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
|
1
errors.h
1
errors.h
@ -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
|
||||
|
5
main.cpp
5
main.cpp
@ -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 += ' ';
|
||||
}
|
||||
|
21
strutil.cpp
21
strutil.cpp
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user