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

SamboyCoding / Cpp2IL / 12284684366

11 Dec 2024 08:50PM UTC coverage: 28.154% (-0.02%) from 28.172%
12284684366

Pull #390

github

web-flow
Merge a07e6a76a into edbb9949b
Pull Request #390: Set AssemblyDefinition::PublicKey when generating AsmResolver assemblies

1266 of 6242 branches covered (20.28%)

Branch coverage included in aggregate %.

1 of 12 new or added lines in 4 files covered. (8.33%)

1 existing line in 1 file now uncovered.

3385 of 10278 relevant lines covered (32.93%)

124847.75 hits per line

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

0.0
/Cpp2IL.Core/OutputFormats/AsmResolverDummyDllOutputFormat.cs
1
// #define VERBOSE_LOGGING
2

3
using System;
4
using System.Collections.Generic;
5
using System.IO;
6
using System.Linq;
7
using System.Threading.Tasks;
8
using AsmResolver.DotNet;
9
using AsmResolver.DotNet.Builder;
10
using AsmResolver.PE.Builder;
11
using AsmResolver.PE.DotNet.Metadata.Tables;
12
using Cpp2IL.Core.Api;
13
using Cpp2IL.Core.Logging;
14
using Cpp2IL.Core.Model.Contexts;
15
using Cpp2IL.Core.Utils;
16
using Cpp2IL.Core.Utils.AsmResolver;
17
using LibCpp2IL.Metadata;
18

19
namespace Cpp2IL.Core.OutputFormats;
20

