diff options
Diffstat (limited to 'src/tools')
32 files changed, 8399 insertions, 0 deletions
diff --git a/src/tools/.gitmirror b/src/tools/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/tools/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt new file mode 100644 index 0000000000..5443e0ddb6 --- /dev/null +++ b/src/tools/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(crossgen) diff --git a/src/tools/GenClrDebugResource/.gitmirror b/src/tools/GenClrDebugResource/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/tools/GenClrDebugResource/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/tools/GenClrDebugResource/CMakeLists.txt b/src/tools/GenClrDebugResource/CMakeLists.txt new file mode 100644 index 0000000000..ad2598a373 --- /dev/null +++ b/src/tools/GenClrDebugResource/CMakeLists.txt @@ -0,0 +1,7 @@ +add_definitions(-MT) +add_executable(GenClrDebugResource GenClrDebugResource.cpp) + +target_link_libraries(GenClrDebugResource + ${STATIC_MT_CRT_LIB} + ${STATIC_MT_VCRT_LIB} +) diff --git a/src/tools/GenClrDebugResource/GenClrDebugResource.cpp b/src/tools/GenClrDebugResource/GenClrDebugResource.cpp new file mode 100644 index 0000000000..de016de845 --- /dev/null +++ b/src/tools/GenClrDebugResource/GenClrDebugResource.cpp @@ -0,0 +1,219 @@ +// 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. + +/* This app writes out the data for a special resource which is embedded in clr.dll + * The resource serves two purposes, to differentiate a random dll named clr.dll from + * an official microsoft clr dll (it isn't foolproof but it should prevent anyone from + * accidentally appearing to be a clr) and to provide file information about DAC and DBI + * which correspond to this build of the CLR. + */ + + +#include <stdlib.h> +#include <stdio.h> +#include <windows.h> +#include <clrinternal.h> + +char* g_appName; + + +struct ClrDebugResource +{ + DWORD dwVersion; + GUID signature; + DWORD dwDacTimeStamp; + DWORD dwDacSizeOfImage; + DWORD dwDbiTimeStamp; + DWORD dwDbiSizeOfImage; +}; + +BOOL +GetBinFileData(__in_z char* binFileName, DWORD* pTimeStamp, DWORD* pSizeOfImage) +{ + HANDLE binFileHandle; + DWORD peHeaderOffset; + ULONG done; + + binFileHandle = CreateFileA(binFileName, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, 0, NULL); + if (!binFileHandle || binFileHandle == INVALID_HANDLE_VALUE) + { + printf("%s: Unable to open '%s', %d\n", g_appName, + binFileName, GetLastError()); + goto error; + } + + // Read the 4 byte value at 0x3c, which is the offset to PE header + if (INVALID_SET_FILE_POINTER == SetFilePointer(binFileHandle, 0x3c, NULL, FILE_BEGIN)) + { + printf("%s: Unable to move file pointer '%s', %d\n", g_appName, + binFileName, GetLastError()); + goto error; + } + + if (!ReadFile(binFileHandle, &peHeaderOffset, 4, &done, NULL) || + done != 4) + { + printf("%s: Unable to read '%s', %d\n", g_appName, + binFileName, GetLastError()); + goto error; + } + + // Read the 4 byte value at 8 bytes after peHeader, that is the timestamp + if (INVALID_SET_FILE_POINTER == SetFilePointer(binFileHandle, peHeaderOffset + 8, NULL, FILE_BEGIN)) + { + printf("%s: Unable to move file pointer '%s', %d\n", g_appName, + binFileName, GetLastError()); + goto error; + } + + if (!ReadFile(binFileHandle, pTimeStamp, 4, &done, NULL) || + done != 4) + { + printf("%s: Unable to read '%s', %d\n", g_appName, + binFileName, GetLastError()); + goto error; + } + + // Read the 4 byte value at 80 bytes after peHeader, that is the sizeOfImage + if (INVALID_SET_FILE_POINTER == SetFilePointer(binFileHandle, peHeaderOffset + 80, NULL, FILE_BEGIN)) + { + printf("%s: Unable to move file pointer '%s', %d\n", g_appName, + binFileName, GetLastError()); + goto error; + } + + if (!ReadFile(binFileHandle, pSizeOfImage, 4, &done, NULL) || + done != 4) + { + printf("%s: Unable to read '%s', %d\n", g_appName, + binFileName, GetLastError()); + goto error; + } + + CloseHandle(binFileHandle); + return TRUE; + +error: + CloseHandle(binFileHandle); + return FALSE; +} + +void +Usage(void) +{ + printf("Usage: %s [options]\n", g_appName); + printf("Options are:\n"); + printf(" /out:<file> - output binary file that contains the raw resource data\n"); + printf(" /dac:<file> - path to mscordacwks that should be referenced\n"); + printf(" /dbi:<file> - path to mscordbi that should be referenced\n"); + printf(" /sku:<sku_name> - Either clr, coreclr, or phoneclr indicating the CLR sku\n"); +} + +void __cdecl +main(int argc, __in_z char** argv) +{ + char* outFile = NULL; + char* dacFile = NULL; + char* dbiFile = NULL; + char* sku = NULL; + + g_appName = argv[0]; + + while (--argc) + { + argv++; + + if (!strncmp(*argv, "/out:", 5)) + { + outFile = *argv + 5; + } + else if (!strncmp(*argv, "/dac:", 5)) + { + dacFile = *argv + 5; + } + else if (!strncmp(*argv, "/dbi:", 5)) + { + dbiFile = *argv + 5; + } + else if (!strncmp(*argv, "/sku:", 5)) + { + sku = *argv + 5; + } + else + { + Usage(); + exit(1); + } + } + + if (!outFile || !dacFile || !dbiFile || !sku) + { + Usage(); + exit(1); + } + + ClrDebugResource res; + res.dwVersion = 0; + + if(strcmp(sku, "clr")==0) + { + res.signature = CLR_ID_V4_DESKTOP; + } + else if(strcmp(sku, "coreclr")==0) + { + res.signature = CLR_ID_CORECLR; + } + else if(strcmp(sku, "phoneclr")==0) + { + res.signature = CLR_ID_PHONE_CLR; + } + else if (strcmp(sku, "onecoreclr") == 0) + { + res.signature = CLR_ID_ONECORE_CLR; + } + else + { + printf("Error: Unrecognized sku option: %s\n", sku); + Usage(); + exit(1); + } + + printf("%s: Reading data from DAC: %s\n", g_appName, dacFile); + if(!GetBinFileData(dacFile, &(res.dwDacTimeStamp), &(res.dwDacSizeOfImage))) + exit(1); + printf("%s: DAC timeStamp = 0x%x sizeOfImage = 0x%x\n", g_appName, res.dwDacTimeStamp, res.dwDacSizeOfImage); + + printf("%s: Reading data from DBI: %s\n", g_appName, dbiFile); + if(!GetBinFileData(dbiFile, &(res.dwDbiTimeStamp), &(res.dwDbiSizeOfImage))) + exit(1); + printf("%s: DBI timeStamp = 0x%x sizeOfImage = 0x%x\n", g_appName, res.dwDbiTimeStamp, res.dwDbiSizeOfImage); + + printf("%s: Writing binary resource file: %s\n", g_appName, outFile); + HANDLE outFileHandle = CreateFileA(outFile, GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, 0, NULL); + if (!outFileHandle || outFileHandle == INVALID_HANDLE_VALUE) + { + printf("%s: Unable to create '%s', %d\n", + g_appName, outFile, GetLastError()); + goto error; + } + + + DWORD done = 0; + if(!WriteFile(outFileHandle, &res, sizeof(res), &done, NULL) || done != sizeof(res)) + { + printf("%s: Unable to write file data '%s', %d\n", + g_appName, outFile, GetLastError()); + goto error; + } + + CloseHandle(outFileHandle); + printf("%s: Success. Returning 0\n", g_appName); + exit(0); + +error: + CloseHandle(outFileHandle); + exit(1); +} diff --git a/src/tools/GenClrDebugResource/GenClrDebugResource.nativeproj b/src/tools/GenClrDebugResource/GenClrDebugResource.nativeproj new file mode 100644 index 0000000000..9bd544d8d0 --- /dev/null +++ b/src/tools/GenClrDebugResource/GenClrDebugResource.nativeproj @@ -0,0 +1,26 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!--*****************************************************--> + <!--This MSBuild project file was automatically generated--> + <!--from the original SOURCES/DIRS file by the KBC tool.--> + <!--*****************************************************--> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + <!--Leaf project Properties--> + <PropertyGroup> + <OutputName>GenClrDebugResource</OutputName> + <TargetType>PROGRAM</TargetType> + <LinkSubsystem>console</LinkSubsystem> + <LinkGenerateManifest>true</LinkGenerateManifest> + <LinkAdditionalOptions>$(LinkAdditionalOptions) /MANIFEST</LinkAdditionalOptions> + <UseMsvcrt>false</UseMsvcrt> + </PropertyGroup> + <!--Leaf Project Items--> + <ItemGroup> + <CppCompile Include="GenClrDebugResource.cpp" /> + </ItemGroup> + <ItemGroup> + <RCResourceFile Include="native.rc" /> + </ItemGroup> + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" /> +</Project>
\ No newline at end of file diff --git a/src/tools/GenClrDebugResource/native.rc b/src/tools/GenClrDebugResource/native.rc new file mode 100644 index 0000000000..4c1007fd9a --- /dev/null +++ b/src/tools/GenClrDebugResource/native.rc @@ -0,0 +1,7 @@ +// 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. +#define FX_VER_FILEDESCRIPTION_STR "Microsoft\0" + +#include <fxver.h> +#include <fxver.rc> diff --git a/src/tools/InjectResource/.gitmirror b/src/tools/InjectResource/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/tools/InjectResource/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/tools/InjectResource/CMakeLists.txt b/src/tools/InjectResource/CMakeLists.txt new file mode 100644 index 0000000000..a4fd538662 --- /dev/null +++ b/src/tools/InjectResource/CMakeLists.txt @@ -0,0 +1,10 @@ +remove_definitions(-DUNICODE) +remove_definitions(-D_UNICODE) +add_definitions(-MT) + +add_executable(InjectResource InjectResource.cpp) + +target_link_libraries(InjectResource + ${STATIC_MT_CRT_LIB} + ${STATIC_MT_VCRT_LIB} +)
\ No newline at end of file diff --git a/src/tools/InjectResource/InjectResource.cpp b/src/tools/InjectResource/InjectResource.cpp new file mode 100644 index 0000000000..1d5470f3f9 --- /dev/null +++ b/src/tools/InjectResource/InjectResource.cpp @@ -0,0 +1,158 @@ +// 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. + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <windows.h> +#include <daccess.h> + +char* g_appName; + +#define MAX(x,y) ((x) > (y) ? (x) : (y)) + +void +AddBinaryResourceToDll(__in_z char* dllName, + __in_z char* resName, + PVOID resData, + ULONG resDataSize) +{ + // a retry loop in case we get transient file access issues + // does exponential backoff with retries after 1,2,4,8,16, and 32 seconds + for(int seconds = 0; seconds < 60; seconds = MAX(seconds*2,1)) + { + if(seconds != 0) + Sleep(seconds * 1000); + + HANDLE dllUpdate = BeginUpdateResourceA(dllName, FALSE); + if (!dllUpdate) + { + printf("Unable to open '%s' for update, error=%d\n", + dllName, GetLastError()); + continue; + } + + if (!UpdateResourceA(dllUpdate, + RT_RCDATA, + resName, + MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), + resData, + resDataSize)) + { + printf("Unable to update '%s', error=%d\n", + dllName, GetLastError()); + continue; + } + + if(!EndUpdateResource(dllUpdate, FALSE)) + { + printf("Unable to write updates to '%s', error=%d\n", + dllName, GetLastError()); + continue; + } + + return; + } + + printf("Stopping after excessive failures\n"); + exit(1); +} + +void +GetBinFileData(__in_z char* binFileName, PVOID* binData, PULONG binDataSize) +{ + HANDLE binFileHandle; + PVOID data; + ULONG size, done; + + binFileHandle = CreateFileA(binFileName, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, 0, NULL); + if (!binFileHandle || binFileHandle == INVALID_HANDLE_VALUE) + { + printf("Unable to open '%s', %d\n", + binFileName, GetLastError()); + exit(1); + } + + size = GetFileSize(binFileHandle, NULL); + data = malloc(size); + if (!data) + { + printf("Out of memory\n"); + exit(1); + } + + if (!ReadFile(binFileHandle, data, size, &done, NULL) || + done != size) + { + printf("Unable to read '%s', %d\n", + binFileName, GetLastError()); + exit(1); + } + + CloseHandle(binFileHandle); + + *binData = data; + *binDataSize = size; +} + +void +Usage(void) +{ + printf("Usage: %s [options]\n", g_appName); + printf("Options are:\n"); + printf(" /bin:<file> - Binary data to attach to DLL\n"); + printf(" /dll:<file> - DLL to modify\n"); + printf(" /name:<name> - resource name [Optional]\n"); + exit(1); +} + +void __cdecl +main(int argc, __in_z char** argv) +{ + char* binFile = NULL; + char* dllFile = NULL; + char* resName = NULL; + + g_appName = argv[0]; + + while (--argc) + { + argv++; + + if (!strncmp(*argv, "/bin:", 5)) + { + binFile = *argv + 5; + } + else if (!strncmp(*argv, "/dll:", 5)) + { + dllFile = *argv + 5; + } + else if (!strncmp(*argv, "/name:", 6)) + { + resName = *argv + 6; + } + else + { + Usage(); + } + } + + if (!binFile || !dllFile) + { + Usage(); + } + + PVOID resData; + ULONG resDataSize; + + GetBinFileData(binFile, &resData, &resDataSize); + + AddBinaryResourceToDll(dllFile, resName?resName:DACCESS_TABLE_RESOURCE, + resData, resDataSize); + + free(resData); + + printf("Updated %s\n", dllFile); +} diff --git a/src/tools/InjectResource/InjectResource.nativeproj b/src/tools/InjectResource/InjectResource.nativeproj new file mode 100644 index 0000000000..057d01f4ec --- /dev/null +++ b/src/tools/InjectResource/InjectResource.nativeproj @@ -0,0 +1,27 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!--*****************************************************--> + <!--This MSBuild project file was automatically generated--> + <!--from the original SOURCES/DIRS file by the KBC tool.--> + <!--*****************************************************--> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + <!--Leaf project Properties--> + <PropertyGroup> + <OutputName>InjectResource</OutputName> + <TargetType>PROGRAM</TargetType> + <LinkSubsystem>console</LinkSubsystem> + <BinplaceSymbols>true</BinplaceSymbols> + <LinkGenerateManifest>true</LinkGenerateManifest> + <LinkAdditionalOptions>$(LinkAdditionalOptions) /MANIFEST</LinkAdditionalOptions> + <UseMsvcrt>false</UseMsvcrt> + </PropertyGroup> + <!--Leaf Project Items--> + <ItemGroup> + <CppCompile Include="InjectResource.cpp" /> + </ItemGroup> + <ItemGroup> + <RCResourceFile Include="native.rc" /> + </ItemGroup> + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" /> +</Project>
\ No newline at end of file diff --git a/src/tools/InjectResource/native.rc b/src/tools/InjectResource/native.rc new file mode 100644 index 0000000000..4c1007fd9a --- /dev/null +++ b/src/tools/InjectResource/native.rc @@ -0,0 +1,7 @@ +// 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. +#define FX_VER_FILEDESCRIPTION_STR "Microsoft\0" + +#include <fxver.h> +#include <fxver.rc> diff --git a/src/tools/crossgen/.gitmirror b/src/tools/crossgen/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/tools/crossgen/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/tools/crossgen/CMakeLists.txt b/src/tools/crossgen/CMakeLists.txt new file mode 100644 index 0000000000..3b9c5ba8db --- /dev/null +++ b/src/tools/crossgen/CMakeLists.txt @@ -0,0 +1,75 @@ +project(crossgen) + +include(${CLR_DIR}/crossgen.cmake) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +include_directories(../util) +include_directories(../../pal/prebuilt/corerror) + +set(crossgen_SOURCES crossgen.cpp ../util/consoleargs.cpp) +if(WIN32) + set(crossgen_RESOURCES Native.rc) + add_definitions(-D_CRT_NON_CONFORMING_WCSTOK) +endif() + +if(CLR_CMAKE_PLATFORM_UNIX) + add_compile_options(-fPIE) + add_definitions(-DNO_NGENPDB) +endif(CLR_CMAKE_PLATFORM_UNIX) + +add_definitions(-DFX_VER_INTERNALNAME_STR=crossgen.exe) + +add_executable_clr(crossgen + ${crossgen_SOURCES} + ${crossgen_RESOURCES} +) + +if(FEATURE_MERGE_JIT_AND_ENGINE) + set(CLRJIT_CROSSGEN clrjit_crossgen) +endif(FEATURE_MERGE_JIT_AND_ENGINE) + +target_link_libraries(crossgen + cee_crossgen + mdcompiler_crossgen + mdruntime_crossgen + mdruntimerw_crossgen + mdhotdata_crossgen + corguids + ${CLRJIT_CROSSGEN} + gcinfo_crossgen + corzap_crossgen + mscorlib_crossgen + strongname_crossgen + utilcode_crossgen + v3binder_crossgen +) + +if(CLR_CMAKE_PLATFORM_UNIX) + target_link_libraries(crossgen + mscorrc_debug + coreclrpal + palrt + ) +else() + target_link_libraries(crossgen + advapi32 + ole32 + oleaut32 + uuid + user32 + version + shlwapi + bcrypt + mdwinmd_crossgen + ${STATIC_MT_CRT_LIB} + ${STATIC_MT_VCRT_LIB} + ) + +endif(CLR_CMAKE_PLATFORM_UNIX) + +add_subdirectory(../../zap/crossgen ../../zap/crossgen) +add_subdirectory(../../vm/crossgen ../../vm/crossgen) +add_subdirectory(../../vm/crossgen_mscorlib ../../vm/crossgen_mscorlib) + +# add the install targets +install_clr(crossgen) diff --git a/src/tools/crossgen/Native.rc b/src/tools/crossgen/Native.rc new file mode 100644 index 0000000000..25b5fe5e67 --- /dev/null +++ b/src/tools/crossgen/Native.rc @@ -0,0 +1,7 @@ +// 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. + +#define FX_VER_FILEDESCRIPTION_STR "Microsoft Common Language Runtime native cross compiler\0" + +#include "..\..\dlls\mscorrc\mscorrc.rc" diff --git a/src/tools/crossgen/compare.bat b/src/tools/crossgen/compare.bat new file mode 100644 index 0000000000..290cc06fc0 --- /dev/null +++ b/src/tools/crossgen/compare.bat @@ -0,0 +1,186 @@ +::mscorlib +::System +::System.Core +::System.Xml +::System.Configuration +::System.Drawing +::System.Data +::System.Windows.Forms +::System.Runtime.Remoting +::System.Serviceprocess +::System.Management +::Accessibility +::Microsoft.VisualBasic +::System.DirectoryServices +::System.Transactions +::System.Web.Services +::CustomMarshalers +::System.Configuration.Install +::System.Xaml +::WindowsBase +::System.Net.Http +::System.Xml.Linq +::System.Runtime.WindowsRuntime +::System.Runtime.WindowsRuntime.UI.Xaml +::System.Runtime.Serialization +::System.ServiceModel +::PresentationCore +::PresentationFramework +::System.EnterpriseServices +::System.Collections.Concurrent +::System.Collections +::System.ComponentModel.Annotations +::System.ComponentModel +::System.ComponentModel.EventBasedAsync +::System.Diagnostics.Contracts +::System.Diagnostics.Debug +::System.Diagnostics.Tools +::System.Diagnostics.Tracing +::System.Dynamic.Runtime +::System.Globalization +::System.IO +::System.Linq +::System.Linq.Expressions +::System.Linq.Parallel +::System.Linq.Queryable +::System.Net.Http.Rtc +::System.Net.NetworkInformation +::System.Net.Primitives +::System.Net.Requests +::System.ObjectModel +::System.Reflection +::System.Reflection.Emit +::System.Reflection.Emit.ILGeneration +::System.Reflection.Emit.Lightweight +::System.Reflection.Extensions +::System.Reflection.Primitives +::System.Resources.ResourceManager +::System.Runtime +::System.Runtime.Extensions +::System.Runtime.InteropServices +::System.Runtime.InteropServices.WindowsRuntime +::System.Runtime.Numerics +::System.Runtime.Serialization.Json +::System.Runtime.Serialization.Primitives +::System.Runtime.Serialization.Xml +::System.Security.Principal +::System.ServiceModel.Duplex +::System.ServiceModel.Http +::System.ServiceModel.NetTcp +::System.ServiceModel.Primitives +::System.ServiceModel.Security +::System.Text.Encoding +::System.Text.Encoding.Extensions +::System.Text.RegularExpressions +::System.Threading +::System.Threading.Tasks +::System.Threading.Tasks.Parallel +::System.Windows +::System.Xml.ReaderWriter +::System.Xml.XDocument +::System.Xml.XmlSerializer +::Windows.ApplicationModel +::Windows.Data +::Windows.Devices +::Windows.Foundation +::Windows.Globalization +::Windows.Graphics +::Windows.Management +::Windows.Media +::Windows.Networking +::Windows.Security +::Windows.Storage +::Windows.System +::Windows.UI +::Windows.UI.Xaml +::Windows.Web +::END_OF_LIST + +@echo off + +rem +rem This script compares ngen and crossgen output for framework assemblies +rem + +SETLOCAL ENABLEDELAYEDEXPANSION + +set BITNESS= +IF /I "%_BuildArch%" == "amd64" set BITNESS=64 +set FRAMEWORKDIR=%SYSTEMROOT%\Microsoft.NET\Framework%BITNESS%\%COMPlus_Version% +IF "%BITNESS%" == "" set BITNESS=32 + +set NATIVEIMAGEPATH=%FRAMEWORKDIR%\assembly\NativeImages_%COMPlus_Version%_%BITNESS% + +rem rmdir /S /Q %NATIVEIMAGEPATH% +rem %FRAMEWORKDIR%\ngen install mscorlib +rem %FRAMEWORKDIR%\ngen update + +%FRAMEWORKDIR%\gacutil /if %_NTTREE%\System.Runtime.WindowsRuntime.dll +%FRAMEWORKDIR%\gacutil /if %_NTTREE%\System.Runtime.WindowsRuntime.UI.Xaml.dll + +set ILIMAGEPATH=%_NTTREE%\il +rmdir /S /Q %ILIMAGEPATH% +if not exist %ILIMAGEPATH% mkdir %ILIMAGEPATH% + +rem Collect all files from the GAC into ILIMAGEPATH directory to guaranteed that we get the exact same IL images +rem between ngen and crossgen. It is important on non-x86 builds because of non-x86 layouts pull files from x86 build. +forfiles /P %FRAMEWORKDIR%\assembly\GAC_%BITNESS% /M *.dll /S /C "cmd /c copy @path %ILIMAGEPATH%\@file > nul" +forfiles /P %FRAMEWORKDIR%\assembly\GAC_MSIL /M *.dll /S /C "cmd /c copy @path %ILIMAGEPATH%\@file > nul" +rem clr.dll and clrjit.dll are required for timestamps +copy %FRAMEWORKDIR%\clr.dll %ILIMAGEPATH%\clr.dll >nul +copy %FRAMEWORKDIR%\clrjit.dll %ILIMAGEPATH%\clrjit.dll >nul + +set CROSSGENIMAGEPATH=%_NTTREE%\ni +rmdir /S /Q %CROSSGENIMAGEPATH% +if not exist %CROSSGENIMAGEPATH% mkdir %CROSSGENIMAGEPATH% + +set WINMDPATH=%WINDIR%\System32\WinMetadata + +set SELF=%~fd0 +set FAILED= + +for /f "eol=; usebackq tokens=1,2,3* delims=,:" %%I in ("%SELF%") DO ( + if "%%I"=="END_OF_LIST" goto LDone + call :ProcessFile %%I + if "!FAILED!"=="1" goto LFailed +) + +:LDone +echo DONE +exit /B 0 + +:LFailed +echo FAILED +exit /B 1 + +:ProcessFile + +set FILEPATH= +call :ProbeFile %ILIMAGEPATH%\%1.dll +call :ProbeFile %WINMDPATH%\%1.winmd + +if "%FILEPATH%" == "" ( echo File not found: %1 & goto LError ) + +echo. +echo ========= COMPILE and COMPARE %1 ========== +echo ngen install /nodependencies %FILEPATH% +ngen install /nodependencies %FILEPATH% +echo. +echo %_NTTREE%\crossgen /platform_assemblies_paths %ILIMAGEPATH%;%CROSSGENIMAGEPATH% /Platform_Winmd_Paths %WINMDPATH% /in %FILEPATH% /out %CROSSGENIMAGEPATH%\%1.ni.dll +%_NTTREE%\crossgen /platform_assemblies_paths %ILIMAGEPATH%;%CROSSGENIMAGEPATH% /Platform_Winmd_Paths %WINMDPATH% /in %FILEPATH% /out %CROSSGENIMAGEPATH%\%1.ni.dll +IF NOT "%ERRORLEVEL%"=="0" set FAILED=1 +echo. +forfiles /P %NATIVEIMAGEPATH% /M %1.ni.dll /S /C "cmd /c echo Compare: @path & fc /B @path %CROSSGENIMAGEPATH%\%1.ni.dll > %CROSSGENIMAGEPATH%\diff.txt & IF NOT ERRORLEVEL 1 del %CROSSGENIMAGEPATH%\diff.txt" +IF not exist %CROSSGENIMAGEPATH%\diff.txt goto LExit +echo ----- DIFFERENT ----- +:LError +set FAILED=1 +goto LExit + +:ProbeFile +if NOT "%FILEPATH%" == "" goto LExit +if NOT exist "%1" goto LExit +set FILEPATH=%1 +goto LExit + +:LExit diff --git a/src/tools/crossgen/crossgen.cpp b/src/tools/crossgen/crossgen.cpp new file mode 100644 index 0000000000..960bcf51cd --- /dev/null +++ b/src/tools/crossgen/crossgen.cpp @@ -0,0 +1,1031 @@ +// 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. + +// +// TO DO: we currently use raw printf() for output. Maybe we need to pick up something like ngen's Output() handling +// to handle multiple code pages, etc, better. + +#include <stdio.h> +#include <fcntl.h> +#include <io.h> + +#include <windows.h> +#include <fxver.h> +#include <mscorsvc.h> + +#include "palclr.h" + +#include <sstring.h> +#include "ex.h" + +#include "coregen.h" +#include "consoleargs.h" + +// Return values from wmain() in case of error +enum ReturnValues +{ + FAILURE_RESULT = 1, + CLR_INIT_ERROR = -2, + ASSEMBLY_NOT_FOUND = -3, + INVALID_ARGUMENTS = -4 +}; + +#define NumItems(s) (sizeof(s) / sizeof(s[0])) + +STDAPI CreatePDBWorker(LPCWSTR pwzAssemblyPath, LPCWSTR pwzPlatformAssembliesPaths, LPCWSTR pwzTrustedPlatformAssemblies, LPCWSTR pwzPlatformResourceRoots, LPCWSTR pwzAppPaths, LPCWSTR pwzAppNiPaths, LPCWSTR pwzPdbPath, BOOL fGeneratePDBLinesInfo, LPCWSTR pwzManagedPdbSearchPath, LPCWSTR pwzPlatformWinmdPaths, LPCWSTR pwzDiasymreaderPath); +STDAPI NGenWorker(LPCWSTR pwzFilename, DWORD dwFlags, LPCWSTR pwzPlatformAssembliesPaths, LPCWSTR pwzTrustedPlatformAssemblies, LPCWSTR pwzPlatformResourceRoots, LPCWSTR pwzAppPaths, LPCWSTR pwzOutputFilename=NULL, LPCWSTR pwzPlatformWinmdPaths=NULL, ICorSvcLogger *pLogger = NULL, LPCWSTR pwszCLRJITPath = nullptr); +void SetSvcLogger(ICorSvcLogger *pCorSvcLogger); +#ifdef FEATURE_CORECLR +void SetMscorlibPath(LPCWSTR wzSystemDirectory); +#endif + +/* --------------------------------------------------------------------------- * + * Console stuff + * --------------------------------------------------------------------------- */ + +void Output(LPCWSTR str) +{ + wprintf(W("%s"), str); +} + +void Outputf(LPCWSTR szFormat, ...) +{ + va_list args; + va_start(args, szFormat); + vfwprintf(stdout, szFormat, args); + va_end(args); +} + +void OutputErr(LPCWSTR str) +{ + fwprintf(stderr, W("%s"), str); +} + +void OutputErrf(LPCWSTR szFormat, ...) +{ + va_list args; + va_start(args, szFormat); + vfwprintf(stderr, szFormat, args); + va_end(args); +} + +void ErrorHR(HRESULT hr) +{ + OutputErrf(W("Error: failed to initialize CoreCLR: 0x%08x\n"), hr); +} + +void ErrorWin32(DWORD err) +{ + ErrorHR(HRESULT_FROM_WIN32(err)); +} + +// Some error messages are useless to callers, so make them generic, except in debug builds, where we want as much +// information as possible. + +#ifdef _DEBUG +#define ERROR_HR(msg,hr) Outputf(msg, hr) +#define ERROR_WIN32(msg,err) Outputf(msg, err) +#else // _DEBUG +#define ERROR_HR(msg,hr) ErrorHR(hr) +#define ERROR_WIN32(msg,err) ErrorWin32(err) +#endif // _DEBUG + + +void PrintLogoHelper() +{ +#ifdef FEATURE_CORECLR + Output(W("Microsoft (R) CoreCLR Native Image ")); +#else + Output(W("Microsoft (R) CLR Native Image ")); +#endif + Outputf(W("Generator - Version %S\n"), VER_FILEVERSION_STR); + Outputf(W("%S\n"), VER_LEGALCOPYRIGHT_LOGO_STR); + Output(W("\n")); +} + +void PrintUsageHelper() +{ + // Always print the logo when we print the usage, even if they've specified /nologo and we've parsed that already. + PrintLogoHelper(); + + Output( + W("Usage: crossgen [args] <assembly name>\n") + W("\n") + W(" /? or /help - Display this screen\n") + W(" /nologo - Prevents displaying the logo\n") + W(" @response.rsp - Process command line arguments from specified\n") + W(" response file\n") +#ifdef FEATURE_CORECLR + W(" /partialtrust - Assembly will be run in a partial trust domain.\n") +#endif + W(" /in <file> - Specifies input filename (optional)\n") + W(" /out <file> - Specifies output filename (optional)\n") +#ifdef FEATURE_CORECLR + W(" /Trusted_Platform_Assemblies <path[;path]>\n") + W(" - List of assemblies treated as trusted platform\n") + W(" - Cannot be used with Platform_Assemblies_Paths\n") + W(" /Platform_Resource_Roots <path[;path]>\n") + W(" - List of paths containing localized assembly directories\n") + W(" /App_Paths <path> - List of paths containing user-application assemblies and resources\n") +#ifndef NO_NGENPDB + W(" /App_Ni_Paths <path[;path]>\n") + W(" - List of paths containing user-application native images\n") + W(" - Must be used with /CreatePDB switch\n") +#endif // NO_NGENPDB +#endif // FEATURE_CORECLR + + W(" /Platform_Assemblies_Paths\n") + W(" - List of paths containing target platform assemblies\n") +#ifdef FEATURE_CORECLR + // If Platform_Assemblies_Paths, we will use it to build the TPA list and thus, + // TPA list cannot be explicitly specified. + W(" - Cannot be used with Trusted_Platform_Assemblies\n") +#endif // FEATURE_CORECLR + +#ifdef FEATURE_COMINTEROP + W(" /Platform_Winmd_Paths\n") + W(" - List of paths containing target platform WinMDs used\n") + W(" for emulating RoResolveNamespace\n") +#endif + W(" /MissingDependenciesOK\n") + W(" - Specifies that crossgen should attempt not to fail\n") + W(" if a dependency is missing.\n") +#if 0 + W(" /Tuning - Generate an instrumented image to collect\n") + W(" scenario traces, which can be used with ibcmerge.exe\n") +#endif +#if defined(FEATURE_CORECLR) && !defined(FEATURE_MERGE_JIT_AND_ENGINE) + W(" /JITPath\n") + W(" - Specifies the absolute file path to JIT compiler to be used.\n") +#endif // defined(FEATURE_CORECLR) && !defined(FEATURE_MERGE_JIT_AND_ENGINE) +#ifdef FEATURE_READYTORUN_COMPILER + W(" /ReadyToRun - Generate images resilient to the runtime and\n") + W(" dependency versions\n") +#endif +#ifdef FEATURE_WINMD_RESILIENT + W(" WinMD Parameters\n") + W(" /WinMDResilient - Generate images resilient to WinMD dependency changes.\n") +#endif +#ifdef FEATURE_CORECLR + W(" Size on Disk Parameters\n") + W(" /NoMetaData - Do not copy metadata and IL into native image.\n") +#endif // FEATURE_CORECLR +#ifndef NO_NGENPDB + W(" Debugging Parameters\n") + W(" /CreatePDB <Dir to store PDB> [/lines [<search path for managed PDB>] ]\n") + W(" When specifying /CreatePDB, the native image should be created\n") + W(" first, and <assembly name> should be the path to the NI.\n") +#ifdef FEATURE_CORECLR + W(" /DiasymreaderPath <Path to diasymreader.dll>\n") + W(" - Specifies the absolute file path to diasymreader.dll to be used.\n") +#endif // FEATURE_CORECLR +#elif defined(FEATURE_PERFMAP) + W(" Debugging Parameters\n") + W(" /CreatePerfMap <Dir to store perf map>\n") + W(" When specifying /CreatePerfMap, the native image should be created\n") + W(" first, and <assembly name> should be the path to the NI.\n") +#endif + ); +} + +class CrossgenLogger : public ICorSvcLogger +{ + STDMETHODIMP_(ULONG) AddRef() {return E_NOTIMPL;} + STDMETHODIMP_(ULONG) Release() {return E_NOTIMPL;} + STDMETHODIMP QueryInterface(REFIID riid,void ** ppv) + { + if (ppv==0) + return E_POINTER; + + *ppv = NULL; + + if (IsEqualIID(riid, IID_ICorSvcLogger) || IsEqualIID(riid, IID_IUnknown)) + { + *ppv = this; + return S_OK; + } + else + { + return E_NOINTERFACE; + } + } + + HRESULT STDMETHODCALLTYPE Log( + /*[in] */CorSvcLogLevel logLevel, + /*[in] */BSTR message + ) + { + if (logLevel == LogLevel_Error) + OutputErr(message); + else + Output(message); + return S_OK; + } +}; + +CrossgenLogger g_CrossgenLogger; + +// +// Tests whether szArg, the currently indexed argv matches the specified parameter name, szTestParamName. +// Specify szTestParamName without a switch. This method handles testing for - and / switches. +// +bool MatchParameter(LPCWSTR szArg, LPCWSTR szTestParamName) +{ + if (wcslen(szArg) == 0) + { + return false; + } + + if (szArg[0] != W('/') && szArg[0] != W('-')) + { + return false; + } + + return !_wcsicmp(szArg + 1, szTestParamName) || !_wcsicmp(szArg + 1, szTestParamName); +} + +// +// Returns true if pwzString ends with the string in pwzCandidate +// Ignores case +// +bool StringEndsWith(LPCWSTR pwzString, LPCWSTR pwzCandidate) +{ + size_t stringLength = wcslen(pwzString); + size_t candidateLength = wcslen(pwzCandidate); + + if (candidateLength > stringLength || stringLength == 0 || candidateLength == 0) + { + return false; + } + + LPCWSTR pwzStringEnd = pwzString + stringLength - candidateLength; + + return !_wcsicmp(pwzStringEnd, pwzCandidate); +} + +#ifdef FEATURE_CORECLR +// +// When using the Phone binding model (TrustedPlatformAssemblies), automatically +// detect which path CoreLib.[ni.]dll lies in. +// +bool ComputeMscorlibPathFromTrustedPlatformAssemblies(SString& pwzMscorlibPath, LPCWSTR pwzTrustedPlatformAssemblies) +{ + LPWSTR wszTrustedPathCopy = new WCHAR[wcslen(pwzTrustedPlatformAssemblies) + 1]; + wcscpy_s(wszTrustedPathCopy, wcslen(pwzTrustedPlatformAssemblies) + 1, pwzTrustedPlatformAssemblies); + wchar_t *context; + LPWSTR wszSingleTrustedPath = wcstok_s(wszTrustedPathCopy, PATH_SEPARATOR_STR_W, &context); + + while (wszSingleTrustedPath != NULL) + { + size_t pathLength = wcslen(wszSingleTrustedPath); + // Strip off enclosing quotes, if present + if (wszSingleTrustedPath[0] == W('\"') && wszSingleTrustedPath[pathLength-1] == W('\"')) + { + wszSingleTrustedPath[pathLength-1] = '\0'; + wszSingleTrustedPath++; + } + + if (StringEndsWith(wszSingleTrustedPath, DIRECTORY_SEPARATOR_STR_W CoreLibName_IL_W) || + StringEndsWith(wszSingleTrustedPath, DIRECTORY_SEPARATOR_STR_W CoreLibName_NI_W)) + { + pwzMscorlibPath.Set(wszSingleTrustedPath); + SString::Iterator pwzSeparator = pwzMscorlibPath.End(); + bool retval = true; + + if (!SUCCEEDED(CopySystemDirectory(pwzMscorlibPath, pwzMscorlibPath))) + { + retval = false; + } + + delete [] wszTrustedPathCopy; + return retval; + } + + wszSingleTrustedPath = wcstok_s(NULL, PATH_SEPARATOR_STR_W, &context); + } + delete [] wszTrustedPathCopy; + + return false; +} + +// Given a path terminated with "\\" and a search mask, this function will add +// the enumerated files, corresponding to the search mask, from the path into +// the refTPAList. +void PopulateTPAList(SString path, LPCWSTR pwszMask, SString &refTPAList, bool fCompilingMscorlib, bool fCreatePDB) +{ + _ASSERTE(path.GetCount() > 0); + ClrDirectoryEnumerator folderEnumerator(path.GetUnicode(), pwszMask); + + while (folderEnumerator.Next()) + { + // Got a valid enumeration handle and the data about the first file. + DWORD dwAttributes = folderEnumerator.GetFileAttributes(); + if ((!(dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) && (!(dwAttributes & FILE_ATTRIBUTE_DEVICE))) + { + bool fAddDelimiter = (refTPAList.GetCount() > 0)?true:false; + bool fAddFileToTPAList = true; + LPCWSTR pwszFilename = folderEnumerator.GetFileName(); + if (fCompilingMscorlib) + { + // When compiling CoreLib, no ".ni.dll" should be on the TPAList. + if (StringEndsWith((LPWSTR)pwszFilename, W(".ni.dll"))) + { + fAddFileToTPAList = false; + } + } + else + { + // When creating PDBs, we must ensure that .ni.dlls are in the TPAList + if (!fCreatePDB) + { + // Only CoreLib's ni.dll should be in the TPAList for the compilation of non-mscorlib assemblies. + if (StringEndsWith((LPWSTR)pwszFilename, W(".ni.dll"))) + { + if (!StringEndsWith((LPWSTR)pwszFilename, CoreLibName_NI_W)) + { + fAddFileToTPAList = false; + } + } + } + + // Ensure that CoreLib's IL version is also not on the TPAlist for this case. + if (StringEndsWith((LPWSTR)pwszFilename, CoreLibName_IL_W)) + { + fAddFileToTPAList = false; + } + } + + if (fAddFileToTPAList) + { + if (fAddDelimiter) + { + // Add the path delimiter if we already have entries in the TPAList + refTPAList.Append(PATH_SEPARATOR_CHAR_W); + } + // Add the path to the TPAList + refTPAList.Append(path); + refTPAList.Append(pwszFilename); + } + } + } + } + +// Given a semi-colon delimited set of absolute folder paths (pwzPlatformAssembliesPaths), this function +// will enumerate all EXE/DLL modules in those folders and add them to the TPAList buffer (refTPAList). +void ComputeTPAListFromPlatformAssembliesPath(LPCWSTR pwzPlatformAssembliesPaths, SString &refTPAList, bool fCompilingMscorlib, bool fCreatePDB) +{ + // We should have a valid pointer to the paths + _ASSERTE(pwzPlatformAssembliesPaths != NULL); + + SString ssPlatformAssembliesPath(pwzPlatformAssembliesPaths); + + // Platform Assemblies Path List is semi-colon delimited + if(ssPlatformAssembliesPath.GetCount() > 0) + { + SString::CIterator start = ssPlatformAssembliesPath.Begin(); + SString::CIterator itr = ssPlatformAssembliesPath.Begin(); + SString::CIterator end = ssPlatformAssembliesPath.End(); + SString qualifiedPath; + + while (itr != end) + { + start = itr; + BOOL found = ssPlatformAssembliesPath.Find(itr, PATH_SEPARATOR_CHAR_W); + if (!found) + { + itr = end; + } + + SString qualifiedPath(ssPlatformAssembliesPath,start,itr); + + if (found) + { + itr++; + } + + unsigned len = qualifiedPath.GetCount(); + + if (len > 0) + { + if (qualifiedPath[len-1]!=DIRECTORY_SEPARATOR_CHAR_W) + { + qualifiedPath.Append(DIRECTORY_SEPARATOR_CHAR_W); + } + + // Enumerate the EXE/DLL modules within this path and add them to the TPAList + EX_TRY + { + PopulateTPAList(qualifiedPath, W("*.exe"), refTPAList, fCompilingMscorlib, fCreatePDB); + PopulateTPAList(qualifiedPath, W("*.dll"), refTPAList, fCompilingMscorlib, fCreatePDB); + } + EX_CATCH + { + Outputf(W("Warning: Error enumerating files under %s.\n"), qualifiedPath.GetUnicode()); + } + EX_END_CATCH(SwallowAllExceptions); + } + } + } +} +#endif // FEATURE_CORECLR + +extern HMODULE g_hThisInst; + +int _cdecl wmain(int argc, __in_ecount(argc) WCHAR **argv) +{ +#ifndef FEATURE_PAL + g_hThisInst = WszGetModuleHandle(NULL); +#endif + + ///////////////////////////////////////////////////////////////////////// + // + // Parse the arguments + // + bool fDisplayLogo = true; + DWORD dwFlags = 0; + LPCWSTR pwzFilename = NULL; + LPCWSTR pwzPlatformResourceRoots = nullptr; + LPCWSTR pwzTrustedPlatformAssemblies = nullptr; + LPCWSTR pwzAppPaths = nullptr; + LPCWSTR pwzAppNiPaths = nullptr; + LPCWSTR pwzPlatformAssembliesPaths = nullptr; + LPCWSTR pwzPlatformWinmdPaths = nullptr; + StackSString wzDirectoryToStorePDB; + bool fCreatePDB = false; + bool fGeneratePDBLinesInfo = false; + LPWSTR pwzSearchPathForManagedPDB = NULL; + LPCWSTR pwzOutputFilename = NULL; + LPCWSTR pwzPublicKeys = nullptr; + +#if defined(FEATURE_CORECLR) && !defined(FEATURE_MERGE_JIT_AND_ENGINE) + LPCWSTR pwszCLRJITPath = nullptr; +#endif // defined(FEATURE_CORECLR) && !defined(FEATURE_MERGE_JIT_AND_ENGINE) + + LPCWSTR pwzDiasymreaderPath = nullptr; + + HRESULT hr; + +#ifndef PLATFORM_UNIX + // This is required to properly display Unicode characters + _setmode(_fileno(stdout), _O_U8TEXT); +#endif + + // Skip this executable path + argv++; + argc--; + + ConsoleArgs consoleArgs; + int argc2; + LPWSTR *argv2; + + if (argc == 0) + { + PrintUsageHelper(); + exit(INVALID_ARGUMENTS); + } + + if (!consoleArgs.ExpandResponseFiles(argc, argv, &argc2, &argv2)) + { + if (consoleArgs.ErrorMessage() != nullptr) + { + wprintf(consoleArgs.ErrorMessage()); + exit(FAILURE_RESULT); + } + } + + argc = argc2; + argv = argv2; + + // By default, Crossgen will assume code-generation for fulltrust domains unless /PartialTrust switch is specified + dwFlags |= NGENWORKER_FLAGS_FULLTRUSTDOMAIN; + +#ifdef FEATURE_CORECLR + // By default, Crossgen will generate readytorun images unless /FragileNonVersionable switch is specified + dwFlags |= NGENWORKER_FLAGS_READYTORUN; +#endif + + while (argc > 0) + { + if (MatchParameter(*argv, W("?")) + || MatchParameter(*argv, W("help"))) + { + PrintUsageHelper(); + exit(INVALID_ARGUMENTS); + } + else if (MatchParameter(*argv, W("nologo"))) + { + fDisplayLogo = false; + } + else if (MatchParameter(*argv, W("Tuning"))) + { + dwFlags |= NGENWORKER_FLAGS_TUNING; + } + else if (MatchParameter(*argv, W("MissingDependenciesOK"))) + { + dwFlags |= NGENWORKER_FLAGS_MISSINGDEPENDENCIESOK; + } +#ifdef FEATURE_CORECLR + else if (MatchParameter(*argv, W("PartialTrust"))) + { + // Clear the /fulltrust flag + dwFlags = dwFlags & ~NGENWORKER_FLAGS_FULLTRUSTDOMAIN; + } + else if (MatchParameter(*argv, W("FullTrust"))) + { + // Keep the "/fulltrust" switch around but let it be no-nop. Without this, any usage of /fulltrust will result in crossgen command-line + // parsing failure. Considering that scripts all over (CLR, Phone Build, etc) specify that switch, we let it be as opposed to going + // and fixing all the scripts. + // + // We dont explicitly set the flag here again so that if "/PartialTrust" is specified, then it will successfully override the default + // fulltrust behaviour. + } +#if !defined(FEATURE_MERGE_JIT_AND_ENGINE) + else if (MatchParameter(*argv, W("JITPath")) && (argc > 1)) + { + pwszCLRJITPath = argv[1]; + + // skip JIT Path + argv++; + argc--; + } +#endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE) +#endif +#ifdef FEATURE_WINMD_RESILIENT + else if (MatchParameter(*argv, W("WinMDResilient"))) + { + dwFlags |= NGENWORKER_FLAGS_WINMD_RESILIENT; + } +#endif +#ifdef FEATURE_READYTORUN_COMPILER + else if (MatchParameter(*argv, W("ReadyToRun"))) + { + dwFlags |= NGENWORKER_FLAGS_READYTORUN; + } + else if (MatchParameter(*argv, W("FragileNonVersionable"))) + { + dwFlags &= ~NGENWORKER_FLAGS_READYTORUN; + } +#endif +#ifdef FEATURE_CORECLR + else if (MatchParameter(*argv, W("NoMetaData"))) + { + dwFlags |= NGENWORKER_FLAGS_NO_METADATA; + } +#endif + else if (MatchParameter(*argv, W("out"))) + { + if (pwzOutputFilename != NULL) + { + Output(W("Cannot specify multiple output files.\n")); + exit(INVALID_ARGUMENTS); + } + pwzOutputFilename = argv[1]; + argv++; + argc--; + } + else if (MatchParameter(*argv, W("in"))) + { + if (pwzFilename != NULL) + { + Output(W("Cannot specify multiple input files.\n")); + exit(INVALID_ARGUMENTS); + } + pwzFilename = argv[1]; + argv++; + argc--; + } +#ifdef FEATURE_CORECLR + else if (MatchParameter(*argv, W("Trusted_Platform_Assemblies")) && (argc > 1)) + { + pwzTrustedPlatformAssemblies = argv[1]; + + // skip path list + argv++; + argc--; + } + else if (MatchParameter(*argv, W("Platform_Resource_Roots")) && (argc > 1)) + { + pwzPlatformResourceRoots = argv[1]; + + // skip path list + argv++; + argc--; + } + else if (MatchParameter(*argv, W("App_Paths")) && (argc > 1)) + { + pwzAppPaths = argv[1]; + + // skip User app path + argv++; + argc--; + } +#ifndef NO_NGENPDB + else if (MatchParameter(*argv, W("App_Ni_Paths")) && (argc > 1)) + { + pwzAppNiPaths = argv[1]; + + // skip User app path + argv++; + argc--; + } +#endif // NO_NGENPDB +#endif // FEATURE_CORECLR + else if (MatchParameter(*argv, W("Platform_Assemblies_Paths")) && (argc > 1)) + { + pwzPlatformAssembliesPaths = argv[1]; + + // skip path list + argv++; + argc--; + } +#ifdef FEATURE_COMINTEROP + else if (MatchParameter(*argv, W("Platform_Winmd_Paths")) && (argc > 1)) + { + pwzPlatformWinmdPaths = argv[1]; + + // skip User app path + argv++; + argc--; + } +#endif // FEATURE_COMINTEROP +#ifndef NO_NGENPDB + else if (MatchParameter(*argv, W("CreatePDB")) && (argc > 1)) + { + // syntax: /CreatePDB <directory to store PDB> [/lines [<search path for managed PDB>] ] + + // Parse: /CreatePDB + fCreatePDB = true; + argv++; + argc--; + + // Clear the /fulltrust flag - /CreatePDB does not work with any other flags. + dwFlags = dwFlags & ~(NGENWORKER_FLAGS_FULLTRUSTDOMAIN | NGENWORKER_FLAGS_READYTORUN); + + // Parse: <directory to store PDB> + wzDirectoryToStorePDB.Set(argv[0]); + argv++; + argc--; + + // Ensure output dir ends in a backslash, or else diasymreader has issues + if (wzDirectoryToStorePDB[wzDirectoryToStorePDB.GetCount()-1] != DIRECTORY_SEPARATOR_CHAR_W) + { + wzDirectoryToStorePDB.Append(DIRECTORY_SEPARATOR_STR_W); + } + + if (argc == 0) + { + Output(W("The /CreatePDB switch requires <directory to store PDB> and <assembly name>.\n")); + exit(FAILURE_RESULT); + } + + // [/lines [<search path for managed PDB>] ] + if (MatchParameter(*argv, W("lines")) && (argc > 1)) + { + // Parse: /lines + fGeneratePDBLinesInfo = true; + argv++; + argc--; + + if (argc == 0) + { + Output(W("The /CreatePDB switch requires <directory to store PDB> and <assembly name>.\n")); + exit(FAILURE_RESULT); + } + + if (argc > 1) + { + // Parse: <search path for managed PDB> + pwzSearchPathForManagedPDB = argv[0]; + argv++; + argc--; + } + } + + // Undo last arg iteration, since we do it for all cases at the bottom of + // the loop + argv--; + argc++; + } +#ifdef FEATURE_CORECLR + else if (MatchParameter(*argv, W("DiasymreaderPath")) && (argc > 1)) + { + pwzDiasymreaderPath = argv[1]; + + // skip diasymreader Path + argv++; + argc--; + } +#endif // FEATURE_CORECLR +#endif // NO_NGENPDB +#ifdef FEATURE_PERFMAP + else if (MatchParameter(*argv, W("CreatePerfMap")) && (argc > 1)) + { + // syntax: /CreatePerfMap <directory to store perfmap> + + // Parse: /CreatePerfMap + // NOTE: We use the same underlying PDB logic. + fCreatePDB = true; + argv++; + argc--; + + // Clear the /fulltrust flag - /CreatePerfMap does not work with any other flags. + dwFlags = dwFlags & ~NGENWORKER_FLAGS_FULLTRUSTDOMAIN; + + // Clear the /ready to run flag - /CreatePerfmap does not work with any other flags. + dwFlags = dwFlags & ~NGENWORKER_FLAGS_READYTORUN; + + // Parse: <directory to store PDB> + wzDirectoryToStorePDB.Set(argv[0]); + argv++; + argc--; + + // Ensure output dir ends in a backslash + if (wzDirectoryToStorePDB[wcslen(wzDirectoryToStorePDB)-1] != DIRECTORY_SEPARATOR_CHAR_W) + { + wzDirectoryToStorePDB.Append(DIRECTORY_SEPARATOR_STR_W); + } + + if (argc == 0) + { + Output(W("The /CreatePerfMap switch requires <directory to store perfmap> and <assembly name>.\n")); + exit(FAILURE_RESULT); + } + + // Undo last arg iteration, since we do it for all cases at the bottom of + // the loop + argv--; + argc++; + } +#endif // FEATURE_PERFMAP + else + { + if (argc == 1) + { +#if !defined(FEATURE_PAL) + // When not running on Mac, which can have forward-slash pathnames, we know + // a command switch here means an invalid argument. + if (*argv[0] == W('-') || *argv[0] == W('/')) + { + Outputf(W("Invalid parameter: %s\n"), *argv); + exit(INVALID_ARGUMENTS); + } +#endif //!FEATURE_PAL + // The last thing on the command line is an assembly name or path, and + // because we got this far is not an argument like /nologo. Because this + // code works on Mac, with forward-slash pathnames, we can't assume + // anything with a forward slash is an argument. So we just always + // assume the last thing on the command line must be an assembly name. + + if (pwzFilename != NULL) + { + Output(W("Cannot use /In and specify an input file as the last argument.\n")); + exit(INVALID_ARGUMENTS); + } + + pwzFilename = *argv; + break; + } + else + { + Outputf(W("Invalid parameter: %s\n"), *argv); + exit(INVALID_ARGUMENTS); + } + } + + argv++; + argc--; + } + + if (pwzFilename == NULL) + { + Output(W("You must specify an assembly to compile\n")); + exit(INVALID_ARGUMENTS); + } + + if (fCreatePDB && (dwFlags != 0)) + { + Output(W("The /CreatePDB switch cannot be used with other switches, except /lines and the various path switches.\n")); + exit(FAILURE_RESULT); + } + + if (pwzAppNiPaths != nullptr && !fCreatePDB) + { + Output(W("The /App_Ni_Paths switch can only be used with the /CreatePDB switch.\n")); + exit(FAILURE_RESULT); + } + +#if defined(FEATURE_CORECLR) && !defined(FEATURE_MERGE_JIT_AND_ENGINE) + if (pwszCLRJITPath != nullptr && fCreatePDB) + { + Output(W("The /JITPath switch can not be used with the /CreatePDB switch.\n")); + exit(FAILURE_RESULT); + } +#endif // defined(FEATURE_CORECLR) && !defined(FEATURE_MERGE_JIT_AND_ENGINE) + +#if defined(FEATURE_CORECLR) && !defined(NO_NGENPDB) + if (pwzDiasymreaderPath != nullptr && !fCreatePDB) + { + Output(W("The /DiasymreaderPath switch can only be used with the /CreatePDB switch.\n")); + exit(FAILURE_RESULT); + } +#endif // defined(FEATURE_CORECLR) && !defined(NO_NGENPDB) + +#if defined(FEATURE_CORECLR) + if ((pwzTrustedPlatformAssemblies != nullptr) && (pwzPlatformAssembliesPaths != nullptr)) + { + Output(W("The /Trusted_Platform_Assemblies and /Platform_Assemblies_Paths switches cannot be both specified.\n")); + exit(FAILURE_RESULT); + } + + if ((dwFlags & NGENWORKER_FLAGS_NO_METADATA) != 0) + { + const size_t windowsDotWinmdLength = 13; // Length of string "Windows.winmd" + size_t filenameLength = wcslen(pwzFilename); + bool isWindowsDotWinmd = true; + if (filenameLength < windowsDotWinmdLength || + _wcsicmp(pwzFilename + filenameLength - windowsDotWinmdLength, W("windows.winmd")) != 0) + { + isWindowsDotWinmd = false; + } + else if (filenameLength > windowsDotWinmdLength) + { + WCHAR pathSeparator = pwzFilename[filenameLength - windowsDotWinmdLength - 1]; + if (pathSeparator != W('\\') && pathSeparator != W('/') && pathSeparator != W(':')) + { + isWindowsDotWinmd = false; + } + } + if (!isWindowsDotWinmd) + { + Output(W("The /NoMetaData switch can only be used with Windows.winmd.\n")); + exit(FAILURE_RESULT); + } + } +#endif // FEATURE_CORESYSTEM + +#ifdef FEATURE_READYTORUN_COMPILER + if (((dwFlags & NGENWORKER_FLAGS_TUNING) != 0) && ((dwFlags & NGENWORKER_FLAGS_READYTORUN) != 0)) + { + Output(W("The /Tuning switch cannot be used with /ReadyToRun switch.\n")); + exit(FAILURE_RESULT); + } +#endif + + // All argument processing has happened by now. The only messages that should appear before here are errors + // related to argument parsing, such as the Usage message. Afterwards, other messages can appear. + + ///////////////////////////////////////////////////////////////////////// + // + // Start processing + // + + if (fDisplayLogo) + { + PrintLogoHelper(); + } + + PathString wzTrustedPathRoot; + +#ifdef FEATURE_CORECLR + SString ssTPAList; + + if (fCreatePDB) + { + // While creating PDB, assembly binder gives preference to files in TPA. + // This can create difficulties if the input file is not in TPA. + // To avoid this issue, put the input file as the first item in TPA. + ssTPAList.Append(pwzFilename); + } + + // Are we compiling mscorlib.dll? + bool fCompilingMscorlib = StringEndsWith((LPWSTR)pwzFilename, CoreLibName_IL_W); + + if (fCompilingMscorlib) + dwFlags &= ~NGENWORKER_FLAGS_READYTORUN; + + if(pwzPlatformAssembliesPaths != nullptr) + { + // Platform_Assemblies_Paths command line switch has been specified. + _ASSERTE(pwzTrustedPlatformAssemblies == nullptr); + + // Formulate the TPAList from Platform_Assemblies_Paths + ComputeTPAListFromPlatformAssembliesPath(pwzPlatformAssembliesPaths, ssTPAList, fCompilingMscorlib, fCreatePDB); + pwzTrustedPlatformAssemblies = (WCHAR *)ssTPAList.GetUnicode(); + pwzPlatformAssembliesPaths = NULL; + } + + if (pwzTrustedPlatformAssemblies != nullptr) + { + if (ComputeMscorlibPathFromTrustedPlatformAssemblies(wzTrustedPathRoot, pwzTrustedPlatformAssemblies)) + { + pwzPlatformAssembliesPaths = wzTrustedPathRoot.GetUnicode(); + SetMscorlibPath(pwzPlatformAssembliesPaths); + } + } +#endif // FEATURE_CORECLR + + if (pwzPlatformAssembliesPaths == NULL) + { + if (!WszGetModuleFileName(NULL, wzTrustedPathRoot)) + { + ERROR_WIN32(W("Error: GetModuleFileName failed (%d)\n"), GetLastError()); + exit(CLR_INIT_ERROR); + } + + if (SUCCEEDED(CopySystemDirectory(wzTrustedPathRoot, wzTrustedPathRoot))) + { + pwzPlatformAssembliesPaths = wzTrustedPathRoot.GetUnicode(); + } + else + { + ERROR_HR(W("Error: wcsrchr returned NULL; GetModuleFileName must have given us something bad\n"), E_UNEXPECTED); + exit(CLR_INIT_ERROR); + } + + + } + + // Initialize the logger + SetSvcLogger(&g_CrossgenLogger); + + //Step - Compile the assembly + + if (fCreatePDB) + { + hr = CreatePDBWorker( + pwzFilename, + pwzPlatformAssembliesPaths, + pwzTrustedPlatformAssemblies, + pwzPlatformResourceRoots, + pwzAppPaths, + pwzAppNiPaths, + wzDirectoryToStorePDB, + fGeneratePDBLinesInfo, + pwzSearchPathForManagedPDB, + pwzPlatformWinmdPaths, + pwzDiasymreaderPath); + + } + else + { + hr = NGenWorker(pwzFilename, dwFlags, + pwzPlatformAssembliesPaths, + pwzTrustedPlatformAssemblies, + pwzPlatformResourceRoots, + pwzAppPaths, + pwzOutputFilename, + pwzPlatformWinmdPaths +#if defined(FEATURE_CORECLR) && !defined(FEATURE_MERGE_JIT_AND_ENGINE) + , + NULL, // ICorSvcLogger + pwszCLRJITPath +#endif // defined(FEATURE_CORECLR) && !defined(FEATURE_MERGE_JIT_AND_ENGINE) + ); + } + + + if (FAILED(hr)) + { + if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + OutputErrf(W("Error: file \"%s\" or one of its dependencies was not found\n"), pwzFilename); + exit(ASSEMBLY_NOT_FOUND); + } + else + { + OutputErrf(W("Error: compilation failed for \"%s\" (0x%08x)\n"), pwzFilename, hr); + exit(hr); + } + } + + return 0; +} + +#ifdef PLATFORM_UNIX +int main(int argc, char *argv[]) +{ + if (0 != PAL_Initialize(argc, argv)) + { + return FAILURE_RESULT; + } + + wchar_t **wargv = new wchar_t*[argc]; + for (int i = 0; i < argc; i++) + { + size_t len = strlen(argv[i]) + 1; + wargv[i] = new wchar_t[len]; + WszMultiByteToWideChar(CP_ACP, 0, argv[i], -1, wargv[i], len); + } + + int ret = wmain(argc, wargv); + + for (int i = 0; i < argc; i++) + { + delete[] wargv[i]; + } + delete[] wargv; + + return ret; +} +#endif // PLATFORM_UNIX diff --git a/src/tools/crossgen/crossgen.nativeproj b/src/tools/crossgen/crossgen.nativeproj new file mode 100644 index 0000000000..28e4741f4d --- /dev/null +++ b/src/tools/crossgen/crossgen.nativeproj @@ -0,0 +1,94 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\xplat\SetCrossGen.props" /> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + <!--Leaf project Properties--> + <PropertyGroup> + <BuildSysBinaries>true</BuildSysBinaries> + <ClAdditionalOptions>$(ClAdditionalOptions) -DUNICODE -D_UNICODE</ClAdditionalOptions> + <OutputName>crossgen</OutputName> + <TargetType>PROGRAM</TargetType> + <LinkSubsystem>console</LinkSubsystem> + <EntryPoint>wmain</EntryPoint> + <LinkGenerateManifest>true</LinkGenerateManifest> + <LinkAdditionalOptions>$(LinkAdditionalOptions) /MANIFEST</LinkAdditionalOptions> + <UserIncludes>$(UserIncludes);$(ClrSrcDirectory)tools\util</UserIncludes> + <IsDesktopTool>true</IsDesktopTool> + </PropertyGroup> + <!--Leaf Project Items--> + <ItemGroup> + <TargetLib Include="$(SdkLibPath)\ole32.lib" /> + <TargetLib Include="$(SdkLibPath)\oleaut32.lib" /> + <TargetLib Include="$(SdkLibPath)\uuid.lib" /> + <TargetLib Include="$(SdkLibPath)\user32.lib" /> + <TargetLib Include="$(SdkLibPath)\version.lib" /> + <TargetLib Include="$(SdkLibPath)\shlwapi.lib" /> + <TargetLib Include="$(SdkLibPath)\bcrypt.lib" /> + </ItemGroup> + + <ItemGroup> + <TargetLib Condition="'$(_BuildArch)' != 'amd64'" Include="$(ClrLibPath)\corguids_x86.lib"> + <ProjectReference>$(ClrSrcDirectory)incx86\corguids.nativeproj</ProjectReference> + </TargetLib> + <TargetLib Condition="'$(_BuildArch)' == 'amd64'" Include="$(ClrLibPath)\corguids_amd64.lib"> + <ProjectReference>$(ClrSrcDirectory)incamd64\corguids.nativeproj</ProjectReference> + </TargetLib> + <LinkPreCrtLibs Include="$(ClrLibPath)\utilcode_crossgen.lib" /> + <TargetLib Include="$(ClrLibPath)\utilcode_crossgen.lib"> + <ProjectReference>$(ClrSrcDirectory)utilcode\crossgen\utilcode_crossgen.nativeproj</ProjectReference> + </TargetLib> + <TargetLib Include="$(ClrLibPath)\corzap_crossgen.lib"> + <ProjectReference>$(ClrSrcDirectory)zap\crossgen\zap_crossgen.nativeproj</ProjectReference> + </TargetLib> + + <!-- We build RyuJIT only for amd64 and arm64, and use JIT32 for ARM and x86 --> + <TargetLib Include="$(ClrLibPath)\jit_crossgen.lib"> + <ProjectReference Condition="'$(_BuildArch)' == 'amd64' or '$(_BuildArch)' == 'arm64'">$(ClrSrcDirectory)jit\crossgen\jit_crossgen.nativeproj</ProjectReference> + <ProjectReference Condition="'$(_BuildArch)' != 'amd64' and '$(_BuildArch)' != 'arm64'">$(ClrSrcDirectory)jit32\crossgen\jit_crossgen.nativeproj</ProjectReference> + </TargetLib> + + <TargetLib Include="$(ClrLibPath)\gcinfo_crossgen.lib"> + <ProjectReference>$(ClrSrcDirectory)gcinfo\crossgen\gcinfo_crossgen.nativeproj</ProjectReference> + </TargetLib> + <TargetLib Include="$(ClrLibPath)\gcdump_crossgen.lib"> + <ProjectReference>$(ClrSrcDirectory)gcdump\crossgen\gcdump_crossgen.nativeproj</ProjectReference> + </TargetLib> + <TargetLib Include="$(ClrLibPath)\strongname_crossgen.lib"> + <ProjectReference>$(ClrSrcDirectory)strongname\api\crossgen\strongname_crossgen.nativeproj</ProjectReference> + </TargetLib> + <TargetLib Include="$(ClrLibPath)\mdcompiler_crossgen.lib"> + <ProjectReference>$(ClrSrcDirectory)md\compiler\crossgen\mdcompiler_crossgen.nativeproj</ProjectReference> + </TargetLib> + <TargetLib Include="$(ClrLibPath)\mdwinmd_crossgen.lib" Condition="'$(FeatureCominterop)' == 'true'"> + <ProjectReference>$(ClrSrcDirectory)md\winmd\crossgen\mdwinmd_crossgen.nativeproj</ProjectReference> + </TargetLib> + <TargetLib Include="$(ClrLibPath)\mdruntimerw_crossgen.lib"> + <ProjectReference>$(ClrSrcDirectory)md\enc\crossgen\mdruntimerw_crossgen.nativeproj</ProjectReference> + </TargetLib> + <TargetLib Include="$(ClrLibPath)\mdhotdata_crossgen.lib"> + <ProjectReference>$(ClrSrcDirectory)md\hotdata\crossgen\mdhotdata_crossgen.nativeproj</ProjectReference> + </TargetLib> + <TargetLib Include="$(ClrLibPath)\mdruntime_crossgen.lib"> + <ProjectReference>$(ClrSrcDirectory)md\runtime\crossgen\mdruntime_crossgen.nativeproj</ProjectReference> + </TargetLib> + <TargetLib Include="$(ClrLibPath)\cee_crossgen.lib"> + <ProjectReference>$(ClrSrcDirectory)vm\crossgen\wks_crossgen.nativeproj</ProjectReference> + </TargetLib> + <TargetLib Include="$(ClrLibPath)\mscorlib_crossgen.lib"> + <ProjectReference>$(ClrSrcDirectory)vm\crossgen_mscorlib\mscorlib_crossgen.nativeproj</ProjectReference> + </TargetLib> + <TargetLib Include="$(ClrLibPath)\v3binder_crossgen.lib"> + <ProjectReference>$(ClrSrcDirectory)binder\v3binder_crossgen\v3binder_crossgen.nativeproj</ProjectReference> + </TargetLib> + </ItemGroup> + <ItemGroup> + <RCResourceFile Include="native.rc" /> + </ItemGroup> + <ItemGroup> + <CppCompile Include="crossgen.cpp" /> + <CppCompile Include="$(ClrSrcDirectory)\tools\util\consoleargs.cpp" /> + </ItemGroup> + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" /> + +</Project> diff --git a/src/tools/dirs.proj b/src/tools/dirs.proj new file mode 100644 index 0000000000..5f21b67fe9 --- /dev/null +++ b/src/tools/dirs.proj @@ -0,0 +1,77 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + + <PropertyGroup> + <BuildInPhase1>true</BuildInPhase1> + <BuildInPhaseDefault>false</BuildInPhaseDefault> + <BuildCoreBinaries>true</BuildCoreBinaries> + <BuildSysBinaries>true</BuildSysBinaries> + </PropertyGroup> + + <ItemDefinitionGroup> + <ProjectFile> + <ProductGroups>FX</ProductGroups> + </ProjectFile> + </ItemDefinitionGroup> + + <!--The following projects will build during PHASE 1--> + <ItemGroup Condition="'$(BuildExePhase)' == '1' and '$(FeatureCoreClr)' != 'true'"> + <ProjectFile Include="allocationsprofiler\allocationsprofiler.nativeproj" /> + <ProjectFile Include="appcompatgenerator\appcompatgenerator.nativeproj" /> + <ProjectFile Include="applaunch\applaunch.nativeproj" /> + <ProjectFile Include="asmlist\asmlist.nativeproj" /> + <ProjectFile Include="asmmeta\asmmeta.nativeproj" /> + <ProjectFile Include="bbsweep\bbsweep.nativeproj" /> + <ProjectFile Include="bindingsnapshot\bindingsnapshot.nativeproj" /> + <ProjectFile Include="clrver\clrver.nativeproj" /> + <ProjectFile Include="cordmpmerge\cordmpmerge.nativeproj" /> + <ProjectFile Include="corflags\corflags.nativeproj" /> + <ProjectFile Include="fixpdbpath\fixpdbpath.nativeproj" /> + <ProjectFile Include="gac\dirs.proj" /> + <ProjectFile Include="installhook\dirs.proj" /> + <ProjectFile Include="ismgd\ismgd.nativeproj" /> + <ProjectFile Include="jitmanager\jitmgr.nativeproj" /> + <ProjectFile Include="ldr64\ldr64.nativeproj" Condition="'$(BuildArchitecture)' == 'amd64'" /> + <ProjectFile Include="logdiff\Logdiff.csproj" /> + <ProjectFile Include="mdepends\mdepends.nativeproj" /> + <ProjectFile Include="metainfo\metainfo.nativeproj" /> + <ProjectFile Include="ngen\ngen.nativeproj" /> + <ProjectFile Include="ngenoffline\ngenoffline.nativeproj" /> + <ProjectFile Include="nidump\nidump.nativeproj" /> + <ProjectFile Include="ndpsetup\ndpsetup.nativeproj" /> + <ProjectFile Include="peverify\dirs.proj" /> + <ProjectFile Include="profpick\profpick.nativeproj" /> + <ProjectFile Include="regtlb\regtlb.nativeproj" /> + <ProjectFile Include="resetdelaysign\resetdelaysign.nativeproj" /> + <ProjectFile Include="showname\showname.nativeproj" /> + <ProjectFile Include="strikers\strikers.nativeproj" /> + <ProjectFile Include="tlbref\tlbref.nativeproj" /> + <ProjectFile Include="verifyasmhash\verifyasmhash.nativeproj" /> + <ProjectFile Include="verstamp\verstamp.nativeproj" Condition="'$(BuildArchitecture)' == 'i386'" /> + <ProjectFile Include="ngentask\ngentask.csproj"/> + <ProjectFile Include="ngentasklauncher\ngentasklauncher.csproj"/> + <ProjectFile Include="McjProfofileUtil\McjProfileUtil.csproj" Condition="'$(BuildArchitecture)' != 'arm' and '$(BuildArchitecture)' != 'arm64'" /> + <ProjectFile Include="winphoneintegrate\winphoneintegrate.csproj" /> + <ProjectFile Include="crossgen\crossgen.nativeproj" /> + <ProjectFile Include="GenClrDebugResource\GenClrDebugResource.nativeproj" > + <ProductGroups>FX;PK</ProductGroups> + </ProjectFile> + <ProjectFile Include="InjectResource\InjectResource.nativeproj" Condition="'$(BuildArchitecture)' == 'i386'"> + <ProductGroups>FX;PK</ProductGroups> + </ProjectFile> + </ItemGroup> + + <!-- CoreClr --> + <ItemGroup Condition="'$(BuildExePhase)' == '1' and '$(FeatureCoreClr)' == 'true'"> + <ProjectFile Include="bbsweep\bbsweep.nativeproj" Condition="'$(BuildProjectName)' == 'CoreSys'" /> + <ProjectFile Condition="'$(BuildArchitecture)' != 'arm' and '$(BuildArchitecture)' != 'arm64' and '$(BuildProjectName)' != 'CoreSys'" Include="ildbsymbols\ildbsymbols.nativeproj" /> + <ProjectFile Include="coregen\coregen.nativeproj" /> + <ProjectFile Include="nidump\nidump.nativeproj" Condition="!('$(BuildProjectName)' == 'CoreSys' and ('$(BuildArchitecture)' == 'arm' or '$(BuildArchitecture)' == 'arm64'))"/> + <ProjectFile Include="crossgen\crossgen.nativeproj" /> + <ProjectFile Include="spawnnowow\spawnnowow.nativeproj" Condition="('$(BuildArchitecture)' == 'i386' or '$(BuildArchitecture)' == 'amd64' or '$(BuildArchitecture)' == 'ia64') and '$(BuildProjectName)' != 'CoreSys'" /> + <ProjectFile Condition="'$(BuildProjectName)' == 'CoreSys'" Include="winphoneintegrate\winphoneintegrate.csproj" /> + </ItemGroup> + + <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" /> +</Project> diff --git a/src/tools/metainfo/.gitmirrorall b/src/tools/metainfo/.gitmirrorall new file mode 100644 index 0000000000..9ee5c57b99 --- /dev/null +++ b/src/tools/metainfo/.gitmirrorall @@ -0,0 +1 @@ +This folder will be mirrored by the Git-TFS Mirror recursively.
\ No newline at end of file diff --git a/src/tools/metainfo/Native.rc b/src/tools/metainfo/Native.rc new file mode 100644 index 0000000000..fbe8437475 --- /dev/null +++ b/src/tools/metainfo/Native.rc @@ -0,0 +1,8 @@ +// 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. + +#define FX_VER_FILEDESCRIPTION_STR "Microsoft Common Language Runtime Metadata Info\0" + +#include <fxver.h> +#include <fxver.rc> diff --git a/src/tools/metainfo/mdinfo.cpp b/src/tools/metainfo/mdinfo.cpp new file mode 100644 index 0000000000..70dfa28bcf --- /dev/null +++ b/src/tools/metainfo/mdinfo.cpp @@ -0,0 +1,4350 @@ +// 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. + +#include <stdio.h> +#include <windows.h> +#include <objbase.h> +#include <crtdbg.h> +#include <assert.h> + +#include <corpriv.h> +#include <cor.h> +#include "assert.h" +#include "corerror.h" +#include <winwrap.h> +#include <prettyprintsig.h> + +#include <cahlpr.h> +#include <limits.h> + +#include "mdinfo.h" + +#define LEGACY_ACTIVATION_SHIM_LOAD_LIBRARY WszLoadLibrary +#define LEGACY_ACTIVATION_SHIM_DEFINE_CoInitializeEE +#ifndef FEATURE_CORECLR +#include "LegacyActivationShim.h" +#endif + +#define ENUM_BUFFER_SIZE 10 +#define TAB_SIZE 8 + +#define NumItems(s) (sizeof(s) / sizeof(s[0])) + +#define ISFLAG(p,x) if (Is##p##x(flags)) strcat_s(sFlags,STRING_BUFFER_LEN, "["#x "] "); + +extern HRESULT _FillVariant( + BYTE bCPlusTypeFlag, + void const *pValue, + ULONG cbValue, + VARIANT *pvar); + +// Validator declarations. +extern DWORD g_ValModuleType; +#include <ivehandler.h> + +// Tables for mapping element type to text +const char *g_szMapElementType[] = +{ + "End", // 0x0 + "Void", // 0x1 + "Boolean", + "Char", + "I1", + "UI1", + "I2", // 0x6 + "UI2", + "I4", + "UI4", + "I8", + "UI8", + "R4", + "R8", + "String", + "Ptr", // 0xf + "ByRef", // 0x10 + "ValueClass", + "Class", + "Var", + "MDArray", // 0x14 + "GenericInst", + "TypedByRef", + "VALUEARRAY", + "I", + "U", + "R", // 0x1a + "FNPTR", + "Object", + "SZArray", + "MVar", + "CMOD_REQD", + "CMOD_OPT", + "INTERNAL", +}; + +const char *g_szMapUndecorateType[] = +{ + "", // 0x0 + "void", + "boolean", + "Char", + "byte", + "unsigned byte", + "short", + "unsigned short", + "int", + "unsigned int", + "long", + "unsigned long", + "float", + "double", + "String", + "*", // 0xf + "ByRef", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Function Pointer", + "Object", + "", + "", + "CMOD_REQD", + "CMOD_OPT", + "INTERNAL", +}; + +// Provide enough entries for IMAGE_CEE_CS_CALLCONV_MASK (defined in CorHdr.h) +const char *g_strCalling[] = +{ + "[DEFAULT]", + "[C]", + "[STDCALL]", + "[THISCALL]", + "[FASTCALL]", + "[VARARG]", + "[FIELD]", + "[LOCALSIG]", + "[PROPERTY]", + "[UNMANAGED]", + "[GENERICINST]", + "[NATIVEVARARG]", + "[INVALID]", + "[INVALID]", + "[INVALID]", + "[INVALID]" +}; + +const char *g_szNativeType[] = +{ + "NATIVE_TYPE_END(DEPRECATED!)", // = 0x0, //DEPRECATED + "NATIVE_TYPE_VOID(DEPRECATED!)", // = 0x1, //DEPRECATED + "NATIVE_TYPE_BOOLEAN", // = 0x2, // (4 byte boolean value: TRUE = non-zero, FALSE = 0) + "NATIVE_TYPE_I1", // = 0x3, + "NATIVE_TYPE_U1", // = 0x4, + "NATIVE_TYPE_I2", // = 0x5, + "NATIVE_TYPE_U2", // = 0x6, + "NATIVE_TYPE_I4", // = 0x7, + "NATIVE_TYPE_U4", // = 0x8, + "NATIVE_TYPE_I8", // = 0x9, + "NATIVE_TYPE_U8", // = 0xa, + "NATIVE_TYPE_R4", // = 0xb, + "NATIVE_TYPE_R8", // = 0xc, + "NATIVE_TYPE_SYSCHAR(DEPRECATED!)", // = 0xd, //DEPRECATED + "NATIVE_TYPE_VARIANT(DEPRECATED!)", // = 0xe, //DEPRECATED + "NATIVE_TYPE_CURRENCY", // = 0xf, + "NATIVE_TYPE_PTR(DEPRECATED!)", // = 0x10, //DEPRECATED + + "NATIVE_TYPE_DECIMAL(DEPRECATED!)", // = 0x11, //DEPRECATED + "NATIVE_TYPE_DATE(DEPRECATED!)", // = 0x12, //DEPRECATED + "NATIVE_TYPE_BSTR", // = 0x13, + "NATIVE_TYPE_LPSTR", // = 0x14, + "NATIVE_TYPE_LPWSTR", // = 0x15, + "NATIVE_TYPE_LPTSTR", // = 0x16, + "NATIVE_TYPE_FIXEDSYSSTRING", // = 0x17, + "NATIVE_TYPE_OBJECTREF(DEPRECATED!)", // = 0x18, //DEPRECATED + "NATIVE_TYPE_IUNKNOWN", // = 0x19, + "NATIVE_TYPE_IDISPATCH", // = 0x1a, + "NATIVE_TYPE_STRUCT", // = 0x1b, + "NATIVE_TYPE_INTF", // = 0x1c, + "NATIVE_TYPE_SAFEARRAY", // = 0x1d, + "NATIVE_TYPE_FIXEDARRAY", // = 0x1e, + "NATIVE_TYPE_INT", // = 0x1f, + "NATIVE_TYPE_UINT", // = 0x20, + + "NATIVE_TYPE_NESTEDSTRUCT(DEPRECATED!)", // = 0x21, //DEPRECATED (use "NATIVE_TYPE_STRUCT) + + "NATIVE_TYPE_BYVALSTR", // = 0x22, + + "NATIVE_TYPE_ANSIBSTR", // = 0x23, + + "NATIVE_TYPE_TBSTR", // = 0x24, // select BSTR or ANSIBSTR depending on platform + + + "NATIVE_TYPE_VARIANTBOOL", // = 0x25, // (2-byte boolean value: TRUE = -1, FALSE = 0) + "NATIVE_TYPE_FUNC", // = 0x26, + "NATIVE_TYPE_LPVOID", // = 0x27, // blind pointer (no deep marshaling) + + "NATIVE_TYPE_ASANY", // = 0x28, + "<UNDEFINED NATIVE TYPE 0x29>", + "NATIVE_TYPE_ARRAY", // = 0x2a, + "NATIVE_TYPE_LPSTRUCT", // = 0x2b, + "NATIVE_TYPE_CUSTOMMARSHALER", // = 0x2c, // Custom marshaler. + "NATIVE_TYPE_ERROR", // = 0x2d, // VT_HRESULT when exporting to a typelib. +}; + + +size_t g_cbCoffNames = 0; + +mdMethodDef g_tkEntryPoint = 0; // integration with ILDASM + + + +// helper to init signature buffer +void MDInfo::InitSigBuffer() +{ + strcpy_s((LPSTR)m_sigBuf.Ptr(), 1, ""); +} // void MDInfo::InitSigBuffer() + +// helper to append a string into the signature buffer. If size of signature buffer is not big enough, +// we will grow it. +HRESULT MDInfo::AddToSigBuffer(__in_z __in const char *string) +{ + HRESULT hr; + size_t LL = strlen((LPSTR)m_sigBuf.Ptr()) + strlen(string) + 1; + IfFailRet( m_sigBuf.ReSizeNoThrow(LL) ); + strcat_s((LPSTR)m_sigBuf.Ptr(), LL, string); + return NOERROR; +} // HRESULT MDInfo::AddToSigBuffer() + +MDInfo::MDInfo(IMetaDataImport2 *pImport, IMetaDataAssemblyImport *pAssemblyImport, LPCWSTR szScope, strPassBackFn inPBFn, ULONG DumpFilter) +{ // This constructor is specific to ILDASM/MetaInfo integration + + _ASSERTE(pImport != NULL); + _ASSERTE(NumItems(g_szMapElementType) == NumItems(g_szMapUndecorateType)); + _ASSERTE(NumItems(g_szMapElementType) == ELEMENT_TYPE_MAX); + + Init(inPBFn, (DUMP_FILTER)DumpFilter); + + m_pImport = pImport; + m_pImport->AddRef(); + if ((m_pAssemblyImport = pAssemblyImport)) + m_pAssemblyImport->AddRef(); + else + { + HRESULT hr = m_pImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**) &m_pAssemblyImport); + if (FAILED(hr)) + Error("QueryInterface failed for IID_IMetaDataAssemblyImport.", hr); + } + +} // MDInfo::MDInfo() + +MDInfo::MDInfo(IMetaDataDispenserEx *pDispenser, LPCWSTR szScope, strPassBackFn inPBFn, ULONG DumpFilter) +{ + HRESULT hr = S_OK; + VARIANT value; + + _ASSERTE(pDispenser != NULL && inPBFn != NULL); + _ASSERTE(NumItems(g_szMapElementType) == NumItems(g_szMapUndecorateType)); + _ASSERTE(NumItems(g_szMapElementType) == ELEMENT_TYPE_MAX); + + Init(inPBFn, (DUMP_FILTER)DumpFilter); + + // Attempt to open scope on given file + V_VT(&value) = VT_UI4; + V_UI4(&value) = MDImportOptionAll; + if (FAILED(hr = pDispenser->SetOption(MetaDataImportOption, &value))) + Error("SetOption failed.", hr); + + hr = pDispenser->OpenScope(szScope, ofNoTransform, IID_IMetaDataImport2, (IUnknown**)&m_pImport); + if (hr == CLDB_E_BADUPDATEMODE) + { + V_VT(&value) = VT_UI4; + V_UI4(&value) = MDUpdateIncremental; + if (FAILED(hr = pDispenser->SetOption(MetaDataSetUpdate, &value))) + Error("SetOption failed.", hr); + hr = pDispenser->OpenScope(szScope, ofNoTransform, IID_IMetaDataImport2, (IUnknown**)&m_pImport); + } + if (FAILED(hr)) + Error("OpenScope failed", hr); + + // Query for the IMetaDataAssemblyImport interface. + hr = m_pImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**) &m_pAssemblyImport); + if (FAILED(hr)) + Error("QueryInterface failed for IID_IMetaDataAssemblyImport.", hr); + +} // MDInfo::MDInfo() + + +MDInfo::MDInfo(IMetaDataDispenserEx *pDispenser, PBYTE pbMetaData, DWORD dwSize, strPassBackFn inPBFn, ULONG DumpFilter) +{ + _ASSERTE(pDispenser != NULL && inPBFn != NULL); + _ASSERTE(NumItems(g_szMapElementType) == NumItems(g_szMapUndecorateType)); + _ASSERTE(NumItems(g_szMapElementType) == ELEMENT_TYPE_MAX); + + Init(inPBFn, (DUMP_FILTER)DumpFilter); + + // Attempt to open scope on manifest. It's valid for this to fail, because + // the blob we open may just be the assembly resources (the space is + // overloaded until we remove LM -a assemblies, at which point this + // constructor should probably be removed too). + HRESULT hr; + VARIANT value; + V_VT(&value) = VT_UI4; + V_UI4(&value) = MDImportOptionAll; + if (FAILED(hr = pDispenser->SetOption(MetaDataImportOption, &value))) + Error("SetOption failed.", hr); + if (SUCCEEDED(hr = pDispenser->OpenScopeOnMemory(pbMetaData, dwSize, ofNoTransform, + IID_IMetaDataImport2, (IUnknown**)&m_pImport))) + { + // Query for the IMetaDataAssemblyImport interface. + hr = m_pImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**) &m_pAssemblyImport); + if (FAILED(hr)) + Error("QueryInterface failed for IID_IMetaDataAssemblyImport.", hr); + } + +} // MDInfo::MDInfo() + +void MDInfo::Init( + strPassBackFn inPBFn, // Callback to write text. + DUMP_FILTER DumpFilter) // Flags to control the dump. +{ + m_VEHandlerReporterPtr = 0; + m_pbFn = inPBFn; + m_DumpFilter = DumpFilter; + m_pTables = NULL; + m_pTables2 = NULL; + m_pImport = NULL; + m_pAssemblyImport = NULL; +} // void MDInfo::Init() + +// Destructor +MDInfo::~MDInfo() +{ + if (m_pImport) + m_pImport->Release(); + if (m_pAssemblyImport) + m_pAssemblyImport->Release(); + if (m_pTables) + m_pTables->Release(); + if (m_pTables2) + m_pTables2->Release(); +} // MDInfo::~MDInfo() + +//===================================================================================================================== +//#define EXTERNAL_VE_HANDLER_FOR_MD_VALIDATION +#ifndef EXTERNAL_VE_HANDLER_FOR_MD_VALIDATION + +HINSTANCE GetModuleInst() +{ + return NULL; +} // HINSTANCE GetModuleInst() + +typedef HRESULT (*REPORTFCTN)(LPCWSTR, VEContext, HRESULT); +HRESULT DefaultReporter( // Return status. + LPCWSTR szMsg, // Error message. + VEContext Context, // Error context (offset,token) + HRESULT hrRpt) // Original HRESULT +{ + if(szMsg) + { + printf("%S", szMsg); + // include token and offset from Context + if(Context.Token) printf(" [token:0x%08X]",Context.Token); + if(Context.uOffset) printf(" [at:0x%X]",Context.uOffset); + printf(" [hr:0x%08X]\n",hrRpt); + fflush(stdout); + } + return S_OK; +} // HRESULT DefaultReporter() + + +#ifdef FEATURE_METADATA_VALIDATOR +class MDVEHandlerClass : public IVEHandler +{ +public: + LONG m_refCount; + REPORTFCTN m_fnReport; + + MDVEHandlerClass() { m_refCount=0; m_fnReport=DefaultReporter; }; + virtual ~MDVEHandlerClass() { }; + + //----------------------------------------------------------- + // IUnknown support + //----------------------------------------------------------- + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, void** pInterface) + { + if (id == IID_IVEHandler) + *pInterface = (IVEHandler*)this; + /* + else if (id == IID_IUnknown) + *pInterface = (IUnknown*)(IVEHandler*)this; + */ + else + { + *pInterface = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; + } + ULONG STDMETHODCALLTYPE AddRef() + { + return InterlockedIncrement(&m_refCount); + } + + ULONG STDMETHODCALLTYPE Release() + { + LONG refCount = InterlockedDecrement(&m_refCount); + if (refCount == 0) delete this; + return (refCount); + } + //----------------------------------------------------------- + // IVEHandler support + //----------------------------------------------------------- + HRESULT STDMETHODCALLTYPE SetReporterFtn(__int64 lFnPtr) + { + m_fnReport = lFnPtr ? reinterpret_cast<REPORTFCTN>(lFnPtr) + : DefaultReporter; + return S_OK; + }; + +//***************************************************************************** +// The Verification Event Handler itself. Declared in VEHandler.h as virtual, may be overridden +//***************************************************************************** + HRESULT STDMETHODCALLTYPE VEHandler(HRESULT hrRpt, VEContext Context, SAFEARRAY *psa) + { + WCHAR rcBuf[1024]; // Resource string. + WCHAR rcMsg[1024]; // Error message. + BYTE *marker; // User text. + HRESULT hr; + ULONG32 k; + WCHAR *pWsz[1024]; // is more than 1024 string arguments likely? + + // Return warnings without text. + if (!FAILED(hrRpt)) + return (hrRpt); + memset(pWsz,0,sizeof(pWsz)); + + ULONG32 nVars; + // Convert safearray of variants into va_list + if(psa && (nVars = psa->rgsabound[0].cElements)) + { + WCHAR *pwsz; + VARIANT *pVar; + ULONG32 i,l; + BYTE *pval; + + _ASSERTE(psa->fFeatures & FADF_VARIANT); + _ASSERTE(psa->cDims == 1); + marker = new BYTE[nVars*sizeof(double)]; // double being the largest variant element + for(i=0,pVar=(VARIANT *)(psa->pvData),pval=marker; i < nVars; pVar++,i++) + { + switch(V_VT(pVar)) + { + case VT_I1: + *(int *)pval = V_I1(pVar); + pval += sizeof(int); + break; + + case VT_UI1: + *(int *)pval = V_UI1(pVar); + pval += sizeof(int); + break; + + + case VT_I2: + *(int *)pval = V_I2(pVar); + pval += sizeof(int); + break; + + case VT_UI2: + *(int *)pval = V_UI2(pVar); + pval += sizeof(int); + break; + + case VT_I8: + case VT_UI8: + *(INT64 *)pval = V_I8(pVar); + pval += sizeof(INT64); + break; + + + case VT_BYREF|VT_I1: + case VT_BYREF|VT_UI1: // it's ASCII string, convert it to UNICODE + { + PBYTE pb = V_UI1REF(pVar); + l = (ULONG32)strlen((char *)pb)+1; + pwsz = new WCHAR[l]; + WszMultiByteToWideChar(CP_ACP,0,(char*)pb,-1,pwsz,l); + for(k=0; pWsz[k]; k++); + pWsz[k] = pwsz; + + *(WCHAR **)pval = pwsz; + pval += sizeof(WCHAR *); + break; + } + + default: + *(int *)pval = V_I4(pVar); + pval += sizeof(int); + break; + } + } + } + else + marker = NULL; + + // If this is one of our errors, then grab the error from the rc file. + if (HRESULT_FACILITY(hrRpt) == FACILITY_URT) + { + hr = UtilLoadStringRC(LOWORD(hrRpt), rcBuf, NumItems(rcBuf), true); + if (hr == S_OK) + { + // Format the error. + vswprintf_s(rcMsg, NumItems(rcMsg), rcBuf, (va_list) marker); + rcMsg[NumItems(rcMsg) - 1] = 0; + } + } + // Otherwise it isn't one of ours, so we need to see if the system can + // find the text for it. + else + { + if (WszFormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + 0, hrRpt, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + rcMsg, NumItems(rcMsg), 0)) + { + hr = S_OK; + + // System messages contain a trailing \r\n, which we don't want normally. + int iLen = lstrlenW(rcMsg); + if (iLen > 3 && rcMsg[iLen - 2] == '\r' && rcMsg[iLen - 1] == '\n') + rcMsg[iLen - 2] = '\0'; + } + else + hr = HRESULT_FROM_WIN32(GetLastError()); + } + if(marker) delete [] marker; + + // If we failed to find the message anywhere, then issue a hard coded message. + if (FAILED(hr)) + { + swprintf_s(rcMsg, NumItems(rcMsg), W("COM+ Runtime Internal error: 0x%08x"), hrRpt); + //DEBUG_STMT(DbgWriteEx(rcMsg)); + } + + // delete WCHAR buffers allocated above (if any) + for(k=0; pWsz[k]; k++) + { + if(pWsz[k]) + { + delete [] pWsz[k]; + pWsz[k] = NULL; + } + } + + return (m_fnReport(rcMsg, Context,hrRpt) == S_OK ? S_OK : E_FAIL); + }; + + static HRESULT STDMETHODCALLTYPE CreateObject(REFIID id, void **object) + { return E_NOTIMPL; } +}; +#endif // FEATURE_METADATA_VALIDATOR + +#endif +//===================================================================================================================== +// DisplayMD() function +// +// Displays the meta data content of a file + +void MDInfo::DisplayMD() +{ + if ((m_DumpFilter & dumpAssem) && m_pAssemblyImport) + DisplayAssemblyInfo(); + WriteLine("==========================================================="); + // Metadata itself: Raw or normal view + if (m_DumpFilter & (dumpSchema | dumpHeader | dumpCSV | dumpRaw | dumpStats | dumpRawHeaps)) + DisplayRaw(); + else + { + DisplayVersionInfo(); + DisplayScopeInfo(); + WriteLine("==========================================================="); + DisplayGlobalFunctions(); + DisplayGlobalFields(); + DisplayGlobalMemberRefs(); + DisplayTypeDefs(); + DisplayTypeRefs(); + DisplayTypeSpecs(); + DisplayMethodSpecs(); + DisplayModuleRefs(); + DisplaySignatures(); + DisplayAssembly(); + DisplayUserStrings(); + + // WriteLine("============================================================"); + // WriteLine("Unresolved MemberRefs"); + // DisplayMemberRefs(0x00000001, "\t"); + + VWrite("\n\nCoff symbol name overhead: %d\n", g_cbCoffNames); + } + WriteLine("==========================================================="); + if (m_DumpFilter & dumpUnsat) + DisplayUnsatInfo(); + WriteLine("==========================================================="); +#ifdef FEATURE_METADATA_VALIDATOR + if (m_DumpFilter & dumpValidate) + { + IMetaDataValidate *pValidate = 0; +#ifndef EXTERNAL_VE_HANDLER_FOR_MD_VALIDATION + MDVEHandlerClass *pVEHandler = 0; +#else + IVEHandler *pVEHandler = 0; +#endif + const char *szErrStr = 0; + HRESULT hr = S_OK; + + // Get a pointer to the Validator interface. + hr = m_pImport->QueryInterface(IID_IMetaDataValidate, (void **) &pValidate); + if (FAILED(hr)) + { + szErrStr = "QueryInterface failed for IMetaDataValidate."; + goto ErrExit; + } + + // Get a pointer to the VEHandler interface. +#ifndef EXTERNAL_VE_HANDLER_FOR_MD_VALIDATION + if((pVEHandler = new MDVEHandlerClass())) hr = S_OK; + else hr = E_FAIL; +#else + hr = CoCreateInstance(CLSID_VEHandlerClass, + NULL, + CLSCTX_INPROC_SERVER, + IID_IVEHandler, + (void **)&pVEHandler); +#endif + if (FAILED(hr)) + { +#ifndef EXTERNAL_VE_HANDLER_FOR_MD_VALIDATION + szErrStr = "Failed to create VEHandler."; +#else + szErrStr = "CoCreateInstance(VEHandler) failed."; +#endif + goto ErrExit; + } + + if(m_VEHandlerReporterPtr) pVEHandler->SetReporterFtn((__int64)m_VEHandlerReporterPtr); + + hr = pValidate->ValidatorInit(g_ValModuleType, pVEHandler); + if (FAILED(hr)) + { + szErrStr = "ValidatorInit failed."; + goto ErrExit; + } + + hr = pValidate->ValidateMetaData(); + if (FAILED(hr)) + { + szErrStr = "ValidateMetaData failed to run successfully."; + goto ErrExit; + } + if (hr == S_OK) + WriteLine("No warnings or errors found."); + else if (hr == VLDTR_S_WRN) + WriteLine("Warnings found."); + else if (hr == VLDTR_S_ERR) + WriteLine("Errors found."); + else if (hr == VLDTR_S_WRNERR) + WriteLine("Warnings and Errors found."); + else + VWriteLine("Validator returned unexpected success code, hr=0x%08x.", hr); +ErrExit: + if (pValidate) + pValidate->Release(); +#ifdef EXTERNAL_VE_HANDLER_FOR_MD_VALIDATION + if (pVEHandler) + pVEHandler->Release(); +#endif + if (szErrStr) + Error(szErrStr, hr); + } +#endif // FEATURE_METADATA_VALIDATOR + WriteLine("==========================================================="); +} // MDVEHandlerClass() + +int MDInfo::WriteLine(__in_z __in const char *str) +{ + ULONG32 count = (ULONG32) strlen(str); + + m_pbFn(str); + m_pbFn("\n"); + return count; +} // int MDInfo::WriteLine() + +int MDInfo::Write(__in_z __in const char *str) +{ + ULONG32 count = (ULONG32) strlen(str); + + m_pbFn(str); + return count; +} // int MDInfo::Write() + +int MDInfo::VWriteLine(__in_z __in const char *str, ...) +{ + va_list marker; + int count; + + va_start(marker, str); + count = VWriteMarker(str, marker); + m_pbFn("\n"); + va_end(marker); + return count; +} // int MDInfo::VWriteLine() + +int MDInfo::VWrite(__in_z __in const char *str, ...) +{ + va_list marker; + int count; + + va_start(marker, str); + count = VWriteMarker(str, marker); + va_end(marker); + return count; +} // int MDInfo::VWrite() + +int MDInfo::VWriteMarker(__in_z __in const char *str, va_list marker) +{ + HRESULT hr; + int count = -1; + // Used to allocate 1K, then if not enough, 2K, then 4K. + // Faster to allocate 32K right away and be done with it, + // we're not running on Commodore 64 + if (FAILED(hr = m_output.ReSizeNoThrow(STRING_BUFFER_LEN * 8))) + Error("ReSize failed.", hr); + else + { + count = vsprintf_s((char *)m_output.Ptr(), STRING_BUFFER_LEN * 8, str, marker); + m_pbFn((char *)m_output.Ptr()); + } + return count; +} // int MDInfo::VWriteToBuffer() + +// Error() function -- prints an error and returns +void MDInfo::Error(const char* szError, HRESULT hr) +{ + printf("\n%s\n",szError); + if (hr != S_OK) + { + printf("Failed return code: 0x%08x\n", hr); + + IErrorInfo *pIErr = NULL; // Error interface. + BSTR bstrDesc = NULL; // Description text. +#ifdef FEATURE_COMINTEROP + // Try to get an error info object and display the message. + if (GetErrorInfo(0, &pIErr) == S_OK && + pIErr->GetDescription(&bstrDesc) == S_OK) + { + printf("%ls ", bstrDesc); + SysFreeString(bstrDesc); + } +#endif + // Free the error interface. + if (pIErr) + pIErr->Release(); + + } +#ifndef FEATURE_CORECLR + LegacyActivationShim::CoUninitializeCor(); +#ifndef FEATURE_PAL + CoUninitialize(); +#endif +#endif + exit(hr); +} // void MDInfo::Error() + +// Print out the optional version info included in the MetaData. + +void MDInfo::DisplayVersionInfo() +{ + if (!(m_DumpFilter & MDInfo::dumpNoLogo)) + { + LPCUTF8 pVersionStr; + HRESULT hr = S_OK; + + if (m_pTables == 0) + { + if (m_pImport) + hr = m_pImport->QueryInterface(IID_IMetaDataTables, (void**)&m_pTables); + else if (m_pAssemblyImport) + hr = m_pAssemblyImport->QueryInterface(IID_IMetaDataTables, (void**)&m_pTables); + else + return; + if (FAILED(hr)) + Error("QueryInterface failed for IID_IMetaDataTables.", hr); + } + + hr = m_pTables->GetString(1, &pVersionStr); + if (FAILED(hr)) + Error("GetString() failed.", hr); + if (strstr(pVersionStr, "Version of runtime against which the binary is built : ") + == pVersionStr) + { + WriteLine(const_cast<char *>(pVersionStr)); + } + } +} // void MDInfo::DisplayVersionInfo() + +// Prints out information about the scope + +void MDInfo::DisplayScopeInfo() +{ + HRESULT hr; + mdModule mdm; + GUID mvid; + WCHAR scopeName[STRING_BUFFER_LEN]; + WCHAR guidString[STRING_BUFFER_LEN]; + + hr = m_pImport->GetScopeProps( scopeName, STRING_BUFFER_LEN, 0, &mvid); + if (FAILED(hr)) Error("GetScopeProps failed.", hr); + + VWriteLine("ScopeName : %ls",scopeName); + + if (!(m_DumpFilter & MDInfo::dumpNoLogo)) + VWriteLine("MVID : %ls",GUIDAsString(mvid, guidString, STRING_BUFFER_LEN)); + + hr = m_pImport->GetModuleFromScope(&mdm); + if (FAILED(hr)) Error("GetModuleFromScope failed.", hr); + DisplayPermissions(mdm, ""); + DisplayCustomAttributes(mdm, "\t"); +} // void MDInfo::DisplayScopeInfo() + +void MDInfo::DisplayRaw() +{ + int iDump; // Level of info to dump. + + if (m_pTables == 0) + m_pImport->QueryInterface(IID_IMetaDataTables, (void**)&m_pTables); + if (m_pTables == 0) + Error("Can't get table info."); + if (m_pTables2 == 0) + m_pImport->QueryInterface(IID_IMetaDataTables2, (void**)&m_pTables2); + + if (m_DumpFilter & dumpCSV) + DumpRawCSV(); + if (m_DumpFilter & (dumpSchema | dumpHeader | dumpRaw | dumpStats)) + { + if (m_DumpFilter & dumpRaw) + iDump = 3; + else + if (m_DumpFilter & dumpSchema) + iDump = 2; + else + iDump = 1; + + DumpRaw(iDump, (m_DumpFilter & dumpStats) != 0); + } + if (m_DumpFilter & dumpRawHeaps) + DumpRawHeaps(); +} // void MDInfo::DisplayRaw() + +// return the name of the type of token passed in + +const char *MDInfo::TokenTypeName(mdToken inToken) +{ + switch(TypeFromToken(inToken)) + { + case mdtTypeDef: return "TypeDef"; + case mdtInterfaceImpl: return "InterfaceImpl"; + case mdtMethodDef: return "MethodDef"; + case mdtFieldDef: return "FieldDef"; + case mdtTypeRef: return "TypeRef"; + case mdtMemberRef: return "MemberRef"; + case mdtCustomAttribute:return "CustomAttribute"; + case mdtParamDef: return "ParamDef"; + case mdtProperty: return "Property"; + case mdtEvent: return "Event"; + case mdtTypeSpec: return "TypeSpec"; + default: return "[UnknownTokenType]"; + } +} // char *MDInfo::TokenTypeName() + +// Prints out name of the given memberref +// + +LPCWSTR MDInfo::MemberRefName(mdMemberRef inMemRef, __out_ecount(bufLen) LPWSTR buffer, ULONG bufLen) +{ + HRESULT hr; + + + hr = m_pImport->GetMemberRefProps( inMemRef, NULL, buffer, bufLen, + NULL, NULL, NULL); + if (FAILED(hr)) Error("GetMemberRefProps failed.", hr); + + return buffer; +} // LPCWSTR MDInfo::MemberRefName() + + +// Prints out information about the given memberref +// + +void MDInfo::DisplayMemberRefInfo(mdMemberRef inMemRef, const char *preFix) +{ + HRESULT hr; + WCHAR memRefName[STRING_BUFFER_LEN]; + ULONG nameLen; + mdToken token; + PCCOR_SIGNATURE pbSigBlob; + ULONG ulSigBlob; + char newPreFix[STRING_BUFFER_LEN]; + + + hr = m_pImport->GetMemberRefProps( inMemRef, &token, memRefName, STRING_BUFFER_LEN, + &nameLen, &pbSigBlob, &ulSigBlob); + if (FAILED(hr)) Error("GetMemberRefProps failed.", hr); + + VWriteLine("%s\t\tMember: (%8.8x) %ls: ", preFix, inMemRef, memRefName); + + if (ulSigBlob) + DisplaySignature(pbSigBlob, ulSigBlob, preFix); + else + VWriteLine("%s\t\tERROR: no valid signature ", preFix); + + sprintf_s (newPreFix, STRING_BUFFER_LEN, "\t\t%s", preFix); + DisplayCustomAttributes(inMemRef, newPreFix); +} // void MDInfo::DisplayMemberRefInfo() + +// Prints out information about all memberrefs of the given typeref +// + +void MDInfo::DisplayMemberRefs(mdToken tkParent, const char *preFix) +{ + HCORENUM memRefEnum = NULL; + HRESULT hr; + mdMemberRef memRefs[ENUM_BUFFER_SIZE]; + ULONG count, totalCount = 1; + + + while (SUCCEEDED(hr = m_pImport->EnumMemberRefs( &memRefEnum, tkParent, + memRefs, NumItems(memRefs), &count)) && + count > 0) + { + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("%s\tMemberRef #%d (%08x)", preFix, totalCount, memRefs[i]); + VWriteLine("%s\t-------------------------------------------------------", preFix); + DisplayMemberRefInfo(memRefs[i], preFix); + } + } + m_pImport->CloseEnum( memRefEnum); +} // void MDInfo::DisplayMemberRefs() + +// Prints out information about all resources in the com object +// + +// Iterates through each typeref and prints out the information of each +// + +void MDInfo::DisplayTypeRefs() +{ + HCORENUM typeRefEnum = NULL; + mdTypeRef typeRefs[ENUM_BUFFER_SIZE]; + ULONG count, totalCount=1; + HRESULT hr; + + while (SUCCEEDED(hr = m_pImport->EnumTypeRefs( &typeRefEnum, + typeRefs, NumItems(typeRefs), &count)) && + count > 0) + { + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("TypeRef #%d (%08x)", totalCount, typeRefs[i]); + WriteLine("-------------------------------------------------------"); + DisplayTypeRefInfo(typeRefs[i]); + DisplayMemberRefs(typeRefs[i], ""); + WriteLine(""); + } + } + m_pImport->CloseEnum( typeRefEnum); +} // void MDInfo::DisplayTypeRefs() + +void MDInfo::DisplayTypeSpecs() +{ + HCORENUM typespecEnum = NULL; + mdTypeSpec typespecs[ENUM_BUFFER_SIZE]; + ULONG count, totalCount=1; + HRESULT hr; + + while (SUCCEEDED(hr = m_pImport->EnumTypeSpecs( &typespecEnum, + typespecs, NumItems(typespecs), &count)) && + count > 0) + { + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("TypeSpec #%d (%08x)", totalCount, typespecs[i]); + WriteLine("-------------------------------------------------------"); + DisplayTypeSpecInfo(typespecs[i], ""); + DisplayMemberRefs(typespecs[i], ""); + WriteLine(""); + } + } + m_pImport->CloseEnum( typespecEnum); +} // void MDInfo::DisplayTypeSpecs() + +void MDInfo::DisplayMethodSpecs() +{ + HCORENUM MethodSpecEnum = NULL; + mdMethodSpec MethodSpecs[ENUM_BUFFER_SIZE]; + ULONG count, totalCount=1; +///// HRESULT hr; + + +///// HACK until I implement EnumMethodSpecs! +///// while (SUCCEEDED(hr = m_pImport->EnumMethodSpecs( &MethodSpecEnum, +///// MethodSpecs, NumItems(MethodSpecs), &count)) && +///// count > 0) + for (ULONG rid=1; m_pImport->IsValidToken(TokenFromRid(rid, mdtMethodSpec)); ++rid) + { +// More hackery +count = 1; +MethodSpecs[0] = TokenFromRid(rid, mdtMethodSpec); +// More hackery + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("MethodSpec #%d (%08x)", totalCount, MethodSpecs[i]); + DisplayMethodSpecInfo(MethodSpecs[i], ""); + WriteLine(""); + } + } + m_pImport->CloseEnum( MethodSpecEnum); +} // void MDInfo::DisplayMethodSpecs() + + + +// Called to display the information about all typedefs in the object. +// + +void MDInfo::DisplayTypeDefs() +{ + HCORENUM typeDefEnum = NULL; + mdTypeDef typeDefs[ENUM_BUFFER_SIZE]; + ULONG count, totalCount = 1; + HRESULT hr; + + while (SUCCEEDED(hr = m_pImport->EnumTypeDefs( &typeDefEnum, + typeDefs, NumItems(typeDefs), &count)) && + count > 0) + { + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("TypeDef #%d (%08x)", totalCount, typeDefs[i]); + WriteLine("-------------------------------------------------------"); + DisplayTypeDefInfo(typeDefs[i]); + WriteLine(""); + } + } + m_pImport->CloseEnum( typeDefEnum); +} // void MDInfo::DisplayTypeDefs() + +// Called to display the information about all modulerefs in the object. +// + +void MDInfo::DisplayModuleRefs() +{ + HCORENUM moduleRefEnum = NULL; + mdModuleRef moduleRefs[ENUM_BUFFER_SIZE]; + ULONG count, totalCount = 1; + HRESULT hr; + + while (SUCCEEDED(hr = m_pImport->EnumModuleRefs( &moduleRefEnum, + moduleRefs, NumItems(moduleRefs), &count)) && + count > 0) + { + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("ModuleRef #%d (%08x)", totalCount, moduleRefs[i]); + WriteLine("-------------------------------------------------------"); + DisplayModuleRefInfo(moduleRefs[i]); + DisplayMemberRefs(moduleRefs[i], ""); + WriteLine(""); + } + } + m_pImport->CloseEnum( moduleRefEnum); +} // void MDInfo::DisplayModuleRefs() + +// Prints out information about the given moduleref +// + +void MDInfo::DisplayModuleRefInfo(mdModuleRef inModuleRef) +{ + HRESULT hr; + WCHAR moduleRefName[STRING_BUFFER_LEN]; + ULONG nameLen; + + + hr = m_pImport->GetModuleRefProps( inModuleRef, moduleRefName, STRING_BUFFER_LEN, + &nameLen); + if (FAILED(hr)) Error("GetModuleRefProps failed.", hr); + + VWriteLine("\t\tModuleRef: (%8.8x) %ls: ", inModuleRef, moduleRefName); + DisplayCustomAttributes(inModuleRef, "\t\t"); +} // void MDInfo::DisplayModuleRefInfo() + + +// Called to display the information about all signatures in the object. +// + +void MDInfo::DisplaySignatures() +{ + HCORENUM signatureEnum = NULL; + mdSignature signatures[ENUM_BUFFER_SIZE]; + ULONG count, totalCount = 1; + HRESULT hr; + + while (SUCCEEDED(hr = m_pImport->EnumSignatures( &signatureEnum, + signatures, NumItems(signatures), &count)) && + count > 0) + { + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("Signature #%d (%#08x)", totalCount, signatures[i]); + WriteLine("-------------------------------------------------------"); + DisplaySignatureInfo(signatures[i]); + WriteLine(""); + } + } + m_pImport->CloseEnum( signatureEnum); +} // void MDInfo::DisplaySignatures() + + +// Prints out information about the given signature +// + +void MDInfo::DisplaySignatureInfo(mdSignature inSignature) +{ + HRESULT hr; + PCCOR_SIGNATURE pbSigBlob; + ULONG ulSigBlob; + + + hr = m_pImport->GetSigFromToken( inSignature, &pbSigBlob, &ulSigBlob ); + if (FAILED(hr)) Error("GetSigFromToken failed.", hr); + if(ulSigBlob) + DisplaySignature(pbSigBlob, ulSigBlob, ""); + else + VWriteLine("\t\tERROR: no valid signature "); +} // void MDInfo::DisplaySignatureInfo() + + +// returns the passed-in buffer which is filled with the name of the given +// member in wide characters +// + +LPCWSTR MDInfo::MemberName(mdToken inToken, __out_ecount(bufLen) LPWSTR buffer, ULONG bufLen) +{ + HRESULT hr; + + + hr = m_pImport->GetMemberProps( inToken, NULL, buffer, bufLen, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + if (FAILED(hr)) Error("GetMemberProps failed.", hr); + + return (buffer); +} // LPCWSTR MDInfo::MemberName() + + +// displays information for the given method +// + +void MDInfo::DisplayMethodInfo(mdMethodDef inMethod, DWORD *pflags) +{ + HRESULT hr; + mdTypeDef memTypeDef; + WCHAR memberName[STRING_BUFFER_LEN]; + ULONG nameLen; + DWORD flags; + PCCOR_SIGNATURE pbSigBlob; + ULONG ulSigBlob; + ULONG ulCodeRVA; + ULONG ulImplFlags; + + + hr = m_pImport->GetMethodProps( inMethod, &memTypeDef, memberName, STRING_BUFFER_LEN, + &nameLen, &flags, &pbSigBlob, &ulSigBlob, &ulCodeRVA, &ulImplFlags); + if (FAILED(hr)) Error("GetMethodProps failed.", hr); + if (pflags) + *pflags = flags; + + VWriteLine("\t\tMethodName: %ls (%8.8X)", memberName, inMethod); + + char sFlags[STRING_BUFFER_LEN]; + + sFlags[0] = 0; + ISFLAG(Md, Public); + ISFLAG(Md, Private); + ISFLAG(Md, Family); + ISFLAG(Md, Assem); + ISFLAG(Md, FamANDAssem); + ISFLAG(Md, FamORAssem); + ISFLAG(Md, PrivateScope); + ISFLAG(Md, Static); + ISFLAG(Md, Final); + ISFLAG(Md, Virtual); + ISFLAG(Md, HideBySig); + ISFLAG(Md, ReuseSlot); + ISFLAG(Md, NewSlot); + ISFLAG(Md, Abstract); + ISFLAG(Md, SpecialName); + ISFLAG(Md, RTSpecialName); + ISFLAG(Md, PinvokeImpl); + ISFLAG(Md, UnmanagedExport); + if (!*sFlags) + strcpy_s(sFlags, STRING_BUFFER_LEN, "[none]"); + + bool result = (((flags) & mdRTSpecialName) && !wcscmp((memberName), W(".ctor"))); + if (result) strcat_s(sFlags, STRING_BUFFER_LEN, "[.ctor] "); + result = (((flags) & mdRTSpecialName) && !wcscmp((memberName), W(".cctor"))); + if (result) strcat_s(sFlags,STRING_BUFFER_LEN, "[.cctor] "); + // "Reserved" flags + ISFLAG(Md, HasSecurity); + ISFLAG(Md, RequireSecObject); + + VWriteLine("\t\tFlags : %s (%08x)", sFlags, flags); + VWriteLine("\t\tRVA : 0x%08x", ulCodeRVA); + + flags = ulImplFlags; + sFlags[0] = 0; + ISFLAG(Mi, Native); + ISFLAG(Mi, IL); + ISFLAG(Mi, OPTIL); + ISFLAG(Mi, Runtime); + ISFLAG(Mi, Unmanaged); + ISFLAG(Mi, Managed); + ISFLAG(Mi, ForwardRef); + ISFLAG(Mi, PreserveSig); + ISFLAG(Mi, InternalCall); + ISFLAG(Mi, Synchronized); + ISFLAG(Mi, NoInlining); + if (!*sFlags) + strcpy_s(sFlags, STRING_BUFFER_LEN, "[none]"); + + VWriteLine("\t\tImplFlags : %s (%08x)", sFlags, flags); + + if (ulSigBlob) + DisplaySignature(pbSigBlob, ulSigBlob, ""); + else + VWriteLine("\t\tERROR: no valid signature "); + + DisplayGenericParams(inMethod, "\t\t"); + +} // void MDInfo::DisplayMethodInfo() + +// displays the member information for the given field +// + +void MDInfo::DisplayFieldInfo(mdFieldDef inField, DWORD *pdwFlags) +{ + HRESULT hr; + mdTypeDef memTypeDef; + WCHAR memberName[STRING_BUFFER_LEN]; + ULONG nameLen; + DWORD flags; + PCCOR_SIGNATURE pbSigBlob; + ULONG ulSigBlob; + DWORD dwCPlusTypeFlag; + void const *pValue; + ULONG cbValue; +#ifdef FEATURE_COMINTEROP + VARIANT defaultValue; + + ::VariantInit(&defaultValue); +#endif + hr = m_pImport->GetFieldProps( inField, &memTypeDef, memberName, STRING_BUFFER_LEN, + &nameLen, &flags, &pbSigBlob, &ulSigBlob, &dwCPlusTypeFlag, + &pValue, &cbValue); + if (FAILED(hr)) Error("GetFieldProps failed.", hr); + + if (pdwFlags) + *pdwFlags = flags; + +#ifdef FEATURE_COMINTEROP + _FillVariant((BYTE)dwCPlusTypeFlag, pValue, cbValue, &defaultValue); +#endif + + char sFlags[STRING_BUFFER_LEN]; + + sFlags[0] = 0; + ISFLAG(Fd, Public); + ISFLAG(Fd, Private); + ISFLAG(Fd, Family); + ISFLAG(Fd, Assembly); + ISFLAG(Fd, FamANDAssem); + ISFLAG(Fd, FamORAssem); + ISFLAG(Fd, PrivateScope); + ISFLAG(Fd, Static); + ISFLAG(Fd, InitOnly); + ISFLAG(Fd, Literal); + ISFLAG(Fd, NotSerialized); + ISFLAG(Fd, SpecialName); + ISFLAG(Fd, RTSpecialName); + ISFLAG(Fd, PinvokeImpl); + // "Reserved" flags + ISFLAG(Fd, HasDefault); + if (!*sFlags) + strcpy_s(sFlags, STRING_BUFFER_LEN, "[none]"); + + VWriteLine("\t\tField Name: %ls (%8.8X)", memberName, inField); + VWriteLine("\t\tFlags : %s (%08x)", sFlags, flags); +#ifdef FEATURE_COMINTEROP + if (IsFdHasDefault(flags)) + VWriteLine("\tDefltValue: (%s) %ls", g_szMapElementType[dwCPlusTypeFlag], VariantAsString(&defaultValue)); +#endif + if (!ulSigBlob) // Signature size should be non-zero for fields + VWriteLine("\t\tERROR: no valid signature "); + else + DisplaySignature(pbSigBlob, ulSigBlob, ""); +#ifdef FEATURE_COMINTEROP + ::VariantClear(&defaultValue); +#endif +} // void MDInfo::DisplayFieldInfo() + +// displays the RVA for the given global field. +void MDInfo::DisplayFieldRVA(mdFieldDef inFieldDef) +{ + HRESULT hr; + ULONG ulRVA; + + hr = m_pImport->GetRVA(inFieldDef, &ulRVA, 0); + if (FAILED(hr) && hr != CLDB_E_RECORD_NOTFOUND) Error("GetRVA failed.", hr); + + VWriteLine("\t\tRVA : 0x%08x", ulRVA); +} // void MDInfo::DisplayFieldRVA() + +// displays information about every global function. +void MDInfo::DisplayGlobalFunctions() +{ + WriteLine("Global functions"); + WriteLine("-------------------------------------------------------"); + DisplayMethods(mdTokenNil); + WriteLine(""); +} // void MDInfo::DisplayGlobalFunctions() + +// displays information about every global field. +void MDInfo::DisplayGlobalFields() +{ + WriteLine("Global fields"); + WriteLine("-------------------------------------------------------"); + DisplayFields(mdTokenNil, NULL, 0); + WriteLine(""); +} // void MDInfo::DisplayGlobalFields() + +// displays information about every global memberref. +void MDInfo::DisplayGlobalMemberRefs() +{ + WriteLine("Global MemberRefs"); + WriteLine("-------------------------------------------------------"); + DisplayMemberRefs(mdTokenNil, ""); + WriteLine(""); +} // void MDInfo::DisplayGlobalMemberRefs() + +// displays information about every method in a given typedef +// + +void MDInfo::DisplayMethods(mdTypeDef inTypeDef) +{ + HCORENUM methodEnum = NULL; + mdToken methods[ENUM_BUFFER_SIZE]; + DWORD flags; + ULONG count, totalCount = 1; + HRESULT hr; + + + while (SUCCEEDED(hr = m_pImport->EnumMethods( &methodEnum, inTypeDef, + methods, NumItems(methods), &count)) && + count > 0) + { + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("\tMethod #%d (%08x) %s", totalCount, methods[i], (methods[i] == g_tkEntryPoint) ? "[ENTRYPOINT]" : ""); + WriteLine("\t-------------------------------------------------------"); + DisplayMethodInfo(methods[i], &flags); + DisplayParams(methods[i]); + DisplayCustomAttributes(methods[i], "\t\t"); + DisplayPermissions(methods[i], "\t"); + DisplayMemberRefs(methods[i], "\t"); + + // P-invoke data if present. + if (IsMdPinvokeImpl(flags)) + DisplayPinvokeInfo(methods[i]); + + WriteLine(""); + } + } + m_pImport->CloseEnum( methodEnum); +} // void MDInfo::DisplayMethods() + + +// displays information about every field in a given typedef +// + +void MDInfo::DisplayFields(mdTypeDef inTypeDef, COR_FIELD_OFFSET *rFieldOffset, ULONG cFieldOffset) +{ + HCORENUM fieldEnum = NULL; + mdToken fields[ENUM_BUFFER_SIZE]; + ULONG count, totalCount = 1; + DWORD flags; + HRESULT hr; + + + while (SUCCEEDED(hr = m_pImport->EnumFields( &fieldEnum, inTypeDef, + fields, NumItems(fields), &count)) && + count > 0) + { + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("\tField #%d (%08x)",totalCount, fields[i]); + WriteLine("\t-------------------------------------------------------"); + DisplayFieldInfo(fields[i], &flags); + DisplayCustomAttributes(fields[i], "\t\t"); + DisplayPermissions(fields[i], "\t"); + DisplayFieldMarshal(fields[i]); + + // RVA if its a global field. + if (inTypeDef == mdTokenNil) + DisplayFieldRVA(fields[i]); + + // P-invoke data if present. + if (IsFdPinvokeImpl(flags)) + DisplayPinvokeInfo(fields[i]); + + // Display offset if present. + if (cFieldOffset) + { + bool found = false; + for (ULONG iLayout = 0; i < cFieldOffset; ++iLayout) + { + if (RidFromToken(rFieldOffset[iLayout].ridOfField) == RidFromToken(fields[i])) + { + found = true; + VWriteLine("\t\tOffset : 0x%08x", rFieldOffset[iLayout].ulOffset); + break; + } + } + _ASSERTE(found); + } + WriteLine(""); + } + } + m_pImport->CloseEnum( fieldEnum); +} // void MDInfo::DisplayFields() + + +// displays information about every methodImpl in a given typedef +// + +void MDInfo::DisplayMethodImpls(mdTypeDef inTypeDef) +{ + HCORENUM methodImplEnum = NULL; + mdMethodDef rtkMethodBody[ENUM_BUFFER_SIZE]; + mdMethodDef rtkMethodDecl[ENUM_BUFFER_SIZE]; + + ULONG count, totalCount=1; + HRESULT hr; + + + while (SUCCEEDED(hr = m_pImport->EnumMethodImpls( &methodImplEnum, inTypeDef, + rtkMethodBody, rtkMethodDecl, NumItems(rtkMethodBody), &count)) && + count > 0) + { + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("\n\tMethodImpl #%d (%08x)", totalCount, totalCount); + WriteLine("\t-------------------------------------------------------"); + VWriteLine("\t\tMethod Body Token : 0x%08x", rtkMethodBody[i]); + VWriteLine("\t\tMethod Declaration Token : 0x%08x", rtkMethodDecl[i]); + WriteLine(""); + } + } + m_pImport->CloseEnum( methodImplEnum); +} // void MDInfo::DisplayMethodImpls() + +// displays information about the given parameter +// + +void MDInfo::DisplayParamInfo(mdParamDef inParamDef) +{ + mdMethodDef md; + ULONG num; + WCHAR paramName[STRING_BUFFER_LEN]; + ULONG nameLen; + DWORD flags; + VARIANT defValue; + DWORD dwCPlusFlags; + void const *pValue; + ULONG cbValue; + +#ifdef FEATURE_COMINTEROP + ::VariantInit(&defValue); +#endif + HRESULT hr = m_pImport->GetParamProps( inParamDef, &md, &num, paramName, NumItems(paramName), + &nameLen, &flags, &dwCPlusFlags, &pValue, &cbValue); + if (FAILED(hr)) Error("GetParamProps failed.", hr); + + _FillVariant((BYTE)dwCPlusFlags, pValue, cbValue, &defValue); + + char sFlags[STRING_BUFFER_LEN]; + sFlags[0] = 0; + ISFLAG(Pd, In); + ISFLAG(Pd, Out); + ISFLAG(Pd, Optional); + // "Reserved" flags. + ISFLAG(Pd, HasDefault); + ISFLAG(Pd, HasFieldMarshal); + if (!*sFlags) + strcpy_s(sFlags,STRING_BUFFER_LEN, "[none]"); + + VWrite("\t\t\t(%ld) ParamToken : (%08x) Name : %ls flags: %s (%08x)", num, inParamDef, paramName, sFlags, flags); +#ifdef FEATURE_COMINTEROP + if (IsPdHasDefault(flags)) + VWriteLine(" Default: (%s) %ls", g_szMapElementType[dwCPlusFlags], VariantAsString(&defValue)); + else +#endif + VWriteLine(""); + DisplayCustomAttributes(inParamDef, "\t\t\t"); + +#ifdef FEATURE_COMINTEROP + ::VariantClear(&defValue); +#endif +} // void MDInfo::DisplayParamInfo() + + +// displays all parameters for a given memberdef +// + +void MDInfo::DisplayParams(mdMethodDef inMethodDef) +{ + HCORENUM paramEnum = NULL; + mdParamDef params[ENUM_BUFFER_SIZE]; + ULONG count, paramCount; + bool first = true; + HRESULT hr; + + + while (SUCCEEDED(hr = m_pImport->EnumParams( ¶mEnum, inMethodDef, + params, NumItems(params), &count)) && + count > 0) + { + if (first) + { + m_pImport->CountEnum( paramEnum, ¶mCount); + VWriteLine("\t\t%d Parameters", paramCount); + } + for (ULONG i = 0; i < count; i++) + { + DisplayParamInfo(params[i]); + DisplayFieldMarshal(params[i]); + } + first = false; + } + m_pImport->CloseEnum( paramEnum); +} // void MDInfo::DisplayParams() + +void MDInfo::DisplayGenericParams(mdToken tk, const char *prefix) +{ + HCORENUM paramEnum = NULL; + mdParamDef params[ENUM_BUFFER_SIZE]; + ULONG count, paramCount; + bool first = true; + HRESULT hr; + + + while (SUCCEEDED(hr = m_pImport->EnumGenericParams( ¶mEnum, tk, + params, NumItems(params), &count)) && + count > 0) + { + if (first) + { + m_pImport->CountEnum( paramEnum, ¶mCount); + VWriteLine("%s%d Generic Parameters", prefix, paramCount); + } + for (ULONG i = 0; i < count; i++) + { + DisplayGenericParamInfo(params[i], prefix); + } + first = false; + } + m_pImport->CloseEnum( paramEnum); +} + +void MDInfo::DisplayGenericParamInfo(mdGenericParam tkParam, const char *prefix) +{ + ULONG ulSeq; + WCHAR paramName[STRING_BUFFER_LEN]; + ULONG nameLen; + DWORD flags; + mdToken tkOwner; + char newprefix[30]; + HCORENUM constraintEnum = NULL; + mdParamDef constraints[4]; + ULONG count, constraintCount; + mdToken constraint; + mdToken owner; + bool first = true; + + HRESULT hr = m_pImport->GetGenericParamProps(tkParam, &ulSeq, &flags, &tkOwner, NULL, paramName, NumItems(paramName), &nameLen); + if (FAILED(hr)) Error("GetGenericParamProps failed.", hr); + + VWriteLine("%s\t(%ld) GenericParamToken : (%08x) Name : %ls flags: %08x Owner: %08x", prefix, ulSeq, tkParam, paramName, flags, tkOwner); + + // Any constraints for the GenericParam + while (SUCCEEDED(hr = m_pImport->EnumGenericParamConstraints(&constraintEnum, tkParam, + constraints, NumItems(constraints), &count)) && + count > 0) + { + if (first) + { + m_pImport->CountEnum( constraintEnum, &constraintCount); + VWriteLine("%s\t\t%d Constraint(s)", prefix, constraintCount); + } + VWrite("%s\t\t", prefix); + for (ULONG i=0; i< count; ++i) + { + hr = m_pImport->GetGenericParamConstraintProps(constraints[i], &owner, &constraint); + if (owner != tkParam) + VWrite("%08x (owner: %08x) ", constraint, owner); + else + VWrite("%08x ", constraint); + } + VWriteLine(""); + } + m_pImport->CloseEnum(constraintEnum); + + sprintf_s(newprefix, 30, "%s\t", prefix); + DisplayCustomAttributes(tkParam, newprefix); +} + +LPCWSTR MDInfo::TokenName(mdToken inToken, __out_ecount(bufLen) LPWSTR buffer, ULONG bufLen) +{ + LPCUTF8 pName; // Token name in UTF8. + + if (IsNilToken(inToken)) + return W(""); + + m_pImport->GetNameFromToken(inToken, &pName); + + WszMultiByteToWideChar(CP_UTF8,0, pName,-1, buffer,bufLen); + + return buffer; +} // LPCWSTR MDInfo::TokenName() + +// prints out name of typeref or typedef +// + +LPCWSTR MDInfo::TypeDeforRefName(mdToken inToken, __out_ecount(bufLen) LPWSTR buffer, ULONG bufLen) +{ + if (RidFromToken(inToken)) + { + if (TypeFromToken(inToken) == mdtTypeDef) + return (TypeDefName((mdTypeDef) inToken, buffer, bufLen)); + else if (TypeFromToken(inToken) == mdtTypeRef) + return (TypeRefName((mdTypeRef) inToken, buffer, bufLen)); + else if (TypeFromToken(inToken) == mdtTypeSpec) + return W("[TypeSpec]"); + else + return W("[InvalidReference]"); + } + else + return W(""); +} // LPCWSTR MDInfo::TypeDeforRefName() + +LPCWSTR MDInfo::MemberDeforRefName(mdToken inToken, __out_ecount(bufLen) LPWSTR buffer, ULONG bufLen) +{ + if (RidFromToken(inToken)) + { + if (TypeFromToken(inToken) == mdtMethodDef || TypeFromToken(inToken) == mdtFieldDef) + return (MemberName(inToken, buffer, bufLen)); + else if (TypeFromToken(inToken) == mdtMemberRef) + return (MemberRefName((mdMemberRef) inToken, buffer, bufLen)); + else + return W("[InvalidReference]"); + } + else + return W(""); +} // LPCWSTR MDInfo::MemberDeforRefName() + +// prints out only the name of the given typedef +// +// + +LPCWSTR MDInfo::TypeDefName(mdTypeDef inTypeDef, __out_ecount(bufLen) LPWSTR buffer, ULONG bufLen) +{ + HRESULT hr; + + hr = m_pImport->GetTypeDefProps( + // [IN] The import scope. + inTypeDef, // [IN] TypeDef token for inquiry. + buffer, // [OUT] Put name here. + bufLen, // [IN] size of name buffer in wide chars. + NULL, // [OUT] put size of name (wide chars) here. + NULL, // [OUT] Put flags here. + NULL); // [OUT] Put base class TypeDef/TypeRef here. + if (FAILED(hr)) + { + swprintf_s(buffer, bufLen, W("[Invalid TypeDef]")); + } + + return buffer; +} // LPCWSTR MDInfo::TypeDefName() + +// prints out all the properties of a given typedef +// + +void MDInfo::DisplayTypeDefProps(mdTypeDef inTypeDef) +{ + HRESULT hr; + WCHAR typeDefName[STRING_BUFFER_LEN]; + ULONG nameLen; + DWORD flags; + mdToken extends; + ULONG dwPacking; // Packing size of class, if specified. + ULONG dwSize; // Total size of class, if specified. + + hr = m_pImport->GetTypeDefProps( + inTypeDef, // [IN] TypeDef token for inquiry. + typeDefName, // [OUT] Put name here. + STRING_BUFFER_LEN, // [IN] size of name buffer in wide chars. + &nameLen, // [OUT] put size of name (wide chars) here. + &flags, // [OUT] Put flags here. + &extends); // [OUT] Put base class TypeDef/TypeRef here. + if (FAILED(hr)) Error("GetTypeDefProps failed.", hr); + + char sFlags[STRING_BUFFER_LEN]; + WCHAR szTempBuf[STRING_BUFFER_LEN]; + + VWriteLine("\tTypDefName: %ls (%8.8X)",typeDefName,inTypeDef); + VWriteLine("\tFlags : %s (%08x)",ClassFlags(flags, sFlags), flags); + VWriteLine("\tExtends : %8.8X [%s] %ls",extends,TokenTypeName(extends), + TypeDeforRefName(extends, szTempBuf, NumItems(szTempBuf))); + + hr = m_pImport->GetClassLayout(inTypeDef, &dwPacking, 0,0,0, &dwSize); + if (hr == S_OK) + VWriteLine("\tLayout : Packing:%d, Size:%d", dwPacking, dwSize); + + if (IsTdNested(flags)) + { + mdTypeDef tkEnclosingClass; + + hr = m_pImport->GetNestedClassProps(inTypeDef, &tkEnclosingClass); + if (hr == S_OK) + { + VWriteLine("\tEnclosingClass : %ls (%8.8X)", TypeDeforRefName(tkEnclosingClass, + szTempBuf, NumItems(szTempBuf)), tkEnclosingClass); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + WriteLine("ERROR: EnclosingClass not found for NestedClass"); + else + Error("GetNestedClassProps failed.", hr); + } +} // void MDInfo::DisplayTypeDefProps() + +// Prints out the name of the given TypeRef +// + +LPCWSTR MDInfo::TypeRefName(mdTypeRef tr, __out_ecount(bufLen) LPWSTR buffer, ULONG bufLen) +{ + HRESULT hr; + + hr = m_pImport->GetTypeRefProps( + tr, // The class ref token. + NULL, // Resolution scope. + buffer, // Put the name here. + bufLen, // Size of the name buffer, wide chars. + NULL); // Put actual size of name here. + if (FAILED(hr)) + { + swprintf_s(buffer, bufLen, W("[Invalid TypeRef]")); + } + + return (buffer); +} // LPCWSTR MDInfo::TypeRefName() + +// Prints out all the info of the given TypeRef +// + +void MDInfo::DisplayTypeRefInfo(mdTypeRef tr) +{ + HRESULT hr; + mdToken tkResolutionScope; + WCHAR typeRefName[STRING_BUFFER_LEN]; + ULONG nameLen; + + hr = m_pImport->GetTypeRefProps( + tr, // The class ref token. + &tkResolutionScope, // ResolutionScope. + typeRefName, // Put the name here. + STRING_BUFFER_LEN, // Size of the name buffer, wide chars. + &nameLen); // Put actual size of name here. + + if (FAILED(hr)) Error("GetTypeRefProps failed.", hr); + + VWriteLine("Token: 0x%08x", tr); + VWriteLine("ResolutionScope: 0x%08x", tkResolutionScope); + VWriteLine("TypeRefName: %ls",typeRefName); + + DisplayCustomAttributes(tr, "\t"); +} // void MDInfo::DisplayTypeRefInfo() + + +void MDInfo::DisplayTypeSpecInfo(mdTypeSpec ts, const char *preFix) +{ + HRESULT hr; + PCCOR_SIGNATURE pvSig; + ULONG cbSig; + ULONG cb; + + InitSigBuffer(); + + hr = m_pImport->GetTypeSpecFromToken( + ts, // The class ref token. + &pvSig, + &cbSig); + + if (FAILED(hr)) Error("GetTypeSpecFromToken failed.", hr); + +// DisplaySignature(pvSig, cbSig, preFix); + + if (FAILED(hr = GetOneElementType(pvSig, cbSig, &cb))) + goto ErrExit; + + VWriteLine("%s\tTypeSpec :%s", preFix, (LPSTR)m_sigBuf.Ptr()); + + // Hex, too? + if (m_DumpFilter & dumpMoreHex) + { + char rcNewPrefix[80]; + sprintf_s(rcNewPrefix, 80, "%s\tSignature", preFix); + DumpHex(rcNewPrefix, pvSig, cbSig, false, 24); + } +ErrExit: + return; +} // void MDInfo::DisplayTypeSpecInfo() + +void MDInfo::DisplayMethodSpecInfo(mdMethodSpec ms, const char *preFix) +{ + HRESULT hr; + PCCOR_SIGNATURE pvSig; + ULONG cbSig; + mdToken tk; + + InitSigBuffer(); + + hr = m_pImport->GetMethodSpecProps( + ms, // The MethodSpec token + &tk, // The MethodDef or MemberRef + &pvSig, // Signature. + &cbSig); // Size of signature. + + VWriteLine("%s\tParent : 0x%08x", preFix, tk); + DisplaySignature(pvSig, cbSig, preFix); +//ErrExit: + return; +} // void MDInfo::DisplayMethodSpecInfo() + +// Return the passed-in buffer filled with a string detailing the class flags +// associated with the class. +// + +char *MDInfo::ClassFlags(DWORD flags, __out_ecount(STRING_BUFFER_LEN) char *sFlags) +{ + sFlags[0] = 0; + ISFLAG(Td, NotPublic); + ISFLAG(Td, Public); + ISFLAG(Td, NestedPublic); + ISFLAG(Td, NestedPrivate); + ISFLAG(Td, NestedFamily); + ISFLAG(Td, NestedAssembly); + ISFLAG(Td, NestedFamANDAssem); + ISFLAG(Td, NestedFamORAssem); + ISFLAG(Td, AutoLayout); + ISFLAG(Td, SequentialLayout); + ISFLAG(Td, ExplicitLayout); + ISFLAG(Td, Class); + ISFLAG(Td, Interface); + ISFLAG(Td, Abstract); + ISFLAG(Td, Sealed); + ISFLAG(Td, SpecialName); + ISFLAG(Td, Import); + ISFLAG(Td, Serializable); + ISFLAG(Td, AnsiClass); + ISFLAG(Td, UnicodeClass); + ISFLAG(Td, AutoClass); + ISFLAG(Td, BeforeFieldInit); + ISFLAG(Td, Forwarder); + // "Reserved" flags + ISFLAG(Td, RTSpecialName); + ISFLAG(Td, HasSecurity); + ISFLAG(Td, WindowsRuntime); + if (!*sFlags) + strcpy_s(sFlags, STRING_BUFFER_LEN, "[none]"); + + return sFlags; +} // char *MDInfo::ClassFlags() + +// prints out all info on the given typeDef, including all information that +// is specific to a given typedef +// + +void MDInfo::DisplayTypeDefInfo(mdTypeDef inTypeDef) +{ + DisplayTypeDefProps(inTypeDef); + + // Get field layout information. + HRESULT hr = NOERROR; + COR_FIELD_OFFSET *rFieldOffset = NULL; + ULONG cFieldOffset = 0; + hr = m_pImport->GetClassLayout(inTypeDef, NULL, rFieldOffset, 0, &cFieldOffset, NULL); + if (SUCCEEDED(hr) && cFieldOffset) + { + rFieldOffset = new COR_FIELD_OFFSET[cFieldOffset]; + if (rFieldOffset == NULL) + Error("_calloc failed.", E_OUTOFMEMORY); + hr = m_pImport->GetClassLayout(inTypeDef, NULL, rFieldOffset, cFieldOffset, &cFieldOffset, NULL); + if (FAILED(hr)) { delete [] rFieldOffset; Error("GetClassLayout() failed.", hr); } + } + + //No reason to display members if we're displaying fields and methods separately + DisplayGenericParams(inTypeDef, "\t"); + DisplayFields(inTypeDef, rFieldOffset, cFieldOffset); + delete [] rFieldOffset; + DisplayMethods(inTypeDef); + DisplayProperties(inTypeDef); + DisplayEvents(inTypeDef); + DisplayMethodImpls(inTypeDef); + DisplayPermissions(inTypeDef, ""); + + DisplayInterfaceImpls(inTypeDef); + DisplayCustomAttributes(inTypeDef, "\t"); +} // void MDInfo::DisplayTypeDefInfo() + +// print out information about every the given typeDef's interfaceImpls +// + +void MDInfo::DisplayInterfaceImpls(mdTypeDef inTypeDef) +{ + HCORENUM interfaceImplEnum = NULL; + mdTypeRef interfaceImpls[ENUM_BUFFER_SIZE]; + ULONG count, totalCount = 1; + HRESULT hr; + + while(SUCCEEDED(hr = m_pImport->EnumInterfaceImpls( &interfaceImplEnum, + inTypeDef,interfaceImpls,NumItems(interfaceImpls), &count)) && + count > 0) + { + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("\tInterfaceImpl #%d (%08x)", totalCount, interfaceImpls[i]); + WriteLine("\t-------------------------------------------------------"); + DisplayInterfaceImplInfo(interfaceImpls[i]); + DisplayPermissions(interfaceImpls[i], "\t"); + WriteLine(""); + } + } + m_pImport->CloseEnum( interfaceImplEnum); +} // void MDInfo::DisplayInterfaceImpls() + +// print the information for the given interface implementation +// + +void MDInfo::DisplayInterfaceImplInfo(mdInterfaceImpl inImpl) +{ + mdTypeDef typeDef; + mdToken token; + HRESULT hr; + + WCHAR szTempBuf[STRING_BUFFER_LEN]; + + hr = m_pImport->GetInterfaceImplProps( inImpl, &typeDef, &token); + if (FAILED(hr)) Error("GetInterfaceImplProps failed.", hr); + + VWriteLine("\t\tClass : %ls",TypeDeforRefName(typeDef, szTempBuf, NumItems(szTempBuf))); + VWriteLine("\t\tToken : %8.8X [%s] %ls",token,TokenTypeName(token), TypeDeforRefName(token, szTempBuf, NumItems(szTempBuf))); + + DisplayCustomAttributes(inImpl, "\t\t"); +} // void MDInfo::DisplayInterfaceImplInfo() + +// displays the information for a particular property +// + +void MDInfo::DisplayPropertyInfo(mdProperty inProp) +{ + HRESULT hr; + mdTypeDef typeDef; + WCHAR propName[STRING_BUFFER_LEN]; + DWORD flags; +#ifdef FEATURE_COMINTEROP + VARIANT defaultValue; +#endif + void const *pValue; + ULONG cbValue; + DWORD dwCPlusTypeFlag; + mdMethodDef setter, getter, otherMethod[ENUM_BUFFER_SIZE]; + ULONG others; + PCCOR_SIGNATURE pbSigBlob; + ULONG ulSigBlob; + + +#ifdef FEATURE_COMINTEROP + ::VariantInit(&defaultValue); +#endif + hr = m_pImport->GetPropertyProps( + inProp, // [IN] property token + &typeDef, // [OUT] typedef containing the property declarion. + + propName, // [OUT] Property name + STRING_BUFFER_LEN, // [IN] the count of wchar of szProperty + NULL, // [OUT] actual count of wchar for property name + + &flags, // [OUT] property flags. + + &pbSigBlob, // [OUT] Signature Blob. + &ulSigBlob, // [OUT] Number of bytes in the signature blob. + + &dwCPlusTypeFlag, // [OUT] default value + &pValue, + &cbValue, + + &setter, // [OUT] setter method of the property + &getter, // [OUT] getter method of the property + + otherMethod, // [OUT] other methods of the property + ENUM_BUFFER_SIZE, // [IN] size of rmdOtherMethod + &others); // [OUT] total number of other method of this property + + if (FAILED(hr)) Error("GetPropertyProps failed.", hr); + + VWriteLine("\t\tProp.Name : %ls (%8.8X)",propName,inProp); + + char sFlags[STRING_BUFFER_LEN]; + + sFlags[0] = 0; + ISFLAG(Pr, SpecialName); + ISFLAG(Pr, RTSpecialName); + ISFLAG(Pr, HasDefault); + if (!*sFlags) + strcpy_s(sFlags, STRING_BUFFER_LEN, "[none]"); + + VWriteLine("\t\tFlags : %s (%08x)", sFlags, flags); + + if (ulSigBlob) + DisplaySignature(pbSigBlob, ulSigBlob, ""); + else + VWriteLine("\t\tERROR: no valid signature "); + + WCHAR szTempBuf[STRING_BUFFER_LEN]; + +#ifdef FEATURE_COMINTEROP + _FillVariant((BYTE)dwCPlusTypeFlag, pValue, cbValue, &defaultValue); + VWriteLine("\t\tDefltValue: %ls",VariantAsString(&defaultValue)); +#endif + + VWriteLine("\t\tSetter : (%08x) %ls",setter,MemberDeforRefName(setter, szTempBuf, NumItems(szTempBuf))); + VWriteLine("\t\tGetter : (%08x) %ls",getter,MemberDeforRefName(getter, szTempBuf, NumItems(szTempBuf))); + + // do something with others? + VWriteLine("\t\t%ld Others",others); + DisplayCustomAttributes(inProp, "\t\t"); + +#ifdef FEATURE_COMINTEROP + ::VariantClear(&defaultValue); +#endif +} // void MDInfo::DisplayPropertyInfo() + +// displays info for each property +// + +void MDInfo::DisplayProperties(mdTypeDef inTypeDef) +{ + HCORENUM propEnum = NULL; + mdProperty props[ENUM_BUFFER_SIZE]; + ULONG count, totalCount = 1; + HRESULT hr; + + + while(SUCCEEDED(hr = m_pImport->EnumProperties( &propEnum, + inTypeDef,props,NumItems(props), &count)) && + count > 0) + { + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("\tProperty #%d (%08x)", totalCount, props[i]); + WriteLine("\t-------------------------------------------------------"); + DisplayPropertyInfo(props[i]); + DisplayPermissions(props[i], "\t"); + WriteLine(""); + } + } + m_pImport->CloseEnum( propEnum); +} // void MDInfo::DisplayProperties() + +// Display all information about a particular event +// + +void MDInfo::DisplayEventInfo(mdEvent inEvent) +{ + HRESULT hr; + mdTypeDef typeDef; + WCHAR eventName[STRING_BUFFER_LEN]; + DWORD flags; + mdToken eventType; + mdMethodDef addOn, removeOn, fire, otherMethod[ENUM_BUFFER_SIZE]; + ULONG totalOther; + + + hr = m_pImport->GetEventProps( + // [IN] The scope. + inEvent, // [IN] event token + &typeDef, // [OUT] typedef containing the event declarion. + + eventName, // [OUT] Event name + STRING_BUFFER_LEN, // [IN] the count of wchar of szEvent + NULL, // [OUT] actual count of wchar for event's name + + &flags, // [OUT] Event flags. + &eventType, // [OUT] EventType class + + &addOn, // [OUT] AddOn method of the event + &removeOn, // [OUT] RemoveOn method of the event + &fire, // [OUT] Fire method of the event + + otherMethod, // [OUT] other method of the event + NumItems(otherMethod), // [IN] size of rmdOtherMethod + &totalOther); // [OUT] total number of other method of this event + if (FAILED(hr)) Error("GetEventProps failed.", hr); + + VWriteLine("\t\tName : %ls (%8.8X)",eventName,inEvent); + + char sFlags[STRING_BUFFER_LEN]; + + sFlags[0] = 0; + ISFLAG(Ev, SpecialName); + ISFLAG(Ev, RTSpecialName); + if (!*sFlags) + strcpy_s(sFlags, STRING_BUFFER_LEN, "[none]"); + + VWriteLine("\t\tFlags : %s (%08x)", sFlags, flags); + + WCHAR szTempBuf[STRING_BUFFER_LEN]; + + VWriteLine("\t\tEventType : %8.8X [%s]",eventType,TokenTypeName(eventType)); + VWriteLine("\t\tAddOnMethd: (%08x) %ls",addOn,MemberDeforRefName(addOn, szTempBuf, NumItems(szTempBuf))); + VWriteLine("\t\tRmvOnMethd: (%08x) %ls",removeOn,MemberDeforRefName(removeOn, szTempBuf, NumItems(szTempBuf))); + VWriteLine("\t\tFireMethod: (%08x) %ls",fire,MemberDeforRefName(fire, szTempBuf, NumItems(szTempBuf))); + + VWriteLine("\t\t%ld OtherMethods",totalOther); + + DisplayCustomAttributes(inEvent, "\t\t"); +} // void MDInfo::DisplayEventInfo() + +// Display information about all events in a typedef +// +void MDInfo::DisplayEvents(mdTypeDef inTypeDef) +{ + HCORENUM eventEnum = NULL; + mdProperty events[ENUM_BUFFER_SIZE]; + ULONG count, totalCount = 1; + HRESULT hr; + + + while(SUCCEEDED(hr = m_pImport->EnumEvents( &eventEnum, + inTypeDef,events,NumItems(events), &count)) && + count > 0) + { + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("\tEvent #%d (%08x)", totalCount, events[i]); + WriteLine("\t-------------------------------------------------------"); + DisplayEventInfo(events[i]); + DisplayPermissions(events[i], "\t"); + WriteLine(""); + } + } + m_pImport->CloseEnum( eventEnum); +} // void MDInfo::DisplayEvents() + + +// print info for the passed-in custom attribute +// This function is used to print the custom attribute information for both TypeDefs and +// MethodDefs which need slightly different formatting. preFix helps fix it up. +// + +void MDInfo::DisplayCustomAttributeInfo(mdCustomAttribute inValue, const char *preFix) +{ + const BYTE *pValue; // The custom value. + ULONG cbValue; // Length of the custom value. + HRESULT hr; // A result. + mdToken tkObj; // Attributed object. + mdToken tkType; // Type of the custom attribute. + mdToken tk; // For name lookup. + LPCUTF8 pMethName=0; // Name of custom attribute ctor, if any. + CQuickBytes qSigName; // Buffer to pretty-print signature. + PCCOR_SIGNATURE pSig=0; // Signature of ctor. + ULONG cbSig; // Size of the signature. + BOOL bCoffSymbol = false; // true for coff symbol CA's. + WCHAR rcName[MAX_CLASS_NAME]; // Name of the type. + + hr = m_pImport->GetCustomAttributeProps( // S_OK or error. + inValue, // The attribute. + &tkObj, // The attributed object + &tkType, // The attributes type. + (const void**)&pValue, // Put pointer to data here. + &cbValue); // Put size here. + if (FAILED(hr)) Error("GetCustomAttributeProps failed.", hr); + + VWriteLine("%s\tCustomAttribute Type: %08x", preFix, tkType); + + // Get the name of the memberref or methoddef. + tk = tkType; + rcName[0] = L'\0'; + // Get the member name, and the parent token. + switch (TypeFromToken(tk)) + { + case mdtMemberRef: + hr = m_pImport->GetNameFromToken(tk, &pMethName); + if (FAILED(hr)) Error("GetNameFromToken failed.", hr); + hr = m_pImport->GetMemberRefProps( tk, &tk, 0, 0, 0, &pSig, &cbSig); + if (FAILED(hr)) Error("GetMemberRefProps failed.", hr); + break; + case mdtMethodDef: + hr = m_pImport->GetNameFromToken(tk, &pMethName); + if (FAILED(hr)) Error("GetNameFromToken failed.", hr); + hr = m_pImport->GetMethodProps(tk, &tk, 0, 0, 0, 0, &pSig, &cbSig, 0, 0); + if (FAILED(hr)) Error("GetMethodProps failed.", hr); + break; + } // switch + + // Get the type name. + switch (TypeFromToken(tk)) + { + case mdtTypeDef: + hr = m_pImport->GetTypeDefProps(tk, rcName,MAX_CLASS_NAME,0, 0,0); + if (FAILED(hr)) Error("GetTypeDefProps failed.", hr); + break; + case mdtTypeRef: + hr = m_pImport->GetTypeRefProps(tk, 0, rcName,MAX_CLASS_NAME,0); + if (FAILED(hr)) Error("GetTypeRefProps failed.", hr); + break; + } // switch + + + if (pSig && pMethName) + { + int iLen; + LPWSTR pwzName = (LPWSTR)(new WCHAR[iLen= 1+(ULONG32)strlen(pMethName)]); + if(pwzName) + { + WszMultiByteToWideChar(CP_UTF8,0, pMethName,-1, pwzName,iLen); + PrettyPrintSigLegacy(pSig, cbSig, pwzName, &qSigName, m_pImport); + delete [] pwzName; + } + } + + VWrite("%s\tCustomAttributeName: %ls", preFix, rcName); + if (pSig && pMethName) + VWrite(" :: %S", qSigName.Ptr()); + + // Keep track of coff overhead. + if (!wcscmp(W("__DecoratedName"), rcName)) + { + bCoffSymbol = true; + g_cbCoffNames += cbValue + 6; + } + WriteLine(""); + + VWriteLine("%s\tLength: %ld", preFix, cbValue); + char newPreFix[40]; + sprintf_s(newPreFix, 40, "%s\tValue ", preFix); + DumpHex(newPreFix, pValue, cbValue); + if (bCoffSymbol) + VWriteLine("%s\t %s", preFix, pValue); + + // Try to decode the constructor blob. This is incomplete, but covers the most popular cases. + if (pSig) + { // Interpret the signature. + PCCOR_SIGNATURE ps = pSig; + ULONG cb; + ULONG ulData; + ULONG cParams; + ULONG ulVal; + UINT8 u1 = 0; + UINT16 u2 = 0; + UINT32 u4 = 0; + UINT64 u8 = 0; + unsigned __int64 uI64; + double dblVal; + ULONG cbVal; + LPCUTF8 pStr; + CustomAttributeParser CA(pValue, cbValue); + CA.ValidateProlog(); + + // Get the calling convention. + cb = CorSigUncompressData(ps, &ulData); + ps += cb; + // Get the count of params. + cb = CorSigUncompressData(ps, &cParams); + ps += cb; + // Get the return value. + cb = CorSigUncompressData(ps, &ulData); + ps += cb; + if (ulData == ELEMENT_TYPE_VOID) + { + VWrite("%s\tctor args: (", preFix); + // For each param... + for (ULONG i=0; i<cParams; ++i) + { // Get the next param type. + cb = CorSigUncompressData(ps, &ulData); + ps += cb; + if (i) Write(", "); + DoObject: + switch (ulData) + { + // For ET_OBJECT, the next byte in the blob is the ET of the actual data. + case ELEMENT_TYPE_OBJECT: + CA.GetU1(&u1); + ulData = u1; + goto DoObject; + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: + CA.GetU1(&u1); + ulVal = u1; + goto PrintVal; + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + CA.GetU2(&u2); + ulVal = u2; + goto PrintVal; + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + CA.GetU4(&u4); + ulVal = u4; + PrintVal: + VWrite("%d", ulVal); + break; + case ELEMENT_TYPE_STRING: + CA.GetString(&pStr, &cbVal); + VWrite("\"%s\"", pStr); + break; + // The only class type that we accept is Type, which is stored as a string. + case ELEMENT_TYPE_CLASS: + // Eat the class type. + cb = CorSigUncompressData(ps, &ulData); + ps += cb; + // Get the name of the type. + CA.GetString(&pStr, &cbVal); + VWrite("typeof(%s)", pStr); + break; + case SERIALIZATION_TYPE_TYPE: + CA.GetString(&pStr, &cbVal); + VWrite("typeof(%s)", pStr); + break; + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + CA.GetU8(&u8); + uI64 = u8; + VWrite("%#lx", uI64); + break; + case ELEMENT_TYPE_R4: + dblVal = CA.GetR4(); + VWrite("%f", dblVal); + break; + case ELEMENT_TYPE_R8: + dblVal = CA.GetR8(); + VWrite("%f", dblVal); + break; + default: + // bail... + i = cParams; + Write(" <can not decode> "); + break; + } + } + WriteLine(")"); + } + + } + WriteLine(""); +} // void MDInfo::DisplayCustomAttributeInfo() + +// Print all custom values for the given token +// This function is used to print the custom value information for all tokens. +// which need slightly different formatting. preFix helps fix it up. +// + +void MDInfo::DisplayCustomAttributes(mdToken inToken, const char *preFix) +{ + HCORENUM customAttributeEnum = NULL; + mdTypeRef customAttributes[ENUM_BUFFER_SIZE]; + ULONG count, totalCount = 1; + HRESULT hr; + + while(SUCCEEDED(hr = m_pImport->EnumCustomAttributes( &customAttributeEnum, inToken, 0, + customAttributes, NumItems(customAttributes), &count)) && + count > 0) + { + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("%sCustomAttribute #%d (%08x)", preFix, totalCount, customAttributes[i]); + VWriteLine("%s-------------------------------------------------------", preFix); + DisplayCustomAttributeInfo(customAttributes[i], preFix); + } + } + m_pImport->CloseEnum( customAttributeEnum); +} // void MDInfo::DisplayCustomAttributes() + +// Show the passed-in token's permissions +// +// + +void MDInfo::DisplayPermissions(mdToken tk, const char *preFix) +{ + HCORENUM permissionEnum = NULL; + mdPermission permissions[ENUM_BUFFER_SIZE]; + ULONG count, totalCount = 1; + HRESULT hr; + + + while (SUCCEEDED(hr = m_pImport->EnumPermissionSets( &permissionEnum, + tk, 0, permissions, NumItems(permissions), &count)) && + count > 0) + { + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("%s\tPermission #%d (%08x)", preFix, totalCount, permissions[i]); + VWriteLine("%s\t-------------------------------------------------------", preFix); + DisplayPermissionInfo(permissions[i], preFix); + WriteLine(""); + } + } + m_pImport->CloseEnum( permissionEnum); +} // void MDInfo::DisplayPermissions() + +// print properties of given rolecheck +// +// + +void MDInfo::DisplayPermissionInfo(mdPermission inPermission, const char *preFix) +{ + DWORD dwAction; + const BYTE *pvPermission; + ULONG cbPermission; + const char *flagDesc = NULL; + char newPreFix[STRING_BUFFER_LEN]; + HRESULT hr; + + + hr = m_pImport->GetPermissionSetProps( inPermission, &dwAction, + (const void**)&pvPermission, &cbPermission); + if (FAILED(hr)) Error("GetPermissionSetProps failed.", hr); + + switch(dwAction) + { + case dclActionNil: flagDesc = "ActionNil"; break; + case dclRequest: flagDesc = "Request"; break; + case dclDemand: flagDesc = "Demand"; break; + case dclAssert: flagDesc = "Assert"; break; + case dclDeny: flagDesc = "Deny"; break; + case dclPermitOnly: flagDesc = "PermitOnly"; break; + case dclLinktimeCheck: flagDesc = "LinktimeCheck"; break; + case dclInheritanceCheck: flagDesc = "InheritanceCheck"; break; + case dclRequestMinimum: flagDesc = "RequestMinimum"; break; + case dclRequestOptional: flagDesc = "RequestOptional"; break; + case dclRequestRefuse: flagDesc = "RequestRefuse"; break; + case dclPrejitGrant: flagDesc = "PrejitGrant"; break; + case dclPrejitDenied: flagDesc = "PrejitDenied"; break; + case dclNonCasDemand: flagDesc = "NonCasDemand"; break; + case dclNonCasLinkDemand: flagDesc = "NonCasLinkDemand"; break; + case dclNonCasInheritance: flagDesc = "NonCasInheritance"; break; + + } + VWriteLine("%s\t\tAction : %s", preFix, flagDesc); + VWriteLine("%s\t\tBlobLen : %d", preFix, cbPermission); + if (cbPermission) + { + sprintf_s(newPreFix, STRING_BUFFER_LEN, "%s\tBlob", preFix); + DumpHex(newPreFix, pvPermission, cbPermission, false, 24); + } + + sprintf_s (newPreFix, STRING_BUFFER_LEN, "\t\t%s", preFix); + DisplayCustomAttributes(inPermission, newPreFix); +} // void MDInfo::DisplayPermissionInfo() + + +// simply prints out the given GUID in standard form + +LPWSTR MDInfo::GUIDAsString(GUID inGuid, __out_ecount(bufLen) LPWSTR guidString, ULONG bufLen) +{ + StringFromGUID2(inGuid, guidString, bufLen); + return guidString; +} // LPWSTR MDInfo::GUIDAsString() + +#ifdef FEATURE_COMINTEROP +LPCWSTR MDInfo::VariantAsString(VARIANT *pVariant) +{ + HRESULT hr = S_OK; + if (V_VT(pVariant) == VT_UNKNOWN) + { + _ASSERTE(V_UNKNOWN(pVariant) == NULL); + return W("<NULL>"); + } + else if (SUCCEEDED(hr = ::VariantChangeType(pVariant, pVariant, 0, VT_BSTR))) + return V_BSTR(pVariant); + else if (hr == DISP_E_BADVARTYPE && V_VT(pVariant) == VT_I8) + { + // allocate the bstr. + char szStr[32]; + WCHAR wszStr[32]; + // Set variant type to bstr. + V_VT(pVariant) = VT_BSTR; + // Create the ansi string. + sprintf_s(szStr, 32, "%I64d", V_CY(pVariant).int64); + // Convert to unicode. + WszMultiByteToWideChar(CP_ACP, 0, szStr, -1, wszStr, 32); + // convert to bstr and set variant value. + V_BSTR(pVariant) = ::SysAllocString(wszStr); + if (V_BSTR(pVariant) == NULL) + Error("SysAllocString() failed.", E_OUTOFMEMORY); + return V_BSTR(pVariant); + } + else + return W("ERROR"); + +} // LPWSTR MDInfo::VariantAsString() +#endif + +bool TrySigUncompress(PCCOR_SIGNATURE pData, // [IN] compressed data + ULONG *pDataOut, // [OUT] the expanded *pData + ULONG *cbCur) +{ + ULONG ulSize = CorSigUncompressData(pData, pDataOut); + if (ulSize == (ULONG)-1) + { + *cbCur = ulSize; + return false; + } else + { + *cbCur += ulSize; + return true; + } +} + +void MDInfo::DisplayFieldMarshal(mdToken inToken) +{ + PCCOR_SIGNATURE pvNativeType; // [OUT] native type of this field + ULONG cbNativeType; // [OUT] the count of bytes of *ppvNativeType + HRESULT hr; + + + hr = m_pImport->GetFieldMarshal( inToken, &pvNativeType, &cbNativeType); + if (FAILED(hr) && hr != CLDB_E_RECORD_NOTFOUND) Error("GetFieldMarshal failed.", hr); + if (hr != CLDB_E_RECORD_NOTFOUND) + { + ULONG cbCur = 0; + ULONG ulData; + ULONG ulStrLoc; + + char szNTDesc[STRING_BUFFER_LEN]; + + while (cbCur < cbNativeType) + { + ulStrLoc = 0; + + ulData = NATIVE_TYPE_MAX; + if (!TrySigUncompress(&pvNativeType[cbCur], &ulData, &cbCur)) + continue; + if (ulData >= sizeof(g_szNativeType)/sizeof(*g_szNativeType)) + { + cbCur = (ULONG)-1; + continue; + } + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, "%s ", g_szNativeType[ulData]); + switch (ulData) + { + case NATIVE_TYPE_FIXEDSYSSTRING: + { + if (cbCur < cbNativeType) + { + if (!TrySigUncompress(&pvNativeType[cbCur], &ulData, &cbCur)) + continue; + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, "{StringElementCount: %d} ",ulData); + } + } + break; + case NATIVE_TYPE_FIXEDARRAY: + { + if (cbCur < cbNativeType) + { + if (!TrySigUncompress(&pvNativeType[cbCur], &ulData, &cbCur)) + continue; + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, "{ArrayElementCount: %d",ulData); + + if (cbCur < cbNativeType) + { + if (!TrySigUncompress(&pvNativeType[cbCur], &ulData, &cbCur)) + continue; + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, ", ArrayElementType(NT): %d",ulData); + } + + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc,"}"); + } + } + break; + case NATIVE_TYPE_ARRAY: + { + if (cbCur < cbNativeType) + { + BOOL bElemTypeSpecified; + + if (!TrySigUncompress(&pvNativeType[cbCur], &ulData, &cbCur)) + continue; + if (ulData != NATIVE_TYPE_MAX) + { + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, "{ArrayElementType(NT): %d", ulData); + bElemTypeSpecified = TRUE; + } + else + { + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, "{"); + bElemTypeSpecified = FALSE; + } + + if (cbCur < cbNativeType) + { + if (bElemTypeSpecified) + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, ", "); + + if (!TrySigUncompress(&pvNativeType[cbCur], &ulData, &cbCur)) + continue; + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, "SizeParamIndex: %d",ulData); + + if (cbCur < cbNativeType) + { + if (!TrySigUncompress(&pvNativeType[cbCur], &ulData, &cbCur)) + continue; + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, ", SizeParamMultiplier: %d",ulData); + + if (cbCur < cbNativeType) + { + if (!TrySigUncompress(&pvNativeType[cbCur], &ulData, &cbCur)) + continue; + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, ", SizeConst: %d",ulData); + } + } + } + + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, "}"); + } + } + break; + case NATIVE_TYPE_SAFEARRAY: + { + if (cbCur < cbNativeType) + { + if (!TrySigUncompress(&pvNativeType[cbCur], &ulData, &cbCur)) + continue; + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, "{SafeArraySubType(VT): %d, ",ulData); + + // Extract the element type name if it is specified. + if (cbCur < cbNativeType) + { + LPUTF8 strTemp = NULL; + int strLen = 0; + int ByteCountLength = 0; + + strLen = CPackedLen::GetLength(&pvNativeType[cbCur], &ByteCountLength); + cbCur += ByteCountLength; + strTemp = (LPUTF8)(new char[strLen + 1]); + if(strTemp) + { + memcpy(strTemp, (LPUTF8)&pvNativeType[cbCur], strLen); + strTemp[strLen] = 0; + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, "ElementTypeName: %s}", strTemp); + cbCur += strLen; + _ASSERTE(cbCur == cbNativeType); + delete [] strTemp; + } + } + else + { + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, "ElementTypeName: }"); + } + } + } + break; + case NATIVE_TYPE_CUSTOMMARSHALER: + { + LPUTF8 strTemp = NULL; + int strLen = 0; + int ByteCountLength = 0; + + // Extract the typelib GUID. + strLen = CPackedLen::GetLength(&pvNativeType[cbCur], &ByteCountLength); + cbCur += ByteCountLength; + strTemp = (LPUTF8)(new char[strLen + 1]); + if(strTemp) + { + memcpy(strTemp, (LPUTF8)&pvNativeType[cbCur], strLen); + strTemp[strLen] = 0; + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, "{Typelib: %s, ", strTemp); + cbCur += strLen; + _ASSERTE(cbCur < cbNativeType); + delete [] strTemp; + } + // Extract the name of the native type. + strLen = CPackedLen::GetLength(&pvNativeType[cbCur], &ByteCountLength); + cbCur += ByteCountLength; + strTemp = (LPUTF8)(new char[strLen + 1]); + if(strTemp) + { + memcpy(strTemp, (LPUTF8)&pvNativeType[cbCur], strLen); + strTemp[strLen] = 0; + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, "Native: %s, ", strTemp); + cbCur += strLen; + _ASSERTE(cbCur < cbNativeType); + delete [] strTemp; + } + + // Extract the name of the custom marshaler. + strLen = CPackedLen::GetLength(&pvNativeType[cbCur], &ByteCountLength); + cbCur += ByteCountLength; + strTemp = (LPUTF8)(new char[strLen + 1]); + if(strTemp) + { + memcpy(strTemp, (LPUTF8)&pvNativeType[cbCur], strLen); + strTemp[strLen] = 0; + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, "Marshaler: %s, ", strTemp); + cbCur += strLen; + _ASSERTE(cbCur < cbNativeType); + delete [] strTemp; + } + // Extract the cookie string. + strLen = CPackedLen::GetLength(&pvNativeType[cbCur], &ByteCountLength); + cbCur += ByteCountLength; + if (strLen > 0) + { + strTemp = (LPUTF8)(new char[strLen + 1]); + if(strTemp) + { + memcpy(strTemp, (LPUTF8)&pvNativeType[cbCur], strLen); + strTemp[strLen] = 0; + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, "Cookie: "); + + // Copy the cookie string and transform the embedded nulls into \0's. + for (int i = 0; i < strLen - 1; i++, cbCur++) + { + if (strTemp[i] == 0) + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, "\\0"); + else + szNTDesc[ulStrLoc++] = strTemp[i]; + } + szNTDesc[ulStrLoc++] = strTemp[strLen - 1]; + cbCur++; + delete [] strTemp; + } + } + else + { + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, "Cookie: "); + } + + // Finish the custom marshaler native type description. + ulStrLoc += sprintf_s(szNTDesc + ulStrLoc, STRING_BUFFER_LEN-ulStrLoc, "}"); + _ASSERTE(cbCur <= cbNativeType); + } + break; + default: + { + // normal nativetype element: do nothing + } + } + VWriteLine("\t\t\t\t%s",szNTDesc); + if (ulData >= NATIVE_TYPE_MAX) + break; + } + if (cbCur == (ULONG)-1) + { + // There was something that we didn't grok in the signature. + // Just dump out the blob as hex + VWrite("\t\t\t\t{", szNTDesc); + while (cbNativeType--) + VWrite(" %2.2X", *pvNativeType++); + VWriteLine(" }"); + } + } +} // void MDInfo::DisplayFieldMarshal() + +void MDInfo::DisplayPinvokeInfo(mdToken inToken) +{ + HRESULT hr = NOERROR; + DWORD flags; + WCHAR rcImport[512]; + mdModuleRef tkModuleRef; + + char sFlags[STRING_BUFFER_LEN]; + + hr = m_pImport->GetPinvokeMap(inToken, &flags, rcImport, + NumItems(rcImport), 0, &tkModuleRef); + if (FAILED(hr)) + { + if (hr != CLDB_E_RECORD_NOTFOUND) + VWriteLine("ERROR: GetPinvokeMap failed.", hr); + return; + } + + WriteLine("\t\tPinvoke Map Data:"); + VWriteLine("\t\tEntry point: %S", rcImport); + VWriteLine("\t\tModule ref: %08x", tkModuleRef); + + sFlags[0] = 0; + ISFLAG(Pm, NoMangle); + ISFLAG(Pm, CharSetNotSpec); + ISFLAG(Pm, CharSetAnsi); + ISFLAG(Pm, CharSetUnicode); + ISFLAG(Pm, CharSetAuto); + ISFLAG(Pm, SupportsLastError); + ISFLAG(Pm, CallConvWinapi); + ISFLAG(Pm, CallConvCdecl); + ISFLAG(Pm, CallConvStdcall); + ISFLAG(Pm, CallConvThiscall); + ISFLAG(Pm, CallConvFastcall); + + ISFLAG(Pm, BestFitEnabled); + ISFLAG(Pm, BestFitDisabled); + ISFLAG(Pm, BestFitUseAssem); + ISFLAG(Pm, ThrowOnUnmappableCharEnabled); + ISFLAG(Pm, ThrowOnUnmappableCharDisabled); + ISFLAG(Pm, ThrowOnUnmappableCharUseAssem); + if (!*sFlags) + strcpy_s(sFlags, STRING_BUFFER_LEN, "[none]"); + + VWriteLine("\t\tMapping flags: %s (%08x)", sFlags, flags); +} // void MDInfo::DisplayPinvokeInfo() + + +///////////////////////////////////////////////////////////////////////// +// void DisplaySignature(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob); +// +// Display COM+ signature -- taken from cordump.cpp's DumpSignature +///////////////////////////////////////////////////////////////////////// +void MDInfo::DisplaySignature(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, const char *preFix) +{ + ULONG cbCur = 0; + ULONG cb; + // 428793: Prefix complained correctly about unitialized data. + ULONG ulData = (ULONG) IMAGE_CEE_CS_CALLCONV_MAX; + ULONG ulArgs; + HRESULT hr = NOERROR; + ULONG ulSigBlobStart = ulSigBlob; + + // initialize sigBuf + InitSigBuffer(); + + cb = CorSigUncompressData(pbSigBlob, &ulData); + VWriteLine("%s\t\tCallCnvntn: %s", preFix, (g_strCalling[ulData & IMAGE_CEE_CS_CALLCONV_MASK])); + if (cb>ulSigBlob) + goto ErrExit; + cbCur += cb; + ulSigBlob -= cb; + + if (ulData & IMAGE_CEE_CS_CALLCONV_HASTHIS) + VWriteLine("%s\t\thasThis ", preFix); + if (ulData & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS) + VWriteLine("%s\t\texplicit ", preFix); + if (ulData & IMAGE_CEE_CS_CALLCONV_GENERIC) + VWriteLine("%s\t\tgeneric ", preFix); + + // initialize sigBuf + InitSigBuffer(); + if ( isCallConv(ulData,IMAGE_CEE_CS_CALLCONV_FIELD) ) + { + + // display field type + if (FAILED(hr = GetOneElementType(&pbSigBlob[cbCur], ulSigBlob, &cb))) + goto ErrExit; + VWriteLine("%s\t\tField type: %s", preFix, (LPSTR)m_sigBuf.Ptr()); + if (cb>ulSigBlob) + goto ErrExit; + cbCur += cb; + ulSigBlob -= cb; + } + else + { + if (ulData & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + ULONG ulTyArgs; + cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulTyArgs); + if (cb>ulSigBlob) + goto ErrExit; + cbCur += cb; + ulSigBlob -= cb; + VWriteLine("%s\t\tType Arity:%d ", preFix, ulTyArgs); + } + cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulArgs); + if (cb>ulSigBlob) + goto ErrExit; + cbCur += cb; + ulSigBlob -= cb; + + if (ulData != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG && ulData != IMAGE_CEE_CS_CALLCONV_GENERICINST) + { + // display return type when it is not a local varsig + if (FAILED(hr = GetOneElementType(&pbSigBlob[cbCur], ulSigBlob, &cb))) + goto ErrExit; + VWriteLine("%s\t\tReturnType:%s", preFix, (LPSTR)m_sigBuf.Ptr()); + if (cb>ulSigBlob) + goto ErrExit; + cbCur += cb; + ulSigBlob -= cb; + } + + // display count of argument + // display arguments + if (ulSigBlob) + VWriteLine("%s\t\t%ld Arguments", preFix, ulArgs); + else + VWriteLine("%s\t\tNo arguments.", preFix); + + ULONG i = 0; + while (i < ulArgs && ulSigBlob > 0) + { + ULONG ulDataTemp; + + // Handle the sentinal for varargs because it isn't counted in the args. + CorSigUncompressData(&pbSigBlob[cbCur], &ulDataTemp); + ++i; + + // initialize sigBuf + InitSigBuffer(); + + if (FAILED(hr = GetOneElementType(&pbSigBlob[cbCur], ulSigBlob, &cb))) + goto ErrExit; + + VWriteLine("%s\t\t\tArgument #%ld: %s",preFix, i, (LPSTR)m_sigBuf.Ptr()); + + if (cb>ulSigBlob) + goto ErrExit; + + cbCur += cb; + ulSigBlob -= cb; + } + } + + // Nothing consumed but not yet counted. + cb = 0; + +ErrExit: + // We should have consumed all signature blob. If not, dump the sig in hex. + // Also dump in hex if so requested. + if (m_DumpFilter & dumpMoreHex || ulSigBlob != 0) + { + // Did we not consume enough, or try to consume too much? + if (cb > ulSigBlob) + WriteLine("\tERROR IN SIGNATURE: Signature should be larger."); + else + if (cb < ulSigBlob) + { + VWrite("\tERROR IN SIGNATURE: Not all of signature blob was consumed. %d byte(s) remain", ulSigBlob); + // If it is short, just append it to the end. + if (ulSigBlob < 4) + { + Write(": "); + for (; ulSigBlob; ++cbCur, --ulSigBlob) + VWrite("%02x ", pbSigBlob[cbCur]); + WriteLine(""); + goto ErrExit2; + } + WriteLine(""); + } + + // Any appropriate error message has been issued. Dump sig in hex, as determined + // by error or command line switch. + cbCur = 0; + ulSigBlob = ulSigBlobStart; + char rcNewPrefix[80]; + sprintf_s(rcNewPrefix, 80, "%s\t\tSignature ", preFix); + DumpHex(rcNewPrefix, pbSigBlob, ulSigBlob, false, 24); + } +ErrExit2: + if (FAILED(hr)) + Error("ERROR!! Bad signature blob value!"); + return; +} // void MDInfo::DisplaySignature() + + +///////////////////////////////////////////////////////////////////////// +// HRESULT GetOneElementType(mdScope tkScope, BYTE *pbSigBlob, ULONG ulSigBlob, ULONG *pcb) +// +// Adds description of element type to the end of buffer -- caller must ensure +// buffer is large enough. +///////////////////////////////////////////////////////////////////////// +HRESULT MDInfo::GetOneElementType(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, ULONG *pcb) +{ + HRESULT hr = S_OK; // A result. + ULONG cbCur = 0; + ULONG cb; + ULONG ulData = ELEMENT_TYPE_MAX; + ULONG ulTemp; + int iTemp = 0; + mdToken tk; + + cb = CorSigUncompressData(pbSigBlob, &ulData); + cbCur += cb; + + // Handle the modifiers. + if (ulData & ELEMENT_TYPE_MODIFIER) + { + if (ulData == ELEMENT_TYPE_SENTINEL) + IfFailGo(AddToSigBuffer("<ELEMENT_TYPE_SENTINEL>")); + else if (ulData == ELEMENT_TYPE_PINNED) + IfFailGo(AddToSigBuffer("PINNED")); + else + { + hr = E_FAIL; + goto ErrExit; + } + if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb))) + goto ErrExit; + cbCur += cb; + goto ErrExit; + } + + // Handle the underlying element types. + if (ulData >= ELEMENT_TYPE_MAX) + { + hr = E_FAIL; + goto ErrExit; + } + while (ulData == ELEMENT_TYPE_PTR || ulData == ELEMENT_TYPE_BYREF) + { + IfFailGo(AddToSigBuffer(" ")); + IfFailGo(AddToSigBuffer(g_szMapElementType[ulData])); + cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulData); + cbCur += cb; + } + IfFailGo(AddToSigBuffer(" ")); + IfFailGo(AddToSigBuffer(g_szMapElementType[ulData])); + if (CorIsPrimitiveType((CorElementType)ulData) || + ulData == ELEMENT_TYPE_TYPEDBYREF || + ulData == ELEMENT_TYPE_OBJECT || + ulData == ELEMENT_TYPE_I || + ulData == ELEMENT_TYPE_U) + { + // If this is a primitive type, we are done + goto ErrExit; + } + if (ulData == ELEMENT_TYPE_VALUETYPE || + ulData == ELEMENT_TYPE_CLASS || + ulData == ELEMENT_TYPE_CMOD_REQD || + ulData == ELEMENT_TYPE_CMOD_OPT) + { + cb = CorSigUncompressToken(&pbSigBlob[cbCur], &tk); + cbCur += cb; + + // get the name of type ref. Don't care if truncated + if (TypeFromToken(tk) == mdtTypeDef || TypeFromToken(tk) == mdtTypeRef) + { + sprintf_s(m_tempFormatBuffer, STRING_BUFFER_LEN, " %ls",TypeDeforRefName(tk, m_szTempBuf, NumItems(m_szTempBuf))); + IfFailGo(AddToSigBuffer(m_tempFormatBuffer)); + } + else + { + _ASSERTE(TypeFromToken(tk) == mdtTypeSpec); + sprintf_s(m_tempFormatBuffer, STRING_BUFFER_LEN, " %8x", tk); + IfFailGo(AddToSigBuffer(m_tempFormatBuffer)); + } + if (ulData == ELEMENT_TYPE_CMOD_REQD || + ulData == ELEMENT_TYPE_CMOD_OPT) + { + if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb))) + goto ErrExit; + cbCur += cb; + } + + goto ErrExit; + } + if (ulData == ELEMENT_TYPE_SZARRAY) + { + // display the base type of SZARRAY + if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb))) + goto ErrExit; + cbCur += cb; + goto ErrExit; + } + // instantiated type + if (ulData == ELEMENT_TYPE_GENERICINST) + { + // display the type constructor + if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb))) + goto ErrExit; + cbCur += cb; + ULONG numArgs; + cb = CorSigUncompressData(&pbSigBlob[cbCur], &numArgs); + cbCur += cb; + IfFailGo(AddToSigBuffer("<")); + + while (numArgs > 0) + { + if (cbCur > ulSigBlob) + goto ErrExit; + if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb))) + goto ErrExit; + cbCur += cb; + --numArgs; + if (numArgs > 0) + IfFailGo(AddToSigBuffer(",")); + } + IfFailGo(AddToSigBuffer(">")); + goto ErrExit; + } + if (ulData == ELEMENT_TYPE_VAR) + { + ULONG index; + cb = CorSigUncompressData(&pbSigBlob[cbCur], &index); + cbCur += cb; + sprintf_s(m_tempFormatBuffer, STRING_BUFFER_LEN, "!%d", index); + IfFailGo(AddToSigBuffer(m_tempFormatBuffer)); + goto ErrExit; + } + if (ulData == ELEMENT_TYPE_MVAR) + { + ULONG index; + cb = CorSigUncompressData(&pbSigBlob[cbCur], &index); + cbCur += cb; + sprintf_s(m_tempFormatBuffer, STRING_BUFFER_LEN, "!!%d", index); + IfFailGo(AddToSigBuffer(m_tempFormatBuffer)); + goto ErrExit; + } + if (ulData == ELEMENT_TYPE_FNPTR) + { + cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulData); + cbCur += cb; + if (ulData & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS) + IfFailGo(AddToSigBuffer(" explicit")); + if (ulData & IMAGE_CEE_CS_CALLCONV_HASTHIS) + IfFailGo(AddToSigBuffer(" hasThis")); + + IfFailGo(AddToSigBuffer(" ")); + IfFailGo(AddToSigBuffer(g_strCalling[ulData & IMAGE_CEE_CS_CALLCONV_MASK])); + + // Get number of args + ULONG numArgs; + cb = CorSigUncompressData(&pbSigBlob[cbCur], &numArgs); + cbCur += cb; + + // do return type + if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb))) + goto ErrExit; + cbCur += cb; + + IfFailGo(AddToSigBuffer("(")); + while (numArgs > 0) + { + if (cbCur > ulSigBlob) + goto ErrExit; + if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb))) + goto ErrExit; + cbCur += cb; + --numArgs; + if (numArgs > 0) + IfFailGo(AddToSigBuffer(",")); + } + IfFailGo(AddToSigBuffer(" )")); + goto ErrExit; + } + + if(ulData != ELEMENT_TYPE_ARRAY) return E_FAIL; + + // display the base type of SDARRAY + if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb))) + goto ErrExit; + cbCur += cb; + + // display the rank of MDARRAY + cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulData); + cbCur += cb; + sprintf_s(m_tempFormatBuffer, STRING_BUFFER_LEN, " %d", ulData); + IfFailGo(AddToSigBuffer(m_tempFormatBuffer)); + if (ulData == 0) + // we are done if no rank specified + goto ErrExit; + + // how many dimensions have size specified? + cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulData); + cbCur += cb; + sprintf_s(m_tempFormatBuffer, STRING_BUFFER_LEN, " %d", ulData); + IfFailGo(AddToSigBuffer(m_tempFormatBuffer)); + while (ulData) + { + cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulTemp); + sprintf_s(m_tempFormatBuffer, STRING_BUFFER_LEN, " %d", ulTemp); + IfFailGo(AddToSigBuffer(m_tempFormatBuffer)); + cbCur += cb; + ulData--; + } + // how many dimensions have lower bounds specified? + cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulData); + cbCur += cb; + sprintf_s(m_tempFormatBuffer, STRING_BUFFER_LEN, " %d", ulData); + IfFailGo(AddToSigBuffer(m_tempFormatBuffer)); + while (ulData) + { + + cb = CorSigUncompressSignedInt(&pbSigBlob[cbCur], &iTemp); + sprintf_s(m_tempFormatBuffer, STRING_BUFFER_LEN, " %d", iTemp); + IfFailGo(AddToSigBuffer(m_tempFormatBuffer)); + cbCur += cb; + ulData--; + } + +ErrExit: + if (cbCur > ulSigBlob) + hr = E_FAIL; + *pcb = cbCur; + return hr; +} // HRESULT MDInfo::GetOneElementType() + +// Display the fields of the N/Direct custom value structure. + +void MDInfo::DisplayCorNativeLink(COR_NATIVE_LINK *pCorNLnk, const char *preFix) +{ + // Print the LinkType. + const char *curField = "\tLink Type : "; + switch(pCorNLnk->m_linkType) + { + case nltNone: + VWriteLine("%s%s%s(%02x)", preFix, curField, "nltNone", pCorNLnk->m_linkType); + break; + case nltAnsi: + VWriteLine("%s%s%s(%02x)", preFix, curField, "nltAnsi", pCorNLnk->m_linkType); + break; + case nltUnicode: + VWriteLine("%s%s%s(%02x)", preFix, curField, "nltUnicode", pCorNLnk->m_linkType); + break; + case nltAuto: + VWriteLine("%s%s%s(%02x)", preFix, curField, "nltAuto", pCorNLnk->m_linkType); + break; + default: + _ASSERTE(!"Invalid Native Link Type!"); + } + + // Print the link flags + curField = "\tLink Flags : "; + switch(pCorNLnk->m_flags) + { + case nlfNone: + VWriteLine("%s%s%s(%02x)", preFix, curField, "nlfNone", pCorNLnk->m_flags); + break; + case nlfLastError: + VWriteLine("%s%s%s(%02x)", preFix, curField, "nlfLastError", pCorNLnk->m_flags); + break; + default: + _ASSERTE(!"Invalid Native Link Flags!"); + } + + // Print the entry point. + WCHAR memRefName[STRING_BUFFER_LEN]; + HRESULT hr; + hr = m_pImport->GetMemberRefProps( pCorNLnk->m_entryPoint, NULL, memRefName, + STRING_BUFFER_LEN, NULL, NULL, NULL); + if (FAILED(hr)) Error("GetMemberRefProps failed.", hr); + VWriteLine("%s\tEntry Point : %ls (0x%08x)", preFix, memRefName, pCorNLnk->m_entryPoint); +} // void MDInfo::DisplayCorNativeLink() + +// Fills given varaint with value given in pValue and of type in bCPlusTypeFlag +// +// Taken from MetaInternal.cpp + +HRESULT _FillVariant( + BYTE bCPlusTypeFlag, + const void *pValue, + ULONG cbValue, + VARIANT *pvar) +{ + HRESULT hr = NOERROR; + switch (bCPlusTypeFlag) + { + case ELEMENT_TYPE_BOOLEAN: + V_VT(pvar) = VT_BOOL; + V_BOOL(pvar) = *((BYTE*)pValue); //*((UNALIGNED VARIANT_BOOL *)pValue); + break; + case ELEMENT_TYPE_I1: + V_VT(pvar) = VT_I1; + V_I1(pvar) = *((CHAR*)pValue); + break; + case ELEMENT_TYPE_U1: + V_VT(pvar) = VT_UI1; + V_UI1(pvar) = *((BYTE*)pValue); + break; + case ELEMENT_TYPE_I2: + V_VT(pvar) = VT_I2; + V_I2(pvar) = GET_UNALIGNED_VAL16(pValue); + break; + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_CHAR: + V_VT(pvar) = VT_UI2; + V_UI2(pvar) = GET_UNALIGNED_VAL16(pValue); + break; + case ELEMENT_TYPE_I4: + V_VT(pvar) = VT_I4; + V_I4(pvar) = GET_UNALIGNED_VAL32(pValue); + break; + case ELEMENT_TYPE_U4: + V_VT(pvar) = VT_UI4; + V_UI4(pvar) = GET_UNALIGNED_VAL32(pValue); + break; + case ELEMENT_TYPE_R4: + { + V_VT(pvar) = VT_R4; + __int32 Value = GET_UNALIGNED_VAL32(pValue); + V_R4(pvar) = (float &)Value; + } + break; + case ELEMENT_TYPE_R8: + { + V_VT(pvar) = VT_R8; + __int64 Value = GET_UNALIGNED_VAL64(pValue); + V_R8(pvar) = (double &) Value; + } + + break; + case ELEMENT_TYPE_STRING: + { + V_VT(pvar) = VT_BSTR; + WCHAR *TempString;; +#if BIGENDIAN + TempString = (WCHAR *)alloca(cbValue); + memcpy(TempString, pValue, cbValue); + SwapStringLength(TempString, cbValue/sizeof(WCHAR)); +#else + TempString = (WCHAR *)pValue; +#endif + // allocated bstr here + V_BSTR(pvar) = ::SysAllocStringLen((LPWSTR)TempString, cbValue/sizeof(WCHAR)); + if (V_BSTR(pvar) == NULL) + hr = E_OUTOFMEMORY; + } + break; + case ELEMENT_TYPE_CLASS: + V_VT(pvar) = VT_UNKNOWN; + V_UNKNOWN(pvar) = NULL; + // _ASSERTE( GET_UNALIGNED_VAL32(pValue) == 0); + break; + case ELEMENT_TYPE_I8: + V_VT(pvar) = VT_I8; + V_CY(pvar).int64 = GET_UNALIGNED_VAL64(pValue); + break; + case ELEMENT_TYPE_U8: + V_VT(pvar) = VT_UI8; + V_CY(pvar).int64 = GET_UNALIGNED_VAL64(pValue); + break; + case ELEMENT_TYPE_VOID: + V_VT(pvar) = VT_EMPTY; + break; + default: + _ASSERTE(!"bad constant value type!"); + } + + return hr; +} // HRESULT _FillVariant() + +void MDInfo::DisplayAssembly() +{ + if (m_pAssemblyImport) + { + DisplayAssemblyInfo(); + DisplayAssemblyRefs(); + DisplayFiles(); + DisplayExportedTypes(); + DisplayManifestResources(); + } +} // void MDInfo::DisplayAssembly() + +void MDInfo::DisplayAssemblyInfo() +{ + HRESULT hr; + mdAssembly mda; + const BYTE *pbPublicKey; + ULONG cbPublicKey; + ULONG ulHashAlgId; + WCHAR szName[STRING_BUFFER_LEN]; + ASSEMBLYMETADATA MetaData; + DWORD dwFlags; + + hr = m_pAssemblyImport->GetAssemblyFromScope(&mda); + if (hr == CLDB_E_RECORD_NOTFOUND) + return; + else if (FAILED(hr)) Error("GetAssemblyFromScope() failed.", hr); + + // Get the required sizes for the arrays of locales, processors etc. + ZeroMemory(&MetaData, sizeof(ASSEMBLYMETADATA)); + hr = m_pAssemblyImport->GetAssemblyProps(mda, + NULL, NULL, // Public Key. + NULL, // Hash Algorithm. + NULL, 0, NULL, // Name. + &MetaData, + NULL); // Flags. + if (FAILED(hr)) Error("GetAssemblyProps() failed.", hr); + + // Allocate space for the arrays in the ASSEMBLYMETADATA structure. + if (MetaData.cbLocale) + MetaData.szLocale = new WCHAR[MetaData.cbLocale]; + if (MetaData.ulProcessor) + MetaData.rProcessor = new DWORD[MetaData.ulProcessor]; + if (MetaData.ulOS) + MetaData.rOS = new OSINFO[MetaData.ulOS]; + + hr = m_pAssemblyImport->GetAssemblyProps(mda, + (const void **)&pbPublicKey, &cbPublicKey, + &ulHashAlgId, + szName, STRING_BUFFER_LEN, NULL, + &MetaData, + &dwFlags); + if (FAILED(hr)) Error("GetAssemblyProps() failed.", hr); + WriteLine("Assembly"); + WriteLine("-------------------------------------------------------"); + VWriteLine("\tToken: 0x%08x", mda); + VWriteLine("\tName : %ls", szName); + DumpHex("\tPublic Key ", pbPublicKey, cbPublicKey, false, 24); + VWriteLine("\tHash Algorithm : 0x%08x", ulHashAlgId); + DisplayASSEMBLYMETADATA(&MetaData); + if(MetaData.szLocale) delete [] MetaData.szLocale; + if(MetaData.rProcessor) delete [] MetaData.rProcessor; + if(MetaData.rOS) delete [] MetaData.rOS; + + char sFlags[STRING_BUFFER_LEN]; + DWORD flags = dwFlags; + + sFlags[0] = 0; + ISFLAG(Af, PublicKey); + ISFLAG(Af, Retargetable); + ISFLAG(AfContentType_, WindowsRuntime); + + if (!*sFlags) + strcpy_s(sFlags, STRING_BUFFER_LEN, "[none]"); + + VWriteLine("\tFlags : %s (%08x)", sFlags, dwFlags); + DisplayCustomAttributes(mda, "\t"); + DisplayPermissions(mda, "\t"); + WriteLine(""); +} // void MDInfo::DisplayAssemblyInfo() + +void MDInfo::DisplayAssemblyRefs() +{ + HCORENUM assemblyRefEnum = NULL; + mdAssemblyRef AssemblyRefs[ENUM_BUFFER_SIZE]; + ULONG count; + ULONG totalCount = 1; + HRESULT hr; + + while (SUCCEEDED(hr = m_pAssemblyImport->EnumAssemblyRefs( &assemblyRefEnum, + AssemblyRefs, NumItems(AssemblyRefs), &count)) && + count > 0) + { + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("AssemblyRef #%d (%08x)", totalCount, AssemblyRefs[i]); + WriteLine("-------------------------------------------------------"); + DisplayAssemblyRefInfo(AssemblyRefs[i]); + WriteLine(""); + } + } + m_pAssemblyImport->CloseEnum(assemblyRefEnum); +} // void MDInfo::DisplayAssemblyRefs() + +void MDInfo::DisplayAssemblyRefInfo(mdAssemblyRef inAssemblyRef) +{ + HRESULT hr; + const BYTE *pbPublicKeyOrToken; + ULONG cbPublicKeyOrToken; + WCHAR szName[STRING_BUFFER_LEN]; + ASSEMBLYMETADATA MetaData; + const BYTE *pbHashValue; + ULONG cbHashValue; + DWORD dwFlags; + + VWriteLine("\tToken: 0x%08x", inAssemblyRef); + + // Get sizes for the arrays in the ASSEMBLYMETADATA structure. + ZeroMemory(&MetaData, sizeof(ASSEMBLYMETADATA)); + hr = m_pAssemblyImport->GetAssemblyRefProps(inAssemblyRef, + NULL, NULL, // Public Key or Token. + NULL, 0, NULL, // Name. + &MetaData, + NULL, NULL, // HashValue. + NULL); // Flags. + if (FAILED(hr)) Error("GetAssemblyRefProps() failed.", hr); + + // Allocate space for the arrays in the ASSEMBLYMETADATA structure. + if (MetaData.cbLocale) + MetaData.szLocale = new WCHAR[MetaData.cbLocale]; + if (MetaData.ulProcessor) + MetaData.rProcessor = new DWORD[MetaData.ulProcessor]; + if (MetaData.ulOS) + MetaData.rOS = new OSINFO[MetaData.ulOS]; + + hr = m_pAssemblyImport->GetAssemblyRefProps(inAssemblyRef, + (const void **)&pbPublicKeyOrToken, &cbPublicKeyOrToken, + szName, STRING_BUFFER_LEN, NULL, + &MetaData, + (const void **)&pbHashValue, &cbHashValue, + &dwFlags); + if (FAILED(hr)) Error("GetAssemblyRefProps() failed.", hr); + + DumpHex("\tPublic Key or Token", pbPublicKeyOrToken, cbPublicKeyOrToken, false, 24); + VWriteLine("\tName: %ls", szName); + DisplayASSEMBLYMETADATA(&MetaData); + if(MetaData.szLocale) delete [] MetaData.szLocale; + if(MetaData.rProcessor) delete [] MetaData.rProcessor; + if(MetaData.rOS) delete [] MetaData.rOS; + DumpHex("\tHashValue Blob", pbHashValue, cbHashValue, false, 24); + + char sFlags[STRING_BUFFER_LEN]; + DWORD flags = dwFlags; + + sFlags[0] = 0; + ISFLAG(Af, PublicKey); + ISFLAG(Af, Retargetable); + ISFLAG(AfContentType_, WindowsRuntime); +#if 0 + ISFLAG(Af, LegacyLibrary); + ISFLAG(Af, LegacyPlatform); + ISFLAG(Af, Library); + ISFLAG(Af, Platform); +#endif + if (!*sFlags) + strcpy_s(sFlags, STRING_BUFFER_LEN, "[none]"); + + VWriteLine("\tFlags: %s (%08x)", sFlags, dwFlags); + DisplayCustomAttributes(inAssemblyRef, "\t"); + WriteLine(""); +} // void MDInfo::DisplayAssemblyRefInfo() + +void MDInfo::DisplayFiles() +{ + HCORENUM fileEnum = NULL; + mdFile Files[ENUM_BUFFER_SIZE]; + ULONG count; + ULONG totalCount = 1; + HRESULT hr; + + while (SUCCEEDED(hr = m_pAssemblyImport->EnumFiles( &fileEnum, + Files, NumItems(Files), &count)) && + count > 0) + { + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("File #%d (%08x)", totalCount, Files[i]); + WriteLine("-------------------------------------------------------"); + DisplayFileInfo(Files[i]); + WriteLine(""); + } + } + m_pAssemblyImport->CloseEnum(fileEnum); +} // void MDInfo::DisplayFiles() + +void MDInfo::DisplayFileInfo(mdFile inFile) +{ + HRESULT hr; + WCHAR szName[STRING_BUFFER_LEN]; + const BYTE *pbHashValue; + ULONG cbHashValue; + DWORD dwFlags; + + VWriteLine("\tToken: 0x%08x", inFile); + + hr = m_pAssemblyImport->GetFileProps(inFile, + szName, STRING_BUFFER_LEN, NULL, + (const void **)&pbHashValue, &cbHashValue, + &dwFlags); + if (FAILED(hr)) Error("GetFileProps() failed.", hr); + VWriteLine("\tName : %ls", szName); + DumpHex("\tHashValue Blob ", pbHashValue, cbHashValue, false, 24); + + char sFlags[STRING_BUFFER_LEN]; + DWORD flags = dwFlags; + + sFlags[0] = 0; + ISFLAG(Ff, ContainsMetaData); + ISFLAG(Ff, ContainsNoMetaData); + if (!*sFlags) + strcpy_s(sFlags, STRING_BUFFER_LEN, "[none]"); + + VWriteLine("\tFlags : %s (%08x)", sFlags, dwFlags); + DisplayCustomAttributes(inFile, "\t"); + WriteLine(""); + +} // MDInfo::DisplayFileInfo() + +void MDInfo::DisplayExportedTypes() +{ + HCORENUM comTypeEnum = NULL; + mdExportedType ExportedTypes[ENUM_BUFFER_SIZE]; + ULONG count; + ULONG totalCount = 1; + HRESULT hr; + + while (SUCCEEDED(hr = m_pAssemblyImport->EnumExportedTypes( &comTypeEnum, + ExportedTypes, NumItems(ExportedTypes), &count)) && + count > 0) + { + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("ExportedType #%d (%08x)", totalCount, ExportedTypes[i]); + WriteLine("-------------------------------------------------------"); + DisplayExportedTypeInfo(ExportedTypes[i]); + WriteLine(""); + } + } + m_pAssemblyImport->CloseEnum(comTypeEnum); +} // void MDInfo::DisplayExportedTypes() + +void MDInfo::DisplayExportedTypeInfo(mdExportedType inExportedType) +{ + HRESULT hr; + WCHAR szName[STRING_BUFFER_LEN]; + mdToken tkImplementation; + mdTypeDef tkTypeDef; + DWORD dwFlags; + char sFlags[STRING_BUFFER_LEN]; + + VWriteLine("\tToken: 0x%08x", inExportedType); + + hr = m_pAssemblyImport->GetExportedTypeProps(inExportedType, + szName, STRING_BUFFER_LEN, NULL, + &tkImplementation, + &tkTypeDef, + &dwFlags); + if (FAILED(hr)) Error("GetExportedTypeProps() failed.", hr); + VWriteLine("\tName: %ls", szName); + VWriteLine("\tImplementation token: 0x%08x", tkImplementation); + VWriteLine("\tTypeDef token: 0x%08x", tkTypeDef); + VWriteLine("\tFlags : %s (%08x)",ClassFlags(dwFlags, sFlags), dwFlags); + DisplayCustomAttributes(inExportedType, "\t"); + WriteLine(""); +} // void MDInfo::DisplayExportedTypeInfo() + +void MDInfo::DisplayManifestResources() +{ + HCORENUM manifestResourceEnum = NULL; + mdManifestResource ManifestResources[ENUM_BUFFER_SIZE]; + ULONG count; + ULONG totalCount = 1; + HRESULT hr; + + while (SUCCEEDED(hr = m_pAssemblyImport->EnumManifestResources( &manifestResourceEnum, + ManifestResources, NumItems(ManifestResources), &count)) && + count > 0) + { + for (ULONG i = 0; i < count; i++, totalCount++) + { + VWriteLine("ManifestResource #%d (%08x)", totalCount, ManifestResources[i]); + WriteLine("-------------------------------------------------------"); + DisplayManifestResourceInfo(ManifestResources[i]); + WriteLine(""); + } + } + m_pAssemblyImport->CloseEnum(manifestResourceEnum); +} // void MDInfo::DisplayManifestResources() + +void MDInfo::DisplayManifestResourceInfo(mdManifestResource inManifestResource) +{ + HRESULT hr; + WCHAR szName[STRING_BUFFER_LEN]; + mdToken tkImplementation; + DWORD dwOffset; + DWORD dwFlags; + + VWriteLine("\tToken: 0x%08x", inManifestResource); + + hr = m_pAssemblyImport->GetManifestResourceProps(inManifestResource, + szName, STRING_BUFFER_LEN, NULL, + &tkImplementation, + &dwOffset, + &dwFlags); + if (FAILED(hr)) Error("GetManifestResourceProps() failed.", hr); + VWriteLine("Name: %ls", szName); + VWriteLine("Implementation token: 0x%08x", tkImplementation); + VWriteLine("Offset: 0x%08x", dwOffset); + + char sFlags[STRING_BUFFER_LEN]; + DWORD flags = dwFlags; + + sFlags[0] = 0; + ISFLAG(Mr, Public); + ISFLAG(Mr, Private); + if (!*sFlags) + strcpy_s(sFlags, STRING_BUFFER_LEN, "[none]"); + + VWriteLine("\tFlags: %s (%08x)", sFlags, dwFlags); + DisplayCustomAttributes(inManifestResource, "\t"); + WriteLine(""); +} // void MDInfo::DisplayManifestResourceInfo() + +void MDInfo::DisplayASSEMBLYMETADATA(ASSEMBLYMETADATA *pMetaData) +{ + ULONG i; + + VWriteLine("\tVersion: %d.%d.%d.%d", pMetaData->usMajorVersion, pMetaData->usMinorVersion, pMetaData->usBuildNumber, pMetaData->usRevisionNumber); + VWriteLine("\tMajor Version: 0x%08x", pMetaData->usMajorVersion); + VWriteLine("\tMinor Version: 0x%08x", pMetaData->usMinorVersion); + VWriteLine("\tBuild Number: 0x%08x", pMetaData->usBuildNumber); + VWriteLine("\tRevision Number: 0x%08x", pMetaData->usRevisionNumber); + VWriteLine("\tLocale: %ls", pMetaData->cbLocale ? pMetaData->szLocale : W("<null>")); + for (i = 0; i < pMetaData->ulProcessor; i++) + VWriteLine("\tProcessor #%ld: 0x%08x", i+1, pMetaData->rProcessor[i]); + for (i = 0; i < pMetaData->ulOS; i++) + { + VWriteLine("\tOS #%ld:", i+1); + VWriteLine("\t\tOS Platform ID: 0x%08x", pMetaData->rOS[i].dwOSPlatformId); + VWriteLine("\t\tOS Major Version: 0x%08x", pMetaData->rOS[i].dwOSMajorVersion); + VWriteLine("\t\tOS Minor Version: 0x%08x", pMetaData->rOS[i].dwOSMinorVersion); + } +} // void MDInfo::DisplayASSEMBLYMETADATA() + +void MDInfo::DisplayUserStrings() +{ + HCORENUM stringEnum = NULL; // string enumerator. + mdString Strings[ENUM_BUFFER_SIZE]; // String tokens from enumerator. + CQuickArray<WCHAR> rUserString; // Buffer to receive string. + WCHAR *szUserString; // Working pointer into buffer. + ULONG chUserString; // Size of user string. + CQuickArray<char> rcBuf; // Buffer to hold the BLOB version of the string. + char *szBuf; // Working pointer into buffer. + ULONG chBuf; // Saved size of the user string. + ULONG count; // Items returned from enumerator. + ULONG totalCount = 1; // Running count of strings. + bool bUnprint = false; // Is an unprintable character found? + HRESULT hr; // A result. + while (SUCCEEDED(hr = m_pImport->EnumUserStrings( &stringEnum, + Strings, NumItems(Strings), &count)) && + count > 0) + { + if (totalCount == 1) + { // If only one, it is the NULL string, so don't print it. + WriteLine("User Strings"); + WriteLine("-------------------------------------------------------"); + } + for (ULONG i = 0; i < count; i++, totalCount++) + { + do { // Try to get the string into the existing buffer. + hr = m_pImport->GetUserString( Strings[i], rUserString.Ptr(),(ULONG32)rUserString.MaxSize(), &chUserString); + if (hr == CLDB_S_TRUNCATION) + { // Buffer wasn't big enough, try to enlarge it. + if (FAILED(rUserString.ReSizeNoThrow(chUserString))) + Error("malloc failed.", E_OUTOFMEMORY); + continue; + } + } while (hr == CLDB_S_TRUNCATION); + if (FAILED(hr)) Error("GetUserString failed.", hr); + + szUserString = rUserString.Ptr(); + chBuf = chUserString; + + VWrite("%08x : (%2d) L\"", Strings[i], chUserString); + for (ULONG j=0; j<chUserString; j++) + { + switch (*szUserString) + { + case 0: + Write("\\0"); break; + case L'\r': + Write("\\r"); break; + case L'\n': + Write("\\n"); break; + case L'\t': + Write("\\t"); break; + default: + if (iswprint(*szUserString)) + VWrite("%lc", *szUserString); + else + { + bUnprint = true; + Write("."); + } + break; + } + ++szUserString; + if((j>0)&&((j&0x7F)==0)) WriteLine(""); + } + WriteLine("\""); + + // Print the user string as a blob if an unprintable character is found. + if (bUnprint) + { + bUnprint = false; + szUserString = rUserString.Ptr(); + if (FAILED(hr = rcBuf.ReSizeNoThrow(81))) //(chBuf * 5 + 1); + Error("ReSize failed.", hr); + szBuf = rcBuf.Ptr(); + ULONG j,k; + WriteLine("\t\tUser string has unprintables, hex format below:"); + for (j = 0,k=0; j < chBuf; j++) + { + sprintf_s (&szBuf[k*5], 81, "%04x ", szUserString[j]); + k++; + if((k==16)||(j == (chBuf-1))) + { + szBuf[k*5] = '\0'; + VWriteLine("\t\t%s", szBuf); + k=0; + } + } + } + } + } + if (stringEnum) + m_pImport->CloseEnum(stringEnum); +} // void MDInfo::DisplayUserStrings() + +void MDInfo::DisplayUnsatInfo() +{ + HRESULT hr = S_OK; + + HCORENUM henum = 0; + mdToken tk; + ULONG cMethods; + + Write("\nUnresolved Externals\n"); + Write("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); + + while ( (hr = m_pImport->EnumUnresolvedMethods( + &henum, + &tk, + 1, + &cMethods)) == S_OK && cMethods ) + { + if ( TypeFromToken(tk) == mdtMethodDef ) + { + // a method definition without implementation + DisplayMethodInfo( tk ); + } + else if ( TypeFromToken(tk) == mdtMemberRef ) + { + // an unresolved MemberRef to a global function + DisplayMemberRefInfo( tk, "" ); + } + else + { + _ASSERTE(!"Unknown token kind!"); + } + } + m_pImport->CloseEnum(henum); +} // void MDInfo::DisplayUnsatInfo() + +//******************************************************************************* +// This code is used for debugging purposes only. This will just print out the +// entire database. +//******************************************************************************* +const char *MDInfo::DumpRawNameOfType(ULONG iType) +{ + if (iType <= iRidMax) + { + const char *pNameTable; + m_pTables->GetTableInfo(iType, 0,0,0,0, &pNameTable); + return pNameTable; + } + else + // Is the field a coded token? + if (iType <= iCodedTokenMax) + { + int iCdTkn = iType - iCodedToken; + const char *pNameCdTkn; + m_pTables->GetCodedTokenInfo(iCdTkn, 0,0, &pNameCdTkn); + return pNameCdTkn; + } + + // Fixed type. + switch (iType) + { + case iBYTE: + return "BYTE"; + case iSHORT: + return "short"; + case iUSHORT: + return "USHORT"; + case iLONG: + return "long"; + case iULONG: + return "ULONG"; + case iSTRING: + return "string"; + case iGUID: + return "GUID"; + case iBLOB: + return "blob"; + } + // default: + static char buf[30]; + sprintf_s(buf, 30, "unknown type 0x%02x", iType); + return buf; +} // const char *MDInfo::DumpRawNameOfType() + +void MDInfo::DumpRawCol(ULONG ixTbl, ULONG ixCol, ULONG rid, bool bStats) +{ + ULONG ulType; // Type of a column. + ULONG ulVal; // Value of a column. + LPCUTF8 pString; // Pointer to a string. + const void *pBlob; // Pointer to a blob. + ULONG cb; // Size of something. + + m_pTables->GetColumn(ixTbl, ixCol, rid, &ulVal); + m_pTables->GetColumnInfo(ixTbl, ixCol, 0, 0, &ulType, 0); + + if (ulType <= iRidMax) + { + const char *pNameTable; + m_pTables->GetTableInfo(ulType, 0,0,0,0, &pNameTable); + VWrite("%s[%x]", pNameTable, ulVal); + } + else + // Is the field a coded token? + if (ulType <= iCodedTokenMax) + { + int iCdTkn = ulType - iCodedToken; + const char *pNameCdTkn; + m_pTables->GetCodedTokenInfo(iCdTkn, 0,0, &pNameCdTkn); + VWrite("%s[%08x]", pNameCdTkn, ulVal); + } + else + { + // Fixed type. + switch (ulType) + { + case iBYTE: + VWrite("%02x", ulVal); + break; + case iSHORT: + case iUSHORT: + VWrite("%04x", ulVal); + break; + case iLONG: + case iULONG: + VWrite("%08x", ulVal); + break; + case iSTRING: + if (ulVal && (m_DumpFilter & dumpNames)) + { + m_pTables->GetString(ulVal, &pString); + VWrite("(%x)\"%s\"", ulVal, pString); + } + else + VWrite("string#%x", ulVal); + if (bStats && ulVal) + { + m_pTables->GetString(ulVal, &pString); + cb = (ULONG) strlen(pString) + 1; + VWrite("(%d)", cb); + } + break; + case iGUID: + VWrite("guid#%x", ulVal); + if (bStats && ulVal) + { + VWrite("(16)"); + } + break; + case iBLOB: + VWrite("blob#%x", ulVal); + if (bStats && ulVal) + { + m_pTables->GetBlob(ulVal, &cb, &pBlob); + cb += 1; + if (cb > 128) + cb += 1; + if (cb > 16535) + cb += 1; + VWrite("(%d)", cb); + } + break; + default: + VWrite("unknown type 0x%04x", ulVal); + break; + } + } +} // void MDInfo::DumpRawCol() + +ULONG MDInfo::DumpRawColStats(ULONG ixTbl, ULONG ixCol, ULONG cRows) +{ + ULONG rslt = 0; + ULONG ulType; // Type of a column. + ULONG ulVal; // Value of a column. + LPCUTF8 pString; // Pointer to a string. + const void *pBlob; // Pointer to a blob. + ULONG cb; // Size of something. + + m_pTables->GetColumnInfo(ixTbl, ixCol, 0, 0, &ulType, 0); + + if (IsHeapType(ulType)) + { + for (ULONG rid=1; rid<=cRows; ++rid) + { + m_pTables->GetColumn(ixTbl, ixCol, rid, &ulVal); + // Fixed type. + switch (ulType) + { + case iSTRING: + if (ulVal) + { + m_pTables->GetString(ulVal, &pString); + cb = (ULONG) strlen(pString); + rslt += cb + 1; + } + break; + case iGUID: + if (ulVal) + rslt += 16; + break; + case iBLOB: + if (ulVal) + { + m_pTables->GetBlob(ulVal, &cb, &pBlob); + rslt += cb + 1; + if (cb > 128) + rslt += 1; + if (cb > 16535) + rslt += 1; + } + break; + default: + break; + } + } + } + return rslt; +} // ULONG MDInfo::DumpRawColStats() + +int MDInfo::DumpHex( + const char *szPrefix, // String prefix for first line. + const void *pvData, // The data to print. + ULONG cbData, // Bytes of data to print. + int bText, // If true, also dump text. + ULONG nLine) // Bytes per line to print. +{ + const BYTE *pbData = static_cast<const BYTE*>(pvData); + ULONG i; // Loop control. + ULONG nPrint; // Number to print in an iteration. + ULONG nSpace; // Spacing calculations. + ULONG nPrefix; // Size of the prefix. + ULONG nLines=0; // Number of lines printed. + const char *pPrefix; // For counting spaces in the prefix. + + // Round down to 8 characters. + nLine = nLine & ~0x7; + + for (nPrefix=0, pPrefix=szPrefix; *pPrefix; ++pPrefix) + { + if (*pPrefix == '\t') + nPrefix = (nPrefix + 8) & ~7; + else + ++nPrefix; + } + //nPrefix = strlen(szPrefix); + do + { // Write the line prefix. + if (szPrefix) + VWrite("%s:", szPrefix); + else + VWrite("%*s:", nPrefix, ""); + szPrefix = 0; + ++nLines; + + // Calculate spacing. + nPrint = min(cbData, nLine); + nSpace = nLine - nPrint; + + // dump in hex. + for(i=0; i<nPrint; i++) + { + if ((i&7) == 0) + Write(" "); + VWrite("%02x ", pbData[i]); + } + if (bText) + { + // Space out to the text spot. + if (nSpace) + VWrite("%*s", nSpace*3+nSpace/8, ""); + // Dump in text. + Write(">"); + for(i=0; i<nPrint; i++) + VWrite("%c", (isprint(pbData[i])) ? pbData[i] : ' '); + // Space out the text, and finish the line. + VWrite("%*s<", nSpace, ""); + } + VWriteLine(""); + + // Next data to print. + cbData -= nPrint; + pbData += nPrint; + } + while (cbData > 0); + + return nLines; +} // int MDInfo::DumpHex() + +void MDInfo::DumpRawHeaps() +{ + HRESULT hr; // A result. + ULONG ulSize; // Bytes in a heap. + const BYTE *pData; // Pointer to a blob. + ULONG cbData; // Size of a blob. + ULONG oData; // Offset of current blob. + char rcPrefix[30]; // To format line prefix. + + m_pTables->GetBlobHeapSize(&ulSize); + VWriteLine(""); + VWriteLine("Blob Heap: %d(%#x) bytes", ulSize,ulSize); + oData = 0; + do + { + m_pTables->GetBlob(oData, &cbData, (const void**)&pData); + sprintf_s(rcPrefix, 30, "%5x,%-2x", oData, cbData); + DumpHex(rcPrefix, pData, cbData); + hr = m_pTables->GetNextBlob(oData, &oData); + } + while (hr == S_OK); + + m_pTables->GetStringHeapSize(&ulSize); + VWriteLine(""); + VWriteLine("String Heap: %d(%#x) bytes", ulSize,ulSize); + oData = 0; + const char *pString; + do + { + m_pTables->GetString(oData, &pString); + if (m_DumpFilter & dumpMoreHex) + { + sprintf_s(rcPrefix, 30, "%08x", oData); + DumpHex(rcPrefix, pString, (ULONG)strlen(pString)+1); + } + else + if (*pString != 0) + VWrite("%08x: %s\n", oData, pString); + hr = m_pTables->GetNextString(oData, &oData); + } + while (hr == S_OK); + VWriteLine(""); + + DisplayUserStrings(); + +} // void MDInfo::DumpRawHeaps() + + +void MDInfo::DumpRaw(int iDump, bool bunused) +{ + ULONG cTables; // Tables in the database. + ULONG cCols; // Columns in a table. + ULONG cRows; // Rows in a table. + ULONG cbRow; // Bytes in a row of a table. + ULONG iKey; // Key column of a table. + const char *pNameTable; // Name of a table. + ULONG oCol; // Offset of a column. + ULONG cbCol; // Size of a column. + ULONG ulType; // Type of a column. + const char *pNameColumn; // Name of a column. + ULONG ulSize; + + // Heaps is easy -- there is a specific bit for that. + bool bStats = (m_DumpFilter & dumpStats) != 0; + // Rows are harder. Was there something else that limited data? + BOOL bRows = (m_DumpFilter & (dumpSchema | dumpHeader)) == 0; + BOOL bSchema = bRows || (m_DumpFilter & dumpSchema); + // (m_DumpFilter & (dumpSchema | dumpHeader | dumpCSV | dumpRaw | dumpStats | dumpRawHeaps)) + + if (m_pTables2) + { + // Get the raw metadata header. + const BYTE *pbData = NULL; + const BYTE *pbStream = NULL; // One of the stream.s + const BYTE *pbMd = NULL; // The metadata stream. + ULONG cbData = 0; + ULONG cbStream = 0; // One of the streams. + ULONG cbMd = 0; // The metadata stream. + const char *pName; + HRESULT hr = S_OK; + ULONG ix; + + m_pTables2->GetMetaDataStorage((const void**)&pbData, &cbData); + + // Per the ECMA spec, the section data looks like this: + struct MDSTORAGESIGNATURE + { + ULONG lSignature; // "Magic" signature. + USHORT iMajorVer; // Major file version. + USHORT iMinorVer; // Minor file version. + ULONG iExtraData; // Offset to next structure of information + ULONG iVersionString; // Length of version string + BYTE pVersion[0]; // Version string + }; + struct MDSTORAGEHEADER + { + BYTE fFlags; // STGHDR_xxx flags. + BYTE pad; + USHORT iStreams; // How many streams are there. + }; + const MDSTORAGESIGNATURE *pStorage = (const MDSTORAGESIGNATURE *) pbData; + const MDSTORAGEHEADER *pSHeader = (const MDSTORAGEHEADER *)(pbData + sizeof(MDSTORAGESIGNATURE) + pStorage->iVersionString); + + VWriteLine("Metadata section: 0x%08x, version: %d.%d, extra: %d, version len: %d, version: %s", pStorage->lSignature, pStorage->iMajorVer, pStorage->iMinorVer, pStorage->iExtraData, pStorage->iVersionString, pStorage->pVersion); + VWriteLine(" flags: 0x%02x, streams: %d", pSHeader->fFlags, pSHeader->iStreams); + if (m_DumpFilter & dumpMoreHex) + { + const BYTE *pbEnd = pbData; + ULONG cb = sizeof(MDSTORAGESIGNATURE) + pStorage->iVersionString + sizeof(MDSTORAGEHEADER); + hr = m_pTables2->GetMetaDataStreamInfo(0, &pName, (const void**)&pbEnd, &cbStream); + if (hr == S_OK) + cb = (ULONG)(pbEnd - pbData); + DumpHex(" ", pbData, cb); + } + + for (ix=0; hr == S_OK; ++ix) + { + hr = m_pTables2->GetMetaDataStreamInfo(ix, &pName, (const void**)&pbStream, &cbStream); + if (hr != S_OK) + break; + if (strcmp(pName, "#~") == 0 || strcmp(pName, "#-") == 0) + { + pbMd = pbStream; + cbMd = cbStream; + } + + VWriteLine("Stream %d: name: %s, size %d", ix, pName, cbStream); + // hex for individual stream headers in metadata section dump. hex for + // the streams themselves distributed throughout the dump. + } + + if (pbMd) + { + // Per ECMA, the metadata header looks like this: + struct MD + { + ULONG m_ulReserved; // Reserved, must be zero. + BYTE m_major; // Version numbers. + BYTE m_minor; + BYTE m_heaps; // Bits for heap sizes. + BYTE m_rid; // log-base-2 of largest rid. + unsigned __int64 m_maskvalid; // Bit mask of present table counts. + unsigned __int64 m_sorted; // Bit mask of sorted tables. }; + }; + + const MD *pMd; + pMd = (const MD *)pbMd; + + VWriteLine("Metadata header: %d.%d, heaps: 0x%02x, rid: 0x%02x, valid: 0x%016I64x, sorted: 0x%016I64x", + pMd->m_major, pMd->m_minor, pMd->m_heaps, pMd->m_rid, + (ULONGLONG)GET_UNALIGNED_VAL64(&(pMd->m_maskvalid)), + (ULONGLONG)GET_UNALIGNED_VAL64(&(pMd->m_sorted))); + + if (m_DumpFilter & dumpMoreHex) + { + DumpHex(" ", pbMd, sizeof(MD)); + } + } + VWriteLine(""); + } + + m_pTables->GetNumTables(&cTables); + + m_pTables->GetStringHeapSize(&ulSize); + VWrite("Strings: %d(%#x)", ulSize, ulSize); + m_pTables->GetBlobHeapSize(&ulSize); + VWrite(", Blobs: %d(%#x)", ulSize, ulSize); + m_pTables->GetGuidHeapSize(&ulSize); + VWrite(", Guids: %d(%#x)", ulSize, ulSize); + m_pTables->GetUserStringHeapSize(&ulSize); + VWriteLine(", User strings: %d(%#x)", ulSize, ulSize); + + for (ULONG ixTbl = 0; ixTbl < cTables; ++ixTbl) + { + m_pTables->GetTableInfo(ixTbl, &cbRow, &cRows, &cCols, &iKey, &pNameTable); + + if (bRows) // when dumping rows, print a break between row data and schema + VWriteLine("================================================="); + VWriteLine("%2d(%#x): %-20s cRecs:%5d(%#x), cbRec:%3d(%#x), cbTable:%6d(%#x)", + ixTbl, ixTbl, pNameTable, cRows, cRows, cbRow, cbRow, cbRow * cRows, cbRow * cRows); + + if (!bSchema && !bRows) + continue; + + // Dump column definitions for the table. + ULONG ixCol; + for (ixCol=0; ixCol<cCols; ++ixCol) + { + m_pTables->GetColumnInfo(ixTbl, ixCol, &oCol, &cbCol, &ulType, &pNameColumn); + + VWrite(" col %2x:%c %-12s oCol:%2x, cbCol:%x, %-7s", + ixCol, ((ixCol==iKey)?'*':' '), pNameColumn, oCol, cbCol, DumpRawNameOfType(ulType)); + + if (bStats) + { + ulSize = DumpRawColStats(ixTbl, ixCol, cRows); + if (ulSize) + VWrite("(%d)", ulSize); + } + VWriteLine(""); + } + + if (!bRows) + continue; + + // Dump the rows. + for (ULONG rid = 1; rid <= cRows; ++rid) + { + if (rid == 1) + VWriteLine("-------------------------------------------------"); + VWrite(" %3x == ", rid); + for (ixCol=0; ixCol < cCols; ++ixCol) + { + if (ixCol) VWrite(", "); + VWrite("%d:", ixCol); + DumpRawCol(ixTbl, ixCol, rid, bStats); + } + VWriteLine(""); + } + } +} // void MDInfo::DumpRaw() + +void MDInfo::DumpRawCSV() +{ + ULONG cTables; // Tables in the database. + ULONG cCols; // Columns in a table. + ULONG cRows; // Rows in a table. + ULONG cbRow; // Bytes in a row of a table. + const char *pNameTable; // Name of a table. + ULONG ulSize; + + m_pTables->GetNumTables(&cTables); + + VWriteLine("Name,Size,cRecs,cbRec"); + + m_pTables->GetStringHeapSize(&ulSize); + VWriteLine("Strings,%d", ulSize); + + m_pTables->GetBlobHeapSize(&ulSize); + VWriteLine("Blobs,%d", ulSize); + + m_pTables->GetGuidHeapSize(&ulSize); + VWriteLine("Guids,%d", ulSize); + + for (ULONG ixTbl = 0; ixTbl < cTables; ++ixTbl) + { + m_pTables->GetTableInfo(ixTbl, &cbRow, &cRows, &cCols, NULL, &pNameTable); + VWriteLine("%s,%d,%d,%d", pNameTable, cbRow*cRows, cRows, cbRow); + } + +} // void MDInfo::DumpRawCSV() + diff --git a/src/tools/metainfo/mdinfo.h b/src/tools/metainfo/mdinfo.h new file mode 100644 index 0000000000..06c0a76b63 --- /dev/null +++ b/src/tools/metainfo/mdinfo.h @@ -0,0 +1,207 @@ +// 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. + +#ifndef _mdinfo_h +#define _mdinfo_h + +#include "winwrap.h" +#include "cor.h" +#include "corhlprpriv.h" + +#ifdef FEATURE_PAL +#include <oleauto.h> +#endif + +#define STRING_BUFFER_LEN 4096 + +typedef void (*strPassBackFn)(const char *str); + +class MDInfo { +public: + enum DUMP_FILTER + { + dumpDefault = 0x00000000, // Dump everything but debugger data. + dumpSchema = 0x00000002, // Dump the metadata schema. + dumpRaw = 0x00000004, // Dump the metadata in raw table format. + dumpHeader = 0x00000008, // Dump just the metadata header info. + dumpCSV = 0x00000010, // Dump the metadata header info in CSV format. + dumpUnsat = 0x00000020, // Dump unresolved methods or memberref + dumpAssem = 0x00000040, + dumpStats = 0x00000080, // Dump more statistics about tables. + dumpMoreHex = 0x00000100, // Dump more things in hex. + dumpValidate = 0x00000200, // Validate MetaData. + dumpRawHeaps = 0x00000400, // Also dump the heaps in the raw dump. + dumpNoLogo = 0x00000800, // Don't display the logo or MVID + dumpNames = 0x00001000, // In a hex dump, display the names, as well as string #'s. + }; + + +public: + MDInfo(IMetaDataImport2* pImport, IMetaDataAssemblyImport* pAssemblyImport, LPCWSTR szScope, strPassBackFn inPBFn, ULONG DumpFilter); + MDInfo(IMetaDataDispenserEx *pDispenser, LPCWSTR szScope, strPassBackFn inPBFn, ULONG DumpFilter=dumpDefault); + MDInfo(IMetaDataDispenserEx *pDispenser, PBYTE pManifest, DWORD dwSize, strPassBackFn inPBFn, ULONG DumpFilter=dumpDefault); + ~MDInfo(); + + void DisplayMD(void); + +#ifdef FEATURE_COMINTEROP + LPCWSTR VariantAsString(VARIANT *pVariant); +#endif + + void DisplayVersionInfo(void); + + void DisplayScopeInfo(void); + + void DisplayGlobalFunctions(void); + void DisplayGlobalFields(void); + void DisplayFieldRVA(mdFieldDef field); + void DisplayGlobalMemberRefs(void); + + void DisplayTypeDefs(void); + void DisplayTypeDefInfo(mdTypeDef inTypeDef); + void DisplayTypeDefProps(mdTypeDef inTypeDef); + + void DisplayModuleRefs(void); + void DisplayModuleRefInfo(mdModuleRef inModuleRef); + + void DisplaySignatures(void); + void DisplaySignatureInfo(mdSignature inSignature); + + LPCWSTR TokenName(mdToken inToken, __out_ecount(bufLen) LPWSTR buffer, ULONG bufLen); + + LPCWSTR TypeDeforRefName(mdToken inToken, __out_ecount(bufLen) LPWSTR buffer, ULONG bufLen); + LPCWSTR TypeDefName(mdTypeDef inTypeDef, __out_ecount(bufLen) LPWSTR buffer, ULONG bufLen); + LPCWSTR TypeRefName(mdTypeRef tr, __out_ecount(bufLen) LPWSTR buffer, ULONG bufLen); + + LPCWSTR MemberDeforRefName(mdToken inToken, __out_ecount(bufLen) LPWSTR buffer, ULONG bufLen); + LPCWSTR MemberRefName(mdToken inMemRef, __out_ecount(bufLen) LPWSTR buffer, ULONG bufLen); + LPCWSTR MemberName(mdToken inMember, __out_ecount(bufLen) LPWSTR buffer, ULONG bufLen); + + LPCWSTR MethodName(mdMethodDef inToken, __out_ecount(bufLen) LPWSTR buffer, ULONG bufLen); + LPCWSTR FieldName(mdFieldDef inToken, __out_ecount(bufLen) LPWSTR buffer, ULONG bufLen); + + char *ClassFlags(DWORD flags, __out_ecount(STRING_BUFFER_LEN) char *sFlags); + + void DisplayTypeRefs(void); + void DisplayTypeRefInfo(mdTypeRef tr); + void DisplayTypeSpecs(void); + void DisplayTypeSpecInfo(mdTypeSpec ts, const char *preFix); + void DisplayMethodSpecs(void); + void DisplayMethodSpecInfo(mdMethodSpec ms, const char *preFix); + + void DisplayCorNativeLink(COR_NATIVE_LINK *pCorNLnk, const char *preFix); + void DisplayCustomAttributeInfo(mdCustomAttribute inValue, const char *preFix); + void DisplayCustomAttributes(mdToken inToken, const char *preFix); + + void DisplayInterfaceImpls(mdTypeDef inTypeDef); + void DisplayInterfaceImplInfo(mdInterfaceImpl inImpl); + + LPWSTR GUIDAsString(GUID inGuid, __out_ecount(bufLen) LPWSTR guidString, ULONG bufLen); + + const char *TokenTypeName(mdToken inToken); + + void DisplayMemberInfo(mdToken inMember); + void DisplayMethodInfo(mdMethodDef method, DWORD *pflags = 0); + void DisplayFieldInfo(mdFieldDef field, DWORD *pflags = 0); + + void DisplayMethods(mdTypeDef inTypeDef); + void DisplayFields(mdTypeDef inTypeDef, COR_FIELD_OFFSET *rFieldOffset, ULONG cFieldOffset); + + void DisplaySignature(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, const char *preFix); + HRESULT GetOneElementType(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, ULONG *pcb); + + void DisplayMemberRefs(mdToken tkParent, const char *preFix); + void DisplayMemberRefInfo(mdMemberRef inMemRef, const char *preFix); + + void DisplayMethodImpls(mdTypeDef inTypeDef); + + void DisplayParams(mdMethodDef inMthDef); + void DisplayParamInfo(mdParamDef inParam); + + void DisplayGenericParams(mdToken tk, const char *prefix); + void DisplayGenericParamInfo(mdGenericParam tkparam, const char *prefix); + + void DisplayPropertyInfo(mdProperty inProp); + void DisplayProperties(mdTypeDef inTypeDef); + + void DisplayEventInfo(mdEvent inEvent); + void DisplayEvents(mdTypeDef inTypeDef); + + void DisplayPermissions(mdToken tk, const char *preFix); + void DisplayPermissionInfo(mdPermission inPermission, const char *preFix); + + void DisplayFieldMarshal(mdToken inToken); + + void DisplayPinvokeInfo(mdToken inToken); + + void DisplayAssembly(); + + void DisplayAssemblyInfo(); + + void DisplayAssemblyRefs(); + void DisplayAssemblyRefInfo(mdAssemblyRef inAssemblyRef); + + void DisplayFiles(); + void DisplayFileInfo(mdFile inFile); + + void DisplayExportedTypes(); + void DisplayExportedTypeInfo(mdExportedType inExportedType); + + void DisplayManifestResources(); + void DisplayManifestResourceInfo(mdManifestResource inManifestResource); + + void DisplayASSEMBLYMETADATA(ASSEMBLYMETADATA *pMetaData); + + void DisplayUserStrings(); + + void DisplayUnsatInfo(); + + void DisplayRaw(); + void DumpRawHeaps(); + void DumpRaw(int iDump=1, bool bStats=false); + void DumpRawCSV(); + void DumpRawCol(ULONG ixTbl, ULONG ixCol, ULONG rid, bool bStats); + ULONG DumpRawColStats(ULONG ixTbl, ULONG ixCol, ULONG cRows); + const char *DumpRawNameOfType(ULONG ulType); + void SetVEHandlerReporter(__int64 VEHandlerReporterPtr) { m_VEHandlerReporterPtr = VEHandlerReporterPtr; }; + + static void Error(const char *szError, HRESULT hr = S_OK); +private: + void Init(strPassBackFn inPBFn, DUMP_FILTER DumpFilter); // Common initialization code. + + int DumpHex(const char *szPrefix, const void *pvData, ULONG cbData, int bText=true, ULONG nLine=16); + + int Write(__in_z __in const char *str); + int WriteLine(__in_z __in const char *str); + + int VWrite(__in_z __in const char *str, ...); + int VWriteLine(__in_z __in const char *str, ...); + int VWriteMarker(__in_z __in const char *str, va_list marker); + + void InitSigBuffer(); + HRESULT AddToSigBuffer(__in_z __in const char *string); + + IMetaDataImport2 *m_pRegImport; + IMetaDataImport2 *m_pImport; + IMetaDataAssemblyImport *m_pAssemblyImport; + strPassBackFn m_pbFn; + __int64 m_VEHandlerReporterPtr; + IMetaDataTables *m_pTables; + IMetaDataTables2 *m_pTables2; + + CQuickBytes m_output; + DUMP_FILTER m_DumpFilter; + + // temporary buffer for TypeDef or TypeRef name. Consume immediately + // because other functions may overwrite it. + WCHAR m_szTempBuf[STRING_BUFFER_LEN]; + + // temporary buffer for formatted string. Consume immediately before any function calls. + char m_tempFormatBuffer[STRING_BUFFER_LEN]; + + // Signature buffer. + CQuickBytes m_sigBuf; +}; + +#endif diff --git a/src/tools/metainfo/mdobj.cpp b/src/tools/metainfo/mdobj.cpp new file mode 100644 index 0000000000..192281f9b9 --- /dev/null +++ b/src/tools/metainfo/mdobj.cpp @@ -0,0 +1,298 @@ +// 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. + +#include <stdio.h> +#include <ctype.h> +#include <crtdbg.h> +#include "mdinfo.h" + +#ifndef STRING_BUFFER_LEN +#define STRING_BUFFER_LEN 4096 +#endif + +#define OBJ_EXT ".obj" +#define OBJ_EXT_W W(".obj") +#define OBJ_EXT_LEN 4 +#define LIB_EXT ".lib" +#define LIB_EXT_W W(".lib") +#define LIB_EXT_LEN 4 + +extern IMetaDataDispenserEx *g_pDisp; +extern DWORD g_ValModuleType; + +// This function is copied from peparse.c file. Making this static, so we won't end up with +// duplicate definitions causing confusion. +static const char g_szCORMETA[] = ".cormeta"; +static HRESULT FindObjMetaData(PVOID pImage, PVOID *ppMetaData, long *pcbMetaData) +{ + IMAGE_FILE_HEADER *pImageHdr; // Header for the .obj file. + IMAGE_SECTION_HEADER *pSectionHdr; // Section header. + WORD i; // Loop control. + + // Get a pointer to the header and the first section. + pImageHdr = (IMAGE_FILE_HEADER *) pImage; + pSectionHdr = (IMAGE_SECTION_HEADER *)(pImageHdr + 1); + + // Avoid confusion. + *ppMetaData = NULL; + *pcbMetaData = 0; + + // Walk each section looking for .cormeta. + for (i=0; i<VAL16(pImageHdr->NumberOfSections); i++, pSectionHdr++) + { + // Simple comparison to section name. + if (strcmp((const char *) pSectionHdr->Name, g_szCORMETA) == 0) + { + *pcbMetaData = VAL32(pSectionHdr->SizeOfRawData); + *ppMetaData = (void *) ((UINT_PTR)pImage + VAL32(pSectionHdr->PointerToRawData)); + break; + } + } + + // Check for errors. + if (*ppMetaData == NULL || *pcbMetaData == 0) + return (E_FAIL); + return (S_OK); +} + + +// This function returns the address to the MapView of file and file size. +void GetMapViewOfFile(__in wchar_t *szFile, PBYTE *ppbMap, DWORD *pdwFileSize) +{ + HANDLE hMapFile; + DWORD dwHighSize; + + HANDLE hFile = WszCreateFile(szFile, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + if (hFile == INVALID_HANDLE_VALUE) + MDInfo::Error("CreateFileA failed!"); + + *pdwFileSize = GetFileSize(hFile, &dwHighSize); + + if ((*pdwFileSize == 0xFFFFFFFF) && (GetLastError() != NO_ERROR)) + { + CloseHandle(hFile); + MDInfo::Error("GetFileSize failed!"); + } + _ASSERTE(dwHighSize == 0); + + hMapFile = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + CloseHandle(hFile); + if (!hMapFile) + MDInfo::Error("CreateFileMappingA failed!"); + + *ppbMap = (PBYTE) MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0); + CloseHandle(hMapFile); + + if (!*ppbMap) + MDInfo::Error("MapViewOfFile failed!"); +} // void GetMapViewOfFile() + +// This function skips a member given the pointer to the member header +// and returns a pointer to the next header. +PBYTE SkipMember(PBYTE pbMapAddress) +{ + PIMAGE_ARCHIVE_MEMBER_HEADER pMemHdr; + ULONG ulMemSize; + int j; + + pMemHdr = (PIMAGE_ARCHIVE_MEMBER_HEADER)pbMapAddress; + + // Get size of the member. + ulMemSize = 0; + for (j = 0; j < 10; j++) + { + if (pMemHdr->Size[j] < '0' || pMemHdr->Size[j] > '9') + break; + else + ulMemSize = ulMemSize * 10 + pMemHdr->Size[j] - '0'; + } + + // Skip past the header. + pbMapAddress += IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR + ulMemSize; + // Find the next even address if the current one is not even. + if ((ULONG_PTR)pbMapAddress % 2) + pbMapAddress++; + + return pbMapAddress; +} // void SkipMember() + +// This function returns the name of the given Obj. If the name fits in the header, +// szBuf will be filled in and returned from the function. Else an offset into the long +// names section will be returned. +char *GetNameOfObj(PBYTE pbLongNames, PIMAGE_ARCHIVE_MEMBER_HEADER pMemHdr, char szBuf[17]) +{ + if (pMemHdr->Name[0] == '/') + { + ULONG ulOffset = 0; + + // Long Names section must exist if the .obj file name starts with '/'. + _ASSERTE(pbLongNames && + "Corrupt archive file - .obj file name in the header starts with " + "'/' but no long names section present in the archive file."); + + // Calculate the offset into the long names section. + for (int j = 1; j < 16; j++) + { + if (pMemHdr->Name[j] < '0' || pMemHdr->Name[j] > '9') + break; + else + ulOffset = ulOffset * 10 + pMemHdr->Name[j] - '0'; + } + return (char *)(pbLongNames + ulOffset); + } + else + { + int j; + for (j = 0; j < 16; j++) + if ((szBuf[j] = pMemHdr->Name[j]) == '/') + break; + szBuf[j] = '\0'; + return szBuf; + } +} // char *GetNameOfObj() + +// DisplayArchive() function +// +// Opens the .LIB file, and displays the metadata in the specified object files. + +void DisplayArchive(__in_z __in wchar_t* szFile, ULONG DumpFilter, __in_z __in_opt wchar_t* szObjName, strPassBackFn pDisplayString) +{ + PBYTE pbMapAddress; + PBYTE pbStartAddress; + PBYTE pbLongNameAddress; + PIMAGE_ARCHIVE_MEMBER_HEADER pMemHdr; + DWORD dwFileSize; + PVOID pvMetaData; + char *szName; + wchar_t wzName[1024]; + char szBuf[17]; + long cbMetaData; + int i; + HRESULT hr; + char szString[1024]; + + GetMapViewOfFile(szFile, &pbMapAddress, &dwFileSize); + pbStartAddress = pbMapAddress; + + // Verify and skip archive signature. + if (dwFileSize < IMAGE_ARCHIVE_START_SIZE || + strncmp((char *)pbMapAddress, IMAGE_ARCHIVE_START, IMAGE_ARCHIVE_START_SIZE)) + { + MDInfo::Error("Bad file format - archive signature mis-match!"); + } + pbMapAddress += IMAGE_ARCHIVE_START_SIZE; + + // Skip linker member 1, linker member 2. + for (i = 0; i < 2; i++) + pbMapAddress = SkipMember(pbMapAddress); + + // Save address of the long name member and skip it if there exists one. + pMemHdr = (PIMAGE_ARCHIVE_MEMBER_HEADER)pbMapAddress; + if (pMemHdr->Name[0] == '/' && pMemHdr->Name[1] == '/') + { + pbLongNameAddress = pbMapAddress + IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR; + pbMapAddress = SkipMember(pbMapAddress); + } + else + pbLongNameAddress = 0; + + pDisplayString ("\n"); + // Get the MetaData for each object file and display it. + while (DWORD(pbMapAddress - pbStartAddress) < dwFileSize) + { + if((szName = GetNameOfObj(pbLongNameAddress, (PIMAGE_ARCHIVE_MEMBER_HEADER)pbMapAddress, szBuf))!=NULL) + { + if (Wsz_mbstowcs(wzName, szName, 1024) == -1) + MDInfo::Error("Conversion from Multi-Byte to Wide-Char failed."); + + // Display metadata only for object files. + // If szObjName is specified, display metadata only for that one object file. + if (!_stricmp(&szName[strlen(szName) - OBJ_EXT_LEN], OBJ_EXT) && + (!szObjName || !_wcsicmp(szObjName, wzName))) + { + // Try to find the MetaData section in the current object file. + hr = FindObjMetaData(pbMapAddress+IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR, &pvMetaData, &cbMetaData); + if (SUCCEEDED(hr)) + { + sprintf_s (szString,1024,"MetaData for object file %s:\n", szName); + pDisplayString(szString); + MDInfo archiveInfo(g_pDisp, + (PBYTE)pvMetaData, + cbMetaData, + pDisplayString, + DumpFilter); + archiveInfo.DisplayMD(); + } + else + { + sprintf_s(szString,1024,"MetaData not found for object file %s!\n\n", szName); + pDisplayString(szString); + } + } + } + + // Skip past the object file. + pbMapAddress = SkipMember(pbMapAddress); + } + + UnmapViewOfFile(pbStartAddress); +} // void DisplayArchive() + +// DisplayFile() function +// +// Opens the meta data content of a .EXE, .CLB, .CLASS, .TLB, .DLL or .LIB file, and +// calls RawDisplay() + +void DisplayFile(__in_z __in wchar_t* szFile, BOOL isFile, ULONG DumpFilter, __in_z __in_opt wchar_t* szObjName, strPassBackFn pDisplayString) +{ + // Open the emit scope + + // We need to make sure this file isn't too long. Checking _MAX_PATH is probably safe, but since we have a much + // larger buffer, we might as well use it all. + if (wcslen(szFile) > 1000) + return; + + + WCHAR szScope[1024]; + char szString[1024]; + + if (isFile) + { + wcscpy_s(szScope, 1024, W("file:")); + wcscat_s(szScope, 1024, szFile); + } + else + wcscpy_s(szScope, 1024, szFile); + + // print bar that separates different files + pDisplayString("////////////////////////////////////////////////////////////////\n"); + wchar_t rcFname[_MAX_FNAME], rcExt[_MAX_EXT]; + + _wsplitpath_s(szFile, NULL, 0, NULL, 0, rcFname, _MAX_FNAME, rcExt, _MAX_EXT); + sprintf_s(szString,1024,"\nFile %S%S: \n",rcFname, rcExt); + pDisplayString(szString); + + if (DumpFilter & MDInfo::dumpValidate) + { + if (!_wcsicmp(rcExt, OBJ_EXT_W) || !_wcsicmp(rcExt, LIB_EXT_W)) + g_ValModuleType = ValidatorModuleTypeObj; + else + g_ValModuleType = ValidatorModuleTypePE; + } + + if (!_wcsicmp(rcExt, LIB_EXT_W)) + DisplayArchive(szFile, DumpFilter, szObjName, pDisplayString); + else + { + MDInfo metaDataInfo(g_pDisp, szScope, pDisplayString, DumpFilter); + metaDataInfo.DisplayMD(); + } +} // void DisplayFile() + diff --git a/src/tools/metainfo/metainfo.cpp b/src/tools/metainfo/metainfo.cpp new file mode 100644 index 0000000000..4e1f1cedc4 --- /dev/null +++ b/src/tools/metainfo/metainfo.cpp @@ -0,0 +1,190 @@ +// 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. + +#include <stdio.h> +#include <ctype.h> +#include <crtdbg.h> +#include <utilcode.h> +#include "mdinfo.h" +#include <ndpversion.h> + +// Provide custom LoadLibrary implementation. +#define LEGACY_ACTIVATION_SHIM_LOAD_LIBRARY WszLoadLibrary +#define LEGACY_ACTIVATION_SHIM_DEFINE_CoInitializeEE +#include "LegacyActivationShim.h" + +#ifdef FEATURE_PAL +#include <palstartupw.h> +#endif + +// Global variables +bool g_bSchema = false; +bool g_bRaw = false; +bool g_bDebug = false; +bool g_bHeader = false; + +// Validator module type. +DWORD g_ValModuleType = ValidatorModuleTypeInvalid; + +IMetaDataImport2 *g_pImport = NULL; +IMetaDataDispenserEx *g_pDisp = NULL; + +void DisplayFile(__in_z __in wchar_t* szFile, BOOL isFile, ULONG DumpFilter, __in_z __in_opt wchar_t* szObjFile, strPassBackFn pDisplayString); +void DisplayArchive(__in_z __in wchar_t* szFile, ULONG DumpFilter, __in_z __in_opt wchar_t* szObjName, strPassBackFn pDisplayString); + +void PrintLogo() +{ + printf("Microsoft (R) .Net Frameworks Runtime Meta Data Dump Utility Version %s\n", VER_FILEVERSION_STR); + printf("%S", VER_LEGALCOPYRIGHT_LOGO_STR_L); + printf("\n"); +}// PrintLogo + +void Usage() +{ + printf("\n"); + printf("metainfo [-? | -header | -schema | -raw | -validate] [-nologo] [-obj <obj file name>] [<filname> | <file pattern>]\n"); + printf(" -? Displays this text.\n"); + printf(" -hex Prints more things in hex as well as words.\n"); + printf(" -header Prints MetaData header information and sizes.\n"); + printf(" -csv Prints the header sizes in Comma Separated format.\n"); + printf(" -unsat Prints unresolved externals.\n"); + printf(" -assem Prints only the Assembly information.\n"); + printf(" -schema Prints the MetaData schema information.\n"); + printf(" -raw Prints the raw MetaData tables.\n"); + printf(" -heaps Prints the raw heaps (only if -raw).\n"); + printf(" -names Prints string columns (only if -raw).\n"); + printf(" -validate Validate the consistency of the metadata.\n"); + printf(" -nologo Do not display the logo and MVID.\n"); + printf(" -obj <objFileName>\n"); + printf(" Prints the MetaData for the specified obj file in the given \n"); + printf(" archive(.lib) - e.g metainfo libc.lib -obj wMSILWinCRTStartup.obj\n"); + + MDInfo::Error(""); +} + +void DisplayString(__in_z __in const char *str) +{ + printf("%s", str); +} + +extern "C" int _cdecl wmain(int argc, __in_ecount(argc) WCHAR **argv) +{ + wchar_t *pArg = NULL; + wchar_t *szObjName = NULL; + ULONG DumpFilter = MDInfo::dumpDefault; + HRESULT hr = 0; + BOOL fWantHelp=FALSE; + + // Validate incoming arguments + for (int i=1; i<argc; i++) + { + const wchar_t *szArg = argv[i]; + if (*szArg == L'-' || *szArg == L'/') + { + if (_wcsicmp(szArg + 1, L"?") == 0) + fWantHelp=TRUE; + + else if (_wcsicmp(szArg + 1, L"nologo") == 0) + DumpFilter |= MDInfo::dumpNoLogo; + + else if (_wcsicmp(szArg + 1, L"Hex") == 0) + DumpFilter |= MDInfo::dumpMoreHex; + + else if (_wcsicmp(szArg + 1, L"header") == 0) + DumpFilter |= MDInfo::dumpHeader; + + else if (_wcsicmp(szArg + 1, L"csv") == 0) + DumpFilter |= MDInfo::dumpCSV; + + else if (_wcsicmp(szArg + 1, L"raw") == 0) + DumpFilter |= MDInfo::dumpRaw; + + else if (_wcsicmp(szArg + 1, L"heaps") == 0) + DumpFilter |= MDInfo::dumpRawHeaps; + + else if (_wcsicmp(szArg + 1, L"names") == 0) + DumpFilter |= MDInfo::dumpNames; + + else if (_wcsicmp(szArg + 1, L"schema") == 0) + DumpFilter |= MDInfo::dumpSchema; + + else if (_wcsicmp(szArg + 1, L"unsat") == 0) + DumpFilter |= MDInfo::dumpUnsat; + + else if (_wcsicmp(szArg + 1, L"stats") == 0) + DumpFilter |= MDInfo::dumpStats; + + else if (_wcsicmp(szArg + 1, L"assem") == 0) + DumpFilter |= MDInfo::dumpAssem; + + else if (_wcsicmp(szArg + 1, L"validate") == 0) + DumpFilter |= MDInfo::dumpValidate; + + else if (_wcsicmp(szArg + 1, L"obj") == 0) + { + if (++i == argc) + Usage(); + else + szObjName = argv[i]; + } + } + else + pArg = argv[i]; + } + + // Print banner. + if (!(DumpFilter & MDInfo::dumpNoLogo)) + PrintLogo(); + + + if (!pArg || fWantHelp) + Usage(); + + +#ifndef FEATURE_PAL + // Init and run. + CoInitialize(0); +#endif + + LegacyActivationShim::CoInitializeCor(0); + + hr = LegacyActivationShim::ClrCoCreateInstance( + CLSID_CorMetaDataDispenser, NULL, CLSCTX_INPROC_SERVER, + IID_IMetaDataDispenserEx, (void **) &g_pDisp); + if(FAILED(hr)) MDInfo::Error("Unable to CoCreate Meta-data Dispenser", hr); + + // Loop through all files in the file pattern passed + WIN32_FIND_DATA fdFiles; + HANDLE hFind; + wchar_t szSpec[_MAX_PATH]; + wchar_t szDrive[_MAX_DRIVE]; + wchar_t szDir[_MAX_DIR]; + + hFind = WszFindFirstFile(pArg, &fdFiles); + + if (hFind == INVALID_HANDLE_VALUE) + { + DisplayFile(pArg, false, DumpFilter, szObjName, DisplayString); + } + else + { + // Convert relative paths to full paths. + LPWSTR szFname; + WszGetFullPathName(pArg, _MAX_PATH, szSpec, &szFname); + SplitPath(szSpec, szDrive, _MAX_DRIVE, szDir, _MAX_DIR, NULL, 0, NULL, 0); + do + { + MakePath(szSpec, szDrive, szDir, fdFiles.cFileName, NULL); + // display the meta data of the file + DisplayFile(szSpec, true, DumpFilter, szObjName, DisplayString); + } while (WszFindNextFile(hFind, &fdFiles)) ; + FindClose(hFind); + } + g_pDisp->Release(); + LegacyActivationShim::CoUninitializeCor(); +#ifndef FEATURE_PAL + CoUninitialize(); +#endif + return 0; +} diff --git a/src/tools/metainfo/metainfo.nativeproj b/src/tools/metainfo/metainfo.nativeproj new file mode 100644 index 0000000000..7591a40d63 --- /dev/null +++ b/src/tools/metainfo/metainfo.nativeproj @@ -0,0 +1,42 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + <!--Leaf project Properties--> + <PropertyGroup> + <OutputName>metainfo</OutputName> + <FileToMarkForSigning>$(BinariesDirectory)\metainfo.exe</FileToMarkForSigning> + <TargetType>PROGRAM</TargetType> + <LinkSubsystem>console</LinkSubsystem> + <EntryPoint>wmain</EntryPoint> + <ClAdditionalOptions>$(ClAdditionalOptions) -DUNICODE -D_UNICODE</ClAdditionalOptions> + <LinkGenerateManifest>true</LinkGenerateManifest> + <LinkAdditionalOptions>$(LinkAdditionalOptions) /MANIFEST</LinkAdditionalOptions> + </PropertyGroup> + <!--Leaf Project Items--> + <ItemGroup> + <LinkPreCrtLibs Include="$(ClrLibPath)\utilcodenohost.lib"> + <ProjectReference>$(ClrSrcDirectory)utilcode\dyncrtnohost\dyncrtnohost.nativeproj</ProjectReference> + </LinkPreCrtLibs> + <LinkPreCrtLibs Include="$(ClrLibPath)\MDHotData.lib" /> + + <TargetLib Include="$(ClrLibPath)\corguids.lib" /> + <TargetLib Include="$(SdkLibPath)\mscoree.lib" /> + <TargetLib Include="$(SdkLibPath)\ole32.lib" /> + <TargetLib Include="$(SdkLibPath)\user32.lib" /> + <TargetLib Include="$(SdkLibPath)\uuid.lib" /> + <TargetLib Include="$(SdkLibPath)\oleaut32.lib" /> + <ProjectReference Include="$(ClrSrcDirectory)md\hotdata\full\mdhotdata.nativeproj"> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <RCResourceFile Include="native.rc" /> + </ItemGroup> + <ItemGroup> + <CppCompile Include="mdinfo.cpp" /> + <CppCompile Include="mdobj.cpp" /> + <CppCompile Include="metainfo.cpp" /> + </ItemGroup> + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" /> +</Project> diff --git a/src/tools/util/.gitmirror b/src/tools/util/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/tools/util/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/tools/util/consoleargs.cpp b/src/tools/util/consoleargs.cpp new file mode 100644 index 0000000000..3baacd1635 --- /dev/null +++ b/src/tools/util/consoleargs.cpp @@ -0,0 +1,958 @@ +// 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. + +#include <stdio.h> +#include "consoleargs.h" +#include <strsafe.h> + +typedef unsigned char byte; + +size_t SafeStrCopy( _In_ LPCWSTR wszSrc, _In_ size_t cchSrc, _Out_ LPWSTR wszDest, _In_ size_t cchDest) +{ + if (cchSrc == (size_t)-1) + cchSrc = wcslen(wszSrc); + + if (cchSrc >= cchDest) { + SetLastError(ERROR_FILENAME_EXCED_RANGE); + return 0; + } + + if (FAILED(StringCchCopyNW( wszDest, cchDest, wszSrc, cchSrc))) { + SetLastError(ERROR_FILENAME_EXCED_RANGE); + return 0; + } + return cchSrc; +} + +size_t SafeStrLower( _In_ LPCWSTR wszSrc, _In_ size_t cchSrc, _Out_ LPWSTR wszDest, _In_ size_t cchDest) +{ + if (cchSrc == (size_t)-1) + cchSrc = wcslen(wszSrc); + + if (cchSrc >= cchDest) { + SetLastError(ERROR_FILENAME_EXCED_RANGE); + return 0; + } + + SafeStrCopy(wszSrc, cchSrc, wszDest, cchDest); + _wcslwr_s((WCHAR*)wszDest, cchDest); + return wcslen(wszDest); +} + +inline int HexValue (WCHAR c) +{ + return (c >= '0' && c <= '9') ? c - '0' : (c & 0xdf) - 'A' + 10; +} + +#ifndef PLATFORM_UNIX +// Get canonical file path from a user specified path. wszSrcfileName can include relative paths, etc. +// Much of this function was taken from csc.exe. +DWORD GetCanonFilePath(_In_z_ LPCWSTR wszSrcFileName, _Out_z_cap_(cchDestFileName) LPWSTR wszDestFileName, _In_ DWORD cchDestFileName, _In_ bool fPreserveSrcCasing) +{ + DWORD full_len; + WCHAR * full_path = new WCHAR[cchDestFileName]; // an intermediate buffer + WCHAR * temp_path = new WCHAR[cchDestFileName]; // Used if FindFile fails + WCHAR * full_cur; + WCHAR * out_cur; + WCHAR * out_end; + bool hasDrive = false; + + memset(full_path, 0, cchDestFileName * sizeof(WCHAR)); + out_cur = wszDestFileName; + out_end = out_cur + cchDestFileName; + if (wszSrcFileName != wszDestFileName) + *out_cur = L'\0'; + full_cur = full_path; + + // Replace '\\' with single backslashes in paths, because W_GetFullPathName fails to do this on win9x. + size_t i = 0; + size_t j = 0; + size_t length = wcslen(wszSrcFileName); + while (j<length) + { + // UNC paths start with '\\' so skip the first character if it is a backslash. + if (j!= 0 && wszSrcFileName[j] == '\\' && wszSrcFileName[j+1] == '\\') + j++; + else + temp_path[i++] = wszSrcFileName[j++]; + if (i >= cchDestFileName) { + SetLastError(ERROR_FILENAME_EXCED_RANGE); + goto FAIL; + } + } + temp_path[i] = L'\0'; + + full_len = GetFullPathNameW(temp_path, cchDestFileName, full_path, NULL); + if (wszSrcFileName == wszDestFileName) + wszDestFileName[cchDestFileName-1] = L'\0'; + if (full_len == 0) { + goto FAIL; + } else if (full_len >= cchDestFileName) { + SetLastError(ERROR_FILENAME_EXCED_RANGE); + goto FAIL; + } + + // Allow only 1 ':' for drives and no long paths with "\\?\" + if (((full_path[0] >= L'a' && full_path[0] <= L'z') || + (full_path[0] >= L'A' && full_path[0] <= L'Z')) && + full_path[1] == L':') + hasDrive = true; + + // We don't allow colons (except after the drive letter) + // long paths beginning with "\\?\" + // devices beginning with "\\.\" + // or wildcards + // or characters 0-31 + if (wcschr( full_path + (hasDrive ? 2 : 0), W(':')) != NULL || + wcsncmp( full_path, W("\\\\?\\"), 4) == 0 || + wcsncmp( full_path, W("\\\\.\\"), 4) == 0 || + wcspbrk(full_path, W("?*\x1\x2\x3\x4\x5\x6\x7\x8\x9") + W("\xA\xB\xC\xD\xE\xF\x10\x11\x12\x13\x14\x15") + W("\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\0")) != NULL) { + SetLastError(ERROR_INVALID_NAME); + goto FAIL; + } + + + if (hasDrive) { + size_t len = SafeStrLower( full_path, 3, out_cur, out_end - out_cur); + if (len == 0) + goto FAIL; + + full_cur += 3; + out_cur += len; + + } else if (full_path[0] == L'\\' && full_path[1] == L'\\') { + // Must be a UNC pathname, so lower-case the server and share + // since there's no known way to get the 'correct casing' + WCHAR * slash = wcschr(full_path + 2, L'\\'); + // slash should now point to the backslash between the server and share + if (slash == NULL || slash == full_path + 2) { + SetLastError(ERROR_INVALID_NAME); + goto FAIL; + } + + slash = wcschr(slash + 1, L'\\'); + if (slash == NULL) { + slash = full_path + wcslen(full_path); + } else if (slash[-1] == L'\\') { + // An empty share-name? + SetLastError(ERROR_INVALID_NAME); + goto FAIL; + } else + slash++; + // slash should now point to char after the slash after the share name + // or the end of the sharename if there's no trailing slash + + size_t len = SafeStrLower( full_path, slash - full_path, out_cur, out_end - out_cur); + if (len == 0) + goto FAIL; + + full_cur = slash; + out_cur += len; + + } else { + // Not a drive-leter path or a UNC path, so assume it's invalid + SetLastError(ERROR_INVALID_NAME); + goto FAIL; + } + + // We either have a lower-cased drive letter or a UNC name + // with it's trailing slash + // out_cur points to the trailing NULL + // full_cur points to the character after the slash + + // Now iterate over each element of the path and attempt to canonicalize it + // It's possible for this loop to never run + // (for strings like "C:\" or "\\unc\share" or "\\unc\share2\") + while (*full_cur) { + WIN32_FIND_DATAW find_data; + bool hasSlash = true; + WCHAR * slash = wcschr(full_cur, '\\'); + if (slash == NULL) { + // This means we're on the last element of the path + // so work with everything left in the string + hasSlash = false; + slash = full_cur + wcslen(full_cur); + } + + // Check to make sure we have enough room for the next part of the path + if (out_cur + (slash - full_cur) >= out_end) { + SetLastError(ERROR_FILENAME_EXCED_RANGE); + goto FAIL; + } + + // Copy over the next path part into the output buffer + // so we can run FindFile to get the correct casing/long filename + memcpy(out_cur, full_cur, (BYTE*)slash - (BYTE*)full_cur); + out_cur[slash - full_cur] = L'\0'; + HANDLE hFind = FindFirstFileW(wszDestFileName, &find_data); + if (hFind == INVALID_HANDLE_VALUE) { + size_t temp_len; + + // We coundn't find the file, the general causes are the file doesn't exist + // or we don't have access to it. Either way we still try to get a canonical filename + // but preserve the passed in casing for the filename + + if (!hasSlash && fPreserveSrcCasing) { + // This is the last component in the filename, we should preserve the user's input text + // even if we can't find it + out_cur += slash - full_cur; + full_cur = slash; + break; + } + + // This will succeed even if we don't have access to the file + // (And on NT4 if the filename is already in 8.3 form) + temp_len = GetShortPathNameW(wszDestFileName, temp_path, cchDestFileName); + if (temp_len == 0) { + // GetShortPathName failed, we have no other way of figuring out the + // The filename, so just lowercase it so it hashes in a case-insensitive manner + + if (!hasSlash) { + // If it doesn't have a slash, then it must be the last part of the filename, + // so don't lowercase it, preserve whatever casing the user gave + temp_len = SafeStrCopy( full_cur, slash - full_cur, out_cur, out_end - out_cur); + } else { + temp_len = SafeStrLower( full_cur, slash - full_cur, out_cur, out_end - out_cur); + } + if (temp_len == 0) + goto FAIL; + + full_cur = slash; + out_cur += temp_len; + + } else if (temp_len >= cchDestFileName) { + // The short filename is longer than the whole thing? + // This shouldn't ever happen, right? + SetLastError(ERROR_FILENAME_EXCED_RANGE); + goto FAIL; + } else { + // GetShortPathName succeeded with a path that is less than BUFFER_LEN + // find the last slash and copy it. (We don't want to copy previous + // path components that we've already 'resolved') + // However, GetShortPathName doesn't always correct the casing + // so as a safe-guard, lower-case it (unless it's the last filename) + WCHAR * temp_slash = wcsrchr(temp_path, L'\\'); + + temp_slash++; + size_t len = 0; + if (!hasSlash) { + len = SafeStrCopy( temp_slash, -1, out_cur, out_end - out_cur); + } else { + len = SafeStrLower( temp_slash, -1, out_cur, out_end - out_cur); + } + if (len == 0) + goto FAIL; + + full_cur = slash; + out_cur += len; + + } + } else { + // Copy over the properly cased long filename + FindClose(hFind); + size_t name_len = wcslen(find_data.cFileName); + if (out_cur + name_len + (hasSlash ? 1 : 0) >= out_end) { + SetLastError(ERROR_FILENAME_EXCED_RANGE); + goto FAIL; + } + + // out_cur already has the filename with the input casing, so we can just leave it alone + // if this is not a directory name and the caller asked to perserve the casing + if (hasSlash || !fPreserveSrcCasing) { + memcpy(out_cur, find_data.cFileName, name_len * sizeof(WCHAR)); + } + else if (name_len != (slash - full_cur) || _wcsnicmp(find_data.cFileName, full_cur, name_len) != 0) { + // The user asked us to preserve the casing of the filename + // and the filename is different by more than just casing so report + // an error indicating we can't create the file + SetLastError(ERROR_FILE_EXISTS); + goto FAIL; + } + + out_cur += name_len; + full_cur = slash; + } + + if (hasSlash) { + if (out_cur + 1 >= out_end) { + SetLastError(ERROR_FILENAME_EXCED_RANGE); + goto FAIL; + } + full_cur++; + *out_cur++ = L'\\'; + } + *out_cur = '\0'; + } + + return (DWORD)(out_cur - wszDestFileName); + +FAIL: + if (full_path) + { + delete [] full_path; + } + if (temp_path) + { + delete [] temp_path; + } + return 0; +} +#endif // !PLATFORM_UNIX + +bool FreeString(LPCWSTR szText) +{ + if (szText) + delete [] (const_cast<LPWSTR>(szText)); + return true; +} + +bool IsWhitespace(WCHAR c) +{ + return c == L' ' || c == L'\t' || c == L'\n' || c == L'\r'; +} + +void ConsoleArgs::CleanUpArgs() +{ + while (m_listArgs) + { + WStrList * next = m_listArgs->next; + if (m_listArgs->arg) + delete [] m_listArgs->arg; + delete m_listArgs; + m_listArgs = next; + } + + if (m_rgArgs) + delete[] m_rgArgs; + + m_rgArgs = NULL; + + if(m_lastErrorMessage) + { + delete[] m_lastErrorMessage; + } +} + +bool ConsoleArgs::GetFullFileName(LPCWSTR szSource, __out_ecount(cchFilenameBuffer) LPWSTR filenameBuffer, DWORD cchFilenameBuffer, bool fOutputFilename) +{ +#ifdef PLATFORM_UNIX + WCHAR tempBuffer[MAX_LONGPATH]; + memset(filenameBuffer, 0, cchFilenameBuffer * sizeof(WCHAR)); + if (!PathCanonicalizeW(tempBuffer, szSource) || + StringCchCopyW(filenameBuffer, cchFilenameBuffer, tempBuffer) != S_OK) +#else + if (0 == GetCanonFilePath( szSource, filenameBuffer, cchFilenameBuffer, fOutputFilename)) +#endif + { + if (filenameBuffer[0] == L'\0') + { + // This could easily fail because of an overflow, but that's OK + // we only want what will fit in the output buffer so we can print + // a good error message + StringCchCopyW(filenameBuffer, cchFilenameBuffer - 4, szSource); + // Don't cat on the ..., only stick it in the last 4 characters + // to indicate truncation (if the string is short than this it just won't print) + StringCchCopyW(filenameBuffer + cchFilenameBuffer - 4, 4, W("...")); + } + return false; + } + return true; +} + +// +// Clear previous error message if any and set the new one by copying into m_lastErrorMessage. +// We are responsible for freeing the memory destruction. +// +void ConsoleArgs::SetErrorMessage(__in LPCWSTR pwzMessage) +{ + if (m_lastErrorMessage != nullptr) + { + delete[] m_lastErrorMessage; + } + m_errorOccurred = true; + m_lastErrorMessage = new WCHAR[wcslen(pwzMessage) + 1]; + if (m_lastErrorMessage == nullptr) + { + // + // Out of memory allocating error string + // + m_lastErrorMessage = kOutOfMemory; + return; + } + + wcscpy_s((LPWSTR)m_lastErrorMessage, wcslen(pwzMessage) + 1, pwzMessage); +} + +// +// Create a simple leaf tree node with the given text +// +b_tree * ConsoleArgs::MakeLeaf(LPCWSTR text) +{ + b_tree * t = NULL; + size_t name_len = wcslen(text) + 1; + LPWSTR szCopy = new WCHAR[name_len]; + + if (!szCopy) + { + return NULL; + } + + HRESULT hr; + hr = StringCchCopyW (szCopy, name_len, text); + + t = new b_tree(szCopy); + if (!t) + { + delete [] szCopy; + return NULL; + } + return t; +} + +// +// Free the memory allocated by the tree (recursive) +// +void ConsoleArgs::CleanupTree(b_tree *root) +{ + if (root == NULL) + return ; + root->InOrderWalk(FreeString); + delete root; +} + +// +// Search the binary tree and add the given string +// return true if it was added or false if it already +// exists +// +HRESULT ConsoleArgs::TreeAdd(b_tree **root, LPCWSTR add + ) +{ + // Special case - init the tree if it + // doesn't already exist + if (*root == NULL) + { + *root = MakeLeaf(add + ); + return *root == NULL ? E_OUTOFMEMORY : S_OK; + } + + size_t name_len = wcslen(add + ) + 1; + LPWSTR szCopy = new WCHAR[name_len]; + + if (!szCopy) + { + return NULL; + } + + HRESULT hr = StringCchCopyW (szCopy, name_len, add + ); + // otherwise, just let the template do the work + hr = (*root)->Add(szCopy, _wcsicmp); + + if (hr != S_OK) // S_FALSE means it already existed + delete [] szCopy; + + return hr; +} + +// +// Parse the text into a list of argument +// return the total count +// and set 'args' to point to the last list element's 'next' +// This function assumes the text is NULL terminated +// +void ConsoleArgs::TextToArgs(LPCWSTR szText, WStrList ** listReplace) +{ + WStrList **argLast; + const WCHAR *pCur; + size_t iSlash; + int iCount; + + argLast = listReplace; + pCur = szText; + iCount = 0; + + // Guaranteed that all tokens are no bigger than the entire file. + LPWSTR szTemp = new WCHAR[wcslen(szText) + 1]; + if (!szTemp) + { + return ; + } + while (*pCur != '\0') + { + WCHAR *pPut, *pFirst, *pLast; + WCHAR chIllegal; + +LEADINGWHITE: + while (IsWhitespace( *pCur) && *pCur != '\0') + pCur++; + + if (*pCur == '\0') + break; + else if (*pCur == L'#') + { + while ( *pCur != '\0' && *pCur != '\n') + pCur++; // Skip to end of line + goto LEADINGWHITE; + } + + // The treatment of quote marks is a bit different than the standard + // treatment. We only remove quote marks at the very beginning and end of the + // string. We still consider interior quotemarks for space ignoring purposes. + // All the below are considered a single argument: + // "foo bar" -> foo bar + // "foo bar";"baz" -> "foo bar";"baz" + // fo"o ba"r -> fo"o ba"r + // + // Additionally, in order to allow multi-line arguments we allow a ^ at the + // end of a line to specify "invisible space". A character sequence matching + // "\^(\r\n|\r|\n)[ \t]*" will be completely ignored (whether inside a quoted + // string or not). The below transformations occur (and represent a single + // argument): + // "foo ^ + // bar" -> foo bar + // foo;^ + // bar -> foo;bar + // Notes: + // 1. Comments are not recognized in a multi-line argument + // 2. A caret escapes only one new-line followed by an arbitrary number of + // tabs or blanks. + // The following will be parsed as the names suggest, into several different + // arguments: + // /option1 ^ + // val1_1;^ + // val1_2;^ + // val1_3;^ + // + // /option2 + // /opt^ + // ion3 -> /option1 val1_1;val1_2;val1_3; /option2 /option3 + int cQuotes = 0; + pPut = pFirst = szTemp; + chIllegal = 0; + while ((!IsWhitespace( *pCur) || !!(cQuotes & 1)) && *pCur != '\0') + { + switch (*pCur) + { + // All this weird slash stuff follows the standard argument processing routines + case L'\\': + iSlash = 0; + // Copy and advance while counting slashes + while (*pCur == L'\\') + { + *pPut++ = *pCur++; + iSlash++; + } + + // Slashes not followed by a quote character don't matter now + if (*pCur != L'\"') + break; + + // If there's an odd count of slashes, it's escaping the quote + // Otherwise the quote is a quote + if ((iSlash & 1) == 0) + { + ++cQuotes; + } + *pPut++ = *pCur++; + break; + + case L'\"': + ++cQuotes; + *pPut++ = *pCur++; + break; + + case L'^': + // ignore this sequence: \^[\r\n|\r|\n]( \t)* + if (pCur[1] == L'\r' || pCur[1] == L'\n') + { + if (pCur[1] == L'\r' && pCur[2] == L'\n') + pCur += 3; + else + pCur += 2; + + while (*pCur == L' ' || *pCur == L'\t') + ++pCur; + } + else + { + *pPut++ = *pCur++; // Copy the caret and advance + } + break; + + case L'\x01': + case L'\x02': + case L'\x03': + case L'\x04': + case L'\x05': + case L'\x06': + case L'\x07': + case L'\x08': + case L'\x09': + case L'\x0A': + case L'\x0B': + case L'\x0C': + case L'\x0D': + case L'\x0E': + case L'\x0F': + case L'\x10': + case L'\x11': + case L'\x12': + case L'\x13': + case L'\x14': + case L'\x15': + case L'\x16': + case L'\x17': + case L'\x18': + case L'\x19': + case L'\x1A': + case L'\x1B': + case L'\x1C': + case L'\x1D': + case L'\x1E': + case L'\x1F': + case L'|': + // Save the first legal character and skip over them + if (chIllegal == 0) + chIllegal = *pCur; + pCur++; + break; + + default: + *pPut++ = *pCur++; // Copy the char and advance + break; + } + } + + pLast = pPut; + *pPut++ = '\0'; + + // If the string is surrounded by quotes, with no interior quotes, remove them. + if (cQuotes == 2 && *pFirst == L'\"' && *(pLast - 1) == L'\"') + { + ++pFirst; + --pLast; + *pLast = L'\0'; + } + + if (chIllegal != 0) + { + SetErrorMessage(W("Illegal option character.")); + break; + } + + size_t cchLen = pLast - pFirst + 1; + WCHAR * szArgCopy = new WCHAR[cchLen]; + if (!szArgCopy || FAILED(StringCchCopyW(szArgCopy, cchLen, pFirst))) + { + SetErrorMessage(W("Out of memory.")); + break; + } + WStrList * listArgNew = new WStrList( szArgCopy, (*argLast)); + if (!listArgNew) + { + SetErrorMessage(W("Out of memory.")); + break; + } + + *argLast = listArgNew; + argLast = &listArgNew->next; + } + + delete[] szTemp; + +} + +// +// Pass in the command line args, argc and argv +// +// We expand any response files that may be contained in the args and return a new +// set of args, pargc2 and pppargv2 that contain the full flat command line. +// +bool ConsoleArgs::ExpandResponseFiles(__in int argc, __deref_in_ecount(argc) const LPCWSTR * argv, int * pargc2, __deref_out_ecount(*pargc2) LPWSTR ** pppargv2) +{ + *pargc2 = 0; + *pppargv2 = NULL; + WStrList **argLast = &m_listArgs; + while (argc > 0) + { + // Make a copy of the original var args so we can just delete[] everything + // once parsing is done: original args and new args from response files + // mixed in amongst the originals. + LPWSTR copyArg = new WCHAR[wcslen(argv[0]) + 1]; + if (!copyArg) + { + SetErrorMessage(W("Out of memory.")); + return false; + } + wcscpy_s(copyArg, wcslen(argv[0]) + 1, argv[0]); + + WStrList * listArgNew = new WStrList(copyArg, (*argLast)); + if (!listArgNew) + { + SetErrorMessage(W("Out of memory.")); + return false; + } + + *argLast = listArgNew; + argLast = &listArgNew->next; + + argc--; + argv++; + } + + // Process Response Files + ProcessResponseArgs(); + if (m_errorOccurred) + return false; + + // Now convert to an argc/argv form for remaining processing. + int newArgc = 0; + for (WStrList * listCurArg = m_listArgs; listCurArg != NULL; listCurArg = listCurArg->next) + { + if (listCurArg->arg) + ++newArgc; + } + + m_rgArgs = new LPWSTR[newArgc]; + if (!m_rgArgs) + { + SetErrorMessage(W("Out of memory.")); + return false; + } + int i = 0; + for (WStrList * listCurArg = m_listArgs; listCurArg != NULL; listCurArg = listCurArg->next) + { + if (listCurArg->arg) + { + LPWSTR newString = new WCHAR[wcslen(listCurArg->arg) + 1]; + wcscpy_s(newString, wcslen(listCurArg->arg) + 1, listCurArg->arg); + m_rgArgs[i++] = newString; + } + } + + *pargc2 = newArgc; + *pppargv2 = m_rgArgs; + return !m_errorOccurred; +} + +// +// Read file to end, converting to unicode +// ppwzTextBuffer is allocated. Caller is responsible for freeing +// +bool ConsoleArgs::ReadTextFile(LPCWSTR pwzFilename, __deref_out LPWSTR *ppwzTextBuffer) +{ + bool success = false; + char *bufA = nullptr; + WCHAR *bufW = nullptr; + + HANDLE hFile = CreateFile(pwzFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + SetErrorMessage(W("Cannot open response file.")); + goto ErrExit; + } + + { + DWORD size = GetFileSize(hFile, NULL); + bufA = new char[size]; + + if (!bufA) + { + SetErrorMessage(W("Out of memory")); + goto ErrExit; + } + DWORD numRead = 0; + if (!ReadFile(hFile, bufA, size, &numRead, NULL) || numRead != size) + { + SetErrorMessage(W("Failure reading response file.")); + goto ErrExit; + } + + char *postByteOrderMarks = bufA; + + // + // If there are Byte Order Marks, skip them make sure they are ones that don't + // require us to handle the wrong endianness + // + + byte byte0 = (byte)bufA[0]; + byte byte1 = (byte)bufA[1]; + byte byte2 = (byte)bufA[2]; + byte byte3 = (byte)bufA[3]; + + bool alreadyUtf16 = false; + + if (byte0 == 0xEF && byte1 == 0xBB && byte2 == 0xBF) + { + postByteOrderMarks += 3; + size -= 3; + } + else if (byte0 == 0xFF && byte1 == 0xFE) + { + postByteOrderMarks += 2; + size -= 2; + alreadyUtf16 = true; + } + else if (byte0 == 0xFE && byte1 == 0xFF) + { + SetErrorMessage(W("Invalid response file format. Use little endian encoding with Unicode")); + goto ErrExit; + } + else if ((byte0 == 0xFF && byte1 == 0xFE && byte2 == 0x00 && byte3 == 0x00) || + (byte0 == 0x00 && byte1 == 0x00 && byte2 == 0xFE && byte3 == 0xFF)) + { + SetErrorMessage(W("Invalid response file format. Use ANSI, UTF-8, or UTF-16")); + goto ErrExit; + } + + if (alreadyUtf16) + { + // + // File is already formatted as UTF-16; just copy the bytes into the output buffer + // + int requiredSize = size + 2; // space for 2 nullptr bytes + + // Sanity check - requiredSize better be an even number since we're dealing with UTF-16 + if (requiredSize % 2 != 0) + { + SetErrorMessage(W("Response file corrupt. Expected UTF-16 encoding but we had an odd number of bytes")); + goto ErrExit; + } + + requiredSize /= 2; + + bufW = new WCHAR[requiredSize]; + if (!bufW) + { + SetErrorMessage(W("Out of memory")); + goto ErrExit; + } + + memcpy(bufW, postByteOrderMarks, size); + bufW[requiredSize - 1] = L'\0'; + } + else + { + // + // File is formated as ANSI or UTF-8 and needs converting to UTF-16 + // + int requiredSize = MultiByteToWideChar(CP_UTF8, 0, postByteOrderMarks, size, nullptr, 0); + bufW = new WCHAR[requiredSize + 1]; + if (!bufW) + { + SetErrorMessage(W("Out of memory")); + goto ErrExit; + } + + if (!MultiByteToWideChar(CP_UTF8, 0, postByteOrderMarks, size, bufW, requiredSize)) + { + SetErrorMessage(W("Failure reading response file.")); + goto ErrExit; + } + + bufW[requiredSize] = L'\0'; + } + + *ppwzTextBuffer = bufW; + + success = true; + } + +ErrExit: + if (bufA) + { + delete[] bufA; + } + CloseHandle(hFile); + return success; +} + +/* + * Process Response files on the command line + */ +void ConsoleArgs::ProcessResponseArgs() +{ + HRESULT hr; + b_tree *response_files = NULL; + + WCHAR szFilename[MAX_LONGPATH]; + + for (WStrList * listCurArg = m_listArgs; + listCurArg != NULL && !m_errorOccurred; + listCurArg = listCurArg->next) + { + WCHAR * szArg = listCurArg->arg; + + // Skip everything except Response files + if (szArg == NULL || szArg[0] != '@') + continue; + + if (wcslen(szArg) == 1) + { + SetErrorMessage(W("No response file specified")); + goto CONTINUE; + } + + // Check for duplicates + if (!GetFullFileName(&szArg[1], szFilename, MAX_LONGPATH, false)) + continue; + + + hr = TreeAdd(&response_files, szFilename); + if (hr == E_OUTOFMEMORY) + { + SetErrorMessage(W("Out of memory.")); + goto CONTINUE; + } + else if (hr == S_FALSE) + { + SetErrorMessage(W("Duplicate response file.")); + goto CONTINUE; + } + + { + LPWSTR pwzFileBuffer; + pwzFileBuffer = nullptr; + if (!ReadTextFile(szFilename, &pwzFileBuffer)) + { + goto CONTINUE; + } + + LPWSTR szActualText = nullptr; +#ifdef PLATFORM_UNIX + szActualText = pwzFileBuffer; +#else + DWORD dwNumChars = ExpandEnvironmentStrings(pwzFileBuffer, NULL, 0); + LPWSTR szExpandedBuffer = new WCHAR[dwNumChars]; + if (szExpandedBuffer != nullptr) + { + DWORD dwRetVal = ExpandEnvironmentStrings(pwzFileBuffer, szExpandedBuffer, dwNumChars); + + if (dwRetVal != 0) + { + szActualText = szExpandedBuffer; + } + else + { + // Expand failed + + } + } +#endif + + TextToArgs(szActualText, &listCurArg->next); + } + +CONTINUE: // remove the response file argument, and continue to the next. + listCurArg->arg = NULL; + } + + CleanupTree(response_files); +} + diff --git a/src/tools/util/consoleargs.h b/src/tools/util/consoleargs.h new file mode 100644 index 0000000000..06b6e75384 --- /dev/null +++ b/src/tools/util/consoleargs.h @@ -0,0 +1,72 @@ +// 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. + +#ifndef __CONSOLEARGS_H__ +#define __CONSOLEARGS_H__ + +#include "list.h" +#include "tree.h" +#include <strsafe.h> + +#include "palclr.h" + +typedef tree<LPCWSTR> b_tree; +typedef list<WCHAR*> WStrList; + +const LPCWSTR kOutOfMemory = W("Out of memory"); + +class ConsoleArgs +{ +public: + // Place the fully-qualified filename in the given output buffer + bool GetFullFileName(LPCWSTR szSource, __out_ecount(cbFilenameBuffer) LPWSTR filenameBuffer, DWORD cbFilenameBuffer, bool fOutputFilename); + + ConsoleArgs() : + m_rgArgs(NULL), + m_listArgs(NULL), + m_errorOccurred(false), + m_lastErrorMessage(nullptr) + { + }; + + ~ConsoleArgs() + { + CleanUpArgs(); + }; + + // returns false if there are errors + bool ExpandResponseFiles(__in int argc, __deref_in_ecount(argc) const LPCWSTR * argv, int * pargc2, __deref_out_ecount(*pargc2) LPWSTR ** pppargv2); + + // Frees all memory used by the arg list and the argv/argc array + void CleanUpArgs(); + + LPCWSTR ErrorMessage() + { + if (m_errorOccurred) + { + return m_lastErrorMessage; + } + else + { + return nullptr; + } + } + +private: + void SetErrorMessage(__in LPCWSTR pwzMessage); + b_tree * MakeLeaf( LPCWSTR szText); + void CleanupTree( b_tree * root); + HRESULT TreeAdd( b_tree ** root, LPCWSTR szAdd); + void TextToArgs( LPCWSTR szText, WStrList ** listReplace); + bool ReadTextFile(LPCWSTR pwzFilename, __deref_out LPWSTR *ppwzTextBuffer); + void ProcessResponseArgs(); + + LPWSTR * m_rgArgs; + WStrList * m_listArgs; + + bool m_errorOccurred; + LPCWSTR m_lastErrorMessage; +}; + +#endif // __CONSOLEARGS_H__ diff --git a/src/tools/util/file_can.h b/src/tools/util/file_can.h new file mode 100644 index 0000000000..71031eb9d7 --- /dev/null +++ b/src/tools/util/file_can.h @@ -0,0 +1,69 @@ +// 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. + +#ifndef __FILE_CAN_H__ +#define __FILE_CAN_H__ + +class CFileChecksum; + +enum FileType +{ + ftUnknown = 0, + ftUnicode, + ftSwappedUnicode, + ftUTF8, + ftASCII, + ftBinary +}; + +HANDLE OpenFileEx( LPCWSTR filename, DWORD *fileLen, LPCWSTR relPath = NULL, bool bWrite = false); +HRESULT ReadTextFile (PCWSTR pszFileName, UINT uiCodePage, WCAllocBuffer & textBuffer, FileType *fileType); +#if !defined(FEATURE_PAL) && !defined(CSEE) +// If you call ReadTextFile a lot you should create one HCRYPTPROV and pass it in to every call, otherwise +// ReadTextFile indirectly creates and destroys a new HCRYPTPROV for every call, which is slow and unnecessary. +// You can use CryptProvider to manage an HCRYPTPROV for you. +HRESULT ReadTextFile (PCWSTR pszFileName, UINT uiCodePage, WCAllocBuffer & textBuffer, FileType *fileType, CFileChecksum *pChecksum, HCRYPTPROV hCryptProv = NULL); +#endif + +// Src and Dest may be the same buffer +// Returns 0 for error (check via GetLastError()) or count of characters +// (not including NULL) copied to Dest. +// if fPreserveSrcCasing is set, ignores on-disk casing of filename (but still gets on-disk casing of directories) +// if fPreserveSrcCasing is set and and existing file matches with different short/longness it will fail +// and set the error code to ERROR_FILE_EXISTS +DWORD GetCanonFilePath(LPCWSTR wszSrcFileName, WCBuffer outBuffer, bool fPreserveSrcCasing); + +// GetCanonFilePath uses a cache to eliminate redundant calls to FindFirstFile. This cache +// is global and is thus long lived. The IDE would like to minimize memory impact, so +// ClearGetCanonFilePathCache is provided here for them to clear the cache when appropriate. +void ClearGetCanonFilePathCache(); + +// Remove quote marks from a string. +// Translation is done in-place +LPWSTR RemoveQuotes(WCBuffer textBuffer); + +// Remove quote marks from a string. +// Replace various characters with other illegal characters if unquoted. +// Translation is done in-place. +LPWSTR RemoveQuotesAndReplaceComma(WCBuffer textBuffer); // "," -> "|" +LPWSTR RemoveQuotesAndReplacePathDelim(WCBuffer textBuffer); // ",;" -> "|" +LPWSTR RemoveQuotesAndReplaceAlias(WCBuffer textBuffer); // ",;" -> "|" and "=" -> "\x1" + +// Safe version of ToLowerCase +// Gaurantees null termination even if buffer size is too small +inline PWSTR WINAPI SafeToLowerCase (PCWSTR pSrc, WCBuffer textBuffer) +{ + PWSTR returnValue = ToLowerCase(pSrc, textBuffer.GetData(), textBuffer.Count()); + if (textBuffer.Count() > 0) + { + textBuffer.SetAt(textBuffer.Count() - 1, 0); + } + return returnValue; +} + +// Joins a relative or absolute filename to the given path and stores the new +// filename in lpBuffer +bool MakePath( /*[in]*/LPCWSTR lpPath, /*[in]*/LPCWSTR lpFileName, WCBuffer pathBuffer); + +#endif // __FILE_CAN_H__ diff --git a/src/tools/util/list.h b/src/tools/util/list.h new file mode 100644 index 0000000000..1748ed9cd3 --- /dev/null +++ b/src/tools/util/list.h @@ -0,0 +1,25 @@ +// 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. + +#ifndef __GENERIC_LIST_H__ +#define __GENERIC_LIST_H__ + +// Simple parameterized linked list +// with some good ctors +template <typename _T> +struct list +{ + _T arg; + list<_T> *next; + + list(_T t, list<_T> *n) + { + arg = t, next = n; + } + list() : arg(), next(NULL) + { + } +}; + +#endif // __GENERIC_LIST_H__ diff --git a/src/tools/util/tree.h b/src/tools/util/tree.h new file mode 100644 index 0000000000..164f4c6975 --- /dev/null +++ b/src/tools/util/tree.h @@ -0,0 +1,242 @@ +// 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. + +#ifndef __GENERIC_TREE_H__ +#define __GENERIC_TREE_H__ + +#include <windows.h> + +// Partially balanced binary tree +// it does individual rotations on insertion, but does nto allow deletion. +// thus the worst case depth is not (n), but (n/2) +// Generic paramter is the element type +// Find and Add require a method that compares 2 elements +template <typename _E> +struct tree +{ + _E name; + tree<_E> *lChild; + tree<_E> *rChild; + size_t lDepth; + size_t rDepth; + + tree(_E e) + { + name = e; + lChild = rChild = NULL; + lDepth = rDepth = 0; + } + ~tree() + { + Cleanup(); + } + + bool InOrderWalk( bool (WalkFunc)(_E)) + { + if (lChild != NULL && !lChild->InOrderWalk(WalkFunc)) + return false; + if (!WalkFunc(name)) + return false; + if (rChild != NULL) + return rChild->InOrderWalk(WalkFunc); + return true; + } + + /* + * return the depths of the tree from here down (minimum of 1) + */ + size_t MaxDepth() + { + return lDepth > rDepth ? lDepth + 1 : rDepth + 1; + } + + /* + * Search the binary tree for the given string + * return a pointer to it was added or NULL if it + * doesn't exist + */ + _E * Find(_E SearchVal, int (__cdecl CompFunc)(_E, _E)) + { + int cmp = CompFunc(name, SearchVal); + if (cmp < 0) + { + if (lChild == NULL) + return NULL; + else + return lChild->Find(SearchVal, CompFunc); + } + else if (cmp > 0) + { + if (rChild == NULL) + return NULL; + else + return rChild->Find(SearchVal, CompFunc); + } + else + return &name; + } + + /* + * Search the binary tree and add the given string + * return S_OK if it was added or S_FALSE if it already + * exists (or E_OUTOFMEMORY) + */ + HRESULT Add(_E add + , int (__cdecl CompFunc)(_E, _E)) + { + int cmp = CompFunc(name, add + ); +REDO: + if (cmp == 0) + return S_FALSE; + + if (cmp < 0) + { + if (lChild == NULL) + { + lDepth = 1; + lChild = new tree<_E>(add + ); + if (lChild == NULL) + return E_OUTOFMEMORY; + return S_OK; + } + else if (rDepth < lDepth) + { + tree<_E> *temp = new tree<_E>(name); + if (temp == NULL) + return E_OUTOFMEMORY; + temp->rChild = rChild; + temp->rDepth = rDepth; + if (lChild != NULL && + (cmp = CompFunc(lChild->name, add + )) > 0) + { + // push right + temp->lChild = NULL; + temp->lDepth = 0; + name = add + ; + rChild = temp; + rDepth++; + return S_OK; + } + else if (cmp == 0) + { + temp->rChild = NULL; + delete temp; + return S_FALSE; + } + else + { + // Rotate right + temp->lChild = lChild->rChild; + temp->lDepth = lChild->rDepth; + name = lChild->name; + lDepth = lChild->lDepth; + rDepth = temp->MaxDepth(); + rChild = temp; + temp = lChild->lChild; + lChild->lChild = lChild->rChild = NULL; + delete lChild; + lChild = temp; + goto REDO; + } + } + else + { + HRESULT hr = lChild->Add(add + , CompFunc); + lDepth = lChild->MaxDepth(); + return hr; + } + } + else + { + if (rChild == NULL) + { + rDepth = 1; + rChild = new tree<_E>(add + ); + if (rChild == NULL) + return E_OUTOFMEMORY; + return S_OK; + } + else if (lDepth < rDepth) + { + tree<_E> *temp = new tree<_E>(name); + if (temp == NULL) + return E_OUTOFMEMORY; + temp->lChild = lChild; + temp->lDepth = lDepth; + if (rChild != NULL && + (cmp = CompFunc(rChild->name, add + )) < 0) + { + // push left + temp->rChild = NULL; + temp->rDepth = 0; + name = add + ; + lChild = temp; + lDepth++; + return S_OK; + } + else if (cmp == 0) + { + temp->lChild = NULL; + delete temp; + return S_FALSE; + } + else + { + // Rotate left + temp->rChild = rChild->lChild; + temp->rDepth = rChild->lDepth; + name = rChild->name; + rDepth = rChild->rDepth; + lDepth = temp->MaxDepth(); + lChild = temp; + temp = rChild->rChild; + rChild->rChild = rChild->lChild = NULL; + delete rChild; + rChild = temp; + goto REDO; + } + } + else + { + HRESULT hr = rChild->Add(add + , CompFunc); + rDepth = rChild->MaxDepth(); + return hr; + } + } + } + + /* + * Free the memory allocated by the tree (recursive) + */ + void Cleanup() + { + if (this == NULL) + return ; + if (lChild != NULL) + { + lChild->Cleanup(); + delete lChild; + lChild = NULL; + } + if (rChild != NULL) + { + rChild->Cleanup(); + delete rChild; + rChild = NULL; + + } + } + +}; + +#endif // __GENERIC_TREE_H__ |