PPTC Follow-up. (#1712)
* Added support for offline invalidation, via PPTC, of low cq translations replaced by high cq translations; both on a single run and between runs. Added invalidation of .cache files in the event of reuse on a different user operating system. Added .info and .cache files invalidation in case of a failed stream decompression. Nits. * InternalVersion = 1712; * Nits. * Address comment. * Get rid of BinaryFormatter. Nits. * Move Ptc.LoadTranslations(). Nits. * Nits. * Fixed corner cases (in case backup copies have to be used). Added save logs. * Not core fixes. * Complement to the previous commit. Added load logs. Removed BinaryFormatter leftovers. * Add LoadTranslations log. * Nits. * Removed the search and management of LowCq overlapping functions. * Final increment of .info and .cache flags. * Nit. * GetIndirectFunctionAddress(): Validate that writing actually takes place in dynamic table memory range (and not elsewhere). * Fix Ptc.UpdateInfo() due to rebase. * Nit for retrigger Checks. * Nit for retrigger Checks.
This commit is contained in:
parent
10aa11ce13
commit
b5c215111d
16 changed files with 670 additions and 169 deletions
|
@ -12,7 +12,7 @@ using System.Diagnostics;
|
|||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -22,7 +22,7 @@ namespace ARMeilleure.Translation.PTC
|
|||
{
|
||||
private const string HeaderMagic = "PTChd";
|
||||
|
||||
private const int InternalVersion = 1519; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
private const uint InternalVersion = 1713; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
|
||||
private const string ActualDir = "0";
|
||||
private const string BackupDir = "1";
|
||||
|
@ -34,6 +34,7 @@ namespace ARMeilleure.Translation.PTC
|
|||
internal const int JumpPointerIndex = -2; // Must be a negative value.
|
||||
internal const int DynamicPointerIndex = -3; // Must be a negative value.
|
||||
|
||||
private const byte FillingByte = 0x00;
|
||||
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
||||
|
||||
private static readonly MemoryStream _infosStream;
|
||||
|
@ -43,8 +44,6 @@ namespace ARMeilleure.Translation.PTC
|
|||
|
||||
private static readonly BinaryWriter _infosWriter;
|
||||
|
||||
private static readonly BinaryFormatter _binaryFormatter;
|
||||
|
||||
private static readonly ManualResetEvent _waitEvent;
|
||||
|
||||
private static readonly AutoResetEvent _loggerEvent;
|
||||
|
@ -54,7 +53,6 @@ namespace ARMeilleure.Translation.PTC
|
|||
private static bool _disposed;
|
||||
|
||||
private static volatile int _translateCount;
|
||||
private static volatile int _rejitCount;
|
||||
|
||||
internal static PtcJumpTable PtcJumpTable { get; private set; }
|
||||
|
||||
|
@ -75,8 +73,6 @@ namespace ARMeilleure.Translation.PTC
|
|||
|
||||
_infosWriter = new BinaryWriter(_infosStream, EncodingCache.UTF8NoBOM, true);
|
||||
|
||||
_binaryFormatter = new BinaryFormatter();
|
||||
|
||||
_waitEvent = new ManualResetEvent(true);
|
||||
|
||||
_loggerEvent = new AutoResetEvent(false);
|
||||
|
@ -174,26 +170,26 @@ namespace ARMeilleure.Translation.PTC
|
|||
|
||||
if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
|
||||
{
|
||||
if (!Load(fileNameActual))
|
||||
if (!Load(fileNameActual, false))
|
||||
{
|
||||
if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
|
||||
{
|
||||
Load(fileNameBackup);
|
||||
Load(fileNameBackup, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
|
||||
{
|
||||
Load(fileNameBackup);
|
||||
Load(fileNameBackup, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool Load(string fileName)
|
||||
private static bool Load(string fileName, bool isBackup)
|
||||
{
|
||||
using (FileStream compressedStream = new FileStream(fileName, FileMode.Open))
|
||||
using (DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress, true))
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
|
||||
using (MD5 md5 = MD5.Create())
|
||||
{
|
||||
int hashSize = md5.HashSize / 8;
|
||||
|
||||
|
@ -247,6 +243,13 @@ namespace ARMeilleure.Translation.PTC
|
|||
return false;
|
||||
}
|
||||
|
||||
if (header.OSPlatform != GetOSPlatform())
|
||||
{
|
||||
InvalidateCompressedStream(compressedStream);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.InfosLen % InfoEntry.Stride != 0)
|
||||
{
|
||||
InvalidateCompressedStream(compressedStream);
|
||||
|
@ -266,7 +269,7 @@ namespace ARMeilleure.Translation.PTC
|
|||
|
||||
try
|
||||
{
|
||||
PtcJumpTable = (PtcJumpTable)_binaryFormatter.Deserialize(stream);
|
||||
PtcJumpTable = PtcJumpTable.Deserialize(stream);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -281,9 +284,13 @@ namespace ARMeilleure.Translation.PTC
|
|||
_codesStream.Write(codesBuf, 0, header.CodesLen);
|
||||
_relocsStream.Write(relocsBuf, 0, header.RelocsLen);
|
||||
_unwindInfosStream.Write(unwindInfosBuf, 0, header.UnwindInfosLen);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
long fileSize = new FileInfo(fileName).Length;
|
||||
|
||||
Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Translation Cache" : "Loaded Translation Cache")} (size: {fileSize} bytes, translated functions: {GetInfosEntriesCount()}).");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool CompareHash(ReadOnlySpan<byte> currentHash, ReadOnlySpan<byte> expectedHash)
|
||||
|
@ -299,8 +306,9 @@ namespace ARMeilleure.Translation.PTC
|
|||
|
||||
header.Magic = headerReader.ReadString();
|
||||
|
||||
header.CacheFileVersion = headerReader.ReadInt32();
|
||||
header.CacheFileVersion = headerReader.ReadUInt32();
|
||||
header.FeatureInfo = headerReader.ReadUInt64();
|
||||
header.OSPlatform = headerReader.ReadUInt32();
|
||||
|
||||
header.InfosLen = headerReader.ReadInt32();
|
||||
header.CodesLen = headerReader.ReadInt32();
|
||||
|
@ -338,7 +346,7 @@ namespace ARMeilleure.Translation.PTC
|
|||
private static void Save(string fileName)
|
||||
{
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
|
||||
using (MD5 md5 = MD5.Create())
|
||||
{
|
||||
int hashSize = md5.HashSize / 8;
|
||||
|
||||
|
@ -351,7 +359,7 @@ namespace ARMeilleure.Translation.PTC
|
|||
_relocsStream.WriteTo(stream);
|
||||
_unwindInfosStream.WriteTo(stream);
|
||||
|
||||
_binaryFormatter.Serialize(stream, PtcJumpTable);
|
||||
PtcJumpTable.Serialize(stream, PtcJumpTable);
|
||||
|
||||
stream.Seek((long)hashSize, SeekOrigin.Begin);
|
||||
byte[] hash = md5.ComputeHash(stream);
|
||||
|
@ -377,6 +385,10 @@ namespace ARMeilleure.Translation.PTC
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
long fileSize = new FileInfo(fileName).Length;
|
||||
|
||||
Logger.Info?.Print(LogClass.Ptc, $"Saved Translation Cache (size: {fileSize} bytes, translated functions: {GetInfosEntriesCount()}).");
|
||||
}
|
||||
|
||||
private static void WriteHeader(MemoryStream stream)
|
||||
|
@ -385,8 +397,9 @@ namespace ARMeilleure.Translation.PTC
|
|||
{
|
||||
headerWriter.Write((string)HeaderMagic); // Header.Magic
|
||||
|
||||
headerWriter.Write((int)InternalVersion); // Header.CacheFileVersion
|
||||
headerWriter.Write((uint)InternalVersion); // Header.CacheFileVersion
|
||||
headerWriter.Write((ulong)GetFeatureInfo()); // Header.FeatureInfo
|
||||
headerWriter.Write((uint)GetOSPlatform()); // Header.OSPlatform
|
||||
|
||||
headerWriter.Write((int)_infosStream.Length); // Header.InfosLen
|
||||
headerWriter.Write((int)_codesStream.Length); // Header.CodesLen
|
||||
|
@ -395,7 +408,7 @@ namespace ARMeilleure.Translation.PTC
|
|||
}
|
||||
}
|
||||
|
||||
internal static void LoadTranslations(ConcurrentDictionary<ulong, TranslatedFunction> funcs, IntPtr pageTablePointer, JumpTable jumpTable)
|
||||
internal static void LoadTranslations(ConcurrentDictionary<ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable)
|
||||
{
|
||||
if ((int)_infosStream.Length == 0 ||
|
||||
(int)_codesStream.Length == 0 ||
|
||||
|
@ -417,26 +430,44 @@ namespace ARMeilleure.Translation.PTC
|
|||
using (BinaryReader relocsReader = new BinaryReader(_relocsStream, EncodingCache.UTF8NoBOM, true))
|
||||
using (BinaryReader unwindInfosReader = new BinaryReader(_unwindInfosStream, EncodingCache.UTF8NoBOM, true))
|
||||
{
|
||||
int infosEntriesCount = (int)_infosStream.Length / InfoEntry.Stride;
|
||||
|
||||
for (int i = 0; i < infosEntriesCount; i++)
|
||||
for (int i = 0; i < GetInfosEntriesCount(); i++)
|
||||
{
|
||||
InfoEntry infoEntry = ReadInfo(infosReader);
|
||||
|
||||
byte[] code = ReadCode(codesReader, infoEntry.CodeLen);
|
||||
|
||||
if (infoEntry.RelocEntriesCount != 0)
|
||||
if (infoEntry.Stubbed)
|
||||
{
|
||||
RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount);
|
||||
|
||||
PatchCode(code, relocEntries, pageTablePointer, jumpTable);
|
||||
SkipCode(infoEntry.CodeLen);
|
||||
SkipReloc(infoEntry.RelocEntriesCount);
|
||||
SkipUnwindInfo(unwindInfosReader);
|
||||
}
|
||||
else if (infoEntry.HighCq || !PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) || !value.highCq)
|
||||
{
|
||||
byte[] code = ReadCode(codesReader, infoEntry.CodeLen);
|
||||
|
||||
UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader);
|
||||
if (infoEntry.RelocEntriesCount != 0)
|
||||
{
|
||||
RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount);
|
||||
|
||||
TranslatedFunction func = FastTranslate(code, infoEntry.GuestSize, unwindInfo, infoEntry.HighCq);
|
||||
PatchCode(code, relocEntries, memory.PageTablePointer, jumpTable);
|
||||
}
|
||||
|
||||
funcs.AddOrUpdate(infoEntry.Address, func, (key, oldFunc) => func.HighCq && !oldFunc.HighCq ? func : oldFunc);
|
||||
UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader);
|
||||
|
||||
TranslatedFunction func = FastTranslate(code, infoEntry.GuestSize, unwindInfo, infoEntry.HighCq);
|
||||
|
||||
bool isAddressUnique = funcs.TryAdd(infoEntry.Address, func);
|
||||
|
||||
Debug.Assert(isAddressUnique, $"The address 0x{infoEntry.Address:X16} is not unique.");
|
||||
}
|
||||
else
|
||||
{
|
||||
infoEntry.Stubbed = true;
|
||||
UpdateInfo(infoEntry);
|
||||
|
||||
StubCode(infoEntry.CodeLen);
|
||||
StubReloc(infoEntry.RelocEntriesCount);
|
||||
StubUnwindInfo(unwindInfosReader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -452,6 +483,13 @@ namespace ARMeilleure.Translation.PTC
|
|||
|
||||
PtcJumpTable.WriteJumpTable(jumpTable, funcs);
|
||||
PtcJumpTable.WriteDynamicTable(jumpTable);
|
||||
|
||||
Logger.Info?.Print(LogClass.Ptc, $"{funcs.Count} translated functions loaded");
|
||||
}
|
||||
|
||||
private static int GetInfosEntriesCount()
|
||||
{
|
||||
return (int)_infosStream.Length / InfoEntry.Stride;
|
||||
}
|
||||
|
||||
private static InfoEntry ReadInfo(BinaryReader infosReader)
|
||||
|
@ -461,12 +499,30 @@ namespace ARMeilleure.Translation.PTC
|
|||
infoEntry.Address = infosReader.ReadUInt64();
|
||||
infoEntry.GuestSize = infosReader.ReadUInt64();
|
||||
infoEntry.HighCq = infosReader.ReadBoolean();
|
||||
infoEntry.Stubbed = infosReader.ReadBoolean();
|
||||
infoEntry.CodeLen = infosReader.ReadInt32();
|
||||
infoEntry.RelocEntriesCount = infosReader.ReadInt32();
|
||||
|
||||
return infoEntry;
|
||||
}
|
||||
|
||||
private static void SkipCode(int codeLen)
|
||||
{
|
||||
_codesStream.Seek(codeLen, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
private static void SkipReloc(int relocEntriesCount)
|
||||
{
|
||||
_relocsStream.Seek(relocEntriesCount * RelocEntry.Stride, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
private static void SkipUnwindInfo(BinaryReader unwindInfosReader)
|
||||
{
|
||||
int pushEntriesLength = unwindInfosReader.ReadInt32();
|
||||
|
||||
_unwindInfosStream.Seek(pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
private static byte[] ReadCode(BinaryReader codesReader, int codeLen)
|
||||
{
|
||||
byte[] codeBuf = new byte[codeLen];
|
||||
|
@ -556,50 +612,79 @@ namespace ARMeilleure.Translation.PTC
|
|||
return tFunc;
|
||||
}
|
||||
|
||||
private static void UpdateInfo(InfoEntry infoEntry)
|
||||
{
|
||||
_infosStream.Seek(-InfoEntry.Stride, SeekOrigin.Current);
|
||||
|
||||
// WriteInfo.
|
||||
_infosWriter.Write((ulong)infoEntry.Address);
|
||||
_infosWriter.Write((ulong)infoEntry.GuestSize);
|
||||
_infosWriter.Write((bool)infoEntry.HighCq);
|
||||
_infosWriter.Write((bool)infoEntry.Stubbed);
|
||||
_infosWriter.Write((int)infoEntry.CodeLen);
|
||||
_infosWriter.Write((int)infoEntry.RelocEntriesCount);
|
||||
}
|
||||
|
||||
private static void StubCode(int codeLen)
|
||||
{
|
||||
for (int i = 0; i < codeLen; i++)
|
||||
{
|
||||
_codesStream.WriteByte(FillingByte);
|
||||
}
|
||||
}
|
||||
|
||||
private static void StubReloc(int relocEntriesCount)
|
||||
{
|
||||
for (int i = 0; i < relocEntriesCount * RelocEntry.Stride; i++)
|
||||
{
|
||||
_relocsStream.WriteByte(FillingByte);
|
||||
}
|
||||
}
|
||||
|
||||
private static void StubUnwindInfo(BinaryReader unwindInfosReader)
|
||||
{
|
||||
int pushEntriesLength = unwindInfosReader.ReadInt32();
|
||||
|
||||
for (int i = 0; i < pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride; i++)
|
||||
{
|
||||
_unwindInfosStream.WriteByte(FillingByte);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void MakeAndSaveTranslations(ConcurrentDictionary<ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable)
|
||||
{
|
||||
if (PtcProfiler.ProfiledFuncs.Count == 0)
|
||||
var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(funcs);
|
||||
|
||||
if (profiledFuncsToTranslate.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_translateCount = 0;
|
||||
_rejitCount = 0;
|
||||
|
||||
ThreadPool.QueueUserWorkItem(TranslationLogger, (funcs.Count, PtcProfiler.ProfiledFuncs.Count));
|
||||
ThreadPool.QueueUserWorkItem(TranslationLogger, profiledFuncsToTranslate.Count);
|
||||
|
||||
int maxDegreeOfParallelism = (Environment.ProcessorCount * 3) / 4;
|
||||
|
||||
Parallel.ForEach(PtcProfiler.ProfiledFuncs, new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism }, (item, state) =>
|
||||
Parallel.ForEach(profiledFuncsToTranslate, new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism }, (item, state) =>
|
||||
{
|
||||
ulong address = item.Key;
|
||||
|
||||
Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address));
|
||||
|
||||
if (!funcs.ContainsKey(address))
|
||||
TranslatedFunction func = Translator.Translate(memory, jumpTable, address, item.Value.mode, item.Value.highCq);
|
||||
|
||||
bool isAddressUnique = funcs.TryAdd(address, func);
|
||||
|
||||
Debug.Assert(isAddressUnique, $"The address 0x{address:X16} is not unique.");
|
||||
|
||||
if (func.HighCq)
|
||||
{
|
||||
TranslatedFunction func = Translator.Translate(memory, jumpTable, address, item.Value.mode, item.Value.highCq);
|
||||
|
||||
funcs.TryAdd(address, func);
|
||||
|
||||
if (func.HighCq)
|
||||
{
|
||||
jumpTable.RegisterFunction(address, func);
|
||||
}
|
||||
|
||||
Interlocked.Increment(ref _translateCount);
|
||||
}
|
||||
else if (item.Value.highCq && !funcs[address].HighCq)
|
||||
{
|
||||
TranslatedFunction func = Translator.Translate(memory, jumpTable, address, item.Value.mode, highCq: true);
|
||||
|
||||
funcs[address] = func;
|
||||
|
||||
jumpTable.RegisterFunction(address, func);
|
||||
|
||||
Interlocked.Increment(ref _rejitCount);
|
||||
}
|
||||
|
||||
Interlocked.Increment(ref _translateCount);
|
||||
|
||||
if (State != PtcState.Enabled)
|
||||
{
|
||||
state.Stop();
|
||||
|
@ -608,30 +693,27 @@ namespace ARMeilleure.Translation.PTC
|
|||
|
||||
_loggerEvent.Set();
|
||||
|
||||
if (_translateCount != 0 || _rejitCount != 0)
|
||||
{
|
||||
PtcJumpTable.Initialize(jumpTable);
|
||||
PtcJumpTable.Initialize(jumpTable);
|
||||
|
||||
PtcJumpTable.ReadJumpTable(jumpTable);
|
||||
PtcJumpTable.ReadDynamicTable(jumpTable);
|
||||
PtcJumpTable.ReadJumpTable(jumpTable);
|
||||
PtcJumpTable.ReadDynamicTable(jumpTable);
|
||||
|
||||
ThreadPool.QueueUserWorkItem(PreSave);
|
||||
}
|
||||
ThreadPool.QueueUserWorkItem(PreSave);
|
||||
}
|
||||
|
||||
private static void TranslationLogger(object state)
|
||||
{
|
||||
const int refreshRate = 1; // Seconds.
|
||||
|
||||
(int funcsCount, int ProfiledFuncsCount) = ((int, int))state;
|
||||
int profiledFuncsToTranslateCount = (int)state;
|
||||
|
||||
do
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Ptc, $"{funcsCount + _translateCount} of {ProfiledFuncsCount} functions to translate - {_rejitCount} functions rejited");
|
||||
Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {profiledFuncsToTranslateCount} functions translated");
|
||||
}
|
||||
while (!_loggerEvent.WaitOne(refreshRate * 1000));
|
||||
|
||||
Logger.Info?.Print(LogClass.Ptc, $"{funcsCount + _translateCount} of {ProfiledFuncsCount} functions to translate - {_rejitCount} functions rejited");
|
||||
Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {profiledFuncsToTranslateCount} functions translated");
|
||||
}
|
||||
|
||||
internal static void WriteInfoCodeReloc(ulong address, ulong guestSize, bool highCq, PtcInfo ptcInfo)
|
||||
|
@ -642,6 +724,7 @@ namespace ARMeilleure.Translation.PTC
|
|||
_infosWriter.Write((ulong)address); // InfoEntry.Address
|
||||
_infosWriter.Write((ulong)guestSize); // InfoEntry.GuestSize
|
||||
_infosWriter.Write((bool)highCq); // InfoEntry.HighCq
|
||||
_infosWriter.Write((bool)false); // InfoEntry.Stubbed
|
||||
_infosWriter.Write((int)ptcInfo.CodeStream.Length); // InfoEntry.CodeLen
|
||||
_infosWriter.Write((int)ptcInfo.RelocEntriesCount); // InfoEntry.RelocEntriesCount
|
||||
|
||||
|
@ -661,12 +744,25 @@ namespace ARMeilleure.Translation.PTC
|
|||
return (ulong)HardwareCapabilities.FeatureInfoEdx << 32 | (uint)HardwareCapabilities.FeatureInfoEcx;
|
||||
}
|
||||
|
||||
private static uint GetOSPlatform()
|
||||
{
|
||||
uint osPlatform = 0u;
|
||||
|
||||
osPlatform |= (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD) ? 1u : 0u) << 0;
|
||||
osPlatform |= (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? 1u : 0u) << 1;
|
||||
osPlatform |= (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 1u : 0u) << 2;
|
||||
osPlatform |= (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 1u : 0u) << 3;
|
||||
|
||||
return osPlatform;
|
||||
}
|
||||
|
||||
private struct Header
|
||||
{
|
||||
public string Magic;
|
||||
|
||||
public int CacheFileVersion;
|
||||
public uint CacheFileVersion;
|
||||
public ulong FeatureInfo;
|
||||
public uint OSPlatform;
|
||||
|
||||
public int InfosLen;
|
||||
public int CodesLen;
|
||||
|
@ -676,11 +772,12 @@ namespace ARMeilleure.Translation.PTC
|
|||
|
||||
private struct InfoEntry
|
||||
{
|
||||
public const int Stride = 25; // Bytes.
|
||||
public const int Stride = 26; // Bytes.
|
||||
|
||||
public ulong Address;
|
||||
public ulong GuestSize;
|
||||
public bool HighCq;
|
||||
public bool Stubbed;
|
||||
public int CodeLen;
|
||||
public int RelocEntriesCount;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue