• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SamboyCoding / Cpp2IL / 10704905744

04 Sep 2024 03:33PM UTC coverage: 28.42% (-0.4%) from 28.835%
10704905744

push

github

SamboyCoding
Implement some x86 instructions

1227 of 6002 branches covered (20.44%)

Branch coverage included in aggregate %.

3 of 132 new or added lines in 3 files covered. (2.27%)

2 existing lines in 2 files now uncovered.

3317 of 9987 relevant lines covered (33.21%)

102420.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.16
/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Linq;
4
using Cpp2IL.Core.Api;
5
using Cpp2IL.Core.Extensions;
6
using Cpp2IL.Core.Il2CppApiFunctions;
7
using Cpp2IL.Core.ISIL;
8
using Cpp2IL.Core.Logging;
9
using Cpp2IL.Core.Model.Contexts;
10
using Cpp2IL.Core.Utils;
11
using Iced.Intel;
12
using LibCpp2IL.BinaryStructures;
13

14
namespace Cpp2IL.Core.InstructionSets;
15

16
// This is honestly an X64InstructionSet by all means. Everything here screams "I AM X64".
17
public class X86InstructionSet : Cpp2IlInstructionSet
18
{
19
    private static readonly MasmFormatter Formatter = new();
×
20
    private static readonly StringOutput Output = new();
×
21

22
    private static string FormatInstructionInternal(Instruction instruction)
23
    {
24
        Formatter.Format(instruction, Output);
×
25
        return Output.ToStringAndReset();
×
26
    }
27

28
    public static string FormatInstruction(Instruction instruction)
29
    {
30
        lock (Formatter)
×
31
        {
32
            return FormatInstructionInternal(instruction);
×
33
        }
34
    }
×
35

36
    public override Memory<byte> GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator) => X86Utils.GetRawManagedOrCaCacheGenMethodBody(context.UnderlyingPointer, isAttributeGenerator);
288,096✔
37

38
    public override BaseKeyFunctionAddresses CreateKeyFunctionAddressesInstance() => new X86KeyFunctionAddresses();
×
39

40
    public override string PrintAssembly(MethodAnalysisContext context)
41
    {
42
        lock (Formatter)
×
43
        {
44
            var insns = X86Utils.Iterate(X86Utils.GetRawManagedOrCaCacheGenMethodBody(context.UnderlyingPointer, false), context.UnderlyingPointer);
×
45

46
            return string.Join("\n", insns.Select(FormatInstructionInternal));
×
47
        }
48
    }
×
49

50
    public override List<InstructionSetIndependentInstruction> GetIsilFromMethod(MethodAnalysisContext context)
51
    {
52
        var builder = new IsilBuilder();
×
53

54
        foreach (var instruction in X86Utils.Iterate(context.RawBytes, context.UnderlyingPointer))
×
55
        {
56
            ConvertInstructionStatement(instruction, builder, context);
×
57
        }
58

59
        builder.FixJumps();
×
60

61
        return builder.BackingStatementList;
×
62
    }
63

64

65
    private void ConvertInstructionStatement(Instruction instruction, IsilBuilder builder, MethodAnalysisContext context)
66
    {
67
        var callNoReturn = false;
×
68

69
        switch (instruction.Mnemonic)
×
70
        {
71
            case Mnemonic.Mov:
72
            case Mnemonic.Movzx: // For all intents and purposes we don't care about zero-extending
73
            case Mnemonic.Movsx: // move with sign-extendign
74
            case Mnemonic.Movsxd: // same
75
            case Mnemonic.Movaps: // Movaps is basically just a mov but with the potential future detail that the size is dependent on reg size
76
            case Mnemonic.Movups: // Movaps but unaligned
77
            case Mnemonic.Movss: // Same as movaps but for floats
78
            case Mnemonic.Movd: // Mov but specifically dword
79
            case Mnemonic.Movq: // Mov but specifically qword
80
            case Mnemonic.Movsd: // Mov but specifically double
81
            case Mnemonic.Movdqa: // Movaps but multiple integers at once in theory
82
            case Mnemonic.Cvtdq2ps: // Technically a convert double to single, but for analysis purposes we can just treat it as a move
83
            case Mnemonic.Cvtps2pd: // same, but float to double
84
            case Mnemonic.Cvttsd2si: // same, but double to integer
85
            case Mnemonic.Movdqu: // DEST[127:0] := SRC[127:0]
86
                builder.Move(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1));
×
87
                break;
×
88
            case Mnemonic.Cbw: // AX := sign-extend AL
NEW
89
                builder.Move(instruction.IP, InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.AX)), 
×
NEW
90
                    InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.AL)));
×
NEW
91
                break;
×
92
            case Mnemonic.Cwde: // EAX := sign-extend AX
