You've already forked linux-packaging-mono
Imported Upstream version 5.16.0.100
Former-commit-id: 38faa55fb9669e35e7d8448b15c25dc447f25767
This commit is contained in:
parent
0a9828183b
commit
7d7f676260
@@ -1,18 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
|
||||
<PropertyGroup>
|
||||
<!-- we need to be supported on pre-nuget-3 platforms (Dev12, Dev11, etc) -->
|
||||
<MinClientVersion>2.8.6</MinClientVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\src\System.Reflection.Metadata.csproj">
|
||||
<SupportedFramework>net45;netcore45;netcoreapp1.0;wpa81;$(AllXamarinFrameworks)</SupportedFramework>
|
||||
</ProjectReference>
|
||||
<FilePackageDependency Include="System.Collections.Immutable">
|
||||
<TargetFramework>portable-net45+win8</TargetFramework>
|
||||
<Version>1.1.37</Version>
|
||||
</FilePackageDependency>
|
||||
|
||||
<!-- Since UAP and .NETCoreApp are package based we still want to enable
|
||||
OOBing libraries that happen to overlap with their framework package.
|
||||
|
||||
@@ -1 +1 @@
|
||||
93e9e802bb048df0a12bc7c17523cc0f099c10c9
|
||||
5fbdd25211eb4a97b9f694ae135116cb03fc3a84
|
||||
@@ -14,7 +14,7 @@ PE/COFF Specification defines the structure of Debug Directory in section 5.1.1.
|
||||
|
||||
### CodeView Debug Directory Entry (type 2)
|
||||
|
||||
*Version Major=0, Minor=0* of the data format:
|
||||
<a name="WindowsCodeViewEntry"></a>*Version Major=0, Minor=0* of the data format:
|
||||
|
||||
| Offset | Size | Field | Description |
|
||||
|:-------|:-----|:---------------|----------------------------------------------------------------|
|
||||
@@ -27,9 +27,9 @@ Guid and Age are used to match PE/COFF image with the associated PDB.
|
||||
|
||||
The associated .pdb file may not exist at the path indicated by Path field. If it doesn't the Path, Guid and Age can be used to find the corresponding PDB file locally or on a symbol server. The exact search algorithm used by tools to locate the PDB depends on the tool and its configuration.
|
||||
|
||||
If the containing PE/COFF file is deterministic the Guid field above and DateTimeStamp field of the directory entry are calculated deterministically based solely on the content of the associated .pdb file. Otherwise the value of Guid is random and the value of DateTimeStamp indicates the time and date that the debug data was created.
|
||||
If the containing PE/COFF file is deterministic the Guid field above and DateTimeStamp field of the directory entry are calculated deterministically based solely on the content of the associated .pdb file. Otherwise the value of Guid is random and the value of DateTimeStamp indicates the time and date that the debug data was created.
|
||||
|
||||
*Version Major=any, Minor=0x504d* of the data format has the same structure as above. The Age shall be 1. The format of the .pdb file that this PE/COFF file was built with is Portable PDB. The Major version specified in the entry indicates the version of the Portable PDB format. Together 16B of the Guid concatenated with 4B of the TimeDateStamp field of the entry form a PDB ID that should be used to match the PE/COFF image with the associated PDB (instead of Guid and Age). Matching PDB ID is stored in the #Pdb stream of the .pdb file.
|
||||
<a name="PortableCodeViewEntry"></a> *Version Major=any, Minor=0x504d* of the data format has the same structure as above. The Age shall be 1. The format of the .pdb file that this PE/COFF file was built with is Portable PDB. The Major version specified in the entry indicates the version of the Portable PDB format. Together 16B of the Guid concatenated with 4B of the TimeDateStamp field of the entry form a PDB ID that should be used to match the PE/COFF image with the associated PDB (instead of Guid and Age). Matching PDB ID is stored in the #Pdb stream of the .pdb file.
|
||||
|
||||
> A matching PDB may be found whose format is different than the format of the PDB the PE/COFF file was built with. This may happen when the original PDB file is [converted](http://github.com/dotnet/symreader-converter) to the other format without updating the PE/COFF file. This scenario is fully supported. A tool looking for the associated PDB shall determine the actual format of the found PDB based on the signature at the start of the PDB file. The tool may use the version in CodeView entry as a hint to prefer the original format over the converted one if both are available.
|
||||
|
||||
@@ -65,3 +65,45 @@ UncompressedSize shall be greater than 0. Other values are reserved for future u
|
||||
The Major version specified in the entry indicates the version of the Portable PDB format. The Minor version indicates the version of the Embedded Portable PDB data format.
|
||||
|
||||
The value of Stamp field in the entry shall be 0.
|
||||
|
||||
### PDB Checksum Debug Directory Entry (type 19)
|
||||
|
||||
Stores crypto hash of the content of the symbol file the PE/COFF file was built with.
|
||||
|
||||
The hash can be used to validate that a given PDB file was built with the PE/COFF file and not altered in any way.
|
||||
|
||||
More than one entry can be present, in case multiple PDBs were produced during the build of the PE/COFF file (e.g. private and public symbols).
|
||||
|
||||
*Version Major=0x0001, Minor=0x0000* of the entry data format is following:
|
||||
|
||||
| Offset | Size | Field | Description |
|
||||
|:--------------|:--------------|:---------------|-------------------------------------------------------------------------|
|
||||
| 0 | AlgNameLength | AlgorithmName | Zero-terminated UTF8 string. The name of the crypho hash algorithm. |
|
||||
| AlgNameLength | ChecksumSize | Checksum | Blob. Checksum of the PDB content. |
|
||||
|
||||
_AlgorithmName_ is the name of the crypto hash algorithm used to calculate the checksum. The name is case-sensitive.
|
||||
|
||||
Supported algorithm names include at least:
|
||||
|
||||
| AlgorithmName | ChecksumSize | Description |
|
||||
|:--------------|:-------------|:-------------------------------------------------------------------|
|
||||
| `SHA256` | 32 | The 256-bit secure hash algorithm. Standard: FIPS 180-2, FIPS 198. |
|
||||
| `SHA384` | 48 | The 384-bit secure hash algorithm. Standard: FIPS 180-2, FIPS 198. |
|
||||
| `SHA512` | 64 | The 512-bit secure hash algorithm. Standard: FIPS 180-2, FIPS 198. |
|
||||
|
||||
#### Portable PDB Checksum
|
||||
|
||||
If the symbol format is Portable PDB the checksum is calculated by hashing the entire content of the PDB file with the PDB ID set to 0 (20 zeroed bytes).
|
||||
|
||||
When validating that Portable PDB matches the debug directory record check that the checksums match and that the PDB ID match the data in the corresponding [Portable CodeView record](#PortableCodeViewEntry).
|
||||
|
||||
> Excluding the PDB ID allows the compiler to calculate a single hash of the PDB file content and use it to generate both deterministic PDB ID and PDB checksum.
|
||||
|
||||
#### Windows PDB Checksum
|
||||
|
||||
If the symbol format is Windows PDB the checksum is calculated by hashing the entire content of the PDB file with the PDB signature comprising of 16B GUID and 4B timestamp zeroed.
|
||||
|
||||
When validating that Windows PDB matches the debug directory record check that the checksums match and that the PDB signature (both GUID and timestamp values) match the data in the corresponding [CodeView record](#WindowsCodeViewEntry).
|
||||
|
||||
> Note that when the debugger (or other tool) searches for the PDB only GUID and Age fields are used to match the PDB, but the timestamp of the CodeView debug directory entry does not need to match the timestamp stored in the PDB. Therefore, to verify byte-for-byte identity of the PDB, the timestamp field should also be checked.
|
||||
|
||||
|
||||
@@ -280,6 +280,9 @@
|
||||
<data name="UnexpectedEmbeddedPortablePdbDataSignature" xml:space="preserve">
|
||||
<value>Unexpected Embedded Portable PDB data signature value.</value>
|
||||
</data>
|
||||
<data name="InvalidPdbChecksumDataFormat" xml:space="preserve">
|
||||
<value>Invalid PDB Checksum data format.</value>
|
||||
</data>
|
||||
<data name="UnexpectedSignatureHeader" xml:space="preserve">
|
||||
<value>Expected signature header for '{0}', but found '{1}' (0x{2:x2}).</value>
|
||||
</data>
|
||||
@@ -340,6 +343,9 @@
|
||||
<data name="ExpectedNonEmptyList" xml:space="preserve">
|
||||
<value>Expected non-empty list.</value>
|
||||
</data>
|
||||
<data name="ExpectedNonEmptyArray" xml:space="preserve">
|
||||
<value>Expected non-empty array.</value>
|
||||
</data>
|
||||
<data name="ExpectedNonEmptyString" xml:space="preserve">
|
||||
<value>Expected non-empty string.</value>
|
||||
</data>
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
<Compile Include="System\Reflection\PortableExecutable\PEBuilder.cs" />
|
||||
<Compile Include="System\Reflection\PortableExecutable\DebugDirectory\DebugDirectoryBuilder.cs" />
|
||||
<Compile Include="System\Reflection\PortableExecutable\DebugDirectory\DebugDirectoryBuilder.EmbeddedPortablePdb.cs" />
|
||||
<Compile Include="System\Reflection\PortableExecutable\DebugDirectory\PdbChecksumDebugDirectoryData.cs" />
|
||||
<Compile Include="System\Reflection\PortableExecutable\PEDirectoriesBuilder.cs" />
|
||||
<Compile Include="System\Reflection\PortableExecutable\PEHeaderBuilder.cs" />
|
||||
<Compile Include="System\Reflection\PortableExecutable\ResourceSectionBuilder.cs" />
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace System.Reflection.Metadata
|
||||
/// <summary>
|
||||
/// Get file name from path.
|
||||
/// </summary>
|
||||
/// <remarks>Unlike <see cref="System.IO.Path.GetFileName"/> doesn't check for invalid path characters.</remarks>
|
||||
/// <remarks>Unlike <see cref="System.IO.Path.GetFileName(string)"/> this method doesn't check for invalid path characters.</remarks>
|
||||
internal static string GetFileName(string path, bool includeExtension = true)
|
||||
{
|
||||
int fileNameStart = IndexOfFileName(path);
|
||||
|
||||
@@ -27,25 +27,27 @@ namespace System.Reflection.Metadata.Ecma335
|
||||
_opCode = (byte)opCode;
|
||||
}
|
||||
|
||||
internal bool IsShortBranchDistance(ImmutableArray<int>.Builder labels, out int distance)
|
||||
internal int GetBranchDistance(ImmutableArray<int>.Builder labels, ILOpCode branchOpCode, int branchILOffset, bool isShortBranch)
|
||||
{
|
||||
const int shortBranchSize = 2;
|
||||
const int longBranchSize = 5;
|
||||
|
||||
int labelTargetOffset = labels[Label.Id - 1];
|
||||
if (labelTargetOffset < 0)
|
||||
{
|
||||
Throw.InvalidOperation_LabelNotMarked(Label.Id);
|
||||
}
|
||||
|
||||
distance = labelTargetOffset - (ILOffset + shortBranchSize);
|
||||
if (unchecked((sbyte)distance) == distance)
|
||||
int branchInstructionSize = 1 + (isShortBranch ? sizeof(sbyte) : sizeof(int));
|
||||
int distance = labelTargetOffset - (ILOffset + branchInstructionSize);
|
||||
|
||||
if (isShortBranch && unchecked((sbyte)distance) != distance)
|
||||
{
|
||||
return true;
|
||||
// We could potentially implement algorithm that automatically fixes up branch instructions to accomodate for bigger distances (short vs long),
|
||||
// however an optimal algorithm would be rather complex (something like: calculate topological ordering of crossing branch instructions
|
||||
// and then use fixed point to eliminate cycles). If the caller doesn't care about optimal IL size they can use long branches whenever the
|
||||
// distance is unknown upfront. If they do they probably implement more sophisticated algorithm for IL layout optimization already.
|
||||
throw new InvalidOperationException(SR.Format(SR.DistanceBetweenInstructionAndLabelTooBig, branchOpCode, branchILOffset, distance));
|
||||
}
|
||||
|
||||
distance = labelTargetOffset - (ILOffset + longBranchSize);
|
||||
return false;
|
||||
return distance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,19 +281,9 @@ namespace System.Reflection.Metadata.Ecma335
|
||||
// write branch opcode:
|
||||
dstBuilder.WriteByte(srcBlob.Buffer[srcBlobOffset]);
|
||||
|
||||
int branchDistance = branch.GetBranchDistance(_labels, branch.OpCode, srcOffset, isShortInstruction);
|
||||
|
||||
// write branch operand:
|
||||
int branchDistance;
|
||||
bool isShortDistance = branch.IsShortBranchDistance(_labels, out branchDistance);
|
||||
|
||||
if (isShortInstruction && !isShortDistance)
|
||||
{
|
||||
// We could potentially implement algortihm that automatically fixes up the branch instructions as well to accomodate bigger distances,
|
||||
// however an optimal algorithm would be rather complex (something like: calculate topological ordering of crossing branch instructions
|
||||
// and then use fixed point to eliminate cycles). If the caller doesn't care about optimal IL size they can use long branches whenever the
|
||||
// distance is unknown upfront. If they do they probably already implement more sophisticad algorithm for IL layout optimization already.
|
||||
throw new InvalidOperationException(SR.Format(SR.DistanceBetweenInstructionAndLabelTooBig, branch.OpCode, srcOffset, branchDistance));
|
||||
}
|
||||
|
||||
if (isShortInstruction)
|
||||
{
|
||||
dstBuilder.WriteSByte((sbyte)branchDistance);
|
||||
@@ -307,7 +299,7 @@ namespace System.Reflection.Metadata.Ecma335
|
||||
branchIndex++;
|
||||
if (branchIndex == _branches.Count)
|
||||
{
|
||||
branch = new BranchInfo(int.MaxValue, default(LabelHandle), 0);
|
||||
branch = new BranchInfo(int.MaxValue, label: default, opCode: default);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -4,10 +4,20 @@
|
||||
|
||||
namespace System.Reflection.Metadata.Ecma335
|
||||
{
|
||||
/// <summary>
|
||||
/// Method body attributes.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum MethodBodyAttributes
|
||||
{
|
||||
/// <summary>
|
||||
/// No local memory initialization is performed.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Zero-initialize any locals the method defines and dynamically allocated local memory.
|
||||
/// </summary>
|
||||
InitLocals = 1,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,13 +43,37 @@ namespace System.Reflection.Metadata.Ecma335
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="codeSize"/>, <paramref name="exceptionRegionCount"/>, or <paramref name="maxStack"/> is out of allowed range.
|
||||
/// </exception>
|
||||
public MethodBody AddMethodBody(
|
||||
int codeSize,
|
||||
int maxStack,
|
||||
int exceptionRegionCount,
|
||||
bool hasSmallExceptionRegions,
|
||||
StandaloneSignatureHandle localVariablesSignature,
|
||||
MethodBodyAttributes attributes)
|
||||
=> AddMethodBody(codeSize, maxStack, exceptionRegionCount, hasSmallExceptionRegions, localVariablesSignature, attributes, hasDynamicStackAllocation: false);
|
||||
|
||||
/// <summary>
|
||||
/// Encodes a method body and adds it to the method body stream.
|
||||
/// </summary>
|
||||
/// <param name="codeSize">Number of bytes to be reserved for instructions.</param>
|
||||
/// <param name="maxStack">Max stack.</param>
|
||||
/// <param name="exceptionRegionCount">Number of exception regions.</param>
|
||||
/// <param name="hasSmallExceptionRegions">True if the exception regions should be encoded in 'small' format.</param>
|
||||
/// <param name="localVariablesSignature">Local variables signature handle.</param>
|
||||
/// <param name="attributes">Attributes.</param>
|
||||
/// <param name="hasDynamicStackAllocation">True if the method allocates from dynamic local memory pool (<c>localloc</c> instruction).</param>
|
||||
/// <returns>The offset of the encoded body within the method body stream.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="codeSize"/>, <paramref name="exceptionRegionCount"/>, or <paramref name="maxStack"/> is out of allowed range.
|
||||
/// </exception>
|
||||
public MethodBody AddMethodBody(
|
||||
int codeSize,
|
||||
int maxStack = 8,
|
||||
int exceptionRegionCount = 0,
|
||||
bool hasSmallExceptionRegions = true,
|
||||
StandaloneSignatureHandle localVariablesSignature = default(StandaloneSignatureHandle),
|
||||
MethodBodyAttributes attributes = MethodBodyAttributes.InitLocals)
|
||||
StandaloneSignatureHandle localVariablesSignature = default,
|
||||
MethodBodyAttributes attributes = MethodBodyAttributes.InitLocals,
|
||||
bool hasDynamicStackAllocation = false)
|
||||
{
|
||||
if (codeSize < 0)
|
||||
{
|
||||
@@ -66,11 +90,11 @@ namespace System.Reflection.Metadata.Ecma335
|
||||
Throw.ArgumentOutOfRange(nameof(exceptionRegionCount));
|
||||
}
|
||||
|
||||
int bodyOffset = SerializeHeader(codeSize, (ushort)maxStack, exceptionRegionCount, attributes, localVariablesSignature);
|
||||
int bodyOffset = SerializeHeader(codeSize, (ushort)maxStack, exceptionRegionCount, attributes, localVariablesSignature, hasDynamicStackAllocation);
|
||||
var instructions = Builder.ReserveBytes(codeSize);
|
||||
|
||||
var regionEncoder = (exceptionRegionCount > 0) ?
|
||||
ExceptionRegionEncoder.SerializeTableHeader(Builder, exceptionRegionCount, hasSmallExceptionRegions) : default(ExceptionRegionEncoder);
|
||||
ExceptionRegionEncoder.SerializeTableHeader(Builder, exceptionRegionCount, hasSmallExceptionRegions) : default;
|
||||
|
||||
return new MethodBody(bodyOffset, instructions, regionEncoder);
|
||||
}
|
||||
@@ -112,13 +136,37 @@ namespace System.Reflection.Metadata.Ecma335
|
||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="maxStack"/> is out of range [0, <see cref="ushort.MaxValue"/>].</exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// A label targeted by a branch in the instruction stream has not been marked,
|
||||
/// or the distance between a branch instruction and the target label is doesn't fit the size of the instruction operand.
|
||||
/// or the distance between a branch instruction and the target label doesn't fit the size of the instruction operand.
|
||||
/// </exception>
|
||||
public int AddMethodBody(
|
||||
InstructionEncoder instructionEncoder,
|
||||
int maxStack,
|
||||
StandaloneSignatureHandle localVariablesSignature,
|
||||
MethodBodyAttributes attributes)
|
||||
=> AddMethodBody(instructionEncoder, maxStack, localVariablesSignature, attributes, hasDynamicStackAllocation: false);
|
||||
|
||||
/// <summary>
|
||||
/// Encodes a method body and adds it to the method body stream.
|
||||
/// </summary>
|
||||
/// <param name="instructionEncoder">Instruction encoder.</param>
|
||||
/// <param name="maxStack">Max stack.</param>
|
||||
/// <param name="localVariablesSignature">Local variables signature handle.</param>
|
||||
/// <param name="attributes">Attributes.</param>
|
||||
/// <param name="hasDynamicStackAllocation">True if the method allocates from dynamic local memory pool (the IL contains <c>localloc</c> instruction).
|
||||
/// </param>
|
||||
/// <returns>The offset of the encoded body within the method body stream.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="instructionEncoder"/> has default value.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="maxStack"/> is out of range [0, <see cref="ushort.MaxValue"/>].</exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// A label targeted by a branch in the instruction stream has not been marked,
|
||||
/// or the distance between a branch instruction and the target label doesn't fit the size of the instruction operand.
|
||||
/// </exception>
|
||||
public int AddMethodBody(
|
||||
InstructionEncoder instructionEncoder,
|
||||
int maxStack = 8,
|
||||
StandaloneSignatureHandle localVariablesSignature = default(StandaloneSignatureHandle),
|
||||
MethodBodyAttributes attributes = MethodBodyAttributes.InitLocals)
|
||||
StandaloneSignatureHandle localVariablesSignature = default,
|
||||
MethodBodyAttributes attributes = MethodBodyAttributes.InitLocals,
|
||||
bool hasDynamicStackAllocation = false)
|
||||
{
|
||||
if (unchecked((uint)maxStack) > ushort.MaxValue)
|
||||
{
|
||||
@@ -142,7 +190,20 @@ namespace System.Reflection.Metadata.Ecma335
|
||||
Throw.ArgumentOutOfRange(nameof(instructionEncoder), SR.TooManyExceptionRegions);
|
||||
}
|
||||
|
||||
int bodyOffset = SerializeHeader(codeBuilder.Count, (ushort)maxStack, exceptionRegionCount, attributes, localVariablesSignature);
|
||||
// Note (see also https://github.com/dotnet/corefx/issues/26910)
|
||||
//
|
||||
// We could potentially automatically determine whether a tiny method with no variables and InitLocals flag set
|
||||
// has localloc instruction and thus needs a fat header. We could parse the IL stored in codeBuilder.
|
||||
// However, it would unnecessarily slow down emit of virtually all tiny methods, which do not use localloc
|
||||
// and would only address uninitialized memory issues in very rare scenarios when the pointer returned by
|
||||
// localloc is not stored in a local variable but passed to a method call or stored in a field.
|
||||
//
|
||||
// Since emitting code with localloc is already a pretty advanced scenario that emits unsafe code
|
||||
// that can be potentially incorrect in many other ways we decide that it's not worth the complexity
|
||||
// and a perf regression to do so. Instead we rely on the caller to let us know if there is a localloc
|
||||
// in the code they emitted.
|
||||
|
||||
int bodyOffset = SerializeHeader(codeBuilder.Count, (ushort)maxStack, exceptionRegionCount, attributes, localVariablesSignature, hasDynamicStackAllocation);
|
||||
|
||||
if (flowBuilder?.BranchCount > 0)
|
||||
{
|
||||
@@ -158,17 +219,27 @@ namespace System.Reflection.Metadata.Ecma335
|
||||
return bodyOffset;
|
||||
}
|
||||
|
||||
private int SerializeHeader(int codeSize, ushort maxStack, int exceptionRegionCount, MethodBodyAttributes attributes, StandaloneSignatureHandle localVariablesSignature)
|
||||
private int SerializeHeader(
|
||||
int codeSize,
|
||||
ushort maxStack,
|
||||
int exceptionRegionCount,
|
||||
MethodBodyAttributes attributes,
|
||||
StandaloneSignatureHandle localVariablesSignature,
|
||||
bool hasDynamicStackAllocation)
|
||||
{
|
||||
const int TinyFormat = 2;
|
||||
const int FatFormat = 3;
|
||||
const int MoreSections = 8;
|
||||
const byte InitLocals = 0x10;
|
||||
|
||||
bool initLocals = (attributes & MethodBodyAttributes.InitLocals) != 0;
|
||||
|
||||
bool isTiny = codeSize < 64 &&
|
||||
maxStack <= 8 &&
|
||||
localVariablesSignature.IsNil && (!hasDynamicStackAllocation || !initLocals) &&
|
||||
exceptionRegionCount == 0;
|
||||
|
||||
int offset;
|
||||
|
||||
bool isTiny = codeSize < 64 && maxStack <= 8 && localVariablesSignature.IsNil && exceptionRegionCount == 0;
|
||||
|
||||
if (isTiny)
|
||||
{
|
||||
offset = Builder.Count;
|
||||
@@ -186,7 +257,7 @@ namespace System.Reflection.Metadata.Ecma335
|
||||
flags |= MoreSections;
|
||||
}
|
||||
|
||||
if ((attributes & MethodBodyAttributes.InitLocals) != 0)
|
||||
if (initLocals)
|
||||
{
|
||||
flags |= InitLocals;
|
||||
}
|
||||
|
||||
@@ -425,7 +425,7 @@ namespace System.Reflection.Metadata.Ecma335
|
||||
return SignatureTypeKind.Unknown;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(typeHandle), SR.UnexpectedHandleKind);
|
||||
throw new ArgumentOutOfRangeException(nameof(typeHandle), SR.Format(SR.UnexpectedHandleKind, typeHandle.Kind));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ namespace System.Reflection.Metadata
|
||||
// A row id of "mscorlib" AssemblyRef in a WinMD file (each WinMD file must have such a reference).
|
||||
internal readonly int WinMDMscorlibRef;
|
||||
|
||||
// Keeps the underlying memory alive.
|
||||
private readonly object _memoryOwnerObj;
|
||||
|
||||
private readonly MetadataReaderOptions _options;
|
||||
private Dictionary<TypeDefinitionHandle, ImmutableArray<TypeDefinitionHandle>> _lazyNestedTypesMap;
|
||||
|
||||
@@ -34,7 +37,7 @@ namespace System.Reflection.Metadata
|
||||
/// The memory is owned by the caller and it must be kept memory alive and unmodified throughout the lifetime of the <see cref="MetadataReader"/>.
|
||||
/// </remarks>
|
||||
public unsafe MetadataReader(byte* metadata, int length)
|
||||
: this(metadata, length, MetadataReaderOptions.Default, null)
|
||||
: this(metadata, length, MetadataReaderOptions.Default, utf8Decoder: null, memoryOwner: null)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -47,7 +50,7 @@ namespace System.Reflection.Metadata
|
||||
/// metadata from a PE image.
|
||||
/// </remarks>
|
||||
public unsafe MetadataReader(byte* metadata, int length, MetadataReaderOptions options)
|
||||
: this(metadata, length, options, null)
|
||||
: this(metadata, length, options, utf8Decoder: null, memoryOwner: null)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -65,17 +68,22 @@ namespace System.Reflection.Metadata
|
||||
/// <exception cref="PlatformNotSupportedException">The current platform is big-endian.</exception>
|
||||
/// <exception cref="BadImageFormatException">Bad metadata header.</exception>
|
||||
public unsafe MetadataReader(byte* metadata, int length, MetadataReaderOptions options, MetadataStringDecoder utf8Decoder)
|
||||
: this(metadata, length, options, utf8Decoder, memoryOwner: null)
|
||||
{
|
||||
}
|
||||
|
||||
internal unsafe MetadataReader(byte* metadata, int length, MetadataReaderOptions options, MetadataStringDecoder utf8Decoder, object memoryOwner)
|
||||
{
|
||||
// Do not throw here when length is 0. We'll throw BadImageFormatException later on, so that the caller doesn't need to
|
||||
// worry about the image (stream) being empty and can handle all image errors by catching BadImageFormatException.
|
||||
if (length < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(length));
|
||||
Throw.ArgumentOutOfRange(nameof(length));
|
||||
}
|
||||
|
||||
if (metadata == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(metadata));
|
||||
Throw.ArgumentNull(nameof(metadata));
|
||||
}
|
||||
|
||||
if (utf8Decoder == null)
|
||||
@@ -85,28 +93,28 @@ namespace System.Reflection.Metadata
|
||||
|
||||
if (!(utf8Decoder.Encoding is UTF8Encoding))
|
||||
{
|
||||
throw new ArgumentException(SR.MetadataStringDecoderEncodingMustBeUtf8, nameof(utf8Decoder));
|
||||
Throw.InvalidArgument(SR.MetadataStringDecoderEncodingMustBeUtf8, nameof(utf8Decoder));
|
||||
}
|
||||
|
||||
this.Block = new MemoryBlock(metadata, length);
|
||||
Block = new MemoryBlock(metadata, length);
|
||||
|
||||
_memoryOwnerObj = memoryOwner;
|
||||
_options = options;
|
||||
this.UTF8Decoder = utf8Decoder;
|
||||
UTF8Decoder = utf8Decoder;
|
||||
|
||||
var headerReader = new BlobReader(this.Block);
|
||||
this.ReadMetadataHeader(ref headerReader, out _versionString);
|
||||
var headerReader = new BlobReader(Block);
|
||||
ReadMetadataHeader(ref headerReader, out _versionString);
|
||||
_metadataKind = GetMetadataKind(_versionString);
|
||||
var streamHeaders = this.ReadStreamHeaders(ref headerReader);
|
||||
var streamHeaders = ReadStreamHeaders(ref headerReader);
|
||||
|
||||
// storage header and stream headers:
|
||||
MemoryBlock metadataTableStream;
|
||||
MemoryBlock standalonePdbStream;
|
||||
this.InitializeStreamReaders(ref this.Block, streamHeaders, out _metadataStreamKind, out metadataTableStream, out standalonePdbStream);
|
||||
InitializeStreamReaders(Block, streamHeaders, out _metadataStreamKind, out var metadataTableStream, out var pdbStream);
|
||||
|
||||
int[] externalTableRowCountsOpt;
|
||||
if (standalonePdbStream.Length > 0)
|
||||
if (pdbStream.Length > 0)
|
||||
{
|
||||
ReadStandalonePortablePdbStream(standalonePdbStream, out _debugMetadataHeader, out externalTableRowCountsOpt);
|
||||
int pdbStreamOffset = (int)(pdbStream.Pointer - metadata);
|
||||
ReadStandalonePortablePdbStream(pdbStream, pdbStreamOffset, out _debugMetadataHeader, out externalTableRowCountsOpt);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -115,30 +123,28 @@ namespace System.Reflection.Metadata
|
||||
|
||||
var tableReader = new BlobReader(metadataTableStream);
|
||||
|
||||
HeapSizes heapSizes;
|
||||
int[] metadataTableRowCounts;
|
||||
this.ReadMetadataTableHeader(ref tableReader, out heapSizes, out metadataTableRowCounts, out _sortedTables);
|
||||
ReadMetadataTableHeader(ref tableReader, out var heapSizes, out var metadataTableRowCounts, out _sortedTables);
|
||||
|
||||
this.InitializeTableReaders(tableReader.GetMemoryBlockAt(0, tableReader.RemainingBytes), heapSizes, metadataTableRowCounts, externalTableRowCountsOpt);
|
||||
InitializeTableReaders(tableReader.GetMemoryBlockAt(0, tableReader.RemainingBytes), heapSizes, metadataTableRowCounts, externalTableRowCountsOpt);
|
||||
|
||||
// This previously could occur in obfuscated assemblies but a check was added to prevent
|
||||
// it getting to this point
|
||||
Debug.Assert(this.AssemblyTable.NumberOfRows <= 1);
|
||||
Debug.Assert(AssemblyTable.NumberOfRows <= 1);
|
||||
|
||||
// Although the specification states that the module table will have exactly one row,
|
||||
// the native metadata reader would successfully read files containing more than one row.
|
||||
// Such files exist in the wild and may be produced by obfuscators.
|
||||
if (standalonePdbStream.Length == 0 && this.ModuleTable.NumberOfRows < 1)
|
||||
if (pdbStream.Length == 0 && ModuleTable.NumberOfRows < 1)
|
||||
{
|
||||
throw new BadImageFormatException(SR.Format(SR.ModuleTableInvalidNumberOfRows, this.ModuleTable.NumberOfRows));
|
||||
}
|
||||
|
||||
// read
|
||||
this.NamespaceCache = new NamespaceCache(this);
|
||||
NamespaceCache = new NamespaceCache(this);
|
||||
|
||||
if (_metadataKind != MetadataKind.Ecma335)
|
||||
{
|
||||
this.WinMDMscorlibRef = FindMscorlibAssemblyRefNoProjection();
|
||||
WinMDMscorlibRef = FindMscorlibAssemblyRefNoProjection();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,14 +265,14 @@ namespace System.Reflection.Metadata
|
||||
}
|
||||
|
||||
private void InitializeStreamReaders(
|
||||
ref MemoryBlock metadataRoot,
|
||||
in MemoryBlock metadataRoot,
|
||||
StreamHeader[] streamHeaders,
|
||||
out MetadataStreamKind metadataStreamKind,
|
||||
out MemoryBlock metadataTableStream,
|
||||
out MemoryBlock standalonePdbStream)
|
||||
{
|
||||
metadataTableStream = default(MemoryBlock);
|
||||
standalonePdbStream = default(MemoryBlock);
|
||||
metadataTableStream = default;
|
||||
standalonePdbStream = default;
|
||||
metadataStreamKind = MetadataStreamKind.Illegal;
|
||||
|
||||
foreach (StreamHeader streamHeader in streamHeaders)
|
||||
@@ -517,9 +523,9 @@ namespace System.Reflection.Metadata
|
||||
}
|
||||
|
||||
// internal for testing
|
||||
internal static void ReadStandalonePortablePdbStream(MemoryBlock block, out DebugMetadataHeader debugMetadataHeader, out int[] externalTableRowCounts)
|
||||
internal static void ReadStandalonePortablePdbStream(MemoryBlock pdbStreamBlock, int pdbStreamOffset, out DebugMetadataHeader debugMetadataHeader, out int[] externalTableRowCounts)
|
||||
{
|
||||
var reader = new BlobReader(block);
|
||||
var reader = new BlobReader(pdbStreamBlock);
|
||||
|
||||
const int PdbIdSize = 20;
|
||||
byte[] pdbId = reader.ReadBytes(PdbIdSize);
|
||||
@@ -544,14 +550,15 @@ namespace System.Reflection.Metadata
|
||||
|
||||
if ((externalTableMask & ~validTables) != 0)
|
||||
{
|
||||
throw new BadImageFormatException(string.Format(SR.UnknownTables, (TableMask)externalTableMask));
|
||||
throw new BadImageFormatException(string.Format(SR.UnknownTables, externalTableMask));
|
||||
}
|
||||
|
||||
externalTableRowCounts = ReadMetadataTableRowCounts(ref reader, externalTableMask);
|
||||
|
||||
debugMetadataHeader = new DebugMetadataHeader(
|
||||
ImmutableByteArrayInterop.DangerousCreateFromUnderlyingArray(ref pdbId),
|
||||
MethodDefinitionHandle.FromRowId(entryPointRowId));
|
||||
MethodDefinitionHandle.FromRowId(entryPointRowId),
|
||||
idStartOffset: pdbStreamOffset);
|
||||
}
|
||||
|
||||
private const int SmallIndexSize = 2;
|
||||
|
||||
@@ -234,7 +234,7 @@ namespace System.Reflection.Metadata
|
||||
/// Gets a <see cref="MetadataReader"/> from a <see cref="MetadataReaderProvider"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The caller must keep the <see cref="MetadataReaderProvider"/> alive and undisposed throughout the lifetime of the metadata reader.
|
||||
/// The caller must keep the <see cref="MetadataReaderProvider"/> undisposed throughout the lifetime of the metadata reader.
|
||||
///
|
||||
/// If this method is called multiple times each call with arguments equal to the arguments passed to the previous successful call
|
||||
/// returns the same instance of <see cref="MetadataReader"/> as the previous call.
|
||||
@@ -266,7 +266,7 @@ namespace System.Reflection.Metadata
|
||||
}
|
||||
|
||||
AbstractMemoryBlock metadata = GetMetadataBlock();
|
||||
var newReader = new MetadataReader(metadata.Pointer, metadata.Size, options, utf8Decoder);
|
||||
var newReader = new MetadataReader(metadata.Pointer, metadata.Size, options, utf8Decoder, memoryOwner: this);
|
||||
_lazyMetadataReader = newReader;
|
||||
return newReader;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace System.Reflection.Metadata
|
||||
/// Gets a <see cref="MetadataReader"/> from a <see cref="PEReader"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The caller must keep the <see cref="PEReader"/> alive and undisposed throughout the lifetime of the metadata reader.
|
||||
/// The caller must keep the <see cref="PEReader"/> undisposed throughout the lifetime of the metadata reader.
|
||||
/// </remarks>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="peReader"/> is null</exception>
|
||||
/// <exception cref="ArgumentException">The encoding of <paramref name="utf8Decoder"/> is not <see cref="UTF8Encoding"/>.</exception>
|
||||
@@ -85,7 +85,7 @@ namespace System.Reflection.Metadata
|
||||
}
|
||||
|
||||
var metadata = peReader.GetMetadata();
|
||||
return new MetadataReader(metadata.Pointer, metadata.Length, options, utf8Decoder);
|
||||
return new MetadataReader(metadata.Pointer, metadata.Length, options, utf8Decoder, memoryOwner: peReader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.Reflection.Metadata
|
||||
{
|
||||
@@ -12,10 +11,16 @@ namespace System.Reflection.Metadata
|
||||
public ImmutableArray<byte> Id { get; }
|
||||
public MethodDefinitionHandle EntryPoint { get; }
|
||||
|
||||
internal DebugMetadataHeader(ImmutableArray<byte> id, MethodDefinitionHandle entryPoint)
|
||||
/// <summary>
|
||||
/// Gets the offset (in bytes) from the start of the metadata blob to the start of the <see cref="Id"/> blob.
|
||||
/// </summary>
|
||||
public int IdStartOffset { get; }
|
||||
|
||||
internal DebugMetadataHeader(ImmutableArray<byte> id, MethodDefinitionHandle entryPoint, int idStartOffset)
|
||||
{
|
||||
Id = id;
|
||||
EntryPoint = entryPoint;
|
||||
IdStartOffset = idStartOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,11 @@ namespace System.Reflection.Metadata
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this is a nested type.
|
||||
/// </summary>
|
||||
public bool IsNested => Attributes.IsNested();
|
||||
|
||||
/// <summary>
|
||||
/// Name of the type.
|
||||
/// </summary>
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace System.Reflection.PortableExecutable
|
||||
type: DebugDirectoryEntryType.EmbeddedPortablePdb,
|
||||
version: PortablePdbVersions.DebugDirectoryEmbeddedVersion(portablePdbVersion),
|
||||
stamp: 0,
|
||||
dataSize: dataSize);
|
||||
dataSize);
|
||||
}
|
||||
|
||||
private static int WriteEmbeddedPortablePdbData(BlobBuilder builder, BlobBuilder debugMetadata)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Collections.Immutable;
|
||||
using System.Reflection.Metadata;
|
||||
|
||||
namespace System.Reflection.PortableExecutable
|
||||
@@ -23,11 +23,11 @@ namespace System.Reflection.PortableExecutable
|
||||
|
||||
public DebugDirectoryBuilder()
|
||||
{
|
||||
_entries = new List<Entry>(2);
|
||||
_entries = new List<Entry>(3);
|
||||
_dataBuilder = new BlobBuilder();
|
||||
}
|
||||
|
||||
internal void AddEntry(DebugDirectoryEntryType type, uint version, uint stamp, int dataSize = 0)
|
||||
internal void AddEntry(DebugDirectoryEntryType type, uint version, uint stamp, int dataSize)
|
||||
{
|
||||
_entries.Add(new Entry()
|
||||
{
|
||||
@@ -38,6 +38,38 @@ namespace System.Reflection.PortableExecutable
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an entry.
|
||||
/// </summary>
|
||||
/// <param name="type">Entry type.</param>
|
||||
/// <param name="version">Entry version.</param>
|
||||
/// <param name="stamp">Entry stamp.</param>
|
||||
public void AddEntry(DebugDirectoryEntryType type, uint version, uint stamp)
|
||||
=> AddEntry(type, version, stamp, dataSize: 0);
|
||||
|
||||
/// <summary>
|
||||
/// Adds an entry.
|
||||
/// </summary>
|
||||
/// <typeparam name="TData">Type of data passed to <paramref name="dataSerializer"/>.</typeparam>
|
||||
/// <param name="type">Entry type.</param>
|
||||
/// <param name="version">Entry version.</param>
|
||||
/// <param name="stamp">Entry stamp.</param>
|
||||
/// <param name="data">Data passed to <paramref name="dataSerializer"/>.</param>
|
||||
/// <param name="dataSerializer">Serializes data to a <see cref="BlobBuilder"/>.</param>
|
||||
public void AddEntry<TData>(DebugDirectoryEntryType type, uint version, uint stamp, TData data, Action<BlobBuilder, TData> dataSerializer)
|
||||
{
|
||||
if (dataSerializer == null)
|
||||
{
|
||||
Throw.ArgumentNull(nameof(dataSerializer));
|
||||
}
|
||||
|
||||
int start = _dataBuilder.Count;
|
||||
dataSerializer(_dataBuilder, data);
|
||||
int dataSize = _dataBuilder.Count - start;
|
||||
|
||||
AddEntry(type, version, stamp, dataSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a CodeView entry.
|
||||
/// </summary>
|
||||
@@ -99,16 +131,14 @@ namespace System.Reflection.PortableExecutable
|
||||
type: DebugDirectoryEntryType.CodeView,
|
||||
version: (portablePdbVersion == 0) ? 0 : PortablePdbVersions.DebugDirectoryEntryVersion(portablePdbVersion),
|
||||
stamp: pdbContentId.Stamp,
|
||||
dataSize: dataSize);
|
||||
dataSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds Reproducible entry.
|
||||
/// </summary>
|
||||
public void AddReproducibleEntry()
|
||||
{
|
||||
AddEntry(type: DebugDirectoryEntryType.Reproducible, version: 0, stamp: 0);
|
||||
}
|
||||
=> AddEntry(type: DebugDirectoryEntryType.Reproducible, version: 0, stamp: 0);
|
||||
|
||||
private static int WriteCodeViewData(BlobBuilder builder, string pdbPath, Guid pdbGuid, int age)
|
||||
{
|
||||
@@ -133,6 +163,58 @@ namespace System.Reflection.PortableExecutable
|
||||
return builder.Count - start;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds PDB checksum entry.
|
||||
/// </summary>
|
||||
/// <param name="algorithmName">Hash algorithm name (e.g. "SHA256").</param>
|
||||
/// <param name="checksum">Checksum.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="algorithmName"/> or <paramref name="checksum"/> is null.</exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="algorithmName"/> or <paramref name="checksum"/> is empty.</exception>
|
||||
public void AddPdbChecksumEntry(string algorithmName, ImmutableArray<byte> checksum)
|
||||
{
|
||||
if (algorithmName == null)
|
||||
{
|
||||
Throw.ArgumentNull(nameof(algorithmName));
|
||||
}
|
||||
|
||||
if (algorithmName.Length == 0)
|
||||
{
|
||||
Throw.ArgumentEmptyString(nameof(algorithmName));
|
||||
}
|
||||
|
||||
if (checksum.IsDefault)
|
||||
{
|
||||
Throw.ArgumentNull(nameof(checksum));
|
||||
}
|
||||
|
||||
if (checksum.Length == 0)
|
||||
{
|
||||
Throw.ArgumentEmptyArray(nameof(checksum));
|
||||
}
|
||||
|
||||
int dataSize = WritePdbChecksumData(_dataBuilder, algorithmName, checksum);
|
||||
|
||||
AddEntry(
|
||||
type: DebugDirectoryEntryType.PdbChecksum,
|
||||
version: 0x00000001,
|
||||
stamp: 0x00000000,
|
||||
dataSize);
|
||||
}
|
||||
|
||||
private static int WritePdbChecksumData(BlobBuilder builder, string algorithmName, ImmutableArray<byte> checksum)
|
||||
{
|
||||
int start = builder.Count;
|
||||
|
||||
// NUL-terminated algorithm name:
|
||||
builder.WriteUTF8(algorithmName, allowUnpairedSurrogates: true);
|
||||
builder.WriteByte(0);
|
||||
|
||||
// checksum:
|
||||
builder.WriteBytes(checksum);
|
||||
|
||||
return builder.Count - start;
|
||||
}
|
||||
|
||||
internal int TableSize => DebugDirectoryEntry.Size * _entries.Count;
|
||||
internal int Size => TableSize + _dataBuilder?.Count ?? 0;
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@ namespace System.Reflection.PortableExecutable
|
||||
/// <summary>
|
||||
/// Associated PDB file description.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PE-COFF.md#codeview-debug-directory-entry-type-2 for specification.
|
||||
/// </remarks>
|
||||
CodeView = 2,
|
||||
|
||||
/// <summary>
|
||||
@@ -41,6 +44,9 @@ namespace System.Reflection.PortableExecutable
|
||||
/// <para>
|
||||
/// The debug directory entry of type <see cref="Reproducible"/> must have all fields, except for Type zeroed.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// See https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PE-COFF.md#deterministic-debug-directory-entry-type-16 for specification.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
Reproducible = 16,
|
||||
|
||||
@@ -53,7 +59,20 @@ namespace System.Reflection.PortableExecutable
|
||||
/// blob ::= uncompressed-size data
|
||||
///
|
||||
/// Data spans the remainder of the blob and contains a Deflate-compressed Portable PDB.
|
||||
///
|
||||
/// See https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PE-COFF.md#embedded-portable-pdb-debug-directory-entry-type-17 for specification.
|
||||
/// </remarks>
|
||||
EmbeddedPortablePdb = 17,
|
||||
|
||||
/// <summary>
|
||||
/// The entry stores crypto hash of the content of the symbol file the PE/COFF file was built with.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The hash can be used to validate that a given PDB file was built with the PE/COFF file and not altered in any way.
|
||||
/// More than one entry can be present, in case multiple PDBs were produced during the build of the PE/COFF file (e.g. private and public symbols).
|
||||
///
|
||||
/// See https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PE-COFF.md#pdb-checksum-debug-directory-entry-type-19 for specification.
|
||||
/// </remarks>
|
||||
PdbChecksum = 19,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.Reflection.PortableExecutable
|
||||
{
|
||||
public readonly struct PdbChecksumDebugDirectoryData
|
||||
{
|
||||
/// <summary>
|
||||
/// Checksum algorithm name.
|
||||
/// </summary>
|
||||
public string AlgorithmName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// GUID (Globally Unique Identifier) of the associated PDB.
|
||||
/// </summary>
|
||||
public ImmutableArray<byte> Checksum { get; }
|
||||
|
||||
internal PdbChecksumDebugDirectoryData(string algorithmName, ImmutableArray<byte> checksum)
|
||||
{
|
||||
Debug.Assert(!string.IsNullOrEmpty(algorithmName));
|
||||
Debug.Assert(!checksum.IsDefaultOrEmpty);
|
||||
|
||||
AlgorithmName = algorithmName;
|
||||
Checksum = checksum;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -624,6 +624,43 @@ namespace System.Reflection.PortableExecutable
|
||||
return new CodeViewDebugDirectoryData(guid, age, path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data pointed to by the specified Debug Directory entry and interprets them as PDB Checksum entry.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentException"><paramref name="entry"/> is not a PDB Checksum entry.</exception>
|
||||
/// <exception cref="BadImageFormatException">Bad format of the data.</exception>
|
||||
/// <exception cref="IOException">IO error while reading from the underlying stream.</exception>
|
||||
/// <exception cref="InvalidOperationException">PE image not available.</exception>
|
||||
public PdbChecksumDebugDirectoryData ReadPdbChecksumDebugDirectoryData(DebugDirectoryEntry entry)
|
||||
{
|
||||
if (entry.Type != DebugDirectoryEntryType.PdbChecksum)
|
||||
{
|
||||
Throw.InvalidArgument(SR.Format(SR.UnexpectedDebugDirectoryType, nameof(DebugDirectoryEntryType.PdbChecksum)), nameof(entry));
|
||||
}
|
||||
|
||||
using (var block = GetDebugDirectoryEntryDataBlock(entry))
|
||||
{
|
||||
return DecodePdbChecksumDebugDirectoryData(block);
|
||||
}
|
||||
}
|
||||
|
||||
// internal for testing
|
||||
internal static PdbChecksumDebugDirectoryData DecodePdbChecksumDebugDirectoryData(AbstractMemoryBlock block)
|
||||
{
|
||||
var reader = block.GetReader();
|
||||
|
||||
var algorithmName = reader.ReadUtf8NullTerminated();
|
||||
var checksum = reader.ReadBytes(reader.RemainingBytes);
|
||||
if (algorithmName.Length == 0 || checksum.Length == 0)
|
||||
{
|
||||
throw new BadImageFormatException(SR.InvalidPdbChecksumDataFormat);
|
||||
}
|
||||
|
||||
return new PdbChecksumDebugDirectoryData(
|
||||
algorithmName,
|
||||
ImmutableByteArrayInterop.DangerousCreateFromUnderlyingArray(ref checksum));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a Portable PDB associated with this PE image.
|
||||
/// </summary>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user