From bb981448876132fad047af543dc95aad4ca2b5c5 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 27 Mar 2026 13:37:35 -0700 Subject: [PATCH 01/20] Implement relocation resolution for WASM_MEMORY_ADDR, WASM_TABLE_INDEX, WASM_FUNCTION_INDEX type relocs --- .../Compiler/DependencyAnalysis/Relocation.cs | 6 +++ .../Compiler/ObjectWriter/WasmObjectWriter.cs | 53 ++++++++++++++----- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs index 861c9bbd7bbe55..1375f67ef98f6a 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs @@ -656,6 +656,12 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v case RelocType.WASM_MEMORY_ADDR_SLEB: DwarfHelper.WritePaddedSLEB128(new Span((byte*)location, WASM_PADDED_RELOC_SIZE_32), value); return; + case RelocType.WASM_TABLE_INDEX_U32: + *(uint*)location = checked((uint)value); + return; + case RelocType.WASM_TABLE_INDEX_U64: + *(ulong*)location = checked((ulong)value); + return; default: Debug.Fail("Invalid RelocType: " + relocType); diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index 36c1c8462cf11e..0334b7b4eaa0a5 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -714,18 +714,18 @@ private unsafe void ResolveRelocations(int sectionIndex, MemoryStream sectionStr { byte[] relocScratchBuffer = new byte[Relocation.MaxSize]; - WebcilSection? webcilSection = null; + WebcilSection? curSectionAsWebcil = null; uint webcilVirtualStart = 0; if (_sections[sectionIndex] is WebcilSection curSection) { - webcilSection = curSection; + curSectionAsWebcil = curSection; webcilVirtualStart = curSection.Header.VirtualAddress; } // If we have a webcil section, we expect it to have a nonzero section start. This is because for webcil, // we should have written the webcil header and each of the section headers (always non-zero size) before any // section contents - Debug.Assert(webcilSection is null || sectionStart != 0); + Debug.Assert(curSectionAsWebcil is null || sectionStart != 0); foreach (SymbolicRelocation reloc in relocs) { @@ -739,23 +739,22 @@ private unsafe void ResolveRelocations(int sectionIndex, MemoryStream sectionStr // The virtual address of the relocation we are resolving uint virtualRelocOffset = 0; + if (curSectionAsWebcil is not null) + { + virtualRelocOffset = webcilVirtualStart + (uint)reloc.Offset; + Debug.Assert(IsWithinSection(virtualRelocOffset, curSectionAsWebcil)); + } // The virtual address of the symbol this relocation refers to uint virtualSymbolImageOffset = 0; - WebcilSection? symbolWebcilSection = null; // TODO-Wasm: Enforce the below boolean as an assert once we are emitting proper Wasm code // relocs for all code containing nodes // ---> bool betweenWebcilSections = false; - if (webcilSection is not null && _sections[definedSymbol.SectionIndex] is WebcilSection targetSection) + if (_sections[definedSymbol.SectionIndex] is WebcilSection targetSection) { - // ---> betweenWebcilSections = true; symbolWebcilSection = targetSection; - - virtualRelocOffset = webcilVirtualStart + (uint)reloc.Offset; - Debug.Assert(IsWithinSection(virtualRelocOffset, webcilSection)); - virtualSymbolImageOffset = symbolWebcilSection.Header.VirtualAddress + (uint)definedSymbol.Value; Debug.Assert(IsWithinSection(virtualSymbolImageOffset, symbolWebcilSection)); } @@ -813,12 +812,42 @@ private unsafe void ResolveRelocations(int sectionIndex, MemoryStream sectionStr break; case RelocType.WASM_MEMORY_ADDR_SLEB: { - // WASM-TODO actually implement this + // These relocs should be for cases of the form: + // global.get __image_base + // i32.const + // i32.add + // so reloc represents an offset relative to image base. + Relocation.WriteValue(reloc.Type, pData, virtualSymbolImageOffset); break; } case RelocType.WASM_TABLE_INDEX_U32: + case RelocType.WASM_TABLE_INDEX_U64: + case RelocType.WASM_TABLE_INDEX_SLEB: + { + if (_uniqueSymbols.TryGetValue(reloc.SymbolName.ToString(), out int index)) + { + // Here, we are effectively writing a table offset relative to the table_base. + // These will need to be fixed up by the runtime after load by adding __image_function_pointer_base + Relocation.WriteValue(reloc.Type, pData, index); + } + else + { + throw new InvalidDataException($"Table index for signature symbol definition '{reloc.SymbolName}' not found"); + } + break; + } + case RelocType.WASM_FUNCTION_INDEX_LEB: { - // WASM-TODO actually implement this + if (_uniqueSymbols.TryGetValue(reloc.SymbolName.ToString(), out int index)) + { + // These are module-local function pointer indices, so we can simply write out the assigned function index + // for this particular symbol + Relocation.WriteValue(reloc.Type, pData, index); + } + else + { + throw new InvalidDataException($"Table index for signature symbol definition '{reloc.SymbolName}' not found"); + } break; } default: From 473c46b53da7d24c71207c9a45bd3e9666a31630 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 27 Mar 2026 13:38:56 -0700 Subject: [PATCH 02/20] WIP relocations for runtime function pointer fixup --- .../Compiler/ObjectWriter/WasmNative.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs index c9ea6fcf30d534..f321081b4984e2 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs @@ -8,6 +8,8 @@ using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysis.Wasm; using ILCompiler.ObjectWriter.WasmInstructions; +using System.Buffers.Binary; +using System.IO; namespace ILCompiler.ObjectWriter { @@ -195,4 +197,43 @@ public int Encode(Span buffer) public int EncodeRelocations(Span buffer) => 0; public int EncodeSize() => 2 + _initExpr.EncodeSize(); } + +#if READYTORUN + public enum WasmNativeRelocKind : byte + { + ADD_TABLE_BASE_OFFSET_SLEB = 1, + ADD_TABLE_BASE_OFFSET_U32 = 2, + ADD_TABLE_BASE_OFFSET_U64 = 3, + } + public class WasmNativeReloc + { + WasmNativeRelocKind Kind; + ulong Offset; + + public WasmNativeReloc(WasmNativeRelocKind kind, ulong offset) + { + Kind = kind; + Offset = offset; + } + + // These relocs are encoded as [kind, reloc offset]. For R2R, these should only be used + // for adding an offset to a function pointer in the function table, so the target of the relocation is implicitly the function table base. + public int Encode(Span buffer, int targetPointerSize = 4) + { + buffer[0] = (byte)Kind; + switch (targetPointerSize) + { + case 4: + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(1), checked((uint)Offset)); + break; + case 8: + BinaryPrimitives.WriteUInt64LittleEndian(buffer.Slice(1), Offset); + break; + default: + throw new NotSupportedException(); + } + return 0; + } + } +#endif } From cc8a1a8b6009afbcf231004d2e1ab8f15c289dfb Mon Sep 17 00:00:00 2001 From: adamperlin Date: Mon, 30 Mar 2026 15:41:38 -0700 Subject: [PATCH 03/20] Webcil native relocations --- .../Compiler/ObjectWriter/WasmNative.cs | 23 ++++-- .../Compiler/ObjectWriter/WasmObjectWriter.cs | 76 ++++++++++++++++--- .../Compiler/ObjectWriter/WebcilEncoder.cs | 18 +++-- src/coreclr/tools/Common/Wasm/Webcil.cs | 10 ++- 4 files changed, 104 insertions(+), 23 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs index f321081b4984e2..9311ab4c8ab392 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs @@ -201,9 +201,8 @@ public int Encode(Span buffer) #if READYTORUN public enum WasmNativeRelocKind : byte { - ADD_TABLE_BASE_OFFSET_SLEB = 1, - ADD_TABLE_BASE_OFFSET_U32 = 2, - ADD_TABLE_BASE_OFFSET_U64 = 3, + ADD_TABLE_BASE_I32 = 0, + ADD_TABLE_BASE_I64 = 1, } public class WasmNativeReloc { @@ -216,8 +215,9 @@ public WasmNativeReloc(WasmNativeRelocKind kind, ulong offset) Offset = offset; } - // These relocs are encoded as [kind, reloc offset]. For R2R, these should only be used - // for adding an offset to a function pointer in the function table, so the target of the relocation is implicitly the function table base. + // These relocs are encoded as (kind:byte, reloc_offset:(u32|u64)). For R2R, these runtime relocs should only be used + // for adding an offset to a function pointer in the function table, so the target fixup for + // the relocation is implicitly to add the function table base. public int Encode(Span buffer, int targetPointerSize = 4) { buffer[0] = (byte)Kind; @@ -234,6 +234,19 @@ public int Encode(Span buffer, int targetPointerSize = 4) } return 0; } + + public int EncodeSize() + { + switch (Kind) + { + case WasmNativeRelocKind.ADD_TABLE_BASE_I32: + return 1 + 4; + case WasmNativeRelocKind.ADD_TABLE_BASE_I64: + return 1 + 8; + default: + throw new NotSupportedException(); + } + } } #endif } diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index 0334b7b4eaa0a5..25bebc12076864 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using System.Numerics; +using System.Runtime.CompilerServices; using System.Text; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysis.Wasm; @@ -370,13 +371,17 @@ private void InsertWasmStub(Utf8String name, WasmFunctionBody body) public const int WebcilSectionAlignment = 16; private WebcilSegment BuildWebcilDataSegment() { - WebcilSection[] webcilSections = _sections.OfType().ToArray(); + List webcilSections = _sections.OfType().ToList(); - uint sizeOfHeaders = (uint)WebcilEncoder.HeaderEncodeSize() + (uint)(webcilSections.Length * WebcilEncoder.SectionHeaderEncodeSize()); + // The reloc section is not materialized as a section in parent object writer, so we add it here. + WebcilSection relocSection = new WebcilSection(WebcilSectionType.Reloc, new Utf8String("reloc"), default(WebcilSectionHeader), new MemoryStream(), _sections.Count + 1); + webcilSections.Add(relocSection); + + uint sizeOfHeaders = (uint)WebcilEncoder.HeaderEncodeSize() + (uint)(webcilSections.Count * WebcilEncoder.SectionHeaderEncodeSize()); uint pointerToRawData = (uint)AlignmentHelper.AlignUp((int)sizeOfHeaders, (int)WebcilSectionAlignment); uint virtualAddress = pointerToRawData; - for (int i = 0; i < webcilSections.Length; i++) + for (int i = 0; i < webcilSections.Count(); i++) { WebcilSection webcilSection = webcilSections[i]; Debug.Assert(BitOperations.IsPow2(webcilSection.MinAlignment) && BitOperations.IsPow2(WebcilSectionAlignment) && @@ -390,6 +395,7 @@ private WebcilSegment BuildWebcilDataSegment() // the pointer to raw data for each section is also the same as the virtual address. uint virtualSize = alignedSectionSize; WebcilSectionHeader sectionHeader = new WebcilSectionHeader( + sectionType: webcilSection.Kind, virtualSize: virtualSize, virtualAddress: virtualAddress, sizeOfRawData: alignedSectionSize, @@ -406,14 +412,13 @@ private WebcilSegment BuildWebcilDataSegment() Id = WebcilConstants.WEBCIL_MAGIC, VersionMajor = WebcilConstants.WC_VERSION_MAJOR, VersionMinor = WebcilConstants.WC_VERSION_MINOR, - CoffSections = (ushort)webcilSections.Length, + CoffSections = (ushort)webcilSections.Count, PeCliHeaderRva = 0, // This RVA will be resolved later PeCliHeaderSize = 0, // Resolved along with RVA PeDebugRva = 0, // This RVA will be resolved later PeDebugSize = 0 // Resolved along with RVA }; - return new WebcilSegment(header, webcilSections.ToArray()); } #endif @@ -452,7 +457,7 @@ private protected override void CreateSection(ObjectNodeSection section, Utf8Str { #if READYTORUN // This is a section which is internally wrapping a Webcil section - wasmSection = new WebcilSection(new Utf8String(section.Name), default(WebcilSectionHeader), sectionStream, sectionIndex); + wasmSection = new WebcilSection(WebcilSectionType.Data, new Utf8String(section.Name), default(WebcilSectionHeader), sectionStream, sectionIndex); #else wasmSection = new WasmSection(WasmSectionType.Data, sectionStream, new Utf8String(section.Name)); #endif @@ -581,6 +586,41 @@ private int[] SectionEmitOrder } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private WasmNativeReloc ToNativeReloc(SymbolicRelocation reloc, ulong runtimeOffset) + { + switch (reloc.Type) + { + case RelocType.WASM_TABLE_INDEX_U32: + return new WasmNativeReloc(WasmNativeRelocKind.ADD_TABLE_BASE_I32, runtimeOffset); + case RelocType.WASM_TABLE_INDEX_U64: + return new WasmNativeReloc(WasmNativeRelocKind.ADD_TABLE_BASE_I64, runtimeOffset); + default: + throw new NotImplementedException(); + } + + } + + private static ObjectNodeSection WebcilRelocSection = new ObjectNodeSection("reloc", SectionType.ReadOnly); + private void EmitRelocSectionData() + { + var relocSectionWriter = GetOrCreateSection(WebcilRelocSection); + for (int i = 0; i < _webcilSegment.Sections.Count(); i++) + { + WebcilSection webcilSection = _webcilSegment.Sections[i]; + if (_runtimeRelocations.TryGetValue(webcilSection.Index, out List runtimeRelocations)) + { + foreach (var reloc in runtimeRelocations) + { + ulong resolvedOffset = (ulong)webcilSection.Header.PointerToRawData + (ulong)reloc.Offset; + WasmNativeReloc wasmReloc = ToNativeReloc(reloc, resolvedOffset); + Span buffer = relocSectionWriter.Buffer.GetSpan(wasmReloc.EncodeSize()); + wasmReloc.Encode(buffer); + } + } + } + } + private PaddingHelper _paddingHelper = new PaddingHelper(WebcilSectionAlignment); private protected override void EmitObjectFile(Stream outputFileStream) @@ -686,7 +726,13 @@ Emit Webcil segment at end of file to support ReadyToRun #endif } + private bool NeedsRuntimeReloc(RelocType reloc) + { + return reloc is RelocType.WASM_TABLE_INDEX_U32 or RelocType.WASM_TABLE_INDEX_U64; + } + Dictionary> _resolvableRelocations = new(); + Dictionary> _runtimeRelocations = new(); private protected override void EmitRelocations(int sectionIndex, List relocationList) { @@ -696,9 +742,19 @@ private protected override void EmitRelocations(int sectionIndex, List(); } - // Unconditionally add the reloc to our resolvable list; all relocs must be resolvable for Wasm - // since we do not emit any relocations in the output object file. + // Unconditionally add the reloc to our resolvable list; we do some amount of relocation resolution + // for all relocation types resolvable.Add(reloc); + + // A few relocation types (table indices are the only case) need + // an additional runtime reloc as well. + if (NeedsRuntimeReloc(reloc.Type)) + { + if (!_runtimeRelocations.TryGetValue(sectionIndex, out List runtimeRelocs)) + { + _runtimeRelocations[sectionIndex] = runtimeRelocs = new List(); + } + } } } @@ -1276,12 +1332,14 @@ class WebcilSection : WasmSection public readonly Stream _stream; private PaddingHelper _paddingHelper; public int MinAlignment = 1; + public WebcilSectionType Kind; public uint Padding => Header.SizeOfRawData - (uint)_stream.Length; - public WebcilSection(Utf8String name, WebcilSectionHeader header, Stream stream, int index) + public WebcilSection(WebcilSectionType webcilSectionType, Utf8String name, WebcilSectionHeader header, Stream stream, int index) : base(WasmSectionType.Data, stream, name) { + Kind = webcilSectionType; Header = header; _stream = stream; Index = index; diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WebcilEncoder.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WebcilEncoder.cs index a2559ec2141c92..2451269cb9120c 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WebcilEncoder.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WebcilEncoder.cs @@ -49,10 +49,11 @@ public static int EmitHeader(in WebcilHeader header, Stream outputStream) public static int SectionHeaderEncodeSize() { - return sizeof(uint) + // VirtualSize: 4 - sizeof(uint) + // VirtualAddress: 8 - sizeof(uint) + // SizeOfRawData: 12 - sizeof(uint); // PointerToRawData: 16 + return sizeof(WebcilSectionType) + // SectionType (uint): 4 + sizeof(uint) + // VirtualSize: 8 + sizeof(uint) + // VirtualAddress: 12 + sizeof(uint) + // SizeOfRawData: 16 + sizeof(uint); // PointerToRawData: 20 } public static int EncodeSectionHeader(in WebcilSectionHeader sectionHeader, Stream outputStream) @@ -61,10 +62,11 @@ public static int EncodeSectionHeader(in WebcilSectionHeader sectionHeader, Stre Span header = stackalloc byte[encodeSize]; // The Webcil spec requires little-endian encoding - BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(0, 4), sectionHeader.VirtualSize); - BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(4, 4), sectionHeader.VirtualAddress); - BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(8, 4), sectionHeader.SizeOfRawData); - BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(12, 4), sectionHeader.PointerToRawData); + BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(0, 4), (uint)sectionHeader.SectionType); + BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(4, 8), sectionHeader.VirtualSize); + BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(8, 12), sectionHeader.VirtualAddress); + BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(12, 16), sectionHeader.SizeOfRawData); + BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(16, 20), sectionHeader.PointerToRawData); outputStream.Write(header); diff --git a/src/coreclr/tools/Common/Wasm/Webcil.cs b/src/coreclr/tools/Common/Wasm/Webcil.cs index 38e0bd43954031..3955b437241f52 100644 --- a/src/coreclr/tools/Common/Wasm/Webcil.cs +++ b/src/coreclr/tools/Common/Wasm/Webcil.cs @@ -42,6 +42,12 @@ public struct WebcilHeader // 28 bytes } +public enum WebcilSectionType : uint +{ + Data = 0x1, + Reloc = 0x2 +} + /// /// Represents a section header in a Webcil file. /// @@ -51,13 +57,15 @@ public struct WebcilHeader [StructLayout(LayoutKind.Sequential, Pack = 1)] public readonly struct WebcilSectionHeader { + public readonly WebcilSectionType SectionType; public readonly uint VirtualSize; public readonly uint VirtualAddress; public readonly uint SizeOfRawData; public readonly uint PointerToRawData; - public WebcilSectionHeader(uint virtualSize, uint virtualAddress, uint sizeOfRawData, uint pointerToRawData) + public WebcilSectionHeader(WebcilSectionType sectionType, uint virtualSize, uint virtualAddress, uint sizeOfRawData, uint pointerToRawData) { + SectionType = sectionType; VirtualSize = virtualSize; VirtualAddress = virtualAddress; SizeOfRawData = sizeOfRawData; From c33cd2f5c0101e69b918829c9fbf5cda31278c1f Mon Sep 17 00:00:00 2001 From: adamperlin Date: Tue, 31 Mar 2026 11:17:49 -0700 Subject: [PATCH 04/20] Re-use IMAGE_REL_BASED style relocs for Wasm, and collect into baseRelocsMap in WasmObjectWriter --- .../Compiler/DependencyAnalysis/Relocation.cs | 10 ++++ .../Compiler/ObjectWriter/WasmNative.cs | 52 ------------------- .../Compiler/ObjectWriter/WasmObjectWriter.cs | 28 ++++++++-- 3 files changed, 33 insertions(+), 57 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs index 1375f67ef98f6a..6af7f0de9e6a4e 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs @@ -18,6 +18,10 @@ public enum RelocType // COFF relocation types IMAGE_REL_BASED_ADDR32NB = 0x0B, // The 32-bit address without an image base (RVA) + // Webcil Relocation Types + IMAGE_REL_BASED_WASM32_TABLE = 0x0C, + IMAGE_REL_BASED_WASM64_TABLE = 0x0D, + // General relocation types IMAGE_REL_BASED_REL32 = 0x10, // 32-bit relative address from byte following reloc IMAGE_REL_BASED_THUMB_BRANCH24 = 0x13, // Thumb2: based B, BL @@ -801,6 +805,12 @@ public static RelocType GetFileRelocationType(RelocType relocationType) case RelocType.IMAGE_REL_BASED_THUMB_MOV32: return relocationType; + case RelocType.WASM_TABLE_INDEX_U32: + return RelocType.IMAGE_REL_BASED_WASM32_TABLE; + + case RelocType.WASM_TABLE_INDEX_U64: + return RelocType.IMAGE_REL_BASED_WASM64_TABLE; + default: return RelocType.IMAGE_REL_BASED_ABSOLUTE; } diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs index 9311ab4c8ab392..76d58096ce3937 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs @@ -197,56 +197,4 @@ public int Encode(Span buffer) public int EncodeRelocations(Span buffer) => 0; public int EncodeSize() => 2 + _initExpr.EncodeSize(); } - -#if READYTORUN - public enum WasmNativeRelocKind : byte - { - ADD_TABLE_BASE_I32 = 0, - ADD_TABLE_BASE_I64 = 1, - } - public class WasmNativeReloc - { - WasmNativeRelocKind Kind; - ulong Offset; - - public WasmNativeReloc(WasmNativeRelocKind kind, ulong offset) - { - Kind = kind; - Offset = offset; - } - - // These relocs are encoded as (kind:byte, reloc_offset:(u32|u64)). For R2R, these runtime relocs should only be used - // for adding an offset to a function pointer in the function table, so the target fixup for - // the relocation is implicitly to add the function table base. - public int Encode(Span buffer, int targetPointerSize = 4) - { - buffer[0] = (byte)Kind; - switch (targetPointerSize) - { - case 4: - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(1), checked((uint)Offset)); - break; - case 8: - BinaryPrimitives.WriteUInt64LittleEndian(buffer.Slice(1), Offset); - break; - default: - throw new NotSupportedException(); - } - return 0; - } - - public int EncodeSize() - { - switch (Kind) - { - case WasmNativeRelocKind.ADD_TABLE_BASE_I32: - return 1 + 4; - case WasmNativeRelocKind.ADD_TABLE_BASE_I64: - return 1 + 8; - default: - throw new NotSupportedException(); - } - } - } -#endif } diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index 25bebc12076864..1d4d29392d7bf2 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -733,6 +733,10 @@ private bool NeedsRuntimeReloc(RelocType reloc) Dictionary> _resolvableRelocations = new(); Dictionary> _runtimeRelocations = new(); + Dictionary> _baseRelocsMap = new(); + + // We group webcil relocs into 4kb blocks, similar to PE + const uint WebcilRelocPageSize = 0x1000; private protected override void EmitRelocations(int sectionIndex, List relocationList) { @@ -746,14 +750,28 @@ private protected override void EmitRelocations(int sectionIndex, List runtimeRelocs)) + WebcilSection webcilSection = _sections[sectionIndex] as WebcilSection; + Debug.Assert(webcilSection is not null); + // Gather file-level relocations that need to go into the webcil .reloc + // section. We collect entries grouped by 4KB page into a map of + // (page RVA -> list of (type<<12 | offsetInPage) WORD entries). + // Note that this handling is logically the same as the implementation in the PE Object Writer. + uint targetRva = webcilSection.Header.VirtualAddress + (uint)reloc.Offset; + uint pageRva = targetRva & ~(WebcilRelocPageSize - 1); + ushort offsetInPage = (ushort)(targetRva & (WebcilRelocPageSize - 1)); + ushort entry = (ushort)(((ushort)fileRelocType << 12) | offsetInPage); + + if (!_baseRelocsMap.TryGetValue(pageRva, out List list)) { - _runtimeRelocations[sectionIndex] = runtimeRelocs = new List(); + list = new List(); + _baseRelocsMap.Add(pageRva, list); } + list.Add(entry); } } } From 04c1a483b9b2414c9e1f6a596562918a82ac7d2b Mon Sep 17 00:00:00 2001 From: adamperlin Date: Tue, 31 Mar 2026 14:13:16 -0700 Subject: [PATCH 05/20] Fix bug in EncodeSectionHeader, small cleanup of unused code+field rename --- .../Compiler/ObjectWriter/WasmObjectWriter.cs | 39 +++++++++++++++---- .../Compiler/ObjectWriter/WebcilEncoder.cs | 8 ++-- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index 9389e17bcfb73b..3f366350f3487f 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -373,10 +373,6 @@ private WebcilSegment BuildWebcilDataSegment() { List webcilSections = _sections.OfType().ToList(); - // The reloc section is not materialized as a section in parent object writer, so we add it here. - WebcilSection relocSection = new WebcilSection(WebcilSectionType.Reloc, new Utf8String("reloc"), default(WebcilSectionHeader), new MemoryStream(), _sections.Count + 1); - webcilSections.Add(relocSection); - uint sizeOfHeaders = (uint)WebcilEncoder.HeaderEncodeSize() + (uint)(webcilSections.Count * WebcilEncoder.SectionHeaderEncodeSize()); uint pointerToRawData = (uint)AlignmentHelper.AlignUp((int)sizeOfHeaders, (int)WebcilSectionAlignment); uint virtualAddress = pointerToRawData; @@ -586,8 +582,35 @@ private int[] SectionEmitOrder } } - private static ObjectNodeSection WebcilRelocSection = new ObjectNodeSection("reloc", SectionType.ReadOnly); + private void EmitRelocSectionData() + { + var writer = GetOrCreateSection(WebcilRelocSection); + Debug.Assert(writer.SectionIndex == _sections.Count - 1, "The .reloc section must be the last section we emit."); + + foreach (var kv in _baseRelocMap) + { + uint pageRva = kv.Key; + List entries = kv.Value; + entries.Sort(); + + int entriesSize = entries.Count * 2; + int sizeOfBlock = 8 + entriesSize; + sizeOfBlock = AlignmentHelper.AlignUp(sizeOfBlock, 4); + + writer.WriteLittleEndian(pageRva); + writer.WriteLittleEndian((uint)sizeOfBlock); + + // Emit entries + foreach (ushort e in entries) + { + writer.WriteLittleEndian(e); + } + + // Ensure block is 4-byte aligned + writer.EmitAlignment(4); + } + } private PaddingHelper _paddingHelper = new PaddingHelper(WebcilSectionAlignment); @@ -696,7 +719,7 @@ Emit Webcil segment at end of file to support ReadyToRun Dictionary> _resolvableRelocations = new(); Dictionary> _runtimeRelocations = new(); - Dictionary> _baseRelocsMap = new(); + Dictionary> _baseRelocMap = new(); // We group webcil relocs into 4kb blocks, similar to PE const uint WebcilRelocPageSize = 0x1000; @@ -729,10 +752,10 @@ private protected override void EmitRelocations(int sectionIndex, List list)) + if (!_baseRelocMap.TryGetValue(pageRva, out List list)) { list = new List(); - _baseRelocsMap.Add(pageRva, list); + _baseRelocMap.Add(pageRva, list); } list.Add(entry); } diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WebcilEncoder.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WebcilEncoder.cs index 2451269cb9120c..b42225844398cf 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WebcilEncoder.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WebcilEncoder.cs @@ -63,10 +63,10 @@ public static int EncodeSectionHeader(in WebcilSectionHeader sectionHeader, Stre // The Webcil spec requires little-endian encoding BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(0, 4), (uint)sectionHeader.SectionType); - BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(4, 8), sectionHeader.VirtualSize); - BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(8, 12), sectionHeader.VirtualAddress); - BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(12, 16), sectionHeader.SizeOfRawData); - BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(16, 20), sectionHeader.PointerToRawData); + BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(4, 4), sectionHeader.VirtualSize); + BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(8, 4), sectionHeader.VirtualAddress); + BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(12, 4), sectionHeader.SizeOfRawData); + BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(16, 4), sectionHeader.PointerToRawData); outputStream.Write(header); From f1bebc40b5f8cb824bf80a318269f75c505ecfee Mon Sep 17 00:00:00 2001 From: adamperlin Date: Tue, 31 Mar 2026 15:29:15 -0700 Subject: [PATCH 06/20] Emit Webcil Reloc section. This means we need to construct the webcil segment description after we've recorded all possible base relocs, and we must re-order when we can write the memory import as a result --- .../Compiler/ObjectWriter/WasmObjectWriter.cs | 75 ++++++++++++------- 1 file changed, 49 insertions(+), 26 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index 3f366350f3487f..5b5aab51cd2c6d 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -245,6 +245,11 @@ private WasmSectionType GetWasmSectionType(ObjectNodeSection section) return _sectionToType[section]; } + private WebcilSectionType GetWebcilSectionType(ObjectNodeSection section) + { + return section == WebcilRelocSection ? WebcilSectionType.Reloc : WebcilSectionType.Data; + } + protected internal override void UpdateSectionAlignment(int sectionIndex, int alignment) { WebcilSection section = _sections[sectionIndex] as WebcilSection; @@ -371,9 +376,9 @@ private void InsertWasmStub(Utf8String name, WasmFunctionBody body) public const int WebcilSectionAlignment = 16; private WebcilSegment BuildWebcilDataSegment() { - List webcilSections = _sections.OfType().ToList(); + WebcilSection[] webcilSections = _sections.OfType().ToArray(); - uint sizeOfHeaders = (uint)WebcilEncoder.HeaderEncodeSize() + (uint)(webcilSections.Count * WebcilEncoder.SectionHeaderEncodeSize()); + uint sizeOfHeaders = (uint)WebcilEncoder.HeaderEncodeSize() + (uint)(webcilSections.Length * WebcilEncoder.SectionHeaderEncodeSize()); uint pointerToRawData = (uint)AlignmentHelper.AlignUp((int)sizeOfHeaders, (int)WebcilSectionAlignment); uint virtualAddress = pointerToRawData; @@ -408,7 +413,7 @@ private WebcilSegment BuildWebcilDataSegment() Id = WebcilConstants.WEBCIL_MAGIC, VersionMajor = WebcilConstants.WC_VERSION_MAJOR, VersionMinor = WebcilConstants.WC_VERSION_MINOR, - CoffSections = (ushort)webcilSections.Count, + CoffSections = (ushort)webcilSections.Length, PeCliHeaderRva = 0, // This RVA will be resolved later PeCliHeaderSize = 0, // Resolved along with RVA PeDebugRva = 0, // This RVA will be resolved later @@ -445,6 +450,7 @@ private protected override SectionWriter.Params WriterParams(ObjectNodeSection s }; } + private protected override void CreateSection(ObjectNodeSection section, Utf8String comdatName, Utf8String symbolName, int sectionIndex, Stream sectionStream) { WasmSectionType sectionType = GetWasmSectionType(section); @@ -453,7 +459,8 @@ private protected override void CreateSection(ObjectNodeSection section, Utf8Str { #if READYTORUN // This is a section which is internally wrapping a Webcil section - wasmSection = new WebcilSection(WebcilSectionType.Data, new Utf8String(section.Name), default(WebcilSectionHeader), sectionStream, sectionIndex); + WebcilSectionType webcilSectionType = GetWebcilSectionType(section); + wasmSection = new WebcilSection(webcilSectionType, new Utf8String(section.Name), default(WebcilSectionHeader), sectionStream, sectionIndex); #else wasmSection = new WasmSection(WasmSectionType.Data, sectionStream, new Utf8String(section.Name)); #endif @@ -478,9 +485,6 @@ private void WriteDataCountSection() private WebcilSegment _webcilSegment = null; private protected override void EmitSectionsAndLayout() { - - _webcilSegment = BuildWebcilDataSegment(); - InsertWasmStub(new Utf8String("getWebcilSize"), GetWebcilSize); InsertWasmStub(new Utf8String("getWebcilPayload"), GetWebcilPayload); @@ -582,7 +586,7 @@ private int[] SectionEmitOrder } } - private static ObjectNodeSection WebcilRelocSection = new ObjectNodeSection("reloc", SectionType.ReadOnly); + private static readonly ObjectNodeSection WebcilRelocSection = new ObjectNodeSection("reloc", SectionType.ReadOnly); private void EmitRelocSectionData() { var writer = GetOrCreateSection(WebcilRelocSection); @@ -618,6 +622,22 @@ private protected override void EmitObjectFile(Stream outputFileStream) { Debug.Assert(outputFileStream.CanSeek, $"EmitObjectFile requires seekable output stream"); + if (_baseRelocMap.Count > 0) + { + EmitRelocSectionData(); + } + + // Creating the webcil segment <- emitting relocs (we need to know the size of the relocs section) + _webcilSegment = BuildWebcilDataSegment(); + // Writing our memory import <- size of the webcil segment (for an accurate minimum size) + WriteMemoryImport((ulong)_webcilSegment.GetFlatMappedSize()); + // Writing element counts <- imports being finalized. + EmitSectionElementCounts(); + + /********************************************************************* + * Write Wasm Sections, Excluding Data + *********************************************************************/ + EmitWasmHeader(outputFileStream); foreach (int index in SectionEmitOrder) { @@ -640,12 +660,9 @@ private protected override void EmitObjectFile(Stream outputFileStream) } #if READYTORUN - /***************************************************************** - Emit Webcil segment at end of file to support ReadyToRun - *****************************************************************/ - - Debug.Assert(_webcilSegment != null); // This should have been built in EmitSectionsAndLayout() + * Emit Webcil segment at end of file to support ReadyToRun + ****************************************************************/ // Populate the RVAs for the Cor header/size and debug directory/size, which are required for the runtime // to be able to load this segment. @@ -940,9 +957,8 @@ void WriteRelocFromDataSpan(SymbolicRelocation reloc, byte* pData, long sectionS const int ImageBaseGlobalIndex = 1; const int ImageFunctionPointerBaseGlobalIndex = 2; - private WasmImport[] _defaultImports = new[] + private WasmImport[] _defaultGlobalImports = new[] { - null, // placeholder for memory, which is set up dynamically in WriteImports() new WasmImport("env", "__stack_pointer", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Mut), index: StackPointerGlobalIndex), new WasmImport("env", "__image_base", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: ImageBaseGlobalIndex), new WasmImport("env", "__image_function_pointer_base", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: ImageFunctionPointerBaseGlobalIndex), @@ -950,16 +966,9 @@ void WriteRelocFromDataSpan(SymbolicRelocation reloc, byte* pData, long sectionS private void WriteImports() { - // Calculate the minimum required memory size based on the Webcil Segment size - ulong contentSize = (ulong)_webcilSegment.GetFlatMappedSize(); - uint dataPages = checked((uint)((contentSize + (1 << 16) - 1) >> 16)); - uint numPages = Math.Max(dataPages, 1); // Ensure at least one page is allocated for the minimum - - // TODO-Wasm: decide on convention here; webcil spec states this should be "webcil" - _defaultImports[0] = new WasmImport("env", "memory", import: new WasmMemoryImportType(WasmLimitType.HasMin, numPages)); // memory limits: flags (0 = only minimum) int[] assignedImportIndices = new int[(int)WasmExternalKind.Count]; - foreach (WasmImport import in _defaultImports) + foreach (WasmImport import in _defaultGlobalImports) { if (import.Index.HasValue) { @@ -973,6 +982,16 @@ private void WriteImports() _numImportedGlobals = assignedImportIndices[(int)WasmExternalKind.Global]; } + private void WriteMemoryImport(ulong contentSize) + { + uint dataPages = checked((uint)((contentSize + (1 << 16) - 1) >> 16)); + uint numPages = Math.Max(dataPages, 1); // Ensure at least one page is allocated for the minimum + + // TODO-Wasm: decide on convention here; webcil spec states this should be "webcil" + WasmImport memoryImport = new WasmImport("env", "memory", import: new WasmMemoryImportType(WasmLimitType.HasMin, numPages)); // memory limits: flags (0 = only minimum) + WriteImport(memoryImport); + } + private void WriteExports() { WriteTableExport("table", 0); @@ -1014,6 +1033,12 @@ private protected override void EmitSymbolTable(IDictionary(definedSymbols); + } + + private void EmitSectionElementCounts() + { int funcIdx = _sectionNameToIndex[WasmObjectNodeSection.FunctionSection.Name]; PrependCount(_sections[funcIdx], _methodCount); @@ -1031,12 +1056,10 @@ private protected override void EmitSymbolTable(IDictionary(definedSymbols); } } + internal class WasmSection { public WasmSectionType Type { get; } From 9fc130801ba66876b0f1c4ea5ce06876bd220834 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Tue, 31 Mar 2026 16:11:17 -0700 Subject: [PATCH 07/20] Refactor: move cor header/debug directory rva initialization into BuildWebcilDataSegment() --- .../Compiler/ObjectWriter/WasmObjectWriter.cs | 69 +++++++++---------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index 5b5aab51cd2c6d..ae84874483133f 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -300,20 +300,7 @@ public int GetFlatMappedSize() return size; } - public long ResolveSymbolRVA(SymbolDefinition definition) - { - for (int i = 0; i < Sections.Length; i++) - { - WebcilSection section = Sections[i]; - if (definition.SectionIndex == section.Index) - { - return section.Header.VirtualAddress + definition.Value; - } - } - - return 0; - } - } + } static WasmFunctionBody GetWebcilSize = new WasmFunctionBody( new WasmFuncType(new([WasmValueType.I32]), new([])), // (func (destPtr i32) (result)) @@ -371,7 +358,21 @@ private void InsertWasmStub(Utf8String name, WasmFunctionBody body) _methodCount++; RegisterStubIndexAndSignature(body); + } + private long ResolveSymbolRVA(WebcilSection[] sections, SymbolDefinition definition) + { + for (int i = 0; i < sections.Length; i++) + { + WebcilSection section = sections[i]; + if (definition.SectionIndex == section.Index) + { + return section.Header.VirtualAddress + definition.Value; + } + } + + return 0; + } public const int WebcilSectionAlignment = 16; private WebcilSegment BuildWebcilDataSegment() @@ -408,16 +409,31 @@ private WebcilSegment BuildWebcilDataSegment() virtualAddress += virtualSize; } + // Populate the RVAs for the Cor header/size and debug directory/size, which are required for the runtime + // to be able to load this segment. + Utf8String corHeaderDefName = _wellKnownSymbols[SortableDependencyNode.ObjectNodeOrder.CorHeaderNode]; + SymbolDefinition corHeaderNode = _definedSymbols[corHeaderDefName]; + uint peCliHeaderRva = (uint)ResolveSymbolRVA(webcilSections, corHeaderNode); + Debug.Assert(peCliHeaderRva != 0); + uint peCliHeaderSize = (uint)corHeaderNode.Size; + + Utf8String debugDirectoryDefName = _wellKnownSymbols[SortableDependencyNode.ObjectNodeOrder.DebugDirectoryNode]; + SymbolDefinition debugDirectoryDef = _definedSymbols[debugDirectoryDefName]; + uint peDebugRva = (uint)ResolveSymbolRVA(webcilSections, debugDirectoryDef); + Debug.Assert(_webcilSegment.Header.PeDebugRva != 0); + uint peDebugSize = (uint)debugDirectoryDef.Size; + WebcilHeader header = new WebcilHeader { Id = WebcilConstants.WEBCIL_MAGIC, VersionMajor = WebcilConstants.WC_VERSION_MAJOR, VersionMinor = WebcilConstants.WC_VERSION_MINOR, CoffSections = (ushort)webcilSections.Length, - PeCliHeaderRva = 0, // This RVA will be resolved later - PeCliHeaderSize = 0, // Resolved along with RVA - PeDebugRva = 0, // This RVA will be resolved later - PeDebugSize = 0 // Resolved along with RVA + Reserved0 = 0, + PeCliHeaderRva = peCliHeaderRva, + PeCliHeaderSize = peCliHeaderSize, + PeDebugRva = peDebugRva, + PeDebugSize = peDebugSize }; return new WebcilSegment(header, webcilSections.ToArray()); @@ -664,23 +680,6 @@ private protected override void EmitObjectFile(Stream outputFileStream) * Emit Webcil segment at end of file to support ReadyToRun ****************************************************************/ - // Populate the RVAs for the Cor header/size and debug directory/size, which are required for the runtime - // to be able to load this segment. - bool exists = _wellKnownSymbols.TryGetValue(SortableDependencyNode.ObjectNodeOrder.CorHeaderNode, out Utf8String corHeaderDefName); - Debug.Assert(exists, $"Cor header symbol definition {SortableDependencyNode.ObjectNodeOrder.CorHeaderNode} not found"); - - SymbolDefinition corHeaderNode = _definedSymbols[corHeaderDefName]; - _webcilSegment.Header.PeCliHeaderRva = (uint)_webcilSegment.ResolveSymbolRVA(corHeaderNode); - Debug.Assert(_webcilSegment.Header.PeCliHeaderRva != 0); - _webcilSegment.Header.PeCliHeaderSize = (uint)corHeaderNode.Size; - - exists = _wellKnownSymbols.TryGetValue(SortableDependencyNode.ObjectNodeOrder.DebugDirectoryNode, out Utf8String debugDirectoryDefName); - Debug.Assert(exists, $"Debug directory symbol definition {SortableDependencyNode.ObjectNodeOrder.DebugDirectoryNode} not found"); - - SymbolDefinition debugDirectoryDef = _definedSymbols[debugDirectoryDefName]; - _webcilSegment.Header.PeDebugRva = (uint)_webcilSegment.ResolveSymbolRVA(debugDirectoryDef); - Debug.Assert(_webcilSegment.Header.PeDebugRva != 0); - _webcilSegment.Header.PeDebugSize = (uint)debugDirectoryDef.Size; MemoryStream webcilStream = new(_webcilSegment.GetFlatMappedSize()); WebcilEncoder.EmitHeader(_webcilSegment.Header, webcilStream); From 2d81282292fe690fb4ba25b5e7401ff6106a3176 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Tue, 31 Mar 2026 16:36:28 -0700 Subject: [PATCH 08/20] Reuse Reserved0 field for index of image base relocs --- .../Compiler/ObjectWriter/WasmObjectWriter.cs | 24 +++++++++---------- .../Compiler/ObjectWriter/WebcilEncoder.cs | 12 ++++------ src/coreclr/tools/Common/Wasm/Webcil.cs | 10 +------- 3 files changed, 18 insertions(+), 28 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index ae84874483133f..dc36fafbb2b9fa 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -245,11 +245,6 @@ private WasmSectionType GetWasmSectionType(ObjectNodeSection section) return _sectionToType[section]; } - private WebcilSectionType GetWebcilSectionType(ObjectNodeSection section) - { - return section == WebcilRelocSection ? WebcilSectionType.Reloc : WebcilSectionType.Data; - } - protected internal override void UpdateSectionAlignment(int sectionIndex, int alignment) { WebcilSection section = _sections[sectionIndex] as WebcilSection; @@ -397,7 +392,6 @@ private WebcilSegment BuildWebcilDataSegment() // the pointer to raw data for each section is also the same as the virtual address. uint virtualSize = alignedSectionSize; WebcilSectionHeader sectionHeader = new WebcilSectionHeader( - sectionType: webcilSection.Kind, virtualSize: virtualSize, virtualAddress: virtualAddress, sizeOfRawData: alignedSectionSize, @@ -423,13 +417,22 @@ private WebcilSegment BuildWebcilDataSegment() Debug.Assert(_webcilSegment.Header.PeDebugRva != 0); uint peDebugSize = (uint)debugDirectoryDef.Size; + // The index of the reloc section is either: 0 (if no reloc section) OR + // the 1-based index of the section, which in our case is assumed to be the last section + if (webcilSections.Length > 0) + { + Debug.Assert(webcilSections[webcilSections.Length - 1].Name.ToString() == "reloc"); + } + ushort relocSectionIdx = _baseRelocMap.Count > 0 ? checked((ushort)webcilSections.Length) : (ushort)0; + WebcilHeader header = new WebcilHeader { Id = WebcilConstants.WEBCIL_MAGIC, VersionMajor = WebcilConstants.WC_VERSION_MAJOR, VersionMinor = WebcilConstants.WC_VERSION_MINOR, CoffSections = (ushort)webcilSections.Length, - Reserved0 = 0, + // In Webcil v1.0, Reserved0 is used for the index of the image base reloc section + Reserved0 = relocSectionIdx, PeCliHeaderRva = peCliHeaderRva, PeCliHeaderSize = peCliHeaderSize, PeDebugRva = peDebugRva, @@ -475,8 +478,7 @@ private protected override void CreateSection(ObjectNodeSection section, Utf8Str { #if READYTORUN // This is a section which is internally wrapping a Webcil section - WebcilSectionType webcilSectionType = GetWebcilSectionType(section); - wasmSection = new WebcilSection(webcilSectionType, new Utf8String(section.Name), default(WebcilSectionHeader), sectionStream, sectionIndex); + wasmSection = new WebcilSection(new Utf8String(section.Name), default(WebcilSectionHeader), sectionStream, sectionIndex); #else wasmSection = new WasmSection(WasmSectionType.Data, sectionStream, new Utf8String(section.Name)); #endif @@ -1358,14 +1360,12 @@ class WebcilSection : WasmSection public readonly Stream _stream; private PaddingHelper _paddingHelper; public int MinAlignment = 1; - public WebcilSectionType Kind; public uint Padding => Header.SizeOfRawData - (uint)_stream.Length; - public WebcilSection(WebcilSectionType webcilSectionType, Utf8String name, WebcilSectionHeader header, Stream stream, int index) + public WebcilSection(Utf8String name, WebcilSectionHeader header, Stream stream, int index) : base(WasmSectionType.Data, stream, name) { - Kind = webcilSectionType; Header = header; _stream = stream; Index = index; diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WebcilEncoder.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WebcilEncoder.cs index b42225844398cf..ebc48e93119cf6 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WebcilEncoder.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WebcilEncoder.cs @@ -49,8 +49,7 @@ public static int EmitHeader(in WebcilHeader header, Stream outputStream) public static int SectionHeaderEncodeSize() { - return sizeof(WebcilSectionType) + // SectionType (uint): 4 - sizeof(uint) + // VirtualSize: 8 + return sizeof(uint) + // VirtualSize: 8 sizeof(uint) + // VirtualAddress: 12 sizeof(uint) + // SizeOfRawData: 16 sizeof(uint); // PointerToRawData: 20 @@ -62,11 +61,10 @@ public static int EncodeSectionHeader(in WebcilSectionHeader sectionHeader, Stre Span header = stackalloc byte[encodeSize]; // The Webcil spec requires little-endian encoding - BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(0, 4), (uint)sectionHeader.SectionType); - BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(4, 4), sectionHeader.VirtualSize); - BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(8, 4), sectionHeader.VirtualAddress); - BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(12, 4), sectionHeader.SizeOfRawData); - BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(16, 4), sectionHeader.PointerToRawData); + BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(0, 4), sectionHeader.VirtualSize); + BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(4, 4), sectionHeader.VirtualAddress); + BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(8, 4), sectionHeader.SizeOfRawData); + BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(12, 4), sectionHeader.PointerToRawData); outputStream.Write(header); diff --git a/src/coreclr/tools/Common/Wasm/Webcil.cs b/src/coreclr/tools/Common/Wasm/Webcil.cs index 3955b437241f52..38e0bd43954031 100644 --- a/src/coreclr/tools/Common/Wasm/Webcil.cs +++ b/src/coreclr/tools/Common/Wasm/Webcil.cs @@ -42,12 +42,6 @@ public struct WebcilHeader // 28 bytes } -public enum WebcilSectionType : uint -{ - Data = 0x1, - Reloc = 0x2 -} - /// /// Represents a section header in a Webcil file. /// @@ -57,15 +51,13 @@ public enum WebcilSectionType : uint [StructLayout(LayoutKind.Sequential, Pack = 1)] public readonly struct WebcilSectionHeader { - public readonly WebcilSectionType SectionType; public readonly uint VirtualSize; public readonly uint VirtualAddress; public readonly uint SizeOfRawData; public readonly uint PointerToRawData; - public WebcilSectionHeader(WebcilSectionType sectionType, uint virtualSize, uint virtualAddress, uint sizeOfRawData, uint pointerToRawData) + public WebcilSectionHeader(uint virtualSize, uint virtualAddress, uint sizeOfRawData, uint pointerToRawData) { - SectionType = sectionType; VirtualSize = virtualSize; VirtualAddress = virtualAddress; SizeOfRawData = sizeOfRawData; From d207de86483a5731d251d738754984147042031c Mon Sep 17 00:00:00 2001 From: adamperlin Date: Wed, 1 Apr 2026 11:19:01 -0700 Subject: [PATCH 09/20] Add ApplyBaseRelocs functionality for webcil in CoreCLR, including new Wasm table-based relocs --- src/coreclr/inc/webcildecoder.h | 4 +- src/coreclr/pal/inc/rt/ntimage.h | 9 ++++ src/coreclr/utilcode/webcildecoder.cpp | 47 ++++++++++++++++++- src/coreclr/vm/peimagelayout.cpp | 62 +++++++++++++++++++------- src/coreclr/vm/peimagelayout.h | 6 +++ src/coreclr/vm/peimagelayout.inl | 5 ++- 6 files changed, 113 insertions(+), 20 deletions(-) diff --git a/src/coreclr/inc/webcildecoder.h b/src/coreclr/inc/webcildecoder.h index c47d91267a8631..ce709c1d3bca85 100644 --- a/src/coreclr/inc/webcildecoder.h +++ b/src/coreclr/inc/webcildecoder.h @@ -48,7 +48,7 @@ struct WebcilHeader uint16_t VersionMajor; // 0 uint16_t VersionMinor; // 0 uint16_t CoffSections; - uint16_t Reserved0; + uint16_t Reserved0; // 1-based index of the base relocations section, or 0 if none uint32_t PeCliHeaderRva; uint32_t PeCliHeaderSize; uint32_t PeDebugRva; @@ -151,7 +151,7 @@ class WebcilDecoder BOOL HasNTHeaders() const { return FALSE; } CHECK CheckNTHeaders() const; BOOL Has32BitNTHeaders() const { return FALSE; } - BOOL HasBaseRelocations() const { return FALSE; } + BOOL HasBaseRelocations() const; BOOL HasWriteableSections() const { return FALSE; } BOOL HasTls() const { return FALSE; } diff --git a/src/coreclr/pal/inc/rt/ntimage.h b/src/coreclr/pal/inc/rt/ntimage.h index 8f88791ef51cf6..9acfdd1f924348 100644 --- a/src/coreclr/pal/inc/rt/ntimage.h +++ b/src/coreclr/pal/inc/rt/ntimage.h @@ -1163,6 +1163,15 @@ typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION; #define IMAGE_REL_BASED_ARM_MOV32 5 #define IMAGE_REL_BASED_THUMB_MOV32 7 +#ifdef FEATURE_WEBCIL +// +// Webcil-specific based relocation types. +// The fixup adds a WASM table base offset to the value at the relocation address. +// +#define IMAGE_REL_BASED_WASM32_TABLE 12 +#define IMAGE_REL_BASED_WASM64_TABLE 13 +#endif // FEATURE_WEBCIL + // // Archive format. // diff --git a/src/coreclr/utilcode/webcildecoder.cpp b/src/coreclr/utilcode/webcildecoder.cpp index 516a85366da85c..1d3296b5da06ee 100644 --- a/src/coreclr/utilcode/webcildecoder.cpp +++ b/src/coreclr/utilcode/webcildecoder.cpp @@ -134,6 +134,15 @@ BOOL WebcilDecoder::HasWebcilHeaders() const RETURN TRUE; } +BOOL WebcilDecoder::HasBaseRelocations() const +{ + LIMITED_METHOD_CONTRACT; + if (!HasWebcilHeaders()) + return FALSE; + const WebcilHeader *pHeader = (const WebcilHeader *)m_base; + return pHeader->Reserved0 != 0; +} + CHECK WebcilDecoder::CheckWebcilHeaders() const { CONTRACT_CHECK @@ -741,13 +750,19 @@ BOOL WebcilDecoder::HasDirectoryEntry(int entry) const LIMITED_METHOD_CONTRACT; // Webcil has no PE IMAGE_DATA_DIRECTORY array. - // Only the debug directory is stored in the WebcilHeader. + // Only the debug directory and base relocation directory are supported. if (entry == IMAGE_DIRECTORY_ENTRY_DEBUG && HasWebcilHeaders()) { const WebcilHeader *pHeader = (const WebcilHeader *)m_base; return pHeader->PeDebugRva != 0 && pHeader->PeDebugSize != 0; } + if (entry == IMAGE_DIRECTORY_ENTRY_BASERELOC && HasWebcilHeaders()) + { + const WebcilHeader *pHeader = (const WebcilHeader *)m_base; + return pHeader->Reserved0 != 0; + } + return FALSE; } @@ -783,6 +798,36 @@ TADDR WebcilDecoder::GetDirectoryEntryData(int entry, COUNT_T *pSize) const return GetRvaData(debugRva); } + // Base relocations: Reserved0 is the 1-based index of the relocations section + if (entry == IMAGE_DIRECTORY_ENTRY_BASERELOC && HasWebcilHeaders()) + { + const WebcilHeader *pHeader = (const WebcilHeader *)m_base; + uint16_t relocSectionIndex = pHeader->Reserved0; + if (relocSectionIndex == 0) + { + if (pSize != NULL) + *pSize = 0; + return (TADDR)0; + } + + // Convert from 1-based to 0-based index and validate + uint16_t sectionIndex = relocSectionIndex - 1; + if (sectionIndex >= pHeader->CoffSections) + { + if (pSize != NULL) + *pSize = 0; + return (TADDR)0; + } + + const WebcilSectionHeader *sections = (const WebcilSectionHeader *)(m_base + sizeof(WebcilHeader)); + const WebcilSectionHeader *relocSection = §ions[sectionIndex]; + + if (pSize != NULL) + *pSize = relocSection->SizeOfRawData; + + return (TADDR)(m_base + relocSection->PointerToRawData); + } + if (pSize != NULL) *pSize = 0; return (TADDR)0; diff --git a/src/coreclr/vm/peimagelayout.cpp b/src/coreclr/vm/peimagelayout.cpp index 3cdc899d6ad67c..7369e5b43cc632 100644 --- a/src/coreclr/vm/peimagelayout.cpp +++ b/src/coreclr/vm/peimagelayout.cpp @@ -233,7 +233,13 @@ void PEImageLayout::ApplyBaseRelocations(bool relocationMustWriteCopy) { STANDARD_VM_CONTRACT; - _ASSERTE(IsPEFormat()); +#ifdef FEATURE_WEBCIL + if (!IsWebcilFormat()) +#endif // FEATURE_WEBCIL + { + _ASSERTE(IsPEFormat()); + } + SetRelocated(); // @@ -243,9 +249,19 @@ void PEImageLayout::ApplyBaseRelocations(bool relocationMustWriteCopy) SSIZE_T delta = (SIZE_T) GetBase() - (SIZE_T) GetPreferredBase(); - // Nothing to do - image is loaded at preferred base - if (delta == 0) +#ifdef FEATURE_WEBCIL + SSIZE_T tableBaseDelta = m_tableBaseOffset; +#endif // FEATURE_WEBCIL + + // Nothing to do - image is loaded at preferred base and no table base offset + if (delta == 0 +#ifdef FEATURE_WEBCIL + && tableBaseDelta == 0 +#endif // FEATURE_WEBCIL + ) + { return; + } LOG((LF_LOADER, LL_INFO100, "PEImage: Applying base relocations (preferred: %x, actual: %x)\n", GetPreferredBase(), GetBase())); @@ -283,8 +299,9 @@ void PEImageLayout::ApplyBaseRelocations(bool relocationMustWriteCopy) BYTE * pageAddress = (BYTE *)GetBase() + rva; - // Check whether the page is outside the unprotected region - if ((SIZE_T)(pageAddress - pWriteableRegion) >= cbWriteableRegion) + // Check whether the page is outside the unprotected region. + // Webcil data is already writable, so skip memory protection management. + if (IsPEFormat() && (SIZE_T)(pageAddress - pWriteableRegion) >= cbWriteableRegion) { // Restore the protection if (dwOldProtection != 0) @@ -361,6 +378,16 @@ void PEImageLayout::ApplyBaseRelocations(bool relocationMustWriteCopy) break; #endif +#ifdef FEATURE_WEBCIL + case IMAGE_REL_BASED_WASM32_TABLE: + *(uint32_t *)address += (uint32_t)tableBaseDelta; + break; + + case IMAGE_REL_BASED_WASM64_TABLE: + *(uint64_t *)address += (uint64_t)tableBaseDelta; + break; +#endif // FEATURE_WEBCIL + case IMAGE_REL_BASED_ABSOLUTE: //no adjustment break; @@ -370,22 +397,25 @@ void PEImageLayout::ApplyBaseRelocations(bool relocationMustWriteCopy) } } - BOOL bExecRegion = (dwOldProtection & (PAGE_EXECUTE | PAGE_EXECUTE_READ | - PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0; - - if (bExecRegion && pEndAddressToFlush != NULL) + if (IsPEFormat()) { - // If the current page is not next to the pending region to flush, flush the current pending region and start a new one - if (pageAddress >= pFlushRegion + cbFlushRegion + cbPageSize || pageAddress < pFlushRegion) + BOOL bExecRegion = (dwOldProtection & (PAGE_EXECUTE | PAGE_EXECUTE_READ | + PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0; + + if (bExecRegion && pEndAddressToFlush != NULL) { - if (pFlushRegion != NULL) + // If the current page is not next to the pending region to flush, flush the current pending region and start a new one + if (pageAddress >= pFlushRegion + cbFlushRegion + cbPageSize || pageAddress < pFlushRegion) { - ClrFlushInstructionCache(pFlushRegion, cbFlushRegion); + if (pFlushRegion != NULL) + { + ClrFlushInstructionCache(pFlushRegion, cbFlushRegion); + } + pFlushRegion = pageAddress; } - pFlushRegion = pageAddress; - } - cbFlushRegion = pEndAddressToFlush - pFlushRegion; + cbFlushRegion = pEndAddressToFlush - pFlushRegion; + } } dirPos += fixupsSize; diff --git a/src/coreclr/vm/peimagelayout.h b/src/coreclr/vm/peimagelayout.h index 76a3b1ec71ab2b..06a74e159b0fbc 100644 --- a/src/coreclr/vm/peimagelayout.h +++ b/src/coreclr/vm/peimagelayout.h @@ -76,6 +76,11 @@ class PEImageLayout void ApplyBaseRelocations(bool relocationMustWriteCopy); +#ifdef FEATURE_WEBCIL + void SetTableBaseOffset(SSIZE_T tableBaseOffset) { m_tableBaseOffset = tableBaseOffset; } + SSIZE_T GetTableBaseOffset() const { return m_tableBaseOffset; } +#endif + // ------------------------------------------------------------ // Format query // ------------------------------------------------------------ @@ -238,6 +243,7 @@ class PEImageLayout PEDecoder m_peDecoder; #ifdef FEATURE_WEBCIL WebcilDecoder m_webcilDecoder; + SSIZE_T m_tableBaseOffset; #endif public: diff --git a/src/coreclr/vm/peimagelayout.inl b/src/coreclr/vm/peimagelayout.inl index 562940a9b6004a..608efaa911d6f7 100644 --- a/src/coreclr/vm/peimagelayout.inl +++ b/src/coreclr/vm/peimagelayout.inl @@ -65,6 +65,9 @@ inline PEImageLayout::~PEImageLayout() inline PEImageLayout::PEImageLayout() : m_refCount(1) , m_format(FORMAT_PE) +#ifdef FEATURE_WEBCIL + , m_tableBaseOffset(0) +#endif , m_pOwner(NULL) { LIMITED_METHOD_CONTRACT; @@ -267,7 +270,7 @@ inline BOOL PEImageLayout::IsDll() const inline BOOL PEImageLayout::HasBaseRelocations() const { LIMITED_METHOD_DAC_CONTRACT; - PE_OR_WEBCIL(HasBaseRelocations(), FALSE) + DECODER_DISPATCH(HasBaseRelocations()) } inline const void *PEImageLayout::GetPreferredBase() const From b437a79e00b69a5017baf49007a2683ee0b47784 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Wed, 1 Apr 2026 15:53:32 -0700 Subject: [PATCH 10/20] Various fixes --- src/coreclr/inc/webcildecoder.h | 4 +++- .../tools/Common/Compiler/ObjectWriter/WasmNative.cs | 2 -- .../Common/Compiler/ObjectWriter/WasmObjectWriter.cs | 9 ++++----- .../tools/Common/Compiler/ObjectWriter/WebcilEncoder.cs | 8 ++++---- src/coreclr/vm/peimagelayout.cpp | 7 +------ src/coreclr/vm/peimagelayout.h | 3 ++- src/coreclr/vm/peimagelayout.inl | 6 ++++++ 7 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/coreclr/inc/webcildecoder.h b/src/coreclr/inc/webcildecoder.h index ce709c1d3bca85..9173e9efc4ae9b 100644 --- a/src/coreclr/inc/webcildecoder.h +++ b/src/coreclr/inc/webcildecoder.h @@ -137,7 +137,8 @@ class WebcilDecoder // Webcil is always flat, never mapped in the PE sense BOOL IsMapped() const { return FALSE; } - BOOL IsRelocated() const { return FALSE; } + BOOL IsRelocated() const { return m_relocated; } + void SetRelocated() { m_relocated = TRUE; } BOOL IsFlat() const { return HasContents(); } // ------------------------------------------------------------ @@ -258,6 +259,7 @@ class WebcilDecoder TADDR m_base; COUNT_T m_size; BOOL m_hasContents; + BOOL m_relocated = FALSE; const WebcilHeader *m_pHeader; mutable IMAGE_COR20_HEADER *m_pCorHeader; }; diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs index 76d58096ce3937..c9ea6fcf30d534 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs @@ -8,8 +8,6 @@ using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysis.Wasm; using ILCompiler.ObjectWriter.WasmInstructions; -using System.Buffers.Binary; -using System.IO; namespace ILCompiler.ObjectWriter { diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index dc36fafbb2b9fa..9ff746f2d0bbfb 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -8,7 +8,6 @@ using System.IO; using System.Linq; using System.Numerics; -using System.Runtime.CompilerServices; using System.Text; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysis.Wasm; @@ -414,7 +413,7 @@ private WebcilSegment BuildWebcilDataSegment() Utf8String debugDirectoryDefName = _wellKnownSymbols[SortableDependencyNode.ObjectNodeOrder.DebugDirectoryNode]; SymbolDefinition debugDirectoryDef = _definedSymbols[debugDirectoryDefName]; uint peDebugRva = (uint)ResolveSymbolRVA(webcilSections, debugDirectoryDef); - Debug.Assert(_webcilSegment.Header.PeDebugRva != 0); + Debug.Assert(peDebugRva != 0); uint peDebugSize = (uint)debugDirectoryDef.Size; // The index of the reloc section is either: 0 (if no reloc section) OR @@ -960,9 +959,9 @@ void WriteRelocFromDataSpan(SymbolicRelocation reloc, byte* pData, long sectionS private WasmImport[] _defaultGlobalImports = new[] { - new WasmImport("env", "__stack_pointer", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Mut), index: StackPointerGlobalIndex), - new WasmImport("env", "__image_base", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: ImageBaseGlobalIndex), - new WasmImport("env", "__image_function_pointer_base", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: ImageFunctionPointerBaseGlobalIndex), + new WasmImport("webcil", "__stack_pointer", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Mut), index: StackPointerGlobalIndex), + new WasmImport("webcil", "__image_base", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: ImageBaseGlobalIndex), + new WasmImport("webcil", "__image_function_pointer_base", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: ImageFunctionPointerBaseGlobalIndex), }; private void WriteImports() diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WebcilEncoder.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WebcilEncoder.cs index ebc48e93119cf6..8e2cb7e35cd190 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WebcilEncoder.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WebcilEncoder.cs @@ -49,10 +49,10 @@ public static int EmitHeader(in WebcilHeader header, Stream outputStream) public static int SectionHeaderEncodeSize() { - return sizeof(uint) + // VirtualSize: 8 - sizeof(uint) + // VirtualAddress: 12 - sizeof(uint) + // SizeOfRawData: 16 - sizeof(uint); // PointerToRawData: 20 + return sizeof(uint) + // VirtualSize: 4 + sizeof(uint) + // VirtualAddress: 8 + sizeof(uint) + // SizeOfRawData: 12 + sizeof(uint); // PointerToRawData: 16 } public static int EncodeSectionHeader(in WebcilSectionHeader sectionHeader, Stream outputStream) diff --git a/src/coreclr/vm/peimagelayout.cpp b/src/coreclr/vm/peimagelayout.cpp index 7369e5b43cc632..9f8ec1c33744e6 100644 --- a/src/coreclr/vm/peimagelayout.cpp +++ b/src/coreclr/vm/peimagelayout.cpp @@ -233,12 +233,7 @@ void PEImageLayout::ApplyBaseRelocations(bool relocationMustWriteCopy) { STANDARD_VM_CONTRACT; -#ifdef FEATURE_WEBCIL - if (!IsWebcilFormat()) -#endif // FEATURE_WEBCIL - { - _ASSERTE(IsPEFormat()); - } + _ASSERTE(IsPEFormat() || IsWebcilFormat()); SetRelocated(); diff --git a/src/coreclr/vm/peimagelayout.h b/src/coreclr/vm/peimagelayout.h index 06a74e159b0fbc..542ec86216569b 100644 --- a/src/coreclr/vm/peimagelayout.h +++ b/src/coreclr/vm/peimagelayout.h @@ -77,6 +77,7 @@ class PEImageLayout void ApplyBaseRelocations(bool relocationMustWriteCopy); #ifdef FEATURE_WEBCIL +// TODO-WASM: These can be removed very soon, and we can fetch this from the webcil header itself void SetTableBaseOffset(SSIZE_T tableBaseOffset) { m_tableBaseOffset = tableBaseOffset; } SSIZE_T GetTableBaseOffset() const { return m_tableBaseOffset; } #endif @@ -232,7 +233,7 @@ class PEImageLayout // Protected forwarding helpers for subclass access to PEDecoder protected members IMAGE_NT_HEADERS* FindNTHeaders() const { return m_peDecoder.FindNTHeaders(); } IMAGE_SECTION_HEADER* RvaToSection(RVA rva) const { return m_peDecoder.RvaToSection(rva); } - void SetRelocated() { m_peDecoder.SetRelocated(); } + void SetRelocated(); private: Volatile m_refCount; diff --git a/src/coreclr/vm/peimagelayout.inl b/src/coreclr/vm/peimagelayout.inl index 608efaa911d6f7..e919c7babef6d5 100644 --- a/src/coreclr/vm/peimagelayout.inl +++ b/src/coreclr/vm/peimagelayout.inl @@ -145,6 +145,12 @@ inline BOOL PEImageLayout::IsRelocated() const PE_OR_WEBCIL(IsRelocated(), FALSE) } +inline void PEImageLayout::SetRelocated() +{ + LIMITED_METHOD_DAC_CONTRACT; + DECODER_DISPATCH(SetRelocated()) +} + inline BOOL PEImageLayout::IsFlat() const { LIMITED_METHOD_DAC_CONTRACT; From a575fae2083c27a5e8a122a37444e30ebfdd7463 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Wed, 1 Apr 2026 16:09:16 -0700 Subject: [PATCH 11/20] Update docs --- docs/design/mono/webcil.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/design/mono/webcil.md b/docs/design/mono/webcil.md index 37d3c4968e4c59..9137a21abb797e 100644 --- a/docs/design/mono/webcil.md +++ b/docs/design/mono/webcil.md @@ -130,6 +130,13 @@ struct WebcilHeader { }; ``` +#### Webcil Header (V1 Changes) +For Webcil V1, the Reserved0 field may be used to store a 1-based index which corresponds to a +base reloc section. +``` + uint16_t Reserved0; // 0, or 1-based index of .reloc webcil section +``` + The Webcil header starts with the magic characters 'W' 'b' 'I' 'L' followed by the version in major minor format (must be 0 and 0). Then a count of the section headers and two reserved bytes. @@ -184,3 +191,18 @@ Lossless conversion from Webcil back to PE is not intended to be supported. The documented in order to support diagnostic tooling and utilities such as decompilers, disassemblers, file identification utilities, dependency analyzers, etc. + +### Webcil V1 (Base Relocations) +It is possible to specify base relocations in the standard PE base relocation format in Webcil V1. +Relocations are grouped by page (default 4kb), and each relocation entry is a `uint16_t` containing: + +- Relocation Type (upper 4 bits) + - IMAGE_REL_BASED_DIR64 (wasm64) + - IMAGE_REL_BASED_HIGHLOW (wasm32) + - IMAGE_REL_BASED_WASM32_TABLE (wasm32) + - IMAGE_REL_BASED_WASM64_TABLE (wasm64) +- Offset (lower 12 bits) + +IMAGE_REL_BASED_WASM{32, 64}_TABLE relocations represent a "table base offset" fixup; They should be used to indicate places +where function pointer table indices need to be offset after the Webcil payload has been loaded by the runtime. The offset will +be dependent on the state of the table when an implementation's loader loads a Webcil module. From 3a341bd808277a74a8aec128f67b50b756cc97d0 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Wed, 1 Apr 2026 16:22:49 -0700 Subject: [PATCH 12/20] Fix markdown lint errors --- docs/design/mono/webcil.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/design/mono/webcil.md b/docs/design/mono/webcil.md index 9137a21abb797e..ca41a31f680147 100644 --- a/docs/design/mono/webcil.md +++ b/docs/design/mono/webcil.md @@ -130,7 +130,7 @@ struct WebcilHeader { }; ``` -#### Webcil Header (V1 Changes) +#### Webcil Header (V1 Changes) For Webcil V1, the Reserved0 field may be used to store a 1-based index which corresponds to a base reloc section. ``` @@ -193,7 +193,7 @@ file identification utilities, dependency analyzers, etc. ### Webcil V1 (Base Relocations) -It is possible to specify base relocations in the standard PE base relocation format in Webcil V1. +It is possible to specify base relocations in the standard PE base relocation format in Webcil V1. Relocations are grouped by page (default 4kb), and each relocation entry is a `uint16_t` containing: - Relocation Type (upper 4 bits) @@ -203,6 +203,6 @@ Relocations are grouped by page (default 4kb), and each relocation entry is a `u - IMAGE_REL_BASED_WASM64_TABLE (wasm64) - Offset (lower 12 bits) -IMAGE_REL_BASED_WASM{32, 64}_TABLE relocations represent a "table base offset" fixup; They should be used to indicate places +`IMAGE_REL_BASED_WASM{32, 64}_TABLE` relocations represent a "table base offset" fixup; They should be used to indicate places where function pointer table indices need to be offset after the Webcil payload has been loaded by the runtime. The offset will be dependent on the state of the table when an implementation's loader loads a Webcil module. From 397f68763fb7a627164282860a7fd44ad666cba5 Mon Sep 17 00:00:00 2001 From: Adam Perlin Date: Wed, 1 Apr 2026 16:27:32 -0700 Subject: [PATCH 13/20] Update src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index 9ff746f2d0bbfb..c06c9f5384f0df 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -735,9 +735,7 @@ private protected override void EmitObjectFile(Stream outputFileStream) } Dictionary> _resolvableRelocations = new(); - Dictionary> _runtimeRelocations = new(); Dictionary> _baseRelocMap = new(); - // We group webcil relocs into 4kb blocks, similar to PE const uint WebcilRelocPageSize = 0x1000; From 8ec58e89005f467847092b30f60e0358034a7975 Mon Sep 17 00:00:00 2001 From: Adam Perlin Date: Wed, 1 Apr 2026 16:27:50 -0700 Subject: [PATCH 14/20] Update src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index c06c9f5384f0df..eff607026afcf8 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -418,8 +418,9 @@ private WebcilSegment BuildWebcilDataSegment() // The index of the reloc section is either: 0 (if no reloc section) OR // the 1-based index of the section, which in our case is assumed to be the last section - if (webcilSections.Length > 0) + if (_baseRelocMap.Count > 0) { + Debug.Assert(webcilSections.Length > 0); Debug.Assert(webcilSections[webcilSections.Length - 1].Name.ToString() == "reloc"); } ushort relocSectionIdx = _baseRelocMap.Count > 0 ? checked((ushort)webcilSections.Length) : (ushort)0; From 379833b52c3e64b96319e5e656a2cf1c161d96d8 Mon Sep 17 00:00:00 2001 From: Adam Perlin Date: Wed, 1 Apr 2026 16:28:39 -0700 Subject: [PATCH 15/20] Update src/coreclr/utilcode/webcildecoder.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreclr/utilcode/webcildecoder.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/coreclr/utilcode/webcildecoder.cpp b/src/coreclr/utilcode/webcildecoder.cpp index 1d3296b5da06ee..1c46dac6fe1d9d 100644 --- a/src/coreclr/utilcode/webcildecoder.cpp +++ b/src/coreclr/utilcode/webcildecoder.cpp @@ -139,8 +139,15 @@ BOOL WebcilDecoder::HasBaseRelocations() const LIMITED_METHOD_CONTRACT; if (!HasWebcilHeaders()) return FALSE; + const WebcilHeader *pHeader = (const WebcilHeader *)m_base; - return pHeader->Reserved0 != 0; + uint32_t relocSectionIndex = pHeader->Reserved0; + uint16_t numSections = pHeader->CoffSections; + + if (relocSectionIndex == 0 || relocSectionIndex > numSections) + return FALSE; + + return TRUE; } CHECK WebcilDecoder::CheckWebcilHeaders() const From 3188b7fe3550f56446438cc0a72504bf89100ba8 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Wed, 1 Apr 2026 16:35:07 -0700 Subject: [PATCH 16/20] Feedback --- src/coreclr/utilcode/webcildecoder.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/utilcode/webcildecoder.cpp b/src/coreclr/utilcode/webcildecoder.cpp index 1c46dac6fe1d9d..d1cc1887b23a8f 100644 --- a/src/coreclr/utilcode/webcildecoder.cpp +++ b/src/coreclr/utilcode/webcildecoder.cpp @@ -57,6 +57,7 @@ void WebcilDecoder::Init(void *flatBase, COUNT_T size) m_hasContents = (size > 0); m_pHeader = (m_hasContents && size >= sizeof(WebcilHeader)) ? (const WebcilHeader *)flatBase : NULL; m_pCorHeader = NULL; + m_relocated = FALSE; } void WebcilDecoder::Reset() @@ -67,6 +68,7 @@ void WebcilDecoder::Reset() m_hasContents = FALSE; m_pHeader = NULL; m_pCorHeader = NULL; + m_relocated = FALSE; } // ------------------------------------------------------------ From 43d6f6bc9f11c1fd6f4635d80b73c19704d21cf3 Mon Sep 17 00:00:00 2001 From: Adam Perlin Date: Thu, 2 Apr 2026 09:47:43 -0700 Subject: [PATCH 17/20] Update src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index eff607026afcf8..bfd841ca75043e 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -986,8 +986,7 @@ private void WriteMemoryImport(ulong contentSize) uint dataPages = checked((uint)((contentSize + (1 << 16) - 1) >> 16)); uint numPages = Math.Max(dataPages, 1); // Ensure at least one page is allocated for the minimum - // TODO-Wasm: decide on convention here; webcil spec states this should be "webcil" - WasmImport memoryImport = new WasmImport("env", "memory", import: new WasmMemoryImportType(WasmLimitType.HasMin, numPages)); // memory limits: flags (0 = only minimum) + WasmImport memoryImport = new WasmImport("webcil", "memory", import: new WasmMemoryImportType(WasmLimitType.HasMin, numPages)); // memory limits: flags (0 = only minimum) WriteImport(memoryImport); } From 832d58507c5bba7e2ac01911557be298d3a938f5 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Thu, 2 Apr 2026 14:45:59 -0700 Subject: [PATCH 18/20] Fix base relocation address bug; When emitting base relocations, webcil section addresses were not yet assigned, leading to a bug where base relocations were not properly offset by the section they apply to. This commit re-orders our base relocation construction/emission so that we construct our base relocations AFTER Webcil sections have been assigned addresses --- .../Compiler/ObjectWriter/WasmObjectWriter.cs | 98 ++++++++++++++----- 1 file changed, 74 insertions(+), 24 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index eff607026afcf8..26e9a41e4e01aa 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -369,15 +369,20 @@ private long ResolveSymbolRVA(WebcilSection[] sections, SymbolDefinition definit } public const int WebcilSectionAlignment = 16; - private WebcilSegment BuildWebcilDataSegment() - { - WebcilSection[] webcilSections = _sections.OfType().ToArray(); + /// + /// Assigns VirtualAddresses and related header fields to each webcil section based on the + /// total section count and each section's stream length. This can be called before all + /// sections have their final content as long as the section count is finalized, though + /// sections whose size changes later must come last they don't invalidate earlier VAs. + /// + private static void AssignWebcilSectionVirtualAddresses(WebcilSection[] webcilSections) + { uint sizeOfHeaders = (uint)WebcilEncoder.HeaderEncodeSize() + (uint)(webcilSections.Length * WebcilEncoder.SectionHeaderEncodeSize()); uint pointerToRawData = (uint)AlignmentHelper.AlignUp((int)sizeOfHeaders, (int)WebcilSectionAlignment); uint virtualAddress = pointerToRawData; - for (int i = 0; i < webcilSections.Count(); i++) + for (int i = 0; i < webcilSections.Length; i++) { WebcilSection webcilSection = webcilSections[i]; Debug.Assert(BitOperations.IsPow2(webcilSection.MinAlignment) && BitOperations.IsPow2(WebcilSectionAlignment) && @@ -401,6 +406,13 @@ private WebcilSegment BuildWebcilDataSegment() pointerToRawData += alignedSectionSize; virtualAddress += virtualSize; } + } + + private WebcilSegment BuildWebcilDataSegment() + { + WebcilSection[] webcilSections = _sections.OfType().ToArray(); + + AssignWebcilSectionVirtualAddresses(webcilSections); // Populate the RVAs for the Cor header/size and debug directory/size, which are required for the runtime // to be able to load this segment. @@ -640,13 +652,29 @@ private protected override void EmitObjectFile(Stream outputFileStream) { Debug.Assert(outputFileStream.CanSeek, $"EmitObjectFile requires seekable output stream"); + if (_pendingBaseRelocs.Count > 0) + { + GetOrCreateSection(WebcilRelocSection); + } + + WebcilSection[] webcilSections = _sections.OfType().ToArray(); + // At this point, our count of sections is final since we've determined if we have base relocs. + // This allows us to do an initial assignment of virtual addresses to our webcil sections, + // which is required for resolving file-level relocations whose RVA depends on the section VAs. + AssignWebcilSectionVirtualAddresses(webcilSections); + + // We can now build our base relocs with the correct addresses + BuildBaseRelocMap(); + if (_baseRelocMap.Count > 0) { EmitRelocSectionData(); } - // Creating the webcil segment <- emitting relocs (we need to know the size of the relocs section) - _webcilSegment = BuildWebcilDataSegment(); + // Build the final webcil segment (re-assigns VAs with reloc section's real size). This must come last, + // since we must know if we have a reloc section as well as its final size to determine the segment layout. + _webcilSegment = BuildWebcilDataSegment(); + // Writing our memory import <- size of the webcil segment (for an accurate minimum size) WriteMemoryImport((ulong)_webcilSegment.GetFlatMappedSize()); // Writing element counts <- imports being finalized. @@ -740,6 +768,11 @@ private protected override void EmitObjectFile(Stream outputFileStream) // We group webcil relocs into 4kb blocks, similar to PE const uint WebcilRelocPageSize = 0x1000; + // File-level relocations whose RVA computation is deferred until webcil section + // VirtualAddresses have been assigned. + private readonly record struct PendingBaseReloc(int SectionIndex, long Offset, RelocType FileRelocType); + private readonly List _pendingBaseRelocs = new(); + private protected override void EmitRelocations(int sectionIndex, List relocationList) { foreach (var reloc in relocationList) @@ -749,32 +782,49 @@ private protected override void EmitRelocations(int sectionIndex, List(); } // Unconditionally add the reloc to our resolvable list; we do some amount of relocation resolution - // for all relocation types + // for all relocation types. resolvable.Add(reloc); // A few relocation types (table indices and IMAGE_REL type relocs in Webcil) need // an additional runtime reloc as well to add a base address. + // We defer the actual RVA computation to EmitObjectFile, where webcil section + // VirtualAddresses will have been assigned. Here we just record the raw info. if (Relocation.GetFileRelocationType(reloc.Type) is RelocType fileRelocType && fileRelocType is not RelocType.IMAGE_REL_BASED_ABSOLUTE) { - WebcilSection webcilSection = _sections[sectionIndex] as WebcilSection; - Debug.Assert(webcilSection is not null); - // Gather file-level relocations that need to go into the webcil .reloc - // section. We collect entries grouped by 4KB page into a map of - // (page RVA -> list of (type<<12 | offsetInPage) WORD entries). - // Note that this handling is logically the same as the implementation in the PE Object Writer. - uint targetRva = webcilSection.Header.VirtualAddress + (uint)reloc.Offset; - uint pageRva = targetRva & ~(WebcilRelocPageSize - 1); - ushort offsetInPage = (ushort)(targetRva & (WebcilRelocPageSize - 1)); - ushort entry = (ushort)(((ushort)fileRelocType << 12) | offsetInPage); - - if (!_baseRelocMap.TryGetValue(pageRva, out List list)) - { - list = new List(); - _baseRelocMap.Add(pageRva, list); - } - list.Add(entry); + Debug.Assert(_sections[sectionIndex] is WebcilSection); + _pendingBaseRelocs.Add(new PendingBaseReloc(sectionIndex, reloc.Offset, fileRelocType)); + } + } + } + + /// + /// Processes the deferred file-level relocations after webcil section VirtualAddresses + /// have been assigned. Populates with page-grouped base reloc + /// entries, mirroring the PE base relocation format. + /// + private void BuildBaseRelocMap() + { + foreach (PendingBaseReloc pending in _pendingBaseRelocs) + { + Debug.Assert(_sections[pending.SectionIndex] is WebcilSection); + WebcilSection webcilSection = _sections[pending.SectionIndex] as WebcilSection; + // Gather file-level relocations that need to go into the webcil .reloc + // section. We collect entries grouped by 4KB page into a map of + // (page RVA -> list of (type<<12 | offsetInPage) WORD entries). + // Note that this handling is logically the same as the implementation in the PE Object Writer. + uint targetRva = webcilSection.Header.VirtualAddress + (uint)pending.Offset; + Debug.Assert(targetRva != 0); // this section should have been assigned a non-zero VirtualAddress at this point. + uint pageRva = targetRva & ~(WebcilRelocPageSize - 1); + ushort offsetInPage = (ushort)(targetRva & (WebcilRelocPageSize - 1)); + ushort entry = (ushort)(((ushort)pending.FileRelocType << 12) | offsetInPage); + + if (!_baseRelocMap.TryGetValue(pageRva, out List list)) + { + list = new List(); + _baseRelocMap.Add(pageRva, list); } + list.Add(entry); } } From c67663ee62d28c29cc25744e4a08d313beb91edc Mon Sep 17 00:00:00 2001 From: adamperlin Date: Thu, 2 Apr 2026 14:56:25 -0700 Subject: [PATCH 19/20] Fix formatting --- .../tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index 0f6e083a2e6612..fdde93f6db2d5c 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -354,8 +354,8 @@ private void InsertWasmStub(Utf8String name, WasmFunctionBody body) RegisterStubIndexAndSignature(body); } - private long ResolveSymbolRVA(WebcilSection[] sections, SymbolDefinition definition) - { + private long ResolveSymbolRVA(WebcilSection[] sections, SymbolDefinition definition) + { for (int i = 0; i < sections.Length; i++) { WebcilSection section = sections[i]; @@ -366,7 +366,7 @@ private long ResolveSymbolRVA(WebcilSection[] sections, SymbolDefinition definit } return 0; - } + } public const int WebcilSectionAlignment = 16; From 08737e68c9547e51365831773838491219abcb05 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Thu, 2 Apr 2026 15:44:57 -0700 Subject: [PATCH 20/20] Address PR review feedback - peimagelayout.inl: Use DECODER_DISPATCH for IsRelocated() so Webcil images correctly report relocated state instead of always FALSE - peimagelayout.cpp: Gate PAL_LOADMarkSectionAsNotNeeded with !TARGET_WASM to avoid operating on Webcil payload pointers - webcildecoder.cpp: Delegate HasDirectoryEntry(BASERELOC) to HasBaseRelocations() for consistent Reserved0 range validation - WasmObjectWriter.cs: Use SortedDictionary for _baseRelocMap to ensure deterministic reloc block emission order - WasmObjectWriter.cs: Replace redundant pattern match on GetFileRelocationType with direct assignment - WasmObjectWriter.cs: Use explicit cast instead of as-cast for WebcilSection, add Debug.Assert for non-negative offset Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Common/Compiler/ObjectWriter/WasmObjectWriter.cs | 9 +++++---- src/coreclr/utilcode/webcildecoder.cpp | 3 +-- src/coreclr/vm/peimagelayout.cpp | 4 ++-- src/coreclr/vm/peimagelayout.inl | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index fdde93f6db2d5c..1528223e128659 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -764,7 +764,7 @@ private protected override void EmitObjectFile(Stream outputFileStream) } Dictionary> _resolvableRelocations = new(); - Dictionary> _baseRelocMap = new(); + SortedDictionary> _baseRelocMap = new(); // We group webcil relocs into 4kb blocks, similar to PE const uint WebcilRelocPageSize = 0x1000; @@ -789,8 +789,8 @@ private protected override void EmitRelocations(int sectionIndex, List= 0, "Pending base relocation has a negative offset."); // Gather file-level relocations that need to go into the webcil .reloc // section. We collect entries grouped by 4KB page into a map of // (page RVA -> list of (type<<12 | offsetInPage) WORD entries). diff --git a/src/coreclr/utilcode/webcildecoder.cpp b/src/coreclr/utilcode/webcildecoder.cpp index d1cc1887b23a8f..efe6f3769018c6 100644 --- a/src/coreclr/utilcode/webcildecoder.cpp +++ b/src/coreclr/utilcode/webcildecoder.cpp @@ -768,8 +768,7 @@ BOOL WebcilDecoder::HasDirectoryEntry(int entry) const if (entry == IMAGE_DIRECTORY_ENTRY_BASERELOC && HasWebcilHeaders()) { - const WebcilHeader *pHeader = (const WebcilHeader *)m_base; - return pHeader->Reserved0 != 0; + return HasBaseRelocations(); } return FALSE; diff --git a/src/coreclr/vm/peimagelayout.cpp b/src/coreclr/vm/peimagelayout.cpp index 9f8ec1c33744e6..5442b4d17e9cfe 100644 --- a/src/coreclr/vm/peimagelayout.cpp +++ b/src/coreclr/vm/peimagelayout.cpp @@ -433,9 +433,9 @@ void PEImageLayout::ApplyBaseRelocations(bool relocationMustWriteCopy) ThrowLastError(); #endif // __APPLE__ && HOST_ARM64 } -#ifdef TARGET_UNIX +#if defined(TARGET_UNIX) && !defined(TARGET_WASM) PAL_LOADMarkSectionAsNotNeeded((void*)dir); -#endif // TARGET_UNIX +#endif // TARGET_UNIX && !TARGET_WASM if (pFlushRegion != NULL) { diff --git a/src/coreclr/vm/peimagelayout.inl b/src/coreclr/vm/peimagelayout.inl index e919c7babef6d5..bd0d1a8c2b5f28 100644 --- a/src/coreclr/vm/peimagelayout.inl +++ b/src/coreclr/vm/peimagelayout.inl @@ -142,7 +142,7 @@ inline BOOL PEImageLayout::IsMapped() const inline BOOL PEImageLayout::IsRelocated() const { LIMITED_METHOD_DAC_CONTRACT; - PE_OR_WEBCIL(IsRelocated(), FALSE) + DECODER_DISPATCH(IsRelocated()) } inline void PEImageLayout::SetRelocated()