mirror of
https://github.com/InoriRus/Kyty.git
synced 2026-08-28 05:06:40 +00:00
add Linux support
This commit is contained in:
@@ -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}")
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(), ×));
|
||||
|
||||
// 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(), ×))
|
||||
// {
|
||||
// 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(), ×));
|
||||
|
||||
// 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(), ×))
|
||||
// {
|
||||
// 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(), ×));
|
||||
|
||||
// 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(), ×))
|
||||
// {
|
||||
// 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
|
||||
|
||||
@@ -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
|
||||
@@ -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,4 +1,4 @@
|
||||
#include "Kyty/Sys/SysWindowsFileIO.h"
|
||||
#include "Kyty/Sys/Windows/SysWindowsFileIO.h"
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
|
||||
@@ -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), ¶m, 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), ¶m2, 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
|
||||
Reference in New Issue
Block a user