Skip to content

Commit 8d84169

Browse files
RodoMa92Gliniak
authored andcommitted
[cpu] Fix System-V ABI guest to host and host to guest thunk emitters for Linux
Upstream changes made from xenia-project#1339 and xenia-project#2228 back to canary builds. This fixes various emulation crashes caused from different calling conventions on System-V ABI platforms compared to Windows standard.
1 parent b9be601 commit 8d84169

File tree

2 files changed

+149
-8
lines changed

2 files changed

+149
-8
lines changed

src/xenia/base/memory_posix.cc

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
******************************************************************************
33
* Xenia : Xbox 360 Emulator Research Project *
44
******************************************************************************
5-
* Copyright 2020 Ben Vanik. All rights reserved. *
5+
* Copyright 2025 Ben Vanik. All rights reserved. *
66
* Released under the BSD license - see LICENSE in the root for more details. *
77
******************************************************************************
88
*/
@@ -91,13 +91,11 @@ void* AllocFixed(void* base_address, size_t length,
9191
} else {
9292
flags = MAP_PRIVATE | MAP_ANONYMOUS;
9393
}
94-
void* result = mmap(base_address, length, prot,
95-
MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
96-
if (result == MAP_FAILED) {
97-
return nullptr;
98-
} else {
94+
void* result = mmap(base_address, length, prot, flags, -1, 0);
95+
if (result != MAP_FAILED) {
9996
return result;
10097
}
98+
return nullptr;
10199
}
102100

103101
bool DeallocFixed(void* base_address, size_t length,

src/xenia/cpu/backend/x64/x64_backend.cc

Lines changed: 145 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
******************************************************************************
33
* Xenia : Xbox 360 Emulator Research Project *
44
******************************************************************************
5-
* Copyright 2019 Ben Vanik. All rights reserved. *
5+
* Copyright 2025 Ben Vanik. All rights reserved. *
66
* Released under the BSD license - see LICENSE in the root for more details. *
77
******************************************************************************
88
*/
99

1010
#include "xenia/cpu/backend/x64/x64_backend.h"
1111

12-
#include <stddef.h>
1312
#include <algorithm>
13+
#include <cstddef>
1414
#include "third_party/capstone/include/capstone/capstone.h"
1515
#include "third_party/capstone/include/capstone/x86.h"
1616

@@ -629,6 +629,7 @@ void* X64HelperEmitter::EmitCurrentForOffsets(const _code_offsets& code_offsets,
629629
return fn;
630630
}
631631
HostToGuestThunk X64HelperEmitter::EmitHostToGuestThunk() {
632+
#ifdef XE_PLATFORM_WIN32
632633
// rcx = target
633634
// rdx = arg0 (context)
634635
// r8 = arg1 (guest return address)
@@ -666,6 +667,53 @@ HostToGuestThunk X64HelperEmitter::EmitHostToGuestThunk() {
666667
mov(rdx, qword[rsp + 8 * 2]);
667668
mov(r8, qword[rsp + 8 * 3]);
668669
ret();
670+
#elif XE_PLATFORM_LINUX || XE_PLATFORM_MAC
671+
// System-V ABI args:
672+
// rdi = target
673+
// rsi = arg0 (context)
674+
// rdx = arg1 (guest return address)
675+
676+
struct _code_offsets {
677+
size_t prolog;
678+
size_t prolog_stack_alloc;
679+
size_t body;
680+
size_t epilog;
681+
size_t tail;
682+
} code_offsets = {};
683+
684+
const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
685+
686+
code_offsets.prolog = getSize();
687+
// rsp + 0 = return address
688+
mov(qword[rsp + 8 * 3], rdx);
689+
mov(qword[rsp + 8 * 2], rsi);
690+
mov(qword[rsp + 8 * 1], rdi);
691+
sub(rsp, stack_size);
692+
693+
code_offsets.prolog_stack_alloc = getSize();
694+
code_offsets.body = getSize();
695+
696+
// Save nonvolatile registers.
697+
EmitSaveNonvolatileRegs();
698+
699+
mov(rax, rdi);
700+
// mov(rsi, rsi); // context
701+
mov(rdi, ptr[rsi + offsetof(ppc::PPCContext, virtual_membase)]); // membase
702+
mov(rcx, rdx); // return address
703+
call(rax);
704+
705+
EmitLoadNonvolatileRegs();
706+
707+
code_offsets.epilog = getSize();
708+
709+
add(rsp, stack_size);
710+
mov(rdi, qword[rsp + 8 * 1]);
711+
mov(rsi, qword[rsp + 8 * 2]);
712+
mov(rdx, qword[rsp + 8 * 3]);
713+
ret();
714+
#else
715+
assert_always("Unknown platform ABI in host to guest thunk!");
716+
#endif
669717

670718
code_offsets.tail = getSize();
671719

@@ -685,6 +733,7 @@ HostToGuestThunk X64HelperEmitter::EmitHostToGuestThunk() {
685733
}
686734

687735
GuestToHostThunk X64HelperEmitter::EmitGuestToHostThunk() {
736+
#if XE_PLATFORM_WIN32
688737
// rcx = target function
689738
// rdx = arg0
690739
// r8 = arg1
@@ -716,6 +765,57 @@ GuestToHostThunk X64HelperEmitter::EmitGuestToHostThunk() {
716765

717766
add(rsp, stack_size);
718767
ret();
768+
#elif XE_PLATFORM_LINUX || XE_PLATFORM_MAC
769+
// This function is being called using the Microsoft ABI from CallNative
770+
// rcx = target function
771+
// rdx = arg0
772+
// r8 = arg1
773+
// r9 = arg2
774+
775+
// Must be translated to System-V ABI:
776+
// rdi = target function
777+
// rsi = arg0
778+
// rdx = arg1
779+
// rcx = arg2
780+
// r8, r9 - unused argument registers
781+
782+
struct _code_offsets {
783+
size_t prolog;
784+
size_t prolog_stack_alloc;
785+
size_t body;
786+
size_t epilog;
787+
size_t tail;
788+
} code_offsets = {};
789+
790+
const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
791+
792+
code_offsets.prolog = getSize();
793+
794+
// rsp + 0 = return address
795+
sub(rsp, stack_size);
796+
797+
code_offsets.prolog_stack_alloc = getSize();
798+
code_offsets.body = getSize();
799+
800+
// Save off volatile registers.
801+
EmitSaveVolatileRegs();
802+
803+
mov(rax, rcx); // function
804+
mov(rdi, GetContextReg()); // context
805+
mov(rsi, rdx); // arg0
806+
mov(rdx, r8); // arg1
807+
mov(rcx, r9); // arg2
808+
call(rax);
809+
810+
EmitLoadVolatileRegs();
811+
812+
code_offsets.epilog = getSize();
813+
814+
add(rsp, stack_size);
815+
ret();
816+
#else
817+
assert_always("Unknown platform ABI in guest to host thunk!")
818+
#endif
719819

720820
code_offsets.tail = getSize();
721821

@@ -738,6 +838,7 @@ GuestToHostThunk X64HelperEmitter::EmitGuestToHostThunk() {
738838
uint64_t ResolveFunction(void* raw_context, uint64_t target_address);
739839

740840
ResolveFunctionThunk X64HelperEmitter::EmitResolveFunctionThunk() {
841+
#if XE_PLATFORM_WIN32
741842
// ebx = target PPC address
742843
// rcx = context
743844

@@ -767,6 +868,48 @@ ResolveFunctionThunk X64HelperEmitter::EmitResolveFunctionThunk() {
767868

768869
add(rsp, stack_size);
769870
jmp(rax);
871+
#elif XE_PLATFORM_LINUX || XE_PLATFORM_MAC
872+
// Function is called with the following params:
873+
// ebx = target PPC address
874+
// rsi = context
875+
876+
// System-V ABI args:
877+
// rdi = context
878+
// rsi = target PPC address
879+
880+
struct _code_offsets {
881+
size_t prolog;
882+
size_t prolog_stack_alloc;
883+
size_t body;
884+
size_t epilog;
885+
size_t tail;
886+
} code_offsets = {};
887+
const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
888+
889+
code_offsets.prolog = getSize();
890+
891+
// rsp + 0 = return address
892+
sub(rsp, stack_size);
893+
894+
code_offsets.prolog_stack_alloc = getSize();
895+
code_offsets.body = getSize();
896+
897+
// Save volatile registers
898+
EmitSaveVolatileRegs();
899+
mov(rdi, rsi); // context
900+
mov(rsi, rbx); // target PPC address
901+
mov(rax, reinterpret_cast<uint64_t>(&ResolveFunction));
902+
call(rax);
903+
904+
EmitLoadVolatileRegs();
905+
906+
code_offsets.epilog = getSize();
907+
908+
add(rsp, stack_size);
909+
jmp(rax);
910+
#else
911+
assert_always("Unknown platform ABI in resolve function!");
912+
#endif
770913

771914
code_offsets.tail = getSize();
772915

0 commit comments

Comments
 (0)