diff --git a/GoogleTestAdapter/DiaResolver.Tests/PeParserTests.cs b/GoogleTestAdapter/DiaResolver.Tests/PeParserTests.cs index e167b00ad..e6174e1a7 100644 --- a/GoogleTestAdapter/DiaResolver.Tests/PeParserTests.cs +++ b/GoogleTestAdapter/DiaResolver.Tests/PeParserTests.cs @@ -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; @@ -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); + } + } } \ No newline at end of file diff --git a/GoogleTestAdapter/DiaResolver/PeParser.cs b/GoogleTestAdapter/DiaResolver/PeParser.cs index 0b19215d6..794e45123 100644 --- a/GoogleTestAdapter/DiaResolver/PeParser.cs +++ b/GoogleTestAdapter/DiaResolver/PeParser.cs @@ -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 @@ -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 { @@ -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 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()) + 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()) + { + return false; + } + + rawFileHeader = (IMAGE_NT_HEADERS*)((byte*)mapAddr + dosHeader->e_lfanew); + } + else if (dosHeader->e_magic == NativeMethods.IMAGE_NT_SIGNATURE) + { + if (fileSize < Marshal.SizeOf()) + 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 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); } } @@ -149,10 +262,25 @@ public static List 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 @@ -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)); } }); diff --git a/GoogleTestAdapter/GoogleTestAdapter.sln b/GoogleTestAdapter/GoogleTestAdapter.sln index 609eda422..95ab440ef 100644 --- a/GoogleTestAdapter/GoogleTestAdapter.sln +++ b/GoogleTestAdapter/GoogleTestAdapter.sln @@ -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 @@ -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 @@ -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} diff --git a/GoogleTestAdapter/TestAdapter.Tests/TestDiscovererTests.cs b/GoogleTestAdapter/TestAdapter.Tests/TestDiscovererTests.cs index a7e240953..b922a77f3 100644 --- a/GoogleTestAdapter/TestAdapter.Tests/TestDiscovererTests.cs +++ b/GoogleTestAdapter/TestAdapter.Tests/TestDiscovererTests.cs @@ -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; @@ -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) @@ -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); } } \ No newline at end of file diff --git a/GoogleTestAdapter/Tests.Common/TestResources.cs b/GoogleTestAdapter/Tests.Common/TestResources.cs index cbc65cc16..3b65ecf90 100644 --- a/GoogleTestAdapter/Tests.Common/TestResources.cs +++ b/GoogleTestAdapter/Tests.Common/TestResources.cs @@ -29,6 +29,7 @@ public static class TestResources public const string AlwaysCrashingExe = GoogleTestAdapterBuildDir + @"CrashingExe\CrashingExe.exe"; public const string AlwaysFailingExe = GoogleTestAdapterBuildDir + @"FailingExe\FailingExe.exe"; public const string SemaphoreExe = GoogleTestAdapterBuildDir + @"SemaphoreExe\SemaphoreExe.exe"; + public const string UnicodeExe = GoogleTestAdapterBuildDir + @"㐀㕵ExtAxCP936丂狛狜\㐀㕵ExtAxCP936丂狛狜.exe"; public const string Tests_DebugX86 = SampleTestsBuildDir + @"Debug\Tests_gta.exe"; public const string Tests_ReleaseX86 = SampleTestsBuildDir + @"Release\Tests_gta.exe"; diff --git "a/GoogleTestAdapter/\343\220\200\343\225\265ExtAxCP936\344\270\202\347\213\233\347\213\234/main.cpp" "b/GoogleTestAdapter/\343\220\200\343\225\265ExtAxCP936\344\270\202\347\213\233\347\213\234/main.cpp" new file mode 100644 index 000000000..0eefc1c8c --- /dev/null +++ "b/GoogleTestAdapter/\343\220\200\343\225\265ExtAxCP936\344\270\202\347\213\233\347\213\234/main.cpp" @@ -0,0 +1,3 @@ +int main() +{ +} \ No newline at end of file diff --git "a/GoogleTestAdapter/\343\220\200\343\225\265ExtAxCP936\344\270\202\347\213\233\347\213\234/\343\220\200\343\225\265ExtAxCP936\344\270\202\347\213\233\347\213\234.vcxproj" "b/GoogleTestAdapter/\343\220\200\343\225\265ExtAxCP936\344\270\202\347\213\233\347\213\234/\343\220\200\343\225\265ExtAxCP936\344\270\202\347\213\233\347\213\234.vcxproj" new file mode 100644 index 000000000..e4579e225 --- /dev/null +++ "b/GoogleTestAdapter/\343\220\200\343\225\265ExtAxCP936\344\270\202\347\213\233\347\213\234/\343\220\200\343\225\265ExtAxCP936\344\270\202\347\213\233\347\213\234.vcxproj" @@ -0,0 +1,92 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {8B27FD59-F03C-468E-B878-CFFC7484B7F7} + Win32Proj + 㐀㕵ExtAxCP936丂狛狜 + 8.1 + + + + + Application + true + v100 + v110 + v120 + v140 + v141 + Unicode + + + Application + false + v100 + v110 + v120 + v140 + v141 + true + Unicode + + + + + + + + + + + + + + + true + + + false + + + + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + + Console + true + + + + + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + + Console + true + + + + + + + + + \ No newline at end of file diff --git "a/GoogleTestAdapter/\343\220\200\343\225\265ExtAxCP936\344\270\202\347\213\233\347\213\234/\343\220\200\343\225\265ExtAxCP936\344\270\202\347\213\233\347\213\234.vcxproj.filters" "b/GoogleTestAdapter/\343\220\200\343\225\265ExtAxCP936\344\270\202\347\213\233\347\213\234/\343\220\200\343\225\265ExtAxCP936\344\270\202\347\213\233\347\213\234.vcxproj.filters" new file mode 100644 index 000000000..4327830c9 --- /dev/null +++ "b/GoogleTestAdapter/\343\220\200\343\225\265ExtAxCP936\344\270\202\347\213\233\347\213\234/\343\220\200\343\225\265ExtAxCP936\344\270\202\347\213\233\347\213\234.vcxproj.filters" @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file