add Linux support

This commit is contained in:
InoriRus
2022-10-03 15:33:23 +10:00
parent e427645422
commit 37b020e9ba
69 changed files with 2716 additions and 971 deletions
+2 -1
View File
@@ -16,6 +16,7 @@
#include "Kyty/Core/SimpleArray.h" // IWYU pragma: associated
#include "Kyty/Core/Singleton.h" // IWYU pragma: associated
#include "Kyty/Core/Vector.h" // IWYU pragma: associated
#include "Kyty/Core/VirtualMemory.h"
namespace Kyty::Core {
@@ -25,8 +26,8 @@ KYTY_SUBSYSTEM_INIT(Core)
core_file_init();
core_debug_init(parent->GetArgv()[0]);
Language::Init();
Database::Init();
VirtualMemory::Init();
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Core) {}
+31 -1
View File
@@ -19,6 +19,36 @@ constexpr int PRINT_STACK_FROM = 4;
constexpr int PRINT_STACK_FROM = 2;
#endif
#if KYTY_PLATFORM == KYTY_PLATFORM_LINUX
int IsDebuggerPresent()
{
bool dbg = false;
FILE* f = fopen("/proc/self/status", "r");
if (f != nullptr)
{
char str[1024];
while (feof(f) == 0)
{
str[1023] = '\0';
int pid = 0;
[[maybe_unused]] auto* result = fgets(str, 1023, f);
if (sscanf(str, "TracerPid: %d", &pid) == 1) // NOLINT
{
dbg = (pid != 0);
break;
}
}
[[maybe_unused]] auto result = fclose(f);
}
return (dbg ? 1 : 0);
}
#endif
void dbg_print_stack()
{
DebugStack s;
@@ -80,7 +110,7 @@ void dbg_exit(int status)
bool dbg_is_debugger_present()
{
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS || KYTY_PLATFORM == KYTY_PLATFORM_LINUX
return !(IsDebuggerPresent() == 0);
#endif
return false;
+4
View File
@@ -210,7 +210,11 @@ void* mem_alloc(size_t size)
{
if (size == 0)
{
#if KYTY_PLATFORM == KYTY_PLATFORM_LINUX
size = 1;
#else
EXIT("size == 0\n");
#endif
}
if ((g_mem_max_size != 0u) && size > g_mem_max_size)
+1
View File
@@ -9,6 +9,7 @@
// IWYU pragma: no_include "SDL_error.h"
// IWYU pragma: no_include "SDL_platform.h"
// IWYU pragma: no_include "SDL_stdinc.h"
// IWYU pragma: no_include "begin_code.h"
#include "SDL.h"
+136 -19
View File
@@ -8,20 +8,39 @@
#include "Kyty/Core/Vector.h"
#include <atomic>
#include <chrono>
#include <chrono> // IWYU pragma: keep
#include <condition_variable> // IWYU pragma: keep
#include <mutex>
#include <sstream>
#include <string>
#include <thread>
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && KYTY_COMPILER == KYTY_COMPILER_CLANG
#define KYTY_WIN_CS
#endif
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS && KYTY_PLATFORM != KYTY_PLATFORM_LINUX
#define KYTY_SDL_THREADS
#define KYTY_SDL_CS
#endif
//#define KYTY_DEBUG_LOCKS
//#define KYTY_DEBUG_LOCKS_TIMED
#ifdef KYTY_SDL_THREADS
#include "SDL_thread.h"
#include "SDL_timer.h"
#else
#include <sstream>
#include <string>
#include <thread>
#endif
#ifdef KYTY_SDL_CS
#include "SDL_mutex.h"
#endif
#if defined(KYTY_WIN_CS) && defined(KYTY_SDL_CS)
#error "defined(KYTY_WIN_CS) && defined(KYTY_SDL_CS)"
#endif
#if !(defined(KYTY_DEBUG_LOCKS) || defined(KYTY_DEBUG_LOCKS_TIMED)) && defined(KYTY_WIN_CS)
#include <windows.h> // IWYU pragma: keep
// IWYU pragma: no_include <winbase.h>
@@ -30,6 +49,10 @@ constexpr DWORD KYTY_CS_SPIN_COUNT = 4000;
// IWYU pragma: no_include <minwindef.h>
// IWYU pragma: no_include <synchapi.h>
// IWYU pragma: no_include <minwinbase.h>
// IWYU pragma: no_include <__mutex_base>
// IWYU pragma: no_include <__threading_support>
// IWYU pragma: no_include <errhandlingapi.h>
// IWYU pragma: no_include <winerror.h>
using InitializeConditionVariable_func_t = /*WINBASEAPI*/ VOID WINAPI (*)(PCONDITION_VARIABLE);
using WakeConditionVariable_func_t = /*WINBASEAPI*/ VOID WINAPI (*)(PCONDITION_VARIABLE);
@@ -38,7 +61,7 @@ using SleepConditionVariableCS_func_t = /*WINBASEAPI*/ BOOL WINAPI (*)(PCO
static InitializeConditionVariable_func_t ResolveInitializeConditionVariable()
{
if (HMODULE h = GetModuleHandle("KernelBase"); h != nullptr)
if (HMODULE h = GetModuleHandle("KernelBase"); h != nullptr) // @suppress("Invalid arguments")
{
return reinterpret_cast<InitializeConditionVariable_func_t>(GetProcAddress(h, "InitializeConditionVariable"));
}
@@ -46,7 +69,7 @@ static InitializeConditionVariable_func_t ResolveInitializeConditionVariable()
}
static WakeConditionVariable_func_t ResolveWakeConditionVariable()
{
if (HMODULE h = GetModuleHandle("KernelBase"); h != nullptr)
if (HMODULE h = GetModuleHandle("KernelBase"); h != nullptr) // @suppress("Invalid arguments")
{
return reinterpret_cast<WakeConditionVariable_func_t>(GetProcAddress(h, "WakeConditionVariable"));
}
@@ -54,7 +77,7 @@ static WakeConditionVariable_func_t ResolveWakeConditionVariable()
}
static WakeAllConditionVariable_func_t ResolveWakeAllConditionVariable()
{
if (HMODULE h = GetModuleHandle("KernelBase"); h != nullptr)
if (HMODULE h = GetModuleHandle("KernelBase"); h != nullptr) // @suppress("Invalid arguments")
{
return reinterpret_cast<WakeAllConditionVariable_func_t>(GetProcAddress(h, "WakeAllConditionVariable"));
}
@@ -62,7 +85,7 @@ static WakeAllConditionVariable_func_t ResolveWakeAllConditionVariable()
}
static SleepConditionVariableCS_func_t ResolveSleepConditionVariableCS()
{
if (HMODULE h = GetModuleHandle("KernelBase"); h != nullptr)
if (HMODULE h = GetModuleHandle("KernelBase"); h != nullptr) // @suppress("Invalid arguments")
{
return reinterpret_cast<SleepConditionVariableCS_func_t>(GetProcAddress(h, "SleepConditionVariableCS"));
}
@@ -73,7 +96,11 @@ static SleepConditionVariableCS_func_t ResolveSleepConditionVariableCS()
namespace Kyty::Core {
#ifdef KYTY_SDL_THREADS
using thread_id_t = uint64_t;
#else
using thread_id_t = std::thread::id;
#endif
#if defined(KYTY_DEBUG_LOCKS) || defined(KYTY_DEBUG_LOCKS_TIMED)
constexpr auto DBG_TRY_SECONDS = std::chrono::seconds(15);
@@ -94,7 +121,15 @@ struct MutexPrivate
DeleteCriticalSection(&m_cs);
}
KYTY_CLASS_NO_COPY(MutexPrivate);
CRITICAL_SECTION m_cs {};
CRITICAL_SECTION m_cs {};
#elif defined(KYTY_SDL_CS)
MutexPrivate(): sdl(SDL_CreateMutex()) {}
~MutexPrivate()
{
SDL_DestroyMutex(sdl);
}
KYTY_CLASS_NO_COPY(MutexPrivate);
SDL_mutex* sdl;
#else
std::recursive_mutex m_mutex;
#endif
@@ -113,6 +148,14 @@ struct CondVarPrivate
~CondVarPrivate() = default;
KYTY_CLASS_NO_COPY(CondVarPrivate);
CONDITION_VARIABLE m_cv {};
#elif !(defined(KYTY_DEBUG_LOCKS) || defined(KYTY_DEBUG_LOCKS_TIMED)) && defined(KYTY_SDL_CS)
CondVarPrivate(): sdl(SDL_CreateCond()) {}
~CondVarPrivate()
{
SDL_DestroyCond(sdl);
}
KYTY_CLASS_NO_COPY(CondVarPrivate);
SDL_cond* sdl;
#else
std::condition_variable_any m_cv;
#endif
@@ -173,7 +216,17 @@ static std::atomic<WaitForGraph*> g_wait_for_graph = nullptr;
struct ThreadPrivate
{
ThreadPrivate(thread_func_t f, void* a): func(f), arg(a), m_thread(&Run, this) {}
ThreadPrivate(thread_func_t f, void* a)
: func(f), arg(a),
#ifdef KYTY_SDL_THREADS
sdl(SDL_CreateThread(SdlThreadRun, "sdl_thread", this))
{
}
#else
m_thread(&Run, this)
{
}
#endif
static void Run(ThreadPrivate* t)
{
@@ -191,13 +244,23 @@ struct ThreadPrivate
}
}
static int SdlThreadRun(void* data)
{
Run(static_cast<ThreadPrivate*>(data));
return 0;
}
thread_func_t func;
void* arg;
std::atomic_bool finished = false;
std::atomic_bool auto_delete = false;
std::atomic_bool started = false;
int unique_id = 0;
std::thread m_thread;
#ifdef KYTY_SDL_THREADS
SDL_Thread* sdl;
#else
std::thread m_thread;
#endif
};
static thread_id_t g_main_thread;
@@ -206,7 +269,11 @@ static std::atomic<int> g_thread_counter = 0;
KYTY_SUBSYSTEM_INIT(Threads)
{
g_main_thread = std::this_thread::get_id();
#ifdef KYTY_SDL_THREADS
g_main_thread = SDL_ThreadID();
#else
g_main_thread = std::this_thread::get_id();
#endif
g_main_thread_int = Thread::GetThreadIdUnique();
g_wait_for_graph = new WaitForGraph;
}
@@ -468,7 +535,13 @@ void Thread::Join()
{
EXIT_IF(m_thread->finished || m_thread->auto_delete);
#ifdef KYTY_SDL_THREADS
int status = -1;
SDL_WaitThread(m_thread->sdl, &status);
EXIT_IF(status != 0);
#else
m_thread->m_thread.join();
#endif
m_thread->finished = true;
}
@@ -478,34 +551,58 @@ void Thread::Detach()
EXIT_IF(m_thread->finished || m_thread->auto_delete);
m_thread->auto_delete = true;
#ifdef KYTY_SDL_THREADS
SDL_DetachThread(m_thread->sdl);
#else
m_thread->m_thread.detach();
#endif
}
void Thread::Sleep(uint32_t millis)
{
#ifdef KYTY_SDL_THREADS
SDL_Delay(millis);
#else
std::this_thread::sleep_for(std::chrono::milliseconds(millis));
#endif
}
void Thread::SleepMicro(uint32_t micros)
{
#ifdef KYTY_SDL_THREADS
SDL_Delay(micros < 1000 && micros != 0 ? 1 : micros / 1000);
#else
std::this_thread::sleep_for(std::chrono::microseconds(micros));
#endif
}
void Thread::SleepNano(uint64_t nanos)
{
#ifdef KYTY_SDL_THREADS
SDL_Delay(nanos < 1000000 && nanos != 0 ? 1 : nanos / 1000000);
#else
std::this_thread::sleep_for(std::chrono::nanoseconds(nanos));
#endif
}
bool Thread::IsMainThread()
{
#ifdef KYTY_SDL_THREADS
return g_main_thread == static_cast<thread_id_t>(SDL_ThreadID());
#else
return g_main_thread == std::this_thread::get_id();
#endif
}
String Thread::GetId() const
{
#ifdef KYTY_SDL_THREADS
return String::FromPrintf("%" PRIu64, static_cast<uint64_t>(SDL_GetThreadID(m_thread->sdl)));
#else
std::stringstream ss;
ss << m_thread->m_thread.get_id();
return String::FromUtf8(ss.str().c_str());
#endif
}
int Thread::GetUniqueId() const
@@ -515,9 +612,13 @@ int Thread::GetUniqueId() const
String Thread::GetThreadId()
{
#ifdef KYTY_SDL_THREADS
return String::FromPrintf("%" PRIu64, static_cast<uint64_t>(SDL_ThreadID()));
#else
std::stringstream ss;
ss << std::this_thread::get_id();
return String::FromUtf8(ss.str().c_str());
#endif
}
Mutex::Mutex(): m_mutex(new MutexPrivate) {}
@@ -560,7 +661,7 @@ void Mutex::Lock()
}
#else
#ifdef KYTY_DEBUG_LOCKS_TIMED
bool locked = false;
bool locked = false;
do
{
locked = m_mutex->m_mutex.try_lock_for(DBG_TRY_SECONDS);
@@ -573,6 +674,8 @@ void Mutex::Lock()
#else
#ifdef KYTY_WIN_CS
EnterCriticalSection(&m_mutex->m_cs);
#elif defined(KYTY_SDL_CS)
SDL_LockMutex(m_mutex->sdl);
#else
m_mutex->m_mutex.lock();
#endif
@@ -591,6 +694,8 @@ void Mutex::Unlock()
#else
#if !defined(KYTY_DEBUG_LOCKS_TIMED) && defined(KYTY_WIN_CS)
LeaveCriticalSection(&m_mutex->m_cs);
#elif !defined(KYTY_DEBUG_LOCKS_TIMED) && defined(KYTY_SDL_CS)
SDL_UnlockMutex(m_mutex->sdl);
#else
m_mutex->m_mutex.unlock();
#endif
@@ -612,6 +717,8 @@ bool Mutex::TryLock()
#else
#if !defined(KYTY_DEBUG_LOCKS_TIMED) && defined(KYTY_WIN_CS)
return (TryEnterCriticalSection(&m_mutex->m_cs) != 0);
#elif !defined(KYTY_DEBUG_LOCKS_TIMED) && defined(KYTY_SDL_CS)
return (SDL_TryLockMutex(m_mutex->sdl) == 0);
#else
return m_mutex->m_mutex.try_lock();
#endif
@@ -630,7 +737,7 @@ void CondVar::Wait(Mutex* mutex)
#if defined(KYTY_DEBUG_LOCKS) || defined(KYTY_DEBUG_LOCKS_TIMED)
std::unique_lock<std::recursive_timed_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
#else
#ifdef KYTY_WIN_CS
#if defined(KYTY_WIN_CS) || defined(KYTY_SDL_CS)
#else
std::unique_lock<std::recursive_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
#endif
@@ -652,22 +759,25 @@ void CondVar::Wait(Mutex* mutex)
static auto func = ResolveSleepConditionVariableCS();
EXIT_NOT_IMPLEMENTED(func == nullptr);
func(&m_cond_var->m_cv, &mutex->m_mutex->m_cs, INFINITE);
#elif !defined(KYTY_DEBUG_LOCKS_TIMED) && defined(KYTY_SDL_CS)
SDL_CondWait(m_cond_var->sdl, mutex->m_mutex->sdl);
#else
m_cond_var->m_cv.wait(cpp_lock);
#endif
#endif
#if !(defined(KYTY_DEBUG_LOCKS) || defined(KYTY_DEBUG_LOCKS_TIMED)) && defined(KYTY_WIN_CS)
#if !(defined(KYTY_DEBUG_LOCKS) || defined(KYTY_DEBUG_LOCKS_TIMED)) && (defined(KYTY_WIN_CS) || defined(KYTY_SDL_CS))
#else
cpp_lock.release();
#endif
}
void CondVar::WaitFor(Mutex* mutex, uint32_t micros)
bool CondVar::WaitFor(Mutex* mutex, uint32_t micros)
{
bool ok = false;
#if defined(KYTY_DEBUG_LOCKS) || defined(KYTY_DEBUG_LOCKS_TIMED)
std::unique_lock<std::recursive_timed_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
#else
#ifdef KYTY_WIN_CS
#if defined(KYTY_WIN_CS) || defined(KYTY_SDL_CS)
#else
std::unique_lock<std::recursive_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
#endif
@@ -675,11 +785,14 @@ void CondVar::WaitFor(Mutex* mutex, uint32_t micros)
#if !(defined(KYTY_DEBUG_LOCKS) || defined(KYTY_DEBUG_LOCKS_TIMED)) && defined(KYTY_WIN_CS)
static auto func = ResolveSleepConditionVariableCS();
EXIT_NOT_IMPLEMENTED(func == nullptr);
func(&m_cond_var->m_cv, &mutex->m_mutex->m_cs, (micros < 1000 ? 1 : micros / 1000));
ok = !(func(&m_cond_var->m_cv, &mutex->m_mutex->m_cs, (micros < 1000 ? 1 : micros / 1000)) == 0 && GetLastError() == ERROR_TIMEOUT);
#elif !(defined(KYTY_DEBUG_LOCKS) || defined(KYTY_DEBUG_LOCKS_TIMED)) && defined(KYTY_SDL_CS)
ok = !(SDL_CondWaitTimeout(m_cond_var->sdl, mutex->m_mutex->sdl, (micros < 1000 ? 1 : micros / 1000)) == SDL_MUTEX_TIMEDOUT);
#else
m_cond_var->m_cv.wait_for(cpp_lock, std::chrono::microseconds(micros));
ok = (m_cond_var->m_cv.wait_for(cpp_lock, std::chrono::microseconds(micros)) == std::cv_status::no_timeout);
cpp_lock.release();
#endif
return ok;
}
void CondVar::Signal()
@@ -688,6 +801,8 @@ void CondVar::Signal()
static auto func = ResolveWakeConditionVariable();
EXIT_NOT_IMPLEMENTED(func == nullptr);
func(&m_cond_var->m_cv);
#elif !(defined(KYTY_DEBUG_LOCKS) || defined(KYTY_DEBUG_LOCKS_TIMED)) && defined(KYTY_SDL_CS)
SDL_CondSignal(m_cond_var->sdl);
#else
m_cond_var->m_cv.notify_one();
#endif
@@ -699,6 +814,8 @@ void CondVar::SignalAll()
static auto func = ResolveWakeAllConditionVariable();
EXIT_NOT_IMPLEMENTED(func == nullptr);
func(&m_cond_var->m_cv);
#elif !(defined(KYTY_DEBUG_LOCKS) || defined(KYTY_DEBUG_LOCKS_TIMED)) && defined(KYTY_SDL_CS)
SDL_CondBroadcast(m_cond_var->sdl);
#else
m_cond_var->m_cv.notify_all();
#endif
+324
View File
@@ -0,0 +1,324 @@
#include "Kyty/Core/VirtualMemory.h"
#include "Kyty/Sys/SysVirtual.h"
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
#define KYTY_HAS_EXCEPTIONS
#endif
#ifdef KYTY_HAS_EXCEPTIONS
#include <windows.h> // IWYU pragma: keep
#endif
// IWYU pragma: no_include <basetsd.h>
// IWYU pragma: no_include <errhandlingapi.h>
// IWYU pragma: no_include <excpt.h>
// IWYU pragma: no_include <minwinbase.h>
// IWYU pragma: no_include <minwindef.h>
// IWYU pragma: no_include <wtypes.h>
namespace Kyty::Core {
SystemInfo GetSystemInfo()
{
SystemInfo ret {};
sys_get_system_info(&ret);
return ret;
}
namespace VirtualMemory {
#ifdef KYTY_HAS_EXCEPTIONS
struct JmpRax
{
template <class Handler>
void SetFunc(Handler func)
{
*reinterpret_cast<Handler*>(&code[2]) = func;
}
// mov rax, 0x1122334455667788
// jmp rax
uint8_t code[16] = {0x48, 0xB8, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0xFF, 0xE0};
};
class ExceptionHandlerPrivate
{
public:
#pragma pack(1)
struct UnwindInfo
{
uint8_t Version : 3;
uint8_t Flags : 5;
uint8_t SizeOfProlog;
uint8_t CountOfCodes;
uint8_t FrameRegister : 4;
uint8_t FrameOffset : 4;
ULONG ExceptionHandler;
ExceptionHandlerPrivate* ExceptionData;
};
struct HandlerInfo
{
JmpRax code;
RUNTIME_FUNCTION function_table = {};
UnwindInfo unwind_info = {};
};
#pragma pack()
static EXCEPTION_DISPOSITION Handler(PEXCEPTION_RECORD exception_record, ULONG64 /*EstablisherFrame*/, PCONTEXT /*ContextRecord*/,
PDISPATCHER_CONTEXT dispatcher_context)
{
ExceptionHandler::ExceptionInfo info {};
info.exception_address = reinterpret_cast<uint64_t>(exception_record->ExceptionAddress);
if (exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
{
info.type = ExceptionHandler::ExceptionType::AccessViolation;
switch (exception_record->ExceptionInformation[0])
{
case 0: info.access_violation_type = ExceptionHandler::AccessViolationType::Read; break;
case 1: info.access_violation_type = ExceptionHandler::AccessViolationType::Write; break;
case 8: info.access_violation_type = ExceptionHandler::AccessViolationType::Execute; break;
default: info.access_violation_type = ExceptionHandler::AccessViolationType::Unknown; break;
}
info.access_violation_vaddr = exception_record->ExceptionInformation[1];
}
info.rbp = dispatcher_context->ContextRecord->Rbp;
info.exception_win_code = exception_record->ExceptionCode;
auto* p = *static_cast<ExceptionHandlerPrivate**>(dispatcher_context->HandlerData);
p->func(&info);
return ExceptionContinueExecution;
}
void InitHandler()
{
auto* h = new (reinterpret_cast<void*>(handler_addr)) HandlerInfo;
auto* code = &h->code;
auto* unwind_info = &h->unwind_info;
function_table = &h->function_table;
function_table->BeginAddress = 0;
function_table->EndAddress = image_size;
function_table->UnwindData = reinterpret_cast<uintptr_t>(unwind_info) - base_address;
unwind_info->Version = 1;
unwind_info->Flags = UNW_FLAG_EHANDLER;
unwind_info->SizeOfProlog = 0;
unwind_info->CountOfCodes = 0;
unwind_info->FrameRegister = 0;
unwind_info->FrameOffset = 0;
unwind_info->ExceptionHandler = reinterpret_cast<uintptr_t>(code) - base_address;
unwind_info->ExceptionData = this;
code->SetFunc(Handler);
FlushInstructionCache(reinterpret_cast<uint64_t>(code), sizeof(h->code));
}
uint64_t base_address = 0;
uint64_t handler_addr = 0;
uint64_t image_size = 0;
PRUNTIME_FUNCTION function_table = nullptr;
ExceptionHandler::handler_func_t func = nullptr;
static ExceptionHandler::handler_func_t g_vec_func;
};
ExceptionHandler::handler_func_t ExceptionHandlerPrivate::g_vec_func = nullptr;
#else
class ExceptionHandlerPrivate
{
};
#endif
ExceptionHandler::ExceptionHandler(): m_p(new ExceptionHandlerPrivate) {}
ExceptionHandler::~ExceptionHandler()
{
#ifdef KYTY_HAS_EXCEPTIONS
Uninstall();
#endif
delete m_p;
}
uint64_t ExceptionHandler::GetSize()
{
#ifdef KYTY_HAS_EXCEPTIONS
return (sizeof(ExceptionHandlerPrivate::HandlerInfo) & ~(static_cast<uint64_t>(0x1000) - 1)) + 0x1000;
#else
return 0x1000;
#endif
}
// NOLINTNEXTLINE(readability-convert-member-functions-to-static, misc-unused-parameters)
bool ExceptionHandler::Install(uint64_t base_address, uint64_t handler_addr, uint64_t image_size, handler_func_t func)
{
#ifdef KYTY_HAS_EXCEPTIONS
if (m_p->function_table == nullptr)
{
m_p->base_address = base_address;
m_p->handler_addr = handler_addr;
m_p->image_size = image_size;
m_p->func = func;
m_p->InitHandler();
if (RtlAddFunctionTable(m_p->function_table, 1, base_address) == FALSE)
{
printf("RtlAddFunctionTable() failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
return false;
}
return true;
}
return false;
#else
return true;
#endif
}
#ifdef KYTY_HAS_EXCEPTIONS
static LONG WINAPI ExceptionFilter(PEXCEPTION_POINTERS exception)
{
PEXCEPTION_RECORD exception_record = exception->ExceptionRecord;
ExceptionHandler::ExceptionInfo info {};
info.exception_address = reinterpret_cast<uint64_t>(exception_record->ExceptionAddress);
// printf("exception_record->ExceptionCode = %u\n", static_cast<uint32_t>(exception_record->ExceptionCode));
if (exception_record->ExceptionCode == DBG_PRINTEXCEPTION_C || exception_record->ExceptionCode == DBG_PRINTEXCEPTION_WIDE_C)
{
return EXCEPTION_CONTINUE_EXECUTION;
}
if (exception_record->ExceptionCode == 0x406D1388)
{
// Set a thread name
return EXCEPTION_CONTINUE_EXECUTION;
}
if (exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
{
info.type = ExceptionHandler::ExceptionType::AccessViolation;
switch (exception_record->ExceptionInformation[0])
{
case 0: info.access_violation_type = ExceptionHandler::AccessViolationType::Read; break;
case 1: info.access_violation_type = ExceptionHandler::AccessViolationType::Write; break;
case 8: info.access_violation_type = ExceptionHandler::AccessViolationType::Execute; break;
default: info.access_violation_type = ExceptionHandler::AccessViolationType::Unknown; break;
}
info.access_violation_vaddr = exception_record->ExceptionInformation[1];
}
info.rbp = exception->ContextRecord->Rbp;
info.exception_win_code = exception_record->ExceptionCode;
ExceptionHandlerPrivate::g_vec_func(&info);
return EXCEPTION_CONTINUE_EXECUTION;
}
#endif
// NOLINTNEXTLINE(readability-convert-member-functions-to-static, misc-unused-parameters)
bool ExceptionHandler::InstallVectored(handler_func_t func)
{
#ifdef KYTY_HAS_EXCEPTIONS
if (ExceptionHandlerPrivate::g_vec_func == nullptr)
{
ExceptionHandlerPrivate::g_vec_func = func;
if (AddVectoredExceptionHandler(1, ExceptionFilter) == nullptr)
{
printf("AddVectoredExceptionHandler() failed\n");
return false;
}
return true;
}
return false;
#else
return true;
#endif
}
// NOLINTNEXTLINE(readability-convert-member-functions-to-static, misc-unused-parameters)
bool ExceptionHandler::Uninstall()
{
#ifdef KYTY_HAS_EXCEPTIONS
if (m_p->function_table != nullptr)
{
if (RtlDeleteFunctionTable(m_p->function_table) == FALSE)
{
printf("RtlDeleteFunctionTable() failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
return false;
}
m_p->function_table = nullptr;
return true;
}
return false;
#else
return true;
#endif
}
void Init()
{
sys_virtual_init();
}
uint64_t Alloc(uint64_t address, uint64_t size, Mode mode)
{
return sys_virtual_alloc(address, size, mode);
}
uint64_t AllocAligned(uint64_t address, uint64_t size, Mode mode, uint64_t alignment)
{
return sys_virtual_alloc_aligned(address, size, mode, alignment);
}
bool AllocFixed(uint64_t address, uint64_t size, Mode mode)
{
return sys_virtual_alloc_fixed(address, size, mode);
}
bool Free(uint64_t address)
{
return sys_virtual_free(address);
}
bool Protect(uint64_t address, uint64_t size, Mode mode, Mode* old_mode)
{
return sys_virtual_protect(address, size, mode, old_mode);
}
bool FlushInstructionCache(uint64_t address, uint64_t size)
{
return sys_virtual_flush_instruction_cache(address, size);
}
bool PatchReplace(uint64_t vaddr, uint64_t value)
{
return sys_virtual_patch_replace(vaddr, value);
}
} // namespace VirtualMemory
} // namespace Kyty::Core
+10 -7
View File
@@ -2,22 +2,25 @@ file(GLOB sys_src
"src/*.cpp"
)
add_library(sys_obj OBJECT ${sys_src})
add_library(sys STATIC $<TARGET_OBJECTS:sys_obj>)
add_library(sys STATIC ${sys_src})
target_link_libraries(sys core)
target_link_libraries(sys core cpuinfo)
get_property(inc_headers TARGET sys PROPERTY INCLUDE_DIRECTORIES)
target_include_directories(sys_obj PRIVATE ${inc_headers})
list(APPEND check_headers
${CMAKE_SOURCE_DIR}/include
)
clang_tidy_check(sys_obj "" "${check_headers}" "${inc_headers}")
list(APPEND inc_headers
${CMAKE_SOURCE_DIR}/3rdparty/sdl2/sdl2/include
${CMAKE_SOURCE_DIR}/3rdparty/cpuinfo/include
)
include_what_you_use(sys_obj "${inc_headers}")
clang_tidy_check(sys "" "${check_headers}" "${inc_headers}")
#clang_tidy_fix(sys_obj "{Checks: '-*,cppcoreguidelines-init-variables'}" "${check_headers}" "${inc_headers}")
include_what_you_use(sys "${inc_headers}")
+145
View File
@@ -4,4 +4,149 @@
//#error "KYTY_PLATFORM != KYTY_PLATFORM_LINUX"
#else
#include "Kyty/Sys/SysDbg.h"
#include <cstdlib>
#include <cstring>
#include <sys/param.h>
#include <sys/types.h>
#include <unistd.h>
namespace Kyty {
static thread_local sys_dbg_stack_info_t g_stack = {0};
void sys_stack_walk(void** /*stack*/, int* depth)
{
*depth = 0;
}
void sys_stack_usage_print(sys_dbg_stack_info_t& stack)
{
printf("stack: (0x%" PRIx64 ", %" PRIu64 ")\n", static_cast<uint64_t>(stack.commited_addr), static_cast<uint64_t>(stack.commited_size));
printf("code: (0x%" PRIx64 ", %" PRIu64 ")\n", static_cast<uint64_t>(stack.code_addr), static_cast<uint64_t>(stack.code_size));
}
void sys_stack_usage(sys_dbg_stack_info_t& s)
{
pid_t pid = getpid();
// printf("pid = %"I64"d\n", (int64_t)pid);
[[maybe_unused]] int result = 0;
char str[1024];
char str2[1024];
result = sprintf(str, "/proc/%d/exe", static_cast<int>(pid));
ssize_t buff_len = 0;
if ((buff_len = readlink(str, str2, 1023)) == -1)
{
return;
}
str2[buff_len] = '\0';
const char* name = basename(str2);
result = sprintf(str, "/proc/%d/maps", static_cast<int>(pid));
memset(&s, 0, sizeof(sys_dbg_stack_info_t));
FILE* f = fopen(str, "r");
if (f == nullptr)
{
return;
}
// printf("&str = %"I64"x\n", (uint64_t)&str);
uint64_t addr = 0;
uint64_t endaddr = 0;
[[maybe_unused]] uint64_t size = 0;
uint64_t offset = 0;
uint64_t inode = 0;
char permissions[8] = {};
char device[8] = {};
char filename[MAXPATHLEN] = {};
auto check_addr = reinterpret_cast<uintptr_t>(&f);
while (true)
{
if (feof(f) != 0)
{
break;
}
if (fgets(str, sizeof(str), f) == nullptr)
{
break;
}
filename[0] = 0;
permissions[0] = 0;
addr = 0;
size = 0;
// printf("%s", str);
// NOLINTNEXTLINE(cert-err34-c)
result = sscanf(str, "%" SCNx64 "-%" SCNx64 " %s %" SCNx64 " %s %" SCNx64 " %s", &addr, &endaddr, permissions, &offset, device,
&inode, filename);
size = endaddr - addr;
bool read = (strchr(permissions, 'r') != nullptr);
bool write = (strchr(permissions, 'w') != nullptr);
bool exec = (strchr(permissions, 'x') != nullptr);
// printf("%016"I64"x, %"I64"d, %s, %d, %d\n", addr, size, filename, read, write);
if (read && write && !exec && strncmp(filename, "[stack", 6) == 0)
{
// printf("stack: %016"I64"x, %"I64"d\n", addr, size);
if (check_addr >= addr && check_addr < addr + size)
{
s.addr = addr;
s.total_size = size;
s.commited_addr = addr;
s.commited_size = size;
if (s.code_addr != 0)
{
break;
}
}
}
if (read && !write && exec && strstr(filename, name) != nullptr)
{
s.code_addr = addr;
s.code_size = size;
if (s.addr != 0)
{
break;
}
}
}
result = fclose(f);
}
void sys_get_code_info(uintptr_t* addr, size_t* size)
{
if (g_stack.code_size == 0)
{
sys_stack_usage(g_stack);
}
*addr = g_stack.code_addr;
*size = g_stack.code_size;
}
void sys_set_exception_filter(exception_filter_func_t /*func*/) {}
} // namespace Kyty
#endif
+665
View File
@@ -4,4 +4,669 @@
//#error "KYTY_PLATFORM != KYTY_PLATFORM_LINUX"
#else
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/MemoryAlloc.h"
#include "Kyty/Core/String.h"
#include "Kyty/Sys/SysFileIO.h"
#include "Kyty/Sys/SysTimer.h"
#include "SDL_system.h"
#include <cerrno>
#include <sys/stat.h>
#include <unistd.h>
#include <utime.h>
namespace Kyty {
template <typename T>
class Vector;
static String* g_internal_files_dir = nullptr;
static String get_internal_name(const String& name)
{
return name.StartsWith(U"/") ? name : *g_internal_files_dir + U"/" + name;
}
bool sys_file_io_init()
{
g_internal_files_dir = new String();
*g_internal_files_dir = U".";
return !g_internal_files_dir->IsEmpty();
}
void sys_file_read(void* data, uint32_t size, sys_file_t& f, uint32_t* bytes_read)
{
if (f.type == SYS_FILE_FILE)
{
size_t w = fread(data, 1, size, f.f);
if (bytes_read != nullptr)
{
*bytes_read = w;
}
} else if (f.type == SYS_FILE_MEMORY_STAT)
{
uint32_t s = size;
if (f.buf->size != 0)
{
uint32_t l = f.buf->size - (f.buf->ptr - f.buf->base);
if (s > l)
{
s = l;
}
}
memcpy(data, f.buf->ptr, s);
f.buf->ptr += s;
if (bytes_read != nullptr)
{
*bytes_read = s;
}
} else if (f.type == SYS_FILE_MEMORY_DYN)
{
uint32_t s = size;
if (f.buf->size != 0)
{
uint32_t l = f.buf->size - (f.buf->ptr - f.buf->base);
if (s > l)
{
s = l;
}
} else
{
s = 0;
}
memcpy(data, f.buf->ptr, s);
f.buf->ptr += s;
if (bytes_read != nullptr)
{
*bytes_read = s;
}
}
}
void sys_file_write(const void* data, uint32_t size, sys_file_t& f, uint32_t* bytes_written)
{
if (f.type == SYS_FILE_FILE)
{
size_t w = fwrite(data, 1, size, f.f);
if (bytes_written != nullptr)
{
*bytes_written = w;
}
} else if (f.type == SYS_FILE_MEMORY_STAT)
{
uint32_t s = size;
if (f.buf->size != 0)
{
uint32_t l = f.buf->size - (f.buf->ptr - f.buf->base);
if (s > l)
{
s = l;
}
}
memcpy(f.buf->ptr, data, s);
f.buf->ptr += s;
if (bytes_written != nullptr)
{
*bytes_written = s;
}
} else if (f.type == SYS_FILE_MEMORY_DYN)
{
uint32_t pos = f.buf->ptr - f.buf->base;
if (f.buf->size < pos + size)
{
f.buf->base = static_cast<uint8_t*>(Core::mem_realloc(f.buf->base, pos + size));
f.buf->ptr = f.buf->base + pos;
f.buf->size = pos + size;
}
memcpy(f.buf->ptr, data, size);
f.buf->ptr += size;
if (bytes_written != nullptr)
{
*bytes_written = size;
}
}
}
void sys_file_read_r(void* data, uint32_t size, sys_file_t& f)
{
// DWORD w;
// ReadFile(f, data, size, &w, 0);
sys_file_read(data, size, f);
for (uint32_t i = 0; i < size / 2; i++)
{
char t = (static_cast<char*>(data))[i];
(static_cast<char*>(data))[i] = (static_cast<char*>(data))[size - i - 1];
(static_cast<char*>(data))[size - i - 1] = t;
}
}
void sys_file_write_r(const void* data, uint32_t size, sys_file_t& f)
{
char* data_r = new char[size];
for (uint32_t i = 0; i < size; i++)
{
data_r[i] = (static_cast<const char*>(data))[size - i - 1];
}
sys_file_write(data_r, size, f);
delete[] data_r;
}
void sys_file_write(uint32_t n, sys_file_t& f)
{
sys_file_write(&n, 4, f);
}
void sys_file_write_r(uint32_t n, sys_file_t& f)
{
sys_file_write_r(&n, 4, f);
}
sys_file_t* sys_file_create(const String& file_name)
{
auto* ret = new sys_file_t;
String real_name = get_internal_name(file_name);
ret->f = fopen(real_name.utf8_str().GetData(), "w+");
if (ret->f == nullptr)
{
printf("can't create file: %s\n", real_name.utf8_str().GetData());
}
ret->type = SYS_FILE_FILE;
return ret;
}
sys_file_t* sys_file_open_r(const String& file_name, sys_file_cache_type_t /*cache_type*/)
{
auto* ret = new sys_file_t;
ret->type = SYS_FILE_FILE;
String internal_name = get_internal_name(file_name);
;
FILE* f = fopen(internal_name.utf8_str().GetData(), "r");
if (f == nullptr)
{
ret->type = SYS_FILE_ERROR;
}
ret->f = f;
return ret;
}
sys_file_t* sys_file_open(uint8_t* buf, uint32_t buf_size)
{
auto* ret = new sys_file_t;
ret->type = SYS_FILE_MEMORY_STAT;
ret->buf = new sys_file_mem_buf_t;
ret->buf->base = buf;
ret->buf->ptr = buf;
ret->buf->size = buf_size;
return ret;
}
sys_file_t* sys_file_create()
{
auto* ret = new sys_file_t;
ret->type = SYS_FILE_MEMORY_DYN;
ret->buf = new sys_file_mem_buf_t;
ret->buf->base = nullptr;
ret->buf->ptr = nullptr;
ret->buf->size = 0;
return ret;
}
sys_file_t* sys_file_open_w(const String& file_name, sys_file_cache_type_t /*cache_type*/)
{
auto* ret = new sys_file_t;
String real_name = get_internal_name(file_name);
;
FILE* f = fopen(real_name.utf8_str().GetData(), "r+");
if (f == nullptr)
{
ret->type = SYS_FILE_ERROR;
} else
{
ret->type = SYS_FILE_FILE;
}
ret->f = f;
return ret;
}
sys_file_t* sys_file_open_rw(const String& file_name, sys_file_cache_type_t /*cache_type*/)
{
auto* ret = new sys_file_t;
String real_name = get_internal_name(file_name);
FILE* f = fopen(real_name.utf8_str().GetData(), "r+");
if (f == nullptr)
{
ret->type = SYS_FILE_ERROR;
} else
{
ret->type = SYS_FILE_FILE;
}
ret->f = f;
return ret;
}
void sys_file_close(sys_file_t* f)
{
[[maybe_unused]] int result = 0;
if (f->type == SYS_FILE_FILE && f->f != nullptr)
{
result = fclose(f->f);
} else if (f->type == SYS_FILE_MEMORY_STAT)
{
delete f->buf;
} else if (f->type == SYS_FILE_MEMORY_DYN)
{
Core::mem_free(f->buf->base);
delete f->buf;
}
// f.type = SYS_FILE_ERROR;
delete f;
}
uint64_t sys_file_size(sys_file_t& f)
{
[[maybe_unused]] int result = 0;
if (f.type == SYS_FILE_FILE)
{
uint32_t pos = ftell(f.f);
result = fseek(f.f, 0, SEEK_END);
uint32_t size = ftell(f.f);
result = fseek(f.f, pos, SEEK_SET);
return size;
}
if (f.type == SYS_FILE_MEMORY_STAT || f.type == SYS_FILE_MEMORY_DYN)
{
return f.buf->size;
}
return 0;
}
uint64_t sys_file_size(const String& file_name)
{
sys_file_t* f = sys_file_open_r(file_name);
uint64_t size = sys_file_size(*f);
sys_file_close(f);
return size;
}
bool sys_file_truncate(sys_file_t& /*f*/, uint64_t /*size*/)
{
return false;
}
bool sys_file_seek(sys_file_t& f, uint64_t offset)
{
bool ok = true;
if (f.type == SYS_FILE_FILE)
{
ok = (fseek(f.f, static_cast<int64_t>(offset), SEEK_SET) == 0);
// LARGE_INTEGER s;
// s.QuadPart = offset;
// SetFilePointerEx(f.handle, s, 0, FILE_BEGIN);
// printf("seek: %u\n", offset);
} else if (f.type == SYS_FILE_MEMORY_STAT || f.type == SYS_FILE_MEMORY_DYN)
{
f.buf->ptr = f.buf->base + offset;
}
return ok;
}
uint64_t sys_file_tell(sys_file_t& f)
{
if (f.type == SYS_FILE_FILE)
{
return ftell(f.f);
}
if (f.type == SYS_FILE_MEMORY_STAT || f.type == SYS_FILE_MEMORY_DYN)
{
return f.buf->ptr - f.buf->base;
}
return 0;
}
bool sys_file_is_error(sys_file_t& f)
{
return f.type == SYS_FILE_ERROR || (f.type == SYS_FILE_FILE && f.f == nullptr);
}
bool sys_file_is_directory_existing(const String& path)
{
String real_name = get_internal_name(path);
struct stat s
{
};
if (0 != stat(real_name.utf8_str().GetData(), &s))
{
return false;
}
return S_ISDIR(s.st_mode); // NOLINT
}
bool sys_file_is_file_existing(const String& name)
{
String real_name = get_internal_name(name);
struct stat s
{
};
if (0 != stat(real_name.utf8_str().GetData(), &s))
{
return false;
}
return !S_ISDIR(s.st_mode); // NOLINT
}
bool sys_file_create_directory(const String& path)
{
String real_name = get_internal_name(path);
mode_t m = S_IRWXU | S_IRWXG | S_IRWXO; // NOLINT
String::Utf8 u = real_name.utf8_str();
int r = mkdir(u.GetDataConst(), m);
if (r != 0)
{
int e = errno;
if (e == EEXIST)
{
printf("mkdir(%s, %" PRIx32 ") failed: The named file exists\n", real_name.C_Str(), static_cast<uint32_t>(m));
} else
{
printf("mkdir(%s, %" PRIx32 ") failed: %d\n", real_name.C_Str(), static_cast<uint32_t>(m), e);
return false;
}
}
return true;
}
bool sys_file_delete_directory(const String& path)
{
String real_name = get_internal_name(path);
return 0 == remove(real_name.utf8_str().GetData());
}
bool sys_file_delete_file(const String& name)
{
String real_name = get_internal_name(name);
return 0 == unlink(real_name.utf8_str().GetData());
}
bool sys_file_flush(sys_file_t& f)
{
if (f.type == SYS_FILE_FILE && f.f != nullptr)
{
return (fflush(f.f) == 0);
}
return false;
}
SysFileTimeStruct sys_file_get_last_access_time_utc(const String& name)
{
SysFileTimeStruct r {};
String real_name = get_internal_name(name);
struct stat s
{
};
if (0 != stat(real_name.utf8_str().GetData(), &s))
{
r.is_invalid = true;
} else
{
r.is_invalid = false;
r.time = s.st_atime;
}
return r;
}
SysFileTimeStruct sys_file_get_last_write_time_utc(const String& name)
{
SysFileTimeStruct r {};
String real_name = get_internal_name(name);
struct stat s
{
};
if (0 != stat(real_name.utf8_str().GetData(), &s))
{
r.is_invalid = true;
} else
{
r.is_invalid = false;
r.time = s.st_mtime;
}
return r;
}
void sys_file_get_last_access_and_write_time_utc(const String& name, SysFileTimeStruct& a, SysFileTimeStruct& w)
{
String real_name = get_internal_name(name);
struct stat s
{
};
if (0 != stat(real_name.utf8_str().GetData(), &s))
{
a.is_invalid = true;
w.is_invalid = true;
} else
{
a.is_invalid = false;
w.is_invalid = false;
a.time = s.st_atime;
w.time = s.st_mtime;
}
}
void sys_file_get_last_access_and_write_time_utc(sys_file_t& /*f*/, SysFileTimeStruct& /*a*/, SysFileTimeStruct& /*w*/)
{
EXIT("not implemented\n");
}
bool sys_file_set_last_access_time_utc(const String& name, SysFileTimeStruct& access)
{
if (access.is_invalid)
{
return false;
}
String real_name = get_internal_name(name);
struct stat s
{
};
String::Utf8 n = real_name.utf8_str();
if (0 != stat(n.GetData(), &s))
{
return false;
}
utimbuf times {};
times.actime = access.time;
times.modtime = s.st_mtime;
return !(0 != utime(n.GetData(), &times));
// if (0 != stat(n.GetData(), &s))
// {
// return false;
// }
//
// times.actime += times.actime - s.st_atime;
// times.modtime += times.modtime - s.st_mtime;
//
// if (0 != utime(n.GetData(), &times))
// {
// return false;
// }
}
bool sys_file_set_last_write_time_utc(const String& name, SysFileTimeStruct& write)
{
if (write.is_invalid)
{
return false;
}
String real_name = get_internal_name(name);
struct stat s
{
};
String::Utf8 n = real_name.utf8_str();
if (0 != stat(n.GetData(), &s))
{
return false;
}
utimbuf times {};
times.actime = s.st_atime;
times.modtime = write.time;
return !(0 != utime(n.GetData(), &times));
// if (0 != stat(n.GetData(), &s))
// {
// return false;
// }
//
// times.actime += times.actime - s.st_atime;
// times.modtime += times.modtime - s.st_mtime;
//
// if (0 != utime(n.GetData(), &times))
// {
// return false;
// }
}
bool sys_file_set_last_access_and_write_time_utc(const String& name, SysFileTimeStruct& access, SysFileTimeStruct& write)
{
if (access.is_invalid || write.is_invalid)
{
return false;
}
String real_name = get_internal_name(name);
struct stat s
{
};
String::Utf8 n = real_name.utf8_str();
if (0 != stat(n.GetData(), &s))
{
return false;
}
utimbuf times {};
times.actime = access.time;
times.modtime = write.time;
return !(0 != utime(n.GetData(), &times));
// if (0 != stat(n.GetData(), &s))
// {
// return false;
// }
//
// times.actime += times.actime - s.st_atime;
// times.modtime += times.modtime - s.st_mtime;
//
// if (0 != utime(n.GetData(), &times))
// {
// return false;
// }
}
void sys_file_find_files(const String& /*path*/, Vector<sys_file_find_t>& /*out*/)
{
EXIT("not implemented\n");
}
void sys_file_get_dents(const String& /*path*/, Kyty::Vector<sys_dir_entry_t>& /*out*/)
{
EXIT("not implemented\n");
}
bool sys_file_copy_file(const String& /*src*/, const String& /*dst*/)
{
EXIT("not implemented\n");
return false;
}
bool sys_file_move_file(const String& /*src*/, const String& /*dst*/)
{
EXIT("not implemented\n");
return false;
}
void sys_file_remove_readonly(const String& /*name*/)
{
EXIT("not implemented\n");
}
} // namespace Kyty
#endif
+373
View File
@@ -0,0 +1,373 @@
#include "Kyty/Core/Common.h"
#if KYTY_PLATFORM != KYTY_PLATFORM_LINUX
//#error "KYTY_PLATFORM != KYTY_PLATFORM_LINUX"
#else
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/String.h"
#include "Kyty/Core/VirtualMemory.h"
#include "Kyty/Sys/SysVirtual.h"
#include "cpuinfo.h"
#include <cerrno>
#include <map>
#include <pthread.h>
#include <sys/mman.h>
// IWYU pragma: no_include <asm/mman-common.h>
// IWYU pragma: no_include <asm/mman.h>
// IWYU pragma: no_include <bits/pthread_types.h>
// IWYU pragma: no_include <linux/mman.h>
#if defined(MAP_FIXED_NOREPLACE) && KYTY_PLATFORM == KYTY_PLATFORM_LINUX
#define KYTY_FIXED_NOREPLACE
#endif
namespace Kyty::Core {
static pthread_mutex_t g_virtual_mutex {};
static std::map<uintptr_t, size_t>* g_allocs = nullptr;
static std::map<uintptr_t, int>* g_protects = nullptr;
void sys_get_system_info(SystemInfo* info)
{
EXIT_IF(info == nullptr);
const auto* p = cpuinfo_get_package(0);
EXIT_IF(p == nullptr);
info->ProcessorName = String::FromUtf8(p->name);
}
void sys_virtual_init()
{
pthread_mutexattr_t attr {};
pthread_mutexattr_init(&attr);
#if KYTY_PLATFORM == KYTY_PLATFORM_LINUX
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_FAST_NP);
#else
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
#endif
pthread_mutex_init(&g_virtual_mutex, &attr);
pthread_mutexattr_destroy(&attr);
g_allocs = new std::map<uintptr_t, size_t>;
g_protects = new std::map<uintptr_t, int>;
cpuinfo_initialize();
}
static int get_protection_flag(VirtualMemory::Mode mode)
{
int protect = PROT_NONE;
switch (mode)
{
case VirtualMemory::Mode::Read: protect = PROT_READ; break;
case VirtualMemory::Mode::Write:
case VirtualMemory::Mode::ReadWrite: protect = PROT_READ | PROT_WRITE; break; // NOLINT
case VirtualMemory::Mode::Execute: protect = PROT_EXEC; break;
case VirtualMemory::Mode::ExecuteRead: protect = PROT_EXEC | PROT_READ; break; // NOLINT
case VirtualMemory::Mode::ExecuteWrite:
case VirtualMemory::Mode::ExecuteReadWrite: protect = PROT_EXEC | PROT_WRITE | PROT_READ; break; // NOLINT
case VirtualMemory::Mode::NoAccess:
default: protect = PROT_NONE; break;
}
return protect;
}
static VirtualMemory::Mode get_protection_flag(int mode)
{
switch (mode)
{
case PROT_NONE: return VirtualMemory::Mode::NoAccess;
case PROT_READ: return VirtualMemory::Mode::Read;
case PROT_WRITE: return VirtualMemory::Mode::Write;
case PROT_READ | PROT_WRITE: return VirtualMemory::Mode::ReadWrite; // NOLINT
case PROT_EXEC: return VirtualMemory::Mode::Execute;
case PROT_EXEC | PROT_WRITE: return VirtualMemory::Mode::ExecuteWrite; // NOLINT
case PROT_EXEC | PROT_READ: return VirtualMemory::Mode::ExecuteRead; // NOLINT
case PROT_EXEC | PROT_WRITE | PROT_READ: return VirtualMemory::Mode::ExecuteReadWrite; // NOLINT
default: return VirtualMemory::Mode::NoAccess;
}
}
uint64_t sys_virtual_alloc(uint64_t address, uint64_t size, VirtualMemory::Mode mode)
{
EXIT_IF(g_allocs == nullptr);
auto addr = static_cast<uintptr_t>(address);
int protect = get_protection_flag(mode);
void* ptr = mmap(reinterpret_cast<void*>(addr), size, protect, MAP_PRIVATE | MAP_ANON, -1, 0); // NOLINT
auto ret_addr = reinterpret_cast<uintptr_t>(ptr);
if (ptr != MAP_FAILED)
{
pthread_mutex_lock(&g_virtual_mutex);
(*g_allocs)[ret_addr] = size;
uintptr_t page_start = ret_addr >> 12u;
uintptr_t page_end = (ret_addr + size - 1) >> 12u;
for (uintptr_t page = page_start; page <= page_end; page++)
{
(*g_protects)[page] = protect;
}
pthread_mutex_unlock(&g_virtual_mutex);
}
return ret_addr;
}
static uintptr_t align_up(uintptr_t addr, uint64_t alignment)
{
return (addr + alignment - 1) & ~(alignment - 1);
}
uint64_t sys_virtual_alloc_aligned(uint64_t address, uint64_t size, VirtualMemory::Mode mode, uint64_t alignment)
{
if (alignment == 0)
{
return 0;
}
EXIT_IF(g_allocs == nullptr);
auto addr = static_cast<uintptr_t>(address);
int protect = get_protection_flag(mode);
void* ptr = mmap(reinterpret_cast<void*>(addr), size, protect, MAP_PRIVATE | MAP_ANON, -1, 0); // NOLINT
auto ret_addr = reinterpret_cast<uintptr_t>(ptr);
if (ptr != MAP_FAILED && ((ret_addr & (alignment - 1)) != 0))
{
munmap(ptr, size);
ptr = mmap(reinterpret_cast<void*>(addr), size + alignment, protect, MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, -1, 0); // NOLINT
ret_addr = reinterpret_cast<uintptr_t>(ptr);
if (ptr != MAP_FAILED)
{
munmap(ptr, size + alignment);
auto aligned_addr = align_up(ret_addr, alignment);
#ifdef KYTY_FIXED_NOREPLACE
// NOLINTNEXTLINE
ptr = mmap(reinterpret_cast<void*>(aligned_addr), size, protect, MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANON, -1, 0);
#else
// NOLINTNEXTLINE
ptr = mmap(reinterpret_cast<void*>(aligned_addr), size, protect, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0);
#endif
ret_addr = reinterpret_cast<uintptr_t>(ptr);
if (ptr == MAP_FAILED)
{
[[maybe_unused]] int err = errno;
// printf("mmap failed: %d\n", err);
}
if (ptr != MAP_FAILED && ((ret_addr & (alignment - 1)) != 0))
{
munmap(ptr, size);
ret_addr = 0;
ptr = MAP_FAILED;
}
}
}
if (ptr == MAP_FAILED)
{
return sys_virtual_alloc_aligned(address, size, mode, alignment << 1u);
}
pthread_mutex_lock(&g_virtual_mutex);
(*g_allocs)[ret_addr] = size;
uintptr_t page_start = ret_addr >> 12u;
uintptr_t page_end = (ret_addr + size - 1) >> 12u;
for (uintptr_t page = page_start; page <= page_end; page++)
{
(*g_protects)[page] = protect;
}
pthread_mutex_unlock(&g_virtual_mutex);
return ret_addr;
}
[[maybe_unused]] static bool is_mmaped(void* ptr, size_t length)
{
FILE* file = fopen("/proc/self/maps", "r");
char line[1024];
bool ret = false;
auto addr = reinterpret_cast<uintptr_t>(ptr);
[[maybe_unused]] int result = 0;
while (feof(file) == 0)
{
if (fgets(line, 1024, file) == nullptr)
{
break;
}
uint64_t start = 0;
uint64_t end = 0;
// NOLINTNEXTLINE(cert-err34-c)
if (sscanf(line, "%" SCNx64 "-%" SCNx64, &start, &end) != 2)
{
continue;
}
if (addr >= start && addr + length <= end)
{
ret = true;
break;
}
}
result = fclose(file);
return ret;
}
bool sys_virtual_alloc_fixed(uint64_t address, uint64_t size, VirtualMemory::Mode mode)
{
EXIT_IF(g_allocs == nullptr);
auto addr = static_cast<uintptr_t>(address);
int protect = get_protection_flag(mode);
#ifdef KYTY_FIXED_NOREPLACE
// NOLINTNEXTLINE
void* ptr = mmap(reinterpret_cast<void*>(addr), size, protect, MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANON, -1, 0);
#else
// NOLINTNEXTLINE
void* ptr = (is_mmaped(reinterpret_cast<void*>(addr), size)
? MAP_FAILED
: mmap(reinterpret_cast<void*>(addr), size, protect, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0));
#endif
auto ret_addr = reinterpret_cast<uintptr_t>(ptr);
if (ptr != MAP_FAILED && ret_addr != addr)
{
munmap(ptr, size);
ret_addr = 0;
ptr = MAP_FAILED;
}
if (ptr != MAP_FAILED)
{
pthread_mutex_lock(&g_virtual_mutex);
(*g_allocs)[ret_addr] = size;
uintptr_t page_start = ret_addr >> 12u;
uintptr_t page_end = (ret_addr + size - 1) >> 12u;
for (uintptr_t page = page_start; page <= page_end; page++)
{
(*g_protects)[page] = protect;
}
pthread_mutex_unlock(&g_virtual_mutex);
return true;
}
return false;
}
bool sys_virtual_free(uint64_t address)
{
EXIT_IF(g_allocs == nullptr);
size_t size = 0;
auto addr = static_cast<uintptr_t>(address & ~static_cast<uint64_t>(0xfffu));
pthread_mutex_lock(&g_virtual_mutex);
if (auto s = g_allocs->find(addr); s != g_allocs->end())
{
size = s->second;
g_allocs->erase(s);
}
pthread_mutex_unlock(&g_virtual_mutex);
if (size == 0)
{
return false;
}
if (munmap(reinterpret_cast<void*>(addr), size) == 0)
{
uintptr_t page_start = addr >> 12u;
uintptr_t page_end = (addr + size - 1) >> 12u;
pthread_mutex_lock(&g_virtual_mutex);
for (uintptr_t page = page_start; page <= page_end; page++)
{
if (auto s = g_protects->find(page); s != g_protects->end())
{
g_protects->erase(s);
}
}
pthread_mutex_unlock(&g_virtual_mutex);
return true;
}
return false;
}
bool sys_virtual_protect(uint64_t address, uint64_t size, VirtualMemory::Mode mode, VirtualMemory::Mode* old_mode)
{
auto addr = static_cast<uintptr_t>(address);
pthread_mutex_lock(&g_virtual_mutex);
if (old_mode != nullptr)
{
if (auto s = g_protects->find(addr >> 12u); s != g_protects->end())
{
*old_mode = get_protection_flag(s->second);
} else
{
*old_mode = VirtualMemory::Mode::NoAccess;
}
}
pthread_mutex_unlock(&g_virtual_mutex);
uintptr_t page_start = addr >> 12u;
uintptr_t page_end = (addr + size - 1) >> 12u;
if (mprotect(reinterpret_cast<void*>(page_start << 12u), (page_end - page_start + 1) << 12u, get_protection_flag(mode)) == 0)
{
pthread_mutex_lock(&g_virtual_mutex);
for (uintptr_t page = page_start; page <= page_end; page++)
{
(*g_protects)[page] = get_protection_flag(mode);
}
pthread_mutex_unlock(&g_virtual_mutex);
return true;
}
return false;
}
bool sys_virtual_flush_instruction_cache(uint64_t /*address*/, uint64_t /*size*/)
{
return true;
}
bool sys_virtual_patch_replace(uint64_t vaddr, uint64_t value)
{
VirtualMemory::Mode old_mode {};
sys_virtual_protect(vaddr, 8, VirtualMemory::Mode::ReadWrite, &old_mode);
auto* ptr = reinterpret_cast<uint64_t*>(vaddr);
bool ret = (*ptr != value);
*ptr = value;
sys_virtual_protect(vaddr, 8, old_mode);
if (VirtualMemory::IsExecute(old_mode))
{
sys_virtual_flush_instruction_cache(vaddr, 8);
}
return ret;
}
} // namespace Kyty::Core
#endif
+4 -3
View File
@@ -1,8 +1,6 @@
#include "Kyty/Sys/SysWindowsDbg.h"
#include "Kyty/Sys/Windows/SysWindowsDbg.h"
#include "Kyty/Core/Common.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Sys/SysDbg.h"
// IWYU pragma: no_include <basetsd.h>
// IWYU pragma: no_include <memoryapi.h>
@@ -16,6 +14,9 @@
//#error "KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS"
#else
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Sys/SysDbg.h"
#include <windows.h> // IWYU pragma: keep
#if KYTY_COMPILER == KYTY_COMPILER_MSVC
#include <intrin.h>
+1 -1
View File
@@ -1,4 +1,4 @@
#include "Kyty/Sys/SysWindowsFileIO.h"
#include "Kyty/Sys/Windows/SysWindowsFileIO.h"
#include "Kyty/Core/Common.h"
+259
View File
@@ -0,0 +1,259 @@
#include "Kyty/Core/Common.h"
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
//#error "KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS"
#else
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/String.h"
#include "Kyty/Core/VirtualMemory.h"
#include "Kyty/Sys/SysVirtual.h"
#include "cpuinfo.h"
#include <windows.h> // IWYU pragma: keep
// IWYU pragma: no_include <basetsd.h>
// IWYU pragma: no_include <errhandlingapi.h>
// IWYU pragma: no_include <memoryapi.h>
// IWYU pragma: no_include <minwindef.h>
// IWYU pragma: no_include <processthreadsapi.h>
// IWYU pragma: no_include <winbase.h>
// IWYU pragma: no_include <winerror.h>
// IWYU pragma: no_include <wtypes.h>
namespace Kyty::Core {
void sys_get_system_info(SystemInfo* info)
{
EXIT_IF(info == nullptr);
const auto* p = cpuinfo_get_package(0);
EXIT_IF(p == nullptr);
info->ProcessorName = String::FromUtf8(p->name);
}
static DWORD get_protection_flag(VirtualMemory::Mode mode)
{
DWORD protect = PAGE_NOACCESS;
switch (mode)
{
case VirtualMemory::Mode::Read: protect = PAGE_READONLY; break;
case VirtualMemory::Mode::Write:
case VirtualMemory::Mode::ReadWrite: protect = PAGE_READWRITE; break;
case VirtualMemory::Mode::Execute: protect = PAGE_EXECUTE; break;
case VirtualMemory::Mode::ExecuteRead: protect = PAGE_EXECUTE_READ; break;
case VirtualMemory::Mode::ExecuteWrite:
case VirtualMemory::Mode::ExecuteReadWrite: protect = PAGE_EXECUTE_READWRITE; break;
case VirtualMemory::Mode::NoAccess:
default: protect = PAGE_NOACCESS; break;
}
return protect;
}
static VirtualMemory::Mode get_protection_flag(DWORD mode)
{
switch (mode)
{
case PAGE_NOACCESS: return VirtualMemory::Mode::NoAccess;
case PAGE_READONLY: return VirtualMemory::Mode::Read;
case PAGE_READWRITE: return VirtualMemory::Mode::ReadWrite;
case PAGE_EXECUTE: return VirtualMemory::Mode::Execute;
case PAGE_EXECUTE_READ: return VirtualMemory::Mode::ExecuteRead;
case PAGE_EXECUTE_READWRITE: return VirtualMemory::Mode::ExecuteReadWrite;
default: return VirtualMemory::Mode::NoAccess;
}
}
void sys_virtual_init()
{
cpuinfo_initialize();
}
uint64_t sys_virtual_alloc(uint64_t address, uint64_t size, VirtualMemory::Mode mode)
{
auto ptr = (address == 0 ? sys_virtual_alloc_aligned(address, size, mode, 1)
: reinterpret_cast<uintptr_t>(VirtualAlloc(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
static_cast<DWORD>(MEM_COMMIT) | static_cast<DWORD>(MEM_RESERVE),
get_protection_flag(mode))));
if (ptr == 0)
{
auto err = static_cast<uint32_t>(GetLastError());
if (err != ERROR_INVALID_ADDRESS)
{
printf("VirtualAlloc() failed: 0x%08" PRIx32 "\n", err);
} else
{
return sys_virtual_alloc_aligned(address, size, mode, 1);
}
}
return ptr;
}
using VirtualAlloc2_func_t = /*WINBASEAPI*/ PVOID WINAPI (*)(HANDLE, PVOID, SIZE_T, ULONG, ULONG, MEM_EXTENDED_PARAMETER*, ULONG);
static VirtualAlloc2_func_t ResolveVirtualAlloc2()
{
HMODULE h = GetModuleHandle("KernelBase"); // @suppress("Invalid arguments")
if (h != nullptr)
{
return reinterpret_cast<VirtualAlloc2_func_t>(GetProcAddress(h, "VirtualAlloc2"));
}
return nullptr;
}
static uint64_t align_up(uint64_t addr, uint64_t alignment)
{
return (addr + alignment - 1) & ~(alignment - 1);
}
uint64_t sys_virtual_alloc_aligned(uint64_t address, uint64_t size, VirtualMemory::Mode mode, uint64_t alignment)
{
if (alignment == 0)
{
printf("VirtualAlloc2 failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
return 0;
}
static constexpr uint64_t SYSTEM_MANAGED_MIN = 0x0000040000u;
static constexpr uint64_t SYSTEM_MANAGED_MAX = 0x07FFFFBFFFu;
static constexpr uint64_t USER_MIN = 0x1000000000u;
static constexpr uint64_t USER_MAX = 0xFBFFFFFFFFu;
MEM_ADDRESS_REQUIREMENTS req {};
MEM_EXTENDED_PARAMETER param {};
req.LowestStartingAddress =
(address == 0 ? reinterpret_cast<PVOID>(SYSTEM_MANAGED_MIN) : reinterpret_cast<PVOID>(align_up(address, alignment)));
req.HighestEndingAddress = (address == 0 ? reinterpret_cast<PVOID>(SYSTEM_MANAGED_MAX) : reinterpret_cast<PVOID>(USER_MAX));
req.Alignment = alignment;
param.Type = MemExtendedParameterAddressRequirements;
param.Pointer = &req;
MEM_ADDRESS_REQUIREMENTS req2 {};
MEM_EXTENDED_PARAMETER param2 {};
req2.LowestStartingAddress = (address == 0 ? reinterpret_cast<PVOID>(USER_MIN) : reinterpret_cast<PVOID>(align_up(address, alignment)));
req2.HighestEndingAddress = reinterpret_cast<PVOID>(USER_MAX);
req2.Alignment = alignment;
param2.Type = MemExtendedParameterAddressRequirements;
param2.Pointer = &req2;
static auto virtual_alloc2 = ResolveVirtualAlloc2();
EXIT_NOT_IMPLEMENTED(virtual_alloc2 == nullptr);
auto ptr = reinterpret_cast<uintptr_t>(virtual_alloc2(GetCurrentProcess(), nullptr, size,
static_cast<DWORD>(MEM_COMMIT) | static_cast<DWORD>(MEM_RESERVE),
get_protection_flag(mode), &param, 1));
if (ptr == 0)
{
ptr = reinterpret_cast<uintptr_t>(virtual_alloc2(GetCurrentProcess(), nullptr, size,
static_cast<DWORD>(MEM_COMMIT) | static_cast<DWORD>(MEM_RESERVE),
get_protection_flag(mode), &param2, 1));
}
if (ptr == 0)
{
auto err = static_cast<uint32_t>(GetLastError());
if (err != ERROR_INVALID_PARAMETER)
{
printf("VirtualAlloc2(alignment = 0x%016" PRIx64 ") failed: 0x%08" PRIx32 "\n", alignment, err);
} else
{
return sys_virtual_alloc_aligned(address, size, mode, alignment << 1u);
}
}
return ptr;
}
bool sys_virtual_alloc_fixed(uint64_t address, uint64_t size, VirtualMemory::Mode mode)
{
auto ptr = reinterpret_cast<uintptr_t>(VirtualAlloc(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
static_cast<DWORD>(MEM_COMMIT) | static_cast<DWORD>(MEM_RESERVE),
get_protection_flag(mode)));
if (ptr == 0)
{
auto err = static_cast<uint32_t>(GetLastError());
printf("VirtualAlloc() failed: 0x%08" PRIx32 "\n", err);
return false;
}
if (ptr != address)
{
printf("VirtualAlloc() failed: wrong address\n");
VirtualFree(reinterpret_cast<LPVOID>(ptr), 0, MEM_RELEASE);
return false;
}
return true;
}
bool sys_virtual_free(uint64_t address)
{
if (VirtualFree(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), 0, MEM_RELEASE) == 0)
{
printf("VirtualFree() failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
return false;
}
return true;
}
bool sys_virtual_protect(uint64_t address, uint64_t size, VirtualMemory::Mode mode, VirtualMemory::Mode* old_mode)
{
DWORD old_protect = 0;
if (VirtualProtect(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size, get_protection_flag(mode), &old_protect) == 0)
{
printf("VirtualProtect() failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
return false;
}
if (old_mode != nullptr)
{
*old_mode = get_protection_flag(old_protect);
}
return true;
}
bool sys_virtual_flush_instruction_cache(uint64_t address, uint64_t size)
{
if (::FlushInstructionCache(GetCurrentProcess(), reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size) == 0)
{
printf("FlushInstructionCache() failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
return false;
}
return true;
}
bool sys_virtual_patch_replace(uint64_t vaddr, uint64_t value)
{
VirtualMemory::Mode old_mode {};
sys_virtual_protect(vaddr, 8, VirtualMemory::Mode::ReadWrite, &old_mode);
auto* ptr = reinterpret_cast<uint64_t*>(vaddr);
bool ret = (*ptr != value);
*ptr = value;
sys_virtual_protect(vaddr, 8, old_mode);
if (VirtualMemory::IsExecute(old_mode))
{
sys_virtual_flush_instruction_cache(vaddr, 8);
}
return ret;
}
} // namespace Kyty::Core
#endif