NEW
93
                builder.Move(instruction.IP, InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.EAX)), 
×
NEW
94
                    InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.AX)));
×
NEW
95
                break;
×
96
            case Mnemonic.Cdqe: // RAX := sign-extend EAX
NEW
97
                builder.Move(instruction.IP, InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.RAX)), 
×
NEW
98
                    InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.EAX)));
×
NEW
99
                break;
×
100
            // it's very unsafe if there's been a jump to the next instruction here before.
101
            case Mnemonic.Cwd: // Convert Word to Doubleword
102
            {
103
                // The CWD instruction copies the sign (bit 15) of the value in the AX register into every bit position in the DX register
NEW
104
                var temp = InstructionSetIndependentOperand.MakeRegister("TEMP");
×
NEW
105
                builder.Move(instruction.IP, temp, InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.AX))); // TEMP = AX
×
NEW
106
                builder.ShiftRight(instruction.IP, temp, InstructionSetIndependentOperand.MakeImmediate(15)); // TEMP >>= 15
×
NEW
107
                builder.Compare(instruction.IP, temp, InstructionSetIndependentOperand.MakeImmediate(1)); // temp == 1
×
NEW
108
                builder.JumpIfNotEqual(instruction.IP, instruction.IP + 1);
×
109
                // temp == 1 ? DX := ushort.Max (1111111111) or DX := 0
NEW
110
                builder.Move(instruction.IP, InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.DX)), InstructionSetIndependentOperand.MakeImmediate(ushort.MaxValue));
×
NEW
111
                builder.Goto(instruction.IP, instruction.IP + 2);
×
NEW
112
                builder.Move(instruction.IP + 1, InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.DX)), InstructionSetIndependentOperand.MakeImmediate(0));
×
NEW
113
                builder.Nop(instruction.IP + 2);
×
NEW
114
                break;
×
115
            }
116
            case Mnemonic.Cdq: // Convert Doubleword to Quadword
117
            {
118
                // The CDQ instruction copies the sign (bit 31) of the value in the EAX register into every bit position in the EDX register.
NEW
119
                var temp = InstructionSetIndependentOperand.MakeRegister("TEMP");
×
NEW
120
                builder.Move(instruction.IP, temp, InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.EAX))); // TEMP = EAX
×
NEW
121
                builder.ShiftRight(instruction.IP, temp, InstructionSetIndependentOperand.MakeImmediate(31)); // TEMP >>= 31
×
NEW
122
                builder.Compare(instruction.IP, temp, InstructionSetIndependentOperand.MakeImmediate(1)); // temp == 1
×
NEW
123
                builder.JumpIfNotEqual(instruction.IP, instruction.IP + 1);
×
124
                // temp == 1 ? EDX := uint.Max (1111111111) or EDX := 0
NEW
125
                builder.Move(instruction.IP, InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.EDX)), InstructionSetIndependentOperand.MakeImmediate(uint.MaxValue));
×
NEW
126
                builder.Goto(instruction.IP, instruction.IP + 2);
×
NEW
127
                builder.Move(instruction.IP + 1, InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.EDX)), InstructionSetIndependentOperand.MakeImmediate(0));
×
NEW
128
                builder.Nop(instruction.IP + 2);
×
NEW
129
                break;
×
130
            }
131
            case Mnemonic.Cqo: // same...
132
            {
133
                // The CQO instruction copies the sign (bit 63) of the value in the EAX register into every bit position in the RDX register.
NEW
134
                var temp = InstructionSetIndependentOperand.MakeRegister("TEMP");
×
NEW
135
                builder.Move(instruction.IP, temp, InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.RAX))); // TEMP = RAX
×
NEW
136
                builder.ShiftRight(instruction.IP, temp, InstructionSetIndependentOperand.MakeImmediate(63)); // TEMP >>= 63
×
NEW
137
                builder.Compare(instruction.IP, temp, InstructionSetIndependentOperand.MakeImmediate(1)); // temp == 1
×
NEW
138
                builder.JumpIfNotEqual(instruction.IP, instruction.IP + 1);
×
139
                // temp == 1 ? RDX := ulong.Max (1111111111) or RDX := 0
NEW
140
                builder.Move(instruction.IP, InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.RDX)), InstructionSetIndependentOperand.MakeImmediate(ulong.MaxValue));
×
NEW
141
                builder.Goto(instruction.IP, instruction.IP + 2);
×
NEW
142
                builder.Move(instruction.IP + 1, InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.RDX)), InstructionSetIndependentOperand.MakeImmediate(0));
×
NEW
143
                builder.Nop(instruction.IP + 2);
×
NEW
144
                break;
×
145
            }
146
            case Mnemonic.Lea:
147
                builder.LoadAddress(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1));
×
148
                break;
×
149
            case Mnemonic.Xor:
