Skip to content
Merged
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
13 changes: 12 additions & 1 deletion GoogleTestAdapter/DiaResolver.Tests/PeParserTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.IO;
// This file has been modified by Microsoft on 9/2017.

using System.IO;
using FluentAssertions;
using GoogleTestAdapter.Tests.Common;
using Microsoft.VisualStudio.TestTools.UnitTesting;
Expand Down Expand Up @@ -45,6 +47,15 @@ public void PeParser_X86StaticallyLinked_FindsEmbeddedPdbPath()
pdb.Should().Be(expectedPdb);
}

[TestMethod]
[TestCategory(Unit)]
public void PeParser_X86UnicodeName_FindsEmbeddedPdbPath()
{
string pdb = PeParser.ExtractPdbPath(TestResources.UnicodeExe, MockLogger.Object);
string expectedPdb = Path.GetFullPath(Path.ChangeExtension(TestResources.UnicodeExe, ".pdb"));
pdb.Should().Be(expectedPdb);
}

}

}
246 changes: 187 additions & 59 deletions GoogleTestAdapter/DiaResolver/PeParser.cs
Original file line number Diff line number Diff line change
@@ -1,56 +1,14 @@
// This file has been modified by Microsoft on 8/2017.
// This file has been modified by Microsoft on 9/2017.

using GoogleTestAdapter.Common;
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using GoogleTestAdapter.Common;
using System.Text;