21
public abstract class AsmResolverDllOutputFormat : Cpp2IlOutputFormat
22
{
23
    private AssemblyDefinition? MostRecentCorLib { get; set; }
×
24

25
    public sealed override void DoOutput(ApplicationAnalysisContext context, string outputRoot)
26
    {
27
        var ret = BuildAssemblies(context);
×
28

29
        var start = DateTime.Now;
×
30
        Logger.Verbose("Generating PE images...", "DllOutput");
×
31

32
        if (!Directory.Exists(outputRoot))
×
33
            Directory.CreateDirectory(outputRoot);
×
34

35
        //Convert assembly definitions to PE files
36
        var peImagesToWrite = ret
×
37
            .AsParallel()
×
38
            .Select(a => (image: a.ManifestModule!.ToPEImage(new ManagedPEImageBuilder()), name: a.ManifestModule.Name!))
×
39
            .ToList();
×
40

41
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
×
42

43
        start = DateTime.Now;
×
44
        Logger.Verbose("Building and writing managed PE files to disk...", "DllOutput");
×
45

46
        //Save them
47
        var fileBuilder = new ManagedPEFileBuilder();
×
48
        foreach (var (image, name) in peImagesToWrite)
×
49
        {
50
            var dllPath = Path.Combine(outputRoot, name);
×
51
            fileBuilder.CreateFile(image).Write(dllPath);
×
52
        }
53

54
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
×
55
    }
×
56

57
    public List<AssemblyDefinition> BuildAssemblies(ApplicationAnalysisContext context)
58
    {
59
#if VERBOSE_LOGGING
60
        var asmCount = context.Assemblies.Count;
61
        var typeCount = context.AllTypes.Count();
62
        var methodCount = context.AllTypes.SelectMany(t => t.Methods).Count();
63
        var fieldCount = context.AllTypes.SelectMany(t => t.Fields).Count();
64
        var propertyCount = context.AllTypes.SelectMany(t => t.Properties).Count();
65
        var eventCount = context.AllTypes.SelectMany(t => t.Events).Count();
66
#endif
67

68
        //Build the stub assemblies
69
        var start = DateTime.Now;
×
70
#if VERBOSE_LOGGING
71
        Logger.Verbose($"Building stub assemblies ({asmCount} assemblies, {typeCount} types)...", "DllOutput");
72
#else
73
        Logger.Verbose($"Building stub assemblies...", "DllOutput");
×
74
#endif
75
        List<AssemblyDefinition> ret = BuildStubAssemblies(context);
×
76
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
×
77

78
        start = DateTime.Now;
×
79
        Logger.Verbose("Configuring inheritance and generics...", "DllOutput");
×
80

81
        Parallel.ForEach(context.Assemblies, AsmResolverAssemblyPopulator.ConfigureHierarchy);
×
82

83
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
×
84

85
        //Populate them
86
        start = DateTime.Now;
×
87

88
#if VERBOSE_LOGGING
89
        Logger.Verbose($"Adding {fieldCount} fields, {methodCount} methods, {propertyCount} properties, and {eventCount} events (in parallel)...", "DllOutput");
90
#else
91
        Logger.Verbose($"Adding fields, methods, properties, and events (in parallel)...", "DllOutput");
×
92
#endif
93

94
        MiscUtils.ExecuteParallel(context.Assemblies, AsmResolverAssemblyPopulator.CopyDataFromIl2CppToManaged);
×
95
        MiscUtils.ExecuteParallel(context.Assemblies, AsmResolverAssemblyPopulator.InferExplicitInterfaceImplementations);
×
96
        MiscUtils.ExecuteParallel(context.Assemblies, FillMethodBodies);
×
97

98
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
×
99

100
        //Populate custom attributes
101
        start = DateTime.Now;
×
102
        Logger.Verbose("Adding custom attributes to all of the above...", "DllOutput");
×
103
        MiscUtils.ExecuteParallel(context.Assemblies, AsmResolverAssemblyPopulator.PopulateCustomAttributes);
×
104

105
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
×
106

107
        TypeDefinitionsAsmResolver.Reset();
×
108

109
        return ret;
×
110
    }
111

112
    protected abstract void FillMethodBody(MethodDefinition methodDefinition, MethodAnalysisContext methodContext);
113

114
    protected virtual void FillMethodBodies(AssemblyAnalysisContext context)
115
    {
116
        foreach (var typeContext in context.Types)
×
117
        {
118
            if (AsmResolverAssemblyPopulator.IsTypeContextModule(typeContext))
×
119
                continue;
120

121
#if !DEBUG
122
            try
123
#endif
124
            {
125
                foreach (var methodCtx in typeContext.Methods)
×
126
                {
127
                    var managedMethod = methodCtx.GetExtraData<MethodDefinition>("AsmResolverMethod") ?? throw new($"AsmResolver method not found in method analysis context for {typeContext.Definition?.FullName}.{methodCtx.Definition?.Name}");
×
128

129
                    FillMethodBody(managedMethod, methodCtx);
×
130
                }
131
            }
×
132
#if !DEBUG
133
            catch (System.Exception e)
×
134
            {
135
                var managedType = typeContext.GetExtraData<TypeDefinition>("AsmResolverType") ?? throw new($"AsmResolver type not found in type analysis context for {typeContext.Definition?.FullName}");
×
136
                throw new($"Failed to process type {managedType.FullName} (module {managedType.Module?.Name}, declaring type {managedType.DeclaringType?.FullName}) in {context.Definition.AssemblyName.Name}", e);
×
137
            }
138
#endif
139
        }
140
    }
×
141

142
    private List<AssemblyDefinition> BuildStubAssemblies(ApplicationAnalysisContext context)
143
    {
144
        var assemblyResolver = new Il2CppAssemblyResolver();
×
145
        var metadataResolver = new DefaultMetadataResolver(assemblyResolver);
×
146

147
        var corlib = context.Assemblies.First(a => a.Definition.AssemblyName.Name == "mscorlib");
×
148
        MostRecentCorLib = BuildStubAssembly(corlib, null, metadataResolver);
×
149
        assemblyResolver.DummyAssemblies.Add(MostRecentCorLib.Name!, MostRecentCorLib);
×
150

151
        var ret = context.Assemblies
×
152
            // .AsParallel()
×
153
            .Where(a => a.Definition.AssemblyName.Name != "mscorlib")
×
154
            .Select(a => BuildStubAssembly(a, MostRecentCorLib, metadataResolver))
×
155
            .ToList();
×
156

157
        ret.ForEach(a => assemblyResolver.DummyAssemblies.Add(a.Name!, a));
×
158

159
        ret.Add(MostRecentCorLib);
×
160
        return ret;
×
161
    }
162

163
    private static AssemblyDefinition BuildStubAssembly(AssemblyAnalysisContext assemblyContext, AssemblyDefinition? corLib, IMetadataResolver metadataResolver)
164
    {
165
        var assemblyDefinition = assemblyContext.Definition;
×
166

167
        var imageDefinition = assemblyDefinition.Image;
×
168

169
        //Get the name of the assembly (= the name of the DLL without the file extension)
170
        var assemblyNameString = assemblyDefinition.AssemblyName.Name;
×
171

172
        //Build an AsmResolver assembly from this definition
173
        Version version;
174
        if (assemblyDefinition.AssemblyName.build >= 0)
×
175
            version = new(assemblyDefinition.AssemblyName.major, assemblyDefinition.AssemblyName.minor, assemblyDefinition.AssemblyName.build, assemblyDefinition.AssemblyName.revision);
×
176
        else
177
            //handle __Generated assembly on v29, which has a version of 0.0.-1.-1
178
            version = new(0, 0, 0, 0);
×
179

180
        var ourAssembly = new AssemblyDefinition(assemblyNameString, version)
×
181
        {
×
NEW
182
            HashAlgorithm = (AssemblyHashAlgorithm)assemblyDefinition.AssemblyName.hash_alg,
×
NEW
183
            Attributes = (AssemblyAttributes)assemblyDefinition.AssemblyName.flags,
×
NEW
184
            Culture = assemblyDefinition.AssemblyName.Culture,
×
NEW
185
            PublicKey = assemblyDefinition.AssemblyName.PublicKey,
×
UNCOV
186
        };
×
187

188
        //Setting the corlib module allows element types in references to that assembly to be set correctly without us having to manually set them.
189
        var moduleName = imageDefinition.Name;
×
190
        if (moduleName == "__Generated")
×
191
            moduleName += ".dll"; //__Generated doesn't have a .dll extension in the metadata but it is still of course a DLL
×
192

193
        var managedModule = new ModuleDefinition(moduleName, new(corLib ?? ourAssembly)) //Use either ourself as corlib, if we are corlib, otherwise the provided one
×
194
        {
×
195
            MetadataResolver = metadataResolver
×
196
        };
×
197
        ourAssembly.Modules.Add(managedModule);
×
198

199
        foreach (var il2CppTypeDefinition in assemblyContext.TopLevelTypes)
×
200
        {
201
            if (il2CppTypeDefinition.Name != "<Module>")
×
202
                //We skip module because I've never come across an il2cpp assembly with any top-level functions, and it's simpler to skip it as AsmResolver adds one by default.
203
                managedModule.TopLevelTypes.Add(BuildStubType(il2CppTypeDefinition));
×
204
        }
205

206
        if (corLib == null)
×
207
        {
208
            //We *are* the corlib, so cache defs now
209
            TypeDefinitionsAsmResolver.CacheNeededTypeDefinitions();
×
210
        }
211

212
        //We can get issues with consumers of the API if the base type is not set correctly for value types or enums, so we set it here (as early as possible) if we can
213
        foreach (var assemblyContextType in assemblyContext.Types)
×
214
        {
215
            if (assemblyContextType.Definition is not { } def || assemblyContextType.GetExtraData<TypeDefinition>("AsmResolverType") is not { } asmResolverType)
×
216
                continue;
217

218
            if (def.IsValueType)
×
219
                asmResolverType.BaseType = managedModule.DefaultImporter.ImportType(TypeDefinitionsAsmResolver.ValueType);
×
220
            else if (def.IsEnumType)
×
221
                asmResolverType.BaseType = managedModule.DefaultImporter.ImportType(TypeDefinitionsAsmResolver.Enum);
×
222
        }
223

224
        //Store the managed assembly in the context so we can use it later.
225
        assemblyContext.PutExtraData("AsmResolverAssembly", ourAssembly);
×
226

227
        return ourAssembly;
×
228
    }
229

230
    private static TypeDefinition BuildStubType(TypeAnalysisContext typeContext)
231
    {
232
        var typeDef = typeContext.Definition;
×
233

234
        //Initialize an empty type definition
235
        var ret = new TypeDefinition(typeContext.Namespace, typeContext.Name, (TypeAttributes)typeContext.TypeAttributes);
×
236

237
        //Set up its layout
238
        if (typeDef != null && typeDef.BaseType?.ToString() != "System.Enum")
×
239
            ConfigureTypeSize(typeDef, ret);
×
240

241
        //Create nested types
242
        foreach (var cppNestedType in typeContext.NestedTypes)
×
243
            ret.NestedTypes.Add(BuildStubType(cppNestedType));
×
244

245
        //Associate this asm resolve td with the type context
246
        typeContext.PutExtraData("AsmResolverType", ret);
×
247

248
        //Add to the lookup-by-id table used by the resolver
249
        if (typeDef != null)
×
250
            AsmResolverUtils.TypeDefsByIndex[typeDef.TypeIndex] = ret;
×
251

252
        return ret;
×
253
    }
254

255
    private static void ConfigureTypeSize(Il2CppTypeDefinition il2CppDefinition, TypeDefinition asmResolverDefinition)
256
    {
257
        ushort packingSize = 0;
×
258
        var classSize = 0U;
×
259
        if (!il2CppDefinition.PackingSizeIsDefault)
×
260
            packingSize = (ushort)il2CppDefinition.PackingSize;
×
261

262
        if (!il2CppDefinition.ClassSizeIsDefault && !il2CppDefinition.IsEnumType)
×
263
        {
264
            if (il2CppDefinition.Size > 1 << 30)
×
265
                throw new Exception($"Got invalid size for type {il2CppDefinition}: {il2CppDefinition.RawSizes}");
×
266

267
            if (il2CppDefinition.Size != -1)
×
268
                classSize = (uint)il2CppDefinition.Size;
×
269
            else
270
                classSize = 0; //Not sure what this value actually implies but it seems to work
×
271
        }
272

273
        if (packingSize != 0 || classSize != 0)
×
274
            asmResolverDefinition.ClassLayout = new(packingSize, classSize);
×
275
    }
×
276
}
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