diff options
author | gdkchan <gab.dark.100@gmail.com> | 2018-11-28 20:18:09 -0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-28 20:18:09 -0200 |
commit | 00579927e43bf55ee06ae02933c1e755fb4120eb (patch) | |
tree | 0fd06db7b28e0accf87b465ec6f4dc74691febab /Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs | |
parent | e7fe7d724778535f8eff390abef54274a343c0b7 (diff) |
Better process implementation (#491)
* Initial implementation of KProcess
* Some improvements to the memory manager, implement back guest stack trace printing
* Better GetInfo implementation, improve checking in some places with information from process capabilities
* Allow the cpu to read/write from the correct memory locations for accesses crossing a page boundary
* Change long -> ulong for address/size on memory related methods to avoid unnecessary casts
* Attempt at implementing ldr:ro with new KProcess
* Allow BSS with size 0 on ldr:ro
* Add checking for memory block slab heap usage, return errors if full, exit gracefully
* Use KMemoryBlockSize const from KMemoryManager
* Allow all methods to read from non-contiguous locations
* Fix for TransactParcelAuto
* Address PR feedback, additionally fix some small issues related to the KIP loader and implement SVCs GetProcessId, GetProcessList, GetSystemInfo, CreatePort and ManageNamedPort
* Fix wrong check for source pages count from page list on MapPhysicalMemory
* Fix some issues with UnloadNro on ldr:ro
Diffstat (limited to 'Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs')
-rw-r--r-- | Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs b/Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs new file mode 100644 index 00000000..a6053b1b --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs @@ -0,0 +1,310 @@ +using ChocolArm64.Memory; +using ChocolArm64.State; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Diagnostics.Demangler; +using Ryujinx.HLE.Loaders.Elf; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; + +namespace Ryujinx.HLE.HOS.Kernel +{ + class HleProcessDebugger + { + private const int Mod0 = 'M' << 0 | 'O' << 8 | 'D' << 16 | '0' << 24; + + private KProcess Owner; + + private class Image + { + public long BaseAddress { get; private set; } + + public ElfSymbol[] Symbols { get; private set; } + + public Image(long BaseAddress, ElfSymbol[] Symbols) + { + this.BaseAddress = BaseAddress; + this.Symbols = Symbols; + } + } + + private List<Image> Images; + + private int Loaded; + + public HleProcessDebugger(KProcess Owner) + { + this.Owner = Owner; + + Images = new List<Image>(); + } + + public void PrintGuestStackTrace(CpuThreadState ThreadState) + { + EnsureLoaded(); + + StringBuilder Trace = new StringBuilder(); + + Trace.AppendLine("Guest stack trace:"); + + void AppendTrace(long Address) + { + Image Image = GetImage(Address, out int ImageIndex); + + if (Image == null || !TryGetSubName(Image, Address, out string SubName)) + { + SubName = $"Sub{Address:x16}"; + } + else if (SubName.StartsWith("_Z")) + { + SubName = Demangler.Parse(SubName); + } + + if (Image != null) + { + long Offset = Address - Image.BaseAddress; + + string ImageName = GetGuessedNsoNameFromIndex(ImageIndex); + + string ImageNameAndOffset = $"[{Owner.Name}] {ImageName}:0x{Offset:x8}"; + + Trace.AppendLine($" {ImageNameAndOffset} {SubName}"); + } + else + { + Trace.AppendLine($" [{Owner.Name}] ??? {SubName}"); + } + } + + long FramePointer = (long)ThreadState.X29; + + while (FramePointer != 0) + { + if ((FramePointer & 7) != 0 || + !Owner.CpuMemory.IsMapped(FramePointer) || + !Owner.CpuMemory.IsMapped(FramePointer + 8)) + { + break; + } + + //Note: This is the return address, we need to subtract one instruction + //worth of bytes to get the branch instruction address. + AppendTrace(Owner.CpuMemory.ReadInt64(FramePointer + 8) - 4); + + FramePointer = Owner.CpuMemory.ReadInt64(FramePointer); + } + + Logger.PrintInfo(LogClass.Cpu, Trace.ToString()); + } + + private bool TryGetSubName(Image Image, long Address, out string Name) + { + Address -= Image.BaseAddress; + + int Left = 0; + int Right = Image.Symbols.Length - 1; + + while (Left <= Right) + { + int Size = Right - Left; + + int Middle = Left + (Size >> 1); + + ElfSymbol Symbol = Image.Symbols[Middle]; + + long EndAddr = Symbol.Value + Symbol.Size; + + if ((ulong)Address >= (ulong)Symbol.Value && (ulong)Address < (ulong)EndAddr) + { + Name = Symbol.Name; + + return true; + } + + if ((ulong)Address < (ulong)Symbol.Value) + { + Right = Middle - 1; + } + else + { + Left = Middle + 1; + } + } + + Name = null; + + return false; + } + + private Image GetImage(long Address, out int Index) + { + lock (Images) + { + for (Index = Images.Count - 1; Index >= 0; Index--) + { + if ((ulong)Address >= (ulong)Images[Index].BaseAddress) + { + return Images[Index]; + } + } + } + + return null; + } + + private string GetGuessedNsoNameFromIndex(int Index) + { + if ((uint)Index > 11) + { + return "???"; + } + + if (Index == 0) + { + return "rtld"; + } + else if (Index == 1) + { + return "main"; + } + else if (Index == GetImagesCount() - 1) + { + return "sdk"; + } + else + { + return "subsdk" + (Index - 2); + } + } + + private int GetImagesCount() + { + lock (Images) + { + return Images.Count; + } + } + + private void EnsureLoaded() + { + if (Interlocked.CompareExchange(ref Loaded, 1, 0) == 0) + { + ScanMemoryForTextSegments(); + } + } + + private void ScanMemoryForTextSegments() + { + ulong OldAddress = 0; + ulong Address = 0; + + while (Address >= OldAddress) + { + KMemoryInfo Info = Owner.MemoryManager.QueryMemory(Address); + + if (Info.State == MemoryState.Reserved) + { + break; + } + + if (Info.State == MemoryState.CodeStatic && Info.Permission == MemoryPermission.ReadAndExecute) + { + LoadMod0Symbols(Owner.CpuMemory, (long)Info.Address); + } + + OldAddress = Address; + + Address = Info.Address + Info.Size; + } + } + + private void LoadMod0Symbols(MemoryManager Memory, long TextOffset) + { + long Mod0Offset = TextOffset + Memory.ReadUInt32(TextOffset + 4); + + if (Mod0Offset < TextOffset || !Memory.IsMapped(Mod0Offset) || (Mod0Offset & 3) != 0) + { + return; + } + + Dictionary<ElfDynamicTag, long> Dynamic = new Dictionary<ElfDynamicTag, long>(); + + int Mod0Magic = Memory.ReadInt32(Mod0Offset + 0x0); + + if (Mod0Magic != Mod0) + { + return; + } + + long DynamicOffset = Memory.ReadInt32(Mod0Offset + 0x4) + Mod0Offset; + long BssStartOffset = Memory.ReadInt32(Mod0Offset + 0x8) + Mod0Offset; + long BssEndOffset = Memory.ReadInt32(Mod0Offset + 0xc) + Mod0Offset; + long EhHdrStartOffset = Memory.ReadInt32(Mod0Offset + 0x10) + Mod0Offset; + long EhHdrEndOffset = Memory.ReadInt32(Mod0Offset + 0x14) + Mod0Offset; + long ModObjOffset = Memory.ReadInt32(Mod0Offset + 0x18) + Mod0Offset; + + while (true) + { + long TagVal = Memory.ReadInt64(DynamicOffset + 0); + long Value = Memory.ReadInt64(DynamicOffset + 8); + + DynamicOffset += 0x10; + + ElfDynamicTag Tag = (ElfDynamicTag)TagVal; + + if (Tag == ElfDynamicTag.DT_NULL) + { + break; + } + + Dynamic[Tag] = Value; + } + + if (!Dynamic.TryGetValue(ElfDynamicTag.DT_STRTAB, out long StrTab) || + !Dynamic.TryGetValue(ElfDynamicTag.DT_SYMTAB, out long SymTab) || + !Dynamic.TryGetValue(ElfDynamicTag.DT_SYMENT, out long SymEntSize)) + { + return; + } + + long StrTblAddr = TextOffset + StrTab; + long SymTblAddr = TextOffset + SymTab; + + List<ElfSymbol> Symbols = new List<ElfSymbol>(); + + while ((ulong)SymTblAddr < (ulong)StrTblAddr) + { + ElfSymbol Sym = GetSymbol(Memory, SymTblAddr, StrTblAddr); + + Symbols.Add(Sym); + + SymTblAddr += SymEntSize; + } + + lock (Images) + { + Images.Add(new Image(TextOffset, Symbols.OrderBy(x => x.Value).ToArray())); + } + } + + private ElfSymbol GetSymbol(MemoryManager Memory, long Address, long StrTblAddr) + { + int NameIndex = Memory.ReadInt32(Address + 0); + int Info = Memory.ReadByte (Address + 4); + int Other = Memory.ReadByte (Address + 5); + int SHIdx = Memory.ReadInt16(Address + 6); + long Value = Memory.ReadInt64(Address + 8); + long Size = Memory.ReadInt64(Address + 16); + + string Name = string.Empty; + + for (int Chr; (Chr = Memory.ReadByte(StrTblAddr + NameIndex++)) != 0;) + { + Name += (char)Chr; + } + + return new ElfSymbol(Name, Info, Other, SHIdx, Value, Size); + } + } +}
\ No newline at end of file |