namespace GoogleTestAdapter.DiaResolver
{

[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
struct LOADED_IMAGE
{
public IntPtr ModuleName;
public IntPtr hFile;
public IntPtr MappedAddress;
public IntPtr FileHeader;
public IntPtr LastRvaSection;
public uint NumbOfSections;
public IntPtr FirstRvaSection;
public uint charachteristics;
public ushort systemImage;
public ushort dosImage;
public ushort readOnly;
public ushort version;
public IntPtr links_1;
public IntPtr links_2;
public uint sizeOfImage;
public IntPtr links_3;
public IntPtr links_4;
public IntPtr links_5;
}

[StructLayout(LayoutKind.Explicit)]
// ReSharper disable once InconsistentNaming
struct IMAGE_IMPORT_DESCRIPTOR
{
[FieldOffset(0)]
public uint Characteristics;
[FieldOffset(0)]
public uint OriginalFirstThunk;

[FieldOffset(4)]
public uint TimeDateStamp;
[FieldOffset(8)]
public uint ForwarderChain;
[FieldOffset(12)]
public uint Name;
[FieldOffset(16)]
public uint FirstThunk;
}

[StructLayout(LayoutKind.Explicit)]
// ReSharper disable once InconsistentNaming
struct IMAGE_DEBUG_DIRECTORY
Expand Down Expand Up @@ -80,6 +38,57 @@ struct IMAGE_DEBUG_DIRECTORY
public uint PointerToRawData;
}

[StructLayout(LayoutKind.Sequential)]
unsafe struct IMAGE_DOS_HEADER
{
public ushort e_magic;
public ushort e_cblp;
public ushort e_cp;
public ushort e_crlc;
public ushort e_cparhdr;
public ushort e_minalloc;
public ushort e_maxalloc;
public ushort e_ss;
public ushort e_sp;
public ushort e_csum;
public ushort e_ip;
public ushort e_cs;
public ushort e_lfarlc;
public ushort e_ovno;
public fixed ushort e_res1[4];
public ushort e_oemid;
public ushort e_oeminfo;
public fixed ushort e_res2[10];
public int e_lfanew;
}

[StructLayout(LayoutKind.Explicit)]
// ReSharper disable once InconsistentNaming
struct IMAGE_IMPORT_DESCRIPTOR
{
[FieldOffset(0)]
public uint Characteristics;
[FieldOffset(0)]
public uint OriginalFirstThunk;

[FieldOffset(4)]
public uint TimeDateStamp;
[FieldOffset(8)]
public uint ForwarderChain;
[FieldOffset(12)]
public uint Name;
[FieldOffset(16)]
public uint FirstThunk;
}

[StructLayout(LayoutKind.Sequential)]
unsafe struct IMAGE_NT_HEADERS
{
public int Signature;
public fixed byte FileHeader[20];
public fixed byte OptionalHeader[224];
}

[StructLayout(LayoutKind.Explicit)]
struct PdbInfo
{
Expand All @@ -99,36 +108,140 @@ struct PdbInfo
public byte PdbFileName;
}

struct LoadedImage
{
public IntPtr MappedAddress;
public IntPtr FileHeader;
}

unsafe public static class PeParser
{
private static class NativeMethods
{
[DllImport("imageHlp.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
public static extern bool MapAndLoad(string imageName, string dllPath, LOADED_IMAGE* loadedImage, bool dotDll, bool readOnly);
public const ushort IMAGE_DOS_SIGNATURE = 0x5A4D;
public const ushort IMAGE_NT_SIGNATURE = 0x00004550;

public const uint GENERIC_READ = unchecked(0x80000000);
public const uint FILE_MAP_READ = 0x0004;
public const uint FILE_SHARE_READ = 0x00000001;
public const uint OPEN_EXISTING = 3;
public const uint PAGE_READONLY = 0x02;

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern SafeFileHandle CreateFile(
string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes,
uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern SafeFileHandle CreateFileMapping(
SafeFileHandle hFile, IntPtr lpFileMappingAttributes, uint flProtect,
uint dwMaximumSizeHigh, uint dwMaximumSizeLow, string lpName);

[DllImport("imageHlp.dll", CallingConvention = CallingConvention.Winapi)]
public static extern bool UnMapAndLoad(ref LOADED_IMAGE loadedImage);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetFileSizeEx(SafeFileHandle hFile, out long lpFileSize);

[DllImport("dbghelp.dll", CallingConvention = CallingConvention.Winapi)]
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr MapViewOfFile(
SafeFileHandle hFileMappingObject, uint dwDesiredAccess, uint dwFileOffsetHigh,
uint dwFileOffsetLow, UIntPtr dwNumberOfBytesToMap);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);

[DllImport("dbghelp.dll")]
public static extern void* ImageDirectoryEntryToData(IntPtr pBase, byte mappedAsImage, ushort directoryEntry, uint* size);

[DllImport("dbghelp.dll", CallingConvention = CallingConvention.Winapi)]
[DllImport("dbghelp.dll")]
public static extern IntPtr ImageRvaToVa(IntPtr pNtHeaders, IntPtr pBase, uint rva, IntPtr pLastRvaSection);
}

private static void ParsePeFile(string executable, ILogger logger, Action<LOADED_IMAGE> action)
private static bool MapAndLoad(string imageName, out LoadedImage loadedImage)
{
LOADED_IMAGE image = new LOADED_IMAGE();
loadedImage = new LoadedImage();

long fileSize;
IntPtr mapAddr;
using (var hFile = NativeMethods.CreateFile(imageName, NativeMethods.GENERIC_READ,
NativeMethods.FILE_SHARE_READ, IntPtr.Zero, NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero))
{
if (hFile.IsInvalid)
return false;

if (!NativeMethods.GetFileSizeEx(hFile, out fileSize))
return false;

using (var hMapping = NativeMethods.CreateFileMapping(hFile, IntPtr.Zero, NativeMethods.PAGE_READONLY, 0, 0, null))
{
if (hMapping.IsInvalid)
return false;

mapAddr = NativeMethods.MapViewOfFile(hMapping, NativeMethods.FILE_MAP_READ, 0, 0, UIntPtr.Zero);
if (mapAddr == IntPtr.Zero)
return false;
}
}

unsafe
{
if (fileSize < Marshal.SizeOf<IMAGE_DOS_HEADER>())
return false;

var dosHeader = (IMAGE_DOS_HEADER*)mapAddr;
IMAGE_NT_HEADERS* rawFileHeader;
if (dosHeader->e_magic == NativeMethods.IMAGE_DOS_SIGNATURE)
{
if (dosHeader->e_lfanew <= 0
|| fileSize < dosHeader->e_lfanew + Marshal.SizeOf<IMAGE_NT_HEADERS>())
{
return false;
}

rawFileHeader = (IMAGE_NT_HEADERS*)((byte*)mapAddr + dosHeader->e_lfanew);
}
else if (dosHeader->e_magic == NativeMethods.IMAGE_NT_SIGNATURE)
{
if (fileSize < Marshal.SizeOf<IMAGE_NT_HEADERS>())
return false;

rawFileHeader = (IMAGE_NT_HEADERS*)mapAddr;
}
else
{
return false;
}

if (rawFileHeader->Signature != NativeMethods.IMAGE_NT_SIGNATURE)
return false;

loadedImage.MappedAddress = mapAddr;
loadedImage.FileHeader = (IntPtr)rawFileHeader;
return true;
}
}

private static bool UnMapAndLoad(ref LoadedImage loadedImage)
{
if (NativeMethods.UnmapViewOfFile(loadedImage.MappedAddress))
{
loadedImage = new LoadedImage();
return true;
}
return false;
}

private static void ParsePeFile(string executable, ILogger logger, Action<LoadedImage> action)
{
LoadedImage image = new LoadedImage();
bool loaded = false;
try
{
loaded = NativeMethods.MapAndLoad(executable, null, &image, true, true);
loaded = MapAndLoad(executable, out image);
if(loaded)
action(image);
}
finally
{
if (loaded && !NativeMethods.UnMapAndLoad(ref image))
if (loaded && !UnMapAndLoad(ref image))
logger.LogError(Resources.UnMapLoad);
}
}
Expand All @@ -149,10 +262,25 @@ public static List<string> ParseImports(string executable, ILogger logger)
return imports;
}

private static string GetString(LOADED_IMAGE image, uint offset)
private static string PtrToStringUtf8(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return null;

int size = 0;
while (Marshal.ReadByte(ptr, size) != 0)
++size;

byte[] buffer = new byte[size];
Marshal.Copy(ptr, buffer, 0, buffer.Length);

return Encoding.UTF8.GetString(buffer);
}

private static string GetString(LoadedImage image, uint offset)
{
IntPtr stringPtr = NativeMethods.ImageRvaToVa(image.FileHeader, image.MappedAddress, offset, IntPtr.Zero);
return Marshal.PtrToStringAnsi(stringPtr);
return PtrToStringUtf8(stringPtr);
}

// Most windows executables contain the path to their PDB
Expand All @@ -172,7 +300,7 @@ public static string ExtractPdbPath(string executable, ILogger logger)
if (dbgDir->SizeOfData > 0)
{
var pdbInfo = (PdbInfo*)NativeMethods.ImageRvaToVa(image.FileHeader, image.MappedAddress, dbgDir->AddressOfRawData, IntPtr.Zero);
pdbPath = Marshal.PtrToStringAnsi(new IntPtr(&pdbInfo->PdbFileName));
pdbPath = PtrToStringUtf8(new IntPtr(&pdbInfo->PdbFileName));
}
});

Expand Down
7 changes: 7 additions & 0 deletions GoogleTestAdapter/GoogleTestAdapter.sln
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Packaging.TAfGT", "Packagin
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SemaphoreExe", "SemaphoreExe\SemaphoreExe.vcxproj", "{F48AD2EC-96B3-41C6-9F89-3542EC7A3D43}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "㐀㕵ExtAxCP936丂狛狜", "㐀㕵ExtAxCP936丂狛狜\㐀㕵ExtAxCP936丂狛狜.vcxproj", "{8B27FD59-F03C-468E-B878-CFFC7484B7F7}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
VsPackage.Shared\VsPackage.Shared.projitems*{55294b5f-a075-43f2-b0e9-2b11925e8b91}*SharedItemsImports = 4
Expand Down Expand Up @@ -249,6 +251,10 @@ Global
{F48AD2EC-96B3-41C6-9F89-3542EC7A3D43}.Debug|Any CPU.Build.0 = Debug|Win32
{F48AD2EC-96B3-41C6-9F89-3542EC7A3D43}.Release|Any CPU.ActiveCfg = Release|Win32
{F48AD2EC-96B3-41C6-9F89-3542EC7A3D43}.Release|Any CPU.Build.0 = Release|Win32
{8B27FD59-F03C-468E-B878-CFFC7484B7F7}.Debug|Any CPU.ActiveCfg = Debug|Win32
{8B27FD59-F03C-468E-B878-CFFC7484B7F7}.Debug|Any CPU.Build.0 = Debug|Win32
{8B27FD59-F03C-468E-B878-CFFC7484B7F7}.Release|Any CPU.ActiveCfg = Release|Win32
{8B27FD59-F03C-468E-B878-CFFC7484B7F7}.Release|Any CPU.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -277,6 +283,7 @@ Global
{BAB53542-85AA-4780-9F48-2984C036189A} = {1FF56AF6-0ACE-4FE8-B802-4832703EC2DC}
{2E3221EB-86DA-427D-84EC-DEFD3F966D9A} = {1FF56AF6-0ACE-4FE8-B802-4832703EC2DC}
{F48AD2EC-96B3-41C6-9F89-3542EC7A3D43} = {475245AA-A07D-41D8-BC84-959C5E12A52C}
{8B27FD59-F03C-468E-B878-CFFC7484B7F7} = {475245AA-A07D-41D8-BC84-959C5E12A52C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C942DDD5-B04E-4D57-BA9F-A444392C9480}
Expand Down
16 changes: 8 additions & 8 deletions GoogleTestAdapter/TestAdapter.Tests/TestDiscovererTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// This file has been modified by Microsoft on 8/2017.
// This file has been modified by Microsoft on 9/2017.

using FluentAssertions;
using GoogleTestAdapter.Helpers;
Expand Down Expand Up @@ -55,7 +55,7 @@ public void DiscoverTests_FailingExecutable_ExitCodeIsLogged()

private void MarkUntrusted(string path)
{
using (var handle = NativeMethods.CreateFileW(path + ":Zone.Identifier", NativeMethods.GENERIC_WRITE, 0, IntPtr.Zero,
using (var handle = NativeMethods.CreateFile(path + ":Zone.Identifier", NativeMethods.GENERIC_WRITE, 0, IntPtr.Zero,
NativeMethods.CREATE_NEW, 0, IntPtr.Zero))
{
if (handle.IsInvalid)
Expand Down Expand Up @@ -155,13 +155,13 @@ private void CheckForDiscoverySinkCalls(int expectedNrOfTests, string customRege

static class NativeMethods
{
public const int GENERIC_WRITE = 1073741824;
public const int CREATE_NEW = 1;
public const uint GENERIC_WRITE = 0x40000000;
public const uint CREATE_NEW = 1;

[DllImport("kernel32.dll")]
public static extern SafeFileHandle CreateFileW(
[MarshalAs(UnmanagedType.LPWStr)] string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes,
uint dwCreationDisposition,uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern SafeFileHandle CreateFile(
string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes,
uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
}

}
Loading