150
            case Mnemonic.Xorps: //xorps is just floating point xor
151
                if (instruction.Op0Kind == OpKind.Register && instruction.Op1Kind == OpKind.Register && instruction.Op0Register == instruction.Op1Register)
×
152
                    builder.Move(instruction.IP, ConvertOperand(instruction, 0), InstructionSetIndependentOperand.MakeImmediate(0));
×
153
                else
154
                    builder.Xor(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 0), ConvertOperand(instruction, 1));
×
155
                break;
×
156
            case Mnemonic.Shl: // unsigned shift
157
            case Mnemonic.Sal: // signed shift
158
                builder.ShiftLeft(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1));
×
159
                break;
×
160
            case Mnemonic.Shr: // unsigned shift
161
            case Mnemonic.Sar: // signed shift
162
                builder.ShiftRight(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1));
×
163
                break;
×
164
            case Mnemonic.And:
165
            case Mnemonic.Andps: //Floating point and
166
                builder.And(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 0), ConvertOperand(instruction, 1));
×
167
                break;
×
168
            case Mnemonic.Or:
169
            case Mnemonic.Orps: //Floating point or
170
                builder.Or(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 0), ConvertOperand(instruction, 1));
×
171
                break;
×
172
            case Mnemonic.Not:
NEW
173
                builder.Neg(instruction.IP, ConvertOperand(instruction, 0));
×
NEW
174
                break;
×
175
            case Mnemonic.Neg: // dest := -dest
NEW
176
                builder.Neg(instruction.IP, ConvertOperand(instruction, 0));
×
UNCOV
177
                break;
×
178
            case Mnemonic.Imul:
179
                if (instruction.OpCount == 1)
×
180
                {
181
                    int opSize = instruction.Op0Kind == OpKind.Register ? instruction.Op0Register.GetSize() : instruction.MemorySize.GetSize();
×
182
                    switch (opSize) // TODO: I don't know how to work with dual registers here, I left hints though
183
                    {
184
                        case 1: // Op0 * AL -> AX
185
                            builder.Multiply(instruction.IP, Register.AX.MakeIndependent(), ConvertOperand(instruction, 0), Register.AL.MakeIndependent());
×
186
                            return;
×
187
                        case 2: // Op0 * AX -> DX:AX
188

189
                            break;
190
                        case 4: // Op0 * EAX -> EDX:EAX
191

192
                            break;
193
                        case 8: // Op0 * RAX -> RDX:RAX
194

195
                            break;
196
                        default: // prob 0, I think fallback to architecture alignment would be good here(issue: idk how to find out arch alignment)
197

198
                            break;
199
                    }
200

201
                    // if got to here, it didn't work
202
                    goto default;
203
                }
204
                else if (instruction.OpCount == 3) builder.Multiply(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1), ConvertOperand(instruction, 2));
×
205
                else builder.Multiply(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 0), ConvertOperand(instruction, 1));
×
206

207
                break;
×
208
            case Mnemonic.Mulss:
209
            case Mnemonic.Vmulss:
210
                if (instruction.OpCount == 3)
×
211
                    builder.Multiply(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1), ConvertOperand(instruction, 2));
×
212
                else if (instruction.OpCount == 2)
×
213
                    builder.Multiply(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 0), ConvertOperand(instruction, 1));
×
214
                else
215
                    goto default;
216

217
                break;
218
            
219
            case Mnemonic.Divss: // Divide Scalar Single Precision Floating-Point Values. DEST[31:0] = DEST[31:0] / SRC[31:0]
NEW
220
                builder.Divide(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 0), ConvertOperand(instruction, 1));
×
NEW
221
                break;
×
222
            case Mnemonic.Vdivss: // VEX Divide Scalar Single Precision Floating-Point Values. DEST[31:0] = SRC1[31:0] / SRC2[31:0]
NEW
223
                builder.Divide(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1), ConvertOperand(instruction, 2));
×
NEW
224
                break;
×
225
            
226
            case Mnemonic.Ret:
227
                // TODO: Verify correctness of operation with Vectors.
228

229
                // On x32, this will require better engineering since ulongs are handled somehow differently (return in 2 registers, I think?)
230
                // The x64 prototype should work.
231
                // Are st* registers even used in il2cpp games?
232

233
                if (context.IsVoid)
×
234
                    builder.Return(instruction.IP);
×
235
                else if (context.Definition?.RawReturnType?.Type is Il2CppTypeEnum.IL2CPP_TYPE_R4 or Il2CppTypeEnum.IL2CPP_TYPE_R8)
×
236
                    builder.Return(instruction.IP, InstructionSetIndependentOperand.MakeRegister("xmm0"));
×
237
                else
238
                    builder.Return(instruction.IP, InstructionSetIndependentOperand.MakeRegister("rax"));
