-
-
Notifications
You must be signed in to change notification settings - Fork 208
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a plugin for outputting Windows PDB files (#382)
- Loading branch information
Showing
11 changed files
with
267 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net9.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\Cpp2IL.Core\Cpp2IL.Core.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="AssetRipper.Bindings.MsPdbCore" Version="1.0.1" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
using System.Reflection.PortableExecutable; | ||
using AssetRipper.Bindings.MsPdbCore; | ||
using Cpp2IL.Core.Api; | ||
using Cpp2IL.Core.Model.Contexts; | ||
|
||
namespace Cpp2IL.Plugin.Pdb; | ||
|
||
internal unsafe class PdbOutputFormat : Cpp2IlOutputFormat | ||
{ | ||
public override string OutputFormatId => "pdb_windows"; | ||
|
||
public override string OutputFormatName => "Windows PDB generation"; | ||
|
||
public override void DoOutput(ApplicationAnalysisContext context, string outputRoot) | ||
{ | ||
if (!Directory.Exists(outputRoot)) | ||
Directory.CreateDirectory(outputRoot); | ||
|
||
using var peReader = new PEReader(new MemoryStream(context.Binary.GetRawBinaryContent())); | ||
|
||
var pdbFilePath = Path.Combine(outputRoot, "GameAssembly.pdb"); | ||
MsPdbCore.PDBOpen2W(pdbFilePath, "w", out var err, out var openError, out var pdb); | ||
|
||
MsPdbCore.PDBOpenDBI(pdb, "w", "", out var dbi); | ||
|
||
MsPdbCore.DBIOpenModW(dbi, "__Globals", "__Globals", out var mod); | ||
|
||
ushort secNum = 1; | ||
ushort i2cs = 1; | ||
foreach (var sectionHeader in peReader.PEHeaders.SectionHeaders) | ||
{ | ||
if (sectionHeader.Name == "il2cpp") | ||
i2cs = secNum; | ||
MsPdbCore.DBIAddSec(dbi, secNum++, 0 /* TODO? */, sectionHeader.VirtualAddress, sectionHeader.VirtualSize); | ||
} | ||
|
||
Dictionary<string, ulong> keyFunctions = []; | ||
|
||
foreach ((var name, var address) in context.GetOrCreateKeyFunctionAddresses().Pairs) | ||
{ | ||
keyFunctions[name] = address; | ||
} | ||
|
||
foreach ((var name, var address) in context.Binary.GetExportedFunctions()) | ||
{ | ||
keyFunctions[name] = address; | ||
} | ||
|
||
foreach ((var name, var address) in keyFunctions) | ||
{ | ||
if (address == 0) | ||
continue; | ||
|
||
GetSectionInformation(peReader, (long)context.Binary.GetRva(address), out var targetSection, out var offset); | ||
MsPdbCore.ModAddPublic2(mod, name, targetSection, offset, CV_PUBSYMFLAGS_e.Function); | ||
} | ||
|
||
foreach ((var virtualAddress, var list) in context.MethodsByAddress) | ||
{ | ||
if (virtualAddress <= 0) | ||
continue; | ||
|
||
GetSectionInformation(peReader, (long)context.Binary.GetRva(virtualAddress), out var targetSection, out var offset); | ||
|
||
foreach (var method in list) | ||
{ | ||
if (method is NativeMethodAnalysisContext nativeMethod) | ||
{ | ||
continue; // Skip native methods | ||
} | ||
MsPdbCore.ModAddPublic2(mod, method.FullName, targetSection, offset, CV_PUBSYMFLAGS_e.Function); | ||
} | ||
} | ||
|
||
MsPdbCore.ModClose(mod); | ||
MsPdbCore.DBIClose(dbi); | ||
|
||
MsPdbCore.PDBCommit(pdb); | ||
|
||
MsPdbCore.PDBQuerySignature2(pdb, out var wrongGuid); | ||
|
||
MsPdbCore.PDBClose(pdb); | ||
|
||
// Hack: manually replace guid and age in generated .pdb, because there's no API on mspdbcore to set them manually | ||
var targetDebugInfo = peReader.ReadCodeViewDebugDirectoryData(peReader.ReadDebugDirectory() | ||
.Single(it => it.Type == DebugDirectoryEntryType.CodeView)); | ||
|
||
var wrongGuidBytes = wrongGuid.ToByteArray(); | ||
var allPdbBytes = File.ReadAllBytes(pdbFilePath); | ||
|
||
var patchTarget = IndexOfBytes(allPdbBytes, wrongGuidBytes); | ||
targetDebugInfo.Guid.TryWriteBytes(allPdbBytes.AsSpan(patchTarget)); | ||
|
||
Console.WriteLine(targetDebugInfo.Guid); | ||
Console.WriteLine(targetDebugInfo.Age); | ||
|
||
BitConverter.TryWriteBytes(allPdbBytes.AsSpan(patchTarget - 4), targetDebugInfo.Age); | ||
File.WriteAllBytes(pdbFilePath, allPdbBytes); | ||
} | ||
|
||
private static void GetSectionInformation(PEReader peReader, long virtualAddress, out ushort targetSection, out int offset) | ||
{ | ||
targetSection = 0; | ||
long tsva = 0; | ||
ushort sc = 1; | ||
foreach (var sectionHeader in peReader.PEHeaders.SectionHeaders) | ||
{ | ||
if (virtualAddress > sectionHeader.VirtualAddress) | ||
{ | ||
targetSection = sc; | ||
tsva = sectionHeader.VirtualAddress; | ||
} | ||
else | ||
break; | ||
|
||
sc++; | ||
} | ||
|
||
if (targetSection == 0) | ||
throw new ApplicationException("Bad segment"); | ||
|
||
offset = (int)(virtualAddress - tsva); | ||
} | ||
|
||
private static int IndexOfBytes(ReadOnlySpan<byte> haystack, ReadOnlySpan<byte> needle) | ||
{ | ||
for (var i = 0; i <= haystack.Length - needle.Length; i++) | ||
{ | ||
if (haystack.Slice(i, needle.Length).SequenceEqual(needle)) | ||
{ | ||
return i; | ||
} | ||
} | ||
|
||
return -1; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
using Cpp2IL.Core.Api; | ||
using Cpp2IL.Core.Attributes; | ||
using Cpp2IL.Plugin.Pdb; | ||
|
||
[assembly: RegisterCpp2IlPlugin(typeof(PdbOutputPlugin))] | ||
|
||
namespace Cpp2IL.Plugin.Pdb; | ||
|
||
public class PdbOutputPlugin : Cpp2IlPlugin | ||
{ | ||
public override string Name => "PDB Output Plugin"; | ||
|
||
public override string Description => "Adds an output format which generates debug symbols"; | ||
|
||
public override void OnLoad() | ||
{ | ||
OutputFormatRegistry.Register<PdbOutputFormat>(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.