From a036bc1a487c4330adabf2509f6cd34e862fba8a Mon Sep 17 00:00:00 2001 From: ParantezTech <12572227+par274@users.noreply.github.com> Date: Wed, 23 Sep 2026 14:46:17 +0300 Subject: [PATCH] [CPU] Host RBX is restored from the saved host frame below --- .../Cpu/Native/DirectExecutionBackend.cs | 2 ++ .../Cpu/Gen5NativeReturnSmokeTests.cs | 34 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs index 1de1af5..5fdce4d 100644 --- a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs +++ b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs @@ -2610,6 +2610,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I byte* code = (byte*)ptr; int offset = 0; + EmitByte(code, ref offset, 0x48); EmitByte(code, ref offset, 0x89); EmitByte(code, ref offset, 0xC3); // mov rbx,rax EmitByte(code, ref offset, 0x48); // sub rsp, 0x20 EmitByte(code, ref offset, 0x83); EmitByte(code, ref offset, 0xEC); @@ -2639,6 +2640,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I EmitByte(code, ref offset, 0x5E); EmitByte(code, ref offset, 0x5F); EmitByte(code, ref offset, 0x5D); + EmitByte(code, ref offset, 0x48); EmitByte(code, ref offset, 0x89); EmitByte(code, ref offset, 0xD8); // mov rax,rbx EmitByte(code, ref offset, 0x5B); EmitByte(code, ref offset, 0xC3); diff --git a/tests/SharpEmu.Libs.Tests/Cpu/Gen5NativeReturnSmokeTests.cs b/tests/SharpEmu.Libs.Tests/Cpu/Gen5NativeReturnSmokeTests.cs index 9426786..6a85028 100644 --- a/tests/SharpEmu.Libs.Tests/Cpu/Gen5NativeReturnSmokeTests.cs +++ b/tests/SharpEmu.Libs.Tests/Cpu/Gen5NativeReturnSmokeTests.cs @@ -82,7 +82,24 @@ public sealed class Gen5NativeReturnSmokeTests ]); } - private static void ExecuteSyntheticGuest(byte[]? callbackInstructions = null) + [Fact] + public async Task SyntheticGen5Continuation_PreservesFullReturnValue() + { + if (!IsSupportedHost) + return; + + if (Environment.GetEnvironmentVariable(WorkerEnvironmentVariable) != "1") + { + var result = await RunIsolatedWorker(nameof(SyntheticGen5Continuation_PreservesFullReturnValue)); + Assert.True(result.Completed, result.Output); + Assert.True(result.ExitCode == 0, result.Output); + return; + } + + ExecuteSyntheticGuest(continuation: true); + } + + private static void ExecuteSyntheticGuest(byte[]? callbackInstructions = null, bool continuation = false) { using var memory = new PhysicalVirtualMemory(); var image = new SelfLoader().Load(BuildSyntheticElf(callbackInstructions), memory); @@ -119,6 +136,21 @@ public sealed class Gen5NativeReturnSmokeTests Assert.Null(dispatcher.LastNotImplementedInfo); var callerContext = new CpuContext(new TrackedCpuMemory(memory), Generation.Gen5); + if (continuation) + { + Assert.True(memory.TryAllocateAtOrAbove(0x1_0000_0000, 0x4000, false, 0x4000, out var stack)); + callerContext[CpuRegister.Rsp] = stack + 0x4000 - sizeof(ulong); + // Resume directly into mov rax, imm64; ret through the shared guest return stub. + var execute = typeof(DirectExecutionBackend).GetMethod( + "ExecuteGuestContinuationEntry", BindingFlags.Instance | BindingFlags.NonPublic)!; + object?[] arguments = [callerContext, image.EntryPoint + 3, callerContext[CpuRegister.Rsp], + "synthetic-native-continuation-return", null]; + var exitReason = execute.Invoke(backend, arguments); + Assert.True(exitReason?.ToString() == "Returned", arguments[4]?.ToString()); + Assert.Equal(CallbackReturnValue, callerContext[CpuRegister.Rax]); + return; + } + ulong dataAddress = 0; var recoveryCounter = typeof(DirectExecutionBackend).GetField( "_sse4aInstructionsEmulated", BindingFlags.Static | BindingFlags.NonPublic)!;