×
239
                break;
×
240
            case Mnemonic.Push:
241
                //var operandSize = instruction.Op0Kind == OpKind.Register ? instruction.Op0Register.GetSize() : instruction.MemorySize.GetSize();
242
                builder.Push(instruction.IP, InstructionSetIndependentOperand.MakeRegister("rsp"), ConvertOperand(instruction, 0));
×
243
                //builder.ShiftStack(instruction.IP, -operandSize);
244
                break;
×
245
            case Mnemonic.Pop:
246
                //var operandSize = instruction.Op0Kind == OpKind.Register ? instruction.Op0Register.GetSize() : instruction.MemorySize.GetSize();
247
                //builder.ShiftStack(instruction.IP, operandSize);
248
                builder.Pop(instruction.IP, InstructionSetIndependentOperand.MakeRegister("rsp"), ConvertOperand(instruction, 0));
×
249
                break;
×
250
            case Mnemonic.Sub:
251
            case Mnemonic.Add:
252
                var isSubtract = instruction.Mnemonic == Mnemonic.Sub;
×
253

254
                // Special case - stack shift
255
                if (instruction.Op0Register == Register.RSP && instruction.Op1Kind.IsImmediate())
×
256
                {
257
                    var amount = (int)instruction.GetImmediate(1);
×
258
                    builder.ShiftStack(instruction.IP, isSubtract ? -amount : amount);
×
259
                    break;
×
260
                }
261

262
                var left = ConvertOperand(instruction, 0);
×
263
                var right = ConvertOperand(instruction, 1);
×
264
                if (isSubtract)
×
265
                    builder.Subtract(instruction.IP, left, left, right);
×
266
                else
267
                    builder.Add(instruction.IP, left, left, right);
×
268

269
                break;
×
270
            case Mnemonic.Addss:
271
            case Mnemonic.Subss:
272
            {
273
                // Addss and subss are just floating point add/sub, but we don't need to handle the stack stuff
274
                // But we do need to handle 2 vs 3 operand forms
275
                InstructionSetIndependentOperand dest;
276
                InstructionSetIndependentOperand src1;
277
                InstructionSetIndependentOperand src2;
278

279
                if (instruction.OpCount == 3)
×
280
                {
281
                    //dest, src1, src2
282
                    dest = ConvertOperand(instruction, 0);
×
283
                    src1 = ConvertOperand(instruction, 1);
×
284
                    src2 = ConvertOperand(instruction, 2);
×
285
                }
286
                else if (instruction.OpCount == 2)
×
287
                {
288
                    //DestAndSrc1, Src2
289
                    dest = ConvertOperand(instruction, 0);
×
290
                    src1 = dest;
×
291
                    src2 = ConvertOperand(instruction, 1);
×
292
                }
293
                else
294
                    goto default;
295

296
                if (instruction.Mnemonic == Mnemonic.Subss)
×
297
                    builder.Subtract(instruction.IP, dest, src1, src2);
×
298
                else
299
                    builder.Add(instruction.IP, dest, src1, src2);
×
300
                break;
×
301
            }
302
            // The following pair of instructions does not update the Carry Flag (CF):
303
            case Mnemonic.Dec:
304
                builder.Subtract(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 0), InstructionSetIndependentOperand.MakeImmediate(1));
×
305
                break;
×
306
            case Mnemonic.Inc:
307
                builder.Add(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 0), InstructionSetIndependentOperand.MakeImmediate(1));
×
308
                break;
×
309

310
            case Mnemonic.Shufps: // Packed Interleave Shuffle of Quadruplets of Single Precision Floating-Point Values
311
            {
NEW
312
                if (instruction.Op1Kind == OpKind.Memory)
×
313
                    goto default;
314
                
NEW
315
                var imm = instruction.Immediate8;
×
NEW
316
                var src1 = X86Utils.GetRegisterName(instruction.Op0Register);
×
NEW
317
                var src2 = X86Utils.GetRegisterName(instruction.Op1Register);
×
NEW
318
                var dest = "XMM_TEMP";
×
319
                //TEMP_DEST[31:0] := Select4(SRC1[127:0], imm8[1:0]);
NEW
320
                builder.Move(instruction.IP, ConvertVector(dest, 0), ConvertVector(src1, imm & 0b11)); 
×
321
                //TEMP_DEST[63:32] := Select4(SRC1[127:0], imm8[3:2]);
NEW
322
                builder.Move(instruction.IP, ConvertVector(dest, 1), ConvertVector(src1, (imm >> 2) & 0b11)); 
×
323
                //TEMP_DEST[95:64] := Select4(SRC2[127:0], imm8[5:4]);
NEW
324
                builder.Move(instruction.IP, ConvertVector(dest, 2), ConvertVector(src2, (imm >> 4) & 0b11));
×
325
                //TEMP_DEST[127:96] := Select4(SRC2[127:0], imm8[7:6]);
NEW
326
                builder.Move(instruction.IP, ConvertVector(dest, 3), ConvertVector(src2, (imm >> 6) & 0b11));
×
327
                // where Select4(regSlice, imm) => regSlice.[imm switch => { 0 => 0..31, 1 => 32..63, 2 => 64..95, 3 => 96...127 }];
NEW
328
                builder.Move(instruction.IP, ConvertOperand(instruction, 0), InstructionSetIndependentOperand.MakeRegister(dest)); // DEST = TEMP_DEST
×
NEW
329
                break;
×
330

331
                static InstructionSetIndependentOperand ConvertVector(string reg, int imm) =>
NEW
332
                    InstructionSetIndependentOperand.MakeVectorElement(reg, IsilVectorRegisterElementOperand.VectorElementWidth.S, imm);
×
333
            }
334
                
335
            case Mnemonic.Unpcklps : // Unpack and Interleave Low Packed Single Precision Floating-Point Values
336
            {
NEW
337
                if (instruction.Op1Kind == OpKind.Memory)
×
338
                    goto default;
339
                
NEW
340
                var src1 = X86Utils.GetRegisterName(instruction.Op0Register);
×
NEW
341
                var src2 = X86Utils.GetRegisterName(instruction.Op1Register);
×
NEW
342
                var dest = "XMM_TEMP";
×
NEW
343
                builder.Move(instruction.IP, ConvertVector(dest, 0), ConvertVector(src1, 0)); //TMP_DEST[31:0] := SRC1[31:0]
×
NEW
344
                builder.Move(instruction.IP, ConvertVector(dest, 1), ConvertVector(src2, 0)); //TMP_DEST[63:32] := SRC2[31:0]
×
NEW
345
                builder.Move(instruction.IP, ConvertVector(dest, 2), ConvertVector(src1, 1)); //TMP_DEST[95:64] := SRC1[63:32]
×
NEW
346
                builder.Move(instruction.IP, ConvertVector(dest, 3), ConvertVector(src2, 1)); //TMP_DEST[127:96] := SRC2[63:32]
×
NEW
347
                builder.Move(instruction.IP, ConvertOperand(instruction, 0), InstructionSetIndependentOperand.MakeRegister(dest)); // DEST = TEMP_DEST
×
NEW
348
                break;
×
349

350
                static InstructionSetIndependentOperand ConvertVector(string reg, int imm) =>
NEW
351
                    InstructionSetIndependentOperand.MakeVectorElement(reg, IsilVectorRegisterElementOperand.VectorElementWidth.S, imm);
×
352
            }
353
            
354
            case Mnemonic.Call:
355
                // We don't try and resolve which method is being called, but we do need to know how many parameters it has
356
                // I would hope that all of these methods have the same number of arguments, else how can they be inlined?
357

358
                var target = instruction.NearBranchTarget;
×
359

360
                if (context.AppContext.MethodsByAddress.TryGetValue(target, out var possibleMethods))
×
361
                {
362
                    if (possibleMethods.Count == 1)
×
363
                    {
364
                        builder.Call(instruction.IP, target, X64CallingConventionResolver.ResolveForManaged(possibleMethods[0]));
×
365
                    }
366
                    else
367
                    {
368
                        MethodAnalysisContext ctx = null!;
×
369
                        var lpars = -1;
×
370

371
                        // Very naive approach, folds with structs in parameters if GCC is used:
372
                        foreach (var method in possibleMethods)
×
373
                        {
374
                            var pars = method.ParameterCount;
×
375
                            if (method.IsStatic) pars++;
×
376
                            if (pars > lpars)
×
377
                            {
378
                                lpars = pars;
×
379
                                ctx = method;
×
380
                            }
381
                        }
382

383
                        // On post-analysis, you can discard methods according to the registers used, see X64CallingConventionResolver.
384
                        // This is less effective on GCC because MSVC doesn't overlap registers.
385

386
                        builder.Call(instruction.IP, target, X64CallingConventionResolver.ResolveForManaged(ctx));
×
387
                    }
388
                }
389
                else
390
                {
391
                    // This isn't a managed method, so for now we don't know its parameter count.
392
                    // This will need to be rewritten if we ever stumble upon an unmanaged method that accepts more than 4 parameters.
393
                    // These can be converted to dedicated ISIL instructions for specific API functions at a later stage. (by a post-processing step)
394

395
                    builder.Call(instruction.IP, target, X64CallingConventionResolver.ResolveForUnmanaged(context.AppContext, target));
×
396
                }
397

398
                if (callNoReturn)
×
399
                {
400
                    // Our function decided to jump into a thunk or do a funny return.
401
                    // We will insert a return after the call.
402
                    // According to common sense, such callee must have the same return value as the caller, unless it's __noreturn.
403
                    // I hope someone else will catch up on this and figure out non-returning functions.
404

405
                    // TODO: Determine whether a function is an actual thunk and it's *technically better* to duplicate code for it, or if it's a regular retcall.
406
                    // Basic implementation may use context.AppContext.MethodsByAddress, but this doesn't catch thunks only.
407
                    // For example, SWDT often calls gc::GarbageCollector::SetWriteBarrier through a long jmp chain. That's a whole function, not just a thunk.
408

409
                    goto case Mnemonic.Ret;
×
410
                }
411

412
                break;
413
            case Mnemonic.Test:
414
                if (instruction.Op0Kind == OpKind.Register && instruction.Op1Kind == OpKind.Register && instruction.Op0Register == instruction.Op1Register)
×
415
                {
416
                    builder.Compare(instruction.IP, ConvertOperand(instruction, 0), InstructionSetIndependentOperand.MakeImmediate(0));
×
417
                    break;
×
418
                }
419

420
                //Fall through to cmp, as test is just a cmp that doesn't set flags
421
                goto case Mnemonic.Cmp;
422
            case Mnemonic.Cmp:
423
            case Mnemonic.Comiss: //comiss is just a floating point compare dest[31:0] == src[31:0]
424
            case Mnemonic.Ucomiss: // same, but unsigned
425
                builder.Compare(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1));
×
426
                break;
×
427
            
428
            case Mnemonic.Cmove: // move if condition
429
            case Mnemonic.Cmovne: 
430
            case Mnemonic.Cmova:
431
            case Mnemonic.Cmovg:
432
            case Mnemonic.Cmovae:
433
            case Mnemonic.Cmovge:
434
            case Mnemonic.Cmovb:
435
            case Mnemonic.Cmovl:
436
            case Mnemonic.Cmovbe:
437
            case Mnemonic.Cmovle: 
438
            case Mnemonic.Cmovs: 
439
            case Mnemonic.Cmovns: 
NEW
440
                switch (instruction.Mnemonic)
×
441
                {
442
                    case Mnemonic.Cmove: // equals
NEW
443
                        builder.JumpIfNotEqual(instruction.IP, instruction.IP + 1); // skip if not eq
×
NEW
444
                        break;
×
445
                    case Mnemonic.Cmovne: // not equals
NEW
446
                        builder.JumpIfEqual(instruction.IP, instruction.IP + 1); // skip if eq
×
NEW
447
                        break;
×
448
                    case Mnemonic.Cmovs: // sign
NEW
449
                        builder.JumpIfNotSign(instruction.IP, instruction.IP + 1); // skip if not sign
×
NEW
450
                        break;
×
451
                    case Mnemonic.Cmovns: // not sign
NEW
452
                        builder.JumpIfSign(instruction.IP, instruction.IP + 1); // skip if sign
×
NEW
453
                        break;
×
454
                    case Mnemonic.Cmova:
455
                    case Mnemonic.Cmovg: // greater
NEW
456
                        builder.JumpIfLessOrEqual(instruction.IP, instruction.IP + 1); // skip if not gt
×
NEW
457
                        break;
×
458
                    case Mnemonic.Cmovae:
459
                    case Mnemonic.Cmovge: // greater or eq
NEW
460
                        builder.JumpIfLess(instruction.IP, instruction.IP + 1); // skip if not gt or eq
×
NEW
461
                        break;
×
462
                    case Mnemonic.Cmovb:
463
                    case Mnemonic.Cmovl: // less
NEW
464
                        builder.JumpIfGreaterOrEqual(instruction.IP, instruction.IP + 1); // skip if not lt
×
NEW
465
                        break;
×
466
                    case Mnemonic.Cmovbe:
467
                    case Mnemonic.Cmovle: // less or eq
NEW
468
                        builder.JumpIfGreater(instruction.IP, instruction.IP + 1); // skip if not lt or eq
×
469
                        break;
470
                }
NEW
471
                builder.Move(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1)); // set if cond
×
NEW
472
                builder.Nop(instruction.IP + 1);
×
NEW
473
                break;
×
474

475
            case Mnemonic.Maxss: // dest < src ? src : dest
476
            case Mnemonic.Minss: // dest > src ? src : dest
477
            {
NEW
478
                var dest = ConvertOperand(instruction, 0);
×
NEW
479
                var src = ConvertOperand(instruction, 1);
×
NEW
480
                builder.Compare(instruction.IP, dest, src); // compare dest & src
×
NEW
481
                if (instruction.Mnemonic == Mnemonic.Maxss)
×
NEW
482
                    builder.JumpIfGreaterOrEqual(instruction.IP, instruction.IP + 1); // enter if dest < src
×
483
                else
NEW
484
                    builder.JumpIfLessOrEqual(instruction.IP, instruction.IP + 1); // enter if dest > src
×
NEW
485
                builder.Move(instruction.IP, dest, src); // dest = src
×
NEW
486
                builder.Nop(instruction.IP + 1); // exit for IF
×
NEW
487
                break;
×
488
            }
489
            
490
            case Mnemonic.Cmpxchg: // compare and exchange
491
            {
NEW
492
                var accumulator = InstructionSetIndependentOperand.MakeRegister(instruction.Op1Register.GetSize() switch
×
NEW
493
                {
×
NEW
494
                    8 => X86Utils.GetRegisterName(Register.RAX),
×
NEW
495
                    4 => X86Utils.GetRegisterName(Register.EAX),
×
NEW
496
                    2 => X86Utils.GetRegisterName(Register.AX),
×
NEW
497
                    1 => X86Utils.GetRegisterName(Register.AL),
×
NEW
498
                    _ => throw new NotSupportedException("unexpected behavior")
×
NEW
499
                });
×
NEW
500
                var dest = ConvertOperand(instruction, 0);
×
NEW
501
                var src = ConvertOperand(instruction, 1);
×
NEW
502
                builder.Compare(instruction.IP, accumulator, dest);
×
NEW
503
                builder.JumpIfNotEqual(instruction.IP, instruction.IP + 1); // if accumulator == dest
×
504
                // SET ZF = 1
NEW
505
                builder.Move(instruction.IP, dest, src); // DEST = SRC
×
NEW
506
                builder.Goto(instruction.IP, instruction.IP + 2); // END IF
×
507
                // ELSE
508
                // SET ZF = 0
NEW
509
                builder.Move(instruction.IP + 1, accumulator, dest); // accumulator = dest
×
510
                
NEW
511
                builder.Nop(instruction.IP + 2); // exit for IF
×
NEW
512
                break;
×
513
            }
514
            
515
            case Mnemonic.Jmp:
516
                if (instruction.Op0Kind != OpKind.Register)
×
517
                {
518
                    var jumpTarget = instruction.NearBranchTarget;
×
519

520
                    var methodEnd = instruction.IP + (ulong)context.RawBytes.Length;
×
521
                    var methodStart = context.UnderlyingPointer;
×
522

523
                    if (jumpTarget < methodStart || jumpTarget > methodEnd)
×
524
                    {
525
                        callNoReturn = true;
×
526
                        goto case Mnemonic.Call;
×
527
                    }
528
                    else
529
                    {
530
                        builder.Goto(instruction.IP, jumpTarget);
×
531
                        break;
×
532
                    }
533
                }
NEW
534
                if (instruction.Op0Kind == OpKind.Register) // ex: jmp rax
×
535
                {
NEW
536
                    builder.CallRegister(instruction.IP, ConvertOperand(instruction, 0), noReturn: true);
×
NEW
537
                    break;
×
538
                }
539

540
                goto default;
541
            case Mnemonic.Je:
542
                if (instruction.Op0Kind != OpKind.Register)
×
543
                {
544
                    var jumpTarget = instruction.NearBranchTarget;
×
545

546
                    builder.JumpIfEqual(instruction.IP, jumpTarget);
×
547
                    break;
×
548
                }
549

550
                goto default;
551
            case Mnemonic.Jne:
552
                if (instruction.Op0Kind != OpKind.Register)
×
553
                {
554
                    var jumpTarget = instruction.NearBranchTarget;
×
555

556
                    builder.JumpIfNotEqual(instruction.IP, jumpTarget);
×
557
                    break;
×
558
                }
559
                goto default;
560
            case Mnemonic.Js:
NEW
561
                if (instruction.Op0Kind != OpKind.Register)
×
562
                {
NEW
563
                    var jumpTarget = instruction.NearBranchTarget;
×
564

NEW
565
                    builder.JumpIfSign(instruction.IP, jumpTarget);
×
NEW
566
                    break;
×
567
                }
568
                
569
                goto default;
570
            case Mnemonic.Jns:
NEW
571
                if (instruction.Op0Kind != OpKind.Register)
×
572
                {
NEW
573
                    var jumpTarget = instruction.NearBranchTarget;
×
574

NEW
575
                    builder.JumpIfNotSign(instruction.IP, jumpTarget);
×
NEW
576
                    break;
×
577
                }
578
                
579
                goto default;
580
            case Mnemonic.Jg:
581
            case Mnemonic.Ja:
582
                if (instruction.Op0Kind != OpKind.Register)
×
583
                {
584
                    var jumpTarget = instruction.NearBranchTarget;
×
585

586
                    builder.JumpIfGreater(instruction.IP, jumpTarget);
×
587
                    break;
×
588
                }
589

590
                goto default;
