aboutsummaryrefslogtreecommitdiff
path: root/ARMeilleure/Translation/PTC/PtcJumpTable.cs
diff options
context:
space:
mode:
Diffstat (limited to 'ARMeilleure/Translation/PTC/PtcJumpTable.cs')
-rw-r--r--ARMeilleure/Translation/PTC/PtcJumpTable.cs222
1 files changed, 222 insertions, 0 deletions
diff --git a/ARMeilleure/Translation/PTC/PtcJumpTable.cs b/ARMeilleure/Translation/PTC/PtcJumpTable.cs
new file mode 100644
index 00000000..0a3ae240
--- /dev/null
+++ b/ARMeilleure/Translation/PTC/PtcJumpTable.cs
@@ -0,0 +1,222 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace ARMeilleure.Translation.PTC
+{
+ [Serializable]
+ class PtcJumpTable
+ {
+ private readonly List<KeyValuePair<long, DirectHostAddress>> _jumpTable;
+ private readonly List<KeyValuePair<long, IndirectHostAddress>> _dynamicTable;
+
+ private readonly List<ulong> _targets;
+ private readonly Dictionary<ulong, LinkedList<int>> _dependants;
+
+ public int TableEnd => _jumpTable.Count;
+ public int DynTableEnd => _dynamicTable.Count;
+
+ public List<ulong> Targets => _targets;
+ public Dictionary<ulong, LinkedList<int>> Dependants => _dependants;
+
+ public PtcJumpTable()
+ {
+ _jumpTable = new List<KeyValuePair<long, DirectHostAddress>>();
+ _dynamicTable = new List<KeyValuePair<long, IndirectHostAddress>>();
+
+ _targets = new List<ulong>();
+ _dependants = new Dictionary<ulong, LinkedList<int>>();
+ }
+
+ public void Initialize(JumpTable jumpTable)
+ {
+ _targets.Clear();
+
+ foreach (ulong guestAddress in jumpTable.Targets.Keys)
+ {
+ _targets.Add(guestAddress);
+ }
+
+ _dependants.Clear();
+
+ foreach (var item in jumpTable.Dependants)
+ {
+ _dependants.Add(item.Key, new LinkedList<int>(item.Value));
+ }
+ }
+
+ public void Clear()
+ {
+ _jumpTable.Clear();
+ _dynamicTable.Clear();
+
+ _targets.Clear();
+ _dependants.Clear();
+ }
+
+ public void WriteJumpTable(JumpTable jumpTable, ConcurrentDictionary<ulong, TranslatedFunction> funcs)
+ {
+ jumpTable.ExpandIfNeededJumpTable(TableEnd);
+
+ int entry = 0;
+
+ foreach (var item in _jumpTable)
+ {
+ entry += 1;
+
+ long guestAddress = item.Key;
+ DirectHostAddress directHostAddress = item.Value;
+
+ long hostAddress;
+
+ if (directHostAddress == DirectHostAddress.CallStub)
+ {
+ hostAddress = DirectCallStubs.DirectCallStub(false).ToInt64();
+ }
+ else if (directHostAddress == DirectHostAddress.TailCallStub)
+ {
+ hostAddress = DirectCallStubs.DirectCallStub(true).ToInt64();
+ }
+ else if (directHostAddress == DirectHostAddress.Host)
+ {
+ if (funcs.TryGetValue((ulong)guestAddress, out TranslatedFunction func))
+ {
+ hostAddress = func.FuncPtr.ToInt64();
+ }
+ else
+ {
+ throw new KeyNotFoundException($"({nameof(guestAddress)} = 0x{(ulong)guestAddress:X16})");
+ }
+ }
+ else
+ {
+ throw new InvalidOperationException(nameof(directHostAddress));
+ }
+
+ IntPtr addr = jumpTable.GetEntryAddressJumpTable(entry);
+
+ Marshal.WriteInt64(addr, 0, guestAddress);
+ Marshal.WriteInt64(addr, 8, hostAddress);
+ }
+ }
+
+ public void WriteDynamicTable(JumpTable jumpTable)
+ {
+ if (JumpTable.DynamicTableElems > 1)
+ {
+ throw new NotSupportedException();
+ }
+
+ jumpTable.ExpandIfNeededDynamicTable(DynTableEnd);
+
+ int entry = 0;
+
+ foreach (var item in _dynamicTable)
+ {
+ entry += 1;
+
+ long guestAddress = item.Key;
+ IndirectHostAddress indirectHostAddress = item.Value;
+
+ long hostAddress;
+
+ if (indirectHostAddress == IndirectHostAddress.CallStub)
+ {
+ hostAddress = DirectCallStubs.IndirectCallStub(false).ToInt64();
+ }
+ else if (indirectHostAddress == IndirectHostAddress.TailCallStub)
+ {
+ hostAddress = DirectCallStubs.IndirectCallStub(true).ToInt64();
+ }
+ else
+ {
+ throw new InvalidOperationException(nameof(indirectHostAddress));
+ }
+
+ IntPtr addr = jumpTable.GetEntryAddressDynamicTable(entry);
+
+ Marshal.WriteInt64(addr, 0, guestAddress);
+ Marshal.WriteInt64(addr, 8, hostAddress);
+ }
+ }
+
+ public void ReadJumpTable(JumpTable jumpTable)
+ {
+ _jumpTable.Clear();
+
+ for (int entry = 1; entry <= jumpTable.TableEnd; entry++)
+ {
+ IntPtr addr = jumpTable.GetEntryAddressJumpTable(entry);
+
+ long guestAddress = Marshal.ReadInt64(addr, 0);
+ long hostAddress = Marshal.ReadInt64(addr, 8);
+
+ DirectHostAddress directHostAddress;
+
+ if (hostAddress == DirectCallStubs.DirectCallStub(false).ToInt64())
+ {
+ directHostAddress = DirectHostAddress.CallStub;
+ }
+ else if (hostAddress == DirectCallStubs.DirectCallStub(true).ToInt64())
+ {
+ directHostAddress = DirectHostAddress.TailCallStub;
+ }
+ else
+ {
+ directHostAddress = DirectHostAddress.Host;
+ }
+
+ _jumpTable.Add(new KeyValuePair<long, DirectHostAddress>(guestAddress, directHostAddress));
+ }
+ }
+
+ public void ReadDynamicTable(JumpTable jumpTable)
+ {
+ if (JumpTable.DynamicTableElems > 1)
+ {
+ throw new NotSupportedException();
+ }
+
+ _dynamicTable.Clear();
+
+ for (int entry = 1; entry <= jumpTable.DynTableEnd; entry++)
+ {
+ IntPtr addr = jumpTable.GetEntryAddressDynamicTable(entry);
+
+ long guestAddress = Marshal.ReadInt64(addr, 0);
+ long hostAddress = Marshal.ReadInt64(addr, 8);
+
+ IndirectHostAddress indirectHostAddress;
+
+ if (hostAddress == DirectCallStubs.IndirectCallStub(false).ToInt64())
+ {
+ indirectHostAddress = IndirectHostAddress.CallStub;
+ }
+ else if (hostAddress == DirectCallStubs.IndirectCallStub(true).ToInt64())
+ {
+ indirectHostAddress = IndirectHostAddress.TailCallStub;
+ }
+ else
+ {
+ throw new InvalidOperationException($"({nameof(hostAddress)} = 0x{hostAddress:X16})");
+ }
+
+ _dynamicTable.Add(new KeyValuePair<long, IndirectHostAddress>(guestAddress, indirectHostAddress));
+ }
+ }
+
+ private enum DirectHostAddress
+ {
+ CallStub,
+ TailCallStub,
+ Host
+ }
+
+ private enum IndirectHostAddress
+ {
+ CallStub,
+ TailCallStub
+ }
+ }
+} \ No newline at end of file