Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/coreclr/tools/Common/Wasm/Webcil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public struct WebcilHeader
public uint PeDebugRva;
public uint PeDebugSize;
// 28 bytes
public uint TableBase;
// 32 bytes
}

/// <summary>
Expand Down
21 changes: 18 additions & 3 deletions src/mono/mono/metadata/webcil-loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@

/* keep in sync with webcil-writer */
enum {
MONO_WEBCIL_VERSION_MAJOR = 0,
MONO_WEBCIL_VERSION_MAJOR_0 = 0,
MONO_WEBCIL_VERSION_MAJOR_1 = 1,
MONO_WEBCIL_VERSION_MINOR = 0,
};

Expand All @@ -36,6 +37,13 @@ typedef struct MonoWebcilHeader {
// 28 bytes
} MonoWebcilHeader;

// Version 1 adds a TableBase field after the V0 header
typedef struct MonoWebcilHeader_1 {
MonoWebcilHeader base;
uint32_t table_base;
// 32 bytes
} MonoWebcilHeader_1;

static gboolean
find_webcil_in_wasm (const uint8_t *ptr, const uint8_t *boundp, const uint8_t **webcil_payload_start);

Expand Down Expand Up @@ -84,7 +92,8 @@ do_load_header (const char *raw_data, uint32_t raw_data_len, int32_t offset, Mon
memcpy (&wcheader, raw_data + offset, sizeof (wcheader));

if (!(wcheader.id [0] == 'W' && wcheader.id [1] == 'b' && wcheader.id[2] == 'I' && wcheader.id[3] == 'L' &&
GUINT16_FROM_LE (wcheader.version_major) == MONO_WEBCIL_VERSION_MAJOR && GUINT16_FROM_LE (wcheader.version_minor) == MONO_WEBCIL_VERSION_MINOR))
(GUINT16_FROM_LE (wcheader.version_major) == MONO_WEBCIL_VERSION_MAJOR_0 || GUINT16_FROM_LE (wcheader.version_major) == MONO_WEBCIL_VERSION_MAJOR_1) &&
GUINT16_FROM_LE (wcheader.version_minor) == MONO_WEBCIL_VERSION_MINOR))
return -1;

memset (header, 0, sizeof(MonoDotNetHeader));
Expand All @@ -94,7 +103,13 @@ do_load_header (const char *raw_data, uint32_t raw_data_len, int32_t offset, Mon
header->datadir.pe_debug.rva = GUINT32_FROM_LE (wcheader.pe_debug_rva);
header->datadir.pe_debug.size = GUINT32_FROM_LE (wcheader.pe_debug_size);

offset += sizeof (wcheader);
// V1 header is larger (32 bytes vs 28)
if (GUINT16_FROM_LE (wcheader.version_major) >= MONO_WEBCIL_VERSION_MAJOR_1) {
if (offset + sizeof (MonoWebcilHeader_1) > raw_data_len)
return -1;
offset += sizeof (MonoWebcilHeader_1);
} else
offset += sizeof (wcheader);
return offset;
}

Expand Down
63 changes: 55 additions & 8 deletions src/tasks/Microsoft.NET.WebAssembly.Webcil/WebcilConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,28 @@ FilePosition SectionStart
);

private const int SectionAlignment = 16;
private const int V0HeaderSize = 28;
private const int V1HeaderSize = 32;

private readonly string _inputPath;
private readonly string _outputPath;
private readonly int _webcilVersion;

private string InputPath => _inputPath;

public bool WrapInWebAssembly { get; set; } = true;

private WebcilConverter(string inputPath, string outputPath)
private WebcilConverter(string inputPath, string outputPath, int webcilVersion)
{
if (webcilVersion != 0 && webcilVersion != 1)
throw new ArgumentOutOfRangeException(nameof(webcilVersion), webcilVersion, "Only webcil version 0 and 1 are supported");
_inputPath = inputPath;
_outputPath = outputPath;
_webcilVersion = webcilVersion;
}

public static WebcilConverter FromPortableExecutable(string inputPath, string outputPath)
=> new WebcilConverter(inputPath, outputPath);
public static WebcilConverter FromPortableExecutable(string inputPath, string outputPath, int webcilVersion = 0)
Comment thread
radekdoulik marked this conversation as resolved.
Comment thread
radekdoulik marked this conversation as resolved.
=> new WebcilConverter(inputPath, outputPath, webcilVersion);

