mirror of https://github.com/icsharpcode/ILSpy.git
12 changed files with 430 additions and 9 deletions
@ -0,0 +1,284 @@ |
|||||||
|
// Copyright (c) 2024 Siegfried Pammer
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Collections.Immutable; |
||||||
|
using System.Diagnostics; |
||||||
|
using System.Diagnostics.CodeAnalysis; |
||||||
|
using System.IO; |
||||||
|
using System.IO.MemoryMappedFiles; |
||||||
|
using System.Reflection.Metadata; |
||||||
|
using System.Text; |
||||||
|
|
||||||
|
using ICSharpCode.Decompiler.TypeSystem; |
||||||
|
|
||||||
|
#nullable enable |
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.Metadata |
||||||
|
{ |
||||||
|
public class WebCilFile : MetadataFile, IDisposable, IModuleReference |
||||||
|
{ |
||||||
|
readonly MemoryMappedViewAccessor view; |
||||||
|
readonly long webcilOffset; |
||||||
|
|
||||||
|
private WebCilFile(string fileName, long webcilOffset, long metadataOffset, MemoryMappedViewAccessor view, ImmutableArray<SectionHeader> sectionHeaders, ImmutableArray<WasmSection> wasmSections, MetadataReaderProvider provider, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default) |
||||||
|
: base(MetadataFileKind.WebCIL, fileName, provider, metadataOptions, 0) |
||||||
|
{ |
||||||
|
this.webcilOffset = webcilOffset; |
||||||
|
this.MetadataOffset = (int)metadataOffset; |
||||||
|
this.view = view; |
||||||
|
this.SectionHeaders = sectionHeaders; |
||||||
|
this.WasmSections = wasmSections; |
||||||
|
} |
||||||
|
|
||||||
|
public static WebCilFile? FromStream(string fileName, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default) |
||||||
|
{ |
||||||
|
using var memoryMappedFile = MemoryMappedFile.CreateFromFile(fileName, FileMode.Open, null, 0, MemoryMappedFileAccess.Read); |
||||||
|
var view = memoryMappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read); |
||||||
|
try |
||||||
|
{ |
||||||
|
// read magic "\0asm"
|
||||||
|
if (view.ReadUInt32(0) != WASM_MAGIC) |
||||||
|
return null; |
||||||
|
|
||||||
|
// read version
|
||||||
|
if (view.ReadUInt32(4) != 1) |
||||||
|
return null; |
||||||
|
|
||||||
|
using var stream = view.AsStream(); |
||||||
|
using var reader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true); |
||||||
|
|
||||||
|
stream.Position += 8; |
||||||
|
|
||||||
|
long metadataOffset = -1; |
||||||
|
List<WasmSection> sections = new List<WasmSection>(); |
||||||
|
|
||||||
|
while (stream.Position < stream.Length) |
||||||
|
{ |
||||||
|
WasmSectionId id = (WasmSectionId)reader.ReadByte(); |
||||||
|
uint size = reader.ReadULEB128(); |
||||||
|
sections.Add(new WasmSection(id, stream.Position, size, view)); |
||||||
|
|
||||||
|
if (id == WasmSectionId.Custom && size == 0) |
||||||
|
{ |
||||||
|
break; |
||||||
|
} |
||||||
|
stream.Seek(size, SeekOrigin.Current); |
||||||
|
} |
||||||
|
|
||||||
|
foreach (var section in sections) |
||||||
|
{ |
||||||
|
if (section.Id != WasmSectionId.Data || metadataOffset > -1) |
||||||
|
continue; |
||||||
|
|
||||||
|
stream.Seek(section.Offset, SeekOrigin.Begin); |
||||||
|
|
||||||
|
uint numSegments = reader.ReadULEB128(); |
||||||
|
if (numSegments != 2) |
||||||
|
continue; |
||||||
|
|
||||||
|
// skip the first segment
|
||||||
|
if (reader.ReadByte() != 1) |
||||||
|
continue; |
||||||
|
|
||||||
|
long segmentLength = reader.ReadULEB128(); |
||||||
|
long segmentStart = reader.BaseStream.Position; |
||||||
|
|
||||||
|
reader.BaseStream.Seek(segmentLength, SeekOrigin.Current); |
||||||
|
|
||||||
|
if (reader.ReadByte() != 1) |
||||||
|
continue; |
||||||
|
|
||||||
|
segmentLength = reader.ReadULEB128(); |
||||||
|
if (TryReadWebCilSegment(reader, out var header, out metadataOffset, out var webcilOffset, out var sectionHeaders)) |
||||||
|
{ |
||||||
|
stream.Seek(metadataOffset, SeekOrigin.Begin); |
||||||
|
var metadata = MetadataReaderProvider.FromMetadataStream(stream, MetadataStreamOptions.LeaveOpen | MetadataStreamOptions.PrefetchMetadata); |
||||||
|
|
||||||
|
var result = new WebCilFile(fileName, webcilOffset, metadataOffset, view, ImmutableArray.Create(sectionHeaders), sections.ToImmutableArray(), metadata, metadataOptions); |
||||||
|
|
||||||
|
view = null; // don't dispose the view, we're still using it in the sections
|
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
finally |
||||||
|
{ |
||||||
|
view?.Dispose(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static unsafe bool TryReadWebCilSegment(BinaryReader reader, out WebcilHeader webcilHeader, out long metadataOffset, out long webcilOffset, [NotNullWhen(true)] out SectionHeader[]? sectionHeaders) |
||||||
|
{ |
||||||
|
webcilHeader = default; |
||||||
|
metadataOffset = -1; |
||||||
|
sectionHeaders = null; |
||||||
|
|
||||||
|
webcilOffset = reader.BaseStream.Position; |
||||||
|
|
||||||
|
if (reader.ReadUInt32() != WEBCIL_MAGIC) |
||||||
|
return false; |
||||||
|
|
||||||
|
webcilHeader.VersionMajor = reader.ReadUInt16(); |
||||||
|
webcilHeader.VersionMinor = reader.ReadUInt16(); |
||||||
|
webcilHeader.CoffSections = reader.ReadUInt16(); |
||||||
|
_ = reader.ReadUInt16(); // reserved0
|
||||||
|
webcilHeader.PECliHeaderRVA = reader.ReadUInt32(); |
||||||
|
webcilHeader.PECliHeaderSize = reader.ReadUInt32(); |
||||||
|
webcilHeader.PEDebugRVA = reader.ReadUInt32(); |
||||||
|
webcilHeader.PEDebugSize = reader.ReadUInt32(); |
||||||
|
|
||||||
|
sectionHeaders = new SectionHeader[webcilHeader.CoffSections]; |
||||||
|
for (int i = 0; i < webcilHeader.CoffSections; i++) |
||||||
|
{ |
||||||
|
sectionHeaders[i].VirtualSize = reader.ReadUInt32(); |
||||||
|
sectionHeaders[i].VirtualAddress = reader.ReadUInt32(); |
||||||
|
sectionHeaders[i].RawDataSize = reader.ReadUInt32(); |
||||||
|
sectionHeaders[i].RawDataPtr = reader.ReadUInt32(); |
||||||
|
} |
||||||
|
|
||||||
|
long corHeaderStart = TranslateRVA(sectionHeaders, webcilOffset, webcilHeader.PECliHeaderRVA); |
||||||
|
if (reader.BaseStream.Seek(corHeaderStart, SeekOrigin.Begin) != corHeaderStart) |
||||||
|
return false; |
||||||
|
int byteCount = reader.ReadInt32(); |
||||||
|
int majorVersion = reader.ReadUInt16(); |
||||||
|
int minorVersion = reader.ReadUInt16(); |
||||||
|
metadataOffset = TranslateRVA(sectionHeaders, webcilOffset, (uint)reader.ReadInt32()); |
||||||
|
return reader.BaseStream.Seek(metadataOffset, SeekOrigin.Begin) == metadataOffset; |
||||||
|
} |
||||||
|
|
||||||
|
public override int MetadataOffset { get; } |
||||||
|
|
||||||
|
private static int GetContainingSectionIndex(IEnumerable<SectionHeader> sections, int rva) |
||||||
|
{ |
||||||
|
int i = 0; |
||||||
|
foreach (var section in sections) |
||||||
|
{ |
||||||
|
if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.VirtualSize) |
||||||
|
{ |
||||||
|
return i; |
||||||
|
} |
||||||
|
i++; |
||||||
|
} |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
private static long TranslateRVA(IEnumerable<SectionHeader> sections, long webcilOffset, uint rva) |
||||||
|
{ |
||||||
|
foreach (var section in sections) |
||||||
|
{ |
||||||
|
if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.VirtualSize) |
||||||
|
{ |
||||||
|
return section.RawDataPtr + (rva - section.VirtualAddress) + webcilOffset; |
||||||
|
} |
||||||
|
} |
||||||
|
throw new BadImageFormatException("RVA not found in any section"); |
||||||
|
} |
||||||
|
|
||||||
|
public override MethodBodyBlock GetMethodBody(int rva) |
||||||
|
{ |
||||||
|
var reader = GetSectionData(rva).GetReader(); |
||||||
|
return MethodBodyBlock.Create(reader); |
||||||
|
} |
||||||
|
|
||||||
|
public override int GetContainingSectionIndex(int rva) |
||||||
|
{ |
||||||
|
return GetContainingSectionIndex(SectionHeaders, rva); |
||||||
|
} |
||||||
|
|
||||||
|
public override unsafe SectionData GetSectionData(int rva) |
||||||
|
{ |
||||||
|
foreach (var section in SectionHeaders) |
||||||
|
{ |
||||||
|
if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.VirtualSize) |
||||||
|
{ |
||||||
|
byte* ptr = (byte*)0; |
||||||
|
view.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr); |
||||||
|
return new SectionData(ptr + section.RawDataPtr + webcilOffset + (rva - section.VirtualAddress), (int)section.RawDataSize); |
||||||
|
} |
||||||
|
} |
||||||
|
throw new BadImageFormatException("RVA not found in any section"); |
||||||
|
} |
||||||
|
|
||||||
|
public override ImmutableArray<SectionHeader> SectionHeaders { get; } |
||||||
|
|
||||||
|
public ImmutableArray<WasmSection> WasmSections { get; } |
||||||
|
|
||||||
|
IModule? IModuleReference.Resolve(ITypeResolveContext context) |
||||||
|
{ |
||||||
|
return new MetadataModule(context.Compilation, this, TypeSystemOptions.Default); |
||||||
|
} |
||||||
|
|
||||||
|
public void Dispose() |
||||||
|
{ |
||||||
|
view.Dispose(); |
||||||
|
} |
||||||
|
|
||||||
|
public struct WebcilHeader |
||||||
|
{ |
||||||
|
public ushort VersionMajor; |
||||||
|
public ushort VersionMinor; |
||||||
|
public ushort CoffSections; |
||||||
|
public uint PECliHeaderRVA; |
||||||
|
public uint PECliHeaderSize; |
||||||
|
public uint PEDebugRVA; |
||||||
|
public uint PEDebugSize; |
||||||
|
} |
||||||
|
|
||||||
|
const uint WASM_MAGIC = 0x6d736100u; // "\0asm"
|
||||||
|
const uint WEBCIL_MAGIC = 0x4c496257u; // "WbIL"
|
||||||
|
|
||||||
|
[DebuggerDisplay("WasmSection {Id}: {Offset} {Size}")] |
||||||
|
public class WasmSection |
||||||
|
{ |
||||||
|
public WasmSectionId Id; |
||||||
|
public long Offset; |
||||||
|
public uint Size; |
||||||
|
private MemoryMappedViewAccessor view; |
||||||
|
|
||||||
|
public WasmSection(WasmSectionId id, long offset, uint size, MemoryMappedViewAccessor view) |
||||||
|
{ |
||||||
|
this.Id = id; |
||||||
|
this.Size = size; |
||||||
|
this.Offset = offset; |
||||||
|
this.view = view; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public enum WasmSectionId : byte |
||||||
|
{ |
||||||
|
// order matters: enum values must match the WebAssembly spec
|
||||||
|
Custom = 0, |
||||||
|
Type = 1, |
||||||
|
Import = 2, |
||||||
|
Function = 3, |
||||||
|
Table = 4, |
||||||
|
Memory = 5, |
||||||
|
Global = 6, |
||||||
|
Export = 7, |
||||||
|
Start = 8, |
||||||
|
Element = 9, |
||||||
|
Code = 10, |
||||||
|
Data = 11, |
||||||
|
DataCount = 12, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
<DrawingGroup xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ClipGeometry="M0,0 V612 H612 V0 H0 Z"> |
||||||
|
<DrawingGroup.Transform> |
||||||
|
<TranslateTransform X="0" Y="2.6645352591003757E-15" /> |
||||||
|
</DrawingGroup.Transform> |
||||||
|
<GeometryDrawing Brush="#FF654FF0" Geometry="F1 M612,612z M0,0z M376,0C376,1.08 376,2.16 376,3.3 376,42.06 344.58,73.47 305.83,73.47 267.07,73.47 235.66,42.05 235.66,3.3L235.66,3.3C235.66,2.16,235.66,1.08,235.66,-2.66453525910038E-15L0,0 0,612 612,612 612,0z" /> |
||||||
|
<GeometryDrawing Brush="#FFFFFFFF" Geometry="F1 M612,612z M0,0z M142.16,329.81L182.72,329.81 210.41,477.28 210.91,477.28 244.19,329.81 282.13,329.81 312.19,479.09 312.78,479.09 344.34,329.81 384.12,329.81 332.43,546.5 292.18,546.5 262.37,399.03 261.59,399.03 229.68,546.5 188.68,546.5z M429.85,329.81L493.79,329.81 557.29,546.5 515.45,546.5 501.64,498.28 428.8,498.28 418.14,546.5 377.39,546.5z M454.19,383.22L436.5,462.72 491.56,462.72 471.25,383.22z" /> |
||||||
|
</DrawingGroup> |
Loading…
Reference in new issue