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

SamboyCoding / Cpp2IL / 15144219583

20 May 2025 05:38PM UTC coverage: 34.28% (+0.2%) from 34.047%
15144219583

Pull #462

github

web-flow
Merge f80b990bc into 5807d2b6c
Pull Request #462: Support overriding member types

1799 of 6646 branches covered (27.07%)

Branch coverage included in aggregate %.

115 of 202 new or added lines in 33 files covered. (56.93%)

22 existing lines in 6 files now uncovered.

4197 of 10845 relevant lines covered (38.7%)

186399.11 hits per line

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

0.0
/Cpp2IL.Core/Utils/X64CallingConventionResolver.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Diagnostics;
4
using Cpp2IL.Core.ISIL;
5
using Cpp2IL.Core.Model.Contexts;
6
using LibCpp2IL.BinaryStructures;
7
using LibCpp2IL.PE;
8

9
namespace Cpp2IL.Core.Utils;
10

11
#pragma warning disable IDE0305, IDE0300
12

13
public static class X64CallingConventionResolver
14
{
15
    // TODO: GCC(Linux) ABI
16

17
    // This class must be used in good faith.
18
    // If that's not possible, uncomment the binary type checks.
19
    // This *will* break everything on x32.
20

21
    const int ptrSize = 8;
22

23
    private static bool IsXMM(ParameterAnalysisContext par)
24
    {
NEW
25
        var parameterType = par.ParameterType;
×
26
        return parameterType == parameterType.AppContext.SystemTypes.SystemSingleType
×
27
            || parameterType == parameterType.AppContext.SystemTypes.SystemDoubleType;
×
28
    }
29

30
    public static InstructionSetIndependentOperand[] ResolveForUnmanaged(ApplicationAnalysisContext app, ulong target)
31
    {
32
        // This is mostly a stub and may be extended in the future. You can traverse exports here for example.
33
        // For now, we'll return all normal registers and omit the floating point registers.
34

35
        return app.Binary is PE ? new[] {
×
36
            ToOperand(MicrosoftNormalRegister.rcx),
×
37
            ToOperand(MicrosoftNormalRegister.rdx),
×
38
            ToOperand(MicrosoftNormalRegister.r8),
×
39
            ToOperand(MicrosoftNormalRegister.r9)
×
40
        } : new[] {
×
41
            ToOperand(LinuxNormalRegister.rdi),
×
42
            ToOperand(LinuxNormalRegister.rsi),
×
43
            ToOperand(LinuxNormalRegister.rdx),
×
44
            ToOperand(LinuxNormalRegister.rcx),
×
45
            ToOperand(LinuxNormalRegister.r8),
×
46
            ToOperand(LinuxNormalRegister.r9)
×
47
        };
×
48
    }
49

50
    public static InstructionSetIndependentOperand[] ResolveForManaged(MethodAnalysisContext ctx)
51
    {
52
        // if (ctx.AppContext.Binary.is32Bit)
53
        //    throw new NotSupportedException("Resolution of 64-bit calling conventions in 32-bit binaries is not supported.");
54

55
        List<InstructionSetIndependentOperand> args = new();
×
56

57
        var addThis = !ctx.IsStatic;
×
58
        var isReturningAnOversizedStructure = false; // TODO: Determine whether we return a structure and whether that structure is oversized.
×
59

60
        /*
61
        GCC:
62
        Small structures - packed into N registers:
63
        {
64
            int x;
65
            int y;
66
        }
67
        will be packed as a single normal register
68

69
        Big structures - packed into N registers:
70
        {
71
            int x;
72
            int y;
73
            int z;
74
            int w;
75
        }
76
        will be packed as two normal registers
77

78
        Large structures - always in the stack:
79
        {
80
            int x;
81
            int y;
82
            int z;
83
            int w;
84
            int kk;
85
            int mm;
86
        }
87
        will be packed into the stack
88

89
        Small XMM structures - packed into NX registers:
90
        {
91
            int x;
92
            double y;
93
        }
94
        x will be packed into normal register, y will be packed into fp register, they do overlap (xmm0 and rdi are used)
95

96
        Fit XMM structures - packed into X registers:
97
        {
98
            double x;
99
            double y;
100
        }
101
        will be packed as 2 xmm registers
102

103
        Big XMM structures - always in the stack:
104
        {
105
            double x;
106
            double y;
107
            int z;
108
        }
109
        will be packed into the stack, even though technically you could pack it into 1 normal and 2 xmm registers (same goes for if the int is a double)
110

111
        Small float structures - packed into N registers:
112
        {
113
            float x;
114
            int y;
115
        }
116
        x and y will be packed into a single normal register
117

118
        Fit float structures - packed into XN registers:
119
        {
120
            float x;
121
            float y;
122
            int z;
123
        }
124
        x and y will be packed into a single fp register(doesn't match int behavior!!!), z will be packed into a normal register
125

126
        Complete float structures - packed into X registers:
127
        {
128
            float x;
129
            float y;
130
            float z;
131
            float w;
132
        }
133
        x,y and z,w will be packed into 2 fp registers
134

135
        Everything else is always in the stack.
136
        Multiple structures in args also follow this rule.
137
        16-byte structures will be put into the stack after the 8th structure. The others stay in registers according to spec.
138
        32-byte structures will be put into the stack after the 4th structure. The others stay in registers according to spec.
139
        Structure sizes above are determined by their register size(16-byte fits into one R*X, 32-byte fits into two R*X, no matter the actual size).
140
        The structures don't get cross-packed in the registers, which means they can't overlap, even if possible on a bit level.
141
        Check .IsRef and pray it's true (don't need to handle struct fields individually, it's a pointer)
142
        */
143

144
        /*
145
        MSVC doesn't need any special code to be implemented.
146
        */
147

148
        if (ctx.AppContext.Binary is PE)
×
149
        {
150
            /*
151
            MSVC cconv:
152
                RCX = XMM0
153
                RDX = XMM1
154
                R8 = XMM2
155
                R9 = XMM3
156
                [stack, every field is 8 incl. f & d, uses mov]
157
            */
158

159
            var i = 0;
×
160

161
            if (isReturningAnOversizedStructure)
×
162
            {
163
                args.Add(ToOperand(MicrosoftNormalRegister.rcx + i));
×
164
                i++;
×
165
            }
166

167
            if (addThis)
×
168
            {
169
                args.Add(ToOperand(MicrosoftNormalRegister.rcx + i));
×
170
                i++;
×
171
            }
172

173
            void AddParameter(ParameterAnalysisContext? par)
174
            {
175
                if (i < 4)
×
176
                {
177
                    args.Add((par != null && IsXMM(par)) ? ToOperand(LinuxFloatingRegister.xmm0 + i) : ToOperand(MicrosoftNormalRegister.rcx + i));
×
178
                }
179
                else
180
                {
181
                    args.Add(InstructionSetIndependentOperand.MakeStack((i - 4) * ptrSize));
×
182
                }
183
            }
×
184

185
            for (; i < ctx.ParameterCount; i++)
×
186
            {
187
                AddParameter(ctx.Parameters[i]);
×
188
            }
189

190
            AddParameter(null); // The MethodInfo argument
×
191
        }
192
        else // if (ctx.AppContext.Binary is ElfFile)
193
        {
194
            /*
195
                                GCC cconv (-O2):
196
                                        Integers & Longs:
197
                                                rdi
198
                                                rsi
199
                                                rdx
200
                                                rcx
201
                                                r8
202
                                                r9
203
                                                [stack, uses push]
204
                                        Doubles:
205
                                                xmm0
206
                                                xmm1
207
                                                xmm2
208
                                                xmm3
209
                                                xmm4
210
                                                xmm5
211
                                                xmm6
212
                                                xmm7
213
                                                [stack, uses push]
214
                        */
215

216
            LinuxNormalRegister nreg = 0;
×
217
            LinuxFloatingRegister freg = 0;
×
218
            var stack = 0;
×
219

220
            void AddParameter(ParameterAnalysisContext? par)
221
            {
222
                if (par != null && IsXMM(par))
×
223
                {
224
                    if (freg == LinuxFloatingRegister.Stack)
×
225
                    {
226
                        args.Add(InstructionSetIndependentOperand.MakeStack(stack));
×
227
                        stack += ptrSize;
×
228
                    }
229
                    else args.Add(ToOperand(freg++));
×
230
                }
231
                else
232
                {
233
                    if (nreg == LinuxNormalRegister.Stack)
×
234
                    {
235
                        args.Add(InstructionSetIndependentOperand.MakeStack(stack));
×
236
                        stack += ptrSize;
×
237
                    }
238
                    else args.Add(ToOperand(nreg++));
×
239
                }
240
            }
×
241

242
            if (isReturningAnOversizedStructure)
×
243
            {
244
                args.Add(ToOperand(nreg++));
×
245
            }
246

247
            if (addThis)
×
248
            {
249
                args.Add(ToOperand(nreg++));
×
250
            }
251

252
            foreach (var par in ctx.Parameters)
×
253
            {
254
                AddParameter(par);
×
255
            }
256

257
            AddParameter(null); // The MethodInfo argument
×
258
        }
259
        // else throw new NotSupportedException($"Resolution of 64-bit calling conventions is not supported for this binary type.");
260

261
        return args.ToArray();
×
262
    }
263

264
    private static InstructionSetIndependentOperand ToOperand(MicrosoftNormalRegister Reg) => Reg switch
×
265
    {
×
266
        MicrosoftNormalRegister.rcx => InstructionSetIndependentOperand.MakeRegister("rcx"),
×
267
        MicrosoftNormalRegister.rdx => InstructionSetIndependentOperand.MakeRegister("rdx"),
×
268
        MicrosoftNormalRegister.r8 => InstructionSetIndependentOperand.MakeRegister("r8"),
×
269
        MicrosoftNormalRegister.r9 => InstructionSetIndependentOperand.MakeRegister("r9"),
×
270
        _ => throw new InvalidOperationException("Went past the register limit during resolution.")
×
271
    };
×
272

273
    private static InstructionSetIndependentOperand ToOperand(LinuxNormalRegister Reg) => Reg switch
×
274
    {
×
275
        LinuxNormalRegister.rdi => InstructionSetIndependentOperand.MakeRegister("rdi"),
×
276
        LinuxNormalRegister.rsi => InstructionSetIndependentOperand.MakeRegister("rsi"),
×
277
        LinuxNormalRegister.rdx => InstructionSetIndependentOperand.MakeRegister("rdx"),
×
278
        LinuxNormalRegister.rcx => InstructionSetIndependentOperand.MakeRegister("rcx"),
×
279
        LinuxNormalRegister.r8 => InstructionSetIndependentOperand.MakeRegister("r8"),
×
280
        LinuxNormalRegister.r9 => InstructionSetIndependentOperand.MakeRegister("r9"),
×
281
        _ => throw new InvalidOperationException("Went past the register limit during resolution.")
×
282
    };
×
283

284
    private static InstructionSetIndependentOperand ToOperand(LinuxFloatingRegister Reg) => Reg switch
×
285
    {
×
286
        LinuxFloatingRegister.xmm0 => InstructionSetIndependentOperand.MakeRegister("xmm0"),
×
287
        LinuxFloatingRegister.xmm1 => InstructionSetIndependentOperand.MakeRegister("xmm1"),
×
288
        LinuxFloatingRegister.xmm2 => InstructionSetIndependentOperand.MakeRegister("xmm2"),
×
289
        LinuxFloatingRegister.xmm3 => InstructionSetIndependentOperand.MakeRegister("xmm3"),
×
290
        LinuxFloatingRegister.xmm4 => InstructionSetIndependentOperand.MakeRegister("xmm4"),
×
291
        LinuxFloatingRegister.xmm5 => InstructionSetIndependentOperand.MakeRegister("xmm5"),
×
292
        LinuxFloatingRegister.xmm6 => InstructionSetIndependentOperand.MakeRegister("xmm6"),
×
293
        LinuxFloatingRegister.xmm7 => InstructionSetIndependentOperand.MakeRegister("xmm7"),
×
294
        _ => throw new InvalidOperationException("Went past the register limit during resolution.")
×
295
    };
×
296

297
    private enum MicrosoftNormalRegister
298
    {
299
        rcx, rdx, r8, r9
300
    }
301

302
    private enum LinuxNormalRegister
303
    {
304
        rdi, rsi, rdx, rcx, r8, r9, Stack
305
    }
306

307
    private enum LinuxFloatingRegister
308
    {
309
        xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, Stack
310
    }
311
}
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