public void ConvertToWebcil()
{
Expand Down Expand Up @@ -104,9 +110,9 @@ public record struct FilePosition(int Position)
public static FilePosition operator +(FilePosition left, int right) => new(left.Position + right);
}

private static unsafe int SizeOfHeader()
private int SizeOfHeader()
{
return sizeof(WebcilHeader);
return _webcilVersion >= 1 ? V1HeaderSize : V0HeaderSize;
}

public unsafe void GatherInfo(PEReader peReader, out WCFileInfo wcInfo, out PEFileInfo peInfo)
Expand All @@ -117,14 +123,18 @@ public unsafe void GatherInfo(PEReader peReader, out WCFileInfo wcInfo, out PEFi
var sections = headers.SectionHeaders;
WebcilHeader header = default;
header.Id = WebcilConstants.WEBCIL_MAGIC;
header.VersionMajor = WebcilConstants.WC_VERSION_MAJOR;
header.VersionMajor = (ushort)_webcilVersion;
header.VersionMinor = WebcilConstants.WC_VERSION_MINOR;
header.CoffSections = (ushort)coffHeader.NumberOfSections;
header.Reserved0 = 0;
header.PeCliHeaderRva = (uint)peHeader.CorHeaderTableDirectory.RelativeVirtualAddress;
header.PeCliHeaderSize = (uint)peHeader.CorHeaderTableDirectory.Size;
header.PeDebugRva = (uint)peHeader.DebugTableDirectory.RelativeVirtualAddress;
header.PeDebugSize = (uint)peHeader.DebugTableDirectory.Size;
if (_webcilVersion >= 1)
{
header.TableBase = uint.MaxValue;
}

// current logical position in the output file
FilePosition pos = SizeOfHeader();
Expand Down Expand Up @@ -174,7 +184,7 @@ public unsafe void GatherInfo(PEReader peReader, out WCFileInfo wcInfo, out PEFi
SectionStart: firstWCSection);
}

private static void WriteHeader(Stream s, WebcilHeader webcilHeader)
private void WriteHeader(Stream s, WebcilHeader webcilHeader)
{
if (!BitConverter.IsLittleEndian)
{
Expand All @@ -186,8 +196,12 @@ private static void WriteHeader(Stream s, WebcilHeader webcilHeader)
webcilHeader.PeCliHeaderSize = BinaryPrimitives.ReverseEndianness(webcilHeader.PeCliHeaderSize);
webcilHeader.PeDebugRva = BinaryPrimitives.ReverseEndianness(webcilHeader.PeDebugRva);
webcilHeader.PeDebugSize = BinaryPrimitives.ReverseEndianness(webcilHeader.PeDebugSize);
if (_webcilVersion >= 1)
{
webcilHeader.TableBase = BinaryPrimitives.ReverseEndianness(webcilHeader.TableBase);
}
}
WriteStructure(s, webcilHeader);
WriteStructure(s, webcilHeader, SizeOfHeader());
}

private static void WriteSectionHeaders(Stream s, ImmutableArray<WebcilSectionHeader> sectionsHeaders)
Expand Down Expand Up @@ -223,6 +237,18 @@ private static void WriteStructure<T>(Stream s, T structure)
s.Write(new ReadOnlySpan<byte>(p, sizeof(T)));
}
}

private static void WriteStructure<T>(Stream s, T structure, int size)
where T : unmanaged
{
unsafe
{
if (size > sizeof(T))
throw new ArgumentOutOfRangeException(nameof(size), size, $"size exceeds struct size {sizeof(T)}");
byte* p = (byte*)&structure;
s.Write(new ReadOnlySpan<byte>(p, size));
}
}
#else
private static void WriteStructure<T>(Stream s, T structure)
where T : unmanaged
Expand All @@ -242,6 +268,27 @@ private static void WriteStructure<T>(Stream s, T structure)
}
s.Write(buffer, 0, size);
}

private static void WriteStructure<T>(Stream s, T structure, int size)
where T : unmanaged
{
int fullSize = Marshal.SizeOf<T>();
if (size > fullSize)
throw new ArgumentOutOfRangeException(nameof(size), size, $"size exceeds struct size {fullSize}");
byte[] buffer = new byte[fullSize];
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(fullSize);
Marshal.StructureToPtr(structure, ptr, false);
Marshal.Copy(ptr, buffer, 0, fullSize);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
s.Write(buffer, 0, size);
}
#endif

private static void CopySections(Stream outStream, FileStream inputStream, ImmutableArray<SectionHeader> peSections, ImmutableArray<WebcilSectionHeader> wcSections)
Expand Down
37 changes: 32 additions & 5 deletions src/tasks/Microsoft.NET.WebAssembly.Webcil/WebcilReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,23 @@ public WebcilReader (Stream stream, string inputPath) : this(stream)
InputPath = inputPath;
}

// V0 header is 28 bytes, V1 adds a 4-byte TableBase field (32 bytes total)
private const int V0HeaderSize = 28;
private const int V1HeaderSize = 32;

private unsafe bool ReadHeader()
{
WebcilHeader header;
var buffer = new byte[Marshal.SizeOf<WebcilHeader>()];
// Read the V0 portion of the header first (28 bytes)
WebcilHeader header = default;
var buffer = new byte[V0HeaderSize];
if (_stream.Read(buffer, 0, buffer.Length) != buffer.Length)
{
return false;
}
fixed (byte* p = buffer)
{
header = *(WebcilHeader*)p;
// Copy V0 fields only (28 bytes) into the 32-byte struct
Buffer.MemoryCopy(p, &header, Marshal.SizeOf<WebcilHeader>(), V0HeaderSize);
}
if (!BitConverter.IsLittleEndian)
{
Expand All @@ -79,11 +85,29 @@ private unsafe bool ReadHeader()
header.PeDebugSize = BinaryPrimitives.ReverseEndianness(header.PeDebugSize);
}
if (header.Id != WebcilConstants.WEBCIL_MAGIC
|| header.VersionMajor != WebcilConstants.WC_VERSION_MAJOR
|| (header.VersionMajor != 0 && header.VersionMajor != 1)
|| header.VersionMinor != WebcilConstants.WC_VERSION_MINOR)
{
return false;
}
if (header.VersionMajor >= 1)
{
// V1 has an additional TableBase field
var extra = new byte[V1HeaderSize - V0HeaderSize];
if (_stream.Read(extra, 0, extra.Length) != extra.Length)
{
return false;
}
header.TableBase = BitConverter.ToUInt32(extra, 0);
if (!BitConverter.IsLittleEndian)
{
header.TableBase = BinaryPrimitives.ReverseEndianness(header.TableBase);
}
}
else
{
header.TableBase = uint.MaxValue;
}
_header = header;
return true;
}
Expand Down Expand Up @@ -353,7 +377,10 @@ private long TranslateRVA(uint rva)
throw new BadImageFormatException("RVA not found in any section", nameof(_stream));
}

private static long SectionDirectoryOffset => Marshal.SizeOf<WebcilHeader>();
// V0 header is 28 bytes (no TableBase), V1 is 32 bytes
private long SectionDirectoryOffset => _header.VersionMajor >= 1
? V1HeaderSize
: V0HeaderSize;

private unsafe ImmutableArray<WebcilSectionHeader> ReadSections()
{
Expand Down
4 changes: 2 additions & 2 deletions src/tasks/WasmAppBuilder/WebcilConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ private WebcilConverter(NET.WebAssembly.Webcil.WebcilConverter converter, string
Log = logger;
}

public static WebcilConverter FromPortableExecutable(string inputPath, string outputPath, LogAdapter logger)
public static WebcilConverter FromPortableExecutable(string inputPath, string outputPath, LogAdapter logger, int webcilVersion = 0)
{
Comment thread
radekdoulik marked this conversation as resolved.
var converter = NET.WebAssembly.Webcil.WebcilConverter.FromPortableExecutable(inputPath, outputPath);
var converter = NET.WebAssembly.Webcil.WebcilConverter.FromPortableExecutable(inputPath, outputPath, webcilVersion);
return new WebcilConverter(converter, inputPath, outputPath, logger);
}

Expand Down
Loading