-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add .mvid section to PE and remove dependency on MetadataReader #19133
Merged
Merged
Changes from 1 commit
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
53a66be
Add .mvid section to PE and remove dependency on MetadataReader
67c31d3
Address PR feedback from Tomas and Jared
51a3c3f
Update refout docs
4de0d32
Address PR feedback from Tomas
jcouv ec9b2d4
Address PR feedback from Chuck and Aleksey
jcouv b1ac2aa
Clarify header format
jcouv 4c02a2d
Add test for .mvid section not being first
jcouv 7df9479
No end-of-stream exception
jcouv 786dbe7
More feedback
jcouv File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,56 +8,78 @@ namespace Microsoft.CodeAnalysis.BuildTasks | |
{ | ||
public static class MvidReader | ||
{ | ||
private static readonly Guid s_empty = Guid.Empty; | ||
|
||
public static Guid ReadAssemblyMvidOrEmpty(Stream stream) | ||
{ | ||
return ReadAssemblyMvidOrEmpty(new BinaryReader(stream)); | ||
} | ||
|
||
private static Guid ReadAssemblyMvidOrEmpty(BinaryReader reader) | ||
{ | ||
Guid empty = Guid.Empty; | ||
|
||
// DOS Header (64), DOS Stub (64), PE Signature (4), COFF Header (20), PE Header (224), 1x Section Header (40) | ||
if (reader.BaseStream.Length < 64 + 64 + 4 + 20 + 224 + 40) | ||
{ | ||
return empty; | ||
} | ||
|
||
// DOS Header: Magic number (2) | ||
if (reader.ReadUInt16() != 0x5a4d) // "MZ" | ||
if (!ReadUInt16(reader, out ushort magicNumber) || magicNumber != 0x5a4d) // "MZ" | ||
{ | ||
return empty; | ||
return s_empty; | ||
} | ||
|
||
// DOS Header: Address of PE Signature (at 0x3C) | ||
MoveTo(0x3C, reader); | ||
uint lfanew = reader.ReadUInt32(); | ||
if (!MoveTo(0x3C, reader)) | ||
{ | ||
return s_empty; | ||
} | ||
if (!ReadUInt32(reader, out uint lfanew)) | ||
{ | ||
return s_empty; | ||
} | ||
|
||
MoveTo(lfanew, reader); // jump over the MS DOS Stub to the PE Signature | ||
// jump over the MS DOS Stub to the PE Signature | ||
if (!MoveTo(lfanew, reader)) | ||
{ | ||
return s_empty; | ||
} | ||
|
||
// PE Signature ('P' 'E' null null) | ||
if (reader.ReadUInt32() != 0x00004550) | ||
if (!ReadUInt32(reader, out uint peSig) || peSig != 0x00004550) | ||
{ | ||
return empty; | ||
return s_empty; | ||
} | ||
|
||
// COFF Header: Machine (2) | ||
Skip(2, reader); | ||
if (!Skip(2, reader)) | ||
{ | ||
return s_empty; | ||
} | ||
|
||
// COFF Header: NumberOfSections (2) | ||
ushort sections = reader.ReadUInt16(); | ||
if (!ReadUInt16(reader, out ushort sections)) | ||
{ | ||
return s_empty; | ||
} | ||
|
||
// COFF Header: TimeDateStamp (4), PointerToSymbolTable (4), NumberOfSymbols (4) | ||
Skip(12, reader); | ||
if (!Skip(12, reader)) | ||
{ | ||
return s_empty; | ||
} | ||
|
||
// COFF Header: OptionalHeaderSize (2) | ||
ushort optionalHeaderSize = reader.ReadUInt16(); | ||
if (!ReadUInt16(reader, out ushort optionalHeaderSize)) | ||
{ | ||
return s_empty; | ||
} | ||
|
||
// COFF Header: Characteristics (2) | ||
Skip(2, reader); | ||
if (!Skip(2, reader)) | ||
{ | ||
return s_empty; | ||
} | ||
|
||
// Optional header | ||
Skip(optionalHeaderSize, reader); | ||
if (!Skip(optionalHeaderSize, reader)) | ||
{ | ||
return s_empty; | ||
} | ||
|
||
// Section headers | ||
return FindMvidInSections(sections, reader); | ||
|
@@ -67,58 +89,116 @@ private static Guid FindMvidInSections(ushort count, BinaryReader reader) | |
{ | ||
for (int i = 0; i < count; i++) | ||
{ | ||
|
||
// .mvid section must be first, if it's there | ||
// Section: Name (8) | ||
byte[] name = reader.ReadBytes(8); | ||
if (!ReadBytes(reader, 8, out byte[] name)) | ||
{ | ||
return s_empty; | ||
} | ||
|
||
if (name.Length == 8 && name[0] == '.' && | ||
name[1] == 'm' && name[2] == 'v' && name[3] == 'i' && name[4] == 'd' && name[5] == '\0') | ||
{ | ||
// Section: VirtualSize (4) | ||
uint virtualSize = reader.ReadUInt32(); | ||
if (!ReadUInt32(reader, out uint virtualSize) || virtualSize != 16) | ||
{ | ||
// The .mvid section only stores a Guid | ||
return s_empty; | ||
} | ||
|
||
// Section: VirtualAddress (4), SizeOfRawData (4) | ||
Skip(8, reader); | ||
|
||
// Section: PointerToRawData (4) | ||
uint pointerToRawData = reader.ReadUInt32(); | ||
|
||
// The .mvid section only stores a Guid | ||
if (virtualSize != 16) | ||
if (!Skip(8, reader)) | ||
{ | ||
Debug.Assert(false); | ||
return Guid.Empty; | ||
return s_empty; | ||
} | ||
|
||
if (MoveTo(pointerToRawData, reader)) | ||
// Section: PointerToRawData (4) | ||
if (!ReadUInt32(reader, out uint pointerToRawData)) | ||
{ | ||
byte[] guidBytes = new byte[16]; | ||
if (reader.BaseStream.Read(guidBytes, 0, 16) == 16) | ||
{ | ||
return new Guid(guidBytes); | ||
} | ||
return s_empty; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
return ReadMvidSection(reader, pointerToRawData); | ||
} | ||
else | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: no need for else since the if-statement returns #Resolved |
||
// Section: VirtualSize (4), VirtualAddress (4), SizeOfRawData (4), | ||
// PointerToRawData (4), PointerToRelocations (4), PointerToLineNumbers (4), | ||
// NumberOfRelocations (2), NumberOfLineNumbers (2), Characteristics (4) | ||
Skip(4 + 4 + 4 + 4 + 4 + 4 + 2 + 2 + 4, reader); | ||
if (!Skip(4 + 4 + 4 + 4 + 4 + 4 + 2 + 2 + 4, reader)) | ||
{ | ||
return s_empty; | ||
} | ||
} | ||
} | ||
|
||
return Guid.Empty; | ||
return s_empty; | ||
} | ||
|
||
private static Guid ReadMvidSection(BinaryReader reader, uint pointerToMvidSection) | ||
{ | ||
if (!MoveTo(pointerToMvidSection, reader)) | ||
{ | ||
return s_empty; | ||
} | ||
|
||
if (!ReadBytes(reader, 16, out byte[] guidBytes)) | ||
{ | ||
return s_empty; | ||
} | ||
|
||
return new Guid(guidBytes); | ||
} | ||
|
||
private static void Skip(int bytes, BinaryReader reader) | ||
private static bool ReadUInt16(BinaryReader reader, out ushort output) | ||
{ | ||
if (reader.BaseStream.Position + 2 >= reader.BaseStream.Length) | ||
{ | ||
output = 0; | ||
return false; | ||
} | ||
|
||
output = reader.ReadUInt16(); | ||
return true; | ||
} | ||
|
||
private static bool ReadUInt32(BinaryReader reader, out uint output) | ||
{ | ||
if (reader.BaseStream.Position + 4 >= reader.BaseStream.Length) | ||
{ | ||
output = 0; | ||
return false; | ||
} | ||
|
||
output = reader.ReadUInt32(); | ||
return true; | ||
} | ||
|
||
private static bool ReadBytes(BinaryReader reader, int count, out byte[] output) | ||
{ | ||
if (reader.BaseStream.Position + count >= reader.BaseStream.Length) | ||
{ | ||
output = null; | ||
return false; | ||
} | ||
|
||
output = reader.ReadBytes(count); | ||
return true; | ||
} | ||
|
||
private static bool Skip(int bytes, BinaryReader reader) | ||
{ | ||
if (reader.BaseStream.Position + bytes >= reader.BaseStream.Length) | ||
{ | ||
return false; | ||
} | ||
|
||
reader.BaseStream.Seek(bytes, SeekOrigin.Current); | ||
return true; | ||
} | ||
|
||
private static bool MoveTo(uint position, BinaryReader reader) | ||
{ | ||
if (reader.BaseStream.Length < position) | ||
if (position >= reader.BaseStream.Length) | ||
{ | ||
return false; | ||
} | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what the variable name means. Perhaps 'signatureOffset' instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what it's called in the spec, which I agree is not very informative. I'll fix.