Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
14 changes: 14 additions & 0 deletions docs/design/coreclr/botr/readytorun-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Revisions:
* 1.1 - [Jan Kotas](https://github.com/jkotas) - 2015
* 3.1 - [Tomas Rylek](https://github.com/trylek) - 2019
* 4.1 - [Tomas Rylek](https://github.com/trylek) - 2020
* 5.3 - [Tomas Rylek](https://github.com/trylek) - 2021

# Introduction

Expand Down Expand Up @@ -161,6 +162,8 @@ The following section types are defined and described later in this document:
| InliningInfo2 | 114 | Image (added in V4.1)
| ComponentAssemblies | 115 | Image (added in V4.1)
| OwnerCompositeExecutable | 116 | Image (added in V4.1)
| PgoInstrumentationData | 117 | Image (added in V5.2)
| ManifestAssemblyMvids | 118 | Image (added in V5.3)

## ReadyToRunSectionType.CompilerIdentifier

Expand Down Expand Up @@ -540,6 +543,17 @@ the `OwnerCompositeExecutable` section that contains a UTF-8 string encoding the
composite R2R executable this MSIL belongs to with extension (without path). Runtime uses this
information to locate the composite R2R executable with the compiled native code when loading the MSIL.

## ReadyToRunSectionType.PgoInstrumentationData (v5.2+)

**TODO**: document PGO instrumentation data

## ReadyToRunSectionType.ManifestAssemblyMvids (v5.3+)

This section is a binary array of 16-byte MVID records, one for each assembly in the manifest metadata.
Number of assemblies stored in the manifest metadata is equal to the number of MVID records in the array.
MVID records are used at runtime to verify that the assemblies loaded match those referenced by the
manifest metadata representing the versioning bubble.

# Native Format

Native format is set of encoding patterns that allow persisting type system data in a binary format that is
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/inc/readytorun.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

// Keep these in sync with src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs
#define READYTORUN_MAJOR_VERSION 0x0005
#define READYTORUN_MINOR_VERSION 0x0002
#define READYTORUN_MINOR_VERSION 0x0003

#define MINIMUM_READYTORUN_MAJOR_VERSION 0x003

Expand Down Expand Up @@ -80,6 +80,7 @@ enum class ReadyToRunSectionType : uint32_t
ComponentAssemblies = 115, // Added in V4.1
OwnerCompositeExecutable = 116, // Added in V4.1
PgoInstrumentationData = 117, // Added in 5.2
ManifestAssemblyMvids = 118, // Added in V5.3

// If you add a new section consider whether it is a breaking or non-breaking change.
// Usually it is non-breaking, but if it is preferable to have older runtimes fail
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal struct ReadyToRunHeaderConstants
public const uint Signature = 0x00525452; // 'RTR'

public const ushort CurrentMajorVersion = 5;
public const ushort CurrentMinorVersion = 2;
public const ushort CurrentMinorVersion = 3;
}

#pragma warning disable 0169
Expand Down Expand Up @@ -66,6 +66,7 @@ public enum ReadyToRunSectionType
ComponentAssemblies = 115, // Added in 4.1
OwnerCompositeExecutable = 116, // Added in 4.1
PgoInstrumentationData = 117, // Added in 5.2
ManifestAssemblyMvids = 118, // Added in 5.3

//
// CoreRT ReadyToRun sections
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

using Internal.Text;
using Internal.TypeSystem.Ecma;

using Debug = System.Diagnostics.Debug;

namespace ILCompiler.DependencyAnalysis.ReadyToRun
{
public class ManifestAssemblyMvidHeaderNode : ObjectNode, ISymbolDefinitionNode
{
private ManifestMetadataTableNode _manifestNode;

public ManifestAssemblyMvidHeaderNode(ManifestMetadataTableNode manifestNode)
{
_manifestNode = manifestNode;
}

public override ObjectNodeSection Section => ObjectNodeSection.TextSection;

public override bool IsShareable => false;

protected internal override int Phase => (int)ObjectNodePhase.Ordered;

public override int ClassCode => 735231445;

public override bool StaticDependenciesAreComputed => true;

public int Offset => 0;

public int Size => _manifestNode.ManifestAssemblyMvidTableSize;

public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
sb.Append(nameMangler.CompilationUnitPrefix);
sb.Append("__ManifestAssemblyMvids");
}

protected override string GetName(NodeFactory nodeFactory)
{
Utf8StringBuilder sb = new Utf8StringBuilder();
AppendMangledName(nodeFactory.NameMangler, sb);
return sb.ToString();
}

public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
if (relocsOnly)
{
return new ObjectData(Array.Empty<byte>(), null, 1, null);
}

byte[] manifestAssemblyMvidTable = _manifestNode.GetManifestAssemblyMvidTableData();
return new ObjectData(manifestAssemblyMvidTable, Array.Empty<Relocation>(), alignment: 0, new ISymbolDefinitionNode[] { this });
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ public class ManifestMetadataTableNode : HeaderTableNode
/// </summary>
private readonly Dictionary<int, AssemblyName> _moduleIdToAssemblyNameMap;

/// <summary>
/// MVID's of the assemblies included in manifest metadata to be emitted as the
/// ManifestAssemblyMvid R2R header table used by the runtime to check loaded assemblies
/// and fail fast in case of mismatch.
/// </summary>
private readonly List<Guid> _manifestAssemblyMvids;

/// <summary>
/// Registered signature emitters.
/// </summary>
Expand Down Expand Up @@ -71,6 +78,7 @@ public ManifestMetadataTableNode(NodeFactory nodeFactory)
{
_assemblyRefToModuleIdMap = new Dictionary<string, int>();
_moduleIdToAssemblyNameMap = new Dictionary<int, AssemblyName>();
_manifestAssemblyMvids = new List<Guid>();
_signatureEmitters = new List<ISignatureEmitter>();
_nodeFactory = nodeFactory;
_nextModuleId = 1;
Expand Down Expand Up @@ -150,6 +158,7 @@ private int ModuleToIndexInternal(EcmaModule module)
Debug.Assert(_nodeFactory.CompilationModuleGroup.VersionsWithModule(module));

_moduleIdToAssemblyNameMap.Add(assemblyRefIndex, assemblyName);
_manifestAssemblyMvids.Add(module.MetadataReader.GetGuid(module.MetadataReader.GetModuleDefinition().Mvid));
}
return assemblyRefIndex;
}
Expand Down Expand Up @@ -241,5 +250,24 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
alignment: 1,
definedSymbols: new ISymbolDefinitionNode[] { this });
}

private const int GuidByteSize = 16;

public int ManifestAssemblyMvidTableSize => GuidByteSize * _manifestAssemblyMvids.Count;

internal byte[] GetManifestAssemblyMvidTableData()
{
byte[] manifestAssemblyMvidTable = new byte[ManifestAssemblyMvidTableSize];
for (int i = 0; i < _manifestAssemblyMvids.Count; i++)
{
Array.Copy(
sourceArray: _manifestAssemblyMvids[i].ToByteArray(),
sourceIndex: 0,
destinationArray: manifestAssemblyMvidTable,
destinationIndex: GuidByteSize * i,
length: GuidByteSize);
}
return manifestAssemblyMvidTable;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,9 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase<NodeFactory> graph)
Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestMetadata, ManifestMetadataTable, ManifestMetadataTable);
Resolver.SetModuleIndexLookup(ManifestMetadataTable.ModuleToIndex);

ManifestAssemblyMvidHeaderNode mvidTableNode = new ManifestAssemblyMvidHeaderNode(ManifestMetadataTable);
Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestAssemblyMvids, mvidTableNode, mvidTableNode);

AssemblyTableNode assemblyTable = null;

if (CompilationModuleGroup.IsCompositeBuildMode)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<AssemblyName>ILCompiler.ReadyToRun</AssemblyName>
Expand Down Expand Up @@ -103,6 +103,7 @@
<Compile Include="..\..\Common\TypeSystem\Interop\InteropTypes.cs" Link="Interop\InteropTypes.cs" />
<Compile Include="Compiler\AssemblyExtensions.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\DeferredTillPhaseNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\ManifestAssemblyMvidHeaderNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\DelayLoadMethodCallThunkNodeRange.cs" />
<Compile Include="Compiler\CallChainProfile.cs" />
<Compile Include="ObjectWriter\MapFileBuilder.cs" />
Expand Down
23 changes: 22 additions & 1 deletion src/coreclr/tools/r2rdump/TextDumper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ namespace R2RDump
{
class TextDumper : Dumper
{
private const int GuidByteSize = 16;

public TextDumper(ReadyToRunReader r2r, TextWriter writer, Disassembler disassembler, DumpOptions options)
: base(r2r, writer, disassembler, options)
{
Expand Down Expand Up @@ -88,7 +90,14 @@ internal override void DumpHeader(bool dumpSections)
int assemblyIndex = 0;
foreach (string assemblyName in _r2r.ManifestReferenceAssemblies.OrderBy(kvp => kvp.Value).Select(kvp => kvp.Key))
{
WriteDivider($@"Component Assembly [{assemblyIndex}]: {assemblyName}");
string dividerName = $@"Component Assembly [{assemblyIndex}]: {assemblyName}";
if (_r2r.ReadyToRunHeader.Sections.TryGetValue(ReadyToRunSectionType.ManifestAssemblyMvids, out ReadyToRunSection mvidSection))
{
int mvidOffset = _r2r.GetOffset(mvidSection.RelativeVirtualAddress) + GuidByteSize * assemblyIndex;
Guid mvid = new Guid(new ReadOnlySpan<byte>(_r2r.Image, mvidOffset, GuidByteSize));
dividerName += $@" - MVID {mvid}";
}
WriteDivider(dividerName);
ReadyToRunCoreHeader assemblyHeader = _r2r.ReadyToRunAssemblyHeaders[assemblyIndex];
foreach (ReadyToRunSection section in NormalizedSections(assemblyHeader))
{
Expand Down Expand Up @@ -500,6 +509,18 @@ internal override void DumpSectionContents(ReadyToRunSection section)
string ownerCompositeExecutable = Encoding.UTF8.GetString(_r2r.Image, oceOffset, section.Size - 1); // exclude the zero terminator
_writer.WriteLine("Composite executable: {0}", ownerCompositeExecutable.ToEscapedString());
break;
case ReadyToRunSectionType.ManifestAssemblyMvids:
int mvidOffset = _r2r.GetOffset(section.RelativeVirtualAddress);
int mvidCount = section.Size / GuidByteSize;
for (int mvidIndex = 0; mvidIndex < mvidCount; mvidIndex++)
{
Guid mvid = new Guid(new Span<byte>(_r2r.Image, mvidOffset + GuidByteSize * mvidIndex, GuidByteSize));
_writer.WriteLine("MVID[{0}] = {1}", mvidIndex, mvid);
}
break;
default:
_writer.WriteLine("Unsupported section type {0}", section.Type);
break;
}
}

Expand Down
7 changes: 7 additions & 0 deletions src/coreclr/vm/appdomain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3043,13 +3043,15 @@ DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity,

// Find the list lock entry
FileLoadLock * fileLock = (FileLoadLock *)lock->FindFileLock(pFile);
bool registerNewAssembly = false;
if (fileLock == NULL)
{
// Check again in case we were racing
result = FindAssembly(pFile, FindAssemblyOptions_IncludeFailedToLoad);
if (result == NULL)
{
// We are the first one in - create the DomainAssembly
registerNewAssembly = true;
fileLock = FileLoadLock::Create(lock, pFile, pDomainAssembly);
pDomainAssembly.SuppressRelease();
#ifndef CROSSGEN_COMPILE
Expand Down Expand Up @@ -3082,6 +3084,11 @@ DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity,
{
result->EnsureLoadLevel(targetLevel);
}

if (registerNewAssembly)
{
pFile->GetAssemblyLoadContext()->AddLoadedAssembly(pDomainAssembly->GetLoadedAssembly());
}
}
else
result->EnsureLoadLevel(targetLevel);
Expand Down
22 changes: 21 additions & 1 deletion src/coreclr/vm/assemblyloadcontext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,26 @@ NativeImage *AssemblyLoadContext::LoadNativeImage(Module *componentModule, LPCUT
AssemblyLoadContext *loadContext = componentModule->GetFile()->GetAssemblyLoadContext();
PTR_LoaderAllocator moduleLoaderAllocator = componentModule->GetLoaderAllocator();

return NativeImage::Open(componentModule, nativeImageName, loadContext, moduleLoaderAllocator);
NativeImage *nativeImage = NativeImage::Open(componentModule, nativeImageName, loadContext, moduleLoaderAllocator);
m_nativeImages.Append(nativeImage);

for (COUNT_T assemblyIndex = 0; assemblyIndex < m_loadedAssemblies.GetCount(); assemblyIndex++)
{
nativeImage->CheckAssemblyMvid(m_loadedAssemblies[assemblyIndex]);
}

return nativeImage;
}
#endif

#ifndef DACCESS_COMPILE
void AssemblyLoadContext::AddLoadedAssembly(Assembly *loadedAssembly)
{
BaseDomain::LoadLockHolder lock(AppDomain::GetCurrentDomain());
m_loadedAssemblies.Append(loadedAssembly);
for (COUNT_T nativeImageIndex = 0; nativeImageIndex < m_nativeImages.GetCount(); nativeImageIndex++)
{
m_nativeImages[nativeImageIndex]->CheckAssemblyMvid(loadedAssembly);
}
}
#endif
4 changes: 4 additions & 0 deletions src/coreclr/vm/assemblyloadcontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

class NativeImage;
class Module;
class Assembly;

//
// Unmanaged counter-part of System.Runtime.Loader.AssemblyLoadContext
Expand All @@ -23,6 +24,8 @@ class AssemblyLoadContext : public IUnknownCommon<ICLRPrivBinder, IID_ICLRPrivBi

NativeImage *LoadNativeImage(Module *componentModule, LPCUTF8 nativeImageName);

void AddLoadedAssembly(Assembly *loadedAssembly);

INT_PTR GetManagedAssemblyLoadContext()
{
return m_ptrManagedAssemblyLoadContext;
Expand All @@ -40,6 +43,7 @@ class AssemblyLoadContext : public IUnknownCommon<ICLRPrivBinder, IID_ICLRPrivBi

private:
SArray<NativeImage *> m_nativeImages;
SArray<Assembly *> m_loadedAssemblies;
};

#endif
44 changes: 44 additions & 0 deletions src/coreclr/vm/nativeimage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ void NativeImage::Initialize(READYTORUN_HEADER *pHeader, LoaderAllocator *pLoade

m_pReadyToRunInfo = new ReadyToRunInfo(/*pModule*/ NULL, pLoaderAllocator, m_pImageLayout, pHeader, /*compositeImage*/ NULL, pamTracker);
m_pComponentAssemblies = m_pReadyToRunInfo->FindSection(ReadyToRunSectionType::ComponentAssemblies);
m_pComponentAssemblyMvids = m_pReadyToRunInfo->FindSection(ReadyToRunSectionType::ManifestAssemblyMvids);
m_componentAssemblyCount = m_pComponentAssemblies->Size / sizeof(READYTORUN_COMPONENT_ASSEMBLIES_ENTRY);

// Check if the current module's image has native manifest metadata, otherwise the current->GetNativeAssemblyImport() asserts.
Expand Down Expand Up @@ -251,6 +252,49 @@ PTR_READYTORUN_CORE_HEADER NativeImage::GetComponentAssemblyHeader(LPCUTF8 simpl
}
#endif

#ifndef DACCESS_COMPILE
void NativeImage::CheckAssemblyMvid(Assembly *assembly) const
{
STANDARD_VM_CONTRACT;
if (m_pComponentAssemblyMvids == NULL)
{
return;
}

const AssemblyNameIndex *assemblyNameIndex = m_assemblySimpleNameToIndexMap.LookupPtr(assembly->GetSimpleName());
if (assemblyNameIndex == NULL)
{
return;
}

GUID assemblyMvid;
assembly->GetManifestImport()->GetScopeProps(NULL, &assemblyMvid);

const byte *pImageBase = (const BYTE *)m_pImageLayout->GetBase();
const GUID *componentMvid = (const GUID *)&pImageBase[m_pComponentAssemblyMvids->VirtualAddress] + assemblyNameIndex->Index;
if (IsEqualGUID(*componentMvid, assemblyMvid))
{
return;
}

static const size_t MVID_TEXT_LENGTH = 39;
WCHAR assemblyMvidText[MVID_TEXT_LENGTH];
StringFromGUID2(assemblyMvid, assemblyMvidText, MVID_TEXT_LENGTH);

WCHAR componentMvidText[MVID_TEXT_LENGTH];
StringFromGUID2(*componentMvid, componentMvidText, MVID_TEXT_LENGTH);

SString message;
message.Printf(W("MVID mismatch between loaded assembly '%s' (MVID = %s) and an assembly with the same simple name embedded in the native image '%s' (MVID = %s)"),
assembly->GetSimpleName(),
assemblyMvidText,
SString(SString::Utf8, GetFileName()).GetUnicode(),
componentMvidText);

EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_FAILFAST, message.GetUnicode());
}
#endif

#ifndef DACCESS_COMPILE
IMDInternalImport *NativeImage::LoadManifestMetadata()
{
Expand Down
Loading