Linux: port the emulator to a working state on Linux (#117)

Linux: port the emulator to a working state
This commit is contained in:
Stefanos Costa
2026-07-29 01:25:58 +03:00
committed by GitHub
parent 3dc793d859
commit 2b9cba457e
30 changed files with 2320 additions and 381 deletions
+109 -35
View File
@@ -46,6 +46,17 @@
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
#include <fmt/format.h>
#include <pthread_time.h>
#elif !defined(__APPLE__)
#include <sys/syscall.h>
#include <unistd.h>
#endif
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
#include <csignal> // pthread_kill is declared here, not in <pthread.h>
#endif
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS && (defined(_M_X64) || defined(__x86_64__))
#include <x86intrin.h>
#endif
#ifdef pthread_attr_getguardsize
@@ -99,8 +110,21 @@ static constexpr int KERNEL_PTHREAD_MUTEX_RECURSIVE = 2;
static constexpr int KERNEL_PTHREAD_MUTEX_NORMAL = 3;
static constexpr int KERNEL_PTHREAD_MUTEX_ADAPTIVE = 4;
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
// OS-level thread id reported to the guest.
static uint64_t GetHostThreadId() {
#if defined(__APPLE__)
uint64_t tid = 0;
pthread_threadid_np(nullptr, &tid);
return tid;
#else
return static_cast<uint64_t>(::syscall(SYS_gettid));
#endif
}
#endif
static uint64_t KernelReadTscNative() {
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && (defined(_M_X64) || defined(__x86_64__))
#if defined(_M_X64) || defined(__x86_64__)
return __rdtsc();
#else
return Common::Timer::QueryPerformanceCounter();
@@ -109,7 +133,7 @@ static uint64_t KernelReadTscNative() {
static uint64_t KernelGetTscFrequencyNative() {
static const uint64_t frequency = [] {
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && (defined(_M_X64) || defined(__x86_64__))
#if defined(_M_X64) || defined(__x86_64__)
const auto host_frequency = Common::Timer::QueryPerformanceFrequency();
if (host_frequency == 0) {
return uint64_t {1000000000};
@@ -784,7 +808,11 @@ static KYTY_SYSV_ABI void* RunOnGuestStack(void* arg, pthread_entry_func_t func,
if (g_pthread_self != nullptr) {
g_pthread_self->guest_host_rbx = host_rbx;
#if defined(__APPLE__)
g_pthread_self->guest_host_rsp = host_rsp - (2u * sizeof(uint64_t));
#else
g_pthread_self->guest_host_rsp = host_rsp - (4u * sizeof(uint64_t));
#endif
g_pthread_self->guest_host_rbp = host_rbp;
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
uintptr_t host_gs8 = 0;
@@ -794,45 +822,14 @@ static KYTY_SYSV_ABI void* RunOnGuestStack(void* arg, pthread_entry_func_t func,
: "=r"(host_gs8), "=r"(host_gs10)
:
: "memory");
g_pthread_self->guest_host_rsp -= 2u * sizeof(uint64_t);
g_pthread_self->guest_host_gs8 = host_gs8;
g_pthread_self->guest_host_gs10 = host_gs10;
#endif
}
// The guest ABI expects the entry argument in rdi and a 16-byte aligned stack before call.
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
asm volatile("pushq %%r12\n\t"
"pushq %%r13\n\t"
"pushq %%r14\n\t"
"pushq %%r15\n\t"
"movq %%gs:0x08, %%r14\n\t"
"movq %%gs:0x10, %%r15\n\t"
"xorq %%rcx, %%rcx\n\t"
"movq %%rcx, %%gs:0x08\n\t"
"movq %%rcx, %%gs:0x10\n\t"
"movq %%rsp, %%r12\n\t"
"movq %%rbp, %%r13\n\t"
"movq %[guest_rsp], %%rsp\n\t"
"movq %[guest_rbp], %%rbp\n\t"
"callq *%%rsi\n\t"
"movq %%r13, %%rbp\n\t"
"movq %%r12, %%rsp\n\t"
"movq %%r14, %%gs:0x08\n\t"
"movq %%r15, %%gs:0x10\n\t"
"popq %%r15\n\t"
"popq %%r14\n\t"
"popq %%r13\n\t"
"popq %%r12\n\t"
: "=a"(ret), "+D"(arg), "+S"(func)
: [guest_rsp] "r"(guest_rsp), [guest_rbp] "r"(guest_rbp)
: "cc", "memory", "rcx", "rdx", "r8", "r9", "r10", "r11", "xmm0", "xmm1", "xmm2",
"xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11",
"xmm12", "xmm13", "xmm14", "xmm15");
#else
// Pin the stack operands to registers the template never touches (r14/r15 are
// SysV callee-saved, so guest code preserves them): plain "r" constraints may
// allocate them into r12/r13, which the template overwrites before use.
#if defined(__APPLE__)
// Keep inputs out of r12/r13.
register uintptr_t guest_rsp_reg asm("r14") = guest_rsp;
register uintptr_t guest_rbp_reg asm("r15") = guest_rbp;
asm volatile("pushq %%r12\n\t"
@@ -851,6 +848,45 @@ static KYTY_SYSV_ABI void* RunOnGuestStack(void* arg, pthread_entry_func_t func,
: "cc", "memory", "rcx", "rdx", "r8", "r9", "r10", "r11", "xmm0", "xmm1", "xmm2",
"xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11",
"xmm12", "xmm13", "xmm14", "xmm15");
#else
// PthreadExit resumes at this frame, so all four saved registers stay on the host stack.
asm volatile("pushq %%r12\n\t"
"pushq %%r13\n\t"
"pushq %%r14\n\t"
"pushq %%r15\n\t"
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
"movq %%gs:0x08, %%r14\n\t"
"movq %%gs:0x10, %%r15\n\t"
"xorq %%rcx, %%rcx\n\t"
"movq %%rcx, %%gs:0x08\n\t"
"movq %%rcx, %%gs:0x10\n\t"
#endif
"movq %%rsp, %%r12\n\t"
"movq %%rbp, %%r13\n\t"
"movq %[guest_rsp], %%rsp\n\t"
"movq %[guest_rbp], %%rbp\n\t"
"callq *%%rsi\n\t"
"movq %%r13, %%rbp\n\t"
"movq %%r12, %%rsp\n\t"
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
"movq %%r14, %%gs:0x08\n\t"
"movq %%r15, %%gs:0x10\n\t"
#endif
"popq %%r15\n\t"
"popq %%r14\n\t"
"popq %%r13\n\t"
"popq %%r12\n\t"
: "=a"(ret), "+D"(arg), "+S"(func)
: [guest_rsp] "r"(guest_rsp), [guest_rbp] "r"(guest_rbp)
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
: "cc", "memory", "rcx", "rdx", "r8", "r9", "r10", "r11", "xmm0", "xmm1", "xmm2",
"xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11",
"xmm12", "xmm13", "xmm14", "xmm15");
#else
: "cc", "memory", "rcx", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "xmm0",
"xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10",
"xmm11", "xmm12", "xmm13", "xmm14", "xmm15");
#endif
#endif
g_guest_entry_return_rsp = 0;
@@ -886,6 +922,29 @@ static void UpdateCurrentThreadStackAttr(PthreadAttr* attr) {
(*attr)->stack_size = high - low;
(*attr)->stack_user = true;
}
#elif defined(__APPLE__)
// macOS reports a stack top and size.
void* top = pthread_get_stackaddr_np(pthread_self());
const size_t size = pthread_get_stacksize_np(pthread_self());
if (top != nullptr && size != 0) {
(*attr)->stack_addr = static_cast<void*>(static_cast<uint8_t*>(top) - size);
(*attr)->stack_size = size;
(*attr)->stack_user = true;
}
#else
// Record the main thread's stack bounds.
pthread_attr_t self_attr {};
if (pthread_getattr_np(pthread_self(), &self_attr) == 0) {
void* base = nullptr;
size_t size = 0;
if (pthread_attr_getstack(&self_attr, &base, &size) == 0 && base != nullptr && size != 0) {
(*attr)->stack_addr = base;
(*attr)->stack_size = size;
(*attr)->stack_user = true;
}
pthread_attr_destroy(&self_attr);
}
#endif
}
@@ -926,6 +985,8 @@ void PthreadInitSelfForMainThread() {
uint64_t os_thread_id = 0;
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
os_thread_id = static_cast<uint64_t>(GetCurrentThreadId());
#else
os_thread_id = GetHostThreadId();
#endif
g_pthread_self->host_thread_id = os_thread_id;
g_pthread_main = g_pthread_self;
@@ -3118,6 +3179,17 @@ uint64_t PthreadGetHostThreadId(Pthread thread) {
return thread != nullptr ? thread->host_thread_id : 0;
}
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
// Raise a host signal on another guest thread.
bool PthreadKillHost(Pthread thread, int host_signal) {
if (thread == nullptr || thread->free) {
return false;
}
return ::pthread_kill(thread->p, host_signal) == 0;
}
#endif
void PthreadQueuePendingSignal(Pthread thread, int signum) {
if (thread == nullptr || signum < 0 || signum >= 64) {
return;
@@ -3215,6 +3287,8 @@ static void* RunThread(void* arg) {
uint64_t os_thread_id = 0;
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
os_thread_id = static_cast<uint64_t>(GetCurrentThreadId());
#else
os_thread_id = GetHostThreadId();
#endif
thread->host_thread_id = os_thread_id;