From a1de4f1b5b9df46474e7357b70d00435321cd8a2 Mon Sep 17 00:00:00 2001 From: merry Date: Wed, 9 Feb 2022 21:29:53 +0000 Subject: [PATCH] Debugger: Do more stuff # Conflicts: # src/Ryujinx.HLE/Debugger/Debugger.cs # src/Ryujinx.HLE/Debugger/GdbXml/aarch64-core.xml # src/Ryujinx.HLE/Debugger/GdbXml/aarch64-fpu.xml # src/Ryujinx.HLE/Debugger/GdbXml/target.xml # src/Ryujinx.HLE/Debugger/RegisterInformation.cs --- src/Ryujinx.HLE/Debugger/Debugger.cs | 184 +++++++++++++----- .../Debugger/GdbXml/aarch64-core.xml | 93 +++++++++ .../Debugger/GdbXml/aarch64-fpu.xml | 159 +++++++++++++++ src/Ryujinx.HLE/Debugger/GdbXml/target.xml | 14 ++ .../Debugger/RegisterInformation.cs | 25 +++ src/Ryujinx.HLE/Debugger/StringStream.cs | 36 +++- src/Ryujinx.HLE/Ryujinx.HLE.csproj | 6 + 7 files changed, 470 insertions(+), 47 deletions(-) create mode 100644 src/Ryujinx.HLE/Debugger/GdbXml/aarch64-core.xml create mode 100644 src/Ryujinx.HLE/Debugger/GdbXml/aarch64-fpu.xml create mode 100644 src/Ryujinx.HLE/Debugger/GdbXml/target.xml create mode 100644 src/Ryujinx.HLE/Debugger/RegisterInformation.cs diff --git a/src/Ryujinx.HLE/Debugger/Debugger.cs b/src/Ryujinx.HLE/Debugger/Debugger.cs index 9bc3c2aa6e..ce83b366dc 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.cs @@ -3,7 +3,6 @@ using Ryujinx.Common.Logging; using Ryujinx.Memory; using System; using System.Collections.Concurrent; -using System.Diagnostics; using System.Linq; using System.Net; using System.Net.Sockets; @@ -70,31 +69,31 @@ namespace Ryujinx.HLE.Debugger switch (gdbRegId) { case >= 0 and <= 31: - return $"{state.GetX(gdbRegId):x16}"; + return ToHex(BitConverter.GetBytes(state.GetX(gdbRegId))); case 32: - return $"{state.DebugPc:x16}"; + return ToHex(BitConverter.GetBytes(state.DebugPc)); case 33: - return $"{state.Pstate:x8}"; + return ToHex(BitConverter.GetBytes(state.Pstate)); default: - throw new ArgumentException(); + return null; } } - private void GdbWriteRegister(Ryujinx.Cpu.IExecutionContext state, int gdbRegId, ulong value) + private bool GdbWriteRegister(Ryujinx.Cpu.IExecutionContext state, int gdbRegId, ulong value) { switch (gdbRegId) { case >= 0 and <= 31: state.SetX(gdbRegId, value); - return; + return true; case 32: state.DebugPc = value; - return; + return true; case 33: state.Pstate = (uint)value; - return; + return true; default: - throw new ArgumentException(); + return false; } } @@ -137,7 +136,7 @@ namespace Ryujinx.HLE.Debugger goto unknownCommand; } // Enable extended mode - Reply("OK"); + ReplyOK(); break; case '?': if (!ss.IsEmpty()) @@ -205,29 +204,74 @@ namespace Ryujinx.HLE.Debugger break; } case 'q': - switch (ss.ReadUntil(':')) + if (ss.ConsumeRemaining("GDBServerVersion")) { - case "GDBServerVersion": - Reply($"name:Ryujinx;version:{ReleaseInformations.GetVersion()};"); - break; - case "HostInfo": - Reply($"triple:{ToHex("aarch64-none-elf")};endian:little;ptrsize:8;hostname:{ToHex("Ryujinx")};"); - break; - case "ProcessInfo": - Reply("pid:1;cputype:100000c;cpusubtype:0;ostype:unknown;vendor:none;endian:little;ptrsize:8;"); - break; - case "fThreadInfo": - Reply($"m {string.Join(",", GetThreadIds().Select(x => $"{x:x}"))}"); - break; - case "sThreadInfo": - Reply("l"); - break; - default: - goto unknownCommand; + Reply($"name:Ryujinx;version:{ReleaseInformation.GetVersion()};"); + break; } - break; + if (ss.ConsumeRemaining("HostInfo")) + { + 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;"); + break; + } + if (ss.ConsumePrefix("Supported:") || ss.ConsumeRemaining("Supported")) + { + Reply("PacketSize=10000,qXfer:features:read+"); + break; + } + if (ss.ConsumeRemaining("fThreadInfo")) + { + Reply($"m{string.Join(",", GetThreadIds().Select(x => $"{x:x}"))}"); + break; + } + if (ss.ConsumeRemaining("sThreadInfo")) + { + Reply("l"); + break; + } + if (ss.ConsumePrefix("Xfer:features:read:")) + { + string feature = ss.ReadUntil(':'); + ulong addr = ss.ReadUntilAsHex(','); + ulong len = ss.ReadRemainingAsHex(); + + string data; + if (RegisterInformation.Features.TryGetValue(feature, out data)) + { + if (addr >= (ulong)data.Length) + { + Reply("l"); + break; + } + + if (len >= (ulong)data.Length - addr) + { + Reply("l" + data.Substring((int)addr)); + break; + } + else + { + Reply("m" + data.Substring((int)addr, (int)len)); + break; + } + } + else + { + Reply("E00"); // Invalid annex + break; + } + } + goto unknownCommand; + case 'Q': + goto unknownCommand; default: - Logger.Notice.Print(LogClass.GdbStub, $"Unknown command: {cmd}"); + unknownCommand: + // Logger.Notice.Print(LogClass.GdbStub, $"Unknown command: {cmd}"); Reply(""); break; } @@ -276,9 +320,16 @@ namespace Ryujinx.HLE.Debugger var ctx = GetThread(gThread); for (int i = 0; i < GdbRegisterCount; i++) { - GdbWriteRegister(ctx, i, ss.ReadLengthAsHex(GdbRegisterHexSize(i))); + GdbWriteRegister(ctx, i, ss.ReadLengthAsLEHex(GdbRegisterHexSize(i))); + } + if (ss.IsEmpty()) + { + ReplyOK(); + } + else + { + ReplyError(); } - Reply(ss.IsEmpty() ? "OK" : "E99"); } void CommandSetThread(char op, ulong threadId) @@ -287,53 +338,93 @@ namespace Ryujinx.HLE.Debugger { case 'c': cThread = threadId; - Reply("OK"); + ReplyOK(); return; case 'g': gThread = threadId; - Reply("OK"); + ReplyOK(); return; default: - Reply("E99"); + ReplyError(); return; } } void CommandReadMemory(ulong addr, ulong len) { - var data = new byte[len]; - GetMemory().Read(addr, data); - Reply(ToHex(data)); + try + { + var data = new byte[len]; + GetMemory().Read(addr, data); + Reply(ToHex(data)); + } + catch (InvalidMemoryRegionException) + { + ReplyError(); + } } void CommandWriteMemory(ulong addr, ulong len, StringStream ss) { - var data = new byte[len]; - for (ulong i = 0; i < len; i++) + try { - data[i] = (byte)ss.ReadLengthAsHex(2); + var data = new byte[len]; + for (ulong i = 0; i < len; i++) + { + data[i] = (byte)ss.ReadLengthAsHex(2); + } + GetMemory().Write(addr, data); + ReplyOK(); + } + catch (InvalidMemoryRegionException) + { + ReplyError(); } - GetMemory().Write(addr, data); } void CommandReadGeneralRegister(int gdbRegId) { var ctx = GetThread(gThread); - Reply(GdbReadRegister(ctx, gdbRegId)); + string result = GdbReadRegister(ctx, gdbRegId); + if (result != null) + { + Reply(result); + } + else + { + ReplyError(); + } } void CommandWriteGeneralRegister(int gdbRegId, ulong value) { var ctx = GetThread(gThread); - GdbWriteRegister(ctx, gdbRegId, value); - Reply("OK"); + if (GdbWriteRegister(ctx, gdbRegId, value)) + { + ReplyOK(); + } + else + { + ReplyError(); + } } private void Reply(string cmd) { + Logger.Notice.Print(LogClass.GdbStub, $"Reply: {cmd}"); WriteStream.Write(Encoding.ASCII.GetBytes($"${cmd}#{CalculateChecksum(cmd):x2}")); } + private void ReplyOK() + { + Reply("OK"); + } + + private void ReplyError() + { + Reply("E01"); + } + private void SocketReaderThreadMain() { restartListen: @@ -345,6 +436,7 @@ namespace Ryujinx.HLE.Debugger Logger.Notice.Print(LogClass.GdbStub, $"Currently waiting on {endpoint} for GDB client"); ClientSocket = ListenerSocket.AcceptSocket(); + ClientSocket.NoDelay = true; ReadStream = new NetworkStream(ClientSocket, System.IO.FileAccess.Read); WriteStream = new NetworkStream(ClientSocket, System.IO.FileAccess.Write); Logger.Notice.Print(LogClass.GdbStub, "GDB client connected"); diff --git a/src/Ryujinx.HLE/Debugger/GdbXml/aarch64-core.xml b/src/Ryujinx.HLE/Debugger/GdbXml/aarch64-core.xml new file mode 100644 index 0000000000..22f2d865e3 --- /dev/null +++ b/src/Ryujinx.HLE/Debugger/GdbXml/aarch64-core.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Ryujinx.HLE/Debugger/GdbXml/aarch64-fpu.xml b/src/Ryujinx.HLE/Debugger/GdbXml/aarch64-fpu.xml new file mode 100644 index 0000000000..9e453016c3 --- /dev/null +++ b/src/Ryujinx.HLE/Debugger/GdbXml/aarch64-fpu.xml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Ryujinx.HLE/Debugger/GdbXml/target.xml b/src/Ryujinx.HLE/Debugger/GdbXml/target.xml new file mode 100644 index 0000000000..d83c066e71 --- /dev/null +++ b/src/Ryujinx.HLE/Debugger/GdbXml/target.xml @@ -0,0 +1,14 @@ + + + + + + aarch64 + + + diff --git a/src/Ryujinx.HLE/Debugger/RegisterInformation.cs b/src/Ryujinx.HLE/Debugger/RegisterInformation.cs new file mode 100644 index 0000000000..1ad70dff73 --- /dev/null +++ b/src/Ryujinx.HLE/Debugger/RegisterInformation.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using System.IO; + +namespace Ryujinx.HLE.Debugger +{ + class RegisterInformation + { + public static readonly Dictionary Features = new() + { + { "target.xml", GetEmbeddedResourceContent("target.xml") }, + { "aarch64-core.xml", GetEmbeddedResourceContent("aarch64-core.xml") }, + { "aarch64-fpu.xml", GetEmbeddedResourceContent("aarch64-fpu.xml") }, + }; + + private static string GetEmbeddedResourceContent(string resourceName) + { + Stream stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("Ryujinx.HLE.Debugger.GdbXml." + resourceName); + StreamReader reader = new StreamReader(stream); + string result = reader.ReadToEnd(); + reader.Dispose(); + stream.Dispose(); + return result; + } + } +} diff --git a/src/Ryujinx.HLE/Debugger/StringStream.cs b/src/Ryujinx.HLE/Debugger/StringStream.cs index b654f6f546..3197d2e55e 100644 --- a/src/Ryujinx.HLE/Debugger/StringStream.cs +++ b/src/Ryujinx.HLE/Debugger/StringStream.cs @@ -1,4 +1,5 @@ -using System.Globalization; +using System.Diagnostics; +using System.Globalization; namespace Ryujinx.HLE.Debugger { @@ -60,6 +61,39 @@ namespace Ryujinx.HLE.Debugger return ulong.Parse(ReadLength(len), NumberStyles.HexNumber); } + public ulong ReadLengthAsLEHex(int len) + { + Debug.Assert(len % 2 == 0); + + ulong result = 0; + int pos = 0; + while (pos < len) + { + result += ReadLengthAsHex(2) << (4 * pos); + pos += 2; + } + return result; + } + public bool ConsumePrefix(string prefix) + { + if (Data.Substring(Position).StartsWith(prefix)) + { + Position += prefix.Length; + return true; + } + return false; + } + + public bool ConsumeRemaining(string match) + { + if (Data.Substring(Position) == match) + { + Position += match.Length; + return true; + } + return false; + } + public bool IsEmpty() { return Position >= Data.Length; diff --git a/src/Ryujinx.HLE/Ryujinx.HLE.csproj b/src/Ryujinx.HLE/Ryujinx.HLE.csproj index 0fcf9e4b57..fa6a0a5794 100644 --- a/src/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/src/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -30,6 +30,9 @@ + + + @@ -39,6 +42,9 @@ + + +