Skip to content
Open
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
34 changes: 34 additions & 0 deletions src/tools/ilasm/src/ILAssembler/CompilationResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;

namespace ILAssembler;

/// <summary>Represents a compiled portable executable image.</summary>
public sealed class CompilationResult
{
private readonly PEBuilder _peBuilder;
private readonly Blob _mvidFixup;

internal CompilationResult(PEBuilder peBuilder, Blob mvidFixup)
{
_peBuilder = peBuilder;
_mvidFixup = mvidFixup;
}

/// <summary>Serializes the compiled image into the specified builder.</summary>
/// <param name="builder">The builder to receive the serialized image.</param>
/// <returns>The content identifier of the serialized image.</returns>
public BlobContentId Serialize(BlobBuilder builder)
{
BlobContentId contentId = _peBuilder.Serialize(builder);
if (!_mvidFixup.IsDefault)
{
new BlobWriter(_mvidFixup).WriteGuid(contentId.Guid);
}

return contentId;
}
}
2 changes: 0 additions & 2 deletions src/tools/ilasm/src/ILAssembler/Diagnostic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ public static class DiagnosticIds
public const string DuplicateMethod = "ILA0030";
public const string MissingExportedTypeImplementation = "ILA0031";
public const string KeyFileError = "ILA0032";
public const string TooManyGenericParameters = "ILA0033";
}

internal static class DiagnosticMessageTemplates
Expand Down Expand Up @@ -88,5 +87,4 @@ internal static class DiagnosticMessageTemplates
public const string ParameterIndexOutOfRange = "Parameter index {0} is out of range";
public const string DuplicateMethod = "Duplicate method definition";
public const string MissingExportedTypeImplementation = "Undefined implementation in ExportedType '{0}' -- ExportedType not emitted";
public const string TooManyGenericParameters = "Generic parameter count {0} exceeds the maximum of {1}";
}
5 changes: 2 additions & 3 deletions src/tools/ilasm/src/ILAssembler/DocumentCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,17 @@
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using Antlr4.Runtime;

namespace ILAssembler;
public sealed class DocumentCompiler
{
public (ImmutableArray<Diagnostic>, PEBuilder?) Compile(SourceText document, Func<string, SourceText> includedDocumentLoader, Func<string, byte[]> resourceLocator, Options options)
public (ImmutableArray<Diagnostic>, CompilationResult?) Compile(SourceText document, Func<string, SourceText> includedDocumentLoader, Func<string, byte[]> resourceLocator, Options options)
{
return Compile([document], includedDocumentLoader, resourceLocator, options);
}

public (ImmutableArray<Diagnostic>, PEBuilder?) Compile(ImmutableArray<SourceText> documents, Func<string, SourceText> includedDocumentLoader, Func<string, byte[]> resourceLocator, Options options)
public (ImmutableArray<Diagnostic>, CompilationResult?) Compile(ImmutableArray<SourceText> documents, Func<string, SourceText> includedDocumentLoader, Func<string, byte[]> resourceLocator, Options options)
{
Dictionary<string, SourceText> loadedDocuments = new();
ImmutableArray<Diagnostic>.Builder diagnostics = ImmutableArray.CreateBuilder<Diagnostic>();
Expand Down
147 changes: 113 additions & 34 deletions src/tools/ilasm/src/ILAssembler/EntityRegistry.cs

Large diffs are not rendered by default.

722 changes: 600 additions & 122 deletions src/tools/ilasm/src/ILAssembler/GrammarVisitor.cs

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions src/tools/ilasm/src/ILAssembler/MetadataExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Reflection;
using System.Reflection.Metadata;

namespace ILAssembler;

Expand All @@ -29,4 +30,9 @@ internal static class MetadataExtensions
public static AssemblyFlags NoPlatform => (AssemblyFlags)0x70;
public static AssemblyFlags ArchitectureMask => (AssemblyFlags)0xF0;
}

extension(ILOpCode)
{
public static ILOpCode Unused => (ILOpCode)0xFE22;
}
}
34 changes: 34 additions & 0 deletions src/tools/ilasm/src/ILAssembler/NameHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,40 @@ namespace ILAssembler
{
internal static class NameHelpers
{
public static string GetPrivateScopeMetadataName(string name, bool isMethod)
{
const int TokenLength = 8;
const string PrivateScopeMarker = "$PST";
int markerIndex = name.Length - PrivateScopeMarker.Length - TokenLength;
if (markerIndex < 0)
{
return name;
}

ReadOnlySpan<char> token = name.AsSpan(markerIndex + PrivateScopeMarker.Length);
if (!name.AsSpan(markerIndex, PrivateScopeMarker.Length).SequenceEqual(PrivateScopeMarker)
|| !token.StartsWith(isMethod ? "06" : "04")
|| !IsHexToken(token))
{
return name;
}

return name.Substring(0, markerIndex);

static bool IsHexToken(ReadOnlySpan<char> token)
{
foreach (char c in token)
{
if (!char.IsAsciiHexDigit(c))
{
return false;
}
}

return true;
}
}

public static (string Namespace, string Name) SplitDottedNameToNamespaceAndName(string dottedName)
{
int lastDotIndex = dottedName.LastIndexOf('.');
Expand Down
5 changes: 5 additions & 0 deletions src/tools/ilasm/src/ILAssembler/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ public sealed class Options
/// </summary>
public Machine? Machine { get; set; }

/// <summary>
/// Produce a DLL image instead of an executable.
/// </summary>
public bool Dll { get; set; }

/// <summary>
/// Create an AppContainer exe or dll.
/// </summary>
Expand Down
106 changes: 52 additions & 54 deletions src/tools/ilasm/src/ILAssembler/VTableExportPEBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,16 +190,15 @@ protected override BlobBuilder SerializeSection(string name, SectionLocation loc
/// </summary>
private void PatchCorHeaderVTableFixups(BlobBuilder textSection, int _)
{
// The COR header is at offset SizeOfImportAddressTable in the text section
// VTableFixups directory is at offset 52 within the COR header (after CodeManagerTable at 44)
// The COR header is at offset SizeOfImportAddressTable in the text section.
bool is32Bit = Header.Machine == Machine.I386 || Header.Machine == 0;
int sizeOfImportAddressTable = (is32Bit || Header.Machine == 0) ? 8 : 0;
int sizeOfImportAddressTable = is32Bit ? 8 : 0;

// COR header offset in text section
int corHeaderOffset = sizeOfImportAddressTable;

// VTableFixups directory entry is at offset 52 within COR header
const int vtableFixupsOffset = 52;
// VTableFixups follows the 8-byte CodeManagerTable directory at offset 40.
const int vtableFixupsOffset = 48;
int patchOffset = corHeaderOffset + vtableFixupsOffset;
Comment on lines +200 to 202

// Find the blob containing this offset and patch it
Expand Down Expand Up @@ -371,8 +370,18 @@ private BlobBuilder SerializeSDataSection(SectionLocation location)

_sdataRva = location.RelativeVirtualAddress;

// Calculate sizes for VTableFixups directory
int vtfDirSize = _vtableFixups.Length * 8; // 8 bytes per IMAGE_COR_VTABLEFIXUP entry
var vtableEntryBuilder =
ImmutableArray.CreateBuilder<VTableFixupSupport.VTableFixupEntry>(_vtableFixups.Length);
foreach (VTableFixupInfo info in _vtableFixups)
{
vtableEntryBuilder.Add(new VTableFixupSupport.VTableFixupEntry(
info.SlotCount,
info.Flags,
info.DataLabel));
}
ImmutableArray<VTableFixupSupport.VTableFixupEntry> vtableEntries =
vtableEntryBuilder.MoveToImmutable();
int vtfDirSize = VTableFixupSupport.CalculateVTableFixupsDirectorySize(vtableEntries);

// Calculate slot data size and build slot offset map
var slotOffsets = new Dictionary<(int EntryIndex, int SlotIndex), int>();
Expand All @@ -391,13 +400,27 @@ private BlobBuilder SerializeSDataSection(SectionLocation location)
slotDataOffset += vtf.SlotCount * slotSize;
}

int slotDataEndOffset = slotDataOffset;
int slotDataEndOffset =
vtfDirSize +
VTableFixupSupport.CalculateVTableSlotDataSize(vtableEntries);

// Calculate export-related sizes
int exportStubsOffset = slotDataEndOffset;
int numExports = _exports.Length;
int exportStubSize = GetExportStubSize();
int exportStubsTotalSize = numExports * exportStubSize;
int baseOrdinal = int.MaxValue;
int maxOrdinal = 0;
foreach (ExportInfo export in _exports)
{
baseOrdinal = Math.Min(baseOrdinal, export.Ordinal);
maxOrdinal = Math.Max(maxOrdinal, export.Ordinal);
}
if (baseOrdinal == int.MaxValue)
{
baseOrdinal = 1;
}
int numFunctions = numExports > 0 ? maxOrdinal - baseOrdinal + 1 : 0;
Comment on lines +412 to +423

// Export directory comes after export stubs
int exportDirOffset = Align(exportStubsOffset + exportStubsTotalSize, 4);
Expand All @@ -410,7 +433,7 @@ private BlobBuilder SerializeSDataSection(SectionLocation location)
// - Export names (null-terminated strings)
// - DLL name (null-terminated string)
int exportAddrTableOffset = exportDirOffset + 40;
int exportNamePtrTableOffset = exportAddrTableOffset + numExports * 4;
int exportNamePtrTableOffset = exportAddrTableOffset + numFunctions * 4;
int exportOrdinalTableOffset = exportNamePtrTableOffset + numExports * 4;

// Calculate name table size
Expand All @@ -428,38 +451,26 @@ private BlobBuilder SerializeSDataSection(SectionLocation location)
// Store total size for COR header patching (only vtfixup directory, not stubs/exports)
_sdataSize = vtfDirSize;
Comment on lines 451 to 452

// Write VTableFixups directory (array of IMAGE_COR_VTABLEFIXUP structures)
var slotDataRvas = new int[_vtableFixups.Length];
int currentSlotDataOffset = vtfDirSize;
foreach (var vtf in _vtableFixups)
for (int i = 0; i < _vtableFixups.Length; i++)
{
int slotDataRva = location.RelativeVirtualAddress + currentSlotDataOffset;
builder.WriteInt32(slotDataRva); // RVA to slot data
builder.WriteUInt16((ushort)vtf.SlotCount); // Count
builder.WriteUInt16(vtf.Flags); // Type/Flags

VTableFixupInfo vtf = _vtableFixups[i];
slotDataRvas[i] = location.RelativeVirtualAddress + currentSlotDataOffset;
bool is64Bit = (vtf.Flags & VTableFixupSupport.COR_VTABLE_64BIT) != 0;
int slotSize = is64Bit ? 8 : 4;
currentSlotDataOffset += vtf.SlotCount * slotSize;
}

// Write slot data (method tokens that get patched by the runtime)
foreach (var vtf in _vtableFixups)
{
bool is64Bit = (vtf.Flags & VTableFixupSupport.COR_VTABLE_64BIT) != 0;

for (int i = 0; i < vtf.SlotCount; i++)
VTableFixupSupport.WriteVTableFixupsDirectory(builder, vtableEntries, slotDataRvas);
VTableFixupSupport.WriteVTableSlotData(
builder,
vtableEntries,
(entryIndex, slotIndex) =>
{
int token = i < vtf.MethodTokens.Length ? vtf.MethodTokens[i] : 0;
if (is64Bit)
{
builder.WriteInt64(token);
}
else
{
builder.WriteInt32(token);
}
}
}
ImmutableArray<int> tokens = _vtableFixups[entryIndex - 1].MethodTokens;
return slotIndex <= tokens.Length ? tokens[slotIndex - 1] : 0;
});

// Write export stubs if we have exports
if (numExports > 0)
Expand Down Expand Up @@ -491,46 +502,33 @@ private BlobBuilder SerializeSDataSection(SectionLocation location)
_exportDirectoryRva = location.RelativeVirtualAddress + builder.Count;

// Write IMAGE_EXPORT_DIRECTORY
int baseOrdinal = int.MaxValue;
int maxOrdinal = 0;
foreach (var export in _exports)
{
if (export.Ordinal < baseOrdinal) baseOrdinal = export.Ordinal;
if (export.Ordinal > maxOrdinal) maxOrdinal = export.Ordinal;
}
if (baseOrdinal == int.MaxValue) baseOrdinal = 1;
int numFunctions = maxOrdinal - baseOrdinal + 1;

int exportDirStart = builder.Count;

builder.WriteUInt32(0); // Characteristics
builder.WriteUInt32(0); // TimeDateStamp (filled later or 0)
builder.WriteUInt16(0); // MajorVersion
builder.WriteUInt16(0); // MinorVersion
builder.WriteInt32(location.RelativeVirtualAddress + exportDirStart + 40 +
numExports * 4 + numExports * 4 + numExports * 2 + nameTableSize); // Name RVA (DLL name)
builder.WriteInt32(location.RelativeVirtualAddress + dllNameOffset); // Name RVA (DLL name)
builder.WriteInt32(baseOrdinal); // Base
builder.WriteInt32(numExports); // NumberOfFunctions
builder.WriteInt32(numFunctions); // NumberOfFunctions
builder.WriteInt32(numExports); // NumberOfNames
builder.WriteInt32(location.RelativeVirtualAddress + exportDirStart + 40); // AddressOfFunctions
builder.WriteInt32(location.RelativeVirtualAddress + exportDirStart + 40 + numExports * 4); // AddressOfNames
builder.WriteInt32(location.RelativeVirtualAddress + exportDirStart + 40 + numExports * 4 * 2); // AddressOfNameOrdinals
builder.WriteInt32(location.RelativeVirtualAddress + exportDirStart + 40 + numFunctions * 4); // AddressOfNames
builder.WriteInt32(location.RelativeVirtualAddress + exportDirStart + 40 + numFunctions * 4 + numExports * 4); // AddressOfNameOrdinals

// Sort exports by name for binary search
var sortedExports = _exports.AsSpan().ToArray();
Array.Sort(sortedExports, (a, b) => string.CompareOrdinal(a.Name, b.Name));

// Write Export Address Table (RVAs to stubs)
var exportsArray = _exports.AsSpan().ToArray();
foreach (var export in sortedExports)
for (int ordinal = baseOrdinal; ordinal <= maxOrdinal; ordinal++)
{
int stubIndex = Array.FindIndex(exportsArray, e => e.Ordinal == export.Ordinal);
builder.WriteInt32(exportStubRvas[stubIndex]);
int stubIndex = Array.FindIndex(exportsArray, export => export.Ordinal == ordinal);
builder.WriteInt32(stubIndex >= 0 ? exportStubRvas[stubIndex] : 0);
}

// Write Export Name Pointer Table (RVAs to names)
int nameOffset = location.RelativeVirtualAddress + exportDirStart + 40 +
numExports * 4 + numExports * 4 + numExports * 2;
int nameOffset = location.RelativeVirtualAddress + exportNamesOffset;
foreach (var export in sortedExports)
{
builder.WriteInt32(nameOffset);
Expand Down
14 changes: 10 additions & 4 deletions src/tools/ilasm/src/ILAssembler/gen/CIL.g4
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ VOID: 'void';
ENUM: 'enum';
CUSTOM: 'custom';
FIXED: 'fixed';
SYSSTRING: 'systring';
SYSSTRING: 'sysstring';
ARRAY: 'array';
VARIANT: 'variant';
CURRENCY: 'currency';
Expand Down Expand Up @@ -123,6 +123,7 @@ MRESOURCE: '.mresource';
// For example, "ldc.r8" must be recognized as INSTR_R token, not as DOTTEDNAME
INSTR_NONE:
'nop'
| 'unused'
| 'break'
| 'ldarg.0'
| 'ldarg.1'
Expand Down Expand Up @@ -409,7 +410,7 @@ id:
| INSTANCE
| SQSTRING;
dottedName: DOTTEDNAME | ((dottedNamePart '.')* dottedNamePart) | SQSTRING;
dottedNamePart: ID | VALUE | INSTANCE;
dottedNamePart: ID | VALUE | INSTANCE | SQSTRING | DOTTEDNAME | 'volatile';
compQstring: (QSTRING PLUS)* QSTRING;


Expand Down Expand Up @@ -696,6 +697,7 @@ instr:
| instr_string 'bytearray' '(' bytes ')'
| instr_sig callConv type sigArgs
| instr_tok ownerType /* ownerType ::= memberRef | typeSpec */
| instr_tok int32
| instr_switch '(' labels ')'
| instr_switch '()';

Expand Down Expand Up @@ -788,6 +790,10 @@ nativeTypeElement:
| marshalType=SAFEARRAY variantType ',' compQstring
| marshalType=INT
| marshalType=UINT
| 'unsigned' unsignedMarshalType=INT8
| 'unsigned' unsignedMarshalType=INT16
| 'unsigned' unsignedMarshalType=INT32_
| 'unsigned' unsignedMarshalType=INT64_
| 'nested' marshalType=STRUCT
| marshalType=BYVALSTR
| ANSI marshalType=BSTR
Expand Down Expand Up @@ -916,6 +922,7 @@ secDecl:
| PERMISSION secAction typeSpec '=' '{' customBlobDescr '}'
| PERMISSION secAction typeSpec
| PERMISSIONSET secAction '=' 'bytearray'? '(' bytes ')'
| PERMISSIONSET secAction 'bytearray' '(' bytes ')'
| PERMISSIONSET secAction compQstring
| PERMISSIONSET secAction '=' '{' secAttrSetBlob '}';

Expand Down Expand Up @@ -1380,7 +1387,7 @@ customAttrDecl:

/* Assembly References */
asmOrRefDecl:
'.publicKey' '=' '(' bytes ')'
('.publickey' | '.publicKey') '=' '(' bytes ')'
| '.ver' intOrWildcard ':' intOrWildcard ':' intOrWildcard ':' intOrWildcard
| '.locale' compQstring
| '.locale' '=' '(' bytes ')'
Expand Down Expand Up @@ -1438,4 +1445,3 @@ manifestResDecl:
| '.assembly' 'extern' dottedName
| customAttrDecl
| compControl;

Loading
Loading