From 20e5eeb39e6c20b6d2be457b8543263c9b54fbc9 Mon Sep 17 00:00:00 2001 From: svc64 Date: Sat, 16 Dec 2023 18:05:51 +0200 Subject: [PATCH] 32-bit debugger support --- src/Ryujinx.HLE/Debugger/Debugger.cs | 227 ++++++++++++++---- src/Ryujinx.HLE/Debugger/GdbXml/arm-core.xml | 27 +++ src/Ryujinx.HLE/Debugger/GdbXml/arm-neon.xml | 86 +++++++ src/Ryujinx.HLE/Debugger/GdbXml/target32.xml | 14 ++ .../GdbXml/{target.xml => target64.xml} | 0 .../Debugger/RegisterInformation.cs | 5 +- src/Ryujinx.HLE/Ryujinx.HLE.csproj | 10 +- 7 files changed, 323 insertions(+), 46 deletions(-) create mode 100644 src/Ryujinx.HLE/Debugger/GdbXml/arm-core.xml create mode 100644 src/Ryujinx.HLE/Debugger/GdbXml/arm-neon.xml create mode 100644 src/Ryujinx.HLE/Debugger/GdbXml/target32.xml rename src/Ryujinx.HLE/Debugger/GdbXml/{target.xml => target64.xml} (100%) diff --git a/src/Ryujinx.HLE/Debugger/Debugger.cs b/src/Ryujinx.HLE/Debugger/Debugger.cs index 840faf6192..d7521b17b7 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.cs @@ -51,30 +51,14 @@ namespace Ryujinx.HLE.Debugger private KThread[] GetThreads() => DebugProcess.GetThreadUids().Select(x => DebugProcess.GetThread(x)).ToArray(); private KernelContext KernelContext => Device.System.KernelContext; - const int GdbRegisterCount = 68; - - private int GdbRegisterHexSize(int gdbRegId) - { - switch (gdbRegId) - { - case >= 0 and <= 31: - return 16; - case 32: - return 16; - case 33: - return 8; - case >= 34 and <= 65: - return 32; - case 66: - return 8; - case 67: - return 8; - default: - throw new ArgumentException(); - } - } - - private string GdbReadRegister(IExecutionContext state, int gdbRegId) + const int GdbRegisterCount64 = 68; + const int GdbRegisterCount32 = 66; + /* FPCR = FPSR & ~FpcrMask + All of FPCR's bits are reserved in FPCR and vice versa, + see ARM's documentation. */ + private const uint FpcrMask = 0xfc1fffff; + + private string GdbReadRegister64(IExecutionContext state, int gdbRegId) { switch (gdbRegId) { @@ -95,7 +79,7 @@ namespace Ryujinx.HLE.Debugger } } - private bool GdbWriteRegister(IExecutionContext state, int gdbRegId, StringStream ss) + private bool GdbWriteRegister64(IExecutionContext state, int gdbRegId, StringStream ss) { switch (gdbRegId) { @@ -107,7 +91,7 @@ namespace Ryujinx.HLE.Debugger } case 32: { - ulong value = ss.ReadLengthAsHex(8); + ulong value = ss.ReadLengthAsHex(16); state.DebugPc = value; return true; } @@ -141,6 +125,83 @@ namespace Ryujinx.HLE.Debugger } } + private string GdbReadRegister32(IExecutionContext state, int gdbRegId) + { + switch (gdbRegId) + { + case >= 0 and <= 14: + return ToHex(BitConverter.GetBytes((uint)state.GetX(gdbRegId))); + case 15: + return ToHex(BitConverter.GetBytes((uint)state.DebugPc)); + case 16: + return ToHex(BitConverter.GetBytes((uint)state.Pstate)); + case >= 17 and <= 32: + return ToHex(state.GetV(gdbRegId - 17).ToArray()); + case >= 33 and <= 64: + int reg = (gdbRegId - 33); + int n = reg / 2; + int shift = reg % 2; + ulong value = state.GetV(n).Extract(shift); + return ToHex(BitConverter.GetBytes(value)); + case 65: + uint fpscr = (uint)state.Fpsr | (uint)state.Fpcr; + return ToHex(BitConverter.GetBytes(fpscr)); + default: + return null; + } + } + + private bool GdbWriteRegister32(IExecutionContext state, int gdbRegId, StringStream ss) + { + switch (gdbRegId) + { + case >= 0 and <= 14: + { + ulong value = ss.ReadLengthAsHex(8); + state.SetX(gdbRegId, value); + return true; + } + case 15: + { + ulong value = ss.ReadLengthAsHex(8); + state.DebugPc = value; + return true; + } + case 16: + { + ulong value = ss.ReadLengthAsHex(8); + state.Pstate = (uint)value; + return true; + } + case >= 17 and <= 32: + { + ulong value0 = ss.ReadLengthAsHex(16); + ulong value1 = ss.ReadLengthAsHex(16); + state.SetV(gdbRegId - 17, new V128(value0, value1)); + return true; + } + case >= 33 and <= 64: + { + ulong value = ss.ReadLengthAsHex(16); + int regId = (gdbRegId - 33); + int regNum = regId / 2; + int shift = regId % 2; + V128 reg = state.GetV(regNum); + reg.Insert(shift, value); + return true; + } + case 65: + { + ulong value = ss.ReadLengthAsHex(8); + state.Fpsr = (uint)value & FpcrMask; + state.Fpcr = (uint)value & ~FpcrMask; + return true; + } + default: + return false; + } + } + private void MessageHandlerMain() { while (!_shuttingDown) @@ -267,15 +328,31 @@ namespace Ryujinx.HLE.Debugger if (ss.ConsumeRemaining("HostInfo")) { - Reply( - $"triple:{ToHex("aarch64-unknown-linux-android")};endian:little;ptrsize:8;hostname:{ToHex("Ryujinx")};"); + if (IsProcessAarch32()) + { + Reply( + $"triple:{ToHex("arm-unknown-linux-android")};endian:little;ptrsize:4;hostname:{ToHex("Ryujinx")};"); + } + else + { + Reply( + $"triple:{ToHex("aarch64-unknown-linux-android")};endian:little;ptrsize:8;hostname:{ToHex("Ryujinx")};"); + } break; } if (ss.ConsumeRemaining("ProcessInfo")) { - Reply( - $"pid:1;cputype:100000c;cpusubtype:0;triple:{ToHex("aarch64-unknown-linux-android")};ostype:unknown;vendor:none;endian:little;ptrsize:8;"); + if (IsProcessAarch32()) + { + Reply( + $"pid:1;cputype:12;cpusubtype:0;triple:{ToHex("arm-unknown-linux-android")};ostype:unknown;vendor:none;endian:little;ptrsize:4;"); + } + else + { + Reply( + $"pid:1;cputype:100000c;cpusubtype:0;triple:{ToHex("aarch64-unknown-linux-android")};ostype:unknown;vendor:none;endian:little;ptrsize:8;"); + } break; } @@ -323,6 +400,11 @@ namespace Ryujinx.HLE.Debugger ulong addr = ss.ReadUntilAsHex(','); ulong len = ss.ReadRemainingAsHex(); + if (feature == "target.xml") + { + feature = IsProcessAarch32() ? "target32.xml" : "target64.xml"; + } + string data; if (RegisterInformation.Features.TryGetValue(feature, out data)) { @@ -410,9 +492,19 @@ namespace Ryujinx.HLE.Debugger var ctx = DebugProcess.GetThread(gThread.Value).Context; string registers = ""; - for (int i = 0; i < GdbRegisterCount; i++) + if (IsProcessAarch32()) { - registers += GdbReadRegister(ctx, i); + for (int i = 0; i < GdbRegisterCount32; i++) + { + registers += GdbReadRegister32(ctx, i); + } + } + else + { + for (int i = 0; i < GdbRegisterCount64; i++) + { + registers += GdbReadRegister64(ctx, i); + } } Reply(registers); @@ -427,12 +519,26 @@ namespace Ryujinx.HLE.Debugger } var ctx = DebugProcess.GetThread(gThread.Value).Context; - for (int i = 0; i < GdbRegisterCount; i++) + if (IsProcessAarch32()) { - if (!GdbWriteRegister(ctx, i, ss)) + for (int i = 0; i < GdbRegisterCount32; i++) { - ReplyError(); - return; + if (!GdbWriteRegister32(ctx, i, ss)) + { + ReplyError(); + return; + } + } + } + else + { + for (int i = 0; i < GdbRegisterCount64; i++) + { + if (!GdbWriteRegister64(ctx, i, ss)) + { + ReplyError(); + return; + } } } @@ -512,14 +618,30 @@ namespace Ryujinx.HLE.Debugger } var ctx = DebugProcess.GetThread(gThread.Value).Context; - string result = GdbReadRegister(ctx, gdbRegId); - if (result != null) + string result; + if (IsProcessAarch32()) { - Reply(result); + result = GdbReadRegister32(ctx, gdbRegId); + if (result != null) + { + Reply(result); + } + else + { + ReplyError(); + } } else { - ReplyError(); + result = GdbReadRegister64(ctx, gdbRegId); + if (result != null) + { + Reply(result); + } + else + { + ReplyError(); + } } } @@ -532,13 +654,27 @@ namespace Ryujinx.HLE.Debugger } var ctx = DebugProcess.GetThread(gThread.Value).Context; - if (GdbWriteRegister(ctx, gdbRegId, ss) && ss.IsEmpty()) + if (IsProcessAarch32()) { - ReplyOK(); + if (GdbWriteRegister32(ctx, gdbRegId, ss) && ss.IsEmpty()) + { + ReplyOK(); + } + else + { + ReplyError(); + } } else { - ReplyError(); + if (GdbWriteRegister64(ctx, gdbRegId, ss) && ss.IsEmpty()) + { + ReplyOK(); + } + else + { + ReplyError(); + } } } @@ -675,6 +811,11 @@ namespace Ryujinx.HLE.Debugger } } + private bool IsProcessAarch32() + { + return DebugProcess.GetThread(gThread.Value).Context.IsAarch32; + } + private byte CalculateChecksum(string cmd) { byte checksum = 0; diff --git a/src/Ryujinx.HLE/Debugger/GdbXml/arm-core.xml b/src/Ryujinx.HLE/Debugger/GdbXml/arm-core.xml new file mode 100644 index 0000000000..2307d65f9b --- /dev/null +++ b/src/Ryujinx.HLE/Debugger/GdbXml/arm-core.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Ryujinx.HLE/Debugger/GdbXml/arm-neon.xml b/src/Ryujinx.HLE/Debugger/GdbXml/arm-neon.xml new file mode 100644 index 0000000000..d61f6b8549 --- /dev/null +++ b/src/Ryujinx.HLE/Debugger/GdbXml/arm-neon.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Ryujinx.HLE/Debugger/GdbXml/target32.xml b/src/Ryujinx.HLE/Debugger/GdbXml/target32.xml new file mode 100644 index 0000000000..4f746efbac --- /dev/null +++ b/src/Ryujinx.HLE/Debugger/GdbXml/target32.xml @@ -0,0 +1,14 @@ + + + + + + arm + + + diff --git a/src/Ryujinx.HLE/Debugger/GdbXml/target.xml b/src/Ryujinx.HLE/Debugger/GdbXml/target64.xml similarity index 100% rename from src/Ryujinx.HLE/Debugger/GdbXml/target.xml rename to src/Ryujinx.HLE/Debugger/GdbXml/target64.xml diff --git a/src/Ryujinx.HLE/Debugger/RegisterInformation.cs b/src/Ryujinx.HLE/Debugger/RegisterInformation.cs index 1ad70dff73..0ba9b558c2 100644 --- a/src/Ryujinx.HLE/Debugger/RegisterInformation.cs +++ b/src/Ryujinx.HLE/Debugger/RegisterInformation.cs @@ -7,9 +7,12 @@ namespace Ryujinx.HLE.Debugger { public static readonly Dictionary Features = new() { - { "target.xml", GetEmbeddedResourceContent("target.xml") }, + { "target64.xml", GetEmbeddedResourceContent("target64.xml") }, + { "target32.xml", GetEmbeddedResourceContent("target32.xml") }, { "aarch64-core.xml", GetEmbeddedResourceContent("aarch64-core.xml") }, { "aarch64-fpu.xml", GetEmbeddedResourceContent("aarch64-fpu.xml") }, + { "arm-core.xml", GetEmbeddedResourceContent("arm-core.xml") }, + { "arm-neon.xml", GetEmbeddedResourceContent("arm-neon.xml") }, }; private static string GetEmbeddedResourceContent(string resourceName) diff --git a/src/Ryujinx.HLE/Ryujinx.HLE.csproj b/src/Ryujinx.HLE/Ryujinx.HLE.csproj index fa6a0a5794..44fdecc1e3 100644 --- a/src/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/src/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -32,7 +32,10 @@ - + + + + @@ -44,7 +47,10 @@ - + + + +