From 5e9678c8fad4625026268e457f4c3e23bdc22697 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 9 Aug 2023 23:27:45 +0200 Subject: [PATCH] Allow access to code memory for exefs mods (#5518) * Allow access to code memory for exefs mods * Add ASLR workaround for Skyline * Hardcode allowCodeMemoryForJit to true --- src/ARMeilleure/Translation/PTC/Ptc.cs | 2 +- .../Translation/PTC/PtcFormatter.cs | 20 ++++++++ .../Translation/PTC/PtcProfiler.cs | 46 ++++++++++++++----- .../Extensions/FileSystemExtensions.cs | 5 +- .../Loaders/Processes/ProcessLoaderHelper.cs | 7 ++- 5 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/ARMeilleure/Translation/PTC/Ptc.cs b/src/ARMeilleure/Translation/PTC/Ptc.cs index ce653383e..6f6dfcadf 100644 --- a/src/ARMeilleure/Translation/PTC/Ptc.cs +++ b/src/ARMeilleure/Translation/PTC/Ptc.cs @@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 5502; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 5518; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; diff --git a/src/ARMeilleure/Translation/PTC/PtcFormatter.cs b/src/ARMeilleure/Translation/PTC/PtcFormatter.cs index ddac31338..60953dcd9 100644 --- a/src/ARMeilleure/Translation/PTC/PtcFormatter.cs +++ b/src/ARMeilleure/Translation/PTC/PtcFormatter.cs @@ -27,6 +27,26 @@ namespace ARMeilleure.Translation.PTC return dictionary; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Dictionary DeserializeAndUpdateDictionary(Stream stream, Func valueFunc, Func updateFunc) where TKey : struct + { + Dictionary dictionary = new(); + + int count = DeserializeStructure(stream); + + for (int i = 0; i < count; i++) + { + TKey key = DeserializeStructure(stream); + TValue value = valueFunc(stream); + + (key, value) = updateFunc(key, value); + + dictionary.Add(key, value); + } + + return dictionary; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List DeserializeList(Stream stream) where T : struct { diff --git a/src/ARMeilleure/Translation/PTC/PtcProfiler.cs b/src/ARMeilleure/Translation/PTC/PtcProfiler.cs index 3a4bfcec6..0fe78edab 100644 --- a/src/ARMeilleure/Translation/PTC/PtcProfiler.cs +++ b/src/ARMeilleure/Translation/PTC/PtcProfiler.cs @@ -9,10 +9,13 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; +using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; +using System.Timers; using static ARMeilleure.Translation.PTC.PtcFormatter; +using Timer = System.Timers.Timer; namespace ARMeilleure.Translation.PTC { @@ -20,7 +23,11 @@ namespace ARMeilleure.Translation.PTC { private const string OuterHeaderMagicString = "Pohd\0\0\0\0"; - private const uint InternalVersion = 1866; //! Not to be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 5518; //! Not to be incremented manually for each change to the ARMeilleure project. + + private static readonly uint[] _migrateInternalVersions = { + 1866, + }; private const int SaveInterval = 30; // Seconds. @@ -28,7 +35,7 @@ namespace ARMeilleure.Translation.PTC private readonly Ptc _ptc; - private readonly System.Timers.Timer _timer; + private readonly Timer _timer; private readonly ulong _outerHeaderMagic; @@ -51,7 +58,7 @@ namespace ARMeilleure.Translation.PTC { _ptc = ptc; - _timer = new System.Timers.Timer((double)SaveInterval * 1000d); + _timer = new Timer(SaveInterval * 1000d); _timer.Elapsed += PreSave; _outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan()); @@ -168,7 +175,7 @@ namespace ARMeilleure.Translation.PTC return false; } - if (outerHeader.InfoFileVersion != InternalVersion) + if (outerHeader.InfoFileVersion != InternalVersion && !_migrateInternalVersions.Contains(outerHeader.InfoFileVersion)) { InvalidateCompressedStream(compressedStream); @@ -211,7 +218,19 @@ namespace ARMeilleure.Translation.PTC return false; } - ProfiledFuncs = Deserialize(stream); + switch (outerHeader.InfoFileVersion) + { + case InternalVersion: + ProfiledFuncs = Deserialize(stream); + break; + case 1866: + ProfiledFuncs = Deserialize(stream, (address, profile) => (address + 0x500000UL, profile)); + break; + default: + Logger.Error?.Print(LogClass.Ptc, $"No migration path for {nameof(outerHeader.InfoFileVersion)} '{outerHeader.InfoFileVersion}'. Discarding cache."); + InvalidateCompressedStream(compressedStream); + return false; + } Debug.Assert(stream.Position == stream.Length); @@ -225,9 +244,14 @@ namespace ARMeilleure.Translation.PTC return true; } - private static Dictionary Deserialize(Stream stream) + private static Dictionary Deserialize(Stream stream, Func migrateEntryFunc = null) { - return DeserializeDictionary(stream, (stream) => DeserializeStructure(stream)); + if (migrateEntryFunc != null) + { + return DeserializeAndUpdateDictionary(stream, DeserializeStructure, migrateEntryFunc); + } + + return DeserializeDictionary(stream, DeserializeStructure); } private static ReadOnlySpan GetReadOnlySpan(MemoryStream memoryStream) @@ -240,7 +264,7 @@ namespace ARMeilleure.Translation.PTC compressedStream.SetLength(0L); } - private void PreSave(object source, System.Timers.ElapsedEventArgs e) + private void PreSave(object source, ElapsedEventArgs e) { _waitEvent.Reset(); @@ -277,7 +301,7 @@ namespace ARMeilleure.Translation.PTC { Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L); - stream.Seek((long)Unsafe.SizeOf(), SeekOrigin.Begin); + stream.Seek(Unsafe.SizeOf(), SeekOrigin.Begin); lock (_lock) { @@ -288,7 +312,7 @@ namespace ARMeilleure.Translation.PTC Debug.Assert(stream.Position == stream.Length); - stream.Seek((long)Unsafe.SizeOf(), SeekOrigin.Begin); + stream.Seek(Unsafe.SizeOf(), SeekOrigin.Begin); Hash128 hash = XXHash128.ComputeHash(GetReadOnlySpan(stream)); stream.Seek(0L, SeekOrigin.Begin); @@ -332,7 +356,7 @@ namespace ARMeilleure.Translation.PTC private static void Serialize(Stream stream, Dictionary profiledFuncs) { - SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure)); + SerializeDictionary(stream, profiledFuncs, SerializeStructure); } [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 29*/)] diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs index 040d1143d..07bbaf12b 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs @@ -89,9 +89,6 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions Logger.Warning?.Print(LogClass.Ptc, "Detected unsupported ExeFs modifications. PTC disabled."); } - // We allow it for nx-hbloader because it can be used to launch homebrew. - bool allowCodeMemoryForJit = programId == 0x010000000000100DUL || isHomebrew; - string programName = ""; if (!isHomebrew && programId > 0x010000000000FFFF) @@ -119,7 +116,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions metaLoader, nacpData, enablePtc, - allowCodeMemoryForJit, + true, programName, metaLoader.GetProgramId(), null, diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs index d14a013af..292a5c122 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs @@ -28,6 +28,11 @@ namespace Ryujinx.HLE.Loaders.Processes { static class ProcessLoaderHelper { + // NOTE: If you want to change this value make sure to increment the InternalVersion of Ptc and PtcProfiler. + // You also need to add a new migration path and adjust the existing ones. + // TODO: Remove this workaround when ASLR is implemented. + private const ulong CodeStartOffset = 0x500000UL; + public static LibHac.Result RegisterProgramMapInfo(Switch device, PartitionFileSystem partitionFileSystem) { ulong applicationId = 0; @@ -242,7 +247,7 @@ namespace Ryujinx.HLE.Loaders.Processes ulong argsStart = 0; uint argsSize = 0; - ulong codeStart = (meta.Flags & 1) != 0 ? 0x8000000UL : 0x200000UL; + ulong codeStart = ((meta.Flags & 1) != 0 ? 0x8000000UL : 0x200000UL) + CodeStartOffset; uint codeSize = 0; var buildIds = executables.Select(e => (e switch