diff --git a/ChocolArm64/AOptimizations.cs b/ChocolArm64/AOptimizations.cs index fbf26a491..40e1674a4 100644 --- a/ChocolArm64/AOptimizations.cs +++ b/ChocolArm64/AOptimizations.cs @@ -2,8 +2,6 @@ using System.Runtime.Intrinsics.X86; public static class AOptimizations { - public static bool GenerateCallStack = true; - private static bool UseAllSseIfAvailable = true; private static bool UseSseIfAvailable = true; diff --git a/ChocolArm64/ATranslatedSub.cs b/ChocolArm64/ATranslatedSub.cs index 9dbc378ec..a11da2646 100644 --- a/ChocolArm64/ATranslatedSub.cs +++ b/ChocolArm64/ATranslatedSub.cs @@ -13,6 +13,8 @@ namespace ChocolArm64 { private delegate long AA64Subroutine(AThreadState Register, AMemory Memory); + private const int MinCallCountForReJit = 250; + private AA64Subroutine ExecDelegate; public static int StateArgIdx { get; private set; } @@ -32,8 +34,6 @@ namespace ChocolArm64 private bool NeedsReJit; - private int MinCallCountForReJit = 250; - public ATranslatedSub(DynamicMethod Method, List Params) { if (Method == null) @@ -46,8 +46,8 @@ namespace ChocolArm64 throw new ArgumentNullException(nameof(Params)); } - this.Method = Method; - this.Params = Params.AsReadOnly(); + this.Method = Method; + this.Params = Params.AsReadOnly(); Callers = new HashSet(); diff --git a/ChocolArm64/ATranslator.cs b/ChocolArm64/ATranslator.cs index 2d9fcb141..5be41d3ef 100644 --- a/ChocolArm64/ATranslator.cs +++ b/ChocolArm64/ATranslator.cs @@ -1,38 +1,24 @@ using ChocolArm64.Decoder; using ChocolArm64.Events; -using ChocolArm64.Instruction; using ChocolArm64.Memory; using ChocolArm64.State; using ChocolArm64.Translation; using System; -using System.Collections.Concurrent; -using System.Collections.Generic; using System.Reflection.Emit; namespace ChocolArm64 { public class ATranslator { - private ConcurrentDictionary CachedSubs; - - private ConcurrentDictionary SymbolTable; + private ATranslatorCache Cache; public event EventHandler CpuTrace; public bool EnableCpuTrace { get; set; } - public ATranslator(IReadOnlyDictionary SymbolTable = null) + public ATranslator() { - CachedSubs = new ConcurrentDictionary(); - - if (SymbolTable != null) - { - this.SymbolTable = new ConcurrentDictionary(SymbolTable); - } - else - { - this.SymbolTable = new ConcurrentDictionary(); - } + Cache = new ATranslatorCache(); } internal void ExecuteSubroutine(AThread Thread, long Position) @@ -70,15 +56,10 @@ namespace ChocolArm64 { if (EnableCpuTrace) { - if (!SymbolTable.TryGetValue(Position, out string SubName)) - { - SubName = string.Empty; - } - - CpuTrace?.Invoke(this, new ACpuTraceEventArgs(Position, SubName)); + CpuTrace?.Invoke(this, new ACpuTraceEventArgs(Position)); } - if (!CachedSubs.TryGetValue(Position, out ATranslatedSub Sub)) + if (!Cache.TryGetSubroutine(Position, out ATranslatedSub Sub)) { Sub = TranslateTier0(State, Memory, Position); } @@ -93,37 +74,20 @@ namespace ChocolArm64 while (Position != 0 && State.Running); } - internal bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub) - { - if (OpCode.Emitter != AInstEmit.Bl) - { - Sub = null; - - return false; - } - - return TryGetCachedSub(((AOpCodeBImmAl)OpCode).Imm, out Sub); - } - - internal bool TryGetCachedSub(long Position, out ATranslatedSub Sub) - { - return CachedSubs.TryGetValue(Position, out Sub); - } - internal bool HasCachedSub(long Position) { - return CachedSubs.ContainsKey(Position); + return Cache.HasSubroutine(Position); } private ATranslatedSub TranslateTier0(AThreadState State, AMemory Memory, long Position) { - ABlock Block = ADecoder.DecodeBasicBlock(State, this, Memory, Position); + ABlock Block = ADecoder.DecodeBasicBlock(State, Memory, Position); ABlock[] Graph = new ABlock[] { Block }; - string SubName = GetSubName(Position); + string SubName = GetSubroutineName(Position); - AILEmitterCtx Context = new AILEmitterCtx(this, Graph, Block, SubName); + AILEmitterCtx Context = new AILEmitterCtx(Cache, Graph, Block, SubName); do { @@ -135,7 +99,7 @@ namespace ChocolArm64 Subroutine.SetType(ATranslatedSubType.SubTier0); - CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine); + Cache.AddOrUpdate(Position, Subroutine, Block.OpCodes.Count); AOpCode LastOp = Block.GetLastOp(); @@ -144,13 +108,11 @@ namespace ChocolArm64 private void TranslateTier1(AThreadState State, AMemory Memory, long Position) { - (ABlock[] Graph, ABlock Root) Cfg = ADecoder.DecodeSubroutine(State, this, Memory, Position); + (ABlock[] Graph, ABlock Root) = ADecoder.DecodeSubroutine(Cache, State, Memory, Position); - string SubName = GetSubName(Position); + string SubName = GetSubroutineName(Position); - PropagateName(Cfg.Graph, SubName); - - AILEmitterCtx Context = new AILEmitterCtx(this, Cfg.Graph, Cfg.Root, SubName); + AILEmitterCtx Context = new AILEmitterCtx(Cache, Graph, Root, SubName); if (Context.CurrBlock.Position != Position) { @@ -165,11 +127,11 @@ namespace ChocolArm64 //Mark all methods that calls this method for ReJiting, //since we can now call it directly which is faster. - if (CachedSubs.TryGetValue(Position, out ATranslatedSub OldSub)) + if (Cache.TryGetSubroutine(Position, out ATranslatedSub OldSub)) { foreach (long CallerPos in OldSub.GetCallerPositions()) { - if (CachedSubs.TryGetValue(Position, out ATranslatedSub CallerSub)) + if (Cache.TryGetSubroutine(Position, out ATranslatedSub CallerSub)) { CallerSub.MarkForReJit(); } @@ -180,27 +142,24 @@ namespace ChocolArm64 Subroutine.SetType(ATranslatedSubType.SubTier1); - CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine); + Cache.AddOrUpdate(Position, Subroutine, GetGraphInstCount(Graph)); } - private string GetSubName(long Position) + private string GetSubroutineName(long Position) { - return SymbolTable.GetOrAdd(Position, $"Sub{Position:x16}"); + return $"Sub{Position:x16}"; } - private void PropagateName(ABlock[] Graph, string Name) + private int GetGraphInstCount(ABlock[] Graph) { + int Size = 0; + foreach (ABlock Block in Graph) { - AOpCode LastOp = Block.GetLastOp(); - - if (LastOp != null && - (LastOp.Emitter == AInstEmit.Bl || - LastOp.Emitter == AInstEmit.Blr)) - { - SymbolTable.TryAdd(LastOp.Position + 4, Name); - } + Size += Block.OpCodes.Count; } + + return Size; } } } \ No newline at end of file diff --git a/ChocolArm64/ATranslatorCache.cs b/ChocolArm64/ATranslatorCache.cs new file mode 100644 index 000000000..e34cc397b --- /dev/null +++ b/ChocolArm64/ATranslatorCache.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace ChocolArm64 +{ + class ATranslatorCache + { + private const int MaxTotalSize = 2 * 1024 * 256; + private const int MaxTimeDelta = 30000; + private const int MinCallCountForUpdate = 1000; + + private class CacheBucket + { + public ATranslatedSub Subroutine { get; private set; } + + public LinkedListNode Node { get; private set; } + + public int CallCount { get; set; } + + public int Size { get; private set; } + + public int Timestamp { get; private set; } + + public CacheBucket(ATranslatedSub Subroutine, LinkedListNode Node, int Size) + { + this.Subroutine = Subroutine; + this.Size = Size; + + UpdateNode(Node); + } + + public void UpdateNode(LinkedListNode Node) + { + this.Node = Node; + + Timestamp = Environment.TickCount; + } + } + + private ConcurrentDictionary Cache; + + private LinkedList SortedCache; + + private int TotalSize; + + public ATranslatorCache() + { + Cache = new ConcurrentDictionary(); + + SortedCache = new LinkedList(); + } + + public void AddOrUpdate(long Position, ATranslatedSub Subroutine, int Size) + { + ClearCacheIfNeeded(); + + TotalSize += Size; + + lock (SortedCache) + { + LinkedListNode Node = SortedCache.AddLast(Position); + + CacheBucket NewBucket = new CacheBucket(Subroutine, Node, Size); + + Cache.AddOrUpdate(Position, NewBucket, (Key, Bucket) => + { + TotalSize -= Bucket.Size; + + SortedCache.Remove(Bucket.Node); + + return NewBucket; + }); + } + } + + public bool HasSubroutine(long Position) + { + return Cache.ContainsKey(Position); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetSubroutine(long Position, out ATranslatedSub Subroutine) + { + if (Cache.TryGetValue(Position, out CacheBucket Bucket)) + { + if (Bucket.CallCount++ > MinCallCountForUpdate) + { + if (Monitor.TryEnter(SortedCache)) + { + try + { + Bucket.CallCount = 0; + + SortedCache.Remove(Bucket.Node); + + Bucket.UpdateNode(SortedCache.AddLast(Position)); + } + finally + { + Monitor.Exit(SortedCache); + } + } + } + + Subroutine = Bucket.Subroutine; + + return true; + } + + Subroutine = default(ATranslatedSub); + + return false; + } + + private void ClearCacheIfNeeded() + { + int Timestamp = Environment.TickCount; + + while (TotalSize > MaxTotalSize) + { + lock (SortedCache) + { + LinkedListNode Node = SortedCache.First; + + if (Node == null) + { + break; + } + + CacheBucket Bucket = Cache[Node.Value]; + + int TimeDelta = RingDelta(Bucket.Timestamp, Timestamp); + + if ((uint)TimeDelta <= (uint)MaxTimeDelta) + { + break; + } + + if (Cache.TryRemove(Node.Value, out Bucket)) + { + TotalSize -= Bucket.Size; + + SortedCache.Remove(Bucket.Node); + } + } + } + } + + private static int RingDelta(int Old, int New) + { + if ((uint)New < (uint)Old) + { + return New + (~Old + 1); + } + else + { + return New - Old; + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoder/ADecoder.cs b/ChocolArm64/Decoder/ADecoder.cs index b154a54cd..64beebd2a 100644 --- a/ChocolArm64/Decoder/ADecoder.cs +++ b/ChocolArm64/Decoder/ADecoder.cs @@ -19,11 +19,7 @@ namespace ChocolArm64.Decoder OpActivators = new ConcurrentDictionary(); } - public static ABlock DecodeBasicBlock( - AThreadState State, - ATranslator Translator, - AMemory Memory, - long Start) + public static ABlock DecodeBasicBlock(AThreadState State, AMemory Memory, long Start) { ABlock Block = new ABlock(Start); @@ -33,10 +29,10 @@ namespace ChocolArm64.Decoder } public static (ABlock[] Graph, ABlock Root) DecodeSubroutine( - AThreadState State, - ATranslator Translator, - AMemory Memory, - long Start) + ATranslatorCache Cache, + AThreadState State, + AMemory Memory, + long Start) { Dictionary Visited = new Dictionary(); Dictionary VisitedEnd = new Dictionary(); @@ -79,7 +75,7 @@ namespace ChocolArm64.Decoder { if (Op.Emitter == AInstEmit.Bl) { - HasCachedSub = Translator.HasCachedSub(Op.Imm); + HasCachedSub = Cache.HasSubroutine(Op.Imm); } else { diff --git a/ChocolArm64/Events/ACpuTraceEventArgs.cs b/ChocolArm64/Events/ACpuTraceEventArgs.cs index fedf3865b..0284f4eeb 100644 --- a/ChocolArm64/Events/ACpuTraceEventArgs.cs +++ b/ChocolArm64/Events/ACpuTraceEventArgs.cs @@ -6,12 +6,9 @@ namespace ChocolArm64.Events { public long Position { get; private set; } - public string SubName { get; private set; } - - public ACpuTraceEventArgs(long Position, string SubName) + public ACpuTraceEventArgs(long Position) { this.Position = Position; - this.SubName = SubName; } } } \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitFlow.cs b/ChocolArm64/Instruction/AInstEmitFlow.cs index 89979d050..91262834f 100644 --- a/ChocolArm64/Instruction/AInstEmitFlow.cs +++ b/ChocolArm64/Instruction/AInstEmitFlow.cs @@ -35,14 +35,6 @@ namespace ChocolArm64.Instruction { AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp; - if (AOptimizations.GenerateCallStack) - { - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - Context.EmitLdc_I8(Op.Imm); - - Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.EnterMethod)); - } - Context.EmitLdc_I(Op.Position + 4); Context.EmitStint(AThreadState.LRIndex); Context.EmitStoreState(); @@ -80,14 +72,6 @@ namespace ChocolArm64.Instruction { AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp; - if (AOptimizations.GenerateCallStack) - { - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - Context.EmitLdintzr(Op.Rn); - - Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.EnterMethod)); - } - Context.EmitLdc_I(Op.Position + 4); Context.EmitStint(AThreadState.LRIndex); Context.EmitStoreState(); @@ -100,14 +84,6 @@ namespace ChocolArm64.Instruction { AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp; - if (AOptimizations.GenerateCallStack) - { - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - Context.EmitLdintzr(Op.Rn); - - Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.JumpMethod)); - } - Context.EmitStoreState(); Context.EmitLdintzr(Op.Rn); @@ -129,13 +105,6 @@ namespace ChocolArm64.Instruction public static void Ret(AILEmitterCtx Context) { - if (AOptimizations.GenerateCallStack) - { - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.ExitMethod)); - } - Context.EmitStoreState(); Context.EmitLdint(AThreadState.LRIndex); diff --git a/ChocolArm64/State/AThreadState.cs b/ChocolArm64/State/AThreadState.cs index 22e3df144..e4953b021 100644 --- a/ChocolArm64/State/AThreadState.cs +++ b/ChocolArm64/State/AThreadState.cs @@ -1,6 +1,5 @@ using ChocolArm64.Events; using System; -using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; @@ -86,17 +85,10 @@ namespace ChocolArm64.State public event EventHandler SvcCall; public event EventHandler Undefined; - private Stack CallStack; - private static Stopwatch TickCounter; private static double HostTickFreq; - public AThreadState() - { - CallStack = new Stack(); - } - static AThreadState() { HostTickFreq = 1.0 / Stopwatch.Frequency; @@ -153,27 +145,5 @@ namespace ChocolArm64.State { Undefined?.Invoke(this, new AInstUndefinedEventArgs(Position, RawOpCode)); } - - internal void EnterMethod(long Position) - { - CallStack.Push(Position); - } - - internal void ExitMethod() - { - CallStack.TryPop(out _); - } - - internal void JumpMethod(long Position) - { - CallStack.TryPop(out _); - - CallStack.Push(Position); - } - - public long[] GetCallStack() - { - return CallStack.ToArray(); - } } } \ No newline at end of file diff --git a/ChocolArm64/Translation/AILEmitterCtx.cs b/ChocolArm64/Translation/AILEmitterCtx.cs index 487007155..cad0d323d 100644 --- a/ChocolArm64/Translation/AILEmitterCtx.cs +++ b/ChocolArm64/Translation/AILEmitterCtx.cs @@ -10,7 +10,7 @@ namespace ChocolArm64.Translation { class AILEmitterCtx { - private ATranslator Translator; + private ATranslatorCache Cache; private Dictionary Labels; @@ -40,29 +40,14 @@ namespace ChocolArm64.Translation private const int Tmp5Index = -5; public AILEmitterCtx( - ATranslator Translator, - ABlock[] Graph, - ABlock Root, - string SubName) + ATranslatorCache Cache, + ABlock[] Graph, + ABlock Root, + string SubName) { - if (Translator == null) - { - throw new ArgumentNullException(nameof(Translator)); - } - - if (Graph == null) - { - throw new ArgumentNullException(nameof(Graph)); - } - - if (Root == null) - { - throw new ArgumentNullException(nameof(Root)); - } - - this.Translator = Translator; - this.Graph = Graph; - this.Root = Root; + this.Cache = Cache ?? throw new ArgumentNullException(nameof(Cache)); + this.Graph = Graph ?? throw new ArgumentNullException(nameof(Graph)); + this.Root = Root ?? throw new ArgumentNullException(nameof(Root)); Labels = new Dictionary(); @@ -147,7 +132,12 @@ namespace ChocolArm64.Translation return false; } - if (!Translator.TryGetCachedSub(CurrOp, out ATranslatedSub Sub)) + if (CurrOp.Emitter != AInstEmit.Bl) + { + return false; + } + + if (!Cache.TryGetSubroutine(((AOpCodeBImmAl)CurrOp).Imm, out ATranslatedSub Subroutine)) { return false; } @@ -157,7 +147,7 @@ namespace ChocolArm64.Translation EmitLdarg(Index); } - foreach (ARegister Reg in Sub.Params) + foreach (ARegister Reg in Subroutine.Params) { switch (Reg.Type) { @@ -167,9 +157,9 @@ namespace ChocolArm64.Translation } } - EmitCall(Sub.Method); + EmitCall(Subroutine.Method); - Sub.AddCaller(Root.Position); + Subroutine.AddCaller(Root.Position); return true; } diff --git a/Ryujinx.HLE/HOS/Process.cs b/Ryujinx.HLE/HOS/Process.cs index 0bc95f0c1..f7ec2604c 100644 --- a/Ryujinx.HLE/HOS/Process.cs +++ b/Ryujinx.HLE/HOS/Process.cs @@ -55,8 +55,6 @@ namespace Ryujinx.HLE.HOS private List Executables; - private Dictionary SymbolTable; - private long ImageBase; private bool Disposed; @@ -122,8 +120,6 @@ namespace Ryujinx.HLE.HOS return false; } - MakeSymbolTable(); - long MainStackTop = MemoryManager.CodeRegionEnd - KMemoryManager.PageSize; long MainStackSize = 1 * 1024 * 1024; @@ -256,31 +252,6 @@ namespace Ryujinx.HLE.HOS throw new UndefinedInstructionException(e.Position, e.RawOpCode); } - private void MakeSymbolTable() - { - SymbolTable = new Dictionary(); - - foreach (Executable Exe in Executables) - { - foreach (KeyValuePair KV in Exe.SymbolTable) - { - SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value); - } - } - } - - private ATranslator GetTranslator() - { - if (Translator == null) - { - Translator = new ATranslator(SymbolTable); - - Translator.CpuTrace += CpuTraceHandler; - } - - return Translator; - } - public void EnableCpuTracing() { Translator.EnableCpuTrace = true; @@ -293,32 +264,53 @@ namespace Ryujinx.HLE.HOS private void CpuTraceHandler(object sender, ACpuTraceEventArgs e) { - string NsoName = string.Empty; + Executable Exe = GetExecutable(e.Position); - for (int Index = Executables.Count - 1; Index >= 0; Index--) + if (Exe == null) { - if (e.Position >= Executables[Index].ImageBase) - { - NsoName = $"{(e.Position - Executables[Index].ImageBase):x16}"; - - break; - } + return; } - Device.Log.PrintDebug(LogClass.Cpu, $"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}"); + if (!TryGetSubName(Exe, e.Position, out string SubName)) + { + SubName = string.Empty; + } + + long Offset = e.Position - Exe.ImageBase; + + string ExeNameWithAddr = $"{Exe.Name}:0x{Offset:x8}"; + + Device.Log.PrintDebug(LogClass.Cpu, ExeNameWithAddr + " " + SubName); + } + + private ATranslator GetTranslator() + { + if (Translator == null) + { + Translator = new ATranslator(); + + Translator.CpuTrace += CpuTraceHandler; + } + + return Translator; } public void PrintStackTrace(AThreadState ThreadState) { - long[] Positions = ThreadState.GetCallStack(); - StringBuilder Trace = new StringBuilder(); Trace.AppendLine("Guest stack trace:"); - foreach (long Position in Positions) + void AppendTrace(long Position) { - if (!SymbolTable.TryGetValue(Position, out string SubName)) + Executable Exe = GetExecutable(Position); + + if (Exe == null) + { + return; + } + + if (!TryGetSubName(Exe, Position, out string SubName)) { SubName = $"Sub{Position:x16}"; } @@ -327,29 +319,77 @@ namespace Ryujinx.HLE.HOS SubName = Demangler.Parse(SubName); } - Trace.AppendLine(" " + SubName + " (" + GetNsoNameAndAddress(Position) + ")"); + long Offset = Position - Exe.ImageBase; + + string ExeNameWithAddr = $"{Exe.Name}:0x{Offset:x8}"; + + Trace.AppendLine(" " + ExeNameWithAddr + " " + SubName); + } + + long FramePointer = (long)ThreadState.X29; + + while (FramePointer != 0) + { + AppendTrace(Memory.ReadInt64(FramePointer + 8)); + + FramePointer = Memory.ReadInt64(FramePointer); } Device.Log.PrintInfo(LogClass.Cpu, Trace.ToString()); } - private string GetNsoNameAndAddress(long Position) + private bool TryGetSubName(Executable Exe, long Position, out string Name) + { + Position -= Exe.ImageBase; + + int Left = 0; + int Right = Exe.SymbolTable.Count - 1; + + while (Left <= Right) + { + int Size = Right - Left; + + int Middle = Left + (Size >> 1); + + ElfSym Symbol = Exe.SymbolTable[Middle]; + + long EndPosition = Symbol.Value + Symbol.Size; + + if ((ulong)Position >= (ulong)Symbol.Value && (ulong)Position < (ulong)EndPosition) + { + Name = Symbol.Name; + + return true; + } + + if ((ulong)Position < (ulong)Symbol.Value) + { + Right = Middle - 1; + } + else + { + Left = Middle + 1; + } + } + + Name = null; + + return false; + } + + private Executable GetExecutable(long Position) { string Name = string.Empty; for (int Index = Executables.Count - 1; Index >= 0; Index--) { - if (Position >= Executables[Index].ImageBase) + if ((ulong)Position >= (ulong)Executables[Index].ImageBase) { - long Offset = Position - Executables[Index].ImageBase; - - Name = $"{Executables[Index].Name}:{Offset:x8}"; - - break; + return Executables[Index]; } } - return Name; + return null; } private void ThreadFinished(object sender, EventArgs e) diff --git a/Ryujinx.HLE/Loaders/Executable.cs b/Ryujinx.HLE/Loaders/Executable.cs index 6a3f0b973..a9850e4af 100644 --- a/Ryujinx.HLE/Loaders/Executable.cs +++ b/Ryujinx.HLE/Loaders/Executable.cs @@ -3,18 +3,21 @@ using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Utilities; +using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.IO; +using System.Linq; namespace Ryujinx.HLE.Loaders { class Executable { + private AMemory Memory; + private List Dynamic; - private Dictionary m_SymbolTable; - - public IReadOnlyDictionary SymbolTable => m_SymbolTable; + public ReadOnlyCollection SymbolTable; public string Name { get; private set; } @@ -23,16 +26,12 @@ namespace Ryujinx.HLE.Loaders public long ImageBase { get; private set; } public long ImageEnd { get; private set; } - private AMemory Memory; - private KMemoryManager MemoryManager; public Executable(IExecutable Exe, KMemoryManager MemoryManager, AMemory Memory, long ImageBase) { Dynamic = new List(); - m_SymbolTable = new Dictionary(); - FilePath = Exe.FilePath; if (FilePath != null) @@ -103,14 +102,18 @@ namespace Ryujinx.HLE.Loaders long SymEntSize = GetFirstValue(ElfDynTag.DT_SYMENT); + List Symbols = new List(); + while ((ulong)SymTblAddr < (ulong)StrTblAddr) { ElfSym Sym = GetSymbol(SymTblAddr, StrTblAddr); - m_SymbolTable.TryAdd(Sym.Value, Sym.Name); + Symbols.Add(Sym); SymTblAddr += SymEntSize; } + + SymbolTable = Array.AsReadOnly(Symbols.OrderBy(x => x.Value).ToArray()); } private ElfRel GetRelocation(long Position)