forked from MonoMod/MonoMod
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathArm64Arch.cs
157 lines (128 loc) · 5.97 KB
/
Arm64Arch.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
using MonoMod.Core.Utils;
using MonoMod.Utils;
using System;
namespace MonoMod.Core.Platforms.Architectures
{
internal sealed class Arm64Arch : IArchitecture
{
public ArchitectureKind Target => ArchitectureKind.Arm64;
public ArchitectureFeature Features => ArchitectureFeature.Immediate64;
private BytePatternCollection? lazyKnownMethodThunks;
public BytePatternCollection KnownMethodThunks => Helpers.GetOrInit(ref lazyKnownMethodThunks, CreateKnownMethodThunks);
public IAltEntryFactory AltEntryFactory => null!;
private readonly ISystem System;
public Arm64Arch(ISystem system)
{
System = system;
}
public NativeDetourInfo ComputeDetourInfo(IntPtr from, IntPtr target, int maxSizeHint)
{
// Should work for arm64 as well
x86Shared.FixSizeHint(ref maxSizeHint);
if (maxSizeHint < BranchRegisterKind.Instance.Size)
{
MMDbgLog.Warning($"Size too small for all known detour kinds! Defaulting to BranchRegister. provided size: {maxSizeHint}");
}
return new(from, target, BranchRegisterKind.Instance, null);
}
public int GetDetourBytes(NativeDetourInfo info, Span<byte> buffer, out IDisposable? allocationHandle)
{
return DetourKindBase.GetDetourBytes(info, buffer, out allocationHandle);
}
public NativeDetourInfo ComputeRetargetInfo(NativeDetourInfo detour, IntPtr target, int maxSizeHint = -1)
{
// Should work for arm64 as well
x86Shared.FixSizeHint(ref maxSizeHint);
if (DetourKindBase.TryFindRetargetInfo(detour, target, maxSizeHint, out var retarget))
{
// the detour knows how to retarget itself, we'll use that
return retarget;
}
// the detour doesn't know how to retarget itself, lets just compute a new detour to our new target
return ComputeDetourInfo(detour.From, target, maxSizeHint);
}
public int GetRetargetBytes(NativeDetourInfo original, NativeDetourInfo retarget, Span<byte> buffer,
out IDisposable? allocationHandle, out bool needsRepatch, out bool disposeOldAlloc)
{
return DetourKindBase.DoRetarget(original, retarget, buffer, out allocationHandle, out needsRepatch, out disposeOldAlloc);
}
public ReadOnlyMemory<IAllocatedMemory> CreateNativeVtableProxyStubs(IntPtr vtableBase, int vtableSize)
{
ReadOnlySpan<byte> stubData = [
0x00, 0x04, 0x40, 0xF9, // ldr x0, [x0, #8]
0x08, 0x00, 0x40, 0xF9, // ldr x8, [x0]
0x8F, 0x00, 0x00, 0x18, // ldr w15, _offset
0x08, 0x01, 0x0F, 0x8B, // add x8, x8, x15
0x08, 0x01, 0x40, 0xF9, // ldr x8, [x8]
0x00, 0x01, 0x1F, 0xD6, // br x8
0x00, 0x00, 0x00, 0x00, // _offset: .word 0x0
];
return Shared.CreateVtableStubs(System, vtableBase, vtableSize, stubData, 24, true);
}
public IAllocatedMemory CreateSpecialEntryStub(IntPtr target, IntPtr argument)
{
// CreateNativeExceptionHelper should be implemented first
throw new NotImplementedException();
}
private static BytePatternCollection CreateKnownMethodThunks()
{
const ushort An = BytePattern.SAnyValue;
const ushort Ad = BytePattern.SAddressValue;
const byte Bn = BytePattern.BAnyValue;
const byte Bd = BytePattern.BAddressValue;
if (PlatformDetection.Runtime is RuntimeKind.Framework or RuntimeKind.CoreCLR)
{
return new BytePatternCollection(
new BytePattern(
new AddressMeaning(AddressKind.Abs64),
true,
0x48, 0x85, 0xc9, 0x48
)
);
}
else
{
// TODO: Mono
return new();
}
}
private sealed class BranchRegisterKind : DetourKindBase
{
public static readonly BranchRegisterKind Instance = new();
public override int Size => 4 + 4 + 8;
public override int GetBytes(IntPtr from, IntPtr to, Span<byte> buffer, object? data, out IDisposable? allocHandle)
{
// ldr x8, _target
buffer[0] = 0x48;
buffer[1] = 0x00;
buffer[2] = 0x00;
buffer[3] = 0x58;
// br x8
buffer[4] = 0x00;
buffer[5] = 0x01;
buffer[6] = 0x1F;
buffer[7] = 0xD6;
// _target: .quad 0x0
Unsafe.WriteUnaligned(ref buffer[8], (ulong)to);
allocHandle = null;
MMDbgLog.Trace($"Detouring arm64 from 0x{from:X16} to 0x{to:X16}");
return Size;
}
public override bool TryGetRetargetInfo(NativeDetourInfo orig, IntPtr to, int maxSize, out NativeDetourInfo retargetInfo)
{
// we can always trivially retarget an abs64 detour (change the absolute constant)
retargetInfo = orig with { To = to };
return true;
}
public override int DoRetarget(NativeDetourInfo origInfo, IntPtr to, Span<byte> buffer, object? data,
out IDisposable? allocationHandle, out bool needsRepatch, out bool disposeOldAlloc)
{
needsRepatch = true;
disposeOldAlloc = true;
// the retarget logic for rel32 is just the same as the normal patch
// the patcher should re-patch the target method with the new bytes, and dispose the old allocation, if present
return GetBytes(origInfo.From, to, buffer, data, out allocationHandle);
}
}
}
}