diff options
author | dotnet-bot <dotnet-bot@microsoft.com> | 2015-01-30 14:14:42 -0800 |
---|---|---|
committer | dotnet-bot <dotnet-bot@microsoft.com> | 2015-01-30 14:14:42 -0800 |
commit | ef1e2ab328087c61a6878c1e84f4fc5d710aebce (patch) | |
tree | dee1bbb89e9d722e16b0d1485e3cdd1b6c8e2cfa /src/utilcode | |
download | coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.gz coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.bz2 coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.zip |
Initial commit to populate CoreCLR repo
[tfs-changeset: 1407945]
Diffstat (limited to 'src/utilcode')
97 files changed, 52229 insertions, 0 deletions
diff --git a/src/utilcode/.gitmirror b/src/utilcode/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/utilcode/.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/utilcode/CMakeLists.txt b/src/utilcode/CMakeLists.txt new file mode 100644 index 0000000000..bf7317b526 --- /dev/null +++ b/src/utilcode/CMakeLists.txt @@ -0,0 +1,100 @@ + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +add_definitions(-DNO_CRT) + +if(WIN32) + add_compile_options(/wd4996) +endif(WIN32) + +set(UTILCODE_SOURCES + clrhost_nodependencies.cpp + ccomprc.cpp + ex.cpp + sbuffer.cpp + sstring_com.cpp + fstring.cpp + namespaceutil.cpp + makepath.cpp + splitpath.cpp + clrconfig.cpp + registrywrapper.cpp + collections.cpp + genericstackprobe.cpp + posterror.cpp + fstream.cpp + clrhelpers.cpp + stgpool.cpp + stgpooli.cpp + stgpoolreadonly.cpp + lazycow.cpp + utsem.cpp + peinformation.cpp + check.cpp + log.cpp + apithreadstress.cpp + arraylist.cpp + bitvector.cpp + comex.cpp + delayloadhelpers.cpp + guidfromname.cpp + jitperf.cpp + memorypool.cpp + rangetree.cpp + iallocator.cpp + loaderheap.cpp + perflog.cpp + outstring.cpp + ilformatter.cpp + opinfo.cpp + dacutil.cpp + sortversioning.cpp + corimage.cpp + format1.cpp + prettyprintsig.cpp + regutil.cpp + sigbuilder.cpp + sigparser.cpp + sstring.cpp + util_nodependencies.cpp + utilmessagebox.cpp + safewrap.cpp + clrhost.cpp + cycletimer.cpp + md5.cpp + util.cpp + stresslog.cpp + debug.cpp + pedecoder.cpp +) + +# These source file do not yet compile on Linux. +# They should be moved out from here into the declaration +# of UTILCODE_SOURCES above after fixing compiler errors. +if(WIN32) + set(UTILCODE_SOURCES + ${UTILCODE_SOURCES} + appxutil.cpp + dlwrap.cpp + downlevel.cpp + loadrc.cpp + newapis.cpp + tls.cpp + securitywrapper.cpp + securityutil.cpp + stacktrace.cpp + winfix.cpp + ) +endif(WIN32) + +convert_to_absolute_path(UTILCODE_SOURCES ${UTILCODE_SOURCES}) + +if(CLR_CMAKE_PLATFORM_UNIX) + add_compile_options(-fPIC) +endif(CLR_CMAKE_PLATFORM_UNIX) + +add_subdirectory(dac) +add_subdirectory(dyncrt) +if(WIN32) + add_subdirectory(staticnohost) +endif(WIN32)
\ No newline at end of file diff --git a/src/utilcode/UtilCode.vcproj b/src/utilcode/UtilCode.vcproj new file mode 100644 index 0000000000..3f147725b8 --- /dev/null +++ b/src/utilcode/UtilCode.vcproj @@ -0,0 +1,606 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9.00" + Name="utilcode" + ProjectGUID="{82B95BDA-224E-49B4-8BB7-03E7B9EEFF3B}" + RootNamespace="utilcode" + SccProjectName="SAK" + SccAuxPath="SAK" + SccLocalPath="SAK" + SccProvider="SAK" + Keyword="MakeFileProj" + TargetFrameworkVersion="131072" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="x86dbg|Win32" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="0" + > + <Tool + Name="VCNMakeTool" + BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:dbg" + ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:dbg" + CleanCommandLine="" + Output="Example.exe" + PreprocessorDefinitions="_X86_=1;i386=1" + IncludeSearchPath="..\inc\" + ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h" + AssemblySearchPath="" + ForcedUsingAssemblies="" + CompileAsManaged="" + /> + </Configuration> + <Configuration + Name="x86chk|Win32" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="0" + > + <Tool + Name="VCNMakeTool" + BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:chk" + ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:chk" + CleanCommandLine="" + Output="Example.exe" + PreprocessorDefinitions="_X86_=1;i386=1" + IncludeSearchPath="..\inc\" + ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h" + AssemblySearchPath="" + ForcedUsingAssemblies="" + CompileAsManaged="" + /> + </Configuration> + <Configuration + Name="x86ret|Win32" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="0" + > + <Tool + Name="VCNMakeTool" + BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:ret" + ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:ret" + CleanCommandLine="" + Output="Example.exe" + PreprocessorDefinitions="_X86_=1;i386=1" + IncludeSearchPath="..\inc\" + ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h" + AssemblySearchPath="" + ForcedUsingAssemblies="" + CompileAsManaged="" + /> + </Configuration> + <Configuration + Name="amd64dbg|Win32" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="0" + > + <Tool + Name="VCNMakeTool" + BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x64/buildType:dbg" + ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x64/buildType:dbg" + CleanCommandLine="" + Output="Example.exe" + PreprocessorDefinitions="_AMD64_=1;_WIN64=1;" + IncludeSearchPath="..\inc\" + ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h" + AssemblySearchPath="" + ForcedUsingAssemblies="" + CompileAsManaged="" + /> + </Configuration> + <Configuration + Name="amd64chk|Win32" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="0" + > + <Tool + Name="VCNMakeTool" + BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x64/buildType:chk" + ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x64/buildType:chk" + CleanCommandLine="" + Output="Example.exe" + PreprocessorDefinitions="_AMD64_=1;_WIN64=1;" + IncludeSearchPath="..\inc\" + ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h" + AssemblySearchPath="" + ForcedUsingAssemblies="" + CompileAsManaged="" + /> + </Configuration> + <Configuration + Name="amd64ret|Win32" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="0" + > + <Tool + Name="VCNMakeTool" + BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x64/buildType:ret" + ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x64/buildType:ret" + CleanCommandLine="" + Output="Example.exe" + PreprocessorDefinitions="_AMD64_=1;_WIN64=1;" + IncludeSearchPath="..\inc\" + ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h" + AssemblySearchPath="" + ForcedUsingAssemblies="" + CompileAsManaged="" + /> + </Configuration> + <Configuration + Name="x86corechk|Win32" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="0" + > + <Tool + Name="VCNMakeTool" + BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:corechk" + ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:corechk" + CleanCommandLine="" + Output="Example.exe" + PreprocessorDefinitions="_X86_=1;i386=1" + IncludeSearchPath="..\inc\" + ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h" + AssemblySearchPath="" + ForcedUsingAssemblies="" + CompileAsManaged="" + /> + </Configuration> + <Configuration + Name="x86coredbg|Win32" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="0" + > + <Tool + Name="VCNMakeTool" + BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:coredbg" + ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:coredbg" + CleanCommandLine="" + Output="Example.exe" + PreprocessorDefinitions="_X86_=1;i386=1" + IncludeSearchPath="..\inc\" + ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h" + AssemblySearchPath="" + ForcedUsingAssemblies="" + CompileAsManaged="" + /> + </Configuration> + <Configuration + Name="x86coreret|Win32" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="0" + > + <Tool + Name="VCNMakeTool" + BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:coreret" + ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:coreret" + CleanCommandLine="" + Output="Example.exe" + PreprocessorDefinitions="_X86_=1;i386=1" + IncludeSearchPath="..\inc\" + ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h" + AssemblySearchPath="" + ForcedUsingAssemblies="" + CompileAsManaged="" + /> + </Configuration> + <Configuration + Name="Mac.inteldbg|Win32" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="0" + > + <Tool + Name="VCNMakeTool" + BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:coredbg" + ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:coredbg" + CleanCommandLine="" + Output="Example.exe" + PreprocessorDefinitions="_X86_=1;i386=1" + IncludeSearchPath="..\inc\;"$(SolutionDir)\..\..\..\rotor\palrt\inc";"$(SolutionDir)\..\..\..\rotor\pal\inc"" + ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h" + AssemblySearchPath="" + ForcedUsingAssemblies="" + CompileAsManaged="" + /> + </Configuration> + <Configuration + Name="amd64corechk|Win32" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="0" + > + <Tool + Name="VCNMakeTool" + BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:amd64/buildType:corechk" + ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:amd64/buildType:corechk" + CleanCommandLine="" + Output="Example.exe" + PreprocessorDefinitions="_AMD64_=1;_WIN64=1" + IncludeSearchPath="..\inc\" + ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h" + AssemblySearchPath="" + ForcedUsingAssemblies="" + CompileAsManaged="" + /> + </Configuration> + <Configuration + Name="amd64coredbg|Win32" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="0" + > + <Tool + Name="VCNMakeTool" + BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:amd64/buildType:coredbg" + ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:amd64/buildType:coredbg" + CleanCommandLine="" + Output="Example.exe" + PreprocessorDefinitions="_AMD64_=1;_WIN64=1" + IncludeSearchPath="..\inc\" + ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h" + AssemblySearchPath="" + ForcedUsingAssemblies="" + CompileAsManaged="" + /> + </Configuration> + <Configuration + Name="amd64coreret|Win32" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="0" + > + <Tool + Name="VCNMakeTool" + BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:amd64/buildType:coreret" + ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:amd64/buildType:coreret" + CleanCommandLine="" + Output="Example.exe" + PreprocessorDefinitions="_AMD64_=1;_WIN64=1" + IncludeSearchPath="..\inc\" + ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h" + AssemblySearchPath="" + ForcedUsingAssemblies="" + CompileAsManaged="" + /> + </Configuration> + <Configuration + Name="armcoredbg|Win32" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="0" + > + <Tool + Name="VCNMakeTool" + BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:arm/buildType:coredbg" + ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:arm/buildType:coredbg" + CleanCommandLine="" + Output="Example.exe" + PreprocessorDefinitions="_ARM_;ARM" + IncludeSearchPath="..\inc\" + ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h" + AssemblySearchPath="" + ForcedUsingAssemblies="" + CompileAsManaged="" + /> + </Configuration> + <Configuration + Name="armcorechk|Win32" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="0" + > + <Tool + Name="VCNMakeTool" + BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:arm/buildType:corechk" + ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:arm/buildType:corechk" + CleanCommandLine="" + Output="Example.exe" + PreprocessorDefinitions="_ARM_;ARM" + IncludeSearchPath="..\inc\" + ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h" + AssemblySearchPath="" + ForcedUsingAssemblies="" + CompileAsManaged="" + /> + </Configuration> + <Configuration + Name="armcoreret|Win32" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="0" + > + <Tool + Name="VCNMakeTool" + BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:arm/buildType:coreret" + ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:arm/buildType:coreret" + CleanCommandLine="" + Output="Example.exe" + PreprocessorDefinitions="_ARM_;ARM" + IncludeSearchPath="..\inc\" + ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h" + AssemblySearchPath="" + ForcedUsingAssemblies="" + CompileAsManaged="" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <File + RelativePath=".\apithreadstress.cpp" + > + </File> + <File + RelativePath=".\arraylist.cpp" + > + </File> + <File + RelativePath=".\assemblyfilehash.cpp" + > + </File> + <File + RelativePath=".\bitvector.cpp" + > + </File> + <File + RelativePath=".\ccomprc.cpp" + > + </File> + <File + RelativePath=".\check.cpp" + > + </File> + <File + RelativePath=".\circularlog.cpp" + > + </File> + <File + RelativePath=".\CLRConfig.cpp" + > + </File> + <File + RelativePath=".\clrhost.cpp" + > + </File> + <File + RelativePath=".\COMEx.cpp" + > + </File> + <File + RelativePath=".\corimage.cpp" + > + </File> + <File + RelativePath=".\corimage.cpp" + > + </File> + <File + RelativePath=".\CycleTimer.cpp" + > + </File> + <File + RelativePath=".\dacutil.cpp" + > + </File> + <File + RelativePath=".\debug.cpp" + > + </File> + <File + RelativePath=".\dlwrap.cpp" + > + </File> + <File + RelativePath=".\downlevel.cpp" + > + </File> + <File + RelativePath=".\ex.cpp" + > + </File> + <File + RelativePath=".\format1.cpp" + > + </File> + <File + RelativePath=".\genericstackprobe.cpp" + > + </File> + <File + RelativePath=".\GuidFromName.cpp" + > + </File> + <File + RelativePath=".\hostimpl.cpp" + > + </File> + <File + RelativePath=".\hostimpl.h" + > + </File> + <File + RelativePath=".\IAllocator.cpp" + > + </File> + <File + RelativePath=".\ilFormatter.cpp" + > + </File> + <File + RelativePath=".\jitperf.cpp" + > + </File> + <File + RelativePath=".\loaderheap.cpp" + > + </File> + <File + RelativePath=".\lazycow.cpp" + > + </File> + <File + RelativePath=".\log.cpp" + > + </File> + <File + RelativePath=".\makepath.cpp" + > + </File> + <File + RelativePath=".\md5.cpp" + > + </File> + <File + RelativePath=".\memorypool.cpp" + > + </File> + <File + RelativePath=".\namespaceutil.cpp" + > + </File> + <File + RelativePath=".\newapis.cpp" + > + </File> + <File + RelativePath=".\pedecoder.cpp" + > + </File> + <File + RelativePath=".\PerfLog.cpp" + > + </File> + <File + RelativePath=".\posterror.cpp" + > + </File> + <File + RelativePath=".\PrettyPrintSig.cpp" + > + </File> + <File + RelativePath=".\rangetree.cpp" + > + </File> + <File + RelativePath=".\regutil.cpp" + > + </File> + <File + RelativePath=".\safewrap.cpp" + > + </File> + <File + RelativePath=".\sbuffer.cpp" + > + </File> + <File + RelativePath=".\SecurityUtil.cpp" + > + </File> + <File + RelativePath=".\securitywrapper.cpp" + > + </File> + <File + RelativePath=".\sigbuilder.cpp" + > + </File> + <File + RelativePath=".\sigparser.cpp" + > + </File> + <File + RelativePath=".\SortVersioning.cpp" + > + </File> + <File + RelativePath=".\SortVersioning.h" + > + </File> + <File + RelativePath=".\splitpat.cpp" + > + </File> + <File + RelativePath=".\sstring.cpp" + > + </File> + <File + RelativePath=".\sstring_com.cpp" + > + </File> + <File + RelativePath=".\stacktrace.cpp" + > + </File> + <File + RelativePath=".\stdafx.cpp" + > + </File> + <File + RelativePath=".\stdafx.h" + > + </File> + <File + RelativePath=".\stgpool.cpp" + > + </File> + <File + RelativePath=".\stgpooli.cpp" + > + </File> + <File + RelativePath=".\stgpoolreadonly.cpp" + > + </File> + <File + RelativePath=".\stresslog.cpp" + > + </File> + <File + RelativePath=".\TlbUtils.cpp" + > + </File> + <File + RelativePath=".\tls.cpp" + > + </File> + <File + RelativePath=".\util.cpp" + > + </File> + <File + RelativePath=".\utilmessagebox.cpp" + > + </File> + <File + RelativePath=".\UTSEM.cpp" + > + </File> + <File + RelativePath=".\winfix.cpp" + > + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/src/utilcode/UtilCode.vcxproj b/src/utilcode/UtilCode.vcxproj new file mode 100644 index 0000000000..d0926fd8c6 --- /dev/null +++ b/src/utilcode/UtilCode.vcxproj @@ -0,0 +1,447 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="amd64chk|Win32"> + <Configuration>amd64chk</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="amd64corechk|Win32"> + <Configuration>amd64corechk</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="amd64coredbg|Win32"> + <Configuration>amd64coredbg</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="amd64coreret|Win32"> + <Configuration>amd64coreret</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="amd64dbg|Win32"> + <Configuration>amd64dbg</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="amd64ret|Win32"> + <Configuration>amd64ret</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="armcorechk|Win32"> + <Configuration>armcorechk</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="armcoredbg|Win32"> + <Configuration>armcoredbg</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="armcoreret|Win32"> + <Configuration>armcoreret</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Mac.inteldbg|Win32"> + <Configuration>Mac.inteldbg</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="x86chk|Win32"> + <Configuration>x86chk</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="x86corechk|Win32"> + <Configuration>x86corechk</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="x86coredbg|Win32"> + <Configuration>x86coredbg</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="x86coreret|Win32"> + <Configuration>x86coreret</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="x86dbg|Win32"> + <Configuration>x86dbg</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="x86ret|Win32"> + <Configuration>x86ret</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{82B95BDA-224E-49B4-8BB7-03E7B9EEFF3B}</ProjectGuid> + <RootNamespace>utilcode</RootNamespace> + <SccProjectName>SAK</SccProjectName> + <SccAuxPath>SAK</SccAuxPath> + <SccLocalPath>SAK</SccLocalPath> + <SccProvider>SAK</SccProvider> + <Keyword>MakeFileProj</Keyword> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='armcoreret|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='armcorechk|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='armcoredbg|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64coreret|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64coredbg|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64corechk|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Mac.inteldbg|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86coreret|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86coredbg|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86corechk|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64ret|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64chk|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64dbg|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86ret|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86chk|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86dbg|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='armcoreret|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='armcorechk|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='armcoredbg|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='amd64coreret|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='amd64coredbg|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='amd64corechk|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Mac.inteldbg|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='x86coreret|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='x86coredbg|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='x86corechk|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='amd64ret|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='amd64chk|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='amd64dbg|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='x86ret|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='x86chk|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='x86dbg|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup> + <_ProjectFileVersion>12.0.20617.1</_ProjectFileVersion> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86dbg|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:dbg</NMakeBuildCommandLine> + <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:dbg</NMakeReBuildCommandLine> + <NMakeCleanCommandLine /> + <NMakeOutput>Example.exe</NMakeOutput> + <NMakePreprocessorDefinitions>_X86_=1;i386=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> + <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86chk|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:chk</NMakeBuildCommandLine> + <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:chk</NMakeReBuildCommandLine> + <NMakeCleanCommandLine /> + <NMakeOutput>Example.exe</NMakeOutput> + <NMakePreprocessorDefinitions>_X86_=1;i386=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> + <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86ret|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:ret</NMakeBuildCommandLine> + <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:ret</NMakeReBuildCommandLine> + <NMakeCleanCommandLine /> + <NMakeOutput>Example.exe</NMakeOutput> + <NMakePreprocessorDefinitions>_X86_=1;i386=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> + <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64dbg|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x64/buildType:dbg</NMakeBuildCommandLine> + <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x64/buildType:dbg</NMakeReBuildCommandLine> + <NMakeCleanCommandLine /> + <NMakeOutput>Example.exe</NMakeOutput> + <NMakePreprocessorDefinitions>_AMD64_=1;_WIN64=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> + <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64chk|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x64/buildType:chk</NMakeBuildCommandLine> + <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x64/buildType:chk</NMakeReBuildCommandLine> + <NMakeCleanCommandLine /> + <NMakeOutput>Example.exe</NMakeOutput> + <NMakePreprocessorDefinitions>_AMD64_=1;_WIN64=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> + <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64ret|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x64/buildType:ret</NMakeBuildCommandLine> + <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x64/buildType:ret</NMakeReBuildCommandLine> + <NMakeCleanCommandLine /> + <NMakeOutput>Example.exe</NMakeOutput> + <NMakePreprocessorDefinitions>_AMD64_=1;_WIN64=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> + <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86corechk|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:corechk</NMakeBuildCommandLine> + <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:corechk</NMakeReBuildCommandLine> + <NMakeCleanCommandLine /> + <NMakeOutput>Example.exe</NMakeOutput> + <NMakePreprocessorDefinitions>_X86_=1;i386=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> + <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86coredbg|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:coredbg</NMakeBuildCommandLine> + <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:coredbg</NMakeReBuildCommandLine> + <NMakeCleanCommandLine /> + <NMakeOutput>Example.exe</NMakeOutput> + <NMakePreprocessorDefinitions>_X86_=1;i386=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> + <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86coreret|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:coreret</NMakeBuildCommandLine> + <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:coreret</NMakeReBuildCommandLine> + <NMakeCleanCommandLine /> + <NMakeOutput>Example.exe</NMakeOutput> + <NMakePreprocessorDefinitions>_X86_=1;i386=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> + <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Mac.inteldbg|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:coredbg</NMakeBuildCommandLine> + <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:coredbg</NMakeReBuildCommandLine> + <NMakeCleanCommandLine /> + <NMakeOutput>Example.exe</NMakeOutput> + <NMakePreprocessorDefinitions>_X86_=1;i386=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> + <NMakeIncludeSearchPath>..\inc\;$(SolutionDir)\..\..\..\rotor\palrt\inc;$(SolutionDir)\..\..\..\rotor\pal\inc;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64corechk|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:amd64/buildType:corechk</NMakeBuildCommandLine> + <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:amd64/buildType:corechk</NMakeReBuildCommandLine> + <NMakeCleanCommandLine /> + <NMakeOutput>Example.exe</NMakeOutput> + <NMakePreprocessorDefinitions>_AMD64_=1;_WIN64=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> + <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64coredbg|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:amd64/buildType:coredbg</NMakeBuildCommandLine> + <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:amd64/buildType:coredbg</NMakeReBuildCommandLine> + <NMakeCleanCommandLine /> + <NMakeOutput>Example.exe</NMakeOutput> + <NMakePreprocessorDefinitions>_AMD64_=1;_WIN64=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> + <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64coreret|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:amd64/buildType:coreret</NMakeBuildCommandLine> + <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:amd64/buildType:coreret</NMakeReBuildCommandLine> + <NMakeCleanCommandLine /> + <NMakeOutput>Example.exe</NMakeOutput> + <NMakePreprocessorDefinitions>_AMD64_=1;_WIN64=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> + <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='armcoredbg|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:arm/buildType:coredbg</NMakeBuildCommandLine> + <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:arm/buildType:coredbg</NMakeReBuildCommandLine> + <NMakeCleanCommandLine /> + <NMakeOutput>Example.exe</NMakeOutput> + <NMakePreprocessorDefinitions>_ARM_;ARM;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> + <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='armcorechk|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:arm/buildType:corechk</NMakeBuildCommandLine> + <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:arm/buildType:corechk</NMakeReBuildCommandLine> + <NMakeCleanCommandLine /> + <NMakeOutput>Example.exe</NMakeOutput> + <NMakePreprocessorDefinitions>_ARM_;ARM;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> + <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='armcoreret|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:arm/buildType:coreret</NMakeBuildCommandLine> + <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:arm/buildType:coreret</NMakeReBuildCommandLine> + <NMakeCleanCommandLine /> + <NMakeOutput>Example.exe</NMakeOutput> + <NMakePreprocessorDefinitions>_ARM_;ARM;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> + <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <ItemDefinitionGroup> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="apithreadstress.cpp" /> + <ClCompile Include="arraylist.cpp" /> + <ClCompile Include="assemblyfilehash.cpp" /> + <ClCompile Include="bitvector.cpp" /> + <ClCompile Include="ccomprc.cpp" /> + <ClCompile Include="check.cpp" /> + <ClCompile Include="circularlog.cpp" /> + <ClCompile Include="CLRConfig.cpp" /> + <ClCompile Include="clrhost.cpp" /> + <ClCompile Include="COMEx.cpp" /> + <ClCompile Include="corimage.cpp" /> + <ClCompile Include="CycleTimer.cpp" /> + <ClCompile Include="dacutil.cpp" /> + <ClCompile Include="debug.cpp" /> + <ClCompile Include="dlwrap.cpp" /> + <ClCompile Include="downlevel.cpp" /> + <ClCompile Include="ex.cpp" /> + <ClCompile Include="format1.cpp" /> + <ClCompile Include="genericstackprobe.cpp" /> + <ClCompile Include="GuidFromName.cpp" /> + <ClCompile Include="hostimpl.cpp" /> + <ClCompile Include="IAllocator.cpp" /> + <ClCompile Include="ilFormatter.cpp" /> + <ClCompile Include="jitperf.cpp" /> + <ClCompile Include="loaderheap.cpp" /> + <ClCompile Include="lazycow.cpp" /> + <ClCompile Include="log.cpp" /> + <ClCompile Include="makepath.cpp" /> + <ClCompile Include="md5.cpp" /> + <ClCompile Include="memorypool.cpp" /> + <ClCompile Include="namespaceutil.cpp" /> + <ClCompile Include="newapis.cpp" /> + <ClCompile Include="pedecoder.cpp" /> + <ClCompile Include="PerfLog.cpp" /> + <ClCompile Include="posterror.cpp" /> + <ClCompile Include="PrettyPrintSig.cpp" /> + <ClCompile Include="rangetree.cpp" /> + <ClCompile Include="regutil.cpp" /> + <ClCompile Include="safewrap.cpp" /> + <ClCompile Include="sbuffer.cpp" /> + <ClCompile Include="SecurityUtil.cpp" /> + <ClCompile Include="securitywrapper.cpp" /> + <ClCompile Include="sigbuilder.cpp" /> + <ClCompile Include="sigparser.cpp" /> + <ClCompile Include="SortVersioning.cpp" /> + <ClCompile Include="splitpat.cpp" /> + <ClCompile Include="sstring.cpp" /> + <ClCompile Include="sstring_com.cpp" /> + <ClCompile Include="stacktrace.cpp" /> + <ClCompile Include="stdafx.cpp" /> + <ClCompile Include="stgpool.cpp" /> + <ClCompile Include="stgpooli.cpp" /> + <ClCompile Include="stgpoolreadonly.cpp" /> + <ClCompile Include="stresslog.cpp" /> + <ClCompile Include="tick.cpp" /> + <ClCompile Include="TlbUtils.cpp" /> + <ClCompile Include="tls.cpp" /> + <ClCompile Include="util.cpp" /> + <ClCompile Include="utilmessagebox.cpp" /> + <ClCompile Include="UTSEM.cpp" /> + <ClCompile Include="winfix.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="hostimpl.h" /> + <ClInclude Include="SortVersioning.h" /> + <ClInclude Include="stdafx.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/src/utilcode/UtilCode.vcxproj.vspscc b/src/utilcode/UtilCode.vcxproj.vspscc new file mode 100644 index 0000000000..b6d32892fd --- /dev/null +++ b/src/utilcode/UtilCode.vcxproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/src/utilcode/apithreadstress.cpp b/src/utilcode/apithreadstress.cpp new file mode 100644 index 0000000000..1dba1190f0 --- /dev/null +++ b/src/utilcode/apithreadstress.cpp @@ -0,0 +1,181 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// --------------------------------------------------------------------------- +// APIThreadStress.cpp (API thread stresser) +// --------------------------------------------------------------------------- + +#include "stdafx.h" + +#include "apithreadstress.h" +#include "clrhost.h" +#include "ex.h" +#include "log.h" + + + +// For now, thread stress is incompatible with hosting. We need a host CreateThread +// to fix this. +#undef SetEvent +#undef ResetEvent + +int APIThreadStress::s_threadStressCount = 0; + +APIThreadStress::APIThreadStress() +{ + WRAPPER_NO_CONTRACT; + + m_threadCount = 0; + + // Don't "fork" stress threads + if (ClrFlsGetValue(TlsIdx_StressThread) == NULL) + m_threadCount = s_threadStressCount; + + if (m_threadCount != 0) + { + m_setupOK = TRUE; + + m_hThreadArray = new (nothrow) HANDLE [ m_threadCount ]; + if (m_hThreadArray == NULL) + m_setupOK = FALSE; + else + { + HANDLE *p = m_hThreadArray; + HANDLE *pEnd = p + m_threadCount; + + while (p < pEnd) + { + DWORD id; + *p = ::CreateThread(NULL, 0, StartThread, this, 0, &id); + if (*p == NULL) + m_setupOK = FALSE; + p++; + } + } + + m_syncEvent = ClrCreateManualEvent(FALSE); + if (m_syncEvent == INVALID_HANDLE_VALUE) + m_setupOK = FALSE; + } +} + +APIThreadStress::~APIThreadStress() +{ + WRAPPER_NO_CONTRACT; + + if (m_threadCount > 0) + { + HANDLE *p = m_hThreadArray; + HANDLE *pEnd = p + m_threadCount; + + if (p != NULL) + { + while (p < pEnd) + { + if (*p != NULL) + { + if (m_threadCount > 0 && m_setupOK) + WaitForSingleObjectEx(*p, INFINITE, FALSE); + + ::CloseHandle(*p); + } + p++; + } + delete [] m_hThreadArray; + } + + if (m_syncEvent != INVALID_HANDLE_VALUE) + CloseHandle(m_syncEvent); + } +} + +void APIThreadStress::SetThreadStressCount(int threadCount) +{ + LIMITED_METHOD_CONTRACT; + + s_threadStressCount = threadCount; +} + + +DWORD WINAPI APIThreadStress::StartThread(void *arg) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_NOT_MAINLINE; // not mainline so scenario + + APIThreadStress *pThis = (APIThreadStress *) arg; + + ClrFlsSetValue(TlsIdx_StressThread, pThis); + + EX_TRY + { + // Perform initial synchronization + WaitForSingleObjectEx(pThis->m_syncEvent, INFINITE, FALSE); + InterlockedIncrement(&pThis->m_runCount); + + LOG((LF_ALL, LL_INFO100, "Stressing operation on thread %d\n", GetCurrentThreadId())); + ((APIThreadStress *)arg)->Invoke(); + LOG((LF_ALL, LL_INFO100, "End stress operation on thread %d\n", GetCurrentThreadId())); + + if (InterlockedDecrement(&pThis->m_runCount) == 0) + ::SetEvent(pThis->m_syncEvent); + } + EX_CATCH + { + LOG((LF_ALL, LL_ERROR, "Exception during stress operation on thread %d\n", GetCurrentThreadId())); + } + EX_END_CATCH(SwallowAllExceptions); + + return 0; +} + +BOOL APIThreadStress::DoThreadStress() +{ + WRAPPER_NO_CONTRACT; + + if (m_threadCount > 0 && m_setupOK) + { + HANDLE *p = m_hThreadArray; + HANDLE *pEnd = p + m_threadCount; + + while (p < pEnd) + { + ::ResumeThread(*p); + p++; + } + + // Start the threads at the same time + ::SetEvent(m_syncEvent); + + return TRUE; + } + else + { + SyncThreadStress(); + return FALSE; + } +} + +void APIThreadStress::SyncThreadStress() +{ + WRAPPER_NO_CONTRACT; + + APIThreadStress *pThis = (APIThreadStress *) ClrFlsGetValue(TlsIdx_StressThread); + + if (pThis != NULL) + { + LOG((LF_ALL, LL_INFO1000, "Syncing stress operation on thread %d\n", GetCurrentThreadId())); + + ::ResetEvent(pThis->m_syncEvent); + + if (InterlockedDecrement(&pThis->m_runCount) == 0) + ::SetEvent(pThis->m_syncEvent); + else + WaitForSingleObjectEx(pThis->m_syncEvent, INFINITE, FALSE); + InterlockedIncrement(&pThis->m_runCount); + + LOG((LF_ALL, LL_INFO1000, "Resuming stress operation on thread %d\n", GetCurrentThreadId())); + } +} + diff --git a/src/utilcode/appxutil.cpp b/src/utilcode/appxutil.cpp new file mode 100644 index 0000000000..538fa38637 --- /dev/null +++ b/src/utilcode/appxutil.cpp @@ -0,0 +1,866 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// + + +#include "stdafx.h" + +#include <strsafe.h> + +#include "utilcode.h" +#include "holder.h" +#include "volatile.h" +#include "clr/fs.h" +#include "clr/str.h" + +#include "appxutil.h" +#include "ex.h" + +#include "shlwapi.h" // Path manipulation APIs + +#ifdef FEATURE_CORECLR + +GVAL_IMPL(bool, g_fAppX); +INDEBUG(bool g_fIsAppXAsked;) + +namespace AppX +{ +#ifdef DACCESS_COMPILE + bool DacIsAppXProcess() + { + return g_fAppX; + } +#else + + // Returns true if host has deemed the process to be appx + bool IsAppXProcess() + { + INDEBUG(g_fIsAppXAsked = true;) + return g_fAppX; + } + + + void SetIsAppXProcess(bool value) + { + _ASSERTE(!g_fIsAppXAsked); + g_fAppX = value; + } +#endif +}; + +#else // FEATURE_CORECLR + +//--------------------------------------------------------------------------------------------- +// Convenience values + +#ifndef E_INSUFFICIENT_BUFFER + #define E_INSUFFICIENT_BUFFER (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) +#endif + +#ifndef E_FILE_NOT_FOUND + #define E_FILE_NOT_FOUND (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) +#endif + +//--------------------------------------------------------------------------------------------- +using clr::str::IsNullOrEmpty; + +//--------------------------------------------------------------------------------------------- +typedef decltype(GetCurrentPackageId) GetCurrentPackageId_t; +typedef decltype(GetCurrentPackageInfo) GetCurrentPackageInfo_t; +typedef decltype(GetCurrentPackagePath) GetCurrentPackagePath_t; +typedef decltype(OpenPackageInfoByFullName) OpenPackageInfoByFullName_t; +typedef decltype(ClosePackageInfo) ClosePackageInfo_t; +typedef decltype(GetPackageInfo) GetPackageInfo_t; + +//--------------------------------------------------------------------------------------------- +// Caches AppX ARI API-related information. +struct AppXRTInfo +{ + HMODULE m_hAppXRTMod; + bool m_fIsAppXProcess; + bool m_fIsAppXAdaptiveApp; + bool m_fIsAppXNGen; + + GetCurrentPackageId_t * m_pfnGetCurrentPackageId; + GetCurrentPackageInfo_t * m_pfnGetCurrentPackageInfo; + GetCurrentPackagePath_t * m_pfnGetCurrentPackagePath; + + NewArrayHolder<BYTE> m_pbAppContainerInfo; + DWORD m_cbAppContainerInfo; + + struct CurrentPackageInfo + { + UINT32 m_cbCurrentPackageInfo; + PBYTE m_pbCurrentPackageInfo; + UINT32 m_nCount; + + CurrentPackageInfo(UINT32 cbPkgInfo, PBYTE pbPkgInfo, UINT32 nCount) + : m_cbCurrentPackageInfo(cbPkgInfo) + , m_pbCurrentPackageInfo(pbPkgInfo) + , m_nCount(nCount) + { LIMITED_METHOD_CONTRACT; } + + ~CurrentPackageInfo() + { + LIMITED_METHOD_CONTRACT; + if (m_pbCurrentPackageInfo != nullptr) + { + delete [] m_pbCurrentPackageInfo; + m_pbCurrentPackageInfo = nullptr; + } + } + }; + + CurrentPackageInfo * m_pCurrentPackageInfo; + + NewArrayHolder<WCHAR> m_AdaptiveAppWinmetadataDir; + + AppXRTInfo() : + m_hAppXRTMod(nullptr), + m_fIsAppXProcess(false), + m_fIsAppXAdaptiveApp(false), + m_fIsAppXNGen(false), + m_pfnGetCurrentPackageId(nullptr), + m_pfnGetCurrentPackageInfo(nullptr), + m_pfnGetCurrentPackagePath(nullptr), + m_pbAppContainerInfo(nullptr), + m_pCurrentPackageInfo(nullptr), + m_AdaptiveAppWinmetadataDir(nullptr) + { LIMITED_METHOD_CONTRACT; } + + ~AppXRTInfo() + { + LIMITED_METHOD_CONTRACT; + if (m_pCurrentPackageInfo != nullptr) + { + delete m_pCurrentPackageInfo; + m_pCurrentPackageInfo = nullptr; + } + + if (m_hAppXRTMod != nullptr) + { + FreeLibrary(m_hAppXRTMod); + m_hAppXRTMod = nullptr; + } + } +}; // struct AppXRTInfo + +GPTR_IMPL(AppXRTInfo, g_pAppXRTInfo); // Relies on zero init static memory. + +#ifndef DACCESS_COMPILE + +//--------------------------------------------------------------------------------------------- +static +HRESULT GetAppContainerTokenInfoForProcess( + DWORD pid, + NewArrayHolder<BYTE>& pbAppContainerTokenInfo, + DWORD* pcbAppContainerTokenInfo) +{ + PRECONDITION(CheckPointer(pcbAppContainerTokenInfo, NULL_OK)); + + HRESULT hr = S_OK; + + pbAppContainerTokenInfo = nullptr; + + // In order to get the AppContainer SID we need to open the process token + HandleHolder hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); + if (hProcess == NULL) + return HRESULT_FROM_GetLastError(); + + HandleHolder hToken; + if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) + return HRESULT_FROM_GetLastError(); + + // Query the process to see if it's inside an AppContainer + ULONG isAppContainer = 0; + DWORD actualLength = 0; + if (!GetTokenInformation(hToken, static_cast<TOKEN_INFORMATION_CLASS>(TokenIsAppContainer), &isAppContainer, sizeof(isAppContainer), &actualLength)) + return HRESULT_FROM_GetLastError(); + + _ASSERTE(actualLength > 0); + + // Not an AppContainer so bail + if (!isAppContainer) + { + return S_FALSE; + } + + // Now we need the AppContainer SID so first get the required buffer length + actualLength = 0; + VERIFY(!GetTokenInformation(hToken, static_cast<TOKEN_INFORMATION_CLASS>(TokenAppContainerSid), NULL, 0, &actualLength)); + hr = HRESULT_FROM_GetLastError(); + _ASSERTE(hr == E_INSUFFICIENT_BUFFER); + + // Something unexpected happened + if (hr != E_INSUFFICIENT_BUFFER) + return hr; + + // Now we know the length of the AppContainer SID so create a buffer and retrieve it + pbAppContainerTokenInfo = new (nothrow) BYTE[actualLength]; + IfNullRet(pbAppContainerTokenInfo); + + if (!GetTokenInformation(hToken, static_cast<TOKEN_INFORMATION_CLASS>(TokenAppContainerSid), pbAppContainerTokenInfo.GetValue(), actualLength, &actualLength)) + return HRESULT_FROM_GetLastError(); + + if (pcbAppContainerTokenInfo != nullptr) + *pcbAppContainerTokenInfo = actualLength; + + return S_OK; +} + +//--------------------------------------------------------------------------------------------- +// Initializes a global AppXRTInfo structure if it has not already been initialized. Returns +// false only in the event of OOM; otherwise caches the result of ARI information in +// g_pAppXRTInfo. Thread safe. +static +HRESULT InitAppXRT() +{ + // This will be used for catastrophic errors. + HRESULT hr = S_OK; + + if (VolatileLoad(&g_pAppXRTInfo) == nullptr) + { + NewHolder<AppXRTInfo> pAppXRTInfo = new (nothrow) AppXRTInfo(); + IfNullRet(pAppXRTInfo); // Catastrophic error. + + pAppXRTInfo->m_fIsAppXProcess = false; + + do + { + LPCWSTR wzAppXRTDll = W("api-ms-win-appmodel-runtime-l1-1-0.dll"); + // Does not use GetLoadWithAlteredSearchPathFlag() because that would cause infinite recursion. + pAppXRTInfo->m_hAppXRTMod = WszLoadLibrary(wzAppXRTDll); + if (pAppXRTInfo->m_hAppXRTMod == nullptr) + { // Error is catastrophic: can't find kernel32.dll? + hr = HRESULT_FROM_GetLastError(); + break; + } + + pAppXRTInfo->m_pfnGetCurrentPackageId = reinterpret_cast<GetCurrentPackageId_t *>( + GetProcAddress(pAppXRTInfo->m_hAppXRTMod, "GetCurrentPackageId")); + if (pAppXRTInfo->m_pfnGetCurrentPackageId == nullptr) + { // Error is non-catastrophic: could be running downlevel + break; + } + + pAppXRTInfo->m_pfnGetCurrentPackageInfo = reinterpret_cast<GetCurrentPackageInfo_t *>( + GetProcAddress(pAppXRTInfo->m_hAppXRTMod, "GetCurrentPackageInfo")); + if (pAppXRTInfo->m_pfnGetCurrentPackageInfo == nullptr) + { // Error is catastrophic: GetCurrentPackageId is available but not GetCurrentPackageInfo? + hr = HRESULT_FROM_GetLastError(); + break; + } + + pAppXRTInfo->m_pfnGetCurrentPackagePath = reinterpret_cast<GetCurrentPackagePath_t *>( + GetProcAddress(pAppXRTInfo->m_hAppXRTMod, "GetCurrentPackagePath")); + if (pAppXRTInfo->m_pfnGetCurrentPackagePath == nullptr) + { // Error is catastrophic: GetCurrentPackageInfo is available but not GetCurrentPackagePath? + hr = HRESULT_FROM_GetLastError(); + break; + } + + // Determine if this is an AppX process + UINT32 cbBuffer = 0; + LONG lRes = (*pAppXRTInfo->m_pfnGetCurrentPackageId)(&cbBuffer, nullptr); + pAppXRTInfo->m_fIsAppXProcess = (lRes == ERROR_INSUFFICIENT_BUFFER); + + _ASSERTE(AppX::IsAppXSupported()); + + hr = GetAppContainerTokenInfoForProcess( + GetCurrentProcessId(), + pAppXRTInfo->m_pbAppContainerInfo, + &pAppXRTInfo->m_cbAppContainerInfo); + + if (FAILED(hr)) + { + if (pAppXRTInfo->m_fIsAppXProcess) + { // Error is catastrophic: running in true immersive process but no token info? + } + else + { // Error is non-catastrophic: reset HRESULT to S_OK. + hr = S_OK; + } + break; + } + } + while (false); + + if (InterlockedCompareExchangeT<AppXRTInfo>(&g_pAppXRTInfo, pAppXRTInfo, nullptr) == nullptr) + { + pAppXRTInfo.SuppressRelease(); + } + } + + return hr; +} + +//--------------------------------------------------------------------------------------------- +// Inline helper to check first an only init when required. +static inline HRESULT CheckInitAppXRT() +{ + return (VolatileLoad(&g_pAppXRTInfo) != nullptr) ? S_OK : InitAppXRT(); +} + +#endif // !DACCESS_COMPILE + +//--------------------------------------------------------------------------------------------- +// Contains general helper methods for interacting with AppX functionality. This code will +// gracefully fail on downlevel OS by returning false from AppX::IsAppXProcess, so always +// call this API first to check before calling any of the others defined in this namespace. +// +// See http://windows/windows8/docs/Windows%208%20Feature%20Documents/Developer%20Experience%20(DEVX)/Apps%20Experience%20(APPX)/Modern%20Client/App%20Runtime%20Improvements%20API%20Developer%20Platform%20Spec.docm +// for more information. + +namespace AppX +{ +#ifdef DACCESS_COMPILE + + //----------------------------------------------------------------------------------------- + // DAC-only IsAppXProcess. Returns false if g_pAppXRTInfo has not been initialized. + bool DacIsAppXProcess() + { + LIMITED_METHOD_DAC_CONTRACT; + return (g_pAppXRTInfo != nullptr && g_pAppXRTInfo->m_fIsAppXProcess); + } + +#else // DACCESS_COMPILE + + //--------------------------------------------------------------------------------------------- + // cleans up resources allocated in InitAppXRT() + void ShutDown() + { + if (VolatileLoad(&g_pAppXRTInfo) != nullptr) + { + delete g_pAppXRTInfo; + g_pAppXRTInfo = nullptr; + } + } + + //----------------------------------------------------------------------------------------- + // Returns true if the current process is immersive. + // NOTE: a return value of true doesn't necessarily indicate that the process is a + // real Metro app, e.g. it could be an ngen process compiling an AppX assembly. + bool IsAppXProcess() + { + LIMITED_METHOD_CONTRACT; + HRESULT hr = S_OK; + + if (FAILED(hr = CheckInitAppXRT())) + { + SetLastError(hr); // HRESULT_FROM_WIN32 is idempotent when error value is HRESULT. + return false; + } + + return g_pAppXRTInfo->m_fIsAppXProcess; + } + + //----------------------------------------------------------------------------------------- + // Returns true if the current process is immersive. + // Only produces reliable results after IsAppXProcess is inititalized + bool IsAppXProcess_Initialized_NoFault() + { + LIMITED_METHOD_CONTRACT; + if (VolatileLoad(&g_pAppXRTInfo) == nullptr) + { + return false; + } + return g_pAppXRTInfo->m_fIsAppXProcess; + } + + bool IsAppXNGen() + { + LIMITED_METHOD_CONTRACT; + return VolatileLoad(&g_pAppXRTInfo) != nullptr && g_pAppXRTInfo->m_fIsAppXNGen; + } + + //----------------------------------------------------------------------------------------- + HRESULT InitCurrentPackageInfoCache() + { + LIMITED_METHOD_CONTRACT; + HRESULT hr = S_OK; + + UINT32 cbBuffer = 0; + hr = HRESULT_FROM_WIN32((*g_pAppXRTInfo->m_pfnGetCurrentPackageInfo)(PACKAGE_FILTER_CLR_DEFAULT, &cbBuffer, nullptr, nullptr)); + if (hr != E_INSUFFICIENT_BUFFER) + return hr; + + NewArrayHolder<BYTE> pbBuffer(new (nothrow) BYTE[cbBuffer]); + IfNullRet(pbBuffer); + + UINT32 nCount = 0; + IfFailRet(HRESULT_FROM_WIN32((*g_pAppXRTInfo->m_pfnGetCurrentPackageInfo)(PACKAGE_FILTER_CLR_DEFAULT, &cbBuffer, pbBuffer, &nCount))); + + NewHolder<AppXRTInfo::CurrentPackageInfo> pPkgInfo( + new (nothrow) AppXRTInfo::CurrentPackageInfo(cbBuffer, pbBuffer.Extract(), nCount)); + IfNullRet(pPkgInfo); + + if (InterlockedCompareExchangeT<AppXRTInfo::CurrentPackageInfo>( + &g_pAppXRTInfo->m_pCurrentPackageInfo, pPkgInfo, nullptr) == nullptr) + { + pPkgInfo.SuppressRelease(); + } + + return S_OK; + } + + //----------------------------------------------------------------------------------------- + FORCEINLINE HRESULT CheckInitCurrentPackageInfoCache() + { + WRAPPER_NO_CONTRACT; + PRECONDITION(IsAppXProcess()); + + if (!IsAppXProcess()) + return E_UNEXPECTED; + + if (g_pAppXRTInfo->m_pCurrentPackageInfo == nullptr) + return InitCurrentPackageInfoCache(); + else + return S_OK; + } + + //----------------------------------------------------------------------------------------- + LPCWSTR GetHeadPackageMoniker() + { + STANDARD_VM_CONTRACT; + + IfFailThrow(CheckInitCurrentPackageInfoCache()); + return reinterpret_cast<PPACKAGE_INFO>( + g_pAppXRTInfo->m_pCurrentPackageInfo->m_pbCurrentPackageInfo)->packageFullName; + } + + //----------------------------------------------------------------------------------------- + // Returns the current process' PACKAGE_ID in the provided buffer. See the ARI spec (above) + // for more information. + HRESULT GetCurrentPackageId( + PUINT32 pBufferLength, + PBYTE pBuffer) + { + LIMITED_METHOD_CONTRACT; + HRESULT hr = S_OK; + + IfFailRet(CheckInitAppXRT()); + IfFailRet(HRESULT_FROM_WIN32((*g_pAppXRTInfo->m_pfnGetCurrentPackageId)(pBufferLength, pBuffer))); + + return S_OK; + } + + //----------------------------------------------------------------------------------------- + // Returns the current process' PACKAGE_INFO in the provided buffer. See the ARI spec + // (above) for more information. + HRESULT GetCurrentPackageInfo( + UINT32 uiFlags, + PUINT32 pcbBuffer, + PBYTE pbBuffer, + PUINT32 pnCount) + { + LIMITED_METHOD_CONTRACT; + PRECONDITION(IsAppXProcess()); + PRECONDITION(CheckPointer(pcbBuffer)); + + HRESULT hr = S_OK; + + IfFailRet(CheckInitAppXRT()); + + if (pcbBuffer == nullptr) + return E_INVALIDARG; + + if (uiFlags == PACKAGE_FILTER_CLR_DEFAULT) + { + IfFailRet(CheckInitCurrentPackageInfoCache()); + + DWORD cbBuffer = *pcbBuffer; + *pcbBuffer = g_pAppXRTInfo->m_pCurrentPackageInfo->m_cbCurrentPackageInfo; + if (pnCount != nullptr) + { + *pnCount = g_pAppXRTInfo->m_pCurrentPackageInfo->m_nCount; + } + + if (pbBuffer == nullptr || cbBuffer < g_pAppXRTInfo->m_pCurrentPackageInfo->m_cbCurrentPackageInfo) + { + return E_INSUFFICIENT_BUFFER; + } + memcpy(pbBuffer, g_pAppXRTInfo->m_pCurrentPackageInfo->m_pbCurrentPackageInfo, g_pAppXRTInfo->m_pCurrentPackageInfo->m_cbCurrentPackageInfo); + } + else + { + IfFailRet(HRESULT_FROM_WIN32((*g_pAppXRTInfo->m_pfnGetCurrentPackageInfo)(uiFlags, pcbBuffer, pbBuffer, pnCount))); + } + + return S_OK; + } + + //----------------------------------------------------------------------------------------- + bool IsAdaptiveApp() + { + LIMITED_METHOD_CONTRACT; + + HRESULT hr = S_OK; + static bool cachedIsAdaptiveApp = false; + + if (!IsAppXProcess()) + { + return false; + } + + if (!cachedIsAdaptiveApp) + { + cachedIsAdaptiveApp = true; + LPWSTR adaptiveAppWinmetaDataDir = NULL; + + if (SUCCEEDED(hr = AppX::GetWinMetadataDirForAdaptiveApps(&adaptiveAppWinmetaDataDir))) + { + g_pAppXRTInfo->m_fIsAppXAdaptiveApp = clr::fs::Dir::Exists(adaptiveAppWinmetaDataDir); + + } + else + { + SetLastError(hr); + g_pAppXRTInfo->m_fIsAppXAdaptiveApp = false; + } + + } + + return g_pAppXRTInfo->m_fIsAppXAdaptiveApp; + } + + + //----------------------------------------------------------------------------------------- + // length : Upon success, contains the the length of packagePath + // [in/out] + // + // packageRoot : Upon success, contains the full packagePath + // [out] [ Note: The memory has to be preallocated for the above length] + // + HRESULT GetCurrentPackagePath(_Inout_ UINT32* length, _Out_opt_ PWSTR packageRoot) + { + PRECONDITION(IsAppXProcess()); + PRECONDITION(CheckPointer(length)); + + HRESULT hr; + IfFailRet(CheckInitAppXRT()); + + IfFailRet(HRESULT_FROM_WIN32((*g_pAppXRTInfo->m_pfnGetCurrentPackagePath)(length, packageRoot))); + return S_OK; + } + + //----------------------------------------------------------------------------------------- + // winMetadDataDir : Upon success, contains the absolute path for the winmetadata directory for the adaptive app + // [out] NOTE: The string the pointer points to is global memory and never should be modified + // + HRESULT GetWinMetadataDirForAdaptiveApps(_Out_ LPWSTR* winMetadDataDir) + { + + PRECONDITION(IsAppXProcess()); + + HRESULT hr; + + IfFailRet(CheckInitAppXRT()); + + if (g_pAppXRTInfo->m_AdaptiveAppWinmetadataDir == nullptr) + { + LPCWSTR wzWinMetadataFolder=W("\\WinMetadata"); + NewArrayHolder<WCHAR> wzCompletePath; + UINT32 length=0; + + hr = GetCurrentPackagePath(&length, NULL); + if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) + return hr; + + NewArrayHolder<WCHAR> wzPath_holder = new (nothrow) WCHAR[length]; + + IfNullRet(wzPath_holder); + IfFailRet(GetCurrentPackagePath(&length, wzPath_holder.GetValue())); + + DWORD cchFullPathBuf = length + (DWORD)wcslen(wzWinMetadataFolder) + 1; + IfNullRet(wzCompletePath = new (nothrow) WCHAR[cchFullPathBuf]); + IfFailRet(clr::fs::Path::Combine(wzPath_holder.GetValue(), wzWinMetadataFolder, &cchFullPathBuf, wzCompletePath.GetValue())); + g_pAppXRTInfo->m_AdaptiveAppWinmetadataDir = wzCompletePath.Extract(); + } + + *winMetadDataDir = g_pAppXRTInfo->m_AdaptiveAppWinmetadataDir; + + return S_OK; + } + +#if defined(FEATURE_APPX_BINDER) + //----------------------------------------------------------------------------------------- + // Iterates the current process' packages and returns the first package that contains a + // file matching wzFileName. + // + // wzFileName : The file to look for in the current process' packages. If this is a + // [in] relative path, then it appends the path to the root of each package in + // sequence to see if the resulting path points to an existing file. If this + // is an absolute path, then for each package it determines if the package + // root is a prefix of wzFileName. + // pcchPathName : Upon entry contains the length of the buffer pwzPathName, and upon return + // [in/out] contains either the number of characters written or the buffer size + // required. + // pwzPathName : Upon success, contains the absolute path for the first matching file. + // [out] + HRESULT FindFileInCurrentPackage( + PCWSTR wzFileName, + PUINT32 pcchPathName, + PWSTR pwzPathName, + UINT32 uiFlags, + __in PCWSTR *rgwzAltPaths, + __in UINT32 cAltPaths, + FindFindInPackageFlags findInCurrentPackageFlags) + { + LIMITED_METHOD_CONTRACT; + PRECONDITION(IsAppXProcess()); + PRECONDITION(CheckPointer(wzFileName)); + PRECONDITION(CheckPointer(pcchPathName)); + PRECONDITION(CheckPointer(pwzPathName)); + + HRESULT hr = S_OK; + + if (!IsAppXProcess()) + return E_UNEXPECTED; + + // PACKAGE_FILTER_ALL_LOADED is obsolete, and shouldn't be used. + // We also don't currently handle the case where PACKAGE_FILTER_HEAD isn't set. + _ASSERTE(uiFlags != PACKAGE_FILTER_ALL_LOADED && (uiFlags & PACKAGE_FILTER_HEAD) != 0); + if (uiFlags == PACKAGE_FILTER_ALL_LOADED || (uiFlags & PACKAGE_FILTER_HEAD) == 0) + return E_NOTIMPL; + + // File name must be non-null and relative + if (IsNullOrEmpty(wzFileName) || pcchPathName == nullptr || pwzPathName == nullptr) + return E_INVALIDARG; + + // If we've been provided a full path and the file doesn't actually exist + // then we can immediately say that this function will fail with "file not found". + bool fIsRelative = clr::fs::Path::IsRelative(wzFileName); + if (!fIsRelative && !clr::fs::File::Exists(wzFileName)) + return E_FILE_NOT_FOUND; + + IfFailRet(CheckInitCurrentPackageInfoCache()); + + DWORD const cchFileName = static_cast<DWORD>(wcslen(wzFileName)); + DWORD cchFullPathBuf = _MAX_PATH; + NewArrayHolder<WCHAR> wzFullPathBuf = new (nothrow) WCHAR[cchFullPathBuf]; + IfNullRet(wzFullPathBuf); + + auto FindFileInCurrentPackageHelper = [&](LPCWSTR wzPath) -> HRESULT + { + HRESULT hr = S_OK; + + if (!(findInCurrentPackageFlags & FindFindInPackageFlags_AllowLongFormatPath) && clr::fs::Path::HasLongFormatPrefix(wzPath)) + return COR_E_BAD_PATHNAME; // We can't handle long format paths. + + // If the path is relative, concatenate the package root and the file name and see + // if the file exists. + if (fIsRelative) + { + DWORD cchFullPath = cchFullPathBuf; + hr = clr::fs::Path::Combine(wzPath, wzFileName, &cchFullPath, wzFullPathBuf); + if (hr == E_INSUFFICIENT_BUFFER) + { + IfNullRet(wzFullPathBuf = new (nothrow) WCHAR[cchFullPathBuf = (cchFullPath + 1)]); + hr = clr::fs::Path::Combine(wzPath, wzFileName, &cchFullPath, wzFullPathBuf); + } + IfFailRet(hr); + + if (!clr::fs::Path::IsValid(wzFullPathBuf, cchFullPath, !!(findInCurrentPackageFlags & FindFindInPackageFlags_AllowLongFormatPath))) + return COR_E_BAD_PATHNAME; + + if (clr::fs::File::Exists(wzFullPathBuf)) + { + DWORD cchPathName = *pcchPathName; + *pcchPathName = cchFullPath; + return StringCchCopy(pwzPathName, cchPathName, wzFullPathBuf); + } + } + // If the path is absolute, see if the file name contains the pacakge root as a prefix + // and if the file exists. + else + { + DWORD cchPath = static_cast<DWORD>(wcslen(wzPath)); + + // Determine if wzPath is a path prefix of wzFileName + if (cchPath < cchFileName && + _wcsnicmp(wzPath, wzFileName, cchPath) == 0 && + (wzFileName[cchPath] == W('\\') || wzPath[cchPath-1] == W('\\'))) // Ensure wzPath is not just a prefix, but a path prefix + { + if (clr::fs::File::Exists(wzFileName)) + { + DWORD cchPathName = *pcchPathName; + *pcchPathName = cchFileName; + return StringCchCopy(pwzPathName, cchPathName, wzFileName); + } + } + } + + return S_FALSE; + }; // FindFileInCurrentPackageHelper + + if (!(findInCurrentPackageFlags & FindFindInPackageFlags_SkipCurrentPackageGraph)) + { + PCPACKAGE_INFO pCurNode = reinterpret_cast<PPACKAGE_INFO>( + g_pAppXRTInfo->m_pCurrentPackageInfo->m_pbCurrentPackageInfo); + PCPACKAGE_INFO pEndNode = pCurNode + g_pAppXRTInfo->m_pCurrentPackageInfo->m_nCount; + for (; pCurNode != pEndNode; ++pCurNode) + { + IfFailRet(FindFileInCurrentPackageHelper(pCurNode->path)); + + if (hr == S_OK) + { + return hr; + } + + // End search if dependent packages should not be checked. + if ((uiFlags & PACKAGE_FILTER_DIRECT) == 0) + { + break; + } + } + } + + // Process alternative paths + for (UINT iAltPath = 0; iAltPath < cAltPaths; iAltPath++) + { + IfFailRet(FindFileInCurrentPackageHelper(rgwzAltPaths[iAltPath])); + + if (hr == S_OK) + { + return hr; + } + } + + return E_FILE_NOT_FOUND; + } +#endif // FEATURE_APPX_BINDER + + //----------------------------------------------------------------------------------------- + HRESULT GetAppContainerTokenInfoForProcess( + DWORD dwPid, + NewArrayHolder<BYTE>& pbAppContainerTokenInfo, + DWORD* pcbAppContainerTokenInfo) + { + LIMITED_METHOD_CONTRACT; + + HRESULT hr = S_OK; + + pbAppContainerTokenInfo = nullptr; + + if (!IsAppXSupported()) + { + return S_FALSE; + } + + if (dwPid == GetCurrentProcessId()) + { + if (FAILED(hr = CheckInitAppXRT())) + { + SetLastError(hr); + return S_FALSE; + } + + if (g_pAppXRTInfo->m_pbAppContainerInfo == nullptr) + { + return S_FALSE; + } + else + { + pbAppContainerTokenInfo = g_pAppXRTInfo->m_pbAppContainerInfo.GetValue(); + pbAppContainerTokenInfo.SuppressRelease(); // Caller does not need to free mem. + if (pcbAppContainerTokenInfo != nullptr) + { + *pcbAppContainerTokenInfo = g_pAppXRTInfo->m_cbAppContainerInfo; + } + return S_OK; + } + } + else + { + return ::GetAppContainerTokenInfoForProcess(dwPid, pbAppContainerTokenInfo, pcbAppContainerTokenInfo); + } + } + + // Called during NGen to pretend that we're in a certain package. Due to Windows restriction, we can't + // start NGen worker processes in the right package environment (only in the right AppContainer). So + // NGen calls this function with a package name, to indicate that all AppX-related code should behave as + // if the current process is running in that package. + HRESULT SetCurrentPackageForNGen(__in PCWSTR pszPackageFullName) + { + LIMITED_METHOD_CONTRACT; + + HRESULT hr = S_OK; + + IfFailRet(CheckInitAppXRT()); + + _ASSERTE(IsAppXSupported()); + _ASSERTE(g_pAppXRTInfo->m_hAppXRTMod != nullptr); + + HMODULE hAppXRTMod = g_pAppXRTInfo->m_hAppXRTMod; + OpenPackageInfoByFullName_t *pfnOpenPackageInfoByFullName + = reinterpret_cast<OpenPackageInfoByFullName_t*>(GetProcAddress(hAppXRTMod, "OpenPackageInfoByFullName")); + _ASSERTE(pfnOpenPackageInfoByFullName != nullptr); + if (pfnOpenPackageInfoByFullName == nullptr) + return HRESULT_FROM_GetLastError(); + + ClosePackageInfo_t *pfnClosePackageInfo + = reinterpret_cast<ClosePackageInfo_t*>(GetProcAddress(hAppXRTMod, "ClosePackageInfo")); + _ASSERTE(pfnClosePackageInfo != nullptr); + if (pfnClosePackageInfo == nullptr) + return HRESULT_FROM_GetLastError(); + + GetPackageInfo_t *pfnGetPackageInfo + = reinterpret_cast<GetPackageInfo_t*>(GetProcAddress(hAppXRTMod, "GetPackageInfo")); + _ASSERTE(pfnGetPackageInfo != nullptr); + if (pfnGetPackageInfo == nullptr) + return HRESULT_FROM_GetLastError(); + + PACKAGE_INFO_REFERENCE packageInfoReference; + hr = HRESULT_FROM_WIN32(pfnOpenPackageInfoByFullName(pszPackageFullName, 0, &packageInfoReference)); + if (FAILED(hr)) + return hr; + + // Automatically close packageInfoReference before we return. + class PackageInfoReferenceHolder + { + public: + PackageInfoReferenceHolder(ClosePackageInfo_t *pfnClosePackageInfo, PACKAGE_INFO_REFERENCE &packageInfoReference) + :m_pfnClosePackageInfo(pfnClosePackageInfo), + m_packageInfoReference(packageInfoReference) + { + } + ~PackageInfoReferenceHolder() { m_pfnClosePackageInfo(m_packageInfoReference); } + private: + ClosePackageInfo_t *m_pfnClosePackageInfo; + PACKAGE_INFO_REFERENCE &m_packageInfoReference; + } pirh(pfnClosePackageInfo, packageInfoReference); + + UINT32 cbBuffer = 0; + hr = HRESULT_FROM_WIN32(pfnGetPackageInfo(packageInfoReference, PACKAGE_FILTER_CLR_DEFAULT, &cbBuffer, nullptr, nullptr)); + if (hr != E_INSUFFICIENT_BUFFER) + return hr; + + NewArrayHolder<BYTE> pbBuffer(new (nothrow) BYTE[cbBuffer]); + IfNullRet(pbBuffer); + + UINT32 nCount = 0; + IfFailRet(HRESULT_FROM_WIN32(pfnGetPackageInfo(packageInfoReference, PACKAGE_FILTER_CLR_DEFAULT, &cbBuffer, pbBuffer, &nCount))); + + NewHolder<AppXRTInfo::CurrentPackageInfo> pPkgInfo( + new (nothrow) AppXRTInfo::CurrentPackageInfo(cbBuffer, pbBuffer.Extract(), nCount)); + IfNullRet(pPkgInfo); + + if (InterlockedCompareExchangeT<AppXRTInfo::CurrentPackageInfo>( + &g_pAppXRTInfo->m_pCurrentPackageInfo, pPkgInfo, nullptr) == nullptr) + { + pPkgInfo.SuppressRelease(); + } + + g_pAppXRTInfo->m_fIsAppXProcess = true; + g_pAppXRTInfo->m_fIsAppXNGen = true; + + return hr; + } + +#endif // DACCESS_COMPILE +} // namespace AppX + + +#endif // FEATURE_CORECLR diff --git a/src/utilcode/arraylist.cpp b/src/utilcode/arraylist.cpp new file mode 100644 index 0000000000..801ab98f54 --- /dev/null +++ b/src/utilcode/arraylist.cpp @@ -0,0 +1,311 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#include "stdafx.h" + +#include "arraylist.h" +#include "utilcode.h" +#include "ex.h" + +// +// ArrayList is a simple class which is used to contain a growable +// list of pointers, stored in chunks. Modification is by appending +// only currently. Access is by index (efficient if the number of +// elements stays small) and iteration (efficient in all cases). +// +// An important property of an ArrayList is that the list remains +// coherent while it is being modified (appended to). This means that readers +// never need to lock when accessing it. (Locking is necessary among multiple +// writers, however.) +// + +void ArrayListBase::Clear() +{ + CONTRACTL + { + NOTHROW; + FORBID_FAULT; + } + CONTRACTL_END + + ArrayListBlock *block = m_firstBlock.m_next; + while (block != NULL) + { + ArrayListBlock *next = block->m_next; + delete [] block; + block = next; + } + m_firstBlock.m_next = 0; + m_count = 0; +} + +PTR_VOID * ArrayListBase::GetPtr(DWORD index) const +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_SO_TOLERANT; + STATIC_CONTRACT_CANNOT_TAKE_LOCK; + SUPPORTS_DAC; + + _ASSERTE(index < m_count); + + ArrayListBlock *b = (ArrayListBlock*)&m_firstBlock; + while (index >= b->m_blockSize) + { + PREFIX_ASSUME(b->m_next != NULL); + index -= b->m_blockSize; + b = b->m_next; + } + + return b->m_array + index; +} + +#ifndef DACCESS_COMPILE + +HRESULT ArrayListBase::Append(void *element) +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + ArrayListBlock *b = (ArrayListBlock*)&m_firstBlock; + DWORD count = m_count; + + while (count >= b->m_blockSize) + { + count -= b->m_blockSize; + + if (b->m_next == NULL) + { + _ASSERTE(count == 0); + + DWORD nextSize = b->m_blockSize * 2; + + ArrayListBlock *bNew = (ArrayListBlock *) + new (nothrow) BYTE [sizeof(ArrayListBlock) + nextSize * sizeof(void*)]; + + if (bNew == NULL) + return E_OUTOFMEMORY; + + bNew->m_next = NULL; + bNew->m_blockSize = nextSize; + + b->m_next = bNew; + } + + b = b->m_next; + } + + b->m_array[count] = element; + + m_count++; + + return S_OK; +} + +#endif // #ifndef DACCESS_COMPILE + +DWORD ArrayListBase::FindElement(DWORD start, PTR_VOID element) const +{ + CONTRACTL + { + NOTHROW; + FORBID_FAULT; + } + CONTRACTL_END + + DWORD index = start; + + _ASSERTE(index <= m_count); + + ArrayListBlock *b = (ArrayListBlock*)&m_firstBlock; + + // + // Skip to the block containing start. + // index should be the index of start in the block. + // + + while (b != NULL && index >= b->m_blockSize) + { + index -= b->m_blockSize; + b = b->m_next; + } + + // + // Adjust start to be the index of the start of the block + // + + start -= index; + + // + // Compute max number of entries from the start of the block + // + + DWORD max = m_count - start; + + while (b != NULL) + { + // + // Compute end of search in this block - either end of the block + // or end of the array + // + + DWORD blockMax; + if (max < b->m_blockSize) + blockMax = max; + else + blockMax = b->m_blockSize; + + // + // Scan for element, until the end. + // + + while (index < blockMax) + { + if (b->m_array[index] == element) + return start + index; + index++; + } + + // + // Otherwise, increment block start index, decrement max count, + // reset index, and go to the next block (if any) + // + + start += b->m_blockSize; + max -= b->m_blockSize; + index = 0; + b = b->m_next; + } + + return (DWORD) NOT_FOUND; +} + +BOOL ArrayListBase::Iterator::Next() +{ + LIMITED_METHOD_DAC_CONTRACT; + + ++m_index; + + if (m_index >= m_remaining) + return FALSE; + + if (m_index >= m_block->m_blockSize) + { + m_remaining -= m_block->m_blockSize; + m_index -= m_block->m_blockSize; + m_total += m_block->m_blockSize; + m_block = m_block->m_next; + } + + return TRUE; +} + +#ifdef DACCESS_COMPILE + +void +ArrayListBase::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + + // Assume that 'this' is enumerated, either explicitly + // or because this class is embedded in another. + + PTR_ArrayListBlock block = m_firstBlock.m_next; + while (block.IsValid()) + { + block.EnumMem(); + block = block->m_next; + } +} + +#endif // #ifdef DACCESS_COMPILE + + +void StructArrayListBase::Destruct (FreeProc *pfnFree) +{ + WRAPPER_NO_CONTRACT; + + StructArrayListEntryBase *pList = m_pChunkListHead; + while (pList) + { + StructArrayListEntryBase *pTrash = pList; + pList = pList->pNext; + pfnFree(this, pTrash); + } +} + +// Copied from jit.h +inline +size_t roundUp(size_t size, size_t mult = sizeof(size_t)) +{ + assert(mult && ((mult & (mult-1)) == 0)); // power of two test + + return (size + (mult - 1)) & ~(mult - 1); +} + +void StructArrayListBase::CreateNewChunk (SIZE_T InitialChunkLength, SIZE_T ChunkLengthGrowthFactor, SIZE_T cbElement, AllocProc *pfnAlloc, SIZE_T alignment) +{ + CONTRACTL { + THROWS; + PRECONDITION(!m_pChunkListHead || m_nItemsInLastChunk == m_nLastChunkCapacity); + } CONTRACTL_END; + + SIZE_T nChunkCapacity; + if (!m_pChunkListHead) + nChunkCapacity = InitialChunkLength; + else + nChunkCapacity = m_nLastChunkCapacity * ChunkLengthGrowthFactor; + + S_SIZE_T cbBaseSize = S_SIZE_T(roundUp(sizeof(StructArrayListEntryBase), alignment)); + S_SIZE_T cbChunk = cbBaseSize + + S_SIZE_T(cbElement) * S_SIZE_T(nChunkCapacity); + _ASSERTE(!cbChunk.IsOverflow()); + if(cbChunk.IsOverflow()) + { + ThrowWin32(ERROR_ARITHMETIC_OVERFLOW); + } + + StructArrayListEntryBase *pNewChunk = (StructArrayListEntryBase*)pfnAlloc(this, cbChunk.Value()); + + if (m_pChunkListTail) + { + _ASSERTE(m_pChunkListHead); + m_pChunkListTail->pNext = pNewChunk; + } + else + { + _ASSERTE(!m_pChunkListHead); + m_pChunkListHead = pNewChunk; + } + + pNewChunk->pNext = NULL; + m_pChunkListTail = pNewChunk; + + m_nItemsInLastChunk = 0; + m_nLastChunkCapacity = nChunkCapacity; +} + + +void StructArrayListBase::ArrayIteratorBase::SetCurrentChunk (StructArrayListEntryBase *pChunk, SIZE_T nChunkCapacity) +{ + LIMITED_METHOD_CONTRACT; + + m_pCurrentChunk = pChunk; + + if (pChunk) + { + if (pChunk == m_pArrayList->m_pChunkListTail) + m_nItemsInCurrentChunk = m_pArrayList->m_nItemsInLastChunk; + else + m_nItemsInCurrentChunk = nChunkCapacity; + + m_nCurrentChunkCapacity = nChunkCapacity; + } +} + diff --git a/src/utilcode/assemblyfilehash.cpp b/src/utilcode/assemblyfilehash.cpp new file mode 100644 index 0000000000..be6d985e8f --- /dev/null +++ b/src/utilcode/assemblyfilehash.cpp @@ -0,0 +1,170 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + + +#include "stdafx.h" +#include <stdlib.h> +#include "utilcode.h" +#include "strongname.h" +#include "assemblyfilehash.h" +#include "ex.h" +#include "corperm.h" + +#include <wincrypt.h> + +HRESULT AssemblyFileHash::ReadData() +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + INJECT_FAULT(return E_OUTOFMEMORY;); +#ifdef MODE_PREEMPTIVE + MODE_PREEMPTIVE; +#endif + } + CONTRACTL_END; + NewArrayHolder<BYTE> pBuffer; + + DWORD dwFileSize = SafeGetFileSize( m_hFile, NULL ); + + if (dwFileSize == 0xffffffff) + return HRESULT_FROM_GetLastError(); + + pBuffer = new (nothrow)BYTE[dwFileSize]; + if(pBuffer==NULL) + return E_OUTOFMEMORY; + + DWORD cbBuffer = dwFileSize; + DWORD cbRead; + + if (!ReadFile( m_hFile, pBuffer, cbBuffer, &cbRead, NULL ) || + cbRead != cbBuffer) + return HRESULT_FROM_GetLastError(); + + pBuffer.SuppressRelease(); + this->m_pbData = pBuffer; + this->m_cbData = cbBuffer; + this->m_bDataOwner = TRUE; + this->m_NeedToReadData=FALSE; + + return S_OK; + +} + +HRESULT AssemblyFileHash::SetFileName(LPCWSTR wszFileName) +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + INJECT_FAULT(return E_OUTOFMEMORY;); +#ifdef MODE_PREEMPTIVE + MODE_PREEMPTIVE; +#endif + } + CONTRACTL_END; + + HRESULT hr = S_OK; + HandleHolder hFile; + + hFile = WszCreateFile(wszFileName, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if (hFile == INVALID_HANDLE_VALUE) + return HRESULT_FROM_GetLastError(); + + IfFailRet(SetFileHandle(hFile)); + hFile.SuppressRelease(); + return S_OK; +} + + +HRESULT AssemblyFileHash::HashData(HCRYPTHASH hHash) +{ + WRAPPER_NO_CONTRACT; + if(!CryptHashData(hHash, m_pbData, m_cbData, 0)) + return HRESULT_FROM_GetLastError(); + return S_OK; +} + +HRESULT AssemblyFileHash::CalculateHash(DWORD algid) +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END; + + HRESULT hr = S_OK; + if(m_NeedToReadData) + IfFailRet(ReadData()); + + _ASSERTE(!m_NeedToReadData); + + HCRYPTPROV pProvider = NULL; + HCRYPTHASH hHash = NULL; + DWORD count; + + if(!WszCryptAcquireContext(&pProvider, + NULL, + NULL, + //PROV_RSA_SIG, + PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) + IfFailGo(HRESULT_FROM_GetLastError()); + + + if(!CryptCreateHash(pProvider, + algid, + 0, + 0, + &hHash)) + IfFailGo(HRESULT_FROM_GetLastError()); + + IfFailGo(HashData(hHash)); + + count = sizeof(m_cbHash); + if(!CryptGetHashParam(hHash, + HP_HASHSIZE, + (PBYTE) &m_cbHash, + &count, + 0)) + IfFailGo(HRESULT_FROM_GetLastError()); + + if(m_cbHash > 0) { + m_pbHash = new (nothrow) BYTE[m_cbHash]; + if (!m_pbHash) + IfFailGo(E_OUTOFMEMORY); + + if(!CryptGetHashParam(hHash, + HP_HASHVAL, + m_pbHash, + &m_cbHash, + 0)) + IfFailGo(HRESULT_FROM_GetLastError()); + } + + ErrExit: + + if(hHash) + CryptDestroyHash(hHash); + if(pProvider) + CryptReleaseContext(pProvider, 0); + return hr; +} + + + + diff --git a/src/utilcode/bitvector.cpp b/src/utilcode/bitvector.cpp new file mode 100644 index 0000000000..63af3acd93 --- /dev/null +++ b/src/utilcode/bitvector.cpp @@ -0,0 +1,406 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +/***************************************************************************/ +/* BitVector.cpp */ +/***************************************************************************/ +// Routines to support a growable bitvector +/***************************************************************************/ + +#include "stdafx.h" +#include <memory.h> + +#include "contract.h" +#include "bitvector.h" + +#if USE_BITVECTOR + +int BitVector::NumBits() const +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } CONTRACTL_END; + + int count = 0; + ChunkType hiChunk; + + if (isBig()) + { + unsigned maxNonZero = 0; + for (unsigned i=1; (i < m_vals.GetLength()); i++) + { + if (m_vals.m_chunks[i] != 0) + { + maxNonZero = i; + } + } + count = (maxNonZero * CHUNK_BITS) - 1; + hiChunk = m_vals.m_chunks[maxNonZero]; + } + else + { + hiChunk = m_val; + } + + while (hiChunk > 0) + { + hiChunk <<= 1; + count++; + } + + _ASSERTE(count >= 0); + return count; +} + +void BitVector::doBigInit(ChunkType arg) +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + m_vals.m_chunks[0] = arg; + m_vals.SetLength(1); +} + +void BitVector::doBigInit(const BitVector& arg) +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + if (arg.isBig()) + { + memcpy(m_vals.m_chunks, arg.m_vals.m_chunks, (sizeof(ChunkType) * arg.m_vals.GetLength())); + m_vals.SetLength(arg.m_vals.GetLength()); + } + else + { + m_val = arg.m_val; + } +} + +void BitVector::doBigLeftShiftAssign(unsigned shift) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } CONTRACTL_END; + + if ((m_val == 0) || (shift == 0)) // Zero is a special case, don't need to do anything + return; + + unsigned numWords = shift / CHUNK_BITS; + unsigned numBits = shift % CHUNK_BITS; + + // + // Change to Big representation + // + toBig(); + + int from = m_vals.GetLength()-1; + int to = from + numWords; + unsigned newlen = to + 1; + + ChunkType topBits = 0; + if (numBits > 0) + { + topBits = m_vals.m_chunks[from] >> (CHUNK_BITS - numBits); + } + + if (topBits != 0 || numWords != 0) + { + if (topBits != 0) + { + m_vals.m_chunks[newlen] = topBits; + newlen++; + } + m_vals.SetLength(newlen); + } + + while (to >= 0) + { + m_vals.m_chunks[to] = (from >= 0) ? (m_vals.m_chunks[from] << numBits) : 0; + from--; + + if ((from >= 0) && (numBits > 0)) + { + m_vals.m_chunks[to] |= m_vals.m_chunks[from] >> (CHUNK_BITS - numBits); + } + to--; + } + + // Convert back to small format if necessary + if ((newlen == 1) && (m_vals.m_chunks[0] <= MaxVal)) + { + m_val = ChunkType(m_vals.m_chunks[0] << 1); + } +} + +void BitVector::doBigRightShiftAssign(unsigned shift) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } CONTRACTL_END; + + if ((m_val == 0) || (shift == 0)) // Zero is a special case, don't need to do anything + return; + + unsigned numWords = shift / CHUNK_BITS; + unsigned numBits = shift % CHUNK_BITS; + + // + // Change to Big representation + // + toBig(); + + unsigned from = numWords; + unsigned to = 0; + unsigned len = m_vals.GetLength(); + unsigned newlen = len - numWords; + + if (from >= len) + { + // we always encode zero in short form + m_val = 0; + } + else + { + m_vals.m_chunks[to] = (m_vals.m_chunks[from] >> numBits); + from++; + + while (from < len) + { + if (numBits > 0) + { + m_vals.m_chunks[to] |= m_vals.m_chunks[from] << (CHUNK_BITS - numBits); + } + to++; + + m_vals.m_chunks[to] = (m_vals.m_chunks[from] >> numBits); + from++; + } + + if ((newlen > 1) && (m_vals.m_chunks[newlen-1] == 0)) + { + newlen--; + } + + m_vals.SetLength(newlen); + + // Convert back to small format if necessary + if ((newlen == 1) && (m_vals.m_chunks[0] <= MaxVal)) + { + m_val = ChunkType(m_vals.m_chunks[0] << 1); + } + } +} + +void BitVector::doBigAndAssign(const BitVector& arg) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } CONTRACTL_END; + + // + // Change to Big representation + // + toBig(); + + if (arg.isBig()) + { + bool isZero = true; // until proven otherwise + unsigned myLen = m_vals.GetLength(); + unsigned argLen = arg.m_vals.GetLength(); + + if (myLen > argLen) + { + // shrink our length to match argLen + m_vals.SetLength(argLen); + myLen = argLen; + } + + for (unsigned i = 0; (i < myLen); i++) + { + ChunkType curChunk = m_vals.m_chunks[i] & arg.m_vals.m_chunks[i]; + m_vals.m_chunks[i] = curChunk; + if (curChunk != 0) + isZero = false; + } + + if (isZero) + { + // we always encode zero in short form + m_val = 0; + } + } + else + { + m_val = (m_vals.m_chunks[0] << 1) & arg.m_val; + } +} + +void BitVector::doBigOrAssign(const BitVector& arg) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } CONTRACTL_END; + + // + // Change to Big representation + // + toBig(); + + if (arg.isBig()) + { + unsigned myLen = m_vals.GetLength(); + unsigned argLen = arg.m_vals.GetLength(); + + if (myLen < argLen) + { + // expand our length to match argLen and zero init + memset(m_vals.m_chunks + myLen, 0, sizeof(ChunkType) * (argLen - myLen)); + m_vals.SetLength(argLen); + myLen = argLen; + } + + for(unsigned i = 0; ((i < myLen) && (i < argLen)); i++) + { + m_vals.m_chunks[i] |= arg.m_vals.m_chunks[i]; + } + } + else + { + m_vals.m_chunks[0] |= arg.smallBits(); + } +} + +void BitVector::doBigDiffAssign(const BitVector& arg) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } CONTRACTL_END; + + // + // Change to Big representation + // + toBig(); + + unsigned myLen = m_vals.GetLength(); + unsigned argLen = arg.m_vals.GetLength(); + bool isZero = true; // until proven otherwise + + for (unsigned i = 0; (i < myLen); i++) + { + ChunkType nextChunk = m_vals.m_chunks[i]; + if (i < argLen) + { + nextChunk &= ~arg.m_vals.m_chunks[i]; + m_vals.m_chunks[i] = nextChunk; + } + else if (i == 0) + { + nextChunk &= ~arg.smallBits(); + m_vals.m_chunks[i] = nextChunk; + } + + if (nextChunk != 0) + isZero = false; + } + + if (isZero) + { + // we always encode zero in short form + m_val = 0; + } +} + +BOOL BitVector::doBigEquals(const BitVector& arg) const +{ + CONTRACT(BOOL) + { + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACT_END; + + unsigned myLen = m_vals.GetLength(); + unsigned argLen = arg.m_vals.GetLength(); + unsigned maxLen = (myLen >= argLen) ? myLen : argLen; + + for (unsigned i=0; (i < maxLen); i++) + { + ChunkType myVal = 0; + ChunkType argVal = 0; + + if (i < myLen) + myVal = m_vals.m_chunks[i]; + + if (i < argLen) + argVal = arg.m_vals.m_chunks[i]; + + if (i == 0) + { + if (myLen == 0) + myVal = smallBits(); + if (argLen == 0) + argVal = arg.smallBits(); + } + + if (myVal != argVal) + RETURN false; + } + RETURN true; +} + +BOOL BitVector::doBigIntersect(const BitVector& arg) const +{ + CONTRACT(BOOL) + { + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACT_END; + + unsigned myLen = m_vals.GetLength(); + unsigned argLen = arg.m_vals.GetLength(); + unsigned minLen = (myLen <= argLen) ? myLen : argLen; + + for (unsigned i=0; (i <= minLen); i++) + { + ChunkType myVal = 0; + ChunkType argVal = 0; + + if (i < myLen) + myVal = m_vals.m_chunks[i]; + + if (i < argLen) + argVal = arg.m_vals.m_chunks[i]; + + if (i == 0) + { + if (myLen == 0) + myVal = smallBits(); + if (argLen == 0) + argVal = arg.smallBits(); + } + + if ((myVal & argVal) != 0) + RETURN true; + } + RETURN false; +} + +#endif // USE_BITVECTOR diff --git a/src/utilcode/ccomprc.cpp b/src/utilcode/ccomprc.cpp new file mode 100644 index 0000000000..fa08b5e456 --- /dev/null +++ b/src/utilcode/ccomprc.cpp @@ -0,0 +1,1134 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#include "stdafx.h" // Standard header. +#include <utilcode.h> // Utility helpers. +#include <corerror.h> +#include "newapis.h" +#include "ndpversion.h" + +#include "../dlls/mscorrc/resource.h" +#include "sstring.h" +#include "stringarraylist.h" + +#ifdef USE_FORMATMESSAGE_WRAPPER +// we implement the wrapper for FormatMessageW. +// Need access to the original +#undef WszFormatMessage +#define WszFormatMessage ::FormatMessageW +#endif + +#define MAX_VERSION_STRING 30 + +// External prototypes. +extern HINSTANCE GetModuleInst(); + +#ifndef FEATURE_PAL + +//***************************************************************************** +// Get the MUI ID, on downlevel platforms where MUI is not supported it +// returns the default system ID. + +typedef LANGID (WINAPI *PFNGETUSERDEFAULTUILANGUAGE)(void); // kernel32!GetUserDefaultUILanguage + +int GetMUILanguageID(LocaleIDValue* pResult) +{ + CONTRACTL + { + GC_NOTRIGGER; + NOTHROW; +#ifdef MODE_PREEMPTIVE + MODE_PREEMPTIVE; +#endif + } + CONTRACTL_END; +#if FEATURE_USE_LCID + int langId=0; + static PFNGETUSERDEFAULTUILANGUAGE pfnGetUserDefaultUILanguage=NULL; + + if( NULL == pfnGetUserDefaultUILanguage ) + { + PFNGETUSERDEFAULTUILANGUAGE proc = NULL; + + HMODULE hmod = GetModuleHandleA(WINDOWS_KERNEL32_DLLNAME_A); + + if( hmod ) + proc = (PFNGETUSERDEFAULTUILANGUAGE) + GetProcAddress(hmod, "GetUserDefaultUILanguage"); + + if(proc == NULL) + proc = (PFNGETUSERDEFAULTUILANGUAGE) -1; + + PVOID value = InterlockedExchangeT(&pfnGetUserDefaultUILanguage, + proc); + } + + // We should never get NULL here, the function is -1 or a valid address. + _ASSERTE(pfnGetUserDefaultUILanguage != NULL); + + + if( pfnGetUserDefaultUILanguage == (PFNGETUSERDEFAULTUILANGUAGE) -1) + langId = GetSystemDefaultLangID(); + else + langId = pfnGetUserDefaultUILanguage(); + + *pResult= langId; +#else // FEATURE_USE_LCID + _ASSERTE(sizeof(LocaleID)/sizeof(WCHAR) >=LOCALE_NAME_MAX_LENGTH); + return NewApis::GetSystemDefaultLocaleName(*pResult, LOCALE_NAME_MAX_LENGTH); +#endif //FEATURE_USE_LCID + return 1; +} + +static void BuildMUIDirectory(int langid, __out SString* pResult) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + PRECONDITION(CheckPointer(pResult)); + } + CONTRACTL_END; + + pResult->Printf(W("MUI\\%04x\\"), langid); +} + +void GetMUILanguageName(__out SString* pResult) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + PRECONDITION(CheckPointer(pResult)); + } + CONTRACTL_END; + + LocaleIDValue langid; + GetMUILanguageID(&langid); + + int lcid; +#ifdef FEATURE_USE_LCID + lcid=langid; +#else + lcid=NewApis::LocaleNameToLCID(langid,0); +#endif + + return BuildMUIDirectory(lcid, pResult); +} + +void GetMUIParentLanguageName(SString* pResult) +{ + WRAPPER_NO_CONTRACT; + int langid = 1033; + + BuildMUIDirectory(langid, pResult); +} +#ifndef DACCESS_COMPILE +HRESULT GetMUILanguageNames(__inout StringArrayList* pCultureNames) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + PRECONDITION(CheckPointer(pCultureNames)); + SO_INTOLERANT; + } + CONTRACTL_END; + + HRESULT hr=S_OK; + EX_TRY + { + SString result; + GetMUILanguageName(&result); + + if(!result.IsEmpty()) + { + pCultureNames->Append(result); + } + + GetMUIParentLanguageName(&result); + + _ASSERTE(!result.IsEmpty()); + pCultureNames->Append(result); + pCultureNames->Append(SString::Empty()); + } + EX_CATCH_HRESULT(hr) + return hr; + +} +#endif // DACCESS_COMPILE + +#endif // !FEATURE_PAL + +BOOL CCompRC::s_bIsMscoree = FALSE; + +//***************************************************************************** +// Do the mapping from an langId to an hinstance node +//***************************************************************************** +HRESOURCEDLL CCompRC::LookupNode(LocaleID langId, BOOL &fMissing) +{ + LIMITED_METHOD_CONTRACT; + + if (m_pHash == NULL) return NULL; + +// Linear search + int i; + for(i = 0; i < m_nHashSize; i ++) { + if (m_pHash[i].IsSet() && m_pHash[i].HasID(langId)) { + return m_pHash[i].GetLibraryHandle(); + } + if (m_pHash[i].IsMissing() && m_pHash[i].HasID(langId)) + { + fMissing = TRUE; + return NULL; + } + } + + return NULL; +} + +//***************************************************************************** +// Add a new node to the map and return it. +//***************************************************************************** +const int MAP_STARTSIZE = 7; +const int MAP_GROWSIZE = 5; + +HRESULT CCompRC::AddMapNode(LocaleID langId, HRESOURCEDLL hInst, BOOL fMissing) +{ + CONTRACTL + { + GC_NOTRIGGER; + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END; + + + if (m_pHash == NULL) { + m_pHash = new (nothrow)CCulturedHInstance[MAP_STARTSIZE]; + if (m_pHash==NULL) + return E_OUTOFMEMORY; + m_nHashSize = MAP_STARTSIZE; + } + +// For now, place in first open slot + int i; + for(i = 0; i < m_nHashSize; i ++) { + if (!m_pHash[i].IsSet() && !m_pHash[i].IsMissing()) { + if (fMissing) + { + m_pHash[i].SetMissing(langId); + } + else + { + m_pHash[i].Set(langId,hInst); + } + + return S_OK; + } + } + +// Out of space, regrow + CCulturedHInstance * pNewHash = new (nothrow)CCulturedHInstance[m_nHashSize + MAP_GROWSIZE]; + if (pNewHash) + { + memcpy(pNewHash, m_pHash, sizeof(CCulturedHInstance) * m_nHashSize); + delete [] m_pHash; + m_pHash = pNewHash; + if (fMissing) + { + m_pHash[m_nHashSize].SetMissing(langId); + } + else + { + m_pHash[m_nHashSize].Set(langId,hInst); + } + m_nHashSize += MAP_GROWSIZE; + } + else + return E_OUTOFMEMORY; + return S_OK; +} + +//***************************************************************************** +// Initialize +//***************************************************************************** +#ifndef FEATURE_CORECLR +LPCWSTR CCompRC::m_pDefaultResource = W("mscorrc.dll"); +#else // !FEATURE_CORECLR +LPCWSTR CCompRC::m_pDefaultResource = W("mscorrc.debug.dll"); +LPCWSTR CCompRC::m_pFallbackResource= W("mscorrc.dll"); +#endif // !FEATURE_CORECLR + +HRESULT CCompRC::Init(LPCWSTR pResourceFile, BOOL bUseFallback) +{ + CONTRACTL + { + GC_NOTRIGGER; + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END; + + // This function is called during Watson process. We need to make sure + // that this function is restartable. + // + // Make sure to NEVER null out the function callbacks in the Init + // function. They get set for the "Default CCompRC" during EEStartup + // and we want to make sure we don't wipe them out. + + m_bUseFallback = bUseFallback; + + if (m_pResourceFile == NULL) + { + if(pResourceFile) + { + NewArrayHolder<WCHAR> pwszResourceFile(NULL); + + DWORD lgth = (DWORD) wcslen(pResourceFile) + 1; + pwszResourceFile = new(nothrow) WCHAR[lgth]; + if (pwszResourceFile) + { + wcscpy_s(pwszResourceFile, lgth, pResourceFile); + LPCWSTR pFile = pwszResourceFile.Extract(); + if (InterlockedCompareExchangeT(&m_pResourceFile, pFile, NULL) != NULL) + { + delete [] pFile; + } + } + } + else + InterlockedCompareExchangeT(&m_pResourceFile, m_pDefaultResource, NULL); + } + + if (m_pResourceFile == NULL) + { + return E_OUTOFMEMORY; + } + + if (m_csMap == NULL) + { + // NOTE: there are times when the debugger's helper thread is asked to do a favor for another thread in the + // process. Typically, this favor involves putting up a dialog for the user. Putting up a dialog usually ends + // up involving the CCompRC code since (of course) the strings in the dialog are in a resource file. Thus, the + // debugger's helper thread will attempt to acquire this CRST. This is okay, since the helper thread only does + // these favors for other threads when there is no debugger attached. Thus, there are no deadlock hazards with + // this lock, and its safe for the helper thread to take, so this CRST is marked with CRST_DEBUGGER_THREAD. + CRITSEC_COOKIE csMap = ClrCreateCriticalSection(CrstCCompRC, + (CrstFlags)(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD | CRST_TAKEN_DURING_SHUTDOWN)); + + if (csMap) + { + if (InterlockedCompareExchangeT(&m_csMap, csMap, NULL) != NULL) + { + ClrDeleteCriticalSection(csMap); + } + } + } + + if (m_csMap == NULL) + return E_OUTOFMEMORY; + + return S_OK; +} + +void CCompRC::SetResourceCultureCallbacks( + FPGETTHREADUICULTURENAMES fpGetThreadUICultureNames, + FPGETTHREADUICULTUREID fpGetThreadUICultureId) +{ + LIMITED_METHOD_CONTRACT; + + m_fpGetThreadUICultureNames = fpGetThreadUICultureNames; + m_fpGetThreadUICultureId = fpGetThreadUICultureId; +} + +void CCompRC::GetResourceCultureCallbacks( + FPGETTHREADUICULTURENAMES* fpGetThreadUICultureNames, + FPGETTHREADUICULTUREID* fpGetThreadUICultureId) +{ + LIMITED_METHOD_CONTRACT; + + if(fpGetThreadUICultureNames) + *fpGetThreadUICultureNames=m_fpGetThreadUICultureNames; + + if(fpGetThreadUICultureId) + *fpGetThreadUICultureId=m_fpGetThreadUICultureId; +} + +void CCompRC::Destroy() +{ + CONTRACTL + { + GC_NOTRIGGER; + NOTHROW; +#ifdef MODE_PREEMPTIVE + MODE_PREEMPTIVE; +#endif + } + CONTRACTL_END; + + // Free all resource libraries + + //***************************************************************************** + // Free the loaded library if we ever loaded it and only if we are not on + // Win 95 which has a known bug with DLL unloading (it randomly unloads a + // dll on shut down, not necessarily the one you asked for). This is done + // only in debug mode to make coverage runs accurate. + //***************************************************************************** + +#if defined(_DEBUG) + if (m_Primary.GetLibraryHandle()) { + ::FreeLibrary(m_Primary.GetLibraryHandle()); + } + + if (m_pHash != NULL) { + int i; + for(i = 0; i < m_nHashSize; i ++) { + if (m_pHash[i].GetLibraryHandle() != NULL) { + ::FreeLibrary(m_pHash[i].GetLibraryHandle()); + break; + } + } + } +#endif + + // destroy map structure + if(m_pResourceFile != m_pDefaultResource) + delete [] m_pResourceFile; + m_pResourceFile = NULL; + + if(m_csMap) { + ClrDeleteCriticalSection(m_csMap); + ZeroMemory(&(m_csMap), sizeof(CRITSEC_COOKIE)); + } + + if(m_pHash != NULL) { + delete [] m_pHash; + m_pHash = NULL; + } +} + + +//***************************************************************************** +// Initialization is done lazily, for backwards compatibility "mscorrc.dll" +// is consider the default location for all strings that use CCompRC. +// An instance value for CCompRC can be created to load resources from a different +// resource dll. +//***************************************************************************** +LONG CCompRC::m_dwDefaultInitialized = 0; +CCompRC CCompRC::m_DefaultResourceDll; + +CCompRC* CCompRC::GetDefaultResourceDll() +{ + CONTRACTL + { + GC_NOTRIGGER; + NOTHROW; +#ifdef MODE_PREEMPTIVE + MODE_PREEMPTIVE; +#endif + } + CONTRACTL_END; + + if (m_dwDefaultInitialized) + return &m_DefaultResourceDll; + + if(FAILED(m_DefaultResourceDll.Init(NULL, TRUE))) + { + return NULL; + } + m_dwDefaultInitialized = 1; + + return &m_DefaultResourceDll; +} + +#ifdef FEATURE_CORECLR +LONG CCompRC::m_dwFallbackInitialized = 0; +CCompRC CCompRC::m_FallbackResourceDll; + +CCompRC* CCompRC::GetFallbackResourceDll() +{ + CONTRACTL + { + GC_NOTRIGGER; + NOTHROW; +#ifdef MODE_PREEMPTIVE + MODE_PREEMPTIVE; +#endif + } + CONTRACTL_END; + + if (m_dwFallbackInitialized) + return &m_FallbackResourceDll; + + if(FAILED(m_FallbackResourceDll.Init(m_pFallbackResource, FALSE))) + { + return NULL; + } + m_dwFallbackInitialized = 1; + + return &m_FallbackResourceDll; +} + +#endif // FEATURE_CORECLR + + +//***************************************************************************** +//***************************************************************************** + +HRESULT CCompRC::GetLibrary(LocaleID langId, HRESOURCEDLL* phInst) +{ + CONTRACTL + { + GC_NOTRIGGER; + NOTHROW; +#ifdef MODE_PREEMPTIVE + MODE_PREEMPTIVE; +#endif + PRECONDITION(phInst != NULL); + } + CONTRACTL_END; + + HRESULT hr = E_FAIL; + HRESOURCEDLL hInst = 0; +#ifndef DACCESS_COMPILE + HRESOURCEDLL hLibInst = 0; //Holds early library instance + BOOL fLibAlreadyOpen = FALSE; //Determine if we can close the opened library. +#endif + + // Try to match the primary entry, or else use the primary if we don't care. + if (m_Primary.IsSet()) + { + if (langId == UICULTUREID_DONTCARE || m_Primary.HasID(langId)) + { + hInst = m_Primary.GetLibraryHandle(); + hr = S_OK; + } + } + else if(m_Primary.IsMissing()) + { + // If primary is missing then the hash will not have anything either + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } +#ifndef DACCESS_COMPILE + // If this is the first visit, we must set the primary entry + else + { + // Don't immediately return if LoadLibrary fails so we can indicate the file was missing + hr = LoadLibrary(&hLibInst); + // If it's a transient failure, don't cache the failure + if (FAILED(hr) && Exception::IsTransient(hr)) + { + return hr; + } + + CRITSEC_Holder csh (m_csMap); + // As we expected + if (!m_Primary.IsSet() && !m_Primary.IsMissing()) + { + hInst = hLibInst; + if (SUCCEEDED(hr)) + { + m_Primary.Set(langId,hLibInst); + } + else + { + m_Primary.SetMissing(langId); + } + } + + // Someone got into this critical section before us and set the primary already + else if (m_Primary.HasID(langId)) + { + hInst = m_Primary.GetLibraryHandle(); + fLibAlreadyOpen = TRUE; + } + + // If neither case is true, someone got into this critical section before us and + // set the primary to other than the language we want... + else + { + fLibAlreadyOpen = TRUE; + } + + IfFailRet(hr); + + if (fLibAlreadyOpen) + { + FreeLibrary(hLibInst); + fLibAlreadyOpen = FALSE; + } + } +#endif + + // If we enter here, we know that the primary is set to something other than the + // language we want - multiple languages use the hash table + if (hInst == NULL && !m_Primary.IsMissing()) + { + // See if the resource exists in the hash table + { + CRITSEC_Holder csh(m_csMap); + BOOL fMissing = FALSE; + hInst = LookupNode(langId, fMissing); + if (fMissing == TRUE) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + goto Exit; + } + } + +#ifndef DACCESS_COMPILE + // If we didn't find it, we have to load the library and insert it into the hash + if (hInst == NULL) + { + hr = LoadLibrary(&hLibInst); + // If it's a transient failure, don't cache the failure + if (FAILED(hr) && Exception::IsTransient(hr)) + { + return hr; + } + { + CRITSEC_Holder csh (m_csMap); + + // Double check - someone may have entered this section before us + BOOL fMissing = FALSE; + hInst = LookupNode(langId, fMissing); + if (hInst == NULL && !fMissing) + { + if (SUCCEEDED(hr)) + { + hInst = hLibInst; + hr = AddMapNode(langId, hInst); + } else + { + HRESULT hrLoadLibrary = hr; + hr = AddMapNode(langId, hInst, TRUE /* fMissing */); + if (SUCCEEDED(hr)) + { + hr = hrLoadLibrary; + } + } + } + else + { + fLibAlreadyOpen = TRUE; + } + } + + if (fLibAlreadyOpen || FAILED(hr)) + { + FreeLibrary(hLibInst); + } + } + + // We found the node, so set hr to be a success. + else + { + hr = S_OK; + } +#endif // DACCESS_COMPILE + } +Exit: + *phInst = hInst; + return hr; +} + +//***************************************************************************** +// Load the string +// We load the localized libraries and cache the handle for future use. +// Mutliple threads may call this, so the cache structure is thread safe. +//***************************************************************************** +HRESULT CCompRC::LoadString(ResourceCategory eCategory, UINT iResourceID, __out_ecount(iMax) LPWSTR szBuffer, int iMax, int *pcwchUsed) +{ + WRAPPER_NO_CONTRACT; + LocaleIDValue langIdValue; + LocaleID langId; + // Must resolve current thread's langId to a dll. + if(m_fpGetThreadUICultureId) { + int ret = (*m_fpGetThreadUICultureId)(&langIdValue); + + // Callback can't return 0, since that indicates empty. + // To indicate empty, callback should return UICULTUREID_DONTCARE + _ASSERTE(ret != 0); + + if (ret == 0) + return E_UNEXPECTED; + langId=langIdValue; + + } + else { + langId = UICULTUREID_DONTCARE; + } + + + return LoadString(eCategory, langId, iResourceID, szBuffer, iMax, pcwchUsed); +} + +HRESULT CCompRC::LoadString(ResourceCategory eCategory, LocaleID langId, UINT iResourceID, __out_ecount(iMax) LPWSTR szBuffer, int iMax, int *pcwchUsed) +{ + CONTRACTL + { + GC_NOTRIGGER; + NOTHROW; +#ifdef MODE_PREEMPTIVE + MODE_PREEMPTIVE; +#endif + } + CONTRACTL_END; + +#ifndef FEATURE_PAL + HRESULT hr; + HRESOURCEDLL hInst = 0; //instance of cultured resource dll + int length; + + hr = GetLibrary(langId, &hInst); + + if (SUCCEEDED(hr)) + { + // Now that we have the proper dll handle, load the string + _ASSERTE(hInst != NULL); + + length = ::WszLoadString(hInst, iResourceID, szBuffer, iMax); + if(length > 0) + { + if(pcwchUsed) + { + *pcwchUsed = length; + } + return (S_OK); + } + if(GetLastError()==ERROR_SUCCESS) + hr=HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + else + hr=HRESULT_FROM_GetLastError(); + } + + + // Failed to load string + if ( hr != E_OUTOFMEMORY && ShouldUseFallback()) + { +#ifdef FEATURE_CORECLR + CCompRC* pFallback=CCompRC::GetFallbackResourceDll(); + if (pFallback) + { + //should not fall back to itself + _ASSERTE(pFallback != this); + + // check existence in the fallback Dll + + hr = pFallback->LoadString(Optional, langId, iResourceID,szBuffer, iMax, pcwchUsed); + + if(SUCCEEDED(hr)) + return hr; + } +#endif + switch (eCategory) + { + case Optional: + hr = E_FAIL; + break; +#ifdef FEATURE_CORECLR + case DesktopCLR: + hr = E_FAIL; + break; + case Debugging: + case Error: + // get stub message + { + + if (pFallback) + { + + StackSString ssErrorFormat; + if (eCategory == Error) + { + hr=ssErrorFormat.LoadResourceAndReturnHR(pFallback, CCompRC::Required, IDS_EE_LINK_FOR_ERROR_MESSAGES); + } + else + { + _ASSERTE(eCategory == Debugging); + hr=ssErrorFormat.LoadResourceAndReturnHR(pFallback, CCompRC::Required, IDS_EE_LINK_FOR_DEBUGGING_MESSAGES); + } + + if (SUCCEEDED(hr)) + { + StackSString sFormattedMessage; + int iErrorCode = HR_FOR_URT_MSG(iResourceID); + + hr = S_OK; + + DWORD_PTR args[] = {(DWORD_PTR)VER_FILEVERSION_STR_L, iResourceID, iErrorCode}; + + length = WszFormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY , + (LPCWSTR)ssErrorFormat, 0, 0, + szBuffer,iMax,(va_list*)args); + + if (length == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + // The buffer wasn't big enough for the message. Tell the caller this. + // + // Clear the buffer, just in case. + if (szBuffer && iMax) + *szBuffer = W('\0'); + + length = iMax; + hr=HRESULT_FROM_GetLastError(); + } + + if(length > 0) + { + if(pcwchUsed) + { + *pcwchUsed = length; + } + return hr; + } + + // Format mesage failed + hr=HRESULT_FROM_GetLastError(); + + } + } + else // if (pFallback) + { + _ASSERTE(FAILED(hr)); + } + } + // if we got here then we couldn't get the fallback message + // the fallback message is required so just falling through into "Required" + +#else // FEATURE_CORECLR + // everything that's not optional goes here for Desktop + case DesktopCLR: + case Debugging: + case Error: +#endif + case Required: + + if ( hr != E_OUTOFMEMORY) + { + // Shouldn't be any reason for this condition but the case where we + // used the wrong ID or didn't update the resource DLL. + _ASSERTE(0); + hr = HRESULT_FROM_GetLastError(); + } + break; + default: + { + _ASSERTE(!"Invalid eCategory"); + } + } + } + + // Return an empty string to save the people with a bad error handling + if (szBuffer && iMax) + *szBuffer = W('\0'); + + return hr; +#else // !FEATURE_PAL + PORTABILITY_ASSERT("UNIXTODO: Implement string loading from resources"); + return S_OK; +#endif // !FEATURE_PAL +} + +#ifndef DACCESS_COMPILE +HRESULT CCompRC::LoadMUILibrary(HRESOURCEDLL * pHInst) +{ + WRAPPER_NO_CONTRACT; + _ASSERTE(pHInst != NULL); + LocaleID langId; + LocaleIDValue langIdValue; + // Must resolve current thread's langId to a dll. + if(m_fpGetThreadUICultureId) { + int ret = (*m_fpGetThreadUICultureId)(&langIdValue); + + // Callback can't return 0, since that indicates empty. + // To indicate empty, callback should return UICULTUREID_DONTCARE + _ASSERTE(ret != 0); + langId=langIdValue; + } + else + langId = UICULTUREID_DONTCARE; + + HRESULT hr = GetLibrary(langId, pHInst); + return hr; +} + +HRESULT CCompRC::LoadResourceFile(HRESOURCEDLL * pHInst, LPCWSTR lpFileName) +{ +#ifndef FEATURE_PAL + DWORD dwLoadLibraryFlags; + if(m_pResourceFile == m_pDefaultResource) + dwLoadLibraryFlags = LOAD_LIBRARY_AS_DATAFILE; + else + dwLoadLibraryFlags = 0; + + if ((*pHInst = WszLoadLibraryEx(lpFileName, NULL, dwLoadLibraryFlags)) == NULL) { + return HRESULT_FROM_GetLastError(); + } +#else // !FEATURE_PAL + PORTABILITY_ASSERT("UNIXTODO: Implement resource loading - use peimagedecoder?"); +#endif // !FEATURE_PAL + return S_OK; +} + +//***************************************************************************** +// Load the library for this thread's current language +// Called once per language. +// Search order is: +// 1. Dll in localized path (<dir of this module>\<lang name (en-US format)>\mscorrc.dll) +// 2. Dll in localized (parent) path (<dir of this module>\<lang name> (en format)\mscorrc.dll) +// 3. Dll in root path (<dir of this module>\mscorrc.dll) +// 4. Dll in current path (<current dir>\mscorrc.dll) +//***************************************************************************** +HRESULT CCompRC::LoadLibraryHelper(HRESOURCEDLL *pHInst, + __out_ecount(rcPathSize) __out_z WCHAR *rcPath, const DWORD rcPathSize) +{ + CONTRACTL + { + GC_NOTRIGGER; + NOTHROW; +#ifdef MODE_PREEMPTIVE + MODE_PREEMPTIVE; +#endif + } + CONTRACTL_END; + + HRESULT hr = E_FAIL; + + WCHAR rcDrive[_MAX_DRIVE]; // Volume name. + WCHAR rcDir[_MAX_PATH]; // Directory. + + size_t rcDriveLen; + size_t rcDirLen; + size_t rcPartialPathLen; + + + _ASSERTE(m_pResourceFile != NULL); + + size_t rcMscorrcLen = wcslen(m_pResourceFile); + + // must initialize before calling SString::Empty() + SString::Startup(); + + // Try and get both the culture fallback sequence + + StringArrayList cultureNames; + + if (m_fpGetThreadUICultureNames) + { + hr = (*m_fpGetThreadUICultureNames)(&cultureNames); + } + else + { + EX_TRY + { + cultureNames.Append(SString::Empty()); + } + EX_CATCH_HRESULT(hr); + } + + if (hr == E_OUTOFMEMORY) + return hr; + + rcDir[0] = W('\0'); + rcDrive[0] = W('\0'); + rcPath[rcPathSize - 1] = 0; + + SplitPath(rcPath, rcDrive, _MAX_DRIVE, rcDir, _MAX_PATH, 0, 0, 0, 0); + rcDriveLen = wcslen(rcDrive); + rcDirLen = wcslen(rcDir); + + // Length that does not include culture name length + rcPartialPathLen = rcDriveLen + rcDirLen + rcMscorrcLen + 1; + + + for (DWORD i=0; i< cultureNames.GetCount();i++) + { + SString& sLang = cultureNames[i]; + if (rcPartialPathLen + sLang.GetCount() <= rcPathSize) + { + wcscpy_s(rcPath, rcDriveLen+1, rcDrive); + WCHAR *rcPathPtr = rcPath + rcDriveLen; + + wcscpy_s(rcPathPtr, rcDirLen+1, rcDir); + rcPathPtr += rcDirLen; + + if(!sLang.IsEmpty()) + { + wcscpy_s(rcPathPtr, sLang.GetCount()+1, sLang); + wcscpy_s(rcPathPtr+ sLang.GetCount(), rcMscorrcLen+1, W("\\")); + wcscpy_s(rcPathPtr + sLang.GetCount()+1, rcMscorrcLen+1, m_pResourceFile); + } + else + { + wcscpy_s(rcPathPtr + sLang.GetCount(), rcMscorrcLen+1, m_pResourceFile); + } + + // Feedback for debugging to eliminate unecessary loads. + DEBUG_STMT(DbgWriteEx(W("Loading %s to load strings.\n"), rcPath)); + + // Load the resource library as a data file, so that the OS doesn't have + // to allocate it as code. This only works so long as the file contains + // only strings. + hr = LoadResourceFile(pHInst, rcPath); + if (SUCCEEDED(hr)) + break; + } + else + { + _ASSERTE(!"Buffer not big enough"); + hr = E_FAIL; + + } + }; + + // Last ditch search effort in current directory + if (FAILED(hr)) { + hr = LoadResourceFile(pHInst, m_pResourceFile); + } + + return hr; +} + +// Two-stage approach: +// First try module directory, then try CORSystemDirectory for default resource +HRESULT CCompRC::LoadLibrary(HRESOURCEDLL * pHInst) +{ + CONTRACTL + { + GC_NOTRIGGER; + NOTHROW; +#ifdef MODE_PREEMPTIVE + MODE_PREEMPTIVE; +#endif + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + _ASSERTE(pHInst != NULL); + +#ifdef CROSSGEN_COMPILE + // The resources are embeded into the .exe itself for crossgen + *pHInst = GetModuleInst(); +#else + WCHAR rcPath[_MAX_PATH]; // Path to resource DLL. + + // Try first in the same directory as this dll. +#if defined(FEATURE_CORECLR) + + VALIDATECORECLRCALLBACKS(); + + DWORD length = 0; + hr = g_CoreClrCallbacks.m_pfnGetCORSystemDirectory(rcPath, NumItems(rcPath), &length); + if (FAILED(hr)) + return hr; + + hr = LoadLibraryHelper(pHInst, rcPath, NumItems(rcPath)); + +#else // FEATURE_CORECLR + + if (!WszGetModuleFileName(GetModuleInst(), rcPath, NumItems(rcPath))) + return HRESULT_FROM_GetLastError(); + + hr = LoadLibraryHelper(pHInst, rcPath, NumItems(rcPath)); + if (hr == E_OUTOFMEMORY) + return hr; + + // In case of default rc file, also try CORSystemDirectory. + // Note that GetRequestedRuntimeInfo is a function in ths shim. As of 12/06, this is the only + // place where utilcode appears to take a dependency on the shim. This forces everyone that links + // with us to also dynamically link to mscoree.dll. Perhaps this should be a delay-load to prevent + // that static dependency and have a gracefull fallback when the shim isn't installed. + // We don't do this in DAC builds because mscordacwks.dll cannot take a dependency on other CLR + // dlls (eg. you must be able to examine a managed dump on a machine without any CLR installed). +#ifndef DACCESS_COMPILE + if (FAILED(hr) && m_pResourceFile == m_pDefaultResource) + { +#ifdef SELF_NO_HOST + WCHAR rcVersion[MAX_VERSION_STRING]; + DWORD rcVersionSize; + + DWORD corSystemPathSize; + + // The reason for using GetRequestedRuntimeInfo is the ability to suppress message boxes + // with RUNTIME_INFO_DONT_SHOW_ERROR_DIALOG. + hr = LegacyActivationShim::GetRequestedRuntimeInfo( + NULL, + W("v")VER_PRODUCTVERSION_NO_QFE_STR_L, + NULL, + 0, + RUNTIME_INFO_UPGRADE_VERSION|RUNTIME_INFO_DONT_SHOW_ERROR_DIALOG|RUNTIME_INFO_CONSIDER_POST_2_0, + rcPath, + NumItems(rcPath), + &corSystemPathSize, + rcVersion, + NumItems(rcVersion), + &rcVersionSize); + + if (SUCCEEDED(hr)) + { + if (rcVersionSize > 0) + { + wcscat_s(rcPath, NumItems(rcPath), rcVersion) ; + wcscat_s(rcPath, NumItems(rcPath), W("\\")) ; + } + } +#else + // If we're hosted, we have the advantage of a CoreClrCallbacks reference. + // More importantly, we avoid calling back to mscoree.dll. + DWORD cchPath; + hr = GetClrCallbacks().m_pfnGetCORSystemDirectory(rcPath, NumItems(rcPath), &cchPath); +#endif + if (SUCCEEDED(hr)) + { + hr = LoadLibraryHelper(pHInst, rcPath, NumItems(rcPath)); + } + } +#endif // !DACCESS_COMPILE + +#endif // FEATURE_CORECLR + +#endif // CROSSGEN_COMPILE + + return hr; +} + +#endif // DACCESS_COMPILE + + + +#ifdef USE_FORMATMESSAGE_WRAPPER +DWORD +PALAPI +CCompRC::FormatMessage( + IN DWORD dwFlags, + IN LPCVOID lpSource, + IN DWORD dwMessageId, + IN DWORD dwLanguageId, + OUT LPWSTR lpBuffer, + IN DWORD nSize, + IN va_list *Arguments) +{ + STATIC_CONTRACT_NOTHROW; + StackSString str; + if (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM) + { + dwFlags&=~FORMAT_MESSAGE_FROM_SYSTEM; + dwFlags|=FORMAT_MESSAGE_FROM_STRING; + str.LoadResourceAndReturnHR(NULL,CCompRC::Error,dwMessageId); + lpSource=str.GetUnicode(); + } + return WszFormatMessage(dwFlags, + lpSource, + dwMessageId, + dwLanguageId, + lpBuffer, + nSize, + Arguments); +} +#endif // USE_FORMATMESSAGE_WRAPPER + diff --git a/src/utilcode/check.cpp b/src/utilcode/check.cpp new file mode 100644 index 0000000000..f4f3f93344 --- /dev/null +++ b/src/utilcode/check.cpp @@ -0,0 +1,309 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +//================================================================================ +// Assertion checking infrastructure +//================================================================================ + +#include "stdafx.h" +#include <check.h> +#include <sstring.h> +#include <ex.h> +#include <contract.h> + +#ifdef _DEBUG +size_t CHECK::s_cLeakedBytes = 0; +size_t CHECK::s_cNumFailures = 0; +#endif + +BOOL CHECK::s_neverEnforceAsserts = 0; + + +// Currently used for scan SPECIAL_HOLDER_* trickery +DEBUG_NOINLINE BOOL CHECK::EnforceAssert_StaticCheckOnly() +{ + return s_neverEnforceAsserts; +} + +#ifdef ENABLE_CONTRACTS_IMPL +// Need a place to stick this, there is no contract.cpp... +BOOL BaseContract::s_alwaysEnforceContracts = 1; + + +void PAL_TryMarker::Enter() +{ + SCAN_SCOPE_BEGIN; + STATIC_CONTRACT_THROWS; +}; + +void PAL_TryMarker::Leave() +{ + SCAN_SCOPE_END; +}; + + +#define SPECIALIZE_CONTRACT_VIOLATION_HOLDER(mask) \ +template<> void ContractViolationHolder<mask>::Enter() \ +{ \ + SCAN_SCOPE_BEGIN; \ + ANNOTATION_VIOLATION(mask); \ + EnterInternal(mask); \ +}; + +#define SPECIALIZE_AUTO_CLEANUP_CONTRACT_VIOLATION_HOLDER(mask) \ +template<> AutoCleanupContractViolationHolder<mask>::AutoCleanupContractViolationHolder(BOOL fEnterViolation) \ +{ \ + SCAN_SCOPE_BEGIN; \ + ANNOTATION_VIOLATION(mask); \ + EnterInternal(fEnterViolation ? mask : 0); \ +}; + +#define SPECIALIZED_VIOLATION(mask) \ + SPECIALIZE_CONTRACT_VIOLATION_HOLDER(mask); \ + SPECIALIZE_AUTO_CLEANUP_CONTRACT_VIOLATION_HOLDER(mask) + +// There is a special case that requires 0... Why??? Who knows, let's fix that case. + +SPECIALIZED_VIOLATION(0); + +// Basic Specializations + +SPECIALIZED_VIOLATION(AllViolation); +SPECIALIZED_VIOLATION(ThrowsViolation); +SPECIALIZED_VIOLATION(GCViolation); +SPECIALIZED_VIOLATION(ModeViolation); +SPECIALIZED_VIOLATION(FaultViolation); +SPECIALIZED_VIOLATION(FaultNotFatal); +SPECIALIZED_VIOLATION(SOToleranceViolation); +SPECIALIZED_VIOLATION(HostViolation); +SPECIALIZED_VIOLATION(TakesLockViolation); +SPECIALIZED_VIOLATION(LoadsTypeViolation); + +// Other Specializations used by the RUNTIME, if you get a compile time error you need +// to add the specific specialization that you are using here. + +SPECIALIZED_VIOLATION(ThrowsViolation|GCViolation); +SPECIALIZED_VIOLATION(ThrowsViolation|GCViolation|TakesLockViolation); +SPECIALIZED_VIOLATION(ThrowsViolation|SOToleranceViolation); +SPECIALIZED_VIOLATION(ThrowsViolation|ModeViolation); +SPECIALIZED_VIOLATION(ThrowsViolation|FaultNotFatal); +SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation); +SPECIALIZED_VIOLATION(ThrowsViolation|TakesLockViolation); +SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|SOToleranceViolation); +SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|TakesLockViolation); +SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation); +SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|TakesLockViolation|LoadsTypeViolation); +SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|ModeViolation); +SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|ModeViolation|FaultNotFatal); +SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|ModeViolation|FaultNotFatal|TakesLockViolation); +SPECIALIZED_VIOLATION(GCViolation|SOToleranceViolation); +SPECIALIZED_VIOLATION(GCViolation|FaultViolation); +SPECIALIZED_VIOLATION(GCViolation|FaultViolation|SOToleranceViolation); +SPECIALIZED_VIOLATION(GCViolation|FaultViolation|ModeViolation|SOToleranceViolation); +SPECIALIZED_VIOLATION(GCViolation|ModeViolation|SOToleranceViolation); +SPECIALIZED_VIOLATION(GCViolation|ModeViolation|SOToleranceViolation|FaultNotFatal); +SPECIALIZED_VIOLATION(GCViolation|ModeViolation|SOToleranceViolation|FaultNotFatal|TakesLockViolation); +SPECIALIZED_VIOLATION(GCViolation|FaultNotFatal|TakesLockViolation); +SPECIALIZED_VIOLATION(FaultViolation|FaultNotFatal); +SPECIALIZED_VIOLATION(FaultNotFatal|TakesLockViolation); + + + +#undef SPECIALIZED_VIOLATION +#undef SPECIALIZE_AUTO_CLEANUP_CONTRACT_VIOLATION_HOLDER +#undef SPECIALIZE_CONTRACT_VIOLATION_HOLDER + +#endif + +// Trigger triggers the actual check failure. The trigger may provide a reason +// to include in the failure message. + +void CHECK::Trigger(LPCSTR reason) +{ + STATIC_CONTRACT_SO_NOT_MAINLINE; + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + const char *messageString = NULL; + NewHolder<StackScratchBuffer> pScratch(NULL); + NewHolder<StackSString> pMessage(NULL); + + EX_TRY + { + FAULT_NOT_FATAL(); + + pScratch = new StackScratchBuffer(); + pMessage = new StackSString(); + + pMessage->AppendASCII(reason); + pMessage->AppendASCII(": "); + if (m_message != NULL) + pMessage->AppendASCII((m_message != (LPCSTR)1) ? m_message : "<runtime check failure>"); + +#if _DEBUG + pMessage->AppendASCII("FAILED: "); + pMessage->AppendASCII(m_condition); +#endif + + messageString = pMessage->GetANSI(*pScratch); + } + EX_CATCH + { + messageString = "<exception occurred while building failure description>"; + } + EX_END_CATCH(SwallowAllExceptions); + +#if _DEBUG + DbgAssertDialog((char*)m_file, m_line, (char *)messageString); +#else + OutputDebugStringA(messageString); + DebugBreak(); +#endif + +} + +#ifdef _DEBUG +// Setup records context info after a failure. + +void CHECK::Setup(LPCSTR message, LPCSTR condition, LPCSTR file, INT line) +{ + STATIC_CONTRACT_SO_NOT_MAINLINE; + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY; + + // + // It might be nice to collect all of the message here. But for now, we will just + // retain the innermost one. + // + + if (m_message == NULL) + { + m_message = message; + m_condition = condition; + m_file = file; + m_line = line; + } + +#ifdef _DEBUG + else if (IsInAssert()) + { + EX_TRY + { + FAULT_NOT_FATAL(); + // Try to build a stack of condition failures + + StackSString context; + context.Printf("%s\n\t%s%s FAILED: %s\n\t\t%s, line: %d", + m_condition, + message && *message ? message : "", + message && *message ? ": " : "", + condition, + file, line); + + m_condition = AllocateDynamicMessage(context); + } + EX_CATCH + { + // If anything goes wrong, we don't push extra context + } + EX_END_CATCH(SwallowAllExceptions) + } +#endif + +#if defined(_DEBUG_IMPL) + if (IsInAssert() && IsDebuggerPresent()) + { + DebugBreak(); + } +#endif +} + +LPCSTR CHECK::FormatMessage(LPCSTR messageFormat, ...) +{ + STATIC_CONTRACT_SO_NOT_MAINLINE; + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + LPCSTR result = NULL; + + // We never delete this allocated string. A dtor would conflict with places + // we use this around SEH stuff. We could have some fancy stack-based allocator, + // but that's too much work for now. In fact we believe that leaking is a reasonable + // policy, since allocations will only happen on a failed assert, and a failed assert + // will generally be fatal to the process. + + // The most common place for format strings will be in an assert; in + // which case we don't care about leaking. + // But to be safe, if we're not-inside an assert, then we'll just use + // the format string literal to avoid allocated (and leaking) any memory. + + CHECK chk; + if (!chk.IsInAssert()) + result = messageFormat; + else + { + // This path is only run in debug. TakesLockViolation suppresses + // problems with SString below. + CONTRACT_VIOLATION(FaultNotFatal|TakesLockViolation); + + EX_TRY + { + SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; + + // Format it. + va_list args; + va_start( args, messageFormat); + + SString s; + s.VPrintf(messageFormat, args); + + va_end(args); + + result = AllocateDynamicMessage(s); + + } + EX_CATCH + { + // If anything goes wrong, just use the format string. + result = messageFormat; + } + EX_END_CATCH(SwallowAllExceptions) + } + + return result; +} + +LPCSTR CHECK::AllocateDynamicMessage(const SString &s) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_SO_NOT_MAINLINE; + + // Make a copy of it. + StackScratchBuffer buffer; + const char * pMsg = s.GetANSI(buffer); + + // Must copy that into our own field. + size_t len = strlen(pMsg) + 1; + char * p = new char[len]; + strcpy(p, pMsg); + + // But we'll keep counters of how much we're leaking for diagnostic purposes. + s_cLeakedBytes += len; + s_cNumFailures ++; + + // This should never fire. + // Note use an ASSERTE (not a check) to avoid a recursive deadlock. + _ASSERTE(s_cLeakedBytes < 10000 || !"Warning - check misuse - leaked over 10,000B due to unexpected usage pattern"); + return p; +} + +void WINAPI ReleaseCheckTls(LPVOID pTlsData) +{ + CHECK::ReleaseTls(pTlsData); +} + +#endif diff --git a/src/utilcode/circularlog.cpp b/src/utilcode/circularlog.cpp new file mode 100644 index 0000000000..49fd4fde28 --- /dev/null +++ b/src/utilcode/circularlog.cpp @@ -0,0 +1,312 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// +// Circular file log +// +#include "stdafx.h" + +#include "utilcode.h" +#include "circularlog.h" + +CircularLog::CircularLog() +{ + m_bInit = false; +} + +CircularLog::~CircularLog() +{ + Shutdown(); +} + +bool CircularLog::Init(const WCHAR* logname, const WCHAR* logHeader, DWORD maxSize) +{ + Shutdown(); + + m_LogFilename = logname; + m_LogFilename.Append(W(".log")); + + m_LockFilename = logname; + m_LockFilename.Append(W(".lock")); + + m_OldLogFilename = logname; + m_OldLogFilename .Append(W(".old.log")); + + if (logHeader) + m_LogHeader = logHeader; + + m_MaxSize = maxSize; + m_uLogCount = 0; + + m_bInit = true; + + if (CheckLogHeader()) + { + CheckForLogReset(FALSE); + } + return true; +} + +void CircularLog::Shutdown() +{ + m_bInit = false; +} + +void CircularLog::Log(const WCHAR* string) +{ + if (!m_bInit) + { + return; + } + + HANDLE hLogFile = OpenFile(); + if (hLogFile == INVALID_HANDLE_VALUE) + { + return; + } + + // Check for file limit only once in a while + if ((m_uLogCount % 16) == 0) + { + // First do a quick check without acquiring lock, optimizing for the common case where file is not overflow. + LARGE_INTEGER fileSize; + if (GetFileSizeEx(hLogFile, &fileSize) && fileSize.QuadPart > m_MaxSize) + { + // Must close existing handle before calling CheckForOverflow, and re-open it afterwards. + CloseHandle(hLogFile); + CheckForLogReset(TRUE); + hLogFile = OpenFile(); + if (hLogFile == INVALID_HANDLE_VALUE) + { + return; + } + } + } + m_uLogCount++; + + // Replace \n with \r\n (we're writing to a binary file) + NewArrayHolder<WCHAR> pwszConvertedHolder = new WCHAR[wcslen(string)*2 + 1]; + WCHAR* pD = pwszConvertedHolder; + WCHAR previous = W('\0'); + for (const WCHAR* pS = string ; *pS != W('\0') ; pS++) + { + // We get mixed strings ('\n' and '\r\n'). So attempt to filter + // the ones that don't need to have a '\r' added. + if (*pS == W('\n') && previous != W('\r')) + { + *pD = W('\r'); + pD ++; + } + + *pD = *pS; + pD++; + + previous = *pS; + } + + *pD = W('\0'); + + // Convert to Utf8 to reduce typical log file size + SString logString(pwszConvertedHolder); + StackScratchBuffer bufUtf8; + COUNT_T cBytesUtf8 = 0; + const UTF8 * pszUtf8Log = logString.GetUTF8(bufUtf8, &cBytesUtf8); + // Remove null terminator from log entry buffer + cBytesUtf8--; + + DWORD dwWritten; + WriteFile(hLogFile, pszUtf8Log, (DWORD)cBytesUtf8, &dwWritten, NULL); + CloseHandle(hLogFile); +} + + +BOOL CircularLog::CheckLogHeader() +{ + BOOL fNeedsPushToBackupLog = FALSE; + + // Check to make sure the header on the log is utf8, if it is not, push the current file to the .bak file and try again. + HANDLE hLogFile = WszCreateFile( + m_LogFilename.GetUnicode(), + FILE_READ_DATA, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (hLogFile != INVALID_HANDLE_VALUE) + { + CHAR unicodeHeader []= {(char)0xef, (char)0xbb, (char)0xbf}; + CHAR unicodeHeaderCheckBuf[_countof(unicodeHeader)]; + + DWORD dwRead = sizeof(unicodeHeaderCheckBuf); + fNeedsPushToBackupLog = !ReadFile(hLogFile, &unicodeHeaderCheckBuf, dwRead, &dwRead, NULL); + + if (!fNeedsPushToBackupLog) + { + // Successfully read from file. Now check to ensure we read the right amount, and that we read the right data + if ((dwRead != sizeof(unicodeHeader)) || (0 != memcmp(unicodeHeader, unicodeHeaderCheckBuf, dwRead))) + { + fNeedsPushToBackupLog = TRUE; + } + } + CloseHandle(hLogFile); + } + + return fNeedsPushToBackupLog; +} + +#define MOVE_FILE_RETRY_TIME 100 +#define MOVE_FILE_RETRY_COUNT 10 +void CircularLog::CheckForLogReset(BOOL fOverflow) +{ + if (!m_MaxSize) + { + return; + } + + for (int i = 0; i < MOVE_FILE_RETRY_COUNT; i++) + { + FileLockHolder lock; + if (FAILED(lock.AcquireNoThrow(m_LockFilename.GetUnicode()))) + { + // FileLockHolder::Acquire already has a retry loop, so don't retry if it fails. + return; + } + + BOOL fLogNeedsReset = FALSE; + + if (fOverflow) + { + WIN32_FILE_ATTRIBUTE_DATA fileData; + if (WszGetFileAttributesEx( + m_LogFilename, + GetFileExInfoStandard, + &fileData) == FALSE) + { + return; + } + + unsigned __int64 fileSize = + (((unsigned __int64) fileData.nFileSizeHigh) << 32) | + ((unsigned __int64) fileData.nFileSizeLow); + + + if (fileSize > (unsigned __int64) m_MaxSize) + { + fLogNeedsReset = TRUE; + } + } + else + { + fLogNeedsReset = CheckLogHeader(); + } + + if (fLogNeedsReset) + { + // Push current log out to .old file + BOOL success = WszMoveFileEx( + m_LogFilename.GetUnicode(), + m_OldLogFilename.GetUnicode(), + MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED); + + if (success || GetLastError() != ERROR_SHARING_VIOLATION) + { + return; + } + } + else + { + // Someone else moved the file before we can. + return; + } + + // Don't want to hold the lock while sleeping. + lock.Release(); + ClrSleepEx(MOVE_FILE_RETRY_TIME, FALSE); + } +} + +#define OPEN_FILE_RETRY_TIME 100 +#define OPEN_FILE_RETRY_COUNT 10 +// Normally we open file with FILE_SHARE_WRITE, to avoid sharing violations between multiple threads or +// processes. However, when we create a new file, the Unicode header must be written at the beginning of the +// file. This can't be guaranteed with multiple writers, so we require exclusive access while creating a new +// file. Our algorithm is first try to open with OPEN_EXISTING and FILE_SHARE_WRITE, and if that fails, try +// again with OPEN_ALWAYS and no write sharing. +HANDLE CircularLog::OpenFile() +{ + for (int i = 0; i < OPEN_FILE_RETRY_COUNT; i++) + { + // First try to open an existing file allowing shared write. + HANDLE hLogFile = WszCreateFile( + m_LogFilename.GetUnicode(), + FILE_APPEND_DATA, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (hLogFile != INVALID_HANDLE_VALUE) + { + return hLogFile; + } + + if (GetLastError() == ERROR_FILE_NOT_FOUND) + { + // Try to create an new file with exclusive access. + HANDLE hLogFile = WszCreateFile( + m_LogFilename.GetUnicode(), + FILE_APPEND_DATA, + FILE_SHARE_READ, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (hLogFile != INVALID_HANDLE_VALUE) + { + LARGE_INTEGER fileSize; + if (! GetFileSizeEx(hLogFile, &fileSize)) + { + CloseHandle(hLogFile); + return INVALID_HANDLE_VALUE; + } + + // If the file size is 0, need to write the Unicode header (utf8 bom). + if (fileSize.QuadPart == 0) + { + CHAR unicodeHeader []= {(char)0xef, (char)0xbb, (char)0xbf}; + DWORD dwWritten; + WriteFile(hLogFile, &unicodeHeader, sizeof(unicodeHeader), &dwWritten, NULL); + + // Write out header + + // Convert to Utf8 to reduce typical log file size + StackScratchBuffer bufUtf8; + COUNT_T cBytesUtf8 = 0; + const UTF8 * pszUtf8Log = m_LogHeader.GetUTF8(bufUtf8, &cBytesUtf8); + // Remove null terminator from log entry buffer + cBytesUtf8--; + + WriteFile(hLogFile, pszUtf8Log, (DWORD)cBytesUtf8, &dwWritten, NULL); + } + + return hLogFile; + } + } + + if (GetLastError() != ERROR_SHARING_VIOLATION) + { + break; + } + + ClrSleepEx(OPEN_FILE_RETRY_TIME, FALSE); + } + + return INVALID_HANDLE_VALUE; + +} diff --git a/src/utilcode/clrconfig.cpp b/src/utilcode/clrconfig.cpp new file mode 100644 index 0000000000..0b164694d4 --- /dev/null +++ b/src/utilcode/clrconfig.cpp @@ -0,0 +1,777 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// CLRConfig.cpp +// + +// +// Unified method of accessing configuration values from environment variables, +// registry and config file. See file:../inc/CLRConfigValues.h for details on how to add config values. +// +//***************************************************************************** + +#include "stdafx.h" +#include "clrconfig.h" + +#ifndef ERANGE +#define ERANGE 34 +#endif + +// +// Initialize the EEConfig::GetConfiguration function pointer to NULL. If EEConfig isn't init'ed, this will +// stay NULL and CLRConfig will ignore config files. +// +CLRConfig::GetConfigValueFunction CLRConfig::s_GetConfigValueCallback = NULL; + +// +// Initialize the PerformanceDefaults::LookupConfigValue function pointer to NULL. If not initialized, CLRConfig +// will ignore LookupOptions::MayHavePerformanceDefault. +// +CLRConfig::GetPerformanceDefaultValueFunction CLRConfig::s_GetPerformanceDefaultValueCallback = NULL; + +#ifdef FEATURE_WIN_DB_APPCOMPAT +PFN_CptQuirkIsEnabled3 CLRConfig::s_IsQuirkEnabledCallback = NULL; +PFN_CptQuirkGetData2 CLRConfig::s_GetQuirkValueCallback = NULL; +#endif + +// +// Creating structs using the macro table in CLRConfigValues.h +// + +// These macros intialize ConfigDWORDInfo structs. +#define RETAIL_CONFIG_DWORD_INFO(symbol, name, defaultValue, description) \ + const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, CLRConfig::EEConfig_default}; +#define RETAIL_CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) \ + const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, lookupOptions}; + +// These macros intialize ConfigStringInfo structs. +#define RETAIL_CONFIG_STRING_INFO(symbol, name, description) \ + const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::EEConfig_default}; +#define RETAIL_CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \ + const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, lookupOptions}; + +// TEMPORARY macros that intialize strings for config value accesses that haven't been moved over to +// CLRConfig yet. Once all accesses have been moved, these macros (and corresponding instantiations in +// file:../utilcode/CLRConfig.h) should be removed. +#define RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) \ + const LPCWSTR CLRConfig::symbol = name; +#define RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) \ + const LPCWSTR CLRConfig::symbol = name; +// +// Debug versions of the macros +// +#ifdef _DEBUG + #define CONFIG_DWORD_INFO(symbol, name, defaultValue, description) \ + const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, CLRConfig::EEConfig_default}; + #define CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) \ + const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, lookupOptions}; + #define CONFIG_STRING_INFO(symbol, name, description) \ + const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::EEConfig_default}; + #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \ + const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, lookupOptions}; + #define CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) \ + const LPCWSTR CLRConfig::symbol = name; + #define CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) \ + const LPCWSTR CLRConfig::symbol = name; +#else + #define CONFIG_DWORD_INFO(symbol, name, defaultValue, description) + #define CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) + #define CONFIG_STRING_INFO(symbol, name, description) + #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) + #define CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) + #define CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) +#endif // _DEBUG + + // Now that we have defined what what the macros in file:../inc/CLRConfigValues.h mean, include it to generate the code. + #include "clrconfigvalues.h" + +#undef RETAIL_CONFIG_DWORD_INFO +#undef RETAIL_CONFIG_STRING_INFO +#undef RETAIL_CONFIG_DWORD_INFO_EX +#undef RETAIL_CONFIG_STRING_INFO_EX +#undef RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS +#undef RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS +#undef CONFIG_DWORD_INFO +#undef CONFIG_STRING_INFO +#undef CONFIG_DWORD_INFO_EX +#undef CONFIG_STRING_INFO_EX +#undef CONFIG_DWORD_INFO_DIRECT_ACCESS +#undef CONFIG_STRING_INFO_DIRECT_ACCESS + + +#ifdef FEATURE_WIN_DB_APPCOMPAT + +#define MAX_QUIRK_LENGTH 60 +#define WIN_DB_COMPONENT_NAME W("NETFX.") +#define WIN_DB_COMPONENT_NAME_LENGTH 6 + +// queries the DB if the quirk is enabled. If the quirk is enabled then it also gets the value associated with the quirk. +// pass in quirkData as NULL if the value is not required and only enabled/disabled is needed. +// Length of quirk cannot be greater than 60. If it is greater than 60 then this api returns E_FAIL. +HRESULT CLRConfig::getQuirkEnabledAndValueFromWinDB(LPCWSTR wszQuirkName, BOOL* isEnabled, CPT_QUIRK_DATA* quirkData) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + PRECONDITION(CheckPointer(isEnabled, NULL_NOT_OK)); + } + CONTRACTL_END; + + if(wszQuirkName == NULL) + return E_FAIL; + + WCHAR wszCompleteQuirkName[MAX_QUIRK_LENGTH + WIN_DB_COMPONENT_NAME_LENGTH + 1]; + WCHAR wszComponentName[] = WIN_DB_COMPONENT_NAME; + size_t cchCompleteQuirkName = MAX_QUIRK_LENGTH + WIN_DB_COMPONENT_NAME_LENGTH + 1; + + _ASSERT(wcslen(wszComponentName) == WIN_DB_COMPONENT_NAME_LENGTH); + + size_t cchOrig = wcslen(wszQuirkName); + if(cchOrig > MAX_QUIRK_LENGTH) + { + return E_FAIL; + } + + // Create comlete name of the quirk. Windows expects complete quirkName i.e. componentName.quirkName + // eg. ETWEnabled will become NETFX.ETWEnabled + errno_t err = wcsncpy_s(wszCompleteQuirkName, cchCompleteQuirkName, wszComponentName, WIN_DB_COMPONENT_NAME_LENGTH); + if (err != 0) + { + return E_FAIL; + } + + err = wcscat_s(wszCompleteQuirkName, cchCompleteQuirkName, wszQuirkName); + if (err != 0) + { + return E_FAIL; + } + + + UINT32 version = 0xFFFFFFFF; + BOOL fIsEnabled; + //call windows api + // Version passed must be 0xFFFFFFFF for NETFX. Passing any other version requires more + // understanding of the windows API. + fIsEnabled = s_IsQuirkEnabledCallback(wszCompleteQuirkName,version); + + if(fIsEnabled && quirkData != NULL) + { + quirkData->Size = sizeof(CPT_QUIRK_DATA); + // Query for quirkData + if(!SUCCEEDED(s_GetQuirkValueCallback(wszCompleteQuirkName, quirkData))) + { + return E_FAIL; + } + } + + *isEnabled = fIsEnabled; + + return S_OK; +} + +#endif + + +// Return if a quirk is a enabled. +// This will also return enabled as true when the quirk has a value set. +BOOL CLRConfig::IsConfigEnabled(const ConfigDWORDInfo & info) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + SO_INTOLERANT; + } + CONTRACTL_END; + + DWORD result = info.defaultValue; + +#ifdef FEATURE_WIN_DB_APPCOMPAT + // Windows Shim DB should be the first place to look as it applies microsoft enforced policy + // and overrides setting at any other place like config or registry + if(CheckLookupOption(info, IgnoreWindowsQuirkDB) == FALSE && + s_IsQuirkEnabledCallback != NULL )// Check that IsQuirkEnabledCallback function has been registered. + { + BOOL enabledInDB = FALSE; + if(SUCCEEDED(getQuirkEnabledAndValueFromWinDB(info.name, &enabledInDB, NULL))) + { + if(enabledInDB) + { + return TRUE; + } + } + } +#endif + // + // Set up REGUTIL options. + // + REGUTIL::CORConfigLevel level = GetConfigLevel(info.options); + BOOL prependCOMPLUS = !CheckLookupOption(info, DontPrependCOMPLUS_); + + // + // If we aren't favoring config files, we check REGUTIL here. + // + if(CheckLookupOption(info, FavorConfigFile) == FALSE) + { + REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &result, level, prependCOMPLUS); + if(result>0) + return TRUE; + LPWSTR result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPLUS, level); + if(result != NULL && result[0] != 0) + { + return TRUE; + } + } + + // + // Check config files through EEConfig. + // + if(CheckLookupOption(info, IgnoreConfigFiles) == FALSE && // Check that we aren't ignoring config files. + s_GetConfigValueCallback != NULL)// Check that GetConfigValueCallback function has been registered. + { + LPCWSTR pvalue; + + // EEConfig lookup options. + BOOL systemOnly = CheckLookupOption(info, ConfigFile_SystemOnly) ? TRUE : FALSE; + BOOL applicationFirst = CheckLookupOption(info, ConfigFile_ApplicationFirst) ? TRUE : FALSE; + + if(SUCCEEDED(s_GetConfigValueCallback(info.name, &pvalue, systemOnly, applicationFirst)) && pvalue != NULL) + { + WCHAR * end; + errno = 0; + result = wcstoul(pvalue, &end, 0); + + // errno is ERANGE if the number is out of range, and end is set to pvalue if + // no valid conversion exists. + if (errno == ERANGE || end == pvalue) + { + if(pvalue[0]!=0) + return TRUE; + + result = info.defaultValue; + } + + if(result>0) + return TRUE; + } + } + + // + // If we are favoring config files and we don't have a result from EEConfig, we check REGUTIL here. + // + if(CheckLookupOption(info, FavorConfigFile) == TRUE) + { + REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &result, level, prependCOMPLUS); + if(result>0) + return TRUE; + LPWSTR result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPLUS, level); + if(result != NULL && result[0] != 0) + { + return TRUE; + } + } + + // + // If we get here, the option was not listed in REGUTIL or EEConfig; check whether the option + // has a PerformanceDefault-specified value before falling back to the built-in default + // + DWORD performanceDefaultValue; + if (CheckLookupOption(info, MayHavePerformanceDefault) && + s_GetPerformanceDefaultValueCallback != NULL && + s_GetPerformanceDefaultValueCallback(info.name, &performanceDefaultValue)) + { + if (!SUCCEEDED(REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &result, level, prependCOMPLUS))) + { + if(performanceDefaultValue>0) + return TRUE; + } + } + + if(info.defaultValue>0) + return TRUE; + else + return FALSE; +} + + + +// +// Look up a DWORD config value. +// +// Arguments: +// * info - see file:../inc/CLRConfig.h for details +// +// static +DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + SO_TOLERANT; // Need this to be tolerant to stack overflows since REGUTIL::GetConfigDWORD was too. (This replaces calls to REGUTIL::GetConfigDWORD) + } + CONTRACTL_END; + + DWORD result = info.defaultValue; + +#ifdef FEATURE_WIN_DB_APPCOMPAT + // Windows Shim DB should be the first place to look as it applies microsoft enforced policy + // and overrides setting at any other place like config or registry + if(CheckLookupOption(info, IgnoreWindowsQuirkDB) == FALSE && + s_IsQuirkEnabledCallback != NULL )// Check that IsQuirkEnabledCallback function has been registered. + { + + BOOL isEnabledInDB = FALSE; + CPT_QUIRK_DATA quirkData; + if(SUCCEEDED(getQuirkEnabledAndValueFromWinDB(info.name, &isEnabledInDB, &quirkData))) + { + if(isEnabledInDB) + { + WCHAR *end; + errno = 0; + result = wcstoul(quirkData.CommandLine, &end, 0); + + // errno is ERANGE if the number is out of range, and end is set to pvalue if + // no valid conversion exists. + if (errno != ERANGE && end != quirkData.CommandLine) + { + return result; + } + else + { + // If an invalid value is defined we treat it as the default value. + // i.e. we don't look further. + return info.defaultValue; + } + } + } + } +#endif // FEATURE_WIN_DB_APPCOMPAT + + // + // Set up REGUTIL options. + // + REGUTIL::CORConfigLevel level = GetConfigLevel(info.options); + BOOL prependCOMPLUS = !CheckLookupOption(info, DontPrependCOMPLUS_); + + // + // If we aren't favoring config files, we check REGUTIL here. + // + if(CheckLookupOption(info, FavorConfigFile) == FALSE) + { + REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &result, level, prependCOMPLUS); + // TODO: We are ignoring explicitly defined default values to avoid change in behavior. + // TODO: Ideally, the following should check the hresult for success. + if(result != info.defaultValue) + { + return result; + } + } + + // + // Check config files through EEConfig. + // + if(CheckLookupOption(info, IgnoreConfigFiles) == FALSE && // Check that we aren't ignoring config files. + s_GetConfigValueCallback != NULL)// Check that GetConfigValueCallback function has been registered. + { + LPCWSTR pvalue; + + // EEConfig lookup options. + BOOL systemOnly = CheckLookupOption(info, ConfigFile_SystemOnly) ? TRUE : FALSE; + BOOL applicationFirst = CheckLookupOption(info, ConfigFile_ApplicationFirst) ? TRUE : FALSE; + + if(SUCCEEDED(s_GetConfigValueCallback(info.name, &pvalue, systemOnly, applicationFirst)) && pvalue != NULL) + { + WCHAR * end; + errno = 0; + result = wcstoul(pvalue, &end, 0); + + // errno is ERANGE if the number is out of range, and end is set to pvalue if + // no valid conversion exists. + if (errno != ERANGE && end != pvalue) + { + return result; + } + else + { + // If an invalid value is defined we treat it as the default value. + // i.e. we don't look further. + return info.defaultValue; + } + } + } + + // + // If we are favoring config files and we don't have a result from EEConfig, we check REGUTIL here. + // + if(CheckLookupOption(info, FavorConfigFile) == TRUE) + { + REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &result, level, prependCOMPLUS); + // TODO: We are ignoring explicitly defined default values to avoid change in behavior. + // TODO: Ideally, the following should check the hresult for success. + if(result != info.defaultValue) + { + return result; + } + } + + // + // If we get here, the option was not listed in REGUTIL or EEConfig; check whether the option + // has a PerformanceDefault-specified value before falling back to the built-in default + // + DWORD performanceDefaultValue; + if (CheckLookupOption(info, MayHavePerformanceDefault) && + s_GetPerformanceDefaultValueCallback != NULL && + s_GetPerformanceDefaultValueCallback(info.name, &performanceDefaultValue)) + { + // TODO: We ignore explicitly defined default values above, but we do not want to let performance defaults override these. + // TODO: Ideally, the above would use hresult for success and this check would be removed. + if (!SUCCEEDED(REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &result, level, prependCOMPLUS))) + return performanceDefaultValue; + } + + return info.defaultValue; +} + +// +// Look up a String config value. +// +// Arguments: +// * info - see file:../inc/CLRConfig.h for details +// +// Return value: +// * Pointer to the string value, if found. You own the string that's returned. Returns NULL if the value +// is not found. +// +// static +LPWSTR CLRConfig::GetConfigValue(const ConfigStringInfo & info) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + } + CONTRACTL_END; + + LPWSTR result = NULL; + + // TODO: We swallow OOM exception here. Is this OK? + FAULT_NOT_FATAL(); + + // If this fails, result will stay NULL. + GetConfigValue(info, &result); + + return result; +} + +// +// Look up a string config value, passing it out through a pointer reference. +// +// Return value: +// * Reports out of memory errors (HRESULT E_OUTOFMEMORY). +// +// Arguments: +// * info - see file:../inc/CLRConfig.h for details +// * outVal - Set to the result string. You own the string that's returned. Set to NULL if the value is +// not found. +// +// static +HRESULT CLRConfig::GetConfigValue(const ConfigStringInfo & info, __deref_out_z LPWSTR * outVal) +{ + CONTRACT(HRESULT) { + NOTHROW; + GC_NOTRIGGER; + INJECT_FAULT (CONTRACT_RETURN E_OUTOFMEMORY); + POSTCONDITION(CheckPointer(outVal, NULL_OK)); // TODO: Should this check be *outVal instead of outVal? + } CONTRACT_END; + + LPWSTR result = NULL; + +#ifdef FEATURE_WIN_DB_APPCOMPAT + // Windows Shim DB should be the first place to look as it applies microsoft enforced policy + // and overrides setting at any other place like config or registry + if(CheckLookupOption(info, IgnoreWindowsQuirkDB) == FALSE && + s_IsQuirkEnabledCallback != NULL )// Check that IsQuirkEnabledCallback function has been registered. + { + + BOOL isEnabledInDB = FALSE; + CPT_QUIRK_DATA quirkData; + if(SUCCEEDED(getQuirkEnabledAndValueFromWinDB(info.name, &isEnabledInDB, &quirkData))) + { + if(isEnabledInDB) + { + size_t len = wcslen(quirkData.CommandLine) + 1; + result = new (nothrow) WCHAR[len]; + if (result == NULL) + { + RETURN E_OUTOFMEMORY; + } + wcscpy_s(result, len, quirkData.CommandLine); + } + } + } +#endif // FEATURE_WIN_DB_APPCOMPAT + + // + // Set up REGUTIL options. + // + REGUTIL::CORConfigLevel level = GetConfigLevel(info.options); + BOOL prependCOMPLUS = !CheckLookupOption(info, DontPrependCOMPLUS_); + + // + // If we aren't favoring config files, we check REGUTIL here. + // + if(result == NULL && CheckLookupOption(info, FavorConfigFile) == FALSE) + { + result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPLUS, level); + } + + // + // Check config files through EEConfig. + // + if(result == NULL && // Check that we don't have a value from REGUTIL + CheckLookupOption(info, IgnoreConfigFiles) == FALSE && // Check that we aren't ignoring config files. + s_GetConfigValueCallback != NULL) // Check that GetConfigValueCallback function has been registered. + { + LPCWSTR pResult; + + // EEConfig lookup options. + BOOL systemOnly = CheckLookupOption(info, ConfigFile_SystemOnly) ? TRUE : FALSE; + BOOL applicationFirst = CheckLookupOption(info, ConfigFile_ApplicationFirst) ? TRUE : FALSE; + + if(SUCCEEDED(s_GetConfigValueCallback(info.name, &pResult, systemOnly, applicationFirst)) && pResult != NULL) + { + size_t len = wcslen(pResult) + 1; + result = new (nothrow) WCHAR[len]; + if (result == NULL) + { + RETURN E_OUTOFMEMORY; + } + wcscpy_s(result, len, pResult); + } + } + + // + // If we are favoring config files and we don't have a result from EEConfig, we check REGUTIL here. + // + if(result==NULL && + CheckLookupOption(info, FavorConfigFile) == TRUE) + { + result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPLUS, level); + } + + if ((result != NULL) && CheckLookupOption(info, TrimWhiteSpaceFromStringValue)) + { + // If this fails, result remains untouched, so we'll just return the untrimmed + // value. + LPWSTR wszTrimmedResult = NULL; + if (SUCCEEDED(TrimWhiteSpace(result, &wszTrimmedResult)) && + (wszTrimmedResult != NULL)) + { + // wszTrimmedResult should be the result we return. Delete the untrimmed + // result. + delete [] result; + result = wszTrimmedResult; + } + } + + // If we ever want a PerformanceDefault for a string value, you can replace this assert + // with code that follows the pattern for DWORD values above. + _ASSERTE(!CheckLookupOption(info, MayHavePerformanceDefault)); + + *outVal = result; + RETURN S_OK; +} + +// +// Check whether an option is specified (e.g. explicitly listed) in any location +// +// Arguments: +// * name - the name field of the desired ConfigDWORDInfo/ConfigStringInfo +// +// static +BOOL CLRConfig::IsConfigOptionSpecified(LPCWSTR name) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + // Check config files + { + LPCWSTR result = NULL; + + if (s_GetConfigValueCallback != NULL && + SUCCEEDED(s_GetConfigValueCallback(name, &result, FALSE, FALSE)) && + result != NULL) + { + return TRUE; + } + } + + // Check REGUTIL, both with and without the COMPLUS_ prefix + { + LPWSTR result = NULL; + + result = REGUTIL::GetConfigString_DontUse_(name, TRUE); + if (result != NULL) + { + FreeConfigString(result); + return TRUE; + } + + result = REGUTIL::GetConfigString_DontUse_(name, FALSE); + if (result != NULL) + { + FreeConfigString(result); + return TRUE; + } + + } + + return FALSE; +} + +//--------------------------------------------------------------------------------------- +// +// Given an input string, returns a newly-allocated string equal to the input but with +// leading and trailing whitespace trimmed off. If input is already trimmed, or if +// trimming would result in an empty string, this function sets the output string to NULL +// +// Caller must free *pwszTrimmed if non-NULL +// +// Arguments: +// * wszOrig - String to trim +// * pwszTrimmed - [out]: On return, points to newly allocated, trimmed string (or +// NULL) +// +// Return Value: +// HRESULT indicating success or failure. +// +HRESULT CLRConfig::TrimWhiteSpace(LPCWSTR wszOrig, __deref_out_z LPWSTR * pwszTrimmed) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + _ASSERTE(wszOrig != NULL); + _ASSERTE(pwszTrimmed != NULL); + + // In case we return early, set [out] to NULL by default + *pwszTrimmed = NULL; + + // Get pointers into internal string that show where to do the trimming. + size_t cchOrig = wcslen(wszOrig); + if (!FitsIn<DWORD>(cchOrig)) + return COR_E_OVERFLOW; + DWORD cchAfterTrim = (DWORD) cchOrig; + LPCWSTR wszAfterTrim = wszOrig; + ::TrimWhiteSpace(&wszAfterTrim, &cchAfterTrim); + + // Is input string already trimmed? If so, save an allocation and just return. + if ((wszOrig == wszAfterTrim) && (cchOrig == cchAfterTrim)) + { + // Yup, just return success + return S_OK; + } + + if (cchAfterTrim == 0) + { + // After trimming, there's nothing left, so just return NULL + return S_OK; + } + + // Create a new buffer to hold a copy of the trimmed string. Caller will be + // responsible for this buffer if we return it. + NewArrayHolder<WCHAR> wszTrimmedCopy(new (nothrow) WCHAR[cchAfterTrim + 1]); + if (wszTrimmedCopy == NULL) + { + return E_OUTOFMEMORY; + } + + errno_t err = wcsncpy_s(wszTrimmedCopy, cchAfterTrim + 1, wszAfterTrim, cchAfterTrim); + if (err != 0) + { + return E_FAIL; + } + + // Successfully made a copy of the trimmed string. Return it. Caller will be responsible for + // deleting it. + wszTrimmedCopy.SuppressRelease(); + *pwszTrimmed = wszTrimmedCopy; + return S_OK; +} + + +// +// Deallocation function for code:CLRConfig::FreeConfigString +// +void CLRConfig::FreeConfigString(__in_z LPWSTR str) +{ + LIMITED_METHOD_CONTRACT; + + delete [] str; +} + +// +// Register EEConfig's GetConfigValueCallback function so CLRConfig can look in config files. +// +//static +void CLRConfig::RegisterGetConfigValueCallback(GetConfigValueFunction func) +{ + LIMITED_METHOD_CONTRACT; + s_GetConfigValueCallback = func; +} + +// +// Register PerformanceDefaults' LookupConfigValue so CLRConfig can support 'MayHavePerformanceDefault' values +// +//static +void CLRConfig::RegisterGetPerformanceDefaultValueCallback(GetPerformanceDefaultValueFunction func) +{ + LIMITED_METHOD_CONTRACT; + s_GetPerformanceDefaultValueCallback = func; +} + +#ifdef FEATURE_WIN_DB_APPCOMPAT +void CLRConfig::RegisterWinDbQuirkApis(PFN_CptQuirkIsEnabled3 func1, PFN_CptQuirkGetData2 func2) +{ + LIMITED_METHOD_CONTRACT; + s_IsQuirkEnabledCallback = func1; + s_GetQuirkValueCallback = func2; +} +#endif // FEATURE_WIN_DB_APPCOMPAT + +// +// Helper method to translate LookupOptions to REGUTIL::CORConfigLevel. +// +//static +REGUTIL::CORConfigLevel CLRConfig::GetConfigLevel(LookupOptions options) +{ + LIMITED_METHOD_CONTRACT; + + REGUTIL::CORConfigLevel level = (REGUTIL::CORConfigLevel) 0; + + if(CheckLookupOption(options, IgnoreEnv) == FALSE) + level = static_cast<REGUTIL::CORConfigLevel>(level | REGUTIL::COR_CONFIG_ENV); + + if(CheckLookupOption(options, IgnoreHKCU) == FALSE) + level = static_cast<REGUTIL::CORConfigLevel>(level | REGUTIL::COR_CONFIG_USER); + + if(CheckLookupOption(options, IgnoreHKLM) == FALSE) + level = static_cast<REGUTIL::CORConfigLevel>(level | REGUTIL::COR_CONFIG_MACHINE); + + return level; +} diff --git a/src/utilcode/clrhelpers.cpp b/src/utilcode/clrhelpers.cpp new file mode 100644 index 0000000000..498d3ad128 --- /dev/null +++ b/src/utilcode/clrhelpers.cpp @@ -0,0 +1,14 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// +// This is file to compile 2 public "headers" from ..\inc directory (we have to include stdafx.h first). +// +/***************************************************************************/ + +#include "stdafx.h" + +#include "../inc/corhlpr.cpp" +#include "../inc/corhlprpriv.cpp" diff --git a/src/utilcode/clrhost.cpp b/src/utilcode/clrhost.cpp new file mode 100644 index 0000000000..6419989a75 --- /dev/null +++ b/src/utilcode/clrhost.cpp @@ -0,0 +1,460 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// + +// + +#include "stdafx.h" + +#include "unsafe.h" +#include "clrhost.h" +#include "utilcode.h" +#include "ex.h" +#include "hostimpl.h" +#include "clrnt.h" +#include "contract.h" +#include "tls.h" + +#if defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE) || defined(CROSSGEN_COMPILE) +CoreClrCallbacks g_CoreClrCallbacks; +#endif + + +// In some cirumstance (e.g, the thread suspecd another thread), allocation on heap +// could cause dead lock. We use a counter in TLS to indicate the current thread is not allowed +// to do heap allocation. +//In cases where CLRTlsInfo doesn't exist and we still want to track CantAlloc info (this is important to +//stress log), We use a global counter. This introduces the problem where one thread could disable allocation +//for another thread, but the cases should be rare (we limit the use to stress log for now) and the period +//should (MUST) be short +//only stress log check this counter + +struct CantAllocThread +{ + PVOID m_fiberId; + LONG m_CantCount; +}; + +#define MaxCantAllocThreadNum 100 +static CantAllocThread g_CantAllocThreads[MaxCantAllocThreadNum] = {}; +static Volatile<LONG> g_CantAllocStressLogCount = 0; + +void IncCantAllocCount() +{ + size_t count = 0; + if (ClrFlsCheckValue(TlsIdx_CantAllocCount, (LPVOID *)&count)) + { + _ASSERTE (count >= 0); + ClrFlsSetValue(TlsIdx_CantAllocCount, (LPVOID)(count+1)); + return; + } + PVOID fiberId = ClrTeb::GetFiberPtrId(); + for (int i = 0; i < MaxCantAllocThreadNum; i ++) + { + if (g_CantAllocThreads[i].m_fiberId == fiberId) + { + g_CantAllocThreads[i].m_CantCount ++; + return; + } + } + for (int i = 0; i < MaxCantAllocThreadNum; i ++) + { + if (g_CantAllocThreads[i].m_fiberId == NULL) + { + if (InterlockedCompareExchangeT(&g_CantAllocThreads[i].m_fiberId, fiberId, NULL) == NULL) + { + _ASSERTE(g_CantAllocThreads[i].m_CantCount == 0); + g_CantAllocThreads[i].m_CantCount = 1; + return; + } + } + } + count = InterlockedIncrement (&g_CantAllocStressLogCount); + _ASSERTE (count >= 1); + return; +} + +void DecCantAllocCount() +{ + size_t count = 0; + if (ClrFlsCheckValue(TlsIdx_CantAllocCount, (LPVOID *)&count)) + { + if (count > 0) + { + ClrFlsSetValue(TlsIdx_CantAllocCount, (LPVOID)(count-1)); + return; + } + } + PVOID fiberId = ClrTeb::GetFiberPtrId(); + for (int i = 0; i < MaxCantAllocThreadNum; i ++) + { + if (g_CantAllocThreads[i].m_fiberId == fiberId) + { + _ASSERTE (g_CantAllocThreads[i].m_CantCount > 0); + g_CantAllocThreads[i].m_CantCount --; + if (g_CantAllocThreads[i].m_CantCount == 0) + { + g_CantAllocThreads[i].m_fiberId = NULL; + } + return; + } + } + _ASSERTE (g_CantAllocStressLogCount > 0); + InterlockedDecrement (&g_CantAllocStressLogCount); + return; + +} + +// for stress log the rule is more restrict, we have to check the global counter too +BOOL IsInCantAllocStressLogRegion() +{ + size_t count = 0; + if (ClrFlsCheckValue(TlsIdx_CantAllocCount, (LPVOID *)&count)) + { + if (count > 0) + { + return true; + } + } + PVOID fiberId = ClrTeb::GetFiberPtrId(); + for (int i = 0; i < MaxCantAllocThreadNum; i ++) + { + if (g_CantAllocThreads[i].m_fiberId == fiberId) + { + _ASSERTE (g_CantAllocThreads[i].m_CantCount > 0); + return true; + } + } + + return g_CantAllocStressLogCount > 0; + +} + + +#ifdef FAILPOINTS_ENABLED +typedef int (*FHashStack) (); + +static FHashStack fHashStack = 0; +static _TEB *HashStackSetupThread = NULL; +static _TEB *RFSCustomDataSetupThread = NULL; + +static void SetupHashStack () +{ + CANNOT_HAVE_CONTRACT; + + FHashStack oldValue = InterlockedCompareExchangeT(&fHashStack, + reinterpret_cast<FHashStack>(1), reinterpret_cast<FHashStack>(0)); + if ((size_t) oldValue >= 2) { + return; + } + else if ((size_t) oldValue == 0) { + // We are the first thread to initialize + HashStackSetupThread = NtCurrentTeb(); + + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HashStack) == 0) { + fHashStack = (FHashStack) 2; + return; + } + + PAL_TRY(void *, unused, NULL) { + FHashStack func; + HMODULE hmod = LoadLibraryExA ("mscorrfs.dll", NULL, 0); + if (hmod) { + func = (FHashStack)GetProcAddress (hmod, "HashStack"); + if (func == 0) { + func = (FHashStack)2; + } + } + else + func = (FHashStack)2; + fHashStack = func; + } + PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + fHashStack = (FHashStack) 2; + } + PAL_ENDTRY; + } + else if (NtCurrentTeb() == HashStackSetupThread) { + // We get here while initializing + return; + } + else { + // All other threads will wait + while (fHashStack == (FHashStack) 1) { + ClrSleepEx (100, FALSE); + } + } +} + +int RFS_HashStack () +{ + CANNOT_HAVE_CONTRACT; + + if ((size_t)fHashStack < 2) { + SetupHashStack (); + } + + if ((size_t)fHashStack <= 2) { + return 0; + } + else + return fHashStack (); +} + +#endif // FAILPOINTS_ENABLED + + +#if defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE) + +//----------------------------------------------------------------------------------- +// This is the approved way to get a module handle to mscorwks.dll (or coreclr.dll). +// Never call GetModuleHandle(mscorwks) yourself as this will break side-by-side inproc. +// +// This function is safe to call before or during CRT initialization. It can not +// legally return NULL (it only does so in the case of a broken build invariant.) +// +// TODO puCLR SxS utilcode work: Since this is never supposed to return NULL, it should +// not be present in SELF_NO_HOST builds of utilcode where there isn't necessarily a +// CLR in the process. We should also ASSERT that GetModuleHandleA isn't returning +// NULL below - we've probably been getting away with this in SELF_NO_HOST cases like +// mscordbi.dll. +//----------------------------------------------------------------------------------- +HMODULE GetCLRModule () +{ + //! WARNING: At the time this function is invoked, the C Runtime has NOT been fully initialized, let alone the CLR. + //! So don't put in a runtime contract and don't invoke other functions in the CLR (not even _ASSERTE!) + + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_SO_TOLERANT; + STATIC_CONTRACT_SUPPORTS_DAC; // DAC can call in here since we initialize the SxS callbacks in ClrDataAccess::Initialize. + +#ifdef DACCESS_COMPILE + // For DAC, "g_CoreClrCallbacks" is populated in InitUtilCode when the latter is invoked + // from ClrDataAccess::Initialize alongwith a reference to a structure allocated in the + // host-process address space. + // + // This function will be invoked in the host when DAC uses SEHException::GetHr that calls into + // IsComplusException, which calls into WasThrownByUs that calls GetCLRModule when EH SxS is enabled. + // However, this function can also be executed within the target space as well. + // + // Since DACCop gives the warning to DACize this global that, actually, would be used only + // in the respective address spaces and does not require marshalling, we need to ignore this + // warning. + DACCOP_IGNORE(UndacizedGlobalVariable, "g_CoreClrCallbacks has the dual mode DAC issue."); +#endif // DACCESS_COMPILE + VALIDATECORECLRCALLBACKS(); + + // This is the normal coreclr case - we return the module handle that was captured in our DllMain. +#ifdef DACCESS_COMPILE + // For DAC, "g_CoreClrCallbacks" is populated in InitUtilCode when the latter is invoked + // from ClrDataAccess::Initialize alongwith a reference to a structure allocated in the + // host-process address space. + // + // This function will be invoked in the host when DAC uses SEHException::GetHr that calls into + // IsComplusException, which calls into WasThrownByUs that calls GetCLRModule when EH SxS is enabled. + // However, this function can also be executed within the target space as well. + // + // Since DACCop gives the warning to DACize this global that, actually, would be used only + // in the respective address spaces and does not require marshalling, we need to ignore this + // warning. + DACCOP_IGNORE(UndacizedGlobalVariable, "g_CoreClrCallbacks has the dual mode DAC issue."); +#endif // DACCESS_COMPILE + return g_CoreClrCallbacks.m_hmodCoreCLR; +} + +#endif // defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE) + + +#if defined(SELF_NO_HOST) + +HMODULE CLRLoadLibrary(LPCWSTR lpLibFileName) +{ + WRAPPER_NO_CONTRACT; + return CLRLoadLibraryEx(lpLibFileName, NULL, 0); +} + +HMODULE CLRLoadLibraryEx(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) +{ + WRAPPER_NO_CONTRACT; + return WszLoadLibraryEx(lpLibFileName, hFile, dwFlags); +} + +BOOL CLRFreeLibrary(HMODULE hModule) +{ + WRAPPER_NO_CONTRACT; + return FreeLibrary(hModule); +} + +#endif // defined(SELF_NO_HOST) + + +#if defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL) + +//----------------------------------------------------------------------------------------------- +// Imposes a new typeload level limit for the scope of the holder. Any attempt to load a type +// past that limit generates a contract violation assert. +// +// Do not invoke this directly. Invoke it through TRIGGERS_TYPE_LOAD or OVERRIDE_TYPE_LOAD_LEVEL_LIMIT. +// +// Arguments: +// fConditional - if FALSE, this holder is a nop - supports the MAYBE_* macros. +// newLevel - a value from classloadlevel.h - specifies the new max limit. +// fEnforceLevelChangeDirection +// - if true, implements TRIGGERS_TYPE_LOAD (level cap only allowed to decrease.) +// if false, implements OVERRIDE (level allowed to increase - may only be used +// by loader and only when recursion is structurally +// impossible.) +// szFunction, +// szFile, +// lineNum - records location of holder so we can print it in assertion boxes +// +// Assumptions: +// ClrDebugState must have been set up (executing any contract will do this.) +// Thread need *not* have a Thread* structure set up. +// +// Notes: +// The holder withholds the assert if a LoadsTypeViolation suppress is in effect (but +// still sets up the new limit.) +// +// As with other contract annoations, however, the violation suppression is *lifted* +// within the scope guarded by the holder itself. +//----------------------------------------------------------------------------------------------- +LoadsTypeHolder::LoadsTypeHolder(BOOL fConditional, + UINT newLevel, + BOOL fEnforceLevelChangeDirection, + const char *szFunction, + const char *szFile, + int lineNum + ) +{ + // This fcn makes non-scoped changes to ClrDebugState so we cannot use a runtime CONTRACT here. + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + + m_fConditional = fConditional; + if (m_fConditional) + { + m_pClrDebugState = CheckClrDebugState(); + _ASSERTE(m_pClrDebugState); + + m_oldClrDebugState = *m_pClrDebugState; + + if (fEnforceLevelChangeDirection) + { + if (newLevel > m_pClrDebugState->GetMaxLoadTypeLevel()) + { + if (!( (LoadsTypeViolation|BadDebugState) & m_pClrDebugState->ViolationMask())) + { + CONTRACT_ASSERT("Illegal attempt to load a type beyond the current level limit.", + (m_pClrDebugState->GetMaxLoadTypeLevel() + 1) << Contract::LOADS_TYPE_Shift, + Contract::LOADS_TYPE_Mask, + szFunction, + szFile, + lineNum + ); + } + } + } + + m_pClrDebugState->ViolationMaskReset(LoadsTypeViolation); + m_pClrDebugState->SetMaxLoadTypeLevel(newLevel); + + m_contractStackRecord.m_szFunction = szFunction; + m_contractStackRecord.m_szFile = szFile; + m_contractStackRecord.m_lineNum = lineNum; + m_contractStackRecord.m_testmask = (Contract::ALL_Disabled & ~((UINT)(Contract::LOADS_TYPE_Mask))) | (((newLevel) + 1) << Contract::LOADS_TYPE_Shift); + m_contractStackRecord.m_construct = fEnforceLevelChangeDirection ? "TRIGGERS_TYPE_LOAD" : "OVERRIDE_TYPE_LOAD_LEVEL_LIMIT"; + m_contractStackRecord.m_pNext = m_pClrDebugState->GetContractStackTrace(); + m_pClrDebugState->SetContractStackTrace(&m_contractStackRecord); + + + } +} // LoadsTypeHolder::LoadsTypeHolder + +//----------------------------------------------------------------------------------------------- +// Restores prior typeload level limit. +//----------------------------------------------------------------------------------------------- +LoadsTypeHolder::~LoadsTypeHolder() +{ + // This fcn makes non-scoped changes to ClrDebugState so we cannot use a runtime CONTRACT here. + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + + if (m_fConditional) + { + *m_pClrDebugState = m_oldClrDebugState; + } +} + +#endif //defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL) + + +//-------------------------------------------------------------------------- +// Side by side inproc support +// +// These are new abstractions designed to support loading multiple CLR +// versions in the same process. +//-------------------------------------------------------------------------- + +#if defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE) + +//-------------------------------------------------------------------------- +// One-time initialized called by coreclr.dll in its dllmain. +//-------------------------------------------------------------------------- +VOID InitUtilcode(CoreClrCallbacks const & cccallbacks) +{ + //! WARNING: At the time this function is invoked, the C Runtime has NOT been fully initialized, let alone the CLR. + //! So don't put in a runtime contract and don't invoke other functions in the CLR (not even _ASSERTE!) + + LIMITED_METHOD_CONTRACT; + + g_CoreClrCallbacks = cccallbacks; +} + +CoreClrCallbacks const & GetClrCallbacks() +{ + LIMITED_METHOD_CONTRACT; + + VALIDATECORECLRCALLBACKS(); + return g_CoreClrCallbacks; +} + +#ifdef _DEBUG +void OnUninitializedCoreClrCallbacks() +{ + // Supports DAC since it can be called from GetCLRModule which supports DAC as well. + LIMITED_METHOD_DAC_CONTRACT; + + // If you got here, the most likely cause of the failure is that you're loading some DLL + // (other than coreclr.dll) that links to utilcode.lib, or that you're using a nohost + // variant of utilcode.lib but hitting code that assumes there is a CLR in the process. + // + // Under FEATURE_CORECLR (and not SELF_NO_HOST), it is expected that coreclr.dll + // is the ONLY dll that links to utilcode libraries. + // + // If you must introduce a new dll that links to utilcode.lib, it is your responsibility + // to ensure that that dll invoke InitUtilcode() and forward it the right data from the *correct* + // loaded instance of coreclr. And you'll have to do without the CRT being initialized. + // + // Can't use an _ASSERTE here because even that's broken if we get to this point. + MessageBoxW(0, + W("g_CoreClrCallbacks not initialized."), + W("\n\n") + W("You got here because the dll that included this copy of utilcode.lib ") + W("did not call InitUtilcode() The most likely cause is that you're running ") + W("a dll (other than coreclr.dll) that links to utilcode.lib.") + , + 0); + _ASSERTE(FALSE); + DebugBreak(); +} +#endif // _DEBUG + +#endif // defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE) diff --git a/src/utilcode/clrhost_nodependencies.cpp b/src/utilcode/clrhost_nodependencies.cpp new file mode 100644 index 0000000000..f5e4133cee --- /dev/null +++ b/src/utilcode/clrhost_nodependencies.cpp @@ -0,0 +1,958 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// + +// + +#include "stdafx.h" + +#include "unsafe.h" +#include "clrhost.h" +#include "utilcode.h" +#include "ex.h" +#include "hostimpl.h" +#include "clrnt.h" +#include "contract.h" +#include "tls.h" + +#ifdef _DEBUG_IMPL + +// +// I'd very much like for this to go away. Its used to disable all THROWS contracts within whatever DLL this +// function is called from. That's obviously very, very bad, since there's no validation of those macros. But it +// can be difficult to remove this without actually fixing every violation at the same time. +// +// When this flag is finally removed, remove RealCLRThrowsExceptionWorker() too and put CONTRACT_THROWS() in place +// of it. +// +// +static BOOL dbg_fDisableThrowCheck = FALSE; + +void DisableThrowCheck() +{ + LIMITED_METHOD_CONTRACT; + + dbg_fDisableThrowCheck = TRUE; +} + +#define CLRThrowsExceptionWorker() RealCLRThrowsExceptionWorker(__FUNCTION__, __FILE__, __LINE__) + +static void RealCLRThrowsExceptionWorker(__in_z const char *szFunction, + __in_z const char *szFile, + int lineNum) +{ + WRAPPER_NO_CONTRACT; + + if (dbg_fDisableThrowCheck) + { + return; + } + + CONTRACT_THROWSEX(szFunction, szFile, lineNum); +} + +#endif //_DEBUG_IMPL + +#if defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL) + +// Fls callback to deallocate ClrDebugState when our FLS block goes away. +void FreeClrDebugState(LPVOID pTlsData) +{ +#ifdef _DEBUG + ClrDebugState *pClrDebugState = (ClrDebugState*)pTlsData; + + // Make sure the ClrDebugState was initialized by a compatible version of + // utilcode.lib. If it was initialized by an older version, we just let it leak. + if (pClrDebugState && (pClrDebugState->ViolationMask() & CanFreeMe) && !(pClrDebugState->ViolationMask() & BadDebugState)) + { +#undef HeapFree +#undef GetProcessHeap + + // Since "!(pClrDebugState->m_violationmask & BadDebugState)", we know we have + // a valid m_pLockData + _ASSERTE(pClrDebugState->GetDbgStateLockData() != NULL); + ::HeapFree (GetProcessHeap(), 0, pClrDebugState->GetDbgStateLockData()); + + ::HeapFree (GetProcessHeap(), 0, pClrDebugState); +#define HeapFree(hHeap, dwFlags, lpMem) Dont_Use_HeapFree(hHeap, dwFlags, lpMem) +#define GetProcessHeap() Dont_Use_GetProcessHeap() + } +#endif //_DEBUG +} + +// This is a drastic shutoff toggle that forces all new threads to fail their CLRInitDebugState calls. +// We only invoke this if FLS can't allocate its master block, preventing us from tracking the shutoff +// on a per-thread basis. +BYTE* GetGlobalContractShutoffFlag() +{ +#ifdef SELF_NO_HOST + + static BYTE gGlobalContractShutoffFlag = 0; + return &gGlobalContractShutoffFlag; +#else //!SELF_NO_HOST + HINSTANCE hmod = GetCLRModule(); + if (!hmod) + { + return NULL; + } + typedef BYTE*(__stdcall * PGETSHUTOFFADDRFUNC)(); + PGETSHUTOFFADDRFUNC pGetContractShutoffFlagFunc = (PGETSHUTOFFADDRFUNC)GetProcAddress(hmod, "GetAddrOfContractShutoffFlag"); + if (!pGetContractShutoffFlagFunc) + { + return NULL; + } + return pGetContractShutoffFlagFunc(); +#endif //!SELF_NO_HOST +} + +static BOOL AreContractsShutoff() +{ + BYTE *pShutoff = GetGlobalContractShutoffFlag(); + if (!pShutoff) + { + return FALSE; + } + else + { + return 0 != *pShutoff; + } +} + +static VOID ShutoffContracts() +{ + BYTE *pShutoff = GetGlobalContractShutoffFlag(); + if (pShutoff) + { + *pShutoff = 1; + } +} + +//============================================================================================= +// Used to initialize the per-thread ClrDebugState. This is called once per thread (with +// possible exceptions for OOM scenarios.) +// +// No matter what, this function will not return NULL. If it can't do its job because of OOM reasons, +// it will return a pointer to &gBadClrDebugState which effectively disables contracts for +// this thread. +//============================================================================================= +ClrDebugState *CLRInitDebugState() +{ + // workaround! + // + // The existing Fls apis didn't provide the support we need and adding support cleanly is + // messy because of the brittleness of IExecutionEngine. + // + // To understand this function, you need to know that the Fls routines have special semantics + // for the TlsIdx_ClrDebugState slot: + // + // - FlsSetValue will never throw. If it fails due to OOM on creation of the slot storage, + // it will silently bail. Thus, we must do a confirming FlsGetValue before we can conclude + // that the SetValue succeeded. + // + // - FlsAssociateCallback will not complain about multiple sets of the callback. + // + // - The mscorwks implemention of FlsAssociateCallback will ignore the passed in value + // and use the version of FreeClrDebugState compiled into mscorwks. This is needed to + // avoid dangling pointer races on shutdown. + + + // This is our global "bad" debug state that thread use when they OOM on CLRInitDebugState. + // We really only need to initialize it once but initializing each time is convenient + // and has low perf impact. + static ClrDebugState gBadClrDebugState; + gBadClrDebugState.ViolationMaskSet( AllViolation ); + // SO_INFRASTRUCTURE_CODE() Macro to remove SO infrastructure code during build + SO_INFRASTRUCTURE_CODE(gBadClrDebugState.BeginSOTolerant();) + gBadClrDebugState.SetOkToThrow(); + + ClrDebugState *pNewClrDebugState = NULL; + ClrDebugState *pClrDebugState = NULL; + DbgStateLockData *pNewLockData = NULL; + + // We call this first partly to force a CheckThreadState. We've hopefully chased out all the + // recursive contract calls inside here but if we haven't, it's best to get them out of the way + // early. + ClrFlsAssociateCallback(TlsIdx_ClrDebugState, FreeClrDebugState); + + + if (AreContractsShutoff()) + { + pNewClrDebugState = NULL; + } + else + { + // Yuck. We cannot call the hosted allocator for ClrDebugState (it is impossible to maintain a guarantee + // that none of code paths, many of them called conditionally, don't themselves trigger a ClrDebugState creation.) + // We have to call the OS directly for this. +#undef HeapAlloc +#undef GetProcessHeap + pNewClrDebugState = (ClrDebugState*)::HeapAlloc(GetProcessHeap(), 0, sizeof(ClrDebugState)); + if (pNewClrDebugState != NULL) + { + // Only allocate a DbgStateLockData if its owning ClrDebugState was successfully alloctaed + pNewLockData = (DbgStateLockData *)::HeapAlloc(GetProcessHeap(), 0, sizeof(DbgStateLockData)); + } +#define GetProcessHeap() Dont_Use_GetProcessHeap() +#define HeapAlloc(hHeap, dwFlags, dwBytes) Dont_Use_HeapAlloc(hHeap, dwFlags, dwBytes) + + if ((pNewClrDebugState != NULL) && (pNewLockData != NULL)) + { + // Both allocations succeeded, so initialize the structures, and have + // pNewClrDebugState point to pNewLockData. If either of the allocations + // failed, we'll use gBadClrDebugState for this thread, and free whichever of + // pNewClrDebugState or pNewLockData actually did get allocated (if either did). + // (See code in this function below, outside this block.) + + pNewClrDebugState->SetStartingValues(); + pNewClrDebugState->ViolationMaskSet( CanFreeMe ); + _ASSERTE(!(pNewClrDebugState->ViolationMask() & BadDebugState)); + + pNewLockData->SetStartingValues(); + pNewClrDebugState->SetDbgStateLockData(pNewLockData); + } + } + + + // This is getting really diseased. All the one-time host init stuff inside the ClrFlsStuff could actually + // have caused mscorwks contracts to be executed since the last time we actually checked to see if the ClrDebugState + // needed creating. + // + // So we must make one last check to see if the ClrDebugState still needs creating. + // + ClrDebugState *pTmp = (ClrDebugState*)(ClrFlsGetValue(TlsIdx_ClrDebugState)); + if (pTmp != NULL) + { + // Recursive call set up ClrDebugState for us + pClrDebugState = pTmp; + } + else if ((pNewClrDebugState != NULL) && (pNewLockData != NULL)) + { + // Normal case: our new ClrDebugState will be the one we just allocated. + // Note that we require BOTH the ClrDebugState and the DbgStateLockData + // structures to have been successfully allocated for contracts to be + // enabled for this thread. + _ASSERTE(!(pNewClrDebugState->ViolationMask() & BadDebugState)); + _ASSERTE(pNewClrDebugState->GetDbgStateLockData() == pNewLockData); + pClrDebugState = pNewClrDebugState; + } + else + { + // OOM case: HeapAlloc of newClrDebugState failed. + pClrDebugState = &gBadClrDebugState; + } + + _ASSERTE(pClrDebugState != NULL); + + + ClrFlsSetValue(TlsIdx_ClrDebugState, (LPVOID)pClrDebugState); + + // For the ClrDebugState index, ClrFlsSetValue does *not* throw on OOM. + // Instead, it silently throws away the value. So we must now do a confirming + // FlsGet to learn if our Set succeeded. + if (ClrFlsGetValue(TlsIdx_ClrDebugState) == NULL) + { + // Our FlsSet didn't work. That means it couldn't allocate the master FLS block for our thread. + // Now we're a bad state because not only can't we succeed, we can't record that we didn't succeed. + // And it's invalid to return a BadClrDebugState here only to return a good debug state later. + // + // So we now take the drastic step of forcing all future ClrInitDebugState calls to return the OOM state. + ShutoffContracts(); + pClrDebugState = &gBadClrDebugState; + + // Try once more time to set the FLS (if it doesn't work, the next call will keep cycling through here + // until it does succeed.) + ClrFlsSetValue(TlsIdx_ClrDebugState, &gBadClrDebugState); + } + + +#if defined(_DEBUG) + // The ClrDebugState we allocated above made it into FLS iff + // the DbgStateLockData we allocated above made it into + // the FLS's ClrDebugState::m_pLockData + // These debug-only checks enforce this invariant + + if (pClrDebugState != NULL) + { + // If we're here, then typically pClrDebugState is what's in FLS. However, + // it's possible that pClrDebugState is gBadClrDebugState, and FLS is NULL + // (if the last ClrFlsSetValue() failed). Either way, our checks below + // are valid ones to make. + + if (pClrDebugState == pNewClrDebugState) + { + // ClrDebugState we allocated above made it into FLS, so DbgStateLockData + // must be there, too + _ASSERTE(pNewLockData != NULL); + _ASSERTE(pClrDebugState->GetDbgStateLockData() == pNewLockData); + } + else + { + // ClrDebugState we allocated above did NOT make it into FLS, + // so the DbgStateLockData we allocated must not be there, either + _ASSERTE(pClrDebugState->GetDbgStateLockData() == NULL || pClrDebugState->GetDbgStateLockData() != pNewLockData); + } + } + + // One more invariant: Because of ordering & conditions around the HeapAllocs above, + // we'll never have a DbgStateLockData without a ClrDebugState + _ASSERTE((pNewLockData == NULL) || (pNewClrDebugState != NULL)); + +#endif //_DEBUG + +#undef HeapFree +#undef GetProcessHeap + if (pNewClrDebugState != NULL && pClrDebugState != pNewClrDebugState) + { + // We allocated a ClrDebugState which didn't make it into FLS, so free it. + ::HeapFree (GetProcessHeap(), 0, pNewClrDebugState); + if (pNewLockData != NULL) + { + // We also allocated a DbgStateLockData that didn't make it into FLS, so + // free it, too. (Remember, we asserted above that we can only have + // this unused DbgStateLockData if we had an unused ClrDebugState + // as well (which we just freed).) + ::HeapFree (GetProcessHeap(), 0, pNewLockData); + } + } +#define HeapFree(hHeap, dwFlags, lpMem) Dont_Use_HeapFree(hHeap, dwFlags, lpMem) +#define GetProcessHeap() Dont_Use_GetProcessHeap() + + // Not necessary as TLS slots are born NULL and potentially problematic for OOM cases as we can't + // take an exception here. + //ClrFlsSetValue(TlsIdx_OwnedCrstsChain, NULL); + + return pClrDebugState; +} // CLRInitDebugState + +#endif //defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL) + + +LPVOID ClrAllocInProcessHeapBootstrap (DWORD dwFlags, SIZE_T dwBytes) +{ + STATIC_CONTRACT_SO_INTOLERANT; + +#if defined(SELF_NO_HOST) + static HANDLE hHeap = NULL; + + // This could race, but the result would be that this + // variable gets double initialized. + if (hHeap == NULL) + hHeap = ClrGetProcessHeap(); + + return ClrHeapAlloc(hHeap, dwFlags, S_SIZE_T(dwBytes)); +#else //!defined(SELF_NO_HOST) + FastAllocInProcessHeapFunc pfnHeapAlloc = (FastAllocInProcessHeapFunc) + GetClrCallbacks().m_pfnGetCLRFunction("EEHeapAllocInProcessHeap"); + if (pfnHeapAlloc != NULL) + { + __ClrAllocInProcessHeap = pfnHeapAlloc; + return pfnHeapAlloc(dwFlags, dwBytes); + } + return ClrHeapAlloc(ClrGetProcessHeap(), dwFlags, S_SIZE_T(dwBytes)); +#endif // !defined(SELF_NO_HOST) +} +FastAllocInProcessHeapFunc __ClrAllocInProcessHeap = (FastAllocInProcessHeapFunc) ClrAllocInProcessHeapBootstrap; + +BOOL ClrFreeInProcessHeapBootstrap (DWORD dwFlags, LPVOID lpMem) +{ + STATIC_CONTRACT_SO_INTOLERANT; + +#if defined(SELF_NO_HOST) + static HANDLE hHeap = NULL; + + // This could race, but the result would be that this + // variable gets double initialized. + if (hHeap == NULL) + hHeap = ClrGetProcessHeap(); + + return ClrHeapFree(hHeap, dwFlags,lpMem); +#else //!defined(SELF_NO_HOST) + FastFreeInProcessHeapFunc pfnHeapFree = (FastFreeInProcessHeapFunc) + GetClrCallbacks().m_pfnGetCLRFunction("EEHeapFreeInProcessHeap"); + if (pfnHeapFree) + { + __ClrFreeInProcessHeap = pfnHeapFree; + return (*pfnHeapFree)(dwFlags,lpMem); + } + return ClrHeapFree(ClrGetProcessHeap(),dwFlags,lpMem); +#endif //!defined(SELF_NO_HOST) +} +FastFreeInProcessHeapFunc __ClrFreeInProcessHeap = (FastFreeInProcessHeapFunc) ClrFreeInProcessHeapBootstrap; + +const NoThrow nothrow = { 0 }; + +void * __cdecl +operator new(size_t n) +{ +#ifdef _DEBUG_IMPL + CLRThrowsExceptionWorker(); +#endif + + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FAULT; + STATIC_CONTRACT_SO_TOLERANT; // The memory allocation itself should be SO-tolerant. But we must protect the use of it. + STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY; + + void * result = ClrAllocInProcessHeap(0, S_SIZE_T(n)); + if (result == NULL) { + ThrowOutOfMemory(); + } + TRASH_LASTERROR; + return result; +} + + +void * __cdecl +operator new[](size_t n) +{ +#ifdef _DEBUG_IMPL + CLRThrowsExceptionWorker(); +#endif + + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FAULT; + STATIC_CONTRACT_SO_TOLERANT; // The memory allocation itself should be SO-tolerant. But we must protect the use of it. + STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY; + + void * result = ClrAllocInProcessHeap(0, S_SIZE_T(n)); + if (result == NULL) { + ThrowOutOfMemory(); + } + TRASH_LASTERROR; + return result; +}; + +void * __cdecl operator new(size_t n, const NoThrow&) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FAULT; + STATIC_CONTRACT_SO_TOLERANT; // The memory allocation itself should be SO-tolerant. But we must protect the use of it. + STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY; + + INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN())); + + void * result = ClrAllocInProcessHeap(0, S_SIZE_T(n)); + TRASH_LASTERROR; + return result; +} + +void * __cdecl operator new[](size_t n, const NoThrow&) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FAULT; + STATIC_CONTRACT_SO_TOLERANT; // The memory allocation itself should be SO-tolerant. But we must protect the use of it. + STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY; + + INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN())); + + void * result = ClrAllocInProcessHeap(0, S_SIZE_T(n)); + TRASH_LASTERROR; + return result; +} + +void __cdecl +operator delete(void *p) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_SO_TOLERANT; // The memory management routines should be SO-tolerant. + STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY; + + if (p != NULL) + ClrFreeInProcessHeap(0, p); + TRASH_LASTERROR; +} + +void __cdecl +operator delete[](void *p) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_SO_TOLERANT; // The memory management routines should be SO-tolerant. + STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY; + + if (p != NULL) + ClrFreeInProcessHeap(0, p); + TRASH_LASTERROR; +} + +/* ------------------------------------------------------------------------ * + * New operator overloading for the executable heap + * ------------------------------------------------------------------------ */ + +#ifndef FEATURE_PAL + +const CExecutable executable = { 0 }; + +void * __cdecl operator new(size_t n, const CExecutable&) +{ +#if defined(_DEBUG_IMPL) + CLRThrowsExceptionWorker(); +#endif + + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FAULT; + STATIC_CONTRACT_SO_TOLERANT; // The memory management routines should be SO-tolerant. + + HANDLE hExecutableHeap = ClrGetProcessExecutableHeap(); + if (hExecutableHeap == NULL) { + ThrowOutOfMemory(); + } + + void * result = ClrHeapAlloc(hExecutableHeap, 0, S_SIZE_T(n)); + if (result == NULL) { + ThrowOutOfMemory(); + } + TRASH_LASTERROR; + return result; +} + +void * __cdecl operator new[](size_t n, const CExecutable&) +{ +#if defined(_DEBUG_IMPL) + CLRThrowsExceptionWorker(); +#endif + + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FAULT; + STATIC_CONTRACT_SO_TOLERANT; // The memory management routines should be SO-tolerant. + + HANDLE hExecutableHeap = ClrGetProcessExecutableHeap(); + if (hExecutableHeap == NULL) { + ThrowOutOfMemory(); + } + + void * result = ClrHeapAlloc(hExecutableHeap, 0, S_SIZE_T(n)); + if (result == NULL) { + ThrowOutOfMemory(); + } + TRASH_LASTERROR; + return result; +} + +void * __cdecl operator new(size_t n, const CExecutable&, const NoThrow&) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FAULT; + STATIC_CONTRACT_SO_TOLERANT; // The memory management routines should be SO-tolerant. + + INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN())); + + HANDLE hExecutableHeap = ClrGetProcessExecutableHeap(); + if (hExecutableHeap == NULL) + return NULL; + + void * result = ClrHeapAlloc(hExecutableHeap, 0, S_SIZE_T(n)); + TRASH_LASTERROR; + return result; +} + +void * __cdecl operator new[](size_t n, const CExecutable&, const NoThrow&) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FAULT; + STATIC_CONTRACT_SO_TOLERANT; // The memory management routines should be SO-tolerant. + + INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN())); + + HANDLE hExecutableHeap = ClrGetProcessExecutableHeap(); + if (hExecutableHeap == NULL) + return NULL; + + void * result = ClrHeapAlloc(hExecutableHeap, 0, S_SIZE_T(n)); + TRASH_LASTERROR; + return result; +} + +#endif // FEATURE_PAL + +#ifdef _DEBUG + +// This is a DEBUG routing to verify that a memory region complies with executable requirements +BOOL DbgIsExecutable(LPVOID lpMem, SIZE_T length) +{ +#if defined(CROSSGEN_COMPILE) + // No NX support on PAL or for crossgen compilations. + return TRUE; +#else // !defined(CROSSGEN_COMPILE) + BYTE *regionStart = (BYTE*) ALIGN_DOWN((BYTE*)lpMem, OS_PAGE_SIZE); + BYTE *regionEnd = (BYTE*) ALIGN_UP((BYTE*)lpMem+length, OS_PAGE_SIZE); + _ASSERTE(length > 0); + _ASSERTE(regionStart < regionEnd); + + while(regionStart < regionEnd) + { + MEMORY_BASIC_INFORMATION mbi; + + SIZE_T cbBytes = ClrVirtualQuery(regionStart, &mbi, sizeof(mbi)); + _ASSERTE(cbBytes); + + // The pages must have EXECUTE set + if(!(mbi.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))) + return FALSE; + + _ASSERTE((BYTE*)mbi.BaseAddress + mbi.RegionSize > regionStart); + regionStart = (BYTE*)mbi.BaseAddress + mbi.RegionSize; + } + + return TRUE; +#endif // defined(CROSSGEN_COMPILE) +} + +#endif //_DEBUG + + + + +// Access various ExecutionEngine support services, like a logical TLS that abstracts +// fiber vs. thread issues. We obtain it from a DLL export via the shim. + +typedef IExecutionEngine * (__stdcall * IEE_FPTR) (); + +// +// Access various ExecutionEngine support services, like a logical TLS that abstracts +// fiber vs. thread issues. +// From an IExecutionEngine is possible to get other services via QueryInterfaces such +// as memory management +// +IExecutionEngine *g_pExecutionEngine = NULL; + +#ifdef SELF_NO_HOST +BYTE g_ExecutionEngineInstance[sizeof(UtilExecutionEngine)]; +#endif + + +IExecutionEngine *GetExecutionEngine() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_CANNOT_TAKE_LOCK; + STATIC_CONTRACT_SO_TOLERANT; + SUPPORTS_DAC_HOST_ONLY; + + if (g_pExecutionEngine == NULL) + { + IExecutionEngine* pExecutionEngine; +#ifdef SELF_NO_HOST + // Create a local copy on the stack and then copy it over to the static instance. + // This avoids race conditions caused by multiple initializations of vtable in the constructor + UtilExecutionEngine local; + memcpy(&g_ExecutionEngineInstance, &local, sizeof(UtilExecutionEngine)); + pExecutionEngine = (IExecutionEngine*)(UtilExecutionEngine*)&g_ExecutionEngineInstance; +#else + // statically linked. + VALIDATECORECLRCALLBACKS(); + pExecutionEngine = g_CoreClrCallbacks.m_pfnIEE(); +#endif // SELF_NO_HOST + + //We use an explicit memory barrier here so that the reference g_pExecutionEngine is valid when + //it is used, This ia a requirement on platforms with weak memory model . We cannot use VolatileStore + //because they are the same as normal assignment for DAC builds [see code:VOLATILE] + + MemoryBarrier(); + g_pExecutionEngine = pExecutionEngine; + } + + // It's a bug to ask for the ExecutionEngine interface in scenarios where the + // ExecutionEngine cannot be loaded. + _ASSERTE(g_pExecutionEngine); + return g_pExecutionEngine; +} // GetExecutionEngine + +IEEMemoryManager * GetEEMemoryManager() +{ + STATIC_CONTRACT_SO_TOLERANT; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_CANNOT_TAKE_LOCK; + SUPPORTS_DAC_HOST_ONLY; + + static IEEMemoryManager *pEEMemoryManager = NULL; + if (NULL == pEEMemoryManager) { + IExecutionEngine *pExecutionEngine = GetExecutionEngine(); + _ASSERTE(pExecutionEngine); + + // It is dangerous to pass a global pointer to QueryInterface. The pointer may be set + // to NULL in the call. Imagine that thread 1 calls QI, and get a pointer. But before thread 1 + // returns the pointer to caller, thread 2 calls QI and the pointer is set to NULL. + IEEMemoryManager *pEEMM; + pExecutionEngine->QueryInterface(IID_IEEMemoryManager, (void**)&pEEMM); + pEEMemoryManager = pEEMM; + } + // It's a bug to ask for the MemoryManager interface in scenarios where it cannot be loaded. + _ASSERTE(pEEMemoryManager); + return pEEMemoryManager; +} + +// should return some error code or exception +void SetExecutionEngine(IExecutionEngine *pExecutionEngine) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + _ASSERTE(pExecutionEngine && !g_pExecutionEngine); + if (!g_pExecutionEngine) { + g_pExecutionEngine = pExecutionEngine; + g_pExecutionEngine->AddRef(); + } +} + +void ClrFlsAssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback) +{ + WRAPPER_NO_CONTRACT; + + GetExecutionEngine()->TLS_AssociateCallback(slot, callback); +} + +void * __stdcall ClrFlsGetBlockGeneric() +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + + return GetExecutionEngine()->TLS_GetDataBlock(); +} + +POPTIMIZEDTLSGETTER __ClrFlsGetBlock = (POPTIMIZEDTLSGETTER)ClrFlsGetBlockGeneric; + +CRITSEC_COOKIE ClrCreateCriticalSection(CrstType crstType, CrstFlags flags) +{ + WRAPPER_NO_CONTRACT; + + return GetExecutionEngine()->CreateLock(NULL, (LPCSTR)crstType, flags); +} + +HRESULT ClrDeleteCriticalSection(CRITSEC_COOKIE cookie) +{ + WRAPPER_NO_CONTRACT; + GetExecutionEngine()->DestroyLock(cookie); + return S_OK; +} + +void ClrEnterCriticalSection(CRITSEC_COOKIE cookie) +{ + WRAPPER_NO_CONTRACT; + + return GetExecutionEngine()->AcquireLock(cookie); +} + +void ClrLeaveCriticalSection(CRITSEC_COOKIE cookie) +{ + WRAPPER_NO_CONTRACT; + + return GetExecutionEngine()->ReleaseLock(cookie); +} + +EVENT_COOKIE ClrCreateAutoEvent(BOOL bInitialState) +{ + WRAPPER_NO_CONTRACT; + + return GetExecutionEngine()->CreateAutoEvent(bInitialState); +} + +EVENT_COOKIE ClrCreateManualEvent(BOOL bInitialState) +{ + WRAPPER_NO_CONTRACT; + + return GetExecutionEngine()->CreateManualEvent(bInitialState); +} + +void ClrCloseEvent(EVENT_COOKIE event) +{ + WRAPPER_NO_CONTRACT; + + GetExecutionEngine()->CloseEvent(event); +} + +BOOL ClrSetEvent(EVENT_COOKIE event) +{ + WRAPPER_NO_CONTRACT; + + return GetExecutionEngine()->ClrSetEvent(event); +} + +BOOL ClrResetEvent(EVENT_COOKIE event) +{ + WRAPPER_NO_CONTRACT; + + return GetExecutionEngine()->ClrResetEvent(event); +} + +DWORD ClrWaitEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable) +{ + WRAPPER_NO_CONTRACT; + + return GetExecutionEngine()->WaitForEvent(event, dwMilliseconds, bAlertable); +} + +SEMAPHORE_COOKIE ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax) +{ + WRAPPER_NO_CONTRACT; + + return GetExecutionEngine()->ClrCreateSemaphore(dwInitial, dwMax); +} + +void ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore) +{ + WRAPPER_NO_CONTRACT; + + GetExecutionEngine()->ClrCloseSemaphore(semaphore); +} + +BOOL ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount) +{ + WRAPPER_NO_CONTRACT; + + return GetExecutionEngine()->ClrReleaseSemaphore(semaphore, lReleaseCount, lpPreviousCount); +} + +DWORD ClrWaitSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable) +{ + WRAPPER_NO_CONTRACT; + + return GetExecutionEngine()->ClrWaitForSemaphore(semaphore, dwMilliseconds, bAlertable); +} + +MUTEX_COOKIE ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, + BOOL bInitialOwner, + LPCTSTR lpName) +{ + WRAPPER_NO_CONTRACT; + + return GetExecutionEngine()->ClrCreateMutex(lpMutexAttributes, bInitialOwner, lpName); +} + +void ClrCloseMutex(MUTEX_COOKIE mutex) +{ + WRAPPER_NO_CONTRACT; + + GetExecutionEngine()->ClrCloseMutex(mutex); +} + +BOOL ClrReleaseMutex(MUTEX_COOKIE mutex) +{ + WRAPPER_NO_CONTRACT; + + return GetExecutionEngine()->ClrReleaseMutex(mutex); +} + +DWORD ClrWaitForMutex(MUTEX_COOKIE mutex, DWORD dwMilliseconds, BOOL bAlertable) +{ + WRAPPER_NO_CONTRACT; + + return GetExecutionEngine()->ClrWaitForMutex(mutex, dwMilliseconds, bAlertable); +} + +DWORD ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable) +{ + WRAPPER_NO_CONTRACT; + + return GetExecutionEngine()->ClrSleepEx(dwMilliseconds, bAlertable); +} + +LPVOID ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) +{ + WRAPPER_NO_CONTRACT; + + LPVOID result = GetEEMemoryManager()->ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect); + LOG((LF_EEMEM, LL_INFO100000, "ClrVirtualAlloc (0x%p, 0x%06x, 0x%06x, 0x%02x) = 0x%p\n", lpAddress, dwSize, flAllocationType, flProtect, result)); + + return result; +} + +BOOL ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType) +{ + WRAPPER_NO_CONTRACT; + + LOG((LF_EEMEM, LL_INFO100000, "ClrVirtualFree (0x%p, 0x%06x, 0x%04x)\n", lpAddress, dwSize, dwFreeType)); + BOOL result = GetEEMemoryManager()->ClrVirtualFree(lpAddress, dwSize, dwFreeType); + + return result; +} + +SIZE_T ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength) +{ + WRAPPER_NO_CONTRACT; + + LOG((LF_EEMEM, LL_INFO100000, "ClrVirtualQuery (0x%p)\n", lpAddress)); + return GetEEMemoryManager()->ClrVirtualQuery(lpAddress, lpBuffer, dwLength); +} + +BOOL ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect) +{ + WRAPPER_NO_CONTRACT; + + LOG((LF_EEMEM, LL_INFO100000, "ClrVirtualProtect(0x%p, 0x%06x, 0x%02x)\n", lpAddress, dwSize, flNewProtect)); + return GetEEMemoryManager()->ClrVirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect); +} + +HANDLE ClrGetProcessHeap() +{ + WRAPPER_NO_CONTRACT; + + return GetEEMemoryManager()->ClrGetProcessHeap(); +} + +HANDLE ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize) +{ + WRAPPER_NO_CONTRACT; + + return GetEEMemoryManager()->ClrHeapCreate(flOptions, dwInitialSize, dwMaximumSize); +} + +BOOL ClrHeapDestroy(HANDLE hHeap) +{ + WRAPPER_NO_CONTRACT; + + return GetEEMemoryManager()->ClrHeapDestroy(hHeap); +} + +LPVOID ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, S_SIZE_T dwBytes) +{ + WRAPPER_NO_CONTRACT; + + if(dwBytes.IsOverflow()) return NULL; + + LPVOID result = GetEEMemoryManager()->ClrHeapAlloc(hHeap, dwFlags, dwBytes.Value()); + + return result; +} + +BOOL ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) +{ + WRAPPER_NO_CONTRACT; + + BOOL result = GetEEMemoryManager()->ClrHeapFree(hHeap, dwFlags, lpMem); + + return result; +} + +BOOL ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem) +{ + WRAPPER_NO_CONTRACT; + + return GetEEMemoryManager()->ClrHeapValidate(hHeap, dwFlags, lpMem); +} + +HANDLE ClrGetProcessExecutableHeap() +{ + WRAPPER_NO_CONTRACT; + + return GetEEMemoryManager()->ClrGetProcessExecutableHeap(); +} + +void GetLastThrownObjectExceptionFromThread(void **ppvException) +{ + WRAPPER_NO_CONTRACT; + + GetExecutionEngine()->GetLastThrownObjectExceptionFromThread(ppvException); +} diff --git a/src/utilcode/collections.cpp b/src/utilcode/collections.cpp new file mode 100644 index 0000000000..10b4aeebfa --- /dev/null +++ b/src/utilcode/collections.cpp @@ -0,0 +1,973 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// Collections.cpp +// + +// +// This contains Collections C++ utility classes. +// +//***************************************************************************** + +#include "stdafx.h" +#include "utilcode.h" +#include "ex.h" + +// +// +// CHashTable +// +// + +#ifndef DACCESS_COMPILE + +//***************************************************************************** +// This is the second part of construction where we do all of the work that +// can fail. We also take the array of structs here because the calling class +// presumably needs to allocate it in its NewInit. +//***************************************************************************** +HRESULT CHashTable::NewInit( // Return status. + BYTE *pcEntries, // Array of structs we are managing. + ULONG iEntrySize) // Size of the entries. +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + _ASSERTE(iEntrySize >= sizeof(FREEHASHENTRY)); + + // Allocate the bucket chain array and init it. + if ((m_piBuckets = new (nothrow) ULONG [m_iBuckets]) == NULL) + return (OutOfMemory()); + memset(m_piBuckets, 0xff, m_iBuckets * sizeof(ULONG)); + + // Save the array of structs we are managing. + m_pcEntries = (TADDR)pcEntries; + m_iEntrySize = iEntrySize; + return (S_OK); +} + +//***************************************************************************** +// Add the struct at the specified index in m_pcEntries to the hash chains. +//***************************************************************************** +BYTE *CHashTable::Add( // New entry. + ULONG iHash, // Hash value of entry to add. + ULONG iIndex) // Index of struct in m_pcEntries. +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + HASHENTRY *psEntry; // The struct we are adding. + + // Get a pointer to the entry we are adding. + psEntry = EntryPtr(iIndex); + + // Compute the hash value for the entry. + iHash %= m_iBuckets; + + _ASSERTE(m_piBuckets[iHash] != iIndex && + (m_piBuckets[iHash] == UINT32_MAX || EntryPtr(m_piBuckets[iHash])->iPrev != iIndex)); + + // Setup this entry. + psEntry->iPrev = UINT32_MAX; + psEntry->iNext = m_piBuckets[iHash]; + + // Link it into the hash chain. + if (m_piBuckets[iHash] != UINT32_MAX) + EntryPtr(m_piBuckets[iHash])->iPrev = iIndex; + m_piBuckets[iHash] = iIndex; + return ((BYTE *) psEntry); +} + +//***************************************************************************** +// Delete the struct at the specified index in m_pcEntries from the hash chains. +//***************************************************************************** +void CHashTable::Delete( + ULONG iHash, // Hash value of entry to delete. + ULONG iIndex) // Index of struct in m_pcEntries. +{ + WRAPPER_NO_CONTRACT; + + HASHENTRY *psEntry; // Struct to delete. + + // Get a pointer to the entry we are deleting. + psEntry = EntryPtr(iIndex); + Delete(iHash, psEntry); +} + +//***************************************************************************** +// Delete the struct at the specified index in m_pcEntries from the hash chains. +//***************************************************************************** +void CHashTable::Delete( + ULONG iHash, // Hash value of entry to delete. + HASHENTRY *psEntry) // The struct to delete. +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + // Compute the hash value for the entry. + iHash %= m_iBuckets; + + _ASSERTE(psEntry->iPrev != psEntry->iNext || psEntry->iPrev == UINT32_MAX); + + // Fix the predecessor. + if (psEntry->iPrev == UINT32_MAX) + m_piBuckets[iHash] = psEntry->iNext; + else + EntryPtr(psEntry->iPrev)->iNext = psEntry->iNext; + + // Fix the successor. + if (psEntry->iNext != UINT32_MAX) + EntryPtr(psEntry->iNext)->iPrev = psEntry->iPrev; +} + +//***************************************************************************** +// The item at the specified index has been moved, update the previous and +// next item. +//***************************************************************************** +void CHashTable::Move( + ULONG iHash, // Hash value for the item. + ULONG iNew) // New location. +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + HASHENTRY *psEntry; // The struct we are deleting. + + psEntry = EntryPtr(iNew); + _ASSERTE(psEntry->iPrev != iNew && psEntry->iNext != iNew); + + if (psEntry->iPrev != UINT32_MAX) + EntryPtr(psEntry->iPrev)->iNext = iNew; + else + m_piBuckets[iHash % m_iBuckets] = iNew; + if (psEntry->iNext != UINT32_MAX) + EntryPtr(psEntry->iNext)->iPrev = iNew; +} + +#endif // !DACCESS_COMPILE + +//***************************************************************************** +// Search the hash table for an entry with the specified key value. +//***************************************************************************** +BYTE *CHashTable::Find( // Index of struct in m_pcEntries. + ULONG iHash, // Hash value of the item. + SIZE_T key) // The key to match. +{ + CONTRACTL + { + NOTHROW; + SO_TOLERANT; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACTL_END; + + ULONG iNext; // Used to traverse the chains. + HASHENTRY *psEntry; // Used to traverse the chains. + + // Start at the top of the chain. + iNext = m_piBuckets[iHash % m_iBuckets]; + + // Search until we hit the end. + +#ifdef _DEBUG + unsigned count = 0; +#endif + + while (iNext != UINT32_MAX) + { + // Compare the keys. + psEntry = EntryPtr(iNext); + +#ifdef _DEBUG + count++; +#endif + if (!Cmp(key, psEntry)) + { +#ifdef _DEBUG + if (count > m_maxSearch) + m_maxSearch = count; +#endif + + return ((BYTE *) psEntry); + } + + // Advance to the next item in the chain. + iNext = psEntry->iNext; + } + + // We couldn't find it. + return (0); +} + +//***************************************************************************** +// Search the hash table for the next entry with the specified key value. +//***************************************************************************** +ULONG CHashTable::FindNext( // Index of struct in m_pcEntries. + SIZE_T key, // The key to match. + ULONG iIndex) // Index of previous match. +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + ULONG iNext; // Used to traverse the chains. + HASHENTRY *psEntry; // Used to traverse the chains. + + // Start at the next entry in the chain. + iNext = EntryPtr(iIndex)->iNext; + + // Search until we hit the end. + while (iNext != UINT32_MAX) + { + // Compare the keys. + psEntry = EntryPtr(iNext); + if (!Cmp(key, psEntry)) + return (iNext); + + // Advance to the next item in the chain. + iNext = psEntry->iNext; + } + + // We couldn't find it. + return (UINT32_MAX); +} + +//***************************************************************************** +// Returns the next entry in the list. +//***************************************************************************** +BYTE *CHashTable::FindNextEntry( // The next entry, or0 for end of list. + HASHFIND *psSrch) // Search object. +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACTL_END; + + HASHENTRY *psEntry; // Used to traverse the chains. + + for (;;) + { + // See if we already have one to use and if so, use it. + if (psSrch->iNext != UINT32_MAX) + { + psEntry = EntryPtr(psSrch->iNext); + psSrch->iNext = psEntry->iNext; + return ((BYTE *) psEntry); + } + + // Advance to the next bucket. + if (psSrch->iBucket < m_iBuckets) + psSrch->iNext = m_piBuckets[psSrch->iBucket++]; + else + break; + } + + // There were no more entries to be found. + return (0); +} + +#ifdef DACCESS_COMPILE + +void +CHashTable::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, + ULONG numEntries) +{ + SUPPORTS_DAC; + + // This class may be embedded so do not enum 'this'. + DacEnumMemoryRegion(m_pcEntries, + (ULONG)numEntries * m_iEntrySize); + DacEnumMemoryRegion(dac_cast<TADDR>(m_piBuckets), + (ULONG)m_iBuckets * sizeof(ULONG)); +} + +#endif // #ifdef DACCESS_COMPILE + +// +// +// CClosedHashBase +// +// + +//***************************************************************************** +// Delete the given value. This will simply mark the entry as deleted (in +// order to keep the collision chain intact). There is an optimization that +// consecutive deleted entries leading up to a free entry are themselves freed +// to reduce collisions later on. +//***************************************************************************** +void CClosedHashBase::Delete( + void *pData) // Key value to delete. +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + BYTE *ptr; + + // Find the item to delete. + if ((ptr = Find(pData)) == 0) + { + // You deleted something that wasn't there, why? + _ASSERTE(0); + return; + } + + // For a perfect system, there are no collisions so it is free. + if (m_bPerfect) + { + SetStatus(ptr, FREE); + + // One less non free entry. + --m_iCount; + + return; + } + + // Mark this entry deleted. + SetStatus(ptr, DELETED); + + // If the next item is free, then we can go backwards freeing + // deleted entries which are no longer part of a chain. This isn't + // 100% great, but it will reduce collisions. + BYTE *pnext; + if ((pnext = ptr + m_iEntrySize) > EntryPtr(m_iSize - 1)) + pnext = &m_rgData[0]; + if (Status(pnext) != FREE) + return; + + // We can now free consecutive entries starting with the one + // we just deleted, up to the first non-deleted one. + while (Status(ptr) == DELETED) + { + // Free this entry. + SetStatus(ptr, FREE); + + // One less non free entry. + --m_iCount; + + // Check the one before it, handle wrap around. + if ((ptr -= m_iEntrySize) < &m_rgData[0]) + ptr = EntryPtr(m_iSize - 1); + } +} + + +//***************************************************************************** +// Iterates over all active values, passing each one to pDeleteLoopFunc. +// If pDeleteLoopFunc returns TRUE, the entry is deleted. This is safer +// and faster than using FindNext() and Delete(). +//***************************************************************************** +void CClosedHashBase::DeleteLoop( + DELETELOOPFUNC pDeleteLoopFunc, // Decides whether to delete item + void *pCustomizer) // Extra value passed to deletefunc. +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + int i; + + if (m_rgData == 0) + { + return; + } + + for (i = 0; i < m_iSize; i++) + { + BYTE *pEntry = EntryPtr(i); + if (Status(pEntry) == USED) + { + if (pDeleteLoopFunc(pEntry, pCustomizer)) + { + if (m_bPerfect) + { + SetStatus(pEntry, FREE); + // One less non free entry. + --m_iCount; + } + else + { + SetStatus(pEntry, DELETED); + } + } + } + } + + if (!m_bPerfect) + { + // Now free DELETED entries that are no longer part of a chain. + for (i = 0; i < m_iSize; i++) + { + if (Status(EntryPtr(i)) == FREE) + { + break; + } + } + if (i != m_iSize) + { + int iFirstFree = i; + + do + { + if (i-- == 0) + { + i = m_iSize - 1; + } + while (Status(EntryPtr(i)) == DELETED) + { + SetStatus(EntryPtr(i), FREE); + + + // One less non free entry. + --m_iCount; + + if (i-- == 0) + { + i = m_iSize - 1; + } + } + + while (Status(EntryPtr(i)) != FREE) + { + if (i-- == 0) + { + i = m_iSize - 1; + } + } + + } + while (i != iFirstFree); + } + } + +} + +//***************************************************************************** +// Lookup a key value and return a pointer to the element if found. +//***************************************************************************** +BYTE *CClosedHashBase::Find( // The item if found, 0 if not. + void *pData) // The key to lookup. +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + unsigned int iHash; // Hash value for this data. + int iBucket; // Which bucke to start at. + int i; // Loop control. + + // Safety check. + if (!m_rgData || m_iCount == 0) + return (0); + + // Hash to the bucket. + iHash = Hash(pData); + iBucket = iHash % m_iBuckets; + + // For a perfect table, the bucket is the correct one. + if (m_bPerfect) + { + // If the value is there, it is the correct one. + if (Status(EntryPtr(iBucket)) != FREE) + return (EntryPtr(iBucket)); + return (0); + } + + // Walk the bucket list looking for the item. + for (i=iBucket; Status(EntryPtr(i)) != FREE; ) + { + // Don't look at deleted items. + if (Status(EntryPtr(i)) == DELETED) + { + // Handle wrap around. + if (++i >= m_iSize) + i = 0; + continue; + } + + // Check this one. + if (Compare(pData, EntryPtr(i)) == 0) + return (EntryPtr(i)); + + // If we never collided while adding items, then there is + // no point in scanning any further. + if (!m_iCollisions) + return (0); + + // Handle wrap around. + if (++i >= m_iSize) + i = 0; + } + return (0); +} + + + +//***************************************************************************** +// Look for an item in the table. If it isn't found, then create a new one and +// return that. +//***************************************************************************** +BYTE *CClosedHashBase::FindOrAdd( // The item if found, 0 if not. + void *pData, // The key to lookup. + bool &bNew) // true if created. +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + unsigned int iHash; // Hash value for this data. + int iBucket; // Which bucke to start at. + int i; // Loop control. + + // If we haven't allocated any memory, or it is too small, fix it. + if (!m_rgData || ((m_iCount + 1) > (m_iSize * 3 / 4) && !m_bPerfect)) + { + if (!ReHash()) + return (0); + } + + // Assume we find it. + bNew = false; + + // Hash to the bucket. + iHash = Hash(pData); + iBucket = iHash % m_iBuckets; + + // For a perfect table, the bucket is the correct one. + if (m_bPerfect) + { + // If the value is there, it is the correct one. + if (Status(EntryPtr(iBucket)) != FREE) + return (EntryPtr(iBucket)); + i = iBucket; + } + else + { + // Walk the bucket list looking for the item. + for (i=iBucket; Status(EntryPtr(i)) != FREE; ) + { + // Don't look at deleted items. + if (Status(EntryPtr(i)) == DELETED) + { + // Handle wrap around. + if (++i >= m_iSize) + i = 0; + continue; + } + + // Check this one. + if (Compare(pData, EntryPtr(i)) == 0) + return (EntryPtr(i)); + + // One more to count. + ++m_iCollisions; + + // Handle wrap around. + if (++i >= m_iSize) + i = 0; + } + } + + // We've found an open slot, use it. + _ASSERTE(Status(EntryPtr(i)) == FREE); + bNew = true; + ++m_iCount; + return (EntryPtr(i)); +} + +//***************************************************************************** +// This helper actually does the add for you. +//***************************************************************************** +BYTE *CClosedHashBase::DoAdd(void *pData, BYTE *rgData, int &iBuckets, int iSize, + int &iCollisions, int &iCount) +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + unsigned int iHash; // Hash value for this data. + int iBucket; // Which bucke to start at. + int i; // Loop control. + + // Hash to the bucket. + iHash = Hash(pData); + iBucket = iHash % iBuckets; + + // For a perfect table, the bucket is free. + if (m_bPerfect) + { + i = iBucket; + _ASSERTE(Status(EntryPtr(i, rgData)) == FREE); + } + // Need to scan. + else + { + // Walk the bucket list looking for a slot. + for (i=iBucket; Status(EntryPtr(i, rgData)) != FREE; ) + { + // Handle wrap around. + if (++i >= iSize) + i = 0; + + // If we made it this far, we collided. + ++iCollisions; + } + } + + // One more item in list. + ++iCount; + + // Return the new slot for the caller. + return (EntryPtr(i, rgData)); +} + +//***************************************************************************** +// This function is called either to init the table in the first place, or +// to rehash the table if we ran out of room. +//***************************************************************************** +bool CClosedHashBase::ReHash() // true if successful. +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + // Allocate memory if we don't have any. + if (!m_rgData) + { + if ((m_rgData = new (nothrow) BYTE [m_iSize * m_iEntrySize]) == 0) + return (false); + InitFree(&m_rgData[0], m_iSize); + return (true); + } + + // We have entries already, allocate a new table. + BYTE *rgTemp, *p; + int iBuckets = m_iBuckets * 2 - 1; + int iSize = iBuckets + 7; + int iCollisions = 0; + int iCount = 0; + + if ((rgTemp = new (nothrow) BYTE [iSize * m_iEntrySize]) == 0) + return (false); + InitFree(&rgTemp[0], iSize); + m_bPerfect = false; + + // Rehash the data. + for (int i=0; i<m_iSize; i++) + { + // Only copy used entries. + if (Status(EntryPtr(i)) != USED) + continue; + + // Add this entry to the list again. + VERIFY((p = DoAdd(GetKey(EntryPtr(i)), rgTemp, iBuckets, + iSize, iCollisions, iCount))); + memmove(p, EntryPtr(i), m_iEntrySize); + } + + // Reset internals. + delete [] m_rgData; + m_rgData = rgTemp; + m_iBuckets = iBuckets; + m_iSize = iSize; + m_iCollisions = iCollisions; + m_iCount = iCount; + return (true); +} + + +// +// +// CStructArray +// +// + + +//***************************************************************************** +// Returns a pointer to the (iIndex)th element of the array, shifts the elements +// in the array if the location is already full. The iIndex cannot exceed the count +// of elements in the array. +//***************************************************************************** +void *CStructArray::InsertThrowing( + int iIndex) +{ + CONTRACTL + { + THROWS; + } + CONTRACTL_END; + + _ASSERTE(iIndex >= 0); + + // We can not insert an element further than the end of the array. + if (iIndex > m_iCount) + return (NULL); + + // The array should grow, if we can't fit one more element into the array. + Grow(1); + + // The pointer to be returned. + BYTE *pcList = m_pList + iIndex * m_iElemSize; + + // See if we need to slide anything down. + if (iIndex < m_iCount) + memmove(pcList + m_iElemSize, pcList, (m_iCount - iIndex) * m_iElemSize); + ++m_iCount; + return(pcList); +} + +//***************************************************************************** +// Non-throwing variant +//***************************************************************************** +void *CStructArray::Insert(int iIndex) +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + void *result = NULL; + EX_TRY + { + result = InsertThrowing(iIndex); + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions); + + return result; +} + + +//***************************************************************************** +// Allocate a new element at the end of the dynamic array and return a pointer +// to it. +//***************************************************************************** +void *CStructArray::AppendThrowing() +{ + CONTRACTL + { + THROWS; + } + CONTRACTL_END; + + // The array should grow, if we can't fit one more element into the array. + Grow(1); + + return (m_pList + m_iCount++ * m_iElemSize); +} + + +//***************************************************************************** +// Non-throwing variant +//***************************************************************************** +void *CStructArray::Append() +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + void *result = NULL; + EX_TRY + { + result = AppendThrowing(); + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions); + + return result; +} + + +//***************************************************************************** +// Allocate enough memory to have at least iCount items. This is a one shot +// check for a block of items, instead of requiring singleton inserts. It also +// avoids realloc headaches caused by growth, since you can do the whole block +// in one piece of code. If the array is already large enough, this is a no-op. +//***************************************************************************** +void CStructArray::AllocateBlockThrowing(int iCount) +{ + CONTRACTL + { + THROWS; + } + CONTRACTL_END; + + if (m_iSize < m_iCount+iCount) + Grow(iCount); + m_iCount += iCount; +} + +//***************************************************************************** +// Non-throwing variant +//***************************************************************************** +int CStructArray::AllocateBlock(int iCount) +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + int result = FALSE; + EX_TRY + { + AllocateBlockThrowing(iCount); + result = TRUE; + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions); + + return result; +} + + +//***************************************************************************** +// Deletes the specified element from the array. +//***************************************************************************** +void CStructArray::Delete( + int iIndex) +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + _ASSERTE(iIndex >= 0); + + // See if we need to slide anything down. + if (iIndex < --m_iCount) + { + BYTE *pcList = m_pList + iIndex * m_iElemSize; + memmove(pcList, pcList + m_iElemSize, (m_iCount - iIndex) * m_iElemSize); + } +} + + +//***************************************************************************** +// Grow the array if it is not possible to fit iCount number of new elements. +//***************************************************************************** +void CStructArray::Grow( + int iCount) +{ + CONTRACTL { + THROWS; + } CONTRACTL_END; + + BYTE *pTemp; // temporary pointer used in realloc. + int iGrow; + + if (m_iSize < m_iCount+iCount) + { + if (m_pList == NULL) + { + iGrow = max(m_iGrowInc, iCount); + + //<TODO>@todo this is a workaround because I don't want to do resize right now.</TODO> + //check added to make PREFAST happy + S_SIZE_T newSize = S_SIZE_T(iGrow) * S_SIZE_T(m_iElemSize); + if(newSize.IsOverflow()) + ThrowOutOfMemory(); + else + { + m_pList = new BYTE[newSize.Value()]; + m_iSize = iGrow; + m_bFree = true; + } + } + else + { + // Adjust grow size as a ratio to avoid too many reallocs. + if (m_iSize / m_iGrowInc >= 3) + { // Don't overflow and go negative. + int newinc = m_iGrowInc * 2; + if (newinc > m_iGrowInc) + m_iGrowInc = newinc; + } + + iGrow = max(m_iGrowInc, iCount); + + // try to allocate memory for reallocation. + S_SIZE_T allocSize = (S_SIZE_T(m_iSize) + S_SIZE_T(iGrow)) * S_SIZE_T(m_iElemSize); + S_SIZE_T copyBytes = S_SIZE_T(m_iSize) * S_SIZE_T(m_iElemSize); + if(allocSize.IsOverflow() || copyBytes.IsOverflow()) + ThrowOutOfMemory(); + if (m_bFree) + { // We already own memory. + pTemp = new BYTE[allocSize.Value()]; + memcpy (pTemp, m_pList, copyBytes.Value()); + delete [] m_pList; + } + else + { // We don't own memory; get our own. + pTemp = new BYTE[allocSize.Value()]; + memcpy(pTemp, m_pList, copyBytes.Value()); + m_bFree = true; + } + m_pList = pTemp; + m_iSize += iGrow; + } + } +} + + +//***************************************************************************** +// Free the memory for this item. +//***************************************************************************** +void CStructArray::Clear() +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + // Free the chunk of memory. + if (m_bFree && m_pList != NULL) + delete [] m_pList; + + m_pList = NULL; + m_iSize = 0; + m_iCount = 0; +} diff --git a/src/utilcode/comex.cpp b/src/utilcode/comex.cpp new file mode 100644 index 0000000000..4c9d861795 --- /dev/null +++ b/src/utilcode/comex.cpp @@ -0,0 +1,52 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// --------------------------------------------------------------------------- +// COMEx.cpp +// + +// +// --------------------------------------------------------------------------- + +#include "stdafx.h" +#include "string.h" +#include "ex.h" +#include "holder.h" +#include "corerror.h" + +// --------------------------------------------------------------------------- +// COMException class. Implements exception API for standard COM-based error info +// --------------------------------------------------------------------------- + +COMException::~COMException() +{ + WRAPPER_NO_CONTRACT; + + if (m_pErrorInfo != NULL) + m_pErrorInfo->Release(); +} + +IErrorInfo *COMException::GetErrorInfo() +{ + LIMITED_METHOD_CONTRACT; + + IErrorInfo *pErrorInfo = m_pErrorInfo; + if (pErrorInfo != NULL) + pErrorInfo->AddRef(); + return pErrorInfo; +} + +void COMException::GetMessage(SString &string) +{ + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_NOTRIGGER; + + if (m_pErrorInfo != NULL) + { + BSTRHolder message(NULL); + if (SUCCEEDED(m_pErrorInfo->GetDescription(&message))) + string.Set(message, SysStringLen(message)); + } +} + diff --git a/src/utilcode/corimage.cpp b/src/utilcode/corimage.cpp new file mode 100644 index 0000000000..b6fd60d785 --- /dev/null +++ b/src/utilcode/corimage.cpp @@ -0,0 +1,261 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// + +/*============================================================ +** +** CorImage.cpp +** +** IMAGEHLP routines so we can avoid early binding to that DLL. +** +===========================================================*/ + +#include "stdafx.h" +#include "contract.h" +#include <daccess.h> +#include "corimage.h" +#include "safemath.h" + +#define RTL_MEG (1024UL * 1024UL) +#define RTLP_IMAGE_MAX_DOS_HEADER ( 256UL * RTL_MEG) + +// IMAGE_FIRST_SECTION doesn't need 32/64 versions since the file header is +// the same either way. + +#define PTR_IMAGE_FIRST_SECTION( ntheader ) \ + PTR_IMAGE_SECTION_HEADER \ + (dac_cast<TADDR>(ntheader) + \ + FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + \ + VAL16((ntheader)->FileHeader.SizeOfOptionalHeader) \ + ) + +#ifndef DACCESS_COMPILE + +IMAGE_NT_HEADERS *Cor_RtlImageNtHeader(VOID *pvBase, ULONG FileLength) +{ + LIMITED_METHOD_CONTRACT; + IMAGE_NT_HEADERS *pNtHeaders = NULL; + if (pvBase && (pvBase != (VOID*)-1)) { + struct Param + { + IMAGE_DOS_HEADER *pDos; + ULONG FileLength; + IMAGE_NT_HEADERS *pNtHeaders; + } param; + param.pDos = (IMAGE_DOS_HEADER*)pvBase; + param.FileLength = FileLength; + param.pNtHeaders = pNtHeaders; + + PAL_TRY(Param *, pParam, ¶m) { + if ( (pParam->pDos->e_magic == VAL16(IMAGE_DOS_SIGNATURE)) + && ((DWORD)VAL32(pParam->pDos->e_lfanew) < RTLP_IMAGE_MAX_DOS_HEADER) + && ovadd_lt((DWORD)VAL32(pParam->pDos->e_lfanew), sizeof(IMAGE_FILE_HEADER) + sizeof(DWORD), pParam->FileLength)) { + pParam->pNtHeaders = (IMAGE_NT_HEADERS*)((BYTE*)pParam->pDos + VAL32(pParam->pDos->e_lfanew)); + if (pParam->pNtHeaders->Signature != VAL32(IMAGE_NT_SIGNATURE)) + pParam->pNtHeaders = NULL; + } + } + PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { + param.pNtHeaders = NULL; + } + PAL_ENDTRY + + pNtHeaders = param.pNtHeaders; + } + return pNtHeaders; +} + +#endif // #ifndef DACCESS_COMPILE + +EXTERN_C PIMAGE_SECTION_HEADER +Cor_RtlImageRvaToSection32(PTR_IMAGE_NT_HEADERS32 NtHeaders, + ULONG Rva, + ULONG FileLength) +{ + LIMITED_METHOD_CONTRACT; + ULONG i; + PTR_IMAGE_SECTION_HEADER NtSection; + + NtSection = PTR_IMAGE_FIRST_SECTION( NtHeaders ); + for (i=0; i<NtHeaders->FileHeader.NumberOfSections; i++) { + if (FileLength && + ((VAL32(NtSection->PointerToRawData) > FileLength)) || + (VAL32(NtSection->SizeOfRawData) > FileLength - VAL32(NtSection->PointerToRawData))) + return NULL; + if (Rva >= VAL32(NtSection->VirtualAddress) && + Rva < VAL32(NtSection->VirtualAddress) + VAL32(NtSection->SizeOfRawData)) + return NtSection; + + ++NtSection; + } + + return NULL; +} + +EXTERN_C PIMAGE_SECTION_HEADER +Cor_RtlImageRvaToSection64(PTR_IMAGE_NT_HEADERS64 NtHeaders, + ULONG Rva, + ULONG FileLength) +{ + LIMITED_METHOD_CONTRACT; + ULONG i; + PTR_IMAGE_SECTION_HEADER NtSection; + + NtSection = PTR_IMAGE_FIRST_SECTION( NtHeaders ); + for (i=0; i<VAL16(NtHeaders->FileHeader.NumberOfSections); i++) { + if (FileLength && + ((VAL32(NtSection->PointerToRawData) > FileLength)) || + (VAL32(NtSection->SizeOfRawData) > FileLength - VAL32(NtSection->PointerToRawData))) + return NULL; + if (Rva >= VAL32(NtSection->VirtualAddress) && + Rva < VAL32(NtSection->VirtualAddress) + VAL32(NtSection->SizeOfRawData)) + return NtSection; + + ++NtSection; + } + + return NULL; +} + +EXTERN_C PIMAGE_SECTION_HEADER +Cor_RtlImageRvaToSection(PTR_IMAGE_NT_HEADERS NtHeaders, + ULONG Rva, + ULONG FileLength) +{ + LIMITED_METHOD_CONTRACT; + if (NtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC)) + return Cor_RtlImageRvaToSection32((PTR_IMAGE_NT_HEADERS32)NtHeaders, + Rva, FileLength); + else if(NtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR64_MAGIC)) + return Cor_RtlImageRvaToSection64((PTR_IMAGE_NT_HEADERS64)NtHeaders, + Rva, FileLength); + else { + _ASSERTE(!"Invalid File Type"); + return NULL; + } +} + +EXTERN_C PBYTE Cor_RtlImageRvaToVa32(PTR_IMAGE_NT_HEADERS32 NtHeaders, + PBYTE Base, + ULONG Rva, + ULONG FileLength) +{ + LIMITED_METHOD_CONTRACT; + PIMAGE_SECTION_HEADER NtSection = + Cor_RtlImageRvaToSection32(NtHeaders, + Rva, + FileLength); + + if (NtSection != NULL) + return (Base + + (Rva - VAL32(NtSection->VirtualAddress)) + + VAL32(NtSection->PointerToRawData)); + else + return NULL; +} + +EXTERN_C PBYTE Cor_RtlImageRvaToVa64(PTR_IMAGE_NT_HEADERS64 NtHeaders, + PBYTE Base, + ULONG Rva, + ULONG FileLength) +{ + LIMITED_METHOD_CONTRACT; + PIMAGE_SECTION_HEADER NtSection = + Cor_RtlImageRvaToSection64(NtHeaders, + Rva, + FileLength); + + if (NtSection != NULL) + return (Base + + (Rva - VAL32(NtSection->VirtualAddress)) + + VAL32(NtSection->PointerToRawData)); + else + return NULL; +} + +EXTERN_C PBYTE Cor_RtlImageRvaToVa(PTR_IMAGE_NT_HEADERS NtHeaders, + PBYTE Base, + ULONG Rva, + ULONG FileLength) +{ + LIMITED_METHOD_CONTRACT; + if (NtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC)) + return Cor_RtlImageRvaToVa32((PTR_IMAGE_NT_HEADERS32)NtHeaders, + Base, Rva, FileLength); + else if(NtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR64_MAGIC)) + return Cor_RtlImageRvaToVa64((PTR_IMAGE_NT_HEADERS64)NtHeaders, + Base, Rva, FileLength); + else { + _ASSERTE(!"Invalid File Type"); + return NULL; + } +} + +EXTERN_C PBYTE Cor_RtlImageDirToVa(PTR_IMAGE_NT_HEADERS NtHeaders, + PBYTE Base, + UINT DirIndex, + ULONG FileLength) +{ + LIMITED_METHOD_CONTRACT; + if (NtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC)) + return Cor_RtlImageRvaToVa32((PTR_IMAGE_NT_HEADERS32)NtHeaders, Base, + VAL32(((PTR_IMAGE_NT_HEADERS32)NtHeaders)->OptionalHeader.DataDirectory[DirIndex].VirtualAddress), + FileLength); + else if(NtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR64_MAGIC)) + return Cor_RtlImageRvaToVa64((PTR_IMAGE_NT_HEADERS64)NtHeaders, Base, + VAL32(((PTR_IMAGE_NT_HEADERS64)NtHeaders)->OptionalHeader.DataDirectory[DirIndex].VirtualAddress), + FileLength); + else { + _ASSERTE(!"Invalid File Type"); + return NULL; + } +} + +EXTERN_C PIMAGE_SECTION_HEADER +Cor_RtlImageRvaRangeToSection(PTR_IMAGE_NT_HEADERS NtHeaders, + ULONG Rva, + ULONG Range, + ULONG FileLength) +{ + LIMITED_METHOD_CONTRACT; + ULONG i; + PTR_IMAGE_SECTION_HEADER NtSection; + + if (!Range) + return Cor_RtlImageRvaToSection(NtHeaders, Rva, FileLength); + + NtSection = PTR_IMAGE_FIRST_SECTION( NtHeaders ); + for (i=0; i<VAL16(NtHeaders->FileHeader.NumberOfSections); i++) { + if (FileLength && + ((VAL32(NtSection->PointerToRawData) > FileLength) || + (VAL32(NtSection->SizeOfRawData) > FileLength - VAL32(NtSection->PointerToRawData)))) + return NULL; + if (Rva >= VAL32(NtSection->VirtualAddress) && + Rva + Range <= VAL32(NtSection->VirtualAddress) + VAL32(NtSection->SizeOfRawData)) + return NtSection; + + ++NtSection; + } + + return NULL; +} + +EXTERN_C DWORD Cor_RtlImageRvaToOffset(PTR_IMAGE_NT_HEADERS NtHeaders, + ULONG Rva, + ULONG FileLength) +{ + LIMITED_METHOD_CONTRACT; + PIMAGE_SECTION_HEADER NtSection = + Cor_RtlImageRvaToSection(NtHeaders, + Rva, + FileLength); + + if (NtSection) + return ((Rva - VAL32(NtSection->VirtualAddress)) + + VAL32(NtSection->PointerToRawData)); + else + return NULL; +} diff --git a/src/utilcode/crossgen/.gitmirror b/src/utilcode/crossgen/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/utilcode/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/utilcode/crossgen/utilcode_crossgen.nativeproj b/src/utilcode/crossgen/utilcode_crossgen.nativeproj new file mode 100644 index 0000000000..618a2eea74 --- /dev/null +++ b/src/utilcode/crossgen/utilcode_crossgen.nativeproj @@ -0,0 +1,21 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + <!--*****************************************************--> + <!--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\xplat\SetCrossGen.props" /> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\Utilcode\utilcode.settings.targets" /> + <!--Leaf project Properties--> + <PropertyGroup> + <BuildSysBinaries>true</BuildSysBinaries> + <OutputName>utilcode_crossgen</OutputName> + </PropertyGroup> + + <ItemGroup> + <CppCompile Include="..\hostimpl.cpp" /> + </ItemGroup> + + <!--Leaf Project Items--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" /> +</Project> diff --git a/src/utilcode/cycletimer.cpp b/src/utilcode/cycletimer.cpp new file mode 100644 index 0000000000..3410669738 --- /dev/null +++ b/src/utilcode/cycletimer.cpp @@ -0,0 +1,85 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + + +#include "cycletimer.h" +#include "winbase.h" +#include "winwrap.h" +#include "assert.h" +#include "utilcode.h" + +bool CycleTimer::GetThreadCyclesS(unsigned __int64* cycles) +{ + BOOL res = FALSE; + res = QueryThreadCycleTime(GetCurrentThread(), cycles); + return res != FALSE; +} + +static const int SampleLoopSize = 1000000; + +// static +double CycleTimer::CyclesPerSecond() +{ + // Windows does not provide a way of converting cycles to time -- reasonably enough, + // since the frequency of a machine may vary, due, e.g., to power management. + // Windows *does* allow you to translate QueryPerformanceCounter counts into time, + // however. So we'll assume that the clock speed stayed constant, and measure both the + // QPC counts and cycles of a short loop, to get a conversion factor. + LARGE_INTEGER lpFrequency; + if (!QueryPerformanceFrequency(&lpFrequency)) return 0.0; + // Otherwise... + LARGE_INTEGER qpcStart; + unsigned __int64 cycleStart; + if (!QueryPerformanceCounter(&qpcStart)) return 0.0; + if (!GetThreadCyclesS(&cycleStart)) return 0.0; + volatile int sum = 0; + for (int k = 0; k < SampleLoopSize; k++) + { + sum += k; + } + LARGE_INTEGER qpcEnd; + if (!QueryPerformanceCounter(&qpcEnd)) return 0.0; + unsigned __int64 cycleEnd; + if (!GetThreadCyclesS(&cycleEnd)) return 0.0; + + double qpcTicks = ((double)qpcEnd.QuadPart) - ((double)qpcStart.QuadPart); + double secs = (qpcTicks / ((double)lpFrequency.QuadPart)); + double cycles = ((double)cycleEnd) - ((double)cycleStart); + return cycles / secs; +} + +// static +unsigned __int64 CycleTimer::QueryOverhead() +{ + unsigned __int64 tot = 0; + unsigned __int64 startCycles; + unsigned __int64 endCycles; + const int N = 1000; + bool b = GetThreadCyclesS(&startCycles); assert(b); + for (int i = 0; i < N; i++) + { + b = GetThreadCyclesS(&endCycles); assert(b); + tot += (endCycles-startCycles); + startCycles = endCycles; + } + return tot/N; +} + +// static +void CycleTimer::InterlockedAddU64(unsigned __int64* loc, unsigned __int64 amount) +{ + volatile __int64* vloc = (volatile __int64*)loc; + unsigned __int64 prev = *vloc; + for (;;) + { + unsigned __int64 next = prev + amount; + __int64 snext = (__int64)next; + __int64 sprev = (__int64)prev; + __int64 res = InterlockedCompareExchange64(vloc, snext, sprev); + if (res == sprev) return; + else prev = (unsigned __int64)res; + } +} + diff --git a/src/utilcode/dac/.gitmirror b/src/utilcode/dac/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/utilcode/dac/.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/utilcode/dac/CMakeLists.txt b/src/utilcode/dac/CMakeLists.txt new file mode 100644 index 0000000000..d792c4ca57 --- /dev/null +++ b/src/utilcode/dac/CMakeLists.txt @@ -0,0 +1,10 @@ + +include(${CLR_DIR}/dac.cmake) + +if(CLR_CMAKE_PLATFORM_UNIX) + add_library(utilcode_dac STATIC ${UTILCODE_SOURCES}) + add_dependencies(utilcode_dac CoreClrPal) +else(CLR_CMAKE_PLATFORM_UNIX) + add_definitions(-DSELF_NO_HOST) + add_library(utilcode_dac STATIC ${UTILCODE_SOURCES} ../hostimpl.cpp) +endif(CLR_CMAKE_PLATFORM_UNIX)
\ No newline at end of file diff --git a/src/utilcode/dac/dirs.proj b/src/utilcode/dac/dirs.proj new file mode 100644 index 0000000000..5556c8634c --- /dev/null +++ b/src/utilcode/dac/dirs.proj @@ -0,0 +1,18 @@ +<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> + + <!--The following projects will build during PHASE 1--> + <ItemGroup Condition="'$(BuildExePhase)' == '1'"> + <ProjectFile Include="HostLocal\dac.nativeproj" /> + </ItemGroup> + + <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" /> +</Project> diff --git a/src/utilcode/dacutil.cpp b/src/utilcode/dacutil.cpp new file mode 100644 index 0000000000..d21b003efb --- /dev/null +++ b/src/utilcode/dacutil.cpp @@ -0,0 +1,251 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// +// Internal data access functionality. +// + +//***************************************************************************** + +#include "stdafx.h" + +#include <winwrap.h> +#include <utilcode.h> +#include <dacprivate.h> +#ifdef FEATURE_IPCMAN +#include <ipcmanagerinterface.h> +#endif // FEATURE_IPCMAN + +//---------------------------------------------------------------------------- +// +// LiveProcDataTarget. +// +//---------------------------------------------------------------------------- + +LiveProcDataTarget::LiveProcDataTarget(HANDLE process, + DWORD processId, + CLRDATA_ADDRESS baseAddressOfEngine) +{ + m_process = process; + m_processId = processId; + m_baseAddressOfEngine = baseAddressOfEngine; +} + +STDMETHODIMP +LiveProcDataTarget::QueryInterface( + THIS_ + IN REFIID InterfaceId, + OUT PVOID* Interface + ) +{ + if (InterfaceId == IID_IUnknown || + InterfaceId == IID_ICLRDataTarget) + { + *Interface = (ICLRDataTarget*)this; + // No need to refcount as this class is contained. + return S_OK; + } + else + { + *Interface = NULL; + return E_NOINTERFACE; + } +} + +STDMETHODIMP_(ULONG) +LiveProcDataTarget::AddRef( + THIS + ) +{ + // No need to refcount as this class is contained. + return 1; +} + +STDMETHODIMP_(ULONG) +LiveProcDataTarget::Release( + THIS + ) +{ + SUPPORTS_DAC_HOST_ONLY; + // No need to refcount as this class is contained. + return 0; +} + +HRESULT STDMETHODCALLTYPE +LiveProcDataTarget::GetMachineType( + /* [out] */ ULONG32 *machine) +{ + LIMITED_METHOD_CONTRACT; + +#if defined(_TARGET_X86_) + *machine = IMAGE_FILE_MACHINE_I386; +#elif defined(_TARGET_AMD64_) + *machine = IMAGE_FILE_MACHINE_AMD64; +#elif defined(_TARGET_ARM_) + *machine = IMAGE_FILE_MACHINE_ARMNT; +#else + PORTABILITY_ASSERT("Unknown Processor"); +#endif + return S_OK; +} + +HRESULT STDMETHODCALLTYPE +LiveProcDataTarget::GetPointerSize( + /* [out] */ ULONG32 *size) +{ + LIMITED_METHOD_CONTRACT; + + *size = sizeof(void*); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE +LiveProcDataTarget::GetImageBase( + /* [string][in] */ LPCWSTR name, + /* [out] */ CLRDATA_ADDRESS *base) +{ + // + // The only image base that the access code cares + // about right now is the base of mscorwks. + // + + if (wcscmp(name, MAIN_CLR_DLL_NAME_W)) + { + return E_NOINTERFACE; + } + + // + // If a base address was specified, use that + // + if (NULL != m_baseAddressOfEngine) + { + *base = m_baseAddressOfEngine; + return S_OK; + } + + // + // Our creator must have told us WHICH clr to work with. + // + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE +LiveProcDataTarget::ReadVirtual( + /* [in] */ CLRDATA_ADDRESS address, + /* [length_is][size_is][out] */ PBYTE buffer, + /* [in] */ ULONG32 request, + /* [optional][out] */ ULONG32 *done) +{ + // ReadProcessMemory will fail if any part of the + // region to read does not have read access. This + // routine attempts to read the largest valid prefix + // so it has to break up reads on page boundaries. + + HRESULT status = S_OK; + ULONG32 totalDone = 0; + SIZE_T read; + ULONG32 readSize; + + while (request > 0) + { + // Calculate bytes to read and don't let read cross + // a page boundary. + readSize = OS_PAGE_SIZE - (ULONG32)(address & (OS_PAGE_SIZE - 1)); + readSize = min(request, readSize); + + if (!ReadProcessMemory(m_process, (PVOID)(ULONG_PTR)address, + buffer, readSize, &read)) + { + if (totalDone == 0) + { + // If we haven't read anything indicate failure. + status = E_FAIL; + } + break; + } + + totalDone += (ULONG32)read; + address += read; + buffer += read; + request -= (ULONG32)read; + } + + *done = totalDone; + return status; +} + +HRESULT STDMETHODCALLTYPE +LiveProcDataTarget::WriteVirtual( + /* [in] */ CLRDATA_ADDRESS address, + /* [size_is][in] */ PBYTE buffer, + /* [in] */ ULONG32 request, + /* [optional][out] */ ULONG32 *done) +{ + // Not necessary yet. + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE +LiveProcDataTarget::GetTLSValue( + /* [in] */ ULONG32 threadID, + /* [in] */ ULONG32 index, + /* [out] */ CLRDATA_ADDRESS* value) +{ + SUPPORTS_DAC; + // Not necessary yet. + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE +LiveProcDataTarget::SetTLSValue( + /* [in] */ ULONG32 threadID, + /* [in] */ ULONG32 index, + /* [in] */ CLRDATA_ADDRESS value) +{ + // Not necessary yet. + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE +LiveProcDataTarget::GetCurrentThreadID( + /* [out] */ ULONG32* threadID) +{ + SUPPORTS_DAC; + // Not necessary yet. + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE +LiveProcDataTarget::GetThreadContext( + /* [in] */ ULONG32 threadID, + /* [in] */ ULONG32 contextFlags, + /* [in] */ ULONG32 contextSize, + /* [out, size_is(contextSize)] */ PBYTE context) +{ + // Not necessary yet. + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE +LiveProcDataTarget::SetThreadContext( + /* [in] */ ULONG32 threadID, + /* [in] */ ULONG32 contextSize, + /* [out, size_is(contextSize)] */ PBYTE context) +{ + // Not necessary yet. + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE +LiveProcDataTarget::Request( + /* [in] */ ULONG32 reqCode, + /* [in] */ ULONG32 inBufferSize, + /* [size_is][in] */ BYTE *inBuffer, + /* [in] */ ULONG32 outBufferSize, + /* [size_is][out] */ BYTE *outBuffer) +{ + // None supported. + return E_INVALIDARG; +} diff --git a/src/utilcode/debug.cpp b/src/utilcode/debug.cpp new file mode 100644 index 0000000000..eec471d49d --- /dev/null +++ b/src/utilcode/debug.cpp @@ -0,0 +1,949 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// Debug.cpp +// +// Helper code for debugging. +//***************************************************************************** +// + + +#include "stdafx.h" +#include "utilcode.h" +#include "ex.h" +#include "corexcep.h" + +#ifdef _DEBUG +#define LOGGING +#endif + + +#include "log.h" + +extern "C" _CRTIMP int __cdecl _flushall(void); + +// Global state counter to implement SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE. +Volatile<LONG> g_DbgSuppressAllocationAsserts = 0; + + +#ifdef _DEBUG + +int LowResourceMessageBoxHelperAnsi( + LPCSTR szText, // Text message + LPCSTR szTitle, // Title + UINT uType); // Style of MessageBox + +//***************************************************************************** +// This struct tracks the asserts we want to ignore in the rest of this +// run of the application. +//***************************************************************************** +struct _DBGIGNOREDATA +{ + char rcFile[_MAX_PATH]; + int iLine; + bool bIgnore; +}; + +typedef CDynArray<_DBGIGNOREDATA> DBGIGNORE; +static BYTE grIgnoreMemory[sizeof(DBGIGNORE)]; +inline DBGIGNORE* GetDBGIGNORE() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + static bool fInit; // = false; + if (!fInit) + { + SCAN_IGNORE_THROW; // Doesn't really throw here. + new (grIgnoreMemory) CDynArray<_DBGIGNOREDATA>(); + fInit = true; + } + + return (DBGIGNORE*)grIgnoreMemory; +} + +// Continue the app on an assert. Still output the assert, but +// Don't throw up a GUI. This is useful for testing fatal error +// paths (like FEEE) where the runtime asserts. +BOOL ContinueOnAssert() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + + static ConfigDWORD fNoGui; + return fNoGui.val(CLRConfig::INTERNAL_ContinueOnAssert); +} + +BOOL NoGuiOnAssert() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + + static ConfigDWORD fNoGui; + return fNoGui.val(CLRConfig::INTERNAL_NoGuiOnAssert); +} + +void DoRaiseExceptionOnAssert(DWORD chance) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_SUPPORTS_DAC; + +#if !defined(DACCESS_COMPILE) + if (chance) + { +#ifndef FEATURE_PAL + PAL_TRY_NAKED + { + RaiseException(EXCEPTION_INTERNAL_ASSERT, 0, 0, NULL); + } + PAL_EXCEPT_NAKED((chance == 1) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + } + PAL_ENDTRY_NAKED +#else // FEATURE_PAL + // For PAL always raise the exception. + RaiseException(EXCEPTION_INTERNAL_ASSERT, 0, 0, NULL); +#endif // FEATURE_PAL + } +#endif // !DACCESS_COMPILE +} + +enum RaiseOnAssertOptions { rTestAndRaise, rTestOnly }; + +BOOL RaiseExceptionOnAssert(RaiseOnAssertOptions option = rTestAndRaise) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_SUPPORTS_DAC; + + // ok for debug-only code to take locks + CONTRACT_VIOLATION(TakesLockViolation); + + DWORD fRet = 0; + +#if !defined(DACCESS_COMPILE) + static ConfigDWORD fRaiseExceptionOnAssert; + // + // we don't want this config key to affect mscordacwks as well! + // + EX_TRY + { + fRet = fRaiseExceptionOnAssert.val(CLRConfig::INTERNAL_RaiseExceptionOnAssert); + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions); + + if (option == rTestAndRaise && fRet != 0) + { + DoRaiseExceptionOnAssert(fRet); + } +#endif // !DACCESS_COMPILE + + return fRet != 0; +} + +BOOL DebugBreakOnAssert() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_SUPPORTS_DAC; + + // ok for debug-only code to take locks + CONTRACT_VIOLATION(TakesLockViolation); + + BOOL fRet = FALSE; + +#ifndef DACCESS_COMPILE + static ConfigDWORD fDebugBreak; + // + // we don't want this config key to affect mscordacwks as well! + // + EX_TRY + { + fRet = fDebugBreak.val(CLRConfig::INTERNAL_DebugBreakOnAssert); + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions); +#endif // DACCESS_COMPILE + + return fRet; +} + +VOID TerminateOnAssert() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + + ShutdownLogging(); + TerminateProcess(GetCurrentProcess(), 123456789); +} + +// Whether this thread is already displaying an assert dialog. +BOOL IsDisplayingAssertDlg() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + + size_t flag = 0; + if (ClrFlsCheckValue(TlsIdx_AssertDlgStatus, (LPVOID *)&flag)) + { + return (flag != 0); + } + return FALSE; +} + +void SetDisplayingAssertDlg(BOOL value) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + + ClrFlsSetValue(TlsIdx_AssertDlgStatus, (LPVOID)(size_t)value); +} + +VOID LogAssert( + LPCSTR szFile, + int iLine, + LPCSTR szExpr +) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_SO_TOLERANT; + STATIC_CONTRACT_DEBUG_ONLY; + + // Log asserts to the stress log. Note that we can't include the szExpr b/c that + // may not be a string literal (particularly for formatt-able asserts). + STRESS_LOG2(LF_ASSERT, LL_ALWAYS, "ASSERT:%s, line:%d\n", szFile, iLine); + + SYSTEMTIME st; +#ifndef FEATURE_PAL + GetLocalTime(&st); +#else + GetSystemTime(&st); +#endif + + WCHAR exename[300]; + WszGetModuleFileName(NULL, exename, sizeof(exename)/sizeof(WCHAR)); + + LOG((LF_ASSERT, + LL_FATALERROR, + "FAILED ASSERT(PID %d [0x%08x], Thread: %d [0x%x]) (%lu/%lu/%lu: %02lu:%02lu:%02lu %s): File: %s, Line %d : %s\n", + GetCurrentProcessId(), + GetCurrentProcessId(), + GetCurrentThreadId(), + GetCurrentThreadId(), + (ULONG)st.wMonth, + (ULONG)st.wDay, + (ULONG)st.wYear, + 1 + (( (ULONG)st.wHour + 11 ) % 12), + (ULONG)st.wMinute, + (ULONG)st.wSecond, + (st.wHour < 12) ? "am" : "pm", + szFile, + iLine, + szExpr)); + LOG((LF_ASSERT, LL_FATALERROR, "RUNNING EXE: %ws\n", exename)); +} + +//***************************************************************************** + +BOOL LaunchJITDebugger() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + + BOOL fSuccess = FALSE; +#ifndef FEATURE_PAL + EX_TRY + { + SString debugger; + GetDebuggerSettingInfo(debugger, NULL); + + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + // We can leave this event as it is since it is inherited by a child process. + // We will block one scheduler, but the process is asking a user if they want to attach debugger. + HandleHolder eventHandle = WszCreateEvent(&sa, TRUE, FALSE, NULL); + if (eventHandle == NULL) + ThrowOutOfMemory(); + + SString cmdLine; + cmdLine.Printf(debugger, GetCurrentProcessId(), eventHandle.GetValue()); + + STARTUPINFO StartupInfo; + memset(&StartupInfo, 0, sizeof(StartupInfo)); + StartupInfo.cb = sizeof(StartupInfo); + StartupInfo.lpDesktop = W("Winsta0\\Default"); + + PROCESS_INFORMATION ProcessInformation; + if (WszCreateProcess(NULL, cmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &StartupInfo, &ProcessInformation)) + { + WaitForSingleObject(eventHandle.GetValue(), INFINITE); + } + + fSuccess = TRUE; + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions); +#endif // !FEATURE_PAL + return fSuccess; +} + + +//***************************************************************************** +// This function is called in order to ultimately return an out of memory +// failed hresult. But this guy will check what environment you are running +// in and give an assert for running in a debug build environment. Usually +// out of memory on a dev machine is a bogus alloction, and this allows you +// to catch such errors. But when run in a stress envrionment where you are +// trying to get out of memory, assert behavior stops the tests. +//***************************************************************************** +HRESULT _OutOfMemory(LPCSTR szFile, int iLine) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + + DbgWriteEx(W("WARNING: Out of memory condition being issued from: %hs, line %d\n"), + szFile, iLine); + return (E_OUTOFMEMORY); +} + +int _DbgBreakCount = 0; +static const char * szLowMemoryAssertMessage = "Assert failure (unable to format)"; + +//***************************************************************************** +// This function will handle ignore codes and tell the user what is happening. +//***************************************************************************** +int _DbgBreakCheck( + LPCSTR szFile, + int iLine, + LPCSTR szExpr, + BOOL fConstrained) +{ + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_DEBUG_ONLY; + + RaiseExceptionOnAssert(rTestAndRaise); + + if (DebugBreakOnAssert()) + { + DebugBreak(); + } + + DBGIGNORE* pDBGIFNORE = GetDBGIGNORE(); + _DBGIGNOREDATA *psData = NULL; + int i; + // Check for ignore all. + for (i=0, psData = pDBGIFNORE->Ptr(); i<pDBGIFNORE->Count(); i++, psData++) + { + if (psData->iLine == iLine && SString::_stricmp(psData->rcFile, szFile) == 0 && + psData->bIgnore == true) + return (false); + } + + CONTRACT_VIOLATION(FaultNotFatal | GCViolation | TakesLockViolation); + + SString debugOutput; + SString dialogOutput; + SString modulePath; + SString dialogTitle; + SString dialogIgnoreMessage; + BOOL formattedMessages = FALSE; + + // If we are low on memory we cannot even format a message. If this happens we want to + // contain the exception here but display as much information as we can about the exception. + if (!fConstrained) + { + EX_TRY + { + ClrGetModuleFileName(0, modulePath); + debugOutput.Printf(W("Assert failure(PID %d [0x%08x], Thread: %d [0x%x]): %hs\n") + W(" File: %hs, Line: %d Image:\n"), + GetCurrentProcessId(), GetCurrentProcessId(), + GetCurrentThreadId(), GetCurrentThreadId(), + szExpr, szFile, iLine); + debugOutput.Append(modulePath); + debugOutput.Append(W("\n")); + + // Change format for message box. The extra spaces in the title + // are there to get around format truncation. + dialogOutput.Printf(W("%hs\n\n%hs, Line: %d\n\nAbort - Kill program\nRetry - Debug\nIgnore - Keep running\n") + W("\n\nImage:\n"), szExpr, szFile, iLine); + dialogOutput.Append(modulePath); + dialogOutput.Append(W("\n")); + dialogTitle.Printf(W("Assert Failure (PID %d, Thread %d/%x) "), + GetCurrentProcessId(), GetCurrentThreadId(), GetCurrentThreadId()); + + dialogIgnoreMessage.Printf(W("Ignore the assert for the rest of this run?\nYes - Assert will never fire again.\nNo - Assert will continue to fire.\n\n%hs\nLine: %d\n"), + szFile, iLine); + + formattedMessages = TRUE; + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions); + } + + // Emit assert in debug output and console for easy access. + if (formattedMessages) + { + WszOutputDebugString(debugOutput); + fwprintf(stderr, W("%s"), (const WCHAR*)debugOutput); + } + else { + // Note: we cannot convert to unicode or concatenate in this situation. + OutputDebugStringA(szLowMemoryAssertMessage); + OutputDebugStringA("\n"); + OutputDebugStringA(szFile); + OutputDebugStringA("\n"); + OutputDebugStringA(szExpr); + OutputDebugStringA("\n"); + printf(szLowMemoryAssertMessage); + printf("\n"); + printf(szFile); + printf("\n"); + printf("%s", szExpr); + printf("\n"); + } + + LogAssert(szFile, iLine, szExpr); + FlushLogging(); // make certain we get the last part of the log + _flushall(); + + if (ContinueOnAssert()) + { + return false; // don't stop debugger. No gui. + } + + if (NoGuiOnAssert()) + { + TerminateOnAssert(); + } + + if (DebugBreakOnAssert()) + { + return(true); // like a retry + } + + if (IsDisplayingAssertDlg()) + { + // We are already displaying an assert dialog box on this thread. The reason why we came here is + // the message loop run by the API we call to display the UI. A message was dispatched and execution + // ended up in the runtime where it fired another assertion. If this happens before the dialog had + // a chance to fully initialize the original assert may not be visible which is misleading for the + // user. So we just continue. + return false; + } + SetDisplayingAssertDlg(TRUE); + + // Tell user there was an error. + _DbgBreakCount++; + int ret; + if (formattedMessages) { + ret = UtilMessageBoxCatastrophicNonLocalized( + W("%s"), dialogTitle, MB_ABORTRETRYIGNORE | MB_ICONEXCLAMATION, TRUE, (const WCHAR*)dialogOutput); + } + else + { + ret = LowResourceMessageBoxHelperAnsi( + szExpr, szLowMemoryAssertMessage, MB_ABORTRETRYIGNORE | MB_ICONEXCLAMATION); + } + --_DbgBreakCount; + + SetDisplayingAssertDlg(FALSE); + + switch(ret) + { + // For abort, just quit the app. + case IDABORT: + TerminateProcess(GetCurrentProcess(), 1); +// WszFatalAppExit(0, W("Shutting down")); + break; + + // Tell caller to break at the correct loction. + case IDRETRY: + + if (IsDebuggerPresent()) + { + SetErrorMode(0); + } + else + { + LaunchJITDebugger(); + } + + return (true); + + // If we want to ignore the assert, find out if this is forever. + case IDIGNORE: + if (formattedMessages) + { + if (UtilMessageBoxCatastrophicNonLocalized( + dialogIgnoreMessage, + W("Ignore Assert Forever?"), + MB_ICONQUESTION | MB_YESNO, + TRUE) != IDYES) + { + break; + } + } + else + { + if (LowResourceMessageBoxHelperAnsi( + "Ignore the assert for the rest of this run?\nYes - Assert will never fire again.\nNo - Assert will continue to fire.\n", + "Ignore Assert Forever?", + MB_ICONQUESTION | MB_YESNO) != IDYES) + { + break; + } + } + if ((psData = pDBGIFNORE->Append()) == 0) + return (false); + psData->bIgnore = true; + psData->iLine = iLine; + strcpy(psData->rcFile, szFile); + break; + } + + return (false); +} + +int _DbgBreakCheckNoThrow( + LPCSTR szFile, + int iLine, + LPCSTR szExpr, + BOOL fConstrained) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_DEBUG_ONLY; + + RaiseExceptionOnAssert(rTestAndRaise); + + if (DebugBreakOnAssert()) + { + DebugBreak(); + } + + int failed = false; + int result = false; + EX_TRY + { + result = _DbgBreakCheck(szFile, iLine, szExpr, fConstrained); + } + EX_CATCH + { + failed = true; + } + EX_END_CATCH(SwallowAllExceptions); + + if (failed == TRUE) + { + return true; + } + return result; +} + +#ifndef FEATURE_PAL +// Get the timestamp from the PE file header. This is useful +unsigned DbgGetEXETimeStamp() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_DEBUG_ONLY; + + static ULONG cache = 0; + if (cache == 0) { + // Use GetModuleHandleA to avoid contracts - this results in a recursive loop initializing the + // debug allocator. + BYTE* imageBase = (BYTE*) GetModuleHandleA(NULL); + if (imageBase == 0) + return(0); + IMAGE_DOS_HEADER *pDOS = (IMAGE_DOS_HEADER*) imageBase; + if ((pDOS->e_magic != VAL16(IMAGE_DOS_SIGNATURE)) || (pDOS->e_lfanew == 0)) + return(0); + + IMAGE_NT_HEADERS *pNT = (IMAGE_NT_HEADERS*) (VAL32(pDOS->e_lfanew) + imageBase); + cache = VAL32(pNT->FileHeader.TimeDateStamp); + } + + return cache; +} +#endif // FEATURE_PAL +// // // +// // // The following function +// // // computes the binomial distribution, with which to compare +// // // hash-table statistics. If a hash function perfectly randomizes +// // // its input, one would expect to see F chains of length K, in a +// // // table with N buckets and M elements, where F is +// // // +// // // F(K,M,N) = N * (M choose K) * (1 - 1/N)^(M-K) * (1/N)^K. +// // // +// // // Don't call this with a K larger than 159. +// // // + +#if !defined(NO_CRT) + +#include <math.h> + +#define MAX_BUCKETS_MATH 160 + +double Binomial (DWORD K, DWORD M, DWORD N) +{ + STATIC_CONTRACT_LEAF; + + if (K >= MAX_BUCKETS_MATH) + return -1 ; + + static double rgKFact [MAX_BUCKETS_MATH] ; + DWORD i ; + + if (rgKFact[0] == 0) + { + rgKFact[0] = 1 ; + for (i=1; i<MAX_BUCKETS_MATH; i++) + rgKFact[i] = rgKFact[i-1] * i ; + } + + double MchooseK = 1 ; + + for (i = 0; i < K; i++) + MchooseK *= (M - i) ; + + MchooseK /= rgKFact[K] ; + + double OneOverNToTheK = pow (1./N, K) ; + + double QToTheMMinusK = pow (1.-1./N, M-K) ; + + double P = MchooseK * OneOverNToTheK * QToTheMMinusK ; + + return N * P ; +} + +#endif // !NO_CRT + +// Called from within the IfFail...() macros. Set a breakpoint here to break on +// errors. +VOID DebBreak() +{ + STATIC_CONTRACT_LEAF; + static int i = 0; // add some code here so that we'll be able to set a BP + i++; +} + + +VOID DebBreakHr(HRESULT hr) +{ + STATIC_CONTRACT_LEAF; + static int i = 0; // add some code here so that we'll be able to set a BP + _ASSERTE(hr != (HRESULT) 0xcccccccc); + i++; + + // @CONSIDER: We maybe want this on retail builds. + #ifdef _DEBUG + static DWORD dwBreakHR = 99; + + if (dwBreakHR == 99) + dwBreakHR = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnHR); + if (dwBreakHR == (DWORD)hr) + { + _DbgBreak(); + } + #endif +} + +CHAR g_szExprWithStack2[10480]; +void *dbgForceToMemory; // dummy pointer that pessimises enregistration + +#ifdef MDA_SUPPORTED +#ifdef _DEBUG +BOOL g_bMdaDisableAsserts = FALSE; +#endif +#endif + +int g_BufferLock = -1; + +VOID DbgAssertDialog(const char *szFile, int iLine, const char *szExpr) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY; + + DEBUG_ONLY_FUNCTION; + +#ifdef MDA_SUPPORTED +#ifdef _DEBUG + if (g_bMdaDisableAsserts) + return; +#endif +#endif + +#ifdef DACCESS_COMPILE + // In the DAC case, asserts can mean one of two things. + // Either there is a bug in the DAC infrastructure itself (a real assert), or just + // that the target is corrupt or being accessed at an inconsistent state (a "target + // consistency failure"). For target consistency failures, we need a mechanism to disable them + // (without affecting other asserts) so that we can test corrupt / inconsistent targets. + + // @dbgtodo DAC: For now we're treating all asserts as if they are target consistency checks. + // In the future we should differentiate the two so that real asserts continue to fire, even when + // we expect the target to be inconsistent. See DevDiv Bugs 31674. + if( !DacTargetConsistencyAssertsEnabled() ) + { + return; + } +#endif // #ifndef DACCESS_COMPILE + + // We increment this every time we use the SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE + // macro below. If it is a big number it means either a lot of threads are asserting + // or we have a recursion in the Assert logic (usually the latter). At least with this + // code in place, we don't get stack overflow (and the process torn down). + // the correct fix is to avoid calling asserting when allocating memory with an assert. + if (g_DbgSuppressAllocationAsserts > 16) + DebugBreak(); + + SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; + + // Raising the assert dialog can cause us to re-enter the host when allocating + // memory for the string. Since this is debug-only code, we can safely skip + // violation asserts here, particularly since they can also cause infinite + // recursion. + PERMANENT_CONTRACT_VIOLATION(HostViolation, ReasonDebugOnly); + + dbgForceToMemory = &szFile; //make certain these args are available in the debugger + dbgForceToMemory = &iLine; + dbgForceToMemory = &szExpr; + + RaiseExceptionOnAssert(rTestAndRaise); + + if (DebugBreakOnAssert()) + { + DebugBreak(); + } + + BOOL fConstrained = FALSE; + + DWORD dwAssertStacktrace = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertStacktrace); + +#if !defined(DACCESS_COMPILE) && defined(FEATURE_STACK_PROBE) + //global g_fpCheckNStackPagesAvailable is not present when SO infrastructure code is not present + // Trying to get a stack trace if there is little stack available can cause a silent process + // teardown, so only try to do this there is plenty of stack. + if ((dwAssertStacktrace) != 0 && (g_fpCheckNStackPagesAvailable != NULL)) + { + if (!g_fpCheckNStackPagesAvailable(12)) + { + fConstrained = TRUE; + } + } +#endif + + LONG lAlreadyOwned = InterlockedExchange((LPLONG)&g_BufferLock, 1); + if (fConstrained || dwAssertStacktrace == 0 || lAlreadyOwned == 1) { + if (1 == _DbgBreakCheckNoThrow(szFile, iLine, szExpr, fConstrained)) + _DbgBreak(); + } else { + BOOL fGotStackTrace = FALSE; + char *szExprToDisplay = &g_szExprWithStack2[0]; + +#if !defined(DACCESS_COMPILE) && !defined(FEATURE_PAL) + EX_TRY + { + FAULT_NOT_FATAL(); + strcpy(szExprToDisplay, szExpr); + _ASSERTE(szExprToDisplay == g_szExprWithStack2); + strcat_s(szExprToDisplay, _countof(g_szExprWithStack2), "\n\n"); + GetStringFromStackLevels(1, 10, szExprToDisplay + strlen(szExprToDisplay)); + fGotStackTrace = TRUE; + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions); +#endif + + // If we failed to get the stack trace, simply display the assert without one. + if (!fGotStackTrace) + szExprToDisplay = (char*)szExpr; + + if (1 == _DbgBreakCheckNoThrow(szFile, iLine, szExprToDisplay, !fGotStackTrace)) + _DbgBreak(); + + g_BufferLock = 0; + } +} // DbgAssertDialog + +#if !defined(DACCESS_COMPILE) +//----------------------------------------------------------------------------- +// Returns an a stacktrace for a given context. +// Very useful inside exception filters. +// Returns true if successful, false on failure (such as OOM). +// This never throws. +//----------------------------------------------------------------------------- +bool GetStackTraceAtContext(SString & s, CONTEXT * pContext) +{ + SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; + STATIC_CONTRACT_DEBUG_ONLY; + + // NULL means use the current context. + bool fSuccess = false; + + FAULT_NOT_FATAL(); + +#ifndef FEATURE_PAL + EX_TRY + { + const int cTotal = cfrMaxAssertStackLevels - 1; + // If we have a supplied context, then don't skip any frames. Else we'll + // be using the current context, so skip this frame. + const int cSkip = (pContext == NULL) ? 1 : 0; + char * szString = s.OpenANSIBuffer(cchMaxAssertStackLevelStringLen * cTotal); + GetStringFromStackLevels(cSkip, cTotal, szString, pContext); + s.CloseBuffer((COUNT_T) strlen(szString)); + + // If we made it this far w/o throwing, we succeeded. + fSuccess = true; + } + EX_CATCH + { + // Nothing to do here. + } + EX_END_CATCH(SwallowAllExceptions); +#endif // FEATURE_PAL + + return fSuccess; +} // GetStackTraceAtContext +#endif // !defined(DACCESS_COMPILE) +#endif // _DEBUG + +// This helper will throw up a message box without allocating or using stack if possible, and is +// appropriate for either low memory or low stack situations. +int LowResourceMessageBoxHelperAnsi( + LPCSTR szText, // Text message + LPCSTR szTitle, // Title + UINT uType) // Style of MessageBox +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return IDCANCEL;); + } + CONTRACTL_END; + + // In low memory or stack constrained code we cannot format or convert strings, so use the + // ANSI version. + int result = MessageBoxA(NULL, szText, szTitle, uType); + return result; +} + + +/**************************************************************************** + The following two functions are defined to allow Free builds to call + DebugBreak or to Assert with a stack trace for unexpected fatal errors. + Typically these paths are enabled via a registry key in a Free Build +*****************************************************************************/ + +VOID __FreeBuildDebugBreak() +{ + WRAPPER_NO_CONTRACT; // If we're calling this, we're well past caring about contract consistency! + + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnRetailAssert)) + { + DebugBreak(); + } +} + +void *freForceToMemory; // dummy pointer that pessimises enregistration + +void DECLSPEC_NORETURN __FreeBuildAssertFail(const char *szFile, int iLine, const char *szExpr) +{ + WRAPPER_NO_CONTRACT; // If we're calling this, we're well past caring about contract consistency! + + freForceToMemory = &szFile; //make certain these args are available in the debugger + freForceToMemory = &iLine; + freForceToMemory = &szExpr; + + __FreeBuildDebugBreak(); + + SString buffer; + SString modulePath; + + // Give assert in output for easy access. + ClrGetModuleFileName(0, modulePath); +#ifndef FEATURE_PAL + buffer.Printf(W("CLR: Assert failure(PID %d [0x%08x], Thread: %d [0x%x]): %hs\n") + W(" File: %hs, Line: %d Image:\n"), + GetCurrentProcessId(), GetCurrentProcessId(), + GetCurrentThreadId(), GetCurrentThreadId(), + szExpr, szFile, iLine); + buffer.Append(modulePath); + buffer.Append(W("\n")); + WszOutputDebugString(buffer); + // Write out the error to the console + _putws(buffer); +#else // FEATURE_PAL + // UNIXTODO: Do this for Unix. +#endif // FEATURE_PAL + // Log to the stress log. Note that we can't include the szExpr b/c that + // may not be a string literal (particularly for formatt-able asserts). + STRESS_LOG2(LF_ASSERT, LL_ALWAYS, "ASSERT:%s, line:%d\n", szFile, iLine); + + FlushLogging(); // make certain we get the last part of the log + + _flushall(); + + // TerminateOnAssert(); + ShutdownLogging(); + + // Failing here implies an error in the runtime - hence we use + // COR_E_EXECUTIONENGINE + TerminateProcess(GetCurrentProcess(), COR_E_EXECUTIONENGINE); + + UNREACHABLE(); +} + +//=================================================================================== +// Used by the ex.h macro: EX_CATCH_HRESULT_AND_NGEN_CLEAN(_hr) +// which is used by ngen and mscorsvc to catch unexpected HRESULT +// from one of the RPC calls. +//=================================================================================== +void RetailAssertIfExpectedClean() +{ + static ConfigDWORD g_NGenClean; + if (g_NGenClean.val(CLRConfig::EXTERNAL_NGenClean) == 1) + { + _ASSERTE_ALL_BUILDS("clr/src/Utilcode/Debug.cpp", !"Error during NGen: expected no exceptions to be thrown"); + } +} + diff --git a/src/utilcode/delayloadhelpers.cpp b/src/utilcode/delayloadhelpers.cpp new file mode 100644 index 0000000000..15dddaf75e --- /dev/null +++ b/src/utilcode/delayloadhelpers.cpp @@ -0,0 +1,116 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// + +// +// Contains convenience functionality for lazily loading modules +// and getting entrypoints within them. +// + +#include "stdafx.h" + +#include "crtwrap.h" +#include "winwrap.h" +#include "utilcode.h" +#include "clrhost.h" +#include "holder.h" +#include "delayloadhelpers.h" + +namespace DelayLoad +{ + //================================================================================================================= + // Used to synchronize initialization. Is not used when initialization has already taken place. + + static CRITSEC_COOKIE g_pLock = nullptr; + + //================================================================================================================= + // Creates and initializes g_pLock when first used. + + static HRESULT InitializeLock() + { + STATIC_CONTRACT_LIMITED_METHOD; + HRESULT hr = S_OK; + + CRITSEC_COOKIE pLock = ClrCreateCriticalSection(CrstLeafLock, CRST_REENTRANCY); + IfNullRet(pLock); + if (InterlockedCompareExchangeT<CRITSEC_COOKIE>(&g_pLock, pLock, nullptr) != nullptr) + { + ClrDeleteCriticalSection(pLock); + } + + return S_OK; + } + + //================================================================================================================= + HRESULT Module::GetValue(HMODULE *pHMODULE) + { + STATIC_CONTRACT_LIMITED_METHOD; + HRESULT hr = S_OK; + + if (pHMODULE == nullptr) + { + return E_INVALIDARG; + } + + if (!m_fInitialized) + { + IfFailRet(InitializeLock()); + + HModuleHolder hMod = ::LoadLibraryW(m_wzDllName); + hr = (hMod == nullptr) ? HRESULT_FROM_GetLastError() : S_OK; + _ASSERTE(FAILED(hr) == (hMod == nullptr)); + + { // Lock scope + CRITSEC_Holder lock(g_pLock); + if (!m_fInitialized) + { + m_hr = hr; + m_hMod = hMod.Extract(); + m_fInitialized = true; + } + } + } + + _ASSERTE(m_fInitialized); + *pHMODULE = m_hMod; + return m_hr; + } + + //================================================================================================================= + HRESULT Function::GetValue(LPVOID * ppvFunc) + { + STATIC_CONTRACT_LIMITED_METHOD; + HRESULT hr = S_OK; + + if (ppvFunc == nullptr) + { + return E_INVALIDARG; + } + + if (!m_fInitialized) + { + HMODULE hMod = nullptr; + IfFailRet(m_pModule->GetValue(&hMod)); + + LPVOID pvFunc = ::GetProcAddress(hMod, m_szFunctionName); + hr = (pvFunc == nullptr) ? HRESULT_FROM_GetLastError() : S_OK; + + { // Lock scope + CRITSEC_Holder lock(g_pLock); + if (!m_fInitialized) + { + m_hr = hr; + m_pvFunction = pvFunc; + m_fInitialized = true; + } + } + } + + _ASSERTE(m_fInitialized); + *ppvFunc = m_pvFunction; + return m_hr; + } +} + diff --git a/src/utilcode/dirs.proj b/src/utilcode/dirs.proj new file mode 100644 index 0000000000..9749af2b11 --- /dev/null +++ b/src/utilcode/dirs.proj @@ -0,0 +1,27 @@ +<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> + + <!--The following projects will build during PHASE 1--> + <ItemGroup Condition="'$(BuildExePhase)' == '1'"> + <ProjectFile Include="dyncrt\dyncrt.nativeproj" /> + <ProjectFile Include="dyncrtnohost\dyncrtnohost.nativeproj" /> + <ProjectFile Include="staticcrt\staticcrt.nativeproj" /> + <ProjectFile Include="staticnohost\staticnohost.nativeproj" /> + <ProjectFile Include="dac\dirs.proj" /> + </ItemGroup> + + <!--The following projects will build during PHASE 1 of the Desktop build--> + <ItemGroup Condition="'$(BuildExePhase)' == '1' and '$(FeatureCoreclr)' != 'true'"> + <ProjectFile Include="nodependencies\utilcode-nodependencies.nativeproj" /> + </ItemGroup> + + <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" /> +</Project> diff --git a/src/utilcode/dlwrap.cpp b/src/utilcode/dlwrap.cpp new file mode 100644 index 0000000000..6cd8f44932 --- /dev/null +++ b/src/utilcode/dlwrap.cpp @@ -0,0 +1,231 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + + +#include "stdafx.h" // Precompiled header key. +#include "utilcode.h" +#include "metadata.h" +#include "ex.h" +#include "pedecoder.h" + +#include <wininet.h> +#include <urlmon.h> +#include <version.h> + +DWORD +GetFileVersionInfoSizeW_NoThrow( + LPCWSTR lptstrFilename, /* Filename of version stamped file */ + LPDWORD lpdwHandle + ) +{ + WRAPPER_NO_CONTRACT; + HRESULT hr=S_OK; + DWORD dwRet=0; + EX_TRY + { + dwRet=GetFileVersionInfoSize( (LPWSTR)lptstrFilename, lpdwHandle ); + } + EX_CATCH_HRESULT(hr); + if (hr!=S_OK) + SetLastError(hr); + return dwRet; + +} + +BOOL +GetFileVersionInfoW_NoThrow( + LPCWSTR lptstrFilename, /* Filename of version stamped file */ + DWORD dwHandle, /* Information from GetFileVersionSize */ + DWORD dwLen, /* Length of buffer for info */ + LPVOID lpData + ) +{ + WRAPPER_NO_CONTRACT; + HRESULT hr=S_OK; + BOOL bRet=FALSE; + EX_TRY + { + bRet=GetFileVersionInfo( (LPWSTR)lptstrFilename, dwHandle,dwLen,lpData ); + } + EX_CATCH_HRESULT(hr); + if (hr!=S_OK) + SetLastError(hr); + return bRet; + +} + +BOOL +VerQueryValueW_NoThrow( + const LPVOID pBlock, + LPCWSTR lpSubBlock, + LPVOID * lplpBuffer, + PUINT puLen + ) +{ + WRAPPER_NO_CONTRACT; + HRESULT hr=S_OK; + BOOL bRet=FALSE; + EX_TRY + { + bRet=VerQueryValueW( pBlock, (LPWSTR)lpSubBlock,lplpBuffer,puLen ); + } + EX_CATCH_HRESULT(hr); + if (hr!=S_OK) + SetLastError(hr); + return bRet; + +} + +#if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE) +// The following functions are not used in CoreCLR. Normally LINKER can remove these functions +// from generated files. But LINKER does it in two steps: +// 1. If no function in a source file is used, the file is ignored by LINKER +// 2. If one function is used, LINKER will first make sure all imported functions in the file +// is available, and then it will remove unused functions. +// Instead of specifying all libs for imported functions needed by the following codes, we just +// remove them from compiling phase. +BOOL +CreateUrlCacheEntryW_NoThrow( + IN LPCWSTR lpszUrlName, + IN DWORD dwExpectedFileSize, + IN LPCWSTR lpszFileExtension, + __out_ecount(MAX_PATH+1) LPWSTR lpszFileName, + IN DWORD dwReserved + ) +{ + WRAPPER_NO_CONTRACT; + HRESULT hr=S_OK; + BOOL bRet=FALSE; + EX_TRY + { + bRet=CreateUrlCacheEntryW(lpszUrlName,dwExpectedFileSize,lpszFileExtension, + lpszFileName,dwReserved); + } + EX_CATCH_HRESULT(hr); + if (hr!=S_OK) + SetLastError(hr); + return bRet; + +} + +BOOL +CommitUrlCacheEntryW_NoThrow( + IN LPCWSTR lpszUrlName, + IN LPCWSTR lpszLocalFileName, + IN FILETIME ExpireTime, + IN FILETIME LastModifiedTime, + IN DWORD CacheEntryType, + IN LPCWSTR lpHeaderInfo, + IN DWORD dwHeaderSize, + IN LPCWSTR lpszFileExtension, + IN LPCWSTR lpszOriginalUrl + ) +{ + WRAPPER_NO_CONTRACT; + HRESULT hr=S_OK; + BOOL bRet=FALSE; + EX_TRY + { + bRet=CommitUrlCacheEntryW(lpszUrlName,lpszLocalFileName,ExpireTime, + LastModifiedTime,CacheEntryType,(LPWSTR)lpHeaderInfo, + dwHeaderSize,lpszFileExtension,lpszOriginalUrl); + } + EX_CATCH_HRESULT(hr); + if (hr!=S_OK) + SetLastError(hr); + return bRet; + +} + +BOOL +InternetTimeToSystemTimeA_NoThrow( + IN LPCSTR lpszTime, // NULL terminated string + OUT SYSTEMTIME *pst, // output in GMT time + IN DWORD dwReserved + ) +{ + WRAPPER_NO_CONTRACT; + HRESULT hr=S_OK; + BOOL bRet=FALSE; + EX_TRY + { + bRet=InternetTimeToSystemTimeA(lpszTime,pst,dwReserved); + } + EX_CATCH_HRESULT(hr); + if (hr!=S_OK) + SetLastError(hr); + return bRet; + +} + +HRESULT +CoInternetCreateSecurityManager_NoThrow( + IServiceProvider *pSP, + IInternetSecurityManager **ppSM, + DWORD dwReserved + ) +{ + WRAPPER_NO_CONTRACT; + HRESULT hr=S_OK; + EX_TRY + { + hr=CoInternetCreateSecurityManager(pSP,ppSM, dwReserved); + } + EX_CATCH_HRESULT(hr); + return hr; +} + +HRESULT +URLDownloadToCacheFileW_NoThrow( + LPUNKNOWN lpUnkcaller, + LPCWSTR szURL, + __out_ecount(dwBufLength) LPWSTR szFileName, + DWORD dwBufLength, + DWORD dwReserved, + IBindStatusCallback *pBSC + ) +{ + WRAPPER_NO_CONTRACT; + HRESULT hr=S_OK; + EX_TRY + { + hr=URLDownloadToCacheFileW(lpUnkcaller,szURL,szFileName,dwBufLength,dwReserved,pBSC); + } + EX_CATCH_HRESULT(hr); + return hr; +} + +HRESULT +CoInternetGetSession_NoThrow( + WORD dwSessionMode, + IInternetSession **ppIInternetSession, + DWORD dwReserved + ) +{ + WRAPPER_NO_CONTRACT; + HRESULT hr=S_OK; + EX_TRY + { + hr=CoInternetGetSession(dwSessionMode,ppIInternetSession,dwReserved); + } + EX_CATCH_HRESULT(hr); + return hr; +} + +HRESULT +CopyBindInfo_NoThrow( + const BINDINFO * pcbiSrc, BINDINFO * pbiDest + ) +{ + WRAPPER_NO_CONTRACT; + HRESULT hr=S_OK; + EX_TRY + { + hr=CopyBindInfo(pcbiSrc,pbiDest ); + } + EX_CATCH_HRESULT(hr); + return hr; +} +#endif // FEATURE_CORECLR && !CROSSGEN_COMPILE diff --git a/src/utilcode/downlevel.cpp b/src/utilcode/downlevel.cpp new file mode 100644 index 0000000000..3b818e59f2 --- /dev/null +++ b/src/utilcode/downlevel.cpp @@ -0,0 +1,2736 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//////////////////////////////////////////////////////////////////////////// +// +// File: downlevel.cpp +// + + +// +// Purpose: functions that need to be emulated on downlevel platforms. +// +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" + +#if defined(ENABLE_DOWNLEVEL_FOR_NLS) + +#ifndef LOCALE_SNAME +#define LOCALE_SNAME 0x0000005c +#endif +#include "downlevel.h" +#include "newapis.h" +#include "utilcode.h" +#include "sstring.h" +#include "ex.h" + +#define LCID_AZ_CYRL_AZ 0x0082c + +// +// Macro to check if more than one bit is set. +// Returns 1 if more than one bit set, 0 otherwise. +// +#define MORE_THAN_ONE(f, bits) (((f & bits) - 1) & (f & bits)) + +struct LCIDEntry +{ + LCID lcid; + LPCWSTR wszName; +}; + +// Known (ie: <= Vista) Name/LCID lookup table, sorted by LCID +const static LCIDEntry s_lcids[]= +{ + // Neutrals + { 0x00001, W("ar") }, // Neutral Locale + { 0x00002, W("bg") }, // Neutral Locale + { 0x00003, W("ca") }, // Neutral Locale + { 0x00004, W("zh-Hans") }, // Neutral Locale + { 0x00005, W("cs") }, // Neutral Locale + { 0x00006, W("da") }, // Neutral Locale + { 0x00007, W("de") }, // Neutral Locale + { 0x00008, W("el") }, // Neutral Locale + { 0x00009, W("en") }, // Neutral Locale + { 0x0000a, W("es") }, // Neutral Locale + { 0x0000b, W("fi") }, // Neutral Locale + { 0x0000c, W("fr") }, // Neutral Locale + { 0x0000d, W("he") }, // Neutral Locale + { 0x0000e, W("hu") }, // Neutral Locale + { 0x0000f, W("is") }, // Neutral Locale + { 0x00010, W("it") }, // Neutral Locale + { 0x00011, W("ja") }, // Neutral Locale + { 0x00012, W("ko") }, // Neutral Locale + { 0x00013, W("nl") }, // Neutral Locale + { 0x00014, W("no") }, // Neutral Locale + { 0x00015, W("pl") }, // Neutral Locale + { 0x00016, W("pt") }, // Neutral Locale + { 0x00017, W("rm") }, // Neutral Locale + { 0x00018, W("ro") }, // Neutral Locale + { 0x00019, W("ru") }, // Neutral Locale + { 0x0001a, W("hr") }, // Neutral Locale + { 0x0001b, W("sk") }, // Neutral Locale + { 0x0001c, W("sq") }, // Neutral Locale + { 0x0001d, W("sv") }, // Neutral Locale + { 0x0001e, W("th") }, // Neutral Locale + { 0x0001f, W("tr") }, // Neutral Locale + { 0x00020, W("ur") }, // Neutral Locale + { 0x00021, W("id") }, // Neutral Locale + { 0x00022, W("uk") }, // Neutral Locale + { 0x00023, W("be") }, // Neutral Locale + { 0x00024, W("sl") }, // Neutral Locale + { 0x00025, W("et") }, // Neutral Locale + { 0x00026, W("lv") }, // Neutral Locale + { 0x00027, W("lt") }, // Neutral Locale + { 0x00028, W("tg") }, // Neutral Locale + { 0x00029, W("fa") }, // Neutral Locale + { 0x0002a, W("vi") }, // Neutral Locale + { 0x0002b, W("hy") }, // Neutral Locale + { 0x0002c, W("az") }, // Neutral Locale + { 0x0002d, W("eu") }, // Neutral Locale + { 0x0002e, W("hsb") }, // Neutral Locale + { 0x0002f, W("mk") }, // Neutral Locale + { 0x00032, W("tn") }, // Neutral Locale + { 0x00034, W("xh") }, // Neutral Locale + { 0x00035, W("zu") }, // Neutral Locale + { 0x00036, W("af") }, // Neutral Locale + { 0x00037, W("ka") }, // Neutral Locale + { 0x00038, W("fo") }, // Neutral Locale + { 0x00039, W("hi") }, // Neutral Locale + { 0x0003a, W("mt") }, // Neutral Locale + { 0x0003b, W("se") }, // Neutral Locale + { 0x0003c, W("ga") }, // Neutral Locale + { 0x0003e, W("ms") }, // Neutral Locale + { 0x0003f, W("kk") }, // Neutral Locale + { 0x00040, W("ky") }, // Neutral Locale + { 0x00041, W("sw") }, // Neutral Locale + { 0x00042, W("tk") }, // Neutral Locale + { 0x00043, W("uz") }, // Neutral Locale + { 0x00044, W("tt") }, // Neutral Locale + { 0x00045, W("bn") }, // Neutral Locale + { 0x00046, W("pa") }, // Neutral Locale + { 0x00047, W("gu") }, // Neutral Locale + { 0x00048, W("or") }, // Neutral Locale + { 0x00049, W("ta") }, // Neutral Locale + { 0x0004a, W("te") }, // Neutral Locale + { 0x0004b, W("kn") }, // Neutral Locale + { 0x0004c, W("ml") }, // Neutral Locale + { 0x0004d, W("as") }, // Neutral Locale + { 0x0004e, W("mr") }, // Neutral Locale + { 0x0004f, W("sa") }, // Neutral Locale + { 0x00050, W("mn") }, // Neutral Locale + { 0x00051, W("bo") }, // Neutral Locale + { 0x00052, W("cy") }, // Neutral Locale + { 0x00053, W("km") }, // Neutral Locale + { 0x00054, W("lo") }, // Neutral Locale + { 0x00056, W("gl") }, // Neutral Locale + { 0x00057, W("kok") }, // Neutral Locale + { 0x0005a, W("syr") }, // Neutral Locale + { 0x0005b, W("si") }, // Neutral Locale + { 0x0005d, W("iu") }, // Neutral Locale + { 0x0005e, W("am") }, // Neutral Locale + { 0x0005f, W("tzm") }, // Neutral Locale + { 0x00061, W("ne") }, // Neutral Locale + { 0x00062, W("fy") }, // Neutral Locale + { 0x00063, W("ps") }, // Neutral Locale + { 0x00064, W("fil") }, // Neutral Locale + { 0x00065, W("dv") }, // Neutral Locale + { 0x00068, W("ha") }, // Neutral Locale + { 0x0006a, W("yo") }, // Neutral Locale + { 0x0006b, W("quz") }, // Neutral Locale + { 0x0006c, W("nso") }, // Neutral Locale + { 0x0006d, W("ba") }, // Neutral Locale + { 0x0006e, W("lb") }, // Neutral Locale + { 0x0006f, W("kl") }, // Neutral Locale + { 0x00070, W("ig") }, // Neutral Locale + { 0x00078, W("ii") }, // Neutral Locale + { 0x0007a, W("arn") }, // Neutral Locale + { 0x0007c, W("moh") }, // Neutral Locale + { 0x0007e, W("br") }, // Neutral Locale + { 0x00080, W("ug") }, // Neutral Locale + { 0x00081, W("mi") }, // Neutral Locale + { 0x00082, W("oc") }, // Neutral Locale + { 0x00083, W("co") }, // Neutral Locale + { 0x00084, W("gsw") }, // Neutral Locale + { 0x00085, W("sah") }, // Neutral Locale + { 0x00086, W("qut") }, // Neutral Locale + { 0x00087, W("rw") }, // Neutral Locale + { 0x00088, W("wo") }, // Neutral Locale + { 0x0008c, W("prs") }, // Neutral Locale + + // Specific Cultures + { 0x00401, W("ar-SA") }, + { 0x00402, W("bg-BG") }, + { 0x00403, W("ca-ES") }, + { 0x00404, W("zh-TW") }, + { 0x00405, W("cs-CZ") }, + { 0x00406, W("da-DK") }, + { 0x00407, W("de-DE") }, + { 0x00408, W("el-GR") }, + { 0x00409, W("en-US") }, + // es-ES_tradnl only gets used if specifically asked for because + // GetCultures() won't return it. (It's not a real locale, its an alt sort) + { 0x0040a, W("es-ES_tradnl") }, + { 0x0040b, W("fi-FI") }, + { 0x0040c, W("fr-FR") }, + { 0x0040d, W("he-IL") }, + { 0x0040e, W("hu-HU") }, + { 0x0040f, W("is-IS") }, + { 0x00410, W("it-IT") }, + { 0x00411, W("ja-JP") }, + { 0x00412, W("ko-KR") }, + { 0x00413, W("nl-NL") }, + { 0x00414, W("nb-NO") }, + { 0x00415, W("pl-PL") }, + { 0x00416, W("pt-BR") }, + { 0x00417, W("rm-CH") }, + { 0x00418, W("ro-RO") }, + { 0x00419, W("ru-RU") }, + { 0x0041a, W("hr-HR") }, + { 0x0041b, W("sk-SK") }, + { 0x0041c, W("sq-AL") }, + { 0x0041d, W("sv-SE") }, + { 0x0041e, W("th-TH") }, + { 0x0041f, W("tr-TR") }, + { 0x00420, W("ur-PK") }, + { 0x00421, W("id-ID") }, + { 0x00422, W("uk-UA") }, + { 0x00423, W("be-BY") }, + { 0x00424, W("sl-SI") }, + { 0x00425, W("et-EE") }, + { 0x00426, W("lv-LV") }, + { 0x00427, W("lt-LT") }, + { 0x00428, W("tg-Cyrl-TJ") }, + { 0x00429, W("fa-IR") }, + { 0x0042a, W("vi-VN") }, + { 0x0042b, W("hy-AM") }, + { 0x0042c, W("az-Latn-AZ") }, + { 0x0042d, W("eu-ES") }, + { 0x0042e, W("hsb-DE") }, + { 0x0042f, W("mk-MK") }, + { 0x00432, W("tn-ZA") }, + { 0x00434, W("xh-ZA") }, + { 0x00435, W("zu-ZA") }, + { 0x00436, W("af-ZA") }, + { 0x00437, W("ka-GE") }, + { 0x00438, W("fo-FO") }, + { 0x00439, W("hi-IN") }, + { 0x0043a, W("mt-MT") }, + { 0x0043b, W("se-NO") }, + { 0x0043e, W("ms-MY") }, + { 0x0043f, W("kk-KZ") }, + { 0x00440, W("ky-KG") }, + { 0x00441, W("sw-KE") }, + { 0x00442, W("tk-TM") }, + { 0x00443, W("uz-Latn-UZ") }, + { 0x00444, W("tt-RU") }, + { 0x00445, W("bn-IN") }, + { 0x00446, W("pa-IN") }, + { 0x00447, W("gu-IN") }, + { 0x00448, W("or-IN") }, + { 0x00449, W("ta-IN") }, + { 0x0044a, W("te-IN") }, + { 0x0044b, W("kn-IN") }, + { 0x0044c, W("ml-IN") }, + { 0x0044d, W("as-IN") }, + { 0x0044e, W("mr-IN") }, + { 0x0044f, W("sa-IN") }, + { 0x00450, W("mn-MN") }, + { 0x00451, W("bo-CN") }, + { 0x00452, W("cy-GB") }, + { 0x00453, W("km-KH") }, + { 0x00454, W("lo-LA") }, + { 0x00456, W("gl-ES") }, + { 0x00457, W("kok-IN") }, + { 0x0045a, W("syr-SY") }, + { 0x0045b, W("si-LK") }, + { 0x0045d, W("iu-Cans-CA") }, + { 0x0045e, W("am-ET") }, + { 0x00461, W("ne-NP") }, + { 0x00462, W("fy-NL") }, + { 0x00463, W("ps-AF") }, + { 0x00464, W("fil-PH") }, + { 0x00465, W("dv-MV") }, + { 0x00468, W("ha-Latn-NG") }, + { 0x0046a, W("yo-NG") }, + { 0x0046b, W("quz-BO") }, + { 0x0046c, W("nso-ZA") }, + { 0x0046d, W("ba-RU") }, + { 0x0046e, W("lb-LU") }, + { 0x0046f, W("kl-GL") }, + { 0x00470, W("ig-NG") }, + { 0x00478, W("ii-CN") }, + { 0x0047a, W("arn-CL") }, + { 0x0047c, W("moh-CA") }, + { 0x0047e, W("br-FR") }, + { 0x00480, W("ug-CN") }, + { 0x00481, W("mi-NZ") }, + { 0x00482, W("oc-FR") }, + { 0x00483, W("co-FR") }, + { 0x00484, W("gsw-FR") }, + { 0x00485, W("sah-RU") }, + { 0x00486, W("qut-GT") }, + { 0x00487, W("rw-RW") }, + { 0x00488, W("wo-SN") }, + { 0x0048c, W("prs-AF") }, + { 0x00501, W("qps-ploc") }, + { 0x005fe, W("qps-ploca") }, + { 0x00801, W("ar-IQ") }, + { 0x00804, W("zh-CN") }, + { 0x00807, W("de-CH") }, + { 0x00809, W("en-GB") }, + { 0x0080a, W("es-MX") }, + { 0x0080c, W("fr-BE") }, + { 0x00810, W("it-CH") }, + { 0x00813, W("nl-BE") }, + { 0x00814, W("nn-NO") }, + { 0x00816, W("pt-PT") }, + { 0x0081a, W("sr-Latn-CS") }, + { 0x0081d, W("sv-FI") }, + { 0x0082c, W("az-Cyrl-AZ") }, + { 0x0082e, W("dsb-DE") }, + { 0x0083b, W("se-SE") }, + { 0x0083c, W("ga-IE") }, + { 0x0083e, W("ms-BN") }, + { 0x00843, W("uz-Cyrl-UZ") }, + { 0x00845, W("bn-BD") }, + { 0x00850, W("mn-Mong-CN") }, + { 0x0085d, W("iu-Latn-CA") }, + { 0x0085f, W("tzm-Latn-DZ") }, + { 0x0086b, W("quz-EC") }, + { 0x009ff, W("qps-plocm") }, + { 0x00c01, W("ar-EG") }, + { 0x00c04, W("zh-HK") }, + { 0x00c07, W("de-AT") }, + { 0x00c09, W("en-AU") }, + { 0x00c0a, W("es-ES") }, + { 0x00c0c, W("fr-CA") }, + { 0x00c1a, W("sr-Cyrl-CS") }, + { 0x00c3b, W("se-FI") }, + { 0x00c6b, W("quz-PE") }, + { 0x01001, W("ar-LY") }, + { 0x01004, W("zh-SG") }, + { 0x01007, W("de-LU") }, + { 0x01009, W("en-CA") }, + { 0x0100a, W("es-GT") }, + { 0x0100c, W("fr-CH") }, + { 0x0101a, W("hr-BA") }, + { 0x0103b, W("smj-NO") }, + { 0x01401, W("ar-DZ") }, + { 0x01404, W("zh-MO") }, + { 0x01407, W("de-LI") }, + { 0x01409, W("en-NZ") }, + { 0x0140a, W("es-CR") }, + { 0x0140c, W("fr-LU") }, + { 0x0141a, W("bs-Latn-BA") }, + { 0x0143b, W("smj-SE") }, + { 0x01801, W("ar-MA") }, + { 0x01809, W("en-IE") }, + { 0x0180a, W("es-PA") }, + { 0x0180c, W("fr-MC") }, + { 0x0181a, W("sr-Latn-BA") }, + { 0x0183b, W("sma-NO") }, + { 0x01c01, W("ar-TN") }, + { 0x01c09, W("en-ZA") }, + { 0x01c0a, W("es-DO") }, + { 0x01c1a, W("sr-Cyrl-BA") }, + { 0x01c3b, W("sma-SE") }, + { 0x02001, W("ar-OM") }, + { 0x02009, W("en-JM") }, + { 0x0200a, W("es-VE") }, + { 0x0201a, W("bs-Cyrl-BA") }, + { 0x0203b, W("sms-FI") }, + { 0x02401, W("ar-YE") }, + { 0x02409, W("en-029") }, + { 0x0240a, W("es-CO") }, + { 0x0243b, W("smn-FI") }, + { 0x02801, W("ar-SY") }, + { 0x02809, W("en-BZ") }, + { 0x0280a, W("es-PE") }, + { 0x02c01, W("ar-JO") }, + { 0x02c09, W("en-TT") }, + { 0x02c0a, W("es-AR") }, + { 0x03001, W("ar-LB") }, + { 0x03009, W("en-ZW") }, + { 0x0300a, W("es-EC") }, + { 0x03401, W("ar-KW") }, + { 0x03409, W("en-PH") }, + { 0x0340a, W("es-CL") }, + { 0x03801, W("ar-AE") }, + { 0x0380a, W("es-UY") }, + { 0x03c01, W("ar-BH") }, + { 0x03c0a, W("es-PY") }, + { 0x04001, W("ar-QA") }, + { 0x04009, W("en-IN") }, + { 0x0400a, W("es-BO") }, + { 0x04409, W("en-MY") }, + { 0x0440a, W("es-SV") }, + { 0x04809, W("en-SG") }, + { 0x0480a, W("es-HN") }, + { 0x04c0a, W("es-NI") }, + { 0x0500a, W("es-PR") }, + { 0x0540a, W("es-US") }, + + // Multiple neutrals + { 0x0781a, W("bs") }, // Neutral Locale + { 0x07c04, W("zh-Hant") }, // Neutral Locale + { 0x07c1a, W("sr") }, // Neutral Locale + + // Alt Sorts + { 0x1007f, W("x-IV_mathan") }, + { 0x10407, W("de-DE_phoneb") }, + { 0x1040e, W("hu-HU_technl") }, + { 0x10437, W("ka-GE_modern") }, + { 0x20804, W("zh-CN_stroke") }, + { 0x21004, W("zh-SG_stroke") }, + { 0x21404, W("zh-MO_stroke") }, + { 0x30404, W("zh-TW_pronun") }, + { 0x40411, W("ja-JP_radstr") } + + // TODO: Turkic? (Necessary ?) +}; + +// Known (ie: <= Vista) Name/LCID lookup table, sorted by Name +const static LCIDEntry s_names[]= +{ + { 0x00036, W("af") }, // Neutral Locale + { 0x00436, W("af-ZA") }, + { 0x0005e, W("am") }, // Neutral Locale + { 0x0045e, W("am-ET") }, + { 0x00001, W("ar") }, // Neutral Locale + { 0x03801, W("ar-AE") }, + { 0x03c01, W("ar-BH") }, + { 0x01401, W("ar-DZ") }, + { 0x00c01, W("ar-EG") }, + { 0x00801, W("ar-IQ") }, + { 0x02c01, W("ar-JO") }, + { 0x03401, W("ar-KW") }, + { 0x03001, W("ar-LB") }, + { 0x01001, W("ar-LY") }, + { 0x01801, W("ar-MA") }, + { 0x02001, W("ar-OM") }, + { 0x04001, W("ar-QA") }, + { 0x00401, W("ar-SA") }, + { 0x02801, W("ar-SY") }, + { 0x01c01, W("ar-TN") }, + { 0x02401, W("ar-YE") }, + { 0x0007a, W("arn") }, // Neutral Locale + { 0x0047a, W("arn-CL") }, + { 0x0004d, W("as") }, // Neutral Locale + { 0x0044d, W("as-IN") }, + { 0x0002c, W("az") }, // Neutral Locale + { 0x0082c, W("az-Cyrl-AZ") }, + { 0x0042c, W("az-Latn-AZ") }, + { 0x0006d, W("ba") }, // Neutral Locale + { 0x0046d, W("ba-RU") }, + { 0x00023, W("be") }, // Neutral Locale + { 0x00423, W("be-BY") }, + { 0x00002, W("bg") }, // Neutral Locale + { 0x00402, W("bg-BG") }, + { 0x00045, W("bn") }, // Neutral Locale + { 0x00845, W("bn-BD") }, + { 0x00445, W("bn-IN") }, + { 0x00051, W("bo") }, // Neutral Locale + { 0x00451, W("bo-CN") }, + { 0x0007e, W("br") }, // Neutral Locale + { 0x0047e, W("br-FR") }, + { 0x0781a, W("bs") }, // Neutral Locale + { 0x0201a, W("bs-Cyrl-BA") }, + { 0x0141a, W("bs-Latn-BA") }, + { 0x00003, W("ca") }, // Neutral Locale + { 0x00403, W("ca-ES") }, + { 0x00083, W("co") }, // Neutral Locale + { 0x00483, W("co-FR") }, + { 0x00005, W("cs") }, // Neutral Locale + { 0x00405, W("cs-CZ") }, + { 0x00052, W("cy") }, // Neutral Locale + { 0x00452, W("cy-GB") }, + { 0x00006, W("da") }, // Neutral Locale + { 0x00406, W("da-DK") }, + { 0x00007, W("de") }, // Neutral Locale + { 0x00c07, W("de-AT") }, + { 0x00807, W("de-CH") }, + { 0x00407, W("de-DE") }, + { 0x10407, W("de-DE_phoneb") }, // Alternate Sort + { 0x01407, W("de-LI") }, + { 0x01007, W("de-LU") }, + { 0x0082e, W("dsb-DE") }, + { 0x00065, W("dv") }, // Neutral Locale + { 0x00465, W("dv-MV") }, + { 0x00008, W("el") }, // Neutral Locale + { 0x00408, W("el-GR") }, + { 0x00009, W("en") }, // Neutral Locale + { 0x02409, W("en-029") }, + { 0x00c09, W("en-AU") }, + { 0x02809, W("en-BZ") }, + { 0x01009, W("en-CA") }, + { 0x00809, W("en-GB") }, + { 0x01809, W("en-IE") }, + { 0x04009, W("en-IN") }, + { 0x02009, W("en-JM") }, + { 0x04409, W("en-MY") }, + { 0x01409, W("en-NZ") }, + { 0x03409, W("en-PH") }, + { 0x04809, W("en-SG") }, + { 0x02c09, W("en-TT") }, + { 0x00409, W("en-US") }, + { 0x01c09, W("en-ZA") }, + { 0x03009, W("en-ZW") }, + { 0x0000a, W("es") }, // Neutral Locale + { 0x02c0a, W("es-AR") }, + { 0x0400a, W("es-BO") }, + { 0x0340a, W("es-CL") }, + { 0x0240a, W("es-CO") }, + { 0x0140a, W("es-CR") }, + { 0x01c0a, W("es-DO") }, + { 0x0300a, W("es-EC") }, + { 0x00c0a, W("es-ES") }, + // es-ES_tradnl only gets used if specifically asked for because + // GetCultures() won't return it. (It's not a real locale, its an alt sort) + { 0x0040a, W("es-ES_tradnl") }, + { 0x0100a, W("es-GT") }, + { 0x0480a, W("es-HN") }, + { 0x0080a, W("es-MX") }, + { 0x04c0a, W("es-NI") }, + { 0x0180a, W("es-PA") }, + { 0x0280a, W("es-PE") }, + { 0x0500a, W("es-PR") }, + { 0x03c0a, W("es-PY") }, + { 0x0440a, W("es-SV") }, + { 0x0540a, W("es-US") }, + { 0x0380a, W("es-UY") }, + { 0x0200a, W("es-VE") }, + { 0x00025, W("et") }, // Neutral Locale + { 0x00425, W("et-EE") }, + { 0x0002d, W("eu") }, // Neutral Locale + { 0x0042d, W("eu-ES") }, + { 0x00029, W("fa") }, // Neutral Locale + { 0x00429, W("fa-IR") }, + { 0x0000b, W("fi") }, // Neutral Locale + { 0x0040b, W("fi-FI") }, + { 0x00064, W("fil") }, // Neutral Locale + { 0x00464, W("fil-PH") }, + { 0x00038, W("fo") }, // Neutral Locale + { 0x00438, W("fo-FO") }, + { 0x0000c, W("fr") }, // Neutral Locale + { 0x0080c, W("fr-BE") }, + { 0x00c0c, W("fr-CA") }, + { 0x0100c, W("fr-CH") }, + { 0x0040c, W("fr-FR") }, + { 0x0140c, W("fr-LU") }, + { 0x0180c, W("fr-MC") }, + { 0x00062, W("fy") }, // Neutral Locale + { 0x00462, W("fy-NL") }, + { 0x0003c, W("ga") }, // Neutral Locale + { 0x0083c, W("ga-IE") }, + { 0x00056, W("gl") }, // Neutral Locale + { 0x00456, W("gl-ES") }, + { 0x00084, W("gsw") }, // Neutral Locale + { 0x00484, W("gsw-FR") }, + { 0x00047, W("gu") }, // Neutral Locale + { 0x00447, W("gu-IN") }, + { 0x00068, W("ha") }, // Neutral Locale + { 0x00468, W("ha-Latn-NG") }, + { 0x0000d, W("he") }, // Neutral Locale + { 0x0040d, W("he-IL") }, + { 0x00039, W("hi") }, // Neutral Locale + { 0x00439, W("hi-IN") }, + { 0x0001a, W("hr") }, // Neutral Locale + { 0x0101a, W("hr-BA") }, + { 0x0041a, W("hr-HR") }, + { 0x0002e, W("hsb") }, // Neutral Locale + { 0x0042e, W("hsb-DE") }, + { 0x0000e, W("hu") }, // Neutral Locale + { 0x0040e, W("hu-HU") }, + { 0x1040e, W("hu-HU_technl") }, // Alternate Sort + { 0x0002b, W("hy") }, // Neutral Locale + { 0x0042b, W("hy-AM") }, + { 0x00021, W("id") }, // Neutral Locale + { 0x00421, W("id-ID") }, + { 0x00070, W("ig") }, // Neutral Locale + { 0x00470, W("ig-NG") }, + { 0x00078, W("ii") }, // Neutral Locale + { 0x00478, W("ii-CN") }, + { 0x0000f, W("is") }, // Neutral Locale + { 0x0040f, W("is-IS") }, + { 0x00010, W("it") }, // Neutral Locale + { 0x00810, W("it-CH") }, + { 0x00410, W("it-IT") }, + { 0x0005d, W("iu") }, // Neutral Locale + { 0x0045d, W("iu-Cans-CA") }, + { 0x0085d, W("iu-Latn-CA") }, + { 0x00011, W("ja") }, // Neutral Locale + { 0x00411, W("ja-JP") }, + { 0x40411, W("ja-JP_radstr") }, // Alternate Sort + { 0x00037, W("ka") }, // Neutral Locale + { 0x00437, W("ka-GE") }, + { 0x10437, W("ka-GE_modern") }, // Alternate Sort + { 0x0003f, W("kk") }, // Neutral Locale + { 0x0043f, W("kk-KZ") }, + { 0x0006f, W("kl") }, // Neutral Locale + { 0x0046f, W("kl-GL") }, + { 0x00053, W("km") }, // Neutral Locale + { 0x00453, W("km-KH") }, + { 0x0004b, W("kn") }, // Neutral Locale + { 0x0044b, W("kn-IN") }, + { 0x00012, W("ko") }, // Neutral Locale + { 0x00412, W("ko-KR") }, + { 0x00057, W("kok") }, // Neutral Locale + { 0x00457, W("kok-IN") }, + { 0x00040, W("ky") }, // Neutral Locale + { 0x00440, W("ky-KG") }, + { 0x0006e, W("lb") }, // Neutral Locale + { 0x0046e, W("lb-LU") }, + { 0x00054, W("lo") }, // Neutral Locale + { 0x00454, W("lo-LA") }, + { 0x00027, W("lt") }, // Neutral Locale + { 0x00427, W("lt-LT") }, + { 0x00026, W("lv") }, // Neutral Locale + { 0x00426, W("lv-LV") }, + { 0x00081, W("mi") }, // Neutral Locale + { 0x00481, W("mi-NZ") }, + { 0x0002f, W("mk") }, // Neutral Locale + { 0x0042f, W("mk-MK") }, + { 0x0004c, W("ml") }, // Neutral Locale + { 0x0044c, W("ml-IN") }, + { 0x00050, W("mn") }, // Neutral Locale + { 0x00450, W("mn-MN") }, + { 0x00850, W("mn-Mong-CN") }, + { 0x0007c, W("moh") }, // Neutral Locale + { 0x0047c, W("moh-CA") }, + { 0x0004e, W("mr") }, // Neutral Locale + { 0x0044e, W("mr-IN") }, + { 0x0003e, W("ms") }, // Neutral Locale + { 0x0083e, W("ms-BN") }, + { 0x0043e, W("ms-MY") }, + { 0x0003a, W("mt") }, // Neutral Locale + { 0x0043a, W("mt-MT") }, + { 0x00414, W("nb-NO") }, + { 0x00061, W("ne") }, // Neutral Locale + { 0x00461, W("ne-NP") }, + { 0x00013, W("nl") }, // Neutral Locale + { 0x00813, W("nl-BE") }, + { 0x00413, W("nl-NL") }, + { 0x00814, W("nn-NO") }, + { 0x00014, W("no") }, // Neutral Locale + { 0x0006c, W("nso") }, // Neutral Locale + { 0x0046c, W("nso-ZA") }, + { 0x00082, W("oc") }, // Neutral Locale + { 0x00482, W("oc-FR") }, + { 0x00048, W("or") }, // Neutral Locale + { 0x00448, W("or-IN") }, + { 0x00046, W("pa") }, // Neutral Locale + { 0x00446, W("pa-IN") }, + { 0x00015, W("pl") }, // Neutral Locale + { 0x00415, W("pl-PL") }, + { 0x0008c, W("prs") }, // Neutral Locale + { 0x0048c, W("prs-AF") }, + { 0x00063, W("ps") }, // Neutral Locale + { 0x00463, W("ps-AF") }, + { 0x00016, W("pt") }, // Neutral Locale + { 0x00416, W("pt-BR") }, + { 0x00816, W("pt-PT") }, + { 0x00501, W("qps-ploc") }, + { 0x005fe, W("qps-ploca") }, + { 0x009ff, W("qps-plocm") }, + { 0x00086, W("qut") }, // Neutral Locale + { 0x00486, W("qut-GT") }, + { 0x0006b, W("quz") }, // Neutral Locale + { 0x0046b, W("quz-BO") }, + { 0x0086b, W("quz-EC") }, + { 0x00c6b, W("quz-PE") }, + { 0x00017, W("rm") }, // Neutral Locale + { 0x00417, W("rm-CH") }, + { 0x00018, W("ro") }, // Neutral Locale + { 0x00418, W("ro-RO") }, + { 0x00019, W("ru") }, // Neutral Locale + { 0x00419, W("ru-RU") }, + { 0x00087, W("rw") }, // Neutral Locale + { 0x00487, W("rw-RW") }, + { 0x0004f, W("sa") }, // Neutral Locale + { 0x0044f, W("sa-IN") }, + { 0x00085, W("sah") }, // Neutral Locale + { 0x00485, W("sah-RU") }, + { 0x0003b, W("se") }, // Neutral Locale + { 0x00c3b, W("se-FI") }, + { 0x0043b, W("se-NO") }, + { 0x0083b, W("se-SE") }, + { 0x0005b, W("si") }, // Neutral Locale + { 0x0045b, W("si-LK") }, + { 0x0001b, W("sk") }, // Neutral Locale + { 0x0041b, W("sk-SK") }, + { 0x00024, W("sl") }, // Neutral Locale + { 0x00424, W("sl-SI") }, + { 0x0183b, W("sma-NO") }, + { 0x01c3b, W("sma-SE") }, + { 0x0103b, W("smj-NO") }, + { 0x0143b, W("smj-SE") }, + { 0x0243b, W("smn-FI") }, + { 0x0203b, W("sms-FI") }, + { 0x0001c, W("sq") }, // Neutral Locale + { 0x0041c, W("sq-AL") }, + { 0x07c1a, W("sr") }, // Neutral Locale + { 0x01c1a, W("sr-Cyrl-BA") }, + { 0x00c1a, W("sr-Cyrl-CS") }, + { 0x0181a, W("sr-Latn-BA") }, + { 0x0081a, W("sr-Latn-CS") }, + { 0x0001d, W("sv") }, // Neutral Locale + { 0x0081d, W("sv-FI") }, + { 0x0041d, W("sv-SE") }, + { 0x00041, W("sw") }, // Neutral Locale + { 0x00441, W("sw-KE") }, + { 0x0005a, W("syr") }, // Neutral Locale + { 0x0045a, W("syr-SY") }, + { 0x00049, W("ta") }, // Neutral Locale + { 0x00449, W("ta-IN") }, + { 0x0004a, W("te") }, // Neutral Locale + { 0x0044a, W("te-IN") }, + { 0x00028, W("tg") }, // Neutral Locale + { 0x00428, W("tg-Cyrl-TJ") }, + { 0x0001e, W("th") }, // Neutral Locale + { 0x0041e, W("th-TH") }, + { 0x00042, W("tk") }, // Neutral Locale + { 0x00442, W("tk-TM") }, + { 0x00032, W("tn") }, // Neutral Locale + { 0x00432, W("tn-ZA") }, + { 0x0001f, W("tr") }, // Neutral Locale + { 0x0041f, W("tr-TR") }, + { 0x00044, W("tt") }, // Neutral Locale + { 0x00444, W("tt-RU") }, + { 0x0005f, W("tzm") }, // Neutral Locale + { 0x0085f, W("tzm-Latn-DZ") }, + { 0x00080, W("ug") }, // Neutral Locale + { 0x00480, W("ug-CN") }, + { 0x00022, W("uk") }, // Neutral Locale + { 0x00422, W("uk-UA") }, + { 0x00020, W("ur") }, // Neutral Locale + { 0x00420, W("ur-PK") }, + { 0x00043, W("uz") }, // Neutral Locale + { 0x00843, W("uz-Cyrl-UZ") }, + { 0x00443, W("uz-Latn-UZ") }, + { 0x0002a, W("vi") }, // Neutral Locale + { 0x0042a, W("vi-VN") }, + { 0x00088, W("wo") }, // Neutral Locale + { 0x00488, W("wo-SN") }, + { 0x1007f, W("x-IV_mathan") }, // Alternate Sort + { 0x00034, W("xh") }, // Neutral Locale + { 0x00434, W("xh-ZA") }, + { 0x0006a, W("yo") }, // Neutral Locale + { 0x0046a, W("yo-NG") }, + { 0x00804, W("zh-CN") }, + { 0x20804, W("zh-CN_stroke") }, // Alternate Sort + { 0x00004, W("zh-Hans") }, // Neutral Locale + { 0x07c04, W("zh-Hant") }, // Neutral Locale + { 0x00c04, W("zh-HK") }, + { 0x01404, W("zh-MO") }, + { 0x21404, W("zh-MO_stroke") }, // Alternate Sort + { 0x01004, W("zh-SG") }, + { 0x21004, W("zh-SG_stroke") }, // Alternate Sort + { 0x00404, W("zh-TW") }, + { 0x30404, W("zh-TW_pronun") }, // Alternate Sort + { 0x00035, W("zu") }, // Neutral Locale + { 0x00435, W("zu-ZA") } +}; + +// This is the data from l_intl.nls as released for XP for the uppercase table. +// This is used for casing with OrdinalCompareStringIgnoreCase +// since XP is the only scenario that needs data and the only data that it needs +// is uppercasing, we duplicate the table here. +const static WORD s_pUppercaseIndexTableXP[] = { +0x0110, 0x0120, 0x0130, 0x0140, 0x0150, 0x0160, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0170, 0x0180, 0x0100, 0x0190, 0x0100, 0x0100, 0x01a0, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x01b0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, +0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, +0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01d0, 0x01e0, +0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01f0, 0x0200, 0x0210, 0x0220, +0x0230, 0x0240, 0x0250, 0x0260, 0x0270, 0x0280, 0x0290, 0x02a0, 0x02b0, 0x02c0, +0x02d0, 0x02e0, 0x02f0, 0x0300, 0x0310, 0x0320, 0x01c0, 0x01c0, 0x01c0, 0x0330, +0x0340, 0x0350, 0x0360, 0x0370, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, +0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x0380, +0x0390, 0x03a0, 0x03b0, 0x03c0, 0x03d0, 0x03e0, 0x01c0, 0x01c0, 0x01c0, 0x03f0, +0x0400, 0x0410, 0x0420, 0x0430, 0x0440, 0x0450, 0x0460, 0x0470, 0x0480, 0x0490, +0x04a0, 0x04b0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x04c0, 0x04d0, +0x04e0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x04f0, 0x0500, +0x0510, 0x0520, 0x0530, 0x0540, 0x0550, 0x0560, 0x0570, 0x0580, 0x0590, 0x05a0, +0x05b0, 0x05c0, 0x05d0, 0x05e0, 0x05f0, 0x0600, 0x0610, 0x0620, 0x0630, 0x0640, +0x0650, 0x0660, 0x01c0, 0x01c0, 0x01c0, 0x0670, 0x01c0, 0x0680, 0x0690, 0x01c0, +0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x06a0, 0x01c0, 0x01c0, +0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, +0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x06b0, +0x06c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x06d0, 0x06e0, 0x01c0, 0x01c0, +0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0x0000, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0x0079, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, +0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, +0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, +0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, +0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, +0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfffe, 0x0000, 0x0000, 0xfffe, +0x0000, 0x0000, 0xfffe, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, +0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0xffb1, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, +0x0000, 0xfffe, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff2e, +0xff32, 0x0000, 0xff33, 0xff33, 0x0000, 0xff36, 0x0000, 0xff35, 0x0000, 0x0000, +0x0000, 0x0000, 0xff33, 0x0000, 0x0000, 0xff31, 0x0000, 0x0000, 0x0000, 0x0000, +0xff2f, 0xff2d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff2d, 0x0000, 0x0000, +0xff2b, 0x0000, 0x0000, 0xff2a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff26, 0x0000, 0x0000, +0x0000, 0x0000, 0xff26, 0x0000, 0xff27, 0xff27, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0xff25, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0xffda, 0xffdb, 0xffdb, 0xffdb, 0x0000, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe1, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffc0, 0xffc1, 0xffc1, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0x0000, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, +0xffb0, 0xffb0, 0xffb0, 0x0000, 0xffb0, 0xffb0, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, +0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffd0, 0xffd0, 0xffd0, +0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, +0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, +0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, +0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, +0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, +0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0008, 0x0000, 0x0008, 0x0000, 0x0008, 0x0000, 0x0008, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, +0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x004a, 0x004a, 0x0056, 0x0056, 0x0056, 0x0056, 0x0064, 0x0064, +0x0080, 0x0080, 0x0070, 0x0070, 0x007e, 0x007e, 0x0000, 0x0000, 0x0008, 0x0008, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0007, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff0, 0xfff0, 0xfff0, 0xfff0, +0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, +0xfff0, 0xfff0, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, +0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, +0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; + +// This is the data from casing.nls as released for Win2K3/XP for the lowercase table. +// This is used for casing when linguistic casing is needed +// since Win2K3/XP is the only scenario that needs data, we duplicate the table here. +// It does NOT contain Turkic I exception behavior +const static WORD s_pLowercaseIndexTableLinguisticXP[] = { +0x0110, 0x0120, 0x0130, 0x0140, 0x0150, 0x0160, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0170, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0180, 0x0190, 0x0100, 0x01a0, 0x0100, 0x0100, 0x01b0, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x01c0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, +0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, +0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01e0, 0x01f0, 0x01d0, 0x01d0, +0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x0200, 0x0210, 0x01d0, 0x01d0, 0x0220, 0x0230, +0x0240, 0x0250, 0x0260, 0x0270, 0x0280, 0x0290, 0x02a0, 0x02b0, 0x02c0, 0x02d0, +0x02e0, 0x02f0, 0x0300, 0x0310, 0x0320, 0x0330, 0x01d0, 0x01d0, 0x01d0, 0x01d0, +0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, +0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x0340, 0x0350, +0x0360, 0x01d0, 0x01d0, 0x0370, 0x0380, 0x01d0, 0x0390, 0x03a0, 0x03b0, 0x01d0, +0x01d0, 0x01d0, 0x03c0, 0x03d0, 0x03e0, 0x03f0, 0x0400, 0x0410, 0x0420, 0x0430, +0x0440, 0x0450, 0x01d0, 0x01d0, 0x01d0, 0x0460, 0x0470, 0x0480, 0x01d0, 0x01d0, +0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, +0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x0490, 0x04a0, +0x04b0, 0x01d0, 0x01d0, 0x01d0, 0x04c0, 0x04d0, 0x04e0, 0x04f0, 0x0500, 0x0510, +0x0520, 0x0530, 0x0540, 0x0550, 0x0560, 0x0570, 0x0580, 0x0590, 0x05a0, 0x05b0, +0x05c0, 0x05d0, 0x05e0, 0x05f0, 0x0600, 0x0610, 0x0620, 0x01d0, 0x01d0, 0x01d0, +0x01d0, 0x0630, 0x0640, 0x0650, 0x0660, 0x0670, 0x01d0, 0x01d0, 0x01d0, 0x01d0, +0x01d0, 0x01d0, 0x0680, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, +0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, +0x01d0, 0x01d0, 0x01d0, 0x0690, 0x06a0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, +0x06b0, 0x06c0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, +0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, +0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, +0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, +0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, +0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0000, 0x0020, 0x0020, 0x0020, 0x0020, +0x0020, 0x0020, 0x0020, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0xff39, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, +0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0xff87, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, +0x0000, 0x0000, 0x0000, 0x00d2, 0x0001, 0x0000, 0x0001, 0x0000, 0x00ce, 0x0001, +0x0000, 0x00cd, 0x00cd, 0x0001, 0x0000, 0x0000, 0x004f, 0x00ca, 0x00cb, 0x0001, +0x0000, 0x00cd, 0x00cf, 0x0000, 0x00d3, 0x00d1, 0x0001, 0x0000, 0x0000, 0x0000, +0x00d3, 0x00d5, 0x0000, 0x00d6, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0000, 0x0001, 0x0000, 0x00da, 0x0000, 0x0000, 0x0001, 0x0000, 0x00da, 0x0001, +0x0000, 0x00d9, 0x00d9, 0x0001, 0x0000, 0x0001, 0x0000, 0x00db, 0x0001, 0x0000, +0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0002, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, +0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, +0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0026, 0x0000, +0x0025, 0x0025, 0x0025, 0x0000, 0x0040, 0x0000, 0x003f, 0x003f, 0x0000, 0x0020, +0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, +0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0000, 0x0020, 0x0020, 0x0020, +0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0xfff3, 0xfffa, 0xfff7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0000, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, +0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0000, 0x0050, 0x0050, 0x0020, 0x0020, +0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, +0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, +0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, +0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, +0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, +0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, +0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, +0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0030, 0x0030, +0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, +0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, +0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, +0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, +0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8, +0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, +0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0xfff8, 0x0000, 0xfff8, 0x0000, 0xfff8, 0x0000, 0xfff8, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8, +0xfff8, 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0xfff8, 0xfff8, 0xffb6, 0xffb6, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffaa, 0xffaa, +0xffaa, 0xffaa, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xff9c, 0xff9c, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0xfff8, 0xfff8, 0xff90, 0xff90, 0xfff9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff80, 0xff80, 0xff82, 0xff82, +0x0000, 0x0000, 0x0000, 0x0000, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, +0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x001a, 0x001a, 0x001a, 0x001a, +0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, +0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, +0x001a, 0x001a, 0x0000, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, +0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, +0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000 +}; + +// This is the data from casing.nls as released for Win2K3/XP for the uppercase table. +// This is used for casing when linguistic casing is needed +// since Win2K3/XP is the only scenario that needs data, we duplicate the table here. +// It does NOT contain Turkic I exception behavior +const static WORD s_pUppercaseIndexTableLinguisticXP[] = { +0x0110, 0x0120, 0x0130, 0x0140, 0x0150, 0x0160, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0170, 0x0180, 0x0100, 0x0190, 0x0100, 0x0100, 0x01a0, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, +0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x01b0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, +0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, +0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01d0, 0x01e0, +0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01f0, 0x0200, 0x0210, 0x0220, +0x0230, 0x0240, 0x0250, 0x0260, 0x0270, 0x0280, 0x0290, 0x02a0, 0x02b0, 0x02c0, +0x02d0, 0x02e0, 0x02f0, 0x0300, 0x0310, 0x0320, 0x01c0, 0x01c0, 0x01c0, 0x0330, +0x0340, 0x0350, 0x0360, 0x0370, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, +0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x0380, +0x0390, 0x03a0, 0x03b0, 0x03c0, 0x03d0, 0x03e0, 0x01c0, 0x01c0, 0x01c0, 0x03f0, +0x0400, 0x0410, 0x0420, 0x0430, 0x0440, 0x0450, 0x0460, 0x0470, 0x0480, 0x0490, +0x04a0, 0x04b0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x04c0, 0x04d0, +0x04e0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x04f0, 0x0500, +0x0510, 0x0520, 0x0530, 0x0540, 0x0550, 0x0560, 0x0570, 0x0580, 0x0590, 0x05a0, +0x05b0, 0x05c0, 0x05d0, 0x05e0, 0x05f0, 0x0600, 0x0610, 0x0620, 0x0630, 0x0640, +0x0650, 0x0660, 0x01c0, 0x01c0, 0x01c0, 0x0670, 0x01c0, 0x0680, 0x0690, 0x01c0, +0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x06a0, 0x01c0, 0x01c0, +0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, +0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x06b0, +0x06c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x06d0, 0x06e0, 0x01c0, 0x01c0, +0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0x0000, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0x0079, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xff18, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, +0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, +0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, +0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, +0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, +0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0xfffe, 0x0000, 0xffff, 0xfffe, +0x0000, 0xffff, 0xfffe, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, +0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0xffb1, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, +0xffff, 0xfffe, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff2e, +0xff32, 0x0000, 0xff33, 0xff33, 0x0000, 0xff36, 0x0000, 0xff35, 0x0000, 0x0000, +0x0000, 0x0000, 0xff33, 0x0000, 0x0000, 0xff31, 0x0000, 0x0000, 0x0000, 0x0000, +0xff2f, 0xff2d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff2d, 0x0000, 0x0000, +0xff2b, 0x0000, 0x0000, 0xff2a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff26, 0x0000, 0x0000, +0x0000, 0x0000, 0xff26, 0x0000, 0xff27, 0xff27, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0xff25, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x001a, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0xffda, 0xffdb, 0xffdb, 0xffdb, 0xfffb, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe1, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffc0, 0xffc1, 0xffc1, 0x0000, +0xffc2, 0xffc7, 0x0000, 0x0000, 0x0000, 0xffd1, 0xffca, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0xffaa, 0xffb0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0x0000, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, +0xffb0, 0xffb0, 0xffb0, 0x0000, 0xffb0, 0xffb0, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, +0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffd0, 0xffd0, 0xffd0, +0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, +0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, +0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, +0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, +0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, +0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, +0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0008, 0x0000, 0x0008, 0x0000, 0x0008, 0x0000, 0x0008, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, +0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x004a, 0x004a, 0x0056, 0x0056, 0x0056, 0x0056, 0x0064, 0x0064, +0x0080, 0x0080, 0x0070, 0x0070, 0x007e, 0x007e, 0x0000, 0x0000, 0x0008, 0x0008, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0007, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff0, 0xfff0, 0xfff0, 0xfff0, +0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, +0xfff0, 0xfff0, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, +0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, +0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, +0xffe0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; + +static CRITSEC_COOKIE g_pDownlevelNlsCrst = NULL; + +static void DelayCreateCriticalSection() +{ + // Lazily allocate a Crst to serialize update access to the info structure. + // Carefully synchronize to ensure we don't leak a Crst in race conditions. + if (g_pDownlevelNlsCrst == NULL) + { + CRITSEC_COOKIE pCrst = ClrCreateCriticalSection(CrstNls, + (CrstFlags)(CRST_UNSAFE_COOPGC)); + + if (InterlockedCompareExchangeT(&g_pDownlevelNlsCrst, pCrst, NULL) != NULL) + { + ClrDeleteCriticalSection(pCrst); + } + } +} + +static void NormalizeCultureName(SString& sName) +{ + for (SString::Iterator i = sName.Begin(); i[0] != W('\0'); ++i) + { + sName.Replace(i,towlower(i[0])); + } +} + +static inline BOOL IsTurkicILcid(LCID lcid) +{ + return (PRIMARYLANGID(lcid) == LANG_TURKISH + || (PRIMARYLANGID(lcid) == LANG_AZERI + && lcid != LCID_AZ_CYRL_AZ)); +} + +namespace DownLevel +{ + + int GetSystemDefaultLocaleName(__out_ecount(cchLocaleName) LPWSTR lpLocaleName, __in int cchLocaleName) + { + LCID lcid=GetSystemDefaultLCID(); + if (lcid == 0) + return 0; + return NewApis::LCIDToLocaleName(lcid,lpLocaleName,cchLocaleName,0); + }; + + // Xp returns lcid 0x0404 (zh-TW) for GetUserDefaultUILanguage and GetSystemDefaultUILanguage + // on a CHH sku (expected 0x0c04) as well as a CHT sku (as expected) + inline LCID WorkAroundXpTaiwanHongKongBug(LCID lcid) + { + const LCID LANGID_ZH_TW = 0x0404; + const LCID LANGID_ZH_HK = 0x0c04; + + if(lcid == LANGID_ZH_TW && !NewApis::IsZhTwSku()) + { + lcid = LANGID_ZH_HK; + } + return lcid; + } + + DWORD GetUserPreferredUILanguages (__in DWORD dwFlags, __out PULONG pulNumLanguages, __out_ecount_opt(*pcchLanguagesBuffer) PWSTR pwszLanguagesBuffer, __in PULONG pcchLanguagesBuffer) + { + _ASSERTE(dwFlags==MUI_LANGUAGE_NAME); + + LCID lcid=GetUserDefaultUILanguage(); + if (lcid == 0) + return 0; + + lcid = WorkAroundXpTaiwanHongKongBug(lcid); + + WCHAR wszBuffer[LOCALE_NAME_MAX_LENGTH]; + if (NewApis::LCIDToLocaleName(lcid, wszBuffer, NumItems(wszBuffer), 0) == 0) + return 0; + + SIZE_T sLen=wcslen(wszBuffer)+2; + ULONG uLen = (ULONG)sLen; + if (uLen != sLen) + { + SetLastError(ERROR_ARITHMETIC_OVERFLOW); + return 0; + } + + *pulNumLanguages=1; + if (pwszLanguagesBuffer == NULL) + { + *pcchLanguagesBuffer=uLen; + return 1; + } + + if (sLen > *pcchLanguagesBuffer) + { + *pcchLanguagesBuffer=uLen; + SetLastError(ERROR_BUFFER_OVERFLOW); + return 0; + } + + *pcchLanguagesBuffer=uLen; + wcscpy_s(pwszLanguagesBuffer, sLen, wszBuffer); + pwszLanguagesBuffer[sLen-1]=W('\0'); + SetLastError(0); + return 1; + } + + + int GetUserDefaultLocaleName(__out_ecount(cchLocaleName) LPWSTR lpLocaleName, __in int cchLocaleName) + { + LCID lcid=GetUserDefaultLCID(); + if (lcid == 0) + return 0; + return NewApis::LCIDToLocaleName(lcid,lpLocaleName,cchLocaleName,0); + } + + int GetDateFormatEx(__in LPCWSTR lpLocaleName, __in DWORD dwFlags, __in_opt CONST SYSTEMTIME* lpDate, __in_opt LPCWSTR lpFormat, + __out_ecount(cchDate) LPWSTR lpDateStr, __in int cchDate, __in_opt LPCWSTR lpCalendar) + { + _ASSERTE(lpCalendar==NULL); + LCID lcid=NewApis::LocaleNameToLCID(lpLocaleName,0); + if (lcid == 0) + return 0; + return GetDateFormatW(lcid, dwFlags, lpDate, lpFormat, lpDateStr, cchDate); + } + + + int GetLocaleInfoEx (__in LPCWSTR lpLocaleName, __in LCTYPE LCType, __out_ecount_opt(cchData) LPWSTR lpLCData, __in int cchData) + { + _ASSERTE((lpLCData == NULL && cchData == 0) || (lpLCData != NULL && cchData > 0)); + + // Note that this'll return neutral LCIDs + LCID lcid=NewApis::LocaleNameToLCID(lpLocaleName,0); + if (lcid == 0) + return 0; + + int iRetCode = 0; + // Special casing LOCALE_SPARENT to do Uplevel fallback. + if ( (LCType & ~LOCALE_NOUSEROVERRIDE) == LOCALE_SPARENT ) + { + // OS doesn't know some LCTypes + iRetCode = UplevelFallback::GetLocaleInfoEx(lpLocaleName, lcid, LCType, lpLCData, cchData); + } + else + { + iRetCode=GetLocaleInfoW(lcid,LCType,lpLCData,cchData); + if (iRetCode == 0 && GetLastError() == ERROR_INVALID_FLAGS) + { + // OS doesn't know some LCTypes + iRetCode = UplevelFallback::GetLocaleInfoEx(lpLocaleName, lcid, LCType, lpLCData, cchData); + } + } + + return iRetCode; + }; + + // + // TurkishCompareStringIgnoreCase + // In downlevel platforms CompareString doesn't support Turkish I comparison. TurkishCompareStringIgnoreCase is to work around for this. + // In Turkish and Azeri cultures: + // ToUpper(i) = u0130 (Upper case I with dot above it) + // ToLower(I) = u0131 (Lower case i with no dot above). + // Toupper(i) != I + // ToLower(I) != i + // TurkishCompareStringIgnoreCase will scan the input strings and convert every i to u0130 and every I to u0131 and then call + // the system CompareString. + // if lpString1 not include i, I, u0130, and u0131 then we'll just call the system CompareString otherwise we'll scan the lpString2 + // to detect if we need to do the conversions mentioned above. + // + + #define TURKISH_CAPITAL_I_DOT_ABOVE ((WCHAR) 0x0130) + #define TURKISH_LOWERCASE_DOTLESS_I ((WCHAR) 0x0131) + #define LATIN_LOWERCASE_I_DOT_ABOVE ('i') + #define LATIN_CAPITAL_DOTLESS_I ('I') + + int TurkishCompareStringIgnoreCase(LCID lcid, DWORD dwCmpFlags, LPCWSTR lpString1, int cchCount1, LPCWSTR lpString2, int cchCount2) + { + int str1Index = 0; + int str2Index = 0; + BOOL fScanStr2 = FALSE; + + for (str1Index=0; str1Index<cchCount1; str1Index++) + { + if (lpString1[str1Index] == LATIN_LOWERCASE_I_DOT_ABOVE + || lpString1[str1Index] == LATIN_CAPITAL_DOTLESS_I) + { + break; + } + else if (lpString1[str1Index] == TURKISH_CAPITAL_I_DOT_ABOVE + || lpString1[str1Index] == TURKISH_LOWERCASE_DOTLESS_I) + { + fScanStr2 = TRUE; + } + } + + if (str1Index >= cchCount1) + { + if (!fScanStr2) + { + return ::CompareStringW(lcid, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2); + } + + for (str2Index=0; str2Index<cchCount2; str2Index++) + { + if (lpString2[str2Index] == LATIN_LOWERCASE_I_DOT_ABOVE + || lpString2[str2Index] == LATIN_CAPITAL_DOTLESS_I) + { + break; + } + } + + if (str2Index >= cchCount2) + { + return ::CompareStringW(lcid, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2); + } + } + + NewArrayHolder<WCHAR> pBuffer = new WCHAR[cchCount1 + cchCount2]; + + if (str1Index>0) + { + memcpy_s(pBuffer, cchCount1 * sizeof(WCHAR), lpString1, str1Index * sizeof(WCHAR)); + } + + for (; str1Index<cchCount1; str1Index++) + { + pBuffer[str1Index] = (lpString1[str1Index] == LATIN_LOWERCASE_I_DOT_ABOVE) + ? TURKISH_CAPITAL_I_DOT_ABOVE + : ((lpString1[str1Index] == LATIN_CAPITAL_DOTLESS_I) + ? TURKISH_LOWERCASE_DOTLESS_I + : lpString1[str1Index]); + } + + if (str2Index>0) + { + memcpy_s(&pBuffer[cchCount1], cchCount2 * sizeof(WCHAR), lpString2, str2Index * sizeof(WCHAR)); + } + + for (; str2Index<cchCount2; str2Index++) + { + pBuffer[cchCount1 + str2Index] = (lpString2[str2Index] == LATIN_LOWERCASE_I_DOT_ABOVE) + ? TURKISH_CAPITAL_I_DOT_ABOVE + : ((lpString2[str2Index] == LATIN_CAPITAL_DOTLESS_I) + ? TURKISH_LOWERCASE_DOTLESS_I + : lpString2[str2Index]); + } + + return ::CompareStringW(lcid, dwCmpFlags, pBuffer, cchCount1, &pBuffer[cchCount1], cchCount2); + } + + int CompareStringEx(__in LPCWSTR lpLocaleName, __in DWORD dwCmpFlags, __in_ecount(cchCount1) LPCWSTR lpString1, __in int cchCount1, __in_ecount(cchCount2) LPCWSTR lpString2, + __in int cchCount2, __in_opt LPNLSVERSIONINFO lpVersionInformation, __in_opt LPVOID lpReserved, __in_opt LPARAM lParam ) + { + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + SO_TOLERANT; + PRECONDITION(CheckPointer(lpLocaleName)); + PRECONDITION(CheckPointer(lpString1)); + PRECONDITION(CheckPointer(lpString2)); + } CONTRACTL_END; + + // Invariant is like en-US (default table) on downlevel (pre-vista) OS's + if (lpLocaleName[0] == W('\0')) + { + // For now, use "en" instead. + lpLocaleName = W("en-US"); + } + + // Get an LCID to call the downlevel function + LCID lcid=NewApis::LocaleNameToLCID(lpLocaleName,0); + if (lcid == 0) + return 0; + + // Need to remap flags to get rid of Vista flags and replace with downlevel flags + // (not used at this moment) + if ((dwCmpFlags & LINGUISTIC_IGNOREDIACRITIC)!=0) + { + dwCmpFlags -= LINGUISTIC_IGNOREDIACRITIC; + dwCmpFlags |= NORM_IGNORENONSPACE; + } + if ((dwCmpFlags & LINGUISTIC_IGNORECASE)!=0) + { + dwCmpFlags -= LINGUISTIC_IGNORECASE; + dwCmpFlags |= NORM_IGNORECASE; + } + dwCmpFlags &= (~NORM_LINGUISTIC_CASING); + + if (((dwCmpFlags & NORM_IGNORECASE)!=0) && IsTurkicILcid(lcid)) + { + return TurkishCompareStringIgnoreCase(lcid, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2); + } + + return ::CompareStringW(lcid,dwCmpFlags,lpString1,cchCount1,lpString2,cchCount2); + } + + __inline WCHAR MapCase(const WORD * table, WCHAR wch) + { + WORD value = table[wch >> 8]; + value = table[value + ((wch >> 4) & 0xf)]; + value = table[value + (wch & 0xf)]; + return (wch + (int)((short)value)); + } + + // since this should only be run on XP, we just use the same table as XP + __inline WCHAR ToUpperXP(WCHAR wch) + { + return MapCase(s_pUppercaseIndexTableXP, wch); + } + + INT32 CompareOrdinalIgnoreCaseHelper(__in_ecount(count) DWORD* strAChars, __in_ecount(count) DWORD* strBChars, int count) + { + if( count == 0) + return 0; + + int temp = 0; + + _ASSERTE( count >0); + + // Only go through fast code path if two strings have the same alignment + if (((size_t)strAChars & 3) == ((size_t)strBChars & 3)) { + int unalignedBytesA = (size_t)strAChars & 3; + + _ASSERTE(unalignedBytesA == 0 || unalignedBytesA == 2); + // First try to make the strings aligned at DWORD boundary. + if( unalignedBytesA != 0 ) { + LPWSTR ptr1 = (LPWSTR)strAChars; + LPWSTR ptr2 = (LPWSTR)strBChars; + + if (*ptr1 != *ptr2) { + temp = ((int)ToUpperXP(*ptr1) - (int)ToUpperXP(*ptr2)); + if( temp != 0) { + return temp; + } + } + + --count; + strAChars = (DWORD *)(ptr1 + 1); + strBChars = (DWORD *)(ptr2 + 1); + } + + // Loop comparing a DWORD at a time. + while (count >= 2) { + _ASSERTE(IS_ALIGNED((size_t)strAChars, 4) && IS_ALIGNED((size_t)strBChars, 4)); + if ((*strAChars - *strBChars) != 0) { + LPCWSTR ptr1 = (WCHAR*)strAChars; + LPCWSTR ptr2 = (WCHAR*)strBChars; + + if (*ptr1 != *ptr2) { + temp = ((int)ToUpperXP(*ptr1) - (int)ToUpperXP(*ptr2)); + } + if (temp != 0) { + return (temp); + } + + temp = (int)ToUpperXP(*(ptr1+1)) - (int)ToUpperXP(*(ptr2+1)); + if (temp != 0) { + return (temp); + } + } + ++strBChars; + ++strAChars; + count -= 2; + } + } + + // We can exit the loop when we see two different DWORDs and one of them contains surrogate + // or they are equal after case conversion. + // We can also exit the loop when there is no or only one character left. + if( count == 0) { + return 0; + } + + // we need to handle one special case here. Say we have two strings like: + // A HS1 LS1 HS2 LS2 or A HS1 LS1 + // A HS1 LS2 HS2 LS2 or A HS1 NS + // we need to go back a char to decide the order + LPCWSTR pwStrB = (LPWSTR)strBChars; + LPCWSTR pwStrA = (LPWSTR)strAChars; + + temp = 0; + while ((count--) > 0) + { + WCHAR charA = *pwStrA++; + WCHAR charB = *pwStrB++; + + if( charA != charB) { + charA = ToUpperXP(charA); + charB = ToUpperXP(charB); + + temp = (int)charA - (int)charB; + + if (temp != 0) { + return (temp); + } + } + } + + return 0; + } + + + int CompareStringOrdinal(__in_ecount(cchCount1) LPCWSTR string1, __in int cchCount1, __in_ecount(cchCount2) LPCWSTR string2, __in int cchCount2, __in BOOL bIgnoreCase) + { + // This should only happen for IgnoreCase == true. The rest are hard coded + if (!bIgnoreCase) + { + return 0; + } + + DWORD *strAChars, *strBChars; + strAChars = (DWORD*)string1; + strBChars = (DWORD*)string2; + + // If the strings are the same length, compare exactly the right # of chars. + // If they are different, compare the shortest # + 1 (the '\0'). + int count = cchCount1; + if( count > cchCount2) + count = cchCount2; + + INT32 ret = CompareOrdinalIgnoreCaseHelper(strAChars, strBChars, count); + if( ret == 0) { + ret = cchCount1 - cchCount2; + } + + if (ret > 0) + { + return CSTR_GREATER_THAN; + } + if (ret < 0) + { + return CSTR_LESS_THAN; + } + + return CSTR_EQUAL; + } + + inline bool HasOnlyUppercaseOrLowercaseFlag(__in DWORD flags) + { + if(((flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE))== 0) + || MORE_THAN_ONE(flags, LCMAP_UPPERCASE) + || MORE_THAN_ONE(flags, LCMAP_LOWERCASE)) + { + return false; + } + return true; + } + + int TurkicICasing( __in DWORD flags, + __in_ecount(cchSrc) LPCWSTR source, + __in int cchSrc, + __out_ecount(cchDest) LPWSTR destination, + __in int cchDest) + { + _ASSERTE(source != NULL); + _ASSERTE(cchSrc > 0); + _ASSERTE(destination != NULL); + _ASSERTE(cchSrc == cchDest); + _ASSERTE(HasOnlyUppercaseOrLowercaseFlag(flags)); + + const WORD * table; + WCHAR dottedI, mappedDottedI; + WCHAR dotlessI, mappedDotlessI; + + if ((flags & LCMAP_UPPERCASE) != 0) + { + table = s_pUppercaseIndexTableLinguisticXP; + dottedI = LATIN_LOWERCASE_I_DOT_ABOVE; + mappedDottedI = TURKISH_CAPITAL_I_DOT_ABOVE; + dotlessI = TURKISH_LOWERCASE_DOTLESS_I; + mappedDotlessI = LATIN_CAPITAL_DOTLESS_I; + } + else + { + table = s_pLowercaseIndexTableLinguisticXP; + dottedI = TURKISH_CAPITAL_I_DOT_ABOVE; + mappedDottedI = LATIN_LOWERCASE_I_DOT_ABOVE; + dotlessI = LATIN_CAPITAL_DOTLESS_I; + mappedDotlessI = TURKISH_LOWERCASE_DOTLESS_I; + } + + for (int i = 0; i < cchSrc && i < cchDest; ++i) + { + if (source[i] == dottedI) + { + destination[i] = mappedDottedI; + } + else if (source[i] == dotlessI) + { + destination[i] = mappedDotlessI; + } + else { + destination[i] = MapCase(table, source[i]); + } + } + + return cchSrc; + } + + int DefaultLinguisticCasing( __in DWORD flags, + __in_ecount(cchSrc) LPCWSTR source, + __in int cchSrc, + __out_ecount(cchDest) LPWSTR destination, + __in int cchDest) + { + _ASSERTE(source != NULL); + _ASSERTE(cchSrc > 0); + _ASSERTE(destination != NULL); + _ASSERTE(cchSrc == cchDest); + _ASSERTE(HasOnlyUppercaseOrLowercaseFlag(flags)); + + const WORD * table; + + if ((flags & LCMAP_UPPERCASE) != 0) + { + table = s_pUppercaseIndexTableLinguisticXP; + } + else + { + table = s_pLowercaseIndexTableLinguisticXP; + } + + for (int i = 0; i < cchSrc && i < cchDest; ++i) + { + destination[i] = MapCase(table, source[i]); + } + + return cchSrc; + } + + int LinguisticCaseString(__in LCID lcid,__in DWORD flags, + __in_ecount(cchSrc) const WCHAR * source, + __in int cchSrc, + __out_ecount_opt(cchDest) WCHAR * destination, + __in int cchDest) + { + _ASSERTE(lcid != 0); + + if ( (cchSrc == 0) || (cchDest < 0) || (source == NULL) || + ((cchDest != 0) && (destination == NULL)) ) + { + SetLastError(ERROR_INVALID_PARAMETER); + return (0); + } + + if(!HasOnlyUppercaseOrLowercaseFlag(flags)) + { + SetLastError(ERROR_INVALID_FLAGS); + return (0); + } + + if(cchSrc < 0) + { + cchSrc = (int)wcslen(source); + } + + if(destination == NULL) + { + return cchSrc; + } + + if(IsTurkicILcid(lcid)) + { + return TurkicICasing(flags, + source, cchSrc, + destination, cchDest); + } + else + { + return DefaultLinguisticCasing(flags, + source, cchSrc, + destination, cchDest); + } + + } + +#ifdef _MSC_VER +// Get rid of size OACR requirement. +#pragma warning(push) +#pragma warning(disable:25057) +#endif + int LCMapStringEx(__in LPCWSTR lpLocaleName, + __in DWORD dwMapFlags, + __in_ecount(cchSrc) LPCWSTR lpSrcStr, + __in int cchSrc, + __out_xcount_opt(cchDest) LPWSTR lpDestStr, + __in int cchDest, + __in_opt LPNLSVERSIONINFO lpVersionInformation, + __in_opt LPVOID lpReserved, + __in_opt LPARAM lParam) +#ifdef _MSC_VER +#pragma warning(pop) +#endif + { + LCID lcid=NewApis::LocaleNameToLCID(lpLocaleName,0); + if (lcid == 0) + return 0; + + // Need to remap flags to get rid of Vista flags and replace with downlevel flags + // (not used at this moment) + if ((dwMapFlags & LINGUISTIC_IGNOREDIACRITIC)!=0) + { + dwMapFlags -= LINGUISTIC_IGNOREDIACRITIC; + dwMapFlags |= NORM_IGNORENONSPACE; + } + if ((dwMapFlags & LINGUISTIC_IGNORECASE)!=0) + { + dwMapFlags -= LINGUISTIC_IGNORECASE; + dwMapFlags |= NORM_IGNORECASE; + } + dwMapFlags &= (~NORM_LINGUISTIC_CASING); + +// START WORK_AROUND_WIN2K3_LCMAPSTRING_BUG + if((dwMapFlags & LCMAP_LINGUISTIC_CASING) != 0 + && (dwMapFlags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE)) != 0) + { + dwMapFlags &= (~LCMAP_LINGUISTIC_CASING); + + return LinguisticCaseString(lcid, dwMapFlags, lpSrcStr, cchSrc, lpDestStr, cchDest); + } +// END WORK_AROUND_WIN2K3_LCMAPSTRING_BUG + return ::LCMapStringW(lcid,dwMapFlags, lpSrcStr, cchSrc, lpDestStr, cchDest); + } + + int FindNLSStringEx(__in LPCWSTR lpLocaleName, + __in DWORD dwFindNLSStringFlags, + __in_ecount(cchSource) LPCWSTR lpStringSource, + __in int cchSource, + __in_ecount(cchValue) LPCWSTR lpStringValue, + __in int cchValue, + __out_opt LPINT pcchFound, + __in_opt LPNLSVERSIONINFO lpVersionInformation, + __in_opt LPVOID lpReserved, + __in_opt LPARAM lParam) + { + int retValue = -1; + + if (lpLocaleName == NULL || lpStringSource == NULL || lpStringValue == NULL) + { + return retValue; + } + + if (dwFindNLSStringFlags & (FIND_ENDSWITH | FIND_FROMEND)) + { + retValue = NewApis::LastIndexOfString(lpLocaleName, lpStringSource, cchSource, lpStringValue, cchValue, (int) dwFindNLSStringFlags & FIND_NLS_STRING_FLAGS_NEGATION, dwFindNLSStringFlags & FIND_ENDSWITH); + } + else + { + retValue = NewApis::IndexOfString(lpLocaleName, lpStringSource, cchSource, lpStringValue, cchValue, (int) dwFindNLSStringFlags & FIND_NLS_STRING_FLAGS_NEGATION, dwFindNLSStringFlags & FIND_STARTSWITH); + } + return retValue; + } + + + // Mac and Windows <= Windows XP + // Note: "Function" is unused, always handles sorting for now + // Note: "dwFlags" is unused, we don't have flags for now + // Note: "lpVersionInfo" is unused, we always presume the current version + BOOL IsNLSDefinedString(NLS_FUNCTION Function, DWORD dwFlags, LPNLSVERSIONINFOEX lpVersionInfo, LPCWSTR pString, int nStringLen ) + { + // Ported downlevel code from comnlsinfo.cpp + + CQuickBytes buffer; + buffer.AllocThrows(16); + int ich = 0; + + while(ich < nStringLen) + { + WCHAR wch = pString[ich]; + + int dwBufSize=NewApis::LCMapStringEx(W("en-US"),LCMAP_SORTKEY|SORT_STRINGSORT,pString+ich,1,(LPWSTR)buffer.Ptr(), + (int)(buffer.Size()/sizeof(WCHAR)),NULL,NULL,0); + + if (dwBufSize == 0) + { + buffer.AllocThrows(buffer.Size()*2); + continue; // try again + } + + if (LPBYTE(buffer.Ptr())[0] == 0x1) // no weight + { + // + // Check for the NULL case and formatting characters case. Not + // defined but valid. + // + switch(wch) + { + case 0x0000: // NULL + case 0x0640: // TATWEEL + case 0x180b: // MONGOLIAN FVS 1 + case 0x180c: // MONGOLIAN FVS 2 + case 0x180d: // MONGOLIAN FVS 3 + case 0x180e: // MONGOLIAN VOWEL SEPERATOR + case 0x200c: // ZWNJ + case 0x200d: // ZWJ + case 0x200e: // LRM + case 0x200f: // RLM + case 0x202a: // LRE + case 0x202b: // RLE + case 0x202c: // PDF + case 0x202d: // LRO + case 0x202e: // RLO + case 0x206a: // ISS + case 0x206b: // SSS + case 0x206c: // IAFS + case 0x206d: // AAFS + case 0x206e: // NATIONAL DS + case 0x206f: // NOMINAL DS + case 0xfeff: // ZWNBSP + case 0xfff9: // IAA + case 0xfffa: // IAS + case 0xfffb: // IAT + case 0xfffc: // ORC + case 0xfffd: // RC + ich++; + continue; + + default: + return (FALSE); + } + } + + // + // Eliminate Private Use characters. They are defined but cannot be considered + // valid because AD-style apps should not use them in identifiers. + // + if ((wch >= PRIVATE_USE_BEGIN) && (wch <= PRIVATE_USE_END)) + { + return (FALSE); + } + + // + // Eliminate invalid surogates pairs or single surrogates. Basically, all invalid + // high surrogates have aleady been filtered (above) since they are unsortable. + // All that is left is to check for standalone low surrogates and valid high + // surrogates without corresponding low surrogates. + // + + if ((wch >= LOW_SURROGATE_START) && (wch <= LOW_SURROGATE_END)) + { + // Leading low surrogate + return (FALSE); + } + else if ((wch >= HIGH_SURROGATE_START) && (wch <= HIGH_SURROGATE_END)) + { + // Leading high surrogate + if ( ((ich + 1) < nStringLen) && // Surrogates not the last character + (pString[ich+1] >= LOW_SURROGATE_START) && (pString[ich+1] <= LOW_SURROGATE_END)) // Low surrogate + { + // Valid surrogates pair, High followed by a low surrogate. Skip the pair! + ich++; + } + else + { + // High surrogate without low surrogate, so exit. + return (FALSE); + } + } + + ich++; + + } + return (TRUE); + } + + + int GetCalendarInfoEx(__in LPCWSTR lpLocaleName, + __in CALID Calendar, + __in_opt LPCWSTR pReserved, + __in CALTYPE CalType, + __out_ecount_opt(cchData) LPWSTR lpCalData, + __in int cchData, + __out_opt LPDWORD lpValue) + { + + _ASSERTE((lpCalData == NULL && cchData == 0) || (lpCalData != NULL && cchData > 0)); + if ((CalType & CAL_RETURN_NUMBER)) + { + // If CAL_RETURN_NUMBER, lpValue must be non-null and lpCalData must be null + _ASSERTE((lpValue != NULL) && (lpCalData == NULL)); + } + + LCID lcid=NewApis::LocaleNameToLCID(lpLocaleName,0); + + // zh-HK has English month/day names in older OS's, so we need to fix that (pre-Vista OS's) + if (lcid == 0x0c04 && Calendar == CAL_GREGORIAN && + ((CalType >= CAL_SDAYNAME1 && CalType <= CAL_SABBREVMONTHNAME13) || + (CalType >= CAL_SSHORTESTDAYNAME1 && CalType <= CAL_SSHORTESTDAYNAME7))) + { + // zh-TW has the English names for those month/day name values + lcid = 0x0404; + } + + if (lcid == 0) + return 0; + return ::GetCalendarInfoW(lcid, Calendar, CalType, lpCalData, cchData, lpValue ); + } + + + namespace LegacyCallbacks + { + LPARAM lDateFormatParam = NULL; + DATEFMT_ENUMPROCEXEX realDateCallback = NULL; + + BOOL CALLBACK EnumDateFormatsProcWrapper(__in_z LPTSTR lpDateFormatString, __in CALID Calendar) + { + if (realDateCallback != NULL && lDateFormatParam != NULL) + return realDateCallback(lpDateFormatString, Calendar, lDateFormatParam); + + // Didn't have the globals, fail + return false; + }; + + BOOL EnumDateFormatsExEx(DATEFMT_ENUMPROCEXEX lpDateFmtEnumProcExEx, LPCWSTR lpLocaleName, DWORD dwFlags, LPARAM lParam) + { + LCID lcid=NewApis::LocaleNameToLCID(lpLocaleName,0); + if (lcid == 0) + return 0; + + DelayCreateCriticalSection(); + + BOOL ret = false; + { + CRITSEC_Holder sCrstHolder(g_pDownlevelNlsCrst); + + // Store our real callback and lParam + lDateFormatParam = lParam; + realDateCallback = lpDateFmtEnumProcExEx; + ret = EnumDateFormatsExW(EnumDateFormatsProcWrapper, lcid, dwFlags); + } + return ret; + }; + + LPARAM lTimeFormatParam = NULL; + TIMEFMT_ENUMPROCEX realTimeCallback = NULL; + + BOOL CALLBACK EnumTimeFormatsProcWrapper(__in_z LPTSTR lpTimeFormatString) + { + if (realTimeCallback != NULL && lTimeFormatParam != NULL) + return realTimeCallback(lpTimeFormatString, lTimeFormatParam); + + // Didn't have the globals, fail + return false; + }; + + BOOL EnumTimeFormatsEx(TIMEFMT_ENUMPROCEX lpTimeFmtEnumProcEx, LPCWSTR lpLocaleName, DWORD dwFlags, LPARAM lParam) + { + LCID lcid=NewApis::LocaleNameToLCID(lpLocaleName,0); + if (lcid == 0) + return 0; + + DelayCreateCriticalSection(); + + BOOL ret = false; + { + CRITSEC_Holder sCrstHolder(g_pDownlevelNlsCrst); + + // Store our real callback and lParam + lTimeFormatParam = lParam; + realTimeCallback = lpTimeFmtEnumProcEx; + ret = EnumTimeFormatsW(EnumTimeFormatsProcWrapper, lcid, dwFlags); + } + return ret; + }; + + LPARAM lCalendarInfoParam = NULL; + CALINFO_ENUMPROCEXEX realCalendarInfoCallback = NULL; + + BOOL CALLBACK EnumCalendarInfoProcWrapper(__in_z LPTSTR lpCalendarInfoString, __in CALID Calendar) + { + if (realCalendarInfoCallback != NULL && lCalendarInfoParam != NULL) + return realCalendarInfoCallback(lpCalendarInfoString, Calendar, NULL, lCalendarInfoParam); + + // Didn't have the globals, fail + return false; + }; + + BOOL EnumCalendarInfoExEx(CALINFO_ENUMPROCEXEX pCalInfoEnumProcExEx, LPCWSTR lpLocaleName, CALID Calendar, LPCWSTR lpReserved, CALTYPE CalType, LPARAM lParam) + { + LCID lcid=NewApis::LocaleNameToLCID(lpLocaleName,0); + if (lcid == 0) + return 0; + + DelayCreateCriticalSection(); + + BOOL ret = false; + { + CRITSEC_Holder sCrstHolder(g_pDownlevelNlsCrst); + + // Store our real callback and lParam + lCalendarInfoParam = lParam; + realCalendarInfoCallback = pCalInfoEnumProcExEx; + ret = EnumCalendarInfoExW(EnumCalendarInfoProcWrapper, lcid, Calendar, CalType); + } + return ret; + }; + } + + // This is where we fudge data the OS doesn't know (even on Vista) + namespace UplevelFallback + { + // Some properties are unknown to downlevel OS's (pre windows 7), so synthesize them + // Pass in LCID if calling from the downlevel APIs. + // Note that Vista gets here for neutrals as well as specifics + // The only neutral Vista properties we support are SNAME, SPARENT & INEUTRAL (which assumes its a locale) + // if lpLCData is NULL, caller wants required size to be returned. So we check before assigning and return + // buffer size. + int GetLocaleInfoEx(__in LPCWSTR lpLocaleName, __in LCID lcid, __in LCTYPE LCType, __out_ecount_opt(cchData) LPWSTR lpLCData, __in int cchData) + { + _ASSERTE((lpLCData == NULL && cchData == 0) || (lpLCData != NULL && cchData > 0)); + + LPCWSTR useString = NULL; + WCHAR buffer[80]; + + // We don't differentiate user overrides for these types + LCType &= ~LOCALE_NOUSEROVERRIDE; + + // TODO: NLS Arrowhead -Find better ways of handling these properties: + // Right now we'll just fill them in with constant data + switch (LCType) + { + case LOCALE_SPERCENT: // Percent symbol + useString = W("%"); + break; + case LOCALE_IPOSITIVEPERCENT | LOCALE_RETURN_NUMBER: // Positive percent format + if (lpLCData) + { + *((DWORD*)lpLCData) = 0; + } + return 2; + case LOCALE_INEGATIVEPERCENT | LOCALE_RETURN_NUMBER: // Negative percent format + if (lpLCData) + { + *((DWORD*)lpLCData) = 0; + } + return 2; + case LOCALE_SPERMILLE: // Per mille symbol + useString = W("\x2030"); + break; + case LOCALE_SSHORTTIME: // Short time format (default) + // CultureData synthesizes short time from long time + + case LOCALE_SENGLISHDISPLAYNAME: // English display name (ie: Fijiian (Fiji)) + case LOCALE_SNATIVEDISPLAYNAME: // Native dispaly name (ie: Deutsch (Deutschland)) + // native & english names are built more easily in managed code + + //case LOCALE_SMONTHDAY: // month/day format + //we get month/day patterns from the calendar data. This would be override, but that's not assigned + // TODO: NLS Arrowhead - in windows 7 if we add overrides + break; + case LOCALE_IREADINGLAYOUT | LOCALE_RETURN_NUMBER: // Is Right To Left? + // Use the RTL bit in the font signature + LOCALESIGNATURE LocSig; + if (NewApis::GetLocaleInfoEx( lpLocaleName, LOCALE_FONTSIGNATURE, (LPWSTR)&LocSig, sizeof(LocSig) / sizeof(WCHAR) ) == + sizeof(LocSig) / sizeof(WCHAR)) + { + // Got the locale signature information, get the isrtl bit 123 to see if its RTL. + if (lpLCData) + { + *((DWORD*)lpLCData) = ((LocSig.lsUsb[3] & 0x0800) != 0) ? 1 : 0; + } + return 2; + } + // Failed, just return 0 + return 0; + + case LOCALE_SNAME: + // If we don't have an LCID, find one, this is < Vista or neutrals + if (lcid == 0) lcid = NewApis::LocaleNameToLCID(lpLocaleName,0); + + // Make sure windows recognizes this LCID, or that its zh-Hant, sr, or bs + if (GetLocaleInfoW(lcid, LOCALE_IDIGITS | LOCALE_RETURN_NUMBER, NULL, 0) == 0 && + lcid != 0x7c04 && lcid != 0x7c1a && lcid != 0x781a) + { + // Not a real locale, fail, don't fail for neutrals zh-Hant, sr, or bs. + return 0; + } + + // Convert name to LCID (so we get pretty name) + if (lcid != 0) + return NewApis::LCIDToLocaleName(lcid, lpLCData, cchData, 0); + else + return 0; + + case LOCALE_INEUTRAL | LOCALE_RETURN_NUMBER: + // If its XP/Win2K3 or lower, then the lcid can tell us + + if (lcid != 0) + { + if (lpLCData) + *((DWORD*)lpLCData) = ((lcid < 0x0400 || lcid > 0x7000) && (lcid < 0x10000)) ? 1 : 0; + return 2; + } + + // Vista or Win2K8 fail for neutrals + // Note this assumes that neutral or not it is a valid locale. + if (NewApis::GetLocaleInfoEx(lpLocaleName, LOCALE_IDIGITS | LOCALE_RETURN_NUMBER, NULL, 0) == 0) + { + // Failed, its a neutral + // Note, we assumed it is a valid locale. (Caller lookind in our name/lcid tables undoubtedly) + if (lpLCData) + *((DWORD*)lpLCData) = 1; + } + else + { + // Succeeded, its not neutral + if (lpLCData) + *((DWORD*)lpLCData) = 0; + } + + // Return "success" + return 2; + + case LOCALE_SPARENT: + // Should only get here for neutrals or downlevel + // Downlevel only needs work if its not neutral + if (lcid != 0) + { + // Downlevel + // If its a neutral LCID then its "" Invariant + if ((lcid < 0x0400 || lcid > 0x7000) && (lcid < 0x10000)) + { + useString = W(""); + } + else + { + // Parent is same as LCID & 0x3ff, except for a few cases + switch (lcid) + { + case 0x0404: // zh-TW + case 0x0c04: // zh-HK + case 0x1404: // zh-MO + lcid = 0x7c04; // zh-Hant + break; + case 0x081a: // sr-Latn-CS + case 0x0c1a: // sr-Cryl-CS + lcid = 0x7c1a; // sr + break; + case 0x201a: // bs-Cyrl-BA + case 0x141a: // bs-Latn-BA + lcid = 0x781a; // bs + break; + default: + lcid &= 0x03ff; + break; + } + + // Get the name from LCIDToName + if (NewApis::LCIDToLocaleName(lcid, buffer, NumItems(buffer), 0)) + { + useString = buffer; + } + } + } + else + { + // Neutral on Vista / W2K8. Always "" Invariant + // or neutral LCID + useString = W(""); + } + break; + + case LOCALE_SNAN: + useString = W("NaN"); + break; + case LOCALE_SPOSINFINITY: + useString = W("Infinity"); + break; + case LOCALE_SNEGINFINITY: + useString = W("-Infinity"); + break; + } + + // Return number already returned, so we should have a string, else its unknown + if (useString == NULL) return 0; + + // Copy our string to the output & return + int size = (int)(wcslen(useString) + 1); + // if cchData is 0, then caller wants us to return size + if (size > cchData && cchData != 0) return 0; + if (lpLCData) + { + memcpy(lpLCData, useString, size * sizeof(WCHAR)); + } + + return size; + } + + LPCWSTR const arabicSuperShortDayNames[] = + { + W("\x0646"), // Day name for Monday + W("\x062b"), + W("\x0631"), + W("\x062e"), + W("\x062c"), + W("\x0633"), + W("\x062d") // Day name for Sunday + }; + + LPCWSTR const chineseSuperShortDayNames[] = + { + W("\x4e00"), // Day name for Monday + W("\x4e8c"), + W("\x4e09"), + W("\x56db"), + W("\x4e94"), + W("\x516d"), + W("\x65e5") // Day name for Sunday + }; + + LPCWSTR const hebrewSuperShortDayNames[] = + { + W("\x05d1"), // Day name for Monday + W("\x05d2"), + W("\x05d3"), + W("\x05d4"), + W("\x05d5"), + W("\x05e9"), + W("\x05d0") // Day name for Sunday + }; + + LPCWSTR const mongolianSuperShortDayNames[] = + { + W("\x0414\x0430"), // Day name for Monday + W("\x041c\x044f"), + W("\x041b\x0445"), + W("\x041f\x04af"), + W("\x0411\x0430"), + W("\x0411\x044f"), + W("\x041d\x044f") // Day name for Sunday + }; + + int GetCalendarInfoEx(__in_opt LPCWSTR lpLocaleName, + __in CALID Calendar, + __in_opt LPCWSTR pReserved, + __in CALTYPE CalType, + __out_ecount_opt(cchData) LPWSTR lpCalData, + __in int cchData, + __out_opt LPDWORD lpValue) + { + + _ASSERTE((lpCalData == NULL && cchData == 0) || (lpCalData != NULL && cchData > 0)); + if ((CalType & CAL_RETURN_NUMBER)) + { + // If CAL_RETURN_NUMBER, lpValue must be non-null and lpCalData must be null + _ASSERTE((lpValue != NULL) && (lpCalData == NULL)); + } + + // We don't differentiate user overrides for these types + CalType &= ~CAL_NOUSEROVERRIDE; + + LCID lcid=NewApis::LocaleNameToLCID(lpLocaleName,0); + int ret = 0; + LPCWSTR pUseString = W(""); + LPCWSTR const *pDays = NULL; + + // + // The requested info is a string. + // + + // NOTE: Genitive names will skip this and just return empty strings + // not much we can do for those. + switch (CalType) + { + case CAL_SMONTHDAY: + // Special cases for older locales with names that can't be truncated + pUseString = W("MMMM dd"); + + // Special case for CJK locales + if ((lcid & 0x3ff) == 0x11 || // Japanese + (lcid & 0x3ff) == 0x04) // Chinese + { + // Japanese & Chinese + pUseString = W("M'\x6708'd'\x65e5'"); + } + else if ((lcid & 0x3ff) == 0x012) // Korean + { + // Korean + pUseString = W("M'\xc6d4' d'\xc77c'"); + } + break; + + case CAL_SSHORTESTDAYNAME1: + case CAL_SSHORTESTDAYNAME2: + case CAL_SSHORTESTDAYNAME3: + case CAL_SSHORTESTDAYNAME4: + case CAL_SSHORTESTDAYNAME5: + case CAL_SSHORTESTDAYNAME6: + case CAL_SSHORTESTDAYNAME7: + // Special cases for older locales with names that can't be truncated + + // Arabic + if (((lcid & 0x3ff) == 0x01) && (Calendar == CAL_GREGORIAN || Calendar == CAL_HIJRI || Calendar == CAL_UMALQURA)) + pDays = arabicSuperShortDayNames; + + // Chinese + if (((lcid & 0x3ff) == 0x04) && (Calendar == CAL_GREGORIAN || Calendar == CAL_TAIWAN)) + pDays = chineseSuperShortDayNames; + + // Hebrew + if (((lcid & 0x3ff) == 0x0d) && (Calendar == CAL_GREGORIAN || Calendar == CAL_HEBREW)) + pDays = hebrewSuperShortDayNames; + + // Mongolian + if ((lcid & 0x3ff) == 0x50) pDays = mongolianSuperShortDayNames; + + if (pDays) + { + // If we have a special case string then use that + pUseString = pDays[CalType - CAL_SSHORTESTDAYNAME1]; + } + else + { + // If lpCalData is null they just want the size + // NOTE: We actually always know the size so we never ask. + if (lpCalData == NULL) + { + ret = 5; + } + else + { + ret = NewApis::GetCalendarInfoEx(lpLocaleName, Calendar, pReserved, CAL_SABBREVDAYNAME1 + (CalType - CAL_SSHORTESTDAYNAME1), lpCalData, cchData, lpValue); + if (ret > 0) + { + if (ret > 3) + { + // Just get the first two character, and NULL-terminate it. + PREFIX_ASSUME(3 < cchData); // we can assume this; otherwise call would have failed + lpCalData[3] = W('\0'); + ret = 3; + } + } + } + + // Done + return ret; + } + break; + default: + // + // Not a CALTYPE that this function provides. Just returns 0. + // + return 0; + break; + } + + // If we have a special case string, copy to the output & return + ret = (int)(wcslen(pUseString) + 1); + if (lpCalData && cchData >= ret) + { + // If they wanted string (not just count), then return it + memcpy(lpCalData, pUseString, ret * sizeof(WCHAR)); + } + + return ret; + } + + // Handle the to titlecaseflag + int LCMapStringEx(__in LPCWSTR lpLocaleName, + __in DWORD dwMapFlags, + __in_ecount(cchSrc) LPCWSTR lpSrcStr, + __in int cchSrc, + __out_ecount_opt(cchDest) LPWSTR lpDestStr, + __in int cchDest, + __in_opt LPNLSVERSIONINFO lpVersionInformation, + __in_opt LPVOID lpReserved, + __in_opt LPARAM lParam) + { + // We only know about the title case flag... + if (dwMapFlags != LCMAP_TITLECASE) + { + return 0; + } + + // Should be unused, we'll just workaround it to upper case in case there's a real problem + // Just call NewAPIs with the upper case flag + return NewApis::LCMapStringEx(lpLocaleName, LCMAP_UPPERCASE, lpSrcStr, cchSrc, + lpDestStr, cchDest, + lpVersionInformation, lpReserved, lParam); + } + + } + + static int __cdecl compareLcidToLCIDEntry ( const void *key, const void *value) + { + LCID lcid=*((LCID*)key); + LCIDEntry* entry=(LCIDEntry*)value; + return lcid-entry->lcid; + } + + int LCIDToLocaleName(__in LCID Locale, __out_ecount_opt(cchName) LPWSTR lpName, __in int cchName, __in DWORD dwFlags) + { + _ASSERTE((lpName == NULL && cchName == 0) || (lpName != NULL && cchName > 0)); + if (Locale==LOCALE_INVARIANT) + { + if (lpName != NULL) + { + *lpName=0; + } + return 1; + } + + LCIDEntry* entry=(LCIDEntry*)bsearch(&Locale,s_lcids,NumItems(s_lcids),sizeof(*s_lcids),compareLcidToLCIDEntry); + + if (entry == NULL) + { + //_ASSERTE(entry); + return 0; + } + + int length = 0; + if (cchName > 0) + { + PREFIX_ASSUME(lpName != NULL); // checked above, but prefix can't figure it out + wcscpy_s(lpName,cchName,entry->wszName); + length = (int)wcslen(entry->wszName) + 1; + } + return length; + } + + LCID LocaleNameToLCID(__in LPCWSTR lpName, __in DWORD dwFlags) + { + if (lpName == NULL || *lpName==0) + { + return LOCALE_INVARIANT; + } + + // Try the last one first, just in case + static int cachedEntry = 0; + PREFIX_ASSUME(cachedEntry < NumItems(s_names)); + + int test = VolatileLoad(&cachedEntry); + if (_wcsicmp(lpName, s_names[test].wszName) == 0) + { + _ASSERTE(s_names[test].lcid != 0); + return s_names[test].lcid; + } + + // Just do a binary lookup for the name + int iBottom =0; + int iTop = NumItems(s_names) - 1; + + while (iBottom <= iTop) + { + int iMiddle = (iBottom + iTop) / 2; + int result = _wcsicmp(lpName, s_names[iMiddle].wszName); + if (result == 0) + { + _ASSERTE(s_names[iMiddle].lcid != 0); + cachedEntry = iMiddle; + return s_names[iMiddle].lcid; + } + if (result < 0) + { + // pLocaleName was < s_names[iMiddle] + iTop = iMiddle - 1; + } + else + { + // pLocaleName was > s_names[iMiddle] + iBottom = iMiddle + 1; + } + } + + // Failed, return 0 + return 0; + } + + // Fallback for pre windows 7 machines + int ResolveLocaleName(__in LPCWSTR lpNameToResolve, __in_ecount_opt(cchLocaleName) LPWSTR lpLocaleName, __in int cchLocaleName) + { + PWSTR pSpecific = NULL; + int retVal = 0; + + // Doesn't matter for mac, for windows map the name to LCID, then ask for LOCALE_ILANGUAGE to map the + // LCID to a specific (legacy GetLocaleInfo behavior), then map the LCID to a name + LCID lcid = LocaleNameToLCID(lpNameToResolve, 0); + DWORD specific; + + // Some neutrals have specific values that downlevel OS's can't provide easily: + retVal = 2; + switch (lcid) + { + case 0x0004: specific = 0x0804; break; // zh-Hans::zh-CN::0804 + case 0x000a: specific = 0x0c0a; break; // es::es-ES::0c0a + case 0x003c: specific = 0x083c; break; // ga::ga-IE::083c + case 0x005d: specific = 0x085d; break; // iu::iu-Latn-CA:085d + case 0x005f: specific = 0x085f; break; // tzm::tzm-Latn-DZ:085f + case 0x703b: specific = 0x243b; break; // smn::smn-FI::243b + case 0x743b: specific = 0x203b; break; // sms::sms-FI::203b + case 0x7804: specific = 0x0804; break; // zh::zh-CN::0804 + case 0x7814: specific = 0x0814; break; // nn::nn-NO::0814 + case 0x781a: specific = 0x141a; break; // bs::bs-Latn-BA:141a + case 0x783b: specific = 0x1c3b; break; // sma::sma-SE::1c3b + case 0x7c04: specific = 0x0c04; break; // zh-Hant::zh-HK::0c04 + case 0x7c1a: specific = 0x081a; break; // sr::sr-Latn-CS:081a (this changes in win7) + case 0x7c2e: specific = 0x082e; break; // dsb::dsb-DE::082e + case 0x7c3b: specific = 0x143b; break; // smj::smj-SE::143b + default: + // Note this won't call our Downlevel API with the undesired LOCALE_ILANGUAGE + retVal = GetLocaleInfoW(lcid, LOCALE_ILANGUAGE | LOCALE_RETURN_NUMBER, (LPWSTR)&specific, sizeof(specific)/sizeof(WCHAR)); + break; + } + + if (retVal > 0) + { + retVal = LCIDToLocaleName(specific, lpLocaleName, cchLocaleName, 0); + if (retVal > 0) + return retVal; + } + + // If we didn't have a specific, then use the locale name passed in + if (!pSpecific) + { + pSpecific = (PWSTR)lpNameToResolve; + } + + // Copy our string to the output & return + int size = (int)(wcslen(pSpecific) + 1); + if (size > cchLocaleName) return 0; + memcpy(lpLocaleName, pSpecific, size * sizeof(WCHAR)); + return size; + } + + + BOOL GetThreadPreferredUILanguages(__in DWORD dwFlags, + __out PULONG pulNumLanguages, + __out_ecount_opt(*pcchLanguagesBuffer) PWSTR pwszLanguagesBuffer, + __inout PULONG pcchLanguagesBuffer) + { + const WCHAR str[]=W("\0"); + ULONG nBufSize=*pcchLanguagesBuffer; + *pcchLanguagesBuffer=NumItems(str); + + if (nBufSize == 0 && pwszLanguagesBuffer == NULL) + { + return TRUE; + } + + if(nBufSize<NumItems(str)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + *pulNumLanguages=0; + memcpy(pwszLanguagesBuffer,str,sizeof(str)); + return TRUE; + + } + +} +#endif // ENABLE_DOWNLEVEL_FOR_NLS + + diff --git a/src/utilcode/dyncrt/.gitmirror b/src/utilcode/dyncrt/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/utilcode/dyncrt/.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/utilcode/dyncrt/CMakeLists.txt b/src/utilcode/dyncrt/CMakeLists.txt new file mode 100644 index 0000000000..ceb0cff1f4 --- /dev/null +++ b/src/utilcode/dyncrt/CMakeLists.txt @@ -0,0 +1,7 @@ + +if(CLR_CMAKE_PLATFORM_UNIX) + add_library(utilcode STATIC ${UTILCODE_SOURCES}) + add_dependencies(utilcode CoreClrPal) +else(CLR_CMAKE_PLATFORM_UNIX) + add_library(utilcode STATIC ${UTILCODE_SOURCES}) +endif(CLR_CMAKE_PLATFORM_UNIX)
\ No newline at end of file diff --git a/src/utilcode/dyncrt/dyncrt.nativeproj b/src/utilcode/dyncrt/dyncrt.nativeproj new file mode 100644 index 0000000000..50a1fb7880 --- /dev/null +++ b/src/utilcode/dyncrt/dyncrt.nativeproj @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + <!--*****************************************************--> + <!--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\src\Utilcode\utilcode.settings.targets" /> + <PropertyGroup Label="Globals"> + <SccProjectName>SAK</SccProjectName> + <SccAuxPath>SAK</SccAuxPath> + <SccLocalPath>SAK</SccLocalPath> + <SccProvider>SAK</SccProvider> + </PropertyGroup> + <!--Leaf project Properties--> + <PropertyGroup> + <BuildCoreBinaries>true</BuildCoreBinaries> + <BuildSysBinaries>true</BuildSysBinaries> + <OutputName>utilcode</OutputName> + <!--WARNING: This is not a valid MSBuild property. Ensure that you place BUILD_PRODUCES macro in your SOURCES file after you have generated the stub version using KBC--> + <BuildProduces>utilcode.lib</BuildProduces> + </PropertyGroup> + <!--Leaf Project Items--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" /> +</Project> diff --git a/src/utilcode/ex.cpp b/src/utilcode/ex.cpp new file mode 100644 index 0000000000..af2b4edcb2 --- /dev/null +++ b/src/utilcode/ex.cpp @@ -0,0 +1,2121 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// + +// +// --------------------------------------------------------------------------- +// Ex.cpp +// --------------------------------------------------------------------------- + +#include "stdafx.h" +#include "string.h" +#include "ex.h" +#include "holder.h" + +// error codes +#include "corerror.h" + +#include "../dlls/mscorrc/resource.h" + +#include "olectl.h" + +#include "corexcep.h" + +#define MAX_EXCEPTION_MSG 200 + +// Set if fatal error (like stack overflow or out of memory) occured in this process. +GVAL_IMPL_INIT(HRESULT, g_hrFatalError, S_OK); + +// Helper function to get an exception object from outside the exception. In +// the CLR, it may be from the Thread object. Non-CLR users have no thread object, +// and it will do nothing. +void GetLastThrownObjectExceptionFromThread(void **ppvException); + +Exception *Exception::g_OOMException = NULL; + +// avoid global constructors +static BYTE g_OOMExceptionInstance[sizeof(OutOfMemoryException)]; + +Exception * Exception::GetOOMException() +{ + LIMITED_METHOD_CONTRACT; + + if (!g_OOMException) { + // Create a local copy on the stack and then copy it over to the static instance. + // This avoids race conditions caused by multiple initializations of vtable in the constructor + + OutOfMemoryException local(TRUE); // Construct a "preallocated" instance. + memcpy(&g_OOMExceptionInstance, &local, sizeof(OutOfMemoryException)); + + g_OOMException = (OutOfMemoryException*)&g_OOMExceptionInstance; + } + + return g_OOMException; +} + +/*virtual*/ Exception *OutOfMemoryException::Clone() +{ + LIMITED_METHOD_CONTRACT; + + return GetOOMException(); +} + +//------------------------------------------------------------------------------ +void Exception::Delete(Exception* pvMemory) +{ + CONTRACTL + { + GC_NOTRIGGER; + NOTHROW; + SO_TOLERANT; + SUPPORTS_DAC_HOST_ONLY; // Exceptions aren't currently marshalled by DAC - just used in the host + } + CONTRACTL_END; + + if ((pvMemory == 0) || pvMemory->IsPreallocatedException()) + { + return; + } + + ::delete((Exception *) pvMemory); +} + +void Exception::GetMessage(SString &result) +{ + WRAPPER_NO_CONTRACT; + + return GenerateTopLevelHRExceptionMessage(GetHR(), result); +} + +void HRMsgException::GetMessage(SString &result) +{ + WRAPPER_NO_CONTRACT; + + if (m_msg.IsEmpty()) + HRException::GetMessage(result); + else + result = m_msg; +} + +Exception *Exception::Clone() +{ + CONTRACTL + { + GC_NOTRIGGER; + THROWS; + } + CONTRACTL_END; + + NewHolder<Exception> retExcep(CloneHelper()); + if (m_innerException) + { + retExcep->m_innerException = m_innerException->Clone(); + } + + retExcep.SuppressRelease(); + return retExcep; +} + +Exception *Exception::CloneHelper() +{ + StackSString s; + GetMessage(s); + return new HRMsgException(GetHR(), s); +} + +Exception *Exception::DomainBoundClone() +{ + CONTRACTL + { + // Because we may call DomainBoundCloneHelper() of ObjrefException or CLRLastThrownObjectException + // this should be GC_TRIGGERS, but we can not include EE contracts in Utilcode. + THROWS; + } + CONTRACTL_END; + + NewHolder<Exception> retExcep(DomainBoundCloneHelper()); + if (m_innerException) + { + retExcep->m_innerException = m_innerException->DomainBoundClone(); + } + + retExcep.SuppressRelease(); + return retExcep; +} + +BOOL Exception::IsTerminal() +{ + CONTRACTL + { + GC_NOTRIGGER; + NOTHROW; + + // CLRException::GetHR() can eventually call BaseDomain::CreateHandle(), + // which can indirectly cause a lock if we get a miss in the handle table + // cache (TableCacheMissOnAlloc). Since CLRException::GetHR() is virtual, + // SCAN won't find this for you (though 40 minutes of one of the sql stress + // tests will :-)) + CAN_TAKE_LOCK; + } + CONTRACTL_END; + + HRESULT hr = GetHR(); + return (COR_E_THREADABORTED == hr); +} + +BOOL Exception::IsTransient() +{ + WRAPPER_NO_CONTRACT; + + return IsTransient(GetHR()); +} + +/* static */ +BOOL Exception::IsTransient(HRESULT hr) +{ + LIMITED_METHOD_CONTRACT; + + return (hr == COR_E_THREADABORTED + || hr == COR_E_THREADINTERRUPTED + || hr == COR_E_THREADSTOP + || hr == COR_E_APPDOMAINUNLOADED + || hr == E_OUTOFMEMORY + || hr == HRESULT_FROM_WIN32(ERROR_COMMITMENT_LIMIT) // ran out of room in pagefile + || hr == HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY) + || hr == (HRESULT)STATUS_NO_MEMORY + || hr == COR_E_STACKOVERFLOW + || hr == MSEE_E_ASSEMBLYLOADINPROGRESS); +} + +//------------------------------------------------------------------------------ +// Functions to manage the preallocated exceptions. +// Virtual +BOOL Exception::IsPreallocatedException() +{ // Most exceptions can't be preallocated. If they can be, their class + // should provide a virtual override of this function. + return FALSE; +} + +BOOL Exception::IsPreallocatedOOMException() +{ // This is the preallocated OOM if it is preallocated and is OOM. + return IsPreallocatedException() && (GetInstanceType() == OutOfMemoryException::GetType()); +} + +//------------------------------------------------------------------------------ +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +LPCSTR Exception::GetHRSymbolicName(HRESULT hr) +{ + LIMITED_METHOD_CONTRACT; + +#define CASE_HRESULT(hrname) case hrname: return #hrname; + + switch (hr) + { + CASE_HRESULT(S_OK)// 0x00000000L + CASE_HRESULT(S_FALSE)// 0x00000001L + + CASE_HRESULT(E_UNEXPECTED)// 0x8000FFFFL + CASE_HRESULT(E_NOTIMPL)// 0x80004001L + CASE_HRESULT(E_OUTOFMEMORY)// 0x8007000EL + CASE_HRESULT(E_INVALIDARG)// 0x80070057L + CASE_HRESULT(E_NOINTERFACE)// 0x80004002L + CASE_HRESULT(E_POINTER)// 0x80004003L + CASE_HRESULT(E_HANDLE)// 0x80070006L + CASE_HRESULT(E_ABORT)// 0x80004004L + CASE_HRESULT(E_FAIL)// 0x80004005L + CASE_HRESULT(E_ACCESSDENIED)// 0x80070005L + +#ifdef FEATURE_COMINTEROP + CASE_HRESULT(CO_E_INIT_TLS)// 0x80004006L + CASE_HRESULT(CO_E_INIT_SHARED_ALLOCATOR)// 0x80004007L + CASE_HRESULT(CO_E_INIT_MEMORY_ALLOCATOR)// 0x80004008L + CASE_HRESULT(CO_E_INIT_CLASS_CACHE)// 0x80004009L + CASE_HRESULT(CO_E_INIT_RPC_CHANNEL)// 0x8000400AL + CASE_HRESULT(CO_E_INIT_TLS_SET_CHANNEL_CONTROL)// 0x8000400BL + CASE_HRESULT(CO_E_INIT_TLS_CHANNEL_CONTROL)// 0x8000400CL + CASE_HRESULT(CO_E_INIT_UNACCEPTED_USER_ALLOCATOR)// 0x8000400DL + CASE_HRESULT(CO_E_INIT_SCM_MUTEX_EXISTS)// 0x8000400EL + CASE_HRESULT(CO_E_INIT_SCM_FILE_MAPPING_EXISTS)// 0x8000400FL + CASE_HRESULT(CO_E_INIT_SCM_MAP_VIEW_OF_FILE)// 0x80004010L + CASE_HRESULT(CO_E_INIT_SCM_EXEC_FAILURE)// 0x80004011L + CASE_HRESULT(CO_E_INIT_ONLY_SINGLE_THREADED)// 0x80004012L + +// ****************** +// FACILITY_ITF +// ****************** + + CASE_HRESULT(OLE_E_OLEVERB)// 0x80040000L + CASE_HRESULT(OLE_E_ADVF)// 0x80040001L + CASE_HRESULT(OLE_E_ENUM_NOMORE)// 0x80040002L + CASE_HRESULT(OLE_E_ADVISENOTSUPPORTED)// 0x80040003L + CASE_HRESULT(OLE_E_NOCONNECTION)// 0x80040004L + CASE_HRESULT(OLE_E_NOTRUNNING)// 0x80040005L + CASE_HRESULT(OLE_E_NOCACHE)// 0x80040006L + CASE_HRESULT(OLE_E_BLANK)// 0x80040007L + CASE_HRESULT(OLE_E_CLASSDIFF)// 0x80040008L + CASE_HRESULT(OLE_E_CANT_GETMONIKER)// 0x80040009L + CASE_HRESULT(OLE_E_CANT_BINDTOSOURCE)// 0x8004000AL + CASE_HRESULT(OLE_E_STATIC)// 0x8004000BL + CASE_HRESULT(OLE_E_PROMPTSAVECANCELLED)// 0x8004000CL + CASE_HRESULT(OLE_E_INVALIDRECT)// 0x8004000DL + CASE_HRESULT(OLE_E_WRONGCOMPOBJ)// 0x8004000EL + CASE_HRESULT(OLE_E_INVALIDHWND)// 0x8004000FL + CASE_HRESULT(OLE_E_NOT_INPLACEACTIVE)// 0x80040010L + CASE_HRESULT(OLE_E_CANTCONVERT)// 0x80040011L + CASE_HRESULT(OLE_E_NOSTORAGE)// 0x80040012L + CASE_HRESULT(DV_E_FORMATETC)// 0x80040064L + CASE_HRESULT(DV_E_DVTARGETDEVICE)// 0x80040065L + CASE_HRESULT(DV_E_STGMEDIUM)// 0x80040066L + CASE_HRESULT(DV_E_STATDATA)// 0x80040067L + CASE_HRESULT(DV_E_LINDEX)// 0x80040068L + CASE_HRESULT(DV_E_TYMED)// 0x80040069L + CASE_HRESULT(DV_E_CLIPFORMAT)// 0x8004006AL + CASE_HRESULT(DV_E_DVASPECT)// 0x8004006BL + CASE_HRESULT(DV_E_DVTARGETDEVICE_SIZE)// 0x8004006CL + CASE_HRESULT(DV_E_NOIVIEWOBJECT)// 0x8004006DL + CASE_HRESULT(DRAGDROP_E_NOTREGISTERED)// 0x80040100L + CASE_HRESULT(DRAGDROP_E_ALREADYREGISTERED)// 0x80040101L + CASE_HRESULT(DRAGDROP_E_INVALIDHWND)// 0x80040102L + CASE_HRESULT(CLASS_E_NOAGGREGATION)// 0x80040110L + CASE_HRESULT(CLASS_E_CLASSNOTAVAILABLE)// 0x80040111L + CASE_HRESULT(VIEW_E_DRAW)// 0x80040140L + CASE_HRESULT(REGDB_E_READREGDB)// 0x80040150L + CASE_HRESULT(REGDB_E_WRITEREGDB)// 0x80040151L + CASE_HRESULT(REGDB_E_KEYMISSING)// 0x80040152L + CASE_HRESULT(REGDB_E_INVALIDVALUE)// 0x80040153L + CASE_HRESULT(REGDB_E_CLASSNOTREG)// 0x80040154L + CASE_HRESULT(CACHE_E_NOCACHE_UPDATED)// 0x80040170L + CASE_HRESULT(OLEOBJ_E_NOVERBS)// 0x80040180L + CASE_HRESULT(INPLACE_E_NOTUNDOABLE)// 0x800401A0L + CASE_HRESULT(INPLACE_E_NOTOOLSPACE)// 0x800401A1L + CASE_HRESULT(CONVERT10_E_OLESTREAM_GET)// 0x800401C0L + CASE_HRESULT(CONVERT10_E_OLESTREAM_PUT)// 0x800401C1L + CASE_HRESULT(CONVERT10_E_OLESTREAM_FMT)// 0x800401C2L + CASE_HRESULT(CONVERT10_E_OLESTREAM_BITMAP_TO_DIB)// 0x800401C3L + CASE_HRESULT(CONVERT10_E_STG_FMT)// 0x800401C4L + CASE_HRESULT(CONVERT10_E_STG_NO_STD_STREAM)// 0x800401C5L + CASE_HRESULT(CONVERT10_E_STG_DIB_TO_BITMAP)// 0x800401C6L + CASE_HRESULT(CLIPBRD_E_CANT_OPEN)// 0x800401D0L + CASE_HRESULT(CLIPBRD_E_CANT_EMPTY)// 0x800401D1L + CASE_HRESULT(CLIPBRD_E_CANT_SET)// 0x800401D2L + CASE_HRESULT(CLIPBRD_E_BAD_DATA)// 0x800401D3L + CASE_HRESULT(CLIPBRD_E_CANT_CLOSE)// 0x800401D4L + CASE_HRESULT(MK_E_CONNECTMANUALLY)// 0x800401E0L + CASE_HRESULT(MK_E_EXCEEDEDDEADLINE)// 0x800401E1L + CASE_HRESULT(MK_E_NEEDGENERIC)// 0x800401E2L + CASE_HRESULT(MK_E_UNAVAILABLE)// 0x800401E3L + CASE_HRESULT(MK_E_SYNTAX)// 0x800401E4L + CASE_HRESULT(MK_E_NOOBJECT)// 0x800401E5L + CASE_HRESULT(MK_E_INVALIDEXTENSION)// 0x800401E6L + CASE_HRESULT(MK_E_INTERMEDIATEINTERFACENOTSUPPORTED)// 0x800401E7L + CASE_HRESULT(MK_E_NOTBINDABLE)// 0x800401E8L + CASE_HRESULT(MK_E_NOTBOUND)// 0x800401E9L + CASE_HRESULT(MK_E_CANTOPENFILE)// 0x800401EAL + CASE_HRESULT(MK_E_MUSTBOTHERUSER)// 0x800401EBL + CASE_HRESULT(MK_E_NOINVERSE)// 0x800401ECL + CASE_HRESULT(MK_E_NOSTORAGE)// 0x800401EDL + CASE_HRESULT(MK_E_NOPREFIX)// 0x800401EEL + CASE_HRESULT(MK_E_ENUMERATION_FAILED)// 0x800401EFL + CASE_HRESULT(CO_E_NOTINITIALIZED)// 0x800401F0L + CASE_HRESULT(CO_E_ALREADYINITIALIZED)// 0x800401F1L + CASE_HRESULT(CO_E_CANTDETERMINECLASS)// 0x800401F2L + CASE_HRESULT(CO_E_CLASSSTRING)// 0x800401F3L + CASE_HRESULT(CO_E_IIDSTRING)// 0x800401F4L + CASE_HRESULT(CO_E_APPNOTFOUND)// 0x800401F5L + CASE_HRESULT(CO_E_APPSINGLEUSE)// 0x800401F6L + CASE_HRESULT(CO_E_ERRORINAPP)// 0x800401F7L + CASE_HRESULT(CO_E_DLLNOTFOUND)// 0x800401F8L + CASE_HRESULT(CO_E_ERRORINDLL)// 0x800401F9L + CASE_HRESULT(CO_E_WRONGOSFORAPP)// 0x800401FAL + CASE_HRESULT(CO_E_OBJNOTREG)// 0x800401FBL + CASE_HRESULT(CO_E_OBJISREG)// 0x800401FCL + CASE_HRESULT(CO_E_OBJNOTCONNECTED)// 0x800401FDL + CASE_HRESULT(CO_E_APPDIDNTREG)// 0x800401FEL + CASE_HRESULT(CO_E_RELEASED)// 0x800401FFL + + CASE_HRESULT(OLE_S_USEREG)// 0x00040000L + CASE_HRESULT(OLE_S_STATIC)// 0x00040001L + CASE_HRESULT(OLE_S_MAC_CLIPFORMAT)// 0x00040002L + CASE_HRESULT(DRAGDROP_S_DROP)// 0x00040100L + CASE_HRESULT(DRAGDROP_S_CANCEL)// 0x00040101L + CASE_HRESULT(DRAGDROP_S_USEDEFAULTCURSORS)// 0x00040102L + CASE_HRESULT(DATA_S_SAMEFORMATETC)// 0x00040130L + CASE_HRESULT(VIEW_S_ALREADY_FROZEN)// 0x00040140L + CASE_HRESULT(CACHE_S_FORMATETC_NOTSUPPORTED)// 0x00040170L + CASE_HRESULT(CACHE_S_SAMECACHE)// 0x00040171L + CASE_HRESULT(CACHE_S_SOMECACHES_NOTUPDATED)// 0x00040172L + CASE_HRESULT(OLEOBJ_S_INVALIDVERB)// 0x00040180L + CASE_HRESULT(OLEOBJ_S_CANNOT_DOVERB_NOW)// 0x00040181L + CASE_HRESULT(OLEOBJ_S_INVALIDHWND)// 0x00040182L + CASE_HRESULT(INPLACE_S_TRUNCATED)// 0x000401A0L + CASE_HRESULT(CONVERT10_S_NO_PRESENTATION)// 0x000401C0L + CASE_HRESULT(MK_S_REDUCED_TO_SELF)// 0x000401E2L + CASE_HRESULT(MK_S_ME)// 0x000401E4L + CASE_HRESULT(MK_S_HIM)// 0x000401E5L + CASE_HRESULT(MK_S_US)// 0x000401E6L + CASE_HRESULT(MK_S_MONIKERALREADYREGISTERED)// 0x000401E7L + +// ****************** +// FACILITY_WINDOWS +// ****************** + + CASE_HRESULT(CO_E_CLASS_CREATE_FAILED)// 0x80080001L + CASE_HRESULT(CO_E_SCM_ERROR)// 0x80080002L + CASE_HRESULT(CO_E_SCM_RPC_FAILURE)// 0x80080003L + CASE_HRESULT(CO_E_BAD_PATH)// 0x80080004L + CASE_HRESULT(CO_E_SERVER_EXEC_FAILURE)// 0x80080005L + CASE_HRESULT(CO_E_OBJSRV_RPC_FAILURE)// 0x80080006L + CASE_HRESULT(MK_E_NO_NORMALIZED)// 0x80080007L + CASE_HRESULT(CO_E_SERVER_STOPPING)// 0x80080008L + CASE_HRESULT(MEM_E_INVALID_ROOT)// 0x80080009L + CASE_HRESULT(MEM_E_INVALID_LINK)// 0x80080010L + CASE_HRESULT(MEM_E_INVALID_SIZE)// 0x80080011L + +// ****************** +// FACILITY_DISPATCH +// ****************** + + CASE_HRESULT(DISP_E_UNKNOWNINTERFACE)// 0x80020001L + CASE_HRESULT(DISP_E_MEMBERNOTFOUND)// 0x80020003L + CASE_HRESULT(DISP_E_PARAMNOTFOUND)// 0x80020004L + CASE_HRESULT(DISP_E_TYPEMISMATCH)// 0x80020005L + CASE_HRESULT(DISP_E_UNKNOWNNAME)// 0x80020006L + CASE_HRESULT(DISP_E_NONAMEDARGS)// 0x80020007L + CASE_HRESULT(DISP_E_BADVARTYPE)// 0x80020008L + CASE_HRESULT(DISP_E_EXCEPTION)// 0x80020009L + CASE_HRESULT(DISP_E_OVERFLOW)// 0x8002000AL + CASE_HRESULT(DISP_E_BADINDEX)// 0x8002000BL + CASE_HRESULT(DISP_E_UNKNOWNLCID)// 0x8002000CL + CASE_HRESULT(DISP_E_ARRAYISLOCKED)// 0x8002000DL + CASE_HRESULT(DISP_E_BADPARAMCOUNT)// 0x8002000EL + CASE_HRESULT(DISP_E_PARAMNOTOPTIONAL)// 0x8002000FL + CASE_HRESULT(DISP_E_BADCALLEE)// 0x80020010L + CASE_HRESULT(DISP_E_NOTACOLLECTION)// 0x80020011L + CASE_HRESULT(TYPE_E_BUFFERTOOSMALL)// 0x80028016L + CASE_HRESULT(TYPE_E_INVDATAREAD)// 0x80028018L + CASE_HRESULT(TYPE_E_UNSUPFORMAT)// 0x80028019L + CASE_HRESULT(TYPE_E_REGISTRYACCESS)// 0x8002801CL + CASE_HRESULT(TYPE_E_LIBNOTREGISTERED)// 0x8002801DL + CASE_HRESULT(TYPE_E_UNDEFINEDTYPE)// 0x80028027L + CASE_HRESULT(TYPE_E_QUALIFIEDNAMEDISALLOWED)// 0x80028028L + CASE_HRESULT(TYPE_E_INVALIDSTATE)// 0x80028029L + CASE_HRESULT(TYPE_E_WRONGTYPEKIND)// 0x8002802AL + CASE_HRESULT(TYPE_E_ELEMENTNOTFOUND)// 0x8002802BL + CASE_HRESULT(TYPE_E_AMBIGUOUSNAME)// 0x8002802CL + CASE_HRESULT(TYPE_E_NAMECONFLICT)// 0x8002802DL + CASE_HRESULT(TYPE_E_UNKNOWNLCID)// 0x8002802EL + CASE_HRESULT(TYPE_E_DLLFUNCTIONNOTFOUND)// 0x8002802FL + CASE_HRESULT(TYPE_E_BADMODULEKIND)// 0x800288BDL + CASE_HRESULT(TYPE_E_SIZETOOBIG)// 0x800288C5L + CASE_HRESULT(TYPE_E_DUPLICATEID)// 0x800288C6L + CASE_HRESULT(TYPE_E_INVALIDID)// 0x800288CFL + CASE_HRESULT(TYPE_E_TYPEMISMATCH)// 0x80028CA0L + CASE_HRESULT(TYPE_E_OUTOFBOUNDS)// 0x80028CA1L + CASE_HRESULT(TYPE_E_IOERROR)// 0x80028CA2L + CASE_HRESULT(TYPE_E_CANTCREATETMPFILE)// 0x80028CA3L + CASE_HRESULT(TYPE_E_CANTLOADLIBRARY)// 0x80029C4AL + CASE_HRESULT(TYPE_E_INCONSISTENTPROPFUNCS)// 0x80029C83L + CASE_HRESULT(TYPE_E_CIRCULARTYPE)// 0x80029C84L + +// ****************** +// FACILITY_STORAGE +// ****************** + + CASE_HRESULT(STG_E_INVALIDFUNCTION)// 0x80030001L + CASE_HRESULT(STG_E_FILENOTFOUND)// 0x80030002L + CASE_HRESULT(STG_E_PATHNOTFOUND)// 0x80030003L + CASE_HRESULT(STG_E_TOOMANYOPENFILES)// 0x80030004L + CASE_HRESULT(STG_E_ACCESSDENIED)// 0x80030005L + CASE_HRESULT(STG_E_INVALIDHANDLE)// 0x80030006L + CASE_HRESULT(STG_E_INSUFFICIENTMEMORY)// 0x80030008L + CASE_HRESULT(STG_E_INVALIDPOINTER)// 0x80030009L + CASE_HRESULT(STG_E_NOMOREFILES)// 0x80030012L + CASE_HRESULT(STG_E_DISKISWRITEPROTECTED)// 0x80030013L + CASE_HRESULT(STG_E_SEEKERROR)// 0x80030019L + CASE_HRESULT(STG_E_WRITEFAULT)// 0x8003001DL + CASE_HRESULT(STG_E_READFAULT)// 0x8003001EL + CASE_HRESULT(STG_E_SHAREVIOLATION)// 0x80030020L + CASE_HRESULT(STG_E_LOCKVIOLATION)// 0x80030021L + CASE_HRESULT(STG_E_FILEALREADYEXISTS)// 0x80030050L + CASE_HRESULT(STG_E_INVALIDPARAMETER)// 0x80030057L + CASE_HRESULT(STG_E_MEDIUMFULL)// 0x80030070L + CASE_HRESULT(STG_E_ABNORMALAPIEXIT)// 0x800300FAL + CASE_HRESULT(STG_E_INVALIDHEADER)// 0x800300FBL + CASE_HRESULT(STG_E_INVALIDNAME)// 0x800300FCL + CASE_HRESULT(STG_E_UNKNOWN)// 0x800300FDL + CASE_HRESULT(STG_E_UNIMPLEMENTEDFUNCTION)// 0x800300FEL + CASE_HRESULT(STG_E_INVALIDFLAG)// 0x800300FFL + CASE_HRESULT(STG_E_INUSE)// 0x80030100L + CASE_HRESULT(STG_E_NOTCURRENT)// 0x80030101L + CASE_HRESULT(STG_E_REVERTED)// 0x80030102L + CASE_HRESULT(STG_E_CANTSAVE)// 0x80030103L + CASE_HRESULT(STG_E_OLDFORMAT)// 0x80030104L + CASE_HRESULT(STG_E_OLDDLL)// 0x80030105L + CASE_HRESULT(STG_E_SHAREREQUIRED)// 0x80030106L + CASE_HRESULT(STG_E_NOTFILEBASEDSTORAGE)// 0x80030107L + CASE_HRESULT(STG_S_CONVERTED)// 0x00030200L + +// ****************** +// FACILITY_RPC +// ****************** + + CASE_HRESULT(RPC_E_CALL_REJECTED)// 0x80010001L + CASE_HRESULT(RPC_E_CALL_CANCELED)// 0x80010002L + CASE_HRESULT(RPC_E_CANTPOST_INSENDCALL)// 0x80010003L + CASE_HRESULT(RPC_E_CANTCALLOUT_INASYNCCALL)// 0x80010004L + CASE_HRESULT(RPC_E_CANTCALLOUT_INEXTERNALCALL)// 0x80010005L + CASE_HRESULT(RPC_E_CONNECTION_TERMINATED)// 0x80010006L + CASE_HRESULT(RPC_E_SERVER_DIED)// 0x80010007L + CASE_HRESULT(RPC_E_CLIENT_DIED)// 0x80010008L + CASE_HRESULT(RPC_E_INVALID_DATAPACKET)// 0x80010009L + CASE_HRESULT(RPC_E_CANTTRANSMIT_CALL)// 0x8001000AL + CASE_HRESULT(RPC_E_CLIENT_CANTMARSHAL_DATA)// 0x8001000BL + CASE_HRESULT(RPC_E_CLIENT_CANTUNMARSHAL_DATA)// 0x8001000CL + CASE_HRESULT(RPC_E_SERVER_CANTMARSHAL_DATA)// 0x8001000DL + CASE_HRESULT(RPC_E_SERVER_CANTUNMARSHAL_DATA)// 0x8001000EL + CASE_HRESULT(RPC_E_INVALID_DATA)// 0x8001000FL + CASE_HRESULT(RPC_E_INVALID_PARAMETER)// 0x80010010L + CASE_HRESULT(RPC_E_CANTCALLOUT_AGAIN)// 0x80010011L + CASE_HRESULT(RPC_E_SERVER_DIED_DNE)// 0x80010012L + CASE_HRESULT(RPC_E_SYS_CALL_FAILED)// 0x80010100L + CASE_HRESULT(RPC_E_OUT_OF_RESOURCES)// 0x80010101L + CASE_HRESULT(RPC_E_ATTEMPTED_MULTITHREAD)// 0x80010102L + CASE_HRESULT(RPC_E_NOT_REGISTERED)// 0x80010103L + CASE_HRESULT(RPC_E_FAULT)// 0x80010104L + CASE_HRESULT(RPC_E_SERVERFAULT)// 0x80010105L + CASE_HRESULT(RPC_E_CHANGED_MODE)// 0x80010106L + CASE_HRESULT(RPC_E_INVALIDMETHOD)// 0x80010107L + CASE_HRESULT(RPC_E_DISCONNECTED)// 0x80010108L + CASE_HRESULT(RPC_E_RETRY)// 0x80010109L + CASE_HRESULT(RPC_E_SERVERCALL_RETRYLATER)// 0x8001010AL + CASE_HRESULT(RPC_E_SERVERCALL_REJECTED)// 0x8001010BL + CASE_HRESULT(RPC_E_INVALID_CALLDATA)// 0x8001010CL + CASE_HRESULT(RPC_E_CANTCALLOUT_ININPUTSYNCCALL)// 0x8001010DL + CASE_HRESULT(RPC_E_WRONG_THREAD)// 0x8001010EL + CASE_HRESULT(RPC_E_THREAD_NOT_INIT)// 0x8001010FL + CASE_HRESULT(RPC_E_UNEXPECTED)// 0x8001FFFFL + +// ****************** +// FACILITY_CTL +// ****************** + + CASE_HRESULT(CTL_E_ILLEGALFUNCTIONCALL) + CASE_HRESULT(CTL_E_OVERFLOW) + CASE_HRESULT(CTL_E_OUTOFMEMORY) + CASE_HRESULT(CTL_E_DIVISIONBYZERO) + CASE_HRESULT(CTL_E_OUTOFSTRINGSPACE) + CASE_HRESULT(CTL_E_OUTOFSTACKSPACE) + CASE_HRESULT(CTL_E_BADFILENAMEORNUMBER) + CASE_HRESULT(CTL_E_FILENOTFOUND) + CASE_HRESULT(CTL_E_BADFILEMODE) + CASE_HRESULT(CTL_E_FILEALREADYOPEN) + CASE_HRESULT(CTL_E_DEVICEIOERROR) + CASE_HRESULT(CTL_E_FILEALREADYEXISTS) + CASE_HRESULT(CTL_E_BADRECORDLENGTH) + CASE_HRESULT(CTL_E_DISKFULL) + CASE_HRESULT(CTL_E_BADRECORDNUMBER) + CASE_HRESULT(CTL_E_BADFILENAME) + CASE_HRESULT(CTL_E_TOOMANYFILES) + CASE_HRESULT(CTL_E_DEVICEUNAVAILABLE) + CASE_HRESULT(CTL_E_PERMISSIONDENIED) + CASE_HRESULT(CTL_E_DISKNOTREADY) + CASE_HRESULT(CTL_E_PATHFILEACCESSERROR) + CASE_HRESULT(CTL_E_PATHNOTFOUND) + CASE_HRESULT(CTL_E_INVALIDPATTERNSTRING) + CASE_HRESULT(CTL_E_INVALIDUSEOFNULL) + CASE_HRESULT(CTL_E_INVALIDFILEFORMAT) + CASE_HRESULT(CTL_E_INVALIDPROPERTYVALUE) + CASE_HRESULT(CTL_E_INVALIDPROPERTYARRAYINDEX) + CASE_HRESULT(CTL_E_SETNOTSUPPORTEDATRUNTIME) + CASE_HRESULT(CTL_E_SETNOTSUPPORTED) + CASE_HRESULT(CTL_E_NEEDPROPERTYARRAYINDEX) + CASE_HRESULT(CTL_E_SETNOTPERMITTED) + CASE_HRESULT(CTL_E_GETNOTSUPPORTEDATRUNTIME) + CASE_HRESULT(CTL_E_GETNOTSUPPORTED) + CASE_HRESULT(CTL_E_PROPERTYNOTFOUND) + CASE_HRESULT(CTL_E_INVALIDCLIPBOARDFORMAT) + CASE_HRESULT(CTL_E_INVALIDPICTURE) + CASE_HRESULT(CTL_E_PRINTERERROR) + CASE_HRESULT(CTL_E_CANTSAVEFILETOTEMP) + CASE_HRESULT(CTL_E_SEARCHTEXTNOTFOUND) + CASE_HRESULT(CTL_E_REPLACEMENTSTOOLONG) +#endif // FEATURE_COMINTEROP + +#ifdef _DEBUG // @todo: do we want to burn strings for this in a free build? + + CASE_HRESULT(CEE_E_ENTRYPOINT) + CASE_HRESULT(CEE_E_CVTRES_NOT_FOUND) + CASE_HRESULT(MSEE_E_LOADLIBFAILED) + CASE_HRESULT(MSEE_E_GETPROCFAILED) + CASE_HRESULT(MSEE_E_MULTCOPIESLOADED) + CASE_HRESULT(COR_E_APPDOMAINUNLOADED) + CASE_HRESULT(COR_E_CANNOTUNLOADAPPDOMAIN) + CASE_HRESULT(MSEE_E_ASSEMBLYLOADINPROGRESS) + CASE_HRESULT(MSEE_E_CANNOTCREATEAPPDOMAIN) + CASE_HRESULT(COR_E_FIXUPSINEXE) + CASE_HRESULT(COR_E_NO_LOADLIBRARY_ALLOWED) + CASE_HRESULT(COR_E_MODULE_HASH_CHECK_FAILED) + CASE_HRESULT(FUSION_E_LOADFROM_BLOCKED) + CASE_HRESULT(FUSION_E_CACHEFILE_FAILED) + CASE_HRESULT(FUSION_E_REF_DEF_MISMATCH) + CASE_HRESULT(FUSION_E_INVALID_PRIVATE_ASM_LOCATION) + CASE_HRESULT(FUSION_E_ASM_MODULE_MISSING) + CASE_HRESULT(FUSION_E_UNEXPECTED_MODULE_FOUND) + CASE_HRESULT(FUSION_E_PRIVATE_ASM_DISALLOWED) + CASE_HRESULT(FUSION_E_SIGNATURE_CHECK_FAILED) + CASE_HRESULT(FUSION_E_DATABASE_ERROR) + CASE_HRESULT(FUSION_E_INVALID_NAME) + CASE_HRESULT(FUSION_E_CODE_DOWNLOAD_DISABLED) + CASE_HRESULT(FUSION_E_UNINSTALL_DISALLOWED) + CASE_HRESULT(CLDB_E_FILE_BADREAD) + CASE_HRESULT(CLDB_E_FILE_BADWRITE) + CASE_HRESULT(CLDB_E_FILE_READONLY) + CASE_HRESULT(CLDB_E_NAME_ERROR) + CASE_HRESULT(CLDB_S_TRUNCATION) + CASE_HRESULT(CLDB_E_TRUNCATION) + CASE_HRESULT(CLDB_E_FILE_OLDVER) + CASE_HRESULT(CLDB_E_RELOCATED) + CASE_HRESULT(CLDB_S_NULL) + CASE_HRESULT(CLDB_E_SMDUPLICATE) + CASE_HRESULT(CLDB_E_NO_DATA) + CASE_HRESULT(CLDB_E_READONLY) + CASE_HRESULT(CLDB_E_INCOMPATIBLE) + CASE_HRESULT(CLDB_E_FILE_CORRUPT) + CASE_HRESULT(CLDB_E_SCHEMA_VERNOTFOUND) + CASE_HRESULT(CLDB_E_BADUPDATEMODE) + CASE_HRESULT(CLDB_E_INDEX_NONULLKEYS) + CASE_HRESULT(CLDB_E_INDEX_DUPLICATE) + CASE_HRESULT(CLDB_E_INDEX_BADTYPE) + CASE_HRESULT(CLDB_E_INDEX_NOTFOUND) + CASE_HRESULT(CLDB_S_INDEX_TABLESCANREQUIRED) + CASE_HRESULT(CLDB_E_RECORD_NOTFOUND) + CASE_HRESULT(CLDB_E_RECORD_OVERFLOW) + CASE_HRESULT(CLDB_E_RECORD_DUPLICATE) + CASE_HRESULT(CLDB_E_RECORD_PKREQUIRED) + CASE_HRESULT(CLDB_E_RECORD_DELETED) + CASE_HRESULT(CLDB_E_RECORD_OUTOFORDER) + CASE_HRESULT(CLDB_E_COLUMN_OVERFLOW) + CASE_HRESULT(CLDB_E_COLUMN_READONLY) + CASE_HRESULT(CLDB_E_COLUMN_SPECIALCOL) + CASE_HRESULT(CLDB_E_COLUMN_PKNONULLS) + CASE_HRESULT(CLDB_E_TABLE_CANTDROP) + CASE_HRESULT(CLDB_E_OBJECT_NOTFOUND) + CASE_HRESULT(CLDB_E_OBJECT_COLNOTFOUND) + CASE_HRESULT(CLDB_E_VECTOR_BADINDEX) + CASE_HRESULT(CLDB_E_TOO_BIG) + CASE_HRESULT(META_E_DUPLICATE) + CASE_HRESULT(META_E_GUID_REQUIRED) + CASE_HRESULT(META_E_TYPEDEF_MISMATCH) + CASE_HRESULT(META_E_MERGE_COLLISION) + CASE_HRESULT(META_E_METHD_NOT_FOUND) + CASE_HRESULT(META_E_FIELD_NOT_FOUND) + CASE_HRESULT(META_S_PARAM_MISMATCH) + CASE_HRESULT(META_E_PARAM_MISMATCH) + CASE_HRESULT(META_E_BADMETADATA) + CASE_HRESULT(META_E_INTFCEIMPL_NOT_FOUND) + CASE_HRESULT(META_E_CLASS_LAYOUT_INCONSISTENT) + CASE_HRESULT(META_E_FIELD_MARSHAL_NOT_FOUND) + CASE_HRESULT(META_E_METHODSEM_NOT_FOUND) + CASE_HRESULT(META_E_EVENT_NOT_FOUND) + CASE_HRESULT(META_E_PROP_NOT_FOUND) + CASE_HRESULT(META_E_BAD_SIGNATURE) + CASE_HRESULT(META_E_BAD_INPUT_PARAMETER) + CASE_HRESULT(META_E_METHDIMPL_INCONSISTENT) + CASE_HRESULT(META_E_MD_INCONSISTENCY) + CASE_HRESULT(META_E_CANNOTRESOLVETYPEREF) + CASE_HRESULT(META_S_DUPLICATE) + CASE_HRESULT(META_E_STRINGSPACE_FULL) + CASE_HRESULT(META_E_UNEXPECTED_REMAP) + CASE_HRESULT(META_E_HAS_UNMARKALL) + CASE_HRESULT(META_E_MUST_CALL_UNMARKALL) + CASE_HRESULT(TLBX_E_CANT_LOAD_MODULE) + CASE_HRESULT(TLBX_E_CANT_LOAD_CLASS) + CASE_HRESULT(TLBX_E_NULL_MODULE) + CASE_HRESULT(TLBX_E_NO_CLSID_KEY) + CASE_HRESULT(TLBX_E_CIRCULAR_EXPORT) + CASE_HRESULT(TLBX_E_CIRCULAR_EXPORT2) + CASE_HRESULT(TLBX_E_CIRCULAR_IMPORT) + CASE_HRESULT(TLBX_E_BAD_NATIVETYPE) + CASE_HRESULT(TLBX_E_BAD_VTABLE) + CASE_HRESULT(TLBX_E_CRM_NON_STATIC) + CASE_HRESULT(TLBX_E_CRM_INVALID_SIG) + CASE_HRESULT(TLBX_E_CLASS_LOAD_EXCEPTION) + CASE_HRESULT(TLBX_E_UNKNOWN_SIGNATURE) + CASE_HRESULT(TLBX_E_REFERENCED_TYPELIB) + CASE_HRESULT(TLBX_S_REFERENCED_TYPELIB) + CASE_HRESULT(TLBX_E_LAYOUT_ERROR) + CASE_HRESULT(TLBX_E_NOTIUNKNOWN) + CASE_HRESULT(TLBX_E_NONVISIBLEVALUECLASS) + CASE_HRESULT(TLBX_E_LPTSTR_NOT_ALLOWED) + CASE_HRESULT(TLBX_E_AUTO_CS_NOT_ALLOWED) + CASE_HRESULT(TLBX_S_NOSTDINTERFACE) + CASE_HRESULT(TLBX_S_DUPLICATE_DISPID) + CASE_HRESULT(TLBX_E_ENUM_VALUE_INVALID) + CASE_HRESULT(TLBX_W_ENUM_VALUE_TOOBIG) + CASE_HRESULT(TLBX_E_DUPLICATE_IID) + CASE_HRESULT(TLBX_E_NO_NESTED_ARRAYS) + CASE_HRESULT(TLBX_E_PARAM_ERROR_NAMED) + CASE_HRESULT(TLBX_E_PARAM_ERROR_UNNAMED) + CASE_HRESULT(TLBX_E_AGNOST_SIGNATURE) + CASE_HRESULT(TLBX_E_CONVERT_FAIL) + CASE_HRESULT(TLBX_W_DUAL_NOT_DISPATCH) + CASE_HRESULT(TLBX_E_BAD_SIGNATURE) + CASE_HRESULT(TLBX_E_ARRAY_NEEDS_NT_FIXED) + CASE_HRESULT(TLBX_E_CLASS_NEEDS_NT_INTF) + CASE_HRESULT(TLBX_E_INVALID_TYPEINFO) + CASE_HRESULT(TLBX_E_INVALID_TYPEINFO_UNNAMED) + CASE_HRESULT(TLBX_E_CTX_NESTED) + CASE_HRESULT(TLBX_E_ERROR_MESSAGE) + CASE_HRESULT(TLBX_E_CANT_SAVE) + CASE_HRESULT(TLBX_W_LIBNOTREGISTERED) + CASE_HRESULT(TLBX_E_CANTLOADLIBRARY) + CASE_HRESULT(TLBX_E_BAD_VT_TYPE) + CASE_HRESULT(TLBX_E_NO_MSCOREE_TLB) + CASE_HRESULT(TLBX_E_BAD_MSCOREE_TLB) + CASE_HRESULT(TLBX_E_TLB_EXCEPTION) + CASE_HRESULT(TLBX_E_MULTIPLE_LCIDS) + CASE_HRESULT(TLBX_I_TYPEINFO_IMPORTED) + CASE_HRESULT(TLBX_E_AMBIGUOUS_RETURN) + CASE_HRESULT(TLBX_E_PROPGET_WITHOUT_RETURN) + CASE_HRESULT(TLBX_E_DUPLICATE_TYPE_NAME) + CASE_HRESULT(TLBX_I_USEIUNKNOWN) + CASE_HRESULT(TLBX_I_UNCONVERTABLE_ARGS) + CASE_HRESULT(TLBX_I_UNCONVERTABLE_FIELD) + CASE_HRESULT(TLBX_I_NONSEQUENTIALSTRUCT) + CASE_HRESULT(TLBX_W_WARNING_MESSAGE) + CASE_HRESULT(TLBX_I_RESOLVEREFFAILED) + CASE_HRESULT(TLBX_E_ASANY) + CASE_HRESULT(TLBX_E_INVALIDLCIDPARAM) + CASE_HRESULT(TLBX_E_LCIDONDISPONLYITF) + CASE_HRESULT(TLBX_E_NONPUBLIC_FIELD) + CASE_HRESULT(TLBX_I_TYPE_EXPORTED) + CASE_HRESULT(TLBX_I_DUPLICATE_DISPID) + CASE_HRESULT(TLBX_E_BAD_NAMES) + CASE_HRESULT(TLBX_E_BITNESS_MISMATCH) + CASE_HRESULT(TLBX_I_REF_TYPE_AS_STRUCT) + CASE_HRESULT(TLBX_E_GENERICINST_SIGNATURE) + CASE_HRESULT(TLBX_E_GENERICPAR_SIGNATURE) + CASE_HRESULT(TLBX_I_GENERIC_TYPE) + CASE_HRESULT(TLBX_W_EXPORTING_AUTO_LAYOUT) + CASE_HRESULT(TLBX_E_TYPED_REF) + CASE_HRESULT(TLBX_W_DEFAULT_INTF_NOT_VISIBLE) + CASE_HRESULT(TLBX_W_NON_INTEGRAL_CA_TYPE) + CASE_HRESULT(TLBX_W_IENUM_CA_ON_IUNK) + CASE_HRESULT(TLBX_E_NO_SAFEHANDLE_ARRAYS) + CASE_HRESULT(TLBX_W_NO_PROPS_IN_EVENTS) + CASE_HRESULT(META_E_CA_INVALID_TARGET) + CASE_HRESULT(META_E_CA_INVALID_VALUE) + CASE_HRESULT(META_E_CA_INVALID_BLOB) + CASE_HRESULT(META_E_CA_REPEATED_ARG) + CASE_HRESULT(META_E_CA_UNKNOWN_ARGUMENT) + CASE_HRESULT(META_E_CA_VARIANT_NYI) + CASE_HRESULT(META_E_CA_ARRAY_NYI) + CASE_HRESULT(META_E_CA_UNEXPECTED_TYPE) + CASE_HRESULT(META_E_CA_INVALID_ARGTYPE) + CASE_HRESULT(META_E_CA_INVALID_ARG_FOR_TYPE) + CASE_HRESULT(META_E_CA_INVALID_UUID) + CASE_HRESULT(META_E_CA_INVALID_MARSHALAS_FIELDS) + CASE_HRESULT(META_E_CA_NT_FIELDONLY) + CASE_HRESULT(META_E_CA_NEGATIVE_PARAMINDEX) + CASE_HRESULT(META_E_CA_NEGATIVE_MULTIPLIER) + CASE_HRESULT(META_E_CA_NEGATIVE_CONSTSIZE) + CASE_HRESULT(META_E_CA_FIXEDSTR_SIZE_REQUIRED) + CASE_HRESULT(META_E_CA_CUSTMARSH_TYPE_REQUIRED) + CASE_HRESULT(META_E_CA_FILENAME_REQUIRED) + CASE_HRESULT(META_E_CA_BAD_FRIENDS_ARGS) + CASE_HRESULT(VLDTR_S_WRN) + CASE_HRESULT(VLDTR_S_ERR) + CASE_HRESULT(VLDTR_S_WRNERR) + CASE_HRESULT(VLDTR_E_RID_OUTOFRANGE) + CASE_HRESULT(VLDTR_E_CDTKN_OUTOFRANGE) + CASE_HRESULT(VLDTR_E_CDRID_OUTOFRANGE) + CASE_HRESULT(VLDTR_E_STRING_INVALID) + CASE_HRESULT(VLDTR_E_GUID_INVALID) + CASE_HRESULT(VLDTR_E_BLOB_INVALID) + CASE_HRESULT(VLDTR_E_MOD_MULTI) + CASE_HRESULT(VLDTR_E_MOD_NULLMVID) + CASE_HRESULT(VLDTR_E_TR_NAMENULL) + CASE_HRESULT(VLDTR_E_TR_DUP) + CASE_HRESULT(VLDTR_E_TD_NAMENULL) + CASE_HRESULT(VLDTR_E_TD_DUPNAME) + CASE_HRESULT(VLDTR_E_TD_DUPGUID) + CASE_HRESULT(VLDTR_E_TD_NOTIFACEOBJEXTNULL) + CASE_HRESULT(VLDTR_E_TD_OBJEXTENDSNONNULL) + CASE_HRESULT(VLDTR_E_TD_EXTENDSSEALED) + CASE_HRESULT(VLDTR_E_TD_DLTNORTSPCL) + CASE_HRESULT(VLDTR_E_TD_RTSPCLNOTDLT) + CASE_HRESULT(VLDTR_E_MI_DECLPRIV) + CASE_HRESULT(VLDTR_E_AS_BADNAME) + CASE_HRESULT(VLDTR_E_FILE_SYSNAME) + CASE_HRESULT(VLDTR_E_MI_BODYSTATIC) + CASE_HRESULT(VLDTR_E_TD_IFACENOTABS) + CASE_HRESULT(VLDTR_E_TD_IFACEPARNOTNIL) + CASE_HRESULT(VLDTR_E_TD_IFACEGUIDNULL) + CASE_HRESULT(VLDTR_E_MI_DECLFINAL) + CASE_HRESULT(VLDTR_E_TD_VTNOTSEAL) + CASE_HRESULT(VLDTR_E_PD_BADFLAGS) + CASE_HRESULT(VLDTR_E_IFACE_DUP) + CASE_HRESULT(VLDTR_E_MR_NAMENULL) + CASE_HRESULT(VLDTR_E_MR_VTBLNAME) + CASE_HRESULT(VLDTR_E_MR_DELNAME) + CASE_HRESULT(VLDTR_E_MR_PARNIL) + CASE_HRESULT(VLDTR_E_MR_BADCALLINGCONV) + CASE_HRESULT(VLDTR_E_MR_NOTVARARG) + CASE_HRESULT(VLDTR_E_MR_NAMEDIFF) + CASE_HRESULT(VLDTR_E_MR_SIGDIFF) + CASE_HRESULT(VLDTR_E_MR_DUP) + CASE_HRESULT(VLDTR_E_CL_TDAUTO) + CASE_HRESULT(VLDTR_E_CL_BADPCKSZ) + CASE_HRESULT(VLDTR_E_CL_DUP) + CASE_HRESULT(VLDTR_E_FL_BADOFFSET) + CASE_HRESULT(VLDTR_E_FL_TDNIL) + CASE_HRESULT(VLDTR_E_FL_NOCL) + CASE_HRESULT(VLDTR_E_FL_TDNOTEXPLCT) + CASE_HRESULT(VLDTR_E_FL_FLDSTATIC) + CASE_HRESULT(VLDTR_E_FL_DUP) + CASE_HRESULT(VLDTR_E_MODREF_NAMENULL) + CASE_HRESULT(VLDTR_E_MODREF_DUP) + CASE_HRESULT(VLDTR_E_TR_BADSCOPE) + CASE_HRESULT(VLDTR_E_TD_NESTEDNOENCL) + CASE_HRESULT(VLDTR_E_TD_EXTTRRES) + CASE_HRESULT(VLDTR_E_SIGNULL) + CASE_HRESULT(VLDTR_E_SIGNODATA) + CASE_HRESULT(VLDTR_E_MD_BADCALLINGCONV) + CASE_HRESULT(VLDTR_E_MD_THISSTATIC) + CASE_HRESULT(VLDTR_E_MD_NOTTHISNOTSTATIC) + CASE_HRESULT(VLDTR_E_MD_NOARGCNT) + CASE_HRESULT(VLDTR_E_SIG_MISSELTYPE) + CASE_HRESULT(VLDTR_E_SIG_MISSTKN) + CASE_HRESULT(VLDTR_E_SIG_TKNBAD) + CASE_HRESULT(VLDTR_E_SIG_MISSFPTR) + CASE_HRESULT(VLDTR_E_SIG_MISSFPTRARGCNT) + CASE_HRESULT(VLDTR_E_SIG_MISSRANK) + CASE_HRESULT(VLDTR_E_SIG_MISSNSIZE) + CASE_HRESULT(VLDTR_E_SIG_MISSSIZE) + CASE_HRESULT(VLDTR_E_SIG_MISSNLBND) + CASE_HRESULT(VLDTR_E_SIG_MISSLBND) + CASE_HRESULT(VLDTR_E_SIG_BADELTYPE) + CASE_HRESULT(VLDTR_E_FD_BADCALLINGCONV) + CASE_HRESULT(VLDTR_E_MD_NAMENULL) + CASE_HRESULT(VLDTR_E_MD_PARNIL) + CASE_HRESULT(VLDTR_E_MD_DUP) + CASE_HRESULT(VLDTR_E_FD_NAMENULL) + CASE_HRESULT(VLDTR_E_FD_PARNIL) + CASE_HRESULT(VLDTR_E_FD_DUP) + CASE_HRESULT(VLDTR_E_AS_MULTI) + CASE_HRESULT(VLDTR_E_AS_NAMENULL) + CASE_HRESULT(VLDTR_E_SIG_TOKTYPEMISMATCH) + CASE_HRESULT(VLDTR_E_CL_TDINTF) + CASE_HRESULT(VLDTR_E_ASOS_OSPLTFRMIDINVAL) + CASE_HRESULT(VLDTR_E_AR_NAMENULL) + CASE_HRESULT(VLDTR_E_TD_ENCLNOTNESTED) + CASE_HRESULT(VLDTR_E_AROS_OSPLTFRMIDINVAL) + CASE_HRESULT(VLDTR_E_FILE_NAMENULL) + CASE_HRESULT(VLDTR_E_CT_NAMENULL) + CASE_HRESULT(VLDTR_E_TD_EXTENDSCHILD) + CASE_HRESULT(VLDTR_E_MAR_NAMENULL) + CASE_HRESULT(VLDTR_E_FILE_DUP) + CASE_HRESULT(VLDTR_E_FILE_NAMEFULLQLFD) + CASE_HRESULT(VLDTR_E_CT_DUP) + CASE_HRESULT(VLDTR_E_MAR_DUP) + CASE_HRESULT(VLDTR_E_MAR_NOTPUBPRIV) + CASE_HRESULT(VLDTR_E_TD_ENUMNOVALUE) + CASE_HRESULT(VLDTR_E_TD_ENUMVALSTATIC) + CASE_HRESULT(VLDTR_E_TD_ENUMVALNOTSN) + CASE_HRESULT(VLDTR_E_TD_ENUMFLDNOTST) + CASE_HRESULT(VLDTR_E_TD_ENUMFLDNOTLIT) + CASE_HRESULT(VLDTR_E_TD_ENUMNOLITFLDS) + CASE_HRESULT(VLDTR_E_TD_ENUMFLDSIGMISMATCH) + CASE_HRESULT(VLDTR_E_TD_ENUMVALNOT1ST) + CASE_HRESULT(VLDTR_E_FD_NOTVALUERTSN) + CASE_HRESULT(VLDTR_E_FD_VALUEPARNOTENUM) + CASE_HRESULT(VLDTR_E_FD_INSTINIFACE) + CASE_HRESULT(VLDTR_E_FD_NOTPUBINIFACE) + CASE_HRESULT(VLDTR_E_FMD_GLOBALNOTPUBPRIVSC) + CASE_HRESULT(VLDTR_E_FMD_GLOBALNOTSTATIC) + CASE_HRESULT(VLDTR_E_FD_GLOBALNORVA) + CASE_HRESULT(VLDTR_E_MD_CTORZERORVA) + CASE_HRESULT(VLDTR_E_FD_MARKEDNOMARSHAL) + CASE_HRESULT(VLDTR_E_FD_MARSHALNOTMARKED) + CASE_HRESULT(VLDTR_E_FD_MARKEDNODEFLT) + CASE_HRESULT(VLDTR_E_FD_DEFLTNOTMARKED) + CASE_HRESULT(VLDTR_E_FMD_MARKEDNOSECUR) + CASE_HRESULT(VLDTR_E_FMD_SECURNOTMARKED) + CASE_HRESULT(VLDTR_E_FMD_PINVOKENOTSTATIC) + CASE_HRESULT(VLDTR_E_FMD_MARKEDNOPINVOKE) + CASE_HRESULT(VLDTR_E_FMD_PINVOKENOTMARKED) + CASE_HRESULT(VLDTR_E_FMD_BADIMPLMAP) + CASE_HRESULT(VLDTR_E_IMAP_BADMODREF) + CASE_HRESULT(VLDTR_E_IMAP_BADMEMBER) + CASE_HRESULT(VLDTR_E_IMAP_BADIMPORTNAME) + CASE_HRESULT(VLDTR_E_IMAP_BADCALLCONV) + CASE_HRESULT(VLDTR_E_FMD_BADACCESSFLAG) + CASE_HRESULT(VLDTR_E_FD_INITONLYANDLITERAL) + CASE_HRESULT(VLDTR_E_FD_LITERALNOTSTATIC) + CASE_HRESULT(VLDTR_E_FMD_RTSNNOTSN) + CASE_HRESULT(VLDTR_E_MD_ABSTPARNOTABST) + CASE_HRESULT(VLDTR_E_MD_NOTSTATABSTININTF) + CASE_HRESULT(VLDTR_E_MD_NOTPUBININTF) + CASE_HRESULT(VLDTR_E_MD_CTORININTF) + CASE_HRESULT(VLDTR_E_MD_GLOBALCTORCCTOR) + CASE_HRESULT(VLDTR_E_MD_CTORSTATIC) + CASE_HRESULT(VLDTR_E_MD_CTORNOTSNRTSN) + CASE_HRESULT(VLDTR_E_MD_CTORVIRT) + CASE_HRESULT(VLDTR_E_MD_CTORABST) + CASE_HRESULT(VLDTR_E_MD_CCTORNOTSTATIC) + CASE_HRESULT(VLDTR_E_MD_ZERORVA) + CASE_HRESULT(VLDTR_E_MD_FINNOTVIRT) + CASE_HRESULT(VLDTR_E_MD_STATANDFINORVIRT) + CASE_HRESULT(VLDTR_E_MD_ABSTANDFINAL) + CASE_HRESULT(VLDTR_E_MD_ABSTANDIMPL) + CASE_HRESULT(VLDTR_E_MD_ABSTANDPINVOKE) + CASE_HRESULT(VLDTR_E_MD_ABSTNOTVIRT) + CASE_HRESULT(VLDTR_E_MD_NOTABSTNOTIMPL) + CASE_HRESULT(VLDTR_E_MD_NOTABSTBADFLAGSRVA) + CASE_HRESULT(VLDTR_E_MD_PRIVSCOPENORVA) + CASE_HRESULT(VLDTR_E_MD_GLOBALABSTORVIRT) + CASE_HRESULT(VLDTR_E_SIG_LONGFORM) + CASE_HRESULT(VLDTR_E_MD_MULTIPLESEMANTICS) + CASE_HRESULT(VLDTR_E_MD_INVALIDSEMANTICS) + CASE_HRESULT(VLDTR_E_MD_SEMANTICSNOTEXIST) + CASE_HRESULT(VLDTR_E_MI_DECLNOTVIRT) + CASE_HRESULT(VLDTR_E_FMD_GLOBALITEM) + CASE_HRESULT(VLDTR_E_MD_MULTSEMANTICFLAGS) + CASE_HRESULT(VLDTR_E_MD_NOSEMANTICFLAGS) + CASE_HRESULT(VLDTR_E_FD_FLDINIFACE) + CASE_HRESULT(VLDTR_E_AS_HASHALGID) + CASE_HRESULT(VLDTR_E_AS_PROCID) + CASE_HRESULT(VLDTR_E_AR_PROCID) + CASE_HRESULT(VLDTR_E_CN_PARENTRANGE) + CASE_HRESULT(VLDTR_E_AS_BADFLAGS) + CASE_HRESULT(VLDTR_E_TR_HASTYPEDEF) + CASE_HRESULT(VLDTR_E_IFACE_BADIMPL) + CASE_HRESULT(VLDTR_E_IFACE_BADIFACE) + CASE_HRESULT(VLDTR_E_TD_SECURNOTMARKED) + CASE_HRESULT(VLDTR_E_TD_MARKEDNOSECUR) + CASE_HRESULT(VLDTR_E_MD_CCTORHASARGS) + CASE_HRESULT(VLDTR_E_CT_BADIMPL) + CASE_HRESULT(VLDTR_E_MI_ALIENBODY) + CASE_HRESULT(VLDTR_E_MD_CCTORCALLCONV) + CASE_HRESULT(VLDTR_E_MI_BADCLASS) + CASE_HRESULT(VLDTR_E_MI_CLASSISINTF) + CASE_HRESULT(VLDTR_E_MI_BADDECL) + CASE_HRESULT(VLDTR_E_MI_BADBODY) + CASE_HRESULT(VLDTR_E_MI_DUP) + CASE_HRESULT(VLDTR_E_FD_BADPARENT) + CASE_HRESULT(VLDTR_E_MD_PARAMOUTOFSEQ) + CASE_HRESULT(VLDTR_E_MD_PARASEQTOOBIG) + CASE_HRESULT(VLDTR_E_MD_PARMMARKEDNOMARSHAL) + CASE_HRESULT(VLDTR_E_MD_PARMMARSHALNOTMARKED) + CASE_HRESULT(VLDTR_E_MD_PARMMARKEDNODEFLT) + CASE_HRESULT(VLDTR_E_MD_PARMDEFLTNOTMARKED) + CASE_HRESULT(VLDTR_E_PR_BADSCOPE) + CASE_HRESULT(VLDTR_E_PR_NONAME) + CASE_HRESULT(VLDTR_E_PR_NOSIG) + CASE_HRESULT(VLDTR_E_PR_DUP) + CASE_HRESULT(VLDTR_E_PR_BADCALLINGCONV) + CASE_HRESULT(VLDTR_E_PR_MARKEDNODEFLT) + CASE_HRESULT(VLDTR_E_PR_DEFLTNOTMARKED) + CASE_HRESULT(VLDTR_E_PR_BADSEMANTICS) + CASE_HRESULT(VLDTR_E_PR_BADMETHOD) + CASE_HRESULT(VLDTR_E_PR_ALIENMETHOD) + CASE_HRESULT(VLDTR_E_CN_BLOBNOTNULL) + CASE_HRESULT(VLDTR_E_CN_BLOBNULL) + CASE_HRESULT(VLDTR_E_EV_BADSCOPE) + CASE_HRESULT(VLDTR_E_EV_NONAME) + CASE_HRESULT(VLDTR_E_EV_DUP) + CASE_HRESULT(VLDTR_E_EV_BADEVTYPE) + CASE_HRESULT(VLDTR_E_EV_EVTYPENOTCLASS) + CASE_HRESULT(VLDTR_E_EV_BADSEMANTICS) + CASE_HRESULT(VLDTR_E_EV_BADMETHOD) + CASE_HRESULT(VLDTR_E_EV_ALIENMETHOD) + CASE_HRESULT(VLDTR_E_EV_NOADDON) + CASE_HRESULT(VLDTR_E_EV_NOREMOVEON) + CASE_HRESULT(VLDTR_E_CT_DUPTDNAME) + CASE_HRESULT(VLDTR_E_MAR_BADOFFSET) + CASE_HRESULT(VLDTR_E_DS_BADOWNER) + CASE_HRESULT(VLDTR_E_DS_BADFLAGS) + CASE_HRESULT(VLDTR_E_DS_NOBLOB) + CASE_HRESULT(VLDTR_E_MAR_BADIMPL) + CASE_HRESULT(VLDTR_E_MR_VARARGCALLINGCONV) + CASE_HRESULT(VLDTR_E_MD_CTORNOTVOID) + CASE_HRESULT(VLDTR_E_EV_FIRENOTVOID) + CASE_HRESULT(VLDTR_E_AS_BADLOCALE) + CASE_HRESULT(VLDTR_E_CN_PARENTTYPE) + CASE_HRESULT(VLDTR_E_SIG_SENTINMETHODDEF) + CASE_HRESULT(VLDTR_E_SIG_SENTMUSTVARARG) + CASE_HRESULT(VLDTR_E_SIG_MULTSENTINELS) + CASE_HRESULT(VLDTR_E_SIG_LASTSENTINEL) + CASE_HRESULT(VLDTR_E_SIG_MISSARG) + CASE_HRESULT(VLDTR_E_SIG_BYREFINFIELD) + CASE_HRESULT(VLDTR_E_MD_SYNCMETHODINVTYPE) + CASE_HRESULT(VLDTR_E_TD_NAMETOOLONG) + CASE_HRESULT(VLDTR_E_AS_PROCDUP) + CASE_HRESULT(VLDTR_E_ASOS_DUP) + CASE_HRESULT(VLDTR_E_MAR_BADFLAGS) + CASE_HRESULT(VLDTR_E_CT_NOTYPEDEFID) + CASE_HRESULT(VLDTR_E_FILE_BADFLAGS) + CASE_HRESULT(VLDTR_E_FILE_NULLHASH) + CASE_HRESULT(VLDTR_E_MOD_NONAME) + CASE_HRESULT(VLDTR_E_MOD_NAMEFULLQLFD) + CASE_HRESULT(VLDTR_E_TD_RTSPCLNOTSPCL) + CASE_HRESULT(VLDTR_E_TD_EXTENDSIFACE) + CASE_HRESULT(VLDTR_E_MD_CTORPINVOKE) + CASE_HRESULT(VLDTR_E_TD_SYSENUMNOTCLASS) + CASE_HRESULT(VLDTR_E_TD_SYSENUMNOTEXTVTYPE) + CASE_HRESULT(VLDTR_E_MI_SIGMISMATCH) + CASE_HRESULT(VLDTR_E_TD_ENUMHASMETHODS) + CASE_HRESULT(VLDTR_E_TD_ENUMIMPLIFACE) + CASE_HRESULT(VLDTR_E_TD_ENUMHASPROP) + CASE_HRESULT(VLDTR_E_TD_ENUMHASEVENT) + CASE_HRESULT(VLDTR_E_TD_BADMETHODLST) + CASE_HRESULT(VLDTR_E_TD_BADFIELDLST) + CASE_HRESULT(VLDTR_E_CN_BADTYPE) + CASE_HRESULT(VLDTR_E_TD_ENUMNOINSTFLD) + CASE_HRESULT(VLDTR_E_TD_ENUMMULINSTFLD) + CASE_HRESULT(VLDTR_E_INTERRUPTED) + CASE_HRESULT(VLDTR_E_NOTINIT) + CASE_HRESULT(VLDTR_E_IFACE_NOTIFACE) + CASE_HRESULT(VLDTR_E_FD_RVAHASNORVA) + CASE_HRESULT(VLDTR_E_FD_RVAHASZERORVA) + CASE_HRESULT(VLDTR_E_MD_RVAANDIMPLMAP) + CASE_HRESULT(VLDTR_E_TD_EXTRAFLAGS) + CASE_HRESULT(VLDTR_E_TD_EXTENDSITSELF) + CASE_HRESULT(VLDTR_E_TD_SYSVTNOTEXTOBJ) + CASE_HRESULT(VLDTR_E_TD_EXTTYPESPEC) + CASE_HRESULT(VLDTR_E_TD_VTNOSIZE) + CASE_HRESULT(VLDTR_E_TD_IFACESEALED) + CASE_HRESULT(VLDTR_E_NC_BADNESTED) + CASE_HRESULT(VLDTR_E_NC_BADENCLOSER) + CASE_HRESULT(VLDTR_E_NC_DUP) + CASE_HRESULT(VLDTR_E_NC_DUPENCLOSER) + CASE_HRESULT(VLDTR_E_FRVA_ZERORVA) + CASE_HRESULT(VLDTR_E_FRVA_BADFIELD) + CASE_HRESULT(VLDTR_E_FRVA_DUPRVA) + CASE_HRESULT(VLDTR_E_FRVA_DUPFIELD) + CASE_HRESULT(VLDTR_E_EP_BADTOKEN) + CASE_HRESULT(VLDTR_E_EP_INSTANCE) + CASE_HRESULT(VLDTR_E_TD_ENUMFLDBADTYPE) + CASE_HRESULT(VLDTR_E_MD_BADRVA) + CASE_HRESULT(VLDTR_E_FD_LITERALNODEFAULT) + CASE_HRESULT(VLDTR_E_IFACE_METHNOTIMPL) + CASE_HRESULT(VLDTR_E_CA_BADPARENT) + CASE_HRESULT(VLDTR_E_CA_BADTYPE) + CASE_HRESULT(VLDTR_E_CA_NOTCTOR) + CASE_HRESULT(VLDTR_E_CA_BADSIG) + CASE_HRESULT(VLDTR_E_CA_NOSIG) + CASE_HRESULT(VLDTR_E_CA_BADPROLOG) + CASE_HRESULT(VLDTR_E_MD_BADLOCALSIGTOK) + CASE_HRESULT(VLDTR_E_MD_BADHEADER) + CASE_HRESULT(VLDTR_E_EP_TOOMANYARGS) + CASE_HRESULT(VLDTR_E_EP_BADRET) + CASE_HRESULT(VLDTR_E_EP_BADARG) + CASE_HRESULT(VLDTR_E_SIG_BADVOID) + CASE_HRESULT(CORDBG_E_UNRECOVERABLE_ERROR) + CASE_HRESULT(CORDBG_E_PROCESS_TERMINATED) + CASE_HRESULT(CORDBG_E_PROCESS_NOT_SYNCHRONIZED) + CASE_HRESULT(CORDBG_E_CLASS_NOT_LOADED) + CASE_HRESULT(CORDBG_E_IL_VAR_NOT_AVAILABLE) + CASE_HRESULT(CORDBG_E_BAD_REFERENCE_VALUE) + CASE_HRESULT(CORDBG_E_FIELD_NOT_AVAILABLE) + CASE_HRESULT(CORDBG_E_NON_NATIVE_FRAME) + CASE_HRESULT(CORDBG_E_NONCONTINUABLE_EXCEPTION) + CASE_HRESULT(CORDBG_E_CODE_NOT_AVAILABLE) + CASE_HRESULT(CORDBG_E_FUNCTION_NOT_IL) + CASE_HRESULT(CORDBG_S_BAD_START_SEQUENCE_POINT) + CASE_HRESULT(CORDBG_S_BAD_END_SEQUENCE_POINT) + CASE_HRESULT(CORDBG_S_INSUFFICIENT_INFO_FOR_SET_IP) + CASE_HRESULT(CORDBG_E_CANT_SET_IP_INTO_FINALLY) + CASE_HRESULT(CORDBG_E_CANT_SET_IP_OUT_OF_FINALLY) + CASE_HRESULT(CORDBG_E_CANT_SET_IP_INTO_CATCH) + CASE_HRESULT(CORDBG_E_SET_IP_NOT_ALLOWED_ON_NONLEAF_FRAME) + CASE_HRESULT(CORDBG_E_SET_IP_IMPOSSIBLE) + CASE_HRESULT(CORDBG_E_FUNC_EVAL_BAD_START_POINT) + CASE_HRESULT(CORDBG_E_INVALID_OBJECT) + CASE_HRESULT(CORDBG_E_FUNC_EVAL_NOT_COMPLETE) + CASE_HRESULT(CORDBG_S_FUNC_EVAL_HAS_NO_RESULT) + CASE_HRESULT(CORDBG_S_VALUE_POINTS_TO_VOID) + CASE_HRESULT(CORDBG_E_INPROC_NOT_IMPL) + CASE_HRESULT(CORDBG_S_FUNC_EVAL_ABORTED) + CASE_HRESULT(CORDBG_E_STATIC_VAR_NOT_AVAILABLE) + CASE_HRESULT(CORDBG_E_OBJECT_IS_NOT_COPYABLE_VALUE_CLASS) + CASE_HRESULT(CORDBG_E_CANT_SETIP_INTO_OR_OUT_OF_FILTER) + CASE_HRESULT(CORDBG_E_CANT_CHANGE_JIT_SETTING_FOR_ZAP_MODULE) + CASE_HRESULT(CORDBG_E_REMOTE_CONNECTION_CONN_RESET) + CASE_HRESULT(CORDBG_E_REMOTE_CONNECTION_KEEP_ALIVE) + CASE_HRESULT(CORDBG_E_REMOTE_CONNECTION_FATAL_ERROR) + CASE_HRESULT(CORDBG_E_CANT_SET_TO_JMC) + CASE_HRESULT(CORDBG_E_BAD_THREAD_STATE) + CASE_HRESULT(CORDBG_E_DEBUGGER_ALREADY_ATTACHED) + CASE_HRESULT(CORDBG_E_SUPERFLOUS_CONTINUE) + CASE_HRESULT(CORDBG_E_SET_VALUE_NOT_ALLOWED_ON_NONLEAF_FRAME) + CASE_HRESULT(CORDBG_E_ENC_EH_MAX_NESTING_LEVEL_CANT_INCREASE) + CASE_HRESULT(CORDBG_E_ENC_MODULE_NOT_ENC_ENABLED) + CASE_HRESULT(CORDBG_E_SET_IP_NOT_ALLOWED_ON_EXCEPTION) + CASE_HRESULT(CORDBG_E_VARIABLE_IS_ACTUALLY_LITERAL) + CASE_HRESULT(CORDBG_E_PROCESS_DETACHED) + CASE_HRESULT(CORDBG_E_ENC_METHOD_SIG_CHANGED) + CASE_HRESULT(CORDBG_E_ENC_METHOD_NO_LOCAL_SIG) + CASE_HRESULT(CORDBG_E_ENC_CANT_ADD_FIELD_TO_VALUE_OR_LAYOUT_CLASS) + CASE_HRESULT(CORDBG_E_ENC_CANT_CHANGE_FIELD) + CASE_HRESULT(CORDBG_E_ENC_CANT_ADD_NON_PRIVATE_MEMBER) + CASE_HRESULT(CORDBG_E_FIELD_NOT_STATIC) + CASE_HRESULT(CORDBG_E_FIELD_NOT_INSTANCE) + CASE_HRESULT(CORDBG_E_ENC_ZAPPED_WITHOUT_ENC) + CASE_HRESULT(CORDBG_E_ENC_BAD_METHOD_INFO) + CASE_HRESULT(CORDBG_E_ENC_JIT_CANT_UPDATE) + CASE_HRESULT(CORDBG_E_ENC_MISSING_CLASS) + CASE_HRESULT(CORDBG_E_ENC_INTERNAL_ERROR) + CASE_HRESULT(CORDBG_E_ENC_HANGING_FIELD) + CASE_HRESULT(CORDBG_E_MODULE_NOT_LOADED) + CASE_HRESULT(CORDBG_E_ENC_CANT_CHANGE_SUPERCLASS) + CASE_HRESULT(CORDBG_E_UNABLE_TO_SET_BREAKPOINT) + CASE_HRESULT(CORDBG_E_DEBUGGING_NOT_POSSIBLE) + CASE_HRESULT(CORDBG_E_KERNEL_DEBUGGER_ENABLED) + CASE_HRESULT(CORDBG_E_KERNEL_DEBUGGER_PRESENT) + CASE_HRESULT(CORDBG_E_HELPER_THREAD_DEAD) + CASE_HRESULT(CORDBG_E_INTERFACE_INHERITANCE_CANT_CHANGE) + CASE_HRESULT(CORDBG_E_INCOMPATIBLE_PROTOCOL) + CASE_HRESULT(CORDBG_E_TOO_MANY_PROCESSES) + CASE_HRESULT(CORDBG_E_INTEROP_NOT_SUPPORTED) + CASE_HRESULT(CORDBG_E_NO_REMAP_BREAKPIONT) + CASE_HRESULT(CORDBG_E_OBJECT_NEUTERED) + CASE_HRESULT(CORPROF_E_FUNCTION_NOT_COMPILED) + CASE_HRESULT(CORPROF_E_DATAINCOMPLETE) + CASE_HRESULT(CORPROF_E_NOT_REJITABLE_METHODS) + CASE_HRESULT(CORPROF_E_CANNOT_UPDATE_METHOD) + CASE_HRESULT(CORPROF_E_FUNCTION_NOT_IL) + CASE_HRESULT(CORPROF_E_NOT_MANAGED_THREAD) + CASE_HRESULT(CORPROF_E_CALL_ONLY_FROM_INIT) + CASE_HRESULT(CORPROF_E_INPROC_NOT_ENABLED) + CASE_HRESULT(CORPROF_E_JITMAPS_NOT_ENABLED) + CASE_HRESULT(CORPROF_E_INPROC_ALREADY_BEGUN) + CASE_HRESULT(CORPROF_E_INPROC_NOT_AVAILABLE) + CASE_HRESULT(CORPROF_E_NOT_YET_AVAILABLE) + CASE_HRESULT(SECURITY_E_XML_TO_ASN_ENCODING) + CASE_HRESULT(SECURITY_E_INCOMPATIBLE_SHARE) + CASE_HRESULT(SECURITY_E_UNVERIFIABLE) + CASE_HRESULT(SECURITY_E_INCOMPATIBLE_EVIDENCE) + CASE_HRESULT(CLDB_E_INTERNALERROR) + CASE_HRESULT(CORSEC_E_DECODE_SET) + CASE_HRESULT(CORSEC_E_ENCODE_SET) + CASE_HRESULT(CORSEC_E_UNSUPPORTED_FORMAT) + CASE_HRESULT(SN_CRYPTOAPI_CALL_FAILED) + CASE_HRESULT(SN_NO_SUITABLE_CSP) + CASE_HRESULT(CORSEC_E_INVALID_ATTR) + CASE_HRESULT(CORSEC_E_POLICY_EXCEPTION) + CASE_HRESULT(CORSEC_E_MIN_GRANT_FAIL) + CASE_HRESULT(CORSEC_E_NO_EXEC_PERM) + CASE_HRESULT(CORSEC_E_XMLSYNTAX) + CASE_HRESULT(CORSEC_E_INVALID_STRONGNAME) + CASE_HRESULT(CORSEC_E_MISSING_STRONGNAME) + CASE_HRESULT(CORSEC_E_CONTAINER_NOT_FOUND) + CASE_HRESULT(CORSEC_E_INVALID_IMAGE_FORMAT) + CASE_HRESULT(CORSEC_E_CRYPTO) + CASE_HRESULT(CORSEC_E_CRYPTO_UNEX_OPER) + CASE_HRESULT(CORSECATTR_E_BAD_ATTRIBUTE) + CASE_HRESULT(CORSECATTR_E_MISSING_CONSTRUCTOR) + CASE_HRESULT(CORSECATTR_E_FAILED_TO_CREATE_PERM) + CASE_HRESULT(CORSECATTR_E_BAD_ACTION_ASM) + CASE_HRESULT(CORSECATTR_E_BAD_ACTION_OTHER) + CASE_HRESULT(CORSECATTR_E_BAD_PARENT) + CASE_HRESULT(CORSECATTR_E_TRUNCATED) + CASE_HRESULT(CORSECATTR_E_BAD_VERSION) + CASE_HRESULT(CORSECATTR_E_BAD_ACTION) + CASE_HRESULT(CORSECATTR_E_NO_SELF_REF) + CASE_HRESULT(CORSECATTR_E_BAD_NONCAS) + CASE_HRESULT(CORSECATTR_E_ASSEMBLY_LOAD_FAILED) + CASE_HRESULT(CORSECATTR_E_ASSEMBLY_LOAD_FAILED_EX) + CASE_HRESULT(CORSECATTR_E_TYPE_LOAD_FAILED) + CASE_HRESULT(CORSECATTR_E_TYPE_LOAD_FAILED_EX) + CASE_HRESULT(CORSECATTR_E_ABSTRACT) + CASE_HRESULT(CORSECATTR_E_UNSUPPORTED_TYPE) + CASE_HRESULT(CORSECATTR_E_UNSUPPORTED_ENUM_TYPE) + CASE_HRESULT(CORSECATTR_E_NO_FIELD) + CASE_HRESULT(CORSECATTR_E_NO_PROPERTY) + CASE_HRESULT(CORSECATTR_E_EXCEPTION) + CASE_HRESULT(CORSECATTR_E_EXCEPTION_HR) + CASE_HRESULT(ISS_E_ISOSTORE) + CASE_HRESULT(ISS_E_OPEN_STORE_FILE) + CASE_HRESULT(ISS_E_OPEN_FILE_MAPPING) + CASE_HRESULT(ISS_E_MAP_VIEW_OF_FILE) + CASE_HRESULT(ISS_E_GET_FILE_SIZE) + CASE_HRESULT(ISS_E_CREATE_MUTEX) + CASE_HRESULT(ISS_E_LOCK_FAILED) + CASE_HRESULT(ISS_E_FILE_WRITE) + CASE_HRESULT(ISS_E_SET_FILE_POINTER) + CASE_HRESULT(ISS_E_CREATE_DIR) + CASE_HRESULT(ISS_E_STORE_NOT_OPEN) + CASE_HRESULT(ISS_E_CORRUPTED_STORE_FILE) + CASE_HRESULT(ISS_E_STORE_VERSION) + CASE_HRESULT(ISS_E_FILE_NOT_MAPPED) + CASE_HRESULT(ISS_E_BLOCK_SIZE_TOO_SMALL) + CASE_HRESULT(ISS_E_ALLOC_TOO_LARGE) + CASE_HRESULT(ISS_E_USAGE_WILL_EXCEED_QUOTA) + CASE_HRESULT(ISS_E_TABLE_ROW_NOT_FOUND) + CASE_HRESULT(ISS_E_DEPRECATE) + CASE_HRESULT(ISS_E_CALLER) + CASE_HRESULT(ISS_E_PATH_LENGTH) + CASE_HRESULT(ISS_E_MACHINE) + CASE_HRESULT(ISS_E_MACHINE_DACL) + CASE_HRESULT(ISS_E_ISOSTORE_END) + CASE_HRESULT(COR_E_APPLICATION) + CASE_HRESULT(COR_E_ARGUMENTOUTOFRANGE) + CASE_HRESULT(COR_E_ARITHMETIC) + CASE_HRESULT(COR_E_ARRAYTYPEMISMATCH) + CASE_HRESULT(COR_E_CONTEXTMARSHAL) + CASE_HRESULT(COR_E_TIMEOUT) + CASE_HRESULT(COR_E_DEVICESNOTSUPPORTED) + CASE_HRESULT(COR_E_DIVIDEBYZERO) + CASE_HRESULT(COR_E_EXCEPTION) + CASE_HRESULT(COR_E_EXECUTIONENGINE) + CASE_HRESULT(COR_E_FIELDACCESS) + CASE_HRESULT(COR_E_FORMAT) + CASE_HRESULT(COR_E_BADIMAGEFORMAT) + CASE_HRESULT(COR_E_ASSEMBLYEXPECTED) + CASE_HRESULT(COR_E_TYPEUNLOADED) + CASE_HRESULT(COR_E_INDEXOUTOFRANGE) + CASE_HRESULT(COR_E_INVALIDOPERATION) + CASE_HRESULT(COR_E_INVALIDPROGRAM) + CASE_HRESULT(COR_E_MEMBERACCESS) + CASE_HRESULT(COR_E_METHODACCESS) + CASE_HRESULT(COR_E_MISSINGFIELD) + CASE_HRESULT(COR_E_MISSINGMANIFESTRESOURCE) + CASE_HRESULT(COR_E_MISSINGMEMBER) + CASE_HRESULT(COR_E_MISSINGMETHOD) + CASE_HRESULT(COR_E_MULTICASTNOTSUPPORTED) + CASE_HRESULT(COR_E_NOTFINITENUMBER) + CASE_HRESULT(COR_E_DUPLICATEWAITOBJECT) + CASE_HRESULT(COR_E_PLATFORMNOTSUPPORTED) + CASE_HRESULT(COR_E_NOTSUPPORTED) + CASE_HRESULT(COR_E_OVERFLOW) + CASE_HRESULT(COR_E_RANK) + CASE_HRESULT(COR_E_REMOTING) + CASE_HRESULT(COR_E_SERVER) + CASE_HRESULT(COR_E_SERVICEDCOMPONENT) + CASE_HRESULT(COR_E_SECURITY) + CASE_HRESULT(COR_E_SERIALIZATION) + CASE_HRESULT(COR_E_STACKOVERFLOW) + CASE_HRESULT(COR_E_SYNCHRONIZATIONLOCK) + CASE_HRESULT(COR_E_SYSTEM) + CASE_HRESULT(COR_E_THREADABORTED) + CASE_HRESULT(COR_E_THREADINTERRUPTED) + CASE_HRESULT(COR_E_THREADSTATE) + CASE_HRESULT(COR_E_THREADSTOP) + CASE_HRESULT(COR_E_TYPEINITIALIZATION) + CASE_HRESULT(COR_E_TYPELOAD) + CASE_HRESULT(COR_E_ENTRYPOINTNOTFOUND) + CASE_HRESULT(COR_E_DLLNOTFOUND) + CASE_HRESULT(COR_E_VERIFICATION) + CASE_HRESULT(COR_E_INVALIDCOMOBJECT) + CASE_HRESULT(COR_E_MARSHALDIRECTIVE) + CASE_HRESULT(COR_E_INVALIDOLEVARIANTTYPE) + CASE_HRESULT(COR_E_SAFEARRAYTYPEMISMATCH) + CASE_HRESULT(COR_E_SAFEARRAYRANKMISMATCH) + CASE_HRESULT(COR_E_INVALIDFILTERCRITERIA) + CASE_HRESULT(COR_E_REFLECTIONTYPELOAD) + CASE_HRESULT(COR_E_TARGET) + CASE_HRESULT(COR_E_TARGETINVOCATION) + CASE_HRESULT(COR_E_CUSTOMATTRIBUTEFORMAT) + CASE_HRESULT(COR_E_ENDOFSTREAM) + CASE_HRESULT(COR_E_FILELOAD) + CASE_HRESULT(COR_E_FILENOTFOUND) + CASE_HRESULT(COR_E_BAD_PATHNAME) + CASE_HRESULT(COR_E_IO) + CASE_HRESULT(COR_E_DIRECTORYNOTFOUND) + CASE_HRESULT(COR_E_PATHTOOLONG) + CASE_HRESULT(COR_E_OBJECTDISPOSED) + CASE_HRESULT(COR_E_NEWER_RUNTIME) + CASE_HRESULT(CLR_E_SHIM_RUNTIMELOAD) + CASE_HRESULT(CLR_E_SHIM_RUNTIMEEXPORT) + CASE_HRESULT(CLR_E_SHIM_INSTALLROOT) + CASE_HRESULT(CLR_E_SHIM_INSTALLCOMP) + CASE_HRESULT(VER_E_HRESULT) + CASE_HRESULT(VER_E_OFFSET) + CASE_HRESULT(VER_E_OPCODE) + CASE_HRESULT(VER_E_OPERAND) + CASE_HRESULT(VER_E_TOKEN) + CASE_HRESULT(VER_E_EXCEPT) + CASE_HRESULT(VER_E_STACK_SLOT) + CASE_HRESULT(VER_E_LOC) + CASE_HRESULT(VER_E_ARG) + CASE_HRESULT(VER_E_FOUND) + CASE_HRESULT(VER_E_EXPECTED) + CASE_HRESULT(VER_E_UNKNOWN_OPCODE) + CASE_HRESULT(VER_E_SIG_CALLCONV) + CASE_HRESULT(VER_E_SIG_ELEMTYPE) + CASE_HRESULT(VER_E_RET_SIG) + CASE_HRESULT(VER_E_FIELD_SIG) + CASE_HRESULT(VER_E_INTERNAL) + CASE_HRESULT(VER_E_STACK_TOO_LARGE) + CASE_HRESULT(VER_E_ARRAY_NAME_LONG) + CASE_HRESULT(VER_E_FALLTHRU) + CASE_HRESULT(VER_E_TRY_GTEQ_END) + CASE_HRESULT(VER_E_TRYEND_GT_CS) + CASE_HRESULT(VER_E_HND_GTEQ_END) + CASE_HRESULT(VER_E_HNDEND_GT_CS) + CASE_HRESULT(VER_E_FLT_GTEQ_CS) + CASE_HRESULT(VER_E_TRY_START) + CASE_HRESULT(VER_E_HND_START) + CASE_HRESULT(VER_E_FLT_START) + CASE_HRESULT(VER_E_TRY_OVERLAP) + CASE_HRESULT(VER_E_TRY_EQ_HND_FIL) + CASE_HRESULT(VER_E_TRY_SHARE_FIN_FAL) + CASE_HRESULT(VER_E_HND_OVERLAP) + CASE_HRESULT(VER_E_HND_EQ) + CASE_HRESULT(VER_E_FIL_OVERLAP) + CASE_HRESULT(VER_E_FIL_EQ) + CASE_HRESULT(VER_E_FIL_CONT_TRY) + CASE_HRESULT(VER_E_FIL_CONT_HND) + CASE_HRESULT(VER_E_FIL_CONT_FIL) + CASE_HRESULT(VER_E_FIL_GTEQ_CS) + CASE_HRESULT(VER_E_FIL_START) + CASE_HRESULT(VER_E_FALLTHRU_EXCEP) + CASE_HRESULT(VER_E_FALLTHRU_INTO_HND) + CASE_HRESULT(VER_E_FALLTHRU_INTO_FIL) + CASE_HRESULT(VER_E_LEAVE) + CASE_HRESULT(VER_E_RETHROW) + CASE_HRESULT(VER_E_ENDFINALLY) + CASE_HRESULT(VER_E_ENDFILTER) + CASE_HRESULT(VER_E_ENDFILTER_MISSING) + CASE_HRESULT(VER_E_BR_INTO_TRY) + CASE_HRESULT(VER_E_BR_INTO_HND) + CASE_HRESULT(VER_E_BR_INTO_FIL) + CASE_HRESULT(VER_E_BR_OUTOF_TRY) + CASE_HRESULT(VER_E_BR_OUTOF_HND) + CASE_HRESULT(VER_E_BR_OUTOF_FIL) + CASE_HRESULT(VER_E_BR_OUTOF_FIN) + CASE_HRESULT(VER_E_RET_FROM_TRY) + CASE_HRESULT(VER_E_RET_FROM_HND) + CASE_HRESULT(VER_E_RET_FROM_FIL) + CASE_HRESULT(VER_E_BAD_JMP_TARGET) + CASE_HRESULT(VER_E_PATH_LOC) + CASE_HRESULT(VER_E_PATH_THIS) + CASE_HRESULT(VER_E_PATH_STACK) + CASE_HRESULT(VER_E_PATH_STACK_DEPTH) + CASE_HRESULT(VER_E_THIS) + CASE_HRESULT(VER_E_THIS_UNINIT_EXCEP) + CASE_HRESULT(VER_E_THIS_UNINIT_STORE) + CASE_HRESULT(VER_E_THIS_UNINIT_RET) + CASE_HRESULT(VER_E_THIS_UNINIT_V_RET) + CASE_HRESULT(VER_E_THIS_UNINIT_BR) + CASE_HRESULT(VER_E_LDFTN_CTOR) + CASE_HRESULT(VER_E_STACK_NOT_EQ) + CASE_HRESULT(VER_E_STACK_UNEXPECTED) + CASE_HRESULT(VER_E_STACK_EXCEPTION) + CASE_HRESULT(VER_E_STACK_OVERFLOW) + CASE_HRESULT(VER_E_STACK_UNDERFLOW) + CASE_HRESULT(VER_E_STACK_EMPTY) + CASE_HRESULT(VER_E_STACK_UNINIT) + CASE_HRESULT(VER_E_STACK_I_I4_I8) + CASE_HRESULT(VER_E_STACK_R_R4_R8) + CASE_HRESULT(VER_E_STACK_NO_R_I8) + CASE_HRESULT(VER_E_STACK_NUMERIC) + CASE_HRESULT(VER_E_STACK_OBJREF) + CASE_HRESULT(VER_E_STACK_P_OBJREF) + CASE_HRESULT(VER_E_STACK_BYREF) + CASE_HRESULT(VER_E_STACK_METHOD) + CASE_HRESULT(VER_E_STACK_ARRAY_SD) + CASE_HRESULT(VER_E_STACK_VALCLASS) + CASE_HRESULT(VER_E_STACK_P_VALCLASS) + CASE_HRESULT(VER_E_STACK_NO_VALCLASS) + CASE_HRESULT(VER_E_LOC_DEAD) + CASE_HRESULT(VER_E_LOC_NUM) + CASE_HRESULT(VER_E_ARG_NUM) + CASE_HRESULT(VER_E_TOKEN_RESOLVE) + CASE_HRESULT(VER_E_TOKEN_TYPE) + CASE_HRESULT(VER_E_TOKEN_TYPE_MEMBER) + CASE_HRESULT(VER_E_TOKEN_TYPE_FIELD) + CASE_HRESULT(VER_E_TOKEN_TYPE_SIG) + CASE_HRESULT(VER_E_UNVERIFIABLE) + CASE_HRESULT(VER_E_LDSTR_OPERAND) + CASE_HRESULT(VER_E_RET_PTR_TO_STACK) + CASE_HRESULT(VER_E_RET_VOID) + CASE_HRESULT(VER_E_RET_MISSING) + CASE_HRESULT(VER_E_RET_EMPTY) + CASE_HRESULT(VER_E_RET_UNINIT) + CASE_HRESULT(VER_E_ARRAY_ACCESS) + CASE_HRESULT(VER_E_ARRAY_V_STORE) + CASE_HRESULT(VER_E_ARRAY_SD) + CASE_HRESULT(VER_E_ARRAY_SD_PTR) + CASE_HRESULT(VER_E_ARRAY_FIELD) + CASE_HRESULT(VER_E_ARGLIST) + CASE_HRESULT(VER_E_VALCLASS) + CASE_HRESULT(VER_E_METHOD_ACCESS) + CASE_HRESULT(VER_E_FIELD_ACCESS) + CASE_HRESULT(VER_E_DEAD) + CASE_HRESULT(VER_E_FIELD_STATIC) + CASE_HRESULT(VER_E_FIELD_NO_STATIC) + CASE_HRESULT(VER_E_ADDR) + CASE_HRESULT(VER_E_ADDR_BYREF) + CASE_HRESULT(VER_E_ADDR_LITERAL) + CASE_HRESULT(VER_E_INITONLY) + CASE_HRESULT(VER_E_THROW) + CASE_HRESULT(VER_E_CALLVIRT_VALCLASS) + CASE_HRESULT(VER_E_CALL_SIG) + CASE_HRESULT(VER_E_CALL_STATIC) + CASE_HRESULT(VER_E_CTOR) + CASE_HRESULT(VER_E_CTOR_VIRT) + CASE_HRESULT(VER_E_CTOR_OR_SUPER) + CASE_HRESULT(VER_E_CTOR_MUL_INIT) + CASE_HRESULT(VER_E_SIG) + CASE_HRESULT(VER_E_SIG_ARRAY) + CASE_HRESULT(VER_E_SIG_ARRAY_PTR) + CASE_HRESULT(VER_E_SIG_ARRAY_BYREF) + CASE_HRESULT(VER_E_SIG_ELEM_PTR) + CASE_HRESULT(VER_E_SIG_VARARG) + CASE_HRESULT(VER_E_SIG_VOID) + CASE_HRESULT(VER_E_SIG_BYREF_BYREF) + CASE_HRESULT(VER_E_CODE_SIZE_ZERO) + CASE_HRESULT(VER_E_BAD_VARARG) + CASE_HRESULT(VER_E_TAIL_CALL) + CASE_HRESULT(VER_E_TAIL_BYREF) + CASE_HRESULT(VER_E_TAIL_RET) + CASE_HRESULT(VER_E_TAIL_RET_VOID) + CASE_HRESULT(VER_E_TAIL_RET_TYPE) + CASE_HRESULT(VER_E_TAIL_STACK_EMPTY) + CASE_HRESULT(VER_E_METHOD_END) + CASE_HRESULT(VER_E_BAD_BRANCH) + CASE_HRESULT(VER_E_FIN_OVERLAP) + CASE_HRESULT(VER_E_LEXICAL_NESTING) + CASE_HRESULT(VER_E_VOLATILE) + CASE_HRESULT(VER_E_UNALIGNED) + CASE_HRESULT(VER_E_INNERMOST_FIRST) + CASE_HRESULT(VER_E_CALLI_VIRTUAL) + CASE_HRESULT(VER_E_CALL_ABSTRACT) + CASE_HRESULT(VER_E_STACK_UNEXP_ARRAY) + CASE_HRESULT(VER_E_NOT_IN_GC_HEAP) + CASE_HRESULT(VER_E_TRY_N_EMPTY_STACK) + CASE_HRESULT(VER_E_DLGT_CTOR) + CASE_HRESULT(VER_E_DLGT_BB) + CASE_HRESULT(VER_E_DLGT_PATTERN) + CASE_HRESULT(VER_E_DLGT_LDFTN) + CASE_HRESULT(VER_E_FTN_ABSTRACT) + CASE_HRESULT(VER_E_SIG_C_VC) + CASE_HRESULT(VER_E_SIG_VC_C) + CASE_HRESULT(VER_E_BOX_PTR_TO_STACK) + CASE_HRESULT(VER_E_SIG_BYREF_TB_AH) + CASE_HRESULT(VER_E_SIG_ARRAY_TB_AH) + CASE_HRESULT(VER_E_ENDFILTER_STACK) + CASE_HRESULT(VER_E_DLGT_SIG_I) + CASE_HRESULT(VER_E_DLGT_SIG_O) + CASE_HRESULT(VER_E_RA_PTR_TO_STACK) + CASE_HRESULT(VER_E_CATCH_VALUE_TYPE) + CASE_HRESULT(VER_E_CATCH_BYREF) + CASE_HRESULT(VER_E_FIL_PRECEED_HND) + CASE_HRESULT(VER_E_LDVIRTFTN_STATIC) + CASE_HRESULT(VER_E_CALLVIRT_STATIC) + CASE_HRESULT(VER_E_INITLOCALS) + CASE_HRESULT(VER_E_BR_TO_EXCEPTION) + CASE_HRESULT(VER_E_CALL_CTOR) + CASE_HRESULT(VER_E_BAD_PE) + CASE_HRESULT(VER_E_BAD_MD) + CASE_HRESULT(VER_E_BAD_APPDOMAIN) + CASE_HRESULT(VER_E_TYPELOAD) + CASE_HRESULT(VER_E_PE_LOAD) + CASE_HRESULT(VER_E_WRITE_RVA_STATIC) + CASE_HRESULT(CORDBG_E_THREAD_NOT_SCHEDULED) +#endif + + default: + return NULL; + } +} +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + + +// --------------------------------------------------------------------------- +// HRException class. Implements exception API for exceptions from HRESULTS +// --------------------------------------------------------------------------- + +HRESULT HRException::GetHR() +{ + LIMITED_METHOD_DAC_CONTRACT; + return m_hr; +} + +// --------------------------------------------------------------------------- +// COMException class. - moved to COMEx.cpp +// --------------------------------------------------------------------------- + +// --------------------------------------------------------------------------- +// SEHException class. Implements exception API for SEH exception info +// --------------------------------------------------------------------------- + +HRESULT SEHException::GetHR() +{ + LIMITED_METHOD_DAC_CONTRACT; + + if (IsComPlusException(&m_exception)) // EE exception + return (HRESULT) m_exception.ExceptionInformation[0]; + else + return m_exception.ExceptionCode; +} + +IErrorInfo *SEHException::GetErrorInfo() +{ + LIMITED_METHOD_CONTRACT; + return NULL; +} + +void SEHException::GetMessage(SString &string) +{ + WRAPPER_NO_CONTRACT; + + if (IsComPlusException(&m_exception)) // EE exception + { + GenerateTopLevelHRExceptionMessage(GetHR(), string); + } + else + { + if (m_exception.ExceptionCode != 0) + { + string.Printf("Exception code 0x%.8x", m_exception.ExceptionCode); + } + else + { + // If we don't have a valid exception code, then give a generic message that's a little nicer than + // "code 0x00000000". + string.Printf("Unknown exception"); + } + } +} + +//============================================================================== +// DelegatingException class. Implements exception API for "foreign" exceptions. +//============================================================================== + +DelegatingException::DelegatingException() + : m_delegatedException((Exception*)DELEGATE_NOT_YET_SET) +{ + LIMITED_METHOD_DAC_CONTRACT; +} // DelegatingException::DelegatingException() + +//------------------------------------------------------------------------------ +DelegatingException::~DelegatingException() +{ + WRAPPER_NO_CONTRACT; + + // If there is a valid delegate pointer (inited and non-NULL), delete it. + if (IsDelegateValid()) + Delete(m_delegatedException); + + // Avoid confusion. + m_delegatedException = NULL; +} // DelegatingException::~DelegatingException() + +//------------------------------------------------------------------------------ +// Retrieve the delegating exception, or get one from the Thread, or get NULL. +Exception* DelegatingException::GetDelegate() +{ + WRAPPER_NO_CONTRACT; + + // If we haven't gotten the exception pointer before.. + if (!IsDelegateSet()) + { + // .. get it now. NULL in case there isn't one and we take default action. + m_delegatedException = NULL; + GetLastThrownObjectExceptionFromThread(reinterpret_cast<void**>(&m_delegatedException)); + } + + return m_delegatedException; +} // Exception* DelegatingException::GetDelegate() + +//------------------------------------------------------------------------------ +// Virtual overrides +HRESULT DelegatingException::GetHR() +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC_HOST_ONLY; + + // Retrieve any delegating exception. + Exception *pDelegate = GetDelegate(); + + // If there is a delegate exception, defer to it. Otherwise, + // default to E_FAIL. + return pDelegate ? pDelegate->GetHR() : E_FAIL; + +} // HRESULT DelegatingException::GetHR() + +//------------------------------------------------------------------------------ +IErrorInfo *DelegatingException::GetErrorInfo() +{ + WRAPPER_NO_CONTRACT; + + // Retrieve any delegating exception. + Exception *pDelegate = GetDelegate(); + + // If there is a delegate exception, defer to it. Otherwise, + // default to NULL. + return pDelegate ? pDelegate->GetErrorInfo() : NULL; + +} // IErrorInfo *DelegatingException::GetErrorInfo() + +//------------------------------------------------------------------------------ +void DelegatingException::GetMessage(SString &result) +{ + WRAPPER_NO_CONTRACT; + + // Retrieve any delegating exception. + Exception *pDelegate = GetDelegate(); + + // If there is a delegate exception, defer to it. Otherwise, + // default to a generic message. + if (pDelegate) + { + pDelegate->GetMessage(result); + } + else + { + // If we don't have a valid exception code, then give a generic message + // that's a little nicer than "code 0x00000000". + result.Printf("Unknown exception"); + } +} // void DelegatingException::GetMessage() + +//------------------------------------------------------------------------------ +Exception *DelegatingException::Clone() +{ + WRAPPER_NO_CONTRACT; + + // Clone the base exception, this will also take care of cloning the inner + // exception if there is one. + NewHolder<DelegatingException> retExcep((DelegatingException*)Exception::Clone()); + + // If there is a valid delegating exception... + if (IsDelegateValid()) + { // ... clone it. + retExcep->m_delegatedException = m_delegatedException->Clone(); + } + else + { // ... but if there is not, just copy -- either NULL or DELEGATE_NOT_YET_SET + retExcep->m_delegatedException = m_delegatedException; + } + + retExcep.SuppressRelease(); + return retExcep; +} // virtual Exception *DelegatingException::Clone() + +//============================================================================== +//============================================================================== + +void DECLSPEC_NORETURN ThrowHR(HRESULT hr) +{ + WRAPPER_NO_CONTRACT; + + STRESS_LOG1(LF_EH, LL_INFO100, "ThrowHR: HR = %x\n", hr); + + if (hr == E_OUTOFMEMORY) + ThrowOutOfMemory(); + + // Catchers assume only failing hresults + _ASSERTE(FAILED(hr)); + if (hr == S_OK) + hr = E_FAIL; + + EX_THROW(HRException, (hr)); +} + +void DECLSPEC_NORETURN ThrowHR(HRESULT hr, SString const &msg) +{ + STATIC_CONTRACT_SO_TOLERANT; + WRAPPER_NO_CONTRACT; + + STRESS_LOG1(LF_EH, LL_INFO100, "ThrowHR: HR = %x\n", hr); + + if (hr == E_OUTOFMEMORY) + ThrowOutOfMemory(); + + // Catchers assume only failing hresults + _ASSERTE(FAILED(hr)); + if (hr == S_OK) + hr = E_FAIL; + + EX_THROW(HRMsgException, (hr, msg)); +} + +void DECLSPEC_NORETURN ThrowHR(HRESULT hr, UINT uText) +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC_HOST_ONLY; + + if (hr == E_OUTOFMEMORY) + ThrowOutOfMemory(); + + // Catchers assume only failing hresults + _ASSERTE(FAILED(hr)); + if (hr == S_OK) + hr = E_FAIL; + + SString sExceptionText; + + // We won't check the return value here. If it fails, we'll just + // throw the HR + sExceptionText.LoadResource(CCompRC::Error, uText); + + EX_THROW(HRMsgException, (hr, sExceptionText)); +} + +void DECLSPEC_NORETURN ThrowWin32(DWORD err) +{ + STATIC_CONTRACT_SO_TOLERANT; + WRAPPER_NO_CONTRACT; + if (err == ERROR_NOT_ENOUGH_MEMORY) + { + ThrowOutOfMemory(); + } + else + { + ThrowHR(HRESULT_FROM_WIN32(err)); + } +} + +void DECLSPEC_NORETURN ThrowLastError() +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + ThrowWin32(GetLastError()); +} + +void DECLSPEC_NORETURN ThrowOutOfMemory() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + SO_TOLERANT; + SUPPORTS_DAC; + } + CONTRACTL_END; + +#ifndef DACCESS_COMPILE + + g_hrFatalError = COR_E_OUTOFMEMORY; + + // Regular CLR builds - throw our pre-created OOM exception object + PAL_CPP_THROW(Exception *, Exception::GetOOMException()); + +#else + + // DAC builds - raise a DacError + DacError(E_OUTOFMEMORY); + + // DacError always throws but isn't marked DECLSPEC_NORETURN so we have to + // tell the compiler that this code is unreachable. We could mark DacError + // (and DacNotImpl) as DECLSPEC_NORETURN, but then we've have to update a + // lot of code where we do something afterwards. Also, due to inlining, + // we'd sometimes have to change functions which call functions that only + // call DacNotImpl. I have these changes in a bbpack and some of them look + // nice, but I'm not sure if it's worth the risk of merge conflicts. + UNREACHABLE(); + +#endif +} + +#include "corexcep.h" + +#ifdef FEATURE_STACK_PROBE +void DECLSPEC_NORETURN ThrowStackOverflow() +{ + CONTRACTL + { + // This should be throws... But it isn't because a SO doesn't technically + // fall into the same THROW/NOTHROW conventions as the rest of the contract + // infrastructure. + NOTHROW; + + GC_NOTRIGGER; + SO_TOLERANT; + SUPPORTS_DAC; + } + CONTRACTL_END; + + //g_hrFatalError=COR_E_STACKOVERFLOW; + PTR_INT32 p_ghrFatalError = dac_cast<PTR_INT32>(GVAL_ADDR(g_hrFatalError)); + _ASSERTE(p_ghrFatalError != NULL); + *p_ghrFatalError = COR_E_STACKOVERFLOW; + + + RaiseException(EXCEPTION_SOFTSO, 0, 0, NULL); + UNREACHABLE(); +} +#endif + +void DECLSPEC_NORETURN ThrowMessage(LPCSTR string, ...) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACTL_END; + + StackSString message; + + va_list args; + va_start(args, string); + message.VPrintf(string, args); + va_end(args); + + EX_THROW(HRMsgException, (E_FAIL, message)); +} + + +//-------------------------------------------------------------------------------- +// Helper for EX_THROW_WITH_INNER() +// +// Clones an exception into the current domain. Also handles special cases for +// OOM and other stuff. Making this a function so we don't inline all this logic +// every place we call EX_THROW_WITH_INNER. +// +// If the "inner" is a transient exception such as OOM or ThreadAbort, this function +// will just throw it rather than allow it to be wrapped in another exception. +//-------------------------------------------------------------------------------- +Exception *ExThrowWithInnerHelper(Exception *inner) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + } + CONTRACTL_END + + // Yes, NULL is a legal case. Makes it easier to author uniform helpers for + // both wrapped and normal exceptions. + if (inner == NULL) + { + return NULL; + } + + if (inner == Exception::GetOOMException()) + { + // We don't want to do allocations if we're already throwing an OOM! + PAL_CPP_THROW(Exception*, inner); + } + + inner = inner->DomainBoundClone(); + + // It isn't useful to wrap OOMs and StackOverflows in other exceptions. Just throw them now. + // + if (inner->IsTransient()) + { + PAL_CPP_THROW(Exception*, inner); + } + return inner; +} + +#ifdef _DEBUG + +#pragma warning(disable: 4748) +#pragma optimize("", off) +#pragma warning(disable: 4748) + + +void ExThrowTrap(const char *fcn, const char *file, int line, const char *szType, HRESULT hr, const char *args) +{ + SUPPORTS_DAC; + return; +} +#endif + + + + +//------------------------------------------------------------------------------------------- +// This routine will generate the most descriptive possible error message for an hresult. +// It will generate at minimum the hex value. It will also try to generate the symbolic name +// (E_POINTER) and the friendly description (from the message tables.) +// +// bNoGeekStuff suppresses hex HR codes. Use this sparingly as most error strings generated by the +// CLR are aimed at developers, not end-users. +//------------------------------------------------------------------------------------------- +void GetHRMsg(HRESULT hr, SString &result, BOOL bNoGeekStuff/* = FALSE*/) +{ + CONTRACTL + { + GC_NOTRIGGER; + THROWS; + } + CONTRACTL_END; + + result = W(""); // Make sure this routine isn't an inadvertent data-leak exploit! + + + + SString strDescr; + BOOL fHaveDescr = FALSE; + + if (FAILED(hr) && HRESULT_FACILITY(hr) == FACILITY_URT && HRESULT_CODE(hr) < MAX_URT_HRESULT_CODE) + { + fHaveDescr = strDescr.LoadResource(CCompRC::Error, MSG_FOR_URT_HR(hr)); + } + else + { + DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM; + dwFlags |= FORMAT_MESSAGE_MAX_WIDTH_MASK; + +#if FEATURE_USE_LCID + fHaveDescr = strDescr.FormatMessage(dwFlags, 0, hr, LANG_USER_DEFAULT); +#else + fHaveDescr = strDescr.FormatMessage(dwFlags, 0, hr, 0); +#endif + } + + LPCSTR name = Exception::GetHRSymbolicName(hr); + + // If we can't get a resource string, print the hresult regardless. + if (!fHaveDescr) + { + bNoGeekStuff = FALSE; + } + + if (fHaveDescr) + { + result.Append(strDescr); + } + + + if (!bNoGeekStuff) + { + if (fHaveDescr) + { + result.Append(W(" (")); + } + + SString strExcepFromHR; + strExcepFromHR.LoadResource(CCompRC::Error, IDS_EE_EXCEPTION_FROM_HRESULT); + result.Append(strExcepFromHR); + result.AppendPrintf(W("0x%.8X"), hr); + if (name != NULL) + { + result.AppendPrintf(W(" (%S)"), name); + } + + + if (fHaveDescr) + { + result.Append(W(")")); + } + + } +} + + +//------------------------------------------------------------------------------------------- +// Similar to GetHRMsg but phrased for top-level exception message. +//------------------------------------------------------------------------------------------- +void GenerateTopLevelHRExceptionMessage(HRESULT hresult, SString &result) +{ + CONTRACTL + { + GC_NOTRIGGER; + THROWS; + } + CONTRACTL_END; + + result = W(""); // Make sure this routine isn't an inadvertent data-leak exploit! + + GetHRMsg(hresult, result); +} + +#if !defined(DACCESS_COMPILE) + +void GetCurrentExceptionPointers(PEXCEPTION_POINTERS pExceptionInfo) +{ + WRAPPER_NO_CONTRACT; + + PEXCEPTION_RECORD pRecord = (PEXCEPTION_RECORD)ClrFlsGetValue(TlsIdx_PEXCEPTION_RECORD); + PCONTEXT pContext = (PCONTEXT)ClrFlsGetValue(TlsIdx_PCONTEXT); + + pExceptionInfo->ContextRecord = pContext; + pExceptionInfo->ExceptionRecord = pRecord; + +#ifdef _DEBUG + if (pRecord != NULL) + { + _ASSERTE ((PVOID)(pRecord) > (PVOID)(&pRecord)); + } +#endif +} +#endif // !defined(DACCESS_COMPILE) + +DWORD GetCurrentExceptionCode() +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC_HOST_ONLY; + + return (DWORD)(size_t)ClrFlsGetValue(TlsIdx_EXCEPTION_CODE); +} + +bool IsCurrentExceptionSO() +{ + WRAPPER_NO_CONTRACT; + DWORD exceptionCode = GetCurrentExceptionCode(); + return IsSOExceptionCode(exceptionCode); +} + +bool IsSOExceptionCode(DWORD exceptionCode) +{ + if (exceptionCode == STATUS_STACK_OVERFLOW +#ifdef FEATURE_STACK_PROBE + || exceptionCode == EXCEPTION_SOFTSO +#endif + ) + { + return TRUE; + } + else + return FALSE; +} + + +//=========================================================================================== +// These abstractions hide the difference between legacy desktop CLR's (that don't support +// side-by-side-inproc and rely on a fixed SEH code to identify managed exceptions) and +// new CLR's that support side-by-side inproc. +// +// The new CLR's use a different set of SEH codes to avoid conflicting with the legacy CLR's. +// In addition, to distinguish between EH's raised by different inproc instances of the CLR, +// the module handle of the owning CLR is stored in ExceptionRecord.ExceptionInformation[4]. +// +// (Note: all existing SEH's use either only slot [0] or no slots at all. We are leaving +// slots [1] thru [3] open for future expansion.) +//=========================================================================================== + +// Is this exception code one of the special CLR-specific SEH codes that participate in the +// instance-tagging scheme? +BOOL IsInstanceTaggedSEHCode(DWORD dwExceptionCode) +{ + LIMITED_METHOD_DAC_CONTRACT; + + return dwExceptionCode == EXCEPTION_COMPLUS; +} + +// This set of overloads generates the NumberParameters and ExceptionInformation[] array to +// pass to RaiseException(). +// +// Parameters: +// exceptionArgs: a fixed-size array of size INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE. +// This will get filled in by this function. (The module handle goes +// in the last slot if this is a side-by-side-inproc enabled build.) +// +// exceptionArg1... up to four arguments that go in slots [0]..[3]. These depends +// the specific requirements of your exception code. +// +// Returns: +// The NumberParameters to pass to RaiseException(). +// +// Basically, this is either INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE or the count of your +// fixed arguments depending on whether this tagged-SEH-enabled build. +// +// This function is not permitted to fail. + +// (the existing system can support more overloads up to 4 fixed arguments but we don't need them at this time.) + +static DWORD MarkAsThrownByUsWorker(UINT numArgs, /*out*/ ULONG_PTR exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE], ULONG_PTR arg0 = 0) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + + _ASSERTE(numArgs < INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE); + FillMemory(exceptionArgs, sizeof(ULONG_PTR) * INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE, 0); + + exceptionArgs[0] = arg0; + +#if !defined(FEATURE_UTILCODE_NO_DEPENDENCIES) && (defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE)) + exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE - 1] = (ULONG_PTR) (GetCLRModule()); +#endif // !defined(FEATURE_UTILCODE_NO_DEPENDENCIES) && defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE) + + return INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE; +} + +DWORD MarkAsThrownByUs(/*out*/ ULONG_PTR exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE]) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + return MarkAsThrownByUsWorker(0, exceptionArgs); +} + +DWORD MarkAsThrownByUs(/*out*/ ULONG_PTR exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE], ULONG_PTR arg0) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + return MarkAsThrownByUsWorker(1, exceptionArgs, arg0); +} + +// Given an exception record, checks if it's exception code matches a specific exception code +// *and* whether it was tagged by the calling instance of the CLR. +// +// If this is a non-tagged-SEH-enabled build, it is blindly assumed to be tagged by the +// calling instance of the CLR. +BOOL WasThrownByUs(const EXCEPTION_RECORD *pcER, DWORD dwExceptionCode) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_SO_TOLERANT; + STATIC_CONTRACT_SUPPORTS_DAC; + + _ASSERTE(IsInstanceTaggedSEHCode(dwExceptionCode)); + _ASSERTE(pcER != NULL); + if (dwExceptionCode != pcER->ExceptionCode) + { + return FALSE; + } + + if (pcER->NumberParameters != INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE) + { + return FALSE; + } +#if!defined(FEATURE_UTILCODE_NO_DEPENDENCIES) && (defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE)) + if ( ((ULONG_PTR)(GetCLRModule())) != pcER->ExceptionInformation[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE - 1] ) + { + return FALSE; + } + return TRUE; +#else // !(!defined(FEATURE_UTILCODE_NO_DEPENDENCIES) && (defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE))) + return FALSE; +#endif // !defined(FEATURE_UTILCODE_NO_DEPENDENCIES) && defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE) +} + + + +//----------------------------------------------------------------------------------- +// The following group wraps the basic abstracts specifically for EXCEPTION_COMPLUS. +//----------------------------------------------------------------------------------- +BOOL IsComPlusException(const EXCEPTION_RECORD *pcER) +{ + STATIC_CONTRACT_WRAPPER; + + return WasThrownByUs(pcER, EXCEPTION_COMPLUS); +} + +VOID RaiseComPlusException() +{ + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + + ULONG_PTR exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE]; + DWORD numParams = MarkAsThrownByUs(exceptionArgs); + RaiseException(EXCEPTION_COMPLUS, 0, numParams, exceptionArgs); +} + +//=========================================================================================== +//=========================================================================================== diff --git a/src/utilcode/format1.cpp b/src/utilcode/format1.cpp new file mode 100644 index 0000000000..83946a7761 --- /dev/null +++ b/src/utilcode/format1.cpp @@ -0,0 +1,121 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +/***************************************************************************/ +/* routines for parsing file format stuff ... */ +/* this is split off from format.cpp because this uses meta-data APIs that + are not present in many builds. Thus if someone needs things in the format.cpp + file but does not have the meta-data APIs, I want it to link */ + +#include "stdafx.h" +#include "cor.h" +#include "corpriv.h" + +//--------------------------------------------------------------------------------------- +// +static LONG FilterAllExceptions(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam) +{ + if ((pExceptionPointers->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) || + (pExceptionPointers->ExceptionRecord->ExceptionCode == EXCEPTION_ARRAY_BOUNDS_EXCEEDED) || + (pExceptionPointers->ExceptionRecord->ExceptionCode == EXCEPTION_IN_PAGE_ERROR)) + return EXCEPTION_EXECUTE_HANDLER; + + return EXCEPTION_CONTINUE_SEARCH; +} + +//--------------------------------------------------------------------------------------- +// +COR_ILMETHOD_DECODER::COR_ILMETHOD_DECODER( + COR_ILMETHOD * header, + void * pInternalImport, + DecoderStatus * wbStatus) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + // Can't put contract because of SEH + // CONTRACTL + // { + // NOTHROW; + // GC_NOTRIGGER; + // FORBID_FAULT; + // } + // CONTRACTL_END + + bool fErrorInInit = false; + struct Param + { + COR_ILMETHOD_DECODER * pThis; + COR_ILMETHOD * header; + } param; + param.pThis = this; + param.header = header; + + PAL_TRY(Param *, pParam, ¶m) + { + // Decode the COR header into a more convenient form + DecoderInit(pParam->pThis, pParam->header); + } + PAL_EXCEPT_FILTER(FilterAllExceptions) + { + fErrorInInit = true; + Code = 0; + SetLocalVarSigTok(0); + if (wbStatus != NULL) + { + *wbStatus = FORMAT_ERROR; + } + } + PAL_ENDTRY + + if (fErrorInInit) + { + return; + } + + // If there is a local variable sig, fetch it into 'LocalVarSig' + if ((GetLocalVarSigTok() != 0) && (pInternalImport != NULL)) + { + IMDInternalImport * pMDI = reinterpret_cast<IMDInternalImport *>(pInternalImport); + + if (wbStatus != NULL) + { + if ((!pMDI->IsValidToken(GetLocalVarSigTok())) || + (TypeFromToken(GetLocalVarSigTok()) != mdtSignature) || + (RidFromToken(GetLocalVarSigTok()) == 0)) + { + *wbStatus = FORMAT_ERROR; // failure bad local variable signature token + return; + } + } + + if (FAILED(pMDI->GetSigFromToken(GetLocalVarSigTok(), &cbLocalVarSig, &LocalVarSig))) + { + // Failure bad local variable signature token + if (wbStatus != NULL) + { + *wbStatus = FORMAT_ERROR; + } + LocalVarSig = NULL; + cbLocalVarSig = 0; + return; + } + + if (wbStatus != NULL) + { + if (FAILED(validateTokenSig(GetLocalVarSigTok(), LocalVarSig, cbLocalVarSig, 0, pMDI)) || + (*LocalVarSig != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG)) + { + *wbStatus = VERIFICATION_ERROR; // failure validating local variable signature + return; + } + } + } + + if (wbStatus != NULL) + { + *wbStatus = SUCCESS; + } +} // COR_ILMETHOD_DECODER::COR_ILMETHOD_DECODER diff --git a/src/utilcode/fstream.cpp b/src/utilcode/fstream.cpp new file mode 100644 index 0000000000..c0c76c3ae7 --- /dev/null +++ b/src/utilcode/fstream.cpp @@ -0,0 +1,296 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + + +#include "stdafx.h" // Precompiled header key. +#include "fstream.h" + +CFileStream::CFileStream() +: _cRef(1) +, _hFile(INVALID_HANDLE_VALUE) +{ +} + +CFileStream::~CFileStream() +{ + Close(); +} + +HRESULT CFileStream::OpenForRead(LPCWSTR wzFilePath) +{ + HRESULT hr = S_OK; + DWORD dwShareMode = FILE_SHARE_READ; + + dwShareMode |= FILE_SHARE_DELETE; + + _ASSERTE(_hFile == INVALID_HANDLE_VALUE && wzFilePath); + if (_hFile != INVALID_HANDLE_VALUE || !wzFilePath) { + hr = E_INVALIDARG; + goto Exit; + } + + _hFile = WszCreateFile(wzFilePath, GENERIC_READ, + dwShareMode, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (_hFile == INVALID_HANDLE_VALUE) { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Exit; + } + +Exit: + return hr; +} + +HRESULT CFileStream::OpenForWrite(LPCWSTR wzFilePath) +{ + HRESULT hr = S_OK; + + _ASSERTE(_hFile == INVALID_HANDLE_VALUE && wzFilePath); + if (_hFile != INVALID_HANDLE_VALUE || !wzFilePath) { + hr = E_INVALIDARG; + goto Exit; + } + + _hFile = WszCreateFile(wzFilePath, GENERIC_WRITE, + FILE_SHARE_READ, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (_hFile == INVALID_HANDLE_VALUE) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Exit; + } + +Exit: + return hr; +} + +HRESULT CFileStream::QueryInterface(REFIID riid, void **ppv) +{ + HRESULT hr = S_OK; + + if (!ppv) + return E_POINTER; + + *ppv = NULL; + + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IStream)) { + *ppv = static_cast<IStream *>(this); + } + else { + hr = E_NOINTERFACE; + } + + if (*ppv) { + AddRef(); + } + + return hr; +} + +STDMETHODIMP_(ULONG) CFileStream::AddRef() +{ + return InterlockedIncrement(&_cRef); +} + +STDMETHODIMP_(ULONG) CFileStream::Release() +{ + ULONG ulRef = InterlockedDecrement(&_cRef); + + if (!ulRef) { + delete this; + } + + return ulRef; +} + +HRESULT CFileStream::Read(void *pv, ULONG cb, ULONG *pcbRead) +{ + HRESULT hr = S_OK; + ULONG cbRead = 0; + + if (pcbRead != NULL) { + *pcbRead = 0; + } + + _ASSERTE(_hFile != INVALID_HANDLE_VALUE); + if (_hFile == INVALID_HANDLE_VALUE) { + hr = E_UNEXPECTED; + goto Exit; + } + + if (!::ReadFile(_hFile, pv, cb, &cbRead, NULL)) { + hr = HRESULT_FROM_WIN32(::GetLastError()); + goto Exit; + } + + if (cbRead == 0) { + hr = S_FALSE; + } + else { + hr = NOERROR; + } + + if (pcbRead != NULL) { + *pcbRead = cbRead; + } + +Exit: + return hr; +} + +HRESULT CFileStream::Write(void const *pv, ULONG cb, ULONG *pcbWritten) +{ + HRESULT hr = S_OK; + ULONG cbWritten = 0; + + if (pcbWritten != NULL) { + *pcbWritten = 0; + } + + _ASSERTE(_hFile != INVALID_HANDLE_VALUE); + if (_hFile == INVALID_HANDLE_VALUE) { + hr = E_UNEXPECTED; + goto Exit; + } + + if (!::WriteFile(_hFile, pv, cb, &cbWritten, NULL)) { + hr = HRESULT_FROM_WIN32(::GetLastError()); + goto Exit; + } + + if (cbWritten == 0) { + hr = S_FALSE; + } + else { + hr = S_OK; + } + + if (pcbWritten != NULL) { + *pcbWritten = cbWritten; + } + +Exit: + return hr; +} + +HRESULT CFileStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) +{ +#if 1 // SetFilePointerEx not supported on Win9x + return E_NOTIMPL; +#else + HRESULT hr = S_OK; + DWORD dwFileOrigin; + BOOL bRet; + + _ASSERTE(_hFile != INVALID_HANDLE_VALUE); + if (_hFile == INVALID_HANDLE_VALUE) { + hr = E_UNEXPECTED; + goto Exit; + } + + switch (dwOrigin) { + case STREAM_SEEK_SET: + dwFileOrigin = FILE_BEGIN; + break; + + case STREAM_SEEK_CUR: + dwFileOrigin = FILE_CURRENT; + break; + + case STREAM_SEEK_END: + dwFileOrigin = FILE_END; + break; + + default: + hr = E_UNEXPECTED; + goto Exit; + } + + bRet = SetFilePointerEx(_hFile, dlibMove, (LARGE_INTEGER *)plibNewPosition, + dwFileOrigin); + if (!bRet) { + hr = HRESULT_FROM_WIN32(::GetLastError()); + goto Exit; + } + + +Exit: + return hr; +#endif +} + +HRESULT CFileStream::SetSize(ULARGE_INTEGER libNewSize) +{ + return E_NOTIMPL; +} + +HRESULT CFileStream::CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) +{ + return E_NOTIMPL; +} + +HRESULT CFileStream::Commit(DWORD grfCommitFlags) +{ + HRESULT hr = S_OK; + + if (grfCommitFlags != 0) { + hr = E_INVALIDARG; + goto Exit; + } + + if (!Close()) { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + +Exit: + return hr; +} + +HRESULT CFileStream::Revert() +{ + return E_NOTIMPL; +} + +HRESULT CFileStream::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) +{ + return E_NOTIMPL; +} + +HRESULT CFileStream::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) +{ + return E_NOTIMPL; +} + +HRESULT CFileStream::Stat(STATSTG *pstatstg, DWORD grfStatFlag) +{ + return E_NOTIMPL; +} + +HRESULT CFileStream::Clone(IStream **ppIStream) +{ + return E_NOTIMPL; +} + + +BOOL CFileStream::Close() +{ + BOOL fSuccess = FALSE; + + if (_hFile != INVALID_HANDLE_VALUE) { + if (!::CloseHandle(_hFile)) { + _hFile = INVALID_HANDLE_VALUE; + goto Exit; + } + + _hFile = INVALID_HANDLE_VALUE; + } + + fSuccess = TRUE; + +Exit: + return fSuccess; +} + diff --git a/src/utilcode/fstring.cpp b/src/utilcode/fstring.cpp new file mode 100644 index 0000000000..c26be635f4 --- /dev/null +++ b/src/utilcode/fstring.cpp @@ -0,0 +1,317 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// --------------------------------------------------------------------------- +// FString.cpp +// + +// --------------------------------------------------------------------------- + +#include "stdafx.h" +#include "ex.h" +#include "holder.h" + +#include "fstring.h" + + +namespace FString +{ + +#ifdef _MSC_VER +#pragma optimize("t", on) +#endif // _MSC_VER + +#define MAX_LENGTH 0x1fffff00 + + +HRESULT Unicode_Utf8_Length(__in_z LPCWSTR pString, __out bool * pAllAscii, __out DWORD * pLength) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + * pAllAscii = true; + + LPCWSTR p = pString; + + while (true) + { + WCHAR ch = * p; + + // Single check for termination and non ASCII + if (((unsigned) (ch - 1)) >= 0x7F) + { + if (ch != 0) + { + * pAllAscii = false; + } + + break; + } + + p ++; + } + + if (* pAllAscii) + { + if ((p - pString) > MAX_LENGTH) + { + return COR_E_OVERFLOW; + } + + * pLength = (DWORD) (p - pString); + } + else // use WideCharToMultiByte to calculate result length + { + * pLength = WszWideCharToMultiByte(CP_UTF8, 0, pString, -1, NULL, 0, NULL, NULL); + + if (*pLength == 0) + { + return HRESULT_FROM_GetLastError(); + } + + if (*pLength > MAX_LENGTH) + { + return COR_E_OVERFLOW; + } + } + + return S_OK; +} + + +// UNICODE to UTF8 +HRESULT Unicode_Utf8(__in_z LPCWSTR pString, bool allAscii, __out_z LPSTR pBuffer, DWORD length) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + pBuffer[length] = 0; + + if (allAscii) + { + LPCWSTR p = pString; + + LPSTR q = pBuffer; + + LPCWSTR endP = p + length - 8; + + // Unfold to optimize for long string: 8 chars per iteration + while (p < endP) + { + q[0] = (char) p[0]; + q[1] = (char) p[1]; + q[2] = (char) p[2]; + q[3] = (char) p[3]; + + q[4] = (char) p[4]; + q[5] = (char) p[5]; + q[6] = (char) p[6]; + q[7] = (char) p[7]; + + q += 8; + p += 8; + } + + endP += 8; + + while (p < endP) + { + * q ++ = (char) * p ++; + } + } + else + { + length = WszWideCharToMultiByte(CP_UTF8, 0, pString, -1, pBuffer, (int) length, NULL, NULL); + + if (length == 0) + { + return HRESULT_FROM_GetLastError(); + } + } + + return S_OK; +} + + +HRESULT Utf8_Unicode_Length(__in_z LPCSTR pString, __out bool * pAllAscii, __out DWORD * pLength) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + * pAllAscii = true; + + LPCSTR p = pString; + + while (true) + { + char ch = * p; + + // Single check for termination and non ASCII + if (((unsigned) (ch - 1)) >= 0x7F) + { + if (ch != 0) + { + * pAllAscii = false; + } + + break; + } + + p ++; + } + + if (* pAllAscii) + { + if ((p - pString) > MAX_LENGTH) + { + return COR_E_OVERFLOW; + } + + * pLength = (DWORD)(p - pString); + } + else + { + * pLength = WszMultiByteToWideChar(CP_UTF8, 0, pString, -1, NULL, 0); + + if (* pLength == 0) + { + return HRESULT_FROM_GetLastError(); + } + + if (* pLength > MAX_LENGTH) + { + return COR_E_OVERFLOW; + } + } + + return S_OK; +} + + +// UTF8 to ANSI + +HRESULT Utf8_Unicode(__in_z LPCSTR pString, bool allAscii, __out_z LPWSTR pBuffer, DWORD length) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + pBuffer[length] = 0; + + if (allAscii) + { + LPCSTR p = pString; + + LPWSTR q = pBuffer; + + LPCSTR endP = p + length - 8; + + // Unfold to optimize for long string: 4 chars per iteration + while (p < endP) + { + q[0] = (WCHAR) p[0]; + q[1] = (WCHAR) p[1]; + q[2] = (WCHAR) p[2]; + q[3] = (WCHAR) p[3]; + + q[4] = (WCHAR) p[4]; + q[5] = (WCHAR) p[5]; + q[6] = (WCHAR) p[6]; + q[7] = (WCHAR) p[7]; + + q += 8; + p += 8; + } + + endP += 8; + + while (p < endP) + { + * q ++ = (WCHAR) * p ++; + } + } + else + { + length = WszMultiByteToWideChar(CP_UTF8, 0, pString, -1, pBuffer, (int) length); + + if (length == 0) + { + return HRESULT_FROM_GetLastError(); + } + } + + return S_OK; +} + + +HRESULT ConvertUnicode_Utf8(__in_z LPCWSTR pString, __out_z LPSTR * pBuffer) +{ + bool allAscii; + DWORD length; + + HRESULT hr = Unicode_Utf8_Length(pString, & allAscii, & length); + + if (SUCCEEDED(hr)) + { + * pBuffer = new (nothrow) char[length + 1]; + + if (* pBuffer == NULL) + { + hr = E_OUTOFMEMORY; + } + else + { + hr = Unicode_Utf8(pString, allAscii, * pBuffer, length); + } + } + + return hr; +} + + +HRESULT ConvertUtf8_Unicode(__in_z LPCSTR pString, __out_z LPWSTR * pBuffer) +{ + bool allAscii; + DWORD length; + + HRESULT hr = Utf8_Unicode_Length(pString, & allAscii, & length); + + if (SUCCEEDED(hr)) + { + * pBuffer = new (nothrow) WCHAR[length + 1]; + + if (* pBuffer == NULL) + { + hr = E_OUTOFMEMORY; + } + else + { + hr = Utf8_Unicode(pString, allAscii, * pBuffer, length); + } + } + + return hr; +} + + +#ifdef _MSC_VER +#pragma optimize("", on) +#endif // _MSC_VER + +} // namespace FString diff --git a/src/utilcode/genericstackprobe.cpp b/src/utilcode/genericstackprobe.cpp new file mode 100644 index 0000000000..23e7726a8f --- /dev/null +++ b/src/utilcode/genericstackprobe.cpp @@ -0,0 +1,511 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// + +// +//***************************************************************************** +// genericstackprobe.cpp +// +// This contains code for generic SO stack probes outside the VM, where we don't have a thread object +// +//***************************************************************************** + +#include "stdafx.h" // Precompiled header key. +#include "utilcode.h" +#include "genericstackprobe.h" +#include "log.h" + +#if defined(FEATURE_STACK_PROBE) && !defined(DACCESS_COMPILE) + +#ifdef ENABLE_CONTRACTS_IMPL +BOOL g_EnableDefaultRWValidation = FALSE; +#endif + +bool g_StackProbingEnabled; +void (*g_fpCheckForSOInSOIntolerantCode)(); +void (*g_fpSetSOIntolerantTransitionMarker)(); +BOOL (*g_fpDoProbe)(unsigned int n); +void (*g_fpHandleSoftStackOverflow)(BOOL fSkipDebugger); + +// This function is used for NO_THROW probes that have no error return path. In this +// case, we'll just force a stack overflow exception. Do not call it directly - use +// one of the FORCE_SO macros. +void DontCallDirectlyForceStackOverflow() +{ +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:26001) // "Suppress PREFast warning about underflows" +#endif + + UINT_PTR *sp = NULL; + // we don't have access to GetCurrentSP from here, so just get an approximation + sp = (UINT_PTR *)&sp; + while (TRUE) + { + sp -= (OS_PAGE_SIZE / sizeof(UINT_PTR)); + *sp = NULL; + } + +#ifdef _PREFAST_ +#pragma warning(pop) +#endif +} + +void (*g_fpHandleStackOverflowAfterCatch)() = 0; + +// HandleStackOverflowAfterCatch +// +void HandleStackOverflowAfterCatch() +{ + if (!g_fpHandleStackOverflowAfterCatch) + { + // If g_fpUnwindGuardChainTo has not been set, then we haven't called InitStackProbes + // and we aren't probing, so bail. + return; + } + + // Reset the SO-tolerance state and restore the current guard + g_fpHandleStackOverflowAfterCatch(); +} + +NOINLINE void SOIntolerantTransitionHandler::CtorImpl() +{ + m_exceptionOccured = true; + m_pPreviousHandler = ClrFlsGetValue(TlsIdx_SOIntolerantTransitionHandler); + g_fpSetSOIntolerantTransitionMarker(); +} + +NOINLINE void SOIntolerantTransitionHandler::DtorImpl() +{ + // if we take a stack overflow exception in SO intolerant code, then we must + // rip the process. We check this by determining if the SP is beyond the calculated + // limit. Checking for the guard page being present is too much overhead during + // exception handling (if you can believe that) and impacts perf. + + if (m_exceptionOccured) + { + g_fpCheckForSOInSOIntolerantCode(); + } + + ClrFlsSetValue(TlsIdx_SOIntolerantTransitionHandler, m_pPreviousHandler); +} + +#ifdef STACK_GUARDS_DEBUG + +// If this is TRUE, we'll make the stack page that we put our stack marker in PAGE_NOACCESS so that you get an AV +// as soon as you go past the stack guard. +BOOL g_ProtectStackPagesInDebugger = FALSE; + +// This is the smallest size backout probe for which we will try to do a virtual protect for debugging. +// If this number is too small, the 1 page ganularity of VirtualProtect becomes a problem. This number +// should be less than or equal to the default backout probe size. +#define MINIMUM_PAGES_FOR_DEBUGGER_PROTECTION 4.0 + +void (*g_fpRestoreCurrentStackGuard)(BOOL fDisabled) = 0; +BOOL g_EnableBackoutStackValidation = FALSE; +BOOL (*g_fpShouldValidateSOToleranceOnThisThread)() = 0; +BOOL (*g_fp_BaseStackGuard_RequiresNStackPages)(BaseStackGuardGeneric *pGuard, unsigned int n, BOOL fThrowOnSO) = NULL; +void (*g_fp_BaseStackGuard_CheckStack)(BaseStackGuardGeneric *pGuard) = NULL; +BOOL (*g_fpCheckNStackPagesAvailable)(unsigned int n) = NULL; + +// Always initialize g_EntryPointProbeAmount to a valid value as there could be a race where a +// function probes with g_EntryPointProbeAmount's value before it is initialized in InitStackProbes. +DWORD g_EntryPointProbeAmount = DEFAULT_ENTRY_PROBE_SIZE; + +// RestoreSOToleranceState +// +// Restores the EE SO-tolerance state after a catch. + +void RestoreSOToleranceState() +{ + if (!g_fpRestoreCurrentStackGuard) + { + // If g_fpUnwindGuardChainTo has not been set, then we haven't called InitStackProbes + // and we aren't probing, so bail. + return; + } + + // Reset the SO-tolerance state and restore the current guard + g_fpRestoreCurrentStackGuard(FALSE); +} + +// +// EnsureSOTolerant ASSERTS if we are not in an SO-tolerant mode +// +void EnsureSOTolerant() +{ +#ifdef ENABLE_CONTRACTS_IMPL + ClrDebugState *pClrDebugState = GetClrDebugState(); + _ASSERTE(! pClrDebugState || pClrDebugState->IsSOTolerant()); +#endif +} + +DEBUG_NOINLINE DebugSOIntolerantTransitionHandler::DebugSOIntolerantTransitionHandler() + : SOIntolerantTransitionHandler() +{ + SCAN_SCOPE_BEGIN; + // This CANNOT be a STATIC_CONTRACT_SO_INTOLERANT b/c that isn't + // really just a static contract, it is actually calls EnsureSOIntolerantOK + // as well. Instead we just use the annotation. + ANNOTATION_FN_SO_INTOLERANT; +#ifdef ENABLE_CONTRACTS_IMPL + m_clrDebugState = GetClrDebugState(); + if (m_clrDebugState) + { + m_prevSOTolerantState = m_clrDebugState->BeginSOIntolerant(); + } +#endif +} + +DEBUG_NOINLINE DebugSOIntolerantTransitionHandler::~DebugSOIntolerantTransitionHandler() +{ + SCAN_SCOPE_END; + + if (m_clrDebugState) + { + m_clrDebugState->SetSOTolerance(m_prevSOTolerantState); + } +} + +// This is effectively an implicit probe, because we are guaranteeing that we have +// enought stack to run and will not take an SO. So we enter SO-intolerant code when +// we install one of these. +DEBUG_NOINLINE BaseStackMarker::BaseStackMarker(float numPages, BOOL fAllowDisabling) + : m_prevWasSOTolerant(FALSE) + , m_pDebugState( +#ifdef ENABLE_CONTRACTS_IMPL + CheckClrDebugState() +#else + NULL +#endif + ) + , m_fMarkerSet(FALSE) + , m_fTemporarilyDisabled(FALSE), m_fAddedToStack(FALSE), m_pPrevious(NULL) + , m_numPages(0.0), m_pMarker(NULL) + , m_fProtectedStackPage(FALSE), m_fAllowDisabling(fAllowDisabling) +{ + SCAN_SCOPE_BEGIN; + // This CANNOT be a STATIC_CONTRACT_SO_INTOLERANT b/c that isn't + // really just a static contract, it is actually calls EnsureSOIntolerantOK + // as well. Instead we just use the annotation. + ANNOTATION_FN_SO_INTOLERANT; + + { + DEBUG_ONLY_REGION(); + // If backout stack validation isn't enabled then we are done. + if (!g_EnableBackoutStackValidation) + { + return; + } + + // If we can't talk to other markers then the markers could get in each others way + if (!m_pDebugState) + { + return; + } + + // Allow only the lowest marker to be active at any one time. Yes, this means that + // the stack will only ever have one element in it. However having multiple markers + // is problematic for debugging and conflicts with the VirtualProtect option. It + // adds little value, in that small backout checks stop happening in exception + // codepaths, but these get plenty of coverage in success cases and the lowest + // placed marked is the one that could actually indicate a stack overflow. + if (!m_pDebugState->GetStackMarkerStack().IsEmpty()) + { + return; + } + + // Switch the SO tolerance mode + m_prevWasSOTolerant = m_pDebugState->SetSOTolerance(FALSE); + + // If we have less then numPages left before the end of the stack then there is + // no point in adding a marker since we will take an SO anyway if we use too much + // stack. Putting the marker is actually very bad since it artificially forces an + // SO in cases where it wouldn't normally occur if we use less than num pages of stack. + if (g_fpCheckNStackPagesAvailable && + !g_fpCheckNStackPagesAvailable(numPages < 1 ? 1 : (unsigned int)numPages)) + { + return; + } + + if (m_fAllowDisabling) + { + // Push ourselves on to the stack of stack markers on the CLR debug state. + m_pDebugState->GetStackMarkerStack().PushStackMarker(this); + m_fAddedToStack = TRUE; + } + + // Set the actual stack guard marker if we have enough stack to do so. + SetMarker(numPages); + + if (m_fMarkerSet && m_fAllowDisabling) + { + ProtectMarkerPageInDebugger(); + } + } +} + +// we have this so that the check of the global can be inlined +// and we don't make the call to CheckMarker unless we need to. +DEBUG_NOINLINE void BaseStackMarker::CheckForBackoutViolation() +{ + SCAN_SCOPE_END; + + // If backout stack validation isn't enabled then we are done. + if (!g_EnableBackoutStackValidation) + { + return; + } + + { + DEBUG_ONLY_REGION() + + // The marker should always be re-enabled at this point. + CONSISTENCY_CHECK_MSG(!m_fTemporarilyDisabled, "The stack guard was disabled but not properly re-enabled. This is a bug somewhere in the code called after this marker has been set up."); + + if (!m_pDebugState || m_fTemporarilyDisabled) + { + return; + } + + // Reset the SO tolerance of the thread. + m_pDebugState->SetSOTolerance(m_prevWasSOTolerant); + + if (m_fAddedToStack) + { + // Pop ourselves off of the stack of stack markers on the CLR debug state. + CONSISTENCY_CHECK(m_pDebugState != NULL); + BaseStackMarker *pPopResult = m_pDebugState->GetStackMarkerStack().PopStackMarker(); + + CONSISTENCY_CHECK_MSG(pPopResult == this, "The marker we pop off the stack should always be the current marker."); + CONSISTENCY_CHECK_MSG(m_pPrevious == NULL, "PopStackMarker should reset the current marker's m_pPrevious field to NULL."); + } + + // Not cancellable markers should only be checked when no cancellable markers are present. + if (!m_fAllowDisabling && !(m_pDebugState->GetStackMarkerStack().IsEmpty())) + { + return; + } + + if (m_fProtectedStackPage) + { + UndoPageProtectionInDebugger(); + } + + if (m_fMarkerSet) + { + // Check to see if we overwrote the stack guard marker. + CheckMarker(); + } + } +} + +void BaseStackMarker::SetMarker(float numPages) +{ + LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_DEBUG_ONLY; + + m_numPages = numPages; + + // Use the address of the argument to get the current stack pointer. Note that this + // won't be the exact SP; however it will be close enough. + LPVOID pStack = &numPages; + + UINT_PTR *pMarker = (UINT_PTR*)pStack - (int)(OS_PAGE_SIZE / sizeof(UINT_PTR) * m_numPages); + + // We might not have committed our stack yet, so allocate the number of pages + // we need so that they will be commited and we won't AV when we try to set the mark. + _alloca( (int)(OS_PAGE_SIZE * m_numPages) ); + m_pMarker = pMarker; + *m_pMarker = STACK_COOKIE_VALUE; + + m_fMarkerSet = TRUE; + +} + +void BaseStackMarker::RareDisableMarker() +{ + LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_DEBUG_ONLY; + + if (m_fProtectedStackPage) + { + UndoPageProtectionInDebugger(); + } + + m_fTemporarilyDisabled = TRUE; + + if (m_fMarkerSet) + { + *m_pMarker = DISABLED_STACK_COOKIE_VALUE; + } +} + +void BaseStackMarker::RareReEnableMarker() +{ + LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_DEBUG_ONLY; + + m_fTemporarilyDisabled = FALSE; + + if (m_fMarkerSet) { + *m_pMarker = STACK_COOKIE_VALUE; + } + + if (m_fProtectedStackPage) + { + ProtectMarkerPageInDebugger(); + } +} + +//----------------------------------------------------------------------------- +// Protect the page where we put the marker if a debugger is attached. That way, you get an AV right away +// when you go past the stack guard when running under a debugger. +//----------------------------------------------------------------------------- +void BaseStackMarker::ProtectMarkerPageInDebugger() +{ + WRAPPER_NO_CONTRACT; + DEBUG_ONLY_FUNCTION; + + if (!g_ProtectStackPagesInDebugger) + { + return; + } + + if (m_numPages < MINIMUM_PAGES_FOR_DEBUGGER_PROTECTION) + { + return; + } + + DWORD flOldProtect; + + LOG((LF_EH, LL_INFO100000, "BSM::PMP: m_pMarker 0x%p, value 0x%p\n", m_pMarker, *m_pMarker)); + + // We cannot call into host for VirtualProtect. EEVirtualProtect will try to restore previous + // guard, but the location has been marked with PAGE_NOACCESS. +#undef VirtualProtect + BOOL fSuccess = ::VirtualProtect(m_pMarker, 1, PAGE_NOACCESS, &flOldProtect); + _ASSERTE(fSuccess); + +#define VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect) \ + Dont_Use_VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect) + + m_fProtectedStackPage = fSuccess; +} + +//----------------------------------------------------------------------------- +// Remove page protection installed for this probe +//----------------------------------------------------------------------------- +void BaseStackMarker::UndoPageProtectionInDebugger() +{ + WRAPPER_NO_CONTRACT; + DEBUG_ONLY_FUNCTION; + + _ASSERTE(m_fProtectedStackPage); + _ASSERTE(!m_fTemporarilyDisabled); + + DWORD flOldProtect; + // EEVirtualProtect installs a BoundaryStackGuard. To avoid recursion, we call + // into OS for VirtualProtect instead. +#undef VirtualProtect + BOOL fSuccess = ::VirtualProtect(m_pMarker, 1, PAGE_READWRITE, &flOldProtect); + _ASSERTE(fSuccess); + + LOG((LF_EH, LL_INFO100000, "BSM::UMP m_pMarker 0x%p\n", m_pMarker)); + +#define VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect) \ + Dont_Use_VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect) +} + +void BaseStackMarker::CheckMarker() +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_DEBUG_ONLY; + + if ( IsMarkerOverrun(m_pMarker) ) + { + SOBackoutViolation(__FUNCTION__, __FILE__, __LINE__); + } +} + +AutoCleanupDisableBackoutStackValidation::AutoCleanupDisableBackoutStackValidation() +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_DEBUG_ONLY; +#ifdef ENABLE_CONTRACTS_IMPL + m_fAlreadyDisabled = GetClrDebugState()->GetStackMarkerStack().IsDisabled(); + if (!m_fAlreadyDisabled) + { + GetClrDebugState()->GetStackMarkerStack().RareDisableStackMarkers(); + } +#endif +} + +AutoCleanupDisableBackoutStackValidation::~AutoCleanupDisableBackoutStackValidation() +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_DEBUG_ONLY; + +#ifdef ENABLE_CONTRACTS_IMPL + if (!m_fAlreadyDisabled) + { + GetClrDebugState()->GetStackMarkerStack().RareReEnableStackMarkers(); + } +#endif +} + +inline void StackMarkerStack::PushStackMarker(BaseStackMarker *pStackMarker) +{ + LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_DEBUG_ONLY; + + pStackMarker->m_pPrevious = m_pTopStackMarker; + m_pTopStackMarker = pStackMarker; +} + +BaseStackMarker *StackMarkerStack::PopStackMarker() +{ + LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_DEBUG_ONLY; + + BaseStackMarker *pOldTop = m_pTopStackMarker; + m_pTopStackMarker = pOldTop->m_pPrevious; + pOldTop->m_pPrevious = NULL; + return pOldTop; +} + +void StackMarkerStack::RareDisableStackMarkers() +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_DEBUG_ONLY; + + // Walk up the stack of markers and disable them all. + BaseStackMarker *pCurrentStackMarker = m_pTopStackMarker; + while (pCurrentStackMarker) + { + pCurrentStackMarker->RareDisableMarker(); + pCurrentStackMarker = pCurrentStackMarker->m_pPrevious; + } + m_fDisabled = TRUE; +} + +void StackMarkerStack::RareReEnableStackMarkers() +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_DEBUG_ONLY; + + // Walk up the stack of markers and re-enable them all. + BaseStackMarker *pCurrentStackMarker = m_pTopStackMarker; + while (pCurrentStackMarker) + { + pCurrentStackMarker->RareReEnableMarker(); + pCurrentStackMarker = pCurrentStackMarker->m_pPrevious; + } + m_fDisabled = FALSE; +} + +#endif // STACK_GUARDS_DEBUG + +#endif // FEATURE_STACK_PROBE && !DACCESS_COMPILE diff --git a/src/utilcode/guidfromname.cpp b/src/utilcode/guidfromname.cpp new file mode 100644 index 0000000000..dad72b5f84 --- /dev/null +++ b/src/utilcode/guidfromname.cpp @@ -0,0 +1,210 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + + +// GuidFromName + +// Algorithm from Internet Draft document "UUIDs and GUIDs" +// By Paul J. Leach and Rich Sals, February 4, 1998. + +// This function has been adapted from the routines in the document +// uuid_create_from_name and format_uuid_v3 + +// Changes from documented routines: +// 1. Changed all instances of uuid_t to GUID. +// uuid_t field time_low is GUID field Data1. +// uuid_t field time_mid is GUID field Data2. +// uuid_t field time_hi_and_version is GUID field Data3. +// uuid_t field clock_seq_hi_and_reserved is GUID field Data4[0]. +// uuid_t field clock_seq_low is GUID field Data4[1]. +// uuid_t field node[6] is GUID field Data4[2] through Data4[8]. +// +// 2. Use a c++ implementation of the md5 cryptographic hash function. +// +// 3. Implemented the htonl, htons, ntohl, ntohs socket routines as inlines. +// +// 4. Renamed variables and types to suit my biases. + +/* +** Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +** Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & +** Digital Equipment Corporation, Maynard, Mass. +** To anyone who acknowledges that this file is provided "AS IS" +** without any express or implied warranty: permission to use, copy, +** modify, and distribute this file for any purpose is hereby +** granted without fee, provided that the above copyright notices and +** this notice appears in all source code copies, and that none of +** the names of Open Software Foundation, Inc., Hewlett-Packard +** Company, or Digital Equipment Corporation be used in advertising +** or publicity pertaining to distribution of the software without +** specific, written prior permission. Neither Open Software +** Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital Equipment +** Corporation makes any representations about the suitability of +** this software for any purpose. +*/ + +#include "stdafx.h" + +#include "md5.h" // cryptographic hash function +#include "guidfromname.h" // verify our function signature +#include "contract.h" + +#if BIGENDIAN +#define BigEndian() true +#else +#define BigEndian() false +#endif + +//============================================================================= +// htons, htonl, ntohs, ntohl equivalents copied and adapted from socket library. +//============================================================================= + +// HostToNetworkLong converts a 32-bit long to network byte order + +inline ULONG HostToNetworkLong(ULONG hostlong) +{ + LIMITED_METHOD_CONTRACT; + + if (BigEndian()) + return hostlong; + else + return ( (hostlong >> 24) & 0x000000FFL) | + ( (hostlong >> 8) & 0x0000FF00L) | + ( (hostlong << 8) & 0x00FF0000L) | + ( (hostlong << 24) & 0xFF000000L); +} + +// HostToNetworkLong converts a 16-bit short to network byte order + +inline USHORT HostToNetworkShort(USHORT hostshort) +{ + LIMITED_METHOD_CONTRACT; + + if (BigEndian()) + return hostshort; + else + return ((hostshort >> 8) & 0x00FF) | ((hostshort << 8) & 0xFF00); +} + +// NetworkToHostLong converts a 32-bit long to local host byte order + +inline ULONG NetworkToHostLong(ULONG netlong) +{ + LIMITED_METHOD_CONTRACT; + + if (BigEndian()) + return netlong; + else + return ( (netlong >> 24) & 0x000000FFL) | + ( (netlong >> 8) & 0x0000FF00L) | + ( (netlong << 8) & 0x00FF0000L) | + ( (netlong << 24) & 0xFF000000L); +} + +// NetworkToHostShort converts a 16-bit short to local host byte order + +inline USHORT NetworkToHostShort(USHORT netshort) +{ + LIMITED_METHOD_CONTRACT; + + if (BigEndian()) + return netshort; + else + return ((netshort >> 8) & 0x00FF) | ((netshort << 8) & 0xFF00); +} + +//============================================================================= +// GuidFromName(GUID * pGuidResult, REFGUID refGuidNsid, +// const void * pvName, DWORD dwcbName); +//============================================================================= + +void GuidFromName +( + GUID * pGuidResult, // resulting GUID + REFGUID refGuidNsid, // Name Space GUID, so identical names from + // different name spaces generate different GUIDs + const void * pvName, // the name from which to generate a GUID + DWORD dwcbName // name length in bytes +) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + MD5 md5; // Cryptographic hash class instance + MD5HASHDATA md5HashData; // 128-bit hash result + GUID guidNsid; // context NameSpace GUID in network byte order + + GUID guidTemp; + + // put name space ID in network byte order so it hashes the same + // no matter what endian machine we're on + guidNsid = refGuidNsid; + + // The sample code in the IETF draft document discards the result of + // htonl and htons. I've implemented what I think is meant and I've + // sent a note to the author asking for confirmation that this is + // his intent. + if (!BigEndian()) // evaluated at compile time in retail builds + { + guidNsid.Data1 = HostToNetworkLong (guidNsid.Data1); + guidNsid.Data2 = HostToNetworkShort(guidNsid.Data2); + guidNsid.Data3 = HostToNetworkShort(guidNsid.Data3); + } + + md5.Init(); + md5.HashMore(&guidNsid, sizeof(GUID)); + md5.HashMore(pvName, dwcbName); + md5.GetHashValue(&md5HashData); + + // the hash is in network byte order at this point + memcpy(&guidTemp, &md5HashData, sizeof(GUID)); + + // Remainder adapted from function "format_uuid_v3" in IETF draft document + // Construct a version 3 uuid with the pseudo-random number plus a few constants. + // convert GUID from network order to local byte order + if (!BigEndian()) // evaluated at compile time in retail builds + { + guidTemp.Data1 = NetworkToHostLong (guidTemp.Data1); + guidTemp.Data2 = NetworkToHostShort(guidTemp.Data2); + guidTemp.Data3 = NetworkToHostShort(guidTemp.Data3); + } + + // set version number + guidTemp.Data3 &= 0x0FFF; // clear version number nibble + guidTemp.Data3 |= (3 << 12);// set version 3 = name-based + + // set variant field + guidTemp.Data4[0] &= 0x3F; // clear variant bits + guidTemp.Data4[0] |= 0x80; // set variant = 100b + + // If two GuidFromName calls were made from different threads with the same parameters, + // we may get incorrect result even though the expected result is the same, because + // GuidFromName is operating on the same pGuidResult buffer. + // Fix this problem by using a temp GUID buffer and then copy to the pGuidResult buffer. + memcpy(pGuidResult, &guidTemp, sizeof(GUID)); +} + + +// This guid is used for calling GuidFromName function as COM+ runtime uniqualifier +// +// {69F9CBC9-DA05-11d1-9408-0000F8083460} +static const GUID COMPLUS_RUNTIME_GUID = {0x69f9cbc9, 0xda05, 0x11d1, + {0x94, 0x8, 0x0, 0x0, 0xf8, 0x8, 0x34, 0x60}}; + +void CorGuidFromNameW +( + GUID * pGuidResult, // resulting GUID + LPCWSTR wzName, // the unicode name from which to generate a GUID + SIZE_T cchName // name length in count of unicode character +) +{ + WRAPPER_NO_CONTRACT; + + GuidFromName( + pGuidResult, + COMPLUS_RUNTIME_GUID, + wzName, + (DWORD)((cchName == (SIZE_T) -1 ? (lstrlenW(wzName)+1) : cchName) * sizeof(WCHAR))); +} diff --git a/src/utilcode/hostimpl.cpp b/src/utilcode/hostimpl.cpp new file mode 100644 index 0000000000..75ee7b6e1c --- /dev/null +++ b/src/utilcode/hostimpl.cpp @@ -0,0 +1,426 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// ==++== +// + +// +// + +// +// ==--== + +#include "stdafx.h" + +#include "mscoree.h" +#include "clrinternal.h" +#include "hostimpl.h" +#include "predeftlsslot.h" +#include "unsafe.h" + +// to avoid to include clrhost.h in this file +#ifdef FAILPOINTS_ENABLED +extern int RFS_HashStack(); +#endif + +static DWORD TlsIndex = TLS_OUT_OF_INDEXES; +static PTLS_CALLBACK_FUNCTION Callbacks[MAX_PREDEFINED_TLS_SLOT]; + +#ifdef SELF_NO_HOST +HANDLE (*g_fnGetExecutableHeapHandle)(); +#endif + +extern LPVOID (*__ClrFlsGetBlock)(); + +// +// FLS getter to avoid unnecessary indirection via execution engine. +// +VOID * ClrFlsGetBlockDirect() +{ + return UnsafeTlsGetValue(TlsIndex); +} + +// +// utility functions for tls functionality +// +static void **CheckThreadState(DWORD slot, BOOL force = TRUE) +{ + // Treat as a runtime assertion, since the invariant spans many DLLs. + _ASSERTE(slot < MAX_PREDEFINED_TLS_SLOT); + + // Ensure we have a TLS Index + if (TlsIndex == TLS_OUT_OF_INDEXES) + { + DWORD tmp = UnsafeTlsAlloc(); + + if (InterlockedCompareExchange((LONG*)&TlsIndex, tmp, TLS_OUT_OF_INDEXES) != (LONG) TLS_OUT_OF_INDEXES) + { + // We lost the race with another thread. + UnsafeTlsFree(tmp); + } + + // Switch to faster TLS getter now that the TLS slot is initialized + __ClrFlsGetBlock = ClrFlsGetBlockDirect; + } + + _ASSERTE(TlsIndex != TLS_OUT_OF_INDEXES); + + void **pTlsData = (void **)TlsGetValue(TlsIndex); + + if (pTlsData == 0 && force) { + + // !!! Contract uses our TLS support. Contract may be used before our host support is set up. + // !!! To better support contract, we call into OS for memory allocation. + pTlsData = (void**) ::HeapAlloc(GetProcessHeap(),0,MAX_PREDEFINED_TLS_SLOT*sizeof(void*)); + + + if (pTlsData == NULL) + { + // workaround! We don't want exceptions being thrown during ClrInitDebugState. Just return NULL out of TlsSetValue. + // ClrInitDebugState will do a confirming FlsGet to see if the value stuck. + + // If this is for the stack probe, and we failed to allocate memory for it, we won't + // put in a guard page. + if (slot == TlsIdx_ClrDebugState || slot == TlsIdx_StackProbe) + { + return NULL; + } + RaiseException(STATUS_NO_MEMORY, 0, 0, NULL); + } + for (int i=0; i<MAX_PREDEFINED_TLS_SLOT; i++) + pTlsData[i] = 0; + UnsafeTlsSetValue(TlsIndex, pTlsData); + } + + return pTlsData; +} // CheckThreadState + +// This function should only be called during process detatch for +// mscoree.dll. +VOID STDMETHODCALLTYPE TLS_FreeMasterSlotIndex() +{ + if (TlsIndex != TLS_OUT_OF_INDEXES) + if (UnsafeTlsFree(TlsIndex)) + TlsIndex = TLS_OUT_OF_INDEXES; +} // TLS_FreeMasterSlotIndex + + + +HRESULT STDMETHODCALLTYPE UtilExecutionEngine::QueryInterface(REFIID id, void **pInterface) +{ + if (!pInterface) + return E_POINTER; + + *pInterface = NULL; + + if (id == IID_IExecutionEngine) + *pInterface = (IExecutionEngine *)this; + else if (id == IID_IEEMemoryManager) + *pInterface = (IEEMemoryManager *)this; + else if (id == IID_IUnknown) + *pInterface = (IUnknown *)(IExecutionEngine *)this; + else + return E_NOINTERFACE; + + AddRef(); + return S_OK; +} // UtilExecutionEngine::QueryInterface + +// +// lifetime of this object is that of the app it lives in so no point in AddRef/Release +// +ULONG STDMETHODCALLTYPE UtilExecutionEngine::AddRef() +{ + return 1; +} + +ULONG STDMETHODCALLTYPE UtilExecutionEngine::Release() +{ + return 1; +} + +VOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback) +{ + CheckThreadState(slot); + + // They can toggle between a callback and no callback. But anything else looks like + // confusion on their part. + // + // (TlsIdx_ClrDebugState associates its callback from utilcode.lib - which can be replicated. But + // all the callbacks are equally good.) + _ASSERTE(slot == TlsIdx_ClrDebugState || Callbacks[slot] == 0 || Callbacks[slot] == callback || callback == 0); + Callbacks[slot] = callback; +} + +LPVOID* STDMETHODCALLTYPE UtilExecutionEngine::TLS_GetDataBlock() +{ + if (TlsIndex == TLS_OUT_OF_INDEXES) + return NULL; + + return (LPVOID *)UnsafeTlsGetValue(TlsIndex); +} + +LPVOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_GetValue(DWORD slot) +{ + void **pTlsData = CheckThreadState(slot, FALSE); + if (pTlsData) + return pTlsData[slot]; + else + return NULL; +} + +BOOL STDMETHODCALLTYPE UtilExecutionEngine::TLS_CheckValue(DWORD slot, LPVOID * pValue) +{ + void **pTlsData = CheckThreadState(slot, FALSE); + if (pTlsData) + { + *pValue = pTlsData[slot]; + return TRUE; + } + return FALSE; +} + +VOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_SetValue(DWORD slot, LPVOID pData) +{ + void **pTlsData = CheckThreadState(slot); + if (pTlsData) // Yes, CheckThreadState(slot, TRUE) can return NULL now. + { + pTlsData[slot] = pData; + } +} + +VOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_ThreadDetaching() +{ + void **pTlsData = CheckThreadState(0, FALSE); + if (pTlsData) + { + for (int i=0; i<MAX_PREDEFINED_TLS_SLOT; i++) + { + // If we have some data and a callback, issue it. + if (Callbacks[i] != 0 && pTlsData[i] != 0) + (*Callbacks[i])(pTlsData[i]); + } + ::HeapFree (GetProcessHeap(),0,pTlsData); + + } +} + +CRITSEC_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags) +{ + CRITICAL_SECTION *cs = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION)); + UnsafeInitializeCriticalSection(cs); + return (CRITSEC_COOKIE)cs; +} + +void STDMETHODCALLTYPE UtilExecutionEngine::DestroyLock(CRITSEC_COOKIE lock) +{ + _ASSERTE(lock); + UnsafeDeleteCriticalSection((CRITICAL_SECTION*)lock); + free(lock); +} + +void STDMETHODCALLTYPE UtilExecutionEngine::AcquireLock(CRITSEC_COOKIE lock) +{ + _ASSERTE(lock); + UnsafeEnterCriticalSection((CRITICAL_SECTION*)lock); +} + +void STDMETHODCALLTYPE UtilExecutionEngine::ReleaseLock(CRITSEC_COOKIE lock) +{ + _ASSERTE(lock); + UnsafeLeaveCriticalSection((CRITICAL_SECTION*)lock); +} + +EVENT_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::CreateAutoEvent(BOOL bInitialState) +{ + HANDLE handle = UnsafeCreateEvent(NULL, FALSE, bInitialState, NULL); + _ASSERTE(handle); + return (EVENT_COOKIE)handle; +} + +EVENT_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::CreateManualEvent(BOOL bInitialState) +{ + HANDLE handle = UnsafeCreateEvent(NULL, TRUE, bInitialState, NULL); + _ASSERTE(handle); + return (EVENT_COOKIE)handle; +} + +void STDMETHODCALLTYPE UtilExecutionEngine::CloseEvent(EVENT_COOKIE event) +{ + _ASSERTE(event); + CloseHandle((HANDLE)event); +} + +BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrSetEvent(EVENT_COOKIE event) +{ + _ASSERTE(event); + return UnsafeSetEvent((HANDLE)event); +} + +BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrResetEvent(EVENT_COOKIE event) +{ + _ASSERTE(event); + return UnsafeResetEvent((HANDLE)event); +} + +DWORD STDMETHODCALLTYPE UtilExecutionEngine::WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable) +{ + _ASSERTE(event); + return WaitForSingleObjectEx((HANDLE)event, dwMilliseconds, bAlertable); +} + +DWORD STDMETHODCALLTYPE UtilExecutionEngine::WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds) +{ + _ASSERTE(handle); + return WaitForSingleObject(handle, dwMilliseconds); +} + +SEMAPHORE_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax) +{ + HANDLE handle = UnsafeCreateSemaphore(NULL, (LONG)dwInitial, (LONG)dwMax, NULL); + _ASSERTE(handle); + return (SEMAPHORE_COOKIE)handle; +} + +void STDMETHODCALLTYPE UtilExecutionEngine::ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore) +{ + _ASSERTE(semaphore); + CloseHandle((HANDLE)semaphore); +} + +DWORD STDMETHODCALLTYPE UtilExecutionEngine::ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable) +{ + _ASSERTE(semaphore); + return WaitForSingleObjectEx((HANDLE)semaphore, dwMilliseconds, bAlertable); +} + +BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount) +{ + _ASSERTE(semaphore); + return UnsafeReleaseSemaphore((HANDLE)semaphore, lReleaseCount, lpPreviousCount); +} + +MUTEX_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, + BOOL bInitialOwner, + LPCTSTR lpName) +{ + return (MUTEX_COOKIE)WszCreateMutex(lpMutexAttributes,bInitialOwner,lpName); +} + +void STDMETHODCALLTYPE UtilExecutionEngine::ClrCloseMutex(MUTEX_COOKIE mutex) +{ + _ASSERTE(mutex); + CloseHandle((HANDLE)mutex); +} + +BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrReleaseMutex(MUTEX_COOKIE mutex) +{ + _ASSERTE(mutex); + return ReleaseMutex((HANDLE)mutex); +} + +DWORD STDMETHODCALLTYPE UtilExecutionEngine::ClrWaitForMutex(MUTEX_COOKIE mutex, + DWORD dwMilliseconds, + BOOL bAlertable) +{ + _ASSERTE(mutex); + return WaitForSingleObjectEx ((HANDLE)mutex, dwMilliseconds, bAlertable); +} + +DWORD STDMETHODCALLTYPE UtilExecutionEngine::ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable) +{ + return SleepEx (dwMilliseconds, bAlertable); +} + +BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrAllocationDisallowed() +{ + return FALSE; +} + +LPVOID STDMETHODCALLTYPE UtilExecutionEngine::ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) +{ +#ifdef FAILPOINTS_ENABLED + if (RFS_HashStack ()) + return NULL; +#endif + return VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect); +} + +BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType) +{ + return VirtualFree(lpAddress, dwSize, dwFreeType); +} + +SIZE_T STDMETHODCALLTYPE UtilExecutionEngine::ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength) +{ + return VirtualQuery(lpAddress, lpBuffer, dwLength); +} + +BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect) +{ + return VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect); +} + +HANDLE STDMETHODCALLTYPE UtilExecutionEngine::ClrGetProcessHeap() +{ + return GetProcessHeap(); +} + +HANDLE STDMETHODCALLTYPE UtilExecutionEngine::ClrGetProcessExecutableHeap() +{ +#ifndef CROSSGEN_COMPILE + _ASSERTE(g_fnGetExecutableHeapHandle); + return (g_fnGetExecutableHeapHandle != NULL) ? g_fnGetExecutableHeapHandle() : NULL; +#else + return GetProcessHeap(); +#endif +} + +HANDLE STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize) +{ + return HeapCreate(flOptions, dwInitialSize, dwMaximumSize); +} + +BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapDestroy(HANDLE hHeap) +{ + return HeapDestroy(hHeap); +} + +LPVOID STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes) +{ +#ifdef FAILPOINTS_ENABLED + if (RFS_HashStack ()) + return NULL; +#endif + return HeapAlloc(hHeap, dwFlags, dwBytes); +} + +BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) +{ + return HeapFree(hHeap, dwFlags, lpMem); +} + +BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem) +{ + return HeapValidate(hHeap, dwFlags, lpMem); +} + + +//------------------------------------------------------------------------------ +// Helper function to get an exception from outside the exception. In +// the CLR, it may be from the Thread object. Non-CLR users have no thread object, +// and it will do nothing. + +void UtilExecutionEngine::GetLastThrownObjectExceptionFromThread(void **ppvException) +{ + // Declare class so we can declare Exception** + class Exception; + + // Cast to our real type. + Exception **ppException = reinterpret_cast<Exception**>(ppvException); + + *ppException = NULL; +} // UtilExecutionEngine::GetLastThrownObjectExceptionFromThread + diff --git a/src/utilcode/hostimpl.h b/src/utilcode/hostimpl.h new file mode 100644 index 0000000000..f82ffda2a6 --- /dev/null +++ b/src/utilcode/hostimpl.h @@ -0,0 +1,112 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// ==++== +// + +// +// + +// +// ==--== + +#ifndef __HOSTIMPL_H__ +#define __HOSTIMPL_H__ + +#ifdef SELF_NO_HOST +extern HANDLE g_ExecutableHeapHandle; +#endif + +// We have an internal class that is used to make sure the hosting api +// is forwarded to the os. This is a must for the shim because mscorwks +// which normally contains the implementation of the hosting api has not +// been loaded yet. In fact the shim is the one component responsible +// for that loading +class UtilExecutionEngine : public IExecutionEngine, public IEEMemoryManager +{ +private: + + //*************************************************************************** + // IUnknown methods + //*************************************************************************** + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, void **pInterface); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + //*************************************************************************** + // IExecutionEngine methods for TLS + //*************************************************************************** + + // Associate a callback for cleanup with a TLS slot + VOID STDMETHODCALLTYPE TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback); + // Get the master TLS slot index + LPVOID* STDMETHODCALLTYPE TLS_GetDataBlock(); + + // Get the value at a slot + LPVOID STDMETHODCALLTYPE TLS_GetValue(DWORD slot); + + // Get the value at a slot, return FALSE if TLS info block doesn't exist + BOOL STDMETHODCALLTYPE TLS_CheckValue(DWORD slot, LPVOID * pValue); + // Set the value at a slot + VOID STDMETHODCALLTYPE TLS_SetValue(DWORD slot, LPVOID pData); + // Free TLS memory block and make callback + VOID STDMETHODCALLTYPE TLS_ThreadDetaching(); + + //*************************************************************************** + // IExecutionEngine methods for locking + //*************************************************************************** + + CRITSEC_COOKIE STDMETHODCALLTYPE CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags); + void STDMETHODCALLTYPE DestroyLock(CRITSEC_COOKIE lock); + void STDMETHODCALLTYPE AcquireLock(CRITSEC_COOKIE lock); + void STDMETHODCALLTYPE ReleaseLock(CRITSEC_COOKIE lock); + + EVENT_COOKIE STDMETHODCALLTYPE CreateAutoEvent(BOOL bInitialState); + EVENT_COOKIE STDMETHODCALLTYPE CreateManualEvent(BOOL bInitialState); + void STDMETHODCALLTYPE CloseEvent(EVENT_COOKIE event); + BOOL STDMETHODCALLTYPE ClrSetEvent(EVENT_COOKIE event); + BOOL STDMETHODCALLTYPE ClrResetEvent(EVENT_COOKIE event); + DWORD STDMETHODCALLTYPE WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable); + DWORD STDMETHODCALLTYPE WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds); + + SEMAPHORE_COOKIE STDMETHODCALLTYPE ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax); + void STDMETHODCALLTYPE ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore); + DWORD STDMETHODCALLTYPE ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable); + BOOL STDMETHODCALLTYPE ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount); + + MUTEX_COOKIE STDMETHODCALLTYPE ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, + BOOL bInitialOwner, + LPCTSTR lpName); + void STDMETHODCALLTYPE ClrCloseMutex(MUTEX_COOKIE mutex); + BOOL STDMETHODCALLTYPE ClrReleaseMutex(MUTEX_COOKIE mutex); + DWORD STDMETHODCALLTYPE ClrWaitForMutex(MUTEX_COOKIE mutex, + DWORD dwMilliseconds, + BOOL bAlertable); + + DWORD STDMETHODCALLTYPE ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable); + + BOOL STDMETHODCALLTYPE ClrAllocationDisallowed(); + + void STDMETHODCALLTYPE GetLastThrownObjectExceptionFromThread(void **ppvException); + + //*************************************************************************** + // IEEMemoryManager methods for locking + //*************************************************************************** + LPVOID STDMETHODCALLTYPE ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); + BOOL STDMETHODCALLTYPE ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType); + SIZE_T STDMETHODCALLTYPE ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength); + BOOL STDMETHODCALLTYPE ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); + HANDLE STDMETHODCALLTYPE ClrGetProcessHeap(); + HANDLE STDMETHODCALLTYPE ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize); + BOOL STDMETHODCALLTYPE ClrHeapDestroy(HANDLE hHeap); + LPVOID STDMETHODCALLTYPE ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes); + BOOL STDMETHODCALLTYPE ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem); + BOOL STDMETHODCALLTYPE ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem); + HANDLE STDMETHODCALLTYPE ClrGetProcessExecutableHeap(); + +}; // class UtilExecutionEngine + +#endif //__HOSTIMPL_H__ diff --git a/src/utilcode/iallocator.cpp b/src/utilcode/iallocator.cpp new file mode 100644 index 0000000000..85966dcc7f --- /dev/null +++ b/src/utilcode/iallocator.cpp @@ -0,0 +1,15 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#include "stdafx.h" // Precompiled header key. +#include "iallocator.h" + +// static +DefaultAllocator DefaultAllocator::s_singleton; + +// static +ProcessHeapAllocator ProcessHeapAllocator::s_singleton; + +int AllowZeroAllocator::s_zeroLenAllocTarg; diff --git a/src/utilcode/ilformatter.cpp b/src/utilcode/ilformatter.cpp new file mode 100644 index 0000000000..41842c6e4d --- /dev/null +++ b/src/utilcode/ilformatter.cpp @@ -0,0 +1,822 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +/***************************************************************************/ +/* ILFormatter.h */ +/***************************************************************************/ + +#include "stdafx.h" +#include <cor.h> +#include <debugmacros.h> // for ASSERTE +#include "ilformatter.h" +#include "outstring.h" +#include "opinfo.h" + +/***************************************************************************/ +void ILFormatter::init(IMetaDataImport* aMeta, const BYTE* aStart, + const BYTE* aLimit, unsigned maxStack, const COR_ILMETHOD_SECT_EH* eh) { + this->~ILFormatter(); // clean out old stuff + + meta = aMeta; + start = aStart; + limit = aLimit; + if (maxStack == 0) maxStack++; + stackStart = stackCur = new StackEntry[maxStack]; + stackEnd = stackStart + maxStack; + targetStart = targetCur = targetEnd = 0; + if (eh != 0) { + COR_ILMETHOD_SECT_EH_CLAUSE_FAT buff; + const COR_ILMETHOD_SECT_EH_CLAUSE_FAT* clause; + for(unsigned i = 0; i < eh->EHCount(); i++) { + clause = (COR_ILMETHOD_SECT_EH_CLAUSE_FAT*)eh->EHClause(i, &buff); + // is it a regular catch clause ? + if ((clause->GetFlags() & (COR_ILEXCEPTION_CLAUSE_FINALLY | COR_ILEXCEPTION_CLAUSE_FAULT)) == 0) + setTarget(clause->GetHandlerOffset(), 1); + if(clause->GetFlags() & COR_ILEXCEPTION_CLAUSE_FILTER) + setTarget(clause->GetFilterOffset(), 1); + } + } +} + +/***************************************************************************/ +inline size_t ILFormatter::stackDepth() { + return(stackCur - stackStart); +} + +/***************************************************************************/ +inline void ILFormatter::pushAndClear(OutString* val, int prec) { + if (stackCur >= stackEnd) { + _ASSERTE(!"Stack Overflow (can be ignored)"); + return; // Ignore overflow in free build + } + stackCur->val.swap(*val); + val->clear(); + stackCur->prec = prec; + stackCur++; +} + +/***************************************************************************/ +inline OutString* ILFormatter::top() { + if (stackDepth() == 0) { + _ASSERTE(!"Stack underflow (can be ignored)"); + stackStart->val.clear(); + stackStart->val << "<UNDERFLOW ERROR>"; + return (&stackStart->val); + } + return(&stackCur[-1].val); +} + +/***************************************************************************/ +inline OutString* ILFormatter::pop(int prec) { + if (stackDepth() == 0) { + _ASSERTE(!"Stack underflow (can be ignored)"); + stackStart->val.clear(); + stackStart->val << "<UNDERFLOW ERROR>"; + return (&stackStart->val); + } + --stackCur; + if (stackCur->prec < prec) { + stackCur->val.prepend('('); + stackCur->val << ')'; + } + return(&stackCur->val); +} + +/***************************************************************************/ +inline void ILFormatter::popN(size_t num) { + if (stackCur-stackStart < (SSIZE_T)num) { + _ASSERTE(!"Stack underflow (can be ignored)"); + stackCur = stackStart; + return; + } + stackCur -= num; +} + +/***************************************************************************/ +void ILFormatter::setStackAsTarget(size_t ilOffset) { + + Target*ptr = targetStart; + for(;;) { + if (ptr >= targetCur) + return; + if (ptr->ilOffset == ilOffset) + break; + ptr++; + } + + for(size_t i = 0; i < ptr->stackDepth; i++) { + stackStart[i].val.clear(); + stackStart[i].val << "@STK" << (unsigned)i; + } + stackCur = stackStart + ptr->stackDepth; +} + +/***************************************************************************/ +void ILFormatter::setTarget(size_t ilOffset, size_t depth) { + if (depth == 0) + return; + + if (targetCur >= targetEnd) { + Target* targetOld = targetStart; + size_t oldLen = targetCur-targetStart; + targetStart = new Target[oldLen+10]; + targetEnd = &targetStart[oldLen+10]; + targetCur = &targetStart[oldLen]; + memcpy(targetStart, targetOld, sizeof(Target)*oldLen); + delete [] targetOld; + } + targetCur->ilOffset = ilOffset; + targetCur->stackDepth = depth; + targetCur++; +} + +/***************************************************************************/ +void ILFormatter::spillStack(OutString* out) { + + for(unsigned i = 0; i < stackDepth(); i++) { + // don't bother spilling something already spilled. + if (memcmp(stackStart[i].val.val(), "@STK", 4) != 0) + *out << "@STK" << i << " = " << stackStart[i].val.val() << "\n"; + stackStart[i].val.clear(); + stackStart[i].val << "@STK" << i ; + } +} + +/***************************************************************************/ +const BYTE* ILFormatter::formatInstr(const BYTE* instrPtr, OutString* out) { + + _ASSERTE(start < instrPtr && instrPtr < limit); + OpArgsVal arg; + OpInfo op; + instrPtr = op.fetch(instrPtr, &arg); + *out << op.getName(); + if (op.getArgsInfo() != InlineNone) + *out << ' '; + formatInstrArgs(op, arg, out, instrPtr - start); + return(instrPtr); +} + +/***************************************************************************/ +void ILFormatter::formatArgs(unsigned numArgs, OutString* out) { + + *out << '('; + if (numArgs > stackDepth()) { + _ASSERTE(!"Underflow error"); + *out << "<UNDERFLOW ERROR>"; + } + else { + popN(numArgs); + for(unsigned i = 0; i < numArgs; i++) { + if (i != 0) *out << ", "; + *out << stackCur[i].val.val(); + } + } + *out << ')'; +} + +/***************************************************************************/ +void ILFormatter::formatInstrArgs(OpInfo op, OpArgsVal arg, OutString* out, size_t curILOffset) { + + MDUTF8CSTR typeName=0; + HRESULT hr = S_OK; + switch(op.getArgsInfo() & PrimaryMask) { + case InlineNone: + break; + case InlineVar: + *out << arg.i; + break; + case InlineI: + case InlineRVA: + out->hex(arg.i, 0, OutString::put0x); + break; + case InlineR: + *out << arg.r; + break; + case InlineBrTarget: { + _ASSERTE(curILOffset != INVALID_IL_OFFSET); + size_t target = curILOffset + arg.i; + setTarget(target, stackDepth()); + *out << "IL_"; out->hex(static_cast<unsigned __int64>(target), 4, OutString::zeroFill); + } break; + case InlineI8: + out->hex(arg.i, 0, OutString::put0x); + break; + case InlineString: { + ULONG numChars; + wchar_t str[84]; + + hr = meta->GetUserString(arg.i, str, 80, &numChars); + _ASSERTE(SUCCEEDED(hr)); + if (numChars < 80) + str[numChars] = 0; + wcscpy_s(&str[79], 4, W("...")); + *out << '"'; + wchar_t* ptr = str; + while(*ptr != 0) { + if (*ptr == '\n') + *out << "\\n"; + else if (*ptr == '"') + *out << "\\\""; + else if (*ptr < 0x20 || * ptr >= 0x80) { + *out << '\\'; + out->hex(*ptr, 4, OutString::zeroFill); + } + else + *out << char(*ptr); + ptr++; + } + *out << '"'; + } break; + case InlineMethod: + case InlineField: + case InlineTok: { + // Get the typeName if possible + mdToken mdType = mdTypeDefNil; + if (TypeFromToken(arg.i) == mdtMethodDef) + hr = meta->GetMethodProps(mdMethodDef(arg.i), &mdType, 0, 0, 0, 0, 0, 0, 0, 0); + else if (TypeFromToken(arg.i) == mdtMemberRef) + hr = meta->GetMemberRefProps(mdMemberRef(arg.i), &mdType, 0, 0, 0, 0, 0); + else if (TypeFromToken(arg.i) == mdtFieldDef) + hr = meta->GetFieldProps(mdMethodDef(arg.i), &mdType, 0, 0, 0, 0, 0, 0, 0, 0, 0); + if (SUCCEEDED(hr) && mdType != mdTypeDefNil) { + hr = meta->GetNameFromToken(mdType, &typeName); + } + } + // FALL THROUGH + case InlineType: { + // FIX handle case if (TypeFromToken(arg.i) == mdtTypeSpec) + MDUTF8CSTR name; + hr = meta->GetNameFromToken(arg.i, &name); + if (SUCCEEDED(hr)) { + if (typeName) { + const char* lastDot = strrchr(typeName, '.'); + if (lastDot) typeName = lastDot + 1; + *out << typeName << "::"; + } + *out << name; + } + else { + *out << "TOK<"; + out->hex(arg.i, 0, OutString::put0x); + *out << '>'; + } + } break; + case InlineSig: + *out << "SIG<"; + out->hex(arg.i, 0, OutString::put0x); + *out << '>'; + break; + case InlineSwitch: { + _ASSERTE(curILOffset != INVALID_IL_OFFSET); + unsigned count = arg.switch_.count; + unsigned i; + for (i = 0; i < count; i++) { + size_t target = curILOffset + GET_UNALIGNED_VAL32(&arg.switch_.targets[i]); + setTarget(target, stackDepth()-1); + *out << "IL_"; out->hex(static_cast<unsigned __int64>(target), 4, OutString::zeroFill); + if (i < count) + *out << ' '; + } + } break; + case InlinePhi: { + unsigned count = arg.phi.count; + unsigned i; + for (i = 0; i < count; i++) { + *out << GET_UNALIGNED_VAL32(&arg.phi.vars[i]); + if (i < count) + *out << ' '; + } + } break; + default: + _ASSERTE(!"BadType"); + } +} + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +/***************************************************************************/ +const BYTE* ILFormatter::formatStatement(const BYTE* instrPtr, OutString* out) { + + OutString result; + OpInfo op; + OutString *lhs, *rhs, *idx; + const char* name; + int prec = 0; + + // set stack as it would be if it was begin jumped to + setStackAsTarget(instrPtr - start); + + while(instrPtr < limit) { + OpArgsVal inlineArg; + instrPtr = op.fetch(instrPtr, &inlineArg); + + switch(op.getOpcode()) { + case CEE_UNALIGNED: + case CEE_TAILCALL: + case CEE_VOLATILE: + // for now just skip these + break; + + case CEE_LDARGA_S: + case CEE_LDARGA: + result << "&"; + goto DO_LDARG; + + case CEE_LDARG_0: + case CEE_LDARG_1: + case CEE_LDARG_2: + case CEE_LDARG_3: + inlineArg.i = op.getOpcode() - CEE_LDARG_0; + goto DO_LDARG; + + case CEE_LDARG: + case CEE_LDARG_S: + DO_LDARG: + name = "arg"; + DO_LDARG_LDLOC: + result << name << inlineArg.i; + prec = 0x1000; + goto DO_PUSH; + DO_PUSH: + pushAndClear(&result, prec); // also clears result! + break; + + case CEE_LDLOCA_S: + case CEE_LDLOCA: + result << "&"; + goto DO_LDLOC; + + case CEE_LDLOC_0: + case CEE_LDLOC_1: + case CEE_LDLOC_2: + case CEE_LDLOC_3: + inlineArg.i = op.getOpcode() - CEE_LDLOC_0; + goto DO_LDLOC; + + case CEE_LDLOC: + case CEE_LDLOC_S: + DO_LDLOC: + name = "loc"; + goto DO_LDARG_LDLOC; + + case CEE_STARG: + case CEE_STARG_S: + name = "arg"; + DO_STARG_STLOC: + lhs = pop(0x10); + result << name << inlineArg.i << " = " << lhs->val(); + DO_STMT: + spillStack(out); + *out << result.val() << '\n'; + // if flow of control does not fall through, + // assume the stack is empty + if (op.getFlow() == FLOW_BRANCH || op.getFlow() == FLOW_RETURN || + op.getFlow() == FLOW_THROW) { + popN(stackDepth()); + } + return(instrPtr); + + case CEE_STLOC_0: + case CEE_STLOC_1: + case CEE_STLOC_2: + case CEE_STLOC_3: + inlineArg.i = op.getOpcode() - CEE_STLOC_0; + goto DO_STLOC; + + case CEE_STLOC: + case CEE_STLOC_S: + DO_STLOC: + name = "loc"; + goto DO_STARG_STLOC; + + case CEE_LDC_I4_M1: + case CEE_LDC_I4_0: + case CEE_LDC_I4_1: + case CEE_LDC_I4_2: + case CEE_LDC_I4_3: + case CEE_LDC_I4_4: + case CEE_LDC_I4_5: + case CEE_LDC_I4_6: + case CEE_LDC_I4_7: + case CEE_LDC_I4_8: + inlineArg.i = op.getOpcode() - CEE_LDC_I4_0; + // FALL THROUGH + case CEE_LDC_I4: + case CEE_LDC_I4_S: + result << inlineArg.i; + prec = 0x1000; + goto DO_PUSH; + + case CEE_LDC_I8: + result.hex(inlineArg.i8); + prec = 0x1000; + goto DO_PUSH; + + case CEE_LDC_R4: + case CEE_LDC_R8: + result << inlineArg.r; + prec = 0x1000; + goto DO_PUSH; + + case CEE_LDNULL: + result << "null"; + prec = 0x1000; + goto DO_PUSH; + + case CEE_LDSTR: + formatInstrArgs(op, inlineArg, &result); + prec = 0x1000; + goto DO_PUSH; + + case CEE_BEQ: + case CEE_BEQ_S: + name = "=="; prec = 0x40; goto DO_BR_BINOP; + case CEE_BGE: + case CEE_BGE_S: + name = ">="; prec = 0x40; goto DO_BR_BINOP; + case CEE_BGE_UN: + case CEE_BGE_UN_S: + name = ">=un"; prec = 0x40; goto DO_BR_BINOP; + + case CEE_BGT: + case CEE_BGT_S: + name = ">"; prec = 0x40; goto DO_BR_BINOP; + case CEE_BGT_UN: + case CEE_BGT_UN_S: + name = ">un"; prec = 0x40; goto DO_BR_BINOP; + case CEE_BLE: + case CEE_BLE_S: + name = "<="; prec = 0x40; goto DO_BR_BINOP; + case CEE_BLE_UN: + case CEE_BLE_UN_S: + name = "<=un"; prec = 0x40; goto DO_BR_BINOP; + case CEE_BLT: + case CEE_BLT_S: + name = "<"; prec = 0x40; goto DO_BR_BINOP; + case CEE_BLT_UN: + case CEE_BLT_UN_S: + name = "<un"; prec = 0x40; goto DO_BR_BINOP; + case CEE_BNE_UN: + case CEE_BNE_UN_S: + name = "!=un"; prec = 0x40; goto DO_BR_BINOP; + DO_BR_BINOP: + rhs = pop(prec); + lhs = pop(prec-1); + result << "if (" << lhs->val() << ' ' << name << ' ' << rhs->val() << ") "; + goto DO_BR; + + case CEE_LEAVE_S: + case CEE_LEAVE: + while (stackDepth() > 0) { + lhs = pop(); + *lhs << '\n' << result; // put the result in front of anything else + result.swap(*lhs); + } + /* fall through */ + case CEE_BR_S: + case CEE_BR: + DO_BR: { + size_t target = (instrPtr - start) + inlineArg.i; + setTarget(target, stackDepth()); + result << "goto IL_"; result.hex(static_cast<unsigned __int64>(target), 4, OutString::zeroFill); + } goto DO_STMT; + + case CEE_BRFALSE_S: + case CEE_BRFALSE: + name = "!"; + goto DO_BR_UNOP; + case CEE_BRTRUE_S: + case CEE_BRTRUE: + name = ""; + DO_BR_UNOP: + lhs = pop(); + result << "if (" << name << lhs->val() << ") "; + goto DO_BR; + + case CEE_OR: + name = "|"; prec = 0x20; goto DO_BINOP; + case CEE_XOR: + name = "^"; prec = 0x20; goto DO_BINOP; + case CEE_AND: + name = "&"; prec = 0x30; goto DO_BINOP; + case CEE_SHL: + name = "<<"; prec = 0x50; goto DO_BINOP; + case CEE_SHR: + name = ">>"; prec = 0x50; goto DO_BINOP; + case CEE_SHR_UN: + name = ">>un"; prec = 0x50; goto DO_BINOP; + case CEE_CEQ: + name = "=="; prec = 0x40; goto DO_BINOP; + case CEE_CGT: + name = ">"; prec = 0x40; goto DO_BINOP; + case CEE_CGT_UN: + name = ">un"; prec = 0x40; goto DO_BINOP; + case CEE_CLT: + name = "<"; prec = 0x40; goto DO_BINOP; + case CEE_CLT_UN: + name = "<un"; prec = 0x40; goto DO_BINOP; + case CEE_ADD: + name = "+"; prec = 0x60; goto DO_BINOP; + case CEE_ADD_OVF: + name = "+ovf"; prec = 0x60; goto DO_BINOP; + case CEE_ADD_OVF_UN: + name = "+ovf.un";prec = 0x60; goto DO_BINOP; + case CEE_SUB: + name = "-"; prec = 0x60; goto DO_BINOP; + case CEE_SUB_OVF: + name = "-ovf"; prec = 0x60; goto DO_BINOP; + case CEE_SUB_OVF_UN: + name = "-ovf.un";prec = 0x60; goto DO_BINOP; + case CEE_MUL: + name = "*"; prec = 0x70; goto DO_BINOP; + case CEE_MUL_OVF: + name = "*ovf"; prec = 0x70; goto DO_BINOP; + case CEE_MUL_OVF_UN: + name = "*ovf.un";prec = 0x70; goto DO_BINOP; + case CEE_DIV: + name = "/"; prec = 0x70; goto DO_BINOP; + case CEE_DIV_UN: + name = "/un"; prec = 0x70; goto DO_BINOP; + case CEE_REM: + name = "%"; prec = 0x70; goto DO_BINOP; + case CEE_REM_UN: + name = "%un"; prec = 0x70; goto DO_BINOP; + DO_BINOP: + rhs = pop(prec); + lhs = pop(prec-1); + result << lhs->val() << ' ' << name << ' ' << rhs->val(); + goto DO_PUSH; + + case CEE_NOT: + name = "~"; prec = 0x80; goto DO_UNOP; + case CEE_NEG: + name = "-"; prec = 0x80; goto DO_UNOP; + DO_UNOP: + lhs = pop(prec-1); + result << name << lhs->val(); + goto DO_PUSH; + + case CEE_RET: + _ASSERTE(stackDepth() <= 1); + result << "return"; + if (stackDepth() > 0) { + lhs = pop(); + result << ' ' << lhs->val(); + } + goto DO_STMT; + + case CEE_POP: + lhs = pop(); + result.swap(*lhs); + goto DO_STMT; + + case CEE_DUP: + spillStack(out); + lhs = top(); + result << lhs->val(); + prec = 0x1000; // spillstack makes them temps, so they have high prec + goto DO_PUSH; + + case CEE_LDFLDA: + name = "&"; + goto DO_LDFLD_LDFLDA; + case CEE_LDFLD: + name = ""; + DO_LDFLD_LDFLDA: + prec = 0x110; + lhs = pop(prec-1); + result << name << lhs->val() << '.'; + formatInstrArgs(op, inlineArg, &result); + goto DO_PUSH; + + case CEE_LDSFLDA: + name = "&"; + goto DO_LDSFLD_LDSFLDA; + case CEE_LDSFLD: + name = ""; + DO_LDSFLD_LDSFLDA: + prec = 0x1000; + result << name; + formatInstrArgs(op, inlineArg, &result); + goto DO_PUSH; + + case CEE_STFLD: + rhs = pop(0x10); + lhs = pop(0x110-1); + result << lhs->val() << '.'; + formatInstrArgs(op, inlineArg, &result); + result << " = " << rhs->val(); + goto DO_STMT; + + case CEE_STSFLD: + rhs = pop(0x20); + formatInstrArgs(op, inlineArg, &result); + result << " = " << rhs->val(); + goto DO_STMT; + + case CEE_CALLI: + lhs = pop(); + result << "CALLI<" << lhs->val() << '>'; + goto DO_CALL; + + case CEE_NEWOBJ: + result << "new "; + // FALL THROUGH + case CEE_CALL: + case CEE_CALLVIRT: { + formatInstrArgs(op, inlineArg, &result); + + DO_CALL: + // Get the signature stuff + PCCOR_SIGNATURE sig; + ULONG cSig; + HRESULT hr; + if (TypeFromToken(inlineArg.i) == mdtMethodDef) + hr = meta->GetMethodProps(mdMethodDef(inlineArg.i), 0, 0, 0, 0, 0, &sig, &cSig, 0, 0); + else if (TypeFromToken(inlineArg.i) == mdtMemberRef) + hr = meta->GetMemberRefProps(mdMemberRef(inlineArg.i), 0, 0, 0, 0, &sig, &cSig); + else + hr = meta->GetSigFromToken(mdSignature(inlineArg.i), &sig, &cSig); + _ASSERTE(SUCCEEDED(hr)); + unsigned callConv = CorSigUncompressData(sig); + unsigned hasThis = callConv & IMAGE_CEE_CS_CALLCONV_HASTHIS; + if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + CorSigUncompressData(sig); + } + unsigned numArgs = CorSigUncompressData(sig); + while(*sig == ELEMENT_TYPE_CMOD_REQD || *sig == ELEMENT_TYPE_CMOD_OPT) { + sig++; + CorSigUncompressToken(sig); + } + + formatArgs(numArgs, &result); + if (hasThis && op.getOpcode() != CEE_NEWOBJ) { + lhs = pop(0x90); + result.swap(*lhs); + result << '.' << lhs->val(); + } + prec = 0x1000; + if (op.getOpcode() == CEE_NEWOBJ || *sig != ELEMENT_TYPE_VOID) + goto DO_PUSH; + } goto DO_STMT; + + case CEE_LDELEM_I1: + case CEE_LDELEM_I2: + case CEE_LDELEM_I4: + case CEE_LDELEM_I8: + case CEE_LDELEM_REF: + case CEE_LDELEM_R4: + case CEE_LDELEM_R8: + case CEE_LDELEM_U1: + case CEE_LDELEM_U2: + case CEE_LDELEM_I: + rhs = pop(0x100); + lhs = pop(); + result << lhs->val() << '[' << rhs->val() << ']'; + prec = 0x100; + goto DO_PUSH; + + case CEE_STELEM_I1: + case CEE_STELEM_I2: + case CEE_STELEM_I4: + case CEE_STELEM_I8: + case CEE_STELEM_REF: + case CEE_STELEM_R4: + case CEE_STELEM_R8: + case CEE_STELEM_I: + rhs = pop(0x100); + idx = pop(); + lhs = pop(0x20); + result << lhs->val() << '[' << idx->val() << "] = " << rhs->val(); + goto DO_STMT; + + case CEE_LDIND_I1: name = "I1"; goto DO_LDIND; + case CEE_LDIND_I2: name = "I2"; goto DO_LDIND; + case CEE_LDIND_I4: name = "I4"; goto DO_LDIND; + case CEE_LDIND_I8: name = "I8"; goto DO_LDIND; + case CEE_LDIND_I: name = "I"; goto DO_LDIND; + case CEE_LDIND_R4: name = "R4"; goto DO_LDIND; + case CEE_LDIND_R8: name = "R8"; goto DO_LDIND; + case CEE_LDIND_U1: name = "U1"; goto DO_LDIND; + case CEE_LDIND_U2: name = "U2"; goto DO_LDIND; + case CEE_LDIND_REF: name = "REF";goto DO_LDIND; + DO_LDIND: + prec = 0x90; + lhs = pop(prec); + result << name << "(*" << lhs->val() << ')'; + goto DO_PUSH; + + case CEE_STIND_I1: name = "I1"; goto DO_STIND; + case CEE_STIND_I2: name = "I2"; goto DO_STIND; + case CEE_STIND_I4: name = "I4"; goto DO_STIND; + case CEE_STIND_I8: name = "I8"; goto DO_STIND; + case CEE_STIND_REF: name = "REF";goto DO_STIND; + case CEE_STIND_R4: name = "R4"; goto DO_STIND; + case CEE_STIND_R8: name = "R8"; goto DO_STIND; + DO_STIND: + rhs = pop(); + lhs = pop(0x90); + result << '*' << lhs->val() << " = " << name << '(' << rhs->val() << ')'; + goto DO_STMT; + + case CEE_LDVIRTFTN: + case CEE_ARGLIST: + case CEE_BREAK: + case CEE_ENDFILTER: + case CEE_CPBLK: + case CEE_INITBLK: + case CEE_LDOBJ: + case CEE_CPOBJ: + case CEE_STOBJ: + case CEE_INITOBJ: + case CEE_LOCALLOC: + case CEE_NOP: + case CEE_SWITCH: + case CEE_CASTCLASS: + case CEE_ISINST: + case CEE_LDLEN: + case CEE_JMP: + case CEE_NEWARR: + case CEE_THROW: + case CEE_RETHROW: + case CEE_LDELEM_U4: + case CEE_LDIND_U4: + case CEE_LDELEMA: + case CEE_ENDFINALLY: + case CEE_STIND_I: + case CEE_CKFINITE: + case CEE_MKREFANY: + case CEE_REFANYTYPE: + case CEE_REFANYVAL: + case CEE_CONV_I1: + case CEE_CONV_I2: + case CEE_CONV_I4: + case CEE_CONV_I8: + case CEE_CONV_R4: + case CEE_CONV_R8: + case CEE_CONV_R_UN: + case CEE_CONV_OVF_I_UN: + case CEE_CONV_OVF_I1_UN: + case CEE_CONV_OVF_I2_UN: + case CEE_CONV_OVF_I4_UN: + case CEE_CONV_OVF_I8_UN: + case CEE_CONV_OVF_U_UN: + case CEE_CONV_OVF_U1_UN: + case CEE_CONV_OVF_U2_UN: + case CEE_CONV_OVF_U4_UN: + case CEE_CONV_OVF_U8_UN: + case CEE_CONV_OVF_I1: + case CEE_CONV_OVF_I2: + case CEE_CONV_OVF_I4: + case CEE_CONV_OVF_I8: + case CEE_CONV_OVF_U1: + case CEE_CONV_OVF_U2: + case CEE_CONV_OVF_U4: + case CEE_CONV_OVF_U8: + case CEE_CONV_U4: + case CEE_CONV_U8: + case CEE_CONV_U2: + case CEE_CONV_U1: + case CEE_CONV_I: + case CEE_CONV_OVF_I: + case CEE_CONV_OVF_U: + case CEE_CONV_U: + case CEE_BOX: + case CEE_LDELEM: + case CEE_STELEM: + case CEE_UNBOX_ANY: + case CEE_UNBOX: + case CEE_LDFTN: + case CEE_LDTOKEN: + case CEE_SIZEOF: + default: + result << op.getName(); + if (op.getArgsInfo() != InlineNone) { + result << '<'; + formatInstrArgs(op, inlineArg, &result, instrPtr-start); + result << '>'; + } + + _ASSERTE(op.getNumPop() >= 0); + if (op.getNumPop() > 0) + formatArgs(op.getNumPop(), &result); + + prec = 0x1000; + _ASSERTE(op.getNumPush() == 0 || op.getNumPush() == 1); + if (op.getNumPush() > 0) + goto DO_PUSH; + goto DO_STMT; + } + } + return(instrPtr); +} +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + + diff --git a/src/utilcode/jitperf.cpp b/src/utilcode/jitperf.cpp new file mode 100644 index 0000000000..1630afa343 --- /dev/null +++ b/src/utilcode/jitperf.cpp @@ -0,0 +1,132 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#include "stdafx.h" +#include "jitperf.h" +#include "perflog.h" +#include "clrhost.h" +#include "contract.h" + +//============================================================================= +// ALL THE JIT PERF STATS GATHERING CODE IS COMPILED ONLY IF THE ENABLE_JIT_PERF WAS DEFINED. +#if defined(ENABLE_JIT_PERF) + +__int64 g_JitCycles = 0; +size_t g_NonJitCycles = 0; +CRITSEC_COOKIE g_csJit; +__int64 g_tlsJitCycles = 0; +int g_fJitPerfOn; + +size_t g_dwTlsx86CodeSize = 0; +size_t g_TotalILCodeSize = 0; +size_t g_Totalx86CodeSize = 0; +size_t g_TotalMethodsJitted = 0; + +void OutputStats () +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + } CONTRACTL_END; + + LARGE_INTEGER cycleFreq; + if (QueryPerformanceFrequency (&cycleFreq)) + { + double dJitC = (double) g_JitCycles; + double dNonJitC = (double) g_NonJitCycles; + double dFreq = (double)cycleFreq.QuadPart; + double compileSpeed = (double)g_TotalILCodeSize/(dJitC/dFreq); + + PERFLOG((W("Jit Cycles"), (dJitC - dNonJitC), CYCLES)); + PERFLOG((W("Jit Time"), (dJitC - dNonJitC)/dFreq, SECONDS)); + PERFLOG((W("Non Jit Cycles"), dNonJitC, CYCLES)); + PERFLOG((W("Non Jit Time"), dNonJitC/dFreq, SECONDS)); + PERFLOG((W("Total Jit Cycles"), dJitC, CYCLES)); + PERFLOG((W("Total Jit Time"), dJitC/dFreq, SECONDS)); + PERFLOG((W("Methods Jitted"), (UINT_PTR)g_TotalMethodsJitted, COUNT)); + PERFLOG((W("IL Code Compiled"), (UINT_PTR)g_TotalILCodeSize, BYTES)); + PERFLOG((W("X86 Code Emitted"), (UINT_PTR)g_Totalx86CodeSize, BYTES)); + // Included the perf counter description in this case because its not obvious what we are reporting. + PERFLOG((W("ExecTime"), compileSpeed/1000, KBYTES_PER_SEC, W("IL Code compiled/sec"))); + } +} + +void InitJitPerf(void) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + } CONTRACTL_END; + + wchar_t lpszValue[2]; + DWORD cchValue = 2; + + g_fJitPerfOn = WszGetEnvironmentVariable (W("JIT_PERF_OUTPUT"), lpszValue, cchValue); + if (g_fJitPerfOn) + { + g_csJit = ClrCreateCriticalSection(CrstJitPerf,CRST_UNSAFE_ANYMODE); + } +} + +void DoneJitPerfStats() +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + } CONTRACTL_END; + + if (g_fJitPerfOn) + { + ClrDeleteCriticalSection(g_csJit); + + // Output stats to stdout and if necessary to the perf automation file. + OutputStats(); + } + + +} + +void StartNonJITPerfWorker(LARGE_INTEGER * pCycleStart) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + } CONTRACTL_END; + + pCycleStart->QuadPart = 0; + + size_t pTlsNonJitCycles = (size_t) ClrFlsGetValue (TlsIdx_JitPerf); + if ((pTlsNonJitCycles & 1) == 0 ) { /* odd value indicates we are in the EE */ + ClrFlsSetValue(TlsIdx_JitPerf, (LPVOID)(pTlsNonJitCycles + 1)); + QueryPerformanceCounter(pCycleStart); + } +} + +void StopNonJITPerfWorker(LARGE_INTEGER * pCycleStart) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + } CONTRACTL_END; + + LARGE_INTEGER CycleStop; + + if (pCycleStart->QuadPart != 0 && QueryPerformanceCounter(&CycleStop) ) { + size_t pTlsNonJitCycles = (size_t)ClrFlsGetValue (TlsIdx_JitPerf); + pTlsNonJitCycles += static_cast<size_t>(CycleStop.QuadPart - pCycleStart->QuadPart); + pTlsNonJitCycles &= ~1; /* even indicate we are not in EE */ + ClrFlsSetValue(TlsIdx_JitPerf, (LPVOID)(pTlsNonJitCycles)); + } +} + + +#endif //ENABLE_JIT_PERF + + diff --git a/src/utilcode/lazycow.cpp b/src/utilcode/lazycow.cpp new file mode 100644 index 0000000000..9a5fcfbf1c --- /dev/null +++ b/src/utilcode/lazycow.cpp @@ -0,0 +1,315 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// -------------------------------------------------------------------------------- +// LazyCOW.cpp +// + +#include "pedecoder.h" +#include "volatile.h" +#include "lazycow.h" + +#ifdef FEATURE_LAZY_COW_PAGES + +// +// We can't use the hosted ClrVirtualProtect, because EnsureWritablePages is called in places where we can't +// call into the host. +// +#ifdef VirtualProtect +#undef VirtualProtect +#endif + +#ifdef VirtualAlloc +#undef VirtualAlloc +#endif + +#ifdef VirtualFree +#undef VirtualFree +#endif + +LONG* g_pCOWPageMap; // one bit for each page in the virtual address space +LONG g_COWPageMapMap; // one bit for each page in g_pCOWPageMap. + +LONG* EnsureCOWPageMapAllocated() +{ + if (g_pCOWPageMap == NULL) + { + // We store one bit for every page in the virtual address space. We may need to revisit this for 64-bit. :) + MEMORYSTATUSEX stats; + stats.dwLength = sizeof(stats); + if (GlobalMemoryStatusEx(&stats)) + { + _ASSERTE(stats.ullTotalVirtual < 0x100000000ULL); + + SIZE_T mapSize = (SIZE_T)((stats.ullTotalVirtual / PAGE_SIZE) / 8); + _ASSERTE(mapSize / PAGE_SIZE <= 32); // g_COWPageMapMap can only track 32 pages + + // Note that VirtualAlloc will zero-fill the pages for us. + LONG* pMap = (LONG*)VirtualAlloc( + NULL, + mapSize, + MEM_RESERVE, + PAGE_READWRITE); + + if (pMap != NULL) + { + if (NULL != InterlockedCompareExchangeT(&g_pCOWPageMap, pMap, NULL)) + VirtualFree(pMap, 0, MEM_RELEASE); + } + } + } + return g_pCOWPageMap; +} + +bool EnsureCOWPageMapElementAllocated(LONG* elem) +{ + _ASSERTE(elem > g_pCOWPageMap); + _ASSERTE(g_pCOWPageMap != NULL); + + size_t offset = (size_t)elem - (size_t)g_pCOWPageMap; + size_t page = offset / PAGE_SIZE; + + _ASSERTE(page < 32); + int bit = (int)(1 << page); + + if (!(g_COWPageMapMap & bit)) + { + if (!VirtualAlloc(elem, 1, MEM_COMMIT, PAGE_READWRITE)) + return false; + + InterlockedOr(&g_COWPageMapMap, bit); + } + + return true; +} + +bool IsCOWPageMapElementAllocated(LONG* elem) +{ + _ASSERTE(elem >= g_pCOWPageMap); + _ASSERTE(g_pCOWPageMap != NULL); + + size_t offset = (size_t)elem - (size_t)g_pCOWPageMap; + size_t page = offset / PAGE_SIZE; + + _ASSERTE(page < 32); + int bit = (int)(1 << page); + + return (g_COWPageMapMap & bit) != 0; +} + +bool SetCOWPageBits(BYTE* pStart, size_t len, bool value) +{ + _ASSERTE(len > 0); + + // we don't need a barrier here, since: + // a) all supported hardware maintains ordering of dependent reads + // b) it's ok if additional reads happen, because this never changes + // once initialized. + LONG* pCOWPageMap = g_pCOWPageMap; + + // + // Write the bits in 32-bit chunks, to avoid doing one interlocked instruction for each bit. + // + size_t page = (size_t)pStart / PAGE_SIZE; + size_t lastPage = (size_t)(pStart+len-1) / PAGE_SIZE; + size_t elem = page / 32; + LONG bits = 0; + do + { + bits |= 1 << (page % 32); + + ++page; + + // + // if we've moved to a new element of the map, or we've covered every page, + // we need to write out the already-accumulated element. + // + size_t newElem = page / 32; + if (page > lastPage || newElem != elem) + { + LONG* pElem = &pCOWPageMap[elem]; + if (!EnsureCOWPageMapElementAllocated(pElem)) + return false; + + if (value) + InterlockedOr(&pCOWPageMap[elem], bits); + else + InterlockedAnd(&pCOWPageMap[elem], ~bits); + + elem = newElem; + bits = 0; + } + } + while (page <= lastPage); + + return true; +} + +bool SetCOWPageBitsForImage(PEDecoder * pImage, bool value) +{ + if (!pImage->HasNativeHeader()) + return true; + + bool success = true; + + IMAGE_SECTION_HEADER* pSection; + BYTE * pStart; + size_t len; + + pSection = pImage->FindSection(".data"); + if (pSection != NULL) + { + pStart = (BYTE*) dac_cast<TADDR>(pImage->GetBase()) + pSection->VirtualAddress; + len = pSection->Misc.VirtualSize; + + if (!SetCOWPageBits(pStart, len, value)) + success = false; + } + + pSection = pImage->FindSection(".xdata"); + if (pSection != NULL) + { + pStart = (BYTE*) dac_cast<TADDR>(pImage->GetBase()) + pSection->VirtualAddress; + len = pSection->Misc.VirtualSize; + + if (!SetCOWPageBits(pStart, len, value)) + success = false; + } + + return success; +} + +bool AreAnyCOWPageBitsSet(BYTE* pStart, size_t len) +{ + LONG* pCOWPageMap = g_pCOWPageMap; + if (pCOWPageMap == NULL) + return false; + + _ASSERTE(len > 0); + size_t page = (size_t)pStart / PAGE_SIZE; + size_t lastPage = (size_t)(pStart+len-1) / PAGE_SIZE; + do + { + LONG* pElem = &pCOWPageMap[page / 32]; + if (IsCOWPageMapElementAllocated(pElem) && + (*pElem & (1 << (page %32)))) + { + return true; + } + ++page; + } + while (page <= lastPage); + + return false; +} + + +void AllocateLazyCOWPages(PEDecoder * pImage) +{ + // + // Note: it's ok to call AllocateLazyCOWPages multiple times for the same loaded image. + // This will result in any already-writable pages being incorrectly marked as read-only + // in our records, but that just means we'll call VirtualProtect one more time. + // + // However, FreeLazyCOWPages must be called just once per loaded image, when it is actually. + // unloaded. Otherwise we will lose track of the COW pages in that image while it might + // still be accessible. + // + + if (!EnsureCOWPageMapAllocated()) + ThrowOutOfMemory(); + + if (!SetCOWPageBitsForImage(pImage, true)) + ThrowOutOfMemory(); +} + +void FreeLazyCOWPages(PEDecoder * pImage) +{ + // + // Must be called just once per image; see note in AllocateLazyCOWPages. + // + SetCOWPageBitsForImage(pImage, false); +} + +bool IsInReadOnlyLazyCOWPage(void* p) +{ + return AreAnyCOWPageBitsSet((BYTE*)p, 1); +} + + +bool MakeWritable(BYTE* pStart, size_t len, DWORD protect) +{ + DWORD oldProtect; + if (!VirtualProtect(pStart, len, protect, &oldProtect)) + return false; + INDEBUG(bool result = ) SetCOWPageBits(pStart, len, false); + _ASSERTE(result); // we already set these, so we must be able to clear them. + return true; +} + +bool EnsureWritablePagesNoThrow(void* p, size_t len) +{ + BYTE* pStart = (BYTE*)p; + + if (len == 0) + return true; + + if (!AreAnyCOWPageBitsSet(pStart, len)) + return true; + + return MakeWritable(pStart, len, PAGE_READWRITE); +} + +void EnsureWritablePages(void* p, size_t len) +{ + CONTRACTL { + THROWS; + } CONTRACTL_END; + + BYTE* pStart = (BYTE*)p; + + if (len == 0) + return; + + if (!AreAnyCOWPageBitsSet(pStart, len)) + return; + + if (!MakeWritable(pStart, len, PAGE_READWRITE)) + ThrowOutOfMemory(); +} + +bool EnsureWritableExecutablePagesNoThrow(void* p, size_t len) +{ + BYTE* pStart = (BYTE*) p; + + if (len == 0) + return true; + + if (!AreAnyCOWPageBitsSet(pStart, len)) + return true; + + return MakeWritable(pStart, len, PAGE_EXECUTE_READWRITE); +} + +void EnsureWritableExecutablePages(void* p, size_t len) +{ + CONTRACTL { + THROWS; + } CONTRACTL_END; + + BYTE* pStart = (BYTE*) p; + + if (len == 0) + return; + + if (!AreAnyCOWPageBitsSet(pStart, len)) + return; + + if (!MakeWritable(pStart, len, PAGE_EXECUTE_READWRITE)) + ThrowOutOfMemory(); +} + +#endif // FEATURE_LAZY_COW_PAGES + + diff --git a/src/utilcode/loaderheap.cpp b/src/utilcode/loaderheap.cpp new file mode 100644 index 0000000000..caa01b7064 --- /dev/null +++ b/src/utilcode/loaderheap.cpp @@ -0,0 +1,2262 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#include "stdafx.h" // Precompiled header key. +#include "loaderheap.h" +#include "perfcounters.h" +#include "ex.h" +#include "pedecoder.h" +#define DONOT_DEFINE_ETW_CALLBACK +#include "eventtracebase.h" + +#ifndef DACCESS_COMPILE + +INDEBUG(DWORD UnlockedLoaderHeap::s_dwNumInstancesOfLoaderHeaps = 0;) + +#ifdef RANDOMIZE_ALLOC +#include <time.h> +static class Random +{ +public: + Random() { seed = (unsigned int)time(NULL); } + unsigned int Next() + { + return ((seed = seed * 214013L + 2531011L) >> 16) & 0x7fff; + } +private: + unsigned int seed; +} s_random; +#endif + +namespace +{ +#if !defined(SELF_NO_HOST) // ETW available only in the runtime + inline void EtwAllocRequest(UnlockedLoaderHeap * const pHeap, void* ptr, size_t dwSize) + { + FireEtwAllocRequest(pHeap, ptr, static_cast<unsigned int>(dwSize), 0, 0, GetClrInstanceId()); + } +#else +#define EtwAllocRequest(pHeap, ptr, dwSize) ((void)0) +#endif // SELF_NO_HOST +} + +// +// RangeLists are constructed so they can be searched from multiple +// threads without locking. They do require locking in order to +// be safely modified, though. +// + +RangeList::RangeList() +{ + WRAPPER_NO_CONTRACT; + + InitBlock(&m_starterBlock); + + m_firstEmptyBlock = &m_starterBlock; + m_firstEmptyRange = 0; +} + +RangeList::~RangeList() +{ + LIMITED_METHOD_CONTRACT; + + RangeListBlock *b = m_starterBlock.next; + + while (b != NULL) + { + RangeListBlock *bNext = b->next; + delete b; + b = bNext; + } +} + +void RangeList::InitBlock(RangeListBlock *b) +{ + LIMITED_METHOD_CONTRACT; + + Range *r = b->ranges; + Range *rEnd = r + RANGE_COUNT; + while (r < rEnd) + r++->id = NULL; + + b->next = NULL; +} + +BOOL RangeList::AddRangeWorker(const BYTE *start, const BYTE *end, void *id) +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + INJECT_FAULT(return FALSE;); + } + CONTRACTL_END + + _ASSERTE(id != NULL); + + RangeListBlock *b = m_firstEmptyBlock; + Range *r = b->ranges + m_firstEmptyRange; + Range *rEnd = b->ranges + RANGE_COUNT; + + while (TRUE) + { + while (r < rEnd) + { + if (r->id == NULL) + { + r->start = (TADDR)start; + r->end = (TADDR)end; + r->id = (TADDR)id; + + r++; + + m_firstEmptyBlock = b; + m_firstEmptyRange = r - b->ranges; + + return TRUE; + } + r++; + } + + // + // If there are no more blocks, allocate a + // new one. + // + + if (b->next == NULL) + { + RangeListBlock *newBlock = new (nothrow) RangeListBlock; + + if (newBlock == NULL) + { + m_firstEmptyBlock = b; + m_firstEmptyRange = r - b->ranges; + return FALSE; + } + + InitBlock(newBlock); + + newBlock->next = NULL; + b->next = newBlock; + } + + // + // Next block + // + + b = b->next; + r = b->ranges; + rEnd = r + RANGE_COUNT; + } +} + +void RangeList::RemoveRangesWorker(void *id, const BYTE* start, const BYTE* end) +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + } + CONTRACTL_END + + RangeListBlock *b = &m_starterBlock; + Range *r = b->ranges; + Range *rEnd = r + RANGE_COUNT; + + // + // Find the first free element, & mark it. + // + + while (TRUE) + { + // + // Clear entries in this block. + // + + while (r < rEnd) + { + if (r->id != NULL) + { + if (start != NULL) + { + _ASSERTE(end != NULL); + + if (r->start >= (TADDR)start && r->start < (TADDR)end) + { + CONSISTENCY_CHECK_MSGF(r->end >= (TADDR)start && + r->end <= (TADDR)end, + ("r: %p start: %p end: %p", r, start, end)); + r->id = NULL; + } + } + else if (r->id == (TADDR)id) + { + r->id = NULL; + } + } + + r++; + } + + // + // If there are no more blocks, we're done. + // + + if (b->next == NULL) + { + m_firstEmptyRange = 0; + m_firstEmptyBlock = &m_starterBlock; + + return; + } + + // + // Next block. + // + + b = b->next; + r = b->ranges; + rEnd = r + RANGE_COUNT; + } +} + +#endif // #ifndef DACCESS_COMPILE + +BOOL RangeList::IsInRangeWorker(TADDR address, TADDR *pID /* = NULL */) +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + FORBID_FAULT; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACTL_END + + SUPPORTS_DAC; + + RangeListBlock* b = &m_starterBlock; + Range* r = b->ranges; + Range* rEnd = r + RANGE_COUNT; + + // + // Look for a matching element + // + + while (TRUE) + { + while (r < rEnd) + { + if (r->id != NULL && + address >= r->start + && address < r->end) + { + if (pID != NULL) + { + *pID = r->id; + } + return TRUE; + } + r++; + } + + // + // If there are no more blocks, we're done. + // + + if (b->next == NULL) + return FALSE; + + // + // Next block. + // + + b = b->next; + r = b->ranges; + rEnd = r + RANGE_COUNT; + } +} + +#ifdef DACCESS_COMPILE + +void +RangeList::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + WRAPPER_NO_CONTRACT; + + // This class is almost always contained in something + // else so there's no enumeration of 'this'. + + RangeListBlock* block = &m_starterBlock; + block->EnumMemoryRegions(flags); + + while (block->next.IsValid()) + { + block->next.EnumMem(); + block = block->next; + + block->EnumMemoryRegions(flags); + } +} + +void +RangeList::RangeListBlock::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) +{ + WRAPPER_NO_CONTRACT; + + Range* range; + TADDR BADFOOD; + TSIZE_T size; + int i; + + // The code below iterates each range stored in the RangeListBlock and + // dumps the memory region represented by each range. + // It is too much memory for a mini-dump, so we just bail out for mini-dumps. + if (flags == CLRDATA_ENUM_MEM_MINI || flags == CLRDATA_ENUM_MEM_TRIAGE) + { + return; + } + + WIN64_ONLY( BADFOOD = 0xbaadf00dbaadf00d; ); + NOT_WIN64( BADFOOD = 0xbaadf00d; ); + + for (i=0; i<RANGE_COUNT; i++) + { + range = &(this->ranges[i]); + if (range->id == NULL || range->start == NULL || range->end == NULL || + // just looking at the lower 4bytes is good enough on WIN64 + range->start == BADFOOD || range->end == BADFOOD) + { + break; + } + + size = range->end - range->start; + _ASSERTE( size < ULONG_MAX ); // ranges should be less than 4gig! + + // We can't be sure this entire range is mapped. For example, the code:StubLinkStubManager + // keeps track of all ranges in the code:BaseDomain::m_pStubHeap LoaderHeap, and + // code:LoaderHeap::UnlockedReservePages adds a range for the entire reserved region, instead + // of updating the RangeList when pages are committed. But in that case, the committed region of + // memory will be enumerated by the LoaderHeap anyway, so it's OK if this fails + DacEnumMemoryRegion(range->start, size, false); + } +} + +#endif // #ifdef DACCESS_COMPILE + + +//===================================================================================== +// In DEBUG builds only, we tag live blocks with the requested size and the type of +// allocation (AllocMem, AllocAlignedMem, AllocateOntoReservedMem). This is strictly +// to validate that those who call Backout* are passing in the right values. +// +// For simplicity, we'll use one LoaderHeapValidationTag structure for all types even +// though not all fields are applicable to all types. +//===================================================================================== +#ifdef _DEBUG +enum AllocationType +{ + kAllocMem = 1, + kFreedMem = 4, +}; + +struct LoaderHeapValidationTag +{ + size_t m_dwRequestedSize; // What the caller requested (not what was actually allocated) + AllocationType m_allocationType; // Which api allocated this block. + const char * m_szFile; // Who allocated me + int m_lineNum; // Who allocated me + +}; +#endif //_DEBUG + + + + + +//===================================================================================== +// These classes do detailed loaderheap sniffing to help in debugging heap crashes +//===================================================================================== +#ifdef _DEBUG + +// This structure logs the results of an Alloc or Free call. They are stored in reverse time order +// with UnlockedLoaderHeap::m_pEventList pointing to the most recent event. +struct LoaderHeapEvent +{ + LoaderHeapEvent *m_pNext; + AllocationType m_allocationType; //Which api was called + const char *m_szFile; //Caller Id + int m_lineNum; //Caller Id + const char *m_szAllocFile; //(BackoutEvents): Who allocated the block? + int m_allocLineNum; //(BackoutEvents): Who allocated the block? + void *m_pMem; //Starting address of block + size_t m_dwRequestedSize; //Requested size of block + size_t m_dwSize; //Actual size of block (including validation tags, padding, everything) + + + void Describe(SString *pSString) + { + CONTRACTL + { + INSTANCE_CHECK; + DISABLED(NOTHROW); + GC_NOTRIGGER; + } + CONTRACTL_END + + pSString->AppendASCII("\n"); + + { + StackSString buf; + if (m_allocationType == kFreedMem) + { + buf.Printf(" Freed at: %s (line %d)\n", m_szFile, m_lineNum); + buf.Printf(" (block originally allocated at %s (line %d)\n", m_szAllocFile, m_allocLineNum); + } + else + { + buf.Printf(" Allocated at: %s (line %d)\n", m_szFile, m_lineNum); + } + pSString->Append(buf); + } + + if (!QuietValidate()) + { + pSString->AppendASCII(" *** THIS BLOCK HAS BEEN CORRUPTED ***\n"); + } + + + + { + StackSString buf; + buf.Printf(" Type: "); + switch (m_allocationType) + { + case kAllocMem: + buf.AppendASCII("AllocMem()\n"); + break; + case kFreedMem: + buf.AppendASCII("Free\n"); + break; + default: + break; + } + pSString->Append(buf); + } + + + { + StackSString buf; + buf.Printf(" Start of block: 0x%p\n", m_pMem); + pSString->Append(buf); + } + + { + StackSString buf; + buf.Printf(" End of block: 0x%p\n", ((BYTE*)m_pMem) + m_dwSize - 1); + pSString->Append(buf); + } + + { + StackSString buf; + buf.Printf(" Requested size: %lu (0x%lx)\n", (ULONG)m_dwRequestedSize, (ULONG)m_dwRequestedSize); + pSString->Append(buf); + } + + { + StackSString buf; + buf.Printf(" Actual size: %lu (0x%lx)\n", (ULONG)m_dwSize, (ULONG)m_dwSize); + pSString->Append(buf); + } + + pSString->AppendASCII("\n"); + } + + + + BOOL QuietValidate(); + +}; + + +class LoaderHeapSniffer +{ + public: + static DWORD InitDebugFlags() + { + WRAPPER_NO_CONTRACT; + + DWORD dwDebugFlags = 0; + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LoaderHeapCallTracing)) + { + dwDebugFlags |= UnlockedLoaderHeap::kCallTracing; + } + return dwDebugFlags; + } + + + static VOID RecordEvent(UnlockedLoaderHeap *pHeap, + AllocationType allocationType, + __in const char *szFile, + int lineNum, + __in const char *szAllocFile, + int allocLineNum, + void *pMem, + size_t dwRequestedSize, + size_t dwSize + ); + + static VOID ClearEvents(UnlockedLoaderHeap *pHeap) + { + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + LoaderHeapEvent *pEvent = pHeap->m_pEventList; + while (pEvent) + { + LoaderHeapEvent *pNext = pEvent->m_pNext; + delete pEvent; + pEvent = pNext; + } + pHeap->m_pEventList = NULL; + } + + + static VOID CompactEvents(UnlockedLoaderHeap *pHeap) + { + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + LoaderHeapEvent **ppEvent = &(pHeap->m_pEventList); + while (*ppEvent) + { + LoaderHeapEvent *pEvent = *ppEvent; + if (pEvent->m_allocationType != kFreedMem) + { + ppEvent = &(pEvent->m_pNext); + } + else + { + LoaderHeapEvent **ppWalk = &(pEvent->m_pNext); + BOOL fMatchFound = FALSE; + while (*ppWalk && !fMatchFound) + { + LoaderHeapEvent *pWalk = *ppWalk; + if (pWalk->m_allocationType != kFreedMem && + pWalk->m_pMem == pEvent->m_pMem && + pWalk->m_dwRequestedSize == pEvent->m_dwRequestedSize) + { + // Delete matched pairs + + // Order is important here - updating *ppWalk may change pEvent->m_pNext, and we want + // to get the updated value when we unlink pEvent. + *ppWalk = pWalk->m_pNext; + *ppEvent = pEvent->m_pNext; + + delete pEvent; + delete pWalk; + fMatchFound = TRUE; + } + else + { + ppWalk = &(pWalk->m_pNext); + } + } + + if (!fMatchFound) + { + ppEvent = &(pEvent->m_pNext); + } + } + } + } + static VOID PrintEvents(UnlockedLoaderHeap *pHeap) + { + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + printf("\n------------- LoaderHeapEvents (in reverse time order!) --------------------"); + + LoaderHeapEvent *pEvent = pHeap->m_pEventList; + while (pEvent) + { + printf("\n"); + switch (pEvent->m_allocationType) + { + case kAllocMem: printf("AllocMem "); break; + case kFreedMem: printf("BackoutMem "); break; + + } + printf(" ptr = 0x%-8p", pEvent->m_pMem); + printf(" rqsize = 0x%-8x", pEvent->m_dwRequestedSize); + printf(" actsize = 0x%-8x", pEvent->m_dwSize); + printf(" (at %s@%d)", pEvent->m_szFile, pEvent->m_lineNum); + if (pEvent->m_allocationType == kFreedMem) + { + printf(" (original allocation at %s@%d)", pEvent->m_szAllocFile, pEvent->m_allocLineNum); + } + + pEvent = pEvent->m_pNext; + + } + printf("\n------------- End of LoaderHeapEvents --------------------------------------"); + printf("\n"); + + } + + + static VOID PitchSniffer(SString *pSString) + { + WRAPPER_NO_CONTRACT; + pSString->AppendASCII("\n" + "\nBecause call-tracing wasn't turned on, we couldn't provide details about who last owned the affected memory block. To get more precise diagnostics," + "\nset the following registry DWORD value:" + "\n" + "\n HKLM\\Software\\Microsoft\\.NETFramework\\LoaderHeapCallTracing = 1" + "\n" + "\nand rerun the scenario that crashed." + "\n" + "\n"); + } + + static LoaderHeapEvent *FindEvent(UnlockedLoaderHeap *pHeap, void *pAddr) + { + LIMITED_METHOD_CONTRACT; + + LoaderHeapEvent *pEvent = pHeap->m_pEventList; + while (pEvent) + { + if (pAddr >= pEvent->m_pMem && pAddr <= ( ((BYTE*)pEvent->m_pMem) + pEvent->m_dwSize - 1)) + { + return pEvent; + } + pEvent = pEvent->m_pNext; + } + return NULL; + + } + + + static void ValidateFreeList(UnlockedLoaderHeap *pHeap); + + static void WeGotAFaultNowWhat(UnlockedLoaderHeap *pHeap) + { + WRAPPER_NO_CONTRACT; + ValidateFreeList(pHeap); + + //If none of the above popped up an assert, pop up a generic one. + _ASSERTE(!("Unexpected AV inside LoaderHeap. The usual reason is that someone overwrote the end of a block or wrote into a freed block.\n")); + + } + +}; + + +#endif + + +#ifdef _DEBUG +#define LOADER_HEAP_BEGIN_TRAP_FAULT BOOL __faulted = FALSE; EX_TRY { +#define LOADER_HEAP_END_TRAP_FAULT } EX_CATCH {__faulted = TRUE; } EX_END_CATCH(SwallowAllExceptions) if (__faulted) LoaderHeapSniffer::WeGotAFaultNowWhat(pHeap); +#else +#define LOADER_HEAP_BEGIN_TRAP_FAULT +#define LOADER_HEAP_END_TRAP_FAULT +#endif + + +size_t AllocMem_TotalSize(size_t dwRequestedSize, UnlockedLoaderHeap *pHeap); + +//===================================================================================== +// This freelist implementation is a first cut and probably needs to be tuned. +// It should be tuned with the following assumptions: +// +// - Freeing LoaderHeap memory is done primarily for OOM backout. LoaderHeaps +// weren't designed to be general purpose heaps and shouldn't be used that way. +// +// - And hence, when memory is freed, expect it to be freed in large clumps and in a +// LIFO order. Since the LoaderHeap normally hands out memory with sequentially +// increasing addresses, blocks will typically be freed with sequentially decreasing +// addresses. +// +// The first cut of the freelist is a single-linked list of free blocks using first-fit. +// Assuming the above alloc-free pattern holds, the list will end up mostly sorted +// in increasing address order. When a block is freed, we'll attempt to coalesce it +// with the first block in the list. We could also choose to be more aggressive about +// sorting and coalescing but this should probably catch most cases in practice. +//===================================================================================== + +// When a block is freed, we place this structure on the first bytes of the freed block (Allocations +// are bumped in size if necessary to make sure there's room.) +struct LoaderHeapFreeBlock +{ + public: + LoaderHeapFreeBlock *m_pNext; // Pointer to next block on free list + size_t m_dwSize; // Total size of this block (including this header) +//! Try not to grow the size of this structure. It places a minimum size on LoaderHeap allocations. + + static void InsertFreeBlock(LoaderHeapFreeBlock **ppHead, void *pMem, size_t dwTotalSize, UnlockedLoaderHeap *pHeap) + { + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + LOADER_HEAP_BEGIN_TRAP_FAULT + + // It's illegal to insert a free block that's smaller than the minimum sized allocation - + // it may stay stranded on the freelist forever. +#ifdef _DEBUG + if (!(dwTotalSize >= AllocMem_TotalSize(1, pHeap))) + { + LoaderHeapSniffer::ValidateFreeList(pHeap); + _ASSERTE(dwTotalSize >= AllocMem_TotalSize(1, pHeap)); + } + + if (!(0 == (dwTotalSize & ALLOC_ALIGN_CONSTANT))) + { + LoaderHeapSniffer::ValidateFreeList(pHeap); + _ASSERTE(0 == (dwTotalSize & ALLOC_ALIGN_CONSTANT)); + } +#endif + + INDEBUG(memset(pMem, 0xcc, dwTotalSize);) + LoaderHeapFreeBlock *pNewBlock = (LoaderHeapFreeBlock*)pMem; + pNewBlock->m_pNext = *ppHead; + pNewBlock->m_dwSize = dwTotalSize; + *ppHead = pNewBlock; + + MergeBlock(pNewBlock, pHeap); + + LOADER_HEAP_END_TRAP_FAULT + } + + + static void *AllocFromFreeList(LoaderHeapFreeBlock **ppHead, size_t dwSize, BOOL fRemoveFromFreeList, UnlockedLoaderHeap *pHeap) + { + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + INCONTRACT(_ASSERTE_IMPL(!ARE_FAULTS_FORBIDDEN())); + + void *pResult = NULL; + LOADER_HEAP_BEGIN_TRAP_FAULT + + LoaderHeapFreeBlock **ppWalk = ppHead; + while (*ppWalk) + { + LoaderHeapFreeBlock *pCur = *ppWalk; + size_t dwCurSize = pCur->m_dwSize; + if (dwCurSize == dwSize) + { + pResult = pCur; + // Exact match. Hooray! + if (fRemoveFromFreeList) + { + *ppWalk = pCur->m_pNext; + } + break; + } + else if (dwCurSize > dwSize && (dwCurSize - dwSize) >= AllocMem_TotalSize(1, pHeap)) + { + // Partial match. Ok... + pResult = pCur; + if (fRemoveFromFreeList) + { + *ppWalk = pCur->m_pNext; + InsertFreeBlock(ppWalk, ((BYTE*)pCur) + dwSize, dwCurSize - dwSize, pHeap ); + } + break; + } + + // Either block is too small or splitting the block would leave a remainder that's smaller than + // the minimum block size. Onto next one. + + ppWalk = &( pCur->m_pNext ); + } + + if (pResult && fRemoveFromFreeList) + { + // Callers of loaderheap assume allocated memory is zero-inited so we must preserve this invariant! + memset(pResult, 0, dwSize); + } + LOADER_HEAP_END_TRAP_FAULT + return pResult; + + + + } + + + private: + // Try to merge pFreeBlock with its immediate successor. Return TRUE if a merge happened. FALSE if no merge happened. + static BOOL MergeBlock(LoaderHeapFreeBlock *pFreeBlock, UnlockedLoaderHeap *pHeap) + { + STATIC_CONTRACT_NOTHROW; + + BOOL result = FALSE; + + LOADER_HEAP_BEGIN_TRAP_FAULT + + LoaderHeapFreeBlock *pNextBlock = pFreeBlock->m_pNext; + size_t dwSize = pFreeBlock->m_dwSize; + + if (pNextBlock == NULL || ((BYTE*)pNextBlock) != (((BYTE*)pFreeBlock) + dwSize)) + { + result = FALSE; + } + else + { + size_t dwCombinedSize = dwSize + pNextBlock->m_dwSize; + LoaderHeapFreeBlock *pNextNextBlock = pNextBlock->m_pNext; + INDEBUG(memset(pFreeBlock, 0xcc, dwCombinedSize);) + pFreeBlock->m_pNext = pNextNextBlock; + pFreeBlock->m_dwSize = dwCombinedSize; + + result = TRUE; + } + + LOADER_HEAP_END_TRAP_FAULT + return result; + + } + +}; + + + + +//===================================================================================== +// These helpers encapsulate the actual layout of a block allocated by AllocMem +// and UnlockedAllocMem(): +// +// ==> Starting address is always pointer-aligned. +// +// - x bytes of user bytes (where "x" is the actual dwSize passed into AllocMem) +// +// - y bytes of "EE" (DEBUG-ONLY) (where "y" == LOADER_HEAP_DEBUG_BOUNDARY (normally 0)) +// - z bytes of pad (DEBUG-ONLY) (where "z" is just enough to pointer-align the following byte) +// - a bytes of tag (DEBUG-ONLY) (where "a" is sizeof(LoaderHeapValidationTag) +// +// - b bytes of pad (if total size after all this < sizeof(LoaderHeapFreeBlock), pad enough to make it the size of LoaderHeapFreeBlock) +// - c bytes of pad (where "c" is just enough to pointer-align the following byte) +// +// ==> Following address is always pointer-aligned +//===================================================================================== + +// Convert the requested size into the total # of bytes we'll actually allocate (including padding) +inline size_t AllocMem_TotalSize(size_t dwRequestedSize, UnlockedLoaderHeap *pHeap) +{ + LIMITED_METHOD_CONTRACT; + + size_t dwSize = dwRequestedSize; +#ifdef _DEBUG + dwSize += LOADER_HEAP_DEBUG_BOUNDARY; + dwSize = ((dwSize + ALLOC_ALIGN_CONSTANT) & (~ALLOC_ALIGN_CONSTANT)); + + if (!pHeap->m_fExplicitControl) + { + dwSize += sizeof(LoaderHeapValidationTag); + } +#endif + if (!pHeap->m_fExplicitControl) + { + if (dwSize < sizeof(LoaderHeapFreeBlock)) + { + dwSize = sizeof(LoaderHeapFreeBlock); + } + } + dwSize = ((dwSize + ALLOC_ALIGN_CONSTANT) & (~ALLOC_ALIGN_CONSTANT)); + + return dwSize; +} + + +#ifdef _DEBUG +LoaderHeapValidationTag *AllocMem_GetTag(LPVOID pBlock, size_t dwRequestedSize) +{ + LIMITED_METHOD_CONTRACT; + + size_t dwSize = dwRequestedSize; + dwSize += LOADER_HEAP_DEBUG_BOUNDARY; + dwSize = ((dwSize + ALLOC_ALIGN_CONSTANT) & (~ALLOC_ALIGN_CONSTANT)); + return (LoaderHeapValidationTag *)( ((BYTE*)pBlock) + dwSize ); +} +#endif + + + + + +//===================================================================================== +// UnlockedLoaderHeap methods +//===================================================================================== + +#ifndef DACCESS_COMPILE + +UnlockedLoaderHeap::UnlockedLoaderHeap(DWORD dwReserveBlockSize, + DWORD dwCommitBlockSize, + const BYTE* dwReservedRegionAddress, + SIZE_T dwReservedRegionSize, + size_t *pPrivatePerfCounter_LoaderBytes, + RangeList *pRangeList, + BOOL fMakeExecutable) +{ + CONTRACTL + { + CONSTRUCTOR_CHECK; + NOTHROW; + FORBID_FAULT; + } + CONTRACTL_END; + + m_pCurBlock = NULL; + m_pFirstBlock = NULL; + + m_dwReserveBlockSize = dwReserveBlockSize; + m_dwCommitBlockSize = dwCommitBlockSize; + + m_pPtrToEndOfCommittedRegion = NULL; + m_pEndReservedRegion = NULL; + m_pAllocPtr = NULL; + + m_pRangeList = pRangeList; + + // Round to VIRTUAL_ALLOC_RESERVE_GRANULARITY + m_dwTotalAlloc = 0; + +#ifdef _DEBUG + m_dwDebugWastedBytes = 0; + s_dwNumInstancesOfLoaderHeaps++; + m_pEventList = NULL; + m_dwDebugFlags = LoaderHeapSniffer::InitDebugFlags(); + m_fPermitStubsWithUnwindInfo = FALSE; + m_fStubUnwindInfoUnregistered= FALSE; +#endif + + m_pPrivatePerfCounter_LoaderBytes = pPrivatePerfCounter_LoaderBytes; + + if (fMakeExecutable) + m_flProtect = PAGE_EXECUTE_READWRITE; + else + m_flProtect = PAGE_READWRITE; + + m_pFirstFreeBlock = NULL; + + if (dwReservedRegionAddress != NULL && dwReservedRegionSize > 0) + { + m_reservedBlock.Init((void *)dwReservedRegionAddress, dwReservedRegionSize, FALSE); + } +} + +// ~LoaderHeap is not synchronised (obviously) +UnlockedLoaderHeap::~UnlockedLoaderHeap() +{ + CONTRACTL + { + DESTRUCTOR_CHECK; + NOTHROW; + FORBID_FAULT; + } + CONTRACTL_END + + _ASSERTE(!m_fPermitStubsWithUnwindInfo || m_fStubUnwindInfoUnregistered); + + if (m_pRangeList != NULL) + m_pRangeList->RemoveRanges((void *) this); + + LoaderHeapBlock *pSearch, *pNext; + + for (pSearch = m_pFirstBlock; pSearch; pSearch = pNext) + { + void * pVirtualAddress; + BOOL fReleaseMemory; + + pVirtualAddress = pSearch->pVirtualAddress; + fReleaseMemory = pSearch->m_fReleaseMemory; + pNext = pSearch->pNext; + + if (fReleaseMemory) + { + BOOL fSuccess; + fSuccess = ClrVirtualFree(pVirtualAddress, 0, MEM_RELEASE); + _ASSERTE(fSuccess); + } + } + + if (m_reservedBlock.m_fReleaseMemory) + { + BOOL fSuccess; + fSuccess = ClrVirtualFree(m_reservedBlock.pVirtualAddress, 0, MEM_RELEASE); + _ASSERTE(fSuccess); + } + + if (m_pPrivatePerfCounter_LoaderBytes) + *m_pPrivatePerfCounter_LoaderBytes = *m_pPrivatePerfCounter_LoaderBytes - (DWORD) m_dwTotalAlloc; + + INDEBUG(s_dwNumInstancesOfLoaderHeaps --;) +} + +void UnlockedLoaderHeap::UnlockedSetReservedRegion(BYTE* dwReservedRegionAddress, SIZE_T dwReservedRegionSize, BOOL fReleaseMemory) +{ + WRAPPER_NO_CONTRACT; + _ASSERTE(m_reservedBlock.pVirtualAddress == NULL); + m_reservedBlock.Init((void *)dwReservedRegionAddress, dwReservedRegionSize, fReleaseMemory); +} + +#endif // #ifndef DACCESS_COMPILE + +#if 0 +// Disables access to all pages in the heap - useful when trying to determine if someone is +// accessing something in the low frequency heap +void UnlockedLoaderHeap::DebugGuardHeap() +{ + WRAPPER_NO_CONTRACT; + LoaderHeapBlock *pSearch, *pNext; + + for (pSearch = m_pFirstBlock; pSearch; pSearch = pNext) + { + void * pResult; + void * pVirtualAddress; + + pVirtualAddress = pSearch->pVirtualAddress; + pNext = pSearch->pNext; + + pResult = ClrVirtualAlloc(pVirtualAddress, pSearch->dwVirtualSize, MEM_COMMIT, PAGE_NOACCESS); + _ASSERTE(pResult != NULL); + } +} +#endif + +size_t UnlockedLoaderHeap::GetBytesAvailCommittedRegion() +{ + LIMITED_METHOD_CONTRACT; + + if (m_pAllocPtr < m_pPtrToEndOfCommittedRegion) + return (size_t)(m_pPtrToEndOfCommittedRegion - m_pAllocPtr); + else + return 0; +} + +size_t UnlockedLoaderHeap::GetBytesAvailReservedRegion() +{ + LIMITED_METHOD_CONTRACT; + + if (m_pAllocPtr < m_pEndReservedRegion) + return (size_t)(m_pEndReservedRegion- m_pAllocPtr); + else + return 0; +} + +#define SETUP_NEW_BLOCK(pData, dwSizeToCommit, dwSizeToReserve) \ + m_pPtrToEndOfCommittedRegion = (BYTE *) (pData) + (dwSizeToCommit); \ + m_pAllocPtr = (BYTE *) (pData) + sizeof(LoaderHeapBlock); \ + m_pEndReservedRegion = (BYTE *) (pData) + (dwSizeToReserve); + + +#ifndef DACCESS_COMPILE + +BOOL UnlockedLoaderHeap::UnlockedReservePages(size_t dwSizeToCommit) +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + INJECT_FAULT(return FALSE;); + } + CONTRACTL_END; + + size_t dwSizeToReserve; + + // Add sizeof(LoaderHeapBlock) + dwSizeToCommit += sizeof(LoaderHeapBlock); + + // Round to page size again + dwSizeToCommit = ALIGN_UP(dwSizeToCommit, PAGE_SIZE); + + void *pData = NULL; + BOOL fReleaseMemory = TRUE; + + // We were provided with a reserved memory block at instance creation time, so use it if it's big enough. + if (m_reservedBlock.pVirtualAddress != NULL && + m_reservedBlock.dwVirtualSize >= dwSizeToCommit) + { + // Get the info out of the block. + pData = m_reservedBlock.pVirtualAddress; + dwSizeToReserve = m_reservedBlock.dwVirtualSize; + fReleaseMemory = m_reservedBlock.m_fReleaseMemory; + + // Zero the block so this memory doesn't get used again. + m_reservedBlock.Init(NULL, 0, FALSE); + } + // The caller is asking us to allocate the memory + else + { + if (m_fExplicitControl) + { + return FALSE; + } + + // Figure out how much to reserve + dwSizeToReserve = max(dwSizeToCommit, m_dwReserveBlockSize); + + // Round to VIRTUAL_ALLOC_RESERVE_GRANULARITY + dwSizeToReserve = ALIGN_UP(dwSizeToReserve, VIRTUAL_ALLOC_RESERVE_GRANULARITY); + + _ASSERTE(dwSizeToCommit <= dwSizeToReserve); + + // + // Reserve pages + // + + pData = ClrVirtualAllocExecutable(dwSizeToReserve, MEM_RESERVE, PAGE_NOACCESS); + if (pData == NULL) + { + return FALSE; + } + } + + // When the user passes in the reserved memory, the commit size is 0 and is adjusted to be the sizeof(LoaderHeap). + // If for some reason this is not true then we just catch this via an assertion and the dev who changed code + // would have to add logic here to handle the case when committed mem is more than the reserved mem. One option + // could be to leak the users memory and reserve+commit a new block, Another option would be to fail the alloc mem + // and notify the user to provide more reserved mem. + _ASSERTE((dwSizeToCommit <= dwSizeToReserve) && "Loaderheap tried to commit more memory than reserved by user"); + + if (pData == NULL) + { + //_ASSERTE(!"Unable to ClrVirtualAlloc reserve in a loaderheap"); + return FALSE; + } + + // Commit first set of pages, since it will contain the LoaderHeapBlock + void *pTemp = ClrVirtualAlloc(pData, dwSizeToCommit, MEM_COMMIT, m_flProtect); + if (pTemp == NULL) + { + //_ASSERTE(!"Unable to ClrVirtualAlloc commit in a loaderheap"); + + // Unable to commit - release pages + if (fReleaseMemory) + ClrVirtualFree(pData, 0, MEM_RELEASE); + + return FALSE; + } + + if (m_pPrivatePerfCounter_LoaderBytes) + *m_pPrivatePerfCounter_LoaderBytes = *m_pPrivatePerfCounter_LoaderBytes + (DWORD) dwSizeToCommit; + + // Record reserved range in range list, if one is specified + // Do this AFTER the commit - otherwise we'll have bogus ranges included. + if (m_pRangeList != NULL) + { + if (!m_pRangeList->AddRange((const BYTE *) pData, + ((const BYTE *) pData) + dwSizeToReserve, + (void *) this)) + { + + if (fReleaseMemory) + ClrVirtualFree(pData, 0, MEM_RELEASE); + + return FALSE; + } + } + + m_dwTotalAlloc += dwSizeToCommit; + + LoaderHeapBlock *pNewBlock; + + pNewBlock = (LoaderHeapBlock *) pData; + + pNewBlock->dwVirtualSize = dwSizeToReserve; + pNewBlock->pVirtualAddress = pData; + pNewBlock->pNext = NULL; + pNewBlock->m_fReleaseMemory = fReleaseMemory; + + LoaderHeapBlock *pCurBlock = m_pCurBlock; + + // Add to linked list + while (pCurBlock != NULL && + pCurBlock->pNext != NULL) + pCurBlock = pCurBlock->pNext; + + if (pCurBlock != NULL) + m_pCurBlock->pNext = pNewBlock; + else + m_pFirstBlock = pNewBlock; + + // If we want to use the memory immediately... + m_pCurBlock = pNewBlock; + + SETUP_NEW_BLOCK(pData, dwSizeToCommit, dwSizeToReserve); + + return TRUE; +} + +// Get some more committed pages - either commit some more in the current reserved region, or, if it +// has run out, reserve another set of pages. +// Returns: FALSE if we can't get any more memory +// TRUE: We can/did get some more memory - check to see if it's sufficient for +// the caller's needs (see UnlockedAllocMem for example of use) +BOOL UnlockedLoaderHeap::GetMoreCommittedPages(size_t dwMinSize) +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + INJECT_FAULT(return FALSE;); + } + CONTRACTL_END; + + // If we have memory we can use, what are you doing here! + _ASSERTE(dwMinSize > (SIZE_T)(m_pPtrToEndOfCommittedRegion - m_pAllocPtr)); + + // Does this fit in the reserved region? + if (dwMinSize <= (size_t)(m_pEndReservedRegion - m_pAllocPtr)) + { + SIZE_T dwSizeToCommit = (m_pAllocPtr + dwMinSize) - m_pPtrToEndOfCommittedRegion; + + if (dwSizeToCommit < m_dwCommitBlockSize) + dwSizeToCommit = min((SIZE_T)(m_pEndReservedRegion - m_pPtrToEndOfCommittedRegion), (SIZE_T)m_dwCommitBlockSize); + + // Round to page size + dwSizeToCommit = ALIGN_UP(dwSizeToCommit, PAGE_SIZE); + + // Yes, so commit the desired number of reserved pages + void *pData = ClrVirtualAlloc(m_pPtrToEndOfCommittedRegion, dwSizeToCommit, MEM_COMMIT, m_flProtect); + if (pData == NULL) + return FALSE; + + if (m_pPrivatePerfCounter_LoaderBytes) + *m_pPrivatePerfCounter_LoaderBytes = *m_pPrivatePerfCounter_LoaderBytes + (DWORD) dwSizeToCommit; + + m_dwTotalAlloc += dwSizeToCommit; + + m_pPtrToEndOfCommittedRegion += dwSizeToCommit; + return TRUE; + } + + // Need to allocate a new set of reserved pages + INDEBUG(m_dwDebugWastedBytes += (size_t)(m_pPtrToEndOfCommittedRegion - m_pAllocPtr);) + + // Note, there are unused reserved pages at end of current region -can't do much about that + // Provide dwMinSize here since UnlockedReservePages will round up the commit size again + // after adding in the size of the LoaderHeapBlock header. + return UnlockedReservePages(dwMinSize); +} + +void *UnlockedLoaderHeap::UnlockedAllocMem(size_t dwSize + COMMA_INDEBUG(__in const char *szFile) + COMMA_INDEBUG(int lineNum)) +{ + CONTRACT(void*) + { + INSTANCE_CHECK; + THROWS; + GC_NOTRIGGER; + INJECT_FAULT(ThrowOutOfMemory();); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + void *pResult = UnlockedAllocMem_NoThrow( + dwSize COMMA_INDEBUG(szFile) COMMA_INDEBUG(lineNum)); + + if (pResult == NULL) + ThrowOutOfMemory(); + + RETURN pResult; +} + +#ifdef _DEBUG +static DWORD ShouldInjectFault() +{ + static DWORD fInjectFault = 99; + + if (fInjectFault == 99) + fInjectFault = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_InjectFault) != 0); + return fInjectFault; +} + +#define SHOULD_INJECT_FAULT(return_statement) \ + do { \ + if (ShouldInjectFault() & 0x1) \ + { \ + char *a = new (nothrow) char; \ + if (a == NULL) \ + { \ + return_statement; \ + } \ + delete a; \ + } \ + } while (FALSE) + +#else + +#define SHOULD_INJECT_FAULT(return_statement) do { (void)((void *)0); } while (FALSE) + +#endif + +void *UnlockedLoaderHeap::UnlockedAllocMem_NoThrow(size_t dwSize + COMMA_INDEBUG(__in const char *szFile) + COMMA_INDEBUG(int lineNum)) +{ + CONTRACT(void*) + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + INJECT_FAULT(CONTRACT_RETURN NULL;); + PRECONDITION(dwSize != 0); + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + SHOULD_INJECT_FAULT(RETURN NULL); + + INDEBUG(size_t dwRequestedSize = dwSize;) + + INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN())); + +#ifdef RANDOMIZE_ALLOC + if (!m_fExplicitControl) + dwSize += s_random.Next() % 256; +#endif + + dwSize = AllocMem_TotalSize(dwSize, this); + +again: + + { + // Any memory available on the free list? + void *pData = LoaderHeapFreeBlock::AllocFromFreeList(&m_pFirstFreeBlock, dwSize, TRUE /*fRemoveFromFreeList*/, this); + if (!pData) + { + // Enough bytes available in committed region? + if (dwSize <= GetBytesAvailCommittedRegion()) + { + pData = m_pAllocPtr; + m_pAllocPtr += dwSize; + } + } + + if (pData) + { +#ifdef _DEBUG + + BYTE *pAllocatedBytes = (BYTE *)pData; +#if LOADER_HEAP_DEBUG_BOUNDARY > 0 + // Don't fill the memory we allocated - it is assumed to be zeroed - fill the memory after it + memset(pAllocatedBytes + dwRequestedSize, 0xEE, LOADER_HEAP_DEBUG_BOUNDARY); +#endif + if (dwRequestedSize > 0) + { + _ASSERTE_MSG(pAllocatedBytes[0] == 0 && memcmp(pAllocatedBytes, pAllocatedBytes + 1, dwRequestedSize - 1) == 0, + "LoaderHeap must return zero-initialized memory"); + } + + if (!m_fExplicitControl) + { + LoaderHeapValidationTag *pTag = AllocMem_GetTag(pData, dwRequestedSize); + pTag->m_allocationType = kAllocMem; + pTag->m_dwRequestedSize = dwRequestedSize; + pTag->m_szFile = szFile; + pTag->m_lineNum = lineNum; + } + + if (m_dwDebugFlags & kCallTracing) + { + LoaderHeapSniffer::RecordEvent(this, + kAllocMem, + szFile, + lineNum, + szFile, + lineNum, + pData, + dwRequestedSize, + dwSize + ); + } + +#endif + + EtwAllocRequest(this, pData, dwSize); + RETURN pData; + } + } + + // Need to commit some more pages in reserved region. + // If we run out of pages in the reserved region, ClrVirtualAlloc some more pages + if (GetMoreCommittedPages(dwSize)) + goto again; + + // We could not satisfy this allocation request + RETURN NULL; +} + +void UnlockedLoaderHeap::UnlockedBackoutMem(void *pMem, + size_t dwRequestedSize + COMMA_INDEBUG(__in const char *szFile) + COMMA_INDEBUG(int lineNum) + COMMA_INDEBUG(__in const char *szAllocFile) + COMMA_INDEBUG(int allocLineNum)) +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + FORBID_FAULT; + } + CONTRACTL_END; + + // Because the primary use of this function is backout, we'll be nice and + // define Backout(NULL) be a legal NOP. + if (pMem == NULL) + { + return; + } + +#ifdef _DEBUG + { + DEBUG_ONLY_REGION(); + + LoaderHeapValidationTag *pTag = AllocMem_GetTag(pMem, dwRequestedSize); + + if (pTag->m_dwRequestedSize != dwRequestedSize || pTag->m_allocationType != kAllocMem) + { + CONTRACT_VIOLATION(ThrowsViolation|FaultViolation); // We're reporting a heap corruption - who cares about violations + + StackSString message; + message.Printf("HEAP VIOLATION: Invalid BackoutMem() call made at:\n" + "\n" + " File: %s\n" + " Line: %d\n" + "\n" + "Attempting to free block originally allocated at:\n" + "\n" + " File: %s\n" + " Line: %d\n" + "\n" + "The arguments to BackoutMem() were:\n" + "\n" + " Pointer: 0x%p\n" + " Size: %lu (0x%lx)\n" + "\n" + ,szFile + ,lineNum + ,szAllocFile + ,allocLineNum + ,pMem + ,(ULONG)dwRequestedSize + ,(ULONG)dwRequestedSize + ); + + + if (m_dwDebugFlags & kCallTracing) + { + message.AppendASCII("*** CALLTRACING ENABLED ***\n"); + LoaderHeapEvent *pEvent = LoaderHeapSniffer::FindEvent(this, pMem); + if (!pEvent) + { + message.AppendASCII("This pointer doesn't appear to have come from this LoaderHeap.\n"); + } + else + { + message.AppendASCII(pMem == pEvent->m_pMem ? "We have the following data about this pointer:" : "This pointer points to the middle of the following block:"); + pEvent->Describe(&message); + } + } + + if (pTag->m_dwRequestedSize != dwRequestedSize) + { + StackSString buf; + buf.Printf( + "Possible causes:\n" + "\n" + " - This pointer wasn't allocated from this loaderheap.\n" + " - This pointer was allocated by AllocAlignedMem and you didn't adjust for the \"extra.\"\n" + " - This pointer has already been freed.\n" + " - You passed in the wrong size. You must pass the exact same size you passed to AllocMem().\n" + " - Someone wrote past the end of this block making it appear as if one of the above were true.\n" + ); + message.Append(buf); + + } + else + { + message.AppendASCII("This memory block is completely unrecognizable.\n"); + } + + + if (!(m_dwDebugFlags & kCallTracing)) + { + LoaderHeapSniffer::PitchSniffer(&message); + } + + StackScratchBuffer scratch; + DbgAssertDialog(szFile, lineNum, (char*) message.GetANSI(scratch)); + + } + } +#endif + + size_t dwSize = AllocMem_TotalSize(dwRequestedSize, this); + +#ifdef _DEBUG + if (m_dwDebugFlags & kCallTracing) + { + DEBUG_ONLY_REGION(); + + LoaderHeapValidationTag *pTag = m_fExplicitControl ? NULL : AllocMem_GetTag(pMem, dwRequestedSize); + + + LoaderHeapSniffer::RecordEvent(this, + kFreedMem, + szFile, + lineNum, + (pTag && (allocLineNum < 0)) ? pTag->m_szFile : szAllocFile, + (pTag && (allocLineNum < 0)) ? pTag->m_lineNum : allocLineNum, + pMem, + dwRequestedSize, + dwSize + ); + } +#endif + + if (m_pAllocPtr == ( ((BYTE*)pMem) + dwSize )) + { + // Cool. This was the last block allocated. We can just undo the allocation instead + // of going to the freelist. + memset(pMem, 0, dwSize); // Must zero init this memory as AllocMem expect it + m_pAllocPtr = (BYTE*)pMem; + } + else + { + LoaderHeapFreeBlock::InsertFreeBlock(&m_pFirstFreeBlock, pMem, dwSize, this); + } + +} + + +// Allocates memory aligned on power-of-2 boundary. +// +// The return value is a pointer that's guaranteed to be aligned. +// +// FREEING THIS BLOCK: Underneath, the actual block allocated may +// be larger and start at an address prior to the one you got back. +// It is this adjusted size and pointer that you pass to BackoutMem. +// The required adjustment is passed back thru the pdwExtra pointer. +// +// Here is how to properly backout the memory: +// +// size_t dwExtra; +// void *pMem = UnlockedAllocAlignedMem(dwRequestedSize, alignment, &dwExtra); +// _ASSERTE( 0 == (pMem & (alignment - 1)) ); +// UnlockedBackoutMem( ((BYTE*)pMem) - dExtra, dwRequestedSize + dwExtra ); +// +// If you use the AllocMemHolder or AllocMemTracker, all this is taken care of +// behind the scenes. +// +// +void *UnlockedLoaderHeap::UnlockedAllocAlignedMem_NoThrow(size_t dwRequestedSize, + size_t alignment, + size_t *pdwExtra + COMMA_INDEBUG(__in const char *szFile) + COMMA_INDEBUG(int lineNum)) +{ + CONTRACT(void*) + { + NOTHROW; + + // Macro syntax can't handle this INJECT_FAULT expression - we'll use a precondition instead + //INJECT_FAULT( do{ if (*pdwExtra) {*pdwExtra = 0} RETURN NULL; } while(0) ); + + PRECONDITION( alignment != 0 ); + PRECONDITION(0 == (alignment & (alignment - 1))); // require power of 2 + POSTCONDITION( (RETVAL) ? + (0 == ( ((UINT_PTR)(RETVAL)) & (alignment - 1))) : // If non-null, pointer must be aligned + (pdwExtra == NULL || 0 == *pdwExtra) // or else *pdwExtra must be set to 0 + ); + } + CONTRACT_END + + STATIC_CONTRACT_FAULT; + + // Set default value + if (pdwExtra) + { + *pdwExtra = 0; + } + + SHOULD_INJECT_FAULT(RETURN NULL); + + void *pResult; + + INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN())); + + // Check for overflow if we align the allocation + if (dwRequestedSize + alignment < dwRequestedSize) + { + RETURN NULL; + } + + // We don't know how much "extra" we need to satisfy the alignment until we know + // which address will be handed out which in turn we don't know because we don't + // know whether the allocation will fit within the current reserved range. + // + // Thus, we'll request as much heap growth as is needed for the worst case (extra == alignment) + size_t dwRoomSize = AllocMem_TotalSize(dwRequestedSize + alignment, this); + if (dwRoomSize > GetBytesAvailCommittedRegion()) + { + if (!GetMoreCommittedPages(dwRoomSize)) + { + RETURN NULL; + } + } + + pResult = m_pAllocPtr; + + size_t extra = alignment - ((size_t)pResult & ((size_t)alignment - 1)); + +// On DEBUG, we force a non-zero extra so people don't forget to adjust for it on backout +#ifndef _DEBUG + if (extra == alignment) + { + extra = 0; + } +#endif + + S_SIZE_T cbAllocSize = S_SIZE_T( dwRequestedSize ) + S_SIZE_T( extra ); + if( cbAllocSize.IsOverflow() ) + { + RETURN NULL; + } + + size_t dwSize = AllocMem_TotalSize( cbAllocSize.Value(), this); + m_pAllocPtr += dwSize; + + + ((BYTE*&)pResult) += extra; +#ifdef _DEBUG + BYTE *pAllocatedBytes = (BYTE *)pResult; +#if LOADER_HEAP_DEBUG_BOUNDARY > 0 + // Don't fill the entire memory - we assume it is all zeroed -just the memory after our alloc + memset(pAllocatedBytes + dwRequestedSize, 0xee, LOADER_HEAP_DEBUG_BOUNDARY); +#endif + + if (dwRequestedSize != 0) + { + _ASSERTE_MSG(pAllocatedBytes[0] == 0 && memcmp(pAllocatedBytes, pAllocatedBytes + 1, dwRequestedSize - 1) == 0, + "LoaderHeap must return zero-initialized memory"); + } + + if (m_dwDebugFlags & kCallTracing) + { + LoaderHeapSniffer::RecordEvent(this, + kAllocMem, + szFile, + lineNum, + szFile, + lineNum, + ((BYTE*)pResult) - extra, + dwRequestedSize + extra, + dwSize + ); + } + + EtwAllocRequest(this, pResult, dwSize); + + if (!m_fExplicitControl) + { + LoaderHeapValidationTag *pTag = AllocMem_GetTag(((BYTE*)pResult) - extra, dwRequestedSize + extra); + pTag->m_allocationType = kAllocMem; + pTag->m_dwRequestedSize = dwRequestedSize + extra; + pTag->m_szFile = szFile; + pTag->m_lineNum = lineNum; + } +#endif //_DEBUG + + if (pdwExtra) + { + *pdwExtra = extra; + } + + RETURN pResult; + +} + + + +void *UnlockedLoaderHeap::UnlockedAllocAlignedMem(size_t dwRequestedSize, + size_t dwAlignment, + size_t *pdwExtra + COMMA_INDEBUG(__in const char *szFile) + COMMA_INDEBUG(int lineNum)) +{ + CONTRACTL + { + THROWS; + INJECT_FAULT(ThrowOutOfMemory()); + } + CONTRACTL_END + + void *pResult = UnlockedAllocAlignedMem_NoThrow(dwRequestedSize, + dwAlignment, + pdwExtra + COMMA_INDEBUG(szFile) + COMMA_INDEBUG(lineNum)); + + if (!pResult) + { + ThrowOutOfMemory(); + } + + return pResult; + + +} + + + +void *UnlockedLoaderHeap::UnlockedAllocMemForCode_NoThrow(size_t dwHeaderSize, size_t dwCodeSize, DWORD dwCodeAlignment) +{ + CONTRACT(void*) + { + INSTANCE_CHECK; + NOTHROW; + INJECT_FAULT(CONTRACT_RETURN NULL;); + PRECONDITION(0 == (dwCodeAlignment & (dwCodeAlignment - 1))); // require power of 2 + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + _ASSERTE(m_fExplicitControl); + + INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN())); + + // We don't know how much "extra" we need to satisfy the alignment until we know + // which address will be handed out which in turn we don't know because we don't + // know whether the allocation will fit within the current reserved range. + // + // Thus, we'll request as much heap growth as is needed for the worst case (we request an extra dwCodeAlignment – 1 bytes) + + S_SIZE_T cbAllocSize = S_SIZE_T(dwHeaderSize) + S_SIZE_T(dwCodeSize) + S_SIZE_T(dwCodeAlignment - 1); + if( cbAllocSize.IsOverflow() ) + { + RETURN NULL; + } + + if (cbAllocSize.Value() > GetBytesAvailCommittedRegion()) + { + if (GetMoreCommittedPages(cbAllocSize.Value()) == FALSE) + { + RETURN NULL; + } + } + + BYTE *pResult = (BYTE *)ALIGN_UP(m_pAllocPtr + dwHeaderSize, dwCodeAlignment); + EtwAllocRequest(this, pResult, (pResult + dwCodeSize) - m_pAllocPtr); + m_pAllocPtr = pResult + dwCodeSize; + + RETURN pResult; +} + + +#endif // #ifndef DACCESS_COMPILE + +#ifdef DACCESS_COMPILE + +void UnlockedLoaderHeap::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) +{ + WRAPPER_NO_CONTRACT; + + DAC_ENUM_DTHIS(); + + PTR_LoaderHeapBlock block = m_pFirstBlock; + while (block.IsValid()) + { + // All we know is the virtual size of this block. We don't have any way to tell how + // much of this space was actually comitted, so don't expect that this will always + // succeed. + // @dbgtodo : Ideally we'd reduce the risk of corruption causing problems here. + // We could extend LoaderHeapBlock to track a commit size, + // but it seems wasteful (eg. makes each AppDomain objects 32 bytes larger on x64). + TADDR addr = dac_cast<TADDR>(block->pVirtualAddress); + TSIZE_T size = block->dwVirtualSize; + DacEnumMemoryRegion(addr, size, false); + + block = block->pNext; + } +} + +#endif // #ifdef DACCESS_COMPILE + + +void UnlockedLoaderHeap::EnumPageRegions (EnumPageRegionsCallback *pCallback) +{ + WRAPPER_NO_CONTRACT; + + PTR_LoaderHeapBlock block = m_pFirstBlock; + while (block) + { + (*pCallback)(block->pVirtualAddress, block->dwVirtualSize); + + block = block->pNext; + } +} + + +#ifdef _DEBUG + +void UnlockedLoaderHeap::DumpFreeList() +{ + LIMITED_METHOD_CONTRACT; + if (m_pFirstFreeBlock == NULL) + { + printf("FREEDUMP: FreeList is empty\n"); + } + else + { + LoaderHeapFreeBlock *pBlock = m_pFirstFreeBlock; + while (pBlock != NULL) + { + size_t dwsize = pBlock->m_dwSize; + BOOL ccbad = FALSE; + BOOL sizeunaligned = FALSE; + BOOL sizesmall = FALSE; + + if ( 0 != (dwsize & ALLOC_ALIGN_CONSTANT) ) + { + sizeunaligned = TRUE; + } + if ( dwsize < sizeof(LoaderHeapBlock)) + { + sizesmall = TRUE; + } + + for (size_t i = sizeof(LoaderHeapFreeBlock); i < dwsize; i++) + { + if ( ((BYTE*)pBlock)[i] != 0xcc ) + { + ccbad = TRUE; + break; + } + } + + printf("Addr = %pxh, Size = %lxh", pBlock, ((ULONG)dwsize)); + if (ccbad) printf(" *** ERROR: NOT CC'd ***"); + if (sizeunaligned) printf(" *** ERROR: size not a multiple of ALLOC_ALIGN_CONSTANT ***"); + if (sizesmall) printf(" *** ERROR: size smaller than sizeof(LoaderHeapFreeBlock) ***"); + printf("\n"); + + pBlock = pBlock->m_pNext; + } + } +} + + +void UnlockedLoaderHeap::UnlockedClearEvents() +{ + WRAPPER_NO_CONTRACT; + LoaderHeapSniffer::ClearEvents(this); +} + +void UnlockedLoaderHeap::UnlockedCompactEvents() +{ + WRAPPER_NO_CONTRACT; + LoaderHeapSniffer::CompactEvents(this); +} + +void UnlockedLoaderHeap::UnlockedPrintEvents() +{ + WRAPPER_NO_CONTRACT; + LoaderHeapSniffer::PrintEvents(this); +} + + +#endif //_DEBUG + +//************************************************************************************ +// LOADERHEAP SNIFFER METHODS +//************************************************************************************ +#ifdef _DEBUG + +/*static*/ VOID LoaderHeapSniffer::RecordEvent(UnlockedLoaderHeap *pHeap, + AllocationType allocationType, + __in const char *szFile, + int lineNum, + __in const char *szAllocFile, + int allocLineNum, + void *pMem, + size_t dwRequestedSize, + size_t dwSize + ) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; //If we OOM in here, we just throw the event away. + } + CONTRACTL_END + + LoaderHeapEvent *pNewEvent; + { + { + FAULT_NOT_FATAL(); + pNewEvent = new (nothrow) LoaderHeapEvent; + } + if (!pNewEvent) + { + if (!(pHeap->m_dwDebugFlags & pHeap->kEncounteredOOM)) + { + pHeap->m_dwDebugFlags |= pHeap->kEncounteredOOM; + _ASSERTE(!"LOADERHEAPSNIFFER: Failed allocation of LoaderHeapEvent. Call tracing information will be incomplete."); + } + } + else + { + pNewEvent->m_allocationType = allocationType; + pNewEvent->m_szFile = szFile; + pNewEvent->m_lineNum = lineNum; + pNewEvent->m_szAllocFile = szAllocFile; + pNewEvent->m_allocLineNum = allocLineNum; + pNewEvent->m_pMem = pMem; + pNewEvent->m_dwRequestedSize = dwRequestedSize; + pNewEvent->m_dwSize = dwSize; + + pNewEvent->m_pNext = pHeap->m_pEventList; + pHeap->m_pEventList = pNewEvent; + } + } +} + + + +/*static*/ +void LoaderHeapSniffer::ValidateFreeList(UnlockedLoaderHeap *pHeap) +{ + CANNOT_HAVE_CONTRACT; + + // No contract. This routine is only called if we've AV'd inside the + // loaderheap. The system is already toast. We're trying to be a hero + // and produce the best diagnostic info we can. Last thing we need here + // is a secondary assert inside the contract stuff. + // + // This contract violation is permanent. + CONTRACT_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|ModeViolation); // This violation won't be removed + + LoaderHeapFreeBlock *pFree = pHeap->m_pFirstFreeBlock; + LoaderHeapFreeBlock *pPrev = NULL; + + + void *pBadAddr = NULL; + LoaderHeapFreeBlock *pProbeThis = NULL; + const char *pExpected = NULL; + + while (pFree != NULL) + { + if ( 0 != ( ((ULONG_PTR)pFree) & ALLOC_ALIGN_CONSTANT )) + { + // Not aligned - can't be a valid freeblock. Most likely we followed a bad pointer from the previous block. + pProbeThis = pPrev; + pBadAddr = pPrev ? &(pPrev->m_pNext) : &(pHeap->m_pFirstFreeBlock); + pExpected = "a pointer to a valid LoaderHeapFreeBlock"; + break; + } + + size_t dwSize = pFree->m_dwSize; + if (dwSize < AllocMem_TotalSize(1, pHeap) || + 0 != (dwSize & ALLOC_ALIGN_CONSTANT)) + { + // Size is not a valid value (out of range or unaligned.) + pProbeThis = pFree; + pBadAddr = &(pFree->m_dwSize); + pExpected = "a valid block size (multiple of pointer size)"; + break; + } + + size_t i; + for (i = sizeof(LoaderHeapFreeBlock); i < dwSize; i++) + { + if ( ((BYTE*)pFree)[i] != 0xcc ) + { + pProbeThis = pFree; + pBadAddr = i + ((BYTE*)pFree); + pExpected = "0xcc (our fill value for free blocks)"; + break; + } + } + if (i != dwSize) + { + break; + } + + + + pPrev = pFree; + pFree = pFree->m_pNext; + } + + if (pFree == NULL) + { + return; // No problems found + } + + { + StackSString message; + + message.Printf("A loaderheap freelist has been corrupted. The bytes at or near address 0x%p appears to have been overwritten. We expected to see %s here.\n" + "\n" + " LoaderHeap: 0x%p\n" + " Suspect address at: 0x%p\n" + " Start of suspect freeblock: 0x%p\n" + "\n" + , pBadAddr + , pExpected + , pHeap + , pBadAddr + , pProbeThis + ); + + if (!(pHeap->m_dwDebugFlags & pHeap->kCallTracing)) + { + message.AppendASCII("\nThe usual reason is that someone wrote past the end of a block or wrote into a block after freeing it." + "\nOf course, the culprit is long gone so it's probably too late to debug this now. Try turning on call-tracing" + "\nand reproing. We can attempt to find out who last owned the surrounding pieces of memory." + "\n" + "\nTo turn on call-tracing, set the following registry DWORD value:" + "\n" + "\n HKLM\\Software\\Microsoft\\.NETFramework\\LoaderHeapCallTracing = 1" + "\n" + ); + + } + else + { + LoaderHeapEvent *pBadAddrEvent = FindEvent(pHeap, pBadAddr); + + message.AppendASCII("*** CALL TRACING ENABLED ***\n\n"); + + if (pBadAddrEvent) + { + message.AppendASCII("\nThe last known owner of the corrupted address was:\n"); + pBadAddrEvent->Describe(&message); + } + else + { + message.AppendASCII("\nNo known owner of last corrupted address.\n"); + } + + LoaderHeapEvent *pPrevEvent = FindEvent(pHeap, ((BYTE*)pProbeThis) - 1); + + int count = 3; + while (count-- && + pPrevEvent != NULL && + ( ((UINT_PTR)pProbeThis) - ((UINT_PTR)(pPrevEvent->m_pMem)) + pPrevEvent->m_dwSize ) < 1024) + { + message.AppendASCII("\nThis block is located close to the corruption point. "); + if (pPrevEvent->QuietValidate()) + { + message.AppendASCII("If it was overrun, it might have caused this."); + } + else + { + message.AppendASCII("*** CORRUPTION DETECTED IN THIS BLOCK ***"); + } + pPrevEvent->Describe(&message); + pPrevEvent = FindEvent(pHeap, ((BYTE*)(pPrevEvent->m_pMem)) - 1); + } + + + } + + StackScratchBuffer scratch; + DbgAssertDialog(__FILE__, __LINE__, (char*) message.GetANSI(scratch)); + + } + + + +} + + +BOOL LoaderHeapEvent::QuietValidate() +{ + WRAPPER_NO_CONTRACT; + + if (m_allocationType == kAllocMem) + { + LoaderHeapValidationTag *pTag = AllocMem_GetTag(m_pMem, m_dwRequestedSize); + return (pTag->m_allocationType == m_allocationType && pTag->m_dwRequestedSize == m_dwRequestedSize); + } + else + { + // We can't easily validate freed blocks. + return TRUE; + } +} + + +#endif //_DEBUG + +#ifndef DACCESS_COMPILE + +AllocMemTracker::AllocMemTracker() +{ + CONTRACTL + { + NOTHROW; + FORBID_FAULT; + CANNOT_TAKE_LOCK; + } + CONTRACTL_END + + m_FirstBlock.m_pNext = NULL; + m_FirstBlock.m_nextFree = 0; + m_pFirstBlock = &m_FirstBlock; + + m_fReleased = FALSE; +} + +AllocMemTracker::~AllocMemTracker() +{ + CONTRACTL + { + NOTHROW; + FORBID_FAULT; + } + CONTRACTL_END + + if (!m_fReleased) + { + AllocMemTrackerBlock *pBlock = m_pFirstBlock; + while (pBlock) + { + // Do the loop in reverse - loaderheaps work best if + // we allocate and backout in LIFO order. + for (int i = pBlock->m_nextFree - 1; i >= 0; i--) + { + AllocMemTrackerNode *pNode = &(pBlock->m_Node[i]); + pNode->m_pHeap->RealBackoutMem(pNode->m_pMem + ,pNode->m_dwRequestedSize +#ifdef _DEBUG + ,__FILE__ + ,__LINE__ + ,pNode->m_szAllocFile + ,pNode->m_allocLineNum +#endif + ); + + } + + pBlock = pBlock->m_pNext; + } + } + + + AllocMemTrackerBlock *pBlock = m_pFirstBlock; + while (pBlock != &m_FirstBlock) + { + AllocMemTrackerBlock *pNext = pBlock->m_pNext; + delete pBlock; + pBlock = pNext; + } + + INDEBUG(memset(this, 0xcc, sizeof(*this));) +} + +void *AllocMemTracker::Track(TaggedMemAllocPtr tmap) +{ + CONTRACTL + { + THROWS; + INJECT_FAULT(ThrowOutOfMemory();); + } + CONTRACTL_END + + _ASSERTE(this); + + void *pv = Track_NoThrow(tmap); + if (!pv) + { + ThrowOutOfMemory(); + } + return pv; +} + +void *AllocMemTracker::Track_NoThrow(TaggedMemAllocPtr tmap) +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return NULL;); + } + CONTRACTL_END + + _ASSERTE(this); + + // Calling Track() after calling SuppressRelease() is almost certainly a bug. You're supposed to call SuppressRelease() only after you're + // sure no subsequent failure will force you to backout the memory. + _ASSERTE( (!m_fReleased) && "You've already called SuppressRelease on this AllocMemTracker which implies you've passed your point of no failure. Why are you still doing allocations?"); + + + if (tmap.m_pMem != NULL) + { + AllocMemHolder<void*> holder(tmap); // If anything goes wrong in here, this holder will backout the allocation for the caller. + if (m_fReleased) + { + holder.SuppressRelease(); + } + AllocMemTrackerBlock *pBlock = m_pFirstBlock; + if (pBlock->m_nextFree == kAllocMemTrackerBlockSize) + { + AllocMemTrackerBlock *pNewBlock = new (nothrow) AllocMemTrackerBlock; + if (!pNewBlock) + { + return NULL; + } + + pNewBlock->m_pNext = m_pFirstBlock; + pNewBlock->m_nextFree = 0; + + m_pFirstBlock = pNewBlock; + + pBlock = pNewBlock; + } + + // From here on, we can't fail + pBlock->m_Node[pBlock->m_nextFree].m_pHeap = tmap.m_pHeap; + pBlock->m_Node[pBlock->m_nextFree].m_pMem = tmap.m_pMem; + pBlock->m_Node[pBlock->m_nextFree].m_dwRequestedSize = tmap.m_dwRequestedSize; +#ifdef _DEBUG + pBlock->m_Node[pBlock->m_nextFree].m_szAllocFile = tmap.m_szFile; + pBlock->m_Node[pBlock->m_nextFree].m_allocLineNum = tmap.m_lineNum; +#endif + + pBlock->m_nextFree++; + + holder.SuppressRelease(); + + + } + return (void *)tmap; + + + +} + + +void AllocMemTracker::SuppressRelease() +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE(this); + + m_fReleased = TRUE; +} + +#endif //#ifndef DACCESS_COMPILE diff --git a/src/utilcode/loadrc-impl.cpp b/src/utilcode/loadrc-impl.cpp new file mode 100644 index 0000000000..4c8ee32ee4 --- /dev/null +++ b/src/utilcode/loadrc-impl.cpp @@ -0,0 +1,322 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// +// LoadRC-impl.cpp : Utility to load a localized file (primarily a DLL) + +#include "palclr.h" + +#if defined(USE_SSTRING) + +// This is the normal path: use SafeString, wrappers, etc... + +#include "sstring.h" +#include "safewrap.h" + +typedef SString MyString; +typedef SString::CIterator MyStringIterator; +#define EndsWithChar(OBJ, CHAR) (OBJ.EndsWith(CHAR)) +#define AppendChar(OBJ, CHAR) (OBJ.Append(CHAR)) +#define AppendStr(OBJ, STR) (OBJ.Append(STR)) +#define TrimLastChar(OBJ) (OBJ.Truncate(OBJ.End() - 1)) +#define GetChars(OBJ) (OBJ.GetUnicode()) +#define IsEmptyStr(OBJ) (OBJ.IsEmpty()) +#define CharLength(OBJ) (OBJ.GetCount()) +#define StrBeginIter(OBJ) (OBJ.Begin()) +#define StrEndIter(OBJ) (OBJ.End()) +#define FindNext(OBJ, ITER, CHAR) (OBJ.Find(ITER, CHAR)) +#define MakeString(DST, OBJ, BEG, END) (DST.Set(OBJ, BEG, END)) +#define StrEquals(STR1, STR2) (STR1.Compare(STR2)==0) +#define FindLast(OBJ, ITER, CHAR) (OBJ.FindBack(ITER, CHAR)) +void SkipChars(MyString &str, MyStringIterator &i, WCHAR c1, WCHAR c2) { while (str.Skip(i, c1) || str.Skip(i, c2)); } + +#elif defined(USE_WSTRING) + +// This stuff is used by GacUtil, because it _really_ doesn't want to link with utilcode :-( + +#include <string> +#include <algorithm> +typedef std::wstring MyString; +typedef std::wstring::const_iterator MyStringIterator; +#define EndsWithChar(OBJ, CHAR) (*(OBJ.rbegin()) == CHAR) +#define AppendChar(OBJ, CHAR) (OBJ.push_back(CHAR)) +#define AppendStr(OBJ, STR) (OBJ += STR) +#define TrimLastChar(OBJ) (OBJ.resize(OBJ.size() - 1)) +#define GetChars(OBJ) (OBJ.c_str()) +#define IsEmptyStr(OBJ) (OBJ.empty()) +#define CharLength(OBJ) (OBJ.size()) +#define StrBeginIter(OBJ) (OBJ.begin()) +#define StrEndIter(OBJ) (OBJ.end()) +#define FindNext(OBJ, ITER, CHAR) (ITER = std::find<std::wstring::const_iterator>(ITER, OBJ.end(), CHAR)) +#define MakeString(DST, OBJ, BEG, END) (DST = MyString(BEG, END)) +#define StrEquals(STR1, STR2) (STR1 == STR2) +#define ClrGetEnvironmentVariable(var, res) GetEnvVar(L##var, res) +bool FindLast(const MyString &str, MyStringIterator &iter, wchar_t c) +{ + size_t pos = str.find_last_of(c); + iter = (pos == std::wstring::npos) ? str.end() : (str.begin() + pos); + return pos != std::wstring::npos; +} +void SkipChars(const MyString &str, MyStringIterator &i, WCHAR c1, WCHAR c2) { while (*i == c1 || *i == c2) i++; } +bool GetEnvVar(_In_z_ wchar_t *var, MyString &res) +{ + wchar_t *buffer; + size_t size; + _wdupenv_s(&buffer, &size, var); + if (!size || !buffer) + return false; + res = buffer; + free(buffer); // Don't forget to free the buffer! + return true; +} +void ClrGetModuleFileName(HMODULE hModule, MyString& value) +{ + wchar_t driverpath_tmp[_MAX_PATH]; + GetModuleFileNameW(hModule, driverpath_tmp, _MAX_PATH); + value = driverpath_tmp; +} + +#else + +#error You must define either USE_SSTRING or USE_WSTRING to use this file + +#endif + +// This is a helper for loading localized string resource DLL files +HMODULE LoadLocalizedResourceDLLForSDK(_In_z_ LPCWSTR wzResourceDllName, _In_opt_z_ LPCWSTR modulePath, bool trySelf); + +// This is a slight variation that can be used for anything else (ildasm.chm, for example) +typedef void* (__cdecl *LocalizedFileHandler)(LPCWSTR); +void* FindLocalizedFile(_In_z_ LPCWSTR wzResourceDllName, LocalizedFileHandler lfh, _In_opt_z_ LPCWSTR modulePath); + +// Helper functions to combine paths +static MyString MakePath(const MyString &root, const MyString &file) +{ + MyString res = root; + if (!EndsWithChar(res, W('\\'))) + AppendChar(res, W('\\')); + AppendStr(res, file); + return res; +} +static MyString MakePath(const MyString &root, const MyString &dir, const MyString &file) +{ + return MakePath(MakePath(root, dir), file); +} + +// Helper to deal with occasional training back-slashes +static bool FileExists(const MyString &file) +{ + if (!EndsWithChar(file, W('\\'))) + return GetFileAttributesW(GetChars(file)) != INVALID_FILE_ATTRIBUTES; + else + { + MyString tmp(file); + TrimLastChar(tmp); + return GetFileAttributesW(GetChars(tmp)) != INVALID_FILE_ATTRIBUTES; + } +} + +// Little helper function to get the codepage integer ID from the LocaleInfo +static UINT GetCodePage(LANGID LanguageID, DWORD locale) +{ + wchar_t CodePageInt[12]; + GetLocaleInfo(MAKELCID(LanguageID, SORT_DEFAULT), LOCALE_IDEFAULTCODEPAGE, CodePageInt, _countof(CodePageInt)); + return _wtoi(CodePageInt); +} + +// LCID helper macro +#define ENGLISH_LCID MAKELCID(MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US ), SORT_DEFAULT) + +// FindLocaleDirectory: Search the provided path for one of the expected code page subdirectories +// Returns empty string on failure, or the full path to the c:\my\directory\1033\myrcfile.dll +static MyString FindLocaleDirectory(const MyString &path, const MyString &dllName) +{ + // We'll be checking for 3 different locales: The user's default locale + // The user's primary language locale, and english (in that order) + const LCID lcidUser = MAKELCID(GetUserDefaultUILanguage(), SORT_DEFAULT); + LCID rglcid[3] = {lcidUser, MAKELCID(MAKELANGID(PRIMARYLANGID(lcidUser), SUBLANG_DEFAULT), SORTIDFROMLCID(lcidUser)), ENGLISH_LCID}; + + for (int i = 0; i < _countof(rglcid); i++) + { + LCID lcid = rglcid[i]; + // Turn the LCID into a string + wchar_t wzNumBuf[12]; + _itow_s(lcid, wzNumBuf, _countof(wzNumBuf), 10); + MyString localePath = MakePath(path, wzNumBuf, dllName); + + // Check to see if the file exists + if (FileExists(localePath)) + { + // make sure the console can support a codepage for this language. + UINT ConsoleCP = GetConsoleOutputCP(); + + // Dev10 #843375: For a GUI application, GetConsoleOutputCP returns 0 + // If that's the case, we don't care about capabilities of the console, + // since we're not outputting to the console, anyway... + if ( ConsoleCP != 0 && lcid != ENGLISH_LCID ) + { + LANGID LanguageID = MAKELANGID( lcid, SUBLANGID(lcid) ); + // we know the console cannot support arabic or hebrew (right to left scripts?) + if( PRIMARYLANGID(LanguageID) == LANG_ARABIC || PRIMARYLANGID(LanguageID) == LANG_HEBREW ) + continue; + + UINT LangOEMCodepage = GetCodePage(LanguageID, LOCALE_IDEFAULTCODEPAGE); + UINT LangANSICodepage = GetCodePage(LanguageID, LOCALE_IDEFAULTANSICODEPAGE); + + // We can only support it if the console's code page is UTF8, OEM, or ANSI + if( ConsoleCP != CP_UTF8 && ConsoleCP != LangOEMCodepage && ConsoleCP != LangANSICodepage ) + continue; + } + + return localePath; + } + } + return W(""); +} + +// Attempt to load the resource file from the locale, first. +// If that fails, then just try any subdirectory of of the path provided +static void *LoadLocalFile(const MyString &path, const MyString &dllName, LocalizedFileHandler lfh) +{ + if (IsEmptyStr(path) || IsEmptyStr(dllName)) + return NULL; + + MyString pathTemp = path; + + // Languages are checked in the following order. + // 1) The UI language: this is returned by GetUserDefaultUILanguage. + // 2) As step 1, but with SUBLANG_DEFAULT + // 3) English + // 4) Any language that can be found! + + MyString localePath = FindLocaleDirectory(pathTemp, dllName); + + if (IsEmptyStr(localePath)) + { + // None of the default choices exists, so now look for the first version of the dll in the given path. + // We don't bother to see if the console supports the dll's language. + MyString wildCard = MakePath(pathTemp, W("*.*")); + WIN32_FIND_DATAW wfdw; + HANDLE hDirs = FindFirstFileW(GetChars(wildCard), &wfdw); + if (hDirs == INVALID_HANDLE_VALUE) + return NULL; + do + { + // We are only interested in directories, since at this level, that should + // be the only thing in this directory, i.e, LCID sub dirs + if (wfdw.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + MyString file(wfdw.cFileName); + + if (StrEquals(file, W("."))) + continue; + + if (StrEquals(file, W(".."))) + continue; + + // Does this dir have the resource dll? + MyString fullPath = MakePath(pathTemp, file, dllName); + + if (GetFileAttributesW(GetChars(fullPath)) != INVALID_FILE_ATTRIBUTES) + { + localePath = fullPath; // Got it - bail out of here + break; + } + } + } while (FindNextFileW(hDirs, &wfdw)); + + FindClose(hDirs); + + + if (IsEmptyStr(localePath)) + { + // + // With CoreCLR we have the resource dll directly in the bin directory so check there now. + // + + // Does this dir have the resource dll? + MyString fullPath = MakePath(path, dllName); + + if (GetFileAttributesW(GetChars(fullPath)) != INVALID_FILE_ATTRIBUTES) + { + localePath = fullPath; // Got it - bail out of here + } + } + } + + // Attempt to load the library + // Beware! A dll loaded with LOAD_LIBRARY_AS_DATAFILE won't + // let you use LoadIcon and things like that (only general calls like FindResource and LoadResource). + return IsEmptyStr(localePath) ? NULL : lfh(GetChars(localePath)); +} + +// Try to load the resource DLL from [each directory in %PATH%]/<lcid>/ +static void *LoadSearchPath(const MyString &resourceDllName, LocalizedFileHandler lfh) +{ + void *hmod = NULL; + + // Get the PATH variable into a C++ string + MyString envPath; + + if (ClrGetEnvironmentVariable("PATH", envPath)) + return hmod; + + MyStringIterator endOfChunk, startOfChunk = StrBeginIter(envPath); + MyString tryPath; + for (SkipChars(envPath, startOfChunk, W(' '), W(';')); + hmod == NULL && startOfChunk != StrEndIter(envPath); + SkipChars(envPath, startOfChunk, W(' '), W(';'))) + { + // copy this chunk of the path into our trypath + endOfChunk = startOfChunk; + FindNext(envPath, endOfChunk, W(';')); + MakeString(tryPath, envPath, startOfChunk, endOfChunk); + + // Don't try invalid locations + if (IsEmptyStr(tryPath) || CharLength(tryPath) >= _MAX_PATH) + continue; + + // Try to load the dll + hmod = LoadLocalFile(tryPath, resourceDllName, lfh); + startOfChunk = endOfChunk; + } + return hmod; +} + +void * __cdecl LibraryLoader(_In_z_ LPCWSTR lpFileName) +{ + return (void *)(LoadLibraryExW(lpFileName, NULL, LOAD_LIBRARY_AS_DATAFILE)); +} + +void *FindLocalizedFile(_In_z_ LPCWSTR wzResourceDllName, LocalizedFileHandler lfh, _In_opt_z_ LPCWSTR modulePathW) +{ + // find path of the modulePath + MyString driverPath; + MyString modulePath; + ClrGetModuleFileName(GetModuleHandleW(modulePathW), modulePath); + + // Rip off the application name. + MyStringIterator trailingSlashLocation = StrEndIter(modulePath); + if (FindLast(modulePath, trailingSlashLocation, W('\\'))) + MakeString(driverPath, modulePath, StrBeginIter(modulePath), trailingSlashLocation); + else + // If it's not a full path, look in the current directory + driverPath = W("."); + + // return the first of the local directory's copy or the resource DLL on %PATH% + void *hmod = LoadLocalFile(driverPath, wzResourceDllName, lfh); + if (hmod == NULL) + hmod = LoadSearchPath(wzResourceDllName, lfh); + return hmod; +} + +// load the satellite dll which contains string resources +HMODULE LoadLocalizedResourceDLLForSDK(_In_z_ LPCWSTR wzResourceDllName, _In_opt_z_ LPCWSTR modulePath, bool trySelf) +{ + HMODULE hmod = (HMODULE)FindLocalizedFile(wzResourceDllName, &LibraryLoader, modulePath); + if (hmod == NULL && trySelf) + hmod = GetModuleHandleW(NULL); + return hmod; +} diff --git a/src/utilcode/loadrc.cpp b/src/utilcode/loadrc.cpp new file mode 100644 index 0000000000..6bc8d656d3 --- /dev/null +++ b/src/utilcode/loadrc.cpp @@ -0,0 +1,17 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// +// Loads an localized string resource file +// this is used by sn.exe, ildasm.exe, peverify.exe, gacutil.exe, and fuslogvw.exe +// To use it by itself (not requiring utilcode*.lib), you'll need to use this CPP file +// along with safewrap, sstring +// + + +#include "stdafx.h" +#include "sstring.h" +#define USE_SSTRING +#include "loadrc-impl.cpp" + diff --git a/src/utilcode/log.cpp b/src/utilcode/log.cpp new file mode 100644 index 0000000000..0429597e29 --- /dev/null +++ b/src/utilcode/log.cpp @@ -0,0 +1,447 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// +// Simple Logging Facility +// +#include "stdafx.h" + +// +// Define LOGGING by default in a checked build. If you want to log in a free +// build, define logging independent of _DEBUG here and each place you want +// to use it. +// +#ifdef _DEBUG +#define LOGGING +#endif + +#include "log.h" +#include "utilcode.h" + +#ifdef LOGGING + +#define DEFAULT_LOGFILE_NAME "COMPLUS.LOG" + +#define LOG_ENABLE_FILE_LOGGING 0x0001 +#define LOG_ENABLE_FLUSH_FILE 0x0002 +#define LOG_ENABLE_CONSOLE_LOGGING 0x0004 +#define LOG_ENABLE_APPEND_FILE 0x0010 +#define LOG_ENABLE_DEBUGGER_LOGGING 0x0020 +#define LOG_ENABLE 0x0040 + + +static DWORD LogFlags = 0; +static char szLogFileName[MAX_PATH+1] = DEFAULT_LOGFILE_NAME; +static HANDLE LogFileHandle = INVALID_HANDLE_VALUE; +static MUTEX_COOKIE LogFileMutex = 0; +static DWORD LogFacilityMask = LF_ALL; +static DWORD LogFacilityMask2 = 0; +static DWORD LogVMLevel = LL_INFO100; + // <TODO>@todo FIX should probably only display warnings and above by default</TODO> + + +VOID InitLogging() +{ + STATIC_CONTRACT_NOTHROW; + + // <TODO>FIX bit of a workaround for now, check for the log file in the + // registry and if there, turn on file logging VPM</TODO> + + LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogEnable, LOG_ENABLE); + LogFacilityMask = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogFacility, LogFacilityMask) | LF_ALWAYS; + LogVMLevel = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_LogLevel, LogVMLevel); + LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogFileAppend, LOG_ENABLE_APPEND_FILE); + LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogFlushFile, LOG_ENABLE_FLUSH_FILE); + LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogToDebugger, LOG_ENABLE_DEBUGGER_LOGGING); + LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogToFile, LOG_ENABLE_FILE_LOGGING); + LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogToConsole, LOG_ENABLE_CONSOLE_LOGGING); + + LogFacilityMask2 = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogFacility2, LogFacilityMask2) | LF_ALWAYS; + + LPWSTR fileName = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogFile); + if (fileName != 0) + { + int ret; + ret = WszWideCharToMultiByte(CP_ACP, 0, fileName, -1, szLogFileName, sizeof(szLogFileName)-1, NULL, NULL); + _ASSERTE(ret != 0); + delete fileName; + } + + if (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogWithPid, FALSE)) + { + char szPid[20]; + sprintf_s(szPid, COUNTOF(szPid), ".%d", GetCurrentProcessId()); + strcat_s(szLogFileName, _countof(szLogFileName), szPid); + } + + if ((LogFlags & LOG_ENABLE) && + (LogFlags & LOG_ENABLE_FILE_LOGGING) && + (LogFileHandle == INVALID_HANDLE_VALUE)) + { + DWORD fdwCreate = (LogFlags & LOG_ENABLE_APPEND_FILE) ? OPEN_ALWAYS : CREATE_ALWAYS; + LogFileHandle = CreateFileA( + szLogFileName, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + fdwCreate, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | ((LogFlags & LOG_ENABLE_FLUSH_FILE) ? FILE_FLAG_WRITE_THROUGH : 0), + NULL); + + if(0 == LogFileMutex) + { + LogFileMutex = ClrCreateMutex( + NULL, + FALSE, + NULL); + _ASSERTE(LogFileMutex != 0); + } + + // Some other logging may be going on, try again with another file name + if (LogFileHandle == INVALID_HANDLE_VALUE) + { + char* ptr = szLogFileName + strlen(szLogFileName) + 1; + ptr[-1] = '.'; + ptr[0] = '0'; + ptr[1] = 0; + + for(int i = 0; i < 10; i++) + { + LogFileHandle = CreateFileA( + szLogFileName, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + fdwCreate, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | ((LogFlags & LOG_ENABLE_FLUSH_FILE) ? FILE_FLAG_WRITE_THROUGH : 0), + NULL); + if (LogFileHandle != INVALID_HANDLE_VALUE) + break; + *ptr = *ptr + 1; + } + if (LogFileHandle == INVALID_HANDLE_VALUE) { + DWORD written; + char buff[MAX_PATH+60]; + strcpy(buff, "Could not open log file, logging to "); + strcat_s(buff, _countof(buff), szLogFileName); + // ARULM--Changed WriteConsoleA to WriteFile to be CE compat + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buff, (DWORD)strlen(buff), &written, 0); + } + } + if (LogFileHandle == INVALID_HANDLE_VALUE) + UtilMessageBoxNonLocalized(NULL, W("Could not open log file"), W("CLR logging"), MB_OK | MB_ICONINFORMATION, FALSE, TRUE); + if (LogFileHandle != INVALID_HANDLE_VALUE) + { + if (LogFlags & LOG_ENABLE_APPEND_FILE) + SetFilePointer(LogFileHandle, 0, NULL, FILE_END); + LogSpew( LF_ALWAYS, FATALERROR, "************************ New Output *****************\n" ); + } + } +} + +VOID EnterLogLock() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + // We don't care about violating CANNOT_TAKE_LOCK in debug-only builds, and it's + // rather hard to care about this, as we LOG all over the place. + CONTRACT_VIOLATION(TakesLockViolation); + + if(LogFileMutex != 0) + { + DWORD status; + status = ClrWaitForMutex(LogFileMutex, INFINITE, FALSE); + _ASSERTE(WAIT_OBJECT_0 == status); + } +} + +VOID LeaveLogLock() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + if(LogFileMutex != 0) + { + BOOL success; + success = ClrReleaseMutex(LogFileMutex); + _ASSERTE(success); + } +} + +static bool bLoggingInitialized = false; +VOID InitializeLogging() +{ + STATIC_CONTRACT_NOTHROW; + + if (bLoggingInitialized) + return; + bLoggingInitialized = true; + + InitLogging(); // You can call this in the debugger to fetch new settings +} + +VOID FlushLogging() { + STATIC_CONTRACT_NOTHROW; + + if (LogFileHandle != INVALID_HANDLE_VALUE) + { + // We must take the lock, as an OS deadlock can occur between + // FlushFileBuffers and WriteFile. + EnterLogLock(); + FlushFileBuffers( LogFileHandle ); + LeaveLogLock(); + } +} + +VOID ShutdownLogging() +{ + STATIC_CONTRACT_NOTHROW; + + if (LogFileHandle != INVALID_HANDLE_VALUE) { + LogSpew( LF_ALWAYS, FATALERROR, "Logging shutting down\n"); + CloseHandle( LogFileHandle ); + } + LogFileHandle = INVALID_HANDLE_VALUE; + bLoggingInitialized = false; +} + + +bool LoggingEnabled() +{ + STATIC_CONTRACT_LEAF; + + return ((LogFlags & LOG_ENABLE) != 0); +} + + +bool LoggingOn(DWORD facility, DWORD level) { + STATIC_CONTRACT_LEAF; + + _ASSERTE(LogFacilityMask & LF_ALWAYS); // LF_ALWAYS should always be enabled + + return((LogFlags & LOG_ENABLE) && + level <= LogVMLevel && + (facility & LogFacilityMask)); +} + +bool Logging2On(DWORD facility2, DWORD level) { + STATIC_CONTRACT_LEAF; + + _ASSERTE(LogFacilityMask2 & LF_ALWAYS); // LF_ALWAYS should always be enabled + + return((LogFlags & LOG_ENABLE) && + level <= LogVMLevel && + (facility2 & LogFacilityMask2)); +} + +// +// Don't use me directly, use the macros in log.h +// +VOID LogSpewValist(DWORD facility, DWORD level, const char *fmt, va_list args) +{ + SCAN_IGNORE_FAULT; // calls to new (nothrow) in logging code are OK + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + if (!LoggingOn(facility, level)) + return; + + DEBUG_ONLY_FUNCTION; + + LogSpewAlwaysValist(fmt, args); +} + + +VOID LogSpew2Valist(DWORD facility2, DWORD level, const char *fmt, va_list args) +{ + SCAN_IGNORE_FAULT; // calls to new (nothrow) in logging code are OK + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + if (!Logging2On(facility2, level)) + return; + + DEBUG_ONLY_FUNCTION; + + LogSpewAlwaysValist(fmt, args); +} + + +VOID LogSpewAlwaysValist(const char *fmt, va_list args) +{ + SCAN_IGNORE_FAULT; // calls to new (nothrow) in logging code are OK + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + DEBUG_ONLY_FUNCTION; + + // We can't do heap allocations at all. The current thread may have + // suspended another thread, and the suspended thread may be inside of the + // heap lock. + // + // (Some historical comments:) + // + // We must operate with a very small stack (in case we're logging durring + // a stack overflow) + // + // We're going to bypass our debug memory allocator and just allocate memory from + // the process heap. Why? Because our debug memory allocator will log out of memory + // conditions. If we're low on memory, and we try to log an out of memory condition, and we try + // and allocate memory again using the debug allocator, we could (and probably will) hit + // another low memory condition, try to log it, and we spin indefinately until we hit a stack overflow. + + const int BUFFERSIZE = 1000; + static char rgchBuffer[BUFFERSIZE]; + + EnterLogLock(); + + char * pBuffer = &rgchBuffer[0]; + DWORD buflen = 0; + DWORD written; + + static bool needsPrefix = true; + + if (needsPrefix) + buflen = sprintf_s(pBuffer, COUNTOF(rgchBuffer), "TID %04x: ", GetCurrentThreadId()); + + needsPrefix = (fmt[strlen(fmt)-1] == '\n'); + + int cCountWritten = _vsnprintf(&pBuffer[buflen], BUFFERSIZE-buflen, fmt, args ); + pBuffer[BUFFERSIZE-1] = 0; + if (cCountWritten < 0) { + buflen = BUFFERSIZE - 1; + } else { + buflen += cCountWritten; + } + + // Its a little late for this, but at least you wont continue + // trashing your program... + _ASSERTE((buflen < (DWORD) BUFFERSIZE) && "Log text is too long!") ; + +#if !PLATFORM_UNIX + //convert NL's to CR NL to fixup notepad + const int BUFFERSIZE2 = BUFFERSIZE + 500; + char rgchBuffer2[BUFFERSIZE2]; + char * pBuffer2 = &rgchBuffer2[0]; + + char *d = pBuffer2; + for (char *p = pBuffer; *p != '\0'; p++) + { + if (*p == '\n') { + _ASSERTE(d < pBuffer2 + BUFFERSIZE2); + *(d++) = '\r'; + } + + _ASSERTE(d < pBuffer2 + BUFFERSIZE2); + *(d++) = *p; + } + *d = 0; + + buflen = (DWORD)(d - pBuffer2); + pBuffer = pBuffer2; +#endif // PLATFORM_UNIX + + if (LogFlags & LOG_ENABLE_FILE_LOGGING && LogFileHandle != INVALID_HANDLE_VALUE) + { + WriteFile(LogFileHandle, pBuffer, buflen, &written, NULL); + if (LogFlags & LOG_ENABLE_FLUSH_FILE) { + FlushFileBuffers( LogFileHandle ); + } + } + + if (LogFlags & LOG_ENABLE_CONSOLE_LOGGING) + { + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), pBuffer, buflen, &written, 0); + //<TODO>@TODO ...Unnecessary to flush console?</TODO> + if (LogFlags & LOG_ENABLE_FLUSH_FILE) + FlushFileBuffers( GetStdHandle(STD_OUTPUT_HANDLE) ); + } + + if (LogFlags & LOG_ENABLE_DEBUGGER_LOGGING) + { + OutputDebugStringA(pBuffer); + } + + LeaveLogLock(); +} + +VOID LogSpew(DWORD facility, DWORD level, const char *fmt, ... ) +{ + STATIC_CONTRACT_WRAPPER; + + ENTER_SO_NOT_MAINLINE_CODE; + +#ifdef SELF_NO_HOST + if (TRUE) +#else //!SELF_NO_HOST + if (CanThisThreadCallIntoHost()) +#endif //!SELF_NO_HOST + { + va_list args; + va_start( args, fmt ); + LogSpewValist (facility, level, fmt, args); + } + else + { + // Cannot acquire the required lock, as this would call back + // into the host. Eat the log message. + } + + LEAVE_SO_NOT_MAINLINE_CODE; +} + +VOID LogSpew2(DWORD facility2, DWORD level, const char *fmt, ... ) +{ + STATIC_CONTRACT_WRAPPER; + + ENTER_SO_NOT_MAINLINE_CODE; + +#ifdef SELF_NO_HOST + if (TRUE) +#else //!SELF_NO_HOST + if (CanThisThreadCallIntoHost()) +#endif //!SELF_NO_HOST + { + va_list args; + va_start( args, fmt ); + LogSpew2Valist(facility2, level, fmt, args); + } + else + { + // Cannot acquire the required lock, as this would call back + // into the host. Eat the log message. + } + + LEAVE_SO_NOT_MAINLINE_CODE; +} + +VOID LogSpewAlways (const char *fmt, ... ) +{ + STATIC_CONTRACT_WRAPPER; + + ENTER_SO_NOT_MAINLINE_CODE; + +#ifdef SELF_NO_HOST + if (TRUE) +#else //!SELF_NO_HOST + if (CanThisThreadCallIntoHost()) +#endif //!SELF_NO_HOST + { + va_list args; + va_start( args, fmt ); + LogSpewValist (LF_ALWAYS, LL_ALWAYS, fmt, args); + } + else + { + // Cannot acquire the required lock, as this would call back + // into the host. Eat the log message. + } + + LEAVE_SO_NOT_MAINLINE_CODE; +} + +#endif // LOGGING + diff --git a/src/utilcode/makepath.cpp b/src/utilcode/makepath.cpp new file mode 100644 index 0000000000..402280bd89 --- /dev/null +++ b/src/utilcode/makepath.cpp @@ -0,0 +1,208 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +/*** +*makepath.c - create path name from components +* + +* +*Purpose: +* To provide support for creation of full path names from components +* +*******************************************************************************/ +#include "stdafx.h" +#include "winwrap.h" +#include "utilcode.h" +#include "ex.h" + +/*** +*void _makepath() - build path name from components +* +*Purpose: +* create a path name from its individual components +* +*Entry: +* WCHAR *path - pointer to buffer for constructed path +* WCHAR *drive - pointer to drive component, may or may not contain +* trailing ':' +* WCHAR *dir - pointer to subdirectory component, may or may not include +* leading and/or trailing '/' or '\' characters +* WCHAR *fname - pointer to file base name component +* WCHAR *ext - pointer to extension component, may or may not contain +* a leading '.'. +* +*Exit: +* path - pointer to constructed path name +* +*Exceptions: +* +*******************************************************************************/ + +void MakePath ( + __out_ecount (MAX_PATH) register WCHAR *path, + __in LPCWSTR drive, + __in LPCWSTR dir, + __in LPCWSTR fname, + __in LPCWSTR ext + ) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + } + CONTRACTL_END + + register const WCHAR *p; + register DWORD count = 0; + + /* we assume that the arguments are in the following form (although we + * do not diagnose invalid arguments or illegal filenames (such as + * names longer than 8.3 or with illegal characters in them) + * + * drive: + * A ; or + * A: + * dir: + * \top\next\last\ ; or + * /top/next/last/ ; or + * either of the above forms with either/both the leading + * and trailing / or \ removed. Mixed use of '/' and '\' is + * also tolerated + * fname: + * any valid file name + * ext: + * any valid extension (none if empty or null ) + */ + + /* copy drive */ + + if (drive && *drive) { + *path++ = *drive; + *path++ = _T(':'); + count += 2; + } + + /* copy dir */ + + if ((p = dir)) { + while (*p) { + *path++ = *p++; + count++; + + if (count == MAX_PATH) { + --path; + *path = _T('\0'); + return; + } + } + +#ifdef _MBCS + if (*(p=_mbsdec(dir,p)) != _T('/') && *p != _T('\\')) { +#else /* _MBCS */ + // suppress warning for the following line; this is safe but would require significant code + // delta for prefast to understand. +#ifdef _PREFAST_ + #pragma warning( suppress: 26001 ) +#endif + if (*(p-1) != _T('/') && *(p-1) != _T('\\')) { +#endif /* _MBCS */ + *path++ = _T('\\'); + count++; + + if (count == MAX_PATH) { + --path; + *path = _T('\0'); + return; + } + } + } + + /* copy fname */ + + if ((p = fname)) { + while (*p) { + *path++ = *p++; + count++; + + if (count == MAX_PATH) { + --path; + *path = _T('\0'); + return; + } + } + } + + /* copy ext, including 0-terminator - check to see if a '.' needs + * to be inserted. + */ + + if ((p = ext)) { + if (*p && *p != _T('.')) { + *path++ = _T('.'); + count++; + + if (count == MAX_PATH) { + --path; + *path = _T('\0'); + return; + } + } + + while ((*path++ = *p++)) { + count++; + + if (count == MAX_PATH) { + --path; + *path = _T('\0'); + return; + } + } + } + else { + /* better add the 0-terminator */ + *path = _T('\0'); + } +} + +#if !defined(FEATURE_CORECLR) +static LPCWSTR g_wszProcessExePath = NULL; + +HRESULT GetProcessExePath(LPCWSTR *pwszProcessExePath) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + CONSISTENCY_CHECK(CheckPointer(pwszProcessExePath)); + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + if (g_wszProcessExePath == NULL) + { + NewArrayHolder<WCHAR> wszProcName = new (nothrow) WCHAR[_MAX_PATH]; + IfNullRet(wszProcName); + + DWORD cchProcName = WszGetModuleFileName(NULL, wszProcName, _MAX_PATH); + if (cchProcName == 0) + { + return HRESULT_FROM_GetLastError(); + } + + if (InterlockedCompareExchangeT(&g_wszProcessExePath, const_cast<LPCWSTR>(wszProcName.GetValue()), NULL) == NULL) + { + wszProcName.SuppressRelease(); + } + } + _ASSERTE(g_wszProcessExePath != NULL); + _ASSERTE(SUCCEEDED(hr)); + + *pwszProcessExePath = g_wszProcessExePath; + return hr; +} +#endif + diff --git a/src/utilcode/md5.cpp b/src/utilcode/md5.cpp new file mode 100644 index 0000000000..5b316122ee --- /dev/null +++ b/src/utilcode/md5.cpp @@ -0,0 +1,293 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// +// md5.cpp +// + +// + +#include "stdafx.h" + +#include <stdlib.h> +#include "md5.h" +#include "contract.h" + +void MD5::Init(BOOL fConstructed) + { + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + // These two fields are read only, and so initialization thereof can be + // omitted on the second and subsequent hashes using this same instance. + // + if (!fConstructed) + { + memset(m_padding, 0, 64); + m_padding[0]=0x80; + } + + m_cbitHashed = 0; + m_cbData = 0; + u.m_a = 0x67452301; // magic + u.m_b = 0xefcdab89; // ... constants + u.m_c = 0x98badcfe; // ... per + u.m_d = 0x10325476; // .. RFC1321 + } + + +void MD5::HashMore(const void* pvInput, ULONG cbInput) +// Hash the additional data into the state + { + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + const BYTE* pbInput = (const BYTE*)pvInput; + + m_cbitHashed += (((ULONGLONG)cbInput) <<3); + + ULONG cbRemaining = 64 - m_cbData; + if (cbInput < cbRemaining) + { + // It doesn't fill up the buffer, so just store it + memcpy(&m_data[m_cbData], pbInput, cbInput); + m_cbData += cbInput; + } + else + { + // It does fill up the buffer. Fill up all that it will take + memcpy(&m_data[m_cbData], pbInput, cbRemaining); + + // Hash the now-full buffer + MD5Transform(m_state, (ULONG*)&m_data[0]); +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:22019) // Suppress this OACR warning 22019: + // 'cbInput-=cbRemaining' may be greater than 'cbInput'. This can be caused by integer underflow. + // This could yield an incorrect loop index 'cbInput>=64' + // We only enter the else clause here if cbInput >= cbRemaining +#endif + cbInput -= cbRemaining; +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + pbInput += cbRemaining; + + // Hash the data in 64-byte runs, starting just after what we've copied + while (cbInput >= 64) + { + MD5Transform(m_state, (ULONG*)pbInput); + pbInput += 64; + cbInput -= 64; + } + + // Store the tail of the input into the buffer + memcpy(&m_data[0], pbInput, cbInput); + m_cbData = cbInput; + } + } + + +void MD5::GetHashValue(MD5HASHDATA* phash) +// Finalize the hash by appending the necessary padding and length count. Then +// return the final hash value. + { + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + union { + ULONGLONG cbitHashed; + BYTE rgb[8]; + }u; + + // Remember how many bits there were in the input data + u.cbitHashed = m_cbitHashed; + + // Calculate amount of padding needed. Enough so total byte count hashed is 56 mod 64 + ULONG cbPad = (m_cbData < 56 ? 56-m_cbData : 120-m_cbData); + + // Hash the padding + HashMore(&m_padding[0], cbPad); + + // Hash the (before padding) bit length + HashMore(&u.rgb[0], 8); + + // Return the hash value + memcpy(phash, &this->u.m_a, 16); + } + + + + + //////////////////////////////////////////////////////////////// + // + // ROTATE_LEFT should be a macro that updates its first operand + // with its present value rotated left by the amount of its + // second operand, which is always a constant. + // + // One way to portably do it would be + // + // #define ROL(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + // #define ROTATE_LEFT(x,n) (x) = ROL(x,n) + // + // but our compiler has an intrinsic! + + #define ROTATE_LEFT(x,n) (x) = _lrotl(x,n) + + //////////////////////////////////////////////////////////////// + // + // Constants used in each of the various rounds + + #define MD5_S11 7 + #define MD5_S12 12 + #define MD5_S13 17 + #define MD5_S14 22 + #define MD5_S21 5 + #define MD5_S22 9 + #define MD5_S23 14 + #define MD5_S24 20 + #define MD5_S31 4 + #define MD5_S32 11 + #define MD5_S33 16 + #define MD5_S34 23 + #define MD5_S41 6 + #define MD5_S42 10 + #define MD5_S43 15 + #define MD5_S44 21 + + //////////////////////////////////////////////////////////////// + // + // The core twiddle functions + +// #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) // the function per the standard + #define F(x, y, z) ((((z) ^ (y)) & (x)) ^ (z)) // an alternate encoding + +// #define G(x, y, z) (((x) & (z)) | ((y) & (~z))) // the function per the standard + #define G(x, y, z) ((((x) ^ (y)) & (z)) ^ (y)) // an alternate encoding + + #define H(x, y, z) ((x) ^ (y) ^ (z)) + + #define I(x, y, z) ((y) ^ ((x) | (~z))) + + #define AC(ac) ((ULONG)(ac)) + + //////////////////////////////////////////////////////////////// + + #define FF(a, b, c, d, x, s, ac) { \ + (a) += F (b,c,d) + (x) + (AC(ac)); \ + ROTATE_LEFT (a, s); \ + (a) += (b); \ + } + + //////////////////////////////////////////////////////////////// + + #define GG(a, b, c, d, x, s, ac) { \ + (a) += G (b,c,d) + (x) + (AC(ac)); \ + ROTATE_LEFT (a, s); \ + (a) += (b); \ + } + + //////////////////////////////////////////////////////////////// + + #define HH(a, b, c, d, x, s, ac) { \ + (a) += H (b,c,d) + (x) + (AC(ac)); \ + ROTATE_LEFT (a, s); \ + (a) += (b); \ + } + + //////////////////////////////////////////////////////////////// + + #define II(a, b, c, d, x, s, ac) { \ + (a) += I (b,c,d) + (x) + (AC(ac)); \ + ROTATE_LEFT (a, s); \ + (a) += (b); \ + } + + void __stdcall MD5Transform(ULONG state[4], const ULONG* data) + { + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + ULONG a=state[0]; + ULONG b=state[1]; + ULONG c=state[2]; + ULONG d=state[3]; + + // Round 1 + FF (a, b, c, d, data[ 0], MD5_S11, 0xd76aa478); // 1 + FF (d, a, b, c, data[ 1], MD5_S12, 0xe8c7b756); // 2 + FF (c, d, a, b, data[ 2], MD5_S13, 0x242070db); // 3 + FF (b, c, d, a, data[ 3], MD5_S14, 0xc1bdceee); // 4 + FF (a, b, c, d, data[ 4], MD5_S11, 0xf57c0faf); // 5 + FF (d, a, b, c, data[ 5], MD5_S12, 0x4787c62a); // 6 + FF (c, d, a, b, data[ 6], MD5_S13, 0xa8304613); // 7 + FF (b, c, d, a, data[ 7], MD5_S14, 0xfd469501); // 8 + FF (a, b, c, d, data[ 8], MD5_S11, 0x698098d8); // 9 + FF (d, a, b, c, data[ 9], MD5_S12, 0x8b44f7af); // 10 + FF (c, d, a, b, data[10], MD5_S13, 0xffff5bb1); // 11 + FF (b, c, d, a, data[11], MD5_S14, 0x895cd7be); // 12 + FF (a, b, c, d, data[12], MD5_S11, 0x6b901122); // 13 + FF (d, a, b, c, data[13], MD5_S12, 0xfd987193); // 14 + FF (c, d, a, b, data[14], MD5_S13, 0xa679438e); // 15 + FF (b, c, d, a, data[15], MD5_S14, 0x49b40821); // 16 + + // Round 2 + GG (a, b, c, d, data[ 1], MD5_S21, 0xf61e2562); // 17 + GG (d, a, b, c, data[ 6], MD5_S22, 0xc040b340); // 18 + GG (c, d, a, b, data[11], MD5_S23, 0x265e5a51); // 19 + GG (b, c, d, a, data[ 0], MD5_S24, 0xe9b6c7aa); // 20 + GG (a, b, c, d, data[ 5], MD5_S21, 0xd62f105d); // 21 + GG (d, a, b, c, data[10], MD5_S22, 0x2441453); // 22 + GG (c, d, a, b, data[15], MD5_S23, 0xd8a1e681); // 23 + GG (b, c, d, a, data[ 4], MD5_S24, 0xe7d3fbc8); // 24 + GG (a, b, c, d, data[ 9], MD5_S21, 0x21e1cde6); // 25 + GG (d, a, b, c, data[14], MD5_S22, 0xc33707d6); // 26 + GG (c, d, a, b, data[ 3], MD5_S23, 0xf4d50d87); // 27 + GG (b, c, d, a, data[ 8], MD5_S24, 0x455a14ed); // 28 + GG (a, b, c, d, data[13], MD5_S21, 0xa9e3e905); // 29 + GG (d, a, b, c, data[ 2], MD5_S22, 0xfcefa3f8); // 30 + GG (c, d, a, b, data[ 7], MD5_S23, 0x676f02d9); // 31 + GG (b, c, d, a, data[12], MD5_S24, 0x8d2a4c8a); // 32 + + // Round 3 + HH (a, b, c, d, data[ 5], MD5_S31, 0xfffa3942); // 33 + HH (d, a, b, c, data[ 8], MD5_S32, 0x8771f681); // 34 + HH (c, d, a, b, data[11], MD5_S33, 0x6d9d6122); // 35 + HH (b, c, d, a, data[14], MD5_S34, 0xfde5380c); // 36 + HH (a, b, c, d, data[ 1], MD5_S31, 0xa4beea44); // 37 + HH (d, a, b, c, data[ 4], MD5_S32, 0x4bdecfa9); // 38 + HH (c, d, a, b, data[ 7], MD5_S33, 0xf6bb4b60); // 39 + HH (b, c, d, a, data[10], MD5_S34, 0xbebfbc70); // 40 + HH (a, b, c, d, data[13], MD5_S31, 0x289b7ec6); // 41 + HH (d, a, b, c, data[ 0], MD5_S32, 0xeaa127fa); // 42 + HH (c, d, a, b, data[ 3], MD5_S33, 0xd4ef3085); // 43 + HH (b, c, d, a, data[ 6], MD5_S34, 0x4881d05); // 44 + HH (a, b, c, d, data[ 9], MD5_S31, 0xd9d4d039); // 45 + HH (d, a, b, c, data[12], MD5_S32, 0xe6db99e5); // 46 + HH (c, d, a, b, data[15], MD5_S33, 0x1fa27cf8); // 47 + HH (b, c, d, a, data[ 2], MD5_S34, 0xc4ac5665); // 48 + + // Round 4 + II (a, b, c, d, data[ 0], MD5_S41, 0xf4292244); // 49 + II (d, a, b, c, data[ 7], MD5_S42, 0x432aff97); // 50 + II (c, d, a, b, data[14], MD5_S43, 0xab9423a7); // 51 + II (b, c, d, a, data[ 5], MD5_S44, 0xfc93a039); // 52 + II (a, b, c, d, data[12], MD5_S41, 0x655b59c3); // 53 + II (d, a, b, c, data[ 3], MD5_S42, 0x8f0ccc92); // 54 + II (c, d, a, b, data[10], MD5_S43, 0xffeff47d); // 55 + II (b, c, d, a, data[ 1], MD5_S44, 0x85845dd1); // 56 + II (a, b, c, d, data[ 8], MD5_S41, 0x6fa87e4f); // 57 + II (d, a, b, c, data[15], MD5_S42, 0xfe2ce6e0); // 58 + II (c, d, a, b, data[ 6], MD5_S43, 0xa3014314); // 59 + II (b, c, d, a, data[13], MD5_S44, 0x4e0811a1); // 60 + II (a, b, c, d, data[ 4], MD5_S41, 0xf7537e82); // 61 + II (d, a, b, c, data[11], MD5_S42, 0xbd3af235); // 62 + II (c, d, a, b, data[ 2], MD5_S43, 0x2ad7d2bb); // 63 + II (b, c, d, a, data[ 9], MD5_S44, 0xeb86d391); // 64 + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + }
\ No newline at end of file diff --git a/src/utilcode/memorypool.cpp b/src/utilcode/memorypool.cpp new file mode 100644 index 0000000000..5330c4b3dc --- /dev/null +++ b/src/utilcode/memorypool.cpp @@ -0,0 +1,318 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#include "stdafx.h" + +#include "memorypool.h" +#include "ex.h" + +size_t MemoryPool::GetSize() +{ + LIMITED_METHOD_CONTRACT; + size_t retval=0; + + Block *block = m_blocks; + while (block != NULL) + { + retval+=(BYTE*)block->elementsEnd-(BYTE*)block->elements; + block = block->next; + } + return retval; +} + +#ifndef DACCESS_COMPILE + +BOOL MemoryPool::AddBlock(SIZE_T elementCount) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + INJECT_FAULT(return FALSE;); + } CONTRACTL_END; + + // + // Check for arithmetic overflow + // + S_SIZE_T cbBlockSize = S_SIZE_T(elementCount) * S_SIZE_T(m_elementSize); + S_SIZE_T cbAllocSize = S_SIZE_T(sizeof(Block)) + cbBlockSize; + if (cbBlockSize.IsOverflow() || cbAllocSize.IsOverflow()) + return FALSE; + + // + // Allocate the new block. + // + + Block *block = (Block *) new (nothrow) BYTE [cbAllocSize.Value()]; + + if (block == NULL) + return FALSE; + + // + // Chain all elements together for the free list + // + + _ASSERTE(m_freeList == NULL); + Element **prev = &m_freeList; + + Element *e = block->elements; + Element *eEnd = (Element *) ((BYTE*) block->elements + elementCount*m_elementSize); + while (e < eEnd) + { + *prev = e; + prev = &e->next; +#if _DEBUG + DeadBeef(e); +#endif + e = (Element*) ((BYTE*)e + m_elementSize); + } + + *prev = NULL; + + // + // Initialize the other block fields & link the block into the block list + // + + block->elementsEnd = e; + block->next = m_blocks; + m_blocks = block; + + return TRUE; +} + +void MemoryPool::DeadBeef(Element *element) +{ +#if _DEBUG + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + } CONTRACTL_END; + + int *i = &element->deadBeef; + int *iEnd = (int*) ((BYTE*)element + m_elementSize); + while (i < iEnd) + *i++ = (int) 0xdeadbeef; +#else + LIMITED_METHOD_CONTRACT; +#endif +} + +MemoryPool::MemoryPool(SIZE_T elementSize, SIZE_T initGrowth, SIZE_T initCount) + : m_elementSize(elementSize), + m_growCount(initGrowth), + m_blocks(NULL), + m_freeList(NULL) +{ + CONTRACTL { + if (initCount) THROWS; else NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + _ASSERTE(elementSize >= sizeof(Element)); + _ASSERTE((elementSize & ((sizeof(PVOID)-1))) == 0); + + if (initCount > 0) + AddBlock(initCount); +} + +MemoryPool::~MemoryPool() +{ + LIMITED_METHOD_CONTRACT; + Block *block = m_blocks; + while (block != NULL) + { + Block *next = block->next; + delete [] (BYTE*) block; + block = next; + } +} + +BOOL MemoryPool::IsElement(void *element) +{ + LIMITED_METHOD_CONTRACT; + + Block *block = m_blocks; + while (block != NULL) + { + if (element >= block->elements && + element < block->elementsEnd ) + { + return ((BYTE *)element - (BYTE*)block->elements) % m_elementSize == 0; + } + block = block->next; + } + + return FALSE; +} + +BOOL MemoryPool::IsAllocatedElement(void *element) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + } CONTRACTL_END; + + if (!IsElement(element)) + return FALSE; + + // + // Now, make sure the element isn't + // in the free list. + // + +#if _DEBUG + // + // In a debug build, all objects on the free list + // will be marked with deadbeef. This means that + // if the object is not deadbeef, it's not on the + // free list. + // + // This check will give us decent performance in + // a debug build for FreeElement, since we + // always expect to return TRUE in that case. + // + + if (((Element*)element)->deadBeef != (int) 0xdeadBeef) + return TRUE; +#endif + + Element *f = m_freeList; + while (f != NULL) + { + if (f == element) + return FALSE; + f = f->next; + } + +#if _DEBUG + // + // We should never get here in a debug build, because + // all free elements should be deadbeefed. + // + _ASSERTE(!"Unreachable"); +#endif + + return TRUE; +} + +void *MemoryPool::AllocateElement() +{ + CONTRACTL { + THROWS; + GC_NOTRIGGER; + } CONTRACTL_END; + + void *element = AllocateElementNoThrow(); + if (element == NULL) + ThrowOutOfMemory(); + + return element; +} + +void *MemoryPool::AllocateElementNoThrow() +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + INJECT_FAULT( return FALSE; ); + } CONTRACTL_END; + + void *element = m_freeList; + + if (element == NULL) + { + if (!AddBlock(m_growCount)) + return NULL; + + m_growCount *= 2; + element = m_freeList; + } + + // if we come there means that addblock succeeded and m_freelist isn't null anymore + PREFIX_ASSUME(m_freeList!= NULL); + m_freeList = m_freeList->next; + + return element; +} + +void MemoryPool::FreeElement(void *element) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + } CONTRACTL_END; + +#if _DEBUG // don't want to do this assert in a non-debug build; it is expensive + _ASSERTE(IsAllocatedElement(element)); +#endif + + Element *e = (Element *) element; + +#if _DEBUG + DeadBeef(e); +#endif + + e->next = m_freeList; + m_freeList = e; +} + +void MemoryPool::FreeAllElements() +{ + LIMITED_METHOD_CONTRACT; + + Block *block = m_blocks; + while (block != NULL) + { + Block *next = block->next; + delete [] block; + block = next; + } + + m_freeList = NULL; + m_blocks = NULL; +} + + + +MemoryPool::Iterator::Iterator(MemoryPool *pool) +{ + LIMITED_METHOD_CONTRACT; + + // + // Warning! This only works if you haven't freed + // any elements. + // + + m_next = pool->m_blocks; + m_e = NULL; + m_eEnd = NULL; + m_end = (BYTE*) pool->m_freeList; + m_size = pool->m_elementSize; +} + +BOOL MemoryPool::Iterator::Next() +{ + LIMITED_METHOD_CONTRACT; + + if (m_e == m_eEnd + || (m_e == m_end && m_end != NULL)) + { + if (m_next == NULL) + return FALSE; + m_e = (BYTE*) m_next->elements; + m_eEnd = (BYTE*) m_next->elementsEnd; + m_next = m_next->next; + if (m_e == m_end) + return FALSE; + } + + m_e += m_size; + + return TRUE; +} + +#endif diff --git a/src/utilcode/namespaceutil.cpp b/src/utilcode/namespaceutil.cpp new file mode 100644 index 0000000000..ac09443760 --- /dev/null +++ b/src/utilcode/namespaceutil.cpp @@ -0,0 +1,680 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// NamespaceUtil.cpp +// + +// +// Helpers for converting namespace separators. +// +//***************************************************************************** +#include "stdafx.h" +#include "corhdr.h" +#include "corhlpr.h" +#include "sstring.h" +#include "utilcode.h" + +#ifndef _ASSERTE +#define _ASSERTE(foo) +#endif + +#include "nsutilpriv.h" + + +//***************************************************************************** +// Determine how many chars large a fully qualified name would be given the +// two parts of the name. The return value includes room for every character +// in both names, as well as room for the separator and a final terminator. +//***************************************************************************** +int ns::GetFullLength( // Number of chars in full name. + const WCHAR *szNameSpace, // Namspace for value. + const WCHAR *szName) // Name of value. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + int iLen = 1; // Null terminator. + if (szNameSpace) + iLen += (int)wcslen(szNameSpace); + if (szName) + iLen += (int)wcslen(szName); + if (szNameSpace && *szNameSpace && szName && *szName) + ++iLen; + return iLen; +} //int ns::GetFullLength() + +int ns::GetFullLength( // Number of chars in full name. + LPCUTF8 szNameSpace, // Namspace for value. + LPCUTF8 szName) // Name of value. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + + int iLen = 1; + if (szNameSpace) + iLen += (int)strlen(szNameSpace); + if (szName) + iLen += (int)strlen(szName); + if (szNameSpace && *szNameSpace && szName && *szName) + ++iLen; + return iLen; +} //int ns::GetFullLength() + + +//***************************************************************************** +// Scan the string from the rear looking for the first valid separator. If +// found, return a pointer to it. Else return null. This code is smart enough +// to skip over special sequences, such as: +// a.b..ctor +// ^ +// | +// The ".ctor" is considered one token. +//***************************************************************************** +WCHAR *ns::FindSep( // Pointer to separator or null. + const WCHAR *szPath) // The path to look in. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + _ASSERTE(szPath); + WCHAR *ptr = (WCHAR*)wcsrchr(szPath, NAMESPACE_SEPARATOR_WCHAR); + if((ptr == NULL) || (ptr == szPath)) return NULL; + if(*(ptr - 1) == NAMESPACE_SEPARATOR_WCHAR) // here ptr is at least szPath+1 + --ptr; + return ptr; +} //WCHAR *ns::FindSep() + +//<TODO>@todo: this isn't dbcs safe if this were ansi, but this is utf8. Still an issue?</TODO> +LPUTF8 ns::FindSep( // Pointer to separator or null. + LPCUTF8 szPath) // The path to look in. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_SUPPORTS_DAC; + + _ASSERTE(szPath); + LPUTF8 ptr = const_cast<LPUTF8>(strrchr(szPath, NAMESPACE_SEPARATOR_CHAR)); + if((ptr == NULL) || (ptr == szPath)) return NULL; + if(*(ptr - 1) == NAMESPACE_SEPARATOR_CHAR) // here ptr is at least szPath+1 + --ptr; + return ptr; +} //LPUTF8 ns::FindSep() + + + +//***************************************************************************** +// Take a path and find the last separator (nsFindSep), and then replace the +// separator with a '\0' and return a pointer to the name. So for example: +// a.b.c +// becomes two strings "a.b" and "c" and the return value points to "c". +//***************************************************************************** +WCHAR *ns::SplitInline( // Pointer to name portion. + __inout __inout_z WCHAR *szPath) // The path to split. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + WCHAR *ptr = ns::FindSep(szPath); + if (ptr) + { + *ptr = 0; + ++ptr; + } + return ptr; +} // WCHAR *ns::SplitInline() + +LPUTF8 ns::SplitInline( // Pointer to name portion. + __inout __inout_z LPUTF8 szPath) // The path to split. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + LPUTF8 ptr = ns::FindSep(szPath); + if (ptr) + { + *ptr = 0; + ++ptr; + } + return ptr; +} // LPUTF8 ns::SplitInline() + +void ns::SplitInline( + __inout __inout_z LPWSTR szPath, // Path to split. + LPCWSTR &szNameSpace, // Return pointer to namespace. + LPCWSTR &szName) // Return pointer to name. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + WCHAR *ptr = SplitInline(szPath); + if (ptr) + { + szNameSpace = szPath; + szName = ptr; + } + else + { + szNameSpace = 0; + szName = szPath; + } +} // void ns::SplitInline() + +void ns::SplitInline( + __inout __inout_z LPUTF8 szPath, // Path to split. + LPCUTF8 &szNameSpace, // Return pointer to namespace. + LPCUTF8 &szName) // Return pointer to name. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + LPUTF8 ptr = SplitInline(szPath); + if (ptr) + { + szNameSpace = szPath; + szName = ptr; + } + else + { + szNameSpace = 0; + szName = szPath; + } +} // void ns::SplitInline() + + +//***************************************************************************** +// Split the last parsable element from the end of the string as the name, +// the first part as the namespace. +//***************************************************************************** +int ns::SplitPath( // true ok, false trunction. + const WCHAR *szPath, // Path to split. + __out_ecount(cchNameSpace) WCHAR *szNameSpace, // Output for namespace value. + int cchNameSpace, // Max chars for output. + __out_ecount(cchName) WCHAR *szName, // Output for name. + int cchName) // Max chars for output. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + const WCHAR *ptr = ns::FindSep(szPath); + size_t iLen = (ptr) ? ptr - szPath : 0; + size_t iCopyMax; + int brtn = true; + if (szNameSpace && cchNameSpace) + { + _ASSERTE(cchNameSpace > 1); + iCopyMax = cchNameSpace - 1; + iCopyMax = min(iCopyMax, iLen); + wcsncpy_s(szNameSpace, cchNameSpace, szPath, iCopyMax); + szNameSpace[iCopyMax] = 0; + + if (iLen >= (size_t)cchNameSpace) + brtn = false; + } + + if (szName && cchName) + { + _ASSERTE(cchName > 1); + iCopyMax = cchName - 1; + if (ptr) + ++ptr; + else + ptr = szPath; + iLen = (int)wcslen(ptr); + iCopyMax = min(iCopyMax, iLen); + wcsncpy_s(szName, cchName, ptr, iCopyMax); + szName[iCopyMax] = 0; + + if (iLen >= (size_t)cchName) + brtn = false; + } + return brtn; +} // int ns::SplitPath() + + +int ns::SplitPath( // true ok, false trunction. + LPCUTF8 szPath, // Path to split. + __out_ecount_opt (cchNameSpace) LPUTF8 szNameSpace, // Output for namespace value. + int cchNameSpace, // Max chars for output. + __out_ecount_opt (cchName) LPUTF8 szName, // Output for name. + int cchName) // Max chars for output. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + LPCUTF8 ptr = ns::FindSep(szPath); + size_t iLen = (ptr) ? ptr - szPath : 0; + size_t iCopyMax; + int brtn = true; + if (szNameSpace && cchNameSpace) + { + _ASSERTE(cchNameSpace > 1); + iCopyMax = cchNameSpace-1; + iCopyMax = min(iCopyMax, iLen); + strncpy_s(szNameSpace, cchNameSpace, szPath, iCopyMax); + szNameSpace[iCopyMax] = 0; + + if (iLen >= (size_t)cchNameSpace) + brtn = false; + } + + if (szName && cchName) + { + _ASSERTE(cchName > 1); + iCopyMax = cchName-1; + if (ptr) + ++ptr; + else + ptr = szPath; + iLen = (int)strlen(ptr); + iCopyMax = min(iCopyMax, iLen); + strncpy_s(szName, cchName, ptr, iCopyMax); + szName[iCopyMax] = 0; + + if (iLen >= (size_t)cchName) + brtn = false; + } + return brtn; +} // int ns::SplitPath() + + +//***************************************************************************** +// Take two values and put them together in a fully qualified path using the +// correct separator. +//***************************************************************************** +int ns::MakePath( // true ok, false truncation. + __out_ecount(cchChars) WCHAR *szOut, // output path for name. + int cchChars, // max chars for output path. + const WCHAR *szNameSpace, // Namespace. + const WCHAR *szName) // Name. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + if (cchChars < 1) + return false; + + if (szOut) + *szOut = 0; + else + return false; + + if (szNameSpace && *szNameSpace != W('\0')) + { + if (wcsncpy_s(szOut, cchChars, szNameSpace, _TRUNCATE) == STRUNCATE) + return false; + + // Add namespace separator if a non-empty name was supplied + if (szName && *szName != W('\0')) + { + if (wcsncat_s(szOut, cchChars, NAMESPACE_SEPARATOR_WSTR, _TRUNCATE) == STRUNCATE) + { + return false; + } + } + } + + if (szName && *szName) + { + if (wcsncat_s(szOut, cchChars, szName, _TRUNCATE) == STRUNCATE) + return false; + } + + return true; +} // int ns::MakePath() + +int ns::MakePath( // true ok, false truncation. + __out_ecount(cchChars) LPUTF8 szOut, // output path for name. + int cchChars, // max chars for output path. + LPCUTF8 szNameSpace, // Namespace. + LPCUTF8 szName) // Name. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + if (cchChars < 1) + return false; + + if (szOut) + *szOut = 0; + else + return false; + + if (szNameSpace && *szNameSpace != W('\0')) + { + if (strncpy_s(szOut, cchChars, szNameSpace, _TRUNCATE) == STRUNCATE) + return false; + + // Add namespace separator if a non-empty name was supplied + if (szName && *szName != W('\0')) + { + if (strncat_s(szOut, cchChars, NAMESPACE_SEPARATOR_STR, _TRUNCATE) == STRUNCATE) + { + return false; + } + } + } + + if (szName && *szName) + { + if (strncat_s(szOut, cchChars, szName, _TRUNCATE) == STRUNCATE) + return false; + } + + return true; + +} // int ns::MakePath() + +int ns::MakePath( // true ok, false truncation. + __out_ecount(cchChars) WCHAR *szOut, // output path for name. + int cchChars, // max chars for output path. + LPCUTF8 szNamespace, // Namespace. + LPCUTF8 szName) // Name. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + if (cchChars < 1) + return false; + + if (szOut) + *szOut = 0; + else + return false; + + if (szNamespace != NULL && *szNamespace != '\0') + { + if (cchChars < 2) + return false; + + int count; + + // We use cBuffer - 2 to account for the '.' and at least a 1 character name below. + count = WszMultiByteToWideChar(CP_UTF8, 0, szNamespace, -1, szOut, cchChars-2); + if (count == 0) + return false; // Supply a bigger buffer! + + // buffer access is bounded: WszMultiByteToWideChar returns 0 if access doesn't fit in range +#ifdef _PREFAST_ + #pragma warning( suppress: 26015 ) +#endif + szOut[count-1] = NAMESPACE_SEPARATOR_WCHAR; + szOut += count; + cchChars -= count; + } + + if (((cchChars == 0) && (szName != NULL) && (*szName != '\0')) || + (WszMultiByteToWideChar(CP_UTF8, 0, szName, -1, szOut, cchChars) == 0)) + return false; // supply a bigger buffer! + return true; +} // int ns::MakePath() + +int ns::MakePath( // true ok, false out of memory + CQuickBytes &qb, // Where to put results. + LPCUTF8 szNameSpace, // Namespace for name. + LPCUTF8 szName) // Final part of name. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FAULT; + + int iLen = 2; + if (szNameSpace) + iLen += (int)strlen(szNameSpace); + if (szName) + iLen += (int)strlen(szName); + LPUTF8 szOut = (LPUTF8) qb.AllocNoThrow(iLen); + if (!szOut) + return false; + return ns::MakePath(szOut, iLen, szNameSpace, szName); +} // int ns::MakePath() + +int ns::MakePath( // true ok, false out of memory + CQuickArray<WCHAR> &qa, // Where to put results. + LPCUTF8 szNameSpace, // Namespace for name. + LPCUTF8 szName) // Final part of name. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FAULT; + + int iLen = 2; + if (szNameSpace) + iLen += (int)strlen(szNameSpace); + if (szName) + iLen += (int)strlen(szName); + WCHAR *szOut = (WCHAR *) qa.AllocNoThrow(iLen); + if (!szOut) + return false; + return ns::MakePath(szOut, iLen, szNameSpace, szName); +} // int ns::MakePath() + +int ns::MakePath( // true ok, false out of memory + CQuickBytes &qb, // Where to put results. + const WCHAR *szNameSpace, // Namespace for name. + const WCHAR *szName) // Final part of name. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FAULT; + + int iLen = 2; + if (szNameSpace) + iLen += (int)wcslen(szNameSpace); + if (szName) + iLen += (int)wcslen(szName); + WCHAR *szOut = (WCHAR *) qb.AllocNoThrow(iLen * sizeof(WCHAR)); + if (!szOut) + return false; + return ns::MakePath(szOut, iLen, szNameSpace, szName); +} // int ns::MakePath() + +void ns::MakePath( // throws on out of memory + SString &ssBuf, // Where to put results. + const SString &ssNameSpace, // Namespace for name. + const SString &ssName) // Final part of name. +{ + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FAULT; + + ssBuf.Clear(); + + if (!ssNameSpace.IsEmpty()) + { + if (ssName.IsEmpty()) + { + ssBuf.Set(ssNameSpace); + } + else + { + SString s(SString::Literal, NAMESPACE_SEPARATOR_WSTR); + ssBuf.Set(ssNameSpace, s); + } + } + + if (!ssName.IsEmpty()) + { + ssBuf.Append(ssName); + } +} + +bool ns::MakeAssemblyQualifiedName( // true ok, false truncation + __out_ecount(dwBuffer) WCHAR* pBuffer, // Buffer to recieve the results + int dwBuffer, // Number of characters total in buffer + const WCHAR *szTypeName, // Namespace for name. + int dwTypeName, // Number of characters (not including null) + const WCHAR *szAssemblyName, // Final part of name. + int dwAssemblyName) // Number of characters (not including null) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + if (dwBuffer < 2) + return false; + + int iCopyMax = 0; + _ASSERTE(pBuffer); + *pBuffer = NULL; + + if (szTypeName && *szTypeName != W('\0')) + { + _ASSERTE(dwTypeName > 0); + iCopyMax = min(dwBuffer-1, dwTypeName); + wcsncpy_s(pBuffer, dwBuffer, szTypeName, iCopyMax); + dwBuffer -= iCopyMax; + } + + if (szAssemblyName && *szAssemblyName != W('\0')) + { + + if(dwBuffer < ASSEMBLY_SEPARATOR_LEN) + return false; + + for(DWORD i = 0; i < ASSEMBLY_SEPARATOR_LEN; i++) + pBuffer[iCopyMax+i] = ASSEMBLY_SEPARATOR_WSTR[i]; + + dwBuffer -= ASSEMBLY_SEPARATOR_LEN; + if(dwBuffer == 0) + return false; + + int iCur = iCopyMax + ASSEMBLY_SEPARATOR_LEN; + _ASSERTE(dwAssemblyName > 0); + iCopyMax = min(dwBuffer-1, dwAssemblyName); + wcsncpy_s(pBuffer + iCur, dwBuffer, szAssemblyName, iCopyMax); + pBuffer[iCur + iCopyMax] = W('\0'); + + if (iCopyMax < dwAssemblyName) + return false; + } + else { + if(dwBuffer == 0) { + PREFIX_ASSUME(iCopyMax > 0); + pBuffer[iCopyMax-1] = W('\0'); + return false; + } + else + pBuffer[iCopyMax] = W('\0'); + } + + return true; +} // int ns::MakePath() + +bool ns::MakeAssemblyQualifiedName( // true ok, false out of memory + CQuickBytes &qb, // Where to put results. + const WCHAR *szTypeName, // Namespace for name. + const WCHAR *szAssemblyName) // Final part of name. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FAULT; + + int iTypeName = 0; + int iAssemblyName = 0; + if (szTypeName) + iTypeName = (int)wcslen(szTypeName); + if (szAssemblyName) + iAssemblyName = (int)wcslen(szAssemblyName); + + int iLen = ASSEMBLY_SEPARATOR_LEN + iTypeName + iAssemblyName + 1; // Space for null terminator + WCHAR *szOut = (WCHAR *) qb.AllocNoThrow(iLen * sizeof(WCHAR)); + if (!szOut) + return false; + + bool ret; + ret = ns::MakeAssemblyQualifiedName(szOut, iLen, szTypeName, iTypeName, szAssemblyName, iAssemblyName); + _ASSERTE(ret); + return true; +} + +int ns::MakeNestedTypeName( // true ok, false out of memory + CQuickBytes &qb, // Where to put results. + LPCUTF8 szEnclosingName, // Full name for enclosing type + LPCUTF8 szNestedName) // Full name for nested type +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FAULT; + + _ASSERTE(szEnclosingName && szNestedName); + int iLen = 2; + iLen += (int)strlen(szEnclosingName); + iLen += (int)strlen(szNestedName); + LPUTF8 szOut = (LPUTF8) qb.AllocNoThrow(iLen); + if (!szOut) + return false; + return ns::MakeNestedTypeName(szOut, iLen, szEnclosingName, szNestedName); +} // int ns::MakeNestedTypeName() + +int ns::MakeNestedTypeName( // true ok, false truncation. + __out_ecount (cchChars) LPUTF8 szOut, // output path for name. + int cchChars, // max chars for output path. + LPCUTF8 szEnclosingName, // Full name for enclosing type + LPCUTF8 szNestedName) // Full name for nested type +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + if (cchChars < 1) + return false; + + int iCopyMax = 0, iLen; + int brtn = true; + *szOut = 0; + + iLen = (int)strlen(szEnclosingName); + iCopyMax = min(cchChars-1, iLen); + strncpy_s(szOut, cchChars, szEnclosingName, iCopyMax); + + if (iLen >= cchChars) + brtn = false; + + szOut[iCopyMax] = NESTED_SEPARATOR_CHAR; + int iCur = iCopyMax+1; // iCopyMax characters + nested_separator_char + cchChars -= iCur; + if(cchChars == 0) + return false; + + iLen = (int)strlen(szNestedName); + iCopyMax = min(cchChars-1, iLen); + strncpy_s(&szOut[iCur], cchChars, szNestedName, iCopyMax); + szOut[iCur + iCopyMax] = 0; + + if (iLen >= cchChars) + brtn = false; + + return brtn; +} // int ns::MakeNestedTypeName() + +void ns::MakeNestedTypeName( // throws on out of memory + SString &ssBuf, // output path for name. + const SString &ssEnclosingName, // Full name for enclosing type + const SString &ssNestedName) // Full name for nested type +{ + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_NOTRIGGER; + + ssBuf.Clear(); + + ssBuf.Append(ssEnclosingName); + ssBuf.Append(NESTED_SEPARATOR_WCHAR); + ssBuf.Append(ssNestedName); +} + diff --git a/src/utilcode/newapis.cpp b/src/utilcode/newapis.cpp new file mode 100644 index 0000000000..8d2af7bbc5 --- /dev/null +++ b/src/utilcode/newapis.cpp @@ -0,0 +1,1429 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//////////////////////////////////////////////////////////////////////////// +// +// File: newapis.cpp +// + + +// Purpose: functions that need to be emulated on downlevel platforms. +// +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "newapis.h" +#ifdef ENABLE_DOWNLEVEL_FOR_NLS +#include "downlevel.h" +#endif + +#include "utilcode.h" +#include "sortversioning.h" + +namespace NewApis +{ + + +#if defined(ENABLE_DOWNLEVEL_FOR_NLS) + + FARPROC GetProcAddressForLocaleApi(__in LPCSTR lpProcName, __in_opt FARPROC pFnDownlevelFallback) + { + _ASSERTE(lpProcName != NULL); + + FARPROC result = NULL; + +#if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE) + + // First try to use the function defined in the culture dll + // if we are running on a platform prior to Win7 + if(!IsWindows7Platform()) + { + // only need to load the culture dll's handle once then we can hold onto it + static HMODULE hCulture = NULL; + // if we haven't loaded the culture dll yet + if (hCulture == NULL) + { + UtilCode::LoadLibraryShim(MAKEDLLNAME_W(W("culture")), NULL, 0, &hCulture); + } + + // make sure we were successful before using the handle + if (hCulture != NULL) + { + result=GetProcAddress(hCulture,lpProcName); + } + } +#endif // !FEATURE_CORECLR && !CROSSGEN_COMPILE + + // next try the kernel + if(result==NULL) + { + HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W); + if(hMod!=NULL) + { + result=GetProcAddress(hMod,lpProcName); + } + } + + // failing all that, use the fallback provided + if(result==NULL) + { + result = pFnDownlevelFallback; + } + + return result; + } + +#endif // ENABLE_DOWNLEVEL_FOR_NLS + + __success(return > 0) int + GetSystemDefaultLocaleName(__out_ecount(cchLocaleName) LPWSTR lpLocaleName, __in int cchLocaleName) + { +#if !defined(ENABLE_DOWNLEVEL_FOR_NLS) + return ::GetSystemDefaultLocaleName(lpLocaleName, cchLocaleName); +#else + typedef int (WINAPI *PFNGetSystemDefaultLocaleName)(LPWSTR, int); + static PFNGetSystemDefaultLocaleName pFNGetSystemDefaultLocaleName=NULL; + if (pFNGetSystemDefaultLocaleName == NULL) + { + HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W); + // TODO: NLS Arrowhead - We should always fallback to the Downlevel APIs if the kernel APIs aren't found, + // regardless of error reason + if(hMod==NULL) + return 0; + pFNGetSystemDefaultLocaleName=(PFNGetSystemDefaultLocaleName)GetProcAddress(hMod,"GetSystemDefaultLocaleName"); + if(pFNGetSystemDefaultLocaleName==NULL) + { + if(GetLastError() == ERROR_PROC_NOT_FOUND) + pFNGetSystemDefaultLocaleName=DownLevel::GetSystemDefaultLocaleName; + else + return 0; + } + } + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable: 26036) // Prefast - Possible postcondition violation due to failure to null terminate string +#endif // _PREFAST_ + return pFNGetSystemDefaultLocaleName(lpLocaleName,cchLocaleName); +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +#endif + }; + + __success(return == TRUE) BOOL + GetUserPreferredUILanguages (__in DWORD dwFlags, __out PULONG pulNumLanguages, __out_ecount_opt(*pcchLanguagesBuffer) PWSTR pwszLanguagesBuffer, __in PULONG pcchLanguagesBuffer) + { +#ifdef ENABLE_DOWNLEVEL_FOR_NLS + typedef DWORD (WINAPI *PFNGetUserPreferredUILanguages)(ULONG, PULONG, LPWSTR, PULONG); + static PFNGetUserPreferredUILanguages pFNGetUserPreferredUILanguages=NULL; + if (pFNGetUserPreferredUILanguages == NULL) + { + HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W); + if(hMod==NULL) + return FALSE; + pFNGetUserPreferredUILanguages=(PFNGetUserPreferredUILanguages)GetProcAddress(hMod,"GetUserPreferredUILanguages"); + if(pFNGetUserPreferredUILanguages==NULL) + { + if(GetLastError() == ERROR_PROC_NOT_FOUND) + pFNGetUserPreferredUILanguages=DownLevel::GetUserPreferredUILanguages; + else + return FALSE; + } + } + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable: 26036) // Prefast - Possible postcondition violation due to failure to null terminate string +#endif // _PREFAST_ + BOOL res = pFNGetUserPreferredUILanguages(dwFlags, pulNumLanguages, pwszLanguagesBuffer, pcchLanguagesBuffer); + if(res == TRUE) + return res; + + //fallback to thread preferred langs + return GetThreadPreferredUILanguages(dwFlags, pulNumLanguages, pwszLanguagesBuffer, pcchLanguagesBuffer); +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +#else + return ::GetUserPreferredUILanguages(dwFlags, pulNumLanguages, pwszLanguagesBuffer, pcchLanguagesBuffer); +#endif + }; + + + + __success(return != 0) int + GetUserDefaultLocaleName(__out_ecount(cchLocaleName) LPWSTR lpLocaleName, __in int cchLocaleName) + { +#if !defined(ENABLE_DOWNLEVEL_FOR_NLS) + return ::GetUserDefaultLocaleName(lpLocaleName, cchLocaleName); +#else + typedef int (WINAPI *PFNGetUserDefaultLocaleName)(LPWSTR, int); + static PFNGetUserDefaultLocaleName pFNGetUserDefaultLocaleName=NULL; + if (pFNGetUserDefaultLocaleName == NULL) + { + HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W); + if(hMod==NULL) + return 0; + pFNGetUserDefaultLocaleName=(PFNGetUserDefaultLocaleName)GetProcAddress(hMod,"GetUserDefaultLocaleName"); + if(pFNGetUserDefaultLocaleName==NULL) + { + if(GetLastError() == ERROR_PROC_NOT_FOUND) + pFNGetUserDefaultLocaleName=DownLevel::GetUserDefaultLocaleName; + else + return 0; + } + } + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable: 26036) // Prefast - Possible postcondition violation due to failure to null terminate string +#endif // _PREFAST_ + return pFNGetUserDefaultLocaleName(lpLocaleName,cchLocaleName); +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +#endif + } + + // Call GetLocaleInfoEx and see if the OS knows about it. + // Note that GetLocaleInfoEx has variations: + // * Pre-Vista it fails and has to go downlevel + // * Vista succeeds, but not for neutrals + // * Win7 succeeds for all locales. + // * Mac does ??? + // + // The caller is expected to call with a specific locale (non-neutral) on Windows < windows 7, + // except for LOCALE_INEUTRAL and LOCALE_SPARENT, which downlevel.cpp handles + // + __success(return != 0) int + GetLocaleInfoEx (__in LPCWSTR lpLocaleName, __in LCTYPE LCType, __out_ecount_opt(cchData) LPWSTR lpLCData, __in int cchData) + { + _ASSERTE((lpLCData == NULL && cchData == 0) || (lpLCData != NULL && cchData > 0)); + // ComNlsInfo::nativeInitCultureData calls GetLocaleInfoEx with LcType LOCALE_SNAME + // to determine if this is a valid culture. We shouldn't assert in this case, but + // all others we should. + _ASSERTE(LCType == LOCALE_SNAME || NotLeakingFrameworkOnlyCultures(lpLocaleName)); + int retVal; + +#if !defined(ENABLE_DOWNLEVEL_FOR_NLS) + retVal = ::GetLocaleInfoEx(lpLocaleName, LCType, lpLCData, cchData); +#else + typedef int (WINAPI *PFNGetLocaleInfoEx)(LPCWSTR, LCTYPE, LPWSTR, int); + static PFNGetLocaleInfoEx pFNGetLocaleInfoEx=NULL; + if (pFNGetLocaleInfoEx== NULL) + { + pFNGetLocaleInfoEx=(PFNGetLocaleInfoEx)GetProcAddressForLocaleApi( + "GetLocaleInfoEx", + (FARPROC)DownLevel::GetLocaleInfoEx); + } + retVal = pFNGetLocaleInfoEx(lpLocaleName,LCType,lpLCData,cchData); + + // Do fallback if we didn't find anything yet + if (retVal == 0) + retVal = DownLevel::UplevelFallback::GetLocaleInfoEx(lpLocaleName,0,LCType,lpLCData,cchData); +#endif + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable: 26036) // Prefast - Possible postcondition violation due to failure to null terminate string +#endif // _PREFAST_ + return retVal; +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + + } + + __success(return != 0) int + GetDateFormatEx(__in LPCWSTR lpLocaleName, __in DWORD dwFlags, __in_opt CONST SYSTEMTIME* lpDate, __in_opt LPCWSTR lpFormat, + __out_ecount(cchDate) LPWSTR lpDateStr, __in int cchDate, __in_opt LPCWSTR lpCalendar) + { + _ASSERTE(NotLeakingFrameworkOnlyCultures(lpLocaleName)); +#if !defined(ENABLE_DOWNLEVEL_FOR_NLS) + return ::GetDateFormatEx(lpLocaleName, dwFlags, lpDate, lpFormat, lpDateStr, cchDate, lpCalendar); +#else + typedef int (WINAPI *PFNGetDateFormatEx)(LPCWSTR, DWORD, CONST SYSTEMTIME*, LPCWSTR, LPWSTR,int, LPCWSTR); + static PFNGetDateFormatEx pFNGetDateFormatEx=NULL; + if (pFNGetDateFormatEx== NULL) + { + HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W); + if(hMod==NULL) + return 0; + pFNGetDateFormatEx=(PFNGetDateFormatEx)GetProcAddress(hMod,"GetDateFormatEx"); + if(pFNGetDateFormatEx==NULL) + { + if(GetLastError() == ERROR_PROC_NOT_FOUND) + pFNGetDateFormatEx=DownLevel::GetDateFormatEx; + else + return 0; + } + } + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable: 26036) // Prefast - Possible postcondition violation due to failure to null terminate string +#endif // _PREFAST_ + return pFNGetDateFormatEx(lpLocaleName,dwFlags,lpDate,lpFormat,lpDateStr,cchDate,lpCalendar); +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +#endif + } + +#if defined(ENABLE_DOWNLEVEL_FOR_NLS) + static BOOL AvoidVistaTurkishBug = FALSE; + + __success(return != NULL) + inline + FARPROC GetSystemProcAddressForSortingApi( + __in LPCSTR lpProcName, + __in_opt FARPROC pFnDownlevelFallback) + { + _ASSERTE(lpProcName != NULL); + + FARPROC result = NULL; + + // try the kernel + HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W); + if(hMod!=NULL) + { + result=GetProcAddress(hMod,lpProcName); + } + + if (IsVistaPlatform()) AvoidVistaTurkishBug = TRUE; + + // failing all that, use the fallback provided + if(result==NULL) + { + result = pFnDownlevelFallback; + } + + return result; + } + + __success(return != NULL) + FARPROC GetProcAddressForSortingApi( + __in LPCSTR lpProcName, + __in FARPROC lpSortDllProcedure, + __in_opt FARPROC pFnDownlevelFallback, + __in_opt CONST NLSVERSIONINFO * lpVersionInformation) + { + _ASSERTE(lpProcName != NULL); + _ASSERTE(lpSortDllProcedure != NULL); + + FARPROC result = NULL; + + // Below windows 8 we have to try the sorting dll + if (!RunningOnWin8() && SortVersioning::IsAvailableVersion(lpVersionInformation)) + { + result=lpSortDllProcedure; + } + + if(result == NULL) + { + result = GetSystemProcAddressForSortingApi(lpProcName, pFnDownlevelFallback); + } + return result; + } +#endif // ENABLE_DOWNLEVEL_FOR_NLS + + +#if defined(ENABLE_DOWNLEVEL_FOR_NLS) + // + // Vista handle tr-TR and az-Latn-AZ incorrectly with the sorting APIs that takes locale names + // work around the problem by using the sorting name instead. + // + + LPWSTR GetLingusticLocaleName(__in LPWSTR pLocaleName, __in DWORD dwFlags) + { + _ASSERTE(IsVistaPlatform()); + + // If the localeName is NULL, then we are using an OS SortHandle and don't need to fix up + // anything. + if (pLocaleName == NULL) + { + return pLocaleName; + } + + if ((dwFlags & CASING_BITS)) + { + if (_wcsicmp(pLocaleName, TURKISH_LOCALE_NAME) == 0) + return TURKISH_SORTING_LOCALE_NAME; + + if (_wcsicmp(pLocaleName, AZERBAIJAN_LOCALE_NAME) == 0) + return AZERBAIJAN_SORTING_LOCALE_NAME; + } + return pLocaleName; + } +#endif + + // + // NOTE: We assume that we're only being called from the BCL with an explicit locale name, so we don't + // support the system/user default tokens used in the OS. + // + // Additionally this is only called for casing and sort keys, other functionality isn't supported. + // + int CompareStringEx(__in LPCWSTR lpLocaleName, __in DWORD dwCmpFlags, __in_ecount(cchCount1) LPCWSTR lpString1, __in int cchCount1, __in_ecount(cchCount2) LPCWSTR lpString2, + __in int cchCount2, __in_opt LPNLSVERSIONINFO lpVersionInformation, __in_opt LPVOID lpReserved, __in_opt LPARAM lParam ) + { + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + SO_TOLERANT; + PRECONDITION((lParam == 0 && CheckPointer(lpLocaleName)) || (lParam != 0 && lpLocaleName == NULL)); + PRECONDITION(CheckPointer(lpString1)); + PRECONDITION(CheckPointer(lpString2)); + } CONTRACTL_END; + + _ASSERTE(lpLocaleName == NULL || NotLeakingFrameworkOnlyCultures(lpLocaleName)); +#if !defined(ENABLE_DOWNLEVEL_FOR_NLS) + return::CompareStringEx(lpLocaleName, dwCmpFlags, lpString1, cchCount1, lpString2, + cchCount2, lpVersionInformation, lpReserved, lParam ); +#else + typedef int (WINAPI *PFNCompareStringEx)(LPCWSTR, DWORD, LPCWSTR, int, LPCWSTR, int, LPNLSVERSIONINFO, LPVOID, LPARAM ); + static PFNCompareStringEx pFNCompareStringEx=NULL; + + // See if we loaded our pointer already + if (pFNCompareStringEx == NULL) + { + pFNCompareStringEx = (PFNCompareStringEx) GetProcAddressForSortingApi( + "CompareStringEx", + (FARPROC)SortVersioning::SortCompareString, + (FARPROC)DownLevel::CompareStringEx, + lpVersionInformation); + } + + // TODO: Remove this workaround after Vista SP2 &/or turkic CompareStringEx() gets fixed on Vista. + // If its Vista and we want a turkik sort, then call CompareStringW not CompareStringEx + LPCWSTR pLingLocaleName = AvoidVistaTurkishBug ? GetLingusticLocaleName((LPWSTR)lpLocaleName, dwCmpFlags) : lpLocaleName; + // TODO: End of workaround for turkish CompareStringEx() on Vista/Win2K8 + + return pFNCompareStringEx(pLingLocaleName, dwCmpFlags, lpString1, cchCount1, lpString2, + cchCount2, lpVersionInformation, lpReserved, lParam ); +#endif + } + + // Note that unlike the real version we always expect our callers to pass counted strings + // I don't think we can assert because I think it'd call this code again + int CompareStringOrdinal(__in_ecount(cchCount1) LPCWSTR lpString1, __in int cchCount1, __in_ecount(cchCount2) LPCWSTR lpString2, __in int cchCount2, __in BOOL bIgnoreCase) + { +#if !defined(ENABLE_DOWNLEVEL_FOR_NLS) + return ::CompareStringOrdinal(lpString1, cchCount1, lpString2, cchCount2, bIgnoreCase); +#else + typedef int (WINAPI *PFNCompareStringOrdinal )(LPCWSTR, int, LPCWSTR, int, BOOL ); + static PFNCompareStringOrdinal pFNCompareStringOrdinal=NULL; + if (pFNCompareStringOrdinal == NULL) + { + HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W); + if(hMod != NULL) + { + // Grab the OS proc if its there + pFNCompareStringOrdinal=(PFNCompareStringOrdinal)GetProcAddress(hMod, "CompareStringOrdinal"); + } + if (pFNCompareStringOrdinal == NULL) + { + // Regardless of the reason why, if we can't find the API just use the downlevel version + pFNCompareStringOrdinal=DownLevel::CompareStringOrdinal; + } + } + return pFNCompareStringOrdinal(lpString1, cchCount1, lpString2, cchCount2, bIgnoreCase); +#endif + } + +// The invariant locale should always match the system for +// upper and lower casing +inline BOOL IsInvariantCasing(__in LPCWSTR lpLocaleName, __in DWORD dwMapFlags) +{ + _ASSERTE(lpLocaleName); + + if(lpLocaleName[0] == NULL) + { + if(dwMapFlags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE)) + { + return TRUE; + } + } + return FALSE; +} + + // + // NOTE: We assume that we're only being called from the BCL with an explicit locale name, so we don't + // support the system/user default tokens used in the OS. + // + // Additionally this is only called for casing and sort keys, other functionality isn't supported. + // + __success(return != 0) + int LCMapStringEx (__in LPCWSTR lpLocaleName, __in DWORD dwMapFlags, __in_ecount(cchSrc) LPCWSTR lpSrcStr, __in int cchSrc, + __out_xcount_opt(cchDest) LPWSTR lpDestStr, __in int cchDest, __in_opt LPNLSVERSIONINFO lpVersionInformation, __in_opt LPVOID lpReserved, __in_opt LPARAM lParam ) + { + int retVal = 0; + // Note: We should only be calling this for casing or sort keys + _ASSERTE((dwMapFlags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE | LCMAP_TITLECASE | LCMAP_SORTKEY | (RunningOnWin8() ? (LCMAP_SORTHANDLE | LCMAP_HASH) : 0))) != 0); + + // Need to have a name or sort node + _ASSERTE(lpLocaleName == NULL && lParam != 0 || lParam == 0 && lpLocaleName != NULL); + + _ASSERTE(lpLocaleName == NULL || NotLeakingFrameworkOnlyCultures(lpLocaleName)); + + // Can't use the system token, which starts with an illegal ! + _ASSERTE(lpLocaleName == NULL || lpLocaleName[0] != W('!')); + +#if !defined(ENABLE_DOWNLEVEL_FOR_NLS) + retVal = ::LCMapStringEx (lpLocaleName, dwMapFlags, lpSrcStr, cchSrc, + lpDestStr, cchDest, lpVersionInformation, lpReserved, lParam ); +#else + typedef int (WINAPI *PFNLCMapStringEx )(LPCWSTR, DWORD, LPCWSTR, int, LPWSTR, int, LPNLSVERSIONINFO, LPVOID, LPARAM); + + PFNLCMapStringEx pLcMapStringEx; + LPCWSTR pLingLocaleName; + if(lpLocaleName == NULL || IsInvariantCasing(lpLocaleName, dwMapFlags)){ + static PFNLCMapStringEx pFNSystemLCMapStringEx=NULL; + if (pFNSystemLCMapStringEx == NULL) + { + pFNSystemLCMapStringEx = (PFNLCMapStringEx) GetSystemProcAddressForSortingApi( + "LCMapStringEx", + (FARPROC)DownLevel::LCMapStringEx); + } + pLcMapStringEx = pFNSystemLCMapStringEx; + pLingLocaleName = lpLocaleName; + } + else + { + static PFNLCMapStringEx pFNLCMapStringEx=NULL; + // See if we still need to find which function to use + if (pFNLCMapStringEx == NULL) + { + pFNLCMapStringEx = (PFNLCMapStringEx) GetProcAddressForSortingApi( + "LCMapStringEx", + (FARPROC)SortVersioning::LCMapStringEx, + (FARPROC)DownLevel::LCMapStringEx, + lpVersionInformation); + } + pLcMapStringEx = pFNLCMapStringEx; + + // TODO: Remove this workaround after Vista SP2 &/or turkic CompareStringEx() gets fixed on Vista. + // If its Vista and we want a turkik sort, then call CompareStringW not CompareStringEx + pLingLocaleName = AvoidVistaTurkishBug ? GetLingusticLocaleName((LPWSTR)lpLocaleName, dwMapFlags) : lpLocaleName; + // TODO: End of workaround for turkish CompareStringEx() on Vista/Win2K8 + } + + retVal = pLcMapStringEx(pLingLocaleName, dwMapFlags, lpSrcStr, cchSrc, + lpDestStr, cchDest, lpVersionInformation, lpReserved, lParam); + if (retVal == 0) + retVal = DownLevel::UplevelFallback::LCMapStringEx(lpLocaleName, dwMapFlags, lpSrcStr, cchSrc, + lpDestStr, cchDest, lpVersionInformation, lpReserved, lParam); +#endif + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable: 26036) // Prefast - Possible postcondition violation due to failure to null terminate string +#endif // _PREFAST_ + return retVal; +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + } + + __inline BOOL IsLowerAsciiString(__in_ecount(cchCount) LPCWSTR lpString, __in int cchCount) + { + int count = cchCount; + LPCWSTR pStr = lpString; + __range(0,10) int cch; + int value = 0; + + while (count > 0) + { + cch = min(count, 10); + switch (cch) + { + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable: 26010) // Prefast - Potential read overflow of null terminated buffer using expression 'pStr[9]' +#endif // _PREFAST_ + case 10: value |= (int) pStr[9]; __fallthrough; // fall through +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + case 9: value |= (int) pStr[8]; __fallthrough; // fall through + case 8: value |= (int) pStr[7]; __fallthrough; // fall through + case 7: value |= (int) pStr[6]; __fallthrough; // fall through + case 6: value |= (int) pStr[5]; __fallthrough; // fall through + case 5: value |= (int) pStr[4]; __fallthrough; // fall through + case 4: value |= (int) pStr[3]; __fallthrough; // fall through + case 3: value |= (int) pStr[2]; __fallthrough; // fall through + case 2: value |= (int) pStr[1]; __fallthrough; // fall through + case 1: value |= (int) pStr[0]; __fallthrough; // fall through + } + + if (value >= 0x80) + { + return FALSE; + } + + count -= cch; + pStr += cch; + } + + return TRUE; + } + + INT32 FastIndexOfString(__in_ecount(sourceLength) const WCHAR *source, __in INT32 sourceLength, __in_ecount(patternLength) const WCHAR *pattern, __in INT32 patternLength) + { + if (source == NULL || pattern == NULL || patternLength < 0 || sourceLength < 1) + { + return -1; + } + + INT32 startIndex = 0; + INT32 endIndex = sourceLength - 1; + + int endPattern = endIndex - patternLength + 1; + + if (endPattern<0) { + return -1; + } + + if (patternLength == 0) { + return startIndex; + } + + WCHAR patternChar0 = pattern[0]; + for (int ctrSrc = startIndex; ctrSrc<=endPattern; ctrSrc++) { + if (source[ctrSrc] != patternChar0) + continue; + int ctrPat; + for (ctrPat = 1; ctrPat < patternLength; ctrPat++) { + if (source[ctrSrc + ctrPat] != pattern[ctrPat]) break; + } + if (ctrPat == patternLength) { + return (ctrSrc); + } + } + + return (-1); + } + + INT32 FastIndexOfStringInsensitive(__in_ecount(sourceLength) const WCHAR *source, __in INT32 sourceLength, __in_ecount(patternLength) const WCHAR *pattern, __in INT32 patternLength) + { + if (source == NULL || pattern == NULL || patternLength < 0 || sourceLength < 1) + { + return -1; + } + + INT32 startIndex = 0; + INT32 endIndex = sourceLength - 1; + + WCHAR srcChar; + WCHAR patChar; + + int endPattern = endIndex - patternLength + 1; + + if (endPattern<0) { + return -1; + } + + if (patternLength == 0) { + return startIndex; + } + + WCHAR pattern0 = pattern[0]; + if (pattern0>='A' && pattern0<='Z') { + pattern0|=0x20; + } + + for (int ctrSrc = startIndex; ctrSrc<=endPattern; ctrSrc++) { + srcChar = source[ctrSrc]; + if (srcChar>='A' && srcChar<='Z') { + srcChar|=0x20; + } + if (srcChar != pattern0) + continue; + + int ctrPat; + for (ctrPat = 1; (ctrPat < patternLength); ctrPat++) { + srcChar = source[ctrSrc + ctrPat]; + if (srcChar>='A' && srcChar<='Z') { + srcChar|=0x20; + } + patChar = pattern[ctrPat]; + if (patChar>='A' && patChar<='Z') { + patChar|=0x20; + } + if (srcChar!=patChar) { + break; + } + } + + if (ctrPat == patternLength) { + return (ctrSrc); + } + } + + return (-1); + } + + // Works backwards, starting at startIndex and ending at endIndex + INT32 FastLastIndexOfString(__in_ecount(sourceLength) const WCHAR *source, __in INT32 sourceLength, __in_ecount(patternLength) const WCHAR *pattern, __in INT32 patternLength) + { + if (source == NULL || pattern == NULL || patternLength < 0 || sourceLength < 1) + { + return -1; + } + + INT32 startIndex = sourceLength - 1; + INT32 endIndex = 0; + + //startIndex is the greatest index into the string. + int startPattern = startIndex - patternLength + 1; + + if (startPattern < 0) { + return (-1); + } + + if (patternLength == 0) { + return startIndex; + } + + WCHAR patternChar0 = pattern[0]; + for (int ctrSrc = startPattern; ctrSrc >= endIndex; ctrSrc--) { + if (source[ctrSrc] != patternChar0) + continue; + int ctrPat; + for (ctrPat = 1; ctrPat<patternLength; ctrPat++) { + if (source[ctrSrc+ctrPat] != pattern[ctrPat]) break; + } + if (ctrPat == patternLength) { + return (ctrSrc); + } + } + + return (-1); + } + + // Works backwards, starting at startIndex and ending at endIndex + INT32 FastLastIndexOfStringInsensitive(__in_ecount(sourceLength) const WCHAR *source, __in INT32 sourceLength, __in_ecount(patternLength) const WCHAR *pattern, __in INT32 patternLength) + { + if (source == NULL || pattern == NULL || patternLength < 0 || sourceLength < 1) + { + return -1; + } + + INT32 startIndex = sourceLength - 1; + INT32 endIndex = 0; + + //startIndex is the greatest index into the string. + int startPattern = startIndex - patternLength + 1; + + if (startPattern < 0) { + return (-1); + } + + if (patternLength == 0) { + return startIndex; + } + + WCHAR srcChar; + WCHAR patChar; + WCHAR pattern0 = pattern[0]; + if (pattern0>='A' && pattern0<='Z') { + pattern0|=0x20; + } + + + for (int ctrSrc = startPattern; ctrSrc >= endIndex; ctrSrc--) { + srcChar = source[ctrSrc]; + if (srcChar>='A' && srcChar<='Z') { + srcChar|=0x20; + } + if (srcChar != pattern0) + continue; + + int ctrPat; + for (ctrPat = 1; ctrPat<patternLength; ctrPat++) { + srcChar = source[ctrSrc+ctrPat]; + if (srcChar>='A' && srcChar<='Z') { + srcChar|=0x20; + } + patChar = pattern[ctrPat]; + if (patChar>='A' && patChar<='Z') { + patChar|=0x20; + } + if (srcChar!=patChar) { + break; + } + } + if (ctrPat == patternLength) { + return (ctrSrc); + } + } + + return (-1); + } + + //////////////////////////////////////////////////////////////////////////// + // + // IndexOfString + // + //////////////////////////////////////////////////////////////////////////// + int IndexOfString( __in LPCWSTR lpLocaleName, + __in_ecount(cchCount1) LPCWSTR pString1, // String to search in + __in int cchCount1, // length of pString1 + __in_ecount(cchCount2) LPCWSTR pString2, // String we're looking for + __in int cchCount2, // length of pString2 + __in DWORD dwFlags, // search flags + __in BOOL startWith) // true if we need to check for prefix case + { + int iRetVal = -1; + + // + // Check the ranges. + // + if (cchCount1 == 0) + { + if (cchCount2 == 0) + iRetVal = 0; + // else iRetVal = -1 (not found) + goto lExit; + } + + // + // See if we have an empty string 2. + // + if (cchCount2 == 0) + { + iRetVal = 0; + goto lExit; + } + + // + // Search for the character in the string. + // + + if (dwFlags == COMPARE_OPTIONS_ORDINAL) + { + iRetVal = FastIndexOfString(pString1, cchCount1, pString2, cchCount2); + goto lExit; + } + //For dwFlags, 0 is the default, 1 is ignore case, we can handle both. + // TODO: NLS Arrowhead -This isn't really right, custom locales could start with en- and have different sort behavior + + if (((dwFlags & ~CASING_BITS) == 0) && IS_FAST_COMPARE_LOCALE(lpLocaleName)) + { + if (IsLowerAsciiString(pString1, cchCount1) && IsLowerAsciiString(pString2, cchCount2)) + { + if ((dwFlags & (LINGUISTIC_IGNORECASE | NORM_IGNORECASE)) == 0) + iRetVal = FastIndexOfString(pString1, cchCount1, pString2, cchCount2); + else + iRetVal = FastIndexOfStringInsensitive(pString1, cchCount1, pString2, cchCount2); + goto lExit; + } + } + + _ASSERTE(iRetVal==-1); + int result; + + // Some things to think about, depending on the options passed in: + // + // LINGUISTIC_IGNORECASE - Can't cause length changes since casing is always changing to the same length + // NORM_IGNORECASE - Can't cause length changes since casing is always changing to the same length + // NORM_LINGUISTIC_CASING - Can't cause length changes since casing is always changing to the same length + // NORM_IGNOREKANATYPE - A 1:1 mapping, so the lengths don't change + // NORM_IGNOREWIDTH - A 1:1 mapping (full & half width), so lengths don't change + // SORT_STRINGSORT - No impact on search size - special treatment for - and ' + // LINGUISTIC_IGNOREDIACRITIC - Terrible because both strings could be all diacritics, except for the last character. + // NORM_IGNORENONSPACE -Terrible because both strings could all be non-spacing characters, except for 1 somewhere. + // NORM_IGNORESYMBOLS - Terrible because both strings could be all symbols + // Compressions/Expansions - Either string may have compressions or expansions impacting the size needing searched + // for default table cultures (including invariant) there're only expansions, and those only expand 2X worst case. + + for (int iOffset=0; iRetVal == -1 && iOffset<cchCount1; iOffset++) + { + // Because of compressions/expansions/ignorable characters we can't just use the known length, but need to consider the entire remainder of the string. + // TODO: NLS: the nested loop is extremely slow. oledbtest.exe can't finish even overnight. (Doing invariant ignore case) + for (int iLength=1; iLength<=cchCount1 - iOffset; iLength++) + { + result = NewApis::CompareStringEx(lpLocaleName, dwFlags, pString2, cchCount2, &pString1[iOffset], iLength, NULL, NULL, 0); + if (result == CSTR_EQUAL) + { + iRetVal = iOffset; + break; + } + else if (result == 0) + { + // return value of 0 indicates failure and error value is supposed to be set. + // shouldn't ever really happen + _ASSERTE(!"catastrophic failure calling NewApis::CompareStringEx! This could be a CultureInfo, RegionInfo, or Calendar bug (bad localeName string) or maybe a GCHole."); + } + } + } + + lExit: + if (startWith && iRetVal != 0) + { + iRetVal = -1; + } + + return iRetVal; + } + //////////////////////////////////////////////////////////////////////////// + // + // LastIndexOfString + // + //////////////////////////////////////////////////////////////////////////// + int LastIndexOfString( __in LPCWSTR lpLocaleName, + __in_ecount(cchCount1) LPCWSTR pString1, // String to search in + __in int cchCount1, // length of pString1 + __in_ecount(cchCount2) LPCWSTR pString2, // String we're looking for + __in int cchCount2, // length of pString2 + __in DWORD dwFlags, + __in BOOL endWith) // check suffix case + { + INT32 iRetVal = -1; + BOOL comparedOrdinal = FALSE; + + // Check for empty strings + if (cchCount1 == 0) + { + if (cchCount2 == 0) + iRetVal = 0; + // else iRetVal = -1 (not found) + goto lExit; + } + + // + // See if we have an empty string 2. + // + if (cchCount2 == 0) + { + iRetVal = 0; + goto lExit; + } + + // + // Search for the character in the string. + //<TODO> + // @ToDo: Should read the nls data tables directly to make this + // much faster and to handle composite characters. + //</TODO> + + if (dwFlags == COMPARE_OPTIONS_ORDINAL) + { + iRetVal = FastLastIndexOfString(pString1, cchCount1, pString2, cchCount2); + comparedOrdinal = TRUE; + goto lExit; + } + + //For dwFlags, 0 is the default, 1 is ignore case, we can handle both. + // TODO: NLS Arrowhead -This isn't really right, custom locales could start with en- and have different sort behavior + if (((dwFlags & ~CASING_BITS) == 0) && IS_FAST_COMPARE_LOCALE(lpLocaleName)) + { + if (IsLowerAsciiString(pString1, cchCount1) && IsLowerAsciiString(pString2, cchCount2)) + { + if ((dwFlags & (LINGUISTIC_IGNORECASE | NORM_IGNORECASE)) == 0) + iRetVal = FastLastIndexOfString(pString1, cchCount1, pString2, cchCount2); + else + iRetVal = FastLastIndexOfStringInsensitive(pString1, cchCount1, pString2, cchCount2); + comparedOrdinal = TRUE; + goto lExit; + } + } + + _ASSERTE(iRetVal==-1); + // TODO: Cleanup like IndexOfString + for (int iOffset=0; iRetVal == -1 && iOffset>-cchCount1; iOffset--) + { + for (int iLength=1; iLength<=cchCount1 + iOffset; iLength++) + { + if (NewApis::CompareStringEx(lpLocaleName, dwFlags, pString2, cchCount2, &pString1[cchCount1 + iOffset - iLength], iLength, NULL, NULL, 0) == CSTR_EQUAL) + { + iRetVal= cchCount1 + iOffset - iLength; + break; + } + } + } + lExit: + + if (endWith && iRetVal>=0) + { + if (comparedOrdinal && (cchCount1 - iRetVal != cchCount2)) // optimize here to avoid calling CompareString + { + iRetVal = -1; + } + else if (NewApis::CompareStringEx(lpLocaleName, dwFlags, pString2, cchCount2, &pString1[iRetVal], cchCount1 - iRetVal, NULL, NULL, 0) != CSTR_EQUAL) + { + iRetVal = -1; + } + } + + return iRetVal; + } + + // + // NOTE: We assume that we're only being called from the BCL with an explicit locale name, so we don't + // support the system/user default tokens used in the OS. + // + // Additionally this is only called for casing and sort keys, other functionality isn't supported. + // + int FindNLSStringEx(__in LPCWSTR lpLocaleName, + __in DWORD dwFindNLSStringFlags, + __in_ecount(cchSource) LPCWSTR lpStringSource, + __in int cchSource, + __in_ecount(cchValue) LPCWSTR lpStringValue, + __in int cchValue, + __out_opt LPINT pcchFound, + __in_opt LPNLSVERSIONINFO lpVersionInformation, + __in_opt LPVOID lpReserved, + __in_opt LPARAM lParam) + { + _ASSERTE(lpLocaleName == NULL || NotLeakingFrameworkOnlyCultures(lpLocaleName)); +#if !defined(ENABLE_DOWNLEVEL_FOR_NLS) + return ::FindNLSStringEx(lpLocaleName, dwFindNLSStringFlags, lpStringSource, cchSource, lpStringValue, cchValue, pcchFound, + lpVersionInformation, lpReserved, lParam ); +#else + typedef int (WINAPI *PFNFindNLSStringEx )(LPCWSTR, DWORD, LPCWSTR, int, LPCWSTR, int, LPINT, LPNLSVERSIONINFOEX, LPVOID, LPARAM ); + static PFNFindNLSStringEx pFNFindNLSStringEx=NULL; + + // See if we still need to figure out which function to call + if (pFNFindNLSStringEx == NULL) + { + pFNFindNLSStringEx = (PFNFindNLSStringEx) GetProcAddressForSortingApi( + "FindNLSStringEx", + (FARPROC)SortVersioning::SortFindString, + (FARPROC)DownLevel::FindNLSStringEx, + lpVersionInformation); + } + + // TODO: Remove this workaround after Vista SP2 &/or turkic CompareStringEx() gets fixed on Vista. + // If its Vista and we want a turkik sort, then call CompareStringW not CompareStringEx + LPCWSTR pLingLocaleName = AvoidVistaTurkishBug ? GetLingusticLocaleName((LPWSTR)lpLocaleName, dwFindNLSStringFlags) : lpLocaleName; + // TODO: End of workaround for turkish CompareStringEx() on Vista/Win2K8 + + int cchFound; // we need to get the length even if the caller doesn't care about it (see below) + int result = pFNFindNLSStringEx(pLingLocaleName, dwFindNLSStringFlags, lpStringSource, cchSource, lpStringValue, cchValue, &cchFound, + lpVersionInformation, lpReserved, lParam ); + // When searching from end with an empty pattern (either empty string or all ignored characters) + // a match is found (result != -1) + // Currently we get a result == 0 but we are hoping this will change + // with Win7 to be the length of the source string (thus pointing past-the-end) + // and the length of the match (cchFound) will be 0 + // For compatibility, we need to return the index of the last character (or 0 if the source is empty) + if((dwFindNLSStringFlags & FIND_FROMEND) && + result != -1 && + cchFound == 0 && + cchSource != 0) + { + result = cchSource - 1; + } + + // if the caller cares about the length, give it to them + if(pcchFound != NULL) + { + *pcchFound = cchFound; + } + + return result; +#endif + } + + __success(return != 0) int + GetCalendarInfoEx(__in LPCWSTR lpLocaleName, __in CALID Calendar, __in_opt LPCWSTR pReserved, __in CALTYPE CalType, __out_ecount_opt(cchData) LPWSTR lpCalData, __in int cchData, __out_opt LPDWORD lpValue ) + { + + _ASSERTE(NotLeakingFrameworkOnlyCultures(lpLocaleName)); + if ( (lpCalData != NULL && cchData == 0) || (lpCalData == NULL && cchData > 0) ) + { + _ASSERTE(FALSE); + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + if ((CalType & CAL_RETURN_NUMBER)) + { + // If CAL_RETURN_NUMBER, lpValue must be non-null and lpCalData must be null + if (lpValue == NULL || lpCalData != NULL) + { + _ASSERTE(FALSE); + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + } + + int retVal = 0; + +#if !defined(ENABLE_DOWNLEVEL_FOR_NLS) + retVal = ::GetCalendarInfoEx(lpLocaleName, Calendar, pReserved, CalType, lpCalData, cchData, lpValue ); +#else + typedef int (WINAPI *PFNGetCalendarInfoEx )(LPCWSTR, CALID, LPCWSTR, CALTYPE, LPWSTR, int, LPDWORD ); + static PFNGetCalendarInfoEx pFNGetCalendarInfoEx=NULL; + if (pFNGetCalendarInfoEx== NULL) + { + pFNGetCalendarInfoEx=(PFNGetCalendarInfoEx)GetProcAddressForLocaleApi( + "GetCalendarInfoEx", + (FARPROC)DownLevel::GetCalendarInfoEx); + } + retVal = pFNGetCalendarInfoEx(lpLocaleName, Calendar, pReserved, CalType, lpCalData, cchData, lpValue ); + + // Do fallback if we didn't find anything yet + if (retVal == 0) + retVal = DownLevel::UplevelFallback::GetCalendarInfoEx(lpLocaleName, Calendar, pReserved, CalType, lpCalData, cchData, lpValue ); +#endif + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable: 26036) // Prefast - Possible postcondition violation due to failure to null terminate string +#endif // _PREFAST_ + return retVal; +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + } + + __success(return != 0) + int LCIDToLocaleName(__in LCID Locale, __out_ecount_opt(cchName) LPWSTR lpName, __in int cchName, __in DWORD dwFlags) + { + int retVal = 0; + +#if !defined(ENABLE_DOWNLEVEL_FOR_NLS) + retVal = ::LCIDToLocaleName(Locale, lpName, cchName, dwFlags); +#else + +#ifdef FEATURE_CORECLR + retVal = DownLevel::LCIDToLocaleName(Locale, lpName,cchName,dwFlags); +#endif // FEATURE_CORECLR + + typedef int (WINAPI *PFNLCIDToLocaleName)(LCID, LPWSTR,int ,DWORD); + static PFNLCIDToLocaleName pFNLCIDToLocaleName=NULL; + if (retVal == 0) + { + if (pFNLCIDToLocaleName==NULL) + { + pFNLCIDToLocaleName=(PFNLCIDToLocaleName)GetProcAddressForLocaleApi( + "LCIDToLocaleName", + (FARPROC)DownLevel::LCIDToLocaleName); + + } + + // Try with the allow neutral flag (will fail in Vista, but + // Downlevel::LCIDToLocaleName knows all vista locales) + retVal = pFNLCIDToLocaleName(Locale, lpName, cchName, dwFlags | LOCALE_ALLOW_NEUTRAL_NAMES); + if(retVal == 0) + { + // in case we are using OS, it could have a problem with the above flag; retry without it + retVal = pFNLCIDToLocaleName(Locale, lpName, cchName, dwFlags); + } + } +#endif // ENABLE_DOWNLEVEL_FOR_NLS + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable: 26036) // Prefast - Possible postcondition violation due to failure to null terminate string +#endif // _PREFAST_ + return retVal; +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + } + + LCID LocaleNameToLCID(__in_opt LPCWSTR lpName , __in DWORD dwFlags) + { + LCID retVal = 0; + +#if !defined(ENABLE_DOWNLEVEL_FOR_NLS) + return ::LocaleNameToLCID(lpName, dwFlags); +#else +#ifdef FEATURE_CORECLR + retVal = DownLevel::LocaleNameToLCID(lpName, dwFlags); +#endif // FEATURE_CORECLR + + typedef int (WINAPI *PFNLocaleNameToLCID)(LPCWSTR,DWORD); + static PFNLocaleNameToLCID pFNLocaleNameToLCID=NULL; + + if (retVal == 0) + { + if (pFNLocaleNameToLCID==NULL) + { + pFNLocaleNameToLCID=(PFNLocaleNameToLCID)GetProcAddressForLocaleApi( + "LocaleNameToLCID", + (FARPROC)DownLevel::LocaleNameToLCID); + + } + + // Try with the allow neutral flag (will fail in Vista, but + // Downlevel::LocaleNametoLCID knows all vista locales) + retVal = pFNLocaleNameToLCID(lpName, dwFlags | LOCALE_ALLOW_NEUTRAL_NAMES); + if(retVal == 0) + { + // in case we are using OS, it could have a problem with the above flag; retry without it + retVal = pFNLocaleNameToLCID(lpName, dwFlags); + } + } +#endif // ENABLE_DOWNLEVEL_FOR_NLS + + return retVal; + } + + __success(return != 0) BOOL + EnumDateFormatsExEx (DATEFMT_ENUMPROCEXEX lpDateFmtEnumProcExEx, LPCWSTR lpLocaleName, DWORD dwFlags, LPARAM lParam) + { + _ASSERTE(NotLeakingFrameworkOnlyCultures(lpLocaleName)); + int retVal = 0; +#if !defined(ENABLE_DOWNLEVEL_FOR_NLS) + retVal = ::EnumDateFormatsExEx (lpDateFmtEnumProcExEx, lpLocaleName, dwFlags, lParam); +#else + typedef int (WINAPI *PFNEnumDateFormatsExEx)(DATEFMT_ENUMPROCEXEX, LPCWSTR, DWORD, LPARAM); + + static PFNEnumDateFormatsExEx pFNEnumDateFormatsExEx=NULL; + if (pFNEnumDateFormatsExEx==NULL) + { + pFNEnumDateFormatsExEx=(PFNEnumDateFormatsExEx)GetProcAddressForLocaleApi( + "EnumDateFormatsExEx", + (FARPROC)DownLevel::LegacyCallbacks::EnumDateFormatsExEx); + } + + retVal = pFNEnumDateFormatsExEx(lpDateFmtEnumProcExEx, lpLocaleName, dwFlags, lParam); + if (retVal == 0) + { + retVal = DownLevel::LegacyCallbacks::EnumDateFormatsExEx(lpDateFmtEnumProcExEx, lpLocaleName, dwFlags, lParam); + } +#endif + return retVal; + } + + __success(return != 0) + BOOL EnumTimeFormatsEx(TIMEFMT_ENUMPROCEX lpTimeFmtEnumProcEx, LPCWSTR lpLocaleName, DWORD dwFlags, LPARAM lParam) + { + _ASSERTE(NotLeakingFrameworkOnlyCultures(lpLocaleName)); + int retVal = 0; +#if !defined(ENABLE_DOWNLEVEL_FOR_NLS) + retVal = ::EnumTimeFormatsEx(lpTimeFmtEnumProcEx, lpLocaleName, dwFlags, lParam); +#else + typedef int (WINAPI *PFNEnumTimeFormatsEx)(TIMEFMT_ENUMPROCEX, LPCWSTR, DWORD, LPARAM); + + static PFNEnumTimeFormatsEx pFNEnumTimeFormatsEx=NULL; + if (pFNEnumTimeFormatsEx==NULL) + { + pFNEnumTimeFormatsEx=(PFNEnumTimeFormatsEx)GetProcAddressForLocaleApi( + "EnumTimeFormatsEx", + (FARPROC)DownLevel::LegacyCallbacks::EnumTimeFormatsEx); + } + + retVal = pFNEnumTimeFormatsEx(lpTimeFmtEnumProcEx, lpLocaleName, dwFlags, lParam); + if (retVal == 0) + { + retVal = DownLevel::LegacyCallbacks::EnumTimeFormatsEx(lpTimeFmtEnumProcEx, lpLocaleName, dwFlags, lParam); + } +#endif + return retVal; + } + + __success(return != 0) + BOOL EnumCalendarInfoExEx(CALINFO_ENUMPROCEXEX pCalInfoEnumProcExEx, LPCWSTR lpLocaleName, CALID Calendar, CALTYPE CalType, LPARAM lParam) + { + _ASSERTE(NotLeakingFrameworkOnlyCultures(lpLocaleName)); + int retVal = 0; +#if !defined(ENABLE_DOWNLEVEL_FOR_NLS) + retVal = ::EnumCalendarInfoExEx (pCalInfoEnumProcExEx, lpLocaleName, Calendar, NULL, CalType, lParam); +#else + typedef int (WINAPI *PFNEnumCalendarInfoExEx)(CALINFO_ENUMPROCEXEX, LPCWSTR, CALID, LPCWSTR, CALTYPE, LPARAM); + + static PFNEnumCalendarInfoExEx pFNEnumCalendarInfoExEx=NULL; + if (pFNEnumCalendarInfoExEx==NULL) + { + pFNEnumCalendarInfoExEx=(PFNEnumCalendarInfoExEx)GetProcAddressForLocaleApi( + "EnumCalendarInfoExEx", + (FARPROC)DownLevel::LegacyCallbacks::EnumCalendarInfoExEx); + } + + retVal = pFNEnumCalendarInfoExEx(pCalInfoEnumProcExEx, lpLocaleName, Calendar, NULL, CalType, lParam); + if (retVal == 0) + { + retVal = DownLevel::LegacyCallbacks::EnumCalendarInfoExEx(pCalInfoEnumProcExEx, lpLocaleName, Calendar, NULL, CalType, lParam); + } +#endif + + return retVal; + } + + // This function exists is in server 2003 and above + // Function should be COMPARE_STRING, dwFlags should be NULL, lpVersionInfo should be NULL for now + BOOL IsNLSDefinedString(__in NLS_FUNCTION Function, __in DWORD dwFlags, __in_opt LPNLSVERSIONINFOEX lpVersionInfo, __in LPCWSTR lpString, __in int cchStr ) + { +#if !defined(ENABLE_DOWNLEVEL_FOR_NLS) + return ::IsNLSDefinedString(Function, dwFlags, lpVersionInfo, lpString, cchStr); +#else + typedef int (WINAPI *PFNIsNLSDefinedString)(NLS_FUNCTION, DWORD, LPNLSVERSIONINFO, LPCWSTR, int); + + static PFNIsNLSDefinedString pFNIsNLSDefinedString=NULL; + + // See if we still need to find our function + if (pFNIsNLSDefinedString== NULL) + { + pFNIsNLSDefinedString = (PFNIsNLSDefinedString) GetProcAddressForSortingApi( + "IsNLSDefinedString", + (FARPROC)SortVersioning::SortIsDefinedString, + (FARPROC)DownLevel::IsNLSDefinedString, + lpVersionInfo); + } + + // Call the appropriate function and return + return pFNIsNLSDefinedString(Function, dwFlags, (LPNLSVERSIONINFO)lpVersionInfo, lpString, cchStr); +#endif + } + + +#if !defined(FEATURE_CORECLR) + BOOL GetNlsVersionEx(__in NLS_FUNCTION Function, __in LPCWSTR lpLocaleName, __inout LPNLSVERSIONINFOEX lpVersionInfo) + { + + typedef BOOL (WINAPI *PFNGetNLSVersionEx)(NLS_FUNCTION, LPCWSTR, LPNLSVERSIONINFOEX); + + static PFNGetNLSVersionEx pFNGetNLSVersionEx=NULL; + + // See if we still need to find our function + if (pFNGetNLSVersionEx == NULL) + { + // We only call this on Win8 and above, so this should always work. + pFNGetNLSVersionEx = (PFNGetNLSVersionEx) GetSystemProcAddressForSortingApi("GetNLSVersionEx", NULL); + } + + _ASSERTE(pFNGetNLSVersionEx != NULL); + + return pFNGetNLSVersionEx(Function, lpLocaleName, lpVersionInfo); + } +#endif + + // This is a Windows 7 and above function + // This returns the "specific" locale from an input name, ie: "en" returns "en-US", + // although note that it should always succeed!(returning "" neutral if nothing else) + __success(return != 0) + int ResolveLocaleName(__in LPCWSTR lpNameToResolve, __in_ecount_opt(cchLocaleName) LPWSTR lpLocaleName, __in int cchLocaleName) + { + _ASSERTE(NotLeakingFrameworkOnlyCultures(lpLocaleName)); + int retVal = 0; +#if !defined(ENABLE_DOWNLEVEL_FOR_NLS) + retVal = ::ResolveLocaleName(lpNameToResolve, lpLocaleName, cchLocaleName); +#else + typedef int (WINAPI *PFNResolveLocaleName)(LPCWSTR, LPWSTR, int); + + static PFNResolveLocaleName pFNResolveLocaleName=NULL; + if (pFNResolveLocaleName == NULL) + { + pFNResolveLocaleName =(PFNResolveLocaleName) GetProcAddressForLocaleApi( + "ResolveLocaleName", + (FARPROC)DownLevel::ResolveLocaleName); + } + retVal = pFNResolveLocaleName(lpNameToResolve, lpLocaleName, cchLocaleName); + if (retVal == 0) + { + retVal = DownLevel::ResolveLocaleName(lpNameToResolve, lpLocaleName, cchLocaleName); + } +#endif + + return retVal; + } + + __success(return == TRUE) BOOL + GetThreadPreferredUILanguages(__in DWORD dwFlags, + __out PULONG pulNumLanguages, + __out_ecount_opt(*pcchLanguagesBuffer) PWSTR pwszLanguagesBuffer, + __inout PULONG pcchLanguagesBuffer) + { + +#if !defined(ENABLE_DOWNLEVEL_FOR_NLS) + return ::GetThreadPreferredUILanguages(dwFlags, pulNumLanguages,pwszLanguagesBuffer,pcchLanguagesBuffer); +#else + typedef int (WINAPI *PFNGetThreadPreferredUILanguages)(DWORD, PULONG,PWSTR,PULONG); + static PFNGetThreadPreferredUILanguages pFNGetThreadPreferredUILanguages=NULL; + if (pFNGetThreadPreferredUILanguages == NULL) + { + HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W); + if(hMod==NULL) + return 0; + pFNGetThreadPreferredUILanguages=(PFNGetThreadPreferredUILanguages)GetProcAddress(hMod,"GetThreadPreferredUILanguages"); + if(pFNGetThreadPreferredUILanguages==NULL) + { + if(GetLastError() == ERROR_PROC_NOT_FOUND) + pFNGetThreadPreferredUILanguages=DownLevel::GetThreadPreferredUILanguages; + else + return 0; + } + } +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable: 26036) // Prefast - Possible postcondition violation due to failure to null terminate string +#endif // _PREFAST_ + return pFNGetThreadPreferredUILanguages(dwFlags, pulNumLanguages,pwszLanguagesBuffer,pcchLanguagesBuffer); +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +#endif + } + + __success(return != 0) + BOOL WINAPI EnumSystemLocalesEx( + __in LOCALE_ENUMPROCEX lpLocaleEnumProc, + __in DWORD dwFlags, + __in LPARAM lParam, + __in_opt LPVOID lpReserved) + { +#if !defined(ENABLE_DOWNLEVEL_FOR_NLS) + return ::EnumSystemLocalesEx(lpLocaleEnumProc, dwFlags, lParam, lpReserved); +#else + typedef BOOL (WINAPI *PFNEnumSystemLocalesEx)( + LOCALE_ENUMPROCEX lpLocaleEnumProc, + DWORD dwFlags, + LPARAM lParam, + LPVOID lpReserved); + static PFNEnumSystemLocalesEx pFNEnumSystemLocalesEx=NULL; + if (pFNEnumSystemLocalesEx== NULL) + { + pFNEnumSystemLocalesEx=(PFNEnumSystemLocalesEx)GetProcAddressForLocaleApi( + "EnumSystemLocalesEx", + NULL); + if (pFNEnumSystemLocalesEx == NULL) + { + + return FALSE; + } + } + BOOL result = pFNEnumSystemLocalesEx(lpLocaleEnumProc, dwFlags, lParam, lpReserved); + + { + if(result == FALSE + && GetLastError() == ERROR_BADDB + && IsWindows7Platform()) + { + HKEY hKey; + if (::RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + W("SYSTEM\\CurrentControlSet\\Control\\Nls\\ExtendedLocale"), + 0, + KEY_READ, + &hKey) == ERROR_SUCCESS) + { + ::RegCloseKey(hKey); + } + else + { + result = TRUE; + } + } + } + return result; +#endif + } +} + + + + + diff --git a/src/utilcode/opinfo.cpp b/src/utilcode/opinfo.cpp new file mode 100644 index 0000000000..a7d71dbfc2 --- /dev/null +++ b/src/utilcode/opinfo.cpp @@ -0,0 +1,129 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +/***************************************************************************/ +/* OpInfo.cpp */ +/***************************************************************************/ + +#include "stdafx.h" +#include <cor.h> // for debugMacros.h +#include "debugmacros.h" // for ASSERTE +#include "opinfo.h" + + +OpInfo::OpInfoData OpInfo::table[] = { + +#ifdef _MSC_VER +#define OPDEF(c,s,pop,push,args,type,l,s1,s2,ctrl) \ + { s, args + type, FLOW_ ## ctrl, pop, push, c }, +#else +#define OPDEF(c,s,pop,push,args,type,l,s1,s2,ctrl) \ + { s, (OPCODE_FORMAT) (args + type), FLOW_ ## ctrl, pop, push, c }, +#endif + + // Kind of a workaround, get the prefixes (IInternal) to return InlineOpcode instead of InlineNone +#define IInternal (InlineOpcode - InlineNone) +#define IMacro 0 +#define IPrimitive 0 +#define IAnnotation 0 +#define IObjModel 0 +#define IPrefix 0 + +#define Pop0 0 +#define Pop1 1 +#define PopI 1 +#define PopI4 1 +#define PopR4 1 +#define PopI8 1 +#define PopR8 1 +#define PopRef 1 +#define VarPop -1 + +#define Push0 0 +#define Push1 1 +#define PushI 1 +#define PushI4 1 +#define PushR4 1 +#define PushI8 1 +#define PushR8 1 +#define PushRef 1 +#define VarPush -1 + +#include "opcode.def" +#undef OPDEF +}; + + +/***************************************************************************/ +/* parse instruction at 'instrPtr', into its opcode (OpInfo), and its + (inline)args, 'args' 'instrPtr' is updated */ + +/***************************************************************************/ +const BYTE* OpInfo::fetch(const BYTE* instrPtr, OpArgsVal* args) { + + data = &table[*instrPtr++]; +AGAIN: + _ASSERTE(data - table == data->opcode); + switch(data->format) { + case InlineNone: + break; + case InlineOpcode: + _ASSERTE(*instrPtr + 256 < (int) (sizeof(table) / sizeof(OpInfoData))); + data = &table[256 + *instrPtr++]; + goto AGAIN; + + case ShortInlineVar: + args->i = *instrPtr; instrPtr +=1; + break; + case InlineVar: + args->i = GET_UNALIGNED_VAL16(instrPtr); instrPtr +=2; + break; + case ShortInlineI: + case ShortInlineBrTarget: + args->i = *instrPtr; instrPtr +=1; + break; + case ShortInlineR: { + DWORD f = GET_UNALIGNED_VAL32(instrPtr); instrPtr +=4; + args->r = *((float*) (&f)); + } + break; + case InlineRVA: + case InlineI: + case InlineMethod: + case InlineField: + case InlineType: + case InlineString: + case InlineSig: + case InlineTok: + case InlineBrTarget: + args->i = GET_UNALIGNED_VAL32(instrPtr); instrPtr +=4; + break; + case InlineI8: + args->i8 = GET_UNALIGNED_VAL64(instrPtr); instrPtr +=8; + break; + case InlineR: { + __int64 d = GET_UNALIGNED_VAL64(instrPtr); instrPtr +=8; + instrPtr += 8; + args->r = *((double*) (&d)); + } break; + case InlineSwitch: + args->switch_.count = GET_UNALIGNED_VAL32(instrPtr); instrPtr +=4; + args->switch_.targets = (int*) instrPtr; instrPtr += (4 * args->switch_.count); + break; + case InlinePhi: + args->phi.count = GET_UNALIGNED_VAL32(instrPtr); instrPtr +=1; + args->phi.vars = (unsigned short*) instrPtr; instrPtr += (2 * args->phi.count); + break; + default: +#ifdef _DEBUG + _ASSERTE(!"BadType"); +#else + __assume(0); // we are really certain the default case does not happen +#endif + break; + } + return(instrPtr); +} + diff --git a/src/utilcode/outstring.cpp b/src/utilcode/outstring.cpp new file mode 100644 index 0000000000..e79d70775e --- /dev/null +++ b/src/utilcode/outstring.cpp @@ -0,0 +1,182 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +/*****************************************************************/ +/* OutString.cpp */ +/*****************************************************************/ +/* A simple, lightweight, character output stream, with very few + external dependancies (like sprintf ... ) */ + +/* + Date : 2/1/99 */ +/*****************************************************************/ + +#include "stdafx.h" +#include "outstring.h" + + +/*****************************************************************/ +// print out 'count' instances of the character 'c' +OutString& OutString::pad(size_t count, char c) { + if (cur+count > end) + Realloc(count); + memset(cur, c, count); + cur = cur + count; + return(*this); +} + +/*****************************************************************/ +// prints out a decimal representation +OutString& OutString::operator<<(double d) { + + if (d == 0.0) { + *this << "0.0"; + return *this; + } + + if (d < 0) { + d = -d; + *this << '-'; + } + + // compute the exponent + int exponent = 0; + while (d > 10.0) { + d /= 10; + exponent++; + if (exponent > 500) { // avoids a possible infinite loop + *this << "INF"; + return *this; + } + } + while (d < 1.0) { + d *= 10; + --exponent; + if (exponent < -500) { // avoids a possible infinite loop + *this << "0.0"; + return *this; + } + } + + // we now have a normalized d (between 1 and 10) + double delta = .5E-10; + d += delta; // round to the precision we are displaying + + unsigned trailingZeros = 0; + for(unsigned i = 0; i < 10; i++) { + int digit = (int) d; + d = (d - digit) * 10; // ISSUE: does roundoff ever bite us here? + + if (digit == 0) // defer printing traiing zeros + trailingZeros++; + else { + if (trailingZeros > 0) { + this->pad(trailingZeros, '0'); + trailingZeros = 0; + } + *this << (char) ('0' + digit); + } + if (i == 0) + *this << '.'; + + } + if (exponent != 0) { + *this << 'E'; + *this << exponent; + } + return(*this); +} + +/*****************************************************************/ +// prints out a decimal representation +OutString& OutString::dec(int i, size_t minWidth) { + char buff[12]; // big enough for any number (10 digits, - sign, null term) + char* ptr = &buff[11]; + *ptr = 0; + + unsigned val = i; + if (i < 0) + val = -i; // note this happens to also work for minint! + + for(;;) { + if (val < 10) { + *--ptr = '0' + val; + break; + } + *--ptr = '0' + (val % 10); + val = val / 10; + } + + if (i < 0) + *--ptr = '-'; + + size_t len = &buff[11] - ptr; // length of string + if (len < minWidth) + pad(minWidth-len, ' '); + + *this << ptr; + return(*this); +} + +/*****************************************************************/ +OutString& OutString::hex(unsigned __int64 i, int minWidth, unsigned flags) { + + unsigned hi = unsigned(i >> 32); + unsigned low = unsigned(i); + + if (hi != 0) { + minWidth -= 8; + hex(hi, minWidth, flags); // print upper bits + flags = zeroFill; + minWidth = 8; + } + return hex(low, minWidth, flags); // print lower bits +} + +/*****************************************************************/ +OutString& OutString::hex(unsigned i, int minWidth, unsigned flags) { + char buff[12]; // big enough for any number + char* ptr = &buff[11]; + *ptr = 0; + + static const char digits[] = "0123456789ABCDEF"; + + for(;;) { + if (i < 16) { + *--ptr = digits[i]; + break; + } + *--ptr = digits[(i % 16)]; + i = i / 16; + } + + size_t len = &buff[11] - ptr; // length of string + if (flags & put0x) { + if (flags & zeroFill) + *this << "0x"; + else + *--ptr = 'x', *--ptr = '0'; + len += 2; + } + + if (len < (size_t)minWidth) + pad(minWidth-len, (flags & zeroFill) ? '0' : ' '); + + *this << ptr; + return(*this); +} + +/*****************************************************************/ +void OutString::Realloc(size_t neededSpace) { + size_t oldSize = cur-start; + size_t newSize = (oldSize + neededSpace) * 3 / 2 + 32; + char* oldBuff = start; + start = new char[newSize+1]; + memcpy(start, oldBuff, oldSize); + cur = &start[oldSize]; + end = &start[newSize]; + delete [] oldBuff; +} + diff --git a/src/utilcode/pedecoder.cpp b/src/utilcode/pedecoder.cpp new file mode 100644 index 0000000000..049276ba5b --- /dev/null +++ b/src/utilcode/pedecoder.cpp @@ -0,0 +1,2938 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// -------------------------------------------------------------------------------- +// PEDecoder.cpp +// + +// -------------------------------------------------------------------------------- + +#include "stdafx.h" + +#include "ex.h" +#include "pedecoder.h" +#include "mdcommon.h" +#include "nibblemapmacros.h" + +CHECK PEDecoder::CheckFormat() const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_CHECK_END; + + CHECK(HasContents()); + + if (HasNTHeaders()) + { + CHECK(CheckNTHeaders()); + + if (HasCorHeader()) + { + CHECK(CheckCorHeader()); + +#if !defined(FEATURE_MIXEDMODE) && !defined(FEATURE_PREJIT) + CHECK(IsILOnly()); +#endif + + if (IsILOnly() && !HasReadyToRunHeader()) + CHECK(CheckILOnly()); + + if (HasNativeHeader()) + CHECK(CheckNativeHeader()); + + CHECK(CheckWillCreateGuardPage()); + } + } + + CHECK_OK; +} + +CHECK PEDecoder::CheckNTFormat() const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + PRECONDITION(HasContents()); + } + CONTRACT_CHECK_END; + + CHECK(CheckFormat()); + CHECK(HasNTHeaders()); + + CHECK_OK; +} + +CHECK PEDecoder::CheckCORFormat() const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + PRECONDITION(HasContents()); + } + CONTRACT_CHECK_END; + + CHECK(CheckFormat()); + CHECK(HasNTHeaders()); + CHECK(HasCorHeader()); + + CHECK_OK; +} + + +CHECK PEDecoder::CheckILFormat() const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + PRECONDITION(HasContents()); + } + CONTRACT_CHECK_END; + + CHECK(CheckFormat()); + CHECK(HasNTHeaders()); + CHECK(HasCorHeader()); + CHECK(!HasNativeHeader()); + + CHECK_OK; +} + + +CHECK PEDecoder::CheckILOnlyFormat() const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + PRECONDITION(HasContents()); + } + CONTRACT_CHECK_END; + + CHECK(CheckFormat()); + CHECK(HasNTHeaders()); + CHECK(HasCorHeader()); + CHECK(IsILOnly()); + CHECK(!HasNativeHeader()); + + CHECK_OK; +} + +CHECK PEDecoder::CheckNativeFormat() const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + PRECONDITION(HasContents()); + } + CONTRACT_CHECK_END; + +#ifdef FEATURE_PREJIT + CHECK(CheckFormat()); + CHECK(HasNTHeaders()); + CHECK(HasCorHeader()); + CHECK(!IsILOnly()); + CHECK(HasNativeHeader()); +#else // FEATURE_PREJIT + CHECK(false); +#endif // FEATURE_PREJIT + + CHECK_OK; +} + +BOOL PEDecoder::HasNTHeaders() const +{ + CONTRACT(BOOL) + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + PRECONDITION(HasContents()); + SO_TOLERANT; + } + CONTRACT_END; + + // Check for a valid DOS header + + if (m_size < sizeof(IMAGE_DOS_HEADER)) + RETURN FALSE; + + IMAGE_DOS_HEADER* pDOS = PTR_IMAGE_DOS_HEADER(m_base); + + { + if (pDOS->e_magic != VAL16(IMAGE_DOS_SIGNATURE) + || (DWORD) pDOS->e_lfanew == VAL32(0)) + { + RETURN FALSE; + } + + // Check for integer overflow + S_SIZE_T cbNTHeaderEnd(S_SIZE_T(static_cast<SIZE_T>(VAL32(pDOS->e_lfanew))) + + S_SIZE_T(sizeof(IMAGE_NT_HEADERS))); + if (cbNTHeaderEnd.IsOverflow()) + { + RETURN FALSE; + } + + // Now check for a valid NT header + if (m_size < cbNTHeaderEnd.Value()) + { + RETURN FALSE; + } + } + + IMAGE_NT_HEADERS *pNT = PTR_IMAGE_NT_HEADERS(m_base + VAL32(pDOS->e_lfanew)); + + if (pNT->Signature != VAL32(IMAGE_NT_SIGNATURE)) + RETURN FALSE; + + if (pNT->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC)) + { + if (pNT->FileHeader.SizeOfOptionalHeader != VAL16(sizeof(IMAGE_OPTIONAL_HEADER32))) + RETURN FALSE; + } + else if (pNT->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR64_MAGIC)) + { + // on 64 bit we can promote this + if (pNT->FileHeader.SizeOfOptionalHeader != VAL16(sizeof(IMAGE_OPTIONAL_HEADER64))) + RETURN FALSE; + + // Check for integer overflow + S_SIZE_T cbNTHeaderEnd(S_SIZE_T(static_cast<SIZE_T>(VAL32(pDOS->e_lfanew))) + + S_SIZE_T(sizeof(IMAGE_NT_HEADERS64))); + + if (cbNTHeaderEnd.IsOverflow()) + { + RETURN FALSE; + } + + // Now check for a valid NT header + if (m_size < cbNTHeaderEnd.Value()) + { + RETURN FALSE; + } + + } + else + RETURN FALSE; + + // Go ahead and cache NT header since we already found it. + const_cast<PEDecoder *>(this)->m_pNTHeaders = dac_cast<PTR_IMAGE_NT_HEADERS>(pNT); + + RETURN TRUE; +} + +CHECK PEDecoder::CheckNTHeaders() const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + PRECONDITION(HasContents()); + SO_TOLERANT; + } + CONTRACT_CHECK_END; + + // Only check once per file + if (m_flags & FLAG_NT_CHECKED) + CHECK_OK; + + CHECK(HasNTHeaders()); + + IMAGE_NT_HEADERS *pNT = FindNTHeaders(); + + CHECK((pNT->FileHeader.Characteristics & VAL16(IMAGE_FILE_SYSTEM)) == 0); + + CHECK(CheckAlignment(VAL32(pNT->OptionalHeader.FileAlignment))); + CHECK(CheckAlignment(VAL32(pNT->OptionalHeader.SectionAlignment))); + + CHECK(CheckAligned((UINT)VAL32(pNT->OptionalHeader.FileAlignment), 512)); + CHECK(CheckAligned((UINT)VAL32(pNT->OptionalHeader.SectionAlignment), VAL32(pNT->OptionalHeader.FileAlignment))); + + // INVESTIGATE: this doesn't seem to be necessary on Win64 - why?? + //CHECK(CheckAligned((UINT)VAL32(pNT->OptionalHeader.SectionAlignment), OS_PAGE_SIZE)); + CHECK(CheckAligned((UINT)VAL32(pNT->OptionalHeader.SectionAlignment), 0x1000)); // for base relocs logic + CHECK(CheckAligned((UINT)VAL32(pNT->OptionalHeader.SizeOfImage), VAL32(pNT->OptionalHeader.SectionAlignment))); + CHECK(CheckAligned((UINT)VAL32(pNT->OptionalHeader.SizeOfHeaders), VAL32(pNT->OptionalHeader.FileAlignment))); + + // Data directories will be validated later on. + PTR_IMAGE_DATA_DIRECTORY pDataDirectories = NULL; + + if (Has32BitNTHeaders()) + { + IMAGE_NT_HEADERS32* pNT32=GetNTHeaders32(); + CHECK(CheckAligned(VAL32(pNT32->OptionalHeader.ImageBase), 0x10000)); + CHECK((VAL32(pNT32->OptionalHeader.SizeOfStackCommit) <= VAL32(pNT32->OptionalHeader.SizeOfStackReserve))); + CHECK((VAL32(pNT32->OptionalHeader.SizeOfHeapCommit) <= VAL32(pNT32->OptionalHeader.SizeOfHeapReserve))); + pDataDirectories = dac_cast<PTR_IMAGE_DATA_DIRECTORY>( + dac_cast<TADDR>(pNT32) + offsetof(IMAGE_NT_HEADERS32, OptionalHeader.DataDirectory)); + } + else + { + IMAGE_NT_HEADERS64* pNT64=GetNTHeaders64(); + CHECK(CheckAligned(VAL64(pNT64->OptionalHeader.ImageBase), 0x10000)); + CHECK((VAL64(pNT64->OptionalHeader.SizeOfStackCommit) <= VAL64(pNT64->OptionalHeader.SizeOfStackReserve))); + CHECK((VAL64(pNT64->OptionalHeader.SizeOfHeapCommit) <= VAL64(pNT64->OptionalHeader.SizeOfHeapReserve))); + pDataDirectories = dac_cast<PTR_IMAGE_DATA_DIRECTORY>( + dac_cast<TADDR>(pNT64) + offsetof(IMAGE_NT_HEADERS64, OptionalHeader.DataDirectory)); + } + + // @todo: this is a bit awkward here, it would be better to make this assertion on + // PEDecoder instantiation. However, we don't necessarily have the NT headers there (in fact + // they might not exist.) + + if (IsMapped()) + { + // Ideally we would require the layout address to honor the section alignment constraints. + // However, we do have 8K aligned IL only images which we load on 32 bit platforms. In this + // case, we can only guarantee OS page alignment (which after all, is good enough.) + CHECK(CheckAligned(m_base, OS_PAGE_SIZE)); + } + + // @todo: check NumberOfSections for overflow of SizeOfHeaders + + UINT32 currentAddress = 0; + UINT32 currentOffset = 0; + + CHECK(CheckSection(currentAddress, 0, VAL32(pNT->OptionalHeader.SizeOfHeaders), + currentOffset, 0, VAL32(pNT->OptionalHeader.SizeOfHeaders))); + + currentAddress=currentOffset=VAL32(pNT->OptionalHeader.SizeOfHeaders); + + PTR_IMAGE_SECTION_HEADER section = FindFirstSection(pNT); + PTR_IMAGE_SECTION_HEADER sectionEnd = section + VAL16(pNT->FileHeader.NumberOfSections); + + CHECK(sectionEnd >= section); + + + while (section < sectionEnd) + { + + // + // NOTE: the if condition is becuase of a design issue in the CLR and OS loader's remapping + // of PE32 headers to PE32+. Because IMAGE_NT_HEADERS64 is bigger than IMAGE_NT_HEADERS32, + // the remapping will expand this part of the header and push out the following + // IMAGE_SECTION_HEADER entries. When IMAGE_DOS_HEADER::e_lfanew is large enough (size is + // proportional to the number of tools used to produce the inputs to the C++ linker, and + // has become larger when producing some WinMD files) this can push the last section header + // beyond the boundary set by IMAGE_NT_HEADERS::OptionalHeader.SizeOfHeaders (e.g., this + // was recently seen where the unaligned size of the headers was 0x1f8 and SizeOfHeaders was + // 0x200, and the header remapping resulted in new headers size of 0x208). To compensate + // for this issue (it would be quite difficult to fix in the remapping code; see Dev11 430008) + // we assume that when the image is mapped that the needed validation has already been done. + // + + if (!IsMapped()) + { + CHECK(CheckBounds(dac_cast<PTR_CVOID>(pNT),VAL32(pNT->OptionalHeader.SizeOfHeaders), + section,sizeof(IMAGE_SECTION_HEADER))); + } + + // Check flags + // Only allow a small list of characteristics + CHECK(!(section->Characteristics & + ~(VAL32((IMAGE_SCN_CNT_CODE | + IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_CNT_UNINITIALIZED_DATA| + IMAGE_SCN_MEM_DISCARDABLE | + IMAGE_SCN_MEM_NOT_CACHED | + IMAGE_SCN_MEM_NOT_PAGED | + IMAGE_SCN_MEM_EXECUTE | + IMAGE_SCN_MEM_READ | + IMAGE_SCN_MEM_WRITE | + // allow shared sections for all images for now. + // we'll constrain this in CheckILOnly + IMAGE_SCN_MEM_SHARED))))); + + // we should not allow writable code sections, check if both flags are set + CHECK((section->Characteristics & VAL32((IMAGE_SCN_CNT_CODE|IMAGE_SCN_MEM_WRITE))) != + VAL32((IMAGE_SCN_CNT_CODE|IMAGE_SCN_MEM_WRITE))); + + CHECK(CheckSection(currentAddress, VAL32(section->VirtualAddress), VAL32(section->Misc.VirtualSize), + currentOffset, VAL32(section->PointerToRawData), VAL32(section->SizeOfRawData))); + + currentAddress = VAL32(section->VirtualAddress) + + AlignUp((UINT)VAL32(section->Misc.VirtualSize), (UINT)VAL32(pNT->OptionalHeader.SectionAlignment)); + currentOffset = VAL32(section->PointerToRawData) + VAL32(section->SizeOfRawData); + + section++; + } + + // Now check that the COR data directory is either NULL, or exists entirely in one section. + { + PTR_IMAGE_DATA_DIRECTORY pCORDataDir = pDataDirectories + IMAGE_DIRECTORY_ENTRY_COMHEADER; + CHECK(CheckRva(VAL32(pCORDataDir->VirtualAddress), VAL32(pCORDataDir->Size), 0, NULL_OK)); + } + + // @todo: verify directory entries + + const_cast<PEDecoder *>(this)->m_flags |= FLAG_NT_CHECKED; + + CHECK_OK; +} + +CHECK PEDecoder::CheckSection(COUNT_T previousAddressEnd, COUNT_T addressStart, COUNT_T addressSize, + COUNT_T previousOffsetEnd, COUNT_T offsetStart, COUNT_T offsetSize) const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + PRECONDITION(HasNTHeaders()); + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + SO_TOLERANT; + } + CONTRACT_CHECK_END; + + // Fetch the NT header + IMAGE_NT_HEADERS *pNT = FindNTHeaders(); + + // OS will zero pad a mapped file up to file alignment size - some images rely on this + // COUNT_T alignedSize = AlignUp(m_size, VAL32(pNT->OptionalHeader.FileAlignment)); + COUNT_T alignedSize = IsMapped() ? AlignUp(m_size, VAL32(pNT->OptionalHeader.FileAlignment)) : m_size; + + // Check to make sure that our memory is big enough to cover the stated range. + // Note that this check is only required if we have a non-flat image. + if (IsMapped()) + CHECK(alignedSize >= VAL32(pNT->OptionalHeader.SizeOfImage)); + + // Check expected alignments + CHECK(CheckAligned(addressStart, VAL32(pNT->OptionalHeader.SectionAlignment))); + CHECK(CheckAligned(offsetStart, VAL32(pNT->OptionalHeader.FileAlignment))); + CHECK(CheckAligned(offsetSize, VAL32(pNT->OptionalHeader.FileAlignment))); + + // addressSize is typically not aligned, so we align it for purposes of checks. + COUNT_T alignedAddressSize = AlignUp(addressSize, VAL32(pNT->OptionalHeader.SectionAlignment)); + CHECK(addressSize <= alignedAddressSize); + + // Check overflow + CHECK(CheckOverflow(addressStart, alignedAddressSize)); + CHECK(CheckOverflow(offsetStart, offsetSize)); + + // Make sure we don't overlap the previous section + CHECK(addressStart >= previousAddressEnd + && (offsetSize == 0 + || offsetStart >= previousOffsetEnd)); + + // Make sure we don't overrun the end of the mapped image + CHECK(addressStart + alignedAddressSize <= VAL32(pNT->OptionalHeader.SizeOfImage)); + + // Make sure we don't overrun the end of the file (only relevant if we're not mapped, otherwise + // we don't know the file size, as it's not declared in the headers.) + if (!IsMapped()) + CHECK(offsetStart + offsetSize <= alignedSize); + + // Make sure the data doesn't overrun the virtual address space + CHECK(offsetSize <= alignedAddressSize); + + CHECK_OK; +} + +CHECK PEDecoder::CheckDirectoryEntry(int entry, int forbiddenFlags, IsNullOK ok) const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + PRECONDITION(entry < IMAGE_NUMBEROF_DIRECTORY_ENTRIES); + PRECONDITION(HasDirectoryEntry(entry)); + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACT_CHECK_END; + + CHECK(CheckDirectory(GetDirectoryEntry(entry), forbiddenFlags, ok)); + + CHECK_OK; +} + +CHECK PEDecoder::CheckDirectory(IMAGE_DATA_DIRECTORY *pDir, int forbiddenFlags, IsNullOK ok) const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + PRECONDITION(CheckPointer(pDir)); + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + SO_TOLERANT; + } + CONTRACT_CHECK_END; + + CHECK(CheckRva(VAL32(pDir->VirtualAddress), VAL32(pDir->Size), forbiddenFlags, ok)); + + CHECK_OK; +} + +CHECK PEDecoder::CheckRva(RVA rva, COUNT_T size, int forbiddenFlags, IsNullOK ok) const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + SO_TOLERANT; + } + CONTRACT_CHECK_END; + + if (rva == 0) + { + CHECK_MSG(ok == NULL_OK, "Zero RVA illegal"); + CHECK(size == 0); + } + else + { + IMAGE_SECTION_HEADER *section = RvaToSection(rva); + + CHECK(section != NULL); + + CHECK(CheckBounds(VAL32(section->VirtualAddress), + // AlignUp((UINT)VAL32(section->Misc.VirtualSize), (UINT)VAL32(FindNTHeaders()->OptionalHeader.SectionAlignment)), + (UINT)VAL32(section->Misc.VirtualSize), + rva, size)); + if(!IsMapped()) + { + CHECK(CheckBounds(VAL32(section->VirtualAddress), VAL32(section->SizeOfRawData), rva, size)); + } + + if (forbiddenFlags!=0) + CHECK((section->Characteristics & VAL32(forbiddenFlags))==0); + } + + CHECK_OK; +} + +CHECK PEDecoder::CheckRva(RVA rva, IsNullOK ok) const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + SO_TOLERANT; + } + CONTRACT_CHECK_END; + + if (rva == 0) + CHECK_MSG(ok == NULL_OK, "Zero RVA illegal"); + else + CHECK(RvaToSection(rva) != NULL); + + CHECK_OK; +} + +CHECK PEDecoder::CheckOffset(COUNT_T fileOffset, COUNT_T size, IsNullOK ok) const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_CHECK_END; + + if (fileOffset == 0) + { + CHECK_MSG(ok == NULL_OK, "zero fileOffset illegal"); + CHECK(size == 0); + } + else + { + IMAGE_SECTION_HEADER *section = OffsetToSection(fileOffset); + + CHECK(section != NULL); + + CHECK(CheckBounds(section->PointerToRawData, section->SizeOfRawData, + fileOffset, size)); + } + + CHECK_OK; +} + +CHECK PEDecoder::CheckOffset(COUNT_T fileOffset, IsNullOK ok) const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_CHECK_END; + + if (fileOffset == NULL) + CHECK_MSG(ok == NULL_OK, "Null pointer illegal"); + else + { + CHECK(OffsetToSection(fileOffset) != NULL); + } + + CHECK_OK; +} + +CHECK PEDecoder::CheckData(const void *data, COUNT_T size, IsNullOK ok) const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_CHECK_END; + + if (data == NULL) + { + CHECK_MSG(ok == NULL_OK, "NULL pointer illegal"); + CHECK(size == 0); + } + else + { + CHECK(CheckUnderflow(data, m_base)); + CHECK((UINT_PTR) (((BYTE *) data) - ((BYTE *) m_base)) <= COUNT_T_MAX); + + if (IsMapped()) + CHECK(CheckRva((COUNT_T) ((BYTE *) data - (BYTE *) m_base), size)); + else + CHECK(CheckOffset((COUNT_T) ((BYTE *) data - (BYTE *) m_base), size)); + } + + CHECK_OK; +} + +CHECK PEDecoder::CheckData(const void *data, IsNullOK ok) const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_CHECK_END; + + if (data == NULL) + CHECK_MSG(ok == NULL_OK, "Null pointer illegal"); + else + { + CHECK(CheckUnderflow(data, m_base)); + CHECK((UINT_PTR) (((BYTE *) data) - ((BYTE *) m_base)) <= COUNT_T_MAX); + + if (IsMapped()) + CHECK(CheckRva((COUNT_T) ((BYTE *) data - (BYTE *) m_base))); + else + CHECK(CheckOffset((COUNT_T) ((BYTE *) data - (BYTE *) m_base))); + } + + CHECK_OK; +} + +CHECK PEDecoder::CheckInternalAddress(SIZE_T address, IsNullOK ok) const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACT_CHECK_END; + + if (address == 0) + CHECK_MSG(ok == NULL_OK, "Zero RVA illegal"); + else + CHECK(RvaToSection(InternalAddressToRva(address)) != NULL); + + CHECK_OK; +} + +CHECK PEDecoder::CheckInternalAddress(SIZE_T address, COUNT_T size, IsNullOK ok) const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACT_CHECK_END; + + if (address == 0) + { + CHECK_MSG(ok == NULL_OK, "Zero RVA illegal"); + CHECK(size == 0); + } + else + { + CHECK(CheckRva(InternalAddressToRva(address), size)); + } + + CHECK_OK; +} + +RVA PEDecoder::InternalAddressToRva(SIZE_T address) const +{ + CONTRACT(RVA) + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + NOTHROW; + GC_NOTRIGGER; + POSTCONDITION(CheckRva(RETVAL)); + SO_TOLERANT; + } + CONTRACT_END; + + if (m_flags & FLAG_RELOCATED) + { + // Address has been fixed up + RETURN (RVA) ((BYTE *) address - (BYTE *) m_base); + } + else + { + // Address has not been fixed up + RETURN (RVA) (address - (SIZE_T) GetPreferredBase()); + } +} + +// Returns a pointer to the named section or NULL if not found. +// The name should include the starting "." as well. +IMAGE_SECTION_HEADER *PEDecoder::FindSection(LPCSTR sectionName) const +{ + CONTRACT(IMAGE_SECTION_HEADER *) + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + PRECONDITION(sectionName != NULL); + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + SO_TOLERANT; + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + // Ensure that the section name length is valid + SIZE_T iSectionNameLength = strlen(sectionName); + if ((iSectionNameLength < 1) || (iSectionNameLength > IMAGE_SIZEOF_SHORT_NAME)) + { + _ASSERTE(!"Invalid section name!"); + RETURN NULL; + } + + // Get the start and ends of the sections + PTR_IMAGE_SECTION_HEADER pSection = FindFirstSection(FindNTHeaders()); + _ASSERTE(pSection != NULL); + PTR_IMAGE_SECTION_HEADER pSectionEnd = pSection + VAL16(FindNTHeaders()->FileHeader.NumberOfSections); + _ASSERTE(pSectionEnd != NULL); + + BOOL fFoundSection = FALSE; + + // Loop thru the sections and see if we got the section we are interested in + while (pSection < pSectionEnd) + { + // Is this the section we are looking for? + if (strncmp(sectionName, (char*)pSection->Name, iSectionNameLength) == 0) + { + // We found our section - break out of the loop + fFoundSection = TRUE; + break; + } + + // Move to the next section + pSection++; + } + + if (TRUE == fFoundSection) + RETURN pSection; + else + RETURN NULL; +} + +IMAGE_SECTION_HEADER *PEDecoder::RvaToSection(RVA rva) const +{ + CONTRACT(IMAGE_SECTION_HEADER *) + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + SUPPORTS_DAC; + SO_TOLERANT; + } + CONTRACT_END; + + PTR_IMAGE_SECTION_HEADER section = dac_cast<PTR_IMAGE_SECTION_HEADER>(FindFirstSection(FindNTHeaders())); + PTR_IMAGE_SECTION_HEADER sectionEnd = section + VAL16(FindNTHeaders()->FileHeader.NumberOfSections); + + while (section < sectionEnd) + { + if (rva < (VAL32(section->VirtualAddress) + + AlignUp((UINT)VAL32(section->Misc.VirtualSize), (UINT)VAL32(FindNTHeaders()->OptionalHeader.SectionAlignment)))) + { + if (rva < VAL32(section->VirtualAddress)) + RETURN NULL; + else + { + RETURN section; + } + } + + section++; + } + + RETURN NULL; +} + +IMAGE_SECTION_HEADER *PEDecoder::OffsetToSection(COUNT_T fileOffset) const +{ + CONTRACT(IMAGE_SECTION_HEADER *) + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + NOTHROW; + GC_NOTRIGGER; + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + SUPPORTS_DAC; + SO_TOLERANT; + } + CONTRACT_END; + + PTR_IMAGE_SECTION_HEADER section = dac_cast<PTR_IMAGE_SECTION_HEADER>(FindFirstSection(FindNTHeaders())); + PTR_IMAGE_SECTION_HEADER sectionEnd = section + VAL16(FindNTHeaders()->FileHeader.NumberOfSections); + + while (section < sectionEnd) + { + if (fileOffset < section->PointerToRawData + section->SizeOfRawData) + { + if (fileOffset < section->PointerToRawData) + RETURN NULL; + else + RETURN section; + } + + section++; + } + + RETURN NULL; +} + +TADDR PEDecoder::GetRvaData(RVA rva, IsNullOK ok /*= NULL_NOT_OK*/) const +{ + CONTRACT(TADDR) + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + PRECONDITION(CheckRva(rva, NULL_OK)); + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + CANNOT_TAKE_LOCK; + SUPPORTS_DAC; + } + CONTRACT_END; + + if ((rva == 0)&&(ok == NULL_NOT_OK)) + RETURN NULL; + + RVA offset; + if (IsMapped()) + offset = rva; + else + { + // !!! check for case where rva is in padded portion of segment + offset = RvaToOffset(rva); + } + + RETURN( m_base + offset ); +} + +RVA PEDecoder::GetDataRva(const TADDR data) const +{ + CONTRACT(RVA) + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + PRECONDITION(CheckData((void *)data, NULL_OK)); + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACT_END; + + if (data == NULL) + RETURN 0; + + COUNT_T offset = (COUNT_T) (data - m_base); + if (IsMapped()) + RETURN offset; + else + RETURN OffsetToRva(offset); +} + +BOOL PEDecoder::PointerInPE(PTR_CVOID data) const +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + SO_TOLERANT; + SUPPORTS_DAC; + } + CONTRACTL_END; + + TADDR taddrData = dac_cast<TADDR>(data); + TADDR taddrBase = dac_cast<TADDR>(m_base); + + if (this->IsMapped()) + { + return taddrBase <= taddrData && taddrData < taddrBase + GetVirtualSize(); + } + else + { + return taddrBase <= taddrData && taddrData < taddrBase + GetSize(); + } +} + +TADDR PEDecoder::GetOffsetData(COUNT_T fileOffset, IsNullOK ok /*= NULL_NOT_OK*/) const +{ + CONTRACT(TADDR) + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + PRECONDITION(CheckOffset(fileOffset, NULL_OK)); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_END; + + if ((fileOffset == 0)&&(ok == NULL_NOT_OK)) + RETURN NULL; + + RETURN GetRvaData(OffsetToRva(fileOffset)); +} + +//------------------------------------------------------------------------------- +// Lifted from "..\md\inc\mdfileformat.h" +// (cannot #include it here because it references lot of other stuff) +#define STORAGE_MAGIC_SIG 0x424A5342 // BSJB +struct STORAGESIGNATURE +{ + 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 +}; +typedef STORAGESIGNATURE UNALIGNED * PSTORAGESIGNATURE; +typedef DPTR(STORAGESIGNATURE UNALIGNED) PTR_STORAGESIGNATURE; + + +struct STORAGEHEADER +{ + BYTE fFlags; // STGHDR_xxx flags. + BYTE pad; + USHORT iStreams; // How many streams are there. +}; +typedef STORAGEHEADER UNALIGNED * PSTORAGEHEADER; +typedef DPTR(STORAGEHEADER UNALIGNED) PTR_STORAGEHEADER; + + +struct STORAGESTREAM +{ + ULONG iOffset; // Offset in file for this stream. + ULONG iSize; // Size of the file. + char rcName[32]; // Start of name, null terminated. +}; +typedef STORAGESTREAM UNALIGNED * PSTORAGESTREAM; +typedef DPTR(STORAGESTREAM UNALIGNED) PTR_STORAGESTREAM; + + +// if the stream's name is shorter than 32 bytes (incl.zero terminator), +// the size of storage stream header is less than sizeof(STORAGESTREAM) +// and is padded to 4-byte alignment +inline PTR_STORAGESTREAM NextStorageStream(PTR_STORAGESTREAM pSS) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + CANNOT_TAKE_LOCK; + } + CONTRACTL_END; + + SUPPORTS_DAC; + TADDR pc = dac_cast<TADDR>(pSS); + pc += (sizeof(STORAGESTREAM) - 32 /*sizeof(STORAGESTREAM::rcName)*/ + strlen(pSS->rcName)+1+3)&~3; + return PTR_STORAGESTREAM(pc); +} +//------------------------------------------------------------------------------- + + +CHECK PEDecoder::CheckCorHeader() const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + SO_TOLERANT; + } + CONTRACT_CHECK_END; + + if (m_flags & FLAG_COR_CHECKED) + CHECK_OK; + + CHECK(CheckNTHeaders()); + + CHECK(HasCorHeader()); + + IMAGE_DATA_DIRECTORY *pDir = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_COMHEADER); + + CHECK(CheckDirectory(pDir, IMAGE_SCN_MEM_WRITE, NULL_NOT_OK)); + + CHECK(VAL32(pDir->Size) >= sizeof(IMAGE_COR20_HEADER)); + + IMAGE_SECTION_HEADER *section = RvaToSection(VAL32(pDir->VirtualAddress)); + CHECK(section != NULL); + CHECK((section->Characteristics & VAL32(IMAGE_SCN_MEM_READ))!=0); + + CHECK(CheckRva(VAL32(pDir->VirtualAddress), sizeof(IMAGE_COR20_HEADER))); + + IMAGE_COR20_HEADER *pCor = GetCorHeader(); + + //CHECK(((ULONGLONG)pCor & 0x3)==0); + + // If the file is COM+ 1.0, which by definition has nothing the runtime can + // use, or if the file requires a newer version of this engine than us, + // it cannot be run by this engine. + CHECK(VAL16(pCor->MajorRuntimeVersion) > 1 && VAL16(pCor->MajorRuntimeVersion) <= COR_VERSION_MAJOR); + + CHECK(CheckDirectory(&pCor->MetaData, IMAGE_SCN_MEM_WRITE, HasNativeHeader() ? NULL_OK : NULL_NOT_OK)); + CHECK(CheckDirectory(&pCor->Resources, IMAGE_SCN_MEM_WRITE, NULL_OK)); + CHECK(CheckDirectory(&pCor->StrongNameSignature, IMAGE_SCN_MEM_WRITE, NULL_OK)); + CHECK(CheckDirectory(&pCor->CodeManagerTable, IMAGE_SCN_MEM_WRITE, NULL_OK)); + CHECK(CheckDirectory(&pCor->VTableFixups, 0, NULL_OK)); + CHECK(CheckDirectory(&pCor->ExportAddressTableJumps, 0, NULL_OK)); + CHECK(CheckDirectory(&pCor->ManagedNativeHeader, 0, NULL_OK)); + + CHECK(VAL32(pCor->cb) >= offsetof(IMAGE_COR20_HEADER, ManagedNativeHeader) + sizeof(IMAGE_DATA_DIRECTORY)); + + DWORD validBits = COMIMAGE_FLAGS_ILONLY + | COMIMAGE_FLAGS_32BITREQUIRED + | COMIMAGE_FLAGS_TRACKDEBUGDATA + | COMIMAGE_FLAGS_STRONGNAMESIGNED + | COMIMAGE_FLAGS_NATIVE_ENTRYPOINT + | COMIMAGE_FLAGS_IL_LIBRARY + | COMIMAGE_FLAGS_32BITPREFERRED; + + CHECK((pCor->Flags&VAL32(~validBits)) == 0); + + // Pure IL images should not have VTable fixups or EAT jumps + if (IsILOnly()) + { + CHECK(pCor->VTableFixups.Size == VAL32(0)); + CHECK(pCor->ExportAddressTableJumps.Size == VAL32(0)); + CHECK(!(pCor->Flags & VAL32(COMIMAGE_FLAGS_NATIVE_ENTRYPOINT))); + //@TODO: If not an exe, check that EntryPointToken is mdNil + } + else + { + if (pCor->Flags & VAL32(COMIMAGE_FLAGS_NATIVE_ENTRYPOINT)) + { + CHECK(CheckRva(VAL32(IMAGE_COR20_HEADER_FIELD(*pCor,EntryPointToken)))); + } + } + + // Strong name signed images should have a signature + if (IsStrongNameSigned()) + CHECK(HasStrongNameSignature()); + + // IL library files (really a misnomer - these are native images) only + // may have a native image header + if ((pCor->Flags&VAL32(COMIMAGE_FLAGS_IL_LIBRARY)) == 0) + { + CHECK(VAL32(pCor->ManagedNativeHeader.Size) == 0); + } + + // Metadata header checks + IMAGE_DATA_DIRECTORY *pDirMD = &pCor->MetaData; + COUNT_T ctMD = (COUNT_T)VAL32(pDirMD->Size); + TADDR pcMD = (TADDR)GetDirectoryData(pDirMD); + + if(pcMD != NULL) + { + // Storage signature checks + CHECK(ctMD >= sizeof(STORAGESIGNATURE)); + PTR_STORAGESIGNATURE pStorageSig = PTR_STORAGESIGNATURE((TADDR)pcMD); + COUNT_T ctMDStreamSize = ctMD; // Store MetaData stream size for later usage + + + CHECK(VAL32(pStorageSig->lSignature) == STORAGE_MAGIC_SIG); + COUNT_T ctSSig; + CHECK(ClrSafeInt<COUNT_T>::addition(sizeof(STORAGESIGNATURE), (COUNT_T)VAL32(pStorageSig->iVersionString), ctSSig)); + CHECK(ctMD > ctSSig); + + // Storage header checks + pcMD += ctSSig; + + PTR_STORAGEHEADER pSHdr = PTR_STORAGEHEADER((TADDR)pcMD); + + + ctMD -= ctSSig; + CHECK(ctMD >= sizeof(STORAGEHEADER)); + pcMD = dac_cast<TADDR>(pSHdr) + sizeof(STORAGEHEADER); + ctMD -= sizeof(STORAGEHEADER); + WORD nStreams = VAL16(pSHdr->iStreams); + + // Storage streams checks (pcMD is a target pointer, so watch out) + PTR_STORAGESTREAM pStr = PTR_STORAGESTREAM((TADDR)pcMD); + PTR_STORAGESTREAM pSSOutOfRange = + PTR_STORAGESTREAM((TADDR)(pcMD + ctMD)); + size_t namelen; + WORD iStr; + PTR_STORAGESTREAM pSS; + for(iStr = 1, pSS = pStr; iStr <= nStreams; iStr++) + { + CHECK(pSS < pSSOutOfRange); + CHECK(pSS + 1 <= pSSOutOfRange); + + for(namelen=0; (namelen<32)&&(pSS->rcName[namelen]!=0); namelen++); + CHECK((0 < namelen)&&(namelen < 32)); + + // Is it ngen image? + if (!HasNativeHeader()) + { + // Forbid HOT_MODEL_STREAM for non-ngen images + CHECK(strcmp(pSS->rcName, HOT_MODEL_STREAM_A) != 0); + } + + pcMD = dac_cast<TADDR>(NextStorageStream(pSS)); + ctMD -= (COUNT_T)(pcMD - dac_cast<TADDR>(pSS)); + + pSS = PTR_STORAGESTREAM((TADDR)pcMD); + } + + // At this moment, pcMD is pointing past the last stream header + // and ctMD contains total size left for streams per se + // Now, check the offsets and sizes of streams + COUNT_T ctStreamsBegin = (COUNT_T)(pcMD - dac_cast<TADDR>(pStorageSig)); // min.possible offset + COUNT_T ctSS, ctSSbegin, ctSSend = 0; + for(iStr = 1, pSS = pStr; iStr <= nStreams; iStr++,pSS = NextStorageStream(pSS)) + { + ctSSbegin = (COUNT_T)VAL32(pSS->iOffset); + CHECK(ctStreamsBegin <= ctSSbegin); + CHECK(ctSSbegin < ctMDStreamSize); + + ctSS = (COUNT_T)VAL32(pSS->iSize); + CHECK(ctMD >= ctSS); + CHECK(ClrSafeInt<COUNT_T>::addition(ctSSbegin, ctSS, ctSSend)); + CHECK(ctSSend <= ctMDStreamSize); + ctMD -= ctSS; + + // Check stream overlap + PTR_STORAGESTREAM pSSprior; + for(pSSprior=pStr; pSSprior < pSS; pSSprior = NextStorageStream(pSSprior)) + { + COUNT_T ctSSprior_end = 0; + CHECK(ClrSafeInt<COUNT_T>::addition((COUNT_T)VAL32(pSSprior->iOffset), (COUNT_T)VAL32(pSSprior->iSize), ctSSprior_end)); + CHECK((ctSSbegin >= ctSSprior_end)||(ctSSend <= (COUNT_T)VAL32(pSSprior->iOffset))); + } + } + } //end if(pcMD != NULL) + + const_cast<PEDecoder *>(this)->m_flags |= FLAG_COR_CHECKED; + + CHECK_OK; +} + +#if !defined(FEATURE_CORECLR) +#define WHIDBEY_SP2_VERSION_MAJOR 2 +#define WHIDBEY_SP2_VERSION_MINOR 0 +#define WHIDBEY_SP2_VERSION_BUILD 50727 +#define WHIDBEY_SP2_VERSION_PRIVATE_BUILD 3053 +#endif // !defined(FEATURE_CORECLR) + + +// This function exists to provide compatibility between two different native image +// (NGEN) formats. In particular, the manifest metadata blob and the full metadata +// blob swapped locations from 3.5RTM to 3.5SP1. The logic here is to look at the +// runtime version embedded in the native image, to determine which format it is. +IMAGE_DATA_DIRECTORY *PEDecoder::GetMetaDataHelper(METADATA_SECTION_TYPE type) const +{ + CONTRACT(IMAGE_DATA_DIRECTORY *) + { + INSTANCE_CHECK; + PRECONDITION(CheckCorHeader()); +#ifdef FEATURE_PREJIT + PRECONDITION(type == METADATA_SECTION_FULL || type == METADATA_SECTION_MANIFEST); + PRECONDITION(type != METADATA_SECTION_MANIFEST || HasNativeHeader()); +#else // FEATURE_PREJIT + PRECONDITION(type == METADATA_SECTION_FULL); +#endif // FEATURE_PREJIT + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDirRet = &GetCorHeader()->MetaData; + +#ifdef FEATURE_PREJIT + // For backward compatibility reasons, we must be able to locate the metadata in all v2 native images as + // well as current version of native images. This is needed by mdbg.exe for SxS debugging scenarios. + // Specifically, mdbg.exe can be used to debug v2 managed applications, and need to be able to find + // metadata in v2 native images. Therefore, the location of the data we need to locate the metadata must + // never be moved. Here are some asserts to ensure that. + // IMAGE_COR20_HEADER should be stable since it is defined in ECMA. Verify a coupld of fields we use: + _ASSERTE(offsetof(IMAGE_COR20_HEADER, MetaData) == 8); + _ASSERTE(offsetof(IMAGE_COR20_HEADER, ManagedNativeHeader) == 64); + // We use a couple of fields in CORCOMPILE_HEADER. + _ASSERTE(offsetof(CORCOMPILE_HEADER, VersionInfo) == 40); + _ASSERTE(offsetof(CORCOMPILE_HEADER, ManifestMetaData) == 88); + // And we use four version fields in CORCOMPILE_VERSION_INFO. + _ASSERTE(offsetof(CORCOMPILE_VERSION_INFO, wVersionMajor) == 4); + _ASSERTE(offsetof(CORCOMPILE_VERSION_INFO, wVersionMinor) == 6); + _ASSERTE(offsetof(CORCOMPILE_VERSION_INFO, wVersionBuildNumber) == 8); + _ASSERTE(offsetof(CORCOMPILE_VERSION_INFO, wVersionPrivateBuildNumber) == 10); + + // Visual Studio took dependency on crossgen /CreatePDB returning COR_E_NI_AND_RUNTIME_VERSION_MISMATCH + // when crossgen and the native image come from different runtimes. In order to reach error path that returns + // COR_E_NI_AND_RUNTIME_VERSION_MISMATCH in this case, size of CORCOMPILE_HEADER has to remain constant to pass earlier + // checks that lead to different error codes. See Windows Phone Blue Bug #45406 for details. + _ASSERTE(sizeof(CORCOMPILE_HEADER) == 160 + sizeof(TADDR)); + + // Handle NGEN format; otherwise, there is only one MetaData section in the + // COR_HEADER and so the value of pDirRet is correct + if (HasNativeHeader()) + { +#ifdef FEATURE_CORECLR + + if (type == METADATA_SECTION_MANIFEST) + pDirRet = &GetNativeHeader()->ManifestMetaData; + +#else // FEATURE_CORECLR + + IMAGE_DATA_DIRECTORY *pDirNativeHeader = &GetNativeHeader()->ManifestMetaData; + + // This code leverages the fact that pre-Whidbey SP2 private build numbers can never + // be greater than Whidbey SP2 private build number, because otherwise major setup + // issues would arise. To prevent this, it is standard to bump the private build + // number up a significant amount for SPs, as was the case between Whidbey SP1 and + // Whidbey SP2. + // + // Since we could be reading an older version of native image, we tell + // GetNativeVersionInfoMaybeNull to skip checking the native header. + CORCOMPILE_VERSION_INFO *pVerInfo = GetNativeVersionInfoMaybeNull(true); + bool fIsPreWhidbeySP2 = false; + + // If pVerInfo is NULL, we assume that we're in an NGEN compilation domain and that + // the information has not yet been written. Since an NGEN compilation domain running + // in the v4.0 runtime can only complie v4.0 native images, we'll assume the default + // fIsPreWhidbeySP2 value (false) is correct. + if (pVerInfo != NULL && pVerInfo->wVersionMajor <= WHIDBEY_SP2_VERSION_MAJOR) + { + if (pVerInfo->wVersionMajor < WHIDBEY_SP2_VERSION_MAJOR) + fIsPreWhidbeySP2 = true; + else if (pVerInfo->wVersionMajor == WHIDBEY_SP2_VERSION_MAJOR) + { + // If the sp2 minor version isn't 0, we need this logic: + // if (pVerInfo->wVersionMinor < WHIDBEY_SP2_VERSION_MINOR) + // fIsPreWhidbeySP2 = true; + // else + // However, if it is zero, with that logic we get a warning about the comparison + // of an unsigned variable to zero always being false. + _ASSERTE(WHIDBEY_SP2_VERSION_MINOR == 0); + + if (pVerInfo->wVersionMinor == WHIDBEY_SP2_VERSION_MINOR) + { + if (pVerInfo->wVersionBuildNumber < WHIDBEY_SP2_VERSION_BUILD) + fIsPreWhidbeySP2 = true; + else if (pVerInfo->wVersionBuildNumber == WHIDBEY_SP2_VERSION_BUILD) + { + if (pVerInfo->wVersionPrivateBuildNumber < WHIDBEY_SP2_VERSION_PRIVATE_BUILD) + fIsPreWhidbeySP2 = true; + } + } + } + } + + // In pre-Whidbey SP2, pDirRet points to manifest and pDirNativeHeader points to full. + if (fIsPreWhidbeySP2) + { + if (type == METADATA_SECTION_FULL) + pDirRet = pDirNativeHeader; + } + // In Whidbey SP2 and later, pDirRet points to full and pDirNativeHeader points to manifest. + else + { + if (type == METADATA_SECTION_MANIFEST) + pDirRet = pDirNativeHeader; + } + +#endif // FEATURE_CORECLR + + } + +#endif // FEATURE_PREJIT + + RETURN pDirRet; +} + +PTR_CVOID PEDecoder::GetMetadata(COUNT_T *pSize) const +{ + CONTRACT(PTR_CVOID) + { + INSTANCE_CHECK; + PRECONDITION(CheckCorHeader()); + PRECONDITION(CheckPointer(pSize, NULL_OK)); + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = GetMetaDataHelper(METADATA_SECTION_FULL); + + if (pSize != NULL) + *pSize = VAL32(pDir->Size); + + RETURN dac_cast<PTR_VOID>(GetDirectoryData(pDir)); +} + +const void *PEDecoder::GetResources(COUNT_T *pSize) const +{ + CONTRACT(const void *) + { + INSTANCE_CHECK; + PRECONDITION(CheckCorHeader()); + PRECONDITION(CheckPointer(pSize, NULL_OK)); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->Resources; + + if (pSize != NULL) + *pSize = VAL32(pDir->Size); + + RETURN (void *)GetDirectoryData(pDir); +} + +CHECK PEDecoder::CheckResource(COUNT_T offset) const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + PRECONDITION(CheckCorHeader()); + } + CONTRACT_CHECK_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->Resources; + + CHECK(CheckOverflow(VAL32(pDir->VirtualAddress), offset)); + + RVA rva = VAL32(pDir->VirtualAddress) + offset; + + // Make sure we have at least enough data for a length + CHECK(CheckRva(rva, sizeof(DWORD))); + + // Make sure resource is within resource section + CHECK(CheckBounds(VAL32(pDir->VirtualAddress), VAL32(pDir->Size), + rva + sizeof(DWORD), GET_UNALIGNED_VAL32((LPVOID)GetRvaData(rva)))); + + CHECK_OK; +} + +const void *PEDecoder::GetResource(COUNT_T offset, COUNT_T *pSize) const +{ + CONTRACT(const void *) + { + INSTANCE_CHECK; + PRECONDITION(CheckCorHeader()); + PRECONDITION(CheckPointer(pSize, NULL_OK)); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->Resources; + + // 403571: Prefix complained correctly about need to always perform rva check + if (CheckResource(offset) == FALSE) + return NULL; + + void * resourceBlob = (void *)GetRvaData(VAL32(pDir->VirtualAddress) + offset); + // Holds if CheckResource(offset) == TRUE + PREFIX_ASSUME(resourceBlob != NULL); + + if (pSize != NULL) + *pSize = GET_UNALIGNED_VAL32(resourceBlob); + + RETURN (const void *) ((BYTE*)resourceBlob+sizeof(DWORD)); +} + +BOOL PEDecoder::HasManagedEntryPoint() const +{ + CONTRACTL { + INSTANCE_CHECK; + PRECONDITION(CheckCorHeader()); + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + ULONG flags = GetCorHeader()->Flags; + return (!(flags & VAL32(COMIMAGE_FLAGS_NATIVE_ENTRYPOINT)) && + (!IsNilToken(GetEntryPointToken()))); +} + +ULONG PEDecoder::GetEntryPointToken() const +{ + CONTRACT(ULONG) + { + INSTANCE_CHECK; + PRECONDITION(CheckCorHeader()); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_END; + + RETURN VAL32(IMAGE_COR20_HEADER_FIELD(*GetCorHeader(), EntryPointToken)); +} + +IMAGE_COR_VTABLEFIXUP *PEDecoder::GetVTableFixups(COUNT_T *pCount) const +{ + CONTRACT(IMAGE_COR_VTABLEFIXUP *) + { + INSTANCE_CHECK; + PRECONDITION(CheckCorHeader()); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_END; + IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->VTableFixups; + + if (pCount != NULL) + *pCount = VAL32(pDir->Size)/sizeof(IMAGE_COR_VTABLEFIXUP); + + RETURN PTR_IMAGE_COR_VTABLEFIXUP(GetDirectoryData(pDir)); +} + +CHECK PEDecoder::CheckILOnly() const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_CHECK_END; + + if (m_flags & FLAG_IL_ONLY_CHECKED) + CHECK_OK; + + CHECK(CheckCorHeader()); + + + // Allow only verifiable directories. + + static int s_allowedBitmap = + ((1 << (IMAGE_DIRECTORY_ENTRY_IMPORT )) | + (1 << (IMAGE_DIRECTORY_ENTRY_RESOURCE )) | + (1 << (IMAGE_DIRECTORY_ENTRY_SECURITY )) | + (1 << (IMAGE_DIRECTORY_ENTRY_BASERELOC)) | + (1 << (IMAGE_DIRECTORY_ENTRY_DEBUG )) | + (1 << (IMAGE_DIRECTORY_ENTRY_IAT )) | + (1 << (IMAGE_DIRECTORY_ENTRY_COMHEADER))); + + + + + for (UINT32 entry=0; entry<GetNumberOfRvaAndSizes(); ++entry) + { + if (Has32BitNTHeaders()) + CheckBounds(dac_cast<PTR_CVOID>(&GetNTHeaders32()->OptionalHeader), + GetNTHeaders32()->FileHeader.SizeOfOptionalHeader, + dac_cast<PTR_CVOID>(GetNTHeaders32()->OptionalHeader.DataDirectory + entry), + sizeof(IMAGE_DATA_DIRECTORY)); + else + CheckBounds(dac_cast<PTR_CVOID>(&GetNTHeaders64()->OptionalHeader), + GetNTHeaders32()->FileHeader.SizeOfOptionalHeader, + dac_cast<PTR_CVOID>(GetNTHeaders64()->OptionalHeader.DataDirectory + entry), + sizeof(IMAGE_DATA_DIRECTORY)); + + if (HasDirectoryEntry(entry)) + { + CHECK((s_allowedBitmap & (1 << entry)) != 0); + if (entry!=IMAGE_DIRECTORY_ENTRY_SECURITY) //ignored by OS loader + CHECK(CheckDirectoryEntry(entry,IMAGE_SCN_MEM_SHARED,NULL_NOT_OK)); + } + } + if (HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT) || + HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_BASERELOC) || + FindNTHeaders()->OptionalHeader.AddressOfEntryPoint != 0) + { + // When the image is LoadLibrary'd, we whack the import, IAT directories and the entrypoint. We have to relax + // the verification for mapped images. Ideally, we would only do it for a post-LoadLibrary image. + if (!IsMapped() || (HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT) || HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_BASERELOC))) + { + CHECK(CheckILOnlyImportDlls()); + CHECK(CheckILOnlyBaseRelocations()); + } + +#ifdef _TARGET_X86_ + if (!IsMapped()) + { + CHECK(CheckILOnlyEntryPoint()); + } +#endif + } + + // Check some section characteristics + IMAGE_NT_HEADERS *pNT = FindNTHeaders(); + IMAGE_SECTION_HEADER *section = FindFirstSection(pNT); + IMAGE_SECTION_HEADER *sectionEnd = section + VAL16(pNT->FileHeader.NumberOfSections); + while (section < sectionEnd) + { + // Don't allow shared sections for IL-only images + CHECK(!(section->Characteristics & IMAGE_SCN_MEM_SHARED)); + + // Be sure that we have some access to the section. Note that this test assumes that + // execute or write permissions will also let us read the section. If that is found to be an + // incorrect assumption, this will need to be modified. + CHECK((section->Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) != 0); + section++; + } + + // For EXE, check that OptionalHeader.Win32VersionValue is zero. When this value is non-zero, GetVersionEx + // returns PE supplied values, rather than native OS values; the runtime relies on knowing the actual + // OS version. + if (!IsDll()) + { + CHECK(GetWin32VersionValue() == 0); + } + + + const_cast<PEDecoder *>(this)->m_flags |= FLAG_IL_ONLY_CHECKED; + + CHECK_OK; +} + + +CHECK PEDecoder::CheckILOnlyImportDlls() const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_CHECK_END; + + // The only allowed DLL Imports are MscorEE.dll:_CorExeMain,_CorDllMain + +#ifdef _WIN64 + // On win64, when the image is LoadLibrary'd, we whack the import and IAT directories. We have to relax + // the verification for mapped images. Ideally, we would only do it for a post-LoadLibrary image. + if (IsMapped() && !HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT)) + CHECK_OK; +#endif + + CHECK(HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT)); + CHECK(CheckDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT, IMAGE_SCN_MEM_WRITE)); + + // Get the import directory entry + PIMAGE_DATA_DIRECTORY pDirEntryImport = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT); + CHECK(pDirEntryImport != NULL); + PREFIX_ASSUME(pDirEntryImport != NULL); + + // There should be space for 2 entries. (mscoree and NULL) + CHECK(VAL32(pDirEntryImport->Size) >= (2 * sizeof(IMAGE_IMPORT_DESCRIPTOR))); + + // Get the import data + PIMAGE_IMPORT_DESCRIPTOR pID = (PIMAGE_IMPORT_DESCRIPTOR) GetDirectoryData(pDirEntryImport); + CHECK(pID != NULL); + PREFIX_ASSUME(pID != NULL); + + // Entry 0: ILT, Name, IAT must be be non-null. Forwarder, DateTime should be NULL. + CHECK( IMAGE_IMPORT_DESC_FIELD(pID[0], Characteristics) != 0 + && pID[0].TimeDateStamp == 0 + && (pID[0].ForwarderChain == 0 || pID[0].ForwarderChain == static_cast<ULONG>(-1)) + && pID[0].Name != 0 + && pID[0].FirstThunk != 0); + + // Entry 1: must be all nulls. + CHECK( IMAGE_IMPORT_DESC_FIELD(pID[1], Characteristics) == 0 + && pID[1].TimeDateStamp == 0 + && pID[1].ForwarderChain == 0 + && pID[1].Name == 0 + && pID[1].FirstThunk == 0); + + // Ensure the RVA of the name plus its length is valid for this image + UINT nameRVA = VAL32(pID[0].Name); + CHECK(CheckRva(nameRVA, (COUNT_T) sizeof("mscoree.dll"))); + + // Make sure the name is equal to mscoree + CHECK(SString::_stricmp( (char *)GetRvaData(nameRVA), "mscoree.dll") == 0); + + // Check the Hint/Name table. + CHECK(CheckILOnlyImportByNameTable(VAL32(IMAGE_IMPORT_DESC_FIELD(pID[0], OriginalFirstThunk)))); + + // The IAT needs to be checked only for size. + CHECK(CheckRva(VAL32(pID[0].FirstThunk), 2*sizeof(UINT32))); + + CHECK_OK; +} + +CHECK PEDecoder::CheckILOnlyImportByNameTable(RVA rva) const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_CHECK_END; + + // Check if we have enough space to hold 2 DWORDS + CHECK(CheckRva(rva, 2*sizeof(UINT32))); + + UINT32 UNALIGNED *pImportArray = (UINT32 UNALIGNED *) GetRvaData(rva); + + CHECK(GET_UNALIGNED_VAL32(&pImportArray[0]) != 0); + CHECK(GET_UNALIGNED_VAL32(&pImportArray[1]) == 0); + + UINT32 importRVA = GET_UNALIGNED_VAL32(&pImportArray[0]); + + // First bit Set implies Ordinal lookup + CHECK((importRVA & 0x80000000) == 0); + +#define DLL_NAME "_CorDllMain" +#define EXE_NAME "_CorExeMain" + + static_assert_no_msg(sizeof(DLL_NAME) == sizeof(EXE_NAME)); + + // Check if we have enough space to hold 2 bytes + + // _CorExeMain or _CorDllMain and a NULL char + CHECK(CheckRva(importRVA, offsetof(IMAGE_IMPORT_BY_NAME, Name) + sizeof(DLL_NAME))); + + IMAGE_IMPORT_BY_NAME *import = (IMAGE_IMPORT_BY_NAME*) GetRvaData(importRVA); + + CHECK(SString::_stricmp((char *) import->Name, DLL_NAME) == 0 || _stricmp((char *) import->Name, EXE_NAME) == 0); + + CHECK_OK; +} + +#ifdef _TARGET_X86_ +// jmp dword ptr ds:[XXXX] +#define JMP_DWORD_PTR_DS_OPCODE { 0xFF, 0x25 } +#define JMP_DWORD_PTR_DS_OPCODE_SIZE 2 // Size of opcode +#define JMP_SIZE 6 // Size of opcode + operand +#endif + +CHECK PEDecoder::CheckILOnlyBaseRelocations() const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_CHECK_END; + + if (!HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_BASERELOC)) + { + // We require base relocs for dlls. + CHECK(!IsDll()); + + CHECK((FindNTHeaders()->FileHeader.Characteristics & VAL16(IMAGE_FILE_RELOCS_STRIPPED)) != 0); + } + else + { + CHECK((FindNTHeaders()->FileHeader.Characteristics & VAL16(IMAGE_FILE_RELOCS_STRIPPED)) == 0); + + CHECK(CheckDirectoryEntry(IMAGE_DIRECTORY_ENTRY_BASERELOC, IMAGE_SCN_MEM_WRITE)); + + IMAGE_DATA_DIRECTORY *pRelocDir = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_BASERELOC); + + IMAGE_SECTION_HEADER *section = RvaToSection(VAL32(pRelocDir->VirtualAddress)); + CHECK(section != NULL); + CHECK((section->Characteristics & VAL32(IMAGE_SCN_MEM_READ))!=0); + + IMAGE_BASE_RELOCATION *pReloc = (IMAGE_BASE_RELOCATION *) + GetRvaData(VAL32(pRelocDir->VirtualAddress)); + + // 403569: PREfix correctly complained about pReloc being possibly NULL + CHECK(pReloc != NULL); + CHECK(VAL32(pReloc->SizeOfBlock) == VAL32(pRelocDir->Size)); + + UINT16 *pRelocEntry = (UINT16 *) (pReloc + 1); + UINT16 *pRelocEntryEnd = (UINT16 *) ((BYTE *) pReloc + VAL32(pReloc->SizeOfBlock)); + if(FindNTHeaders()->FileHeader.Machine == VAL16(IMAGE_FILE_MACHINE_IA64)) + { + // Exactly 2 Reloc records, both IMAGE_REL_BASED_DIR64 + CHECK(VAL32(pReloc->SizeOfBlock) >= (sizeof(IMAGE_BASE_RELOCATION)+2*sizeof(UINT16))); + CHECK((VAL16(pRelocEntry[0]) & 0xF000) == (IMAGE_REL_BASED_DIR64 << 12)); + pRelocEntry++; + CHECK((VAL16(pRelocEntry[0]) & 0xF000) == (IMAGE_REL_BASED_DIR64 << 12)); + } + else + { + // Only one Reloc record is expected + CHECK(VAL32(pReloc->SizeOfBlock) >= (sizeof(IMAGE_BASE_RELOCATION)+sizeof(UINT16))); + if(FindNTHeaders()->FileHeader.Machine == VAL16(IMAGE_FILE_MACHINE_AMD64)) + CHECK((VAL16(pRelocEntry[0]) & 0xF000) == (IMAGE_REL_BASED_DIR64 << 12)); + else + CHECK((VAL16(pRelocEntry[0]) & 0xF000) == (IMAGE_REL_BASED_HIGHLOW << 12)); + } + + while (++pRelocEntry < pRelocEntryEnd) + { + // NULL padding entries are allowed + CHECK((VAL16(pRelocEntry[0]) & 0xF000) == IMAGE_REL_BASED_ABSOLUTE); + } + } + + CHECK_OK; +} + +#ifdef _TARGET_X86_ +CHECK PEDecoder::CheckILOnlyEntryPoint() const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_CHECK_END; + + CHECK(FindNTHeaders()->OptionalHeader.AddressOfEntryPoint != 0); + + if(FindNTHeaders()->FileHeader.Machine == VAL16(IMAGE_FILE_MACHINE_I386)) + { + // EntryPoint should be a jmp dword ptr ds:[XXXX] instruction. + // XXXX should be RVA of the first and only entry in the IAT. + + CHECK(CheckRva(VAL32(FindNTHeaders()->OptionalHeader.AddressOfEntryPoint), JMP_SIZE)); + + BYTE *stub = (BYTE *) GetRvaData(VAL32(FindNTHeaders()->OptionalHeader.AddressOfEntryPoint)); + + static const BYTE s_DllOrExeMain[] = JMP_DWORD_PTR_DS_OPCODE; + + // 403570: prefix complained about stub being possibly NULL. + // Unsure here. PREFIX_ASSUME might be also correct as indices are + // verified in the above CHECK statement. + CHECK(stub != NULL); + CHECK(memcmp(stub, s_DllOrExeMain, JMP_DWORD_PTR_DS_OPCODE_SIZE) == 0); + + // Verify target of jump - it should be first entry in the IAT. + + PIMAGE_IMPORT_DESCRIPTOR pID = + (PIMAGE_IMPORT_DESCRIPTOR) GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_IMPORT); + + UINT32 va = * (UINT32 *) (stub + JMP_DWORD_PTR_DS_OPCODE_SIZE); + + CHECK(VAL32(pID[0].FirstThunk) == (va - (SIZE_T) GetPreferredBase())); + } + + CHECK_OK; +} +#endif // _TARGET_X86_ + +#ifndef DACCESS_COMPILE + +void PEDecoder::LayoutILOnly(void *base, BOOL allowFullPE) const +{ + CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(allowFullPE || CheckILOnlyFormat()); + PRECONDITION(CheckZeroedMemory(base, VAL32(FindNTHeaders()->OptionalHeader.SizeOfImage))); + // Ideally we would require the layout address to honor the section alignment constraints. + // However, we do have 8K aligned IL only images which we load on 32 bit platforms. In this + // case, we can only guarantee OS page alignment (which after all, is good enough.) + PRECONDITION(CheckAligned((SIZE_T)base, OS_PAGE_SIZE)); + THROWS; + GC_NOTRIGGER; + } + CONTRACT_END; + + // We're going to copy everything first, and write protect what we need to later. + + // First, copy headers + CopyMemory(base, (void *)m_base, VAL32(FindNTHeaders()->OptionalHeader.SizeOfHeaders)); + + // Now, copy all sections to appropriate virtual address + + IMAGE_SECTION_HEADER *sectionStart = IMAGE_FIRST_SECTION(FindNTHeaders()); + IMAGE_SECTION_HEADER *sectionEnd = sectionStart + VAL16(FindNTHeaders()->FileHeader.NumberOfSections); + + IMAGE_SECTION_HEADER *section = sectionStart; + while (section < sectionEnd) + { + // Raw data may be less than section size if tail is zero, but may be more since VirtualSize is + // not padded. + DWORD size = min(VAL32(section->SizeOfRawData), VAL32(section->Misc.VirtualSize)); + + CopyMemory((BYTE *) base + VAL32(section->VirtualAddress), (BYTE *) m_base + VAL32(section->PointerToRawData), size); + + // Note that our memory is zeroed already, so no need to initialize any tail. + + section++; + } + + // Apply write protection to copied headers + DWORD oldProtection; + if (!ClrVirtualProtect((void *) base, VAL32(FindNTHeaders()->OptionalHeader.SizeOfHeaders), + PAGE_READONLY, &oldProtection)) + ThrowLastError(); + + // Finally, apply proper protection to copied sections + section = sectionStart; + while (section < sectionEnd) + { + // Add appropriate page protection. + if ((section->Characteristics & VAL32(IMAGE_SCN_MEM_WRITE)) == 0) + { + if (!ClrVirtualProtect((void *) ((BYTE *)base + VAL32(section->VirtualAddress)), + VAL32(section->Misc.VirtualSize), + PAGE_READONLY, &oldProtection)) + ThrowLastError(); + } + + section++; + } + + RETURN; +} + +#endif // #ifndef DACCESS_COMPILE + +#ifndef FEATURE_PAL +void * PEDecoder::GetWin32Resource(LPCWSTR lpName, LPCWSTR lpType, COUNT_T *pSize /*=NULL*/) const +{ + CONTRACTL { + INSTANCE_CHECK; + PRECONDITION(IsMapped()); + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + if (pSize != NULL) + *pSize = 0; + + HMODULE hModule = (HMODULE) dac_cast<TADDR>(GetBase()); + + // Use the Win32 functions to decode the resources + + HRSRC hResource = WszFindResourceEx(hModule, lpType, lpName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); + if (!hResource) + return NULL; + + HGLOBAL hLoadedResource = ::LoadResource(hModule, hResource); + if (!hLoadedResource) + return NULL; + + PVOID pResource = ::LockResource(hLoadedResource); + if (!pResource) + return NULL; + + if (pSize != NULL) + *pSize = ::SizeofResource(hModule, hResource); + + return pResource; +} +#endif // FEATURE_PAL + +BOOL PEDecoder::HasNativeHeader() const +{ + CONTRACT(BOOL) + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + SO_TOLERANT; + } + CONTRACT_END; + +#ifdef FEATURE_PREJIT + // Pretend that ready-to-run images do not have native header + RETURN (((GetCorHeader()->Flags & VAL32(COMIMAGE_FLAGS_IL_LIBRARY)) != 0) && !HasReadyToRunHeader()); +#else + RETURN FALSE; +#endif +} + +CHECK PEDecoder::CheckNativeHeader() const +{ + CONTRACT_CHECK + { + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + SO_TOLERANT; + } + CONTRACT_CHECK_END; + +#ifdef FEATURE_PREJIT + if (m_flags & FLAG_NATIVE_CHECKED) + CHECK_OK; + + CHECK(CheckCorHeader()); + + CHECK(HasNativeHeader()); + + IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->ManagedNativeHeader; + + CHECK(CheckDirectory(pDir)); + CHECK(VAL32(pDir->Size) == sizeof(CORCOMPILE_HEADER)); + +#if 0 + // We want to be sure to not trigger these checks when loading a native + // image in a retail build + + // And we do not want to trigger these checks in debug builds either to avoid debug/retail behavior + // differences. + + PTR_CORCOMPILE_HEADER pHeader = PTR_CORCOMPILE_HEADER((TADDR)GetDirectoryData(pDir)); + + CHECK(CheckDirectory(&pHeader->EEInfoTable)); + CHECK(pHeader->EEInfoTable.Size == sizeof(CORCOMPILE_EE_INFO_TABLE)); + + CHECK(CheckDirectory(&pHeader->HelperTable, 0, NULL_OK)); + // @todo: verify helper table size + + CHECK(CheckDirectory(&pHeader->ImportSections, 0, NULL_OK)); + // @todo verify import sections + + CHECK(CheckDirectory(&pHeader->ImportTable, 0, NULL_OK)); + // @todo verify import table + + CHECK(CheckDirectory(&pHeader->VersionInfo, 0, NULL_OK)); // no version header for precompiled netmodules + CHECK(pHeader->VersionInfo.Size == 0 + || (pHeader->VersionInfo.Size == sizeof(CORCOMPILE_VERSION_INFO) && + // Sanity check that we are not just pointing to zeroed-out memory + ((CORCOMPILE_VERSION_INFO*)PTR_READ(GetDirectoryData(&pHeader->VersionInfo), sizeof(CORCOMPILE_VERSION_INFO)))->wOSMajorVersion != 0)); + + CHECK(CheckDirectory(&pHeader->Dependencies, 0, NULL_OK)); // no version header for precompiled netmodules + CHECK(pHeader->Dependencies.Size % sizeof(CORCOMPILE_DEPENDENCY) == 0); + + CHECK(CheckDirectory(&pHeader->DebugMap, 0, NULL_OK)); + CHECK(pHeader->DebugMap.Size % sizeof(CORCOMPILE_DEBUG_RID_ENTRY) == 0); + + // GetPersistedModuleImage() + CHECK(CheckDirectory(&pHeader->ModuleImage)); + CHECK(pHeader->ModuleImage.Size > 0); // sizeof(Module) if we knew it here + + CHECK(CheckDirectory(&pHeader->CodeManagerTable)); + CHECK(pHeader->CodeManagerTable.Size == sizeof(CORCOMPILE_CODE_MANAGER_ENTRY)); + + PTR_CORCOMPILE_CODE_MANAGER_ENTRY pEntry = PTR_CORCOMPILE_CODE_MANAGER_ENTRY((TADDR)GetDirectoryData(&pHeader->CodeManagerTable)); + CHECK(CheckDirectory(&pEntry->HotCode, IMAGE_SCN_MEM_WRITE, NULL_OK)); + CHECK(CheckDirectory(&pEntry->Code, IMAGE_SCN_MEM_WRITE, NULL_OK)); + CHECK(CheckDirectory(&pEntry->ColdCode, IMAGE_SCN_MEM_WRITE, NULL_OK)); + + CHECK(CheckDirectory(&pHeader->ProfileDataList, 0, NULL_OK)); + CHECK(pHeader->ProfileDataList.Size >= sizeof(CORCOMPILE_METHOD_PROFILE_LIST) + || pHeader->ProfileDataList.Size == 0); + +#endif + + const_cast<PEDecoder *>(this)->m_flags |= FLAG_NATIVE_CHECKED; + +#else // FEATURE_PREJIT + CHECK(false); +#endif // FEATURE_PREJIT + + CHECK_OK; +} + +READYTORUN_HEADER * PEDecoder::FindReadyToRunHeader() const +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + SO_TOLERANT; + } + CONTRACTL_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->ManagedNativeHeader; + + if (VAL32(pDir->Size) >= sizeof(READYTORUN_HEADER) && CheckDirectory(pDir)) + { + PTR_READYTORUN_HEADER pHeader = PTR_READYTORUN_HEADER((TADDR)GetDirectoryData(pDir)); + if (pHeader->Signature == READYTORUN_SIGNATURE) + { + const_cast<PEDecoder *>(this)->m_pReadyToRunHeader = pHeader; + return pHeader; + } + } + + const_cast<PEDecoder *>(this)->m_flags |= FLAG_HAS_NO_READYTORUN_HEADER; + return NULL; +} + +// +// code:PEDecoder::CheckILMethod and code:PEDecoder::ComputeILMethodSize really belong to +// file:..\inc\corhlpr.cpp. Unfortunately, corhlpr.cpp is public header file that cannot be +// properly DACized and have other dependencies on the rest of the CLR. +// + +typedef DPTR(COR_ILMETHOD_TINY) PTR_COR_ILMETHOD_TINY; +typedef DPTR(COR_ILMETHOD_FAT) PTR_COR_ILMETHOD_FAT; +typedef DPTR(COR_ILMETHOD_SECT_SMALL) PTR_COR_ILMETHOD_SECT_SMALL; +typedef DPTR(COR_ILMETHOD_SECT_FAT) PTR_COR_ILMETHOD_SECT_FAT; + +CHECK PEDecoder::CheckILMethod(RVA rva) +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_CHECK_END; + + // + // Incrementaly validate that the entire IL method body is within the bounds of the image + // + + // We need to have at least the tiny header + CHECK(CheckRva(rva, sizeof(IMAGE_COR_ILMETHOD_TINY))); + + TADDR pIL = GetRvaData(rva); + + PTR_COR_ILMETHOD_TINY pMethodTiny = PTR_COR_ILMETHOD_TINY(pIL); + + if (pMethodTiny->IsTiny()) + { + // Tiny header has no optional sections - we are done. + CHECK(CheckRva(rva, sizeof(IMAGE_COR_ILMETHOD_TINY) + pMethodTiny->GetCodeSize())); + CHECK_OK; + } + + // + // Fat header + // + + CHECK(CheckRva(rva, sizeof(IMAGE_COR_ILMETHOD_FAT))); + + PTR_COR_ILMETHOD_FAT pMethodFat = PTR_COR_ILMETHOD_FAT(pIL); + + CHECK(pMethodFat->IsFat()); + + S_UINT32 codeEnd = S_UINT32(4) * S_UINT32(pMethodFat->GetSize()) + S_UINT32(pMethodFat->GetCodeSize()); + CHECK(!codeEnd.IsOverflow()); + + // Check minimal size of the header + CHECK(pMethodFat->GetSize() >= (sizeof(COR_ILMETHOD_FAT) / 4)); + + CHECK(CheckRva(rva, codeEnd.Value())); + + if (!pMethodFat->More()) + { + CHECK_OK; + } + + // DACized copy of code:COR_ILMETHOD_FAT::GetSect + TADDR pSect = AlignUp(pIL + codeEnd.Value(), 4); + + // + // Optional sections following the code + // + + for (;;) + { + CHECK(CheckRva(rva, UINT32(pSect - pIL) + sizeof(IMAGE_COR_ILMETHOD_SECT_SMALL))); + + PTR_COR_ILMETHOD_SECT_SMALL pSectSmall = PTR_COR_ILMETHOD_SECT_SMALL(pSect); + + UINT32 sectSize; + + if (pSectSmall->IsSmall()) + { + sectSize = pSectSmall->DataSize; + + // Workaround for bug in shipped compilers - see comment in code:COR_ILMETHOD_SECT::DataSize + if ((pSectSmall->Kind & CorILMethod_Sect_KindMask) == CorILMethod_Sect_EHTable) + sectSize = COR_ILMETHOD_SECT_EH_SMALL::Size(sectSize / sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_SMALL)); + } + else + { + CHECK(CheckRva(rva, UINT32(pSect - pIL) + sizeof(IMAGE_COR_ILMETHOD_SECT_FAT))); + + PTR_COR_ILMETHOD_SECT_FAT pSectFat = PTR_COR_ILMETHOD_SECT_FAT(pSect); + + sectSize = pSectFat->GetDataSize(); + + // Workaround for bug in shipped compilers - see comment in code:COR_ILMETHOD_SECT::DataSize + if ((pSectSmall->Kind & CorILMethod_Sect_KindMask) == CorILMethod_Sect_EHTable) + sectSize = COR_ILMETHOD_SECT_EH_FAT::Size(sectSize / sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT)); + } + + // Section has to be non-empty to avoid infinite loop below + CHECK(sectSize > 0); + + S_UINT32 sectEnd = S_UINT32(UINT32(pSect - pIL)) + S_UINT32(sectSize); + CHECK(!sectEnd.IsOverflow()); + + CHECK(CheckRva(rva, sectEnd.Value())); + + if (!pSectSmall->More()) + { + CHECK_OK; + } + + // DACized copy of code:COR_ILMETHOD_FAT::Next + pSect = AlignUp(pIL + sectEnd.Value(), 4); + } +} + +// +// Compute size of IL blob. Assumes that the IL is within the bounds of the image - make sure +// to call code:PEDecoder::CheckILMethod before calling this method. +// +// code:PEDecoder::ComputeILMethodSize is DACized duplicate of code:COR_ILMETHOD_DECODER::GetOnDiskSize. +// code:MethodDesc::GetILHeader contains debug-only check that ensures that both implementations +// are in sync. +// + +SIZE_T PEDecoder::ComputeILMethodSize(TADDR pIL) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } CONTRACTL_END; + + // + // Mirror flow of code:PEDecoder::CheckILMethod, except for the range checks + // + + PTR_COR_ILMETHOD_TINY pMethodTiny = PTR_COR_ILMETHOD_TINY(pIL); + + if (pMethodTiny->IsTiny()) + { + return sizeof(IMAGE_COR_ILMETHOD_TINY) + pMethodTiny->GetCodeSize(); + } + + PTR_COR_ILMETHOD_FAT pMethodFat = PTR_COR_ILMETHOD_FAT(pIL); + + UINT32 codeEnd = 4 * pMethodFat->GetSize() + pMethodFat->GetCodeSize(); + + if (!pMethodFat->More()) + { + return codeEnd; + } + + // DACized copy of code:COR_ILMETHOD_FAT::GetSect + TADDR pSect = AlignUp(pIL + codeEnd, 4); + + for (;;) + { + PTR_COR_ILMETHOD_SECT_SMALL pSectSmall = PTR_COR_ILMETHOD_SECT_SMALL(pSect); + + UINT32 sectSize; + + if (pSectSmall->IsSmall()) + { + sectSize = pSectSmall->DataSize; + + // Workaround for bug in shipped compilers - see comment in code:COR_ILMETHOD_SECT::DataSize + if ((pSectSmall->Kind & CorILMethod_Sect_KindMask) == CorILMethod_Sect_EHTable) + sectSize = COR_ILMETHOD_SECT_EH_SMALL::Size(sectSize / sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_SMALL)); + } + else + { + PTR_COR_ILMETHOD_SECT_FAT pSectFat = PTR_COR_ILMETHOD_SECT_FAT(pSect); + + sectSize = pSectFat->GetDataSize(); + + // Workaround for bug in shipped compilers - see comment in code:COR_ILMETHOD_SECT::DataSize + if ((pSectSmall->Kind & CorILMethod_Sect_KindMask) == CorILMethod_Sect_EHTable) + sectSize = COR_ILMETHOD_SECT_EH_FAT::Size(sectSize / sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT)); + } + + UINT32 sectEnd = UINT32(pSect - pIL) + sectSize; + + if (!pSectSmall->More() || (sectSize == 0)) + { + return sectEnd; + } + + // DACized copy of code:COR_ILMETHOD_FAT::Next + pSect = AlignUp(pIL + sectEnd, 4); + } +} + +// +// GetDebugDirectoryEntry - return the debug directory entry at the specified index +// +// Arguments: +// index The 0-based index of the entry to return. Usually this is just 0, +// but there can be multiple debug directory entries in a PE file. +// +// Return value: +// A pointer to the IMAGE_DEBUG_DIRECTORY in the PE file for the specified index, +// or NULL if it doesn't exist. +// +// Note that callers on untrusted input are required to validate the debug directory +// first by calling CheckDirectoryEntry(IMAGE_DIRECTORY_ENTRY_DEBUG) (possibly +// indirectly via one of the CheckILOnly* functions). +// +PTR_IMAGE_DEBUG_DIRECTORY PEDecoder::GetDebugDirectoryEntry(UINT index) const +{ + CONTRACT(PTR_IMAGE_DEBUG_DIRECTORY) + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACT_END; + + if (!HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_DEBUG)) + { + RETURN NULL; + } + + // Get a pointer to the contents and size of the debug directory + // Also validates (in CHK builds) that this is all within one section, which the + // caller should have already validated if they don't trust the context of this PE file. + COUNT_T cbDebugDir; + TADDR taDebugDir = GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_DEBUG, &cbDebugDir); + + // Check if the specified directory entry exists (based on the size of the directory) + // Note that the directory size should be an even multiple of the entry size, but we + // just round-down because we need to be resiliant (without asserting) to corrupted / + // fuzzed PE files. + UINT cNumEntries = cbDebugDir / sizeof(IMAGE_DEBUG_DIRECTORY); + if (index >= cNumEntries) + { + RETURN NULL; // index out of range + } + + // Get the debug directory entry at the specified index. + PTR_IMAGE_DEBUG_DIRECTORY pDebugEntry = dac_cast<PTR_IMAGE_DEBUG_DIRECTORY>(taDebugDir); + pDebugEntry += index; // offset from the first entry to the requested entry + RETURN pDebugEntry; +} + + +#ifdef FEATURE_PREJIT + +CORCOMPILE_EE_INFO_TABLE *PEDecoder::GetNativeEEInfoTable() const +{ + CONTRACT(CORCOMPILE_EE_INFO_TABLE *) + { + PRECONDITION(CheckNativeHeader()); + NOTHROW; + GC_NOTRIGGER; + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->EEInfoTable; + + // 403523: PREFIX correctly complained here. Fixed GetDirectoryData. + RETURN PTR_CORCOMPILE_EE_INFO_TABLE(GetDirectoryData(pDir)); +} + + +void *PEDecoder::GetNativeHelperTable(COUNT_T *pSize) const +{ + CONTRACT(void *) + { + PRECONDITION(CheckNativeHeader()); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->HelperTable; + + if (pSize != NULL) + *pSize = VAL32(pDir->Size); + + RETURN (void *)GetDirectoryData(pDir); +} + +CORCOMPILE_VERSION_INFO *PEDecoder::GetNativeVersionInfoMaybeNull(bool skipCheckNativeHeader) const +{ + CONTRACT(CORCOMPILE_VERSION_INFO *) + { + PRECONDITION(skipCheckNativeHeader || CheckNativeHeader()); + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->VersionInfo; + + RETURN PTR_CORCOMPILE_VERSION_INFO(GetDirectoryData(pDir)); +} + +CORCOMPILE_VERSION_INFO *PEDecoder::GetNativeVersionInfo() const +{ + CONTRACT(CORCOMPILE_VERSION_INFO *) + { + POSTCONDITION(CheckPointer(RETVAL)); + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACT_END; + + RETURN GetNativeVersionInfoMaybeNull(); +} + +BOOL PEDecoder::HasNativeDebugMap() const +{ + CONTRACT(BOOL) + { + PRECONDITION(CheckNativeHeader()); + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACT_END; + + // 403522: Prefix complained correctly here. + CORCOMPILE_HEADER *pNativeHeader = GetNativeHeader(); + if (!pNativeHeader) + RETURN FALSE; + else + RETURN (VAL32(pNativeHeader->DebugMap.VirtualAddress) != 0); +} + +TADDR PEDecoder::GetNativeDebugMap(COUNT_T *pSize) const +{ + CONTRACT(TADDR) + { + PRECONDITION(CheckNativeHeader()); + PRECONDITION(CheckPointer(pSize, NULL_OK)); + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->DebugMap; + + if (pSize != NULL) + *pSize = VAL32(pDir->Size); + + RETURN (GetDirectoryData(pDir)); +} + +Module *PEDecoder::GetPersistedModuleImage(COUNT_T *pSize) const +{ + CONTRACT(Module *) + { + PRECONDITION(CheckNativeHeader()); + PRECONDITION(CheckPointer(pSize, NULL_OK)); + POSTCONDITION(CheckPointer(RETVAL)); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ModuleImage; + + if (pSize != NULL) + *pSize = VAL32(pDir->Size); + + RETURN (Module *) GetDirectoryData(pDir); +} + +CHECK PEDecoder::CheckNativeHeaderVersion() const +{ + CONTRACT_CHECK + { + PRECONDITION(CheckNativeHeader()); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_CHECK_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->ManagedNativeHeader; + CHECK(VAL32(pDir->Size) == sizeof(CORCOMPILE_HEADER)); + + CORCOMPILE_HEADER *pNativeHeader = GetNativeHeader(); + CHECK(pNativeHeader->Signature == CORCOMPILE_SIGNATURE); + CHECK(pNativeHeader->MajorVersion == CORCOMPILE_MAJOR_VERSION); + CHECK(pNativeHeader->MinorVersion == CORCOMPILE_MINOR_VERSION); + + CHECK_OK; +} + +CORCOMPILE_CODE_MANAGER_ENTRY *PEDecoder::GetNativeCodeManagerTable() const +{ + CONTRACT(CORCOMPILE_CODE_MANAGER_ENTRY *) + { + PRECONDITION(CheckNativeHeader()); + POSTCONDITION(CheckPointer(RETVAL)); + SUPPORTS_DAC; + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->CodeManagerTable; + + RETURN PTR_CORCOMPILE_CODE_MANAGER_ENTRY(GetDirectoryData(pDir)); +} + +PCODE PEDecoder::GetNativeHotCode(COUNT_T * pSize) const +{ + CONTRACT(PCODE) + { + PRECONDITION(CheckNativeHeader()); + PRECONDITION(CheckPointer(pSize, NULL_OK)); + SUPPORTS_DAC; + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetNativeCodeManagerTable()->HotCode; + + if (pSize != NULL) + *pSize = VAL32(pDir->Size); + + RETURN GetDirectoryData(pDir); +} + +PCODE PEDecoder::GetNativeCode(COUNT_T * pSize) const +{ + CONTRACT(PCODE) + { + PRECONDITION(CheckNativeHeader()); + PRECONDITION(CheckPointer(pSize, NULL_OK)); + SUPPORTS_DAC; + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetNativeCodeManagerTable()->Code; + + if (pSize != NULL) + *pSize = VAL32(pDir->Size); + + RETURN GetDirectoryData(pDir); +} + +PCODE PEDecoder::GetNativeColdCode(COUNT_T * pSize) const +{ + CONTRACT(PCODE) + { + PRECONDITION(CheckNativeHeader()); + PRECONDITION(CheckPointer(pSize, NULL_OK)); + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetNativeCodeManagerTable()->ColdCode; + + if (pSize != NULL) + *pSize = VAL32(pDir->Size); + + RETURN GetDirectoryData(pDir); +} + + +CORCOMPILE_METHOD_PROFILE_LIST *PEDecoder::GetNativeProfileDataList(COUNT_T * pSize) const +{ + CONTRACT(CORCOMPILE_METHOD_PROFILE_LIST *) + { + PRECONDITION(CheckNativeHeader()); + PRECONDITION(CheckPointer(pSize, NULL_OK)); + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ProfileDataList; + + if (pSize != NULL) + *pSize = VAL32(pDir->Size); + + RETURN PTR_CORCOMPILE_METHOD_PROFILE_LIST(GetDirectoryData(pDir)); +} + + + +PTR_CVOID PEDecoder::GetNativeManifestMetadata(COUNT_T *pSize) const +{ + CONTRACT(PTR_CVOID) + { + INSTANCE_CHECK; + PRECONDITION(CheckNativeHeader()); + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); // TBD - may not store metadata for IJW + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = GetMetaDataHelper(METADATA_SECTION_MANIFEST); + + if (pSize != NULL) + *pSize = VAL32(pDir->Size); + + RETURN dac_cast<PTR_VOID>(GetDirectoryData(pDir)); +} + +CHECK PEDecoder::CheckNativeImportFromIndex(COUNT_T index) const +{ + CONTRACT_CHECK + { + PRECONDITION(CheckNativeHeader()); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_CHECK_END; + + CHECK_MSG(index >= 0 && index < GetNativeImportTableCount(), "Bad Native Import Index"); + + CHECK_OK; +} + +COUNT_T PEDecoder::GetNativeImportTableCount() const +{ + CONTRACT(COUNT_T) + { + PRECONDITION(CheckNativeHeader()); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ImportTable; + + RETURN (VAL32(pDir->Size) / sizeof(CORCOMPILE_IMPORT_TABLE_ENTRY)); +} + +CORCOMPILE_IMPORT_TABLE_ENTRY *PEDecoder::GetNativeImportFromIndex(COUNT_T index) const +{ + CONTRACT(CORCOMPILE_IMPORT_TABLE_ENTRY *) + { + PRECONDITION(CheckNativeHeader()); + PRECONDITION(CheckNativeImportFromIndex(index)); + POSTCONDITION(CheckPointer(RETVAL)); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ImportTable; + + CORCOMPILE_IMPORT_TABLE_ENTRY *pEntry + = (CORCOMPILE_IMPORT_TABLE_ENTRY *) GetDirectoryData(pDir); + + RETURN pEntry + index; +} + +PTR_CORCOMPILE_IMPORT_SECTION PEDecoder::GetNativeImportSections(COUNT_T *pCount) const +{ + CONTRACT(PTR_CORCOMPILE_IMPORT_SECTION) + { + PRECONDITION(CheckNativeHeader()); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ImportSections; + + if (pCount != NULL) + *pCount = VAL32(pDir->Size) / sizeof(CORCOMPILE_IMPORT_SECTION); + + RETURN dac_cast<PTR_CORCOMPILE_IMPORT_SECTION>(GetDirectoryData(pDir)); +} + +PTR_CORCOMPILE_IMPORT_SECTION PEDecoder::GetNativeImportSectionFromIndex(COUNT_T index) const +{ + CONTRACT(PTR_CORCOMPILE_IMPORT_SECTION) + { + PRECONDITION(CheckNativeHeader()); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ImportSections; + + _ASSERTE(VAL32(pDir->Size) % sizeof(CORCOMPILE_IMPORT_SECTION) == 0); + _ASSERTE(index * sizeof(CORCOMPILE_IMPORT_SECTION) < VAL32(pDir->Size)); + + RETURN dac_cast<PTR_CORCOMPILE_IMPORT_SECTION>(GetDirectoryData(pDir)) + index; +} + +PTR_CORCOMPILE_IMPORT_SECTION PEDecoder::GetNativeImportSectionForRVA(RVA rva) const +{ + CONTRACT(PTR_CORCOMPILE_IMPORT_SECTION) + { + PRECONDITION(CheckNativeHeader()); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ImportSections; + + _ASSERTE(VAL32(pDir->Size) % sizeof(CORCOMPILE_IMPORT_SECTION) == 0); + + PTR_CORCOMPILE_IMPORT_SECTION pSections = dac_cast<PTR_CORCOMPILE_IMPORT_SECTION>(GetDirectoryData(pDir)); + PTR_CORCOMPILE_IMPORT_SECTION pEnd = dac_cast<PTR_CORCOMPILE_IMPORT_SECTION>(dac_cast<TADDR>(pSections) + VAL32(pDir->Size)); + + for (PTR_CORCOMPILE_IMPORT_SECTION pSection = pSections; pSection < pEnd; pSection++) + { + if (rva >= VAL32(pSection->Section.VirtualAddress) && rva < VAL32(pSection->Section.VirtualAddress) + VAL32(pSection->Section.Size)) + RETURN pSection; + } + + RETURN NULL; +} + +TADDR PEDecoder::GetStubsTable(COUNT_T *pSize) const +{ + CONTRACTL { + INSTANCE_CHECK; + PRECONDITION(CheckNativeHeader()); + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->StubsData; + + if (pSize != NULL) + *pSize = VAL32(pDir->Size); + + return (GetDirectoryData(pDir)); +} + +TADDR PEDecoder::GetVirtualSectionsTable(COUNT_T *pSize) const +{ + CONTRACTL { + INSTANCE_CHECK; + PRECONDITION(CheckNativeHeader()); + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->VirtualSectionsTable; + + if (pSize != NULL) + *pSize = VAL32(pDir->Size); + + return (GetDirectoryData(pDir)); +} + +#endif // FEATURE_PREJIT + +// Get the SizeOfStackReserve and SizeOfStackCommit from the PE file that was used to create +// the calling process (.exe file). +void PEDecoder::GetEXEStackSizes(SIZE_T *PE_SizeOfStackReserve, SIZE_T *PE_SizeOfStackCommit) const +{ + CONTRACTL { + PRECONDITION(!IsDll()); // This routine should only be called for EXE files. + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + * PE_SizeOfStackReserve = GetSizeOfStackReserve(); + * PE_SizeOfStackCommit = GetSizeOfStackCommit(); +} + +CHECK PEDecoder::CheckWillCreateGuardPage() const +{ + CONTRACT_CHECK + { + PRECONDITION(CheckNTHeaders()); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_CHECK_END; + + if (!IsDll()) + { + SIZE_T sizeReservedStack = 0; + SIZE_T sizeCommitedStack = 0; + + GetEXEStackSizes(&sizeReservedStack, &sizeCommitedStack); + + CHECK(ThreadWillCreateGuardPage(sizeReservedStack, sizeCommitedStack)); + + } + + CHECK_OK; +} + +BOOL PEDecoder::HasNativeEntryPoint() const +{ + CONTRACTL { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + PRECONDITION(CheckCorHeader()); + } CONTRACTL_END; + + ULONG flags = GetCorHeader()->Flags; + return ((flags & VAL32(COMIMAGE_FLAGS_NATIVE_ENTRYPOINT)) && + (IMAGE_COR20_HEADER_FIELD(*GetCorHeader(), EntryPointToken) != VAL32(0))); +} + +void *PEDecoder::GetNativeEntryPoint() const +{ + CONTRACT (void *) { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + PRECONDITION(CheckCorHeader()); + PRECONDITION(HasNativeEntryPoint()); + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } CONTRACT_END; + + RETURN ((void *) GetRvaData((RVA)VAL32(IMAGE_COR20_HEADER_FIELD(*GetCorHeader(), EntryPointToken)))); +} + +#ifdef DACCESS_COMPILE + +void +PEDecoder::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, + bool enumThis) +{ + SUPPORTS_DAC; + if (enumThis) + { + DAC_ENUM_DTHIS(); + } + + DacEnumMemoryRegion((TADDR)m_base, sizeof(IMAGE_DOS_HEADER)); + m_pNTHeaders.EnumMem(); + m_pCorHeader.EnumMem(); + m_pNativeHeader.EnumMem(); + m_pReadyToRunHeader.EnumMem(); + + if (HasNTHeaders()) + { + // resource file does not have NT Header. + // + // we also need to write out section header. + DacEnumMemoryRegion(dac_cast<TADDR>(FindFirstSection()), sizeof(IMAGE_SECTION_HEADER) * GetNumberOfSections()); + } +} + +#endif // #ifdef DACCESS_COMPILE + +// -------------------------------------------------------------------------------- + +#ifdef _DEBUG + +// This is a stress mode to force DLLs to be relocated. +// This is particularly useful for hardbinding of ngen images as we +// embed pointers into other hardbound ngen dependencies. + +BOOL PEDecoder::GetForceRelocs() +{ + WRAPPER_NO_CONTRACT; + + static ConfigDWORD forceRelocs; + return (forceRelocs.val(CLRConfig::INTERNAL_ForceRelocs) != 0); +} + +BOOL PEDecoder::ForceRelocForDLL(LPCWSTR lpFileName) +{ + // Use static contracts to avoid recursion, as the dynamic contracts + // do WszLoadLibrary(MSCOREE_SHIM_W). +#ifdef _DEBUG + STATIC_CONTRACT_NOTHROW; \ + ANNOTATION_DEBUG_ONLY; \ + STATIC_CONTRACT_CANNOT_TAKE_LOCK; \ + ANNOTATION_FN_SO_NOT_MAINLINE; +#endif + +#if defined(DACCESS_COMPILE) || defined(FEATURE_PAL) + return TRUE; +#else + + CONTRACT_VIOLATION(SOToleranceViolation); + + // Contracts in ConfigDWORD do WszLoadLibrary(MSCOREE_SHIM_W). + // This check prevents recursion. + if (wcsstr(lpFileName, MSCOREE_SHIM_W) != 0) + return TRUE; + + if (!GetForceRelocs()) + return TRUE; + + BOOL fSuccess = FALSE; + PBYTE hndle = NULL; + PEDecoder pe; + void* pPreferredBase; + COUNT_T nVirtualSize; + + HANDLE hFile = WszCreateFile(lpFileName, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + if (hFile == INVALID_HANDLE_VALUE) + goto ErrExit; + + HANDLE hMap = WszCreateFileMapping(hFile, + NULL, + SEC_IMAGE | PAGE_READONLY, + 0, + 0, + NULL); + CloseHandle(hFile); + + if (hMap == NULL) + goto ErrExit; + + hndle = (PBYTE)MapViewOfFile(hMap, + FILE_MAP_READ, + 0, + 0, + 0); + CloseHandle(hMap); + + if (!hndle) + goto ErrExit; + + pe.Init(hndle); + + pPreferredBase = (void*)pe.GetPreferredBase(); + nVirtualSize = pe.GetVirtualSize(); + + UnmapViewOfFile(hndle); + hndle = NULL; + + // Reserve the space so nobody can use it. A potential bug is likely to + // result in a plain AV this way. It is not a good idea to use the original + // mapping for the reservation since since it would lock the file on the disk. + if (!ClrVirtualAlloc(pPreferredBase, nVirtualSize, MEM_RESERVE, PAGE_NOACCESS)) + goto ErrExit; + + fSuccess = TRUE; + +ErrExit: + if (hndle != NULL) + UnmapViewOfFile(hndle); + + return fSuccess; + +#endif // DACCESS_COMPILE || FEATURE_PAL +} + +#endif // _DEBUG + +// +// MethodSectionIterator class is used to iterate hot (or) cold method section in an ngen image. +// Also used to iterate over jitted methods in the code heap +// +MethodSectionIterator::MethodSectionIterator(const void *code, SIZE_T codeSize, + const void *codeTable, SIZE_T codeTableSize) +{ + //For DAC builds,we'll read the table one DWORD at a time. Note that m_code IS + //NOT a host pointer. + m_codeTableStart = PTR_DWORD(TADDR(codeTable)); + m_codeTable = m_codeTableStart; + _ASSERTE((codeTableSize % sizeof(DWORD)) == 0); + m_codeTableEnd = m_codeTableStart + (codeTableSize / sizeof(DWORD)); + m_code = (BYTE *) code; + m_current = NULL; + + + if (m_codeTable < m_codeTableEnd) + { + m_dword = *m_codeTable++; + m_index = 0; + } + else + { + m_index = NIBBLES_PER_DWORD; + } +} + +BOOL MethodSectionIterator::Next() +{ + while (m_codeTable < m_codeTableEnd || m_index < (int)NIBBLES_PER_DWORD) + { + while (m_index++ < (int)NIBBLES_PER_DWORD) + { + int nibble = (m_dword & HIGHEST_NIBBLE_MASK)>>HIGHEST_NIBBLE_BIT; + m_dword <<= NIBBLE_SIZE; + + if (nibble != 0) + { + // We have found a method start + m_current = m_code + ((nibble-1)*CODE_ALIGN); + m_code += BYTES_PER_BUCKET; + return TRUE; + } + + m_code += BYTES_PER_BUCKET; + } + + if (m_codeTable < m_codeTableEnd) + { + m_dword = *m_codeTable++; + m_index = 0; + } + } + return FALSE; +} diff --git a/src/utilcode/peinformation.cpp b/src/utilcode/peinformation.cpp new file mode 100644 index 0000000000..e2261dd70a --- /dev/null +++ b/src/utilcode/peinformation.cpp @@ -0,0 +1,194 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// -------------------------------------------------------------------------------- +// PEInformation.cpp +// + +// -------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "utilcode.h" +#include "peinformation.h" + +#if defined(FEATURE_FUSION) && !defined(DACCESS_COMPILE) + +extern BOOL g_fWow64Process; // Wow64 Process + +PEKIND GetCurrentRealProcessorPEKIND() +{ + PEKIND curProcessorPEKind = TargetNativePEKIND(); + +#ifdef _TARGET_X86_ + if (g_fWow64Process) + { + SYSTEM_INFO si = {0}; + + GetNativeSystemInfo(&si); + switch (si.wProcessorArchitecture) + { + case PROCESSOR_ARCHITECTURE_AMD64: + curProcessorPEKind = peAMD64; + break; + default: + _ASSERTE(FALSE); + curProcessorPEKind = peInvalid; + break; + } + } +#endif // _TARGET_X86_ + + return curProcessorPEKind; +} + +HRESULT RuntimeIsValidAssemblyOnThisPlatform_CheckProcessorArchitecture(PEKIND processorArchitecture, BOOL bForInstall) +{ + LIMITED_METHOD_CONTRACT; + + HRESULT hr = S_OK; + + // MSIL / legacy images always allowed + if (IsPEMSIL(processorArchitecture) || (processorArchitecture == peNone)) + { + goto Exit; + } + else if (IsPE32(processorArchitecture)) + { +#ifdef _TARGET_ARM_ + // ARM can use only native ones + if (processorArchitecture != TargetNativePEKIND()) + { + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + goto Exit; + } + +#else //!_TARGET_ARM_ + //ARM assemblies can be installed only on ARM + if (processorArchitecture == peARM) + { + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + goto Exit; + } +#endif //!_TARGET_ARM_ + + if (bForInstall) + { + goto Exit; + } + else + { + // won't allow bind to x86 while in 64 bit process. + if (!IsProcess32()) + { + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + } + goto Exit; + } + } + // 64 bit images must match processor type + else if(IsPE64(processorArchitecture)) + { + if (!IsProcess32() && (processorArchitecture == TargetNativePEKIND())) + { + goto Exit; + } + else if (bForInstall && (GetCurrentRealProcessorPEKIND() == processorArchitecture)) + { + goto Exit; + } + } + + // Everything else, fails match + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + +Exit: + return hr; +} +#endif // FEATURE_FUSION && !DACCESS_COMPILE + +HRESULT TranslatePEToArchitectureType(CorPEKind CLRPeKind, DWORD dwImageType, PEKIND * pPeKind) +{ + return TranslatePEToArchitectureType(CLRPeKind, dwImageType, 0, pPeKind); +} + +HRESULT TranslatePEToArchitectureType(CorPEKind CLRPeKind, DWORD dwImageType, DWORD dwAssemblyFlags, PEKIND * pPeKind) +{ + HRESULT hr = S_OK; + + _ASSERTE(pPeKind != NULL); + + if (CLRPeKind == peNot) + { // Not a PE. Shouldn't ever get here. + *pPeKind = peInvalid; + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + goto Exit; + } + else if (IsAfPA_NoPlatform(dwAssemblyFlags)) + { + *pPeKind = peNone; + goto Exit; + } + else + { + if ((CLRPeKind & peILonly) && + !(CLRPeKind & pe32Plus) && + !(CLRPeKind & pe32BitRequired) && + (dwImageType == IMAGE_FILE_MACHINE_I386)) + { + // Processor-agnostic (MSIL) + *pPeKind = peMSIL; + } + else if (CLRPeKind & pe32Plus) + { + // 64-bit + + if (CLRPeKind & pe32BitRequired) + { + *pPeKind = peInvalid; + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + goto Exit; + } + + // Regardless of whether ILONLY is set or not, the architecture + // is the machine type. + + if (dwImageType == IMAGE_FILE_MACHINE_IA64) + { + *pPeKind = peIA64; + } + else if (dwImageType == IMAGE_FILE_MACHINE_AMD64) + { + *pPeKind = peAMD64; + } + else + { // We don't support other architectures + *pPeKind = peInvalid; + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + goto Exit; + } + } + else + { + // 32-bit, non-agnostic + + if (dwImageType == IMAGE_FILE_MACHINE_I386) + { + *pPeKind = peI386; + } + else if (dwImageType == IMAGE_FILE_MACHINE_ARMNT) + { + *pPeKind = peARM; + } + else + { // Not supported + *pPeKind = peInvalid; + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + goto Exit; + } + } + } + +Exit: + return hr; +} diff --git a/src/utilcode/perflog.cpp b/src/utilcode/perflog.cpp new file mode 100644 index 0000000000..14e4737d13 --- /dev/null +++ b/src/utilcode/perflog.cpp @@ -0,0 +1,327 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#include "stdafx.h" +#include "perflog.h" +#include "jitperf.h" +#include <limits.h> + +//============================================================================= +// ALL THE PERF LOG CODE IS COMPILED ONLY IF THE ENABLE_PERF_LOG WAS DEFINED. +// ENABLE_PERF_LOGis defined if GOLDEN or DISABLE_PERF_LOG is not defined. +#if defined (ENABLE_PERF_LOG) +//============================================================================= + +//----------------------------------------------------------------------------- +// Widechar strings representing the units in UnitOfMeasure. *** Keep in sync *** +// with the array defined in PerfLog.cpp +const wchar_t * const wszUnitOfMeasureDescr[MAX_UNITS_OF_MEASURE] = +{ + W(""), + W("sec"), + W("Bytes"), + W("KBytes"), + W("KBytes/sec"), + W("cycles") +}; + +//----------------------------------------------------------------------------- +// Widechar strings representing the "direction" property of above units. +// *** Keep in sync *** with the array defined in PerfLog.cpp +// "Direction" property is false if an increase in the value of the counter indicates +// a degrade. +// "Direction" property is true if an increase in the value of the counter indicates +// an improvement. +const wchar_t * const wszIDirection[MAX_UNITS_OF_MEASURE] = +{ + W("false"), + W("false"), + W("false"), + W("false"), + W("true"), + W("false") +}; + +//----------------------------------------------------------------------------- +// Initialize static variables of the PerfLog class. +bool PerfLog::m_perfLogInit = false; +wchar_t PerfLog::m_wszOutStr_1[]; +DWORD PerfLog::m_dwWriteByte = 0; +int PerfLog::m_fLogPerfData = 0; +HANDLE PerfLog::m_hPerfLogFileHandle = 0; +bool PerfLog::m_perfAutomationFormat = false; +bool PerfLog::m_commaSeparatedFormat = false; + +//----------------------------------------------------------------------------- +// Initliaze perf logging. Must be called before calling PERFLOG (x)... +void PerfLog::PerfLogInitialize() +{ + LIMITED_METHOD_CONTRACT; + + // Make sure we are called only once. + if (m_perfLogInit) + { + return; + } + + // First check for special cases: + +#if defined(ENABLE_JIT_PERF) + // Checks the JIT_PERF_OUTPUT env var and sets g_fJitPerfOn. + InitJitPerf(); +#endif + +#ifdef WS_PERF + // Private working set perf stats + InitWSPerf(); +#endif // WS_PERF + + // Put other special cases here. + + // <TODO>@TODO agk: clean this logic a bit</TODO> + // Special cases considered. Now turn on loggin if any of above want logging + // or if PERF_OUTPUT says so. + + wchar_t lpszValue[2]; + // Read the env var PERF_OUTPUT and if set continue. + m_fLogPerfData = WszGetEnvironmentVariable (W("PERF_OUTPUT"), lpszValue, sizeof(lpszValue)/sizeof(lpszValue[0])); + +#if defined(ENABLE_JIT_PERF) + if (!m_fLogPerfData) + { + // Make sure that JIT perf was not requested. + if (!g_fJitPerfOn) + return; + + // JIT perf stats are needed so set the flags also. + m_fLogPerfData = 1; + } +#endif + + // See if we want to output to the database + wchar_t _lpszValue[11]; + DWORD _cchValue = 10; // 11 - 1 + _cchValue = WszGetEnvironmentVariable (W("PerfOutput"), _lpszValue, _cchValue); + if (_cchValue && (wcscmp (_lpszValue, W("DBase")) == 0)) + m_perfAutomationFormat = true; + if (_cchValue && (wcscmp (_lpszValue, W("CSV")) == 0)) + m_commaSeparatedFormat = true; + + if (PerfAutomationFormat() || CommaSeparatedFormat()) + { + // Hardcoded file name for spitting the perf auotmation formatted perf data. Open + // the file here for writing and close in PerfLogDone(). + m_hPerfLogFileHandle = WszCreateFile ( +#ifdef PLATFORM_UNIX + L"/tmp/PerfData.dat", +#else + W("C:\\PerfData.dat"), +#endif + GENERIC_WRITE, + FILE_SHARE_WRITE, + 0, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + 0); + + // check return value + if(m_hPerfLogFileHandle == INVALID_HANDLE_VALUE) + { + m_fLogPerfData = 0; + goto ErrExit; + } + + // Make sure we append to the file. <TODO>@TODO agk: Is this necessary?</TODO> + if(SetFilePointer (m_hPerfLogFileHandle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER ) + { + CloseHandle (m_hPerfLogFileHandle); + m_fLogPerfData = 0; + goto ErrExit; + } + } + + m_perfLogInit = true; + +ErrExit: + return; +} + +// Wrap up... +void PerfLog::PerfLogDone() +{ + LIMITED_METHOD_CONTRACT; + +#if defined(ENABLE_JIT_PERF) + DoneJitPerfStats(); +#endif + +#ifdef WS_PERF + // Private working set perf + OutputWSPerfStats(); +#endif // WS_PERF + + if (CommaSeparatedFormat()) + { + if (0 == WriteFile (m_hPerfLogFileHandle, "\n", (DWORD)strlen("\n"), &m_dwWriteByte, NULL)) + printf("ERROR: Could not write to perf log.\n"); + } + + if (PerfLoggingEnabled()) + CloseHandle (m_hPerfLogFileHandle); +} + +void PerfLog::OutToStdout(__in_z const wchar_t *wszName, UnitOfMeasure unit, __in_opt const wchar_t *wszDescr) +{ + LIMITED_METHOD_CONTRACT; + + wchar_t wszOutStr_2[PRINT_STR_LEN]; + + if (wszDescr) + _snwprintf_s(wszOutStr_2, PRINT_STR_LEN, PRINT_STR_LEN - 1, W(" (%s)\n"), wszDescr); + else + _snwprintf_s(wszOutStr_2, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("\n")); + + printf("%S", m_wszOutStr_1); + printf("%S", wszOutStr_2); +} + +void PerfLog::OutToPerfFile(__in_z const wchar_t *wszName, UnitOfMeasure unit, __in_opt const wchar_t *wszDescr) +{ + LIMITED_METHOD_CONTRACT; + + char szPrintStr[PRINT_STR_LEN]; + + if (CommaSeparatedFormat()) + { + if (WszWideCharToMultiByte (CP_ACP, 0, m_wszOutStr_1, -1, szPrintStr, PRINT_STR_LEN-1, 0, 0) ) { + if (0 == WriteFile (m_hPerfLogFileHandle, szPrintStr, (DWORD)strlen(szPrintStr), &m_dwWriteByte, NULL)) + printf("ERROR: Could not write to perf log.\n"); + } + else + wprintf(W("ERROR: Could not do string conversion.\n")); + } + else + { + wchar_t wszOutStr_2[PRINT_STR_LEN]; + + // workaround. The formats for ExecTime is slightly different from a custom value. + if (wcscmp(wszName, W("ExecTime")) == 0) + _snwprintf_s(wszOutStr_2, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("ExecUnitDescr=%s\nExecIDirection=%s\n"), wszDescr, wszIDirection[unit]); + else + { + if (wszDescr) + _snwprintf_s(wszOutStr_2, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s Descr=%s\n%s Unit Descr=None\n%s IDirection=%s\n"), wszName, wszDescr, wszName, wszName, wszIDirection[unit]); + else + _snwprintf_s(wszOutStr_2, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s Descr=None\n%s Unit Descr=None\n%s IDirection=%s\n"), wszName, wszName, wszName, wszIDirection[unit]); + } + + // Write both pieces to the file. + if(WszWideCharToMultiByte (CP_ACP, 0, m_wszOutStr_1, -1, szPrintStr, PRINT_STR_LEN-1, 0, 0) ) { + if (0 == WriteFile (m_hPerfLogFileHandle, szPrintStr, (DWORD)strlen(szPrintStr), &m_dwWriteByte, NULL)) + printf("ERROR: Could not write to perf log.\n"); + } + else + wprintf(W("ERROR: Could not do string conversion.\n")); + + if(WszWideCharToMultiByte (CP_ACP, 0, wszOutStr_2, -1, szPrintStr, PRINT_STR_LEN-1, 0, 0)) { + if (0 == WriteFile (m_hPerfLogFileHandle, szPrintStr, (DWORD)strlen(szPrintStr), &m_dwWriteByte, NULL)) + printf("ERROR: Could not write to perf log.\n"); + } + else + wprintf(W("ERROR: Could not do string conversion.\n")); + } +} + +// Output stats in pretty print to stdout and perf automation format to file +// handle m_hPerfLogFileHandle +void PerfLog::Log(__in_z const wchar_t *wszName, UINT64 val, UnitOfMeasure unit, __in_opt const wchar_t *wszDescr) +{ + LIMITED_METHOD_CONTRACT; + + // Format the output into two pieces: The first piece is formatted here, rest in OutToStdout. + _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%-30s%12.3I64u %s"), wszName, val, wszUnitOfMeasureDescr[unit]); + OutToStdout (wszName, unit, wszDescr); + + // Format the output into two pieces: The first piece is formatted here, rest in OutToPerfFile + if (CommaSeparatedFormat()) + { + _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s;%0.3I64u;"), wszName, val); + OutToPerfFile (wszName, unit, wszDescr); + } + + if (PerfAutomationFormat()) + { + // workaround, Special case for ExecTime. since the format is slightly different than for custom value. + if (wcscmp(wszName, W("ExecTime")) == 0) + _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s=%0.3I64u\nExecUnit=%s\n"), wszName, val, wszUnitOfMeasureDescr[unit]); + else + _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s=%0.3I64u\n%s Unit=%s\n"), wszName, val, wszName, wszUnitOfMeasureDescr[unit]); + OutToPerfFile (wszName, unit, wszDescr); + } +} + +// Output stats in pretty print to stdout and perf automation format to file +// handle m_hPerfLogFileHandle +void PerfLog::Log(__in_z const wchar_t *wszName, double val, UnitOfMeasure unit, __in_opt const wchar_t *wszDescr) +{ + LIMITED_METHOD_CONTRACT; + + // Format the output into two pieces: The first piece is formatted here, rest in OutToStdout. + _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%-30s%12.3g %s"), wszName, val, wszUnitOfMeasureDescr[unit]); + OutToStdout (wszName, unit, wszDescr); + + // Format the output into two pieces: The first piece is formatted here, rest in OutToPerfFile + if (CommaSeparatedFormat()) + { + _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s;%0.3g;"), wszName, val); + OutToPerfFile (wszName, unit, wszDescr); + } + + if (PerfAutomationFormat()) + { + // workaround, Special case for ExecTime. since the format is slightly different than for custom value. + if (wcscmp(wszName, W("ExecTime")) == 0) + _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s=%0.3g\nExecUnit=%s\n"), wszName, val, wszUnitOfMeasureDescr[unit]); + else + _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s=%0.3g\n%s Unit=%s\n"), wszName, val, wszName, wszUnitOfMeasureDescr[unit]); + OutToPerfFile (wszName, unit, wszDescr); + } +} + +// Output stats in pretty print to stdout and perf automation format to file +// handle m_hPerfLogFileHandle +void PerfLog::Log(__in_z const wchar_t *wszName, UINT32 val, UnitOfMeasure unit, __in_opt const wchar_t *wszDescr) +{ + LIMITED_METHOD_CONTRACT; + + // Format the output into two pieces: The first piece is formatted here, rest in OutToStdout. + + _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%-30s%12d %s"), wszName, val, wszUnitOfMeasureDescr[unit]); + OutToStdout (wszName, unit, wszDescr); + + // Format the output into two pieces: The first piece is formatted here, rest in OutToPerfFile + if (CommaSeparatedFormat()) + { + _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s;%d;"), wszName, val); + OutToPerfFile (wszName, unit, wszDescr); + } + + if (PerfAutomationFormat()) + { + // workaround, Special case for ExecTime. since the format is slightly different than for custom value. + if (wcscmp(wszName, W("ExecTime")) == 0) + _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s=%0.3d\nExecUnit=%s\n"), wszName, val, wszUnitOfMeasureDescr[unit]); + else + _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s=%0.3d\n%s Unit=%s\n"), wszName, val, wszName, wszUnitOfMeasureDescr[unit]); + OutToPerfFile (wszName, unit, wszDescr); + } +} + + +//============================================================================= +// ALL THE PERF LOG CODE IS COMPILED ONLY IF THE ENABLE_PERF_LOG WAS DEFINED. +#endif // ENABLE_PERF_LOG +//============================================================================= + diff --git a/src/utilcode/posterror.cpp b/src/utilcode/posterror.cpp new file mode 100644 index 0000000000..0ae48aab71 --- /dev/null +++ b/src/utilcode/posterror.cpp @@ -0,0 +1,404 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// PostErrors.cpp +// +// This module contains the error handling/posting code for the engine. It +// is assumed that all methods may be called by a dispatch client, and therefore +// errors are always posted using IErrorInfo. +// + +//***************************************************************************** +#include "stdafx.h" // Standard header. + +#ifndef FEATURE_UTILCODE_NO_DEPENDENCIES + +#include <utilcode.h> // Utility helpers. +#include <corerror.h> +#include "../dlls/mscorrc/resource.h" +#include "ex.h" + +#include <posterror.h> + +#if !defined(lengthof) +#define lengthof(x) (sizeof(x)/sizeof(x[0])) +#endif + +// Local prototypes. +HRESULT FillErrorInfo(LPCWSTR szMsg, DWORD dwHelpContext); + +//***************************************************************************** +// Function that we'll expose to the outside world to fire off the shutdown method +//***************************************************************************** +#ifdef SHOULD_WE_CLEANUP +void ShutdownCompRC() +{ + CCompRC::ShutdownDefaultResourceDll(); +} +#endif /* SHOULD_WE_CLEANUP */ + +void GetResourceCultureCallbacks( + FPGETTHREADUICULTURENAMES* fpGetThreadUICultureNames, + FPGETTHREADUICULTUREID* fpGetThreadUICultureId) +{ + WRAPPER_NO_CONTRACT; + CCompRC::GetDefaultCallbacks( + fpGetThreadUICultureNames, + fpGetThreadUICultureId + ); +} +//***************************************************************************** +// Set callbacks to get culture info +//***************************************************************************** +void SetResourceCultureCallbacks( + FPGETTHREADUICULTURENAMES fpGetThreadUICultureNames, + FPGETTHREADUICULTUREID fpGetThreadUICultureId // TODO: Don't rely on the LCID, only the name +) +{ + WRAPPER_NO_CONTRACT; + CCompRC::SetDefaultCallbacks( + fpGetThreadUICultureNames, + fpGetThreadUICultureId + ); + +} + +//***************************************************************************** +// Public function to load a resource string +//***************************************************************************** +STDAPI UtilLoadStringRC( + UINT iResourceID, + __out_ecount(iMax) LPWSTR szBuffer, + int iMax, + int bQuiet +) +{ + WRAPPER_NO_CONTRACT; + return UtilLoadResourceString(bQuiet? CCompRC::Optional : CCompRC::Required,iResourceID, szBuffer, iMax); +} + +HRESULT UtilLoadResourceString(CCompRC::ResourceCategory eCategory, UINT iResourceID, __out_ecount (iMax) LPWSTR szBuffer, int iMax) +{ + CONTRACTL + { + DISABLED(NOTHROW); + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACTL_END; + + HRESULT retVal = E_OUTOFMEMORY; + + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); + SString::Startup(); + EX_TRY + { + CCompRC *pResourceDLL = CCompRC::GetDefaultResourceDll(); + + if (pResourceDLL != NULL) + { + retVal = pResourceDLL->LoadString(eCategory, iResourceID, szBuffer, iMax); + } + } + EX_CATCH + { + // Catch any errors and return E_OUTOFMEMORY; + retVal = E_OUTOFMEMORY; + } + EX_END_CATCH(SwallowAllExceptions); + + END_SO_INTOLERANT_CODE; + + return retVal; +} + +#ifdef FEATURE_USE_LCID +STDAPI UtilLoadStringRCEx( + LCID lcid, + UINT iResourceID, + __out_ecount(iMax) LPWSTR szBuffer, + int iMax, + int bQuiet, + int *pcwchUsed +) +{ + CONTRACTL + { + DISABLED(NOTHROW); + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACTL_END; + + HRESULT retVal = E_OUTOFMEMORY; + + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); + EX_TRY + { + SString::Startup(); + CCompRC *pResourceDLL = CCompRC::GetDefaultResourceDll(); + + if (pResourceDLL != NULL) + { + retVal = pResourceDLL->LoadString(bQuiet? CCompRC::Optional : CCompRC::Required,lcid, iResourceID, szBuffer, iMax, pcwchUsed); + } + } + EX_CATCH + { + // Catch any errors and return E_OUTOFMEMORY; + retVal = E_OUTOFMEMORY; + } + EX_END_CATCH(SwallowAllExceptions); + END_SO_INTOLERANT_CODE; + + return retVal; +} +#endif //FEATURE_USE_LCID + +//***************************************************************************** +// Format a Runtime Error message. +//***************************************************************************** +HRESULT __cdecl FormatRuntimeErrorVa( + __inout_ecount(cchMsg) WCHAR *rcMsg, // Buffer into which to format. + ULONG cchMsg, // Size of buffer, characters. + HRESULT hrRpt, // The HR to report. + va_list marker) // Optional args. +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + WCHAR rcBuf[512]; // Resource string. + HRESULT hr; + + // Ensure nul termination. + *rcMsg = W('\0'); + + // If this is one of our errors or if it is simply a resource ID, then grab the error from the rc file. + if ((HRESULT_FACILITY(hrRpt) == FACILITY_URT) || (HIWORD(hrRpt) == 0)) + { + hr = UtilLoadStringRC(LOWORD(hrRpt), rcBuf, NumItems(rcBuf), true); + if (hr == S_OK) + { + _vsnwprintf_s(rcMsg, cchMsg, _TRUNCATE, rcBuf, marker); + } + } + // Otherwise it isn't one of ours, so we need to see if the system can + // find the text for it. + else + { +#ifdef FEATURE_USE_LCID + if (WszFormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + 0, hrRpt, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + rcMsg, cchMsg, 0/*<TODO>@todo: marker</TODO>*/)) +#else + if (WszFormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + 0, hrRpt, 0, + rcMsg, cchMsg, 0/*<TODO>@todo: marker</TODO>*/)) +#endif + { + hr = S_OK; + + // System messages contain a trailing \r\n, which we don't want normally. + size_t iLen = wcslen(rcMsg); + if (iLen > 3 && rcMsg[iLen - 2] == '\r' && rcMsg[iLen - 1] == '\n') + rcMsg[iLen - 2] = '\0'; + } + else + hr = HRESULT_FROM_GetLastError(); + } + + // If we failed to find the message anywhere, then issue a hard coded message. + if (FAILED(hr)) + { + _snwprintf_s(rcMsg, cchMsg, _TRUNCATE, W("Common Language Runtime Internal error: 0x%08x"), hrRpt); + DEBUG_STMT(DbgWriteEx(rcMsg)); + } + + return hrRpt; +} // FormatRuntimeErrorVa + +//***************************************************************************** +// Format a Runtime Error message, varargs. +//***************************************************************************** +HRESULT __cdecl FormatRuntimeError( + __out_ecount(cchMsg) WCHAR *rcMsg, // Buffer into which to format. + ULONG cchMsg, // Size of buffer, characters. + HRESULT hrRpt, // The HR to report. + ...) // Optional args. +{ + WRAPPER_NO_CONTRACT; + va_list marker; // User text. + va_start(marker, hrRpt); + hrRpt = FormatRuntimeErrorVa(rcMsg, cchMsg, hrRpt, marker); + va_end(marker); + return hrRpt; +} + +#ifdef FEATURE_COMINTEROP +//***************************************************************************** +// Create, fill out and set an error info object. Note that this does not fill +// out the IID for the error object; that is done elsewhere. +//***************************************************************************** +HRESULT FillErrorInfo( // Return status. + LPCWSTR szMsg, // Error message. + DWORD dwHelpContext) // Help context. +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + ICreateErrorInfo *pICreateErr; // Error info creation Iface pointer. + IErrorInfo *pIErrInfo = NULL; // The IErrorInfo interface. + HRESULT hr; // Return status. + + // Get the ICreateErrorInfo pointer. + hr = S_OK; + EX_TRY + { + hr = CreateErrorInfo(&pICreateErr); + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + } + EX_END_CATCH(SwallowAllExceptions); + + if (FAILED(hr)) + return (hr); + + // Set message text description. + if (FAILED(hr = pICreateErr->SetDescription((LPWSTR) szMsg))) + goto Exit1; + + // suppress PreFast warning about passing literal string to non-const API. + // This API (ICreateErrorInfo::SetHelpFile) is documented to take a const argument, but + // we can't put const in the signature because it would break existing implementors of + // the API. +#ifdef _PREFAST_ +#pragma prefast(push) +#pragma warning(disable:6298) +#endif + + // Set the help file and help context. + //<TODO>@todo: we don't have a help file yet.</TODO> + if (FAILED(hr = pICreateErr->SetHelpFile(const_cast<wchar_t*>(W("complib.hlp")))) || + FAILED(hr = pICreateErr->SetHelpContext(dwHelpContext))) + goto Exit1; + +#ifdef _PREFAST_ +#pragma prefast(pop) +#endif + + // Get the IErrorInfo pointer. + if (FAILED(hr = pICreateErr->QueryInterface(IID_IErrorInfo, (PVOID *) &pIErrInfo))) + goto Exit1; + + // Save the error and release our local pointers. + { + // If we get here, we have loaded oleaut32.dll. + CONTRACT_VIOLATION(ThrowsViolation); + SetErrorInfo(0L, pIErrInfo); + } + +Exit1: + pICreateErr->Release(); + if (pIErrInfo) { + pIErrInfo->Release(); + } + return hr; +} +#endif // FEATURE_COMINTEROP + +//***************************************************************************** +// This function will post an error for the client. If the LOWORD(hrRpt) can +// be found as a valid error message, then it is loaded and formatted with +// the arguments passed in. If it cannot be found, then the error is checked +// against FormatMessage to see if it is a system error. System errors are +// not formatted so no add'l parameters are required. If any errors in this +// process occur, hrRpt is returned for the client with no error posted. +//***************************************************************************** +extern "C" +HRESULT __cdecl PostErrorVA( // Returned error. + HRESULT hrRpt, // Reported error. + va_list marker) // Error arguments. +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + +#ifdef FEATURE_COMINTEROP + + const DWORD cchMsg = 4096; + WCHAR *rcMsg = (WCHAR*)alloca(cchMsg * sizeof(WCHAR)); // Error message. + HRESULT hr; + + BEGIN_ENTRYPOINT_NOTHROW; + + // Return warnings without text. + if (!FAILED(hrRpt)) + goto ErrExit; + + // If we are already out of memory or out of stack or the thread is in some bad state, + // we don't want throw gasoline on the fire by calling ErrorInfo stuff below (which can + // trigger a delayload of oleaut32.dll). We don't need to embellish transient errors + // so just return this without text. + if (Exception::IsTransient(hrRpt)) + { + goto ErrExit; + } + + // Format the error. + FormatRuntimeErrorVa(rcMsg, cchMsg, hrRpt, marker); + + // Turn the error into a posted error message. If this fails, we still + // return the original error message since a message caused by our error + // handling system isn't going to give you a clue about the original error. + hr = FillErrorInfo(rcMsg, LOWORD(hrRpt)); + _ASSERTE(hr == S_OK); + +ErrExit: + + END_ENTRYPOINT_NOTHROW; + +#endif // FEATURE_COMINTEROP + + return (hrRpt); +} // PostErrorVA + +#endif //!FEATURE_UTILCODE_NO_DEPENDENCIES + +//***************************************************************************** +// This function will post an error for the client. If the LOWORD(hrRpt) can +// be found as a valid error message, then it is loaded and formatted with +// the arguments passed in. If it cannot be found, then the error is checked +// against FormatMessage to see if it is a system error. System errors are +// not formatted so no add'l parameters are required. If any errors in this +// process occur, hrRpt is returned for the client with no error posted. +//***************************************************************************** +extern "C" +HRESULT __cdecl PostError( + HRESULT hrRpt, // Reported error. + ...) // Error arguments. +{ +#ifndef FEATURE_UTILCODE_NO_DEPENDENCIES + WRAPPER_NO_CONTRACT; + va_list marker; // User text. + va_start(marker, hrRpt); + hrRpt = PostErrorVA(hrRpt, marker); + va_end(marker); +#endif //!FEATURE_UTILCODE_NO_DEPENDENCIES + return hrRpt; +} diff --git a/src/utilcode/prettyprintsig.cpp b/src/utilcode/prettyprintsig.cpp new file mode 100644 index 0000000000..928cfbb88c --- /dev/null +++ b/src/utilcode/prettyprintsig.cpp @@ -0,0 +1,1004 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +//***************************************************************************** +// This code supports formatting a method and it's signature in a friendly +// and consistent format. +// +//***************************************************************************** +#include "stdafx.h" +#include "prettyprintsig.h" +#include "utilcode.h" +#include "metadata.h" +#include "corpriv.h" + +/***********************************************************************/ +// Null-terminates the string held in "out" + +static WCHAR* asStringW(CQuickBytes *out) +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return NULL;); + } + CONTRACTL_END + + SIZE_T oldSize = out->Size(); + if (FAILED(out->ReSizeNoThrow(oldSize + 1))) + return 0; + WCHAR * cur = (WCHAR *) ((BYTE *) out->Ptr() + oldSize); + *cur = 0; + return((WCHAR*) out->Ptr()); +} // static WCHAR* asStringW() + +// Null-terminates the string held in "out" + +static CHAR* asStringA(CQuickBytes *out) +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return NULL;); + } + CONTRACTL_END + + SIZE_T oldSize = out->Size(); + if (FAILED(out->ReSizeNoThrow(oldSize + 1))) + return 0; + CHAR * cur = (CHAR *) ((BYTE *) out->Ptr() + oldSize); + *cur = 0; + return((CHAR*) out->Ptr()); +} // static CHAR* asStringA() + +/***********************************************************************/ +// Appends the str to "out" +// The string held in "out" is not NULL-terminated. asStringW() needs to +// be called for the NULL-termination + +static HRESULT appendStrW(CQuickBytes *out, const WCHAR* str) +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + SIZE_T len = wcslen(str) * sizeof(WCHAR); + SIZE_T oldSize = out->Size(); + if (FAILED(out->ReSizeNoThrow(oldSize + len))) + return E_OUTOFMEMORY; + WCHAR * cur = (WCHAR *) ((BYTE *) out->Ptr() + oldSize); + memcpy(cur, str, len); + // Note no trailing null! + return S_OK; +} // static HRESULT appendStrW() + +// Appends the str to "out" +// The string held in "out" is not NULL-terminated. asStringA() needs to +// be called for the NULL-termination + +static HRESULT appendStrA(CQuickBytes *out, const CHAR* str) +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + SIZE_T len = strlen(str) * sizeof(CHAR); + SIZE_T oldSize = out->Size(); + if (FAILED(out->ReSizeNoThrow(oldSize + len))) + return E_OUTOFMEMORY; + CHAR * cur = (CHAR *) ((BYTE *) out->Ptr() + oldSize); + memcpy(cur, str, len); + // Note no trailing null! + return S_OK; +} // static HRESULT appendStrA() + + +static HRESULT appendStrNumW(CQuickBytes *out, int num) +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + WCHAR buff[32]; + swprintf_s(buff, 32, W("%d"), num); + return appendStrW(out, buff); +} // static HRESULT appendStrNumW() + +static HRESULT appendStrNumA(CQuickBytes *out, int num) +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + CHAR buff[32]; + sprintf_s(buff, 32, "%d", num); + return appendStrA(out, buff); +} // static HRESULT appendStrNumA() + +static HRESULT appendStrHexW(CQuickBytes *out, int num) +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + WCHAR buff[32]; + swprintf_s(buff, 32, W("%08X"), num); + return appendStrW(out, buff); +} // static HRESULT appendStrHexW() + +static HRESULT appendStrHexA(CQuickBytes *out, int num) +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + CHAR buff[32]; + sprintf_s(buff, 32, "%08X", num); + return appendStrA(out, buff); +} // static HRESULT appendStrHexA() + +/***********************************************************************/ + +LPCWSTR PrettyPrintSigWorker( + PCCOR_SIGNATURE & typePtr, // type to convert, + size_t typeLen, // length of type + const WCHAR * name, // can be "", the name of the method for this sig + CQuickBytes * out, // where to put the pretty printed string + IMetaDataImport * pIMDI); // Import api to use. + +//***************************************************************************** +//***************************************************************************** +// pretty prints 'type' to the buffer 'out' returns a poitner to the next type, +// or 0 on a format failure + +static PCCOR_SIGNATURE PrettyPrintType( + PCCOR_SIGNATURE typePtr, // type to convert, + size_t typeLen, // Maximum length of the type + CQuickBytes * out, // where to put the pretty printed string + IMetaDataImport * pIMDI) // ptr to IMDInternal class with ComSig +{ + mdToken tk; + const WCHAR * str; + WCHAR rcname[MAX_CLASS_NAME]; + HRESULT hr; + unsigned __int8 elt = *typePtr++; + PCCOR_SIGNATURE typeEnd = typePtr + typeLen; + + switch(elt) + { + case ELEMENT_TYPE_VOID: + str = W("void"); + goto APPEND; + + case ELEMENT_TYPE_BOOLEAN: + str = W("bool"); + goto APPEND; + + case ELEMENT_TYPE_CHAR: + str = W("wchar"); + goto APPEND; + + case ELEMENT_TYPE_I1: + str = W("int8"); + goto APPEND; + + case ELEMENT_TYPE_U1: + str = W("unsigned int8"); + goto APPEND; + + case ELEMENT_TYPE_I2: + str = W("int16"); + goto APPEND; + + case ELEMENT_TYPE_U2: + str = W("unsigned int16"); + goto APPEND; + + case ELEMENT_TYPE_I4: + str = W("int32"); + goto APPEND; + + case ELEMENT_TYPE_U4: + str = W("unsigned int32"); + goto APPEND; + + case ELEMENT_TYPE_I8: + str = W("int64"); + goto APPEND; + + case ELEMENT_TYPE_U8: + str = W("unsigned int64"); + goto APPEND; + + case ELEMENT_TYPE_R4: + str = W("float32"); + goto APPEND; + + case ELEMENT_TYPE_R8: + str = W("float64"); + goto APPEND; + + case ELEMENT_TYPE_U: + str = W("unsigned int"); + goto APPEND; + + case ELEMENT_TYPE_I: + str = W("int"); + goto APPEND; + + case ELEMENT_TYPE_OBJECT: + str = W("class System.Object"); + goto APPEND; + + case ELEMENT_TYPE_STRING: + str = W("class System.String"); + goto APPEND; + + case ELEMENT_TYPE_CANON_ZAPSIG: + str = W("class System.__Canon"); + goto APPEND; + + case ELEMENT_TYPE_TYPEDBYREF: + str = W("refany"); + goto APPEND; + + APPEND: + appendStrW(out, str); + break; + + case ELEMENT_TYPE_VALUETYPE: + str = W("value class "); + goto DO_CLASS; + + case ELEMENT_TYPE_CLASS: + str = W("class "); + goto DO_CLASS; + + DO_CLASS: + typePtr += CorSigUncompressToken(typePtr, &tk); + appendStrW(out, str); + rcname[0] = 0; + str = rcname; + + if (TypeFromToken(tk) == mdtTypeRef) + { + hr = pIMDI->GetTypeRefProps(tk, 0, rcname, NumItems(rcname), 0); + } + else if (TypeFromToken(tk) == mdtTypeDef) + { + hr = pIMDI->GetTypeDefProps(tk, rcname, NumItems(rcname), 0, 0, 0); + } + else + { + _ASSERTE(!"Unknown token type encountered in signature."); + str = W("<UNKNOWN>"); + } + + appendStrW(out, str); + break; + + case ELEMENT_TYPE_SZARRAY: + typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI); + appendStrW(out, W("[]")); + break; + + case ELEMENT_TYPE_ARRAY: + { + typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI); + unsigned rank = CorSigUncompressData(typePtr); + PREFIX_ASSUME(rank <= 0xffffff); + + // <TODO>TODO what is the syntax for the rank 0 case? </TODO> + if (rank == 0) + { + appendStrW(out, W("[??]")); + } + else + { + _ASSERTE(rank != 0); + int* lowerBounds = (int*) _alloca(sizeof(int)*2*rank); + int* sizes = &lowerBounds[rank]; + memset(lowerBounds, 0, sizeof(int)*2*rank); + + unsigned numSizes = CorSigUncompressData(typePtr); + _ASSERTE(numSizes <= rank); + unsigned int i; + for(i =0; i < numSizes; i++) + sizes[i] = CorSigUncompressData(typePtr); + + unsigned numLowBounds = CorSigUncompressData(typePtr); + _ASSERTE(numLowBounds <= rank); + for(i = 0; i < numLowBounds; i++) + lowerBounds[i] = CorSigUncompressData(typePtr); + + appendStrW(out, W("[")); + for(i = 0; i < rank; i++) + { + if (sizes[i] != 0 && lowerBounds[i] != 0) + { + if (lowerBounds[i] == 0) + appendStrNumW(out, sizes[i]); + else + { + appendStrNumW(out, lowerBounds[i]); + appendStrW(out, W("...")); + if (sizes[i] != 0) + appendStrNumW(out, lowerBounds[i] + sizes[i] + 1); + } + } + if (i < rank-1) + appendStrW(out, W(",")); + } + appendStrW(out, W("]")); + } + } + break; + + case ELEMENT_TYPE_MVAR: + appendStrW(out, W("!!")); + appendStrNumW(out, CorSigUncompressData(typePtr)); + break; + + case ELEMENT_TYPE_VAR: + appendStrW(out, W("!")); + appendStrNumW(out, CorSigUncompressData(typePtr)); + break; + + case ELEMENT_TYPE_GENERICINST: + { + typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI); + unsigned ntypars = CorSigUncompressData(typePtr); + appendStrW(out, W("<")); + for (unsigned i = 0; i < ntypars; i++) + { + if (i > 0) + appendStrW(out, W(",")); + typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI); + } + appendStrW(out, W(">")); + } + break; + + case ELEMENT_TYPE_MODULE_ZAPSIG: + appendStrW(out, W("[module#")); + appendStrNumW(out, CorSigUncompressData(typePtr)); + appendStrW(out, W(", token#")); + typePtr += CorSigUncompressToken(typePtr, &tk); + appendStrHexW(out, tk); + appendStrW(out, W("]")); + break; + + case ELEMENT_TYPE_FNPTR: + appendStrW(out, W("fnptr ")); + PrettyPrintSigWorker(typePtr, (typeEnd - typePtr), W(""), out, pIMDI); + break; + + case ELEMENT_TYPE_NATIVE_ARRAY_TEMPLATE_ZAPSIG: + case ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG: + appendStrW(out, W("native ")); + typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI); + break; + + // Modifiers or depedant types + case ELEMENT_TYPE_PINNED: + str = W(" pinned"); + goto MODIFIER; + + case ELEMENT_TYPE_PTR: + str = W("*"); + goto MODIFIER; + + case ELEMENT_TYPE_BYREF: + str = W("&"); + goto MODIFIER; + + MODIFIER: + typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI); + appendStrW(out, str); + break; + + default: + case ELEMENT_TYPE_SENTINEL: + case ELEMENT_TYPE_END: + _ASSERTE(!"Unknown Type"); + return(typePtr); + break; + } + return(typePtr); +} // static PCCOR_SIGNATURE PrettyPrintType() + +//***************************************************************************** +// Converts a com signature to a text signature. +// +// Note that this function DOES NULL terminate the result signature string. +//***************************************************************************** +LPCWSTR PrettyPrintSigLegacy( + PCCOR_SIGNATURE typePtr, // type to convert, + unsigned typeLen, // length of type + const WCHAR * name, // can be "", the name of the method for this sig + CQuickBytes * out, // where to put the pretty printed string + IMetaDataImport * pIMDI) // Import api to use. +{ + return PrettyPrintSigWorker(typePtr, typeLen, name, out, pIMDI); +} // LPCWSTR PrettyPrintSigLegacy() + +LPCWSTR PrettyPrintSigWorker( + PCCOR_SIGNATURE & typePtr, // type to convert, + size_t typeLen, // length of type + const WCHAR * name, // can be "", the name of the method for this sig + CQuickBytes * out, // where to put the pretty printed string + IMetaDataImport * pIMDI) // Import api to use. +{ + out->Shrink(0); + unsigned numTyArgs = 0; + unsigned numArgs; + PCCOR_SIGNATURE typeEnd = typePtr + typeLen; // End of the signature. + + if (name != 0) // 0 means a local var sig + { + // get the calling convention out + unsigned callConv = CorSigUncompressData(typePtr); + + if (isCallConv(callConv, IMAGE_CEE_CS_CALLCONV_FIELD)) + { + PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI); + if (name != 0 && *name != 0) + { + appendStrW(out, W(" ")); + appendStrW(out, name); + } + return(asStringW(out)); + } + + if (callConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) + appendStrW(out, W("instance ")); + + if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + appendStrW(out, W("generic ")); + numTyArgs = CorSigUncompressData(typePtr); + } + + static const WCHAR * const callConvNames[IMAGE_CEE_CS_CALLCONV_MAX] = + { + W(""), + W("unmanaged cdecl "), + W("unmanaged stdcall "), + W("unmanaged thiscall "), + W("unmanaged fastcall "), + W("vararg "), + W("<error> "), + W("<error> "), + W(""), + W(""), + W(""), + W("native vararg ") + }; + + if ((callConv & IMAGE_CEE_CS_CALLCONV_MASK) < IMAGE_CEE_CS_CALLCONV_MAX) + { + appendStrW(out, callConvNames[callConv & IMAGE_CEE_CS_CALLCONV_MASK]); + } + + + numArgs = CorSigUncompressData(typePtr); + // do return type + typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI); + + } + else + { + numArgs = CorSigUncompressData(typePtr); + } + + if (name != 0 && *name != 0) + { + appendStrW(out, W(" ")); + appendStrW(out, name); + } + appendStrW(out, W("(")); + + bool needComma = false; + + while (numArgs) + { + if (typePtr >= typeEnd) + break; + + if (*typePtr == ELEMENT_TYPE_SENTINEL) + { + if (needComma) + appendStrW(out, W(",")); + appendStrW(out, W("...")); + typePtr++; + } + else + { + if (numArgs <= 0) + break; + if (needComma) + appendStrW(out, W(",")); + typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI); + --numArgs; + } + needComma = true; + } + appendStrW(out, W(")")); + return (asStringW(out)); +} // LPCWSTR PrettyPrintSigWorker() + + +// Internal implementation of PrettyPrintSig(). + +HRESULT PrettyPrintSigWorkerInternal( + PCCOR_SIGNATURE & typePtr, // type to convert, + size_t typeLen, // length of type + const CHAR * name, // can be "", the name of the method for this sig + CQuickBytes * out, // where to put the pretty printed string + IMDInternalImport * pIMDI); // Import api to use. + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +//***************************************************************************** +//***************************************************************************** +// pretty prints 'type' to the buffer 'out' returns a poitner to the next type, +// or 0 on a format failure + +__checkReturn +static HRESULT PrettyPrintTypeA( + PCCOR_SIGNATURE &typePtr, // type to convert, + size_t typeLen, // Maximum length of the type. + CQuickBytes *out, // where to put the pretty printed string + IMDInternalImport *pIMDI) // ptr to IMDInternal class with ComSig +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + mdToken tk; // A type's token. + const CHAR *str; // Temporary string. + LPCUTF8 pNS; // A type's namespace. + LPCUTF8 pN; // A type's name. + HRESULT hr; // A result. + + PCCOR_SIGNATURE typeEnd = typePtr + typeLen; // End of the signature. + unsigned __int8 elt = *typePtr++; + + switch(elt) { + case ELEMENT_TYPE_VOID: + str = "void"; + goto APPEND; + + case ELEMENT_TYPE_BOOLEAN: + str = "bool"; + goto APPEND; + + case ELEMENT_TYPE_CHAR: + str = "wchar"; + goto APPEND; + + case ELEMENT_TYPE_I1: + str = "int8"; + goto APPEND; + + case ELEMENT_TYPE_U1: + str = "unsigned int8"; + goto APPEND; + + case ELEMENT_TYPE_I2: + str = "int16"; + goto APPEND; + + case ELEMENT_TYPE_U2: + str = "unsigned int16"; + goto APPEND; + + case ELEMENT_TYPE_I4: + str = "int32"; + goto APPEND; + + case ELEMENT_TYPE_U4: + str = "unsigned int32"; + goto APPEND; + + case ELEMENT_TYPE_I8: + str = "int64"; + goto APPEND; + + case ELEMENT_TYPE_U8: + str = "unsigned int64"; + goto APPEND; + + case ELEMENT_TYPE_R4: + str = "float32"; + goto APPEND; + + case ELEMENT_TYPE_R8: + str = "float64"; + goto APPEND; + + case ELEMENT_TYPE_U: + str = "unsigned int"; + goto APPEND; + + case ELEMENT_TYPE_I: + str = "int"; + goto APPEND; + + case ELEMENT_TYPE_OBJECT: + str = "class System.Object"; + goto APPEND; + + case ELEMENT_TYPE_STRING: + str = "class System.String"; + goto APPEND; + + case ELEMENT_TYPE_CANON_ZAPSIG: + str = "class System.__Canon"; + goto APPEND; + + case ELEMENT_TYPE_TYPEDBYREF: + str = "refany"; + goto APPEND; + + APPEND: + IfFailGo(appendStrA(out, str)); + break; + + case ELEMENT_TYPE_INTERNAL: + void* pMT; + pMT = *((void* UNALIGNED *)typePtr); + typePtr += sizeof(void*); + CHAR tempBuffer[64]; + sprintf_s(tempBuffer, 64, "pMT: %p", pMT); + IfFailGo(appendStrA(out, tempBuffer)); + break; + + case ELEMENT_TYPE_VALUETYPE: + str = "value class "; + goto DO_CLASS; + + case ELEMENT_TYPE_CLASS: + str = "class "; + goto DO_CLASS; + + case ELEMENT_TYPE_CMOD_REQD: + str = "required_modifier "; + goto DO_CLASS; + + case ELEMENT_TYPE_CMOD_OPT: + str = "optional_modifier "; + goto DO_CLASS; + + DO_CLASS: + typePtr += CorSigUncompressToken(typePtr, &tk); + IfFailGo(appendStrA(out, str)); + str = "<UNKNOWN>"; + + if (TypeFromToken(tk) == mdtTypeSpec) + { + ULONG cSig; + PCCOR_SIGNATURE sig; + IfFailGo(pIMDI->GetSigFromToken(tk, &cSig, &sig)); + IfFailGo(PrettyPrintTypeA(sig, cSig, out, pIMDI)); + } + else + { + if (TypeFromToken(tk) == mdtTypeRef) + { + //<TODO>@consider: assembly name?</TODO> + if (FAILED(pIMDI->GetNameOfTypeRef(tk, &pNS, &pN))) + { + pNS = pN = "Invalid TypeRef record"; + } + } + else + { + _ASSERTE(TypeFromToken(tk) == mdtTypeDef); + if (FAILED(pIMDI->GetNameOfTypeDef(tk, &pN, &pNS))) + { + pNS = pN = "Invalid TypeDef record"; + } + } + + if (pNS && *pNS) + { + IfFailGo(appendStrA(out, pNS)); + IfFailGo(appendStrA(out, NAMESPACE_SEPARATOR_STR)); + } + IfFailGo(appendStrA(out, pN)); + } + break; + + case ELEMENT_TYPE_SZARRAY: + IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI)); + IfFailGo(appendStrA(out, "[]")); + break; + + case ELEMENT_TYPE_ARRAY: + { + IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI)); + unsigned rank = CorSigUncompressData(typePtr); + PREFIX_ASSUME(rank <= 0xffffff); + // <TODO>TODO what is the syntax for the rank 0 case? </TODO> + if (rank == 0) + { + IfFailGo(appendStrA(out, "[??]")); + } + else + { + _ASSERTE(rank != 0); + int* lowerBounds = (int*) _alloca(sizeof(int)*2*rank); + int* sizes = &lowerBounds[rank]; + memset(lowerBounds, 0, sizeof(int)*2*rank); + + unsigned numSizes = CorSigUncompressData(typePtr); + _ASSERTE(numSizes <= rank); + unsigned int i; + for(i =0; i < numSizes; i++) + sizes[i] = CorSigUncompressData(typePtr); + + unsigned numLowBounds = CorSigUncompressData(typePtr); + _ASSERTE(numLowBounds <= rank); + for(i = 0; i < numLowBounds; i++) + lowerBounds[i] = CorSigUncompressData(typePtr); + + IfFailGo(appendStrA(out, "[")); + for(i = 0; i < rank; i++) + { + if (sizes[i] != 0 && lowerBounds[i] != 0) + { + if (lowerBounds[i] == 0) + appendStrNumA(out, sizes[i]); + else + { + appendStrNumA(out, lowerBounds[i]); + IfFailGo(appendStrA(out, "...")); + if (sizes[i] != 0) + appendStrNumA(out, lowerBounds[i] + sizes[i] + 1); + } + } + if (i < rank-1) + IfFailGo(appendStrA(out, ",")); + } + IfFailGo(appendStrA(out, "]")); + } + } + break; + + case ELEMENT_TYPE_MVAR: + IfFailGo(appendStrA(out, "!!")); + appendStrNumA(out, CorSigUncompressData(typePtr)); + break; + + case ELEMENT_TYPE_VAR: + IfFailGo(appendStrA(out, "!")); + appendStrNumA(out, CorSigUncompressData(typePtr)); + break; + + case ELEMENT_TYPE_GENERICINST: + { + IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI)); + unsigned ntypars = CorSigUncompressData(typePtr); + IfFailGo(appendStrA(out, "<")); + for (unsigned i = 0; i < ntypars; i++) + { + if (i > 0) + IfFailGo(appendStrA(out, ",")); + IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI)); + } + IfFailGo(appendStrA(out, ">")); + } + break; + + case ELEMENT_TYPE_MODULE_ZAPSIG: + IfFailGo(appendStrA(out, "[module#")); + appendStrNumA(out, CorSigUncompressData(typePtr)); + IfFailGo(appendStrA(out, ", token#")); + typePtr += CorSigUncompressToken(typePtr, &tk); + IfFailGo(appendStrHexA(out, tk)); + IfFailGo(appendStrA(out, "]")); + break; + + case ELEMENT_TYPE_FNPTR: + IfFailGo(appendStrA(out, "fnptr ")); + IfFailGo(PrettyPrintSigWorkerInternal(typePtr, (typeEnd - typePtr), "", out,pIMDI)); + break; + + case ELEMENT_TYPE_NATIVE_ARRAY_TEMPLATE_ZAPSIG: + case ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG: + IfFailGo(appendStrA(out, "native ")); + IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI)); + break; + + // Modifiers or dependent types + case ELEMENT_TYPE_PINNED: + str = " pinned"; + goto MODIFIER; + + case ELEMENT_TYPE_PTR: + str = "*"; + goto MODIFIER; + + case ELEMENT_TYPE_BYREF: + str = "&"; + goto MODIFIER; + + MODIFIER: + IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI)); + IfFailGo(appendStrA(out, str)); + break; + + default: + case ELEMENT_TYPE_SENTINEL: + case ELEMENT_TYPE_END: + hr = E_INVALIDARG; + break; + } + ErrExit: + return hr; +} // PrettyPrintTypeA +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +//***************************************************************************** +// Converts a com signature to a text signature. +// +// Note that this function DOES NULL terminate the result signature string. +//***************************************************************************** +HRESULT PrettyPrintSigInternalLegacy( + PCCOR_SIGNATURE typePtr, // type to convert, + unsigned typeLen, // length of type + const CHAR * name, // can be "", the name of the method for this sig + CQuickBytes * out, // where to put the pretty printed string + IMDInternalImport * pIMDI) // Import api to use. +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + return PrettyPrintSigWorkerInternal(typePtr, typeLen, name, out, pIMDI); +} // HRESULT PrettyPrintSigInternalLegacy() + +HRESULT PrettyPrintSigWorkerInternal( + PCCOR_SIGNATURE & typePtr, // type to convert, + size_t typeLen, // length of type + const CHAR * name, // can be "", the name of the method for this sig + CQuickBytes * out, // where to put the pretty printed string + IMDInternalImport * pIMDI) // Import api to use. +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + HRESULT hr = S_OK; + unsigned numArgs; // Count of arugments to function, or count of local vars. + unsigned numTyArgs = 0; + PCCOR_SIGNATURE typeEnd = typePtr + typeLen; + bool needComma = false; + + out->Shrink(0); + + if (name != 0) // 0 means a local var sig + { + // get the calling convention out + unsigned callConv = CorSigUncompressData(typePtr); + + if (isCallConv(callConv, IMAGE_CEE_CS_CALLCONV_FIELD)) + { + IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI)); + if (name != 0 && *name != 0) + + { + IfFailGo(appendStrA(out, " ")); + IfFailGo(appendStrA(out, name)); + } + goto ErrExit; + } + + if (callConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) + IfFailGo(appendStrA(out, "instance ")); + + if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + IfFailGo(appendStrA(out, "generic ")); + numTyArgs = CorSigUncompressData(typePtr); + } + + static const CHAR* const callConvNames[IMAGE_CEE_CS_CALLCONV_MAX] = + { + "", + "unmanaged cdecl ", + "unmanaged stdcall ", + "unmanaged thiscall ", + "unmanaged fastcall ", + "vararg ", + "<error> ", + "<error> ", + "", + "", + "", + "native vararg " + }; + + if ((callConv & IMAGE_CEE_CS_CALLCONV_MASK) < IMAGE_CEE_CS_CALLCONV_MAX) + { + appendStrA(out, callConvNames[callConv & IMAGE_CEE_CS_CALLCONV_MASK]); + } + + numArgs = CorSigUncompressData(typePtr); + // do return type + IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI)); + } + else + { + numArgs = CorSigUncompressData(typePtr); + } + + if (name != 0 && *name != 0) + { + IfFailGo(appendStrA(out, " ")); + IfFailGo(appendStrA(out, name)); + } + IfFailGo(appendStrA(out, "(")); + + while (numArgs) + { + if (typePtr >= typeEnd) + break; + + if (*typePtr == ELEMENT_TYPE_SENTINEL) + { + if (needComma) + IfFailGo(appendStrA(out, ",")); + IfFailGo(appendStrA(out, "...")); + ++typePtr; + } + else + { + if (needComma) + IfFailGo(appendStrA(out, ",")); + IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI)); + --numArgs; + } + needComma = true; + } + IfFailGo(appendStrA(out, ")")); + if (asStringA(out) == 0) + IfFailGo(E_OUTOFMEMORY); + + ErrExit: + return hr; +} // HRESULT PrettyPrintSigWorkerInternal() diff --git a/src/utilcode/rangetree.cpp b/src/utilcode/rangetree.cpp new file mode 100644 index 0000000000..028ebd9769 --- /dev/null +++ b/src/utilcode/rangetree.cpp @@ -0,0 +1,520 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#include "stdafx.h" + +#include "rangetree.h" + +#ifndef DACCESS_COMPILE + +void RangeTree::Node::Init(SIZE_T rangeStart, SIZE_T rangeEnd + DEBUGARG(DWORD ord)) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + _ASSERTE(rangeEnd >= rangeStart); + + start = rangeStart; + end = rangeEnd; + + mask = GetRangeCommonMask(start, end); + + IsIntermediate(FALSE); +#ifdef _DEBUG + ordinal = ord; +#endif + + children[0] = NULL; + children[1] = NULL; +} + + +RangeTree::RangeTree() : + m_root(NULL), m_pool(sizeof(Node), 16, 16) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + +#ifdef _DEBUG + m_nodeCount = 0; +#endif +} + +RangeTree::Node *RangeTree::Lookup(SIZE_T address) const +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + Node *node = m_root; + + // + // When comparing an address to a node, + // there are 5 possibilities: + // * the node is null - no match + // * the address doesn't contain the prefix m - no match + // * the address is inside the node's range (and necessarily + // contains the prefix m) - match + // * the address is less than the range (and necessarily + // has the prefix m0) - traverse the zero child + // * the address is greater than the range (and necessarily + // has the prefix m1) - traverse the one child + // + + while (node != NULL + && (address < node->start || address >= node->end)) + { + // + // See if the address has prefix m. + // + + if ((address & node->mask) != (node->start & node->mask)) + return NULL; + + // + // Determine which subnode to look in. + // + + node = *node->Child(address); + } + + if (node != NULL && node->IsIntermediate()) + node = NULL; + + return node; +} + +RangeTree::Node *RangeTree::LookupEndInclusive(SIZE_T address) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + // + // Lookup an address which may be the ending range + // of a node. In order for this to make sense, it + // must be the case that address is never the starting + // address of the node. (Otherwise there is an + // ambiguity when 2 nodes are adjacent.) + // + + Node *result = Lookup(address-1); + + if ((result != NULL) && (address >= result->start) + && (address <= result->end)) + return result; + else + return NULL; +} + +HRESULT RangeTree::AddNode(Node *addNode, SIZE_T start, SIZE_T end) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + INJECT_FAULT(return E_OUTOFMEMORY;); + } CONTRACTL_END; + + addNode->Init(start, end DEBUGARG(++m_nodeCount)); + + Node **nodePtr = &m_root; + + while (TRUE) + { + Node *node = *nodePtr; + + // + // See if we can live here + // + + if (node == NULL) + { + *nodePtr = addNode; + return S_OK; + } + + // + // Decide if we are a child of the + // current node, or it is a child + // of us, or neither. + // + + SIZE_T diffBits = start ^ node->start; + + // See if the nodes are disjoint + if (diffBits & (node->mask & addNode->mask)) + { + // We need to construct a intermediate node to be the parent of these two. + + // AddIntermediateNode throws to indicate OOM. We need to either + // propagate this behavior upward or convert the exception here. + CONTRACT_VIOLATION(ThrowsViolation); + *nodePtr = AddIntermediateNode(node, addNode); + // <TODO> data structure is hosed at this point if we get an exception - should + // we undo the operation?</TODO> + + return S_OK; + } + else + { + SIZE_T maskDiff = node->mask ^ addNode->mask; + + if (maskDiff == 0) + { + // Masks are the same size, ranges overlap. + // This must be an intermediate node or we have a problem. + if (!node->IsIntermediate()) + return E_INVALIDARG; + + // Replace the intermediate node with this one. + addNode->children[0] = node->children[0]; + addNode->children[1] = node->children[1]; + *nodePtr = addNode; + FreeIntermediate(node); + + return S_OK; + } + + // Make sure the range doesn't intersect. + if (end > node->start && start < node->end) + return E_INVALIDARG; + + else if (addNode->mask & maskDiff) + { + // added node's mask is bigger - it should be the child + nodePtr = node->Child(start); + } + else + { + // existing node's mask is bigger - it should be the child + *nodePtr = addNode; + nodePtr = addNode->Child(node->start); + addNode = node; + } + } + } +} + +HRESULT RangeTree::RemoveNode(Node *removeNode) +{ + CONTRACTL { + THROWS; + GC_NOTRIGGER; + } CONTRACTL_END; + + Node **nodePtr = &m_root; + + while (TRUE) + { + Node *node = *nodePtr; + + _ASSERTE(node != NULL); + + if (node == removeNode) + { + if (node->children[0] == NULL) + *nodePtr = node->children[1]; + else if (node->children[1] == NULL) + *nodePtr = node->children[0]; + else + { + *nodePtr = AddIntermediateNode(node->children[0], + node->children[1]); + } + + return S_OK; + } + else if (node->IsIntermediate()) + { + if (node->children[0] == removeNode) + { + if (removeNode->children[0] == NULL && removeNode->children[1] == NULL) + { + *nodePtr = node->children[1]; + FreeIntermediate(node); + return S_OK; + } + } + else if (node->children[1] == removeNode) + { + if (removeNode->children[0] == NULL && removeNode->children[1] == NULL) + { + *nodePtr = node->children[0]; + FreeIntermediate(node); + return S_OK; + } + } + } + + nodePtr = node->Child(removeNode->start); + } +} + +void RangeTree::Iterate(IterationCallback pCallback, void *context) +{ + WRAPPER_NO_CONTRACT; + + if (m_root != NULL) + IterateNode(m_root, pCallback, context); +} + +void RangeTree::IterateNode(Node *node, IterationCallback pCallback, void *context) +{ + WRAPPER_NO_CONTRACT; + + if (node->children[0] != NULL) + IterateNode(node->children[0], pCallback, context); + + if (!node->IsIntermediate()) + pCallback(node, context); + + if (node->children[1] != NULL) + IterateNode(node->children[1], pCallback, context); +} + +void RangeTree::IterateRange(SIZE_T start, SIZE_T end, IterationCallback pCallback, void *context) +{ + WRAPPER_NO_CONTRACT; + + if (m_root != NULL) + IterateRangeNode(m_root, start, end, GetRangeCommonMask(start, end), + pCallback, context); +} + +void RangeTree::IterateRangeNode(Node *node, SIZE_T start, SIZE_T end, + SIZE_T mask, IterationCallback pCallback, void *context) +{ + WRAPPER_NO_CONTRACT; + + // Compute which bits are different between the two start ranges + SIZE_T diffBits = start ^ node->start; + + // See if the nodes are disjoint + if (diffBits & (node->mask & mask)) + { + return; + } + else + { + if (node->children[0] != NULL) + IterateRangeNode(node->children[0], start, end, mask, pCallback, context); + + if (!node->IsIntermediate() + && (end > node->start && start < node->end)) + (*pCallback)(node, context); + + if (node->children[1] != NULL) + IterateRangeNode(node->children[1], start, end, mask, pCallback, context); + } +} + + +BOOL RangeTree::Overlaps(SIZE_T start, SIZE_T end) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + if (m_root != NULL) + return OverlapsNode(m_root, start, end, GetRangeCommonMask(start, end)); + else + return FALSE; +} + +BOOL RangeTree::OverlapsNode(Node *node, SIZE_T start, SIZE_T end, SIZE_T mask) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + // + // Decide if we are a child of the + // current node, or it is a child + // of us, or neither. + // + + SIZE_T diffBits = start ^ node->start; + + // See if the nodes are disjoint + if (diffBits & (node->mask & mask)) + { + return FALSE; + } + else + { + if (!node->IsIntermediate() + && (end > node->start && start < node->end)) + return TRUE; + + if (node->children[0] != NULL && OverlapsNode(node->children[0], start, end, mask) + || node->children[1] != NULL && OverlapsNode(node->children[1], start, end, mask)) + return TRUE; + + return FALSE; + } +} + + +void RangeTree::RemoveRange(SIZE_T start, SIZE_T end) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + if (m_root != NULL) + RemoveRangeNode(&m_root, start, end, GetRangeCommonMask(start, end)); +} + + +void RangeTree::RemoveRangeNode(Node **nodePtr, SIZE_T start, SIZE_T end, SIZE_T mask) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + Node *node = *nodePtr; + + // Compute which bits are different between the two start ranges + SIZE_T diffBits = start ^ node->start; + + // See if the nodes are disjoint + if (diffBits & (node->mask & mask)) + { + // do nothing + } + else + { + // First, remove from children + if (node->children[0] != NULL) + RemoveRangeNode(&node->children[0], start, end, mask); + if (node->children[1] != NULL) + RemoveRangeNode(&node->children[1], start, end, mask); + + // Now, remove this node if necessary. + if (node->IsIntermediate()) + { + if (node->children[0] == NULL) + { + *nodePtr = node->children[0]; + FreeIntermediate(node); + } + else if (node->children[1] == NULL) + { + *nodePtr = node->children[1]; + FreeIntermediate(node); + } + } + else if (end > node->start && start < node->end) + { + if (node->children[0] == NULL) + *nodePtr = node->children[1]; + else if (node->children[1] == NULL) + *nodePtr = node->children[0]; + else + { + + // AddIntermediateNode throws to indicate OOM. We need to either + // propagate this behavior upward or convert the exception here. + CONTRACT_VIOLATION(ThrowsViolation); + *nodePtr = AddIntermediateNode(node->children[0], + node->children[1]); + } + } + } +} + + +SIZE_T RangeTree::GetRangeCommonMask(SIZE_T start, SIZE_T end) +{ + LIMITED_METHOD_CONTRACT; + + // Compute which bits are the different + + SIZE_T diff = start ^ end; + + // Fill up bits to the right until all are set + + diff |= (diff>>1); + diff |= (diff>>2); + diff |= (diff>>4); + diff |= (diff>>8); + diff |= (diff>>16); + +#if (POINTER_BITS > 32) + diff |= (diff>>32); +#endif + + // flip bits to form high mask + return ~diff; +} + +RangeTree::Node *RangeTree::AddIntermediateNode(Node *node0, + Node *node1) +{ + CONTRACTL { + THROWS; + GC_NOTRIGGER; + } CONTRACTL_END; + + SIZE_T mask = GetRangeCommonMask(node0->start, + node1->start); + + _ASSERTE((mask & ~node0->mask) == 0); + _ASSERTE((mask & ~node1->mask) == 0); + _ASSERTE((node0->start & mask) == (node1->start & mask)); + _ASSERTE((node0->start & mask) == (node1->start & mask)); + + SIZE_T middle = (node0->start & mask) + (~mask>>1); + + Node *intermediate = AllocateIntermediate(); + + intermediate->start = middle; + intermediate->end = middle+1; + intermediate->mask = mask; + intermediate->IsIntermediate(TRUE); +#ifdef _DEBUG + intermediate->ordinal = ++m_nodeCount; +#endif + + int less = (node0->start < node1->start); + + intermediate->children[!less] = node0; + intermediate->children[less] = node1; + + return intermediate; +} + +RangeTree::Node *RangeTree::AllocateIntermediate() +{ + CONTRACTL { + THROWS; + GC_NOTRIGGER; + } CONTRACTL_END; + + return (RangeTree::Node *) m_pool.AllocateElement(); +} + +void RangeTree::FreeIntermediate(Node *node) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + m_pool.FreeElement(node); +} + +#endif diff --git a/src/utilcode/registrywrapper.cpp b/src/utilcode/registrywrapper.cpp new file mode 100644 index 0000000000..e48fb0941c --- /dev/null +++ b/src/utilcode/registrywrapper.cpp @@ -0,0 +1,360 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// File: registrywrapper.cpp +// + +// +// Wrapper around Win32 Registry Functions allowing redirection of .Net +// Framework root registry location +// +// Notes on Offline Ngen Implementation: +// +// This implementation redirects file accesses to the GAC, NIC, framework directory +// and registry accesses to root store and fusion. +// Essentially, if we open a file or reg key directly from the CLR, we redirect it +// into the mounted VHD specified in the COMPLUS config values. +// +// Terminology: +// Host Machine - The machine running a copy of windows that mounts a VHD to +// compile the assemblies within. This is the build machine in the build lab. +// +// Target Machine - The VHD that gets mounted inside the host. We compile +// native images storing them inside the target. This is the freshly build +// copy of windows in the build lab. +// +// The OS itself pulls open all manner of registry keys and files as side-effects +// of our API calls. Here is a list of things the redirection implementation does +// not cover: +// +// - COM +// We use COM in Ngen to create and communicate with worker processes. In +// order to marshal arguments between ngen and worker, mscoree.tlb is loaded. +// The COM system from the loaded and running OS is used, which means the host +// machine's mscoree.tlb gets loaded for marshalling arguments for the CLR +// running on the target machine. In the next release (4.5) the ngen interfaces +// don't expect to change for existing ngen operations. If new functionality +// is added, new interfaces would be added, but existing ones will not be +// altered since we have a high compat bar with an inplace release. mscoree.tlb +// also contains interfaces for mscoree shim and gchost which again we expect to +// remain compatible in this next product release. In order to fix this, we will +// need support from Windows for using the COM system on another copy of Windows. +// - Registry Accesses under +// - HKLM\software[\Wow6432Node]\policies\microsoft : SQM, Cryptography, MUI, codeidentifiers, appcompat, RPC +// - HKLM\software[\Wow6432Node]\RPC,OLE,COM and under these keys +// - HKLM\Software\Microsoft\Cryptography and under +// - HKLM\Software\Microsoft\SQMClient +// - HKLM\Software[\Wow6432Node]\Microsoft\Windows\Windows Error Reporting\WMR and under +// +// These locations are not accessed directly by the CLR, but looked up by Windows +// as part of other API calls. It is safer that we are accessing these +// on the host machine since they correspond with the actively running copy of +// Windows. If we could somehow redirect these to the target VM, we would have +// Windows 7/2K8 OS looking up its config keys from a Win8 registry. If Windows +// has made incompatible changes here, such as moving the location or redefining +// values, we would break. +// - Accesses from C:\Windows\System32 and C:\Windows\Syswow64 and HKCU +// HKCU does not contain any .Net Framework settings (Microsoft\.NETFramework +// is empty). +// There are various files accessed from C:\Windows\System32 and these are a +// function of the OS loader. We load an executable and it automatically +// pulls in kernel32.dll, for example. This should not be a problem for running +// the CLR, since v4.5 will run on Win2K8, and for offline ngen compilation, we +// will not be using the new Win8 APIs for AppX. We had considered setting +// the PATH to point into the target machine's System32 directory, but the +// Windows team advised us that would break pretty quickly due to Api-sets +// having their version numbers rev'd and the Win2k8 host machine not having +// the ability to load them. +// +// +//***************************************************************************** +#include "stdafx.h" +#include "registrywrapper.h" +#include "clrconfig.h" +#include "strsafe.h" + +#if !defined(FEATURE_UTILCODE_NO_DEPENDENCIES) && !defined(FEATURE_CORECLR) + +static LPCWSTR kRegistryRootKey = W("SOFTWARE\\"); +static const HKEY kRegistryRootHive = HKEY_LOCAL_MACHINE; +static LPCWSTR kWow6432Node = W("Wow6432Node\\"); +static LPCWSTR kMicrosoftDotNetFrameworkPolicy = W("Microsoft\\.NETFramework\\Policy"); +static LPCWSTR wszHKLM = W("HKLM\\"); +static WCHAR * g_registryRoot = NULL; +static volatile bool g_initialized = false; +static const WCHAR BACKSLASH_CHARACTER[] = W("\\"); + +// +// Called the first time we use ClrRegCreateKeyEx or ClrRegOpenKeyEx to determine if the registry redirection +// config value has been set. If so, we parse it into g_registryRoot +// If the config string is mal-formed, we don't set the global variable. +// +HRESULT ParseRegistryRootConfigValue() +{ + if (!IsNgenOffline()) + { + return ERROR_SUCCESS; + } + + CLRConfigStringHolder configValue(CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_RegistryRoot)); + // Since IsNgenOffline returned true, this better not be NULL + if (configValue == NULL) + return ERROR_BAD_ARGUMENTS; + + if (_wcsnicmp(configValue, wszHKLM, wcslen(wszHKLM)) != 0) + { + return ERROR_BAD_ARGUMENTS; + } + + // The rest of the string is the location of the redirected registry key + LPWSTR configValueKey = (LPWSTR)configValue; + configValueKey = _wcsninc(configValueKey, wcslen(wszHKLM)); + + size_t len = wcslen(configValueKey) + 1; + + bool appendBackslash = false; + if (configValueKey[wcslen(configValueKey) - 1] != W('\\')) + { + appendBackslash = true; + len += wcslen(BACKSLASH_CHARACTER); + } + g_registryRoot = new (nothrow) WCHAR[len]; + + if (g_registryRoot == NULL) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + wcscpy_s(g_registryRoot, len, configValueKey); + if (appendBackslash) + { + StringCchCat(g_registryRoot, len, BACKSLASH_CHARACTER); + } + + return ERROR_SUCCESS; +} + +// +// This is our check that the target machine is 32/64bit +// If 32bit only, there will be no Wow6432Node key +// If 64bit, there will be a Wow6432Node key +// +HRESULT CheckWow6432NodeNetFxExists(bool * fResult) +{ + static bool bInitialized = false; + static bool bNodeExists = false; + HRESULT hr = ERROR_SUCCESS; + + if (!bInitialized) + { + size_t redirectedLength = wcslen(g_registryRoot) + wcslen(kWow6432Node) + wcslen(kMicrosoftDotNetFrameworkPolicy) + 1; + LPWSTR lpRedirectedKey = new (nothrow) WCHAR[redirectedLength]; + + if (lpRedirectedKey == NULL) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + wcscpy_s(lpRedirectedKey, redirectedLength, g_registryRoot); + StringCchCat(lpRedirectedKey, redirectedLength, kWow6432Node); + StringCchCat(lpRedirectedKey, redirectedLength, kMicrosoftDotNetFrameworkPolicy); + + HKEY hkey; + hr = RegOpenKeyExW(HKEY_LOCAL_MACHINE, lpRedirectedKey, 0, KEY_READ, &hkey); + bNodeExists = hr == ERROR_SUCCESS; + + if (hr == ERROR_FILE_NOT_FOUND) + { + hr = ERROR_SUCCESS; + } + + if (hkey) + { + RegCloseKey(hkey); + } + bInitialized = true; + } + *fResult = bNodeExists; + return hr; +} + +HRESULT CheckUseWow6432Node(REGSAM samDesired, bool * fResult) +{ + HRESULT hr = ERROR_SUCCESS; + bool fWow6432NodeExists = false; + + hr = CheckWow6432NodeNetFxExists(&fWow6432NodeExists); + if (hr == ERROR_SUCCESS) + { + if (!fWow6432NodeExists) + { + *fResult = false; + } else + { +#if defined(_WIN64) + *fResult = false; +#else + *fResult = true; +#endif + // If KEY_WOW64_64KEY or KEY_WOW64_32KEY are set, override + // If both are set, the actual registry call will fail with ERROR_INVALID_PARAMETER + // so no worries here + if (samDesired & KEY_WOW64_64KEY) + { + *fResult = false; + } else if (samDesired & KEY_WOW64_32KEY) + { + *fResult = true; + } + } + } + return hr; +} + +// +// lpRedirectedKey is allocated and returned from this method. Caller owns the new string and +// should release +// +HRESULT RedirectKey(REGSAM samDesired, HKEY hKey, LPCWSTR lpKey, __deref_out_z LPWSTR * lpRedirectedKey) +{ + if (hKey != kRegistryRootHive || _wcsnicmp(lpKey, kRegistryRootKey, wcslen(kRegistryRootKey)) != 0) + { + return ERROR_SUCCESS; + } + + LPCWSTR lpRootStrippedKey = lpKey + wcslen(kRegistryRootKey); + size_t redirectedLength = wcslen(g_registryRoot) + wcslen(lpRootStrippedKey) + 1; + + bool bUseWow = false; + HRESULT hr = CheckUseWow6432Node(samDesired, &bUseWow); + + if (hr != ERROR_SUCCESS) + { + return hr; + } + + if (bUseWow) + { + redirectedLength += wcslen(kWow6432Node); + } + + *lpRedirectedKey = new (nothrow) WCHAR[redirectedLength]; + + if (*lpRedirectedKey == NULL) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + wcscpy_s(*lpRedirectedKey, redirectedLength, g_registryRoot); + if (bUseWow) + { + StringCchCat(*lpRedirectedKey, redirectedLength, kWow6432Node); + } + + StringCchCat(*lpRedirectedKey, redirectedLength, lpRootStrippedKey); + + return ERROR_SUCCESS; +} + +LONG ClrRegCreateKeyEx( + HKEY hKey, + LPCWSTR lpSubKey, + DWORD Reserved, + __in_opt LPWSTR lpClass, + DWORD dwOptions, + REGSAM samDesired, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + PHKEY phkResult, + LPDWORD lpdwDisposition + ) +{ + HRESULT hr; + if (!g_initialized) + { + hr = ParseRegistryRootConfigValue(); + if (hr != ERROR_SUCCESS) + return hr; + g_initialized = true; + } + + if (g_registryRoot != NULL) + { + NewArrayHolder<WCHAR> lpRedirectedKey(NULL); + hr = RedirectKey(samDesired, hKey, lpSubKey, &lpRedirectedKey); + // We don't want to touch the registry if we're low on memory and can't redirect properly. It could lead + // to use reading and writing to two separate registries and corrupting both in the process + if (hr != ERROR_SUCCESS) + return hr; + LPCWSTR lpKeyToUse = lpRedirectedKey == NULL ? lpSubKey : lpRedirectedKey; + return RegCreateKeyExW(hKey, lpKeyToUse, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition); + } + return RegCreateKeyExW(hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition); +} // ClrRegCreateKeyEx + +LONG ClrRegOpenKeyEx( + HKEY hKey, + LPCWSTR lpSubKey, + DWORD ulOptions, + REGSAM samDesired, + PHKEY phkResult + ) +{ + HRESULT hr; + if (!g_initialized) + { + hr = ParseRegistryRootConfigValue(); + if (hr != ERROR_SUCCESS) + return hr; + g_initialized = true; + } + + if (g_registryRoot != NULL) + { + NewArrayHolder<WCHAR> lpRedirectedKey(NULL); + hr = RedirectKey(samDesired, hKey, lpSubKey, &lpRedirectedKey); + // We don't want to touch the registry if we're low on memory and can't redirect properly. It could lead + // to use reading and writing to two separate registries and corrupting both in the process + if (hr != ERROR_SUCCESS) + return hr; + LPCWSTR lpKeyToUse = lpRedirectedKey == NULL ? lpSubKey : lpRedirectedKey; + return RegOpenKeyExW(hKey, lpKeyToUse, ulOptions, samDesired, phkResult); + } + return RegOpenKeyExW(hKey, lpSubKey, ulOptions, samDesired, phkResult); +} + +// +// Determine whether we're running Offline Ngen for Build Lab based on the settings of several Config Values. +// +bool IsNgenOffline() +{ + static volatile bool fInitialized = false; + static volatile bool fNgenOffline = false; + + if (!fInitialized) + { + REGUTIL::CORConfigLevel kCorConfigLevel = static_cast<REGUTIL::CORConfigLevel>(REGUTIL::COR_CONFIG_ENV); + CLRConfigStringHolder pwzConfigInstallRoot = REGUTIL::GetConfigString_DontUse_(CLRConfig::INTERNAL_InstallRoot, + TRUE /* Prepend Complus */, + kCorConfigLevel, + FALSE /* fUsePerfCache */); + CLRConfigStringHolder pwzConfigNicPath(CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_NicPath)); + CLRConfigStringHolder pwzRegistryRoot(CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_RegistryRoot)); + CLRConfigStringHolder pwzConfigAssemblyPath(CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_AssemblyPath)); + CLRConfigStringHolder pwzConfigAssemblyPath2(CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_AssemblyPath2)); + if (pwzConfigInstallRoot != NULL && + pwzConfigNicPath != NULL && + pwzConfigAssemblyPath != NULL && + pwzConfigAssemblyPath2 != NULL && + pwzRegistryRoot != NULL) + { + fNgenOffline = true; + } + fInitialized = true; + } + + return fNgenOffline; +} + +#endif //!FEATURE_UTILCODE_NO_DEPENDENCIES && !FEATURE_CORECLR diff --git a/src/utilcode/regutil.cpp b/src/utilcode/regutil.cpp new file mode 100644 index 0000000000..6b000288a3 --- /dev/null +++ b/src/utilcode/regutil.cpp @@ -0,0 +1,1496 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// regutil.cpp +// + +// +// This module contains a set of functions that can be used to access the +// registry. +// +//***************************************************************************** + + +#include "stdafx.h" +#include "utilcode.h" +#include "mscoree.h" +#include "sstring.h" + +#define COMPLUS_PREFIX W("COMPlus_") +#define LEN_OF_COMPLUS_PREFIX 8 + +#if (!defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(DEBUG)) && !defined(FEATURE_PAL) +#define ALLOW_REGISTRY +#endif + +#undef WszRegCreateKeyEx +#undef WszRegOpenKeyEx +#undef WszRegOpenKey +#define WszRegCreateKeyEx RegCreateKeyExW +#define WszRegOpenKeyEx RegOpenKeyExW +#define WszRegOpenKey(hKey, wszSubKey, phkRes) RegOpenKeyExW(hKey, wszSubKey, 0, KEY_ALL_ACCESS, phkRes) + +//***************************************************************************** +// Reads from the environment setting +//***************************************************************************** +LPWSTR REGUTIL::EnvGetString(LPCWSTR name, BOOL fPrependCOMPLUS) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + SO_TOLERANT; + CANNOT_TAKE_LOCK; + } + CONTRACTL_END; + + WCHAR buff[64]; + + if(wcslen(name) > (size_t)(64 - 1 - (fPrependCOMPLUS ? LEN_OF_COMPLUS_PREFIX : 0))) + { + return(0); + } + +#ifdef ALLOW_REGISTRY + if (fPrependCOMPLUS) + { + if (!EnvCacheValueNameSeenPerhaps(name)) + return 0; + } +#endif // ALLOW_REGISTRY + + if (fPrependCOMPLUS) + { + wcscpy_s(buff, _countof(buff), COMPLUS_PREFIX); + } + else + { + *buff = 0; + } + + wcscat_s(buff, _countof(buff), name); + + FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value. + + int len = WszGetEnvironmentVariable(buff, 0, 0); + + if (len == 0) + { + return(0); + } + + // If we can't get memory to return the string, then will simply pretend we didn't find it. + WCHAR * ret = new (nothrow) WCHAR [len]; + if (ret != NULL) + { + WszGetEnvironmentVariable(buff, ret, len); + } + + return ret; +} + +#ifdef ALLOW_REGISTRY + +#ifndef FEATURE_CORECLR + +//***************************************************************************** +// Gives the use the ability to turn on/off REGUTIL's ability to read from +// the registry. This is useful in mscoree.dll on startup, in order to avoid +// loading advapi32 and rpcrt4 until we're ready for them +//***************************************************************************** +void REGUTIL::AllowRegistryUse(BOOL fAllowUse) +{ + s_fUseRegistry = fAllowUse; +}// AllowRegistryUse + +#endif // !FEATURE_CORECLR + +#endif // ALLOW_REGISTRY + + +BOOL REGUTIL::UseRegistry() +{ +#if !defined(ALLOW_REGISTRY) + return TRUE; +#else + return s_fUseRegistry; +#endif +}// UseRegistry + +//***************************************************************************** +// Reads a DWORD from the COR configuration according to the level specified +// Returns back defValue if the key cannot be found +//***************************************************************************** +DWORD REGUTIL::GetConfigDWORD_DontUse_(LPCWSTR name, DWORD defValue, CORConfigLevel level, BOOL fPrependCOMPLUS) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + SO_TOLERANT; + CANNOT_TAKE_LOCK; + } + CONTRACTL_END; + + SUPPORTS_DAC_HOST_ONLY; + + ULONGLONG result; + GetConfigInteger(name, defValue, &result, TRUE, level, fPrependCOMPLUS); + + return (DWORD)result; +} + +#define uniwcst(val, endptr, base) (fGetDWORD ? wcstoul(val, endptr, base) : _wcstoui64(val, endptr, base)) + +// +// Look up a dword config value, and write the result to the DWORD passed in by reference. +// +// Return value: +// * E_FAIL if the value is not found. (result is assigned the default value) +// * S_OK if the value is found. (result is assigned the value that was found) +// +// Arguments: +// * info - see file:../inc/CLRConfig.h for details +// * result - Pointer to the output DWORD. +// +// static +HRESULT REGUTIL::GetConfigDWORD_DontUse_(LPCWSTR name, DWORD defValue, __out DWORD * result, CORConfigLevel level, BOOL fPrependCOMPLUS) +{ + ULONGLONG ullResult; + HRESULT hr = GetConfigInteger(name, defValue, &ullResult, TRUE, level, fPrependCOMPLUS); + *result = (DWORD)ullResult; + return hr; +} + +ULONGLONG REGUTIL::GetConfigULONGLONG_DontUse_(LPCWSTR name, ULONGLONG defValue, CORConfigLevel level, BOOL fPrependCOMPLUS) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + SO_TOLERANT; + CANNOT_TAKE_LOCK; + } + CONTRACTL_END; + + SUPPORTS_DAC_HOST_ONLY; + + ULONGLONG result; + GetConfigInteger(name, defValue, &result, FALSE, level, fPrependCOMPLUS); + + return result; +} + +// This function should really be refactored to return the string from the environment and let the caller decide +// what to convert it to; and return the buffer read from the reg call. +// Note for PAL: right now PAL does not have a _wcstoui64 API, so I am temporarily reading in all numbers as +// a 32-bit number. When we have the _wcstoui64 API on MAC we will use uniwcst instead of wcstoul. +HRESULT REGUTIL::GetConfigInteger(LPCWSTR name, ULONGLONG defValue, __out ULONGLONG * result, BOOL fGetDWORD, CORConfigLevel level, BOOL fPrependCOMPLUS) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + SO_TOLERANT; + CANNOT_TAKE_LOCK; + } + CONTRACTL_END; + + SUPPORTS_DAC_HOST_ONLY; + + ULONGLONG rtn; + ULONGLONG ret = 0; + DWORD type = 0; + HKEY userKey; + HKEY machineKey; + DWORD size = 4; + + FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value. + + if (level & COR_CONFIG_ENV) + { + WCHAR* val = EnvGetString(name, fPrependCOMPLUS); // try getting it from the environement first + if (val != 0) { + errno = 0; + LPWSTR endPtr; + rtn = uniwcst(val, &endPtr, 16); // treat it has hex + BOOL fSuccess = ((errno != ERANGE) && (endPtr != val)); + delete[] val; + + if (fSuccess) // success + { + *result = rtn; + return (S_OK); + } + } + } + + // Early out if no registry access, simplifies following code. + // + if (!UseRegistry() || !(level & COR_CONFIG_REGISTRY)) + { + *result = defValue; + return (E_FAIL); + } + +#ifdef ALLOW_REGISTRY + // Probe the config cache to see if there is any point + // probing the registry; if not, don't bother. + // + if (!RegCacheValueNameSeenPerhaps(name)) + { + *result = defValue; + return (E_FAIL); + } +#endif // ALLOW_REGISTRY + + if (level & COR_CONFIG_USER) + { +#ifdef ALLOW_REGISTRY + { + LONG retVal = ERROR_SUCCESS; + BOOL bCloseHandle = FALSE; + userKey = s_hUserFrameworkKey; + + if (userKey == INVALID_HANDLE_VALUE) + { + retVal = WszRegOpenKeyEx(HKEY_CURRENT_USER, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &userKey); + bCloseHandle = TRUE; + } + + if (retVal == ERROR_SUCCESS) + { + rtn = WszRegQueryValueEx(userKey, name, 0, &type, (LPBYTE)&ret, &size); + + if (bCloseHandle) + VERIFY(!RegCloseKey(userKey)); + + if (rtn == ERROR_SUCCESS && (type == REG_DWORD || (!fGetDWORD && type == REG_QWORD))) + { + *result = ret; + return (S_OK); + } + } + } +#endif // ALLOW_REGISTRY + } + + if (level & COR_CONFIG_MACHINE) + { +#ifdef ALLOW_REGISTRY + { + LONG retVal = ERROR_SUCCESS; + BOOL bCloseHandle = FALSE; + machineKey = s_hMachineFrameworkKey; + + if (machineKey == INVALID_HANDLE_VALUE) + { + retVal = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &machineKey); + bCloseHandle = TRUE; + } + + if (retVal == ERROR_SUCCESS) + { + rtn = WszRegQueryValueEx(machineKey, name, 0, &type, (LPBYTE)&ret, &size); + + if (bCloseHandle) + VERIFY(!RegCloseKey(machineKey)); + + if (rtn == ERROR_SUCCESS && (type == REG_DWORD || (!fGetDWORD && type == REG_QWORD))) + { + *result = ret; + return (S_OK); + } + } + } +#endif // ALLOW_REGISTRY + } + + *result = defValue; + return (E_FAIL); +} + +#define FUSION_REGISTRY_KEY_W W("Software\\Microsoft\\Fusion") + +//***************************************************************************** +// Reads a string from the COR configuration according to the level specified +// The caller is responsible for deallocating the returned string by +// calling code:REGUTIL::FreeConfigString or using a code:ConfigStringHolder +//***************************************************************************** + +LPWSTR REGUTIL::GetConfigString_DontUse_(LPCWSTR name, BOOL fPrependCOMPLUS, CORConfigLevel level, BOOL fUsePerfCache) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + } + CONTRACTL_END; + +#ifdef ALLOW_REGISTRY + HRESULT lResult; + RegKeyHolder userKey = NULL; + RegKeyHolder machineKey = NULL; + RegKeyHolder fusionKey = NULL; + DWORD type; + DWORD size; +#endif // ALLOW_REGISTRY + NewArrayHolder<WCHAR> ret(NULL); + + FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value. + + if (level & COR_CONFIG_ENV) + { + ret = EnvGetString(name, fPrependCOMPLUS); // try getting it from the environement first + if (ret != 0) { + if (*ret != 0) + { + ret.SuppressRelease(); + return(ret); + } + ret.Clear(); + } + } + + // Early out if no registry access, simplifies following code. + // + if (!UseRegistry() || !(level & COR_CONFIG_REGISTRY)) + { + return(ret); + } + +#ifdef ALLOW_REGISTRY + // Probe the config cache to see if there is any point + // probing the registry; if not, don't bother. + // + if (fUsePerfCache && !RegCacheValueNameSeenPerhaps(name)) + return ret; +#endif // ALLOW_REGISTRY + + if (level & COR_CONFIG_USER) + { +#ifdef ALLOW_REGISTRY + BOOL bUsingCachedKey = FALSE; + + if (s_hUserFrameworkKey != INVALID_HANDLE_VALUE) + { + bUsingCachedKey = TRUE; + userKey = s_hUserFrameworkKey; + } + + if (bUsingCachedKey || WszRegOpenKeyEx(HKEY_CURRENT_USER, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &userKey) == ERROR_SUCCESS) + { + BOOL bReturn = FALSE; + if (WszRegQueryValueEx(userKey, name, 0, &type, 0, &size) == ERROR_SUCCESS && + type == REG_SZ) + { + ret = (LPWSTR) new (nothrow) BYTE [size]; + if (ret) + { + ret[0] = W('\0'); + lResult = WszRegQueryValueEx(userKey, name, 0, 0, (LPBYTE) ret.GetValue(), &size); + _ASSERTE(lResult == ERROR_SUCCESS); + { + ret.SuppressRelease(); + } + } + bReturn = TRUE; + } + + if (bUsingCachedKey) + userKey.SuppressRelease(); + + if (bReturn) + return ret; + } + +#endif // ALLOW_REGISTRY + } + + if (level & COR_CONFIG_MACHINE) + { +#ifdef ALLOW_REGISTRY + BOOL bUsingCachedKey = FALSE; + + if (s_hMachineFrameworkKey != INVALID_HANDLE_VALUE) + { + bUsingCachedKey = TRUE; + machineKey = s_hMachineFrameworkKey; + } + + if (bUsingCachedKey || WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &machineKey) == ERROR_SUCCESS) + { + BOOL bReturn = FALSE; + if (WszRegQueryValueEx(machineKey, name, 0, &type, 0, &size) == ERROR_SUCCESS && + type == REG_SZ) + { + ret = (LPWSTR) new (nothrow) BYTE [size]; + if (ret) + { + ret[0] = W('\0'); + lResult = WszRegQueryValueEx(machineKey, name, 0, 0, (LPBYTE) ret.GetValue(), &size); + _ASSERTE(lResult == ERROR_SUCCESS); + { + ret.SuppressRelease(); + } + } + bReturn = TRUE; + } + + if (bUsingCachedKey) + machineKey.SuppressRelease(); + + if (bReturn) + return ret; + } + +#endif // ALLOW_REGISTRY + } + + if (level & COR_CONFIG_FUSION) + { +#ifdef ALLOW_REGISTRY + if ((WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, FUSION_REGISTRY_KEY_W, 0, KEY_READ, &fusionKey) == ERROR_SUCCESS) && + (WszRegQueryValueEx(fusionKey, name, 0, &type, 0, &size) == ERROR_SUCCESS) && + type == REG_SZ) + { + ret = (LPWSTR) new (nothrow) BYTE [size]; + if (!ret) + { + return NULL; + } + ret[0] = W('\0'); + lResult = WszRegQueryValueEx(fusionKey, name, 0, 0, (LPBYTE) ret.GetValue(), &size); + _ASSERTE(lResult == ERROR_SUCCESS); + ret.SuppressRelease(); + return(ret); + } +#endif // ALLOW_REGISTRY + } + + return NULL; +} + +//***************************************************************************** +// Deallocation function for code:REGUTIL::GetConfigString_DontUse_ +// +// Notes: +// Use a code:ConfigStringHolder to automatically call this. +//***************************************************************************** +void REGUTIL::FreeConfigString(__in_z LPWSTR str) +{ + LIMITED_METHOD_CONTRACT; + + delete [] str; +} + +//***************************************************************************** +// Reads a BIT flag from the COR configuration according to the level specified +// Returns back defValue if the key cannot be found +//***************************************************************************** +DWORD REGUTIL::GetConfigFlag_DontUse_(LPCWSTR name, DWORD bitToSet, BOOL defValue) +{ + WRAPPER_NO_CONTRACT; + + return(GetConfigDWORD_DontUse_(name, defValue) != 0 ? bitToSet : 0); +} + + +#ifdef ALLOW_REGISTRY + +#ifndef FEATURE_CORECLR + +//***************************************************************************** +// Open's the given key and returns the value desired. If the key or value is +// not found, then the default is returned. +//***************************************************************************** +long REGUTIL::GetLong( // Return value from registry or default. + LPCTSTR szName, // Name of value to get. + long iDefault, // Default value to return if not found. + LPCTSTR szKey, // Name of key, NULL==default. + HKEY hKeyVal) // What key to work on. +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + SO_TOLERANT; + } + CONTRACTL_END; + + long iValue; // The value to read. + DWORD iType; // Type of value to get. + DWORD iSize; // Size of buffer. + HKEY hKey; // Key for the registry entry. + + _ASSERTE(UseRegistry()); + + FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value. + + // Open the key if it is there. + if (ERROR_SUCCESS != WszRegOpenKeyEx(hKeyVal, (szKey) ? szKey : FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &hKey)) + return (iDefault); + + // Read the key value if found. + iType = REG_DWORD; + iSize = sizeof(long); + if (ERROR_SUCCESS != WszRegQueryValueEx(hKey, szName, NULL, + &iType, (LPBYTE)&iValue, &iSize) || iType != REG_DWORD) + iValue = iDefault; + + // We're done with the key now. + VERIFY(!RegCloseKey(hKey)); + return (iValue); +} + + +// Opens or creates desired reg key, then writes iValue +//***************************************************************************** +long REGUTIL::SetOrCreateLong( // Return value from registry or default. + LPCTSTR szName, // Name of value to get. + long iValue, // Value to set. + LPCTSTR szKey, // Name of key, NULL==default. + HKEY hKeyVal) // What key to work on. +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + _ASSERTE(UseRegistry()); + + long lRtn; // Return code. + HKEY hKey; // Key for the registry entry. + + + // Open the key if it is there, else create it + if (WszRegCreateKeyEx(hKeyVal, + (szKey) ? szKey : FRAMEWORK_REGISTRY_KEY_W, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_WRITE, + NULL, + &hKey, + NULL) != ERROR_SUCCESS) + { + return (-1); + } + + // Read the key value if found. + lRtn = WszRegSetValueEx(hKey, szName, NULL, REG_DWORD, (const BYTE *) &iValue, sizeof(DWORD)); + + // We're done with the key now. + VERIFY(!RegCloseKey(hKey)); + return (lRtn); +} + + +//***************************************************************************** +// Open's the given key and returns the value desired. If the key or value is +// not found, then the default is returned. +//***************************************************************************** +long REGUTIL::SetLong( // Return value from registry or default. + LPCTSTR szName, // Name of value to get. + long iValue, // Value to set. + LPCTSTR szKey, // Name of key, NULL==default. + HKEY hKeyVal) // What key to work on. +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + _ASSERTE(UseRegistry()); + + long lRtn; // Return code. + HKEY hKey; // Key for the registry entry. + + // Open the key if it is there. + if (ERROR_SUCCESS != WszRegOpenKey(hKeyVal, (szKey) ? szKey : FRAMEWORK_REGISTRY_KEY_W, &hKey)) + return (-1); + + // Read the key value if found. + lRtn = WszRegSetValueEx(hKey, szName, NULL, REG_DWORD, (const BYTE *) &iValue, sizeof(DWORD)); + + // We're done with the key now. + VERIFY(!RegCloseKey(hKey)); + return (lRtn); +} + +//***************************************************************************** +// Set an entry in the registry of the form: +// HKEY_CLASSES_ROOT\szKey\szSubkey = szValue. If szSubkey or szValue are +// NULL, omit them from the above expression. +//***************************************************************************** +BOOL REGUTIL::SetKeyAndValue( // TRUE or FALSE. + LPCTSTR szKey, // Name of the reg key to set. + LPCTSTR szSubkey, // Optional subkey of szKey. + LPCTSTR szValue) // Optional value for szKey\szSubkey. +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + _ASSERTE(UseRegistry()); + + size_t nLen = _tcslen(szKey) + 1; + if (szSubkey) + nLen += (_tcslen(szSubkey) + 1); + + NewArrayHolder<TCHAR> rcKey = new (nothrow) TCHAR[nLen]; // Buffer for the full key name. + if (rcKey == NULL) + return FALSE; + + HKEY hKey = NULL; // Handle to the new reg key. + + // Init the key with the base key name. + _tcscpy_s(rcKey, nLen, szKey); + + // Append the subkey name (if there is one). + if (szSubkey != NULL) + { + _tcscat_s(rcKey, nLen, _T("\\")); + _tcscat_s(rcKey, nLen, szSubkey); + } + + // Create the registration key. + if (WszRegCreateKeyEx(HKEY_CLASSES_ROOT, rcKey, 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, + &hKey, NULL) != ERROR_SUCCESS) + return(FALSE); + + // Set the value (if there is one). + if (szValue != NULL) + if( WszRegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *) szValue, + (Wszlstrlen(szValue)+1) * sizeof(TCHAR)) != ERROR_SUCCESS ) { + VERIFY(!RegCloseKey(hKey)); + return(FALSE); + } + + VERIFY(!RegCloseKey(hKey)); + return(TRUE); +} + + +//***************************************************************************** +// Delete an entry in the registry of the form: +// HKEY_CLASSES_ROOT\szKey\szSubkey. +//***************************************************************************** +LONG REGUTIL::DeleteKey( // TRUE or FALSE. + LPCTSTR szKey, // Name of the reg key to set. + LPCTSTR szSubkey) // Subkey of szKey. +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + _ASSERTE(UseRegistry()); + + size_t nLen = _tcslen(szKey) + 1; + if (szSubkey) + nLen += (_tcslen(szSubkey) + 1); + + NewArrayHolder<TCHAR> rcKey = new (nothrow) TCHAR[nLen]; // Buffer for the full key name. + if (rcKey == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Init the key with the base key name. + _tcscpy_s(rcKey, nLen, szKey); + + // Append the subkey name (if there is one). + if (szSubkey != NULL) + { + _tcscat_s(rcKey, nLen, _T("\\")); + _tcscat_s(rcKey, nLen, szSubkey); + } + + // Delete the registration key. + return WszRegDeleteKey(HKEY_CLASSES_ROOT, rcKey); +} + + +//***************************************************************************** +// Open the key, create a new keyword and value pair under it. +//***************************************************************************** +BOOL REGUTIL::SetRegValue( // Return status. + LPCTSTR szKeyName, // Name of full key. + LPCTSTR szKeyword, // Name of keyword. + LPCTSTR szValue) // Value of keyword. +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + _ASSERTE(UseRegistry()); + + HKEY hKey; // Handle to the new reg key. + + // Create the registration key. + if (WszRegCreateKeyEx(HKEY_CLASSES_ROOT, szKeyName, 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, + &hKey, NULL) != ERROR_SUCCESS) + return (FALSE); + + // Set the value (if there is one). + if (szValue != NULL) + if( WszRegSetValueEx(hKey, szKeyword, 0, REG_SZ, (BYTE *)szValue, + (Wszlstrlen(szValue)+1) * sizeof(TCHAR)) != ERROR_SUCCESS) { + VERIFY(!RegCloseKey(hKey)); + return(FALSE); + } + + VERIFY(!RegCloseKey(hKey)); + return (TRUE); +} + + +//***************************************************************************** +// Does standard registration of a CoClass with a progid. +//***************************************************************************** +HRESULT REGUTIL::RegisterCOMClass( // Return code. + REFCLSID rclsid, // Class ID. + LPCTSTR szDesc, // Description of the class. + LPCTSTR szProgIDPrefix, // Prefix for progid. + int iVersion, // Version # for progid. + LPCTSTR szClassProgID, // Class progid. + LPCTSTR szThreadingModel, // What threading model to use. + LPCTSTR szModule, // Path to class. + HINSTANCE hInst, // Handle to module being registered + LPCTSTR szAssemblyName, // Optional Assembly, + LPCTSTR szVersion, // Optional Runtime version (directory containing runtime) + BOOL fExternal, // flag - External to mscoree. + BOOL fRelativePath) // flag - Relative path in szModule +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + WCHAR rcCLSID[256]; // CLSID\\szID. + WCHAR rcInproc[_MAX_PATH+64]; // CLSID\\InprocServer32 + WCHAR rcProgID[256]; // szProgIDPrefix.szClassProgID + WCHAR rcIndProgID[256]; // rcProgID.iVersion + WCHAR rcShim[_MAX_PATH]; + HRESULT hr; + + // Format the prog ID values. + VERIFY(_snwprintf_s(rcIndProgID, _countof(rcIndProgID), _TRUNCATE, W("%s.%s"), szProgIDPrefix, szClassProgID)); + + VERIFY(_snwprintf_s(rcProgID, _countof(rcProgID), _TRUNCATE, W("%s.%d"), rcIndProgID, iVersion)); + + // Do the initial portion. + if (FAILED(hr = RegisterClassBase(rclsid, szDesc, rcProgID, rcIndProgID, rcCLSID, NumItems(rcCLSID)))) + return (hr); + + VERIFY(_snwprintf_s(rcInproc, _countof(rcInproc), _TRUNCATE, W("%s\\%s"), rcCLSID, W("InprocServer32"))); + + if (!fExternal){ + SetKeyAndValue(rcCLSID, W("InprocServer32"), szModule); + } + else{ + LPCTSTR pSep = szModule; + if (!fRelativePath && szModule) { + pSep = wcsrchr(szModule, W('\\')); + if(pSep == NULL) + pSep = szModule; + else + pSep++; + } + HMODULE hMod = WszLoadLibrary(W("mscoree.dll")); + if (!hMod) + return E_FAIL; + + DWORD ret; + VERIFY(ret = WszGetModuleFileName(hMod, rcShim, NumItems(rcShim))); + FreeLibrary(hMod); + if( !ret ) + return E_FAIL; + + // Set the server path. + SetKeyAndValue(rcCLSID, W("InprocServer32"), rcShim); + if(pSep) + SetKeyAndValue(rcCLSID, W("Server"), pSep); + + if(szAssemblyName) { + SetRegValue(rcInproc, W("Assembly"), szAssemblyName); + SetRegValue(rcInproc, W("Class"), rcIndProgID); + } + } + + // Set the runtime version, it needs to be passed in from the outside + if(szVersion != NULL) { + LPCTSTR pSep2 = NULL; + LPTSTR pSep1 = const_cast<LPTSTR>(wcsrchr(szVersion, W('\\'))); + if(pSep1 != NULL) { + *pSep1 = '\0'; + pSep2 = wcsrchr(szVersion, W('\\')); + if (!pSep2) + pSep2 = szVersion; + else + pSep2 = pSep2++; // exclude '\\' + } + else + pSep2 = szVersion; + + size_t bufLen = wcslen(rcInproc)+wcslen(pSep2)+2; + WCHAR* rcVersion = new (nothrow) WCHAR[bufLen]; + if(rcVersion==NULL) + return (E_OUTOFMEMORY); + wcscpy_s(rcVersion, bufLen, rcInproc); + wcscat_s(rcVersion, bufLen, W("\\")); + wcscat_s(rcVersion, bufLen, pSep2); + SetRegValue(rcVersion, W("ImplementedInThisVersion"), W("")); + delete[] rcVersion; + + if(pSep1 != NULL) + *pSep1 = W('\\'); + } + + // Add the threading model information. + SetRegValue(rcInproc, W("ThreadingModel"), szThreadingModel); + return (S_OK); +} + + + +//***************************************************************************** +// Does standard registration of a CoClass with a progid. +// NOTE: This is the non-side-by-side execution version. +//***************************************************************************** +HRESULT REGUTIL::RegisterCOMClass( // Return code. + REFCLSID rclsid, // Class ID. + LPCTSTR szDesc, // Description of the class. + LPCTSTR szProgIDPrefix, // Prefix for progid. + int iVersion, // Version # for progid. + LPCTSTR szClassProgID, // Class progid. + LPCTSTR szThreadingModel, // What threading model to use. + LPCTSTR szModule, // Path to class. + BOOL bInprocServer) // Whether we register the server as inproc or local +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + WCHAR rcCLSID[256]; // CLSID\\szID. + WCHAR rcInproc[_MAX_PATH+64]; // CLSID\\InprocServer32 + WCHAR rcProgID[256]; // szProgIDPrefix.szClassProgID + WCHAR rcIndProgID[256]; // rcProgID.iVersion + HRESULT hr; + + // Format the prog ID values. + VERIFY(_snwprintf_s(rcIndProgID, _countof(rcIndProgID), _TRUNCATE, W("%s.%s"), szProgIDPrefix, szClassProgID)); + + VERIFY(_snwprintf_s(rcProgID, _countof(rcProgID), _TRUNCATE, W("%s.%d"), rcIndProgID, iVersion)); + + // Do the initial portion. + if (FAILED(hr = RegisterClassBase(rclsid, szDesc, rcProgID, rcIndProgID, rcCLSID, NumItems(rcCLSID)))) + return (hr); + + WCHAR *szServerType = bInprocServer ? W("InprocServer32") : W("LocalServer32"); + + // Set the server path. + SetKeyAndValue(rcCLSID, szServerType , szModule); + + // Add the threading model information. + VERIFY(_snwprintf_s(rcInproc, _countof(rcInproc), _TRUNCATE, W("%s\\%s"), rcCLSID, szServerType)); + + SetRegValue(rcInproc, W("ThreadingModel"), szThreadingModel); + return (S_OK); +} + + + +//***************************************************************************** +// Register the basics for a in proc server. +//***************************************************************************** +HRESULT REGUTIL::RegisterClassBase( // Return code. + REFCLSID rclsid, // Class ID we are registering. + LPCTSTR szDesc, // Class description. + LPCTSTR szProgID, // Class prog ID. + LPCTSTR szIndepProgID, // Class version independant prog ID. + __out_ecount(cchOutCLSID) LPTSTR szOutCLSID, // CLSID formatted in character form. + DWORD cchOutCLSID) // Out CLS ID buffer size +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + TCHAR szID[64]; // The class ID to register. + + // Create some base key strings. + GuidToLPWSTR(rclsid, szID, NumItems(szID)); + + size_t nLen = _tcslen(_T("CLSID\\")) + _tcslen( szID) + 1; + if( cchOutCLSID < nLen ) + return E_INVALIDARG; + + _tcscpy_s(szOutCLSID, cchOutCLSID, W("CLSID\\")); + _tcscat_s(szOutCLSID, cchOutCLSID, szID); + + // Create ProgID keys. + SetKeyAndValue(szProgID, NULL, szDesc); + SetKeyAndValue(szProgID, W("CLSID"), szID); + + // Create VersionIndependentProgID keys. + SetKeyAndValue(szIndepProgID, NULL, szDesc); + SetKeyAndValue(szIndepProgID, W("CurVer"), szProgID); + SetKeyAndValue(szIndepProgID, W("CLSID"), szID); + + // Create entries under CLSID. + SetKeyAndValue(szOutCLSID, NULL, szDesc); + SetKeyAndValue(szOutCLSID, W("ProgID"), szProgID); + SetKeyAndValue(szOutCLSID, W("VersionIndependentProgID"), szIndepProgID); + SetKeyAndValue(szOutCLSID, W("NotInsertable"), NULL); + return (S_OK); +} + + + +//***************************************************************************** +// Unregister the basic information in the system registry for a given object +// class. +//***************************************************************************** +HRESULT REGUTIL::UnregisterCOMClass( // Return code. + REFCLSID rclsid, // Class ID we are registering. + LPCTSTR szProgIDPrefix, // Prefix for progid. + int iVersion, // Version # for progid. + LPCTSTR szClassProgID, // Class progid. + BOOL fExternal) // flag - External to mscoree. +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + WCHAR rcCLSID[64]; // CLSID\\szID. + WCHAR rcProgID[128]; // szProgIDPrefix.szClassProgID + WCHAR rcIndProgID[128]; // rcProgID.iVersion + + // Format the prog ID values. + VERIFY(_snwprintf_s(rcProgID, _countof(rcProgID), _TRUNCATE, W("%s.%s"), szProgIDPrefix, szClassProgID)); + + VERIFY(_snwprintf_s(rcIndProgID, _countof(rcIndProgID), _TRUNCATE, W("%s.%d"), rcProgID, iVersion)); + + UnregisterClassBase(rclsid, rcProgID, rcIndProgID, rcCLSID, NumItems(rcCLSID)); + DeleteKey(rcCLSID, W("InprocServer32")); + if (fExternal){ + DeleteKey(rcCLSID, W("Server")); + DeleteKey(rcCLSID, W("Version")); + } + GuidToLPWSTR(rclsid, rcCLSID, NumItems(rcCLSID)); + DeleteKey(W("CLSID"), rcCLSID); + return (S_OK); +} + + +//***************************************************************************** +// Unregister the basic information in the system registry for a given object +// class. +// NOTE: This is the non-side-by-side execution version. +//***************************************************************************** +HRESULT REGUTIL::UnregisterCOMClass( // Return code. + REFCLSID rclsid, // Class ID we are registering. + LPCTSTR szProgIDPrefix, // Prefix for progid. + int iVersion, // Version # for progid. + LPCTSTR szClassProgID) // Class progid. +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + WCHAR rcCLSID[64]; // CLSID\\szID. + WCHAR rcProgID[128]; // szProgIDPrefix.szClassProgID + WCHAR rcIndProgID[128]; // rcProgID.iVersion + + // Format the prog ID values. + VERIFY(_snwprintf_s(rcProgID, _countof(rcProgID), _TRUNCATE, W("%s.%s"), szProgIDPrefix, szClassProgID)); + + VERIFY(_snwprintf_s(rcIndProgID, _countof(rcIndProgID), _TRUNCATE, W("%s.%d"), rcProgID, iVersion)); + + UnregisterClassBase(rclsid, rcProgID, rcIndProgID, rcCLSID, NumItems(rcCLSID)); + DeleteKey(rcCLSID, W("InprocServer32")); + DeleteKey(rcCLSID, W("LocalServer32")); + + GuidToLPWSTR(rclsid, rcCLSID, NumItems(rcCLSID)); + DeleteKey(W("CLSID"), rcCLSID); + return (S_OK); +} + + +//***************************************************************************** +// Delete the basic settings for an inproc server. +//***************************************************************************** +HRESULT REGUTIL::UnregisterClassBase( // Return code. + REFCLSID rclsid, // Class ID we are registering. + LPCTSTR szProgID, // Class prog ID. + LPCTSTR szIndepProgID, // Class version independant prog ID. + __out_ecount(cchOutCLSID) LPTSTR szOutCLSID, // Return formatted class ID here. + DWORD cchOutCLSID) // Out CLS ID buffer size +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + TCHAR szID[64]; // The class ID to register. + + // Create some base key strings. + GuidToLPWSTR(rclsid, szID, NumItems(szID)); + size_t nLen = _tcslen(_T("CLSID\\")) + _tcslen( szID) + 1; + if( cchOutCLSID < nLen ) + return E_INVALIDARG; + + _tcscpy_s(szOutCLSID, cchOutCLSID, W("CLSID\\")); + _tcscat_s(szOutCLSID, cchOutCLSID, szID); + + // Delete the version independant prog ID settings. + DeleteKey(szIndepProgID, W("CurVer")); + DeleteKey(szIndepProgID, W("CLSID")); + WszRegDeleteKey(HKEY_CLASSES_ROOT, szIndepProgID); + + // Delete the prog ID settings. + DeleteKey(szProgID, W("CLSID")); + WszRegDeleteKey(HKEY_CLASSES_ROOT, szProgID); + + // Delete the class ID settings. + DeleteKey(szOutCLSID, W("ProgID")); + DeleteKey(szOutCLSID, W("VersionIndependentProgID")); + DeleteKey(szOutCLSID, W("NotInsertable")); + WszRegDeleteKey(HKEY_CLASSES_ROOT, szOutCLSID); + return (S_OK); +} + + +//***************************************************************************** +// Register a type library. +//***************************************************************************** +HRESULT REGUTIL::RegisterTypeLib( // Return code. + REFGUID rtlbid, // TypeLib ID we are registering. + int iVersion, // Typelib version. + LPCTSTR szDesc, // TypeLib description. + LPCTSTR szModule) // Path to the typelib. +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + WCHAR szID[64]; // The typelib ID to register. + WCHAR szTLBID[256]; // TypeLib\\szID. + WCHAR szHelpDir[_MAX_PATH]; + WCHAR szDrive[_MAX_DRIVE]; + WCHAR szDir[_MAX_DIR]; + WCHAR szVersion[64]; + LPWSTR szTmp; + + // Create some base key strings. + GuidToLPWSTR(rtlbid, szID, NumItems(szID)); + + _tcscpy_s(szTLBID, _countof(szTLBID), W("TypeLib\\")); + _tcscat_s(szTLBID, _countof(szTLBID), szID); + + VERIFY(_snwprintf_s(szVersion, _countof(szVersion), _TRUNCATE, W("%d.0"), iVersion)); + + // Create Typelib keys. + SetKeyAndValue(szTLBID, NULL, NULL); + SetKeyAndValue(szTLBID, szVersion, szDesc); + _tcscat_s(szTLBID, _countof(szTLBID), W("\\")); + _tcscat_s(szTLBID, _countof(szTLBID), szVersion); + SetKeyAndValue(szTLBID, W("0"), NULL); + SetKeyAndValue(szTLBID, W("0\\win32"), szModule); + SetKeyAndValue(szTLBID, W("FLAGS"), W("0")); + SplitPath(szModule, szDrive, _MAX_DRIVE, szDir, _MAX_DIR, NULL, 0, NULL, 0); + _tcscpy_s(szHelpDir, _countof(szHelpDir), szDrive); + if ((szTmp = CharPrev(szDir, szDir + Wszlstrlen(szDir))) != NULL) + *szTmp = '\0'; + _tcscat_s(szHelpDir, _countof(szHelpDir), szDir); + SetKeyAndValue(szTLBID, W("HELPDIR"), szHelpDir); + return (S_OK); +} + + +//***************************************************************************** +// Remove the registry keys for a type library. +//***************************************************************************** +HRESULT REGUTIL::UnregisterTypeLib( // Return code. + REFGUID rtlbid, // TypeLib ID we are registering. + int iVersion) // Typelib version. +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + WCHAR szID[64]; // The typelib ID to register. + WCHAR szTLBID[256]; // TypeLib\\szID. + WCHAR szTLBVersion[256]; // TypeLib\\szID\\szVersion + WCHAR szVersion[64]; + + // Create some base key strings. + GuidToLPWSTR(rtlbid, szID, NumItems(szID)); + + VERIFY(_snwprintf_s(szVersion, _countof(szVersion), _TRUNCATE, W("%d.0"), iVersion)); + + _tcscpy_s(szTLBID, _countof(szTLBID), W("TypeLib\\")); + _tcscat_s(szTLBID, _countof(szTLBID), szID); + _tcscpy_s(szTLBVersion, _countof(szTLBVersion), szTLBID); + _tcscat_s(szTLBVersion, _countof(szTLBVersion), W("\\")); + _tcscat_s(szTLBVersion, _countof(szTLBVersion), szVersion); + + // Delete Typelib keys. + DeleteKey(szTLBVersion, W("HELPDIR")); + DeleteKey(szTLBVersion, W("FLAGS")); + DeleteKey(szTLBVersion, W("0\\win32")); + DeleteKey(szTLBVersion, W("0")); + DeleteKey(szTLBID, szVersion); + WszRegDeleteKey(HKEY_CLASSES_ROOT, szTLBID); + return (0); +} + +#endif // !FEATURE_CORECLR + + +// +// ProbabilisticNameSet: +// +// (Used by ConfigCache, below. If used elsewhere, might justify +// promotion to a standalone header file.) +// +// Represent a set of names in a small, fixed amount of storage. +// We turn a name into a small integer, then add the integer to a bitvector. +// An old trick we used in VC++4 minimal rebuild. +// +// For best results, the number of elements should be a fraction of +// the total number of bits in 'bits'. +// +// Note, only the const methods are thread-safe. +// Callers are responsible for providing their own synchronization when +// constructing and Add'ing names to the set. +// +class ProbabilisticNameSet { +public: + ProbabilisticNameSet() + { + WRAPPER_NO_CONTRACT; + + memset(bits, 0, sizeof(bits)); + } + + // Add a name to the set. + // + void Add(LPCWSTR name) + { + WRAPPER_NO_CONTRACT; + + unsigned i, mask; + GetBitIndex(name, 0, &i, &mask); + bits[i] |= mask; + } + + void Add(LPCWSTR name, DWORD count) + { + WRAPPER_NO_CONTRACT; + + unsigned i, mask; + GetBitIndex(name, count, &i, &mask); + bits[i] |= mask; + } + + // Return TRUE if a name *may have* been added to the set; + // return FALSE if the name *definitely* was NOT ever added to the set. + // + BOOL MayContain(LPCWSTR name) const + { + WRAPPER_NO_CONTRACT; + + unsigned i, mask; + GetBitIndex(name, 0, &i, &mask); + return !!(bits[i] & mask); + } + +private: + static const unsigned cbitSet = 256U; + static const unsigned cbitWord = 8U*sizeof(unsigned); + unsigned bits[cbitSet/cbitWord]; + + // Return the word index and bit mask corresponding to the bitvector member + // addressed by the *case-insensitive* hash of the given name. + // + void GetBitIndex(LPCWSTR name, DWORD count, unsigned* pi, unsigned* pmask) const + { + LIMITED_METHOD_CONTRACT; + unsigned hash; + if (count > 0) + hash = HashiStringNKnownLower80(name, count) % cbitSet; + else + hash = HashiStringKnownLower80(name) % cbitSet; + *pi = hash / cbitWord; + *pmask = (1U << (hash % cbitWord)); + } + +}; + + +// From the Win32 SDK docs: +// Registry Element Size Limits +// ... +// The maximum size of a value name is as follows: +// Windows Server 2003 and Windows XP: 16,383 characters +// Windows 2000: 260 ANSI characters or 16,383 Unicode characters. +// Windows Me/98/95: 255 characters +// Despite that, we only cache value names of 80 characters or less -- +// longer names don't make sense as configuration settings names. +// +static const unsigned cchRegValueNameMax = 80; + +BOOL REGUTIL::s_fUseRegCache = FALSE; +BOOL REGUTIL::s_fUseEnvCache = FALSE; +HKEY REGUTIL::s_hMachineFrameworkKey = (HKEY) INVALID_HANDLE_VALUE; +HKEY REGUTIL::s_hUserFrameworkKey = (HKEY) INVALID_HANDLE_VALUE; +BOOL REGUTIL::s_fUseRegistry = TRUE; +static ProbabilisticNameSet regNames; // set of registry value names seen; should be + // a static field of REGUTIL but I don't + // want to expose ProbabilisticNameSet. +static ProbabilisticNameSet envNames; // set of environment value names seen; + +// "Registry Configuration Cache" +// +// Initialize the (optional) registry config cache. +// +// The purpose of the cache is to avoid hundreds of registry probes +// otherwise incurred by calls to GetConfigDWORD_DontUse_ and GetConfigString_DontUse_. +// +// We accomplish this by enumerating the relevant registry keys and +// remembering the extant value names; and then by avoiding probing +// for a name that was not seen in the enumeration (initialization) phase. +// +// It is optional in the sense that REGUTIL facilities like +// GetConfigDWORD_DontUse_ and GetConfigString_DontUse_ will work fine if the cache +// is never initialized; however, each config access then will hit +// the registry (typically multiple times to search HKCU and HKLM). +// +// +// Initialization: Enumerate these registry keys +// HKCU Software\Microsoft\.NetFramework +// HKLM Software\Microsoft\.NetFramework +// for value names, and "remember" them in the ProbalisticNameSet 'names'. +// +// If we ever find a reg value named DisableConfigCache under any of these +// three keys, the feature is disabled. +// +// This method is not thread-safe. It should only be called once. +// +// Perf Optimization for VSWhidbey:113373. +// +void REGUTIL::InitOptionalConfigCache() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + static const HKEY roots[] = { HKEY_CURRENT_USER, + HKEY_LOCAL_MACHINE}; + + LONG l = ERROR_SUCCESS; // general Win32 API error return code + HKEY hkey = NULL; + + // No caching if the environment variable COMPLUS_DisableConfigCache is set + // + if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DisableConfigCache) != 0) + goto failure; + + // Enumerate each root + // + for (int i = 0; i < NumItems(roots); i++) { + hkey = NULL; // defensive + l = WszRegOpenKeyEx(roots[i], FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &hkey); + if (l == ERROR_FILE_NOT_FOUND) { + // That registry key is not present. + // For example, installation with no HKCU\...\.NETFramework. + // Should be OK to proceed. + continue; + } +#if defined(FEATURE_APPX) && !defined(DACCESS_COMPILE) + else if (l == ERROR_ACCESS_DENIED && AppX::IsAppXProcess()) { + // If we encounter access denied for the current key in AppX, ignore + // the failure and continue to cache the rest. Effectively this means + // we are caching that key as containing no values, which is correct + // because in the unlikely event there are values hiding underneath + // later attempts to access them (open the key) would also hit access + // denied and continue on probing other locations. + continue; + } +#endif // FEATURE_APPX && !DACCESS_COMPILE + else if (l != ERROR_SUCCESS) { + // Something else went wrong. To be safe, don't enable the cache. + goto failure; + } + + // Enumerate every value name under this key. + // + for (int j = 0; ; j++) { + WCHAR wszValue[cchRegValueNameMax + 1]; + DWORD dwValueSize = NumItems(wszValue); + l = WszRegEnumValue(hkey, j, wszValue, &dwValueSize, + NULL, NULL, NULL, NULL); + + if (l == ERROR_SUCCESS) { + // Add value name to the names cache. + regNames.Add(wszValue); + } + else if (l == ERROR_NO_MORE_ITEMS) { + // Expected case: we've considered every value under this key. + break; + } + else if ((l == ERROR_INSUFFICIENT_BUFFER || l == ERROR_MORE_DATA) && + (dwValueSize > cchRegValueNameMax)) { + // Name is too long. That's OK, we don't cache such names. + continue; + } +#if defined(FEATURE_APPX) && !defined DACCESS_COMPILE + else if (l == ERROR_ACCESS_DENIED && AppX::IsAppXProcess()) { + // As above, ignore access denied in AppX and continue on trying to cache + continue; + } +#endif // FEATURE_APPX && !DACCESS_COMPILE + else { + // WszRegEnumValue failed OOM, or something else went wrong. + // To be safe, don't enable the cache. + goto failure; + } + } + + // Save the handles to framework regkeys so that future reads dont have to + // open it again + if (roots[i] == HKEY_CURRENT_USER) + s_hUserFrameworkKey = hkey; + else if (roots[i] == HKEY_LOCAL_MACHINE) + s_hMachineFrameworkKey = hkey; + else + RegCloseKey(hkey); + + hkey = NULL; + } + + // Success. We've enumerated all value names under the roots; + // enable the REGUTIL value name config cache. + // + s_fUseRegCache = TRUE; + + // Now create a cache of environment variables + WCHAR * wszStrings = WszGetEnvironmentStrings(); + if (wszStrings) + { + // GetEnvironmentStrings returns pointer to a null terminated block containing + // null terminated strings + for(WCHAR *wszCurr = wszStrings; *wszCurr; wszCurr++) + { + WCHAR wch = towlower(*wszCurr); + + // Lets only cache env variables with the COMPlus prefix only + if (wch == W('c')) + { + WCHAR *wszName = wszCurr; + + // Look for the separator between name and value + while (*wszCurr && *wszCurr != W('=')) + wszCurr++; + + if (*wszCurr == W('=')) + { + // Check the prefix + if(!SString::_wcsnicmp(wszName, COMPLUS_PREFIX, LEN_OF_COMPLUS_PREFIX)) + { + wszName += LEN_OF_COMPLUS_PREFIX; + envNames.Add(wszName, (DWORD) (wszCurr - wszName)); + } + } + + } + // Look for current string termination + while (*wszCurr) + wszCurr++; + + } + + WszFreeEnvironmentStrings(wszStrings); + s_fUseEnvCache = TRUE; + + } + return; + +failure: + if (hkey != NULL) + RegCloseKey(hkey); +} + +// Return TRUE if the registry value name was seen (or might have been seen) +// in the registry at cache initialization time; +// return FALSE if it definitely was not seen at startup. +// +// If not using the config cache, return TRUE always. +// +// Perf Optimization for VSWhidbey:113373. +// +BOOL REGUTIL::RegCacheValueNameSeenPerhaps(LPCWSTR name) +{ + WRAPPER_NO_CONTRACT; + + return !s_fUseRegCache + || (wcslen(name) > cchRegValueNameMax) + || regNames.MayContain(name); +} + +BOOL REGUTIL::EnvCacheValueNameSeenPerhaps(LPCWSTR name) +{ + WRAPPER_NO_CONTRACT; + + return !s_fUseEnvCache + || envNames.MayContain(name); +} + +#endif // ALLOW_REGISTRY diff --git a/src/utilcode/regutilformac.cpp b/src/utilcode/regutilformac.cpp new file mode 100644 index 0000000000..d04b1076a6 --- /dev/null +++ b/src/utilcode/regutilformac.cpp @@ -0,0 +1,13 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// + +// +//***************************************************************************** + +/* The mac build does not like filenames with .CPP (in capitals) for an extension, so we need this workaround + so we can include REGUTIL.CPP. */ +#include "REGUTIL.CPP" diff --git a/src/utilcode/safewrap.cpp b/src/utilcode/safewrap.cpp new file mode 100644 index 0000000000..041ed5632c --- /dev/null +++ b/src/utilcode/safewrap.cpp @@ -0,0 +1,415 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// SafeWrap.cpp +// + +// +// This file contains wrapper functions for Win32 API's that take SStrings +// and use CLR-safe holders. +// +// See guidelines in SafeWrap.h for writing these APIs. +//***************************************************************************** + +#include "stdafx.h" // Precompiled header key. +#include "safewrap.h" +#include "winwrap.h" // Header for macros and functions. +#include "utilcode.h" +#include "holder.h" +#include "sstring.h" +#include "ex.h" + +//----------------------------------------------------------------------------- +// Get the current directory. +// On success, returns true and sets 'Value' to unicode version of cur dir. +// Throws on all failures. This should mainly be oom. +//----------------------------------------------------------------------------- +void ClrGetCurrentDirectory(SString & value) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + } + CONTRACTL_END; + + // Get size needed + DWORD lenWithNull = WszGetCurrentDirectory(0, NULL); + + // Now read it for content. + WCHAR * pCharBuf = value.OpenUnicodeBuffer(lenWithNull); + DWORD lenWithoutNull = WszGetCurrentDirectory(lenWithNull, pCharBuf); + + // An actual API failure in GetCurrentDirectory failure should be very rare, so we'll throw on those. + if (lenWithoutNull == 0) + { + value.CloseBuffer(0); + ThrowLastError(); + } + if (lenWithoutNull != (lenWithNull - 1)) + { + value.CloseBuffer(lenWithoutNull); + + // must have changed underneath us. + ThrowHR(E_FAIL); + } + + value.CloseBuffer(lenWithoutNull); +} + +// Nothrowing wrapper. +bool ClrGetCurrentDirectoryNoThrow(SString & value) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + bool fOk = true; + EX_TRY + { + ClrGetCurrentDirectory(value); + } + EX_CATCH + { + fOk = false; + } + EX_END_CATCH(SwallowAllExceptions) + return fOk; +} +//----------------------------------------------------------------------------- +// Reads an environment variable into the given SString. +// Returns true on success, false on failure (includes if the var does not exist). +// May throw on oom. +//----------------------------------------------------------------------------- +bool ClrGetEnvironmentVariable(LPCSTR szEnvVarName, SString & value) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + + PRECONDITION(szEnvVarName != NULL); + } + CONTRACTL_END; + + // First read it to get the needed length. + DWORD lenWithNull = GetEnvironmentVariableA(szEnvVarName, NULL, 0); + if (lenWithNull == 0) + { + return false; + } + + // Now read it for content. + char * pCharBuf = value.OpenANSIBuffer(lenWithNull); + DWORD lenWithoutNull = GetEnvironmentVariableA(szEnvVarName, pCharBuf, lenWithNull); + value.CloseBuffer(lenWithoutNull); + + if (lenWithoutNull != (lenWithNull - 1)) + { + // Env var must have changed underneath us. + return false; + } + return true; +} + +// Nothrowing wrapper. +bool ClrGetEnvironmentVariableNoThrow(LPCSTR szEnvVarName, SString & value) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + + bool fOk = false; + EX_TRY + { + fOk = ClrGetEnvironmentVariable(szEnvVarName, value); + } + EX_CATCH + { + fOk = false; + } + EX_END_CATCH(SwallowAllExceptions) + return fOk; +} + +void ClrGetModuleFileName(HMODULE hModule, SString & value) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + } + CONTRACTL_END; + + WCHAR * pCharBuf = value.OpenUnicodeBuffer(_MAX_PATH); + DWORD numChars = GetModuleFileNameW(hModule, pCharBuf, _MAX_PATH); + value.CloseBuffer(numChars); +} + +bool ClrGetModuleFileNameNoThrow(HMODULE hModule, SString & value) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + bool fOk = true; + EX_TRY + { + ClrGetModuleFileName(hModule, value); + } + EX_CATCH + { + fOk = false; + } + EX_END_CATCH(SwallowAllExceptions) + return fOk; +} + +ClrDirectoryEnumerator::ClrDirectoryEnumerator(LPCWSTR pBaseDirectory, LPCWSTR pMask /*= W("*")*/) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + } + CONTRACTL_END; + + StackSString strMask; + SString s(SString::Literal, W("\\")); + strMask.Set(pBaseDirectory, s, pMask); + dirHandle = WszFindFirstFile(strMask, &data); + + if (dirHandle == INVALID_HANDLE_VALUE) + { + DWORD dwLastError = GetLastError(); + + // We either ran out of files, or didnt encounter even a single file matching the + // search mask. If it is neither of these conditions, then convert the error to an exception + // and raise it. + if ((dwLastError != ERROR_FILE_NOT_FOUND) && (dwLastError != ERROR_NO_MORE_FILES)) + ThrowLastError(); + } + + fFindNext = FALSE; +} + +bool ClrDirectoryEnumerator::Next() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + } + CONTRACTL_END; + + if (dirHandle == INVALID_HANDLE_VALUE) + return FALSE; + + for (;;) + { + if (fFindNext) + { + if (!WszFindNextFile(dirHandle, &data)) + { + if (GetLastError() != ERROR_NO_MORE_FILES) + ThrowLastError(); + + return FALSE; + } + } + else + { + fFindNext = TRUE; + } + + // Skip junk + if (wcscmp(data.cFileName, W(".")) != 0 && wcscmp(data.cFileName, W("..")) != 0) + return TRUE; + } +} + +DWORD ClrReportEvent( + LPCWSTR pEventSource, + WORD wType, + WORD wCategory, + DWORD dwEventID, + PSID lpUserSid, + WORD wNumStrings, + LPCWSTR *lpStrings, + DWORD dwDataSize /*=0*/, + LPVOID lpRawData /*=NULL*/) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + +#ifndef FEATURE_PAL + HANDLE h = ::RegisterEventSourceW( + NULL, // uses local computer + pEventSource); + if (h == NULL) + { + // Return the error code to the caller so that + // appropriate asserts/logging can be done + // incase of errors like event log being full + return GetLastError(); + } + + // Every event id should have matching description in dlls\shim\eventmsg.mc. This allows + // event view to know how to display message. + _ASSERTE (dwEventID != 0); + + // Attempt to report the event to the event log. Note that if the operation fails + // (for example because of low memory conditions) it isn't fatal so we can safely ignore + // the return code from ReportEventW. + BOOL ret = ::ReportEventW( + h, // event log handle + wType, + wCategory, + dwEventID, + lpUserSid, + wNumStrings, + dwDataSize, + lpStrings, + lpRawData); + + DWORD dwRetStatus = GetLastError(); + + ::DeregisterEventSource(h); + + return (ret == TRUE)?ERROR_SUCCESS:dwRetStatus; +#else // FEATURE_PAL + // UNIXTODO: Report the event somewhere? + return ERROR_SUCCESS; +#endif // FEATURE_PAL +} + +// Returns ERROR_SUCCESS if succeessful in reporting to event log, or +// Windows error code to indicate the specific error. +DWORD ClrReportEvent( + LPCWSTR pEventSource, + WORD wType, + WORD wCategory, + DWORD dwEventID, + PSID lpUserSid, + LPCWSTR pMessage) +{ + return ClrReportEvent(pEventSource, wType, wCategory, dwEventID, lpUserSid, 1, &pMessage); +} + +#ifndef FEATURE_PAL +// Read a REG_SZ (null-terminated string) value from the registry. Throws. +// +// Arguments: +// hKey - key to registry hive. +// szValueName - null-terminated string for value name to lookup. +// If this is empty, this gets the (default) value in the registry hive. +// value - out parameter to hold registry value string contents. +// +// Returns: +// value is set on success. Throws on any failure, including if the value doesn't exist +// or if the value exists but is not a REG_SZ. +// +// Notes: +// REG_SZ is a single null-terminated string in the registry. +// This is only support on Windows because the registry is windows specific. +void ClrRegReadString(HKEY hKey, const SString & szValueName, SString & value) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + } + CONTRACTL_END; + + DWORD type; + DWORD numBytesData; + + // Preemptively clear the string such that it's empty in any failure case. + value.Clear(); + + // + // Step 1: First call to find size of buffer and ensure data type is correct + // + LONG ret = WszRegQueryValueEx( + hKey, + szValueName.GetUnicode(), // NULL or "\0" represents the (default) key. + NULL, // reserved + &type, // should be REG_SZ + NULL, // not requesting data yet + &numBytesData + ); + + if (ret != ERROR_SUCCESS) + { + ThrowWin32(ret); + } + + if (type != REG_SZ) + { + // The registry value is not a string. + ThrowHR(E_INVALIDARG); + } + + // REG_SZ includes the null terminator. + DWORD numCharsIncludingNull = numBytesData / sizeof(WCHAR); + + // + // Step 2: Allocate buffer to hold final result + // + WCHAR * pData = value.OpenUnicodeBuffer(numCharsIncludingNull); + DWORD numBytesData2 = numBytesData; + + + // + // Step 3: Requery to get actual contents + // + ret = WszRegQueryValueEx( + hKey, + szValueName.GetUnicode(), + NULL, // reserved + &type, // should still be REG_SZ + (LPBYTE) pData, + &numBytesData2 + ); + + // This check should only fail if the registry was changed inbetween the first query + // and the second. In practice, that should never actually happen. + if ((numBytesData2 != numBytesData) || (type != REG_SZ)) + { + // On error, leave string empty. + value.CloseBuffer(0); + + ThrowHR(E_FAIL); + } + + if (ret != ERROR_SUCCESS) + { + // On error, leave string empty. + value.CloseBuffer(0); + ThrowWin32(ret); + } + + + // + // Step 4: Close the string buffer + // + COUNT_T numCharsNoNull = numCharsIncludingNull - 1; + value.CloseBuffer(numCharsNoNull); +} +#endif // FEATURE_PAL diff --git a/src/utilcode/sbuffer.cpp b/src/utilcode/sbuffer.cpp new file mode 100644 index 0000000000..7d2e719135 --- /dev/null +++ b/src/utilcode/sbuffer.cpp @@ -0,0 +1,147 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// --------------------------------------------------------------------------- + +// +// Buffer.cpp +// --------------------------------------------------------------------------- + +#include "stdafx.h" +#include "sbuffer.h" +#include "ex.h" +#include "holder.h" +#include "stdmacros.h" + +// Try to minimize the performance impact of contracts on CHK build. +#if defined(_MSC_VER) +#pragma inline_depth (20) +#endif + +const DWORD g_garbageFillBuffer[GARBAGE_FILL_BUFFER_ITEMS] = +{ + GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, + GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, + GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, + GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, +}; + +//---------------------------------------------------------------------------- +// ReallocateBuffer +// Low level buffer reallocate routine +//---------------------------------------------------------------------------- +void SBuffer::ReallocateBuffer(COUNT_T allocation, Preserve preserve) +{ + CONTRACT_VOID + { + PRECONDITION(CheckPointer(this)); + PRECONDITION(CheckBufferClosed()); + PRECONDITION(CheckAllocation(allocation)); + PRECONDITION(allocation >= m_size); + POSTCONDITION(m_allocation == allocation); + if (allocation > 0) THROWS; else NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC_HOST_ONLY; + } + CONTRACT_END; + + BYTE *newBuffer = NULL; + if (allocation > 0) + { + newBuffer = NewBuffer(allocation); + + if (preserve == PRESERVE) + { + // Copy the relevant contents of the old buffer over + DebugMoveBuffer(newBuffer, m_buffer, m_size); + } + } + + if (IsAllocated()) + DeleteBuffer(m_buffer, m_allocation); + + m_buffer = newBuffer; + m_allocation = allocation; + + if (allocation > 0) + SetAllocated(); + else + ClearAllocated(); + + ClearImmutable(); + + RETURN; +} + +void SBuffer::Replace(const Iterator &i, COUNT_T deleteSize, COUNT_T insertSize) +{ + CONTRACT_VOID + { + THROWS; + GC_NOTRIGGER; + PRECONDITION(CheckPointer(this)); + PRECONDITION(CheckIteratorRange(i, deleteSize)); + SUPPORTS_DAC_HOST_ONLY; + } + CONTRACT_END; + + COUNT_T startRange = (COUNT_T) (i.m_ptr - m_buffer); + // The PRECONDITION(CheckIterationRange(i, deleteSize)) should check this in + // contract-checking builds, but this ensures we don't integer overflow if someone + // passes in an aggregious deleteSize by capping it to the remaining length in the + // buffer. + if ((COUNT_T)(m_buffer + m_size - i.m_ptr) < deleteSize) + { + _ASSERTE(!"Trying to replace more bytes than are remaining in the buffer."); + deleteSize = (COUNT_T)(m_buffer + m_size - i.m_ptr); + } + COUNT_T endRange = startRange + deleteSize; + COUNT_T end = m_size; + + SCOUNT_T delta = insertSize - deleteSize; + + if (delta < 0) + { + // Buffer is shrinking + + DebugDestructBuffer(i.m_ptr, deleteSize); + + DebugMoveBuffer(m_buffer + endRange + delta, + m_buffer + endRange, + end - endRange); + + Resize(m_size+delta, PRESERVE); + + i.Resync(this, m_buffer + startRange); + + } + else if (delta > 0) + { + // Buffer is growing + + ResizePadded(m_size+delta); + + i.Resync(this, m_buffer + startRange); + + DebugDestructBuffer(i.m_ptr, deleteSize); + + DebugMoveBuffer(m_buffer + endRange + delta, + m_buffer + endRange, + end - endRange); + + } + else + { + // Buffer stays the same size. We need to DebugDestruct it first to keep + // the invariant that the new space is clean. + + DebugDestructBuffer(i.m_ptr, insertSize); + } + + DebugConstructBuffer(i.m_ptr, insertSize); + + RETURN; +} + + diff --git a/src/utilcode/securityutil.cpp b/src/utilcode/securityutil.cpp new file mode 100644 index 0000000000..22b99735ba --- /dev/null +++ b/src/utilcode/securityutil.cpp @@ -0,0 +1,509 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + + +#include "stdafx.h" + +#include "securityutil.h" +#include "ex.h" + +#include "securitywrapper.h" + +// This is defined in ipcmanagerinterface.h, but including that is problematic at the moment... +// These are the right that we will give to the global section and global events used +// in communicating between debugger and debugee +// +// SECTION_ALL_ACCESS is needed for the IPC block. Unfortunately, we DACL our events and +// IPC block identically. Or this particular right does not need to bleed into here. +// +#ifndef CLR_IPC_GENERIC_RIGHT +#define CLR_IPC_GENERIC_RIGHT (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | STANDARD_RIGHTS_ALL | SECTION_ALL_ACCESS) +#endif + + +//***************************************************************** +// static helper function +// +// helper to form ACL that contains AllowedACE of users of current +// process and target process +// +// [IN] pid - target process id +// [OUT] ppACL - ACL for the process +// +// Clean up - +// Caller remember to call FreeACL() on *ppACL +//***************************************************************** +HRESULT SecurityUtil::GetACLOfPid(DWORD pid, PACL *ppACL) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + _ASSERTE(ppACL); + *ppACL = NULL; + + PSID pCurrentProcessSid = NULL; + PSID pTargetProcessSid = NULL; + PSID pTargetProcessAppContainerSid = NULL; + DWORD cSid = 0; + DWORD dwAclSize = 0; + + LOG((LF_CORDB, LL_INFO10000, + "SecurityUtil::GetACLOfPid on pid : 0x%08x\n", + pid)); + + + SidBuffer sidCurrentProcess; + SidBuffer sidTargetProcess; + SidBuffer sidTargetProcessAppContainer; + + // Get sid for current process. + EX_TRY + { + sidCurrentProcess.InitFromProcess(GetCurrentProcessId()); // throw on error. + pCurrentProcessSid = sidCurrentProcess.GetSid().RawSid(); + cSid++; + } + EX_CATCH + { + } + EX_END_CATCH(RethrowTerminalExceptions); + + // Get sid for target process. + EX_TRY + { + sidTargetProcess.InitFromProcess(pid); // throws on error. + pTargetProcessSid = sidTargetProcess.GetSid().RawSid(); + cSid++; + } + EX_CATCH + { + } + EX_END_CATCH(RethrowTerminalExceptions); + + //FISHY: what is the scenario where only one of the above calls succeeds? + if (cSid == 0) + { + // failed to get any useful sid. Just return. + // need a better error. + // + hr = E_FAIL; + goto exit; + } + + hr = sidTargetProcessAppContainer.InitFromProcessAppContainerSidNoThrow(pid); + if (FAILED(hr)) + { + goto exit; + } + else if (hr == S_OK) + { + pTargetProcessAppContainerSid = sidTargetProcessAppContainer.GetSid().RawSid(); + cSid++; + } + else if(hr == S_FALSE) //not an app container, no sid to add + { + hr = S_OK; // don't leak S_FALSE + } + + LOG((LF_CORDB, LL_INFO10000, + "SecurityUtil::GetACLOfPid number of sid : 0x%08x\n", + cSid)); + + // Now allocate space for ACL. First calculate the space is need to hold ACL + dwAclSize = sizeof(ACL) + (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) * cSid; + if (pCurrentProcessSid) + { + dwAclSize += GetLengthSid(pCurrentProcessSid); + } + if (pTargetProcessSid) + { + dwAclSize += GetLengthSid(pTargetProcessSid); + } + if (pTargetProcessAppContainerSid) + { + dwAclSize += GetLengthSid(pTargetProcessAppContainerSid); + } + + *ppACL = (PACL) new (nothrow) char[dwAclSize]; + if (*ppACL == NULL) + { + hr = E_OUTOFMEMORY; + goto exit; + } + + // Initialize ACL + // add each sid to the allowed ace list + if (!InitializeAcl(*ppACL, dwAclSize, ACL_REVISION)) + { + hr = HRESULT_FROM_GetLastError(); + goto exit; + } + + if (pCurrentProcessSid) + { + // add the current process's sid into ACL if we have it + if (!AddAccessAllowedAce(*ppACL, ACL_REVISION, CLR_IPC_GENERIC_RIGHT, pCurrentProcessSid)) + { + hr = HRESULT_FROM_GetLastError(); + goto exit; + } + } + + if (pTargetProcessSid) + { + // add the target process's sid into ACL if we have it + if (!AddAccessAllowedAce(*ppACL, ACL_REVISION, CLR_IPC_GENERIC_RIGHT, pTargetProcessSid)) + { + hr = HRESULT_FROM_GetLastError(); + goto exit; + } + } + + if (pTargetProcessAppContainerSid) + { + // add the target process's AppContainer's sid into ACL if we have it + if (!AddAccessAllowedAce(*ppACL, ACL_REVISION, CLR_IPC_GENERIC_RIGHT, pTargetProcessAppContainerSid)) + { + hr = HRESULT_FROM_GetLastError(); + goto exit; + } + } + + // we better to form a valid ACL to return + _ASSERTE(IsValidAcl(*ppACL)); +exit: + if (FAILED(hr) && *ppACL) + { + delete [] (reinterpret_cast<char*>(ppACL)); + } + return hr; +} // SecurityUtil::GetACLOfPid + + +//***************************************************************** +// static helper function +// +// free the ACL allocated by SecurityUtil::GetACLOfPid +// +// [IN] pACL - ACL to be freed +// +//***************************************************************** +void SecurityUtil::FreeACL(PACL pACL) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + if (pACL) + { + delete [] (reinterpret_cast<char*>(pACL)); + } +} // SecurityUtil::FreeACL + + +//***************************************************************** +// constructor +// +// [IN] pACL - ACL that this instance of SecurityUtil will held on to +// +//***************************************************************** +SecurityUtil::SecurityUtil(PACL pACL) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + m_pACL = pACL; + m_pSacl = NULL; + m_fInitialized = false; +} + +//***************************************************************** +// destructor +// +// free the ACL that this instance of SecurityUtil helds on to +// +//***************************************************************** +SecurityUtil::~SecurityUtil() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + FreeACL(m_pACL); + FreeACL(m_pSacl); +} + +//***************************************************************** +// Initialization function +// +// form the SecurityDescriptor that will represent the m_pACL +// +//***************************************************************** +HRESULT SecurityUtil::Init() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + if (m_pACL) + { + if (!InitializeSecurityDescriptor(&m_SD, SECURITY_DESCRIPTOR_REVISION)) + { + hr = HRESULT_FROM_GetLastError(); + return hr; + } + if (!SetSecurityDescriptorDacl(&m_SD, TRUE, m_pACL, FALSE)) + { + hr = HRESULT_FROM_GetLastError(); + return hr; + } + + m_SA.nLength = sizeof(SECURITY_ATTRIBUTES); + m_SA.lpSecurityDescriptor = &m_SD; + m_SA.bInheritHandle = FALSE; + m_fInitialized = true; + } + return S_OK; +} + +// *************************************************************************** +// Initialization functions which will call the normal Init and add a +// mandatory label entry to the sacl +// +// Expects hProcess to be a valid handle to the process which has the desired +// mandatory label +// *************************************************************************** +HRESULT SecurityUtil::Init(HANDLE hProcess) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + HRESULT hr = Init(); + if (FAILED(hr)) + { + return hr; + } + + NewArrayHolder<BYTE> pLabel; + + hr = GetMandatoryLabelFromProcess(hProcess, &pLabel); + if (FAILED(hr)) + { + return hr; + } + + TOKEN_MANDATORY_LABEL * ptml = (TOKEN_MANDATORY_LABEL *) pLabel.GetValue(); + + hr = SetSecurityDescriptorMandatoryLabel(ptml->Label.Sid); + + return hr; +} + + +// *************************************************************************** +// Given a process, this will put the mandatory label into a buffer and point +// ppbLabel at the buffer. +// +// Caller must free ppbLabel via the array "delete []" operator +// *************************************************************************** +HRESULT SecurityUtil::GetMandatoryLabelFromProcess(HANDLE hProcess, LPBYTE * ppbLabel) +{ + *ppbLabel = NULL; + + DWORD dwSize = 0; + HandleHolder hToken; + DWORD err = 0; + + if(!OpenProcessToken(hProcess, TOKEN_READ, &hToken)) + { + return HRESULT_FROM_GetLastError(); + } + + if(!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIntegrityLevel, NULL, 0, &dwSize)) + { + err = GetLastError(); + } + + // We need to make sure that GetTokenInformation failed in a predictable manner so we know that + // dwSize has the correct buffer size in it. + if (err != ERROR_INSUFFICIENT_BUFFER || dwSize == 0) + { + return HRESULT_FROM_WIN32(err); + } + + NewArrayHolder<BYTE> pLabel = new (nothrow) BYTE[dwSize]; + if (pLabel == NULL) + { + return E_OUTOFMEMORY; + } + + if(!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIntegrityLevel, pLabel, dwSize, &dwSize)) + { + return HRESULT_FROM_GetLastError(); + } + + // Our caller will be freeing the memory so use Extract + *ppbLabel = pLabel.Extract(); + + return S_OK; +} + +//--------------------------------------------------------------------------------------- +// +// Returns pointer inside the specified mandatory SID to the DWORD representing the +// integrity level of the process. This DWORD will be one of the +// SECURITY_MANDATORY_*_RID constants. +// +// Arguments: +// psidIntegrityLevelLabel - [in] PSID in which to find the integrity level. +// +// Return Value: +// Pointer to the RID stored in the specified SID. This RID represents the +// integrity level of the process +// + +// static +DWORD * SecurityUtil::GetIntegrityLevelFromMandatorySID(PSID psidIntegrityLevelLabel) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return GetSidSubAuthority(psidIntegrityLevelLabel, (*GetSidSubAuthorityCount(psidIntegrityLevelLabel) - 1)); +} + +// Creates a mandatory label ace and sets it to be the entry in the +// security descriptor's sacl. This assumes there are no other entries +// in the sacl +HRESULT SecurityUtil::SetSecurityDescriptorMandatoryLabel(PSID psidIntegrityLevelLabel) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + HRESULT hr; + + DWORD cbSid = GetLengthSid(psidIntegrityLevelLabel); + DWORD cbAceStart = offsetof(SYSTEM_MANDATORY_LABEL_ACE, SidStart); + // We are about allocate memory for a ACL and an ACE so we need space for: + // 1) the ACL: sizeof(ACL) + // 2) the entry: the sid is of variable size, so the SYSTEM_MANDATORY_LABEL_ACE + // structure has only the first DWORD of the sid in its definition, to get the + // appropriate size we get size without SidStart and add on the actual size of the sid + DWORD cbSacl = sizeof(ACL) + cbAceStart + cbSid; + + NewArrayHolder<BYTE> sacl = new (nothrow) BYTE[cbSacl]; + + m_pSacl = NULL; + + if (sacl == NULL) + { + return E_OUTOFMEMORY; + } + ZeroMemory(sacl.GetValue(), cbSacl); + PACL pSacl = reinterpret_cast<ACL *>(sacl.GetValue()); + SYSTEM_MANDATORY_LABEL_ACE * pLabelAce = reinterpret_cast<SYSTEM_MANDATORY_LABEL_ACE *>(sacl.GetValue() + sizeof(ACL)); + PSID psid = reinterpret_cast<SID *>(&pLabelAce->SidStart); + + // Our buffer looks like this now: (not drawn to scale) + // sacl pSacl pLabelAce psid + // - - + // | | + // | - - + // | | + // | | - + // | - | + // - - + + DWORD dwIntegrityLevel = *(GetIntegrityLevelFromMandatorySID(psidIntegrityLevelLabel)); + + if (dwIntegrityLevel >= SECURITY_MANDATORY_MEDIUM_RID) + { + // No need to set the integrity level unless it's lower than medium + return S_OK; + } + + if(!InitializeAcl(pSacl, cbSacl, ACL_REVISION)) + { + return HRESULT_FROM_GetLastError(); + } + + pSacl->AceCount = 1; + + pLabelAce->Header.AceType = SYSTEM_MANDATORY_LABEL_ACE_TYPE; + pLabelAce->Header.AceSize = WORD(cbAceStart + cbSid); + pLabelAce->Mask = SYSTEM_MANDATORY_LABEL_NO_WRITE_UP; + + memcpy(psid, psidIntegrityLevelLabel, cbSid); + + if(!SetSecurityDescriptorSacl(m_SA.lpSecurityDescriptor, TRUE, pSacl, FALSE)) + { + return HRESULT_FROM_GetLastError(); + } + + // No need to delete the sacl buffer, it will be deleted in the + // destructor of this class + m_pSacl = (PACL)sacl.Extract(); + return S_OK; +} + +//***************************************************************** +// Return SECURITY_ATTRIBUTES that we form in the Init function +// +// No clean up is needed after calling this function. The destructor of the +// instance will do the right thing. Note that this is designed such that +// we minimize memory allocation, ie the SECURITY_DESCRIPTOR and +// SECURITY_ATTRIBUTES are embedded in the SecurityUtil instance. +// +// Caller should not modify the returned SECURITY_ATTRIBUTES!!! +//***************************************************************** +HRESULT SecurityUtil::GetSA(SECURITY_ATTRIBUTES **ppSA) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + _ASSERTE(ppSA); + + if (m_fInitialized == false) + { + _ASSERTE(!"Bad code path!"); + *ppSA = NULL; + return E_FAIL; + } + + *ppSA = &m_SA; + return S_OK; +} diff --git a/src/utilcode/securitywrapper.cpp b/src/utilcode/securitywrapper.cpp new file mode 100644 index 0000000000..249fe494e1 --- /dev/null +++ b/src/utilcode/securitywrapper.cpp @@ -0,0 +1,842 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// File: SecurityWrapper.cpp +// + +// +// Wrapper around Win32 Security functions +// +//***************************************************************************** + +#include "stdafx.h" + +#include "securitywrapper.h" +#include "ex.h" +#include "holder.h" + + +// For GetSidFromProcess* +#include <tlhelp32.h> +#include "wtsapi32.h" + + +//----------------------------------------------------------------------------- +// Constructor for Sid wrapper class. +// pSid - OS sid to wrap +//----------------------------------------------------------------------------- +Sid::Sid(PSID pSid) +{ + _ASSERTE(pSid != NULL); + m_pSid = pSid; +} + +//----------------------------------------------------------------------------- +// Aesthetic wrapper for Sid equality +//----------------------------------------------------------------------------- +bool Sid::Equals(PSID a, PSID b) +{ + return EqualSid(a, b) != 0; +} + +//----------------------------------------------------------------------------- +// Ctor for SidBuffer class +//----------------------------------------------------------------------------- +SidBuffer::SidBuffer() +{ + m_pBuffer = NULL; +} + +//----------------------------------------------------------------------------- +// Dtor for SidBuffer class. +//----------------------------------------------------------------------------- +SidBuffer::~SidBuffer() +{ + delete [] m_pBuffer; +} + +//----------------------------------------------------------------------------- +// Get the underlying sid +// Caller assumes SidBuffer has been initialized. +//----------------------------------------------------------------------------- +Sid SidBuffer::GetSid() +{ + _ASSERTE(m_pBuffer != NULL); + Sid s((PSID) m_pBuffer); + return s; +} + +// ---------------------------------------------------------------------------- +// Used by GetSidFromProcessWorker to determine which SID from the +// process token to use when initializing the SID +enum SidType +{ + // Use TokenOwner: the default owner SID used for newly created objects + kOwnerSid, + + // Use TokenUser: the user account from the token + kUserSid, +}; + +// ---------------------------------------------------------------------------- +// GetSidFromProcessWorker +// +// Description: +// Internal helper. Gets the SID for the given process and given sid type +// +// Arguments: +// * dwProcessId - [in] Process to get SID from +// * sidType - [in] Type of sid to get (owner or user) +// * ppSid - [out] SID found. Caller responsible for deleting this memory. +// +// Return Value: +// HRESULT indicating success / failure. +// +// Notes: +// * Caller owns deleting (*ppSid) when done with the SID +// + +HRESULT GetSidFromProcessWorker(DWORD dwProcessId, SidType sidType, PSID *ppSid) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + TOKEN_USER *pTokUser = NULL; + HANDLE hProc = INVALID_HANDLE_VALUE; + HANDLE hToken = INVALID_HANDLE_VALUE; + DWORD dwRetLength; + LPVOID pvTokenInfo = NULL; + TOKEN_INFORMATION_CLASS tokenInfoClass; + PSID pSidFromTokenInfo = NULL; + DWORD cbSid; + PSID pSid = NULL; + + LOG((LF_CORDB, LL_INFO10000, + "SecurityUtil::GetSidFromProcess: 0x%08x\n", + dwProcessId)); + + _ASSERTE(ppSid); + *ppSid = NULL; + + _ASSERTE((sidType == kOwnerSid) || (sidType == kUserSid)); + tokenInfoClass = (sidType == kOwnerSid) ? TokenOwner : TokenUser; + + hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId); + + if (hProc == NULL) + { + hr = HRESULT_FROM_GetLastError(); + goto exit; + } + if (!OpenProcessToken(hProc, TOKEN_QUERY, &hToken)) + { + hr = HRESULT_FROM_GetLastError(); + goto exit; + } + + // figure out the length + GetTokenInformation(hToken, tokenInfoClass, NULL, 0, &dwRetLength); + _ASSERTE(dwRetLength); + + pvTokenInfo = new (nothrow) BYTE[dwRetLength]; + if (pvTokenInfo == NULL) + { + hr = E_OUTOFMEMORY; + goto exit; + } + + if (!GetTokenInformation(hToken, tokenInfoClass, pvTokenInfo, dwRetLength, &dwRetLength)) + { + hr = HRESULT_FROM_GetLastError(); + goto exit; + } + + // Copy over the SID + pSidFromTokenInfo = + (sidType == kOwnerSid) ? + ((TOKEN_OWNER *) pvTokenInfo)->Owner : + ((TOKEN_USER *) pvTokenInfo)->User.Sid; + cbSid = GetLengthSid(pSidFromTokenInfo); + pSid = new (nothrow) BYTE[cbSid]; + if (pSid == NULL) + { + hr = E_OUTOFMEMORY; + } + else + { + if (!CopySid(cbSid, pSid, pSidFromTokenInfo)) + { + hr = HRESULT_FROM_GetLastError(); + goto exit; + } + } + + *ppSid = pSid; + pSid = NULL; + +exit: + if (hToken != INVALID_HANDLE_VALUE) + { + CloseHandle(hToken); + } + if (hProc != INVALID_HANDLE_VALUE) + { + // clean up + CloseHandle(hProc); + } + if (pvTokenInfo) + { + delete [] (reinterpret_cast<BYTE*>(pvTokenInfo)); + } + + if (pSid) + { + delete [] (reinterpret_cast<BYTE*>(pSid)); + } + + LOG((LF_CORDB, LL_INFO10000, + "SecurityUtil::GetSidFromProcess return hr : 0x%08x\n", + hr)); + + return hr; +} + +#ifndef FEATURE_CORESYSTEM +//----------------------------------------------------------------------------- +// get the sid of a given process id using WTSEnumerateProcesses +// @todo: Make this function fail when WTSEnumerateProcesses is not available +// Or is it always available on all of our platform? +// +// Caller remember to call delete on *ppSid +//----------------------------------------------------------------------------- +HRESULT GetSidFromProcessEXWorker(DWORD dwProcessId, PSID *ppSid) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + PRECONDITION(CheckPointer(ppSid)); + } + CONTRACTL_END; + + HRESULT hr = S_OK; + PWTS_PROCESS_INFOW rgProcessInfo = NULL; + DWORD dwNumProcesses; + DWORD iProc; + DWORD cbSid; + PSID pSid = NULL; + + LOG((LF_CORDB, LL_INFO10000, + "SecurityUtil::GetSidFromProcessEx: 0x%08x\n", + dwProcessId)); + + + *ppSid = NULL; + if (!WTSEnumerateProcessesW(WTS_CURRENT_SERVER_HANDLE, // use local server + 0, // Reserved must be zero + 1, // version must be 1 + &rgProcessInfo, // Receives pointer to process list + &dwNumProcesses)) + { + hr = HRESULT_FROM_GetLastError(); + goto exit; + } + + for (iProc = 0; iProc < dwNumProcesses; iProc++) + { + + if (rgProcessInfo[iProc].ProcessId == dwProcessId) + { + if (rgProcessInfo[iProc].pUserSid == NULL) + { + LOG((LF_CORDB, LL_INFO10000, + "SecurityUtil::GetSidFromProcessEx is not able to retreive SID\n")); + + // if there is no Sid for the user, don't call GetLengthSid. + // It will crash! It is ok to return E_FAIL as caller will ignore it. + hr = E_FAIL; + goto exit; + } + cbSid = GetLengthSid(rgProcessInfo[iProc].pUserSid); + pSid = new (nothrow) BYTE[cbSid]; + if (pSid == NULL) + { + hr = E_OUTOFMEMORY; + } + else + { + if (!CopySid(cbSid, pSid, rgProcessInfo[iProc].pUserSid)) + { + hr = HRESULT_FROM_GetLastError(); + } + else + { + // We are done. Go to exit + hr = S_OK; + } + } + + // we already find a match. Even if we fail from memory allocation of CopySid, still + // goto exit. + goto exit; + } + } + + // Walk the whole list and cannot find the matching PID + // Find a better error code. + hr = E_FAIL; + +exit: + + if (rgProcessInfo) + { + WTSFreeMemory(rgProcessInfo); + } + + if (FAILED(hr) && pSid) + { + delete [] (reinterpret_cast<BYTE*>(pSid)); + } + + if (SUCCEEDED(hr)) + { + _ASSERTE(pSid); + *ppSid = pSid; + } + LOG((LF_CORDB, LL_INFO10000, + "SecurityUtil::GetSidFromProcessEx return hr : 0x%08x\n", + hr)); + + + return hr; +} +#endif // !FEATURE_CORESYSTEM + +//----------------------------------------------------------------------------- +// The functions below initialize this SidBuffer instance with a Sid from +// the token of the specified process. The first pair use the OWNER sid from +// the process token if possible; else use the term serv API to find the +// USER sid from the process token. This seems a little inconsistent, but +// remains this way for backward compatibility. The second pair consistently +// use the USER sid (never the OWNER). +// +// While the USER and OWNER sid are often the same, they are not always the +// same. For example, running a process on win2k3 server as a member of the +// local admin group causes the USER sid to be the logged-on user, and the +// OWNER sid to be the local admins group. At least, that's how it was on +// Monday. Expect this to change randomly at unexpected times, as most +// security-related behavior does. +//----------------------------------------------------------------------------- + + +// ---------------------------------------------------------------------------- +// SidBuffer::InitFromProcessNoThrow +// +// Description: +// Initialize this SidBuffer instance with a Sid from the token of the specified +// process. Use the OWNER sid from the process token if possible; else use the term +// serv API to find the USER sid from the process token. This seems a little +// inconsistent, but remains this way for backward compatibility. +// +// Arguments: +// * pid - Process ID from which to grab the SID +// +// Return Value: +// HRESULT indicating success / failure +// + +HRESULT SidBuffer::InitFromProcessNoThrow(DWORD pid) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + _ASSERTE(m_pBuffer == NULL); + HRESULT hr = GetSidFromProcessWorker(pid, kOwnerSid, (PSID *) &m_pBuffer); +#ifndef FEATURE_CORESYSTEM + if (FAILED(hr)) + { + hr = GetSidFromProcessEXWorker(pid, (PSID *) &m_pBuffer); + } +#endif // !FEATURE_CORESYSTEM + if (FAILED(hr)) + { + return hr; + } + + _ASSERTE(m_pBuffer != NULL); + return S_OK; +} + +// See code:SidBuffer::InitFromProcessNoThrow. Throws if there's an error. +void SidBuffer::InitFromProcess(DWORD pid) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + } + CONTRACTL_END; + + HRESULT hr = InitFromProcessNoThrow(pid); + if (FAILED(hr)) + { + ThrowHR(hr); + } +} + +// ---------------------------------------------------------------------------- +// SidBuffer::InitFromProcessAppContainerSidNoThrow +// +// Description: +// Initialize this SidBuffer instance with the TokenAppContainerSid from +// the process token +// +// Arguments: +// * pid - Process ID from which to grab the SID +// +// Return Value: +// HRESULT indicating success / failure +// S_FALSE indicates the process isn't in an AppContainer +// +HRESULT SidBuffer::InitFromProcessAppContainerSidNoThrow(DWORD pid) +{ + HRESULT hr = S_OK; + HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); + if (hProcess == NULL) + { + hr = HRESULT_FROM_GetLastError(); + goto exit; + } + HANDLE hToken = NULL; + hr = OpenProcessToken(hProcess, TOKEN_QUERY, &hToken); + if (FAILED(hr)) + { + goto exit; + } + else + { + hr = S_OK; // not sure why, but OpenProcessToken can return S_FALSE + // we don't want to return S_FALSE by accident + } + + // Define new TOKEN_INFORMATION_CLASS/ TOKEN_APPCONTAINER_INFORMATION members for Win8 since they are not in the DevDiv copy of WinSDK yet + typedef enum _TOKEN_INFORMATION_CLASS_WIN8 { + TokenIsAppContainer = TokenLogonSid + 1, + TokenCapabilities, + TokenAppContainerSid + } TOKEN_INFORMATION_CLASS_WIN8; + + typedef struct _TOKEN_APPCONTAINER_INFORMATION + { + PSID TokenPackage; + } TOKEN_APPCONTAINER_INFORMATION, *PTOKEN_APPCONTAINER_INFORMATION; + + DWORD size; + BOOL fIsLowBox = FALSE; + if (!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIsAppContainer, &fIsLowBox, sizeof(fIsLowBox), &size)) + { + DWORD gle = GetLastError(); + if (gle == ERROR_INVALID_PARAMETER || gle == ERROR_INVALID_FUNCTION) + { + hr = S_FALSE; // We are on an OS which doesn't understand LowBox + } + else + { + hr = HRESULT_FROM_WIN32(gle); + } + goto exit; + } + + if (!fIsLowBox) + { + hr = S_FALSE; + goto exit; + } + + UCHAR PackSid[SECURITY_MAX_SID_SIZE + sizeof(TOKEN_APPCONTAINER_INFORMATION)]; + if (!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenAppContainerSid, &PackSid, sizeof(PackSid), &size)) + { + hr = HRESULT_FROM_GetLastError(); + goto exit; + } + + PTOKEN_APPCONTAINER_INFORMATION pTokPack = (PTOKEN_APPCONTAINER_INFORMATION)&PackSid; + PSID pLowBoxPackage = pTokPack->TokenPackage; + DWORD dwSidLen = GetLengthSid(pLowBoxPackage); + m_pBuffer = new (nothrow) BYTE[dwSidLen]; + if (m_pBuffer == NULL) + { + hr = E_OUTOFMEMORY; + goto exit; + } + else + { + if (!CopySid(dwSidLen, m_pBuffer, pLowBoxPackage)) + { + hr = HRESULT_FROM_GetLastError(); + delete m_pBuffer; + m_pBuffer = NULL; + goto exit; + } + } + +exit: + if (hProcess != NULL) + { + CloseHandle(hProcess); + } + if (hToken != NULL) + { + CloseHandle(hToken); + } + + return hr; +} + +// ---------------------------------------------------------------------------- +// SidBuffer::InitFromProcessUserNoThrow +// +// Description: +// Initialize this SidBuffer instance with a Sid from the token of the specified +// process. Use the USER sid from the process token if possible; else use the term +// serv API to find the USER sid from the process token. +// +// Arguments: +// * pid - Process ID from which to grab the SID +// +// Return Value: +// HRESULT indicating success / failure +// + +HRESULT SidBuffer::InitFromProcessUserNoThrow(DWORD pid) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + _ASSERTE(m_pBuffer == NULL); + HRESULT hr = GetSidFromProcessWorker(pid, kUserSid, (PSID *) &m_pBuffer); +#ifndef FEATURE_CORESYSTEM + if (FAILED(hr)) + { + hr = GetSidFromProcessEXWorker(pid, (PSID *) &m_pBuffer); + } +#endif // !FEATURE_CORESYSTEM + if (FAILED(hr)) + { + return hr; + } + + _ASSERTE(m_pBuffer != NULL); + return S_OK; +} + +// See code:SidBuffer::InitFromProcessUserNoThrow. Throws if there's an error. +void SidBuffer::InitFromProcessUser(DWORD pid) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + } + CONTRACTL_END; + + HRESULT hr = InitFromProcessUserNoThrow(pid); + if (FAILED(hr)) + { + ThrowHR(hr); + } +} + +//----------------------------------------------------------------------------- +// Ctor for Dacl class. Wraps a win32 dacl. +//----------------------------------------------------------------------------- +Dacl::Dacl(PACL pAcl) +{ + m_acl = pAcl; +} + +//----------------------------------------------------------------------------- +// Get number of ACE (Access Control Entries) in this DACL. +//----------------------------------------------------------------------------- +SIZE_T Dacl::GetAceCount() +{ + return (SIZE_T) m_acl->AceCount; +} + +//----------------------------------------------------------------------------- +// Get Raw a ACE at the given index. +// Caller assumes index is valid (0 <= dwAceIndex < GetAceCount()) +// Throws on error (which should only be if the index is out of bounds). +//----------------------------------------------------------------------------- +ACE_HEADER * Dacl::GetAce(SIZE_T dwAceIndex) +{ + CONTRACTL { + THROWS; + GC_NOTRIGGER; + } CONTRACTL_END; + + ACE_HEADER * pAce = NULL; + BOOL fOk = ::GetAce(m_acl, (DWORD) dwAceIndex, (LPVOID*) &pAce); + _ASSERTE(fOk == (pAce != NULL)); + if (!fOk) + { + ThrowLastError(); + } + return pAce; +} + + + +//----------------------------------------------------------------------------- +// Ctor for SecurityDescriptor +//----------------------------------------------------------------------------- +Win32SecurityDescriptor::Win32SecurityDescriptor() +{ + m_pDesc = NULL; +} + +//----------------------------------------------------------------------------- +// Dtor for security Descriptor. +//----------------------------------------------------------------------------- +Win32SecurityDescriptor::~Win32SecurityDescriptor() +{ + delete [] ((BYTE*) m_pDesc); +} + + + +//----------------------------------------------------------------------------- +// Get the dacl for this security descriptor. +//----------------------------------------------------------------------------- +Dacl Win32SecurityDescriptor::GetDacl() +{ + CONTRACTL { + THROWS; + GC_NOTRIGGER; + } CONTRACTL_END; + + _ASSERTE(m_pDesc != NULL); + + BOOL bPresent; + BOOL bDaclDefaulted; + PACL acl; + + if (GetSecurityDescriptorDacl(m_pDesc, &bPresent, &acl, &bDaclDefaulted) == 0) + { + ThrowLastError(); + } + if (!bPresent) + { + // No dacl. We consider this an error because all of the objects we expect + // to see should be dacled. If it's not dacled, then it's a malicious user spoofing it. + ThrowHR(E_INVALIDARG); + } + + Dacl d(acl); + return d; +} + +//----------------------------------------------------------------------------- +// Get the owner from the security descriptor. +//----------------------------------------------------------------------------- +HRESULT Win32SecurityDescriptor::GetOwnerNoThrow( PSID* ppSid) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + _ASSERTE(m_pDesc != NULL); + BOOL bOwnerDefaulted; + + if( ppSid == NULL ) + { + return E_INVALIDARG; + } + + if (GetSecurityDescriptorOwner(m_pDesc, ppSid, &bOwnerDefaulted) == 0) + { + DWORD err = GetLastError(); + return HRESULT_FROM_WIN32(err); + } + + return S_OK; +} +Sid Win32SecurityDescriptor::GetOwner() +{ + CONTRACTL { + THROWS; + GC_NOTRIGGER; + } CONTRACTL_END; + + PSID pSid; + HRESULT hr = GetOwnerNoThrow( &pSid ); + if( FAILED(hr) ) + { + ThrowHR( hr ); + } + + Sid s(pSid); + return s; +} + +//----------------------------------------------------------------------------- +// Initialize this instance of a SecurityDescriptor with the SD for the handle. +// The handle must ahve READ_CONTROL permissions to do this. +// Throws on error. +//----------------------------------------------------------------------------- +HRESULT Win32SecurityDescriptor::InitFromHandleNoThrow(HANDLE h) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + _ASSERTE(m_pDesc == NULL); // only init once. + + DWORD cbNeeded = 0; + + DWORD flags = OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION; + + // Now get the creator's SID. First get the size of the array needed. + BOOL fOk = GetKernelObjectSecurity(h, flags, NULL, 0, &cbNeeded); + DWORD err = GetLastError(); + + // Caller should give us a handle for which this succeeds. First call will + // fail w/ InsufficientBuffer. + CONSISTENCY_CHECK_MSGF(fOk || (err == ERROR_INSUFFICIENT_BUFFER), ("Failed to get KernelSecurity for object handle=%p.Err=%d\n", h, err)); + + PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR) new(nothrow) BYTE[cbNeeded]; + if( pSD == NULL ) + { + return E_OUTOFMEMORY; + } + + if (GetKernelObjectSecurity(h, flags, pSD, cbNeeded, &cbNeeded) == 0) + { + // get last error and fail out. + err = GetLastError(); + delete [] ((BYTE*) pSD); + return HRESULT_FROM_WIN32(err); + } + + m_pDesc = pSD; + return S_OK; +} +void Win32SecurityDescriptor::InitFromHandle(HANDLE h) +{ + CONTRACTL { + THROWS; + GC_NOTRIGGER; + } CONTRACTL_END; + + HRESULT hr = InitFromHandleNoThrow(h); + if (FAILED(hr)) + { + ThrowHR(hr); + } +} + +//----------------------------------------------------------------------------- +// We open several named kernel objects that are well-known names decorated with +// pid of some target process (usually a debuggee). +// Since anybody can create any kernel object with any name, we we want to make +// sure the objects we're opening were actually created by who we think they +// were. Each kernel object has an "owner" property which serves as the +// fingerprints of who created the handle. +// +// Check if the handle owner belongs to either the process specified by the +// pid or the current process (in case the target process is impersonating us). +// This lets us know if the handle is spoofed. +// +// Parameters: +// handle - handle for kernel object to test +// pid - target process that it may belong to. +// +// Returns: +// false- if we can verify that Owner(handle) is in the set of { Owner(Process(pid)), or Owner(this Process) } +// +// +// true - Elsewise, including if we can't verify that it's false. +//----------------------------------------------------------------------------- +bool IsHandleSpoofed(HANDLE handle, DWORD pid) +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + _ASSERTE(handle != NULL); + _ASSERTE(pid != 0); + bool fIsSpoofed = true; + + EX_TRY + { + // Get the owner of the kernel object referenced by the handle. + Win32SecurityDescriptor sdHandle; + sdHandle.InitFromHandle(handle); + Sid sidOwner(sdHandle.GetOwner()); + + SidBuffer sbPidOther; + SidBuffer sbPidThis; + + // Is the object owner the "other" pid? + sbPidOther.InitFromProcess(pid); + if (Sid::Equals(sbPidOther.GetSid(), sidOwner)) + { + // We now know that the kernel object was created by the user of the "other" pid. + // This should be the common case by far. It's not spoofed. All is well. + fIsSpoofed = false; + goto Label_Done; + } + + // Test against our current pid if it's different than the "other" pid. + // This can happen if the other process impersonates us. The most common case would + // be if we're an admin and the other process (say some service) is impersonating Admin + // when it spins up the CLR. + DWORD pidThis = GetCurrentProcessId(); + if (pidThis != pid) + { + sbPidThis.InitFromProcess(pidThis); + if (Sid::Equals(sbPidThis.GetSid(), sidOwner)) + { + // The object was created by somebody pretending to be us. If they had sufficient permissions + // to pretend to be us, then we still trust them. + fIsSpoofed = false; + goto Label_Done; + } + } + + // This should only happen if we're being attacked. + _ASSERTE(fIsSpoofed); + STRESS_LOG2(LF_CORDB, LL_INFO1000, "Security Check failed with mismatch. h=%x,pid=%x", handle, pid); + +Label_Done: + ; + } + EX_CATCH + { + // This should only happen if something goes really bad and we can't find the information. + STRESS_LOG2(LF_CORDB, LL_INFO1000, "Security Check failed with exception. h=%x,pid=%x", handle, pid); + _ASSERTE(fIsSpoofed); // should still have its default value + } + EX_END_CATCH(SwallowAllExceptions); + + return fIsSpoofed; +} diff --git a/src/utilcode/sigbuilder.cpp b/src/utilcode/sigbuilder.cpp new file mode 100644 index 0000000000..0fb837d934 --- /dev/null +++ b/src/utilcode/sigbuilder.cpp @@ -0,0 +1,166 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + + +#include "stdafx.h" +#include "sigbuilder.h" +#include "ex.h" + +void SigBuilder::AppendByte(BYTE b) +{ + STANDARD_VM_CONTRACT; + + Ensure(1); + m_pBuffer[m_dwLength++] = b; +} + +void SigBuilder::AppendData(ULONG data) +{ + STANDARD_VM_CONTRACT; + + // + // Inlined logic from CorSigCompressData + // + + if (data <= 0x7F) + { + Ensure(1); + m_pBuffer[m_dwLength++] = BYTE(data); + return; + } + + if (data <= 0x3FFF) + { + Ensure(2); + + DWORD dwLength = m_dwLength; + BYTE * pBuffer = m_pBuffer; + + pBuffer[dwLength] = BYTE((data >> 8) | 0x80); + pBuffer[dwLength+1] = BYTE(data); + + m_dwLength = dwLength + 2; + return; + } + + if (data <= 0x1FFFFFFF) + { + Ensure(4); + + DWORD dwLength = m_dwLength; + BYTE * pBuffer = m_pBuffer; + + pBuffer[dwLength] = BYTE((data >> 24) | 0xC0); + pBuffer[dwLength+1] = BYTE(data >> 16); + pBuffer[dwLength+2] = BYTE(data >> 8); + pBuffer[dwLength+3] = BYTE(data); + + m_dwLength = dwLength + 4; + return; + } + + // We currently can only represent to 0x1FFFFFFF. + ThrowHR(COR_E_OVERFLOW); +} + +void SigBuilder::AppendToken(mdToken tk) +{ + STANDARD_VM_CONTRACT; + + // + // Inlined logic from CorSigCompressToken + // + + RID rid = RidFromToken(tk); + ULONG32 ulTyp = TypeFromToken(tk); + + _ASSERTE(rid <= 0x3FFFFFF); + rid = (rid << 2); + + // TypeDef is encoded with low bits 00 + // TypeRef is encoded with low bits 01 + // TypeSpec is encoded with low bits 10 + // BaseType is encoded with low bit 11 + // + if (ulTyp == g_tkCorEncodeToken[0]) + { + // make the last two bits 00 + // nothing to do + } + else if (ulTyp == g_tkCorEncodeToken[1]) + { + // make the last two bits 01 + rid |= 0x1; + } + else if (ulTyp == g_tkCorEncodeToken[2]) + { + // make last two bits 0 + rid |= 0x2; + } + else if (ulTyp == g_tkCorEncodeToken[3]) + { + rid |= 0x3; + } + else + { + ThrowHR(COR_E_BADIMAGEFORMAT); + } + + AppendData(rid); +} + +void SigBuilder::AppendBlob(const PVOID pBlob, SIZE_T cbBlob) +{ + STANDARD_VM_CONTRACT; + + Ensure(cbBlob); + memcpy(m_pBuffer + m_dwLength, pBlob, cbBlob); + m_dwLength += (DWORD)cbBlob; +} + +void SigBuilder::Grow(SIZE_T cbMin) +{ + STANDARD_VM_CONTRACT; + + DWORD dwNewAllocation = max(m_dwLength + (DWORD)cbMin, 2 * m_dwAllocation); + + // Overflow checks + if (dwNewAllocation < m_dwLength || (dwNewAllocation - m_dwLength) < cbMin) + ThrowOutOfMemory(); + + BYTE * pNewAllocation = new BYTE[dwNewAllocation]; + memcpy(pNewAllocation, m_pBuffer, m_dwLength); + + BYTE * pOldAllocation = m_pBuffer; + + m_pBuffer = pNewAllocation; + m_dwAllocation = dwNewAllocation; + + if (pOldAllocation != m_prealloc) + delete [] pOldAllocation; +} + +SigBuilder::~SigBuilder() +{ + if (m_pBuffer != m_prealloc) + delete [] m_pBuffer; +} + +SigBuilder::SigBuilder(DWORD cbPreallocationSize) +{ + STANDARD_VM_CONTRACT; + + m_dwLength = 0; + if (cbPreallocationSize <= sizeof(m_prealloc)) + { + m_pBuffer = m_prealloc; + m_dwAllocation = sizeof(m_prealloc); + } + else + { + m_pBuffer = new BYTE[cbPreallocationSize]; + m_dwAllocation = cbPreallocationSize; + } +} diff --git a/src/utilcode/sigparser.cpp b/src/utilcode/sigparser.cpp new file mode 100644 index 0000000000..b7f683ca43 --- /dev/null +++ b/src/utilcode/sigparser.cpp @@ -0,0 +1,197 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// +// sigparser.cpp +// + +// +// Signature parsing code +// +#include "stdafx.h" +#include "sigparser.h" +#include "contract.h" + +HRESULT SigParser::SkipExactlyOne() +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + SUPPORTS_DAC; + } + CONTRACTL_END + + CorElementType typ; + HRESULT hr = GetElemType(&typ); + + IfFailRet(hr); + + if (!CorIsPrimitiveType(typ)) + { + switch (typ) + { + default: + // _ASSERT(!"Illegal or unimplement type in COM+ sig."); + return META_E_BAD_SIGNATURE; + break; + case ELEMENT_TYPE_VAR: + case ELEMENT_TYPE_MVAR: + IfFailRet(GetData(NULL)); // Skip variable number + break; + case ELEMENT_TYPE_VAR_ZAPSIG: + IfFailRet(GetData(NULL)); // Skip RID + break; + case ELEMENT_TYPE_OBJECT: + case ELEMENT_TYPE_STRING: + case ELEMENT_TYPE_TYPEDBYREF: + case ELEMENT_TYPE_CANON_ZAPSIG: + break; + + case ELEMENT_TYPE_BYREF: //fallthru + case ELEMENT_TYPE_PTR: + case ELEMENT_TYPE_PINNED: + case ELEMENT_TYPE_SZARRAY: + case ELEMENT_TYPE_NATIVE_ARRAY_TEMPLATE_ZAPSIG: + case ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG: + IfFailRet(SkipExactlyOne()); // Skip referenced type + break; + + case ELEMENT_TYPE_VALUETYPE: //fallthru + case ELEMENT_TYPE_CLASS: + IfFailRet(GetToken(NULL)); // Skip RID + break; + + case ELEMENT_TYPE_MODULE_ZAPSIG: + IfFailRet(GetData(NULL)); // Skip index + IfFailRet(SkipExactlyOne()); // Skip type + break; + + case ELEMENT_TYPE_FNPTR: + IfFailRet(SkipSignature()); + break; + + case ELEMENT_TYPE_ARRAY: + { + IfFailRet(SkipExactlyOne()); // Skip element type + ULONG rank; + IfFailRet(GetData(&rank)); // Get rank + if (rank) + { + ULONG nsizes; + IfFailRet(GetData(&nsizes)); // Get # of sizes + while (nsizes--) + { + IfFailRet(GetData(NULL)); // Skip size + } + + ULONG nlbounds; + IfFailRet(GetData(&nlbounds)); // Get # of lower bounds + while (nlbounds--) + { + IfFailRet(GetData(NULL)); // Skip lower bounds + } + } + + } + break; + + case ELEMENT_TYPE_SENTINEL: + // Should be unreachable since GetElem strips it + break; + + case ELEMENT_TYPE_INTERNAL: + IfFailRet(GetPointer(NULL)); + break; + + case ELEMENT_TYPE_GENERICINST: + IfFailRet(SkipExactlyOne()); // Skip generic type + ULONG argCnt; + IfFailRet(GetData(&argCnt)); // Get number of parameters + while (argCnt--) + { + IfFailRet(SkipExactlyOne()); // Skip the parameters + } + break; + + } + } + + return hr; +} + +//--------------------------------------------------------------------------------------- +// +// Skip only a method header signature - not the sigs of the args to the method. +// +HRESULT +SigParser::SkipMethodHeaderSignature( + ULONG * pcArgs) +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + SUPPORTS_DAC; + } + CONTRACTL_END + + HRESULT hr = S_OK; + + // Skip calling convention + ULONG uCallConv; + IfFailRet(GetCallingConvInfo(&uCallConv)); + + if ((uCallConv == IMAGE_CEE_CS_CALLCONV_FIELD) || + (uCallConv == IMAGE_CEE_CS_CALLCONV_LOCAL_SIG)) + { + return META_E_BAD_SIGNATURE; + } + + // Skip type parameter count + if (uCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC) + IfFailRet(GetData(NULL)); + + // Get arg count; + IfFailRet(GetData(pcArgs)); + + // Skip return type; + IfFailRet(SkipExactlyOne()); + + return hr; +} // SigParser::SkipMethodHeaderSignature + +//--------------------------------------------------------------------------------------- +// +// Skip a sub signature (as immediately follows an ELEMENT_TYPE_FNPTR). +HRESULT SigParser::SkipSignature() +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + SUPPORTS_DAC; + } + CONTRACTL_END + + HRESULT hr = S_OK; + + ULONG cArgs; + + IfFailRet(SkipMethodHeaderSignature(&cArgs)); + + // Skip args. + while (cArgs) { + IfFailRet(SkipExactlyOne()); + cArgs--; + } + + return hr; +} diff --git a/src/utilcode/sortversioning.cpp b/src/utilcode/sortversioning.cpp new file mode 100644 index 0000000000..6b9f56cc8c --- /dev/null +++ b/src/utilcode/sortversioning.cpp @@ -0,0 +1,1139 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//////////////////////////////////////////////////////////////////////////// +// +// File: SortVersioning.cpp +// + + +// Purpose: Provides access of the sort versioning functionality on +// downlevel (pre-Win7) machines. +// +// +// This is not used on CoreCLR, where we always go to the OS +// for sorting. +// +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "sortversioning.h" +#include "newapis.h" + +#include "mscoree.h" +#include "clrconfig.h" + +#define SORT_VERSION_V4 0x00060101 +#define SORT_VERSION_WHIDBEY 0x00001000 +#define SORT_VERSION_DEFAULT SORT_VERSION_V4 +#define SORT_DEFAULT_DLL_NAME MAKEDLLNAME(W("nlssorting")) + +namespace SortVersioning +{ +#define SORT_HASH_TBL_SIZE 128 + + // + // Forward Declarations + // + PSORTHANDLE MakeSortHashNode( + __in LPCWSTR pSortName, + __in DWORD dwVersion); + + + PSORTHANDLE InsertSortHashNode( + __in PSORTHANDLE pHashN); + + static PSORTHANDLE g_pSortHash[SORT_HASH_TBL_SIZE]; // Sort node hash table + + static HMODULE g_hSortDefault = (HMODULE)-1; + + __encoded_pointer static SORTGETHANDLE g_pDefaultGetHandle; + __encoded_pointer static SORTCLOSEHANDLE g_pDefaultCloseHandle; + + static HMODULE g_hSortCompatV2 = (HMODULE)-1; + + __encoded_pointer static SORTGETHANDLE g_pV2GetHandle; + __encoded_pointer static SORTCLOSEHANDLE g_pV2CloseHandle; + + static HMODULE g_hSortCompatV4 = (HMODULE)-1; + + __encoded_pointer static SORTGETHANDLE g_pV4GetHandle; + __encoded_pointer static SORTCLOSEHANDLE g_pV4CloseHandle; + + + //////////////////////////////////////////////////////////////////////////// + // + // NlsCompareInvariantNoCase + // + // This routine does fast caseless comparison without needing the tables. + // This helps us do the comparisons we need to load the tables :-) + // + // Returns 0 if identical, <0 if pFirst if first string sorts first. + // + // This is only intended to help with our locale name comparisons, + // which are effectively limited to A-Z, 0-9, a-z and - where A-Z and a-z + // compare as equal. + // + // WARNING: [\]^_` will be less than A-Z because we make everything lower + // case before comparing them. + // + // When bNullEnd is TRUE, both of the strings should be null-terminator to be considered equal. + // When bNullEnd is FALSE, the strings are considered equal when we reach the number of characters specifed by size + // or when null terminators are reached, whichever happens first (strncmp-like behavior) + // + //////////////////////////////////////////////////////////////////////////// + int NlsCompareInvariantNoCase( + LPCWSTR pFirst, + LPCWSTR pSecond, + int size, + BOOL bNullEnd) + { + int i=0; + WCHAR first; + WCHAR second; + + for (; + size > 0 && (first = *pFirst) != 0 && (second = *pSecond) != 0; + size--, pFirst++, pSecond++) + { + // Make them lower case + if ((first >= 'A') && (first <= 'Z')) first |= 0x20; + if ((second >= 'A') && (second <= 'Z')) second |= 0x20; + + // Get the diff + i = (first - second); + + // Are they the same? + if (i == 0) + continue; + + // Otherwise the difference. Remember we made A-Z into lower case, so + // the characters [\]^_` will sort < A-Z and also < a-z. (Those are the + // characters between A-Z and a-Z in ascii) + return i; + } + + // When we are here, one of these holds: + // size == 0 + // or one of the strings has a null terminator + // or both of the string reaches null terminator + + if (bNullEnd || size != 0) + { + // If bNullEnd is TRUE, always check for null terminator. + // If bNullEnd is FALSE, we still have to check if one of the strings is terminated eariler + // than another (hense the size != 0 check). + + // See if one string ended first + if (*pFirst != 0 || *pSecond != 0) + { + // Which one? + return *pFirst == 0 ? -1 : 1; + } + } + + // Return our difference (0) + return i; + } + +#if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE) + //////////////////////////////////////////////////////////////////////////// + // + // LoadSortModuleAndInvariant() + // + // Attempts to load the given dll. If that is successful, attempts to + // load the invariant data. If that is successful, returns the module handle + // and the addresses of the SortGetHandle function and SortCloseHandle function + // + // failure is indicated by returning NULL for the module handle and the + // function addresses + // + //////////////////////////////////////////////////////////////////////////// + + HMODULE LoadSortModuleAndInvariant( + __in LPCWSTR sDllName, + __in DWORD dwVersion, + __out SORTGETHANDLE* ppGetHandle, + __out SORTCLOSEHANDLE* ppCloseHandle + ) + { + *ppGetHandle = NULL; + *ppCloseHandle = NULL; + HMODULE hSort; + + if(FAILED(UtilCode::LoadLibraryShim(sDllName, NULL, NULL, &hSort))) + { + return NULL; + } + + SORTGETHANDLE pGetHandle = (SORTGETHANDLE)GetProcAddress(hSort, "SortGetHandle"); + SORTCLOSEHANDLE pCloseHandle = (SORTCLOSEHANDLE)GetProcAddress(hSort, "SortCloseHandle"); + + // If we didn't load the procs, then remember that + if (pCloseHandle == NULL || pGetHandle == NULL) + { + ::FreeLibrary(hSort); + return NULL; + } + + // Verify that the data file's available + NLSVERSIONINFO sortVersion; + sortVersion.dwNLSVersionInfoSize = sizeof(NLSVERSIONINFO); + sortVersion.dwNLSVersion = dwVersion; + sortVersion.dwDefinedVersion = dwVersion; + + // Invariant must be there and is kinda common, so we'll just get it + PSORTHANDLE pSort = pGetHandle(W(""), &sortVersion, NULL); + if (!pSort) + { + // Yikes, invariant failed, forget about it. + ::FreeLibrary(hSort); + return NULL; + } + + // Since we found it, may as well remember it. + const SORTHANDLE * const pSortInHash = InsertSortHashNode(pSort); + + // If we got a different one back then free the one we added + if (pSortInHash != pSort && pSortInHash) + { + // We got a different one from the hash (someone beat us to the cache) + // so use that and discard the new one. + pCloseHandle(pSort); + } + + *ppGetHandle = pGetHandle; + *ppCloseHandle = pCloseHandle; + return hSort; + } + + // Attempts to load a Sort DLL. If this fails, the values of the __out parameters are unchanged. + BOOL LoadSortDllAndPublish( + __in LPCWSTR sDllName, + __in DWORD dwVersion, + __out __encoded_pointer SORTGETHANDLE* ppGetHandle, + __out __encoded_pointer SORTCLOSEHANDLE* ppCloseHandle, + __out HMODULE* phSortDll) + { + HMODULE hSortDll; + SORTGETHANDLE pGetHandle; + SORTCLOSEHANDLE pCloseHandle; + + hSortDll = LoadSortModuleAndInvariant(sDllName, dwVersion, &pGetHandle, &pCloseHandle); + + if(hSortDll != NULL) { + *phSortDll = hSortDll; + *ppGetHandle = (SORTGETHANDLE)EncodePointer(pGetHandle); + *ppCloseHandle = (SORTCLOSEHANDLE)EncodePointer(pCloseHandle); + return TRUE; + } + + return FALSE; + } + + //////////////////////////////////////////////////////////////////////////// + // + // GetSortGetHandle() + // + // Get the SortGetHandle() function for the proper dll version. + // + //////////////////////////////////////////////////////////////////////////// + SORTGETHANDLE GetSortGetHandle(__in DWORD dwVersion) + { + if(dwVersion == SORT_VERSION_DEFAULT) + { + // If we haven't tried to load the module/proc before do so now + if (g_hSortDefault == (HMODULE)-1) + { + LoadSortDllAndPublish(SORT_DEFAULT_DLL_NAME, SORT_VERSION_DEFAULT, &g_pDefaultGetHandle, &g_pDefaultCloseHandle, &g_hSortDefault); + } + + // This check is necessary because the LoadSortDllAndPublish call may have failed since some platforms + // won't have nlssorting.dll (e.g. Windows 8 and above). + if (g_hSortDefault != (HMODULE)-1) + { + return (SORTGETHANDLE)DecodePointer(g_pDefaultGetHandle); + } + } + + HMODULE* pHSortModule; + SORTGETHANDLE* ppGetHandle; + SORTCLOSEHANDLE* ppCloseHandle; + + if(dwVersion == SORT_VERSION_V4) + { + ppGetHandle = &g_pV4GetHandle; + ppCloseHandle = &g_pV4CloseHandle; + pHSortModule = &g_hSortCompatV4; + } + else if(dwVersion == SORT_VERSION_WHIDBEY) + { + ppGetHandle = &g_pV2GetHandle; + ppCloseHandle = &g_pV2CloseHandle; + pHSortModule = &g_hSortCompatV2; + } + else + { + // Unsupported sorting version. + return NULL; + } + + if(*pHSortModule == (HMODULE) -1) + { + // get module name - the module name should be "Sort"+dwVersion.ToString("x8")+".dll" + WCHAR moduleName[] = W("Sort00000000.dll"); + // replace the "00000000" with the hexadecimal of dwVersion + LPCWSTR hex = W("0123456789abcdef"); + WCHAR* p = &moduleName[4+8]; // position at end of number part of dll name + + unsigned int value = dwVersion; + + while (value != 0 && p != moduleName) { + int digit = value & 0xF; + *--p = hex[digit]; + value >>= 4; + } + + if(!LoadSortDllAndPublish(&moduleName[0], dwVersion, ppGetHandle, ppCloseHandle, pHSortModule)) + { + // We failed to load a versioned sort dll, try to fall back to the current version. + // If we haven't tried to load the module/proc before do so now + if (g_hSortDefault == (HMODULE)-1) + { + LoadSortDllAndPublish(SORT_DEFAULT_DLL_NAME, SORT_VERSION_DEFAULT, &g_pDefaultGetHandle, &g_pDefaultCloseHandle, &g_hSortDefault); + } + + *pHSortModule = g_hSortDefault; + *ppCloseHandle = g_pDefaultCloseHandle; + *ppGetHandle = g_pDefaultGetHandle; + } + } + + // At this point, we've either loaded a sorting dll or we've exausted all options. + if(*pHSortModule == (HMODULE) -1) + { + // Couldn't find anything, give up. + return NULL; + } + else + { + return (SORTGETHANDLE)DecodePointer(*ppGetHandle); + } + + // Unknown version requested + return NULL; + } + + void DoSortCloseHandle(__in DWORD dwVersion, __in PSORTHANDLE pSort) + { + if (dwVersion == SORT_VERSION_DEFAULT) + { + SORTCLOSEHANDLE pDefaultCloseHandle = (SORTCLOSEHANDLE)DecodePointer(g_pDefaultCloseHandle); + if(pDefaultCloseHandle != NULL) + { + (pDefaultCloseHandle)(pSort); + return; + } + } + + if (dwVersion == SORT_VERSION_V4) + { + SORTCLOSEHANDLE pV4CloseHandle = (SORTCLOSEHANDLE)DecodePointer(g_pV4CloseHandle); + if(pV4CloseHandle != NULL) + { + (pV4CloseHandle)(pSort); + return; + } + } + + + if (dwVersion == SORT_VERSION_WHIDBEY) + { + SORTCLOSEHANDLE pV2CloseHandle = (SORTCLOSEHANDLE)DecodePointer(g_pV2CloseHandle); + if(pV2CloseHandle != NULL) + { + (pV2CloseHandle)(pSort); + return; + } + } + } + +#else // !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE) + + SORTGETHANDLE GetSortGetHandle(__in DWORD dwVersion) + { + return NULL; + } + + void DoSortCloseHandle(__in DWORD dwVersion, __in PSORTHANDLE pSort) + { + } + +#endif // !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE) + + //////////////////////////////////////////////////////////////////////////// + // + // GetSortHashValue + // + // Returns the hash value for given sort name & version. + // + // WARNING: This must be case insensitive. Currently we're expecting only + // a-z, A-Z, 0-9 & -. + // + //////////////////////////////////////////////////////////////////////////// + __inline __range(0, SORT_HASH_TBL_SIZE-1) int GetSortHashValue( + __in LPCWSTR pSortName, + __in DWORD dwVersion) + { + int iHash = 12; // Seed hash value + int iMax; // Number of characters to count (prevent problems with too-bad strings) + + // Hash the string + if (pSortName) + { + for (iMax = 10; *pSortName != 0 && iMax != 0; pSortName++, iMax--) + { + iHash <<= 1; + iHash ^= ((*pSortName) & 0xdf); // 0x20 will make cases be the same (and other wierd stuff too, but we don't care about that) + } + } + + // Add the version hash + // (the middle 2 bytes are most interesting) + iHash ^= dwVersion >> 8; + + // Mix up our bits and hash it with 128 + _ASSERT(SORT_HASH_TBL_SIZE == 128); + return (iHash + (iHash >> 8)) & 0x7f; + } + + + //////////////////////////////////////////////////////////////////////////// + // + // InsertSortHashNode + // + // Inserts a sort hash node into the global sort hash tables. It assumes + // that all unused hash values in the table are pointing to NULL. If + // there is a collision, the new node will be added LAST in the list. + // (Presuming that the most often used are also the first used) + // + // We do an interlocked exchange and free the pointer if we can't add it. + // + // Warning: We stick stuff in this list, but we never remove it, so it + // get kind of big. Removing entries would be difficult however + // because it would require some sort of synchronization with the + // reader functions (like GetLocaleInfo), or maybe an in-use flag + // or spin count. + // + //////////////////////////////////////////////////////////////////////////// + PSORTHANDLE InsertSortHashNode(PSORTHANDLE pHashN) + { + __range(0, SORT_HASH_TBL_SIZE-1) UINT index; + PSORTHANDLE pSearch; + PSORTHANDLE* pNextToUpdate; + + // + // Insert the hash node into the list (by name/version) + // +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable: 26037) // Prefast warning - Possible precondition violation due to failure to null terminate string - GetSortHashValue only uses first 10 characters and sortName is null terminated +#endif // _PREFAST_ + index = GetSortHashValue(pHashN->sortName, pHashN->dwNLSVersion); +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + + // Get hash node + pSearch = g_pSortHash[index]; + + // Remember last pointer in case we need to add it + pNextToUpdate = &g_pSortHash[index]; + + // We'll be the last node when added + pHashN->pNext = NULL; + + while(TRUE) + { + while (pSearch != NULL) + { + // See if we already found a node. + if ((pSearch->dwNLSVersion == pHashN->dwNLSVersion) && + NlsCompareInvariantNoCase( pSearch->sortName, pHashN->sortName, + LOCALE_NAME_MAX_LENGTH, TRUE) == 0) + { + // Its the same, which is unexpected, return the old one + return pSearch; + } + + pNextToUpdate = &pSearch->pNext; + pSearch = pSearch->pNext; + } + + // At end, try to add our node + pSearch = InterlockedCompareExchangeT(pNextToUpdate, pHashN, NULL); + + // If pNextToUpdate isn't NULL then another process snuck in and updated the list + // while we were getting ready. + if (pSearch == NULL) + { + // It was added, stop + break; + } + + // It wasn't added, pSearch now points to a new node that snuck in, so + // continue and try that one. This should be really rare, even in a busy + // loop, so we don't try a real lock. Either + // a) the snuck in node is the same as pHashN, and we'll return pSearch + // in the first loop, or + // b) the snuck in node is new, in which case we'll try to readd. Very worst + // case we'd collide while someone added ALL of the other locales with our + // hash, but eventually we'd hit case a. (And there's only a couple hundred + // tries, so this can't lock for long.) + } + + // Return the same one we added + return pHashN; + } + + + //////////////////////////////////////////////////////////////////////////// + // + // FindSortHashNode + // + // Searches for the sort hash node for the given sort name & version. + // The result is returned. If none are found NULL is returned. + // + // NOTE: Call GetSortNode() which calls this. + // + // Defined as inline. + // + //////////////////////////////////////////////////////////////////////////// + + __inline PSORTHANDLE FindSortHashNode( + __in LPCWSTR pSortName, + __in DWORD dwVersion) + { + PSORTHANDLE pHashN; + __range(0,SORT_HASH_TBL_SIZE-1) int index; + + // Get Index + index = GetSortHashValue(pSortName, dwVersion); + + // Get hash node + pHashN = g_pSortHash[index]; + + // Look through the list to see if one matches name and user info + // We're sneaky here because we know our length of our hash name string is stored + // just before that string. + while ((pHashN != NULL) && + ((dwVersion != pHashN->dwNLSVersion) || + (NlsCompareInvariantNoCase(pSortName, pHashN->sortName, LOCALE_NAME_MAX_LENGTH, TRUE) != 0))) + { + pHashN = pHashN->pNext; + } + + return pHashN; + } + + + //////////////////////////////////////////////////////////////////////////// + // + // MakeSortHashNode + // + // Builds a sort hash node and sticks it in the hash table. + // + // NOTE: Call GetSortNode() which calls this. + // + // Defined as inline. + // + //////////////////////////////////////////////////////////////////////////// + PSORTHANDLE MakeSortHashNode( + __in LPCWSTR pSortName, + __in DWORD dwVersion) + { + NLSVERSIONINFO sortVersion; + + PSORTHANDLE pSort = NULL; + PSORTHANDLE pSortInHash; + + // Valid locale, now we need to find out where to point this version at + SORTGETHANDLE pGetHandle = GetSortGetHandle(dwVersion); + if (pGetHandle == NULL) return NULL; + + sortVersion.dwNLSVersionInfoSize = sizeof(NLSVERSIONINFO); + sortVersion.dwNLSVersion = dwVersion; + sortVersion.dwDefinedVersion = dwVersion; + + pSort = pGetHandle(pSortName, &sortVersion, NULL); + + // If still missing, fail + if (pSort == NULL) + { + // Invalid sort, fail + return NULL; + } + + // Now we need to add it + pSortInHash = InsertSortHashNode(pSort); + + // If we got a different one back then free the one we added + if (pSortInHash != pSort && pSortInHash) + { + // We got a different one from the hash (someone beat us to the cache) + // so use that and discard the new one. + DoSortCloseHandle(dwVersion, pSort); + } + + return pSortInHash; + } + + + //////////////////////////////////////////////////////////////////////////// + // + // GetSortNode + // + // Get a sort hash node for the specified sort name & version + // + //////////////////////////////////////////////////////////////////////////// + PSORTHANDLE GetSortNode( + __in LPCWSTR pSortName, + __in DWORD dwVersion) + { + PSORTHANDLE pSortHashN = NULL; + + // WARNING: We don't bother doing the null/default/system checks + + // Didn't have an obvious one, look in the hash table + pSortHashN = FindSortHashNode(pSortName, dwVersion); + + // + // If the hash node does not exist, we may need to get make one + // + if (pSortHashN == NULL) + { + // + // Hash node does NOT exist, try to make it + + // + pSortHashN = MakeSortHashNode(pSortName, dwVersion); + } + + // + // If the hash node still does not exist, we may need to fallback to default + // version + // + if (pSortHashN == NULL && dwVersion != SORT_VERSION_DEFAULT) + { + return GetSortNode(pSortName, SORT_VERSION_DEFAULT); + } + + // + // Return pointer to hash node + // (null if we still don't have one) + // + return pSortHashN; + } + + //////////////////////////////////////////////////////////////////////////// + // + // SortNLSVersion + // Check for the DWORD "CompatSortNLSVersion" CLR config option. + // + // .Net 4.0 introduces sorting changes that can affect the behavior of any of the methods + // in CompareInfo. To mitigate against compatibility problems Applications can enable the + // legacy CompareInfo behavior by using the 'SortNLSVersion' configuration option + // + // There are three ways to use the configuration option: + // + // 1) Config file (MyApp.exe.config) + // <?xml version ="1.0"?> + // <configuration> + // <runtime> + // <CompatSortNLSVersion enabled="4096"/><!--0x00001000 --> + // </runtime> + // </configuration> + // 2) Environment variable + // set COMPLUS_CompatSortNLSVersion=4096 + // 3) RegistryKey + // [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework] + // "CompatSortNLSVersion"=dword:00001000 + // + //////////////////////////////////////////////////////////////////////////// + DWORD SortNLSVersion() + { +#ifdef FEATURE_CORECLR + return SORT_VERSION_DEFAULT; +#else + static bool sortNLSVersionConfigChecked = false; + static DWORD sortNLSVersion = SORT_VERSION_DEFAULT; + + if(!sortNLSVersionConfigChecked) + { + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return false); + sortNLSVersion = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CompatSortNLSVersion); + if(sortNLSVersion == 0) + { + sortNLSVersion = SORT_VERSION_DEFAULT; + } + END_SO_INTOLERANT_CODE; + + sortNLSVersionConfigChecked = true; + } + return sortNLSVersion; +#endif // !FEATURE_CORECLR + } + + //////////////////////////////////////////////////////////////////////////// + // + // VersionValue + // + // Get the version from a version blob, resolving to the default version + // if NULL + // + //////////////////////////////////////////////////////////////////////////// + __inline DWORD VersionValue(__in_opt const NLSVERSIONINFO * const lpVersionInformation) + { + + // + // If the caller passed null or zero we use the default version + // + if ((lpVersionInformation == NULL) || + ((lpVersionInformation->dwNLSVersion == 0) && + (lpVersionInformation->dwDefinedVersion ==0)) + ) + { + return SortNLSVersion(); + } + + // TODO: Will need to review this + if(((lpVersionInformation->dwNLSVersion == 0) && + (lpVersionInformation->dwDefinedVersion != 0 ))) + { + return lpVersionInformation->dwDefinedVersion; + } + + return lpVersionInformation->dwNLSVersion; + } + + //////////////////////////////////////////////////////////////////////////// + // + // SortGetSortKey + // + // Supposed to call the dll for the appropriate version. If the default + // version isn't available call the ordinal behavior (for minwin) + // + // Just get the sort hash node and call the worker function + // + //////////////////////////////////////////////////////////////////////////// + __success(return != 0) int WINAPI SortGetSortKey( + __in LPCWSTR pLocaleName, + __in DWORD dwFlags, + __in_ecount(cchSrc) LPCWSTR pSrc, + __in int cchSrc, + __out_bcount_opt(cbDest) LPBYTE pDest, + __in int cbDest, + __in_opt CONST NLSVERSIONINFO *lpVersionInformation, + __in_opt LPVOID lpReserved, + __in_opt LPARAM lParam + ) + { + PSORTHANDLE pSort = GetSortNode(pLocaleName, VersionValue(lpVersionInformation)); + return SortDllGetSortKey(pSort, dwFlags, pSrc, cchSrc, pDest, cbDest, lpReserved, lParam); + } + + // SortDllGetSortKey handles any modification to flags + // necessary before the actual call to the dll + __success(return != 0) int WINAPI SortDllGetSortKey( + __in PSORTHANDLE pSort, + __in DWORD dwFlags, + __in_ecount(cchSrc) LPCWSTR pSrc, + __in int cchSrc, + __out_bcount_opt(cbDest) LPBYTE pDest, + __in int cbDest, + __in_opt LPVOID lpReserved, + __in_opt LPARAM lParam ) + { + if (pSort == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + // + // Note that GetSortKey'll have the opposite behavior for the + // linguistic casing flag (eg: use flag for bad behavior, linguistic + // by default) + dwFlags ^= NORM_LINGUISTIC_CASING; + + return pSort->pSortGetSortKey(pSort, dwFlags, pSrc, cchSrc, pDest, cbDest, lpReserved, lParam); + } + + __success(return != 0) int SortDllGetHashCode( + __in PSORTHANDLE pSort, + __in DWORD dwFlags, + __in_ecount(cchSrc) LPCWSTR pSrc, + __in int cchSrc, + __in_opt LPVOID lpReserved, + __in_opt LPARAM lParam ) + { + if (pSort == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + const int SortDllGetHashCodeApiIntroducedVersion = 2; + if(pSort->dwSHVersion < SortDllGetHashCodeApiIntroducedVersion) + { + SetLastError(ERROR_NOT_SUPPORTED); + return 0; + } + + // + // Note that GetSortKey'll have the opposite behavior for the + // linguistic casing flag (eg: use flag for bad behavior, linguistic + // by default) + dwFlags ^= NORM_LINGUISTIC_CASING; + + return pSort->pSortGetHashCode(pSort, dwFlags, pSrc, cchSrc, lpReserved, lParam); + } + + + //////////////////////////////////////////////////////////////////////////// + // + // SortChangeCase + // + // Supposed to call the dll for the appropriate version. If the default + // version isn't available call the ordinal behavior (for minwin) + // + // NOTE: The linguistic casing flags are backwards (ie: set the flag to + // get the non-linguistic behavior.) If we expose this then we'll + // need to publish the no-linguistic flag. + // + // Just get the sort hash node and call the worker function + // + //////////////////////////////////////////////////////////////////////////// + __success(return != 0) int WINAPI SortChangeCase( + __in LPCWSTR pLocaleName, + __in DWORD dwFlags, + __in_ecount(cchSrc) LPCWSTR pSrc, + __in int cchSrc, + __out_ecount_opt(cchDest) LPWSTR pDest, + __in int cchDest, + __in_opt CONST NLSVERSIONINFO * lpVersionInformation, + __in_opt LPVOID lpReserved, + __in_opt LPARAM lParam) + { + PSORTHANDLE pSort = GetSortNode(pLocaleName, VersionValue(lpVersionInformation)); + return SortDllChangeCase(pSort, dwFlags, pSrc, cchSrc, pDest, cchDest, lpReserved, lParam); + } + + // SortDllChangeCase handles any modification to flags + // necessary before the actual call to the dll + __success(return != 0) int WINAPI SortDllChangeCase( + __in PSORTHANDLE pSort, + __in DWORD dwFlags, + __in_ecount(cchSrc) LPCWSTR pSrc, + __in int cchSrc, + __out_ecount_opt(cchDest) LPWSTR pDest, + __in int cchDest, + __in_opt LPVOID lpReserved, + __in_opt LPARAM lParam) + { + if (pSort == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + // Note that Change Case'll have the opposite behavior for the + // linguistic casing flag (eg: use flag for bad behavior, linguistic + // by default) + dwFlags ^= LCMAP_LINGUISTIC_CASING; +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable: 26036) // prefast - Possible postcondition violation due to failure to null terminate string +#endif // _PREFAST_ + return pSort->pSortChangeCase(pSort, dwFlags, pSrc, cchSrc, pDest, cchDest, lpReserved, lParam); +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + } + + //////////////////////////////////////////////////////////////////////////// + // + // SortCompareString + // + // Supposed to call the dll for the appropriate version. If the default + // version isn't available call the ordinal behavior (for minwin) + // + // Just get the sort hash node and call the worker function + // + //////////////////////////////////////////////////////////////////////////// + __success(return != 0) int WINAPI SortCompareString( + __in LPCWSTR lpLocaleName, + __in DWORD dwCmpFlags, + __in_ecount(cchCount1) LPCWSTR lpString1, + __in int cchCount1, + __in_ecount(cchCount2) LPCWSTR lpString2, + __in int cchCount2, + __in_opt CONST NLSVERSIONINFO * lpVersionInformation, + __in_opt LPVOID lpReserved, + __in_opt LPARAM lParam) + { + PSORTHANDLE pSort = GetSortNode(lpLocaleName, VersionValue(lpVersionInformation)); + return SortDllCompareString(pSort, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2, lpReserved, lParam); + + } + + // SortDllCompareString handles any modification to flags + // necessary before the actual call to the dll + __success(return != 0) int WINAPI SortDllCompareString( + __in PSORTHANDLE pSort, + __in DWORD dwCmpFlags, + __in_ecount(cchCount1) LPCWSTR lpString1, + __in int cchCount1, + __in_ecount(cchCount2) LPCWSTR lpString2, + __in int cchCount2, + __in_opt LPVOID lpReserved, + __in_opt LPARAM lParam) + { + if (pSort == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + // Note that the dll will have the opposite behavior of CompareStringEx for the + // linguistic casing flag (eg: use flag for bad behavior, linguistic + // by default) because we want new public APIs to have the "right" + // behavior by default + dwCmpFlags ^= NORM_LINGUISTIC_CASING; + + return pSort->pSortCompareString(pSort, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2, lpReserved, lParam); + } + + //////////////////////////////////////////////////////////////////////////// + // + // SortFindString + // + // Finds lpStringValue within lpStringSource based on the rules given + // in dwFindNLSStringFlags. + // + // Supposed to call the dll for the appropriate version. If the default + // version isn't available call the ordinal behavior (for minwin) + // + // Just get the sort hash node and call the worker function + // + //////////////////////////////////////////////////////////////////////////// + __success(return != 0) int WINAPI SortFindString( + __in LPCWSTR lpLocaleName, + __in DWORD dwFindNLSStringFlags, + __in_ecount(cchSource) LPCWSTR lpStringSource, + __in int cchSource, + __in_ecount(cchValue) LPCWSTR lpStringValue, + __in int cchValue, + __out_opt LPINT pcchFound, + __in_opt CONST NLSVERSIONINFO * lpVersionInformation, + __in_opt LPVOID lpReserved, + __in_opt LPARAM lParam) + { + PSORTHANDLE pSort = GetSortNode(lpLocaleName, VersionValue(lpVersionInformation)); + return SortDllFindString(pSort, dwFindNLSStringFlags, lpStringSource, cchSource, lpStringValue, cchValue, pcchFound, lpReserved, lParam); + } + + // SortDllFindString handles any modification to flags + // necessary before the actual call to the dll + __success(return != 0) int WINAPI SortDllFindString( + __in PSORTHANDLE pSort, + __in DWORD dwFindNLSStringFlags, + __in_ecount(cchSource) LPCWSTR lpStringSource, + __in int cchSource, + __in_ecount(cchValue) LPCWSTR lpStringValue, + __in int cchValue, + __out_opt LPINT pcchFound, + __in_opt LPVOID lpReserved, + __in_opt LPARAM lParam) + { + if (pSort == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + // Note that the dll will have the opposite behavior of FindNlsString for the + // linguistic casing flag (eg: use flag for bad behavior, linguistic + // by default) because we want new public APIs to have the "right" + // behavior by default + dwFindNLSStringFlags ^= NORM_LINGUISTIC_CASING; + + int cchFound; // we need to get the length even if the caller doesn't care about it (see below) + int result = pSort->pSortFindString(pSort, dwFindNLSStringFlags, lpStringSource, cchSource, lpStringValue, cchValue, &cchFound, lpReserved, lParam); + // When searching from end with an empty pattern (either empty string or all ignored characters) + // a match is found (result != -1) + // Currently we get a result == 0 but we are hoping this will change + // with Win7 to be the length of the source string (thus pointing past-the-end) + // and the length of the match (cchFound) will be 0 + // For compatibility, we need to return the index of the last character (or 0 if the source is empty) + if((dwFindNLSStringFlags & FIND_FROMEND) && + result != -1 && + cchFound == 0 && + cchSource != 0) + { + result = cchSource - 1; + } + + // if the caller cares about the length, give it to them + if(pcchFound != NULL) + { + *pcchFound = cchFound; + } + + return result; + + } + + //////////////////////////////////////////////////////////////////////////// + // + // SortIsDefinedString + // + // This routine looks for code points inside a string to see if they are + // defined within the NSL context. If lpVersionInformation is NULL, the + // version is the current version. Same thing the dwDefinedVersion is equal + // to zero. + // + // Supposed to call the dll for the appropriate version. If the default + // version isn't available call the ordinal behavior (for minwin) + // + // Just get the sort hash node and call the worker function + // + //////////////////////////////////////////////////////////////////////////// + BOOL WINAPI SortIsDefinedString( + __in NLS_FUNCTION Function, + __in DWORD dwFlags, + __in CONST NLSVERSIONINFOEX * lpVersionInformation, + __in_ecount(cchStr) LPCWSTR lpString, + __in INT cchStr) + { + // Get an invariant sort node + PSORTHANDLE pSort = GetSortNode(W(""), VersionValue((CONST NLSVERSIONINFO *)lpVersionInformation)); + return SortDllIsDefinedString(pSort, Function, dwFlags, lpString, cchStr); + } + + // SortDllIsDefinedString handles any modification to flags + // necessary before the actual call to the dll + BOOL WINAPI SortDllIsDefinedString( + __in PSORTHANDLE pSort, + __in NLS_FUNCTION Function, + __in DWORD dwFlags, + __in_ecount(cchStr) LPCWSTR lpString, + __in INT cchStr) + { + // Fail if we couldn't find one + if (pSort == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + return pSort->pSortIsDefinedString(pSort, Function, dwFlags, lpString, cchStr); + } + + BOOL SortGetNLSVersion(__in PSORTHANDLE pSort, + __in NLS_FUNCTION Function, + __inout NLSVERSIONINFO * lpVersionInformation ) + { + lpVersionInformation->dwNLSVersion = pSort->dwNLSVersion; + lpVersionInformation->dwDefinedVersion = pSort->dwDefinedVersion; + + return TRUE; + } + + // Wrapper for SortGetSortKey and SortChangeCase, which are both + // smushed into LCMapStringEx + __success(return != 0) int + LCMapStringEx (__in LPCWSTR lpLocaleName, + __in DWORD dwMapFlags, + __in_ecount(cchSrc) LPCWSTR lpSrcStr, + __in int cchSrc, + __out_ecount_opt(cchDest) LPWSTR lpDestStr, // really this should be __out_awcount_opt(dwMapFlags & LCMAP_SORTKEY, cchDest) + __in int cchDest, + __in_opt CONST NLSVERSIONINFO * lpVersionInformation, + __in_opt LPVOID lpReserved, + __in_opt LPARAM lParam ) + { + // Should be either sort key... + if (dwMapFlags & LCMAP_SORTKEY) + { +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable: 26036) // Prefast - Possible precondition violation due to failure to null terminate string lpDestStr-interpreted differently depending on flag +#endif // _PREFAST_ + return SortGetSortKey(lpLocaleName, + dwMapFlags & ~(LCMAP_SORTKEY), // Don't need sort key flag + lpSrcStr, + cchSrc, + (LPBYTE)lpDestStr, // Sort keys are bytes not WCHARs + cchDest, // Sort keys are bytes not WCHARs + lpVersionInformation, + lpReserved, + lParam); +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + } + + // + // Check for changing case conditions. This may be combined with Chinese or Japanese + // transliteration, but not with sort key nor ignore space/symbols + // + _ASSERT(dwMapFlags & (LCMAP_TITLECASE | LCMAP_UPPERCASE | LCMAP_LOWERCASE)); + + // + // Call casing wrapper, which'll either call the correct version dll + // or call ordinal behavior in the minwin case + // + return SortChangeCase(lpLocaleName, + dwMapFlags & ~(LCMAP_BYTEREV), + lpSrcStr, + cchSrc, + lpDestStr, + cchDest, + lpVersionInformation, + lpReserved, + lParam); + } + + + //////////////////////////////////////////////////////////////////////////// + // + // IsAvailableVersion() + // + // Get the SortGetHandle() function for the proper dll version. + // + //////////////////////////////////////////////////////////////////////////// + BOOL IsAvailableVersion(__in_opt CONST NLSVERSIONINFO * pVersion) + { + return GetSortGetHandle(VersionValue(pVersion)) != NULL; + } + + + //////////////////////////////////////////////////////////////////////////// + // + // GetSortHandle() + // + // Get the SortHandle for the given locale and version + // + //////////////////////////////////////////////////////////////////////////// + PSORTHANDLE GetSortHandle(__in LPCWSTR lpLocaleName, __in_opt CONST NLSVERSIONINFO * pVersion) + { + DWORD version = VersionValue(pVersion); + if (GetSortGetHandle(version) == NULL) + { + return NULL; + } + return GetSortNode(lpLocaleName, version); + } + +} diff --git a/src/utilcode/splitpath.cpp b/src/utilcode/splitpath.cpp new file mode 100644 index 0000000000..1be90a828d --- /dev/null +++ b/src/utilcode/splitpath.cpp @@ -0,0 +1,281 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +/*** +*splitpath.c - break down path name into components +* + +* +*Purpose: +* To provide support for accessing the individual components of an +* arbitrary path name +* +*******************************************************************************/ +#include "stdafx.h" +#include "winwrap.h" +#include "utilcode.h" +#include "sstring.h" + + +/*** +*_splitpath() - split a path name into its individual components +* +*Purpose: +* to split a path name into its individual components +* +*Entry: +* path - pointer to path name to be parsed +* drive - pointer to buffer for drive component, if any +* dir - pointer to buffer for subdirectory component, if any +* fname - pointer to buffer for file base name component, if any +* ext - pointer to buffer for file name extension component, if any +* +*Exit: +* drive - pointer to drive string. Includes ':' if a drive was given. +* dir - pointer to subdirectory string. Includes leading and trailing +* '/' or '\', if any. +* fname - pointer to file base name +* ext - pointer to file extension, if any. Includes leading '.'. +* +*Exceptions: +* +*******************************************************************************/ + +void SplitPath( + register const WCHAR *path, + __inout_z __inout_ecount_opt(driveSizeInWords) WCHAR *drive, int driveSizeInWords, + __inout_z __inout_ecount_opt(dirSizeInWords) WCHAR *dir, int dirSizeInWords, + __inout_z __inout_ecount_opt(fnameSizeInWords) WCHAR *fname, size_t fnameSizeInWords, + __inout_z __inout_ecount_opt(extSizeInWords) WCHAR *ext, size_t extSizeInWords) +{ + WRAPPER_NO_CONTRACT; + + LPCWSTR _wszDrive, _wszDir, _wszFileName, _wszExt; + size_t _cchDrive, _cchDir, _cchFileName, _cchExt; + + SplitPathInterior(path, + &_wszDrive, &_cchDrive, + &_wszDir, &_cchDir, + &_wszFileName, &_cchFileName, + &_wszExt, &_cchExt); + + if (drive && _wszDrive) + wcsncpy_s(drive, driveSizeInWords, _wszDrive, min(_cchDrive, _MAX_DRIVE)); + + if (dir && _wszDir) + wcsncpy_s(dir, dirSizeInWords, _wszDir, min(_cchDir, _MAX_DIR)); + + if (fname && _wszFileName) + wcsncpy_s(fname, fnameSizeInWords, _wszFileName, min(_cchFileName, _MAX_FNAME)); + + if (ext && _wszExt) + wcsncpy_s(ext, extSizeInWords, _wszExt, min(_cchExt, _MAX_EXT)); +} + +//******************************************************************************* +// A much more sensible version that just points to each section of the string. +//******************************************************************************* +void SplitPathInterior( + __in LPCWSTR wszPath, + __out_opt LPCWSTR *pwszDrive, __out_opt size_t *pcchDrive, + __out_opt LPCWSTR *pwszDir, __out_opt size_t *pcchDir, + __out_opt LPCWSTR *pwszFileName, __out_opt size_t *pcchFileName, + __out_opt LPCWSTR *pwszExt, __out_opt size_t *pcchExt) +{ + LIMITED_METHOD_CONTRACT; + + // Arguments must come in valid pairs + _ASSERTE(!!pwszDrive == !!pcchDrive); + _ASSERTE(!!pwszDir == !!pcchDir); + _ASSERTE(!!pwszFileName == !!pcchFileName); + _ASSERTE(!!pwszExt == !!pcchExt); + + register WCHAR *p; + LPCWSTR last_slash = NULL, dot = NULL; + + /* we assume that the path argument has the following form, where any + * or all of the components may be missing. + * + * <drive><dir><fname><ext> + * + * and each of the components has the following expected form(s) + * + * drive: + * 0 to _MAX_DRIVE-1 characters, the last of which, if any, is a + * ':' + * dir: + * 0 to _MAX_DIR-1 characters in the form of an absolute path + * (leading '/' or '\') or relative path, the last of which, if + * any, must be a '/' or '\'. E.g - + * absolute path: + * \top\next\last\ ; or + * /top/next/last/ + * relative path: + * top\next\last\ ; or + * top/next/last/ + * Mixed use of '/' and '\' within a path is also tolerated + * fname: + * 0 to _MAX_FNAME-1 characters not including the '.' character + * ext: + * 0 to _MAX_EXT-1 characters where, if any, the first must be a + * '.' + * + */ + + /* extract drive letter and :, if any */ + + if ((wcslen(wszPath) >= (_MAX_DRIVE - 2)) && (*(wszPath + _MAX_DRIVE - 2) == _T(':'))) { + if (pwszDrive && pcchDrive) { + *pwszDrive = wszPath; + *pcchDrive = _MAX_DRIVE - 1; + } + wszPath += _MAX_DRIVE - 1; + } + else if (pwszDrive && pcchDrive) { + *pwszDrive = NULL; + *pcchDrive = 0; + } + + /* extract path string, if any. Path now points to the first character + * of the path, if any, or the filename or extension, if no path was + * specified. Scan ahead for the last occurence, if any, of a '/' or + * '\' path separator character. If none is found, there is no path. + * We will also note the last '.' character found, if any, to aid in + * handling the extension. + */ + + for (last_slash = NULL, p = (WCHAR *)wszPath; *p; p++) { +#ifdef _MBCS + if (_ISLEADBYTE (*p)) + p++; + else { +#endif /* _MBCS */ + if (*p == _T('/') || *p == _T('\\')) + /* point to one beyond for later copy */ + last_slash = p + 1; + else if (*p == _T('.')) + dot = p; +#ifdef _MBCS + } +#endif /* _MBCS */ + } + + if (last_slash) { + /* found a path - copy up through last_slash or max. characters + * allowed, whichever is smaller + */ + + if (pwszDir && pcchDir) { + *pwszDir = wszPath; + *pcchDir = last_slash - wszPath; + } + wszPath = last_slash; + } + else if (pwszDir && pcchDir) { + *pwszDir = NULL; + *pcchDir = 0; + } + + /* extract file name and extension, if any. Path now points to the + * first character of the file name, if any, or the extension if no + * file name was given. Dot points to the '.' beginning the extension, + * if any. + */ + + if (dot && (dot >= wszPath)) { + /* found the marker for an extension - copy the file name up to + * the '.'. + */ + if (pwszFileName && pcchFileName) { + *pwszFileName = wszPath; + *pcchFileName = dot - wszPath; + } + /* now we can get the extension - remember that p still points + * to the terminating nul character of path. + */ + if (pwszExt && pcchExt) { + *pwszExt = dot; + *pcchExt = p - dot; + } + } + else { + /* found no extension, give empty extension and copy rest of + * string into fname. + */ + if (pwszFileName && pcchFileName) { + *pwszFileName = wszPath; + *pcchFileName = p - wszPath; + } + if (pwszExt && pcchExt) { + *pwszExt = NULL; + *pcchExt = 0; + } + } +} + +/*** +*_splitpath() - split a path name into its individual components +* +*Purpose: +* to split a path name into its individual components +* +*Entry: +* path - SString representing the path name to be parsed +* drive - Out SString for drive component +* dir - Out SString for subdirectory component +* fname - Out SString for file base name component +* ext - Out SString for file name extension component +* +*Exit: +* drive - Drive string. Includes ':' if a drive was given. +* dir - Subdirectory string. Includes leading and trailing +* '/' or '\', if any. +* fname - File base name +* ext - File extension, if any. Includes leading '.'. +* +*Exceptions: +* +*******************************************************************************/ + +void SplitPath(__in SString const &path, + __inout_opt SString *drive, + __inout_opt SString *dir, + __inout_opt SString *fname, + __inout_opt SString *ext) +{ + LPWSTR wzDrive = NULL; + if (drive != NULL) + wzDrive = drive->OpenUnicodeBuffer(_MAX_DRIVE); + + LPWSTR wzDir = NULL; + if (dir != NULL) + wzDir = dir->OpenUnicodeBuffer(_MAX_DIR); + + LPWSTR wzFname = NULL; + if (fname != NULL) + wzFname = fname->OpenUnicodeBuffer(_MAX_FNAME); + + LPWSTR wzExt = NULL; + if (ext != NULL) + wzExt = ext->OpenUnicodeBuffer(_MAX_EXT); + + SplitPath(path, + wzDrive, _MAX_DRIVE, + wzDir, _MAX_DIR, + wzFname, _MAX_FNAME, + wzExt, _MAX_EXT); + + if (drive != NULL) + drive->CloseBuffer(static_cast<COUNT_T>(wcslen(wzDrive))); + + if (dir != NULL) + dir->CloseBuffer(static_cast<COUNT_T>(wcslen(wzDir))); + + if (fname != NULL) + fname->CloseBuffer(static_cast<COUNT_T>(wcslen(wzFname))); + + if (ext != NULL) + ext->CloseBuffer(static_cast<COUNT_T>(wcslen(wzExt))); +} + diff --git a/src/utilcode/sstring.cpp b/src/utilcode/sstring.cpp new file mode 100644 index 0000000000..69321e72ef --- /dev/null +++ b/src/utilcode/sstring.cpp @@ -0,0 +1,2871 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// --------------------------------------------------------------------------- +// SString.cpp +// + +// --------------------------------------------------------------------------- + +#include "stdafx.h" +#include "sstring.h" +#include "ex.h" +#include "holder.h" +#include "newapis.h" + + +#if defined(_MSC_VER) +#pragma inline_depth (25) +#endif + +//----------------------------------------------------------------------------- +// Static variables +//----------------------------------------------------------------------------- + +// Have one internal, well-known, literal for the empty string. +const BYTE SString::s_EmptyBuffer[2] = { 0 }; + +// @todo: these need to be initialized by calling GetACP() or GetConsoleOutputCP() +// followed by a GetCPInfo to see if the max character size is 1. + +UINT SString::s_ACP = 0; +SVAL_IMPL_INIT(BOOL, SString, s_IsANSIMultibyte, TRUE); + +#ifndef DACCESS_COMPILE +static BYTE s_EmptySpace[sizeof(SString)] = { 0 }; +#endif // DACCESS_COMPILE + +#if FEATURE_USE_LCID +const LocaleID SString::s_defaultLCID = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); +#else +const LocaleID SString::s_defaultLCID = NULL; +#endif + +SPTR_IMPL(SString,SString,s_Empty); + +void SString::Startup() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + if (s_ACP == 0) + { + CPINFO info; + + UINT ACP = GetACP(); + if (GetCPInfo(ACP, &info) && info.MaxCharSize == 1) + s_IsANSIMultibyte = FALSE; + +#ifndef DACCESS_COMPILE + s_Empty = PTR_SString(new (s_EmptySpace) SString()); + s_Empty->SetNormalized(); +#endif // DACCESS_COMPILE + + MemoryBarrier(); + s_ACP = ACP; + } +} + +CHECK SString::CheckStartup() +{ + WRAPPER_NO_CONTRACT; + + CHECK(s_Empty != NULL); + CHECK_OK; +} + +//----------------------------------------------------------------------------- +// Case insensitive helpers. +//----------------------------------------------------------------------------- + +static WCHAR MapChar(WCHAR wc, DWORD dwFlags, LocaleID lcid) +{ + WRAPPER_NO_CONTRACT; + + WCHAR wTmp; + +#ifndef FEATURE_PAL + +#ifdef FEATURE_USE_LCID + int iRet = WszLCMapString(lcid, dwFlags, &wc, 1, &wTmp, 1); +#else + // TODO: Uncertain if this is the best behavior. Caller should specify locale name + if (lcid == NULL || lcid[0]==W('!')) lcid = W(""); + int iRet = NewApis::LCMapStringEx(lcid, dwFlags, &wc, 1, &wTmp, 1, NULL, NULL, 0); +#endif + if (!iRet) { + // This can fail in non-exceptional cases becauseof unknown unicode characters. + wTmp = wc; + } + +#else // !FEATURE_PAL + // For PAL, no locale specific processing is done + + if (dwFlags == LCMAP_UPPERCASE) + { + wTmp = toupper(wc); + } + else + { + _ASSERTE(dwFlags == LCMAP_LOWERCASE); + wTmp = tolower(wc); + } +#endif // !FEATURE_PAL + + return wTmp; +} + +#define IS_UPPER_A_TO_Z(x) (((x) >= W('A')) && ((x) <= W('Z'))) +#define IS_LOWER_A_TO_Z(x) (((x) >= W('a')) && ((x) <= W('z'))) +#define CAN_SIMPLE_UPCASE(x) (((x)&~0x7f) == 0) +#define CAN_SIMPLE_DOWNCASE(x) (((x)&~0x7f) == 0) +#define SIMPLE_UPCASE(x) (IS_LOWER_A_TO_Z(x) ? ((x) - W('a') + W('A')) : (x)) +#define SIMPLE_DOWNCASE(x) (IS_UPPER_A_TO_Z(x) ? ((x) - W('A') + W('a')) : (x)) + +/* static */ +int SString::CaseCompareHelper(const WCHAR *buffer1, const WCHAR *buffer2, COUNT_T count, LocaleID lcid, BOOL stopOnNull, BOOL stopOnCount) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE(stopOnNull || stopOnCount); + + const WCHAR *buffer1End = buffer1 + count; + int diff = 0; + + while (!stopOnCount || (buffer1 < buffer1End)) + { + WCHAR ch1 = *buffer1++; + WCHAR ch2 = *buffer2++; + diff = ch1 - ch2; + if ((ch1 == 0) || (ch2 == 0)) + { + if (diff != 0 || stopOnNull) + { + break; + } + } + else + { + if (diff != 0) + { + diff = ((CAN_SIMPLE_UPCASE(ch1) ? SIMPLE_UPCASE(ch1) : MapChar(ch1, LCMAP_UPPERCASE, lcid)) + - (CAN_SIMPLE_UPCASE(ch2) ? SIMPLE_UPCASE(ch2) : MapChar(ch2, LCMAP_UPPERCASE, lcid))); + } + if (diff != 0) + { + break; + } + } + } + + return diff; +} + +#define IS_LOWER_A_TO_Z_ANSI(x) (((x) >= 'a') && ((x) <= 'z')) +#define CAN_SIMPLE_UPCASE_ANSI(x) (((x) >= 0x20) && ((x) <= 0x7f)) +#define SIMPLE_UPCASE_ANSI(x) (IS_LOWER_A_TO_Z(x) ? ((x) - 'a' + 'A') : (x)) + +// TODO: Need to get rid of LocaleID and use a LPCWSTR locale name instead. +int GetCaseInsensitiveValueA(LocaleID lcid, const CHAR *buffer, int length) { + LIMITED_METHOD_CONTRACT; + _ASSERTE(buffer != NULL); + _ASSERTE(length == 1 || ((length == 2) && IsDBCSLeadByte(*buffer))); + + WCHAR wideCh; + int sortValue; + int conversionReturn = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, buffer, length, &wideCh, 1); + if (conversionReturn == 0) + { + // An invalid sequence should only compare equal to itself, so use a negative mapping. + if (length == 1) + { + sortValue = -((int)((unsigned char)(*buffer))); + } + else + { + sortValue = -(((((int)((unsigned char)(*buffer))) << 8) | ((int)((unsigned char)(*(buffer + 1)))))); + } + } + else + { + _ASSERTE(conversionReturn == 1); + sortValue = MapChar(wideCh, LCMAP_UPPERCASE, lcid); + } + return sortValue; +} + +/* static */ +int SString::CaseCompareHelperA(const CHAR *buffer1, const CHAR *buffer2, COUNT_T count, LocaleID lcid, BOOL stopOnNull, BOOL stopOnCount) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE(stopOnNull || stopOnCount); + + const CHAR *buffer1End = buffer1 + count; + int diff = 0; + + while (!stopOnCount || (buffer1 < buffer1End)) + { + CHAR ch1 = *buffer1; + CHAR ch2 = *buffer2; + if ((ch1 == 0) || (ch2 == 0)) + { + diff = ch1 - ch2; + if (diff != 0 || stopOnNull) + { + break; + } + buffer1++; + buffer2++; + } + else if (CAN_SIMPLE_UPCASE_ANSI(ch1) && CAN_SIMPLE_UPCASE_ANSI(ch2)) + { + diff = ch1 - ch2; + if (diff != 0) + { + diff = (SIMPLE_UPCASE_ANSI(ch1) - SIMPLE_UPCASE_ANSI(ch2)); + if (diff != 0) + { + break; + } + } + buffer1++; + buffer2++; + } + else + { + int length = 1; + if (s_IsANSIMultibyte + && IsDBCSLeadByte(ch1) + && IsDBCSLeadByte(ch2) + && (!stopOnCount || ((buffer1 + 1) < buffer1End))) + { + length = 2; + } + int sortValue1 = GetCaseInsensitiveValueA(lcid, buffer1, length); + int sortValue2 = GetCaseInsensitiveValueA(lcid, buffer2, length); + diff = sortValue1 - sortValue2; + if (diff != 0) + { + break; + } + buffer1 += length; + buffer2 += length; + } + } + return diff; +} + + +int CaseHashHelper(const WCHAR *buffer, COUNT_T count, LocaleID lcid) +{ + LIMITED_METHOD_CONTRACT; + + const WCHAR *bufferEnd = buffer + count; + ULONG hash = 5381; + + while (buffer < bufferEnd) + { + WCHAR ch = *buffer++; + ch = CAN_SIMPLE_UPCASE(ch) ? SIMPLE_UPCASE(ch) : MapChar(ch, LCMAP_UPPERCASE, lcid); + + hash = (((hash << 5) + hash) ^ ch); + } + + return hash; +} + +static int CaseHashHelperA(const CHAR *buffer, COUNT_T count) +{ + LIMITED_METHOD_CONTRACT; + + const CHAR *bufferEnd = buffer + count; + ULONG hash = 5381; + + while (buffer < bufferEnd) + { + CHAR ch = *buffer++; + ch = SIMPLE_UPCASE_ANSI(ch); + + hash = (((hash << 5) + hash) ^ ch); + } + + return hash; +} + +//----------------------------------------------------------------------------- +// Set this string to a copy of the unicode string +//----------------------------------------------------------------------------- +void SString::Set(const WCHAR *string) +{ + CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(CheckPointer(string, NULL_OK)); + THROWS; + GC_NOTRIGGER; + SUPPORTS_DAC_HOST_ONLY; + } + CONTRACT_END; + + if (string == NULL || *string == 0) + Clear(); + else + { + Resize((COUNT_T) wcslen(string), REPRESENTATION_UNICODE); + wcscpy_s(GetRawUnicode(), GetBufferSizeInCharIncludeNullChar(), string); + } + + RETURN; +} + +//----------------------------------------------------------------------------- +// Set this string to a copy of the first count characters of the given +// unicode string. +//----------------------------------------------------------------------------- +void SString::Set(const WCHAR *string, COUNT_T count) +{ + SS_CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(CheckPointer(string, NULL_OK)); + PRECONDITION(CheckCount(count)); + THROWS; + GC_NOTRIGGER; + } + SS_CONTRACT_END; + + if (count == 0) + Clear(); + else + { + Resize(count, REPRESENTATION_UNICODE); + wcsncpy_s(GetRawUnicode(), GetBufferSizeInCharIncludeNullChar(), string, count); + GetRawUnicode()[count] = 0; + } + + SS_RETURN; +} + +//----------------------------------------------------------------------------- +// Set this string to a point to the first count characters of the given +// preallocated unicode string (shallow copy). +//----------------------------------------------------------------------------- +void SString::SetPreallocated(const WCHAR *string, COUNT_T count) +{ + SS_CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(CheckPointer(string, NULL_OK)); + PRECONDITION(CheckCount(count)); + SS_POSTCONDITION(IsEmpty()); + GC_NOTRIGGER; + NOTHROW; + SUPPORTS_DAC_HOST_ONLY; + } + SS_CONTRACT_END; + + SetImmutable(); + SetImmutable((BYTE*) string, count*2); + ClearAllocated(); + SetRepresentation(REPRESENTATION_UNICODE); + + SS_RETURN; +} + +//----------------------------------------------------------------------------- +// Set this string to a copy of the given ansi string +//----------------------------------------------------------------------------- +void SString::SetASCII(const ASCII *string) +{ + SS_CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(CheckPointer(string, NULL_OK)); + PRECONDITION(CheckASCIIString(string)); + THROWS; + GC_NOTRIGGER; + } + SS_CONTRACT_END; + + if (string == NULL || *string == 0) + Clear(); + else + { + Resize((COUNT_T) strlen(string), REPRESENTATION_ASCII); + strcpy_s(GetRawUTF8(), GetBufferSizeInCharIncludeNullChar(), string); + } + + SS_RETURN; +} + +//----------------------------------------------------------------------------- +// Set this string to a copy of the first count characters of the given +// ascii string +//----------------------------------------------------------------------------- +void SString::SetASCII(const ASCII *string, COUNT_T count) +{ + SS_CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(CheckPointer(string, NULL_OK)); + PRECONDITION(CheckASCIIString(string, count)); + PRECONDITION(CheckCount(count)); + THROWS; + GC_NOTRIGGER; + } + SS_CONTRACT_END; + + if (count == 0) + Clear(); + else + { + Resize(count, REPRESENTATION_ASCII); + strncpy_s(GetRawASCII(), GetBufferSizeInCharIncludeNullChar(), string, count); + GetRawASCII()[count] = 0; + } + + SS_RETURN; +} + +//----------------------------------------------------------------------------- +// Set this string to a copy of the given UTF8 string +//----------------------------------------------------------------------------- +void SString::SetUTF8(const UTF8 *string) +{ + SS_CONTRACT_VOID + { + // !!! Check for illegal UTF8 encoding? + INSTANCE_CHECK; + PRECONDITION(CheckPointer(string, NULL_OK)); + THROWS; + GC_NOTRIGGER; + SUPPORTS_DAC_HOST_ONLY; + } + SS_CONTRACT_END; + + if (string == NULL || *string == 0) + Clear(); + else + { + Resize((COUNT_T) strlen(string), REPRESENTATION_UTF8); + strcpy_s(GetRawUTF8(), GetBufferSizeInCharIncludeNullChar(), string); + } + + SS_RETURN; +} + +//----------------------------------------------------------------------------- +// Set this string to a copy of the first count characters of the given +// UTF8 string. +//----------------------------------------------------------------------------- +void SString::SetUTF8(const UTF8 *string, COUNT_T count) +{ + SS_CONTRACT_VOID + { + // !!! Check for illegal UTF8 encoding? + INSTANCE_CHECK; + PRECONDITION(CheckPointer(string, NULL_OK)); + PRECONDITION(CheckCount(count)); + THROWS; + GC_NOTRIGGER; + } + SS_CONTRACT_END; + + if (count == 0) + Clear(); + else + { + Resize(count, REPRESENTATION_UTF8); + strncpy_s(GetRawUTF8(), GetBufferSizeInCharIncludeNullChar(), string, count); + GetRawUTF8()[count] = 0; + } + + SS_RETURN; +} + +//----------------------------------------------------------------------------- +// Set this string to a copy of the given ANSI string +//----------------------------------------------------------------------------- +void SString::SetANSI(const ANSI *string) +{ + SS_CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(CheckPointer(string, NULL_OK)); + THROWS; + GC_NOTRIGGER; + } + SS_CONTRACT_END; + + if (string == NULL || *string == 0) + Clear(); + else + { + Resize((COUNT_T) strlen(string), REPRESENTATION_ANSI); + strcpy_s(GetRawANSI(), GetBufferSizeInCharIncludeNullChar(), string); + } + + SS_RETURN; +} + +//----------------------------------------------------------------------------- +// Set this string to a copy of the first count characters of the given +// ANSI string. +//----------------------------------------------------------------------------- +void SString::SetANSI(const ANSI *string, COUNT_T count) +{ + SS_CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(CheckPointer(string, NULL_OK)); + PRECONDITION(CheckCount(count)); + THROWS; + GC_NOTRIGGER; + } + SS_CONTRACT_END; + + if (count == 0) + Clear(); + else + { + Resize(count, REPRESENTATION_ANSI); + strncpy_s(GetRawANSI(), GetBufferSizeInCharIncludeNullChar(), string, count); + GetRawANSI()[count] = 0; + } + + SS_RETURN; +} + +//----------------------------------------------------------------------------- +// Set this string to the given unicode character +//----------------------------------------------------------------------------- +void SString::Set(WCHAR character) +{ + SS_CONTRACT_VOID + { + INSTANCE_CHECK; + THROWS; + GC_NOTRIGGER; + SUPPORTS_DAC_HOST_ONLY; + } + SS_CONTRACT_END; + + if (character == 0) + Clear(); + else + { + Resize(1, REPRESENTATION_UNICODE); + GetRawUnicode()[0] = character; + GetRawUnicode()[1] = 0; + } + + SS_RETURN; +} + +//----------------------------------------------------------------------------- +// Set this string to the given UTF8 character +//----------------------------------------------------------------------------- +void SString::SetUTF8(CHAR character) +{ + SS_CONTRACT_VOID + { + INSTANCE_CHECK; + THROWS; + GC_NOTRIGGER; + } + SS_CONTRACT_END; + + if (character == 0) + Clear(); + else + { + Resize(1, REPRESENTATION_UTF8); + GetRawUTF8()[0] = character; + GetRawUTF8()[1] = 0; + } + + SS_RETURN; +} + + +//----------------------------------------------------------------------------- +// Set this string to the given ansi literal. +// This will share the memory and not make a copy. +//----------------------------------------------------------------------------- +void SString::SetLiteral(const ASCII *literal) +{ + SS_CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(CheckPointer(literal)); + PRECONDITION(CheckASCIIString(literal)); + THROWS; + GC_NOTRIGGER; + } + SS_CONTRACT_END; + + SString s(Literal, literal); + Set(s); + + SS_RETURN; +} + +//----------------------------------------------------------------------------- +// Set this string to the given unicode literal. +// This will share the memory and not make a copy. +//----------------------------------------------------------------------------- +void SString::SetLiteral(const WCHAR *literal) +{ + SS_CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(CheckPointer(literal)); + THROWS; + GC_NOTRIGGER; + } + SS_CONTRACT_END; + + SString s(Literal, literal); + Set(s); + + SS_RETURN; +} + +//----------------------------------------------------------------------------- +// Hash the string contents +//----------------------------------------------------------------------------- +ULONG SString::Hash() const +{ + SS_CONTRACT(ULONG) + { + INSTANCE_CHECK; + THROWS_UNLESS_NORMALIZED; + GC_NOTRIGGER; + } + SS_CONTRACT_END; + + ConvertToUnicode(); + + SS_RETURN HashString(GetRawUnicode()); +} + +//----------------------------------------------------------------------------- +// Hash the string contents +//----------------------------------------------------------------------------- +ULONG SString::HashCaseInsensitive(LocaleID lcid) const +{ + SS_CONTRACT(ULONG) + { + INSTANCE_CHECK; + THROWS_UNLESS_NORMALIZED; + GC_NOTRIGGER; + } + SS_CONTRACT_END; + + ConvertToIteratable(); + + ULONG result; + + switch (GetRepresentation()) + { + case REPRESENTATION_UNICODE: + case REPRESENTATION_EMPTY: + result = CaseHashHelper(GetRawUnicode(), GetRawCount(), lcid); + break; + + case REPRESENTATION_ASCII: + result = CaseHashHelperA(GetRawASCII(), GetRawCount()); + break; + + default: + UNREACHABLE(); + } + + SS_RETURN result; +} + +//----------------------------------------------------------------------------- +// Truncate this string to count characters. +//----------------------------------------------------------------------------- +void SString::Truncate(const Iterator &i) +{ + SS_CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(CheckIteratorRange(i)); + SS_POSTCONDITION(GetRawCount() == i - Begin()); + THROWS; + GC_NOTRIGGER; + SUPPORTS_DAC_HOST_ONLY; + } + SS_CONTRACT_END; + + CONSISTENCY_CHECK(IsFixedSize()); + + COUNT_T size = i - Begin(); + + Resize(size, GetRepresentation(), PRESERVE); + + i.Resync(this, (BYTE *) (GetRawUnicode() + size)); + + SS_RETURN; +} + +//----------------------------------------------------------------------------- +// Convert the ASCII representation for this String to Unicode. We can do this +// quickly and in-place (if this == &dest), which is why it is optimized. +//----------------------------------------------------------------------------- +void SString::ConvertASCIIToUnicode(SString &dest) const +{ + CONTRACT_VOID + { + PRECONDITION(IsRepresentation(REPRESENTATION_ASCII)); + POSTCONDITION(dest.IsRepresentation(REPRESENTATION_UNICODE)); + THROWS; + GC_NOTRIGGER; + SUPPORTS_DAC_HOST_ONLY; + } + CONTRACT_END; + + // Handle the empty case. + if (IsEmpty()) + { + dest.Clear(); + RETURN; + } + + CONSISTENCY_CHECK(CheckPointer(GetRawASCII())); + CONSISTENCY_CHECK(GetRawCount() > 0); + + // If dest is the same as this, then we need to preserve on resize. + dest.Resize(GetRawCount(), REPRESENTATION_UNICODE, + this == &dest ? PRESERVE : DONT_PRESERVE); + + // Make sure the buffer is big enough. + CONSISTENCY_CHECK(dest.GetAllocation() > (GetRawCount() * sizeof(WCHAR))); + + // This is a poor man's widen. Since we know that the representation is ASCII, + // we can just pad the string with a bunch of zero-value bytes. Of course, + // we move from the end of the string to the start so that we can convert in + // place (in the case that &dest == this). + WCHAR *outBuf = dest.GetRawUnicode() + dest.GetRawCount(); + ASCII *inBuf = GetRawASCII() + GetRawCount(); + + while (GetRawASCII() <= inBuf) + { + CONSISTENCY_CHECK(dest.GetRawUnicode() <= outBuf); + // The casting zero-extends the value, thus giving us the zero-valued byte. + *outBuf = (WCHAR) *inBuf; + outBuf--; + inBuf--; + } + + RETURN; +} + +//----------------------------------------------------------------------------- +// Convert the internal representation for this String to Unicode. +//----------------------------------------------------------------------------- +void SString::ConvertToUnicode() const +{ + CONTRACT_VOID + { + POSTCONDITION(IsRepresentation(REPRESENTATION_UNICODE)); + if (IsRepresentation(REPRESENTATION_UNICODE)) NOTHROW; else THROWS; + GC_NOTRIGGER; + SUPPORTS_DAC_HOST_ONLY; + } + CONTRACT_END; + + if (!IsRepresentation(REPRESENTATION_UNICODE)) + { + if (IsRepresentation(REPRESENTATION_ASCII)) + { + ConvertASCIIToUnicode(*(const_cast<SString *>(this))); + } + else + { + StackSString s; + ConvertToUnicode(s); + PREFIX_ASSUME(!s.IsImmutable()); + (const_cast<SString*>(this))->Set(s); + } + } + + RETURN; +} + +//----------------------------------------------------------------------------- +// Convert the internal representation for this String to Unicode, while +// preserving the iterator if the conversion is done. +//----------------------------------------------------------------------------- +void SString::ConvertToUnicode(const CIterator &i) const +{ + CONTRACT_VOID + { + PRECONDITION(i.Check()); + POSTCONDITION(IsRepresentation(REPRESENTATION_UNICODE)); + if (IsRepresentation(REPRESENTATION_UNICODE)) NOTHROW; else THROWS; + GC_NOTRIGGER; + SUPPORTS_DAC_HOST_ONLY; + } + CONTRACT_END; + + if (!IsRepresentation(REPRESENTATION_UNICODE)) + { + CONSISTENCY_CHECK(IsFixedSize()); + + COUNT_T index = 0; + // Get the current index of the iterator + if (i.m_ptr != NULL) + { + CONSISTENCY_CHECK(GetCharacterSizeShift() == 0); + index = (COUNT_T) (i.m_ptr - m_buffer); + } + + if (IsRepresentation(REPRESENTATION_ASCII)) + { + ConvertASCIIToUnicode(*(const_cast<SString *>(this))); + } + else + { + StackSString s; + ConvertToUnicode(s); + (const_cast<SString*>(this))->Set(s); + } + + // Move the iterator to the new location. + if (i.m_ptr != NULL) + { + i.Resync(this, (BYTE *) (GetRawUnicode() + index)); + } + } + + RETURN; +} + +//----------------------------------------------------------------------------- +// Set s to be a copy of this string's contents, but in the unicode format. +//----------------------------------------------------------------------------- +void SString::ConvertToUnicode(SString &s) const +{ + CONTRACT_VOID + { + PRECONDITION(s.Check()); + POSTCONDITION(s.IsRepresentation(REPRESENTATION_UNICODE)); + THROWS; + GC_NOTRIGGER; + SUPPORTS_DAC_HOST_ONLY; + } + CONTRACT_END; + + int page = 0; + + switch (GetRepresentation()) + { + case REPRESENTATION_EMPTY: + s.Clear(); + RETURN; + + case REPRESENTATION_UNICODE: + s.Set(*this); + RETURN; + + case REPRESENTATION_UTF8: + page = CP_UTF8; + break; + + case REPRESENTATION_ASCII: + ConvertASCIIToUnicode(s); + RETURN; + + case REPRESENTATION_ANSI: + page = CP_ACP; + break; + + default: + UNREACHABLE(); + } + + COUNT_T length = WszMultiByteToWideChar(page, 0, GetRawANSI(), GetRawCount()+1, 0, 0); + if (length == 0) + ThrowLastError(); + + s.Resize(length-1, REPRESENTATION_UNICODE); + + length = WszMultiByteToWideChar(page, 0, GetRawANSI(), GetRawCount()+1, s.GetRawUnicode(), length); + if (length == 0) + ThrowLastError(); + + RETURN; +} + +//----------------------------------------------------------------------------- +// Set s to be a copy of this string's contents, but in the ANSI format. +//----------------------------------------------------------------------------- +void SString::ConvertToANSI(SString &s) const +{ + CONTRACT_VOID + { + PRECONDITION(s.Check()); + POSTCONDITION(s.IsRepresentation(REPRESENTATION_ANSI)); + THROWS; + GC_NOTRIGGER; + } + CONTRACT_END; + + switch (GetRepresentation()) + { + case REPRESENTATION_EMPTY: + s.Clear(); + RETURN; + + case REPRESENTATION_ASCII: + case REPRESENTATION_ANSI: + s.Set(*this); + RETURN; + + case REPRESENTATION_UTF8: + // No direct conversion to ANSI + ConvertToUnicode(); + // fall through + + case REPRESENTATION_UNICODE: + break; + + default: + UNREACHABLE(); + } + + // @todo: use WC_NO_BEST_FIT_CHARS + COUNT_T length = WszWideCharToMultiByte(CP_ACP, 0, GetRawUnicode(), GetRawCount()+1, + NULL, 0, NULL, NULL); + + s.Resize(length-1, REPRESENTATION_ANSI); + + // @todo: use WC_NO_BEST_FIT_CHARS + length = WszWideCharToMultiByte(CP_ACP, 0, GetRawUnicode(), GetRawCount()+1, + s.GetRawANSI(), length, NULL, NULL); + if (length == 0) + ThrowLastError(); + + RETURN; +} + +//----------------------------------------------------------------------------- +// Set s to be a copy of this string's contents, but in the utf8 format. +//----------------------------------------------------------------------------- +COUNT_T SString::ConvertToUTF8(SString &s) const +{ + CONTRACT(COUNT_T) + { + PRECONDITION(s.Check()); + POSTCONDITION(s.IsRepresentation(REPRESENTATION_UTF8)); + THROWS; + GC_NOTRIGGER; + } + CONTRACT_END; + + switch (GetRepresentation()) + { + case REPRESENTATION_EMPTY: + s.Clear(); + RETURN 1; + + case REPRESENTATION_ASCII: + case REPRESENTATION_UTF8: + s.Set(*this); + RETURN s.GetRawCount()+1; + + case REPRESENTATION_ANSI: + // No direct conversion from ANSI to UTF8 + ConvertToUnicode(); + // fall through + + case REPRESENTATION_UNICODE: + break; + + default: + UNREACHABLE(); + } + + // <TODO> @todo: use WC_NO_BEST_FIT_CHARS </TODO> + bool allAscii; + DWORD length; + + HRESULT hr = FString::Unicode_Utf8_Length(GetRawUnicode(), & allAscii, & length); + + if (SUCCEEDED(hr)) + { + s.Resize(length, REPRESENTATION_UTF8); + + //FString::Unicode_Utf8 expects an array all the time + //we optimize the empty string by replacing it with null for SString above in Resize + if (length > 0) + { + hr = FString::Unicode_Utf8(GetRawUnicode(), allAscii, (LPSTR) s.GetRawUTF8(), length); + } + } + + IfFailThrow(hr); + + RETURN length + 1; +} + +//----------------------------------------------------------------------------- +// Replace a single character with another character. +//----------------------------------------------------------------------------- +void SString::Replace(const Iterator &i, WCHAR c) +{ + CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(CheckIteratorRange(i, 1)); + POSTCONDITION(Match(i, c)); + THROWS; + GC_NOTRIGGER; + } + CONTRACT_END; + + if (IsRepresentation(REPRESENTATION_ASCII) && ((c&~0x7f) == 0)) + { + *(BYTE*)i.m_ptr = (BYTE) c; + } + else + { + ConvertToUnicode(i); + + *(USHORT*)i.m_ptr = c; + } + + RETURN; +} + +//----------------------------------------------------------------------------- +// Replace the substring specified by position, length with the given string s. +//----------------------------------------------------------------------------- +void SString::Replace(const Iterator &i, COUNT_T length, const SString &s) +{ + CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(CheckIteratorRange(i, length)); + PRECONDITION(s.Check()); + POSTCONDITION(Match(i, s)); + THROWS; + GC_NOTRIGGER; + SUPPORTS_DAC_HOST_ONLY; + } + CONTRACT_END; + + Representation representation = GetRepresentation(); + if (representation == REPRESENTATION_EMPTY) + { + // This special case contains some optimizations (like literal sharing). + Set(s); + ConvertToIteratable(); + i.Resync(this, m_buffer); + } + else + { + StackSString temp; + const SString &source = GetCompatibleString(s, temp, i); + + COUNT_T deleteSize = length<<GetCharacterSizeShift(); + COUNT_T insertSize = source.GetRawCount()<<source.GetCharacterSizeShift(); + + SBuffer::Replace(i, deleteSize, insertSize); + SBuffer::Copy(i, source.m_buffer, insertSize); + } + + RETURN; +} + +//----------------------------------------------------------------------------- +// Find s in this string starting at i. Return TRUE & update iterator if found. +//----------------------------------------------------------------------------- +BOOL SString::Find(CIterator &i, const SString &s) const +{ + CONTRACT(BOOL) + { + INSTANCE_CHECK; + PRECONDITION(CheckIteratorRange(i)); + PRECONDITION(s.Check()); + POSTCONDITION(RETVAL == Match(i, s)); + THROWS_UNLESS_BOTH_NORMALIZED(s); + GC_NOTRIGGER; + } + CONTRACT_END; + + // Get a compatible string from s + StackSString temp; + const SString &source = GetCompatibleString(s, temp, i); + + switch (GetRepresentation()) + { + case REPRESENTATION_UNICODE: + { + COUNT_T count = source.GetRawCount(); + const WCHAR *start = i.GetUnicode(); + const WCHAR *end = GetUnicode() + GetRawCount() - count; + while (start <= end) + { + if (wcsncmp(start, source.GetRawUnicode(), count) == 0) + { + i.Resync(this, (BYTE*) start); + RETURN TRUE; + } + start++; + } + } + break; + + case REPRESENTATION_ANSI: + case REPRESENTATION_ASCII: + { + COUNT_T count = source.GetRawCount(); + const CHAR *start = i.GetASCII(); + const CHAR *end = GetRawASCII() + GetRawCount() - count; + while (start <= end) + { + if (strncmp(start, source.GetRawASCII(), count) == 0) + { + i.Resync(this, (BYTE*) start); + RETURN TRUE; + } + start++; + } + } + break; + + case REPRESENTATION_EMPTY: + { + if (source.GetRawCount() == 0) + RETURN TRUE; + } + break; + + case REPRESENTATION_UTF8: + default: + UNREACHABLE(); + } + + RETURN FALSE; +} + +//----------------------------------------------------------------------------- +// Find s in this string starting at i. Return TRUE & update iterator if found. +//----------------------------------------------------------------------------- +BOOL SString::Find(CIterator &i, WCHAR c) const +{ + CONTRACT(BOOL) + { + INSTANCE_CHECK; + PRECONDITION(CheckIteratorRange(i)); + POSTCONDITION(RETVAL == Match(i, c)); + THROWS_UNLESS_NORMALIZED; + GC_NOTRIGGER; + } + CONTRACT_END; + + // Get a compatible string + if (c & ~0x7f) + ConvertToUnicode(i); + + switch (GetRepresentation()) + { + case REPRESENTATION_UNICODE: + { + const WCHAR *start = i.GetUnicode(); + const WCHAR *end = GetUnicode() + GetRawCount() - 1; + while (start <= end) + { + if (*start == c) + { + i.Resync(this, (BYTE*) start); + RETURN TRUE; + } + start++; + } + } + break; + + case REPRESENTATION_ANSI: + case REPRESENTATION_ASCII: + { + const CHAR *start = i.GetASCII(); + const CHAR *end = GetRawASCII() + GetRawCount() - 1; + while (start <= end) + { + if (*start == c) + { + i.Resync(this, (BYTE*) start); + RETURN TRUE; + } + start++; + } + } + break; + + case REPRESENTATION_EMPTY: + break; + + case REPRESENTATION_UTF8: + default: + UNREACHABLE(); + } + + RETURN FALSE; +} + +//----------------------------------------------------------------------------- +// Find s in this string, working backwards staring at i. +// Return TRUE and update iterator if found. +//----------------------------------------------------------------------------- +BOOL SString::FindBack(CIterator &i, const SString &s) const +{ + CONTRACT(BOOL) + { + INSTANCE_CHECK; + PRECONDITION(CheckIteratorRange(i)); + PRECONDITION(s.Check()); + POSTCONDITION(RETVAL == Match(i, s)); + THROWS_UNLESS_BOTH_NORMALIZED(s); + GC_NOTRIGGER; + } + CONTRACT_END; + + // Get a compatible string from s + StackSString temp; + const SString &source = GetCompatibleString(s, temp, i); + + switch (GetRepresentation()) + { + case REPRESENTATION_UNICODE: + { + COUNT_T count = source.GetRawCount(); + const WCHAR *start = GetRawUnicode() + GetRawCount() - count; + if (start > i.GetUnicode()) + start = i.GetUnicode(); + const WCHAR *end = GetRawUnicode(); + + while (start >= end) + { + if (wcsncmp(start, source.GetRawUnicode(), count) == 0) + { + i.Resync(this, (BYTE*) start); + RETURN TRUE; + } + start--; + } + } + break; + + case REPRESENTATION_ANSI: + case REPRESENTATION_ASCII: + { + COUNT_T count = source.GetRawCount(); + const CHAR *start = GetRawASCII() + GetRawCount() - count; + if (start > i.GetASCII()) + start = i.GetASCII(); + const CHAR *end = GetRawASCII(); + + while (start >= end) + { + if (strncmp(start, source.GetRawASCII(), count) == 0) + { + i.Resync(this, (BYTE*) start); + RETURN TRUE; + } + start--; + } + } + break; + + case REPRESENTATION_EMPTY: + { + if (source.GetRawCount() == 0) + RETURN TRUE; + } + break; + + case REPRESENTATION_UTF8: + default: + UNREACHABLE(); + } + + RETURN FALSE; +} + +//----------------------------------------------------------------------------- +// Find s in this string, working backwards staring at i. +// Return TRUE and update iterator if found. +//----------------------------------------------------------------------------- +BOOL SString::FindBack(CIterator &i, WCHAR c) const +{ + CONTRACT(BOOL) + { + INSTANCE_CHECK; + PRECONDITION(CheckIteratorRange(i)); + POSTCONDITION(RETVAL == Match(i, c)); + THROWS_UNLESS_NORMALIZED; + GC_NOTRIGGER; + } + CONTRACT_END; + + // Get a compatible string from s + if (c & ~0x7f) + ConvertToUnicode(i); + + switch (GetRepresentation()) + { + case REPRESENTATION_UNICODE: + { + const WCHAR *start = GetRawUnicode() + GetRawCount() - 1; + if (start > i.GetUnicode()) + start = i.GetUnicode(); + const WCHAR *end = GetRawUnicode(); + + while (start >= end) + { + if (*start == c) + { + i.Resync(this, (BYTE*) start); + RETURN TRUE; + } + start--; + } + } + break; + + case REPRESENTATION_ANSI: + case REPRESENTATION_ASCII: + { + const CHAR *start = GetRawASCII() + GetRawCount() - 1; + if (start > i.GetASCII()) + start = i.GetASCII(); + const CHAR *end = GetRawASCII(); + + while (start >= end) + { + if (*start == c) + { + i.Resync(this, (BYTE*) start); + RETURN TRUE; + } + start--; + } + } + break; + + case REPRESENTATION_EMPTY: + break; + + case REPRESENTATION_UTF8: + default: + UNREACHABLE(); + } + + RETURN FALSE; +} + +//----------------------------------------------------------------------------- +// Returns TRUE if this string begins with the contents of s +//----------------------------------------------------------------------------- +BOOL SString::BeginsWith(const SString &s) const +{ + WRAPPER_NO_CONTRACT; + + return Match(Begin(), s); +} + +//----------------------------------------------------------------------------- +// Returns TRUE if this string begins with the contents of s +//----------------------------------------------------------------------------- +BOOL SString::BeginsWithCaseInsensitive(const SString &s, LocaleID locale) const +{ + WRAPPER_NO_CONTRACT; + + return MatchCaseInsensitive(Begin(), s, locale); +} + +//----------------------------------------------------------------------------- +// Returns TRUE if this string begins with the contents of s. Invariant locale. +//----------------------------------------------------------------------------- +BOOL SString::BeginsWithCaseInsensitive(const SString &s) const +{ + WRAPPER_NO_CONTRACT; + + return BeginsWithCaseInsensitive(s, s_defaultLCID); +} + +//----------------------------------------------------------------------------- +// Returns TRUE if this string ends with the contents of s +//----------------------------------------------------------------------------- +BOOL SString::EndsWith(const SString &s) const +{ + WRAPPER_NO_CONTRACT; + + // Need this check due to iterator arithmetic below. + if (GetCount() < s.GetCount()) + { + return FALSE; + } + + return Match(End() - s.GetCount(), s); +} + +//----------------------------------------------------------------------------- +// Returns TRUE if this string ends with the contents of s +//----------------------------------------------------------------------------- +BOOL SString::EndsWithCaseInsensitive(const SString &s, LocaleID locale) const +{ + WRAPPER_NO_CONTRACT; + + // Need this check due to iterator arithmetic below. + if (GetCount() < s.GetCount()) + { + return FALSE; + } + + return MatchCaseInsensitive(End() - s.GetCount(), s, locale); +} + +//----------------------------------------------------------------------------- +// Returns TRUE if this string ends with the contents of s. Invariant locale. +//----------------------------------------------------------------------------- +BOOL SString::EndsWithCaseInsensitive(const SString &s) const +{ + WRAPPER_NO_CONTRACT; + + return EndsWithCaseInsensitive(s, s_defaultLCID); +} + +//----------------------------------------------------------------------------- +// Compare this string's contents to s's contents. +// The comparison does not take into account localization issues like case folding. +// Return 0 if equal, <0 if this < s, >0 is this > s. (same as strcmp). +//----------------------------------------------------------------------------- +int SString::Compare(const SString &s) const +{ + CONTRACT(int) + { + INSTANCE_CHECK; + PRECONDITION(s.Check()); + THROWS_UNLESS_BOTH_NORMALIZED(s); + GC_NOTRIGGER; + } + CONTRACT_END; + + StackSString temp; + const SString &source = GetCompatibleString(s, temp); + + COUNT_T smaller; + int equals = 0; + int result = 0; + + if (GetRawCount() < source.GetRawCount()) + { + smaller = GetRawCount(); + equals = -1; + } + else if (GetRawCount() > source.GetRawCount()) + { + smaller = source.GetRawCount(); + equals = 1; + } + else + { + smaller = GetRawCount(); + equals = 0; + } + + switch (GetRepresentation()) + { + case REPRESENTATION_UNICODE: + result = wcsncmp(GetRawUnicode(), source.GetRawUnicode(), smaller); + break; + + case REPRESENTATION_ASCII: + case REPRESENTATION_ANSI: + result = strncmp(GetRawASCII(), source.GetRawASCII(), smaller); + break; + + case REPRESENTATION_EMPTY: + result = 0; + break; + + default: + case REPRESENTATION_UTF8: + UNREACHABLE(); + } + + if (result == 0) + RETURN equals; + else + RETURN result; +} + +//----------------------------------------------------------------------------- +// Compare this string's contents to s's contents. +// Return 0 if equal, <0 if this < s, >0 is this > s. (same as strcmp). +//----------------------------------------------------------------------------- + +int SString::CompareCaseInsensitive(const SString &s, LocaleID lcid) const +{ + CONTRACT(int) + { + INSTANCE_CHECK; + PRECONDITION(s.Check()); + THROWS_UNLESS_BOTH_NORMALIZED(s); + GC_NOTRIGGER; + } + CONTRACT_END; + + StackSString temp; + const SString &source = GetCompatibleString(s, temp); + + COUNT_T smaller; + int equals = 0; + int result = 0; + + if (GetRawCount() < source.GetRawCount()) + { + smaller = GetRawCount(); + equals = -1; + } + else if (GetRawCount() > source.GetRawCount()) + { + smaller = source.GetRawCount(); + equals = 1; + } + else + { + smaller = GetRawCount(); + equals = 0; + } + + switch (GetRepresentation()) + { + case REPRESENTATION_UNICODE: + result = CaseCompareHelper(GetRawUnicode(), source.GetRawUnicode(), smaller, lcid, FALSE, TRUE); + break; + + case REPRESENTATION_ASCII: + case REPRESENTATION_ANSI: + result = CaseCompareHelperA(GetRawASCII(), source.GetRawASCII(), smaller, lcid, FALSE, TRUE); + break; + + case REPRESENTATION_EMPTY: + result = 0; + break; + + default: + case REPRESENTATION_UTF8: + UNREACHABLE(); + } + + if (result == 0) + RETURN equals; + else + RETURN result; +} + +//----------------------------------------------------------------------------- +// Compare this string's contents to s's contents. +// The comparison does not take into account localization issues like case folding. +// Return 1 if equal, 0 if not. +//----------------------------------------------------------------------------- +BOOL SString::Equals(const SString &s) const +{ + CONTRACT(BOOL) + { + INSTANCE_CHECK; + PRECONDITION(s.Check()); + THROWS_UNLESS_BOTH_NORMALIZED(s); + FAULTS_UNLESS_BOTH_NORMALIZED(s, ThrowOutOfMemory()); + GC_NOTRIGGER; + } + CONTRACT_END; + + StackSString temp; + const SString &source = GetCompatibleString(s, temp); + + COUNT_T count = GetRawCount(); + + if (count != source.GetRawCount()) + RETURN FALSE; + + switch (GetRepresentation()) + { + case REPRESENTATION_UNICODE: + RETURN (wcsncmp(GetRawUnicode(), source.GetRawUnicode(), count) == 0); + + case REPRESENTATION_ASCII: + case REPRESENTATION_ANSI: + RETURN (strncmp(GetRawASCII(), source.GetRawASCII(), count) == 0); + + case REPRESENTATION_EMPTY: + RETURN TRUE; + + default: + case REPRESENTATION_UTF8: + UNREACHABLE(); + } + + RETURN FALSE; +} + +//----------------------------------------------------------------------------- +// Compare this string's contents case insensitively to s's contents. +// Return 1 if equal, 0 if not. +//----------------------------------------------------------------------------- +BOOL SString::EqualsCaseInsensitive(const SString &s, LocaleID lcid) const +{ + CONTRACT(BOOL) + { + INSTANCE_CHECK; + PRECONDITION(s.Check()); + THROWS_UNLESS_BOTH_NORMALIZED(s); + FAULTS_UNLESS_BOTH_NORMALIZED(s, ThrowOutOfMemory()); + GC_NOTRIGGER; + } + CONTRACT_END; + + StackSString temp; + const SString &source = GetCompatibleString(s, temp); + + COUNT_T count = GetRawCount(); + + if (count != source.GetRawCount()) + RETURN FALSE; + + switch (GetRepresentation()) + { + case REPRESENTATION_UNICODE: + RETURN (CaseCompareHelper(GetRawUnicode(), source.GetRawUnicode(), count, lcid, FALSE, TRUE) == 0); + + case REPRESENTATION_ASCII: + case REPRESENTATION_ANSI: + RETURN (CaseCompareHelperA(GetRawASCII(), source.GetRawASCII(), count, lcid, FALSE, TRUE) == 0); + + case REPRESENTATION_EMPTY: + RETURN TRUE; + + default: + case REPRESENTATION_UTF8: + UNREACHABLE(); + } + + RETURN FALSE; +} + +//----------------------------------------------------------------------------- +// Compare s's contents to the substring starting at position +// The comparison does not take into account localization issues like case folding. +// Return TRUE if equal, FALSE if not +//----------------------------------------------------------------------------- +BOOL SString::Match(const CIterator &i, const SString &s) const +{ + CONTRACT(BOOL) + { + INSTANCE_CHECK; + PRECONDITION(CheckIteratorRange(i)); + PRECONDITION(s.Check()); + THROWS_UNLESS_BOTH_NORMALIZED(s); + GC_NOTRIGGER; + } + CONTRACT_END; + + StackSString temp; + const SString &source = GetCompatibleString(s, temp, i); + + COUNT_T remaining = End() - i; + COUNT_T count = source.GetRawCount(); + + if (remaining < count) + RETURN FALSE; + + switch (GetRepresentation()) + { + case REPRESENTATION_UNICODE: + RETURN (wcsncmp(i.GetUnicode(), source.GetRawUnicode(), count) == 0); + + case REPRESENTATION_ASCII: + case REPRESENTATION_ANSI: + RETURN (strncmp(i.GetASCII(), source.GetRawASCII(), count) == 0); + + case REPRESENTATION_EMPTY: + RETURN TRUE; + + default: + case REPRESENTATION_UTF8: + UNREACHABLE(); + } + + RETURN FALSE; +} + +//----------------------------------------------------------------------------- +// Compare s's contents case insensitively to the substring starting at position +// Return TRUE if equal, FALSE if not +//----------------------------------------------------------------------------- +BOOL SString::MatchCaseInsensitive(const CIterator &i, const SString &s, LocaleID lcid) const +{ + CONTRACT(BOOL) + { + INSTANCE_CHECK; + PRECONDITION(CheckIteratorRange(i)); + PRECONDITION(s.Check()); + THROWS_UNLESS_BOTH_NORMALIZED(s); + GC_NOTRIGGER; + } + CONTRACT_END; + + StackSString temp; + const SString &source = GetCompatibleString(s, temp, i); + + COUNT_T remaining = End() - i; + COUNT_T count = source.GetRawCount(); + + if (remaining < count) + RETURN FALSE; + + switch (GetRepresentation()) + { + case REPRESENTATION_UNICODE: + case REPRESENTATION_ANSI: + RETURN (CaseCompareHelper(i.GetUnicode(), source.GetRawUnicode(), count, lcid, FALSE, TRUE) == 0); + + case REPRESENTATION_ASCII: + RETURN (CaseCompareHelperA(i.GetASCII(), source.GetRawASCII(), count, lcid, FALSE, TRUE) == 0); + + case REPRESENTATION_EMPTY: + RETURN TRUE; + + default: + case REPRESENTATION_UTF8: + UNREACHABLE(); + } + + RETURN FALSE; +} + +//----------------------------------------------------------------------------- +// Compare c case insensitively to the character at position +// Return TRUE if equal, FALSE if not +//----------------------------------------------------------------------------- +BOOL SString::MatchCaseInsensitive(const CIterator &i, WCHAR c, LocaleID lcid) const +{ + SS_CONTRACT(BOOL) + { + GC_NOTRIGGER; + INSTANCE_CHECK; + PRECONDITION(CheckIteratorRange(i)); + NOTHROW; + } + SS_CONTRACT_END; + + // End() will not throw here + CONTRACT_VIOLATION(ThrowsViolation); + if (i >= End()) + SS_RETURN FALSE; + + WCHAR test = i[0]; + + SS_RETURN (test == c + || ((CAN_SIMPLE_UPCASE(test) ? SIMPLE_UPCASE(test) : MapChar(test, LCMAP_UPPERCASE, lcid)) + == (CAN_SIMPLE_UPCASE(c) ? SIMPLE_UPCASE(c) : MapChar(c, LCMAP_UPPERCASE, lcid)))); +} + +//----------------------------------------------------------------------------- +// Convert string to unicode lowercase using the invariant culture +// Note: Please don't use it in PATH as multiple character can map to the same +// lower case symbol +//----------------------------------------------------------------------------- +void SString::LowerCase() +{ + SS_CONTRACT_VOID + { + GC_NOTRIGGER; + PRECONDITION(CheckPointer(this)); + SS_POSTCONDITION(CheckPointer(RETVAL)); + if (IsRepresentation(REPRESENTATION_UNICODE)) NOTHROW; else THROWS; + SUPPORTS_DAC; + } + SS_CONTRACT_END; + + ConvertToUnicode(); + + for (WCHAR *pwch = GetRawUnicode(); pwch < GetRawUnicode() + GetRawCount(); ++pwch) + { + *pwch = (CAN_SIMPLE_DOWNCASE(*pwch) ? SIMPLE_DOWNCASE(*pwch) : MapChar(*pwch, LCMAP_LOWERCASE, s_defaultLCID)); + } +} + +//----------------------------------------------------------------------------- +// Convert null-terminated string to lowercase using the invariant culture +//----------------------------------------------------------------------------- +//static +void SString::LowerCase(__inout_z LPWSTR wszString) +{ + SS_CONTRACT_VOID + { + GC_NOTRIGGER; + NOTHROW; + SUPPORTS_DAC; + } + SS_CONTRACT_END; + + if (wszString == NULL) + { + return; + } + + for (WCHAR * pwch = wszString; *pwch != '\0'; ++pwch) + { + *pwch = (CAN_SIMPLE_DOWNCASE(*pwch) ? SIMPLE_DOWNCASE(*pwch) : MapChar(*pwch, LCMAP_LOWERCASE, s_defaultLCID)); + } +} + +//----------------------------------------------------------------------------- +// Convert string to unicode uppercase using the invariant culture +// Note: Please don't use it in PATH as multiple character can map to the same +// upper case symbol +//----------------------------------------------------------------------------- +void SString::UpperCase() +{ + SS_CONTRACT_VOID + { + GC_NOTRIGGER; + PRECONDITION(CheckPointer(this)); + SS_POSTCONDITION(CheckPointer(RETVAL)); + if (IsRepresentation(REPRESENTATION_UNICODE)) NOTHROW; else THROWS; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + SS_CONTRACT_END; + + ConvertToUnicode(); + + for (WCHAR *pwch = GetRawUnicode(); pwch < GetRawUnicode() + GetRawCount(); ++pwch) + { + *pwch = (CAN_SIMPLE_UPCASE(*pwch) ? SIMPLE_UPCASE(*pwch) : MapChar(*pwch, LCMAP_UPPERCASE, s_defaultLCID)); + } +} + +//----------------------------------------------------------------------------- +// Get a const pointer to the internal buffer as an ANSI string. +//----------------------------------------------------------------------------- +const CHAR *SString::GetANSI(AbstractScratchBuffer &scratch) const +{ + SS_CONTRACT(const CHAR *) + { + INSTANCE_CHECK_NULL; + THROWS; + GC_NOTRIGGER; + } + SS_CONTRACT_END; + + if (this == NULL) + SS_RETURN NULL; + + if (IsRepresentation(REPRESENTATION_ANSI)) + SS_RETURN GetRawANSI(); + + ConvertToANSI((SString&)scratch); + SS_RETURN ((SString&)scratch).GetRawANSI(); +} + +//----------------------------------------------------------------------------- +// Get a const pointer to the internal buffer as a UTF8 string. +//----------------------------------------------------------------------------- +const UTF8 *SString::GetUTF8(AbstractScratchBuffer &scratch) const +{ + CONTRACT(const UTF8 *) + { + INSTANCE_CHECK_NULL; + THROWS; + GC_NOTRIGGER; + } + CONTRACT_END; + + if (this == NULL) + RETURN NULL; + + if (IsRepresentation(REPRESENTATION_UTF8)) + RETURN GetRawUTF8(); + + ConvertToUTF8((SString&)scratch); + RETURN ((SString&)scratch).GetRawUTF8(); +} + +const UTF8 *SString::GetUTF8(AbstractScratchBuffer &scratch, COUNT_T *pcbUtf8) const +{ + CONTRACT(const UTF8 *) + { + INSTANCE_CHECK_NULL; + THROWS; + GC_NOTRIGGER; + } + CONTRACT_END; + + if (this == NULL) + RETURN NULL; + + if (IsRepresentation(REPRESENTATION_UTF8)) + { + *pcbUtf8 = GetRawCount() + 1; + RETURN GetRawUTF8(); + } + + *pcbUtf8 = ConvertToUTF8((SString&)scratch); + RETURN ((SString&)scratch).GetRawUTF8(); +} + +//----------------------------------------------------------------------------- +// Get a const pointer to the internal buffer which must already be a UTF8 string. +// This avoids the need to create a scratch buffer we know will never be used. +//----------------------------------------------------------------------------- +const UTF8 *SString::GetUTF8NoConvert() const +{ + CONTRACT(const UTF8 *) + { + INSTANCE_CHECK_NULL; + THROWS; + GC_NOTRIGGER; + } + CONTRACT_END; + + if (this == NULL) + RETURN NULL; + + if (IsRepresentation(REPRESENTATION_UTF8)) + RETURN GetRawUTF8(); + + ThrowHR(E_INVALIDARG); +} + +//----------------------------------------------------------------------------- +// Safe version of sprintf. +// Prints formatted ansi text w/ var args to this buffer. +//----------------------------------------------------------------------------- +void SString::Printf(const CHAR *format, ...) +{ + WRAPPER_NO_CONTRACT; + + va_list args; + va_start(args, format); + VPrintf(format, args); + va_end(args); +} + +#ifdef _DEBUG +// +// Check the Printf use for potential globalization bugs. %S formatting +// specifier does Unicode->Ansi or Ansi->Unicode conversion using current +// C-locale. This almost always means globalization bug in the CLR codebase. +// +// Ideally, we would elimitate %S from all format strings. Unfortunately, +// %S is too widespread in non-shipping code that such cleanup is not feasible. +// +static void CheckForFormatStringGlobalizationIssues(const SString &format, const SString &result) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + DEBUG_ONLY; + } + CONTRACTL_END; + + BOOL fDangerousFormat = FALSE; + + // Check whether the format string contains the %S formatting specifier + SString::CIterator itrFormat = format.Begin(); + while (*itrFormat) + { + if (*itrFormat++ == '%') + { + // <TODO>Handle the complex format strings like %blahS</TODO> + if (*itrFormat++ == 'S') + { + fDangerousFormat = TRUE; + break; + } + } + } + + if (fDangerousFormat) + { + BOOL fNonAsciiUsed = FALSE; + + // Now check whether there are any non-ASCII characters in the output. + + // Check whether the result contains non-Ascii characters + SString::CIterator itrResult = format.Begin(); + while (*itrResult) + { + if (*itrResult++ > 127) + { + fNonAsciiUsed = TRUE; + break; + } + } + + CONSISTENCY_CHECK_MSGF(!fNonAsciiUsed, + ("Non-ASCII string was produced by %%S format specifier. This is likely globalization bug." + "To fix this, change the format string to %%s and do the correct encoding at the Printf callsite")); + } +} +#endif + +#ifndef EBADF +#define EBADF 9 +#endif + +#ifndef ENOMEM +#define ENOMEM 12 +#endif + +#ifndef ERANGE +#define ERANGE 34 +#endif + +void SString::VPrintf(const CHAR *format, va_list args) +{ + CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(CheckPointer(format)); + THROWS; + GC_NOTRIGGER; + } + CONTRACT_END; + + // sprintf gives us no means to know how many characters are written + // other than guessing and trying + + if (GetRawCount() > 0) + { + // First, try to use the existing buffer + int result = _vsnprintf_s(GetRawANSI(), GetRawCount()+1, _TRUNCATE, format, args); + + if (result >=0) + { + // Succeeded in writing. Now resize - + Resize(result, REPRESENTATION_ANSI, PRESERVE); + SString sss(Ansi, format); + INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this)); + RETURN; + } + } + + // Make a guess how long the result will be (note this will be doubled) + + COUNT_T guess = (COUNT_T) strlen(format)+1; + if (guess < GetRawCount()) + guess = GetRawCount(); + if (guess < MINIMUM_GUESS) + guess = MINIMUM_GUESS; + + while (TRUE) + { + // Double the previous guess - eventually we will get enough space + guess *= 2; + Resize(guess, REPRESENTATION_ANSI); + + // Clear errno to avoid false alarms + errno = 0; + + int result = _vsnprintf_s(GetRawANSI(), GetRawCount()+1, _TRUNCATE, format, args); + + if (result >= 0) + { + // Succeed in writing. Shrink the buffer to fit exactly. + Resize(result, REPRESENTATION_ANSI, PRESERVE); + SString sss(Ansi, format); + INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this)); + RETURN; + } + + if (errno==ENOMEM) + { + ThrowOutOfMemory(); + } + else + if (errno!=0 && errno!=EBADF && errno!=ERANGE) + { + CONSISTENCY_CHECK_MSG(FALSE, "_vsnprintf failed. Potential globalization bug."); + ThrowHR(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION)); + } + } + RETURN; +} + +void SString::Printf(const WCHAR *format, ...) +{ + WRAPPER_NO_CONTRACT; + + va_list args; + va_start(args, format); + VPrintf(format, args); + va_end(args); +} + +void SString::PPrintf(const WCHAR *format, ...) +{ + CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(CheckPointer(format)); + THROWS; + GC_NOTRIGGER; + } + CONTRACT_END; + + va_list argItr; + va_start(argItr, format); + PVPrintf(format, argItr); + va_end(argItr); + + RETURN; +} + +void SString::VPrintf(const WCHAR *format, va_list args) +{ + CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(CheckPointer(format)); + THROWS; + GC_NOTRIGGER; + } + CONTRACT_END; + + // sprintf gives us no means to know how many characters are written + // other than guessing and trying + + if (GetRawCount() > 0) + { + // First, try to use the existing buffer + int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, args); + + if (result >= 0) + { + // succeeded + Resize(result, REPRESENTATION_UNICODE, PRESERVE); + SString sss(format); + INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this)); + RETURN; + } + } + + // Make a guess how long the result will be (note this will be doubled) + + COUNT_T guess = (COUNT_T) wcslen(format)+1; + if (guess < GetRawCount()) + guess = GetRawCount(); + if (guess < MINIMUM_GUESS) + guess = MINIMUM_GUESS; + + while (TRUE) + { + // Double the previous guess - eventually we will get enough space + guess *= 2; + Resize(guess, REPRESENTATION_UNICODE); + + // Clear errno to avoid false alarms + errno = 0; + + int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, args); + + if (result >= 0) + { + Resize(result, REPRESENTATION_UNICODE, PRESERVE); + SString sss(format); + INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this)); + RETURN; + } + + if (errno==ENOMEM) + { + ThrowOutOfMemory(); + } + else + if (errno!=0 && errno!=EBADF && errno!=ERANGE) + { + CONSISTENCY_CHECK_MSG(FALSE, "_vsnwprintf failed. Potential globalization bug."); + ThrowHR(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION)); + } + } + RETURN; +} + +void SString::PVPrintf(const WCHAR *format, va_list args) +{ + CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(CheckPointer(format)); + THROWS; + GC_NOTRIGGER; + } + CONTRACT_END; + + // sprintf gives us no means to know how many characters are written + // other than guessing and trying + + if (GetRawCount() > 0) + { + // First, try to use the existing buffer +#if defined(FEATURE_CORESYSTEM) + int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, args); +#else + int result = _vswprintf_p(GetRawUnicode(), GetRawCount()+1, format, args); +#endif + if (result >= 0) + { + // succeeded + Resize(result, REPRESENTATION_UNICODE, PRESERVE); + SString sss(format); + INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this)); + RETURN; + } + } + + // Make a guess how long the result will be (note this will be doubled) + + COUNT_T guess = (COUNT_T) wcslen(format)+1; + if (guess < GetRawCount()) + guess = GetRawCount(); + if (guess < MINIMUM_GUESS) + guess = MINIMUM_GUESS; + + while (TRUE) + { + // Double the previous guess - eventually we will get enough space + guess *= 2; + Resize(guess, REPRESENTATION_UNICODE, DONT_PRESERVE); + + // Clear errno to avoid false alarms + errno = 0; + +#if defined(FEATURE_CORESYSTEM) + int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, args); +#else + int result = _vswprintf_p(GetRawUnicode(), GetRawCount()+1, format, args); +#endif + + if (result >= 0) + { + Resize(result, REPRESENTATION_UNICODE, PRESERVE); + SString sss(format); + INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this)); + RETURN; + } + + if (errno==ENOMEM) + { + ThrowOutOfMemory(); + } + else + if (errno!=0 && errno!=EBADF && errno!=ERANGE) + { + CONSISTENCY_CHECK_MSG(FALSE, "_vsnwprintf failed. Potential globalization bug."); + ThrowHR(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION)); + } + } + RETURN; +} + +void SString::AppendPrintf(const CHAR *format, ...) +{ + WRAPPER_NO_CONTRACT; + + va_list args; + va_start(args, format); + AppendVPrintf(format, args); + va_end(args); +} + +void SString::AppendVPrintf(const CHAR *format, va_list args) +{ + WRAPPER_NO_CONTRACT; + + StackSString s; + s.VPrintf(format, args); + Append(s); +} + +void SString::AppendPrintf(const WCHAR *format, ...) +{ + WRAPPER_NO_CONTRACT; + + va_list args; + va_start(args, format); + AppendVPrintf(format, args); + va_end(args); +} + +void SString::AppendVPrintf(const WCHAR *format, va_list args) +{ + WRAPPER_NO_CONTRACT; + + StackSString s; + s.VPrintf(format, args); + Append(s); +} + +//---------------------------------------------------------------------------- +// LoadResource - moved to sstring_com.cpp +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- +// Format the message and put the contents in this string +//---------------------------------------------------------------------------- + +BOOL SString::FormatMessage(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, + const SString &arg1, const SString &arg2, + const SString &arg3, const SString &arg4, + const SString &arg5, const SString &arg6, + const SString &arg7, const SString &arg8, + const SString &arg9, const SString &arg10) +{ + CONTRACT(BOOL) + { + INSTANCE_CHECK; + THROWS; + GC_NOTRIGGER; + } + CONTRACT_END; + + const WCHAR *args[] = {arg1.GetUnicode(), arg2.GetUnicode(), arg3.GetUnicode(), arg4.GetUnicode(), + arg5.GetUnicode(), arg6.GetUnicode(), arg7.GetUnicode(), arg8.GetUnicode(), + arg9.GetUnicode(), arg10.GetUnicode()}; + + if (GetRawCount() > 0) + { + // First, try to use our existing buffer to hold the result. + Resize(GetRawCount(), REPRESENTATION_UNICODE); + + DWORD result = ::WszFormatMessage(dwFlags | FORMAT_MESSAGE_ARGUMENT_ARRAY, + lpSource, dwMessageId, dwLanguageId, + GetRawUnicode(), GetRawCount()+1, (va_list*)args); + + // Although we cannot directly detect truncation, we can tell if we + // used up all the space (in which case we will assume truncation.) + + if (result != 0 && result < GetRawCount()) + { + if (GetRawUnicode()[result-1] == W(' ')) + { + GetRawUnicode()[result-1] = W('\0'); + result -= 1; + } + Resize(result, REPRESENTATION_UNICODE, PRESERVE); + RETURN TRUE; + } + } + + // We don't have enough space in our buffer, do dynamic allocation. + LocalAllocHolder<WCHAR> string; + + DWORD result = ::WszFormatMessage(dwFlags | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY, + lpSource, dwMessageId, dwLanguageId, + (LPWSTR)(LPWSTR*)&string, 0, (va_list*)args); + + if (result == 0) + RETURN FALSE; + else + { + if (string[result-1] == W(' ')) + string[result-1] = W('\0'); + + Set(string); + RETURN TRUE; + } +} + +#if 1 +//---------------------------------------------------------------------------- +// Helper +//---------------------------------------------------------------------------- + +// @todo -this should be removed and placed outside of SString +void SString::MakeFullNamespacePath(const SString &nameSpace, const SString &name) +{ + CONTRACT_VOID + { + INSTANCE_CHECK; + THROWS; + GC_NOTRIGGER; + } + CONTRACT_END; + + if (nameSpace.GetRepresentation() == REPRESENTATION_UTF8 + && name.GetRepresentation() == REPRESENTATION_UTF8) + { + const UTF8 *ns = nameSpace.GetRawUTF8(); + const UTF8 *n = name.GetRawUTF8(); + COUNT_T count = ns::GetFullLength(ns, n)-1; + Resize(count, REPRESENTATION_UTF8); + if (count > 0) + ns::MakePath(GetRawUTF8(), count+1, ns, n); + } + else + { + const WCHAR *ns = nameSpace; + const WCHAR *n = name; + COUNT_T count = ns::GetFullLength(ns, n)-1; + Resize(count, REPRESENTATION_UNICODE); + if (count > 0) + ns::MakePath(GetRawUnicode(), count+1, ns, n); + } + + RETURN; +} +#endif + + + +//---------------------------------------------------------------------------- +// Private helper. +// Check to see if the string fits the suggested representation +//---------------------------------------------------------------------------- +BOOL SString::IsRepresentation(Representation representation) const +{ + CONTRACT(BOOL) + { + PRECONDITION(CheckRepresentation(representation)); + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACT_END; + + Representation currentRepresentation = GetRepresentation(); + + // If representations are the same, cool. + if (currentRepresentation == representation) + RETURN TRUE; + + // If we have an empty representation, we match everything + if (currentRepresentation == REPRESENTATION_EMPTY) + RETURN TRUE; + + // If we're a 1 byte charset, there are some more chances to match + if (currentRepresentation != REPRESENTATION_UNICODE + && representation != REPRESENTATION_UNICODE) + { + // If we're ASCII, we can be any 1 byte rep + if (currentRepresentation == REPRESENTATION_ASCII) + RETURN TRUE; + + // We really want to be ASCII - scan to see if we qualify + if (ScanASCII()) + RETURN TRUE; + } + + // Sorry, must convert. + RETURN FALSE; +} + +//---------------------------------------------------------------------------- +// Private helper. +// Get the contents of the given string in a form which is compatible with our +// string (and is in a fixed character set.) Updates the given iterator +// if necessary to keep it in sync. +//---------------------------------------------------------------------------- +const SString &SString::GetCompatibleString(const SString &s, SString &scratch, const CIterator &i) const +{ + CONTRACTL + { + PRECONDITION(s.Check()); + PRECONDITION(scratch.Check()); + PRECONDITION(scratch.CheckEmpty()); + THROWS_UNLESS_BOTH_NORMALIZED(s); + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACTL_END; + + // Since we have an iterator, we should be fixed size already + CONSISTENCY_CHECK(IsFixedSize()); + + switch (GetRepresentation()) + { + case REPRESENTATION_EMPTY: + return s; + + case REPRESENTATION_ASCII: + if (s.IsRepresentation(REPRESENTATION_ASCII)) + return s; + + // We can't in general convert to ASCII, so try unicode. + ConvertToUnicode(i); + // fall through + + case REPRESENTATION_UNICODE: + if (s.IsRepresentation(REPRESENTATION_UNICODE)) + return s; + + // @todo: we could convert s to unicode - is that a good policy???? + s.ConvertToUnicode(scratch); + return scratch; + + case REPRESENTATION_UTF8: + case REPRESENTATION_ANSI: + // These should all be impossible since we have an CIterator on us. + default: + UNREACHABLE_MSG("Unexpected string representation"); + } + + return s; +} + +//---------------------------------------------------------------------------- +// Private helper. +// Get the contents of the given string in a form which is compatible with our +// string (and is in a fixed character set.) +// May convert our string to unicode. +//---------------------------------------------------------------------------- +const SString &SString::GetCompatibleString(const SString &s, SString &scratch) const +{ + CONTRACTL + { + PRECONDITION(s.Check()); + PRECONDITION(scratch.Check()); + PRECONDITION(scratch.CheckEmpty()); + THROWS_UNLESS_BOTH_NORMALIZED(s); + GC_NOTRIGGER; + } + CONTRACTL_END; + + // First, make sure we have a fixed size. + ConvertToFixed(); + + switch (GetRepresentation()) + { + case REPRESENTATION_EMPTY: + return s; + + case REPRESENTATION_ANSI: + if (s.IsRepresentation(REPRESENTATION_ANSI)) + return s; + + s.ConvertToANSI(scratch); + return scratch; + + case REPRESENTATION_ASCII: + if (s.IsRepresentation(REPRESENTATION_ASCII)) + return s; + + // We can't in general convert to ASCII, so try unicode. + ConvertToUnicode(); + // fall through + + case REPRESENTATION_UNICODE: + if (s.IsRepresentation(REPRESENTATION_UNICODE)) + return s; + + // @todo: we could convert s to unicode in place - is that a good policy???? + s.ConvertToUnicode(scratch); + return scratch; + + case REPRESENTATION_UTF8: + default: + UNREACHABLE(); + } + + return s; +} + +//---------------------------------------------------------------------------- +// Private helper. +// If we have a 1 byte representation, scan the buffer to see if we can gain +// some conversion flexibility by labelling it ASCII +//---------------------------------------------------------------------------- +BOOL SString::ScanASCII() const +{ + CONTRACT(BOOL) + { + POSTCONDITION(IsRepresentation(REPRESENTATION_ASCII) || IsASCIIScanned()); + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACT_END; + + if (!IsASCIIScanned()) + { + const CHAR *c = GetRawANSI(); + const CHAR *cEnd = c + GetRawCount(); + while (c < cEnd) + { + if (*c & 0x80) + break; + c++; + } + if (c == cEnd) + { + const_cast<SString *>(this)->SetRepresentation(REPRESENTATION_ASCII); + RETURN TRUE; + } + else + const_cast<SString *>(this)->SetASCIIScanned(); + } + RETURN FALSE; +} + +//---------------------------------------------------------------------------- +// Private helper. +// Resize updates the geometry of the string and ensures that +// the space can be written to. +// count - number of characters (not including null) to hold +// preserve - if we realloc, do we copy data from old to new? +//---------------------------------------------------------------------------- + +void SString::Resize(COUNT_T count, SString::Representation representation, Preserve preserve) +{ + CONTRACT_VOID + { + PRECONDITION(CountToSize(count) >= count); + POSTCONDITION(IsRepresentation(representation)); + POSTCONDITION(GetRawCount() == count); + if (count == 0) NOTHROW; else THROWS; + GC_NOTRIGGER; + SUPPORTS_DAC_HOST_ONLY; + } + CONTRACT_END; + + // If we are resizing to zero, Clear is more efficient + if (count == 0) + { + Clear(); + } + else + { + SetRepresentation(representation); + + COUNT_T size = CountToSize(count); + + // detect overflow + if (size < count) + ThrowOutOfMemory(); + + ClearNormalized(); + + SBuffer::Resize(size, preserve); + + if (IsImmutable()) + EnsureMutable(); + + NullTerminate(); + } + + RETURN; +} + +//----------------------------------------------------------------------------- +// This is essentially a specialized version of the above for size 0 +//----------------------------------------------------------------------------- +void SString::Clear() +{ + CONTRACT_VOID + { + INSTANCE_CHECK; + POSTCONDITION(IsEmpty()); + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + SUPPORTS_DAC_HOST_ONLY; + } + CONTRACT_END; + + SetRepresentation(REPRESENTATION_EMPTY); + + if (IsImmutable()) + { + // Use shared empty string rather than allocating a new buffer + SBuffer::SetImmutable(s_EmptyBuffer, sizeof(s_EmptyBuffer)); + } + else + { + // Leave allocated buffer for future growth + SBuffer::TweakSize(sizeof(WCHAR)); + GetRawUnicode()[0] = 0; + } + + RETURN; +} + + +#ifdef DACCESS_COMPILE + +//--------------------------------------------------------------------------------------- +// +// Return a pointer to the raw buffer +// +// Returns: +// A pointer to the raw string buffer. +// +void * SString::DacGetRawContent() const +{ + if (IsEmpty()) + { + return NULL; + } + + switch (GetRepresentation()) + { + case REPRESENTATION_EMPTY: + return NULL; + + case REPRESENTATION_UNICODE: + case REPRESENTATION_UTF8: + case REPRESENTATION_ASCII: + case REPRESENTATION_ANSI: + // Note: no need to call DacInstantiateString because we know the exact length already. + return SBuffer::DacGetRawContent(); + + default: + DacNotImpl(); + return NULL; + } +} + +//--------------------------------------------------------------------------------------- +// +// Return a pointer to the raw buffer as a pointer to a unicode string. Does not +// do conversion, and thus requires that the representation already be in unicode. +// +// Returns: +// A pointer to the raw string buffer as a unicode string. +// +const WCHAR * SString::DacGetRawUnicode() const +{ + if (IsEmpty() || (GetRepresentation() == REPRESENTATION_EMPTY)) + { + return W(""); + } + + if (GetRepresentation() != REPRESENTATION_UNICODE) + { + DacError(E_UNEXPECTED); + } + + HRESULT status = S_OK; + WCHAR* wszBuf = NULL; + EX_TRY + { + wszBuf = static_cast<WCHAR*>(SBuffer::DacGetRawContent()); + } + EX_CATCH_HRESULT(status); + + if (SUCCEEDED(status)) + { + return wszBuf; + } + else + { + return NULL; + } +} + +//--------------------------------------------------------------------------------------- +// +// Copy the string from the target into the provided buffer, converting to unicode if necessary +// +// Arguments: +// cBufChars - size of pBuffer in count of unicode characters. +// pBuffer - a buffer of cBufChars unicode chars. +// pcNeedChars - space to store the number of unicode chars in the SString. +// +// Returns: +// true if successful - and buffer is filled with the unicode representation of +// the string. +// false if unsuccessful. +// +bool SString::DacGetUnicode(COUNT_T cBufChars, + __out_z __inout_ecount(cBufChars) WCHAR * pBuffer, + COUNT_T * pcNeedChars) const +{ + SUPPORTS_DAC; + + PVOID pContent = NULL; + int iPage = CP_ACP; + + if (IsEmpty() || (GetRepresentation() == REPRESENTATION_EMPTY)) + { + if (pcNeedChars) + { + *pcNeedChars = 1; + } + if (pBuffer && cBufChars) + { + pBuffer[0] = 0; + } + return true; + } + + HRESULT status = S_OK; + EX_TRY + { + pContent = SBuffer::DacGetRawContent(); + } + EX_CATCH_HRESULT(status); + + if (SUCCEEDED(status) && pContent != NULL) + { + switch (GetRepresentation()) + { + + case REPRESENTATION_UNICODE: + + if (pcNeedChars) + { + *pcNeedChars = GetCount() + 1; + } + + if (pBuffer && cBufChars) + { + if (cBufChars > GetCount() + 1) + { + cBufChars = GetCount() + 1; + } + memcpy(pBuffer, pContent, cBufChars * sizeof(*pBuffer)); + pBuffer[cBufChars - 1] = 0; + } + + return true; + + case REPRESENTATION_UTF8: + iPage = CP_UTF8; + case REPRESENTATION_ASCII: + case REPRESENTATION_ANSI: + // iPage defaults to CP_ACP. + if (pcNeedChars) + { + *pcNeedChars = WszMultiByteToWideChar(iPage, 0, reinterpret_cast<PSTR>(pContent), -1, NULL, 0); + } + if (pBuffer && cBufChars) + { + if (!WszMultiByteToWideChar(iPage, 0, reinterpret_cast<PSTR>(pContent), -1, pBuffer, cBufChars)) + { + return false; + } + } + return true; + + default: + DacNotImpl(); + return false; + } + } + return false; +} + +#endif //DACCESS_COMPILE diff --git a/src/utilcode/sstring_com.cpp b/src/utilcode/sstring_com.cpp new file mode 100644 index 0000000000..94cdd45c3b --- /dev/null +++ b/src/utilcode/sstring_com.cpp @@ -0,0 +1,103 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// --------------------------------------------------------------------------- +// SString_COM.cpp + +// --------------------------------------------------------------------------- + +#include "stdafx.h" +#include "sstring.h" +#include "ex.h" +#include "holder.h" + +#define DEFAULT_RESOURCE_STRING_SIZE 255 + +//---------------------------------------------------------------------------- +// Load the string resource into this string. +//---------------------------------------------------------------------------- +BOOL SString::LoadResource(CCompRC::ResourceCategory eCategory, int resourceID) +{ + return SUCCEEDED(LoadResourceAndReturnHR(eCategory, resourceID)); +} + +HRESULT SString::LoadResourceAndReturnHR(CCompRC::ResourceCategory eCategory, int resourceID) +{ + WRAPPER_NO_CONTRACT; + return LoadResourceAndReturnHR(NULL, eCategory,resourceID); +} + +HRESULT SString::LoadResourceAndReturnHR(CCompRC* pResourceDLL, CCompRC::ResourceCategory eCategory, int resourceID) +{ + CONTRACT(BOOL) + { + INSTANCE_CHECK; + NOTHROW; + } + CONTRACT_END; + + HRESULT hr = E_FAIL; + +#ifndef FEATURE_UTILCODE_NO_DEPENDENCIES + if (pResourceDLL == NULL) + { + pResourceDLL = CCompRC::GetDefaultResourceDll(); + } + + if (pResourceDLL != NULL) + { + + int size = 0; + + EX_TRY + { + if (GetRawCount() == 0) + Resize(DEFAULT_RESOURCE_STRING_SIZE, REPRESENTATION_UNICODE); + + while (TRUE) + { + // First try and load the string in the amount of space that we have. + // In fatal error reporting scenarios, we may not have enough memory to + // allocate a larger buffer. + + hr = pResourceDLL->LoadString(eCategory, resourceID, GetRawUnicode(), GetRawCount()+1,&size); + if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) + { + if (FAILED(hr)) + { + Clear(); + break; + } + + // Although we cannot generally detect truncation, we can tell if we + // used up all the space (in which case we will assume truncation.) + if (size < (int)GetRawCount()) + { + break; + } + } + + // Double the size and try again. + Resize(size*2, REPRESENTATION_UNICODE); + + } + + if (SUCCEEDED(hr)) + { + Truncate(Begin() + (COUNT_T) wcslen(GetRawUnicode())); + } + + Normalize(); + + } + EX_CATCH + { + hr = E_FAIL; + } + EX_END_CATCH(SwallowAllExceptions); + } +#endif //!FEATURE_UTILCODE_NO_DEPENDENCIES + + RETURN hr; +} // SString::LoadResourceAndReturnHR diff --git a/src/utilcode/stacktrace.cpp b/src/utilcode/stacktrace.cpp new file mode 100644 index 0000000000..7cea7ffc92 --- /dev/null +++ b/src/utilcode/stacktrace.cpp @@ -0,0 +1,978 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- + +#include "stdafx.h" + +#include "stacktrace.h" +#include <imagehlp.h> +#include "corhlpr.h" +#include "utilcode.h" +#include "pedecoder.h" // for IMAGE_FILE_MACHINE_NATIVE + +//This is a workaround. We need to work with the debugger team to figure +//out how the module handle of the CLR can be found in a SxS safe way. +HMODULE GetCLRModuleHack() +{ + static HMODULE s_hModCLR = 0; + if (!s_hModCLR) + { + s_hModCLR = GetModuleHandleA(MAIN_CLR_DLL_NAME_A); + } + return s_hModCLR; +} + +HINSTANCE LoadImageHlp() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_CANNOT_TAKE_LOCK; + SCAN_IGNORE_FAULT; // Faults from Wsz funcs are handled. + + return LoadLibraryExA("imagehlp.dll", NULL, 0); +} + +HINSTANCE LoadDbgHelp() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + SCAN_IGNORE_FAULT; // Faults from Wsz funcs are handled. + + return LoadLibraryExA("dbghelp.dll", NULL, 0); +} + +/**************************************************************************** +* SymCallback * +*---------------------* +* Description: +* Callback for imghelp. +****************************************************************************/ +BOOL __stdcall SymCallback +( +HANDLE hProcess, +ULONG ActionCode, +PVOID CallbackData, +PVOID UserContext +) +{ + WRAPPER_NO_CONTRACT; + + switch (ActionCode) + { + case CBA_DEBUG_INFO: + OutputDebugStringA("IMGHLP: "); + OutputDebugStringA((LPCSTR) CallbackData); + OutputDebugStringA("\n"); + break; + + case CBA_DEFERRED_SYMBOL_LOAD_START: + OutputDebugStringA("IMGHLP: Deferred symbol load start "); + OutputDebugStringA(((IMAGEHLP_DEFERRED_SYMBOL_LOAD*)CallbackData)->FileName); + OutputDebugStringA("\n"); + break; + + case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE: + OutputDebugStringA("IMGHLP: Deferred symbol load complete "); + OutputDebugStringA(((IMAGEHLP_DEFERRED_SYMBOL_LOAD*)CallbackData)->FileName); + OutputDebugStringA("\n"); + break; + + case CBA_DEFERRED_SYMBOL_LOAD_FAILURE: + OutputDebugStringA("IMGHLP: Deferred symbol load failure "); + OutputDebugStringA(((IMAGEHLP_DEFERRED_SYMBOL_LOAD*)CallbackData)->FileName); + OutputDebugStringA("\n"); + break; + + case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL: + OutputDebugStringA("IMGHLP: Deferred symbol load partial "); + OutputDebugStringA(((IMAGEHLP_DEFERRED_SYMBOL_LOAD*)CallbackData)->FileName); + OutputDebugStringA("\n"); + break; + } + + return FALSE; +} + +// @TODO_IA64: all of this stack trace stuff is pretty much broken on 64-bit +// right now because this code doesn't use the new SymXxxx64 functions. + +#define LOCAL_ASSERT(x) +// +//--- Macros ------------------------------------------------------------------ +// + +#define COUNT_OF(x) (sizeof(x) / sizeof(x[0])) + +// +// Types and Constants -------------------------------------------------------- +// + +struct SYM_INFO +{ + DWORD_PTR dwOffset; + char achModule[cchMaxAssertModuleLen]; + char achSymbol[cchMaxAssertSymbolLen]; +}; + +//--- Function Pointers to APIs in IMAGEHLP.DLL. Loaded dynamically. --------- + +typedef LPAPI_VERSION (__stdcall *pfnImgHlp_ImagehlpApiVersionEx)( + LPAPI_VERSION AppVersion + ); + +typedef BOOL (__stdcall *pfnImgHlp_StackWalk)( + DWORD MachineType, + HANDLE hProcess, + HANDLE hThread, + LPSTACKFRAME StackFrame, + LPVOID ContextRecord, + PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine, + PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine, + PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine, + PTRANSLATE_ADDRESS_ROUTINE TranslateAddress + ); + +#ifdef _WIN64 +typedef DWORD64 (__stdcall *pfnImgHlp_SymGetModuleBase64)( + IN HANDLE hProcess, + IN DWORD64 dwAddr + ); + +typedef IMAGEHLP_SYMBOL64 PLAT_IMAGEHLP_SYMBOL; +typedef IMAGEHLP_MODULE64 PLAT_IMAGEHLP_MODULE; + +#else +typedef IMAGEHLP_SYMBOL PLAT_IMAGEHLP_SYMBOL; +typedef IMAGEHLP_MODULE PLAT_IMAGEHLP_MODULE; +#endif + +#undef IMAGEHLP_SYMBOL +#undef IMAGEHLP_MODULE + + +typedef BOOL (__stdcall *pfnImgHlp_SymGetModuleInfo)( + IN HANDLE hProcess, + IN DWORD_PTR dwAddr, + OUT PLAT_IMAGEHLP_MODULE* ModuleInfo + ); + +typedef LPVOID (__stdcall *pfnImgHlp_SymFunctionTableAccess)( + HANDLE hProcess, + DWORD_PTR AddrBase + ); + +typedef BOOL (__stdcall *pfnImgHlp_SymGetSymFromAddr)( + IN HANDLE hProcess, + IN DWORD_PTR dwAddr, + OUT DWORD_PTR* pdwDisplacement, + OUT PLAT_IMAGEHLP_SYMBOL* Symbol + ); + +typedef BOOL (__stdcall *pfnImgHlp_SymInitialize)( + IN HANDLE hProcess, + IN LPSTR UserSearchPath, + IN BOOL fInvadeProcess + ); + +typedef BOOL (__stdcall *pfnImgHlp_SymUnDName)( + IN PLAT_IMAGEHLP_SYMBOL* sym, // Symbol to undecorate + OUT LPSTR UnDecName, // Buffer to store undecorated name in + IN DWORD UnDecNameLength // Size of the buffer + ); + +typedef BOOL (__stdcall *pfnImgHlp_SymLoadModule)( + IN HANDLE hProcess, + IN HANDLE hFile, + IN PSTR ImageName, + IN PSTR ModuleName, + IN DWORD_PTR BaseOfDll, + IN DWORD SizeOfDll + ); + +typedef BOOL (_stdcall *pfnImgHlp_SymRegisterCallback)( + IN HANDLE hProcess, + IN PSYMBOL_REGISTERED_CALLBACK CallbackFunction, + IN PVOID UserContext + ); + +typedef DWORD (_stdcall *pfnImgHlp_SymSetOptions)( + IN DWORD SymOptions + ); + +typedef DWORD (_stdcall *pfnImgHlp_SymGetOptions)( + ); + + +struct IMGHLPFN_LOAD +{ + LPSTR pszFnName; + LPVOID * ppvfn; +}; + + +#if defined(_WIN64) +typedef void (*pfn_GetRuntimeStackWalkInfo)( + IN ULONG64 ControlPc, + OUT UINT_PTR* pModuleBase, + OUT UINT_PTR* pFuncEntry + ); +#endif // _WIN64 + + +// +// Globals -------------------------------------------------------------------- +// + +static BOOL g_fLoadedImageHlp = FALSE; // set to true on success +static BOOL g_fLoadedImageHlpFailed = FALSE; // set to true on failure +static HINSTANCE g_hinstImageHlp = NULL; +static HINSTANCE g_hinstDbgHelp = NULL; +static HANDLE g_hProcess = NULL; + +pfnImgHlp_ImagehlpApiVersionEx _ImagehlpApiVersionEx; +pfnImgHlp_StackWalk _StackWalk; +pfnImgHlp_SymGetModuleInfo _SymGetModuleInfo; +pfnImgHlp_SymFunctionTableAccess _SymFunctionTableAccess; +pfnImgHlp_SymGetSymFromAddr _SymGetSymFromAddr; +pfnImgHlp_SymInitialize _SymInitialize; +pfnImgHlp_SymUnDName _SymUnDName; +pfnImgHlp_SymLoadModule _SymLoadModule; +pfnImgHlp_SymRegisterCallback _SymRegisterCallback; +pfnImgHlp_SymSetOptions _SymSetOptions; +pfnImgHlp_SymGetOptions _SymGetOptions; +#if defined(_WIN64) +pfn_GetRuntimeStackWalkInfo _GetRuntimeStackWalkInfo; +#endif // _WIN64 + +IMGHLPFN_LOAD ailFuncList[] = +{ + { "ImagehlpApiVersionEx", (LPVOID*)&_ImagehlpApiVersionEx }, + { "StackWalk", (LPVOID*)&_StackWalk }, + { "SymGetModuleInfo", (LPVOID*)&_SymGetModuleInfo }, + { "SymFunctionTableAccess", (LPVOID*)&_SymFunctionTableAccess }, + { "SymGetSymFromAddr", (LPVOID*)&_SymGetSymFromAddr }, + { "SymInitialize", (LPVOID*)&_SymInitialize }, + { "SymUnDName", (LPVOID*)&_SymUnDName }, + { "SymLoadModule", (LPVOID*)&_SymLoadModule }, + { "SymRegisterCallback", (LPVOID*)&_SymRegisterCallback }, + { "SymSetOptions", (LPVOID*)&_SymSetOptions }, + { "SymGetOptions", (LPVOID*)&_SymGetOptions }, +}; + + +/**************************************************************************** +* FillSymbolSearchPath * +*----------------------* +* Description: +* Manually pick out all the symbol path information we need for a real +* stack trace to work. This includes the default NT symbol paths and +* places on a VBL build machine where they should live. +****************************************************************************/ +#define MAX_SYM_PATH (1024*8) +#define DEFAULT_SYM_PATH W("symsrv*symsrv.dll*\\\\symbols\\symbols;") +#define STR_ENGINE_NAME MAIN_CLR_DLL_NAME_W +LPSTR FillSymbolSearchPath(CQuickBytes &qb) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_CANNOT_TAKE_LOCK; + SCAN_IGNORE_FAULT; // Faults from Wsz funcs are handled. + +#ifndef DACCESS_COMPILE + // not allowed to do allocation if current thread suspends EE. + if (IsSuspendEEThread ()) + return NULL; +#endif + + NewArrayHolder<WCHAR> rcBuff = new (nothrow) WCHAR[MAX_SYM_PATH]; // Working buffer + WCHAR rcVerString[64]; // Extension for install directory. + int chTotal = 0; // How full is working buffer. + int ch; + + + if (rcBuff == NULL) + return NULL; // Unable to allocate working buffer - fail + + ZeroMemory(rcBuff, MAX_SYM_PATH*sizeof(WCHAR)); + + // If the NT symbol server path vars are there, then use those. + chTotal = WszGetEnvironmentVariable(W("_NT_SYMBOL_PATH"), rcBuff, MAX_SYM_PATH); // Cannot use NumItems(rcBuff) since NumItems does not work with holders + if (chTotal + 1 < MAX_SYM_PATH) + rcBuff[chTotal++] = W(';'); + + // Copy the defacto NT symbol path as well. + size_t sympathLength = chTotal + NumItems(DEFAULT_SYM_PATH) + 1; + // integer overflow occurred + if (sympathLength < (size_t)chTotal || sympathLength < NumItems(DEFAULT_SYM_PATH)) + { + return NULL; + } + + if (sympathLength < MAX_SYM_PATH) + { + wcsncpy_s(&rcBuff[chTotal], MAX_SYM_PATH-chTotal, DEFAULT_SYM_PATH, _TRUNCATE); + chTotal = (int) wcslen(rcBuff); + } + + // Next, if there is a URTTARGET, add that since that is where ndpsetup places + // your symobls on an install. + ch = WszGetEnvironmentVariable(W("URTTARGET"), &rcBuff[chTotal], MAX_SYM_PATH - chTotal); + if (ch != 0 && (chTotal + ch + 1 < MAX_SYM_PATH)) + { + size_t chNewTotal = chTotal + ch; + if (chNewTotal < (size_t)chTotal || chNewTotal < (size_t)ch) + { // integer overflow occurred + return NULL; + } + chTotal += ch; + rcBuff[chTotal++] = W(';'); + } + +#ifndef SELF_NO_HOST + // Fetch the path location of the engine dll and add that path as well, just + // in case URTARGET didn't cut it either. + // For no-host builds of utilcode, we don't necessarily have an engine DLL in the + // process, so skip this part. + ch = WszGetModuleFileName(GetCLRModuleHack(), + &rcBuff[chTotal], MAX_SYM_PATH - chTotal); + size_t pathLocationLength = chTotal + ch + 1; + // integer overflow occurred + if (pathLocationLength < (size_t)chTotal || pathLocationLength < (size_t)ch) + { + return NULL; + } + + if (ch != 0 && (pathLocationLength < MAX_SYM_PATH)) + { + chTotal = chTotal + ch - NumItems(STR_ENGINE_NAME); + rcBuff[chTotal++] = W(';'); + rcBuff[chTotal] = 0; + } +#endif + + // We want to ensure the resulting string is always NULL-terminated. + rcBuff[MAX_SYM_PATH-1] = W('\0'); + + // Now we have a working buffer with a bunch of interesting stuff. Time + // to convert it back to ansi for the imagehlp api's. Allocate the buffer + // 2x bigger to handle worst case for MBCS. + ch = ::WszWideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, rcBuff, -1, 0, 0, 0, 0); + LPSTR szRtn = (LPSTR) qb.AllocNoThrow(ch + 1); + if (!szRtn) + return NULL; + WszWideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, rcBuff, -1, szRtn, ch+1, 0, 0); + return (szRtn); +} + + +/**************************************************************************** +* MagicInit * +*-----------* +* Description: +* Initializes the symbol loading code. Currently called (if necessary) +* at the beginning of each method that might need ImageHelp to be +* loaded. +****************************************************************************/ +void MagicInit() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_CANNOT_TAKE_LOCK; + + if (g_fLoadedImageHlp || g_fLoadedImageHlpFailed) + { + return; + } + + g_hProcess = GetCurrentProcess(); + + if (g_hinstDbgHelp == NULL) + { + g_hinstDbgHelp = LoadDbgHelp(); + } + if (NULL == g_hinstDbgHelp) + { + // Imagehlp.dll has dependency on dbghelp.dll through delay load. + // If dbghelp.dll is not available, Imagehlp.dll initializes API's like ImageApiVersionEx to + // some dummy function. Then we AV when we use data from _ImagehlpApiVersionEx + g_fLoadedImageHlpFailed = TRUE; + return; + } + + // + // Try to load imagehlp.dll + // + if (g_hinstImageHlp == NULL) { + g_hinstImageHlp = LoadImageHlp(); + } + LOCAL_ASSERT(g_hinstImageHlp); + + if (NULL == g_hinstImageHlp) + { + g_fLoadedImageHlpFailed = TRUE; + return; + } + + // + // Try to get the API entrypoints in imagehlp.dll + // + for (int i = 0; i < COUNT_OF(ailFuncList); i++) + { + *(ailFuncList[i].ppvfn) = GetProcAddress( + g_hinstImageHlp, + ailFuncList[i].pszFnName); + LOCAL_ASSERT(*(ailFuncList[i].ppvfn)); + + if (!*(ailFuncList[i].ppvfn)) + { + g_fLoadedImageHlpFailed = TRUE; + return; + } + } + + API_VERSION AppVersion = { 4, 0, API_VERSION_NUMBER, 0 }; + LPAPI_VERSION papiver = _ImagehlpApiVersionEx(&AppVersion); + + // + // We assume any version 4 or greater is OK. + // + LOCAL_ASSERT(papiver->Revision >= 4); + if (papiver->Revision < 4) + { + g_fLoadedImageHlpFailed = TRUE; + return; + } + + g_fLoadedImageHlp = TRUE; + + // + // Initialize imagehlp.dll. A NULL search path is supposed to resolve + // symbols but never works. So pull in everything and put some additional + // hints that might help out a dev box. + // + + _SymSetOptions(_SymGetOptions() | SYMOPT_DEFERRED_LOADS|SYMOPT_DEBUG); +#ifndef _WIN64 + _SymRegisterCallback(g_hProcess, SymCallback, 0); +#endif + + CQuickBytes qbSearchPath; + LPSTR szSearchPath = FillSymbolSearchPath(qbSearchPath); + _SymInitialize(g_hProcess, szSearchPath, TRUE); + + return; +} + + +/**************************************************************************** +* FillSymbolInfo * +*----------------* +* Description: +* Fills in a SYM_INFO structure +****************************************************************************/ +void FillSymbolInfo +( +SYM_INFO *psi, +DWORD_PTR dwAddr +) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_CANNOT_TAKE_LOCK; + + if (!g_fLoadedImageHlp) + { + return; + } + + LOCAL_ASSERT(psi); + memset(psi, 0, sizeof(SYM_INFO)); + + PLAT_IMAGEHLP_MODULE mi; + mi.SizeOfStruct = sizeof(mi); + + if (!_SymGetModuleInfo(g_hProcess, dwAddr, &mi)) + { + strcpy_s(psi->achModule, _countof(psi->achModule), "<no module>"); + } + else + { + strcpy_s(psi->achModule, _countof(psi->achModule), mi.ModuleName); + _strupr_s(psi->achModule, _countof(psi->achModule)); + } + + CHAR rgchUndec[256]; + CHAR * pszSymbol = NULL; + + // Name field of IMAGEHLP_SYMBOL is dynamically sized. + // Pad with space for 255 characters. + union + { + CHAR rgchSymbol[sizeof(PLAT_IMAGEHLP_SYMBOL) + 255]; + PLAT_IMAGEHLP_SYMBOL sym; + }; + + __try + { + sym.SizeOfStruct = sizeof(PLAT_IMAGEHLP_SYMBOL); + sym.Address = dwAddr; + sym.MaxNameLength = 255; + + if (_SymGetSymFromAddr(g_hProcess, dwAddr, &psi->dwOffset, &sym)) + { + pszSymbol = sym.Name; + + if (_SymUnDName(&sym, rgchUndec, COUNT_OF(rgchUndec)-1)) + { + pszSymbol = rgchUndec; + } + } + else + { + pszSymbol = "<no symbol>"; + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + pszSymbol = "<EX: no symbol>"; + psi->dwOffset = dwAddr - mi.BaseOfImage; + } + + strcpy_s(psi->achSymbol, _countof(psi->achSymbol), pszSymbol); +} + +/**************************************************************************** +* FunctionTableAccess * +*---------------------* +* Description: +* Helper for imagehlp's StackWalk API. +****************************************************************************/ +LPVOID __stdcall FunctionTableAccess +( +HANDLE hProcess, +DWORD_PTR dwPCAddr +) +{ + WRAPPER_NO_CONTRACT; + + HANDLE hFuncEntry = _SymFunctionTableAccess( hProcess, dwPCAddr ); + +#if defined(_WIN64) + if (hFuncEntry == NULL) + { + if (_GetRuntimeStackWalkInfo == NULL) + { + _GetRuntimeStackWalkInfo = (pfn_GetRuntimeStackWalkInfo) + GetProcAddress(GetCLRModuleHack(), "GetRuntimeStackWalkInfo"); + if (_GetRuntimeStackWalkInfo == NULL) + return NULL; + } + + _GetRuntimeStackWalkInfo((ULONG64)dwPCAddr, NULL, (UINT_PTR*)(&hFuncEntry)); + } +#endif // _WIN64 + + return hFuncEntry; +} + +/**************************************************************************** +* GetModuleBase * +*---------------* +* Description: +* Helper for imagehlp's StackWalk API. Retrieves the base address of +* the module containing the giving virtual address. +* +* NOTE: If the module information for the given module hasnot yet been +* loaded, then it is loaded on this call. +* +* Return: +* Base virtual address where the module containing ReturnAddress is +* loaded, or 0 if the address cannot be determined. +****************************************************************************/ +DWORD_PTR __stdcall GetModuleBase +( +HANDLE hProcess, +DWORD_PTR dwAddr +) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + PLAT_IMAGEHLP_MODULE ModuleInfo; + ModuleInfo.SizeOfStruct = sizeof(ModuleInfo); + + if (_SymGetModuleInfo(hProcess, dwAddr, &ModuleInfo)) + { + return ModuleInfo.BaseOfImage; + } + else + { + MEMORY_BASIC_INFORMATION mbi; + + if (VirtualQueryEx(hProcess, (LPVOID)dwAddr, &mbi, sizeof(mbi))) + { + if (mbi.Type & MEM_IMAGE) + { + char achFile[MAX_PATH] = {0}; + DWORD cch; + + cch = GetModuleFileNameA( + (HINSTANCE)mbi.AllocationBase, + achFile, + MAX_PATH); + + // Ignore the return code since we can't do anything with it. + _SymLoadModule( + hProcess, + NULL, + ((cch) ? achFile : NULL), + NULL, + (DWORD_PTR)mbi.AllocationBase, + 0); + + return (DWORD_PTR)mbi.AllocationBase; + } + } + } + +#if defined(_WIN64) + if (_GetRuntimeStackWalkInfo == NULL) + { + _GetRuntimeStackWalkInfo = (pfn_GetRuntimeStackWalkInfo) + GetProcAddress(GetCLRModuleHack(), "GetRuntimeStackWalkInfo"); + if (_GetRuntimeStackWalkInfo == NULL) + return NULL; + } + + DWORD_PTR moduleBase; + _GetRuntimeStackWalkInfo((ULONG64)dwAddr, (UINT_PTR*)&moduleBase, NULL); + if (moduleBase != NULL) + return moduleBase; +#endif // _WIN64 + + return 0; +} + +#if !defined(DACCESS_COMPILE) +/**************************************************************************** +* GetStackBacktrace * +*-------------------* +* Description: +* Gets a stacktrace of the current stack, including symbols. +* +* Return: +* The number of elements actually retrieved. +****************************************************************************/ + +UINT GetStackBacktrace +( +UINT ifrStart, // How many stack elements to skip before starting. +UINT cfrTotal, // How many elements to trace after starting. +DWORD_PTR* pdwEip, // Array to be filled with stack addresses. +SYM_INFO* psiSymbols, // This array is filled with symbol information. + // It should be big enough to hold cfrTotal elts. + // If NULL, no symbol information is stored. +CONTEXT * pContext // Context to use (or NULL to use current) +) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_CANNOT_TAKE_LOCK; + + UINT nElements = 0; + DWORD_PTR* pdw = pdwEip; + SYM_INFO* psi = psiSymbols; + + MagicInit(); + + memset(pdwEip, 0, cfrTotal*sizeof(DWORD_PTR)); + + if (psiSymbols) + { + memset(psiSymbols, 0, cfrTotal * sizeof(SYM_INFO)); + } + + if (!g_fLoadedImageHlp) + { + return 0; + } + + CONTEXT context; + if (pContext == NULL) + { + ClrCaptureContext(&context); + } + else + { + memcpy(&context, pContext, sizeof(CONTEXT)); + } + +#ifdef _WIN64 + STACKFRAME64 stkfrm; + memset(&stkfrm, 0, sizeof(STACKFRAME64)); +#else + STACKFRAME stkfrm; + memset(&stkfrm, 0, sizeof(STACKFRAME)); +#endif + + stkfrm.AddrPC.Mode = AddrModeFlat; + stkfrm.AddrStack.Mode = AddrModeFlat; + stkfrm.AddrFrame.Mode = AddrModeFlat; +#if defined(_M_IX86) + stkfrm.AddrPC.Offset = context.Eip; + stkfrm.AddrStack.Offset = context.Esp; + stkfrm.AddrFrame.Offset = context.Ebp; // Frame Pointer +#endif + +#ifndef _TARGET_X86_ + // If we don't have a user-supplied context, then don't skip any frames. + // So ignore this function (GetStackBackTrace) + // ClrCaptureContext on x86 gives us the ESP/EBP/EIP of its caller's caller + // so we don't need to do this. + if (pContext == NULL) + { + ifrStart += 1; + } +#endif // !_TARGET_X86_ + + for (UINT i = 0; i < ifrStart + cfrTotal; i++) + { + if (!_StackWalk(IMAGE_FILE_MACHINE_NATIVE, + g_hProcess, + GetCurrentThread(), + &stkfrm, + &context, + NULL, + (PFUNCTION_TABLE_ACCESS_ROUTINE)FunctionTableAccess, + (PGET_MODULE_BASE_ROUTINE)GetModuleBase, + NULL)) + { + break; + } + + if (i >= ifrStart) + { + *pdw++ = stkfrm.AddrPC.Offset; + nElements++; + + if (psi) + { + FillSymbolInfo(psi++, stkfrm.AddrPC.Offset); + } + } + } + + LOCAL_ASSERT(nElements == (UINT)(pdw - pdwEip)); + return nElements; +} +#endif // !defined(DACCESS_COMPILE) + +/**************************************************************************** +* GetStringFromSymbolInfo * +*-------------------------* +* Description: +* Actually prints the info into the string for the symbol. +****************************************************************************/ + +#ifdef _WIN64 + #define FMT_ADDR_BARE "%08x`%08x" +#else + #define FMT_ADDR_BARE "%08x" +#endif + +void GetStringFromSymbolInfo +( +DWORD_PTR dwAddr, +SYM_INFO *psi, // @parm Pointer to SYMBOL_INFO. Can be NULL. +__out_ecount (cchMaxAssertStackLevelStringLen) CHAR *pszString // @parm Place to put string. +) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_CANNOT_TAKE_LOCK; + + LOCAL_ASSERT(pszString); + + // <module>! <symbol> + 0x<offset> 0x<addr>\n + + if (psi) + { + sprintf_s(pszString, + cchMaxAssertStackLevelStringLen, + "%s! %s + 0x%X (0x" FMT_ADDR_BARE ")", + (psi->achModule[0]) ? psi->achModule : "<no module>", + (psi->achSymbol[0]) ? psi->achSymbol : "<no symbol>", + psi->dwOffset, + DBG_ADDR(dwAddr)); + } + else + { + + sprintf_s(pszString, cchMaxAssertStackLevelStringLen, "<symbols not available> (0x%p)", (void *)dwAddr); + } + + LOCAL_ASSERT(strlen(pszString) < cchMaxAssertStackLevelStringLen); +} + +#if !defined(DACCESS_COMPILE) + +/**************************************************************************** +* GetStringFromStackLevels * +*--------------------------* +* Description: +* Retrieves a string from the stack frame. If more than one frame, they +* are separated by newlines +****************************************************************************/ +void GetStringFromStackLevels +( +UINT ifrStart, // @parm How many stack elements to skip before starting. +UINT cfrTotal, // @parm How many elements to trace after starting. + // Can't be more than cfrMaxAssertStackLevels. +__out_ecount(cchMaxAssertStackLevelStringLen * cfrTotal) CHAR *pszString, // @parm Place to put string. + // Max size will be cchMaxAssertStackLevelStringLen * cfrTotal. +CONTEXT * pContext // @parm Context to start the stack trace at; null for current context. +) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_CANNOT_TAKE_LOCK; + + LOCAL_ASSERT(pszString); + LOCAL_ASSERT(cfrTotal < cfrMaxAssertStackLevels); + + *pszString = '\0'; + + if (cfrTotal == 0) + { + return; + } + + DWORD_PTR rgdwStackAddrs[cfrMaxAssertStackLevels]; + SYM_INFO rgsi[cfrMaxAssertStackLevels]; + + // Ignore this function (GetStringFromStackLevels) if we don't have a user-supplied context. + if (pContext == NULL) + { + ifrStart += 1; + } + + UINT uiRetrieved = + GetStackBacktrace(ifrStart, cfrTotal, rgdwStackAddrs, rgsi, pContext); + + // First level + CHAR aszLevel[cchMaxAssertStackLevelStringLen]; + GetStringFromSymbolInfo(rgdwStackAddrs[0], &rgsi[0], aszLevel); + + size_t bufSize = cchMaxAssertStackLevelStringLen * cfrTotal; + + strcpy_s(pszString, bufSize, aszLevel); + + // Additional levels + for (UINT i = 1; i < uiRetrieved; ++i) + { + strcat_s(pszString, bufSize, "\n"); + GetStringFromSymbolInfo(rgdwStackAddrs[i], + &rgsi[i], aszLevel); + strcat_s(pszString, bufSize, aszLevel); + } + + LOCAL_ASSERT(strlen(pszString) <= cchMaxAssertStackLevelStringLen * cfrTotal); +} +#endif // !defined(DACCESS_COMPILE) + +/**************************************************************************** +* GetStringFromAddr * +*-------------------* +* Description: +* Returns a string from an address. +****************************************************************************/ +void GetStringFromAddr +( +DWORD_PTR dwAddr, +__out_ecount(cchMaxAssertStackLevelStringLen) LPSTR szString // Place to put string. + // Buffer must hold at least cchMaxAssertStackLevelStringLen. +) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + LOCAL_ASSERT(szString); + + SYM_INFO si; + FillSymbolInfo(&si, dwAddr); + + sprintf_s(szString, + cchMaxAssertStackLevelStringLen, + "%s! %s + 0x%p (0x%p)", + (si.achModule[0]) ? si.achModule : "<no module>", + (si.achSymbol[0]) ? si.achSymbol : "<no symbol>", + (void*)si.dwOffset, + (void*)dwAddr); +} + +/**************************************************************************** +* MagicDeinit * +*-------------* +* Description: +* Cleans up for the symbol loading code. Should be called before exit +* to free the dynamically loaded imagehlp.dll. +****************************************************************************/ +void MagicDeinit(void) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + if (g_hinstImageHlp) + { + FreeLibrary(g_hinstImageHlp); + + g_hinstImageHlp = NULL; + g_fLoadedImageHlp = FALSE; + } +} + +#if defined(_TARGET_X86_) && defined(FEATURE_CORECLR) +/**************************************************************************** +* ClrCaptureContext * +*-------------------* +* Description: +* Exactly the contents of RtlCaptureContext for Win7 - Win2K doesn't +* support this, so we need it for CoreCLR 4, if we require Win2K support +****************************************************************************/ +extern "C" __declspec(naked) void __stdcall +ClrCaptureContext(__out PCONTEXT ctx) +{ + __asm { + push ebx; + mov ebx,dword ptr [esp+8] + mov dword ptr [ebx+0B0h],eax + mov dword ptr [ebx+0ACh],ecx + mov dword ptr [ebx+0A8h],edx + mov eax,dword ptr [esp] + mov dword ptr [ebx+0A4h],eax + mov dword ptr [ebx+0A0h],esi + mov dword ptr [ebx+09Ch],edi + mov word ptr [ebx+0BCh],cs + mov word ptr [ebx+098h],ds + mov word ptr [ebx+094h],es + mov word ptr [ebx+090h],fs + mov word ptr [ebx+08Ch],gs + mov word ptr [ebx+0C8h],ss + pushfd + pop dword ptr [ebx+0C0h] + mov eax,dword ptr [ebp+4] + mov dword ptr [ebx+0B8h],eax + mov eax,dword ptr [ebp] + mov dword ptr [ebx+0B4h],eax + lea eax,[ebp+8] + mov dword ptr [ebx+0C4h],eax + mov dword ptr [ebx],10007h + pop ebx + ret 4 + } +} +#endif // _TARGET_X86_ && FEATURE_CORECLR diff --git a/src/utilcode/staticnohost/.gitmirror b/src/utilcode/staticnohost/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/utilcode/staticnohost/.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/utilcode/staticnohost/CMakeLists.txt b/src/utilcode/staticnohost/CMakeLists.txt new file mode 100644 index 0000000000..7dc8b9a3af --- /dev/null +++ b/src/utilcode/staticnohost/CMakeLists.txt @@ -0,0 +1,6 @@ +add_definitions(-DSELF_NO_HOST) +add_definitions(-D_CRTIMP=) # use static version of crt + +set(UTILCODE_SOURCES ${UTILCODE_SOURCES} ../hostimpl.cpp) + +add_library(utilcodestaticnohost STATIC ${UTILCODE_SOURCES}) diff --git a/src/utilcode/staticnohost/staticnohost.nativeproj b/src/utilcode/staticnohost/staticnohost.nativeproj new file mode 100644 index 0000000000..4ef47670f3 --- /dev/null +++ b/src/utilcode/staticnohost/staticnohost.nativeproj @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + <PropertyGroup Label="Globals"> + <SccProjectName>SAK</SccProjectName> + <SccAuxPath>SAK</SccAuxPath> + <SccLocalPath>SAK</SccLocalPath> + <SccProvider>SAK</SccProvider> + </PropertyGroup> + <PropertyGroup> + <BuildCoreBinaries>true</BuildCoreBinaries> + <BuildSysBinaries>true</BuildSysBinaries> + <OutputName>utilcodestaticnohost</OutputName> + </PropertyGroup> + <Import Project="staticnohost.targets" /> +</Project> diff --git a/src/utilcode/staticnohost/staticnohost.targets b/src/utilcode/staticnohost/staticnohost.targets new file mode 100644 index 0000000000..163090adeb --- /dev/null +++ b/src/utilcode/staticnohost/staticnohost.targets @@ -0,0 +1,15 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\Utilcode\utilcode.settings.targets" /> + + <PropertyGroup> + <ClAdditionalOptions>$(ClAdditionalOptions) -DSELF_NO_HOST -D_CRTIMP=</ClAdditionalOptions> + </PropertyGroup> + + <ItemGroup> + <CppCompile Include="..\hostimpl.cpp" /> + </ItemGroup> + + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" /> + +</Project> diff --git a/src/utilcode/stdafx.cpp b/src/utilcode/stdafx.cpp new file mode 100644 index 0000000000..d82c602102 --- /dev/null +++ b/src/utilcode/stdafx.cpp @@ -0,0 +1,13 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// stdafx.cpp +// + +// +// Host for precompiled header. +// +//***************************************************************************** +#include "stdafx.h" // Precompiled header key. diff --git a/src/utilcode/stdafx.h b/src/utilcode/stdafx.h new file mode 100644 index 0000000000..c572c3b302 --- /dev/null +++ b/src/utilcode/stdafx.h @@ -0,0 +1,23 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// stdafx.h +// + +// +// Common include file for utility code. +//***************************************************************************** +#pragma once + +#include <switches.h> +#include <crtwrap.h> + +#define IN_WINFIX_CPP + +#include <winwrap.h> + +#include "volatile.h" +#include "static_assert.h" + diff --git a/src/utilcode/stgpool.cpp b/src/utilcode/stgpool.cpp new file mode 100644 index 0000000000..a4dda6ba82 --- /dev/null +++ b/src/utilcode/stgpool.cpp @@ -0,0 +1,2451 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// StgPool.cpp +// + +// +// Pools are used to reduce the amount of data actually required in the database. +// This allows for duplicate string and binary values to be folded into one +// copy shared by the rest of the database. Strings are tracked in a hash +// table when insert/changing data to find duplicates quickly. The strings +// are then persisted consecutively in a stream in the database format. +// +//***************************************************************************** +#include "stdafx.h" // Standard include. +#include <stgpool.h> // Our interface definitions. +#include <posterror.h> // Error handling. +#include <safemath.h> // CLRSafeInt integer overflow checking +#include "../md/inc/streamutil.h" + +#include "ex.h" + +#ifdef FEATURE_PREJIT +#include <corcompile.h> +#endif + +using namespace StreamUtil; + +#define MAX_CHAIN_LENGTH 20 // Max chain length before rehashing. + +// +// +// StgPool +// +// + + +//***************************************************************************** +// Free any memory we allocated. +//***************************************************************************** +StgPool::~StgPool() +{ + WRAPPER_NO_CONTRACT; + + Uninit(); +} // StgPool::~StgPool() + + +//***************************************************************************** +// Init the pool for use. This is called for both the create empty case. +//***************************************************************************** +__checkReturn +HRESULT +StgPool::InitNew( + ULONG cbSize, // Estimated size. + ULONG cItems) // Estimated item count. +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY); + } + CONTRACTL_END + + // Make sure we aren't stomping anything and are properly initialized. + _ASSERTE(m_pSegData == m_zeros); + _ASSERTE(m_pNextSeg == 0); + _ASSERTE(m_pCurSeg == this); + _ASSERTE(m_cbCurSegOffset == 0); + _ASSERTE(m_cbSegSize == 0); + _ASSERTE(m_cbSegNext == 0); + + m_bReadOnly = false; + m_bFree = false; + + return S_OK; +} // StgPool::InitNew + +//***************************************************************************** +// Init the pool from existing data. +//***************************************************************************** +__checkReturn +HRESULT +StgPool::InitOnMem( + void *pData, // Predefined data. + ULONG iSize, // Size of data. + int bReadOnly) // true if append is forbidden. +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + // Make sure we aren't stomping anything and are properly initialized. + _ASSERTE(m_pSegData == m_zeros); + _ASSERTE(m_pNextSeg == 0); + _ASSERTE(m_pCurSeg == this); + _ASSERTE(m_cbCurSegOffset == 0); + + // Create case requires no further action. + if (!pData) + return (E_INVALIDARG); + + // Might we be extending this heap? + m_bReadOnly = bReadOnly; + + + m_pSegData = reinterpret_cast<BYTE*>(pData); + m_cbSegSize = iSize; + m_cbSegNext = iSize; + + m_bFree = false; + + return (S_OK); +} // StgPool::InitOnMem + +//***************************************************************************** +// Called when the pool must stop accessing memory passed to InitOnMem(). +//***************************************************************************** +__checkReturn +HRESULT +StgPool::TakeOwnershipOfInitMem() +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + // If the pool doesn't have a pointer to non-owned memory, done. + if (m_bFree) + return (S_OK); + + // If the pool doesn't have a pointer to memory at all, done. + if (m_pSegData == m_zeros) + { + _ASSERTE(m_cbSegSize == 0); + return (S_OK); + } + + // Get some memory to keep. + BYTE *pData = new (nothrow) BYTE[m_cbSegSize+4]; + if (pData == 0) + return (PostError(OutOfMemory())); + + // Copy the old data to the new memory. + memcpy(pData, m_pSegData, m_cbSegSize); + m_pSegData = pData; + m_bFree = true; + + return (S_OK); +} // StgPool::TakeOwnershipOfInitMem + +//***************************************************************************** +// Clear out this pool. Cannot use until you call InitNew. +//***************************************************************************** +void StgPool::Uninit() +{ + CONTRACTL + { + NOTHROW; + FORBID_FAULT; + } + CONTRACTL_END + + // Free base segment, if appropriate. + if (m_bFree && (m_pSegData != m_zeros)) + { + delete [] m_pSegData; + m_bFree = false; + } + + // Free chain, if any. + StgPoolSeg *pSeg = m_pNextSeg; + while (pSeg) + { + StgPoolSeg *pNext = pSeg->m_pNextSeg; + delete [] (BYTE*)pSeg; + pSeg = pNext; + } + + // Clear vars. + m_pSegData = (BYTE*)m_zeros; + m_cbSegSize = m_cbSegNext = 0; + m_pNextSeg = 0; + m_pCurSeg = this; + m_cbCurSegOffset = 0; +} // StgPool::Uninit + +//***************************************************************************** +// Called to copy the pool to writable memory, reset the r/o bit. +//***************************************************************************** +__checkReturn +HRESULT +StgPool::ConvertToRW() +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + HRESULT hr; // A result. + IfFailRet(TakeOwnershipOfInitMem()); + + IfFailRet(SetHash(true)); + + m_bReadOnly = false; + + return S_OK; +} // StgPool::ConvertToRW + +//***************************************************************************** +// Turn hashing off or on. Real implementation as required in subclass. +//***************************************************************************** +__checkReturn +HRESULT +StgPool::SetHash(int bHash) +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + return S_OK; +} // StgPool::SetHash + +//***************************************************************************** +// Trim any empty final segment. +//***************************************************************************** +void StgPool::Trim() +{ + CONTRACTL + { + NOTHROW; + FORBID_FAULT; + } + CONTRACTL_END + + // If no chained segments, nothing to do. + if (m_pNextSeg == 0) + return; + + // Handle special case for a segment that was completely unused. + if (m_pCurSeg->m_cbSegNext == 0) + { + // Find the segment which points to the empty segment. + StgPoolSeg *pPrev; + for (pPrev = this; pPrev && pPrev->m_pNextSeg != m_pCurSeg; pPrev = pPrev->m_pNextSeg); + _ASSERTE(pPrev && pPrev->m_pNextSeg == m_pCurSeg); + + // Free the empty segment. + delete [] (BYTE*) m_pCurSeg; + + // Fix the pCurSeg pointer. + pPrev->m_pNextSeg = 0; + m_pCurSeg = pPrev; + + // Adjust the base offset, because the PREVIOUS seg is now current. + _ASSERTE(m_pCurSeg->m_cbSegNext <= m_cbCurSegOffset); + m_cbCurSegOffset = m_cbCurSegOffset - m_pCurSeg->m_cbSegNext; + } +} // StgPool::Trim + +//***************************************************************************** +// Allocate memory if we don't have any, or grow what we have. If successful, +// then at least iRequired bytes will be allocated. +//***************************************************************************** +bool StgPool::Grow( // true if successful. + ULONG iRequired) // Min required bytes to allocate. +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return FALSE;); + } + CONTRACTL_END + + ULONG iNewSize; // New size we want. + StgPoolSeg *pNew; // Temp pointer for malloc. + + _ASSERTE(!m_bReadOnly); + + // Would this put the pool over 2GB? + if ((m_cbCurSegOffset + iRequired) > INT_MAX) + return (false); + + // Adjust grow size as a ratio to avoid too many reallocs. + if ((m_pCurSeg->m_cbSegNext + m_cbCurSegOffset) / m_ulGrowInc >= 3) + m_ulGrowInc *= 2; + + // NOTE: MD\DataSource\RemoteMDInternalRWSource has taken a dependency that there + // won't be more than 1000 segments. Given the current exponential growth algorithm + // we'll never get anywhere close to that, but if the algorithm changes to allow for + // many segments, please update that source as well. + + // If first time, handle specially. + if (m_pSegData == m_zeros) + { + // Allocate the buffer. + iNewSize = max(m_ulGrowInc, iRequired); + BYTE *pSegData = new (nothrow) BYTE[iNewSize + 4]; + if (pSegData == NULL) + return false; + m_pSegData = pSegData; + + // Will need to delete it. + m_bFree = true; + + // How big is this initial segment? + m_cbSegSize = iNewSize; + + // Do some validation of var fields. + _ASSERTE(m_cbSegNext == 0); + _ASSERTE(m_pCurSeg == this); + _ASSERTE(m_pNextSeg == NULL); + + return true; + } + + // Allocate the new space enough for header + data. + iNewSize = (ULONG)(max(m_ulGrowInc, iRequired) + sizeof(StgPoolSeg)); + pNew = (StgPoolSeg *)new (nothrow) BYTE[iNewSize+4]; + if (pNew == NULL) + return false; + + // Set the fields in the new segment. + pNew->m_pSegData = reinterpret_cast<BYTE*>(pNew) + sizeof(StgPoolSeg); + _ASSERTE(ALIGN4BYTE(reinterpret_cast<ULONG_PTR>(pNew->m_pSegData)) == reinterpret_cast<ULONG_PTR>(pNew->m_pSegData)); + pNew->m_pNextSeg = 0; + pNew->m_cbSegSize = iNewSize - sizeof(StgPoolSeg); + pNew->m_cbSegNext = 0; + + // Calculate the base offset of the new segment. + m_cbCurSegOffset = m_cbCurSegOffset + m_pCurSeg->m_cbSegNext; + + // Handle special case for a segment that was completely unused. + //<TODO>@todo: Trim();</TODO> + if (m_pCurSeg->m_cbSegNext == 0) + { + // Find the segment which points to the empty segment. + StgPoolSeg *pPrev; + for (pPrev = this; pPrev && pPrev->m_pNextSeg != m_pCurSeg; pPrev = pPrev->m_pNextSeg); + _ASSERTE(pPrev && pPrev->m_pNextSeg == m_pCurSeg); + + // Free the empty segment. + delete [] (BYTE *) m_pCurSeg; + + // Link in the new segment. + pPrev->m_pNextSeg = pNew; + m_pCurSeg = pNew; + + return true; + } + +#ifndef NO_CRT + // Give back any memory that we won't use. + if (m_pNextSeg == 0) + { // First segment allocated as [header]->[data]. + // Be sure that we are contracting the allocation. + if (m_pCurSeg->m_cbSegNext < (_msize(m_pCurSeg->m_pSegData) - 4)) + { + // Contract the allocation. + void *pRealloc = _expand(m_pCurSeg->m_pSegData, m_pCurSeg->m_cbSegNext+4); + // Shouldn't have moved. + _ASSERTE(pRealloc == m_pCurSeg->m_pSegData); + } + } + else + { // Chained segments are allocated together, [header][data]. + // Be sure that we are contracting the allocation. + if (m_pCurSeg->m_cbSegNext+sizeof(StgPoolSeg) < (_msize(m_pCurSeg) - 4)) + { + // Contract the allocation. + void *pRealloc = _expand(m_pCurSeg, m_pCurSeg->m_cbSegNext + sizeof(StgPoolSeg) + 4); + // Shouldn't have moved. + _ASSERTE(pRealloc == m_pCurSeg); + } + } +#endif + + // Fix the size of the old segment. + m_pCurSeg->m_cbSegSize = m_pCurSeg->m_cbSegNext; + + // Link the new segment into the chain. + m_pCurSeg->m_pNextSeg = pNew; + m_pCurSeg = pNew; + + return true; +} // StgPool::Grow + +//***************************************************************************** +// Add a segment to the chain of segments. +//***************************************************************************** +__checkReturn +HRESULT +StgPool::AddSegment( + const void *pData, // The data. + ULONG cbData, // Size of the data. + bool bCopy) // If true, make a copy of the data. +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + StgPoolSeg *pNew; // Temp pointer for malloc. + + + // If we need to copy the data, just grow the heap by enough to take the + // the new data, and copy it in. + if (bCopy) + { + void *pDataToAdd = new (nothrow) BYTE[cbData]; + IfNullRet(pDataToAdd); + memcpy(pDataToAdd, pData, cbData); + pData = pDataToAdd; + } + + // If first time, handle specially. + if (m_pSegData == m_zeros) + { // Data was passed in. + m_pSegData = reinterpret_cast<BYTE*>(const_cast<void*>(pData)); + m_cbSegSize = cbData; + m_cbSegNext = cbData; + _ASSERTE(m_pNextSeg == NULL); + + // Will not delete it. + m_bFree = false; + + return S_OK; + } + + // Not first time. Handle a completely empty tail segment. + Trim(); + + // Abandon any space past the end of the current live data. + _ASSERTE(m_pCurSeg->m_cbSegSize >= m_pCurSeg->m_cbSegNext); + m_pCurSeg->m_cbSegSize = m_pCurSeg->m_cbSegNext; + + // Allocate a new segment header. + pNew = (StgPoolSeg *) new (nothrow) BYTE[sizeof(StgPoolSeg)]; + IfNullRet(pNew); + + // Set the fields in the new segment. + pNew->m_pSegData = reinterpret_cast<BYTE*>(const_cast<void*>(pData)); + pNew->m_pNextSeg = NULL; + pNew->m_cbSegSize = cbData; + pNew->m_cbSegNext = cbData; + + // Calculate the base offset of the new segment. + m_cbCurSegOffset = m_cbCurSegOffset + m_pCurSeg->m_cbSegNext; + + // Link the segment into the chain. + _ASSERTE(m_pCurSeg->m_pNextSeg == NULL); + m_pCurSeg->m_pNextSeg = pNew; + m_pCurSeg = pNew; + + return S_OK; +} // StgPool::AddSegment + +#ifndef DACCESS_COMPILE +//***************************************************************************** +// The entire string pool is written to the given stream. The stream is aligned +// to a 4 byte boundary. +//***************************************************************************** +__checkReturn +HRESULT +StgPool::PersistToStream( + IStream *pIStream) // The stream to write to. +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY); + } + CONTRACTL_END + + HRESULT hr = S_OK; + ULONG cbTotal; // Total bytes written. + StgPoolSeg *pSeg; // A segment being written. + + _ASSERTE(m_pSegData != m_zeros); + + // Start with the base segment. + pSeg = this; + cbTotal = 0; + + EX_TRY + { + // As long as there is data, write it. + while (pSeg != NULL) + { + // If there is data in the segment . . . + if (pSeg->m_cbSegNext) + { // . . . write and count the data. + if (FAILED(hr = pIStream->Write(pSeg->m_pSegData, pSeg->m_cbSegNext, 0))) + break; + cbTotal += pSeg->m_cbSegNext; + } + + // Get the next segment. + pSeg = pSeg->m_pNextSeg; + } + + if (SUCCEEDED(hr)) + { + // Align to variable (0-4 byte) boundary. + UINT32 cbTotalAligned; + if (FAILED(Align(cbTotal, &cbTotalAligned))) + { + hr = COR_E_BADIMAGEFORMAT; + } + else + { + if (cbTotalAligned > cbTotal) + { + _ASSERTE(sizeof(hr) >= 3); + hr = 0; + hr = pIStream->Write(&hr, cbTotalAligned - cbTotal, 0); + } + } + } + } + EX_CATCH + { + hr = E_FAIL; + } + EX_END_CATCH(SwallowAllExceptions); + + return hr; +} // StgPool::PersistToStream +#endif //!DACCESS_COMPILE + +//***************************************************************************** +// The entire string pool is written to the given stream. The stream is aligned +// to a 4 byte boundary. +//***************************************************************************** +__checkReturn +HRESULT +StgPool::PersistPartialToStream( + IStream *pIStream, // The stream to write to. + ULONG iOffset) // Starting offset. +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY); + } + CONTRACTL_END + + HRESULT hr = S_OK; // A result. + ULONG cbTotal; // Total bytes written. + StgPoolSeg *pSeg; // A segment being written. + + _ASSERTE(m_pSegData != m_zeros); + + // Start with the base segment. + pSeg = this; + cbTotal = 0; + + // As long as there is data, write it. + while (pSeg != NULL) + { + // If there is data in the segment . . . + if (pSeg->m_cbSegNext) + { // If this data should be skipped... + if (iOffset >= pSeg->m_cbSegNext) + { // Skip it + iOffset -= pSeg->m_cbSegNext; + } + else + { // At least some data should be written, so write and count the data. + IfFailRet(pIStream->Write(pSeg->m_pSegData+iOffset, pSeg->m_cbSegNext-iOffset, 0)); + cbTotal += pSeg->m_cbSegNext-iOffset; + iOffset = 0; + } + } + + // Get the next segment. + pSeg = pSeg->m_pNextSeg; + } + + // Align to variable (0-4 byte) boundary. + UINT32 cbTotalAligned; + if (FAILED(Align(cbTotal, &cbTotalAligned))) + { + return COR_E_BADIMAGEFORMAT; + } + if (cbTotalAligned > cbTotal) + { + _ASSERTE(sizeof(hr) >= 3); + hr = 0; + hr = pIStream->Write(&hr, cbTotalAligned - cbTotal, 0); + } + + return hr; +} // StgPool::PersistPartialToStream + +// Copies data from pSourcePool starting at index nStartSourceIndex. +__checkReturn +HRESULT +StgPool::CopyPool( + UINT32 nStartSourceIndex, + const StgPool *pSourcePool) +{ + HRESULT hr; + UINT32 cbDataSize; + BYTE *pbData = NULL; + + if (nStartSourceIndex == pSourcePool->GetRawSize()) + { // There's nothing to copy + return S_OK; + } + if (nStartSourceIndex > pSourcePool->GetRawSize()) + { // Invalid input + Debug_ReportInternalError("The caller should not pass invalid start index in the pool."); + IfFailGo(METADATA_E_INDEX_NOTFOUND); + } + + // Allocate new segment + cbDataSize = pSourcePool->GetRawSize() - nStartSourceIndex; + pbData = new (nothrow) BYTE[cbDataSize]; + IfNullGo(pbData); + + // Copy data to the new segment + UINT32 cbCopiedDataSize; + IfFailGo(pSourcePool->CopyData( + nStartSourceIndex, + pbData, + cbDataSize, + &cbCopiedDataSize)); + // Check that we copied everything + if (cbDataSize != cbCopiedDataSize) + { + Debug_ReportInternalError("It is expected to copy everything from the source pool."); + IfFailGo(E_FAIL); + } + + // Add the newly allocated segment to the pool + IfFailGo(AddSegment( + pbData, + cbDataSize, + false)); // fCopyData + +ErrExit: + if (FAILED(hr)) + { + if (pbData != NULL) + { + delete [] pbData; + } + } + return hr; +} // StgPool::CopyPool + +// Copies data from the pool into a buffer. It will correctly walk all segments for the copy. +__checkReturn +HRESULT +StgPool::CopyData( + UINT32 nOffset, + BYTE *pBuffer, + UINT32 cbBuffer, + UINT32 *pcbWritten) const +{ + CONTRACTL + { + NOTHROW; + PRECONDITION(CheckPointer(pBuffer)); + PRECONDITION(CheckPointer(pcbWritten)); + } + CONTRACTL_END + + HRESULT hr = S_OK; + const StgPoolSeg *pSeg; // A segment being written. + + _ASSERTE(m_pSegData != m_zeros); + + // Start with the base segment. + pSeg = this; + *pcbWritten = 0; + + // As long as there is data, write it. + while (pSeg != NULL) + { + // If there is data in the segment . . . + if (pSeg->m_cbSegNext) + { // If this data should be skipped... + if (nOffset >= pSeg->m_cbSegNext) + { // Skip it + nOffset -= pSeg->m_cbSegNext; + } + else + { + ULONG nNumBytesToCopy = pSeg->m_cbSegNext - nOffset; + if (nNumBytesToCopy > (cbBuffer - *pcbWritten)) + { + _ASSERTE(!"Buffer isn't big enough to copy everything!"); + nNumBytesToCopy = cbBuffer - *pcbWritten; + } + + memcpy(pBuffer + *pcbWritten, pSeg->m_pSegData+nOffset, nNumBytesToCopy); + + *pcbWritten += nNumBytesToCopy; + nOffset = 0; + } + } + + // Get the next segment. + pSeg = pSeg->m_pNextSeg; + } + + return hr; +} // StgPool::CopyData + +//***************************************************************************** +// Get a pointer to the data at some offset. May require traversing the +// chain of extensions. It is the caller's responsibility not to attempt +// to access data beyond the end of a segment. +// This is an internal accessor, and should only be called when the data +// is not in the base segment. +//***************************************************************************** +__checkReturn +HRESULT +StgPool::GetData_i( + UINT32 nOffset, + MetaData::DataBlob *pData) +{ + LIMITED_METHOD_CONTRACT; + + // Shouldn't be called on base segment. + _ASSERTE(nOffset >= m_cbSegNext); + StgPoolSeg *pSeg = this; + + while ((nOffset > 0) && (nOffset >= pSeg->m_cbSegNext)) + { + // On to next segment. + nOffset -= pSeg->m_cbSegNext; + pSeg = pSeg->m_pNextSeg; + + // Is there a next? + if (pSeg == NULL) + { + Debug_ReportError("Invalid offset passed - reached end of pool."); + pData->Clear(); + return CLDB_E_INDEX_NOTFOUND; + } + } + + // For the case where we want to read the first item and the pool is empty. + if (nOffset == pSeg->m_cbSegNext) + { // Can only be if both == 0 + Debug_ReportError("Invalid offset passed - it is at the end of pool."); + pData->Clear(); + return CLDB_E_INDEX_NOTFOUND; + } + + pData->Init(pSeg->m_pSegData + nOffset, pSeg->m_cbSegNext - nOffset); + + return S_OK; +} // StgPool::GetData_i + +// +// +// StgStringPool +// +// + + +//***************************************************************************** +// Create a new, empty string pool. +//***************************************************************************** +__checkReturn +HRESULT +StgStringPool::InitNew( + ULONG cbSize, // Estimated size. + ULONG cItems) // Estimated item count. +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY); + } + CONTRACTL_END + + HRESULT hr; + UINT32 nEmptyStringOffset; + + // Let base class intialize. + IfFailRet(StgPool::InitNew()); + + // Set initial table sizes, if specified. + if (cbSize > 0) + { + if (!Grow(cbSize)) + { + return E_OUTOFMEMORY; + } + } + if (cItems > 0) + { + m_Hash.SetBuckets(cItems); + } + + // Init with empty string. + IfFailRet(AddString("", &nEmptyStringOffset)); + // Empty string had better be at offset 0. + _ASSERTE(nEmptyStringOffset == 0); + + return hr; +} // StgStringPool::InitNew + +//***************************************************************************** +// Load a string heap from persisted memory. If a copy of the data is made +// (so that it may be updated), then a new hash table is generated which can +// be used to elminate duplicates with new strings. +//***************************************************************************** +__checkReturn +HRESULT +StgStringPool::InitOnMem( + void *pData, // Predefined data. + ULONG iSize, // Size of data. + int bReadOnly) // true if append is forbidden. +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY); + } + CONTRACTL_END + + HRESULT hr = S_OK; + + // There may be up to three extra '\0' characters appended for padding. Trim them. + char *pchData = reinterpret_cast<char*>(pData); + while (iSize > 1 && pchData[iSize-1] == 0 && pchData[iSize-2] == 0) + --iSize; + + // Let base class init our memory structure. + IfFailRet(StgPool::InitOnMem(pData, iSize, bReadOnly)); + + //<TODO>@todo: defer this until we hand out a pointer.</TODO> + if (!bReadOnly) + { + IfFailRet(TakeOwnershipOfInitMem()); + IfFailRet(RehashStrings()); + } + + return hr; +} // StgStringPool::InitOnMem + +//***************************************************************************** +// Clears the hash table then calls the base class. +//***************************************************************************** +void StgStringPool::Uninit() +{ + CONTRACTL + { + NOTHROW; + FORBID_FAULT; + } + CONTRACTL_END + + // Clear the hash table. + m_Hash.Clear(); + + // Let base class clean up. + StgPool::Uninit(); +} // StgStringPool::Uninit + +//***************************************************************************** +// Turn hashing off or on. If you turn hashing on, then any existing data is +// thrown away and all data is rehashed during this call. +//***************************************************************************** +__checkReturn +HRESULT +StgStringPool::SetHash(int bHash) +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + HRESULT hr = S_OK; + + // If turning on hash again, need to rehash all strings. + if (bHash) + hr = RehashStrings(); + + m_bHash = bHash; + return (hr); +} // StgStringPool::SetHash + +//***************************************************************************** +// The string will be added to the pool. The offset of the string in the pool +// is returned in *piOffset. If the string is already in the pool, then the +// offset will be to the existing copy of the string. +//***************************************************************************** +__checkReturn +HRESULT +StgStringPool::AddString( + LPCSTR szString, // The string to add to pool. + UINT32 *pnOffset) // Return offset of string here. +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + STRINGHASH *pHash; // Hash item for add. + ULONG iLen; // To handle non-null strings. + LPSTR pData; // Pointer to location for new string. + HRESULT hr; + + _ASSERTE(!m_bReadOnly); + + // Null pointer is an error. + if (szString == 0) + return (PostError(E_INVALIDARG)); + + // Find the real length we need in buffer. + iLen = (ULONG)(strlen(szString) + 1); + + // Where to put the new string? + if (iLen > GetCbSegAvailable()) + { + if (!Grow(iLen)) + return (PostError(OutOfMemory())); + } + pData = reinterpret_cast<LPSTR>(GetNextLocation()); + + // Copy the data into the buffer. + strcpy_s(pData, iLen, szString); + + // If the hash table is to be kept built (default). + if (m_bHash) + { + // Find or add the entry. + pHash = m_Hash.Find(pData, true); + if (!pHash) + return (PostError(OutOfMemory())); + + // If the entry was new, keep the new string. + if (pHash->iOffset == 0xffffffff) + { + *pnOffset = pHash->iOffset = GetNextOffset(); + SegAllocate(iLen); + + // Check for hash chains that are too long. + if (m_Hash.MaxChainLength() > MAX_CHAIN_LENGTH) + { + IfFailRet(RehashStrings()); + } + } + // Else use the old one. + else + { + *pnOffset = pHash->iOffset; + } + } + // Probably an import which defers the hash table for speed. + else + { + *pnOffset = GetNextOffset(); + SegAllocate(iLen); + } + return S_OK; +} // StgStringPool::AddString + +//***************************************************************************** +// Add a string to the pool with Unicode to UTF8 conversion. +//***************************************************************************** +__checkReturn +HRESULT +StgStringPool::AddStringW( + LPCWSTR szString, // The string to add to pool. + UINT32 *pnOffset) // Return offset of string here. +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + STRINGHASH *pHash; // Hash item for add. + ULONG iLen; // Correct length after conversion. + LPSTR pData; // Pointer to location for new string. + + _ASSERTE(!m_bReadOnly); + + // Null pointer is an error. + if (szString == 0) + return (PostError(E_INVALIDARG)); + + // Special case empty string. + if (*szString == '\0') + { + *pnOffset = 0; + return (S_OK); + } + + // How many bytes will be required in the heap? + iLen = ::WszWideCharToMultiByte( + CP_UTF8, + 0, + szString, + -1, // null-terminated string + NULL, + 0, + NULL, + NULL); + // WCTMB includes trailing 0 if (when passing parameter #4 (length) -1. + + // Check for room. + if (iLen > GetCbSegAvailable()) + { + if (!Grow(iLen)) + return (PostError(OutOfMemory())); + } + pData = reinterpret_cast<LPSTR>(GetNextLocation()); + + // Convert the data in place to the correct location. + iLen = ::WszWideCharToMultiByte( + CP_UTF8, + 0, + szString, + -1, + pData, + GetCbSegAvailable(), + NULL, + NULL); + if (iLen == 0) + return (BadError(HRESULT_FROM_NT(GetLastError()))); + + // If the hash table is to be kept built (default). + if (m_bHash) + { + // Find or add the entry. + pHash = m_Hash.Find(pData, true); + if (!pHash) + return (PostError(OutOfMemory())); + + // If the entry was new, keep the new string. + if (pHash->iOffset == 0xffffffff) + { + *pnOffset = pHash->iOffset = GetNextOffset(); + SegAllocate(iLen); + } + // Else use the old one. + else + { + *pnOffset = pHash->iOffset; + } + } + // Probably an import which defers the hash table for speed. + else + { + *pnOffset = GetNextOffset(); + SegAllocate(iLen); + } + return (S_OK); +} // StgStringPool::AddStringW + + +//***************************************************************************** +// Clears out the existing hash table used to eliminate duplicates. Then +// rebuilds the hash table from scratch based on the current data. +//***************************************************************************** +__checkReturn +HRESULT +StgStringPool::RehashStrings() +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + ULONG iOffset; // Loop control. + ULONG iMax; // End of loop. + ULONG iSeg; // Location within segment. + StgPoolSeg *pSeg = this; // To loop over segments. + STRINGHASH *pHash; // Hash item for add. + LPCSTR pString; // A string; + ULONG iLen; // The string's length. + int iBuckets; // Buckets in the hash. + int iCount; // Items in the hash. + int iNewBuckets; // New count of buckets in the hash. + + // Determine the new bucket size. + iBuckets = m_Hash.Buckets(); + iCount = m_Hash.Count(); + iNewBuckets = max(iCount, iBuckets+iBuckets/2+1); + + // Remove any stale data. + m_Hash.Clear(); + m_Hash.SetBuckets(iNewBuckets); + + // How far should the loop go. + iMax = GetNextOffset(); + + // Go through each string, skipping initial empty string. + for (iSeg=iOffset=1; iOffset < iMax; ) + { + // Get the string from the pool. + pString = reinterpret_cast<LPCSTR>(pSeg->m_pSegData + iSeg); + // Add the string to the hash table. + if ((pHash = m_Hash.Add(pString)) == 0) + return (PostError(OutOfMemory())); + pHash->iOffset = iOffset; + + // Move to next string. + iLen = (ULONG)(strlen(pString) + 1); + iOffset += iLen; + iSeg += iLen; + if (iSeg >= pSeg->m_cbSegNext) + { + pSeg = pSeg->m_pNextSeg; + iSeg = 0; + } + } + return (S_OK); +} // StgStringPool::RehashStrings + +// +// +// StgGuidPool +// +// + +__checkReturn +HRESULT +StgGuidPool::InitNew( + ULONG cbSize, // Estimated size. + ULONG cItems) // Estimated item count. +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + HRESULT hr; // A result. + + if (FAILED(hr = StgPool::InitNew())) + return (hr); + + // Set initial table sizes, if specified. + if (cbSize) + if (!Grow(cbSize)) + return E_OUTOFMEMORY; + if (cItems) + m_Hash.SetBuckets(cItems); + + return (S_OK); +} // StgGuidPool::InitNew + +//***************************************************************************** +// Load a Guid heap from persisted memory. If a copy of the data is made +// (so that it may be updated), then a new hash table is generated which can +// be used to elminate duplicates with new Guids. +//***************************************************************************** +__checkReturn +HRESULT +StgGuidPool::InitOnMem( + void *pData, // Predefined data. + ULONG iSize, // Size of data. + int bReadOnly) // true if append is forbidden. +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + HRESULT hr; + + // Let base class init our memory structure. + IfFailRet(StgPool::InitOnMem(pData, iSize, bReadOnly)); + + // For init on existing mem case. + if (pData && iSize) + { + // If we cannot update, then we don't need a hash table. + if (bReadOnly) + return S_OK; + + //<TODO>@todo: defer this until we hand out a pointer.</TODO> + IfFailRet(TakeOwnershipOfInitMem()); + + // Build the hash table on the data. + if (FAILED(hr = RehashGuids())) + { + Uninit(); + return hr; + } + } + + return S_OK; +} // StgGuidPool::InitOnMem + +//***************************************************************************** +// Clears the hash table then calls the base class. +//***************************************************************************** +void StgGuidPool::Uninit() +{ + CONTRACTL + { + NOTHROW; + FORBID_FAULT; + } + CONTRACTL_END + + // Clear the hash table. + m_Hash.Clear(); + + // Let base class clean up. + StgPool::Uninit(); +} // StgGuidPool::Uninit + +//***************************************************************************** +// Add a segment to the chain of segments. +//***************************************************************************** +__checkReturn +HRESULT +StgGuidPool::AddSegment( + const void *pData, // The data. + ULONG cbData, // Size of the data. + bool bCopy) // If true, make a copy of the data. +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + // Want an integeral number of GUIDs. + _ASSERTE((cbData % sizeof(GUID)) == 0); + + return StgPool::AddSegment(pData, cbData, bCopy); + +} // StgGuidPool::AddSegment + +//***************************************************************************** +// Turn hashing off or on. If you turn hashing on, then any existing data is +// thrown away and all data is rehashed during this call. +//***************************************************************************** +__checkReturn +HRESULT +StgGuidPool::SetHash(int bHash) +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + HRESULT hr = S_OK; + + // If turning on hash again, need to rehash all guids. + if (bHash) + hr = RehashGuids(); + + m_bHash = bHash; + return (hr); +} // StgGuidPool::SetHash + +//***************************************************************************** +// The Guid will be added to the pool. The index of the Guid in the pool +// is returned in *piIndex. If the Guid is already in the pool, then the +// index will be to the existing copy of the Guid. +//***************************************************************************** +__checkReturn +HRESULT +StgGuidPool::AddGuid( + const GUID *pGuid, // The Guid to add to pool. + UINT32 *pnIndex) // Return 1-based index of Guid here. +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + GUIDHASH *pHash = NULL; // Hash item for add. + + GUID guid = *pGuid; + SwapGuid(&guid); + + // Special case for GUID_NULL + if (guid == GUID_NULL) + { + *pnIndex = 0; + return S_OK; + } + + // If the hash table is to be kept built (default). + if (m_bHash) + { + // Find or add the entry. + pHash = m_Hash.Find(&guid, true); + if (!pHash) + return (PostError(OutOfMemory())); + + // If the guid was found, just use it. + if (pHash->iIndex != 0xffffffff) + { // Return 1-based index. + *pnIndex = pHash->iIndex; + return S_OK; + } + } + + // Space on heap for new guid? + if (sizeof(GUID) > GetCbSegAvailable()) + { + if (!Grow(sizeof(GUID))) + return (PostError(OutOfMemory())); + } + + // Copy the guid to the heap. + *reinterpret_cast<GUID*>(GetNextLocation()) = guid; + + // Give the 1-based index back to caller. + *pnIndex = (GetNextOffset() / sizeof(GUID)) + 1; + + // If hashing, save the 1-based index in the hash. + if (m_bHash) + pHash->iIndex = *pnIndex; + + // Update heap counters. + SegAllocate(sizeof(GUID)); + + return S_OK; +} // StgGuidPool::AddGuid + +//***************************************************************************** +// Recompute the hashes for the pool. +//***************************************************************************** +__checkReturn +HRESULT +StgGuidPool::RehashGuids() +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + ULONG iOffset; // Loop control. + ULONG iMax; // End of loop. + ULONG iSeg; // Location within segment. + StgPoolSeg *pSeg = this; // To loop over segments. + GUIDHASH *pHash; // Hash item for add. + GUID *pGuid; // A guid; + + // Remove any stale data. + m_Hash.Clear(); + + // How far should the loop go. + iMax = GetNextOffset(); + + // Go through each guid. + for (iSeg=iOffset=0; iOffset < iMax; ) + { + // Get a pointer to the guid. + pGuid = reinterpret_cast<GUID*>(pSeg->m_pSegData + iSeg); + // Add the guid to the hash table. + if ((pHash = m_Hash.Add(pGuid)) == 0) + return (PostError(OutOfMemory())); + pHash->iIndex = iOffset / sizeof(GUID); + + // Move to next Guid. + iOffset += sizeof(GUID); + iSeg += sizeof(GUID); + if (iSeg > pSeg->m_cbSegNext) + { + pSeg = pSeg->m_pNextSeg; + iSeg = 0; + } + } + return (S_OK); +} // StgGuidPool::RehashGuids + +// +// +// StgBlobPool +// +// + + + +//***************************************************************************** +// Create a new, empty blob pool. +//***************************************************************************** +__checkReturn +HRESULT +StgBlobPool::InitNew( + ULONG cbSize, // Estimated size. + ULONG cItems, // Estimated item count. + BOOL fAddEmptryItem) // Should we add an empty item at offset 0 +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + HRESULT hr; + + // Let base class intialize. + IfFailRet(StgPool::InitNew()); + + // Set initial table sizes, if specified. + if (cbSize > 0) + { + if (!Grow(cbSize)) + return E_OUTOFMEMORY; + } + if (cItems > 0) + m_Hash.SetBuckets(cItems); + + // Init with empty blob. + + // Normally must do this, regardless if we currently have anything in the pool. + // If we don't do this, the first blob that gets added to the pool will + // have an offset of 0. This will cause this blob to have a token of + // 0x70000000, which is considered a nil string token. + // + // By inserting a zero length blob into the pool the being with, we're + // assured that the first blob added to the pool will have an offset + // of 1 and a token of 0x70000001, which is a valid token. + // + // The only time we wouldn't want to do this is if we're reading in a delta metadata. + // Then, we don't care if the first string is at offset 0... when the delta gets applied, + // the string will get moved to the appropriate offset. + if (fAddEmptryItem) + { + MetaData::DataBlob emptyBlob(NULL, 0); + UINT32 nIndex_Ignore; + IfFailRet(AddBlob(&emptyBlob, &nIndex_Ignore)); + // Empty blob better be at offset 0. + _ASSERTE(nIndex_Ignore == 0); + } + return hr; +} // StgBlobPool::InitNew + +//***************************************************************************** +// Init the blob pool for use. This is called for both create and read case. +// If there is existing data and bCopyData is true, then the data is rehashed +// to eliminate dupes in future adds. +//***************************************************************************** +__checkReturn +HRESULT +StgBlobPool::InitOnMem( + void *pBuf, // Predefined data. + ULONG iBufSize, // Size of data. + int bReadOnly) // true if append is forbidden. +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + HRESULT hr; + + // Let base class init our memory structure. + IfFailRet(StgPool::InitOnMem(pBuf, iBufSize, bReadOnly)); + + // Init hash table from existing data. + // If we cannot update, we don't need a hash table. + if (bReadOnly) + { + return S_OK; + } + + //<TODO>@todo: defer this until we hand out a pointer.</TODO> + IfFailRet(TakeOwnershipOfInitMem()); + + UINT32 nMaxOffset = GetNextOffset(); + for (UINT32 nOffset = 0; nOffset < nMaxOffset; ) + { + MetaData::DataBlob blob; + BLOBHASH *pHash; + + IfFailRet(GetBlobWithSizePrefix(nOffset, &blob)); + + // Add the blob to the hash table. + if ((pHash = m_Hash.Add(blob.GetDataPointer())) == NULL) + { + Uninit(); + return E_OUTOFMEMORY; + } + pHash->iOffset = nOffset; + + nOffset += blob.GetSize(); + } + return S_OK; +} // StgBlobPool::InitOnMem + +//***************************************************************************** +// Clears the hash table then calls the base class. +//***************************************************************************** +void StgBlobPool::Uninit() +{ + CONTRACTL + { + NOTHROW; + FORBID_FAULT; + } + CONTRACTL_END + + // Clear the hash table. + m_Hash.Clear(); + + // Let base class clean up. + StgPool::Uninit(); +} // StgBlobPool::Uninit + + +//***************************************************************************** +// The blob will be added to the pool. The offset of the blob in the pool +// is returned in *piOffset. If the blob is already in the pool, then the +// offset will be to the existing copy of the blob. +//***************************************************************************** +__checkReturn +HRESULT +StgBlobPool::AddBlob( + const MetaData::DataBlob *pData, + UINT32 *pnOffset) // Return offset of blob here. +{ + BLOBHASH *pHash; // Hash item for add. + void *pBytes; // Working pointer. + BYTE *pStartLoc; // Location to write real blob + ULONG iRequired; // How much buffer for this blob? + ULONG iFillerLen; // space to fill to make byte-aligned + HRESULT hr; + + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + // Can we handle this blob? + if (pData->GetSize() > CPackedLen::MAX_LEN) + return (PostError(CLDB_E_TOO_BIG)); + + // worst case is we need three more bytes to ensure byte-aligned, hence the 3 + iRequired = pData->GetSize() + CPackedLen::Size(pData->GetSize()) + 3; + if (iRequired > GetCbSegAvailable()) + { + if (!Grow(iRequired)) + return (PostError(OutOfMemory())); + } + + // unless changed due to alignment, the location of the blob is just + // the value returned by GetNextLocation(), which is also a iFillerLen of + // 0 + + pStartLoc = (BYTE *)GetNextLocation(); + iFillerLen = 0; + + // technichally, only the data portion must be DWORD-aligned. So, if the + // data length is zero, we don't need to worry about alignment. + + // Pack in the length at pStartLoc (the start location) + pBytes = CPackedLen::PutLength(pStartLoc, pData->GetSize()); + + // Put the bytes themselves. + memcpy(pBytes, pData->GetDataPointer(), pData->GetSize()); + + // Find or add the entry. + if ((pHash = m_Hash.Find(GetNextLocation() + iFillerLen, true)) == NULL) + return (PostError(OutOfMemory())); + + // If the entry was new, keep the new blob. + if (pHash->iOffset == 0xffffffff) + { + // this blob's offset is increased by iFillerLen bytes + pHash->iOffset = *pnOffset = GetNextOffset() + iFillerLen; + // only SegAllocate what we actually used, rather than what we requested + SegAllocate(pData->GetSize() + CPackedLen::Size(pData->GetSize()) + iFillerLen); + + // Check for hash chains that are too long. + if (m_Hash.MaxChainLength() > MAX_CHAIN_LENGTH) + { + IfFailRet(RehashBlobs()); + } + } + // Else use the old one. + else + { + *pnOffset = pHash->iOffset; + } + + return S_OK; +} // StgBlobPool::AddBlob + +//***************************************************************************** +// Return a pointer to a blob, and the size of the blob. +//***************************************************************************** +__checkReturn +HRESULT +StgBlobPool::GetBlob( + UINT32 nOffset, // Offset of blob in pool. + MetaData::DataBlob *pData) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + HRESULT hr; + + if (nOffset == 0) + { + // TODO: It would be nice to remove it, but people read behind the end of buffer, + // e.g. VBC reads 2 zeros even though the size is 0 when it's storing string in the blob. + // Nice to have: Move this to the public API only as a compat layer. + pData->Init((BYTE *)m_zeros, 0); + return S_OK; + } + + IfFailGo(StgPool::GetData(nOffset, pData)); + + UINT32 cbBlobContentSize; + if (!pData->GetCompressedU(&cbBlobContentSize)) + { + IfFailGo(COR_E_BADIMAGEFORMAT); + } + if (!pData->TruncateToExactSize(cbBlobContentSize)) + { + IfFailGo(COR_E_BADIMAGEFORMAT); + } + + return S_OK; +ErrExit: + pData->Clear(); + return hr; +} // StgBlobPool::GetBlob + +//***************************************************************************** +// Return a pointer to a blob, and the size of the blob. +//***************************************************************************** +__checkReturn +HRESULT +StgBlobPool::GetBlobWithSizePrefix( + UINT32 nOffset, // Offset of blob in pool. + MetaData::DataBlob *pData) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + HRESULT hr; + + if (nOffset == 0) + { + // TODO: Should be a static empty blob once we get rid of m_zeros + pData->Init((BYTE *)m_zeros, 1); + return S_OK; + } + + IfFailGo(StgPool::GetData(nOffset, pData)); + + UINT32 cbBlobContentSize; + UINT32 cbBlobSizePrefixSize; + if (!pData->PeekCompressedU(&cbBlobContentSize, &cbBlobSizePrefixSize)) + { + IfFailGo(COR_E_BADIMAGEFORMAT); + } + //_ASSERTE(cbBlobSizePrefixSize <= 4); + //_ASSERTE(cbBlobContentSize <= CompressedInteger::const_Max); + + // Cannot overflow, because previous asserts hold (in comments) + UINT32 cbBlobSize; + cbBlobSize = cbBlobContentSize + cbBlobSizePrefixSize; + if (!pData->TruncateToExactSize(cbBlobSize)) + { + IfFailGo(COR_E_BADIMAGEFORMAT); + } + + return S_OK; +ErrExit: + pData->Clear(); + return hr; +} // StgBlobPool::GetBlob + +//***************************************************************************** +// Turn hashing off or on. If you turn hashing on, then any existing data is +// thrown away and all data is rehashed during this call. +//***************************************************************************** +__checkReturn +HRESULT +StgBlobPool::SetHash(int bHash) +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + HRESULT hr = S_OK; + + // If turning on hash again, need to rehash all Blobs. + if (bHash) + hr = RehashBlobs(); + + //<TODO>@todo: m_bHash = bHash;</TODO> + return (hr); +} // StgBlobPool::SetHash + +//***************************************************************************** +// Clears out the existing hash table used to eliminate duplicates. Then +// rebuilds the hash table from scratch based on the current data. +//***************************************************************************** +__checkReturn +HRESULT +StgBlobPool::RehashBlobs() +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + void const *pBlob; // Pointer to a given blob. + ULONG cbBlob; // Length of a blob. + int iSizeLen = 0; // Size of an encoded length. + ULONG iOffset; // Location within iteration. + ULONG iMax; // End of loop. + ULONG iSeg; // Location within segment. + StgPoolSeg *pSeg = this; // To loop over segments. + BLOBHASH *pHash; // Hash item for add. + int iBuckets; // Buckets in the hash. + int iCount; // Items in the hash. + int iNewBuckets; // New count of buckets in the hash. + + // Determine the new bucket size. + iBuckets = m_Hash.Buckets(); + iCount = m_Hash.Count(); + iNewBuckets = max(iCount, iBuckets+iBuckets/2+1); + + // Remove any stale data. + m_Hash.Clear(); + m_Hash.SetBuckets(iNewBuckets); + + // How far should the loop go. + iMax = GetNextOffset(); + + // Go through each string, skipping initial empty string. + for (iSeg=iOffset=0; iOffset < iMax; ) + { + // Get the string from the pool. + pBlob = pSeg->m_pSegData + iSeg; + + cbBlob = CPackedLen::GetLength(pBlob, &iSizeLen); + if (cbBlob == (ULONG)-1) + { // Invalid blob size encoding + + //#GarbageInBlobHeap + // Note that this is allowed in ECMA spec (see chapter "#US and #Blob heaps"): + // Both these heaps can contain garbage, as long as any part that is reachable from any of + // the tables contains a valid 'blob'. + + // The hash is incomplete, which means that we might emit duplicate blob entries ... that is fine + return S_OK; + } + //_ASSERTE((iSizeLen >= 1) && (iSizeLen <= 4) && (cbBlob <= 0x1fffffff)); + + // Make it blob size incl. its size encoding (cannot integer overflow) + cbBlob += iSizeLen; + // Check for integer overflow and that the entire blob entry is in this segment + if ((iSeg > (iSeg + cbBlob)) || ((iSeg + cbBlob) > pSeg->m_cbSegNext)) + { // Invalid blob size + + // See code:#GarbageInBlobHeap + // The hash is incomplete, which means that we might emit duplicate blob entries ... that is fine + return S_OK; + } + + // Add the blob to the hash table. + if ((pHash = m_Hash.Add(pBlob)) == 0) + { + Uninit(); + return (E_OUTOFMEMORY); + } + pHash->iOffset = iOffset; + + // Move to next blob. + iOffset += cbBlob; + iSeg += cbBlob; + if (iSeg >= pSeg->m_cbSegNext) + { + pSeg = pSeg->m_pNextSeg; + iSeg = 0; + } + } + return (S_OK); +} // StgBlobPool::RehashBlobs + + +// +// CInMemoryStream +// + + +ULONG +STDMETHODCALLTYPE CInMemoryStream::Release() +{ + CONTRACTL + { + NOTHROW; + FORBID_FAULT; + SUPPORTS_DAC_HOST_ONLY; + } + CONTRACTL_END + + ULONG cRef = InterlockedDecrement(&m_cRef); + if (cRef == 0) + { + if (m_dataCopy != NULL) + delete [] m_dataCopy; + + delete this; + } + return (cRef); +} // CInMemoryStream::Release + +HRESULT +STDMETHODCALLTYPE +CInMemoryStream::QueryInterface(REFIID riid, PVOID *ppOut) +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + if (!ppOut) + { + return E_POINTER; + } + + *ppOut = NULL; + if (riid == IID_IStream || riid == IID_ISequentialStream || riid == IID_IUnknown) + { + *ppOut = this; + AddRef(); + return (S_OK); + } + + return E_NOINTERFACE; + +} // CInMemoryStream::QueryInterface + +HRESULT +STDMETHODCALLTYPE +CInMemoryStream::Read( + void *pv, + ULONG cb, + ULONG *pcbRead) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY; + + ULONG cbRead = min(cb, m_cbSize - m_cbCurrent); + + if (cbRead == 0) + return (S_FALSE); + memcpy(pv, (void *) ((ULONG_PTR) m_pMem + m_cbCurrent), cbRead); + if (pcbRead) + *pcbRead = cbRead; + m_cbCurrent += cbRead; + return (S_OK); +} // CInMemoryStream::Read + +HRESULT +STDMETHODCALLTYPE +CInMemoryStream::Write( + const void *pv, + ULONG cb, + ULONG *pcbWritten) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY; + + if (ovadd_gt(m_cbCurrent, cb, m_cbSize)) + return (OutOfMemory()); + + memcpy((BYTE *) m_pMem + m_cbCurrent, pv, cb); + m_cbCurrent += cb; + if (pcbWritten) *pcbWritten = cb; + return (S_OK); +} // CInMemoryStream::Write + +HRESULT +STDMETHODCALLTYPE +CInMemoryStream::Seek( + LARGE_INTEGER dlibMove, + DWORD dwOrigin, + ULARGE_INTEGER *plibNewPosition) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY; + + _ASSERTE(dwOrigin == STREAM_SEEK_SET || dwOrigin == STREAM_SEEK_CUR); + _ASSERTE(dlibMove.QuadPart <= static_cast<LONGLONG>(ULONG_MAX)); + + if (dwOrigin == STREAM_SEEK_SET) + { + m_cbCurrent = (ULONG) dlibMove.QuadPart; + } + else + if (dwOrigin == STREAM_SEEK_CUR) + { + m_cbCurrent+= (ULONG)dlibMove.QuadPart; + } + + if (plibNewPosition) + { + plibNewPosition->QuadPart = m_cbCurrent; + } + + return (m_cbCurrent < m_cbSize) ? (S_OK) : E_FAIL; +} // CInMemoryStream::Seek + +HRESULT +STDMETHODCALLTYPE +CInMemoryStream::CopyTo( + IStream *pstm, + ULARGE_INTEGER cb, + ULARGE_INTEGER *pcbRead, + ULARGE_INTEGER *pcbWritten) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY; + + HRESULT hr; + // We don't handle pcbRead or pcbWritten. + _ASSERTE(pcbRead == 0); + _ASSERTE(pcbWritten == 0); + + _ASSERTE(cb.QuadPart <= ULONG_MAX); + ULONG cbTotal = min(static_cast<ULONG>(cb.QuadPart), m_cbSize - m_cbCurrent); + ULONG cbRead=min(1024, cbTotal); + CQuickBytes rBuf; + void *pBuf = rBuf.AllocNoThrow(cbRead); + if (pBuf == 0) + return (PostError(OutOfMemory())); + + while (cbTotal) + { + if (cbRead > cbTotal) + cbRead = cbTotal; + if (FAILED(hr=Read(pBuf, cbRead, 0))) + return (hr); + if (FAILED(hr=pstm->Write(pBuf, cbRead, 0))) + return (hr); + cbTotal -= cbRead; + } + + // Adjust seek pointer to the end. + m_cbCurrent = m_cbSize; + + return (S_OK); +} // CInMemoryStream::CopyTo + +HRESULT +CInMemoryStream::CreateStreamOnMemory( + void *pMem, // Memory to create stream on. + ULONG cbSize, // Size of data. + IStream **ppIStream, // Return stream object here. + BOOL fDeleteMemoryOnRelease) +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + CInMemoryStream *pIStream; // New stream object. + if ((pIStream = new (nothrow) CInMemoryStream) == 0) + return (PostError(OutOfMemory())); + pIStream->InitNew(pMem, cbSize); + if (fDeleteMemoryOnRelease) + { + // make sure this memory is allocated using new + pIStream->m_dataCopy = (BYTE *)pMem; + } + *ppIStream = pIStream; + return (S_OK); +} // CInMemoryStream::CreateStreamOnMemory + +HRESULT +CInMemoryStream::CreateStreamOnMemoryCopy( + void *pMem, + ULONG cbSize, + IStream **ppIStream) +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + CInMemoryStream *pIStream; // New stream object. + if ((pIStream = new (nothrow) CInMemoryStream) == 0) + return (PostError(OutOfMemory())); + + // Init the stream. + pIStream->m_cbCurrent = 0; + pIStream->m_cbSize = cbSize; + + // Copy the data. + pIStream->m_dataCopy = new (nothrow) BYTE[cbSize]; + + if (pIStream->m_dataCopy == NULL) + { + delete pIStream; + return (PostError(OutOfMemory())); + } + + pIStream->m_pMem = pIStream->m_dataCopy; + memcpy(pIStream->m_dataCopy, pMem, cbSize); + + *ppIStream = pIStream; + return (S_OK); +} // CInMemoryStream::CreateStreamOnMemoryCopy + +//--------------------------------------------------------------------------- +// CGrowableStream is a simple IStream implementation that grows as +// its written to. All the memory is contigious, so read access is +// fast. A grow does a realloc, so be aware of that if you're going to +// use this. +//--------------------------------------------------------------------------- + +//Constructs a new GrowableStream +// multiplicativeGrowthRate - when the stream grows it will be at least this +// multiple of its old size. Values greater than 1 ensure O(N) amortized +// performance growing the stream to size N, 1 ensures O(N^2) amortized perf +// but gives the tightest memory usage. Valid range is [1.0, 2.0]. +// additiveGrowthRate - when the stream grows it will increase in size by at least +// this number of bytes. Larger numbers cause fewer re-allocations at the cost of +// increased memory usage. +CGrowableStream::CGrowableStream(float multiplicativeGrowthRate, DWORD additiveGrowthRate) +{ + CONTRACTL + { + NOTHROW; + FORBID_FAULT; + } + CONTRACTL_END + + m_swBuffer = NULL; + m_dwBufferSize = 0; + m_dwBufferIndex = 0; + m_dwStreamLength = 0; + m_cRef = 1; + + // Lets make sure these values stay somewhat sane... if you adjust the limits + // make sure you also write correct overflow checking code in EnsureCapcity + _ASSERTE(multiplicativeGrowthRate >= 1.0F && multiplicativeGrowthRate <= 2.0F); + m_multiplicativeGrowthRate = min(max(1.0F, multiplicativeGrowthRate), 2.0F); + + _ASSERTE(additiveGrowthRate >= 1); + m_additiveGrowthRate = max(1, additiveGrowthRate); +} // CGrowableStream::CGrowableStream + +#ifndef DACCESS_COMPILE + +CGrowableStream::~CGrowableStream() +{ + CONTRACTL + { + NOTHROW; + FORBID_FAULT; + } + CONTRACTL_END + + // Destroy the buffer. + if (m_swBuffer != NULL) + delete [] m_swBuffer; + + m_swBuffer = NULL; + m_dwBufferSize = 0; +} // CGrowableStream::~CGrowableStream + +// Grows the stream and optionally the internal buffer to ensure it is at least +// newLogicalSize +HRESULT CGrowableStream::EnsureCapacity(DWORD newLogicalSize) +{ + _ASSERTE(m_dwBufferSize >= m_dwStreamLength); + + // If there is no enough space left in the buffer, grow it + if (newLogicalSize > m_dwBufferSize) + { + // Grow to max of newLogicalSize, m_dwBufferSize*multiplicativeGrowthRate, and + // m_dwBufferSize+m_additiveGrowthRate + S_UINT32 addSize = S_UINT32(m_dwBufferSize) + S_UINT32(m_additiveGrowthRate); + if (addSize.IsOverflow()) + { + addSize = S_UINT32(UINT_MAX); + } + + // this should have been enforced in the constructor too + _ASSERTE(m_multiplicativeGrowthRate <= 2.0 && m_multiplicativeGrowthRate >= 1.0); + + // 2*UINT_MAX doesn't overflow a float so this certain to be safe + float multSizeF = (float)m_dwBufferSize * m_multiplicativeGrowthRate; + DWORD multSize; + if(multSizeF > (float)UINT_MAX) + { + multSize = UINT_MAX; + } + else + { + multSize = (DWORD)multSizeF; + } + + DWORD newBufferSize = max(max(newLogicalSize, multSize), addSize.Value()); + + char *tmp = new (nothrow) char[newBufferSize]; + if(tmp == NULL) + { + return E_OUTOFMEMORY; + } + + if (m_swBuffer) { + memcpy (tmp, m_swBuffer, m_dwBufferSize); + delete [] m_swBuffer; + } + m_swBuffer = (BYTE *)tmp; + m_dwBufferSize = newBufferSize; + } + + _ASSERTE(m_dwBufferSize >= newLogicalSize); + // the internal buffer is big enough, might have to increase logical size + // though + if(newLogicalSize > m_dwStreamLength) + { + m_dwStreamLength = newLogicalSize; + } + + _ASSERTE(m_dwBufferSize >= m_dwStreamLength); + return S_OK; +} + +ULONG +STDMETHODCALLTYPE +CGrowableStream::Release() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + ULONG cRef = InterlockedDecrement(&m_cRef); + + if (cRef == 0) + delete this; + + return cRef; +} // CGrowableStream::Release + +HRESULT +STDMETHODCALLTYPE +CGrowableStream::QueryInterface( + REFIID riid, + PVOID *ppOut) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY + + if (riid != IID_IUnknown && riid!=IID_ISequentialStream && riid!=IID_IStream) + return E_NOINTERFACE; + + *ppOut = this; + AddRef(); + return (S_OK); +} // CGrowableStream::QueryInterface + +HRESULT +CGrowableStream::Read( + void *pv, + ULONG cb, + ULONG *pcbRead) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY + + HRESULT hr = S_OK; + DWORD dwCanReadBytes = 0; + + if (NULL == pv) + return E_POINTER; + + // short-circuit a zero-length read or see if we are at the end + if (cb == 0 || m_dwBufferIndex >= m_dwStreamLength) + { + if (pcbRead != NULL) + *pcbRead = 0; + + return S_OK; + } + + // Figure out if we have enough room in the stream (excluding any + // unused space at the end of the buffer) + dwCanReadBytes = cb; + + S_UINT32 dwNewIndex = S_UINT32(dwCanReadBytes) + S_UINT32(m_dwBufferIndex); + if (dwNewIndex.IsOverflow() || (dwNewIndex.Value() > m_dwStreamLength)) + { + // Only read whatever is left in the buffer (if any) + dwCanReadBytes = (m_dwStreamLength - m_dwBufferIndex); + } + + // copy from our buffer to caller's buffer + memcpy(pv, &m_swBuffer[m_dwBufferIndex], dwCanReadBytes); + + // adjust our current position + m_dwBufferIndex += dwCanReadBytes; + + // if they want the info, tell them how many byte we read for them + if (pcbRead != NULL) + *pcbRead = dwCanReadBytes; + + return hr; +} // CGrowableStream::Read + +HRESULT +CGrowableStream::Write( + const void *pv, + ULONG cb, + ULONG *pcbWritten) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY + + HRESULT hr = S_OK; + DWORD dwActualWrite = 0; + + // avoid NULL write + if (cb == 0) + { + hr = S_OK; + goto Error; + } + + // Check if our buffer is large enough + _ASSERTE(m_dwBufferIndex <= m_dwStreamLength); + _ASSERTE(m_dwStreamLength <= m_dwBufferSize); + + // If there is no enough space left in the buffer, grow it + if (cb > (m_dwStreamLength - m_dwBufferIndex)) + { + // Determine the new size needed + S_UINT32 size = S_UINT32(m_dwBufferSize) + S_UINT32(cb); + if (size.IsOverflow()) + { + hr = HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + goto Error; + } + + hr = EnsureCapacity(size.Value()); + if(FAILED(hr)) + { + goto Error; + } + } + + if ((pv != NULL) && (cb > 0)) + { + // write to current position in the buffer + memcpy(&m_swBuffer[m_dwBufferIndex], pv, cb); + + // now update our current index + m_dwBufferIndex += cb; + + // in case they want to know the number of bytes written + dwActualWrite = cb; + } + +Error: + if (pcbWritten) + *pcbWritten = dwActualWrite; + + return hr; +} // CGrowableStream::Write + +STDMETHODIMP +CGrowableStream::Seek( + LARGE_INTEGER dlibMove, + DWORD dwOrigin, + ULARGE_INTEGER *plibNewPosition) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY + + // a Seek() call on STREAM_SEEK_CUR and a dlibMove == 0 is a + // request to get the current seek position. + if ((dwOrigin == STREAM_SEEK_CUR && dlibMove.u.LowPart == 0) && + (dlibMove.u.HighPart == 0) && + (NULL != plibNewPosition)) + { + goto Error; + } + + // we only support STREAM_SEEK_SET (beginning of buffer) + if (dwOrigin != STREAM_SEEK_SET) + return E_NOTIMPL; + + // did they ask to seek past end of stream? If so we're supposed to + // extend with zeros. But we've never supported that. + if (dlibMove.u.LowPart > m_dwStreamLength) + return E_UNEXPECTED; + + // we ignore the high part of the large integer + SIMPLIFYING_ASSUMPTION(dlibMove.u.HighPart == 0); + m_dwBufferIndex = dlibMove.u.LowPart; + +Error: + if (NULL != plibNewPosition) + { + plibNewPosition->u.HighPart = 0; + plibNewPosition->u.LowPart = m_dwBufferIndex; + } + + return S_OK; +} // CGrowableStream::Seek + +STDMETHODIMP +CGrowableStream::SetSize( + ULARGE_INTEGER libNewSize) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY + + DWORD dwNewSize = libNewSize.u.LowPart; + + _ASSERTE(libNewSize.u.HighPart == 0); + + // we don't support large allocations + if (libNewSize.u.HighPart > 0) + return E_OUTOFMEMORY; + + HRESULT hr = EnsureCapacity(dwNewSize); + if(FAILED(hr)) + { + return hr; + } + + // EnsureCapacity doesn't shrink the logicalSize if dwNewSize is smaller + // and SetSize is allowed to shrink the stream too. Note that we won't + // release physical memory here, we just appear to get smaller + m_dwStreamLength = dwNewSize; + + return S_OK; +} // CGrowableStream::SetSize + +STDMETHODIMP +CGrowableStream::Stat( + STATSTG *pstatstg, + DWORD grfStatFlag) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY + + if (NULL == pstatstg) + return E_POINTER; + + // this is the only useful information we hand out - the length of the stream + pstatstg->cbSize.u.HighPart = 0; + pstatstg->cbSize.u.LowPart = m_dwStreamLength; + pstatstg->type = STGTY_STREAM; + + // we ignore the grfStatFlag - we always assume STATFLAG_NONAME + pstatstg->pwcsName = NULL; + + pstatstg->grfMode = 0; + pstatstg->grfLocksSupported = 0; + pstatstg->clsid = CLSID_NULL; + pstatstg->grfStateBits = 0; + + return S_OK; +} // CGrowableStream::Stat + +// +// Clone - Make a deep copy of the stream into a new cGrowableStream instance +// +// Arguments: +// ppStream - required output parameter for the new stream instance +// +// Returns: +// S_OK on succeess, or an error code on failure. +// +HRESULT +CGrowableStream::Clone( + IStream **ppStream) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY + + if (NULL == ppStream) + return E_POINTER; + + // Copy our entire buffer into the new stream + CGrowableStream * newStream = new (nothrow) CGrowableStream(); + if (newStream == NULL) + { + return E_OUTOFMEMORY; + } + + HRESULT hr = newStream->Write(m_swBuffer, m_dwStreamLength, NULL); + if (FAILED(hr)) + { + delete newStream; + return hr; + } + + *ppStream = newStream; + return S_OK; +} // CGrowableStream::Clone + +#endif // !DACCESS_COMPILE diff --git a/src/utilcode/stgpooli.cpp b/src/utilcode/stgpooli.cpp new file mode 100644 index 0000000000..653289b1a1 --- /dev/null +++ b/src/utilcode/stgpooli.cpp @@ -0,0 +1,349 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// StgPool.cpp +// + +// +// Pools are used to reduce the amount of data actually required in the database. +// This allows for duplicate string and binary values to be folded into one +// copy shared by the rest of the database. Strings are tracked in a hash +// table when insert/changing data to find duplicates quickly. The strings +// are then persisted consecutively in a stream in the database format. +// +//***************************************************************************** +#include "stdafx.h" // Standard include. +#include <stgpool.h> // Our interface definitions. + +int CStringPoolHash::Cmp( + const void *pData, // A string. + void *pItem) // A hash item which refers to a string. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + LPCSTR p1 = reinterpret_cast<LPCSTR>(pData); + LPCSTR p2; + if (FAILED(m_Pool->GetString(reinterpret_cast<STRINGHASH*>(pItem)->iOffset, &p2))) + { + return -1; + } + return (strcmp(p1, p2)); +} // int CStringPoolHash::Cmp() + + +int CBlobPoolHash::Cmp( + const void *pData, // A blob. + void *pItem) // A hash item which refers to a blob. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + ULONG ul1; + ULONG ul2; + MetaData::DataBlob data2; + + // Get size of first item. + ul1 = CPackedLen::GetLength(pData); + // Adjust size to include the length of size field. + ul1 += CPackedLen::Size(ul1); + + // Get the second item. + if (FAILED(m_Pool->GetData(reinterpret_cast<BLOBHASH*>(pItem)->iOffset, &data2))) + { + return -1; + } + + // Get and adjust size of second item. + ul2 = CPackedLen::GetLength(data2.GetDataPointer()); + ul2 += CPackedLen::Size(ul2); + + if (ul1 < ul2) + return (-1); + else if (ul1 > ul2) + return (1); + return (memcmp(pData, data2.GetDataPointer(), ul1)); +} // int CBlobPoolHash::Cmp() + +int CGuidPoolHash::Cmp(const void *pData, void *pItem) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + GUID *p2; + if (FAILED(m_Pool->GetGuid(reinterpret_cast<GUIDHASH*>(pItem)->iIndex, &p2))) + { + return -1; + } + return (memcmp(pData, p2, sizeof(GUID))); +} // int CGuidPoolHash::Cmp() + +// +// +// CPackedLen +// +// + + +//***************************************************************************** +// Parse a length, return the data, store length. +//***************************************************************************** +void const *CPackedLen::GetData( // Pointer to data, or 0 on error. + void const *pData, // First byte of length. + ULONG *pLength) // Put length here, or -1 on error. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + BYTE const *pBytes = reinterpret_cast<BYTE const*>(pData); + + if ((*pBytes & 0x80) == 0x00) // 0??? ???? + { + *pLength = (*pBytes & 0x7f); + return pBytes + 1; + } + + if ((*pBytes & 0xC0) == 0x80) // 10?? ???? + { + *pLength = ((*pBytes & 0x3f) << 8 | *(pBytes+1)); + return pBytes + 2; + } + + if ((*pBytes & 0xE0) == 0xC0) // 110? ???? + { + *pLength = ((*pBytes & 0x1f) << 24 | *(pBytes+1) << 16 | *(pBytes+2) << 8 | *(pBytes+3)); + return pBytes + 4; + } + + *pLength = (ULONG) -1; + return 0; +} // void const *CPackedLen::GetData() + +#ifndef MAX_PTR +#define MAX_PTR ((BYTE*)(~(SSIZE_T)0)) +#endif + +//***************************************************************************** +// Parse a length, return the data, store length. +//***************************************************************************** +HRESULT CPackedLen::SafeGetLength( // S_OK, or error + void const *pDataSource, // First byte of length. + void const *pDataSourceEnd, // End of valid source data memory + ULONG *pLength, // Length of data, if return S_OK + void const **ppDataNext) // Pointer immediately following encoded length +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + if (pDataSource == NULL || + pDataSourceEnd == NULL || + pDataSourceEnd < pDataSource || + ppDataNext == NULL || + pLength == NULL || + pDataSource > (MAX_PTR - 4)) + { + return E_INVALIDARG; + } + + BYTE const *pBytes = reinterpret_cast<BYTE const*>(pDataSource); + BYTE const *pBytesEnd = reinterpret_cast<BYTE const*>(pDataSourceEnd); + + size_t cbAvail = pBytesEnd - pBytes; + + if (cbAvail < 1) + { // Fail if no source data available + return COR_E_OVERFLOW; + } + + if ((*pBytes & 0x80) == 0x00) // 0??? ???? + { + *pLength = (*pBytes & 0x7f); + *ppDataNext = pBytes + 1; + return S_OK; + } + + if (cbAvail < 2) + { // Fail if not enough source data available + return COR_E_OVERFLOW; + } + + if ((*pBytes & 0xC0) == 0x80) // 10?? ???? + { + *pLength = ((*pBytes & 0x3f) << 8 | *(pBytes+1)); + *ppDataNext = pBytes + 2; + return S_OK; + } + + if (cbAvail < 4) + { // Fail if not enough source data available + return COR_E_OVERFLOW; + } + + if ((*pBytes & 0xE0) == 0xC0) // 110? ???? + { + *pLength = ((*pBytes & 0x1f) << 24 | *(pBytes+1) << 16 | *(pBytes+2) << 8 | *(pBytes+3)); + *ppDataNext = pBytes + 4; + return S_OK;; + } + + return COR_E_OVERFLOW; +} // CPackedLen::GetLength + +//***************************************************************************** +// Parse a length, return the data, store length. +//***************************************************************************** +HRESULT CPackedLen::SafeGetData( // S_OK, or error + void const *pDataSource, // First byte of length. + void const *pDataSourceEnd, // End of valid source data memory + ULONG *pcbData, // Length of data + void const **ppData) // Start of data +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + HRESULT hr = S_OK; + + IfFailRet(SafeGetLength(pDataSource, pDataSourceEnd, pcbData, ppData)); + + if (*pcbData == 0) + { // Zero length value means zero data, so no range checking required. + return S_OK; + } + + BYTE const *pbData = reinterpret_cast<BYTE const*>(*ppData); + + if (pbData + *pcbData < pbData) + { // First check for integer overflow + return COR_E_OVERFLOW; + } + + if (pDataSourceEnd < pbData + *pcbData) + { // Now check for data buffer overflow + return COR_E_OVERFLOW; + } + + return S_OK; +} // CPackedLen::GetLength + +//***************************************************************************** +// Parse a length, return the data, store length. +//***************************************************************************** +HRESULT CPackedLen::SafeGetData( // S_OK, or error + void const *pDataSource, // First byte of data + ULONG cbDataSource, // Count of valid bytes in data source + ULONG *pcbData, // Length of data + void const **ppData) // Start of data +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + return SafeGetData(pDataSource, (void const *)((BYTE const *)pDataSource + cbDataSource), pcbData, ppData); +} // CPackedLen::GetLength + +//***************************************************************************** +// Parse a length, return the length, pointer to actual bytes. +//***************************************************************************** +ULONG CPackedLen::GetLength( // Length or -1 on error. + void const *pData, // First byte of length. + void const **ppCode) // Put pointer to bytes here, if not 0. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + BYTE const *pBytes = reinterpret_cast<BYTE const*>(pData); + + if ((*pBytes & 0x80) == 0x00) // 0??? ???? + { + if (ppCode) *ppCode = pBytes + 1; + return (*pBytes & 0x7f); + } + + if ((*pBytes & 0xC0) == 0x80) // 10?? ???? + { + if (ppCode) *ppCode = pBytes + 2; + return ((*pBytes & 0x3f) << 8 | *(pBytes+1)); + } + + if ((*pBytes & 0xE0) == 0xC0) // 110? ???? + { + if (ppCode) *ppCode = pBytes + 4; + return ((*pBytes & 0x1f) << 24 | *(pBytes+1) << 16 | *(pBytes+2) << 8 | *(pBytes+3)); + } + + return (ULONG) -1; +} // ULONG CPackedLen::GetLength() + +//***************************************************************************** +// Parse a length, return the length, size of the length. +//***************************************************************************** +ULONG CPackedLen::GetLength( // Length or -1 on error. + void const *pData, // First byte of length. + int *pSizeLen) // Put size of length here, if not 0. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + BYTE const *pBytes = reinterpret_cast<BYTE const*>(pData); + + if ((*pBytes & 0x80) == 0x00) // 0??? ???? + { + if (pSizeLen) *pSizeLen = 1; + return (*pBytes & 0x7f); + } + + if ((*pBytes & 0xC0) == 0x80) // 10?? ???? + { + if (pSizeLen) *pSizeLen = 2; + return ((*pBytes & 0x3f) << 8 | *(pBytes+1)); + } + + if ((*pBytes & 0xE0) == 0xC0) // 110? ???? + { + if (pSizeLen) *pSizeLen = 4; + return ((*pBytes & 0x1f) << 24 | *(pBytes+1) << 16 | *(pBytes+2) << 8 | *(pBytes+3)); + } + + return (ULONG) -1; +} // ULONG CPackedLen::GetLength() + +//***************************************************************************** +// Encode a length. +//***************************************************************************** +#ifdef _MSC_VER +#pragma warning(disable:4244) // conversion from unsigned long to unsigned char +#endif +void* CPackedLen::PutLength( // First byte past length. + void *pData, // Pack the length here. + ULONG iLen) // The length. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + BYTE *pBytes = reinterpret_cast<BYTE*>(pData); + + if (iLen <= 0x7F) + { + *pBytes = iLen; + return pBytes + 1; + } + + if (iLen <= 0x3FFF) + { + *pBytes = (iLen >> 8) | 0x80; + *(pBytes+1) = iLen & 0xFF; + return pBytes + 2; + } + + _ASSERTE(iLen <= 0x1FFFFFFF); + *pBytes = (iLen >> 24) | 0xC0; + *(pBytes+1) = (iLen >> 16) & 0xFF; + *(pBytes+2) = (iLen >> 8) & 0xFF; + *(pBytes+3) = iLen & 0xFF; + return pBytes + 4; +} // void* CPackedLen::PutLength() +#ifdef _MSC_VER +#pragma warning(default:4244) // conversion from unsigned long to unsigned char +#endif + diff --git a/src/utilcode/stgpoolreadonly.cpp b/src/utilcode/stgpoolreadonly.cpp new file mode 100644 index 0000000000..abd27b884d --- /dev/null +++ b/src/utilcode/stgpoolreadonly.cpp @@ -0,0 +1,212 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// StgPoolReadOnly.cpp +// + +// +// Read only pools are used to reduce the amount of data actually required in the database. +// +//***************************************************************************** +#include "stdafx.h" // Standard include. +#include <stgpool.h> // Our interface definitions. +#include "metadatatracker.h" +// +// +// StgPoolReadOnly +// +// + +#if METADATATRACKER_ENABLED +MetaDataTracker *MetaDataTracker::m_MDTrackers = NULL; +BOOL MetaDataTracker::s_bEnabled = FALSE; + +void (*MetaDataTracker::s_IBCLogMetaDataAccess)(const void *addr) = NULL; +void (*MetaDataTracker::s_IBCLogMetaDataSearch)(const void *result) = NULL; + +#endif // METADATATRACKER_ENABLED + +const BYTE StgPoolSeg::m_zeros[64] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + + +//***************************************************************************** +// Free any memory we allocated. +//***************************************************************************** +StgPoolReadOnly::~StgPoolReadOnly() +{ + LIMITED_METHOD_CONTRACT; +} + + +//***************************************************************************** +// Init the pool from existing data. +//***************************************************************************** +HRESULT StgPoolReadOnly::InitOnMemReadOnly(// Return code. + void *pData, // Predefined data. + ULONG iSize) // Size of data. +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return E_OUTOFMEMORY); + } + CONTRACTL_END + + // Make sure we aren't stomping anything and are properly initialized. + _ASSERTE(m_pSegData == m_zeros); + + // Create case requires no further action. + if (pData == NULL) + return E_INVALIDARG; + + // Keep m_zeros data pointer if there's no content of the pool + if (iSize != 0) + { + m_pSegData = reinterpret_cast<BYTE*>(pData); + } + m_cbSegSize = iSize; + m_cbSegNext = iSize; + return S_OK; +} + +//***************************************************************************** +// Prepare to shut down or reinitialize. +//***************************************************************************** +void StgPoolReadOnly::Uninit() +{ + LIMITED_METHOD_CONTRACT; + + m_pSegData = (BYTE*)m_zeros; + m_pNextSeg = 0; +} + + +//***************************************************************************** +// Convert a string to UNICODE into the caller's buffer. +//***************************************************************************** +HRESULT StgPoolReadOnly::GetStringW( // Return code. + ULONG iOffset, // Offset of string in pool. + __out_ecount(cchBuffer) LPWSTR szOut, // Output buffer for string. + int cchBuffer) // Size of output buffer. +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; + + HRESULT hr; + LPCSTR pString; // The string in UTF8. + int iChars; + + IfFailRet(GetString(iOffset, &pString)); + iChars = ::WszMultiByteToWideChar(CP_UTF8, 0, pString, -1, szOut, cchBuffer); + if (iChars == 0) + return (BadError(HRESULT_FROM_NT(GetLastError()))); + return S_OK; +} + +//***************************************************************************** +// Return a pointer to a null terminated blob given an offset previously +// handed out by Addblob or Findblob. +//***************************************************************************** +HRESULT +StgPoolReadOnly::GetBlob( + UINT32 nOffset, // Offset of blob in pool. + MetaData::DataBlob *pData) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + HRESULT hr; + UINT32 cbBlobContentSize; + + // This should not be a necessary special case. The zero byte at the + // start of the pool will code for a length of zero. We will return + // a pointer to the next length byte, but the caller should notice that + // the size is zero, and should not look at any bytes. + // [SL] Yes, but we don't need all further computations and checks if iOffset==0 + + if (nOffset == 0) + { + pData->Clear(); + return S_OK; + } + + // Is the offset within this heap? + if (!IsValidOffset(nOffset)) + { + Debug_ReportError("Invalid blob offset."); + IfFailGo(CLDB_E_INDEX_NOTFOUND); + } + + IfFailGo(GetDataReadOnly(nOffset, pData)); + if (!pData->GetCompressedU(&cbBlobContentSize)) + { + Debug_ReportError("Invalid blob - size compression."); + IfFailGo(COR_E_BADIMAGEFORMAT); + } + if (!pData->TruncateToExactSize(cbBlobContentSize)) + { + Debug_ReportError("Invalid blob - reaches behind the end of data block."); + IfFailGo(COR_E_BADIMAGEFORMAT); + } + + return S_OK; +ErrExit: + pData->Clear(); + return hr; +} // StgPoolReadOnly::GetBlob + +//***************************************************************************** +// code:StgPoolReadOnly::GetBlob specialization with inlined check for valid offsets to avoid redundant code:StgPoolReadOnly::GetDataReadOnly calls. +// code:StgPoolReadOnly::GetDataReadOnly is not cheap because of it performs binary lookup in hot metadata. +//***************************************************************************** +HRESULT +StgBlobPoolReadOnly::GetBlob( + UINT32 nOffset, // Offset of blob in pool. + MetaData::DataBlob *pData) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + HRESULT hr; + UINT32 cbBlobContentSize; + + // This should not be a necessary special case. The zero byte at the + // start of the pool will code for a length of zero. We will return + // a pointer to the next length byte, but the caller should notice that + // the size is zero, and should not look at any bytes. + // [SL] Yes, but we don't need all further computations and checks if iOffset==0 + + if (nOffset == 0) + { + pData->Clear(); + return S_OK; + } + + if (m_pSegData == m_zeros) + { + Debug_ReportError("Invalid blob offset."); + IfFailGo(CLDB_E_INDEX_NOTFOUND); + } + + IfFailGo(GetDataReadOnly(nOffset, pData)); + if (!pData->GetCompressedU(&cbBlobContentSize)) + { + Debug_ReportError("Invalid blob - size compression."); + IfFailGo(CLDB_E_INDEX_NOTFOUND); + } + if (!pData->TruncateToExactSize(cbBlobContentSize)) + { + Debug_ReportError("Invalid blob - reaches behind the end of data block."); + IfFailGo(CLDB_E_INDEX_NOTFOUND); + } + + return S_OK; +ErrExit: + pData->Clear(); + return hr; +} // StgBlobPoolReadOnly::GetBlob diff --git a/src/utilcode/stresslog.cpp b/src/utilcode/stresslog.cpp new file mode 100644 index 0000000000..3e1a928c03 --- /dev/null +++ b/src/utilcode/stresslog.cpp @@ -0,0 +1,720 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +/*************************************************************************************/ +/* StressLog.cpp */ +/*************************************************************************************/ + +/*************************************************************************************/ + +#include "stdafx.h" // precompiled headers + +#include "switches.h" +#include "stresslog.h" +#include "clrhost.h" +#define DONOT_DEFINE_ETW_CALLBACK +#include "eventtracebase.h" +#include "ex.h" + + #if !defined(STRESS_LOG_READONLY) +HANDLE StressLogChunk::s_LogChunkHeap = NULL; +#endif // !STRESS_LOG_READONLY + +/*********************************************************************************/ +#if defined(_TARGET_X86_) + +/* This is like QueryPerformanceCounter but a lot faster. On machines with + variable-speed CPUs (for power management), this is not accurate, but may + be good enough. +*/ +__forceinline __declspec(naked) unsigned __int64 getTimeStamp() { + STATIC_CONTRACT_LEAF; + + __asm { + RDTSC // read time stamp counter + ret + }; +} + +#else // _TARGET_X86_ +unsigned __int64 getTimeStamp() { + STATIC_CONTRACT_LEAF; + + LARGE_INTEGER ret; + ZeroMemory(&ret, sizeof(LARGE_INTEGER)); + + QueryPerformanceCounter(&ret); + + return ret.QuadPart; +} + +#endif // _TARGET_X86_ + +#if defined(_TARGET_X86_) + +/*********************************************************************************/ +/* Get the the frequency cooresponding to 'getTimeStamp'. For x86, this is the + frequency of the RDTSC instruction, which is just the clock rate of the CPU. + This can vary due to power management, so this is at best a rough approximation. +*/ +unsigned __int64 getTickFrequency() +{ + // + // At startup, the OS calculates the CPU clock frequency and makes it available + // at HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor\0 + // + + unsigned __int64 hz = 0; + + HKEY hKey; + if (ERROR_SUCCESS == RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + W("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"), + 0, + KEY_QUERY_VALUE, + &hKey)) + { + DWORD mhz; + DWORD mhzType; + DWORD cbMhz = (DWORD)sizeof(mhz); + if (ERROR_SUCCESS == RegQueryValueExW( + hKey, + W("~MHz"), + NULL, + &mhzType, + (LPBYTE)&mhz, + &cbMhz)) + { + _ASSERTE(REG_DWORD == mhzType); + _ASSERTE((DWORD)sizeof(mhz) == cbMhz); + + hz = (unsigned __int64)mhz * 1000000; + } + + RegCloseKey(hKey); + } + + return hz; +} + +#else // _TARGET_X86_ + + +/*********************************************************************************/ +/* Get the the frequency cooresponding to 'getTimeStamp'. For non-x86 + architectures, this is just the performance counter frequency. +*/ +unsigned __int64 getTickFrequency() +{ + LARGE_INTEGER ret; + ZeroMemory(&ret, sizeof(LARGE_INTEGER)); + QueryPerformanceFrequency(&ret); + return ret.QuadPart; +} + +#endif // _TARGET_X86_ + +#ifdef STRESS_LOG + +StressLog StressLog::theLog = { 0, 0, 0, 0, 0, 0, TLS_OUT_OF_INDEXES, 0, 0, 0 }; +const static unsigned __int64 RECYCLE_AGE = 0x40000000L; // after a billion cycles, we can discard old threads + +/*********************************************************************************/ +void StressLog::Enter(CRITSEC_COOKIE) { + STATIC_CONTRACT_LEAF; + + IncCantAllocCount(); + ClrEnterCriticalSection(theLog.lock); + DecCantAllocCount(); +} + +void StressLog::Leave(CRITSEC_COOKIE) { + STATIC_CONTRACT_LEAF; + + IncCantAllocCount(); + ClrLeaveCriticalSection(theLog.lock); + DecCantAllocCount(); +} + +/*********************************************************************************/ +void StressLog::Initialize(unsigned facilities, unsigned level, unsigned maxBytesPerThread, + unsigned maxBytesTotal, HMODULE hMod) +{ + STATIC_CONTRACT_LEAF; + + if (theLog.MaxSizePerThread != 0) + { + // guard ourself against multiple initialization. First init wins. + return; + } + + _ASSERTE (theLog.TLSslot == (unsigned int)TLS_OUT_OF_INDEXES); + theLog.lock = ClrCreateCriticalSection(CrstStressLog,(CrstFlags)(CRST_UNSAFE_ANYMODE|CRST_DEBUGGER_THREAD)); + // StressLog::Terminate is going to free memory. + if (maxBytesPerThread < STRESSLOG_CHUNK_SIZE) + { + maxBytesPerThread = STRESSLOG_CHUNK_SIZE; + } + theLog.MaxSizePerThread = maxBytesPerThread; + + if (maxBytesTotal < STRESSLOG_CHUNK_SIZE * 256) + { + maxBytesTotal = STRESSLOG_CHUNK_SIZE * 256; + } + theLog.MaxSizeTotal = maxBytesTotal; + theLog.totalChunk = 0; + theLog.facilitiesToLog = facilities | LF_ALWAYS; + theLog.levelToLog = level; + theLog.deadCount = 0; + theLog.TLSslot = TlsIdx_StressLog; + + theLog.tickFrequency = getTickFrequency(); + + GetSystemTimeAsFileTime (&theLog.startTime); + theLog.startTimeStamp = getTimeStamp(); + +#ifndef FEATURE_PAL + theLog.moduleOffset = (SIZE_T)hMod; // HMODULES are base addresses. + +#ifdef _DEBUG + HMODULE hModNtdll = GetModuleHandleA("ntdll.dll"); + theLog.RtlCaptureStackBackTrace = reinterpret_cast<PFNRtlCaptureStackBackTrace>( + GetProcAddress(hModNtdll, "RtlCaptureStackBackTrace")); +#endif // _DEBUG + +#else // !FEATURE_PAL + theLog.moduleOffset = (SIZE_T)PAL_GetCoreClrModuleBase(); +#endif // !FEATURE_PAL + +#if !defined (STRESS_LOG_READONLY) + StressLogChunk::s_LogChunkHeap = ClrHeapCreate (0, STRESSLOG_CHUNK_SIZE * 128, 0); + if (StressLogChunk::s_LogChunkHeap == NULL) + { + StressLogChunk::s_LogChunkHeap = ClrGetProcessHeap (); + } + _ASSERTE (StressLogChunk::s_LogChunkHeap); +#endif //!STRESS_LOG_READONLY +} + +/*********************************************************************************/ +void StressLog::Terminate(BOOL fProcessDetach) { + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + if (theLog.TLSslot != (unsigned int)TLS_OUT_OF_INDEXES) { + theLog.facilitiesToLog = 0; + + StressLogLockHolder lockh(theLog.lock, FALSE); + if (!fProcessDetach) { + lockh.Acquire(); lockh.Release(); // The Enter() Leave() forces a memory barrier on weak memory model systems + // we want all the other threads to notice that facilitiesToLog is now zero + + // This is not strictly threadsafe, since there is no way of insuring when all the + // threads are out of logMsg. In practice, since they can no longer enter logMsg + // and there are no blocking operations in logMsg, simply sleeping will insure + // that everyone gets out. + ClrSleepEx(2, FALSE); + lockh.Acquire(); + } + + // Free the log memory + ThreadStressLog* ptr = theLog.logs; + theLog.logs = 0; + while(ptr != 0) { + ThreadStressLog* tmp = ptr; + ptr = ptr->next; + delete tmp; + } + + theLog.TLSslot = TLS_OUT_OF_INDEXES; + if (!fProcessDetach) { + lockh.Release(); + } + } +#if !defined (STRESS_LOG_READONLY) + if (StressLogChunk::s_LogChunkHeap != NULL && StressLogChunk::s_LogChunkHeap != ClrGetProcessHeap ()) + { + ClrHeapDestroy (StressLogChunk::s_LogChunkHeap); + } +#endif //!STRESS_LOG_READONLY +} + +/*********************************************************************************/ +/* create a new thread stress log buffer associated with Thread local slot TLSslot, for the Stress log */ + +ThreadStressLog* StressLog::CreateThreadStressLog() { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + SO_TOLERANT; + } + CONTRACTL_END; + + static PVOID callerID = NULL; + + ThreadStressLog* msgs = (ThreadStressLog*) ClrFlsGetValue(theLog.TLSslot); + if (msgs != NULL) + { + return msgs; + } + + if (callerID == ClrTeb::GetFiberPtrId()) + { + return NULL; + } + + //if we are not allowed to allocate stress log, we should not even try to take the lock + if (!StressLogChunk::s_LogChunkHeap || !CanThisThreadCallIntoHost() || IsInCantAllocStressLogRegion ()) + { + return NULL; + } + + // if it looks like we won't be allowed to allocate a new chunk, exit early + if (theLog.deadCount == 0 && !AllowNewChunk (0)) + { + return NULL; + } + + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return NULL); + StressLogLockHolder lockh(theLog.lock, FALSE); + + class NestedCaller + { + public: + NestedCaller() + { + } + ~NestedCaller() + { + callerID = NULL; + } + void Mark() + { + callerID = ClrTeb::GetFiberPtrId(); + } + }; + + NestedCaller nested; + + BOOL noFLSNow = FALSE; + + PAL_CPP_TRY + { + // Acquiring the lack can throw an OOM exception the first time its called on a thread. We go + // ahead and try to provoke that now, before we've altered the list of available stress logs, and bail if + // we fail. + lockh.Acquire(); + nested.Mark(); + + // ClrFlsSetValue can throw an OOM exception the first time its called on a thread for a given slot. We go + // ahead and try to provoke that now, before we've altered the list of available stress logs, and bail if + // we fail. + ClrFlsSetValue(theLog.TLSslot, NULL); + } + PAL_CPP_CATCH_DERIVED(OutOfMemoryException, obj) + { + // Just leave on any exception. Note: can't goto or return from within EX_CATCH... + noFLSNow = TRUE; + } + PAL_CPP_ENDTRY; + + if (noFLSNow == FALSE && theLog.facilitiesToLog != 0) + msgs = CreateThreadStressLogHelper(); + + END_SO_INTOLERANT_CODE; + + return msgs; +} + +ThreadStressLog* StressLog::CreateThreadStressLogHelper() { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + SO_INTOLERANT; + CANNOT_TAKE_LOCK; + } + CONTRACTL_END; + + _ASSERTE(theLog.TLSslot != (unsigned int)TLS_OUT_OF_INDEXES); // because facilitiesToLog is != 0 + + + BOOL skipInsert = FALSE; + ThreadStressLog* msgs = NULL; + + // See if we can recycle a dead thread + if (theLog.deadCount > 0) + { + unsigned __int64 recycleStamp = getTimeStamp() - RECYCLE_AGE; + msgs = theLog.logs; + //find out oldest dead ThreadStressLog in case we can't find one within + //recycle age but can't create a new chunk + ThreadStressLog * oldestDeadMsg = NULL; + + while(msgs != 0) + { + if (msgs->isDead) + { + BOOL hasTimeStamp = msgs->curPtr != (StressMsg *)msgs->chunkListTail->EndPtr(); + if (hasTimeStamp && msgs->curPtr->timeStamp < recycleStamp) + { + skipInsert = TRUE; + InterlockedDecrement(&theLog.deadCount); + break; + } + + if (!oldestDeadMsg) + { + oldestDeadMsg = msgs; + } + else if (hasTimeStamp && oldestDeadMsg->curPtr->timeStamp > msgs->curPtr->timeStamp) + { + oldestDeadMsg = msgs; + } + } + + msgs = msgs->next; + } + + //if the total stress log size limit is already passed and we can't add new chunk, + //always reuse the oldest dead msg + if (!AllowNewChunk (0) && !msgs) + { + msgs = oldestDeadMsg; + skipInsert = TRUE; + InterlockedDecrement(&theLog.deadCount); + } + } + + if (msgs == 0) { + FAULT_NOT_FATAL(); // We don't mind if we can't allocate here, we'll try again later. + if (IsInCantAllocStressLogRegion ()) + { + goto LEAVE; + } + + msgs = new (nothrow) ThreadStressLog; + + if (msgs == 0 ||!msgs->IsValid ()) + { + delete msgs; + msgs = 0; + goto LEAVE; + } + } + + msgs->Activate (); + + // We know this isn't going to throw an exception now because the call to ClrFlsSetValue above succeeded for + // this thread. + { + CONTRACT_VIOLATION(ThrowsViolation); + ClrFlsSetValue(theLog.TLSslot, msgs); + } + + if (!skipInsert) { +#ifdef _DEBUG + ThreadStressLog* walk = theLog.logs; + while (walk) + { + _ASSERTE (walk != msgs); + walk = walk->next; + } +#endif + // Put it into the stress log + msgs->next = theLog.logs; + theLog.logs = msgs; + } + +LEAVE: + ; + return msgs; +} + +/*********************************************************************************/ +/* static */ +void StressLog::ThreadDetach(ThreadStressLog *msgs) { + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_CANNOT_TAKE_LOCK; + +#ifndef DACCESS_COMPILE + if (msgs == 0) + { + return; + } + + _ASSERTE(theLog.TLSslot != (unsigned int)TLS_OUT_OF_INDEXES); // because facilitiesToLog is != 0 + // We are deleting a fiber. The thread is running a different fiber now. + // We should write this message to the StressLog for deleted fiber. + msgs->LogMsg (LF_STARTUP, 0, "******* DllMain THREAD_DETACH called Thread dying *******\n"); +#endif + + msgs->isDead = TRUE; + InterlockedIncrement(&theLog.deadCount); +} + +BOOL StressLog::AllowNewChunk (LONG numChunksInCurThread) +{ + _ASSERTE (numChunksInCurThread <= theLog.totalChunk); + DWORD perThreadLimit = theLog.MaxSizePerThread; + +#ifndef DACCESS_COMPILE + if (numChunksInCurThread == 0 && IsSuspendEEThread()) + return TRUE; + + if (IsGCSpecialThread()) + { + perThreadLimit *= GC_STRESSLOG_MULTIPLY; + } +#endif + + if ((DWORD)numChunksInCurThread * STRESSLOG_CHUNK_SIZE >= perThreadLimit) + { + return FALSE; + } + + return (DWORD)theLog.totalChunk * STRESSLOG_CHUNK_SIZE < theLog.MaxSizeTotal; +} + +BOOL StressLog::ReserveStressLogChunks (unsigned chunksToReserve) +{ + ThreadStressLog* msgs = (ThreadStressLog*) ClrFlsGetValue(theLog.TLSslot); + + if (msgs == 0) + { + msgs = CreateThreadStressLog(); + + if (msgs == 0) + return FALSE; + } + + if (chunksToReserve == 0) + { + chunksToReserve = (theLog.MaxSizePerThread + STRESSLOG_CHUNK_SIZE - 1) / STRESSLOG_CHUNK_SIZE; + } + + LONG numTries = (LONG)chunksToReserve - msgs->chunkListLength; + for (LONG i = 0; i < numTries; i++) + { + msgs->GrowChunkList (); + } + + return msgs->chunkListLength >= (LONG)chunksToReserve; +} + +void (*FSwitchToSOTolerant)(); +void (*FSwitchToSOIntolerant)(); +void TrackSO(BOOL tolerance) +{ + if (tolerance) + { + if (FSwitchToSOTolerant) + { + FSwitchToSOTolerant(); + } + } + else + { + if (FSwitchToSOIntolerant) + { + FSwitchToSOIntolerant(); + } + } +} + +/*********************************************************************************/ +/* fetch a buffer that can be used to write a stress message, it is thread safe */ +void ThreadStressLog::LogMsg ( DWORD_PTR facility, int cArgs, const char* format, va_list Args) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_SO_TOLERANT; + + // Asserts in this function cause infinite loops in the asserting mechanism. + // Just use debug breaks instead. + +#ifndef DACCESS_COMPILE +#ifdef _DEBUG + // _ASSERTE ( cArgs >= 0 && cArgs <= 7 ); + if (cArgs < 0 || cArgs > 7) DebugBreak(); +#endif // + + size_t offs = ((size_t)format - StressLog::theLog.moduleOffset); + + // _ASSERTE ( offs < StressMsg::maxOffset ); + if (offs >= StressMsg::maxOffset) + { +#ifdef _DEBUG + DebugBreak(); // in lieu of the above _ASSERTE +#endif // _DEBUG + + // Set it to this string instead. + offs = +#ifdef _DEBUG + (size_t)"<BUG: StressLog format string beyond maxOffset>"; +#else // _DEBUG + 0; // a 0 offset is ignored by StressLog::Dump +#endif // _DEBUG else + } + + // Get next available slot + StressMsg* msg = AdvanceWrite(cArgs); + + msg->timeStamp = getTimeStamp(); + msg->facility = facility; + msg->formatOffset = offs; + msg->numberOfArgs = cArgs; + + for ( int i = 0; i < cArgs; ++i ) + { + void* data = va_arg(Args, void*); + msg->args[i] = data; + } + +#ifdef _DEBUG + if (!IsValid () || threadId != GetCurrentThreadId ()) + DebugBreak(); +#endif // _DEBUG +#endif //DACCESS_COMPILE +} + +FORCEINLINE BOOL StressLog::InlinedStressLogOn(unsigned facility, unsigned level) +{ + STATIC_CONTRACT_LEAF; + STATIC_CONTRACT_SUPPORTS_DAC; + +#if defined(DACCESS_COMPILE) + return FALSE; +#else + return ((theLog.facilitiesToLog & facility) && (level <= theLog.levelToLog)); +#endif +} + +BOOL StressLog::StressLogOn(unsigned facility, unsigned level) +{ + STATIC_CONTRACT_LEAF; + STATIC_CONTRACT_SUPPORTS_DAC; + + return InlinedStressLogOn(facility, level); +} + +FORCEINLINE BOOL StressLog::InlinedETWLogOn(unsigned facility, unsigned level) +{ + STATIC_CONTRACT_LEAF; + STATIC_CONTRACT_SUPPORTS_DAC; + +#if defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_CORECLR) && !defined(DACCESS_COMPILE) + return ((Microsoft_Windows_DotNETRuntimeStressHandle != 0) && + (ETW_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_Context, (UCHAR)level, facility) != 0)); +#else + return FALSE; +#endif +} + +BOOL StressLog::ETWLogOn(unsigned facility, unsigned level) +{ + STATIC_CONTRACT_LEAF; + STATIC_CONTRACT_SUPPORTS_DAC; + + return InlinedETWLogOn(facility, level); +} + +#if !defined(DACCESS_COMPILE) +BOOL StressLog::LogOn(unsigned facility, unsigned level) +{ + STATIC_CONTRACT_LEAF; + STATIC_CONTRACT_SUPPORTS_DAC; + + return InlinedStressLogOn(facility, level) || InlinedETWLogOn(facility, level); +} +#endif + +/* static */ +void StressLog::LogMsg (unsigned level, unsigned facility, int cArgs, const char* format, ... ) +{ + STATIC_CONTRACT_SUPPORTS_DAC; +#ifndef DACCESS_COMPILE + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_SO_TOLERANT; + STATIC_CONTRACT_SUPPORTS_DAC; + + // Any stresslog LogMsg could theoretically create a new stress log and thus + // enter a critical section. But we don't want these to cause violations in + // CANNOT_TAKE_LOCK callers, since the callers would otherwise be fine in runs that don't + // set the stress log config parameter. + CONTRACT_VIOLATION(TakesLockViolation); + + _ASSERTE ( cArgs >= 0 && cArgs <= 7 ); + + va_list Args; + va_start(Args, format); + + if(InlinedStressLogOn(facility, level)) + { + ThreadStressLog* msgs = (ThreadStressLog*) ClrFlsGetValue(theLog.TLSslot); + + if (msgs == 0) { + msgs = CreateThreadStressLog(); + + if (msgs == 0) + return; + } + msgs->LogMsg (facility, cArgs, format, Args); + } + +// Stress Log ETW feature available only on the desktop versions of the runtime +#if !defined(FEATURE_CORECLR) + if(InlinedETWLogOn(facility, level)) + { +#define MAX_STRESSLOG_DATA_ETW_LENGTH 256 + CHAR logMessage[MAX_STRESSLOG_DATA_ETW_LENGTH]; + + ULONG messageLength = (USHORT)_vsnprintf_s(logMessage, COUNTOF(logMessage), MAX_STRESSLOG_DATA_ETW_LENGTH-1, format, Args); + + if(messageLength >= 0 && + messageLength < MAX_STRESSLOG_DATA_ETW_LENGTH) // this condition has been added to make prefast happy + { + logMessage[messageLength] = 0; + } + messageLength++; + logMessage[MAX_STRESSLOG_DATA_ETW_LENGTH-1] = 0; + FireEtwStressLogEvent_V1((UINT32)facility, (UCHAR)level, logMessage, GetClrInstanceId()); +#undef MAX_STRESSLOG_DATA_ETW_LENGTH + } +#endif // !FEATURE_CORECLR + + va_end(Args); +#endif //!DACCESS_COMPILE +} + +#ifdef _DEBUG +/* static */ +void StressLog::LogCallStack(const char *const callTag){ + if (theLog.RtlCaptureStackBackTrace) + { + size_t CallStackTrace[MAX_CALL_STACK_TRACE]; + ULONG hash; + USHORT stackTraceCount = theLog.RtlCaptureStackBackTrace (2, MAX_CALL_STACK_TRACE, (PVOID *)CallStackTrace, &hash); + if (stackTraceCount > MAX_CALL_STACK_TRACE) + stackTraceCount = MAX_CALL_STACK_TRACE; + LogMsgOL("Start of %s stack \n", callTag); + USHORT i = 0; + for (;i < stackTraceCount; i++) + { + LogMsgOL("(%s stack)%pK\n", callTag, CallStackTrace[i]); + } + LogMsgOL("End of %s stack\n", callTag); + } +} +#endif //_DEBUG + +#endif // STRESS_LOG + diff --git a/src/utilcode/sxshelpers.cpp b/src/utilcode/sxshelpers.cpp new file mode 100644 index 0000000000..4d2734d7e9 --- /dev/null +++ b/src/utilcode/sxshelpers.cpp @@ -0,0 +1,1509 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// +// sxshelpers.cpp +// +// Some helping classes and methods for SxS in mscoree and mscorwks +// + +//***************************************************************************** + +#include "stdafx.h" +#include "utilcode.h" +#include "sxshelpers.h" + +#define SXS_GUID_INFORMATION_CLR_FLAG_IS_SURROGATE (0x00000001) +#define SXS_GUID_INFORMATION_CLR_FLAG_IS_CLASS (0x00000002) + +typedef struct _SXS_GUID_INFORMATION_CLR +{ + DWORD cbSize; + DWORD dwFlags; + PCWSTR pcwszRuntimeVersion; + PCWSTR pcwszTypeName; + PCWSTR pcwszAssemblyIdentity; +} SXS_GUID_INFORMATION_CLR, *PSXS_GUID_INFORMATION_CLR; +typedef const SXS_GUID_INFORMATION_CLR *PCSXS_GUID_INFORMATION_CLR; + +#define SXS_LOOKUP_CLR_GUID_USE_ACTCTX (0x00000001) +#define SXS_LOOKUP_CLR_GUID_FIND_SURROGATE (0x00010000) +#define SXS_LOOKUP_CLR_GUID_FIND_CLR_CLASS (0x00020000) +#define SXS_LOOKUP_CLR_GUID_FIND_ANY (SXS_LOOKUP_CLR_GUID_FIND_CLR_CLASS | SXS_LOOKUP_CLR_GUID_FIND_SURROGATE) + +#define SXS_DLL_NAME_W (W("sxs.dll")) +#define SXS_LOOKUP_CLR_GUID ("SxsLookupClrGuid") + +typedef BOOL (WINAPI* PFN_SXS_LOOKUP_CLR_GUID)( + IN DWORD dwFlags, + IN LPGUID pClsid, + IN HANDLE hActCtx, + IN OUT PVOID pvOutputBuffer, + IN SIZE_T cbOutputBuffer, + OUT PSIZE_T pcbOutputBuffer + ); + +// forward declaration +BOOL TranslateWin32AssemblyIdentityToFusionDisplayName(__deref_out_z LPWSTR *ppwzFusionDisplayName, PCWSTR lpWin32AssemblyIdentity); + +// The initial size of the buffer passed to SxsLookupClrGuid. +#define INIT_GUID_LOOKUP_BUFFER_SIZE 512 + +// Function pointer to the function to lookup a CLR type by GUID in the unmanaged +// fusion activation context. +PFN_SXS_LOOKUP_CLR_GUID g_pfnLookupGuid = NULL; +Volatile<BOOL> g_fSxSInfoInitialized = FALSE; + +HMODULE g_hmSxsDll = NULL; + +// And Here are the functions for getting shim info from +// Win32 activation context + +// FindShimInfoFromWin32 +// +// This method is used in ComInterop. If a COM client calls +// CoCreateInstance on a managed COM server, we will use this method +// trying to find required info of the managed COM server from Win32 subsystem. +// If this fails, we will fall back to query the registry. +// +// Parameters: +// rclsid: [in] The CLSID of the managed COM server +// bLoadRecord: [in] Set to TRUE if we are looking for a record +// *ppwzRuntimeVersion: [out] Runtime version +// *ppwzClassName: [out] Class name +// *ppwzAssemblyString: [out] Assembly display name +// *pfRegFreePIA: [out] TRUE if the entry is <clrSurrogate> +// Return: +// FAILED(hr) if cannot find shim info from Win32 +// SUCCEEDED(HR) if shim info is found from Win32 + +HRESULT +FindShimInfoFromWin32( + REFCLSID rClsid, + BOOL bLoadRecord, + __deref_out_z __deref_opt_out_opt LPWSTR *ppwszRuntimeVersion, + __deref_out_z __deref_opt_out_opt LPWSTR *ppwszSupportedRuntimeVersions, + __deref_out_z __deref_opt_out_opt LPWSTR *ppwszClassName, + __deref_out_z __deref_opt_out_opt LPWSTR *ppwszAssemblyString, + BOOL *pfRegFreePIA + ) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; + + CQuickBytes rDataBuffer; + SIZE_T cbWritten; + HRESULT hr = S_OK; + PCSXS_GUID_INFORMATION_CLR pFoundInfo = NULL; + SIZE_T cch; + GUID MyGuid = rClsid; + DWORD dwFlags = bLoadRecord ? SXS_LOOKUP_CLR_GUID_FIND_SURROGATE : SXS_LOOKUP_CLR_GUID_FIND_ANY; + + if (!ppwszRuntimeVersion && !ppwszClassName && !ppwszAssemblyString) + IfFailGo(E_INVALIDARG); + + if (ppwszRuntimeVersion) + *ppwszRuntimeVersion = NULL; + + if (ppwszSupportedRuntimeVersions) + *ppwszSupportedRuntimeVersions = NULL; + + if (ppwszClassName) + *ppwszClassName = NULL; + + if (ppwszAssemblyString) + *ppwszAssemblyString = NULL; + + if (pfRegFreePIA) + *pfRegFreePIA = FALSE; + + // If we haven't initialized the SxS info yet, then do so now. + if (!g_fSxSInfoInitialized) + { + if (g_hmSxsDll == NULL) + g_hmSxsDll = WszLoadLibrary(SXS_DLL_NAME_W); + + if (g_hmSxsDll != NULL) + { + // Lookup the SxsLookupClrGuid function in the SxS DLL. + g_pfnLookupGuid = (PFN_SXS_LOOKUP_CLR_GUID)GetProcAddress(g_hmSxsDll, SXS_LOOKUP_CLR_GUID); + } + + // The SxS info has been initialized. + g_fSxSInfoInitialized = TRUE; + } + + // If we don't have the proc address of SxsLookupClrGuid, then return a failure. + if (g_pfnLookupGuid == NULL) + IfFailGo(E_FAIL); + + // Resize the CQuickBytes to the initial buffer size. + IfFailGo(rDataBuffer.ReSizeNoThrow(INIT_GUID_LOOKUP_BUFFER_SIZE)); + + if (!g_pfnLookupGuid(dwFlags, &MyGuid, INVALID_HANDLE_VALUE, rDataBuffer.Ptr(), rDataBuffer.Size(), &cbWritten)) + { + const DWORD dwLastError = ::GetLastError(); + + // Failed b/c we need more space? Expand and try again. + if (dwLastError == ERROR_INSUFFICIENT_BUFFER) + { + IfFailGo(rDataBuffer.ReSizeNoThrow(cbWritten)); + + // Still failed even with enough space? Bummer. + if (!g_pfnLookupGuid(dwFlags, &MyGuid, INVALID_HANDLE_VALUE, rDataBuffer.Ptr(), rDataBuffer.Size(), &cbWritten)) + IfFailGo(E_FAIL); + } + // All other failures are real failures - probably the section isn't present + // or some other problem. + else + { + IfFailGo(E_FAIL); + } + } + + pFoundInfo = (PCSXS_GUID_INFORMATION_CLR)rDataBuffer.Ptr(); + + if (pFoundInfo->dwFlags == SXS_GUID_INFORMATION_CLR_FLAG_IS_SURROGATE && ppwszRuntimeVersion) + { + // Surrogate does not have runtime version information !!! + IfFailGo(E_FAIL); + } + + // + // This is special - translate the win32 assembly name into a managed + // assembly identity. + // + if (ppwszAssemblyString && pFoundInfo->pcwszAssemblyIdentity) + { + if (!TranslateWin32AssemblyIdentityToFusionDisplayName(ppwszAssemblyString, pFoundInfo->pcwszAssemblyIdentity)) + IfFailGo(E_FAIL); + } + + // + // For each field, allocate the outbound pointer and call through. + // + if (ppwszClassName && pFoundInfo->pcwszTypeName) + { + cch = wcslen(pFoundInfo->pcwszTypeName); + + if (cch > 0) + { + IfNullGo(*ppwszClassName = new (nothrow) WCHAR[cch + 1]); + wcscpy_s(*ppwszClassName, cch+1, pFoundInfo->pcwszTypeName); + } + else + IfFailGo(E_FAIL); + } + + if (ppwszRuntimeVersion) + { + if (pFoundInfo->pcwszRuntimeVersion && (cch = wcslen(pFoundInfo->pcwszRuntimeVersion)) > 0) + { + IfNullGo(*ppwszRuntimeVersion = new (nothrow) WCHAR[cch + 1]); + wcscpy_s(*ppwszRuntimeVersion, cch+1, pFoundInfo->pcwszRuntimeVersion); + } + else + { + // Sxs.dll returns empty string even when the runtimeVersion attribute is missing so + // we cannot tell whether it's not there or is empty. We'll return 1.0 in both cases. + // + // The goal is to emulate pre-4.0 behavior where this function wasn't called with + // non-NULL ppwszRuntimeVersion on the COM activation path at all so shim loaded the + // latest runtime on the machine. + IfNullGo(*ppwszRuntimeVersion = DuplicateString(V1_VERSION_NUM)); + } + } + + if (pfRegFreePIA) + { + *pfRegFreePIA = (pFoundInfo->dwFlags == SXS_GUID_INFORMATION_CLR_FLAG_IS_SURROGATE); + } + +ErrExit: + // + // Deallocate in case of failure + // + if (FAILED(hr)) + { + if (ppwszRuntimeVersion && *ppwszRuntimeVersion) + { + delete [] *ppwszRuntimeVersion; + *ppwszRuntimeVersion = NULL; + } + if (ppwszAssemblyString && *ppwszAssemblyString) + { + delete [] *ppwszAssemblyString; + *ppwszAssemblyString = NULL; + } + if (ppwszClassName && *ppwszClassName) + { + delete [] *ppwszClassName; + *ppwszClassName = NULL; + } + } + + return hr; +} + +// TranslateWin32AssemblyIdentityToFusionDisplayName +// +// Culture info is missing in the assemblyIdentity returned from win32, +// So Need to do a little more work here to get the correct fusion display name +// +// replace "language=" in assemblyIdentity to "culture=" if any. +// If "language=" is not present in assemblyIdentity, add "culture=neutral" +// to it. +// +// Also check other attributes as well. +// +// Parameters: +// ppwzFusionDisplayName: the corrected output of assembly displayname +// lpWin32AssemblyIdentity: input assemblyIdentity returned from win32 +// +// returns: +// TRUE if the conversion is done. +// FALSE otherwise + +BOOL TranslateWin32AssemblyIdentityToFusionDisplayName(__deref_out_z LPWSTR *ppwzFusionDisplayName, PCWSTR lpWin32AssemblyIdentity) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; + + size_t size = 0; + LPWSTR lpAssemblyIdentityCopy = NULL; + LPWSTR lpVersionKey = W("version="); + LPWSTR lpPublicKeyTokenKey = W("publickeytoken="); + LPWSTR lpCultureKey = W("culture="); + LPWSTR lpNeutral = W("neutral"); + LPWSTR lpLanguageKey = W("language="); + LPWSTR lpMatch = NULL; + LPWSTR lpwzFusionDisplayName = NULL; + + if (ppwzFusionDisplayName == NULL) return FALSE; + *ppwzFusionDisplayName = NULL; + + if (lpWin32AssemblyIdentity == NULL) return FALSE; + + size = wcslen(lpWin32AssemblyIdentity); + if (size == 0) return FALSE; + + // make a local copy + lpAssemblyIdentityCopy = new (nothrow) WCHAR[size+1]; + if (!lpAssemblyIdentityCopy) + return FALSE; + + wcscpy_s(lpAssemblyIdentityCopy, size+1, lpWin32AssemblyIdentity); + + // convert to lower case + _wcslwr_s(lpAssemblyIdentityCopy, size+1); + + // check if "version" key is presented + if (!wcsstr(lpAssemblyIdentityCopy, lpVersionKey)) + { + // version is not presented, append it + size += wcslen(lpVersionKey)+8; // length of ","+"0.0.0.0" + lpwzFusionDisplayName = new (nothrow) WCHAR[size+1]; + if (!lpwzFusionDisplayName) + { + // clean up + delete[] lpAssemblyIdentityCopy; + return FALSE; + } + + //copy old one + wcscpy_s(lpwzFusionDisplayName, size+1, lpAssemblyIdentityCopy); + wcscat_s(lpwzFusionDisplayName, size+1, W(",")); + wcscat_s(lpwzFusionDisplayName, size+1, lpVersionKey); + wcscat_s(lpwzFusionDisplayName, size+1, W("0.0.0.0")); + + // delete the old copy + delete[] lpAssemblyIdentityCopy; + + // lpAssemblyIdentityCopy has the new copy + lpAssemblyIdentityCopy = lpwzFusionDisplayName; + lpwzFusionDisplayName = NULL; + } + + // check if "publickeytoken" key is presented + if (!wcsstr(lpAssemblyIdentityCopy, lpPublicKeyTokenKey)) + { + // publickeytoken is not presented, append it + size += wcslen(lpPublicKeyTokenKey)+5; //length of ","+"null" + lpwzFusionDisplayName = new (nothrow) WCHAR[size+1]; + if (!lpwzFusionDisplayName) + { + // clean up + delete[] lpAssemblyIdentityCopy; + return FALSE; + } + + // copy the old one + wcscpy_s(lpwzFusionDisplayName, size+1, lpAssemblyIdentityCopy); + wcscat_s(lpwzFusionDisplayName, size+1, W(",")); + wcscat_s(lpwzFusionDisplayName, size+1, lpPublicKeyTokenKey); + wcscat_s(lpwzFusionDisplayName, size+1, W("null")); + + // delete the old copy + delete[] lpAssemblyIdentityCopy; + + // lpAssemblyIdentityCopy has the new copy + lpAssemblyIdentityCopy = lpwzFusionDisplayName; + lpwzFusionDisplayName = NULL; + } + + if (wcsstr(lpAssemblyIdentityCopy, lpCultureKey)) + { + // culture info is already included in the assemblyIdentity + // nothing need to be done + lpwzFusionDisplayName = lpAssemblyIdentityCopy; + *ppwzFusionDisplayName = lpwzFusionDisplayName; + return TRUE; + } + + if ((lpMatch = wcsstr(lpAssemblyIdentityCopy, lpLanguageKey)) !=NULL ) + { + // language info is included in the assembly identity + // need to replace it with culture + + // final size + size += wcslen(lpCultureKey)-wcslen(lpLanguageKey); + lpwzFusionDisplayName = new (nothrow) WCHAR[size + 1]; + if (!lpwzFusionDisplayName) + { + // clean up + delete[] lpAssemblyIdentityCopy; + return FALSE; + } + wcsncpy_s(lpwzFusionDisplayName, size+1, lpAssemblyIdentityCopy, lpMatch-lpAssemblyIdentityCopy); + lpwzFusionDisplayName[lpMatch-lpAssemblyIdentityCopy] = W('\0'); + wcscat_s(lpwzFusionDisplayName, size+1, lpCultureKey); + wcscat_s(lpwzFusionDisplayName, size+1, lpMatch+wcslen(lpLanguageKey)); + *ppwzFusionDisplayName = lpwzFusionDisplayName; + + // clean up + delete[] lpAssemblyIdentityCopy; + return TRUE; + } + else + { + // neither culture or language key is presented + // let us attach culture info key to the identity + size += wcslen(lpCultureKey)+wcslen(lpNeutral)+1; + lpwzFusionDisplayName = new (nothrow) WCHAR[size + 1]; + if (!lpwzFusionDisplayName) + { + // clean up + delete[] lpAssemblyIdentityCopy; + return FALSE; + } + + wcscpy_s(lpwzFusionDisplayName, size+1, lpAssemblyIdentityCopy); + wcscat_s(lpwzFusionDisplayName, size+1, W(",")); + wcscat_s(lpwzFusionDisplayName, size+1, lpCultureKey); + wcscat_s(lpwzFusionDisplayName, size+1, lpNeutral); + *ppwzFusionDisplayName = lpwzFusionDisplayName; + + // clean up + delete[] lpAssemblyIdentityCopy; + return TRUE; + } +} + +//**************************************************************************** +// AssemblyVersion +// +// class to handle assembly version +// Since only functions in this file will use it, +// we declare it in the cpp file so other people won't use it. +// +//**************************************************************************** + +// Extract version info from pwzVersion, expecting "a.b.c.d", +// where a,b,c and d are all digits. +HRESULT AssemblyVersion::Init(__in_z LPCWSTR pcwzVersion, BOOL bStartsWithV) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; + + HRESULT hr = S_OK; + LPWSTR pwzVersionCopy = NULL; + LPWSTR pwzTokens = NULL; + LPWSTR pwzToken = NULL; + size_t size = 0; + int iVersion = 0; + + if ((pcwzVersion == NULL) || (*pcwzVersion == W('\0'))) + IfFailGo(E_INVALIDARG); + + // If fStartsWithV is true, then the version string should start with a v. + // Verify this and if it is the case, start tokenizing at the character after the v. + if (bStartsWithV) + { + if (*pcwzVersion == W('v') || *pcwzVersion == W('V')) + pcwzVersion++; + else + IfFailGo(E_INVALIDARG); + } + + IfFailGo(ValidateVersion(pcwzVersion)); + + size = wcslen(pcwzVersion); + + IfNullGo(pwzVersionCopy = new (nothrow) WCHAR[size + 1]); + + wcscpy_s(pwzVersionCopy, size+1, pcwzVersion); + pwzTokens = pwzVersionCopy; + + // parse major version + pwzToken = wcstok_s(pwzTokens, W("."),&pwzTokens); + if (pwzToken != NULL) + { + iVersion = _wtoi(pwzToken); + if (iVersion > 0xffff) + IfFailGo(E_INVALIDARG); + _major = (WORD)iVersion; + } + + // parse minor version + pwzToken = wcstok_s(pwzTokens, W("."),&pwzTokens); + if (pwzToken != NULL) + { + iVersion = _wtoi(pwzToken); + if (iVersion > 0xffff) + IfFailGo(E_INVALIDARG); + _minor = (WORD)iVersion; + } + + // parse build version + pwzToken = wcstok_s(pwzTokens, W("."),&pwzTokens); + if (pwzToken != NULL) + { + iVersion = _wtoi(pwzToken); + if (iVersion > 0xffff) + IfFailGo(E_INVALIDARG); + _build = (WORD)iVersion; + } + + // parse revision version + pwzToken = wcstok_s(pwzTokens, W("."),&pwzTokens); + if (pwzToken != NULL) + { + iVersion = _wtoi(pwzToken); + if (iVersion > 0xffff) + IfFailGo(E_INVALIDARG); + _revision = (WORD)iVersion; + } + +ErrExit: + if (pwzVersionCopy) + delete[] pwzVersionCopy; + return hr; +} + +// pcwzVersion must be in format of a.b.c.d, where a, b, c, d are numbers +HRESULT AssemblyVersion::ValidateVersion(LPCWSTR pcwzVersion) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; + + LPCWSTR pwCh = pcwzVersion; + INT dots = 0; // number of dots + BOOL bIsDot = FALSE; // is previous char a dot? + + // first char cannot be . + if (*pwCh == W('.')) + return E_INVALIDARG; + + for(;*pwCh != W('\0');pwCh++) + { + if (*pwCh == W('.')) + { + if (bIsDot) // .. + return E_INVALIDARG; + else + { + dots++; + bIsDot = TRUE; + } + } + /* + // We can't do this sort of validation, because then our v1.2.x86chk version numbers will be invalid + else if (!iswdigit(*pwCh)) + return E_INVALIDARG; + */ + else + bIsDot = FALSE; + } + + if (dots > 3) + return E_INVALIDARG; + + return S_OK; +} + +BOOL operator==(const AssemblyVersion& version1, + const AssemblyVersion& version2) +{ + LIMITED_METHOD_CONTRACT; + + return ((version1._major == version2._major) + && (version1._minor == version2._minor) + && (version1._build == version2._build) + && (version1._revision == version2._revision)); +} + +BOOL operator>=(const AssemblyVersion& version1, + const AssemblyVersion& version2) +{ + LIMITED_METHOD_CONTRACT; + + ULONGLONG ulVersion1; + ULONGLONG ulVersion2; + + ulVersion1 = version1._major; + ulVersion1 = (ulVersion1<<16)|version1._minor; + ulVersion1 = (ulVersion1<<16)|version1._build; + ulVersion1 = (ulVersion1<<16)|version1._revision; + + ulVersion2 = version2._major; + ulVersion2 = (ulVersion2<<16)|version2._minor; + ulVersion2 = (ulVersion2<<16)|version2._build; + ulVersion2 = (ulVersion2<<16)|version2._revision; + + return (ulVersion1 >= ulVersion2); +} + +enum RegistryBasePath +{ + RegistryBasePath_Record, + RegistryBasePath_CLSID_InprocServer32, + RegistryBasePath_CLSID_LocalServer32_32Key, + RegistryBasePath_CLSID_LocalServer32_64Key, +}; + +// Find which subkey has the highest verion +// If return S_OK, *ppwzHighestVersion has the highest version string. +// *pbIsTopKey indicates if top key is the one with highest version. +// If return S_FALSE, cannot find any version. *ppwzHighestVersion is set +// to NULL, and *pbIsTopKey is TRUE. +// If failed, *ppwzHighestVersion will be set to NULL, and *pbIsTopKey is +// undefined. +// Note: If succeeded, this function will allocate memory for *ppwzVersion. +// Caller is responsible to release them +HRESULT FindHighestVersion(REFCLSID rclsid, RegistryBasePath basePath, AssemblyVersion *prvHighestAllowed, + __deref_out_z LPWSTR *ppwzHighestVersion, BOOL *pbIsTopKey, BOOL *pbIsUnmanagedObject) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; + + HRESULT hr = S_OK; + WCHAR szID[64]; + WCHAR clsidKeyname[128]; + WCHAR wzSubKeyName[32]; + DWORD cwSubKeySize; + DWORD dwIndex; // subkey index + HKEY hKeyCLSID = NULL; + HKEY hSubKey = NULL; + DWORD type; + DWORD size; + BOOL bIsTopKey = FALSE; // Does top key have the highest version? + BOOL bGotVersion = FALSE; // Do we get anything out of registry? + LONG lResult; + LPWSTR wzAssemblyString = NULL; + DWORD numSubKeys = 0; + AssemblyVersion avHighest; WCHAR wzHighest[32]; + AssemblyVersion avCurrent; WCHAR wzCurrent[32]; + + _ASSERTE(pbIsUnmanagedObject != NULL); + *pbIsUnmanagedObject = FALSE; + + if ((ppwzHighestVersion == NULL) || (pbIsTopKey == NULL)) + IfFailGo(E_INVALIDARG); + + *ppwzHighestVersion = NULL; + *pbIsTopKey = FALSE; + + if (!GuidToLPWSTR(rclsid, szID, NumItems(szID))) + IfFailGo(E_INVALIDARG); + + if (basePath == RegistryBasePath_Record) + { + wcscpy_s(clsidKeyname, 128, W("Record\\")); + wcscat_s(clsidKeyname, 128, szID); + } + else + { + wcscpy_s(clsidKeyname, 128, W("CLSID\\")); + wcscat_s(clsidKeyname, 128, szID); + if (basePath == RegistryBasePath_CLSID_InprocServer32) + { + wcscat_s(clsidKeyname, 128, W("\\InprocServer32")); + } + else + { + _ASSERTE(basePath == RegistryBasePath_CLSID_LocalServer32_32Key || basePath == RegistryBasePath_CLSID_LocalServer32_64Key); + wcscat_s(clsidKeyname, 128, W("\\LocalServer32")); + } + } + + // Open HKCR\CLSID\<clsid> , or HKCR\Record\<RecordId> + REGSAM accessFlags = KEY_ENUMERATE_SUB_KEYS | KEY_READ; + if (basePath == RegistryBasePath_CLSID_LocalServer32_32Key) + { + // open the WoW key + accessFlags |= KEY_WOW64_32KEY; + } + else if (basePath == RegistryBasePath_CLSID_LocalServer32_64Key) + { + // open the 64-bit key + accessFlags |= KEY_WOW64_64KEY; + } + + IfFailWin32Go(WszRegOpenKeyEx( + HKEY_CLASSES_ROOT, + clsidKeyname, + 0, + accessFlags, + &hKeyCLSID)); + + + // + // Start by looking for a version subkey. + // + + IfFailWin32Go(WszRegQueryInfoKey(hKeyCLSID, NULL, NULL, NULL, + &numSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL)); + + for ( dwIndex = 0; dwIndex < numSubKeys; dwIndex++) + { + cwSubKeySize = NumItems(wzSubKeyName); + + IfFailWin32Go(WszRegEnumKeyEx(hKeyCLSID, //HKCR\CLSID\<clsid>\InprocServer32 + dwIndex, // which subkey + wzSubKeyName, // subkey name + &cwSubKeySize, // size of subkey name + NULL, // lpReserved + NULL, // lpClass + NULL, // lpcbClass + NULL)); // lpftLastWriteTime + + hr = avCurrent.Init(wzSubKeyName, FALSE); + if (FAILED(hr)) + { + // not valid version subkey, ignore + continue; + } + wcscpy_s(wzCurrent, COUNTOF(wzCurrent), wzSubKeyName); + + IfFailWin32Go(WszRegOpenKeyEx( + hKeyCLSID, + wzSubKeyName, + 0, + accessFlags, + &hSubKey)); + + // Check if this is a non-interop scenario + lResult = WszRegQueryValueEx( + hSubKey, + SBSVERSIONVALUE, + NULL, + &type, + NULL, + &size); + if (lResult == ERROR_SUCCESS) + { + *pbIsUnmanagedObject = TRUE; + } + // This is an interop assembly + else + { + lResult = WszRegQueryValueEx( + hSubKey, + W("Assembly"), + NULL, + &type, + NULL, + &size); + if (!((lResult == ERROR_SUCCESS)&&(type == REG_SZ)&&(size > 0))) + { + // do not have value "Assembly" + RegCloseKey(hSubKey); + hSubKey = NULL; + continue; + } + + lResult = WszRegQueryValueEx( + hSubKey, + W("Class"), + NULL, + &type, + NULL, + &size); + if (!((lResult == ERROR_SUCCESS)&&(type == REG_SZ)&&(size > 0))) + { + // do not have value "Class" + RegCloseKey(hSubKey); + hSubKey = NULL; + continue; + } + + lResult = WszRegQueryValueEx( + hSubKey, + W("RuntimeVersion"), + NULL, + &type, + NULL, + &size); + if (!((lResult == ERROR_SUCCESS)&&(type == REG_SZ)&&(size > 0))) + { + // We didn't find a RuntimeVersion value. This is fine for Records since + // in V1.1, Records didn't have a RuntimeVersion value. However if we aren't + // dealing with a Record, then this version subkey is invalid. + if (basePath != RegistryBasePath_Record) + { + // do not have value "RuntimeVersion" + RegCloseKey(hSubKey); + hSubKey = NULL; + continue; + } + } + else + { + // If a highest allowed runtime version was specified, make sure that the component + // was built with a runtime version lower or equal to the highest. + if (prvHighestAllowed) + { + NewArrayHolder<WCHAR> wzRuntimeVersionString = + new (nothrow) WCHAR[(size/sizeof(WCHAR)) + 1]; + IfNullGo(wzRuntimeVersionString); + // RegQueryValueEx() does not guarantee NULL-terminated strings + wzRuntimeVersionString[size/sizeof(WCHAR)] = W('\0'); + IfFailWin32Go(WszRegQueryValueEx( + hSubKey, + W("RuntimeVersion"), + NULL, + &type, + (LPBYTE)(WCHAR*)wzRuntimeVersionString, + &size)); + + AssemblyVersion rvCurrent; + rvCurrent.Init(wzRuntimeVersionString, TRUE); + + // We only care about major and minor version number. + rvCurrent.SetBuild(0); + rvCurrent.SetRevision(0); + + if ((*prvHighestAllowed) < rvCurrent) + { + // This version of the component was built with a runtime version higher + // than maximum allowed one so we don't want to consider it. + RegCloseKey(hSubKey); + hSubKey = NULL; + continue; + } + } + } + } + + // ok. Now I believe this is a valid subkey + RegCloseKey(hSubKey); + hSubKey = NULL; + + if (bGotVersion) + { + if (avCurrent >= avHighest) + { + avHighest = avCurrent; + wcscpy_s(wzHighest, COUNTOF(wzHighest), wzCurrent); + } + } + else + { + avHighest = avCurrent; + wcscpy_s(wzHighest, COUNTOF(wzHighest), wzCurrent); + } + + bGotVersion = TRUE; + } + + + // + // If there are no subkeys, then look at the top level key. + // + + if (!bGotVersion) + { + // make sure value Class exists + // If not dealing with record, also make sure RuntimeVersion exists. + if ((WszRegQueryValueEx(hKeyCLSID, W("Class"), NULL, &type, NULL, &size) == ERROR_SUCCESS) && (type == REG_SZ) && (size > 0)) + { + // If there is no RuntimeVersion value, we will assume the component was built against + // the V1.0 CLR. + BOOL bSupportedVersion = TRUE; + + lResult = WszRegQueryValueEx( + hKeyCLSID, + W("RuntimeVersion"), + NULL, + &type, + NULL, + &size); + if ((lResult == ERROR_SUCCESS) && (type == REG_SZ) && (size > 0)) + { + // If a highest allowed runtime version was specified, make sure that the component + // was built with a runtime version lower or equal to the highest. + if (prvHighestAllowed) + { + NewArrayHolder<WCHAR> wzRuntimeVersionString = + new (nothrow) WCHAR[(size/sizeof(WCHAR)) + 1]; + IfNullGo(wzRuntimeVersionString); + // RegQueryValueEx() does not guarantee NULL-terminated strings + wzRuntimeVersionString[size/sizeof(WCHAR)] = W('\0'); + IfFailWin32Go(WszRegQueryValueEx( + hKeyCLSID, + W("RuntimeVersion"), + NULL, + &type, + (LPBYTE)(WCHAR*)wzRuntimeVersionString, + &size)); + + AssemblyVersion rvCurrent; + rvCurrent.Init(wzRuntimeVersionString, TRUE); + + // We only care about major and minor version number. + rvCurrent.SetBuild(0); + rvCurrent.SetRevision(0); + + if ((*prvHighestAllowed) < rvCurrent) + { + // This version of the component was built with a runtime version higher + // than maximum allowed one so we don't want to consider it. + bSupportedVersion = FALSE; + } + } + } + + if (bSupportedVersion) + { + // Get the size of assembly display name + lResult = WszRegQueryValueEx( + hKeyCLSID, + W("Assembly"), + NULL, + &type, + NULL, + &size); + + if ((lResult == ERROR_SUCCESS) && (type == REG_SZ) && (size > 0)) + { + IfNullGo(wzAssemblyString = new (nothrow) WCHAR[size + 1]); + IfFailWin32Go(WszRegQueryValueEx( + hKeyCLSID, + W("Assembly"), + NULL, + &type, + (LPBYTE)wzAssemblyString, + &size)); + + // Now we have the assembly display name. + // Extract the version out. + + // first lowercase display name + _wcslwr_s(wzAssemblyString,size+1); + + // locate "version=" + LPWSTR pwzVersion = wcsstr(wzAssemblyString, W("version=")); + if (pwzVersion) { + // point to the character after "version=" + pwzVersion += 8; // length of W("version=") + + // Now find the next W(',') + LPWSTR pwzEnd = pwzVersion; + + while((*pwzEnd != W(',')) && (*pwzEnd != W('\0'))) + pwzEnd++; + + // terminate version string + *pwzEnd = W('\0'); + + // trim version string + while(iswspace(*pwzVersion)) + pwzVersion++; + + pwzEnd--; + while(iswspace(*pwzEnd)&&(pwzEnd > pwzVersion)) + { + *pwzEnd = W('\0'); + pwzEnd--; + } + + // Make sure the version is valid. + if(SUCCEEDED(avHighest.Init(pwzVersion, FALSE))) + { + // This is the first version found, so it is the highest version + wcscpy_s(wzHighest, COUNTOF(wzHighest), pwzVersion); + bIsTopKey = TRUE; + bGotVersion = TRUE; + } + } + } + } + } // end of handling of key HKCR\CLSID\<clsid>\InprocServer32 + } + + if (bGotVersion) + { + // Now we have the highest version. Copy it out + size_t cchHighest = wcslen(wzHighest) + 1; + *ppwzHighestVersion = new (nothrow) WCHAR[cchHighest]; + wcscpy_s(*ppwzHighestVersion, cchHighest, wzHighest); + + *pbIsTopKey = bIsTopKey; + + // return S_OK to indicate we successfully found the highest version. + hr = S_OK; + } + else + { + // return E_CLASSNOTREG to indicate that we didn't find anything + hr = REGDB_E_CLASSNOTREG; + } + +ErrExit: + if (hKeyCLSID) + RegCloseKey(hKeyCLSID); + if (hSubKey) + RegCloseKey(hSubKey); + if (wzAssemblyString) + delete[] wzAssemblyString; + + return hr; +} + +// If the value exists and is retrieved successfully, returns S_OK +// If the value does not exist, returns S_FALSE +// If some other error occurs, returns error-specific HRESULT +static +HRESULT ReadRegistryStringValue( + HKEYHolder &hKey, + LPCWSTR wszName, + __deref_out_z __deref_out_opt LPWSTR *pwszValue) +{ + HRESULT hr = S_OK; + + _ASSERTE(pwszValue != NULL); + *pwszValue = NULL; + + // extract the string value. + DWORD dwSize; + DWORD dwType; + NewArrayHolder<WCHAR> wszValue(NULL); + hr = HRESULT_FROM_WIN32(WszRegQueryValueEx(hKey, wszName, NULL, &dwType, NULL, &dwSize)); + + // If the function succeeds, the return value is ERROR_SUCCESS. + // If the wszName registry value does not exist, the function returns ERROR_FILE_NOT_FOUND. + if (hr == HRESULT_FROM_WIN32(ERROR_SUCCESS)) + { + // The value is not a string + if (dwType != REG_SZ) + return E_INVALIDARG; + + if(!ClrSafeInt<DWORD>::addition(dwSize, 1, dwSize)) + IfFailWin32Ret(ERROR_ARITHMETIC_OVERFLOW); + + IfNullRet(wszValue = new (nothrow) WCHAR[dwSize]); + IfFailWin32Ret(WszRegQueryValueEx(hKey, wszName, NULL, NULL, (LPBYTE)static_cast<LPCWSTR>(wszValue), &dwSize)); + hr = S_OK; + } + else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + return S_FALSE; + } + + _ASSERTE(hr == S_OK); + wszValue.SuppressRelease(); + *pwszValue = wszValue; + return hr; +} + +// FindRuntimeVersionFromRegistry +// +// Find the runtimeVersion corresponding to the highest version +HRESULT FindRuntimeVersionFromRegistry( + REFCLSID rclsid, + __deref_out_z __deref_out_opt LPWSTR *ppwzRuntimeVersion, + __deref_out_z __deref_out_opt LPWSTR *ppwzSupportedVersions) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; + + HRESULT hr = S_OK; + HKEYHolder userKey; + WCHAR szID[64]; + WCHAR keyname[256]; + DWORD size; + DWORD type; + NewArrayHolder<WCHAR> pwzVersion = NULL; + BOOL bIsTopKey; + BOOL bIsUnmanagedObject = FALSE; + NewArrayHolder<WCHAR> pwzRuntimeVersion = NULL; + NewArrayHolder<WCHAR> pwzSupportedRuntimeVersions = NULL; + + if (ppwzRuntimeVersion == NULL) + IfFailRet(E_INVALIDARG); + + // Initialize the string passed in to NULL. + *ppwzRuntimeVersion = NULL; + + if (ppwzSupportedVersions != NULL) + *ppwzSupportedVersions = NULL; + + // Convert the GUID to its string representation. + if (GuidToLPWSTR(rclsid, szID, NumItems(szID)) == 0) + IfFailRet(E_INVALIDARG); + + // retrieve the highest version. + IfFailRet(FindHighestVersion(rclsid, RegistryBasePath_CLSID_InprocServer32, NULL, &pwzVersion, &bIsTopKey, &bIsUnmanagedObject)); + + if (!bIsUnmanagedObject) + { + // if highest version is in top key, + // we will look at HKCR\CLSID\<clsid>\InprocServer32 or HKCR\Record\<RecordId> + // Otherwise we will look at HKCR\CLSID\<clsid>\InprocServer32\<version> or HKCR\Record\<RecordId>\<Version> + wcscpy_s(keyname, 256, W("CLSID\\")); + wcscat_s(keyname, 256, szID); + wcscat_s(keyname, 256, W("\\InprocServer32")); + if (!bIsTopKey) + { + wcscat_s(keyname, 256, W("\\")); + wcscat_s(keyname, 256, pwzVersion); + } + + // open the registry key + IfFailWin32Ret(WszRegOpenKeyEx(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &userKey)); + + // extract the runtime version. + IfFailRet(ReadRegistryStringValue(userKey, W("RuntimeVersion"), &pwzRuntimeVersion)); + if (hr == S_FALSE) + { + IfNullRet(pwzRuntimeVersion = DuplicateString(V1_VERSION_NUM)); + } + + // extract the supported runtime versions + if (ppwzSupportedVersions != NULL) + IfFailRet(ReadRegistryStringValue(userKey, W("SupportedRuntimeVersions"), &pwzSupportedRuntimeVersions)); + } + + else + { + // We need to prepend the 'v' to the version string + IfNullRet(pwzRuntimeVersion = new (nothrow) WCHAR[wcslen(pwzVersion)+1+1]); // +1 for the v, +1 for the null + *pwzRuntimeVersion = W('v'); + wcscpy_s(pwzRuntimeVersion+1, wcslen(pwzVersion)+1, pwzVersion); + } + + _ASSERTE(SUCCEEDED(hr)); + + // now we have the data, copy it out + pwzRuntimeVersion.SuppressRelease(); + *ppwzRuntimeVersion = pwzRuntimeVersion; + + if (ppwzSupportedVersions != NULL) + { + pwzSupportedRuntimeVersions.SuppressRelease(); + *ppwzSupportedVersions = pwzSupportedRuntimeVersions; + } + + return hr; +} + +// FindShimInfoFromRegistry +// +HRESULT FindShimInfoFromRegistryWorker( + REFCLSID rclsid, + RegistryBasePath basePath, + WORD wHighestRuntimeMajorVersion, + WORD wHighestRuntimeMinorVersion, + __deref_out_z LPWSTR *ppwzClassName, + __deref_out_z LPWSTR *ppwzAssemblyString, + __deref_out_z LPWSTR *ppwzCodeBase) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; + + HRESULT hr = S_OK; + HKEY userKey = NULL; + WCHAR szID[64]; + WCHAR keyname[256]; + DWORD size; + DWORD type; + LPWSTR pwzVersion = NULL; + BOOL bIsTopKey; + NewArrayHolder<WCHAR> wzClassName = NULL; + NewArrayHolder<WCHAR> wzAssemblyString = NULL; + NewArrayHolder<WCHAR> wzCodeBase = NULL; + LONG lResult; + AssemblyVersion highestRuntimeVersion; + + // at least one should be specified. + // codebase is optional + if ((ppwzClassName == NULL) && (ppwzAssemblyString == NULL)) + IfFailGo(E_INVALIDARG); + + // Initialize the strings passed in to NULL. + if (ppwzClassName) + *ppwzClassName = NULL; + if (ppwzAssemblyString) + *ppwzAssemblyString = NULL; + if (ppwzCodeBase) + *ppwzCodeBase = NULL; + + // Convert the GUID to its string representation. + if (GuidToLPWSTR(rclsid, szID, NumItems(szID)) == 0) + IfFailGo(E_INVALIDARG); + + // retrieve the highest version. + BOOL bIsUnmanaged = FALSE; + + // Initialize the highest runtime version based on the passed in major and minor version numbers. + highestRuntimeVersion.Init(wHighestRuntimeMajorVersion, wHighestRuntimeMinorVersion, 0, 0); + + IfFailGo(FindHighestVersion(rclsid, basePath, &highestRuntimeVersion, &pwzVersion, &bIsTopKey, &bIsUnmanaged)); + + // if highest version is in top key, + // we will look at HKCR\CLSID\<clsid>\{Inproc,Local}Server32 or HKCR\Record\<RecordId> + // Otherwise we will look at HKCR\CLSID\<clsid>\{Inproc,Local}Server32\<version> or HKCR\Record\<RecordId>\<Version> + if (basePath == RegistryBasePath_Record) + { + wcscpy_s(keyname, 256, W("Record\\")); + wcscat_s(keyname, 256, szID); + } + else + { + wcscpy_s(keyname, 256, W("CLSID\\")); + wcscat_s(keyname, 256, szID); + if (basePath == RegistryBasePath_CLSID_InprocServer32) + { + wcscat_s(keyname, 256, W("\\InprocServer32")); + } + else + { + _ASSERTE(basePath == RegistryBasePath_CLSID_LocalServer32_32Key || basePath == RegistryBasePath_CLSID_LocalServer32_64Key); + wcscat_s(keyname, 256, W("\\LocalServer32")); + } + } + if (!bIsTopKey) + { + wcscat_s(keyname, 256, W("\\")); + wcscat_s(keyname, 256, pwzVersion); + } + + // open the registry + REGSAM accessFlags = KEY_READ; + if (basePath == RegistryBasePath_CLSID_LocalServer32_32Key) + { + // open the WoW key + accessFlags |= KEY_WOW64_32KEY; + } + else if (basePath == RegistryBasePath_CLSID_LocalServer32_64Key) + { + // open the 64-bit key + accessFlags |= KEY_WOW64_64KEY; + } + + IfFailWin32Go(WszRegOpenKeyEx(HKEY_CLASSES_ROOT, keyname, 0, accessFlags, &userKey)); + + // get the class name + IfFailWin32Go(WszRegQueryValueEx(userKey, W("Class"), NULL, &type, NULL, &size)); + IfNullGo(wzClassName = new (nothrow) WCHAR[size + 1]); + IfFailWin32Go(WszRegQueryValueEx(userKey, W("Class"), NULL, NULL, (LPBYTE)wzClassName.GetValue(), &size)); + + // get the assembly string + IfFailWin32Go(WszRegQueryValueEx(userKey, W("Assembly"), NULL, &type, NULL, &size)); + IfNullGo(wzAssemblyString = new (nothrow) WCHAR[size + 1]); + IfFailWin32Go(WszRegQueryValueEx(userKey, W("Assembly"), NULL, NULL, (LPBYTE)wzAssemblyString.GetValue(), &size)); + + // get the code base if requested + if (ppwzCodeBase) + { + // get the codebase, however not finding it does not constitute + // a fatal error. + lResult = WszRegQueryValueEx(userKey, W("CodeBase"), NULL, &type, NULL, &size); + if ((lResult == ERROR_SUCCESS) && (type == REG_SZ) && (size > 0)) + { + IfNullGo(wzCodeBase = new (nothrow) WCHAR[size + 1]); + IfFailWin32Go(WszRegQueryValueEx(userKey, W("CodeBase"), NULL, NULL, (LPBYTE)wzCodeBase.GetValue(), &size)); + } + } + + // now we got everything. Copy them out + if (ppwzClassName) + *ppwzClassName = wzClassName.Extract(); + + if (ppwzAssemblyString) + *ppwzAssemblyString = wzAssemblyString.Extract(); + + if (ppwzCodeBase) + *ppwzCodeBase = wzCodeBase.Extract(); + + hr = S_OK; + +ErrExit: + if (userKey) + RegCloseKey(userKey); + + if (pwzVersion) + delete[] pwzVersion; + + return hr; +} + +// Find shim info corresponding to the highest version +HRESULT FindShimInfoFromRegistry( + REFCLSID rclsid, + BOOL bLoadRecord, + WORD wHighestRuntimeMajorVersion, + WORD wHighestRuntimeMinorVersion, + __deref_out_z LPWSTR *ppwzClassName, + __deref_out_z LPWSTR *ppwzAssemblyString, + __deref_out_z LPWSTR *ppwzCodeBase) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; + + if (bLoadRecord) + { + return FindShimInfoFromRegistryWorker(rclsid, + RegistryBasePath_Record, + wHighestRuntimeMajorVersion, + wHighestRuntimeMinorVersion, + ppwzClassName, + ppwzAssemblyString, + ppwzCodeBase); + } + + // try InprocServer32 first + HRESULT hr = FindShimInfoFromRegistryWorker(rclsid, + RegistryBasePath_CLSID_InprocServer32, + wHighestRuntimeMajorVersion, + wHighestRuntimeMinorVersion, + ppwzClassName, + ppwzAssemblyString, + ppwzCodeBase); + + // if it fails try both 64-bit and WoW LocalServer32 + if (FAILED(hr)) + { + // prefer the bitness of the process, use the other bitness as a fallback + hr = FindShimInfoFromRegistryWorker(rclsid, +#ifdef _WIN64 + RegistryBasePath_CLSID_LocalServer32_64Key, +#else // _WIN64 + RegistryBasePath_CLSID_LocalServer32_32Key, +#endif // _WIN64 + wHighestRuntimeMajorVersion, + wHighestRuntimeMinorVersion, + ppwzClassName, + ppwzAssemblyString, + ppwzCodeBase); + + if (FAILED(hr) +#ifndef _WIN64 + && RunningInWow64() +#endif // !_WIN64 + ) + { + hr = FindShimInfoFromRegistryWorker(rclsid, +#ifdef _WIN64 + RegistryBasePath_CLSID_LocalServer32_32Key, +#else // _WIN64 + RegistryBasePath_CLSID_LocalServer32_64Key, +#endif // _WIN64 + wHighestRuntimeMajorVersion, + wHighestRuntimeMinorVersion, + ppwzClassName, + ppwzAssemblyString, + ppwzCodeBase); + } + } + return hr; +} + + +// ---------------------------------------------------------------------------------------------------------- +// +// Gets config file name from Win32 manifest file. Strips the last extension (.manifest) from the manifest +// file name. Doesn't strip the last extension if it is '.exe'. +// +// Note: Config file name could be already set in activation context by COM+ (out-of-process COM) - they +// pick up CLR version information from registry. +// +// Arguments: +// wszBuffer - [in, out] The buffer to fill the configuration file name (can be NULL) +// cBuffer - [in] Size of the buffer in wide characters +// pcNameSize - [out] Size of the name filled in the buffer (in wide characters including terminating null). +// Can be NULL. +// +// Return value: +// S_OK - Success. Buffer (wszBuffer) and config file name size (pcNameSize) are filled. +// HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) - Buffer is too small for filling config file name. +// Sets pcNameSize (if not NULL) to the size of config file name including null terminator. +// +HRESULT GetConfigFileFromWin32Manifest(__out_ecount(cBuffer) WCHAR *wszBuffer, + SIZE_T cBuffer, + SIZE_T *pcNameSize) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; + + HRESULT hr = S_OK; + + HANDLE hActCtx = NULL; + SIZE_T cbInfo = 0; + SIZE_T cConfigFileName = 0; + + // Get detailed information about activation activation context + if (!WszQueryActCtxW(0, // Flags + hActCtx, // Activation context being queried + NULL, // Specific to the information class + ActivationContextDetailedInformation, + // Information class - detailed information + NULL, // [out] Buffer + 0, // [in] Buffer size + &cbInfo)) // [out] Written/required sized in buffer + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + CQuickBytes qbInfo; + ACTIVATION_CONTEXT_DETAILED_INFORMATION *pInfo = NULL; + + pInfo = (ACTIVATION_CONTEXT_DETAILED_INFORMATION *)qbInfo.AllocNoThrow(cbInfo); + IfNullGo(pInfo); + + if (WszQueryActCtxW(0, // Flags + hActCtx, // Activation context being queried + NULL, // Specific to the information class + ActivationContextDetailedInformation, + // Information class - detailed information + pInfo, // [out] Buffer + cbInfo, // [in] Buffer size + &cbInfo) && // [out] Written size in the buffer + pInfo->ulAppDirPathType == ACTIVATION_CONTEXT_PATH_TYPE_WIN32_FILE) + { // Application manifest was loaded from Win32 file (not an URL, AssemblyRef etc.) + WCHAR *wszConfigFileName = NULL; + CQuickWSTR qwszConfigFileName; + + if (pInfo->lpRootConfigurationPath != NULL) + { // Configuration file name is provided, use it + wszConfigFileName = (WCHAR *)pInfo->lpRootConfigurationPath; + } + else if (pInfo->lpRootManifestPath != NULL) + { // Manifest file name is provided, use it + SIZE_T cManifestFileName = wcslen(pInfo->lpRootManifestPath); + if (cManifestFileName != 0) + { // Manifest file does exist + WCHAR wszConfigFileExtension[] = W(".config"); + SIZE_T cConfigFileExtension = sizeof(wszConfigFileExtension) / sizeof(WCHAR); + // Allocate space for manifest file name + .config + terminating null + // (included in .config extension) + SIZE_T cConfigFileNameAllocated = cManifestFileName + cConfigFileExtension; + wszConfigFileName = qwszConfigFileName.AllocNoThrow(cConfigFileNameAllocated); + IfNullGo(wszConfigFileName); + // Use manifest file name as the template for config file name + wcscpy_s(wszConfigFileName, cConfigFileNameAllocated, pInfo->lpRootManifestPath); + + // Find the last extension in the manifest file name (or NULL if not found) + LPWSTR wszLastExtension = wcsrchr(wszConfigFileName, W('.')); + // Is the manifest file in an external separate file? + if ((wszLastExtension != NULL) && (_wcsicmp(wszLastExtension, W(".exe")) != 0)) + { // It is an external manifest file (with .manifest or similar extension) + // Excluded are files without an extension and with .exe extension (embeded + // manifest in executable resources) + + // Strip the last extension in the manifest file name + *wszLastExtension = 0; + } + // Manifest file name has stripped last extension (.manifest) + + // Append the .config extension behind the manifest file name + wcscat_s(wszConfigFileName, + cConfigFileNameAllocated, + wszConfigFileExtension); + } + } + + // Do we have a configuration file name? + if (wszConfigFileName != NULL) + { // We have a configuration file name + // Get the real configuration file name length (including terminating null) + cConfigFileName = wcslen(wszConfigFileName) + 1; + // Check the output buffer - is its size sufficient? + if ((wszBuffer == NULL) || (cConfigFileName > cBuffer)) + { // Insufficient output buffer (too small or not passed) + IfFailGo(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)); + } + else + { // Fill output buffer + wcscpy_s(wszBuffer, cConfigFileName, wszConfigFileName); + } + } + } + } + } + +ErrExit: + // Should we return config file name size? + if (pcNameSize != NULL) + { // We should return name size + // Return either copied size or potentially copied size + *pcNameSize = cConfigFileName; + } + return hr; +} + +HRESULT GetApplicationPathFromWin32Manifest(__out_ecount(dwBuffer) WCHAR* buffer, SIZE_T dwBuffer, SIZE_T* pSize) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FAULT; + + HRESULT hr = S_OK; + + // Get the basic activation context first. + ACTIVATION_CONTEXT_DETAILED_INFORMATION* pInfo = NULL; + SIZE_T length = 0; + + HANDLE hActCtx = NULL; + SIZE_T nCount = 0; + + if (!WszQueryActCtxW(0, hActCtx, NULL, ActivationContextDetailedInformation, + NULL, nCount, &nCount)) + { + + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + + pInfo = (ACTIVATION_CONTEXT_DETAILED_INFORMATION*) alloca(nCount); + + if (WszQueryActCtxW(0, hActCtx, NULL, ActivationContextDetailedInformation, + pInfo, nCount, &nCount) && + pInfo->ulAppDirPathType == ACTIVATION_CONTEXT_PATH_TYPE_WIN32_FILE) + { + + if(pInfo->lpAppDirPath) { + length = wcslen(pInfo->lpAppDirPath) + 1; + if(length > dwBuffer || buffer == NULL) { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + else { + wcscpy_s(buffer, dwBuffer, pInfo->lpAppDirPath); + } + } + + } + } + } + if(pSize) *pSize = length; + return hr; +} diff --git a/src/utilcode/tlbutils.cpp b/src/utilcode/tlbutils.cpp new file mode 100644 index 0000000000..88906bac64 --- /dev/null +++ b/src/utilcode/tlbutils.cpp @@ -0,0 +1,222 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// +// Utilities used to help manipulating typelibs. + +#include "stdafx.h" // Precompiled header key. + +#include "tlbutils.h" +#include "dispex.h" +#include "posterror.h" +#include "ndpversion.h" + +#define CUSTOM_MARSHALER_ASM ", CustomMarshalers, Version=" VER_ASSEMBLYVERSION_STR ", Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + +static const LPCWSTR DLL_EXTENSION = {W(".dll")}; +static const int DLL_EXTENSION_LEN = 4; +static const LPCWSTR EXE_EXTENSION = {W(".exe")}; +static const int EXE_EXTENSION_LEN = 4; + +const StdConvertibleItfInfo aStdConvertibleInterfaces[] = +{ + { "System.Runtime.InteropServices.Expando.IExpando", (GUID*)&IID_IDispatchEx, + "System.Runtime.InteropServices.CustomMarshalers.ExpandoToDispatchExMarshaler" CUSTOM_MARSHALER_ASM, "IExpando" }, + + { "System.Reflection.IReflect", (GUID*)&IID_IDispatchEx, + "System.Runtime.InteropServices.CustomMarshalers.ExpandoToDispatchExMarshaler" CUSTOM_MARSHALER_ASM, "IReflect" }, + + { "System.Collections.IEnumerator", (GUID*)&IID_IEnumVARIANT, + "System.Runtime.InteropServices.CustomMarshalers.EnumeratorToEnumVariantMarshaler" CUSTOM_MARSHALER_ASM, "" }, + + { "System.Type", (GUID*)&IID_ITypeInfo, + "System.Runtime.InteropServices.CustomMarshalers.TypeToTypeInfoMarshaler" CUSTOM_MARSHALER_ASM, "" }, +}; + +// This method returns the custom marshaler info to convert the native interface +// to its managed equivalent. Or null if the interface is not a standard convertible interface. +const StdConvertibleItfInfo *GetConvertionInfoFromNativeIID(REFGUID rGuidNativeItf) +{ + CONTRACT (const StdConvertibleItfInfo*) + { + NOTHROW; + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + // Look in the table of interfaces that have standard convertions to see if the + // specified interface is there. + for (int i = 0; i < sizeof(aStdConvertibleInterfaces) / sizeof(StdConvertibleItfInfo); i++) + { + if (IsEqualGUID(rGuidNativeItf, *(aStdConvertibleInterfaces[i].m_pNativeTypeIID))) + RETURN &aStdConvertibleInterfaces[i]; + } + + // The interface is not in the table. + RETURN NULL; +} + +//***************************************************************************** +// Given a typelib, determine the managed namespace name. +//***************************************************************************** +HRESULT GetNamespaceNameForTypeLib( // S_OK or error. + ITypeLib *pITLB, // [IN] The TypeLib. + BSTR *pwzNamespace) // [OUT] Put the namespace name here. +{ + CONTRACTL + { + DISABLED(NOTHROW); // PostError goes down a throwing path right now. Revisit this when fixed. + PRECONDITION(CheckPointer(pITLB)); + PRECONDITION(CheckPointer(pwzNamespace)); + } + CONTRACTL_END; + + HRESULT hr = S_OK; // A result. + ITypeLib2 *pITLB2=0; //For getting custom value. + TLIBATTR *pAttr=0; // Typelib attributes. + BSTR szPath=0; // Typelib path. + + // If custom attribute for namespace exists, use it. + if (pITLB->QueryInterface(IID_ITypeLib2, (void **)&pITLB2) == S_OK) + { + VARIANT vt; + VariantInit(&vt); + if (pITLB2->GetCustData(GUID_ManagedName, &vt) == S_OK) + { + if (V_VT(&vt) == VT_BSTR) + { + // If the namespace ends with .dll then remove the extension. + LPWSTR pDest = wcsstr(vt.bstrVal, DLL_EXTENSION); + if (pDest && (pDest[DLL_EXTENSION_LEN] == 0 || pDest[DLL_EXTENSION_LEN] == ' ')) + *pDest = 0; + + if (!pDest) + { + // If the namespace ends with .exe then remove the extension. + pDest = wcsstr(vt.bstrVal, EXE_EXTENSION); + if (pDest && (pDest[EXE_EXTENSION_LEN] == 0 || pDest[EXE_EXTENSION_LEN] == ' ')) + *pDest = 0; + } + + if (pDest) + { + // We removed the extension so re-allocate a string of the new length. + *pwzNamespace = SysAllocString(vt.bstrVal); + SysFreeString(vt.bstrVal); + } + else + { + // There was no extension to remove so we can use the string returned + // by GetCustData(). + *pwzNamespace = vt.bstrVal; + } + + goto ErrExit; + } + else + { + VariantClear(&vt); + } + } + } + + // No custom attribute, use library name. + IfFailGo(pITLB->GetDocumentation(MEMBERID_NIL, pwzNamespace, 0, 0, 0)); + +ErrExit: + if (szPath) + ::SysFreeString(szPath); + if (pAttr) + pITLB->ReleaseTLibAttr(pAttr); + if (pITLB2) + pITLB2->Release(); + + return hr; +} // HRESULT GetNamespaceNameForTypeLib() + +//***************************************************************************** +// Given an ITypeInfo, determine the managed name. Optionally supply a default +// namespace, otherwise derive namespace from containing typelib. +//***************************************************************************** +HRESULT GetManagedNameForTypeInfo( // S_OK or error. + ITypeInfo *pITI, // [IN] The TypeInfo. + LPCWSTR wzNamespace, // [IN, OPTIONAL] Default namespace name. + LPCWSTR wzAsmName, // [IN, OPTIONAL] Assembly name. + BSTR *pwzName) // [OUT] Put the name here. +{ + CONTRACTL + { + DISABLED(NOTHROW); + PRECONDITION(CheckPointer(pITI)); + PRECONDITION(CheckPointer(wzNamespace, NULL_OK)); + PRECONDITION(CheckPointer(wzAsmName, NULL_OK)); + PRECONDITION(CheckPointer(pwzName)); + } + CONTRACTL_END; + + HRESULT hr = S_OK; // A result. + ITypeInfo2 *pITI2=0; // For getting custom value. + ITypeLib *pITLB=0; // Containing typelib. + + BSTR bstrName=0; // Typeinfo's name. + BSTR bstrNamespace=0; // Typelib's namespace. + int cchFullyQualifiedName; // Size of namespace + name buffer. + int cchAsmName=0; // The size of the assembly name. + int cchAsmQualifiedName=0; // The size of the assembly qualified name buffer. + CQuickArray<WCHAR> qbFullyQualifiedName; // The fully qualified type name. + + // Check for a custom value with name. + if (pITI->QueryInterface(IID_ITypeInfo2, (void **)&pITI2) == S_OK) + { + VARIANT vt; // For getting custom value. + ::VariantInit(&vt); + if (pITI2->GetCustData(GUID_ManagedName, &vt) == S_OK && vt.vt == VT_BSTR) + { // There is a custom value with the name. Just believe it. + *pwzName = vt.bstrVal; + vt.bstrVal = 0; + vt.vt = VT_EMPTY; + goto ErrExit; + } + } + + // Still need name, get the namespace. + if (wzNamespace == 0) + { + IfFailGo(pITI->GetContainingTypeLib(&pITLB, 0)); + IfFailGo(GetNamespaceNameForTypeLib(pITLB, &bstrNamespace)); + wzNamespace = bstrNamespace; + } + + // Get the name, and combine with namespace. + IfFailGo(pITI->GetDocumentation(MEMBERID_NIL, &bstrName, 0,0,0)); + cchFullyQualifiedName = (int)(wcslen(bstrName) + wcslen(wzNamespace) + 1); + IfFailGo(qbFullyQualifiedName.ReSizeNoThrow(cchFullyQualifiedName + 1)); + ns::MakePath(qbFullyQualifiedName.Ptr(), cchFullyQualifiedName + 1, wzNamespace, bstrName); + + // If the assembly name is specified, then add it to the type name. + if (wzAsmName) + { + cchAsmName = (int)wcslen(wzAsmName); + cchAsmQualifiedName = cchFullyQualifiedName + cchAsmName + 3; + IfNullGo(*pwzName = ::SysAllocStringLen(0, cchAsmQualifiedName)); + ns::MakeAssemblyQualifiedName(*pwzName, cchAsmQualifiedName, qbFullyQualifiedName.Ptr(), cchFullyQualifiedName, wzAsmName, cchAsmName); + } + else + { + IfNullGo(*pwzName = ::SysAllocStringLen(qbFullyQualifiedName.Ptr(), cchFullyQualifiedName)); + } + +ErrExit: + if (bstrName) + ::SysFreeString(bstrName); + if (bstrNamespace) + ::SysFreeString(bstrNamespace); + if (pITLB) + pITLB->Release(); + if (pITI2) + pITI2->Release(); + + return (hr); +} // HRESULT GetManagedNameForTypeInfo() diff --git a/src/utilcode/tls.cpp b/src/utilcode/tls.cpp new file mode 100644 index 0000000000..c80d8c3d03 --- /dev/null +++ b/src/utilcode/tls.cpp @@ -0,0 +1,272 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +/* TLS.CPP: + * + + * + * Encapsulates TLS access for maximum performance. + * + */ + +#include "stdafx.h" + +#include "unsafe.h" +#include "tls.h" +#include "contract.h" +#include "corerror.h" +#include "ex.h" +#include "clrhost.h" + +#ifndef SELF_NO_HOST +#include "clrconfig.h" +#endif + +#include "clrnt.h" + +#ifndef SELF_NO_HOST + +//--------------------------------------------------------------------------- +// Win95 and WinNT store the TLS in different places relative to the +// fs:[0]. This api reveals which. Can also return TLSACCESS_GENERIC if +// no info is available about the Thread location (you have to use the TlsGetValue +// api.) This is intended for use by stub generators that want to inline TLS +// access. +//--------------------------------------------------------------------------- +TLSACCESSMODE GetTLSAccessMode(DWORD tlsIndex) +{ + // Static contracts because this is used by contract infrastructure + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + TLSACCESSMODE tlsAccessMode = TLSACCESS_GENERIC; + +#ifdef _DEBUG + // Debug builds allow user to throw a switch to force use of the generic + // (non-optimized) Thread/AppDomain getters. Even if the user doesn't throw + // the switch, force tests to go down the generic getter code path about 1% of the + // time so it's exercised a couple dozen times during each devbvt run. + if ((CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_UseGenericTlsGetters) != 0) || DbgRandomOnExe(.01)) + return TLSACCESS_GENERIC; +#endif + + if (tlsIndex < TLS_MINIMUM_AVAILABLE) + { + tlsAccessMode = TLSACCESS_WNT; + } + else + if (tlsIndex < (TLS_MINIMUM_AVAILABLE + TLS_EXPANSION_SLOTS)) + { + // Expansion slots are lazily created at the first call to + // TlsGetValue on a thread, and the code we generate + // assumes that the expansion slots will exist. + // + // <TODO> On newer flavors of NT we could use the vectored + // exception handler to take the AV, call TlsGetValue, and + // resume execution at the start of the getter. </TODO> + tlsAccessMode = TLSACCESS_GENERIC;//TLSACCESS_WNT_HIGH; + } + else + { + // + // If the app verifier is enabled, TLS indices + // are faked to help detect invalid handle use. + // + } + + return tlsAccessMode; +} + +//--------------------------------------------------------------------------- +// Creates a platform-optimized version of TlsGetValue compiled +// for a particular index. Can return NULL. +//--------------------------------------------------------------------------- +// A target for the optimized getter can be passed in, this is +// useful so that code can avoid an indirect call for the GetThread +// and GetAppDomain calls for instance. If NULL is passed then +// we will allocate from the executeable heap. +POPTIMIZEDTLSGETTER MakeOptimizedTlsGetter(DWORD tlsIndex, LPVOID pBuffer, SIZE_T cbBuffer, POPTIMIZEDTLSGETTER pGenericImpl, BOOL fForceGeneric) +{ + // Static contracts because this is used by contract infrastructure + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + ARM_ONLY(pBuffer = ThumbCodeToDataPointer<BYTE*>(pBuffer)); + + // Buffer that should be big enough to encode the TLS getter on any reasonable platform + TADDR patch[4 INDEBUG(+4 /* last error trashing */)]; + + PBYTE pPatch = (PBYTE)&patch; + + TLSACCESSMODE mode = fForceGeneric ? TLSACCESS_GENERIC : GetTLSAccessMode(tlsIndex); + +#if defined(_DEBUG) + if (mode != TLSACCESS_GENERIC) + { + // + // Trash last error in debug builds + // + +#ifdef _TARGET_X86_ + *((DWORD*) (pPatch + 0)) = 0x05c764; // mov dword ptr fs:[offsetof(TEB, LastErrorValue)], LAST_ERROR_TRASH_VALUE + *((DWORD*) (pPatch + 3)) = offsetof(TEB, LastErrorValue); + *((DWORD*) (pPatch + 7)) = LAST_ERROR_TRASH_VALUE; + pPatch += 11; +#endif // _TARGET_X86_ + +#ifdef _TARGET_AMD64_ + // iDNA doesn't like writing directly to gs:[nn] + *((UINT64*)(pPatch + 0)) = 0x25048b4865; // mov rax, gs:[offsetof(TEB, NtTib.Self)] + *((DWORD*) (pPatch + 5)) = offsetof(TEB, NtTib.Self); + *((WORD*) (pPatch + 9)) = 0x80c7; // mov dword ptr [rax + offsetof(TEB, LastErrorValue)], LAST_ERROR_TRASH_VALUE + *((DWORD*) (pPatch + 11)) = offsetof(TEB, LastErrorValue); + *((DWORD*) (pPatch + 15)) = LAST_ERROR_TRASH_VALUE; + pPatch += 19; +#endif + } +#endif // _DEBUG + + switch (mode) + { +#ifdef _TARGET_X86_ + case TLSACCESS_WNT: + *((WORD*) (pPatch + 0)) = 0xa164; // mov eax, fs:[IMM32] + *((DWORD*) (pPatch + 2)) = offsetof(TEB, TlsSlots) + tlsIndex * sizeof(void*); + *((BYTE*) (pPatch + 6)) = 0xc3; // retn + pPatch += 7; + break; + + case TLSACCESS_GENERIC: + if (pGenericImpl == NULL) + return NULL; + + _ASSERTE(pBuffer != NULL); + *((BYTE*) (pPatch + 0)) = 0xE9; // jmp pGenericImpl + TADDR rel32 = ((TADDR)pGenericImpl - ((TADDR)pBuffer + 1 + sizeof(INT32))); + *((INT32*) (pPatch + 1)) = (INT32)rel32; + pPatch += 5; + break; +#endif // _TARGET_X86_ + +#ifdef _TARGET_AMD64_ + case TLSACCESS_WNT: + *((UINT64*)(pPatch + 0)) = 0x25048b4865; // mov rax, gs:[IMM32] + *((DWORD*) (pPatch + 5)) = offsetof(TEB, TlsSlots) + (tlsIndex * sizeof(void*)); + *((BYTE*) (pPatch + 9)) = 0xc3; // return + pPatch += 10; + break; + + case TLSACCESS_GENERIC: + if (pGenericImpl == NULL) + return NULL; + + _ASSERTE(pBuffer != NULL); + *((BYTE*) (pPatch + 0)) = 0xE9; // jmp pGenericImpl + TADDR rel32 = ((TADDR)pGenericImpl - ((TADDR)pBuffer + 1 + sizeof(INT32))); + _ASSERTE((INT64)(INT32)rel32 == (INT64)rel32); + *((INT32*) (pPatch + 1)) = (INT32)rel32; + pPatch += 5; + + *pPatch++ = 0xCC; // Make sure there is full 8 bytes worth of data + *pPatch++ = 0xCC; + *pPatch++ = 0xCC; + break; + +#endif // _TARGET_AMD64_ + +#ifdef _TARGET_ARM_ + case TLSACCESS_WNT: + { + WORD slotOffset = (WORD)(offsetof(TEB, TlsSlots) + tlsIndex * sizeof(void*)); + _ASSERTE(slotOffset < 4096); + + WORD *pInstr = (WORD*)pPatch; + + *pInstr++ = 0xee1d; // mrc p15, 0, r0, c13, c0, 2 + *pInstr++ = 0x0f50; + *pInstr++ = 0xf8d0; // ldr r0, [r0, #slotOffset] + *pInstr++ = slotOffset; + *pInstr++ = 0x4770; // bx lr + + pPatch = (PBYTE)pInstr; + } + break; + + case TLSACCESS_GENERIC: + { + if (pGenericImpl == NULL) + return NULL; + + _ASSERTE(pBuffer != NULL); + + *(DWORD *)pPatch = 0x9000F000; // b pGenericImpl + PutThumb2BlRel24((WORD*)pPatch, (TADDR)pGenericImpl - ((TADDR)pBuffer + 4 + THUMB_CODE)); + + pPatch += 4; + } + break; +#endif // _TARGET_ARM_ + } + + SIZE_T cbCode = (TADDR)pPatch - (TADDR)&patch; + _ASSERTE(cbCode <= sizeof(patch)); + + if (pBuffer != NULL) + { + _ASSERTE_ALL_BUILDS("clr/src/utilcode/tls.cpp", cbCode <= cbBuffer); + + // We assume that the first instruction of the buffer is a short jump to dummy helper + // that can be atomically overwritten to avoid races with other threads executing the code. + // It is the same basic technique as hot patching. + + // Assert on all builds to make sure that retail optimizations are not affecting the alignment. + _ASSERTE_ALL_BUILDS("clr/src/utilcode/tls.cpp", IS_ALIGNED((void*)pBuffer, sizeof(TADDR))); + + // Size of short jump that gets patched last. + if (cbCode > sizeof(TADDR)) + { + memcpy((BYTE *)pBuffer + sizeof(TADDR), &patch[1], cbCode - sizeof(TADDR)); + FlushInstructionCache(GetCurrentProcess(), (BYTE *)pBuffer + sizeof(TADDR), cbCode - sizeof(TADDR)); + } + + // Make sure that the the dummy implementation still works. + _ASSERTE(((POPTIMIZEDTLSGETTER)ARM_ONLY(DataPointerToThumbCode<BYTE*>)(pBuffer))() == NULL); + + // It is important for this write to happen atomically + VolatileStore<TADDR>((TADDR *)pBuffer, patch[0]); + + FlushInstructionCache(GetCurrentProcess(), (BYTE *)pBuffer, sizeof(TADDR)); + } + else + { + pBuffer = (BYTE*) new (executable, nothrow) BYTE[cbCode]; + if (pBuffer == NULL) + return NULL; + + memcpy(pBuffer, &patch, cbCode); + + FlushInstructionCache(GetCurrentProcess(), pBuffer, cbCode); + } + + return (POPTIMIZEDTLSGETTER)ARM_ONLY(DataPointerToThumbCode<BYTE*>)(pBuffer); +} + + +//--------------------------------------------------------------------------- +// Frees a function created by MakeOptimizedTlsGetter(). +//--------------------------------------------------------------------------- +VOID FreeOptimizedTlsGetter(POPTIMIZEDTLSGETTER pOptimizedTlsGetter) +{ + // Static contracts because this is used by contract infrastructure + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + + BYTE* pGetter = (BYTE*)pOptimizedTlsGetter; +#ifdef _TARGET_ARM_ + pGetter = ThumbCodeToDataPointer<BYTE*>(pGetter); +#endif + DeleteExecutable(pGetter); +} + +#endif // !SELF_NO_HOST diff --git a/src/utilcode/util.cpp b/src/utilcode/util.cpp new file mode 100644 index 0000000000..865009d38e --- /dev/null +++ b/src/utilcode/util.cpp @@ -0,0 +1,4091 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// util.cpp +// + +// +// This contains a bunch of C++ utility classes. +// +//***************************************************************************** +#include "stdafx.h" // Precompiled header key. +#include "utilcode.h" +#include "metadata.h" +#include "ex.h" +#include "pedecoder.h" +#include "loaderheap.h" +#include "sigparser.h" +#include "cor.h" + +#ifndef FEATURE_CORECLR +#include "metahost.h" +#endif // !FEATURE_CORECLR + +const char g_RTMVersion[]= "v1.0.3705"; + +#ifndef DACCESS_COMPILE +UINT32 g_nClrInstanceId = 0; +#endif //!DACCESS_COMPILE + +//********** Code. ************************************************************ + +#if defined(FEATURE_COMINTEROP) && !defined(FEATURE_CORESYSTEM) +extern WinRTStatusEnum gWinRTStatus = WINRT_STATUS_UNINITED; +#endif // FEATURE_COMINTEROP && !FEATURE_CORESYSTEM + +#if defined(FEATURE_COMINTEROP) && !defined(FEATURE_CORESYSTEM) +//------------------------------------------------------------------------------ +// +// Attempt to detect the presense of Windows Runtime support on the current OS. +// Our algorithm to do this is to ensure that: +// 1. combase.dll exists +// 2. combase.dll contains a RoInitialize export +// + +void InitWinRTStatus() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_CANNOT_TAKE_LOCK; + STATIC_CONTRACT_SO_TOLERANT; + + WinRTStatusEnum winRTStatus = WINRT_STATUS_UNSUPPORTED; + + const WCHAR wszComBaseDll[] = W("\\combase.dll"); + const SIZE_T cchComBaseDll = _countof(wszComBaseDll); + + WCHAR wszComBasePath[MAX_PATH + 1]; + const SIZE_T cchComBasePath = _countof(wszComBasePath); + + ZeroMemory(wszComBasePath, cchComBasePath * sizeof(wszComBasePath[0])); + + UINT cchSystemDirectory = WszGetSystemDirectory(wszComBasePath, MAX_PATH); + + // Make sure that we're only probing in the system directory. If we can't find the system directory, or + // we find it but combase.dll doesn't fit into it, we'll fall back to a safe default of saying that WinRT + // is simply not present. + if (cchSystemDirectory > 0 && cchComBasePath - cchSystemDirectory >= cchComBaseDll) + { + if (wcscat_s(wszComBasePath, wszComBaseDll) == 0) + { + HModuleHolder hComBase(WszLoadLibrary(wszComBasePath)); + if (hComBase != NULL) + { + FARPROC activateInstace = GetProcAddress(hComBase, "RoInitialize"); + if (activateInstace != NULL) + { + winRTStatus = WINRT_STATUS_SUPPORTED; + } + } + } + } + + gWinRTStatus = winRTStatus; +} +#endif // FEATURE_COMINTEROP && !FEATURE_CORESYSTEM +//***************************************************************************** +// Convert a string of hex digits into a hex value of the specified # of bytes. +//***************************************************************************** +HRESULT GetHex( // Return status. + LPCSTR szStr, // String to convert. + int size, // # of bytes in pResult. + void *pResult) // Buffer for result. +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + int count = size * 2; // # of bytes to take from string. + unsigned int Result = 0; // Result value. + char ch; + + _ASSERTE(size == 1 || size == 2 || size == 4); + + while (count-- && (ch = *szStr++) != '\0') + { + switch (ch) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + Result = 16 * Result + (ch - '0'); + break; + + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + Result = 16 * Result + 10 + (ch - 'A'); + break; + + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + Result = 16 * Result + 10 + (ch - 'a'); + break; + + default: + return (E_FAIL); + } + } + + // Set the output. + switch (size) + { + case 1: + *((BYTE *) pResult) = (BYTE) Result; + break; + + case 2: + *((WORD *) pResult) = (WORD) Result; + break; + + case 4: + *((DWORD *) pResult) = Result; + break; + + default: + _ASSERTE(0); + break; + } + return (S_OK); +} + +//***************************************************************************** +// Convert a pointer to a string into a GUID. +//***************************************************************************** +HRESULT LPCSTRToGuid( // Return status. + LPCSTR szGuid, // String to convert. + GUID *psGuid) // Buffer for converted GUID. +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + int i; + + // Verify the surrounding syntax. + if (strlen(szGuid) != 38 || szGuid[0] != '{' || szGuid[9] != '-' || + szGuid[14] != '-' || szGuid[19] != '-' || szGuid[24] != '-' || szGuid[37] != '}') + { + return (E_FAIL); + } + + // Parse the first 3 fields. + if (FAILED(GetHex(szGuid + 1, 4, &psGuid->Data1))) + return E_FAIL; + if (FAILED(GetHex(szGuid + 10, 2, &psGuid->Data2))) + return E_FAIL; + if (FAILED(GetHex(szGuid + 15, 2, &psGuid->Data3))) + return E_FAIL; + + // Get the last two fields (which are byte arrays). + for (i = 0; i < 2; ++i) + { + if (FAILED(GetHex(szGuid + 20 + (i * 2), 1, &psGuid->Data4[i]))) + { + return E_FAIL; + } + } + for (i=0; i < 6; ++i) + { + if (FAILED(GetHex(szGuid + 25 + (i * 2), 1, &psGuid->Data4[i+2]))) + { + return E_FAIL; + } + } + return S_OK; +} + +// +// +// Global utility functions. +// +// + + + +typedef HRESULT __stdcall DLLGETCLASSOBJECT(REFCLSID rclsid, + REFIID riid, + void **ppv); + +EXTERN_C const IID _IID_IClassFactory = + {0x00000001, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; + +// ---------------------------------------------------------------------------- +// FakeCoCreateInstanceEx +// +// Description: +// A private function to do the equivalent of a CoCreateInstance in cases where we +// can't make the real call. Use this when, for instance, you need to create a symbol +// reader in the Runtime but we're not CoInitialized. Obviously, this is only good +// for COM objects for which CoCreateInstance is just a glorified find-and-load-me +// operation. +// +// Arguments: +// * rclsid - [in] CLSID of object to instantiate +// * wszDllPath [in] - Path to profiler DLL. If wszDllPath is NULL, FakeCoCreateInstanceEx +// will look up the registry to find the path of the COM dll associated with rclsid. +// If the path ends in a backslash, FakeCoCreateInstanceEx will treat this as a prefix +// if the InprocServer32 found in the registry is a simple filename (not a full path). +// This allows the caller to specify the directory in which the InprocServer32 should +// be found. Also, if this path is provided and the InprocServer32 is MSCOREE.DLL, then +// the Server value is used instead, if it exists. +// * riid - [in] IID of interface on object to return in ppv +// * ppv - [out] Pointer to implementation of requested interface +// * phmodDll - [out] HMODULE of DLL that was loaded to instantiate the COM object. +// The caller may eventually call FreeLibrary() on this if it can be determined +// that we no longer reference the generated COM object or dependencies. Else, the +// caller may ignore this and the DLL will stay loaded forever. If caller +// specifies phmodDll==NULL, then this parameter is ignored and the HMODULE is not +// returned. +// +// Return Value: +// HRESULT indicating success or failure. +// +// Notes: +// * (*phmodDll) on [out] may always be trusted, even if this function returns an +// error. Therefore, even if creation of the COM object failed, if (*phmodDll != +// NULL), then the DLL was actually loaded. The caller may wish to call +// FreeLibrary on (*phmodDll) in such a case. +HRESULT FakeCoCreateInstanceEx(REFCLSID rclsid, + LPCWSTR wszDllPath, + REFIID riid, + void ** ppv, + HMODULE * phmodDll) +{ + CONTRACTL + { + THROWS; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + // Call the function to get a class factory for the rclsid passed in. + ReleaseHolder<IClassFactory> classFactory; + HModuleHolder hDll; + IfFailRet(FakeCoCallDllGetClassObject(rclsid, wszDllPath, _IID_IClassFactory, (void**)&classFactory, &hDll)); + + // Ask the class factory to create an instance of the + // necessary object. + IfFailRet(classFactory->CreateInstance(NULL, riid, ppv)); + + hDll.SuppressRelease(); + + if (phmodDll != NULL) + { + *phmodDll = hDll.GetValue(); + } + + return hr; +} + +HRESULT FakeCoCallDllGetClassObject(REFCLSID rclsid, + LPCWSTR wszDllPath, + REFIID riid, + void ** ppv, + HMODULE * phmodDll) +{ + CONTRACTL + { + THROWS; + } + CONTRACTL_END; + + _ASSERTE(ppv != NULL); + + HRESULT hr = S_OK; + + if (phmodDll != NULL) + { // Initialize [out] HMODULE (if it was requested) + *phmodDll = NULL; + } + + bool fIsDllPathPrefix = (wszDllPath != NULL) && (wszDllPath[wcslen(wszDllPath) - 1] == W('\\')); + + // - An empty string will be treated as NULL. + // - A string ending will a backslash will be treated as a prefix for where to look for the DLL + // if the InProcServer32 value is just a DLL name and not a full path. + StackSString ssDllName; + if ((wszDllPath == NULL) || (wszDllPath[0] == W('\0')) || fIsDllPathPrefix) + { +#ifndef FEATURE_PAL + IfFailRet(Clr::Util::Com::FindInprocServer32UsingCLSID(rclsid, ssDllName)); + + EX_TRY + { + if (fIsDllPathPrefix) + { + if (Clr::Util::Com::IsMscoreeInprocServer32(ssDllName)) + { // If the InprocServer32 is mscoree.dll, then we skip the shim and look for + // the corresponding server DLL (if it exists) in the directory provided. + hr = Clr::Util::Com::FindServerUsingCLSID(rclsid, ssDllName); + + if (FAILED(hr)) + { // We don't fail if there is no server object, because in this case we assume that + // the clsid is implemented in the runtime itself (clr.dll) and we do not place + // entries in the registry for this case. + ssDllName.Set(MAIN_CLR_MODULE_NAME_W); + } + } + + SString::Iterator i = ssDllName.Begin(); + if (!ssDllName.Find(i, W('\\'))) + { // If the InprocServer32 is just a DLL name (not a fully qualified path), then + // prefix wszFilePath with wszDllPath. + ssDllName.Insert(i, wszDllPath); + } + } + } + EX_CATCH_HRESULT(hr); + IfFailRet(hr); + + wszDllPath = ssDllName.GetUnicode(); +#else // !FEATURE_PAL + return E_FAIL; +#endif // !FEATURE_PAL + } + _ASSERTE(wszDllPath != NULL); + + // We've got the name of the DLL to load, so load it. + HModuleHolder hDll = WszLoadLibraryEx(wszDllPath, NULL, GetLoadWithAlteredSearchPathFlag()); + if (hDll == NULL) + { + return HRESULT_FROM_GetLastError(); + } + + // We've loaded the DLL, so find the DllGetClassObject function. + DLLGETCLASSOBJECT *dllGetClassObject = (DLLGETCLASSOBJECT*)GetProcAddress(hDll, "DllGetClassObject"); + if (dllGetClassObject == NULL) + { + return HRESULT_FROM_GetLastError(); + } + + // Call the function to get a class object for the rclsid and riid passed in. + IfFailRet(dllGetClassObject(rclsid, riid, ppv)); + + hDll.SuppressRelease(); + + if (phmodDll != NULL) + { + *phmodDll = hDll.GetValue(); + } + + return hr; +} + +#if USE_UPPER_ADDRESS +static BYTE * s_CodeMinAddr; // Preferred region to allocate the code in. +static BYTE * s_CodeMaxAddr; +static BYTE * s_CodeAllocStart; +static BYTE * s_CodeAllocHint; // Next address to try to allocate for code in the preferred region. +#endif + +// +// Use this function to initialize the s_CodeAllocHint +// during startup. base is runtime .dll base address, +// size is runtime .dll virtual size. +// +void InitCodeAllocHint(SIZE_T base, SIZE_T size, int randomPageOffset) +{ +#if USE_UPPER_ADDRESS + +#ifdef _DEBUG + // If GetForceRelocs is enabled we don't constrain the pMinAddr + if (PEDecoder::GetForceRelocs()) + return; +#endif + +// + // If we are using the UPPER_ADDRESS space (on Win64) + // then for any code heap that doesn't specify an address + // range using [pMinAddr..pMaxAddr] we place it in the + // upper address space + // This enables us to avoid having to use long JumpStubs + // to reach the code for our ngen-ed images. + // Which are also placed in the UPPER_ADDRESS space. + // + SIZE_T reach = 0x7FFF0000u; + + // We will choose the preferred code region based on the address of clr.dll. The JIT helpers + // in clr.dll are the most heavily called functions. + s_CodeMinAddr = (base + size > reach) ? (BYTE *)(base + size - reach) : (BYTE *)0; + s_CodeMaxAddr = (base + reach > base) ? (BYTE *)(base + reach) : (BYTE *)-1; + + BYTE * pStart; + + if (s_CodeMinAddr <= (BYTE *)CODEHEAP_START_ADDRESS && + (BYTE *)CODEHEAP_START_ADDRESS < s_CodeMaxAddr) + { + // clr.dll got loaded at its preferred base address? (OS without ASLR - pre-Vista) + // Use the code head start address that does not cause collisions with NGen images. + // This logic is coupled with scripts that we use to assign base addresses. + pStart = (BYTE *)CODEHEAP_START_ADDRESS; + } + else + if (base > UINT32_MAX) + { + // clr.dll got address assigned by ASLR? + // Try to occupy the space as far as possible to minimize collisions with other ASLR assigned + // addresses. Do not start at s_CodeMinAddr exactly so that we can also reach common native images + // that can be placed at higher addresses than clr.dll. + pStart = s_CodeMinAddr + (s_CodeMaxAddr - s_CodeMinAddr) / 8; + } + else + { + // clr.dll missed the base address? + // Try to occupy the space right after it. + pStart = (BYTE *)(base + size); + } + + // Randomize the adddress space + pStart += PAGE_SIZE * randomPageOffset; + + s_CodeAllocStart = pStart; + s_CodeAllocHint = pStart; +#endif +} + +// +// Use this function to reset the s_CodeAllocHint +// after unloading an AppDomain +// +void ResetCodeAllocHint() +{ + LIMITED_METHOD_CONTRACT; +#if USE_UPPER_ADDRESS + s_CodeAllocHint = s_CodeAllocStart; +#endif +} + +// +// Returns TRUE if p is located in near clr.dll that allows us +// to use rel32 IP-relative addressing modes. +// +BOOL IsPreferredExecutableRange(void * p) +{ + LIMITED_METHOD_CONTRACT; +#if USE_UPPER_ADDRESS + if (s_CodeMinAddr <= (BYTE *)p && (BYTE *)p < s_CodeMaxAddr) + return TRUE; +#endif + return FALSE; +} + +// +// Allocate free memory that will be used for executable code +// Handles the special requirements that we have on 64-bit platforms +// where we want the executable memory to be located near clr.dll +// +BYTE * ClrVirtualAllocExecutable(SIZE_T dwSize, + DWORD flAllocationType, + DWORD flProtect) +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + +#if USE_UPPER_ADDRESS + // + // If we are using the UPPER_ADDRESS space (on Win64) + // then for any heap that will contain executable code + // we will place it in the upper address space + // + // This enables us to avoid having to use JumpStubs + // to reach the code for our ngen-ed images on x64, + // since they are also placed in the UPPER_ADDRESS space. + // + BYTE * pHint = s_CodeAllocHint; + + if (dwSize <= (SIZE_T)(s_CodeMaxAddr - s_CodeMinAddr) && pHint != NULL) + { + // Try to allocate in the preferred region after the hint + BYTE * pResult = ClrVirtualAllocWithinRange(pHint, s_CodeMaxAddr, dwSize, flAllocationType, flProtect); + + if (pResult != NULL) + { + s_CodeAllocHint = pResult + dwSize; + return pResult; + } + + // Try to allocate in the preferred region before the hint + pResult = ClrVirtualAllocWithinRange(s_CodeMinAddr, pHint + dwSize, dwSize, flAllocationType, flProtect); + + if (pResult != NULL) + { + s_CodeAllocHint = pResult + dwSize; + return pResult; + } + + s_CodeAllocHint = NULL; + } + + // Fall through to +#endif // USE_UPPER_ADDRESS + + return (BYTE *) ClrVirtualAlloc (NULL, dwSize, flAllocationType, flProtect); + +} + +// +// Allocate free memory with specific alignment. +// +LPVOID ClrVirtualAllocAligned(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect, SIZE_T alignment) +{ + // Verify that the alignment is a power of 2 + _ASSERTE(alignment != 0); + _ASSERTE((alignment & (alignment - 1)) == 0); + +#ifndef FEATURE_PAL + + // The VirtualAlloc on Windows ensures 64kB alignment + _ASSERTE(alignment <= 0x10000); + return ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect); + +#else // !FEATURE_PAL + + // UNIXTODO: Add a specialized function to PAL so that we don't have to waste memory + dwSize += alignment; + SIZE_T addr = (SIZE_T)ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect); + return (LPVOID)((addr + (alignment - 1)) & ~(alignment - 1)); + +#endif // !FEATURE_PAL +} + +// Reserves free memory within the range [pMinAddr..pMaxAddr] using +// ClrVirtualQuery to find free memory and ClrVirtualAlloc to reserve it. +// +// This method only supports the flAllocationType of MEM_RESERVE +// Callers also should set dwSize to a multiple of sysInfo.dwAllocationGranularity (64k). +// That way they can reserve a large region and commit smaller sized pages +// from that region until it fills up. +// +// This functions returns the reserved memory block upon success +// +// It returns NULL when it fails to find any memory that satisfies +// the range. +// + +#ifdef _DEBUG +static DWORD ShouldInjectFaultInRange() +{ + static DWORD fInjectFaultInRange = 99; + + if (fInjectFaultInRange == 99) + fInjectFaultInRange = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_InjectFault) & 0x40); + return fInjectFaultInRange; +} +#endif + +BYTE * ClrVirtualAllocWithinRange(const BYTE *pMinAddr, + const BYTE *pMaxAddr, + SIZE_T dwSize, + DWORD flAllocationType, + DWORD flProtect) +{ + CONTRACTL + { + NOTHROW; + PRECONDITION(dwSize != 0); + PRECONDITION(flAllocationType == MEM_RESERVE); + } + CONTRACTL_END; + + BYTE *pResult = NULL; + // + // First lets normalize the pMinAddr and pMaxAddr values + // + // If pMinAddr is NULL then set it to BOT_MEMORY + if ((pMinAddr == 0) || (pMinAddr < (BYTE *) BOT_MEMORY)) + { + pMinAddr = (BYTE *) BOT_MEMORY; + } + + // If pMaxAddr is NULL then set it to TOP_MEMORY + if ((pMaxAddr == 0) || (pMaxAddr > (BYTE *) TOP_MEMORY)) + { + pMaxAddr = (BYTE *) TOP_MEMORY; + } + + // If pMinAddr is BOT_MEMORY and pMaxAddr is TOP_MEMORY + // then we can call ClrVirtualAlloc instead + if ((pMinAddr == (BYTE *) BOT_MEMORY) && (pMaxAddr == (BYTE *) TOP_MEMORY)) + { + return (BYTE*) ClrVirtualAlloc(NULL, dwSize, flAllocationType, flProtect); + } + + // If pMaxAddr is not greater than pMinAddr we can not make an allocation + if (dwSize == 0 || pMaxAddr <= pMinAddr) + { + return NULL; + } + + // We will do one scan: [pMinAddr .. pMaxAddr] + // Align to 64k. See docs for VirtualAllocEx and lpAddress and 64k alignment for reasons. + BYTE *tryAddr = (BYTE *)ALIGN_UP((BYTE *)pMinAddr, VIRTUAL_ALLOC_RESERVE_GRANULARITY); + + // Now scan memory and try to find a free block of the size requested. + while ((tryAddr + dwSize) <= (BYTE *) pMaxAddr) + { + MEMORY_BASIC_INFORMATION mbInfo; + + // Use VirtualQuery to find out if this address is MEM_FREE + // + if (!ClrVirtualQuery((LPCVOID)tryAddr, &mbInfo, sizeof(mbInfo))) + break; + + // Is there enough memory free from this start location? + if ((mbInfo.State == MEM_FREE) && (mbInfo.RegionSize >= (SIZE_T) dwSize)) + { + // Try reserving the memory using VirtualAlloc now + pResult = (BYTE*) ClrVirtualAlloc(tryAddr, dwSize, MEM_RESERVE, flProtect); + + if (pResult != NULL) + { + return pResult; + } +#ifdef _DEBUG + // pResult == NULL + else if (ShouldInjectFaultInRange()) + { + return NULL; + } +#endif // _DEBUG + + // We could fail in a race. Just move on to next region and continue trying + tryAddr = tryAddr + VIRTUAL_ALLOC_RESERVE_GRANULARITY; + } + else + { + // Try another section of memory + tryAddr = max(tryAddr + VIRTUAL_ALLOC_RESERVE_GRANULARITY, + (BYTE*) mbInfo.BaseAddress + mbInfo.RegionSize); + } + } + + // Our tryAddr reached pMaxAddr + return NULL; +} + +//****************************************************************************** +// NumaNodeInfo +//****************************************************************************** +#if !defined(FEATURE_REDHAWK) && !defined(FEATURE_PAL) +#if !defined(FEATURE_CORESYSTEM) +/*static*/ NumaNodeInfo::PGNPN NumaNodeInfo::m_pGetNumaProcessorNode = NULL; +#endif +/*static*/ NumaNodeInfo::PGNHNN NumaNodeInfo::m_pGetNumaHighestNodeNumber = NULL; +/*static*/ NumaNodeInfo::PVAExN NumaNodeInfo::m_pVirtualAllocExNuma = NULL; + +#if !defined(FEATURE_CORESYSTEM) +/*static*/ BOOL NumaNodeInfo::GetNumaProcessorNode(UCHAR proc_no, PUCHAR node_no) +{ + return (*m_pGetNumaProcessorNode)(proc_no, node_no); +} +#endif + +/*static*/ LPVOID NumaNodeInfo::VirtualAllocExNuma(HANDLE hProc, LPVOID lpAddr, SIZE_T dwSize, + DWORD allocType, DWORD prot, DWORD node) +{ + return (*m_pVirtualAllocExNuma)(hProc, lpAddr, dwSize, allocType, prot, node); +} +#if !defined(FEATURE_CORECLR) || defined(FEATURE_CORESYSTEM) +/*static*/ NumaNodeInfo::PGNPNEx NumaNodeInfo::m_pGetNumaProcessorNodeEx = NULL; + +/*static*/ BOOL NumaNodeInfo::GetNumaProcessorNodeEx(PPROCESSOR_NUMBER proc_no, PUSHORT node_no) +{ + return (*m_pGetNumaProcessorNodeEx)(proc_no, node_no); +} +#endif +#endif + +/*static*/ BOOL NumaNodeInfo::m_enableGCNumaAware = FALSE; +/*static*/ BOOL NumaNodeInfo::InitNumaNodeInfoAPI() +{ +#if !defined(FEATURE_REDHAWK) && !defined(FEATURE_PAL) + //check for numa support if multiple heaps are used + ULONG highest = 0; + + if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCNumaAware) == 0) + return FALSE; + + // check if required APIs are supported + HMODULE hMod = GetModuleHandleW(WINDOWS_KERNEL32_DLLNAME_W); + if (hMod == NULL) + return FALSE; + + m_pGetNumaHighestNodeNumber = (PGNHNN) GetProcAddress(hMod, "GetNumaHighestNodeNumber"); + if (m_pGetNumaHighestNodeNumber == NULL) + return FALSE; + + // fail to get the highest numa node number + if (!m_pGetNumaHighestNodeNumber(&highest) || (highest == 0)) + return FALSE; + +#if !defined(FEATURE_CORESYSTEM) + m_pGetNumaProcessorNode = (PGNPN) GetProcAddress(hMod, "GetNumaProcessorNode"); + if (m_pGetNumaProcessorNode == NULL) + return FALSE; +#endif + +#if !defined(FEATURE_CORECLR) || defined(FEATURE_CORESYSTEM) + m_pGetNumaProcessorNodeEx = (PGNPNEx) GetProcAddress(hMod, "GetNumaProcessorNodeEx"); + if (m_pGetNumaProcessorNodeEx == NULL) + return FALSE; +#endif + + m_pVirtualAllocExNuma = (PVAExN) GetProcAddress(hMod, "VirtualAllocExNuma"); + if (m_pVirtualAllocExNuma == NULL) + return FALSE; + + return TRUE; +#else + return FALSE; +#endif +} + +/*static*/ BOOL NumaNodeInfo::CanEnableGCNumaAware() +{ + return m_enableGCNumaAware; +} + +/*static*/ void NumaNodeInfo::InitNumaNodeInfo() +{ + m_enableGCNumaAware = InitNumaNodeInfoAPI(); +} + +//****************************************************************************** +// NumaNodeInfo +//****************************************************************************** +#if !defined(FEATURE_REDHAWK) && !defined(FEATURE_PAL) +/*static*/ CPUGroupInfo::PGLPIEx CPUGroupInfo::m_pGetLogicalProcessorInformationEx = NULL; +/*static*/ CPUGroupInfo::PSTGA CPUGroupInfo::m_pSetThreadGroupAffinity = NULL; +/*static*/ CPUGroupInfo::PGTGA CPUGroupInfo::m_pGetThreadGroupAffinity = NULL; +#if !defined(FEATURE_CORESYSTEM) && !defined(FEATURE_CORECLR) +/*static*/ CPUGroupInfo::PGCPNEx CPUGroupInfo::m_pGetCurrentProcessorNumberEx = NULL; +/*static*/ CPUGroupInfo::PGST CPUGroupInfo::m_pGetSystemTimes = NULL; +#endif +/*static*/ //CPUGroupInfo::PNTQSIEx CPUGroupInfo::m_pNtQuerySystemInformationEx = NULL; + +/*static*/ BOOL CPUGroupInfo::GetLogicalProcessorInformationEx(DWORD relationship, + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *slpiex, PDWORD count) +{ + LIMITED_METHOD_CONTRACT; + return (*m_pGetLogicalProcessorInformationEx)(relationship, slpiex, count); +} + +/*static*/ BOOL CPUGroupInfo::SetThreadGroupAffinity(HANDLE h, + GROUP_AFFINITY *groupAffinity, GROUP_AFFINITY *previousGroupAffinity) +{ + LIMITED_METHOD_CONTRACT; + return (*m_pSetThreadGroupAffinity)(h, groupAffinity, previousGroupAffinity); +} + +/*static*/ BOOL CPUGroupInfo::GetThreadGroupAffinity(HANDLE h, GROUP_AFFINITY *groupAffinity) +{ + LIMITED_METHOD_CONTRACT; + return (*m_pGetThreadGroupAffinity)(h, groupAffinity); +} + +#if !defined(FEATURE_CORESYSTEM) && !defined(FEATURE_CORECLR) +/*static*/ BOOL CPUGroupInfo::GetSystemTimes(FILETIME *idleTime, FILETIME *kernelTime, FILETIME *userTime) +{ + LIMITED_METHOD_CONTRACT; + return (*m_pGetSystemTimes)(idleTime, kernelTime, userTime); +} +#endif +#endif + +/*static*/ BOOL CPUGroupInfo::m_enableGCCPUGroups = FALSE; +/*static*/ BOOL CPUGroupInfo::m_threadUseAllCpuGroups = FALSE; +/*static*/ WORD CPUGroupInfo::m_nGroups = 0; +/*static*/ WORD CPUGroupInfo::m_nProcessors = 0; +/*static*/ WORD CPUGroupInfo::m_initialGroup = 0; +/*static*/ CPU_Group_Info *CPUGroupInfo::m_CPUGroupInfoArray = NULL; +/*static*/ LONG CPUGroupInfo::m_initialization = 0; + +// Check and setup function pointers for >64 LP Support +/*static*/ BOOL CPUGroupInfo::InitCPUGroupInfoAPI() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + +#if !defined(FEATURE_REDHAWK) && defined(_TARGET_AMD64_) && !defined(FEATURE_PAL) + HMODULE hMod = GetModuleHandleW(WINDOWS_KERNEL32_DLLNAME_W); + if (hMod == NULL) + return FALSE; + + m_pGetLogicalProcessorInformationEx = (PGLPIEx)GetProcAddress(hMod, "GetLogicalProcessorInformationEx"); + if (m_pGetLogicalProcessorInformationEx == NULL) + return FALSE; + + m_pSetThreadGroupAffinity = (PSTGA)GetProcAddress(hMod, "SetThreadGroupAffinity"); + if (m_pSetThreadGroupAffinity == NULL) + return FALSE; + + m_pGetThreadGroupAffinity = (PGTGA)GetProcAddress(hMod, "GetThreadGroupAffinity"); + if (m_pGetThreadGroupAffinity == NULL) + return FALSE; + +#if !defined(FEATURE_CORESYSTEM) && !defined(FEATURE_CORECLR) + m_pGetCurrentProcessorNumberEx = (PGCPNEx)GetProcAddress(hMod, "GetCurrentProcessorNumberEx"); + if (m_pGetCurrentProcessorNumberEx == NULL) + return FALSE; + + m_pGetSystemTimes = (PGST)GetProcAddress(hMod, "GetSystemTimes"); + if (m_pGetSystemTimes == NULL) + return FALSE; +#endif + + return TRUE; +#else + return FALSE; +#endif +} + +/*static*/ BOOL CPUGroupInfo::InitCPUGroupInfoArray() +{ + CONTRACTL + { + NOTHROW; + SO_TOLERANT; + GC_NOTRIGGER; + } + CONTRACTL_END; + +#if !defined(FEATURE_REDHAWK) && defined(_TARGET_AMD64_) && !defined(FEATURE_PAL) + BYTE *bBuffer = NULL; + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pSLPIEx = NULL; + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pRecord = NULL; + DWORD cbSLPIEx = 0; + DWORD byteOffset = 0; + DWORD dwNumElements = 0; + DWORD dwWeight = 1; + + if (CPUGroupInfo::GetLogicalProcessorInformationEx(RelationGroup, pSLPIEx, &cbSLPIEx) && + GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return FALSE; + + _ASSERTE(cbSLPIEx); + + // Fail to allocate buffer + bBuffer = new (nothrow) BYTE[ cbSLPIEx ]; + if (bBuffer == NULL) + return FALSE; + + pSLPIEx = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)bBuffer; + if (!m_pGetLogicalProcessorInformationEx(RelationGroup, pSLPIEx, &cbSLPIEx)) + { + delete[] bBuffer; + return FALSE; + } + + pRecord = pSLPIEx; + while (byteOffset < cbSLPIEx) + { + if (pRecord->Relationship == RelationGroup) + { + m_nGroups = pRecord->Group.ActiveGroupCount; + break; + } + byteOffset += pRecord->Size; + pRecord = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)(bBuffer + byteOffset); + } + + m_CPUGroupInfoArray = new (nothrow) CPU_Group_Info[m_nGroups]; + if (m_CPUGroupInfoArray == NULL) + { + delete[] bBuffer; + return FALSE; + } + + for (DWORD i = 0; i < m_nGroups; i++) + { + m_CPUGroupInfoArray[i].nr_active = (WORD)pRecord->Group.GroupInfo[i].ActiveProcessorCount; + m_CPUGroupInfoArray[i].active_mask = pRecord->Group.GroupInfo[i].ActiveProcessorMask; + m_nProcessors += m_CPUGroupInfoArray[i].nr_active; + dwWeight *= (DWORD)m_CPUGroupInfoArray[i].nr_active; + } + + //NOTE: the weight setting should work fine with 4 CPU groups upto 64 LPs each. the minimum number of threads + // per group before the weight overflow is 2^32/(2^6x2^6x2^6) = 2^14 (i.e. 16K threads) + for (DWORD i = 0; i < m_nGroups; i++) + { + m_CPUGroupInfoArray[i].groupWeight = dwWeight / (DWORD)m_CPUGroupInfoArray[i].nr_active; + m_CPUGroupInfoArray[i].activeThreadWeight = 0; + } + + delete[] bBuffer; // done with it; free it + return TRUE; +#else + return FALSE; +#endif +} + +/*static*/ BOOL CPUGroupInfo::InitCPUGroupInfoRange() +{ + LIMITED_METHOD_CONTRACT; + +#if !defined(FEATURE_REDHAWK) && defined(_TARGET_AMD64_) && !defined(FEATURE_PAL) + WORD begin = 0; + WORD nr_proc = 0; + + for (WORD i = 0; i < m_nGroups; i++) + { + nr_proc += m_CPUGroupInfoArray[i].nr_active; + m_CPUGroupInfoArray[i].begin = begin; + m_CPUGroupInfoArray[i].end = nr_proc - 1; + begin = nr_proc; + } + return TRUE; +#else + return FALSE; +#endif +} + +/*static*/ void CPUGroupInfo::InitCPUGroupInfo() +{ + CONTRACTL + { + NOTHROW; + SO_TOLERANT; + GC_NOTRIGGER; + } + CONTRACTL_END; + +#if !defined(FEATURE_REDHAWK) && defined(_TARGET_AMD64_) && !defined(FEATURE_PAL) + BOOL enableGCCPUGroups = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCCpuGroup) != 0; + BOOL threadUseAllCpuGroups = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_Thread_UseAllCpuGroups) != 0; + + if (!enableGCCPUGroups) + return; + + if (!InitCPUGroupInfoAPI()) + return; + + if (!InitCPUGroupInfoArray()) + return; + + if (!InitCPUGroupInfoRange()) + return; + + // initalGroup is whatever the CPU group that the main thread is running on + GROUP_AFFINITY groupAffinity; + CPUGroupInfo::GetThreadGroupAffinity(GetCurrentThread(), &groupAffinity); + m_initialGroup = groupAffinity.Group; + + // only enable CPU groups if more than one group exists + BOOL hasMultipleGroups = m_nGroups > 1; + m_enableGCCPUGroups = enableGCCPUGroups && hasMultipleGroups; + m_threadUseAllCpuGroups = threadUseAllCpuGroups && hasMultipleGroups; +#endif // _TARGET_AMD64_ +} + +/*static*/ BOOL CPUGroupInfo::IsInitialized() +{ + LIMITED_METHOD_CONTRACT; + return (m_initialization == -1); +} + +/*static*/ void CPUGroupInfo::EnsureInitialized() +{ + CONTRACTL + { + NOTHROW; + SO_TOLERANT; + GC_NOTRIGGER; + } + CONTRACTL_END; + + // CPUGroupInfo needs to be initialized only once. This could happen in three cases + // 1. CLR initialization at begining of EEStartup, or + // 2. Sometimes, when hosted by ASP.NET, the hosting process may initialize ThreadPool + // before initializing CLR, thus require CPUGroupInfo to be initialized to determine + // if CPU group support should/could be enabled. + // 3. Call into Threadpool functions before Threadpool _and_ CLR is initialized. + // Vast majority of time, CPUGroupInfo is initialized in case 1. or 2. + // The chance of contention will be extremely small, so the following code should be fine + // +retry: + if (IsInitialized()) + return; + + if (InterlockedCompareExchange(&m_initialization, 1, 0) == 0) + { + InitCPUGroupInfo(); + m_initialization = -1; + } + else //some other thread started initialization, just wait until complete; + { + while (m_initialization != -1) + { + SwitchToThread(); + } + goto retry; + } +} + +/*static*/ WORD CPUGroupInfo::GetNumActiveProcessors() +{ + LIMITED_METHOD_CONTRACT; + return (WORD)m_nProcessors; +} + +/*static*/ void CPUGroupInfo::GetGroupForProcessor(WORD processor_number, + WORD* group_number, WORD* group_processor_number) +{ + LIMITED_METHOD_CONTRACT; + +#if !defined(FEATURE_REDHAWK) && defined(_TARGET_AMD64_) && !defined(FEATURE_PAL) + WORD bTemp = 0; + WORD bDiff = processor_number - bTemp; + + for (WORD i=0; i < m_nGroups; i++) + { + bTemp += m_CPUGroupInfoArray[i].nr_active; + if (bTemp > processor_number) + { + *group_number = i; + *group_processor_number = bDiff; + break; + } + bDiff = processor_number - bTemp; + } +#else + *group_number = 0; + *group_processor_number = 0; +#endif +} + +/*static*/ DWORD CPUGroupInfo::CalculateCurrentProcessorNumber() +{ + CONTRACTL + { + NOTHROW; + SO_TOLERANT; + GC_NOTRIGGER; + } + CONTRACTL_END; + +#if !defined(FEATURE_REDHAWK) && !defined(FEATURE_CORESYSTEM) && !defined(FEATURE_CORECLR) && defined(_TARGET_AMD64_) && !defined(FEATURE_PAL) + // m_enableGCCPUGroups and m_threadUseAllCpuGroups must be TRUE + _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups); + + PROCESSOR_NUMBER proc_no; + proc_no.Group=0; + proc_no.Number=0; + proc_no.Reserved=0; + (*m_pGetCurrentProcessorNumberEx)(&proc_no); + + DWORD fullNumber = 0; + for (WORD i = 0; i < proc_no.Group; i++) + fullNumber += (DWORD)m_CPUGroupInfoArray[i].nr_active; + fullNumber += (DWORD)(proc_no.Number); + + return fullNumber; +#else + return 0; +#endif +} + +#if !defined(FEATURE_REDHAWK) && !defined(FEATURE_CORESYSTEM) && !defined(FEATURE_CORECLR) && !defined(FEATURE_PAL) +//Lock ThreadStore before calling this function, so that updates of weights/counts are consistent +/*static*/ void CPUGroupInfo::ChooseCPUGroupAffinity(GROUP_AFFINITY *gf) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + +#if defined(_TARGET_AMD64_) + WORD i, minGroup = 0; + DWORD minWeight = 0; + + // m_enableGCCPUGroups and m_threadUseAllCpuGroups must be TRUE + _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups); + + for (i = 0; i < m_nGroups; i++) + { + minGroup = (m_initialGroup + i) % m_nGroups; + + // the group is not filled up, use it + if (m_CPUGroupInfoArray[minGroup].activeThreadWeight / m_CPUGroupInfoArray[minGroup].groupWeight + < (DWORD)m_CPUGroupInfoArray[minGroup].nr_active) + goto found; + } + + // all groups filled up, distribute proportionally + minGroup = m_initialGroup; + minWeight = m_CPUGroupInfoArray[m_initialGroup].activeThreadWeight; + for (i = 0; i < m_nGroups; i++) + { + if (m_CPUGroupInfoArray[i].activeThreadWeight < minWeight) + { + minGroup = i; + minWeight = m_CPUGroupInfoArray[i].activeThreadWeight; + } + } + +found: + gf->Group = minGroup; + gf->Mask = m_CPUGroupInfoArray[minGroup].active_mask; + gf->Reserved[0] = 0; + gf->Reserved[1] = 0; + gf->Reserved[2] = 0; + m_CPUGroupInfoArray[minGroup].activeThreadWeight += m_CPUGroupInfoArray[minGroup].groupWeight; +#endif +} + +//Lock ThreadStore before calling this function, so that updates of weights/counts are consistent +/*static*/ void CPUGroupInfo::ClearCPUGroupAffinity(GROUP_AFFINITY *gf) +{ + LIMITED_METHOD_CONTRACT; +#if defined(_TARGET_AMD64_) + // m_enableGCCPUGroups and m_threadUseAllCpuGroups must be TRUE + _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups); + + WORD group = gf->Group; + m_CPUGroupInfoArray[group].activeThreadWeight -= m_CPUGroupInfoArray[group].groupWeight; +#endif +} +#endif + +/*static*/ BOOL CPUGroupInfo::CanEnableGCCPUGroups() +{ + LIMITED_METHOD_CONTRACT; + return m_enableGCCPUGroups; +} + +/*static*/ BOOL CPUGroupInfo::CanEnableThreadUseAllCpuGroups() +{ + LIMITED_METHOD_CONTRACT; + return m_threadUseAllCpuGroups; +} + +//****************************************************************************** +// Returns the number of processors that a process has been configured to run on +//****************************************************************************** +//****************************************************************************** +// Returns the number of processors that a process has been configured to run on +//****************************************************************************** +int GetCurrentProcessCpuCount() +{ + CONTRACTL + { + NOTHROW; + SO_TOLERANT; + CANNOT_TAKE_LOCK; + } + CONTRACTL_END; + + static int cCPUs = 0; + + if (cCPUs != 0) + return cCPUs; + +#if !defined(FEATURE_CORESYSTEM) + + DWORD_PTR pmask, smask; + + if (!GetProcessAffinityMask(GetCurrentProcess(), &pmask, &smask)) + return 1; + + if (pmask == 1) + return 1; + + pmask &= smask; + + int count = 0; + while (pmask) + { + if (pmask & 1) + count++; + + pmask >>= 1; + } + + // GetProcessAffinityMask can return pmask=0 and smask=0 on systems with more + // than 64 processors, which would leave us with a count of 0. Since the GC + // expects there to be at least one processor to run on (and thus at least one + // heap), we'll return 64 here if count is 0, since there are likely a ton of + // processors available in that case. The GC also cannot (currently) handle + // the case where there are more than 64 processors, so we will return a + // maximum of 64 here. + if (count == 0 || count > 64) + count = 64; + + cCPUs = count; + + return count; + +#else // !FEATURE_CORESYSTEM + + SYSTEM_INFO sysInfo; + ::GetSystemInfo(&sysInfo); + cCPUs = sysInfo.dwNumberOfProcessors; + return sysInfo.dwNumberOfProcessors; + +#endif // !FEATURE_CORESYSTEM +} + +DWORD_PTR GetCurrentProcessCpuMask() +{ + CONTRACTL + { + NOTHROW; + SO_TOLERANT; + CANNOT_TAKE_LOCK; + } + CONTRACTL_END; + +#if !defined(FEATURE_CORESYSTEM) + DWORD_PTR pmask, smask; + + if (!GetProcessAffinityMask(GetCurrentProcess(), &pmask, &smask)) + return 1; + + pmask &= smask; + return pmask; +#else + return 0; +#endif +} + +/**************************************************************************/ + +/**************************************************************************/ +void ConfigMethodSet::init(const CLRConfig::ConfigStringInfo & info) +{ + CONTRACTL + { + THROWS; + } + CONTRACTL_END; + + // make sure that the memory was zero initialized + _ASSERTE(m_inited == 0 || m_inited == 1); + + LPWSTR str = CLRConfig::GetConfigValue(info); + if (str) + { + m_list.Insert(str); + delete[] str; + } + m_inited = 1; +} + +/**************************************************************************/ +bool ConfigMethodSet::contains(LPCUTF8 methodName, LPCUTF8 className, PCCOR_SIGNATURE sig) +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + _ASSERTE(m_inited == 1); + + if (m_list.IsEmpty()) + return false; + return(m_list.IsInList(methodName, className, sig)); +} + +/**************************************************************************/ +void ConfigDWORD::init_DontUse_(__in_z LPCWSTR keyName, DWORD defaultVal) +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + // make sure that the memory was zero initialized + _ASSERTE(m_inited == 0 || m_inited == 1); + + m_value = REGUTIL::GetConfigDWORD_DontUse_(keyName, defaultVal); + m_inited = 1; +} + +/**************************************************************************/ +void ConfigString::init(const CLRConfig::ConfigStringInfo & info) +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + // make sure that the memory was zero initialized + _ASSERTE(m_inited == 0 || m_inited == 1); + + // Note: m_value will be leaking + m_value = CLRConfig::GetConfigValue(info); + m_inited = 1; +} + +//============================================================================= +// AssemblyNamesList +//============================================================================= +// The string should be of the form +// MyAssembly +// MyAssembly;mscorlib;System +// MyAssembly;mscorlib System + +AssemblyNamesList::AssemblyNamesList(__in LPWSTR list) +{ + CONTRACTL { + THROWS; + } CONTRACTL_END; + + WCHAR prevChar = '?'; // dummy + LPWSTR nameStart = NULL; // start of the name currently being processed. NULL if no current name + AssemblyName ** ppPrevLink = &m_pNames; + + for (LPWSTR listWalk = list; prevChar != '\0'; prevChar = *listWalk, listWalk++) + { + WCHAR curChar = *listWalk; + + if (iswspace(curChar) || curChar == ';' || curChar == '\0' ) + { + // + // Found white-space + // + + if (nameStart) + { + // Found the end of the current name + + AssemblyName * newName = new AssemblyName(); + size_t nameLen = listWalk - nameStart; + + MAKE_UTF8PTR_FROMWIDE(temp, nameStart); + newName->m_assemblyName = new char[nameLen + 1]; + memcpy(newName->m_assemblyName, temp, nameLen * sizeof(newName->m_assemblyName[0])); + newName->m_assemblyName[nameLen] = '\0'; + + *ppPrevLink = newName; + ppPrevLink = &newName->m_next; + + nameStart = NULL; + } + } + else if (!nameStart) + { + // + // Found the start of a new name + // + + nameStart = listWalk; + } + } + + _ASSERTE(!nameStart); // cannot be in the middle of a name + *ppPrevLink = NULL; +} + +AssemblyNamesList::~AssemblyNamesList() +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + for (AssemblyName * pName = m_pNames; pName; /**/) + { + AssemblyName * cur = pName; + pName = pName->m_next; + + delete [] cur->m_assemblyName; + delete cur; + } +} + +bool AssemblyNamesList::IsInList(LPCUTF8 assemblyName) +{ + if (IsEmpty()) + return false; + + for (AssemblyName * pName = m_pNames; pName; pName = pName->m_next) + { + if (_stricmp(pName->m_assemblyName, assemblyName) == 0) + return true; + } + + return false; +} + +//============================================================================= +// MethodNamesList +//============================================================================= +// str should be of the form : +// "foo1 MyNamespace.MyClass:foo3 *:foo4 foo5(x,y,z)" +// "MyClass:foo2 MyClass:*" will match under _DEBUG +// + +void MethodNamesListBase::Insert(__in_z LPWSTR str) +{ + CONTRACTL { + THROWS; + } CONTRACTL_END; + + enum State { NO_NAME, CLS_NAME, FUNC_NAME, ARG_LIST }; // parsing state machine + + const char SEP_CHAR = ' '; // current character use to separate each entry +// const char SEP_CHAR = ';'; // better character use to separate each entry + + WCHAR lastChar = '?'; // dummy + LPWSTR nameStart = NULL; // while walking over the classname or methodname, this points to start + MethodName nameBuf; // Buffer used while parsing the current entry + MethodName** lastName = &pNames; // last entry inserted into the list + bool bQuote = false; + + nameBuf.methodName = NULL; + nameBuf.className = NULL; + nameBuf.numArgs = -1; + nameBuf.next = NULL; + + for(State state = NO_NAME; lastChar != '\0'; str++) + { + lastChar = *str; + + switch(state) + { + case NO_NAME: + if (*str != SEP_CHAR) + { + nameStart = str; + state = CLS_NAME; // we have found the start of the next entry + } + break; + + case CLS_NAME: + if (*nameStart == '"') + { + while (*str && *str!='"') + { + str++; + } + nameStart++; + bQuote=true; + } + + if (*str == ':') + { + if (*nameStart == '*' && !bQuote) + { + // Is the classname string a wildcard. Then set it to NULL + nameBuf.className = NULL; + } + else + { + int len = (int)(str - nameStart); + + // Take off the quote + if (bQuote) { len--; bQuote=false; } + + nameBuf.className = new char[len + 1]; + MAKE_UTF8PTR_FROMWIDE(temp, nameStart); + memcpy(nameBuf.className, temp, len*sizeof(nameBuf.className[0])); + nameBuf.className[len] = '\0'; + } + if (str[1] == ':') // Accept class::name syntax too + str++; + nameStart = str + 1; + state = FUNC_NAME; + } + else if (*str == '\0' || *str == SEP_CHAR || *str == '(') + { + /* This was actually a method name without any class */ + nameBuf.className = NULL; + goto DONE_FUNC_NAME; + } + break; + + case FUNC_NAME: + if (*nameStart == '"') + { + while ( (nameStart==str) || // workaround to handle when className!=NULL + (*str && *str!='"')) + { + str++; + } + + nameStart++; + bQuote=true; + } + + if (*str == '\0' || *str == SEP_CHAR || *str == '(') + { + DONE_FUNC_NAME: + _ASSERTE(*str == '\0' || *str == SEP_CHAR || *str == '('); + + if (*nameStart == '*' && !bQuote) + { + // Is the name string a wildcard. Then set it to NULL + nameBuf.methodName = NULL; + } + else + { + int len = (int)(str - nameStart); + + // Take off the quote + if (bQuote) { len--; bQuote=false; } + + nameBuf.methodName = new char[len + 1]; + MAKE_UTF8PTR_FROMWIDE(temp, nameStart); + memcpy(nameBuf.methodName, temp, len*sizeof(nameBuf.methodName[0])); + nameBuf.methodName[len] = '\0'; + } + + if (*str == '\0' || *str == SEP_CHAR) + { + nameBuf.numArgs = -1; + goto DONE_ARG_LIST; + } + else + { + _ASSERTE(*str == '('); + nameBuf.numArgs = -1; + state = ARG_LIST; + } + } + break; + + case ARG_LIST: + if (*str == '\0' || *str == ')') + { + if (nameBuf.numArgs == -1) + nameBuf.numArgs = 0; + + DONE_ARG_LIST: + _ASSERTE(*str == '\0' || *str == SEP_CHAR || *str == ')'); + + // We have parsed an entire method name. + // Create a new entry in the list for it + + MethodName * newName = new MethodName(); + *newName = nameBuf; + newName->next = NULL; + *lastName = newName; + lastName = &newName->next; + state = NO_NAME; + + // Skip anything after the argument list until we find the next + // separator character, otherwise if we see "func(a,b):foo" we + // create entries for "func(a,b)" as well as ":foo". + if (*str == ')') + { + while (*str && *str != SEP_CHAR) + { + str++; + } + lastChar = *str; + } + } + else + { + if (*str != SEP_CHAR && nameBuf.numArgs == -1) + nameBuf.numArgs = 1; + if (*str == ',') + nameBuf.numArgs++; + } + break; + + default: _ASSERTE(!"Bad state"); break; + } + } +} + +/**************************************************************/ + +void MethodNamesListBase::Destroy() +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + for(MethodName * pName = pNames; pName; /**/) + { + if (pName->className) + delete [] pName->className; + if (pName->methodName) + delete [] pName->methodName; + + MethodName * curName = pName; + pName = pName->next; + delete curName; + } +} + +/**************************************************************/ +bool MethodNamesListBase::IsInList(LPCUTF8 methName, LPCUTF8 clsName, PCCOR_SIGNATURE sig) +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + ULONG numArgs = -1; + if (sig != NULL) + { + sig++; // Skip calling convention + numArgs = CorSigUncompressData(sig); + } + + // Try to match all the entries in the list + + for(MethodName * pName = pNames; pName; pName = pName->next) + { + // If numArgs is valid, check for mismatch + if (pName->numArgs != -1 && (ULONG)pName->numArgs != numArgs) + continue; + + // If methodName is valid, check for mismatch + if (pName->methodName) { + if (strcmp(pName->methodName, methName) != 0) { + + // C++ embeds the class name into the method name, + // deal with that here (workaround) + const char* ptr = strchr(methName, ':'); + if (ptr != 0 && ptr[1] == ':' && strcmp(&ptr[2], pName->methodName) == 0) { + unsigned clsLen = (unsigned)(ptr - methName); + if (pName->className == 0 || strncmp(pName->className, methName, clsLen) == 0) + return true; + } + continue; + } + } + + // check for class Name exact match + if (clsName == 0 || pName->className == 0 || strcmp(pName->className, clsName) == 0) + return true; + + // check for suffix wildcard like System.* + unsigned len = (unsigned)strlen(pName->className); + if (len > 0 && pName->className[len-1] == '*' && strncmp(pName->className, clsName, len-1) == 0) + return true; + +#ifdef _DEBUG + // Maybe className doesnt include namespace. Try to match that + LPCUTF8 onlyClass = ns::FindSep(clsName); + if (onlyClass && strcmp(pName->className, onlyClass+1) == 0) + return true; +#endif + } + return(false); +} + +//============================================================================= +// Signature Validation Functions (scaled down version from MDValidator +//============================================================================= + +//***************************************************************************** +// This function validates one argument given an offset into the signature +// where the argument begins. This function assumes that the signature is well +// formed as far as the compression scheme is concerned. +// <TODO>@todo: Validate tokens embedded.</TODO> +//***************************************************************************** +HRESULT validateOneArg( + mdToken tk, // [IN] Token whose signature needs to be validated. + SigParser *pSig, + ULONG *pulNSentinels, // [IN/OUT] Number of sentinels + IMDInternalImport* pImport, // [IN] Internal MD Import interface ptr + BOOL bNoVoidAllowed) // [IN] Flag indicating whether "void" is disallowed for this arg + +{ + CONTRACTL + { + NOTHROW; + SO_TOLERANT; + } + CONTRACTL_END; + + BYTE elementType; // Current element type being processed. + mdToken token; // Embedded token. + ULONG ulArgCnt; // Argument count for function pointer. + ULONG ulIndex; // Index for type parameters + ULONG ulRank; // Rank of the array. + ULONG ulSizes; // Count of sized dimensions of the array. + ULONG ulLbnds; // Count of lower bounds of the array. + ULONG ulCallConv; + + HRESULT hr = S_OK; // Value returned. + BOOL bRepeat = TRUE; // MODOPT and MODREQ belong to the arg after them + + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); + while(bRepeat) + { + bRepeat = FALSE; + // Validate that the argument is not missing. + + // Get the element type. + if (FAILED(pSig->GetByte(&elementType))) + { + IfFailGo(VLDTR_E_SIG_MISSARG); + } + + // Walk past all the modifier types. + while (elementType & ELEMENT_TYPE_MODIFIER) + { + if (elementType == ELEMENT_TYPE_SENTINEL) + { + if(pulNSentinels) *pulNSentinels+=1; + if(TypeFromToken(tk) != mdtMemberRef) IfFailGo(VLDTR_E_SIG_SENTINMETHODDEF); + } + if (FAILED(pSig->GetByte(&elementType))) + { + IfFailGo(VLDTR_E_SIG_MISSELTYPE); + } + } + + switch (elementType) + { + case ELEMENT_TYPE_VOID: + if(bNoVoidAllowed) IfFailGo(VLDTR_E_SIG_BADVOID); + + case ELEMENT_TYPE_BOOLEAN: + case ELEMENT_TYPE_CHAR: + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + case ELEMENT_TYPE_R4: + case ELEMENT_TYPE_R8: + case ELEMENT_TYPE_STRING: + case ELEMENT_TYPE_OBJECT: + case ELEMENT_TYPE_TYPEDBYREF: + case ELEMENT_TYPE_U: + case ELEMENT_TYPE_I: + break; + case ELEMENT_TYPE_PTR: + // Validate the referenced type. + if(FAILED(hr = validateOneArg(tk, pSig, pulNSentinels, pImport, FALSE))) IfFailGo(hr); + break; + case ELEMENT_TYPE_BYREF: //fallthru + if(TypeFromToken(tk)==mdtFieldDef) IfFailGo(VLDTR_E_SIG_BYREFINFIELD); + case ELEMENT_TYPE_PINNED: + case ELEMENT_TYPE_SZARRAY: + // Validate the referenced type. + if(FAILED(hr = validateOneArg(tk, pSig, pulNSentinels, pImport, TRUE))) IfFailGo(hr); + break; + case ELEMENT_TYPE_CMOD_OPT: + case ELEMENT_TYPE_CMOD_REQD: + bRepeat = TRUE; // go on validating, we're not done with this arg + case ELEMENT_TYPE_VALUETYPE: //fallthru + case ELEMENT_TYPE_CLASS: + // See if the token is missing. + if (FAILED(pSig->GetToken(&token))) + { + IfFailGo(VLDTR_E_SIG_MISSTKN); + } + // Token validation . + if(pImport) + { + ULONG rid = RidFromToken(token); + ULONG typ = TypeFromToken(token); + ULONG maxrid = pImport->GetCountWithTokenKind(typ); + if(typ == mdtTypeDef) maxrid++; + if((rid==0)||(rid > maxrid)) IfFailGo(VLDTR_E_SIG_TKNBAD); + } + break; + + case ELEMENT_TYPE_FNPTR: + // <TODO>@todo: More function pointer validation?</TODO> + // Validate that calling convention is present. + if (FAILED(pSig->GetCallingConvInfo(&ulCallConv))) + { + IfFailGo(VLDTR_E_SIG_MISSFPTR); + } + if(((ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK) >= IMAGE_CEE_CS_CALLCONV_MAX) + ||((ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS) + &&(!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)))) IfFailGo(VLDTR_E_MD_BADCALLINGCONV); + + // Validate that argument count is present. + if (FAILED(pSig->GetData(&ulArgCnt))) + { + IfFailGo(VLDTR_E_SIG_MISSFPTRARGCNT); + } + + // FNPTR signature must follow the rules of MethodDef + // Validate and consume return type. + IfFailGo(validateOneArg(mdtMethodDef, pSig, NULL, pImport, FALSE)); + + // Validate and consume the arguments. + while(ulArgCnt--) + { + IfFailGo(validateOneArg(mdtMethodDef, pSig, NULL, pImport, TRUE)); + } + break; + + case ELEMENT_TYPE_ARRAY: + // Validate and consume the base type. + IfFailGo(validateOneArg(tk, pSig, pulNSentinels, pImport, TRUE)); + + // Validate that the rank is present. + if (FAILED(pSig->GetData(&ulRank))) + { + IfFailGo(VLDTR_E_SIG_MISSRANK); + } + + // Process the sizes. + if (ulRank) + { + // Validate that the count of sized-dimensions is specified. + if (FAILED(pSig->GetData(&ulSizes))) + { + IfFailGo(VLDTR_E_SIG_MISSNSIZE); + } + + // Loop over the sizes. + while(ulSizes--) + { + // Validate the current size. + if (FAILED(pSig->GetData(NULL))) + { + IfFailGo(VLDTR_E_SIG_MISSSIZE); + } + } + + // Validate that the count of lower bounds is specified. + if (FAILED(pSig->GetData(&ulLbnds))) + { + IfFailGo(VLDTR_E_SIG_MISSNLBND); + } + + // Loop over the lower bounds. + while(ulLbnds--) + { + // Validate the current lower bound. + if (FAILED(pSig->GetData(NULL))) + { + IfFailGo(VLDTR_E_SIG_MISSLBND); + } + } + } + break; + case ELEMENT_TYPE_VAR: + case ELEMENT_TYPE_MVAR: + // Validate that index is present. + if (FAILED(pSig->GetData(&ulIndex))) + { + IfFailGo(VLDTR_E_SIG_MISSFPTRARGCNT); + } + + //@todo GENERICS: check that index is in range + break; + + case ELEMENT_TYPE_GENERICINST: + // Validate the generic type. + IfFailGo(validateOneArg(tk, pSig, pulNSentinels, pImport, TRUE)); + + // Validate that parameter count is present. + if (FAILED(pSig->GetData(&ulArgCnt))) + { + IfFailGo(VLDTR_E_SIG_MISSFPTRARGCNT); + } + + //@todo GENERICS: check that number of parameters matches definition? + + // Validate and consume the parameters. + while(ulArgCnt--) + { + IfFailGo(validateOneArg(tk, pSig, NULL, pImport, TRUE)); + } + break; + + case ELEMENT_TYPE_SENTINEL: // this case never works because all modifiers are skipped before switch + if(TypeFromToken(tk) == mdtMethodDef) IfFailGo(VLDTR_E_SIG_SENTINMETHODDEF); + break; + + default: + IfFailGo(VLDTR_E_SIG_BADELTYPE); + break; + } // switch (ulElementType) + } // end while(bRepeat) +ErrExit: + + END_SO_INTOLERANT_CODE; + return hr; +} // validateOneArg() + +//***************************************************************************** +// This function validates the given Method/Field/Standalone signature. +//@todo GENERICS: MethodInstantiation? +//***************************************************************************** +HRESULT validateTokenSig( + mdToken tk, // [IN] Token whose signature needs to be validated. + PCCOR_SIGNATURE pbSig, // [IN] Signature. + ULONG cbSig, // [IN] Size in bytes of the signature. + DWORD dwFlags, // [IN] Method flags. + IMDInternalImport* pImport) // [IN] Internal MD Import interface ptr +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + ULONG ulCallConv; // Calling convention. + ULONG ulArgCount = 1; // Count of arguments (1 because of the return type) + ULONG ulTyArgCount = 0; // Count of type arguments + ULONG ulArgIx = 0; // Starting index of argument (standalone sig: 1) + ULONG i; // Looping index. + HRESULT hr = S_OK; // Value returned. + ULONG ulNSentinels = 0; + SigParser sig(pbSig, cbSig); + + _ASSERTE(TypeFromToken(tk) == mdtMethodDef || + TypeFromToken(tk) == mdtMemberRef || + TypeFromToken(tk) == mdtSignature || + TypeFromToken(tk) == mdtFieldDef); + + // Check for NULL signature. + if (!pbSig || !cbSig) return VLDTR_E_SIGNULL; + + // Validate the calling convention. + + // Moves behind calling convention + IfFailRet(sig.GetCallingConvInfo(&ulCallConv)); + i = ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK; + switch(TypeFromToken(tk)) + { + case mdtMethodDef: // MemberRefs have no flags available + // If HASTHIS is set on the calling convention, the method should not be static. + if ((ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) && + IsMdStatic(dwFlags)) return VLDTR_E_MD_THISSTATIC; + + // If HASTHIS is not set on the calling convention, the method should be static. + if (!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) && + !IsMdStatic(dwFlags)) return VLDTR_E_MD_NOTTHISNOTSTATIC; + // fall thru to callconv check; + + case mdtMemberRef: + if(i == IMAGE_CEE_CS_CALLCONV_FIELD) return validateOneArg(tk, &sig, NULL, pImport, TRUE); + + // EXPLICITTHIS and native call convs are for stand-alone sigs only (for calli) + if((i != IMAGE_CEE_CS_CALLCONV_DEFAULT)&&( i != IMAGE_CEE_CS_CALLCONV_VARARG) + || (ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)) return VLDTR_E_MD_BADCALLINGCONV; + break; + + case mdtSignature: + if(i != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) // then it is function sig for calli + { + if((i >= IMAGE_CEE_CS_CALLCONV_MAX) + ||((ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS) + &&(!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)))) return VLDTR_E_MD_BADCALLINGCONV; + } + else + ulArgIx = 1; // Local variable signatures don't have a return type + break; + + case mdtFieldDef: + if(i != IMAGE_CEE_CS_CALLCONV_FIELD) return VLDTR_E_MD_BADCALLINGCONV; + return validateOneArg(tk, &sig, NULL, pImport, TRUE); + } + // Is there any sig left for arguments? + + // Get the type argument count + if (ulCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + if (FAILED(sig.GetData(&ulTyArgCount))) + { + return VLDTR_E_MD_NOARGCNT; + } + } + + // Get the argument count. + if (FAILED(sig.GetData(&ulArgCount))) + { + return VLDTR_E_MD_NOARGCNT; + } + + // Validate the return type and the arguments. + // (at this moment ulArgCount = num.args+1, ulArgIx = (standalone sig. ? 1 :0); ) + for(; ulArgIx < ulArgCount; ulArgIx++) + { + if(FAILED(hr = validateOneArg(tk, &sig, &ulNSentinels, pImport, (ulArgIx!=0)))) return hr; + } + + // <TODO>@todo: we allow junk to be at the end of the signature (we may not consume it all) + // do we care?</TODO> + + if((ulNSentinels != 0) && ((ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK) != IMAGE_CEE_CS_CALLCONV_VARARG )) + return VLDTR_E_SIG_SENTMUSTVARARG; + if(ulNSentinels > 1) return VLDTR_E_SIG_MULTSENTINELS; + return S_OK; +} // validateTokenSig() + +const CHAR g_VersionBase[] = "v1."; +const CHAR g_DevelopmentVersion[] = "x86"; +const CHAR g_RetString[] = "retail"; +const CHAR g_ComplusString[] = "COMPLUS"; + +const WCHAR g_VersionBaseW[] = W("v1."); +const WCHAR g_DevelopmentVersionW[] = W("x86"); +const WCHAR g_RetStringW[] = W("retail"); +const WCHAR g_ComplusStringW[] = W("COMPLUS"); + +//***************************************************************************** +// Determine the version number of the runtime that was used to build the +// specified image. The pMetadata pointer passed in is the pointer to the +// metadata contained in the image. +//***************************************************************************** +static BOOL IsReallyRTM(LPCWSTR szVersion) +{ + LIMITED_METHOD_CONTRACT; + if (szVersion==NULL) + return FALSE; + + size_t lgth = sizeof(g_VersionBaseW) / sizeof(WCHAR) - 1; + size_t foundLgth = wcslen(szVersion); + + // Have normal version, v1.* + if ( (foundLgth >= lgth+2) && + !wcsncmp(szVersion, g_VersionBaseW, lgth) ) { + + // v1.0.* means RTM + if (szVersion[lgth+1] == W('.')) { + if (szVersion[lgth] == W('0')) + return TRUE; + } + + // Check for dev version (v1.x86ret, v1.x86fstchk...) + else if(!wcsncmp(szVersion+lgth, g_DevelopmentVersionW, + (sizeof(g_DevelopmentVersionW) / sizeof(WCHAR) - 1))) + return TRUE; + } + // Some weird version... + else if( (!wcscmp(szVersion, g_RetStringW)) || + (!wcscmp(szVersion, g_ComplusStringW)) ) + return TRUE; + return FALSE; +} + +void AdjustImageRuntimeVersion(SString* pVersion) +{ + CONTRACTL + { + THROWS; + } + CONTRACTL_END; + + if (IsReallyRTM(*pVersion)) + { + pVersion->SetANSI(g_RTMVersion); + } +}; + +HRESULT GetImageRuntimeVersionString(PVOID pMetaData, LPCSTR* pString) +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + _ASSERTE(pString); + STORAGESIGNATURE* pSig = (STORAGESIGNATURE*) pMetaData; + + // Verify the signature. + + // If signature didn't match, you shouldn't be here. + if (pSig->GetSignature() != STORAGE_MAGIC_SIG) + return CLDB_E_FILE_CORRUPT; + + // The version started in version 1.1 + if (pSig->GetMajorVer() < 1) + return CLDB_E_FILE_OLDVER; + + if (pSig->GetMajorVer() == 1 && pSig->GetMinorVer() < 1) + return CLDB_E_FILE_OLDVER; + + // Header data starts after signature. + *pString = (LPCSTR) pSig->pVersion; + if(*pString) { + size_t lgth = sizeof(g_VersionBase) / sizeof(char) - 1; + size_t foundLgth = strlen(*pString); + + // Have normal version, v1.* + if ( (foundLgth >= lgth+2) && + !strncmp(*pString, g_VersionBase, lgth) ) { + + // v1.0.* means RTM + if ((*pString)[lgth+1] == '.') { + if ((*pString)[lgth] == '0') + *pString = g_RTMVersion; + } + + // Check for dev version (v1.x86ret, v1.x86fstchk...) + else if(!strncmp(&(*pString)[lgth], g_DevelopmentVersion, + (sizeof(g_DevelopmentVersion) / sizeof(char) - 1))) + *pString = g_RTMVersion; + } + + // Some weird version... + else if( (!strcmp(*pString, g_RetString)) || + (!strcmp(*pString, g_ComplusString)) ) + *pString = g_RTMVersion; + } + + return S_OK; +} + +//***************************************************************************** +// Convert a UTF8 string to Unicode, into a CQuickArray<WCHAR>. +//***************************************************************************** +HRESULT Utf2Quick( + LPCUTF8 pStr, // The string to convert. + CQuickArray<WCHAR> &rStr, // The QuickArray<WCHAR> to convert it into. + int iCurLen) // Inital characters in the array to leave (default 0). +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + HRESULT hr = S_OK; // A result. + int iReqLen; // Required additional length. + int iActLen; + int bAlloc = 0; // If non-zero, allocation was required. + + if (iCurLen < 0 ) + { + _ASSERTE_MSG(false, "Invalid current length"); + return E_INVALIDARG; + } + + // Calculate the space available + S_SIZE_T cchAvail = S_SIZE_T(rStr.MaxSize()) - S_SIZE_T(iCurLen); + if (cchAvail.IsOverflow() || cchAvail.Value() > INT_MAX) + { + _ASSERTE_MSG(false, "Integer overflow/underflow"); + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + } + + // Attempt the conversion. + LPWSTR rNewStr = rStr.Ptr()+iCurLen; + if(rNewStr < rStr.Ptr()) + { + _ASSERTE_MSG(false, "Integer overflow/underflow"); + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + } + iReqLen = WszMultiByteToWideChar(CP_UTF8, 0, pStr, -1, rNewStr, (int)(cchAvail.Value())); + + // If the buffer was too small, determine what is required. + if (iReqLen == 0) + bAlloc = iReqLen = WszMultiByteToWideChar(CP_UTF8, 0, pStr, -1, 0, 0); + // Resize the buffer. If the buffer was large enough, this just sets the internal + // counter, but if it was too small, this will attempt a reallocation. Note that + // the length includes the terminating W('/0'). + IfFailGo(rStr.ReSizeNoThrow(iCurLen+iReqLen)); + // If we had to realloc, then do the conversion again, now that the buffer is + // large enough. + if (bAlloc) { + //recalculating cchAvail since MaxSize could have been changed. + cchAvail = S_SIZE_T(rStr.MaxSize()) - S_SIZE_T(iCurLen); + if (cchAvail.IsOverflow() || cchAvail.Value() > INT_MAX) + { + _ASSERTE_MSG(false, "Integer overflow/underflow"); + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + } + //reculculating rNewStr + rNewStr = rStr.Ptr()+iCurLen; + + if(rNewStr < rStr.Ptr()) + { + _ASSERTE_MSG(false, "Integer overflow/underflow"); + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + } + iActLen = WszMultiByteToWideChar(CP_UTF8, 0, pStr, -1, rNewStr, (int)(cchAvail.Value())); + _ASSERTE(iReqLen == iActLen); + } +ErrExit: + return hr; +} // HRESULT Utf2Quick() + + +//***************************************************************************** +// Extract the movl 64-bit unsigned immediate from an IA64 bundle +// (Format X2) +//***************************************************************************** +UINT64 GetIA64Imm64(UINT64 * pBundle) +{ + WRAPPER_NO_CONTRACT; + + UINT64 temp0 = PTR_UINT64(pBundle)[0]; + UINT64 temp1 = PTR_UINT64(pBundle)[1]; + + return GetIA64Imm64(temp0, temp1); +} + +UINT64 GetIA64Imm64(UINT64 qword0, UINT64 qword1) +{ + LIMITED_METHOD_CONTRACT; + + UINT64 imm64 = 0; + +#ifdef _DEBUG_IMPL + // + // make certain we're decoding a movl opcode, with template 4 or 5 + // + UINT64 templa = (qword0 >> 0) & 0x1f; + UINT64 opcode = (qword1 >> 60) & 0xf; + + _ASSERTE((opcode == 0x6) && ((templa == 0x4) || (templa == 0x5))); +#endif + + imm64 = (qword1 >> 59) << 63; // 1 i + imm64 |= (qword1 << 41) >> 1; // 23 high bits of imm41 + imm64 |= (qword0 >> 46) << 22; // 18 low bits of imm41 + imm64 |= (qword1 >> 23) & 0x200000; // 1 ic + imm64 |= (qword1 >> 29) & 0x1F0000; // 5 imm5c + imm64 |= (qword1 >> 43) & 0xFF80; // 9 imm9d + imm64 |= (qword1 >> 36) & 0x7F; // 7 imm7b + + return imm64; +} + +//***************************************************************************** +// Deposit the movl 64-bit unsigned immediate into an IA64 bundle +// (Format X2) +//***************************************************************************** +void PutIA64Imm64(UINT64 * pBundle, UINT64 imm64) +{ + LIMITED_METHOD_CONTRACT; + +#ifdef _DEBUG_IMPL + // + // make certain we're decoding a movl opcode, with template 4 or 5 + // + UINT64 templa = (pBundle[0] >> 0) & 0x1f; + UINT64 opcode = (pBundle[1] >> 60) & 0xf ; + + _ASSERTE((opcode == 0x6) && ((templa == 0x4) || (templa == 0x5))); +#endif + + const UINT64 mask0 = UI64(0x00003FFFFFFFFFFF); + const UINT64 mask1 = UI64(0xF000080FFF800000); + + /* Clear all bits used as part of the imm64 */ + pBundle[0] &= mask0; + pBundle[1] &= mask1; + + UINT64 temp0; + UINT64 temp1; + + temp1 = (imm64 >> 63) << 59; // 1 i + temp1 |= (imm64 & 0xFF80) << 43; // 9 imm9d + temp1 |= (imm64 & 0x1F0000) << 29; // 5 imm5c + temp1 |= (imm64 & 0x200000) << 23; // 1 ic + temp1 |= (imm64 & 0x7F) << 36; // 7 imm7b + temp1 |= (imm64 << 1) >> 41; // 23 high bits of imm41 + temp0 = (imm64 >> 22) << 46; // 18 low bits of imm41 + + /* Or in the new bits used in the imm64 */ + pBundle[0] |= temp0; + pBundle[1] |= temp1; + FlushInstructionCache(GetCurrentProcess(),pBundle,16); +} + +//***************************************************************************** +// Extract the addl 22-bit signed immediate from an IA64 bundle +// (Format A5) +//***************************************************************************** +INT32 GetIA64Imm22(UINT64 * pBundle, UINT32 slot) +{ + INT32 imm22 = 0; + UINT64 temp0 = PTR_UINT64(pBundle)[0]; + UINT64 temp1 = PTR_UINT64(pBundle)[1]; + + if (slot == 0) + { + if ((temp0 >> 41) & 1) + imm22 = 0xFFE00000; // 1 sign bit + imm22 |= (temp0 >> 11) & 0x001F0000; // 5 imm5c + imm22 |= (temp0 >> 25) & 0x0000FF80; // 9 imm9d + imm22 |= (temp0 >> 18) & 0x0000007F; // 7 imm7b + } + else if (slot == 1) + { + if ((temp1 >> 18) & 1) + imm22 = 0xFFE00000; // 1 sign bit + imm22 |= (temp1 << 9) & 0x001F0000; // 5 imm5c + imm22 |= (temp1 >> 2) & 0x0000FF80; // 9 imm9d + imm22 |= (temp1 << 5) & 0x00000060; // 2 imm7b (hi2) + imm22 |= (temp0 >> 59) & 0x0000001F; // 5 imm7b (lo5) + } + else if (slot == 2) + { + if ((temp1 >> 59) & 1) + imm22 = 0xFFE00000; // 1 sign bit + imm22 |= (temp1 >> 32) & 0x001F0000; // 5 imm5c + imm22 |= (temp1 >> 43) & 0x0000FF80; // 9 imm9d + imm22 |= (temp1 >> 36) & 0x0000007F; // 7 imm7b + } + + return imm22; +} + +//***************************************************************************** +// Deposit the addl 22-bit signed immediate into an IA64 bundle +// (Format A5) +//***************************************************************************** +void PutIA64Imm22(UINT64 * pBundle, UINT32 slot, INT32 imm22) +{ + if (slot == 0) + { + const UINT64 mask0 = UI64(0xFFFFFC000603FFFF); + /* Clear all bits used as part of the imm22 */ + pBundle[0] &= mask0; + + UINT64 temp0; + + temp0 = (UINT64) (imm22 & 0x200000) << 20; // 1 s + temp0 |= (UINT64) (imm22 & 0x1F0000) << 11; // 5 imm5c + temp0 |= (UINT64) (imm22 & 0x00FF80) << 25; // 9 imm9d + temp0 |= (UINT64) (imm22 & 0x00007F) << 18; // 7 imm7b + + /* Or in the new bits used in the imm22 */ + pBundle[0] |= temp0; + + } + else if (slot == 1) + { + const UINT64 mask0 = UI64(0x07FFFFFFFFFFFFFF); + const UINT64 mask1 = UI64(0xFFFFFFFFFFF8000C); + /* Clear all bits used as part of the imm22 */ + pBundle[0] &= mask0; + pBundle[1] &= mask1; + + UINT64 temp0; + UINT64 temp1; + + temp1 = (UINT64) (imm22 & 0x200000) >> 4; // 1 s + temp1 |= (UINT64) (imm22 & 0x1F0000) >> 9; // 5 imm5c + temp1 |= (UINT64) (imm22 & 0x00FF80) << 2; // 9 imm9d + temp1 |= (UINT64) (imm22 & 0x000060) >> 5; // 2 imm7b (hi2) + temp0 = (UINT64) (imm22 & 0x00001F) << 59; // 5 imm7b (hi2) + + /* Or in the new bits used in the imm22 */ + pBundle[0] |= temp0; + pBundle[1] |= temp1; + } + else if (slot == 0) + { + const UINT64 mask1 = UI64(0xF000180FFFFFFFFF); + /* Clear all bits used as part of the imm22 */ + pBundle[1] &= mask1; + + UINT64 temp1; + + temp1 = (UINT64) (imm22 & 0x200000) << 37; // 1 s + temp1 |= (UINT64) (imm22 & 0x1F0000) << 32; // 5 imm5c + temp1 |= (UINT64) (imm22 & 0x00FF80) << 43; // 9 imm9d + temp1 |= (UINT64) (imm22 & 0x00007F) << 36; // 7 imm7b + + /* Or in the new bits used in the imm22 */ + pBundle[1] |= temp1; + } + FlushInstructionCache(GetCurrentProcess(),pBundle,16); +} + +//***************************************************************************** +// Extract the IP-Relative signed 25-bit immediate from an IA64 bundle +// (Formats B1, B2 or B3) +// Note that due to branch target alignment requirements +// the lowest four bits in the result will always be zero. +//***************************************************************************** +INT32 GetIA64Rel25(UINT64 * pBundle, UINT32 slot) +{ + WRAPPER_NO_CONTRACT; + + UINT64 temp0 = PTR_UINT64(pBundle)[0]; + UINT64 temp1 = PTR_UINT64(pBundle)[1]; + + return GetIA64Rel25(temp0, temp1, slot); +} + +INT32 GetIA64Rel25(UINT64 qword0, UINT64 qword1, UINT32 slot) +{ + LIMITED_METHOD_CONTRACT; + + INT32 imm25 = 0; + + if (slot == 2) + { + if ((qword1 >> 59) & 1) + imm25 = 0xFF000000; + imm25 |= (qword1 >> 32) & 0x00FFFFF0; // 20 imm20b + } + else if (slot == 1) + { + if ((qword1 >> 18) & 1) + imm25 = 0xFF000000; + imm25 |= (qword1 << 9) & 0x00FFFE00; // high 15 of imm20b + imm25 |= (qword0 >> 55) & 0x000001F0; // low 5 of imm20b + } + else if (slot == 0) + { + if ((qword0 >> 41) & 1) + imm25 = 0xFF000000; + imm25 |= (qword0 >> 14) & 0x00FFFFF0; // 20 imm20b + } + + return imm25; +} + +//***************************************************************************** +// Deposit the IP-Relative signed 25-bit immediate into an IA64 bundle +// (Formats B1, B2 or B3) +// Note that due to branch target alignment requirements +// the lowest four bits are required to be zero. +//***************************************************************************** +void PutIA64Rel25(UINT64 * pBundle, UINT32 slot, INT32 imm25) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE((imm25 & 0xF) == 0); + + if (slot == 2) + { + const UINT64 mask1 = UI64(0xF700000FFFFFFFFF); + /* Clear all bits used as part of the imm25 */ + pBundle[1] &= mask1; + + UINT64 temp1; + + temp1 = (UINT64) (imm25 & 0x1000000) << 35; // 1 s + temp1 |= (UINT64) (imm25 & 0x0FFFFF0) << 32; // 20 imm20b + + /* Or in the new bits used in the imm64 */ + pBundle[1] |= temp1; + } + else if (slot == 1) + { + const UINT64 mask0 = UI64(0x0EFFFFFFFFFFFFFF); + const UINT64 mask1 = UI64(0xFFFFFFFFFFFB8000); + /* Clear all bits used as part of the imm25 */ + pBundle[0] &= mask0; + pBundle[1] &= mask1; + + UINT64 temp0; + UINT64 temp1; + + temp1 = (UINT64) (imm25 & 0x1000000) >> 7; // 1 s + temp1 |= (UINT64) (imm25 & 0x0FFFE00) >> 9; // high 15 of imm20b + temp0 = (UINT64) (imm25 & 0x00001F0) << 55; // low 5 of imm20b + + /* Or in the new bits used in the imm64 */ + pBundle[0] |= temp0; + pBundle[1] |= temp1; + } + else if (slot == 0) + { + const UINT64 mask0 = UI64(0xFFFFFDC00003FFFF); + /* Clear all bits used as part of the imm25 */ + pBundle[0] &= mask0; + + UINT64 temp0; + + temp0 = (UINT64) (imm25 & 0x1000000) << 16; // 1 s + temp0 |= (UINT64) (imm25 & 0x0FFFFF0) << 14; // 20 imm20b + + /* Or in the new bits used in the imm64 */ + pBundle[0] |= temp0; + + } + FlushInstructionCache(GetCurrentProcess(),pBundle,16); +} + +//***************************************************************************** +// Extract the IP-Relative signed 64-bit immediate from an IA64 bundle +// (Formats X3 or X4) +//***************************************************************************** +INT64 GetIA64Rel64(UINT64 * pBundle) +{ + WRAPPER_NO_CONTRACT; + + UINT64 temp0 = PTR_UINT64(pBundle)[0]; + UINT64 temp1 = PTR_UINT64(pBundle)[1]; + + return GetIA64Rel64(temp0, temp1); +} + +INT64 GetIA64Rel64(UINT64 qword0, UINT64 qword1) +{ + LIMITED_METHOD_CONTRACT; + + INT64 imm64 = 0; + +#ifdef _DEBUG_IMPL + // + // make certain we're decoding a brl opcode, with template 4 or 5 + // + UINT64 templa = (qword0 >> 0) & 0x1f; + UINT64 opcode = (qword1 >> 60) & 0xf; + + _ASSERTE(((opcode == 0xC) || (opcode == 0xD)) && + ((templa == 0x4) || (templa == 0x5))); +#endif + + imm64 = (qword1 >> 59) << 63; // 1 i + imm64 |= (qword1 << 41) >> 1; // 23 high bits of imm39 + imm64 |= (qword0 >> 48) << 24; // 16 low bits of imm39 + imm64 |= (qword1 >> 32) & 0xFFFFF0; // 20 imm20b + // 4 bits of zeros + return imm64; +} + +//***************************************************************************** +// Deposit the IP-Relative signed 64-bit immediate into an IA64 bundle +// (Formats X3 or X4) +//***************************************************************************** +void PutIA64Rel64(UINT64 * pBundle, INT64 imm64) +{ + LIMITED_METHOD_CONTRACT; + +#ifdef _DEBUG_IMPL + // + // make certain we're decoding a brl opcode, with template 4 or 5 + // + UINT64 templa = (pBundle[0] >> 0) & 0x1f; + UINT64 opcode = (pBundle[1] >> 60) & 0xf; + + _ASSERTE(((opcode == 0xC) || (opcode == 0xD)) && + ((templa == 0x4) || (templa == 0x5))); + _ASSERTE((imm64 & 0xF) == 0); +#endif + + const UINT64 mask0 = UI64(0x00003FFFFFFFFFFF); + const UINT64 mask1 = UI64(0xF700000FFF800000); + + /* Clear all bits used as part of the imm64 */ + pBundle[0] &= mask0; + pBundle[1] &= mask1; + + UINT64 temp0 = (imm64 & UI64(0x000000FFFF000000)) << 24; // 16 low bits of imm39 + UINT64 temp1 = (imm64 & UI64(0x8000000000000000)) >> 4 // 1 i + | (imm64 & UI64(0x7FFFFF0000000000)) >> 40 // 23 high bits of imm39 + | (imm64 & UI64(0x0000000000FFFFF0)) << 32; // 20 imm20b + + /* Or in the new bits used in the imm64 */ + pBundle[0] |= temp0; + pBundle[1] |= temp1; + FlushInstructionCache(GetCurrentProcess(),pBundle,16); +} + +//***************************************************************************** +// Extract the 16-bit immediate from ARM Thumb2 Instruction (format T2_N) +//***************************************************************************** +static FORCEINLINE UINT16 GetThumb2Imm16(UINT16 * p) +{ + LIMITED_METHOD_CONTRACT; + + return ((p[0] << 12) & 0xf000) | + ((p[0] << 1) & 0x0800) | + ((p[1] >> 4) & 0x0700) | + ((p[1] >> 0) & 0x00ff); +} + +//***************************************************************************** +// Extract the 32-bit immediate from movw/movt sequence +//***************************************************************************** +UINT32 GetThumb2Mov32(UINT16 * p) +{ + LIMITED_METHOD_CONTRACT; + + // Make sure we are decoding movw/movt sequence + _ASSERTE_IMPL((*(p+0) & 0xFBF0) == 0xF240); + _ASSERTE_IMPL((*(p+2) & 0xFBF0) == 0xF2C0); + + return (UINT32)GetThumb2Imm16(p) + ((UINT32)GetThumb2Imm16(p + 2) << 16); +} + +//***************************************************************************** +// Deposit the 16-bit immediate into ARM Thumb2 Instruction (format T2_N) +//***************************************************************************** +static FORCEINLINE void PutThumb2Imm16(UINT16 * p, UINT16 imm16) +{ + LIMITED_METHOD_CONTRACT; + + USHORT Opcode0 = p[0]; + USHORT Opcode1 = p[1]; + Opcode0 &= ~((0xf000 >> 12) | (0x0800 >> 1)); + Opcode1 &= ~((0x0700 << 4) | (0x00ff << 0)); + Opcode0 |= (imm16 & 0xf000) >> 12; + Opcode0 |= (imm16 & 0x0800) >> 1; + Opcode1 |= (imm16 & 0x0700) << 4; + Opcode1 |= (imm16 & 0x00ff) << 0; + p[0] = Opcode0; + p[1] = Opcode1; +} + +//***************************************************************************** +// Deposit the 32-bit immediate into movw/movt Thumb2 sequence +//***************************************************************************** +void PutThumb2Mov32(UINT16 * p, UINT32 imm32) +{ + LIMITED_METHOD_CONTRACT; + + // Make sure we are decoding movw/movt sequence + _ASSERTE_IMPL((*(p+0) & 0xFBF0) == 0xF240); + _ASSERTE_IMPL((*(p+2) & 0xFBF0) == 0xF2C0); + + PutThumb2Imm16(p, (UINT16)imm32); + PutThumb2Imm16(p + 2, (UINT16)(imm32 >> 16)); +} + +//***************************************************************************** +// Extract the 24-bit rel offset from bl instruction +//***************************************************************************** +INT32 GetThumb2BlRel24(UINT16 * p) +{ + LIMITED_METHOD_CONTRACT; + + USHORT Opcode0 = p[0]; + USHORT Opcode1 = p[1]; + + UINT32 S = Opcode0 >> 10; + UINT32 J2 = Opcode1 >> 11; + UINT32 J1 = Opcode1 >> 13; + + INT32 ret = + ((S << 24) & 0x1000000) | + (((J1 ^ S ^ 1) << 23) & 0x0800000) | + (((J2 ^ S ^ 1) << 22) & 0x0400000) | + ((Opcode0 << 12) & 0x03FF000) | + ((Opcode1 << 1) & 0x0000FFE); + + // Sign-extend and return + return (ret << 7) >> 7; +} + +//***************************************************************************** +// Extract the 24-bit rel offset from bl instruction +//***************************************************************************** +void PutThumb2BlRel24(UINT16 * p, INT32 imm24) +{ + LIMITED_METHOD_CONTRACT; + + // Verify that we got a valid offset + _ASSERTE(FitsInThumb2BlRel24(imm24)); + +#if defined(_TARGET_ARM_) + // Ensure that the ThumbBit is not set on the offset + // as it cannot be encoded. + _ASSERTE(!(imm24 & THUMB_CODE)); +#endif // _TARGET_ARM_ + + USHORT Opcode0 = p[0]; + USHORT Opcode1 = p[1]; + Opcode0 &= 0xF800; + Opcode1 &= 0xD000; + + UINT32 S = (imm24 & 0x1000000) >> 24; + UINT32 J1 = ((imm24 & 0x0800000) >> 23) ^ S ^ 1; + UINT32 J2 = ((imm24 & 0x0400000) >> 22) ^ S ^ 1; + + Opcode0 |= ((imm24 & 0x03FF000) >> 12) | (S << 10); + Opcode1 |= ((imm24 & 0x0000FFE) >> 1) | (J1 << 13) | (J2 << 11); + + p[0] = Opcode0; + p[1] = Opcode1; + + _ASSERTE(GetThumb2BlRel24(p) == imm24); +} + +//***************************************************************************** +// Extract the PC-Relative offset from a b or bl instruction +//***************************************************************************** +INT32 GetArm64Rel28(UINT32 * pCode) +{ + LIMITED_METHOD_CONTRACT; + + UINT32 branchInstr = *pCode; + + // first shift 6 bits left to set the sign bit, + // then arithmetic shift right by 4 bits + INT32 imm28 = (((INT32)(branchInstr & 0x03FFFFFF)) << 6) >> 4; + + return imm28; +} + +//***************************************************************************** +// Deposit the PC-Relative offset 'imm28' into a b or bl instruction +//***************************************************************************** +void PutArm64Rel28(UINT32 * pCode, INT32 imm28) +{ + LIMITED_METHOD_CONTRACT; + + // Verify that we got a valid offset + _ASSERTE(FitsInRel28(imm28)); + _ASSERTE((imm28 & 0x3) == 0); // the low two bits must be zero + + UINT32 branchInstr = *pCode; + + branchInstr &= 0xFC000000; // keep bits 31-26 + + // Assemble the pc-relative delta 'imm28' into the branch instruction + branchInstr |= ((imm28 >> 2) & 0x03FFFFFF); + + *pCode = branchInstr; // write the assembled instruction + + _ASSERTE(GetArm64Rel28(pCode) == imm28); +} + +//--------------------------------------------------------------------- +// Splits a command line into argc/argv lists, using the VC7 parsing rules. +// +// This functions interface mimics the CommandLineToArgvW api. +// +// If function fails, returns NULL. +// +// If function suceeds, call delete [] on return pointer when done. +// +//--------------------------------------------------------------------- +// NOTE: Implementation-wise, once every few years it would be a good idea to +// compare this code with the C runtime library's parse_cmdline method, +// which is in vctools\crt\crtw32\startup\stdargv.c. (Note we don't +// support wild cards, and we use Unicode characters exclusively.) +// We are up to date as of ~6/2005. +//--------------------------------------------------------------------- +LPWSTR *SegmentCommandLine(LPCWSTR lpCmdLine, DWORD *pNumArgs) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FAULT; + + + *pNumArgs = 0; + + int nch = (int)wcslen(lpCmdLine); + + // Calculate the worstcase storage requirement. (One pointer for + // each argument, plus storage for the arguments themselves.) + int cbAlloc = (nch+1)*sizeof(LPWSTR) + sizeof(WCHAR)*(nch + 1); + LPWSTR pAlloc = new (nothrow) WCHAR[cbAlloc / sizeof(WCHAR)]; + if (!pAlloc) + return NULL; + + LPWSTR *argv = (LPWSTR*) pAlloc; // We store the argv pointers in the first halt + LPWSTR pdst = (LPWSTR)( ((BYTE*)pAlloc) + sizeof(LPWSTR)*(nch+1) ); // A running pointer to second half to store arguments + LPCWSTR psrc = lpCmdLine; + WCHAR c; + BOOL inquote; + BOOL copychar; + int numslash; + + // First, parse the program name (argv[0]). Argv[0] is parsed under + // special rules. Anything up to the first whitespace outside a quoted + // subtring is accepted. Backslashes are treated as normal characters. + argv[ (*pNumArgs)++ ] = pdst; + inquote = FALSE; + do { + if (*psrc == W('"') ) + { + inquote = !inquote; + c = *psrc++; + continue; + } + *pdst++ = *psrc; + + c = *psrc++; + + } while ( (c != W('\0') && (inquote || (c != W(' ') && c != W('\t')))) ); + + if ( c == W('\0') ) { + psrc--; + } else { + *(pdst-1) = W('\0'); + } + + inquote = FALSE; + + + + /* loop on each argument */ + for(;;) + { + if ( *psrc ) + { + while (*psrc == W(' ') || *psrc == W('\t')) + { + ++psrc; + } + } + + if (*psrc == W('\0')) + break; /* end of args */ + + /* scan an argument */ + argv[ (*pNumArgs)++ ] = pdst; + + /* loop through scanning one argument */ + for (;;) + { + copychar = 1; + /* Rules: 2N backslashes + " ==> N backslashes and begin/end quote + 2N+1 backslashes + " ==> N backslashes + literal " + N backslashes ==> N backslashes */ + numslash = 0; + while (*psrc == W('\\')) + { + /* count number of backslashes for use below */ + ++psrc; + ++numslash; + } + if (*psrc == W('"')) + { + /* if 2N backslashes before, start/end quote, otherwise + copy literally */ + if (numslash % 2 == 0) + { + if (inquote && psrc[1] == W('"')) + { + psrc++; /* Double quote inside quoted string */ + } + else + { + /* skip first quote char and copy second */ + copychar = 0; /* don't copy quote */ + inquote = !inquote; + } + } + numslash /= 2; /* divide numslash by two */ + } + + /* copy slashes */ + while (numslash--) + { + *pdst++ = W('\\'); + } + + /* if at end of arg, break loop */ + if (*psrc == W('\0') || (!inquote && (*psrc == W(' ') || *psrc == W('\t')))) + break; + + /* copy character into argument */ + if (copychar) + { + *pdst++ = *psrc; + } + ++psrc; + } + + /* null-terminate the argument */ + + *pdst++ = W('\0'); /* terminate string */ + } + + /* We put one last argument in -- a null ptr */ + argv[ (*pNumArgs) ] = NULL; + + // If we hit this assert, we overwrote our destination buffer. + // Since we're supposed to allocate for the worst + // case, either the parsing rules have changed or our worse case + // formula is wrong. + _ASSERTE((BYTE*)pdst <= (BYTE*)pAlloc + cbAlloc); + return argv; +} + +Volatile<PVOID> ForbidCallsIntoHostOnThisThread::s_pvOwningFiber = NULL; + +#ifdef ENABLE_CONTRACTS_IMPL + +enum SOViolationType { + SO_Violation_Intolerant = 0, + SO_Violation_NotMainline = 1, + SO_Violation_Backout = 2, +}; + +struct HashedSOViolations { + ULONG m_hash; + HashedSOViolations* m_pNext; + HashedSOViolations(ULONG hash, HashedSOViolations *pNext) : m_hash(hash), m_pNext(pNext) {} +}; + +static HashedSOViolations *s_pHashedSOViolations = NULL; + +void SOViolation(const char *szFunction, const char *szFile, int lineNum, SOViolationType violation); + + +// +// SOTolerantViolation is used to report an SO-intolerant function that is not running behind a probe. +// +void SOTolerantViolation(const char *szFunction, const char *szFile, int lineNum) +{ + return SOViolation(szFunction, szFile, lineNum, SO_Violation_Intolerant); +} + +// +// SONotMainlineViolation is used to report any code with SO_NOT_MAINLINE being run in a test environment +// with COMPLUS_NO_SO_NOT_MAINLINE enabled +// +void SONotMainlineViolation(const char *szFunction, const char *szFile, int lineNum) +{ + return SOViolation(szFunction, szFile, lineNum, SO_Violation_NotMainline); +} + +// +// SONotMainlineViolation is used to report any code with SO_NOT_MAINLINE being run in a test environment +// with COMPLUS_NO_SO_NOT_MAINLINE enabled +// +void SOBackoutViolation(const char *szFunction, const char *szFile, int lineNum) +{ + return SOViolation(szFunction, szFile, lineNum, SO_Violation_Backout); +} + +// +// Code common to SO violations +// +// The default is to throw up an ASSERT. But the function can also dump violations to a file and +// ensure that only unique violations are tracked. +// +void SOViolation(const char *szFunction, const char *szFile, int lineNum, SOViolationType violationType) +{ + // This function is called from places that don't allow a throw. But this is debug-only + // code that should eventually never be called once all the violations are gone. + CONTRACT_VIOLATION(ThrowsViolation|FaultViolation|TakesLockViolation); + + static BOOL fDumpToFileInitialized = FALSE; + static BOOL fDumpToFile = FALSE; + +#pragma warning(disable:4640) // Suppress warning: construction of local static object is not thread-safe + static SString hashFN; + static SString fnameFN; + static SString detailsFN; +#pragma warning(default:4640) + + static int dumpLock = -1; + + static CHAR szExprWithStack[10480]; + static DWORD stackTraceLength = 20; + + if (fDumpToFileInitialized == FALSE) + { + stackTraceLength = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_SODumpViolationsStackTraceLength, stackTraceLength); + // Limit the length or we'll overflow our buffer + if (stackTraceLength > cfrMaxAssertStackLevels) + { + stackTraceLength = cfrMaxAssertStackLevels; + } + NewArrayHolder<WCHAR> dumpDir(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_SODumpViolationsDir)); + if (dumpDir == NULL) + { + fDumpToFileInitialized = TRUE; + } + else + { + fDumpToFile = TRUE; + hashFN.Append(SString(dumpDir.GetValue())); + hashFN.Append(W("\\SOViolationHashes.txt")); + fnameFN.Append(SString(dumpDir.GetValue())); + fnameFN.Append(W("\\SOViolationFunctionNames.txt")); + detailsFN.Append(SString(dumpDir.GetValue())); + detailsFN.Append(W("\\SOViolationDetails.txt")); + } + } + + char buff[1024]; + + if (violationType == SO_Violation_NotMainline) + { + sprintf_s(buff, + _countof(buff), + "CONTRACT VIOLATION by %s at \"%s\" @ %d\n\n" + "SO-not-mainline function being called with not-mainline checking enabled.\n" + "\nPlease open a bug against the feature owner.\n" + "\nNOTE: You can disable this ASSERT by setting COMPLUS_SOEnableDefaultRWValidation=0.\n" + " or by turning of not-mainline checking by by setting COMPLUS_NO_SO_NOT_MAINLINE=0.\n" + "\nFor details about this feature, see, in a CLR enlistment,\n" + "src\\ndp\\clr\\doc\\OtherDevDocs\\untriaged\\clrdev_web\\SO Guide for CLR Developers.doc\n", + szFunction, szFile, lineNum); + } + else if (violationType == SO_Violation_Backout) + { + sprintf_s(buff, + _countof(buff), + "SO Backout Marker overrun.\n\n" + "A dtor or handler path exceeded the backout code stack consumption limit.\n" + "\nPlease open a bug against the feature owner.\n" + "\nNOTE: You can disable this ASSERT by setting COMPLUS_SOEnableBackoutStackValidation=0.\n" + "\nFor details about this feature, see, in a CLR enlistment,\n" + "src\\ndp\\clr\\doc\\OtherDevDocs\\untriaged\\clrdev_web\\SO Guide for CLR Developers.doc\n", + szFunction, szFile, lineNum); + } + else + { + sprintf_s(buff, + _countof(buff), + "CONTRACT VIOLATION by %s at \"%s\" @ %d\n\n" + "SO-intolerant function called outside an SO probe.\n" + "\nPlease open a bug against the feature owner.\n" + "\nNOTE: You can disable this ASSERT by setting COMPLUS_SOEnableDefaultRWValidation=0.\n" + "\nFor details about this feature, see, in a CLR enlistment,\n" + "src\\ndp\\clr\\doc\\OtherDevDocs\\untriaged\\clrdev_web\\SO Guide for CLR Developers.doc\n", + szFunction, szFile, lineNum); + } + + // At this point, we've checked if we should dump to file or not and so can either + // do the assert or fall through and dump to a file. + if (! fDumpToFile) + { + DbgAssertDialog((char *)szFile, lineNum, buff); + return; + } + + // If we are dumping violations to a file, we want to avoid duplicates so that we can run multiple tests + // and find unique violations and not end up with massively long files. + // We keep three files: + // 1) a list of the hashed strings for each unique filename/function + // 2) a list of the actual filename/function for unique violations and + // 3) a detailed assert dump for the violation itself + // + // First thing to do is read in the hashes file if this is our first violation. We read the filenames into a linked + // list with their hashes. + // + // Then we want to search through the list for that violation + + // If it's new, then we insert the violation at the front of our list and append it to the violation files + // Otherwise, if we've already seen this violation, we can ignore it. + + + HANDLE hashesDumpFileHandle = INVALID_HANDLE_VALUE; + + StackScratchBuffer buffer; + // First see if we've initialized yet + if (fDumpToFileInitialized == FALSE) + { + LONG lAlreadyOwned = InterlockedExchange((LPLONG)&dumpLock, 1); + if (lAlreadyOwned == 1) + { + // somebody else has gotten here first. So just skip this violation. + return; + } + + // This is our first time through, so read in the existing file and create a linked list of hashed names from it. + hashesDumpFileHandle = CreateFileA( + hashFN.GetANSI(buffer), + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_WRITE_THROUGH, + NULL); + + // If we successfully opened the file, pull out each hash number and add it to a linked list of known violations. + // Otherwise, if we couldn't open the file, assume that there were no preexisting violations. The worse thing + // that will happen in this case is that we might report some dups. + if (hashesDumpFileHandle != INVALID_HANDLE_VALUE) + { + DWORD dwFileSize = GetFileSize( hashesDumpFileHandle, NULL ); + + NewArrayHolder<char> pBuffer(new char[dwFileSize]); + DWORD cbBuffer = dwFileSize; + DWORD cbRead; + DWORD result = ReadFile( hashesDumpFileHandle, pBuffer.GetValue(), cbBuffer, &cbRead, NULL ); + + CloseHandle( hashesDumpFileHandle ); + hashesDumpFileHandle = INVALID_HANDLE_VALUE; + + // If we couldn't read the file, assume that there were no preexisting violations. Worse thing + // that will happen is we might report some dups. + if (result && cbRead == cbBuffer) + { + char *pBuf = pBuffer.GetValue(); + COUNT_T count = 0; + LOG((LF_EH, LL_INFO100000, "SOTolerantViolation: Reading known violations\n")); + while (count < cbRead) + { + char *pHashStart = pBuf + count; + char *pHashEnd = strstr(pHashStart, "\r\n"); + COUNT_T len = static_cast<COUNT_T>(pHashEnd-pHashStart); + SString hashString(SString::Ascii, pHashStart, len); + ULONG hashValue = wcstoul(hashString.GetUnicode(), NULL, 16); + HashedSOViolations *pHashedSOViolations = new HashedSOViolations(hashValue, s_pHashedSOViolations); + s_pHashedSOViolations = pHashedSOViolations; + count += (len + 2); + LOG((LF_ALWAYS, LL_ALWAYS, " %8.8x\n", pHashedSOViolations->m_hash)); + } + } + } + fDumpToFileInitialized = TRUE; + dumpLock = -1; + } + + + SString violation; + violation.Append(SString(SString::Ascii, szFile)); + violation.Append(W(" ")); + violation.Append(SString(SString::Ascii, szFunction)); + HashedSOViolations *cur = s_pHashedSOViolations; + + // look for the violation in the list + while (cur != NULL) + { + if (cur->m_hash == violation.Hash()) + { + return; + } + cur = cur->m_pNext; + } + + LONG lAlreadyOwned = InterlockedExchange((LPLONG)&dumpLock, 1); + if (lAlreadyOwned == 1) + { + // somebody else has gotten here first. So just skip this violation. + return; + } + + HANDLE functionsDumpFileHandle = INVALID_HANDLE_VALUE; + HANDLE detailsDumpFileHandle = INVALID_HANDLE_VALUE; + + // This is a new violation + // Append new violations to the output files + functionsDumpFileHandle = CreateFileA( + fnameFN.GetANSI(buffer), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_WRITE_THROUGH, NULL); + + if (functionsDumpFileHandle != INVALID_HANDLE_VALUE) + { + // First write it to the filename dump + SetFilePointer(functionsDumpFileHandle, NULL, NULL, FILE_END); + + DWORD written; + char *szExpr = &szExprWithStack[0]; + sprintf_s(szExpr, _countof(szExprWithStack), "%s %8.8x\r\n", violation.GetANSI(buffer), violation.Hash()); + WriteFile(functionsDumpFileHandle, szExpr, static_cast<DWORD>(strlen(szExpr)), &written, NULL); + CloseHandle(functionsDumpFileHandle); + + // Now write it to the hashes dump. Once we've got it in the filename dump, we don't + // care if these others fail. We can live w/o detailed info or with dups. + hashesDumpFileHandle = CreateFileA( + hashFN.GetANSI(buffer), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_WRITE_THROUGH, NULL); + + if (hashesDumpFileHandle != INVALID_HANDLE_VALUE) + { + SetFilePointer(hashesDumpFileHandle, NULL, NULL, FILE_END); + + DWORD written; + sprintf_s(szExpr, _countof(szExprWithStack), "%8.8x", violation.Hash()); + strcat_s(szExpr, _countof(szExprWithStack), "\r\n"); + WriteFile(hashesDumpFileHandle, szExpr, static_cast<DWORD>(strlen(szExpr)), &written, NULL); + CloseHandle(hashesDumpFileHandle); + hashesDumpFileHandle = INVALID_HANDLE_VALUE; + } + + // Now write it to the details dump + strcpy_s(szExpr, _countof(szExprWithStack), buff); + strcat_s(szExpr, _countof(szExprWithStack), "\n\n"); +#ifndef FEATURE_PAL + GetStringFromStackLevels(1, stackTraceLength, szExprWithStack + strlen(szExprWithStack)); + strcat_s(szExpr, _countof(szExprWithStack), "\n\n"); +#endif // FEATURE_PAL + char exeName[300]; + GetModuleFileNameA(NULL, exeName, sizeof(exeName)/sizeof(WCHAR)); + strcat_s(szExpr, _countof(szExprWithStack), exeName); + strcat_s(szExpr, _countof(szExprWithStack), "\n\n\n"); + + detailsDumpFileHandle = CreateFileA( + detailsFN.GetANSI(buffer), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_WRITE_THROUGH, NULL); + + if (detailsDumpFileHandle != INVALID_HANDLE_VALUE) + { + SetFilePointer(detailsDumpFileHandle, NULL, NULL, FILE_END); + WriteFile(detailsDumpFileHandle, szExpr, static_cast<DWORD>(strlen(szExpr)), &written, NULL); + CloseHandle(detailsDumpFileHandle); + detailsDumpFileHandle = INVALID_HANDLE_VALUE; + } + + // add the new violation to our list + HashedSOViolations *pHashedSOViolations = new HashedSOViolations(violation.Hash(), s_pHashedSOViolations); + s_pHashedSOViolations = pHashedSOViolations; + LOG((LF_ALWAYS, LL_ALWAYS, "SOTolerantViolation: Adding new violation %8.8x %s\n", pHashedSOViolations->m_hash, violation.GetANSI(buffer))); + dumpLock = -1; + } +} + +void SoTolerantViolationHelper(const char *szFunction, + const char *szFile, + int lineNum) +{ + // Keep this function separate to avoid overhead of EH in the normal case where we don't assert + // Enter SO-tolerant mode for scope of this call so that we don't get contract asserts + // in anything called downstream of CONTRACT_ASSERT. If we unwind out of here, our dtor + // will reset our state to what it was on entry. + CONTRACT_VIOLATION(SOToleranceViolation); + + SOTolerantViolation(szFunction, szFile, lineNum); + +} + +void CloseSOTolerantViolationFile() +{ + // We used to have a file to close. Now we just cleanup the memory. + HashedSOViolations *ptr = s_pHashedSOViolations; + while (ptr != NULL) + { + s_pHashedSOViolations = s_pHashedSOViolations->m_pNext; + delete ptr; + ptr = s_pHashedSOViolations; + } +} +#endif //ENABLE_CONTRACTS_IMPL + +BOOL FileExists(LPCWSTR filename) +{ + WIN32_FIND_DATA data; + HANDLE h = WszFindFirstFile(filename, &data); + if (h == INVALID_HANDLE_VALUE) + { + return FALSE; + } + + ::FindClose(h); + + return TRUE; +} + +#ifndef FEATURE_CORECLR +// Current users for FileLock are ngen and ngen service + +FileLockHolder::FileLockHolder() +{ + _hLock = INVALID_HANDLE_VALUE; +} + +FileLockHolder::~FileLockHolder() +{ + Release(); +} + +// the amount of time we want to wait +#define FILE_LOCK_RETRY_TIME 100 + +void FileLockHolder::Acquire(LPCWSTR lockName, HANDLE hInterrupt, BOOL* pInterrupted) +{ + DWORD dwErr = 0; + DWORD dwAccessDeniedRetry = 0; + const DWORD MAX_ACCESS_DENIED_RETRIES = 10; + + if (pInterrupted) + { + *pInterrupted = FALSE; + } + + _ASSERTE(_hLock == INVALID_HANDLE_VALUE); + + for (;;) { + _hLock = WszCreateFile(lockName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL); + if (_hLock != INVALID_HANDLE_VALUE) { + return; + } + + dwErr = GetLastError(); + // Logically we should only expect ERROR_SHARING_VIOLATION, but Windows can also return + // ERROR_ACCESS_DENIED for underlying NtStatus DELETE_PENDING. That happens when another process + // (gacutil.exe or indexer) have the file opened. Unfortunately there is no public API that would + // allow us to detect this NtStatus and distinguish it from ‘real’ access denied (candidates are + // RtlGetLastNtStatus that is not documented on MSDN and NtCreateFile that is internal and can change + // at any time), so we retry on access denied, but only for a limited number of times. + if (dwErr == ERROR_SHARING_VIOLATION || + (dwErr == ERROR_ACCESS_DENIED && ++dwAccessDeniedRetry <= MAX_ACCESS_DENIED_RETRIES)) + { + // Somebody is holding the lock. Let's sleep, and come back again. + if (hInterrupt) + { + _ASSERTE(pInterrupted && + "If you can be interrupted, you better want to know if you actually were interrupted"); + if (WaitForSingleObject(hInterrupt, FILE_LOCK_RETRY_TIME) == WAIT_OBJECT_0) + { + if (pInterrupted) + { + *pInterrupted = TRUE; + } + + // We've been interrupted, so return without acquiring + return; + } + } + else + { + ClrSleepEx(FILE_LOCK_RETRY_TIME, FALSE); + } + } + else { + ThrowHR(HRESULT_FROM_WIN32(dwErr)); + } + } +} + + +HRESULT FileLockHolder::AcquireNoThrow(LPCWSTR lockName, HANDLE hInterrupt, BOOL* pInterrupted) +{ + HRESULT hr = S_OK; + + EX_TRY + { + Acquire(lockName, hInterrupt, pInterrupted); + } + EX_CATCH_HRESULT(hr); + + return hr; +} + +BOOL FileLockHolder::IsTaken(LPCWSTR lockName) +{ + + // We don't want to do an acquire the lock to know if its taken, so we want to see if the file + // exists. However, in situations like unplugging a machine, a DELETE_ON_CLOSE still leaves the file + // around. We try to delete it here. If the lock is acquired, DeleteFile will fail, as the file is + // not opened with SHARE_DELETE. + WszDeleteFile(lockName); + + return FileExists(lockName); +} + +void FileLockHolder::Release() +{ + if (_hLock != INVALID_HANDLE_VALUE) { + CloseHandle(_hLock); + _hLock = INVALID_HANDLE_VALUE; + } +} +#endif // FEATURE_CORECLR + +//====================================================================== +// This function returns true, if it can determine that the instruction pointer +// refers to a code address that belongs in the range of the given image. +// <TODO>@TODO: Merge with IsIPInModule from vm\util.hpp</TODO> + +BOOL IsIPInModule(HMODULE_TGT hModule, PCODE ip) +{ + STATIC_CONTRACT_LEAF; + SUPPORTS_DAC; + + struct Param + { + HMODULE_TGT hModule; + PCODE ip; + BOOL fRet; + } param; + param.hModule = hModule; + param.ip = ip; + param.fRet = FALSE; + +// UNIXTODO: implement a proper version for PAL +#ifndef FEATURE_PAL + PAL_TRY(Param *, pParam, ¶m) + { + PTR_BYTE pBase = dac_cast<PTR_BYTE>(pParam->hModule); + + PTR_IMAGE_DOS_HEADER pDOS = NULL; + PTR_IMAGE_NT_HEADERS pNT = NULL; + USHORT cbOptHdr; + PCODE baseAddr; + + // + // First, must validate the format of the PE headers to make sure that + // the fields we're interested in using exist in the image. + // + + // Validate the DOS header. + pDOS = PTR_IMAGE_DOS_HEADER(pBase); + if (pDOS->e_magic != VAL16(IMAGE_DOS_SIGNATURE) || + pDOS->e_lfanew == 0) + { + goto lDone; + } + + // Validate the NT header + pNT = PTR_IMAGE_NT_HEADERS(pBase + VAL32(pDOS->e_lfanew)); + + if (pNT->Signature != VAL32(IMAGE_NT_SIGNATURE)) + { + goto lDone; + } + + // Validate that the optional header is large enough to contain the fields + // we're interested, namely IMAGE_OPTIONAL_HEADER::SizeOfImage. The reason + // we don't just check that SizeOfOptionalHeader == IMAGE_SIZEOF_NT_OPTIONAL_HEADER + // is due to VSW443590, which states that the extensibility of this structure + // is such that it is possible to include only a portion of the optional header. + cbOptHdr = pNT->FileHeader.SizeOfOptionalHeader; + + // Check that the magic field is contained by the optional header and set to the correct value. + if (cbOptHdr < (offsetof(IMAGE_OPTIONAL_HEADER, Magic) + sizeofmember(IMAGE_OPTIONAL_HEADER, Magic)) || + pNT->OptionalHeader.Magic != VAL16(IMAGE_NT_OPTIONAL_HDR_MAGIC)) + { + goto lDone; + } + + // Check that the SizeOfImage is contained by the optional header. + if (cbOptHdr < (offsetof(IMAGE_OPTIONAL_HEADER, SizeOfImage) + sizeofmember(IMAGE_OPTIONAL_HEADER, SizeOfImage))) + { + goto lDone; + } + + // + // The real check + // + + baseAddr = dac_cast<PCODE>(pBase); + if ((pParam->ip < baseAddr) || (pParam->ip >= (baseAddr + VAL32(pNT->OptionalHeader.SizeOfImage)))) + { + goto lDone; + } + + pParam->fRet = TRUE; + +lDone: ; + } + PAL_EXCEPT (EXCEPTION_EXECUTE_HANDLER) + { + } + PAL_ENDTRY +#endif // !FEATURE_PAL + + return param.fRet; +} + +#ifdef FEATURE_CORRUPTING_EXCEPTIONS + +// To include definition of EXCEPTION_SOFTSO +#include "corexcep.h" + +// These functions provide limited support for corrupting exceptions +// outside the VM folder. Its limited since we dont have access to the +// throwable. +// +// These functions are also wrapped by the corresponding CEHelper +// methods in excep.cpp. + +// Given an exception code, this method returns a BOOL to indicate if the +// code belongs to a corrupting exception or not. +BOOL IsProcessCorruptedStateException(DWORD dwExceptionCode, BOOL fCheckForSO /*=TRUE*/) +{ + LIMITED_METHOD_CONTRACT; + + // By default, assume its not corrupting + BOOL fIsCorruptedStateException = FALSE; + + if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_legacyCorruptedStateExceptionsPolicy) == 1) + { + return fIsCorruptedStateException; + } + + // If we have been asked not to include SO in the CSE check + // and the code represent SO, then exit now. + if ((fCheckForSO == FALSE) && (dwExceptionCode == STATUS_STACK_OVERFLOW)) + { + return fIsCorruptedStateException; + } + + switch(dwExceptionCode) + { + case STATUS_ACCESS_VIOLATION: + case STATUS_STACK_OVERFLOW: + case EXCEPTION_ILLEGAL_INSTRUCTION: + case EXCEPTION_IN_PAGE_ERROR: + case EXCEPTION_INVALID_DISPOSITION: + case EXCEPTION_NONCONTINUABLE_EXCEPTION: + case EXCEPTION_PRIV_INSTRUCTION: + case STATUS_UNWIND_CONSOLIDATE: + fIsCorruptedStateException = TRUE; + break; + } + + return fIsCorruptedStateException; +} + +#endif // FEATURE_CORRUPTING_EXCEPTIONS + +void EnableTerminationOnHeapCorruption() +{ + HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); +} + +RUNTIMEVERSIONINFO RUNTIMEVERSIONINFO::notDefined; + +BOOL IsV2RuntimeLoaded(void) +{ +#ifndef FEATURE_CORECLR + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + } + CONTRACTL_END; + + ReleaseHolder<ICLRMetaHost> pMetaHost(NULL); + ReleaseHolder<IEnumUnknown> pEnum(NULL); + ReleaseHolder<IUnknown> pUnk(NULL); + ReleaseHolder<ICLRRuntimeInfo> pRuntime(NULL); + HRESULT hr; + + HModuleHolder hModule = WszLoadLibrary(MSCOREE_SHIM_W); + if (hModule == NULL) + return FALSE; + + CLRCreateInstanceFnPtr pfnCLRCreateInstance = (CLRCreateInstanceFnPtr)::GetProcAddress(hModule, "CLRCreateInstance"); + if (pfnCLRCreateInstance == NULL) + return FALSE; + + hr = (*pfnCLRCreateInstance)(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID *)&pMetaHost); + if (FAILED(hr)) + return FALSE; + + hr = pMetaHost->EnumerateLoadedRuntimes(GetCurrentProcess(), &pEnum); + if (FAILED(hr)) + return FALSE; + + while (pEnum->Next(1, &pUnk, NULL) == S_OK) + { + hr = pUnk->QueryInterface(IID_ICLRRuntimeInfo, (void **)&pRuntime); + if (FAILED(hr)) + continue; + + WCHAR wszVersion[30]; + DWORD cchVersion = _countof(wszVersion); + hr = pRuntime->GetVersionString(wszVersion, &cchVersion); + if (FAILED(hr)) + continue; + + // Is it a V2 runtime? + if ((cchVersion < 3) || + ((wszVersion[0] != W('v')) && (wszVersion[0] != W('V'))) || + (wszVersion[1] != W('2')) || + (wszVersion[2] != W('.'))) + continue; + + return TRUE; + } +#endif // FEATURE_CORECLR + + return FALSE; +} + +#ifdef FEATURE_COMINTEROP +BOOL IsClrHostedLegacyComObject(REFCLSID rclsid) +{ + // let's simply check for all CLSIDs that are known to be runtime implemented and capped to 2.0 + return ( + rclsid == CLSID_ComCallUnmarshal || +#ifdef FEATURE_INCLUDE_ALL_INTERFACES + rclsid == CLSID_CorRuntimeHost || + rclsid == CLSID_CLRRuntimeHost || + rclsid == CLSID_CLRProfiling || +#endif + rclsid == CLSID_CorMetaDataDispenser || + rclsid == CLSID_CorMetaDataDispenserRuntime || + rclsid == CLSID_TypeNameFactory); +} +#endif // FEATURE_COMINTEROP + +// Returns the directory for HMODULE. So, if HMODULE was for "C:\Dir1\Dir2\Filename.DLL", +// then this would return "C:\Dir1\Dir2\" (note the trailing backslash). +HRESULT GetHModuleDirectory( + __in HMODULE hMod, + __out_z __out_ecount(cchPath) LPWSTR wszPath, + size_t cchPath) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + } + CONTRACTL_END; + + DWORD dwRet = WszGetModuleFileName(hMod, wszPath, static_cast<DWORD>(cchPath)); + + if (dwRet == cchPath) + { // If there are cchPath characters in the string, it means that the string + // itself is longer than cchPath and GetModuleFileName had to truncate at cchPath. + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + else if (dwRet == 0) + { // Some other error. + return HRESULT_FROM_GetLastError(); + } + + LPWSTR wszEnd = wcsrchr(wszPath, W('\\')); + if (wszEnd == NULL) + { // There was no backslash? Not sure what's going on. + return E_UNEXPECTED; + } + + // Include the backslash in the resulting string. + *(++wszEnd) = W('\0'); + + return S_OK; +} + +SString & GetHModuleDirectory(HMODULE hMod, SString &ssDir) +{ + LPWSTR wzDir = ssDir.OpenUnicodeBuffer(_MAX_PATH); + HRESULT hr = GetHModuleDirectory(hMod, wzDir, _MAX_PATH); + ssDir.CloseBuffer(FAILED(hr) ? 0 : static_cast<COUNT_T>(wcslen(wzDir))); + IfFailThrow(hr); + return ssDir; +} + +#if !defined(FEATURE_CORECLR) && !defined(SELF_NO_HOST) && !defined(FEATURE_UTILCODE_NO_DEPENDENCIES) + +namespace UtilCode +{ + +#pragma warning(push) +#pragma warning(disable:4996) // For use of deprecated LoadLibraryShim + + // When a NULL version is passed to LoadLibraryShim, this told the shim to bind the already-loaded + // runtime or to the latest runtime. In hosted environments, we already know a runtime (or two) is + // loaded, and since we are no longer guaranteed that a call to mscoree!LoadLibraryShim with a NULL + // version will return the correct runtime, this code uses the ClrCallbacks infrastructure + // available to get the ICLRRuntimeInfo for the runtime in which this code is hosted, and then + // calls ICLRRuntimeInfo::LoadLibrary to make sure that the load occurs within the context of the + // correct runtime. + HRESULT LoadLibraryShim(LPCWSTR szDllName, LPCWSTR szVersion, LPVOID pvReserved, HMODULE *phModDll) + { + HRESULT hr = S_OK; + + if (szVersion != NULL) + { // If a version is provided, then we just fall back to the legacy function to allow + // it to construct the explicit path and load from that location. + //@TODO: Can we verify that all callers of LoadLibraryShim in hosted environments always pass null and eliminate this code? + return ::LoadLibraryShim(szDllName, szVersion, pvReserved, phModDll); + } + + // + // szVersion is NULL, which means we should load the DLL from the hosted environment's directory. + // + + typedef ICLRRuntimeInfo *GetCLRRuntime_t(); + GetCLRRuntime_t *pfnGetCLRRuntime = + reinterpret_cast<GetCLRRuntime_t *>((*GetClrCallbacks().m_pfnGetCLRFunction)("GetCLRRuntime")); + if (pfnGetCLRRuntime == NULL) + return E_UNEXPECTED; + + ICLRRuntimeInfo* pRI = (*pfnGetCLRRuntime)(); + if (pRI == NULL) + return E_UNEXPECTED; + + return pRI->LoadLibrary(szDllName, phModDll); + } + +#pragma warning(pop) + +} + +#endif //!FEATURE_CORECLR && !SELF_NO_HOST && !FEATURE_UTILCODE_NO_DEPENDENCIES + +namespace Clr +{ +namespace Util +{ + static BOOL g_fLocalAppDataDirectoryInitted = FALSE; + static WCHAR *g_wszLocalAppDataDirectory = NULL; + +// This api returns a pointer to a null-terminated string that contains the local appdata directory +// or it returns NULL in the case that the directory could not be found. The return value from this function +// is not actually checked for existence. + HRESULT GetLocalAppDataDirectory(LPCWSTR *ppwzLocalAppDataDirectory) + { + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + HRESULT hr = S_OK; + *ppwzLocalAppDataDirectory = NULL; + + EX_TRY + { + if (!g_fLocalAppDataDirectoryInitted) + { + WCHAR *wszLocalAppData = NULL; + + DWORD cCharsNeeded; + cCharsNeeded = GetEnvironmentVariableW(W("LOCALAPPDATA"), NULL, 0); + + if ((cCharsNeeded != 0) && (cCharsNeeded < MAX_PATH)) + { + wszLocalAppData = new WCHAR[cCharsNeeded]; + cCharsNeeded = GetEnvironmentVariableW(W("LOCALAPPDATA"), wszLocalAppData, cCharsNeeded); + if (cCharsNeeded != 0) + { + // We've collected the appropriate app data directory into a local. Now publish it. + if (InterlockedCompareExchangeT(&g_wszLocalAppDataDirectory, wszLocalAppData, NULL) == NULL) + { + // This variable doesn't need to be freed, as it has been stored in the global + wszLocalAppData = NULL; + } + } + } + + g_fLocalAppDataDirectoryInitted = TRUE; + delete[] wszLocalAppData; + } + } + EX_CATCH_HRESULT(hr); + + if (SUCCEEDED(hr)) + *ppwzLocalAppDataDirectory = g_wszLocalAppDataDirectory; + + return hr; + } + + HRESULT SetLocalAppDataDirectory(LPCWSTR pwzLocalAppDataDirectory) + { + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + if (pwzLocalAppDataDirectory == NULL || *pwzLocalAppDataDirectory == W('\0')) + return E_INVALIDARG; + + if (g_fLocalAppDataDirectoryInitted) + return E_UNEXPECTED; + + HRESULT hr = S_OK; + + EX_TRY + { + size_t size = wcslen(pwzLocalAppDataDirectory) + 1; + WCHAR *wszLocalAppData = new WCHAR[size]; + wcscpy_s(wszLocalAppData, size, pwzLocalAppDataDirectory); + + // We've collected the appropriate app data directory into a local. Now publish it. + if (InterlockedCompareExchangeT(&g_wszLocalAppDataDirectory, wszLocalAppData, NULL) != NULL) + { + // Someone else already set LocalAppData. Free our copy and return an error. + delete[] wszLocalAppData; + hr = E_UNEXPECTED; + } + + g_fLocalAppDataDirectoryInitted = TRUE; + } + EX_CATCH_HRESULT(hr); + + return hr; + } + +#ifndef FEATURE_PAL +namespace Reg +{ + HRESULT ReadStringValue(HKEY hKey, LPCWSTR wszSubKeyName, LPCWSTR wszValueName, SString & ssValue) + { + STANDARD_VM_CONTRACT; + + if (hKey == NULL) + { + return E_INVALIDARG; + } + + RegKeyHolder hTargetKey; + if (wszSubKeyName == NULL || *wszSubKeyName == W('\0')) + { // No subkey was requested, use hKey as the resolved key. + hTargetKey = hKey; + hTargetKey.SuppressRelease(); + } + else + { // Try to open the specified subkey. + if (WszRegOpenKeyEx(hKey, wszSubKeyName, 0, KEY_READ, &hTargetKey) != ERROR_SUCCESS) + return REGDB_E_CLASSNOTREG; + } + + DWORD type; + DWORD size; + if ((WszRegQueryValueEx(hTargetKey, wszValueName, 0, &type, 0, &size) == ERROR_SUCCESS) && + type == REG_SZ && size > 0) + { + LPWSTR wszValueBuf = ssValue.OpenUnicodeBuffer(static_cast<COUNT_T>((size / sizeof(WCHAR)) - 1)); + LONG lResult = WszRegQueryValueEx( + hTargetKey, + wszValueName, + 0, + 0, + reinterpret_cast<LPBYTE>(wszValueBuf), + &size); + + _ASSERTE(lResult == ERROR_SUCCESS); + if (lResult == ERROR_SUCCESS) + { + // Can't count on the returned size being accurate - I've seen at least + // one string with an extra NULL at the end that will cause the resulting + // SString to count the extra NULL as part of the string. An extra + // terminating NULL is not a legitimate scenario for REG_SZ - this must + // be done using REG_MULTI_SZ - however this was tolerated in the + // past and so it would be a breaking change to stop doing so. + _ASSERTE(wcslen(wszValueBuf) <= (size / sizeof(WCHAR)) - 1); + ssValue.CloseBuffer((COUNT_T)wcsnlen(wszValueBuf, (size_t)size)); + } + else + { + ssValue.CloseBuffer(0); + return HRESULT_FROM_WIN32(lResult); + } + + return S_OK; + } + else + { + return REGDB_E_KEYMISSING; + } + } + + HRESULT ReadStringValue(HKEY hKey, LPCWSTR wszSubKey, LPCWSTR wszName, __deref_out __deref_out_z LPWSTR* pwszValue) + { + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + HRESULT hr = S_OK; + EX_TRY + { + StackSString ssValue; + if (SUCCEEDED(hr = ReadStringValue(hKey, wszSubKey, wszName, ssValue))) + { + *pwszValue = new WCHAR[ssValue.GetCount() + 1]; + wcscpy_s(*pwszValue, ssValue.GetCount() + 1, ssValue.GetUnicode()); + } + } + EX_CATCH_HRESULT(hr); + return hr; + } +} // namespace Reg + +namespace Com +{ + namespace __imp + { + static + HRESULT FindSubKeyDefaultValueForCLSID(REFCLSID rclsid, LPCWSTR wszSubKeyName, SString & ssValue) + { + STANDARD_VM_CONTRACT; + + HRESULT hr = S_OK; + + WCHAR wszClsid[39]; + if (GuidToLPWSTR(rclsid, wszClsid, NumItems(wszClsid)) == 0) + return E_UNEXPECTED; + + StackSString ssKeyName; + ssKeyName.Append(SL(W("CLSID\\"))); + ssKeyName.Append(wszClsid); + ssKeyName.Append(SL(W("\\"))); + ssKeyName.Append(wszSubKeyName); + + return Clr::Util::Reg::ReadStringValue(HKEY_CLASSES_ROOT, ssKeyName.GetUnicode(), NULL, ssValue); + } + + static + HRESULT FindSubKeyDefaultValueForCLSID(REFCLSID rclsid, LPCWSTR wszSubKeyName, __deref_out __deref_out_z LPWSTR* pwszValue) + { + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + HRESULT hr = S_OK; + EX_TRY + { + StackSString ssValue; + if (SUCCEEDED(hr = FindSubKeyDefaultValueForCLSID(rclsid, wszSubKeyName, ssValue))) + { + *pwszValue = new WCHAR[ssValue.GetCount() + 1]; + wcscpy_s(*pwszValue, ssValue.GetCount() + 1, ssValue.GetUnicode()); + } + } + EX_CATCH_HRESULT(hr); + return hr; + } + } + + HRESULT FindServerUsingCLSID(REFCLSID rclsid, __deref_out __deref_out_z LPWSTR* pwszServerName) + { + WRAPPER_NO_CONTRACT; + return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("Server"), pwszServerName); + } + + HRESULT FindServerUsingCLSID(REFCLSID rclsid, SString & ssServerName) + { + WRAPPER_NO_CONTRACT; + return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("Server"), ssServerName); + } + + HRESULT FindInprocServer32UsingCLSID(REFCLSID rclsid, __deref_out __deref_out_z LPWSTR* pwszInprocServer32Name) + { + WRAPPER_NO_CONTRACT; + return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("InprocServer32"), pwszInprocServer32Name); + } + + HRESULT FindInprocServer32UsingCLSID(REFCLSID rclsid, SString & ssInprocServer32Name) + { + WRAPPER_NO_CONTRACT; + return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("InprocServer32"), ssInprocServer32Name); + } + + BOOL IsMscoreeInprocServer32(const SString & ssInprocServer32Name) + { + WRAPPER_NO_CONTRACT; + + return (ssInprocServer32Name.EqualsCaseInsensitive(SL(MSCOREE_SHIM_W)) || + ssInprocServer32Name.EndsWithCaseInsensitive(SL(W("\\") MSCOREE_SHIM_W))); + } + + BOOL CLSIDHasMscoreeAsInprocServer32(REFCLSID rclsid) + { + WRAPPER_NO_CONTRACT; + + StackSString ssInprocServer32; + FindInprocServer32UsingCLSID(rclsid, ssInprocServer32); + return IsMscoreeInprocServer32(ssInprocServer32); + } + +} // namespace Com +#endif // FEATURE_PAL + +namespace Win32 +{ + void GetModuleFileName( + HMODULE hModule, + SString & ssFileName, + bool fAllowLongFileNames) + { + STANDARD_VM_CONTRACT; + + // Try to use what the SString already has allocated. If it does not have anything allocated + // or it has < 20 characters allocated, then bump the size requested to _MAX_PATH. + DWORD dwSize = (DWORD)(ssFileName.GetUnicodeAllocation()) + 1; + dwSize = (dwSize < 20) ? (_MAX_PATH) : (dwSize); + DWORD dwResult = WszGetModuleFileName(hModule, ssFileName.OpenUnicodeBuffer(dwSize - 1), dwSize); + + // if there was a failure, dwResult == 0; + // if there was insufficient buffer, dwResult == dwSize; + // if there was sufficient buffer and a successful write, dwResult < dwSize + ssFileName.CloseBuffer(dwResult < dwSize ? dwResult : 0); + + if (dwResult == 0) + ThrowHR(HRESULT_FROM_GetLastError()); + + // Ok, we didn't have enough buffer. Let's loop, doubling the buffer each time, until we succeed. + while (dwResult == dwSize) + { + dwSize = dwSize * 2; + dwResult = WszGetModuleFileName(hModule, ssFileName.OpenUnicodeBuffer(dwSize - 1), dwSize); + ssFileName.CloseBuffer(dwResult < dwSize ? dwResult : 0); + + if (dwResult == 0) + ThrowHR(HRESULT_FROM_GetLastError()); + } + + // Most of the runtime is not able to handle long filenames. fAllowLongFileNames + // has a default value of false, so that callers will not accidentally get long + // file names returned. + if (!fAllowLongFileNames && ssFileName.BeginsWith(SL(LONG_FILENAME_PREFIX_W))) + { + ssFileName.Clear(); + ThrowHR(E_UNEXPECTED); + } + + _ASSERTE(dwResult != 0 && dwResult < dwSize); + } + + // Returns heap-allocated string in *pwszFileName + HRESULT GetModuleFileName( + HMODULE hModule, + __deref_out_z LPWSTR * pwszFileName, + bool fAllowLongFileNames) + { + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + PRECONDITION(CheckPointer(pwszFileName)); + } CONTRACTL_END; + + HRESULT hr = S_OK; + EX_TRY + { + InlineSString<_MAX_PATH> ssFileName; + GetModuleFileName(hModule, ssFileName); + *pwszFileName = DuplicateStringThrowing(ssFileName.GetUnicode()); + } + EX_CATCH_HRESULT(hr); + + return hr; + } + + void GetFullPathName( + SString const & ssFileName, + SString & ssPathName, + DWORD * pdwFilePartIdx, + bool fAllowLongFileNames) + { + STANDARD_VM_CONTRACT; + + // Get the required buffer length (including terminating NULL). + DWORD dwLengthRequired = WszGetFullPathName(ssFileName.GetUnicode(), 0, NULL, NULL); + + if (dwLengthRequired == 0) + ThrowHR(HRESULT_FROM_GetLastError()); + + LPWSTR wszPathName = ssPathName.OpenUnicodeBuffer(dwLengthRequired - 1); + LPWSTR wszFileName = NULL; + DWORD dwLengthWritten = WszGetFullPathName( + ssFileName.GetUnicode(), + dwLengthRequired, + wszPathName, + &wszFileName); + + // Calculate the index while the buffer is open and the string poiner is stable. + if (dwLengthWritten != 0 && dwLengthWritten < dwLengthRequired && pdwFilePartIdx != NULL) + *pdwFilePartIdx = static_cast<DWORD>(wszFileName - wszPathName); + + ssPathName.CloseBuffer(dwLengthWritten < dwLengthRequired ? dwLengthWritten : 0); + + if (dwLengthRequired == 0) + ThrowHR(HRESULT_FROM_GetLastError()); + + // Overly defensive? Perhaps. + if (!(dwLengthWritten < dwLengthRequired)) + ThrowHR(E_UNEXPECTED); + + // Most of the runtime is not able to handle long filenames. fAllowLongFileNames + // has a default value of false, so that callers will not accidentally get long + // file names returned. + if (!fAllowLongFileNames && ssFileName.BeginsWith(SL(LONG_FILENAME_PREFIX_W))) + { + ssPathName.Clear(); + if (pdwFilePartIdx != NULL) + *pdwFilePartIdx = 0; + ThrowHR(E_UNEXPECTED); + } + } +} // namespace Win32 + +} // namespace Util +} // namespace Clr diff --git a/src/utilcode/util_nodependencies.cpp b/src/utilcode/util_nodependencies.cpp new file mode 100644 index 0000000000..f2bb90918c --- /dev/null +++ b/src/utilcode/util_nodependencies.cpp @@ -0,0 +1,1119 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// Util_NoDependencies.cpp +// + +// +// This contains a bunch of C++ utility classes needed also for UtilCode without dependencies +// (standalone version without CLR/clr.dll/mscoree.dll dependencies). +// +//***************************************************************************** + +#include "stdafx.h" +#include "utilcode.h" +#include "ex.h" + +#if !defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(_DEBUG) + +extern RunningOnStatusEnum gRunningOnStatus = RUNNING_ON_STATUS_UNINITED; +extern BOOL gExInfoAvailable = FALSE; +extern BOOL gExInfoIsServer = TRUE; + +#define NON_SUPPORTED_PLATFORM_MSGBOX_TITLE W("Platform not supported") +#define NON_SUPPORTED_PLATFORM_MSGBOX_TEXT W("The minimum supported platform is Windows 2000") +#define NON_SUPPORTED_PLATFORM_TERMINATE_ERROR_CODE 0xBAD1BAD1 + +//***************************************************************************** +// One time initialization of the OS version +//***************************************************************************** +void InitRunningOnVersionStatus () +{ +#ifndef FEATURE_PAL + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_CANNOT_TAKE_LOCK; + STATIC_CONTRACT_SO_TOLERANT; + + BOOL fSupportedPlatform = FALSE; + OSVERSIONINFOEX sVer; + DWORDLONG dwlConditionMask; + + ZeroMemory(&sVer, sizeof(OSVERSIONINFOEX)); + sVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + sVer.dwMajorVersion = 6; + sVer.dwMinorVersion = 2; + sVer.dwPlatformId = VER_PLATFORM_WIN32_NT; + + + dwlConditionMask = 0; + dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, CLR_VER_PLATFORMID, VER_EQUAL); + dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, CLR_VER_MAJORVERSION, VER_GREATER_EQUAL); + dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, CLR_VER_MINORVERSION, VER_GREATER_EQUAL); + + if(VerifyVersionInfo(&sVer, CLR_VER_MAJORVERSION | CLR_VER_PLATFORMID | CLR_VER_MINORVERSION, dwlConditionMask)) + { + gRunningOnStatus = RUNNING_ON_WIN8; + fSupportedPlatform = TRUE; + goto CHECK_SUPPORTED; + } + + + ZeroMemory(&sVer, sizeof(OSVERSIONINFOEX)); + sVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + sVer.dwMajorVersion = 6; + sVer.dwMinorVersion = 1; + sVer.dwPlatformId = VER_PLATFORM_WIN32_NT; + + + dwlConditionMask = 0; + dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, CLR_VER_PLATFORMID, VER_EQUAL); + dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, CLR_VER_MAJORVERSION, VER_GREATER_EQUAL); + dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, CLR_VER_MINORVERSION, VER_GREATER_EQUAL); + + if(VerifyVersionInfo(&sVer, CLR_VER_MAJORVERSION | CLR_VER_PLATFORMID | CLR_VER_MINORVERSION, dwlConditionMask)) + { + gRunningOnStatus = RUNNING_ON_WIN7; + fSupportedPlatform = TRUE; + goto CHECK_SUPPORTED; + } + + + ZeroMemory(&sVer, sizeof(OSVERSIONINFOEX)); + sVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + sVer.dwMajorVersion = 6; + sVer.dwPlatformId = VER_PLATFORM_WIN32_NT; + + + dwlConditionMask = 0; + VER_SET_CONDITION(dwlConditionMask, CLR_VER_PLATFORMID, VER_EQUAL); + VER_SET_CONDITION(dwlConditionMask, CLR_VER_MAJORVERSION, VER_GREATER_EQUAL); + + if(VerifyVersionInfo(&sVer, CLR_VER_MAJORVERSION | CLR_VER_PLATFORMID, dwlConditionMask)) + { + gRunningOnStatus = RUNNING_ON_VISTA; + fSupportedPlatform = TRUE; + goto CHECK_SUPPORTED; + } + + + ZeroMemory(&sVer, sizeof(OSVERSIONINFOEX)); + sVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + sVer.dwMajorVersion = 5; + sVer.dwMinorVersion = 2; + sVer.dwPlatformId = VER_PLATFORM_WIN32_NT; + + + dwlConditionMask = 0; + VER_SET_CONDITION(dwlConditionMask, CLR_VER_PLATFORMID, VER_EQUAL); + VER_SET_CONDITION(dwlConditionMask, CLR_VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(dwlConditionMask, CLR_VER_MINORVERSION, VER_GREATER_EQUAL); + + if(VerifyVersionInfo(&sVer, CLR_VER_MAJORVERSION | CLR_VER_PLATFORMID | CLR_VER_MINORVERSION, dwlConditionMask)) + { + gRunningOnStatus = RUNNING_ON_WIN2003; + fSupportedPlatform = TRUE; + goto CHECK_SUPPORTED; + } + + sVer.dwMajorVersion = 5; + sVer.dwMinorVersion = 1; + sVer.dwPlatformId = VER_PLATFORM_WIN32_NT; + + dwlConditionMask = 0; + VER_SET_CONDITION(dwlConditionMask, CLR_VER_PLATFORMID, VER_EQUAL); + VER_SET_CONDITION(dwlConditionMask, CLR_VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(dwlConditionMask, CLR_VER_MINORVERSION, VER_GREATER_EQUAL); + + if(VerifyVersionInfo(&sVer, CLR_VER_MAJORVERSION | CLR_VER_PLATFORMID | CLR_VER_MINORVERSION, dwlConditionMask)) + { + gRunningOnStatus = RUNNING_ON_WINXP; + fSupportedPlatform = TRUE; + goto CHECK_SUPPORTED; + } + + + + ZeroMemory(&sVer, sizeof(OSVERSIONINFOEX)); + sVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + sVer.dwMajorVersion = 5; + sVer.dwMinorVersion = 0; + sVer.dwPlatformId = VER_PLATFORM_WIN32_NT; + + + dwlConditionMask = 0; + VER_SET_CONDITION(dwlConditionMask, CLR_VER_PLATFORMID, VER_EQUAL); + VER_SET_CONDITION(dwlConditionMask, CLR_VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(dwlConditionMask, CLR_VER_MINORVERSION, VER_GREATER_EQUAL); + + if(VerifyVersionInfo(&sVer, CLR_VER_MAJORVERSION | CLR_VER_PLATFORMID | CLR_VER_MINORVERSION, dwlConditionMask)) + { + gRunningOnStatus = RUNNING_ON_WINNT5; + fSupportedPlatform = TRUE; + goto CHECK_SUPPORTED; + } + +CHECK_SUPPORTED: + + if (!fSupportedPlatform) + { + // The current platform isn't supported. Display a message box to this effect and exit. + // Note that this should never happen since the .NET Fx setup should not install on + // non supported platforms (which is why the message box text isn't localized). + UtilMessageBoxCatastrophicNonLocalized(NON_SUPPORTED_PLATFORM_MSGBOX_TITLE, NON_SUPPORTED_PLATFORM_MSGBOX_TEXT, MB_OK | MB_ICONERROR, TRUE); + TerminateProcess(GetCurrentProcess(), NON_SUPPORTED_PLATFORM_TERMINATE_ERROR_CODE); + } + + gExInfoAvailable = 0; + gExInfoIsServer = 0; + + OSVERSIONINFOEX sVerX; + ZeroMemory(&sVerX, sizeof(OSVERSIONINFOEX)); + sVerX.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + sVerX.wProductType = VER_NT_DOMAIN_CONTROLLER; + + dwlConditionMask = 0; + VER_SET_CONDITION(dwlConditionMask, CLR_VER_PRODUCT_TYPE, VER_EQUAL); + + if(VerifyVersionInfo(&sVerX, CLR_VER_PRODUCT_TYPE, dwlConditionMask)) + { + gExInfoIsServer = 1; + } + + + ZeroMemory(&sVerX, sizeof(OSVERSIONINFOEX)); + sVerX.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + sVerX.wProductType = VER_NT_SERVER; + + dwlConditionMask = 0; + VER_SET_CONDITION(dwlConditionMask, CLR_VER_PRODUCT_TYPE, VER_EQUAL); + + if(VerifyVersionInfo(&sVerX, CLR_VER_PRODUCT_TYPE, dwlConditionMask)) + { + gExInfoIsServer = 1; + } + + gExInfoAvailable = 1; +#else // FEATURE_PAL + // UNIXTODO: Do we need version checks for Linux? +#endif // FEATURE_PAL +} // InitRunningOnVersionStatus + +#ifndef _WIN64 +//------------------------------------------------------------------------------ +// Returns TRUE if we are running on a 64-bit OS in WoW, FALSE otherwise. +BOOL RunningInWow64() +{ + static int s_Wow64Process; + + if (s_Wow64Process == 0) + { + BOOL fWow64Process = FALSE; + + if (!IsWow64Process(GetCurrentProcess(), &fWow64Process)) + fWow64Process = FALSE; + + s_Wow64Process = fWow64Process ? 1 : -1; + } + + return (s_Wow64Process == 1) ? TRUE : FALSE; +} +#endif + +#ifndef FEATURE_PAL +//------------------------------------------------------------------------------ +// +// GetRegistryLongValue - Reads a configuration LONG value from the registry. +// +// Parameters +// hKeyParent -- Parent key +// szKey -- key to open +// szName -- name of the value +// pValue -- put value here, if found +// fReadNonVirtualizedKey -- whether to read 64-bit hive on WOW64 +// +// Returns +// TRUE -- If the value was found and read +// FALSE -- The value was not found, could not be read, or was not DWORD +// +// Exceptions +// None +//------------------------------------------------------------------------------ +BOOL GetRegistryLongValue(HKEY hKeyParent, + LPCWSTR szKey, + LPCWSTR szName, + long *pValue, + BOOL fReadNonVirtualizedKey) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + DWORD ret; // Return value from registry operation. + HKEYHolder hkey; // Registry key. + long iValue; // The value to read. + DWORD iType; // Type of value to get. + DWORD iSize; // Size of buffer. + REGSAM samDesired = KEY_READ; // Desired access rights to the key + + if (fReadNonVirtualizedKey) + { + if (RunningInWow64()) + { + samDesired |= KEY_WOW64_64KEY; + } + } + + ret = WszRegOpenKeyEx(hKeyParent, szKey, 0, samDesired, &hkey); + + // If we opened the key, see if there is a value. + if (ret == ERROR_SUCCESS) + { + iType = REG_DWORD; + iSize = sizeof(long); + ret = WszRegQueryValueEx(hkey, szName, NULL, &iType, reinterpret_cast<BYTE*>(&iValue), &iSize); + + if (ret == ERROR_SUCCESS && iType == REG_DWORD && iSize == sizeof(long)) + { // We successfully read a DWORD value. + *pValue = iValue; + return TRUE; + } + } + + return FALSE; +} // GetRegistryLongValue + +//---------------------------------------------------------------------------- +// +// GetCurrentModuleFileName - Retrieve the current module's filename +// +// Arguments: +// pBuffer - output string buffer +// pcchBuffer - the number of characters of the string buffer +// +// Return Value: +// S_OK on success, else detailed error code. +// +// Note: +// +//---------------------------------------------------------------------------- +HRESULT GetCurrentModuleFileName(__out_ecount(*pcchBuffer) LPWSTR pBuffer, __inout DWORD *pcchBuffer) +{ + LIMITED_METHOD_CONTRACT; + + if ((pBuffer == NULL) || (pcchBuffer == NULL)) + { + return E_INVALIDARG; + } + + // Get the appname to look up in the exclusion or inclusion list. + WCHAR appPath[MAX_PATH + 2]; + + DWORD ret = WszGetModuleFileName(NULL, appPath, NumItems(appPath)); + + if ((ret == NumItems(appPath)) || (ret == 0)) + { + // The module file name exceeded maxpath, or GetModuleFileName failed. + return E_UNEXPECTED; + } + + // Pick off the part after the path. + WCHAR* appName = wcsrchr(appPath, W('\\')); + + // If no backslash, use the whole name; if there is a backslash, skip it. + appName = appName ? appName+1 : appPath; + + if (*pcchBuffer < wcslen(appName)) + { + *pcchBuffer = static_cast<DWORD>(wcslen(appName)) + 1; + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + wcscpy_s(pBuffer, *pcchBuffer, appName); + return S_OK; +} + +//---------------------------------------------------------------------------- +// +// IsCurrentModuleFileNameInAutoExclusionList - decide if the current module's filename +// is in the AutoExclusionList list +// +// Arguments: +// None +// +// Return Value: +// TRUE or FALSE +// +// Note: +// This function cannot be used in out of process scenarios like DAC because it +// looks at current module's filename. In OOP we want to use target process's +// module's filename. +// +//---------------------------------------------------------------------------- +BOOL IsCurrentModuleFileNameInAutoExclusionList() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + HKEYHolder hKeyHolder; + + // Look for "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug\\AutoExclusionList" + DWORD ret = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, kUnmanagedDebuggerAutoExclusionListKey, 0, KEY_READ, &hKeyHolder); + + if (ret != ERROR_SUCCESS) + { + // there's not even an AutoExclusionList hive + return FALSE; + } + + WCHAR wszAppName[MAX_PATH]; + DWORD cchAppName = NumItems(wszAppName); + + // Get the appname to look up in the exclusion or inclusion list. + if (GetCurrentModuleFileName(wszAppName, &cchAppName) != S_OK) + { + // Assume it is not on the exclusion list if we cannot find the module's filename. + return FALSE; + } + + // Look in AutoExclusionList key for appName get the size of any value stored there. + DWORD value, valueType, valueSize = sizeof(value); + ret = WszRegQueryValueEx(hKeyHolder, wszAppName, 0, &valueType, reinterpret_cast<BYTE*>(&value), &valueSize); + if ((ret == ERROR_SUCCESS) && (valueType == REG_DWORD) && (value == 1)) + { + return TRUE; + } + + return FALSE; +} // IsCurrentModuleFileNameInAutoExclusionList + +//***************************************************************************** +// Retrieve information regarding what registered default debugger +//***************************************************************************** +void GetDebuggerSettingInfo(SString &ssDebuggerString, BOOL *pfAuto) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + EX_TRY + { + DWORD cchDebuggerString = MAX_PATH; + INDEBUG(DWORD cchOldDebuggerString = cchDebuggerString); + + WCHAR * buf = ssDebuggerString.OpenUnicodeBuffer(cchDebuggerString); + HRESULT hr = GetDebuggerSettingInfoWorker(buf, &cchDebuggerString, pfAuto); + ssDebuggerString.CloseBuffer(cchDebuggerString); + + while (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) + { + _ASSERTE(cchDebuggerString > cchOldDebuggerString); + INDEBUG(cchOldDebuggerString = cchDebuggerString); + + buf = ssDebuggerString.OpenUnicodeBuffer(cchDebuggerString); + hr = GetDebuggerSettingInfoWorker(buf, &cchDebuggerString, pfAuto); + ssDebuggerString.CloseBuffer(cchDebuggerString); + } + + if (*ssDebuggerString.GetUnicode() == W('\0')) + { + ssDebuggerString.Clear(); + } + + if (FAILED(hr)) + { + ssDebuggerString.Clear(); + if (pfAuto) + { + *pfAuto = FALSE; + } + } + } + EX_CATCH + { + ssDebuggerString.Clear(); + if (pfAuto) + { + *pfAuto = FALSE; + } + } + EX_END_CATCH(SwallowAllExceptions); +} // GetDebuggerSettingInfo + +//--------------------------------------------------------------------------------------- +// +// GetDebuggerSettingInfoWorker - retrieve information regarding what registered default debugger +// +// Arguments: +// * wszDebuggerString - [out] the string buffer to store the registered debugger launch +// string +// * pcchDebuggerString - [in, out] the size of string buffer in characters +// * pfAuto - [in] the flag to indicate whether the debugger neeeds to be launched +// automatically +// +// Return Value: +// HRESULT indicating success or failure. +// +// Notes: +// * wszDebuggerString can be NULL. When wszDebuggerString is NULL, pcchDebuggerString should +// * point to a DWORD of zero. pcchDebuggerString cannot be NULL, and the DWORD pointed by +// * pcchDebuggerString will store the used or required string buffer size in characters. +HRESULT GetDebuggerSettingInfoWorker(__out_ecount_part_opt(*pcchDebuggerString, *pcchDebuggerString) LPWSTR wszDebuggerString, DWORD * pcchDebuggerString, BOOL * pfAuto) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + PRECONDITION(pcchDebuggerString != NULL); + } + CONTRACTL_END; + + if ((pcchDebuggerString == NULL) || ((wszDebuggerString == NULL) && (*pcchDebuggerString != 0))) + { + return E_INVALIDARG; + } + + // Initialize the output values before we start. + if ((wszDebuggerString != NULL) && (*pcchDebuggerString > 0)) + { + *wszDebuggerString = W('\0'); + } + + if (pfAuto != NULL) + { + *pfAuto = FALSE; + } + + HKEYHolder hKeyHolder; + + // Look for "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug" + DWORD ret = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, kUnmanagedDebuggerKey, 0, KEY_READ, &hKeyHolder); + + if (ret != ERROR_SUCCESS) + { // Wow, there's not even an AeDebug hive, so no native debugger, no auto. + return S_OK; + } + + // Look in AeDebug key for "Debugger"; get the size of any value stored there. + DWORD valueType, valueSize; + ret = WszRegQueryValueEx(hKeyHolder, kUnmanagedDebuggerValue, 0, &valueType, 0, &valueSize); + + _ASSERTE(pcchDebuggerString != NULL); + if ((wszDebuggerString == NULL) || (*pcchDebuggerString < valueSize / sizeof(WCHAR))) + { + *pcchDebuggerString = valueSize / sizeof(WCHAR) + 1; + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + *pcchDebuggerString = valueSize / sizeof(WCHAR); + + // The size of an empty string with the null terminator is 2. + BOOL fIsDebuggerStringEmptry = valueSize <= 2 ? TRUE : FALSE; + + if ((ret != ERROR_SUCCESS) || (valueType != REG_SZ) || fIsDebuggerStringEmptry) + { + return S_OK; + } + + _ASSERTE(wszDebuggerString != NULL); + ret = WszRegQueryValueEx(hKeyHolder, kUnmanagedDebuggerValue, NULL, NULL, reinterpret_cast< LPBYTE >(wszDebuggerString), &valueSize); + if (ret != ERROR_SUCCESS) + { + *wszDebuggerString = W('\0'); + return S_OK; + } + + // The callers are in nothrow scope, so we must swallow exceptions and reset the output parameters to the + // default values if exceptions like OOM ever happen. + EX_TRY + { + if (pfAuto != NULL) + { + BOOL fAuto = FALSE; + + // Get the appname to look up in DebugApplications key. + WCHAR wzAppName[MAX_PATH]; + DWORD cchAppName = NumItems(wzAppName); + long iValue; + + // Check DebugApplications setting + if ((SUCCEEDED(GetCurrentModuleFileName(wzAppName, &cchAppName))) && + ( + GetRegistryLongValue(HKEY_LOCAL_MACHINE, kDebugApplicationsPoliciesKey, wzAppName, &iValue, TRUE) || + GetRegistryLongValue(HKEY_LOCAL_MACHINE, kDebugApplicationsKey, wzAppName, &iValue, TRUE) || + GetRegistryLongValue(HKEY_CURRENT_USER, kDebugApplicationsPoliciesKey, wzAppName, &iValue, TRUE) || + GetRegistryLongValue(HKEY_CURRENT_USER, kDebugApplicationsKey, wzAppName, &iValue, TRUE) + ) && + (iValue == 1)) + { + fAuto = TRUE; + } + else + { + // Look in AeDebug key for "Auto"; get the size of any value stored there. + ret = WszRegQueryValueEx(hKeyHolder, kUnmanagedDebuggerAutoValue, 0, &valueType, 0, &valueSize); + if ((ret == ERROR_SUCCESS) && (valueType == REG_SZ) && (valueSize / sizeof(WCHAR) < MAX_PATH)) + { + WCHAR wzAutoKey[MAX_PATH]; + valueSize = NumItems(wzAutoKey) * sizeof(WCHAR); + WszRegQueryValueEx(hKeyHolder, kUnmanagedDebuggerAutoValue, NULL, NULL, reinterpret_cast< LPBYTE >(wzAutoKey), &valueSize); + + // The OS's behavior is to consider Auto to be FALSE unless the first character is set + // to 1. They don't take into consideration the following characters. Also if the value + // isn't present they assume an Auto value of FALSE. + if ((wzAutoKey[0] == W('1')) && !IsCurrentModuleFileNameInAutoExclusionList()) + { + fAuto = TRUE; + } + } + } + + *pfAuto = fAuto; + } + } + EX_CATCH + { + if ((wszDebuggerString != NULL) && (*pcchDebuggerString > 0)) + { + *wszDebuggerString = W('\0'); + } + + if (pfAuto != NULL) + { + *pfAuto = FALSE; + } + } + EX_END_CATCH(SwallowAllExceptions); + + return S_OK; +} // GetDebuggerSettingInfoWorker +#endif // FEATURE_PAL + +#endif //!defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(_DEBUG) + +//***************************************************************************** +// Convert hex value into a wide string of hex digits +//***************************************************************************** +HRESULT GetStr( + DWORD hHexNum, + __out_ecount((cbHexNum * 2)) LPWSTR szHexNum, + DWORD cbHexNum) +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + _ASSERTE (szHexNum); + cbHexNum *= 2; // each nibble is a char + while (cbHexNum != 0) + { + DWORD thisHexDigit = hHexNum % 16; + hHexNum /= 16; + cbHexNum--; + if (thisHexDigit < 10) + { + *(szHexNum+cbHexNum) = (BYTE)(thisHexDigit + W('0')); + } + else + { + *(szHexNum+cbHexNum) = (BYTE)(thisHexDigit - 10 + W('A')); + } + } + return S_OK; +} + +//***************************************************************************** +// Convert a GUID into a pointer to a Wide char string +//***************************************************************************** +int +GuidToLPWSTR( + GUID Guid, // The GUID to convert. + __out_ecount(cchGuid) LPWSTR szGuid, // String into which the GUID is stored + DWORD cchGuid) // Count in wchars +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + int i; + + // successive fields break the GUID into the form DWORD-WORD-WORD-WORD-WORD.DWORD + // covering the 128-bit GUID. The string includes enclosing braces, which are an OLE convention. + + if (cchGuid < 39) // 38 chars + 1 null terminating. + return 0; + + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + // ^ + szGuid[0] = W('{'); + + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + // ^^^^^^^^ + if (FAILED (GetStr(Guid.Data1, szGuid+1 , 4))) return 0; + + szGuid[9] = W('-'); + + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + // ^^^^ + if (FAILED (GetStr(Guid.Data2, szGuid+10, 2))) return 0; + + szGuid[14] = W('-'); + + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + // ^^^^ + if (FAILED (GetStr(Guid.Data3, szGuid+15, 2))) return 0; + + szGuid[19] = W('-'); + + // Get the last two fields (which are byte arrays). + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + // ^^^^ + for (i=0; i < 2; ++i) + if (FAILED(GetStr(Guid.Data4[i], szGuid + 20 + (i * 2), 1))) + return (0); + + szGuid[24] = W('-'); + + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + // ^^^^^^^^^^^^ + for (i=0; i < 6; ++i) + if (FAILED(GetStr(Guid.Data4[i+2], szGuid + 25 + (i * 2), 1))) + return (0); + + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + // ^ + szGuid[37] = W('}'); + szGuid[38] = W('\0'); + + return 39; +} // GuidToLPWSTR + +//***************************************************************************** +// Convert wide string of (at most eight) hex digits into a hex value +//***************************************************************************** +HRESULT GetHex( + DWORD * phHexNum, + __in_ecount((cbHexNum * 2)) LPCWSTR szHexNum, + DWORD cbHexNum) +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + _ASSERTE (szHexNum && phHexNum); + _ASSERTE(cbHexNum == 1 || cbHexNum == 2 || cbHexNum == 4); + + cbHexNum *= 2; // each nibble is a char + DWORD val = 0; + for (DWORD i = 0; i < cbHexNum; ++i) + { + DWORD nibble = 0; + if (szHexNum[i] >= W('0') && szHexNum[i] <= W('9')) + { + nibble = szHexNum[i] - '0'; + } + else if (szHexNum[i] >= W('A') && szHexNum[i] <= W('F')) + { + nibble = 10 + szHexNum[i] - 'A'; + } + else if (szHexNum[i] >= W('a') && szHexNum[i] <= W('f')) + { + nibble = 10 + szHexNum[i] - 'a'; + } + else + { + return E_FAIL; + } + val = (val << 4) + nibble; + } + *phHexNum = val; + return S_OK; +} + +//***************************************************************************** +// Parse a Wide char string into a GUID +//***************************************************************************** +BOOL +LPWSTRToGuid( + GUID * Guid, // [OUT] The GUID to fill in + __in_ecount(cchGuid) LPCWSTR szGuid, // [IN] String to parse + DWORD cchGuid) // [IN] Count in wchars in string +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + int i; + DWORD dw; + + // successive fields break the GUID into the form DWORD-WORD-WORD-WORD-WORD.DWORD + // covering the 128-bit GUID. The string includes enclosing braces, which are an OLE convention. + + if (cchGuid < 38) // 38 chars + 1 null terminating. + return FALSE; + + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + // ^ + if (szGuid[0] != W('{')) return FALSE; + + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + // ^^^^^^^^ + if (FAILED (GetHex(&dw, szGuid+1 , 4))) return FALSE; + Guid->Data1 = dw; + + if (szGuid[9] != W('-')) return FALSE; + + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + // ^^^^ + if (FAILED (GetHex(&dw, szGuid+10, 2))) return FALSE; + Guid->Data2 = (WORD)dw; + + if (szGuid[14] != W('-')) return FALSE; + + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + // ^^^^ + if (FAILED (GetHex(&dw, szGuid+15, 2))) return FALSE; + Guid->Data3 = (WORD)dw; + + if (szGuid[19] != W('-')) return FALSE; + + // Get the last two fields (which are byte arrays). + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + // ^^^^ + for (i=0; i < 2; ++i) + { + if (FAILED(GetHex(&dw, szGuid + 20 + (i * 2), 1))) return FALSE; + Guid->Data4[i] = (BYTE)dw; + } + + if (szGuid[24] != W('-')) return FALSE; + + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + // ^^^^^^^^^^^^ + for (i=0; i < 6; ++i) + { + if (FAILED(GetHex(&dw, szGuid + 25 + (i * 2), 1))) return FALSE; + Guid->Data4[i+2] = (BYTE)dw; + } + + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + // ^ + if (szGuid[37] != W('}')) return FALSE; + + return TRUE; +} // GuidToLPWSTR + + +#ifdef _DEBUG +// Always write regardless of registry. +void _cdecl DbgWriteEx(LPCTSTR szFmt, ...) +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + WCHAR rcBuff[1024]; + va_list marker; + + va_start(marker, szFmt); + _vsnwprintf_s(rcBuff, _countof(rcBuff), _TRUNCATE, szFmt, marker); + va_end(marker); + WszOutputDebugString(rcBuff); +} +#endif //_DEBUG + +/**************************************************************************/ +void ConfigDWORD::init(const CLRConfig::ConfigDWORDInfo & info) +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + // make sure that the memory was zero initialized + _ASSERTE(m_inited == 0 || m_inited == 1); + + m_value = CLRConfig::GetConfigValue(info); + m_inited = 1; +} + +//--------------------------------------------------------------------------------------- +// +// Takes a const input string, and returns the start & size of the substring that has all +// leading and trailing whitespace removed. The original string is not modified. +// +// Arguments: +// * pwsz - [in] points to const string we want to trim; [out] points to beginning +// of trimmed substring of input string +// * pcch - [in] Points to length in chars of input string (not counting null +// terminator); [out] Points to length in chars of trimmed substring (not +// counting null terminator) +// +void TrimWhiteSpace(__deref_inout_ecount(*pcch) LPCWSTR *pwsz, __inout LPDWORD pcch) +{ + LIMITED_METHOD_DAC_CONTRACT; + + _ASSERTE (pwsz != NULL); + _ASSERTE (*pwsz != NULL); + _ASSERTE (pcch != NULL); + + DWORD cch = *pcch; + LPCWSTR wszBeginning = *pwsz; + LPCWSTR wszEnd = wszBeginning + (cch - 1); + + while ((cch != 0) && iswspace(*wszBeginning)) + { + wszBeginning++; + cch--; + } + + while ((cch != 0) && iswspace(*wszEnd)) + { + wszEnd--; + cch--; + } + + *pwsz = wszBeginning; + *pcch = cch; +} // TrimWhiteSpace + +BOOL ThreadWillCreateGuardPage(SIZE_T sizeReservedStack, SIZE_T sizeCommitedStack) +{ + // We need to make sure there will be a reserved but never committed page at the end + // of the stack. We do here the check NT does when it creates the user stack to decide + // if there is going to be a guard page. However, that is not enough, as if we only + // have a guard page, we have nothing to protect us from going pass it. Well, in + // fact, there is something that we will protect you, there are certain places + // (RTLUnwind) in NT that will check that the current frame is within stack limits. + // If we are not it will bomb out. We will also bomb out if we touch the hard guard + // page. + // + // For situation B, teb->StackLimit is at the beggining of the user stack (ie + // before updating StackLimit it checks if it was able to create a new guard page, + // in this case, it can't), which makes the check fail in RtlUnwind. + // + // Situation A [ Hard guard page | Guard page | user stack] + // + // Situation B [ Guard page | User stack ] + // + // Situation C [ User stack ( no room for guard page) ] + // + // Situation D (W9x) : Guard page or not, w9x has a 64k reserved region below + // the stack, we don't need any checks at all + // + // We really want to be in situation A all the time, so we add one more page + // to our requirements (we require guard page + hard guard) + + SYSTEM_INFO sysInfo; + ::GetSystemInfo(&sysInfo); + + // OS rounds up sizes the following way to decide if it marks a guard page + sizeReservedStack = ALIGN(sizeReservedStack, ((size_t)sysInfo.dwAllocationGranularity)); // Allocation granularity + sizeCommitedStack = ALIGN(sizeCommitedStack, ((size_t)sysInfo.dwPageSize)); // Page Size + + // OS wont create guard page, we can't execute managed code safely. + // We also have to make sure we have a 'hard' guard, thus we add another + // page to the memory we would need comitted. + // That is, the following code will check if sizeReservedStack is at least 2 pages + // more than sizeCommitedStack. + return (sizeReservedStack > sizeCommitedStack + ((size_t)sysInfo.dwPageSize)); +} // ThreadWillCreateGuardPage + +//The following characters have special sorting weights when combined with other +//characters, which means we can't use our fast sorting algorithm on them. +//Most of these are pretty rare control characters, but apostrophe and hyphen +//are fairly common and force us down the slower path. This is because we want +//"word sorting", which means that "coop" and "co-op" sort together, instead of +//separately as they would if we were doing a string sort. +// 0x0001 6 3 2 2 0 ;Start Of Heading +// 0x0002 6 4 2 2 0 ;Start Of Text +// 0x0003 6 5 2 2 0 ;End Of Text +// 0x0004 6 6 2 2 0 ;End Of Transmission +// 0x0005 6 7 2 2 0 ;Enquiry +// 0x0006 6 8 2 2 0 ;Acknowledge +// 0x0007 6 9 2 2 0 ;Bell +// 0x0008 6 10 2 2 0 ;Backspace + +// 0x000e 6 11 2 2 0 ;Shift Out +// 0x000f 6 12 2 2 0 ;Shift In +// 0x0010 6 13 2 2 0 ;Data Link Escape +// 0x0011 6 14 2 2 0 ;Device Control One +// 0x0012 6 15 2 2 0 ;Device Control Two +// 0x0013 6 16 2 2 0 ;Device Control Three +// 0x0014 6 17 2 2 0 ;Device Control Four +// 0x0015 6 18 2 2 0 ;Negative Acknowledge +// 0x0016 6 19 2 2 0 ;Synchronous Idle +// 0x0017 6 20 2 2 0 ;End Of Transmission Block +// 0x0018 6 21 2 2 0 ;Cancel +// 0x0019 6 22 2 2 0 ;End Of Medium +// 0x001a 6 23 2 2 0 ;Substitute +// 0x001b 6 24 2 2 0 ;Escape +// 0x001c 6 25 2 2 0 ;File Separator +// 0x001d 6 26 2 2 0 ;Group Separator +// 0x001e 6 27 2 2 0 ;Record Separator +// 0x001f 6 28 2 2 0 ;Unit Separator + +// 0x0027 6 128 2 2 0 ;Apostrophe-Quote +// 0x002d 6 130 2 2 0 ;Hyphen-Minus + +// 0x007f 6 29 2 2 0 ;Delete + +const BYTE +HighCharHelper::HighCharTable[]= { + TRUE, /* 0x0, 0x0 */ + TRUE, /* 0x1, .*/ + TRUE, /* 0x2, .*/ + TRUE, /* 0x3, .*/ + TRUE, /* 0x4, .*/ + TRUE, /* 0x5, .*/ + TRUE, /* 0x6, .*/ + TRUE, /* 0x7, .*/ + TRUE, /* 0x8, .*/ + FALSE, /* 0x9, */ + FALSE, /* 0xA, */ + FALSE, /* 0xB, .*/ + FALSE, /* 0xC, .*/ + FALSE, /* 0xD, */ + TRUE, /* 0xE, .*/ + TRUE, /* 0xF, .*/ + TRUE, /* 0x10, .*/ + TRUE, /* 0x11, .*/ + TRUE, /* 0x12, .*/ + TRUE, /* 0x13, .*/ + TRUE, /* 0x14, .*/ + TRUE, /* 0x15, .*/ + TRUE, /* 0x16, .*/ + TRUE, /* 0x17, .*/ + TRUE, /* 0x18, .*/ + TRUE, /* 0x19, .*/ + TRUE, /* 0x1A, */ + TRUE, /* 0x1B, .*/ + TRUE, /* 0x1C, .*/ + TRUE, /* 0x1D, .*/ + TRUE, /* 0x1E, .*/ + TRUE, /* 0x1F, .*/ + FALSE, /*0x20, */ + FALSE, /*0x21, !*/ + FALSE, /*0x22, "*/ + FALSE, /*0x23, #*/ + FALSE, /*0x24, $*/ + FALSE, /*0x25, %*/ + FALSE, /*0x26, &*/ + TRUE, /*0x27, '*/ + FALSE, /*0x28, (*/ + FALSE, /*0x29, )*/ + FALSE, /*0x2A **/ + FALSE, /*0x2B, +*/ + FALSE, /*0x2C, ,*/ + TRUE, /*0x2D, -*/ + FALSE, /*0x2E, .*/ + FALSE, /*0x2F, /*/ + FALSE, /*0x30, 0*/ + FALSE, /*0x31, 1*/ + FALSE, /*0x32, 2*/ + FALSE, /*0x33, 3*/ + FALSE, /*0x34, 4*/ + FALSE, /*0x35, 5*/ + FALSE, /*0x36, 6*/ + FALSE, /*0x37, 7*/ + FALSE, /*0x38, 8*/ + FALSE, /*0x39, 9*/ + FALSE, /*0x3A, :*/ + FALSE, /*0x3B, ;*/ + FALSE, /*0x3C, <*/ + FALSE, /*0x3D, =*/ + FALSE, /*0x3E, >*/ + FALSE, /*0x3F, ?*/ + FALSE, /*0x40, @*/ + FALSE, /*0x41, A*/ + FALSE, /*0x42, B*/ + FALSE, /*0x43, C*/ + FALSE, /*0x44, D*/ + FALSE, /*0x45, E*/ + FALSE, /*0x46, F*/ + FALSE, /*0x47, G*/ + FALSE, /*0x48, H*/ + FALSE, /*0x49, I*/ + FALSE, /*0x4A, J*/ + FALSE, /*0x4B, K*/ + FALSE, /*0x4C, L*/ + FALSE, /*0x4D, M*/ + FALSE, /*0x4E, N*/ + FALSE, /*0x4F, O*/ + FALSE, /*0x50, P*/ + FALSE, /*0x51, Q*/ + FALSE, /*0x52, R*/ + FALSE, /*0x53, S*/ + FALSE, /*0x54, T*/ + FALSE, /*0x55, U*/ + FALSE, /*0x56, V*/ + FALSE, /*0x57, W*/ + FALSE, /*0x58, X*/ + FALSE, /*0x59, Y*/ + FALSE, /*0x5A, Z*/ + FALSE, /*0x5B, [*/ + FALSE, /*0x5C, \*/ + FALSE, /*0x5D, ]*/ + FALSE, /*0x5E, ^*/ + FALSE, /*0x5F, _*/ + FALSE, /*0x60, `*/ + FALSE, /*0x61, a*/ + FALSE, /*0x62, b*/ + FALSE, /*0x63, c*/ + FALSE, /*0x64, d*/ + FALSE, /*0x65, e*/ + FALSE, /*0x66, f*/ + FALSE, /*0x67, g*/ + FALSE, /*0x68, h*/ + FALSE, /*0x69, i*/ + FALSE, /*0x6A, j*/ + FALSE, /*0x6B, k*/ + FALSE, /*0x6C, l*/ + FALSE, /*0x6D, m*/ + FALSE, /*0x6E, n*/ + FALSE, /*0x6F, o*/ + FALSE, /*0x70, p*/ + FALSE, /*0x71, q*/ + FALSE, /*0x72, r*/ + FALSE, /*0x73, s*/ + FALSE, /*0x74, t*/ + FALSE, /*0x75, u*/ + FALSE, /*0x76, v*/ + FALSE, /*0x77, w*/ + FALSE, /*0x78, x*/ + FALSE, /*0x79, y*/ + FALSE, /*0x7A, z*/ + FALSE, /*0x7B, {*/ + FALSE, /*0x7C, |*/ + FALSE, /*0x7D, }*/ + FALSE, /*0x7E, ~*/ + TRUE, /*0x7F, */ +}; // HighCharHelper::HighCharTable diff --git a/src/utilcode/utilcode.settings.targets b/src/utilcode/utilcode.settings.targets new file mode 100644 index 0000000000..03db64c642 --- /dev/null +++ b/src/utilcode/utilcode.settings.targets @@ -0,0 +1,128 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + + <!--Leaf project Properties--> + <PropertyGroup> + <!--OK to delete NO_NTDLL for devdiv builds.--> + <OutputPath>$(ClrLibDest)</OutputPath> + <TargetType>LIBRARY</TargetType> + <!--PCHHeader>stdafx.h</PCHHeader> + <EnableCxxPCHHeaders>true</EnableCxxPCHHeaders--> + <!--PCH: Both precompiled header and cpp are on the same ..\ path this is likely to be wrong.--> + <UtilCodeSrcDir>$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\Utilcode</UtilCodeSrcDir> + <!--PCHCompile>$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\Utilcode\stdafx.cpp</PCHCompile> + <PCHObject>stdafx_utilcode.obj</PCHObject--> + <ClAdditionalOptions>$(ClAdditionalOptions) -DUNICODE -D_UNICODE $(_CRTIMPFLAGS) -DNO_CRT</ClAdditionalOptions> + <ClAdditionalOptions Condition="'FeatureUtilcodeNoDependencies' != 'true'">$(ClAdditionalOptions) -DENABLE_PERF_COUNTERS</ClAdditionalOptions> + <UserIncludes> + $(UserIncludes); + $(VCToolsIncPath); + $(_NTDRIVE)$(_NTROOT)\ndp\clr\src\strongname\inc + </UserIncludes> + </PropertyGroup> + + <!-- Dependencies --> + <ItemGroup> + <ProjectReference Include="$(ClrSrcDirectory)inc\corguids.nativeproj"> + <Comment>clrinternal.h</Comment> + </ProjectReference> + </ItemGroup> + + <!-- Sources common for 'NoDependencies' and 'normal' build --> + <ItemGroup> + <CppCompile Include="$(UtilCodeSrcDir)\ClrHost_NoDependencies.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\Ex.cpp" /> + + <CppCompile Include="$(UtilCodeSrcDir)\SBuffer.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\SString.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\SString_COM.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\FString.cpp" /> + + <CppCompile Include="$(UtilCodeSrcDir)\NamespaceUtil.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\MakePath.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\SplitPath.cpp" /> + + <CppCompile Include="$(UtilCodeSrcDir)\ClrConfig.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\RegUtil.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\RegistryWrapper.cpp" /> + + <CppCompile Include="$(UtilCodeSrcDir)\Collections.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\Util_NoDependencies.cpp" /> + + <CppCompile Include="$(UtilCodeSrcDir)\GenericStackProbe.cpp" /> + + <CppCompile Include="$(UtilCodeSrcDir)\PostError.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\fstream.cpp" /> + + <!-- File format and MetaData --> + <CppCompile Include="$(UtilCodeSrcDir)\ClrHelpers.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\StgPool.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\StgPooli.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\StgPoolReadOnly.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\PEDecoder.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\LazyCOW.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\UTSem.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\peinformation.cpp" /> + </ItemGroup> + + <ItemGroup> + <DebuggingSupportSources Include="$(UtilCodeSrcDir)\Check.cpp" /> + <DebuggingSupportSources Include="$(UtilCodeSrcDir)\Debug.cpp" /> + <DebuggingSupportSources Include="$(UtilCodeSrcDir)\Log.cpp" /> + <DebuggingSupportSources Include="$(UtilCodeSrcDir)\UtilMessageBox.cpp" /> + <DebuggingSupportSources Include="$(UtilCodeSrcDir)\StackTrace.cpp" /> + + <DebuggingSupportSources Include="$(UtilCodeSrcDir)\SafeWrap.cpp" /> + <DebuggingSupportSources Include="$(UtilCodeSrcDir)\WinFix.cpp" /> + </ItemGroup> + + <!-- Sources for 'normal' build (can depend on mscoree.dll and other DLLs) --> + <ItemGroup Condition="'$(FeatureUtilcodeNoDependencies)' != 'true'"> + <CppCompile Include="@(DebuggingSupportSources)" /> + <CppCompile Include="$(UtilCodeSrcDir)\ApiThreadStress.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\ArrayList.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\AssemblyFileHash.cpp" Condition="'$(FeatureCoreClr)' != 'true'"/> + <CppCompile Include="$(UtilCodeSrcDir)\BitVector.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\ccomprc.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\CircularLog.cpp" Condition="'$(FeatureCoreClr)' != 'true'"/> + <CppCompile Include="$(UtilCodeSrcDir)\ClrHost.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\COMex.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\CycleTimer.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\DelayLoadHelpers.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\GuidFromName.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\jitperf.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\md5.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\MemoryPool.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\Util.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\RangeTree.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\IAllocator.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\LoaderHeap.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\PerfLog.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\OutString.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\ilFormatter.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\OpInfo.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\DacUtil.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\TlbUtils.cpp" Condition="'$(FeatureCominteropTlbSupport)' == 'true'"/> + <CppCompile Include="$(UtilCodeSrcDir)\SortVersioning.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\NewApis.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\DownLevel.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\StressLog.cpp" /> + + <!-- File format and MetaData --> + <CppCompile Include="$(UtilCodeSrcDir)\CorImage.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\format1.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\PrettyPrintSig.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\SigBuilder.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\SigParser.cpp" /> + + <CppCompile Include="$(UtilCodeSrcDir)\dlwrap.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\LoadRC.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\SxSHelpers.cpp" Condition="'$(FeatureCoreClr)' != 'true'"/> + <CppCompile Include="$(UtilCodeSrcDir)\TLS.cpp" Condition="'$(FeatureImplicitTls)' != 'true'"/> + <CppCompile Include="$(UtilCodeSrcDir)\SecurityWrapper.cpp" /> + <CppCompile Include="$(UtilCodeSrcDir)\SecurityUtil.cpp" /> + + <CppCompile Include="$(UtilCodeSrcDir)\AppXUtil.cpp" Condition="'$(FeatureAppX)' == 'true'"/> + </ItemGroup> +</Project> diff --git a/src/utilcode/utilmessagebox.cpp b/src/utilcode/utilmessagebox.cpp new file mode 100644 index 0000000000..737e89b15f --- /dev/null +++ b/src/utilcode/utilmessagebox.cpp @@ -0,0 +1,599 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// UtilMessageBox.cpp +// + +// +// This module contains the message box utility code for the CLR. It is used +// by code in the CLR itself as well as other tools that build in the CLR tree. +// For message boxes inside the ExecutionEngine, EEMessageBox must be used +// instead of the these APIs. +// +//***************************************************************************** +#include "stdafx.h" // Standard header. +#include <utilcode.h> // Utility helpers. +#include <corerror.h> +#include "ndpversion.h" +#include "../dlls/mscorrc/resource.h" +#include "ex.h" +#if !defined(FEATURE_CORESYSTEM) +#undef NTDDI_VERSION +#define NTDDI_VERSION NTDDI_WIN7 +#include "commctrl.h" +#endif + +#if !defined(SELF_NO_HOST) && !defined(FEATURE_CORECLR) + +// +// This should be used for runtime dialog box, because we assume the resource is from mscorrc.dll +// For tools like ildasm or Shim which uses their own resource file, you need to define IDS_RTL in +// their resource file and define a function like this and append the style returned from the function +// to every calls to WszMessageBox. +// +UINT GetCLRMBRTLStyle() +{ + WRAPPER_NO_CONTRACT; + + UINT mbStyle = 0; + WCHAR buff[MAX_PATH]; + if(SUCCEEDED(UtilLoadStringRC(IDS_RTL, buff, MAX_PATH, true))) { + if(wcscmp(buff, W("RTL_True")) == 0) { + mbStyle = 0x00080000 |0x00100000; // MB_RIGHT || MB_RTLREADING + } + } + return mbStyle; +} + +#endif //!defined(SELF_NO_HOST) && !defined(FEATURE_CORECLR) + +BOOL ShouldDisplayMsgBoxOnCriticalFailure() +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + +#ifdef _DEBUG + // To help find issues, we will always display dialogs for critical failures + // under debug builds. This includes asserts and other critical issues. + return TRUE; +#else + // Retrieve error mode + UINT last = SetErrorMode(0); + SetErrorMode(last); //set back to previous value + + // SEM_FAILCRITICALERRORS indicates that the system does not display the critical-error-handler + // message box. Instead, the system sends the error to the calling process. + return !(last & SEM_FAILCRITICALERRORS); +#endif // _DEBUG +} + + +#if !defined(FEATURE_CORESYSTEM) && !defined(FEATURE_CORECLR) +enum ProbedTaskDialogIndirectState +{ + ProbedTaskDialogIndirectState_NotProbed = 0, + ProbedTaskDialogIndirectState_NotAvailable = 1, + ProbedTaskDialogIndirectState_Available = 2 +}; + +static ProbedTaskDialogIndirectState siProbedTaskDialogIndirect = ProbedTaskDialogIndirectState_NotProbed; +#endif // !FEATURE_CORESYSTEM && !FEATURE_CORECLR + + +// We'd like to use TaskDialogIndirect for asserts coming from managed code in particular +// to display the detailedText in a scrollable way. Also, we'd like to reuse the CLR's +// plumbing code for the rest of parts of the assert dialog. Note that the simple +// Win32 MessageBox does not support the detailedText value. +// If we later refactor MessageBoxImpl into its own DLL, move the lines referencing +// "Microsoft.Windows.Common-Controls" version 6 in stdafx.h as well. +int MessageBoxImpl( + HWND hWnd, // Handle to Owner Window + LPCWSTR message, // Message + LPCWSTR title, // Dialog box title + LPCWSTR detailedText, // Details like a stack trace, etc. + UINT uType) +{ + CONTRACTL + { + // May pump messages. Callers should be GC_TRIGGERS and MODE_PREEMPTIVE, + // but we can't include EE contracts here. + THROWS; + INJECT_FAULT(return IDCANCEL;); + + // Assert if none of MB_ICON is set + PRECONDITION((uType & MB_ICONMASK) != 0); + } + CONTRACTL_END; + +#if defined(FEATURE_CORESYSTEM) || defined (FEATURE_CORECLR) + return WszMessageBox(hWnd, message, title, uType); +#else + bool mustUseMessageBox = false; // Mac, Silverlight, pre-Vista? Do we support this type of message box? + decltype(TaskDialogIndirect)* pfnTaskDialogIndirect = NULL; + ULONG_PTR cookie = NULL; // For activation context. + bool activatedActivationContext = false; + HModuleHolder hmodComctl32; + HANDLE hActCtx = INVALID_HANDLE_VALUE; + + // Note: TaskDialogIndirect is only in the v6 and above versions of comctl32. Windows + // stores that library in the WinSxS directory in a directory with + // "Microsoft.Windows.Common-Controls" in the name. Your application can only see + // this library if the linker has added a manifest dependency on the V6 common controls + // to your application. Or, you can create an activation context to make this work, + // if your library also has the appropriate manifest dependency. + // Also, I'm not going to leave comctl32.dll mapped, to ensure it can't somehow + // interfere with older versions. Therefore, re-load comctl32.dll every time through + // this method. We will record whether TaskDialogIndirect is available though, so + // we can fall back to MessageBox faster. + + // We don't yet have a perfect mapping from all MessageBox behavior to TaskDialogIndirect behavior. + // Use MessageBox to avoid most of this complexity. + if (((uType & MB_ICONMASK) != MB_ICONWARNING) && (uType & MB_ICONMASK) != MB_ICONERROR || + (uType & MB_TYPEMASK) != MB_ABORTRETRYIGNORE || + (uType & MB_DEFMASK) != 0 || + (uType & MB_MODEMASK) != 0 || + (uType & MB_MISCMASK) != 0) + mustUseMessageBox = true; + else if (mustUseMessageBox || siProbedTaskDialogIndirect == ProbedTaskDialogIndirectState_NotAvailable) + mustUseMessageBox = true; + else { + // Replace our application's ActivationContext temporarily, load comctl32 + // & look for TaskDialogIndirect. Don't cache pointer. + // The following code was suggested by some Windows experts. We do not want + // to add a manifest to our library saying we use comctl32 v6, because that + // will mean loading a lot of extra libraries on startup (a significant perf hit). + // We could either store the manifest as a resource, or more creatively since + // we are effectively a Windows component, rely on %windir%\WindowsShell.manifest. + ACTCTX ctx = { sizeof(ACTCTX) }; + ctx.dwFlags = 0; + StackSString manifestPath; // Point this at %windir%\WindowsShell.manifest, for comctl32 version 6. + UINT numChars = WszGetWindowsDirectory(manifestPath.OpenUnicodeBuffer(MAX_PATH), MAX_PATH); + if (numChars == 0 || numChars >= MAX_PATH) + { + _ASSERTE(0); // How did this fail? + } + else { + manifestPath.CloseBuffer(numChars); + if (manifestPath[manifestPath.GetCount() - 1] != W('\\')) + manifestPath.Append(W('\\')); + manifestPath.Append(W("WindowsShell.manifest")); // Other Windows components have already loaded this. + ctx.lpSource = manifestPath.GetUnicode(); + hActCtx = CreateActCtx(&ctx); + if (hActCtx != INVALID_HANDLE_VALUE) + { + if (!ActivateActCtx(hActCtx, &cookie)) + { + cookie = NULL; + _ASSERTE(0); // Why did ActivateActCtx fail? (We'll continue executing & cope with the failure.) + } + else { + activatedActivationContext = true; + // Activation context was replaced - now we can load comctl32 version 6. + hmodComctl32 = WszLoadLibrary(W("comctl32.dll")); + + if (hmodComctl32 != INVALID_HANDLE_VALUE) { + pfnTaskDialogIndirect = (decltype(TaskDialogIndirect)*)GetProcAddress(hmodComctl32, "TaskDialogIndirect"); + if (pfnTaskDialogIndirect == NULL) { + hmodComctl32.Release(); + } + } + } + } + } + + siProbedTaskDialogIndirect = (pfnTaskDialogIndirect == NULL) ? ProbedTaskDialogIndirectState_NotAvailable : ProbedTaskDialogIndirectState_Available; + mustUseMessageBox = (pfnTaskDialogIndirect == NULL); + } + + int result; + if (mustUseMessageBox) { + result = WszMessageBox(hWnd, message, title, uType); + } + else { + _ASSERTE(pfnTaskDialogIndirect != NULL); + int nButtonPressed = 0; + TASKDIALOGCONFIG config = {0}; + config.cbSize = sizeof(config); + config.hwndParent = hWnd; + config.dwCommonButtons = 0; + config.pszWindowTitle = title; + config.dwFlags = (uType & MB_RTLREADING) ? TDF_RTL_LAYOUT : 0; + + // Set the user-visible icon in the window. + _ASSERTE(((uType & MB_ICONMASK) == MB_ICONWARNING) || ((uType & MB_ICONMASK) == MB_ICONERROR)); + config.pszMainIcon = ((uType & MB_ICONMASK) == MB_ICONWARNING) ? TD_WARNING_ICON : TD_ERROR_ICON; + + config.pszMainInstruction = title; + config.pszContent = message; + config.pszExpandedInformation = detailedText; + + // Set up the buttons + // Note about button hot keys: Windows keeps track of of where the last input came from + // (ie, mouse or keyboard). If you use the mouse to interact w/ one dialog box and then use + // the keyboard, the next dialog will not include hot keys. This is a Windows feature to + // minimize clutter on the screen for mouse users. + _ASSERTE((uType & MB_TYPEMASK) == MB_ABORTRETRYIGNORE); + StackSString abortLabel, debugLabel, ignoreLabel; + const WCHAR *pAbortLabel, *pDebugLabel, *pIgnoreLabel; + + if (abortLabel.LoadResource(CCompRC::Optional, IDS_DIALOG_BOX_ABORT_BUTTON)) + pAbortLabel = abortLabel.GetUnicode(); + else + pAbortLabel = W("&Abort"); + if (debugLabel.LoadResource(CCompRC::Optional, IDS_DIALOG_BOX_DEBUG_BUTTON)) + pDebugLabel = debugLabel.GetUnicode(); + else + pDebugLabel = W("&Debug"); + if (ignoreLabel.LoadResource(CCompRC::Optional, IDS_DIALOG_BOX_IGNORE_BUTTON)) + pIgnoreLabel = ignoreLabel.GetUnicode(); + else + pIgnoreLabel = W("&Ignore"); + + const TASKDIALOG_BUTTON abortDebugIgnoreButtons[] = { + { IDOK, pAbortLabel }, + { IDRETRY, pDebugLabel }, + { IDIGNORE, pIgnoreLabel } + }; + config.pButtons = abortDebugIgnoreButtons; + config.cButtons = 3; + + HRESULT hr = pfnTaskDialogIndirect(&config, &nButtonPressed, NULL, NULL); + _ASSERTE(hr == S_OK); + if (hr == S_OK) { + result = nButtonPressed; + } + + _ASSERTE(result == IDOK || result == IDRETRY || result == IDIGNORE); + } + + if (activatedActivationContext) { + DeactivateActCtx(0, cookie); + ReleaseActCtx(hActCtx); // perf isn’t important so we won’t bother caching the actctx + } + + return result; +#endif +} + +int UtilMessageBoxVA( + HWND hWnd, // Handle to Owner Window + UINT uText, // Resource Identifier for Text message + UINT uTitle, // Resource Identifier for Title + UINT uType, // Style of MessageBox + BOOL displayForNonInteractive, // Display even if the process is running non interactive + BOOL showFileNameInTitle, // Flag to show FileName in Caption + va_list args) // Additional Arguments +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return IDCANCEL;); + } + CONTRACTL_END; + + SString text; + SString title; + int result = IDCANCEL; + + EX_TRY + { + text.LoadResource(CCompRC::Error, uText); + title.LoadResource(CCompRC::Error, uTitle); + + result = UtilMessageBoxNonLocalizedVA(hWnd, (LPWSTR)text.GetUnicode(), + (LPWSTR)title.GetUnicode(), uType, displayForNonInteractive, showFileNameInTitle, NULL, args); + } + EX_CATCH + { + result = IDCANCEL; + } + EX_END_CATCH(SwallowAllExceptions); + + return result; +} + +int UtilMessageBoxNonLocalizedVA( + HWND hWnd, // Handle to Owner Window + LPCWSTR lpText, // Text message + LPCWSTR lpTitle, // Title + UINT uType, // Style of MessageBox + BOOL displayForNonInteractive, // Display even if the process is running non interactive + BOOL showFileNameInTitle, // Flag to show FileName in Caption + BOOL * pInputFromUser, // To distinguish between user pressing abort vs. assuming abort. + va_list args) // Additional Arguments +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return IDCANCEL;); + + // Assert if none of MB_ICON is set + PRECONDITION((uType & MB_ICONMASK) != 0); + } + CONTRACTL_END; + + return UtilMessageBoxNonLocalizedVA(hWnd, lpText, lpTitle, NULL, uType, displayForNonInteractive, showFileNameInTitle, pInputFromUser, args); +} + +int UtilMessageBoxNonLocalizedVA( + HWND hWnd, // Handle to Owner Window + LPCWSTR lpText, // Text message + LPCWSTR lpTitle, // Title + LPCWSTR lpDetails,// Details like a stack trace, etc. + UINT uType, // Style of MessageBox + BOOL displayForNonInteractive, // Display even if the process is running non interactive + BOOL showFileNameInTitle, // Flag to show FileName in Caption + BOOL * pInputFromUser, // To distinguish between user pressing abort vs. assuming abort. + va_list args) // Additional Arguments +{ + CONTRACTL + { + NOTHROW; + INJECT_FAULT(return IDCANCEL;); + + // Assert if none of MB_ICON is set + PRECONDITION((uType & MB_ICONMASK) != 0); + } + CONTRACTL_END; + + int result = IDCANCEL; + if (pInputFromUser != NULL) + { + *pInputFromUser = FALSE; + } + + EX_TRY + { + StackSString formattedMessage; + StackSString formattedTitle; + SString details(lpDetails); + StackSString fileName; + BOOL fDisplayMsgBox = TRUE; + + // Format message string using optional parameters + formattedMessage.VPrintf(lpText, args); + + // Try to get filename of Module and add it to title + if (showFileNameInTitle && WszGetModuleFileName(NULL, fileName.OpenUnicodeBuffer(MAX_PATH), MAX_PATH)) + { + LPCWSTR wszName = NULL; + size_t cchName = 0; + + // Close the buffer we opened before the call to WszGetModuleFileName. + fileName.CloseBuffer(); + + SplitPathInterior(fileName, NULL, NULL, NULL, NULL, &wszName, &cchName, NULL, NULL); + formattedTitle.Printf(W("%s - %s"), wszName, lpTitle); + } + else + { + formattedTitle.Set(lpTitle); + } + +#if !defined(FEATURE_UTILCODE_NO_DEPENDENCIES) + // If the current process isn't interactive (a service for example), then we report the message + // in the event log and via OutputDebugString. + // + // We may still however attempt to display the message box if the MB_SERVICE_NOTIFICATION + // message box style was specified. + if (!RunningInteractive()) + { + HANDLE h; + StackSString message; + + message.Printf(W(".NET Runtime version : %s - "), VER_FILEVERSION_STR_L); + if (lpTitle) + message.Append(lpTitle); + if (!formattedMessage.IsEmpty()) + message.Append(formattedMessage); + + ClrReportEvent(W(".NET Runtime"), + EVENTLOG_ERROR_TYPE, // event type + 0, // category zero + 1024, // event identifier + NULL, // no user security identifier + message.GetUnicode()); + + if(lpTitle != NULL) + WszOutputDebugString(lpTitle); + if(!formattedMessage.IsEmpty()) + WszOutputDebugString(formattedMessage); + + // If we are running as a service and displayForNonInteractive is FALSE then IDABORT is + // the best value to return as it will most likely cause callers of this API to abort the process. + // This is the right thing to do since attaching a debugger doesn't make much sense when the process isn't + // running in interactive mode. + if(!displayForNonInteractive) + { + fDisplayMsgBox = FALSE; + result = IDABORT; + } + else + { + // Include in the MB_DEFAULT_DESKTOP_ONLY style. + uType |= MB_DEFAULT_DESKTOP_ONLY; + } + } +#endif //!defined(FEATURE_UTILCODE_NO_DEPENDENCIES) + + if (fDisplayMsgBox) + { + // We normally want to set the reading direction (right-to-left etc.) based on the resources + // in use. However, outside the CLR (SELF_NO_HOST) we can't assume we have resources and + // in CORECLR we can't even necessarily expect that our CLR callbacks have been initialized. + // This code path is used for ASSERT dialogs. +#if !defined(SELF_NO_HOST) && !defined(FEATURE_CORECLR) + uType |= GetCLRMBRTLStyle(); +#endif + + result = MessageBoxImpl(hWnd, formattedMessage, formattedTitle, details, uType); + + if (pInputFromUser != NULL) + { + *pInputFromUser = TRUE; + } + } + } + EX_CATCH + { + result = IDCANCEL; + } + EX_END_CATCH(SwallowAllExceptions); + + return result; +} + +int UtilMessageBox( + HWND hWnd, // Handle to Owner Window + UINT uText, // Resource Identifier for Text message + UINT uTitle, // Resource Identifier for Title + UINT uType, // Style of MessageBox + BOOL displayForNonInteractive, // Display even if the process is running non interactive + BOOL showFileNameInTitle, // Flag to show FileName in Caption + ...) // Additional Arguments +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + va_list marker; + va_start(marker, showFileNameInTitle); + + int result = UtilMessageBoxVA(hWnd, uText, uTitle, uType, displayForNonInteractive, showFileNameInTitle, marker); + va_end( marker ); + + return result; +} + +int UtilMessageBoxNonLocalized( + HWND hWnd, // Handle to Owner Window + LPCWSTR lpText, // Text message + LPCWSTR lpTitle, // Title message + UINT uType, // Style of MessageBox + BOOL displayForNonInteractive, // Display even if the process is running non interactive + BOOL showFileNameInTitle, // Flag to show FileName in Caption + ... ) // Additional Arguments +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + va_list marker; + va_start(marker, showFileNameInTitle); + + int result = UtilMessageBoxNonLocalizedVA( + hWnd, lpText, lpTitle, uType, displayForNonInteractive, showFileNameInTitle, NULL, marker); + va_end( marker ); + + return result; +} + +int UtilMessageBoxCatastrophic( + UINT uText, // Text for MessageBox + UINT uTitle, // Title for MessageBox + UINT uType, // Style of MessageBox + BOOL showFileNameInTitle, // Flag to show FileName in Caption + ...) +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + va_list marker; + va_start(marker, showFileNameInTitle); + + int result = UtilMessageBoxCatastrophicVA(uText, uTitle, uType, showFileNameInTitle, marker); + va_end( marker ); + + return result; +} + +int UtilMessageBoxCatastrophicNonLocalized( + LPCWSTR lpText, // Text for MessageBox + LPCWSTR lpTitle, // Title for MessageBox + UINT uType, // Style of MessageBox + BOOL showFileNameInTitle, // Flag to show FileName in Caption + ...) +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + va_list marker; + va_start(marker, showFileNameInTitle); + + int result = UtilMessageBoxCatastrophicNonLocalizedVA(lpText, lpTitle, uType, showFileNameInTitle, marker); + va_end( marker ); + + return result; +} + +int UtilMessageBoxCatastrophicVA( + UINT uText, // Text for MessageBox + UINT uTitle, // Title for MessageBox + UINT uType, // Style of MessageBox + BOOL showFileNameInTitle, // Flag to show FileName in Caption + va_list args) // Additional Arguments +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + HWND hwnd = NULL; + + // We are already in a catastrophic situation so we can tolerate faults as well as SO & GC mode violations to keep going. + CONTRACT_VIOLATION(FaultNotFatal | GCViolation | ModeViolation | SOToleranceViolation); + + if (!ShouldDisplayMsgBoxOnCriticalFailure()) + return IDABORT; + + // Add the MB_TASKMODAL style to indicate that the dialog should be displayed on top of the windows + // owned by the current thread and should prevent interaction with them until dismissed. + uType |= MB_TASKMODAL; + + return UtilMessageBoxVA(hwnd, uText, uTitle, uType, TRUE, showFileNameInTitle, args); +} + +int UtilMessageBoxCatastrophicNonLocalizedVA( + LPCWSTR lpText, // Text for MessageBox + LPCWSTR lpTitle, // Title for MessageBox + UINT uType, // Style of MessageBox + BOOL showFileNameInTitle, // Flag to show FileName in Caption + va_list args) // Additional Arguments +{ + CONTRACTL + { + NOTHROW; + } + CONTRACTL_END; + + HWND hwnd = NULL; + + // We are already in a catastrophic situation so we can tolerate faults as well as SO & GC mode violations to keep going. + CONTRACT_VIOLATION(FaultNotFatal | GCViolation | ModeViolation | SOToleranceViolation); + + if (!ShouldDisplayMsgBoxOnCriticalFailure()) + return IDABORT; + + // Add the MB_TASKMODAL style to indicate that the dialog should be displayed on top of the windows + // owned by the current thread and should prevent interaction with them until dismissed. + uType |= MB_TASKMODAL; + + return UtilMessageBoxNonLocalizedVA(hwnd, lpText, lpTitle, uType, TRUE, showFileNameInTitle, NULL, args); +} + diff --git a/src/utilcode/utsem.cpp b/src/utilcode/utsem.cpp new file mode 100644 index 0000000000..0d3125068d --- /dev/null +++ b/src/utilcode/utsem.cpp @@ -0,0 +1,553 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +/****************************************************************************** + FILE : UTSEM.CPP + + + + Purpose: Part of the utilities library for the VIPER project + + Abstract : Implements the UTSemReadWrite class. +------------------------------------------------------------------------------- +Revision History: + + +*******************************************************************************/ +#include "stdafx.h" +#include "clrhost.h" +#include "ex.h" + +#include <utsem.h> +#include "contract.h" + +// Consider replacing this with a #ifdef INTEROP_DEBUGGING +#if !defined(SELF_NO_HOST) && defined(_TARGET_X86_) +// For Interop debugging, the UTSemReadWrite class must inform the debugger +// that this thread can't be suspended currently. See vm\util.hpp for the +// implementation of these methods. +void IncCantStopCount(); +void DecCantStopCount(); +#else +#define IncCantStopCount() +#define DecCantStopCount() +#endif // !SELF_NO_HOST && _TARGET_X86_ + +/****************************************************************************** +Definitions of the bit fields in UTSemReadWrite::m_dwFlag: + +Warning: The code assume that READER_MASK is in the low-order bits of the DWORD. +******************************************************************************/ + +const ULONG READERS_MASK = 0x000003FF; // field that counts number of readers +const ULONG READERS_INCR = 0x00000001; // amount to add to increment number of readers + +// The following field is 2 bits long to make it easier to catch errors. +// (If the number of writers ever exceeds 1, we've got problems.) +const ULONG WRITERS_MASK = 0x00000C00; // field that counts number of writers +const ULONG WRITERS_INCR = 0x00000400; // amount to add to increment number of writers + +const ULONG READWAITERS_MASK = 0x003FF000; // field that counts number of threads waiting to read +const ULONG READWAITERS_INCR = 0x00001000; // amount to add to increment number of read waiters + +const ULONG WRITEWAITERS_MASK = 0xFFC00000; // field that counts number of threads waiting to write +const ULONG WRITEWAITERS_INCR = 0x00400000; // amoun to add to increment number of write waiters + +// ====================================================================================== +// Spinning support + +// Copy of definition from file:..\VM\spinlock.h +#define CALLER_LIMITS_SPINNING 0 + +#if defined(SELF_NO_HOST) && !defined(CROSSGEN_COMPILE) + +// When we do not have host, we just call OS - see file:..\VM\hosting.cpp#__SwitchToThread +BOOL __SwitchToThread(DWORD dwSleepMSec, DWORD dwSwitchCount) +{ + // This is just simple implementation that does not support full dwSwitchCount arg + _ASSERTE(dwSwitchCount == CALLER_LIMITS_SPINNING); + return SwitchToThread(); +} + +Volatile<BOOL> g_fInitializedGlobalSystemInfo = FALSE; + +// Global System Information +SYSTEM_INFO g_SystemInfo; + +// Configurable constants used across our spin locks +SpinConstants g_SpinConstants = { + 50, // dwInitialDuration + 40000, // dwMaximumDuration - ideally (20000 * max(2, numProc)) ... updated in code:InitializeSpinConstants_NoHost + 3, // dwBackoffFactor + 10 // dwRepetitions +}; + +inline void InitializeSpinConstants_NoHost() +{ + g_SpinConstants.dwMaximumDuration = max(2, g_SystemInfo.dwNumberOfProcessors) * 20000; +} + +#else //!SELF_NO_HOST || CROSSGEN_COMPILE + +// Use VM/CrossGen functions and variables +BOOL __SwitchToThread (DWORD dwSleepMSec, DWORD dwSwitchCount); +extern SYSTEM_INFO g_SystemInfo; +extern SpinConstants g_SpinConstants; + +#endif //!SELF_NO_HOST || CROSSGEN_COMPILE + +/****************************************************************************** +Function : UTSemReadWrite::UTSemReadWrite + +Abstract: Constructor. +******************************************************************************/ +UTSemReadWrite::UTSemReadWrite() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + +#if defined(SELF_NO_HOST) && !defined(CROSSGEN_COMPILE) + if (!g_fInitializedGlobalSystemInfo) + { + GetSystemInfo(&g_SystemInfo); + InitializeSpinConstants_NoHost(); + + g_fInitializedGlobalSystemInfo = TRUE; + } +#endif //SELF_NO_HOST && !CROSSGEN_COMPILE + + m_dwFlag = 0; + m_pReadWaiterSemaphore = NULL; + m_pWriteWaiterEvent = NULL; +} + + +/****************************************************************************** +Function : UTSemReadWrite::~UTSemReadWrite + +Abstract: Destructor +******************************************************************************/ +UTSemReadWrite::~UTSemReadWrite() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + _ASSERTE_MSG((m_dwFlag == (ULONG)0), "Destroying a UTSemReadWrite while in use"); + + if (m_pReadWaiterSemaphore != NULL) + delete m_pReadWaiterSemaphore; + + if (m_pWriteWaiterEvent != NULL) + delete m_pWriteWaiterEvent; +} + +//======================================================================================= +// +// Initialize the lock (its semaphore and event) +// +HRESULT +UTSemReadWrite::Init() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + _ASSERTE(m_pReadWaiterSemaphore == NULL); + _ASSERTE(m_pWriteWaiterEvent == NULL); + + EX_TRY + { + CONTRACT_VIOLATION(ThrowsViolation); + + m_pReadWaiterSemaphore = new Semaphore(); + m_pReadWaiterSemaphore->Create(0, MAXLONG); + + m_pWriteWaiterEvent = new Event(); + m_pWriteWaiterEvent->CreateAutoEvent(FALSE); + } + EX_CATCH + { + hr = E_OUTOFMEMORY; + } + EX_END_CATCH(SwallowAllExceptions) + IfFailGo(hr); + +ErrExit: + return hr; +} // UTSemReadWrite::Init + +/****************************************************************************** +Function : UTSemReadWrite::LockRead + +Abstract: Obtain a shared lock +******************************************************************************/ +HRESULT UTSemReadWrite::LockRead() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + CAN_TAKE_LOCK; + } + CONTRACTL_END; + + // Inform CLR that the debugger shouldn't suspend this thread while + // holding this lock. + IncCantStopCount(); + + // First do some spinning - copied from file:..\VM\crst.cpp#CrstBase::SpinEnter + for (DWORD iter = 0; iter < g_SpinConstants.dwRepetitions; iter++) + { + DWORD i = g_SpinConstants.dwInitialDuration; + + do + { + DWORD dwFlag = m_dwFlag; + + if (dwFlag < READERS_MASK) + { // There are just readers in the play, try to add one more + if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + READERS_INCR, dwFlag)) + { + goto ReadLockAcquired; + } + } + + if (g_SystemInfo.dwNumberOfProcessors <= 1) + { // We do not need to spin on a single processor + break; + } + + // Delay by approximately 2*i clock cycles (Pentium III). + // This is brittle code - future processors may of course execute this + // faster or slower, and future code generators may eliminate the loop altogether. + // The precise value of the delay is not critical, however, and I can't think + // of a better way that isn't machine-dependent. + int sum = 0; + + for (int delayCount = i; --delayCount; ) + { + sum += delayCount; + YieldProcessor(); // indicate to the processor that we are spining + } + + if (sum == 0) + { + // never executed, just to fool the compiler into thinking sum is live here, + // so that it won't optimize away the loop. + static char dummy; + dummy++; + } + // exponential backoff: wait a factor longer in the next iteration + i *= g_SpinConstants.dwBackoffFactor; + } while (i < g_SpinConstants.dwMaximumDuration); + + __SwitchToThread(0, CALLER_LIMITS_SPINNING); + } + // Stop spinning + + // Start waiting + for (;;) + { + DWORD dwFlag = m_dwFlag; + + if (dwFlag < READERS_MASK) + { // There are just readers in the play, try to add one more + if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + READERS_INCR, dwFlag)) + { + break; + } + } + else if ((dwFlag & READERS_MASK) == READERS_MASK) + { // The number of readers has reached the maximum (0x3ff), wait 1s + ClrSleepEx(1000, FALSE); + } + else if ((dwFlag & READWAITERS_MASK) == READWAITERS_MASK) + { // The number of readers waiting on semaphore has reached the maximum (0x3ff), wait 1s + ClrSleepEx(1000, FALSE); + } + else + { // Try to add waiting reader and then wait for signal + if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + READWAITERS_INCR, dwFlag)) + { + m_pReadWaiterSemaphore->Wait(INFINITE, FALSE); + break; + } + } + } + +ReadLockAcquired: + _ASSERTE ((m_dwFlag & READERS_MASK) != 0 && "reader count is zero after acquiring read lock"); + _ASSERTE ((m_dwFlag & WRITERS_MASK) == 0 && "writer count is nonzero after acquiring write lock"); + EE_LOCK_TAKEN(this); + + return S_OK; +} // UTSemReadWrite::LockRead + + + +/****************************************************************************** +Function : UTSemReadWrite::LockWrite + +Abstract: Obtain an exclusive lock +******************************************************************************/ +HRESULT UTSemReadWrite::LockWrite() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + CAN_TAKE_LOCK; + } + CONTRACTL_END; + + // Inform CLR that the debugger shouldn't suspend this thread while + // holding this lock. + IncCantStopCount(); + + // First do some spinning - copied from file:..\VM\crst.cpp#CrstBase::SpinEnter + for (DWORD iter = 0; iter < g_SpinConstants.dwRepetitions; iter++) + { + DWORD i = g_SpinConstants.dwInitialDuration; + + do + { + DWORD dwFlag = m_dwFlag; + + if (dwFlag == 0) + { // No readers/writers in play, try to add a writer + if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, WRITERS_INCR, dwFlag)) + { + goto WriteLockAcquired; + } + } + + if (g_SystemInfo.dwNumberOfProcessors <= 1) + { // We do not need to spin on a single processor + break; + } + + // Delay by approximately 2*i clock cycles (Pentium III). + // This is brittle code - future processors may of course execute this + // faster or slower, and future code generators may eliminate the loop altogether. + // The precise value of the delay is not critical, however, and I can't think + // of a better way that isn't machine-dependent. + int sum = 0; + + for (int delayCount = i; --delayCount; ) + { + sum += delayCount; + YieldProcessor(); // indicate to the processor that we are spining + } + + if (sum == 0) + { + // never executed, just to fool the compiler into thinking sum is live here, + // so that it won't optimize away the loop. + static char dummy; + dummy++; + } + // exponential backoff: wait a factor longer in the next iteration + i *= g_SpinConstants.dwBackoffFactor; + } while (i < g_SpinConstants.dwMaximumDuration); + + __SwitchToThread(0, CALLER_LIMITS_SPINNING); + } + // Stop spinning + + // Start waiting + for (;;) + { + DWORD dwFlag = m_dwFlag; + + if (dwFlag == 0) + { // No readers/writers in play, try to add a writer + if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, WRITERS_INCR, dwFlag)) + { + break; + } + } + else if ((dwFlag & WRITEWAITERS_MASK) == WRITEWAITERS_MASK) + { // The number of writers waiting on semaphore has reached the maximum (0x3ff), wait 1s + ClrSleepEx(1000, FALSE); + } + else + { // Try to add waiting writer and then wait for signal + if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + WRITEWAITERS_INCR, dwFlag)) + { + m_pWriteWaiterEvent->Wait(INFINITE, FALSE); + break; + } + } + + } + +WriteLockAcquired: + _ASSERTE ((m_dwFlag & READERS_MASK) == 0 && "reader count is nonzero after acquiring write lock"); + _ASSERTE ((m_dwFlag & WRITERS_MASK) == WRITERS_INCR && "writer count is not 1 after acquiring write lock"); + EE_LOCK_TAKEN(this); + + return S_OK; +} // UTSemReadWrite::LockWrite + + + +/****************************************************************************** +Function : UTSemReadWrite::UnlockRead + +Abstract: Release a shared lock +******************************************************************************/ +void UTSemReadWrite::UnlockRead() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + ULONG dwFlag; + + + _ASSERTE ((m_dwFlag & READERS_MASK) != 0 && "reader count is zero before releasing read lock"); + _ASSERTE ((m_dwFlag & WRITERS_MASK) == 0 && "writer count is nonzero before releasing read lock"); + + for (;;) + { + dwFlag = m_dwFlag; + + if (dwFlag == READERS_INCR) + { // we're the last reader, and nobody is waiting + if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, (ULONG)0, dwFlag)) + { + break; + } + } + + else if ((dwFlag & READERS_MASK) > READERS_INCR) + { // we're not the last reader + if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag - READERS_INCR, dwFlag)) + { + break; + } + } + + else + { + // here, there should be exactly 1 reader (us), and at least one waiting writer. + _ASSERTE ((dwFlag & READERS_MASK) == READERS_INCR && "UnlockRead consistency error 1"); + _ASSERTE ((dwFlag & WRITEWAITERS_MASK) != 0 && "UnlockRead consistency error 2"); + + // one or more writers is waiting, do one of them next + // (remove a reader (us), remove a write waiter, add a writer + if (dwFlag == + InterlockedCompareExchangeT( + &m_dwFlag, + dwFlag - READERS_INCR - WRITEWAITERS_INCR + WRITERS_INCR, + dwFlag)) + { + m_pWriteWaiterEvent->Set(); + break; + } + } + } + + DecCantStopCount(); + EE_LOCK_RELEASED(this); +} // UTSemReadWrite::UnlockRead + + +/****************************************************************************** +Function : UTSemReadWrite::UnlockWrite + +Abstract: Release an exclusive lock +******************************************************************************/ +void UTSemReadWrite::UnlockWrite() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + ULONG dwFlag; + ULONG count; + + _ASSERTE ((m_dwFlag & READERS_MASK) == 0 && "reader count is nonzero before releasing write lock"); + _ASSERTE ((m_dwFlag & WRITERS_MASK) == WRITERS_INCR && "writer count is not 1 before releasing write lock"); + + for (;;) + { + dwFlag = m_dwFlag; + + if (dwFlag == WRITERS_INCR) + { // nobody is waiting + if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, (ULONG)0, dwFlag)) + { + break; + } + } + + else if ((dwFlag & READWAITERS_MASK) != 0) + { // one or more readers are waiting, do them all next + count = (dwFlag & READWAITERS_MASK) / READWAITERS_INCR; + // remove a writer (us), remove all read waiters, turn them into readers + if (dwFlag == + InterlockedCompareExchangeT( + &m_dwFlag, + dwFlag - WRITERS_INCR - count * READWAITERS_INCR + count * READERS_INCR, + dwFlag)) + { + m_pReadWaiterSemaphore->Release(count, NULL); + break; + } + } + + else + { // one or more writers is waiting, do one of them next + _ASSERTE ((dwFlag & WRITEWAITERS_MASK) != 0 && "UnlockWrite consistency error"); + // (remove a writer (us), remove a write waiter, add a writer + if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag - WRITEWAITERS_INCR, dwFlag)) + { + m_pWriteWaiterEvent->Set(); + break; + } + } + } + + DecCantStopCount(); + EE_LOCK_RELEASED(this); +} // UTSemReadWrite::UnlockWrite + +#ifdef _DEBUG + +//======================================================================================= +BOOL +UTSemReadWrite::Debug_IsLockedForRead() +{ + return ((m_dwFlag & READERS_MASK) != 0); +} + +//======================================================================================= +BOOL +UTSemReadWrite::Debug_IsLockedForWrite() +{ + return ((m_dwFlag & WRITERS_MASK) != 0); +} + +#endif //_DEBUG + diff --git a/src/utilcode/winfix.cpp b/src/utilcode/winfix.cpp new file mode 100644 index 0000000000..00c6392ec7 --- /dev/null +++ b/src/utilcode/winfix.cpp @@ -0,0 +1,515 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// WinWrap.cpp +// + +// +// This file contains wrapper functions for Win32 API's that take strings. +// +// COM+ internally uses UNICODE as the internal state and string format. This +// file will undef the mapping macros so that one cannot mistakingly call a +// method that isn't going to work. Instead, you have to call the correct +// wrapper API. +// +//***************************************************************************** + +#include "stdafx.h" // Precompiled header key. +#include "winwrap.h" // Header for macros and functions. +#include "utilcode.h" +#include "holder.h" +#include "ndpversion.h" +#include "pedecoder.h" + +// ====== READ BEFORE ADDING CONTRACTS ================================================== +// The functions in this file propagate SetLastError codes to their callers. +// Contracts are not guaranteed to preserve these codes (and no, we're not taking +// the overhead hit to make them do so. Don't bother asking.) +// +// Most of the wrappers have a contract of the form: +// +// NOTHROW; +// INJECT_FAULT(xxx); +// +// For such functions, use the special purpose construct: +// +// WINWRAPPER_NO_CONTRACT(xxx); +// +// For everything else, use STATIC_CONTRACT. +// +#undef CONTRACT +#define CONTRACT $$$$$$$$READ_COMMENT_IN_WINFIX_CPP$$$$$$$$$$ + +#undef CONTRACTL +#define CONTRACTL $$$$$$$$READ_COMMENT_IN_WINFIX_CPP$$$$$$$$$$ + +#ifdef ENABLE_CONTRACTS_IMPL +static BOOL gWinWrapperContractRecursionBreak = FALSE; + + +class WinWrapperContract +{ + public: + WinWrapperContract(char *szFunction, char *szFile, int lineNum) + { + CANNOT_HAVE_CONTRACT; + + m_pClrDebugState = NULL; + + if (gWinWrapperContractRecursionBreak) + { + return; + } + + m_pClrDebugState = GetClrDebugState(); + + // Save old debug state + m_IncomingClrDebugState = *m_pClrDebugState; + + + m_pClrDebugState->ViolationMaskReset( ThrowsViolation ); + + if (m_pClrDebugState->IsFaultForbid() && !(m_pClrDebugState->ViolationMask() & (FaultViolation|FaultNotFatal|BadDebugState))) + { + gWinWrapperContractRecursionBreak = TRUE; + + CONTRACT_ASSERT("INJECT_FAULT called in a FAULTFORBID region.", + Contract::FAULT_Forbid, + Contract::FAULT_Mask, + szFunction, + szFile, + lineNum + ); + } + + + }; + + ~WinWrapperContract() + { + CANNOT_HAVE_CONTRACT; + + //!!!!!! THIS DESTRUCTOR MUST NOT CHANGE THE GETLASTERROR VALUE !!!!!! + + // Backout all changes to debug state. + if (m_pClrDebugState != NULL) + { + *m_pClrDebugState = m_IncomingClrDebugState; + } + } + private: + ClrDebugState *m_pClrDebugState; + ClrDebugState m_IncomingClrDebugState; + +}; + + + +#endif + +#ifdef ENABLE_CONTRACTS_IMPL +#define WINWRAPPER_NO_CONTRACT(stmt) \ + STATIC_CONTRACT_NOTHROW; \ + STATIC_CONTRACT_FAULT; \ + STATIC_CONTRACT_CANNOT_TAKE_LOCK; \ + WinWrapperContract __wcontract(__FUNCTION__, __FILE__, __LINE__); \ + if (0) {stmt} \ + +#define STATIC_WINWRAPPER_NO_CONTRACT(stmt) \ + STATIC_CONTRACT_NOTHROW; \ + STATIC_CONTRACT_CANNOT_TAKE_LOCK; \ + STATIC_CONTRACT_FAULT; \ + if (0) {stmt} \ + + +#else +#define WINWRAPPER_NO_CONTRACT(stmt) +#define STATIC_WINWRAPPER_NO_CONTRACT(stmt) +#endif + +ULONG g_dwMaxDBCSCharByteSize = 0; + +#include "psapi.h" +#include "tlhelp32.h" +#include "winnls.h" + +//********** Globals. ********************************************************* +bool g_fEnsureCharSetInfoInitialized = FALSE; // true if we've detected the platform's character set characteristics + +// Detect Unicode support of the operating system, and initialize globals +void EnsureCharSetInfoInitialized() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_CANNOT_TAKE_LOCK; + STATIC_CONTRACT_SO_TOLERANT; + + if (!g_fEnsureCharSetInfoInitialized) + { + // NOTE: Do not use any of the Wsz* wrapper functions right now. They will have + // problems. + + // Per Shupak, you're supposed to get the maximum size of a DBCS char + // dynamically to work properly on all locales (bug 2757). + CPINFO cpInfo; + if (GetCPInfo(CP_ACP, &cpInfo)) + g_dwMaxDBCSCharByteSize = cpInfo.MaxCharSize; + else + g_dwMaxDBCSCharByteSize = 2; + + VolatileStore(&g_fEnsureCharSetInfoInitialized, true); + } + + return; +} + + +// Running with an interactive workstation. +BOOL RunningInteractive() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + static int fInteractive = -1; + if (fInteractive != -1) + return fInteractive != 0; + +#if !defined(FEATURE_CORESYSTEM) + HWINSTA hwinsta = NULL; + + if ((hwinsta = GetProcessWindowStation() ) != NULL) + { + DWORD lengthNeeded; + USEROBJECTFLAGS flags; + + if (GetUserObjectInformationW (hwinsta, UOI_FLAGS, &flags, sizeof(flags), &lengthNeeded)) + { + if ((flags.dwFlags & WSF_VISIBLE) == 0) + fInteractive = 0; + } + } +#endif // !FEATURE_CORESYSTEM + + if (fInteractive != 0) + fInteractive = 1; + + return fInteractive != 0; +} + + +// Wrapper function around CheckTokenMembership to determine if the token enables the SID "S-1-5-<rid>". +// If hToken is NULL, this function uses the thread's impersonation token. If the thread is not impersonating, the +// process token is used. +// +// If the function succeeds, it returns ERROR_SUCCESS, else it returns the error code returned by GetLastError() +static DWORD TokenEnablesSID(IN HANDLE hToken OPTIONAL, IN DWORD rid, OUT BOOL& fResult) +{ + DWORD dwError; + SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY; + PSID pSid = NULL; + HMODULE hAdvApi32 = NULL; + typedef BOOL (WINAPI *CheckTokenMembership_t)(HANDLE TokenHandle, PSID SidToCheck, PBOOL IsMember); + CheckTokenMembership_t pfnCheckTokenMembership = NULL; + + hAdvApi32 = WszGetModuleHandle(W("advapi32.dll")); + if (hAdvApi32 == NULL) + { + dwError = ERROR_MOD_NOT_FOUND; + goto lExit; + } + pfnCheckTokenMembership = (CheckTokenMembership_t) GetProcAddress(hAdvApi32, "CheckTokenMembership"); + if (pfnCheckTokenMembership == NULL) + { + dwError = GetLastError(); + goto lExit; + } + + fResult = FALSE; + if (!AllocateAndInitializeSid(&SIDAuthNT, 1, rid, 0, 0, 0, 0, 0, 0, 0, &pSid)) + { + dwError = GetLastError(); + goto lExit; + } + if (!pfnCheckTokenMembership(hToken, pSid, &fResult)) + { + dwError = GetLastError(); + goto lExit; + } + dwError = ERROR_SUCCESS; + +lExit: + if (pSid) FreeSid(pSid); + return dwError; + +} + +// Determines if the process is running as Local System or as a service. Note that +// the function attempts to determine the process' identity and not the thread's +// (if the thread is impersonating). +// +// Parameters: +// fIsLocalSystemOrService - TRUE if the function succeeds and the process is +// running as SYSTEM or as a service +// +// Return value: +// +// If the function succeeds, it returns ERROR_SUCCESS, else it returns the error +// code returned by GetLastError() +// +// Notes: +// +// This function will generally fail if the calling thread is impersonating at the +// ANONYMOUS level; see the comments in the function. +// +DWORD RunningAsLocalSystemOrService(OUT BOOL& fIsLocalSystemOrService) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_FORBID_FAULT; + + static int fLocalSystemOrService = -1; + if (fLocalSystemOrService != -1) + { + fIsLocalSystemOrService = fLocalSystemOrService != 0; + return ERROR_SUCCESS; + } + + DWORD dwError; + HANDLE hThreadToken = NULL; + HANDLE hProcessToken = NULL; + HANDLE hDuplicatedProcessToken = NULL; + BOOL fLocalSystem = FALSE; + BOOL fService = FALSE; + BOOL bReverted = FALSE; + + if (OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &hThreadToken)) + { + if (RevertToSelf()) + { + bReverted = TRUE; + } +#ifdef _DEBUG + else + { + // For debugging only, continue as the impersonated user; see comment below + dwError = GetLastError(); + } +#endif // #ifdef _DEBUG + } +#ifdef _DEBUG + else + { + dwError = GetLastError(); + if (dwError == ERROR_NO_IMPERSONATION_TOKEN || dwError == ERROR_NO_TOKEN) + { + // The thread is not impersonating; it's safe to continue + } + else + { + // The thread could be impersonating, but we won't be able to restore the impersonation + // token if we RevertToSelf(). Continue as the impersonated user. OpenProcessToken will + // fail (unless the impersonated user is SYSTEM or the same as the process' user). + // + // Note that this case will occur if the impersonation level is ANONYMOUS, the error + // code will be ERROR_CANT_OPEN_ANONYMOUS. + } + } +#endif // #ifdef _DEBUG + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE, &hProcessToken)) + { + dwError = GetLastError(); + goto lExit; + } + + if (!DuplicateToken(hProcessToken, SecurityImpersonation, &hDuplicatedProcessToken)) + { + dwError = GetLastError(); + goto lExit; + } + + dwError = TokenEnablesSID(hDuplicatedProcessToken, SECURITY_LOCAL_SYSTEM_RID, fLocalSystem); + if (dwError != ERROR_SUCCESS) + { + goto lExit; + } + if (fLocalSystem) + { + goto lExit; + } + + dwError = TokenEnablesSID(hDuplicatedProcessToken, SECURITY_SERVICE_RID, fService); + +lExit: + if (bReverted) + { + if (!SetThreadToken(NULL, hThreadToken)) + { + DWORD dwLastError = GetLastError(); + _ASSERT("SetThreadToken failed"); + + TerminateProcess(GetCurrentProcess(), dwLastError); + } + } + + if (hThreadToken) CloseHandle(hThreadToken); + if (hProcessToken) CloseHandle(hProcessToken); + if (hDuplicatedProcessToken) CloseHandle(hDuplicatedProcessToken); + + if (dwError != ERROR_SUCCESS) + { + fIsLocalSystemOrService = FALSE; // We don't really know + } + else + { + fLocalSystemOrService = (fLocalSystem || fService)? 1 : 0; + fIsLocalSystemOrService = fLocalSystemOrService != 0; + } + + return dwError; + +} + +BOOL +WszCreateProcess( + LPCWSTR lpApplicationName, + LPCWSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + LPVOID lpEnvironment, + LPCWSTR lpCurrentDirectory, + LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation + ) +{ + WINWRAPPER_NO_CONTRACT(SetLastError(ERROR_OUTOFMEMORY); return 0;); + + BOOL fResult; + DWORD err; + { + size_t commandLineLength = wcslen(lpCommandLine) + 1; + NewArrayHolder<WCHAR> nonConstCommandLine(new (nothrow) WCHAR[commandLineLength]); + if (nonConstCommandLine == NULL) + { + SetLastError(ERROR_OUTOFMEMORY); + return 0; + } + + memcpy(nonConstCommandLine, lpCommandLine, commandLineLength * sizeof(WCHAR)); + + fResult = CreateProcessW(lpApplicationName, + nonConstCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwCreationFlags, + lpEnvironment, + (LPWSTR)lpCurrentDirectory, + lpStartupInfo, + lpProcessInformation); + + // At the end of the current scope, the last error code will be overwritten by the destructor of + // NewArrayHolder. So we save the error code here, and restore it after the end of the current scope. + err = GetLastError(); + } + + SetLastError(err); + return fResult; +} + + +DWORD +WszGetWorkingSet() +{ + WINWRAPPER_NO_CONTRACT(SetLastError(ERROR_OUTOFMEMORY); return 0;); + + DWORD dwMemUsage = 0; + + // Consider also calling GetProcessWorkingSetSize to get the min & max working + // set size. I don't know how to get the current working set though... + PROCESS_MEMORY_COUNTERS pmc; + + HINSTANCE hPSapi; + typedef BOOL (GET_PROCESS_MEMORY_INFO)(HANDLE, PROCESS_MEMORY_COUNTERS*, DWORD); + GET_PROCESS_MEMORY_INFO* pGetProcessMemoryInfo; + + hPSapi = WszLoadLibrary(W("psapi.dll")); + if (hPSapi == NULL) { + _ASSERTE(0); + return 0; + } + + pGetProcessMemoryInfo = + (GET_PROCESS_MEMORY_INFO*)GetProcAddress(hPSapi, "GetProcessMemoryInfo"); + // 403746: Prefix correctly complained about + // pGetProcessMemoryInfo != NULL assertion. + if (pGetProcessMemoryInfo == NULL) { + _ASSERTE(0); + FreeLibrary(hPSapi); + return 0; + } + PREFIX_ASSUME(pGetProcessMemoryInfo != NULL); + + BOOL r = pGetProcessMemoryInfo(GetCurrentProcess(), &pmc, (DWORD) sizeof(PROCESS_MEMORY_COUNTERS)); + FreeLibrary(hPSapi); + _ASSERTE(r); + + dwMemUsage = (DWORD)pmc.WorkingSetSize; + + return dwMemUsage; +} + + +SIZE_T +WszGetPagefileUsage() +{ + WINWRAPPER_NO_CONTRACT(SetLastError(ERROR_OUTOFMEMORY); return 0;); + + SIZE_T dwPagefileUsage = 0; + + typedef BOOL (WINAPI FnGetProcessMemoryInfo)(HANDLE, PROCESS_MEMORY_COUNTERS *, DWORD); + + HMODULE hPSApi = WszLoadLibrary(W("Psapi.dll")); + if (hPSApi== NULL) + { + return 0; + } + + FnGetProcessMemoryInfo *pfnGetProcessMemoryInfo = reinterpret_cast<FnGetProcessMemoryInfo *>( + GetProcAddress(hPSApi, "GetProcessMemoryInfo")); + + if (pfnGetProcessMemoryInfo != NULL) + { + PROCESS_MEMORY_COUNTERS pmc; + ZeroMemory(&pmc, sizeof(pmc)); + + if (pfnGetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) + { + dwPagefileUsage = pmc.PagefileUsage; + } + } + + FreeLibrary(hPSApi); + hPSApi = NULL; + + return dwPagefileUsage; +} + +DWORD +WszGetProcessHandleCount() +{ + WINWRAPPER_NO_CONTRACT(SetLastError(ERROR_OUTOFMEMORY); return 0;); + + DWORD dwHandleCount = 0; + + if (!GetProcessHandleCount(GetCurrentProcess(), &dwHandleCount)) + { + dwHandleCount = 0; + } + + return dwHandleCount; +} + |