591
            case Mnemonic.Jl:
592
            case Mnemonic.Jb:
593
                if (instruction.Op0Kind != OpKind.Register)
×
594
                {
595
                    var jumpTarget = instruction.NearBranchTarget;
×
596

597
                    builder.JumpIfLess(instruction.IP, jumpTarget);
×
598
                    break;
×
599
                }
600

601
                goto default;
602
            case Mnemonic.Jge:
603
            case Mnemonic.Jae:
604
                if (instruction.Op0Kind != OpKind.Register)
×
605
                {
606
                    var jumpTarget = instruction.NearBranchTarget;
×
607

608
                    builder.JumpIfGreaterOrEqual(instruction.IP, jumpTarget);
×
609
                    break;
×
610
                }
611

612
                goto default;
613
            case Mnemonic.Jle:
614
            case Mnemonic.Jbe:
615
                if (instruction.Op0Kind != OpKind.Register)
×
616
                {
617
                    var jumpTarget = instruction.NearBranchTarget;
×
618

619
                    builder.JumpIfLessOrEqual(instruction.IP, jumpTarget);
×
620
                    break;
×
621
                }
622

623
                goto default;
624
            case Mnemonic.Xchg:
625
                // There was supposed to be a push-mov-pop set but instructionAddress said no
626
                builder.Exchange(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1));
×
627
                break;
×
628
            case Mnemonic.Int:
629
            case Mnemonic.Int3:
630
                builder.Interrupt(instruction.IP); // We'll add it but eliminate later, can be used as a hint since compilers only emit it in normally unreachable code or in error handlers
×
631
                break;
×
632
            case Mnemonic.Prefetchw: // Fetches the cache line containing the specified byte from memory to the 1st or 2nd level cache, invalidating other cached copies.
633
            case Mnemonic.Nop:
634
                // While this is literally a nop and there's in theory no point emitting anything for it, it could be used as a jump target.
635
                // So we'll emit an ISIL nop for it.
636
                builder.Nop(instruction.IP);
×
637
                break;
×
638
            default:
639
                builder.NotImplemented(instruction.IP, FormatInstruction(instruction));
×
640
                break;
641
        }
642
    }
×
643

644

645
    private InstructionSetIndependentOperand ConvertOperand(Instruction instruction, int operand)
646
    {
647
        var kind = instruction.GetOpKind(operand);
×
648

649
        if (kind == OpKind.Register)
×
650
            return InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(instruction.GetOpRegister(operand)));
×
651
        if (kind.IsImmediate())
×
652
            return InstructionSetIndependentOperand.MakeImmediate(instruction.GetImmediate(operand));
×
653
        if (kind == OpKind.Memory && instruction.MemoryBase == Register.RSP)
×
654
            return InstructionSetIndependentOperand.MakeStack((int)instruction.MemoryDisplacement32);
×
655

656
        //Memory
657
        //Most complex to least complex
658

659
        if (instruction.IsIPRelativeMemoryOperand)
×
660
            return InstructionSetIndependentOperand.MakeMemory(new((long)instruction.IPRelativeMemoryAddress));
×
661

662
        //All four components
663
        if (instruction.MemoryIndex != Register.None && instruction.MemoryBase != Register.None && instruction.MemoryDisplacement64 != 0)
×
664
        {
665
            var mBase = InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(instruction.MemoryBase));
×
666
            var mIndex = InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(instruction.MemoryIndex));
×
667
            return InstructionSetIndependentOperand.MakeMemory(new(mBase, mIndex, instruction.MemoryDisplacement32, instruction.MemoryIndexScale));
×
668
        }
669

670
        //No addend
671
        if (instruction.MemoryIndex != Register.None && instruction.MemoryBase != Register.None)
×
672
        {
673
            var mBase = InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(instruction.MemoryBase));
×
674
            var mIndex = InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(instruction.MemoryIndex));
×
675
            return InstructionSetIndependentOperand.MakeMemory(new(mBase, mIndex, instruction.MemoryIndexScale));
×
676
        }
677

678
        //No index (and so no scale)
679
        if (instruction.MemoryBase != Register.None && instruction.MemoryDisplacement64 > 0)
×
680
        {
681
            var mBase = InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(instruction.MemoryBase));
×
682
            return InstructionSetIndependentOperand.MakeMemory(new(mBase, (long)instruction.MemoryDisplacement64));
×
683
        }
684

685
        //Only base
686
        if (instruction.MemoryBase != Register.None)
×
687
        {
688
            return InstructionSetIndependentOperand.MakeMemory(new(InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(instruction.MemoryBase))));
×
689
        }
690

691
        //Only addend
692
        return InstructionSetIndependentOperand.MakeMemory(new((long)instruction.MemoryDisplacement64));
×
693
    }
694
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc