Skip to content

Commit 3f28d48

Browse files
authored
Add runtime MVID checks for composite images (#49668)
* Crossgen2 implementation of new MVID R2R section * Runtime checks for assembly MVIDs against native images * Basic R2RDump support for dumping the manifest assembly MVID table. * R2R minor version increment; format spec update Thanks Tomas
1 parent 1e233e7 commit 3f28d48

File tree

13 files changed

+211
-7
lines changed

13 files changed

+211
-7
lines changed

docs/design/coreclr/botr/readytorun-format.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Revisions:
55
* 1.1 - [Jan Kotas](https://github.com/jkotas) - 2015
66
* 3.1 - [Tomas Rylek](https://github.com/trylek) - 2019
77
* 4.1 - [Tomas Rylek](https://github.com/trylek) - 2020
8+
* 5.3 - [Tomas Rylek](https://github.com/trylek) - 2021
89

910
# Introduction
1011

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

165168
## ReadyToRunSectionType.CompilerIdentifier
166169

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

546+
## ReadyToRunSectionType.PgoInstrumentationData (v5.2+)
547+
548+
**TODO**: document PGO instrumentation data
549+
550+
## ReadyToRunSectionType.ManifestAssemblyMvids (v5.3+)
551+
552+
This section is a binary array of 16-byte MVID records, one for each assembly in the manifest metadata.
553+
Number of assemblies stored in the manifest metadata is equal to the number of MVID records in the array.
554+
MVID records are used at runtime to verify that the assemblies loaded match those referenced by the
555+
manifest metadata representing the versioning bubble.
556+
543557
# Native Format
544558

545559
Native format is set of encoding patterns that allow persisting type system data in a binary format that is

src/coreclr/inc/readytorun.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
// Keep these in sync with src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs
1818
#define READYTORUN_MAJOR_VERSION 0x0005
19-
#define READYTORUN_MINOR_VERSION 0x0002
19+
#define READYTORUN_MINOR_VERSION 0x0003
2020

2121
#define MINIMUM_READYTORUN_MAJOR_VERSION 0x003
2222

@@ -79,7 +79,8 @@ enum class ReadyToRunSectionType : uint32_t
7979
InliningInfo2 = 114, // Added in V4.1
8080
ComponentAssemblies = 115, // Added in V4.1
8181
OwnerCompositeExecutable = 116, // Added in V4.1
82-
PgoInstrumentationData = 117, // Added in 5.2
82+
PgoInstrumentationData = 117, // Added in V5.2
83+
ManifestAssemblyMvids = 118, // Added in V5.3
8384

8485
// If you add a new section consider whether it is a breaking or non-breaking change.
8586
// Usually it is non-breaking, but if it is preferable to have older runtimes fail

src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ internal struct ReadyToRunHeaderConstants
1515
public const uint Signature = 0x00525452; // 'RTR'
1616

1717
public const ushort CurrentMajorVersion = 5;
18-
public const ushort CurrentMinorVersion = 2;
18+
public const ushort CurrentMinorVersion = 3;
1919
}
2020

2121
#pragma warning disable 0169
@@ -66,6 +66,7 @@ public enum ReadyToRunSectionType
6666
ComponentAssemblies = 115, // Added in 4.1
6767
OwnerCompositeExecutable = 116, // Added in 4.1
6868
PgoInstrumentationData = 117, // Added in 5.2
69+
ManifestAssemblyMvids = 118, // Added in 5.3
6970

7071
//
7172
// CoreRT ReadyToRun sections
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Reflection.Metadata;
6+
using System.Reflection.PortableExecutable;
7+
8+
using Internal.Text;
9+
using Internal.TypeSystem.Ecma;
10+
11+
using Debug = System.Diagnostics.Debug;
12+
13+
namespace ILCompiler.DependencyAnalysis.ReadyToRun
14+
{
15+
public class ManifestAssemblyMvidHeaderNode : ObjectNode, ISymbolDefinitionNode
16+
{
17+
private ManifestMetadataTableNode _manifestNode;
18+
19+
public ManifestAssemblyMvidHeaderNode(ManifestMetadataTableNode manifestNode)
20+
{
21+
_manifestNode = manifestNode;
22+
}
23+
24+
public override ObjectNodeSection Section => ObjectNodeSection.TextSection;
25+
26+
public override bool IsShareable => false;
27+
28+
protected internal override int Phase => (int)ObjectNodePhase.Ordered;
29+
30+
public override int ClassCode => 735231445;
31+
32+
public override bool StaticDependenciesAreComputed => true;
33+
34+
public int Offset => 0;
35+
36+
public int Size => _manifestNode.ManifestAssemblyMvidTableSize;
37+
38+
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
39+
{
40+
sb.Append(nameMangler.CompilationUnitPrefix);
41+
sb.Append("__ManifestAssemblyMvids");
42+
}
43+
44+
protected override string GetName(NodeFactory nodeFactory)
45+
{
46+
Utf8StringBuilder sb = new Utf8StringBuilder();
47+
AppendMangledName(nodeFactory.NameMangler, sb);
48+
return sb.ToString();
49+
}
50+
51+
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
52+
{
53+
if (relocsOnly)
54+
{
55+
return new ObjectData(Array.Empty<byte>(), null, 0, null);
56+
}
57+
58+
byte[] manifestAssemblyMvidTable = _manifestNode.GetManifestAssemblyMvidTableData();
59+
return new ObjectData(manifestAssemblyMvidTable, Array.Empty<Relocation>(), alignment: 0, new ISymbolDefinitionNode[] { this });
60+
}
61+
}
62+
}

src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Licensed to the .NET Foundation under one or more agreements.
1+
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
@@ -41,6 +41,13 @@ public class ManifestMetadataTableNode : HeaderTableNode
4141
/// </summary>
4242
private readonly Dictionary<int, AssemblyName> _moduleIdToAssemblyNameMap;
4343

44+
/// <summary>
45+
/// MVIDs of the assemblies included in manifest metadata to be emitted as the
46+
/// ManifestAssemblyMvid R2R header table used by the runtime to check loaded assemblies
47+
/// and fail fast in case of mismatch.
48+
/// </summary>
49+
private readonly List<Guid> _manifestAssemblyMvids;
50+
4451
/// <summary>
4552
/// Registered signature emitters.
4653
/// </summary>
@@ -71,6 +78,7 @@ public ManifestMetadataTableNode(NodeFactory nodeFactory)
7178
{
7279
_assemblyRefToModuleIdMap = new Dictionary<string, int>();
7380
_moduleIdToAssemblyNameMap = new Dictionary<int, AssemblyName>();
81+
_manifestAssemblyMvids = new List<Guid>();
7482
_signatureEmitters = new List<ISignatureEmitter>();
7583
_nodeFactory = nodeFactory;
7684
_nextModuleId = 1;
@@ -150,6 +158,7 @@ private int ModuleToIndexInternal(EcmaModule module)
150158
Debug.Assert(_nodeFactory.CompilationModuleGroup.VersionsWithModule(module));
151159

152160
_moduleIdToAssemblyNameMap.Add(assemblyRefIndex, assemblyName);
161+
_manifestAssemblyMvids.Add(module.MetadataReader.GetGuid(module.MetadataReader.GetModuleDefinition().Mvid));
153162
}
154163
return assemblyRefIndex;
155164
}
@@ -241,5 +250,19 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
241250
alignment: 1,
242251
definedSymbols: new ISymbolDefinitionNode[] { this });
243252
}
253+
254+
private const int GuidByteSize = 16;
255+
256+
public int ManifestAssemblyMvidTableSize => GuidByteSize * _manifestAssemblyMvids.Count;
257+
258+
internal byte[] GetManifestAssemblyMvidTableData()
259+
{
260+
byte[] manifestAssemblyMvidTable = new byte[ManifestAssemblyMvidTableSize];
261+
for (int i = 0; i < _manifestAssemblyMvids.Count; i++)
262+
{
263+
_manifestAssemblyMvids[i].TryWriteBytes(new Span<byte>(manifestAssemblyMvidTable, GuidByteSize * i, GuidByteSize));
264+
}
265+
return manifestAssemblyMvidTable;
266+
}
244267
}
245268
}

src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,9 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase<NodeFactory> graph)
570570
Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestMetadata, ManifestMetadataTable, ManifestMetadataTable);
571571
Resolver.SetModuleIndexLookup(ManifestMetadataTable.ModuleToIndex);
572572

573+
ManifestAssemblyMvidHeaderNode mvidTableNode = new ManifestAssemblyMvidHeaderNode(ManifestMetadataTable);
574+
Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestAssemblyMvids, mvidTableNode, mvidTableNode);
575+
573576
AssemblyTableNode assemblyTable = null;
574577

575578
if (CompilationModuleGroup.IsCompositeBuildMode)

src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
<Compile Include="..\..\Common\TypeSystem\Interop\InteropTypes.cs" Link="Interop\InteropTypes.cs" />
104104
<Compile Include="Compiler\AssemblyExtensions.cs" />
105105
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\DeferredTillPhaseNode.cs" />
106+
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\ManifestAssemblyMvidHeaderNode.cs" />
106107
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\DelayLoadMethodCallThunkNodeRange.cs" />
107108
<Compile Include="Compiler\CallChainProfile.cs" />
108109
<Compile Include="ObjectWriter\MapFileBuilder.cs" />

src/coreclr/tools/r2rdump/TextDumper.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ namespace R2RDump
1818
{
1919
class TextDumper : Dumper
2020
{
21+
private const int GuidByteSize = 16;
22+
2123
public TextDumper(ReadyToRunReader r2r, TextWriter writer, Disassembler disassembler, DumpOptions options)
2224
: base(r2r, writer, disassembler, options)
2325
{
@@ -88,7 +90,14 @@ internal override void DumpHeader(bool dumpSections)
8890
int assemblyIndex = 0;
8991
foreach (string assemblyName in _r2r.ManifestReferenceAssemblies.OrderBy(kvp => kvp.Value).Select(kvp => kvp.Key))
9092
{
91-
WriteDivider($@"Component Assembly [{assemblyIndex}]: {assemblyName}");
93+
string dividerName = $@"Component Assembly [{assemblyIndex}]: {assemblyName}";
94+
if (_r2r.ReadyToRunHeader.Sections.TryGetValue(ReadyToRunSectionType.ManifestAssemblyMvids, out ReadyToRunSection mvidSection))
95+
{
96+
int mvidOffset = _r2r.GetOffset(mvidSection.RelativeVirtualAddress) + GuidByteSize * assemblyIndex;
97+
Guid mvid = new Guid(new ReadOnlySpan<byte>(_r2r.Image, mvidOffset, GuidByteSize));
98+
dividerName += $@" - MVID {mvid:b}";
99+
}
100+
WriteDivider(dividerName);
92101
ReadyToRunCoreHeader assemblyHeader = _r2r.ReadyToRunAssemblyHeaders[assemblyIndex];
93102
foreach (ReadyToRunSection section in NormalizedSections(assemblyHeader))
94103
{
@@ -500,6 +509,18 @@ internal override void DumpSectionContents(ReadyToRunSection section)
500509
string ownerCompositeExecutable = Encoding.UTF8.GetString(_r2r.Image, oceOffset, section.Size - 1); // exclude the zero terminator
501510
_writer.WriteLine("Composite executable: {0}", ownerCompositeExecutable.ToEscapedString());
502511
break;
512+
case ReadyToRunSectionType.ManifestAssemblyMvids:
513+
int mvidOffset = _r2r.GetOffset(section.RelativeVirtualAddress);
514+
int mvidCount = section.Size / GuidByteSize;
515+
for (int mvidIndex = 0; mvidIndex < mvidCount; mvidIndex++)
516+
{
517+
Guid mvid = new Guid(new Span<byte>(_r2r.Image, mvidOffset + GuidByteSize * mvidIndex, GuidByteSize));
518+
_writer.WriteLine("MVID[{0}] = {1:b}", mvidIndex, mvid);
519+
}
520+
break;
521+
default:
522+
_writer.WriteLine("Unsupported section type {0}", section.Type);
523+
break;
503524
}
504525
}
505526

src/coreclr/vm/appdomain.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3043,13 +3043,15 @@ DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity,
30433043

30443044
// Find the list lock entry
30453045
FileLoadLock * fileLock = (FileLoadLock *)lock->FindFileLock(pFile);
3046+
bool registerNewAssembly = false;
30463047
if (fileLock == NULL)
30473048
{
30483049
// Check again in case we were racing
30493050
result = FindAssembly(pFile, FindAssemblyOptions_IncludeFailedToLoad);
30503051
if (result == NULL)
30513052
{
30523053
// We are the first one in - create the DomainAssembly
3054+
registerNewAssembly = true;
30533055
fileLock = FileLoadLock::Create(lock, pFile, pDomainAssembly);
30543056
pDomainAssembly.SuppressRelease();
30553057
#ifndef CROSSGEN_COMPILE
@@ -3082,6 +3084,11 @@ DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity,
30823084
{
30833085
result->EnsureLoadLevel(targetLevel);
30843086
}
3087+
3088+
if (registerNewAssembly)
3089+
{
3090+
pFile->GetAssemblyLoadContext()->AddLoadedAssembly(pDomainAssembly->GetCurrentAssembly());
3091+
}
30853092
}
30863093
else
30873094
result->EnsureLoadLevel(targetLevel);

src/coreclr/vm/assemblyloadcontext.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,26 @@ NativeImage *AssemblyLoadContext::LoadNativeImage(Module *componentModule, LPCUT
2424
AssemblyLoadContext *loadContext = componentModule->GetFile()->GetAssemblyLoadContext();
2525
PTR_LoaderAllocator moduleLoaderAllocator = componentModule->GetLoaderAllocator();
2626

27-
return NativeImage::Open(componentModule, nativeImageName, loadContext, moduleLoaderAllocator);
27+
NativeImage *nativeImage = NativeImage::Open(componentModule, nativeImageName, loadContext, moduleLoaderAllocator);
28+
m_nativeImages.Append(nativeImage);
29+
30+
for (COUNT_T assemblyIndex = 0; assemblyIndex < m_loadedAssemblies.GetCount(); assemblyIndex++)
31+
{
32+
nativeImage->CheckAssemblyMvid(m_loadedAssemblies[assemblyIndex]);
33+
}
34+
35+
return nativeImage;
36+
}
37+
#endif
38+
39+
#ifndef DACCESS_COMPILE
40+
void AssemblyLoadContext::AddLoadedAssembly(Assembly *loadedAssembly)
41+
{
42+
BaseDomain::LoadLockHolder lock(AppDomain::GetCurrentDomain());
43+
m_loadedAssemblies.Append(loadedAssembly);
44+
for (COUNT_T nativeImageIndex = 0; nativeImageIndex < m_nativeImages.GetCount(); nativeImageIndex++)
45+
{
46+
m_nativeImages[nativeImageIndex]->CheckAssemblyMvid(loadedAssembly);
47+
}
2848
}
2949
#endif

0 commit comments

Comments
 (0)