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
This commit is contained in:
parent
773e239db7
commit
5e9678c8fa
5 changed files with 63 additions and 17 deletions
|
@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||||
private const string InnerHeaderMagicString = "PTCihd\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 ActualDir = "0";
|
||||||
private const string BackupDir = "1";
|
private const string BackupDir = "1";
|
||||||
|
|
|
@ -27,6 +27,26 @@ namespace ARMeilleure.Translation.PTC
|
||||||
return dictionary;
|
return dictionary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Dictionary<TKey, TValue> DeserializeAndUpdateDictionary<TKey, TValue>(Stream stream, Func<Stream, TValue> valueFunc, Func<TKey, TValue, (TKey, TValue)> updateFunc) where TKey : struct
|
||||||
|
{
|
||||||
|
Dictionary<TKey, TValue> dictionary = new();
|
||||||
|
|
||||||
|
int count = DeserializeStructure<int>(stream);
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
TKey key = DeserializeStructure<TKey>(stream);
|
||||||
|
TValue value = valueFunc(stream);
|
||||||
|
|
||||||
|
(key, value) = updateFunc(key, value);
|
||||||
|
|
||||||
|
dictionary.Add(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dictionary;
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static List<T> DeserializeList<T>(Stream stream) where T : struct
|
public static List<T> DeserializeList<T>(Stream stream) where T : struct
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,10 +9,13 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Timers;
|
||||||
using static ARMeilleure.Translation.PTC.PtcFormatter;
|
using static ARMeilleure.Translation.PTC.PtcFormatter;
|
||||||
|
using Timer = System.Timers.Timer;
|
||||||
|
|
||||||
namespace ARMeilleure.Translation.PTC
|
namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
|
@ -20,7 +23,11 @@ namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
private const string OuterHeaderMagicString = "Pohd\0\0\0\0";
|
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.
|
private const int SaveInterval = 30; // Seconds.
|
||||||
|
|
||||||
|
@ -28,7 +35,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
private readonly Ptc _ptc;
|
private readonly Ptc _ptc;
|
||||||
|
|
||||||
private readonly System.Timers.Timer _timer;
|
private readonly Timer _timer;
|
||||||
|
|
||||||
private readonly ulong _outerHeaderMagic;
|
private readonly ulong _outerHeaderMagic;
|
||||||
|
|
||||||
|
@ -51,7 +58,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
_ptc = ptc;
|
_ptc = ptc;
|
||||||
|
|
||||||
_timer = new System.Timers.Timer((double)SaveInterval * 1000d);
|
_timer = new Timer(SaveInterval * 1000d);
|
||||||
_timer.Elapsed += PreSave;
|
_timer.Elapsed += PreSave;
|
||||||
|
|
||||||
_outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
|
_outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
|
||||||
|
@ -168,7 +175,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outerHeader.InfoFileVersion != InternalVersion)
|
if (outerHeader.InfoFileVersion != InternalVersion && !_migrateInternalVersions.Contains(outerHeader.InfoFileVersion))
|
||||||
{
|
{
|
||||||
InvalidateCompressedStream(compressedStream);
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
|
@ -211,7 +218,19 @@ namespace ARMeilleure.Translation.PTC
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (outerHeader.InfoFileVersion)
|
||||||
|
{
|
||||||
|
case InternalVersion:
|
||||||
ProfiledFuncs = Deserialize(stream);
|
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);
|
Debug.Assert(stream.Position == stream.Length);
|
||||||
|
|
||||||
|
@ -225,9 +244,14 @@ namespace ARMeilleure.Translation.PTC
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Dictionary<ulong, FuncProfile> Deserialize(Stream stream)
|
private static Dictionary<ulong, FuncProfile> Deserialize(Stream stream, Func<ulong, FuncProfile, (ulong, FuncProfile)> migrateEntryFunc = null)
|
||||||
{
|
{
|
||||||
return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(stream));
|
if (migrateEntryFunc != null)
|
||||||
|
{
|
||||||
|
return DeserializeAndUpdateDictionary(stream, DeserializeStructure<FuncProfile>, migrateEntryFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeserializeDictionary<ulong, FuncProfile>(stream, DeserializeStructure<FuncProfile>);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream)
|
private static ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream)
|
||||||
|
@ -240,7 +264,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
compressedStream.SetLength(0L);
|
compressedStream.SetLength(0L);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PreSave(object source, System.Timers.ElapsedEventArgs e)
|
private void PreSave(object source, ElapsedEventArgs e)
|
||||||
{
|
{
|
||||||
_waitEvent.Reset();
|
_waitEvent.Reset();
|
||||||
|
|
||||||
|
@ -277,7 +301,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
|
Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
|
||||||
|
|
||||||
stream.Seek((long)Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin);
|
stream.Seek(Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin);
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
|
@ -288,7 +312,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
Debug.Assert(stream.Position == stream.Length);
|
Debug.Assert(stream.Position == stream.Length);
|
||||||
|
|
||||||
stream.Seek((long)Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin);
|
stream.Seek(Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin);
|
||||||
Hash128 hash = XXHash128.ComputeHash(GetReadOnlySpan(stream));
|
Hash128 hash = XXHash128.ComputeHash(GetReadOnlySpan(stream));
|
||||||
|
|
||||||
stream.Seek(0L, SeekOrigin.Begin);
|
stream.Seek(0L, SeekOrigin.Begin);
|
||||||
|
@ -332,7 +356,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
private static void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs)
|
private static void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs)
|
||||||
{
|
{
|
||||||
SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure));
|
SerializeDictionary(stream, profiledFuncs, SerializeStructure);
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 29*/)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 29*/)]
|
||||||
|
|
|
@ -89,9 +89,6 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
||||||
Logger.Warning?.Print(LogClass.Ptc, "Detected unsupported ExeFs modifications. PTC disabled.");
|
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 = "";
|
string programName = "";
|
||||||
|
|
||||||
if (!isHomebrew && programId > 0x010000000000FFFF)
|
if (!isHomebrew && programId > 0x010000000000FFFF)
|
||||||
|
@ -119,7 +116,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
||||||
metaLoader,
|
metaLoader,
|
||||||
nacpData,
|
nacpData,
|
||||||
enablePtc,
|
enablePtc,
|
||||||
allowCodeMemoryForJit,
|
true,
|
||||||
programName,
|
programName,
|
||||||
metaLoader.GetProgramId(),
|
metaLoader.GetProgramId(),
|
||||||
null,
|
null,
|
||||||
|
|
|
@ -28,6 +28,11 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||||
{
|
{
|
||||||
static class ProcessLoaderHelper
|
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)
|
public static LibHac.Result RegisterProgramMapInfo(Switch device, PartitionFileSystem partitionFileSystem)
|
||||||
{
|
{
|
||||||
ulong applicationId = 0;
|
ulong applicationId = 0;
|
||||||
|
@ -242,7 +247,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||||
|
|
||||||
ulong argsStart = 0;
|
ulong argsStart = 0;
|
||||||
uint argsSize = 0;
|
uint argsSize = 0;
|
||||||
ulong codeStart = (meta.Flags & 1) != 0 ? 0x8000000UL : 0x200000UL;
|
ulong codeStart = ((meta.Flags & 1) != 0 ? 0x8000000UL : 0x200000UL) + CodeStartOffset;
|
||||||
uint codeSize = 0;
|
uint codeSize = 0;
|
||||||
|
|
||||||
var buildIds = executables.Select(e => (e switch
|
var buildIds = executables.Select(e => (e switch
|
||||||
|
|
Reference in a new issue